diff --git a/ANNOUNCE.md b/ANNOUNCE.md
index 8ff85622f237..5dd0cfc2706e 100644
--- a/ANNOUNCE.md
+++ b/ANNOUNCE.md
@@ -1,12 +1,13 @@
-The Wine development release 11.10 is now available.
+The Wine development release 11.11 is now available.
 
 What's new in this release:
-  - Bundled vkd3d upgraded to version 2.0.
-  - XPath support reimplemented without libxml2.
+  - Bundled SymCrypt library, to replace TomCrypt.
+  - More USER32 window information moved to shared memory.
+  - Layered windows in the Wayland driver.
   - More VBScript compatibility improvements.
   - Various bug fixes.
 
-The source is available at <https://dl.winehq.org/wine/source/11.x/wine-11.10.tar.xz>
+The source is available at <https://dl.winehq.org/wine/source/11.x/wine-11.11.tar.xz>
 
 Binary packages for various distributions will be available
 from the respective [download sites][1].
@@ -18,363 +19,341 @@ See the file [AUTHORS][3] for the complete list.
 
 [1]: https://gitlab.winehq.org/wine/wine/-/wikis/Download
 [2]: https://gitlab.winehq.org/wine/wine/-/wikis/Documentation
-[3]: https://gitlab.winehq.org/wine/wine/-/raw/wine-11.10/AUTHORS
+[3]: https://gitlab.winehq.org/wine/wine/-/raw/wine-11.11/AUTHORS
 
 ----------------------------------------------------------------
 
-### Bugs fixed in 11.10 (total 17):
-
- - #9404   cannot select TWAIN source in irfanview
- - #41047  Star Wars Racer videos have no sound
- - #46635  git for windows works or crashes depending on how its invoked
- - #48644  Kodak EasyShare installer doesn't populate list of countries (needs GEO_FRIENDLYNAME)
- - #49720  Family Tree Maker 2017 installer fails after numerous "fixme" console messages
- - #50589  Foxit PhantomPDF Business v10.0 installer fails, reports 'Printer installation failed, please check the print service.'
- - #56482  Vocaloid 6 does not run
- - #57329  Desktop files generated by Wine do not conform to the Desktop Entry Specification
- - #58887  Opening screen of Photolemur 3 has 'tofu' characters between letters on three lines
- - #58888  Photolemur 3 crashes if you use the Settings / Language menu
- - #59522  explorerframe: ITaskbarList3 SetProgressValue/SetProgressState stubs - could use native desktop backends
- - #59539  Querying namespace nodes via XPath's namespace:: axis returns NULL for all nodes. selectSingleNode("namespace::prefix") returns S_FALSE/NULL and selectNodes("namespace::*")   returns an empty list, even when the namespace is in scope.
- - #59555  Star Wars: Knights of the Old Republic - Rendering failure with active framebuffer effects
- - #59625  shcore: Set/GetCurrentProcessExplicitAppUserModelID are stubs
- - #59771  TrackChecker-x64.exe hangs at 100% CPU on startup since Wine 11.7 (msxml3 regression)
- - #59774  Age of Empires III: The Asian Dynasties crashes in the beginning cinematics
- - #59789  comctl32:button: Test fails since 67898adb91
-
-### Changes since 11.9:
+### Bugs fixed in 11.11 (total 25):
+
+ - #27579  The appearance of uTorrent 3.x torrents list is broken
+ - #32079  Dotted (.foobar) files are not displayed in file open dialog
+ - #37849  Wine causing color temperature setting(redshift) not working for a short time
+ - #41134  Foxit Reader 8.x service 'FoxitConnectedPDFService.exe' crashes on startup due to invalid database permissions ('ConvertStringSecurityDescriptorToSecurityDescriptor' SDDL / ACL parser must take whitespace between ACEs into account)
+ - #49622  error in installing battle.net launcher
+ - #49722  Space Empires V can't load certain images (.bmp) with built-in d3dxof.dll
+ - #50583  Foxit PhantomPDF Business v10.0 installer crashes in MSI custom action 'SetStartMenuWindows10T' with WinVer set to 'Windows 10'
+ - #50763  Wreckfest has blurry rendering with OpenGL renderer
+ - #51433  Battle.Net: Installer terminates with BLZBNTBTS0000005C error screen
+ - #51567  Battle.net app takes a long time to start
+ - #53293  Total War: Shogun 2 crashes with builtin d3dx9_42
+ - #53653  winmine.exe is not scaled properly to match the output DPI
+ - #54189  Fail in SetupDiEnumDeviceInfo: err=259: No more data available.
+ - #54694  Marvel's Spider-Man Remastered requires Windows 10 version 1909, build 18363 (or newer)
+ - #55230  Yesterday Origins shows black screen while playing some videos
+ - #56398  Think or Swim running in WINE, loads, then crashes, and need to be shut down.
+ - #57806  visual and response lags in Guitar Pro v8.1.3 (build 121)
+ - #58297  [Wayland-only mode] Minimize button click makes the app window buttons lock up
+ - #59125  Sunlogin crashes on unimplemented function advapi32.dll.WmiCloseBlock
+ - #59453  uSimmics crashes on unimplemented function msvcp100.dll._Sinh
+ - #59549  MS-Money 2000 - shows errors when loading INV7.OCX
+ - #59604  setupapi: CM_Get_Parent stub breaks USB HID device identification
+ - #59807  Regression in multithreaded application
+ - #59841  Missing audio in Gray Matter due to FAudio 26.06 import
+ - #59845  Istaria: game window fails to appear since Wine 11.10
+
+### Changes since 11.10:
 ```
-Aaron Yourk (2):
-      ole32/tests: Add test for OLE clipboard reuse across STA threads.
-      ole32: Validate cached clipboard window handle in get_clipbrd_window.
-
-Alex Henrie (7):
-      wininet: Fix a memory leak in HTTP_DoAuthorization.
-      shell32: Correct folder rename icon ID.
-      shell32: Correct XP control panel icon ID.
-      shell32: Add computer-turning-off icon.
-      shell32: Correct shield icon ID.
-      winex11: Add missing const in langid_from_xkb_layout (GCC).
-      winebth: Fix a memory leak in bluez_gatt_characteristic_read.
-
-Alexandre Julliard (22):
-      win32u: Implement NtUserGetMessagePos().
-      win32u: Implement NtUserSetMessageExtraInfo().
-      mmdevapi: Use a system thread for the audio driver main loop thread.
-      mmdevapi: Use a system thread for the audio driver timer loop thread.
-      user32: Reimplement InSendMessageEx() using NtUserGetThreadState().
-      win32u: Remove redundant receive_flags from the user thread info.
-      user32: Use NtUserGetAncestor to check for desktop window.
-      win32u: Move desktop and message windows to the internal thread info.
-      win32u: Move the GetMessagePos/Time/ExtraInfo values to the internal thread info.
-      imm32: Get the default IMC from the win32u thread state.
-      win32u: Move the default IMC to the internal thread info.
-      win32u: Use a private counter for winproc user callbacks recursion.
-      win32u: Use pthread instead of the TEB to access internal per-thread data.
-      win32u: Avoid using NtUserGetThreadInfo() on the Unix side.
-      vkd3d: Import upstream release 2.0.
-      user32/tests: Add some more tests for GetCurrentInputMessageSource().
-      win32u: Save/restore the message source directly in call_window_proc().
-      win32u: Add support for threads that don't have a TEB.
-      uxtheme: Don't repaint the window itself in DrawThemeParentBackground.
-      include: Move some definitions to processthreadsapi.h.
-      include: Add a number of Interlocked intrinsic defines.
-      nls: Update sorting weights to the Windows 11 version.
-
-Alistair Leslie-Hughes (14):
-      uiautomationcore: Add UiaDisconnectAllProviders stub.
-      include: Add ndattrib.idl.
-      include: Add ndfapi.h.
-      hnetcfg: Return empty collection in INetFwRules_NewEnum.
-      ndfapi: Add a stub dll.
-      wkscli: Add stub dll.
-      wkscli: Move some functions from netapi32.
-      include: Rename tpcshrd.h to tpcshrd.idl.
-      include: Add rtscom.idl.
-      advapi32: Add WmiCloseBlock stub.
-      thumbcache: Add stub dll.
-      uuid: Add thumbcache.h.
-      thumbcache: Add LocalThumbnailCache stubbed interface.
-      sechost: Skip spaces after a bracket when parsing acl strings.
-
-Andrey Gusev (1):
-      qcap: Change helpstring for AVICo coclass.
-
-Aric Stewart (1):
-      uniscribe: Correct possible buffer over/under runs.
-
-Arie Miller (1):
-      shell32: Don't add backslash after CSIDL_PERSONAL.
-
-Bartosz Kosiorek (6):
-      msvcp90: Add _FSinh implementation.
-      msvcp90: Add _Sinh implementation.
-      msvcp90: Add _FCosh implementation.
-      msvcp90: Add _Cosh implementation.
-      msvcp90: Add _LCosh implementation.
-      msvcp90: Add _LSinh implementation.
-
-Benjamin Fischer (1):
-      msvcrt: Avoid expensive timezone queries from plain time() calls.
-
-Bernhard Kölbl (1):
-      wine.inf: Add ImmersiveShell registry key.
-
-Bernhard Übelacker (3):
-      regedit: Remove unused variable.
-      uninstaller: Remove unused variable.
-      setupapi: Only copy dest when it got written by SetupCopyOEMInfW (ASan).
-
-Bohdan Tkachenko (2):
-      sapi: Implement ISpVoice::GetStatus.
-      sapi: Implement ISpVoice::SetPriority and GetPriority.
-
-Brendan Shanks (1):
-      ntdll: Handle SIGBUS with the SIGSEGV handler on ARM64.
-
-Connor McAdams (14):
-      d3dx10/tests: Add tests for loading 2D texture array DDS files.
-      d3dx11/tests: Add tests for loading 2D texture array DDS files.
-      d3dx10: Add support for loading DDS files containing texture arrays.
-      d3dx11: Add support for loading DDS files containing texture arrays.
-      d3dx10/tests: Add tests for D3DX10LoadTextureFromTexture().
-      d3dx10: Validate some input arguments in D3DX10LoadTextureFromTexture().
-      d3dx10: Handle cases in D3DX10LoadTextureFromTexture() where no data is loaded.
-      d3dx10: Implement texture level loading in D3DX10LoadTextureFromTexture().
-      d3dx10: Add support for mipmap generation in D3DX10LoadTextureFromTexture().
-      d3dx10: Add support for 3D textures in D3DX10LoadTextureFromTexture().
-      user32/tests: Add tests for RealGetWindowClass.
-      comctl32/tests: Add tests for RealGetWindowClass.
-      win32u/tests: Add a test for real window class name retrieval.
-      user32: Pass real argument to NtUserGetClassName in RealGetWindowClass{A/W}.
-
-Elizabeth Figura (33):
-      Revert "packager: Prefer native version."
-      Revert "dxcore: Prefer native."
-      vulkan-1: Do not prefer native.
-      ksuser: No longer prefer native.
-      qdvd: No longer prefer native.
-      wmvcore: No longer prefer native.
-      wmp: No longer prefer native.
-      d3dcompiler: No longer prefer native.
-      d3dx9: No longer prefer native.
-      d3dx10: No longer prefer native.
-      d3dx11: No longer prefer native.
-      bluetoothapis: No longer prefer native.
-      dcomp: No longer prefer native.
-      dxva2: No longer prefer native.
-      fltlib: No longer prefer native.
-      pdh: No longer prefer native.
-      virtdisk: No longer prefer native.
-      winsta: No longer prefer native.
-      sfc: No longer prefer native.
-      sfc_os: No longer prefer native.
-      sas: No longer prefer native.
-      fwpuclnt: No longer prefer native.
-      d3dim: No longer prefer native.
-      d3dim700: No longer prefer native.
-      tdh: No longer prefer native.
-      rtutils: No longer prefer native.
-      quartz/tests: Use %I64.
-      quartz: Use %I64.
-      qasf/tests: Use %I64.
-      qcap/tests: Use %I64.
-      qedit/tests: Use %I64.
-      amstream/tests: Use %I64.
-      amstream: Use %I64.
+Alexandre Julliard (35):
+      configure: Re-generate with autoconf 2.73.
+      libs: Import upstream code from SymCrypt 103.11.0.
+      rsaenh: Use SymCrypt for hash algorithms.
+      rsaenh: Use SymCrypt for AES, DES and RC2.
+      symcrypt: Add pclmul target for older compilers.
+      ntdll: Use SymCrypt for SHA hashes.
+      ntdll: Get crc32 implementation from zlib instead of tomcrypt.
+      configure: Don't try to link to external zlib.
+      makefiles: Look for dependencies in assembly files.
+      makefiles: Only build asm files in PE builds.
+      makefiles: Use a separate directory for ARM64EC asm files.
+      symcrypt: Import upstream assembly code for x86-64.
+      symcrypt: Import upstream assembly code for ARM64.
+      symcrypt: Import upstream assembly code for ARM.
+      symcrypt: Import upstream assembly code for i386.
+      faudio: Import upstream release 26.06.
+      mpg123: Import upstream release 1.33.5.
+      png: Import upstream release 1.6.58.
+      lcms2: Import upstream release 2.19.1.
+      sqlite3: Import upstream release 3.53.2.
+      sqlite3: Disable debug assertions.
+      rsaenh: Align allocations for SymCrypt.
+      dssenh: Align allocations for SymCrypt.
+      notices: Add a file to collect all third-party copyright notices.
+      makedep: Add a helper function to skip an initial directory.
+      makedep: Also ignore missing headers when included from external lib header.
+      fluidsynth: Remove unused files.
+      gsm: Remove unused files.
+      icucommon: Remove unused files.
+      icui18n: Remove unused files.
+      ldap: Remove unused files.
+      vkd3d: Remove unused files.
+      xml2: Remove unused files.
+      faudio: Don't fixup wma bitrate if it's already valid.
+      ntdll: Support either a Unix call function table or a __wine_unix_lib_init entrypoint.
+
+Alistair Leslie-Hughes (1):
+      include: Add IInkRecognizerContext coclass.
+
+Anton Baskanov (5):
+      dsound: Use DWORD to store freqAdjustNum, freqAdjustDen and freqAccNum.
+      dsound: Move cp_fields_noresample after cp_fields_resample.
+      dsound: Use #define for fir.h constants.
+      dsound: Add an SSE version of upsample.
+      dsound: Add an SSE version of downsample.
+
+Bernhard Kölbl (2):
+      win32u: Add missing internal messages to spy.
+      win32u: Add win32u internal driver messages to spy.
+
+Bernhard Übelacker (9):
+      crypt32/tests: Avoid crash in testCertProperties.
+      rsaenh: Use _aligned_free instead of free for aligned allocations (ASan).
+      dssenh: Use _aligned_free instead of free for aligned allocations (ASan).
+      dsound/tests: Don't fail test if device cannot be opened the second time.
+      webservices/tests: Improve double tests.
+      user32/tests: Fix test_window_extra_data_proc.
+      windows.ui.core.textinput/tests: Only check interfaces if retrieval succeeded.
+      imagehlp/tests: Accept zero checksum if the compiler emitted a zero checksum.
+      kernel32/tests: Fix tests with old Windows 10 versions.
+
+Charlotte Pabst (3):
+      include: Add some missing definitions in mfd3d12.idl.
+      mfplat/tests: Add tests for mf d3d12 synchronization object.
+      mfplat: Implement mf d3d12 synchronization object.
+
+Conor McCarthy (6):
+      quartz: Update current and elapsed positions in Stop().
+      d3d8/tests: Test multisampled back buffer locking.
+      d3d9/tests: Test multisampled back buffer locking.
+      wined3d: Disallow locking of multisampled back buffers.
+      mfplat/tests: Test CreateObjectFromByteStream() URL with unknown or no extension.
+      mfplat: Do not require CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE if no extension is present.
+
+Dmitry Timoshkov (9):
+      msi: Catch all exceptions in the custom DLL procedure.
+      msi: Return ERROR_INSTALL_FAILURE on an exception in the custom action.
+      ole32/tests: Add some tests for IStorage::MoveElementTo().
+      ole32: Implement IStorage::MoveElementTo() for stream element.
+      ole32: Implement IStorage::MoveElementTo() for storage element.
+      widl: Add support for enums to SLTG typelib generator.
+      widl: Add support for unions to SLTG typelib generator.
+      sane.ds: Add more traces.
+      sane.ds: SANE_DisableDSUserInterface() should accept states 5 and 6.
+
+Elizabeth Figura (11):
+      srclient: No longer prefer native.
+      wevtapi: No longer prefer native.
+      ninput: No longer prefer native.
+      msctf: No longer prefer native.
+      msctfmonitor: No longer prefer native.
+      quartz/tests: Add a few more seeking tests.
+      directmanipulation: No longer prefer native.
+      feclient: No longer prefer native.
+      d3d8thk: No longer prefer native.
+      shcore: No longer prefer native.
+      userenv: No longer prefer native.
 
 Esme Povirk (1):
-      configure: Document the other --enable-archs values.
-
-Feifan He (1):
-      winemac: Handle reset parameter in ClipCursor.
-
-Francis De Brabandere (21):
-      vbscript/tests: Add tests for ExecuteGlobal re-Dim of an existing array.
-      vbscript: Reject re-Dim of an existing global array with err 13.
-      vbscript/tests: Add tests for Cxxx coercion of Null and Nothing.
-      vbscript: Raise err 94 on Cxxx(Null) and err 91 on Cxxx(Nothing).
-      vbscript/tests: Add tests for And/Or/Imp with BSTR operands.
-      vbscript: Coerce BSTR operands of And/Or/Imp to number-or-Boolean.
-      vbscript/tests: Add tests for ReDim on class member arrays.
-      vbscript: Mark class member fixed-size arrays as FADF_FIXEDSIZE.
-      vbscript/tests: Add tests for UBound/LBound on non-array arguments.
-      vbscript: Match native error codes for non-array arguments to UBound/LBound.
-      vbscript: Parse octal numeric literals (&O...).
-      vbscript: Parse double literals via ucrtbase _wcstod_l.
-      vbscript/tests: Add tests for declaration scope in redefinition checks.
-      vbscript: Check top-level name redefinition against global scope only.
-      scrrun/tests: Add test for hashing failure of a degenerate object key.
-      scrrun/dictionary: Report key-hashing failures from lookups.
-      vbscript/tests: Add tests for error reporting from a GetRef reference.
-      vbscript: Run a script-invoked function reference as an internal call.
-      vbscript/tests: Note the error HRESULT seen by an external caller.
-      vbscript/tests: Add tests for class members sharing a global Dim/Const name.
-      vbscript: Compile class members in a separate namespace from globals.
-
-Hans Leidekker (3):
-      include: Update CERT_KEY_CONTEXT.
-      msctf: Add a stub implementation of ITfFnReconversion.
-      odbc32: Add support for driver connection attributes.
-
-Henri Verbeet (1):
-      wined3d: Remove the "more than one back buffer" FIXME from wined3d_swapchain_init().
-
-Huw D. M. Davies (1):
-      hal: Fix #endif guard comment.
-
-Ivan Ivlev (1):
-      oleaut32: Initialize page_info in OleCreatePropertyFrameIndirect.
-
-Jacek Caban (4):
-      ntdll: Don't hold the InSyscallCallback flag during the NtReadFile syscall.
-      ntdll: Use cooperative suspend for syscall callbacks.
-      ntdll/tests: Add tests for suspending ARM64EC syscall callbacks.
-      ntdll: Allow calling NtContinue in syscall callbacks.
-
-Joerg Rueppel (1):
-      d3dxof: Pad with magic value before object data in parse_object().
-
-Jon Koops (2):
-      dsound: Get rid of DirectSoundDevice reference counting.
-      dsound: Remove unused DSOUND_renderers list and lock.
-
-Kun Yang (2):
-      kernel32/tests: Add a test for ReplaceFileW.
-      kernelbase: Fail in ReplaceFileW when replaced file is a directory.
-
-Marc-Aurel Zent (2):
-      winemac: Set IME open status depending on HKL.
-      imm32: Fix ime_ui_notify() TRACE log.
-
-Matteo Bruni (6):
-      win32u: Tweak a couple of comments.
-      win32u: Simplify layout name generation for base keyboard layouts.
-      winex11: Keep a list of every known Xkb keyboard layout.
-      winex11: Store Xkb keyboard layout KLID.
-      winex11: Store Xkb keyboard layout id.
-      dsound: Get rid of the all_stopped argument.
-
-Matthias Gorzellik (1):
-      kernelbase: Add null check for str in OutputDebugStringW.
-
-Nikolay Sivov (10):
-      msxml3: Add a root element helper.
-      msxml3: Add a helper to get last child node.
-      msxml3: Expose previous sibling helper.
-      msxml3: Mark namespace declaration nodes on creation.
-      msxml3: Add implicit 'xml' namespace as document attribute.
-      msxml3: Reimplement XPath support without libxml2.
-      msxml3: Properly free parsed selection namespaces (ASan).
-      msxml3/sax: Fix UTF-16 BE decoder.
-      msxml3/sax: Remove commented out code.
-      oleaut32/tests: Use correct symbol for the Seek() argument.
-
-Paul Gofman (4):
-      ws2_32/tests: Test select() with valid but non-socket handle.
-      ws2_32: Return WSAENOTSOCK instead of WSAENOTSUPP from select() when trying to select on non-socket handle.
-      wined3d: Handle data aliasing when blitting to itself with FX transforms.
-      msctf: Return stub interface from ThreadMgr_GetFunctionProvider().
-
-Piotr Caban (12):
-      uuid: Add IID_IEnterActivityWithNoLock.
-      uuid: Add IID_ICallbackWithNoReentrancyToApplicationSTA.
-      combase: Share context token between MTA threads.
-      combase: Add IComThreadingInfo::SetCurrentLogicalThreadId implementation.
-      ole32: Add IRundown interface definition.
-      combase: Add IRundown interface stub.
-      combase: Implement IContextCallback::ContextCallback.
-      ole32: Add IContextCallback::ContextCallback tests.
-      combase: Enable remoting for STA threads in CoWaitForMultipleHandles.
-      wbemprox: Fix Win32_OperatingSystem.TotalVirtualMemorySize value.
-      wbemprox: Fix Win32_OperatingSystem.FreeVirtualMemory value.
-      msxml3: Fallback to UTF-8 encoding if detection fails.
-
-Robert Gerigk (8):
-      ntoskrnl: Store DEVPKEY_Device_Parent when enumerating child devices.
-      ntoskrnl/tests: Add DEVPKEY_Device_Parent test.
-      ntoskrnl: Store DEVPKEY_Device_Children on parent after bus enumeration.
-      ntoskrnl/tests: Add DEVPKEY_Device_Children test.
-      ntoskrnl: Store DEVPKEY_Device_Siblings on each child after bus enumeration.
-      ntoskrnl/tests: Add DEVPKEY_Device_Siblings test.
-      cfgmgr32: Implement CM_Get_Parent using DEVPKEY_Device_Parent property.
-      ntoskrnl/tests: Test CM_Get_Parent on the PnP-managed child device.
-
-Rose Hellsing (2):
-      riched20: Implement EM_LINELENGTH with wParam==-1.
-      riched20/tests: Add EM_LINELENGTH(-1) tests.
-
-Rémi Bernon (22):
-      win32u: Handle menu name ansi conversion on the PE side.
-      win32u: Simplify window class menu name management.
-      win32u: Avoid leaking user lock on NtUserGetClassInfoEx error.
-      win32u: Simplify internal small class icon creation.
-      win32u: Allow OpenGL context / HDC pixel format mismatch.
-      server: Move shared memory initialization out of create_class.
-      server: Introduce a struct class_info to wrap window class info.
-      server: Move class window proc to the shared memory.
-      win32u: Move class cursor and background to shared memory.
-      win32u: Move window class icons to the shared memory.
-      win32u: Move window class menu names to the shared memory.
-      win32u: Keep track of the registered builtin class atoms.
-      win32u: Create FNID from the ntuser_client_procs enum.
-      win32u: Get rid of unnecessary private_off window field.
-      win32u: Move window info private len to window shared memory.
-      server: Use a dedicated request to set window builtin FNID.
-      win32u: Implement real class names using builtin class FNID.
-      user32: Add helpers to set static control state.
-      user32: Set builtin class FNID for button controls.
-      user32: Set builtin class FNID for combo controls.
-      user32: Set builtin class FNID for edit controls.
-      user32: Set builtin class FNID for listbox controls.
-
-Shaun Ren (2):
-      mf/tests: Add some further tests for the presentation clock's SetTimer().
-      mf/clock: Insert timers in chronological order in present_clock_timer_SetTimer().
-
-Stefan Dösinger (2):
-      shell32: Add an Info icon.
-      shell32: Use IDI_SHELL_INFO icon for SIID_INFO.
-
-Stian Low (1):
-      wined3d: Add GPU information for Intel UHD 620 (0x5917).
-
-Trent Waddington (1):
-      crypt32: CertContext_SetProperty(CERT_NCRYPT_KEY_HANDLE_PROP_ID) takes a handle, not a pointer.
-
-Twaik Yont (7):
-      wineandroid: Restrict Java keyboard focus to whole Wine views.
-      wineandroid: Focus the desktop view after creating it.
-      wineandroid: Ignore duplicate primary button motion events.
-      wineandroid: Create desktop view via ioctl and pass event pipe.
-      wineandroid: Make JNI initialization work with direct JVM loading.
-      wineandroid: Terminate activity when desktop client disconnects.
-      wineandroid: Split Android driver and Wine into separate processes.
-
-Vibhav Pant (11):
-      winebth.sys: Fix potential NULL deref in bluez_gatt_characteristic_value_free.
-      winebth.sys: Push newly enumerated bluetooth devices directly to the watcher event queue.
-      winebth.sys: Factor code for handling new BlueZ objects into bluez_handle_new_object.
-      winebth.sys: Perform initial device enumeration inside a DBusPendingCall callback.
-      winebth.sys: Return the correct boolean value in bluez_gatt_characteristic_props_from_dict_entry.
-      windows.devices.bluetooth/tests: Add tests for IBluetoothLEDeviceStatics::FromBluetoothAddressAsync, IBluetoothLEDevice.
-      windows.devices.bluetooth: Implement BluetoothLEDeviceStatics::FromBluetoothAddressAsync.
-      windows.devices.bluetooth: Implement IBluetoothLEDevice::get_DeviceId.
-      windows.devices.bluetooth: Implement IBluetoothLEDevice::get_GattServices.
-      windows.devices.bluetooth: Implement IGattDeviceService::get_Uuid.
-      windows.devices.bluetooth: Implement IGattDeviceService::get_AttributeHandle.
-
-Zhiyi Zhang (9):
-      winex11.drv: Return WithdrawnState when get_window_wm_state() fails.
-      winex11.drv: Wait for the previous request when transitioning from IconicState to NormalState.
-      ntdll: Add NtAlpcCreatePort() stub.
-      ntdll: Add NtAlpcConnectPort() stub.
-      ntdll: Add NtAlpcAcceptConnectPort() stub.
-      ntdll: Add NtAlpcSendWaitReceivePort() stub.
-      ntdll: Add NtAlpcDisconnectPort() stub.
-      ntdll: Add NtAlpcImpersonateClientOfPort() stub.
-      ntdll: Add missing ZwAlpc* function names.
-
-Ziqing Hui (1):
-      dwrite: Add fallback for Miscellaneous Symbols.
+      comdlg32: Treat empty default extension as null.
+
+Etaash Mathamsetty (3):
+      winewayland: Do not place_above on a detached client surface.
+      winewayland: Treat maximized+fullscreen state as fullscreen.
+      winewayland: Implement min/max size hints for non-resizeable windows.
+
+Fatih Uzunoglu (1):
+      mscms: Add stub for `WcsCreateIccProfile()`.
+
+Francis De Brabandere (20):
+      scrrun/tests: Add tests for dictionary put_Key.
+      scrrun/dictionary: Rename keys in place in put_Key.
+      vbscript: Bind local variables and arguments at compile time.
+      vbscript: Factor out assign_local_var from assign_ident.
+      vbscript: Bind local variable assignments at compile time.
+      vbscript: Factor out do_for_step and do_incc from interp_step and interp_incc.
+      vbscript: Bind For-loop variables at compile time.
+      vbscript: Bind class properties at compile time.
+      scrrun/tests: Add tests for Empty dictionary keys.
+      scrrun/dictionary: Match Empty keys against zero-valued and empty-string keys.
+      scrrun/tests: Add tests for Boolean dictionary keys.
+      scrrun/dictionary: Match native key semantics for Boolean keys.
+      vbscript/tests: Add tests for class declaration scope.
+      vbscript: Handle class declaration scope in the parser.
+      vbscript/tests: Add tests for sub declaration scope.
+      vbscript: Lex a dot immediately followed by a digit as a numeric literal.
+      vbscript: Report specific compile errors for trailing tokens and missing identifiers.
+      vbscript: Allow Dim to shadow a global const from a previous compile unit.
+      vbscript/tests: Add tests for one-line sub headers with multi-line bodies.
+      vbscript: Keep the offending identifier out of Err.Description.
+
+Francisco Casas (3):
+      win32u: Enable VK_KHR_external_fence_capabilities in d3dkmt_init_vulkan().
+      wined3d: Enable VK_KHR_external_fence_capabilities when initializing vulkan.
+      wined3d: Don't use texelFetchOffset() for buffers and multi-sample textures.
+
+Gabriel Ivăncescu (1):
+      ieframe: Return S_FALSE for NULL URLs in WebBrowser_Navigate.
+
+Georg Lehmann (1):
+      winevulkan: Update to VK spec version 1.4.353.
+
+Hans Leidekker (9):
+      winhttp: Revert recursion limit to 3.
+      rsaenh: Use SymCrypt for RSA and RC4.
+      dssenh: Use SymCrypt.
+      bcrypt: Use SymCrypt for hashes.
+      tomcrypt: Remove library.
+      bcrypt: Use SymCrypt for keys.
+      bcrypt: Get rid of the unixlib.
+      bcrypt: Add initial support for CHACHA20_POLY1305.
+      bcrypt: Add support for Brainpool curves.
+
+Ivan Lyugaev (1):
+      secur32: Return SEC_E_INVALID_TOKEN for invalid TLS record headers.
+
+Jacek Caban (12):
+      jscript/tests: Remove unused variables.
+      vbscript/tests: Remove unused variables.
+      ucrtbase/tests: Link directly to environment functions.
+      msvcp140_1/tests: Remove unused variable.
+      msvcp60/tests: Remove unused variable.
+      ddraw/tests: Remove unused variable.
+      ddraw: Remove unused variable.
+      ntdll: Synthesize caller context in ARM64EC RtlRaiseException.
+      ieframe/tests: Add missing CHECK_CALLED statements.
+      scrobj/tests: Remove unused variables.
+      wpcap/tests: Remove unused variables.
+      shcore/tests: Remove unused variables.
+
+Jiangyi Chen (2):
+      winewayland.drv: Add alpha-modifier-v1 protocol to support change alpha by WAYLAND_SetLayeredWindowAttributes.
+      winewayland.drv: Add WAYLAND_UpdateLayeredWindow and add support for changing alpha using the alpha-modifier-v1 protocol.
+
+Kellen Kopp (1):
+      win32u: Fixed incorrect glyph outline size.
+
+Marc-Aurel Zent (3):
+      winemac: Remove unused includes in vulkan.c.
+      winemac: Factor out Metal device and view logic into MetalViewSwapChain.
+      winemac: Implement cross-process MetalViewSwapChain via CALayerHost.
+
+Martin Storsjö (2):
+      ntdll: Fix building for arm64 without HWCAP_CPUID defined.
+      ntdll: Fix building for arm64 without ESR_MAGIC/esr_context.
+
+Matteo Bruni (4):
+      server: Fix numpad vkey handling.
+      winex11: Return the layout index from from detect_keyboard_layout().
+      winex11: Create layouts when falling back to fuzzy detection.
+      configure: Add -msse2 to default i386_EXTRACFLAGS.
+
+Nikolay Sivov (5):
+      msxml3/writer: Use wchar literals in more places.
+      msxml3/tests: Add more tests for the writer output.
+      msxml3/tests: Use writer output test helper throughout the tests.
+      msxml3/tests: Avoid string conversion in a few places.
+      msxml3/writer: Normalize newlines in CDATA content.
+
+Paul Gofman (8):
+      ddraw: Renamed is_root surface attribute to is_implicit.
+      ddraw: Allow attaching flip chain surfaces.
+      ddraw/tests: Add tests for attaching and detaching backbuffers on ddraw1.
+      ddraw: Force render target bind flags on v1 swapchain candidate surfaces.
+      kernelbase: Add stub for GetIntegratedDisplaySize().
+      dsound: Fail IDirectSoundCaptureBufferImpl_Lock() for invalid buffer range.
+      ddraw/tests: Remove extra backbuffer1 release in test_surface_attachment().
+      win32u: Always consider newly created shape updated in set_surface_shape().
+
+Piotr Caban (3):
+      msxml3: Don't add newline before first element when indentation is enabled.
+      msxml3: Don't indent endElement since it changes text node content.
+      include: Define more runtimeclasses in windows.storage.streams.idl.
+
+Rose Hellsing (6):
+      wineboot: Create SQMClient MachineId registry key.
+      ntdll: Account for read-only cookie in loader.
+      ntoskrnl.exe: Account for read-only cookies.
+      kernel32/tests: Add test for read-only cookie in loader.
+      riched20: Properly capture \pntext content.
+      riched20/tests: Expand editor tests to verify \pntext behaviour.
+
+Russell Greene (1):
+      hhctrl: Return a nonzero cookie from HH_INITIALIZE.
+
+Rémi Bernon (43):
+      server: Keep track of builtin class FNID on registration.
+      server: Introduce a get_class_fnid helper.
+      user32: Set builtin class FNID for menu controls.
+      user32: Set builtin class FNID for MDI client controls.
+      user32: Set builtin class FNID for dialog controls.
+      win32u: Set builtin class FNID on window creation.
+      winex11: Move find_xkb_layout_variant helper around.
+      opengl32: Allocate display lists for client-side contexts.
+      opengl32: Share display lists in wglShareLists on the PE side.
+      opengl32: Query the context profile mask on init.
+      opengl32: Track client-side buffer names allocation.
+      opengl32: Track implicit buffer names allocation.
+      user32: Store a scroll_info pointer in the scrollbar control info.
+      user32: Set FNID for the builtin scrollbar window class.
+      opengl32: Call get_integer hook after a successful unix call.
+      opengl32: Add get_integer hooks to indexed glGet functions.
+      opengl32: Pass the returned unix value to get_integer hooks.
+      opengl32: Hook more glGet functions with get_integer.
+      opengl32: Implement client / host mapping for buffer names.
+      win32u: Introduce a server_set_window_info helper.
+      server: Move window extra data to the shared memory.
+      win32u: Return value directly from get_window_extra.
+      server: Move window id / menu to the shared memory.
+      server: Move window instance to the shared memory.
+      server: Move window user data to shared memory.
+      server: Move class local flag to the shared memory.
+      server: Keep track of class window proc ansi nature.
+      win32u: Simplify winproc handle to index conversions.
+      win32u: Move window procedure to the shared memory.
+      opengl32: Implement client / host mapping for framebuffer names.
+      opengl32: Allow framebuffer implicit allocation in some core contexts.
+      opengl32: Implement client / host mapping for renderbuffer names.
+      opengl32: Allow renderbuffer implicit allocation in some core contexts.
+      opengl32: Implement client / host mapping for texture names.
+      opengl32: Ignore more cases in make_opengl get_object_type.
+      opengl32: Implement client / host mapping for sampler names.
+      opengl32: Implement client / host mapping for display list names.
+      opengl32: Implement client / host mapping for program names.
+      opengl32: Implement client / host mapping for vertex shader names.
+      opengl32: Implement client / host mapping for fragment shader names.
+      opengl32: Implement client / host mapping for semaphore names.
+      opengl32: Implement client / host mapping for memory names.
+      opengl32: Implement client / host mapping for path names.
+
+SeongChan Lee (1):
+      winexinput.sys: Fix handling of non-32-bit axis.
+
+Soham Nandy (1):
+      ntdll: Preserve nonvolatile registers across VEH callbacks.
+
+Steven Don (2):
+      atl: Implement registering ATL window messages.
+      atl/tests: Add tests for proper handling of WM_ATLGETCONTROL and WM_ATLGETHOST.
+
+Thibault Payet (1):
+      ntdll/unix: Always define get_current_thread_data.
+
+Tim Clem (1):
+      hnetcfg: Report 0 returned elements from IEnumVARIANT::Next.
+
+Twaik Yont (1):
+      wineandroid: Raise minimum SDK version to 28 (Android 9.0).
+
+Vibhav Pant (2):
+      combase/tests: Add tests for delayed marshaling of objects with apartment affinity.
+      combase: Implement delayed marshaling for RoGetAgileReference.
+
+Wolfgang Hartl (1):
+      ir50_32: Export DllRegisterServer and DllUnregisterServer.
+
+Yuxuan Shui (2):
+      mf/tests: Check frame size of video processor's output type.
+      winegstreamer: Video processor shouldn't have a preferred size.
+
+Zhengyong Chen (1):
+      win32u: Fix signed coordinate extraction in drag_drop_call.
 ```
diff --git a/AUTHORS b/AUTHORS
index 9efc8904d085..e59fbd687931 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -574,6 +574,7 @@ Fabrice Ménard
 Fan WenJie
 Farshad Agah
 Fatih Aşıcı
+Fatih Uzunoglu
 Federico Schwindt
 Feifan He
 Felix Hädicke
@@ -1051,6 +1052,7 @@ Keith Bowes
 Keith Matthews
 Keith Reynolds
 Keith Stevens
+Kellen Kopp
 Ken Belleau
 Ken Coleman
 Ken Myers
@@ -1766,6 +1768,7 @@ Sin-ta Hsiea
 Slava Monich
 Slaven Rezic
 Snorri Sturluson
+Soham Nandy
 Spencer Wallace
 Sreehari Anil
 Srivatsa Kanchi, R
diff --git a/LICENSE.OLD b/LICENSE.OLD
deleted file mode 100644
index e0243408742f..000000000000
--- a/LICENSE.OLD
+++ /dev/null
@@ -1,25 +0,0 @@
-Note: Wine is now distributed under the terms of the GNU Lesser
-General Public License, as specified in the file LICENSE.
-
-Older releases of Wine, up to release 20020228, were distributed under
-the following license:
-
-Copyright (c) 1993-2002 the Wine project authors (see the file AUTHORS
-for a complete list)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/NOTICES.md b/NOTICES.md
new file mode 100644
index 000000000000..dc4b94d902e2
--- /dev/null
+++ b/NOTICES.md
@@ -0,0 +1,1492 @@
+# Third-party notices
+
+Wine imports code from a number of third-party projects. This file
+contains the original copyright notices for all such imports.
+
+----------------------------------------------------------------
+
+### Adobe glyph list
+```
+Copyright 2002-2019 Adobe (http://www.adobe.com/).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+Redistributions of source code must retain the above
+copyright notice, this list of conditions and the following
+disclaimer.
+
+Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials
+provided with the distribution.
+
+Neither the name of Adobe nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+```
+
+
+### BCDec
+```
+This software is available under 2 licenses -- choose whichever you prefer.
+
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+
+Copyright (c) 2022 Sergii Kudlai
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+------------------------------------------------------------------------------
+ALTERNATIVE B - The Unlicense
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org>
+```
+
+
+### Bitstream Vera Sans
+```
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
+Vera is a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute
+the Font Software, including without limitation the rights to use,
+copy, merge, publish, distribute, and/or sell copies of the Font
+Software, and to permit persons to whom the Font Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Bitstream" or the word "Vera".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Bitstream Vera" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
+SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font
+Software without prior written authorization from the Gnome Foundation
+or Bitstream Inc., respectively. For further information, contact:
+fonts at gnome dot org.
+```
+
+
+### Capstone
+```
+Copyright (c) 2013, COSEINC.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+* Neither the name of the developer(s) nor the names of its
+  contributors may be used to endorse or promote products derived from this
+  software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+```
+
+
+### DirectXMath
+```
+Copyright (c) Microsoft Corporation.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE
+```
+
+
+### An Efficient and Robust Ray-Box Intersection Algorithm (article)
+```
+Amy Williams             University of Utah
+Steve Barrus             University of Utah
+R. Keith Morley          University of Utah
+Peter Shirley            University of Utah
+
+International Conference on Computer Graphics and Interactive Techniques  archive
+ACM SIGGRAPH 2005 Courses
+Los Angeles, California
+
+This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
+```
+
+
+### FluidSynth
+```
+The source code for FluidSynth is distributed under the terms of the
+GNU Lesser General Public License version 2.1.
+```
+
+
+### GIFLIB
+```
+The GIFLIB distribution is Copyright (c) 1997  Eric S. Raymond
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+```
+
+
+### GLU
+```
+SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice including the dates of first publication and
+either this permission notice or a reference to
+http://oss.sgi.com/projects/FreeB/
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Except as contained in this notice, the name of Silicon Graphics, Inc.
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization from
+Silicon Graphics, Inc.
+```
+
+
+### GStreamer
+```
+Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+                   2000 Wim Taymans <wtay@chello.be>
+                   2002 Thomas Vander Stichele <thomas@apestaart.org>
+                   2004 Wim Taymans <wim@fluendo.com>
+                   2015 Jan Schmidt <jan@centricular.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to the
+Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+Boston, MA 02110-1301, USA.
+```
+
+
+### IAccessible2
+```
+Copyright (c) 2007, 2013 Linux Foundation
+Copyright (c) 2006 IBM Corporation
+Copyright (c) 2000, 2006 Sun Microsystems, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following
+    disclaimer in the documentation and/or other materials
+    provided with the distribution.
+
+ 3. Neither the name of the Linux Foundation nor the names of its
+    contributors may be used to endorse or promote products
+    derived from this software without specific prior written
+    permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+This BSD License conforms to the Open Source Initiative "Simplified
+BSD License" as published at:
+http://www.opensource.org/licenses/bsd-license.php
+
+IAccessible2 is a trademark of the Linux Foundation. The IAccessible2
+mark may be used in accordance with the Linux Foundation Trademark
+Policy to indicate compliance with the IAccessible2 specification.
+```
+
+
+### ICU
+```
+Copyright © 1991-2022 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+```
+
+
+### JXR
+```
+Copyright © Microsoft Corp.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+• Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+• Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+```
+
+
+### LibJPEG
+```
+The authors make NO WARRANTY or representation, either express or implied,
+with respect to this software, its quality, accuracy, merchantability, or
+fitness for a particular purpose.  This software is provided "AS IS", and you,
+its user, assume the entire risk as to its quality and accuracy.
+
+This software is copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding.
+All Rights Reserved except as specified below.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+software (or portions thereof) for any purpose, without fee, subject to these
+conditions:
+(1) If any part of the source code for this software is distributed, then this
+README file must be included, with this copyright and no-warranty notice
+unaltered; and any additions, deletions, or changes to the original files
+must be clearly indicated in accompanying documentation.
+(2) If only executable code is distributed, then the accompanying
+documentation must state that "this software is based in part on the work of
+the Independent JPEG Group".
+(3) Permission for use of this software is granted only if the user accepts
+full responsibility for any undesirable consequences; the authors accept
+NO LIABILITY for damages of any kind.
+
+These conditions apply to any software derived from or based on the IJG code,
+not just to the unmodified library.  If you use our work, you ought to
+acknowledge us.
+
+Permission is NOT granted for the use of any IJG author's name or company name
+in advertising or publicity relating to this software or products derived from
+it.  This software may be referred to only as "the Independent JPEG Group's
+software".
+
+We specifically permit and encourage the use of this software as the basis of
+commercial products, provided that all warranty or liability claims are
+assumed by the product vendor.
+```
+
+
+### LibPNG
+```
+ * Copyright (c) 1995-2026 The PNG Reference Library Authors.
+ * Copyright (c) 2018-2026 Cosmin Truta.
+ * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
+ * Copyright (c) 1996-1997 Andreas Dilger.
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+
+The software is supplied "as is", without warranty of any kind,
+express or implied, including, without limitation, the warranties
+of merchantability, fitness for a particular purpose, title, and
+non-infringement.  In no event shall the Copyright owners, or
+anyone distributing the software, be liable for any damages or
+other liability, whether in contract, tort or otherwise, arising
+from, out of, or in connection with the software, or the use or
+other dealings in the software, even if advised of the possibility
+of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute
+this software, or portions hereof, for any purpose, without fee,
+subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you
+    must not claim that you wrote the original software.  If you
+    use this software in a product, an acknowledgment in the product
+    documentation would be appreciated, but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must
+    not be misrepresented as being the original software.
+
+ 3. This Copyright notice may not be removed or altered from any
+    source or altered source distribution.
+```
+
+
+### LibTIFF
+```
+Copyright (c) 1988-1997 Sam Leffler
+Copyright (c) 1991-1997 Silicon Graphics, Inc.
+
+Permission to use, copy, modify, distribute, and sell this software and
+its documentation for any purpose is hereby granted without fee, provided
+that (i) the above copyright notices and this permission notice appear in
+all copies of the software and related documentation, and (ii) the names of
+Sam Leffler and Silicon Graphics may not be used in any advertising or
+publicity relating to the software without the specific, prior written
+permission of Sam Leffler and Silicon Graphics.
+
+THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+OF THIS SOFTWARE.
+```
+
+
+### LibXML2
+```
+ Copyright (C) 1998-2012 Daniel Veillard.  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+```
+
+
+### LibXSLT
+```
+ Copyright (C) 2001-2002 Daniel Veillard.  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Daniel Veillard shall not
+be used in advertising or otherwise to promote the sale, use or other deal-
+ings in this Software without prior written authorization from him.
+```
+
+
+### Little CMS
+```
+Copyright (c) 1998-2020 Marti Maria Saguer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject
+to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+```
+
+
+### LLVM C++ libraries
+```
+The libc++ library is dual licensed under both the University of Illinois
+"BSD-Like" license and the MIT license.  As a user of this code you may choose
+to use it under either license.  As a contributor, you agree to allow your code
+to be used under both.
+
+Full text of the relevant licenses is included below.
+
+----------------------------------------------------------------
+
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
+
+All rights reserved.
+
+Developed by:
+
+    LLVM Team
+
+    University of Illinois at Urbana-Champaign
+
+    http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimers.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimers in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the names of the LLVM Team, University of Illinois at
+      Urbana-Champaign, nor the names of its contributors may be used to
+      endorse or promote products derived from this Software without specific
+      prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+----------------------------------------------------------------
+
+Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+```
+
+
+### Mpg123
+```
+	Copyright (c) 1995-2020 by Michael Hipp and others,
+	free software under the terms of the LGPL v2.1
+
+There is an attempt to cover the actual list of authors in the AUTHORS file.
+Project maintainer since 2006 is Thomas Orgis and many people have contributed
+since the Michael Hipp era, but he stays the initial source and it would
+be impractical to count them all individually, so it's "and others".
+Source files contain the phrase "the mpg123 project" to the same effect
+in their license boilerplate; especially those that were added after
+maintainership changed. The person mainly responsible for the first version
+is usually named in the phrase "initially written by ...".
+
+All files in the distribution that don't carry a license note on their own are
+licensed under the terms of the LGPL 2.1; exceptions may apply, especially to
+files not in the official distribution but in the revision control repository.
+```
+
+
+### Musl
+```
+musl as a whole is licensed under the following standard MIT license:
+
+Copyright © 2005-2020 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
+
+Portions of this software are derived from third-party works licensed
+under terms compatible with the above MIT license:
+
+Much of the math library code (src/math/* and src/complex/*) is
+Copyright © 1993,2004 Sun Microsystems or
+Copyright © 2003-2011 David Schultz or
+Copyright © 2003-2009 Steven G. Kargl or
+Copyright © 2003-2009 Bruce D. Evans or
+Copyright © 2008 Stephen L. Moshier or
+Copyright © 2017-2018 Arm Limited
+and labelled as such in comments in the individual source files. All
+have been licensed under extremely permissive terms.
+
+The x86_64 port was written by Nicholas J. Kain and is licensed under
+the standard MIT terms.
+
+All other files which have no copyright comments are original works
+produced specifically for use as part of this library, written either
+by Rich Felker, the main author of the library, or by one or more
+contibutors listed above. Details on authorship of individual files
+can be found in the git version control history of the project. The
+omission of copyright and license comments in each file is in the
+interest of source tree size.
+
+In addition, permission is hereby granted for all public header files
+(include/* and arch/*/bits/*) and crt files intended to be linked into
+applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
+the copyright notice and permission notice otherwise required by the
+license, and to use these files without any requirement of
+attribution. These files include substantial contributions from:
+
+Bobby Bingham
+John Spencer
+Nicholas J. Kain
+Rich Felker
+Richard Pennington
+Stefan Kristiansson
+Szabolcs Nagy
+
+all of whom have explicitly granted such permission.
+
+This file previously contained text expressing a belief that most of
+the files covered by the above exception were sufficiently trivial not
+to be subject to copyright, resulting in confusion over whether it
+negated the permissions granted in the license. In the spirit of
+permissive licensing, and of not having licensing issues being an
+obstacle to adoption, that text has been removed.
+```
+
+
+### OpenCL specification
+```
+Copyright (c) 2013-2020 The Khronos Group Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+---- Exceptions to the Apache 2.0 License: ----
+
+As an exception, if you use this Software to generate code and portions of
+this Software are embedded into the generated code as a result, you may
+redistribute such product without providing attribution as would otherwise
+be required by Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link code generated by this Software with
+software that is licensed under the GPLv2 or the LGPL v2.0 or 2.1
+("`Combined Software`") and if a court of competent jurisdiction determines
+that the patent provision (Section 3), the indemnity provision (Section 9)
+or other Section of the License conflicts with the conditions of the
+applicable GPL or LGPL license, you may retroactively and prospectively
+choose to deem waived or otherwise exclude such Section(s) of the License,
+but only in their entirety and only with respect to the Combined Software.
+```
+
+
+### OpenLDAP
+```
+Copyright 1998-2024 The OpenLDAP Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Individual files and/or contributed packages may be copyright by
+other parties and/or subject to additional restrictions.
+
+This work is derived from the University of Michigan LDAP v3.3
+distribution.  Information concerning this software is available
+at <http://www.umich.edu/~dirsvcs/ldap/ldap.html>.
+
+This work also contains materials derived from public sources.
+
+Additional information about OpenLDAP can be obtained at
+<http://www.openldap.org/>.
+
+---
+
+Portions Copyright 1998-2013 Kurt D. Zeilenga.
+Portions Copyright 1998-2006 Net Boolean Incorporated.
+Portions Copyright 2001-2006 IBM Corporation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+---
+
+Portions Copyright 1999-2008 Howard Y.H. Chu.
+Portions Copyright 1999-2008 Symas Corporation.
+Portions Copyright 1998-2003 Hallvard B. Furuseth.
+Portions Copyright 2007-2011 Gavin Henry.
+Portions Copyright 2007-2011 Suretec Systems Ltd.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that this notice is preserved.
+The names of the copyright holders may not be used to endorse or
+promote products derived from this software without their specific
+prior written permission.  This software is provided ``as is''
+without express or implied warranty.
+
+---
+
+Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+All rights reserved.
+
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to the University of Michigan at Ann Arbor.  The name of the
+University may not be used to endorse or promote products derived
+from this software without specific prior written permission.  This
+software is provided ``as is'' without express or implied warranty.
+
+
+The OpenLDAP Public License
+  Version 2.8, 17 August 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+   and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+   statements and notices, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials provided
+   with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number.  You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission.  Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA.  All Rights Reserved.  Permission to copy and
+distribute verbatim copies of this document is granted.
+```
+
+
+### RTF Tools
+```
+Author: Paul DuBois	dubois@primate.wisc.edu
+
+This software may be redistributed without restriction and used for
+any purpose whatsoever.
+```
+
+
+### SQLite3
+```
+The author disclaims copyright to this source code.  In place of
+a legal notice, here is a blessing:
+
+   May you do good and not evil.
+   May you find forgiveness for yourself and forgive others.
+   May you share freely, never taking more than you give.
+```
+
+
+### STB DXT
+```
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+```
+
+
+### SymCrypt
+```
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
+
+
+### Unicode Bidirectional Algorithm
+```
+Copyright (C) 1999-2005, ASMUS, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished
+to do so, provided that (a) the above copyright notice(s) and this
+permission notice appear with all copies of the Data Files or Software,
+(b) both the above copyright notice(s) and this permission notice appear
+in associated documentation, and (c) there is clear notice in each
+modified Data File or in the Software as well as in the documentation
+associated with the Data File(s) or Software that the data or software
+has been modified.
+```
+
+
+### Unicode data files
+```
+Copyright © 1991-2022 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+```
+
+
+### Vkd3d
+```
+Copyright 2016-2026 the vkd3d project authors (see the file AUTHORS for a
+complete list)
+
+Vkd3d is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at
+your option) any later version.
+
+This library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+```
+
+
+### Wayland alpha modified protocol
+```
+Copyright © 2024 Xaver Hugl
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland cursor shape protocol
+```
+Copyright 2018 The Chromium Authors
+Copyright 2023 Simon Ser
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland pointer constraints protocol
+```
+Copyright © 2014      Jonas Ådahl
+Copyright © 2015      Red Hat Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland pointer warp protocol
+```
+Copyright © 2024 Neal Gompa
+Copyright © 2024 Xaver Hugl
+Copyright © 2024 Matthias Klumpp
+Copyright © 2024 Vlad Zahorodnii
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland relative pointer protocol
+```
+Copyright © 2014      Jonas Ådahl
+Copyright © 2015      Red Hat Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland text input protocol
+```
+Copyright © 2012, 2013 Intel Corporation
+Copyright © 2015, 2016 Jan Arne Petersen
+Copyright © 2017, 2018 Red Hat, Inc.
+Copyright © 2018       Purism SPC
+
+Permission to use, copy, modify, distribute, and sell this
+software and its documentation for any purpose is hereby granted
+without fee, provided that the above copyright notice appear in
+all copies and that both that copyright notice and this permission
+notice appear in supporting documentation, and that the name of
+the copyright holders not be used in advertising or publicity
+pertaining to distribution of the software without specific,
+written prior permission.  The copyright holders make no
+representations about the suitability of this software for any
+purpose.  It is provided "as is" without express or implied
+warranty.
+
+THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+```
+
+
+### Wayland viewporter protocol
+```
+Copyright © 2013-2016 Collabora, Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland wlr data control protocol
+```
+Copyright © 2018 Simon Ser
+Copyright © 2019 Ivan Molodetskikh
+
+Permission to use, copy, modify, distribute, and sell this
+software and its documentation for any purpose is hereby granted
+without fee, provided that the above copyright notice appear in
+all copies and that both that copyright notice and this permission
+notice appear in supporting documentation, and that the name of
+the copyright holders not be used in advertising or publicity
+pertaining to distribution of the software without specific,
+written prior permission.  The copyright holders make no
+representations about the suitability of this software for any
+purpose.  It is provided "as is" without express or implied
+warranty.
+
+THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+```
+
+
+### Wayland XDG output protocol
+```
+Copyright © 2017 Red Hat Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland XDG shell protocol
+```
+Copyright © 2008-2013 Kristian Høgsberg
+Copyright © 2013      Rafael Antognolli
+Copyright © 2013      Jasper St. Pierre
+Copyright © 2010-2013 Intel Corporation
+Copyright © 2015-2017 Samsung Electronics Co., Ltd
+Copyright © 2015-2017 Red Hat Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Wayland XDG toplevel icon protocol
+```
+Copyright © 2023-2024 Matthias Klumpp
+Copyright ©      2024 David Edmundson
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+```
+
+
+### Windows Presentation Foundation
+```
+Copyright (c) .NET Foundation and Contributors
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
+
+
+### Wine until release 20020228
+```
+Copyright (c) 1993-2002 the Wine project authors (see the file AUTHORS
+for a complete list)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+```
+
+
+### X11
+```
+Copyright (c) 1987, 1988  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+			All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+```
+
+
+### ZLib
+```
+Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly        Mark Adler
+jloup@gzip.org          madler@alumni.caltech.edu
+```
diff --git a/VERSION b/VERSION
index 3d72741195b6..1d3a286ee68a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Wine version 11.10
+Wine version 11.11
diff --git a/configure b/configure
index 6832c29abe58..4fad7b2636d5 100755
--- a/configure
+++ b/configure
@@ -1,11 +1,11 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for Wine 11.10.
+# Generated by GNU Autoconf 2.73 for Wine 11.11.
 #
 # Report bugs to <wine-devel@winehq.org>.
 #
 #
-# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation,
+# Copyright (C) 1992-1996, 1998-2017, 2020-2026 Free Software Foundation,
 # Inc.
 #
 #
@@ -22,7 +22,7 @@ then :
   emulate sh
   NULLCMD=:
   # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
-  # is contrary to our usage.  Disable this feature.
+  # contradicts POSIX and common usage.  Disable this.
   alias -g '${1+"$@"}'='"$@"'
   setopt NO_GLOB_SUBST
 else case e in #(
@@ -109,7 +109,7 @@ if test "x$as_myself" = x; then
   as_myself=$0
 fi
 if test ! -f "$as_myself"; then
-  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  printf '%s\n' "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
   exit 1
 fi
 
@@ -131,10 +131,13 @@ case $- in # ((((
   *x* ) as_opts=-x ;;
   * ) as_opts= ;;
 esac
-exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+case $# in # ((
+  0) exec $CONFIG_SHELL $as_opts "$as_myself" ;;
+  *) exec $CONFIG_SHELL $as_opts "$as_myself" "$@" ;;
+esac
 # Admittedly, this is quite paranoid, since all the known shells bail
 # out after a failed 'exec'.
-printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+printf '%s\n' "$0: could not re-execute with $CONFIG_SHELL" >&2
 exit 255
   fi
   # We don't want this to propagate to other subprocesses.
@@ -145,7 +148,7 @@ then :
   emulate sh
   NULLCMD=:
   # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
-  # is contrary to our usage.  Disable this feature.
+  # contradicts POSIX and common usage.  Disable this.
   alias -g '\${1+\"\$@\"}'='\"\$@\"'
   setopt NO_GLOB_SUBST
 else case e in #(
@@ -254,22 +257,25 @@ case $- in # ((((
   *x* ) as_opts=-x ;;
   * ) as_opts= ;;
 esac
-exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+case $# in # ((
+  0) exec $CONFIG_SHELL $as_opts "$as_myself" ;;
+  *) exec $CONFIG_SHELL $as_opts "$as_myself" "$@" ;;
+esac
 # Admittedly, this is quite paranoid, since all the known shells bail
 # out after a failed 'exec'.
-printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+printf '%s\n' "$0: could not re-execute with $CONFIG_SHELL" >&2
 exit 255
 fi
 
     if test x$as_have_required = xno
 then :
-  printf "%s\n" "$0: This script requires a shell more modern than all"
-  printf "%s\n" "$0: the shells that I found on your system."
+  printf '%s\n' "$0: This script requires a shell more modern than all"
+  printf '%s\n' "$0: the shells that I found on your system."
   if test ${ZSH_VERSION+y} ; then
-    printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
-    printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
+    printf '%s\n' "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    printf '%s\n' "$0: be upgraded to zsh 4.3.4 or later."
   else
-    printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
+    printf '%s\n' "$0: Please tell bug-autoconf@gnu.org and
 $0: wine-devel@winehq.org about your system, including any
 $0: error possibly output before this message. Then install
 $0: a modern shell, or manually run the script under such a
@@ -330,7 +336,7 @@ as_fn_mkdir_p ()
     as_dirs=
     while :; do
       case $as_dir in #(
-      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *\'*) as_qdir=`printf '%s\n' "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
       *) as_qdir=$as_dir;;
       esac
       as_dirs="'$as_qdir' $as_dirs"
@@ -339,7 +345,7 @@ $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
 	 X"$as_dir" : 'X\(//\)[^/]' \| \
 	 X"$as_dir" : 'X\(//\)$' \| \
 	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-printf "%s\n" X"$as_dir" |
+printf '%s\n' X"$as_dir" |
     sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
 	    s//\1/
 	    q
@@ -422,9 +428,9 @@ as_fn_error ()
   as_status=$1; test $as_status -eq 0 && as_status=1
   if test "$4"; then
     as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
   fi
-  printf "%s\n" "$as_me: error: $2" >&2
+  printf '%s\n' "$as_me: error: $2" >&2
   as_fn_exit $as_status
 } # as_fn_error
 
@@ -451,7 +457,7 @@ as_me=`$as_basename -- "$0" ||
 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
 	 X"$0" : 'X\(//\)$' \| \
 	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-printf "%s\n" X/"$0" |
+printf '%s\n' X/"$0" |
     sed '/^.*\/\([^/][^/]*\)\/*$/{
 	    s//\1/
 	    q
@@ -497,7 +503,7 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
       s/-\n.*//
     ' >$as_me.lineno &&
   chmod +x "$as_me.lineno" ||
-    { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+    { printf '%s\n' "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
 
   # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
   # already done that, so ensure we don't try to do so again and fall
@@ -511,29 +517,6 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
   exit
 }
 
-
-# Determine whether it's possible to make 'echo' print without a newline.
-# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
-# for compatibility with existing Makefiles.
-ECHO_C= ECHO_N= ECHO_T=
-case `echo -n x` in #(((((
--n*)
-  case `echo 'xy\c'` in
-  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
-  xy)  ECHO_C='\c';;
-  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
-       ECHO_T='	';;
-  esac;;
-*)
-  ECHO_N='-n';;
-esac
-
-# For backward compatibility with old third-party macros, we provide
-# the shell variables $as_echo and $as_echo_n.  New code should use
-# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
-as_echo='printf %s\n'
-as_echo_n='printf %s'
-
 rm -f conf$$ conf$$.exe conf$$.file
 if test -d conf$$.dir; then
   rm -f conf$$.dir/conf$$.file
@@ -592,6 +575,7 @@ ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
 # Initializations.
 #
 ac_default_prefix=/usr/local
+ac_clean_CONFIG_STATUS=
 ac_clean_files=
 ac_config_libobj_dir=.
 LIBOBJS=
@@ -603,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Wine'
 PACKAGE_TARNAME='wine'
-PACKAGE_VERSION='11.10'
-PACKAGE_STRING='Wine 11.10'
+PACKAGE_VERSION='11.11'
+PACKAGE_STRING='Wine 11.11'
 PACKAGE_BUGREPORT='wine-devel@winehq.org'
 PACKAGE_URL='https://www.winehq.org'
 
@@ -641,7 +625,7 @@ ac_includes_default="\
 #endif"
 
 ac_header_c_list=
-enable_year2038=yes
+: ${enable_year2038:=yes}
 ac_subst_vars='LTLIBOBJS
 LIBOBJS
 TAGSFLAGS
@@ -724,10 +708,10 @@ VKD3D_PE_LIBS
 VKD3D_PE_CFLAGS
 UNWIND_PE_LIBS
 UNWIND_PE_CFLAGS
-TOMCRYPT_PE_LIBS
-TOMCRYPT_PE_CFLAGS
 TIFF_PE_LIBS
 TIFF_PE_CFLAGS
+SYMCRYPT_PE_LIBS
+SYMCRYPT_PE_CFLAGS
 SQLITE3_PE_LIBS
 SQLITE3_PE_CFLAGS
 PNG_PE_LIBS
@@ -898,13 +882,13 @@ build_vendor
 build_cpu
 build
 system_dllpath
+ECHO_T
+ECHO_N
+ECHO_C
 target_alias
 host_alias
 build_alias
 LIBS
-ECHO_T
-ECHO_N
-ECHO_C
 DEFS
 mandir
 localedir
@@ -1707,8 +1691,8 @@ enable_sqlite3
 enable_strmbase
 enable_strmiids
 enable_strsafe
+enable_symcrypt
 enable_tiff
-enable_tomcrypt
 enable_unwind
 enable_uuid
 enable_vkd3d
@@ -1891,10 +1875,10 @@ PNG_PE_CFLAGS
 PNG_PE_LIBS
 SQLITE3_PE_CFLAGS
 SQLITE3_PE_LIBS
+SYMCRYPT_PE_CFLAGS
+SYMCRYPT_PE_LIBS
 TIFF_PE_CFLAGS
 TIFF_PE_LIBS
-TOMCRYPT_PE_CFLAGS
-TOMCRYPT_PE_LIBS
 UNWIND_PE_CFLAGS
 UNWIND_PE_LIBS
 VKD3D_PE_CFLAGS
@@ -2069,7 +2053,7 @@ do
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
       as_fn_error $? "invalid feature name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
-    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
       *"
 "enable_$ac_useropt"
@@ -2095,7 +2079,7 @@ do
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
       as_fn_error $? "invalid feature name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
-    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
       *"
 "enable_$ac_useropt"
@@ -2308,7 +2292,7 @@ do
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
       as_fn_error $? "invalid package name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
-    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
       *"
 "with_$ac_useropt"
@@ -2324,7 +2308,7 @@ do
     expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
       as_fn_error $? "invalid package name: '$ac_useropt'"
     ac_useropt_orig=$ac_useropt
-    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'`
     case $ac_user_opts in
       *"
 "with_$ac_useropt"
@@ -2368,9 +2352,9 @@ Try '$0 --help' for more information"
 
   *)
     # FIXME: should be removed in autoconf 3.0.
-    printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
+    printf '%s\n' "$as_me: WARNING: you should use --build, --host, --target" >&2
     expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
-      printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
+      printf '%s\n' "$as_me: WARNING: invalid host type: $ac_option" >&2
     : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
     ;;
 
@@ -2378,7 +2362,7 @@ Try '$0 --help' for more information"
 done
 
 if test -n "$ac_prev"; then
-  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  ac_option=--`printf '%s\n' $ac_prev | sed 's/_/-/g'`
   as_fn_error $? "missing argument to $ac_option"
 fi
 
@@ -2386,7 +2370,7 @@ if test -n "$ac_unrecognized_opts"; then
   case $enable_option_checking in
     no) ;;
     fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
-    *)     printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+    *)     printf '%s\n' "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
   esac
 fi
 
@@ -2450,7 +2434,7 @@ $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
 	 X"$as_myself" : 'X\(//\)[^/]' \| \
 	 X"$as_myself" : 'X\(//\)$' \| \
 	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
-printf "%s\n" X"$as_myself" |
+printf '%s\n' X"$as_myself" |
     sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
 	    s//\1/
 	    q
@@ -2507,7 +2491,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-'configure' configures Wine 11.10 to adapt to many kinds of systems.
+'configure' configures Wine 11.11 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -2577,7 +2561,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Wine 11.10:";;
+     short | recursive ) echo "Configuration of Wine 11.11:";;
    esac
   cat <<\_ACEOF
 
@@ -2739,15 +2723,15 @@ Some influential environment variables:
               version
   SQLITE3_PE_LIBS
               Linker flags for the PE sqlite3, overriding the bundled version
+  SYMCRYPT_PE_CFLAGS
+              C compiler flags for the PE symcrypt, overriding the bundled
+              version
+  SYMCRYPT_PE_LIBS
+              Linker flags for the PE symcrypt, overriding the bundled version
   TIFF_PE_CFLAGS
               C compiler flags for the PE tiff, overriding the bundled version
   TIFF_PE_LIBS
               Linker flags for the PE tiff, overriding the bundled version
-  TOMCRYPT_PE_CFLAGS
-              C compiler flags for the PE tomcrypt, overriding the bundled
-              version
-  TOMCRYPT_PE_LIBS
-              Linker flags for the PE tomcrypt, overriding the bundled version
   UNWIND_PE_CFLAGS
               C compiler flags for the PE unwind, overriding the bundled
               version
@@ -2876,9 +2860,9 @@ if test "$ac_init_help" = "recursive"; then
 case "$ac_dir" in
 .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
 *)
-  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  ac_dir_suffix=/`printf '%s\n' "$ac_dir" | sed 's|^\.[\\/]||'`
   # A ".." for each directory in $ac_dir_suffix.
-  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  ac_top_builddir_sub=`printf '%s\n' "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
   case $ac_top_builddir_sub in
   "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
   *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
@@ -2915,7 +2899,7 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
       echo &&
       $SHELL "$ac_srcdir/configure" --help=recursive
     else
-      printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+      printf '%s\n' "$as_me: WARNING: no configuration information is in $ac_dir" >&2
     fi || ac_status=$?
     cd "$ac_pwd" || { ac_status=$?; break; }
   done
@@ -2924,10 +2908,10 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Wine configure 11.10
-generated by GNU Autoconf 2.72
+Wine configure 11.11
+generated by GNU Autoconf 2.73
 
-Copyright (C) 2023 Free Software Foundation, Inc.
+Copyright (C) 2026 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it.
 _ACEOF
@@ -2951,7 +2935,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compile") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -2959,7 +2943,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
     mv -f conftest.er1 conftest.err
   fi
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; } && {
 	 test -z "$ac_c_werror_flag" ||
 	 test ! -s conftest.err
@@ -2967,7 +2951,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
 then :
   ac_retval=0
 else case e in #(
-  e) printf "%s\n" "$as_me: failed program was:" >&5
+  e) printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
 	ac_retval=1 ;;
@@ -2991,7 +2975,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compile") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -2999,7 +2983,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
     mv -f conftest.er1 conftest.err
   fi
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; } && {
 	 test -z "$ac_cxx_werror_flag" ||
 	 test ! -s conftest.err
@@ -3007,7 +2991,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
 then :
   ac_retval=0
 else case e in #(
-  e) printf "%s\n" "$as_me: failed program was:" >&5
+  e) printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
 	ac_retval=1 ;;
@@ -3031,7 +3015,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_link") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -3039,7 +3023,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
     mv -f conftest.er1 conftest.err
   fi
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; } && {
 	 test -z "$ac_c_werror_flag" ||
 	 test ! -s conftest.err
@@ -3050,7 +3034,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
 then :
   ac_retval=0
 else case e in #(
-  e) printf "%s\n" "$as_me: failed program was:" >&5
+  e) printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
 	ac_retval=1 ;;
@@ -3073,7 +3057,7 @@ fi
 ac_fn_c_check_header_compile ()
 {
   as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
 printf %s "checking for $2... " >&6; }
 if eval test \${$3+y}
 then :
@@ -3095,8 +3079,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
 eval ac_res=\$$3
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # ac_fn_c_check_header_compile
@@ -3114,7 +3098,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compile") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -3122,7 +3106,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
     mv -f conftest.er1 conftest.err
   fi
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; } && {
 	 test -z "$ac_objc_werror_flag" ||
 	 test ! -s conftest.err
@@ -3130,7 +3114,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
 then :
   ac_retval=0
 else case e in #(
-  e) printf "%s\n" "$as_me: failed program was:" >&5
+  e) printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
 	ac_retval=1 ;;
@@ -3147,7 +3131,7 @@ fi
 ac_fn_c_check_func ()
 {
   as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
 printf %s "checking for $2... " >&6; }
 if eval test \${$3+y}
 then :
@@ -3199,8 +3183,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 esac
 fi
 eval ac_res=\$$3
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # ac_fn_c_check_func
@@ -3217,7 +3201,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -3225,7 +3209,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
     mv -f conftest.er1 conftest.err
   fi
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; } > conftest.i && {
 	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
 	 test ! -s conftest.err
@@ -3233,7 +3217,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
 then :
   ac_retval=0
 else case e in #(
-  e) printf "%s\n" "$as_me: failed program was:" >&5
+  e) printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
     ac_retval=1 ;;
@@ -3251,7 +3235,7 @@ fi
 ac_fn_c_check_member ()
 {
   as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
 printf %s "checking for $2.$3... " >&6; }
 if eval test \${$4+y}
 then :
@@ -3301,8 +3285,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
 eval ac_res=\$$4
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # ac_fn_c_check_member
@@ -3311,7 +3295,7 @@ for ac_arg
 do
   case $ac_arg in
   *\'*)
-    ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    ac_arg=`printf '%s\n' "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
   esac
   as_fn_append ac_configure_args_raw " '$ac_arg'"
 done
@@ -3323,15 +3307,15 @@ case $ac_configure_args_raw in
     ac_unsafe_z='|&;<>()$`\\"*?[ ''	' # This string ends in space, tab.
     ac_unsafe_a="$ac_unsafe_z#~"
     ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
-    ac_configure_args_raw=`      printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+    ac_configure_args_raw=`      printf '%s\n' "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
 esac
 
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Wine $as_me 11.10, which was
-generated by GNU Autoconf 2.72.  Invocation command line was
+It was created by Wine $as_me 11.11, which was
+generated by GNU Autoconf 2.73.  Invocation command line was
 
   $ $0$ac_configure_args_raw
 
@@ -3371,7 +3355,7 @@ do
     */) ;;
     *) as_dir=$as_dir/ ;;
   esac
-    printf "%s\n" "PATH: $as_dir"
+    printf '%s\n' "PATH: $as_dir"
   done
 IFS=$as_save_IFS
 
@@ -3406,7 +3390,7 @@ do
     | -silent | --silent | --silen | --sile | --sil)
       continue ;;
     *\'*)
-      ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+      ac_arg=`printf '%s\n' "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
     esac
     case $ac_pass in
     1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
@@ -3435,31 +3419,22 @@ done
 { ac_configure_args0=; unset ac_configure_args0;}
 { ac_configure_args1=; unset ac_configure_args1;}
 
-# When interrupted or exit'd, cleanup temporary files, and complete
-# config.log.  We remove comments because anyway the quotes in there
-# would cause problems or look ugly.
-# WARNING: Use '\'' to represent an apostrophe within the trap.
-# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
-trap 'exit_status=$?
-  # Sanitize IFS.
-  IFS=" ""	$as_nl"
-  # Save into config.log some information that might help in debugging.
-  {
-    echo
-
-    printf "%s\n" "## ---------------- ##
-## Cache variables. ##
-## ---------------- ##"
-    echo
-    # The following way of writing the cache mishandles newlines in values,
+# Dump the cache to stdout.  It can be in a pipe (this is a requirement).
+ac_cache_dump ()
+{
+  # The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
 (
-  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
     eval ac_val=\$$ac_var
     case $ac_val in #(
     *${as_nl}*)
       case $ac_var in #(
-      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
-printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      *_cv_*) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf '%s\n' "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
       esac
       case $ac_var in #(
       _ | IFS | as_nl) ;; #(
@@ -3468,67 +3443,95 @@ printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;}
       esac ;;
     esac
   done
+
   (set) 2>&1 |
-    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
     *${as_nl}ac_space=\ *)
+      # 'set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
       sed -n \
-	"s/'\''/'\''\\\\'\'''\''/g;
-	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
       ;; #(
     *)
+      # 'set' quotes correctly as required by POSIX, so do not add quotes.
       sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
       ;;
     esac |
     sort
 )
-    echo
+}
+
+# Print debugging info to stdout.
+ac_dump_debugging_info ()
+{
+  echo
+
+  printf '%s\n' "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+  echo
+  ac_cache_dump
+  echo
 
-    printf "%s\n" "## ----------------- ##
+  printf '%s\n' "## ----------------- ##
 ## Output variables. ##
 ## ----------------- ##"
+  echo
+  for ac_var in $ac_subst_vars
+  do
+    eval ac_val=\$$ac_var
+    case $ac_val in
+    *\'*) ac_val=`printf '%s\n' "$ac_val" | sed "s/'/'\\\\\\\\''/g"`;;
+    esac
+    printf '%s\n' "$ac_var='$ac_val'"
+  done | sort
+  echo
+
+  if test -n "$ac_subst_files"; then
+    printf '%s\n' "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
     echo
-    for ac_var in $ac_subst_vars
+    for ac_var in $ac_subst_files
     do
       eval ac_val=\$$ac_var
       case $ac_val in
-      *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      *\'*) ac_val=`printf '%s\n' "$ac_val" | sed "s/'/'\\\\\\\\''/g"`;;
       esac
-      printf "%s\n" "$ac_var='\''$ac_val'\''"
+      printf '%s\n' "$ac_var='$ac_val'"
     done | sort
     echo
+  fi
 
-    if test -n "$ac_subst_files"; then
-      printf "%s\n" "## ------------------- ##
-## File substitutions. ##
-## ------------------- ##"
-      echo
-      for ac_var in $ac_subst_files
-      do
-	eval ac_val=\$$ac_var
-	case $ac_val in
-	*\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
-	esac
-	printf "%s\n" "$ac_var='\''$ac_val'\''"
-      done | sort
-      echo
-    fi
-
-    if test -s confdefs.h; then
-      printf "%s\n" "## ----------- ##
+  if test -s confdefs.h; then
+    printf '%s\n' "## ----------- ##
 ## confdefs.h. ##
 ## ----------- ##"
-      echo
-      cat confdefs.h
-      echo
-    fi
-    test "$ac_signal" != 0 &&
-      printf "%s\n" "$as_me: caught signal $ac_signal"
-    printf "%s\n" "$as_me: exit $exit_status"
-  } >&5
-  rm -f core *.core core.conftest.* &&
+    echo
+    cat confdefs.h
+    echo
+  fi
+  test "$ac_signal" != 0 &&
+    printf '%s\n' "$as_me: caught signal $ac_signal"
+  printf '%s\n' "$as_me: exit $exit_status"
+}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.
+ac_exit_trap ()
+{
+  exit_status=
+  # Sanitize IFS.
+  IFS=" ""	$as_nl"
+  # Save into config.log some information that might help in debugging.
+  ac_dump_debugging_info >&5
+  eval "rm -f $ac_clean_CONFIG_STATUS core *.core core.conftest.*" &&
     rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
     exit $exit_status
-' 0
+}
+
+trap 'ac_exit_trap $?' 0
 for ac_signal in 1 2 13 15; do
   trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
 done
@@ -3537,21 +3540,21 @@ ac_signal=0
 # confdefs.h avoids OS command line length limits that DEFS can exceed.
 rm -f -r conftest* confdefs.h
 
-printf "%s\n" "/* confdefs.h */" > confdefs.h
+printf '%s\n' "/* confdefs.h */" > confdefs.h
 
 # Predefined preprocessor variables.
 
-printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
+printf '%s\n' "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
 
-printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
+printf '%s\n' "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
 
-printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
+printf '%s\n' "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
 
-printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
+printf '%s\n' "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
 
-printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
+printf '%s\n' "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
 
-printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
+printf '%s\n' "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
 
 
 # Let the site file select an alternate cache file if it wants to.
@@ -3573,12 +3576,12 @@ do
     ac_site_file=./$ac_site_file ;;
 esac
   if test -f "$ac_site_file" && test -r "$ac_site_file"; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
-printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf '%s\n' "$as_me: loading site script $ac_site_file" >&6;}
     sed 's/^/| /' "$ac_site_file" >&5
     . "$ac_site_file" \
-      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+      || { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "failed to load site script $ac_site_file
 See 'config.log' for more details" "$LINENO" 5; }
   fi
@@ -3588,27 +3591,120 @@ if test -r "$cache_file"; then
   # Some versions of bash will fail to source /dev/null (special files
   # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
   if test /dev/null != "$cache_file" && test -f "$cache_file"; then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
-printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf '%s\n' "$as_me: loading cache $cache_file" >&6;}
     case $cache_file in
       [\\/]* | ?:[\\/]* ) . "$cache_file";;
       *)                      . "./$cache_file";;
     esac
   fi
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
-printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf '%s\n' "$as_me: creating cache $cache_file" >&6;}
   >$cache_file
 fi
 
+# Test code for whether the C compiler supports C23 (global declarations)
+ac_c_conftest_c23_globals='
+/* Does the compiler advertise conformance to C17 or earlier?
+   Although GCC 14 does not do that, even with -std=gnu23,
+   it is close enough, and defines __STDC_VERSION == 202000L.  */
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ <= 201710L
+# error "Compiler advertises conformance to C17 or earlier"
+#endif
+
+// Check alignas.
+char alignas (double) c23_aligned_as_double;
+char alignas (0) c23_no_special_alignment;
+extern char c23_aligned_as_int;
+char alignas (0) alignas (int) c23_aligned_as_int;
+
+// Check alignof.
+enum
+{
+  c23_int_alignment = alignof (int),
+  c23_int_array_alignment = alignof (int[100]),
+  c23_char_alignment = alignof (char)
+};
+static_assert (0 < -alignof (int), "alignof is signed");
+
+int function_with_unnamed_parameter (int) { return 0; }
+
+void c23_noreturn ();
+
+/* Test parsing of string and char UTF-8 literals (including hex escapes).
+   The parens pacify GCC 15.  */
+bool use_u8 = (!sizeof u8"\xFF") == (!u8'\''x'\'');
+
+bool check_that_bool_works = true | false | !nullptr;
+#if !true
+# error "true does not work in #if"
+#endif
+#if false
+#elifdef __STDC_VERSION__
+#else
+# error "#elifdef does not work"
+#endif
+
+#ifndef __has_c_attribute
+# error "__has_c_attribute not defined"
+#endif
+
+#ifndef __has_include
+# error "__has_include not defined"
+#endif
+
+#define LPAREN() (
+#define FORTY_TWO(x) 42
+#define VA_OPT_TEST(r, x, ...) __VA_OPT__ (FORTY_TWO r x))
+static_assert (VA_OPT_TEST (LPAREN (), 0, <:-) == 42);
+
+static_assert (0b101010 == 42);
+static_assert (0B101010 == 42);
+static_assert (0xDEAD'\''BEEF == 3'\''735'\''928'\''559);
+static_assert (0.500'\''000'\''000 == 0.5);
+
+enum unsignedish : unsigned int { uione = 1 };
+static_assert (0 < -uione);
+
+#include <stddef.h>
+constexpr nullptr_t null_pointer = nullptr;
+
+static typeof (1 + 1L) two () { return 2; }
+static long int three () { return 3; }
+'
+
+# Test code for whether the C compiler supports C23 (body of main).
+ac_c_conftest_c23_main='
+  {
+    label_before_declaration:
+      int arr[10] = {};
+      if (arr[0])
+        goto label_before_declaration;
+      if (!arr[0])
+        goto label_at_end_of_block;
+    label_at_end_of_block:
+  }
+  ok |= !null_pointer;
+  ok |= two != three;
+'
+
+# Test code for whether the C compiler supports C23 (complete).
+ac_c_conftest_c23_program="${ac_c_conftest_c23_globals}
+
+int
+main (int, char **)
+{
+  int ok = 0;
+  ${ac_c_conftest_c23_main}
+  return ok;
+}
+"
+
 # Test code for whether the C compiler supports C89 (global declarations)
 ac_c_conftest_c89_globals='
-/* Does the compiler advertise C89 conformance?
-   Do not test the value of __STDC__, because some compilers set it to 0
-   while being otherwise adequately conformant. */
-#if !defined __STDC__
-# error "Compiler does not advertise C89 conformance"
-#endif
+/* Do not test the value of __STDC__, because some compilers define it to 0
+   or do not define it, while otherwise adequately conforming.  */
 
 #include <stddef.h>
 #include <stdarg.h>
@@ -3688,7 +3784,8 @@ extern void free (void *);
 
 // Check varargs macros.  These examples are taken from C99 6.10.3.5.
 // dprintf is used instead of fprintf to avoid needing to declare
-// FILE and stderr.
+// FILE and stderr, and "aND" is used instead of "and" to work around
+// GCC bug 40564 which is irrelevant here.
 #define debug(...) dprintf (2, __VA_ARGS__)
 #define showlist(...) puts (#__VA_ARGS__)
 #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
@@ -3699,7 +3796,7 @@ test_varargs_macros (void)
   int y = 5678;
   debug ("Flag");
   debug ("X = %d\n", x);
-  showlist (The first, second, and third items.);
+  showlist (The first, second, aND third items.);
   report (x>y, "x is %d but y is %d", x, y);
 }
 
@@ -3787,15 +3884,15 @@ ac_c_conftest_c99_main='
   // Check restrict.
   if (test_restrict ("String literal") == 0)
     success = true;
-  char *restrict newvar = "Another string";
+  const char *restrict newvar = "Another string";
 
   // Check varargs.
   success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
   test_varargs_macros ();
 
   // Check flexible array members.
-  struct incomplete_array *ia =
-    malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+  static struct incomplete_array *volatile incomplete_array_pointer;
+  struct incomplete_array *ia = incomplete_array_pointer;
   ia->datasize = 10;
   for (int i = 0; i < ia->datasize; ++i)
     ia->data[i] = i * 1.234;
@@ -3811,13 +3908,12 @@ ac_c_conftest_c99_main='
 
   ni.number = 58;
 
-  int dynamic_array[ni.number];
-  dynamic_array[0] = argv[0][0];
-  dynamic_array[ni.number - 1] = 543;
+  // Do not test for VLAs, as some otherwise-conforming compilers lack them.
+  // C code should instead use __STDC_NO_VLA__; see Autoconf manual.
 
   // work around unused variable warnings
   ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
-	 || dynamic_array[ni.number - 1] != 543);
+	 || ni.number != 58);
 '
 
 # Test code for whether the C compiler supports C11 (global declarations)
@@ -3926,222 +4022,6 @@ main (int argc, char **argv)
 }
 "
 
-# Test code for whether the C++ compiler supports C++98 (global declarations)
-ac_cxx_conftest_cxx98_globals='
-// Does the compiler advertise C++98 conformance?
-#if !defined __cplusplus || __cplusplus < 199711L
-# error "Compiler does not advertise C++98 conformance"
-#endif
-
-// These inclusions are to reject old compilers that
-// lack the unsuffixed header files.
-#include <cstdlib>
-#include <exception>
-
-// <cassert> and <cstring> are *not* freestanding headers in C++98.
-extern void assert (int);
-namespace std {
-  extern int strcmp (const char *, const char *);
-}
-
-// Namespaces, exceptions, and templates were all added after "C++ 2.0".
-using std::exception;
-using std::strcmp;
-
-namespace {
-
-void test_exception_syntax()
-{
-  try {
-    throw "test";
-  } catch (const char *s) {
-    // Extra parentheses suppress a warning when building autoconf itself,
-    // due to lint rules shared with more typical C programs.
-    assert (!(strcmp) (s, "test"));
-  }
-}
-
-template <typename T> struct test_template
-{
-  T const val;
-  explicit test_template(T t) : val(t) {}
-  template <typename U> T add(U u) { return static_cast<T>(u) + val; }
-};
-
-} // anonymous namespace
-'
-
-# Test code for whether the C++ compiler supports C++98 (body of main)
-ac_cxx_conftest_cxx98_main='
-  assert (argc);
-  assert (! argv[0]);
-{
-  test_exception_syntax ();
-  test_template<double> tt (2.0);
-  assert (tt.add (4) == 6.0);
-  assert (true && !false);
-}
-'
-
-# Test code for whether the C++ compiler supports C++11 (global declarations)
-ac_cxx_conftest_cxx11_globals='
-// Does the compiler advertise C++ 2011 conformance?
-#if !defined __cplusplus || __cplusplus < 201103L
-# error "Compiler does not advertise C++11 conformance"
-#endif
-
-namespace cxx11test
-{
-  constexpr int get_val() { return 20; }
-
-  struct testinit
-  {
-    int i;
-    double d;
-  };
-
-  class delegate
-  {
-  public:
-    delegate(int n) : n(n) {}
-    delegate(): delegate(2354) {}
-
-    virtual int getval() { return this->n; };
-  protected:
-    int n;
-  };
-
-  class overridden : public delegate
-  {
-  public:
-    overridden(int n): delegate(n) {}
-    virtual int getval() override final { return this->n * 2; }
-  };
-
-  class nocopy
-  {
-  public:
-    nocopy(int i): i(i) {}
-    nocopy() = default;
-    nocopy(const nocopy&) = delete;
-    nocopy & operator=(const nocopy&) = delete;
-  private:
-    int i;
-  };
-
-  // for testing lambda expressions
-  template <typename Ret, typename Fn> Ret eval(Fn f, Ret v)
-  {
-    return f(v);
-  }
-
-  // for testing variadic templates and trailing return types
-  template <typename V> auto sum(V first) -> V
-  {
-    return first;
-  }
-  template <typename V, typename... Args> auto sum(V first, Args... rest) -> V
-  {
-    return first + sum(rest...);
-  }
-}
-'
-
-# Test code for whether the C++ compiler supports C++11 (body of main)
-ac_cxx_conftest_cxx11_main='
-{
-  // Test auto and decltype
-  auto a1 = 6538;
-  auto a2 = 48573953.4;
-  auto a3 = "String literal";
-
-  int total = 0;
-  for (auto i = a3; *i; ++i) { total += *i; }
-
-  decltype(a2) a4 = 34895.034;
-}
-{
-  // Test constexpr
-  short sa[cxx11test::get_val()] = { 0 };
-}
-{
-  // Test initializer lists
-  cxx11test::testinit il = { 4323, 435234.23544 };
-}
-{
-  // Test range-based for
-  int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3,
-                 14, 19, 17, 8, 6, 20, 16, 2, 11, 1};
-  for (auto &x : array) { x += 23; }
-}
-{
-  // Test lambda expressions
-  using cxx11test::eval;
-  assert (eval ([](int x) { return x*2; }, 21) == 42);
-  double d = 2.0;
-  assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0);
-  assert (d == 5.0);
-  assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0);
-  assert (d == 5.0);
-}
-{
-  // Test use of variadic templates
-  using cxx11test::sum;
-  auto a = sum(1);
-  auto b = sum(1, 2);
-  auto c = sum(1.0, 2.0, 3.0);
-}
-{
-  // Test constructor delegation
-  cxx11test::delegate d1;
-  cxx11test::delegate d2();
-  cxx11test::delegate d3(45);
-}
-{
-  // Test override and final
-  cxx11test::overridden o1(55464);
-}
-{
-  // Test nullptr
-  char *c = nullptr;
-}
-{
-  // Test template brackets
-  test_template<::test_template<int>> v(test_template<int>(12));
-}
-{
-  // Unicode literals
-  char const *utf8 = u8"UTF-8 string \u2500";
-  char16_t const *utf16 = u"UTF-8 string \u2500";
-  char32_t const *utf32 = U"UTF-32 string \u2500";
-}
-'
-
-# Test code for whether the C compiler supports C++11 (complete).
-ac_cxx_conftest_cxx11_program="${ac_cxx_conftest_cxx98_globals}
-${ac_cxx_conftest_cxx11_globals}
-
-int
-main (int argc, char **argv)
-{
-  int ok = 0;
-  ${ac_cxx_conftest_cxx98_main}
-  ${ac_cxx_conftest_cxx11_main}
-  return ok;
-}
-"
-
-# Test code for whether the C compiler supports C++98 (complete).
-ac_cxx_conftest_cxx98_program="${ac_cxx_conftest_cxx98_globals}
-int
-main (int argc, char **argv)
-{
-  int ok = 0;
-  ${ac_cxx_conftest_cxx98_main}
-  return ok;
-}
-"
-
 as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
 as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
 as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
@@ -4165,7 +4045,7 @@ ac_aux_dir_candidates="${srcdir}/tools"
 # $ac_aux_dir_candidates and give up.
 ac_missing_aux_files=""
 ac_first_candidate=:
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 as_found=false
 for as_dir in $ac_aux_dir_candidates
@@ -4178,7 +4058,7 @@ do
   esac
   as_found=:
 
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}:  trying $as_dir" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}:  trying $as_dir" >&5
   ac_aux_dir_found=yes
   ac_install_sh=
   for ac_aux in $ac_aux_files
@@ -4189,13 +4069,13 @@ do
     if test x"$ac_aux" = x"install-sh"
     then
       if test -f "${as_dir}install-sh"; then
-        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install-sh found" >&5
+        printf '%s\n' "$as_me:${as_lineno-$LINENO}:   ${as_dir}install-sh found" >&5
         ac_install_sh="${as_dir}install-sh -c"
       elif test -f "${as_dir}install.sh"; then
-        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install.sh found" >&5
+        printf '%s\n' "$as_me:${as_lineno-$LINENO}:   ${as_dir}install.sh found" >&5
         ac_install_sh="${as_dir}install.sh -c"
       elif test -f "${as_dir}shtool"; then
-        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}shtool found" >&5
+        printf '%s\n' "$as_me:${as_lineno-$LINENO}:   ${as_dir}shtool found" >&5
         ac_install_sh="${as_dir}shtool install -c"
       else
         ac_aux_dir_found=no
@@ -4207,7 +4087,7 @@ do
       fi
     else
       if test -f "${as_dir}${ac_aux}"; then
-        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}${ac_aux} found" >&5
+        printf '%s\n' "$as_me:${as_lineno-$LINENO}:   ${as_dir}${ac_aux} found" >&5
       else
         ac_aux_dir_found=no
         if $ac_first_candidate; then
@@ -4260,38 +4140,44 @@ for ac_var in $ac_precious_vars; do
   eval ac_new_val=\$ac_env_${ac_var}_value
   case $ac_old_set,$ac_new_set in
     set,)
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5
-printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;}
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5
+printf '%s\n' "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;}
       ac_cache_corrupted=: ;;
     ,set)
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5
-printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;}
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5
+printf '%s\n' "$as_me: error: '$ac_var' was not set in the previous run" >&2;}
       ac_cache_corrupted=: ;;
     ,);;
     *)
       if test "x$ac_old_val" != "x$ac_new_val"; then
 	# differences in whitespace do not lead to failure.
-	ac_old_val_w=`echo x $ac_old_val`
-	ac_new_val_w=`echo x $ac_new_val`
+	ac_old_val_w=
+	for ac_val in x $ac_old_val; do
+	  ac_old_val_w="$ac_old_val_w $ac_val"
+	done
+	ac_new_val_w=
+	for ac_val in x $ac_new_val; do
+	  ac_new_val_w="$ac_new_val_w $ac_val"
+	done
 	if test "$ac_old_val_w" != "$ac_new_val_w"; then
-	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5
-printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;}
+	  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5
+printf '%s\n' "$as_me: error: '$ac_var' has changed since the previous run:" >&2;}
 	  ac_cache_corrupted=:
 	else
-	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5
-printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;}
+	  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5
+printf '%s\n' "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;}
 	  eval $ac_var=\$ac_old_val
 	fi
-	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  '$ac_old_val'" >&5
-printf "%s\n" "$as_me:   former value:  '$ac_old_val'" >&2;}
-	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: '$ac_new_val'" >&5
-printf "%s\n" "$as_me:   current value: '$ac_new_val'" >&2;}
+	{ printf '%s\n' "$as_me:${as_lineno-$LINENO}:   former value:  '$ac_old_val'" >&5
+printf '%s\n' "$as_me:   former value:  '$ac_old_val'" >&2;}
+	{ printf '%s\n' "$as_me:${as_lineno-$LINENO}:   current value: '$ac_new_val'" >&5
+printf '%s\n' "$as_me:   current value: '$ac_new_val'" >&2;}
       fi;;
   esac
   # Pass precious variables to config.status.
   if test "$ac_new_set" = set; then
     case $ac_new_val in
-    *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *\'*) ac_arg=$ac_var=`printf '%s\n' "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
     *) ac_arg=$ac_var=$ac_new_val ;;
     esac
     case " $ac_configure_args " in
@@ -4301,10 +4187,10 @@ printf "%s\n" "$as_me:   current value: '$ac_new_val'" >&2;}
   fi
 done
 if $ac_cache_corrupted; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
-printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf '%s\n' "$as_me: error: changes in the environment can compromise the build" >&2;}
   as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file'
 	    and start over" "$LINENO" 5
 fi
@@ -4312,6 +4198,23 @@ fi
 ## Main body of script. ##
 ## -------------------- ##
 
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -4720,7 +4623,7 @@ fi
 $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 ||
   as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
 printf %s "checking build system type... " >&6; }
 if test ${ac_cv_build+y}
 then :
@@ -4736,8 +4639,8 @@ ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` ||
  ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
-printf "%s\n" "$ac_cv_build" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf '%s\n' "$ac_cv_build" >&6; }
 case $ac_cv_build in
 *-*-*) ;;
 *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
@@ -4753,10 +4656,10 @@ shift; shift
 # except with old shells:
 build_os=$*
 IFS=$ac_save_IFS
-case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+case $build_os in *\ *) build_os=`printf '%s\n' "$build_os" | sed 's/ /-/g'`;; esac
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
 printf %s "checking host system type... " >&6; }
 if test ${ac_cv_host+y}
 then :
@@ -4771,8 +4674,8 @@ fi
  ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
-printf "%s\n" "$ac_cv_host" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf '%s\n' "$ac_cv_host" >&6; }
 case $ac_cv_host in
 *-*-*) ;;
 *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
@@ -4788,7 +4691,7 @@ shift; shift
 # except with old shells:
 host_os=$*
 IFS=$ac_save_IFS
-case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+case $host_os in *\ *) host_os=`printf '%s\n' "$host_os" | sed 's/ /-/g'`;; esac
 
 
 
@@ -4801,7 +4704,7 @@ for ac_prog in sarif-converter
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_SARIF_CONVERTER+y}
 then :
@@ -4822,7 +4725,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_SARIF_CONVERTER="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -4834,11 +4737,11 @@ esac
 fi
 SARIF_CONVERTER=$ac_cv_prog_SARIF_CONVERTER
 if test -n "$SARIF_CONVERTER"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SARIF_CONVERTER" >&5
-printf "%s\n" "$SARIF_CONVERTER" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $SARIF_CONVERTER" >&5
+printf '%s\n' "$SARIF_CONVERTER" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -4858,6 +4761,9 @@ fi
 
 
 
+
+
+
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -4866,7 +4772,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}gcc; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CC+y}
 then :
@@ -4887,7 +4793,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}gcc"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -4899,11 +4805,11 @@ esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-printf "%s\n" "$CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf '%s\n' "$CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -4912,7 +4818,7 @@ if test -z "$ac_cv_prog_CC"; then
   ac_ct_CC=$CC
   # Extract the first word of "gcc", so it can be a program name with args.
 set dummy gcc; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_CC+y}
 then :
@@ -4933,7 +4839,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="gcc"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -4945,11 +4851,11 @@ esac
 fi
 ac_ct_CC=$ac_cv_prog_ac_ct_CC
 if test -n "$ac_ct_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-printf "%s\n" "$ac_ct_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf '%s\n' "$ac_ct_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_CC" = x; then
@@ -4957,8 +4863,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     CC=$ac_ct_CC
@@ -4971,7 +4877,7 @@ if test -z "$CC"; then
           if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CC+y}
 then :
@@ -4992,7 +4898,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}cc"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5004,11 +4910,11 @@ esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-printf "%s\n" "$CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf '%s\n' "$CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5017,7 +4923,7 @@ fi
 if test -z "$CC"; then
   # Extract the first word of "cc", so it can be a program name with args.
 set dummy cc; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CC+y}
 then :
@@ -5043,7 +4949,7 @@ do
        continue
      fi
     ac_cv_prog_CC="cc"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5067,11 +4973,11 @@ esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-printf "%s\n" "$CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf '%s\n' "$CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5082,7 +4988,7 @@ if test -z "$CC"; then
   do
     # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CC+y}
 then :
@@ -5103,7 +5009,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5115,11 +5021,11 @@ esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-printf "%s\n" "$CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf '%s\n' "$CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5132,7 +5038,7 @@ if test -z "$CC"; then
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_CC+y}
 then :
@@ -5153,7 +5059,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5165,11 +5071,11 @@ esac
 fi
 ac_ct_CC=$ac_cv_prog_ac_ct_CC
 if test -n "$ac_ct_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-printf "%s\n" "$ac_ct_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf '%s\n' "$ac_ct_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5181,8 +5087,8 @@ done
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     CC=$ac_ct_CC
@@ -5194,7 +5100,7 @@ if test -z "$CC"; then
   if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
 set dummy ${ac_tool_prefix}clang; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CC+y}
 then :
@@ -5215,7 +5121,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}clang"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5227,11 +5133,11 @@ esac
 fi
 CC=$ac_cv_prog_CC
 if test -n "$CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-printf "%s\n" "$CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf '%s\n' "$CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5240,7 +5146,7 @@ if test -z "$ac_cv_prog_CC"; then
   ac_ct_CC=$CC
   # Extract the first word of "clang", so it can be a program name with args.
 set dummy clang; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_CC+y}
 then :
@@ -5261,7 +5167,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="clang"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5273,11 +5179,11 @@ esac
 fi
 ac_ct_CC=$ac_cv_prog_ac_ct_CC
 if test -n "$ac_ct_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-printf "%s\n" "$ac_ct_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf '%s\n' "$ac_ct_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_CC" = x; then
@@ -5285,8 +5191,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     CC=$ac_ct_CC
@@ -5298,13 +5204,13 @@ fi
 fi
 
 
-test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+test -z "$CC" && { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "no acceptable C compiler found in \$PATH
 See 'config.log' for more details" "$LINENO" 5; }
 
 # Provide some information about the compiler.
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
 set X $ac_compile
 ac_compiler=$2
 for ac_option in --version -v -V -qversion -version; do
@@ -5314,7 +5220,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compiler $ac_option >&5") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -5324,7 +5230,7 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 done
 
@@ -5344,9 +5250,9 @@ ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
 # Try to create an executable without -o first, disregard a.out.
 # It will help us diagnose broken compilers, and finding out an intuition
 # of exeext.
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
 printf %s "checking whether the C compiler works... " >&6; }
-ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+ac_link_default=`printf '%s\n' "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
 
 # The possible output files:
 ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
@@ -5367,10 +5273,10 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_link_default") 2>&5
   ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 then :
   # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'.
@@ -5411,29 +5317,29 @@ esac
 fi
 if test -z "$ac_file"
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-printf "%s\n" "$as_me: failed program was:" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
+printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+{ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error 77 "C compiler cannot create executables
 See 'config.log' for more details" "$LINENO" 5; }
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; } ;;
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf '%s\n' "yes" >&6; } ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
 printf %s "checking for C compiler default output file name... " >&6; }
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
-printf "%s\n" "$ac_file" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf '%s\n' "$ac_file" >&6; }
 ac_exeext=$ac_cv_exeext
 
 rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
 ac_clean_files=$ac_clean_files_save
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
 printf %s "checking for suffix of executables... " >&6; }
 if { { ac_try="$ac_link"
 case "(($ac_try" in
@@ -5441,10 +5347,10 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_link") 2>&5
   ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 then :
   # If both 'conftest.exe' and 'conftest' are 'present' (well, observable)
@@ -5461,15 +5367,15 @@ for ac_file in conftest.exe conftest conftest.*; do
   esac
 done
 else case e in #(
-  e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+  e) { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "cannot compute suffix of executables: cannot compile and link
 See 'config.log' for more details" "$LINENO" 5; } ;;
 esac
 fi
 rm -f conftest conftest$ac_cv_exeext
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
-printf "%s\n" "$ac_cv_exeext" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf '%s\n' "$ac_cv_exeext" >&6; }
 
 rm -f conftest.$ac_ext
 EXEEXT=$ac_cv_exeext
@@ -5492,7 +5398,7 @@ _ACEOF
 ac_clean_files="$ac_clean_files conftest.out"
 # Check that the compiler produces executables we can run.  If not, either
 # the compiler is broken, or we cross compile.
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
 printf %s "checking whether we are cross compiling... " >&6; }
 if test "$cross_compiling" != yes; then
   { { ac_try="$ac_link"
@@ -5501,10 +5407,10 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_link") 2>&5
   ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
   if { ac_try='./conftest$ac_cv_exeext'
   { { case "(($ac_try" in
@@ -5512,31 +5418,31 @@ printf "%s\n" "$ac_try_echo"; } >&5
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_try") 2>&5
   ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; }; then
     cross_compiling=no
   else
     if test "$cross_compiling" = maybe; then
 	cross_compiling=yes
     else
-	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+	{ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error 77 "cannot run C compiled programs.
 If you meant to cross compile, use '--host'.
 See 'config.log' for more details" "$LINENO" 5; }
     fi
   fi
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
-printf "%s\n" "$cross_compiling" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf '%s\n' "$cross_compiling" >&6; }
 
 rm -f conftest.$ac_ext conftest$ac_cv_exeext \
   conftest.o conftest.obj conftest.out
 ac_clean_files=$ac_clean_files_save
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
 printf %s "checking for suffix of object files... " >&6; }
 if test ${ac_cv_objext+y}
 then :
@@ -5560,10 +5466,10 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compile") 2>&5
   ac_status=$?
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 then :
   for ac_file in conftest.o conftest.obj conftest.*; do
@@ -5575,11 +5481,11 @@ then :
   esac
 done
 else case e in #(
-  e) printf "%s\n" "$as_me: failed program was:" >&5
+  e) printf '%s\n' "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+{ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "cannot compute suffix of object files: cannot compile
 See 'config.log' for more details" "$LINENO" 5; } ;;
 esac
@@ -5587,11 +5493,11 @@ fi
 rm -f conftest.$ac_cv_objext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
-printf "%s\n" "$ac_cv_objext" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf '%s\n' "$ac_cv_objext" >&6; }
 OBJEXT=$ac_cv_objext
 ac_objext=$OBJEXT
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
 printf %s "checking whether the compiler supports GNU C... " >&6; }
 if test ${ac_cv_c_compiler_gnu+y}
 then :
@@ -5623,8 +5529,8 @@ ac_cv_c_compiler_gnu=$ac_compiler_gnu
  ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
-printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf '%s\n' "$ac_cv_c_compiler_gnu" >&6; }
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 if test $ac_compiler_gnu = yes; then
@@ -5634,7 +5540,7 @@ else
 fi
 ac_test_CFLAGS=${CFLAGS+y}
 ac_save_CFLAGS=$CFLAGS
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
 printf %s "checking whether $CC accepts -g... " >&6; }
 if test ${ac_cv_prog_cc_g+y}
 then :
@@ -5702,8 +5608,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
    ac_c_werror_flag=$ac_save_c_werror_flag ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
-printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf '%s\n' "$ac_cv_prog_cc_g" >&6; }
 if test $ac_test_CFLAGS; then
   CFLAGS=$ac_save_CFLAGS
 elif test $ac_cv_prog_cc_g = yes; then
@@ -5722,7 +5628,56 @@ fi
 ac_prog_cc_stdc=no
 if test x$ac_prog_cc_stdc = xno
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C23 features" >&5
+printf %s "checking for $CC option to enable C23 features... " >&6; }
+if test ${ac_cv_prog_cc_c23+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) ac_cv_prog_cc_c23=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c23_program
+_ACEOF
+for ac_arg in '' -std=gnu23
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c23=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c23" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC ;;
+esac
+fi
+
+if test "x$ac_cv_prog_cc_c23" = xno
+then :
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf '%s\n' "unsupported" >&6; }
+else case e in #(
+  e) if test "x$ac_cv_prog_cc_c23" = x
+then :
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf '%s\n' "none needed" >&6; }
+else case e in #(
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c23" >&5
+printf '%s\n' "$ac_cv_prog_cc_c23" >&6; }
+     CC="$CC $ac_cv_prog_cc_c23" ;;
+esac
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c23
+  ac_prog_cc_stdc=c23 ;;
+esac
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
 printf %s "checking for $CC option to enable C11 features... " >&6; }
 if test ${ac_cv_prog_cc_c11+y}
 then :
@@ -5734,7 +5689,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 $ac_c_conftest_c11_program
 _ACEOF
-for ac_arg in '' -std=gnu11
+for ac_arg in '' -std=gnu11 -std:c11
 do
   CC="$ac_save_CC $ac_arg"
   if ac_fn_c_try_compile "$LINENO"
@@ -5751,16 +5706,16 @@ fi
 
 if test "x$ac_cv_prog_cc_c11" = xno
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf '%s\n' "unsupported" >&6; }
 else case e in #(
   e) if test "x$ac_cv_prog_cc_c11" = x
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf '%s\n' "none needed" >&6; }
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
-printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf '%s\n' "$ac_cv_prog_cc_c11" >&6; }
      CC="$CC $ac_cv_prog_cc_c11" ;;
 esac
 fi
@@ -5771,7 +5726,7 @@ fi
 fi
 if test x$ac_prog_cc_stdc = xno
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
 printf %s "checking for $CC option to enable C99 features... " >&6; }
 if test ${ac_cv_prog_cc_c99+y}
 then :
@@ -5800,16 +5755,16 @@ fi
 
 if test "x$ac_cv_prog_cc_c99" = xno
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf '%s\n' "unsupported" >&6; }
 else case e in #(
   e) if test "x$ac_cv_prog_cc_c99" = x
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf '%s\n' "none needed" >&6; }
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
-printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf '%s\n' "$ac_cv_prog_cc_c99" >&6; }
      CC="$CC $ac_cv_prog_cc_c99" ;;
 esac
 fi
@@ -5820,7 +5775,7 @@ fi
 fi
 if test x$ac_prog_cc_stdc = xno
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
 printf %s "checking for $CC option to enable C89 features... " >&6; }
 if test ${ac_cv_prog_cc_c89+y}
 then :
@@ -5849,16 +5804,16 @@ fi
 
 if test "x$ac_cv_prog_cc_c89" = xno
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf '%s\n' "unsupported" >&6; }
 else case e in #(
   e) if test "x$ac_cv_prog_cc_c89" = x
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf '%s\n' "none needed" >&6; }
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
-printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf '%s\n' "$ac_cv_prog_cc_c89" >&6; }
      CC="$CC $ac_cv_prog_cc_c89" ;;
 esac
 fi
@@ -5874,12 +5829,6 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
-
-
-
-
-
-
 ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -5894,7 +5843,7 @@ if test -z "$CXX"; then
   do
     # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CXX+y}
 then :
@@ -5915,7 +5864,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5927,11 +5876,11 @@ esac
 fi
 CXX=$ac_cv_prog_CXX
 if test -n "$CXX"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
-printf "%s\n" "$CXX" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+printf '%s\n' "$CXX" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5944,7 +5893,7 @@ if test -z "$CXX"; then
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_CXX+y}
 then :
@@ -5965,7 +5914,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CXX="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -5977,11 +5926,11 @@ esac
 fi
 ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
 if test -n "$ac_ct_CXX"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
-printf "%s\n" "$ac_ct_CXX" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+printf '%s\n' "$ac_ct_CXX" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -5993,8 +5942,8 @@ done
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     CXX=$ac_ct_CXX
@@ -6004,7 +5953,7 @@ fi
   fi
 fi
 # Provide some information about the compiler.
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
 set X $ac_compile
 ac_compiler=$2
 for ac_option in --version -v -V -qversion; do
@@ -6014,7 +5963,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compiler $ac_option >&5") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -6024,11 +5973,11 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 done
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5
 printf %s "checking whether the compiler supports GNU C++... " >&6; }
 if test ${ac_cv_cxx_compiler_gnu+y}
 then :
@@ -6060,8 +6009,8 @@ ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
  ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
-printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+printf '%s\n' "$ac_cv_cxx_compiler_gnu" >&6; }
 ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
 
 if test $ac_compiler_gnu = yes; then
@@ -6071,7 +6020,7 @@ else
 fi
 ac_test_CXXFLAGS=${CXXFLAGS+y}
 ac_save_CXXFLAGS=$CXXFLAGS
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
 printf %s "checking whether $CXX accepts -g... " >&6; }
 if test ${ac_cv_prog_cxx_g+y}
 then :
@@ -6106,156 +6055,56 @@ main (void)
 
   ;
   return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"
-then :
-
-else case e in #(
-  e) ac_cxx_werror_flag=$ac_save_cxx_werror_flag
-	 CXXFLAGS="-g"
-	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main (void)
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"
-then :
-  ac_cv_prog_cxx_g=yes
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-   ac_cxx_werror_flag=$ac_save_cxx_werror_flag ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
-printf "%s\n" "$ac_cv_prog_cxx_g" >&6; }
-if test $ac_test_CXXFLAGS; then
-  CXXFLAGS=$ac_save_CXXFLAGS
-elif test $ac_cv_prog_cxx_g = yes; then
-  if test "$GXX" = yes; then
-    CXXFLAGS="-g -O2"
-  else
-    CXXFLAGS="-g"
-  fi
-else
-  if test "$GXX" = yes; then
-    CXXFLAGS="-O2"
-  else
-    CXXFLAGS=
-  fi
-fi
-ac_prog_cxx_stdcxx=no
-if test x$ac_prog_cxx_stdcxx = xno
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
-printf %s "checking for $CXX option to enable C++11 features... " >&6; }
-if test ${ac_cv_prog_cxx_cxx11+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) ac_cv_prog_cxx_cxx11=no
-ac_save_CXX=$CXX
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$ac_cxx_conftest_cxx11_program
-_ACEOF
-for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA
-do
-  CXX="$ac_save_CXX $ac_arg"
-  if ac_fn_cxx_try_compile "$LINENO"
-then :
-  ac_cv_prog_cxx_cxx11=$ac_arg
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam
-  test "x$ac_cv_prog_cxx_cxx11" != "xno" && break
-done
-rm -f conftest.$ac_ext
-CXX=$ac_save_CXX ;;
-esac
-fi
-
-if test "x$ac_cv_prog_cxx_cxx11" = xno
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
-else case e in #(
-  e) if test "x$ac_cv_prog_cxx_cxx11" = x
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; }
-else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
-printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; }
-     CXX="$CXX $ac_cv_prog_cxx_cxx11" ;;
-esac
-fi
-  ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
-  ac_prog_cxx_stdcxx=cxx11 ;;
-esac
-fi
-fi
-if test x$ac_prog_cxx_stdcxx = xno
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
-printf %s "checking for $CXX option to enable C++98 features... " >&6; }
-if test ${ac_cv_prog_cxx_cxx98+y}
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
 then :
-  printf %s "(cached) " >&6
+
 else case e in #(
-  e) ac_cv_prog_cxx_cxx98=no
-ac_save_CXX=$CXX
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  e) ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+	 CXXFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-$ac_cxx_conftest_cxx98_program
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
 _ACEOF
-for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA
-do
-  CXX="$ac_save_CXX $ac_arg"
-  if ac_fn_cxx_try_compile "$LINENO"
+if ac_fn_cxx_try_compile "$LINENO"
 then :
-  ac_cv_prog_cxx_cxx98=$ac_arg
+  ac_cv_prog_cxx_g=yes
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam
-  test "x$ac_cv_prog_cxx_cxx98" != "xno" && break
-done
-rm -f conftest.$ac_ext
-CXX=$ac_save_CXX ;;
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-
-if test "x$ac_cv_prog_cxx_cxx98" = xno
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
-else case e in #(
-  e) if test "x$ac_cv_prog_cxx_cxx98" = x
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; }
-else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
-printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; }
-     CXX="$CXX $ac_cv_prog_cxx_cxx98" ;;
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-  ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
-  ac_prog_cxx_stdcxx=cxx98 ;;
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag ;;
 esac
 fi
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+printf '%s\n' "$ac_cv_prog_cxx_g" >&6; }
+if test $ac_test_CXXFLAGS; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
 fi
-
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -6265,7 +6114,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}cpp", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cpp; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CPPBIN+y}
 then :
@@ -6286,7 +6135,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CPPBIN="${ac_tool_prefix}cpp"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6298,11 +6147,11 @@ esac
 fi
 CPPBIN=$ac_cv_prog_CPPBIN
 if test -n "$CPPBIN"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPPBIN" >&5
-printf "%s\n" "$CPPBIN" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CPPBIN" >&5
+printf '%s\n' "$CPPBIN" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -6311,7 +6160,7 @@ if test -z "$ac_cv_prog_CPPBIN"; then
   ac_ct_CPPBIN=$CPPBIN
   # Extract the first word of "cpp", so it can be a program name with args.
 set dummy cpp; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_CPPBIN+y}
 then :
@@ -6332,7 +6181,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CPPBIN="cpp"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6344,11 +6193,11 @@ esac
 fi
 ac_ct_CPPBIN=$ac_cv_prog_ac_ct_CPPBIN
 if test -n "$ac_ct_CPPBIN"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CPPBIN" >&5
-printf "%s\n" "$ac_ct_CPPBIN" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CPPBIN" >&5
+printf '%s\n' "$ac_ct_CPPBIN" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_CPPBIN" = x; then
@@ -6356,8 +6205,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     CPPBIN=$ac_ct_CPPBIN
@@ -6367,12 +6216,12 @@ else
 fi
 
 
-printf "%s\n" "#define EXEEXT \"$ac_exeext\"" >>confdefs.h
+printf '%s\n' "#define EXEEXT \"$ac_exeext\"" >>confdefs.h
 
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args.
 set dummy ${ac_tool_prefix}ld; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_LD+y}
 then :
@@ -6393,7 +6242,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_LD="${ac_tool_prefix}ld"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6405,11 +6254,11 @@ esac
 fi
 LD=$ac_cv_prog_LD
 if test -n "$LD"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
-printf "%s\n" "$LD" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+printf '%s\n' "$LD" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -6418,7 +6267,7 @@ if test -z "$ac_cv_prog_LD"; then
   ac_ct_LD=$LD
   # Extract the first word of "ld", so it can be a program name with args.
 set dummy ld; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_LD+y}
 then :
@@ -6439,7 +6288,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_LD="ld"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6451,11 +6300,11 @@ esac
 fi
 ac_ct_LD=$ac_cv_prog_ac_ct_LD
 if test -n "$ac_ct_LD"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5
-printf "%s\n" "$ac_ct_LD" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5
+printf '%s\n' "$ac_ct_LD" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_LD" = x; then
@@ -6463,8 +6312,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     LD=$ac_ct_LD
@@ -6482,7 +6331,7 @@ case $host in
       CC="$CC -m32"
       CXX="$CXX -m32"
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC works" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC works" >&5
 printf %s "checking whether $CC works... " >&6; }
 if test ${wine_cv_cc_m32+y}
 then :
@@ -6510,8 +6359,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     conftest$ac_exeext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_cc_m32" >&5
-printf "%s\n" "$wine_cv_cc_m32" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_cc_m32" >&5
+printf '%s\n' "$wine_cv_cc_m32" >&6; }
       test $wine_cv_cc_m32 != no || as_fn_error $? "Cannot build a 32-bit program, you need to install 32-bit development libraries." "$LINENO" 5
       host_cpu="i386"
       notice_platform="32-bit "
@@ -6527,7 +6376,7 @@ printf "%s\n" "$wine_cv_cc_m32" >&6; }
     fi
     ;;
   arm*)
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports Thumb-2" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports Thumb-2" >&5
 printf %s "checking whether $CC supports Thumb-2... " >&6; }
 if test ${wine_cv_thumb2+y}
 then :
@@ -6555,15 +6404,15 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     conftest$ac_exeext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_thumb2" >&5
-printf "%s\n" "$wine_cv_thumb2" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_thumb2" >&5
+printf '%s\n' "$wine_cv_thumb2" >&6; }
     if test x"$wine_cv_thumb2" = xyes
     then
         CFLAGS="$CFLAGS -mthumb"
     else
         CFLAGS="$CFLAGS -marm"
     fi
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports floating point instructions" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports floating point instructions" >&5
 printf %s "checking whether $CC supports floating point instructions... " >&6; }
 if test ${wine_cv_float_abi+y}
 then :
@@ -6591,8 +6440,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     conftest$ac_exeext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_float_abi" >&5
-printf "%s\n" "$wine_cv_float_abi" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_float_abi" >&5
+printf '%s\n' "$wine_cv_float_abi" >&6; }
     test $wine_cv_float_abi != no || as_fn_error $? "The ARM target needs to support floating point instructions." "$LINENO" 5
     ;;
 esac
@@ -6606,7 +6455,7 @@ then
     test -f "$with_wine64/Makefile" || as_fn_error $? "the 64-bit tree in $with_wine64 has not been configured yet." "$LINENO" 5
     libdir64=$(grep "^libdir *=" "$with_wine64/Makefile" | sed 's/^libdir *= *//')
     if test "$libdir64" != "$libdir"; then
-        printf "%s\n" "$as_me:${as_lineno-$LINENO}: libdir64: $libdir64" >&5
+        printf '%s\n' "$as_me:${as_lineno-$LINENO}: libdir64: $libdir64" >&5
         as_fn_error $? "when using --with-wine64 you must use the same --libdir in both build trees." "$LINENO" 5
     fi
 fi
@@ -6649,7 +6498,7 @@ esac
 
 toolsdir=$with_wine_tools
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the directory containing the Wine tools" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for the directory containing the Wine tools" >&5
 printf %s "checking for the directory containing the Wine tools... " >&6; }
 if test -z "$toolsdir"; then
     if test "$cross_compiling" = "yes"; then
@@ -6667,8 +6516,8 @@ elif test -d "$toolsdir/tools/winebuild"; then
 else
     as_fn_error $? "could not find Wine tools in $toolsdir" "$LINENO" 5
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${toolsdir:-none needed}" >&5
-printf "%s\n" "${toolsdir:-none needed}" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: ${toolsdir:-none needed}" >&5
+printf '%s\n' "${toolsdir:-none needed}" >&6; }
 wine64dir=$with_wine64
 
 RUNTESTFLAGS="-q -P wine"
@@ -6685,7 +6534,7 @@ for ac_prog in flex
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_FLEX+y}
 then :
@@ -6706,7 +6555,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_FLEX="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6718,11 +6567,11 @@ esac
 fi
 FLEX=$ac_cv_prog_FLEX
 if test -n "$FLEX"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FLEX" >&5
-printf "%s\n" "$FLEX" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $FLEX" >&5
+printf '%s\n' "$FLEX" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -6734,7 +6583,7 @@ if test "$FLEX" = "none"
 then
   as_fn_error $? "no suitable flex found. Please install the 'flex' package." "$LINENO" 5
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether flex is recent enough" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether flex is recent enough" >&5
 printf %s "checking whether flex is recent enough... " >&6; }
 if test ${wine_cv_recent_flex+y}
 then :
@@ -6754,15 +6603,15 @@ EOF
     fi ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_recent_flex" >&5
-printf "%s\n" "$wine_cv_recent_flex" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_recent_flex" >&5
+printf '%s\n' "$wine_cv_recent_flex" >&6; }
 test $wine_cv_recent_flex != no || as_fn_error $? "Your flex version is too old. Please install flex version 2.5.33 or newer." "$LINENO" 5
 
 for ac_prog in bison
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_BISON+y}
 then :
@@ -6783,7 +6632,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_BISON="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6795,11 +6644,11 @@ esac
 fi
 BISON=$ac_cv_prog_BISON
 if test -n "$BISON"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BISON" >&5
-printf "%s\n" "$BISON" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $BISON" >&5
+printf '%s\n' "$BISON" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -6811,7 +6660,7 @@ if test "$BISON" = "none"
 then
   as_fn_error $? "no suitable bison found. Please install the 'bison' package." "$LINENO" 5
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether bison is recent enough" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether bison is recent enough" >&5
 printf %s "checking whether bison is recent enough... " >&6; }
 if test ${wine_cv_recent_bison+y}
 then :
@@ -6830,14 +6679,14 @@ EOF
     fi ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_recent_bison" >&5
-printf "%s\n" "$wine_cv_recent_bison" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_recent_bison" >&5
+printf '%s\n' "$wine_cv_recent_bison" >&6; }
 test $wine_cv_recent_bison != no || as_fn_error $? "Your bison version is too old. Please install bison version 3.0 or newer." "$LINENO" 5
 
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
 set dummy ${ac_tool_prefix}strip; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_STRIP+y}
 then :
@@ -6858,7 +6707,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_STRIP="${ac_tool_prefix}strip"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6870,11 +6719,11 @@ esac
 fi
 STRIP=$ac_cv_prog_STRIP
 if test -n "$STRIP"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
-printf "%s\n" "$STRIP" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+printf '%s\n' "$STRIP" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -6883,7 +6732,7 @@ if test -z "$ac_cv_prog_STRIP"; then
   ac_ct_STRIP=$STRIP
   # Extract the first word of "strip", so it can be a program name with args.
 set dummy strip; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_STRIP+y}
 then :
@@ -6904,7 +6753,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_STRIP="strip"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6916,11 +6765,11 @@ esac
 fi
 ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
 if test -n "$ac_ct_STRIP"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
-printf "%s\n" "$ac_ct_STRIP" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+printf '%s\n' "$ac_ct_STRIP" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_STRIP" = x; then
@@ -6928,8 +6777,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     STRIP=$ac_ct_STRIP
@@ -6938,22 +6787,22 @@ else
   STRIP="$ac_cv_prog_STRIP"
 fi
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
 printf %s "checking whether ln -s works... " >&6; }
 LN_S=$as_ln_s
 if test "$LN_S" = "ln -s"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf '%s\n' "yes" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
-printf "%s\n" "no, using $LN_S" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+printf '%s\n' "no, using $LN_S" >&6; }
 fi
 
 if test ${ac_tool_prefix+y}
 then :
   # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
 set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_PKG_CONFIG+y}
 then :
@@ -6974,7 +6823,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_PKG_CONFIG="${ac_tool_prefix}pkg-config"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -6986,11 +6835,11 @@ esac
 fi
 PKG_CONFIG=$ac_cv_prog_PKG_CONFIG
 if test -n "$PKG_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
-printf "%s\n" "$PKG_CONFIG" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+printf '%s\n' "$PKG_CONFIG" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7004,7 +6853,7 @@ else case e in #(
   e) { ac_cv_prog_PKG_CONFIG=; unset ac_cv_prog_PKG_CONFIG;}
      # Extract the first word of "pkg-config", so it can be a program name with args.
 set dummy pkg-config; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_PKG_CONFIG+y}
 then :
@@ -7025,7 +6874,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_PKG_CONFIG="pkg-config"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7037,11 +6886,11 @@ esac
 fi
 PKG_CONFIG=$ac_cv_prog_PKG_CONFIG
 if test -n "$PKG_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
-printf "%s\n" "$PKG_CONFIG" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+printf '%s\n' "$PKG_CONFIG" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
  ;;
@@ -7064,7 +6913,7 @@ else
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_FONTFORGE+y}
 then :
@@ -7085,7 +6934,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_FONTFORGE="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7097,11 +6946,11 @@ esac
 fi
 FONTFORGE=$ac_cv_prog_FONTFORGE
 if test -n "$FONTFORGE"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FONTFORGE" >&5
-printf "%s\n" "$FONTFORGE" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $FONTFORGE" >&5
+printf '%s\n' "$FONTFORGE" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7113,7 +6962,7 @@ test -n "$FONTFORGE" || FONTFORGE="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_RSVG+y}
 then :
@@ -7134,7 +6983,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_RSVG="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7146,11 +6995,11 @@ esac
 fi
 RSVG=$ac_cv_prog_RSVG
 if test -n "$RSVG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RSVG" >&5
-printf "%s\n" "$RSVG" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $RSVG" >&5
+printf '%s\n' "$RSVG" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7162,7 +7011,7 @@ test -n "$RSVG" || RSVG="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_CONVERT+y}
 then :
@@ -7183,7 +7032,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_CONVERT="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7195,11 +7044,11 @@ esac
 fi
 CONVERT=$ac_cv_prog_CONVERT
 if test -n "$CONVERT"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CONVERT" >&5
-printf "%s\n" "$CONVERT" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CONVERT" >&5
+printf '%s\n' "$CONVERT" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7211,7 +7060,7 @@ test -n "$CONVERT" || CONVERT="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ICOTOOL+y}
 then :
@@ -7232,7 +7081,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ICOTOOL="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7244,11 +7093,11 @@ esac
 fi
 ICOTOOL=$ac_cv_prog_ICOTOOL
 if test -n "$ICOTOOL"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ICOTOOL" >&5
-printf "%s\n" "$ICOTOOL" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ICOTOOL" >&5
+printf '%s\n' "$ICOTOOL" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7263,7 +7112,7 @@ test -n "$ICOTOOL" || ICOTOOL="false"
   then
     as_fn_error $? "You need imagemagick to rebuild icons in maintainer mode." "$LINENO" 5
   else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for recent enough imagemagick" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for recent enough imagemagick" >&5
 printf %s "checking for recent enough imagemagick... " >&6; }
     convert_version=`convert --version | head -n1`
     if test "x$convert_version" != "x"
@@ -7277,12 +7126,12 @@ printf %s "checking for recent enough imagemagick... " >&6; }
     fi
     if test "$CONVERT" = false
     then
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no ($convert_version_major.$convert_version_minor)" >&5
-printf "%s\n" "no ($convert_version_major.$convert_version_minor)" >&6; }
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no ($convert_version_major.$convert_version_minor)" >&5
+printf '%s\n' "no ($convert_version_major.$convert_version_minor)" >&6; }
       as_fn_error $? "You need imagemagick version 6.6 or newer to rebuild icons in maintainer mode." "$LINENO" 5
     else
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($convert_version_major.$convert_version_minor)" >&5
-printf "%s\n" "yes ($convert_version_major.$convert_version_minor)" >&6; }
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes ($convert_version_major.$convert_version_minor)" >&5
+printf '%s\n' "yes ($convert_version_major.$convert_version_minor)" >&6; }
     fi
   fi
 
@@ -7290,7 +7139,7 @@ printf "%s\n" "yes ($convert_version_major.$convert_version_minor)" >&6; }
   then
     as_fn_error $? "You need icotool to rebuild icons in maintainer mode." "$LINENO" 5
   else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for recent enough icotool" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for recent enough icotool" >&5
 printf %s "checking for recent enough icotool... " >&6; }
     icotool_version=`icotool --version | head -n1`
     if test "x$icotool_version" != "x"
@@ -7305,12 +7154,12 @@ printf %s "checking for recent enough icotool... " >&6; }
     fi
     if test "$ICOTOOL" = false
     then
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no ($icotool_version_major.$icotool_version_minor)" >&5
-printf "%s\n" "no ($icotool_version_major.$icotool_version_minor)" >&6; }
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no ($icotool_version_major.$icotool_version_minor)" >&5
+printf '%s\n' "no ($icotool_version_major.$icotool_version_minor)" >&6; }
       as_fn_error $? "You need icotool version 0.29.0 or newer to rebuild icons in maintainer mode." "$LINENO" 5
     else
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($icotool_version_major.$icotool_version_minor)" >&5
-printf "%s\n" "yes ($icotool_version_major.$icotool_version_minor)" >&6; }
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes ($icotool_version_major.$icotool_version_minor)" >&5
+printf '%s\n' "yes ($icotool_version_major.$icotool_version_minor)" >&6; }
     fi
   fi
 
@@ -7326,7 +7175,7 @@ then
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_MSGFMT+y}
 then :
@@ -7347,7 +7196,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_MSGFMT="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7359,11 +7208,11 @@ esac
 fi
 MSGFMT=$ac_cv_prog_MSGFMT
 if test -n "$MSGFMT"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
-printf "%s\n" "$MSGFMT" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
+printf '%s\n' "$MSGFMT" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7373,7 +7222,7 @@ test -n "$MSGFMT" || MSGFMT="false"
 
   if test "$MSGFMT" != "false"
   then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether msgfmt supports contexts" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether msgfmt supports contexts" >&5
 printf %s "checking whether msgfmt supports contexts... " >&6; }
 if test ${wine_cv_msgfmt_contexts+y}
 then :
@@ -7393,8 +7242,8 @@ EOF
     fi ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_msgfmt_contexts" >&5
-printf "%s\n" "$wine_cv_msgfmt_contexts" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_msgfmt_contexts" >&5
+printf '%s\n' "$wine_cv_msgfmt_contexts" >&6; }
     test $wine_cv_msgfmt_contexts != no || MSGFMT=false
   fi
 else
@@ -7479,7 +7328,7 @@ then :
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_aarch64_CC+y}
 then :
@@ -7500,7 +7349,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_aarch64_CC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7512,11 +7361,11 @@ esac
 fi
 aarch64_CC=$ac_cv_prog_aarch64_CC
 if test -n "$aarch64_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $aarch64_CC" >&5
-printf "%s\n" "$aarch64_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $aarch64_CC" >&5
+printf '%s\n' "$aarch64_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7530,7 +7379,7 @@ test -n "$aarch64_CC" || aarch64_CC="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_arm64ec_CC+y}
 then :
@@ -7551,7 +7400,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_arm64ec_CC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7563,11 +7412,11 @@ esac
 fi
 arm64ec_CC=$ac_cv_prog_arm64ec_CC
 if test -n "$arm64ec_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $arm64ec_CC" >&5
-printf "%s\n" "$arm64ec_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $arm64ec_CC" >&5
+printf '%s\n' "$arm64ec_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7581,7 +7430,7 @@ test -n "$arm64ec_CC" || arm64ec_CC="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_arm_CC+y}
 then :
@@ -7602,7 +7451,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_arm_CC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7614,11 +7463,11 @@ esac
 fi
 arm_CC=$ac_cv_prog_arm_CC
 if test -n "$arm_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $arm_CC" >&5
-printf "%s\n" "$arm_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $arm_CC" >&5
+printf '%s\n' "$arm_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7633,7 +7482,7 @@ test -n "$arm_CC" || arm_CC="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_i386_CC+y}
 then :
@@ -7654,7 +7503,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_i386_CC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7666,11 +7515,11 @@ esac
 fi
 i386_CC=$ac_cv_prog_i386_CC
 if test -n "$i386_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $i386_CC" >&5
-printf "%s\n" "$i386_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $i386_CC" >&5
+printf '%s\n' "$i386_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7685,7 +7534,7 @@ test -n "$i386_CC" || i386_CC="false"
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_x86_64_CC+y}
 then :
@@ -7706,7 +7555,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_x86_64_CC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -7718,11 +7567,11 @@ esac
 fi
 x86_64_CC=$ac_cv_prog_x86_64_CC
 if test -n "$x86_64_CC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $x86_64_CC" >&5
-printf "%s\n" "$x86_64_CC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $x86_64_CC" >&5
+printf '%s\n' "$x86_64_CC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -7803,8 +7652,8 @@ fi
         llvm_extra_cflags="-fuse-ld=lld"
         llvm_cflags="-target $target"
     fi
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_$llvm_extra_cflags $llvm_cflags --no-default-config" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $llvm_extra_cflags $llvm_cflags --no-default-config" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_$llvm_extra_cflags $llvm_cflags --no-default-config" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $llvm_extra_cflags $llvm_cflags --no-default-config" >&5
 printf %s "checking whether $CC supports $llvm_extra_cflags $llvm_cflags --no-default-config... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -7837,15 +7686,15 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   llvm_extra_cflags="$llvm_extra_cflags --no-default-config"
                         llvm_extra_ldflags="--no-default-config"
 else case e in #(
-  e) { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_$llvm_extra_cflags $llvm_cflags" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $llvm_extra_cflags $llvm_cflags" >&5
+  e) { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_$llvm_extra_cflags $llvm_cflags" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $llvm_extra_cflags $llvm_cflags" >&5
 printf %s "checking whether $CC supports $llvm_extra_cflags $llvm_cflags... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -7878,8 +7727,8 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   :
@@ -7898,9 +7747,9 @@ fi }
         CFLAGS="$CFLAGS $llvm_extra_cflags $llvm_cflags"
         CXXFLAGS="$CXXFLAGS $llvm_extra_cflags $llvm_cflags"
     else
-        as_wine_cv_crosscc=`printf "%s\n" "wine_cv_${wine_arch}_crosscc" | sed "$as_sed_sh"`
+        as_wine_cv_crosscc=`printf '%s\n' "wine_cv_${wine_arch}_crosscc" | sed "$as_sed_sh"`
 
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC works" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC works" >&5
 printf %s "checking whether $CC works... " >&6; }
 if eval test \${$as_wine_cv_crosscc+y}
 then :
@@ -7928,8 +7777,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
 eval ac_res=\$$as_wine_cv_crosscc
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
         if eval test \"x\$"$as_wine_cv_crosscc"\" = x"no"
 then :
   target=
@@ -7949,9 +7798,9 @@ fi
 
     eval "${wine_arch}_TARGET=\$target"
     eval "${wine_arch}_STRIP=\$target_strip"
-    as_wine_cv_crosscc_c99=`printf "%s\n" "ac_cv_${wine_arch}_crosscc_c99" | sed "$as_sed_sh"`
+    as_wine_cv_crosscc_c99=`printf '%s\n' "ac_cv_${wine_arch}_crosscc_c99" | sed "$as_sed_sh"`
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
 printf %s "checking for $CC option to enable C99 features... " >&6; }
     if eval test \${$as_wine_cv_crosscc_c99+y}
 then :
@@ -7988,10 +7837,10 @@ fi
     eval res=\$$as_wine_cv_crosscc_c99
 
     case "x$res" in
-      x) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; } ;;
-      xno) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
+      x) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf '%s\n' "none needed" >&6; } ;;
+      xno) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf '%s\n' "unsupported" >&6; }
            if test ${enable_archs+y}
 then :
   as_fn_error $? "$wine_arch PE cross-compiler supporting C99 not found.
@@ -7999,8 +7848,8 @@ This is an error since --enable-archs=$wine_arch was requested." "$LINENO" 5
 fi
            continue
            ;;
-      x*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $res" >&5
-printf "%s\n" "$res" >&6; }
+      x*) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $res" >&5
+printf '%s\n' "$res" >&6; }
           as_fn_append ${wine_arch}_CC " $res" ;;
     esac
 
@@ -8008,9 +7857,9 @@ printf "%s\n" "$res" >&6; }
 then :
 
 else case e in #(
-  e) as_wine_cv_seh_support=`printf "%s\n" "ac_cv_${wine_arch}_seh_support" | sed "$as_sed_sh"`
+  e) as_wine_cv_seh_support=`printf '%s\n' "ac_cv_${wine_arch}_seh_support" | sed "$as_sed_sh"`
 
-               { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports SEH directives" >&5
+               { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports SEH directives" >&5
 printf %s "checking whether $CC supports SEH directives... " >&6; }
 if eval test \${$as_wine_cv_seh_support+y}
 then :
@@ -8038,8 +7887,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
 eval ac_res=\$$as_wine_cv_seh_support
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
                eval res=\$$as_wine_cv_seh_support
 
                if test "x$res" = xyes
@@ -8065,9 +7914,9 @@ else case e in #(
 esac
 fi
 
-    as_wine_cv_crosscxx_cxx17=`printf "%s\n" "ac_cv_${wine_arch}_crosscxx_cxx17" | sed "$as_sed_sh"`
+    as_wine_cv_crosscxx_cxx17=`printf '%s\n' "ac_cv_${wine_arch}_crosscxx_cxx17" | sed "$as_sed_sh"`
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++17 features" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++17 features" >&5
 printf %s "checking for $CXX option to enable C++17 features... " >&6; }
     ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
@@ -8865,14 +8714,14 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
     case "x$res" in
-      x) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-printf "%s\n" "none needed" >&6; }
+      x) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf '%s\n' "none needed" >&6; }
          enable_cplusplus="$enable_cplusplus,$wine_arch" ;;
-      xno) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-printf "%s\n" "unsupported" >&6; }
+      xno) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf '%s\n' "unsupported" >&6; }
            as_fn_append wine_warnings "|$wine_arch PE cross-compiler supporting C++17 not found, some modules won't be built." ;;
-      x*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $res" >&5
-printf "%s\n" "$res" >&6; }
+      x*) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $res" >&5
+printf '%s\n' "$res" >&6; }
           as_fn_append ${wine_arch}_CXX " $res"
           enable_cplusplus="$enable_cplusplus,$wine_arch" ;;
     esac
@@ -8887,8 +8736,8 @@ else case e in #(
 esac
 fi
 
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-fno-strict-aliasing" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fno-strict-aliasing" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-fno-strict-aliasing" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fno-strict-aliasing" >&5
 printf %s "checking whether $CC supports -fno-strict-aliasing... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -8921,14 +8770,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -fno-strict-aliasing"
 fi }
-        { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Werror=unknown-warning-option" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=unknown-warning-option" >&5
+        { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Werror=unknown-warning-option" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=unknown-warning-option" >&5
 printf %s "checking whether $CC supports -Werror=unknown-warning-option... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -8961,14 +8810,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   CFLAGS="$CFLAGS -Werror=unknown-warning-option"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Werror=unused-command-line-argument" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=unused-command-line-argument" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Werror=unused-command-line-argument" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=unused-command-line-argument" >&5
 printf %s "checking whether $CC supports -Werror=unused-command-line-argument... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9001,14 +8850,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   CFLAGS="$CFLAGS -Werror=unused-command-line-argument"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Werror=ignored-optimization-argument" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=ignored-optimization-argument" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Werror=ignored-optimization-argument" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=ignored-optimization-argument" >&5
 printf %s "checking whether $CC supports -Werror=ignored-optimization-argument... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9041,14 +8890,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   CFLAGS="$CFLAGS -Werror=ignored-optimization-argument"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Werror=unsupported-floating-point-opt" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=unsupported-floating-point-opt" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Werror=unsupported-floating-point-opt" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=unsupported-floating-point-opt" >&5
 printf %s "checking whether $CC supports -Werror=unsupported-floating-point-opt... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9081,14 +8930,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   CFLAGS="$CFLAGS -Werror=unsupported-floating-point-opt"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wempty-body" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wempty-body" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wempty-body" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wempty-body" >&5
 printf %s "checking whether $CC supports -Wempty-body... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9121,14 +8970,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wempty-body"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wignored-qualifiers" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wignored-qualifiers" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wignored-qualifiers" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wignored-qualifiers" >&5
 printf %s "checking whether $CC supports -Wignored-qualifiers... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9161,14 +9010,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wignored-qualifiers"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Winit-self" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Winit-self" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Winit-self" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Winit-self" >&5
 printf %s "checking whether $CC supports -Winit-self... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9201,14 +9050,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Winit-self"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wpacked-not-aligned" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wpacked-not-aligned" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wpacked-not-aligned" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wpacked-not-aligned" >&5
 printf %s "checking whether $CC supports -Wpacked-not-aligned... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9241,14 +9090,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wno-packed-not-aligned"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wmicrosoft-enum-forward-reference" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wmicrosoft-enum-forward-reference" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wmicrosoft-enum-forward-reference" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wmicrosoft-enum-forward-reference" >&5
 printf %s "checking whether $CC supports -Wmicrosoft-enum-forward-reference... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9281,14 +9130,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wno-microsoft-enum-forward-reference"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wshift-overflow=2" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wshift-overflow=2" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wshift-overflow=2" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wshift-overflow=2" >&5
 printf %s "checking whether $CC supports -Wshift-overflow=2... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9321,14 +9170,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wshift-overflow=2"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wtype-limits" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wtype-limits" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wtype-limits" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wtype-limits" >&5
 printf %s "checking whether $CC supports -Wtype-limits... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9361,14 +9210,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wtype-limits"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wunused-but-set-parameter" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wunused-but-set-parameter" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wunused-but-set-parameter" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wunused-but-set-parameter" >&5
 printf %s "checking whether $CC supports -Wunused-but-set-parameter... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9401,14 +9250,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wunused-but-set-parameter"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wvla" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wvla" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wvla" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wvla" >&5
 printf %s "checking whether $CC supports -Wvla... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9441,14 +9290,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wvla"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wwrite-strings" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wwrite-strings" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wwrite-strings" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wwrite-strings" >&5
 printf %s "checking whether $CC supports -Wwrite-strings... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9481,14 +9330,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wwrite-strings"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wpointer-arith" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wpointer-arith" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wpointer-arith" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wpointer-arith" >&5
 printf %s "checking whether $CC supports -Wpointer-arith... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9521,14 +9370,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wpointer-arith"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wlogical-op" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wlogical-op" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wlogical-op" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wlogical-op" >&5
 printf %s "checking whether $CC supports -Wlogical-op... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9561,14 +9410,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wlogical-op"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wenum-enum-conversion" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wenum-enum-conversion" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wenum-enum-conversion" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wenum-enum-conversion" >&5
 printf %s "checking whether $CC supports -Wenum-enum-conversion... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9601,14 +9450,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   :
 else case e in #(
-  e) { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wenum-conversion" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wenum-conversion" >&5
+  e) { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wenum-conversion" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wenum-conversion" >&5
 printf %s "checking whether $CC supports -Wenum-conversion... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9641,16 +9490,16 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wenum-conversion"
 fi } ;;
 esac
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-ffunction-sections" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -ffunction-sections" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-ffunction-sections" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -ffunction-sections" >&5
 printf %s "checking whether $CC supports -ffunction-sections... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9683,14 +9532,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -ffunction-sections"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-ffp-exception-behavior=maytrap" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -ffp-exception-behavior=maytrap" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-ffp-exception-behavior=maytrap" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -ffp-exception-behavior=maytrap" >&5
 printf %s "checking whether $CC supports -ffp-exception-behavior=maytrap... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9723,15 +9572,15 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -ffp-exception-behavior=maytrap"
 fi }
 
-        { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-fms-hotpatch -DMIN_CLANG_VERSION=18" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fms-hotpatch -DMIN_CLANG_VERSION=18" >&5
+        { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-fms-hotpatch -DMIN_CLANG_VERSION=18" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fms-hotpatch -DMIN_CLANG_VERSION=18" >&5
 printf %s "checking whether $CC supports -fms-hotpatch -DMIN_CLANG_VERSION=18... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9764,16 +9613,16 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -fms-hotpatch"
                         as_fn_append ${wine_arch}_LDFLAGS " -fms-hotpatch"
 fi }
 
-        { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-flarge-source-files -Wmisleading-indentation" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -flarge-source-files -Wmisleading-indentation" >&5
+        { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-flarge-source-files -Wmisleading-indentation" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -flarge-source-files -Wmisleading-indentation" >&5
 printf %s "checking whether $CC supports -flarge-source-files -Wmisleading-indentation... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9806,16 +9655,16 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wno-misleading-indentation"
 fi }
 
     case $wine_arch in
-      i386) { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-fno-omit-frame-pointer" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fno-omit-frame-pointer" >&5
+      i386) { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-fno-omit-frame-pointer" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fno-omit-frame-pointer" >&5
 printf %s "checking whether $CC supports -fno-omit-frame-pointer... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9848,14 +9697,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -fno-omit-frame-pointer"
 fi }
-            { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-mpreferred-stack-boundary=2" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mpreferred-stack-boundary=2" >&5
+            { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-mpreferred-stack-boundary=2" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mpreferred-stack-boundary=2" >&5
 printf %s "checking whether $CC supports -mpreferred-stack-boundary=2... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9888,14 +9737,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -mpreferred-stack-boundary=2"
 fi }
-            { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-mlong-double-64" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mlong-double-64" >&5
+            { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-mlong-double-64" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mlong-double-64" >&5
 printf %s "checking whether $CC supports -mlong-double-64... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9928,14 +9777,54 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -mlong-double-64"
 fi }
-            { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wl,--disable-stdcall-fixup" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wl,--disable-stdcall-fixup" >&5
+            { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-msse2" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -msse2" >&5
+printf %s "checking whether $CC supports -msse2... " >&6; }
+if eval test \${$as_ac_var+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) ac_wine_try_cflags_saved=$CFLAGS
+ac_wine_try_cflags_saved_exeext=$ac_exeext
+CFLAGS="$CFLAGS -nostdlib -nodefaultlibs -msse2"
+ac_exeext=".exe"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+void *__os_arm64x_dispatch_ret = 0;
+const unsigned int _load_config_used[0x50] = { sizeof(_load_config_used) };
+#if defined(__clang_major__) && defined(MIN_CLANG_VERSION) && __clang_major__ < MIN_CLANG_VERSION
+#error Too old clang version
+#endif
+int __cdecl mainCRTStartup(void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  eval "$as_ac_var=yes"
+else case e in #(
+  e) eval "$as_ac_var=no" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+CFLAGS=$ac_wine_try_cflags_saved
+ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
+esac
+fi
+eval ac_res=\$$as_ac_var
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_var"\" = x"yes"
+then :
+  as_fn_append ${wine_arch}_EXTRACFLAGS " -msse2"
+fi }
+            { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wl,--disable-stdcall-fixup" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wl,--disable-stdcall-fixup" >&5
 printf %s "checking whether $CC supports -Wl,--disable-stdcall-fixup... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -9968,14 +9857,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_LDFLAGS " -Wl,--disable-stdcall-fixup"
 fi } ;;
-      x86_64) { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wformat-overflow" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wformat-overflow" >&5
+      x86_64) { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wformat-overflow" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wformat-overflow" >&5
 printf %s "checking whether $CC supports -Wformat-overflow... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10008,14 +9897,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wformat-overflow"
 fi }
-              { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wnonnull" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wnonnull" >&5
+              { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wnonnull" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wnonnull" >&5
 printf %s "checking whether $CC supports -Wnonnull... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10048,14 +9937,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wnonnull"
 fi }
-              { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-mlong-double-64" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mlong-double-64" >&5
+              { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-mlong-double-64" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mlong-double-64" >&5
 printf %s "checking whether $CC supports -mlong-double-64... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10088,14 +9977,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -mlong-double-64"
 fi }
-              { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-mcx16" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mcx16" >&5
+              { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-mcx16" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mcx16" >&5
 printf %s "checking whether $CC supports -mcx16... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10128,14 +10017,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -mcx16"
 fi }
-              { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-mcmodel=small" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mcmodel=small" >&5
+              { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-mcmodel=small" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mcmodel=small" >&5
 printf %s "checking whether $CC supports -mcmodel=small... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10168,8 +10057,8 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -mcmodel=small"
@@ -10204,8 +10093,8 @@ fi } ;;
       for wine_debug_format in $wine_crossdebug
       do
         case $wine_debug_format in
-          *dwarf)                     { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wl,-debug:dwarf" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wl,-debug:dwarf" >&5
+          *dwarf)                     { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wl,-debug:dwarf" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wl,-debug:dwarf" >&5
 printf %s "checking whether $CC supports -Wl,-debug:dwarf... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10238,15 +10127,15 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_LDFLAGS " -Wl,-debug:dwarf"
                                                           CFLAGS="$CFLAGS -Wl,-debug:dwarf"
 fi }
-                   { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-gdwarf-4" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -gdwarf-4" >&5
+                   { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-gdwarf-4" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -gdwarf-4" >&5
 printf %s "checking whether $CC supports -gdwarf-4... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10279,14 +10168,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -gdwarf-4"
 fi } ;;
-          pdb)     { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-gcodeview" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -gcodeview" >&5
+          pdb)     { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-gcodeview" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -gcodeview" >&5
 printf %s "checking whether $CC supports -gcodeview... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10319,8 +10208,8 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -gcodeview"
@@ -10330,8 +10219,8 @@ fi } ;;
     fi
     eval "${wine_arch}_DEBUG=\$wine_crossdebug"
 
-    test "x$enable_werror" != xyes || { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Werror" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror" >&5
+    test "x$enable_werror" != xyes || { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Werror" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror" >&5
 printf %s "checking whether $CC supports -Werror... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10364,8 +10253,8 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Werror"
@@ -10373,8 +10262,8 @@ fi }
             test "x$enable_build_id" != xno && as_fn_append ${wine_arch}_LDFLAGS " -Wl,--build-id"
 
         eval ${wine_arch}_EXTRACXXFLAGS=\$${wine_arch}_EXTRACFLAGS
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wdeclaration-after-statement" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wdeclaration-after-statement" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wdeclaration-after-statement" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wdeclaration-after-statement" >&5
 printf %s "checking whether $CC supports -Wdeclaration-after-statement... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10407,14 +10296,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wdeclaration-after-statement"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wstrict-prototypes" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wstrict-prototypes" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wstrict-prototypes" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wstrict-prototypes" >&5
 printf %s "checking whether $CC supports -Wstrict-prototypes... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10447,14 +10336,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wstrict-prototypes"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-Wabsolute-value" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wabsolute-value" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-Wabsolute-value" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wabsolute-value" >&5
 printf %s "checking whether $CC supports -Wabsolute-value... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10487,14 +10376,14 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -Wabsolute-value"
 fi }
-    { as_ac_var=`printf "%s\n" "ac_cv_${wine_arch}_cflags_-fasync-exceptions -DMIN_CLANG_VERSION=19" | sed "$as_sed_sh"`
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fasync-exceptions -DMIN_CLANG_VERSION=19" >&5
+    { as_ac_var=`printf '%s\n' "ac_cv_${wine_arch}_cflags_-fasync-exceptions -DMIN_CLANG_VERSION=19" | sed "$as_sed_sh"`
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fasync-exceptions -DMIN_CLANG_VERSION=19" >&5
 printf %s "checking whether $CC supports -fasync-exceptions -DMIN_CLANG_VERSION=19... " >&6; }
 if eval test \${$as_ac_var+y}
 then :
@@ -10527,8 +10416,8 @@ ac_exeext=$ac_wine_try_cflags_saved_exeext ;;
 esac
 fi
 eval ac_res=\$$as_ac_var
-	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-printf "%s\n" "$ac_res" >&6; }
+	       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf '%s\n' "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_var"\" = x"yes"
 then :
   as_fn_append ${wine_arch}_EXTRACFLAGS " -fasync-exceptions"
@@ -10563,7 +10452,7 @@ fi ;;
 esac
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for i386_set_ldt in -li386" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for i386_set_ldt in -li386" >&5
 printf %s "checking for i386_set_ldt in -li386... " >&6; }
 if test ${ac_cv_lib_i386_i386_set_ldt+y}
 then :
@@ -10604,8 +10493,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_i386_i386_set_ldt" >&5
-printf "%s\n" "$ac_cv_lib_i386_i386_set_ldt" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_i386_i386_set_ldt" >&5
+printf '%s\n' "$ac_cv_lib_i386_i386_set_ldt" >&6; }
 if test "x$ac_cv_lib_i386_i386_set_ldt" = xyes
 then :
   I386_LIBS="-li386"
@@ -10624,8 +10513,8 @@ then :
 fi
 if test "$enable_largefile,$enable_year2038" != no,no
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CPPFLAGS option for large files" >&5
-printf %s "checking for $CPPFLAGS option for large files... " >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to support large files" >&5
+printf %s "checking for $CC option to support large files... " >&6; }
 if test ${ac_cv_sys_largefile_opts+y}
 then :
   printf %s "(cached) " >&6
@@ -10690,8 +10579,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
   test $ac_opt_found = yes || ac_cv_sys_largefile_opts="support not detected" ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_opts" >&5
-printf "%s\n" "$ac_cv_sys_largefile_opts" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_opts" >&5
+printf '%s\n' "$ac_cv_sys_largefile_opts" >&6; }
 
 ac_have_largefile=yes
 case $ac_cv_sys_largefile_opts in #(
@@ -10703,11 +10592,11 @@ case $ac_cv_sys_largefile_opts in #(
     ac_have_largefile=no ;; #(
   "-D_FILE_OFFSET_BITS=64") :
 
-printf "%s\n" "#define _FILE_OFFSET_BITS 64" >>confdefs.h
+printf '%s\n' "#define _FILE_OFFSET_BITS 64" >>confdefs.h
  ;; #(
   "-D_LARGE_FILES=1") :
 
-printf "%s\n" "#define _LARGE_FILES 1" >>confdefs.h
+printf '%s\n' "#define _LARGE_FILES 1" >>confdefs.h
  ;; #(
   *) :
     as_fn_error $? "internal error: bad value for \$ac_cv_sys_largefile_opts" "$LINENO" 5 ;;
@@ -10715,8 +10604,8 @@ esac
 
 if test "$enable_year2038" != no
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CPPFLAGS option for timestamps after 2038" >&5
-printf %s "checking for $CPPFLAGS option for timestamps after 2038... " >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to support timestamps after 2038" >&5
+printf %s "checking for $CC option to support timestamps after 2038... " >&6; }
 if test ${ac_cv_sys_year2038_opts+y}
 then :
   printf %s "(cached) " >&6
@@ -10759,8 +10648,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
   test $ac_opt_found = yes || ac_cv_sys_year2038_opts="support not detected" ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_year2038_opts" >&5
-printf "%s\n" "$ac_cv_sys_year2038_opts" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_year2038_opts" >&5
+printf '%s\n' "$ac_cv_sys_year2038_opts" >&6; }
 
 ac_have_year2038=yes
 case $ac_cv_sys_year2038_opts in #(
@@ -10770,15 +10659,15 @@ case $ac_cv_sys_year2038_opts in #(
     ac_have_year2038=no ;; #(
   "-D_TIME_BITS=64") :
 
-printf "%s\n" "#define _TIME_BITS 64" >>confdefs.h
+printf '%s\n' "#define _TIME_BITS 64" >>confdefs.h
  ;; #(
   "-D__MINGW_USE_VC2005_COMPAT") :
 
-printf "%s\n" "#define __MINGW_USE_VC2005_COMPAT 1" >>confdefs.h
+printf '%s\n' "#define __MINGW_USE_VC2005_COMPAT 1" >>confdefs.h
  ;; #(
   "-U_USE_32_BIT_TIME_T"*) :
-    { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+    { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "the 'time_t' type is currently forced to be 32-bit. It
 will stop working after mid-January 2038. Remove
 _USE_32BIT_TIME_T from the compiler flags.
@@ -10806,8 +10695,8 @@ then :
 then :
   case `TZ=UTC0 LC_ALL=C ls -l conftest.time 2>/dev/null` in #(
   *'Feb  7  2106'* | *'Feb  7 17:10'*) :
-    { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+    { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "this system appears to support timestamps after
 mid-January 2038, but no mechanism for enabling wide
 'time_t' was detected. Did you mean to build a 64-bit
@@ -10827,7 +10716,7 @@ do
   if test $ac_cache; then
     ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
     if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
-      printf "%s\n" "#define $ac_item 1" >> confdefs.h
+      printf '%s\n' "#define $ac_item 1" >> confdefs.h
     fi
     ac_header= ac_cache=
   elif test $ac_header; then
@@ -10847,499 +10736,499 @@ done
 if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
 then :
 
-printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
+printf '%s\n' "#define STDC_HEADERS 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "CL/cl.h" "ac_cv_header_CL_cl_h" "$ac_includes_default"
 if test "x$ac_cv_header_CL_cl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_CL_CL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_CL_CL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "OpenCL/opencl.h" "ac_cv_header_OpenCL_opencl_h" "$ac_includes_default"
 if test "x$ac_cv_header_OpenCL_opencl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_OPENCL_OPENCL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_OPENCL_OPENCL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default"
 if test "x$ac_cv_header_arpa_inet_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_ARPA_INET_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_ARPA_INET_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "arpa/nameser.h" "ac_cv_header_arpa_nameser_h" "$ac_includes_default"
 if test "x$ac_cv_header_arpa_nameser_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_ARPA_NAMESER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_ARPA_NAMESER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "asm/termbits.h" "ac_cv_header_asm_termbits_h" "$ac_includes_default"
 if test "x$ac_cv_header_asm_termbits_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_ASM_TERMBITS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_ASM_TERMBITS_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "asm/types.h" "ac_cv_header_asm_types_h" "$ac_includes_default"
 if test "x$ac_cv_header_asm_types_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_ASM_TYPES_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_ASM_TYPES_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "asm/user.h" "ac_cv_header_asm_user_h" "$ac_includes_default"
 if test "x$ac_cv_header_asm_user_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_ASM_USER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_ASM_USER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "bluetooth/bluetooth.h" "ac_cv_header_bluetooth_bluetooth_h" "$ac_includes_default"
 if test "x$ac_cv_header_bluetooth_bluetooth_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_BLUETOOTH_BLUETOOTH_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_BLUETOOTH_BLUETOOTH_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "elf.h" "ac_cv_header_elf_h" "$ac_includes_default"
 if test "x$ac_cv_header_elf_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_ELF_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_ELF_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "gettext-po.h" "ac_cv_header_gettext_po_h" "$ac_includes_default"
 if test "x$ac_cv_header_gettext_po_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_GETTEXT_PO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GETTEXT_PO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "link.h" "ac_cv_header_link_h" "$ac_includes_default"
 if test "x$ac_cv_header_link_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINK_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINK_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/cdrom.h" "ac_cv_header_linux_cdrom_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_cdrom_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_CDROM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_CDROM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/filter.h" "ac_cv_header_linux_filter_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_filter_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/fs.h" "ac_cv_header_linux_fs_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_fs_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_FS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_FS_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/hdreg.h" "ac_cv_header_linux_hdreg_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_hdreg_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_HDREG_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_HDREG_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/hidraw.h" "ac_cv_header_linux_hidraw_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_hidraw_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_HIDRAW_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_HIDRAW_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/input.h" "ac_cv_header_linux_input_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_input_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_INPUT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_INPUT_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/ioctl.h" "ac_cv_header_linux_ioctl_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_ioctl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_IOCTL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_IOCTL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/major.h" "ac_cv_header_linux_major_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_major_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/memfd.h" "ac_cv_header_linux_memfd_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_memfd_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_MEMFD_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_MEMFD_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/ntsync.h" "ac_cv_header_linux_ntsync_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_ntsync_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_NTSYNC_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_NTSYNC_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/param.h" "ac_cv_header_linux_param_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_param_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_PARAM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_PARAM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/serial.h" "ac_cv_header_linux_serial_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_serial_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_SERIAL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_SERIAL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/types.h" "ac_cv_header_linux_types_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_types_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_TYPES_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_TYPES_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/ucdrom.h" "ac_cv_header_linux_ucdrom_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_ucdrom_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/userfaultfd.h" "ac_cv_header_linux_userfaultfd_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_userfaultfd_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_USERFAULTFD_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_USERFAULTFD_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/wireless.h" "ac_cv_header_linux_wireless_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_wireless_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_WIRELESS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_WIRELESS_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default"
 if test "x$ac_cv_header_lwp_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LWP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LWP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "mach-o/loader.h" "ac_cv_header_mach_o_loader_h" "$ac_includes_default"
 if test "x$ac_cv_header_mach_o_loader_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_MACH_O_LOADER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_MACH_O_LOADER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "mach/mach.h" "ac_cv_header_mach_mach_h" "$ac_includes_default"
 if test "x$ac_cv_header_mach_mach_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_MACH_MACH_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_MACH_MACH_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "machine/cpu.h" "ac_cv_header_machine_cpu_h" "$ac_includes_default"
 if test "x$ac_cv_header_machine_cpu_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_MACHINE_CPU_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_MACHINE_CPU_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "machine/sysarch.h" "ac_cv_header_machine_sysarch_h" "$ac_includes_default"
 if test "x$ac_cv_header_machine_sysarch_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_MACHINE_SYSARCH_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_MACHINE_SYSARCH_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "mntent.h" "ac_cv_header_mntent_h" "$ac_includes_default"
 if test "x$ac_cv_header_mntent_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_MNTENT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_MNTENT_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default"
 if test "x$ac_cv_header_netdb_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETDB_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETDB_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default"
 if test "x$ac_cv_header_netinet_in_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IN_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IN_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/in_systm.h" "ac_cv_header_netinet_in_systm_h" "$ac_includes_default"
 if test "x$ac_cv_header_netinet_in_systm_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IN_SYSTM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IN_SYSTM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/tcp.h" "ac_cv_header_netinet_tcp_h" "$ac_includes_default"
 if test "x$ac_cv_header_netinet_tcp_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_TCP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_TCP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/tcp_fsm.h" "ac_cv_header_netinet_tcp_fsm_h" "$ac_includes_default"
 if test "x$ac_cv_header_netinet_tcp_fsm_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_TCP_FSM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_TCP_FSM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "pcap/pcap.h" "ac_cv_header_pcap_pcap_h" "$ac_includes_default"
 if test "x$ac_cv_header_pcap_pcap_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_PCAP_PCAP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PCAP_PCAP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "port.h" "ac_cv_header_port_h" "$ac_includes_default"
 if test "x$ac_cv_header_port_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_PORT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PORT_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "pwd.h" "ac_cv_header_pwd_h" "$ac_includes_default"
 if test "x$ac_cv_header_pwd_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_PWD_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PWD_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sched.h" "ac_cv_header_sched_h" "$ac_includes_default"
 if test "x$ac_cv_header_sched_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SCHED_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SCHED_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "scsi/scsi.h" "ac_cv_header_scsi_scsi_h" "$ac_includes_default"
 if test "x$ac_cv_header_scsi_scsi_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SCSI_SCSI_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SCSI_SCSI_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "scsi/scsi_ioctl.h" "ac_cv_header_scsi_scsi_ioctl_h" "$ac_includes_default"
 if test "x$ac_cv_header_scsi_scsi_ioctl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SCSI_SCSI_IOCTL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SCSI_SCSI_IOCTL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "scsi/sg.h" "ac_cv_header_scsi_sg_h" "$ac_includes_default"
 if test "x$ac_cv_header_scsi_sg_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SCSI_SG_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SCSI_SG_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/attr.h" "ac_cv_header_sys_attr_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_attr_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_ATTR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_ATTR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/auxv.h" "ac_cv_header_sys_auxv_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_auxv_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_AUXV_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_AUXV_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/cdio.h" "ac_cv_header_sys_cdio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_cdio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_CDIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_CDIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/epoll.h" "ac_cv_header_sys_epoll_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_epoll_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_EPOLL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_EPOLL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/event.h" "ac_cv_header_sys_event_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_event_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_EVENT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_EVENT_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/extattr.h" "ac_cv_header_sys_extattr_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_extattr_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_EXTATTR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_EXTATTR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/filio.h" "ac_cv_header_sys_filio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_filio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_FILIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_FILIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/ipc.h" "ac_cv_header_sys_ipc_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_ipc_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_IPC_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_IPC_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/link.h" "ac_cv_header_sys_link_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_link_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_LINK_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_LINK_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/modem.h" "ac_cv_header_sys_modem_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_modem_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_MODEM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_MODEM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/mtio.h" "ac_cv_header_sys_mtio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_mtio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_MTIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_MTIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_param_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/personality.h" "ac_cv_header_sys_personality_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_personality_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_PERSONALITY_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_PERSONALITY_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/prctl.h" "ac_cv_header_sys_prctl_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_prctl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_PRCTL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_PRCTL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/ptrace.h" "ac_cv_header_sys_ptrace_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_ptrace_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_PTRACE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_PTRACE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/queue.h" "ac_cv_header_sys_queue_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_queue_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_QUEUE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_QUEUE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/random.h" "ac_cv_header_sys_random_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_random_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_RANDOM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_RANDOM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/resource.h" "ac_cv_header_sys_resource_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_resource_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_RESOURCE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_RESOURCE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/scsiio.h" "ac_cv_header_sys_scsiio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_scsiio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SCSIIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SCSIIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/shm.h" "ac_cv_header_sys_shm_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_shm_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SHM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SHM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/signal.h" "ac_cv_header_sys_signal_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_signal_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SIGNAL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SIGNAL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/socketvar.h" "ac_cv_header_sys_socketvar_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_socketvar_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SOCKETVAR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SOCKETVAR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/sockio.h" "ac_cv_header_sys_sockio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_sockio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SOCKIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SOCKIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/statvfs.h" "ac_cv_header_sys_statvfs_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_statvfs_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_STATVFS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_STATVFS_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/strtio.h" "ac_cv_header_sys_strtio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_strtio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_STRTIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_STRTIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/syscall.h" "ac_cv_header_sys_syscall_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_syscall_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SYSCALL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SYSCALL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/sysinfo.h" "ac_cv_header_sys_sysinfo_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_sysinfo_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SYSINFO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SYSINFO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/times.h" "ac_cv_header_sys_times_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_times_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_TIMES_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_TIMES_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/uio.h" "ac_cv_header_sys_uio_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_uio_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_UIO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_UIO_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/user.h" "ac_cv_header_sys_user_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_user_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_USER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_USER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/utsname.h" "ac_cv_header_sys_utsname_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_utsname_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_UTSNAME_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_UTSNAME_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/vnode.h" "ac_cv_header_sys_vnode_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_vnode_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_VNODE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_VNODE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/xattr.h" "ac_cv_header_sys_xattr_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_xattr_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_XATTR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_XATTR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "syscall.h" "ac_cv_header_syscall_h" "$ac_includes_default"
 if test "x$ac_cv_header_syscall_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYSCALL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYSCALL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "utime.h" "ac_cv_header_utime_h" "$ac_includes_default"
 if test "x$ac_cv_header_utime_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_UTIME_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_UTIME_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "valgrind/memcheck.h" "ac_cv_header_valgrind_memcheck_h" "$ac_includes_default"
 if test "x$ac_cv_header_valgrind_memcheck_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_VALGRIND_MEMCHECK_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_VALGRIND_MEMCHECK_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "valgrind/valgrind.h" "ac_cv_header_valgrind_valgrind_h" "$ac_includes_default"
 if test "x$ac_cv_header_valgrind_valgrind_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_VALGRIND_VALGRIND_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_VALGRIND_VALGRIND_H 1" >>confdefs.h
 
 fi
 
@@ -11348,7 +11237,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h"
 if test "x$ac_cv_header_sys_mkdev_h" = xyes
 then :
 
-printf "%s\n" "#define MAJOR_IN_MKDEV 1" >>confdefs.h
+printf '%s\n' "#define MAJOR_IN_MKDEV 1" >>confdefs.h
 
 fi
 
@@ -11357,7 +11246,7 @@ if test $ac_cv_header_sys_mkdev_h = no; then
 if test "x$ac_cv_header_sys_sysmacros_h" = xyes
 then :
 
-printf "%s\n" "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h
+printf '%s\n' "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h
 
 fi
 
@@ -11372,7 +11261,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/conf.h" "ac_cv_header_sys_conf_h" "#
 "
 if test "x$ac_cv_header_sys_conf_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_CONF_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_CONF_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/mount.h" "ac_cv_header_sys_mount_h" "#include <sys/types.h>
@@ -11382,7 +11271,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/mount.h" "ac_cv_header_sys_mount_h"
 "
 if test "x$ac_cv_header_sys_mount_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_MOUNT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_MOUNT_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/statfs.h" "ac_cv_header_sys_statfs_h" "#include <sys/types.h>
@@ -11392,7 +11281,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/statfs.h" "ac_cv_header_sys_statfs_h
 "
 if test "x$ac_cv_header_sys_statfs_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_STATFS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_STATFS_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/user.h" "ac_cv_header_sys_user_h" "#include <sys/types.h>
@@ -11402,7 +11291,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/user.h" "ac_cv_header_sys_user_h" "#
 "
 if test "x$ac_cv_header_sys_user_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_USER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_USER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/vfs.h" "ac_cv_header_sys_vfs_h" "#include <sys/types.h>
@@ -11412,7 +11301,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/vfs.h" "ac_cv_header_sys_vfs_h" "#in
 "
 if test "x$ac_cv_header_sys_vfs_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_VFS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_VFS_H 1" >>confdefs.h
 
 fi
 
@@ -11426,7 +11315,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h
 "
 if test "x$ac_cv_header_sys_sysctl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_SYSCTL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_SYSCTL_H 1" >>confdefs.h
 
 fi
 
@@ -11455,7 +11344,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h
 "
 if test "x$ac_cv_header_netinet_ip_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "#include <sys/types.h>
@@ -11481,7 +11370,7 @@ ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "#incl
 "
 if test "x$ac_cv_header_net_if_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NET_IF_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NET_IF_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "net/if_arp.h" "ac_cv_header_net_if_arp_h" "#include <sys/types.h>
@@ -11507,7 +11396,7 @@ ac_fn_c_check_header_compile "$LINENO" "net/if_arp.h" "ac_cv_header_net_if_arp_h
 "
 if test "x$ac_cv_header_net_if_arp_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NET_IF_ARP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NET_IF_ARP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "net/if_dl.h" "ac_cv_header_net_if_dl_h" "#include <sys/types.h>
@@ -11533,7 +11422,7 @@ ac_fn_c_check_header_compile "$LINENO" "net/if_dl.h" "ac_cv_header_net_if_dl_h"
 "
 if test "x$ac_cv_header_net_if_dl_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NET_IF_DL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NET_IF_DL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "net/if_types.h" "ac_cv_header_net_if_types_h" "#include <sys/types.h>
@@ -11559,7 +11448,7 @@ ac_fn_c_check_header_compile "$LINENO" "net/if_types.h" "ac_cv_header_net_if_typ
 "
 if test "x$ac_cv_header_net_if_types_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NET_IF_TYPES_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NET_IF_TYPES_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "net/route.h" "ac_cv_header_net_route_h" "#include <sys/types.h>
@@ -11585,7 +11474,7 @@ ac_fn_c_check_header_compile "$LINENO" "net/route.h" "ac_cv_header_net_route_h"
 "
 if test "x$ac_cv_header_net_route_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NET_ROUTE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NET_ROUTE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/if_ether.h" "ac_cv_header_netinet_if_ether_h" "#include <sys/types.h>
@@ -11611,7 +11500,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/if_ether.h" "ac_cv_header_netine
 "
 if test "x$ac_cv_header_netinet_if_ether_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IF_ETHER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IF_ETHER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/in_pcb.h" "ac_cv_header_netinet_in_pcb_h" "#include <sys/types.h>
@@ -11637,7 +11526,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/in_pcb.h" "ac_cv_header_netinet_
 "
 if test "x$ac_cv_header_netinet_in_pcb_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IN_PCB_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IN_PCB_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/ip_icmp.h" "ac_cv_header_netinet_ip_icmp_h" "#include <sys/types.h>
@@ -11663,7 +11552,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/ip_icmp.h" "ac_cv_header_netinet
 "
 if test "x$ac_cv_header_netinet_ip_icmp_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IP_ICMP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IP_ICMP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/ip_var.h" "ac_cv_header_netinet_ip_var_h" "#include <sys/types.h>
@@ -11689,7 +11578,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/ip_var.h" "ac_cv_header_netinet_
 "
 if test "x$ac_cv_header_netinet_ip_var_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_IP_VAR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_IP_VAR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/udp.h" "ac_cv_header_netinet_udp_h" "#include <sys/types.h>
@@ -11715,7 +11604,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/udp.h" "ac_cv_header_netinet_udp
 "
 if test "x$ac_cv_header_netinet_udp_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_UDP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_UDP_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet6/ip6_var.h" "ac_cv_header_netinet6_ip6_var_h" "#include <sys/types.h>
@@ -11741,7 +11630,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet6/ip6_var.h" "ac_cv_header_netine
 "
 if test "x$ac_cv_header_netinet6_ip6_var_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET6_IP6_VAR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET6_IP6_VAR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netipx/ipx.h" "ac_cv_header_netipx_ipx_h" "#include <sys/types.h>
@@ -11767,7 +11656,7 @@ ac_fn_c_check_header_compile "$LINENO" "netipx/ipx.h" "ac_cv_header_netipx_ipx_h
 "
 if test "x$ac_cv_header_netipx_ipx_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETIPX_IPX_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETIPX_IPX_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "sys/un.h" "ac_cv_header_sys_un_h" "#include <sys/types.h>
@@ -11793,7 +11682,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/un.h" "ac_cv_header_sys_un_h" "#incl
 "
 if test "x$ac_cv_header_sys_un_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_UN_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_UN_H 1" >>confdefs.h
 
 fi
 
@@ -11827,7 +11716,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/udp_var.h" "ac_cv_header_netinet
 "
 if test "x$ac_cv_header_netinet_udp_var_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_UDP_VAR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_UDP_VAR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/icmp_var.h" "ac_cv_header_netinet_icmp_var_h" "#include <sys/types.h>
@@ -11859,7 +11748,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/icmp_var.h" "ac_cv_header_netine
 "
 if test "x$ac_cv_header_netinet_icmp_var_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_ICMP_VAR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_ICMP_VAR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/icmp6.h" "ac_cv_header_netinet_icmp6_h" "#include <sys/types.h>
@@ -11891,7 +11780,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/icmp6.h" "ac_cv_header_netinet_i
 "
 if test "x$ac_cv_header_netinet_icmp6_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_ICMP6_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_ICMP6_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "netinet/tcp_var.h" "ac_cv_header_netinet_tcp_var_h" "#include <sys/types.h>
@@ -11923,7 +11812,7 @@ ac_fn_c_check_header_compile "$LINENO" "netinet/tcp_var.h" "ac_cv_header_netinet
 "
 if test "x$ac_cv_header_netinet_tcp_var_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_NETINET_TCP_VAR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_NETINET_TCP_VAR_H 1" >>confdefs.h
 
 fi
 
@@ -11939,7 +11828,7 @@ ac_fn_c_check_header_compile "$LINENO" "linux/ipx.h" "ac_cv_header_linux_ipx_h"
 "
 if test "x$ac_cv_header_linux_ipx_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_IPX_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_IPX_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/irda.h" "ac_cv_header_linux_irda_h" "#include <sys/types.h>
@@ -11953,7 +11842,7 @@ ac_fn_c_check_header_compile "$LINENO" "linux/irda.h" "ac_cv_header_linux_irda_h
 "
 if test "x$ac_cv_header_linux_irda_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_IRDA_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_IRDA_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/rtnetlink.h" "ac_cv_header_linux_rtnetlink_h" "#include <sys/types.h>
@@ -11967,7 +11856,7 @@ ac_fn_c_check_header_compile "$LINENO" "linux/rtnetlink.h" "ac_cv_header_linux_r
 "
 if test "x$ac_cv_header_linux_rtnetlink_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_RTNETLINK_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_RTNETLINK_H 1" >>confdefs.h
 
 fi
 
@@ -11983,7 +11872,7 @@ ac_fn_c_check_header_compile "$LINENO" "resolv.h" "ac_cv_header_resolv_h" "#incl
 "
 if test "x$ac_cv_header_resolv_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_RESOLV_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_RESOLV_H 1" >>confdefs.h
 
 fi
 
@@ -11992,7 +11881,7 @@ ac_fn_c_check_header_compile "$LINENO" "ifaddrs.h" "ac_cv_header_ifaddrs_h" "#in
 "
 if test "x$ac_cv_header_ifaddrs_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_IFADDRS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_IFADDRS_H 1" >>confdefs.h
 
 fi
 
@@ -12001,7 +11890,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/ucontext.h" "ac_cv_header_sys_uconte
 "
 if test "x$ac_cv_header_sys_ucontext_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_UCONTEXT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_UCONTEXT_H 1" >>confdefs.h
 
 fi
 
@@ -12013,7 +11902,7 @@ ac_fn_c_check_header_compile "$LINENO" "sys/thr.h" "ac_cv_header_sys_thr_h" "#in
 "
 if test "x$ac_cv_header_sys_thr_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_THR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_THR_H 1" >>confdefs.h
 
 fi
 
@@ -12022,7 +11911,7 @@ ac_fn_c_check_header_compile "$LINENO" "pthread_np.h" "ac_cv_header_pthread_np_h
 "
 if test "x$ac_cv_header_pthread_np_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_PTHREAD_NP_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PTHREAD_NP_H 1" >>confdefs.h
 
 fi
 
@@ -12035,7 +11924,7 @@ ac_fn_c_check_header_compile "$LINENO" "linux/videodev2.h" "ac_cv_header_linux_v
 "
 if test "x$ac_cv_header_linux_videodev2_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_VIDEODEV2_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_VIDEODEV2_H 1" >>confdefs.h
 
 fi
 
@@ -12050,7 +11939,7 @@ ac_fn_c_check_header_compile "$LINENO" "libprocstat.h" "ac_cv_header_libprocstat
 "
 if test "x$ac_cv_header_libprocstat_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LIBPROCSTAT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LIBPROCSTAT_H 1" >>confdefs.h
 
 fi
 
@@ -12061,14 +11950,14 @@ ac_fn_c_check_header_compile "$LINENO" "bluetooth/rfcomm.h" "ac_cv_header_blueto
 "
 if test "x$ac_cv_header_bluetooth_rfcomm_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_BLUETOOTH_RFCOMM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_BLUETOOTH_RFCOMM_H 1" >>confdefs.h
 
 fi
 
 
 if test "x$ac_cv_header_sys_xattr_h" = xyes
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether getxattr takes additional arguments" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether getxattr takes additional arguments" >&5
 printf %s "checking whether getxattr takes additional arguments... " >&6; }
 if test ${wine_cv_xattr_extra_args+y}
 then :
@@ -12095,10 +11984,10 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_xattr_extra_args" >&5
-printf "%s\n" "$wine_cv_xattr_extra_args" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_xattr_extra_args" >&5
+printf '%s\n' "$wine_cv_xattr_extra_args" >&6; }
     test $wine_cv_xattr_extra_args != yes ||
-printf "%s\n" "#define XATTR_ADDITIONAL_OPTIONS 1" >>confdefs.h
+printf '%s\n' "#define XATTR_ADDITIONAL_OPTIONS 1" >>confdefs.h
 
 fi
 
@@ -12126,7 +12015,7 @@ WINEPRELOADER_LDFLAGS=""
 LIBEXT="so"
 # Extract the first word of "ldd", so it can be a program name with args.
 set dummy ldd; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_path_LDD+y}
 then :
@@ -12150,7 +12039,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_path_LDD="$as_dir$ac_word$ac_exec_ext"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12164,18 +12053,18 @@ esac
 fi
 LDD=$ac_cv_path_LDD
 if test -n "$LDD"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDD" >&5
-printf "%s\n" "$LDD" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $LDD" >&5
+printf '%s\n' "$LDD" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
 set dummy ${ac_tool_prefix}otool; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_OTOOL+y}
 then :
@@ -12196,7 +12085,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12208,11 +12097,11 @@ esac
 fi
 OTOOL=$ac_cv_prog_OTOOL
 if test -n "$OTOOL"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
-printf "%s\n" "$OTOOL" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+printf '%s\n' "$OTOOL" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -12221,7 +12110,7 @@ if test -z "$ac_cv_prog_OTOOL"; then
   ac_ct_OTOOL=$OTOOL
   # Extract the first word of "otool", so it can be a program name with args.
 set dummy otool; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_OTOOL+y}
 then :
@@ -12242,7 +12131,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_OTOOL="otool"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12254,11 +12143,11 @@ esac
 fi
 ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
 if test -n "$ac_ct_OTOOL"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
-printf "%s\n" "$ac_ct_OTOOL" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+printf '%s\n' "$ac_ct_OTOOL" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_OTOOL" = x; then
@@ -12266,8 +12155,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     OTOOL=$ac_ct_OTOOL
@@ -12279,7 +12168,7 @@ fi
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}readelf", so it can be a program name with args.
 set dummy ${ac_tool_prefix}readelf; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_READELF+y}
 then :
@@ -12300,7 +12189,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_READELF="${ac_tool_prefix}readelf"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12312,11 +12201,11 @@ esac
 fi
 READELF=$ac_cv_prog_READELF
 if test -n "$READELF"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $READELF" >&5
-printf "%s\n" "$READELF" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $READELF" >&5
+printf '%s\n' "$READELF" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -12325,7 +12214,7 @@ if test -z "$ac_cv_prog_READELF"; then
   ac_ct_READELF=$READELF
   # Extract the first word of "readelf", so it can be a program name with args.
 set dummy readelf; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_READELF+y}
 then :
@@ -12346,7 +12235,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_READELF="readelf"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12358,11 +12247,11 @@ esac
 fi
 ac_ct_READELF=$ac_cv_prog_ac_ct_READELF
 if test -n "$ac_ct_READELF"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_READELF" >&5
-printf "%s\n" "$ac_ct_READELF" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_READELF" >&5
+printf '%s\n' "$ac_ct_READELF" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
   if test "x$ac_ct_READELF" = x; then
@@ -12370,8 +12259,8 @@ fi
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     READELF=$ac_ct_READELF
@@ -12449,7 +12338,7 @@ case $host_os in
     case $HOST_ARCH in
       i386) wine_use_preloader=yes ;;
       x86_64)
-                { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-no_huge" >&5
+                { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-no_huge" >&5
 printf %s "checking whether the compiler supports -Wl,-no_huge... " >&6; }
 if test ${ac_cv_cflags__Wl__no_huge+y}
 then :
@@ -12473,8 +12362,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__no_huge" >&5
-printf "%s\n" "$ac_cv_cflags__Wl__no_huge" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__no_huge" >&5
+printf '%s\n' "$ac_cv_cflags__Wl__no_huge" >&6; }
 if test "x$ac_cv_cflags__Wl__no_huge" = xyes
 then :
   wine_use_preloader=no
@@ -12490,7 +12379,7 @@ fi
     if test "$wine_use_preloader" = "yes"
     then
         WINEPRELOADER_LDFLAGS="-nostartfiles -nodefaultlibs -e _start -ldylib1.o -mmacosx-version-min=10.7 -Wl,-no_new_main,-segalign,0x1000,-pagezero_size,0x1000,-sectcreate,__TEXT,__info_plist,loader/wine_info.plist"
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-no_pie" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-no_pie" >&5
 printf %s "checking whether the compiler supports -Wl,-no_pie... " >&6; }
 if test ${ac_cv_cflags__Wl__no_pie+y}
 then :
@@ -12514,8 +12403,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__no_pie" >&5
-printf "%s\n" "$ac_cv_cflags__Wl__no_pie" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__no_pie" >&5
+printf '%s\n' "$ac_cv_cflags__Wl__no_pie" >&6; }
 if test "x$ac_cv_cflags__Wl__no_pie" = xyes
 then :
   WINEPRELOADER_LDFLAGS="-Wl,-no_pie $WINEPRELOADER_LDFLAGS"
@@ -12556,11 +12445,11 @@ ac_compile='$OBJC -c $OBJCFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$OBJC -o conftest$ac_exeext $OBJCFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_objc_compiler_gnu
 if test -n "$ac_tool_prefix"; then
-  for ac_prog in gcc objcc objc cc CC clang
+  for ac_prog in gobjc gcc objcc objc cc CC clang
   do
     # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_OBJC+y}
 then :
@@ -12581,7 +12470,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_OBJC="$ac_tool_prefix$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12593,11 +12482,11 @@ esac
 fi
 OBJC=$ac_cv_prog_OBJC
 if test -n "$OBJC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJC" >&5
-printf "%s\n" "$OBJC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $OBJC" >&5
+printf '%s\n' "$OBJC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -12606,11 +12495,11 @@ fi
 fi
 if test -z "$OBJC"; then
   ac_ct_OBJC=$OBJC
-  for ac_prog in gcc objcc objc cc CC clang
+  for ac_prog in gobjc gcc objcc objc cc CC clang
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_ac_ct_OBJC+y}
 then :
@@ -12631,7 +12520,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_OBJC="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -12643,11 +12532,11 @@ esac
 fi
 ac_ct_OBJC=$ac_cv_prog_ac_ct_OBJC
 if test -n "$ac_ct_OBJC"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJC" >&5
-printf "%s\n" "$ac_ct_OBJC" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJC" >&5
+printf '%s\n' "$ac_ct_OBJC" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -12659,8 +12548,8 @@ done
   else
     case $cross_compiling:$ac_tool_warned in
 yes:)
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
 ac_tool_warned=yes ;;
 esac
     OBJC=$ac_ct_OBJC
@@ -12668,7 +12557,7 @@ esac
 fi
 
 # Provide some information about the compiler.
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Objective C compiler version" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for Objective C compiler version" >&5
 set X $ac_compile
 ac_compiler=$2
 for ac_option in --version -v -V -qversion; do
@@ -12678,7 +12567,7 @@ case "(($ac_try" in
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
+printf '%s\n' "$ac_try_echo"; } >&5
   (eval "$ac_compiler $ac_option >&5") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
@@ -12688,11 +12577,11 @@ printf "%s\n" "$ac_try_echo"; } >&5
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
-  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 done
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU Objective C" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU Objective C" >&5
 printf %s "checking whether the compiler supports GNU Objective C... " >&6; }
 if test ${ac_cv_objc_compiler_gnu+y}
 then :
@@ -12724,8 +12613,8 @@ ac_cv_objc_compiler_gnu=$ac_compiler_gnu
  ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objc_compiler_gnu" >&5
-printf "%s\n" "$ac_cv_objc_compiler_gnu" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objc_compiler_gnu" >&5
+printf '%s\n' "$ac_cv_objc_compiler_gnu" >&6; }
 ac_compiler_gnu=$ac_cv_objc_compiler_gnu
 
 if test $ac_compiler_gnu = yes; then
@@ -12735,7 +12624,7 @@ else
 fi
 ac_test_OBJCFLAGS=${OBJCFLAGS+y}
 ac_save_OBJCFLAGS=$OBJCFLAGS
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $OBJC accepts -g" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $OBJC accepts -g" >&5
 printf %s "checking whether $OBJC accepts -g... " >&6; }
 if test ${ac_cv_prog_objc_g+y}
 then :
@@ -12803,8 +12692,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
    ac_objc_werror_flag=$ac_save_objc_werror_flag ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_objc_g" >&5
-printf "%s\n" "$ac_cv_prog_objc_g" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_objc_g" >&5
+printf '%s\n' "$ac_cv_prog_objc_g" >&6; }
 if test $ac_test_OBJCFLAGS; then
   OBJCFLAGS=$ac_save_OBJCFLAGS
 elif test $ac_cv_prog_objc_g = yes; then
@@ -12827,7 +12716,7 @@ ac_link='$OBJC -o conftest$ac_exeext $OBJCFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_
 ac_compiler_gnu=$ac_cv_objc_compiler_gnu
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether MTLDevice protocol supports registryID property" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether MTLDevice protocol supports registryID property" >&5
 printf %s "checking whether MTLDevice protocol supports registryID property... " >&6; }
 if test ${ac_cv_mtldevice_registryid+y}
 then :
@@ -12854,8 +12743,8 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mtldevice_registryid" >&5
-printf "%s\n" "$ac_cv_mtldevice_registryid" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mtldevice_registryid" >&5
+printf '%s\n' "$ac_cv_mtldevice_registryid" >&6; }
     ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -12863,7 +12752,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
     test $ac_cv_mtldevice_registryid != yes ||
-printf "%s\n" "#define HAVE_MTLDEVICE_REGISTRYID 1" >>confdefs.h
+printf '%s\n' "#define HAVE_MTLDEVICE_REGISTRYID 1" >>confdefs.h
 
 
     UNIXLDFLAGS="-dynamiclib -install_name @rpath/\$(UNIXLIB) -Wl,-rpath,@loader_path\/"
@@ -12873,13 +12762,13 @@ printf "%s\n" "#define HAVE_MTLDEVICE_REGISTRYID 1" >>confdefs.h
 
   linux-android*)
 
-printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h
+printf '%s\n' "#define _GNU_SOURCE 1" >>confdefs.h
 
     DLLFLAGS="$DLLFLAGS -fPIC"
     LDDLLFLAGS="-fPIC"
     LDEXECFLAGS="-Wl,-pie"
     enable_wineandroid_drv=${enable_wineandroid_drv:-yes}
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-z,defs" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-z,defs" >&5
 printf %s "checking whether the compiler supports -Wl,-z,defs... " >&6; }
 if test ${ac_cv_cflags__Wl__z_defs+y}
 then :
@@ -12903,13 +12792,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__z_defs" >&5
-printf "%s\n" "$ac_cv_cflags__Wl__z_defs" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__z_defs" >&5
+printf '%s\n' "$ac_cv_cflags__Wl__z_defs" >&6; }
 if test "x$ac_cv_cflags__Wl__z_defs" = xyes
 then :
   UNIXLDFLAGS="$UNIXLDFLAGS -Wl,-z,defs"
 fi
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fPIC -Wl,--export-dynamic" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fPIC -Wl,--export-dynamic" >&5
 printf %s "checking whether the compiler supports -fPIC -Wl,--export-dynamic... " >&6; }
 if test ${ac_cv_cflags__fPIC__Wl___export_dynamic+y}
 then :
@@ -12933,14 +12822,14 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fPIC__Wl___export_dynamic" >&5
-printf "%s\n" "$ac_cv_cflags__fPIC__Wl___export_dynamic" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fPIC__Wl___export_dynamic" >&5
+printf '%s\n' "$ac_cv_cflags__fPIC__Wl___export_dynamic" >&6; }
 if test "x$ac_cv_cflags__fPIC__Wl___export_dynamic" = xyes
 then :
   WINELOADER_LDFLAGS="-Wl,--export-dynamic"
 fi
     WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7d400000"
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--rosegment" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--rosegment" >&5
 printf %s "checking whether the compiler supports -Wl,--rosegment... " >&6; }
 if test ${ac_cv_cflags__Wl___rosegment+y}
 then :
@@ -12964,14 +12853,14 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___rosegment" >&5
-printf "%s\n" "$ac_cv_cflags__Wl___rosegment" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___rosegment" >&5
+printf '%s\n' "$ac_cv_cflags__Wl___rosegment" >&6; }
 if test "x$ac_cv_cflags__Wl___rosegment" = xyes
 then :
   WINEPRELOADER_LDFLAGS="$WINEPRELOADER_LDFLAGS -Wl,--rosegment"
 fi
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lGLESv2" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lGLESv2" >&5
 printf %s "checking for -lGLESv2... " >&6; }
 if test ${ac_cv_lib_soname_GLESv2+y}
 then :
@@ -13022,21 +12911,21 @@ esac
 fi
 if ${ac_cv_lib_soname_GLESv2:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_GLESv2" >&5
-printf "%s\n" "$ac_cv_lib_soname_GLESv2" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_GLESv2" >&5
+printf '%s\n' "$ac_cv_lib_soname_GLESv2" >&6; }
 
-printf "%s\n" "#define SONAME_LIBGLESV2 \"$ac_cv_lib_soname_GLESv2\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBGLESV2 \"$ac_cv_lib_soname_GLESv2\"" >>confdefs.h
 
         ;;
 esac
 fi
     # Extract the first word of "gradle", so it can be a program name with args.
 set dummy gradle; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_path_GRADLE+y}
 then :
@@ -13059,7 +12948,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_path_GRADLE="$as_dir$ac_word$ac_exec_ext"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -13072,11 +12961,11 @@ esac
 fi
 GRADLE=$ac_cv_path_GRADLE
 if test -n "$GRADLE"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $GRADLE" >&5
-printf "%s\n" "$GRADLE" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $GRADLE" >&5
+printf '%s\n' "$GRADLE" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -13097,13 +12986,13 @@ fi
 
   *)
 
-printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h
+printf '%s\n' "#define _GNU_SOURCE 1" >>confdefs.h
 
     if test $HOST_ARCH = i386
     then
         DLLFLAGS="$DLLFLAGS -fno-PIC"
         LDDLLFLAGS="-fno-PIC"
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-PIC -Wl,-z,notext" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-PIC -Wl,-z,notext" >&5
 printf %s "checking whether the compiler supports -fno-PIC -Wl,-z,notext... " >&6; }
 if test ${ac_cv_cflags__fno_PIC__Wl__z_notext+y}
 then :
@@ -13127,8 +13016,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_PIC__Wl__z_notext" >&5
-printf "%s\n" "$ac_cv_cflags__fno_PIC__Wl__z_notext" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_PIC__Wl__z_notext" >&5
+printf '%s\n' "$ac_cv_cflags__fno_PIC__Wl__z_notext" >&6; }
 if test "x$ac_cv_cflags__fno_PIC__Wl__z_notext" = xyes
 then :
   LDDLLFLAGS="$LDDLLFLAGS -Wl,-z,notext"
@@ -13138,7 +13027,7 @@ fi
         LDDLLFLAGS="-fPIC"
     fi
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -Wl,-z,defs works correctly" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether -Wl,-z,defs works correctly" >&5
 printf %s "checking whether -Wl,-z,defs works correctly... " >&6; }
 if test ${ac_cv_wl_z_defs+y}
 then :
@@ -13162,11 +13051,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
                     CFLAGS=$ac_save_CFLAGS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_wl_z_defs" >&5
-printf "%s\n" "$ac_cv_wl_z_defs" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_wl_z_defs" >&5
+printf '%s\n' "$ac_cv_wl_z_defs" >&6; }
     test $ac_cv_wl_z_defs != yes || as_fn_append UNIXLDFLAGS " -Wl,-z,defs"
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--export-dynamic" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--export-dynamic" >&5
 printf %s "checking whether the compiler supports -Wl,--export-dynamic... " >&6; }
 if test ${ac_cv_cflags__Wl___export_dynamic+y}
 then :
@@ -13190,8 +13079,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___export_dynamic" >&5
-printf "%s\n" "$ac_cv_cflags__Wl___export_dynamic" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___export_dynamic" >&5
+printf '%s\n' "$ac_cv_cflags__Wl___export_dynamic" >&6; }
 if test "x$ac_cv_cflags__Wl___export_dynamic" = xyes
 then :
   WINELOADER_LDFLAGS="-Wl,--export-dynamic"
@@ -13205,7 +13094,7 @@ fi
           i386|arm)
             as_fn_append WINEPRELOADER_LDFLAGS " -static -Wl,-Ttext=0x7d400000" ;;
           *)
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -static-pie" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -static-pie" >&5
 printf %s "checking whether the compiler supports -static-pie... " >&6; }
 if test ${ac_cv_cflags__static_pie+y}
 then :
@@ -13229,8 +13118,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__static_pie" >&5
-printf "%s\n" "$ac_cv_cflags__static_pie" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__static_pie" >&5
+printf '%s\n' "$ac_cv_cflags__static_pie" >&6; }
 if test "x$ac_cv_cflags__static_pie" = xyes
 then :
   as_fn_append WINEPRELOADER_LDFLAGS " -static-pie"
@@ -13244,7 +13133,7 @@ fi
       *)
         case $HOST_ARCH in
           i386|arm)
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-Ttext-segment=0x60000000" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-Ttext-segment=0x60000000" >&5
 printf %s "checking whether the compiler supports -Wl,-Ttext-segment=0x60000000... " >&6; }
 if test ${ac_cv_cflags__Wl__Ttext_segment_0x60000000+y}
 then :
@@ -13268,13 +13157,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__Ttext_segment_0x60000000" >&5
-printf "%s\n" "$ac_cv_cflags__Wl__Ttext_segment_0x60000000" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__Ttext_segment_0x60000000" >&5
+printf '%s\n' "$ac_cv_cflags__Wl__Ttext_segment_0x60000000" >&6; }
 if test "x$ac_cv_cflags__Wl__Ttext_segment_0x60000000" = xyes
 then :
   as_fn_append WINELOADER_LDFLAGS " -Wl,-Ttext-segment=0x60000000"
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--section-start,.interp=0x60000400" >&5
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--section-start,.interp=0x60000400" >&5
 printf %s "checking whether the compiler supports -Wl,--section-start,.interp=0x60000400... " >&6; }
 if test ${ac_cv_cflags__Wl___section_start__interp_0x60000400+y}
 then :
@@ -13298,8 +13187,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___section_start__interp_0x60000400" >&5
-printf "%s\n" "$ac_cv_cflags__Wl___section_start__interp_0x60000400" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___section_start__interp_0x60000400" >&5
+printf '%s\n' "$ac_cv_cflags__Wl___section_start__interp_0x60000400" >&6; }
 if test "x$ac_cv_cflags__Wl___section_start__interp_0x60000400" = xyes
 then :
   as_fn_append WINELOADER_LDFLAGS " -Wl,--section-start,.interp=0x60000400"
@@ -13334,7 +13223,7 @@ for ac_prog in $ac_prefix_list
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_prog_MINGW_PKG_CONFIG+y}
 then :
@@ -13355,7 +13244,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_prog_MINGW_PKG_CONFIG="$ac_prog"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -13367,11 +13256,11 @@ esac
 fi
 MINGW_PKG_CONFIG=$ac_cv_prog_MINGW_PKG_CONFIG
 if test -n "$MINGW_PKG_CONFIG"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MINGW_PKG_CONFIG" >&5
-printf "%s\n" "$MINGW_PKG_CONFIG" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $MINGW_PKG_CONFIG" >&5
+printf '%s\n' "$MINGW_PKG_CONFIG" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -13398,7 +13287,7 @@ if test -n "$FAUDIO_PE_CFLAGS$FAUDIO_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $FAUDIO_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW FAudio.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW FAudio.h" >&5
 printf %s "checking for MinGW FAudio.h... " >&6; }
 if test ${ac_cv_mingw_header_FAudio_h+y}
 then :
@@ -13432,11 +13321,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_FAudio_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_FAudio_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_FAudio_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_FAudio_h" >&6; }
 if test "x$ac_cv_mingw_header_FAudio_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for FAudio_CommitOperationSet in MinGW -lFAudio" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for FAudio_CommitOperationSet in MinGW -lFAudio" >&5
 printf %s "checking for FAudio_CommitOperationSet in MinGW -lFAudio... " >&6; }
 if test ${ac_cv_mingw_lib_FAudio+y}
 then :
@@ -13483,8 +13372,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_FAudio" >&5
-printf "%s\n" "$ac_cv_mingw_lib_FAudio" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_FAudio" >&5
+printf '%s\n' "$ac_cv_mingw_lib_FAudio" >&6; }
 if test "x$ac_cv_mingw_lib_FAudio" = xyes
 then :
   :
@@ -13523,7 +13412,7 @@ if test -n "$FLUIDSYNTH_PE_CFLAGS$FLUIDSYNTH_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $FLUIDSYNTH_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW fluidsynth.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW fluidsynth.h" >&5
 printf %s "checking for MinGW fluidsynth.h... " >&6; }
 if test ${ac_cv_mingw_header_fluidsynth_h+y}
 then :
@@ -13557,11 +13446,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_fluidsynth_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_fluidsynth_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_fluidsynth_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_fluidsynth_h" >&6; }
 if test "x$ac_cv_mingw_header_fluidsynth_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for new_fluid_synth in MinGW -lfluidsynth" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for new_fluid_synth in MinGW -lfluidsynth" >&5
 printf %s "checking for new_fluid_synth in MinGW -lfluidsynth... " >&6; }
 if test ${ac_cv_mingw_lib_fluidsynth+y}
 then :
@@ -13608,8 +13497,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_fluidsynth" >&5
-printf "%s\n" "$ac_cv_mingw_lib_fluidsynth" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_fluidsynth" >&5
+printf '%s\n' "$ac_cv_mingw_lib_fluidsynth" >&6; }
 if test "x$ac_cv_mingw_lib_fluidsynth" = xyes
 then :
   :
@@ -13648,7 +13537,7 @@ if test -n "$JPEG_PE_CFLAGS$JPEG_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $JPEG_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW jpeglib.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW jpeglib.h" >&5
 printf %s "checking for MinGW jpeglib.h... " >&6; }
 if test ${ac_cv_mingw_header_jpeglib_h+y}
 then :
@@ -13683,11 +13572,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_jpeglib_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_jpeglib_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_jpeglib_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_jpeglib_h" >&6; }
 if test "x$ac_cv_mingw_header_jpeglib_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for jpeg_start_decompress in MinGW -ljpeg" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for jpeg_start_decompress in MinGW -ljpeg" >&5
 printf %s "checking for jpeg_start_decompress in MinGW -ljpeg... " >&6; }
 if test ${ac_cv_mingw_lib_jpeg+y}
 then :
@@ -13734,8 +13623,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_jpeg" >&5
-printf "%s\n" "$ac_cv_mingw_lib_jpeg" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_jpeg" >&5
+printf '%s\n' "$ac_cv_mingw_lib_jpeg" >&6; }
 if test "x$ac_cv_mingw_lib_jpeg" = xyes
 then :
   :
@@ -13774,7 +13663,7 @@ if test -n "$LCMS2_PE_CFLAGS$LCMS2_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $LCMS2_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW lcms2.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW lcms2.h" >&5
 printf %s "checking for MinGW lcms2.h... " >&6; }
 if test ${ac_cv_mingw_header_lcms2_h+y}
 then :
@@ -13808,11 +13697,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_lcms2_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_lcms2_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_lcms2_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_lcms2_h" >&6; }
 if test "x$ac_cv_mingw_header_lcms2_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cmsOpenProfileFromFile in MinGW -llcms2" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for cmsOpenProfileFromFile in MinGW -llcms2" >&5
 printf %s "checking for cmsOpenProfileFromFile in MinGW -llcms2... " >&6; }
 if test ${ac_cv_mingw_lib_lcms2+y}
 then :
@@ -13859,8 +13748,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_lcms2" >&5
-printf "%s\n" "$ac_cv_mingw_lib_lcms2" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_lcms2" >&5
+printf '%s\n' "$ac_cv_mingw_lib_lcms2" >&6; }
 if test "x$ac_cv_mingw_lib_lcms2" = xyes
 then :
   :
@@ -13899,7 +13788,7 @@ if test -n "$MPG123_PE_CFLAGS$MPG123_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $MPG123_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW mpg123.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW mpg123.h" >&5
 printf %s "checking for MinGW mpg123.h... " >&6; }
 if test ${ac_cv_mingw_header_mpg123_h+y}
 then :
@@ -13933,11 +13822,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_mpg123_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_mpg123_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_mpg123_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_mpg123_h" >&6; }
 if test "x$ac_cv_mingw_header_mpg123_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for mpg123_feed in MinGW -lmpg123" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for mpg123_feed in MinGW -lmpg123" >&5
 printf %s "checking for mpg123_feed in MinGW -lmpg123... " >&6; }
 if test ${ac_cv_mingw_lib_mpg123+y}
 then :
@@ -13984,8 +13873,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_mpg123" >&5
-printf "%s\n" "$ac_cv_mingw_lib_mpg123" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_mpg123" >&5
+printf '%s\n' "$ac_cv_mingw_lib_mpg123" >&6; }
 if test "x$ac_cv_mingw_lib_mpg123" = xyes
 then :
   :
@@ -14024,7 +13913,7 @@ if test -n "$PNG_PE_CFLAGS$PNG_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $PNG_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW png.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW png.h" >&5
 printf %s "checking for MinGW png.h... " >&6; }
 if test ${ac_cv_mingw_header_png_h+y}
 then :
@@ -14058,11 +13947,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_png_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_png_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_png_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_png_h" >&6; }
 if test "x$ac_cv_mingw_header_png_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for png_create_read_struct in MinGW -lpng" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for png_create_read_struct in MinGW -lpng" >&5
 printf %s "checking for png_create_read_struct in MinGW -lpng... " >&6; }
 if test ${ac_cv_mingw_lib_png+y}
 then :
@@ -14109,8 +13998,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_png" >&5
-printf "%s\n" "$ac_cv_mingw_lib_png" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_png" >&5
+printf '%s\n' "$ac_cv_mingw_lib_png" >&6; }
 if test "x$ac_cv_mingw_lib_png" = xyes
 then :
   :
@@ -14149,7 +14038,7 @@ if test -n "$TIFF_PE_CFLAGS$TIFF_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $TIFF_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW tiffio.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW tiffio.h" >&5
 printf %s "checking for MinGW tiffio.h... " >&6; }
 if test ${ac_cv_mingw_header_tiffio_h+y}
 then :
@@ -14183,11 +14072,11 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_tiffio_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_tiffio_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_tiffio_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_tiffio_h" >&6; }
 if test "x$ac_cv_mingw_header_tiffio_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for TIFFClientOpen in MinGW -ltiff" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for TIFFClientOpen in MinGW -ltiff" >&5
 printf %s "checking for TIFFClientOpen in MinGW -ltiff... " >&6; }
 if test ${ac_cv_mingw_lib_tiff+y}
 then :
@@ -14234,8 +14123,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_tiff" >&5
-printf "%s\n" "$ac_cv_mingw_lib_tiff" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_tiff" >&5
+printf '%s\n' "$ac_cv_mingw_lib_tiff" >&6; }
 if test "x$ac_cv_mingw_lib_tiff" = xyes
 then :
   :
@@ -14274,7 +14163,7 @@ if test -n "$VKD3D_PE_CFLAGS$VKD3D_PE_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $VKD3D_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW vkd3d_utils.h" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW vkd3d_utils.h" >&5
 printf %s "checking for MinGW vkd3d_utils.h... " >&6; }
 if test ${ac_cv_mingw_header_vkd3d_utils_h+y}
 then :
@@ -14308,13 +14197,13 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_vkd3d_utils_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_vkd3d_utils_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_vkd3d_utils_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_vkd3d_utils_h" >&6; }
 if test "x$ac_cv_mingw_header_vkd3d_utils_h" = xyes
 then :
 
 fi
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW vkd3d_shader.h" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for MinGW vkd3d_shader.h" >&5
 printf %s "checking for MinGW vkd3d_shader.h... " >&6; }
 if test ${ac_cv_mingw_header_vkd3d_shader_h+y}
 then :
@@ -14348,15 +14237,15 @@ CC=$ac_wine_check_headers_saved_cc
 ac_exeext=$ac_wine_check_headers_saved_exeext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_vkd3d_shader_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_vkd3d_shader_h" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_vkd3d_shader_h" >&5
+printf '%s\n' "$ac_cv_mingw_header_vkd3d_shader_h" >&6; }
 if test "x$ac_cv_mingw_header_vkd3d_shader_h" = xyes
 then :
 
 fi
         if test "$ac_cv_mingw_header_vkd3d_utils_h" = "yes" -a "$ac_cv_mingw_header_vkd3d_shader_h" = "yes"
         then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for vkd3d_utils_set_log_callback in MinGW -lvkd3d-utils" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for vkd3d_utils_set_log_callback in MinGW -lvkd3d-utils" >&5
 printf %s "checking for vkd3d_utils_set_log_callback in MinGW -lvkd3d-utils... " >&6; }
 if test ${ac_cv_mingw_lib_vkd3d_utils+y}
 then :
@@ -14403,8 +14292,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_vkd3d_utils" >&5
-printf "%s\n" "$ac_cv_mingw_lib_vkd3d_utils" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_vkd3d_utils" >&5
+printf '%s\n' "$ac_cv_mingw_lib_vkd3d_utils" >&6; }
 if test "x$ac_cv_mingw_lib_vkd3d_utils" = xyes
 then :
   :
@@ -14412,7 +14301,7 @@ else case e in #(
   e) : ;;
 esac
 fi
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for vkd3d_shader_build_varying_map in MinGW -lvkd3d-shader" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for vkd3d_shader_build_varying_map in MinGW -lvkd3d-shader" >&5
 printf %s "checking for vkd3d_shader_build_varying_map in MinGW -lvkd3d-shader... " >&6; }
 if test ${ac_cv_mingw_lib_vkd3d_shader+y}
 then :
@@ -14459,8 +14348,8 @@ ac_exeext=$ac_wine_check_headers_saved_exeext
 LIBS=$ac_wine_check_headers_saved_libs ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_vkd3d_shader" >&5
-printf "%s\n" "$ac_cv_mingw_lib_vkd3d_shader" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_vkd3d_shader" >&5
+printf '%s\n' "$ac_cv_mingw_lib_vkd3d_shader" >&6; }
 if test "x$ac_cv_mingw_lib_vkd3d_shader" = xyes
 then :
   :
@@ -14484,131 +14373,6 @@ fi
     then
         as_fn_append wine_notices "|libvkd3d ${notice_platform}MinGW development files not found (or too old); using bundled version."
     fi
-
-    if ${ZLIB_PE_CFLAGS:+false} :
-then :
-  if test ${MINGW_PKG_CONFIG+y}
-then :
-  ZLIB_PE_CFLAGS=`$MINGW_PKG_CONFIG --cflags zlib 2>/dev/null`
-fi
-fi
-if ${ZLIB_PE_LIBS:+false} :
-then :
-  if test ${MINGW_PKG_CONFIG+y}
-then :
-  ZLIB_PE_LIBS=`$MINGW_PKG_CONFIG --libs zlib 2>/dev/null`
-fi
-fi
-ZLIB_PE_LIBS=${ZLIB_PE_LIBS:-"-lz"}
-if test -n "$ZLIB_PE_CFLAGS$ZLIB_PE_LIBS"
-then :
-  ac_save_CPPFLAGS=$CPPFLAGS
-CPPFLAGS="$CPPFLAGS $ZLIB_PE_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for MinGW zlib.h" >&5
-printf %s "checking for MinGW zlib.h... " >&6; }
-if test ${ac_cv_mingw_header_zlib_h+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) ac_wine_check_headers_saved_cc=$CC
-ac_wine_check_headers_saved_exeext=$ac_exeext
-eval CC=\$${wine_arch}_CC
-ac_exeext=".exe"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-#include <zlib.h>
-int
-main (void)
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"
-then :
-  ac_cv_mingw_header_zlib_h=yes
-else case e in #(
-  e) ac_cv_mingw_header_zlib_h=no ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-CC=$ac_wine_check_headers_saved_cc
-ac_exeext=$ac_wine_check_headers_saved_exeext ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_header_zlib_h" >&5
-printf "%s\n" "$ac_cv_mingw_header_zlib_h" >&6; }
-if test "x$ac_cv_mingw_header_zlib_h" = xyes
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inflate in MinGW -lz" >&5
-printf %s "checking for inflate in MinGW -lz... " >&6; }
-if test ${ac_cv_mingw_lib_z+y}
-then :
-  printf %s "(cached) " >&6
-else case e in #(
-  e) ac_wine_check_headers_saved_cc=$CC
-ac_wine_check_headers_saved_exeext=$ac_exeext
-ac_wine_check_headers_saved_libs=$LIBS
-eval CC=\$${wine_arch}_CC
-ac_exeext=".exe"
-LIBS="-lz $ZLIB_PE_LIBS $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.
-   The 'extern "C"' is for builds by C++ compilers;
-   although this is not generally supported in C code supporting it here
-   has little cost and some practical benefit (sr 110532).  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char inflate (void);
-int
-main (void)
-{
-return inflate ();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
-  ac_cv_mingw_lib_z=yes
-else case e in #(
-  e) ac_cv_mingw_lib_z=no ;;
-esac
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
-    conftest$ac_exeext conftest.$ac_ext
-CC=$ac_wine_check_headers_saved_cc
-ac_exeext=$ac_wine_check_headers_saved_exeext
-LIBS=$ac_wine_check_headers_saved_libs ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mingw_lib_z" >&5
-printf "%s\n" "$ac_cv_mingw_lib_z" >&6; }
-if test "x$ac_cv_mingw_lib_z" = xyes
-then :
-  :
-else case e in #(
-  e) ZLIB_PE_CFLAGS=""; ZLIB_PE_LIBS="" ;;
-esac
-fi
-else case e in #(
-  e) ZLIB_PE_CFLAGS=""; ZLIB_PE_LIBS="" ;;
-esac
-fi
-CPPFLAGS=$ac_save_CPPFLAGS
-fi
-
-    if test "x$ZLIB_PE_LIBS" = "x"
-    then
-        as_fn_append wine_notices "|zlib ${notice_platform}MinGW development files not found; using bundled version."
-    fi
 fi
 
 if ${CAPSTONE_PE_LIBS:+false} :
@@ -14625,8 +14389,8 @@ else case e in #(
   e) enable_capstone=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: capstone cflags: $CAPSTONE_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: capstone libs: $CAPSTONE_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: capstone cflags: $CAPSTONE_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: capstone libs: $CAPSTONE_PE_LIBS" >&5
 
 if ${CXX_PE_LIBS:+false} :
 then :
@@ -14642,8 +14406,8 @@ else case e in #(
   e) enable_cpp=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: c++ cflags: $CXX_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: c++ libs: $CXX_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: c++ cflags: $CXX_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: c++ libs: $CXX_PE_LIBS" >&5
 
 if ${CXXABI_PE_LIBS:+false} :
 then :
@@ -14659,8 +14423,8 @@ else case e in #(
   e) enable_cppabi=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: c++abi cflags: $CXXABI_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: c++abi libs: $CXXABI_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: c++abi cflags: $CXXABI_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: c++abi libs: $CXXABI_PE_LIBS" >&5
 
 if ${FAUDIO_PE_LIBS:+false} :
 then :
@@ -14676,8 +14440,8 @@ else case e in #(
   e) enable_faudio=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: faudio cflags: $FAUDIO_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: faudio libs: $FAUDIO_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: faudio cflags: $FAUDIO_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: faudio libs: $FAUDIO_PE_LIBS" >&5
 
 if ${FLUIDSYNTH_PE_LIBS:+false} :
 then :
@@ -14693,8 +14457,8 @@ else case e in #(
   e) enable_fluidsynth=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: fluidsynth cflags: $FLUIDSYNTH_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: fluidsynth libs: $FLUIDSYNTH_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: fluidsynth cflags: $FLUIDSYNTH_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: fluidsynth libs: $FLUIDSYNTH_PE_LIBS" >&5
 
 if ${GSM_PE_LIBS:+false} :
 then :
@@ -14710,8 +14474,8 @@ else case e in #(
   e) enable_gsm=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: gsm cflags: $GSM_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: gsm libs: $GSM_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: gsm cflags: $GSM_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: gsm libs: $GSM_PE_LIBS" >&5
 
 if ${ICUCOMMON_PE_LIBS:+false} :
 then :
@@ -14727,8 +14491,8 @@ else case e in #(
   e) enable_icucommon=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: icucommon cflags: $ICUCOMMON_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: icucommon libs: $ICUCOMMON_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: icucommon cflags: $ICUCOMMON_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: icucommon libs: $ICUCOMMON_PE_LIBS" >&5
 
 if ${ICUI18N_PE_LIBS:+false} :
 then :
@@ -14744,8 +14508,8 @@ else case e in #(
   e) enable_icui18n=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: icui18n cflags: $ICUI18N_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: icui18n libs: $ICUI18N_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: icui18n cflags: $ICUI18N_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: icui18n libs: $ICUI18N_PE_LIBS" >&5
 
 if ${JPEG_PE_LIBS:+false} :
 then :
@@ -14761,8 +14525,8 @@ else case e in #(
   e) enable_jpeg=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: jpeg cflags: $JPEG_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: jpeg libs: $JPEG_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: jpeg cflags: $JPEG_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: jpeg libs: $JPEG_PE_LIBS" >&5
 
 if ${JXR_PE_LIBS:+false} :
 then :
@@ -14778,8 +14542,8 @@ else case e in #(
   e) enable_jxr=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: jxr cflags: $JXR_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: jxr libs: $JXR_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: jxr cflags: $JXR_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: jxr libs: $JXR_PE_LIBS" >&5
 
 if ${LCMS2_PE_LIBS:+false} :
 then :
@@ -14795,8 +14559,8 @@ else case e in #(
   e) enable_lcms2=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: lcms2 cflags: $LCMS2_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: lcms2 libs: $LCMS2_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: lcms2 cflags: $LCMS2_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: lcms2 libs: $LCMS2_PE_LIBS" >&5
 
 if ${LDAP_PE_LIBS:+false} :
 then :
@@ -14812,8 +14576,8 @@ else case e in #(
   e) enable_ldap=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: ldap cflags: $LDAP_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: ldap libs: $LDAP_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: ldap cflags: $LDAP_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: ldap libs: $LDAP_PE_LIBS" >&5
 
 if ${MPG123_PE_LIBS:+false} :
 then :
@@ -14829,8 +14593,8 @@ else case e in #(
   e) enable_mpg123=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: mpg123 cflags: $MPG123_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: mpg123 libs: $MPG123_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: mpg123 cflags: $MPG123_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: mpg123 libs: $MPG123_PE_LIBS" >&5
 
 if ${MUSL_PE_LIBS:+false} :
 then :
@@ -14846,8 +14610,8 @@ else case e in #(
   e) enable_musl=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: musl cflags: $MUSL_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: musl libs: $MUSL_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: musl cflags: $MUSL_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: musl libs: $MUSL_PE_LIBS" >&5
 
 if ${PNG_PE_LIBS:+false} :
 then :
@@ -14863,8 +14627,8 @@ else case e in #(
   e) enable_png=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: png cflags: $PNG_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: png libs: $PNG_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: png cflags: $PNG_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: png libs: $PNG_PE_LIBS" >&5
 
 if ${SQLITE3_PE_LIBS:+false} :
 then :
@@ -14880,42 +14644,42 @@ else case e in #(
   e) enable_sqlite3=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: sqlite3 cflags: $SQLITE3_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: sqlite3 libs: $SQLITE3_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: sqlite3 cflags: $SQLITE3_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: sqlite3 libs: $SQLITE3_PE_LIBS" >&5
 
-if ${TIFF_PE_LIBS:+false} :
+if ${SYMCRYPT_PE_LIBS:+false} :
 then :
-  TIFF_PE_LIBS="tiff \$(ZLIB_PE_LIBS)"
-   if ${TIFF_PE_CFLAGS:+false} :
+  SYMCRYPT_PE_LIBS=symcrypt
+   if ${SYMCRYPT_PE_CFLAGS:+false} :
 then :
-  TIFF_PE_CFLAGS="-I\$(top_srcdir)/libs/tiff/libtiff"
+  SYMCRYPT_PE_CFLAGS="-I\$(top_srcdir)/libs/symcrypt/inc"
 else case e in #(
-  e) enable_tiff=no ;;
+  e) enable_symcrypt=no ;;
 esac
 fi
 else case e in #(
-  e) enable_tiff=no ;;
+  e) enable_symcrypt=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: tiff cflags: $TIFF_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: tiff libs: $TIFF_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: symcrypt cflags: $SYMCRYPT_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: symcrypt libs: $SYMCRYPT_PE_LIBS" >&5
 
-if ${TOMCRYPT_PE_LIBS:+false} :
+if ${TIFF_PE_LIBS:+false} :
 then :
-  TOMCRYPT_PE_LIBS=tomcrypt
-   if ${TOMCRYPT_PE_CFLAGS:+false} :
+  TIFF_PE_LIBS="tiff \$(ZLIB_PE_LIBS)"
+   if ${TIFF_PE_CFLAGS:+false} :
 then :
-  TOMCRYPT_PE_CFLAGS="-I\$(top_srcdir)/libs/tomcrypt/src/headers -DLTC_NO_PROTOTYPES -DLTC_SOURCE"
+  TIFF_PE_CFLAGS="-I\$(top_srcdir)/libs/tiff/libtiff"
 else case e in #(
-  e) enable_tomcrypt=no ;;
+  e) enable_tiff=no ;;
 esac
 fi
 else case e in #(
-  e) enable_tomcrypt=no ;;
+  e) enable_tiff=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: tomcrypt cflags: $TOMCRYPT_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: tomcrypt libs: $TOMCRYPT_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: tiff cflags: $TIFF_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: tiff libs: $TIFF_PE_LIBS" >&5
 
 if ${UNWIND_PE_LIBS:+false} :
 then :
@@ -14931,8 +14695,8 @@ else case e in #(
   e) enable_unwind=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: unwind cflags: $UNWIND_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: unwind libs: $UNWIND_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: unwind cflags: $UNWIND_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: unwind libs: $UNWIND_PE_LIBS" >&5
 
 if ${VKD3D_PE_LIBS:+false} :
 then :
@@ -14948,8 +14712,8 @@ else case e in #(
   e) enable_vkd3d=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: vkd3d cflags: $VKD3D_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: vkd3d libs: $VKD3D_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: vkd3d cflags: $VKD3D_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: vkd3d libs: $VKD3D_PE_LIBS" >&5
 
 if ${XML2_PE_LIBS:+false} :
 then :
@@ -14965,8 +14729,8 @@ else case e in #(
   e) enable_xml2=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xml2 cflags: $XML2_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xml2 libs: $XML2_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xml2 cflags: $XML2_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xml2 libs: $XML2_PE_LIBS" >&5
 
 if ${XSLT_PE_LIBS:+false} :
 then :
@@ -14982,8 +14746,8 @@ else case e in #(
   e) enable_xslt=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xslt cflags: $XSLT_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xslt libs: $XSLT_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xslt cflags: $XSLT_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xslt libs: $XSLT_PE_LIBS" >&5
 
 if ${ZLIB_PE_LIBS:+false} :
 then :
@@ -14999,8 +14763,8 @@ else case e in #(
   e) enable_zlib=no ;;
 esac
 fi
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: zlib cflags: $ZLIB_PE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: zlib libs: $ZLIB_PE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: zlib cflags: $ZLIB_PE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: zlib libs: $ZLIB_PE_LIBS" >&5
 
 
 
@@ -15013,7 +14777,7 @@ if test "x$ac_cv_func_pthread_create" = xyes
 then :
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
 printf %s "checking for pthread_create in -lpthread... " >&6; }
 if test ${ac_cv_lib_pthread_pthread_create+y}
 then :
@@ -15054,8 +14818,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
-printf "%s\n" "$ac_cv_lib_pthread_pthread_create" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+printf '%s\n' "$ac_cv_lib_pthread_pthread_create" >&6; }
 if test "x$ac_cv_lib_pthread_pthread_create" = xyes
 then :
   PTHREAD_LIBS="-lpthread"
@@ -15099,8 +14863,8 @@ fi
 fi
 
 EGL_LIBS=${EGL_LIBS:-"-lEGL"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: egl cflags: $EGL_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: egl libs: $EGL_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: egl cflags: $EGL_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: egl libs: $EGL_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: egl errors: " >&5
      cat conftest.err >&5
@@ -15110,7 +14874,7 @@ if test -n "$EGL_CFLAGS$EGL_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $EGL_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lEGL" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lEGL" >&5
 printf %s "checking for -lEGL... " >&6; }
 if test ${ac_cv_lib_soname_EGL+y}
 then :
@@ -15161,14 +14925,14 @@ esac
 fi
 if ${ac_cv_lib_soname_EGL:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_EGL" >&5
-printf "%s\n" "$ac_cv_lib_soname_EGL" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_EGL" >&5
+printf '%s\n' "$ac_cv_lib_soname_EGL" >&6; }
 
-printf "%s\n" "#define SONAME_LIBEGL \"$ac_cv_lib_soname_EGL\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBEGL \"$ac_cv_lib_soname_EGL\"" >>confdefs.h
 
         ;;
 esac
@@ -15194,7 +14958,7 @@ ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
 printf %s "checking how to run the C preprocessor... " >&6; }
 # On Suns, sometimes $CPP names a directory.
 if test -n "$CPP" && test -d "$CPP"; then
@@ -15265,8 +15029,8 @@ fi
 else
   ac_cv_prog_CPP=$CPP
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
-printf "%s\n" "$CPP" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+printf '%s\n' "$CPP" >&6; }
 ac_preproc_ok=false
 for ac_c_preproc_warn_flag in '' yes
 do
@@ -15314,8 +15078,8 @@ if $ac_preproc_ok
 then :
 
 else case e in #(
-  e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+  e) { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;}
 as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
 See 'config.log' for more details" "$LINENO" 5; } ;;
 esac
@@ -15328,7 +15092,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for X" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for X" >&5
 printf %s "checking for X... " >&6; }
 
 
@@ -15384,11 +15148,11 @@ if mkdir conftest.dir; then
   cd conftest.dir
   cat >Imakefile <<'_ACEOF'
 incroot:
-	@echo incroot='${INCROOT}'
+	@printf '%s\n' incroot='${INCROOT}'
 usrlibdir:
-	@echo usrlibdir='${USRLIBDIR}'
+	@printf '%s\n' usrlibdir='${USRLIBDIR}'
 libdir:
-	@echo libdir='${LIBDIR}'
+	@printf '%s\n' libdir='${LIBDIR}'
 _ACEOF
   if (export CC; ${XMKMF-xmkmf}) >/dev/null 2>/dev/null && test -f Makefile; then
     # GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
@@ -15508,7 +15272,7 @@ then :
 ac_x_libraries=
 else case e in #(
   e) LIBS=$ac_save_LIBS
-for ac_dir in `printf "%s\n" "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g`
+for ac_dir in `printf '%s\n' "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g`
 do
   # Don't even attempt the hair of trying to link an X program!
   for ac_extension in a so sl dylib la dll; do
@@ -15545,8 +15309,8 @@ fi
 fi # $with_x != no
 
 if test "$have_x" != yes; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $have_x" >&5
-printf "%s\n" "$have_x" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $have_x" >&5
+printf '%s\n' "$have_x" >&6; }
   no_x=yes
 else
   # If each of the values was on the command line, it overrides each guess.
@@ -15556,8 +15320,8 @@ else
   ac_cv_have_x="have_x=yes\
 	ac_x_includes='$x_includes'\
 	ac_x_libraries='$x_libraries'"
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: libraries $x_libraries, headers $x_includes" >&5
-printf "%s\n" "libraries $x_libraries, headers $x_includes" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: libraries $x_libraries, headers $x_includes" >&5
+printf '%s\n' "libraries $x_libraries, headers $x_includes" >&6; }
 fi
 
 if test "$have_x" = "yes"
@@ -15570,7 +15334,7 @@ then
     ac_save_CPPFLAGS="$CPPFLAGS"
     CPPFLAGS="$CPPFLAGS $X_CFLAGS"
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lX11" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lX11" >&5
 printf %s "checking for -lX11... " >&6; }
 if test ${ac_cv_lib_soname_X11+y}
 then :
@@ -15621,19 +15385,19 @@ esac
 fi
 if ${ac_cv_lib_soname_X11:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_X11" >&5
-printf "%s\n" "$ac_cv_lib_soname_X11" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_X11" >&5
+printf '%s\n' "$ac_cv_lib_soname_X11" >&6; }
 
-printf "%s\n" "#define SONAME_LIBX11 \"$ac_cv_lib_soname_X11\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBX11 \"$ac_cv_lib_soname_X11\"" >>confdefs.h
 
         ;;
 esac
 fi
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXext" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXext" >&5
 printf %s "checking for -lXext... " >&6; }
 if test ${ac_cv_lib_soname_Xext+y}
 then :
@@ -15684,14 +15448,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xext:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xext" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xext" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xext" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xext" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXEXT \"$ac_cv_lib_soname_Xext\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXEXT \"$ac_cv_lib_soname_Xext\"" >>confdefs.h
 
        X_LIBS="$X_LIBS -lXext" ;;
 esac
@@ -15705,63 +15469,63 @@ fi
 "
 if test "x$ac_cv_header_X11_extensions_shape_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_SHAPE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_SHAPE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/XInput2.h" "ac_cv_header_X11_extensions_XInput2_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_XInput2_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XINPUT2_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XINPUT2_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/XShm.h" "ac_cv_header_X11_extensions_XShm_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_XShm_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XSHM_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XSHM_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/Xfixes.h" "ac_cv_header_X11_extensions_Xfixes_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_Xfixes_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XFIXES_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XFIXES_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/Xinerama.h" "ac_cv_header_X11_extensions_Xinerama_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_Xinerama_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XINERAMA_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XINERAMA_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/Xrandr.h" "ac_cv_header_X11_extensions_Xrandr_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_Xrandr_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XRANDR_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XRANDR_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/Xrender.h" "ac_cv_header_X11_extensions_Xrender_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_Xrender_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XRENDER_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XRENDER_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/xf86vmode.h" "ac_cv_header_X11_extensions_xf86vmode_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_xf86vmode_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XF86VMODE_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XF86VMODE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "X11/extensions/xf86vmproto.h" "ac_cv_header_X11_extensions_xf86vmproto_h" "$xlib_includes
 "
 if test "x$ac_cv_header_X11_extensions_xf86vmproto_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_X11_EXTENSIONS_XF86VMPROTO_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_X11_EXTENSIONS_XF86VMPROTO_H 1" >>confdefs.h
 
 fi
 
@@ -15770,7 +15534,7 @@ fi
 "
 if test "x$ac_cv_header_X11_Xcursor_Xcursor_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXcursor" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXcursor" >&5
 printf %s "checking for -lXcursor... " >&6; }
 if test ${ac_cv_lib_soname_Xcursor+y}
 then :
@@ -15821,14 +15585,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xcursor:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xcursor" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xcursor" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xcursor" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xcursor" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXCURSOR \"$ac_cv_lib_soname_Xcursor\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXCURSOR \"$ac_cv_lib_soname_Xcursor\"" >>confdefs.h
 
         ;;
 esac
@@ -15850,7 +15614,7 @@ fi
 "
 if test "x$ac_cv_header_X11_extensions_XInput_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXi" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXi" >&5
 printf %s "checking for -lXi... " >&6; }
 if test ${ac_cv_lib_soname_Xi+y}
 then :
@@ -15901,14 +15665,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xi:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xi" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xi" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xi" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xi" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXI \"$ac_cv_lib_soname_Xi\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXI \"$ac_cv_lib_soname_Xi\"" >>confdefs.h
 
         ;;
 esac
@@ -15942,7 +15706,7 @@ fi
 
                 if test "$ac_cv_header_X11_extensions_XShm_h" = "yes"
         then
-              { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for XShmQueryExtension in -lXext" >&5
+              { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for XShmQueryExtension in -lXext" >&5
 printf %s "checking for XShmQueryExtension in -lXext... " >&6; }
 if test ${ac_cv_lib_Xext_XShmQueryExtension+y}
 then :
@@ -15983,12 +15747,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xext_XShmQueryExtension" >&5
-printf "%s\n" "$ac_cv_lib_Xext_XShmQueryExtension" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xext_XShmQueryExtension" >&5
+printf '%s\n' "$ac_cv_lib_Xext_XShmQueryExtension" >&6; }
 if test "x$ac_cv_lib_Xext_XShmQueryExtension" = xyes
 then :
 
-printf "%s\n" "#define HAVE_LIBXXSHM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LIBXXSHM 1" >>confdefs.h
 
 fi
 
@@ -16006,7 +15770,7 @@ fi
 
                 if test "$ac_cv_header_X11_extensions_shape_h" = "yes"
         then
-              { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for XShapeQueryExtension in -lXext" >&5
+              { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for XShapeQueryExtension in -lXext" >&5
 printf %s "checking for XShapeQueryExtension in -lXext... " >&6; }
 if test ${ac_cv_lib_Xext_XShapeQueryExtension+y}
 then :
@@ -16047,12 +15811,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xext_XShapeQueryExtension" >&5
-printf "%s\n" "$ac_cv_lib_Xext_XShapeQueryExtension" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xext_XShapeQueryExtension" >&5
+printf '%s\n' "$ac_cv_lib_Xext_XShapeQueryExtension" >&6; }
 if test "x$ac_cv_lib_Xext_XShapeQueryExtension" = xyes
 then :
 
-printf "%s\n" "#define HAVE_LIBXSHAPE 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LIBXSHAPE 1" >>confdefs.h
 
 fi
 
@@ -16070,7 +15834,7 @@ fi
 
                 if test "$ac_cv_header_X11_extensions_xf86vmode_h" = "yes" -o "$ac_cv_header_X11_extensions_xf86vmproto_h" = "yes"
         then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXxf86vm" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXxf86vm" >&5
 printf %s "checking for -lXxf86vm... " >&6; }
 if test ${ac_cv_lib_soname_Xxf86vm+y}
 then :
@@ -16121,14 +15885,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xxf86vm:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xxf86vm" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xxf86vm" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xxf86vm" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xxf86vm" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXXF86VM \"$ac_cv_lib_soname_Xxf86vm\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXXF86VM \"$ac_cv_lib_soname_Xxf86vm\"" >>confdefs.h
 
         ;;
 esac
@@ -16147,7 +15911,7 @@ fi
 
                 if test "$ac_cv_header_X11_extensions_Xrender_h" = "yes" -a "x$ac_cv_lib_soname_X11" != "x"
         then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXrender" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXrender" >&5
 printf %s "checking for -lXrender... " >&6; }
 if test ${ac_cv_lib_soname_Xrender+y}
 then :
@@ -16198,16 +15962,16 @@ esac
 fi
 if ${ac_cv_lib_soname_Xrender:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xrender" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xrender" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xrender" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xrender" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXRENDER \"$ac_cv_lib_soname_Xrender\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXRENDER \"$ac_cv_lib_soname_Xrender\"" >>confdefs.h
 
-       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for XRenderSetPictureTransform in -lXrender" >&5
+       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for XRenderSetPictureTransform in -lXrender" >&5
 printf %s "checking for XRenderSetPictureTransform in -lXrender... " >&6; }
 if test ${ac_cv_lib_Xrender_XRenderSetPictureTransform+y}
 then :
@@ -16248,16 +16012,16 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xrender_XRenderSetPictureTransform" >&5
-printf "%s\n" "$ac_cv_lib_Xrender_XRenderSetPictureTransform" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xrender_XRenderSetPictureTransform" >&5
+printf '%s\n' "$ac_cv_lib_Xrender_XRenderSetPictureTransform" >&6; }
 if test "x$ac_cv_lib_Xrender_XRenderSetPictureTransform" = xyes
 then :
 
-printf "%s\n" "#define HAVE_XRENDERSETPICTURETRANSFORM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_XRENDERSETPICTURETRANSFORM 1" >>confdefs.h
 
 fi
 
-               { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for XRenderCreateLinearGradient in -lXrender" >&5
+               { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for XRenderCreateLinearGradient in -lXrender" >&5
 printf %s "checking for XRenderCreateLinearGradient in -lXrender... " >&6; }
 if test ${ac_cv_lib_Xrender_XRenderCreateLinearGradient+y}
 then :
@@ -16298,12 +16062,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xrender_XRenderCreateLinearGradient" >&5
-printf "%s\n" "$ac_cv_lib_Xrender_XRenderCreateLinearGradient" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xrender_XRenderCreateLinearGradient" >&5
+printf '%s\n' "$ac_cv_lib_Xrender_XRenderCreateLinearGradient" >&6; }
 if test "x$ac_cv_lib_Xrender_XRenderCreateLinearGradient" = xyes
 then :
 
-printf "%s\n" "#define HAVE_XRENDERCREATELINEARGRADIENT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_XRENDERCREATELINEARGRADIENT 1" >>confdefs.h
 
 fi
  ;;
@@ -16338,7 +16102,7 @@ static typeof(XRRSetScreenConfigAndRate) * func; if (func) return 0;
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXrandr" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXrandr" >&5
 printf %s "checking for -lXrandr... " >&6; }
 if test ${ac_cv_lib_soname_Xrandr+y}
 then :
@@ -16389,14 +16153,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xrandr:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xrandr" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xrandr" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xrandr" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xrandr" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXRANDR \"$ac_cv_lib_soname_Xrandr\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXRANDR \"$ac_cv_lib_soname_Xrandr\"" >>confdefs.h
 
        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -16413,7 +16177,7 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"
 then :
 
-printf "%s\n" "#define HAVE_XRRGETPROVIDERRESOURCES 1" >>confdefs.h
+printf '%s\n' "#define HAVE_XRRGETPROVIDERRESOURCES 1" >>confdefs.h
 
 else case e in #(
   e) as_fn_append wine_notices "|libxrandr ${notice_platform}development files too old, XRandR display device handler won't be supported." ;;
@@ -16452,7 +16216,7 @@ static typeof(XFixesQueryVersion) * func; if (func) return 0;
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXfixes" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXfixes" >&5
 printf %s "checking for -lXfixes... " >&6; }
 if test ${ac_cv_lib_soname_Xfixes+y}
 then :
@@ -16503,14 +16267,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xfixes:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xfixes" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xfixes" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xfixes" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xfixes" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXFIXES \"$ac_cv_lib_soname_Xfixes\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXFIXES \"$ac_cv_lib_soname_Xfixes\"" >>confdefs.h
 
         ;;
 esac
@@ -16545,7 +16309,7 @@ static typeof(XineramaQueryScreens) * func; if (func) return 0;
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXinerama" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXinerama" >&5
 printf %s "checking for -lXinerama... " >&6; }
 if test ${ac_cv_lib_soname_Xinerama+y}
 then :
@@ -16596,14 +16360,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xinerama:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xinerama" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xinerama" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xinerama" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xinerama" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXINERAMA \"$ac_cv_lib_soname_Xinerama\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXINERAMA \"$ac_cv_lib_soname_Xinerama\"" >>confdefs.h
 
         ;;
 esac
@@ -16626,7 +16390,7 @@ fi
 "
 if test "x$ac_cv_header_X11_extensions_Xcomposite_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lXcomposite" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lXcomposite" >&5
 printf %s "checking for -lXcomposite... " >&6; }
 if test ${ac_cv_lib_soname_Xcomposite+y}
 then :
@@ -16677,14 +16441,14 @@ esac
 fi
 if ${ac_cv_lib_soname_Xcomposite:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xcomposite" >&5
-printf "%s\n" "$ac_cv_lib_soname_Xcomposite" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_Xcomposite" >&5
+printf '%s\n' "$ac_cv_lib_soname_Xcomposite" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXCOMPOSITE \"$ac_cv_lib_soname_Xcomposite\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXCOMPOSITE \"$ac_cv_lib_soname_Xcomposite\"" >>confdefs.h
 
         ;;
 esac
@@ -16707,7 +16471,7 @@ fi
 if test "x$ac_cv_member_XICCallback_callback" = xyes
 then :
 
-printf "%s\n" "#define HAVE_XICCALLBACK_CALLBACK 1" >>confdefs.h
+printf '%s\n' "#define HAVE_XICCALLBACK_CALLBACK 1" >>confdefs.h
 
 
 fi
@@ -16716,7 +16480,7 @@ ac_fn_c_check_member "$LINENO" "XEvent" "xcookie" "ac_cv_member_XEvent_xcookie"
 if test "x$ac_cv_member_XEvent_xcookie" = xyes
 then :
 
-printf "%s\n" "#define HAVE_XEVENT_XCOOKIE 1" >>confdefs.h
+printf '%s\n' "#define HAVE_XEVENT_XCOOKIE 1" >>confdefs.h
 
 
 fi
@@ -16726,7 +16490,7 @@ fi
         opengl_msg=""
     if test "x$with_opengl" != "xno"
     then
-	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lGL" >&5
+	{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lGL" >&5
 printf %s "checking for -lGL... " >&6; }
 if test ${ac_cv_lib_soname_GL+y}
 then :
@@ -16777,9 +16541,9 @@ esac
 fi
 if ${ac_cv_lib_soname_GL:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
-       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lGL" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
+       { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lGL" >&5
 printf %s "checking for -lGL... " >&6; }
 if test ${ac_cv_lib_soname_GL+y}
 then :
@@ -16830,8 +16594,8 @@ esac
 fi
 if ${ac_cv_lib_soname_GL:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        if test -f /usr/X11R6/lib/libGL.a
                             then
 	                        opengl_msg="/usr/X11R6/lib/libGL.a is present on your system.
@@ -16840,19 +16604,19 @@ This probably prevents linking to OpenGL. Try deleting the file and restarting c
                                 opengl_msg="No OpenGL library found on this system."
                             fi
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_GL" >&5
-printf "%s\n" "$ac_cv_lib_soname_GL" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_GL" >&5
+printf '%s\n' "$ac_cv_lib_soname_GL" >&6; }
 
-printf "%s\n" "#define SONAME_LIBGL \"$ac_cv_lib_soname_GL\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBGL \"$ac_cv_lib_soname_GL\"" >>confdefs.h
 
        OPENGL_LIBS="-Xlinker -dylib_file -Xlinker /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib -lGL" ;;
 esac
 fi
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_GL" >&5
-printf "%s\n" "$ac_cv_lib_soname_GL" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_GL" >&5
+printf '%s\n' "$ac_cv_lib_soname_GL" >&6; }
 
-printf "%s\n" "#define SONAME_LIBGL \"$ac_cv_lib_soname_GL\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBGL \"$ac_cv_lib_soname_GL\"" >>confdefs.h
 
        OPENGL_LIBS="-lGL" ;;
 esac
@@ -16897,8 +16661,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbregistry cflags: $XKBREGISTRY_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbregistry libs: $XKBREGISTRY_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xkbregistry cflags: $XKBREGISTRY_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xkbregistry libs: $XKBREGISTRY_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: xkbregistry errors: " >&5
      cat conftest.err >&5
@@ -16911,7 +16675,7 @@ CPPFLAGS="$CPPFLAGS $XKBREGISTRY_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "xkbcommon/xkbregistry.h" "ac_cv_header_xkbcommon_xkbregistry_h" "$ac_includes_default"
 if test "x$ac_cv_header_xkbcommon_xkbregistry_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lxkbregistry" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lxkbregistry" >&5
 printf %s "checking for -lxkbregistry... " >&6; }
 if test ${ac_cv_lib_soname_xkbregistry+y}
 then :
@@ -16962,14 +16726,14 @@ esac
 fi
 if ${ac_cv_lib_soname_xkbregistry:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        XKBREGISTRY_LIBS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_xkbregistry" >&5
-printf "%s\n" "$ac_cv_lib_soname_xkbregistry" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_xkbregistry" >&5
+printf '%s\n' "$ac_cv_lib_soname_xkbregistry" >&6; }
 
-printf "%s\n" "#define SONAME_LIBXKBREGISTRY \"$ac_cv_lib_soname_xkbregistry\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBXKBREGISTRY \"$ac_cv_lib_soname_xkbregistry\"" >>confdefs.h
 
        : ;;
 esac
@@ -17001,8 +16765,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: wayland-client cflags: $WAYLAND_CLIENT_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: wayland-client libs: $WAYLAND_CLIENT_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: wayland-client cflags: $WAYLAND_CLIENT_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: wayland-client libs: $WAYLAND_CLIENT_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: wayland-client errors: " >&5
      cat conftest.err >&5
@@ -17015,7 +16779,7 @@ CPPFLAGS="$CPPFLAGS $WAYLAND_CLIENT_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "wayland-client.h" "ac_cv_header_wayland_client_h" "$ac_includes_default"
 if test "x$ac_cv_header_wayland_client_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wl_display_connect in -lwayland-client" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for wl_display_connect in -lwayland-client" >&5
 printf %s "checking for wl_display_connect in -lwayland-client... " >&6; }
 if test ${ac_cv_lib_wayland_client_wl_display_connect+y}
 then :
@@ -17056,13 +16820,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wayland_client_wl_display_connect" >&5
-printf "%s\n" "$ac_cv_lib_wayland_client_wl_display_connect" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wayland_client_wl_display_connect" >&5
+printf '%s\n' "$ac_cv_lib_wayland_client_wl_display_connect" >&6; }
 if test "x$ac_cv_lib_wayland_client_wl_display_connect" = xyes
 then :
   # Extract the first word of "wayland-scanner", so it can be a program name with args.
 set dummy wayland-scanner; ac_word=$2
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 printf %s "checking for $ac_word... " >&6; }
 if test ${ac_cv_path_WAYLAND_SCANNER+y}
 then :
@@ -17085,7 +16849,7 @@ do
     for ac_exec_ext in '' $ac_executable_extensions; do
   if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
     ac_cv_path_WAYLAND_SCANNER="$as_dir$ac_word$ac_exec_ext"
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
     break 2
   fi
 done
@@ -17099,11 +16863,11 @@ esac
 fi
 WAYLAND_SCANNER=$ac_cv_path_WAYLAND_SCANNER
 if test -n "$WAYLAND_SCANNER"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $WAYLAND_SCANNER" >&5
-printf "%s\n" "$WAYLAND_SCANNER" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $WAYLAND_SCANNER" >&5
+printf '%s\n' "$WAYLAND_SCANNER" >&6; }
 else
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
 fi
 
 
@@ -17135,8 +16899,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbcommon cflags: $XKBCOMMON_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: xkbcommon libs: $XKBCOMMON_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xkbcommon cflags: $XKBCOMMON_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: xkbcommon libs: $XKBCOMMON_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: xkbcommon errors: " >&5
      cat conftest.err >&5
@@ -17146,7 +16910,7 @@ if test -n "$XKBCOMMON_CFLAGS$XKBCOMMON_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $XKBCOMMON_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for xkb_context_new in -lxkbcommon" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for xkb_context_new in -lxkbcommon" >&5
 printf %s "checking for xkb_context_new in -lxkbcommon... " >&6; }
 if test ${ac_cv_lib_xkbcommon_xkb_context_new+y}
 then :
@@ -17187,8 +16951,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xkbcommon_xkb_context_new" >&5
-printf "%s\n" "$ac_cv_lib_xkbcommon_xkb_context_new" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xkbcommon_xkb_context_new" >&5
+printf '%s\n' "$ac_cv_lib_xkbcommon_xkb_context_new" >&6; }
 if test "x$ac_cv_lib_xkbcommon_xkb_context_new" = xyes
 then :
   :
@@ -17220,8 +16984,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: wayland-egl cflags: $WAYLAND_EGL_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: wayland-egl libs: $WAYLAND_EGL_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: wayland-egl cflags: $WAYLAND_EGL_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: wayland-egl libs: $WAYLAND_EGL_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: wayland-egl errors: " >&5
      cat conftest.err >&5
@@ -17234,7 +16998,7 @@ CPPFLAGS="$CPPFLAGS $WAYLAND_EGL_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "wayland-egl.h" "ac_cv_header_wayland_egl_h" "$ac_includes_default"
 if test "x$ac_cv_header_wayland_egl_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wl_egl_window_create in -lwayland-egl" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for wl_egl_window_create in -lwayland-egl" >&5
 printf %s "checking for wl_egl_window_create in -lwayland-egl... " >&6; }
 if test ${ac_cv_lib_wayland_egl_wl_egl_window_create+y}
 then :
@@ -17275,12 +17039,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wayland_egl_wl_egl_window_create" >&5
-printf "%s\n" "$ac_cv_lib_wayland_egl_wl_egl_window_create" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wayland_egl_wl_egl_window_create" >&5
+printf '%s\n' "$ac_cv_lib_wayland_egl_wl_egl_window_create" >&6; }
 if test "x$ac_cv_lib_wayland_egl_wl_egl_window_create" = xyes
 then :
 
-printf "%s\n" "#define HAVE_LIBWAYLAND_EGL 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LIBWAYLAND_EGL 1" >>confdefs.h
 
 else case e in #(
   e) WAYLAND_EGL_LIBS="" ;;
@@ -17346,7 +17110,7 @@ fi
 
 if test "$ac_cv_header_CL_cl_h" = "yes"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clGetPlatformInfo in -lOpenCL" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for clGetPlatformInfo in -lOpenCL" >&5
 printf %s "checking for clGetPlatformInfo in -lOpenCL... " >&6; }
 if test ${ac_cv_lib_OpenCL_clGetPlatformInfo+y}
 then :
@@ -17387,8 +17151,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_OpenCL_clGetPlatformInfo" >&5
-printf "%s\n" "$ac_cv_lib_OpenCL_clGetPlatformInfo" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_OpenCL_clGetPlatformInfo" >&5
+printf '%s\n' "$ac_cv_lib_OpenCL_clGetPlatformInfo" >&6; }
 if test "x$ac_cv_lib_OpenCL_clGetPlatformInfo" = xyes
 then :
   OPENCL_LIBS="-lOpenCL"
@@ -17409,7 +17173,7 @@ fi
 
 if test "$ac_cv_header_pcap_pcap_h" = "yes"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcap_init in -lpcap" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for pcap_init in -lpcap" >&5
 printf %s "checking for pcap_init in -lpcap... " >&6; }
 if test ${ac_cv_lib_pcap_pcap_init+y}
 then :
@@ -17450,8 +17214,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_init" >&5
-printf "%s\n" "$ac_cv_lib_pcap_pcap_init" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_init" >&5
+printf '%s\n' "$ac_cv_lib_pcap_pcap_init" >&6; }
 if test "x$ac_cv_lib_pcap_pcap_init" = xyes
 then :
   PCAP_LIBS="-lpcap"
@@ -17472,7 +17236,7 @@ fi
 
 if test "x$with_pcsclite" != "xno"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SCardEstablishContext in -lpcsclite" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for SCardEstablishContext in -lpcsclite" >&5
 printf %s "checking for SCardEstablishContext in -lpcsclite... " >&6; }
 if test ${ac_cv_lib_pcsclite_SCardEstablishContext+y}
 then :
@@ -17513,8 +17277,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcsclite_SCardEstablishContext" >&5
-printf "%s\n" "$ac_cv_lib_pcsclite_SCardEstablishContext" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcsclite_SCardEstablishContext" >&5
+printf '%s\n' "$ac_cv_lib_pcsclite_SCardEstablishContext" >&6; }
 if test "x$ac_cv_lib_pcsclite_SCardEstablishContext" = xyes
 then :
   PCSCLITE_LIBS="-lpcsclite"
@@ -17559,8 +17323,8 @@ fi
 fi
 
 INOTIFY_LIBS=${INOTIFY_LIBS:-"-linotify"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libinotify cflags: $INOTIFY_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libinotify libs: $INOTIFY_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libinotify cflags: $INOTIFY_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libinotify libs: $INOTIFY_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libinotify errors: " >&5
      cat conftest.err >&5
@@ -17573,11 +17337,11 @@ CPPFLAGS="$CPPFLAGS $INOTIFY_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default"
 if test "x$ac_cv_header_sys_inotify_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h
 
 fi
 
-         { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inotify_add_watch in -linotify" >&5
+         { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for inotify_add_watch in -linotify" >&5
 printf %s "checking for inotify_add_watch in -linotify... " >&6; }
 if test ${ac_cv_lib_inotify_inotify_add_watch+y}
 then :
@@ -17618,8 +17382,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inotify_inotify_add_watch" >&5
-printf "%s\n" "$ac_cv_lib_inotify_inotify_add_watch" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inotify_inotify_add_watch" >&5
+printf '%s\n' "$ac_cv_lib_inotify_inotify_add_watch" >&6; }
 if test "x$ac_cv_lib_inotify_inotify_add_watch" = xyes
 then :
   :
@@ -17663,8 +17427,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: dbus-1 cflags: $DBUS_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: dbus-1 libs: $DBUS_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: dbus-1 cflags: $DBUS_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: dbus-1 libs: $DBUS_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: dbus-1 errors: " >&5
      cat conftest.err >&5
@@ -17677,7 +17441,7 @@ CPPFLAGS="$CPPFLAGS $DBUS_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "dbus/dbus.h" "ac_cv_header_dbus_dbus_h" "$ac_includes_default"
 if test "x$ac_cv_header_dbus_dbus_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -ldbus-1" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -ldbus-1" >&5
 printf %s "checking for -ldbus-1... " >&6; }
 if test ${ac_cv_lib_soname_dbus_1+y}
 then :
@@ -17728,14 +17492,14 @@ esac
 fi
 if ${ac_cv_lib_soname_dbus_1:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        DBUS_CFLAGS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_dbus_1" >&5
-printf "%s\n" "$ac_cv_lib_soname_dbus_1" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_dbus_1" >&5
+printf '%s\n' "$ac_cv_lib_soname_dbus_1" >&6; }
 
-printf "%s\n" "#define SONAME_LIBDBUS_1 \"$ac_cv_lib_soname_dbus_1\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBDBUS_1 \"$ac_cv_lib_soname_dbus_1\"" >>confdefs.h
 
         ;;
 esac
@@ -17783,8 +17547,8 @@ fi
 fi
 
 GNUTLS_LIBS=${GNUTLS_LIBS:-"-lgnutls"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: gnutls cflags: $GNUTLS_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: gnutls libs: $GNUTLS_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: gnutls cflags: $GNUTLS_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: gnutls libs: $GNUTLS_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: gnutls errors: " >&5
      cat conftest.err >&5
@@ -17811,7 +17575,7 @@ static typeof(gnutls_mac_get_key_size) *func; if (func) return 0;
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lgnutls" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lgnutls" >&5
 printf %s "checking for -lgnutls... " >&6; }
 if test ${ac_cv_lib_soname_gnutls+y}
 then :
@@ -17862,14 +17626,14 @@ esac
 fi
 if ${ac_cv_lib_soname_gnutls:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        GNUTLS_CFLAGS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_gnutls" >&5
-printf "%s\n" "$ac_cv_lib_soname_gnutls" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_gnutls" >&5
+printf '%s\n' "$ac_cv_lib_soname_gnutls" >&6; }
 
-printf "%s\n" "#define SONAME_LIBGNUTLS \"$ac_cv_lib_soname_gnutls\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBGNUTLS \"$ac_cv_lib_soname_gnutls\"" >>confdefs.h
 
         ;;
 esac
@@ -17882,7 +17646,7 @@ do :
   ac_fn_c_check_func "$LINENO" "gnutls_cipher_init" "ac_cv_func_gnutls_cipher_init"
 if test "x$ac_cv_func_gnutls_cipher_init" = xyes
 then :
-  printf "%s\n" "#define HAVE_GNUTLS_CIPHER_INIT 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GNUTLS_CIPHER_INIT 1" >>confdefs.h
 
 else case e in #(
   e) as_fn_append wine_notices "|libgnutls ${notice_platform}development files too old, bcrypt encryption won't be supported." ;;
@@ -17935,8 +17699,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: hwloc cflags: $HWLOC_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: hwloc libs: $HWLOC_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: hwloc cflags: $HWLOC_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: hwloc libs: $HWLOC_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: hwloc errors: " >&5
      cat conftest.err >&5
@@ -17949,7 +17713,7 @@ CPPFLAGS="$CPPFLAGS $HWLOC_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "hwloc.h" "ac_cv_header_hwloc_h" "$ac_includes_default"
 if test "x$ac_cv_header_hwloc_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for hwloc_topology_init in -lhwloc" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for hwloc_topology_init in -lhwloc" >&5
 printf %s "checking for hwloc_topology_init in -lhwloc... " >&6; }
 if test ${ac_cv_lib_hwloc_hwloc_topology_init+y}
 then :
@@ -17990,12 +17754,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_hwloc_hwloc_topology_init" >&5
-printf "%s\n" "$ac_cv_lib_hwloc_hwloc_topology_init" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_hwloc_hwloc_topology_init" >&5
+printf '%s\n' "$ac_cv_lib_hwloc_hwloc_topology_init" >&6; }
 if test "x$ac_cv_lib_hwloc_hwloc_topology_init" = xyes
 then :
 
-printf "%s\n" "#define HAVE_LIBHWLOC 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LIBHWLOC 1" >>confdefs.h
 
 else case e in #(
   e) HWLOC_LIBS="" ;;
@@ -18044,8 +17808,8 @@ fi
 fi
 test "$cross_compiling" = yes || SANE_LIBS=${SANE_LIBS:-`${SANE_CONFIG:-sane-config} --ldflags 2>/dev/null`}
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: sane-backends cflags: $SANE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: sane-backends libs: $SANE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: sane-backends cflags: $SANE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: sane-backends libs: $SANE_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: sane-backends errors: " >&5
      cat conftest.err >&5
@@ -18058,7 +17822,7 @@ CPPFLAGS="$CPPFLAGS $SANE_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "sane/sane.h" "ac_cv_header_sane_sane_h" "$ac_includes_default"
 if test "x$ac_cv_header_sane_sane_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sane_init in -lsane" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for sane_init in -lsane" >&5
 printf %s "checking for sane_init in -lsane... " >&6; }
 if test ${ac_cv_lib_sane_sane_init+y}
 then :
@@ -18099,8 +17863,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sane_sane_init" >&5
-printf "%s\n" "$ac_cv_lib_sane_sane_init" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sane_sane_init" >&5
+printf '%s\n' "$ac_cv_lib_sane_sane_init" >&6; }
 if test "x$ac_cv_lib_sane_sane_init" = xyes
 then :
   :
@@ -18143,8 +17907,8 @@ fi
 fi
 
 USB_LIBS=${USB_LIBS:-"-lusb-1.0"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libusb-1.0 cflags: $USB_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libusb-1.0 libs: $USB_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libusb-1.0 cflags: $USB_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libusb-1.0 libs: $USB_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libusb-1.0 errors: " >&5
      cat conftest.err >&5
@@ -18157,7 +17921,7 @@ CPPFLAGS="$CPPFLAGS $USB_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "libusb.h" "ac_cv_header_libusb_h" "$ac_includes_default"
 if test "x$ac_cv_header_libusb_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libusb_interrupt_event_handler in -lusb-1.0" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for libusb_interrupt_event_handler in -lusb-1.0" >&5
 printf %s "checking for libusb_interrupt_event_handler in -lusb-1.0... " >&6; }
 if test ${ac_cv_lib_usb_1_0_libusb_interrupt_event_handler+y}
 then :
@@ -18198,8 +17962,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_usb_1_0_libusb_interrupt_event_handler" >&5
-printf "%s\n" "$ac_cv_lib_usb_1_0_libusb_interrupt_event_handler" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_usb_1_0_libusb_interrupt_event_handler" >&5
+printf '%s\n' "$ac_cv_lib_usb_1_0_libusb_interrupt_event_handler" >&6; }
 if test "x$ac_cv_lib_usb_1_0_libusb_interrupt_event_handler" = xyes
 then :
   :
@@ -18230,7 +17994,7 @@ fi
 
 if test "x$with_v4l2" != "xno"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lv4l2" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lv4l2" >&5
 printf %s "checking for -lv4l2... " >&6; }
 if test ${ac_cv_lib_soname_v4l2+y}
 then :
@@ -18281,14 +18045,14 @@ esac
 fi
 if ${ac_cv_lib_soname_v4l2:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_v4l2" >&5
-printf "%s\n" "$ac_cv_lib_soname_v4l2" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_v4l2" >&5
+printf '%s\n' "$ac_cv_lib_soname_v4l2" >&6; }
 
-printf "%s\n" "#define SONAME_LIBV4L2 \"$ac_cv_lib_soname_v4l2\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBV4L2 \"$ac_cv_lib_soname_v4l2\"" >>confdefs.h
 
         ;;
 esac
@@ -18325,8 +18089,8 @@ fi
 fi
 test "$cross_compiling" = yes || GPHOTO2_LIBS=${GPHOTO2_LIBS:-`${GPHOTO2_CONFIG:-gphoto2-config} --libs 2>/dev/null`}
 GPHOTO2_LIBS=${GPHOTO2_LIBS:-"-lgphoto2"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libgphoto2 cflags: $GPHOTO2_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libgphoto2 libs: $GPHOTO2_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libgphoto2 cflags: $GPHOTO2_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libgphoto2 libs: $GPHOTO2_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libgphoto2 errors: " >&5
      cat conftest.err >&5
@@ -18339,7 +18103,7 @@ CPPFLAGS="$CPPFLAGS $GPHOTO2_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "gphoto2-camera.h" "ac_cv_header_gphoto2_camera_h" "$ac_includes_default"
 if test "x$ac_cv_header_gphoto2_camera_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gp_camera_new in -lgphoto2" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for gp_camera_new in -lgphoto2" >&5
 printf %s "checking for gp_camera_new in -lgphoto2... " >&6; }
 if test ${ac_cv_lib_gphoto2_gp_camera_new+y}
 then :
@@ -18380,8 +18144,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gphoto2_gp_camera_new" >&5
-printf "%s\n" "$ac_cv_lib_gphoto2_gp_camera_new" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gphoto2_gp_camera_new" >&5
+printf '%s\n' "$ac_cv_lib_gphoto2_gp_camera_new" >&6; }
 if test "x$ac_cv_lib_gphoto2_gp_camera_new" = xyes
 then :
   :
@@ -18410,8 +18174,8 @@ fi
 fi
 test "$cross_compiling" = yes || GPHOTO2_PORT_LIBS=${GPHOTO2_PORT_LIBS:-`${GPHOTO2_PORT_CONFIG:-gphoto2-port-config} --libs 2>/dev/null`}
 GPHOTO2_PORT_LIBS=${GPHOTO2_PORT_LIBS:-"-lgphoto2_port"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libgphoto2_port cflags: $GPHOTO2_PORT_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libgphoto2_port libs: $GPHOTO2_PORT_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libgphoto2_port cflags: $GPHOTO2_PORT_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libgphoto2_port libs: $GPHOTO2_PORT_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libgphoto2_port errors: " >&5
      cat conftest.err >&5
@@ -18424,7 +18188,7 @@ CPPFLAGS="$CPPFLAGS $GPHOTO2_PORT_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "gphoto2-port.h" "ac_cv_header_gphoto2_port_h" "$ac_includes_default"
 if test "x$ac_cv_header_gphoto2_port_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gp_port_info_list_new in -lgphoto2_port" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for gp_port_info_list_new in -lgphoto2_port" >&5
 printf %s "checking for gp_port_info_list_new in -lgphoto2_port... " >&6; }
 if test ${ac_cv_lib_gphoto2_port_gp_port_info_list_new+y}
 then :
@@ -18465,12 +18229,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gphoto2_port_gp_port_info_list_new" >&5
-printf "%s\n" "$ac_cv_lib_gphoto2_port_gp_port_info_list_new" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gphoto2_port_gp_port_info_list_new" >&5
+printf '%s\n' "$ac_cv_lib_gphoto2_port_gp_port_info_list_new" >&6; }
 if test "x$ac_cv_lib_gphoto2_port_gp_port_info_list_new" = xyes
 then :
 
-printf "%s\n" "#define HAVE_GPHOTO2_PORT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_GPHOTO2_PORT 1" >>confdefs.h
 
 else case e in #(
   e) GPHOTO2_PORT_LIBS=""; GPHOTO2_PORT_CFLAGS="" ;;
@@ -18510,7 +18274,7 @@ fi
 
 if test "$ac_cv_header_resolv_h" = "yes"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for resolver library" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for resolver library" >&5
 printf %s "checking for resolver library... " >&6; }
 if test ${ac_cv_have_resolv+y}
 then :
@@ -18548,17 +18312,17 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
        LIBS="$ac_save_LIBS" ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_resolv" >&5
-printf "%s\n" "$ac_cv_have_resolv" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_resolv" >&5
+printf '%s\n' "$ac_cv_have_resolv" >&6; }
 
     case "$ac_cv_have_resolv" in
       "not found") ;;
       "none required")
 
-printf "%s\n" "#define HAVE_RESOLV 1" >>confdefs.h
+printf '%s\n' "#define HAVE_RESOLV 1" >>confdefs.h
  ;;
       *)
-        printf "%s\n" "#define HAVE_RESOLV 1" >>confdefs.h
+        printf '%s\n' "#define HAVE_RESOLV 1" >>confdefs.h
 
         RESOLV_LIBS=$ac_cv_have_resolv
  ;;
@@ -18566,7 +18330,7 @@ printf "%s\n" "#define HAVE_RESOLV 1" >>confdefs.h
 
     if test "x$ac_cv_have_resolv" != "xnot found"
     then
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for res_getservers" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for res_getservers" >&5
 printf %s "checking for res_getservers... " >&6; }
 if test ${ac_cv_have_res_getservers+y}
 then :
@@ -18597,12 +18361,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 	                 LIBS="$ac_save_LIBS" ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_res_getservers" >&5
-printf "%s\n" "$ac_cv_have_res_getservers" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_res_getservers" >&5
+printf '%s\n' "$ac_cv_have_res_getservers" >&6; }
         if test "$ac_cv_have_res_getservers" = "yes"
         then
 
-printf "%s\n" "#define HAVE_RES_GETSERVERS 1" >>confdefs.h
+printf '%s\n' "#define HAVE_RES_GETSERVERS 1" >>confdefs.h
 
         fi
     fi
@@ -18628,8 +18392,8 @@ fi
 fi
 test "$cross_compiling" = yes || FREETYPE_LIBS=${FREETYPE_LIBS:-`(${FREETYPE_CONFIG:-freetype-config} --libs || ${FREETYPE2_CONFIG:-freetype2-config} --libs) 2>/dev/null`}
 FREETYPE_LIBS=${FREETYPE_LIBS:-"-lfreetype"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: freetype2 cflags: $FREETYPE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: freetype2 libs: $FREETYPE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: freetype2 cflags: $FREETYPE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: freetype2 libs: $FREETYPE_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: freetype2 errors: " >&5
      cat conftest.err >&5
@@ -18642,13 +18406,13 @@ CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "ft2build.h" "ac_cv_header_ft2build_h" "$ac_includes_default"
 if test "x$ac_cv_header_ft2build_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_FT2BUILD_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_FT2BUILD_H 1" >>confdefs.h
 
 fi
 
         if test "$ac_cv_header_ft2build_h" = "yes"
         then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lfreetype" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lfreetype" >&5
 printf %s "checking for -lfreetype... " >&6; }
 if test ${ac_cv_lib_soname_freetype+y}
 then :
@@ -18699,14 +18463,14 @@ esac
 fi
 if ${ac_cv_lib_soname_freetype:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        FREETYPE_LIBS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_freetype" >&5
-printf "%s\n" "$ac_cv_lib_soname_freetype" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_freetype" >&5
+printf '%s\n' "$ac_cv_lib_soname_freetype" >&6; }
 
-printf "%s\n" "#define SONAME_LIBFREETYPE \"$ac_cv_lib_soname_freetype\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBFREETYPE \"$ac_cv_lib_soname_freetype\"" >>confdefs.h
 
         ;;
 esac
@@ -18734,7 +18498,7 @@ LIBS="$LIBS $PTHREAD_LIBS"
 ac_fn_c_check_func "$LINENO" "pthread_getthreadid_np" "ac_cv_func_pthread_getthreadid_np"
 if test "x$ac_cv_func_pthread_getthreadid_np" = xyes
 then :
-  printf "%s\n" "#define HAVE_PTHREAD_GETTHREADID_NP 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PTHREAD_GETTHREADID_NP 1" >>confdefs.h
 
 fi
 
@@ -18744,7 +18508,7 @@ if test "x$enable_tools" != xno -a "x$with_gettextpo" = xyes
 then
     if test "$ac_cv_header_gettext_po_h" = "yes"
     then
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for po_message_msgctxt in -lgettextpo" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for po_message_msgctxt in -lgettextpo" >&5
 printf %s "checking for po_message_msgctxt in -lgettextpo... " >&6; }
 if test ${ac_cv_lib_gettextpo_po_message_msgctxt+y}
 then :
@@ -18785,12 +18549,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gettextpo_po_message_msgctxt" >&5
-printf "%s\n" "$ac_cv_lib_gettextpo_po_message_msgctxt" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gettextpo_po_message_msgctxt" >&5
+printf '%s\n' "$ac_cv_lib_gettextpo_po_message_msgctxt" >&6; }
 if test "x$ac_cv_lib_gettextpo_po_message_msgctxt" = xyes
 then :
 
-printf "%s\n" "#define HAVE_LIBGETTEXTPO 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LIBGETTEXTPO 1" >>confdefs.h
 
                       GETTEXTPO_LIBS="-lgettextpo"
 
@@ -18839,8 +18603,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libpulse cflags: $PULSE_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libpulse libs: $PULSE_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libpulse cflags: $PULSE_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libpulse libs: $PULSE_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libpulse errors: " >&5
      cat conftest.err >&5
@@ -18853,7 +18617,7 @@ CPPFLAGS="$CPPFLAGS $PULSE_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "pulse/pulseaudio.h" "ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default"
 if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5
 printf %s "checking for pa_stream_is_corked in -lpulse... " >&6; }
 if test ${ac_cv_lib_pulse_pa_stream_is_corked+y}
 then :
@@ -18894,8 +18658,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5
-printf "%s\n" "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5
+printf '%s\n' "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; }
 if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes
 then :
   :
@@ -18944,8 +18708,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libavutil libavformat libavcodec cflags: $FFMPEG_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libavutil libavformat libavcodec libs: $FFMPEG_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libavutil libavformat libavcodec cflags: $FFMPEG_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libavutil libavformat libavcodec libs: $FFMPEG_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libavutil libavformat libavcodec errors: " >&5
      cat conftest.err >&5
@@ -18958,31 +18722,31 @@ CPPFLAGS="$CPPFLAGS $FFMPEG_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "libavutil/avutil.h" "ac_cv_header_libavutil_avutil_h" "$ac_includes_default"
 if test "x$ac_cv_header_libavutil_avutil_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LIBAVUTIL_AVUTIL_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LIBAVUTIL_AVUTIL_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "libavformat/avformat.h" "ac_cv_header_libavformat_avformat_h" "$ac_includes_default"
 if test "x$ac_cv_header_libavformat_avformat_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LIBAVFORMAT_AVFORMAT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LIBAVFORMAT_AVFORMAT_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "libavcodec/avcodec.h" "ac_cv_header_libavcodec_avcodec_h" "$ac_includes_default"
 if test "x$ac_cv_header_libavcodec_avcodec_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LIBAVCODEC_AVCODEC_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LIBAVCODEC_AVCODEC_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "libavcodec/bsf.h" "ac_cv_header_libavcodec_bsf_h" "$ac_includes_default"
 if test "x$ac_cv_header_libavcodec_bsf_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LIBAVCODEC_BSF_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LIBAVCODEC_BSF_H 1" >>confdefs.h
 
 fi
 
          if test "$ac_cv_header_libavutil_avutil_h" = "yes" -a "$ac_cv_header_libavformat_avformat_h" = "yes" -a "$ac_cv_header_libavcodec_avcodec_h" = "yes"
          then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for av_log_set_callback in -lavutil" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for av_log_set_callback in -lavutil" >&5
 printf %s "checking for av_log_set_callback in -lavutil... " >&6; }
 if test ${ac_cv_lib_avutil_av_log_set_callback+y}
 then :
@@ -19023,11 +18787,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avutil_av_log_set_callback" >&5
-printf "%s\n" "$ac_cv_lib_avutil_av_log_set_callback" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avutil_av_log_set_callback" >&5
+printf '%s\n' "$ac_cv_lib_avutil_av_log_set_callback" >&6; }
 if test "x$ac_cv_lib_avutil_av_log_set_callback" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for av_find_input_format in -lavformat" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for av_find_input_format in -lavformat" >&5
 printf %s "checking for av_find_input_format in -lavformat... " >&6; }
 if test ${ac_cv_lib_avformat_av_find_input_format+y}
 then :
@@ -19068,11 +18832,11 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avformat_av_find_input_format" >&5
-printf "%s\n" "$ac_cv_lib_avformat_av_find_input_format" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avformat_av_find_input_format" >&5
+printf '%s\n' "$ac_cv_lib_avformat_av_find_input_format" >&6; }
 if test "x$ac_cv_lib_avformat_av_find_input_format" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for avcodec_get_name in -lavcodec" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for avcodec_get_name in -lavcodec" >&5
 printf %s "checking for avcodec_get_name in -lavcodec... " >&6; }
 if test ${ac_cv_lib_avcodec_avcodec_get_name+y}
 then :
@@ -19113,12 +18877,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avcodec_avcodec_get_name" >&5
-printf "%s\n" "$ac_cv_lib_avcodec_avcodec_get_name" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avcodec_avcodec_get_name" >&5
+printf '%s\n' "$ac_cv_lib_avcodec_avcodec_get_name" >&6; }
 if test "x$ac_cv_lib_avcodec_avcodec_get_name" = xyes
 then :
 
-printf "%s\n" "#define HAVE_FFMPEG 1" >>confdefs.h
+printf '%s\n' "#define HAVE_FFMPEG 1" >>confdefs.h
 
 else case e in #(
   e) FFMPEG_LIBS="" ;;
@@ -19173,8 +18937,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 gstreamer-tag-1.0 cflags: $GSTREAMER_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 gstreamer-tag-1.0 libs: $GSTREAMER_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 gstreamer-tag-1.0 cflags: $GSTREAMER_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 gstreamer-tag-1.0 libs: $GSTREAMER_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 gstreamer-tag-1.0 errors: " >&5
      cat conftest.err >&5
@@ -19187,7 +18951,7 @@ CPPFLAGS="$CPPFLAGS $GSTREAMER_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "gst/gst.h" "ac_cv_header_gst_gst_h" "$ac_includes_default"
 if test "x$ac_cv_header_gst_gst_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether gint64 defined by gst/gst.h is indeed 64-bit" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether gint64 defined by gst/gst.h is indeed 64-bit" >&5
 printf %s "checking whether gint64 defined by gst/gst.h is indeed 64-bit... " >&6; }
               cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -19202,9 +18966,9 @@ static int a[sizeof(gint64) > 4 ? 1 : -1]; if (a[0]) return 0;
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
-                 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gst_pad_new in -lgstreamer-1.0" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf '%s\n' "yes" >&6; }
+                 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for gst_pad_new in -lgstreamer-1.0" >&5
 printf %s "checking for gst_pad_new in -lgstreamer-1.0... " >&6; }
 if test ${ac_cv_lib_gstreamer_1_0_gst_pad_new+y}
 then :
@@ -19245,16 +19009,16 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gstreamer_1_0_gst_pad_new" >&5
-printf "%s\n" "$ac_cv_lib_gstreamer_1_0_gst_pad_new" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gstreamer_1_0_gst_pad_new" >&5
+printf '%s\n' "$ac_cv_lib_gstreamer_1_0_gst_pad_new" >&6; }
 if test "x$ac_cv_lib_gstreamer_1_0_gst_pad_new" = xyes
 then :
   :
 fi
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf '%s\n' "no" >&6; }
                  ac_glib2_broken=yes
                  enable_winegstreamer=${enable_winegstreamer:-no}
                  as_fn_append wine_notices "|glib-2.0 pkgconfig configuration is for the wrong architecture, winegstreamer won't be built." ;;
@@ -19300,8 +19064,8 @@ fi
 fi
 
 ALSA_LIBS=${ALSA_LIBS:-"-lasound"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: alsa cflags: $ALSA_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: alsa libs: $ALSA_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: alsa cflags: $ALSA_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: alsa libs: $ALSA_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: alsa errors: " >&5
      cat conftest.err >&5
@@ -19311,7 +19075,7 @@ if test -n "$ALSA_CFLAGS$ALSA_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $ALSA_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_hw_params_get_access_mask in -lasound" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_hw_params_get_access_mask in -lasound" >&5
 printf %s "checking for snd_pcm_hw_params_get_access_mask in -lasound... " >&6; }
 if test ${ac_cv_lib_asound_snd_pcm_hw_params_get_access_mask+y}
 then :
@@ -19352,8 +19116,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_hw_params_get_access_mask" >&5
-printf "%s\n" "$ac_cv_lib_asound_snd_pcm_hw_params_get_access_mask" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_hw_params_get_access_mask" >&5
+printf '%s\n' "$ac_cv_lib_asound_snd_pcm_hw_params_get_access_mask" >&6; }
 if test "x$ac_cv_lib_asound_snd_pcm_hw_params_get_access_mask" = xyes
 then :
   :
@@ -19394,11 +19158,11 @@ then :
 if test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes
 then :
 
-printf "%s\n" "#define HAVE_OSS_SYSINFO_NUMAUDIOENGINES 1" >>confdefs.h
+printf '%s\n' "#define HAVE_OSS_SYSINFO_NUMAUDIOENGINES 1" >>confdefs.h
 
 OSS4_CFLAGS="$ac_oss_incl"
 
-             { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _oss_ioctl in -lossaudio" >&5
+             { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for _oss_ioctl in -lossaudio" >&5
 printf %s "checking for _oss_ioctl in -lossaudio... " >&6; }
 if test ${ac_cv_lib_ossaudio__oss_ioctl+y}
 then :
@@ -19439,8 +19203,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ossaudio__oss_ioctl" >&5
-printf "%s\n" "$ac_cv_lib_ossaudio__oss_ioctl" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ossaudio__oss_ioctl" >&5
+printf '%s\n' "$ac_cv_lib_ossaudio__oss_ioctl" >&6; }
 if test "x$ac_cv_lib_ossaudio__oss_ioctl" = xyes
 then :
   OSS4_LIBS="-lossaudio"
@@ -19484,8 +19248,8 @@ fi
 fi
 
 UDEV_LIBS=${UDEV_LIBS:-"-ludev"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libudev cflags: $UDEV_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: libudev libs: $UDEV_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libudev cflags: $UDEV_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: libudev libs: $UDEV_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: libudev errors: " >&5
      cat conftest.err >&5
@@ -19500,8 +19264,8 @@ do :
   ac_fn_c_check_header_compile "$LINENO" "libudev.h" "ac_cv_header_libudev_h" "$ac_includes_default"
 if test "x$ac_cv_header_libudev_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LIBUDEV_H 1" >>confdefs.h
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for udev_new in -ludev" >&5
+  printf '%s\n' "#define HAVE_LIBUDEV_H 1" >>confdefs.h
+ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for udev_new in -ludev" >&5
 printf %s "checking for udev_new in -ludev... " >&6; }
 if test ${ac_cv_lib_udev_udev_new+y}
 then :
@@ -19542,12 +19306,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_udev_udev_new" >&5
-printf "%s\n" "$ac_cv_lib_udev_udev_new" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_udev_udev_new" >&5
+printf '%s\n' "$ac_cv_lib_udev_udev_new" >&6; }
 if test "x$ac_cv_lib_udev_udev_new" = xyes
 then :
 
-printf "%s\n" "#define HAVE_UDEV 1" >>confdefs.h
+printf '%s\n' "#define HAVE_UDEV 1" >>confdefs.h
 
 else case e in #(
   e) UDEV_LIBS="" ;;
@@ -19595,8 +19359,8 @@ fi
 fi
 
 SDL2_LIBS=${SDL2_LIBS:-"-lSDL2"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: sdl2 cflags: $SDL2_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: sdl2 libs: $SDL2_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: sdl2 cflags: $SDL2_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: sdl2 libs: $SDL2_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: sdl2 errors: " >&5
      cat conftest.err >&5
@@ -19611,8 +19375,8 @@ do :
   ac_fn_c_check_header_compile "$LINENO" "SDL.h" "ac_cv_header_SDL_h" "$ac_includes_default"
 if test "x$ac_cv_header_SDL_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_SDL_H 1" >>confdefs.h
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lSDL2" >&5
+  printf '%s\n' "#define HAVE_SDL_H 1" >>confdefs.h
+ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lSDL2" >&5
 printf %s "checking for -lSDL2... " >&6; }
 if test ${ac_cv_lib_soname_SDL2+y}
 then :
@@ -19663,14 +19427,14 @@ esac
 fi
 if ${ac_cv_lib_soname_SDL2:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_SDL2" >&5
-printf "%s\n" "$ac_cv_lib_soname_SDL2" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_SDL2" >&5
+printf '%s\n' "$ac_cv_lib_soname_SDL2" >&6; }
 
-printf "%s\n" "#define SONAME_LIBSDL2 \"$ac_cv_lib_soname_SDL2\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBSDL2 \"$ac_cv_lib_soname_SDL2\"" >>confdefs.h
 
         ;;
 esac
@@ -19713,8 +19477,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: capi20 cflags: $CAPI20_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: capi20 libs: $CAPI20_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: capi20 cflags: $CAPI20_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: capi20 libs: $CAPI20_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: capi20 errors: " >&5
      cat conftest.err >&5
@@ -19728,20 +19492,20 @@ CPPFLAGS="$CPPFLAGS $CAPI20_CFLAGS"
 "
 if test "x$ac_cv_header_capi20_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_CAPI20_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_CAPI20_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/capi.h" "ac_cv_header_linux_capi_h" "#define __user
 "
 if test "x$ac_cv_header_linux_capi_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_LINUX_CAPI_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_LINUX_CAPI_H 1" >>confdefs.h
 
 fi
 
     if test "$ac_cv_header_capi20_h" = "yes" -a "$ac_cv_header_linux_capi_h" = "yes"
     then
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for capi20_register in -lcapi20" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for capi20_register in -lcapi20" >&5
 printf %s "checking for capi20_register in -lcapi20... " >&6; }
 if test ${ac_cv_lib_capi20_capi20_register+y}
 then :
@@ -19782,8 +19546,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_capi20_capi20_register" >&5
-printf "%s\n" "$ac_cv_lib_capi20_capi20_register" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_capi20_capi20_register" >&5
+printf '%s\n' "$ac_cv_lib_capi20_capi20_register" >&6; }
 if test "x$ac_cv_lib_capi20_capi20_register" = xyes
 then :
   :
@@ -19828,8 +19592,8 @@ fi
 fi
 test "$cross_compiling" = yes || CUPS_LIBS=${CUPS_LIBS:-`${CUPS_CONFIG:-cups-config} --libs 2>/dev/null`}
 CUPS_LIBS=${CUPS_LIBS:-"-lcups"}
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: cups cflags: $CUPS_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: cups libs: $CUPS_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: cups cflags: $CUPS_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: cups libs: $CUPS_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: cups errors: " >&5
      cat conftest.err >&5
@@ -19842,19 +19606,19 @@ CPPFLAGS="$CPPFLAGS $CUPS_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "cups/cups.h" "ac_cv_header_cups_cups_h" "$ac_includes_default"
 if test "x$ac_cv_header_cups_cups_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_CUPS_CUPS_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_CUPS_CUPS_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "cups/ppd.h" "ac_cv_header_cups_ppd_h" "$ac_includes_default"
 if test "x$ac_cv_header_cups_ppd_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_CUPS_PPD_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_CUPS_PPD_H 1" >>confdefs.h
 
 fi
 
                         if test "$ac_cv_header_cups_cups_h" = "yes"
                         then
-                            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lcups" >&5
+                            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lcups" >&5
 printf %s "checking for -lcups... " >&6; }
 if test ${ac_cv_lib_soname_cups+y}
 then :
@@ -19905,14 +19669,14 @@ esac
 fi
 if ${ac_cv_lib_soname_cups:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        CUPS_LIBS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_cups" >&5
-printf "%s\n" "$ac_cv_lib_soname_cups" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_cups" >&5
+printf '%s\n' "$ac_cv_lib_soname_cups" >&6; }
 
-printf "%s\n" "#define SONAME_LIBCUPS \"$ac_cv_lib_soname_cups\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBCUPS \"$ac_cv_lib_soname_cups\"" >>confdefs.h
 
         ;;
 esac
@@ -19956,8 +19720,8 @@ fi
 fi
 test "$cross_compiling" = yes || FONTCONFIG_LIBS=${FONTCONFIG_LIBS:-$X_LIBS}
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: fontconfig cflags: $FONTCONFIG_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: fontconfig libs: $FONTCONFIG_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: fontconfig cflags: $FONTCONFIG_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: fontconfig libs: $FONTCONFIG_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: fontconfig errors: " >&5
      cat conftest.err >&5
@@ -19970,7 +19734,7 @@ CPPFLAGS="$CPPFLAGS $FONTCONFIG_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "fontconfig/fontconfig.h" "ac_cv_header_fontconfig_fontconfig_h" "$ac_includes_default"
 if test "x$ac_cv_header_fontconfig_fontconfig_h" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lfontconfig" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lfontconfig" >&5
 printf %s "checking for -lfontconfig... " >&6; }
 if test ${ac_cv_lib_soname_fontconfig+y}
 then :
@@ -20021,14 +19785,14 @@ esac
 fi
 if ${ac_cv_lib_soname_fontconfig:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        FONTCONFIG_CFLAGS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_fontconfig" >&5
-printf "%s\n" "$ac_cv_lib_soname_fontconfig" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_fontconfig" >&5
+printf '%s\n' "$ac_cv_lib_soname_fontconfig" >&6; }
 
-printf "%s\n" "#define SONAME_LIBFONTCONFIG \"$ac_cv_lib_soname_fontconfig\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBFONTCONFIG \"$ac_cv_lib_soname_fontconfig\"" >>confdefs.h
 
         ;;
 esac
@@ -20073,8 +19837,8 @@ fi
 fi
 test "$cross_compiling" = yes || KRB5_LIBS=${KRB5_LIBS:-`${KRB5_CONFIG:-krb5-config} --libs 2>/dev/null`}
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: krb5 cflags: $KRB5_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: krb5 libs: $KRB5_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: krb5 cflags: $KRB5_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: krb5 libs: $KRB5_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: krb5 errors: " >&5
      cat conftest.err >&5
@@ -20087,13 +19851,13 @@ CPPFLAGS="$CPPFLAGS $KRB5_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "krb5/krb5.h" "ac_cv_header_krb5_krb5_h" "$ac_includes_default"
 if test "x$ac_cv_header_krb5_krb5_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_KRB5_KRB5_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_KRB5_KRB5_H 1" >>confdefs.h
 
 fi
 
          if test "$ac_cv_header_krb5_krb5_h" = "yes"
          then
-             { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lkrb5" >&5
+             { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lkrb5" >&5
 printf %s "checking for -lkrb5... " >&6; }
 if test ${ac_cv_lib_soname_krb5+y}
 then :
@@ -20144,14 +19908,14 @@ esac
 fi
 if ${ac_cv_lib_soname_krb5:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        KRB5_CFLAGS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_krb5" >&5
-printf "%s\n" "$ac_cv_lib_soname_krb5" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_krb5" >&5
+printf '%s\n' "$ac_cv_lib_soname_krb5" >&6; }
 
-printf "%s\n" "#define SONAME_LIBKRB5 \"$ac_cv_lib_soname_krb5\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBKRB5 \"$ac_cv_lib_soname_krb5\"" >>confdefs.h
 
         ;;
 esac
@@ -20195,8 +19959,8 @@ fi
 fi
 test "$cross_compiling" = yes || GSSAPI_LIBS=${GSSAPI_LIBS:-`${KRB5_CONFIG:-krb5-config} --libs gssapi 2>/dev/null`}
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: krb5-gssapi cflags: $GSSAPI_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: krb5-gssapi libs: $GSSAPI_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: krb5-gssapi cflags: $GSSAPI_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: krb5-gssapi libs: $GSSAPI_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: krb5-gssapi errors: " >&5
      cat conftest.err >&5
@@ -20209,19 +19973,19 @@ CPPFLAGS="$CPPFLAGS $GSSAPI_CFLAGS"
 ac_fn_c_check_header_compile "$LINENO" "gssapi/gssapi.h" "ac_cv_header_gssapi_gssapi_h" "$ac_includes_default"
 if test "x$ac_cv_header_gssapi_gssapi_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_GSSAPI_GSSAPI_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GSSAPI_GSSAPI_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "gssapi/gssapi_ext.h" "ac_cv_header_gssapi_gssapi_ext_h" "$ac_includes_default"
 if test "x$ac_cv_header_gssapi_gssapi_ext_h" = xyes
 then :
-  printf "%s\n" "#define HAVE_GSSAPI_GSSAPI_EXT_H 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GSSAPI_GSSAPI_EXT_H 1" >>confdefs.h
 
 fi
 
         if test "$ac_cv_header_gssapi_gssapi_h" = "yes" -a "$ac_cv_header_gssapi_gssapi_ext_h" = "yes"
         then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lgssapi_krb5" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lgssapi_krb5" >&5
 printf %s "checking for -lgssapi_krb5... " >&6; }
 if test ${ac_cv_lib_soname_gssapi_krb5+y}
 then :
@@ -20272,14 +20036,14 @@ esac
 fi
 if ${ac_cv_lib_soname_gssapi_krb5:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
        GSSAPI_CFLAGS=""
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_gssapi_krb5" >&5
-printf "%s\n" "$ac_cv_lib_soname_gssapi_krb5" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_gssapi_krb5" >&5
+printf '%s\n' "$ac_cv_lib_soname_gssapi_krb5" >&6; }
 
-printf "%s\n" "#define SONAME_LIBGSSAPI_KRB5 \"$ac_cv_lib_soname_gssapi_krb5\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBGSSAPI_KRB5 \"$ac_cv_lib_soname_gssapi_krb5\"" >>confdefs.h
 
         ;;
 esac
@@ -20304,7 +20068,7 @@ fi
 
 if test "$ac_cv_header_libprocstat_h" = "yes"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for procstat_open_sysctl in -lprocstat" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for procstat_open_sysctl in -lprocstat" >&5
 printf %s "checking for procstat_open_sysctl in -lprocstat... " >&6; }
 if test ${ac_cv_lib_procstat_procstat_open_sysctl+y}
 then :
@@ -20345,12 +20109,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 LIBS=$ac_check_lib_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_procstat_procstat_open_sysctl" >&5
-printf "%s\n" "$ac_cv_lib_procstat_procstat_open_sysctl" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_procstat_procstat_open_sysctl" >&5
+printf '%s\n' "$ac_cv_lib_procstat_procstat_open_sysctl" >&6; }
 if test "x$ac_cv_lib_procstat_procstat_open_sysctl" = xyes
 then :
 
-printf "%s\n" "#define HAVE_LIBPROCSTAT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LIBPROCSTAT 1" >>confdefs.h
 
                   PROCSTAT_LIBS="-lprocstat"
 
@@ -20358,7 +20122,7 @@ fi
 
 fi
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lodbc" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lodbc" >&5
 printf %s "checking for -lodbc... " >&6; }
 if test ${ac_cv_lib_soname_odbc+y}
 then :
@@ -20409,15 +20173,15 @@ esac
 fi
 if ${ac_cv_lib_soname_odbc:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
-       printf "%s\n" "#define SONAME_LIBODBC \"libodbc.$LIBEXT\"" >>confdefs.h
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
+       printf '%s\n' "#define SONAME_LIBODBC \"libodbc.$LIBEXT\"" >>confdefs.h
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_odbc" >&5
-printf "%s\n" "$ac_cv_lib_soname_odbc" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_odbc" >&5
+printf '%s\n' "$ac_cv_lib_soname_odbc" >&6; }
 
-printf "%s\n" "#define SONAME_LIBODBC \"$ac_cv_lib_soname_odbc\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBODBC \"$ac_cv_lib_soname_odbc\"" >>confdefs.h
 
         ;;
 esac
@@ -20443,8 +20207,8 @@ fi
 fi
 
 
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: netapi cflags: $NETAPI_CFLAGS" >&5
-printf "%s\n" "$as_me:${as_lineno-$LINENO}: netapi libs: $NETAPI_LIBS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: netapi cflags: $NETAPI_CFLAGS" >&5
+printf '%s\n' "$as_me:${as_lineno-$LINENO}: netapi libs: $NETAPI_LIBS" >&5
 if test -s conftest.err; then
      printf %s "$as_me:${as_lineno-$LINENO}: netapi errors: " >&5
      cat conftest.err >&5
@@ -20454,7 +20218,7 @@ if test -n "$NETAPI_CFLAGS$NETAPI_LIBS"
 then :
   ac_save_CPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS $NETAPI_CFLAGS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lnetapi" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lnetapi" >&5
 printf %s "checking for -lnetapi... " >&6; }
 if test ${ac_cv_lib_soname_netapi+y}
 then :
@@ -20505,15 +20269,15 @@ esac
 fi
 if ${ac_cv_lib_soname_netapi:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
-       printf "%s\n" "#define SONAME_LIBNETAPI \"libnetapi.$LIBEXT\"" >>confdefs.h
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
+       printf '%s\n' "#define SONAME_LIBNETAPI \"libnetapi.$LIBEXT\"" >>confdefs.h
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_netapi" >&5
-printf "%s\n" "$ac_cv_lib_soname_netapi" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_netapi" >&5
+printf '%s\n' "$ac_cv_lib_soname_netapi" >&6; }
 
-printf "%s\n" "#define SONAME_LIBNETAPI \"$ac_cv_lib_soname_netapi\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBNETAPI \"$ac_cv_lib_soname_netapi\"" >>confdefs.h
 
         ;;
 esac
@@ -20542,7 +20306,7 @@ fi
 
 if test "x$with_vulkan" != "xno"
 then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lvulkan" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lvulkan" >&5
 printf %s "checking for -lvulkan... " >&6; }
 if test ${ac_cv_lib_soname_vulkan+y}
 then :
@@ -20593,21 +20357,21 @@ esac
 fi
 if ${ac_cv_lib_soname_vulkan:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_vulkan" >&5
-printf "%s\n" "$ac_cv_lib_soname_vulkan" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_vulkan" >&5
+printf '%s\n' "$ac_cv_lib_soname_vulkan" >&6; }
 
-printf "%s\n" "#define SONAME_LIBVULKAN \"$ac_cv_lib_soname_vulkan\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBVULKAN \"$ac_cv_lib_soname_vulkan\"" >>confdefs.h
 
         ;;
 esac
 fi
     if test "x$ac_cv_lib_soname_vulkan" = "x"
     then
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lMoltenVK" >&5
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for -lMoltenVK" >&5
 printf %s "checking for -lMoltenVK... " >&6; }
 if test ${ac_cv_lib_soname_MoltenVK+y}
 then :
@@ -20658,16 +20422,16 @@ esac
 fi
 if ${ac_cv_lib_soname_MoltenVK:+false} :
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5
-printf "%s\n" "not found" >&6; }
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+printf '%s\n' "not found" >&6; }
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_MoltenVK" >&5
-printf "%s\n" "$ac_cv_lib_soname_MoltenVK" >&6; }
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_MoltenVK" >&5
+printf '%s\n' "$ac_cv_lib_soname_MoltenVK" >&6; }
 
-printf "%s\n" "#define SONAME_LIBMOLTENVK \"$ac_cv_lib_soname_MoltenVK\"" >>confdefs.h
+printf '%s\n' "#define SONAME_LIBMOLTENVK \"$ac_cv_lib_soname_MoltenVK\"" >>confdefs.h
 
-       printf "%s\n" "#define SONAME_LIBVULKAN \"$ac_cv_lib_soname_MoltenVK\"" >>confdefs.h
+       printf '%s\n' "#define SONAME_LIBVULKAN \"$ac_cv_lib_soname_MoltenVK\"" >>confdefs.h
  ;;
 esac
 fi
@@ -20690,7 +20454,7 @@ then
   EXTRACFLAGS="$EXTRACFLAGS -Wall -pipe"
 
       saved_CFLAGS=$CFLAGS
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror=unknown-warning-option" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror=unknown-warning-option" >&5
 printf %s "checking whether the compiler supports -Werror=unknown-warning-option... " >&6; }
 if test ${ac_cv_cflags__Werror_unknown_warning_option+y}
 then :
@@ -20714,13 +20478,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror_unknown_warning_option" >&5
-printf "%s\n" "$ac_cv_cflags__Werror_unknown_warning_option" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror_unknown_warning_option" >&5
+printf '%s\n' "$ac_cv_cflags__Werror_unknown_warning_option" >&6; }
 if test "x$ac_cv_cflags__Werror_unknown_warning_option" = xyes
 then :
   CFLAGS="$CFLAGS -Werror=unknown-warning-option"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror=unused-command-line-argument" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror=unused-command-line-argument" >&5
 printf %s "checking whether the compiler supports -Werror=unused-command-line-argument... " >&6; }
 if test ${ac_cv_cflags__Werror_unused_command_line_argument+y}
 then :
@@ -20744,13 +20508,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror_unused_command_line_argument" >&5
-printf "%s\n" "$ac_cv_cflags__Werror_unused_command_line_argument" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror_unused_command_line_argument" >&5
+printf '%s\n' "$ac_cv_cflags__Werror_unused_command_line_argument" >&6; }
 if test "x$ac_cv_cflags__Werror_unused_command_line_argument" = xyes
 then :
   CFLAGS="$CFLAGS -Werror=unused-command-line-argument"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror=ignored-optimization-argument" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror=ignored-optimization-argument" >&5
 printf %s "checking whether the compiler supports -Werror=ignored-optimization-argument... " >&6; }
 if test ${ac_cv_cflags__Werror_ignored_optimization_argument+y}
 then :
@@ -20774,13 +20538,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror_ignored_optimization_argument" >&5
-printf "%s\n" "$ac_cv_cflags__Werror_ignored_optimization_argument" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror_ignored_optimization_argument" >&5
+printf '%s\n' "$ac_cv_cflags__Werror_ignored_optimization_argument" >&6; }
 if test "x$ac_cv_cflags__Werror_ignored_optimization_argument" = xyes
 then :
   CFLAGS="$CFLAGS -Werror=ignored-optimization-argument"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fcf-protection=none" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fcf-protection=none" >&5
 printf %s "checking whether the compiler supports -fcf-protection=none... " >&6; }
 if test ${ac_cv_cflags__fcf_protection_none+y}
 then :
@@ -20804,13 +20568,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fcf_protection_none" >&5
-printf "%s\n" "$ac_cv_cflags__fcf_protection_none" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fcf_protection_none" >&5
+printf '%s\n' "$ac_cv_cflags__fcf_protection_none" >&6; }
 if test "x$ac_cv_cflags__fcf_protection_none" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -fcf-protection=none"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fvisibility=hidden" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fvisibility=hidden" >&5
 printf %s "checking whether the compiler supports -fvisibility=hidden... " >&6; }
 if test ${ac_cv_cflags__fvisibility_hidden+y}
 then :
@@ -20834,13 +20598,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fvisibility_hidden" >&5
-printf "%s\n" "$ac_cv_cflags__fvisibility_hidden" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fvisibility_hidden" >&5
+printf '%s\n' "$ac_cv_cflags__fvisibility_hidden" >&6; }
 if test "x$ac_cv_cflags__fvisibility_hidden" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -fvisibility=hidden"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-stack-protector" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-stack-protector" >&5
 printf %s "checking whether the compiler supports -fno-stack-protector... " >&6; }
 if test ${ac_cv_cflags__fno_stack_protector+y}
 then :
@@ -20864,13 +20628,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_stack_protector" >&5
-printf "%s\n" "$ac_cv_cflags__fno_stack_protector" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_stack_protector" >&5
+printf '%s\n' "$ac_cv_cflags__fno_stack_protector" >&6; }
 if test "x$ac_cv_cflags__fno_stack_protector" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -fno-stack-protector"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-strict-aliasing" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-strict-aliasing" >&5
 printf %s "checking whether the compiler supports -fno-strict-aliasing... " >&6; }
 if test ${ac_cv_cflags__fno_strict_aliasing+y}
 then :
@@ -20894,13 +20658,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_strict_aliasing" >&5
-printf "%s\n" "$ac_cv_cflags__fno_strict_aliasing" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_strict_aliasing" >&5
+printf '%s\n' "$ac_cv_cflags__fno_strict_aliasing" >&6; }
 if test "x$ac_cv_cflags__fno_strict_aliasing" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -fno-strict-aliasing"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wdeclaration-after-statement" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wdeclaration-after-statement" >&5
 printf %s "checking whether the compiler supports -Wdeclaration-after-statement... " >&6; }
 if test ${ac_cv_cflags__Wdeclaration_after_statement+y}
 then :
@@ -20924,13 +20688,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wdeclaration_after_statement" >&5
-printf "%s\n" "$ac_cv_cflags__Wdeclaration_after_statement" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wdeclaration_after_statement" >&5
+printf '%s\n' "$ac_cv_cflags__Wdeclaration_after_statement" >&6; }
 if test "x$ac_cv_cflags__Wdeclaration_after_statement" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wdeclaration-after-statement"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wempty-body" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wempty-body" >&5
 printf %s "checking whether the compiler supports -Wempty-body... " >&6; }
 if test ${ac_cv_cflags__Wempty_body+y}
 then :
@@ -20954,13 +20718,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wempty_body" >&5
-printf "%s\n" "$ac_cv_cflags__Wempty_body" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wempty_body" >&5
+printf '%s\n' "$ac_cv_cflags__Wempty_body" >&6; }
 if test "x$ac_cv_cflags__Wempty_body" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wempty-body"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wignored-qualifiers" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wignored-qualifiers" >&5
 printf %s "checking whether the compiler supports -Wignored-qualifiers... " >&6; }
 if test ${ac_cv_cflags__Wignored_qualifiers+y}
 then :
@@ -20984,13 +20748,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wignored_qualifiers" >&5
-printf "%s\n" "$ac_cv_cflags__Wignored_qualifiers" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wignored_qualifiers" >&5
+printf '%s\n' "$ac_cv_cflags__Wignored_qualifiers" >&6; }
 if test "x$ac_cv_cflags__Wignored_qualifiers" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wignored-qualifiers"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Winit-self" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Winit-self" >&5
 printf %s "checking whether the compiler supports -Winit-self... " >&6; }
 if test ${ac_cv_cflags__Winit_self+y}
 then :
@@ -21014,13 +20778,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Winit_self" >&5
-printf "%s\n" "$ac_cv_cflags__Winit_self" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Winit_self" >&5
+printf '%s\n' "$ac_cv_cflags__Winit_self" >&6; }
 if test "x$ac_cv_cflags__Winit_self" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Winit-self"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wlogical-op" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wlogical-op" >&5
 printf %s "checking whether the compiler supports -Wlogical-op... " >&6; }
 if test ${ac_cv_cflags__Wlogical_op+y}
 then :
@@ -21044,13 +20808,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wlogical_op" >&5
-printf "%s\n" "$ac_cv_cflags__Wlogical_op" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wlogical_op" >&5
+printf '%s\n' "$ac_cv_cflags__Wlogical_op" >&6; }
 if test "x$ac_cv_cflags__Wlogical_op" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wlogical-op"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wpacked-not-aligned" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wpacked-not-aligned" >&5
 printf %s "checking whether the compiler supports -Wpacked-not-aligned... " >&6; }
 if test ${ac_cv_cflags__Wpacked_not_aligned+y}
 then :
@@ -21074,13 +20838,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wpacked_not_aligned" >&5
-printf "%s\n" "$ac_cv_cflags__Wpacked_not_aligned" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wpacked_not_aligned" >&5
+printf '%s\n' "$ac_cv_cflags__Wpacked_not_aligned" >&6; }
 if test "x$ac_cv_cflags__Wpacked_not_aligned" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wno-packed-not-aligned"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wpointer-arith" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wpointer-arith" >&5
 printf %s "checking whether the compiler supports -Wpointer-arith... " >&6; }
 if test ${ac_cv_cflags__Wpointer_arith+y}
 then :
@@ -21104,13 +20868,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wpointer_arith" >&5
-printf "%s\n" "$ac_cv_cflags__Wpointer_arith" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wpointer_arith" >&5
+printf '%s\n' "$ac_cv_cflags__Wpointer_arith" >&6; }
 if test "x$ac_cv_cflags__Wpointer_arith" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wpointer-arith"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wshift-overflow=2" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wshift-overflow=2" >&5
 printf %s "checking whether the compiler supports -Wshift-overflow=2... " >&6; }
 if test ${ac_cv_cflags__Wshift_overflow_2+y}
 then :
@@ -21134,13 +20898,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wshift_overflow_2" >&5
-printf "%s\n" "$ac_cv_cflags__Wshift_overflow_2" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wshift_overflow_2" >&5
+printf '%s\n' "$ac_cv_cflags__Wshift_overflow_2" >&6; }
 if test "x$ac_cv_cflags__Wshift_overflow_2" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wshift-overflow=2"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wstrict-prototypes" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wstrict-prototypes" >&5
 printf %s "checking whether the compiler supports -Wstrict-prototypes... " >&6; }
 if test ${ac_cv_cflags__Wstrict_prototypes+y}
 then :
@@ -21164,13 +20928,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wstrict_prototypes" >&5
-printf "%s\n" "$ac_cv_cflags__Wstrict_prototypes" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wstrict_prototypes" >&5
+printf '%s\n' "$ac_cv_cflags__Wstrict_prototypes" >&6; }
 if test "x$ac_cv_cflags__Wstrict_prototypes" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wstrict-prototypes"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wtype-limits" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wtype-limits" >&5
 printf %s "checking whether the compiler supports -Wtype-limits... " >&6; }
 if test ${ac_cv_cflags__Wtype_limits+y}
 then :
@@ -21194,13 +20958,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wtype_limits" >&5
-printf "%s\n" "$ac_cv_cflags__Wtype_limits" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wtype_limits" >&5
+printf '%s\n' "$ac_cv_cflags__Wtype_limits" >&6; }
 if test "x$ac_cv_cflags__Wtype_limits" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wtype-limits"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wunused-but-set-parameter" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wunused-but-set-parameter" >&5
 printf %s "checking whether the compiler supports -Wunused-but-set-parameter... " >&6; }
 if test ${ac_cv_cflags__Wunused_but_set_parameter+y}
 then :
@@ -21224,13 +20988,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wunused_but_set_parameter" >&5
-printf "%s\n" "$ac_cv_cflags__Wunused_but_set_parameter" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wunused_but_set_parameter" >&5
+printf '%s\n' "$ac_cv_cflags__Wunused_but_set_parameter" >&6; }
 if test "x$ac_cv_cflags__Wunused_but_set_parameter" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wunused-but-set-parameter"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wvla" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wvla" >&5
 printf %s "checking whether the compiler supports -Wvla... " >&6; }
 if test ${ac_cv_cflags__Wvla+y}
 then :
@@ -21254,13 +21018,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wvla" >&5
-printf "%s\n" "$ac_cv_cflags__Wvla" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wvla" >&5
+printf '%s\n' "$ac_cv_cflags__Wvla" >&6; }
 if test "x$ac_cv_cflags__Wvla" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wvla"
 fi
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wwrite-strings" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wwrite-strings" >&5
 printf %s "checking whether the compiler supports -Wwrite-strings... " >&6; }
 if test ${ac_cv_cflags__Wwrite_strings+y}
 then :
@@ -21284,14 +21048,14 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wwrite_strings" >&5
-printf "%s\n" "$ac_cv_cflags__Wwrite_strings" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wwrite_strings" >&5
+printf '%s\n' "$ac_cv_cflags__Wwrite_strings" >&6; }
 if test "x$ac_cv_cflags__Wwrite_strings" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wwrite-strings"
 fi
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -flarge-source-files -Wmisleading-indentation" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -flarge-source-files -Wmisleading-indentation" >&5
 printf %s "checking whether the compiler supports -flarge-source-files -Wmisleading-indentation... " >&6; }
 if test ${ac_cv_cflags__flarge_source_files__Wmisleading_indentation+y}
 then :
@@ -21315,14 +21079,14 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__flarge_source_files__Wmisleading_indentation" >&5
-printf "%s\n" "$ac_cv_cflags__flarge_source_files__Wmisleading_indentation" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__flarge_source_files__Wmisleading_indentation" >&5
+printf '%s\n' "$ac_cv_cflags__flarge_source_files__Wmisleading_indentation" >&6; }
 if test "x$ac_cv_cflags__flarge_source_files__Wmisleading_indentation" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Wno-misleading-indentation"
 fi
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for flags needed for 64-bit compare-and-swap support" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for flags needed for 64-bit compare-and-swap support" >&5
 printf %s "checking for flags needed for 64-bit compare-and-swap support... " >&6; }
 if test ${wine_cv_64bit_compare_swap+y}
 then :
@@ -21381,8 +21145,8 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_64bit_compare_swap" >&5
-printf "%s\n" "$wine_cv_64bit_compare_swap" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_64bit_compare_swap" >&5
+printf '%s\n' "$wine_cv_64bit_compare_swap" >&6; }
   case "$wine_cv_64bit_compare_swap" in
       unknown) as_fn_error $? "64-bit compare-and-swap is not supported on this platform" "$LINENO" 5 ;;
       "none needed") ;;
@@ -21398,7 +21162,7 @@ printf "%s\n" "$wine_cv_64bit_compare_swap" >&6; }
   done
   if test "x$ac_debug_format_seen" = xdefault
   then
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -gdwarf-4" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -gdwarf-4" >&5
 printf %s "checking whether the compiler supports -gdwarf-4... " >&6; }
 if test ${ac_cv_cflags__gdwarf_4+y}
 then :
@@ -21422,8 +21186,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__gdwarf_4" >&5
-printf "%s\n" "$ac_cv_cflags__gdwarf_4" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__gdwarf_4" >&5
+printf '%s\n' "$ac_cv_cflags__gdwarf_4" >&6; }
 if test "x$ac_cv_cflags__gdwarf_4" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -gdwarf-4"
@@ -21434,7 +21198,7 @@ fi
   then
     MSVCRTFLAGS="-D_WIN32"
 
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -mlong-double-64" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -mlong-double-64" >&5
 printf %s "checking whether the compiler supports -mlong-double-64... " >&6; }
 if test ${ac_cv_cflags__mlong_double_64+y}
 then :
@@ -21458,13 +21222,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__mlong_double_64" >&5
-printf "%s\n" "$ac_cv_cflags__mlong_double_64" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__mlong_double_64" >&5
+printf '%s\n' "$ac_cv_cflags__mlong_double_64" >&6; }
 if test "x$ac_cv_cflags__mlong_double_64" = xyes
 then :
   MSVCRTFLAGS="$MSVCRTFLAGS -mlong-double-64"
 fi
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-builtin" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-builtin" >&5
 printf %s "checking whether the compiler supports -fno-builtin... " >&6; }
 if test ${ac_cv_cflags__fno_builtin+y}
 then :
@@ -21488,13 +21252,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_builtin" >&5
-printf "%s\n" "$ac_cv_cflags__fno_builtin" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_builtin" >&5
+printf '%s\n' "$ac_cv_cflags__fno_builtin" >&6; }
 if test "x$ac_cv_cflags__fno_builtin" = xyes
 then :
   MSVCRTFLAGS="$MSVCRTFLAGS -fno-builtin"
 fi
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fshort-wchar" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fshort-wchar" >&5
 printf %s "checking whether the compiler supports -fshort-wchar... " >&6; }
 if test ${ac_cv_cflags__fshort_wchar+y}
 then :
@@ -21518,13 +21282,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fshort_wchar" >&5
-printf "%s\n" "$ac_cv_cflags__fshort_wchar" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fshort_wchar" >&5
+printf '%s\n' "$ac_cv_cflags__fshort_wchar" >&6; }
 if test "x$ac_cv_cflags__fshort_wchar" = xyes
 then :
   MSVCRTFLAGS="$MSVCRTFLAGS -fshort-wchar"
 fi
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wno-format" >&5
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wno-format" >&5
 printf %s "checking whether the compiler supports -Wno-format... " >&6; }
 if test ${ac_cv_cflags__Wno_format+y}
 then :
@@ -21548,15 +21312,15 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wno_format" >&5
-printf "%s\n" "$ac_cv_cflags__Wno_format" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wno_format" >&5
+printf '%s\n' "$ac_cv_cflags__Wno_format" >&6; }
 if test "x$ac_cv_cflags__Wno_format" = xyes
 then :
   MSVCRTFLAGS="$MSVCRTFLAGS -Wno-format"
 fi
     if test "$HOST_ARCH" = i386
     then
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-omit-frame-pointer" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fno-omit-frame-pointer" >&5
 printf %s "checking whether the compiler supports -fno-omit-frame-pointer... " >&6; }
 if test ${ac_cv_cflags__fno_omit_frame_pointer+y}
 then :
@@ -21580,14 +21344,14 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_omit_frame_pointer" >&5
-printf "%s\n" "$ac_cv_cflags__fno_omit_frame_pointer" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__fno_omit_frame_pointer" >&5
+printf '%s\n' "$ac_cv_cflags__fno_omit_frame_pointer" >&6; }
 if test "x$ac_cv_cflags__fno_omit_frame_pointer" = xyes
 then :
   MSVCRTFLAGS="$MSVCRTFLAGS -fno-omit-frame-pointer"
 fi
     else
-            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working -mabi=ms" >&5
+            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for working -mabi=ms" >&5
 printf %s "checking for working -mabi=ms... " >&6; }
 if test ${ac_cv_mabi_ms+y}
 then :
@@ -21617,8 +21381,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
               CFLAGS=$saved_CFLAGS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mabi_ms" >&5
-printf "%s\n" "$ac_cv_mabi_ms" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mabi_ms" >&5
+printf '%s\n' "$ac_cv_mabi_ms" >&6; }
       if test "x$ac_cv_mabi_ms" = xyes
 then :
   MSVCRTFLAGS="$MSVCRTFLAGS -mabi=ms"
@@ -21634,7 +21398,7 @@ fi
 
     if test "x$enable_werror" = "xyes"
   then
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror" >&5
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Werror" >&5
 printf %s "checking whether the compiler supports -Werror... " >&6; }
 if test ${ac_cv_cflags__Werror+y}
 then :
@@ -21658,8 +21422,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror" >&5
-printf "%s\n" "$ac_cv_cflags__Werror" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Werror" >&5
+printf '%s\n' "$ac_cv_cflags__Werror" >&6; }
 if test "x$ac_cv_cflags__Werror" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -Werror"
@@ -21668,7 +21432,7 @@ fi
 
   if test "x$enable_build_id" = xyes
 then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--build-id" >&5
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--build-id" >&5
 printf %s "checking whether the compiler supports -Wl,--build-id... " >&6; }
 if test ${ac_cv_cflags__Wl___build_id+y}
 then :
@@ -21692,8 +21456,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
 CFLAGS=$ac_wine_try_cflags_saved ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___build_id" >&5
-printf "%s\n" "$ac_cv_cflags__Wl___build_id" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___build_id" >&5
+printf '%s\n' "$ac_cv_cflags__Wl___build_id" >&6; }
 if test "x$ac_cv_cflags__Wl___build_id" = xyes
 then :
   LDFLAGS="$LDFLAGS -Wl,--build-id"
@@ -21702,7 +21466,7 @@ fi
 fi
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the need to disable Fortify" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for the need to disable Fortify" >&5
 printf %s "checking for the need to disable Fortify... " >&6; }
 if test ${ac_cv_c_fortify_enabled+y}
 then :
@@ -21731,15 +21495,15 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_fortify_enabled" >&5
-printf "%s\n" "$ac_cv_c_fortify_enabled" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_fortify_enabled" >&5
+printf '%s\n' "$ac_cv_c_fortify_enabled" >&6; }
 if test "$ac_cv_c_fortify_enabled" = yes
 then
     CFLAGS="$CFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"
 fi
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether CFI directives are supported in assembly code" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether CFI directives are supported in assembly code" >&5
 printf %s "checking whether CFI directives are supported in assembly code... " >&6; }
 if test ${ac_cv_c_cfi_support+y}
 then :
@@ -21766,8 +21530,8 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_cfi_support" >&5
-printf "%s\n" "$ac_cv_c_cfi_support" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_cfi_support" >&5
+printf '%s\n' "$ac_cv_c_cfi_support" >&6; }
 if test "$ac_cv_c_cfi_support" = "yes"
 then
    DLLFLAGS="$DLLFLAGS -fasynchronous-unwind-tables"
@@ -21795,12 +21559,12 @@ if test "$wine_use_preloader" = "yes"
 then
   WINELOADER_PROGRAMS="$WINELOADER_PROGRAMS wine-preloader"
 
-printf "%s\n" "#define HAVE_WINE_PRELOADER 1" >>confdefs.h
+printf '%s\n' "#define HAVE_WINE_PRELOADER 1" >>confdefs.h
 
 fi
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
 printf %s "checking for library containing dlopen... " >&6; }
 if test ${ac_cv_search_dlopen+y}
 then :
@@ -21858,8 +21622,8 @@ rm conftest.$ac_ext
 LIBS=$ac_func_search_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
-printf "%s\n" "$ac_cv_search_dlopen" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
+printf '%s\n' "$ac_cv_search_dlopen" >&6; }
 ac_res=$ac_cv_search_dlopen
 if test "$ac_res" != no
 then :
@@ -21873,188 +21637,188 @@ CFLAGS="$CFLAGS $BUILTINFLAG"
 ac_fn_c_check_func "$LINENO" "dladdr1" "ac_cv_func_dladdr1"
 if test "x$ac_cv_func_dladdr1" = xyes
 then :
-  printf "%s\n" "#define HAVE_DLADDR1 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_DLADDR1 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "dlinfo" "ac_cv_func_dlinfo"
 if test "x$ac_cv_func_dlinfo" = xyes
 then :
-  printf "%s\n" "#define HAVE_DLINFO 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_DLINFO 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "epoll_create" "ac_cv_func_epoll_create"
 if test "x$ac_cv_func_epoll_create" = xyes
 then :
-  printf "%s\n" "#define HAVE_EPOLL_CREATE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_EPOLL_CREATE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "epoll_pwait2" "ac_cv_func_epoll_pwait2"
 if test "x$ac_cv_func_epoll_pwait2" = xyes
 then :
-  printf "%s\n" "#define HAVE_EPOLL_PWAIT2 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_EPOLL_PWAIT2 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "fstatfs" "ac_cv_func_fstatfs"
 if test "x$ac_cv_func_fstatfs" = xyes
 then :
-  printf "%s\n" "#define HAVE_FSTATFS 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_FSTATFS 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "futimens" "ac_cv_func_futimens"
 if test "x$ac_cv_func_futimens" = xyes
 then :
-  printf "%s\n" "#define HAVE_FUTIMENS 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_FUTIMENS 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "futimes" "ac_cv_func_futimes"
 if test "x$ac_cv_func_futimes" = xyes
 then :
-  printf "%s\n" "#define HAVE_FUTIMES 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_FUTIMES 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "futimesat" "ac_cv_func_futimesat"
 if test "x$ac_cv_func_futimesat" = xyes
 then :
-  printf "%s\n" "#define HAVE_FUTIMESAT 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_FUTIMESAT 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "getaddrinfo" "ac_cv_func_getaddrinfo"
 if test "x$ac_cv_func_getaddrinfo" = xyes
 then :
-  printf "%s\n" "#define HAVE_GETADDRINFO 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GETADDRINFO 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "getattrlist" "ac_cv_func_getattrlist"
 if test "x$ac_cv_func_getattrlist" = xyes
 then :
-  printf "%s\n" "#define HAVE_GETATTRLIST 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GETATTRLIST 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "getauxval" "ac_cv_func_getauxval"
 if test "x$ac_cv_func_getauxval" = xyes
 then :
-  printf "%s\n" "#define HAVE_GETAUXVAL 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GETAUXVAL 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "getifaddrs" "ac_cv_func_getifaddrs"
 if test "x$ac_cv_func_getifaddrs" = xyes
 then :
-  printf "%s\n" "#define HAVE_GETIFADDRS 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GETIFADDRS 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "getrandom" "ac_cv_func_getrandom"
 if test "x$ac_cv_func_getrandom" = xyes
 then :
-  printf "%s\n" "#define HAVE_GETRANDOM 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_GETRANDOM 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "kqueue" "ac_cv_func_kqueue"
 if test "x$ac_cv_func_kqueue" = xyes
 then :
-  printf "%s\n" "#define HAVE_KQUEUE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_KQUEUE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "memfd_create" "ac_cv_func_memfd_create"
 if test "x$ac_cv_func_memfd_create" = xyes
 then :
-  printf "%s\n" "#define HAVE_MEMFD_CREATE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_MEMFD_CREATE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "pipe2" "ac_cv_func_pipe2"
 if test "x$ac_cv_func_pipe2" = xyes
 then :
-  printf "%s\n" "#define HAVE_PIPE2 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PIPE2 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "port_create" "ac_cv_func_port_create"
 if test "x$ac_cv_func_port_create" = xyes
 then :
-  printf "%s\n" "#define HAVE_PORT_CREATE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PORT_CREATE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "posix_fadvise" "ac_cv_func_posix_fadvise"
 if test "x$ac_cv_func_posix_fadvise" = xyes
 then :
-  printf "%s\n" "#define HAVE_POSIX_FADVISE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_POSIX_FADVISE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "posix_fallocate" "ac_cv_func_posix_fallocate"
 if test "x$ac_cv_func_posix_fallocate" = xyes
 then :
-  printf "%s\n" "#define HAVE_POSIX_FALLOCATE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_POSIX_FALLOCATE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "prctl" "ac_cv_func_prctl"
 if test "x$ac_cv_func_prctl" = xyes
 then :
-  printf "%s\n" "#define HAVE_PRCTL 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PRCTL 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "process_vm_readv" "ac_cv_func_process_vm_readv"
 if test "x$ac_cv_func_process_vm_readv" = xyes
 then :
-  printf "%s\n" "#define HAVE_PROCESS_VM_READV 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PROCESS_VM_READV 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "process_vm_writev" "ac_cv_func_process_vm_writev"
 if test "x$ac_cv_func_process_vm_writev" = xyes
 then :
-  printf "%s\n" "#define HAVE_PROCESS_VM_WRITEV 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_PROCESS_VM_WRITEV 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "sched_getcpu" "ac_cv_func_sched_getcpu"
 if test "x$ac_cv_func_sched_getcpu" = xyes
 then :
-  printf "%s\n" "#define HAVE_SCHED_GETCPU 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SCHED_GETCPU 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "sched_yield" "ac_cv_func_sched_yield"
 if test "x$ac_cv_func_sched_yield" = xyes
 then :
-  printf "%s\n" "#define HAVE_SCHED_YIELD 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SCHED_YIELD 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "setproctitle" "ac_cv_func_setproctitle"
 if test "x$ac_cv_func_setproctitle" = xyes
 then :
-  printf "%s\n" "#define HAVE_SETPROCTITLE 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SETPROCTITLE 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "setprogname" "ac_cv_func_setprogname"
 if test "x$ac_cv_func_setprogname" = xyes
 then :
-  printf "%s\n" "#define HAVE_SETPROGNAME 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SETPROGNAME 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "sigprocmask" "ac_cv_func_sigprocmask"
 if test "x$ac_cv_func_sigprocmask" = xyes
 then :
-  printf "%s\n" "#define HAVE_SIGPROCMASK 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SIGPROCMASK 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "sysinfo" "ac_cv_func_sysinfo"
 if test "x$ac_cv_func_sysinfo" = xyes
 then :
-  printf "%s\n" "#define HAVE_SYSINFO 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_SYSINFO 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "tcdrain" "ac_cv_func_tcdrain"
 if test "x$ac_cv_func_tcdrain" = xyes
 then :
-  printf "%s\n" "#define HAVE_TCDRAIN 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_TCDRAIN 1" >>confdefs.h
 
 fi
 ac_fn_c_check_func "$LINENO" "thr_kill2" "ac_cv_func_thr_kill2"
 if test "x$ac_cv_func_thr_kill2" = xyes
 then :
-  printf "%s\n" "#define HAVE_THR_KILL2 1" >>confdefs.h
+  printf '%s\n' "#define HAVE_THR_KILL2 1" >>confdefs.h
 
 fi
 
 CFLAGS="$ac_save_CFLAGS"
 
 ac_save_LIBS=$LIBS
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
 printf %s "checking for library containing clock_gettime... " >&6; }
 if test ${ac_cv_search_clock_gettime+y}
 then :
@@ -22112,14 +21876,14 @@ rm conftest.$ac_ext
 LIBS=$ac_func_search_save_LIBS ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
-printf "%s\n" "$ac_cv_search_clock_gettime" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+printf '%s\n' "$ac_cv_search_clock_gettime" >&6; }
 ac_res=$ac_cv_search_clock_gettime
 if test "$ac_res" != no
 then :
   test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
 
-printf "%s\n" "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h
+printf '%s\n' "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h
 
                 test "$ac_res" = "none required" || RT_LIBS="$ac_res"
 
@@ -22127,7 +21891,7 @@ fi
 
 LIBS=$ac_save_LIBS
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sched_setaffinity" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for sched_setaffinity" >&5
 printf %s "checking for sched_setaffinity... " >&6; }
 if test ${wine_cv_have_sched_setaffinity+y}
 then :
@@ -22155,17 +21919,17 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     conftest$ac_exeext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_have_sched_setaffinity" >&5
-printf "%s\n" "$wine_cv_have_sched_setaffinity" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_have_sched_setaffinity" >&5
+printf '%s\n' "$wine_cv_have_sched_setaffinity" >&6; }
 if test "$wine_cv_have_sched_setaffinity" = "yes"
 then
 
-printf "%s\n" "#define HAVE_SCHED_SETAFFINITY 1" >>confdefs.h
+printf '%s\n' "#define HAVE_SCHED_SETAFFINITY 1" >>confdefs.h
 
 fi
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we can use re-entrant gethostbyname_r Linux style" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we can use re-entrant gethostbyname_r Linux style" >&5
 printf %s "checking whether we can use re-entrant gethostbyname_r Linux style... " >&6; }
 if test ${wine_cv_linux_gethostbyname_r_6+y}
 then :
@@ -22207,12 +21971,12 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $wine_cv_linux_gethostbyname_r_6" >&5
-printf "%s\n" "$wine_cv_linux_gethostbyname_r_6" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $wine_cv_linux_gethostbyname_r_6" >&5
+printf '%s\n' "$wine_cv_linux_gethostbyname_r_6" >&6; }
    if test "$wine_cv_linux_gethostbyname_r_6" = "yes"
    then
 
-printf "%s\n" "#define HAVE_LINUX_GETHOSTBYNAME_R_6 1" >>confdefs.h
+printf '%s\n' "#define HAVE_LINUX_GETHOSTBYNAME_R_6 1" >>confdefs.h
 
    fi
 
@@ -22225,7 +21989,7 @@ ac_fn_c_check_member "$LINENO" "struct sockaddr_un" "sun_len" "ac_cv_member_stru
 if test "x$ac_cv_member_struct_sockaddr_un_sun_len" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_UN_SUN_LEN 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_SOCKADDR_UN_SUN_LEN 1" >>confdefs.h
 
 
 fi
@@ -22239,7 +22003,7 @@ ac_fn_c_check_member "$LINENO" "sg_io_hdr_t" "interface_id" "ac_cv_member_sg_io_
 if test "x$ac_cv_member_sg_io_hdr_t_interface_id" = xyes
 then :
 
-printf "%s\n" "#define HAVE_SG_IO_HDR_T_INTERFACE_ID 1" >>confdefs.h
+printf '%s\n' "#define HAVE_SG_IO_HDR_T_INTERFACE_ID 1" >>confdefs.h
 
 
 fi
@@ -22250,7 +22014,7 @@ ac_fn_c_check_member "$LINENO" "siginfo_t" "si_fd" "ac_cv_member_siginfo_t_si_fd
 if test "x$ac_cv_member_siginfo_t_si_fd" = xyes
 then :
 
-printf "%s\n" "#define HAVE_SIGINFO_T_SI_FD 1" >>confdefs.h
+printf '%s\n' "#define HAVE_SIGINFO_T_SI_FD 1" >>confdefs.h
 
 
 fi
@@ -22264,7 +22028,7 @@ ac_fn_c_check_member "$LINENO" "struct mtget" "mt_blksiz" "ac_cv_member_struct_m
 if test "x$ac_cv_member_struct_mtget_mt_blksiz" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_MTGET_MT_BLKSIZ 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_MTGET_MT_BLKSIZ 1" >>confdefs.h
 
 
 fi
@@ -22276,7 +22040,7 @@ ac_fn_c_check_member "$LINENO" "struct mtget" "mt_gstat" "ac_cv_member_struct_mt
 if test "x$ac_cv_member_struct_mtget_mt_gstat" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_MTGET_MT_GSTAT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_MTGET_MT_GSTAT 1" >>confdefs.h
 
 
 fi
@@ -22288,7 +22052,7 @@ ac_fn_c_check_member "$LINENO" "struct mtget" "mt_blkno" "ac_cv_member_struct_mt
 if test "x$ac_cv_member_struct_mtget_mt_blkno" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_MTGET_MT_BLKNO 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_MTGET_MT_BLKNO 1" >>confdefs.h
 
 
 fi
@@ -22298,7 +22062,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim" "ac_cv_member_struct_stat
 if test "x$ac_cv_member_struct_stat_st_mtim" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_MTIM 1" >>confdefs.h
 
 
 fi
@@ -22306,7 +22070,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec" "ac_cv_member_struct
 if test "x$ac_cv_member_struct_stat_st_mtimespec" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIMESPEC 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_MTIMESPEC 1" >>confdefs.h
 
 
 fi
@@ -22314,7 +22078,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_ctim" "ac_cv_member_struct_stat
 if test "x$ac_cv_member_struct_stat_st_ctim" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_CTIM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_CTIM 1" >>confdefs.h
 
 
 fi
@@ -22322,7 +22086,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_ctimespec" "ac_cv_member_struct
 if test "x$ac_cv_member_struct_stat_st_ctimespec" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_CTIMESPEC 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_CTIMESPEC 1" >>confdefs.h
 
 
 fi
@@ -22330,7 +22094,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_atim" "ac_cv_member_struct_stat
 if test "x$ac_cv_member_struct_stat_st_atim" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_ATIM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_ATIM 1" >>confdefs.h
 
 
 fi
@@ -22338,7 +22102,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec" "ac_cv_member_struct
 if test "x$ac_cv_member_struct_stat_st_atimespec" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_ATIMESPEC 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_ATIMESPEC 1" >>confdefs.h
 
 
 fi
@@ -22346,7 +22110,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtime" "ac_cv_member_struct
 if test "x$ac_cv_member_struct_stat_st_birthtime" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1" >>confdefs.h
 
 
 fi
@@ -22354,7 +22118,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtim" "ac_cv_member_struct_
 if test "x$ac_cv_member_struct_stat_st_birthtim" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BIRTHTIM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_BIRTHTIM 1" >>confdefs.h
 
 
 fi
@@ -22362,7 +22126,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtimespec" "ac_cv_member_st
 if test "x$ac_cv_member_struct_stat_st_birthtimespec" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC 1" >>confdefs.h
 
 
 fi
@@ -22370,7 +22134,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "__st_birthtime" "ac_cv_member_stru
 if test "x$ac_cv_member_struct_stat___st_birthtime" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT___ST_BIRTHTIME 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT___ST_BIRTHTIME 1" >>confdefs.h
 
 
 fi
@@ -22378,7 +22142,7 @@ ac_fn_c_check_member "$LINENO" "struct stat" "__st_birthtim" "ac_cv_member_struc
 if test "x$ac_cv_member_struct_stat___st_birthtim" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_STAT___ST_BIRTHTIM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_STAT___ST_BIRTHTIM 1" >>confdefs.h
 
 
 fi
@@ -22392,7 +22156,7 @@ ac_fn_c_check_member "$LINENO" "struct sockaddr_in6" "sin6_scope_id" "ac_cv_memb
 if test "x$ac_cv_member_struct_sockaddr_in6_sin6_scope_id" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1" >>confdefs.h
 
 
 fi
@@ -22405,7 +22169,7 @@ ac_fn_c_check_member "$LINENO" "struct __res_state" "_u._ext.nscount6" "ac_cv_me
 if test "x$ac_cv_member_struct___res_state__u__ext_nscount6" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT___RES_STATE__U__EXT_NSCOUNT6 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT___RES_STATE__U__EXT_NSCOUNT6 1" >>confdefs.h
 
 
 fi
@@ -22418,7 +22182,7 @@ ac_fn_c_check_member "$LINENO" "struct in6_pktinfo" "ipi6_addr" "ac_cv_member_st
 if test "x$ac_cv_member_struct_in6_pktinfo_ipi6_addr" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_IN6_PKTINFO_IPI6_ADDR 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_IN6_PKTINFO_IPI6_ADDR 1" >>confdefs.h
 
 
 fi
@@ -22438,7 +22202,7 @@ ac_fn_c_check_member "$LINENO" "struct ipstat" "ips_total" "ac_cv_member_struct_
 if test "x$ac_cv_member_struct_ipstat_ips_total" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_IPSTAT_IPS_TOTAL 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_IPSTAT_IPS_TOTAL 1" >>confdefs.h
 
 
 fi
@@ -22451,7 +22215,7 @@ ac_fn_c_check_member "$LINENO" "struct ip_stats" "ips_total" "ac_cv_member_struc
 if test "x$ac_cv_member_struct_ip_stats_ips_total" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_IP_STATS_IPS_TOTAL 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_IP_STATS_IPS_TOTAL 1" >>confdefs.h
 
 
 fi
@@ -22471,7 +22235,7 @@ ac_fn_c_check_member "$LINENO" "struct ip6stat" "ip6s_total" "ac_cv_member_struc
 if test "x$ac_cv_member_struct_ip6stat_ip6s_total" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_IP6STAT_IP6S_TOTAL 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_IP6STAT_IP6S_TOTAL 1" >>confdefs.h
 
 
 fi
@@ -22497,7 +22261,7 @@ ac_fn_c_check_member "$LINENO" "struct icmpstat" "icps_error" "ac_cv_member_stru
 if test "x$ac_cv_member_struct_icmpstat_icps_error" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_ICMPSTAT_ICPS_ERROR 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_ICMPSTAT_ICPS_ERROR 1" >>confdefs.h
 
 
 fi
@@ -22517,7 +22281,7 @@ ac_fn_c_check_member "$LINENO" "struct icmp6stat" "icp6s_error" "ac_cv_member_st
 if test "x$ac_cv_member_struct_icmp6stat_icp6s_error" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_ICMP6STAT_ICP6S_ERROR 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_ICMP6STAT_ICP6S_ERROR 1" >>confdefs.h
 
 
 fi
@@ -22540,7 +22304,7 @@ ac_fn_c_check_member "$LINENO" "struct tcpstat" "tcps_connattempt" "ac_cv_member
 if test "x$ac_cv_member_struct_tcpstat_tcps_connattempt" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT 1" >>confdefs.h
 
 
 fi
@@ -22553,7 +22317,7 @@ ac_fn_c_check_member "$LINENO" "struct tcp_stats" "tcps_connattempt" "ac_cv_memb
 if test "x$ac_cv_member_struct_tcp_stats_tcps_connattempt" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT 1" >>confdefs.h
 
 
 fi
@@ -22576,7 +22340,7 @@ ac_fn_c_check_member "$LINENO" "struct udpstat" "udps_ipackets" "ac_cv_member_st
 if test "x$ac_cv_member_struct_udpstat_udps_ipackets" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS 1" >>confdefs.h
 
 
 fi
@@ -22590,7 +22354,7 @@ ac_fn_c_check_member "$LINENO" "struct ifreq" "ifr_hwaddr" "ac_cv_member_struct_
 if test "x$ac_cv_member_struct_ifreq_ifr_hwaddr" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_IFREQ_IFR_HWADDR 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_IFREQ_IFR_HWADDR 1" >>confdefs.h
 
 
 fi
@@ -22603,7 +22367,7 @@ ac_fn_c_check_member "$LINENO" "struct sysinfo" "totalram" "ac_cv_member_struct_
 if test "x$ac_cv_member_struct_sysinfo_totalram" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_SYSINFO_TOTALRAM 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_SYSINFO_TOTALRAM 1" >>confdefs.h
 
 
 fi
@@ -22614,13 +22378,13 @@ ac_fn_c_check_member "$LINENO" "struct sysinfo" "mem_unit" "ac_cv_member_struct_
 if test "x$ac_cv_member_struct_sysinfo_mem_unit" = xyes
 then :
 
-printf "%s\n" "#define HAVE_STRUCT_SYSINFO_MEM_UNIT 1" >>confdefs.h
+printf '%s\n' "#define HAVE_STRUCT_SYSINFO_MEM_UNIT 1" >>confdefs.h
 
 
 fi
 
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __builtin_popcount" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for __builtin_popcount" >&5
 printf %s "checking for __builtin_popcount... " >&6; }
 if test ${ac_cv_have___builtin_popcount+y}
 then :
@@ -22648,16 +22412,16 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     conftest$ac_exeext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_popcount" >&5
-printf "%s\n" "$ac_cv_have___builtin_popcount" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_popcount" >&5
+printf '%s\n' "$ac_cv_have___builtin_popcount" >&6; }
 if test "$ac_cv_have___builtin_popcount" = "yes"
 then
 
-printf "%s\n" "#define HAVE___BUILTIN_POPCOUNT 1" >>confdefs.h
+printf '%s\n' "#define HAVE___BUILTIN_POPCOUNT 1" >>confdefs.h
 
 fi
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __clear_cache" >&5
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for __clear_cache" >&5
 printf %s "checking for __clear_cache... " >&6; }
 if test ${ac_cv_have___clear_cache+y}
 then :
@@ -22685,18 +22449,18 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
     conftest$ac_exeext conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___clear_cache" >&5
-printf "%s\n" "$ac_cv_have___clear_cache" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___clear_cache" >&5
+printf '%s\n' "$ac_cv_have___clear_cache" >&6; }
 if test "$ac_cv_have___clear_cache" = "yes"
 then
 
-printf "%s\n" "#define HAVE___CLEAR_CACHE 1" >>confdefs.h
+printf '%s\n' "#define HAVE___CLEAR_CACHE 1" >>confdefs.h
 
 fi
 
 
 case $host_cpu in
-  *i[3456789]86*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need to define __i386__" >&5
+  *i[3456789]86*) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we need to define __i386__" >&5
 printf %s "checking whether we need to define __i386__... " >&6; }
 if test ${ac_cv_cpp_def___i386__+y}
 then :
@@ -22725,13 +22489,13 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___i386__" >&5
-printf "%s\n" "$ac_cv_cpp_def___i386__" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___i386__" >&5
+printf '%s\n' "$ac_cv_cpp_def___i386__" >&6; }
 if test "x$ac_cv_cpp_def___i386__" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -D__i386__"
 fi ;;
-  *x86_64*)         { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need to define __x86_64__" >&5
+  *x86_64*)         { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we need to define __x86_64__" >&5
 printf %s "checking whether we need to define __x86_64__... " >&6; }
 if test ${ac_cv_cpp_def___x86_64__+y}
 then :
@@ -22760,13 +22524,13 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___x86_64__" >&5
-printf "%s\n" "$ac_cv_cpp_def___x86_64__" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___x86_64__" >&5
+printf '%s\n' "$ac_cv_cpp_def___x86_64__" >&6; }
 if test "x$ac_cv_cpp_def___x86_64__" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -D__x86_64__"
 fi ;;
-  *powerpc64*)      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need to define __powerpc64__" >&5
+  *powerpc64*)      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we need to define __powerpc64__" >&5
 printf %s "checking whether we need to define __powerpc64__... " >&6; }
 if test ${ac_cv_cpp_def___powerpc64__+y}
 then :
@@ -22795,13 +22559,13 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___powerpc64__" >&5
-printf "%s\n" "$ac_cv_cpp_def___powerpc64__" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___powerpc64__" >&5
+printf '%s\n' "$ac_cv_cpp_def___powerpc64__" >&6; }
 if test "x$ac_cv_cpp_def___powerpc64__" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -D__powerpc64__"
 fi ;;
-  *aarch64*)        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need to define __aarch64__" >&5
+  *aarch64*)        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we need to define __aarch64__" >&5
 printf %s "checking whether we need to define __aarch64__... " >&6; }
 if test ${ac_cv_cpp_def___aarch64__+y}
 then :
@@ -22830,13 +22594,13 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___aarch64__" >&5
-printf "%s\n" "$ac_cv_cpp_def___aarch64__" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___aarch64__" >&5
+printf '%s\n' "$ac_cv_cpp_def___aarch64__" >&6; }
 if test "x$ac_cv_cpp_def___aarch64__" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -D__aarch64__"
 fi ;;
-  *arm*)            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need to define __arm__" >&5
+  *arm*)            { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we need to define __arm__" >&5
 printf %s "checking whether we need to define __arm__... " >&6; }
 if test ${ac_cv_cpp_def___arm__+y}
 then :
@@ -22865,8 +22629,8 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
 esac
 fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___arm__" >&5
-printf "%s\n" "$ac_cv_cpp_def___arm__" >&6; }
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cpp_def___arm__" >&5
+printf '%s\n' "$ac_cv_cpp_def___arm__" >&6; }
 if test "x$ac_cv_cpp_def___arm__" = xyes
 then :
   EXTRACFLAGS="$EXTRACFLAGS -D__arm__"
@@ -24034,8 +23798,8 @@ wine_fn_config_makefile libs/sqlite3 enable_sqlite3
 wine_fn_config_makefile libs/strmbase enable_strmbase
 wine_fn_config_makefile libs/strmiids enable_strmiids
 wine_fn_config_makefile libs/strsafe enable_strsafe
+wine_fn_config_makefile libs/symcrypt enable_symcrypt
 wine_fn_config_makefile libs/tiff enable_tiff
-wine_fn_config_makefile libs/tomcrypt enable_tomcrypt
 wine_fn_config_makefile libs/unwind enable_unwind
 wine_fn_config_makefile libs/uuid enable_uuid
 wine_fn_config_makefile libs/vkd3d enable_vkd3d
@@ -24262,7 +24026,7 @@ dlls/wineandroid.drv/wine-debug.apk: dlls/wineandroid.drv/build.gradle ${wine_sr
 	mv dlls/wineandroid.drv/build/outputs/apk/debug/wine-debug.apk \$@"
 
 
-EXTERNAL_SUBDIRS="libs/capstone libs/c++ libs/c++abi libs/faudio libs/fluidsynth libs/gsm libs/icucommon libs/icui18n libs/jpeg libs/jxr libs/lcms2 libs/ldap libs/mpg123 libs/musl libs/png libs/sqlite3 libs/tiff libs/tomcrypt libs/unwind libs/vkd3d libs/xml2 libs/xslt libs/zlib libs/compiler-rt"
+EXTERNAL_SUBDIRS="libs/capstone libs/c++ libs/c++abi libs/faudio libs/fluidsynth libs/gsm libs/icucommon libs/icui18n libs/jpeg libs/jxr libs/lcms2 libs/ldap libs/mpg123 libs/musl libs/png libs/sqlite3 libs/symcrypt libs/tiff libs/unwind libs/vkd3d libs/xml2 libs/xslt libs/zlib libs/compiler-rt"
 
 TAGSFLAGS="--langmap='c:+.idl.l.rh,make:(Make*.in)'"
 
@@ -24276,7 +24040,7 @@ tags ctags:
 dummy:
 .PHONY: dummy"
 
-printf "%s\n" " done" >&6
+printf '%s\n' " done" >&6
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
 # tests run on this system so they can be shared between configure
@@ -24293,44 +24057,7 @@ cat >confcache <<\_ACEOF
 
 _ACEOF
 
-# The following way of writing the cache mishandles newlines in values,
-# but we know of no workaround that is simple, portable, and efficient.
-# So, we kill variables containing newlines.
-# Ultrix sh set writes to stderr and can't be redirected directly,
-# and sets the high bit in the cache file unless we assign to the vars.
-(
-  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
-    eval ac_val=\$$ac_var
-    case $ac_val in #(
-    *${as_nl}*)
-      case $ac_var in #(
-      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
-printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
-      esac
-      case $ac_var in #(
-      _ | IFS | as_nl) ;; #(
-      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
-      *) { eval $ac_var=; unset $ac_var;} ;;
-      esac ;;
-    esac
-  done
-
-  (set) 2>&1 |
-    case $as_nl`(ac_space=' '; set) 2>&1` in #(
-    *${as_nl}ac_space=\ *)
-      # 'set' does not quote correctly, so add quotes: double-quote
-      # substitution turns \\\\ into \\, and sed turns \\ into \.
-      sed -n \
-	"s/'/'\\\\''/g;
-	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
-      ;; #(
-    *)
-      # 'set' quotes correctly as required by POSIX, so do not add quotes.
-      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
-      ;;
-    esac |
-    sort
-) |
+ac_cache_dump |
   sed '
      /^ac_cv_env_/b end
      t clear
@@ -24342,8 +24069,8 @@ printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;}
 if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
   if test -w "$cache_file"; then
     if test "x$cache_file" != "x/dev/null"; then
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
-printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf '%s\n' "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
 	cat confcache >"$cache_file"
       else
@@ -24357,8 +24084,8 @@ printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
       fi
     fi
   else
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
-printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
+    { printf '%s\n' "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf '%s\n' "$as_me: not updating unwritable cache $cache_file" >&6;}
   fi
 fi
 rm -f confcache
@@ -24375,7 +24102,7 @@ U=
 for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
   # 1. Remove the extension, and $U if already installed.
   ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
-  ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
+  ac_i=`printf '%s\n' "$ac_i" | sed "$ac_script"`
   # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
   #    will be set to the directory where LIBOBJS objects are built.
   as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
@@ -24394,13 +24121,21 @@ fi
 
 
 : "${CONFIG_STATUS=./config.status}"
+case $CONFIG_STATUS in #(
+  -*) :
+    CONFIG_STATUS=./$CONFIG_STATUS ;; #(
+  */*) :
+     ;; #(
+  *) :
+    CONFIG_STATUS=./$CONFIG_STATUS ;;
+esac
+
 ac_write_fail=0
-ac_clean_files_save=$ac_clean_files
-ac_clean_files="$ac_clean_files $CONFIG_STATUS"
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
-printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
+ac_clean_CONFIG_STATUS='"$CONFIG_STATUS"'
+{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf '%s\n' "$as_me: creating $CONFIG_STATUS" >&6;}
 as_write_fail=0
-cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+cat >"$CONFIG_STATUS" <<_ASEOF || as_write_fail=1
 #! $SHELL
 # Generated by $as_me.
 # Run this file to recreate the current configuration.
@@ -24414,7 +24149,7 @@ ac_cs_silent=false
 SHELL=\${CONFIG_SHELL-$SHELL}
 export SHELL
 _ASEOF
-cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ASEOF || as_write_fail=1
 ## -------------------- ##
 ## M4sh Initialization. ##
 ## -------------------- ##
@@ -24426,7 +24161,7 @@ then :
   emulate sh
   NULLCMD=:
   # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
-  # is contrary to our usage.  Disable this feature.
+  # contradicts POSIX and common usage.  Disable this.
   alias -g '${1+"$@"}'='"$@"'
   setopt NO_GLOB_SUBST
 else case e in #(
@@ -24513,7 +24248,7 @@ if test "x$as_myself" = x; then
   as_myself=$0
 fi
 if test ! -f "$as_myself"; then
-  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  printf '%s\n' "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
   exit 1
 fi
 
@@ -24529,9 +24264,9 @@ as_fn_error ()
   as_status=$1; test $as_status -eq 0 && as_status=1
   if test "$4"; then
     as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+    printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
   fi
-  printf "%s\n" "$as_me: error: $2" >&2
+  printf '%s\n' "$as_me: error: $2" >&2
   as_fn_exit $as_status
 } # as_fn_error
 
@@ -24626,7 +24361,7 @@ as_me=`$as_basename -- "$0" ||
 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
 	 X"$0" : 'X\(//\)$' \| \
 	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-printf "%s\n" X/"$0" |
+printf '%s\n' X/"$0" |
     sed '/^.*\/\([^/][^/]*\)\/*$/{
 	    s//\1/
 	    q
@@ -24648,29 +24383,6 @@ as_cr_Letters=$as_cr_letters$as_cr_LETTERS
 as_cr_digits='0123456789'
 as_cr_alnum=$as_cr_Letters$as_cr_digits
 
-
-# Determine whether it's possible to make 'echo' print without a newline.
-# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
-# for compatibility with existing Makefiles.
-ECHO_C= ECHO_N= ECHO_T=
-case `echo -n x` in #(((((
--n*)
-  case `echo 'xy\c'` in
-  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
-  xy)  ECHO_C='\c';;
-  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
-       ECHO_T='	';;
-  esac;;
-*)
-  ECHO_N='-n';;
-esac
-
-# For backward compatibility with old third-party macros, we provide
-# the shell variables $as_echo and $as_echo_n.  New code should use
-# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
-as_echo='printf %s\n'
-as_echo_n='printf %s'
-
 rm -f conf$$ conf$$.exe conf$$.file
 if test -d conf$$.dir; then
   rm -f conf$$.dir/conf$$.file
@@ -24712,7 +24424,7 @@ as_fn_mkdir_p ()
     as_dirs=
     while :; do
       case $as_dir in #(
-      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *\'*) as_qdir=`printf '%s\n' "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
       *) as_qdir=$as_dir;;
       esac
       as_dirs="'$as_qdir' $as_dirs"
@@ -24721,7 +24433,7 @@ $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
 	 X"$as_dir" : 'X\(//\)[^/]' \| \
 	 X"$as_dir" : 'X\(//\)$' \| \
 	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-printf "%s\n" X"$as_dir" |
+printf '%s\n' X"$as_dir" |
     sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
 	    s//\1/
 	    q
@@ -24774,19 +24486,19 @@ as_tr_sh="eval sed '$as_sed_sh'" # deprecated
 
 
 exec 6>&1
-## ----------------------------------- ##
-## Main body of $CONFIG_STATUS script. ##
-## ----------------------------------- ##
+## ------------------------------------- ##
+## Main body of "$CONFIG_STATUS" script. ##
+## ------------------------------------- ##
 _ASEOF
-test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+test $as_write_fail = 0 && chmod +x "$CONFIG_STATUS" || ac_write_fail=1
 
-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
 # Save the log message, to keep $0 and so on meaningful, and to
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Wine $as_me 11.10, which was
-generated by GNU Autoconf 2.72.  Invocation command line was
+This file was extended by Wine $as_me 11.11, which was
+generated by GNU Autoconf 2.73.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
@@ -24805,14 +24517,14 @@ case $ac_config_headers in *"
 esac
 
 
-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
 # Files that config.status was made for.
 config_headers="$ac_config_headers"
 config_commands="$ac_config_commands"
 
 _ACEOF
 
-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
 ac_cs_usage="\
 '$as_me' instantiates files and other configuration actions
 from templates according to the current configuration.  Unless the files
@@ -24840,25 +24552,29 @@ Report bugs to <wine-devel@winehq.org>.
 Wine home page: <https://www.winehq.org>."
 
 _ACEOF
-ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
-ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config=`printf '%s\n' "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf '%s\n' "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
+cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-Wine config.status 11.10
-configured by $0, generated by GNU Autoconf 2.72,
+Wine config.status 11.11
+configured by $0, generated by GNU Autoconf 2.73,
   with options \\"\$ac_cs_config\\"
 
-Copyright (C) 2023 Free Software Foundation, Inc.
+Copyright (C) 2026 Free Software Foundation, Inc.
 This config.status script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it."
 
 ac_pwd='$ac_pwd'
 srcdir='$srcdir'
-test -n "\$AWK" || AWK=awk
+test -n "\$AWK" || {
+  awk '' </dev/null ||
+    as_fn_error \$? "try installing gawk"
+  AWK=awk
+}
 _ACEOF
 
-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
 # The default lists apply if the user does not specify any file.
 ac_need_defaults=:
 while test $# != 0
@@ -24886,15 +24602,15 @@ do
   -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
     ac_cs_recheck=: ;;
   --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
-    printf "%s\n" "$ac_cs_version"; exit ;;
+    printf '%s\n' "$ac_cs_version"; exit ;;
   --config | --confi | --conf | --con | --co | --c )
-    printf "%s\n" "$ac_cs_config"; exit ;;
+    printf '%s\n' "$ac_cs_config"; exit ;;
   --debug | --debu | --deb | --de | --d | -d )
     debug=: ;;
   --header | --heade | --head | --hea )
     $ac_shift
     case $ac_optarg in
-    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *\'*) ac_optarg=`printf '%s\n' "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
     esac
     as_fn_append CONFIG_HEADERS " '$ac_optarg'"
     ac_need_defaults=false;;
@@ -24903,7 +24619,7 @@ do
     as_fn_error $? "ambiguous option: '$1'
 Try '$0 --help' for more information.";;
   --help | --hel | -h )
-    printf "%s\n" "$ac_cs_usage"; exit ;;
+    printf '%s\n' "$ac_cs_usage"; exit ;;
   -q | -quiet | --quiet | --quie | --qui | --qu | --q \
   | -silent | --silent | --silen | --sile | --sil | --si | --s)
     ac_cs_silent=: ;;
@@ -24927,29 +24643,29 @@ if $ac_cs_silent; then
 fi
 
 _ACEOF
-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
 if \$ac_cs_recheck; then
   set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
   shift
-  \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
+  \printf '%s\n' "running CONFIG_SHELL=$SHELL \$*" >&6
   CONFIG_SHELL='$SHELL'
   export CONFIG_SHELL
   exec "\$@"
 fi
 
 _ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
 exec 5>>config.log
 {
   echo
   sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
 ## Running $as_me. ##
 _ASBOX
-  printf "%s\n" "$ac_log"
+  printf '%s\n' "$ac_log"
 } >&5
 
 _ACEOF
-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
 #
 # INIT-COMMANDS
 #
@@ -25008,13 +24724,13 @@ libdir = $libdir
 localedir = $localedir
 mandir = $mandir
 DEFS = $DEFS
-ECHO_C = $ECHO_C
-ECHO_N = $ECHO_N
-ECHO_T = $ECHO_T
 LIBS = $LIBS
 build_alias = $build_alias
 host_alias = $host_alias
 target_alias = $target_alias
+ECHO_C = $ECHO_C
+ECHO_N = $ECHO_N
+ECHO_T = $ECHO_T
 system_dllpath = $system_dllpath
 build = $build
 build_cpu = $build_cpu
@@ -25185,10 +24901,10 @@ PNG_PE_CFLAGS = $PNG_PE_CFLAGS
 PNG_PE_LIBS = $PNG_PE_LIBS
 SQLITE3_PE_CFLAGS = $SQLITE3_PE_CFLAGS
 SQLITE3_PE_LIBS = $SQLITE3_PE_LIBS
+SYMCRYPT_PE_CFLAGS = $SYMCRYPT_PE_CFLAGS
+SYMCRYPT_PE_LIBS = $SYMCRYPT_PE_LIBS
 TIFF_PE_CFLAGS = $TIFF_PE_CFLAGS
 TIFF_PE_LIBS = $TIFF_PE_LIBS
-TOMCRYPT_PE_CFLAGS = $TOMCRYPT_PE_CFLAGS
-TOMCRYPT_PE_LIBS = $TOMCRYPT_PE_LIBS
 UNWIND_PE_CFLAGS = $UNWIND_PE_CFLAGS
 UNWIND_PE_LIBS = $UNWIND_PE_LIBS
 VKD3D_PE_CFLAGS = $VKD3D_PE_CFLAGS
@@ -25279,7 +24995,7 @@ _WINE_EOF
 
 _ACEOF
 
-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
 
 # Handling of arguments.
 for ac_config_target in $ac_config_targets
@@ -25399,9 +25115,9 @@ s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
 b cont
 ' <confdefs.h | sed '
 s/'"$ac_delim"'/"\\\
-"/g' >>$CONFIG_STATUS || ac_write_fail=1
+"/g' >>"$CONFIG_STATUS" || ac_write_fail=1
 
-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
   for (key in D) D_is_set[key] = 1
   FS = ""
 }
@@ -25419,8 +25135,12 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
   macro = mac2[1]
   prefix = substr(line, 1, index(line, defundef) - 1)
   if (D_is_set[macro]) {
+    suffix = P[macro] D[macro]
+    while (suffix ~ /[\t ]$/) {
+      suffix = substr(suffix, 1, length(suffix) - 1)
+    }
     # Preserve the white space surrounding the "#".
-    print prefix "define", macro P[macro] D[macro]
+    print prefix "define", macro suffix
     next
   } else {
     # Replace #undef with comments.  This is necessary, for example,
@@ -25435,7 +25155,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 { print }
 _ACAWK
 _ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
   as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
 fi # test -n "$CONFIG_HEADERS"
 
@@ -25479,7 +25199,7 @@ do
 	   esac ||
 	   as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;;
       esac
-      case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      case $ac_f in *\'*) ac_f=`printf '%s\n' "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
       as_fn_append ac_file_inputs " '$ac_f'"
     done
 
@@ -25487,17 +25207,17 @@ do
     # use $as_me), people would be surprised to read:
     #    /* config.h.  Generated by config.status.  */
     configure_input='Generated from '`
-	  printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	  printf '%s\n' "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
 	`' by configure.'
     if test x"$ac_file" != x-; then
       configure_input="$ac_file.  $configure_input"
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
-printf "%s\n" "$as_me: creating $ac_file" >&6;}
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf '%s\n' "$as_me: creating $ac_file" >&6;}
     fi
     # Neutralize special characters interpreted by sed in replacement strings.
     case $configure_input in #(
     *\&* | *\|* | *\\* )
-       ac_sed_conf_input=`printf "%s\n" "$configure_input" |
+       ac_sed_conf_input=`printf '%s\n' "$configure_input" |
        sed 's/[\\\\&|]/\\\\&/g'`;; #(
     *) ac_sed_conf_input=$configure_input;;
     esac
@@ -25514,7 +25234,7 @@ $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
 	 X"$ac_file" : 'X\(//\)[^/]' \| \
 	 X"$ac_file" : 'X\(//\)$' \| \
 	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
-printf "%s\n" X"$ac_file" |
+printf '%s\n' X"$ac_file" |
     sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
 	    s//\1/
 	    q
@@ -25538,9 +25258,9 @@ printf "%s\n" X"$ac_file" |
 case "$ac_dir" in
 .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
 *)
-  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  ac_dir_suffix=/`printf '%s\n' "$ac_dir" | sed 's|^\.[\\/]||'`
   # A ".." for each directory in $ac_dir_suffix.
-  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  ac_top_builddir_sub=`printf '%s\n' "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
   case $ac_top_builddir_sub in
   "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
   *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
@@ -25576,27 +25296,27 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
   #
   if test x"$ac_file" != x-; then
     {
-      printf "%s\n" "/* $configure_input  */" >&1 \
+      printf '%s\n' "/* $configure_input  */" >&1 \
       && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
     } >"$ac_tmp/config.h" \
       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
     if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
-      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
-printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
+      { printf '%s\n' "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf '%s\n' "$as_me: $ac_file is unchanged" >&6;}
     else
       rm -f "$ac_file"
       mv "$ac_tmp/config.h" "$ac_file" \
 	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
     fi
   else
-    printf "%s\n" "/* $configure_input  */" >&1 \
+    printf '%s\n' "/* $configure_input  */" >&1 \
       && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
       || as_fn_error $? "could not create -" "$LINENO" 5
   fi
  ;;
 
-  :C)  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
-printf "%s\n" "$as_me: executing $ac_file commands" >&6;}
+  :C)  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+printf '%s\n' "$as_me: executing $ac_file commands" >&6;}
  ;;
   esac
 
@@ -25612,7 +25332,7 @@ done # for ac_tag
 
 as_fn_exit 0
 _ACEOF
-ac_clean_files=$ac_clean_files_save
+ac_clean_CONFIG_STATUS=
 
 test $ac_write_fail = 0 ||
   as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
@@ -25628,19 +25348,26 @@ test $ac_write_fail = 0 ||
 # need to make the FD available again.
 if test "$no_create" != yes; then
   ac_cs_success=:
+  case $CONFIG_STATUS in #(
+  -*) :
+    ac_no_opts=-- ;; #(
+  *) :
+    ac_no_opts= ;;
+esac
   ac_config_status_args=
   test "$silent" = yes &&
     ac_config_status_args="$ac_config_status_args --quiet"
   exec 5>/dev/null
-  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  $SHELL $ac_no_opts "$CONFIG_STATUS" $ac_config_status_args ||
+    ac_cs_success=false
   exec 5>>config.log
   # Use ||, not &&, to avoid exiting from the if with $? = 1, which
   # would make configure fail if this is the last instruction.
   $ac_cs_success || as_fn_exit 1
 fi
 if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
-printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+  { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf '%s\n' "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
 fi
 
 
@@ -25659,8 +25386,8 @@ if test "x$wine_notices" != x; then
 then :
 
 else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $msg" >&5
-printf "%s\n" "$as_me: $msg" >&6;} ;;
+  e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: $msg" >&5
+printf '%s\n' "$as_me: $msg" >&6;} ;;
 esac
 fi
     done
@@ -25673,14 +25400,14 @@ then :
 
 else case e in #(
   e) echo >&2
-        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $msg" >&5
-printf "%s\n" "$as_me: WARNING: $msg" >&2;} ;;
+        { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: $msg" >&5
+printf '%s\n' "$as_me: WARNING: $msg" >&2;} ;;
 esac
 fi
 done
 IFS="$ac_save_IFS"
 
-printf "%s\n" "
+printf '%s\n' "
 $as_me: Finished.  Do '${MAKE-make}' to compile Wine.
 " >&6
 
diff --git a/configure.ac b/configure.ac
index 60262f26f68c..d597bad37504 100644
--- a/configure.ac
+++ b/configure.ac
@@ -629,6 +629,7 @@ This is an error since --enable-archs=$wine_arch was requested.])])
       i386) WINE_TRY_PE_CFLAGS([-fno-omit-frame-pointer])
             WINE_TRY_PE_CFLAGS([-mpreferred-stack-boundary=2])
             WINE_TRY_PE_CFLAGS([-mlong-double-64])
+            WINE_TRY_PE_CFLAGS([-msse2])
             WINE_TRY_PE_CFLAGS([-Wl,--disable-stdcall-fixup],
                                [AS_VAR_APPEND([${wine_arch}_LDFLAGS],[" -Wl,--disable-stdcall-fixup"])]) ;;
       x86_64) WINE_TRY_PE_CFLAGS([-Wformat-overflow])
@@ -1219,15 +1220,6 @@ then
     then
         WINE_NOTICE([libvkd3d ${notice_platform}MinGW development files not found (or too old); using bundled version.])
     fi
-
-    WINE_MINGW_PACKAGE_FLAGS(ZLIB,[zlib],[-lz],
-        [WINE_CHECK_MINGW_HEADER(zlib.h,
-            [WINE_CHECK_MINGW_LIB(z,inflate,[:],[ZLIB_PE_CFLAGS=""; ZLIB_PE_LIBS=""],[$ZLIB_PE_LIBS])],
-            [ZLIB_PE_CFLAGS=""; ZLIB_PE_LIBS=""])])
-    if test "x$ZLIB_PE_LIBS" = "x"
-    then
-        WINE_NOTICE([zlib ${notice_platform}MinGW development files not found; using bundled version.])
-    fi
 fi
 
 WINE_EXTLIB_FLAGS(CAPSTONE, capstone, capstone, "-I\$(top_srcdir)/libs/capstone/include")
@@ -1246,8 +1238,8 @@ WINE_EXTLIB_FLAGS(MPG123, mpg123, mpg123, "-I\$(top_srcdir)/libs/mpg123/src/incl
 WINE_EXTLIB_FLAGS(MUSL, musl, musl)
 WINE_EXTLIB_FLAGS(PNG, png, "png \$(ZLIB_PE_LIBS)", "-I\$(top_srcdir)/libs/png")
 WINE_EXTLIB_FLAGS(SQLITE3, sqlite3, sqlite3)
+WINE_EXTLIB_FLAGS(SYMCRYPT, symcrypt, symcrypt, "-I\$(top_srcdir)/libs/symcrypt/inc")
 WINE_EXTLIB_FLAGS(TIFF, tiff, "tiff \$(ZLIB_PE_LIBS)", "-I\$(top_srcdir)/libs/tiff/libtiff")
-WINE_EXTLIB_FLAGS(TOMCRYPT, tomcrypt, tomcrypt, "-I\$(top_srcdir)/libs/tomcrypt/src/headers -DLTC_NO_PROTOTYPES -DLTC_SOURCE")
 WINE_EXTLIB_FLAGS(UNWIND, unwind, unwind, "-I\$(top_srcdir)/libs/unwind/include")
 WINE_EXTLIB_FLAGS(VKD3D, vkd3d, vkd3d, "-I\$(top_srcdir)/libs/vkd3d/include")
 WINE_EXTLIB_FLAGS(XML2, xml2, xml2, "-I\$(top_srcdir)/libs/xml2/include -DLIBXML_STATIC")
@@ -3541,8 +3533,8 @@ WINE_CONFIG_MAKEFILE(libs/sqlite3)
 WINE_CONFIG_MAKEFILE(libs/strmbase)
 WINE_CONFIG_MAKEFILE(libs/strmiids)
 WINE_CONFIG_MAKEFILE(libs/strsafe)
+WINE_CONFIG_MAKEFILE(libs/symcrypt)
 WINE_CONFIG_MAKEFILE(libs/tiff)
-WINE_CONFIG_MAKEFILE(libs/tomcrypt)
 WINE_CONFIG_MAKEFILE(libs/unwind)
 WINE_CONFIG_MAKEFILE(libs/uuid)
 WINE_CONFIG_MAKEFILE(libs/vkd3d)
diff --git a/dlls/atl/atl_ax.c b/dlls/atl/atl_ax.c
index 4ff0a796d3b4..3bf2ce662e48 100644
--- a/dlls/atl/atl_ax.c
+++ b/dlls/atl/atl_ax.c
@@ -55,6 +55,9 @@ typedef struct IOCS {
     BOOL fActive, fInPlace, fWindowless;
 } IOCS;
 
+static UINT wmAtlGetHost = 0;
+static UINT wmAtlGetControl = 0;
+
 /**********************************************************************
  * AtlAxWin class window procedure
  */
@@ -71,6 +74,18 @@ static LRESULT CALLBACK AtlAxWin_wndproc( HWND hWnd, UINT wMsg, WPARAM wParam, L
             free( ptr );
             return 0;
     }
+    if ( wMsg == wmAtlGetControl )
+    {
+        IUnknown *control = NULL;
+        AtlAxGetControl( hWnd, &control );
+        return (LRESULT)control;
+    }
+    if ( wMsg == wmAtlGetHost )
+    {
+        IUnknown *host = NULL;
+        AtlAxGetHost( hWnd, &host );
+        return (LRESULT)host;
+    }
     return DefWindowProcW( hWnd, wMsg, wParam, lParam );
 }
 
@@ -132,6 +147,11 @@ BOOL WINAPI AtlAxWinInit(void)
             return FALSE;
     }
 
+    if (!wmAtlGetControl)
+        wmAtlGetControl = RegisterWindowMessageW( L"WM_ATLGETCONTROL" );
+    if (!wmAtlGetHost)
+        wmAtlGetHost = RegisterWindowMessageW( L"WM_ATLGETHOST" );
+
     return TRUE;
 }
 
diff --git a/dlls/atl/tests/atl_ax.c b/dlls/atl/tests/atl_ax.c
index 26e4ea5a54d2..3cfef085ae2f 100644
--- a/dlls/atl/tests/atl_ax.c
+++ b/dlls/atl/tests/atl_ax.c
@@ -128,6 +128,50 @@ static void test_ax_win(void)
     ok(wcex.style == CS_GLOBALCLASS, "wcex.style %08x\n", wcex.style);
 }
 
+static void test_atl_messages(void)
+{
+    UINT wmAtlGetHost;
+    UINT wmAtlGetControl;
+    HWND hwnd;
+    HRESULT ret;
+    IUnknown *host1 = NULL, *host2;
+    IUnknown *ctrl1 = NULL, *ctrl2;
+
+    /* Already called by test_ax_win, but left in to allow this test to be standalone */
+    AtlAxWinInit();
+
+    wmAtlGetHost = RegisterWindowMessageW( L"WM_ATLGETHOST" );
+    wmAtlGetControl = RegisterWindowMessageW( L"WM_ATLGETCONTROL" );
+
+    hwnd = CreateWindowExW(0, L"AtlAxWin", L"Shell.Explorer", 0,
+                           CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+                           CW_USEDEFAULT, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "Failed to create AtlAxWin window\n");
+
+    ret = AtlAxGetHost(hwnd, &host1);
+    ok(ret == S_OK, "AtlAxGetHost failed\n");
+    ok(host1 != NULL, "AtlAxGetHost did not fill the out pointer\n");
+
+    ret = AtlAxGetControl(hwnd, &ctrl1);
+    ok(ret == S_OK, "AtlAxGetControl failed\n");
+    ok(ctrl1 != NULL, "AtlAxGetControl did not fill the out pointer\n");
+
+    host2 = (IUnknown *)SendMessageW(hwnd, wmAtlGetHost, 0, 0);
+    ok(host2 != NULL, "WM_ATLGETHOST did not return a value\n");
+    ctrl2 = (IUnknown *)SendMessageW(hwnd, wmAtlGetControl, 0, 0);
+    ok(ctrl2 != NULL, "WM_ATLGETCONTROL did not return a value\n");
+
+    ok(host1 == host2, "Mismatch between AtlAxGetHost and WM_ATLGETHOST\n");
+    ok(ctrl1 == ctrl2, "Mismatch between AtlAxGetControl and WM_ATLGETCONTROL\n");
+
+    IUnknown_Release(host1);
+    IUnknown_Release(ctrl1);
+    IUnknown_Release(host2);
+    IUnknown_Release(ctrl2);
+
+    DestroyWindow(hwnd);
+}
+
 START_TEST(atl_ax)
 {
     init_function_pointers();
@@ -143,6 +187,7 @@ START_TEST(atl_ax)
         win_skip("AtlAxAttachControl is not available\n");
 
     test_ax_win();
+    test_atl_messages();
 
     CoUninitialize();
 }
diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in
index 6b4a95d9e3c2..9cbbc529104a 100644
--- a/dlls/bcrypt/Makefile.in
+++ b/dlls/bcrypt/Makefile.in
@@ -1,13 +1,10 @@
 MODULE    = bcrypt.dll
 IMPORTLIB = bcrypt
-IMPORTS   = $(TOMCRYPT_PE_LIBS) advapi32
-EXTRAINCL = $(TOMCRYPT_PE_CFLAGS)
-UNIXLIB   = bcrypt.so
-UNIX_CFLAGS = $(GNUTLS_CFLAGS)
+IMPORTS   = $(SYMCRYPT_PE_LIBS) advapi32
+EXTRAINCL = $(SYMCRYPT_PE_CFLAGS)
 
 VER_FILEDESCRIPTION_STR = "Wine Crypto Library"
 VER_PRODUCTVERSION      = 6,0,6000,16386
 
 SOURCES = \
-	bcrypt_main.c \
-	gnutls.c
+	bcrypt_main.c
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h
deleted file mode 100644
index 49c2ede69037..000000000000
--- a/dlls/bcrypt/bcrypt_internal.h
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright 2016 Michael Müller
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- */
-
-#ifndef __BCRYPT_INTERNAL_H
-#define __BCRYPT_INTERNAL_H
-
-#include <stdarg.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "winternl.h"
-#include "wincrypt.h"
-#include "bcrypt.h"
-#include "wine/unixlib.h"
-
-#define MAGIC_DSS1 ('D' | ('S' << 8) | ('S' << 16) | ('1' << 24))
-#define MAGIC_DSS2 ('D' | ('S' << 8) | ('S' << 16) | ('2' << 24))
-
-#define MAGIC_ALG  (('A' << 24) | ('L' << 16) | ('G' << 8) | '0')
-#define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H')
-#define MAGIC_KEY  (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0')
-#define MAGIC_SECRET (('S' << 24) | ('C' << 16) | ('R' << 8) | 'T')
-struct object
-{
-    ULONG magic;
-};
-
-enum alg_id
-{
-    /* cipher */
-    ALG_ID_3DES,
-    ALG_ID_AES,
-    ALG_ID_RC4,
-
-    /* hash */
-    ALG_ID_SHA256,
-    ALG_ID_SHA384,
-    ALG_ID_SHA512,
-    ALG_ID_SHA1,
-    ALG_ID_MD5,
-    ALG_ID_MD4,
-    ALG_ID_MD2,
-
-    /* asymmetric encryption */
-    ALG_ID_RSA,
-
-    /* secret agreement */
-    ALG_ID_DH,
-    ALG_ID_ECDH,
-    ALG_ID_ECDH_P256,
-    ALG_ID_ECDH_P384,
-    ALG_ID_ECDH_P521,
-
-    /* signature */
-    ALG_ID_RSA_SIGN,
-    ALG_ID_ECDSA,
-    ALG_ID_ECDSA_P256,
-    ALG_ID_ECDSA_P384,
-    ALG_ID_ECDSA_P521,
-    ALG_ID_DSA,
-
-    /* rng */
-    ALG_ID_RNG,
-
-    /* key derivation */
-    ALG_ID_PBKDF2,
-};
-
-enum chain_mode
-{
-    CHAIN_MODE_CBC,
-    CHAIN_MODE_ECB,
-    CHAIN_MODE_CFB,
-    CHAIN_MODE_CCM,
-    CHAIN_MODE_GCM,
-};
-
-enum ecc_curve_id
-{
-    ECC_CURVE_NONE,
-    ECC_CURVE_25519,
-    ECC_CURVE_P256R1,
-    ECC_CURVE_P384R1,
-    ECC_CURVE_P521R1,
-};
-
-struct algorithm
-{
-    struct object     hdr;
-    enum alg_id       id;
-    enum chain_mode   mode;
-    unsigned          flags;
-    enum ecc_curve_id curve_id;
-};
-
-struct key_symmetric
-{
-    enum chain_mode  mode;
-    ULONG            block_size;
-    UCHAR           *vector;
-    ULONG            vector_len;
-    UCHAR           *secret;
-    unsigned         secret_len;
-    CRITICAL_SECTION cs;
-};
-
-#define KEY_FLAG_LEGACY_DSA_V2  0x00000001
-#define KEY_FLAG_FINALIZED      0x00000002
-
-struct key_asymmetric
-{
-    ULONG             bitlen;     /* key strength for ECC keys */
-    enum ecc_curve_id curve_id;
-    unsigned          flags;
-    DSSSEED           dss_seed;
-};
-
-#define PRIVATE_DATA_SIZE 3
-struct key
-{
-    struct object hdr;
-    enum alg_id   alg_id;
-    UINT64        private[PRIVATE_DATA_SIZE];  /* private data for backend */
-    union
-    {
-        struct key_symmetric s;
-        struct key_asymmetric a;
-    } u;
-};
-
-struct secret
-{
-    struct object hdr;
-    UCHAR        *derived_key;
-    ULONG         derived_key_len;
-};
-
-struct key_symmetric_set_auth_data_params
-{
-    struct key  *key;
-    UCHAR       *auth_data;
-    ULONG        len;
-};
-
-struct key_symmetric_encrypt_params
-{
-    struct key  *key;
-    const UCHAR *input;
-    unsigned     input_len;
-    UCHAR       *output;
-    ULONG        output_len;
-};
-
-struct key_symmetric_decrypt_params
-{
-    struct key  *key;
-    const UCHAR *input;
-    unsigned     input_len;
-    UCHAR       *output;
-    ULONG        output_len;
-};
-
-struct key_symmetric_get_tag_params
-{
-    struct key  *key;
-    UCHAR       *tag;
-    ULONG        len;
-};
-
-struct key_asymmetric_decrypt_params
-{
-    struct key  *key;
-    UCHAR       *input;
-    unsigned     input_len;
-    void        *padding;
-    UCHAR       *output;
-    ULONG        output_len;
-    ULONG       *ret_len;
-    ULONG        flags;
-};
-
-struct key_asymmetric_encrypt_params
-{
-    struct key  *key;
-    UCHAR       *input;
-    unsigned     input_len;
-    void        *padding;
-    UCHAR       *output;
-    ULONG        output_len;
-    ULONG       *ret_len;
-    ULONG        flags;
-};
-
-struct key_asymmetric_duplicate_params
-{
-    struct key *key_orig;
-    struct key *key_copy;
-};
-
-struct key_asymmetric_sign_params
-{
-    struct key  *key;
-    void        *padding;
-    UCHAR       *input;
-    unsigned     input_len;
-    UCHAR       *output;
-    ULONG        output_len;
-    ULONG       *ret_len;
-    unsigned     flags;
-};
-
-struct key_asymmetric_verify_params
-{
-    struct key *key;
-    void       *padding;
-    UCHAR      *hash;
-    unsigned    hash_len;
-    UCHAR      *signature;
-    ULONG       signature_len;
-    unsigned    flags;
-};
-
-#define KEY_EXPORT_FLAG_PUBLIC        0x00000001
-#define KEY_EXPORT_FLAG_RSA_FULL      0x00000002
-#define KEY_EXPORT_FLAG_DH_PARAMETERS 0x00000004
-
-struct key_asymmetric_export_params
-{
-    struct key  *key;
-    ULONG        flags;
-    UCHAR       *buf;
-    ULONG        len;
-    ULONG       *ret_len;
-};
-
-#define KEY_IMPORT_FLAG_PUBLIC        0x00000001
-#define KEY_IMPORT_FLAG_DH_PARAMETERS 0x00000002
-
-struct key_asymmetric_import_params
-{
-    struct key  *key;
-    ULONG        flags;
-    UCHAR       *buf;
-    ULONG        len;
-};
-
-struct key_asymmetric_derive_key_params
-{
-    struct key *privkey;
-    struct key *pubkey;
-    UCHAR      *output;
-    ULONG       output_len;
-    ULONG      *ret_len;
-};
-
-enum key_funcs
-{
-    unix_process_attach,
-    unix_process_detach,
-    unix_key_symmetric_vector_reset,
-    unix_key_symmetric_set_auth_data,
-    unix_key_symmetric_encrypt,
-    unix_key_symmetric_decrypt,
-    unix_key_symmetric_get_tag,
-    unix_key_symmetric_destroy,
-    unix_key_asymmetric_generate,
-    unix_key_asymmetric_decrypt,
-    unix_key_asymmetric_encrypt,
-    unix_key_asymmetric_duplicate,
-    unix_key_asymmetric_sign,
-    unix_key_asymmetric_verify,
-    unix_key_asymmetric_destroy,
-    unix_key_asymmetric_export,
-    unix_key_asymmetric_import,
-    unix_key_asymmetric_derive_key,
-    unix_funcs_count,
-};
-
-static inline ULONG len_from_bitlen( ULONG bitlen )
-{
-    return (bitlen + 7) / 8;
-}
-
-#endif /* __BCRYPT_INTERNAL_H */
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 076044b5cef3..3ca369538b29 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -28,15 +28,259 @@
 #include "wincrypt.h"
 #include "winternl.h"
 #include "bcrypt.h"
-#include "tomcrypt.h"
 
+#include "symcrypt.h"
 #include "wine/debug.h"
-#include "bcrypt_internal.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(bcrypt);
 
-#define UNIX_CALL( func, params ) WINE_UNIX_CALL( unix_ ## func, params )
+SYMCRYPT_ENVIRONMENT_DEFS( WindowsUsermodeWin8_1nLater );
 
+#define MAGIC_DSS1 ('D' | ('S' << 8) | ('S' << 16) | ('1' << 24))
+#define MAGIC_DSS2 ('D' | ('S' << 8) | ('S' << 16) | ('2' << 24))
+
+#define MAGIC_ALG  (('A' << 24) | ('L' << 16) | ('G' << 8) | '0')
+#define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H')
+#define MAGIC_KEY  (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0')
+#define MAGIC_SECRET (('S' << 24) | ('C' << 16) | ('R' << 8) | 'T')
+struct object
+{
+    ULONG magic;
+};
+
+enum alg_id
+{
+    /* cipher */
+    ALG_ID_3DES,
+    ALG_ID_CHACHA20_POLY1305,
+    ALG_ID_AES,
+    ALG_ID_RC4,
+
+    /* hash */
+    ALG_ID_SHA256,
+    ALG_ID_SHA384,
+    ALG_ID_SHA512,
+    ALG_ID_SHA1,
+    ALG_ID_MD5,
+    ALG_ID_MD4,
+    ALG_ID_MD2,
+
+    /* asymmetric encryption */
+    ALG_ID_RSA,
+
+    /* secret agreement */
+    ALG_ID_DH,
+    ALG_ID_ECDH,
+    ALG_ID_ECDH_P256,
+    ALG_ID_ECDH_P384,
+    ALG_ID_ECDH_P521,
+
+    /* signature */
+    ALG_ID_RSA_SIGN,
+    ALG_ID_ECDSA,
+    ALG_ID_ECDSA_P256,
+    ALG_ID_ECDSA_P384,
+    ALG_ID_ECDSA_P521,
+    ALG_ID_DSA,
+
+    /* rng */
+    ALG_ID_RNG,
+
+    /* key derivation */
+    ALG_ID_PBKDF2,
+};
+
+#define HASH_FLAG_HMAC      0x01
+#define HASH_FLAG_REUSABLE  0x02
+struct hash
+{
+    struct object hdr;
+    enum alg_id   alg_id;
+    ULONG         len;
+    ULONG         flags;
+    UCHAR        *secret;
+    ULONG         secret_len;
+
+    const SYMCRYPT_HASH       *desc;
+    SYMCRYPT_HASH_STATE        state;
+    SYMCRYPT_HMAC_EXPANDED_KEY hmac;
+    SYMCRYPT_HMAC_STATE        hmac_state;
+};
+
+enum chain_mode
+{
+    CHAIN_MODE_CBC,
+    CHAIN_MODE_ECB,
+    CHAIN_MODE_CFB,
+    CHAIN_MODE_CCM,
+    CHAIN_MODE_GCM,
+};
+
+enum ecc_curve_id
+{
+    ECC_CURVE_NONE,
+    ECC_CURVE_25519,
+    ECC_CURVE_BRAINPOOLP256R1,
+    ECC_CURVE_BRAINPOOLP384R1,
+    ECC_CURVE_P256R1,
+    ECC_CURVE_P384R1,
+    ECC_CURVE_P521R1,
+};
+
+struct algorithm
+{
+    struct object     hdr;
+    enum alg_id       id;
+    enum chain_mode   mode;
+    ULONG             flags;
+    enum ecc_curve_id curve_id;
+};
+
+struct aes_key
+{
+    SYMCRYPT_AES_EXPANDED_KEY handle;
+};
+
+struct gcm_key
+{
+    SYMCRYPT_GCM_EXPANDED_KEY handle;
+    SYMCRYPT_GCM_STATE        state;
+};
+
+#define BLOCK_LENGTH_RC4    1
+#define BLOCK_LENGTH_CHACHA20_POLY1305 1
+#define BLOCK_LENGTH_3DES   8
+#define BLOCK_LENGTH_AES   16
+
+struct symmetric_key
+{
+    enum chain_mode  mode;
+    ULONG            block_size;
+    UCHAR           *secret;
+    ULONG            secret_len;
+    UCHAR            vector[BLOCK_LENGTH_AES];
+    CRITICAL_SECTION cs;
+    union
+    {
+        struct aes_key aes;
+        struct gcm_key gcm;
+    };
+};
+
+struct rsa_key
+{
+    SYMCRYPT_RSAKEY *handle;
+};
+
+struct ecc_key
+{
+    SYMCRYPT_ECKEY   *handle;
+    SYMCRYPT_ECURVE  *curve;
+    enum ecc_curve_id curve_id;
+};
+
+struct dsa_key
+{
+    SYMCRYPT_DLKEY   *handle;
+    SYMCRYPT_DLGROUP *group;
+    UCHAR             seed[20];
+    UINT32            count;
+};
+
+struct asymmetric_key
+{
+    ULONG bitlen;     /* key strength for ECC keys */
+    union
+    {
+        struct rsa_key rsa;
+        struct ecc_key ecc;
+        struct dsa_key dsa;
+    };
+};
+
+#define KEY_FLAG_PRIVATE    0x00000001
+#define KEY_FLAG_FINALIZED  0x00000002
+
+struct key
+{
+    struct object hdr;
+    enum alg_id   alg_id;
+    ULONG         flags;
+    union
+    {
+        struct symmetric_key s;
+        struct asymmetric_key a;
+    };
+};
+
+#define KEY_EXPORT_FLAG_PRIVATE     0x00000001
+#define KEY_EXPORT_FLAG_RSA_FULL    0x00000002
+
+struct secret
+{
+    struct object hdr;
+    UCHAR        *derived_key;
+    ULONG         derived_key_len;
+    struct key   *privkey;
+    struct key   *pubkey;
+};
+
+void * SYMCRYPT_CALL SymCryptCallbackAlloc( SIZE_T size )
+{
+    return _aligned_malloc( size, SYMCRYPT_ASYM_ALIGN_VALUE );
+}
+
+void SYMCRYPT_CALL SymCryptCallbackFree( void *ptr )
+{
+    _aligned_free( ptr );
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptCallbackRandom( BYTE *buf, SIZE_T size )
+{
+    return RtlGenRandom( buf, size ) ? SYMCRYPT_NO_ERROR : SYMCRYPT_EXTERNAL_FAILURE;
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptRsaSignVerifyPct( const SYMCRYPT_RSAKEY *key )
+{
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptDsaPct( const SYMCRYPT_DLKEY *key )
+{
+    return SYMCRYPT_NO_ERROR;
+}
+
+void SYMCRYPT_CALL SymCryptRsaSelftest( void )
+{
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptEcDsaPct( const SYMCRYPT_ECKEY *key )
+{
+    return SYMCRYPT_NO_ERROR;
+}
+
+void SYMCRYPT_CALL SymCryptDsaSelftest( void )
+{
+}
+
+void SYMCRYPT_CALL SymCryptEcDsaSelftest( void )
+{
+}
+
+void SYMCRYPT_CALL SymCryptDhSecretAgreementSelftest( void )
+{
+}
+
+void SYMCRYPT_CALL SymCryptEcDhSecretAgreementSelftest( void )
+{
+}
+
+UINT32 g_SymCryptFipsSelftestsPerformed;
+
+static inline ULONG len_from_bitlen( ULONG bitlen )
+{
+    return (bitlen + 7) / 8;
+}
 
 NTSTATUS WINAPI BCryptAddContextFunction( ULONG table, const WCHAR *ctx, ULONG iface, const WCHAR *func, ULONG pos )
 {
@@ -100,10 +344,12 @@ static const struct
     ULONG        object_length;
     ULONG        hash_length;
     ULONG        block_bits;
+    enum ecc_curve_id curve_id;
 }
 builtin_algorithms[] =
 {
     {  BCRYPT_3DES_ALGORITHM,       BCRYPT_CIPHER_INTERFACE,                522,    0,    0 },
+    {  BCRYPT_CHACHA20_POLY1305_ALGORITHM, BCRYPT_CIPHER_INTERFACE,         166,    0,    0 },
     {  BCRYPT_AES_ALGORITHM,        BCRYPT_CIPHER_INTERFACE,                654,    0,    0 },
     {  BCRYPT_RC4_ALGORITHM,        BCRYPT_CIPHER_INTERFACE,                654,    0,    0 },
     {  BCRYPT_SHA256_ALGORITHM,     BCRYPT_HASH_INTERFACE,                  286,   32,  512 },
@@ -116,38 +362,29 @@ builtin_algorithms[] =
     {  BCRYPT_RSA_ALGORITHM,        BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE, 0,      0,    0 },
     {  BCRYPT_DH_ALGORITHM,         BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0 },
     {  BCRYPT_ECDH_ALGORITHM,       BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0 },
-    {  BCRYPT_ECDH_P256_ALGORITHM,  BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0 },
-    {  BCRYPT_ECDH_P384_ALGORITHM,  BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0 },
-    {  BCRYPT_ECDH_P521_ALGORITHM,  BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0 },
+    {  BCRYPT_ECDH_P256_ALGORITHM,  BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0, ECC_CURVE_P256R1 },
+    {  BCRYPT_ECDH_P384_ALGORITHM,  BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0, ECC_CURVE_P384R1 },
+    {  BCRYPT_ECDH_P521_ALGORITHM,  BCRYPT_SECRET_AGREEMENT_INTERFACE,      0,      0,    0, ECC_CURVE_P521R1 },
     {  BCRYPT_RSA_SIGN_ALGORITHM,   BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0 },
     {  BCRYPT_ECDSA_ALGORITHM,      BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0 },
-    {  BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0 },
-    {  BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0 },
-    {  BCRYPT_ECDSA_P521_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0 },
+    {  BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0, ECC_CURVE_P256R1 },
+    {  BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0, ECC_CURVE_P384R1 },
+    {  BCRYPT_ECDSA_P521_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0, ECC_CURVE_P521R1 },
     {  BCRYPT_DSA_ALGORITHM,        BCRYPT_SIGNATURE_INTERFACE,             0,      0,    0 },
     {  BCRYPT_RNG_ALGORITHM,        BCRYPT_RNG_INTERFACE,                   0,      0,    0 },
     {  BCRYPT_PBKDF2_ALGORITHM,     BCRYPT_KEY_DERIVATION_INTERFACE,      618,      0,    0 },
 };
 
-static inline BOOL is_symmetric_key( const struct key *key )
-{
-    return builtin_algorithms[key->alg_id].class == BCRYPT_CIPHER_INTERFACE
-        || builtin_algorithms[key->alg_id].class == BCRYPT_KEY_DERIVATION_INTERFACE;
-}
-
-static inline BOOL is_asymmetric_encryption_key( struct key *key )
-{
-    return builtin_algorithms[key->alg_id].class == BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE;
-}
-
-static inline BOOL is_agreement_key( struct key *key )
+static inline BOOL is_symmetric_alg( const struct algorithm *alg )
 {
-    return builtin_algorithms[key->alg_id].class == BCRYPT_SECRET_AGREEMENT_INTERFACE;
+    return builtin_algorithms[alg->id].class == BCRYPT_CIPHER_INTERFACE
+        || builtin_algorithms[alg->id].class == BCRYPT_KEY_DERIVATION_INTERFACE;
 }
 
-static inline BOOL is_signature_key( struct key *key )
+static inline BOOL is_symmetric_key( const struct key *key )
 {
-    return builtin_algorithms[key->alg_id].class == BCRYPT_SIGNATURE_INTERFACE || key->alg_id == ALG_ID_RSA;
+    return builtin_algorithms[key->alg_id].class == BCRYPT_CIPHER_INTERFACE
+        || builtin_algorithms[key->alg_id].class == BCRYPT_KEY_DERIVATION_INTERFACE;
 }
 
 static BOOL match_operation_type( ULONG type, ULONG class )
@@ -258,6 +495,13 @@ static const struct algorithm pseudo_algorithms[] =
     {{ MAGIC_ALG }, ALG_ID_RSA_SIGN },
     {{ 0 }}, /* CAPI_KDF */
     {{ MAGIC_ALG }, ALG_ID_PBKDF2 },
+    {{ 0 }}, /* SP800108_CTR_HMAC */
+    {{ 0 }}, /* SP80056A_CONCAT */
+    {{ 0 }}, /* TLS1_1_KDF */
+    {{ 0 }}, /* TLS1_2_KDF */
+    {{ 0 }}, /* XTS_AES */
+    {{ 0 }}, /* HKDF */
+    {{ MAGIC_ALG }, ALG_ID_CHACHA20_POLY1305 },
 };
 
 /* Algorithm pseudo-handles are denoted by having the lowest bit set.
@@ -313,7 +557,7 @@ static inline struct secret *get_secret_object( BCRYPT_SECRET_HANDLE handle )
 NTSTATUS WINAPI BCryptGenRandom( BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG count, ULONG flags )
 {
     const DWORD supported_flags = BCRYPT_USE_SYSTEM_PREFERRED_RNG;
-    struct algorithm *alg = get_alg_object( handle );
+    const struct algorithm *alg = get_alg_object( handle );
 
     TRACE("%p, %p, %lu, %#lx - semi-stub\n", handle, buffer, count, flags);
 
@@ -342,9 +586,7 @@ NTSTATUS WINAPI BCryptGenRandom( BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG
     if (alg)
         FIXME("ignoring selected algorithm\n");
 
-    /* When zero bytes are requested the function returns success too. */
-    if (!count)
-        return STATUS_SUCCESS;
+    if (!count) return STATUS_SUCCESS;
 
     if (alg || (flags & BCRYPT_USE_SYSTEM_PREFERRED_RNG))
     {
@@ -355,14 +597,14 @@ NTSTATUS WINAPI BCryptGenRandom( BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static struct algorithm *create_algorithm( enum alg_id id, enum chain_mode mode, DWORD flags )
+static struct algorithm *create_algorithm( enum alg_id id, DWORD flags )
 {
     struct algorithm *ret;
     if (!(ret = calloc( 1, sizeof(*ret) ))) return NULL;
     ret->hdr.magic = MAGIC_ALG;
     ret->id        = id;
-    ret->mode      = mode;
     ret->flags     = flags;
+    ret->curve_id  = builtin_algorithms[id].curve_id;
     return ret;
 }
 
@@ -403,15 +645,21 @@ NTSTATUS WINAPI BCryptOpenAlgorithmProvider( BCRYPT_ALG_HANDLE *handle, const WC
         return STATUS_NOT_IMPLEMENTED;
     }
 
-    if (!(alg = create_algorithm( alg_id, 0, flags ))) return STATUS_NO_MEMORY;
+    if (!(alg = create_algorithm( alg_id, flags ))) return STATUS_NO_MEMORY;
     *handle = alg;
+
     TRACE( "returning handle %p\n", *handle );
     return STATUS_SUCCESS;
 }
 
+static void secure_zero( void *ptr, SIZE_T size )
+{
+    if (ptr) SecureZeroMemory( ptr, size );
+}
+
 static void destroy_object( struct object *obj )
 {
-    SecureZeroMemory( &obj->magic, sizeof(obj->magic) );
+    secure_zero( &obj->magic, sizeof(obj->magic) );
     free( obj );
 }
 
@@ -422,6 +670,7 @@ NTSTATUS WINAPI BCryptCloseAlgorithmProvider( BCRYPT_ALG_HANDLE handle, DWORD fl
     TRACE( "%p, %#lx\n", handle, flags );
 
     if (!handle || is_alg_pseudo_handle( handle ) || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE;
+
     destroy_object( &alg->hdr );
     return STATUS_SUCCESS;
 }
@@ -437,74 +686,31 @@ NTSTATUS WINAPI BCryptGetFipsAlgorithmMode(BOOLEAN *enabled)
     return STATUS_SUCCESS;
 }
 
-static const struct ltc_hash_descriptor *get_hash_descriptor( enum alg_id alg_id )
-{
-    switch (alg_id)
-    {
-    case ALG_ID_MD2: return &md2_desc;
-    case ALG_ID_MD4: return &md4_desc;
-    case ALG_ID_MD5: return &md5_desc;
-    case ALG_ID_SHA1: return &sha1_desc;
-    case ALG_ID_SHA256: return &sha256_desc;
-    case ALG_ID_SHA384: return &sha384_desc;
-    case ALG_ID_SHA512: return &sha512_desc;
-    default:
-        ERR( "unhandled id %u\n", alg_id );
-        return NULL;
-    }
-}
-
-#define HASH_FLAG_HMAC      0x01
-#define HASH_FLAG_REUSABLE  0x02
-struct hash
-{
-    struct object     hdr;
-    enum alg_id       alg_id;
-    const struct ltc_hash_descriptor *desc;
-    ULONG             flags;
-    UCHAR            *secret;
-    ULONG             secret_len;
-    hash_state        outer;
-    hash_state        inner;
-};
-
-#define BLOCK_LENGTH_RC4        1
-#define BLOCK_LENGTH_3DES       8
-#define BLOCK_LENGTH_AES        16
-
-static NTSTATUS generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
+static NTSTATUS get_generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
 {
     if (!wcscmp( prop, BCRYPT_OBJECT_LENGTH ))
     {
-        if (!builtin_algorithms[id].object_length)
-            return STATUS_NOT_SUPPORTED;
+        if (!builtin_algorithms[id].object_length) return STATUS_NOT_SUPPORTED;
         *ret_size = sizeof(ULONG);
-        if (size < sizeof(ULONG))
-            return STATUS_BUFFER_TOO_SMALL;
-        if (buf)
-            *(ULONG *)buf = builtin_algorithms[id].object_length;
+        if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL;
+        if (buf) *(ULONG *)buf = builtin_algorithms[id].object_length;
         return STATUS_SUCCESS;
     }
 
     if (!wcscmp( prop, BCRYPT_HASH_LENGTH ))
     {
-        if (!builtin_algorithms[id].hash_length)
-            return STATUS_NOT_SUPPORTED;
+        if (!builtin_algorithms[id].hash_length) return STATUS_NOT_SUPPORTED;
         *ret_size = sizeof(ULONG);
-        if (size < sizeof(ULONG))
-            return STATUS_BUFFER_TOO_SMALL;
-        if(buf)
-            *(ULONG*)buf = builtin_algorithms[id].hash_length;
+        if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL;
+        if (buf) *(ULONG*)buf = builtin_algorithms[id].hash_length;
         return STATUS_SUCCESS;
     }
 
     if (!wcscmp( prop, BCRYPT_ALGORITHM_NAME ))
     {
         *ret_size = (lstrlenW(builtin_algorithms[id].name) + 1) * sizeof(WCHAR);
-        if (size < *ret_size)
-            return STATUS_BUFFER_TOO_SMALL;
-        if(buf)
-            memcpy(buf, builtin_algorithms[id].name, *ret_size);
+        if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL;
+        if(buf) memcpy(buf, builtin_algorithms[id].name, *ret_size);
         return STATUS_SUCCESS;
     }
 
@@ -525,13 +731,15 @@ static NTSTATUS get_3des_property( enum chain_mode mode, const WCHAR *prop, UCHA
         const WCHAR *str;
         switch (mode)
         {
-        case CHAIN_MODE_CBC: str = BCRYPT_CHAIN_MODE_CBC; break;
-        default: return STATUS_NOT_IMPLEMENTED;
+        case CHAIN_MODE_CBC:
+            str = BCRYPT_CHAIN_MODE_CBC; break;
+        default:
+            return STATUS_NOT_IMPLEMENTED;
         }
 
-        *ret_size = 64;
+        *ret_size = (wcslen(str) + 1) * sizeof(WCHAR) * 2; /* quirk */
         if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL;
-        memcpy( buf, str, (lstrlenW(str) + 1) * sizeof(WCHAR) );
+        memcpy( buf, str, (wcslen(str) + 1) * sizeof(WCHAR) );
         return STATUS_SUCCESS;
     }
     if (!wcscmp( prop, BCRYPT_KEY_LENGTHS ))
@@ -572,9 +780,9 @@ static NTSTATUS get_aes_property( enum chain_mode mode, const WCHAR *prop, UCHAR
         default: return STATUS_NOT_IMPLEMENTED;
         }
 
-        *ret_size = 64;
+        *ret_size = (wcslen(str) + 1) * sizeof(WCHAR) * 2; /* quirk */
         if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL;
-        memcpy( buf, str, (lstrlenW(str) + 1) * sizeof(WCHAR) );
+        memcpy( buf, str, (wcslen(str) + 1) * sizeof(WCHAR) );
         return STATUS_SUCCESS;
     }
     if (!wcscmp( prop, BCRYPT_KEY_LENGTHS ))
@@ -664,13 +872,26 @@ static NTSTATUS get_pbkdf2_property( enum chain_mode mode, const WCHAR *prop, UC
     return STATUS_NOT_IMPLEMENTED;
 }
 
+static NTSTATUS get_chacha20_poly1305_property( const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
+{
+    if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH ))
+    {
+        *ret_size = sizeof(ULONG);
+        if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL;
+        if (buf) *(ULONG *)buf = BLOCK_LENGTH_CHACHA20_POLY1305;
+        return STATUS_SUCCESS;
+    }
+
+    FIXME( "unsupported property %s\n", debugstr_w(prop) );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop, UCHAR *buf, ULONG size,
                                   ULONG *ret_size )
 {
     NTSTATUS status;
 
-    status = generic_alg_property( alg->id, prop, buf, size, ret_size );
-    if (status != STATUS_NOT_IMPLEMENTED)
+    if ((status = get_generic_alg_property( alg->id, prop, buf, size, ret_size )) != STATUS_NOT_IMPLEMENTED)
         return status;
 
     switch (alg->id)
@@ -678,6 +899,9 @@ static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop
     case ALG_ID_3DES:
         return get_3des_property( alg->mode, prop, buf, size, ret_size );
 
+    case ALG_ID_CHACHA20_POLY1305:
+        return get_chacha20_poly1305_property( prop, buf, size, ret_size );
+
     case ALG_ID_AES:
         return get_aes_property( alg->mode, prop, buf, size, ret_size );
 
@@ -691,7 +915,7 @@ static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop
         return get_dsa_property( alg->mode, prop, buf, size, ret_size );
 
     case ALG_ID_PBKDF2:
-	return get_pbkdf2_property( alg->mode, prop, buf, size, ret_size );
+        return get_pbkdf2_property( alg->mode, prop, buf, size, ret_size );
 
     default:
         break;
@@ -708,6 +932,7 @@ static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHA
     case ALG_ID_3DES:
         if (!wcscmp( prop, BCRYPT_CHAINING_MODE ))
         {
+            TRACE( "mode %s\n", debugstr_w((WCHAR *)value) );
             if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC ))
             {
                 alg->mode = CHAIN_MODE_CBC;
@@ -722,9 +947,21 @@ static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHA
         FIXME( "unsupported 3des algorithm property %s\n", debugstr_w(prop) );
         return STATUS_NOT_IMPLEMENTED;
 
+    case ALG_ID_CHACHA20_POLY1305:
+        if (!wcscmp( prop, BCRYPT_CHAINING_MODE ))
+        {
+            if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_NA )) return STATUS_SUCCESS;
+
+            FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) );
+            return STATUS_NOT_IMPLEMENTED;
+        }
+        FIXME( "unsupported CHACHA20_POLY1305 algorithm property %s\n", debugstr_w(prop) );
+        return STATUS_NOT_IMPLEMENTED;
+
     case ALG_ID_AES:
         if (!wcscmp( prop, BCRYPT_CHAINING_MODE ))
         {
+            TRACE( "mode %s\n", debugstr_w((WCHAR *)value) );
             if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_ECB ))
             {
                 alg->mode = CHAIN_MODE_ECB;
@@ -757,6 +994,7 @@ static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHA
     case ALG_ID_RC4:
         if (!wcscmp( prop, BCRYPT_CHAINING_MODE ))
         {
+            TRACE( "mode %s\n", debugstr_w((WCHAR *)value) );
             if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_NA )) return STATUS_SUCCESS;
 
             FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) );
@@ -791,6 +1029,16 @@ static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHA
                 alg->curve_id = ECC_CURVE_P521R1;
                 return STATUS_SUCCESS;
             }
+            else if (!wcscmp( (WCHAR *)value, BCRYPT_ECC_CURVE_BRAINPOOLP256R1 ))
+            {
+                alg->curve_id = ECC_CURVE_BRAINPOOLP256R1;
+                return STATUS_SUCCESS;
+            }
+            else if (!wcscmp( (WCHAR *)value, BCRYPT_ECC_CURVE_BRAINPOOLP384R1 ))
+            {
+                alg->curve_id = ECC_CURVE_BRAINPOOLP384R1;
+                return STATUS_SUCCESS;
+            }
             FIXME( "unsupported curve %s\n", debugstr_w((WCHAR *)value) );
             return STATUS_NOT_IMPLEMENTED;
         }
@@ -803,28 +1051,52 @@ static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHA
     }
 }
 
+static NTSTATUS set_dh_parameters( struct key *key, const UCHAR *buf, ULONG size )
+{
+    BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)buf;
+    UCHAR *prime, *generator;
+    ULONG key_size = len_from_bitlen( key->a.bitlen );
+
+    if (key->alg_id != ALG_ID_DH || (key->flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
+
+    if (size < sizeof(*hdr) || hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != key_size ||
+        hdr->cbLength != sizeof(*hdr) + key_size * 2 || size != hdr->cbLength) return STATUS_INVALID_PARAMETER;
+
+    if (!key->a.dsa.group && !(key->a.dsa.group = SymCryptDlgroupAllocate( key_size * 8, 0 )))
+        return STATUS_NO_MEMORY;
+
+    prime = (UCHAR *)(hdr + 1);
+    generator = prime + key_size;
+
+    if (SymCryptDlgroupSetValue( prime, key_size, NULL, 0, generator, key_size, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                 NULL, NULL, 0, key->a.dsa.count, SYMCRYPT_DLGROUP_FIPS_NONE, key->a.dsa.group ))
+        return STATUS_INTERNAL_ERROR;
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
 {
     if (!wcscmp( prop, BCRYPT_CHAINING_MODE ))
     {
         if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_ECB ))
         {
-            key->u.s.mode = CHAIN_MODE_ECB;
+            key->s.mode = CHAIN_MODE_ECB;
             return STATUS_SUCCESS;
         }
         else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC ))
         {
-            key->u.s.mode = CHAIN_MODE_CBC;
+            key->s.mode = CHAIN_MODE_CBC;
             return STATUS_SUCCESS;
         }
         else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_GCM ))
         {
-            key->u.s.mode = CHAIN_MODE_GCM;
+            key->s.mode = CHAIN_MODE_GCM;
             return STATUS_SUCCESS;
         }
         else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CFB ))
         {
-            key->u.s.mode = CHAIN_MODE_CFB;
+            key->s.mode = CHAIN_MODE_CFB;
             return STATUS_SUCCESS;
         }
         else
@@ -836,53 +1108,78 @@ static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *val
     else if (!wcscmp( prop, BCRYPT_KEY_LENGTH ))
     {
         if (size < sizeof(DWORD)) return STATUS_INVALID_PARAMETER;
-        key->u.a.bitlen = *(DWORD*)value;
+        key->a.bitlen = *(DWORD*)value;
         return STATUS_SUCCESS;
     }
     else if (!wcscmp( prop, BCRYPT_DH_PARAMETERS ))
     {
-        BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)value;
-        struct key_asymmetric_import_params params;
-
-        if (key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE;
-        if (key->alg_id != ALG_ID_DH || size < sizeof(*hdr) || hdr->cbLength != size ||
-            hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != len_from_bitlen( key->u.a.bitlen ))
-            return STATUS_INVALID_PARAMETER;
-
-        params.key   = key;
-        params.flags = KEY_IMPORT_FLAG_DH_PARAMETERS;
-        params.buf   = value;
-        params.len   = size;
-        return UNIX_CALL( key_asymmetric_import, &params );
+        return set_dh_parameters( key, value, size );
     }
 
     FIXME( "unsupported key property %s\n", debugstr_w(prop) );
     return STATUS_NOT_IMPLEMENTED;
 }
 
+NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
+{
+    struct object *object = get_object( handle, 0 );
+
+    TRACE( "%p, %s, %p, %lu, %#lx\n", handle, debugstr_w(prop), value, size, flags );
+
+    if (!handle) return STATUS_INVALID_HANDLE;
+    if (is_alg_pseudo_handle( handle )) return STATUS_ACCESS_DENIED;
+
+    switch (object->magic)
+    {
+    case MAGIC_ALG:
+    {
+        struct algorithm *alg = (struct algorithm *)object;
+        return set_alg_property( alg, prop, value, size, flags );
+    }
+    case MAGIC_KEY:
+    {
+        struct key *key = (struct key *)object;
+        return set_key_property( key, prop, value, size, flags );
+    }
+    default:
+        WARN( "unknown magic %#lx\n", object->magic );
+        return STATUS_INVALID_HANDLE;
+    }
+}
+
 static NTSTATUS get_hash_property( const struct hash *hash, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
 {
     NTSTATUS status;
 
-    status = generic_alg_property( hash->alg_id, prop, buf, size, ret_size );
-    if (status == STATUS_NOT_IMPLEMENTED)
+    if ((status = get_generic_alg_property( hash->alg_id, prop, buf, size, ret_size )) == STATUS_NOT_IMPLEMENTED)
         FIXME( "unsupported property %s\n", debugstr_w(prop) );
     return status;
 }
 
-static NTSTATUS get_dh_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
+static NTSTATUS get_dh_parameters( const struct key *key, UCHAR *buf, ULONG size, ULONG *ret_size )
 {
-    struct key_asymmetric_export_params params;
+    BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)buf;
+    UCHAR *prime, *generator;
+    UINT32 key_size = len_from_bitlen( key->a.bitlen ), required_size = sizeof(*hdr) + key_size * 2;
+
+    if (!(key->flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
+
+    *ret_size = required_size;
+    if (!buf) return STATUS_SUCCESS;
+    else if (size < required_size) return STATUS_BUFFER_TOO_SMALL;
 
-    if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED;
-    if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
+    prime = (UCHAR *)(hdr + 1);
+    generator = prime + key_size;
 
-    params.key     = (struct key *)key;
-    params.flags   = KEY_EXPORT_FLAG_DH_PARAMETERS;
-    params.buf     = buf;
-    params.len     = size;
-    params.ret_len = ret_size;
-    return UNIX_CALL( key_asymmetric_export, &params );
+    if (SymCryptDlgroupGetValue( key->a.dsa.group, prime, key_size, NULL, 0, generator, key_size,
+                                 SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, NULL, (BYTE *)key->a.dsa.seed,
+                                 sizeof(key->a.dsa.seed), (UINT32 *)&key->a.dsa.count )) return STATUS_INTERNAL_ERROR;
+
+    hdr->cbLength    = sizeof(*hdr) + key_size * 2;
+    hdr->dwMagic     = BCRYPT_DH_PARAMETERS_MAGIC;
+    hdr->cbKeyLength = key_size;
+
+    return STATUS_SUCCESS;
 }
 
 static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
@@ -893,8 +1190,8 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA
         if (size < sizeof(DWORD)) return STATUS_BUFFER_TOO_SMALL;
         if (buf)
         {
-            if (is_symmetric_key(key)) *(DWORD *)buf = key->u.s.block_size * 8;
-            else *(DWORD *)buf = key->u.a.bitlen;
+            if (is_symmetric_key(key)) *(DWORD *)buf = key->s.block_size * 8;
+            else *(DWORD *)buf = key->a.bitlen;
         }
         return STATUS_SUCCESS;
     }
@@ -902,14 +1199,15 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA
     switch (key->alg_id)
     {
     case ALG_ID_3DES:
-        return get_3des_property( key->u.s.mode, prop, buf, size, ret_size );
+        return get_3des_property( key->s.mode, prop, buf, size, ret_size );
 
     case ALG_ID_AES:
         if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) return STATUS_NOT_SUPPORTED;
-        return get_aes_property( key->u.s.mode, prop, buf, size, ret_size );
+        return get_aes_property( key->s.mode, prop, buf, size, ret_size );
 
     case ALG_ID_DH:
-        return get_dh_property( key, prop, buf, size, ret_size );
+        if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED;
+        return get_dh_parameters( key, buf, size, ret_size );
 
     default:
         FIXME( "unsupported algorithm %u\n", key->alg_id );
@@ -920,7 +1218,7 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA
 NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHAR *buffer, ULONG count, ULONG *res,
                                    ULONG flags )
 {
-    struct object *object = get_object( handle, 0 );
+    const struct object *object = get_object( handle, 0 );
 
     TRACE( "%p, %s, %p, %lu, %p, %#lx\n", handle, wine_dbgstr_w(prop), buffer, count, res, flags );
 
@@ -950,43 +1248,44 @@ NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHA
     }
 }
 
-static void hash_prepare( struct hash *hash )
+static void prepare_hash( struct hash *hash )
 {
-    UCHAR buffer[MAX_HASH_BLOCK_BITS / 8] = {0};
-    int block_bytes, i;
-
-    /* initialize hash */
-    hash->desc->init( &hash->inner );
-    if (!(hash->flags & HASH_FLAG_HMAC)) return;
-
-    /* initialize hmac */
-    hash->desc->init( &hash->outer );
-    block_bytes = hash->desc->blocksize;
-    if (hash->secret_len > block_bytes)
+    if (hash->flags & HASH_FLAG_HMAC)
     {
-        hash_state temp;
-        hash->desc->init( &temp );
-        hash->desc->process( &temp, hash->secret, hash->secret_len );
-        hash->desc->done( &temp, buffer );
+        SymCryptHmacExpandKey( hash->desc, &hash->hmac, hash->secret, hash->secret_len );
+        SymCryptHmacInit( &hash->hmac_state, &hash->hmac );
     }
-    else memcpy( buffer, hash->secret, hash->secret_len );
+    else SymCryptHashInit( hash->desc, &hash->state );
+}
 
-    for (i = 0; i < block_bytes; i++) buffer[i] ^= 0x5c;
-    hash->desc->process( &hash->outer, buffer, block_bytes );
-    for (i = 0; i < block_bytes; i++) buffer[i] ^= (0x5c ^ 0x36);
-    hash->desc->process( &hash->inner, buffer, block_bytes );
+static const SYMCRYPT_HASH *get_hash_from_alg( enum alg_id alg_id )
+{
+    switch (alg_id)
+    {
+    case ALG_ID_MD2:    return SymCryptMd2Algorithm;
+    case ALG_ID_MD4:    return SymCryptMd4Algorithm;
+    case ALG_ID_MD5:    return SymCryptMd5Algorithm;
+    case ALG_ID_SHA1:   return SymCryptSha1Algorithm;
+    case ALG_ID_SHA256: return SymCryptSha256Algorithm;
+    case ALG_ID_SHA384: return SymCryptSha384Algorithm;
+    case ALG_ID_SHA512: return SymCryptSha512Algorithm;
+    default:
+        FIXME( "unhandled algorithm %u\n", alg_id );
+        return NULL;
+    }
 }
 
-static NTSTATUS hash_create( const struct algorithm *alg, UCHAR *secret, ULONG secret_len, ULONG flags,
+static NTSTATUS create_hash( const struct algorithm *alg, UCHAR *secret, ULONG secret_len, ULONG flags,
                              struct hash **ret_hash )
 {
     struct hash *hash;
-    const struct ltc_hash_descriptor *desc = get_hash_descriptor( alg->id );
+    const SYMCRYPT_HASH *desc = get_hash_from_alg( alg->id );
 
     if (!desc) return STATUS_NOT_IMPLEMENTED;
     if (!(hash = calloc( 1, sizeof(*hash) ))) return STATUS_NO_MEMORY;
     hash->hdr.magic = MAGIC_HASH;
     hash->alg_id    = alg->id;
+    hash->len       = builtin_algorithms[alg->id].hash_length;
     hash->desc      = desc;
     if (alg->flags & BCRYPT_ALG_HANDLE_HMAC_FLAG) hash->flags = HASH_FLAG_HMAC;
     if ((alg->flags & BCRYPT_HASH_REUSABLE_FLAG) || (flags & BCRYPT_HASH_REUSABLE_FLAG))
@@ -1000,7 +1299,7 @@ static NTSTATUS hash_create( const struct algorithm *alg, UCHAR *secret, ULONG s
     memcpy( hash->secret, secret, secret_len );
     hash->secret_len = secret_len;
 
-    hash_prepare( hash );
+    prepare_hash( hash );
     *ret_hash = hash;
     return STATUS_SUCCESS;
 }
@@ -1023,19 +1322,20 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE handle, BCRYPT_HASH_HANDLE *
     if (!alg) return STATUS_INVALID_HANDLE;
     if (!ret_handle) return STATUS_INVALID_PARAMETER;
 
-    if ((status = hash_create( alg, secret, secret_len, flags, &hash ))) return status;
+    if ((status = create_hash( alg, secret, secret_len, flags, &hash ))) return status;
     *ret_handle = hash;
+
     TRACE( "returning handle %p\n", *ret_handle );
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptDuplicateHash( BCRYPT_HASH_HANDLE handle, BCRYPT_HASH_HANDLE *handle_copy,
-                                     UCHAR *object, ULONG objectlen, ULONG flags )
+NTSTATUS WINAPI BCryptDuplicateHash( BCRYPT_HASH_HANDLE handle, BCRYPT_HASH_HANDLE *handle_copy, UCHAR *object,
+                                     ULONG object_len, ULONG flags )
 {
-    struct hash *hash_orig = get_hash_object( handle );
+    const struct hash *hash_orig = get_hash_object( handle );
     struct hash *hash_copy;
 
-    TRACE( "%p, %p, %p, %lu, %#lx\n", handle, handle_copy, object, objectlen, flags );
+    TRACE( "%p, %p, %p, %lu, %#lx\n", handle, handle_copy, object, object_len, flags );
 
     if (!hash_orig) return STATUS_INVALID_HANDLE;
     if (!handle_copy) return STATUS_INVALID_PARAMETER;
@@ -1051,12 +1351,20 @@ NTSTATUS WINAPI BCryptDuplicateHash( BCRYPT_HASH_HANDLE handle, BCRYPT_HASH_HAND
     }
     memcpy( hash_copy->secret, hash_orig->secret, hash_orig->secret_len );
 
+    if (hash_orig->flags & HASH_FLAG_HMAC)
+    {
+        SymCryptHmacKeyCopy( &hash_orig->hmac, &hash_copy->hmac );
+        SymCryptHmacStateCopy( &hash_orig->hmac_state, &hash_orig->hmac, &hash_copy->hmac_state );
+    }
+    else
+        SymCryptHashStateCopy( hash_orig->desc, &hash_orig->state, &hash_copy->state );
+
     *handle_copy = hash_copy;
     TRACE( "returning handle %p\n", *handle_copy );
     return STATUS_SUCCESS;
 }
 
-static void hash_destroy( struct hash *hash )
+static void destroy_hash( struct hash *hash )
 {
     if (!hash) return;
     free( hash->secret );
@@ -1069,11 +1377,20 @@ NTSTATUS WINAPI BCryptDestroyHash( BCRYPT_HASH_HANDLE handle )
 
     TRACE( "%p\n", handle );
 
-    if (!hash) return STATUS_INVALID_PARAMETER;
-    hash_destroy( hash );
+    if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_PARAMETER;
+
+    destroy_hash( hash );
     return STATUS_SUCCESS;
 }
 
+static void hash_data( struct hash *hash, UCHAR *input, ULONG size )
+{
+    if (hash->flags & HASH_FLAG_HMAC)
+        SymCryptHmacAppend( &hash->hmac_state, input, size );
+    else
+        SymCryptHashAppend( hash->desc, &hash->state, input, size );
+}
+
 NTSTATUS WINAPI BCryptHashData( BCRYPT_HASH_HANDLE handle, UCHAR *input, ULONG size, ULONG flags )
 {
     struct hash *hash = get_hash_object( handle );
@@ -1081,28 +1398,19 @@ NTSTATUS WINAPI BCryptHashData( BCRYPT_HASH_HANDLE handle, UCHAR *input, ULONG s
     TRACE( "%p, %p, %lu, %#lx\n", handle, input, size, flags );
 
     if (!hash) return STATUS_INVALID_HANDLE;
-    if (!input) return STATUS_SUCCESS;
 
-    if (hash->desc->process( &hash->inner, input, size )) return STATUS_INVALID_PARAMETER;
+    hash_data( hash, input, size );
     return STATUS_SUCCESS;
 }
 
-static void hash_finalize( struct hash *hash, UCHAR *output )
+static void finish_hash( struct hash *hash, UCHAR *output )
 {
-    UCHAR buffer[MAX_HASH_OUTPUT_BYTES];
-
-    if (!(hash->flags & HASH_FLAG_HMAC))
-    {
-        hash->desc->done( &hash->inner, output );
-        if (hash->flags & HASH_FLAG_REUSABLE) hash_prepare( hash );
-        return;
-    }
-
-    hash->desc->done( &hash->inner, buffer );
-    hash->desc->process( &hash->outer, buffer, hash->desc->hashsize );
-    hash->desc->done( &hash->outer, output );
+    if (hash->flags & HASH_FLAG_HMAC)
+        SymCryptHmacResult( &hash->hmac_state, output );
+    else
+        SymCryptHashResult( hash->desc, &hash->state, output, hash->len );
 
-    if (hash->flags & HASH_FLAG_REUSABLE) hash_prepare( hash );
+    if (hash->flags & HASH_FLAG_REUSABLE) prepare_hash( hash );
 }
 
 NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULONG size, ULONG flags )
@@ -1112,26 +1420,26 @@ NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULON
     TRACE( "%p, %p, %lu, %#lx\n", handle, output, size, flags );
 
     if (!hash) return STATUS_INVALID_HANDLE;
-    if (!output || size != hash->desc->hashsize) return STATUS_INVALID_PARAMETER;
+    if (!output || size != hash->len) return STATUS_INVALID_PARAMETER;
 
-    hash_finalize( hash, output );
+    finish_hash( hash, output );
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS hash_single( struct algorithm *alg, UCHAR *secret, ULONG secret_len, UCHAR *input, ULONG input_len,
-                             UCHAR *output, ULONG output_len )
+static NTSTATUS hash_single( const struct algorithm *alg, UCHAR *secret, ULONG secret_len, UCHAR *input,
+                             ULONG input_len, UCHAR *output )
 {
     struct hash *hash;
     NTSTATUS status;
 
-    if ((status = hash_create( alg, secret, secret_len, 0, &hash ))) return status;
-    if (input_len && hash->desc->process( &hash->inner, input, input_len ))
-    {
-        hash_destroy( hash );
-        return STATUS_INVALID_PARAMETER;
-    }
-    hash_finalize( hash, output );
-    hash_destroy( hash );
+    if ((status = create_hash( alg, secret, secret_len, 0, &hash ))) return status;
+
+    if (hash->flags & HASH_FLAG_HMAC)
+        SymCryptHmac( &hash->hmac, input, input_len, output );
+    else
+        SymCryptHash( hash->desc, input, input_len, output, hash->len );
+
+    destroy_hash( hash );
     return STATUS_SUCCESS;
 }
 
@@ -1145,273 +1453,437 @@ NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE handle, UCHAR *secret, ULONG secre
     if (!alg) return STATUS_INVALID_HANDLE;
     if (!output || output_len != builtin_algorithms[alg->id].hash_length) return STATUS_INVALID_PARAMETER;
 
-    return hash_single(alg, secret, secret_len, input, input_len, output, output_len );
+    return hash_single( alg, secret, secret_len, input, input_len, output );
 }
 
-static ULONG curve_strength( enum ecc_curve_id curve_id )
+static void destroy_key( struct key *key )
 {
-    switch (curve_id)
+    if (!key) return;
+    if (is_symmetric_key( key ))
     {
-    case ECC_CURVE_25519:  return 253;
-    case ECC_CURVE_P256R1: return 256;
-    case ECC_CURVE_P384R1: return 384;
-    case ECC_CURVE_P521R1: return 521;
-    default:
-        FIXME( "unsupported curve %u\n", curve_id );
-        return 0;
+        secure_zero( key->s.secret, key->s.secret_len );
+        free( key->s.secret );
+        secure_zero( key->s.vector, key->s.block_size );
+        DeleteCriticalSection( &key->s.cs );
     }
-}
-
-static NTSTATUS key_asymmetric_create( enum alg_id alg_id, enum ecc_curve_id curve_id, ULONG bitlen, struct key **ret_key )
-{
-    struct key *key;
-
-    if (!__wine_unixlib_handle)
+    else
     {
-        ERR( "no encryption support\n" );
-        return STATUS_NOT_IMPLEMENTED;
+        switch (key->alg_id)
+        {
+        case ALG_ID_RSA:
+        case ALG_ID_RSA_SIGN:
+            if (key->a.rsa.handle) SymCryptRsakeyFree( key->a.rsa.handle );
+            break;
+        case ALG_ID_ECDH:
+        case ALG_ID_ECDH_P256:
+        case ALG_ID_ECDH_P384:
+        case ALG_ID_ECDH_P521:
+        case ALG_ID_ECDSA:
+        case ALG_ID_ECDSA_P256:
+        case ALG_ID_ECDSA_P384:
+        case ALG_ID_ECDSA_P521:
+            if (key->a.ecc.handle) SymCryptEckeyFree( key->a.ecc.handle );
+            if (key->a.ecc.curve) SymCryptEcurveFree( key->a.ecc.curve );
+            break;
+        case ALG_ID_DH:
+        case ALG_ID_DSA:
+            if (key->a.dsa.handle) SymCryptDlkeyFree( key->a.dsa.handle );
+            if (key->a.dsa.group) SymCryptDlgroupFree( key->a.dsa.group );
+            break;
+        default:
+            FIXME( "algorithm %u not handled\n", key->alg_id );
+            break;
+        }
     }
 
-    if (alg_id == ALG_ID_DH && bitlen < 512) return STATUS_INVALID_PARAMETER;
-
-    if (!(key = calloc( 1, sizeof(*key) ))) return STATUS_NO_MEMORY;
-    key->hdr.magic    = MAGIC_KEY;
-    key->alg_id       = alg_id;
-    key->u.a.bitlen   = curve_id ? curve_strength( curve_id ) : bitlen;
-    key->u.a.curve_id = curve_id;
-
-    *ret_key = key;
-    return STATUS_SUCCESS;
+    secure_zero( &key->hdr.magic, sizeof(key->hdr.magic) );
+    _aligned_free( key );
 }
 
-static BOOL is_equal_vector( const UCHAR *vector, ULONG len, const UCHAR *vector2, ULONG len2 )
+static NTSTATUS alloc_aes_key( struct key *key, enum chain_mode mode, ULONG block_size, const UCHAR *secret,
+                               ULONG secret_len )
 {
-    if (!vector && !vector2) return TRUE;
-    if (len != len2) return FALSE;
-    return !memcmp( vector, vector2, len );
-}
+    key->s.mode       = mode;
+    key->s.block_size = block_size;
 
-static NTSTATUS key_symmetric_set_vector( struct key *key, UCHAR *vector, ULONG vector_len, BOOL force_reset )
-{
-    BOOL needs_reset = force_reset || !is_equal_vector( key->u.s.vector, key->u.s.vector_len, vector, vector_len );
-    if (vector)
+    if (!(key->s.secret = malloc( secret_len ))) return STATUS_NO_MEMORY;
+    memcpy( key->s.secret, secret, secret_len );
+    key->s.secret_len = secret_len;
+
+    InitializeCriticalSection( &key->s.cs );
+
+    switch (mode)
     {
-        free( key->u.s.vector );
-        key->u.s.vector = NULL;
-        key->u.s.vector_len = 0;
+    case CHAIN_MODE_ECB:
+    case CHAIN_MODE_CBC:
+    case CHAIN_MODE_CFB:
+        if (SymCryptAesExpandKey( &key->s.aes.handle, secret, secret_len )) return STATUS_INTERNAL_ERROR;
+        break;
+
+    case CHAIN_MODE_GCM:
+        if (SymCryptGcmExpandKey( &key->s.gcm.handle, SymCryptAesBlockCipher, secret, secret_len ))
+            return STATUS_INTERNAL_ERROR;
+        break;
 
-        if (!(key->u.s.vector = malloc( vector_len ))) return STATUS_NO_MEMORY;
-        memcpy( key->u.s.vector, vector, vector_len );
-        key->u.s.vector_len = vector_len;
+    default:
+        FIXME( "unhandled mode %u\n", mode );
+        return STATUS_NOT_SUPPORTED;
     }
-    if (needs_reset) UNIX_CALL( key_symmetric_vector_reset, key );
+
     return STATUS_SUCCESS;
 }
 
-static struct key *key_symmetric_create( enum alg_id alg, enum chain_mode mode, ULONG block_size, const UCHAR *secret,
-                                         ULONG secret_len )
+static NTSTATUS alloc_key( enum alg_id alg_id, ULONG flags, struct key **ret_key )
 {
-    struct key *ret;
-
-    if (!(ret = calloc( 1, sizeof(*ret) ))) return NULL;
-    InitializeCriticalSection( &ret->u.s.cs );
-    ret->hdr.magic      = MAGIC_KEY;
-    ret->alg_id         = alg;
-    ret->u.s.mode       = mode;
-    ret->u.s.block_size = block_size;
+    struct key *key;
 
-    if (!(ret->u.s.secret = malloc( secret_len )))
-    {
-        DeleteCriticalSection( &ret->u.s.cs );
-        free( ret );
-        return NULL;
-    }
-    memcpy( ret->u.s.secret, secret, secret_len );
-    ret->u.s.secret_len = secret_len;
+    if (!(key = _aligned_malloc( sizeof(struct key), TYPE_ALIGNMENT(struct key) ))) return STATUS_NO_MEMORY;
+    memset( key, 0, sizeof(*key) );
+    key->hdr.magic = MAGIC_KEY;
+    key->alg_id    = alg_id;
+    key->flags     = flags;
 
-    return ret;
+    *ret_key = key;
+    return STATUS_SUCCESS;
 }
 
-static void key_destroy( struct key *key )
+static NTSTATUS validate_len_aes( const BCRYPT_KEY_LENGTHS_STRUCT *key_lengths, ULONG secret_len, ULONG *ret_len )
 {
-    if (is_symmetric_key( key ))
+    ULONG size;
+
+    if (secret_len > (size = key_lengths->dwMaxLength / 8))
     {
-        UNIX_CALL( key_symmetric_destroy, key );
-        free( key->u.s.vector );
-        free( key->u.s.secret );
-        DeleteCriticalSection( &key->u.s.cs );
+        WARN( "secret_len %lu exceeds key max length %lu, setting to maximum\n", secret_len, size );
+        *ret_len = size;
+        return STATUS_SUCCESS;
+    }
+    else if (secret_len < (size = key_lengths->dwMinLength / 8))
+    {
+        WARN( "secret_len %lu is less than minimum key length %lu\n", secret_len, size );
+        return STATUS_INVALID_PARAMETER;
+    }
+    else if (key_lengths->dwIncrement && (secret_len * 8 - key_lengths->dwMinLength) % key_lengths->dwIncrement)
+    {
+        WARN( "secret_len %lu is not a valid key length\n", secret_len );
+        return STATUS_INVALID_PARAMETER;
     }
-    else
-        UNIX_CALL( key_asymmetric_destroy, key );
-
-    destroy_object( &key->hdr );
-}
 
-static ULONG get_block_size( struct algorithm *alg )
-{
-    ULONG ret = 0, size = sizeof(ret);
-    get_alg_property( alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&ret, sizeof(ret), &size );
-    return ret;
+    *ret_len = secret_len;
+    return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_symmetric_generate( struct algorithm *alg, BCRYPT_KEY_HANDLE *ret_handle, const UCHAR *secret,
-                                        ULONG secret_len )
+static NTSTATUS generate_symmetric_key( const struct algorithm *alg, const UCHAR *secret, ULONG secret_len,
+                                        struct key **ret_key )
 {
     BCRYPT_KEY_LENGTHS_STRUCT key_lengths;
-    ULONG block_size, size;
     struct key *key;
     NTSTATUS status;
+    ULONG size;
 
-    if (alg->id == ALG_ID_PBKDF2 &&
-            !get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size ))
-    {
-        if (secret_len > key_lengths.dwMaxLength / 8 || secret_len < key_lengths.dwMinLength / 8)
-            return STATUS_INVALID_PARAMETER;
-        block_size = secret_len;
-    }
-    else if (!(block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER;
-    else if (!get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size ))
+    if ((status = get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size )))
+        return status;
+
+    if ((status = alloc_key( alg->id, 0, &key ))) return status;
+
+    switch (alg->id)
     {
-        if (secret_len > (size = key_lengths.dwMaxLength / 8))
+    case ALG_ID_AES:
+        if ((status = validate_len_aes( &key_lengths, secret_len, &secret_len )) ||
+            (status = alloc_aes_key( key, alg->mode, BLOCK_LENGTH_AES, secret, secret_len )))
         {
-            WARN( "secret_len %lu exceeds key max length %lu, setting to maximum\n", secret_len, size );
-            secret_len = size;
+            destroy_key( key );
+            return status;
         }
-        else if (secret_len < (size = key_lengths.dwMinLength / 8))
+        break;
+
+    case ALG_ID_PBKDF2:
+        if (secret_len > key_lengths.dwMaxLength / 8 || secret_len < key_lengths.dwMinLength / 8)
         {
-            WARN( "secret_len %lu is less than minimum key length %lu\n", secret_len, size );
+            destroy_key( key );
             return STATUS_INVALID_PARAMETER;
         }
-        else if (key_lengths.dwIncrement && (secret_len * 8 - key_lengths.dwMinLength) % key_lengths.dwIncrement)
+        if (!(key->s.secret = malloc( secret_len )))
         {
-            WARN( "secret_len %lu is not a valid key length\n", secret_len );
-            return STATUS_INVALID_PARAMETER;
+            destroy_key( key );
+            return STATUS_NO_MEMORY;
         }
-    }
+        memcpy( key->s.secret, secret, secret_len );
+        key->s.block_size = key->s.secret_len = secret_len;
+        break;
 
-    if (!(key = key_symmetric_create( alg->id, alg->mode, block_size, secret, secret_len ))) status = STATUS_NO_MEMORY;
-    else
-    {
-        *ret_handle = key;
-        status = STATUS_SUCCESS;
+    default:
+        FIXME( "unhandled algorithm %u\n", alg->id );
+        destroy_key( key );
+        return STATUS_NOT_SUPPORTED;
     }
 
+    *ret_key = key;
     return status;
 }
 
-static NTSTATUS key_symmetric_decrypt( struct key *key, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
-                                       ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle, UCHAR *object,
+                                            ULONG object_len, UCHAR *secret, ULONG secret_len, ULONG flags )
 {
-    struct key_symmetric_set_auth_data_params auth_params;
-    struct key_symmetric_decrypt_params decrypt_params;
-    struct key_symmetric_get_tag_params tag_params;
-    ULONG bytes_left = input_len;
+    const struct algorithm *alg = get_alg_object( handle );
+    struct key *key;
     NTSTATUS status;
 
-    if (key->u.s.mode == CHAIN_MODE_GCM)
-    {
-        BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *auth_info = padding;
-        UCHAR tag[16];
+    TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, ret_handle, object, object_len, secret, secret_len, flags );
+    if (object) FIXME( "ignoring object buffer\n" );
 
-        if (!auth_info) return STATUS_INVALID_PARAMETER;
-        if (!auth_info->pbNonce) return STATUS_INVALID_PARAMETER;
-        if (!auth_info->pbTag) return STATUS_INVALID_PARAMETER;
-        if (auth_info->cbTag < 12 || auth_info->cbTag > 16) return STATUS_INVALID_PARAMETER;
+    if (!alg) return STATUS_INVALID_HANDLE;
 
-        if ((status = key_symmetric_set_vector( key, auth_info->pbNonce, auth_info->cbNonce, TRUE )))
-            return status;
+    if ((status = generate_symmetric_key( alg, secret, secret_len, &key ))) return status;
+    *ret_handle = key;
 
-        *ret_len = input_len;
-        if (flags & BCRYPT_BLOCK_PADDING) return STATUS_INVALID_PARAMETER;
-        if (!output) return STATUS_SUCCESS;
-        if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
-
-        auth_params.key = key;
-        auth_params.auth_data = auth_info->pbAuthData;
-        auth_params.len = auth_info->cbAuthData;
-        if ((status = UNIX_CALL( key_symmetric_set_auth_data, &auth_params ))) return status;
-
-        decrypt_params.key = key;
-        decrypt_params.input = input;
-        decrypt_params.input_len = input_len;
-        decrypt_params.output = output;
-        decrypt_params.output_len = output_len;
-        if ((status = UNIX_CALL( key_symmetric_decrypt, &decrypt_params ))) return status;
-
-        tag_params.key = key;
-        tag_params.tag = tag;
-        tag_params.len = sizeof(tag);
-        if ((status = UNIX_CALL( key_symmetric_get_tag, &tag_params ))) return status;
-        if (memcmp( tag, auth_info->pbTag, auth_info->cbTag )) return STATUS_AUTH_TAG_MISMATCH;
+    TRACE( "returning handle %p\n", *ret_handle );
+    return STATUS_SUCCESS;
+}
 
-        return STATUS_SUCCESS;
+static NTSTATUS decrypt_aes_ecb( const struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
+                                 ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    ULONG offset = input_len, block_size = key->s.block_size;
+    UCHAR buf[BLOCK_LENGTH_AES];
+    NTSTATUS status = STATUS_SUCCESS;
+
+    if (input_len & (block_size - 1)) return STATUS_INVALID_BUFFER_SIZE;
+    if (!output) return STATUS_SUCCESS;
+
+    if (flags & BCRYPT_BLOCK_PADDING)
+    {
+        if (output_len + block_size < *ret_len || input_len < block_size) return STATUS_BUFFER_TOO_SMALL;
+        offset -= block_size;
     }
+    else if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
 
-    *ret_len = input_len;
+    SymCryptEcbDecrypt( SymCryptAesBlockCipher, &key->s.aes.handle, input, output, offset );
 
-    if (input_len & (key->u.s.block_size - 1)) return STATUS_INVALID_BUFFER_SIZE;
+    if (flags & BCRYPT_BLOCK_PADDING)
+    {
+        SymCryptEcbDecrypt( SymCryptAesBlockCipher, &key->s.aes.handle, input + offset, buf, block_size );
+
+        if (buf[block_size - 1] <= block_size)
+        {
+            *ret_len -= buf[block_size - 1];
+            if (output_len < *ret_len) status = STATUS_BUFFER_TOO_SMALL;
+            else memcpy( output + offset, buf, block_size - buf[block_size - 1] );
+        }
+        else status = STATUS_UNSUCCESSFUL; /* FIXME: invalid padding */
+    }
+
+    return status;
+}
+
+static NTSTATUS decrypt_aes_gcm( struct key *key, const UCHAR *input, ULONG input_len,
+                                 const BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *info, UCHAR *output )
+{
+    SYMCRYPT_ERROR error;
+
+    if (!info || !info->pbNonce || !info->pbTag || info->cbTag < 12 || info->cbTag > 16)
+        return STATUS_INVALID_PARAMETER;
+    if (info->dwFlags & BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG) FIXME( "call chaining not implemented\n" );
+
+    EnterCriticalSection( &key->s.cs );
+
+    SymCryptGcmInit( &key->s.gcm.state, &key->s.gcm.handle, info->pbNonce, info->cbNonce );
+    SymCryptGcmAuthPart( &key->s.gcm.state, info->pbAuthData, info->cbAuthData );
+    SymCryptGcmDecryptPart( &key->s.gcm.state, input, output, input_len );
+    error = SymCryptGcmDecryptFinal( &key->s.gcm.state, info->pbTag, info->cbTag );
+
+    LeaveCriticalSection( &key->s.cs );
+
+    if (error == SYMCRYPT_AUTHENTICATION_FAILURE) return STATUS_AUTH_TAG_MISMATCH;
+    if (error) return STATUS_INTERNAL_ERROR;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS decrypt_aes_vector( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *iv, UCHAR *output,
+                                    ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    UCHAR buf[BLOCK_LENGTH_AES], *out;
+    const UCHAR *in;
+    ULONG bytes_left = input_len, block_size = key->s.block_size;
+    NTSTATUS status = STATUS_SUCCESS;
+
+    if (input_len & (block_size - 1)) return STATUS_INVALID_BUFFER_SIZE;
     if (!output) return STATUS_SUCCESS;
+
     if (flags & BCRYPT_BLOCK_PADDING)
     {
-        if (output_len + key->u.s.block_size < *ret_len) return STATUS_BUFFER_TOO_SMALL;
-        if (input_len < key->u.s.block_size) return STATUS_BUFFER_TOO_SMALL;
-        bytes_left -= key->u.s.block_size;
+        if (output_len + block_size < *ret_len || input_len < block_size) return STATUS_BUFFER_TOO_SMALL;
+        bytes_left -= block_size;
     }
     else if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
 
-    if (key->u.s.mode == CHAIN_MODE_ECB && iv) return STATUS_INVALID_PARAMETER;
-    if ((status = key_symmetric_set_vector( key, iv, iv_len, flags & BCRYPT_BLOCK_PADDING ))) return status;
+    EnterCriticalSection( &key->s.cs );
+
+    if (iv) memcpy( key->s.vector, iv, block_size );
 
-    decrypt_params.key = key;
-    decrypt_params.input = input;
-    decrypt_params.input_len = key->u.s.block_size;
-    decrypt_params.output = output;
-    decrypt_params.output_len = key->u.s.block_size;
-    while (bytes_left >= key->u.s.block_size)
+    in = input;
+    out = output;
+    while (bytes_left >= block_size)
     {
-        if ((status = UNIX_CALL( key_symmetric_decrypt, &decrypt_params ))) return status;
-        if (key->u.s.mode == CHAIN_MODE_ECB && (status = key_symmetric_set_vector( key, NULL, 0, TRUE )))
-            return status;
-        bytes_left -= key->u.s.block_size;
-        decrypt_params.input += key->u.s.block_size;
-        decrypt_params.output += key->u.s.block_size;
+        if (key->s.mode == CHAIN_MODE_CFB)
+            SymCryptCfbDecrypt( SymCryptAesBlockCipher, 1, &key->s.aes.handle, key->s.vector, in, out, block_size );
+        else
+            SymCryptCbcDecrypt( SymCryptAesBlockCipher, &key->s.aes.handle, key->s.vector, in, out, block_size );
+
+        bytes_left -= block_size;
+        in += block_size;
+        out += block_size;
     }
 
     if (flags & BCRYPT_BLOCK_PADDING)
     {
-        UCHAR *buf, *dst = decrypt_params.output;
-        if (!(buf = malloc( key->u.s.block_size ))) return STATUS_NO_MEMORY;
-        decrypt_params.output = buf;
-        status = UNIX_CALL( key_symmetric_decrypt, &decrypt_params );
-        if (!status && buf[ key->u.s.block_size - 1 ] <= key->u.s.block_size)
+        if (key->s.mode == CHAIN_MODE_CFB)
+            SymCryptCfbDecrypt( SymCryptAesBlockCipher, 1, &key->s.aes.handle, key->s.vector, in, buf, block_size );
+        else
+            SymCryptCbcDecrypt( SymCryptAesBlockCipher, &key->s.aes.handle, key->s.vector, in, buf, block_size );
+
+        if (buf[block_size - 1] <= block_size)
         {
-            *ret_len -= buf[ key->u.s.block_size - 1 ];
+            *ret_len -= buf[block_size - 1];
             if (output_len < *ret_len) status = STATUS_BUFFER_TOO_SMALL;
-            else memcpy( dst, buf, key->u.s.block_size - buf[ key->u.s.block_size - 1 ] );
+            else memcpy( out, buf, block_size - buf[block_size - 1] );
         }
         else status = STATUS_UNSUCCESSFUL; /* FIXME: invalid padding */
-        free( buf );
     }
 
-    if (!status)
+    if (iv) memcpy( iv, key->s.vector, block_size );
+
+    LeaveCriticalSection( &key->s.cs );
+    return status;
+}
+
+static NTSTATUS decrypt_symmetric( struct key *key, const UCHAR *input, ULONG input_len,
+                                   const BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *info, UCHAR *iv, ULONG iv_len,
+                                   UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    *ret_len = input_len;
+
+    if (key->s.mode == CHAIN_MODE_GCM && (flags & BCRYPT_BLOCK_PADDING)) return STATUS_INVALID_PARAMETER;
+    if (key->s.mode == CHAIN_MODE_ECB && iv) return STATUS_INVALID_PARAMETER;
+
+    switch (key->alg_id)
+    {
+    case ALG_ID_AES:
+        switch (key->s.mode)
+        {
+        case CHAIN_MODE_ECB:
+            return decrypt_aes_ecb( key, input, input_len, output, output_len, ret_len, flags );
+
+        case CHAIN_MODE_GCM:
+            return decrypt_aes_gcm( key, input, input_len, info, output );
+
+        default:
+            if (iv && iv_len != key->s.block_size) return STATUS_INVALID_PARAMETER;
+            return decrypt_aes_vector( key, input, input_len, iv, output, output_len, ret_len, flags );
+        }
+    default:
+        FIXME( "unhandled algorithm %u\n", key->alg_id );
+        break;
+    }
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+static const SYMCRYPT_HASH *get_hash_from_str( const WCHAR *str )
+{
+    if (!str) return NULL;
+    if (!wcscmp( str, BCRYPT_MD5_ALGORITHM ))    return SymCryptMd5Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM ))   return SymCryptSha1Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) return SymCryptSha256Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) return SymCryptSha384Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) return SymCryptSha512Algorithm;
+
+    FIXME( "unhandled algorithm %s\n", debugstr_w(str) );
+    return NULL;
+}
+
+static NTSTATUS decrypt_rsa( const struct key *key, const UCHAR *input, ULONG input_len, const void *padding,
+                             UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    SIZE_T size = len_from_bitlen( key->a.bitlen );
+
+    if (!flags || flags == BCRYPT_PAD_NONE)
+    {
+        if (input_len % size) return STATUS_INVALID_PARAMETER;
+
+        *ret_len = size;
+        if (output_len < size)
+        {
+            if (output) return STATUS_BUFFER_TOO_SMALL;
+            return STATUS_SUCCESS;
+        }
+        if (SymCryptRsaRawDecrypt( key->a.rsa.handle, input, input_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, output,
+                                   output_len )) return STATUS_INTERNAL_ERROR;
+    }
+    else if (flags == BCRYPT_PAD_PKCS1)
+    {
+        if (SymCryptRsaPkcs1Decrypt( key->a.rsa.handle, input, input_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, output,
+                                     output_len, &size )) return STATUS_INTERNAL_ERROR;
+        *ret_len = size;
+    }
+    else if (flags == BCRYPT_PAD_OAEP)
+    {
+        const BCRYPT_OAEP_PADDING_INFO *pad = padding;
+        const SYMCRYPT_HASH *hash_desc;
+
+        if (!pad) return STATUS_INVALID_PARAMETER;
+        hash_desc = get_hash_from_str( pad->pszAlgId );
+
+        if (SymCryptRsaOaepDecrypt( key->a.rsa.handle, input, input_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, hash_desc,
+                                    pad->pbLabel, pad->cbLabel, 0, output, output_len, &size ))
+            return STATUS_INTERNAL_ERROR;
+        *ret_len = size;
+    }
+    else
+    {
+        FIXME( "unsupported flags %#lx", flags );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS WINAPI BCryptDecrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
+                               ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    struct key *key = get_key_object( handle );
+    NTSTATUS status;
+
+    TRACE( "%p, %p, %lu, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, input, input_len, padding, iv, iv_len, output,
+           output_len, ret_len, flags );
+
+    if (!key) return STATUS_INVALID_HANDLE;
+
+    if (is_symmetric_key( key ))
     {
-        if (key->u.s.vector && input_len >= key->u.s.vector_len)
+        if (flags & ~BCRYPT_BLOCK_PADDING)
         {
-            memcpy( key->u.s.vector, input + input_len - key->u.s.vector_len, key->u.s.vector_len );
-            if (iv) memcpy( iv, key->u.s.vector, min( iv_len, key->u.s.vector_len ));
+            FIXME( "flags %#lx not supported\n", flags );
+            return STATUS_NOT_IMPLEMENTED;
         }
-        else if (key->u.s.vector)
-            FIXME( "Unexpected vector len %lu, *ret_len %lu.\n", key->u.s.vector_len, *ret_len );
+
+        status = decrypt_symmetric( key, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags );
     }
+    else
+    {
+        if (key->alg_id != ALG_ID_RSA) return STATUS_NOT_SUPPORTED;
+        if (!(key->flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
 
+        status = decrypt_rsa( key, input, input_len, padding, output, output_len, ret_len, flags );
+    }
     return status;
 }
 
 /* AES Key Wrap Algorithm (RFC3394) */
-static NTSTATUS aes_unwrap( const UCHAR *secret, ULONG secret_len, const UCHAR *cipher, ULONG cipher_len, UCHAR *plain )
+static NTSTATUS unwrap_aes( const UCHAR *secret, ULONG secret_len, const UCHAR *cipher, ULONG cipher_len, UCHAR *plain )
 {
     UCHAR a[8], *r, b[16];
-    ULONG len, t, i, n = cipher_len / 8;
+    ULONG len = 16, t, i, n = cipher_len / 8;
+    NTSTATUS status;
     int j;
     struct key *key;
 
@@ -1419,7 +1891,12 @@ static NTSTATUS aes_unwrap( const UCHAR *secret, ULONG secret_len, const UCHAR *
     r = plain;
     memcpy( r, cipher + 8, 8 * n );
 
-    if (!(key = key_symmetric_create( ALG_ID_AES, CHAIN_MODE_ECB, 16, secret, secret_len ))) return STATUS_NO_MEMORY;
+    if ((status = alloc_key( ALG_ID_AES, 0, &key ))) return status;
+    if ((status = alloc_aes_key( key, CHAIN_MODE_ECB, BLOCK_LENGTH_AES, secret, secret_len )))
+    {
+        destroy_key( key );
+        return status;
+    }
 
     for (j = 5; j >= 0; j--)
     {
@@ -1434,21 +1911,21 @@ static NTSTATUS aes_unwrap( const UCHAR *secret, ULONG secret_len, const UCHAR *
             b[4] ^= t >> 24;
 
             memcpy( b + 8, r, 8 );
-            key_symmetric_decrypt( key, b, 16, NULL, NULL, 0, b, 16, &len, 0 );
+            decrypt_aes_ecb( key, b, 16, b, 16, &len, 0 );
             memcpy( a, b, 8 );
             memcpy( r, b + 8, 8 );
             r -= 8;
         }
     }
 
-    key_destroy( key );
+    destroy_key( key );
 
     for (i = 0; i < 8; i++) if (a[i] != 0xa6) return STATUS_UNSUCCESSFUL;
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_import( struct algorithm *alg, struct key *decrypt_key, const WCHAR *type, BCRYPT_KEY_HANDLE *key,
-                            UCHAR *object, ULONG object_len, UCHAR *input, ULONG input_len )
+static NTSTATUS import_key( const struct algorithm *alg, const struct key *decrypt_key, const WCHAR *type,
+                            const UCHAR *input, ULONG input_len, struct key **ret_key )
 {
     ULONG len;
     NTSTATUS status;
@@ -1473,7 +1950,7 @@ static NTSTATUS key_import( struct algorithm *alg, struct key *decrypt_key, cons
         len = header->cbKeyData;
         if (len + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) > input_len) return STATUS_INVALID_PARAMETER;
 
-        return key_symmetric_generate( alg, key, (UCHAR *)&header[1], len );
+        return generate_symmetric_key( alg, (UCHAR *)&header[1], len, ret_key );
     }
     else if (!wcscmp( type, BCRYPT_OPAQUE_KEY_BLOB ))
     {
@@ -1481,7 +1958,7 @@ static NTSTATUS key_import( struct algorithm *alg, struct key *decrypt_key, cons
         len = *(ULONG *)input;
         if (len + sizeof(len) > input_len) return STATUS_INVALID_PARAMETER;
 
-        return key_symmetric_generate( alg, key, input + sizeof(len), len );
+        return generate_symmetric_key( alg, input + sizeof(len), len, ret_key);
     }
     else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB ))
     {
@@ -1492,121 +1969,257 @@ static NTSTATUS key_import( struct algorithm *alg, struct key *decrypt_key, cons
         len = input_len - 8;
         if (len < BLOCK_LENGTH_AES || len & (BLOCK_LENGTH_AES - 1)) return STATUS_INVALID_PARAMETER;
 
-        if ((status = aes_unwrap( decrypt_key->u.s.secret, decrypt_key->u.s.secret_len, input, len, output )))
+        if ((status = unwrap_aes( decrypt_key->s.secret, decrypt_key->s.secret_len, input, len, output )))
             return status;
 
-        return key_symmetric_generate( alg, key, output, len );
+        return generate_symmetric_key( alg, output, len, ret_key );
     }
 
-    FIXME( "unsupported key type %s\n", debugstr_w(type) );
+    FIXME( "unsupported blob type %s\n", debugstr_w(type) );
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static NTSTATUS key_symmetric_encrypt( struct key *key,  UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
-                                       ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key_handle, const WCHAR *type,
+                                 BCRYPT_KEY_HANDLE *ret_handle, UCHAR *object, ULONG object_len, UCHAR *input,
+                                 ULONG input_len, ULONG flags )
 {
-    struct key_symmetric_set_auth_data_params auth_params;
-    struct key_symmetric_encrypt_params encrypt_params;
-    struct key_symmetric_get_tag_params tag_params;
-    ULONG bytes_left = input_len;
-    UCHAR *buf;
+    const struct algorithm *alg = get_alg_object( handle );
+    struct key *key;
+    const struct key *decrypt_key = NULL;
     NTSTATUS status;
 
-    if (key->u.s.mode == CHAIN_MODE_GCM)
-    {
-        BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *auth_info = padding;
+    TRACE( "%p, %p, %s, %p, %p, %lu, %p, %lu, %#lx\n", handle, decrypt_key_handle, debugstr_w(type), ret_handle,
+           object, object_len, input, input_len, flags );
+
+    if (!alg || !is_symmetric_alg( alg )) return STATUS_INVALID_HANDLE;
+    if (!ret_handle || !type || !input) return STATUS_INVALID_PARAMETER;
+    if (decrypt_key_handle && !(decrypt_key = get_key_object( decrypt_key_handle ))) return STATUS_INVALID_HANDLE;
 
-        if (!auth_info) return STATUS_INVALID_PARAMETER;
-        if (!auth_info->pbNonce) return STATUS_INVALID_PARAMETER;
-        if (!auth_info->pbTag) return STATUS_INVALID_PARAMETER;
-        if (auth_info->cbTag < 12 || auth_info->cbTag > 16) return STATUS_INVALID_PARAMETER;
-        if (auth_info->dwFlags & BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG)
-            FIXME( "call chaining not implemented\n" );
+    if ((status = import_key( alg, decrypt_key, type, input, input_len, &key ))) return status;
+    *ret_handle = key;
 
-        if ((status = key_symmetric_set_vector( key, auth_info->pbNonce, auth_info->cbNonce, TRUE )))
-            return status;
+    TRACE( "returning handle %p\n", *ret_handle );
+    return STATUS_SUCCESS;
+}
 
-        *ret_len = input_len;
-        if (flags & BCRYPT_BLOCK_PADDING) return STATUS_INVALID_PARAMETER;
-        if (input && !output) return STATUS_SUCCESS;
-        if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
+static NTSTATUS encrypt_aes_ecb( const struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
+                                 ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    ULONG bytes_left, offset, block_size = key->s.block_size;
+    UCHAR buf[BLOCK_LENGTH_AES];
 
-        auth_params.key = key;
-        auth_params.auth_data = auth_info->pbAuthData;
-        auth_params.len = auth_info->cbAuthData;
-        if ((status = UNIX_CALL( key_symmetric_set_auth_data, &auth_params ))) return status;
+    if (flags & BCRYPT_BLOCK_PADDING) *ret_len = (input_len + block_size) & ~(block_size - 1);
+    else if (input_len & (block_size - 1)) return STATUS_INVALID_BUFFER_SIZE;
 
-        encrypt_params.key = key;
-        encrypt_params.input = input;
-        encrypt_params.input_len = input_len;
-        encrypt_params.output = output;
-        encrypt_params.output_len = output_len;
-        if ((status = UNIX_CALL( key_symmetric_encrypt, &encrypt_params ))) return status;
+    if (!output) return STATUS_SUCCESS;
+    if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
 
-        tag_params.key = key;
-        tag_params.tag = auth_info->pbTag;
-        tag_params.len = auth_info->cbTag;
-        return UNIX_CALL( key_symmetric_get_tag, &tag_params );
-    }
+    bytes_left = input_len % block_size;
+    offset = input_len - bytes_left;
 
-    *ret_len = input_len;
+    SymCryptEcbEncrypt( SymCryptAesBlockCipher, &key->s.aes.handle, input, output, offset );
 
     if (flags & BCRYPT_BLOCK_PADDING)
-        *ret_len = (input_len + key->u.s.block_size) & ~(key->u.s.block_size - 1);
-    else if (input_len & (key->u.s.block_size - 1))
-        return STATUS_INVALID_BUFFER_SIZE;
+    {
+        memcpy( buf, input + offset, bytes_left );
+        memset( buf + bytes_left, block_size - bytes_left, block_size - bytes_left );
+
+        SymCryptEcbEncrypt( SymCryptAesBlockCipher, &key->s.aes.handle, buf, output + offset, block_size );
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS encrypt_aes_gcm( struct key *key, const UCHAR *input, ULONG input_len,
+                                 const BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *info, UCHAR *output )
+{
+    if (!info || !info->pbNonce || !info->pbTag || info->cbTag < 12 || info->cbTag > 16)
+        return STATUS_INVALID_PARAMETER;
+    if (info->dwFlags & BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG) FIXME( "call chaining not implemented\n" );
+
+    if (input && !input_len) return STATUS_SUCCESS;
+
+    EnterCriticalSection( &key->s.cs );
+
+    SymCryptGcmInit( &key->s.gcm.state, &key->s.gcm.handle, info->pbNonce, info->cbNonce );
+    SymCryptGcmAuthPart( &key->s.gcm.state, info->pbAuthData, info->cbAuthData );
+    SymCryptGcmEncryptPart( &key->s.gcm.state, input, output, input_len );
+    SymCryptGcmEncryptFinal( &key->s.gcm.state, info->pbTag, info->cbTag );
+
+    LeaveCriticalSection( &key->s.cs );
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS encrypt_aes_vector( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *iv, UCHAR *output,
+                                    ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    ULONG bytes_left = input_len, block_size = key->s.block_size;
+    UCHAR buf[BLOCK_LENGTH_AES], *out;
+    const UCHAR *in;
+
+    if (flags & BCRYPT_BLOCK_PADDING) *ret_len = (input_len + block_size) & ~(block_size - 1);
+    else if (input_len & (block_size - 1)) return STATUS_INVALID_BUFFER_SIZE;
 
     if (!output) return STATUS_SUCCESS;
     if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
-    if (key->u.s.mode == CHAIN_MODE_ECB && iv) return STATUS_INVALID_PARAMETER;
-    if ((status = key_symmetric_set_vector( key, iv, iv_len, flags & BCRYPT_BLOCK_PADDING ))) return status;
 
-    encrypt_params.key = key;
-    encrypt_params.input = input;
-    encrypt_params.input_len = key->u.s.block_size;
-    encrypt_params.output = output;
-    encrypt_params.output_len = key->u.s.block_size;
-    while (bytes_left >= key->u.s.block_size)
+    EnterCriticalSection( &key->s.cs );
+
+    if (iv) memcpy( key->s.vector, iv, block_size );
+
+    in = input;
+    out = output;
+    while (bytes_left >= block_size)
     {
-        if ((status = UNIX_CALL( key_symmetric_encrypt, &encrypt_params )))
-            return status;
-        if (key->u.s.mode == CHAIN_MODE_ECB && (status = key_symmetric_set_vector( key, NULL, 0, TRUE )))
-            return status;
-        bytes_left -= key->u.s.block_size;
-        encrypt_params.input += key->u.s.block_size;
-        encrypt_params.output += key->u.s.block_size;
+        if (key->s.mode == CHAIN_MODE_CFB)
+            SymCryptCfbEncrypt( SymCryptAesBlockCipher, 1, &key->s.aes.handle, key->s.vector, in, out, block_size );
+        else
+            SymCryptCbcEncrypt( SymCryptAesBlockCipher, &key->s.aes.handle, key->s.vector, in, out, block_size );
+
+        bytes_left -= block_size;
+        in += block_size;
+        out += block_size;
     }
 
     if (flags & BCRYPT_BLOCK_PADDING)
     {
-        if (!(buf = malloc( key->u.s.block_size ))) return STATUS_NO_MEMORY;
-        memcpy( buf, encrypt_params.input, bytes_left );
-        memset( buf + bytes_left, key->u.s.block_size - bytes_left, key->u.s.block_size - bytes_left );
-        encrypt_params.input = buf;
-        status = UNIX_CALL( key_symmetric_encrypt, &encrypt_params );
-        free( buf );
-    }
+        memcpy( buf, in, bytes_left );
+        memset( buf + bytes_left, block_size - bytes_left, block_size - bytes_left );
 
-    if (!status)
-    {
-        if (key->u.s.vector && *ret_len >= key->u.s.vector_len)
-        {
-            memcpy( key->u.s.vector, output + *ret_len - key->u.s.vector_len, key->u.s.vector_len );
-            if (iv) memcpy( iv, key->u.s.vector, min( iv_len, key->u.s.vector_len ));
-        }
-        else if (key->u.s.vector)
-            FIXME( "Unexpected vector len %lu, *ret_len %lu.\n", key->u.s.vector_len, *ret_len );
+        if (key->s.mode == CHAIN_MODE_CFB)
+            SymCryptCfbEncrypt( SymCryptAesBlockCipher, 1, &key->s.aes.handle, key->s.vector, buf, out, block_size );
+        else
+            SymCryptCbcEncrypt( SymCryptAesBlockCipher, &key->s.aes.handle, key->s.vector, buf, out, block_size );
     }
 
-    return status;
+    if (iv) memcpy( iv, key->s.vector, block_size );
+
+    LeaveCriticalSection( &key->s.cs );
+    return STATUS_SUCCESS;
 }
 
-/* AES Key Wrap Algorithm (RFC3394) */
-static NTSTATUS aes_wrap( const UCHAR *secret, ULONG secret_len, const UCHAR *plain, ULONG plain_len, UCHAR *cipher )
+static NTSTATUS encrypt_symmetric( struct key *key, const UCHAR *input, ULONG input_len,
+                                   const BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *info, UCHAR *iv, ULONG iv_len,
+                                   UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
 {
-    UCHAR *a, *r, b[16];
-    ULONG len, t, i, j, n = plain_len / 8;
-    struct key *key;
+    *ret_len = input_len;
+
+    if (key->s.mode == CHAIN_MODE_ECB && iv) return STATUS_INVALID_PARAMETER;
+    if (key->s.mode == CHAIN_MODE_GCM && (flags & BCRYPT_BLOCK_PADDING)) return STATUS_INVALID_PARAMETER;
+
+    switch (key->alg_id)
+    {
+    case ALG_ID_AES:
+        switch (key->s.mode)
+        {
+        case CHAIN_MODE_ECB:
+            return encrypt_aes_ecb( key, input, input_len, output, output_len, ret_len, flags );
+
+        case CHAIN_MODE_GCM:
+            return encrypt_aes_gcm( key, input, input_len, info, output );
+
+        default:
+            if (iv && iv_len != key->s.block_size) return STATUS_INVALID_PARAMETER;
+            return encrypt_aes_vector( key, input, input_len, iv, output, output_len, ret_len, flags );
+        }
+    default:
+        FIXME( "unhandled algorithm %u\n", key->alg_id );
+        break;
+    }
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS encrypt_rsa( const struct key *key, const UCHAR *input, ULONG input_len, const void *padding,
+                             UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    SIZE_T size = len_from_bitlen( key->a.bitlen );
+
+    *ret_len = size;
+    if (output_len < size)
+    {
+        if (output) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
+
+    if (!flags || flags == BCRYPT_PAD_NONE)
+    {
+        if (input_len % size) return STATUS_INVALID_PARAMETER;
+
+        if (SymCryptRsaRawEncrypt( key->a.rsa.handle, input, input_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, output,
+                                   output_len )) return STATUS_INTERNAL_ERROR;
+    }
+    else if (flags == BCRYPT_PAD_PKCS1)
+    {
+        if (SymCryptRsaPkcs1Encrypt( key->a.rsa.handle, input, input_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, output,
+                                     output_len, &size )) return STATUS_INTERNAL_ERROR;
+        *ret_len = size;
+    }
+    else if (flags == BCRYPT_PAD_OAEP)
+    {
+        const BCRYPT_OAEP_PADDING_INFO *pad = padding;
+        const SYMCRYPT_HASH *hash_desc = SymCryptSha1Algorithm;
+        const BYTE *label = NULL;
+        SIZE_T label_len = 0;
+
+        if (pad)
+        {
+            hash_desc = get_hash_from_str( pad->pszAlgId );
+            label = pad->pbLabel;
+            label_len = pad->cbLabel;
+        }
+        if (SymCryptRsaOaepEncrypt( key->a.rsa.handle, input, input_len, hash_desc, label, label_len, 0,
+                                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, output, output_len, &size ))
+            return STATUS_INTERNAL_ERROR;
+        *ret_len = size;
+    }
+    else
+    {
+        FIXME( "unsupported flags %#lx", flags );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS WINAPI BCryptEncrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
+                               ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    struct key *key = get_key_object( handle );
+    NTSTATUS status;
+
+    TRACE( "%p, %p, %lu, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, input, input_len, padding, iv, iv_len, output,
+           output_len, ret_len, flags );
+
+    if (!key) return STATUS_INVALID_HANDLE;
+
+    if (is_symmetric_key( key ))
+    {
+        if (flags & ~BCRYPT_BLOCK_PADDING)
+        {
+            FIXME( "flags %#lx not implemented\n", flags );
+            return STATUS_NOT_IMPLEMENTED;
+        }
+
+        status = encrypt_symmetric( key, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags );
+    }
+    else
+    {
+        if (key->alg_id != ALG_ID_RSA) return STATUS_NOT_SUPPORTED;
+        if (!(key->flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
+
+        status = encrypt_rsa( key, input, input_len, padding, output, output_len, ret_len, flags );
+    }
+    return status;
+}
+
+/* AES Key Wrap Algorithm (RFC3394) */
+static NTSTATUS wrap_aes( const UCHAR *secret, ULONG secret_len, const UCHAR *plain, ULONG plain_len, UCHAR *cipher )
+{
+    UCHAR *a, *r, b[16];
+    ULONG len = 16, t, i, j, n = plain_len / 8;
+    NTSTATUS status;
+    struct key *key;
 
     a = cipher;
     r = cipher + 8;
@@ -1614,7 +2227,12 @@ static NTSTATUS aes_wrap( const UCHAR *secret, ULONG secret_len, const UCHAR *pl
     memset( a, 0xa6, 8 );
     memcpy( r, plain, 8 * n );
 
-    if (!(key = key_symmetric_create( ALG_ID_AES, CHAIN_MODE_ECB, 16, secret, secret_len ))) return STATUS_NO_MEMORY;
+    if ((status = alloc_key( ALG_ID_AES, 0, &key ))) return status;
+    if ((status = alloc_aes_key( key, CHAIN_MODE_ECB, BLOCK_LENGTH_AES, secret, secret_len )))
+    {
+        destroy_key( key );
+        return status;
+    }
 
     for (j = 0; j <= 5; j++)
     {
@@ -1623,7 +2241,7 @@ static NTSTATUS aes_wrap( const UCHAR *secret, ULONG secret_len, const UCHAR *pl
         {
             memcpy( b, a, 8 );
             memcpy( b + 8, r, 8 );
-            key_symmetric_encrypt( key, b, 16, NULL, NULL, 0, b, 16, &len, 0 );
+            encrypt_aes_ecb( key, b, 16, b, 16, &len, 0 );
             memcpy( a, b, 8 );
             t = n * j + i;
             a[7] ^= t;
@@ -1635,14 +2253,13 @@ static NTSTATUS aes_wrap( const UCHAR *secret, ULONG secret_len, const UCHAR *pl
         }
     }
 
-    key_destroy( key );
+    destroy_key( key );
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_export( struct key *key, struct key *encrypt_key, const WCHAR *type, UCHAR *output,
-                            ULONG output_len, ULONG *size )
+static NTSTATUS export_symmetric_key( const struct key *key, const struct key *encrypt_key, const WCHAR *type,
+                                      UCHAR *output, ULONG output_len, ULONG *ret_len )
 {
-    struct key_asymmetric_export_params params;
     NTSTATUS status;
 
     if (encrypt_key && wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB ))
@@ -1654,634 +2271,1045 @@ static NTSTATUS key_export( struct key *key, struct key *encrypt_key, const WCHA
     if (!wcscmp( type, BCRYPT_KEY_DATA_BLOB ))
     {
         BCRYPT_KEY_DATA_BLOB_HEADER *header = (BCRYPT_KEY_DATA_BLOB_HEADER *)output;
-        ULONG req_size = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key->u.s.secret_len;
+        ULONG size = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key->s.secret_len;
 
-        *size = req_size;
-        if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL;
+        *ret_len = size;
         if (output)
         {
+            if (output_len < size) return STATUS_BUFFER_TOO_SMALL;
             header->dwMagic   = BCRYPT_KEY_DATA_BLOB_MAGIC;
             header->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
-            header->cbKeyData = key->u.s.secret_len;
-            memcpy( &header[1], key->u.s.secret, key->u.s.secret_len );
+            header->cbKeyData = key->s.secret_len;
+            memcpy( &header[1], key->s.secret, key->s.secret_len );
         }
         return STATUS_SUCCESS;
     }
     else if (!wcscmp( type, BCRYPT_OPAQUE_KEY_BLOB ))
     {
-        ULONG len, req_size = sizeof(len) + key->u.s.secret_len;
+        ULONG size = sizeof(ULONG) + key->s.secret_len;
 
-        *size = req_size;
-        if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL;
+        *ret_len = size;
         if (output)
         {
-            *(ULONG *)output = key->u.s.secret_len;
-            memcpy( output + sizeof(len), key->u.s.secret, key->u.s.secret_len );
+            if (output_len < size) return STATUS_BUFFER_TOO_SMALL;
+            *(ULONG *)output = key->s.secret_len;
+            memcpy( output + sizeof(ULONG), key->s.secret, key->s.secret_len );
         }
         return STATUS_SUCCESS;
     }
-    else if (!wcscmp( type, BCRYPT_DSA_PRIVATE_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB ) ||
-             !wcscmp( type, BCRYPT_ECCPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))
-    {
-        params.key     = key;
-        params.flags   = 0;
-        params.buf     = output;
-        params.len     = output_len;
-        params.ret_len = size;
-        return UNIX_CALL( key_asymmetric_export, &params );
-    }
-    else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB ))
-    {
-        params.key     = key;
-        params.flags   = (wcscmp( type, BCRYPT_RSAPRIVATE_BLOB )) ? KEY_EXPORT_FLAG_RSA_FULL : 0;
-        params.buf     = output;
-        params.len     = output_len;
-        params.ret_len = size;
-        return UNIX_CALL( key_asymmetric_export, &params );
-    }
-    else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB ) ||
-             !wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ) ||
-             !wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ))
-    {
-        params.key     = key;
-        params.flags   = KEY_EXPORT_FLAG_PUBLIC;
-        params.buf     = output;
-        params.len     = output_len;
-        params.ret_len = size;
-        return UNIX_CALL( key_asymmetric_export, &params );
-    }
     else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB ))
     {
-        ULONG req_size = key->u.s.secret_len + 8;
+        ULONG size = key->s.secret_len + 8;
 
         if (!encrypt_key) return STATUS_INVALID_PARAMETER;
 
-        *size = req_size;
+        *ret_len = size;
         if (output)
         {
-            if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL;
-            if ((status = aes_wrap( encrypt_key->u.s.secret, encrypt_key->u.s.secret_len, key->u.s.secret, key->u.s.secret_len, output )))
-                return status;
+            if (output_len < size) return STATUS_BUFFER_TOO_SMALL;
+            if ((status = wrap_aes( encrypt_key->s.secret, encrypt_key->s.secret_len, key->s.secret,
+                                    key->s.secret_len, output ))) return status;
         }
         return STATUS_SUCCESS;
     }
 
-    FIXME( "unsupported key type %s\n", debugstr_w(type) );
+    FIXME( "unsupported blob type %s\n", debugstr_w(type) );
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static NTSTATUS convert_legacy_rsaprivate_blob( struct algorithm *alg, BCRYPT_RSAKEY_BLOB **rsa_data,
-                                                ULONG *rsa_len, UCHAR *input, ULONG input_len )
-{
-    struct {
-        PUBLICKEYSTRUC header;
-        RSAPUBKEY rsapubkey;
-    } *blob = (void *)input;
-    ULONG len, pos;
-    UCHAR *conv;
-    int i;
-
-    if (alg->id != ALG_ID_RSA) return STATUS_NOT_SUPPORTED;
-    if (input_len < sizeof(*blob)) return NTE_BAD_DATA;
-    if (blob->header.bType != PRIVATEKEYBLOB || blob->header.bVersion != CUR_BLOB_VERSION ||
-            blob->header.aiKeyAlg != CALG_RSA_KEYX ||
-            blob->rsapubkey.magic != BCRYPT_RSAPRIVATE_MAGIC ||
-            input_len != sizeof(*blob) + blob->rsapubkey.bitlen / 16 * 9) return NTE_BAD_DATA;
-    if (blob->rsapubkey.bitlen & 0xf) return STATUS_INVALID_PARAMETER;
-
-    len = sizeof(**rsa_data) + sizeof(blob->rsapubkey.pubexp) + blob->rsapubkey.bitlen / 4;
-    if (!(conv = malloc( len ))) return STATUS_NO_MEMORY;
-
-    *rsa_data = (BCRYPT_RSAKEY_BLOB *)conv;
-    (*rsa_data)->Magic = blob->rsapubkey.magic;
-    (*rsa_data)->BitLength = blob->rsapubkey.bitlen;
-    (*rsa_data)->cbPublicExp = sizeof(blob->rsapubkey.pubexp);
-    (*rsa_data)->cbModulus = blob->rsapubkey.bitlen / 8;
-    (*rsa_data)->cbPrime1 = blob->rsapubkey.bitlen / 16;
-    (*rsa_data)->cbPrime2 = blob->rsapubkey.bitlen / 16;
-    len = sizeof(**rsa_data);
-
-    for(i = 0; i < (*rsa_data)->cbPublicExp; i++)
-        conv[len++] = ((UCHAR *)&blob->rsapubkey.pubexp)[(*rsa_data)->cbPublicExp - i - 1];
-    pos = sizeof(*blob);
-    for(i = 0; i < (*rsa_data)->cbModulus; i++)
-        conv[len++] = input[pos + (*rsa_data)->cbModulus - i - 1];
-    pos += (*rsa_data)->cbModulus;
-    for(i = 0; i < (*rsa_data)->cbPrime1; i++)
-        conv[len++] = input[pos + (*rsa_data)->cbPrime1 - i - 1];
-    pos += (*rsa_data)->cbPrime1;
-    for(i = 0; i < (*rsa_data)->cbPrime2; i++)
-        conv[len++] = input[pos + (*rsa_data)->cbPrime2 - i - 1];
-
-    *rsa_len = len;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY_HANDLE *ret_key, UCHAR *input,
-                                 ULONG input_len )
+static NTSTATUS export_rsa_key( const struct key *key, UCHAR *output, ULONG output_len, ULONG flags, ULONG *ret_len )
 {
-    struct key_asymmetric_import_params params;
-    struct key *key;
-    NTSTATUS status;
-    ULONG size;
+    BCRYPT_RSAKEY_BLOB *blob = (BCRYPT_RSAKEY_BLOB *)output;
+    BYTE *mod, *primes[2] = {}, *exponents[2], *coefficient, *private_exp, *ptr = (BYTE *)(blob + 1);
+    UINT64 pubexp;
+    SIZE_T mod_size, pubexp_size = sizeof(pubexp), primes_sizes[2] = {};
+    ULONG size, magic, num_primes = 0;
+
+    pubexp_size = SymCryptRsakeySizeofPublicExponent( key->a.rsa.handle, 0 );
+    mod_size = SymCryptRsakeySizeofModulus( key->a.rsa.handle );
 
-    if (!wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ))
+    if (flags & KEY_EXPORT_FLAG_PRIVATE)
     {
-        BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input;
-        DWORD bitlen, magic, magic_priv;
-        enum ecc_curve_id curve;
+        if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
+        primes_sizes[0] = SymCryptRsakeySizeofPrime( key->a.rsa.handle, 0 );
+        primes_sizes[1] = SymCryptRsakeySizeofPrime( key->a.rsa.handle, 1 );
+        num_primes = 2;
+    }
+    else primes_sizes[0] = primes_sizes[1] = 0;
 
-        if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER;
+    size = sizeof(*blob) + pubexp_size + mod_size + primes_sizes[0] + primes_sizes[1];
+    if (flags & KEY_EXPORT_FLAG_RSA_FULL) size += primes_sizes[0] * 2 + primes_sizes[1] + mod_size;
+    if (output_len < size || !output)
+    {
+        *ret_len = size;
+        if (output) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
 
-        switch (alg->id)
-        {
-        case ALG_ID_ECDH:
-            bitlen = ecc_blob->cbKey * 8;
-            curve = alg->curve_id;
-            magic = BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC;
-            magic_priv = BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC;
-            break;
+    if (flags & KEY_EXPORT_FLAG_RSA_FULL) magic = BCRYPT_RSAFULLPRIVATE_MAGIC;
+    else if (flags & KEY_EXPORT_FLAG_PRIVATE) magic = BCRYPT_RSAPRIVATE_MAGIC;
+    else magic = BCRYPT_RSAPUBLIC_MAGIC;
 
-        case ALG_ID_ECDH_P256:
-            bitlen = 256;
-            curve = ECC_CURVE_P256R1;
-            magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC;
-            magic_priv = BCRYPT_ECDH_PRIVATE_P256_MAGIC;
-            break;
+    blob->Magic       = magic;
+    blob->BitLength   = key->a.bitlen;
+    blob->cbPublicExp = pubexp_size;
+    blob->cbModulus   = mod_size;
+    blob->cbPrime1    = primes_sizes[0];
+    blob->cbPrime2    = primes_sizes[1];
 
-        case ALG_ID_ECDH_P384:
-            bitlen = 384;
-            curve = ECC_CURVE_P384R1;
-            magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC;
-            magic_priv = BCRYPT_ECDH_PRIVATE_P384_MAGIC;
-            break;
+    mod = ptr + pubexp_size;
+    primes[0] = mod + mod_size;
+    primes[1] = mod + mod_size + primes_sizes[0];
 
-        case ALG_ID_ECDH_P521:
-            bitlen = 521;
-            curve = ECC_CURVE_P521R1;
-            magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC;
-            magic_priv = BCRYPT_ECDH_PRIVATE_P521_MAGIC;
-            break;
+    if (SymCryptRsakeyGetValue( key->a.rsa.handle, mod, mod_size, &pubexp, 1, primes, primes_sizes, num_primes,
+                                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0 )) return STATUS_INTERNAL_ERROR;
 
-        case ALG_ID_ECDSA_P256:
-            bitlen = 256;
-            curve = ECC_CURVE_P256R1;
-            magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
-            magic_priv = BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
-            break;
+    memcpy( ptr, &pubexp, pubexp_size );
 
-        case ALG_ID_ECDSA_P384:
-            bitlen = 384;
-            curve = ECC_CURVE_P384R1;
-            magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
-            magic_priv = BCRYPT_ECDSA_PRIVATE_P384_MAGIC;
-            break;
+    if (flags & KEY_EXPORT_FLAG_RSA_FULL)
+    {
+        exponents[0] = primes[1]    + primes_sizes[1];
+        exponents[1] = exponents[0] + primes_sizes[0];
+        coefficient  = exponents[1] + primes_sizes[1];
+        private_exp  = coefficient  + primes_sizes[0];
+
+        if (SymCryptRsakeyGetCrtValue( key->a.rsa.handle, exponents, primes_sizes, 2, coefficient, primes_sizes[0],
+                                       private_exp, mod_size, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0 ))
+            return STATUS_INTERNAL_ERROR;
+    }
 
-        case ALG_ID_ECDSA_P521:
-            bitlen = 521;
-            curve = ECC_CURVE_P521R1;
-            magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC;
-            magic_priv = BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
-            break;
+    *ret_len = size;
+    return STATUS_SUCCESS;
+}
 
-        default:
-            FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) );
-            return STATUS_NOT_SUPPORTED;
-        }
+static DWORD get_ecc_magic( enum alg_id alg, BOOL private )
+{
+    switch (alg)
+    {
+    case ALG_ID_ECDH:       return private ? BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC  : BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC;
+    case ALG_ID_ECDH_P256:  return private ? BCRYPT_ECDH_PRIVATE_P256_MAGIC     : BCRYPT_ECDH_PUBLIC_P256_MAGIC;
+    case ALG_ID_ECDH_P384:  return private ? BCRYPT_ECDH_PRIVATE_P384_MAGIC     : BCRYPT_ECDH_PUBLIC_P384_MAGIC;
+    case ALG_ID_ECDH_P521:  return private ? BCRYPT_ECDH_PRIVATE_P521_MAGIC     : BCRYPT_ECDH_PUBLIC_P521_MAGIC;
+
+    case ALG_ID_ECDSA:      return private ? BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC : BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC;
+    case ALG_ID_ECDSA_P256: return private ? BCRYPT_ECDSA_PRIVATE_P256_MAGIC    : BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
+    case ALG_ID_ECDSA_P384: return private ? BCRYPT_ECDSA_PRIVATE_P384_MAGIC    : BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
+    case ALG_ID_ECDSA_P521: return private ? BCRYPT_ECDSA_PRIVATE_P521_MAGIC    : BCRYPT_ECDSA_PUBLIC_P521_MAGIC;
+    default:
+        FIXME( "unhandled algorithm %u\n", alg );
+        return 0;
+    }
+}
 
-        if (ecc_blob->dwMagic != magic && ecc_blob->dwMagic != magic_priv) return STATUS_INVALID_PARAMETER;
-        if (ecc_blob->cbKey != len_from_bitlen( bitlen ) || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 2)
-            return STATUS_INVALID_PARAMETER;
+static NTSTATUS export_ecc_key( const struct key *key, UCHAR *output, ULONG output_len, ULONG flags, ULONG *ret_len )
+{
+    BCRYPT_ECCKEY_BLOB *blob = (BCRYPT_ECCKEY_BLOB *)output;
+    UINT32 pub_size = SymCryptEckeySizeofPublicKey( key->a.ecc.handle, SYMCRYPT_ECPOINT_FORMAT_XY ), priv_size = 0;
+    UCHAR *pub = (UCHAR *)(blob + 1), *priv = NULL;
+    ULONG size, key_size = len_from_bitlen( key->a.bitlen );
 
-        if ((status = key_asymmetric_create( alg->id, curve, bitlen, &key ))) return status;
-        params.key   = key;
-        params.flags = KEY_IMPORT_FLAG_PUBLIC;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
+    size = sizeof(*blob) + key_size * 2;
+    if (flags & KEY_EXPORT_FLAG_PRIVATE)
+    {
+        if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
+        priv_size = SymCryptEckeySizeofPrivateKey( key->a.ecc.handle );
+        priv = pub + key_size * 2;
+        size += key_size;
     }
-    else if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB ))
+    if (output_len < size || !output)
     {
-        BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input;
-        DWORD bitlen, magic;
-        enum ecc_curve_id curve;
+        *ret_len = size;
+        if (output) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
 
-        if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER;
+    blob->dwMagic = get_ecc_magic( key->alg_id, flags & KEY_EXPORT_FLAG_PRIVATE );
+    blob->cbKey   = key_size;
 
-        switch (alg->id)
-        {
-        case ALG_ID_ECDH:
-            bitlen = ecc_blob->cbKey * 8;
-            curve = alg->curve_id;
-            magic = BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC;
-            break;
+    if (SymCryptEckeyGetValue( key->a.ecc.handle, priv, priv_size, pub, pub_size, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                               SYMCRYPT_ECPOINT_FORMAT_XY, 0 )) return STATUS_INTERNAL_ERROR;
+    *ret_len = size;
+    return STATUS_SUCCESS;
+}
 
-        case ALG_ID_ECDH_P256:
-            bitlen = 256;
-            curve = ECC_CURVE_P256R1;
-            magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC;
-            break;
+static NTSTATUS export_dsa_key( const struct key *key, UCHAR *output, ULONG output_len, ULONG flags, ULONG *ret_len )
+{
+    BCRYPT_DSA_KEY_BLOB *blob = (BCRYPT_DSA_KEY_BLOB *)output;
+    UINT32 key_size = len_from_bitlen( key->a.bitlen ), priv_size = 0;
+    UCHAR *prime, *generator, *pub, *priv = NULL;
+    ULONG size;
 
-        case ALG_ID_ECDH_P384:
-            bitlen = 384;
-            curve = ECC_CURVE_P384R1;
-            magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC;
-            break;
+    size = sizeof(*blob) + key_size * 3;
+    if (flags & KEY_EXPORT_FLAG_PRIVATE)
+    {
+        if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
+        priv_size = 20;
+        size += priv_size;
+    }
+    if (output_len < size || !output)
+    {
+        *ret_len = size;
+        if (output) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
 
-        case ALG_ID_ECDH_P521:
-            bitlen = 521;
-            curve = ECC_CURVE_P521R1;
-            magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC;
-            break;
+    blob->dwMagic = (flags & KEY_EXPORT_FLAG_PRIVATE) ? BCRYPT_DSA_PRIVATE_MAGIC : BCRYPT_DSA_PUBLIC_MAGIC;
+    blob->cbKey   = key_size;
 
-        case ALG_ID_ECDSA_P256:
-            bitlen = 256;
-            curve = ECC_CURVE_P256R1;
-            magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
-            break;
+    prime = (UCHAR *)(blob + 1);
+    generator = prime + key_size;
 
-        case ALG_ID_ECDSA_P384:
-            bitlen = 384;
-            curve = ECC_CURVE_P384R1;
-            magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC;
-            break;
+    if (SymCryptDlgroupGetValue( key->a.dsa.group, prime, key_size, blob->q, sizeof(blob->q), generator, key_size,
+                                 SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, NULL, blob->Seed, sizeof(blob->Seed),
+                                 (UINT32 *)blob->Count )) return STATUS_INTERNAL_ERROR;
+    pub = generator + key_size;
+    if (flags & KEY_EXPORT_FLAG_PRIVATE) priv = pub + key_size;
 
-        case ALG_ID_ECDSA_P521:
-            bitlen = 521;
-            curve = ECC_CURVE_P521R1;
-            magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
-            break;
+    if (SymCryptDlkeyGetValue( key->a.dsa.handle, priv, priv_size, pub, key_size, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                               0 )) return STATUS_INTERNAL_ERROR;
+    *ret_len = size;
+    return STATUS_SUCCESS;
+}
 
-        default:
-            FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) );
-            return STATUS_NOT_SUPPORTED;
-        }
+static NTSTATUS export_legacy_dsa_key( const struct key *key, UCHAR *output, ULONG output_len, ULONG flags,
+                                       ULONG *ret_len )
+{
+    BLOBHEADER *hdr = (BLOBHEADER *)output;
+    DSSPUBKEY *dsskey = (DSSPUBKEY *)(hdr + 1);
+    UINT32 key_size = len_from_bitlen( key->a.bitlen ), pub_size = 0, priv_size = 0;
+    UCHAR *prime_p, *prime_q, *generator, *pub = NULL, *priv = NULL, *seed, *count;
+    ULONG size;
 
-        if (ecc_blob->dwMagic != magic) return STATUS_INVALID_PARAMETER;
-        if (ecc_blob->cbKey != len_from_bitlen( bitlen ) || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 3)
-            return STATUS_INVALID_PARAMETER;
+    if ((flags & KEY_EXPORT_FLAG_PRIVATE) && !(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
 
-        if ((status = key_asymmetric_create( alg->id, curve, bitlen, &key ))) return status;
-        params.key   = key;
-        params.flags = 0;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
-    }
-    else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ))
+    size = sizeof(*hdr) + sizeof(*dsskey) + key_size * 2 + 20 /* q */ + 20 /* x or y */ + 20 /* seed */ + 4 /* count */;
+    if (output_len < size || !output)
     {
-        BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)input;
+        *ret_len = size;
+        if (output) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
 
-        if (input_len < sizeof(*rsa_blob)) return STATUS_INVALID_PARAMETER;
-        if ((alg->id != ALG_ID_RSA && alg->id != ALG_ID_RSA_SIGN) || rsa_blob->Magic != BCRYPT_RSAPUBLIC_MAGIC)
-            return STATUS_NOT_SUPPORTED;
+    hdr->bType    = (flags & KEY_EXPORT_FLAG_PRIVATE) ? PRIVATEKEYBLOB : PUBLICKEYBLOB;
+    hdr->bVersion = 2;
+    hdr->reserved = 0;
+    hdr->aiKeyAlg = CALG_DSS_SIGN;
 
-        size = sizeof(*rsa_blob) + rsa_blob->cbPublicExp + rsa_blob->cbModulus;
-        if (size != input_len) return NTE_BAD_DATA;
+    dsskey->magic  = (flags & KEY_EXPORT_FLAG_PRIVATE) ? MAGIC_DSS2 : MAGIC_DSS1;
+    dsskey->bitlen = key->a.bitlen;
 
-        if ((status = key_asymmetric_create( alg->id, 0, rsa_blob->BitLength, &key ))) return status;
-        params.key   = key;
-        params.flags = KEY_IMPORT_FLAG_PUBLIC;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
-    }
-    else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB ))
-    {
-        BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)input;
+    prime_p = (UCHAR *)(dsskey + 1);
+    prime_q = prime_p + key_size;
+    generator = prime_q + 20;
+    seed = generator + key_size + 20;
+    count = seed + 20;
 
-        if (input_len < sizeof(*rsa_blob)) return STATUS_INVALID_PARAMETER;
-        if (alg->id != ALG_ID_RSA || (rsa_blob->Magic != BCRYPT_RSAPRIVATE_MAGIC &&
-            rsa_blob->Magic != BCRYPT_RSAFULLPRIVATE_MAGIC)) return STATUS_NOT_SUPPORTED;
+    if (SymCryptDlgroupGetValue( key->a.dsa.group, prime_p, key_size, prime_q, 20, generator, key_size,
+                                 SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, NULL, seed, 20, (UINT32 *)count ))
+        return STATUS_INTERNAL_ERROR;
 
-        if ((status = key_asymmetric_create( alg->id, 0, rsa_blob->BitLength, &key ))) return status;
-        params.key   = key;
-        params.flags = 0;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
-    }
-    else if (!wcscmp( type, LEGACY_RSAPRIVATE_BLOB ))
+    if (flags & KEY_EXPORT_FLAG_PRIVATE)
     {
-        BCRYPT_RSAKEY_BLOB *rsa_blob;
-
-        status = convert_legacy_rsaprivate_blob( alg, &rsa_blob, &input_len, input, input_len );
-        if (status != STATUS_SUCCESS)
-                return status;
-
-        status = key_import_pair( alg, BCRYPT_RSAPRIVATE_BLOB, ret_key, (UCHAR *)rsa_blob, input_len );
-        SecureZeroMemory( rsa_blob, input_len );
-        free( rsa_blob );
-        return status;
+        priv = generator + key_size;
+        priv_size = 20;
     }
-    else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB ))
+    else
     {
-        BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)input;
-
-        if (input_len < sizeof(*dsa_blob)) return STATUS_INVALID_PARAMETER;
-        if (alg->id != ALG_ID_DSA || dsa_blob->dwMagic != BCRYPT_DSA_PUBLIC_MAGIC)
-            return STATUS_NOT_SUPPORTED;
-
-        if ((status = key_asymmetric_create( alg->id, 0, dsa_blob->cbKey * 8, &key ))) return status;
-        params.key   = key;
-        params.flags = KEY_IMPORT_FLAG_PUBLIC;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
+        pub = generator + key_size;
+        pub_size = 20;
     }
-    else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB ))
-    {
-        BLOBHEADER *hdr = (BLOBHEADER *)input;
-        DSSPUBKEY *pubkey;
-
-        if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER;
-
-        if (hdr->bType != PRIVATEKEYBLOB && hdr->bVersion != 2 && hdr->aiKeyAlg != CALG_DSS_SIGN)
-        {
-            FIXME( "blob type %u version %u alg id %u not supported\n", hdr->bType, hdr->bVersion, hdr->aiKeyAlg );
-            return STATUS_NOT_SUPPORTED;
-        }
-        if (alg->id != ALG_ID_DSA)
-        {
-            FIXME( "algorithm %u does not support importing blob of type %s\n", alg->id, debugstr_w(type) );
-            return STATUS_NOT_SUPPORTED;
-        }
 
-        if (input_len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER;
-        pubkey = (DSSPUBKEY *)(hdr + 1);
-        if (pubkey->magic != MAGIC_DSS2) return STATUS_NOT_SUPPORTED;
+    if (SymCryptDlkeyGetValue( key->a.dsa.handle, priv, priv_size, pub, pub_size, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST,
+                               0 )) return STATUS_INTERNAL_ERROR;
+    *ret_len = size;
+    return STATUS_SUCCESS;
+}
 
-        if (input_len < sizeof(*hdr) + sizeof(*pubkey) + len_from_bitlen( pubkey->bitlen ) * 2 + 40 + sizeof(DSSSEED))
-            return STATUS_INVALID_PARAMETER;
+static NTSTATUS export_dh_key( const struct key *key, UCHAR *output, ULONG output_len, ULONG flags, ULONG *ret_len )
+{
+    BCRYPT_DH_KEY_BLOB *blob = (BCRYPT_DH_KEY_BLOB *)output;
+    UCHAR *modulus, *generator, *pub, *priv = NULL;
+    ULONG size, priv_size = 0, key_size = len_from_bitlen( key->a.bitlen );
 
-        if ((status = key_asymmetric_create( alg->id, 0, pubkey->bitlen, &key ))) return status;
-        key->u.a.flags |= KEY_FLAG_LEGACY_DSA_V2;
-        params.key   = key;
-        params.flags = 0;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
+    size = sizeof(*blob) + key_size * 3;
+    if (flags & KEY_EXPORT_FLAG_PRIVATE)
+    {
+        if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
+        priv_size = key_size;
+        size += key_size;
     }
-    else if (!wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB )) /* not supported on native */
+    if (output_len < size || !output)
     {
-        BLOBHEADER *hdr = (BLOBHEADER *)input;
-        DSSPUBKEY *pubkey;
+        *ret_len = size;
+        if (output) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
 
-        if (alg->id != ALG_ID_DSA) return STATUS_NOT_SUPPORTED;
-        if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER;
+    blob->dwMagic = (flags & KEY_EXPORT_FLAG_PRIVATE) ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC;
+    blob->cbKey   = key_size;
 
-        if (hdr->bType != PUBLICKEYBLOB && hdr->bVersion != 2 && hdr->aiKeyAlg != CALG_DSS_SIGN)
-        {
-            FIXME( "blob type %u version %u alg id %u not supported\n", hdr->bType, hdr->bVersion, hdr->aiKeyAlg );
-            return STATUS_NOT_SUPPORTED;
-        }
+    modulus = (UCHAR *)(blob + 1);
+    generator = modulus + key_size;
+
+    if (SymCryptDlgroupGetValue( key->a.dsa.group, modulus, key_size, NULL, 0, generator, key_size,
+                                 SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, NULL, NULL, 0, 0 )) return STATUS_INTERNAL_ERROR;
+    pub = generator + key_size;
+    if (flags & KEY_EXPORT_FLAG_PRIVATE) priv = pub + key_size;
 
-        if (input_len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER;
-        pubkey = (DSSPUBKEY *)(hdr + 1);
-        if (pubkey->magic != MAGIC_DSS1) return STATUS_NOT_SUPPORTED;
+    if (SymCryptDlkeyGetValue( key->a.dsa.handle, priv, priv_size, pub, key_size,
+                               SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0 )) return STATUS_INTERNAL_ERROR;
+    *ret_len = size;
+    return STATUS_SUCCESS;
+}
 
-        size = sizeof(*hdr) + sizeof(*pubkey) + len_from_bitlen( pubkey->bitlen ) * 3 + 20 + sizeof(DSSSEED);
-        if (input_len < size) return STATUS_INVALID_PARAMETER;
+static NTSTATUS export_asymmetric_key( const struct key *key, const WCHAR *type, UCHAR *output, ULONG output_len,
+                                       ULONG *ret_len )
+{
+    if (!(key->flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
 
-        if ((status = key_asymmetric_create( alg->id, 0, pubkey->bitlen, &key ))) return status;
-        key->u.a.flags |= KEY_FLAG_LEGACY_DSA_V2;
-        params.key   = key;
-        params.flags = KEY_IMPORT_FLAG_PUBLIC;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
+    if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB ))
+    {
+        return export_ecc_key( key, output, output_len, KEY_EXPORT_FLAG_PRIVATE, ret_len );
+    }
+    else if (!wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ))
+    {
+        return export_ecc_key( key, output, output_len, 0, ret_len );
+    }
+    else if (!wcscmp( type, BCRYPT_DSA_PRIVATE_BLOB ))
+    {
+        return export_dsa_key( key, output, output_len, KEY_EXPORT_FLAG_PRIVATE, ret_len );
+    }
+    else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB ))
+    {
+        return export_dsa_key( key, output, output_len, 0, ret_len );
     }
     else if (!wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))
     {
-        BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)input;
-
-        if (input_len != sizeof(*dh_blob) + dh_blob->cbKey * 4) return STATUS_INVALID_PARAMETER;
-        if (alg->id != ALG_ID_DH || dh_blob->dwMagic != BCRYPT_DH_PRIVATE_MAGIC)
-            return STATUS_NOT_SUPPORTED;
-
-        if ((status = key_asymmetric_create( alg->id, 0, dh_blob->cbKey * 8, &key ))) return status;
-        params.key   = key;
-        params.flags = 0;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
+        return export_dh_key( key, output, output_len, KEY_EXPORT_FLAG_PRIVATE, ret_len );
     }
     else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ))
     {
-        BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)input;
-
-        if (input_len != sizeof(*dh_blob) + dh_blob->cbKey * 3) return STATUS_INVALID_PARAMETER;
-        if (alg->id != ALG_ID_DH || dh_blob->dwMagic != BCRYPT_DH_PUBLIC_MAGIC)
-            return STATUS_NOT_SUPPORTED;
-
-        if ((status = key_asymmetric_create( alg->id, 0, dh_blob->cbKey * 8, &key ))) return status;
-        params.key   = key;
-        params.flags = KEY_IMPORT_FLAG_PUBLIC;
-        params.buf   = input;
-        params.len   = input_len;
-        if ((status = UNIX_CALL( key_asymmetric_import, &params )))
-        {
-            key_destroy( key );
-            return status;
-        }
+        return export_dh_key( key, output, output_len, 0, ret_len );
     }
-    else
+    else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ))
     {
-        FIXME( "unsupported key type %s\n", debugstr_w(type) );
-        return STATUS_NOT_SUPPORTED;
+        return export_rsa_key( key, output, output_len, KEY_EXPORT_FLAG_PRIVATE, ret_len );
     }
-
-    if (!status)
+    else if (!wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB ))
     {
-        key->u.a.flags |= KEY_FLAG_FINALIZED;
-        *ret_key = key;
+        return export_rsa_key( key, output, output_len, KEY_EXPORT_FLAG_PRIVATE | KEY_EXPORT_FLAG_RSA_FULL, ret_len );
     }
-    return status;
+    else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ))
+    {
+        return export_rsa_key( key, output, output_len, 0, ret_len );
+    }
+    else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB ))
+    {
+        return export_legacy_dsa_key( key, output, output_len, KEY_EXPORT_FLAG_PRIVATE, ret_len );
+    }
+    else if (!wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB ))
+    {
+        return export_legacy_dsa_key( key, output, output_len, 0, ret_len );
+    }
+
+    FIXME( "unsupported blob type %s\n", debugstr_w(type) );
+    return STATUS_NOT_IMPLEMENTED;
 }
 
-NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle,
-                                            UCHAR *object, ULONG object_len, UCHAR *secret, ULONG secret_len,
-                                            ULONG flags )
+NTSTATUS WINAPI BCryptExportKey( BCRYPT_KEY_HANDLE export_key_handle, BCRYPT_KEY_HANDLE encrypt_key_handle,
+                                 const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size, ULONG flags )
 {
-    struct algorithm *alg = get_alg_object( handle );
-    NTSTATUS status;
+    const struct key *key = get_key_object( export_key_handle ), *encrypt_key = NULL;
 
-    TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, ret_handle, object, object_len, secret, secret_len, flags );
-    if (object) FIXME( "ignoring object buffer\n" );
-    if (!__wine_unixlib_handle)
-    {
-        ERR( "no encryption support\n" );
-        return STATUS_NOT_IMPLEMENTED;
+    TRACE( "%p, %p, %s, %p, %lu, %p, %#lx\n", export_key_handle, encrypt_key_handle, debugstr_w(type), output,
+           output_len, size, flags );
+
+    if (!key) return STATUS_INVALID_HANDLE;
+    if (!type || !size) return STATUS_INVALID_PARAMETER;
+    if (encrypt_key_handle && !(encrypt_key = get_key_object( encrypt_key_handle ))) return STATUS_INVALID_HANDLE;
+
+    if (is_symmetric_key( key )) return export_symmetric_key( key, encrypt_key, type, output, output_len, size );
+    return export_asymmetric_key( key, type, output, output_len, size );
+}
+
+static NTSTATUS alloc_rsa_key( struct key *key, ULONG bitlen )
+{
+    SYMCRYPT_RSA_PARAMS params;
+
+    params.version = 1;
+    params.nBitsOfModulus = bitlen;
+    params.nPrimes = (key->flags & KEY_FLAG_PRIVATE) ? 2 : 0;
+    params.nPubExp = 1;
+    if (!(key->a.rsa.handle = SymCryptRsakeyAllocate( &params, 0 ))) return STATUS_NO_MEMORY;
+
+    key->a.bitlen = bitlen;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS import_rsa_key( enum alg_id alg, const UCHAR *input, ULONG input_len, struct key **ret_key )
+{
+    const BCRYPT_RSAKEY_BLOB *blob = (const BCRYPT_RSAKEY_BLOB *)input;
+    const UCHAR *mod, *primes[2] = {}, *exp = (const UCHAR *)(blob + 1);
+    SIZE_T primes_sizes[2] = {};
+    UINT64 exp64 = 0;
+    ULONG key_flags = 0, set_flags = SYMCRYPT_FLAG_KEY_NO_FIPS, size, i, num_primes = 0;
+    NTSTATUS status;
+    struct key *key = NULL;
+
+    if (input_len < sizeof(*blob)) return STATUS_INVALID_PARAMETER;
+
+    if (alg != ALG_ID_RSA && alg != ALG_ID_RSA_SIGN) return STATUS_NOT_SUPPORTED;
+    if (blob->Magic == BCRYPT_RSAPRIVATE_MAGIC || blob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC)
+        key_flags = KEY_FLAG_PRIVATE;
+    else if (blob->Magic != BCRYPT_RSAPUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
+
+    if (blob->cbPublicExp > sizeof(exp64)) return NTE_BAD_DATA;
+
+    size = sizeof(*blob) + blob->cbPublicExp + blob->cbModulus;
+    if (key_flags & KEY_FLAG_PRIVATE) size += blob->cbPrime1 + blob->cbPrime2;
+    if (blob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC) size += blob->cbPrime1 * 2 + blob->cbPrime2 + blob->cbModulus;
+    if (size != input_len) return NTE_BAD_DATA;
+
+    if ((status = alloc_key( alg, key_flags, &key )) || (status = alloc_rsa_key( key, blob->cbModulus * 8 )))
+    {
+        destroy_key( key );
+        return status;
     }
 
-    if (!alg) return STATUS_INVALID_HANDLE;
-    if ((status = key_symmetric_generate( alg, ret_handle, secret, secret_len ))) return status;
-    TRACE( "returning handle %p\n", *ret_handle );
+    if (alg == ALG_ID_RSA) set_flags |= SYMCRYPT_FLAG_RSAKEY_ENCRYPT | SYMCRYPT_FLAG_RSAKEY_SIGN;
+    else if (alg == ALG_ID_RSA_SIGN) set_flags |= SYMCRYPT_FLAG_RSAKEY_SIGN;
+
+    mod = exp + blob->cbPublicExp;
+    for (i = 0; i < blob->cbPublicExp; i++) exp64 += exp[i] << ((blob->cbPublicExp - i - 1) * 8);
+
+    if (key_flags & KEY_FLAG_PRIVATE)
+    {
+        num_primes = 2;
+
+        primes[0] = mod + blob->cbModulus;
+        primes[1] = primes[0] + blob->cbPrime1;
+
+        primes_sizes[0] = blob->cbPrime1;
+        primes_sizes[1] = blob->cbPrime2;
+    }
+
+    if (SymCryptRsakeySetValue( mod, blob->cbModulus, &exp64, 1, primes, primes_sizes, num_primes,
+                                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, set_flags, key->a.rsa.handle ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    *ret_key = key;
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle, ULONG key_len,
-                                       ULONG flags )
+static NTSTATUS import_legacy_rsa_key( enum alg_id alg, const UCHAR *input, ULONG input_len, struct key **ret_key )
 {
-    struct algorithm *alg = get_alg_object( handle );
-    struct key *key;
+    const BLOBHEADER *hdr = (const BLOBHEADER *)input;
+    const RSAPUBKEY *rsakey = (RSAPUBKEY *)(hdr + 1);
+    const UCHAR *mod, *primes[2] = {};
+    SIZE_T primes_sizes[2] = {};
+    UINT64 exp64;
+    ULONG key_size, key_flags = 0, set_flags = SYMCRYPT_FLAG_KEY_NO_FIPS, size, num_primes = 0;
     NTSTATUS status;
+    struct key *key = NULL;
 
-    TRACE( "%p, %p, %lu, %#lx\n", handle, ret_handle, key_len, flags );
+    if (alg != ALG_ID_RSA && alg != ALG_ID_RSA_SIGN) return STATUS_NOT_SUPPORTED;
+    if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER;
 
-    if (!alg) return STATUS_INVALID_HANDLE;
-    if (!ret_handle) return STATUS_INVALID_PARAMETER;
+    if (hdr->bType == PRIVATEKEYBLOB) key_flags |= KEY_FLAG_PRIVATE;
+    else if (hdr->bType != PUBLICKEYBLOB) return STATUS_NOT_SUPPORTED;
+    if (hdr->bVersion != 2 || (hdr->aiKeyAlg != CALG_RSA_KEYX && hdr->aiKeyAlg != CALG_RSA_SIGN))
+        return STATUS_NOT_SUPPORTED;
 
-    if ((status = key_asymmetric_create( alg->id, alg->curve_id, key_len, &key ))) return status;
-    *ret_handle = key;
-    TRACE( "returning handle %p\n", *ret_handle );
+    size = sizeof(*hdr) + sizeof(*rsakey);
+    if (input_len < size || rsakey->bitlen & 0xf) return STATUS_INVALID_PARAMETER;
+
+    key_size = len_from_bitlen( rsakey->bitlen );
+    size += key_size;
+    if (key_flags & KEY_FLAG_PRIVATE) size += key_size; /* prime1 + prime2 */
+    if (input_len < size) return STATUS_INVALID_PARAMETER;
+
+    if ((status = alloc_key( alg, key_flags, &key )) || (status = alloc_rsa_key( key, rsakey->bitlen )))
+    {
+        destroy_key( key );
+        return status;
+    }
+
+    if (alg == ALG_ID_RSA) set_flags |= SYMCRYPT_FLAG_RSAKEY_ENCRYPT | SYMCRYPT_FLAG_RSAKEY_SIGN;
+    else if (alg == ALG_ID_RSA_SIGN) set_flags |= SYMCRYPT_FLAG_RSAKEY_SIGN;
+
+    exp64 = rsakey->pubexp;
+    mod = (const UCHAR *)(rsakey + 1);
+    if (key_flags & KEY_FLAG_PRIVATE)
+    {
+        num_primes = 2;
+
+        primes[0] = mod + key_size;
+        primes[1] = primes[0] + key_size / 2;
+
+        primes_sizes[0] = key_size / 2;
+        primes_sizes[1] = key_size / 2;
+    }
+
+    if (SymCryptRsakeySetValue( mod, key_size, &exp64, 1, primes, primes_sizes, num_primes,
+                                SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, set_flags, key->a.rsa.handle ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    *ret_key = key;
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags )
+static enum ecc_curve_id get_ecc_blob_curve( enum alg_id alg, const BCRYPT_ECCKEY_BLOB *blob, ULONG *size,
+                                             ULONG *flags )
 {
-    struct key *key = get_key_object( handle );
-    NTSTATUS ret;
+    enum ecc_curve_id curve_id = 0;
 
-    TRACE( "%p, %#lx\n", key, flags );
+    *flags = 0;
+    *size = blob->cbKey;
 
-    if (!key || key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE;
+    switch (alg)
+    {
+    case ALG_ID_ECDH:
+        if (blob->dwMagic == BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC) return FALSE;
+        break;
 
-    if (!(ret = UNIX_CALL( key_asymmetric_generate, key ))) key->u.a.flags |= KEY_FLAG_FINALIZED;
-    return ret;
+    case ALG_ID_ECDH_P256:
+        if (blob->dwMagic == BCRYPT_ECDH_PRIVATE_P256_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDH_PUBLIC_P256_MAGIC) return STATUS_INVALID_PARAMETER;
+        curve_id = ECC_CURVE_P256R1;
+        *size = 32;
+        break;
+
+    case ALG_ID_ECDH_P384:
+        if (blob->dwMagic == BCRYPT_ECDH_PRIVATE_P384_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDH_PUBLIC_P384_MAGIC) return STATUS_INVALID_PARAMETER;
+        curve_id = ECC_CURVE_P384R1;
+        *size = 48;
+        break;
+
+    case ALG_ID_ECDH_P521:
+        if (blob->dwMagic == BCRYPT_ECDH_PRIVATE_P521_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDH_PUBLIC_P521_MAGIC) return STATUS_INVALID_PARAMETER;
+        curve_id = ECC_CURVE_P521R1;
+        *size = 66;
+        break;
+
+    case ALG_ID_ECDSA:
+        if (blob->dwMagic == BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC) return STATUS_INVALID_PARAMETER;
+        break;
+
+    case ALG_ID_ECDSA_P256:
+        if (blob->dwMagic == BCRYPT_ECDSA_PRIVATE_P256_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDSA_PUBLIC_P256_MAGIC) return STATUS_INVALID_PARAMETER;
+        curve_id = ECC_CURVE_P256R1;
+        *size = 32;
+        break;
+
+    case ALG_ID_ECDSA_P384:
+        if (blob->dwMagic == BCRYPT_ECDSA_PRIVATE_P384_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDSA_PUBLIC_P384_MAGIC) return STATUS_INVALID_PARAMETER;
+        curve_id = ECC_CURVE_P384R1;
+        *size = 48;
+        break;
+
+    case ALG_ID_ECDSA_P521:
+        if (blob->dwMagic == BCRYPT_ECDSA_PRIVATE_P521_MAGIC) *flags = KEY_FLAG_PRIVATE;
+        else if (blob->dwMagic != BCRYPT_ECDSA_PUBLIC_P521_MAGIC) return STATUS_INVALID_PARAMETER;
+        curve_id = ECC_CURVE_P521R1;
+        *size = 66;
+        break;
+
+    default:
+        ERR( "unhandled algorithm %u\n", alg );
+        return 0;
+    }
+    return curve_id;
 }
 
-NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key_handle, const WCHAR *type,
-                                 BCRYPT_KEY_HANDLE *ret_handle, UCHAR *object, ULONG object_len, UCHAR *input,
-                                 ULONG input_len, ULONG flags )
+static UINT32 get_ecc_import_flags( enum alg_id alg )
 {
-    struct algorithm *alg = get_alg_object( handle );
-    struct key *decrypt_key = NULL;
+    UINT32 flags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION;
+
+    switch (alg)
+    {
+    case ALG_ID_ECDH:
+    case ALG_ID_ECDH_P256:
+    case ALG_ID_ECDH_P384:
+    case ALG_ID_ECDH_P521:
+        flags |= SYMCRYPT_FLAG_ECKEY_ECDH;
+        break;
+
+    case ALG_ID_ECDSA:
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    case ALG_ID_ECDSA_P521:
+        flags |= SYMCRYPT_FLAG_ECKEY_ECDSA;
+        break;
+
+    default:
+        FIXME( "unhandled algorithm %u\n", alg );
+        break;
+    }
+    return flags;
+}
+
+static const BYTE brainpoolP256r1[] =
+{
+    /* version */
+    0x01, 0x00, 0x00, 0x00,
+    /* type */
+    0x01, 0x00, 0x00, 0x00,
+    /* algId */
+    0x00, 0x00, 0x00, 0x00,
+    /* cbFieldLength */
+    0x20, 0x00, 0x00, 0x00,
+    /* cbSubgroupOrder */
+    0x20, 0x00, 0x00, 0x00,
+    /* cbCofactor */
+    0x01, 0x00, 0x00, 0x00,
+    /* cbSeed */
+    0x00, 0x00, 0x00, 0x00,
+    /* P */
+    0xa9, 0xfb, 0x57, 0xdb, 0xa1, 0xee, 0xa9, 0xbc,
+    0x3e, 0x66, 0x0a, 0x90, 0x9d, 0x83, 0x8d, 0x72,
+    0x6e, 0x3b, 0xf6, 0x23, 0xd5, 0x26, 0x20, 0x28,
+    0x20, 0x13, 0x48, 0x1d, 0x1f, 0x6e, 0x53, 0x77,
+    /* A */
+    0x7d, 0x5a, 0x09, 0x75, 0xfc, 0x2c, 0x30, 0x57,
+    0xee, 0xf6, 0x75, 0x30, 0x41, 0x7a, 0xff, 0xe7,
+    0xfb, 0x80, 0x55, 0xc1, 0x26, 0xdc, 0x5c, 0x6c,
+    0xe9, 0x4a, 0x4b, 0x44, 0xf3, 0x30, 0xb5, 0xd9,
+    /* B */
+    0x26, 0xdc, 0x5c, 0x6c, 0xe9, 0x4a, 0x4b, 0x44,
+    0xf3, 0x30, 0xb5, 0xd9, 0xbb, 0xd7, 0x7c, 0xbf,
+    0x95, 0x84, 0x16, 0x29, 0x5c, 0xf7, 0xe1, 0xce,
+    0x6b, 0xcc, 0xdc, 0x18, 0xff, 0x8c, 0x07, 0xb6,
+    /* Gx */
+    0x8b, 0xd2, 0xae, 0xb9, 0xcb, 0x7e, 0x57, 0xcb,
+    0x2c, 0x4b, 0x48, 0x2f, 0xfc, 0x81, 0xb7, 0xaf,
+    0xb9, 0xde, 0x27, 0xe1, 0xe3, 0xbd, 0x23, 0xc2,
+    0x3a, 0x44, 0x53, 0xbd, 0x9a, 0xce, 0x32, 0x62,
+    /* Gy */
+    0x54, 0x7e, 0xf8, 0x35, 0xc3, 0xda, 0xc4, 0xfd,
+    0x97, 0xf8, 0x46, 0x1a, 0x14, 0x61, 0x1d, 0xc9,
+    0xc2, 0x77, 0x45, 0x13, 0x2d, 0xed, 0x8e, 0x54,
+    0x5c, 0x1d, 0x54, 0xc7, 0x2f, 0x04, 0x69, 0x97,
+    /* n */
+    0xa9, 0xfb, 0x57, 0xdb, 0xa1, 0xee, 0xa9, 0xbc,
+    0x3e, 0x66, 0x0a, 0x90, 0x9d, 0x83, 0x8d, 0x71,
+    0x8c, 0x39, 0x7a, 0xa3, 0xb5, 0x61, 0xa6, 0xf7,
+    0x90, 0x1e, 0x0e, 0x82, 0x97, 0x48, 0x56, 0xa7,
+    /* h */
+    0x01
+};
+const SYMCRYPT_ECURVE_PARAMS *SymCryptEcurveParamsBrainpoolP256r1 = (const SYMCRYPT_ECURVE_PARAMS *)brainpoolP256r1;
+
+static const BYTE brainpoolP384r1[] =
+{
+    /* version */
+    0x01, 0x00, 0x00, 0x00,
+    /* type */
+    0x01, 0x00, 0x00, 0x00,
+    /* algId */
+    0x00, 0x00, 0x00, 0x00,
+    /* cbFieldLength */
+    0x30, 0x00, 0x00, 0x00,
+    /* cbSubgroupOrder */
+    0x30, 0x00, 0x00, 0x00,
+    /* cbCofactor */
+    0x01, 0x00, 0x00, 0x00,
+    /* cbSeed */
+    0x00, 0x00, 0x00, 0x00,
+    /* P */
+    0x8c, 0xb9, 0x1e, 0x82, 0xa3, 0x38, 0x6d, 0x28,
+    0x0f, 0x5d, 0x6f, 0x7e, 0x50, 0xe6, 0x41, 0xdf,
+    0x15, 0x2f, 0x71, 0x09, 0xed, 0x54, 0x56, 0xb4,
+    0x12, 0xb1, 0xda, 0x19, 0x7f, 0xb7, 0x11, 0x23,
+    0xac, 0xd3, 0xa7, 0x29, 0x90, 0x1d, 0x1a, 0x71,
+    0x87, 0x47, 0x00, 0x13, 0x31, 0x07, 0xec, 0x53,
+    /* A */
+    0x7b, 0xc3, 0x82, 0xc6, 0x3d, 0x8c, 0x15, 0x0c,
+    0x3c, 0x72, 0x08, 0x0a, 0xce, 0x05, 0xaf, 0xa0,
+    0xc2, 0xbe, 0xa2, 0x8e, 0x4f, 0xb2, 0x27, 0x87,
+    0x13, 0x91, 0x65, 0xef, 0xba, 0x91, 0xf9, 0x0f,
+    0x8a, 0xa5, 0x81, 0x4a, 0x50, 0x3a, 0xd4, 0xeb,
+    0x04, 0xa8, 0xc7, 0xdd, 0x22, 0xce, 0x28, 0x26,
+    /* B */
+    0x04, 0xa8, 0xc7, 0xdd, 0x22, 0xce, 0x28, 0x26,
+    0x8b, 0x39, 0xb5, 0x54, 0x16, 0xf0, 0x44, 0x7c,
+    0x2f, 0xb7, 0x7d, 0xe1, 0x07, 0xdc, 0xd2, 0xa6,
+    0x2e, 0x88, 0x0e, 0xa5, 0x3e, 0xeb, 0x62, 0xd5,
+    0x7c, 0xb4, 0x39, 0x02, 0x95, 0xdb, 0xc9, 0x94,
+    0x3a, 0xb7, 0x86, 0x96, 0xfa, 0x50, 0x4c, 0x11,
+    /* Gx */
+    0x1d, 0x1c, 0x64, 0xf0, 0x68, 0xcf, 0x45, 0xff,
+    0xa2, 0xa6, 0x3a, 0x81, 0xb7, 0xc1, 0x3f, 0x6b,
+    0x88, 0x47, 0xa3, 0xe7, 0x7e, 0xf1, 0x4f, 0xe3,
+    0xdb, 0x7f, 0xca, 0xfe, 0x0c, 0xbd, 0x10, 0xe8,
+    0xe8, 0x26, 0xe0, 0x34, 0x36, 0xd6, 0x46, 0xaa,
+    0xef, 0x87, 0xb2, 0xe2, 0x47, 0xd4, 0xaf, 0x1e,
+    /* Gy */
+    0x8a, 0xbe, 0x1d, 0x75, 0x20, 0xf9, 0xc2, 0xa4,
+    0x5c, 0xb1, 0xeb, 0x8e, 0x95, 0xcf, 0xd5, 0x52,
+    0x62, 0xb7, 0x0b, 0x29, 0xfe, 0xec, 0x58, 0x64,
+    0xe1, 0x9c, 0x05, 0x4f, 0xf9, 0x91, 0x29, 0x28,
+    0x0e, 0x46, 0x46, 0x21, 0x77, 0x91, 0x81, 0x11,
+    0x42, 0x82, 0x03, 0x41, 0x26, 0x3c, 0x53, 0x15,
+    /* n */
+    0x8c, 0xb9, 0x1e, 0x82, 0xa3, 0x38, 0x6d, 0x28,
+    0x0f, 0x5d, 0x6f, 0x7e, 0x50, 0xe6, 0x41, 0xdf,
+    0x15, 0x2f, 0x71, 0x09, 0xed, 0x54, 0x56, 0xb3,
+    0x1f, 0x16, 0x6e, 0x6c, 0xac, 0x04, 0x25, 0xa7,
+    0xcf, 0x3a, 0xb6, 0xaf, 0x6b, 0x7f, 0xc3, 0x10,
+    0x3b, 0x88, 0x32, 0x02, 0xe9, 0x04, 0x65, 0x65,
+    /* h */
+    0x01
+};
+const SYMCRYPT_ECURVE_PARAMS *SymCryptEcurveParamsBrainpoolP384r1 = (const SYMCRYPT_ECURVE_PARAMS *)brainpoolP384r1;
+
+static SYMCRYPT_ECURVE *alloc_ecc_curve( enum ecc_curve_id curve_id )
+{
+    const SYMCRYPT_ECURVE_PARAMS *params;
+
+    switch (curve_id)
+    {
+    case ECC_CURVE_25519:
+        params = SymCryptEcurveParamsCurve25519;
+        break;
+    case ECC_CURVE_BRAINPOOLP256R1:
+        params = SymCryptEcurveParamsBrainpoolP256r1;
+        break;
+    case ECC_CURVE_BRAINPOOLP384R1:
+        params = SymCryptEcurveParamsBrainpoolP384r1;
+        break;
+    case ECC_CURVE_P256R1:
+        params = SymCryptEcurveParamsNistP256;
+        break;
+    case ECC_CURVE_P384R1:
+        params = SymCryptEcurveParamsNistP384;
+        break;
+    case ECC_CURVE_P521R1:
+        params = SymCryptEcurveParamsNistP521;
+        break;
+    default:
+        FIXME( "unhandled curve %u\n", curve_id );
+        return NULL;
+    }
+    return SymCryptEcurveAllocate( params, 0 );
+}
+
+static ULONG curve_strength( enum ecc_curve_id curve_id )
+{
+    switch (curve_id)
+    {
+    case ECC_CURVE_25519:           return 253;
+    case ECC_CURVE_BRAINPOOLP256R1:
+    case ECC_CURVE_P256R1:          return 256;
+    case ECC_CURVE_BRAINPOOLP384R1:
+    case ECC_CURVE_P384R1:          return 384;
+    case ECC_CURVE_P521R1:          return 521;
+    default:
+        FIXME( "unsupported curve %u\n", curve_id );
+        return 0;
+    }
+}
+
+static NTSTATUS alloc_ecc_key( struct key *key, enum ecc_curve_id curve_id )
+{
+    if (!(key->a.ecc.curve = alloc_ecc_curve( curve_id ))) return STATUS_NO_MEMORY;
+    if (!(key->a.ecc.handle = SymCryptEckeyAllocate( key->a.ecc.curve ))) return STATUS_NO_MEMORY;
+    key->a.ecc.curve_id = curve_id;
+    key->a.bitlen = curve_strength( curve_id );
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS import_ecc_key( enum alg_id alg, enum ecc_curve_id curve, const UCHAR *input, ULONG input_len,
+                                struct key **ret_key )
+{
+    const BCRYPT_ECCKEY_BLOB *blob = (const BCRYPT_ECCKEY_BLOB *)input;
+    UINT32 pub_size, priv_size = 0, set_flags = get_ecc_import_flags( alg );
+    ULONG size, key_size, key_flags = 0;
+    const UCHAR *pub = (UCHAR *)(blob + 1), *priv = NULL;
+    enum ecc_curve_id blob_curve;
     NTSTATUS status;
+    struct key *key = NULL;
 
-    TRACE( "%p, %p, %s, %p, %p, %lu, %p, %lu, %#lx\n", handle, decrypt_key_handle, debugstr_w(type), ret_handle,
-           object, object_len, input, input_len, flags );
+    if (input_len < sizeof(*blob)) return STATUS_INVALID_PARAMETER;
+    if (!(blob_curve = get_ecc_blob_curve( alg, blob, &key_size, &key_flags ))) blob_curve = curve;
+    if (blob_curve != curve) return STATUS_INVALID_PARAMETER;
 
-    if (!alg) return STATUS_INVALID_HANDLE;
-    if (!ret_handle || !type || !input) return STATUS_INVALID_PARAMETER;
-    if (decrypt_key_handle && !(decrypt_key = get_key_object( decrypt_key_handle ))) return STATUS_INVALID_HANDLE;
+    size = sizeof(*blob) + blob->cbKey * 2;
+    if (key_flags & KEY_FLAG_PRIVATE)
+    {
+        priv = pub + blob->cbKey * 2;
+        size += blob->cbKey;
+    }
+    if (blob->cbKey != key_size || input_len != size) return STATUS_INVALID_PARAMETER;
 
-    if ((status = key_import( alg, decrypt_key, type, ret_handle, object, object_len, input, input_len ))) return status;
-    TRACE( "returning handle %p\n", *ret_handle );
+    if ((status = alloc_key( alg, key_flags, &key )) || (status = alloc_ecc_key( key, curve )))
+    {
+        destroy_key( key );
+        return status;
+    }
+
+    pub_size = SymCryptEckeySizeofPublicKey( key->a.ecc.handle, SYMCRYPT_ECPOINT_FORMAT_XY );
+    if (key_flags & KEY_FLAG_PRIVATE) priv_size = SymCryptEckeySizeofPrivateKey( key->a.ecc.handle );
+
+    if (SymCryptEckeySetValue( priv, priv_size, pub, pub_size, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                               SYMCRYPT_ECPOINT_FORMAT_XY, set_flags, key->a.ecc.handle ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    *ret_key = key;
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptExportKey( BCRYPT_KEY_HANDLE export_key_handle, BCRYPT_KEY_HANDLE encrypt_key_handle,
-                                 const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size, ULONG flags )
+static NTSTATUS alloc_dsa_key( struct key *key, ULONG bitlen )
 {
-    struct key *key = get_key_object( export_key_handle );
-    struct key *encrypt_key = NULL;
+    if (!(key->a.dsa.group = SymCryptDlgroupAllocate( bitlen, 0 ))) return STATUS_NO_MEMORY;
+    if (!(key->a.dsa.handle = SymCryptDlkeyAllocate( key->a.dsa.group ))) return STATUS_NO_MEMORY;
+    key->a.bitlen = bitlen;
+    return STATUS_SUCCESS;
+}
 
-    TRACE( "%p, %p, %s, %p, %lu, %p, %#lx\n", export_key_handle, encrypt_key_handle, debugstr_w(type), output,
-           output_len, size, flags );
+static NTSTATUS import_dsa_key( enum alg_id alg, const UCHAR *input, ULONG input_len, struct key **ret_key )
+{
+    const BCRYPT_DSA_KEY_BLOB *blob = (const BCRYPT_DSA_KEY_BLOB *)input;
+    ULONG size, count, priv_size = 0, key_flags = 0, set_flags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_DLKEY_DSA;
+    const UCHAR *prime, *generator, *pub, *priv = NULL;
+    struct key *key = NULL;
+    NTSTATUS status;
 
-    if (!key) return STATUS_INVALID_HANDLE;
-    if (!type || !size) return STATUS_INVALID_PARAMETER;
-    if (encrypt_key_handle && !(encrypt_key = get_key_object( encrypt_key_handle ))) return STATUS_INVALID_HANDLE;
+    if (input_len < sizeof(*blob)) return STATUS_INVALID_PARAMETER;
+
+    if (alg != ALG_ID_DSA) return STATUS_NOT_SUPPORTED;
+    if (blob->dwMagic == BCRYPT_DSA_PRIVATE_MAGIC) key_flags = KEY_FLAG_PRIVATE;
+    else if (blob->dwMagic != BCRYPT_DSA_PUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
+
+    size = sizeof(*blob) + blob->cbKey * 3;
+    if (key_flags & KEY_FLAG_PRIVATE) size += 20;
+    if (input_len != size) return STATUS_INVALID_PARAMETER;
+
+    if ((status = alloc_key( alg, key_flags, &key )) || (status = alloc_dsa_key( key, blob->cbKey * 8 )))
+    {
+        destroy_key( key );
+        return status;
+    }
+
+    prime = (const UCHAR *)(blob + 1);
+    generator = prime + blob->cbKey;
+    count = *(UINT32 *)blob->Count;
+
+    if (SymCryptDlgroupSetValue( prime, blob->cbKey, blob->q, sizeof(blob->q), generator, blob->cbKey,
+                                 SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, NULL, blob->Seed, sizeof(blob->Seed),
+                                 count, SYMCRYPT_DLGROUP_FIPS_NONE, key->a.dsa.group ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    pub = generator + blob->cbKey;
+    if (key_flags & KEY_FLAG_PRIVATE)
+    {
+        priv = pub + blob->cbKey;
+        priv_size = 20;
+    }
 
-    return key_export( key, encrypt_key, type, output, output_len, size );
+    if (alg == ALG_ID_DH) set_flags |= SYMCRYPT_FLAG_DLKEY_DH;
+    else if (alg == ALG_ID_DSA) set_flags |= SYMCRYPT_FLAG_DLKEY_DSA;
+
+    if (SymCryptDlkeySetValue( priv, priv_size, pub, blob->cbKey, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, set_flags,
+                               key->a.dsa.handle ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    memcpy( key->a.dsa.seed, blob->Seed, sizeof(blob->Seed) );
+    memcpy( &key->a.dsa.count, blob->Count, sizeof(blob->Count) );
+
+    *ret_key = key;
+    return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_duplicate( struct key *key_orig, struct key **ret_key )
+static NTSTATUS import_legacy_dsa_key( enum alg_id alg, const UCHAR *input, ULONG input_len, struct key **ret_key )
 {
-    struct key_asymmetric_duplicate_params params;
-    struct key *key_copy;
+    const BLOBHEADER *hdr = (const BLOBHEADER *)input;
+    const DSSPUBKEY *dsskey = (DSSPUBKEY *)(hdr + 1);
+    UINT32 priv_size = 0, pub_size = 0, set_flags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_DLKEY_DSA;
+    const UCHAR *prime_p, *prime_q, *generator, *pub = NULL, *priv = NULL, *seed, *count;
+    ULONG size, key_size, key_flags = 0;
+    struct key *key = NULL;
     NTSTATUS status;
-    UCHAR *buffer;
 
-    if (!(key_copy = calloc( 1, sizeof(*key_copy) ))) return STATUS_NO_MEMORY;
-    key_copy->hdr    = key_orig->hdr;
-    key_copy->alg_id = key_orig->alg_id;
+    if (alg != ALG_ID_DSA) return STATUS_NOT_SUPPORTED;
+    if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER;
+
+    if (hdr->bType == PRIVATEKEYBLOB) key_flags |= KEY_FLAG_PRIVATE;
+    else if (hdr->bType != PUBLICKEYBLOB) return STATUS_NOT_SUPPORTED;
+    if (hdr->bVersion != 2 || hdr->aiKeyAlg != CALG_DSS_SIGN) return STATUS_NOT_SUPPORTED;
+
+    size = sizeof(*hdr) + sizeof(*dsskey);
+    if (input_len < size || dsskey->bitlen & 0xf) return STATUS_INVALID_PARAMETER;
+
+    key_size = len_from_bitlen( dsskey->bitlen );
+    size += key_size * 2 + 20 /* q */ + 20 /* x or y */ + 20 /* seed */ + 4 /* count */;
+    if (input_len != size) return STATUS_INVALID_PARAMETER;
 
-    if (is_symmetric_key( key_orig ))
+    if ((status = alloc_key( alg, key_flags, &key )) || (status = alloc_dsa_key( key, dsskey->bitlen )))
     {
-        if (!(buffer = malloc( key_orig->u.s.secret_len )))
-        {
-            free( key_copy );
-            return STATUS_NO_MEMORY;
-        }
-        memcpy( buffer, key_orig->u.s.secret, key_orig->u.s.secret_len );
-
-        key_copy->u.s.mode       = key_orig->u.s.mode;
-        key_copy->u.s.block_size = key_orig->u.s.block_size;
-        key_copy->u.s.secret     = buffer;
-        key_copy->u.s.secret_len = key_orig->u.s.secret_len;
-        InitializeCriticalSection( &key_copy->u.s.cs );
-        *ret_key = key_copy;
-        return STATUS_SUCCESS;
+        destroy_key( key );
+        return status;
     }
 
-    key_copy->u.a.bitlen   = key_orig->u.a.bitlen;
-    key_copy->u.a.flags    = key_orig->u.a.flags;
-    key_copy->u.a.dss_seed = key_orig->u.a.dss_seed;
+    prime_p = (const UCHAR *)(dsskey + 1);
+    prime_q = prime_p + key_size;
+    generator = prime_q + 20;
+    seed = generator + key_size + 20;
+    count = seed + 20;
 
-    params.key_orig = key_orig;
-    params.key_copy = key_copy;
-    if (!(status = UNIX_CALL( key_asymmetric_duplicate, &params ))) *ret_key = key_copy;
-    else free( key_copy );
-    return status;
+    if (SymCryptDlgroupSetValue( prime_p, key_size, prime_q, 20, generator, key_size,
+                                 SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, NULL, seed, 20, *(UINT32 *)count,
+                                 SYMCRYPT_DLGROUP_FIPS_NONE, key->a.dsa.group ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    if (key_flags & KEY_FLAG_PRIVATE)
+    {
+        priv = generator + key_size;
+        priv_size = 20;
+    }
+    else
+    {
+        pub = generator + key_size;
+        pub_size = 20;
+    }
+
+    if (SymCryptDlkeySetValue( priv, priv_size, pub, pub_size, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, set_flags,
+                               key->a.dsa.handle ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    memcpy( key->a.dsa.seed, seed, 20 );
+
+    *ret_key = key;
+    return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptDuplicateKey( BCRYPT_KEY_HANDLE handle, BCRYPT_KEY_HANDLE *handle_copy, UCHAR *object,
-                                    ULONG object_len, ULONG flags )
+static NTSTATUS import_dh_key( enum alg_id alg, const UCHAR *input, ULONG input_len, struct key **ret_key )
 {
-    struct key *key_orig = get_key_object( handle );
-    struct key *key_copy;
+    const BCRYPT_DH_KEY_BLOB *blob = (const BCRYPT_DH_KEY_BLOB *)input;
+    ULONG size, priv_size = 0, key_flags = 0, set_flags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_DLKEY_DH;
+    const UCHAR *modulus, *generator, *pub, *priv = NULL;
+    struct key *key = NULL;
     NTSTATUS status;
 
-    TRACE( "%p, %p, %p, %lu, %#lx\n", handle, handle_copy, object, object_len, flags );
-    if (object) FIXME( "ignoring object buffer\n" );
+    if (input_len < sizeof(*blob)) return STATUS_INVALID_PARAMETER;
 
-    if (!key_orig) return STATUS_INVALID_HANDLE;
-    if (!handle_copy) return STATUS_INVALID_PARAMETER;
+    if (alg != ALG_ID_DH) return STATUS_NOT_SUPPORTED;
+    if (blob->dwMagic == BCRYPT_DH_PRIVATE_MAGIC) key_flags = KEY_FLAG_PRIVATE;
+    else if (blob->dwMagic != BCRYPT_DH_PUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
 
-    if ((status = key_duplicate( key_orig, &key_copy ))) return status;
+    size = sizeof(*blob) + blob->cbKey * 3;
+    if (key_flags & KEY_FLAG_PRIVATE) size += blob->cbKey;
+    if (input_len != size) return STATUS_INVALID_PARAMETER;
 
-    *handle_copy = key_copy;
-    TRACE( "returning handle %p\n", *handle_copy );
+    if ((status = alloc_key( alg, key_flags, &key )) || (status = alloc_dsa_key( key, blob->cbKey * 8 )))
+    {
+        destroy_key( key );
+        return status;
+    }
+
+    modulus = (const UCHAR *)(blob + 1);
+    generator = modulus + blob->cbKey;
+
+    if (SymCryptDlgroupSetValue( modulus, blob->cbKey, NULL, 0, generator, blob->cbKey,
+                                 SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, NULL, NULL, 0, 0,
+                                 SYMCRYPT_DLGROUP_FIPS_NONE, key->a.dsa.group ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    pub = generator + blob->cbKey;
+    if (key_flags & KEY_FLAG_PRIVATE)
+    {
+        priv = pub + blob->cbKey;
+        priv_size = blob->cbKey;
+    }
+
+    if (SymCryptDlkeySetValue( priv, priv_size, pub, blob->cbKey, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, set_flags,
+                               key->a.dsa.handle ))
+    {
+        destroy_key( key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    *ret_key = key;
     return STATUS_SUCCESS;
 }
 
-static const WCHAR *resolve_blob_type( const WCHAR *type, UCHAR *input, ULONG input_len )
+static NTSTATUS import_key_pair( const struct algorithm *alg, const WCHAR *type, const UCHAR *input, ULONG input_len,
+                                 struct key **ret_key )
 {
-    BCRYPT_KEY_BLOB *blob = (BCRYPT_KEY_BLOB *)input;
+    struct key *key;
+    NTSTATUS status;
+
+    if (!wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_ECCPRIVATE_BLOB ))
+    {
+        status = import_ecc_key( alg->id, alg->curve_id, input, input_len, &key );
+    }
+    else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) ||
+             !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB ))
+    {
+        status = import_rsa_key( alg->id, input, input_len, &key );
+    }
+    else if (!wcscmp( type, LEGACY_RSAPRIVATE_BLOB ))
+    {
+        status = import_legacy_rsa_key( alg->id, input, input_len, &key );
+    }
+    else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB ) || !wcscmp( type, BCRYPT_DSA_PRIVATE_BLOB ))
+    {
+        status = import_dsa_key( alg->id, input, input_len, &key );
+    }
+    else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ) || !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))
+    {
+        status = import_dh_key( alg->id, input, input_len, &key );
+    }
+    else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB ))
+    {
+        status = import_legacy_dsa_key( alg->id, input, input_len, &key );
+    }
+    else
+    {
+        FIXME( "unsupported blob type %s\n", debugstr_w(type) );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    if (!status)
+    {
+        key->flags |= KEY_FLAG_FINALIZED;
+        *ret_key = key;
+    }
+    return status;
+}
+
+static const WCHAR *resolve_blob_type( const WCHAR *type, const UCHAR *input, ULONG input_len )
+{
+    const BCRYPT_KEY_BLOB *blob = (const BCRYPT_KEY_BLOB *)input;
 
     if (!type) return NULL;
     if (wcscmp( type, BCRYPT_PUBLIC_KEY_BLOB )) return type;
@@ -2312,377 +3340,610 @@ static const WCHAR *resolve_blob_type( const WCHAR *type, UCHAR *input, ULONG in
 NTSTATUS WINAPI BCryptImportKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key_handle, const WCHAR *type,
                                      BCRYPT_KEY_HANDLE *ret_handle, UCHAR *input, ULONG input_len, ULONG flags )
 {
-    struct algorithm *alg = get_alg_object( handle );
+    const struct algorithm *alg = get_alg_object( handle );
+    struct key *key;
     NTSTATUS status;
 
     TRACE( "%p, %p, %s, %p, %p, %lu, %#lx\n", handle, decrypt_key_handle, debugstr_w(type), ret_handle, input,
            input_len, flags );
 
     if (!alg) return STATUS_INVALID_HANDLE;
-    if (!ret_handle || !input || !(type = resolve_blob_type( type, input, input_len )))
-        return STATUS_INVALID_PARAMETER;
+    if (!ret_handle || !input || !(type = resolve_blob_type( type, input, input_len ))) return STATUS_INVALID_PARAMETER;
     if (decrypt_key_handle)
     {
         FIXME( "decryption of key not yet supported\n" );
         return STATUS_NOT_IMPLEMENTED;
     }
 
-    if ((status = key_import_pair( alg, type, ret_handle, input, input_len ))) return status;
+    if ((status = import_key_pair( alg, type, input, input_len, &key ))) return status;
+    *ret_handle = key;
+
     TRACE( "returning handle %p\n", *ret_handle );
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptSignHash( BCRYPT_KEY_HANDLE handle, void *padding, UCHAR *input, ULONG input_len,
-                                UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+static NTSTATUS generate_key_pair( const struct algorithm *alg, ULONG bitlen, ULONG flags, struct key **ret_key )
 {
-    struct key_asymmetric_sign_params params;
-    struct key *key = get_key_object( handle );
+    NTSTATUS status;
+    struct key *key;
 
-    TRACE( "%p, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, padding, input, input_len, output, output_len,
-           ret_len, flags );
+    if ((status = alloc_key( alg->id, KEY_FLAG_PRIVATE, &key ))) return status;
 
-    if (!key) return STATUS_INVALID_HANDLE;
-    if (!is_signature_key( key )) return STATUS_NOT_SUPPORTED;
+    switch (alg->id)
+    {
+    case ALG_ID_DH:
+        if (bitlen < 512)
+        {
+            destroy_key( key );
+            return STATUS_INVALID_PARAMETER;
+        }
+        /* fall through */
+    case ALG_ID_DSA:
+    case ALG_ID_RSA:
+    case ALG_ID_RSA_SIGN:
+        key->a.bitlen = bitlen;
+        break;
+
+    case ALG_ID_ECDH:
+    case ALG_ID_ECDH_P256:
+    case ALG_ID_ECDH_P384:
+    case ALG_ID_ECDH_P521:
+    case ALG_ID_ECDSA:
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    case ALG_ID_ECDSA_P521:
+        if (bitlen && bitlen != curve_strength( alg->curve_id ))
+        {
+            destroy_key( key );
+            return STATUS_INVALID_PARAMETER;
+        }
+        key->a.ecc.curve_id = alg->curve_id;
+        key->a.bitlen = curve_strength( key->a.ecc.curve_id );
+        break;
 
-    params.key        = key;
-    params.padding    = padding;
-    params.input      = input;
-    params.input_len  = input_len;
-    params.output     = output;
-    params.output_len = output_len;
-    params.ret_len    = ret_len;
-    params.flags      = flags;
-    return UNIX_CALL( key_asymmetric_sign, &params );
+    default:
+        FIXME( "unhandled algorithm %u\n", alg->id );
+        destroy_key( key );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    *ret_key = key;
+    return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptVerifySignature( BCRYPT_KEY_HANDLE handle, void *padding, UCHAR *hash, ULONG hash_len,
-                                       UCHAR *signature, ULONG signature_len, ULONG flags )
+NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle, ULONG bitlen,
+                                       ULONG flags )
+{
+    const struct algorithm *alg = get_alg_object( handle );
+    struct key *key;
+    NTSTATUS status;
+
+    TRACE( "%p, %p, %lu, %#lx\n", handle, ret_handle, bitlen, flags );
+
+    if (!alg) return STATUS_INVALID_HANDLE;
+    if (!ret_handle) return STATUS_INVALID_PARAMETER;
+
+    if ((status = generate_key_pair( alg, bitlen, flags, &key ))) return status;
+
+    *ret_handle = key;
+    TRACE( "returning handle %p\n", *ret_handle );
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS finalize_key_pair( struct key *key )
+{
+    UINT32 flags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+    NTSTATUS status;
+
+    switch (key->alg_id)
+    {
+    case ALG_ID_RSA:
+    case ALG_ID_RSA_SIGN:
+    {
+        UINT64 exp = 65537;
+
+        if (key->alg_id == ALG_ID_RSA) flags |= SYMCRYPT_FLAG_RSAKEY_ENCRYPT | SYMCRYPT_FLAG_RSAKEY_SIGN;
+        else flags |= SYMCRYPT_FLAG_RSAKEY_SIGN;
+
+        if ((status = alloc_rsa_key( key, key->a.bitlen ))) return status;
+        if (SymCryptRsakeyGenerate( key->a.rsa.handle, &exp, 1, flags )) return STATUS_INTERNAL_ERROR;
+        break;
+    }
+    case ALG_ID_ECDH:
+        if (!key->a.ecc.curve_id) return STATUS_INVALID_PARAMETER;
+        /* fall through */
+    case ALG_ID_ECDH_P256:
+    case ALG_ID_ECDH_P384:
+    case ALG_ID_ECDH_P521:
+    {
+        flags |= SYMCRYPT_FLAG_ECKEY_ECDH;
+        if ((status = alloc_ecc_key( key, key->a.ecc.curve_id ))) return status;
+        if (SymCryptEckeySetRandom( flags, key->a.ecc.handle )) return STATUS_INTERNAL_ERROR;
+        break;
+    }
+    case ALG_ID_ECDSA:
+        if (!key->a.ecc.curve_id) return STATUS_INVALID_PARAMETER;
+        /* fall through */
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    case ALG_ID_ECDSA_P521:
+    {
+        flags |= SYMCRYPT_FLAG_ECKEY_ECDSA;
+        if ((status = alloc_ecc_key( key, key->a.ecc.curve_id ))) return status;
+        if (SymCryptEckeySetRandom( flags, key->a.ecc.handle )) return STATUS_INTERNAL_ERROR;
+        break;
+    }
+    case ALG_ID_DH:
+    case ALG_ID_DSA:
+    {
+        if (key->alg_id == ALG_ID_DSA) flags |= SYMCRYPT_FLAG_DLKEY_DSA;
+        else flags |= SYMCRYPT_FLAG_DLKEY_DH;
+
+        if (!(key->a.dsa.group)) /* no parameters have been imported */
+        {
+            if (!(key->a.dsa.group = SymCryptDlgroupAllocate( key->a.bitlen, 0 ))) return STATUS_NO_MEMORY;
+            if (SymCryptDlgroupGenerate( NULL, SYMCRYPT_DLGROUP_FIPS_186_2, key->a.dsa.group ))
+                return STATUS_INTERNAL_ERROR;
+        }
+        if (!(key->a.dsa.handle = SymCryptDlkeyAllocate( key->a.dsa.group ))) return STATUS_NO_MEMORY;
+        if (SymCryptDlkeyGenerate( flags, key->a.dsa.handle )) return STATUS_INTERNAL_ERROR;
+        break;
+    }
+    default:
+        FIXME( "unhandled algorithm %u\n", key->alg_id );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    key->flags |= KEY_FLAG_FINALIZED;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags )
 {
-    struct key_asymmetric_verify_params params;
     struct key *key = get_key_object( handle );
 
-    TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, padding, hash, hash_len, signature, signature_len, flags );
+    TRACE( "%p, %#lx\n", key, flags );
 
-    if (!key) return STATUS_INVALID_HANDLE;
-    if (!is_signature_key( key )) return STATUS_NOT_SUPPORTED;
-    if (!hash || !hash_len || !signature || !signature_len) return STATUS_INVALID_PARAMETER;
+    if (!key || key->flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE;
+
+    return finalize_key_pair( key );
+}
+
+static NTSTATUS duplicate_key( const struct key *src, struct key **ret_key )
+{
+    struct key *dst;
+    NTSTATUS status;
+
+    switch (src->alg_id)
+    {
+    case ALG_ID_AES:
+        if ((status = alloc_key( src->alg_id, src->flags, &dst ))) return status;
+        if ((status = alloc_aes_key( dst, src->s.mode, src->s.block_size, src->s.secret, src->s.secret_len )))
+        {
+            destroy_key( dst );
+            return status;
+        }
+        if (src->s.mode == CHAIN_MODE_GCM)
+        {
+            SymCryptGcmKeyCopy( &src->s.gcm.handle, &dst->s.gcm.handle );
+            SymCryptGcmStateCopy( &src->s.gcm.state, &src->s.gcm.handle, &dst->s.gcm.state );
+        }
+        else SymCryptAesKeyCopy( &src->s.aes.handle, &dst->s.aes.handle );
+        break;
+
+    default:
+        FIXME( "algorithm %u not supported\n", src->alg_id );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    *ret_key = dst;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS WINAPI BCryptDuplicateKey( BCRYPT_KEY_HANDLE handle, BCRYPT_KEY_HANDLE *handle_copy, UCHAR *object,
+                                    ULONG object_len, ULONG flags )
+{
+    const struct key *key_orig = get_key_object( handle );
+    struct key *key_copy;
+    NTSTATUS status;
+
+    TRACE( "%p, %p, %p, %lu, %#lx\n", handle, handle_copy, object, object_len, flags );
+    if (object) FIXME( "ignoring object buffer\n" );
+
+    if (!key_orig) return STATUS_INVALID_HANDLE;
+    if (!handle_copy) return STATUS_INVALID_PARAMETER;
+
+    if ((status = duplicate_key( key_orig, &key_copy ))) return status;
+    *handle_copy = key_copy;
+
+    TRACE( "returning handle %p\n", *handle_copy );
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS get_hash_oid( const WCHAR *str, const SYMCRYPT_OID **oid, UINT32 *oid_count )
+{
+    if (!str)
+    {
+        *oid = NULL;
+        *oid_count = 0;
+        return STATUS_SUCCESS;
+    }
+    if (!wcscmp( str, BCRYPT_MD5_ALGORITHM ))
+    {
+        *oid = SymCryptMd5OidList;
+        *oid_count = ARRAY_SIZE( SymCryptMd5OidList );
+        return STATUS_SUCCESS;
+    }
+    if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM ))
+    {
+        *oid = SymCryptSha1OidList;
+        *oid_count = ARRAY_SIZE( SymCryptSha1OidList );
+        return STATUS_SUCCESS;
+    }
+    if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM ))
+    {
+        *oid = SymCryptSha256OidList;
+        *oid_count = ARRAY_SIZE( SymCryptSha256OidList );
+        return STATUS_SUCCESS;
+    }
+    if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM ))
+    {
+        *oid = SymCryptSha384OidList;
+        *oid_count = ARRAY_SIZE( SymCryptSha384OidList );
+        return STATUS_SUCCESS;
+    }
+    if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM ))
+    {
+        *oid = SymCryptSha512OidList;
+        *oid_count = ARRAY_SIZE( SymCryptSha512OidList );
+        return STATUS_SUCCESS;
+    }
+    FIXME( "unhandled algorithm %s\n", debugstr_w(str) );
+    return STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS sign_hash_rsa( const struct key *key, const void *padding, const UCHAR *hash, ULONG hash_len,
+                               UCHAR *signature, ULONG signature_len, ULONG *ret_len, ULONG flags )
+{
+    ULONG required_len = len_from_bitlen( key->a.bitlen );
+    NTSTATUS status;
+
+    if (!padding) return STATUS_INVALID_PARAMETER;
+    if (signature_len < required_len)
+    {
+        *ret_len = required_len;
+        if (signature) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
+    if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
+
+    if (flags == BCRYPT_PAD_PKCS1)
+    {
+        const BCRYPT_PKCS1_PADDING_INFO *pad = padding;
+        UINT32 hash_oid_count, flags = 0;
+        const SYMCRYPT_OID *hash_oid;
+        SIZE_T size;
+
+        if ((status = get_hash_oid( pad->pszAlgId, &hash_oid, &hash_oid_count ))) return status;
+        if (!hash_oid) flags = SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1;
+
+        if (SymCryptRsaPkcs1Sign( key->a.rsa.handle, hash, hash_len, hash_oid, hash_oid_count, flags,
+                                  SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, signature, required_len, &size ))
+            return STATUS_INTERNAL_ERROR;
+        *ret_len = size;
+    }
+    else if (flags == BCRYPT_PAD_PSS)
+    {
+        const BCRYPT_PSS_PADDING_INFO *pad = padding;
+        const SYMCRYPT_HASH *hash_desc = get_hash_from_str( pad->pszAlgId );
+        SIZE_T size;
+
+        if (SymCryptRsaPssSign( key->a.rsa.handle, hash, hash_len, hash_desc, pad->cbSalt, 0,
+                                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, signature, required_len, &size ))
+            return STATUS_INTERNAL_ERROR;
+        *ret_len = size;
+    }
+    else
+    {
+        FIXME( "unsupported flags %#lx", flags );
+        if (padding) return STATUS_INVALID_PARAMETER;
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS sign_hash_ecc( const struct key *key, const UCHAR *hash, ULONG hash_len, UCHAR *signature,
+                               ULONG signature_len, ULONG *ret_len )
+{
+    ULONG required_len = len_from_bitlen( key->a.bitlen ) * 2;
+
+    if (signature_len < required_len)
+    {
+        *ret_len = required_len;
+        if (signature) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
+    if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
+
+    if (SymCryptEcDsaSign( key->a.ecc.handle, hash, hash_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, signature,
+                           required_len )) return STATUS_INTERNAL_ERROR;
 
-    params.key = key;
-    params.padding = padding;
-    params.hash = hash;
-    params.hash_len = hash_len;
-    params.signature = signature;
-    params.signature_len = signature_len;
-    params.flags = flags;
-    return UNIX_CALL( key_asymmetric_verify, &params );
+    *ret_len = required_len;
+    return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptDestroyKey( BCRYPT_KEY_HANDLE handle )
+static NTSTATUS sign_hash_dsa( const struct key *key, const UCHAR *hash, ULONG hash_len, UCHAR *signature,
+                               ULONG signature_len, ULONG *ret_len )
 {
-    struct key *key = get_key_object( handle );
+    ULONG required_len = hash_len * 2;
 
-    TRACE( "%p\n", handle );
+    if (signature_len < required_len)
+    {
+        *ret_len = required_len;
+        if (signature) return STATUS_BUFFER_TOO_SMALL;
+        return STATUS_SUCCESS;
+    }
+    if (!(key->flags & KEY_FLAG_PRIVATE)) return STATUS_INVALID_PARAMETER;
 
-    if (!key) return STATUS_INVALID_HANDLE;
-    key_destroy( key );
+    if (SymCryptDsaSign( key->a.dsa.handle, hash, hash_len, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, signature,
+                         required_len )) return STATUS_INTERNAL_ERROR;
+
+    *ret_len = required_len;
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptEncrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
-                               ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+NTSTATUS WINAPI BCryptSignHash( BCRYPT_KEY_HANDLE handle, void *padding, UCHAR *hash, ULONG hash_len,
+                                UCHAR *signature, ULONG signature_len, ULONG *ret_len, ULONG flags )
 {
-    struct key *key = get_key_object( handle );
-    struct key_asymmetric_encrypt_params asymmetric_params;
-    NTSTATUS ret;
+    const struct key *key = get_key_object( handle );
 
-    TRACE( "%p, %p, %lu, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, input, input_len, padding, iv, iv_len, output,
-           output_len, ret_len, flags );
+    TRACE( "%p, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, padding, hash, hash_len, signature, signature_len,
+           ret_len, flags );
 
     if (!key) return STATUS_INVALID_HANDLE;
 
-    if (is_symmetric_key( key ))
-    {
-        if (flags & ~BCRYPT_BLOCK_PADDING)
-        {
-            FIXME( "flags %#lx not implemented\n", flags );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        EnterCriticalSection( &key->u.s.cs );
-        ret = key_symmetric_encrypt( key, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags );
-        LeaveCriticalSection( &key->u.s.cs );
-    }
-    else
+    switch (key->alg_id)
     {
-        if (flags & BCRYPT_PAD_NONE)
-        {
-            FIXME( "flags %#lx not implemented\n", flags );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        if (!is_asymmetric_encryption_key( key )) return STATUS_NOT_SUPPORTED;
+    case ALG_ID_RSA:
+    case ALG_ID_RSA_SIGN:
+        return sign_hash_rsa( key, padding, hash, hash_len, signature, signature_len, ret_len, flags );
+
+    case ALG_ID_ECDSA:
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    case ALG_ID_ECDSA_P521:
+        if (padding) return STATUS_INVALID_PARAMETER;
+        return sign_hash_ecc( key, hash, hash_len, signature, signature_len, ret_len );
 
-        asymmetric_params.input = input;
-        asymmetric_params.input_len = input_len;
-        asymmetric_params.padding = padding;
-        asymmetric_params.key = key;
-        asymmetric_params.output = output;
-        asymmetric_params.output_len = output_len;
-        asymmetric_params.ret_len = ret_len;
-        asymmetric_params.flags = flags;
-        ret = UNIX_CALL(key_asymmetric_encrypt, &asymmetric_params);
+    case ALG_ID_DSA:
+        return sign_hash_dsa( key, hash, hash_len, signature, signature_len, ret_len );
+
+    default:
+        FIXME( "unhandled algorithm %u\n", key->alg_id );
+        break;
     }
 
-    return ret;
+    return STATUS_NOT_SUPPORTED;
 }
 
-NTSTATUS WINAPI BCryptDecrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
-                               ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+static NTSTATUS verify_signature_rsa( const struct key *key, const void *padding, const UCHAR *hash, ULONG hash_len,
+                                      const UCHAR *signature, ULONG signature_len, ULONG flags )
 {
-    struct key *key = get_key_object( handle );
-    struct key_asymmetric_decrypt_params params;
-    NTSTATUS ret;
+    NTSTATUS status;
+    SYMCRYPT_ERROR error;
 
-    TRACE( "%p, %p, %lu, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, input, input_len, padding, iv, iv_len, output,
-           output_len, ret_len, flags );
+    if (!padding || signature_len != len_from_bitlen( key->a.bitlen )) return STATUS_INVALID_PARAMETER;
 
-    if (!key) return STATUS_INVALID_HANDLE;
+    if (flags == BCRYPT_PAD_PKCS1)
+    {
+        const BCRYPT_PKCS1_PADDING_INFO *pad = padding;
+        UINT32 flags = 0, hash_oid_count;
+        const SYMCRYPT_OID *hash_oid;
 
-    if (is_symmetric_key( key ))
+        if (!pad) return STATUS_INVALID_PARAMETER;
+        if ((status = get_hash_oid( pad->pszAlgId, &hash_oid, &hash_oid_count ))) return status;
+        if (!hash_oid) flags = SYMCRYPT_FLAG_RSA_PKCS1_OPTIONAL_HASH_OID;
+
+        error = SymCryptRsaPkcs1Verify( key->a.rsa.handle, hash, hash_len, signature, signature_len,
+                                        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, hash_oid, hash_oid_count, flags );
+    }
+    else if (flags == BCRYPT_PAD_PSS)
     {
-        if (flags & ~BCRYPT_BLOCK_PADDING)
-        {
-            FIXME( "flags %#lx not supported\n", flags );
-            return STATUS_NOT_IMPLEMENTED;
-        }
+        const BCRYPT_PSS_PADDING_INFO *pad = padding;
+        const SYMCRYPT_HASH *hash_desc = get_hash_from_str( pad->pszAlgId );
 
-        EnterCriticalSection( &key->u.s.cs );
-        ret = key_symmetric_decrypt( key, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags );
-        LeaveCriticalSection( &key->u.s.cs );
+        error = SymCryptRsaPssVerify( key->a.rsa.handle, hash, hash_len, signature, signature_len,
+                                      SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, hash_desc, pad->cbSalt, 0 );
     }
     else
     {
-        if (flags & BCRYPT_PAD_NONE)
-        {
-            FIXME( "flags %#lx not implemented\n", flags );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        if (!is_asymmetric_encryption_key( key )) return STATUS_NOT_SUPPORTED;
-
-        params.key = key;
-        params.input = input;
-        params.input_len = input_len;
-        params.padding = padding;
-        params.output = output;
-        params.output_len = output_len;
-        params.ret_len = ret_len;
-        params.flags = flags;
-        ret = UNIX_CALL(key_asymmetric_decrypt, &params);
+        FIXME( "unsupported flags %#lx\n", flags );
+        if (padding) return STATUS_INVALID_PARAMETER;
+        return STATUS_NOT_SUPPORTED;
     }
 
-    return ret;
+    if (error == SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE) return STATUS_INVALID_SIGNATURE;
+    if (error) return STATUS_INTERNAL_ERROR;
+    return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
+static NTSTATUS verify_signature_ecc( const struct key *key, const UCHAR *hash, ULONG hash_len, const UCHAR *signature,
+                                      ULONG signature_len, ULONG flags )
 {
-    struct object *object = get_object( handle, 0 );
+    SYMCRYPT_ERROR error;
 
-    TRACE( "%p, %s, %p, %lu, %#lx\n", handle, debugstr_w(prop), value, size, flags );
+    if (signature_len != len_from_bitlen( key->a.bitlen ) * 2) return STATUS_INVALID_PARAMETER;
 
-    if (!handle) return STATUS_INVALID_HANDLE;
-    if (is_alg_pseudo_handle( handle )) return STATUS_ACCESS_DENIED;
+    error = SymCryptEcDsaVerify( key->a.ecc.handle, hash, hash_len, signature, signature_len,
+                                 SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0 );
 
-    switch (object->magic)
-    {
-    case MAGIC_ALG:
-    {
-        struct algorithm *alg = (struct algorithm *)object;
-        return set_alg_property( alg, prop, value, size, flags );
-    }
-    case MAGIC_KEY:
-    {
-        struct key *key = (struct key *)object;
-        return set_key_property( key, prop, value, size, flags );
-    }
-    default:
-        WARN( "unknown magic %#lx\n", object->magic );
-        return STATUS_INVALID_HANDLE;
-    }
+    if (error == SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE) return STATUS_INVALID_SIGNATURE;
+    if (error) return STATUS_INTERNAL_ERROR;
+    return STATUS_SUCCESS;
 }
 
-#define HMAC_PAD_LEN 64
-NTSTATUS WINAPI BCryptDeriveKeyCapi( BCRYPT_HASH_HANDLE handle, BCRYPT_ALG_HANDLE halg, UCHAR *key, ULONG keylen, ULONG flags )
+static NTSTATUS verify_signature_dsa( const struct key *key, const UCHAR *hash, ULONG hash_len, const UCHAR *signature,
+                                      ULONG signature_len, ULONG flags )
 {
-    struct hash *hash = get_hash_object( handle );
-    UCHAR buf[MAX_HASH_OUTPUT_BYTES * 2];
-    ULONG len;
+    SYMCRYPT_ERROR error;
 
-    TRACE( "%p, %p, %p, %lu, %#lx\n", handle, halg, key, keylen, flags );
+    if (signature_len != hash_len * 2) return STATUS_INVALID_PARAMETER;
 
-    if (!hash) return STATUS_INVALID_HANDLE;
-    if (!key || !keylen) return STATUS_INVALID_PARAMETER;
-    if (keylen > hash->desc->hashsize * 2) return STATUS_INVALID_PARAMETER;
-    if (halg)
-    {
-        FIXME( "algorithm handle not supported\n" );
-        return STATUS_NOT_IMPLEMENTED;
-    }
+    error = SymCryptDsaVerify( key->a.dsa.handle, hash, hash_len, signature, signature_len,
+                               SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0 );
+
+    if (error == SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE) return STATUS_INVALID_SIGNATURE;
+    if (error) return STATUS_INTERNAL_ERROR;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS WINAPI BCryptVerifySignature( BCRYPT_KEY_HANDLE handle, void *padding, UCHAR *hash, ULONG hash_len,
+                                       UCHAR *signature, ULONG signature_len, ULONG flags )
+{
+    const struct key *key = get_key_object( handle );
+
+    TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, padding, hash, hash_len, signature, signature_len, flags );
 
-    hash_finalize( hash, buf );
+    if (!key) return STATUS_INVALID_HANDLE;
+    if (!hash || !hash_len || !signature || !signature_len) return STATUS_INVALID_PARAMETER;
 
-    len = hash->desc->hashsize;
-    if (len < keylen)
+    switch (key->alg_id)
     {
-        UCHAR pad1[HMAC_PAD_LEN], pad2[HMAC_PAD_LEN];
-        ULONG i;
+    case ALG_ID_RSA:
+    case ALG_ID_RSA_SIGN:
+        return verify_signature_rsa( key, padding, hash, hash_len, signature, signature_len, flags );
 
-        for (i = 0; i < sizeof(pad1); i++)
-        {
-            pad1[i] = 0x36 ^ (i < len ? buf[i] : 0);
-            pad2[i] = 0x5c ^ (i < len ? buf[i] : 0);
-        }
+    case ALG_ID_ECDSA:
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    case ALG_ID_ECDSA_P521:
+        return verify_signature_ecc( key, hash, hash_len, signature, signature_len, flags );
 
-        hash_prepare( hash );
-        hash->desc->process( &hash->inner, pad1, sizeof(pad1) );
-        hash_finalize( hash, buf );
+    case ALG_ID_DSA:
+        return verify_signature_dsa( key, hash, hash_len, signature, signature_len, flags );
 
-        hash_prepare( hash );
-        hash->desc->process( &hash->inner, pad2, sizeof(pad2) );
-        hash_finalize( hash, buf + len );
+    default:
+        FIXME( "unhandled algorithm %u\n", key->alg_id );
+        break;
     }
-    memcpy( key, buf, keylen );
-    return STATUS_SUCCESS;
+
+    return STATUS_NOT_SUPPORTED;
 }
 
-static NTSTATUS pbkdf2( struct hash *hash, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len,
-                        ULONGLONG iterations, ULONG i, UCHAR *dst, ULONG hash_len )
+NTSTATUS WINAPI BCryptDestroyKey( BCRYPT_KEY_HANDLE handle )
 {
-    UCHAR bytes[4], *buf;
-    ULONG j, k;
-
-    if (!iterations) return STATUS_INVALID_PARAMETER;
-    if (!(buf = malloc( hash_len ))) return STATUS_NO_MEMORY;
+    struct key *key = get_key_object( handle );
 
-    for (j = 0; j < iterations; j++)
-    {
-        if (j == 0)
-        {
-            /* use salt || INT(i) */
-            if (salt_len && hash->desc->process( &hash->inner, salt, salt_len ))
-            {
-                free( buf );
-                return STATUS_INVALID_PARAMETER;
-            }
-            bytes[0] = (i >> 24) & 0xff;
-            bytes[1] = (i >> 16) & 0xff;
-            bytes[2] = (i >> 8) & 0xff;
-            bytes[3] = i & 0xff;
-            hash->desc->process( &hash->inner, bytes, 4 );
-        }
-        else hash->desc->process( &hash->inner, buf, hash_len ); /* use U_j */
+    TRACE( "%p\n", handle );
 
-        hash_finalize( hash, buf );
-        if (j == 0) memcpy( dst, buf, hash_len );
-        else for (k = 0; k < hash_len; k++) dst[k] ^= buf[k];
-    }
+    if (!key || key->hdr.magic != MAGIC_KEY) return STATUS_INVALID_HANDLE;
 
-    free( buf );
+    destroy_key( key );
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS derive_key_pbkdf2( struct algorithm *alg, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len,
-                                   ULONGLONG iterations, UCHAR *dk, ULONG dk_len )
+#define HMAC_PAD_LEN 64
+NTSTATUS WINAPI BCryptDeriveKeyCapi( BCRYPT_HASH_HANDLE handle, BCRYPT_ALG_HANDLE halg, UCHAR *key, ULONG keylen,
+                                     ULONG flags )
 {
-    ULONG hash_len, block_count, bytes_left, i;
-    struct hash *hash;
-    UCHAR *partial;
-    NTSTATUS status;
+    struct hash *hash = get_hash_object( handle );
+    UCHAR buf[MAX_HASH_OUTPUT_BYTES * 2];
 
-    hash_len = builtin_algorithms[alg->id].hash_length;
-    if (dk_len <= 0 || dk_len > ((((ULONGLONG)1) << 32) - 1) * hash_len) return STATUS_INVALID_PARAMETER;
+    TRACE( "%p, %p, %p, %lu, %#lx\n", handle, halg, key, keylen, flags );
 
-    block_count = 1 + ((dk_len - 1) / hash_len); /* ceil(dk_len / hash_len) */
-    bytes_left = dk_len - (block_count - 1) * hash_len;
+    if (!key || !keylen || keylen > hash->len * 2) return STATUS_INVALID_PARAMETER;
+    if (!hash) return STATUS_INVALID_HANDLE;
+    if (halg)
+    {
+        FIXME( "algorithm handle not supported\n" );
+        return STATUS_NOT_IMPLEMENTED;
+    }
 
-    if ((status = hash_create( alg, pwd, pwd_len, BCRYPT_HASH_REUSABLE_FLAG, &hash ))) return status;
+    finish_hash( hash, buf );
 
-    /* full blocks */
-    for (i = 1; i < block_count; i++)
+    if (hash->len < keylen)
     {
-        if ((status = pbkdf2( hash, pwd, pwd_len, salt, salt_len, iterations, i, dk + ((i - 1) * hash_len), hash_len )))
+        UCHAR pad1[HMAC_PAD_LEN], pad2[HMAC_PAD_LEN];
+        ULONG i;
+
+        for (i = 0; i < sizeof(pad1); i++)
         {
-            hash_destroy( hash );
-            return status;
+            pad1[i] = 0x36 ^ (i < hash->len ? buf[i] : 0);
+            pad2[i] = 0x5c ^ (i < hash->len ? buf[i] : 0);
         }
-    }
 
-    /* final partial block */
-    if (!(partial = malloc( hash_len )))
-    {
-        hash_destroy( hash );
-        return STATUS_NO_MEMORY;
-    }
+        prepare_hash( hash );
+        hash_data( hash, pad1, sizeof(pad1) );
+        finish_hash( hash, buf );
 
-    if ((status = pbkdf2( hash, pwd, pwd_len, salt, salt_len, iterations, block_count, partial, hash_len )))
-    {
-        hash_destroy( hash );
-        free( partial );
-        return status;
+        prepare_hash( hash );
+        hash_data( hash, pad2, sizeof(pad2) );
+        finish_hash( hash, buf + hash->len );
     }
-    memcpy( dk + ((block_count - 1) * hash_len), partial, bytes_left );
 
-    hash_destroy( hash );
-    free( partial );
+    memcpy( key, buf, keylen );
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len,
-                                       ULONGLONG iterations, UCHAR *dk, ULONG dk_len, ULONG flags )
+static void destroy_secret( struct secret *secret )
 {
-    struct algorithm *alg = get_alg_object( handle );
-
-    TRACE( "%p, %p, %lu, %p, %lu, %s, %p, %lu, %#lx\n", handle, pwd, pwd_len, salt, salt_len,
-           wine_dbgstr_longlong(iterations), dk, dk_len, flags );
-
-    if (!alg) return STATUS_INVALID_HANDLE;
-    return derive_key_pbkdf2( alg, pwd, pwd_len, salt, salt_len, iterations, dk, dk_len );
+    if (!secret) return;
+    secure_zero( secret->derived_key, secret->derived_key_len );
+    free( secret->derived_key );
+    destroy_object( &secret->hdr );
 }
 
 NTSTATUS WINAPI BCryptSecretAgreement( BCRYPT_KEY_HANDLE privkey_handle, BCRYPT_KEY_HANDLE pubkey_handle,
                                        BCRYPT_SECRET_HANDLE *ret_handle, ULONG flags )
 {
-    struct key_asymmetric_derive_key_params params;
-    struct key *privkey = get_key_object( privkey_handle );
-    struct key *pubkey = get_key_object( pubkey_handle );
+    const struct key *privkey = get_key_object( privkey_handle );
+    const struct key *pubkey = get_key_object( pubkey_handle );
     struct secret *secret;
-    ULONG len;
-    NTSTATUS status;
 
     TRACE( "%p, %p, %p, %#lx\n", privkey_handle, pubkey_handle, ret_handle, flags );
 
-    if (!privkey || !pubkey) return STATUS_INVALID_HANDLE;
-    if (!is_agreement_key( privkey ) || !is_agreement_key( pubkey )) return STATUS_NOT_SUPPORTED;
+    if (!privkey || !pubkey || privkey->alg_id != pubkey->alg_id || !(privkey->flags & KEY_FLAG_PRIVATE))
+        return STATUS_INVALID_HANDLE;
     if (!ret_handle) return STATUS_INVALID_PARAMETER;
 
     if (!(secret = calloc( 1, sizeof(*secret) ))) return STATUS_NO_MEMORY;
     secret->hdr.magic = MAGIC_SECRET;
-    secret->derived_key_len = len_from_bitlen( privkey->u.a.bitlen );
+    secret->derived_key_len = len_from_bitlen( privkey->a.bitlen );
     if (!(secret->derived_key = malloc( secret->derived_key_len )))
     {
         free( secret );
         return STATUS_NO_MEMORY;
     }
 
-    params.privkey    = privkey;
-    params.pubkey     = pubkey;
-    params.output     = secret->derived_key;
-    params.output_len = secret->derived_key_len;
-    params.ret_len    = &len;
-    if ((status = UNIX_CALL( key_asymmetric_derive_key, &params )))
+    switch (privkey->alg_id)
     {
-        free( secret->derived_key );
-        free( secret );
-        return status;
+    case ALG_ID_DH:
+        if (SymCryptDhSecretAgreement( privkey->a.dsa.handle, pubkey->a.dsa.handle,
+                                       SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, secret->derived_key,
+                                       secret->derived_key_len ))
+        {
+            destroy_secret( secret );
+            return STATUS_INTERNAL_ERROR;
+        }
+        break;
+    case ALG_ID_ECDH:
+    case ALG_ID_ECDH_P256:
+    case ALG_ID_ECDH_P384:
+    case ALG_ID_ECDH_P521:
+        if (SymCryptEcDhSecretAgreement( privkey->a.ecc.handle, pubkey->a.ecc.handle,
+                                         SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, 0, secret->derived_key,
+                                         secret->derived_key_len ))
+        {
+            destroy_secret( secret );
+            return STATUS_INTERNAL_ERROR;
+        }
+        break;
+    default:
+        FIXME( "algorithm %u not supported\n", privkey->alg_id );
+        destroy_secret( secret );
+        return STATUS_NOT_SUPPORTED;
     }
-    secret->derived_key_len = len;
 
     *ret_handle = secret;
     TRACE( "returning handle %p\n", *ret_handle );
@@ -2695,126 +3956,163 @@ NTSTATUS WINAPI BCryptDestroySecret( BCRYPT_SECRET_HANDLE handle )
 
     TRACE( "%p\n", handle );
 
-    if (!secret) return STATUS_INVALID_HANDLE;
-    SecureZeroMemory( secret->derived_key, secret->derived_key_len );
-    free( secret->derived_key );
-    destroy_object( &secret->hdr );
+    if (!secret || secret->hdr.magic != MAGIC_SECRET) return STATUS_INVALID_HANDLE;
+
+    destroy_secret( secret );
     return STATUS_SUCCESS;
 }
 
-static void reverse_bytes( UCHAR *buf, ULONG len )
+static const SYMCRYPT_MAC *get_mac_from_alg( enum alg_id alg )
 {
-    ULONG i;
-    for (i = 0; i < len / 2; i++)
+    switch (alg)
     {
-        UCHAR tmp = buf[i];
-        buf[i] = buf[len - i - 1];
-        buf[len - i - 1] = tmp;
+    case ALG_ID_SHA1:   return SymCryptHmacSha1Algorithm;
+    case ALG_ID_SHA256: return SymCryptHmacSha256Algorithm;
+    case ALG_ID_SHA384: return SymCryptHmacSha384Algorithm;
+    case ALG_ID_SHA512: return SymCryptHmacSha512Algorithm;
+    default:
+        FIXME( "hash algorithm %u not supported\n", alg );
+        return NULL;
     }
 }
 
-static NTSTATUS derive_key_raw( struct secret *secret, UCHAR *output, ULONG output_len, ULONG *ret_len )
+NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len,
+                                       ULONGLONG iterations, UCHAR *key, ULONG key_len, ULONG flags )
 {
-    if (output && output_len >= secret->derived_key_len)
-    {
-        memcpy( output, secret->derived_key, secret->derived_key_len );
-        reverse_bytes( output, secret->derived_key_len );
-    }
-    *ret_len = secret->derived_key_len;
-    return STATUS_SUCCESS;
-}
+    const struct algorithm *alg = get_alg_object( handle );
+    const SYMCRYPT_MAC *mac;
 
-static struct algorithm *get_hash_alg( BCryptBuffer *buf, BOOL hmac )
-{
-    const WCHAR *str = buf->pvBuffer;
-    BCRYPT_ALG_HANDLE handle = NULL;
+    TRACE( "%p, %p, %lu, %p, %lu, %s, %p, %lu, %#lx\n", handle, pwd, pwd_len, salt, salt_len,
+           wine_dbgstr_longlong(iterations), key, key_len, flags );
 
-    if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM ))
-        handle = hmac ? BCRYPT_HMAC_SHA1_ALG_HANDLE : BCRYPT_SHA1_ALG_HANDLE;
-    else if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM ))
-        handle = hmac ? BCRYPT_HMAC_SHA256_ALG_HANDLE : BCRYPT_SHA256_ALG_HANDLE;
-    else if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM ))
-        handle = hmac ? BCRYPT_HMAC_SHA384_ALG_HANDLE : BCRYPT_SHA384_ALG_HANDLE;
-    else if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM ))
-        handle = hmac ? BCRYPT_HMAC_SHA512_ALG_HANDLE : BCRYPT_SHA512_ALG_HANDLE;
-
-    if (handle) return get_alg_object( handle );
-    FIXME( "hash algorithm %s not supported\n", debugstr_w(str) );
-    return NULL;
+    if (!alg || !(alg->flags & BCRYPT_ALG_HANDLE_HMAC_FLAG)) return STATUS_INVALID_HANDLE;
+    if (!iterations) return STATUS_INVALID_PARAMETER;
+
+    if (!(mac = get_mac_from_alg( alg->id ))) return STATUS_NOT_SUPPORTED;
+
+    if (SymCryptPbkdf2( mac, pwd, pwd_len, salt, salt_len, iterations, key, key_len )) return STATUS_INTERNAL_ERROR;
+    return STATUS_SUCCESS;
 }
 
-static NTSTATUS derive_key_hash( struct secret *secret, BCryptBufferDesc *desc, UCHAR *output, ULONG output_len,
-                                 ULONG *ret_len )
+static const struct algorithm *get_hash_from_desc( const BCryptBufferDesc *desc )
 {
-    ULONG hash_len;
-    UCHAR hash_buf[MAX_HASH_OUTPUT_BYTES];
-    struct algorithm *alg = NULL;
-    NTSTATUS status;
+    const struct algorithm *alg = get_alg_object( BCRYPT_SHA1_ALG_HANDLE );
     ULONG i;
 
-    for (i = 0; i < (desc ? desc->cBuffers : 0); i++)
+    if (!desc) return alg;
+    for (i = 0; i < desc->cBuffers; i++)
     {
         if (desc->pBuffers[i].BufferType == KDF_HASH_ALGORITHM)
         {
-            alg = get_hash_alg( desc->pBuffers + i, FALSE );
-            if (!alg) return STATUS_NOT_SUPPORTED;
+            const WCHAR *str = desc->pBuffers[i].pvBuffer;
+
+            if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM )) alg = get_alg_object( BCRYPT_SHA1_ALG_HANDLE );
+            else if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) alg = get_alg_object( BCRYPT_SHA256_ALG_HANDLE );
+            else if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) alg = get_alg_object( BCRYPT_SHA384_ALG_HANDLE );
+            else if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) alg = get_alg_object( BCRYPT_SHA512_ALG_HANDLE );
+            else
+            {
+                FIXME( "hash %s not suported\n", debugstr_w(str) );
+                return NULL;
+            }
         }
         else FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType );
     }
-    if (!alg) alg = get_alg_object( BCRYPT_SHA1_ALG_HANDLE );
+    return alg;
+}
 
-    hash_len = builtin_algorithms[alg->id].hash_length;
-    assert( hash_len <= sizeof(hash_buf) );
-    if (!(status = hash_single( alg, NULL, 0, secret->derived_key, secret->derived_key_len, hash_buf, hash_len )))
+static NTSTATUS derive_key_hash( const struct secret *secret, const BCryptBufferDesc *desc, UCHAR *output,
+                                 ULONG output_len, ULONG *ret_len )
+{
+    const struct algorithm *alg;
+    ULONG len;
+    UCHAR hash[MAX_HASH_OUTPUT_BYTES];
+    NTSTATUS status;
+
+    if (!(alg = get_hash_from_desc( desc ))) return STATUS_NOT_SUPPORTED;
+    len = builtin_algorithms[alg->id].hash_length;
+
+    if (!output)
     {
-        if (!output) *ret_len = hash_len;
-        else
-        {
-            *ret_len = min( hash_len, output_len );
-            memcpy( output, hash_buf, *ret_len );
-        }
+        *ret_len = len;
+        return STATUS_SUCCESS;
     }
-    return status;
+
+    if ((status = hash_single( alg, NULL, 0, secret->derived_key, secret->derived_key_len, hash ))) return status;
+
+    len = min( len, output_len );
+    memcpy( output, hash, len );
+
+    *ret_len = len;
+    return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, BCryptBufferDesc *desc,
-                                 UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+static NTSTATUS derive_key_raw( const struct secret *secret, UCHAR *output, ULONG output_len, ULONG *ret_len )
 {
-    struct secret *secret = get_secret_object( handle );
+    ULONG i, len = secret->derived_key_len;
 
-    TRACE( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), desc, output, output_len,
-           ret_len, flags );
+    if (!output)
+    {
+        *ret_len = len;
+        return STATUS_SUCCESS;
+    }
+
+    len = min( len, output_len );
+    for (i = 0; i < len; i++) output[i] = secret->derived_key[len - i - 1];
+
+    *ret_len = len;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, BCryptBufferDesc *desc, UCHAR *output,
+                                 ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    const struct secret *secret = get_secret_object( handle );
+
+    TRACE( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), desc, output, output_len, ret_len, flags );
+    if (flags) FIXME( "ignoring flags %#lx\n", flags );
 
     if (!secret) return STATUS_INVALID_HANDLE;
     if (!kdf || !ret_len) return STATUS_INVALID_PARAMETER;
 
-    if (!wcscmp(kdf, BCRYPT_KDF_RAW_SECRET))
+    if (!wcscmp( kdf, BCRYPT_KDF_HASH ))
     {
-        return derive_key_raw( secret, output, output_len, ret_len );
+        return derive_key_hash( secret, desc, output, output_len, ret_len );
     }
-    else if (!wcscmp(kdf, BCRYPT_KDF_HASH))
+    if (!wcscmp( kdf, BCRYPT_KDF_RAW_SECRET ))
     {
-        return derive_key_hash( secret, desc, output, output_len, ret_len );
+        return derive_key_raw( secret, output, output_len, ret_len );
     }
 
     FIXME( "kdf %s not supportedi\n", debugstr_w(kdf) );
     return STATUS_NOT_SUPPORTED;
 }
 
-NTSTATUS WINAPI BCryptKeyDerivation( BCRYPT_KEY_HANDLE handle, BCryptBufferDesc *desc,
-                                     UCHAR *output, ULONG output_size, ULONG *ret_len, ULONG flags )
+static const SYMCRYPT_MAC *get_mac_from_buf( const BCryptBuffer *buf )
 {
-    struct key *key = get_key_object( handle );
-    struct algorithm *alg = NULL;
-    ULONGLONG iter_count = 10000;
-    ULONG salt_size = 0;
-    UCHAR *salt = NULL;
-    NTSTATUS status;
-    ULONG i;
+    const WCHAR *str = buf->pvBuffer;
+
+    if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM )) return SymCryptHmacSha1Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) return SymCryptHmacSha256Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) return SymCryptHmacSha384Algorithm;
+    if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) return SymCryptHmacSha512Algorithm;
+
+    FIXME( "hash algorithm %s not supported\n", debugstr_w(str) );
+    return NULL;
+}
+
+NTSTATUS WINAPI BCryptKeyDerivation( BCRYPT_KEY_HANDLE handle, BCryptBufferDesc *desc, UCHAR *output, ULONG output_len,
+                                     ULONG *ret_len, ULONG flags )
+{
+    const struct key *key = get_key_object( handle );
+    const SYMCRYPT_MAC *mac = NULL;
+    UINT64 iterations = 10000;
+    ULONG salt_len = 0, i;
+    const UCHAR *salt = NULL;
 
-    TRACE( "%p, %p, %p, %lu, %p, %#lx\n", key, desc, output, output_size, ret_len, flags );
+    TRACE( "%p, %p, %p, %lu, %p, %#lx\n", key, desc, output, output_len, ret_len, flags );
 
-    if (!key || !desc || !ret_len) return STATUS_INVALID_PARAMETER;
+    if (!key || !desc || !output || !ret_len) return STATUS_INVALID_PARAMETER;
     if (key->alg_id != ALG_ID_PBKDF2)
     {
         FIXME( "unsupported key %d\n", key->alg_id );
@@ -2826,26 +4124,28 @@ NTSTATUS WINAPI BCryptKeyDerivation( BCRYPT_KEY_HANDLE handle, BCryptBufferDesc
         switch (desc->pBuffers[i].BufferType)
         {
         case KDF_HASH_ALGORITHM:
-            alg = get_hash_alg( desc->pBuffers + i, TRUE );
+            mac = get_mac_from_buf( desc->pBuffers + i );
             break;
         case KDF_SALT:
             salt = desc->pBuffers[i].pvBuffer;
-            salt_size = desc->pBuffers[i].cbBuffer;
+            salt_len = desc->pBuffers[i].cbBuffer;
             break;
         case KDF_ITERATION_COUNT:
-            if (desc->pBuffers[i].cbBuffer != sizeof(ULONGLONG)) return STATUS_INVALID_PARAMETER;
-            iter_count = *(ULONGLONG *)desc->pBuffers[i].pvBuffer;
+            if (desc->pBuffers[i].cbBuffer != sizeof(iterations)) return STATUS_INVALID_PARAMETER;
+            iterations = *(UINT64 *)desc->pBuffers[i].pvBuffer;
             break;
         default:
             FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType );
-            break;
+            return STATUS_NOT_IMPLEMENTED;
         }
     }
+    if (!mac) return STATUS_INVALID_PARAMETER;
 
-    status = derive_key_pbkdf2( alg, key->u.s.secret, key->u.s.secret_len,
-            salt, salt_size, iter_count, output, output_size );
-    if (!status) *ret_len = output_size;
-    return status;
+    if (SymCryptPbkdf2( mac, key->s.secret, key->s.secret_len, salt, salt_len, iterations, output, output_len ))
+        return STATUS_INTERNAL_ERROR;
+
+    *ret_len = output_len;
+    return STATUS_SUCCESS;
 }
 
 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
@@ -2854,14 +4154,10 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
     {
     case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls( hinst );
-        if (!__wine_init_unix_call())
-        {
-            if (UNIX_CALL( process_attach, NULL)) __wine_unixlib_handle = 0;
-        }
+        SymCryptInit();
         break;
     case DLL_PROCESS_DETACH:
         if (reserved) break;
-        if (__wine_unixlib_handle) UNIX_CALL( process_detach, NULL );
         break;
     }
     return TRUE;
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c
deleted file mode 100644
index e8c6b6dd8026..000000000000
--- a/dlls/bcrypt/gnutls.c
+++ /dev/null
@@ -1,3594 +0,0 @@
-/*
- * Copyright 2009 Henri Verbeet for CodeWeavers
- * Copyright 2018 Hans Leidekker for CodeWeavers
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- */
-
-#if 0
-#pragma makedep unix
-#endif
-
-#include "config.h"
-
-#ifdef HAVE_GNUTLS_CIPHER_INIT
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <dlfcn.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
-#include <gnutls/abstract.h>
-
-#include "ntstatus.h"
-#include "windef.h"
-#include "winbase.h"
-#include "winternl.h"
-#include "ntsecapi.h"
-#include "wincrypt.h"
-#include "bcrypt.h"
-
-#include "bcrypt_internal.h"
-
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(bcrypt);
-WINE_DECLARE_DEBUG_CHANNEL(winediag);
-
-#if GNUTLS_VERSION_MAJOR < 3
-#define GNUTLS_CIPHER_AES_192_CBC 92
-#define GNUTLS_CIPHER_AES_128_GCM 93
-#define GNUTLS_CIPHER_AES_256_GCM 94
-#define GNUTLS_PK_ECC 4
-
-#define GNUTLS_CURVE_TO_BITS(curve) (unsigned int)(((unsigned int)1<<31)|((unsigned int)(curve)))
-
-typedef enum
-{
-    GNUTLS_ECC_CURVE_INVALID,
-    GNUTLS_ECC_CURVE_SECP224R1,
-    GNUTLS_ECC_CURVE_SECP256R1,
-    GNUTLS_ECC_CURVE_SECP384R1,
-    GNUTLS_ECC_CURVE_SECP521R1,
-} gnutls_ecc_curve_t;
-#endif
-
-#if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 6)
-#define GNUTLS_CIPHER_AES_128_CFB8 29
-#define GNUTLS_CIPHER_AES_192_CFB8 30
-#define GNUTLS_CIPHER_AES_256_CFB8 31
-
-#define GNUTLS_PK_RSA_PSS 6
-#define GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS (1 << 7)
-typedef struct gnutls_x509_spki_st *gnutls_x509_spki_t;
-#endif
-
-#if GUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 8)
-#define GNUTLS_KEYGEN_DH 4
-#endif
-
-union key_data
-{
-    gnutls_cipher_hd_t cipher;
-    struct
-    {
-        gnutls_privkey_t   privkey;
-        gnutls_pubkey_t    pubkey;
-        gnutls_dh_params_t dh_params;
-    } a;
-};
-C_ASSERT( sizeof(union key_data) <= sizeof(((struct key *)0)->private) );
-
-static union key_data *key_data( struct key *key )
-{
-    return (union key_data *)key->private;
-}
-
-/* Not present in gnutls version < 3.0 */
-static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t);
-static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t);
-static gnutls_sign_algorithm_t (*pgnutls_pk_to_sign)(gnutls_pk_algorithm_t, gnutls_digest_algorithm_t);
-static int (*pgnutls_pubkey_import_ecc_raw)(gnutls_pubkey_t, gnutls_ecc_curve_t,
-                                            const gnutls_datum_t *, const gnutls_datum_t *);
-static int (*pgnutls_pubkey_export_ecc_raw)(gnutls_pubkey_t key, gnutls_ecc_curve_t *curve,
-                                            gnutls_datum_t *x, gnutls_datum_t *y);
-static int (*pgnutls_privkey_import_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t, const gnutls_datum_t *,
-                                             const gnutls_datum_t *, const gnutls_datum_t *);
-static int (*pgnutls_pubkey_verify_hash2)(gnutls_pubkey_t, gnutls_sign_algorithm_t, unsigned int,
-                                          const gnutls_datum_t *, const gnutls_datum_t *);
-static int (*pgnutls_pubkey_encrypt_data)(gnutls_pubkey_t, unsigned int flags, const gnutls_datum_t *,
-                                          gnutls_datum_t *);
-
-/* Not present in gnutls version < 2.11.0 */
-static int (*pgnutls_pubkey_import_rsa_raw)(gnutls_pubkey_t, const gnutls_datum_t *, const gnutls_datum_t *);
-
-/* Not present in gnutls version < 2.12.0 */
-static int (*pgnutls_pubkey_import_dsa_raw)(gnutls_pubkey_t, const gnutls_datum_t *, const gnutls_datum_t *,
-                                            const gnutls_datum_t *, const gnutls_datum_t *);
-static int (*pgnutls_pubkey_import_privkey)(gnutls_pubkey_t, gnutls_privkey_t, unsigned int, unsigned int);
-static int (*pgnutls_privkey_decrypt_data)(gnutls_privkey_t, unsigned int flags, const gnutls_datum_t *,
-                                           gnutls_datum_t *);
-
-/* Not present in gnutls version < 3.3.0 */
-static int (*pgnutls_pubkey_export_dsa_raw)(gnutls_pubkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *,
-                                            gnutls_datum_t *);
-static int (*pgnutls_pubkey_export_rsa_raw)(gnutls_pubkey_t, gnutls_datum_t *, gnutls_datum_t *);
-static int (*pgnutls_privkey_export_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t *,
-                                             gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *);
-static int (*pgnutls_privkey_export_rsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *,
-                                             gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *,
-                                             gnutls_datum_t *);
-static int (*pgnutls_privkey_export_dsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *,
-                                             gnutls_datum_t *, gnutls_datum_t *);
-static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *, const gnutls_datum_t *,
-                                             const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *,
-                                             const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *);
-
-/* Not present in gnutls version < 3.5.0 */
-static int (*pgnutls_privkey_generate2)(gnutls_privkey_t, gnutls_pk_algorithm_t, unsigned int, unsigned int,
-                                        const gnutls_keygen_data_st *, unsigned);
-
-/* Not present in gnutls version < 3.6.0 */
-static int (*pgnutls_decode_rs_value)(const gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *);
-static int (*pgnutls_x509_spki_init)(gnutls_x509_spki_t *);
-static void (*pgnutls_x509_spki_deinit)(gnutls_x509_spki_t);
-static void (*pgnutls_x509_spki_set_rsa_pss_params)(gnutls_x509_spki_t, gnutls_digest_algorithm_t, unsigned int);
-static int (*pgnutls_pubkey_set_spki)(gnutls_pubkey_t, const gnutls_x509_spki_t, unsigned int);
-static int (*pgnutls_privkey_set_spki)(gnutls_privkey_t, const gnutls_x509_spki_t, unsigned int);
-
-/* Not present in gnutls version < 3.8.2 */
-static int (*pgnutls_privkey_derive_secret)(gnutls_privkey_t, gnutls_pubkey_t, const gnutls_datum_t *,
-                                            gnutls_datum_t *, unsigned int);
-static int (*pgnutls_privkey_export_dh_raw)(gnutls_privkey_t, gnutls_dh_params_t, gnutls_datum_t *, gnutls_datum_t *,
-                                            unsigned int);
-static int (*pgnutls_pubkey_export_dh_raw)(gnutls_pubkey_t, gnutls_dh_params_t, gnutls_datum_t *, unsigned);
-static int (*pgnutls_privkey_import_dh_raw)(gnutls_privkey_t, const gnutls_dh_params_t, const gnutls_datum_t *,
-                                            const gnutls_datum_t *);
-static int (*pgnutls_pubkey_import_dh_raw)(gnutls_pubkey_t, const gnutls_dh_params_t, const gnutls_datum_t *);
-
-/* Not present in gnutls version < 3.8.4 */
-static int (*pgnutls_x509_spki_set_rsa_oaep_params)(gnutls_x509_spki_t, gnutls_digest_algorithm_t, gnutls_datum_t *);
-
-static void *libgnutls_handle;
-#define MAKE_FUNCPTR(f) static typeof(f) * p##f
-MAKE_FUNCPTR(gnutls_cipher_decrypt2);
-MAKE_FUNCPTR(gnutls_cipher_deinit);
-MAKE_FUNCPTR(gnutls_cipher_encrypt2);
-MAKE_FUNCPTR(gnutls_cipher_init);
-MAKE_FUNCPTR(gnutls_dh_params_deinit);
-MAKE_FUNCPTR(gnutls_dh_params_export_raw);
-MAKE_FUNCPTR(gnutls_dh_params_import_raw);
-MAKE_FUNCPTR(gnutls_dh_params_init);
-MAKE_FUNCPTR(gnutls_global_deinit);
-MAKE_FUNCPTR(gnutls_global_init);
-MAKE_FUNCPTR(gnutls_global_set_log_function);
-MAKE_FUNCPTR(gnutls_global_set_log_level);
-MAKE_FUNCPTR(gnutls_perror);
-MAKE_FUNCPTR(gnutls_privkey_decrypt_data);
-MAKE_FUNCPTR(gnutls_privkey_deinit);
-MAKE_FUNCPTR(gnutls_privkey_import_dsa_raw);
-MAKE_FUNCPTR(gnutls_privkey_init);
-MAKE_FUNCPTR(gnutls_privkey_sign_hash);
-MAKE_FUNCPTR(gnutls_pubkey_deinit);
-MAKE_FUNCPTR(gnutls_pubkey_encrypt_data);
-MAKE_FUNCPTR(gnutls_pubkey_import_privkey);
-MAKE_FUNCPTR(gnutls_pubkey_init);
-#undef MAKE_FUNCPTR
-
-static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static int compat_gnutls_cipher_add_auth(gnutls_cipher_hd_t handle, const void *ptext, size_t ptext_size)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static int compat_gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t curve,
-                                               const gnutls_datum_t *x, const gnutls_datum_t *y)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_export_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t *curve,
-                                               gnutls_datum_t *x, gnutls_datum_t *y)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_export_dsa_raw(gnutls_pubkey_t key, gnutls_datum_t *p, gnutls_datum_t *q,
-                                               gnutls_datum_t *g, gnutls_datum_t *y)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_export_rsa_raw(gnutls_pubkey_t key, gnutls_datum_t *m, gnutls_datum_t *e)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_export_rsa_raw(gnutls_privkey_t key, gnutls_datum_t *m, gnutls_datum_t *e,
-                                                gnutls_datum_t *d, gnutls_datum_t *p, gnutls_datum_t *q,
-                                                gnutls_datum_t *u, gnutls_datum_t *e1, gnutls_datum_t *e2)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_export_ecc_raw(gnutls_privkey_t key, gnutls_ecc_curve_t *curve,
-                                                gnutls_datum_t *x, gnutls_datum_t *y, gnutls_datum_t *k)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_import_ecc_raw(gnutls_privkey_t key, gnutls_ecc_curve_t curve,
-                                                const gnutls_datum_t *x, const gnutls_datum_t *y,
-                                                const gnutls_datum_t *k)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_export_dsa_raw(gnutls_privkey_t key, gnutls_datum_t *p, gnutls_datum_t *q,
-                                                gnutls_datum_t *g, gnutls_datum_t *y, gnutls_datum_t *x)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static gnutls_sign_algorithm_t compat_gnutls_pk_to_sign(gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t hash)
-{
-    return GNUTLS_SIGN_UNKNOWN;
-}
-
-static int compat_gnutls_pubkey_verify_hash2(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo,
-                                             unsigned int flags, const gnutls_datum_t *hash,
-                                             const gnutls_datum_t *signature)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_import_rsa_raw(gnutls_pubkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_import_dsa_raw(gnutls_pubkey_t key, const gnutls_datum_t *p, const gnutls_datum_t *q,
-                                               const gnutls_datum_t *g, const gnutls_datum_t *y)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_decode_rs_value(const gnutls_datum_t * sig_value, gnutls_datum_t * r, gnutls_datum_t * s)
-{
-    return GNUTLS_E_INTERNAL_ERROR;
-}
-
-static int compat_gnutls_privkey_import_rsa_raw(gnutls_privkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e,
-                                                const gnutls_datum_t *d, const gnutls_datum_t *p, const gnutls_datum_t *q,
-                                                const gnutls_datum_t *u, const gnutls_datum_t *e1, const gnutls_datum_t *e2)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_decrypt_data(gnutls_privkey_t key, unsigned int flags, const gnutls_datum_t *cipher_text,
-                                              gnutls_datum_t *plain_text)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_encrypt_data(gnutls_pubkey_t key, unsigned int flags, const gnutls_datum_t *cipher_text,
-                                              gnutls_datum_t *plain_text)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_x509_spki_init(gnutls_x509_spki_t *spki)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static void compat_gnutls_x509_spki_deinit(gnutls_x509_spki_t spki)
-{
-}
-
-static int compat_gnutls_x509_spki_set_rsa_oaep_params(gnutls_x509_spki_t spki, gnutls_digest_algorithm_t dig,
-                                                       gnutls_datum_t *label)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static void compat_gnutls_x509_spki_set_rsa_pss_params(gnutls_x509_spki_t spki, gnutls_digest_algorithm_t dig,
-                                                       unsigned int salt_size)
-{
-}
-
-static int compat_gnutls_pubkey_set_spki(gnutls_pubkey_t key, const gnutls_x509_spki_t spki, unsigned int flags)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_set_spki(gnutls_privkey_t key, const gnutls_x509_spki_t spki, unsigned int flags)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_derive_secret(gnutls_privkey_t privkey, gnutls_pubkey_t pubkey, const gnutls_datum_t *nonce,
-                                               gnutls_datum_t *secret, unsigned int flags)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_export_dh_raw(gnutls_privkey_t privkey, gnutls_dh_params_t params, gnutls_datum_t *y,
-                                               gnutls_datum_t *x, unsigned int flags )
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_export_dh_raw(gnutls_pubkey_t pubkey, gnutls_dh_params_t params, gnutls_datum_t *y,
-                                              unsigned flags)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_import_dh_raw(gnutls_privkey_t privkey, const gnutls_dh_params_t params,
-                                               const gnutls_datum_t *y, const gnutls_datum_t *x)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_pubkey_import_dh_raw(gnutls_pubkey_t pubkey, const gnutls_dh_params_t params,
-                                              const gnutls_datum_t *y)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static int compat_gnutls_privkey_generate2(gnutls_privkey_t privkey, gnutls_pk_algorithm_t alg, unsigned int bits,
-                                           unsigned int flags, const gnutls_keygen_data_st *data, unsigned data_size)
-{
-    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-}
-
-static void gnutls_log( int level, const char *msg )
-{
-    TRACE( "<%d> %s", level, msg );
-}
-
-static NTSTATUS gnutls_process_attach( void *args )
-{
-    const char *env_str;
-    int ret;
-
-    if ((env_str = getenv("GNUTLS_SYSTEM_PRIORITY_FILE")))
-    {
-        WARN("GNUTLS_SYSTEM_PRIORITY_FILE is %s.\n", debugstr_a(env_str));
-    }
-    else
-    {
-        WARN("Setting GNUTLS_SYSTEM_PRIORITY_FILE to \"/dev/null\".\n");
-        setenv("GNUTLS_SYSTEM_PRIORITY_FILE", "/dev/null", 0);
-    }
-
-    if (!(libgnutls_handle = dlopen( SONAME_LIBGNUTLS, RTLD_NOW )))
-    {
-        ERR_(winediag)( "failed to load libgnutls, no support for encryption\n" );
-        return STATUS_DLL_NOT_FOUND;
-    }
-
-#define LOAD_FUNCPTR(f) \
-    if (!(p##f = dlsym( libgnutls_handle, #f ))) \
-    { \
-        ERR( "failed to load %s\n", #f ); \
-        goto fail; \
-    }
-
-    LOAD_FUNCPTR(gnutls_cipher_decrypt2)
-    LOAD_FUNCPTR(gnutls_cipher_deinit)
-    LOAD_FUNCPTR(gnutls_cipher_encrypt2)
-    LOAD_FUNCPTR(gnutls_cipher_init)
-    LOAD_FUNCPTR(gnutls_dh_params_deinit)
-    LOAD_FUNCPTR(gnutls_dh_params_export_raw)
-    LOAD_FUNCPTR(gnutls_dh_params_import_raw)
-    LOAD_FUNCPTR(gnutls_dh_params_init)
-    LOAD_FUNCPTR(gnutls_global_deinit)
-    LOAD_FUNCPTR(gnutls_global_init)
-    LOAD_FUNCPTR(gnutls_global_set_log_function)
-    LOAD_FUNCPTR(gnutls_global_set_log_level)
-    LOAD_FUNCPTR(gnutls_perror)
-    LOAD_FUNCPTR(gnutls_privkey_deinit);
-    LOAD_FUNCPTR(gnutls_privkey_import_dsa_raw);
-    LOAD_FUNCPTR(gnutls_privkey_init);
-    LOAD_FUNCPTR(gnutls_privkey_sign_hash);
-    LOAD_FUNCPTR(gnutls_pubkey_deinit);
-    LOAD_FUNCPTR(gnutls_pubkey_import_privkey);
-    LOAD_FUNCPTR(gnutls_pubkey_init);
-#undef LOAD_FUNCPTR
-
-#define LOAD_FUNCPTR_OPT(f) \
-    if (!(p##f = dlsym( libgnutls_handle, #f ))) \
-    { \
-        WARN( "failed to load %s\n", #f ); \
-        p##f =  compat_##f; \
-    }
-
-    LOAD_FUNCPTR_OPT(gnutls_cipher_tag)
-    LOAD_FUNCPTR_OPT(gnutls_cipher_add_auth)
-    LOAD_FUNCPTR_OPT(gnutls_decode_rs_value)
-    LOAD_FUNCPTR_OPT(gnutls_pk_to_sign)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_decrypt_data)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_derive_secret)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_export_dh_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_export_dsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_export_ecc_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_export_rsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_generate2)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_import_dh_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_import_ecc_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_import_rsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_privkey_set_spki)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_encrypt_data)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_export_dh_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_export_dsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_export_ecc_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_export_rsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_import_dh_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_import_dsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_import_ecc_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_import_rsa_raw)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_set_spki)
-    LOAD_FUNCPTR_OPT(gnutls_pubkey_verify_hash2)
-    LOAD_FUNCPTR_OPT(gnutls_x509_spki_deinit)
-    LOAD_FUNCPTR_OPT(gnutls_x509_spki_init)
-    LOAD_FUNCPTR_OPT(gnutls_x509_spki_set_rsa_oaep_params)
-    LOAD_FUNCPTR_OPT(gnutls_x509_spki_set_rsa_pss_params)
-
-#undef LOAD_FUNCPTR_OPT
-
-    if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS)
-    {
-        pgnutls_perror( ret );
-        goto fail;
-    }
-
-    if (TRACE_ON( bcrypt ))
-    {
-        char *env = getenv("GNUTLS_DEBUG_LEVEL");
-        int level = env ? atoi(env) : 4;
-        pgnutls_global_set_log_level(level);
-        pgnutls_global_set_log_function( gnutls_log );
-    }
-
-    return STATUS_SUCCESS;
-
-fail:
-    dlclose( libgnutls_handle );
-    libgnutls_handle = NULL;
-    return STATUS_DLL_NOT_FOUND;
-}
-
-static NTSTATUS gnutls_process_detach( void *args )
-{
-    if (libgnutls_handle)
-    {
-        if (TRACE_ON( bcrypt ))
-            pgnutls_global_set_log_function( NULL );
-        pgnutls_global_deinit();
-        dlclose( libgnutls_handle );
-        libgnutls_handle = NULL;
-    }
-    return STATUS_SUCCESS;
-}
-
-struct buffer
-{
-    BYTE  *buffer;
-    DWORD  length;
-    DWORD  pos;
-    BOOL   error;
-};
-
-static void buffer_init( struct buffer *buffer )
-{
-    buffer->buffer = NULL;
-    buffer->length = 0;
-    buffer->pos    = 0;
-    buffer->error  = FALSE;
-}
-
-static void buffer_free( struct buffer *buffer )
-{
-    free( buffer->buffer );
-}
-
-static void buffer_append( struct buffer *buffer, BYTE *data, DWORD len )
-{
-    if (!len) return;
-
-    if (buffer->pos + len > buffer->length)
-    {
-        DWORD new_length = max( max( buffer->pos + len, buffer->length * 2 ), 64 );
-        BYTE *new_buffer;
-
-        if (!(new_buffer = realloc( buffer->buffer, new_length )))
-        {
-            ERR( "out of memory\n" );
-            buffer->error = TRUE;
-            return;
-        }
-
-        buffer->buffer = new_buffer;
-        buffer->length = new_length;
-    }
-
-    memcpy( &buffer->buffer[buffer->pos], data, len );
-    buffer->pos += len;
-}
-
-static void buffer_append_byte( struct buffer *buffer, BYTE value )
-{
-    buffer_append( buffer, &value, sizeof(value) );
-}
-
-static void buffer_append_asn1_length( struct buffer *buffer, DWORD length )
-{
-    DWORD num_bytes;
-
-    if (length < 128)
-    {
-        buffer_append_byte( buffer, length );
-        return;
-    }
-
-    if (length <= 0xff) num_bytes = 1;
-    else if (length <= 0xffff) num_bytes = 2;
-    else if (length <= 0xffffff) num_bytes = 3;
-    else num_bytes = 4;
-
-    buffer_append_byte( buffer, 0x80 | num_bytes );
-    while (num_bytes--) buffer_append_byte( buffer, length >> (num_bytes * 8) );
-}
-
-static void buffer_append_asn1_integer( struct buffer *buffer, BYTE *data, DWORD len )
-{
-    DWORD leading_zero = (*data & 0x80) != 0;
-
-    buffer_append_byte( buffer, 0x02 );  /* tag */
-    buffer_append_asn1_length( buffer, len + leading_zero );
-    if (leading_zero) buffer_append_byte( buffer, 0 );
-    buffer_append( buffer, data, len );
-}
-
-static void buffer_append_asn1_sequence( struct buffer *buffer, struct buffer *content )
-{
-    if (content->error)
-    {
-        buffer->error = TRUE;
-        return;
-    }
-
-    buffer_append_byte( buffer, 0x30 );  /* tag */
-    buffer_append_asn1_length( buffer, content->pos );
-    buffer_append( buffer, content->buffer, content->pos );
-}
-
-static void buffer_append_asn1_r_s( struct buffer *buffer, BYTE *r, DWORD r_len, BYTE *s, DWORD s_len )
-{
-    struct buffer value;
-
-    buffer_init( &value );
-    buffer_append_asn1_integer( &value, r, r_len );
-    buffer_append_asn1_integer( &value, s, s_len );
-    buffer_append_asn1_sequence( buffer, &value );
-    buffer_free( &value );
-}
-
-static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key )
-{
-    switch (key->alg_id)
-    {
-    case ALG_ID_3DES:
-        WARN( "handle block size\n" );
-        switch (key->u.s.mode)
-        {
-        case CHAIN_MODE_CBC:
-            return GNUTLS_CIPHER_3DES_CBC;
-        default:
-            break;
-        }
-        FIXME( "3DES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len );
-        return GNUTLS_CIPHER_UNKNOWN;
-
-    case ALG_ID_AES:
-        WARN( "handle block size\n" );
-        switch (key->u.s.mode)
-        {
-        case CHAIN_MODE_GCM:
-            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_GCM;
-            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_GCM;
-            break;
-        case CHAIN_MODE_ECB: /* can be emulated with CBC + empty IV */
-        case CHAIN_MODE_CBC:
-            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_CBC;
-            if (key->u.s.secret_len == 24) return GNUTLS_CIPHER_AES_192_CBC;
-            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_CBC;
-            break;
-        case CHAIN_MODE_CFB:
-            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_CFB8;
-            if (key->u.s.secret_len == 24) return GNUTLS_CIPHER_AES_192_CFB8;
-            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_CFB8;
-            break;
-        default:
-            break;
-        }
-        FIXME( "AES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len );
-        return GNUTLS_CIPHER_UNKNOWN;
-
-    default:
-        FIXME( "algorithm %u not supported\n", key->alg_id );
-        return GNUTLS_CIPHER_UNKNOWN;
-    }
-}
-
-static NTSTATUS key_symmetric_vector_reset( void *args )
-{
-    struct key *key = args;
-
-    if (!key_data(key)->cipher) return STATUS_SUCCESS;
-    TRACE( "invalidating cipher handle\n" );
-    pgnutls_cipher_deinit( key_data(key)->cipher );
-    key_data(key)->cipher = NULL;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS init_cipher_handle( struct key *key )
-{
-    gnutls_cipher_algorithm_t cipher;
-    gnutls_datum_t secret, vector;
-    int ret;
-
-    if (key_data(key)->cipher) return STATUS_SUCCESS;
-    if ((cipher = get_gnutls_cipher( key )) == GNUTLS_CIPHER_UNKNOWN) return STATUS_NOT_SUPPORTED;
-
-    secret.data = key->u.s.secret;
-    secret.size = key->u.s.secret_len;
-
-    vector.data = key->u.s.vector;
-    vector.size = key->u.s.vector_len;
-
-    if ((ret = pgnutls_cipher_init( &key_data(key)->cipher, cipher, &secret, key->u.s.vector ? &vector : NULL )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_set_auth_data( void *args )
-{
-    const struct key_symmetric_set_auth_data_params *params = args;
-    NTSTATUS status;
-    int ret;
-
-    if (!params->auth_data) return STATUS_SUCCESS;
-    if ((status = init_cipher_handle( params->key ))) return status;
-
-    if ((ret = pgnutls_cipher_add_auth( key_data(params->key)->cipher, params->auth_data, params->len )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_encrypt( void *args )
-{
-    const struct key_symmetric_encrypt_params *params = args;
-    NTSTATUS status;
-    int ret;
-
-    if ((status = init_cipher_handle( params->key ))) return status;
-
-    if ((ret = pgnutls_cipher_encrypt2( key_data(params->key)->cipher, params->input, params->input_len,
-                                        params->output, params->output_len )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_decrypt( void *args )
-{
-    const struct key_symmetric_decrypt_params *params = args;
-    NTSTATUS status;
-    int ret;
-
-    if ((status = init_cipher_handle( params->key ))) return status;
-
-    if ((ret = pgnutls_cipher_decrypt2( key_data(params->key)->cipher, params->input, params->input_len,
-                                        params->output, params->output_len )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_get_tag( void *args )
-{
-    const struct key_symmetric_get_tag_params *params = args;
-    NTSTATUS status;
-    int ret;
-
-    if ((status = init_cipher_handle( params->key ))) return status;
-
-    if ((ret = pgnutls_cipher_tag( key_data(params->key)->cipher, params->tag, params->len )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_destroy( void *args )
-{
-    struct key *key = args;
-
-    if (key_data(key)->cipher) pgnutls_cipher_deinit( key_data(key)->cipher );
-    return STATUS_SUCCESS;
-}
-
-static ULONG export_gnutls_datum( UCHAR *buffer, ULONG buflen, gnutls_datum_t *d, BOOL zero_pad )
-{
-    ULONG size = d->size;
-    UCHAR *src = d->data;
-    ULONG offset = 0;
-
-    assert( size <= buflen + 1 );
-    if (size == buflen + 1)
-    {
-        assert( !src[0] );
-        src++;
-        size--;
-    }
-    if (zero_pad)
-    {
-        offset = buflen - size;
-        if (buffer) memset( buffer, 0, offset );
-        size = buflen;
-    }
-
-    if (buffer) memcpy( buffer + offset, src, size - offset );
-    return size;
-}
-
-#define EXPORT_SIZE(d,l,p) export_gnutls_datum( NULL, l, &d, p )
-static NTSTATUS key_export_rsa_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
-    gnutls_datum_t m, e;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    UCHAR *dst;
-    int ret;
-
-    if (key_data(key)->a.pubkey)
-        ret = pgnutls_pubkey_export_rsa_raw( key_data(key)->a.pubkey, &m, &e );
-    else
-        return STATUS_INVALID_PARAMETER;
-
-    if (ret)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *ret_len = sizeof(*rsa_blob) + EXPORT_SIZE( e, size, 0 ) + EXPORT_SIZE( m, size, 1 );
-    if (len >= *ret_len && buf)
-    {
-        dst = (UCHAR *)(rsa_blob + 1);
-        rsa_blob->cbPublicExp = export_gnutls_datum( dst, size, &e, 0 );
-
-        dst += rsa_blob->cbPublicExp;
-        rsa_blob->cbModulus = export_gnutls_datum( dst, size, &m, 1 );
-
-        rsa_blob->Magic     = BCRYPT_RSAPUBLIC_MAGIC;
-        rsa_blob->BitLength = key->u.a.bitlen;
-        rsa_blob->cbPrime1  = 0;
-        rsa_blob->cbPrime2  = 0;
-    }
-
-    free( e.data ); free( m.data );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_ecc_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
-    gnutls_ecc_curve_t curve;
-    gnutls_datum_t x, y;
-    DWORD magic, size;
-    UCHAR *dst;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDH:
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_25519:
-            magic = BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC;
-            size = 32;
-            break;
-        case ECC_CURVE_P256R1:
-            magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC;
-            size = 32;
-            break;
-        case ECC_CURVE_P384R1:
-            magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC;
-            size = 48;
-            break;
-        case ECC_CURVE_P521R1:
-            magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC;
-            size = 66;
-            break;
-        default:
-            FIXME( "unsupported curve %u\n", key->u.a.curve_id );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        break;
-
-    case ALG_ID_ECDH_P256:
-        magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC;
-        size = 32;
-        break;
-
-    case ALG_ID_ECDH_P384:
-        magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC;
-        size = 48;
-        break;
-
-    case ALG_ID_ECDH_P521:
-        magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC;
-        size = 66;
-        break;
-
-    case ALG_ID_ECDSA:
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_P256R1:
-            magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
-            size = 32;
-            break;
-        case ECC_CURVE_P384R1:
-            magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
-            size = 48;
-            break;
-        case ECC_CURVE_P521R1:
-            magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC;
-            size = 66;
-            break;
-        default:
-            FIXME( "unsupported curve %u\n", key->u.a.curve_id );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        break;
-
-    case ALG_ID_ECDSA_P256:
-        magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
-        size = 32;
-        break;
-
-    case ALG_ID_ECDSA_P384:
-        magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
-        size = 48;
-        break;
-
-    case ALG_ID_ECDSA_P521:
-        magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC;
-        size = 66;
-        break;
-
-    default:
-        FIXME( "algorithm %u not supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (key_data(key)->a.pubkey)
-        ret = pgnutls_pubkey_export_ecc_raw( key_data(key)->a.pubkey, &curve, &x, &y );
-    else
-        return STATUS_INVALID_PARAMETER;
-
-    if (ret)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (curve != GNUTLS_ECC_CURVE_X25519 && curve != GNUTLS_ECC_CURVE_SECP256R1 &&
-        curve != GNUTLS_ECC_CURVE_SECP384R1 && curve != GNUTLS_ECC_CURVE_SECP521R1)
-    {
-        FIXME( "curve %u not supported\n", curve );
-        free( x.data ); free( y.data );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    *ret_len = sizeof(*ecc_blob) + EXPORT_SIZE( x, size, 1 ) + EXPORT_SIZE( y, size, 1 );
-    if (len >= *ret_len && buf)
-    {
-        ecc_blob->dwMagic = magic;
-        ecc_blob->cbKey   = size;
-
-        dst = (UCHAR *)(ecc_blob + 1);
-        dst += export_gnutls_datum( dst, size, &x, 1 );
-        export_gnutls_datum( dst, size, &y, 1 );
-    }
-
-    free( x.data ); free( y.data );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_dsa_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf;
-    gnutls_datum_t p, q, g, y;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    NTSTATUS status = STATUS_SUCCESS;
-    UCHAR *dst;
-    int ret;
-
-    if (key->u.a.bitlen > 1024)
-    {
-        FIXME( "bitlen > 1024 not supported\n" );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (key_data(key)->a.pubkey)
-        ret = pgnutls_pubkey_export_dsa_raw( key_data(key)->a.pubkey, &p, &q, &g, &y );
-    else
-        return STATUS_INVALID_PARAMETER;
-
-    if (ret)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (EXPORT_SIZE( q, sizeof(dsa_blob->q), 1 ) > sizeof(dsa_blob->q))
-    {
-        status = STATUS_INVALID_PARAMETER;
-        goto done;
-    }
-
-    *ret_len = sizeof(*dsa_blob) + EXPORT_SIZE( p, size, 1 ) + EXPORT_SIZE( g, size, 1 ) + EXPORT_SIZE( y, size, 1 );
-    if (len >= *ret_len && buf)
-    {
-        dst = (UCHAR *)(dsa_blob + 1);
-        dst += export_gnutls_datum( dst, size, &p, 1 );
-        dst += export_gnutls_datum( dst, size, &g, 1 );
-        export_gnutls_datum( dst, size, &y, 1 );
-
-        dst = dsa_blob->q;
-        export_gnutls_datum( dst, sizeof(dsa_blob->q), &q, 1 );
-
-        dsa_blob->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC;
-        dsa_blob->cbKey   = size;
-        memset( dsa_blob->Count, 0, sizeof(dsa_blob->Count) ); /* FIXME */
-        memset( dsa_blob->Seed, 0, sizeof(dsa_blob->Seed) ); /* FIXME */
-    }
-
-done:
-    free( p.data ); free( q.data ); free( g.data ); free( y.data );
-    return status;
-}
-
-static void reverse_bytes( UCHAR *buf, ULONG len )
-{
-    unsigned int i;
-    UCHAR tmp;
-
-    for (i = 0; i < len / 2; ++i)
-    {
-        tmp = buf[i];
-        buf[i] = buf[len - i - 1];
-        buf[len - i - 1] = tmp;
-    }
-}
-
-#define Q_SIZE 20
-static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BLOBHEADER *hdr = (BLOBHEADER *)buf;
-    DSSPUBKEY *dsskey;
-    gnutls_datum_t p, q, g, y;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    NTSTATUS status = STATUS_SUCCESS;
-    UCHAR *dst;
-    int ret;
-
-    if (key->u.a.bitlen > 1024)
-    {
-        FIXME( "bitlen > 1024 not supported\n" );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (key_data(key)->a.pubkey)
-        ret = pgnutls_pubkey_export_dsa_raw( key_data(key)->a.pubkey, &p, &q, &g, &y );
-    else if (key_data(key)->a.privkey)
-        ret = pgnutls_privkey_export_dsa_raw( key_data(key)->a.privkey, &p, &q, &g, &y, NULL );
-    else
-        return STATUS_INVALID_PARAMETER;
-
-    if (ret)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (EXPORT_SIZE( q, Q_SIZE, 1 ) > Q_SIZE)
-    {
-        status = STATUS_INVALID_PARAMETER;
-        goto done;
-    }
-
-    *ret_len = sizeof(*hdr) + sizeof(*dsskey) + sizeof(key->u.a.dss_seed) +
-               EXPORT_SIZE( p, size, 1 ) + Q_SIZE + EXPORT_SIZE( g, size, 1 ) + EXPORT_SIZE( y, size, 1 );
-    if (len >= *ret_len && buf)
-    {
-        hdr->bType    = PUBLICKEYBLOB;
-        hdr->bVersion = 2;
-        hdr->reserved = 0;
-        hdr->aiKeyAlg = CALG_DSS_SIGN;
-
-        dsskey = (DSSPUBKEY *)(hdr + 1);
-        dsskey->magic  = MAGIC_DSS1;
-        dsskey->bitlen = key->u.a.bitlen;
-
-        dst = (UCHAR *)(dsskey + 1);
-        export_gnutls_datum( dst, size, &p, 1 );
-        reverse_bytes( dst, size );
-        dst += size;
-
-        export_gnutls_datum( dst, Q_SIZE, &q, 1 );
-        reverse_bytes( dst, Q_SIZE );
-        dst += Q_SIZE;
-
-        export_gnutls_datum( dst, size, &g, 1 );
-        reverse_bytes( dst, size );
-        dst += size;
-
-        export_gnutls_datum( dst, size, &y, 1 );
-        reverse_bytes( dst, size );
-        dst += size;
-
-        memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) );
-    }
-
-done:
-    free( p.data ); free( q.data ); free( g.data ); free( y.data );
-    return status;
-}
-
-static gnutls_privkey_t create_privkey( gnutls_pk_algorithm_t pk_alg, unsigned int bitlen,
-                                        const gnutls_keygen_data_st *data, unsigned int data_size )
-{
-    gnutls_privkey_t privkey;
-    int ret;
-
-    if ((ret = pgnutls_privkey_init( &privkey )))
-    {
-        pgnutls_perror( ret );
-        return NULL;
-    }
-
-    if ((ret = pgnutls_privkey_generate2( privkey, pk_alg, bitlen, 0, data, data_size )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_privkey_deinit( privkey );
-        return NULL;
-    }
-
-    return privkey;
-}
-
-static gnutls_pubkey_t create_pubkey_from_privkey( gnutls_privkey_t privkey )
-{
-    gnutls_pubkey_t pubkey;
-    int ret;
-
-    if ((ret = pgnutls_pubkey_init( &pubkey )))
-    {
-        pgnutls_perror( ret );
-        return NULL;
-    }
-
-    if ((ret = pgnutls_pubkey_import_privkey( pubkey, privkey, 0, 0 )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( pubkey );
-        return NULL;
-    }
-
-    return pubkey;
-}
-
-static gnutls_dh_params_t get_dh_params( gnutls_privkey_t privkey )
-{
-    gnutls_dh_params_t params;
-    gnutls_datum_t x;
-    int ret;
-
-    if ((ret = pgnutls_dh_params_init( &params )))
-    {
-        pgnutls_perror( ret );
-        return NULL;
-    }
-
-    if ((ret = pgnutls_privkey_export_dh_raw( privkey, params, NULL, &x, 0 )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        return NULL;
-    }
-
-    free( x.data );
-    return params;
-}
-
-static NTSTATUS key_asymmetric_generate( void *args )
-{
-    struct key *key = args;
-    gnutls_pk_algorithm_t pk_alg;
-    gnutls_privkey_t privkey;
-    gnutls_pubkey_t pubkey;
-    unsigned int bitlen;
-
-    if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
-    if (key_data(key)->a.privkey) return STATUS_INVALID_HANDLE;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-        pk_alg = GNUTLS_PK_RSA;
-        bitlen = key->u.a.bitlen;
-        break;
-
-    case ALG_ID_DH:
-        pk_alg = GNUTLS_PK_DH;
-        bitlen = key->u.a.bitlen;
-        break;
-
-    case ALG_ID_DSA:
-        pk_alg = GNUTLS_PK_DSA;
-        bitlen = key->u.a.bitlen;
-        break;
-
-    case ALG_ID_ECDH:
-    case ALG_ID_ECDSA:
-        pk_alg = GNUTLS_PK_ECC;
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_25519:
-            assert( key->alg_id == ALG_ID_ECDH );
-            pk_alg = GNUTLS_PK_ECDH_X25519;
-            bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_X25519 );
-            break;
-        case ECC_CURVE_P256R1:
-            bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP256R1 );
-            break;
-        case ECC_CURVE_P384R1:
-            bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 );
-            break;
-        case ECC_CURVE_P521R1:
-            bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP521R1 );
-            break;
-        default:
-            FIXME( "unsupported ECDH/ECDSA curve %u\n", key->u.a.curve_id );
-            return STATUS_INVALID_PARAMETER;
-        }
-        break;
-
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDSA_P256:
-        pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */
-        bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP256R1 );
-        break;
-
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDSA_P384:
-        pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */
-        bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 );
-        break;
-
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA_P521:
-        pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */
-        bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP521R1 );
-        break;
-
-    default:
-        FIXME( "algorithm %u not supported\n", key->alg_id );
-        return STATUS_NOT_SUPPORTED;
-    }
-
-    if (key->alg_id == ALG_ID_DH && key_data(key)->a.dh_params)
-    {
-        gnutls_keygen_data_st data;
-
-        data.type = GNUTLS_KEYGEN_DH;
-        data.data = (unsigned char *)key_data(key)->a.dh_params;
-        data.size = 0;
-        if (!(privkey = create_privkey( pk_alg, bitlen, &data, 1 ))) return STATUS_INTERNAL_ERROR;
-    }
-    else if (!(privkey = create_privkey( pk_alg, bitlen, NULL, 0 ))) return STATUS_INTERNAL_ERROR;
-
-    if (key->alg_id == ALG_ID_DH && !key_data(key)->a.dh_params &&
-        !(key_data(key)->a.dh_params = get_dh_params( privkey )))
-    {
-        pgnutls_privkey_deinit( privkey );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (!(pubkey = create_pubkey_from_privkey( privkey )))
-    {
-        pgnutls_privkey_deinit( privkey );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    key_data(key)->a.privkey = privkey;
-    key_data(key)->a.pubkey  = pubkey;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_ECCKEY_BLOB *ecc_blob;
-    gnutls_ecc_curve_t curve;
-    gnutls_datum_t x, y, d;
-    DWORD magic, size;
-    UCHAR *dst;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDH:
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_25519:
-            magic = BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC;
-            size = 32;
-            break;
-        case ECC_CURVE_P256R1:
-            magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC;
-            size = 32;
-            break;
-        case ECC_CURVE_P384R1:
-            magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC;
-            size = 48;
-            break;
-        case ECC_CURVE_P521R1:
-            magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC;
-            size = 66;
-            break;
-        default:
-            FIXME( "unsupported curve %u\n", key->u.a.curve_id );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        break;
-
-    case ALG_ID_ECDH_P256:
-        magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC;
-        size = 32;
-        break;
-
-    case ALG_ID_ECDH_P384:
-        magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC;
-        size = 48;
-        break;
-
-    case ALG_ID_ECDH_P521:
-        magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC;
-        size = 66;
-        break;
-
-    case ALG_ID_ECDSA:
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_P256R1:
-            magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
-            size = 32;
-            break;
-        case ECC_CURVE_P384R1:
-            magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC;
-            size = 48;
-            break;
-        case ECC_CURVE_P521R1:
-            magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
-            size = 66;
-            break;
-        default:
-            FIXME( "unsupported curve %u\n", key->u.a.curve_id );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        break;
-
-    case ALG_ID_ECDSA_P256:
-        magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
-        size = 32;
-        break;
-
-    case ALG_ID_ECDSA_P384:
-        magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC;
-        size = 48;
-        break;
-
-    case ALG_ID_ECDSA_P521:
-        magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
-        size = 66;
-        break;
-
-    default:
-        FIXME( "algorithm %u does not yet support exporting ecc blob\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER;
-
-    if ((ret = pgnutls_privkey_export_ecc_raw( key_data(key)->a.privkey, &curve, &x, &y, &d )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (curve != GNUTLS_ECC_CURVE_X25519 && curve != GNUTLS_ECC_CURVE_SECP256R1 &&
-        curve != GNUTLS_ECC_CURVE_SECP384R1 && curve != GNUTLS_ECC_CURVE_SECP521R1)
-    {
-        FIXME( "curve %u not supported\n", curve );
-        free( x.data ); free( y.data ); free( d.data );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    *ret_len = sizeof(*ecc_blob) + EXPORT_SIZE( x, size, 1 ) + EXPORT_SIZE( y, size, 1 ) + EXPORT_SIZE( d, size, 1 );
-    if (len >= *ret_len && buf)
-    {
-        ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
-        ecc_blob->dwMagic = magic;
-        ecc_blob->cbKey   = size;
-
-        dst = (UCHAR *)(ecc_blob + 1);
-        dst += export_gnutls_datum( dst, size, &x, 1 );
-        dst += export_gnutls_datum( dst, size, &y, 1 );
-        export_gnutls_datum( dst, size, &d, 1 );
-    }
-
-    free( x.data ); free( y.data ); free( d.data );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_ECCKEY_BLOB *ecc_blob;
-    gnutls_ecc_curve_t curve;
-    gnutls_privkey_t handle;
-    gnutls_datum_t x, y, k;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDH:
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_25519:  curve = GNUTLS_ECC_CURVE_X25519; break;
-        case ECC_CURVE_P256R1: curve = GNUTLS_ECC_CURVE_SECP256R1; break;
-        case ECC_CURVE_P384R1: curve = GNUTLS_ECC_CURVE_SECP384R1; break;
-        case ECC_CURVE_P521R1: curve = GNUTLS_ECC_CURVE_SECP521R1; break;
-        default:
-            FIXME( "curve %u not supported\n", key->u.a.curve_id );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        break;
-
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDSA_P256:
-        curve = GNUTLS_ECC_CURVE_SECP256R1;
-        break;
-
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDSA_P384:
-        curve = GNUTLS_ECC_CURVE_SECP384R1;
-        break;
-
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA_P521:
-        curve = GNUTLS_ECC_CURVE_SECP521R1;
-        break;
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((ret = pgnutls_privkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
-    x.data = (unsigned char *)(ecc_blob + 1);
-    x.size = ecc_blob->cbKey;
-    y.data = x.data + ecc_blob->cbKey;
-    y.size = ecc_blob->cbKey;
-    k.data = y.data + ecc_blob->cbKey;
-    k.size = ecc_blob->cbKey;
-
-    if ((ret = pgnutls_privkey_import_ecc_raw( handle, curve, &x, &y, &k )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey );
-    key_data(key)->a.privkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_rsa( struct key *key, ULONG flags, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_RSAKEY_BLOB *rsa_blob;
-    gnutls_datum_t m, e, d, p, q, u, e1, e2;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    BOOL full = (flags & KEY_EXPORT_FLAG_RSA_FULL);
-    UCHAR *dst;
-    int ret;
-
-    if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER;
-
-    if ((ret = pgnutls_privkey_export_rsa_raw( key_data(key)->a.privkey, &m, &e, &d, &p, &q, &u, &e1, &e2 )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *ret_len = sizeof(*rsa_blob) + EXPORT_SIZE( e, size, 0 ) + EXPORT_SIZE( m, size, 1 ) +
-               EXPORT_SIZE( p, size / 2, 1 ) + EXPORT_SIZE( q, size / 2, 1 );
-
-    if (full) *ret_len += EXPORT_SIZE( e1, size / 2, 1 ) + EXPORT_SIZE( e2, size / 2, 1 ) +
-                          EXPORT_SIZE( u, size / 2, 1 ) + EXPORT_SIZE( d, size, 1 );
-
-    if (len >= *ret_len && buf)
-    {
-        rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
-        rsa_blob->Magic     = full ? BCRYPT_RSAFULLPRIVATE_MAGIC : BCRYPT_RSAPRIVATE_MAGIC;
-        rsa_blob->BitLength = key->u.a.bitlen;
-
-        dst = (UCHAR *)(rsa_blob + 1);
-        rsa_blob->cbPublicExp = export_gnutls_datum( dst, size, &e, 0 );
-
-        dst += rsa_blob->cbPublicExp;
-        rsa_blob->cbModulus = export_gnutls_datum( dst, size, &m, 1 );
-
-        dst += rsa_blob->cbModulus;
-        rsa_blob->cbPrime1 = export_gnutls_datum( dst, size / 2, &p, 1 );
-
-        dst += rsa_blob->cbPrime1;
-        rsa_blob->cbPrime2 = export_gnutls_datum( dst, size / 2, &q, 1 );
-
-        if (full)
-        {
-            dst += rsa_blob->cbPrime2;
-            export_gnutls_datum( dst, size / 2, &e1, 1 );
-
-            dst += rsa_blob->cbPrime1;
-            export_gnutls_datum( dst, size / 2, &e2, 1 );
-
-            dst += rsa_blob->cbPrime2;
-            export_gnutls_datum( dst, size / 2, &u, 1 );
-
-            dst += rsa_blob->cbPrime1;
-            export_gnutls_datum( dst, size, &d, 1 );
-        }
-    }
-
-    free( m.data ); free( e.data ); free( d.data ); free( p.data ); free( q.data ); free( u.data );
-    free( e1.data ); free( e2.data );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_rsa( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
-    gnutls_datum_t m, e, p, q;
-    gnutls_privkey_t handle;
-    int ret;
-
-    if ((ret = pgnutls_privkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    e.data = (unsigned char *)(rsa_blob + 1);
-    e.size = rsa_blob->cbPublicExp;
-    m.data = e.data + e.size;
-    m.size = rsa_blob->cbModulus;
-    p.data = m.data + m.size;
-    p.size = rsa_blob->cbPrime1;
-    q.data = p.data + p.size;
-    q.size = rsa_blob->cbPrime2;
-
-    if ((ret = pgnutls_privkey_import_rsa_raw( handle, &m, &e, NULL, &p, &q, NULL, NULL, NULL )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey );
-    key_data(key)->a.privkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_dsa_capi( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BLOBHEADER *hdr;
-    DSSPUBKEY *pubkey;
-    gnutls_datum_t p, q, g, y, x;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    UCHAR *dst;
-    int ret;
-
-    if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER;
-
-    if ((ret = pgnutls_privkey_export_dsa_raw( key_data(key)->a.privkey, &p, &q, &g, &y, &x )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (q.size > 21 || x.size > 21)
-    {
-        ERR( "can't export key in this format\n" );
-        free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data );
-        return STATUS_NOT_SUPPORTED;
-    }
-
-    *ret_len = sizeof(*hdr) + sizeof(*pubkey) + sizeof(key->u.a.dss_seed) +
-               EXPORT_SIZE( p, size, 1 ) + 20 + EXPORT_SIZE( g, size, 1 ) + 20;
-    if (len >= *ret_len && buf)
-    {
-        hdr = (BLOBHEADER *)buf;
-        hdr->bType    = PRIVATEKEYBLOB;
-        hdr->bVersion = 2;
-        hdr->reserved = 0;
-        hdr->aiKeyAlg = CALG_DSS_SIGN;
-
-        pubkey = (DSSPUBKEY *)(hdr + 1);
-        pubkey->magic  = MAGIC_DSS2;
-        pubkey->bitlen = key->u.a.bitlen;
-
-        dst = (UCHAR *)(pubkey + 1);
-        export_gnutls_datum( dst, size, &p, 1 );
-        reverse_bytes( dst, size );
-        dst += size;
-
-        export_gnutls_datum( dst, 20, &q, 1 );
-        reverse_bytes( dst, 20 );
-        dst += 20;
-
-        export_gnutls_datum( dst, size, &g, 1 );
-        reverse_bytes( dst, size );
-        dst += size;
-
-        export_gnutls_datum( dst, 20, &x, 1 );
-        reverse_bytes( dst, 20 );
-        dst += 20;
-
-        memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) );
-    }
-
-    free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_dsa_capi( struct key *key, UCHAR *buf, ULONG len )
-{
-    BLOBHEADER *hdr = (BLOBHEADER *)buf;
-    DSSPUBKEY *pubkey;
-    gnutls_privkey_t handle;
-    gnutls_datum_t p, q, g, x;
-    unsigned char *data, p_data[128], q_data[20], g_data[128], x_data[20];
-    int i, ret, size;
-
-    if ((ret = pgnutls_privkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    pubkey = (DSSPUBKEY *)(hdr + 1);
-    if ((size = len_from_bitlen( pubkey->bitlen )) > sizeof(p_data))
-    {
-        FIXME( "size %u not supported\n", size );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_NOT_SUPPORTED;
-    }
-    data = (unsigned char *)(pubkey + 1);
-
-    p.data = p_data;
-    p.size = size;
-    for (i = 0; i < p.size; i++) p.data[i] = data[p.size - i - 1];
-    data += p.size;
-
-    q.data = q_data;
-    q.size = sizeof(q_data);
-    for (i = 0; i < q.size; i++) q.data[i] = data[q.size - i - 1];
-    data += q.size;
-
-    g.data = g_data;
-    g.size = size;
-    for (i = 0; i < g.size; i++) g.data[i] = data[g.size - i - 1];
-    data += g.size;
-
-    x.data = x_data;
-    x.size = sizeof(x_data);
-    for (i = 0; i < x.size; i++) x.data[i] = data[x.size - i - 1];
-    data += x.size;
-
-    if ((ret = pgnutls_privkey_import_dsa_raw( handle, &p, &q, &g, NULL, &x )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    memcpy( &key->u.a.dss_seed, data, sizeof(key->u.a.dss_seed) );
-
-    if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey );
-    key_data(key)->a.privkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_ecc_public( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_ECCKEY_BLOB *ecc_blob;
-    gnutls_ecc_curve_t curve;
-    gnutls_datum_t x, y;
-    gnutls_pubkey_t handle;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDH:
-        switch (key->u.a.curve_id)
-        {
-        case ECC_CURVE_25519:  curve = GNUTLS_ECC_CURVE_X25519; break;
-        case ECC_CURVE_P256R1: curve = GNUTLS_ECC_CURVE_SECP256R1; break;
-        case ECC_CURVE_P384R1: curve = GNUTLS_ECC_CURVE_SECP384R1; break;
-        case ECC_CURVE_P521R1: curve = GNUTLS_ECC_CURVE_SECP521R1; break;
-        default:
-            FIXME( "curve %u not supported\n", key->u.a.curve_id );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-        break;
-
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDSA_P256:
-        curve = GNUTLS_ECC_CURVE_SECP256R1; break;
-
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDSA_P384:
-        curve = GNUTLS_ECC_CURVE_SECP384R1; break;
-
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA_P521:
-        curve = GNUTLS_ECC_CURVE_SECP521R1; break;
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((ret = pgnutls_pubkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
-    x.data = buf + sizeof(*ecc_blob);
-    x.size = ecc_blob->cbKey;
-    y.data = buf + sizeof(*ecc_blob) + ecc_blob->cbKey;
-    y.size = ecc_blob->cbKey;
-
-    if ((ret = pgnutls_pubkey_import_ecc_raw( handle, curve, &x, &y )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-    key_data(key)->a.pubkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_rsa_public( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_RSAKEY_BLOB *rsa_blob;
-    gnutls_pubkey_t handle;
-    gnutls_datum_t m, e;
-    int ret;
-
-    if ((ret = pgnutls_pubkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
-    e.data = buf + sizeof(*rsa_blob);
-    e.size = rsa_blob->cbPublicExp;
-    m.data = buf + sizeof(*rsa_blob) + rsa_blob->cbPublicExp;
-    m.size = rsa_blob->cbModulus;
-
-    if ((ret = pgnutls_pubkey_import_rsa_raw( handle, &m, &e )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-    key_data(key)->a.pubkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_dsa_public( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_DSA_KEY_BLOB *dsa_blob;
-    gnutls_datum_t p, q, g, y;
-    gnutls_pubkey_t handle;
-    int ret;
-
-    if ((ret = pgnutls_pubkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf;
-    p.data = buf + sizeof(*dsa_blob);
-    p.size = dsa_blob->cbKey;
-    q.data = dsa_blob->q;
-    q.size = sizeof(dsa_blob->q);
-    g.data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey;
-    g.size = dsa_blob->cbKey;
-    y.data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey * 2;
-    y.size = dsa_blob->cbKey;
-
-    if ((ret = pgnutls_pubkey_import_dsa_raw( handle, &p, &q, &g, &y )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-    key_data(key)->a.pubkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_dsa_capi_public( struct key *key, UCHAR *buf, ULONG len )
-{
-    BLOBHEADER *hdr;
-    DSSPUBKEY *pubkey;
-    gnutls_datum_t p, q, g, y;
-    gnutls_pubkey_t handle;
-    unsigned char *data, p_data[128], q_data[20], g_data[128], y_data[128];
-    int i, ret, size;
-
-    if ((ret = pgnutls_pubkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    hdr = (BLOBHEADER *)buf;
-    pubkey = (DSSPUBKEY *)(hdr + 1);
-    size = len_from_bitlen( pubkey->bitlen );
-    data = (unsigned char *)(pubkey + 1);
-
-    p.data = p_data;
-    p.size = size;
-    for (i = 0; i < p.size; i++) p.data[i] = data[p.size - i - 1];
-    data += p.size;
-
-    q.data = q_data;
-    q.size = sizeof(q_data);
-    for (i = 0; i < q.size; i++) q.data[i] = data[q.size - i - 1];
-    data += q.size;
-
-    g.data = g_data;
-    g.size = size;
-    for (i = 0; i < g.size; i++) g.data[i] = data[g.size - i - 1];
-    data += g.size;
-
-    y.data = y_data;
-    y.size = sizeof(y_data);
-    for (i = 0; i < y.size; i++) y.data[i] = data[y.size - i - 1];
-
-    if ((ret = pgnutls_pubkey_import_dsa_raw( handle, &p, &q, &g, &y )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-    key_data(key)->a.pubkey = handle;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_dh_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    gnutls_dh_params_t params;
-    gnutls_datum_t p, g, y;
-    UCHAR *dst;
-    int ret = GNUTLS_E_INVALID_REQUEST;
-
-    if ((ret = pgnutls_dh_params_init( &params )) < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_pubkey_export_dh_raw( key_data(key)->a.pubkey, params, &y, 0 )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL )) < 0)
-    {
-        pgnutls_perror( ret );
-        free( y.data );
-        pgnutls_dh_params_deinit( params );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *ret_len = sizeof(*dh_blob) + EXPORT_SIZE(p, size, 1) + EXPORT_SIZE(g, size, 1) + EXPORT_SIZE(y, size, 1);
-    if (len >= *ret_len && buf)
-    {
-        dst = (UCHAR *)(dh_blob + 1);
-        dst += export_gnutls_datum( dst, size, &p, 1 );
-        dst += export_gnutls_datum( dst, size, &g, 1 );
-        dst += export_gnutls_datum( dst, size, &y, 1 );
-
-        dh_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
-        dh_blob->cbKey   = size;
-    }
-
-    free( p.data ); free( g.data ); free( y.data );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_dh( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
-    gnutls_datum_t p, g, y, x;
-    gnutls_dh_params_t params;
-    ULONG size = len_from_bitlen( key->u.a.bitlen );
-    UCHAR *dst;
-    int ret;
-
-    if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER;
-
-    if ((ret = pgnutls_dh_params_init( &params )) < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, &y, &x, 0 )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL )) < 0)
-    {
-        pgnutls_perror( ret );
-        free( y.data ); free( x.data );
-        pgnutls_dh_params_deinit( params );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *ret_len = sizeof(*dh_blob) + EXPORT_SIZE(p, size, 1) + EXPORT_SIZE(g, size, 1) +
-                                  EXPORT_SIZE(y, size, 1) + EXPORT_SIZE(x, size, 1);
-    if (len >= *ret_len && buf)
-    {
-        dst = (UCHAR *)(dh_blob + 1);
-        dst += export_gnutls_datum( dst, size, &p, 1 );
-        dst += export_gnutls_datum( dst, size, &g, 1 );
-        dst += export_gnutls_datum( dst, size, &y, 1 );
-        dst += export_gnutls_datum( dst, size, &x, 1 );
-
-        dh_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC;
-        dh_blob->cbKey   = size;
-    }
-
-    free( p.data ); free( g.data ); free( y.data ); free( x.data );
-    pgnutls_dh_params_deinit( params );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_export_dh_params( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
-{
-    BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)buf;
-    unsigned int size = sizeof(*hdr) + len_from_bitlen( key->u.a.bitlen ) * 2;
-    gnutls_datum_t p, g;
-    NTSTATUS status = STATUS_SUCCESS;
-    UCHAR *dst;
-    int ret;
-
-    if (!key_data(key)->a.dh_params) return STATUS_INVALID_PARAMETER;
-
-    if ((ret = pgnutls_dh_params_export_raw( key_data(key)->a.dh_params, &p, &g, NULL )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *ret_len = size;
-    if (len < size) status = STATUS_BUFFER_TOO_SMALL;
-    else if (buf)
-    {
-        hdr->cbLength    = size;
-        hdr->dwMagic     = BCRYPT_DH_PARAMETERS_MAGIC;
-        hdr->cbKeyLength = len_from_bitlen( key->u.a.bitlen );
-
-        dst = (UCHAR *)(hdr + 1);
-        dst += export_gnutls_datum( dst, hdr->cbKeyLength, &p, 1 );
-        dst += export_gnutls_datum( dst, hdr->cbKeyLength, &g, 1 );
-    }
-
-    free( p.data ); free( g.data );
-    return status;
-}
-
-static NTSTATUS key_asymmetric_export( void *args )
-{
-    const struct key_asymmetric_export_params *params = args;
-    struct key *key = params->key;
-    unsigned flags = params->flags;
-
-    if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDH:
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA:
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-        if (flags & KEY_EXPORT_FLAG_PUBLIC)
-            return key_export_ecc_public( key, params->buf, params->len, params->ret_len );
-        return key_export_ecc( key, params->buf, params->len, params->ret_len );
-
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-        if (flags & KEY_EXPORT_FLAG_PUBLIC)
-            return key_export_rsa_public( key, params->buf, params->len, params->ret_len );
-        return key_export_rsa( key, flags, params->buf, params->len, params->ret_len );
-
-    case ALG_ID_DSA:
-        if (flags & KEY_EXPORT_FLAG_PUBLIC)
-        {
-            if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
-                return key_export_dsa_capi_public( key, params->buf, params->len, params->ret_len );
-            return key_export_dsa_public( key, params->buf, params->len, params->ret_len );
-        }
-        if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
-            return key_export_dsa_capi( key, params->buf, params->len, params->ret_len );
-        return STATUS_NOT_IMPLEMENTED;
-
-    case ALG_ID_DH:
-        if (flags & KEY_EXPORT_FLAG_DH_PARAMETERS)
-            return key_export_dh_params( key, params->buf, params->len, params->ret_len );
-        if (flags & KEY_EXPORT_FLAG_PUBLIC)
-            return key_export_dh_public( key, params->buf, params->len, params->ret_len );
-        return key_export_dh( key, params->buf, params->len, params->ret_len );
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-}
-
-static NTSTATUS key_import_dh_public( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_DH_KEY_BLOB *dh_blob;
-    gnutls_dh_params_t params;
-    gnutls_datum_t p, g, y;
-    gnutls_pubkey_t handle;
-    int ret;
-
-    if ((ret = pgnutls_pubkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_dh_params_init( &params )) < 0)
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
-    p.data = buf + sizeof(*dh_blob);
-    p.size = dh_blob->cbKey;
-    g.data = buf + sizeof(*dh_blob) + dh_blob->cbKey;
-    g.size = dh_blob->cbKey;
-    y.data = buf + sizeof(*dh_blob) + dh_blob->cbKey * 2;
-    y.size = dh_blob->cbKey;
-
-    if ((ret = pgnutls_dh_params_import_raw( params, &p, &g )) < 0)
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_pubkey_import_dh_raw( handle, params, &y )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        pgnutls_pubkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-    key_data(key)->a.pubkey = handle;
-
-    if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params );
-    key_data(key)->a.dh_params = params;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_dh( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_DH_KEY_BLOB *dh_blob;
-    gnutls_dh_params_t params;
-    gnutls_datum_t p, g, y, x;
-    gnutls_privkey_t handle;
-    int ret;
-
-    if ((ret = pgnutls_privkey_init( &handle )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_dh_params_init( &params )) < 0)
-    {
-        pgnutls_perror( ret );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
-    p.data = buf + sizeof(*dh_blob);
-    p.size = dh_blob->cbKey;
-    g.data = buf + sizeof(*dh_blob) + dh_blob->cbKey;
-    g.size = dh_blob->cbKey;
-    y.data = buf + sizeof(*dh_blob) + dh_blob->cbKey * 2;
-    y.size = dh_blob->cbKey;
-    x.data = buf + sizeof(*dh_blob) + dh_blob->cbKey * 3;
-    x.size = dh_blob->cbKey;
-
-    if ((ret = pgnutls_dh_params_import_raw( params, &p, &g )) < 0)
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if ((ret = pgnutls_privkey_import_dh_raw( handle, params, &y, &x )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        pgnutls_privkey_deinit( handle );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey );
-    key_data(key)->a.privkey = handle;
-
-    if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params );
-    key_data(key)->a.dh_params = params;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_import_dh_params( struct key *key, UCHAR *buf, ULONG len )
-{
-    BCRYPT_DH_PARAMETER_HEADER *dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buf;
-    gnutls_dh_params_t params;
-    gnutls_datum_t p, g;
-    int ret;
-
-    if ((ret = pgnutls_dh_params_init( &params )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    p.data = (unsigned char *)(dh_header + 1);
-    p.size = dh_header->cbKeyLength;
-    g.data = p.data + dh_header->cbKeyLength;
-    g.size = dh_header->cbKeyLength;
-
-    if ((ret = pgnutls_dh_params_import_raw( params, &p, &g )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_dh_params_deinit( params );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params );
-    key_data(key)->a.dh_params = params;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_import( void *args )
-{
-    const struct key_asymmetric_import_params *params = args;
-    struct key *key = params->key;
-    unsigned flags = params->flags;
-    gnutls_pubkey_t pubkey;
-    NTSTATUS ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDH:
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA:
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-        if (flags & KEY_IMPORT_FLAG_PUBLIC)
-            return key_import_ecc_public( key, params->buf, params->len );
-        ret = key_import_ecc( key, params->buf, params->len );
-        break;
-
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-        if (flags & KEY_IMPORT_FLAG_PUBLIC)
-            return key_import_rsa_public( key, params->buf, params->len );
-        ret = key_import_rsa( key, params->buf, params->len );
-        break;
-
-    case ALG_ID_DSA:
-        if (flags & KEY_IMPORT_FLAG_PUBLIC)
-        {
-            if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
-                return key_import_dsa_capi_public( key, params->buf, params->len );
-            return key_import_dsa_public( key, params->buf, params->len );
-        }
-        if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
-        {
-            ret = key_import_dsa_capi( key, params->buf, params->len );
-            break;
-        }
-        FIXME( "DSA private key not supported\n" );
-        return STATUS_NOT_IMPLEMENTED;
-
-    case ALG_ID_DH:
-        if (flags & KEY_IMPORT_FLAG_DH_PARAMETERS)
-            return key_import_dh_params( key, params->buf, params->len );
-        if (flags & KEY_IMPORT_FLAG_PUBLIC)
-            return key_import_dh_public( key, params->buf, params->len );
-        ret = key_import_dh( key, params->buf, params->len );
-        break;
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (ret) return ret;
-
-    if ((ret = pgnutls_pubkey_init( &pubkey )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (pgnutls_pubkey_import_privkey( pubkey, key_data(params->key)->a.privkey, 0, 0 ))
-    {
-        /* Imported private key may be legitimately missing public key, so ignore the failure here. */
-        pgnutls_pubkey_deinit( pubkey );
-    }
-    else
-    {
-        if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-        key_data(key)->a.pubkey = pubkey;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS prepare_gnutls_signature_dsa( struct key *key, UCHAR *signature, ULONG signature_len,
-                                              gnutls_datum_t *gnutls_signature )
-{
-    struct buffer buffer;
-    DWORD r_len = signature_len / 2;
-    DWORD s_len = r_len;
-    BYTE *r = signature;
-    BYTE *s = signature + r_len;
-
-    buffer_init( &buffer );
-    buffer_append_asn1_r_s( &buffer, r, r_len, s, s_len );
-    if (buffer.error)
-    {
-        buffer_free( &buffer );
-        return STATUS_NO_MEMORY;
-    }
-
-    gnutls_signature->data = buffer.buffer;
-    gnutls_signature->size = buffer.pos;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS prepare_gnutls_signature_rsa( struct key *key, UCHAR *signature, ULONG signature_len,
-                                              gnutls_datum_t *gnutls_signature )
-{
-    gnutls_signature->data = signature;
-    gnutls_signature->size = signature_len;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULONG signature_len,
-                                          gnutls_datum_t *gnutls_signature )
-{
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-    case ALG_ID_DSA:
-        return prepare_gnutls_signature_dsa( key, signature, signature_len, gnutls_signature );
-
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-        return prepare_gnutls_signature_rsa( key, signature, signature_len, gnutls_signature );
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-}
-
-static gnutls_digest_algorithm_t get_digest_from_id( const WCHAR *alg_id )
-{
-    if (!wcscmp( alg_id, BCRYPT_SHA1_ALGORITHM ))   return GNUTLS_DIG_SHA1;
-    if (!wcscmp( alg_id, BCRYPT_SHA256_ALGORITHM )) return GNUTLS_DIG_SHA256;
-    if (!wcscmp( alg_id, BCRYPT_SHA384_ALGORITHM )) return GNUTLS_DIG_SHA384;
-    if (!wcscmp( alg_id, BCRYPT_SHA512_ALGORITHM )) return GNUTLS_DIG_SHA512;
-    if (!wcscmp( alg_id, BCRYPT_MD2_ALGORITHM ))    return GNUTLS_DIG_MD2;
-    if (!wcscmp( alg_id, BCRYPT_MD5_ALGORITHM ))    return GNUTLS_DIG_MD5;
-    return GNUTLS_DIG_UNKNOWN;
-}
-
-static NTSTATUS pubkey_set_rsa_pss_params( gnutls_pubkey_t key, gnutls_digest_algorithm_t dig, unsigned int salt_size )
-{
-    gnutls_x509_spki_t spki;
-    int ret;
-
-    if ((ret = pgnutls_x509_spki_init( &spki )) < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    pgnutls_x509_spki_set_rsa_pss_params( spki, dig, salt_size );
-    ret = pgnutls_pubkey_set_spki( key, spki, 0 );
-    pgnutls_x509_spki_deinit( spki );
-    if (ret < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_verify( void *args )
-{
-#ifdef GNUTLS_VERIFY_ALLOW_BROKEN
-    unsigned int verify_flags = GNUTLS_VERIFY_ALLOW_BROKEN;
-#else
-    unsigned int verify_flags = 0;
-#endif
-    const struct key_asymmetric_verify_params *params = args;
-    struct key *key = params->key;
-    unsigned flags = params->flags;
-    gnutls_digest_algorithm_t hash_alg;
-    gnutls_sign_algorithm_t sign_alg;
-    gnutls_datum_t gnutls_hash, gnutls_signature;
-    gnutls_pk_algorithm_t pk_alg;
-    NTSTATUS status;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-    {
-        if (flags) FIXME( "flags %#x not supported\n", flags );
-
-        /* only the hash size must match, not the actual hash function */
-        switch (params->hash_len)
-        {
-        case 20: hash_alg = GNUTLS_DIG_SHA1; break;
-        case 32: hash_alg = GNUTLS_DIG_SHA256; break;
-        case 48: hash_alg = GNUTLS_DIG_SHA384; break;
-        case 64: hash_alg = GNUTLS_DIG_SHA512; break;
-
-        default:
-            FIXME( "hash size %u not yet supported\n", params->hash_len );
-            return STATUS_INVALID_SIGNATURE;
-        }
-        pk_alg = GNUTLS_PK_ECC;
-        break;
-    }
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-    {
-        if (flags & BCRYPT_PAD_PKCS1)
-        {
-            BCRYPT_PKCS1_PADDING_INFO *info = params->padding;
-
-            if (!info) return STATUS_INVALID_PARAMETER;
-            if (!info->pszAlgId)
-            {
-                hash_alg = GNUTLS_DIG_UNKNOWN;
-                verify_flags |= GNUTLS_VERIFY_USE_TLS1_RSA;
-            }
-            else if ((hash_alg = get_digest_from_id(info->pszAlgId)) == GNUTLS_DIG_UNKNOWN)
-            {
-                FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) );
-                return STATUS_NOT_SUPPORTED;
-            }
-            pk_alg = GNUTLS_PK_RSA;
-        }
-        else if (flags & BCRYPT_PAD_PSS)
-        {
-            BCRYPT_PSS_PADDING_INFO *info = params->padding;
-
-            if (!info) return STATUS_INVALID_PARAMETER;
-            if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE;
-            if ((hash_alg = get_digest_from_id(info->pszAlgId)) == GNUTLS_DIG_UNKNOWN)
-            {
-                FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) );
-                return STATUS_NOT_SUPPORTED;
-            }
-            if ((status = pubkey_set_rsa_pss_params( key_data(key)->a.pubkey, hash_alg, info->cbSalt ))) return status;
-            pk_alg = GNUTLS_PK_RSA_PSS;
-        }
-        else return STATUS_INVALID_PARAMETER;
-        break;
-    }
-    case ALG_ID_DSA:
-    {
-        if (flags) FIXME( "flags %#x not supported\n", flags );
-        if (params->hash_len != 20)
-        {
-            FIXME( "hash size %u not supported\n", params->hash_len );
-            return STATUS_INVALID_PARAMETER;
-        }
-        hash_alg = GNUTLS_DIG_SHA1;
-        pk_alg = GNUTLS_PK_DSA;
-        break;
-    }
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((sign_alg = pgnutls_pk_to_sign( pk_alg, hash_alg )) == GNUTLS_SIGN_UNKNOWN)
-    {
-        FIXME("GnuTLS does not support algorithm %u with hash len %u\n", key->alg_id, params->hash_len );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((status = prepare_gnutls_signature( key, params->signature, params->signature_len, &gnutls_signature )))
-        return status;
-
-    gnutls_hash.data = params->hash;
-    gnutls_hash.size = params->hash_len;
-
-    ret = pgnutls_pubkey_verify_hash2( key_data(key)->a.pubkey, sign_alg, verify_flags, &gnutls_hash, &gnutls_signature );
-    if (gnutls_signature.data != params->signature) free( gnutls_signature.data );
-    return (ret < 0) ? STATUS_INVALID_SIGNATURE : STATUS_SUCCESS;
-}
-
-static unsigned int get_signature_length( enum alg_id id )
-{
-    switch (id)
-    {
-    case ALG_ID_ECDSA_P256: return 64;
-    case ALG_ID_ECDSA_P384: return 96;
-    case ALG_ID_ECDSA_P521: return 132;
-    case ALG_ID_DSA:        return 40;
-    default:
-        FIXME( "unhandled algorithm %u\n", id );
-        return 0;
-    }
-}
-
-static NTSTATUS format_gnutls_signature( enum alg_id type, gnutls_datum_t signature,
-                                         UCHAR *output, ULONG output_len, ULONG *ret_len )
-{
-    switch (type)
-    {
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-    {
-        *ret_len = signature.size;
-        if (output_len < signature.size) return STATUS_BUFFER_TOO_SMALL;
-        if (output) memcpy( output, signature.data, signature.size );
-        return STATUS_SUCCESS;
-    }
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-    case ALG_ID_DSA:
-    {
-        int err;
-        unsigned int sig_len = get_signature_length( type );
-        gnutls_datum_t r, s; /* format as r||s */
-
-        if ((err = pgnutls_decode_rs_value( &signature, &r, &s )))
-        {
-            pgnutls_perror( err );
-            return STATUS_INTERNAL_ERROR;
-        }
-
-        *ret_len = sig_len;
-        if (output_len < sig_len) return STATUS_BUFFER_TOO_SMALL;
-
-        if (r.size > sig_len / 2 + 1 || s.size > sig_len / 2 + 1)
-        {
-            ERR( "we didn't get a correct signature\n" );
-            return STATUS_INTERNAL_ERROR;
-        }
-
-        if (output)
-        {
-            export_gnutls_datum( output, sig_len / 2, &r, 1 );
-            export_gnutls_datum( output + sig_len / 2, sig_len / 2, &s, 1 );
-        }
-
-        free( r.data ); free( s.data );
-        return STATUS_SUCCESS;
-    }
-    default:
-        return STATUS_INTERNAL_ERROR;
-    }
-}
-
-static NTSTATUS privkey_set_rsa_pss_params( gnutls_privkey_t key, gnutls_digest_algorithm_t dig, unsigned int salt_size )
-{
-    gnutls_x509_spki_t spki;
-    int ret;
-
-    if ((ret = pgnutls_x509_spki_init( &spki )) < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    pgnutls_x509_spki_set_rsa_pss_params( spki, dig, salt_size );
-    ret = pgnutls_privkey_set_spki( key, spki, 0 );
-    pgnutls_x509_spki_deinit( spki );
-    if (ret < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_sign( void *args )
-{
-    const struct key_asymmetric_sign_params *params = args;
-    struct key *key = params->key;
-    unsigned int flags = params->flags, gnutls_flags = 0;
-    gnutls_datum_t hash, signature;
-    gnutls_digest_algorithm_t hash_alg;
-    NTSTATUS status;
-    int ret;
-
-    if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384 || key->alg_id == ALG_ID_ECDSA_P521)
-    {
-        /* With ECDSA, we find the digest algorithm from the hash length, and verify it */
-        switch (params->input_len)
-        {
-        case 20: hash_alg = GNUTLS_DIG_SHA1; break;
-        case 32: hash_alg = GNUTLS_DIG_SHA256; break;
-        case 48: hash_alg = GNUTLS_DIG_SHA384; break;
-        case 64: hash_alg = GNUTLS_DIG_SHA512; break;
-
-        default:
-            FIXME( "hash size %u not yet supported\n", params->input_len );
-            return STATUS_INVALID_PARAMETER;
-        }
-
-        if (flags == BCRYPT_PAD_PKCS1)
-        {
-            BCRYPT_PKCS1_PADDING_INFO *pad = params->padding;
-            if (pad && pad->pszAlgId && get_digest_from_id( pad->pszAlgId ) != hash_alg)
-            {
-                WARN( "incorrect hashing algorithm %s, expected %u\n", debugstr_w(pad->pszAlgId), hash_alg );
-                return STATUS_INVALID_PARAMETER;
-            }
-        }
-    }
-    else if (key->alg_id == ALG_ID_DSA)
-    {
-        if (flags) FIXME( "flags %#x not supported\n", flags );
-        if (params->input_len != 20)
-        {
-            FIXME( "hash size %u not supported\n", params->input_len );
-            return STATUS_INVALID_PARAMETER;
-        }
-        hash_alg = GNUTLS_DIG_SHA1;
-    }
-    else if (flags == BCRYPT_PAD_PKCS1)
-    {
-        BCRYPT_PKCS1_PADDING_INFO *pad = params->padding;
-
-        if (!pad)
-        {
-            WARN( "padding info not found\n" );
-            return STATUS_INVALID_PARAMETER;
-        }
-        if (!pad->pszAlgId) hash_alg = GNUTLS_DIG_UNKNOWN;
-        else if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN)
-        {
-            FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
-            return STATUS_NOT_SUPPORTED;
-        }
-    }
-    else if (flags == BCRYPT_PAD_PSS)
-    {
-        BCRYPT_PSS_PADDING_INFO *pad = params->padding;
-
-        if (!pad || !pad->pszAlgId)
-        {
-            WARN( "padding info not found\n" );
-            return STATUS_INVALID_PARAMETER;
-        }
-        if (key->alg_id != ALG_ID_RSA && key->alg_id != ALG_ID_RSA_SIGN)
-        {
-            FIXME( "BCRYPT_PAD_PSS not supported for key algorithm %u\n", key->alg_id );
-            return STATUS_NOT_SUPPORTED;
-        }
-        if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN)
-        {
-            FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
-            return STATUS_NOT_SUPPORTED;
-        }
-
-        if ((status = privkey_set_rsa_pss_params( key_data(key)->a.privkey, hash_alg, pad->cbSalt ))) return status;
-        gnutls_flags = GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS;
-    }
-    else if (!flags)
-    {
-         WARN( "invalid flags %#x\n", flags );
-         return STATUS_INVALID_PARAMETER;
-    }
-    else
-    {
-        FIXME( "flags %#x not implemented\n", flags );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (!params->output)
-    {
-        *params->ret_len = len_from_bitlen( key->u.a.bitlen );
-        return STATUS_SUCCESS;
-    }
-    if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER;
-
-    hash.data = params->input;
-    hash.size = params->input_len;
-
-    signature.data = NULL;
-    signature.size = 0;
-
-    if ((ret = pgnutls_privkey_sign_hash( key_data(key)->a.privkey, hash_alg, gnutls_flags, &hash, &signature )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    status = format_gnutls_signature( key->alg_id, signature, params->output, params->output_len, params->ret_len );
-    free( signature.data );
-    return status;
-}
-
-static NTSTATUS key_asymmetric_destroy( void *args )
-{
-    struct key *key = args;
-
-    if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey );
-    if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey );
-    if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS dup_privkey( struct key *key_orig, struct key *key_copy )
-{
-    gnutls_privkey_t privkey;
-    int ret;
-
-    if ((ret = pgnutls_privkey_init( &privkey )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    switch (key_orig->alg_id)
-    {
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-    {
-        gnutls_datum_t m, e, d, p, q, u, e1, e2;
-
-        if ((ret = pgnutls_privkey_export_rsa_raw( key_data(key_orig)->a.privkey, &m, &e, &d, &p, &q, &u, &e1, &e2 )))
-            break;
-        ret = pgnutls_privkey_import_rsa_raw( privkey, &m, &e, &d, &p, &q, &u, &e1, &e2 );
-        free( m.data ); free( e.data ); free( d.data ); free( p.data ); free( q.data ); free( u.data );
-        free( e1.data ); free( e2.data );
-        break;
-    }
-    case ALG_ID_DSA:
-    {
-        gnutls_datum_t p, q, g, y, x;
-
-        if ((ret = pgnutls_privkey_export_dsa_raw( key_data(key_orig)->a.privkey, &p, &q, &g, &y, &x ))) break;
-        ret = pgnutls_privkey_import_dsa_raw( privkey, &p, &q, &g, &y, &x );
-        free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data );
-        if (!ret) key_copy->u.a.dss_seed = key_orig->u.a.dss_seed;
-        break;
-    }
-    case ALG_ID_ECDH:
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA:
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-    {
-        gnutls_ecc_curve_t curve;
-        gnutls_datum_t x, y, k;
-
-        if ((ret = pgnutls_privkey_export_ecc_raw( key_data(key_orig)->a.privkey, &curve, &x, &y, &k ))) break;
-        ret = pgnutls_privkey_import_ecc_raw( privkey, curve, &x, &y, &k );
-        free( x.data ); free( y.data ); free( k.data );
-        break;
-    }
-    case ALG_ID_DH:
-    {
-        gnutls_dh_params_t params;
-        gnutls_datum_t y, x;
-
-        if ((ret = pgnutls_dh_params_init( &params )) < 0) break;
-        if ((ret = pgnutls_privkey_export_dh_raw( key_data(key_orig)->a.privkey, params, &y, &x, 0 )) < 0)
-        {
-            pgnutls_dh_params_deinit( params );
-            break;
-        }
-        ret = pgnutls_privkey_import_dh_raw( privkey, params, &y, &x );
-        pgnutls_dh_params_deinit( params );
-        free( x.data ); free( y.data );
-        break;
-    }
-    default:
-        ERR( "unhandled algorithm %u\n", key_orig->alg_id );
-        pgnutls_privkey_deinit( privkey );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (ret < 0)
-    {
-        pgnutls_perror( ret );
-        pgnutls_privkey_deinit( privkey );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    key_data(key_copy)->a.privkey = privkey;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS dup_pubkey( struct key *key_orig, struct key *key_copy )
-{
-    gnutls_pubkey_t pubkey;
-    int ret;
-
-    if ((ret = pgnutls_pubkey_init( &pubkey )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    switch (key_orig->alg_id)
-    {
-    case ALG_ID_RSA:
-    case ALG_ID_RSA_SIGN:
-    {
-        gnutls_datum_t m, e;
-
-        if ((ret = pgnutls_pubkey_export_rsa_raw( key_data(key_orig)->a.pubkey, &m, &e ))) break;
-        ret = pgnutls_pubkey_import_rsa_raw( pubkey, &m, &e );
-        free( m.data ); free( e.data );
-        break;
-    }
-    case ALG_ID_DSA:
-    {
-        gnutls_datum_t p, q, g, y;
-
-        if ((ret = pgnutls_pubkey_export_dsa_raw( key_data(key_orig)->a.pubkey, &p, &q, &g, &y ))) break;
-        ret = pgnutls_pubkey_import_dsa_raw( pubkey, &p, &q, &g, &y );
-        free( p.data ); free( q.data ); free( g.data ); free( y.data );
-        if (!ret) key_copy->u.a.dss_seed = key_orig->u.a.dss_seed;
-        break;
-    }
-    case ALG_ID_ECDH:
-    case ALG_ID_ECDH_P256:
-    case ALG_ID_ECDH_P384:
-    case ALG_ID_ECDH_P521:
-    case ALG_ID_ECDSA:
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_ECDSA_P521:
-    {
-        gnutls_ecc_curve_t curve;
-        gnutls_datum_t x, y;
-
-        if ((ret = pgnutls_pubkey_export_ecc_raw( key_data(key_orig)->a.pubkey, &curve, &x, &y ))) break;
-        ret = pgnutls_pubkey_import_ecc_raw( pubkey, curve, &x, &y );
-        free( x.data ); free( y.data );
-        break;
-    }
-    case ALG_ID_DH:
-    {
-        gnutls_dh_params_t params;
-        gnutls_datum_t y;
-
-        if ((ret = pgnutls_dh_params_init( &params )) < 0) break;
-        if ((ret = pgnutls_pubkey_export_dh_raw( key_data(key_orig)->a.pubkey, params, &y, 0 )) < 0)
-        {
-            pgnutls_dh_params_deinit( params );
-            break;
-        }
-        ret = pgnutls_pubkey_import_dh_raw( pubkey, params, &y );
-        pgnutls_dh_params_deinit( params );
-        free( y.data );
-        break;
-    }
-    default:
-        ERR( "unhandled algorithm %u\n", key_orig->alg_id );
-        pgnutls_pubkey_deinit( pubkey );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    if (ret < 0)
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( pubkey );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    key_data(key_copy)->a.pubkey = pubkey;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_duplicate( void *args )
-{
-    const struct key_asymmetric_duplicate_params *params = args;
-    NTSTATUS status;
-
-    if (key_data(params->key_orig)->a.privkey && (status = dup_privkey( params->key_orig, params->key_copy )))
-        return status;
-
-    if (key_data(params->key_orig)->a.pubkey && (status = dup_pubkey( params->key_orig, params->key_copy )))
-        return status;
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS privkey_set_rsa_oaep_params( gnutls_privkey_t key, gnutls_digest_algorithm_t dig, gnutls_datum_t *label )
-{
-    gnutls_x509_spki_t spki;
-    int ret;
-
-    if ((ret = pgnutls_x509_spki_init( &spki )) < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    if ((ret = pgnutls_x509_spki_set_rsa_oaep_params( spki, dig, label )) < 0)
-    {
-        pgnutls_x509_spki_deinit( spki );
-        if (ret == GNUTLS_E_UNKNOWN_PK_ALGORITHM) return STATUS_NOT_SUPPORTED;
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    ret = pgnutls_privkey_set_spki( key, spki, 0 );
-    pgnutls_x509_spki_deinit( spki );
-    if (ret < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_decrypt( void *args )
-{
-    const struct key_asymmetric_decrypt_params *params = args;
-    gnutls_datum_t e, d = { 0 };
-    NTSTATUS status = STATUS_SUCCESS;
-    int ret;
-
-    if (params->key->alg_id == ALG_ID_RSA && (params->flags & BCRYPT_PAD_OAEP))
-    {
-        BCRYPT_OAEP_PADDING_INFO *pad = params->padding;
-        gnutls_digest_algorithm_t dig;
-        gnutls_datum_t label;
-
-        if (!pad || !pad->pszAlgId)
-        {
-            WARN( "padding info not found\n" );
-            return STATUS_INVALID_PARAMETER;
-        }
-
-        if ((dig = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN)
-        {
-            FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
-            return STATUS_NOT_SUPPORTED;
-        }
-
-        if (pad->pbLabel && pad->cbLabel)
-        {
-            label.data = pad->pbLabel;
-            label.size = pad->cbLabel;
-            status = privkey_set_rsa_oaep_params( key_data(params->key)->a.privkey, dig, &label );
-            if (status == STATUS_NOT_SUPPORTED) status = STATUS_SUCCESS;
-            if (status) return status;
-        }
-    }
-
-    e.data = params->input;
-    e.size = params->input_len;
-    if ((ret = pgnutls_privkey_decrypt_data( key_data(params->key)->a.privkey, 0, &e, &d )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *params->ret_len = d.size;
-    if (params->output_len >= d.size) memcpy( params->output, d.data, *params->ret_len );
-    else if (params->output) status = STATUS_BUFFER_TOO_SMALL;
-
-    free( d.data );
-    return status;
-}
-
-static NTSTATUS pubkey_set_rsa_oaep_params( gnutls_pubkey_t key, gnutls_digest_algorithm_t dig, gnutls_datum_t *label )
-{
-    gnutls_x509_spki_t spki;
-    int ret;
-
-    if ((ret = pgnutls_x509_spki_init( &spki )) < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    if ((ret = pgnutls_x509_spki_set_rsa_oaep_params( spki, dig, label )) < 0)
-    {
-        pgnutls_x509_spki_deinit( spki );
-        if (ret == GNUTLS_E_UNKNOWN_PK_ALGORITHM) return STATUS_NOT_SUPPORTED;
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    ret = pgnutls_pubkey_set_spki( key, spki, 0 );
-    pgnutls_x509_spki_deinit( spki );
-    if (ret < 0)
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_encrypt( void *args )
-{
-    const struct key_asymmetric_encrypt_params *params = args;
-    gnutls_datum_t d, e = { 0 };
-    NTSTATUS status = STATUS_SUCCESS;
-    int ret;
-
-    if (!key_data(params->key)->a.pubkey) return STATUS_INVALID_HANDLE;
-
-    if (params->key->alg_id == ALG_ID_RSA
-        && (!params->output || len_from_bitlen( params->key->u.a.bitlen ) > params->output_len))
-    {
-        *params->ret_len = len_from_bitlen( params->key->u.a.bitlen );
-        return !params->output ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
-    }
-
-    if (params->key->alg_id == ALG_ID_RSA && (params->flags & BCRYPT_PAD_OAEP))
-    {
-        BCRYPT_OAEP_PADDING_INFO *pad = params->padding;
-        gnutls_digest_algorithm_t dig;
-        gnutls_datum_t label;
-
-        if (!pad || !pad->pszAlgId)
-        {
-            WARN( "padding info not found\n" );
-            return STATUS_INVALID_PARAMETER;
-        }
-
-        if ((dig = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN)
-        {
-            FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
-            return STATUS_NOT_SUPPORTED;
-        }
-
-        if (pad->pbLabel && pad->cbLabel)
-        {
-            label.data = pad->pbLabel;
-            label.size = pad->cbLabel;
-            status = pubkey_set_rsa_oaep_params( key_data(params->key)->a.pubkey, dig, &label );
-            if (status == STATUS_NOT_SUPPORTED) status = STATUS_SUCCESS;
-            if (status) return status;
-        }
-    }
-
-    d.data = params->input;
-    d.size = params->input_len;
-    if ((ret = pgnutls_pubkey_encrypt_data(key_data(params->key)->a.pubkey, 0, &d, &e)))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *params->ret_len = e.size;
-    if (params->output_len >= e.size) memcpy( params->output, e.data, *params->ret_len );
-    else if (params->output_len == 0) status = STATUS_SUCCESS;
-    else status = STATUS_BUFFER_TOO_SMALL;
-
-    free( e.data );
-    return status;
-}
-
-static NTSTATUS key_asymmetric_derive_key( void *args )
-{
-    const struct key_asymmetric_derive_key_params *params = args;
-    gnutls_datum_t s;
-    NTSTATUS status = STATUS_SUCCESS;
-    int ret;
-
-    if ((ret = pgnutls_privkey_derive_secret( key_data(params->privkey)->a.privkey,
-                                              key_data(params->pubkey)->a.pubkey, NULL, &s, 0 )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    *params->ret_len = EXPORT_SIZE( s, len_from_bitlen( params->privkey->u.a.bitlen ), 1 );
-    if (params->output)
-    {
-        if (params->output_len < *params->ret_len) status = STATUS_BUFFER_TOO_SMALL;
-        else export_gnutls_datum( params->output, *params->ret_len, &s, 1 );
-    }
-
-    free( s.data );
-    return status;
-}
-
-const unixlib_entry_t __wine_unix_call_funcs[] =
-{
-    gnutls_process_attach,
-    gnutls_process_detach,
-    key_symmetric_vector_reset,
-    key_symmetric_set_auth_data,
-    key_symmetric_encrypt,
-    key_symmetric_decrypt,
-    key_symmetric_get_tag,
-    key_symmetric_destroy,
-    key_asymmetric_generate,
-    key_asymmetric_decrypt,
-    key_asymmetric_encrypt,
-    key_asymmetric_duplicate,
-    key_asymmetric_sign,
-    key_asymmetric_verify,
-    key_asymmetric_destroy,
-    key_asymmetric_export,
-    key_asymmetric_import,
-    key_asymmetric_derive_key,
-};
-
-C_ASSERT( ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count );
-
-#ifdef _WIN64
-
-typedef ULONG PTR32;
-
-struct key_symmetric32
-{
-    enum chain_mode mode;
-    ULONG           block_size;
-    PTR32           vector;
-    ULONG           vector_len;
-    PTR32           secret;
-    ULONG           secret_len;
-    ULONG           __cs[6];
-};
-
-struct key_asymmetric32
-{
-    ULONG             bitlen;     /* ignored for ECC keys */
-    enum ecc_curve_id curve_id;
-    ULONG             flags;
-    DSSSEED           dss_seed;
-};
-
-struct key32
-{
-    struct object hdr;
-    enum alg_id   alg_id;
-    UINT64        private[PRIVATE_DATA_SIZE];  /* private data for backend */
-    union
-    {
-        struct key_symmetric32 s;
-        struct key_asymmetric32 a;
-    } u;
-};
-
-union padding
-{
-    BCRYPT_OAEP_PADDING_INFO oaep;
-    BCRYPT_PKCS1_PADDING_INFO pkcs1;
-    BCRYPT_PSS_PADDING_INFO pss;
-};
-
-union padding32
-{
-    struct
-    {
-        PTR32 pszAlgId;
-        PTR32 pbLabel;
-        ULONG cbLabel;
-    } oaep;
-    struct
-    {
-        PTR32 pszAlgId;
-    } pkcs1;
-    struct
-    {
-        PTR32 pszAlgId;
-        ULONG cbSalt;
-    } pss;
-};
-
-static union padding *get_padding( union padding32 *padding32, union padding *padding, ULONG flags)
-{
-    if (!padding32) return NULL;
-
-    switch (flags)
-    {
-    case BCRYPT_PAD_OAEP:
-        padding->oaep.pszAlgId = ULongToPtr( padding32->oaep.pszAlgId );
-        padding->oaep.pbLabel = ULongToPtr( padding32->oaep.pbLabel );
-        padding->oaep.cbLabel = padding32->oaep.cbLabel;
-        return padding;
-
-    case BCRYPT_PAD_PKCS1:
-        padding->pkcs1.pszAlgId = ULongToPtr( padding32->pkcs1.pszAlgId );
-        return padding;
-
-    case BCRYPT_PAD_PSS:
-        padding->pss.pszAlgId = ULongToPtr( padding32->pss.pszAlgId );
-        padding->pss.cbSalt = padding32->pss.cbSalt;
-        return padding;
-
-    default:
-        break;
-    }
-    return NULL;
-}
-
-static struct key *get_symmetric_key( struct key32 *key32, struct key *key )
-{
-    key->hdr            = key32->hdr;
-    key->alg_id         = key32->alg_id;
-    memcpy( key->private, key32->private, sizeof(key->private) );
-    key->u.s.mode       = key32->u.s.mode;
-    key->u.s.block_size = key32->u.s.block_size;
-    key->u.s.vector     = ULongToPtr(key32->u.s.vector);
-    key->u.s.vector_len = key32->u.s.vector_len;
-    key->u.s.secret     = ULongToPtr(key32->u.s.secret);
-    key->u.s.secret_len = key32->u.s.secret_len;
-    return key;
-}
-
-static struct key *get_asymmetric_key( struct key32 *key32, struct key *key )
-{
-    key->hdr            = key32->hdr;
-    key->alg_id         = key32->alg_id;
-    memcpy( key->private, key32->private, sizeof(key->private) );
-    key->u.a.bitlen     = key32->u.a.bitlen;
-    key->u.a.curve_id   = key32->u.a.curve_id;
-    key->u.a.flags      = key32->u.a.flags;
-    key->u.a.dss_seed   = key32->u.a.dss_seed;
-    return key;
-}
-
-static void put_symmetric_key32( struct key *key, struct key32 *key32 )
-{
-    memcpy( key32->private, key->private, sizeof(key32->private) );
-}
-
-static void put_asymmetric_key32( struct key *key, struct key32 *key32 )
-{
-    memcpy( key32->private, key->private, sizeof(key32->private) );
-    key32->u.a.curve_id   = key->u.a.curve_id;
-    key32->u.a.flags      = key->u.a.flags;
-    key32->u.a.dss_seed   = key->u.a.dss_seed;
-}
-
-static NTSTATUS wow64_key_symmetric_vector_reset( void *args )
-{
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = args;
-
-    ret = key_symmetric_vector_reset( get_symmetric_key( key32, &key ));
-    put_symmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_symmetric_set_auth_data( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 auth_data;
-        ULONG len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_symmetric_set_auth_data_params params =
-    {
-        get_symmetric_key( key32, &key ),
-        ULongToPtr(params32->auth_data),
-        params32->len
-    };
-
-    ret = key_symmetric_set_auth_data( &params );
-    put_symmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_symmetric_encrypt( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 input;
-        ULONG input_len;
-        PTR32 output;
-        ULONG output_len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_symmetric_encrypt_params params =
-    {
-        get_symmetric_key( key32, &key ),
-        ULongToPtr(params32->input),
-        params32->input_len,
-        ULongToPtr(params32->output),
-        params32->output_len
-    };
-
-    ret = key_symmetric_encrypt( &params );
-    put_symmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_symmetric_decrypt( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 input;
-        ULONG input_len;
-        PTR32 output;
-        ULONG output_len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_symmetric_decrypt_params params =
-    {
-        get_symmetric_key( key32, &key ),
-        ULongToPtr(params32->input),
-        params32->input_len,
-        ULongToPtr(params32->output),
-        params32->output_len
-    };
-
-    ret = key_symmetric_decrypt( &params );
-    put_symmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_symmetric_get_tag( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 tag;
-        ULONG len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_symmetric_get_tag_params params =
-    {
-        get_symmetric_key( key32, &key ),
-        ULongToPtr(params32->tag),
-        params32->len
-    };
-
-    ret = key_symmetric_get_tag( &params );
-    put_symmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_symmetric_destroy( void *args )
-{
-    struct key32 *key32 = args;
-    struct key key;
-
-    return key_symmetric_destroy( get_symmetric_key( key32, &key ));
-}
-
-static NTSTATUS wow64_key_asymmetric_generate( void *args )
-{
-    struct key32 *key32 = args;
-    struct key key;
-    NTSTATUS ret;
-
-    ret = key_asymmetric_generate( get_asymmetric_key( key32, &key ));
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_decrypt( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 input;
-        ULONG input_len;
-        PTR32 padding;
-        PTR32 output;
-        ULONG output_len;
-        PTR32 ret_len;
-        ULONG flags;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    union padding padding;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_asymmetric_decrypt_params params =
-    {
-        get_asymmetric_key( key32, &key ),
-        ULongToPtr(params32->input),
-        params32->input_len,
-        get_padding( ULongToPtr(params32->padding), &padding, params32->flags ),
-        ULongToPtr(params32->output),
-        params32->output_len,
-        ULongToPtr(params32->ret_len),
-        params32->flags
-    };
-
-    ret = key_asymmetric_decrypt( &params );
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_encrypt( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 input;
-        ULONG input_len;
-        PTR32 padding;
-        PTR32 output;
-        ULONG output_len;
-        PTR32 ret_len;
-        ULONG flags;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    union padding padding;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_asymmetric_encrypt_params params =
-    {
-        get_asymmetric_key( key32, &key ),
-        ULongToPtr(params32->input),
-        params32->input_len,
-        get_padding( ULongToPtr(params32->padding), &padding, params32->flags ),
-        ULongToPtr(params32->output),
-        params32->output_len,
-        ULongToPtr(params32->ret_len),
-        params32->flags
-    };
-
-    ret = key_asymmetric_encrypt( &params );
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_duplicate( void *args )
-{
-    struct
-    {
-        PTR32 key_orig;
-        PTR32 key_copy;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key_orig, key_copy;
-    struct key32 *key_orig32 = ULongToPtr( params32->key_orig );
-    struct key32 *key_copy32 = ULongToPtr( params32->key_copy );
-    struct key_asymmetric_duplicate_params params =
-    {
-        get_asymmetric_key( key_orig32, &key_orig ),
-        get_asymmetric_key( key_copy32, &key_copy )
-    };
-
-    ret = key_asymmetric_duplicate( &params );
-    put_asymmetric_key32( &key_copy, key_copy32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_sign( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 padding;
-        PTR32 input;
-        ULONG input_len;
-        PTR32 output;
-        ULONG output_len;
-        PTR32 ret_len;
-        ULONG flags;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    union padding padding;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_asymmetric_sign_params params =
-    {
-        get_asymmetric_key( key32, &key ),
-        get_padding(ULongToPtr( params32->padding ), &padding, params32->flags),
-        ULongToPtr(params32->input),
-        params32->input_len,
-        ULongToPtr(params32->output),
-        params32->output_len,
-        ULongToPtr(params32->ret_len),
-        params32->flags
-    };
-
-    ret = key_asymmetric_sign( &params );
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_verify( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        PTR32 padding;
-        PTR32 hash;
-        ULONG hash_len;
-        PTR32 signature;
-        ULONG signature_len;
-        ULONG flags;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    union padding padding;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_asymmetric_verify_params params =
-    {
-        get_asymmetric_key( key32, &key ),
-        get_padding(ULongToPtr( params32->padding ), &padding, params32->flags),
-        ULongToPtr(params32->hash),
-        params32->hash_len,
-        ULongToPtr(params32->signature),
-        params32->signature_len,
-        params32->flags
-    };
-
-    ret = key_asymmetric_verify( &params );
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_destroy( void *args )
-{
-    struct key32 *key32 = args;
-    struct key key;
-
-    return key_asymmetric_destroy( get_asymmetric_key( key32, &key ));
-}
-
-static NTSTATUS wow64_key_asymmetric_export( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        ULONG flags;
-        PTR32 buf;
-        ULONG len;
-        PTR32 ret_len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_asymmetric_export_params params =
-    {
-        get_asymmetric_key( key32, &key ),
-        params32->flags,
-        ULongToPtr(params32->buf),
-        params32->len,
-        ULongToPtr(params32->ret_len),
-    };
-
-    ret = key_asymmetric_export( &params );
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_import( void *args )
-{
-    struct
-    {
-        PTR32 key;
-        ULONG flags;
-        PTR32 buf;
-        ULONG len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key key;
-    struct key32 *key32 = ULongToPtr( params32->key );
-    struct key_asymmetric_import_params params =
-    {
-        get_asymmetric_key( key32, &key ),
-        params32->flags,
-        ULongToPtr(params32->buf),
-        params32->len
-    };
-
-    ret = key_asymmetric_import( &params );
-    put_asymmetric_key32( &key, key32 );
-    return ret;
-}
-
-static NTSTATUS wow64_key_asymmetric_derive_key( void *args )
-{
-    struct
-    {
-        PTR32 privkey;
-        PTR32 pubkey;
-        PTR32 output;
-        ULONG output_len;
-        PTR32 ret_len;
-    } const *params32 = args;
-
-    NTSTATUS ret;
-    struct key privkey, pubkey;
-    struct key32 *privkey32 = ULongToPtr( params32->privkey );
-    struct key32 *pubkey32 = ULongToPtr( params32->pubkey );
-    struct key_asymmetric_derive_key_params params =
-    {
-        get_asymmetric_key( privkey32, &privkey ),
-        get_asymmetric_key( pubkey32, &pubkey ),
-        ULongToPtr(params32->output),
-        params32->output_len,
-        ULongToPtr(params32->ret_len),
-    };
-
-    ret = key_asymmetric_derive_key( &params );
-    put_asymmetric_key32( &privkey, privkey32 );
-    put_asymmetric_key32( &pubkey, pubkey32 );
-    return ret;
-}
-
-const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
-{
-    gnutls_process_attach,
-    gnutls_process_detach,
-    wow64_key_symmetric_vector_reset,
-    wow64_key_symmetric_set_auth_data,
-    wow64_key_symmetric_encrypt,
-    wow64_key_symmetric_decrypt,
-    wow64_key_symmetric_get_tag,
-    wow64_key_symmetric_destroy,
-    wow64_key_asymmetric_generate,
-    wow64_key_asymmetric_decrypt,
-    wow64_key_asymmetric_encrypt,
-    wow64_key_asymmetric_duplicate,
-    wow64_key_asymmetric_sign,
-    wow64_key_asymmetric_verify,
-    wow64_key_asymmetric_destroy,
-    wow64_key_asymmetric_export,
-    wow64_key_asymmetric_import,
-    wow64_key_asymmetric_derive_key,
-};
-
-C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count );
-
-#endif  /* _WIN64 */
-
-#endif /* HAVE_GNUTLS_CIPHER_INIT */
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c
index a1eb766a03e7..e7905f321458 100644
--- a/dlls/bcrypt/tests/bcrypt.c
+++ b/dlls/bcrypt/tests/bcrypt.c
@@ -2059,8 +2059,10 @@ static void test_BCryptDecrypt(void)
     ret = BCryptDestroyKey(key);
     ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
 
+    if (0) {
     ret = BCryptDestroyKey(key);
     ok(ret == STATUS_INVALID_HANDLE, "got %#lx\n", ret);
+    }
     free(buf);
 
     ret = BCryptDestroyKey(NULL);
@@ -2254,6 +2256,9 @@ static BYTE cert521Signature[] =
 
 static void test_ECDSA(void)
 {
+    static UCHAR hash[] =
+        {0x7e, 0xe3, 0x74, 0xe7, 0xc5, 0x0b, 0x6b, 0x70, 0xdb, 0xab, 0x32, 0x6d, 0x1d, 0x51, 0xd6,
+         0x74, 0x79, 0x8e, 0x5b, 0x4b};
     BYTE buffer[sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(ecc521Privkey)];
     BCRYPT_ECCKEY_BLOB *ecckey = (void *)buffer;
     BCRYPT_ALG_HANDLE alg;
@@ -2261,6 +2266,7 @@ static void test_ECDSA(void)
     NTSTATUS status;
     DWORD keylen;
     ULONG size, strength;
+    UCHAR sig[64];
 
     status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P256_ALGORITHM, NULL, 0);
     ok(!status, "got %#lx\n", status);
@@ -2440,7 +2446,7 @@ static void test_ECDSA(void)
     ok(status == STATUS_SUCCESS, "got %#lx\n", status);
 
     status = BCryptGenerateKeyPair(alg, &key, 255, 0);
-    todo_wine ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status);
+    ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status);
 
     status = BCryptGenerateKeyPair(alg, &key, 0, 0);
     ok(status == STATUS_SUCCESS, "got %#lx\n", status);
@@ -2461,6 +2467,35 @@ static void test_ECDSA(void)
     status = BCryptSetProperty(alg, BCRYPT_ECC_CURVE_NAME, (UCHAR *)BCRYPT_ECC_CURVE_25519, sizeof(BCRYPT_ECC_CURVE_25519), 0);
     ok(status == STATUS_NOT_SUPPORTED, "got %#lx\n", status);
     BCryptCloseAlgorithmProvider(alg, 0);
+
+    /* Brainpool curve */
+    status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_ALGORITHM, NULL, 0);
+    ok(!status, "got %#lx\n", status);
+
+    status = BCryptSetProperty(alg, BCRYPT_ECC_CURVE_NAME, (UCHAR *)BCRYPT_ECC_CURVE_BRAINPOOLP256R1,
+                               sizeof(BCRYPT_ECC_CURVE_BRAINPOOLP256R1), 0 );
+    ok(!status, "got %#lx\n", status);
+
+    status = BCryptGenerateKeyPair(alg, &key, 256, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+
+    strength = 0;
+    status = BCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&strength, sizeof(strength), &size, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+    ok(strength == 256, "got %lu\n", strength);
+
+    status = BCryptFinalizeKeyPair(key, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+
+    size = 0;
+    status = BCryptSignHash(key, NULL, hash, sizeof(hash), sig, sizeof(sig), &size, 0);
+    ok (status == STATUS_SUCCESS, "got %#lx\n", status);
+    ok (size == 64, "got %lu\n", size);
+
+    status = BCryptVerifySignature(key, NULL, hash, sizeof(hash), sig, size, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+    BCryptDestroyKey(key);
+    BCryptCloseAlgorithmProvider(alg, 0);
 }
 
 static UCHAR rsaPublicBlob[] =
@@ -2658,7 +2693,6 @@ static void test_rsa_encrypt(void)
     ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
 
     /*   No padding    */
-    todo_wine {
     memset(input_no_padding, 0, sizeof(input_no_padding));
 
     encrypted_size = 0;
@@ -2690,11 +2724,9 @@ static void test_rsa_encrypt(void)
 
     ret = BCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, encrypted_b, encrypted_size, &encrypted_size, BCRYPT_PAD_NONE);
     ok(ret == STATUS_SUCCESS, "got %lx\n", ret);
-    }
     ok(!memcmp(encrypted_a, encrypted_b, encrypted_size), "Both outputs should be the same\n");
     ok(!memcmp(encrypted_b, rsa_encrypted_no_padding, encrypted_size), "Data mismatch.\n");
 
-    todo_wine {
     decrypted_size = 0;
     ret = BCryptDecrypt(key, encrypted_a, encrypted_size, NULL, NULL, 0, NULL, 0, &decrypted_size, BCRYPT_PAD_NONE);
     ok(ret == STATUS_SUCCESS, "got %lx\n", ret);
@@ -2704,7 +2736,6 @@ static void test_rsa_encrypt(void)
     ok(ret == STATUS_SUCCESS, "got %lx\n", ret);
     ok(decrypted_size == sizeof(input_no_padding), "got %lu\n", decrypted_size);
     ok(!memcmp(decrypted, input_no_padding, sizeof(input_no_padding)), "unexpected output\n");
-    }
 
     /*  PKCS1 Padding  */
     encrypted_size = 0;
@@ -2849,7 +2880,7 @@ static void test_rsa_encrypt(void)
             encrypted_null = malloc(encrypted_null_size);
             ret = BCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, encrypted_null, encrypted_null_size,
                                 &encrypted_null_size, BCRYPT_PAD_OAEP);
-            ok(ret == STATUS_INVALID_PARAMETER || broken(ret == STATUS_SUCCESS),
+            todo_wine ok(ret == STATUS_INVALID_PARAMETER || broken(ret == STATUS_SUCCESS),
                "unexpected OAEP(NULL) encrypt status %lx\n", ret);
 
             free(encrypted_null);
@@ -3550,7 +3581,7 @@ static void test_ECDH(void)
     ok(status == STATUS_SUCCESS, "got %#lx\n", status);
 
     status = BCryptGenerateKeyPair(alg, &key, 255, 0);
-    todo_wine ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status);
+    ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status);
 
     status = BCryptGenerateKeyPair(alg, &key, 0, 0);
     ok(status == STATUS_SUCCESS, "got %#lx\n", status);
@@ -3601,6 +3632,20 @@ static void test_ECDH(void)
     BCryptDestroyKey(key2);
     BCryptDestroyKey(key);
     BCryptCloseAlgorithmProvider(alg, 0);
+
+    /* Brainpool curve */
+    status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_ALGORITHM, NULL, 0);
+    ok(!status, "got %#lx\n", status);
+
+    status = BCryptSetProperty(alg, BCRYPT_ECC_CURVE_NAME, (UCHAR *)BCRYPT_ECC_CURVE_BRAINPOOLP256R1,
+                               sizeof(BCRYPT_ECC_CURVE_BRAINPOOLP256R1), 0 );
+    ok(!status, "got %#lx\n", status);
+
+    status = BCryptGenerateKeyPair(alg, &key, 256, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+
+    BCryptDestroyKey(key);
+    BCryptCloseAlgorithmProvider(alg, 0);
 }
 
 static BYTE dh_pubkey[] =
@@ -3974,9 +4019,9 @@ static void test_BCryptSignHash(void)
     ok(!ret, "got %#lx\n", ret);
 
     ret = BCryptGenerateKeyPair(alg, &key, 256, 0);
-    todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
+    ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
     ret = BCryptGenerateKeyPair(alg, &key, 522, 0);
-    todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
+    ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
 
     ret = BCryptGenerateKeyPair(alg, &key, 521, 0);
     ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
@@ -4257,7 +4302,7 @@ static void test_DSA(void)
     ok(size == sizeof(*dsablob) + dsablob->cbKey * 3, "got %lu\n", size);
 
     ret = BCryptExportKey(key, NULL, BCRYPT_DSA_PRIVATE_BLOB, buf2, sizeof(buf2), &size, 0);
-    todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
+    ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
 
     ret = BCryptVerifySignature(key, NULL, dsaHash, sizeof(dsaHash), dsaSignature, sizeof(dsaSignature), 0);
     ok(!ret, "got %#lx\n", ret);
@@ -4890,6 +4935,40 @@ static void test_PBKDF2(void)
     ok(status == STATUS_SUCCESS, "got %#lx\n", status);
 }
 
+static void test_CHACHA20_POLY1305(void)
+{
+    BCRYPT_ALG_HANDLE alg;
+    NTSTATUS status;
+    ULONG len, size;
+
+    status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_CHACHA20_POLY1305_ALGORITHM, NULL, 0);
+    if (status == STATUS_NOT_FOUND)
+    {
+        win_skip("CHACHA20_POLY1305 not supported\n");
+        return;
+    }
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+
+    len = size = 0;
+    status = BCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+    ok(len, "expected non-zero len\n");
+    ok(size == sizeof(len), "got %lu\n", size);
+
+    len = size = 0;
+    status = BCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+    ok(len == 1, "got %lu\n", len);
+    ok(size == sizeof(len), "got %lu\n", size);
+
+    size = sizeof(BCRYPT_CHAIN_MODE_NA);
+    status = BCryptSetProperty(alg, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_NA, size, 0);
+    ok(!status, "got %#lx\n", status);
+
+    status = BCryptCloseAlgorithmProvider(alg, 0);
+    ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+}
+
 START_TEST(bcrypt)
 {
     HMODULE module;
@@ -4930,6 +5009,7 @@ START_TEST(bcrypt)
     test_rsa_encrypt();
     test_RC4();
     test_PBKDF2();
+    test_CHACHA20_POLY1305();
 
     FreeLibrary(module);
 }
diff --git a/dlls/combase/roapi.c b/dlls/combase/roapi.c
index f4f7c0aac268..6085b4897a15 100644
--- a/dlls/combase/roapi.c
+++ b/dlls/combase/roapi.c
@@ -18,6 +18,8 @@
  */
 #define COBJMACROS
 #include "objbase.h"
+#include "ctxtcall.h"
+#include "comsvcs.h"
 #include "initguid.h"
 #include "roapi.h"
 #include "roparameterizediid.h"
@@ -246,6 +248,7 @@ struct agile_reference
     CRITICAL_SECTION cs;
     IUnknown *obj;
     BOOLEAN is_agile;
+    IUnknown *ctx;
     LONG ref;
 };
 
@@ -324,10 +327,28 @@ static ULONG WINAPI agile_ref_Release(IAgileReference *iface)
     return ref;
 }
 
+struct marshal_context_params
+{
+    struct agile_reference *impl;
+    REFIID iid;
+};
+
+static HRESULT WINAPI marshal_object_in_context(ComCallData *arg)
+{
+    struct marshal_context_params *params = (struct marshal_context_params *)arg;
+    HRESULT hr;
+
+    hr = marshal_object_in_agile_reference(params->impl, params->iid, params->impl->obj);
+    IUnknown_Release(params->impl->obj);
+    params->impl->obj = NULL;
+    return hr;
+}
+
 static HRESULT WINAPI agile_ref_Resolve(IAgileReference *iface, REFIID riid, void **obj)
 {
     struct agile_reference *impl = impl_from_IAgileReference(iface);
     LARGE_INTEGER zero = {0};
+    void *cur_ctx;
     HRESULT hr;
 
     TRACE("(%p, %s, %p)\n", iface, debugstr_guid(riid), obj);
@@ -335,17 +356,29 @@ static HRESULT WINAPI agile_ref_Resolve(IAgileReference *iface, REFIID riid, voi
     if (impl->is_agile)
         return IUnknown_QueryInterface(impl->obj, riid, obj);
 
+    if (FAILED(hr = CoGetContextToken((ULONG_PTR *)&cur_ctx)))
+        return hr;
+
     EnterCriticalSection(&impl->cs);
     if (impl->option == AGILEREFERENCE_DELAYEDMARSHAL && impl->marshal_stream == NULL)
     {
-        if (FAILED(hr = marshal_object_in_agile_reference(impl, riid, impl->obj)))
+        struct marshal_context_params params = { impl, riid };
+        IContextCallback *ctx;
+
+        if (FAILED(hr = IUnknown_QueryInterface(impl->ctx, &IID_IContextCallback, (void **)&ctx)))
         {
             LeaveCriticalSection(&impl->cs);
             return hr;
         }
 
-        IUnknown_Release(impl->obj);
-        impl->obj = NULL;
+        hr = IContextCallback_ContextCallback(ctx, marshal_object_in_context, (ComCallData *)&params,
+                                              &IID_IContextCallback, 5, NULL);
+        IContextCallback_Release(ctx);
+        if (FAILED(hr))
+        {
+            LeaveCriticalSection(&impl->cs);
+            return hr;
+        }
     }
 
     if (SUCCEEDED(hr = IStream_Seek(impl->marshal_stream, zero, STREAM_SEEK_SET, NULL)))
@@ -380,6 +413,7 @@ static BOOL object_has_interface(IUnknown *obj, REFIID iid)
 HRESULT WINAPI RoGetAgileReference(enum AgileReferenceOptions option, REFIID riid, IUnknown *obj,
                                    IAgileReference **agile_reference)
 {
+    struct apartment *apt;
     struct agile_reference *impl;
     HRESULT hr;
 
@@ -388,11 +422,13 @@ HRESULT WINAPI RoGetAgileReference(enum AgileReferenceOptions option, REFIID rii
     if (option != AGILEREFERENCE_DEFAULT && option != AGILEREFERENCE_DELAYEDMARSHAL)
         return E_INVALIDARG;
 
-    if (!InternalIsProcessInitialized())
+    if (!(apt = apartment_get_current_or_mta()))
     {
         ERR("Apartment not initialized\n");
         return CO_E_NOTINITIALIZED;
     }
+    rpc_start_remoting(apt);
+    apartment_release(apt);
 
     if (!object_has_interface(obj, riid))
         return E_NOINTERFACE;
@@ -407,6 +443,11 @@ HRESULT WINAPI RoGetAgileReference(enum AgileReferenceOptions option, REFIID rii
     impl->option = option;
     impl->is_agile = object_has_interface(obj, &IID_IAgileObject);
     impl->ref = 1;
+    if (FAILED(hr = CoGetContextToken((ULONG_PTR *)&impl->ctx)))
+    {
+        free( impl );
+        return hr;
+    }
 
     if (option == AGILEREFERENCE_DELAYEDMARSHAL || impl->is_agile)
     {
diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c
index e4100cb11bc8..70977c9920e1 100644
--- a/dlls/combase/tests/roapi.c
+++ b/dlls/combase/tests/roapi.c
@@ -450,6 +450,166 @@ static const IUnknownVtbl unk_agile_vtbl =
     unk_Release
 };
 
+/* IUnknown implementation that has affinity to the STA it was created in. */
+struct unk_ctx_impl
+{
+    IUnknown IUnknown_iface;
+    BOOL todo;
+    UINT64 apt_id; /* Identifier of the apartment this object belongs to. */
+    ULONG_PTR context; /* The COM context this object belongs to. */
+    APTTYPE type; /* APTTYPE of the apartment this object belongs to. */
+    DWORD thread_id;
+    GUID com_thread_id;
+    LONG ref;
+};
+
+static inline struct unk_ctx_impl *impl_unk_ctx_impl_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct unk_ctx_impl, IUnknown_iface);
+}
+
+/* If true, the method call is being made from the test thread. */
+static BOOL caller_test_thread;
+
+#define test_apartment_context(impl) test_apartment_context_(__LINE__, impl)
+static void test_apartment_context_(int line, const struct unk_ctx_impl *impl)
+{
+    APTTYPEQUALIFIER qualifier;
+    ULONG_PTR cur_ctx;
+    UINT64 cur_id;
+    APTTYPE type;
+    HRESULT hr;
+
+    hr = CoGetContextToken(&cur_ctx);
+    ok_(__FILE__, line)(hr == S_OK, "CoGetContextToken failed, got hr %#lx\n", hr);
+    /* As this object has apartment-affinity, its methods can only be called from the apartment and context it was
+     * created in. */
+    ok_(__FILE__, line)(cur_ctx == impl->context, "got cur_ctx %#Ix != %#Ix\n", cur_ctx, impl->context);
+
+    hr = CoGetApartmentType(&type, &qualifier);
+    ok_(__FILE__, line)(hr == S_OK, "CoGetApartmentType failed, got hr %#lx\n", hr);
+    ok_(__FILE__, line)(type == impl->type, "got type %d\n", type);
+
+    hr = RoGetApartmentIdentifier(&cur_id);
+    /* win10 and below fail with ERROR_API_UNAVAILABLE */
+    ok_(__FILE__, line)(hr == S_OK || broken(hr == HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE)),
+                        "RoGetApartmentIdentifier failed, got hr %#lx\n", hr);
+    if (SUCCEEDED(hr))
+        ok_(__FILE__, line)(cur_id == impl->apt_id, "got cur_id %#I64x != %#I64x\n", cur_id, impl->apt_id);
+
+    if (caller_test_thread)
+    {
+        DWORD cur_tid = GetCurrentThreadId();
+        IUnknown *unk = (IUnknown *)cur_ctx;
+        GUID ctx_com_tid, cur_com_tid;
+        IComThreadingInfo *info;
+
+        hr = IUnknown_QueryInterface(unk, &IID_IComThreadingInfo, (void **)&info);
+        ok_(__FILE__, line)(hr == S_OK, "QueryInterface failed, got hr %#lx\n", hr);
+
+        hr = IComThreadingInfo_GetCurrentLogicalThreadId(info, &ctx_com_tid);
+        ok_(__FILE__, line)(hr == S_OK, "GetCurrentLogicalThreadId failed, got hr %#lx\n", hr);
+
+        hr = CoGetCurrentLogicalThreadId(&cur_com_tid);
+        ok_(__FILE__, line)(hr == S_OK, "CoGetCurrentLogicalThreadId failed, got hr %#lx\n", hr);
+        ok_(__FILE__, line)(IsEqualGUID(&cur_com_tid, &ctx_com_tid), "Got cur_com_tid %s != %s.\n",
+                            debugstr_guid(&cur_com_tid), debugstr_guid(&ctx_com_tid));
+        IComThreadingInfo_Release(info);
+
+        /* If this object belongs to an STA, we should now be in the same thread that the object was created in. */
+        if (impl->type == APTTYPE_STA || impl->type == APTTYPE_MAINSTA)
+        {
+            ok(cur_tid == impl->thread_id, "Got cur_tid %lu != %lu\n", cur_tid, impl->thread_id);
+            ok(!IsEqualGUID(&ctx_com_tid, &impl->com_thread_id), "Got cur_com_tid %s != %s\n",
+               debugstr_guid(&ctx_com_tid), debugstr_guid(&impl->com_thread_id));
+        }
+        else
+            /* If this object belongs to a MTA, then the method call will be made from the same thread as that of the
+             * caller. */
+            ok(cur_tid != impl->thread_id, "Got cur_tid %lu\n", cur_tid);
+    }
+}
+
+static HRESULT WINAPI unk_ctx_impl_QueryInterface(IUnknown *iface, const GUID *iid, void **out)
+{
+    struct unk_ctx_impl *impl = impl_unk_ctx_impl_from_IUnknown(iface);
+
+    if (winetest_debug > 1)
+        trace("(%p, %s, %p)\n", iface, debugstr_guid(iid), out);
+
+    test_apartment_context(impl);
+    if (IsEqualGUID(iid, &IID_IUnknown))
+    {
+        *out = &impl->IUnknown_iface;
+        IUnknown_AddRef((IUnknown *)*out);
+        return S_OK;
+    }
+
+    *out = NULL;
+    if (winetest_debug > 1)
+        trace("%s not implemeneted, returning E_NOINTERFACE\n", debugstr_guid(iid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI unk_ctx_impl_AddRef(IUnknown *iface)
+{
+    struct unk_ctx_impl *impl = impl_unk_ctx_impl_from_IUnknown(iface);
+
+    test_apartment_context(impl);
+    return InterlockedIncrement(&impl->ref);
+}
+
+static ULONG WINAPI unk_ctx_impl_Release(IUnknown *iface)
+{
+    struct unk_ctx_impl *impl = impl_unk_ctx_impl_from_IUnknown(iface);
+    ULONG ref = InterlockedDecrement(&impl->ref);
+
+    test_apartment_context(impl);
+    if (!ref) free(impl);
+    return ref;
+}
+
+static const IUnknownVtbl unk_ctx_impl_IUnknown_vtbl =
+{
+    unk_ctx_impl_QueryInterface,
+    unk_ctx_impl_AddRef,
+    unk_ctx_impl_Release,
+};
+
+static IUnknown *unk_ctx_impl_create(void)
+{
+    APTTYPEQUALIFIER qualifier;
+    struct unk_ctx_impl *impl;
+    UINT64 apt_id = 0;
+    ULONG_PTR context;
+    APTTYPE type;
+    HRESULT hr;
+    GUID id;
+
+    hr = CoGetApartmentType(&type, &qualifier);
+    ok(hr == S_OK, "got hr %#lx\n", hr);
+
+    hr = CoGetContextToken(&context);
+    ok(hr == S_OK, "got hr %#lx\n", hr);
+
+    hr = RoGetApartmentIdentifier(&apt_id);
+    /* win10 and below fail with ERROR_API_UNAVAILABLE */
+    ok(hr == S_OK || broken(hr == HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE)), "got hr %#lx\n", hr);
+
+    hr = CoGetCurrentLogicalThreadId(&id);
+    ok(hr == S_OK, "got hr %#lx\n", hr);
+
+    if (!(impl = calloc(1, sizeof(*impl)))) return NULL;
+    impl->IUnknown_iface.lpVtbl = &unk_ctx_impl_IUnknown_vtbl;
+    impl->context = context;
+    impl->type = type;
+    impl->apt_id = apt_id;
+    impl->com_thread_id = id;
+    impl->thread_id = GetCurrentThreadId();
+    impl->ref = 1;
+    return &impl->IUnknown_iface;
+}
+
 struct test_RoGetAgileReference_thread_param
 {
     enum AgileReferenceOptions option;
@@ -499,12 +659,42 @@ static DWORD CALLBACK test_RoGetAgileReference_thread_proc(void *arg)
     return 0;
 }
 
+struct test_agile_resolve_context_params
+{
+    RO_INIT_TYPE from_type;
+    RO_INIT_TYPE to_type;
+    IAgileReference *ref;
+};
+
+static DWORD CALLBACK test_agile_resolve_context(void *arg)
+{
+    struct test_agile_resolve_context_params *params = arg;
+    IUnknown *unknown;
+    HRESULT hr;
+
+    caller_test_thread = TRUE;
+    hr = RoInitialize(params->to_type);
+    ok(hr == S_OK, "got hr %#lx.\n", hr);
+
+    winetest_push_context("from_type=%d, to_type=%d", params->from_type, params->to_type);
+    hr = IAgileReference_Resolve(params->ref, &IID_IUnknown, (void **)&unknown);
+    ok(hr == S_OK, "got hr %#lx\n", hr);
+    if (SUCCEEDED(hr))
+        IUnknown_Release(unknown);
+    winetest_pop_context();
+
+    RoUninitialize();
+    caller_test_thread = FALSE;
+    return 0;
+}
+
 static void test_RoGetAgileReference(void)
 {
     struct test_RoGetAgileReference_thread_param param;
     struct unk_impl unk_no_marshal_obj = {{&unk_no_marshal_vtbl}, 1};
     struct unk_impl unk_obj = {{&unk_vtbl}, 1};
     struct unk_impl unk_agile_obj = {{&unk_agile_vtbl}, 1};
+    struct unk_ctx_impl *unk_ctx_impl;
     enum AgileReferenceOptions option;
     IAgileReference *agile_reference;
     RO_INIT_TYPE from_type, to_type;
@@ -624,6 +814,43 @@ static void test_RoGetAgileReference(void)
             winetest_pop_context();
         }
     }
+
+    /* Tests specific to delayed marshaling */
+    for (from_type = RO_INIT_SINGLETHREADED; from_type <= RO_INIT_MULTITHREADED; from_type++)
+    {
+        winetest_push_context("from_type=%d", from_type);
+        hr = RoInitialize(from_type);
+        ok(hr == S_OK, "got hr %#lx.\n", hr);
+
+        unknown = unk_ctx_impl_create();
+        ok(unknown != NULL, "got unknown %p.\n", unknown);
+
+        unk_ctx_impl = impl_unk_ctx_impl_from_IUnknown(unknown);
+        hr = RoGetAgileReference(AGILEREFERENCE_DELAYEDMARSHAL, &IID_IUnknown, unknown, &agile_reference);
+        ok(hr == S_OK, "got hr %#lx\n", hr);
+        EXPECT_REF(unknown, 2);
+
+        for (to_type = RO_INIT_SINGLETHREADED; to_type <= RO_INIT_MULTITHREADED; to_type++)
+        {
+            struct test_agile_resolve_context_params params = {from_type, to_type, agile_reference};
+
+            winetest_push_context("to_type=%d", to_type);
+            unk_ctx_impl->todo = TRUE;
+            thread = CreateThread(NULL, 0, test_agile_resolve_context, &params, 0, NULL);
+            flush_events();
+            ret = WaitForSingleObject(thread, INFINITE);
+            ok(!ret, "got ret %lu\n", ret);
+            CloseHandle(thread);
+            unk_ctx_impl->todo = FALSE;
+            winetest_pop_context();
+        }
+
+        IAgileReference_Release(agile_reference);
+        EXPECT_REF(unknown, 1);
+        IUnknown_Release(unknown);
+        RoUninitialize();
+        winetest_pop_context();
+    }
 }
 
 static void test_RoGetErrorReportingFlags(void)
diff --git a/dlls/comctl32/tests/misc.c b/dlls/comctl32/tests/misc.c
index 83dc93eaed33..8f578e7fca8c 100644
--- a/dlls/comctl32/tests/misc.c
+++ b/dlls/comctl32/tests/misc.c
@@ -430,7 +430,9 @@ static void check_real_class_name_( int line, HWND hwnd, const char *expect )
     ok_(__FILE__, line)( len == wcslen( expectW ), "got %ld\n", len );
 }
 
-static void check_class( const char *name, int must_exist, UINT style, UINT ignore, BOOL v6, DWORD classnameidx, BOOL classnameidx_todo )
+#define check_class( a, b, c, d, e, f, g ) check_class_( a, b, c, d, e, f, g, FALSE )
+static void check_class_( const char *name, int must_exist, UINT style, UINT ignore, BOOL v6,
+                          DWORD classnameidx, BOOL classnameidx_todo, BOOL name_todo )
 {
     WNDCLASSA wc;
 
@@ -476,7 +478,7 @@ static void check_class( const char *name, int must_exist, UINT style, UINT igno
         RegisterClassA( &wc );
 
         hwnd = CreateWindowA( wc.lpszClassName, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA( NULL ), 0 );
-        check_real_class_name( hwnd, wc.lpszClassName );
+        todo_wine_if( name_todo ) check_real_class_name( hwnd, wc.lpszClassName );
 
         DestroyWindow( hwnd );
         UnregisterClassA( wc.lpszClassName, GetModuleHandleA( NULL ) );
@@ -495,7 +497,7 @@ static void test_builtin_classes(void)
     check_class( "ComboBox",   1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE, 0x10005, FALSE );
     check_class( "Edit",       1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE, 0x10004, FALSE );
     check_class( "ListBox",    1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE, 0x10000, FALSE );
-    check_class( "ScrollBar",  1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE, 0x1000a, FALSE );
+    check_class_( "ScrollBar",  1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE, 0x1000a, FALSE, TRUE );
     check_class( "Static",     1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE, 0x10003, FALSE );
     check_class( "ComboLBox",  1, CS_SAVEBITS | CS_DBLCLKS | CS_DROPSHADOW | CS_GLOBALCLASS, CS_DROPSHADOW, FALSE, 0x10000, FALSE );
 }
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c
index c12ae22e1c71..6e47cb12ca5e 100644
--- a/dlls/comdlg32/itemdlg.c
+++ b/dlls/comdlg32/itemdlg.c
@@ -2876,7 +2876,7 @@ static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LP
     TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
 
     LocalFree(This->default_ext);
-    This->default_ext = StrDupW(pszDefaultExtension);
+    This->default_ext = (pszDefaultExtension && pszDefaultExtension[0]) ? StrDupW(pszDefaultExtension) : NULL;
 
     return S_OK;
 }
diff --git a/dlls/comdlg32/tests/itemdlg.c b/dlls/comdlg32/tests/itemdlg.c
index c74abd5350a6..fa8ae71dd66a 100644
--- a/dlls/comdlg32/tests/itemdlg.c
+++ b/dlls/comdlg32/tests/itemdlg.c
@@ -1528,6 +1528,8 @@ static void test_filename(void)
 
     /* No extension */
     test_filename_savedlg(filename_noextW, NULL, NULL, NULL, 0, 0, filename_noextW);
+    /* Empty extension */
+    test_filename_savedlg(filename_noextW, NULL, L"", NULL, 0, 0, filename_noextW);
     /* Default extension */
     test_filename_savedlg(filename_noextW, NULL, defextW, NULL, 0, 0, filename_defextW);
     /* Default extension on filename ending with a . */
diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c
index 4771be721b78..9189343b1089 100644
--- a/dlls/crypt32/tests/cert.c
+++ b/dlls/crypt32/tests/cert.c
@@ -545,7 +545,8 @@ static void testCertProperties(void)
 
     /* Set an NCrypt key handle via property 78 */
     {
-        HCRYPTPROV_OR_NCRYPT_KEY_HANDLE ncryptHandle = 0xBEEF1234;
+        char buf[100] = {0};
+        HCRYPTPROV_OR_NCRYPT_KEY_HANDLE ncryptHandle = (HCRYPTPROV_OR_NCRYPT_KEY_HANDLE)buf;
         HCRYPTPROV_OR_NCRYPT_KEY_HANDLE retrievedHandle = 0;
 
         ret = CertSetCertificateContextProperty(context,
diff --git a/dlls/d3d8/device.c b/dlls/d3d8/device.c
index 036c60edc375..c04283c1ad45 100644
--- a/dlls/d3d8/device.c
+++ b/dlls/d3d8/device.c
@@ -308,7 +308,8 @@ static BOOL wined3d_swapchain_desc_from_d3d8(struct wined3d_swapchain_desc *swap
     swapchain_desc->auto_depth_stencil_format
             = wined3dformat_from_d3dformat(present_parameters->AutoDepthStencilFormat);
     swapchain_desc->flags
-            = (present_parameters->Flags & D3DPRESENTFLAGS_MASK) | WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH;
+            = (present_parameters->Flags & D3DPRESENTFLAGS_MASK) | WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH
+            | WINED3D_SWAPCHAIN_ALLOW_MS_LOCKABLE_BACKBUFFER;
     swapchain_desc->refresh_rate = present_parameters->FullScreen_RefreshRateInHz;
     swapchain_desc->auto_restore_display_mode = TRUE;
 
diff --git a/dlls/d3d8/tests/device.c b/dlls/d3d8/tests/device.c
index 02383233cff1..fed5923ca5dc 100644
--- a/dlls/d3d8/tests/device.c
+++ b/dlls/d3d8/tests/device.c
@@ -9341,6 +9341,7 @@ static void test_lockable_backbuffer(void)
 {
     D3DPRESENT_PARAMETERS present_parameters = {0};
     struct device_desc device_desc;
+    IDirect3DSwapChain8 *swapchain;
     IDirect3DSurface8 *surface;
     IDirect3DDevice8 *device;
     D3DLOCKED_RECT lockrect;
@@ -9415,6 +9416,45 @@ static void test_lockable_backbuffer(void)
     IDirect3DSurface8_Release(surface);
     refcount = IDirect3DDevice8_Release(device);
     ok(!refcount, "Device has %lu references left.\n", refcount);
+
+    if (SUCCEEDED(IDirect3D8_CheckDeviceMultiSampleType(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
+            D3DFMT_A8R8G8B8, TRUE, D3DMULTISAMPLE_4_SAMPLES)))
+    {
+        /* Multisampling is allowed. */
+        present_parameters.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES;
+        hr = IDirect3D8_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
+                D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &device);
+        ok(hr == D3D_OK, "Failed to create device, hr %#lx.\n", hr);
+
+        hr = IDirect3DDevice8_GetBackBuffer(device, 0, D3DBACKBUFFER_TYPE_MONO, &surface);
+        ok(hr == D3D_OK, "Failed to get backbuffer, hr %#lx.\n", hr);
+
+        /* But locking multisampled is not allowed. */
+        hr = IDirect3DSurface8_LockRect(surface, &lockrect, NULL, D3DLOCK_READONLY);
+        ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
+        IDirect3DSurface8_Release(surface);
+
+        present_parameters.hDeviceWindow = NULL;
+        hr = IDirect3DDevice8_CreateAdditionalSwapChain(device, &present_parameters, &swapchain);
+        ok(hr == D3D_OK, "Failed to create swapchain, hr %#lx.\n", hr);
+
+        hr = IDirect3DSwapChain8_GetBackBuffer(swapchain, 0, 0, &surface);
+        ok(hr == D3D_OK, "Failed to get backbuffer, hr %#lx.\n", hr);
+
+        hr = IDirect3DSurface8_LockRect(surface, &lockrect, NULL, D3DLOCK_READONLY);
+        ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
+        IDirect3DSurface8_Release(surface);
+        IDirect3DSwapChain8_Release(swapchain);
+
+        IDirect3DDevice8_Release(device);
+    }
+    else
+    {
+        skip("Multisampling not supported for D3DFMT_A8R8G8B8.\n");
+    }
+
     IDirect3D8_Release(d3d);
     DestroyWindow(window);
 }
@@ -9507,7 +9547,9 @@ static void test_clip_planes_limits(void)
 static void test_swapchain_multisample_reset(void)
 {
     D3DPRESENT_PARAMETERS present_parameters;
+    IDirect3DSurface8 *surface;
     IDirect3DDevice8 *device;
+    D3DLOCKED_RECT lr;
     IDirect3D8 *d3d;
     ULONG refcount;
     HWND window;
@@ -9552,6 +9594,20 @@ static void test_swapchain_multisample_reset(void)
     hr = IDirect3DDevice8_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0xffffffff, 0.0f, 0);
     ok(hr == D3D_OK, "Failed to clear, hr %#lx.\n", hr);
 
+    /* Lockable back buffer flag is allowed. */
+    present_parameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
+    hr = IDirect3DDevice8_Reset(device, &present_parameters);
+    ok(hr == D3D_OK, "Failed to reset device, hr %#lx.\n", hr);
+
+    hr = IDirect3DDevice8_GetBackBuffer(device, 0, D3DBACKBUFFER_TYPE_MONO, &surface);
+    ok(hr == D3D_OK, "Failed to get backbuffer, hr %#lx.\n", hr);
+
+    /* But locking is not allowed. */
+    hr = IDirect3DSurface8_LockRect(surface, &lr, NULL, 0);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
+    IDirect3DSurface8_Release(surface);
+
     refcount = IDirect3DDevice8_Release(device);
     ok(!refcount, "Device has %lu references left.\n", refcount);
     IDirect3D8_Release(d3d);
diff --git a/dlls/d3d8thk/Makefile.in b/dlls/d3d8thk/Makefile.in
index bb7fd643922e..978094131af1 100644
--- a/dlls/d3d8thk/Makefile.in
+++ b/dlls/d3d8thk/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = d3d8thk.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine d3d8thk"
 VER_PRODUCTVERSION      = 5,3,2600,5512
diff --git a/dlls/d3d9/tests/device.c b/dlls/d3d9/tests/device.c
index 3dc9bff4dc27..24c02ea6b68e 100644
--- a/dlls/d3d9/tests/device.c
+++ b/dlls/d3d9/tests/device.c
@@ -13047,6 +13047,7 @@ static void test_lockable_backbuffer(void)
 {
     D3DPRESENT_PARAMETERS present_parameters = {0};
     struct device_desc device_desc;
+    IDirect3DSwapChain9 *swapchain;
     IDirect3DSurface9 *surface;
     IDirect3DDevice9 *device;
     D3DLOCKED_RECT lockrect;
@@ -13125,8 +13126,20 @@ static void test_lockable_backbuffer(void)
     ok(SUCCEEDED(hr), "Failed to unlock rect, hr %#lx.\n", hr);
 
     IDirect3DSurface9_Release(surface);
+
+    present_parameters.hDeviceWindow = NULL;
+    present_parameters.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES;
+    hr = IDirect3DDevice9_CreateAdditionalSwapChain(device, &present_parameters, &swapchain);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
     refcount = IDirect3DDevice9_Release(device);
     ok(!refcount, "Device has %lu references left.\n", refcount);
+
+    present_parameters.hDeviceWindow = window;
+    hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
+            D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &device);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
     IDirect3D9_Release(d3d);
     DestroyWindow(window);
 }
@@ -13269,6 +13282,13 @@ static void test_swapchain_multisample_reset(void)
     hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
     ok(hr == D3D_OK, "Failed to clear, hr %#lx.\n", hr);
 
+    /* Lockable back buffer flag is not allowed. */
+    d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
+    hr = IDirect3DDevice9_Reset(device, &d3dpp);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+    hr = IDirect3DDevice9_TestCooperativeLevel(device);
+    ok(hr == D3DERR_DEVICENOTRESET, "TestCooperativeLevel returned hr %#lx.\n", hr);
+
     refcount = IDirect3DDevice9_Release(device);
     ok(!refcount, "Device has %lu references left.\n", refcount);
     IDirect3D9_Release(d3d);
diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h
index f2c99a30d57b..7baf43a3f248 100644
--- a/dlls/ddraw/ddraw_private.h
+++ b/dlls/ddraw/ddraw_private.h
@@ -191,6 +191,7 @@ struct ddraw_surface
     struct ddraw_surface *next_attached;
     struct ddraw_surface *first_attached;
     IUnknown                *attached_iface;
+    DWORD attach_saved_caps;
 
     /* Complex surfaces are organized in a tree, although the tree is degenerated to a list in most cases.
      * In mipmap and primary surfaces each level has only one attachment, which is the next surface level.
@@ -199,10 +200,8 @@ struct ddraw_surface
      */
 #define MAX_COMPLEX_ATTACHED 6
     struct ddraw_surface *complex_array[MAX_COMPLEX_ATTACHED];
-    /* You can't traverse the tree upwards. Only a flag for Surface::Release because it's needed there,
-     * but no pointer to prevent temptations to traverse it in the wrong direction.
-     */
-    unsigned int is_root : 1;
+    unsigned int is_implicit : 1;
+    unsigned int is_flip_chain_start : 1;
     unsigned int is_lost : 1;
     unsigned int sysmem_fallback : 1;
 
diff --git a/dlls/ddraw/main.c b/dlls/ddraw/main.c
index faf6e13c8f25..8f2402554fec 100644
--- a/dlls/ddraw/main.c
+++ b/dlls/ddraw/main.c
@@ -32,8 +32,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
 
 static struct list global_ddraw_list = LIST_INIT(global_ddraw_list);
 
-static HINSTANCE instance;
-
 /* value of ForceRefreshRate */
 DWORD force_refresh_rate = 0;
 
@@ -874,7 +872,6 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved)
                 (const WCHAR *)&ddraw_self, &ddraw_self))
             ERR("Failed to get own module handle.\n");
 
-        instance = inst;
         DisableThreadLibraryCalls(inst);
         break;
     }
diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c
index 829fbf699ff9..1b351e7ba333 100644
--- a/dlls/ddraw/surface.c
+++ b/dlls/ddraw/surface.c
@@ -28,11 +28,19 @@ WINE_DECLARE_DEBUG_CHANNEL(fps);
 
 static struct ddraw_surface *unsafe_impl_from_IDirectDrawSurface2(IDirectDrawSurface2 *iface);
 static struct ddraw_surface *unsafe_impl_from_IDirectDrawSurface3(IDirectDrawSurface3 *iface);
+static HRESULT ddraw_surface_delete_attached_surface(struct ddraw_surface *surface,
+        struct ddraw_surface *attachment, IUnknown *detach_iface);
 
 static const struct wined3d_parent_ops ddraw_surface_wined3d_parent_ops;
 static const struct wined3d_parent_ops ddraw_texture_wined3d_parent_ops;
 static const struct wined3d_parent_ops ddraw_view_wined3d_parent_ops;
 
+static BOOL surface_attachable_for_flip(struct ddraw_surface *surface)
+{
+    return surface->version == 1 && !surface->is_implicit
+            && !(surface->surface_desc.ddsCaps.dwCaps & (DDSCAPS_ZBUFFER | DDSCAPS_TEXTURE | DDSCAPS_OVERLAY));
+}
+
 static inline struct ddraw_surface *impl_from_IDirectDrawGammaControl(IDirectDrawGammaControl *iface)
 {
     return CONTAINING_RECORD(iface, struct ddraw_surface, IDirectDrawGammaControl_iface);
@@ -595,8 +603,20 @@ static void ddraw_surface_cleanup(struct ddraw_surface *surface)
             break;
 
         surf = surface->complex_array[i];
+        while (surf && !surf->is_implicit && !surf->is_flip_chain_start)
+        {
+            if (FAILED(ddraw_surface_delete_attached_surface(surface, surf, surf->attached_iface)))
+            {
+                ERR("ddraw_surface_delete_attached_surface failed.\n");
+                break;
+            }
+            surf = surface->complex_array[i];
+        }
+        if (!surf)
+            continue;
+
         surface->complex_array[i] = NULL;
-        if (!surf->is_root)
+        if (surf->is_implicit)
         {
             struct ddraw_texture *texture = wined3d_texture_get_parent(surf->wined3d_texture);
             struct wined3d_device *wined3d_device = texture->wined3d_device;
@@ -646,7 +666,7 @@ static ULONG ddraw_surface_release_iface(struct ddraw_surface *This)
 
         /* Complex attached surfaces are destroyed implicitly when the root is released */
         wined3d_mutex_lock();
-        if (!This->is_root)
+        if (This->is_implicit)
         {
             WARN("(%p) Attempt to destroy a surface that is not a complex root\n", This);
             wined3d_mutex_unlock();
@@ -1401,7 +1421,8 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface1_Flip(IDirectDrawSurface *
 
     TRACE("iface %p, src %p, flags %#lx.\n", iface, src, flags);
 
-    if (src == iface || !(dst_impl->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_OVERLAY)))
+    if (src == iface || !(dst_impl->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_OVERLAY))
+            || !(dst_impl->surface_desc.ddsCaps.dwCaps & DDSCAPS_FLIP))
         return DDERR_NOTFLIPPABLE;
 
     if (ddraw_surface_is_lost(dst_impl))
@@ -1960,6 +1981,22 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface2_Blt(IDirectDrawSurface2 *
             src ? &src->IDirectDrawSurface_iface : NULL, src_rect, flags, fx);
 }
 
+static void surface_update_backbuffer_count(struct ddraw_surface *surface, int inc)
+{
+    struct ddraw_surface *s;
+
+    s = surface;
+    do
+    {
+        if (!s->is_implicit && s->surface_desc.dwFlags & DDSD_BACKBUFFERCOUNT)
+        {
+            s->surface_desc.dwBackBufferCount += inc;
+            break;
+        }
+        s = s->complex_array[0];
+    } while (s != surface);
+}
+
 /*****************************************************************************
  * IDirectDrawSurface7::AddAttachedSurface
  *
@@ -1981,15 +2018,16 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH ddraw_surface2_Blt(IDirectDrawSurface2 *
  * the surface that the app requested, not the complex root. See
  * GetAttachedSurface for a description how surfaces are found.
  *
+ * Attachments forming a flip chain can only be explicitly attached on ddraw
+ * v1. That is implemented by inserting those into the complex_array[] chain
+ * so that they appear the same way as implicitly created back buffers in the
+ * attachment structure.
+ *
  * This is how the current implementation works, and it was coded by looking
  * at the needs of the applications.
  *
- * So far only Z-Buffer attachments are tested, and they are activated in
+ * So far only Z-Buffer and flip chain attachments are tested, and they are activated in
  * WineD3D. Mipmaps could be tricky to activate in WineD3D.
- * Back buffers should work in 2D mode, but they are not tested(They can be
- * attached in older iface versions). Rendering to the front buffer and
- * switching between that and double buffering is not yet implemented in
- * WineD3D, so for 3D it might have unexpected results.
  *
  * ddraw_surface_attach_surface is the real thing,
  * ddraw_surface7_AddAttachedSurface is a wrapper around it that
@@ -2015,6 +2053,55 @@ static HRESULT ddraw_surface_attach_surface(struct ddraw_surface *This, struct d
 
     wined3d_mutex_lock();
 
+    if (surface_attachable_for_flip(Surf))
+    {
+        if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE
+                && !(This->ddraw->cooperative_level & DDERR_NOEXCLUSIVEMODE))
+        {
+            wined3d_mutex_unlock();
+            return DDERR_NOEXCLUSIVEMODE;
+        }
+        if (This->complex_array[0] == Surf)
+        {
+            wined3d_mutex_unlock();
+            return DDERR_SURFACEALREADYATTACHED;
+        }
+        if (Surf->complex_array[0] || Surf->surface_desc.dwWidth != This->surface_desc.dwWidth
+                || Surf->surface_desc.dwHeight != This->surface_desc.dwHeight
+                || wined3dformat_from_ddrawformat(&Surf->surface_desc.ddpfPixelFormat)
+                != wined3dformat_from_ddrawformat(&This->surface_desc.ddpfPixelFormat))
+        {
+            wined3d_mutex_unlock();
+            return DDERR_CANNOTATTACHSURFACE;
+        }
+        Surf->attach_saved_caps = Surf->surface_desc.ddsCaps.dwCaps & ~DDSCAPS_FRONTBUFFER;
+        Surf->is_flip_chain_start = 0;
+        Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP;
+        if (!This->complex_array[0])
+        {
+            This->is_flip_chain_start = 1;
+            This->complex_array[0] = Surf;
+            Surf->complex_array[0] = This;
+            This->attach_saved_caps = This->surface_desc.ddsCaps.dwCaps & ~DDSCAPS_FRONTBUFFER;
+            This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FLIP;
+            if (!(This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER)))
+                This->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER;
+
+            if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER)
+                Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER;
+            else
+                Surf->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER;
+        }
+        else
+        {
+            Surf->complex_array[0] = This->complex_array[0];
+            This->complex_array[0] = Surf;
+        }
+        surface_update_backbuffer_count(This, 1);
+        wined3d_mutex_unlock();
+        return DD_OK;
+    }
+
     /* Check if the surface is already attached somewhere */
     if (Surf->next_attached || Surf->first_attached != Surf)
     {
@@ -2095,8 +2182,11 @@ static HRESULT WINAPI ddraw_surface4_AddAttachedSurface(IDirectDrawSurface4 *ifa
             return DDERR_CANNOTATTACHSURFACE;
         }
     }
-    else if (!(surface->surface_desc.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE))
-            || !(attachment_impl->surface_desc.ddsCaps.dwCaps & (DDSCAPS_ZBUFFER)))
+    else if ((!(surface->surface_desc.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE))
+            || !(attachment_impl->surface_desc.ddsCaps.dwCaps & DDSCAPS_ZBUFFER))
+            && !(surface->version == 1
+            && !(surface->surface_desc.ddsCaps.dwCaps & ((DDSCAPS_ZBUFFER | DDSCAPS_TEXTURE | DDSCAPS_OVERLAY)))
+            && surface_attachable_for_flip(attachment_impl)))
     {
         WARN("Invalid attachment combination.\n");
         return DDERR_CANNOTATTACHSURFACE;
@@ -2188,7 +2278,57 @@ static HRESULT ddraw_surface_delete_attached_surface(struct ddraw_surface *surfa
 
     TRACE("surface %p, attachment %p, detach_iface %p.\n", surface, attachment, detach_iface);
 
+    if (!attachment)
+        return DDERR_CANNOTDETACHSURFACE;
+
     wined3d_mutex_lock();
+
+    if (surface_attachable_for_flip(attachment))
+    {
+        struct ddraw_surface *next;
+        if (surface->version != 1 || surface->complex_array[0] != attachment)
+        {
+            WARN("backbuffer %p is not attached to %p.\n", attachment, surface);
+            wined3d_mutex_unlock();
+            return DDERR_SURFACENOTATTACHED;
+        }
+        if (attachment->is_implicit || attachment->is_flip_chain_start)
+        {
+            wined3d_mutex_unlock();
+            WARN("trying to detach non-root attachment %p.\n", attachment);
+            return DDERR_CANNOTDETACHSURFACE;
+        }
+        assert(attachment->complex_array[0]);
+        surface_update_backbuffer_count(surface, -1);
+        next = surface->complex_array[0] = attachment->complex_array[0];
+        if (next == surface)
+        {
+            surface->is_flip_chain_start = 0;
+            surface->complex_array[0] = NULL;
+            surface->surface_desc.ddsCaps.dwCaps = surface->attach_saved_caps;
+        }
+        else
+        {
+            next->surface_desc.ddsCaps.dwCaps &= ~(DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER);
+            if (attachment->surface_desc.ddsCaps.dwCaps & DDSCAPS_FRONTBUFFER)
+            {
+                surface->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER;
+                next->surface_desc.ddsCaps.dwCaps |= DDSCAPS_FRONTBUFFER;
+            }
+            else
+            {
+                next->surface_desc.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER;
+            }
+        }
+        attachment->surface_desc.ddsCaps.dwCaps = attachment->attach_saved_caps;
+        attachment->complex_array[0] = NULL;
+        IUnknown_Release(detach_iface);
+        attachment->attached_iface = NULL;
+
+        wined3d_mutex_unlock();
+        return DD_OK;
+    }
+
     if (!attachment || (attachment->first_attached != surface) || (attachment == surface) )
     {
         wined3d_mutex_unlock();
@@ -2659,9 +2799,9 @@ static HRESULT WINAPI ddraw_surface7_GetPriority(IDirectDrawSurface7 *iface, DWO
         WARN("Called on offscreenplain surface, returning DDERR_INVALIDOBJECT.\n");
         hr = DDERR_INVALIDOBJECT;
     }
-    else if (!(surface->surface_desc.ddsCaps.dwCaps2 & managed) || !surface->is_root)
+    else if (!(surface->surface_desc.ddsCaps.dwCaps2 & managed) || surface->is_implicit)
     {
-        WARN("Called on non-managed texture or non-root surface, returning DDERR_INVALIDPARAMS.\n");
+        WARN("Called on non-managed texture implicit surface, returning DDERR_INVALIDPARAMS.\n");
         hr = DDERR_INVALIDPARAMS;
     }
     else
@@ -5173,7 +5313,7 @@ static HRESULT ddraw_surface_set_color_key(struct ddraw_surface *surface, DWORD
         }
     }
 
-    if (surface->is_root)
+    if (!surface->is_implicit)
         hr = ddraw_surface_set_wined3d_textures_colour_key(surface, flags,
                 color_key ? (struct wined3d_color_key *)&fixed_color_key : NULL);
 
@@ -6117,6 +6257,7 @@ static void ddraw_surface_init(struct ddraw_surface *surface, struct ddraw *ddra
     surface->iface_count = 1;
     surface->version = version;
     surface->ddraw = ddraw;
+    surface->is_implicit = 1;
 
     if (version == 7)
     {
@@ -6207,10 +6348,17 @@ static HRESULT ddraw_surface_reserve_memory(struct wined3d_texture *wined3d_text
     return hr;
 }
 
+static bool is_render_target(struct ddraw *ddraw, DWORD caps, unsigned int surface_version)
+{
+    if (caps & DDSCAPS_3DDEVICE)
+        return true;
+    return surface_version == 1 && caps & DDSCAPS_PRIMARYSURFACE && !(ddraw->flags & DDRAW_NO3D);
+}
+
 static void wined3d_resource_desc_from_ddraw(struct ddraw *ddraw,
-        struct wined3d_resource_desc *wined3d_desc, const DDSURFACEDESC2 *desc)
+        struct wined3d_resource_desc *wined3d_desc, const DDSURFACEDESC2 *desc, unsigned int version)
 {
-    const DWORD caps = desc->ddsCaps.dwCaps;
+    DWORD caps = desc->ddsCaps.dwCaps;
     const DWORD caps2 = desc->ddsCaps.dwCaps2;
 
     wined3d_desc->resource_type = WINED3D_RTYPE_TEXTURE_2D;
@@ -6230,7 +6378,7 @@ static void wined3d_resource_desc_from_ddraw(struct ddraw *ddraw,
     wined3d_desc->bind_flags |= WINED3D_BIND_SHADER_RESOURCE;
     if (caps & DDSCAPS_ZBUFFER)
         wined3d_desc->bind_flags |= WINED3D_BIND_DEPTH_STENCIL;
-    else if (caps & DDSCAPS_3DDEVICE)
+    else if (is_render_target(ddraw, caps, version))
         wined3d_desc->bind_flags |= WINED3D_BIND_RENDER_TARGET;
 
     if ((caps & DDSCAPS_SYSTEMMEMORY) && !(caps2 & (DDSCAPS2_TEXTUREMANAGE | DDSCAPS2_D3DTEXTUREMANAGE)))
@@ -6248,7 +6396,7 @@ static void wined3d_resource_desc_from_ddraw(struct ddraw *ddraw,
         else if (caps & DDSCAPS_VIDEOMEMORY)
         {
             /* Dynamic resources can't be written by the GPU. */
-            if (!(caps & (DDSCAPS_3DDEVICE | DDSCAPS_ZBUFFER)))
+            if (!(caps & DDSCAPS_ZBUFFER) && !is_render_target(ddraw, caps, version))
                 wined3d_desc->usage |= WINED3DUSAGE_DYNAMIC;
         }
     }
@@ -6277,7 +6425,7 @@ static HRESULT ddraw_texture_init(struct ddraw_texture *texture, struct ddraw *d
     unsigned int i, j;
     HRESULT hr;
 
-    wined3d_resource_desc_from_ddraw(ddraw, &wined3d_desc, desc);
+    wined3d_resource_desc_from_ddraw(ddraw, &wined3d_desc, desc, texture->version);
 
     if (wined3d_desc.format == WINED3DFMT_UNKNOWN)
     {
@@ -6856,7 +7004,7 @@ HRESULT ddraw_surface_create(struct ddraw *ddraw, const DDSURFACEDESC2 *surface_
 
             if (desc->ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
                 bind_flags |= WINED3D_BIND_DEPTH_STENCIL;
-            else if (desc->ddsCaps.dwCaps & DDSCAPS_3DDEVICE)
+            else if (is_render_target(ddraw, desc->ddsCaps.dwCaps, version))
                 bind_flags |= WINED3D_BIND_RENDER_TARGET;
 
             if (!(ddraw->flags & DDRAW_NO3D) && SUCCEEDED(hr = wined3d_check_device_format(ddraw->wined3d,
@@ -6979,13 +7127,14 @@ HRESULT ddraw_surface_create(struct ddraw *ddraw, const DDSURFACEDESC2 *surface_
     }
 
     root = texture->root;
-    root->is_root = TRUE;
+    root->is_implicit = 0;
 
     if (desc->dwFlags & DDSD_BACKBUFFERCOUNT)
     {
         unsigned int count = desc->dwBackBufferCount;
         struct ddraw_surface *last = root;
 
+        root->is_flip_chain_start = 1;
         attach = &last->complex_array[0];
         for (i = 0; i < count; ++i)
         {
diff --git a/dlls/ddraw/tests/d3d.c b/dlls/ddraw/tests/d3d.c
index ec536a837290..0403d8da6dd8 100644
--- a/dlls/ddraw/tests/d3d.c
+++ b/dlls/ddraw/tests/d3d.c
@@ -1554,101 +1554,6 @@ static void BackBuffer3DCreateSurfaceTest(void)
     IDirectDraw7_Release(dd7);
 }
 
-static void BackBuffer3DAttachmentTest(void)
-{
-    HRESULT hr;
-    IDirectDrawSurface *surface1, *surface2, *surface3, *surface4;
-    DDSURFACEDESC ddsd;
-    HWND window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
-            100, 100, 160, 160, NULL, NULL, NULL, NULL);
-
-    hr = IDirectDraw_SetCooperativeLevel(DirectDraw1, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    /* Perform attachment tests on a back-buffer */
-    memset(&ddsd, 0, sizeof(ddsd));
-    ddsd.dwSize = sizeof(ddsd);
-    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
-    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE;
-    ddsd.dwWidth = GetSystemMetrics(SM_CXSCREEN);
-    ddsd.dwHeight = GetSystemMetrics(SM_CYSCREEN);
-    hr = IDirectDraw_CreateSurface(DirectDraw1, &ddsd, &surface2, NULL);
-    ok(SUCCEEDED(hr), "Got hr %#lx.\n", hr);
-
-    if (surface2 != NULL)
-    {
-        /* Try a single primary and a two back buffers */
-        memset(&ddsd, 0, sizeof(ddsd));
-        ddsd.dwSize = sizeof(ddsd);
-        ddsd.dwFlags = DDSD_CAPS;
-        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
-        hr = IDirectDraw_CreateSurface(DirectDraw1, &ddsd, &surface1, NULL);
-        ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-        memset(&ddsd, 0, sizeof(ddsd));
-        ddsd.dwSize = sizeof(ddsd);
-        ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
-        ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE;
-        ddsd.dwWidth = GetSystemMetrics(SM_CXSCREEN);
-        ddsd.dwHeight = GetSystemMetrics(SM_CYSCREEN);
-        hr = IDirectDraw_CreateSurface(DirectDraw1, &ddsd, &surface3, NULL);
-        ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-        /* This one has a different size */
-        memset(&ddsd, 0, sizeof(ddsd));
-        ddsd.dwSize = sizeof(ddsd);
-        ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
-        ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE;
-        ddsd.dwWidth = 128;
-        ddsd.dwHeight = 128;
-        hr = IDirectDraw_CreateSurface(DirectDraw1, &ddsd, &surface4, NULL);
-        ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-        hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2);
-        todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr);
-        if(SUCCEEDED(hr))
-        {
-            /* Try the reverse without detaching first */
-            hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1);
-            ok(hr == DDERR_SURFACEALREADYATTACHED, "Got hr %#lx.\n", hr);
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2);
-            ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-        }
-        hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1);
-        todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr);
-        if(SUCCEEDED(hr))
-        {
-            /* Try to detach reversed */
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2);
-            ok(hr == DDERR_CANNOTDETACHSURFACE, "Got hr %#lx.\n", hr);
-            /* Now the proper detach */
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface1);
-            ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-        }
-        hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface3);
-        todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr);
-        if(SUCCEEDED(hr))
-        {
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface3);
-            ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-        }
-        hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface4);
-        ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr);
-        hr = IDirectDrawSurface_AddAttachedSurface(surface4, surface1);
-        ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr);
-
-        IDirectDrawSurface_Release(surface4);
-        IDirectDrawSurface_Release(surface3);
-        IDirectDrawSurface_Release(surface2);
-        IDirectDrawSurface_Release(surface1);
-    }
-
-    hr =IDirectDraw_SetCooperativeLevel(DirectDraw1, NULL, DDSCL_NORMAL);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    DestroyWindow(window);
-}
-
 static void dump_format(const DDPIXELFORMAT *fmt)
 {
     trace("dwFlags %08lx, FourCC %08lx, dwZBufferBitDepth %lu, stencil %08lx\n", fmt->dwFlags, fmt->dwFourCC,
@@ -2101,7 +2006,6 @@ START_TEST(d3d)
         TextureLoadTest();
         ViewportTest();
         BackBuffer3DCreateSurfaceTest();
-        BackBuffer3DAttachmentTest();
         test_get_caps1();
         D3D1_releaseObjects();
     }
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 007daf294074..b3a21c5010be 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -452,6 +452,21 @@ static void fill_surface(IDirectDrawSurface *surface, D3DCOLOR color)
     ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#lx.\n", hr);
 }
 
+#define check_surface_caps(a, b, c, d) check_surface_caps_(__LINE__, (a), (b), (c), (d))
+static void check_surface_caps_(unsigned int line, IDirectDrawSurface *surface, DWORD expected_caps,
+        DWORD exclude_caps, DWORD expected_buffer_count)
+{
+    DDSURFACEDESC surface_desc = {.dwSize = sizeof(surface_desc) };
+    HRESULT hr;
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(surface, &surface_desc);
+    ok_(__FILE__, line)(hr == DD_OK, "got %#lx.\n", hr);
+    ok_(__FILE__, line)((surface_desc.ddsCaps.dwCaps & ~exclude_caps) == expected_caps, "got %#lx, expected %#lx.\n",
+            surface_desc.ddsCaps.dwCaps & ~exclude_caps, expected_caps);
+    ok_(__FILE__, line)(surface_desc.dwBackBufferCount == expected_buffer_count, "got backbuffer count %ld, expected %ld.\n",
+            surface_desc.dwBackBufferCount, expected_buffer_count);
+}
+
 static void check_rect(IDirectDrawSurface *surface, RECT r)
 {
     LONG x_coords[2][2] =
@@ -5406,13 +5421,16 @@ static void test_flip(void)
     IDirectDrawSurface *frontbuffer, *backbuffer1, *backbuffer2, *backbuffer3, *surface;
     DDSCAPS caps = {DDSCAPS_FLIP};
     DDSURFACEDESC surface_desc;
+    IDirect3DDevice *device;
     unsigned int color, i;
+    DWORD expected_caps;
     BOOL sysmem_primary;
     IDirectDraw *ddraw;
-    DWORD expected_caps;
+    BOOL have_3ddevice;
     ULONG refcount;
     HWND window;
     HRESULT hr;
+    LONG ref;
 
     static const struct
     {
@@ -5430,6 +5448,16 @@ static void test_flip(void)
     ddraw = create_ddraw();
     ok(!!ddraw, "Failed to create a ddraw object.\n");
 
+    if ((device = create_device(ddraw, window, DDSCL_NORMAL)))
+    {
+        have_3ddevice = TRUE;
+        IDirect3DDevice_Release(device);
+    }
+    else
+    {
+        have_3ddevice = FALSE;
+    }
+
     hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
     ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#lx.\n", hr);
 
@@ -5489,52 +5517,26 @@ static void test_flip(void)
         hr = restore_surfaces(ddraw);
         ok(SUCCEEDED(hr), "%s: Failed to restore surfaces, hr %#lx.\n", test_data[i].name, hr);
 
-        memset(&surface_desc, 0, sizeof(surface_desc));
-        surface_desc.dwSize = sizeof(surface_desc);
-        hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc);
-        ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr);
         expected_caps = DDSCAPS_FRONTBUFFER | DDSCAPS_COMPLEX | DDSCAPS_FLIP | test_data[i].caps;
         if (test_data[i].caps & DDSCAPS_PRIMARYSURFACE)
             expected_caps |= DDSCAPS_VISIBLE;
-        ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps,
-                "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps);
+        check_surface_caps(frontbuffer, expected_caps, placement, 3);
         sysmem_primary = surface_desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY;
 
         hr = IDirectDrawSurface_GetAttachedSurface(frontbuffer, &caps, &backbuffer1);
         ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr);
-        memset(&surface_desc, 0, sizeof(surface_desc));
-        surface_desc.dwSize = sizeof(surface_desc);
-        hr = IDirectDrawSurface_GetSurfaceDesc(backbuffer1, &surface_desc);
-        ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr);
-        ok(!surface_desc.dwBackBufferCount, "%s: Got unexpected back buffer count %lu.\n",
-                test_data[i].name, surface_desc.dwBackBufferCount);
         expected_caps &= ~(DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER);
         expected_caps |= DDSCAPS_BACKBUFFER;
-        ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps,
-                "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps);
+        check_surface_caps(backbuffer1, expected_caps, placement, 0);
 
         hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &backbuffer2);
         ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr);
-        memset(&surface_desc, 0, sizeof(surface_desc));
-        surface_desc.dwSize = sizeof(surface_desc);
-        hr = IDirectDrawSurface_GetSurfaceDesc(backbuffer2, &surface_desc);
-        ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr);
-        ok(!surface_desc.dwBackBufferCount, "%s: Got unexpected back buffer count %lu.\n",
-                test_data[i].name, surface_desc.dwBackBufferCount);
         expected_caps &= ~DDSCAPS_BACKBUFFER;
-        ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps,
-                "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps);
+        check_surface_caps(backbuffer2, expected_caps, placement, 0);
 
         hr = IDirectDrawSurface_GetAttachedSurface(backbuffer2, &caps, &backbuffer3);
         ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr);
-        memset(&surface_desc, 0, sizeof(surface_desc));
-        surface_desc.dwSize = sizeof(surface_desc);
-        hr = IDirectDrawSurface_GetSurfaceDesc(backbuffer3, &surface_desc);
-        ok(SUCCEEDED(hr), "%s: Failed to get surface desc, hr %#lx.\n", test_data[i].name, hr);
-        ok(!surface_desc.dwBackBufferCount, "%s: Got unexpected back buffer count %lu.\n",
-                test_data[i].name, surface_desc.dwBackBufferCount);
-        ok((surface_desc.ddsCaps.dwCaps & ~placement) == expected_caps,
-                "%s: Got unexpected caps %#lx.\n", test_data[i].name, surface_desc.ddsCaps.dwCaps);
+        check_surface_caps(backbuffer3, expected_caps, placement, 0);
 
         hr = IDirectDrawSurface_GetAttachedSurface(backbuffer3, &caps, &surface);
         ok(SUCCEEDED(hr), "%s: Failed to get attached surface, hr %#lx.\n", test_data[i].name, hr);
@@ -5631,6 +5633,241 @@ static void test_flip(void)
         IDirectDrawSurface_Release(frontbuffer);
     }
 
+    /* Test adding backbuffers to primary surface in exclusive fullscreen mode. */
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &frontbuffer, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    fill_surface(frontbuffer, 0xffff0000);
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer1, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    fill_surface(backbuffer1, 0xff00ff00);
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    fill_surface(backbuffer2, 0xff0000ff);
+
+    hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "Got unexpected hr %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, frontbuffer);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0);
+    check_surface_caps(backbuffer1, DDSCAPS_3DDEVICE | DDSCAPS_BACKBUFFER | DDSCAPS_FLIP, placement, 0);
+    hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    color = get_surface_color(frontbuffer, 1, 1);
+    ok(color == 0x0000ff00, "got %#x.\n", color);
+    color = get_surface_color(backbuffer1, 1, 1);
+    ok(color == 0x00ff0000, "got %#x.\n", color);
+
+    hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(frontbuffer, 0, backbuffer1);
+    ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, frontbuffer);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE, placement, 0);
+    check_surface_caps(backbuffer1, DDSCAPS_3DDEVICE | DDSCAPS_BACKBUFFER, placement, 0);
+
+    check_surface_caps(backbuffer2, DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE, placement, 0);
+    check_surface_caps(backbuffer1, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE, placement, 0);
+    check_surface_caps(backbuffer2, DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE, placement, 0);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer2, 0, backbuffer1);
+    ok(hr == DDERR_CANNOTDETACHSURFACE, "Failed to delete attached surface, hr %#lx.\n", hr);
+
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 2, "got %ld.\n", ref);
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 1, "got %ld.\n", ref);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2);
+    ok(hr == DD_OK, "Failed to delete attached surface, hr %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 1, "got %ld.\n", ref);
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 1, "got %ld.\n", ref);
+
+    check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE, placement, 0);
+
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 1, "got %ld.\n", ref);
+    hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 2, "got %ld.\n", ref);
+
+    check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0);
+    check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0);
+
+    hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    if (have_3ddevice)
+    {
+        hr = IDirectDrawSurface_QueryInterface(backbuffer1, &IID_IDirect3DHALDevice, (void **)&device);
+        ok(hr == DD_OK || broken(hr == E_FAIL), "got %#lx.\n", hr);
+        if (SUCCEEDED(hr))
+            IDirect3DDevice_Release(device);
+    }
+
+    color = get_surface_color(frontbuffer, 1, 1);
+    ok(color == 0x00ff0000, "got %#x.\n", color);
+    color = get_surface_color(backbuffer1, 1, 1);
+    ok(color == 0x000ff00, "got %#x.\n", color);
+
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 1, "got %ld.\n", ref);
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 2, "got %ld.\n", ref);
+    check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2);
+    ok(hr == DDERR_SURFACEALREADYATTACHED, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer2);
+    ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_Flip(backbuffer2, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    color = get_surface_color(frontbuffer, 1, 1);
+    ok(color == 0x0000ff00, "got %#x.\n", color);
+    color = get_surface_color(backbuffer1, 1, 1);
+    ok(color == 0x000000ff, "got %#x.\n", color);
+    color = get_surface_color(backbuffer2, 1, 1);
+    ok(color == 0x00ff0000, "got %#x.\n", color);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(frontbuffer, 0, backbuffer2);
+    ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)frontbuffer);
+    ok(ref == 1, "got %ld.\n", ref);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)frontbuffer);
+    todo_wine ok(ref == 2, "got %ld.\n", ref);
+
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 2, "got %ld.\n", ref);
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 1, "got %ld.\n", ref);
+    ref = IDirectDrawSurface_Release(frontbuffer);
+    todo_wine ok(ref == 1, "got %ld.\n", ref);
+    if (ref)
+        IDirectDrawSurface_Release(frontbuffer);
+    ref = IDirectDrawSurface_Release(backbuffer1);
+    ok(!ref, "got %ld.\n", ref);
+    ref = IDirectDrawSurface_Release(backbuffer2);
+    ok(!ref, "got %ld.\n", ref);
+
+    /* Test adding backbuffers to primary surface in windowed mode. */
+    hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &frontbuffer, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    fill_surface(frontbuffer, 0xffff0000);
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer1, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    fill_surface(backbuffer1, 0xff00ff00);
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(frontbuffer, &surface_desc);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    fill_surface(backbuffer2, 0xff0000ff);
+
+    hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "Got unexpected hr %#lx.\n", hr);
+
+    check_surface_caps(frontbuffer, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER, placement, 0);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1);
+    ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer2);
+    ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1);
+    ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr);
+
+    check_surface_caps(backbuffer2, DDSCAPS_FRONTBUFFER | DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer2, 0, backbuffer1);
+    ok(hr == DDERR_CANNOTDETACHSURFACE, "Failed to delete attached surface, hr %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(frontbuffer, backbuffer1);
+    ok(hr == DDERR_NOEXCLUSIVEMODE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_Flip(frontbuffer, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_Flip(backbuffer2, NULL, DDFLIP_WAIT);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    color = get_surface_color(backbuffer1, 1, 1);
+    ok(color == 0x000000ff, "got %#x.\n", color);
+    color = get_surface_color(backbuffer2, 1, 1);
+    ok(color == 0x0000ff00, "got %#x.\n", color);
+
+    hr = IDirectDrawSurface_Flip(backbuffer2, NULL, DDFLIP_WAIT);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_Flip(backbuffer1, NULL, DDFLIP_WAIT);
+    ok(hr == DDERR_NOTFLIPPABLE, "got %#lx.\n", hr);
+
+    color = get_surface_color(backbuffer1, 1, 1);
+    ok(color == 0x0000ff00, "got %#x.\n", color);
+    color = get_surface_color(backbuffer2, 1, 1);
+    ok(color == 0x000000ff, "got %#x.\n", color);
+
+    ref = IDirectDrawSurface_Release(frontbuffer);
+    ok(!ref, "got %ld.\n", ref);
+    ref = IDirectDrawSurface_Release(backbuffer1);
+    ok(!ref, "got %ld.\n", ref);
+    ref = IDirectDrawSurface_Release(backbuffer2);
+    ok(!ref, "got %ld.\n", ref);
+
+    hr = IDirectDraw_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
     refcount = IDirectDraw_Release(ddraw);
     ok(!refcount, "Unexpected refcount %lu.\n", refcount);
     DestroyWindow(window);
@@ -5840,14 +6077,17 @@ static HRESULT WINAPI surface_counter(IDirectDrawSurface *surface, DDSURFACEDESC
 
 static void test_surface_attachment(void)
 {
-    IDirectDrawSurface *surface1, *surface2, *surface3, *surface4;
-    DDSCAPS caps = {DDSCAPS_TEXTURE};
+    const DWORD placement = DDSCAPS_LOCALVIDMEM | DDSCAPS_VIDEOMEMORY | DDSCAPS_SYSTEMMEMORY;
+    IDirectDrawSurface *surface1, *surface2, *surface3, *surface4, *surface5, *surface6, *tmp;
+    IDirectDrawSurface *backbuffer1, *backbuffer2;
     DDSURFACEDESC surface_desc;
     IDirectDraw *ddraw;
     UINT surface_count;
     ULONG refcount;
+    DDSCAPS caps;
     HWND window;
     HRESULT hr;
+    LONG ref;
 
     window = create_window();
     ddraw = create_ddraw();
@@ -5865,6 +6105,7 @@ static void test_surface_attachment(void)
     hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface1, NULL);
     ok(SUCCEEDED(hr), "Failed to create surface, hr %#lx.\n", hr);
 
+    caps.dwCaps = DDSCAPS_TEXTURE;
     hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &surface2);
     ok(SUCCEEDED(hr), "Failed to get mip level, hr %#lx.\n", hr);
     hr = IDirectDrawSurface_GetAttachedSurface(surface2, &caps, &surface3);
@@ -5980,8 +6221,30 @@ static void test_surface_attachment(void)
     hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface4, NULL);
     ok(SUCCEEDED(hr), "Failed to create surface, hr %#lx.\n", hr);
 
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
+    surface_desc.dwWidth = registry_mode.dmPelsWidth;
+    surface_desc.dwHeight = registry_mode.dmPelsHeight;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface5, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_FRONTBUFFER;
+    surface_desc.dwWidth = registry_mode.dmPelsWidth;
+    surface_desc.dwHeight = registry_mode.dmPelsHeight;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface6, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE,
+            placement, 0);
     hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2);
     ok(SUCCEEDED(hr), "Failed to attach surface, hr %#lx.\n", hr);
+    check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_VISIBLE | DDSCAPS_FLIP,
+            placement, 0);
     /* Try the reverse without detaching first. */
     hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1);
     ok(hr == DDERR_SURFACEALREADYATTACHED, "Got unexpected hr %#lx.\n", hr);
@@ -6006,10 +6269,249 @@ static void test_surface_attachment(void)
     hr = IDirectDrawSurface_AddAttachedSurface(surface4, surface1);
     ok(hr == DDERR_CANNOTATTACHSURFACE, "Got unexpected hr %#lx.\n", hr);
 
+    check_surface_caps(surface5, DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, DDSCAPS_FRONTBUFFER, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(surface5, surface6);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface6);
+    ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface5);
+    ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface5, 0, surface6);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(surface5, DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, 0, placement, 0);
+    check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface5);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(surface1, DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE, placement, 0);
+    check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, 0, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(surface5, surface6);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0);
+    check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, DDSCAPS_FLIP, placement, 0);
+
+    caps.dwCaps = DDSCAPS_FLIP;
+    hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == surface5, "got %p, %p.\n", tmp, surface5);
+    IDirectDrawSurface_Release(tmp);
+    hr = IDirectDrawSurface_GetAttachedSurface(surface5, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == surface6, "got %p, %p.\n", tmp, surface5);
+    IDirectDrawSurface_Release(tmp);
+    hr = IDirectDrawSurface_GetAttachedSurface(surface6, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == surface1, "got %p, %p.\n", tmp, surface5);
+    IDirectDrawSurface_Release(tmp);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface6);
+    ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface6, 0, surface1);
+    ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)surface5);
+    ok(ref == 2, "got %ld,\n", ref);
+    ref = get_refcount((IUnknown *)surface6);
+    ok(ref == 2, "got %ld,\n", ref);
+    check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0);
+    check_surface_caps(surface5, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, DDSCAPS_FLIP, placement, 0);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface5);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)surface5);
+    ok(ref == 1, "got %ld,\n", ref);
+    ref = get_refcount((IUnknown *)surface6);
+    ok(ref == 2, "got %ld,\n", ref);
+    check_surface_caps(surface1, DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE | DDSCAPS_FRONTBUFFER | DDSCAPS_FLIP, placement, 0);
+    check_surface_caps(surface5, DDSCAPS_BACKBUFFER, placement, 0);
+    check_surface_caps(surface6, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER, placement, 0);
+
+    hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == surface6, "got %p, %p.\n", tmp, surface6);
+    IDirectDrawSurface_Release(surface6);
+
+    hr = IDirectDrawSurface_GetAttachedSurface(surface5, &caps, &tmp);
+    ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface5, 0, surface6);
+    ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)surface6);
+    ok(ref == 2, "got %ld,\n", ref);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface6);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(surface6, 0, placement, 0);
+
+    hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp);
+    ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr);
+
+    ref = IDirectDrawSurface_Release(surface1);
+    ok(!ref, "got %ld\n", ref);
+    check_surface_caps(surface6, 0, placement, 0);
+
+    ref = IDirectDrawSurface_Release(surface6);
+    ok(!ref, "got %ld\n", ref);
+    ref = IDirectDrawSurface_Release(surface5);
+    ok(!ref, "got %ld\n", ref);
     IDirectDrawSurface_Release(surface4);
     IDirectDrawSurface_Release(surface3);
     IDirectDrawSurface_Release(surface2);
-    IDirectDrawSurface_Release(surface1);
+
+    /* Test adding backbuffers to complex surface with auto created backbuffers. */
+    caps.dwCaps = DDSCAPS_FLIP;
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
+    surface_desc.dwBackBufferCount = 2;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface1, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &backbuffer1);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 2, "got %ld.\n", ref);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface1);
+    ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, backbuffer1);
+    ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(surface1, &surface_desc);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_3DDEVICE;
+    surface_desc.dwWidth /= 2;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    /* size mismatch */
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, backbuffer2);
+    ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr);
+    IDirectDrawSurface_Release(backbuffer2);
+
+    hr = IDirectDrawSurface_GetSurfaceDesc(surface1, &surface_desc);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_3DDEVICE;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &backbuffer2, NULL);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_ZBUFFER;
+    surface_desc.ddpfPixelFormat.dwZBufferBitDepth = 16;
+    surface_desc.ddpfPixelFormat.dwZBitMask = 0x0000ffff;
+    surface_desc.dwWidth = registry_mode.dmPelsWidth;
+    surface_desc.dwHeight = registry_mode.dmPelsHeight;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface2, NULL);
+    ok(hr == D3D_OK, "Failed to create surface, hr %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)surface1);
+    ok(ref == 1, "got %ld.\n", ref);
+    caps.dwCaps = DDSCAPS_ZBUFFER;
+    hr = IDirectDrawSurface_GetAttachedSurface(surface1, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == surface2, "got %p, %p.\n", tmp, surface2);
+    IDirectDrawSurface_Release(tmp);
+
+    caps.dwCaps = DDSCAPS_FLIP;
+    hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &surface3);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+
+    caps.dwCaps = DDSCAPS_COMPLEX;
+    hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == surface3, "got %p, %p.\n", tmp, surface3);
+    IDirectDrawSurface_Release(tmp);
+    caps.dwCaps = DDSCAPS_ZBUFFER;
+    hr = IDirectDrawSurface_GetAttachedSurface(backbuffer1, &caps, &tmp);
+    ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr);
+    ref = IDirectDrawSurface_Release(surface3);
+    ok(ref == 1, "got %ld.\n", ref);
+
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 2, "got %ld.\n", ref);
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 1, "got %ld.\n", ref);
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ref = get_refcount((IUnknown *)backbuffer2);
+    ok(ref == 2, "got %ld.\n", ref);
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 2, "got %ld.\n", ref);
+    check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER
+            | DDSCAPS_FLIP, placement, 3);
+    caps.dwCaps = DDSCAPS_FLIP;
+    hr = IDirectDrawSurface_GetAttachedSurface(backbuffer2, &caps, &tmp);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    ok(tmp == backbuffer1, "got %p, %p.\n", backbuffer1, tmp);
+    ref = IDirectDrawSurface_Release(tmp);
+    ok(ref == 2, "got %ld.\n", ref);
+
+    caps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+    hr = IDirectDrawSurface_GetAttachedSurface(backbuffer2, &caps, &tmp);
+    ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr);
+    caps.dwCaps = DDSCAPS_FLIP;
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer2, 0, backbuffer1);
+    ok(hr == DDERR_CANNOTDETACHSURFACE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2);
+    ok(hr == DDERR_SURFACENOTATTACHED, "got %#lx.\n", hr);
+
+    ref = get_refcount((IUnknown *)backbuffer1);
+    ok(ref == 2, "got %ld.\n", ref);
+    hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    /* Looks like here is a ddraw bug here, when deleting a surface the next one in chain gets an extra reference. */
+    ref = get_refcount((IUnknown *)backbuffer1);
+    todo_wine ok(ref == 3, "got %ld.\n", ref);
+    check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER
+            | DDSCAPS_FLIP, placement, 2);
+    check_surface_caps(backbuffer1, DDSCAPS_FLIP | DDSCAPS_BACKBUFFER | DDSCAPS_COMPLEX, placement, 0);
+    check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE, placement, 0);
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer1, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP | DDSCAPS_COMPLEX, placement, 0);
+    check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE | DDSCAPS_FLIP, placement, 0);
+    check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER
+            | DDSCAPS_FLIP, placement, 3);
+
+    hr = IDirectDrawSurface_AddAttachedSurface(surface1, backbuffer2);
+    ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer2, surface1);
+    ok(hr == DDERR_CANNOTATTACHSURFACE  , "got %#lx.\n", hr);
+    hr = IDirectDrawSurface_AddAttachedSurface(backbuffer2, backbuffer1);
+    ok(hr == DDERR_CANNOTATTACHSURFACE, "got %#lx.\n", hr);
+
+    hr = IDirectDrawSurface_DeleteAttachedSurface(backbuffer1, 0, backbuffer2);
+    ok(hr == DD_OK, "got %#lx.\n", hr);
+    check_surface_caps(surface1, DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER
+            | DDSCAPS_FLIP, placement, 2);
+    check_surface_caps(backbuffer1, DDSCAPS_BACKBUFFER | DDSCAPS_FLIP | DDSCAPS_COMPLEX, placement, 0);
+    check_surface_caps(backbuffer2, DDSCAPS_3DDEVICE, placement, 0);
+
+    ref = IDirectDrawSurface_Release(backbuffer1);
+    todo_wine ok(ref == 2, "got %ld.\n", ref);
+    if (ref == 2)
+        ref = IDirectDrawSurface_Release(backbuffer1);
+    ref = IDirectDrawSurface_Release(surface1);
+    ok(!ref, "got %ld.\n", ref);
+    /* backbuffer1 is auto-generated attachemnt for surface1 and is supposed to be released during surface1 destruction. */
+    ref = IDirectDrawSurface_Release(backbuffer2);
+    ok(!ref, "got %ld.\n", ref);
+    ref = IDirectDrawSurface_Release(surface2);
+    ok(!ref, "got %ld.\n", ref);
 
     /* Test depth surfaces of different sizes. */
     memset(&surface_desc, 0, sizeof(surface_desc));
@@ -6087,6 +6589,10 @@ static void test_surface_attachment(void)
     ok(SUCCEEDED(hr), "Failed to attach surface, hr %#lx.\n", hr);
     refcount = get_refcount((IUnknown *)surface2);
     ok(refcount == 2, "Got unexpected refcount %lu.\n", refcount);
+    caps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    hr = IDirectDrawSurface_GetAttachedSurface(surface2, &caps, &tmp);
+    ok(hr == DDERR_NOTFOUND, "got %#lx.\n", hr);
+
     hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2);
     ok(hr == DDERR_SURFACEALREADYATTACHED, "Got unexpected hr %#lx.\n", hr);
 
diff --git a/dlls/ddraw/tests/dsurface.c b/dlls/ddraw/tests/dsurface.c
index e65160034f6e..e466162b3b33 100644
--- a/dlls/ddraw/tests/dsurface.c
+++ b/dlls/ddraw/tests/dsurface.c
@@ -29,8 +29,6 @@
 #include "d3d.h"
 #include "unknwn.h"
 
-static HRESULT (WINAPI *pDirectDrawCreateEx)(GUID *, void **, REFIID, IUnknown *);
-
 static IDirectDraw *lpDD;
 static DDCAPS ddcaps;
 
@@ -1669,124 +1667,6 @@ static void BackBufferCreateSurfaceTest(void)
     IDirectDraw7_Release(dd7);
 }
 
-static void BackBufferAttachmentFlipTest(void)
-{
-    HRESULT hr;
-    IDirectDrawSurface *surface1, *surface2, *surface3, *surface4;
-    DDSURFACEDESC ddsd;
-    HWND window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
-            100, 100, 160, 160, NULL, NULL, NULL, NULL);
-
-    hr = IDirectDraw_SetCooperativeLevel(lpDD, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    /* Perform attachment tests on a back-buffer */
-    memset(&ddsd, 0, sizeof(ddsd));
-    ddsd.dwSize = sizeof(ddsd);
-    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
-    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
-    ddsd.dwWidth = GetSystemMetrics(SM_CXSCREEN);
-    ddsd.dwHeight = GetSystemMetrics(SM_CYSCREEN);
-    hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface2, NULL);
-    ok(SUCCEEDED(hr), "Got hr %#lx.\n", hr);
-
-    if (surface2 != NULL)
-    {
-        /* Try a single primary and a two back buffers */
-        memset(&ddsd, 0, sizeof(ddsd));
-        ddsd.dwSize = sizeof(ddsd);
-        ddsd.dwFlags = DDSD_CAPS;
-        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
-        hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface1, NULL);
-        ok(hr==DD_OK, "Got hr %#lx.\n", hr);
-
-        memset(&ddsd, 0, sizeof(ddsd));
-        ddsd.dwSize = sizeof(ddsd);
-        ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
-        ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
-        ddsd.dwWidth = GetSystemMetrics(SM_CXSCREEN);
-        ddsd.dwHeight = GetSystemMetrics(SM_CYSCREEN);
-        hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface3, NULL);
-        ok(hr==DD_OK, "Got hr %#lx.\n", hr);
-
-        /* This one has a different size */
-        memset(&ddsd, 0, sizeof(ddsd));
-        ddsd.dwSize = sizeof(ddsd);
-        ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
-        ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
-        ddsd.dwWidth = 128;
-        ddsd.dwHeight = 128;
-        hr = IDirectDraw_CreateSurface(lpDD, &ddsd, &surface4, NULL);
-        ok(hr==DD_OK, "Got hr %#lx.\n", hr);
-
-        hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface2);
-        todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE),
-           "Attaching a back buffer to a front buffer returned %#lx\n", hr);
-        if(SUCCEEDED(hr))
-        {
-            /* Try flipping the surfaces */
-            hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT);
-            todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-            hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT);
-            todo_wine ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr);
-
-            /* Try the reverse without detaching first */
-            hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1);
-            ok(hr == DDERR_SURFACEALREADYATTACHED, "Got hr %#lx.\n", hr);
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2);
-            ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-        }
-        hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface1);
-        todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE),
-           "Attaching a front buffer to a back buffer returned %#lx\n", hr);
-        if(SUCCEEDED(hr))
-        {
-            /* Try flipping the surfaces */
-            hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT);
-            todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-            hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT);
-            todo_wine ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr);
-
-            /* Try to detach reversed */
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface1, 0, surface2);
-            ok(hr == DDERR_CANNOTDETACHSURFACE, "Got hr %#lx.\n", hr);
-            /* Now the proper detach */
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface1);
-            ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-        }
-        hr = IDirectDrawSurface_AddAttachedSurface(surface2, surface3);
-        todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE),
-           "Attaching a back buffer to another back buffer returned %#lx\n", hr);
-        if(SUCCEEDED(hr))
-        {
-            /* Try flipping the surfaces */
-            hr = IDirectDrawSurface_Flip(surface3, NULL, DDFLIP_WAIT);
-            todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-            hr = IDirectDrawSurface_Flip(surface2, NULL, DDFLIP_WAIT);
-            todo_wine ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr);
-            hr = IDirectDrawSurface_Flip(surface1, NULL, DDFLIP_WAIT);
-            ok(hr == DDERR_NOTFLIPPABLE, "Got hr %#lx.\n", hr);
-
-            hr = IDirectDrawSurface_DeleteAttachedSurface(surface2, 0, surface3);
-            ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-        }
-        hr = IDirectDrawSurface_AddAttachedSurface(surface1, surface4);
-        ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr);
-        hr = IDirectDrawSurface_AddAttachedSurface(surface4, surface1);
-        ok(hr == DDERR_CANNOTATTACHSURFACE, "Got hr %#lx.\n", hr);
-
-        IDirectDrawSurface_Release(surface4);
-        IDirectDrawSurface_Release(surface3);
-        IDirectDrawSurface_Release(surface2);
-        IDirectDrawSurface_Release(surface1);
-    }
-
-    hr =IDirectDraw_SetCooperativeLevel(lpDD, NULL, DDSCL_NORMAL);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    DestroyWindow(window);
-}
-
 static void CreateSurfaceBadCapsSizeTest(void)
 {
     DDSURFACEDESC ddsd_ok;
@@ -2466,9 +2346,6 @@ START_TEST(dsurface)
     HRESULT ret;
     IDirectDraw4 *dd4;
 
-    HMODULE ddraw_mod = GetModuleHandleA("ddraw.dll");
-    pDirectDrawCreateEx = (void *) GetProcAddress(ddraw_mod, "DirectDrawCreateEx");
-
     if (!CreateDirectDraw())
         return;
 
@@ -2506,7 +2383,6 @@ START_TEST(dsurface)
     PaletteTest();
     SurfaceCapsTest();
     BackBufferCreateSurfaceTest();
-    BackBufferAttachmentFlipTest();
     CreateSurfaceBadCapsSizeTest();
     no_ddsd_caps_test();
     zbufferbitdepth_test();
diff --git a/dlls/ddraw/tests/visual.c b/dlls/ddraw/tests/visual.c
index f5c6e79f832b..08cac331d184 100644
--- a/dlls/ddraw/tests/visual.c
+++ b/dlls/ddraw/tests/visual.c
@@ -1154,25 +1154,6 @@ static void D3D3_ViewportClearTest(void)
     if(window) DestroyWindow(window);
 }
 
-static COLORREF getPixelColor_GDI(IDirectDrawSurface *Surface, UINT x, UINT y)
-{
-    COLORREF clr = CLR_INVALID;
-    HDC hdc;
-    HRESULT hr;
-
-    hr = IDirectDrawSurface_GetDC(Surface, &hdc);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    if (SUCCEEDED(hr)) {
-        clr = GetPixel(hdc, x, y);
-
-        hr = IDirectDrawSurface_ReleaseDC(Surface, hdc);
-        ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-    }
-
-    return clr;
-}
-
 static void cubemap_test(IDirect3DDevice7 *device)
 {
     IDirect3D7 *d3d;
@@ -1476,126 +1457,6 @@ static void depth_clamp_test(IDirect3DDevice7 *device)
     ok(SUCCEEDED(hr), "Got hr %#lx.\n", hr);
 }
 
-static void DX1_BackBufferFlipTest(void)
-{
-    HRESULT hr;
-    IDirectDraw *DirectDraw1 = NULL;
-    IDirectDrawSurface *Primary = NULL;
-    IDirectDrawSurface *Backbuffer = NULL;
-    WNDCLASSA wc = {0};
-    DDSURFACEDESC ddsd;
-    DDBLTFX ddbltfx;
-    COLORREF color;
-    const DWORD white = 0xffffff;
-    const DWORD red = 0xff0000;
-    BOOL attached = FALSE;
-
-    wc.lpfnWndProc = DefWindowProcA;
-    wc.lpszClassName = "DX1_BackBufferFlipTest_wc";
-    RegisterClassA(&wc);
-    window = CreateWindowA("DX1_BackBufferFlipTest_wc", "DX1_BackBufferFlipTest",
-            WS_MAXIMIZE | WS_VISIBLE | WS_CAPTION, 0, 0, 640, 480, 0, 0, 0, 0);
-
-    hr = DirectDrawCreate( NULL, &DirectDraw1, NULL );
-    ok(hr == DD_OK || hr == DDERR_NODIRECTDRAWSUPPORT, "Got hr %#lx.\n", hr);
-    if(FAILED(hr)) goto out;
-
-    hr = IDirectDraw_SetCooperativeLevel(DirectDraw1, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-    if(FAILED(hr)) goto out;
-
-    hr = IDirectDraw_SetDisplayMode(DirectDraw1, 640, 480, 32);
-    if(FAILED(hr)) {
-        /* 24 bit is fine too */
-        hr = IDirectDraw_SetDisplayMode(DirectDraw1, 640, 480, 24);
-    }
-    ok(hr == DD_OK || hr == DDERR_UNSUPPORTED, "Got hr %#lx.\n", hr);
-    if (FAILED(hr)) {
-        goto out;
-    }
-
-    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
-    ddsd.dwSize = sizeof(DDSURFACEDESC);
-    ddsd.dwFlags = DDSD_CAPS;
-    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
-
-    hr = IDirectDraw_CreateSurface(DirectDraw1, &ddsd, &Primary, NULL);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
-    ddsd.dwSize = sizeof(DDSURFACEDESC);
-    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
-    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
-    ddsd.dwWidth = 640;
-    ddsd.dwHeight = 480;
-    ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
-    ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
-    ddsd.ddpfPixelFormat.dwRGBBitCount      = 32;
-    ddsd.ddpfPixelFormat.dwRBitMask         = 0x00ff0000;
-    ddsd.ddpfPixelFormat.dwGBitMask         = 0x0000ff00;
-    ddsd.ddpfPixelFormat.dwBBitMask         = 0x000000ff;
-
-    hr = IDirectDraw_CreateSurface(DirectDraw1, &ddsd, &Backbuffer, NULL);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-    if(FAILED(hr)) goto out;
-
-    hr = IDirectDrawSurface_AddAttachedSurface(Primary, Backbuffer);
-    todo_wine ok(hr == DD_OK || broken(hr == DDERR_CANNOTATTACHSURFACE), "Got hr %#lx.\n", hr);
-    if (FAILED(hr)) goto out;
-
-    attached = TRUE;
-
-    memset(&ddbltfx, 0, sizeof(ddbltfx));
-    ddbltfx.dwSize = sizeof(ddbltfx);
-    ddbltfx.dwFillColor = red;
-    hr = IDirectDrawSurface_Blt(Backbuffer, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    ddbltfx.dwFillColor = white;
-    hr = IDirectDrawSurface_Blt(Primary, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
-    ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    /* Check it out */
-    color = getPixelColor_GDI(Primary, 5, 5);
-    ok(GetRValue(color) == 0xFF && GetGValue(color) == 0xFF && GetBValue(color) == 0xFF,
-            "got R %02X G %02X B %02X, expected R FF G FF B FF\n",
-            GetRValue(color), GetGValue(color), GetBValue(color));
-
-    color = getPixelColor_GDI(Backbuffer, 5, 5);
-    ok(GetRValue(color) == 0xFF && GetGValue(color) == 0 && GetBValue(color) == 0,
-            "got R %02X G %02X B %02X, expected R FF G 00 B 00\n",
-            GetRValue(color), GetGValue(color), GetBValue(color));
-
-    hr = IDirectDrawSurface_Flip(Primary, NULL, DDFLIP_WAIT);
-    todo_wine ok(hr == DD_OK, "Got hr %#lx.\n", hr);
-
-    if (hr == DD_OK)
-    {
-        color = getPixelColor_GDI(Primary, 5, 5);
-        ok(GetRValue(color) == 0xFF && GetGValue(color) == 0 && GetBValue(color) == 0,
-                "got R %02X G %02X B %02X, expected R FF G 00 B 00\n",
-                GetRValue(color), GetGValue(color), GetBValue(color));
-
-        color = getPixelColor_GDI(Backbuffer, 5, 5);
-        ok((GetRValue(color) == 0xFF && GetGValue(color) == 0xFF && GetBValue(color) == 0xFF) ||
-           broken(GetRValue(color) == 0xFF && GetGValue(color) == 0 && GetBValue(color) == 0),  /* broken driver */
-                "got R %02X G %02X B %02X, expected R FF G FF B FF\n",
-                GetRValue(color), GetGValue(color), GetBValue(color));
-    }
-
-    out:
-
-    if (Backbuffer)
-    {
-        if (attached)
-            IDirectDrawSurface_DeleteAttachedSurface(Primary, 0, Backbuffer);
-        IDirectDrawSurface_Release(Backbuffer);
-    }
-    if (Primary) IDirectDrawSurface_Release(Primary);
-    if (DirectDraw1) IDirectDraw_Release(DirectDraw1);
-    if (window) DestroyWindow(window);
-}
-
 START_TEST(visual)
 {
     unsigned int color;
@@ -1647,7 +1508,6 @@ START_TEST(visual)
     releaseObjects(); /* release DX7 interfaces to test D3D1 */
 
     D3D3_ViewportClearTest();
-    DX1_BackBufferFlipTest();
 
     return ;
 
diff --git a/dlls/directmanipulation/Makefile.in b/dlls/directmanipulation/Makefile.in
index af884c3842b7..144e893852d3 100644
--- a/dlls/directmanipulation/Makefile.in
+++ b/dlls/directmanipulation/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = directmanipulation.dll
 IMPORTS   = uuid
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	directmanip.idl \
 	directmanipulation.c
diff --git a/dlls/dsound/capture.c b/dlls/dsound/capture.c
index dfb5e7d921c2..2112f5dfe869 100644
--- a/dlls/dsound/capture.c
+++ b/dlls/dsound/capture.c
@@ -451,19 +451,21 @@ static HRESULT WINAPI IDirectSoundCaptureBufferImpl_Lock(IDirectSoundCaptureBuff
     EnterCriticalSection(&(This->device->lock));
 
     if (This->device->client) {
-        *lplpvAudioPtr1 = This->device->buffer + dwReadCusor;
-        if ( (dwReadCusor + dwReadBytes) > This->device->buflen) {
-            *lpdwAudioBytes1 = This->device->buflen - dwReadCusor;
-	    if (lplpvAudioPtr2)
-            	*lplpvAudioPtr2 = This->device->buffer;
-	    if (lpdwAudioBytes2)
-		*lpdwAudioBytes2 = dwReadBytes - *lpdwAudioBytes1;
+        if ( dwReadCusor >= This->device->buflen || !dwReadBytes || dwReadBytes > This->device->buflen) {
+            *lpdwAudioBytes1 = 0;
+            *lplpvAudioPtr1 = NULL;
+            if (lplpvAudioPtr2)
+                *lplpvAudioPtr2 = NULL;
+            if (lpdwAudioBytes2)
+                *lpdwAudioBytes2 = 0;
+            hres = DSERR_INVALIDPARAM;
         } else {
-            *lpdwAudioBytes1 = dwReadBytes;
-	    if (lplpvAudioPtr2)
-            	*lplpvAudioPtr2 = 0;
-	    if (lpdwAudioBytes2)
-            	*lpdwAudioBytes2 = 0;
+            *lplpvAudioPtr1 = This->device->buffer + dwReadCusor;
+            *lpdwAudioBytes1 = min(dwReadBytes, This->device->buflen - dwReadCusor);
+            if (lpdwAudioBytes2)
+                *lpdwAudioBytes2 = dwReadBytes - *lpdwAudioBytes1;
+            if (lplpvAudioPtr2)
+                *lplpvAudioPtr2 = dwReadBytes - *lpdwAudioBytes1 ? This->device->buffer : NULL;
         }
     } else {
         TRACE("invalid call\n");
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 244d88f7538b..7e6768a21a8e 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -145,8 +145,8 @@ struct IDirectSoundBufferImpl
     DSBUFFERDESC                dsbd;
     /* used for frequency conversion (PerfectPitch) */
     float                       firgain;
-    LONG64                      freqAdjustNum,freqAdjustDen;
-    LONG64                      freqAccNum;
+    DWORD                       freqAdjustNum,freqAdjustDen;
+    DWORD                       freqAccNum;
     /* used for mixing */
     DWORD                       sec_mixpos;
     /* Holds a copy of the next 'writelead' bytes, to be used for mixing. This makes it
diff --git a/dlls/dsound/fir.h b/dlls/dsound/fir.h
index 45ad65d73981..39a32af1412e 100644
--- a/dlls/dsound/fir.h
+++ b/dlls/dsound/fir.h
@@ -86,11 +86,11 @@ int main()
     fprintf(stderr, "q %f\n", (double)output.q);
     fprintf(stderr, "status %s\n", get_pm_status_str(output.status));
 
-    printf("static const int fir_width_shift = %d;\n", fir_width_shift);
-    printf("static const int fir_width = %d;\n", fir_width);
-    printf("static const int fir_step_shift = %d;\n", fir_step_shift);
-    printf("static const int fir_step = %d;\n", fir_step);
-    printf("static const float fir[] = {");
+    printf("#define FIR_WIDTH_SHIFT %d\n", fir_width_shift);
+    printf("#define FIR_WIDTH %d\n", fir_width);
+    printf("#define FIR_STEP_SHIFT %d\n", fir_step_shift);
+    printf("#define FIR_STEP %d\n", fir_step);
+    printf("static const float DECLSPEC_ALIGN(16) fir[] = {");
     // Print the FIR array with an additional row at the end. This simplifies
     // calculation of the interpolated value by allowing the index to overflow
     // into the extra row. It just repeats the first row, starting from its
@@ -114,11 +114,11 @@ int main()
     printf("};\n");
 }
 */
-static const int fir_width_shift = 6;
-static const int fir_width = 64;
-static const int fir_step_shift = 7;
-static const int fir_step = 128;
-static const float fir[] = {
+#define FIR_WIDTH_SHIFT 6
+#define FIR_WIDTH 64
+#define FIR_STEP_SHIFT 7
+#define FIR_STEP 128
+static const float DECLSPEC_ALIGN(16) fir[] = {
      0.0000000000e+00, -2.4830013102e-06,  1.9318705150e-06,  2.6614854151e-06,
     -1.5313785194e-05,  4.2076214553e-05, -9.1417167945e-05,  1.7455895136e-04,
     -3.0567859821e-04,  5.0191365396e-04, -7.8311909082e-04,  1.1713337628e-03,
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 1be5b5a23858..3b7715e0c69c 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -25,6 +25,9 @@
 #include <assert.h>
 #include <stdarg.h>
 #include <math.h>	/* Insomnia - pow() function */
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
 
 #define COBJMACROS
 
@@ -106,7 +109,7 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
 {
 	DWORD ichannels = dsb->pwfx->nChannels;
 	DWORD ochannels = dsb->device->pwfx->nChannels;
-	LONG64 oldFreqAdjustDen = dsb->freqAdjustDen;
+	DWORD oldFreqAdjustDen = dsb->freqAdjustDen;
 	WAVEFORMATEXTENSIBLE *pwfxe;
 	BOOL ieee = FALSE;
 
@@ -131,7 +134,8 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
 	dsb->maxwritelead = (DSBFREQUENCY_MAX / 100) * dsb->pwfx->nBlockAlign;
 
 	if (oldFreqAdjustDen)
-		dsb->freqAccNum = (dsb->freqAccNum * dsb->freqAdjustDen + oldFreqAdjustDen / 2) / oldFreqAdjustDen;
+		dsb->freqAccNum = (dsb->freqAccNum * (LONG64)dsb->freqAdjustDen +
+				oldFreqAdjustDen / 2) / oldFreqAdjustDen;
 
 	dsb->get_aux = ieee ? getbpp[4] : getbpp[dsb->pwfx->wBitsPerSample/8 - 1];
 	dsb->put_aux = putieee32;
@@ -283,36 +287,52 @@ static inline float get_current_sample(const IDirectSoundBufferImpl *dsb,
     return dsb->get(dsb, buffer + (mixpos % buflen), channel);
 }
 
-static UINT cp_fields_noresample(IDirectSoundBufferImpl *dsb, UINT count)
+#ifdef __SSE__
+
+/**
+ * Note that this function will overwrite up to FIR_WIDTH - 1 frames before and
+ * after output[].
+ */
+void downsample_sse(LONG64 opos_num, DWORD opos_num_step, float rem_float, float rem_step_float,
+        float firgain_float, UINT required_input, float *input, float *output)
 {
-    UINT istride = dsb->pwfx->nBlockAlign;
-    UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
-    UINT committed_samples = 0;
-    DWORD channel, i;
+    __m128 rem = _mm_set1_ps(rem_float);
+    __m128 rem_step = _mm_set1_ps(rem_step_float);
+    __m128 firgain = _mm_set_ss(firgain_float);
+    __m128 one = _mm_set1_ps(1.0f);
+    int j;
 
-    if (!secondarybuffer_is_audible(dsb))
-        return count;
+    for (j = 0; j < required_input; ++j) {
+        /* opos is in the range [-(fir_width - 1), count) */
+        int opos = (int)(opos_num >> FREQ_ADJUST_SHIFT) - FIR_WIDTH;
+        UINT idx = ~(DWORD)opos_num >> (FREQ_ADJUST_SHIFT - FIR_STEP_SHIFT) << FIR_WIDTH_SHIFT;
+        __m128 rem_inv = _mm_sub_ps(one, rem);
 
-    if(dsb->use_committed) {
-        committed_samples = (dsb->writelead - dsb->committed_mixpos) / istride;
-        committed_samples = committed_samples <= count ? committed_samples : count;
-    }
+        __m128 input_value_ss = _mm_mul_ss(_mm_load_ss(&input[j]), firgain);
+        __m128 input_value = _mm_shuffle_ps(input_value_ss, input_value_ss, 0);
+        __m128 input_value0 = _mm_mul_ps(rem_inv, input_value);
+        __m128 input_value1 = _mm_mul_ps(rem, input_value);
 
-    for (i = 0; i < committed_samples; i++)
-        for (channel = 0; channel < dsb->mix_channels; channel++)
-            dsb->put(dsb, i * ostride, channel, get_current_sample(dsb, dsb->committedbuff,
-                dsb->writelead, dsb->committed_mixpos + i * istride, channel));
+        int i;
+        C_ASSERT(!(FIR_WIDTH % 4));
+        for (i = 0; i < FIR_WIDTH; i += 4) {
+            __m128 value0 = _mm_mul_ps(_mm_load_ps(&fir[idx + i]), input_value0);
+            __m128 value1 = _mm_mul_ps(_mm_load_ps(&fir[idx + FIR_WIDTH + i]), input_value1);
+            __m128 value = _mm_add_ps(value0, value1);
+            _mm_storeu_ps(&output[opos + i], _mm_add_ps(_mm_loadu_ps(&output[opos + i]), value));
+        }
 
-    for (; i < count; i++)
-        for (channel = 0; channel < dsb->mix_channels; channel++)
-            dsb->put(dsb, i * ostride, channel, get_current_sample(dsb, dsb->buffer->memory,
-                dsb->buflen, dsb->sec_mixpos + i * istride, channel));
+        rem = _mm_add_ps(rem, rem_step);
+        rem = _mm_sub_ps(rem, _mm_and_ps(one, _mm_cmple_ps(one, rem)));
 
-    return count;
+        opos_num += opos_num_step;
+    }
 }
 
+#endif
+
 /**
- * Note that this function will overwrite up to fir_width - 1 frames before and
+ * Note that this function will overwrite up to FIR_WIDTH - 1 frames before and
  * after output[].
  */
 static void downsample(DWORD freq_adjust_den, DWORD freq_acc_start, float firgain,
@@ -336,36 +356,84 @@ static void downsample(DWORD freq_adjust_den, DWORD freq_acc_start, float firgai
      * Clearing the bits is safe as it has the same effect as rounding up the
      * resampling ratio and the subsample position and doesn't affect the
      * initial opos value. */
-    LONG64 opos_num_mask = ~0ull << (FREQ_ADJUST_SHIFT - 23 - fir_step_shift);
+    LONG64 opos_num_mask = ~0ull << (FREQ_ADJUST_SHIFT - 23 - FIR_STEP_SHIFT);
     LONG64 opos_num = (freq_adjust_den - freq_acc_start + (1ll << FREQ_ADJUST_SHIFT) - 1) & opos_num_mask;
     DWORD opos_num_step = freq_adjust_den & (DWORD)opos_num_mask;
 
     /* Use XOR to invert the lower part of opos_num so that the lower bits
      * remain cleared. */
-    float rem = FIXED_0_32_TO_FLOAT(((DWORD)opos_num ^ (DWORD)opos_num_mask) << fir_step_shift);
-    float rem_step = FIXED_0_32_TO_FLOAT(-opos_num_step << fir_step_shift);
-    int j;
+    float rem = FIXED_0_32_TO_FLOAT(((DWORD)opos_num ^ (DWORD)opos_num_mask) << FIR_STEP_SHIFT);
+    float rem_step = FIXED_0_32_TO_FLOAT(-opos_num_step << FIR_STEP_SHIFT);
 
+#ifdef __SSE__
+    downsample_sse(opos_num, opos_num_step, rem, rem_step, firgain, required_input, input, output);
+#else
+    int j;
     for (j = 0; j < required_input; ++j) {
         /* opos is in the range [-(fir_width - 1), count) */
-        int opos = (int)(opos_num >> FREQ_ADJUST_SHIFT) - fir_width;
-        UINT idx = ~(DWORD)opos_num >> (FREQ_ADJUST_SHIFT - fir_step_shift) << fir_width_shift;
+        int opos = (int)(opos_num >> FREQ_ADJUST_SHIFT) - FIR_WIDTH;
+        UINT idx = ~(DWORD)opos_num >> (FREQ_ADJUST_SHIFT - FIR_STEP_SHIFT) << FIR_WIDTH_SHIFT;
 
         float input_value = input[j] * firgain;
         float input_value0 = (1.0f - rem) * input_value;
         float input_value1 = rem * input_value;
 
         int i;
-        for (i = 0; i < fir_width; ++i)
-            output[opos + i] += fir[idx + i] * input_value0 + fir[idx + fir_width + i] * input_value1;
+        for (i = 0; i < FIR_WIDTH; ++i)
+            output[opos + i] += fir[idx + i] * input_value0 + fir[idx + FIR_WIDTH + i] * input_value1;
 
         rem += rem_step;
         rem -= rem >= 1.0f ? 1.0f : 0.0f;
 
         opos_num += opos_num_step;
     }
+#endif
 }
 
+#ifdef __SSE__
+
+void upsample_sse(LONG64 ipos_num, DWORD ipos_num_step, float rem_inv_float,
+        float rem_inv_step_float, UINT count, float *input, float *output)
+{
+    __m128 rem_inv = _mm_set1_ps(rem_inv_float);
+    __m128 rem_inv_step = _mm_set1_ps(rem_inv_step_float);
+    __m128 one = _mm_set1_ps(1.0f);
+
+    UINT i;
+
+    for(i = 0; i < count; ++i) {
+        UINT ipos = ipos_num >> FREQ_ADJUST_SHIFT;
+        UINT idx = ~(DWORD)ipos_num >> (FREQ_ADJUST_SHIFT - FIR_STEP_SHIFT) << FIR_WIDTH_SHIFT;
+        __m128 rem = _mm_sub_ps(one, rem_inv);
+
+        int j;
+        __m128 sum = _mm_set1_ps(0.0f);
+        float* cache = &input[ipos];
+
+        C_ASSERT(!(FIR_WIDTH % 4));
+        for (j = 0; j < FIR_WIDTH; j += 4) {
+            __m128 fir_value0 = _mm_mul_ps(_mm_load_ps(&fir[idx + j]), rem_inv);
+            __m128 fir_value1 = _mm_mul_ps(_mm_load_ps(&fir[idx + j + FIR_WIDTH]), rem);
+            __m128 fir_value = _mm_add_ps(fir_value0, fir_value1);
+            __m128 input_value = _mm_loadu_ps(&cache[j]);
+            sum = _mm_add_ps(sum, _mm_mul_ps(fir_value, input_value));
+        }
+
+        /* Add the even-numbered sums to the odd-numbered ones. */
+        sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(0, 3, 0, 1)));
+        /* Calculate the final sum and store it to the output array. */
+        sum = _mm_add_ss(sum, _mm_movehl_ps(sum, sum));
+        _mm_store_ss(&output[i], sum);
+
+        rem_inv = _mm_add_ps(rem_inv, rem_inv_step);
+        rem_inv = _mm_sub_ps(rem_inv, _mm_and_ps(one, _mm_cmple_ps(one, rem_inv)));
+
+        ipos_num += ipos_num_step;
+    }
+}
+
+#endif
+
 static void upsample(DWORD freq_adjust_num, DWORD freq_acc_start, UINT count, float *input,
         float *output)
 {
@@ -387,25 +455,28 @@ static void upsample(DWORD freq_adjust_num, DWORD freq_acc_start, UINT count, fl
      *
      * Clearing the bits is safe as it has the same effect as rounding down the
      * resampling ratio and the subsample position. */
-    DWORD ipos_num_mask = ~0u << (FREQ_ADJUST_SHIFT - 23 - fir_step_shift);
+    DWORD ipos_num_mask = ~0u << (FREQ_ADJUST_SHIFT - 23 - FIR_STEP_SHIFT);
     LONG64 ipos_num = freq_acc_start & ipos_num_mask;
     DWORD ipos_num_step = freq_adjust_num & ipos_num_mask;
 
-    float rem_inv = FIXED_0_32_TO_FLOAT((DWORD)ipos_num << fir_step_shift);
-    float rem_inv_step = FIXED_0_32_TO_FLOAT(ipos_num_step << fir_step_shift);
-    UINT i;
+    float rem_inv = FIXED_0_32_TO_FLOAT((DWORD)ipos_num << FIR_STEP_SHIFT);
+    float rem_inv_step = FIXED_0_32_TO_FLOAT(ipos_num_step << FIR_STEP_SHIFT);
 
+#ifdef __SSE__
+    upsample_sse(ipos_num, ipos_num_step, rem_inv, rem_inv_step, count, input, output);
+#else
+    UINT i;
     for(i = 0; i < count; ++i) {
         UINT ipos = ipos_num >> FREQ_ADJUST_SHIFT;
-        UINT idx = ~(DWORD)ipos_num >> (FREQ_ADJUST_SHIFT - fir_step_shift) << fir_width_shift;
+        UINT idx = ~(DWORD)ipos_num >> (FREQ_ADJUST_SHIFT - FIR_STEP_SHIFT) << FIR_WIDTH_SHIFT;
         float rem = 1.0f - rem_inv;
 
         int j;
         float sum = 0.0;
         float* cache = &input[ipos];
 
-        for (j = 0; j < fir_width; j++)
-            sum += (fir[idx + j] * rem_inv + fir[idx + j + fir_width] * rem) * cache[j];
+        for (j = 0; j < FIR_WIDTH; j++)
+            sum += (fir[idx + j] * rem_inv + fir[idx + j + FIR_WIDTH] * rem) * cache[j];
         output[i] = sum;
 
         rem_inv += rem_inv_step;
@@ -413,23 +484,25 @@ static void upsample(DWORD freq_adjust_num, DWORD freq_acc_start, UINT count, fl
 
         ipos_num += ipos_num_step;
     }
+#endif
 }
 
 /**
- * Note that this function will overwrite up to fir_width - 1 frames before and
+ * Note that this function will overwrite up to FIR_WIDTH - 1 frames before and
  * after output[].
  */
-static void resample(LONG64 freq_adjust_num, LONG64 freq_adjust_den, LONG64 freq_acc_start,
+static void resample(DWORD freq_adjust_num, DWORD freq_adjust_den, DWORD freq_acc_start,
         float firgain, UINT required_input, UINT count, float *input, float *output)
 {
     if (freq_adjust_num > freq_adjust_den) {
         /* Take a reciprocal of the resampling ratio and convert it to a 0.32
          * fixed point. Round down to prevent output buffer overflow. */
-        DWORD freq_adjust_fixed_den = (freq_adjust_den << FREQ_ADJUST_SHIFT) / freq_adjust_num;
+        DWORD freq_adjust_fixed_den = ((LONG64)freq_adjust_den << FREQ_ADJUST_SHIFT)
+                / freq_adjust_num;
         /* Convert the subsample position to a 0.32 fixed point. Round up to
          * prevent output buffer overflow. */
-        DWORD freq_acc_fixed_start = (freq_acc_start * freq_adjust_fixed_den + freq_adjust_den - 1)
-                / freq_adjust_den;
+        DWORD freq_acc_fixed_start = ((LONG64)freq_acc_start * freq_adjust_fixed_den
+                + freq_adjust_den - 1) / freq_adjust_den;
 
         memset(output, 0, count * sizeof(float));
         downsample(freq_adjust_fixed_den, freq_acc_fixed_start, firgain, required_input, input,
@@ -437,16 +510,18 @@ static void resample(LONG64 freq_adjust_num, LONG64 freq_adjust_den, LONG64 freq
     } else {
         /* Convert the resampling ratio to a 0.32 fixed point. Round down to
          * prevent input buffer overflow. */
-        DWORD freq_adjust_fixed_num = (freq_adjust_num << FREQ_ADJUST_SHIFT) / freq_adjust_den;
+        DWORD freq_adjust_fixed_num = ((LONG64)freq_adjust_num << FREQ_ADJUST_SHIFT)
+                / freq_adjust_den;
         /* Convert the subsample position to a 0.32 fixed point. Round down to
          * prevent input buffer overflow. */
-        DWORD freq_acc_fixed_start = (freq_acc_start << FREQ_ADJUST_SHIFT) / freq_adjust_den;
+        DWORD freq_acc_fixed_start = ((LONG64)freq_acc_start << FREQ_ADJUST_SHIFT)
+                / freq_adjust_den;
 
         upsample(freq_adjust_fixed_num, freq_acc_fixed_start, count, input, output);
     }
 }
 
-static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *freqAccNum)
+static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, DWORD *freqAccNum)
 {
     UINT i, channel;
     UINT istride = dsb->pwfx->nBlockAlign;
@@ -459,15 +534,15 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *
     UINT max_ipos = (freqAcc_start + count * dsb->freqAdjustNum) / dsb->freqAdjustDen;
 
     UINT required_input = max(
-            (freqAcc_start + (count - 1) * dsb->freqAdjustNum) / dsb->freqAdjustDen + fir_width,
-            (freqAcc_start + (count - 1 + fir_width) * dsb->freqAdjustNum) / dsb->freqAdjustDen);
+            (freqAcc_start + (count - 1) * dsb->freqAdjustNum) / dsb->freqAdjustDen + FIR_WIDTH,
+            (freqAcc_start + (count - 1 + FIR_WIDTH) * dsb->freqAdjustNum) / dsb->freqAdjustDen);
     float *intermediate, *output, *itmp;
 
     DWORD len = required_input * channels;
     /* Allocate an output buffer for each channel with padding on both ends as
      * required by the resample function. Padding at the end of one channel
      * buffer is reused as a start padding for the next channel buffer. */
-    len += fir_width - 1 + (count + fir_width - 1) * channels;
+    len += FIR_WIDTH - 1 + (count + FIR_WIDTH - 1) * channels;
     len *= sizeof(float);
 
     *freqAccNum = freqAcc_end % dsb->freqAdjustDen;
@@ -484,7 +559,7 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *
     }
 
     intermediate = dsb->device->cp_buffer;
-    output = intermediate + required_input * channels + fir_width - 1;
+    output = intermediate + required_input * channels + FIR_WIDTH - 1;
 
     if(dsb->use_committed) {
         committed_samples = (dsb->writelead - dsb->committed_mixpos) / istride;
@@ -508,16 +583,44 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *
     for (channel = 0; channel < channels; channel++)
         resample(dsb->freqAdjustNum, dsb->freqAdjustDen, freqAcc_start, dsb->firgain,
                 required_input, count, intermediate + channel * required_input,
-                output + channel * (fir_width - 1 + count));
+                output + channel * (FIR_WIDTH - 1 + count));
 
     for(i = 0; i < count; ++i)
         for (channel = 0; channel < channels; channel++)
-            dsb->put(dsb, i * ostride, channel, output[channel * (fir_width - 1 + count) + i]);
+            dsb->put(dsb, i * ostride, channel, output[channel * (FIR_WIDTH - 1 + count) + i]);
 
     return max_ipos;
 }
 
-static void cp_fields(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *freqAccNum)
+static UINT cp_fields_noresample(IDirectSoundBufferImpl *dsb, UINT count)
+{
+    UINT istride = dsb->pwfx->nBlockAlign;
+    UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
+    UINT committed_samples = 0;
+    DWORD channel, i;
+
+    if (!secondarybuffer_is_audible(dsb))
+        return count;
+
+    if(dsb->use_committed) {
+        committed_samples = (dsb->writelead - dsb->committed_mixpos) / istride;
+        committed_samples = committed_samples <= count ? committed_samples : count;
+    }
+
+    for (i = 0; i < committed_samples; i++)
+        for (channel = 0; channel < dsb->mix_channels; channel++)
+            dsb->put(dsb, i * ostride, channel, get_current_sample(dsb, dsb->committedbuff,
+                dsb->writelead, dsb->committed_mixpos + i * istride, channel));
+
+    for (; i < count; i++)
+        for (channel = 0; channel < dsb->mix_channels; channel++)
+            dsb->put(dsb, i * ostride, channel, get_current_sample(dsb, dsb->buffer->memory,
+                dsb->buflen, dsb->sec_mixpos + i * istride, channel));
+
+    return count;
+}
+
+static void cp_fields(IDirectSoundBufferImpl *dsb, UINT count, DWORD *freqAccNum)
 {
     DWORD ipos, adv;
 
diff --git a/dlls/dsound/tests/capture.c b/dlls/dsound/tests/capture.c
index 63998276a77c..fc05b8ae7457 100644
--- a/dlls/dsound/tests/capture.c
+++ b/dlls/dsound/tests/capture.c
@@ -290,6 +290,59 @@ static BOOL capture_buffer_service(capture_state_t* state)
     if (rc!=DS_OK)
         return FALSE;
 
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,(DWORD)-32,0,&ptr1,&len1,&ptr2,&len2,0);
+    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_Lock() failed: %08lx\n", rc);
+    ok(!ptr1 && !ptr2 && !len1 && !len2, "got %p, %lu, %p, %lu.\n", ptr1, len1, ptr2, len2);
+
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,(DWORD)-32,64,&ptr1,&len1,&ptr2,&len2,0);
+    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_Lock() returned: %08lx\n", rc);
+    ok(!ptr1 && !ptr2 && !len1 && !len2, "got %p, %lu, %p, %lu.\n", ptr1, len1, ptr2, len2);
+
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,0,0,&ptr1,&len1,&ptr2,&len2,0);
+    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_Lock() returned: %08lx\n", rc);
+    ok(!ptr1 && !ptr2 && !len1 && !len2, "got %p, %lu, %p, %lu.\n", ptr1, len1, ptr2, len2);
+
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,0,0,&ptr1,&len1,NULL,NULL,0);
+    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_Lock() returned: %08lx\n", rc);
+    ok(!ptr1 && !len1, "got %p, %lu.\n", ptr1, len1);
+
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,state->buffer_size,1,&ptr1,&len1,&ptr2,&len2,0);
+    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_Lock() returned: %08lx\n", rc);
+    ok(!ptr1 && !ptr2 && !len1 && !len2, "got %p, %lu, %p, %lu.\n", ptr1, len1, ptr2, len2);
+
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,0,state->buffer_size + 1,&ptr1,&len1,&ptr2,&len2,0);
+    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_Lock() returned: %08lx\n", rc);
+    ok(!ptr1 && !ptr2 && !len1 && !len2, "got %p, %lu, %p, %lu.\n", ptr1, len1, ptr2, len2);
+
+    ptr1 = ptr2 = (void *)0xdeadbeef;
+    len1 = len2 = 0xdeadbeef;
+    rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,state->offset,state->buffer_size,
+                                      &ptr1,&len1,&ptr2,&len2,0);
+    ok(!rc,"IDirectSoundCaptureBuffer_Lock() returned: %08lx\n", rc);
+    if (state->offset)
+        ok(ptr1 == (char *)ptr2 + state->offset, "got %p, %p, buffer_size %ld, offset %ld.\n",
+           ptr1, ptr2, state->buffer_size, state->offset);
+    else
+        ok(!ptr2, "got %p.\n", ptr2);
+    ok(len1 == state->buffer_size - state->offset, "got %ld, buffer_size %ld, offset %ld.\n",
+       len1, state->buffer_size, state->offset);
+    ok(len2 == state->offset, "got %ld, buffer_size %ld, offset %ld.\n",
+       len2, state->buffer_size, state->offset);
+    rc=IDirectSoundCaptureBuffer_Unlock(state->dscbo,ptr1,len1,ptr2,len2);
+    ok(rc==DS_OK,"IDirectSoundCaptureBuffer_Unlock() returned: %08lx\n", rc);
+
     rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,state->offset,state->size,
                                       &ptr1,&len1,&ptr2,&len2,0);
     ok(rc==DS_OK,"IDirectSoundCaptureBuffer_Lock() failed: %08lx\n", rc);
diff --git a/dlls/dsound/tests/dsound.c b/dlls/dsound/tests/dsound.c
index 44b5136d7be1..ed5c1925e37c 100644
--- a/dlls/dsound/tests/dsound.c
+++ b/dlls/dsound/tests/dsound.c
@@ -291,6 +291,9 @@ static void IDirectSound_tests(void)
         IDirectSound_Release(dso);
 }
 
+#define AUDCLNT_ERR(n) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, n)
+#define AUDCLNT_E_DEVICE_IN_USE AUDCLNT_ERR(0x0a)
+
 static HRESULT test_dsound(LPGUID lpGuid)
 {
     HRESULT rc;
@@ -327,7 +330,11 @@ static HRESULT test_dsound(LPGUID lpGuid)
 
         /* Create a second DirectSound object */
         rc = DirectSoundCreate(lpGuid, &dso1, NULL);
-        ok(rc==DS_OK,"DirectSoundCreate() failed: %08lx\n",rc);
+        /* Running without pulseaudio can't open twice. */
+        if (rc==AUDCLNT_E_DEVICE_IN_USE)
+            skip("Failed to open device a second time, skipping test.\n");
+        else
+            ok(rc==DS_OK,"DirectSoundCreate() failed: %08lx\n",rc);
         if (rc==DS_OK) {
             /* Release the second DirectSound object */
             ref=IDirectSound_Release(dso1);
diff --git a/dlls/dsound/tests/dsound8.c b/dlls/dsound/tests/dsound8.c
index 07b10af45e0b..1b363b9ad7b4 100644
--- a/dlls/dsound/tests/dsound8.c
+++ b/dlls/dsound/tests/dsound8.c
@@ -311,7 +311,11 @@ static HRESULT test_dsound8(LPGUID lpGuid)
 
         /* Create a second DirectSound8 object */
         rc = DirectSoundCreate8(lpGuid, &dso1, NULL);
-        ok(rc==DS_OK,"DirectSoundCreate8() failed: %08lx\n",rc);
+        /* Running without pulseaudio can't open twice. */
+        if (rc==AUDCLNT_E_DEVICE_IN_USE)
+            skip("Failed to open device a second time, skipping test.\n");
+        else
+            ok(rc==DS_OK,"DirectSoundCreate8() failed: %08lx\n",rc);
         if (rc==DS_OK) {
             /* Release the second DirectSound8 object */
             ref=IDirectSound8_Release(dso1);
diff --git a/dlls/dssenh/Makefile.in b/dlls/dssenh/Makefile.in
index f888dfab5fad..19ec22b0d14f 100644
--- a/dlls/dssenh/Makefile.in
+++ b/dlls/dssenh/Makefile.in
@@ -1,5 +1,6 @@
 MODULE    = dssenh.dll
-IMPORTS   = bcrypt crypt32 advapi32
+IMPORTS   = $(SYMCRYPT_PE_LIBS) crypt32 advapi32
+EXTRAINCL = $(SYMCRYPT_PE_CFLAGS)
 
 SOURCES = \
 	main.c \
diff --git a/dlls/dssenh/main.c b/dlls/dssenh/main.c
index f637c894e702..3f72aefd70e8 100644
--- a/dlls/dssenh/main.c
+++ b/dlls/dssenh/main.c
@@ -24,22 +24,27 @@
 #include "winbase.h"
 #include "wincrypt.h"
 #include "winreg.h"
-#include "bcrypt.h"
 #include "objbase.h"
 #include "rpcproxy.h"
 #include "ntsecapi.h"
 
+#include "symcrypt.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dssenh);
 
+#define MAGIC_DSS1 ('D' | ('S' << 8) | ('S' << 16) | ('1' << 24))
+#define MAGIC_DSS2 ('D' | ('S' << 8) | ('S' << 16) | ('2' << 24))
+
 #define MAGIC_KEY (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0')
 struct key
 {
     DWORD             magic;
-    DWORD             algid;
+    ALG_ID            algid;
     DWORD             flags;
-    BCRYPT_KEY_HANDLE handle;
+    DWORD             bitlen;
+    SYMCRYPT_DLGROUP *group;
+    SYMCRYPT_DLKEY   *handle;
 };
 
 #define MAGIC_CONTAINER (('C' << 24) | ('O' << 16) | ('N' << 8) | 'T')
@@ -57,11 +62,12 @@ struct container
 #define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H')
 struct hash
 {
-    DWORD              magic;
-    BCRYPT_HASH_HANDLE handle;
-    DWORD              len;
-    UCHAR              value[64];
-    BOOL               finished;
+    DWORD                magic;
+    const SYMCRYPT_HASH *desc;
+    SYMCRYPT_HASH_STATE  state;
+    DWORD                len;
+    UCHAR                value[64];
+    BOOL                 finished;
 };
 
 static const char dss_path_fmt[] = "Software\\Wine\\Crypto\\DSS\\%s";
@@ -73,6 +79,51 @@ static const PROV_ENUMALGS_EX supported_base_algs[] =
     { CALG_MD5, 128, 128, 128, 0, S("MD5"), S("Message Digest 5 (MD5)") },
     { CALG_DSS_SIGN, 1024, 512, 1024, CRYPT_FLAG_SIGNING, S("DSA_SIGN"), S("Digital Signature Algorithm") }
 };
+#undef S
+
+SYMCRYPT_ENVIRONMENT_DEFS( WindowsUsermodeWin8_1nLater );
+
+void * SYMCRYPT_CALL SymCryptCallbackAlloc( SIZE_T size )
+{
+    return _aligned_malloc( size, SYMCRYPT_ASYM_ALIGN_VALUE );
+}
+
+void SYMCRYPT_CALL SymCryptCallbackFree( void *ptr )
+{
+    _aligned_free( ptr );
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptCallbackRandom( BYTE *buf, SIZE_T size )
+{
+    return RtlGenRandom( buf, size ) ? SYMCRYPT_NO_ERROR : SYMCRYPT_EXTERNAL_FAILURE;
+}
+
+void SYMCRYPT_CALL SymCryptDsaSelftest( void )
+{
+}
+
+void SYMCRYPT_CALL SymCryptDhSecretAgreementSelftest( void )
+{
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptDsaPct( const SYMCRYPT_DLKEY *key )
+{
+    return SYMCRYPT_NO_ERROR;
+}
+
+UINT32 g_SymCryptFipsSelftestsPerformed;
+
+BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved )
+{
+    switch (reason)
+    {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls( instance );
+        SymCryptInit();
+        break;
+    }
+    return TRUE;
+}
 
 static BOOL create_container_regkey( struct container *container, REGSAM sam, HKEY *hkey )
 {
@@ -127,7 +178,7 @@ static BOOL open_container_regkey( const char *name, DWORD flags, REGSAM access,
     return !RegOpenKeyExA( rootkey, path, 0, access, hkey );
 }
 
-static const WCHAR *map_keyspec_to_keypair_name( DWORD keyspec )
+static const WCHAR *map_keyspec_to_keypair_name( DWORD keyspec, ALG_ID *algid )
 {
     const WCHAR *name;
 
@@ -135,9 +186,11 @@ static const WCHAR *map_keyspec_to_keypair_name( DWORD keyspec )
     {
     case AT_KEYEXCHANGE:
         name = L"KeyExchangeKeyPair";
+        if (algid) *algid = CALG_DH_SF;
         break;
     case AT_SIGNATURE:
         name = L"SignatureKeyPair";
+        if (algid) *algid = CALG_DSS_SIGN;
         break;
     default:
         ERR( "invalid key spec %lu\n", keyspec );
@@ -146,51 +199,101 @@ static const WCHAR *map_keyspec_to_keypair_name( DWORD keyspec )
     return name;
 }
 
-static struct key *create_key( ALG_ID algid, DWORD flags )
+static struct key *create_key( ALG_ID algid, DWORD bitlen, DWORD flags )
 {
-    struct key *ret;
+    struct key *key;
 
     switch (algid)
     {
-    case AT_SIGNATURE:
     case CALG_DSS_SIGN:
+    case CALG_DH_SF:
         break;
 
     default:
         FIXME( "unhandled algorithm %08x\n", algid );
+        SetLastError( NTE_BAD_ALGID );
         return NULL;
     }
 
-    if (!(ret = calloc( 1, sizeof(*ret) ))) return NULL;
+    if (!(key = calloc( 1, sizeof(*key) ))) return NULL;
 
-    ret->magic = MAGIC_KEY;
-    ret->algid = algid;
-    ret->flags = flags;
-    return ret;
+    key->magic  = MAGIC_KEY;
+    key->algid  = algid;
+    key->bitlen = bitlen;
+    key->flags  = flags;
+    return key;
 }
 
 static void destroy_key( struct key *key )
 {
     if (!key) return;
-    BCryptDestroyKey( key->handle );
+    if (key->handle) SymCryptDlkeyFree( key->handle );
+    if (key->group) SymCryptDlgroupFree( key->group );
     /* Ensure compiler doesn't optimize out the assignment with 0. */
     SecureZeroMemory( &key->magic, sizeof(key->magic) );
     free( key );
 }
 
-static struct key *import_key( DWORD keyspec, BYTE *data, DWORD len )
+static struct key *import_key( ALG_ID algid, const BYTE *input, DWORD input_len, DWORD flags )
 {
-    struct key *ret;
+    const BLOBHEADER *hdr = (const BLOBHEADER *)input;
+    const DSSPUBKEY *dsskey = (const DSSPUBKEY *)(hdr + 1);
+    UINT32 priv_size = 0, pub_size = 0, set_flags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_DLKEY_DSA;
+    const UCHAR *prime_p, *prime_q, *generator, *pub = NULL, *priv = NULL, *seed, *count;
+    ULONG size, key_size;
+    struct key *key;
+
+    if (input_len < sizeof(*hdr)) return NULL;
+    if (hdr->bType != PRIVATEKEYBLOB && hdr->bType != PUBLICKEYBLOB) return NULL;
+    if (hdr->bVersion != 2 || (hdr->aiKeyAlg != CALG_DSS_SIGN && hdr->aiKeyAlg != CALG_DH_SF)) return NULL;
+
+    size = sizeof(*hdr) + sizeof(*dsskey);
+    if (input_len < size || dsskey->bitlen & 0xf) return NULL;
 
-    if (!(ret = create_key( keyspec, 0 ))) return NULL;
+    key_size = dsskey->bitlen / 8;
+    size += key_size * 2 /* p and g */+ 20 /* q */ + 4 /* count */ + 20 /* seed */;
+    size += (hdr->bType == PRIVATEKEYBLOB ? 20 /* x */ : key_size /* y */);
+    if (input_len != size) return NULL;
 
-    if (BCryptImportKeyPair( BCRYPT_DSA_ALG_HANDLE, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, &ret->handle, data, len, 0 ))
+    if (!(key = create_key( algid, dsskey->bitlen, flags ))) return NULL;
+    if (!(key->group = SymCryptDlgroupAllocate( dsskey->bitlen, 0 )) ||
+        !(key->handle = SymCryptDlkeyAllocate( key->group )))
     {
-        WARN( "failed to import key\n" );
-        destroy_key( ret );
+        destroy_key( key );
         return NULL;
     }
-    return ret;
+
+    prime_p = (const UCHAR *)(dsskey + 1);
+    prime_q = prime_p + key_size;
+    generator = prime_q + 20;
+    count = generator + key_size + (hdr->bType == PRIVATEKEYBLOB ? 20 : key_size);
+    seed = count + 4;
+
+    if (SymCryptDlgroupSetValue( prime_p, key_size, prime_q, 20, generator, key_size, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST,
+                                 NULL, seed, 20, *(UINT32 *)count, SYMCRYPT_DLGROUP_FIPS_NONE, key->group ))
+    {
+        destroy_key( key );
+        return NULL;
+    }
+
+    if (hdr->bType == PRIVATEKEYBLOB)
+    {
+        priv = generator + key_size;
+        priv_size = 20;
+    }
+    else
+    {
+        pub = generator + key_size;
+        pub_size = key_size;
+    }
+
+    if (SymCryptDlkeySetValue( priv, priv_size, pub, pub_size, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, set_flags, key->handle ))
+    {
+        destroy_key( key );
+        return NULL;
+    }
+
+    return key;
 }
 
 static struct key *read_key( HKEY hkey, DWORD keyspec, DWORD flags )
@@ -199,9 +302,10 @@ static struct key *read_key( HKEY hkey, DWORD keyspec, DWORD flags )
     DWORD type, len;
     BYTE *data;
     DATA_BLOB blob_in, blob_out;
-    struct key *ret = NULL;
+    ALG_ID algid;
+    struct key *key = NULL;
 
-    if (!(value = map_keyspec_to_keypair_name( keyspec ))) return NULL;
+    if (!(value = map_keyspec_to_keypair_name( keyspec, &algid ))) return NULL;
     if (RegQueryValueExW( hkey, value, 0, &type, NULL, &len )) return NULL;
     if (!(data = malloc( len ))) return NULL;
 
@@ -211,13 +315,13 @@ static struct key *read_key( HKEY hkey, DWORD keyspec, DWORD flags )
         blob_in.cbData = len;
         if (CryptUnprotectData( &blob_in, NULL, NULL, NULL, NULL, flags, &blob_out ))
         {
-            ret = import_key( keyspec, blob_out.pbData, blob_out.cbData );
+            key = import_key( algid, blob_out.pbData, blob_out.cbData, flags );
             LocalFree( blob_out.pbData );
         }
     }
 
     free( data );
-    return ret;
+    return key;
 }
 
 static void destroy_container( struct container *container )
@@ -328,9 +432,14 @@ BOOL WINAPI CPReleaseContext( HCRYPTPROV hprov, DWORD flags )
 {
     struct container *container = (struct container *)hprov;
 
-    TRACE( "%p, %08lx\n", (void *)hprov, flags );
+    TRACE( "%IX, %08lx\n", hprov, flags );
+
+    if (!container || container->magic != MAGIC_CONTAINER)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
 
-    if (container->magic != MAGIC_CONTAINER) return FALSE;
     destroy_container( container );
     return TRUE;
 }
@@ -364,17 +473,16 @@ BOOL WINAPI CPGetProvParam( HCRYPTPROV hprov, DWORD param, BYTE *data, DWORD *le
 {
     struct container *container = (struct container *)hprov;
 
-    TRACE( "%p, %lu, %p, %p, %08lx\n", (void *)hprov, param, data, len, flags );
+    TRACE( "%IX, %lu, %p, %p, %08lx\n", hprov, param, data, len, flags );
 
-    if (!len)
+    if (!container || container->magic != MAGIC_CONTAINER)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError( NTE_BAD_UID );
         return FALSE;
     }
-
-    if (container->magic != MAGIC_CONTAINER)
+    if (!len)
     {
-        SetLastError(NTE_BAD_UID);
+        SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
 
@@ -418,10 +526,10 @@ BOOL WINAPI CPGetProvParam( HCRYPTPROV hprov, DWORD param, BYTE *data, DWORD *le
         {
             PROV_ENUMALGS alg;
 
-            alg.aiAlgid = supported_base_algs[container->alg_idx].aiAlgid;
-            alg.dwBitLen = supported_base_algs[container->alg_idx].dwDefaultLen;
-            strcpy( alg.szName, supported_base_algs[container->alg_idx].szName );
+            alg.aiAlgid   = supported_base_algs[container->alg_idx].aiAlgid;
+            alg.dwBitLen  = supported_base_algs[container->alg_idx].dwDefaultLen;
             alg.dwNameLen = supported_base_algs[container->alg_idx].dwNameLen;
+            strcpy( alg.szName, supported_base_algs[container->alg_idx].szName );
             return copy_param( data, len, &alg, sizeof(alg) );
         }
         else
@@ -435,6 +543,58 @@ BOOL WINAPI CPGetProvParam( HCRYPTPROV hprov, DWORD param, BYTE *data, DWORD *le
     }
 }
 
+static BOOL export_key( const struct key *key, DWORD type, BYTE *output, DWORD output_len, DWORD *ret_len )
+{
+    BLOBHEADER *hdr = (BLOBHEADER *)output;
+    DSSPUBKEY *dsskey = (DSSPUBKEY *)(hdr + 1);
+    UINT32 key_size = key->bitlen / 8, pub_size = 0, priv_size = 0;
+    UCHAR *prime_p, *prime_q, *generator, *pub = NULL, *priv = NULL, *seed, *count;
+    ULONG size;
+
+    size = sizeof(*hdr) + sizeof(*dsskey) + key_size * 2 /* p and g */ + 20 /* q */ + 4 /* count */ + 20 /* seed */;
+    size += (type == PRIVATEKEYBLOB) ? 20 /* x */ : key_size; /* y */
+    if (output_len < size || !output)
+    {
+        *ret_len = size;
+        if (output) return FALSE;
+        return TRUE;
+    }
+
+    hdr->bType    = type;
+    hdr->bVersion = 2;
+    hdr->reserved = 0;
+    hdr->aiKeyAlg = CALG_DSS_SIGN;
+
+    dsskey->magic  = (type == PRIVATEKEYBLOB) ? MAGIC_DSS2 : MAGIC_DSS1;
+    dsskey->bitlen = key->bitlen;
+
+    prime_p = (UCHAR *)(dsskey + 1);
+    prime_q = prime_p + key_size;
+    generator = prime_q + 20;
+    count = generator + key_size + (type == PRIVATEKEYBLOB ? 20 : key_size);
+    seed = count + 4;
+
+    if (SymCryptDlgroupGetValue( key->group, prime_p, key_size, prime_q, 20, generator, key_size,
+                                 SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, NULL, seed, 20, (UINT32 *)count )) return FALSE;
+
+    if (type == PRIVATEKEYBLOB)
+    {
+        priv = generator + key_size;
+        priv_size = 20;
+    }
+    else
+    {
+        pub = generator + key_size;
+        pub_size = key_size;
+    }
+
+    if (SymCryptDlkeyGetValue( key->handle, priv, priv_size, pub, pub_size, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, 0 ))
+        return FALSE;
+
+    *ret_len = size;
+    return TRUE;
+}
+
 static BOOL store_key_pair( struct key *key, HKEY hkey, DWORD keyspec, DWORD flags )
 {
     const WCHAR *value;
@@ -444,12 +604,11 @@ static BOOL store_key_pair( struct key *key, HKEY hkey, DWORD keyspec, DWORD fla
     BOOL ret = TRUE;
 
     if (!key) return TRUE;
-    if (!(value = map_keyspec_to_keypair_name( keyspec ))) return FALSE;
+    if (!(value = map_keyspec_to_keypair_name( keyspec, NULL ))) return FALSE;
 
-    if (BCryptExportKey( key->handle, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, NULL, 0, &len, 0 )) return FALSE;
+    if (!export_key( key, PRIVATEKEYBLOB, NULL, 0, &len )) return FALSE;
     if (!(data = malloc( len ))) return FALSE;
-
-    if (!BCryptExportKey( key->handle, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, data, len, &len, 0 ))
+    if (export_key( key, PRIVATEKEYBLOB, data, len, &len ))
     {
         blob_in.pbData = data;
         blob_in.cbData = len;
@@ -467,59 +626,51 @@ static BOOL store_key_pair( struct key *key, HKEY hkey, DWORD keyspec, DWORD fla
 static BOOL store_key_container_keys( struct container *container )
 {
     HKEY hkey;
-    DWORD flags;
+    DWORD flags = (container->flags & CRYPT_MACHINE_KEYSET) ? CRYPTPROTECT_LOCAL_MACHINE : 0;
     BOOL ret;
 
-    if (container->flags & CRYPT_MACHINE_KEYSET)
-        flags = CRYPTPROTECT_LOCAL_MACHINE;
-    else
-        flags = 0;
-
     if (!create_container_regkey( container, KEY_WRITE, &hkey )) return FALSE;
 
     ret = store_key_pair( container->exch_key, hkey, AT_KEYEXCHANGE, flags );
-    if (ret) store_key_pair( container->sign_key, hkey, AT_SIGNATURE, flags );
+    if (ret) ret = store_key_pair( container->sign_key, hkey, AT_SIGNATURE, flags );
     RegCloseKey( hkey );
     return ret;
 }
 
-static struct key *duplicate_key( const struct key *key )
+static struct key *duplicate_key( const struct key *src )
 {
-    struct key *ret;
-
-    if (!(ret = create_key( key->algid, key->flags ))) return NULL;
+    struct key *dst;
+    DWORD len, type = SymCryptDlkeyHasPrivateKey( src->handle ) ? PRIVATEKEYBLOB : PUBLICKEYBLOB;
+    BYTE *data;
 
-    if (BCryptDuplicateKey( key->handle, &ret->handle, NULL, 0, 0 ))
+    if (!export_key( src, type, NULL, 0, &len )) return NULL;
+    if (!(data = malloc( len ))) return NULL;
+    if (!export_key( src, type, data, len, &len ) || !(dst = import_key( src->algid, data, len, src->flags )))
     {
-        free( ret );
+        free( data );
         return NULL;
     }
-    return ret;
+
+    free( data );
+    return dst;
 }
 
 static BOOL generate_key( struct container *container, ALG_ID algid, DWORD bitlen, DWORD flags, HCRYPTKEY *ret_key )
 {
-    struct key *key, *sign_key;
-    NTSTATUS status;
+    UINT32 generate_flags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_DLKEY_DSA;
+    struct key *key, *sign_key, *exch_key;
 
-    if (!(key = create_key( algid, flags ))) return FALSE;
+    if (algid == AT_SIGNATURE) algid = CALG_DSS_SIGN;
+    else if (algid == AT_KEYEXCHANGE) algid = CALG_DH_SF;
 
-    if ((status = BCryptGenerateKeyPair( BCRYPT_DSA_ALG_HANDLE, &key->handle, bitlen, 0 )))
-    {
-        ERR( "failed to generate key %08lx\n", status );
-        destroy_key( key );
-        return FALSE;
-    }
-    if ((status = BCryptFinalizeKeyPair( key->handle, 0 )))
-    {
-        ERR( "failed to finalize key %08lx\n", status );
-        destroy_key( key );
-        return FALSE;
-    }
+    if (!(key = create_key( algid, bitlen, flags ))) return FALSE;
+    if (!(key->group = SymCryptDlgroupAllocate( bitlen, 0 ))) goto error;
+    if (SymCryptDlgroupGenerate( NULL, SYMCRYPT_DLGROUP_FIPS_186_2, key->group )) goto error;
+    if (!(key->handle = SymCryptDlkeyAllocate( key->group ))) goto error;
+    if (SymCryptDlkeyGenerate( generate_flags, key->handle )) goto error;
 
     switch (algid)
     {
-    case AT_SIGNATURE:
     case CALG_DSS_SIGN:
         if (!(sign_key = duplicate_key( key )))
         {
@@ -530,8 +681,19 @@ static BOOL generate_key( struct container *container, ALG_ID algid, DWORD bitle
         container->sign_key = sign_key;
         break;
 
+    case CALG_DH_SF:
+        if (!(exch_key = duplicate_key( key )))
+        {
+            destroy_key( key );
+            return FALSE;
+        }
+        destroy_key( container->exch_key );
+        container->exch_key = exch_key;
+        break;
+
     default:
         FIXME( "unhandled algorithm %08x\n", algid );
+        SetLastError( NTE_BAD_ALGID );
         return FALSE;
     }
 
@@ -539,6 +701,11 @@ static BOOL generate_key( struct container *container, ALG_ID algid, DWORD bitle
 
     *ret_key = (HCRYPTKEY)key;
     return TRUE;
+
+error:
+    SetLastError( NTE_BAD_FLAGS );
+    destroy_key( key );
+    return FALSE;
 }
 
 BOOL WINAPI CPGenKey( HCRYPTPROV hprov, ALG_ID algid, DWORD flags, HCRYPTKEY *ret_key )
@@ -547,15 +714,14 @@ BOOL WINAPI CPGenKey( HCRYPTPROV hprov, ALG_ID algid, DWORD flags, HCRYPTKEY *re
     struct container *container = (struct container *)hprov;
     ULONG i, bitlen = HIWORD(flags) ? HIWORD(flags) : 1024;
 
-    TRACE( "%p, %08x, %08lx, %p\n", (void *)hprov, algid, flags, ret_key );
+    TRACE( "%IX, %08x, %08lx, %p\n", hprov, algid, flags, ret_key );
 
-    if (container->magic != MAGIC_CONTAINER) return FALSE;
-
-    if (bitlen % 2)
+    if (!container || container->magic != MAGIC_CONTAINER || bitlen % 2 || !ret_key)
     {
-        SetLastError( STATUS_INVALID_PARAMETER );
+        SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
+
     for (i = 0; i < ARRAY_SIZE(supported_key_lengths); i++)
     {
         if (bitlen == supported_key_lengths[i]) break;
@@ -573,9 +739,9 @@ BOOL WINAPI CPDestroyKey( HCRYPTPROV hprov, HCRYPTKEY hkey )
 {
     struct key *key = (struct key *)hkey;
 
-    TRACE( "%p, %p\n", (void *)hprov, (void *)hkey );
+    TRACE( "%IX, %IX\n", hprov, hkey );
 
-    if (key->magic != MAGIC_KEY)
+    if (!key || key->magic != MAGIC_KEY)
     {
         SetLastError( NTE_BAD_KEY );
         return FALSE;
@@ -585,50 +751,17 @@ BOOL WINAPI CPDestroyKey( HCRYPTPROV hprov, HCRYPTKEY hkey )
     return TRUE;
 }
 
-#define MAGIC_DSS1 ('D' | ('S' << 8) | ('S' << 16) | ('1' << 24))
-#define MAGIC_DSS2 ('D' | ('S' << 8) | ('S' << 16) | ('2' << 24))
-#define MAGIC_DSS3 ('D' | ('S' << 8) | ('S' << 16) | ('3' << 24))
-
 static BOOL import_key_dss2( struct container *container, ALG_ID algid, const BYTE *data, DWORD len, DWORD flags,
                              HCRYPTKEY *ret_key )
 {
-    const BLOBHEADER *hdr = (const BLOBHEADER *)data;
-    const DSSPUBKEY *pubkey = (const DSSPUBKEY *)(hdr + 1);
-    const WCHAR *type;
     struct key *key, *exch_key, *sign_key;
-    NTSTATUS status;
-
-    if (len < sizeof(*hdr) + sizeof(*pubkey)) return FALSE;
 
-    switch (pubkey->magic)
-    {
-    case MAGIC_DSS1:
-        type = LEGACY_DSA_V2_PUBLIC_BLOB;
-        break;
-
-    case MAGIC_DSS2:
-        type = LEGACY_DSA_V2_PRIVATE_BLOB;
-        break;
-
-    default:
-        FIXME( "unsupported key magic %08lx\n", pubkey->magic );
-        return FALSE;
-    }
-
-    if (!(key = create_key( CALG_DSS_SIGN, flags ))) return FALSE;
-
-    if ((status = BCryptImportKeyPair( BCRYPT_DSA_ALG_HANDLE, NULL, type, &key->handle, (UCHAR *)data, len, 0 )))
-    {
-        TRACE( "failed to import key %08lx\n", status );
-        destroy_key( key );
-        return FALSE;
-    }
+    if (!(key = import_key( algid, data, len, flags ))) return FALSE;
 
-    if (!wcscmp(type, LEGACY_DSA_V2_PRIVATE_BLOB))
+    if (SymCryptDlkeyHasPrivateKey( key->handle ))
     {
         switch (algid)
         {
-        case AT_KEYEXCHANGE:
         case CALG_DH_SF:
             if (!(exch_key = duplicate_key( key )))
             {
@@ -639,7 +772,6 @@ static BOOL import_key_dss2( struct container *container, ALG_ID algid, const BY
             container->exch_key = exch_key;
             break;
 
-        case AT_SIGNATURE:
         case CALG_DSS_SIGN:
             if (!(sign_key = duplicate_key( key )))
             {
@@ -656,84 +788,13 @@ static BOOL import_key_dss2( struct container *container, ALG_ID algid, const BY
             return FALSE;
         }
 
-        if (!store_key_container_keys( container )) return FALSE;
-    }
-
-    *ret_key = (HCRYPTKEY)key;
-    return TRUE;
-}
-
-static BOOL import_key_dss3( struct container *container, ALG_ID algid, const BYTE *data, DWORD len, DWORD flags,
-                             HCRYPTKEY *ret_key )
-{
-    const BLOBHEADER *hdr = (const BLOBHEADER *)data;
-    const DSSPUBKEY_VER3 *pubkey = (const DSSPUBKEY_VER3 *)(hdr + 1);
-    BCRYPT_DSA_KEY_BLOB *blob;
-    struct key *key;
-    BYTE *src, *dst;
-    ULONG i, size, size_q;
-    NTSTATUS status;
-
-    if (len < sizeof(*hdr) + sizeof(*pubkey)) return FALSE;
-
-    switch (pubkey->magic)
-    {
-    case MAGIC_DSS3:
-        break;
-
-    default:
-        FIXME( "unsupported key magic %08lx\n", pubkey->magic );
-        return FALSE;
-    }
-
-    if ((size_q = pubkey->bitlenQ / 8) > sizeof(blob->q))
-    {
-        FIXME( "q too large\n" );
-        return FALSE;
-    }
-
-    if (!(key = create_key( CALG_DSS_SIGN, flags ))) return FALSE;
-
-    size = sizeof(*blob) + (pubkey->bitlenP / 8) * 3;
-    if (!(blob = calloc( 1, size )))
-    {
-        destroy_key( key );
-        return FALSE;
-    }
-    blob->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC;
-    blob->cbKey   = pubkey->bitlenP / 8;
-    memcpy( blob->Count, &pubkey->DSSSeed.counter, sizeof(blob->Count) );
-    memcpy( blob->Seed, pubkey->DSSSeed.seed, sizeof(blob->Seed) );
-
-    /* q */
-    src = (BYTE *)(pubkey + 1) + blob->cbKey;
-    for (i = 0; i < size_q; i++) blob->q[i] = src[size_q - i - 1];
-
-    /* p */
-    src -= blob->cbKey;
-    dst = (BYTE *)(blob + 1);
-    for (i = 0; i < blob->cbKey; i++) dst[i] = src[blob->cbKey - i - 1];
-
-    /* g */
-    src += blob->cbKey + size_q;
-    dst += blob->cbKey;
-    for (i = 0; i < blob->cbKey; i++) dst[i] = src[blob->cbKey - i - 1];
-
-    /* y */
-    src += blob->cbKey + pubkey->bitlenJ / 8;
-    dst += blob->cbKey;
-    for (i = 0; i < blob->cbKey; i++) dst[i] = src[blob->cbKey - i - 1];
-
-    if ((status = BCryptImportKeyPair( BCRYPT_DSA_ALG_HANDLE, NULL, BCRYPT_DSA_PUBLIC_BLOB, &key->handle,
-                                       (UCHAR *)blob, size, 0 )))
-    {
-        WARN( "failed to import key %08lx\n", status );
-        destroy_key( key );
-        free( blob );
-        return FALSE;
+        if (!store_key_container_keys( container ))
+        {
+            destroy_key( key );
+            return FALSE;
+        }
     }
 
-    free( blob );
     *ret_key = (HCRYPTKEY)key;
     return TRUE;
 }
@@ -745,10 +806,13 @@ BOOL WINAPI CPImportKey( HCRYPTPROV hprov, const BYTE *data, DWORD len, HCRYPTKE
     const BLOBHEADER *hdr;
     BOOL ret;
 
-    TRACE( "%p, %p, %lu, %p, %08lx, %p\n", (void *)hprov, data, len, (void *)hpubkey, flags, ret_key );
+    TRACE( "%IX, %p, %lu, %IX, %08lx, %p\n", hprov, data, len, hpubkey, flags, ret_key );
 
-    if (container->magic != MAGIC_CONTAINER) return FALSE;
-    if (len < sizeof(*hdr)) return FALSE;
+    if (!container || container->magic != MAGIC_CONTAINER || !data || len < sizeof(*hdr) || !ret_key)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
 
     hdr = (const BLOBHEADER *)data;
     if ((hdr->bType != PRIVATEKEYBLOB && hdr->bType != PUBLICKEYBLOB) || hdr->aiKeyAlg != CALG_DSS_SIGN)
@@ -763,10 +827,6 @@ BOOL WINAPI CPImportKey( HCRYPTPROV hprov, const BYTE *data, DWORD len, HCRYPTKE
         ret = import_key_dss2( container, hdr->aiKeyAlg, data, len, flags, ret_key );
         break;
 
-    case 3:
-        ret = import_key_dss3( container, hdr->aiKeyAlg, data, len, flags, ret_key );
-        break;
-
     default:
         FIXME( "version %u not supported\n", hdr->bVersion );
         return FALSE;
@@ -775,16 +835,18 @@ BOOL WINAPI CPImportKey( HCRYPTPROV hprov, const BYTE *data, DWORD len, HCRYPTKE
     return ret;
 }
 
-BOOL WINAPI CPExportKey( HCRYPTPROV hprov, HCRYPTKEY hkey, HCRYPTKEY hexpkey, DWORD blobtype, DWORD flags,
-                         BYTE *data, DWORD *len )
+BOOL WINAPI CPExportKey( HCRYPTPROV hprov, HCRYPTKEY hkey, HCRYPTKEY hexpkey, DWORD type, DWORD flags, BYTE *data,
+                         DWORD *len )
 {
     struct key *key = (struct key *)hkey;
-    const WCHAR *type;
 
-    TRACE( "%p, %p, %p, %08lx, %08lx, %p, %p\n", (void *)hprov, (void *)hkey, (void *)hexpkey, blobtype, flags,
-           data, len );
+    TRACE( "%IX, %IX, %IX, %08lx, %08lx, %p, %p\n", hprov, hkey, hexpkey, type, flags, data, len );
 
-    if (key->magic != MAGIC_KEY) return FALSE;
+    if (!key || key->magic != MAGIC_KEY || (type != PUBLICKEYBLOB && type != PRIVATEKEYBLOB) || !len)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
     if (hexpkey)
     {
         FIXME( "export key not supported\n" );
@@ -796,34 +858,23 @@ BOOL WINAPI CPExportKey( HCRYPTPROV hprov, HCRYPTKEY hkey, HCRYPTKEY hexpkey, DW
         return FALSE;
     }
 
-    switch (blobtype)
-    {
-    case PUBLICKEYBLOB:
-        type = LEGACY_DSA_V2_PUBLIC_BLOB;
-        break;
-
-    case PRIVATEKEYBLOB:
-        type = LEGACY_DSA_V2_PRIVATE_BLOB;
-        break;
-
-    default:
-        FIXME( "blob type %lu not supported\n", blobtype );
-        return FALSE;
-    }
-
-    return !BCryptExportKey( key->handle, NULL, type, data, *len, len, 0 );
+    return export_key( key, type, data, *len, len );
 }
 
 BOOL WINAPI CPDuplicateKey( HCRYPTPROV hprov, HCRYPTKEY hkey, DWORD *reserved, DWORD flags, HCRYPTKEY *ret_key )
 {
-    struct key *key = (struct key *)hkey, *ret;
+    struct key *src = (struct key *)hkey, *dst;
 
-    TRACE( "%p, %p, %p, %08lx, %p\n", (void *)hprov, (void *)hkey, reserved, flags, ret_key );
+    TRACE( "%IX, %IX, %p, %08lx, %p\n", hprov, hkey, reserved, flags, ret_key );
 
-    if (key->magic != MAGIC_KEY) return FALSE;
+    if (!src || src->magic != MAGIC_KEY || !ret_key)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
 
-    if (!(ret = duplicate_key( key ))) return FALSE;
-    *ret_key = (HCRYPTKEY)ret;
+    if (!(dst = duplicate_key( src ))) return FALSE;
+    *ret_key = (HCRYPTKEY)dst;
     return TRUE;
 }
 
@@ -832,9 +883,13 @@ BOOL WINAPI CPGetUserKey( HCRYPTPROV hprov, DWORD keyspec, HCRYPTKEY *ret_key )
     struct container *container = (struct container *)hprov;
     BOOL ret = FALSE;
 
-    TRACE( "%p, %08lx, %p\n", (void *)hprov, keyspec, ret_key );
+    TRACE( "%IX, %08lx, %p\n", hprov, keyspec, ret_key );
 
-    if (container->magic != MAGIC_CONTAINER) return FALSE;
+    if (!container || container->magic != MAGIC_CONTAINER || !ret_key)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
 
     switch (keyspec)
     {
@@ -858,12 +913,8 @@ BOOL WINAPI CPGetUserKey( HCRYPTPROV hprov, DWORD keyspec, HCRYPTKEY *ret_key )
 
 BOOL WINAPI CPSetKeyParam( HCRYPTPROV hprov, HCRYPTKEY hkey, DWORD param, BYTE *data, DWORD flags )
 {
-    if (!data)
-    {
-        SetLastError( ERROR_INVALID_PARAMETER );
-        return FALSE;
-    }
-
+    FIXME( "%IX, %IX, %lu, %p, %08lx\n", hprov, hkey, param, data, flags );
+    SetLastError( ERROR_INVALID_PARAMETER );
     return FALSE;
 }
 
@@ -871,28 +922,32 @@ BOOL WINAPI CPGenRandom( HCRYPTPROV hprov, DWORD len, BYTE *buffer )
 {
     struct container *container = (struct container *)hprov;
 
-    TRACE( "%p, %lu, %p\n", (void *)hprov, len, buffer );
+    TRACE( "%IX, %lu, %p\n", hprov, len, buffer );
 
-    if (container->magic != MAGIC_CONTAINER) return FALSE;
+    if (!container || container->magic != MAGIC_CONTAINER || !buffer)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
 
     return RtlGenRandom( buffer, len );
 }
 
 static struct hash *create_hash( ALG_ID algid )
 {
-    struct hash *ret;
-    BCRYPT_ALG_HANDLE alg_handle;
+    struct hash *hash;
+    const SYMCRYPT_HASH *desc;
     DWORD len;
 
     switch (algid)
     {
     case CALG_MD5:
-        alg_handle = BCRYPT_MD5_ALG_HANDLE;
+        desc = SymCryptMd5Algorithm;
         len = 16;
         break;
 
     case CALG_SHA1:
-        alg_handle = BCRYPT_SHA1_ALG_HANDLE;
+        desc = SymCryptSha1Algorithm;
         len = 20;
         break;
 
@@ -901,23 +956,27 @@ static struct hash *create_hash( ALG_ID algid )
         return NULL;
     }
 
-    if (!(ret = calloc( 1, sizeof(*ret) ))) return NULL;
+    if (!(hash = calloc( 1, sizeof(*hash) ))) return NULL;
 
-    ret->magic = MAGIC_HASH;
-    ret->len   = len;
-    if (BCryptCreateHash( alg_handle, &ret->handle, NULL, 0, NULL, 0, 0 ))
-    {
-        free( ret );
-        return NULL;
-    }
-    return ret;
+    hash->magic = MAGIC_HASH;
+    hash->desc  = desc;
+    hash->len   = len;
+
+    SymCryptHashInit( hash->desc, &hash->state );
+    return hash;
 }
 
 BOOL WINAPI CPCreateHash( HCRYPTPROV hprov, ALG_ID algid, HCRYPTKEY hkey, DWORD flags, HCRYPTHASH *ret_hash )
 {
     struct hash *hash;
 
-    TRACE( "%p, %08x, %p, %08lx, %p\n", (void *)hprov, algid, (void *)hkey, flags, ret_hash );
+    TRACE( "%IX, %08x, %IX, %08lx, %p\n", hprov, algid, hkey, flags, ret_hash );
+
+    if (!ret_hash)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
 
     switch (algid)
     {
@@ -940,7 +999,6 @@ BOOL WINAPI CPCreateHash( HCRYPTPROV hprov, ALG_ID algid, HCRYPTKEY hkey, DWORD
 static void destroy_hash( struct hash *hash )
 {
     if (!hash) return;
-    BCryptDestroyHash( hash->handle );
     /* Ensure compiler doesn't optimize out the assignment with 0. */
     SecureZeroMemory( &hash->magic, sizeof(hash->magic) );
     free( hash );
@@ -950,9 +1008,9 @@ BOOL WINAPI CPDestroyHash( HCRYPTPROV hprov, HCRYPTHASH hhash )
 {
     struct hash *hash = (struct hash *)hhash;
 
-    TRACE( "%p, %p\n", (void *)hprov, (void *)hhash);
+    TRACE( "%IX, %IX\n", hprov, hhash);
 
-    if (hash->magic != MAGIC_HASH)
+    if (!hash || hash->magic != MAGIC_HASH)
     {
         SetLastError( NTE_BAD_HASH );
         return FALSE;
@@ -962,34 +1020,36 @@ BOOL WINAPI CPDestroyHash( HCRYPTPROV hprov, HCRYPTHASH hhash )
     return TRUE;
 }
 
-static struct hash *duplicate_hash( const struct hash *hash )
+static struct hash *duplicate_hash( const struct hash *src )
 {
-    struct hash *ret;
+    struct hash *dst;
 
-    if (!(ret = malloc( sizeof(*ret) ))) return NULL;
+    if (!(dst = malloc( sizeof(*dst) ))) return NULL;
 
-    ret->magic = hash->magic;
-    ret->len   = hash->len;
-    if (BCryptDuplicateHash( hash->handle, &ret->handle, NULL, 0, 0 ))
-    {
-        free( ret );
-        return NULL;
-    }
-    memcpy( ret->value, hash->value, sizeof(hash->value) );
-    ret->finished = hash->finished;
-    return ret;
+    dst->magic    = src->magic;
+    dst->desc     = src->desc;
+    dst->len      = src->len;
+    dst->finished = src->finished;
+
+    SymCryptHashStateCopy( src->desc, &src->state, &dst->state );
+    memcpy( dst->value, src->value, sizeof(src->value) );
+    return dst;
 }
 
 BOOL WINAPI CPDuplicateHash( HCRYPTPROV hprov, HCRYPTHASH hhash, DWORD *reserved, DWORD flags, HCRYPTHASH *ret_hash )
 {
-    struct hash *hash = (struct hash *)hhash, *ret;
+    struct hash *src = (struct hash *)hhash, *dst;
 
-    TRACE( "%p, %p, %p, %08lx, %p\n", (void *)hprov, (void *)hhash, reserved, flags, ret_hash );
+    TRACE( "%IX, %IX, %p, %08lx, %p\n", hprov, hhash, reserved, flags, ret_hash );
 
-    if (hash->magic != MAGIC_HASH) return FALSE;
+    if (!src || src->magic != MAGIC_HASH)
+    {
+        SetLastError( NTE_BAD_HASH );
+        return FALSE;
+    }
 
-    if (!(ret = duplicate_hash( hash ))) return FALSE;
-    *ret_hash = (HCRYPTHASH)ret;
+    if (!(dst = duplicate_hash( src ))) return FALSE;
+    *ret_hash = (HCRYPTHASH)dst;
     return TRUE;
 }
 
@@ -997,25 +1057,35 @@ BOOL WINAPI CPHashData( HCRYPTPROV hprov, HCRYPTHASH hhash, const BYTE *data, DW
 {
     struct hash *hash = (struct hash *)hhash;
 
-    TRACE("%p, %p, %p, %lu, %08lx\n", (void *)hprov, (void *)hhash, data, len, flags );
+    TRACE("%IX, %IX, %p, %lu, %08lx\n", hprov, hhash, data, len, flags );
 
-    if (hash->magic != MAGIC_HASH) return FALSE;
+    if (!hash || hash->magic != MAGIC_HASH)
+    {
+        SetLastError( NTE_BAD_HASH );
+        return FALSE;
+    }
 
     if (hash->finished)
     {
         SetLastError( NTE_BAD_HASH_STATE );
         return FALSE;
     }
-    return !BCryptHashData( hash->handle, (UCHAR *)data, len, 0 );
+
+    SymCryptHashAppend( hash->desc, &hash->state, data, len );
+    return TRUE;
 }
 
 BOOL WINAPI CPGetHashParam( HCRYPTPROV hprov, HCRYPTHASH hhash, DWORD param, BYTE *data, DWORD *len, DWORD flags )
 {
     struct hash *hash = (struct hash *)hhash;
 
-    TRACE( "%p, %p, %08lx, %p, %p, %08lx\n", (void *)hprov, (void *)hhash, param, data, len, flags );
+    TRACE( "%IX, %IX, %08lx, %p, %p, %08lx\n", hprov, hhash, param, data, len, flags );
 
-    if (hash->magic != MAGIC_HASH) return FALSE;
+    if (!hash || hash->magic != MAGIC_HASH)
+    {
+        SetLastError( NTE_BAD_HASH );
+        return FALSE;
+    }
 
     switch (param)
     {
@@ -1033,7 +1103,7 @@ BOOL WINAPI CPGetHashParam( HCRYPTPROV hprov, HCRYPTHASH hhash, DWORD param, BYT
     case HP_HASHVAL:
         if (!hash->finished)
         {
-            if (BCryptFinishHash( hash->handle, hash->value, hash->len, 0 )) return FALSE;
+            SymCryptHashResult( hash->desc, &hash->state, hash->value, hash->len );
             hash->finished = TRUE;
         }
         if (hash->len > *len)
@@ -1056,9 +1126,13 @@ BOOL WINAPI CPSetHashParam( HCRYPTPROV hprov, HCRYPTHASH hhash, DWORD param, con
 {
     struct hash *hash = (struct hash *)hhash;
 
-    TRACE( "%p, %p, %08lx, %p, %08lx\n", (void *)hprov, (void *)hhash, param, data, flags );
+    TRACE( "%IX, %IX, %08lx, %p, %08lx\n", hprov, hhash, param, data, flags );
 
-    if (hash->magic != MAGIC_HASH) return FALSE;
+    if (!hash || hash->magic != MAGIC_HASH)
+    {
+        SetLastError( NTE_BAD_HASH );
+        return FALSE;
+    }
 
     switch (param)
     {
@@ -1075,6 +1149,7 @@ BOOL WINAPI CPSetHashParam( HCRYPTPROV hprov, HCRYPTHASH hhash, DWORD param, con
 
 BOOL WINAPI CPDeriveKey( HCRYPTPROV hprov, ALG_ID algid, HCRYPTHASH hhash, DWORD flags, HCRYPTKEY *ret_key )
 {
+    FIXME( "%p, %p, %08lx, %p\n", (void *)hprov, (void *)hhash, flags, ret_key );
     return FALSE;
 }
 
@@ -1096,84 +1171,77 @@ BOOL WINAPI CPSignHash( HCRYPTPROV hprov, HCRYPTHASH hhash, DWORD keyspec, const
 {
     struct container *container = (struct container *)hprov;
     struct hash *hash = (struct hash *)hhash;
-    ULONG len, i;
-    BYTE temp;
+    ULONG len;
 
-    TRACE( "%p, %p, %lu, %s, %08lx, %p, %p\n", (void *)hprov, (void *)hhash, keyspec, debugstr_w(desc), flags, sig,
-           siglen );
+    TRACE( "%IX, %IX, %lu, %s, %08lx, %p, %p\n", hprov, hhash, keyspec, debugstr_w(desc), flags, sig, siglen );
 
-    if (!hash->finished)
+    if (!container || container->magic != MAGIC_CONTAINER || !container->sign_key || !siglen)
     {
-        if (BCryptFinishHash( hash->handle, hash->value, hash->len, 0 )) return FALSE;
-        hash->finished = TRUE;
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+    if (!hash || hash->magic != MAGIC_HASH)
+    {
+        SetLastError( NTE_BAD_HASH );
+        return FALSE;
+    }
+    if (!(len = get_signature_length( container->sign_key->algid )))
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
     }
-
-    if (container->magic != MAGIC_CONTAINER || !container->sign_key) return FALSE;
-    if (hash->magic != MAGIC_HASH) return FALSE;
-
-    if (!(len = get_signature_length( container->sign_key->algid ))) return FALSE;
     if (*siglen < len)
     {
         *siglen = len;
         return TRUE;
     }
 
-    if (BCryptSignHash( container->sign_key->handle, NULL, hash->value, hash->len, sig, *siglen, siglen, 0 ))
+    if (!hash->finished)
     {
-        return FALSE;
+        SymCryptHashResult( hash->desc, &hash->state, hash->value, hash->len );
+        hash->finished = TRUE;
     }
 
-    /* Swap the r and s integers in the signature from big-endian to little-endian */
-    for (i = 0; i < len / 4; i++) {
-        /* r */
-        temp = sig[i];
-        sig[i] = sig[len / 2 - 1 - i];
-        sig[len / 2 - 1 - i] = temp;
-        /* s */
-        temp = sig[len / 2 + i];
-        sig[len / 2 + i] = sig[len - 1 - i];
-        sig[len - 1 - i] = temp;
-    }
+    if (SymCryptDsaSign( container->sign_key->handle, hash->value, hash->len, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, 0,
+                         sig, len )) return FALSE;
 
+    *siglen = len;
     return TRUE;
 }
 
 BOOL WINAPI CPVerifySignature( HCRYPTPROV hprov, HCRYPTHASH hhash, const BYTE *sig, DWORD siglen, HCRYPTKEY hpubkey,
                                const WCHAR *desc, DWORD flags )
 {
-    UCHAR signature[40];
-    DWORD i;
     struct hash *hash = (struct hash *)hhash;
     struct key *key = (struct key *)hpubkey;
+    ULONG len;
 
-    TRACE( "%p, %p, %p, %lu %p, %s, %08lx\n", (void *)hprov, (void *)hhash, sig, siglen, (void *)hpubkey,
-           debugstr_w(desc), flags );
+    TRACE( "%IX, %IX, %p, %lu %IX, %s, %08lx\n", hprov, hhash, sig, siglen, hpubkey, debugstr_w(desc), flags );
 
-    if (hash->magic != MAGIC_HASH || key->magic != MAGIC_KEY) return FALSE;
-    if (flags)
+    if (!hash || hash->magic != MAGIC_HASH || !key || key->magic != MAGIC_KEY)
     {
-        FIXME( "flags %08lx not supported\n", flags );
+        SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
-
-    if (siglen > sizeof(signature))
+    if (!(len = get_signature_length( key->algid )) || len != siglen)
     {
-        FIXME( "signature longer than 40 bytes is not supported\n");
+        SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
-
-    /* Swap the r and s integers in the signature from little-endian to big-endian */
-    for (i = 0; i < siglen / 2; i++)
+    if (flags)
     {
-        signature[i] = sig[siglen / 2 - 1 - i]; /* r */
-        signature[siglen / 2 + i] = sig[siglen - 1 - i]; /* s */
+        FIXME( "flags %08lx not supported\n", flags );
+        return FALSE;
     }
 
     if (!hash->finished)
     {
-        if (BCryptFinishHash( hash->handle, hash->value, hash->len, 0 )) return FALSE;
+        SymCryptHashResult( hash->desc, &hash->state, hash->value, hash->len );
         hash->finished = TRUE;
     }
 
-    return !BCryptVerifySignature( key->handle, NULL, hash->value, hash->len, signature, siglen, 0 );
+    if (SymCryptDsaVerify( key->handle, hash->value, hash->len, sig, siglen, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, 0 ))
+        return FALSE;
+
+    return TRUE;
 }
diff --git a/dlls/dssenh/tests/dssenh.c b/dlls/dssenh/tests/dssenh.c
index 9c9b18ed4ba8..c0f2dc39bac2 100644
--- a/dlls/dssenh/tests/dssenh.c
+++ b/dlls/dssenh/tests/dssenh.c
@@ -84,13 +84,7 @@ static void test_acquire_context(void)
     result = CryptReleaseContext(hProv, 0);
     ok(result, "CryptReleaseContext failed.\n");
 
-    result = CryptAcquireContextA(
-        &hProv, NULL, MS_DEF_DSS_PROV_A, PROV_DSS, CRYPT_VERIFYCONTEXT);
-    if(!result)
-    {
-        skip("DSS csp is currently not available, skipping tests.\n");
-        return;
-    }
+    result = CryptAcquireContextA(&hProv, NULL, MS_DEF_DSS_PROV_A, PROV_DSS, CRYPT_VERIFYCONTEXT);
     ok(result, "Expected no errors.\n");
 
     result = CryptReleaseContext(hProv, 0);
@@ -211,30 +205,30 @@ struct keylength_test {
 static const struct keylength_test baseDSS_keylength[] = {
     /* AT_KEYEXCHANGE is not supported by the base DSS provider */
     {AT_KEYEXCHANGE, 448 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
-    {AT_KEYEXCHANGE, 512 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
-    {AT_KEYEXCHANGE, 1024 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
+    {AT_KEYEXCHANGE, 512 << 16, FALSE, NTE_BAD_ALGID, 0, 1, 1},
+    {AT_KEYEXCHANGE, 1024 << 16, FALSE, NTE_BAD_ALGID, 0, 1, 1},
     {AT_KEYEXCHANGE, 1088 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
     /* min 512 max 1024 increment by 64 */
     {AT_SIGNATURE, 448 << 16, FALSE, NTE_BAD_FLAGS},
     {AT_SIGNATURE, 512 << 16, TRUE},
-    {AT_SIGNATURE, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL},
+    {AT_SIGNATURE, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL, 0, 1},
     {AT_SIGNATURE, 768 << 16, TRUE},
     {AT_SIGNATURE, 1024 << 16, TRUE},
     {AT_SIGNATURE, 1088 << 16, FALSE, NTE_BAD_FLAGS},
     /* CALG_DH_EPHEM is not supported by the base DSS provider */
     {CALG_DH_EPHEM, 448 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
-    {CALG_DH_EPHEM, 512 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
-    {CALG_DH_EPHEM, 1024 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
+    {CALG_DH_EPHEM, 512 << 16, FALSE, NTE_BAD_ALGID},
+    {CALG_DH_EPHEM, 1024 << 16, FALSE, NTE_BAD_ALGID},
     {CALG_DH_EPHEM, 1088 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
     /* CALG_DH_SF is not supported by the base DSS provider */
     {CALG_DH_SF, 448 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
-    {CALG_DH_SF, 512 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
-    {CALG_DH_SF, 1024 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
+    {CALG_DH_SF, 512 << 16, FALSE, NTE_BAD_ALGID, 0, 1, 1},
+    {CALG_DH_SF, 1024 << 16, FALSE, NTE_BAD_ALGID, 0, 1, 1},
     {CALG_DH_SF, 1088 << 16, FALSE, NTE_BAD_ALGID, 0, 0, 1},
     /* min 512 max 1024, increment by 64 */
     {CALG_DSS_SIGN, 448 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DSS_SIGN, 512 << 16, TRUE},
-    {CALG_DSS_SIGN, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL},
+    {CALG_DSS_SIGN, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL, 0, 1},
     {CALG_DSS_SIGN, 768 << 16, TRUE},
     {CALG_DSS_SIGN, 1024 << 16, TRUE},
     {CALG_DSS_SIGN, 1088 << 16, FALSE, NTE_BAD_FLAGS}
@@ -243,14 +237,14 @@ static const struct keylength_test baseDSS_keylength[] = {
 static const struct keylength_test dssDH_keylength[] = {
     /* min 512 max 1024, increment by 64 */
     {AT_KEYEXCHANGE, 448 << 16, FALSE, NTE_BAD_FLAGS},
-    {AT_KEYEXCHANGE, 512 << 16, TRUE, 0, 0, 1},
+    {AT_KEYEXCHANGE, 512 << 16, TRUE},
     {AT_KEYEXCHANGE, 513 << 16, FALSE, NTE_BAD_FLAGS, 0, 0, 1},
-    {AT_KEYEXCHANGE, 768 << 16, TRUE, 0, 0, 1},
-    {AT_KEYEXCHANGE, 1024 << 16, TRUE, 0, 0, 1},
+    {AT_KEYEXCHANGE, 768 << 16, TRUE},
+    {AT_KEYEXCHANGE, 1024 << 16, TRUE},
     {AT_KEYEXCHANGE, 1088 << 16, FALSE, NTE_BAD_FLAGS},
     {AT_SIGNATURE, 448 << 16, FALSE, NTE_BAD_FLAGS},
     {AT_SIGNATURE, 512 << 16, TRUE},
-    {AT_SIGNATURE, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL},
+    {AT_SIGNATURE, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL, 0, 1},
     {AT_SIGNATURE, 768 << 16, TRUE},
     {AT_SIGNATURE, 1024 << 16, TRUE},
     {AT_SIGNATURE, 1088 << 16, FALSE, NTE_BAD_FLAGS},
@@ -261,14 +255,14 @@ static const struct keylength_test dssDH_keylength[] = {
     {CALG_DH_EPHEM, 1024 << 16, TRUE, 0, 0, 1},
     {CALG_DH_EPHEM, 1088 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DH_SF, 448 << 16, FALSE, NTE_BAD_FLAGS},
-    {CALG_DH_SF, 512 << 16, TRUE, 0, 0, 1},
+    {CALG_DH_SF, 512 << 16, TRUE},
     {CALG_DH_SF, 513 << 16, FALSE, NTE_BAD_FLAGS, 0, 0, 1},
-    {CALG_DH_SF, 768 << 16, TRUE, 0, 0, 1},
-    {CALG_DH_SF, 1024 << 16, TRUE, 0, 0, 1},
+    {CALG_DH_SF, 768 << 16, TRUE},
+    {CALG_DH_SF, 1024 << 16, TRUE},
     {CALG_DH_SF, 1088 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DSS_SIGN, 448 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DSS_SIGN, 512 << 16, TRUE},
-    {CALG_DSS_SIGN, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL},
+    {CALG_DSS_SIGN, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL, 0, 1},
     {CALG_DSS_SIGN, 768 << 16, TRUE},
     {CALG_DSS_SIGN, 1024 << 16, TRUE},
     {CALG_DSS_SIGN, 1088 << 16, FALSE, NTE_BAD_FLAGS}
@@ -277,10 +271,10 @@ static const struct keylength_test dssDH_keylength[] = {
 static const struct keylength_test dssENH_keylength[] = {
     /* min 512 max 1024 (AT_KEYEXCHANGE, CALG_DH_EPHEM, CALG_DH_SF max 4096), increment by 64*/
     {AT_KEYEXCHANGE, 448 << 16, FALSE, NTE_BAD_FLAGS},
-    {AT_KEYEXCHANGE, 512 << 16, TRUE, 0, 0, 1},
+    {AT_KEYEXCHANGE, 512 << 16, TRUE},
     {AT_KEYEXCHANGE, 513 << 16, FALSE, NTE_BAD_FLAGS, 0, 0, 1},
-    {AT_KEYEXCHANGE, 768 << 16, TRUE, 0, 0, 1},
-    {AT_KEYEXCHANGE, 1024 << 16, TRUE, 0, 0, 1},
+    {AT_KEYEXCHANGE, 768 << 16, TRUE},
+    {AT_KEYEXCHANGE, 1024 << 16, TRUE},
     {AT_KEYEXCHANGE, 1088 << 16, TRUE, 0, 0, 1},
     {AT_KEYEXCHANGE, 2048 << 16, TRUE, 0, 0, 1},
     /* Keylength too large - test bot timeout.
@@ -289,7 +283,7 @@ static const struct keylength_test dssENH_keylength[] = {
     {AT_KEYEXCHANGE, 4160 << 16, FALSE, NTE_BAD_FLAGS},
     {AT_SIGNATURE, 448 << 16, FALSE, NTE_BAD_FLAGS},
     {AT_SIGNATURE, 512 << 16, TRUE},
-    {AT_SIGNATURE, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL},
+    {AT_SIGNATURE, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL, 0, 1},
     {AT_SIGNATURE, 768 << 16, TRUE},
     {AT_SIGNATURE, 1024 << 16, TRUE},
     {AT_SIGNATURE, 1032 << 16, FALSE, NTE_BAD_FLAGS},
@@ -302,16 +296,16 @@ static const struct keylength_test dssENH_keylength[] = {
     {CALG_DH_EPHEM, 1088 << 16, TRUE, 0, 0, 1},
     {CALG_DH_EPHEM, 4160 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DH_SF, 448 << 16, FALSE, NTE_BAD_FLAGS},
-    {CALG_DH_SF, 512 << 16, TRUE, 0, 0, 1},
+    {CALG_DH_SF, 512 << 16, TRUE},
     {CALG_DH_SF, 513 << 16, FALSE, NTE_BAD_FLAGS, 0, 0, 1},
-    {CALG_DH_SF, 768 << 16, TRUE, 0, 0, 1},
-    {CALG_DH_SF, 1024 << 16, TRUE, 0, 0, 1},
+    {CALG_DH_SF, 768 << 16, TRUE},
+    {CALG_DH_SF, 1024 << 16, TRUE},
     {CALG_DH_SF, 1032 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DH_SF, 1088 << 16, TRUE, 0, 0, 1},
     {CALG_DH_SF, 4160 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DSS_SIGN, 448 << 16, FALSE, NTE_BAD_FLAGS},
     {CALG_DSS_SIGN, 512 << 16, TRUE},
-    {CALG_DSS_SIGN, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL},
+    {CALG_DSS_SIGN, 513 << 16, FALSE, STATUS_INVALID_PARAMETER, NTE_FAIL, 0, 1},
     {CALG_DSS_SIGN, 768 << 16, TRUE},
     {CALG_DSS_SIGN, 1024 << 16, TRUE},
     {CALG_DSS_SIGN, 1088 << 16, FALSE, NTE_BAD_FLAGS}
@@ -355,13 +349,7 @@ static void test_keylength(void)
     BOOL result;
 
     /* acquire base dss provider */
-    result = CryptAcquireContextA(
-        &hProv, NULL, MS_DEF_DSS_PROV_A, PROV_DSS, CRYPT_VERIFYCONTEXT);
-    if(!result)
-    {
-        skip("DSSENH is currently not available, skipping key length tests.\n");
-        return;
-    }
+    result = CryptAcquireContextA(&hProv, NULL, MS_DEF_DSS_PROV_A, PROV_DSS, CRYPT_VERIFYCONTEXT);
     ok(result, "Expected no errors.\n");
 
     result = CryptGenKey(hProv, AT_SIGNATURE, 0, &key);
@@ -452,13 +440,7 @@ static void test_hash(const struct hash_test *tests, int testLen)
     BOOL result;
     int i;
 
-    result = CryptAcquireContextA(
-        &hProv, NULL, MS_ENH_DSS_DH_PROV_A, PROV_DSS_DH, CRYPT_VERIFYCONTEXT);
-    if(!result)
-    {
-        skip("DSSENH is currently not available, skipping hashing tests.\n");
-        return;
-    }
+    result = CryptAcquireContextA(&hProv, NULL, MS_ENH_DSS_DH_PROV_A, PROV_DSS_DH, CRYPT_VERIFYCONTEXT);
     ok(result, "Expected no errors.\n");
 
     for(i = 0; i < testLen; i++)
@@ -485,8 +467,10 @@ static void test_hash(const struct hash_test *tests, int testLen)
         ok(dataLen == hashLen, "Expected hash length to match.\n");
 
         hashLen = 0xdeadbeef;
+        memset(hashValue, 0, sizeof(hashValue));
         result = CryptGetHashParam(hHash, HP_HASHVAL, hashValue, &hashLen, 0);
         ok(result, "Expected hash value return.\n");
+        ok(dataLen == hashLen, "Expected hash length to match.\n");
 
         ok(dataLen == hashLen, "Expected hash length to match.\n");
         ok(!memcmp(hashValue, tests[i].hash, tests[i].hashLen), "Incorrect hash output.\n");
@@ -613,13 +597,7 @@ static void test_data_encryption(const struct encrypt_test *tests, int testLen)
 
     /* acquire dss enhanced provider */
     SetLastError(0xdeadbeef);
-    result = CryptAcquireContextA(
-        &hProv, NULL, MS_ENH_DSS_DH_PROV_A, PROV_DSS_DH, CRYPT_VERIFYCONTEXT);
-    if (!result)
-    {
-        skip("DSSENH is currently not available, skipping encryption tests.\n");
-        return;
-    }
+    result = CryptAcquireContextA(&hProv, NULL, MS_ENH_DSS_DH_PROV_A, PROV_DSS_DH, CRYPT_VERIFYCONTEXT);
     ok(result, "Expected no errors.\n");
 
     /* testing various encryption algorithms */
@@ -709,13 +687,7 @@ static void test_cipher_modes(const struct ciphermode_test *tests, int testLen)
 
     /* acquire dss enhanced provider */
     SetLastError(0xdeadbeef);
-    result = CryptAcquireContextA(
-        &hProv, NULL, MS_ENH_DSS_DH_PROV_A, PROV_DSS_DH, CRYPT_VERIFYCONTEXT);
-    if (!result)
-    {
-        skip("DSSENH is currently not available, skipping block cipher mode tests.\n");
-        return;
-    }
+    result = CryptAcquireContextA(&hProv, NULL, MS_ENH_DSS_DH_PROV_A, PROV_DSS_DH, CRYPT_VERIFYCONTEXT);
     ok(result, "Expected no errors.\n");
 
     SetLastError(0xdeadbeef);
@@ -1063,18 +1035,23 @@ static void test_signhash(HCRYPTPROV hProv, const struct signature_test *test)
     ok(result, "Failed to destroy private key, got %lx\n", GetLastError());
 }
 
+#define MAGIC_DSS1 ('D' | ('S' << 8) | ('S' << 16) | ('1' << 24))
+
 static void test_exportkey(HCRYPTPROV hProv, const struct signature_test *test)
 {
     HCRYPTKEY privKey = 0;
     BYTE pubKeyBuffer[512];
     DWORD pubKeyLen;
     BOOL result;
+    const BLOBHEADER *hdr = (const BLOBHEADER *)pubKeyBuffer;
+    const DSSPUBKEY *dsskey = (const DSSPUBKEY *)(hdr + 1);
 
     /* Get a private key of array specified ALG_ID */
     SetLastError(0xdeadbeef);
     result = CryptImportKey(hProv, test->privateKey, test->privateKeyLen, 0, 0, &privKey);
     ok(result, "Failed to imported key, got %lx\n", GetLastError());
 
+    pubKeyLen = 0;
     SetLastError(0xdeadbeef);
     result = CryptExportKey(privKey, 0, PUBLICKEYBLOB, 0, NULL, &pubKeyLen);
     ok(result, "Failed to acquire public key length, got %lx\n", GetLastError());
@@ -1086,6 +1063,7 @@ static void test_exportkey(HCRYPTPROV hProv, const struct signature_test *test)
     SetLastError(0xdeadbeef);
     result = CryptExportKey(privKey, 0, PUBLICKEYBLOB, 0, pubKeyBuffer, &pubKeyLen);
     ok(result, "Failed to export public key, got %lx\n", GetLastError());
+    ok(dsskey->magic == MAGIC_DSS1, "got %lx\n", dsskey->magic);
 
     ok(memcmp(test->publicKey, pubKeyBuffer, test->publicKeyLen) == 0,
         "The exported public key doesn't match the expected public key\n");
@@ -1140,11 +1118,6 @@ static void test_signature(void)
     /* acquire base dss provider */
     SetLastError(0xdeadbeef);
     result = CryptAcquireContextA(&hProv[0], NULL, MS_DEF_DSS_PROV_A, PROV_DSS, 0);
-    if(!result)
-    {
-        skip("DSSENH is currently not available, skipping signature verification tests.\n");
-        return;
-    }
     ok(result, "Failed to acquire CSP, got %lx\n", GetLastError());
 
     /* acquire diffie hellman dss provider */
@@ -1273,20 +1246,20 @@ static void test_keyExchange_baseDSS(HCRYPTPROV hProv, const struct keyExchange_
 
         /* Generate key exchange keys for user1 and user2 */
         result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey1);
-        if (!result)
-        {
-            skip("skipping key exchange tests\n");
-            return;
-        }
-        ok(!result && GetLastError() == NTE_BAD_ALGID,
+        todo_wine ok(!result && GetLastError() == NTE_BAD_ALGID,
            "Expected NTE_BAD_ALGID, got %lx\n", GetLastError());
 
         result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey2);
-        ok(!result && GetLastError() == NTE_BAD_ALGID,
+        todo_wine ok(!result && GetLastError() == NTE_BAD_ALGID,
            "Expected NTE_BAD_ALGID, got %lx\n", GetLastError());
 
         /* Set the prime and generator values, which are agreed upon */
         result = CryptSetKeyParam(privKey1, KP_P, (BYTE *)&Prime, 0);
+        if (!result)
+        {
+            skip("skipping key exchange tests\n");
+            return;
+        }
         ok(!result && GetLastError() == ERROR_INVALID_PARAMETER,
            "Expected ERROR_INVALID_PARAMETER, got %lx\n", GetLastError());
 
@@ -1427,11 +1400,6 @@ static void test_keyExchange_dssDH(HCRYPTPROV hProv, const struct keyExchange_te
 
         /* Generate key exchange keys for user1 and user2 */
         result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey1);
-        if (!result)
-        {
-            skip("skipping key exchange tests\n");
-            return;
-        }
         ok(result, "Failed to generate a key for user1, got %lx\n", GetLastError());
 
         result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey2);
@@ -1439,6 +1407,11 @@ static void test_keyExchange_dssDH(HCRYPTPROV hProv, const struct keyExchange_te
 
         /* Set the prime and generator values, which are agreed upon */
         result = CryptSetKeyParam(privKey1, KP_P, (BYTE *)&Prime, 0);
+        if (!result)
+        {
+            skip("skipping key exchange tests\n");
+            return;
+        }
         ok(result, "Failed to set prime for user 1's key, got %lx\n", GetLastError());
 
         result = CryptSetKeyParam(privKey2, KP_P, (BYTE *)&Prime, 0);
@@ -1532,11 +1505,6 @@ static void test_key_exchange(void)
 
     /* acquire base dss provider */
     result = CryptAcquireContextA(&hProv, NULL, MS_DEF_DSS_PROV_A, PROV_DSS, CRYPT_VERIFYCONTEXT);
-    if(!result)
-    {
-        skip("DSSENH is currently not available, skipping shared key tests.\n");
-        return;
-    }
     ok(result, "Failed to acquire CSP.\n");
 
     test_keyExchange_baseDSS(hProv, baseDSSkey_data, ARRAY_SIZE(baseDSSkey_data));
diff --git a/dlls/feclient/Makefile.in b/dlls/feclient/Makefile.in
index ce3fb7e491e7..1895d5a7c7d4 100644
--- a/dlls/feclient/Makefile.in
+++ b/dlls/feclient/Makefile.in
@@ -1,3 +1 @@
 MODULE    = feclient.dll
-
-EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/hhctrl.ocx/hhctrl.c b/dlls/hhctrl.ocx/hhctrl.c
index 14e18bbaf984..8bf1a1a83307 100644
--- a/dlls/hhctrl.ocx/hhctrl.c
+++ b/dlls/hhctrl.ocx/hhctrl.c
@@ -377,6 +377,15 @@ HWND WINAPI HtmlHelpW(HWND caller, LPCWSTR filename, UINT command, DWORD_PTR dat
         free(window);
         return 0;
     }
+    case HH_INITIALIZE:
+        /* Return a non-zero cookie that is later passed back to
+         * HH_UNINITIALIZE.  Applications check this value and treat a NULL
+         * cookie as an initialization failure. */
+        if (data)
+            *(DWORD *)data = 1;
+        return 0;
+    case HH_UNINITIALIZE:
+        return 0;
     default:
         FIXME("HH case %s not handled.\n", command_to_string( command ));
     }
diff --git a/dlls/hnetcfg/policy.c b/dlls/hnetcfg/policy.c
index 88de6ed54040..b5306e84c53b 100644
--- a/dlls/hnetcfg/policy.c
+++ b/dlls/hnetcfg/policy.c
@@ -841,6 +841,7 @@ static HRESULT WINAPI enumvar_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var
 {
     FIXME("%p,%lu,%p,%p\n", iface, celt, var, fetched);
 
+    if (fetched) *fetched = 0;
     return  S_FALSE;
 }
 
diff --git a/dlls/ieframe/tests/webbrowser.c b/dlls/ieframe/tests/webbrowser.c
index 521b18bb599a..8c514a6ffc2f 100644
--- a/dlls/ieframe/tests/webbrowser.c
+++ b/dlls/ieframe/tests/webbrowser.c
@@ -2930,18 +2930,20 @@ static void test_QueryStatusWB(IWebBrowser2 *webbrowser, BOOL has_document)
      * is enabled and IDM_STOP is not.
      */
     status = 0xdeadbeef;
-    if (use_container_olecmd) SET_EXPECT(QueryStatus_STOP);
+    if (use_container_olecmd && has_document) SET_EXPECT(QueryStatus_STOP);
     hres = IWebBrowser2_QueryStatusWB(webbrowser, OLECMDID_STOP, &status);
     ok(hres == success_state, "QueryStatusWB failed: %08lx %08lx\n", hres, success_state);
     todo_wine_if (!use_container_olecmd && has_document)
         ok((has_document && status == success_flags) || (!has_document && status == 0xdeadbeef),
            "OLECMDID_STOP not enabled/supported: %08x %08x\n", status, success_flags);
+    if (use_container_olecmd && has_document) CHECK_CALLED(QueryStatus_STOP);
     status = 0xdeadbeef;
-    if (use_container_olecmd) SET_EXPECT(QueryStatus_IDM_STOP);
+    if (use_container_olecmd && has_document) SET_EXPECT(QueryStatus_IDM_STOP);
     hres = IWebBrowser2_QueryStatusWB(webbrowser, IDM_STOP, &status);
     ok(hres == success_state, "QueryStatusWB failed: %08lx %08lx\n", hres, success_state);
     ok((has_document && status == 0) || (!has_document && status == 0xdeadbeef),
        "IDM_STOP is enabled/supported: %08x %d\n", status, has_document);
+    if (use_container_olecmd && has_document) CHECK_CALLED(QueryStatus_IDM_STOP);
 }
 
 static void test_ExecWB(IWebBrowser2 *webbrowser, BOOL has_document)
@@ -2965,24 +2967,29 @@ static void test_ExecWB(IWebBrowser2 *webbrowser, BOOL has_document)
      * These tests show that QueryStatusWB uses a NULL pguidCmdGroup, since OLECMDID_STOP
      * succeeds (S_OK) and IDM_STOP does not (OLECMDERR_E_NOTSUPPORTED).
      */
-    if(use_container_olecmd) {
-        SET_EXPECT(Exec_STOP);
-    }else if(has_document) {
+    if(has_document) {
+        if(use_container_olecmd)
+            SET_EXPECT(Exec_STOP);
         SET_EXPECT(Invoke_STATUSTEXTCHANGE);
         SET_EXPECT(SetStatusText);
     }
     hres = IWebBrowser2_ExecWB(webbrowser, OLECMDID_STOP, OLECMDEXECOPT_DONTPROMPTUSER, 0, 0);
-    if(!use_container_olecmd && has_document) {
-        todo_wine ok(hres == olecmdid_state, "ExecWB failed: %08lx %08lx\n", hres, olecmdid_state);
+    if(has_document) {
+        if(use_container_olecmd)
+            CHECK_CALLED(Exec_STOP);
+        else
+            todo_wine ok(hres == olecmdid_state, "ExecWB failed: %08lx %08lx\n", hres, olecmdid_state);
         CLEAR_CALLED(Invoke_STATUSTEXTCHANGE); /* Called by IE9 */
         CLEAR_CALLED(SetStatusText); /* Called by IE9 */
     }else {
         ok(hres == olecmdid_state, "ExecWB failed: %08lx %08lx\n", hres, olecmdid_state);
     }
-    if (use_container_olecmd)
+    if (use_container_olecmd && has_document)
         SET_EXPECT(Exec_IDM_STOP);
     hres = IWebBrowser2_ExecWB(webbrowser, IDM_STOP, OLECMDEXECOPT_DONTPROMPTUSER, 0, 0);
     ok(hres == idm_state, "ExecWB failed: %08lx %08lx\n", hres, idm_state);
+    if (use_container_olecmd && has_document)
+        CHECK_CALLED(Exec_IDM_STOP);
 }
 
 static void test_download(DWORD flags)
@@ -3900,6 +3907,9 @@ static void test_WebBrowser(DWORD flags, BOOL do_close)
         IHTMLDocument2_Release(doc2);
         IHTMLDocument2_Release(doc);
 
+        hres = IWebBrowser2_Navigate(webbrowser, NULL, NULL, NULL, NULL, NULL);
+        ok(hres == S_FALSE, "Navigate returned: %08lx\n", hres);
+
         if(!do_close) {
             trace("Navigate2 http URL...\n");
             test_ready_state(READYSTATE_COMPLETE, VARIANT_FALSE);
diff --git a/dlls/ieframe/webbrowser.c b/dlls/ieframe/webbrowser.c
index 857287f6311e..1acd6e3f7457 100644
--- a/dlls/ieframe/webbrowser.c
+++ b/dlls/ieframe/webbrowser.c
@@ -326,7 +326,7 @@ static HRESULT WINAPI WebBrowser_Navigate(IWebBrowser2 *iface, BSTR szUrl,
           debugstr_variant(TargetFrameName), debugstr_variant(PostData),
           debugstr_variant(Headers));
 
-    return navigate_url(&This->doc_host, szUrl, Flags, TargetFrameName, PostData, Headers);
+    return szUrl ? navigate_url(&This->doc_host, szUrl, Flags, TargetFrameName, PostData, Headers) : S_FALSE;
 }
 
 static HRESULT WINAPI WebBrowser_Refresh(IWebBrowser2 *iface)
diff --git a/dlls/imagehlp/tests/integrity.c b/dlls/imagehlp/tests/integrity.c
index 4081fa2f6d76..e817176012f8 100644
--- a/dlls/imagehlp/tests/integrity.c
+++ b/dlls/imagehlp/tests/integrity.c
@@ -356,7 +356,10 @@ static void test_pe_checksum(void)
     ret = CheckSumMappedFile(modinfo.lpBaseOfDll, 0, &checksum_orig, &checksum_new);
     ok(!ret || (ret == nt_header), "Expected CheckSumMappedFile to fail, got %p\n", ret);
     ok((checksum_orig == 0xdeadbeef) || (checksum_orig == checksum_correct), "Expected %lx, got %lx\n", checksum_correct, checksum_orig);
-    ok((checksum_new == 0xdeadbeef) || (checksum_new != 0 && checksum_new != 0xdeadbeef), "Got unexpected value %lx\n", checksum_new);
+    ok((checksum_new == 0xdeadbeef) ||
+       (checksum_new != 0 && checksum_new != 0xdeadbeef) ||
+       (ret && ret->OptionalHeader.CheckSum == 0 && checksum_new == 0), /* llvm-mingw creates binaries with zero CheckSum */
+       "Got unexpected value %lx\n", checksum_new);
 
     checksum_orig = checksum_new = 0xdeadbeef;
     ret = CheckSumMappedFile((char *)modinfo.lpBaseOfDll + 1, 0,
diff --git a/dlls/ir50_32/ir50_32.spec b/dlls/ir50_32/ir50_32.spec
index e5c54ef9c56f..e99a6fc41590 100644
--- a/dlls/ir50_32/ir50_32.spec
+++ b/dlls/ir50_32/ir50_32.spec
@@ -1 +1,3 @@
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
 @ stdcall -private DriverProc(long long long long long) IV50_DriverProc
diff --git a/dlls/jscript/tests/activex.c b/dlls/jscript/tests/activex.c
index d10261342953..69336572df18 100644
--- a/dlls/jscript/tests/activex.c
+++ b/dlls/jscript/tests/activex.c
@@ -86,7 +86,6 @@ DEFINE_EXPECT(ProcessUrlAction);
 DEFINE_EXPECT(QueryCustomPolicy);
 DEFINE_EXPECT(reportSuccess);
 DEFINE_EXPECT(Host_QS_SecMgr);
-DEFINE_EXPECT(Caller_QS_SecMgr);
 DEFINE_EXPECT(QI_IObjectWithSite);
 DEFINE_EXPECT(SetSite);
 
@@ -532,10 +531,8 @@ static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
         return E_NOINTERFACE;
 
     if(IsEqualGUID(&SID_SInternetHostSecurityManager, guidService)) {
-        if(iface == &ServiceProvider)
-            CHECK_EXPECT(Host_QS_SecMgr);
-        else
-            CHECK_EXPECT(Caller_QS_SecMgr);
+        ok(iface == &ServiceProvider, "called on non-host iface\n");
+        CHECK_EXPECT(Host_QS_SecMgr);
         ok(IsEqualGUID(&IID_IInternetHostSecurityManager, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid));
         if(SUCCEEDED(QS_SecMgr_hres))
             *ppv = &InternetHostSecurityManager;
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 5bb32a5fd5b0..baf5a705fe24 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -2117,6 +2117,100 @@ static void test_section_access(void)
     }
 }
 
+static void test_security_cookie_readonly(void)
+{
+    /* a PE whose load-config SecurityCookie points into a
+     * read-only section (e.g. .rdata) must still load successfully.  The
+     * loader is expected to temporarily make the page writable, initialize
+     * the cookie and restore the original protection. */
+#ifdef _WIN64
+    static const ULONG_PTR default_cookie = (((ULONG_PTR)0x00002b99 << 32) | 0x2ddfa232);
+    static const WORD reloc_type = IMAGE_REL_BASED_DIR64;
+#else
+    static const ULONG_PTR default_cookie = 0xbb40e64e;
+    static const WORD reloc_type = IMAGE_REL_BASED_HIGHLOW;
+#endif
+    IMAGE_NT_HEADERS nt_header;
+    IMAGE_SECTION_HEADER sections[1];
+    BYTE section_data[0x200];
+    IMAGE_LOAD_CONFIG_DIRECTORY *cfg = (IMAGE_LOAD_CONFIG_DIRECTORY *)section_data;
+    const DWORD cookie_offset = 0x100;  /* inside .rdata, past cfg */
+    const DWORD reloc_offset = 0x180;   /* inside .rdata, past the cookie */
+    ULONG_PTR *cookie_slot = (ULONG_PTR *)(section_data + cookie_offset);
+    IMAGE_BASE_RELOCATION *base_reloc = (IMAGE_BASE_RELOCATION *)(section_data + reloc_offset);
+    WORD *reloc_entries = (WORD *)(section_data + reloc_offset + sizeof(*base_reloc));
+    char dll_name[MAX_PATH];
+    HMODULE hlib;
+    MEMORY_BASIC_INFORMATION info;
+    SIZE_T size;
+    ULONG_PTR final_cookie;
+
+    memset(section_data, 0, sizeof(section_data));
+
+    nt_header = nt_header_template;
+    nt_header.FileHeader.NumberOfSections = 1;
+    nt_header.OptionalHeader.SectionAlignment = page_size;
+    nt_header.OptionalHeader.FileAlignment = 0x200;
+    nt_header.OptionalHeader.SizeOfImage = page_size * 2;
+    nt_header.OptionalHeader.SizeOfHeaders = nt_header.OptionalHeader.FileAlignment;
+
+    memset(sections, 0, sizeof(sections));
+    memcpy(sections[0].Name, ".rdata", 7);
+    sections[0].Misc.VirtualSize = sizeof(section_data);
+    sections[0].VirtualAddress = page_size;
+    sections[0].SizeOfRawData = sizeof(section_data);
+    sections[0].PointerToRawData = nt_header.OptionalHeader.FileAlignment;
+    /* READ only, so no IMAGE_SCN_MEM_WRITE */
+    sections[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+
+    /* place the cookie inside .rdata so the loader decides to write it */
+    *cookie_slot = default_cookie;
+
+    /* fill in the load-config directory at the start of .rdata */
+    cfg->Size = sizeof(*cfg);
+    cfg->SecurityCookie = (ULONG_PTR)nt_header.OptionalHeader.ImageBase
+                          + sections[0].VirtualAddress + cookie_offset;
+
+    /* embed the base-reloc block inside .rdata
+     * One fix-up entry rewrites cfg->SecurityCookie when the loader relocates
+     * the DLL, plus a zero terminator entry (WORD-aligned). */
+    base_reloc->VirtualAddress = sections[0].VirtualAddress;
+    base_reloc->SizeOfBlock = sizeof(*base_reloc) + 2 * sizeof(WORD);
+    reloc_entries[0] = (reloc_type << 12)
+                       | (offsetof(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie) & 0xfff);
+    reloc_entries[1] = 0;
+
+    nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = sections[0].VirtualAddress;
+    nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = sizeof(*cfg);
+    nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = sections[0].VirtualAddress + reloc_offset;
+    nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = base_reloc->SizeOfBlock;
+
+    create_test_dll_sections(&dos_header, &nt_header, sections, section_data, dll_name);
+
+    SetLastError(0xdeadbeef);
+    hlib = LoadLibraryA(dll_name);
+    ok(hlib != NULL, "LoadLibrary failed err %lu\n", GetLastError());
+    if (!hlib)
+    {
+        DeleteFileA(dll_name);
+        return;
+    }
+
+    final_cookie = *(ULONG_PTR *)((char *)hlib + sections[0].VirtualAddress + cookie_offset);
+    ok(final_cookie != default_cookie,
+       "security cookie was not initialized (still %#Ix)\n", final_cookie);
+
+    /* page protection should be restored to PAGE_READONLY after cookie init */
+    size = VirtualQuery((char *)hlib + sections[0].VirtualAddress, &info, sizeof(info));
+    ok(size == sizeof(info), "VirtualQuery error %lu\n", GetLastError());
+    ok(info.Protect == PAGE_READONLY,
+       "section protection not restored: got %#lx, expected PAGE_READONLY\n",
+       info.Protect);
+
+    FreeLibrary(hlib);
+    DeleteFileA(dll_name);
+}
+
 static void check_tls_index(HANDLE dll, BOOL tls_initialized)
 {
     BOOL found_dll = FALSE;
@@ -4913,6 +5007,7 @@ START_TEST(loader)
     test_ResolveDelayLoadedAPI();
     test_ImportDescriptors();
     test_section_access();
+    test_security_cookie_readonly();
     test_import_resolution();
     test_export_forwarder_dep_chain();
     test_ExitProcess();
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index 935eba3fbab1..74b03ec8f544 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -166,7 +166,9 @@ static void test_VirtualAllocEx(void)
     if (GetLastError() == ERROR_NOACCESS)
         ok( bytes_read == 0, "%Iu bytes read\n", bytes_read );
     else
-        ok( bytes_read == 0x2000, "%Iu bytes read\n", bytes_read );
+        ok( bytes_read == 0x2000 ||
+            broken(LOWORD(bytes_read) == 0x2000 || bytes_read == 0xffffffff) /* Win10 1607/1709 */,
+            "%Iu bytes read\n", bytes_read );
     bytes_written = 0xdeadbeef;
     status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written );
     ok( status == STATUS_PARTIAL_COPY, "wrong status %lx\n", status );
@@ -174,7 +176,9 @@ static void test_VirtualAllocEx(void)
     bytes_read = 0xdeadbeef;
     status = pNtReadVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_read );
     ok( status == STATUS_PARTIAL_COPY || status == STATUS_ACCESS_VIOLATION, "wrong status %lx\n", status );
-    ok( bytes_read == (status == STATUS_PARTIAL_COPY ? 0x2000 : 0), "%Iu bytes read\n", bytes_read );
+    ok( bytes_read == (status == STATUS_PARTIAL_COPY ? 0x2000 : 0) ||
+        broken(LOWORD(bytes_read) == (status == STATUS_PARTIAL_COPY ? 0x2000 : 0) || bytes_read == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes read\n", bytes_read );
 
     b = VirtualProtect( src, 0x2000, PAGE_NOACCESS, &old_prot );
     ok( b, "VirtualProtect failed error %lu\n", GetLastError() );
@@ -191,7 +195,9 @@ static void test_VirtualAllocEx(void)
     ok( GetLastError() == ERROR_NOACCESS ||
         GetLastError() == ERROR_PARTIAL_COPY, /* win10 v1607+ */
         "wrong error %lu\n", GetLastError() );
-    ok( bytes_read == 0, "%Iu bytes read\n", bytes_read );
+    ok( bytes_read == 0 ||
+        broken(LOWORD(bytes_read) == 0 || bytes_read == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes read\n", bytes_read );
     bytes_written = 0xdeadbeef;
     status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written );
     ok( status == STATUS_PARTIAL_COPY, "wrong status %lx\n", status );
@@ -199,7 +205,9 @@ static void test_VirtualAllocEx(void)
     bytes_read = 0xdeadbeef;
     status = pNtReadVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_read );
     ok( status == STATUS_PARTIAL_COPY || status == STATUS_ACCESS_VIOLATION, "wrong status %lx\n", status );
-    ok( bytes_read == 0, "%Iu bytes read\n", bytes_read );
+    ok( bytes_read == 0 ||
+        broken(LOWORD(bytes_read) == 0 || bytes_read == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes read\n", bytes_read );
     b = VirtualProtect( src, alloc_size, PAGE_READWRITE, &old_prot );
     ok( b, "VirtualProtect failed error %lu\n", GetLastError() );
 
@@ -217,21 +225,27 @@ static void test_VirtualAllocEx(void)
     ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_ACCESS_VIOLATION),
         "wrong status %lx\n", status );
     todo_wine_if(status == STATUS_SUCCESS)
-    ok( bytes_written == 0, "%Iu bytes written\n", bytes_written );
+    ok( bytes_written == 0 ||
+        broken(LOWORD(bytes_written) == 0 || bytes_written == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes written\n", bytes_written );
 
     b = VirtualProtectEx( hProcess, addr1, alloc_size, PAGE_EXECUTE_READ, &old_prot );
     ok( b, "VirtualProtectEx, error %lu\n", GetLastError() );
     bytes_written = 0xdeadbeef;
     b = WriteProcessMemory(hProcess, addr1, src, alloc_size, &bytes_written);
-    ok( b, "WriteProcessMemory failed\n" );
-    ok( bytes_written == alloc_size, "%Iu bytes written\n", bytes_written );
+    ok( b ||
+        broken(b == 0 && GetLastError() == ERROR_NOACCESS) /* Win10 1607 */,
+        "WriteProcessMemory failed GetLastError()=%ld\n", GetLastError() );
+    if (b) ok( bytes_written == alloc_size, "%Iu bytes written\n", bytes_written );
     bytes_written = 0xdeadbeef;
     status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written );
     todo_wine_if(status == STATUS_SUCCESS)
     ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_ACCESS_VIOLATION),
         "wrong status %lx\n", status );
     todo_wine_if(status == STATUS_SUCCESS)
-    ok( bytes_written == 0, "%Iu bytes written\n", bytes_written );
+    ok( bytes_written == 0 ||
+        broken(LOWORD(bytes_written) == 0 || bytes_written == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes written\n", bytes_written );
 
     b = VirtualProtectEx( hProcess, addr1, 0x2000, PAGE_EXECUTE_READWRITE, &old_prot );
     ok( b, "VirtualProtectEx, error %lu\n", GetLastError() );
@@ -244,7 +258,9 @@ static void test_VirtualAllocEx(void)
     todo_wine_if(status == STATUS_SUCCESS)
     ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_SUCCESS), /* <= win10 1507 */
         "wrong status %lx\n", status );
-    ok( bytes_written == (status ? 0x2000 : alloc_size), "%Iu bytes written\n", bytes_written );
+    ok( bytes_written == (status ? 0x2000 : alloc_size) ||
+        broken(LOWORD(bytes_written) == (status ? 0x2000 : alloc_size) || bytes_written == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes written\n", bytes_written );
 
     b = VirtualProtectEx( hProcess, (char *)addr1 + 0x2000, alloc_size - 0x2000, PAGE_READONLY, &old_prot );
     ok( b, "VirtualProtectEx, error %lu\n", GetLastError() );
@@ -256,7 +272,9 @@ static void test_VirtualAllocEx(void)
     todo_wine_if(b)
     ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_SUCCESS), /* <= win10 1507 */
         "wrong status %lx\n", status );
-    ok( bytes_written == (status ? 0x2000 : alloc_size), "%Iu bytes written\n", bytes_written );
+    ok( bytes_written == (status ? 0x2000 : alloc_size) ||
+        broken(LOWORD(bytes_written) == (status ? 0x2000 : alloc_size) || bytes_written == 0xffffffff) /* Win10 1607/1709 */,
+        "%Iu bytes written\n", bytes_written );
 
     VirtualFree( src, 0, MEM_RELEASE );
     VirtualFree( dst, 0, MEM_RELEASE );
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 2c5906a29c22..6c1d14e7dca6 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -569,7 +569,7 @@
 @ stdcall GetGeoInfoEx(ptr long ptr long)
 @ stdcall GetHandleInformation(long ptr)
 # @ stub GetHivePath
-# @ stub GetIntegratedDisplaySize
+@ stdcall GetIntegratedDisplaySize(ptr)
 # @ stub GetIsEdpEnabled
 @ stdcall GetKernelObjectSecurity(long long ptr long ptr)
 @ stdcall GetLargePageMinimum()
diff --git a/dlls/kernelbase/main.c b/dlls/kernelbase/main.c
index 456bcf5e7d78..8c37e5da1ab5 100644
--- a/dlls/kernelbase/main.c
+++ b/dlls/kernelbase/main.c
@@ -635,3 +635,9 @@ HRESULT WINAPI GetAcceptLanguagesW(WCHAR *langbuf, DWORD *buflen)
     *buflen = 0;
     return E_NOT_SUFFICIENT_BUFFER;
 }
+
+HRESULT WINAPI GetIntegratedDisplaySize( double *sz_inches )
+{
+    FIXME( "%p stub.\n", sz_inches );
+    return E_NOTIMPL;
+}
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c
index 5b7420054cfd..b92652dcfd3c 100644
--- a/dlls/mf/tests/transform.c
+++ b/dlls/mf/tests/transform.c
@@ -10265,25 +10265,64 @@ static void test_video_processor(BOOL use_2d_buffer)
         ok(hr == S_OK, "IsEqual returned %#lx.\n", hr);
         IMFMediaType_Release(media_type2);
 
-        ret = IMFMediaType_Release(media_type);
-        ok(ret == 1, "Release returned %lu\n", ret);
-
         j = k = 0;
-        while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++j, &media_type)))
+        while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++j, &media_type2)))
         {
+            UINT64 tmp = 0xdeadbeef;
             winetest_push_context("out %lu", j);
             ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr);
-            check_media_type(media_type, expect_available_common, -1);
+            check_media_type(media_type2, expect_available_common, -1);
 
-            hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &guid);
+            hr = IMFMediaType_GetGUID(media_type2, &MF_MT_SUBTYPE, &guid);
             ok(hr == S_OK, "GetGUID returned %#lx\n", hr);
 
+            hr = IMFMediaType_GetUINT64(media_type2, &MF_MT_FRAME_SIZE, &tmp);
+            ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected frame size: %#lx %#I64x\n", hr, tmp);
+
             for (; k < ARRAY_SIZE(expect_available_outputs); k++)
                 if (IsEqualGUID(&expect_available_outputs[k], &guid))
                     break;
             todo_wine_if(IsEqualGUID(&guid, &MFVideoFormat_ABGR32)) /* enumerated on purpose on Wine */
             ok(k < ARRAY_SIZE(expect_available_outputs), "got subtype %s\n", debugstr_guid(&guid));
 
+            ret = IMFMediaType_Release(media_type2);
+            ok(ret == 0, "Release returned %lu\n", ret);
+            winetest_pop_context();
+        }
+        ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr);
+
+        /* How does video processor react to pixel aspect ratio? */
+        hr = IMFMediaType_SetUINT64(media_type, &MF_MT_PIXEL_ASPECT_RATIO, (UINT64)3 << 32 | 4);
+        ok(hr == S_OK, "SetUINT64 returned %#lx.\n", hr);
+        hr = IMFTransform_SetInputType(transform, 0, media_type, 0);
+        ok(hr == S_OK, "SetInputType returned %#lx.\n", hr);
+
+        hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type2);
+        ok(hr == S_OK, "GetOutputAvailableType returned %#lx.\n", hr);
+        hr = IMFMediaType_IsEqual(media_type, media_type2, &flags);
+        ok(hr == S_OK, "IsEqual returned %#lx.\n", hr);
+        IMFMediaType_Release(media_type2);
+
+        ret = IMFMediaType_Release(media_type);
+        ok(ret == 1, "Release returned %lu\n", ret);
+
+        j = 0;
+        while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++j, &media_type)))
+        {
+            UINT64 tmp = 0xdeadbeef;
+            winetest_push_context("out %lu", j);
+            ok(hr == S_OK, "GetOutputAvailableType returned %#lx\n", hr);
+            check_media_type(media_type, expect_available_common, -1);
+
+            hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &guid);
+            ok(hr == S_OK, "GetGUID returned %#lx\n", hr);
+
+            hr = IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &tmp);
+            ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected frame size: %#lx %#I64x\n", hr, tmp);
+
+            hr = IMFMediaType_GetUINT64(media_type, &MF_MT_PIXEL_ASPECT_RATIO, &tmp);
+            ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected PAR: %#lx %#I64x\n", hr, tmp);
+
             ret = IMFMediaType_Release(media_type);
             ok(ret == 0, "Release returned %lu\n", ret);
             winetest_pop_context();
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index 6763ee868879..f0716cc9abb3 100644
--- a/dlls/mfplat/main.c
+++ b/dlls/mfplat/main.c
@@ -6351,7 +6351,7 @@ static HRESULT resolver_get_bytestream_handler(IMFByteStream *stream, const WCHA
     if (SUCCEEDED(hr))
         return hr;
 
-    if (!(flags & MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE))
+    if (url_ext && !(flags & MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE))
         return MF_E_UNSUPPORTED_BYTESTREAM_TYPE;
 
     if (FAILED(hr = resolver_get_bytestream_url_hint(stream, &url_ext)))
@@ -8912,6 +8912,405 @@ HRESULT WINAPI CreatePropertyStore(IPropertyStore **store)
     return S_OK;
 }
 
+struct d3d12_sync_object_release
+{
+    struct list entry;
+    ID3D12Fence *fence;
+    HANDLE event;
+};
+
+struct d3d12_sync_object
+{
+    IMFD3D12SynchronizationObject IMFD3D12SynchronizationObject_iface;
+    IMFD3D12SynchronizationObjectCommands IMFD3D12SynchronizationObjectCommands_iface;
+    IRtwqAsyncCallback async_release_iface;
+    LONG refcount;
+    CRITICAL_SECTION cs;
+    ID3D12Device *device;
+    ID3D12Fence *ready_fence;
+    UINT64 generation;
+    unsigned int release_wait;
+    HANDLE final_release_event;
+    struct list release_freelist;
+    struct list *release_freelist_cursor;
+};
+
+static struct d3d12_sync_object *impl_from_IMFD3D12SynchronizationObject(IMFD3D12SynchronizationObject *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_sync_object, IMFD3D12SynchronizationObject_iface);
+}
+
+static struct d3d12_sync_object *impl_from_IMFD3D12SynchronizationObjectCommands(IMFD3D12SynchronizationObjectCommands *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_sync_object, IMFD3D12SynchronizationObjectCommands_iface);
+}
+
+static struct d3d12_sync_object *d3d12_sync_object_from_IRtwqAsyncCallback(IRtwqAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_sync_object, async_release_iface);
+}
+
+static HRESULT WINAPI d3d12_sync_object_QueryInterface(IMFD3D12SynchronizationObject *iface, REFIID riid, void **obj)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface);
+
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFD3D12SynchronizationObject) || IsEqualIID(riid, &IID_IUnknown))
+        *obj = &syncobj->IMFD3D12SynchronizationObject_iface;
+    else if (IsEqualIID(riid, &IID_IMFD3D12SynchronizationObjectCommands))
+        *obj = &syncobj->IMFD3D12SynchronizationObjectCommands_iface;
+    else
+    {
+        *obj = NULL;
+        WARN("Unsupported interface %s.\n", debugstr_guid(riid));
+        return E_NOINTERFACE;
+    }
+
+    IMFD3D12SynchronizationObject_AddRef(&syncobj->IMFD3D12SynchronizationObject_iface);
+    return S_OK;
+}
+
+static ULONG WINAPI d3d12_sync_object_AddRef(IMFD3D12SynchronizationObject *iface)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface);
+    ULONG refcount = InterlockedIncrement(&syncobj->refcount);
+
+    TRACE("%p, refcount %ld.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI d3d12_sync_object_Release(IMFD3D12SynchronizationObject *iface)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface);
+    ULONG refcount = InterlockedDecrement(&syncobj->refcount);
+
+    TRACE("%p, refcount %ld.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_sync_object_release *cursor, *cursor2;
+
+        LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &syncobj->release_freelist,
+                struct d3d12_sync_object_release, entry)
+        {
+            if (cursor->fence)
+                ID3D12Fence_Release(cursor->fence);
+            CloseHandle(cursor->event);
+            free(cursor);
+        }
+
+        ID3D12Device_Release(syncobj->device);
+        ID3D12Fence_Release(syncobj->ready_fence);
+        DeleteCriticalSection(&syncobj->cs);
+        free(syncobj);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI d3d12_sync_object_SignalEventOnFinalResourceRelease(IMFD3D12SynchronizationObject *iface, HANDLE event)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface);
+
+    TRACE("%p, %p.\n", iface, event);
+
+    if (!event)
+        return E_INVALIDARG;
+
+    EnterCriticalSection(&syncobj->cs);
+    if (syncobj->release_wait)
+        syncobj->final_release_event = event;
+    else
+        SetEvent(event);
+    LeaveCriticalSection(&syncobj->cs);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI d3d12_sync_object_Reset(IMFD3D12SynchronizationObject *iface)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p.\n", iface);
+
+    EnterCriticalSection(&syncobj->cs);
+    if (syncobj->release_wait)
+        hr = MF_E_UNEXPECTED;
+    else
+    {
+        ++syncobj->generation;
+        syncobj->release_freelist_cursor = &syncobj->release_freelist;
+    }
+    LeaveCriticalSection(&syncobj->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI d3d12_sync_object_commands_QueryInterface(IMFD3D12SynchronizationObjectCommands *iface, REFIID riid, void **obj)
+{
+    return d3d12_sync_object_QueryInterface(&impl_from_IMFD3D12SynchronizationObjectCommands(iface)->IMFD3D12SynchronizationObject_iface, riid, obj);
+}
+
+static ULONG WINAPI d3d12_sync_object_commands_AddRef(IMFD3D12SynchronizationObjectCommands *iface)
+{
+    return d3d12_sync_object_AddRef(&impl_from_IMFD3D12SynchronizationObjectCommands(iface)->IMFD3D12SynchronizationObject_iface);
+}
+
+static ULONG WINAPI d3d12_sync_object_commands_Release(IMFD3D12SynchronizationObjectCommands *iface)
+{
+    return d3d12_sync_object_Release(&impl_from_IMFD3D12SynchronizationObjectCommands(iface)->IMFD3D12SynchronizationObject_iface);
+}
+
+static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceReady(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *producer_queue)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface);
+    HRESULT hr;
+
+    TRACE("%p, %p.\n", iface, producer_queue);
+
+    if (!producer_queue)
+        return E_INVALIDARG;
+
+    EnterCriticalSection(&syncobj->cs);
+    hr = ID3D12CommandQueue_Signal(producer_queue, syncobj->ready_fence, syncobj->generation);
+    LeaveCriticalSection(&syncobj->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceReadyWait(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *consumer_queue)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface);
+    HRESULT hr;
+
+    TRACE("%p, %p.\n", iface, consumer_queue);
+
+    if (!consumer_queue)
+        return E_INVALIDARG;
+
+    EnterCriticalSection(&syncobj->cs);
+    hr = ID3D12CommandQueue_Wait(consumer_queue, syncobj->ready_fence, syncobj->generation);
+    LeaveCriticalSection(&syncobj->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI d3d12_sync_object_commands_SignalEventOnResourceReady(IMFD3D12SynchronizationObjectCommands *iface, HANDLE event)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface);
+    HRESULT hr;
+    UINT64 generation;
+
+    TRACE("%p, %p.\n", iface, event);
+
+    EnterCriticalSection(&syncobj->cs);
+    generation = syncobj->generation;
+    LeaveCriticalSection(&syncobj->cs);
+
+    hr = ID3D12Fence_SetEventOnCompletion(syncobj->ready_fence, generation, event);
+    return hr;
+}
+
+static HRESULT d3d12_sync_object_create_release(struct d3d12_sync_object *syncobj, struct d3d12_sync_object_release **out)
+{
+    struct list *next;
+    struct d3d12_sync_object_release *relobj;
+    HRESULT hr;
+
+    next = list_next(&syncobj->release_freelist, syncobj->release_freelist_cursor);
+    if (next)
+    {
+        relobj = LIST_ENTRY(next, struct d3d12_sync_object_release, entry);
+    }
+    else
+    {
+        relobj = calloc(1, sizeof(*relobj));
+        if (!relobj)
+            return E_OUTOFMEMORY;
+        relobj->event = CreateEventA(NULL, FALSE, FALSE, NULL);
+        list_add_after(syncobj->release_freelist_cursor, &relobj->entry);
+    }
+
+    if (!relobj->fence)
+    {
+        hr = ID3D12Device_CreateFence(syncobj->device, 0, D3D12_FENCE_FLAG_SHARED, &IID_ID3D12Fence, (void **) &relobj->fence);
+        if (FAILED(hr))
+            return hr;
+    }
+
+    *out = relobj;
+    return S_OK;
+}
+
+static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceRelease(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *consumer_queue)
+{
+    struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface);
+    struct d3d12_sync_object_release *relobj;
+    IRtwqAsyncResult *result;
+    RTWQWORKITEM_KEY key;
+    HRESULT hr;
+
+    TRACE("%p, %p.\n", iface, consumer_queue);
+
+    if (!consumer_queue)
+        return E_INVALIDARG;
+
+    EnterCriticalSection(&syncobj->cs);
+    hr = d3d12_sync_object_create_release(syncobj, &relobj);
+    if (FAILED(hr))
+        goto end;
+
+    hr = RtwqCreateAsyncResult(NULL, &syncobj->async_release_iface, NULL, &result);
+    if (FAILED(hr))
+        goto end;
+
+    hr = RtwqPutWaitingWorkItem(relobj->event, 0, result, &key);
+    IRtwqAsyncResult_Release(result);
+    if (FAILED(hr))
+        goto end;
+
+    hr = ID3D12CommandQueue_Signal(consumer_queue, relobj->fence, syncobj->generation);
+    if (FAILED(hr))
+    {
+        RtwqCancelWorkItem(key);
+        goto end;
+    }
+
+    hr = ID3D12Fence_SetEventOnCompletion(relobj->fence, syncobj->generation, relobj->event);
+    if (FAILED(hr))
+    {
+        RtwqCancelWorkItem(key);
+        /* can't re-use fence since queue might signal it */
+        ID3D12Fence_Release(relobj->fence);
+        relobj->fence = NULL;
+        goto end;
+    }
+
+    ++syncobj->release_wait;
+    syncobj->release_freelist_cursor = &relobj->entry;
+
+end:
+    LeaveCriticalSection(&syncobj->cs);
+    return hr;
+}
+
+static HRESULT WINAPI d3d12_sync_object_async_release_QueryInterface(IRtwqAsyncCallback *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IRtwqAsyncCallback))
+    {
+        *obj = iface;
+        IRtwqAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    *obj = NULL;
+    WARN("Unsupported interface %s.\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI d3d12_sync_object_async_release_AddRef(IRtwqAsyncCallback *iface)
+{
+    return d3d12_sync_object_AddRef(&d3d12_sync_object_from_IRtwqAsyncCallback(iface)->IMFD3D12SynchronizationObject_iface);
+}
+
+static ULONG WINAPI d3d12_sync_object_async_release_Release(IRtwqAsyncCallback *iface)
+{
+    return d3d12_sync_object_Release(&d3d12_sync_object_from_IRtwqAsyncCallback(iface)->IMFD3D12SynchronizationObject_iface);
+}
+
+static HRESULT WINAPI d3d12_sync_object_async_release_GetParameters(IRtwqAsyncCallback *iface, DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI d3d12_sync_object_async_release_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result)
+{
+    struct d3d12_sync_object *syncobj = d3d12_sync_object_from_IRtwqAsyncCallback(iface);
+
+    TRACE("%p, %p.\n", iface, result);
+
+    EnterCriticalSection(&syncobj->cs);
+    if (!--syncobj->release_wait && syncobj->final_release_event)
+    {
+        SetEvent(syncobj->final_release_event);
+        syncobj->final_release_event = NULL;
+    }
+    LeaveCriticalSection(&syncobj->cs);
+    return S_OK;
+}
+
+static const IMFD3D12SynchronizationObjectVtbl d3d12_sync_object_vtbl =
+{
+    d3d12_sync_object_QueryInterface,
+    d3d12_sync_object_AddRef,
+    d3d12_sync_object_Release,
+    d3d12_sync_object_SignalEventOnFinalResourceRelease,
+    d3d12_sync_object_Reset,
+};
+
+static const IMFD3D12SynchronizationObjectCommandsVtbl d3d12_sync_object_commands_vtbl =
+{
+    d3d12_sync_object_commands_QueryInterface,
+    d3d12_sync_object_commands_AddRef,
+    d3d12_sync_object_commands_Release,
+    d3d12_sync_object_commands_EnqueueResourceReady,
+    d3d12_sync_object_commands_EnqueueResourceReadyWait,
+    d3d12_sync_object_commands_SignalEventOnResourceReady,
+    d3d12_sync_object_commands_EnqueueResourceRelease,
+};
+
+static const IRtwqAsyncCallbackVtbl d3d12_sync_object_async_release_vtbl =
+{
+    d3d12_sync_object_async_release_QueryInterface,
+    d3d12_sync_object_async_release_AddRef,
+    d3d12_sync_object_async_release_Release,
+    d3d12_sync_object_async_release_GetParameters,
+    d3d12_sync_object_async_release_Invoke,
+};
+
+/***********************************************************************
+ *      MFCreateD3D12SynchronizationObject (mfplat.@)
+ */
+
+HRESULT WINAPI MFCreateD3D12SynchronizationObject(ID3D12Device *device, REFIID riid, void **obj)
+{
+    HRESULT hr;
+    struct d3d12_sync_object *syncobj;
+
+    if (!obj)
+        return E_INVALIDARG;
+
+    syncobj = calloc(1, sizeof(*syncobj));
+    if (!syncobj)
+        return E_OUTOFMEMORY;
+
+    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_SHARED, &IID_ID3D12Fence, (void **) &syncobj->ready_fence);
+    if (FAILED(hr))
+    {
+        free(syncobj);
+        return hr;
+    }
+
+    syncobj->refcount = 1;
+    syncobj->IMFD3D12SynchronizationObject_iface.lpVtbl = &d3d12_sync_object_vtbl;
+    syncobj->IMFD3D12SynchronizationObjectCommands_iface.lpVtbl = &d3d12_sync_object_commands_vtbl;
+    syncobj->async_release_iface.lpVtbl = &d3d12_sync_object_async_release_vtbl;
+    InitializeCriticalSection(&syncobj->cs);
+    ID3D12Device_AddRef(device);
+    syncobj->device = device;
+    syncobj->generation = 1;
+    list_init(&syncobj->release_freelist);
+    syncobj->release_freelist_cursor = &syncobj->release_freelist;
+
+    hr = IMFD3D12SynchronizationObject_QueryInterface(&syncobj->IMFD3D12SynchronizationObject_iface, riid, obj);
+    IMFD3D12SynchronizationObject_Release(&syncobj->IMFD3D12SynchronizationObject_iface);
+
+    return hr;
+}
+
 struct shared_dxgi_manager
 {
     IMFDXGIDeviceManager *manager;
diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec
index 229230cd8992..9f653d70df33 100644
--- a/dlls/mfplat/mfplat.spec
+++ b/dlls/mfplat/mfplat.spec
@@ -45,6 +45,7 @@
 @ stdcall MFCreateAttributes(ptr long)
 @ stdcall MFCreateAudioMediaType(ptr ptr)
 @ stdcall MFCreateCollection(ptr)
+@ stdcall MFCreateD3D12SynchronizationObject(ptr ptr ptr)
 @ stdcall MFCreateDXGIDeviceManager(ptr ptr)
 @ stdcall MFCreateDXGISurfaceBuffer(ptr ptr long long ptr)
 @ stdcall MFCreateDXSurfaceBuffer(ptr ptr long ptr)
diff --git a/dlls/mfplat/tests/Makefile.in b/dlls/mfplat/tests/Makefile.in
index 217ea437b3cf..898420268e3e 100644
--- a/dlls/mfplat/tests/Makefile.in
+++ b/dlls/mfplat/tests/Makefile.in
@@ -1,5 +1,5 @@
 TESTDLL   = mfplat.dll
-IMPORTS   = ole32 mfplat user32 d3d9 dxva2 mfuuid propsys uuid strmiids
+IMPORTS   = ole32 mfplat user32 d3d9 dxva2 mfuuid propsys uuid strmiids kernelbase
 
 SOURCES = \
 	mfplat.c \
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index ab5a997dbf5a..2a8af296c915 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -29,6 +29,7 @@
 #include "winuser.h"
 #include "winreg.h"
 #include "ole2.h"
+#include "pathcch.h"
 #include "ks.h"
 #include "ksmedia.h"
 #include "amvideo.h"
@@ -272,6 +273,13 @@ static void check_platform_lock_count_(unsigned int line, unsigned int expected)
     ok_(__FILE__, line)(count == expected, "Unexpected lock count %d.\n", count);
 }
 
+static void path_copy_replace_filename(WCHAR *dst_path, size_t size, const WCHAR *src_path, const WCHAR *filename)
+{
+    lstrcpyW(dst_path, src_path);
+    PathCchRemoveFileSpec(dst_path, size);
+    PathCchAppend(dst_path, size, filename);
+}
+
 struct d3d9_surface_readback
 {
     IDirect3DSurface9 *surface, *readback_surface;
@@ -561,6 +569,7 @@ static HRESULT (WINAPI *pMFLockSharedWorkQueue)(const WCHAR *name, LONG base_pri
 static HRESULT (WINAPI *pMFLockDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager);
 static HRESULT (WINAPI *pMFUnlockDXGIDeviceManager)(void);
 static HRESULT (WINAPI *pMFInitVideoFormat_RGB)(MFVIDEOFORMAT *format, DWORD width, DWORD height, DWORD d3dformat);
+static HRESULT (WINAPI *pMFCreateD3D12SynchronizationObject)(ID3D12Device *device, REFIID riid, void **obj);
 
 static HRESULT (WINAPI *pRtwqStartup)(void);
 static HRESULT (WINAPI *pRtwqShutdown)(void);
@@ -1396,6 +1405,65 @@ static void test_source_resolver(void)
     hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &stream);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
+    path_copy_replace_filename(pathW, ARRAY_SIZE(pathW), filename, L"noextension");
+
+    hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, pathW, MF_RESOLUTION_MEDIASOURCE, NULL,
+            &obj_type, (IUnknown **)&mediasource);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        IMFMediaSource_Shutdown(mediasource);
+        IMFMediaSource_Release(mediasource);
+    }
+    IMFByteStream_Release(stream);
+
+    hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &stream);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    /* MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE is not required if mime mismatches. */
+    hr = IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFAttributes_SetString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, L"video/avi");
+    ok(hr == S_OK, "Failed to set string value, hr %#lx.\n", hr);
+    IMFAttributes_Release(attributes);
+
+    hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, pathW, MF_RESOLUTION_MEDIASOURCE, NULL,
+            &obj_type, (IUnknown **)&mediasource);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    IMFMediaSource_Shutdown(mediasource);
+    IMFMediaSource_Release(mediasource);
+    IMFByteStream_Release(stream);
+
+    hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &stream);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    path_copy_replace_filename(pathW, ARRAY_SIZE(pathW), filename, L"temp.foo");
+
+    hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, pathW, MF_RESOLUTION_MEDIASOURCE, NULL,
+            &obj_type, (IUnknown **)&mediasource);
+    todo_wine
+    ok(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE, "Unexpected hr %#lx.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        IMFMediaSource_Shutdown(mediasource);
+        IMFMediaSource_Release(mediasource);
+    }
+    IMFByteStream_Release(stream);
+
+    hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &stream);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, pathW,
+            MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
+            NULL, &obj_type, (IUnknown **)&mediasource);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    IMFMediaSource_Shutdown(mediasource);
+    IMFMediaSource_Release(mediasource);
+    IMFByteStream_Release(stream);
+
+    hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &stream);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
     hr = IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     hr = IMFAttributes_SetString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, L"video/mp4");
@@ -1749,6 +1817,8 @@ static void test_source_resolver(void)
             (void **)&scheme_handler);
     ok(hr == S_OK, "Failed to create handler object, hr %#lx.\n", hr);
 
+    lstrcpyW(pathW, fileschemeW);
+    lstrcatW(pathW, filename);
     cancel_cookie = NULL;
     hr = IMFSchemeHandler_BeginCreateObject(scheme_handler, pathW, MF_RESOLUTION_MEDIASOURCE, NULL, &cancel_cookie,
             &callback2->IMFAsyncCallback_iface, (IUnknown *)scheme_handler);
@@ -1816,6 +1886,7 @@ static void init_functions(void)
     X(MFTUnregisterLocal);
     X(MFTUnregisterLocalByCLSID);
     X(MFUnlockDXGIDeviceManager);
+    X(MFCreateD3D12SynchronizationObject);
 
     if ((mod = LoadLibraryA("d3d11.dll")))
     {
@@ -11458,6 +11529,571 @@ notsupported:
     ok(!refcount, "Unexpected device refcount %u.\n", refcount);
 }
 
+static DWORD WINAPI test_d3d12_sync_object_thread(LPVOID param)
+{
+    HRESULT hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(param, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    return 0;
+}
+
+static void test_d3d12_sync_object(void)
+{
+    IMFD3D12SynchronizationObject *sync_obj;
+    IMFD3D12SynchronizationObjectCommands *sync_cmd;
+    IUnknown *unk;
+    ID3D12Device *device, *device2;
+    ID3D12CommandQueue *queue, *queue2;
+    ID3D12Fence *fence, *fence2;
+    HANDLE event, event2, event3, event4, thread;
+    D3D12_COMMAND_QUEUE_DESC queue_desc = { .Type = D3D12_COMMAND_LIST_TYPE_DIRECT };
+    DWORD status;
+    unsigned int refcount;
+    HRESULT hr;
+
+    /* d3d12 */
+    if (!(device = create_d3d12_device()))
+    {
+        skip("Failed to create a D3D12 device, skipping tests.\n");
+        return;
+    }
+
+    if (!pMFCreateD3D12SynchronizationObject)
+    {
+        win_skip("MFCreateD3D12SynchronizationObject() is not available.\n");
+        goto notsupported;
+    }
+
+    hr = ID3D12Device_CreateCommandQueue(device, &queue_desc, &IID_ID3D12CommandQueue, (void **) &queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateCommandQueue(device, &queue_desc, &IID_ID3D12CommandQueue, (void **) &queue2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    event = CreateEventA(NULL, FALSE,  FALSE, NULL);
+    event2 = CreateEventA(NULL, FALSE,  FALSE, NULL);
+
+    /* MFCreateD3D12SynchronizationObject */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IUnknown, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IUnknown, (void **) &unk);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IUnknown_QueryInterface(unk, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IUnknown_QueryInterface(unk, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    IUnknown_Release(unk);
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObject_QueryInterface(sync_obj, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* invalid arguments */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* EnqueueResourceReady / EnqueueResourceReadyWait */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12Fence_SetEventOnCompletion(fence2, 1, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, queue2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12CommandQueue_Signal(queue2, fence2, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+    ID3D12Fence_Release(fence2);
+
+    /* EnqueueResourceReady / SignalEventOnResourceReady */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+    status = WaitForSingleObject(event2, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+    status = WaitForSingleObject(event2, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* EnqueueResourceReady is a manual-reset event */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* SignalEventOnResourceReady(NULL) */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    thread = CreateThread(NULL, 0, test_d3d12_sync_object_thread, sync_cmd, 0, NULL);
+
+    status = WaitForSingleObject(thread, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(thread, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    CloseHandle(thread);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* EnqueueResourceReady works cross-device */
+
+    device2 = create_d3d12_device();
+    hr = pMFCreateD3D12SynchronizationObject(device2, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Device_Release(device2);
+
+    /* EnqueueResourceRelease depends on MFStartup */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
+
+    hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
+    ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* SignalEventOnFinalResourceRelease signals immediately if no Release enqueued  */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+
+    /* EnqueueResourceRelease / SignalEventOnFinalResourceRelease */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    for (UINT64 gen = 1; gen <= 5; gen++)
+    {
+    hr = ID3D12CommandQueue_Wait(queue, fence, gen);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    hr = ID3D12CommandQueue_Wait(queue2, fence2, gen);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12Fence_Signal(fence, gen);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    hr = ID3D12Fence_Signal(fence2, gen);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    }
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+    ID3D12Fence_Release(fence2);
+
+    /* SignalEventOnFinalResourceRelease only tracks one event */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+    status = WaitForSingleObject(event2, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+
+    /* SignalEventOnFinalResourceRelease with duplicated event */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    event3 = CreateEventA(NULL, FALSE,  FALSE, NULL);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event3);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    DuplicateHandle(GetCurrentProcess(), event3, GetCurrentProcess(), &event4, 0, FALSE, DUPLICATE_SAME_ACCESS);
+    CloseHandle(event3);
+
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    status = WaitForSingleObject(event4, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+    CloseHandle(event4);
+
+    /* SignalEventOnFinalResourceRelease is not affected by pending Ready */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    /* unblock queue */
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+
+    /* Reset errors if Release pending */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == MF_E_UNEXPECTED, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+
+    /* Reset clears Ready */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+
+    /* ReadyWait before Reset is signaled by Ready after Reset */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12CommandQueue_Signal(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    ID3D12Fence_SetEventOnCompletion(fence, 1, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+
+    /* Ready after Reset is cleared by deferred Ready before Reset */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12CommandQueue_Signal(queue, fence2, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12Fence_SetEventOnCompletion(fence2, 1, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_TIMEOUT, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+    ID3D12Fence_Release(fence2);
+
+    /* ReadyWait before Reset is signaled by deferred Ready before Reset */
+
+    hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12CommandQueue_Wait(queue, fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, queue2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ID3D12CommandQueue_Signal(queue2, fence2, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMFD3D12SynchronizationObject_Reset(sync_obj);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ID3D12Fence_Signal(fence, 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    ID3D12Fence_SetEventOnCompletion(fence2, 1, event);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    status = WaitForSingleObject(event, 100);
+    ok(status == WAIT_OBJECT_0, "got %#lx.\n", status);
+
+    IMFD3D12SynchronizationObject_Release(sync_obj);
+    IMFD3D12SynchronizationObjectCommands_Release(sync_cmd);
+    ID3D12Fence_Release(fence);
+    ID3D12Fence_Release(fence2);
+
+    hr = MFShutdown();
+    ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr);
+
+    ID3D12CommandQueue_Release(queue);
+    ID3D12CommandQueue_Release(queue2);
+    CloseHandle(event);
+    CloseHandle(event2);
+
+notsupported:
+    refcount = ID3D12Device_Release(device);
+    ok(!refcount, "Unexpected device refcount %u.\n", refcount);
+}
+
 static void test_sample_allocator_sysmem(void)
 {
     IMFVideoSampleAllocatorNotify test_notify = { &test_notify_callback_vtbl };
@@ -14060,6 +14696,7 @@ START_TEST(mfplat)
     test_MFMapDXGIFormatToDX9Format();
     test_d3d11_surface_buffer();
     test_d3d12_surface_buffer();
+    test_d3d12_sync_object();
     test_sample_allocator_sysmem();
     test_sample_allocator_d3d9();
     test_sample_allocator_d3d11();
diff --git a/dlls/mscms/mscms.spec b/dlls/mscms/mscms.spec
index ec14db72d629..9c50455fa394 100644
--- a/dlls/mscms/mscms.spec
+++ b/dlls/mscms/mscms.spec
@@ -86,7 +86,7 @@
 @ stdcall UnregisterCMMW(wstr long)
 @ stub WcsAssociateColorProfileWithDevice
 @ stub WcsCheckColors
-@ stub WcsCreateIccProfile
+@ stdcall WcsCreateIccProfile(ptr long)
 @ stub WcsDisassociateColorProfileFromDevice
 @ stub WcsEnumColorProfiles
 @ stdcall WcsEnumColorProfilesSize(long ptr ptr)
diff --git a/dlls/mscms/profile.c b/dlls/mscms/profile.c
index 395fe50dabfc..9ab946e34bbb 100644
--- a/dlls/mscms/profile.c
+++ b/dlls/mscms/profile.c
@@ -1526,3 +1526,13 @@ HPROFILE WINAPI WcsOpenColorProfileW( PROFILE *cdm, PROFILE *camp, PROFILE *gmmp
 
     return OpenColorProfileW( cdm, access, sharing, creation );
 }
+
+/******************************************************************************
+ * WcsCreateIccProfile                [MSCMS.@]
+ */
+HPROFILE WINAPI WcsCreateIccProfile( HPROFILE profile, DWORD options )
+{
+    FIXME( "%p, %#lx stub!\n", profile, options );
+
+    return NULL;
+}
diff --git a/dlls/msctf/Makefile.in b/dlls/msctf/Makefile.in
index a38ef7a28e15..db78811b6d99 100644
--- a/dlls/msctf/Makefile.in
+++ b/dlls/msctf/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = msctf.dll
 IMPORTS   = uuid ole32 oleaut32 user32 advapi32
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_PRODUCTVERSION = 5,1,2600,3319
 
 SOURCES = \
diff --git a/dlls/msctfmonitor/Makefile.in b/dlls/msctfmonitor/Makefile.in
index fc641d6dd7da..9511e79cb894 100644
--- a/dlls/msctfmonitor/Makefile.in
+++ b/dlls/msctfmonitor/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = msctfmonitor.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/msi/custom.c b/dlls/msi/custom.c
index cee8e06981c6..7b0e9fed87ee 100644
--- a/dlls/msi/custom.c
+++ b/dlls/msi/custom.c
@@ -553,11 +553,11 @@ UINT CDECL __wine_msi_call_dll_function(DWORD client_pid, const GUID *guid)
         {
             r = custom_proc_wrapper( fn, hPackage );
         }
-        __EXCEPT_PAGE_FAULT
+        __EXCEPT_ALL
         {
-            ERR( "Custom action (%s:%s) caused a page fault: %#lx\n",
+            ERR( "Custom action (%s:%s) caused an exception: %#lx\n",
                  debugstr_w(dll), debugstr_a(proc), GetExceptionCode() );
-            r = ERROR_SUCCESS;
+            r = ERROR_INSTALL_FAILURE;
         }
         __ENDTRY;
     }
diff --git a/dlls/msi/tests/action.c b/dlls/msi/tests/action.c
index 82d950ce4662..1c475aa8e419 100644
--- a/dlls/msi/tests/action.c
+++ b/dlls/msi/tests/action.c
@@ -1970,6 +1970,24 @@ static const char rep_install_exec_seq_dat[] =
     "UnpublishFeatures\t\t5300\n"
     "InstallFinalize\t\t6000\n";
 
+static const char exception_install_exec_seq_dat[] =
+    "Action\tCondition\tSequence\n"
+    "s72\tS255\tI2\n"
+    "InstallExecuteSequence\tAction\n"
+    "page_fault\tPAGEFAULT AND NOT REMOVE\t601\n"
+    "page_fault_ignore\tPAGEFAULT_IGNORE AND NOT REMOVE\t602\n"
+    "raise_exception\tEXCEPTION AND NOT REMOVE\t603\n"
+    "raise_exception_ignore\tEXCEPTION_IGNORE AND NOT REMOVE\t604\n";
+
+static const char exception_custom_action_dat[] =
+    "Action\tType\tSource\tTarget\n"
+    "s72\ti2\tS64\tS0\n"
+    "CustomAction\tAction\n"
+    "page_fault\t1\tcustom.dll\tpage_fault\n"
+    "page_fault_ignore\t65\tcustom.dll\tpage_fault\n"
+    "raise_exception\t1\tcustom.dll\traise_exception\n"
+    "raise_exception_ignore\t65\tcustom.dll\traise_exception\n";
+
 static const msi_table env_tables[] =
 {
     ADD_TABLE(component),
@@ -2371,6 +2389,19 @@ static const msi_table rep_tables[] =
     ADD_TABLE(media)
 };
 
+static const msi_table exception_tables[] =
+{
+    ADD_TABLE(component),
+    ADD_TABLE(directory),
+    ADD_TABLE(feature),
+    ADD_TABLE(feature_comp),
+    ADD_TABLE(file),
+    ADD_TABLE(exception_install_exec_seq),
+    ADD_TABLE(exception_custom_action),
+    ADD_TABLE(media),
+    ADD_TABLE(property)
+};
+
 /* cabinet definitions */
 
 /* make the max size large so there is only one cab file */
@@ -6541,6 +6572,41 @@ error:
     DeleteFileA(msifile);
 }
 
+static void test_custom_action_exception(void)
+{
+    UINT r;
+
+    if (!is_process_elevated())
+    {
+        skip("Process is limited\n");
+        return;
+    }
+
+    create_database(msifile, exception_tables, ARRAY_SIZE(exception_tables));
+
+    MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
+
+    r = MsiInstallProductA(msifile, "PAGEFAULT=1");
+    if (r == ERROR_INSTALL_PACKAGE_REJECTED)
+    {
+        skip("Not enough rights to perform tests\n");
+        goto done;
+    }
+    ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r);
+
+    r = MsiInstallProductA(msifile, "PAGEFAULT_IGNORE=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+    r = MsiInstallProductA(msifile, "EXCEPTION=1");
+    ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_SUCCESS, got %u\n", r);
+
+    r = MsiInstallProductA(msifile, "EXCEPTION_IGNORE=1");
+    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+done:
+    DeleteFileA(msifile);
+}
+
 static HANDLE get_admin_token(void)
 {
     TOKEN_ELEVATION_TYPE type;
@@ -6647,6 +6713,7 @@ START_TEST(action)
     test_register_mime_info();
     test_publish_assemblies();
     test_remove_existing_products();
+    test_custom_action_exception();
 
     DeleteFileA(log_file);
     SetCurrentDirectoryA(prev_path);
diff --git a/dlls/msi/tests/custom.c b/dlls/msi/tests/custom.c
index 8c5e7e4bbd07..5e4687fa789a 100644
--- a/dlls/msi/tests/custom.c
+++ b/dlls/msi/tests/custom.c
@@ -1411,6 +1411,22 @@ UINT WINAPI async2(MSIHANDLE hinst)
     return ERROR_SUCCESS;
 }
 
+UINT WINAPI page_fault(MSIHANDLE hinst)
+{
+    UINT *p = NULL;
+
+    *(volatile UINT *)p = 0xdeadbeef;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI raise_exception(MSIHANDLE hinst)
+{
+    RaiseException(0xdeadbeef, 0, 0, NULL);
+
+    return ERROR_SUCCESS;
+}
+
 static BOOL pf_exists(const char *file)
 {
     char path[MAX_PATH];
diff --git a/dlls/msi/tests/custom.spec b/dlls/msi/tests/custom.spec
index 5d78377a799c..4f3f1cb298a0 100644
--- a/dlls/msi/tests/custom.spec
+++ b/dlls/msi/tests/custom.spec
@@ -60,3 +60,5 @@
 @ stdcall tl_absent(long)
 @ stdcall wrv_present(long)
 @ stdcall wrv_absent(long)
+@ stdcall page_fault(long)
+@ stdcall raise_exception(long)
diff --git a/dlls/msvcp140_1/tests/msvcp140_1.c b/dlls/msvcp140_1/tests/msvcp140_1.c
index 4a399c152cc2..ac4bcec5ef1d 100644
--- a/dlls/msvcp140_1/tests/msvcp140_1.c
+++ b/dlls/msvcp140_1/tests/msvcp140_1.c
@@ -50,7 +50,6 @@ struct thiscall_thunk
 };
 #pragma pack(pop)
 
-static void* (WINAPI *call_thiscall_func1)(void *func, void *this);
 static void* (WINAPI *call_thiscall_func2)(void *func, void *this, const void *a);
 static void* (WINAPI *call_thiscall_func3)(void *func,
         void *this, const void *a, const void *b);
@@ -66,13 +65,11 @@ static void init_thiscall_thunk(void)
     thunk->pop_ecx  = 0x59;   /* popl  %ecx */
     thunk->push_eax = 0x50;   /* pushl %eax */
     thunk->jmp_edx  = 0xe2ff; /* jmp  *%edx */
-    call_thiscall_func1 = (void*)thunk;
     call_thiscall_func2 = (void*)thunk;
     call_thiscall_func3 = (void*)thunk;
     call_thiscall_func4 = (void*)thunk;
 }
 
-#define call_func1(func,_this) call_thiscall_func1(func,_this)
 #define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)(a))
 #define call_func3(func,_this,a,b) call_thiscall_func3(func,_this,\
         (const void*)(a),(const void*)(b))
diff --git a/dlls/msvcp60/tests/ios.c b/dlls/msvcp60/tests/ios.c
index 85dfdb76c95b..8b4c5e3ced38 100644
--- a/dlls/msvcp60/tests/ios.c
+++ b/dlls/msvcp60/tests/ios.c
@@ -498,7 +498,6 @@ static void * (WINAPI *call_thiscall_func1)( void *func, void *this );
 static void * (WINAPI *call_thiscall_func2)( void *func, void *this, void *a );
 static void * (WINAPI *call_thiscall_func3)( void *func, void *this, void *a, void *b );
 static void * (WINAPI *call_thiscall_func4)( void *func, void *this, void *a, void *b, void *c );
-static void * (WINAPI *call_thiscall_func5)( void *func, void *this, void *a, void *b, void *c, void *d );
 
 /* to silence compiler errors */
 static void * (WINAPI *call_thiscall_func2_ptr_dbl)( void *func, void *this, double a );
@@ -519,7 +518,6 @@ static void init_thiscall_thunk(void)
     call_thiscall_func2 = (void *)thunk;
     call_thiscall_func3 = (void *)thunk;
     call_thiscall_func4 = (void *)thunk;
-    call_thiscall_func5 = (void *)thunk;
 
     call_thiscall_func2_ptr_dbl  = (void *)thunk;
     call_thiscall_func2_ptr_fpos = (void *)thunk;
@@ -529,8 +527,6 @@ static void init_thiscall_thunk(void)
 #define call_func2(func,_this,a) call_thiscall_func2(func,_this,(void*)(a))
 #define call_func3(func,_this,a,b) call_thiscall_func3(func,_this,(void*)(a),(void*)(b))
 #define call_func4(func,_this,a,b,c) call_thiscall_func4(func,_this,(void*)(a),(void*)(b),(void*)(c))
-#define call_func5(func,_this,a,b,c,d) call_thiscall_func5(func,_this,(void*)(a),(void*)(b), \
-        (void*)(c), (void *)(d))
 
 #define call_func2_ptr_dbl(func,_this,a)  call_thiscall_func2_ptr_dbl(func,_this,a)
 #define call_func2_ptr_fpos(func,_this,a) call_thiscall_func2_ptr_fpos(func,_this,a)
diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index 6b1cd4706cb3..947777e6fb44 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -35,15 +35,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
-static const WCHAR emptyW[] = {0};
-static const WCHAR spaceW[] = {' '};
-static const WCHAR quotW[]  = {'\"'};
-static const WCHAR closetagW[] = {'>','\r','\n'};
-static const WCHAR crlfW[] = {'\r','\n'};
-static const WCHAR entityW[] = {'<','!','E','N','T','I','T','Y',' '};
-static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
-static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
-
 /* should be ordered as encoding names are sorted */
 typedef enum
 {
@@ -461,9 +452,9 @@ static HRESULT write_output_buffer(mxwriter *writer, const WCHAR *data, int len)
 
 static HRESULT write_output_buffer_quoted(mxwriter *writer, const WCHAR *data, int len)
 {
-    write_output_buffer(writer, quotW, 1);
+    write_output_buffer(writer, L"\"", 1);
     write_output_buffer(writer, data, len);
-    write_output_buffer(writer, quotW, 1);
+    write_output_buffer(writer, L"\"", 1);
 
     return S_OK;
 }
@@ -578,33 +569,27 @@ static void write_string_with_crlf(mxwriter *writer, const WCHAR *str, int len)
 
 static void write_prolog_buffer(mxwriter *writer)
 {
-    static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
-    static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
-    static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
-    static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
-    static const WCHAR noW[] = {'n','o','\"','?','>'};
-
     /* version */
-    write_output_buffer(writer, versionW, ARRAY_SIZE(versionW));
+    write_output_buffer(writer, L"<?xml version=", 14);
     write_output_buffer_quoted(writer, writer->version, -1);
 
     /* encoding */
-    write_output_buffer(writer, encodingW, ARRAY_SIZE(encodingW));
+    write_output_buffer(writer, L" encoding=", 10);
 
     if (writer->dest)
-        write_output_buffer(writer, writer->encoding, -1);
+        write_output_buffer_quoted(writer, writer->encoding, -1);
     else
-        write_output_buffer(writer, L"UTF-16", ARRAY_SIZE(L"UTF-16") - 1);
-    write_output_buffer(writer, quotW, 1);
+        write_output_buffer_quoted(writer, L"UTF-16", 6);
 
     /* standalone */
-    write_output_buffer(writer, standaloneW, ARRAY_SIZE(standaloneW));
+    write_output_buffer(writer, L" standalone=", 12);
     if (writer->props[MXWriter_Standalone] == VARIANT_TRUE)
-        write_output_buffer(writer, yesW, ARRAY_SIZE(yesW));
+        write_output_buffer_quoted(writer, L"yes", 3);
     else
-        write_output_buffer(writer, noW, ARRAY_SIZE(noW));
+        write_output_buffer_quoted(writer, L"no", 2);
+    write_output_buffer(writer, L"?>", 2);
 
-    write_output_buffer(writer, crlfW, ARRAY_SIZE(crlfW));
+    write_output_buffer(writer, L"\r\n", 2);
     writer->newline = TRUE;
 }
 
@@ -637,14 +622,12 @@ static HRESULT write_data_to_stream(mxwriter *writer)
    we have to close it differently. */
 static void close_element_starttag(mxwriter *writer)
 {
-    static const WCHAR gtW[] = {'>'};
     if (!writer->element) return;
-    write_output_buffer(writer, gtW, 1);
+    write_output_buffer(writer, L">", 1);
 }
 
 static void write_node_indent(mxwriter *writer)
 {
-    static const WCHAR tabW[] = {'\t'};
     int indent = writer->indent;
 
     if (!writer->props[MXWriter_Indent] || writer->text)
@@ -656,9 +639,9 @@ static void write_node_indent(mxwriter *writer)
     /* This is to workaround PI output logic that always puts newline chars,
        document prolog PI does that too. */
     if (!writer->newline)
-        write_output_buffer(writer, crlfW, ARRAY_SIZE(crlfW));
+        write_output_buffer(writer, L"\r\n", 2);
     while (indent--)
-        write_output_buffer(writer, tabW, 1);
+        write_output_buffer(writer, L"\t", 1);
 
     writer->newline = FALSE;
     writer->text = FALSE;
@@ -672,9 +655,6 @@ static inline void writer_inc_indent(mxwriter *This)
 static inline void writer_dec_indent(mxwriter *This)
 {
     if (This->indent) This->indent--;
-    /* depth is decreased only when element is closed, meaning it's not a text node
-       at this point */
-    This->text = FALSE;
 }
 
 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
@@ -1274,12 +1254,10 @@ static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
 static void mxwriter_write_attribute(mxwriter *writer, const WCHAR *qname, int qname_len,
     const WCHAR *value, int value_len, BOOL escape)
 {
-    static const WCHAR eqW[] = {'='};
-
     /* space separator in front of every attribute */
-    write_output_buffer(writer, spaceW, 1);
+    write_output_buffer(writer, L" ", 1);
     write_output_buffer(writer, qname, qname_len);
-    write_output_buffer(writer, eqW, 1);
+    write_output_buffer(writer, L"=", 1);
 
     write_output_buffer(writer, L"\"", 1);
     if (escape)
@@ -1291,15 +1269,13 @@ static void mxwriter_write_attribute(mxwriter *writer, const WCHAR *qname, int q
 
 static void mxwriter_write_starttag(mxwriter *writer, const WCHAR *qname, int len)
 {
-    static const WCHAR ltW[] = {'<'};
-
     close_element_starttag(writer);
-    set_element_name(writer, qname ? qname : emptyW, qname ? len : 0);
+    set_element_name(writer, qname ? qname : L"", qname ? len : 0);
 
     write_node_indent(writer);
 
-    write_output_buffer(writer, ltW, 1);
-    write_output_buffer(writer, qname ? qname : emptyW, qname ? len : 0);
+    write_output_buffer(writer, L"<", 1);
+    write_output_buffer(writer, qname ? qname : L"", qname ? len : 0);
     writer_inc_indent(writer);
 }
 
@@ -1375,18 +1351,14 @@ static HRESULT WINAPI SAXContentHandler_endElement(
 
     if (This->element)
     {
-        static const WCHAR closeW[] = {'/','>'};
-        write_output_buffer(This, closeW, 2);
+        write_output_buffer(This, L"/>", 2);
     }
     else
     {
-        static const WCHAR closetagW[] = {'<','/'};
-        static const WCHAR gtW[] = {'>'};
-
         write_node_indent(This);
-        write_output_buffer(This, closetagW, 2);
+        write_output_buffer(This, L"</", 2);
         write_output_buffer(This, QName, nQName);
-        write_output_buffer(This, gtW, 1);
+        write_output_buffer(This, L">", 1);
     }
 
     set_element_name(This, NULL, 0);
@@ -1410,7 +1382,9 @@ static HRESULT WINAPI SAXContentHandler_characters(ISAXContentHandler *iface, co
 
     if (nchars)
     {
-        if (writer->cdata || writer->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
+        if (writer->cdata)
+            write_string_with_crlf(writer, chars, nchars);
+        else if (writer->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
             write_output_buffer(writer, chars, nchars);
         else
             write_escaped_string(writer, chars, nchars, EscapeText);
@@ -1442,28 +1416,26 @@ static HRESULT WINAPI SAXContentHandler_processingInstruction(
     const WCHAR *data,
     int ndata)
 {
-    mxwriter *This = impl_from_ISAXContentHandler( iface );
-    static const WCHAR openpiW[] = {'<','?'};
-    static const WCHAR closepiW[] = {'?','>','\r','\n'};
+    mxwriter *writer = impl_from_ISAXContentHandler(iface);
 
-    TRACE("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
+    TRACE("%p, %s, %s.\n", iface, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
 
     if (!target) return E_INVALIDARG;
 
-    write_node_indent(This);
-    write_output_buffer(This, openpiW, ARRAY_SIZE(openpiW));
+    write_node_indent(writer);
+    write_output_buffer(writer, L"<?", 2);
 
     if (*target)
-        write_output_buffer(This, target, ntarget);
+        write_output_buffer(writer, target, ntarget);
 
     if (data && *data && ndata)
     {
-        write_output_buffer(This, spaceW, 1);
-        write_output_buffer(This, data, ndata);
+        write_output_buffer(writer, L" ", 1);
+        write_output_buffer(writer, data, ndata);
     }
 
-    write_output_buffer(This, closepiW, ARRAY_SIZE(closepiW));
-    This->newline = TRUE;
+    write_output_buffer(writer, L"?>\r\n", 4);
+    writer->newline = TRUE;
 
     return S_OK;
 }
@@ -1520,60 +1492,56 @@ static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
     const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
     const WCHAR *systemId, int systemId_len)
 {
-    static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
-    static const WCHAR openintW[] = {'[','\r','\n'};
+    mxwriter *writer = impl_from_ISAXLexicalHandler(iface);
 
-    mxwriter *This = impl_from_ISAXLexicalHandler( iface );
-
-    TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
+    TRACE("%p, %s. %s. %s.\n", iface, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
         debugstr_wn(systemId, systemId_len));
 
     if (!name) return E_INVALIDARG;
 
-    write_output_buffer(This, doctypeW, ARRAY_SIZE(doctypeW));
+    write_output_buffer(writer, L"<!DOCTYPE ", 10);
 
     if (*name)
     {
-        write_output_buffer(This, name, name_len);
-        write_output_buffer(This, spaceW, 1);
+        write_output_buffer(writer, name, name_len);
+        write_output_buffer(writer, L" ", 1);
     }
 
     if (publicId)
     {
-        write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
-        write_output_buffer_quoted(This, publicId, publicId_len);
+        write_output_buffer(writer, L"PUBLIC ", 7);
+        write_output_buffer_quoted(writer, publicId, publicId_len);
 
         if (!systemId) return E_INVALIDARG;
 
         if (*publicId)
-            write_output_buffer(This, spaceW, 1);
+            write_output_buffer(writer, L" ", 1);
 
-        write_output_buffer_quoted(This, systemId, systemId_len);
+        write_output_buffer_quoted(writer, systemId, systemId_len);
 
         if (*systemId)
-            write_output_buffer(This, spaceW, 1);
+            write_output_buffer(writer, L" ", 1);
     }
     else if (systemId)
     {
-        write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
-        write_output_buffer_quoted(This, systemId, systemId_len);
+        write_output_buffer(writer, L"SYSTEM ", 7);
+        write_output_buffer_quoted(writer, systemId, systemId_len);
         if (*systemId)
-            write_output_buffer(This, spaceW, 1);
+            write_output_buffer(writer, L" ", 1);
     }
 
-    write_output_buffer(This, openintW, ARRAY_SIZE(openintW));
+    write_output_buffer(writer, L"[\r\n", 3);
 
     return S_OK;
 }
 
 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
 {
-    mxwriter *This = impl_from_ISAXLexicalHandler( iface );
-    static const WCHAR closedtdW[] = {']','>','\r','\n'};
+    mxwriter *writer = impl_from_ISAXLexicalHandler(iface);
 
-    TRACE("(%p)\n", This);
+    TRACE("%p.\n", iface);
 
-    write_output_buffer(This, closedtdW, ARRAY_SIZE(closedtdW));
+    write_output_buffer(writer, L"]>\r\n", 4);
 
     return S_OK;
 }
@@ -1594,48 +1562,44 @@ static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, con
 
 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
 {
-    static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
-    mxwriter *This = impl_from_ISAXLexicalHandler( iface );
+    mxwriter *writer = impl_from_ISAXLexicalHandler(iface);
 
-    TRACE("(%p)\n", This);
+    TRACE("%p.\n", iface);
 
-    write_node_indent(This);
-    write_output_buffer(This, scdataW, ARRAY_SIZE(scdataW));
-    This->cdata = TRUE;
+    write_node_indent(writer);
+    write_output_buffer(writer, L"<![CDATA[", 9);
+    writer->cdata = TRUE;
 
     return S_OK;
 }
 
 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
 {
-    mxwriter *This = impl_from_ISAXLexicalHandler( iface );
-    static const WCHAR ecdataW[] = {']',']','>'};
+    mxwriter *writer = impl_from_ISAXLexicalHandler(iface);
 
-    TRACE("(%p)\n", This);
+    TRACE("%p.\n", iface);
 
-    write_output_buffer(This, ecdataW, ARRAY_SIZE(ecdataW));
-    This->cdata = FALSE;
+    write_output_buffer(writer, L"]]>", 3);
+    writer->cdata = FALSE;
 
     return S_OK;
 }
 
 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
 {
-    mxwriter *This = impl_from_ISAXLexicalHandler( iface );
-    static const WCHAR copenW[] = {'<','!','-','-'};
-    static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
+    mxwriter *writer = impl_from_ISAXLexicalHandler(iface);
 
-    TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
+    TRACE("%p, %s:%d.\n", iface, debugstr_wn(chars, nchars), nchars);
 
     if (!chars) return E_INVALIDARG;
 
-    close_element_starttag(This);
-    write_node_indent(This);
+    close_element_starttag(writer);
+    write_node_indent(writer);
 
-    write_output_buffer(This, copenW, ARRAY_SIZE(copenW));
+    write_output_buffer(writer, L"<!--", 4);
     if (nchars)
-        write_output_buffer(This, chars, nchars);
-    write_output_buffer(This, ccloseW, ARRAY_SIZE(ccloseW));
+        write_output_buffer(writer, chars, nchars);
+    write_output_buffer(writer, L"-->\r\n", 5);
 
     return S_OK;
 }
@@ -1677,7 +1641,6 @@ static ULONG WINAPI SAXDeclHandler_Release(ISAXDeclHandler *iface)
 static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
     const WCHAR *name, int n_name, const WCHAR *model, int n_model)
 {
-    static const WCHAR elementW[] = {'<','!','E','L','E','M','E','N','T',' '};
     mxwriter *This = impl_from_ISAXDeclHandler( iface );
 
     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
@@ -1685,14 +1648,14 @@ static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
 
     if (!name || !model) return E_INVALIDARG;
 
-    write_output_buffer(This, elementW, ARRAY_SIZE(elementW));
+    write_output_buffer(This, L"<!ELEMENT ", 10);
     if (n_name) {
         write_output_buffer(This, name, n_name);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(This, L" ", 1);
     }
     if (n_model)
         write_output_buffer(This, model, n_model);
-    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+    write_output_buffer(This, L">\r\n", 3);
 
     return S_OK;
 }
@@ -1703,38 +1666,36 @@ static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
     const WCHAR *value, int n_value)
 {
     mxwriter *This = impl_from_ISAXDeclHandler( iface );
-    static const WCHAR attlistW[] = {'<','!','A','T','T','L','I','S','T',' '};
-    static const WCHAR closetagW[] = {'>','\r','\n'};
 
     TRACE("(%p)->(%s:%d %s:%d %s:%d %s:%d %s:%d)\n", This, debugstr_wn(element, n_element), n_element,
         debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
         debugstr_wn(value, n_value), n_value);
 
-    write_output_buffer(This, attlistW, ARRAY_SIZE(attlistW));
+    write_output_buffer(This, L"<!ATTLIST ", 10);
     if (n_element) {
         write_output_buffer(This, element, n_element);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(This, L" ", 1);
     }
 
     if (n_attr) {
         write_output_buffer(This, attr, n_attr);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(This, L" ", 1);
     }
 
     if (n_type) {
         write_output_buffer(This, type, n_type);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(This, L" ", 1);
     }
 
     if (n_default) {
         write_output_buffer(This, Default, n_default);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(This, L" ", 1);
     }
 
     if (n_value)
         write_output_buffer_quoted(This, value, n_value);
 
-    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+    write_output_buffer(This, L">\r\n", 3);
 
     return S_OK;
 }
@@ -1742,23 +1703,23 @@ static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
 static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
     const WCHAR *name, int n_name, const WCHAR *value, int n_value)
 {
-    mxwriter *This = impl_from_ISAXDeclHandler( iface );
+    mxwriter *writer = impl_from_ISAXDeclHandler(iface);
 
-    TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
+    TRACE("%p, %s:%d, %s:%d.\n", iface, debugstr_wn(name, n_name), n_name,
         debugstr_wn(value, n_value), n_value);
 
     if (!name || !value) return E_INVALIDARG;
 
-    write_output_buffer(This, entityW, ARRAY_SIZE(entityW));
+    write_output_buffer(writer, L"<!ENTITY ", 9);
     if (n_name) {
-        write_output_buffer(This, name, n_name);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(writer, name, n_name);
+        write_output_buffer(writer, L" ", 1);
     }
 
     if (n_value)
-        write_output_buffer_quoted(This, value, n_value);
+        write_output_buffer_quoted(writer, value, n_value);
 
-    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+    write_output_buffer(writer, L">\r\n", 3);
 
     return S_OK;
 }
@@ -1767,33 +1728,33 @@ static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
     const WCHAR *name, int n_name, const WCHAR *publicId, int n_publicId,
     const WCHAR *systemId, int n_systemId)
 {
-    mxwriter *This = impl_from_ISAXDeclHandler( iface );
+    mxwriter *writer = impl_from_ISAXDeclHandler(iface);
 
-    TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
+    TRACE("%p, %s:%d, %s:%d, %s:%d.\n", iface, debugstr_wn(name, n_name), n_name,
         debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
 
     if (!name || !systemId) return E_INVALIDARG;
 
-    write_output_buffer(This, entityW, ARRAY_SIZE(entityW));
+    write_output_buffer(writer, L"<!ENTITY ", 9);
     if (n_name) {
-        write_output_buffer(This, name, n_name);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+        write_output_buffer(writer, name, n_name);
+        write_output_buffer(writer, L" ", 1);
     }
 
     if (publicId)
     {
-        write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
-        write_output_buffer_quoted(This, publicId, n_publicId);
-        write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
-        write_output_buffer_quoted(This, systemId, n_systemId);
+        write_output_buffer(writer, L"PUBLIC ", 7);
+        write_output_buffer_quoted(writer, publicId, n_publicId);
+        write_output_buffer(writer, L" ", 1);
+        write_output_buffer_quoted(writer, systemId, n_systemId);
     }
     else
     {
-        write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
-        write_output_buffer_quoted(This, systemId, n_systemId);
+        write_output_buffer(writer, L"SYSTEM ", 7);
+        write_output_buffer_quoted(writer, systemId, n_systemId);
     }
 
-    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+    write_output_buffer(writer, L">\r\n", 3);
 
     return S_OK;
 }
@@ -2296,7 +2257,6 @@ static HRESULT WINAPI SAXDTDHandler_notationDecl(ISAXDTDHandler *iface,
     const WCHAR *publicid, INT n_publicid,
     const WCHAR *systemid, INT n_systemid)
 {
-    static const WCHAR notationW[] = {'<','!','N','O','T','A','T','I','O','N',' '};
     mxwriter *This = impl_from_ISAXDTDHandler( iface );
 
     TRACE("(%p)->(%s:%d, %s:%d, %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
@@ -2305,30 +2265,30 @@ static HRESULT WINAPI SAXDTDHandler_notationDecl(ISAXDTDHandler *iface,
     if (!name || !n_name)
         return E_INVALIDARG;
 
-    write_output_buffer(This, notationW, ARRAY_SIZE(notationW));
+    write_output_buffer(This, L"<!NOTATION ", 11);
     write_output_buffer(This, name, n_name);
 
     if (!publicid && !systemid)
         return E_INVALIDARG;
 
-    write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+    write_output_buffer(This, L" ", 1);
     if (publicid)
     {
-        write_output_buffer(This, publicW, ARRAY_SIZE(publicW));
+        write_output_buffer(This, L"PUBLIC ", 7);
         write_output_buffer_quoted(This, publicid, n_publicid);
         if (systemid)
         {
-            write_output_buffer(This, spaceW, ARRAY_SIZE(spaceW));
+            write_output_buffer(This, L" ", 1);
             write_output_buffer_quoted(This, systemid, n_systemid);
         }
     }
     else
     {
-        write_output_buffer(This, systemW, ARRAY_SIZE(systemW));
+        write_output_buffer(This, L"SYSTEM ", 7);
         write_output_buffer_quoted(This, systemid, n_systemid);
     }
 
-    write_output_buffer(This, closetagW, ARRAY_SIZE(closetagW));
+    write_output_buffer(This, L">\r\n", 3);
 
     return S_OK;
 }
@@ -2618,7 +2578,7 @@ HRESULT MXWriter_create(MSXML_VERSION version, void **ppObj)
     This->cdata = FALSE;
     This->indent = 0;
     This->text = FALSE;
-    This->newline = FALSE;
+    This->newline = TRUE;
 
     This->dest = NULL;
 
@@ -2774,7 +2734,7 @@ static HRESULT WINAPI MXAttributes_addAttribute(IMXAttributes *iface,
     attr->qname = SysAllocString(QName);
     attr->local = SysAllocString(localName);
     attr->uri   = SysAllocString(uri);
-    attr->type  = SysAllocString(type ? type : emptyW);
+    attr->type  = SysAllocString(type ? type : L"");
     attr->value = SysAllocString(value);
     This->length++;
 
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index 564ec69eb280..53c5702fc39f 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -23,6 +23,7 @@
 #define COBJMACROS
 #define CONST_VTABLE
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <assert.h>
 
@@ -35,6 +36,22 @@
 
 #include "wine/test.h"
 
+#define check_writer_output(writer, expected) check_writer_output_(writer, expected, false, __LINE__)
+#define check_writer_output_todo(writer, expected) check_writer_output_(writer, expected, true, __LINE__)
+static void check_writer_output_(IMXWriter *writer, const WCHAR *expected, bool todo, int line)
+{
+    VARIANT dest;
+    HRESULT hr;
+
+    V_VT(&dest) = VT_EMPTY;
+    hr = IMXWriter_get_output(writer, &dest);
+    ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest));
+    todo_wine_if(todo)
+    ok_(__FILE__, line)(!wcscmp(expected, V_BSTR(&dest)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&dest)));
+    VariantClear(&dest);
+}
+
 struct sink
 {
     IVBSAXContentHandler IVBSAXContentHandler_iface;
@@ -2513,8 +2530,6 @@ static void test_saxreader_cdata(void)
         ISAXXMLReader_Release(reader);
         table++;
     }
-
-    free_bstrs();
 }
 
 static void test_saxreader_pi(void)
@@ -2562,8 +2577,6 @@ static void test_saxreader_pi(void)
         ISAXXMLReader_Release(reader);
         table++;
     }
-
-    free_bstrs();
 }
 
 static void test_saxreader_characters(void)
@@ -2622,8 +2635,6 @@ static void test_saxreader_characters(void)
         ISAXXMLReader_Release(reader);
         table++;
     }
-
-    free_bstrs();
 }
 
 static void test_saxreader_properties(void)
@@ -3695,7 +3706,6 @@ static void test_mxwriter_properties(void)
     VARIANT_BOOL b;
     HRESULT hr;
     BSTR str, str2;
-    VARIANT dest;
 
     test_mxwriter_default_properties(mxwriter_default_props);
 
@@ -3788,13 +3798,8 @@ static void test_mxwriter_properties(void)
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n");
+
     ISAXContentHandler_Release(content);
 
     hr = IMXWriter_get_version(writer, NULL);
@@ -4041,7 +4046,6 @@ static void test_mxwriter_startenddocument(void)
 {
     ISAXContentHandler *content;
     IMXWriter *writer;
-    VARIANT dest;
     HRESULT hr;
 
     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
@@ -4057,26 +4061,14 @@ static void test_mxwriter_startenddocument(void)
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n", V_BSTR(&dest)),
-        "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n");
 
     /* now try another startDocument */
     hr = ISAXContentHandler_startDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     /* and get duplicated prolog */
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
-                  "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n", V_BSTR(&dest)),
-        "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
+                  "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
@@ -4098,17 +4090,32 @@ static void test_mxwriter_startenddocument(void)
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
 
-    free_bstrs();
+    /* declaration with indentation */
+    hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IMXWriter, (void**)&writer);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_startDocument(content);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_endDocument(content);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n<a>");
 }
 
 enum startendtype
@@ -4232,6 +4239,8 @@ static void test_mxwriter_startendelement_batch(const struct writer_startendelem
             &IID_IMXWriter, (void**)&writer);
         ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
+        winetest_push_context("Test %d", i);
+
         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
         ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
@@ -4252,7 +4261,7 @@ static void test_mxwriter_startendelement_batch(const struct writer_startendelem
             hr = ISAXContentHandler_startElement(content, table->uri, table->uri ? lstrlenW(table->uri) : 0,
                 table->local_name, table->local_name ? lstrlenW(table->local_name) : 0, table->qname,
                 table->qname ? lstrlenW(table->qname) : 0, table->attr);
-            ok(hr == table->hr, "test %d: got %#lx, expected %#lx\n", i, hr, table->hr);
+            ok(hr == table->hr, "Unexpected hr %#lx.\n", hr);
         }
 
         if (table->type & EndElement)
@@ -4260,31 +4269,21 @@ static void test_mxwriter_startendelement_batch(const struct writer_startendelem
             hr = ISAXContentHandler_endElement(content, table->uri, table->uri ? lstrlenW(table->uri) : 0,
                 table->local_name, table->local_name ? lstrlenW(table->local_name) : 0, table->qname,
                 table->qname ? lstrlenW(table->qname) : 0);
-            ok(hr == table->hr, "test %d: got %#lx, expected %#lx\n", i, hr, table->hr);
+            ok(hr == table->hr, "Unexpected hr %#lx.\n", hr);
         }
 
         /* test output */
         if (hr == S_OK)
-        {
-            VARIANT dest;
-
-            V_VT(&dest) = VT_EMPTY;
-            hr = IMXWriter_get_output(writer, &dest);
-            ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-            ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-            ok(!lstrcmpW(table->output, V_BSTR(&dest)),
-                "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output));
-            VariantClear(&dest);
-        }
+            check_writer_output(writer, table->output);
 
         ISAXContentHandler_Release(content);
         IMXWriter_Release(writer);
 
+        winetest_pop_context();
+
         table++;
         i++;
     }
-
-    free_bstrs();
 }
 
 /* point of these test is to start/end element with different names and name lengths */
@@ -4345,25 +4344,13 @@ static void test_mxwriter_startendelement_batch2(const struct writer_startendele
 
         /* test output */
         if (hr == S_OK)
-        {
-            VARIANT dest;
-
-            V_VT(&dest) = VT_EMPTY;
-            hr = IMXWriter_get_output(writer, &dest);
-            ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-            ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-            ok(!lstrcmpW(table->output, V_BSTR(&dest)),
-                "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output));
-            VariantClear(&dest);
-        }
+            check_writer_output(writer, table->output);
 
         ISAXContentHandler_Release(content);
         IMXWriter_Release(writer);
 
         table++;
         i++;
-
-        free_bstrs();
     }
 }
 
@@ -4404,22 +4391,12 @@ static void test_mxwriter_startendelement(void)
     hr = IVBSAXContentHandler_startElement(vb_content, &bstr_empty, &bstr_b, &bstr_empty, NULL);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<>");
 
     hr = IVBSAXContentHandler_startElement(vb_content, &bstr_empty, &bstr_empty, &bstr_b, NULL);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<><b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<><b>");
 
     hr = IVBSAXContentHandler_endElement(vb_content, &bstr_null, &bstr_null, &bstr_b);
     ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
@@ -4439,12 +4416,7 @@ static void test_mxwriter_startendelement(void)
     hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_empty, &bstr_b);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<><b></b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<><b></b>");
 
     SysFreeString(bstr_empty);
     SysFreeString(bstr_a);
@@ -4474,22 +4446,12 @@ static void test_mxwriter_startendelement(void)
     hr = ISAXContentHandler_startElement(content, L"", 0, L"b", 1, L"", 0, NULL);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<>");
 
     hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"b", 1, NULL);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<><b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<><b>");
 
     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, L"a:b", 3);
     ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
@@ -4503,13 +4465,7 @@ static void test_mxwriter_startendelement(void)
 
     hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"b", 1);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<><b></b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<><b></b>");
 
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -4518,25 +4474,14 @@ static void test_mxwriter_startendelement(void)
     hr = IMXWriter_put_output(writer, dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"");
 
     hr = ISAXContentHandler_startDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
     hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"abcdef", 3, NULL);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<abc>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<abc>");
 
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -4544,12 +4489,7 @@ static void test_mxwriter_startendelement(void)
 
     hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"abdcdef", 3);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<abc></abd>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<abc></abd>");
 
     V_VT(&dest) = VT_EMPTY;
     hr = IMXWriter_put_output(writer, dest);
@@ -4558,12 +4498,7 @@ static void test_mxwriter_startendelement(void)
     /* length -1 */
     hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<a>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<a>");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
@@ -4624,12 +4559,7 @@ static void test_mxwriter_characters(void)
     hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 14);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"VbCharsTESTCHARDATA .", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"VbCharsTESTCHARDATA .");
 
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -4661,12 +4591,7 @@ static void test_mxwriter_characters(void)
     hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", 1);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<a></a>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<a></a>");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
@@ -4746,12 +4671,7 @@ static void test_mxwriter_characters(void)
     hr = ISAXContentHandler_characters(content, L"\ncd", 3);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest));
-    ok(!lstrcmpW(L"ab\r\n\r\ncd", V_BSTR(&dest)), "Unexpected content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"ab\r\n\r\ncd");
 
     V_VT(&dest) = VT_EMPTY;
     hr = IMXWriter_put_output(writer, dest);
@@ -4760,12 +4680,7 @@ static void test_mxwriter_characters(void)
     hr = ISAXContentHandler_characters(content, L"\nab\rc\r\n", 7);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest));
-    ok(!lstrcmpW(L"\r\nab\r\nc\r\n", V_BSTR(&dest)), "Unexpected content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"\r\nab\r\nc\r\n");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
@@ -4779,6 +4694,8 @@ static void test_mxwriter_characters(void)
             continue;
         }
 
+        winetest_push_context("Test %d", i);
+
         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
             &IID_IMXWriter, (void**)&writer);
         ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -4797,15 +4714,7 @@ static void test_mxwriter_characters(void)
 
         /* test output */
         if (hr == S_OK)
-        {
-            V_VT(&dest) = VT_EMPTY;
-            hr = IMXWriter_get_output(writer, &dest);
-            ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-            ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-            ok(!lstrcmpW(table->output, V_BSTR(&dest)),
-                "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output));
-            VariantClear(&dest);
-        }
+            check_writer_output(writer, table->output);
 
         /* with disabled escaping */
         V_VT(&dest) = VT_EMPTY;
@@ -4820,23 +4729,48 @@ static void test_mxwriter_characters(void)
 
         /* test output */
         if (hr == S_OK)
-        {
-            V_VT(&dest) = VT_EMPTY;
-            hr = IMXWriter_get_output(writer, &dest);
-            ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-            ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-            ok(!lstrcmpW(table->data, V_BSTR(&dest)),
-                "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->data));
-            VariantClear(&dest);
-        }
+            check_writer_output(writer, table->data);
 
         ISAXContentHandler_Release(content);
         IMXWriter_Release(writer);
 
+        winetest_pop_context();
+
         table++;
         i++;
     }
 
+    /* test surrounding node indentation */
+    hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IMXWriter, (void**)&writer);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_startDocument(content);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", 1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_characters(content, L"test", 4);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", 1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    check_writer_output(writer, L"<a>test</a>");
+
+    ISAXContentHandler_Release(content);
+    IMXWriter_Release(writer);
+
     free_bstrs();
 }
 
@@ -4982,13 +4916,7 @@ static void test_mxwriter_stream(void)
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n", V_BSTR(&dest)),
-            "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n");
 
     /* test when BOM is written to output stream */
     V_VT(&dest) = VT_EMPTY;
@@ -5071,13 +4999,7 @@ static void test_mxwriter_encoding(void)
     /* The content is always re-encoded to UTF-16 when the output is
      * retrieved as a BSTR.
      */
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n", V_BSTR(&dest)),
-            "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n");
 
     /* switch encoding when something is written already */
     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
@@ -5358,48 +5280,72 @@ static void test_mxwriter_comment(void)
     hr = ISAXLexicalHandler_comment(lexical, L"comment", 0);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!---->\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!---->\r\n");
 
     hr = ISAXLexicalHandler_comment(lexical, L"comment", 7);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!---->\r\n<!--comment-->\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!---->\r\n<!--comment-->\r\n");
 
     /* As an element child */
     V_VT(&dest) = VT_EMPTY;
     hr = IMXWriter_put_output(writer, dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("a"), -1, NULL);
+    hr = ISAXLexicalHandler_comment(lexical, L"c1", 2);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    hr = ISAXLexicalHandler_comment(lexical, L"comment", 7);
+    check_writer_output(writer, L"<!--c1-->\r\n");
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output(writer, L"<!--c1-->\r\n<a>");
+    hr = ISAXLexicalHandler_comment(lexical, L"c2", 2);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, _bstr_("a"), -1);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a><!--c2-->");
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", -1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a><!--c2--></a>");
+    hr = ISAXLexicalHandler_comment(lexical, L"c3", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a><!--c2--></a><!--c3-->");
+
+    hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
     V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
+    hr = IMXWriter_put_output(writer, dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest));
-    todo_wine
-    ok(!lstrcmpW(L"<a><!--comment--></a>", V_BSTR(&dest)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+
+    hr = ISAXLexicalHandler_comment(lexical, L"c1", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->");
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a>");
+    hr = ISAXLexicalHandler_comment(lexical, L"c2", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a>\r\n\t<!--c2-->");
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", -1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a>\r\n\t<!--c2-->\r\n</a>");
+    hr = ISAXLexicalHandler_comment(lexical, L"c3", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a>\r\n\t<!--c2-->\r\n</a>\r\n<!--c3-->");
+
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"b", -1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXLexicalHandler_comment(lexical, L"c4", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"b", -1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXLexicalHandler_comment(lexical, L"c5", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<!--c1-->\r\n<a>\r\n\t<!--c2-->\r\n</a>\r\n"
+            "<!--c3-->\r\n<b>\r\n\t<!--c4-->\r\n</b>\r\n<!--c5-->");
 
     ISAXContentHandler_Release(content);
     ISAXLexicalHandler_Release(lexical);
     IVBSAXLexicalHandler_Release(vblexical);
     IMXWriter_Release(writer);
-    free_bstrs();
 }
 
 static void test_mxwriter_cdata(void)
@@ -5408,7 +5354,6 @@ static void test_mxwriter_cdata(void)
     ISAXContentHandler *content;
     ISAXLexicalHandler *lexical;
     IMXWriter *writer;
-    VARIANT dest;
     HRESULT hr;
 
     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
@@ -5433,12 +5378,7 @@ static void test_mxwriter_cdata(void)
     hr = ISAXLexicalHandler_startCDATA(lexical);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<![CDATA[", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<![CDATA[");
 
     hr = IVBSAXLexicalHandler_startCDATA(vblexical);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -5453,19 +5393,12 @@ static void test_mxwriter_cdata(void)
     hr = ISAXLexicalHandler_endCDATA(lexical);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    todo_wine
-    ok(!lstrcmpW(L"<![CDATA[<![CDATA[< > & \"\r\na\r\nb\r\n]]>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<![CDATA[<![CDATA[< > & \"\r\na\r\nb\r\n]]>");
 
     ISAXContentHandler_Release(content);
     ISAXLexicalHandler_Release(lexical);
     IVBSAXLexicalHandler_Release(vblexical);
     IMXWriter_Release(writer);
-    free_bstrs();
 }
 
 static void test_mxwriter_pi(void)
@@ -5491,37 +5424,72 @@ static void test_mxwriter_pi(void)
     hr = ISAXContentHandler_processingInstruction(content, L"target", 6, NULL, 0);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?\?>\r\n<?target?>\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?\?>\r\n<?target?>\r\n");
 
     hr = ISAXContentHandler_processingInstruction(content, L"target", 4, L"data", 4);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
+    check_writer_output(writer, L"<?\?>\r\n<?target?>\r\n<?targ data?>\r\n");
+
     V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
+    hr = IMXWriter_put_output(writer, dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?\?>\r\n<?target?>\r\n<?targ data?>\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
 
+    hr = ISAXContentHandler_processingInstruction(content, L"target", 6, L"data", 0);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    check_writer_output(writer, L"<?target?>\r\n");
+
+    /* As an element child */
     V_VT(&dest) = VT_EMPTY;
     hr = IMXWriter_put_output(writer, dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    hr = ISAXContentHandler_processingInstruction(content, L"target", 6, L"data", 0);
+    hr = ISAXContentHandler_processingInstruction(content, L"t1", 2, L"d1", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output(writer, L"<?t1 d1?>\r\n");
+
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output(writer, L"<?t1 d1?>\r\n<a>");
+
+    hr = ISAXContentHandler_processingInstruction(content, L"t2", 2, L"d2", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", -1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXContentHandler_processingInstruction(content, L"t3", 2, L"d3", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXContentHandler_processingInstruction(content, L"t4", 2, L"d4", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    check_writer_output_todo(writer, L"<?t1 d1?>\r\n<a><?t2 d2?></a><?t3 d3?><?t4 d4?>");
+
+    /* With indentation */
+    hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
     V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
+    hr = IMXWriter_put_output(writer, dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?target?>\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
 
+    hr = ISAXContentHandler_processingInstruction(content, L"t1", 2, L"d1", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<?t1 d1?>");
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output(writer, L"<?t1 d1?>\r\n<a>");
+    hr = ISAXContentHandler_processingInstruction(content, L"t2", 2, L"d2", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output_todo(writer, L"<?t1 d1?>\r\n<a>\r\n\t<?t2 d2?>");
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", -1);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_writer_output(writer, L"<?t1 d1?>\r\n<a>\r\n\t<?t2 d2?>\r\n</a>");
+    hr = ISAXContentHandler_processingInstruction(content, L"t3", 2, L"d3", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    hr = ISAXContentHandler_processingInstruction(content, L"t4", 2, L"d4", 2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    check_writer_output_todo(writer, L"<?t1 d1?>\r\n<a>\r\n\t<?t2 d2?>\r\n</a>\r\n<?t3 d3?>\r\n<?t4 d4?>");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
@@ -5531,7 +5499,6 @@ static void test_mxwriter_ignorablespaces(void)
 {
     ISAXContentHandler *content;
     IMXWriter *writer;
-    VARIANT dest;
     HRESULT hr;
 
     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
@@ -5553,12 +5520,7 @@ static void test_mxwriter_ignorablespaces(void)
     hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 1);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"datad", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"datad");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
@@ -5619,12 +5581,7 @@ static void test_mxwriter_dtd(void)
     hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, NULL, 0, NULL, 0);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!DOCTYPE name [\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!DOCTYPE name [\r\n");
 
     /* system id is required if public is present */
     hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, L"pub", 3, NULL, 0);
@@ -5633,13 +5590,8 @@ static void test_mxwriter_dtd(void)
     hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, L"pub", 3, L"sys", 3);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
-        "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
+        "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n");
 
     hr = ISAXLexicalHandler_endDTD(lexical);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -5647,14 +5599,8 @@ static void test_mxwriter_dtd(void)
     hr = IVBSAXLexicalHandler_endDTD(vblexical);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
-         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
+         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n");
 
     /* element declaration */
     V_VT(&dest) = VT_EMPTY;
@@ -5673,13 +5619,7 @@ static void test_mxwriter_dtd(void)
     hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, L"content", 7);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!ELEMENT name content>\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!ELEMENT name content>\r\n");
 
     V_VT(&dest) = VT_EMPTY;
     hr = IMXWriter_put_output(writer, dest);
@@ -5688,13 +5628,7 @@ static void test_mxwriter_dtd(void)
     hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, L"content", 0);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!ELEMENT name >\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!ELEMENT name >\r\n");
 
     /* attribute declaration */
     V_VT(&dest) = VT_EMPTY;
@@ -5705,13 +5639,7 @@ static void test_mxwriter_dtd(void)
             L"#REQUIRED", 9, L"value", 5);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n");
 
     hr = ISAXDeclHandler_attributeDecl(decl, L"element", 7, L"attribute2", 10, L"CDATA", 5,
             L"#REQUIRED", 9, L"value2", 6);
@@ -5721,15 +5649,9 @@ static void test_mxwriter_dtd(void)
             L"#REQUIRED", 9, L"value3", 6);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"
+    check_writer_output(writer, L"<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"
                         "<!ATTLIST element attribute2 CDATA #REQUIRED \"value2\">\r\n"
-                        "<!ATTLIST element2 attribute3 CDATA #REQUIRED \"value3\">\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+                        "<!ATTLIST element2 attribute3 CDATA #REQUIRED \"value3\">\r\n");
 
     /* internal entities */
     V_VT(&dest) = VT_EMPTY;
@@ -5748,12 +5670,7 @@ static void test_mxwriter_dtd(void)
     hr = ISAXDeclHandler_internalEntityDecl(decl, L"name", 4, L"value", 5);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!ENTITY name \"value\">\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!ENTITY name \"value\">\r\n");
 
     /* external entities */
     V_VT(&dest) = VT_EMPTY;
@@ -5781,14 +5698,7 @@ static void test_mxwriter_dtd(void)
     hr = ISAXDeclHandler_externalEntityDecl(decl, L"name", 4, L"pubid", 5, NULL, 0);
     ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<!ENTITY name PUBLIC \"pubid\" \"sysid\">\r\n<!ENTITY name SYSTEM \"sysid\">\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-
-    VariantClear(&dest);
+    check_writer_output(writer, L"<!ENTITY name PUBLIC \"pubid\" \"sysid\">\r\n<!ENTITY name SYSTEM \"sysid\">\r\n");
 
     /* notation declaration */
     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDTDHandler, (void**)&dtd);
@@ -5813,17 +5723,11 @@ static void test_mxwriter_dtd(void)
     hr = ISAXDTDHandler_notationDecl(dtd, L"name", 4, NULL, 0, L"sysid", 5);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(
+    check_writer_output(writer,
         L"<!NOTATION name"
         "<!NOTATION name PUBLIC \"pubid\">\r\n"
         "<!NOTATION name PUBLIC \"pubid\" \"sysid\">\r\n"
-        "<!NOTATION name SYSTEM \"sysid\">\r\n",
-        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-
-    VariantClear(&dest);
+        "<!NOTATION name SYSTEM \"sysid\">\r\n");
 
     ISAXDTDHandler_Release(dtd);
 
@@ -5833,7 +5737,6 @@ static void test_mxwriter_dtd(void)
     IVBSAXDeclHandler_Release(vbdecl);
     ISAXDeclHandler_Release(decl);
     IMXWriter_Release(writer);
-    free_bstrs();
 }
 
 typedef struct {
@@ -6274,7 +6177,6 @@ static void test_mxwriter_indent(void)
 {
     ISAXContentHandler *content;
     IMXWriter *writer;
-    VARIANT dest;
     HRESULT hr;
 
     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer);
@@ -6313,18 +6215,11 @@ static void test_mxwriter_indent(void)
     hr = ISAXContentHandler_endDocument(content);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    V_VT(&dest) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &dest);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n<a><b>\r\n\t\t<c/>\r\n\t</b>\r\n</a>", V_BSTR(&dest)),
-        "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
-    VariantClear(&dest);
+    check_writer_output(writer, L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
+            "<a><b>\r\n\t\t<c/>\r\n\t</b>\r\n</a>");
 
     ISAXContentHandler_Release(content);
     IMXWriter_Release(writer);
-
-    free_bstrs();
 }
 
 static void test_saxreader_vb_content_handler(void)
@@ -6381,12 +6276,7 @@ static void test_mxwriter_from_reader(void)
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     VariantClear(&var);
 
-    V_VT(&var) = VT_EMPTY;
-    hr = IMXWriter_get_output(writer, &var);
-    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    ok(V_VT(&var) == VT_BSTR, "Unexpected output type %d.\n", V_VT(&var));
-    ok(!lstrcmpW(L"<a>text</a>", V_BSTR(&var)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&var)));
-    VariantClear(&var);
+    check_writer_output(writer, L"<a>text</a>");
 
     IMXWriter_Release(writer);
     IVBSAXXMLReader_Release(reader);
@@ -6516,34 +6406,34 @@ static void test_saxreader_max_element_depth(void)
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
     memset(&v, 0, sizeof(v));
-    hr = ISAXXMLReader_getProperty(reader, _bstr_("max-element-depth"), &v);
+    hr = ISAXXMLReader_getProperty(reader, L"max-element-depth", &v);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     ok(V_VT(&v) == VT_I4, "Unexpected type %d.\n", V_VT(&v));
     ok(V_I4(&v) == 5000, "Unexpected value %ld.\n", V_I4(&v));
 
     V_UI4(&v) = 2147483648;
-    hr = ISAXXMLReader_putProperty(reader, _bstr_("max-element-depth"), v);
+    hr = ISAXXMLReader_putProperty(reader, L"max-element-depth", v);
     ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
 
     V_I4(&v) = 2147483647;
-    hr = ISAXXMLReader_putProperty(reader, _bstr_("max-element-depth"), v);
+    hr = ISAXXMLReader_putProperty(reader, L"max-element-depth", v);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    hr = ISAXXMLReader_getProperty(reader, _bstr_("max-element-depth"), &v);
+    hr = ISAXXMLReader_getProperty(reader, L"max-element-depth", &v);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     ok(V_VT(&v) == VT_I4, "Unexpected type %d.\n", V_VT(&v));
     ok(V_I4(&v) == 2147483647, "Unexpected value %ld.\n", V_I4(&v));
 
     V_I4(&v) = 0;
-    hr = ISAXXMLReader_putProperty(reader, _bstr_("max-element-depth"), v);
+    hr = ISAXXMLReader_putProperty(reader, L"max-element-depth", v);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
-    hr = ISAXXMLReader_getProperty(reader, _bstr_("max-element-depth"), &v);
+    hr = ISAXXMLReader_getProperty(reader, L"max-element-depth", &v);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     ok(V_I4(&v) == 0, "Unexpected value %ld.\n", V_I4(&v));
 
     V_I4(&v) = 1;
-    hr = ISAXXMLReader_putProperty(reader, _bstr_("max-element-depth"), v);
+    hr = ISAXXMLReader_putProperty(reader, L"max-element-depth", v);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
     V_VT(&v) = VT_BSTR;
diff --git a/dlls/msxml4/tests/saxreader.c b/dlls/msxml4/tests/saxreader.c
index 196208c8d750..3dc185d723bd 100644
--- a/dlls/msxml4/tests/saxreader.c
+++ b/dlls/msxml4/tests/saxreader.c
@@ -4847,7 +4847,6 @@ static void test_mxwriter_cdata(void)
     hr = IMXWriter_get_output(writer, &dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    todo_wine
     ok(!lstrcmpW(L"<![CDATA[<![CDATA[< > & \"\r\na\r\nb\r\n]]>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
     VariantClear(&dest);
 
diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c
index ad11ea299456..27e6e8c688bd 100644
--- a/dlls/msxml6/tests/saxreader.c
+++ b/dlls/msxml6/tests/saxreader.c
@@ -2855,7 +2855,6 @@ static void test_mxwriter_cdata(void)
     hr = IMXWriter_get_output(writer, &dest);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
-    todo_wine
     ok(!lstrcmpW(L"<![CDATA[<![CDATA[< > & \"\r\na\r\nb\r\n]]>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
     VariantClear(&dest);
 
diff --git a/dlls/ncrypt/tests/ncrypt.c b/dlls/ncrypt/tests/ncrypt.c
index f5dae649a10c..007c33c7e556 100644
--- a/dlls/ncrypt/tests/ncrypt.c
+++ b/dlls/ncrypt/tests/ncrypt.c
@@ -636,7 +636,7 @@ static void test_verify_signature(void)
 
     ret = NCryptVerifySignature(key, &padinfo, sha256_hash, sizeof(sha256_hash), signature_pkcs1_sha256, 4,
                                 NCRYPT_PAD_PKCS1_FLAG);
-    todo_wine ok(ret == NTE_INVALID_PARAMETER, "got %#lx\n", ret);
+    ok(ret == NTE_INVALID_PARAMETER, "got %#lx\n", ret);
 
     invalid_padinfo.pszAlgId = BCRYPT_MD5_ALGORITHM;
     ret = NCryptVerifySignature(key, &invalid_padinfo, sha256_hash, sizeof(sha256_hash), signature_pkcs1_sha256,
diff --git a/dlls/ninput/Makefile.in b/dlls/ninput/Makefile.in
index c9a479af9a81..00f49f545b80 100644
--- a/dlls/ninput/Makefile.in
+++ b/dlls/ninput/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = ninput.dll
 IMPORTLIB = ninput
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index 460aada5c5ed..9a15bc319a4f 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -2,8 +2,8 @@ EXTRADEFS = -D_NTSYSTEM_ -D_ACRTIMP= -DWINBASEAPI=
 MODULE    = ntdll.dll
 UNIXLIB   = ntdll.so
 IMPORTLIB = ntdll
-IMPORTS   = $(TOMCRYPT_PE_LIBS) $(MUSL_PE_LIBS)
-EXTRAINCL = $(TOMCRYPT_PE_CFLAGS)
+IMPORTS   = $(SYMCRYPT_PE_LIBS) $(ZLIB_PE_LIBS) $(MUSL_PE_LIBS)
+EXTRAINCL = $(SYMCRYPT_PE_CFLAGS) $(ZLIB_PE_CFLAGS)
 UNIX_CFLAGS  = $(HWLOC_CFLAGS)
 UNIX_LIBS    = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS) $(HWLOC_LIBS)
 
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c
index a2b7513bfa3a..cdb9bd66a5ff 100644
--- a/dlls/ntdll/exception.c
+++ b/dlls/ntdll/exception.c
@@ -140,6 +140,49 @@ static ULONG remove_vectored_handler( LIST_ENTRY *handler_list, VECTORED_HANDLER
     return ret;
 }
 
+#if defined(__x86_64__) && !defined(__arm64ec__)
+/*
+ * Some vectored exception handlers restore nonvolatile registers before
+ * returning. Preserve the caller state across the callback.
+ */
+extern LONG WINAPI call_vectored_handler( EXCEPTION_POINTERS *ptr, PVECTORED_EXCEPTION_HANDLER func );
+__ASM_GLOBAL_FUNC( call_vectored_handler,
+                    "pushq %rbx\n\t"
+                    __ASM_SEH(".seh_pushreg %rbx\n\t")
+                    "pushq %rsi\n\t"
+                    __ASM_SEH(".seh_pushreg %rsi\n\t")
+                    "pushq %rdi\n\t"
+                    __ASM_SEH(".seh_pushreg %rdi\n\t")
+                    "pushq %rbp\n\t"
+                    __ASM_SEH(".seh_pushreg %rbp\n\t")
+                    "pushq %r12\n\t"
+                    __ASM_SEH(".seh_pushreg %r12\n\t")
+                    "pushq %r13\n\t"
+                    __ASM_SEH(".seh_pushreg %r13\n\t")
+                    "pushq %r14\n\t"
+                    __ASM_SEH(".seh_pushreg %r14\n\t")
+                    "pushq %r15\n\t"
+                    __ASM_SEH(".seh_pushreg %r15\n\t")
+                    "subq $0x28,%rsp\n\t"
+                    __ASM_SEH(".seh_stackalloc 0x28\n\t")
+                    __ASM_SEH(".seh_endprologue\n\t")
+                    "callq *%rdx\n\t"
+                    "addq $0x28,%rsp\n\t"
+                    "popq %r15\n\t"
+                    "popq %r14\n\t"
+                    "popq %r13\n\t"
+                    "popq %r12\n\t"
+                    "popq %rbp\n\t"
+                    "popq %rdi\n\t"
+                    "popq %rsi\n\t"
+                    "popq %rbx\n\t"
+                    "ret" )
+#else
+static inline LONG call_vectored_handler( EXCEPTION_POINTERS *ptr, PVECTORED_EXCEPTION_HANDLER func )
+{
+    return func( ptr );
+}
+#endif
 
 /**********************************************************************
  *           call_vectored_handlers
@@ -172,7 +215,7 @@ static LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
 
         TRACE( "calling handler at %p code=%lx flags=%lx\n",
                func, rec->ExceptionCode, rec->ExceptionFlags );
-        ret = func( &except_ptrs );
+        ret = call_vectored_handler( &except_ptrs, func );
         TRACE( "handler at %p returned %lx\n", func, ret );
 
         RtlEnterCriticalSection( &vectored_handlers_section );
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index b19bf78a4564..443b98411707 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -2105,27 +2105,42 @@ NTSTATUS WINAPI LdrGetProcedureAddress(HMODULE module, const ANSI_STRING *name,
 static void set_security_cookie( ULONG_PTR *cookie )
 {
     static ULONG seed;
+    ULONG_PTR new_cookie;
+    SIZE_T size;
+    void *addr;
+    ULONG old_prot;
 
     TRACE( "initializing security cookie %p\n", cookie );
 
     if (!seed) seed = NtGetTickCount() ^ GetCurrentProcessId();
+    new_cookie = *cookie;
     for (;;)
     {
-        if (*cookie == DEFAULT_SECURITY_COOKIE_16)
-            *cookie = RtlRandom( &seed ) >> 16; /* leave the high word clear */
-        else if (*cookie == DEFAULT_SECURITY_COOKIE_32)
-            *cookie = RtlRandom( &seed );
+        if (new_cookie == DEFAULT_SECURITY_COOKIE_16)
+            new_cookie = RtlRandom( &seed ) >> 16; /* leave the high word clear */
+        else if (new_cookie == DEFAULT_SECURITY_COOKIE_32)
+            new_cookie = RtlRandom( &seed );
 #ifdef DEFAULT_SECURITY_COOKIE_64
-        else if (*cookie == DEFAULT_SECURITY_COOKIE_64)
+        else if (new_cookie == DEFAULT_SECURITY_COOKIE_64)
         {
-            *cookie = RtlRandom( &seed );
+            new_cookie = RtlRandom( &seed );
             /* fill up, but keep the highest word clear */
-            *cookie ^= (ULONG_PTR)RtlRandom( &seed ) << 16;
+            new_cookie ^= (ULONG_PTR)RtlRandom( &seed ) << 16;
         }
 #endif
         else
             break;
     }
+
+    if (new_cookie == *cookie) return;  /* already initialized */
+
+    addr = cookie;
+    size = sizeof(*cookie);
+    if (!NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &old_prot ))
+    {
+        *cookie = new_cookie;
+        NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, old_prot, &old_prot );
+    }
 }
 
 
diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c
index c566ffe158b8..04231658c1ec 100644
--- a/dlls/ntdll/rtl.c
+++ b/dlls/ntdll/rtl.c
@@ -31,7 +31,7 @@
 #include "winbase.h"
 #include "winternl.h"
 #include "ntuser.h"
-#include "tomcrypt.h"
+#include "zlib.h"
 #include "wine/debug.h"
 #include "wine/exception.h"
 #include "ntdll_misc.h"
@@ -883,10 +883,7 @@ void WINAPI RtlAssert(void *assertion, void *filename, ULONG linenumber, char *m
  */
 DWORD WINAPI RtlComputeCrc32(DWORD dwInitial, const BYTE *pData, INT iLen)
 {
-    crc32_state state = { .crc = ~dwInitial };
-
-    crc32_update( &state, pData, iLen );
-    return ~state.crc;
+    return crc32( dwInitial, pData, iLen );
 }
 
 
diff --git a/dlls/ntdll/sec.c b/dlls/ntdll/sec.c
index ad48fc41df12..2cb8d8819062 100644
--- a/dlls/ntdll/sec.c
+++ b/dlls/ntdll/sec.c
@@ -29,14 +29,26 @@
 #include "ntstatus.h"
 #include "windef.h"
 #include "winbase.h"
-#include "tomcrypt.h"
 #include "ntdll_misc.h"
 #include "ddk/ntddk.h"
+#include "symcrypt.h"
 #include "wine/exception.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
 
+/* wrappers for symcrypt */
+SYMCRYPT_CPU_FEATURES SYMCRYPT_CALL SymCryptCpuFeaturesNeverPresent(void) { return 0; }
+void SYMCRYPT_CALL SymCryptFatal( UINT32 fatalCode ) { }
+void SYMCRYPT_CALL SymCryptInjectError( PBYTE pbBuf, SIZE_T cbBuf ) { }
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptSaveXmm( PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ) { return SYMCRYPT_NO_ERROR; }
+void SYMCRYPT_CALL SymCryptRestoreXmm( PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ) { }
+#endif
+__ASM_GLOBAL_IMPORT(memcmp)
+__ASM_GLOBAL_IMPORT(memcpy)
+__ASM_GLOBAL_IMPORT(memset)
+
 #define SELF_RELATIVE_FIELD(sd,field) ((BYTE *)(sd) + ((SECURITY_DESCRIPTOR_RELATIVE *)(sd))->field)
 
 static const SID world_sid = { SID_REVISION, 1, { SECURITY_WORLD_SID_AUTHORITY} , { SECURITY_WORLD_RID } };
@@ -1878,13 +1890,12 @@ NTSTATUS WINAPI RtlCreateServiceSid( PUNICODE_STRING name, PSID pSid, LPDWORD le
     DWORD sid_length;
     NTSTATUS status;
     UNICODE_STRING name_upper;
-    hash_state hash_ctx;
-    ULONG hash[5];
     SID *sid = (SID *)pSid;
+    ULONG count = 1 + SYMCRYPT_SHA1_RESULT_SIZE / sizeof(sid->SubAuthority[0]);
 
     if (name == NULL || len == NULL) return STATUS_INVALID_PARAMETER;
 
-    sid_length = RtlLengthRequiredSid( 1 + ARRAY_SIZE(hash) );
+    sid_length = RtlLengthRequiredSid( count );
     if (*len < sid_length)
     {
         *len = sid_length;
@@ -1893,17 +1904,14 @@ NTSTATUS WINAPI RtlCreateServiceSid( PUNICODE_STRING name, PSID pSid, LPDWORD le
 
     sid->Revision = SID_REVISION;
     sid->IdentifierAuthority = nt_authority;
-    sid->SubAuthorityCount = 1 + ARRAY_SIZE(hash);
+    sid->SubAuthorityCount = count;
     sid->SubAuthority[0] = SECURITY_SERVICE_ID_BASE_RID;
     *len = sid_length;
 
     if ((status = RtlUpcaseUnicodeString( &name_upper, name, TRUE ))) return status;
 
-    sha1_init( &hash_ctx );
-    sha1_process( &hash_ctx, (UCHAR *)name_upper.Buffer, name_upper.Length );
-    sha1_done( &hash_ctx, (UCHAR *)hash );
+    SymCryptSha1( (BYTE *)name_upper.Buffer, name_upper.Length, (BYTE *)(sid->SubAuthority + 1) );
     RtlFreeUnicodeString( &name_upper );
-    memcpy( sid->SubAuthority + 1, hash, sizeof(hash) );
     return STATUS_SUCCESS;
 }
 
@@ -1915,7 +1923,6 @@ NTSTATUS WINAPI RtlDeriveCapabilitySidsFromName( UNICODE_STRING *cap_name, PSID
     static const SID_IDENTIFIER_AUTHORITY app_authority = { SECURITY_APP_PACKAGE_AUTHORITY };
     static const SID_IDENTIFIER_AUTHORITY nt_authority = { SECURITY_NT_AUTHORITY };
     UNICODE_STRING cap_upcase;
-    hash_state hash_ctx;
     NTSTATUS status;
     ULONG hash[8];
     SID *sid;
@@ -1923,9 +1930,7 @@ NTSTATUS WINAPI RtlDeriveCapabilitySidsFromName( UNICODE_STRING *cap_name, PSID
     TRACE( "cap_name %s, cap_group_sid %p, cap_sid %p.\n", debugstr_us(cap_name), cap_group_sid, cap_sid );
 
     if ((status = RtlUpcaseUnicodeString( &cap_upcase, cap_name, TRUE ))) return status;
-    sha256_init( &hash_ctx );
-    sha256_process( &hash_ctx, (UCHAR *)cap_upcase.Buffer, cap_upcase.Length );
-    sha256_done( &hash_ctx, (UCHAR *)hash );
+    SymCryptSha256( (BYTE *)cap_upcase.Buffer, cap_upcase.Length, (BYTE *)hash );
     RtlFreeUnicodeString( &cap_upcase );
 
     sid = cap_sid;
diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c
index baee1f237221..140775d1a724 100644
--- a/dlls/ntdll/signal_arm64ec.c
+++ b/dlls/ntdll/signal_arm64ec.c
@@ -2042,8 +2042,12 @@ void __attribute((naked)) RtlRaiseException( EXCEPTION_RECORD *rec )
          "add x0, sp, #0x20\n\t"
          "bl \"#RtlCaptureContext\"\n\t"
          "add x1, sp, #0x20\n\t"       /* context pointer */
+         "add x0, x1, #0x4d0\n\t"      /* orig stack pointer */
+         "str x0, [x1, #0x98]\n\t"     /* context->Rsp */
          "ldr x0, [sp, #0x10]\n\t"     /* rec */
-         "ldr x2, [x1, #0xf8]\n\t"     /* context->Rip */
+         "str x0, [x1, #0x80]\n\t"     /* context->Rcx */
+         "ldr x2, [sp, #0x08]\n\t"     /* return address */
+         "str x2, [x1, #0xf8]\n\t"     /* context->Rip */
          "str x2, [x0, #0x10]\n\t"     /* rec->ExceptionAddress */
          "ldr w2, [x1, #0x30]\n\t"     /* context->ContextFlags */
          "orr w2, w2, #0x20000000\n\t" /* CONTEXT_UNWOUND_TO_CALL */
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
index 5a56c2d7d64a..522ade368f06 100644
--- a/dlls/ntdll/unix/signal_arm64.c
+++ b/dlls/ntdll/unix/signal_arm64.c
@@ -110,8 +110,10 @@ static struct fpsimd_context *get_fpsimd_context( const ucontext_t *sigcontext )
 
 static DWORD64 get_fault_esr( ucontext_t *sigcontext )
 {
+#ifdef ESR_MAGIC
     struct esr_context *esr = (struct esr_context *)get_extended_sigcontext( sigcontext, ESR_MAGIC );
     if (esr) return esr->esr;
+#endif
     return 0;
 }
 
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index a42be942ca28..41293fe56077 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -536,14 +536,12 @@ static unsigned int xstate_size = sizeof(XSAVE_AREA_HEADER);
 static UINT64 xstate_extended_features;
 static LONG syscall_dispatch_enabled = TRUE;
 
-#if defined(__linux__) || defined(__APPLE__)
 static inline struct thread_data *get_current_thread_data(void)
 {
     unsigned long rsp;
     __asm__( "movq %%rsp,%0" : "=r" (rsp) );
     return (struct thread_data *)(rsp & ~signal_stack_mask);
 }
-#endif
 
 extern void __wine_syscall_dispatcher_instrumentation(void);
 static void *instrumentation_callback;
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c
index bcfb3306aea5..cf2883f05aee 100644
--- a/dlls/ntdll/unix/system.c
+++ b/dlls/ntdll/unix/system.c
@@ -1966,6 +1966,7 @@ static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs
         regs[regidx++] = (struct smbios_wine_id_reg_value_arm64){ 0x4000, value };
     }
 
+#ifdef HWCAP_CPUID
     if (!(getauxval(AT_HWCAP) & HWCAP_CPUID))
     {
         WARN( "Skipping ID register population as kernel is missing emulation support.\n" );
@@ -1998,6 +1999,9 @@ static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs
     READ_ID_REG( 0x5801 ); /* CTR_EL0 */
     /* Windows exposes SCTLR_EL1, ACTLR_EL1, TTBR0_EL1 and MAIR_EL1, but these are inaccessible under
      * linux so leave them unpopulated. */
+#else
+    WARN( "Skipping ID register population as HWCAP_CPUID isn't supported.\n" );
+#endif
 
 #undef READ_ID_REG
 #undef STR
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index a2ea5244b9f1..f6173f0d1e84 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -843,7 +843,7 @@ void *get_builtin_so_handle( void *module )
 static NTSTATUS get_unixlib_funcs( void *so_handle, BOOL wow, const void **funcs, NTSTATUS (**entry)(void) )
 {
     *funcs = dlsym( so_handle, wow ? "__wine_unix_call_wow64_funcs" : "__wine_unix_call_funcs" );
-    *entry = dlsym( so_handle, "__wine_unix_lib_init" );
+    if (!*funcs) *entry = dlsym( so_handle, "__wine_unix_lib_init" );
     return *funcs || *entry ? STATUS_SUCCESS : STATUS_ENTRYPOINT_NOT_FOUND;
 }
 
@@ -6182,7 +6182,7 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
             {
                 UINT64 res[2];
                 const UNICODE_STRING *name = addr;
-                NTSTATUS (*entry)(void);
+                NTSTATUS (*entry)(void) = NULL;
                 const void *funcs;
                 void *handle;
 
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 015ecec36032..bf6554debe17 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -3912,25 +3912,42 @@ static void update_security_cookie( void *module, IMAGE_NT_HEADERS *nt )
     {
         static ULONG seed;
         ULONG_PTR *cookie = (ULONG_PTR *)cfg->SecurityCookie;
+        ULONG_PTR new_cookie;
+        SIZE_T prot_size;
+        void *prot_addr;
+        ULONG old_prot;
 
         TRACE( "initializing security cookie %p\n", cookie );
 
         if (!seed) seed = NtGetTickCount() ^ GetCurrentProcessId();
+
+        new_cookie = *cookie;
         for (;;)
         {
-            if (*cookie == DEFAULT_SECURITY_COOKIE_16)
-                *cookie = RtlRandom( &seed ) >> 16; /* leave the high word clear */
-            else if (*cookie == DEFAULT_SECURITY_COOKIE_32)
-                *cookie = RtlRandom( &seed );
+            if (new_cookie == DEFAULT_SECURITY_COOKIE_16)
+                new_cookie = RtlRandom( &seed ) >> 16; /* leave the high word clear */
+            else if (new_cookie == DEFAULT_SECURITY_COOKIE_32)
+                new_cookie = RtlRandom( &seed );
 #ifdef DEFAULT_SECURITY_COOKIE_64
-            else if (*cookie == DEFAULT_SECURITY_COOKIE_64)
+            else if (new_cookie == DEFAULT_SECURITY_COOKIE_64)
             {
-                *cookie = RtlRandom( &seed );
+                new_cookie = RtlRandom( &seed );
                 /* fill up, but keep the highest word clear */
-                *cookie ^= (ULONG_PTR)RtlRandom( &seed ) << 16;
+                new_cookie ^= (ULONG_PTR)RtlRandom( &seed ) << 16;
             }
 #endif
-            else break;
+            else
+                break;
+        }
+
+        if (new_cookie == *cookie) return;  /* already initialized */
+
+        prot_addr = cookie;
+        prot_size = sizeof(*cookie);
+        if (!NtProtectVirtualMemory( NtCurrentProcess(), &prot_addr, &prot_size, PAGE_READWRITE, &old_prot ))
+        {
+            *cookie = new_cookie;
+            NtProtectVirtualMemory( NtCurrentProcess(), &prot_addr, &prot_size, old_prot, &old_prot );
         }
     }
 }
diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c
index d3f4760cfe12..39700ca785f2 100644
--- a/dlls/ole32/storage32.c
+++ b/dlls/ole32/storage32.c
@@ -2363,16 +2363,73 @@ static HRESULT WINAPI StorageBaseImpl_CopyTo(
 /*************************************************************************
  * MoveElementTo (IStorage)
  */
-static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
-  IStorage*     iface,
-  const OLECHAR *pwcsName,   /* [string][in] */
-  IStorage      *pstgDest,   /* [unique][in] */
-  const OLECHAR *pwcsNewName,/* [string][in] */
-  DWORD           grfFlags)    /* [in] */
+static HRESULT WINAPI StorageBaseImpl_MoveElementTo(IStorage *iface,
+        const OLECHAR *name, IStorage *dest, const OLECHAR *new_name, DWORD mode)
 {
-  FIXME("%p, %s, %p, %s, %#lx: stub\n", iface, debugstr_w(pwcsName), pstgDest,
-      debugstr_w(pwcsNewName), grfFlags);
-  return E_NOTIMPL;
+    IStream *src, *dst;
+    HRESULT hr;
+    DWORD create_mode;
+
+    TRACE("%p, %s, %p, %s, %#lx\n", iface, debugstr_w(name), dest, debugstr_w(new_name), mode);
+
+    if (mode != STGMOVE_COPY && mode != STGMOVE_MOVE)
+        return STG_E_INVALIDFLAG;
+
+    if (!name || !new_name)
+        return STG_E_INVALIDNAME;
+
+    if (!dest)
+        return STG_E_INVALIDPOINTER;
+
+    if (iface == dest) /* FIXME */
+        return STG_E_ACCESSDENIED;
+
+    create_mode = STGM_WRITE | STGM_SHARE_EXCLUSIVE;
+    create_mode |= (mode == STGMOVE_MOVE) ? STGM_FAILIFTHERE : STGM_CREATE;
+
+    hr = IStorage_OpenStream(iface, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &src);
+    if (hr == S_OK)
+    {
+        STATSTG stat;
+
+        hr = IStream_Stat(src, &stat, STATFLAG_NONAME);
+        if (hr != S_OK)
+        {
+            IStream_Release(src);
+            return hr;
+        }
+
+        hr = IStorage_CreateStream(dest, new_name, create_mode, 0, 0, &dst);
+        if (hr == S_OK)
+        {
+            hr = IStream_CopyTo(src, dst, stat.cbSize, NULL, NULL);
+            IStream_Release(dst);
+        }
+
+        IStream_Release(src);
+    }
+    else if (hr == STG_E_FILENOTFOUND)
+    {
+        IStorage *src_stg, *dst_stg;
+
+        hr = IStorage_OpenStorage(iface, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &src_stg);
+        if (hr == S_OK)
+        {
+            hr = IStorage_CreateStorage(dest, new_name, create_mode, 0, 0, &dst_stg);
+            if (hr == S_OK)
+            {
+                hr = IStorage_CopyTo(src_stg, 0, NULL, NULL, dst_stg);
+                IStorage_Release(dst_stg);
+            }
+
+            IStorage_Release(src_stg);
+        }
+    }
+
+    if (hr == S_OK && mode == STGMOVE_MOVE)
+        hr = IStorage_DestroyElement(iface, name);
+
+    return hr;
 }
 
 /*************************************************************************
diff --git a/dlls/ole32/tests/storage32.c b/dlls/ole32/tests/storage32.c
index cb77b0252938..7bca822aa961 100644
--- a/dlls/ole32/tests/storage32.c
+++ b/dlls/ole32/tests/storage32.c
@@ -3937,6 +3937,114 @@ static void test_custom_lockbytes(void)
     DeleteTestLockBytes(lockbytes);
 }
 
+static void test_MoveElementTo(void)
+{
+    WCHAR temp[MAX_PATH], src_name[MAX_PATH], dst_name[MAX_PATH];
+    IStorage *src, *dst, *stg;
+    IStream *stream;
+    HRESULT hr;
+
+    GetTempPathW(MAX_PATH, temp);
+    GetTempFileNameW(temp, L"stg", 0, src_name);
+    GetTempFileNameW(temp, L"stg", 0, dst_name);
+
+    hr = StgCreateDocfile(src_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &src);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = create_test_file(src);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = StgCreateDocfile(dst_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &dst);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    /* STGTY_STREAM */
+
+    hr = IStorage_MoveElementTo(src, NULL, NULL, NULL, 0);
+    ok(hr == STG_E_INVALIDNAME, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmA_name, dst, strmC_name, ~0);
+    ok(hr == STG_E_INVALIDFLAG, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmA_name, dst, strmC_name, STGMOVE_COPY);
+    ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, dst, NULL, 0);
+    ok(hr == STG_E_INVALIDNAME, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, NULL, strmC_name, STGMOVE_COPY);
+    ok(hr == STG_E_INVALIDPOINTER, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, NULL, strmC_name, ~0);
+    ok(hr == STG_E_INVALIDFLAG, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, src, strmC_name, STGMOVE_COPY);
+    ok(hr == STG_E_ACCESSDENIED, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_COPY);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    /* STGMOVE_COPY doesn't fail if the target already exsists */
+    hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_COPY);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    /* STGMOVE_MOVE fails if the target already exsists */
+    hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_MOVE);
+    ok(hr == STG_E_FILEALREADYEXISTS, "got %#lx\n", hr);
+
+    hr = IStorage_DestroyElement(dst, strmC_name);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_MOVE);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = IStorage_OpenStream(src, strmC_name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
+    ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr);
+
+    hr = IStorage_OpenStream(dst, strmC_name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
+    ok(hr == S_OK, "got %#lx\n", hr);
+    IStream_Release(stream);
+
+    hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_COPY);
+    ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr);
+
+    /* STGTY_STORAGE */
+
+    hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    /* STGMOVE_COPY doesn't fail if the target already exsists */
+    hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    /* STGMOVE_MOVE fails if the target already exsists */
+    hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_MOVE);
+    ok(hr == STG_E_FILEALREADYEXISTS, "got %#lx\n", hr);
+
+    hr = IStorage_DestroyElement(dst, stgB_name);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_MOVE);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY);
+    ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, stgB_name, dst, stgA_name, STGMOVE_MOVE);
+    ok(hr == S_OK, "got %#lx\n", hr);
+
+    hr = IStorage_MoveElementTo(src, stgB_name, dst, stgA_name, STGMOVE_COPY);
+    ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr);
+
+    hr = IStorage_OpenStorage(dst, stgB_name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
+    ok(hr == S_OK, "got %#lx\n", hr);
+    IStorage_Release(stg);
+
+    IStorage_Release(src);
+    IStorage_Release(dst);
+    DeleteFileW(src_name);
+    DeleteFileW(dst_name);
+}
+
 START_TEST(storage32)
 {
     CHAR temp[MAX_PATH];
@@ -3986,4 +4094,5 @@ START_TEST(storage32)
     test_transacted_shared();
     test_overwrite();
     test_custom_lockbytes();
+    test_MoveElementTo();
 }
diff --git a/dlls/opengl32/make_opengl b/dlls/opengl32/make_opengl
index 3dec39cc0f09..ad4afc54076b 100755
--- a/dlls/opengl32/make_opengl
+++ b/dlls/opengl32/make_opengl
@@ -161,6 +161,10 @@ my %manual_win_thunks =
      "glGetString" => 1,
      "glGetStringi" => 1,
      "glImportSyncEXT" => 1,
+     "glGetFramebufferAttachmentParameteriv" => 1,
+     "glGetFramebufferAttachmentParameterivEXT" => 1,
+     "glGetNamedFramebufferAttachmentParameteriv" => 1,
+     "glGetNamedFramebufferAttachmentParameterivEXT" => 1,
      "wglChoosePixelFormatARB" => 1,
      "wglCreateContextAttribsARB" => 1,
      "wglCreatePbufferARB" => 1,
@@ -172,6 +176,7 @@ my %manual_win_thunks =
      "wglMakeContextCurrentARB" => 1,
      "wglQueryCurrentRendererStringWINE" => 1,
      "wglQueryRendererStringWINE" => 1,
+     "wglShareLists" => 1,
      "wglSwapBuffers" => 1,
     );
 
@@ -677,19 +682,129 @@ sub get_handle_function($)
     return 0;
 }
 
+sub get_object_type($$$$)
+{
+    my ($func, $ptype, $pname, $class) = @_;
+
+    $class = "program" if $class eq "program/shader" and has_extension( $func, "GL_ARB_fragment_program" );
+    $class = "program" if $class eq "program/shader" and has_extension( $func, "GL_ARB_vertex_program" );
+    $class = "program" if $class eq "program/shader" and has_extension( $func, "GL_NV_fragment_program" );
+    $class = "program" if $class eq "program/shader" and has_extension( $func, "GL_NV_vertex_program" );
+    $class = "program" if $class eq "program/shader" and has_extension( $func, "GL_EXT_direct_state_access" );
+
+    $class = "shader_ati" if has_extension( $func, "GL_ATI_fragment_shader" );
+    $class = "shader_ext" if has_extension( $func, "GL_EXT_vertex_shader" );
+
+    return "srcTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE" if $pname eq "srcName";
+    return "dstTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE" if $pname eq "dstName";
+
+    return "OBJ_TYPE_BUFFER"              if $class eq "buffer";
+    return "OBJ_TYPE_BUFFER"              if $func eq "glDeleteObjectBufferATI" and $pname eq "buffer";
+    return "OBJ_TYPE_BUFFER"              if $func eq "glDrawCommandsNV" and $pname eq "buffer";
+    return "OBJ_TYPE_DISPLAY_LIST"        if $class eq "display list";
+    return "OBJ_TYPE_FRAMEBUFFER"         if $class eq "framebuffer";
+    return "OBJ_TYPE_FRAMEBUFFER"         if $pname =~ "fbo";
+    return "OBJ_TYPE_MEMORY"              if $class eq "memory";
+    return "OBJ_TYPE_MEMORY"              if $ptype =~ /GLuint \*/ and $pname =~ /^memoryObjects/;
+    return "OBJ_TYPE_MEMORY"              if $ptype =~ /GLuint/ and $pname =~ /^memory(Object)?$/;
+    return "OBJ_TYPE_PATH"                if $class eq "Path";
+    return "OBJ_TYPE_PATH"                if $func =~ /^glGenPaths/ and $pname eq "ret";
+    return "OBJ_TYPE_PATH"                if $func eq "glPathGlyphIndexArrayNV" and $pname eq "firstPathName";
+    return "OBJ_TYPE_PATH"                if $func eq "glPathMemoryGlyphIndexArrayNV" and $pname eq "firstPathName";
+    return "OBJ_TYPE_PROGRAM"             if $class eq "program";
+    return "OBJ_TYPE_RENDERBUFFER"        if $class eq "renderbuffer";
+    return "OBJ_TYPE_SAMPLER"             if $class eq "sampler";
+    return "OBJ_TYPE_SEMAPHORE"           if $class eq "semaphore";
+    return "OBJ_TYPE_SEMAPHORE"           if $ptype =~ /GLuint \*/ and $pname =~ /^(waitS|signalS|s)emaphore(s|Array)$/;
+    return "OBJ_TYPE_SEMAPHORE"           if $ptype =~ /GLuint/ and $pname =~ /^semaphore$/;
+    return "OBJ_TYPE_SHADER_ATI"          if $class eq "shader_ati" and $func =~ /^gl(Bind|GenFragment|Delete)/ and $pname =~ /^(id|ret)$/;
+    return "OBJ_TYPE_SHADER_EXT"          if $class eq "shader_ext" and $func =~ /^gl(Bind|GenVertex|Delete)/ and $pname =~ /^(id|ret)$/;
+    return "OBJ_TYPE_TEXTURE"             if $class eq "texture";
+    return "OBJ_TYPE_TEXTURE"             if $func =~ /^glVDPAURegister/ and $pname eq "textureNames";
+    return "OBJ_TYPE_TEXTURE"             if $func =~ /^gl(LGPU|Multicast|Async|)CopyImageSubData/ and $pname =~ /^(src|dst)Name$/;
+    return 0                              if $class eq "program/shader";
+
+    return 0 if $class eq "Color";
+    return 0 if $class eq "ColorIndexMask";
+    return 0 if $class eq "perf query handle";
+    return 0 if $class eq "perf query id";
+    return 0 if $class eq "program pipeline";
+    return 0 if $class eq "query";
+    return 0 if $class eq "StencilMask";
+    return 0 if $class eq "StencilValue";
+    return 0 if $class eq "SelectName";
+    return 0 if $class eq "sync";
+    return 0 if $class eq "transform feedback";
+    return 0 if $class eq "vertex array";
+    return 0 if $class =~ /^Vector/;
+    return 0 if $func eq "glGetActiveAtomicCounterBufferiv" and $pname eq "bufferIndex";
+    return 0 if $func eq "glSelectPerfMonitorCountersAMD" and $pname eq "counterList";
+    return 0 if $func =~ /^glBlend(Equation|Func)/ and $pname eq "buf";
+    return 0 if $func =~ /^gl(Signal|Wait)Semaphore/ and $pname eq "numBufferBarriers";
+    return 0 if $func =~ /^gl(Signal|Wait)Semaphore/ and $pname eq "numTextureBarriers";
+    return 0 if $func eq "glCallCommandListNV" and $pname =~ /^list/;
+    return 0 if $func eq "glCommandListSegmentsNV" and $pname =~ /^list/;
+    return 0 if $func eq "glCompileCommandListNV" and $pname =~ /^list/;
+    return 0 if $func eq "glCreateCommandListsNV" and $pname =~ /^list/;
+    return 0 if $func eq "glDeleteCommandListsNV" and $pname =~ /^list/;
+    return 0 if $func eq "glIsCommandListNV" and $pname =~ /^list/;
+    return 0 if $func eq "glListDrawCommandsStatesClientNV" and $pname =~ /^list/;
+    return 0 if $ptype !~ /GLuint|GLhandleARB/;
+    return 0 if $ptype =~ /GLuint \*/ and $pname =~ /^fences$/;
+    return 0 if $pname eq "fence";
+    return 0 if $pname eq "vkSemaphore";
+    return 0 if $pname eq "vkFence";
+    return 0 if $pname eq "fenceValueArray";
+    return 0 if $pname eq "pathParameterTemplate";
+
+    print "Missing possible buffer: $func $ptype $pname $class\n" if lc( $pname ) =~ /buffer/;
+    print "Missing possible display list: $func $ptype $pname $class\n" if lc( $pname ) =~ /list/;
+    print "Missing possible framebuffer: $func $ptype $pname $class\n" if lc( $pname ) =~ /fbo/;
+    print "Missing possible memory: $func $ptype $pname $class\n" if lc( $pname ) =~ /memory/;
+    print "Missing possible path: $func $ptype $pname $class\n" if lc( $pname ) =~ /path/;
+    print "Missing possible program: $func $ptype $pname $class\n" if lc( $pname ) =~ /program/;
+    print "Missing possible sampler: $func $ptype $pname $class\n" if lc( $pname ) =~ /sampler/;
+    print "Missing possible semaphore: $func $ptype $pname $class\n" if lc( $pname ) =~ /semaphore/;
+    print "Missing possible texture: $func $ptype $pname $class\n" if lc( $pname ) =~ /texture/;
+    print "Missing possible list: $func $ptype $pname $class\n" if lc( $pname ) =~ /list/;
+    return 0;
+}
+
+sub allocate_object_names($)
+{
+    my $name = shift;
+    my $ret = $name =~ /(APPLE|ARB|ATI|EXT|NV)$/ ? "TRUE" : "FALSE";
+
+    return "TRUE" if has_extension( $name, "GL_EXT_direct_state_access" );
+    return "TRUE" if has_extension( $name, "GL_NV_path_rendering" );
+    return $ret if $name =~ /^glBindBuffer/;
+    return $ret if $name =~ /^glBindFramebuffer/;
+    return $ret if $name =~ /^glBindProgram/;
+    return $ret if $name =~ /^glBindRenderbuffer/;
+    return $ret if $name =~ /^glBindSampler/;
+    return $ret if $name =~ /^glBindTexture/;
+    return $ret if $name =~ /^glBindFragmentShader/;
+    return $ret if $name =~ /^glBindVertexShader/;
+    return $ret if $name =~ /^glImportSemaphore/;
+    return $ret if $name =~ /^glNewList/;
+
+    return 0;
+}
+
 sub generate_win_thunk($$)
 {
     my ($name, $func) = @_;
     my $get_integer = get_integer_call( $name, $func );
     my $decl_args = get_func_args( $func );
     my $func_ret = get_func_ret( $func );
+    my $post_call = "";
+    my $map_args = "";
     my $params = "";
     my $checks = "";
     my $ret = "";
 
     $ret .= "$func_ret WINAPI $name($decl_args)\n";
     $ret .= "{\n";
-    $ret .= "    struct $name\_params args";
     $params .= " .teb = NtCurrentTeb()";
     foreach my $arg (@{$func->[1]})
     {
@@ -701,19 +816,80 @@ sub generate_win_thunk($$)
             $retval = "{ set_gl_error( GL_INVALID_VALUE ); $retval }" if $ptype =~ /GLsync/ && $name !~ /glIsSync/;
             $checks .= "    if (!$get_handle( $pname, &args.$pname )) $retval\n";
         }
+        elsif (my $map_type = get_object_type( $name, $ptype, $pname, get_arg_class( $arg ) ))
+        {
+            my $len = get_arg_len( $arg );
+            my $addr = $len eq "1" ? "&" : "";
+            my $deref = $len eq "1" ? "*" : "";
+
+            $len = "count" if $name =~ /^glDrawCommandsStates/ and $pname eq "fbos";
+            $addr = "" if $name =~ /^glDrawCommandsStates/ and $pname eq "fbos";
+
+            if ($name =~ /^(glGen|glCreate)/ and $name !~ /^glGenerate/)
+            {
+                $len = "n" if $name eq "glCreateMemoryObjectsEXT";
+                $addr = "" if $name eq "glCreateMemoryObjectsEXT";
+                $params .= ", .$pname = $pname";
+                $post_call .= "    if ($len > 0) put_context_objects( $map_type, $len, $addr$pname );\n";
+            }
+            elsif ($name =~ /^glDelete/)
+            {
+                if ($len eq "1")
+                {
+                    $map_args .= "    args.$pname = *del_context_objects( $map_type, 1, &$pname );\n";
+                }
+                else
+                {
+                    my ($buf, $tmp) = ($pname . "_buf", $pname . "_tmp");
+                    $ret .= "    GLuint $buf\[64\], *$tmp;\n";
+
+                    $map_args .= "    $tmp = $len > 0 ? memdup_objects( $len, $pname, $buf, ARRAY_SIZE($buf) ) : NULL;\n";
+                    $map_args .= "    args.$pname = $len > 0 ? del_context_objects( $map_type, $len, $tmp ) : NULL;\n";
+                    $post_call .= "    if ($tmp != $buf) free( $tmp );\n";
+                }
+            }
+            else
+            {
+                my $return = is_void_func($func) ? "" : " args.ret";
+                my $alloc = allocate_object_names( $name );
+
+                if ($len eq "1")
+                {
+                    $checks .= "    if (!alloc_context_objects( $map_type, 1, &$pname, $alloc )) return$return;\n" if $alloc;
+                    $map_args .= "    args.$pname = *map_context_objects( $map_type, 1, &$pname );\n";
+                }
+                else
+                {
+                    my ($buf, $tmp) = ($pname . "_buf", $pname . "_tmp");
+                    $ret .= "    GLuint $buf\[64\], *$tmp;\n";
+                    $len =~ s/^COMPSIZE\((.*)\)$/$1/;
+
+                    $checks .= "    if (!alloc_context_objects( $map_type, $len, $pname, $alloc )) return$return;\n" if $alloc;
+                    $map_args .= "    $tmp = $len > 0 ? memdup_objects( $len, $pname, $buf, ARRAY_SIZE($buf) ) : NULL;\n";
+                    $map_args .= "    args.$pname = $len > 0 ? map_context_objects( $map_type, $len, $tmp ) : NULL;\n";
+                    $post_call .= "    if ($tmp != $buf) free( $tmp );\n";
+                }
+            }
+        }
         else
         {
             $params .= ", .$pname = $pname";
         }
     }
-    $ret .= " = {$params }";
-    $ret .= ";\n";
+    if (my $map_type = get_object_type( $name, $func_ret, "ret", get_arg_class( $func->[0] ) ))
+    {
+        $post_call .= "    args.ret = put_context_object_range( $map_type, range, args.ret );\n" if $name =~ /^glGen/;
+        $post_call .= "    put_context_objects( $map_type, 1, &args.ret );\n" if $name =~ /^glCreate/;
+    }
+    $ret .= "    struct $name\_params args = {$params };\n";
     $ret .= "    NTSTATUS status;\n";
     $ret .= "    int integer;\n" if $get_integer;
     $ret .= "    " . get_func_trace( $name, $func, 1 );
     $ret .= $checks;
-    $ret .= $get_integer ? "    $get_integer\n    else " : "    ";
-    $ret .= "if ((status = UNIX_CALL( $name, &args ))) WARN( \"$name returned %#lx\\n\", status );\n";
+    $ret .= $map_args;
+    $ret .= "    if ((status = UNIX_CALL( $name, &args ))) WARN( \"$name returned %#lx\\n\", status );\n";
+    $ret .= "    else $get_integer\n" if $get_integer;
+    $ret .= $post_call;
     $ret .= "    return args.ret;\n" unless is_void_func($func);
     $ret .= "}\n";
 
@@ -817,6 +993,19 @@ sub get_arg_name($)
     return $name[0]->textContent();
 }
 
+sub get_arg_class($)
+{
+    my $p = shift;
+    return "program/shader" if defined $p->{class} and $p->{class} eq "program";
+    return $p->{class} || $p->{kind} || "";
+}
+
+sub get_arg_len($)
+{
+    my $p = shift;
+    return $p->{len} || "1";
+}
+
 #
 # Fetch the registry files
 #
@@ -1069,6 +1258,12 @@ sub is_exposed_function($)
     return 0;
 }
 
+sub has_extension($$)
+{
+    my ($func, $name) = @_;
+    return grep( /^$name$/, keys %{$ext_functions{$func}->[2]} );
+}
+
 # some functions need a hand-written wrapper
 sub needs_wrapper($$)
 {
@@ -1097,6 +1292,9 @@ sub add_extension($)
 sub get_integer_call($)
 {
     my ($name, $func) = @_;
+    my $to_int = "";
+    my $to_val = "";
+    my $index = 0;
     my $params;
     my $pname;
 
@@ -1105,13 +1303,81 @@ sub get_integer_call($)
         my $name = get_arg_name( $arg );
         my $type = get_arg_type( $arg );
         $pname = $name if $name =~ /^(pname|target|value)$/;
+        $index = $name if $name eq "index";
+        $to_val = "(void *)(UINT_PTR)" if $type =~ /void \*/;
+        $to_int = "(UINT_PTR)" if $type =~ /void \*/;
         $params = $name;
     }
 
     return 0 if !$pname;
-
-    my $hook = "if (get_integer( $pname, &integer )) *$params = integer;";
-    return $hook if $name =~ /^glGet(Boolean|Double|Float|Integer|Integer64)v/;
+    return 0 if $name !~ /^glGet/;
+
+    my $hook = "if (get_integer( $pname, $index, $to_int*$params, &integer )) *$params = $to_val" . "integer;";
+    return $hook if $name =~ /^glGet(Boolean|Double|Fixed|Float|Integer|Pointer|UnsignedByte)/;
+    return $hook if $name =~ /^glGet(Multi)?Tex(ture)?LevelParameter/;
+    return $hook if $name =~ /^glGet(Named)?FramebufferAttachmentParameter/;
+    return $hook if $name =~ /^glGetProgramivARB/;
+    return $hook if $name =~ /^glGetProgramPipeline/;
+    return $hook if $name =~ /^glGetTransformFeedback/;
+    return $hook if $name =~ /^glGetVertex(Array|Attrib)/;
+
+    return 0 if $name =~ /^glGet(Detail|Sharpen)?Tex(Filter)?Func/;
+    return 0 if $name =~ /^glGet(Final)?Combiner/;
+    return 0 if $name =~ /^glGet(Multi)?Tex(ture)?Parameter/;
+    return 0 if $name =~ /^glGet(Multi)?TexEnv/;
+    return 0 if $name =~ /^glGet(Multi|Path)?TexGen/;
+    return 0 if $name =~ /^glGet(Named)?BufferParameter/;
+    return 0 if $name =~ /^glGet(Named)?BufferPointer/;
+    return 0 if $name =~ /^glGet(Named)?FramebufferParameter/;
+    return 0 if $name =~ /^glGet(Named)?Program(Env|Local|Subroutine)?Parameter/;
+    return 0 if $name =~ /^glGet(Named)?Program(Interface|Stage|String)/;
+    return 0 if $name =~ /^glGet(Named)?RenderbufferParameter/;
+    return 0 if $name =~ /^glGetActive(Subroutine)?Uniform/;
+    return 0 if $name =~ /^glGetActiveAtomicCounterBuffer/;
+    return 0 if $name =~ /^glGetArrayObject/;
+    return 0 if $name =~ /^glGetBuffer(Sub)?Data/;
+    return 0 if $name =~ /^glGetFence/;
+    return 0 if $name =~ /^glGetFragment(Material|Light)/;
+    return 0 if $name =~ /^glGetHandle/;
+    return 0 if $name =~ /^glGetImageTransformParameter/;
+    return 0 if $name =~ /^glGetInternalformat/;
+    return 0 if $name =~ /^glGetInvariant/;
+    return 0 if $name =~ /^glGetLight/;
+    return 0 if $name =~ /^glGetListParameter/;
+    return 0 if $name =~ /^glGetLocalConstant/;
+    return 0 if $name =~ /^glGetMaterial/;
+    return 0 if $name =~ /^glGetMemoryObjectDetachedResourcesu/;
+    return 0 if $name =~ /^glGetMemoryObjectParameter/;
+    return 0 if $name =~ /^glGetMultisample/;
+    return 0 if $name =~ /^glGetn?(Compressed)?(Multi)?Tex(ture)?Image/;
+    return 0 if $name =~ /^glGetn?Color/;
+    return 0 if $name =~ /^glGetn?Convolution/;
+    return 0 if $name =~ /^glGetn?Histogram/;
+    return 0 if $name =~ /^glGetn?Map/;
+    return 0 if $name =~ /^glGetn?Minmax/;
+    return 0 if $name =~ /^glGetn?Separable/;
+    return 0 if $name =~ /^glGetNamedProgram/;
+    return 0 if $name =~ /^glGetNamedString/;
+    return 0 if $name =~ /^glGetObjectBuffer/;
+    return 0 if $name =~ /^glGetObjectParameter/;
+    return 0 if $name =~ /^glGetOcclusionQuery/;
+    return 0 if $name =~ /^glGetPath(Parameter|Color)/;
+    return 0 if $name =~ /^glGetPerfMonitorCounter/;
+    return 0 if $name =~ /^glGetPixelTexGenParameter/;
+    return 0 if $name =~ /^glGetPixelTransformParameter/;
+    return 0 if $name =~ /^glGetProgram/;
+    return 0 if $name =~ /^glGetQuery/;
+    return 0 if $name =~ /^glGetSamplerParameter/;
+    return 0 if $name =~ /^glGetSemaphoreParameter/;
+    return 0 if $name =~ /^glGetShader/;
+    return 0 if $name =~ /^glGetSync/;
+    return 0 if $name =~ /^glGetTexBumpParameter/;
+    return 0 if $name =~ /^glGetTrackMatrixiv/;
+    return 0 if $name =~ /^glGetVariant/;
+    return 0 if $name =~ /^glGetVideo/;
+
+    print "Missing possible glGet integer: $name\n";
+    return 0;
 }
 
 sub parse_registry($)
diff --git a/dlls/opengl32/private.h b/dlls/opengl32/private.h
index aa14924d1a8f..b297325c49ee 100644
--- a/dlls/opengl32/private.h
+++ b/dlls/opengl32/private.h
@@ -44,6 +44,37 @@ extern BOOL get_context_from_handle( HGLRC handle, HGLRC *obj );
 extern BOOL get_sync_from_handle( GLsync handle, GLsync *obj );
 extern void set_gl_error( GLenum error );
 extern struct registry_entry *get_function_entry( const char *name );
-extern BOOL get_integer( GLenum name, GLint *data );
+extern BOOL get_integer( GLenum pname, GLuint index, GLint value, GLint *data );
+
+enum object_type
+{
+    OBJ_TYPE_BUFFER,
+    OBJ_TYPE_DISPLAY_LIST,
+    OBJ_TYPE_FRAMEBUFFER,
+    OBJ_TYPE_MEMORY,
+    OBJ_TYPE_PATH,
+    OBJ_TYPE_PROGRAM,
+    OBJ_TYPE_RENDERBUFFER,
+    OBJ_TYPE_SAMPLER,
+    OBJ_TYPE_SEMAPHORE,
+    OBJ_TYPE_SHADER_ATI,
+    OBJ_TYPE_SHADER_EXT,
+    OBJ_TYPE_TEXTURE,
+    OBJ_TYPE_COUNT,
+};
+
+static inline GLuint *memdup_objects( UINT n, const GLuint *handles, GLuint *buf, UINT max )
+{
+    GLuint *tmp = buf;
+    if (n > max && !(tmp = malloc( n * sizeof(*handles) ))) return NULL;
+    memcpy( tmp, handles, n * sizeof(*handles) );
+    return tmp;
+}
+
+extern void put_context_objects( enum object_type type, UINT n, GLuint *handles );
+extern GLuint put_context_object_range( enum object_type type, UINT n, GLuint base );
+extern BOOL alloc_context_objects( enum object_type type, UINT n, const GLuint *handles, BOOL extension );
+extern GLuint *del_context_objects( enum object_type type, UINT n, GLuint *handles );
+extern GLuint *map_context_objects( enum object_type type, UINT n, GLuint *handles );
 
 #endif /* __WINE_OPENGL32_PRIVATE_H */
diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c
index ed28698c2817..ca2c877c5338 100644
--- a/dlls/opengl32/tests/opengl.c
+++ b/dlls/opengl32/tests/opengl.c
@@ -1483,9 +1483,7 @@ static void test_object_creation( HDC winhdc )
         }
         if (!is_implicit_allowed( i, TRUE ))
         {
-            todo_wine_if( i == OBJ_FRAMEBUFFER || i == OBJ_RENDERBUFFER )
             ok_ret( GL_INVALID_OPERATION, glGetError() );
-            if (!winetest_platform_is_wine || (i != OBJ_FRAMEBUFFER && i != OBJ_RENDERBUFFER))
             ok_ret( TRUE, create_object( i, 0, &obj ) );
         }
         ok_ret( GL_NO_ERROR, glGetError() );
@@ -1513,11 +1511,10 @@ static void test_object_creation( HDC winhdc )
         if (i == OBJ_DISPLAY_LIST) ok_ret( GL_INVALID_OPERATION, glGetError() );
         else
         {
+            /* host often doesn't support legacy program / shaders in core contexts */
+            todo_wine_if( i == OBJ_PROGRAM_ARB || i == OBJ_PROGRAM_NV || i == OBJ_SHADER_EXT || i == OBJ_SHADER_ATI )
             /* Wine never allows implicit allocation in core contexts */
-            todo_wine_if( i == OBJ_FENCE_APPLE || i == OBJ_FENCE_NV || i == OBJ_FRAMEBUFFER_EXT || i == OBJ_PATH_NV ||
-                          i == OBJ_PROGRAM_ARB || i == OBJ_PROGRAM_NV || i == OBJ_SHADER_EXT || i == OBJ_SHADER_ATI ||
-                          i == OBJ_RENDERBUFFER_EXT || i == OBJ_SEMAPHORE_EXT || i == OBJ_TRANSFORM_FEEDBACK_NV ||
-                          i == OBJ_VERTEX_ARRAY_APPLE )
+            todo_wine_if( i == OBJ_FENCE_APPLE || i == OBJ_FENCE_NV || i == OBJ_TRANSFORM_FEEDBACK_NV || i == OBJ_VERTEX_ARRAY_APPLE )
             ok_ret( GL_NO_ERROR, glGetError() );
             ok_u4( obj, ==, 1 );
         }
@@ -2021,7 +2018,7 @@ static void test_sharelists(HDC winhdc)
         ok_ret( GL_NO_ERROR, glGetError() );
 
         /* cannot overwrite non-empty lists with some other */
-        todo_wine ok_ret( FALSE, wglShareLists( ctx1, ctx3 ) );
+        todo_wine_if( i >= 14 ) ok_ret( FALSE, wglShareLists( ctx1, ctx3 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
         ok_ret( FALSE, wglShareLists( ctx2, ctx1 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
@@ -2069,7 +2066,7 @@ static void test_sharelists(HDC winhdc)
         ok_ret( GL_NO_ERROR, glGetError() );
         ok_ret( TRUE, wglMakeCurrent( winhdc, ctx2 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
-        todo_wine ok_ret( FALSE, test->exists( obj1 ) );
+        todo_wine_if( i >= 14 ) ok_ret( FALSE, test->exists( obj1 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
         ok_ret( FALSE, test->exists( obj2 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
@@ -2080,7 +2077,7 @@ static void test_sharelists(HDC winhdc)
         ok_ret( TRUE, wglDeleteContext( ctx3 ) );
 
         /* objects are still valid after shared context destruction */
-        todo_wine ok_ret( FALSE, test->exists( obj1 ) );
+        todo_wine_if( i >= 14 ) ok_ret( FALSE, test->exists( obj1 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
         ok_ret( FALSE, test->exists( obj2 ) );
         ok_ret( GL_NO_ERROR, glGetError() );
diff --git a/dlls/opengl32/thunks.c b/dlls/opengl32/thunks.c
index c62a6fdaa9f7..20f05d022300 100644
--- a/dlls/opengl32/thunks.c
+++ b/dlls/opengl32/thunks.c
@@ -36,17 +36,6 @@ BOOL WINAPI wglSetPixelFormat( HDC hdc, int ipfd, const PIXELFORMATDESCRIPTOR *p
     return args.ret;
 }
 
-BOOL WINAPI wglShareLists( HGLRC hrcSrvShare, HGLRC hrcSrvSource )
-{
-    struct wglShareLists_params args = { .teb = NtCurrentTeb() };
-    NTSTATUS status;
-    TRACE( "hrcSrvShare %p, hrcSrvSource %p\n", hrcSrvShare, hrcSrvSource );
-    if (!get_context_from_handle( hrcSrvShare, &args.hrcSrvShare )) return 0;
-    if (!get_context_from_handle( hrcSrvSource, &args.hrcSrvSource )) return 0;
-    if ((status = UNIX_CALL( wglShareLists, &args ))) WARN( "wglShareLists returned %#lx\n", status );
-    return args.ret;
-}
-
 void WINAPI glAccum( GLenum op, GLfloat value )
 {
     struct glAccum_params args = { .teb = NtCurrentTeb(), .op = op, .value = value };
@@ -65,10 +54,14 @@ void WINAPI glAlphaFunc( GLenum func, GLfloat ref )
 
 GLboolean WINAPI glAreTexturesResident( GLsizei n, const GLuint *textures, GLboolean *residences )
 {
-    struct glAreTexturesResident_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures, .residences = residences };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glAreTexturesResident_params args = { .teb = NtCurrentTeb(), .n = n, .residences = residences };
     NTSTATUS status;
     TRACE( "n %d, textures %p, residences %p\n", n, textures, residences );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glAreTexturesResident, &args ))) WARN( "glAreTexturesResident returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
     return args.ret;
 }
 
@@ -90,9 +83,11 @@ void WINAPI glBegin( GLenum mode )
 
 void WINAPI glBindTexture( GLenum target, GLuint texture )
 {
-    struct glBindTexture_params args = { .teb = NtCurrentTeb(), .target = target, .texture = texture };
+    struct glBindTexture_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, texture %d\n", target, texture );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, FALSE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindTexture, &args ))) WARN( "glBindTexture returned %#lx\n", status );
 }
 
@@ -114,9 +109,10 @@ void WINAPI glBlendFunc( GLenum sfactor, GLenum dfactor )
 
 void WINAPI glCallList( GLuint list )
 {
-    struct glCallList_params args = { .teb = NtCurrentTeb(), .list = list };
+    struct glCallList_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "list %d\n", list );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glCallList, &args ))) WARN( "glCallList returned %#lx\n", status );
 }
 
@@ -514,18 +510,23 @@ void WINAPI glCullFace( GLenum mode )
 
 void WINAPI glDeleteLists( GLuint list, GLsizei range )
 {
-    struct glDeleteLists_params args = { .teb = NtCurrentTeb(), .list = list, .range = range };
+    struct glDeleteLists_params args = { .teb = NtCurrentTeb(), .range = range };
     NTSTATUS status;
     TRACE( "list %d, range %d\n", list, range );
+    args.list = *del_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glDeleteLists, &args ))) WARN( "glDeleteLists returned %#lx\n", status );
 }
 
 void WINAPI glDeleteTextures( GLsizei n, const GLuint *textures )
 {
-    struct glDeleteTextures_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glDeleteTextures_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, textures %p\n", n, textures );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? del_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteTextures, &args ))) WARN( "glDeleteTextures returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 void WINAPI glDepthFunc( GLenum func )
@@ -830,6 +831,7 @@ GLuint WINAPI glGenLists( GLsizei range )
     NTSTATUS status;
     TRACE( "range %d\n", range );
     if ((status = UNIX_CALL( glGenLists, &args ))) WARN( "glGenLists returned %#lx\n", status );
+    args.ret = put_context_object_range( OBJ_TYPE_DISPLAY_LIST, range, args.ret );
     return args.ret;
 }
 
@@ -839,6 +841,7 @@ void WINAPI glGenTextures( GLsizei n, GLuint *textures )
     NTSTATUS status;
     TRACE( "n %d, textures %p\n", n, textures );
     if ((status = UNIX_CALL( glGenTextures, &args ))) WARN( "glGenTextures returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_TEXTURE, n, textures );
 }
 
 void WINAPI glGetBooleanv( GLenum pname, GLboolean *data )
@@ -847,8 +850,8 @@ void WINAPI glGetBooleanv( GLenum pname, GLboolean *data )
     NTSTATUS status;
     int integer;
     TRACE( "pname %d, data %p\n", pname, data );
-    if (get_integer( pname, &integer )) *data = integer;
-    else if ((status = UNIX_CALL( glGetBooleanv, &args ))) WARN( "glGetBooleanv returned %#lx\n", status );
+    if ((status = UNIX_CALL( glGetBooleanv, &args ))) WARN( "glGetBooleanv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *data, &integer )) *data = integer;
 }
 
 void WINAPI glGetClipPlane( GLenum plane, GLdouble *equation )
@@ -865,8 +868,8 @@ void WINAPI glGetDoublev( GLenum pname, GLdouble *data )
     NTSTATUS status;
     int integer;
     TRACE( "pname %d, data %p\n", pname, data );
-    if (get_integer( pname, &integer )) *data = integer;
-    else if ((status = UNIX_CALL( glGetDoublev, &args ))) WARN( "glGetDoublev returned %#lx\n", status );
+    if ((status = UNIX_CALL( glGetDoublev, &args ))) WARN( "glGetDoublev returned %#lx\n", status );
+    else if (get_integer( pname, 0, *data, &integer )) *data = integer;
 }
 
 GLenum WINAPI glGetError(void)
@@ -884,8 +887,8 @@ void WINAPI glGetFloatv( GLenum pname, GLfloat *data )
     NTSTATUS status;
     int integer;
     TRACE( "pname %d, data %p\n", pname, data );
-    if (get_integer( pname, &integer )) *data = integer;
-    else if ((status = UNIX_CALL( glGetFloatv, &args ))) WARN( "glGetFloatv returned %#lx\n", status );
+    if ((status = UNIX_CALL( glGetFloatv, &args ))) WARN( "glGetFloatv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *data, &integer )) *data = integer;
 }
 
 void WINAPI glGetIntegerv( GLenum pname, GLint *data )
@@ -894,8 +897,8 @@ void WINAPI glGetIntegerv( GLenum pname, GLint *data )
     NTSTATUS status;
     int integer;
     TRACE( "pname %d, data %p\n", pname, data );
-    if (get_integer( pname, &integer )) *data = integer;
-    else if ((status = UNIX_CALL( glGetIntegerv, &args ))) WARN( "glGetIntegerv returned %#lx\n", status );
+    if ((status = UNIX_CALL( glGetIntegerv, &args ))) WARN( "glGetIntegerv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *data, &integer )) *data = integer;
 }
 
 void WINAPI glGetLightfv( GLenum light, GLenum pname, GLfloat *params )
@@ -982,8 +985,10 @@ void WINAPI glGetPointerv( GLenum pname, void **params )
 {
     struct glGetPointerv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, params %p\n", pname, params );
     if ((status = UNIX_CALL( glGetPointerv, &args ))) WARN( "glGetPointerv returned %#lx\n", status );
+    else if (get_integer( pname, 0, (UINT_PTR)*params, &integer )) *params = (void *)(UINT_PTR)integer;
 }
 
 void WINAPI glGetPolygonStipple( GLubyte *mask )
@@ -1046,16 +1051,20 @@ void WINAPI glGetTexLevelParameterfv( GLenum target, GLint level, GLenum pname,
 {
     struct glGetTexLevelParameterfv_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, level %d, pname %d, params %p\n", target, level, pname, params );
     if ((status = UNIX_CALL( glGetTexLevelParameterfv, &args ))) WARN( "glGetTexLevelParameterfv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 void WINAPI glGetTexLevelParameteriv( GLenum target, GLint level, GLenum pname, GLint *params )
 {
     struct glGetTexLevelParameteriv_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, level %d, pname %d, params %p\n", target, level, pname, params );
     if ((status = UNIX_CALL( glGetTexLevelParameteriv, &args ))) WARN( "glGetTexLevelParameteriv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 void WINAPI glGetTexParameterfv( GLenum target, GLenum pname, GLfloat *params )
@@ -1205,18 +1214,20 @@ GLboolean WINAPI glIsEnabled( GLenum cap )
 
 GLboolean WINAPI glIsList( GLuint list )
 {
-    struct glIsList_params args = { .teb = NtCurrentTeb(), .list = list };
+    struct glIsList_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "list %d\n", list );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glIsList, &args ))) WARN( "glIsList returned %#lx\n", status );
     return args.ret;
 }
 
 GLboolean WINAPI glIsTexture( GLuint texture )
 {
-    struct glIsTexture_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glIsTexture_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glIsTexture, &args ))) WARN( "glIsTexture returned %#lx\n", status );
     return args.ret;
 }
@@ -1303,9 +1314,10 @@ void WINAPI glLineWidth( GLfloat width )
 
 void WINAPI glListBase( GLuint base )
 {
-    struct glListBase_params args = { .teb = NtCurrentTeb(), .base = base };
+    struct glListBase_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "base %d\n", base );
+    args.base = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &base );
     if ((status = UNIX_CALL( glListBase, &args ))) WARN( "glListBase returned %#lx\n", status );
 }
 
@@ -1471,9 +1483,11 @@ void WINAPI glMultMatrixf( const GLfloat *m )
 
 void WINAPI glNewList( GLuint list, GLenum mode )
 {
-    struct glNewList_params args = { .teb = NtCurrentTeb(), .list = list, .mode = mode };
+    struct glNewList_params args = { .teb = NtCurrentTeb(), .mode = mode };
     NTSTATUS status;
     TRACE( "list %d, mode %d\n", list, mode );
+    if (!alloc_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list, FALSE )) return;
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glNewList, &args ))) WARN( "glNewList returned %#lx\n", status );
 }
 
@@ -1711,10 +1725,14 @@ void WINAPI glPopName(void)
 
 void WINAPI glPrioritizeTextures( GLsizei n, const GLuint *textures, const GLfloat *priorities )
 {
-    struct glPrioritizeTextures_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures, .priorities = priorities };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glPrioritizeTextures_params args = { .teb = NtCurrentTeb(), .n = n, .priorities = priorities };
     NTSTATUS status;
     TRACE( "n %d, textures %p, priorities %p\n", n, textures, priorities );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glPrioritizeTextures, &args ))) WARN( "glPrioritizeTextures returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 void WINAPI glPushAttrib( GLbitfield mask )
@@ -2752,9 +2770,10 @@ static void WINAPI glAccumxOES( GLenum op, GLfixed value )
 
 static GLboolean WINAPI glAcquireKeyedMutexWin32EXT( GLuint memory, GLuint64 key, GLuint timeout )
 {
-    struct glAcquireKeyedMutexWin32EXT_params args = { .teb = NtCurrentTeb(), .memory = memory, .key = key, .timeout = timeout };
+    struct glAcquireKeyedMutexWin32EXT_params args = { .teb = NtCurrentTeb(), .key = key, .timeout = timeout };
     NTSTATUS status;
     TRACE( "memory %d, key %s, timeout %d\n", memory, wine_dbgstr_longlong(key), timeout );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glAcquireKeyedMutexWin32EXT, &args ))) WARN( "glAcquireKeyedMutexWin32EXT returned %#lx\n", status );
     return args.ret;
 }
@@ -2873,19 +2892,27 @@ static void WINAPI glApplyTextureEXT( GLenum mode )
 
 static GLboolean WINAPI glAreProgramsResidentNV( GLsizei n, const GLuint *programs, GLboolean *residences )
 {
-    struct glAreProgramsResidentNV_params args = { .teb = NtCurrentTeb(), .n = n, .programs = programs, .residences = residences };
+    GLuint programs_buf[64], *programs_tmp;
+    struct glAreProgramsResidentNV_params args = { .teb = NtCurrentTeb(), .n = n, .residences = residences };
     NTSTATUS status;
     TRACE( "n %d, programs %p, residences %p\n", n, programs, residences );
+    programs_tmp = n > 0 ? memdup_objects( n, programs, programs_buf, ARRAY_SIZE(programs_buf) ) : NULL;
+    args.programs = n > 0 ? map_context_objects( OBJ_TYPE_PROGRAM, n, programs_tmp ) : NULL;
     if ((status = UNIX_CALL( glAreProgramsResidentNV, &args ))) WARN( "glAreProgramsResidentNV returned %#lx\n", status );
+    if (programs_tmp != programs_buf) free( programs_tmp );
     return args.ret;
 }
 
 static GLboolean WINAPI glAreTexturesResidentEXT( GLsizei n, const GLuint *textures, GLboolean *residences )
 {
-    struct glAreTexturesResidentEXT_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures, .residences = residences };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glAreTexturesResidentEXT_params args = { .teb = NtCurrentTeb(), .n = n, .residences = residences };
     NTSTATUS status;
     TRACE( "n %d, textures %p, residences %p\n", n, textures, residences );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glAreTexturesResidentEXT, &args ))) WARN( "glAreTexturesResidentEXT returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
     return args.ret;
 }
 
@@ -2899,27 +2926,48 @@ static void WINAPI glArrayElementEXT( GLint i )
 
 static void WINAPI glArrayObjectATI( GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset )
 {
-    struct glArrayObjectATI_params args = { .teb = NtCurrentTeb(), .array = array, .size = size, .type = type, .stride = stride, .buffer = buffer, .offset = offset };
+    struct glArrayObjectATI_params args = { .teb = NtCurrentTeb(), .array = array, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "array %d, size %d, type %d, stride %d, buffer %d, offset %d\n", array, size, type, stride, buffer, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glArrayObjectATI, &args ))) WARN( "glArrayObjectATI returned %#lx\n", status );
 }
 
 static GLuint WINAPI glAsyncCopyBufferSubDataNVX( GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray )
 {
-    struct glAsyncCopyBufferSubDataNVX_params args = { .teb = NtCurrentTeb(), .waitSemaphoreCount = waitSemaphoreCount, .waitSemaphoreArray = waitSemaphoreArray, .fenceValueArray = fenceValueArray, .readGpu = readGpu, .writeGpuMask = writeGpuMask, .readBuffer = readBuffer, .writeBuffer = writeBuffer, .readOffset = readOffset, .writeOffset = writeOffset, .size = size, .signalSemaphoreCount = signalSemaphoreCount, .signalSemaphoreArray = signalSemaphoreArray, .signalValueArray = signalValueArray };
+    GLuint waitSemaphoreArray_buf[64], *waitSemaphoreArray_tmp;
+    GLuint signalSemaphoreArray_buf[64], *signalSemaphoreArray_tmp;
+    struct glAsyncCopyBufferSubDataNVX_params args = { .teb = NtCurrentTeb(), .waitSemaphoreCount = waitSemaphoreCount, .fenceValueArray = fenceValueArray, .readGpu = readGpu, .writeGpuMask = writeGpuMask, .readOffset = readOffset, .writeOffset = writeOffset, .size = size, .signalSemaphoreCount = signalSemaphoreCount, .signalValueArray = signalValueArray };
     NTSTATUS status;
     TRACE( "waitSemaphoreCount %d, waitSemaphoreArray %p, fenceValueArray %p, readGpu %d, writeGpuMask %d, readBuffer %d, writeBuffer %d, readOffset %Id, writeOffset %Id, size %Id, signalSemaphoreCount %d, signalSemaphoreArray %p, signalValueArray %p\n", waitSemaphoreCount, waitSemaphoreArray, fenceValueArray, readGpu, writeGpuMask, readBuffer, writeBuffer, readOffset, writeOffset, size, signalSemaphoreCount, signalSemaphoreArray, signalValueArray );
+    waitSemaphoreArray_tmp = waitSemaphoreCount > 0 ? memdup_objects( waitSemaphoreCount, waitSemaphoreArray, waitSemaphoreArray_buf, ARRAY_SIZE(waitSemaphoreArray_buf) ) : NULL;
+    args.waitSemaphoreArray = waitSemaphoreCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, waitSemaphoreCount, waitSemaphoreArray_tmp ) : NULL;
+    args.readBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &readBuffer );
+    args.writeBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &writeBuffer );
+    signalSemaphoreArray_tmp = signalSemaphoreCount > 0 ? memdup_objects( signalSemaphoreCount, signalSemaphoreArray, signalSemaphoreArray_buf, ARRAY_SIZE(signalSemaphoreArray_buf) ) : NULL;
+    args.signalSemaphoreArray = signalSemaphoreCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, signalSemaphoreCount, signalSemaphoreArray_tmp ) : NULL;
     if ((status = UNIX_CALL( glAsyncCopyBufferSubDataNVX, &args ))) WARN( "glAsyncCopyBufferSubDataNVX returned %#lx\n", status );
+    if (waitSemaphoreArray_tmp != waitSemaphoreArray_buf) free( waitSemaphoreArray_tmp );
+    if (signalSemaphoreArray_tmp != signalSemaphoreArray_buf) free( signalSemaphoreArray_tmp );
     return args.ret;
 }
 
 static GLuint WINAPI glAsyncCopyImageSubDataNVX( GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray )
 {
-    struct glAsyncCopyImageSubDataNVX_params args = { .teb = NtCurrentTeb(), .waitSemaphoreCount = waitSemaphoreCount, .waitSemaphoreArray = waitSemaphoreArray, .waitValueArray = waitValueArray, .srcGpu = srcGpu, .dstGpuMask = dstGpuMask, .srcName = srcName, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstName = dstName, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .srcWidth = srcWidth, .srcHeight = srcHeight, .srcDepth = srcDepth, .signalSemaphoreCount = signalSemaphoreCount, .signalSemaphoreArray = signalSemaphoreArray, .signalValueArray = signalValueArray };
+    GLuint waitSemaphoreArray_buf[64], *waitSemaphoreArray_tmp;
+    GLuint signalSemaphoreArray_buf[64], *signalSemaphoreArray_tmp;
+    struct glAsyncCopyImageSubDataNVX_params args = { .teb = NtCurrentTeb(), .waitSemaphoreCount = waitSemaphoreCount, .waitValueArray = waitValueArray, .srcGpu = srcGpu, .dstGpuMask = dstGpuMask, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .srcWidth = srcWidth, .srcHeight = srcHeight, .srcDepth = srcDepth, .signalSemaphoreCount = signalSemaphoreCount, .signalValueArray = signalValueArray };
     NTSTATUS status;
     TRACE( "waitSemaphoreCount %d, waitSemaphoreArray %p, waitValueArray %p, srcGpu %d, dstGpuMask %d, srcName %d, srcTarget %d, srcLevel %d, srcX %d, srcY %d, srcZ %d, dstName %d, dstTarget %d, dstLevel %d, dstX %d, dstY %d, dstZ %d, srcWidth %d, srcHeight %d, srcDepth %d, signalSemaphoreCount %d, signalSemaphoreArray %p, signalValueArray %p\n", waitSemaphoreCount, waitSemaphoreArray, waitValueArray, srcGpu, dstGpuMask, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth, signalSemaphoreCount, signalSemaphoreArray, signalValueArray );
+    waitSemaphoreArray_tmp = waitSemaphoreCount > 0 ? memdup_objects( waitSemaphoreCount, waitSemaphoreArray, waitSemaphoreArray_buf, ARRAY_SIZE(waitSemaphoreArray_buf) ) : NULL;
+    args.waitSemaphoreArray = waitSemaphoreCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, waitSemaphoreCount, waitSemaphoreArray_tmp ) : NULL;
+    args.srcName = *map_context_objects( srcTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &srcName );
+    args.dstName = *map_context_objects( dstTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &dstName );
+    signalSemaphoreArray_tmp = signalSemaphoreCount > 0 ? memdup_objects( signalSemaphoreCount, signalSemaphoreArray, signalSemaphoreArray_buf, ARRAY_SIZE(signalSemaphoreArray_buf) ) : NULL;
+    args.signalSemaphoreArray = signalSemaphoreCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, signalSemaphoreCount, signalSemaphoreArray_tmp ) : NULL;
     if ((status = UNIX_CALL( glAsyncCopyImageSubDataNVX, &args ))) WARN( "glAsyncCopyImageSubDataNVX returned %#lx\n", status );
+    if (waitSemaphoreArray_tmp != waitSemaphoreArray_buf) free( waitSemaphoreArray_tmp );
+    if (signalSemaphoreArray_tmp != signalSemaphoreArray_buf) free( signalSemaphoreArray_tmp );
     return args.ret;
 }
 
@@ -3085,98 +3133,128 @@ static void WINAPI glBindAttribLocationARB( GLhandleARB programObj, GLuint index
 
 static void WINAPI glBindBuffer( GLenum target, GLuint buffer )
 {
-    struct glBindBuffer_params args = { .teb = NtCurrentTeb(), .target = target, .buffer = buffer };
+    struct glBindBuffer_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, buffer %d\n", target, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, FALSE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBuffer, &args ))) WARN( "glBindBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferARB( GLenum target, GLuint buffer )
 {
-    struct glBindBufferARB_params args = { .teb = NtCurrentTeb(), .target = target, .buffer = buffer };
+    struct glBindBufferARB_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, buffer %d\n", target, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferARB, &args ))) WARN( "glBindBufferARB returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferBase( GLenum target, GLuint index, GLuint buffer )
 {
-    struct glBindBufferBase_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer };
+    struct glBindBufferBase_params args = { .teb = NtCurrentTeb(), .target = target, .index = index };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d\n", target, index, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, FALSE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferBase, &args ))) WARN( "glBindBufferBase returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferBaseEXT( GLenum target, GLuint index, GLuint buffer )
 {
-    struct glBindBufferBaseEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer };
+    struct glBindBufferBaseEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d\n", target, index, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferBaseEXT, &args ))) WARN( "glBindBufferBaseEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferBaseNV( GLenum target, GLuint index, GLuint buffer )
 {
-    struct glBindBufferBaseNV_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer };
+    struct glBindBufferBaseNV_params args = { .teb = NtCurrentTeb(), .target = target, .index = index };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d\n", target, index, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferBaseNV, &args ))) WARN( "glBindBufferBaseNV returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferOffsetEXT( GLenum target, GLuint index, GLuint buffer, GLintptr offset )
 {
-    struct glBindBufferOffsetEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer, .offset = offset };
+    struct glBindBufferOffsetEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d, offset %Id\n", target, index, buffer, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferOffsetEXT, &args ))) WARN( "glBindBufferOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferOffsetNV( GLenum target, GLuint index, GLuint buffer, GLintptr offset )
 {
-    struct glBindBufferOffsetNV_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer, .offset = offset };
+    struct glBindBufferOffsetNV_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d, offset %Id\n", target, index, buffer, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferOffsetNV, &args ))) WARN( "glBindBufferOffsetNV returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferRange( GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glBindBufferRange_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer, .offset = offset, .size = size };
+    struct glBindBufferRange_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d, offset %Id, size %Id\n", target, index, buffer, offset, size );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, FALSE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferRange, &args ))) WARN( "glBindBufferRange returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferRangeEXT( GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glBindBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer, .offset = offset, .size = size };
+    struct glBindBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d, offset %Id, size %Id\n", target, index, buffer, offset, size );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferRangeEXT, &args ))) WARN( "glBindBufferRangeEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindBufferRangeNV( GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glBindBufferRangeNV_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .buffer = buffer, .offset = offset, .size = size };
+    struct glBindBufferRangeNV_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "target %d, index %d, buffer %d, offset %Id, size %Id\n", target, index, buffer, offset, size );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindBufferRangeNV, &args ))) WARN( "glBindBufferRangeNV returned %#lx\n", status );
 }
 
 static void WINAPI glBindBuffersBase( GLenum target, GLuint first, GLsizei count, const GLuint *buffers )
 {
-    struct glBindBuffersBase_params args = { .teb = NtCurrentTeb(), .target = target, .first = first, .count = count, .buffers = buffers };
+    GLuint buffers_buf[64], *buffers_tmp;
+    struct glBindBuffersBase_params args = { .teb = NtCurrentTeb(), .target = target, .first = first, .count = count };
     NTSTATUS status;
     TRACE( "target %d, first %d, count %d, buffers %p\n", target, first, count, buffers );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, count, buffers, FALSE )) return;
+    buffers_tmp = count > 0 ? memdup_objects( count, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = count > 0 ? map_context_objects( OBJ_TYPE_BUFFER, count, buffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glBindBuffersBase, &args ))) WARN( "glBindBuffersBase returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
 }
 
 static void WINAPI glBindBuffersRange( GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes )
 {
-    struct glBindBuffersRange_params args = { .teb = NtCurrentTeb(), .target = target, .first = first, .count = count, .buffers = buffers, .offsets = offsets, .sizes = sizes };
+    GLuint buffers_buf[64], *buffers_tmp;
+    struct glBindBuffersRange_params args = { .teb = NtCurrentTeb(), .target = target, .first = first, .count = count, .offsets = offsets, .sizes = sizes };
     NTSTATUS status;
     TRACE( "target %d, first %d, count %d, buffers %p, offsets %p, sizes %p\n", target, first, count, buffers, offsets, sizes );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, count, buffers, FALSE )) return;
+    buffers_tmp = count > 0 ? memdup_objects( count, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = count > 0 ? map_context_objects( OBJ_TYPE_BUFFER, count, buffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glBindBuffersRange, &args ))) WARN( "glBindBuffersRange returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
 }
 
 static void WINAPI glBindFragDataLocation( GLuint program, GLuint color, const GLchar *name )
@@ -3205,50 +3283,62 @@ static void WINAPI glBindFragDataLocationIndexed( GLuint program, GLuint colorNu
 
 static void WINAPI glBindFragmentShaderATI( GLuint id )
 {
-    struct glBindFragmentShaderATI_params args = { .teb = NtCurrentTeb(), .id = id };
+    struct glBindFragmentShaderATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "id %d\n", id );
+    if (!alloc_context_objects( OBJ_TYPE_SHADER_ATI, 1, &id, TRUE )) return;
+    args.id = *map_context_objects( OBJ_TYPE_SHADER_ATI, 1, &id );
     if ((status = UNIX_CALL( glBindFragmentShaderATI, &args ))) WARN( "glBindFragmentShaderATI returned %#lx\n", status );
 }
 
 static void WINAPI glBindFramebuffer( GLenum target, GLuint framebuffer )
 {
-    struct glBindFramebuffer_params args = { .teb = NtCurrentTeb(), .target = target, .framebuffer = framebuffer };
+    struct glBindFramebuffer_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, framebuffer %d\n", target, framebuffer );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, FALSE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glBindFramebuffer, &args ))) WARN( "glBindFramebuffer returned %#lx\n", status );
 }
 
 static void WINAPI glBindFramebufferEXT( GLenum target, GLuint framebuffer )
 {
-    struct glBindFramebufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .framebuffer = framebuffer };
+    struct glBindFramebufferEXT_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, framebuffer %d\n", target, framebuffer );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glBindFramebufferEXT, &args ))) WARN( "glBindFramebufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindImageTexture( GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format )
 {
-    struct glBindImageTexture_params args = { .teb = NtCurrentTeb(), .unit = unit, .texture = texture, .level = level, .layered = layered, .layer = layer, .access = access, .format = format };
+    struct glBindImageTexture_params args = { .teb = NtCurrentTeb(), .unit = unit, .level = level, .layered = layered, .layer = layer, .access = access, .format = format };
     NTSTATUS status;
     TRACE( "unit %d, texture %d, level %d, layered %d, layer %d, access %d, format %d\n", unit, texture, level, layered, layer, access, format );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindImageTexture, &args ))) WARN( "glBindImageTexture returned %#lx\n", status );
 }
 
 static void WINAPI glBindImageTextureEXT( GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format )
 {
-    struct glBindImageTextureEXT_params args = { .teb = NtCurrentTeb(), .index = index, .texture = texture, .level = level, .layered = layered, .layer = layer, .access = access, .format = format };
+    struct glBindImageTextureEXT_params args = { .teb = NtCurrentTeb(), .index = index, .level = level, .layered = layered, .layer = layer, .access = access, .format = format };
     NTSTATUS status;
     TRACE( "index %d, texture %d, level %d, layered %d, layer %d, access %d, format %d\n", index, texture, level, layered, layer, access, format );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindImageTextureEXT, &args ))) WARN( "glBindImageTextureEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindImageTextures( GLuint first, GLsizei count, const GLuint *textures )
 {
-    struct glBindImageTextures_params args = { .teb = NtCurrentTeb(), .first = first, .count = count, .textures = textures };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glBindImageTextures_params args = { .teb = NtCurrentTeb(), .first = first, .count = count };
     NTSTATUS status;
     TRACE( "first %d, count %d, textures %p\n", first, count, textures );
+    textures_tmp = count > 0 ? memdup_objects( count, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = count > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, count, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glBindImageTextures, &args ))) WARN( "glBindImageTextures returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static GLuint WINAPI glBindLightParameterEXT( GLenum light, GLenum value )
@@ -3271,9 +3361,11 @@ static GLuint WINAPI glBindMaterialParameterEXT( GLenum face, GLenum value )
 
 static void WINAPI glBindMultiTextureEXT( GLenum texunit, GLenum target, GLuint texture )
 {
-    struct glBindMultiTextureEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target, .texture = texture };
+    struct glBindMultiTextureEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target };
     NTSTATUS status;
     TRACE( "texunit %d, target %d, texture %d\n", texunit, target, texture );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindMultiTextureEXT, &args ))) WARN( "glBindMultiTextureEXT returned %#lx\n", status );
 }
 
@@ -3288,17 +3380,21 @@ static GLuint WINAPI glBindParameterEXT( GLenum value )
 
 static void WINAPI glBindProgramARB( GLenum target, GLuint program )
 {
-    struct glBindProgramARB_params args = { .teb = NtCurrentTeb(), .target = target, .program = program };
+    struct glBindProgramARB_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, program %d\n", target, program );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glBindProgramARB, &args ))) WARN( "glBindProgramARB returned %#lx\n", status );
 }
 
 static void WINAPI glBindProgramNV( GLenum target, GLuint id )
 {
-    struct glBindProgramNV_params args = { .teb = NtCurrentTeb(), .target = target, .id = id };
+    struct glBindProgramNV_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, id %d\n", target, id );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &id, TRUE )) return;
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glBindProgramNV, &args ))) WARN( "glBindProgramNV returned %#lx\n", status );
 }
 
@@ -3312,41 +3408,53 @@ static void WINAPI glBindProgramPipeline( GLuint pipeline )
 
 static void WINAPI glBindRenderbuffer( GLenum target, GLuint renderbuffer )
 {
-    struct glBindRenderbuffer_params args = { .teb = NtCurrentTeb(), .target = target, .renderbuffer = renderbuffer };
+    struct glBindRenderbuffer_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, renderbuffer %d\n", target, renderbuffer );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, FALSE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glBindRenderbuffer, &args ))) WARN( "glBindRenderbuffer returned %#lx\n", status );
 }
 
 static void WINAPI glBindRenderbufferEXT( GLenum target, GLuint renderbuffer )
 {
-    struct glBindRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .renderbuffer = renderbuffer };
+    struct glBindRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, renderbuffer %d\n", target, renderbuffer );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glBindRenderbufferEXT, &args ))) WARN( "glBindRenderbufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindSampler( GLuint unit, GLuint sampler )
 {
-    struct glBindSampler_params args = { .teb = NtCurrentTeb(), .unit = unit, .sampler = sampler };
+    struct glBindSampler_params args = { .teb = NtCurrentTeb(), .unit = unit };
     NTSTATUS status;
     TRACE( "unit %d, sampler %d\n", unit, sampler );
+    if (!alloc_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler, FALSE )) return;
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glBindSampler, &args ))) WARN( "glBindSampler returned %#lx\n", status );
 }
 
 static void WINAPI glBindSamplers( GLuint first, GLsizei count, const GLuint *samplers )
 {
-    struct glBindSamplers_params args = { .teb = NtCurrentTeb(), .first = first, .count = count, .samplers = samplers };
+    GLuint samplers_buf[64], *samplers_tmp;
+    struct glBindSamplers_params args = { .teb = NtCurrentTeb(), .first = first, .count = count };
     NTSTATUS status;
     TRACE( "first %d, count %d, samplers %p\n", first, count, samplers );
+    if (!alloc_context_objects( OBJ_TYPE_SAMPLER, count, samplers, FALSE )) return;
+    samplers_tmp = count > 0 ? memdup_objects( count, samplers, samplers_buf, ARRAY_SIZE(samplers_buf) ) : NULL;
+    args.samplers = count > 0 ? map_context_objects( OBJ_TYPE_SAMPLER, count, samplers_tmp ) : NULL;
     if ((status = UNIX_CALL( glBindSamplers, &args ))) WARN( "glBindSamplers returned %#lx\n", status );
+    if (samplers_tmp != samplers_buf) free( samplers_tmp );
 }
 
 static void WINAPI glBindShadingRateImageNV( GLuint texture )
 {
-    struct glBindShadingRateImageNV_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glBindShadingRateImageNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindShadingRateImageNV, &args ))) WARN( "glBindShadingRateImageNV returned %#lx\n", status );
 }
 
@@ -3361,17 +3469,21 @@ static GLuint WINAPI glBindTexGenParameterEXT( GLenum unit, GLenum coord, GLenum
 
 static void WINAPI glBindTextureEXT( GLenum target, GLuint texture )
 {
-    struct glBindTextureEXT_params args = { .teb = NtCurrentTeb(), .target = target, .texture = texture };
+    struct glBindTextureEXT_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, texture %d\n", target, texture );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindTextureEXT, &args ))) WARN( "glBindTextureEXT returned %#lx\n", status );
 }
 
 static void WINAPI glBindTextureUnit( GLuint unit, GLuint texture )
 {
-    struct glBindTextureUnit_params args = { .teb = NtCurrentTeb(), .unit = unit, .texture = texture };
+    struct glBindTextureUnit_params args = { .teb = NtCurrentTeb(), .unit = unit };
     NTSTATUS status;
     TRACE( "unit %d, texture %d\n", unit, texture );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, FALSE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindTextureUnit, &args ))) WARN( "glBindTextureUnit returned %#lx\n", status );
 }
 
@@ -3386,10 +3498,15 @@ static GLuint WINAPI glBindTextureUnitParameterEXT( GLenum unit, GLenum value )
 
 static void WINAPI glBindTextures( GLuint first, GLsizei count, const GLuint *textures )
 {
-    struct glBindTextures_params args = { .teb = NtCurrentTeb(), .first = first, .count = count, .textures = textures };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glBindTextures_params args = { .teb = NtCurrentTeb(), .first = first, .count = count };
     NTSTATUS status;
     TRACE( "first %d, count %d, textures %p\n", first, count, textures );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, count, textures, FALSE )) return;
+    textures_tmp = count > 0 ? memdup_objects( count, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = count > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, count, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glBindTextures, &args ))) WARN( "glBindTextures returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static void WINAPI glBindTransformFeedback( GLenum target, GLuint id )
@@ -3426,25 +3543,32 @@ static void WINAPI glBindVertexArrayAPPLE( GLuint array )
 
 static void WINAPI glBindVertexBuffer( GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride )
 {
-    struct glBindVertexBuffer_params args = { .teb = NtCurrentTeb(), .bindingindex = bindingindex, .buffer = buffer, .offset = offset, .stride = stride };
+    struct glBindVertexBuffer_params args = { .teb = NtCurrentTeb(), .bindingindex = bindingindex, .offset = offset, .stride = stride };
     NTSTATUS status;
     TRACE( "bindingindex %d, buffer %d, offset %Id, stride %d\n", bindingindex, buffer, offset, stride );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glBindVertexBuffer, &args ))) WARN( "glBindVertexBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glBindVertexBuffers( GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides )
 {
-    struct glBindVertexBuffers_params args = { .teb = NtCurrentTeb(), .first = first, .count = count, .buffers = buffers, .offsets = offsets, .strides = strides };
+    GLuint buffers_buf[64], *buffers_tmp;
+    struct glBindVertexBuffers_params args = { .teb = NtCurrentTeb(), .first = first, .count = count, .offsets = offsets, .strides = strides };
     NTSTATUS status;
     TRACE( "first %d, count %d, buffers %p, offsets %p, strides %p\n", first, count, buffers, offsets, strides );
+    buffers_tmp = count > 0 ? memdup_objects( count, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = count > 0 ? map_context_objects( OBJ_TYPE_BUFFER, count, buffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glBindVertexBuffers, &args ))) WARN( "glBindVertexBuffers returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
 }
 
 static void WINAPI glBindVertexShaderEXT( GLuint id )
 {
-    struct glBindVertexShaderEXT_params args = { .teb = NtCurrentTeb(), .id = id };
+    struct glBindVertexShaderEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "id %d\n", id );
+    if (!alloc_context_objects( OBJ_TYPE_SHADER_EXT, 1, &id, TRUE )) return;
+    args.id = *map_context_objects( OBJ_TYPE_SHADER_EXT, 1, &id );
     if ((status = UNIX_CALL( glBindVertexShaderEXT, &args ))) WARN( "glBindVertexShaderEXT returned %#lx\n", status );
 }
 
@@ -3458,9 +3582,10 @@ static void WINAPI glBindVideoCaptureStreamBufferNV( GLuint video_capture_slot,
 
 static void WINAPI glBindVideoCaptureStreamTextureNV( GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture )
 {
-    struct glBindVideoCaptureStreamTextureNV_params args = { .teb = NtCurrentTeb(), .video_capture_slot = video_capture_slot, .stream = stream, .frame_region = frame_region, .target = target, .texture = texture };
+    struct glBindVideoCaptureStreamTextureNV_params args = { .teb = NtCurrentTeb(), .video_capture_slot = video_capture_slot, .stream = stream, .frame_region = frame_region, .target = target };
     NTSTATUS status;
     TRACE( "video_capture_slot %d, stream %d, frame_region %d, target %d, texture %d\n", video_capture_slot, stream, frame_region, target, texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glBindVideoCaptureStreamTextureNV, &args ))) WARN( "glBindVideoCaptureStreamTextureNV returned %#lx\n", status );
 }
 
@@ -3802,9 +3927,11 @@ static void WINAPI glBlitFramebufferLayersEXT( GLint srcX0, GLint srcY0, GLint s
 
 static void WINAPI glBlitNamedFramebuffer( GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter )
 {
-    struct glBlitNamedFramebuffer_params args = { .teb = NtCurrentTeb(), .readFramebuffer = readFramebuffer, .drawFramebuffer = drawFramebuffer, .srcX0 = srcX0, .srcY0 = srcY0, .srcX1 = srcX1, .srcY1 = srcY1, .dstX0 = dstX0, .dstY0 = dstY0, .dstX1 = dstX1, .dstY1 = dstY1, .mask = mask, .filter = filter };
+    struct glBlitNamedFramebuffer_params args = { .teb = NtCurrentTeb(), .srcX0 = srcX0, .srcY0 = srcY0, .srcX1 = srcX1, .srcY1 = srcY1, .dstX0 = dstX0, .dstY0 = dstY0, .dstX1 = dstX1, .dstY1 = dstY1, .mask = mask, .filter = filter };
     NTSTATUS status;
     TRACE( "readFramebuffer %d, drawFramebuffer %d, srcX0 %d, srcY0 %d, srcX1 %d, srcY1 %d, dstX0 %d, dstY0 %d, dstX1 %d, dstY1 %d, mask %d, filter %d\n", readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter );
+    args.readFramebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &readFramebuffer );
+    args.drawFramebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &drawFramebuffer );
     if ((status = UNIX_CALL( glBlitNamedFramebuffer, &args ))) WARN( "glBlitNamedFramebuffer returned %#lx\n", status );
 }
 
@@ -3818,9 +3945,10 @@ static void WINAPI glBufferAddressRangeNV( GLenum pname, GLuint index, GLuint64E
 
 static void WINAPI glBufferAttachMemoryNV( GLenum target, GLuint memory, GLuint64 offset )
 {
-    struct glBufferAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .target = target, .memory = memory, .offset = offset };
+    struct glBufferAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .target = target, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, memory %d, offset %s\n", target, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glBufferAttachMemoryNV, &args ))) WARN( "glBufferAttachMemoryNV returned %#lx\n", status );
 }
 
@@ -3850,9 +3978,10 @@ static void WINAPI glBufferPageCommitmentARB( GLenum target, GLintptr offset, GL
 
 static void WINAPI glBufferPageCommitmentMemNV( GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit )
 {
-    struct glBufferPageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .target = target, .offset = offset, .size = size, .memory = memory, .memOffset = memOffset, .commit = commit };
+    struct glBufferPageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .target = target, .offset = offset, .size = size, .memOffset = memOffset, .commit = commit };
     NTSTATUS status;
     TRACE( "target %d, offset %Id, size %Id, memory %d, memOffset %s, commit %d\n", target, offset, size, memory, wine_dbgstr_longlong(memOffset), commit );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glBufferPageCommitmentMemNV, &args ))) WARN( "glBufferPageCommitmentMemNV returned %#lx\n", status );
 }
 
@@ -3891,9 +4020,10 @@ static void WINAPI glBufferStorageExternalEXT( GLenum target, GLintptr offset, G
 
 static void WINAPI glBufferStorageMemEXT( GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset )
 {
-    struct glBufferStorageMemEXT_params args = { .teb = NtCurrentTeb(), .target = target, .size = size, .memory = memory, .offset = offset };
+    struct glBufferStorageMemEXT_params args = { .teb = NtCurrentTeb(), .target = target, .size = size, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, size %Id, memory %d, offset %s\n", target, size, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glBufferStorageMemEXT, &args ))) WARN( "glBufferStorageMemEXT returned %#lx\n", status );
 }
 
@@ -3941,18 +4071,21 @@ static GLenum WINAPI glCheckFramebufferStatusEXT( GLenum target )
 
 static GLenum WINAPI glCheckNamedFramebufferStatus( GLuint framebuffer, GLenum target )
 {
-    struct glCheckNamedFramebufferStatus_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .target = target };
+    struct glCheckNamedFramebufferStatus_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "framebuffer %d, target %d\n", framebuffer, target );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glCheckNamedFramebufferStatus, &args ))) WARN( "glCheckNamedFramebufferStatus returned %#lx\n", status );
     return args.ret;
 }
 
 static GLenum WINAPI glCheckNamedFramebufferStatusEXT( GLuint framebuffer, GLenum target )
 {
-    struct glCheckNamedFramebufferStatusEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .target = target };
+    struct glCheckNamedFramebufferStatusEXT_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "framebuffer %d, target %d\n", framebuffer, target );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return args.ret;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glCheckNamedFramebufferStatusEXT, &args ))) WARN( "glCheckNamedFramebufferStatusEXT returned %#lx\n", status );
     return args.ret;
 }
@@ -4103,81 +4236,93 @@ static void WINAPI glClearDepthxOES( GLfixed depth )
 
 static void WINAPI glClearNamedBufferData( GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data )
 {
-    struct glClearNamedBufferData_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .internalformat = internalformat, .format = format, .type = type, .data = data };
+    struct glClearNamedBufferData_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .format = format, .type = type, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, internalformat %d, format %d, type %d, data %p\n", buffer, internalformat, format, type, data );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glClearNamedBufferData, &args ))) WARN( "glClearNamedBufferData returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedBufferDataEXT( GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data )
 {
-    struct glClearNamedBufferDataEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .internalformat = internalformat, .format = format, .type = type, .data = data };
+    struct glClearNamedBufferDataEXT_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .format = format, .type = type, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, internalformat %d, format %d, type %d, data %p\n", buffer, internalformat, format, type, data );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glClearNamedBufferDataEXT, &args ))) WARN( "glClearNamedBufferDataEXT returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedBufferSubData( GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data )
 {
-    struct glClearNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .internalformat = internalformat, .offset = offset, .size = size, .format = format, .type = type, .data = data };
+    struct glClearNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .offset = offset, .size = size, .format = format, .type = type, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, internalformat %d, offset %Id, size %Id, format %d, type %d, data %p\n", buffer, internalformat, offset, size, format, type, data );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glClearNamedBufferSubData, &args ))) WARN( "glClearNamedBufferSubData returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedBufferSubDataEXT( GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data )
 {
-    struct glClearNamedBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .internalformat = internalformat, .offset = offset, .size = size, .format = format, .type = type, .data = data };
+    struct glClearNamedBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .offset = offset, .size = size, .format = format, .type = type, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, internalformat %d, offset %Id, size %Id, format %d, type %d, data %p\n", buffer, internalformat, offset, size, format, type, data );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glClearNamedBufferSubDataEXT, &args ))) WARN( "glClearNamedBufferSubDataEXT returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedFramebufferfi( GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil )
 {
-    struct glClearNamedFramebufferfi_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .buffer = buffer, .drawbuffer = drawbuffer, .depth = depth, .stencil = stencil };
+    struct glClearNamedFramebufferfi_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .drawbuffer = drawbuffer, .depth = depth, .stencil = stencil };
     NTSTATUS status;
     TRACE( "framebuffer %d, buffer %d, drawbuffer %d, depth %f, stencil %d\n", framebuffer, buffer, drawbuffer, depth, stencil );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glClearNamedFramebufferfi, &args ))) WARN( "glClearNamedFramebufferfi returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedFramebufferfv( GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value )
 {
-    struct glClearNamedFramebufferfv_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .buffer = buffer, .drawbuffer = drawbuffer, .value = value };
+    struct glClearNamedFramebufferfv_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .drawbuffer = drawbuffer, .value = value };
     NTSTATUS status;
     TRACE( "framebuffer %d, buffer %d, drawbuffer %d, value %p\n", framebuffer, buffer, drawbuffer, value );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glClearNamedFramebufferfv, &args ))) WARN( "glClearNamedFramebufferfv returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedFramebufferiv( GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value )
 {
-    struct glClearNamedFramebufferiv_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .buffer = buffer, .drawbuffer = drawbuffer, .value = value };
+    struct glClearNamedFramebufferiv_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .drawbuffer = drawbuffer, .value = value };
     NTSTATUS status;
     TRACE( "framebuffer %d, buffer %d, drawbuffer %d, value %p\n", framebuffer, buffer, drawbuffer, value );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glClearNamedFramebufferiv, &args ))) WARN( "glClearNamedFramebufferiv returned %#lx\n", status );
 }
 
 static void WINAPI glClearNamedFramebufferuiv( GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value )
 {
-    struct glClearNamedFramebufferuiv_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .buffer = buffer, .drawbuffer = drawbuffer, .value = value };
+    struct glClearNamedFramebufferuiv_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .drawbuffer = drawbuffer, .value = value };
     NTSTATUS status;
     TRACE( "framebuffer %d, buffer %d, drawbuffer %d, value %p\n", framebuffer, buffer, drawbuffer, value );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glClearNamedFramebufferuiv, &args ))) WARN( "glClearNamedFramebufferuiv returned %#lx\n", status );
 }
 
 static void WINAPI glClearTexImage( GLuint texture, GLint level, GLenum format, GLenum type, const void *data )
 {
-    struct glClearTexImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .format = format, .type = type, .data = data };
+    struct glClearTexImage_params args = { .teb = NtCurrentTeb(), .level = level, .format = format, .type = type, .data = data };
     NTSTATUS status;
     TRACE( "texture %d, level %d, format %d, type %d, data %p\n", texture, level, format, type, data );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glClearTexImage, &args ))) WARN( "glClearTexImage returned %#lx\n", status );
 }
 
 static void WINAPI glClearTexSubImage( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data )
 {
-    struct glClearTexSubImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .data = data };
+    struct glClearTexSubImage_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .data = data };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, format %d, type %d, data %p\n", texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glClearTexSubImage, &args ))) WARN( "glClearTexSubImage returned %#lx\n", status );
 }
 
@@ -4215,10 +4360,14 @@ static void WINAPI glClientAttribDefaultEXT( GLbitfield mask )
 
 static void WINAPI glClientWaitSemaphoreui64NVX( GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray )
 {
-    struct glClientWaitSemaphoreui64NVX_params args = { .teb = NtCurrentTeb(), .fenceObjectCount = fenceObjectCount, .semaphoreArray = semaphoreArray, .fenceValueArray = fenceValueArray };
+    GLuint semaphoreArray_buf[64], *semaphoreArray_tmp;
+    struct glClientWaitSemaphoreui64NVX_params args = { .teb = NtCurrentTeb(), .fenceObjectCount = fenceObjectCount, .fenceValueArray = fenceValueArray };
     NTSTATUS status;
     TRACE( "fenceObjectCount %d, semaphoreArray %p, fenceValueArray %p\n", fenceObjectCount, semaphoreArray, fenceValueArray );
+    semaphoreArray_tmp = fenceObjectCount > 0 ? memdup_objects( fenceObjectCount, semaphoreArray, semaphoreArray_buf, ARRAY_SIZE(semaphoreArray_buf) ) : NULL;
+    args.semaphoreArray = fenceObjectCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, fenceObjectCount, semaphoreArray_tmp ) : NULL;
     if ((status = UNIX_CALL( glClientWaitSemaphoreui64NVX, &args ))) WARN( "glClientWaitSemaphoreui64NVX returned %#lx\n", status );
+    if (semaphoreArray_tmp != semaphoreArray_buf) free( semaphoreArray_tmp );
 }
 
 static GLenum WINAPI glClientWaitSync( GLsync sync, GLbitfield flags, GLuint64 timeout )
@@ -4825,73 +4974,88 @@ static void WINAPI glCompressedTexSubImage3DARB( GLenum target, GLint level, GLi
 
 static void WINAPI glCompressedTextureImage1DEXT( GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits )
 {
-    struct glCompressedTextureImage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .width = width, .border = border, .imageSize = imageSize, .bits = bits };
+    struct glCompressedTextureImage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .width = width, .border = border, .imageSize = imageSize, .bits = bits };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, width %d, border %d, imageSize %d, bits %p\n", texture, target, level, internalformat, width, border, imageSize, bits );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureImage1DEXT, &args ))) WARN( "glCompressedTextureImage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureImage2DEXT( GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits )
 {
-    struct glCompressedTextureImage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .border = border, .imageSize = imageSize, .bits = bits };
+    struct glCompressedTextureImage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .border = border, .imageSize = imageSize, .bits = bits };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, width %d, height %d, border %d, imageSize %d, bits %p\n", texture, target, level, internalformat, width, height, border, imageSize, bits );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureImage2DEXT, &args ))) WARN( "glCompressedTextureImage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureImage3DEXT( GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits )
 {
-    struct glCompressedTextureImage3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .border = border, .imageSize = imageSize, .bits = bits };
+    struct glCompressedTextureImage3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .border = border, .imageSize = imageSize, .bits = bits };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, width %d, height %d, depth %d, border %d, imageSize %d, bits %p\n", texture, target, level, internalformat, width, height, depth, border, imageSize, bits );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureImage3DEXT, &args ))) WARN( "glCompressedTextureImage3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureSubImage1D( GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data )
 {
-    struct glCompressedTextureSubImage1D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .width = width, .format = format, .imageSize = imageSize, .data = data };
+    struct glCompressedTextureSubImage1D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .width = width, .format = format, .imageSize = imageSize, .data = data };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, width %d, format %d, imageSize %d, data %p\n", texture, level, xoffset, width, format, imageSize, data );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureSubImage1D, &args ))) WARN( "glCompressedTextureSubImage1D returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureSubImage1DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits )
 {
-    struct glCompressedTextureSubImage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .width = width, .format = format, .imageSize = imageSize, .bits = bits };
+    struct glCompressedTextureSubImage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .width = width, .format = format, .imageSize = imageSize, .bits = bits };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, width %d, format %d, imageSize %d, bits %p\n", texture, target, level, xoffset, width, format, imageSize, bits );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureSubImage1DEXT, &args ))) WARN( "glCompressedTextureSubImage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureSubImage2D( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data )
 {
-    struct glCompressedTextureSubImage2D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .imageSize = imageSize, .data = data };
+    struct glCompressedTextureSubImage2D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .imageSize = imageSize, .data = data };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, width %d, height %d, format %d, imageSize %d, data %p\n", texture, level, xoffset, yoffset, width, height, format, imageSize, data );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureSubImage2D, &args ))) WARN( "glCompressedTextureSubImage2D returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureSubImage2DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits )
 {
-    struct glCompressedTextureSubImage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .imageSize = imageSize, .bits = bits };
+    struct glCompressedTextureSubImage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .imageSize = imageSize, .bits = bits };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, yoffset %d, width %d, height %d, format %d, imageSize %d, bits %p\n", texture, target, level, xoffset, yoffset, width, height, format, imageSize, bits );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureSubImage2DEXT, &args ))) WARN( "glCompressedTextureSubImage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureSubImage3D( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data )
 {
-    struct glCompressedTextureSubImage3D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .imageSize = imageSize, .data = data };
+    struct glCompressedTextureSubImage3D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .imageSize = imageSize, .data = data };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, format %d, imageSize %d, data %p\n", texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureSubImage3D, &args ))) WARN( "glCompressedTextureSubImage3D returned %#lx\n", status );
 }
 
 static void WINAPI glCompressedTextureSubImage3DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits )
 {
-    struct glCompressedTextureSubImage3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .imageSize = imageSize, .bits = bits };
+    struct glCompressedTextureSubImage3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .imageSize = imageSize, .bits = bits };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, format %d, imageSize %d, bits %p\n", texture, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCompressedTextureSubImage3DEXT, &args ))) WARN( "glCompressedTextureSubImage3DEXT returned %#lx\n", status );
 }
 
@@ -5097,17 +5261,21 @@ static void WINAPI glCopyConvolutionFilter2DEXT( GLenum target, GLenum internalf
 
 static void WINAPI glCopyImageSubData( GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth )
 {
-    struct glCopyImageSubData_params args = { .teb = NtCurrentTeb(), .srcName = srcName, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstName = dstName, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .srcWidth = srcWidth, .srcHeight = srcHeight, .srcDepth = srcDepth };
+    struct glCopyImageSubData_params args = { .teb = NtCurrentTeb(), .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .srcWidth = srcWidth, .srcHeight = srcHeight, .srcDepth = srcDepth };
     NTSTATUS status;
     TRACE( "srcName %d, srcTarget %d, srcLevel %d, srcX %d, srcY %d, srcZ %d, dstName %d, dstTarget %d, dstLevel %d, dstX %d, dstY %d, dstZ %d, srcWidth %d, srcHeight %d, srcDepth %d\n", srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth );
+    args.srcName = *map_context_objects( srcTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &srcName );
+    args.dstName = *map_context_objects( dstTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &dstName );
     if ((status = UNIX_CALL( glCopyImageSubData, &args ))) WARN( "glCopyImageSubData returned %#lx\n", status );
 }
 
 static void WINAPI glCopyImageSubDataNV( GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth )
 {
-    struct glCopyImageSubDataNV_params args = { .teb = NtCurrentTeb(), .srcName = srcName, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstName = dstName, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .width = width, .height = height, .depth = depth };
+    struct glCopyImageSubDataNV_params args = { .teb = NtCurrentTeb(), .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .width = width, .height = height, .depth = depth };
     NTSTATUS status;
     TRACE( "srcName %d, srcTarget %d, srcLevel %d, srcX %d, srcY %d, srcZ %d, dstName %d, dstTarget %d, dstLevel %d, dstX %d, dstY %d, dstZ %d, width %d, height %d, depth %d\n", srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, width, height, depth );
+    args.srcName = *map_context_objects( srcTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &srcName );
+    args.dstName = *map_context_objects( dstTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &dstName );
     if ((status = UNIX_CALL( glCopyImageSubDataNV, &args ))) WARN( "glCopyImageSubDataNV returned %#lx\n", status );
 }
 
@@ -5153,17 +5321,23 @@ static void WINAPI glCopyMultiTexSubImage3DEXT( GLenum texunit, GLenum target, G
 
 static void WINAPI glCopyNamedBufferSubData( GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size )
 {
-    struct glCopyNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .readBuffer = readBuffer, .writeBuffer = writeBuffer, .readOffset = readOffset, .writeOffset = writeOffset, .size = size };
+    struct glCopyNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .readOffset = readOffset, .writeOffset = writeOffset, .size = size };
     NTSTATUS status;
     TRACE( "readBuffer %d, writeBuffer %d, readOffset %Id, writeOffset %Id, size %Id\n", readBuffer, writeBuffer, readOffset, writeOffset, size );
+    args.readBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &readBuffer );
+    args.writeBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &writeBuffer );
     if ((status = UNIX_CALL( glCopyNamedBufferSubData, &args ))) WARN( "glCopyNamedBufferSubData returned %#lx\n", status );
 }
 
 static void WINAPI glCopyPathNV( GLuint resultPath, GLuint srcPath )
 {
-    struct glCopyPathNV_params args = { .teb = NtCurrentTeb(), .resultPath = resultPath, .srcPath = srcPath };
+    struct glCopyPathNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "resultPath %d, srcPath %d\n", resultPath, srcPath );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &resultPath, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &srcPath, TRUE )) return;
+    args.resultPath = *map_context_objects( OBJ_TYPE_PATH, 1, &resultPath );
+    args.srcPath = *map_context_objects( OBJ_TYPE_PATH, 1, &srcPath );
     if ((status = UNIX_CALL( glCopyPathNV, &args ))) WARN( "glCopyPathNV returned %#lx\n", status );
 }
 
@@ -5217,97 +5391,118 @@ static void WINAPI glCopyTexSubImage3DEXT( GLenum target, GLint level, GLint xof
 
 static void WINAPI glCopyTextureImage1DEXT( GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border )
 {
-    struct glCopyTextureImage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .x = x, .y = y, .width = width, .border = border };
+    struct glCopyTextureImage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .x = x, .y = y, .width = width, .border = border };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, x %d, y %d, width %d, border %d\n", texture, target, level, internalformat, x, y, width, border );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureImage1DEXT, &args ))) WARN( "glCopyTextureImage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureImage2DEXT( GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border )
 {
-    struct glCopyTextureImage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .x = x, .y = y, .width = width, .height = height, .border = border };
+    struct glCopyTextureImage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .x = x, .y = y, .width = width, .height = height, .border = border };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, x %d, y %d, width %d, height %d, border %d\n", texture, target, level, internalformat, x, y, width, height, border );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureImage2DEXT, &args ))) WARN( "glCopyTextureImage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureSubImage1D( GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width )
 {
-    struct glCopyTextureSubImage1D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .x = x, .y = y, .width = width };
+    struct glCopyTextureSubImage1D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .x = x, .y = y, .width = width };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, x %d, y %d, width %d\n", texture, level, xoffset, x, y, width );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureSubImage1D, &args ))) WARN( "glCopyTextureSubImage1D returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureSubImage1DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width )
 {
-    struct glCopyTextureSubImage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .x = x, .y = y, .width = width };
+    struct glCopyTextureSubImage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .x = x, .y = y, .width = width };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, x %d, y %d, width %d\n", texture, target, level, xoffset, x, y, width );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureSubImage1DEXT, &args ))) WARN( "glCopyTextureSubImage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureSubImage2D( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height )
 {
-    struct glCopyTextureSubImage2D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .x = x, .y = y, .width = width, .height = height };
+    struct glCopyTextureSubImage2D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .x = x, .y = y, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, x %d, y %d, width %d, height %d\n", texture, level, xoffset, yoffset, x, y, width, height );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureSubImage2D, &args ))) WARN( "glCopyTextureSubImage2D returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureSubImage2DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height )
 {
-    struct glCopyTextureSubImage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .x = x, .y = y, .width = width, .height = height };
+    struct glCopyTextureSubImage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .x = x, .y = y, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, yoffset %d, x %d, y %d, width %d, height %d\n", texture, target, level, xoffset, yoffset, x, y, width, height );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureSubImage2DEXT, &args ))) WARN( "glCopyTextureSubImage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureSubImage3D( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height )
 {
-    struct glCopyTextureSubImage3D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .x = x, .y = y, .width = width, .height = height };
+    struct glCopyTextureSubImage3D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .x = x, .y = y, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, x %d, y %d, width %d, height %d\n", texture, level, xoffset, yoffset, zoffset, x, y, width, height );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureSubImage3D, &args ))) WARN( "glCopyTextureSubImage3D returned %#lx\n", status );
 }
 
 static void WINAPI glCopyTextureSubImage3DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height )
 {
-    struct glCopyTextureSubImage3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .x = x, .y = y, .width = width, .height = height };
+    struct glCopyTextureSubImage3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .x = x, .y = y, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, yoffset %d, zoffset %d, x %d, y %d, width %d, height %d\n", texture, target, level, xoffset, yoffset, zoffset, x, y, width, height );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glCopyTextureSubImage3DEXT, &args ))) WARN( "glCopyTextureSubImage3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glCoverFillPathInstancedNV( GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glCoverFillPathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
+    struct glCoverFillPathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "numPaths %d, pathNameType %d, paths %p, pathBase %d, coverMode %d, transformType %d, transformValues %p\n", numPaths, pathNameType, paths, pathBase, coverMode, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glCoverFillPathInstancedNV, &args ))) WARN( "glCoverFillPathInstancedNV returned %#lx\n", status );
 }
 
 static void WINAPI glCoverFillPathNV( GLuint path, GLenum coverMode )
 {
-    struct glCoverFillPathNV_params args = { .teb = NtCurrentTeb(), .path = path, .coverMode = coverMode };
+    struct glCoverFillPathNV_params args = { .teb = NtCurrentTeb(), .coverMode = coverMode };
     NTSTATUS status;
     TRACE( "path %d, coverMode %d\n", path, coverMode );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glCoverFillPathNV, &args ))) WARN( "glCoverFillPathNV returned %#lx\n", status );
 }
 
 static void WINAPI glCoverStrokePathInstancedNV( GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glCoverStrokePathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
+    struct glCoverStrokePathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "numPaths %d, pathNameType %d, paths %p, pathBase %d, coverMode %d, transformType %d, transformValues %p\n", numPaths, pathNameType, paths, pathBase, coverMode, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glCoverStrokePathInstancedNV, &args ))) WARN( "glCoverStrokePathInstancedNV returned %#lx\n", status );
 }
 
 static void WINAPI glCoverStrokePathNV( GLuint path, GLenum coverMode )
 {
-    struct glCoverStrokePathNV_params args = { .teb = NtCurrentTeb(), .path = path, .coverMode = coverMode };
+    struct glCoverStrokePathNV_params args = { .teb = NtCurrentTeb(), .coverMode = coverMode };
     NTSTATUS status;
     TRACE( "path %d, coverMode %d\n", path, coverMode );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glCoverStrokePathNV, &args ))) WARN( "glCoverStrokePathNV returned %#lx\n", status );
 }
 
@@ -5333,6 +5528,7 @@ static void WINAPI glCreateBuffers( GLsizei n, GLuint *buffers )
     NTSTATUS status;
     TRACE( "n %d, buffers %p\n", n, buffers );
     if ((status = UNIX_CALL( glCreateBuffers, &args ))) WARN( "glCreateBuffers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_BUFFER, n, buffers );
 }
 
 static void WINAPI glCreateCommandListsNV( GLsizei n, GLuint *lists )
@@ -5349,6 +5545,7 @@ static void WINAPI glCreateFramebuffers( GLsizei n, GLuint *framebuffers )
     NTSTATUS status;
     TRACE( "n %d, framebuffers %p\n", n, framebuffers );
     if ((status = UNIX_CALL( glCreateFramebuffers, &args ))) WARN( "glCreateFramebuffers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_FRAMEBUFFER, n, framebuffers );
 }
 
 static void WINAPI glCreateMemoryObjectsEXT( GLsizei n, GLuint *memoryObjects )
@@ -5357,6 +5554,7 @@ static void WINAPI glCreateMemoryObjectsEXT( GLsizei n, GLuint *memoryObjects )
     NTSTATUS status;
     TRACE( "n %d, memoryObjects %p\n", n, memoryObjects );
     if ((status = UNIX_CALL( glCreateMemoryObjectsEXT, &args ))) WARN( "glCreateMemoryObjectsEXT returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_MEMORY, n, memoryObjects );
 }
 
 static void WINAPI glCreatePerfQueryINTEL( GLuint queryId, GLuint *queryHandle )
@@ -5416,6 +5614,7 @@ static void WINAPI glCreateRenderbuffers( GLsizei n, GLuint *renderbuffers )
     NTSTATUS status;
     TRACE( "n %d, renderbuffers %p\n", n, renderbuffers );
     if ((status = UNIX_CALL( glCreateRenderbuffers, &args ))) WARN( "glCreateRenderbuffers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_RENDERBUFFER, n, renderbuffers );
 }
 
 static void WINAPI glCreateSamplers( GLsizei n, GLuint *samplers )
@@ -5424,6 +5623,7 @@ static void WINAPI glCreateSamplers( GLsizei n, GLuint *samplers )
     NTSTATUS status;
     TRACE( "n %d, samplers %p\n", n, samplers );
     if ((status = UNIX_CALL( glCreateSamplers, &args ))) WARN( "glCreateSamplers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_SAMPLER, n, samplers );
 }
 
 static void WINAPI glCreateSemaphoresNV( GLsizei n, GLuint *semaphores )
@@ -5432,6 +5632,7 @@ static void WINAPI glCreateSemaphoresNV( GLsizei n, GLuint *semaphores )
     NTSTATUS status;
     TRACE( "n %d, semaphores %p\n", n, semaphores );
     if ((status = UNIX_CALL( glCreateSemaphoresNV, &args ))) WARN( "glCreateSemaphoresNV returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_SEMAPHORE, n, semaphores );
 }
 
 static GLuint WINAPI glCreateShader( GLenum type )
@@ -5484,6 +5685,7 @@ static void WINAPI glCreateTextures( GLenum target, GLsizei n, GLuint *textures
     NTSTATUS status;
     TRACE( "target %d, n %d, textures %p\n", target, n, textures );
     if ((status = UNIX_CALL( glCreateTextures, &args ))) WARN( "glCreateTextures returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_TEXTURE, n, textures );
 }
 
 static void WINAPI glCreateTransformFeedbacks( GLsizei n, GLuint *ids )
@@ -5640,18 +5842,26 @@ static void WINAPI glDeleteBufferRegion( GLenum region )
 
 static void WINAPI glDeleteBuffers( GLsizei n, const GLuint *buffers )
 {
-    struct glDeleteBuffers_params args = { .teb = NtCurrentTeb(), .n = n, .buffers = buffers };
+    GLuint buffers_buf[64], *buffers_tmp;
+    struct glDeleteBuffers_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, buffers %p\n", n, buffers );
+    buffers_tmp = n > 0 ? memdup_objects( n, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = n > 0 ? del_context_objects( OBJ_TYPE_BUFFER, n, buffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteBuffers, &args ))) WARN( "glDeleteBuffers returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
 }
 
 static void WINAPI glDeleteBuffersARB( GLsizei n, const GLuint *buffers )
 {
-    struct glDeleteBuffersARB_params args = { .teb = NtCurrentTeb(), .n = n, .buffers = buffers };
+    GLuint buffers_buf[64], *buffers_tmp;
+    struct glDeleteBuffersARB_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, buffers %p\n", n, buffers );
+    buffers_tmp = n > 0 ? memdup_objects( n, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = n > 0 ? del_context_objects( OBJ_TYPE_BUFFER, n, buffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteBuffersARB, &args ))) WARN( "glDeleteBuffersARB returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
 }
 
 static void WINAPI glDeleteCommandListsNV( GLsizei n, const GLuint *lists )
@@ -5680,34 +5890,47 @@ static void WINAPI glDeleteFencesNV( GLsizei n, const GLuint *fences )
 
 static void WINAPI glDeleteFragmentShaderATI( GLuint id )
 {
-    struct glDeleteFragmentShaderATI_params args = { .teb = NtCurrentTeb(), .id = id };
+    struct glDeleteFragmentShaderATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "id %d\n", id );
+    args.id = *del_context_objects( OBJ_TYPE_SHADER_ATI, 1, &id );
     if ((status = UNIX_CALL( glDeleteFragmentShaderATI, &args ))) WARN( "glDeleteFragmentShaderATI returned %#lx\n", status );
 }
 
 static void WINAPI glDeleteFramebuffers( GLsizei n, const GLuint *framebuffers )
 {
-    struct glDeleteFramebuffers_params args = { .teb = NtCurrentTeb(), .n = n, .framebuffers = framebuffers };
+    GLuint framebuffers_buf[64], *framebuffers_tmp;
+    struct glDeleteFramebuffers_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, framebuffers %p\n", n, framebuffers );
+    framebuffers_tmp = n > 0 ? memdup_objects( n, framebuffers, framebuffers_buf, ARRAY_SIZE(framebuffers_buf) ) : NULL;
+    args.framebuffers = n > 0 ? del_context_objects( OBJ_TYPE_FRAMEBUFFER, n, framebuffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteFramebuffers, &args ))) WARN( "glDeleteFramebuffers returned %#lx\n", status );
+    if (framebuffers_tmp != framebuffers_buf) free( framebuffers_tmp );
 }
 
 static void WINAPI glDeleteFramebuffersEXT( GLsizei n, const GLuint *framebuffers )
 {
-    struct glDeleteFramebuffersEXT_params args = { .teb = NtCurrentTeb(), .n = n, .framebuffers = framebuffers };
+    GLuint framebuffers_buf[64], *framebuffers_tmp;
+    struct glDeleteFramebuffersEXT_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, framebuffers %p\n", n, framebuffers );
+    framebuffers_tmp = n > 0 ? memdup_objects( n, framebuffers, framebuffers_buf, ARRAY_SIZE(framebuffers_buf) ) : NULL;
+    args.framebuffers = n > 0 ? del_context_objects( OBJ_TYPE_FRAMEBUFFER, n, framebuffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteFramebuffersEXT, &args ))) WARN( "glDeleteFramebuffersEXT returned %#lx\n", status );
+    if (framebuffers_tmp != framebuffers_buf) free( framebuffers_tmp );
 }
 
 static void WINAPI glDeleteMemoryObjectsEXT( GLsizei n, const GLuint *memoryObjects )
 {
-    struct glDeleteMemoryObjectsEXT_params args = { .teb = NtCurrentTeb(), .n = n, .memoryObjects = memoryObjects };
+    GLuint memoryObjects_buf[64], *memoryObjects_tmp;
+    struct glDeleteMemoryObjectsEXT_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, memoryObjects %p\n", n, memoryObjects );
+    memoryObjects_tmp = n > 0 ? memdup_objects( n, memoryObjects, memoryObjects_buf, ARRAY_SIZE(memoryObjects_buf) ) : NULL;
+    args.memoryObjects = n > 0 ? del_context_objects( OBJ_TYPE_MEMORY, n, memoryObjects_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteMemoryObjectsEXT, &args ))) WARN( "glDeleteMemoryObjectsEXT returned %#lx\n", status );
+    if (memoryObjects_tmp != memoryObjects_buf) free( memoryObjects_tmp );
 }
 
 static void WINAPI glDeleteNamedStringARB( GLint namelen, const GLchar *name )
@@ -5736,9 +5959,10 @@ static void WINAPI glDeleteObjectARB( GLhandleARB obj )
 
 static void WINAPI glDeleteObjectBufferATI( GLuint buffer )
 {
-    struct glDeleteObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glDeleteObjectBufferATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *del_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glDeleteObjectBufferATI, &args ))) WARN( "glDeleteObjectBufferATI returned %#lx\n", status );
 }
 
@@ -5752,9 +5976,10 @@ static void WINAPI glDeleteOcclusionQueriesNV( GLsizei n, const GLuint *ids )
 
 static void WINAPI glDeletePathsNV( GLuint path, GLsizei range )
 {
-    struct glDeletePathsNV_params args = { .teb = NtCurrentTeb(), .path = path, .range = range };
+    struct glDeletePathsNV_params args = { .teb = NtCurrentTeb(), .range = range };
     NTSTATUS status;
     TRACE( "path %d, range %d\n", path, range );
+    args.path = *del_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glDeletePathsNV, &args ))) WARN( "glDeletePathsNV returned %#lx\n", status );
 }
 
@@ -5792,18 +6017,26 @@ static void WINAPI glDeleteProgramPipelines( GLsizei n, const GLuint *pipelines
 
 static void WINAPI glDeleteProgramsARB( GLsizei n, const GLuint *programs )
 {
-    struct glDeleteProgramsARB_params args = { .teb = NtCurrentTeb(), .n = n, .programs = programs };
+    GLuint programs_buf[64], *programs_tmp;
+    struct glDeleteProgramsARB_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, programs %p\n", n, programs );
+    programs_tmp = n > 0 ? memdup_objects( n, programs, programs_buf, ARRAY_SIZE(programs_buf) ) : NULL;
+    args.programs = n > 0 ? del_context_objects( OBJ_TYPE_PROGRAM, n, programs_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteProgramsARB, &args ))) WARN( "glDeleteProgramsARB returned %#lx\n", status );
+    if (programs_tmp != programs_buf) free( programs_tmp );
 }
 
 static void WINAPI glDeleteProgramsNV( GLsizei n, const GLuint *programs )
 {
-    struct glDeleteProgramsNV_params args = { .teb = NtCurrentTeb(), .n = n, .programs = programs };
+    GLuint programs_buf[64], *programs_tmp;
+    struct glDeleteProgramsNV_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, programs %p\n", n, programs );
+    programs_tmp = n > 0 ? memdup_objects( n, programs, programs_buf, ARRAY_SIZE(programs_buf) ) : NULL;
+    args.programs = n > 0 ? del_context_objects( OBJ_TYPE_PROGRAM, n, programs_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteProgramsNV, &args ))) WARN( "glDeleteProgramsNV returned %#lx\n", status );
+    if (programs_tmp != programs_buf) free( programs_tmp );
 }
 
 static void WINAPI glDeleteQueries( GLsizei n, const GLuint *ids )
@@ -5832,34 +6065,50 @@ static void WINAPI glDeleteQueryResourceTagNV( GLsizei n, const GLint *tagIds )
 
 static void WINAPI glDeleteRenderbuffers( GLsizei n, const GLuint *renderbuffers )
 {
-    struct glDeleteRenderbuffers_params args = { .teb = NtCurrentTeb(), .n = n, .renderbuffers = renderbuffers };
+    GLuint renderbuffers_buf[64], *renderbuffers_tmp;
+    struct glDeleteRenderbuffers_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, renderbuffers %p\n", n, renderbuffers );
+    renderbuffers_tmp = n > 0 ? memdup_objects( n, renderbuffers, renderbuffers_buf, ARRAY_SIZE(renderbuffers_buf) ) : NULL;
+    args.renderbuffers = n > 0 ? del_context_objects( OBJ_TYPE_RENDERBUFFER, n, renderbuffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteRenderbuffers, &args ))) WARN( "glDeleteRenderbuffers returned %#lx\n", status );
+    if (renderbuffers_tmp != renderbuffers_buf) free( renderbuffers_tmp );
 }
 
 static void WINAPI glDeleteRenderbuffersEXT( GLsizei n, const GLuint *renderbuffers )
 {
-    struct glDeleteRenderbuffersEXT_params args = { .teb = NtCurrentTeb(), .n = n, .renderbuffers = renderbuffers };
+    GLuint renderbuffers_buf[64], *renderbuffers_tmp;
+    struct glDeleteRenderbuffersEXT_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, renderbuffers %p\n", n, renderbuffers );
+    renderbuffers_tmp = n > 0 ? memdup_objects( n, renderbuffers, renderbuffers_buf, ARRAY_SIZE(renderbuffers_buf) ) : NULL;
+    args.renderbuffers = n > 0 ? del_context_objects( OBJ_TYPE_RENDERBUFFER, n, renderbuffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteRenderbuffersEXT, &args ))) WARN( "glDeleteRenderbuffersEXT returned %#lx\n", status );
+    if (renderbuffers_tmp != renderbuffers_buf) free( renderbuffers_tmp );
 }
 
 static void WINAPI glDeleteSamplers( GLsizei count, const GLuint *samplers )
 {
-    struct glDeleteSamplers_params args = { .teb = NtCurrentTeb(), .count = count, .samplers = samplers };
+    GLuint samplers_buf[64], *samplers_tmp;
+    struct glDeleteSamplers_params args = { .teb = NtCurrentTeb(), .count = count };
     NTSTATUS status;
     TRACE( "count %d, samplers %p\n", count, samplers );
+    samplers_tmp = count > 0 ? memdup_objects( count, samplers, samplers_buf, ARRAY_SIZE(samplers_buf) ) : NULL;
+    args.samplers = count > 0 ? del_context_objects( OBJ_TYPE_SAMPLER, count, samplers_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteSamplers, &args ))) WARN( "glDeleteSamplers returned %#lx\n", status );
+    if (samplers_tmp != samplers_buf) free( samplers_tmp );
 }
 
 static void WINAPI glDeleteSemaphoresEXT( GLsizei n, const GLuint *semaphores )
 {
-    struct glDeleteSemaphoresEXT_params args = { .teb = NtCurrentTeb(), .n = n, .semaphores = semaphores };
+    GLuint semaphores_buf[64], *semaphores_tmp;
+    struct glDeleteSemaphoresEXT_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, semaphores %p\n", n, semaphores );
+    semaphores_tmp = n > 0 ? memdup_objects( n, semaphores, semaphores_buf, ARRAY_SIZE(semaphores_buf) ) : NULL;
+    args.semaphores = n > 0 ? del_context_objects( OBJ_TYPE_SEMAPHORE, n, semaphores_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteSemaphoresEXT, &args ))) WARN( "glDeleteSemaphoresEXT returned %#lx\n", status );
+    if (semaphores_tmp != semaphores_buf) free( semaphores_tmp );
 }
 
 static void WINAPI glDeleteShader( GLuint shader )
@@ -5880,10 +6129,14 @@ static void WINAPI glDeleteStatesNV( GLsizei n, const GLuint *states )
 
 static void WINAPI glDeleteTexturesEXT( GLsizei n, const GLuint *textures )
 {
-    struct glDeleteTexturesEXT_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glDeleteTexturesEXT_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, textures %p\n", n, textures );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? del_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glDeleteTexturesEXT, &args ))) WARN( "glDeleteTexturesEXT returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static void WINAPI glDeleteTransformFeedbacks( GLsizei n, const GLuint *ids )
@@ -5920,9 +6173,10 @@ static void WINAPI glDeleteVertexArraysAPPLE( GLsizei n, const GLuint *arrays )
 
 static void WINAPI glDeleteVertexShaderEXT( GLuint id )
 {
-    struct glDeleteVertexShaderEXT_params args = { .teb = NtCurrentTeb(), .id = id };
+    struct glDeleteVertexShaderEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "id %d\n", id );
+    args.id = *del_context_objects( OBJ_TYPE_SHADER_EXT, 1, &id );
     if ((status = UNIX_CALL( glDeleteVertexShaderEXT, &args ))) WARN( "glDeleteVertexShaderEXT returned %#lx\n", status );
 }
 
@@ -6240,26 +6494,36 @@ static void WINAPI glDrawCommandsAddressNV( GLenum primitiveMode, const GLuint64
 
 static void WINAPI glDrawCommandsNV( GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count )
 {
-    struct glDrawCommandsNV_params args = { .teb = NtCurrentTeb(), .primitiveMode = primitiveMode, .buffer = buffer, .indirects = indirects, .sizes = sizes, .count = count };
+    struct glDrawCommandsNV_params args = { .teb = NtCurrentTeb(), .primitiveMode = primitiveMode, .indirects = indirects, .sizes = sizes, .count = count };
     NTSTATUS status;
     TRACE( "primitiveMode %d, buffer %d, indirects %p, sizes %p, count %d\n", primitiveMode, buffer, indirects, sizes, count );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glDrawCommandsNV, &args ))) WARN( "glDrawCommandsNV returned %#lx\n", status );
 }
 
 static void WINAPI glDrawCommandsStatesAddressNV( const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count )
 {
-    struct glDrawCommandsStatesAddressNV_params args = { .teb = NtCurrentTeb(), .indirects = indirects, .sizes = sizes, .states = states, .fbos = fbos, .count = count };
+    GLuint fbos_buf[64], *fbos_tmp;
+    struct glDrawCommandsStatesAddressNV_params args = { .teb = NtCurrentTeb(), .indirects = indirects, .sizes = sizes, .states = states, .count = count };
     NTSTATUS status;
     TRACE( "indirects %p, sizes %p, states %p, fbos %p, count %d\n", indirects, sizes, states, fbos, count );
+    fbos_tmp = count > 0 ? memdup_objects( count, fbos, fbos_buf, ARRAY_SIZE(fbos_buf) ) : NULL;
+    args.fbos = count > 0 ? map_context_objects( OBJ_TYPE_FRAMEBUFFER, count, fbos_tmp ) : NULL;
     if ((status = UNIX_CALL( glDrawCommandsStatesAddressNV, &args ))) WARN( "glDrawCommandsStatesAddressNV returned %#lx\n", status );
+    if (fbos_tmp != fbos_buf) free( fbos_tmp );
 }
 
 static void WINAPI glDrawCommandsStatesNV( GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count )
 {
-    struct glDrawCommandsStatesNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .indirects = indirects, .sizes = sizes, .states = states, .fbos = fbos, .count = count };
+    GLuint fbos_buf[64], *fbos_tmp;
+    struct glDrawCommandsStatesNV_params args = { .teb = NtCurrentTeb(), .indirects = indirects, .sizes = sizes, .states = states, .count = count };
     NTSTATUS status;
     TRACE( "buffer %d, indirects %p, sizes %p, states %p, fbos %p, count %d\n", buffer, indirects, sizes, states, fbos, count );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
+    fbos_tmp = count > 0 ? memdup_objects( count, fbos, fbos_buf, ARRAY_SIZE(fbos_buf) ) : NULL;
+    args.fbos = count > 0 ? map_context_objects( OBJ_TYPE_FRAMEBUFFER, count, fbos_tmp ) : NULL;
     if ((status = UNIX_CALL( glDrawCommandsStatesNV, &args ))) WARN( "glDrawCommandsStatesNV returned %#lx\n", status );
+    if (fbos_tmp != fbos_buf) free( fbos_tmp );
 }
 
 static void WINAPI glDrawElementArrayAPPLE( GLenum mode, GLint first, GLsizei count )
@@ -6424,9 +6688,11 @@ static void WINAPI glDrawRangeElementsEXT( GLenum mode, GLuint start, GLuint end
 
 static void WINAPI glDrawTextureNV( GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1 )
 {
-    struct glDrawTextureNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .sampler = sampler, .x0 = x0, .y0 = y0, .x1 = x1, .y1 = y1, .z = z, .s0 = s0, .t0 = t0, .s1 = s1, .t1 = t1 };
+    struct glDrawTextureNV_params args = { .teb = NtCurrentTeb(), .x0 = x0, .y0 = y0, .x1 = x1, .y1 = y1, .z = z, .s0 = s0, .t0 = t0, .s1 = s1, .t1 = t1 };
     NTSTATUS status;
     TRACE( "texture %d, sampler %d, x0 %f, y0 %f, x1 %f, y1 %f, z %f, s0 %f, t0 %f, s1 %f, t1 %f\n", texture, sampler, x0, y0, x1, y1, z, s0, t0, s1, t1 );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glDrawTextureNV, &args ))) WARN( "glDrawTextureNV returned %#lx\n", status );
 }
 
@@ -6472,9 +6738,10 @@ static void WINAPI glDrawTransformFeedbackStreamInstanced( GLenum mode, GLuint i
 
 static void WINAPI glDrawVkImageNV( GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1 )
 {
-    struct glDrawVkImageNV_params args = { .teb = NtCurrentTeb(), .vkImage = vkImage, .sampler = sampler, .x0 = x0, .y0 = y0, .x1 = x1, .y1 = y1, .z = z, .s0 = s0, .t0 = t0, .s1 = s1, .t1 = t1 };
+    struct glDrawVkImageNV_params args = { .teb = NtCurrentTeb(), .vkImage = vkImage, .x0 = x0, .y0 = y0, .x1 = x1, .y1 = y1, .z = z, .s0 = s0, .t0 = t0, .s1 = s1, .t1 = t1 };
     NTSTATUS status;
     TRACE( "vkImage %s, sampler %d, x0 %f, y0 %f, x1 %f, y1 %f, z %f, s0 %f, t0 %f, s1 %f, t1 %f\n", wine_dbgstr_longlong(vkImage), sampler, x0, y0, x1, y1, z, s0, t0, s1, t1 );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glDrawVkImageNV, &args ))) WARN( "glDrawVkImageNV returned %#lx\n", status );
 }
 
@@ -6488,9 +6755,10 @@ static void WINAPI glEGLImageTargetTexStorageEXT( GLenum target, GLeglImageOES i
 
 static void WINAPI glEGLImageTargetTextureStorageEXT( GLuint texture, GLeglImageOES image, const GLint* attrib_list )
 {
-    struct glEGLImageTargetTextureStorageEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .image = image, .attrib_list = attrib_list };
+    struct glEGLImageTargetTextureStorageEXT_params args = { .teb = NtCurrentTeb(), .image = image, .attrib_list = attrib_list };
     NTSTATUS status;
     TRACE( "texture %d, image %p, attrib_list %p\n", texture, image, attrib_list );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glEGLImageTargetTextureStorageEXT, &args ))) WARN( "glEGLImageTargetTextureStorageEXT returned %#lx\n", status );
 }
 
@@ -6881,17 +7149,20 @@ static void WINAPI glFlushMappedBufferRangeAPPLE( GLenum target, GLintptr offset
 
 static void WINAPI glFlushMappedNamedBufferRange( GLuint buffer, GLintptr offset, GLsizeiptr length )
 {
-    struct glFlushMappedNamedBufferRange_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .length = length };
+    struct glFlushMappedNamedBufferRange_params args = { .teb = NtCurrentTeb(), .offset = offset, .length = length };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, length %Id\n", buffer, offset, length );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glFlushMappedNamedBufferRange, &args ))) WARN( "glFlushMappedNamedBufferRange returned %#lx\n", status );
 }
 
 static void WINAPI glFlushMappedNamedBufferRangeEXT( GLuint buffer, GLintptr offset, GLsizeiptr length )
 {
-    struct glFlushMappedNamedBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .length = length };
+    struct glFlushMappedNamedBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .length = length };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, length %Id\n", buffer, offset, length );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glFlushMappedNamedBufferRangeEXT, &args ))) WARN( "glFlushMappedNamedBufferRangeEXT returned %#lx\n", status );
 }
 
@@ -7217,17 +7488,21 @@ static void WINAPI glFrameZoomSGIX( GLint factor )
 
 static void WINAPI glFramebufferDrawBufferEXT( GLuint framebuffer, GLenum mode )
 {
-    struct glFramebufferDrawBufferEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .mode = mode };
+    struct glFramebufferDrawBufferEXT_params args = { .teb = NtCurrentTeb(), .mode = mode };
     NTSTATUS status;
     TRACE( "framebuffer %d, mode %d\n", framebuffer, mode );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glFramebufferDrawBufferEXT, &args ))) WARN( "glFramebufferDrawBufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferDrawBuffersEXT( GLuint framebuffer, GLsizei n, const GLenum *bufs )
 {
-    struct glFramebufferDrawBuffersEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .n = n, .bufs = bufs };
+    struct glFramebufferDrawBuffersEXT_params args = { .teb = NtCurrentTeb(), .n = n, .bufs = bufs };
     NTSTATUS status;
     TRACE( "framebuffer %d, n %d, bufs %p\n", framebuffer, n, bufs );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glFramebufferDrawBuffersEXT, &args ))) WARN( "glFramebufferDrawBuffersEXT returned %#lx\n", status );
 }
 
@@ -7257,25 +7532,29 @@ static void WINAPI glFramebufferParameteriMESA( GLenum target, GLenum pname, GLi
 
 static void WINAPI glFramebufferReadBufferEXT( GLuint framebuffer, GLenum mode )
 {
-    struct glFramebufferReadBufferEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .mode = mode };
+    struct glFramebufferReadBufferEXT_params args = { .teb = NtCurrentTeb(), .mode = mode };
     NTSTATUS status;
     TRACE( "framebuffer %d, mode %d\n", framebuffer, mode );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glFramebufferReadBufferEXT, &args ))) WARN( "glFramebufferReadBufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferRenderbuffer( GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer )
 {
-    struct glFramebufferRenderbuffer_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .renderbuffertarget = renderbuffertarget, .renderbuffer = renderbuffer };
+    struct glFramebufferRenderbuffer_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .renderbuffertarget = renderbuffertarget };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, renderbuffertarget %d, renderbuffer %d\n", target, attachment, renderbuffertarget, renderbuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glFramebufferRenderbuffer, &args ))) WARN( "glFramebufferRenderbuffer returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferRenderbufferEXT( GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer )
 {
-    struct glFramebufferRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .renderbuffertarget = renderbuffertarget, .renderbuffer = renderbuffer };
+    struct glFramebufferRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .renderbuffertarget = renderbuffertarget };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, renderbuffertarget %d, renderbuffer %d\n", target, attachment, renderbuffertarget, renderbuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glFramebufferRenderbufferEXT, &args ))) WARN( "glFramebufferRenderbufferEXT returned %#lx\n", status );
 }
 
@@ -7305,137 +7584,154 @@ static void WINAPI glFramebufferSamplePositionsfvAMD( GLenum target, GLuint nums
 
 static void WINAPI glFramebufferShadingRateEXT( GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight )
 {
-    struct glFramebufferShadingRateEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .baseLayer = baseLayer, .numLayers = numLayers, .texelWidth = texelWidth, .texelHeight = texelHeight };
+    struct glFramebufferShadingRateEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .baseLayer = baseLayer, .numLayers = numLayers, .texelWidth = texelWidth, .texelHeight = texelHeight };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, baseLayer %d, numLayers %d, texelWidth %d, texelHeight %d\n", target, attachment, texture, baseLayer, numLayers, texelWidth, texelHeight );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferShadingRateEXT, &args ))) WARN( "glFramebufferShadingRateEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture( GLenum target, GLenum attachment, GLuint texture, GLint level )
 {
-    struct glFramebufferTexture_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level };
+    struct glFramebufferTexture_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d\n", target, attachment, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture, &args ))) WARN( "glFramebufferTexture returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture1D( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
 {
-    struct glFramebufferTexture1D_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level };
+    struct glFramebufferTexture1D_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, textarget %d, texture %d, level %d\n", target, attachment, textarget, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture1D, &args ))) WARN( "glFramebufferTexture1D returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture1DEXT( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
 {
-    struct glFramebufferTexture1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level };
+    struct glFramebufferTexture1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, textarget %d, texture %d, level %d\n", target, attachment, textarget, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture1DEXT, &args ))) WARN( "glFramebufferTexture1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture2D( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
 {
-    struct glFramebufferTexture2D_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level };
+    struct glFramebufferTexture2D_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, textarget %d, texture %d, level %d\n", target, attachment, textarget, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture2D, &args ))) WARN( "glFramebufferTexture2D returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture2DEXT( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
 {
-    struct glFramebufferTexture2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level };
+    struct glFramebufferTexture2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, textarget %d, texture %d, level %d\n", target, attachment, textarget, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture2DEXT, &args ))) WARN( "glFramebufferTexture2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture3D( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset )
 {
-    struct glFramebufferTexture3D_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level, .zoffset = zoffset };
+    struct glFramebufferTexture3D_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .level = level, .zoffset = zoffset };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, textarget %d, texture %d, level %d, zoffset %d\n", target, attachment, textarget, texture, level, zoffset );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture3D, &args ))) WARN( "glFramebufferTexture3D returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTexture3DEXT( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset )
 {
-    struct glFramebufferTexture3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level, .zoffset = zoffset };
+    struct glFramebufferTexture3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .textarget = textarget, .level = level, .zoffset = zoffset };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, textarget %d, texture %d, level %d, zoffset %d\n", target, attachment, textarget, texture, level, zoffset );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTexture3DEXT, &args ))) WARN( "glFramebufferTexture3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureARB( GLenum target, GLenum attachment, GLuint texture, GLint level )
 {
-    struct glFramebufferTextureARB_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level };
+    struct glFramebufferTextureARB_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d\n", target, attachment, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureARB, &args ))) WARN( "glFramebufferTextureARB returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureEXT( GLenum target, GLenum attachment, GLuint texture, GLint level )
 {
-    struct glFramebufferTextureEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level };
+    struct glFramebufferTextureEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d\n", target, attachment, texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureEXT, &args ))) WARN( "glFramebufferTextureEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureFaceARB( GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face )
 {
-    struct glFramebufferTextureFaceARB_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level, .face = face };
+    struct glFramebufferTextureFaceARB_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level, .face = face };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d, face %d\n", target, attachment, texture, level, face );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureFaceARB, &args ))) WARN( "glFramebufferTextureFaceARB returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureFaceEXT( GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face )
 {
-    struct glFramebufferTextureFaceEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level, .face = face };
+    struct glFramebufferTextureFaceEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level, .face = face };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d, face %d\n", target, attachment, texture, level, face );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureFaceEXT, &args ))) WARN( "glFramebufferTextureFaceEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureLayer( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer )
 {
-    struct glFramebufferTextureLayer_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level, .layer = layer };
+    struct glFramebufferTextureLayer_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level, .layer = layer };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d, layer %d\n", target, attachment, texture, level, layer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureLayer, &args ))) WARN( "glFramebufferTextureLayer returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureLayerARB( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer )
 {
-    struct glFramebufferTextureLayerARB_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level, .layer = layer };
+    struct glFramebufferTextureLayerARB_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level, .layer = layer };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d, layer %d\n", target, attachment, texture, level, layer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureLayerARB, &args ))) WARN( "glFramebufferTextureLayerARB returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureLayerEXT( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer )
 {
-    struct glFramebufferTextureLayerEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level, .layer = layer };
+    struct glFramebufferTextureLayerEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level, .layer = layer };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d, layer %d\n", target, attachment, texture, level, layer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureLayerEXT, &args ))) WARN( "glFramebufferTextureLayerEXT returned %#lx\n", status );
 }
 
 static void WINAPI glFramebufferTextureMultiviewOVR( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews )
 {
-    struct glFramebufferTextureMultiviewOVR_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .texture = texture, .level = level, .baseViewIndex = baseViewIndex, .numViews = numViews };
+    struct glFramebufferTextureMultiviewOVR_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .level = level, .baseViewIndex = baseViewIndex, .numViews = numViews };
     NTSTATUS status;
     TRACE( "target %d, attachment %d, texture %d, level %d, baseViewIndex %d, numViews %d\n", target, attachment, texture, level, baseViewIndex, numViews );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glFramebufferTextureMultiviewOVR, &args ))) WARN( "glFramebufferTextureMultiviewOVR returned %#lx\n", status );
 }
 
 static void WINAPI glFreeObjectBufferATI( GLuint buffer )
 {
-    struct glFreeObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glFreeObjectBufferATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glFreeObjectBufferATI, &args ))) WARN( "glFreeObjectBufferATI returned %#lx\n", status );
 }
 
@@ -7486,6 +7782,7 @@ static void WINAPI glGenBuffers( GLsizei n, GLuint *buffers )
     NTSTATUS status;
     TRACE( "n %d, buffers %p\n", n, buffers );
     if ((status = UNIX_CALL( glGenBuffers, &args ))) WARN( "glGenBuffers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_BUFFER, n, buffers );
 }
 
 static void WINAPI glGenBuffersARB( GLsizei n, GLuint *buffers )
@@ -7494,6 +7791,7 @@ static void WINAPI glGenBuffersARB( GLsizei n, GLuint *buffers )
     NTSTATUS status;
     TRACE( "n %d, buffers %p\n", n, buffers );
     if ((status = UNIX_CALL( glGenBuffersARB, &args ))) WARN( "glGenBuffersARB returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_BUFFER, n, buffers );
 }
 
 static void WINAPI glGenFencesAPPLE( GLsizei n, GLuint *fences )
@@ -7518,6 +7816,7 @@ static GLuint WINAPI glGenFragmentShadersATI( GLuint range )
     NTSTATUS status;
     TRACE( "range %d\n", range );
     if ((status = UNIX_CALL( glGenFragmentShadersATI, &args ))) WARN( "glGenFragmentShadersATI returned %#lx\n", status );
+    args.ret = put_context_object_range( OBJ_TYPE_SHADER_ATI, range, args.ret );
     return args.ret;
 }
 
@@ -7527,6 +7826,7 @@ static void WINAPI glGenFramebuffers( GLsizei n, GLuint *framebuffers )
     NTSTATUS status;
     TRACE( "n %d, framebuffers %p\n", n, framebuffers );
     if ((status = UNIX_CALL( glGenFramebuffers, &args ))) WARN( "glGenFramebuffers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_FRAMEBUFFER, n, framebuffers );
 }
 
 static void WINAPI glGenFramebuffersEXT( GLsizei n, GLuint *framebuffers )
@@ -7535,6 +7835,7 @@ static void WINAPI glGenFramebuffersEXT( GLsizei n, GLuint *framebuffers )
     NTSTATUS status;
     TRACE( "n %d, framebuffers %p\n", n, framebuffers );
     if ((status = UNIX_CALL( glGenFramebuffersEXT, &args ))) WARN( "glGenFramebuffersEXT returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_FRAMEBUFFER, n, framebuffers );
 }
 
 static void WINAPI glGenNamesAMD( GLenum identifier, GLuint num, GLuint *names )
@@ -7559,6 +7860,7 @@ static GLuint WINAPI glGenPathsNV( GLsizei range )
     NTSTATUS status;
     TRACE( "range %d\n", range );
     if ((status = UNIX_CALL( glGenPathsNV, &args ))) WARN( "glGenPathsNV returned %#lx\n", status );
+    args.ret = put_context_object_range( OBJ_TYPE_PATH, range, args.ret );
     return args.ret;
 }
 
@@ -7584,6 +7886,7 @@ static void WINAPI glGenProgramsARB( GLsizei n, GLuint *programs )
     NTSTATUS status;
     TRACE( "n %d, programs %p\n", n, programs );
     if ((status = UNIX_CALL( glGenProgramsARB, &args ))) WARN( "glGenProgramsARB returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_PROGRAM, n, programs );
 }
 
 static void WINAPI glGenProgramsNV( GLsizei n, GLuint *programs )
@@ -7592,6 +7895,7 @@ static void WINAPI glGenProgramsNV( GLsizei n, GLuint *programs )
     NTSTATUS status;
     TRACE( "n %d, programs %p\n", n, programs );
     if ((status = UNIX_CALL( glGenProgramsNV, &args ))) WARN( "glGenProgramsNV returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_PROGRAM, n, programs );
 }
 
 static void WINAPI glGenQueries( GLsizei n, GLuint *ids )
@@ -7624,6 +7928,7 @@ static void WINAPI glGenRenderbuffers( GLsizei n, GLuint *renderbuffers )
     NTSTATUS status;
     TRACE( "n %d, renderbuffers %p\n", n, renderbuffers );
     if ((status = UNIX_CALL( glGenRenderbuffers, &args ))) WARN( "glGenRenderbuffers returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_RENDERBUFFER, n, renderbuffers );
 }
 
 static void WINAPI glGenRenderbuffersEXT( GLsizei n, GLuint *renderbuffers )
@@ -7632,6 +7937,7 @@ static void WINAPI glGenRenderbuffersEXT( GLsizei n, GLuint *renderbuffers )
     NTSTATUS status;
     TRACE( "n %d, renderbuffers %p\n", n, renderbuffers );
     if ((status = UNIX_CALL( glGenRenderbuffersEXT, &args ))) WARN( "glGenRenderbuffersEXT returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_RENDERBUFFER, n, renderbuffers );
 }
 
 static void WINAPI glGenSamplers( GLsizei count, GLuint *samplers )
@@ -7640,6 +7946,7 @@ static void WINAPI glGenSamplers( GLsizei count, GLuint *samplers )
     NTSTATUS status;
     TRACE( "count %d, samplers %p\n", count, samplers );
     if ((status = UNIX_CALL( glGenSamplers, &args ))) WARN( "glGenSamplers returned %#lx\n", status );
+    if (count > 0) put_context_objects( OBJ_TYPE_SAMPLER, count, samplers );
 }
 
 static void WINAPI glGenSemaphoresEXT( GLsizei n, GLuint *semaphores )
@@ -7648,6 +7955,7 @@ static void WINAPI glGenSemaphoresEXT( GLsizei n, GLuint *semaphores )
     NTSTATUS status;
     TRACE( "n %d, semaphores %p\n", n, semaphores );
     if ((status = UNIX_CALL( glGenSemaphoresEXT, &args ))) WARN( "glGenSemaphoresEXT returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_SEMAPHORE, n, semaphores );
 }
 
 static GLuint WINAPI glGenSymbolsEXT( GLenum datatype, GLenum storagetype, GLenum range, GLuint components )
@@ -7665,6 +7973,7 @@ static void WINAPI glGenTexturesEXT( GLsizei n, GLuint *textures )
     NTSTATUS status;
     TRACE( "n %d, textures %p\n", n, textures );
     if ((status = UNIX_CALL( glGenTexturesEXT, &args ))) WARN( "glGenTexturesEXT returned %#lx\n", status );
+    if (n > 0) put_context_objects( OBJ_TYPE_TEXTURE, n, textures );
 }
 
 static void WINAPI glGenTransformFeedbacks( GLsizei n, GLuint *ids )
@@ -7705,6 +8014,7 @@ static GLuint WINAPI glGenVertexShadersEXT( GLuint range )
     NTSTATUS status;
     TRACE( "range %d\n", range );
     if ((status = UNIX_CALL( glGenVertexShadersEXT, &args ))) WARN( "glGenVertexShadersEXT returned %#lx\n", status );
+    args.ret = put_context_object_range( OBJ_TYPE_SHADER_EXT, range, args.ret );
     return args.ret;
 }
 
@@ -7734,17 +8044,20 @@ static void WINAPI glGenerateMultiTexMipmapEXT( GLenum texunit, GLenum target )
 
 static void WINAPI glGenerateTextureMipmap( GLuint texture )
 {
-    struct glGenerateTextureMipmap_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glGenerateTextureMipmap_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGenerateTextureMipmap, &args ))) WARN( "glGenerateTextureMipmap returned %#lx\n", status );
 }
 
 static void WINAPI glGenerateTextureMipmapEXT( GLuint texture, GLenum target )
 {
-    struct glGenerateTextureMipmapEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target };
+    struct glGenerateTextureMipmapEXT_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "texture %d, target %d\n", texture, target );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGenerateTextureMipmapEXT, &args ))) WARN( "glGenerateTextureMipmapEXT returned %#lx\n", status );
 }
 
@@ -7906,16 +8219,20 @@ static void WINAPI glGetBooleanIndexedvEXT( GLenum target, GLuint index, GLboole
 {
     struct glGetBooleanIndexedvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetBooleanIndexedvEXT, &args ))) WARN( "glGetBooleanIndexedvEXT returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetBooleani_v( GLenum target, GLuint index, GLboolean *data )
 {
     struct glGetBooleani_v_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetBooleani_v, &args ))) WARN( "glGetBooleani_v returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetBufferParameteri64v( GLenum target, GLenum pname, GLint64 *params )
@@ -8161,25 +8478,29 @@ static void WINAPI glGetCompressedTexImageARB( GLenum target, GLint level, void
 
 static void WINAPI glGetCompressedTextureImage( GLuint texture, GLint level, GLsizei bufSize, void *pixels )
 {
-    struct glGetCompressedTextureImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .bufSize = bufSize, .pixels = pixels };
+    struct glGetCompressedTextureImage_params args = { .teb = NtCurrentTeb(), .level = level, .bufSize = bufSize, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, bufSize %d, pixels %p\n", texture, level, bufSize, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetCompressedTextureImage, &args ))) WARN( "glGetCompressedTextureImage returned %#lx\n", status );
 }
 
 static void WINAPI glGetCompressedTextureImageEXT( GLuint texture, GLenum target, GLint lod, void *img )
 {
-    struct glGetCompressedTextureImageEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .lod = lod, .img = img };
+    struct glGetCompressedTextureImageEXT_params args = { .teb = NtCurrentTeb(), .target = target, .lod = lod, .img = img };
     NTSTATUS status;
     TRACE( "texture %d, target %d, lod %d, img %p\n", texture, target, lod, img );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetCompressedTextureImageEXT, &args ))) WARN( "glGetCompressedTextureImageEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetCompressedTextureSubImage( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels )
 {
-    struct glGetCompressedTextureSubImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .bufSize = bufSize, .pixels = pixels };
+    struct glGetCompressedTextureSubImage_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .bufSize = bufSize, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, bufSize %d, pixels %p\n", texture, level, xoffset, yoffset, zoffset, width, height, depth, bufSize, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetCompressedTextureSubImage, &args ))) WARN( "glGetCompressedTextureSubImage returned %#lx\n", status );
 }
 
@@ -8286,24 +8607,30 @@ static void WINAPI glGetDoubleIndexedvEXT( GLenum target, GLuint index, GLdouble
 {
     struct glGetDoubleIndexedvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetDoubleIndexedvEXT, &args ))) WARN( "glGetDoubleIndexedvEXT returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetDoublei_v( GLenum target, GLuint index, GLdouble *data )
 {
     struct glGetDoublei_v_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetDoublei_v, &args ))) WARN( "glGetDoublei_v returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetDoublei_vEXT( GLenum pname, GLuint index, GLdouble *params )
 {
     struct glGetDoublei_vEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .index = index, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, index %d, params %p\n", pname, index, params );
     if ((status = UNIX_CALL( glGetDoublei_vEXT, &args ))) WARN( "glGetDoublei_vEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetFenceivNV( GLuint fence, GLenum pname, GLint *params )
@@ -8342,40 +8669,50 @@ static void WINAPI glGetFixedv( GLenum pname, GLfixed *params )
 {
     struct glGetFixedv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, params %p\n", pname, params );
     if ((status = UNIX_CALL( glGetFixedv, &args ))) WARN( "glGetFixedv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetFixedvOES( GLenum pname, GLfixed *params )
 {
     struct glGetFixedvOES_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, params %p\n", pname, params );
     if ((status = UNIX_CALL( glGetFixedvOES, &args ))) WARN( "glGetFixedvOES returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetFloatIndexedvEXT( GLenum target, GLuint index, GLfloat *data )
 {
     struct glGetFloatIndexedvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetFloatIndexedvEXT, &args ))) WARN( "glGetFloatIndexedvEXT returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetFloati_v( GLenum target, GLuint index, GLfloat *data )
 {
     struct glGetFloati_v_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetFloati_v, &args ))) WARN( "glGetFloati_v returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetFloati_vEXT( GLenum pname, GLuint index, GLfloat *params )
 {
     struct glGetFloati_vEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .index = index, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, index %d, params %p\n", pname, index, params );
     if ((status = UNIX_CALL( glGetFloati_vEXT, &args ))) WARN( "glGetFloati_vEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetFogFuncSGIS( GLfloat *points )
@@ -8453,22 +8790,6 @@ static void WINAPI glGetFragmentShadingRatesEXT( GLsizei samples, GLsizei maxCou
     if ((status = UNIX_CALL( glGetFragmentShadingRatesEXT, &args ))) WARN( "glGetFragmentShadingRatesEXT returned %#lx\n", status );
 }
 
-static void WINAPI glGetFramebufferAttachmentParameteriv( GLenum target, GLenum attachment, GLenum pname, GLint *params )
-{
-    struct glGetFramebufferAttachmentParameteriv_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .pname = pname, .params = params };
-    NTSTATUS status;
-    TRACE( "target %d, attachment %d, pname %d, params %p\n", target, attachment, pname, params );
-    if ((status = UNIX_CALL( glGetFramebufferAttachmentParameteriv, &args ))) WARN( "glGetFramebufferAttachmentParameteriv returned %#lx\n", status );
-}
-
-static void WINAPI glGetFramebufferAttachmentParameterivEXT( GLenum target, GLenum attachment, GLenum pname, GLint *params )
-{
-    struct glGetFramebufferAttachmentParameterivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .pname = pname, .params = params };
-    NTSTATUS status;
-    TRACE( "target %d, attachment %d, pname %d, params %p\n", target, attachment, pname, params );
-    if ((status = UNIX_CALL( glGetFramebufferAttachmentParameterivEXT, &args ))) WARN( "glGetFramebufferAttachmentParameterivEXT returned %#lx\n", status );
-}
-
 static void WINAPI glGetFramebufferParameterfvAMD( GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values )
 {
     struct glGetFramebufferParameterfvAMD_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .numsamples = numsamples, .pixelindex = pixelindex, .size = size, .values = values };
@@ -8487,9 +8808,11 @@ static void WINAPI glGetFramebufferParameteriv( GLenum target, GLenum pname, GLi
 
 static void WINAPI glGetFramebufferParameterivEXT( GLuint framebuffer, GLenum pname, GLint *params )
 {
-    struct glGetFramebufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .pname = pname, .params = params };
+    struct glGetFramebufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "framebuffer %d, pname %d, params %p\n", framebuffer, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glGetFramebufferParameterivEXT, &args ))) WARN( "glGetFramebufferParameterivEXT returned %#lx\n", status );
 }
 
@@ -8586,18 +8909,20 @@ static void WINAPI glGetHistogramParameterxvOES( GLenum target, GLenum pname, GL
 
 static GLuint64 WINAPI glGetImageHandleARB( GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format )
 {
-    struct glGetImageHandleARB_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .layered = layered, .layer = layer, .format = format };
+    struct glGetImageHandleARB_params args = { .teb = NtCurrentTeb(), .level = level, .layered = layered, .layer = layer, .format = format };
     NTSTATUS status;
     TRACE( "texture %d, level %d, layered %d, layer %d, format %d\n", texture, level, layered, layer, format );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetImageHandleARB, &args ))) WARN( "glGetImageHandleARB returned %#lx\n", status );
     return args.ret;
 }
 
 static GLuint64 WINAPI glGetImageHandleNV( GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format )
 {
-    struct glGetImageHandleNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .layered = layered, .layer = layer, .format = format };
+    struct glGetImageHandleNV_params args = { .teb = NtCurrentTeb(), .level = level, .layered = layered, .layer = layer, .format = format };
     NTSTATUS status;
     TRACE( "texture %d, level %d, layered %d, layer %d, format %d\n", texture, level, layered, layer, format );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetImageHandleNV, &args ))) WARN( "glGetImageHandleNV returned %#lx\n", status );
     return args.ret;
 }
@@ -8639,8 +8964,10 @@ static void WINAPI glGetInteger64i_v( GLenum target, GLuint index, GLint64 *data
 {
     struct glGetInteger64i_v_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetInteger64i_v, &args ))) WARN( "glGetInteger64i_v returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetInteger64v( GLenum pname, GLint64 *data )
@@ -8649,40 +8976,48 @@ static void WINAPI glGetInteger64v( GLenum pname, GLint64 *data )
     NTSTATUS status;
     int integer;
     TRACE( "pname %d, data %p\n", pname, data );
-    if (get_integer( pname, &integer )) *data = integer;
-    else if ((status = UNIX_CALL( glGetInteger64v, &args ))) WARN( "glGetInteger64v returned %#lx\n", status );
+    if ((status = UNIX_CALL( glGetInteger64v, &args ))) WARN( "glGetInteger64v returned %#lx\n", status );
+    else if (get_integer( pname, 0, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetIntegerIndexedvEXT( GLenum target, GLuint index, GLint *data )
 {
     struct glGetIntegerIndexedvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetIntegerIndexedvEXT, &args ))) WARN( "glGetIntegerIndexedvEXT returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetIntegeri_v( GLenum target, GLuint index, GLint *data )
 {
     struct glGetIntegeri_v_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetIntegeri_v, &args ))) WARN( "glGetIntegeri_v returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetIntegerui64i_vNV( GLenum value, GLuint index, GLuint64EXT *result )
 {
     struct glGetIntegerui64i_vNV_params args = { .teb = NtCurrentTeb(), .value = value, .index = index, .result = result };
     NTSTATUS status;
+    int integer;
     TRACE( "value %d, index %d, result %p\n", value, index, result );
     if ((status = UNIX_CALL( glGetIntegerui64i_vNV, &args ))) WARN( "glGetIntegerui64i_vNV returned %#lx\n", status );
+    else if (get_integer( value, index, *result, &integer )) *result = integer;
 }
 
 static void WINAPI glGetIntegerui64vNV( GLenum value, GLuint64EXT *result )
 {
     struct glGetIntegerui64vNV_params args = { .teb = NtCurrentTeb(), .value = value, .result = result };
     NTSTATUS status;
+    int integer;
     TRACE( "value %d, result %p\n", value, result );
     if ((status = UNIX_CALL( glGetIntegerui64vNV, &args ))) WARN( "glGetIntegerui64vNV returned %#lx\n", status );
+    else if (get_integer( value, 0, *result, &integer )) *result = integer;
 }
 
 static void WINAPI glGetInternalformatSampleivNV( GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params )
@@ -8751,17 +9086,19 @@ static void WINAPI glGetLightxv( GLenum light, GLenum pname, GLfixed *params )
 
 static void WINAPI glGetListParameterfvSGIX( GLuint list, GLenum pname, GLfloat *params )
 {
-    struct glGetListParameterfvSGIX_params args = { .teb = NtCurrentTeb(), .list = list, .pname = pname, .params = params };
+    struct glGetListParameterfvSGIX_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "list %d, pname %d, params %p\n", list, pname, params );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glGetListParameterfvSGIX, &args ))) WARN( "glGetListParameterfvSGIX returned %#lx\n", status );
 }
 
 static void WINAPI glGetListParameterivSGIX( GLuint list, GLenum pname, GLint *params )
 {
-    struct glGetListParameterivSGIX_params args = { .teb = NtCurrentTeb(), .list = list, .pname = pname, .params = params };
+    struct glGetListParameterivSGIX_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "list %d, pname %d, params %p\n", list, pname, params );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glGetListParameterivSGIX, &args ))) WARN( "glGetListParameterivSGIX returned %#lx\n", status );
 }
 
@@ -8855,17 +9192,19 @@ static void WINAPI glGetMaterialxv( GLenum face, GLenum pname, GLfixed *params )
 
 static void WINAPI glGetMemoryObjectDetachedResourcesuivNV( GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params )
 {
-    struct glGetMemoryObjectDetachedResourcesuivNV_params args = { .teb = NtCurrentTeb(), .memory = memory, .pname = pname, .first = first, .count = count, .params = params };
+    struct glGetMemoryObjectDetachedResourcesuivNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .first = first, .count = count, .params = params };
     NTSTATUS status;
     TRACE( "memory %d, pname %d, first %d, count %d, params %p\n", memory, pname, first, count, params );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glGetMemoryObjectDetachedResourcesuivNV, &args ))) WARN( "glGetMemoryObjectDetachedResourcesuivNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetMemoryObjectParameterivEXT( GLuint memoryObject, GLenum pname, GLint *params )
 {
-    struct glGetMemoryObjectParameterivEXT_params args = { .teb = NtCurrentTeb(), .memoryObject = memoryObject, .pname = pname, .params = params };
+    struct glGetMemoryObjectParameterivEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "memoryObject %d, pname %d, params %p\n", memoryObject, pname, params );
+    args.memoryObject = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memoryObject );
     if ((status = UNIX_CALL( glGetMemoryObjectParameterivEXT, &args ))) WARN( "glGetMemoryObjectParameterivEXT returned %#lx\n", status );
 }
 
@@ -8969,16 +9308,20 @@ static void WINAPI glGetMultiTexLevelParameterfvEXT( GLenum texunit, GLenum targ
 {
     struct glGetMultiTexLevelParameterfvEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "texunit %d, target %d, level %d, pname %d, params %p\n", texunit, target, level, pname, params );
     if ((status = UNIX_CALL( glGetMultiTexLevelParameterfvEXT, &args ))) WARN( "glGetMultiTexLevelParameterfvEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetMultiTexLevelParameterivEXT( GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params )
 {
     struct glGetMultiTexLevelParameterivEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "texunit %d, target %d, level %d, pname %d, params %p\n", texunit, target, level, pname, params );
     if ((status = UNIX_CALL( glGetMultiTexLevelParameterivEXT, &args ))) WARN( "glGetMultiTexLevelParameterivEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetMultiTexParameterIivEXT( GLenum texunit, GLenum target, GLenum pname, GLint *params )
@@ -9031,169 +9374,183 @@ static void WINAPI glGetMultisamplefvNV( GLenum pname, GLuint index, GLfloat *va
 
 static void WINAPI glGetNamedBufferParameteri64v( GLuint buffer, GLenum pname, GLint64 *params )
 {
-    struct glGetNamedBufferParameteri64v_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetNamedBufferParameteri64v_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferParameteri64v, &args ))) WARN( "glGetNamedBufferParameteri64v returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferParameteriv( GLuint buffer, GLenum pname, GLint *params )
 {
-    struct glGetNamedBufferParameteriv_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetNamedBufferParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferParameteriv, &args ))) WARN( "glGetNamedBufferParameteriv returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferParameterivEXT( GLuint buffer, GLenum pname, GLint *params )
 {
-    struct glGetNamedBufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetNamedBufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferParameterivEXT, &args ))) WARN( "glGetNamedBufferParameterivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferParameterui64vNV( GLuint buffer, GLenum pname, GLuint64EXT *params )
 {
-    struct glGetNamedBufferParameterui64vNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetNamedBufferParameterui64vNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferParameterui64vNV, &args ))) WARN( "glGetNamedBufferParameterui64vNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferPointerv( GLuint buffer, GLenum pname, void **params )
 {
-    struct glGetNamedBufferPointerv_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetNamedBufferPointerv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferPointerv, &args ))) WARN( "glGetNamedBufferPointerv returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferPointervEXT( GLuint buffer, GLenum pname, void **params )
 {
-    struct glGetNamedBufferPointervEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetNamedBufferPointervEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferPointervEXT, &args ))) WARN( "glGetNamedBufferPointervEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferSubData( GLuint buffer, GLintptr offset, GLsizeiptr size, void *data )
 {
-    struct glGetNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .data = data };
+    struct glGetNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, data %p\n", buffer, offset, size, data );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferSubData, &args ))) WARN( "glGetNamedBufferSubData returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedBufferSubDataEXT( GLuint buffer, GLintptr offset, GLsizeiptr size, void *data )
 {
-    struct glGetNamedBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .data = data };
+    struct glGetNamedBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, data %p\n", buffer, offset, size, data );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetNamedBufferSubDataEXT, &args ))) WARN( "glGetNamedBufferSubDataEXT returned %#lx\n", status );
 }
 
-static void WINAPI glGetNamedFramebufferAttachmentParameteriv( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params )
-{
-    struct glGetNamedFramebufferAttachmentParameteriv_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .pname = pname, .params = params };
-    NTSTATUS status;
-    TRACE( "framebuffer %d, attachment %d, pname %d, params %p\n", framebuffer, attachment, pname, params );
-    if ((status = UNIX_CALL( glGetNamedFramebufferAttachmentParameteriv, &args ))) WARN( "glGetNamedFramebufferAttachmentParameteriv returned %#lx\n", status );
-}
-
-static void WINAPI glGetNamedFramebufferAttachmentParameterivEXT( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params )
-{
-    struct glGetNamedFramebufferAttachmentParameterivEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .pname = pname, .params = params };
-    NTSTATUS status;
-    TRACE( "framebuffer %d, attachment %d, pname %d, params %p\n", framebuffer, attachment, pname, params );
-    if ((status = UNIX_CALL( glGetNamedFramebufferAttachmentParameterivEXT, &args ))) WARN( "glGetNamedFramebufferAttachmentParameterivEXT returned %#lx\n", status );
-}
-
 static void WINAPI glGetNamedFramebufferParameterfvAMD( GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values )
 {
-    struct glGetNamedFramebufferParameterfvAMD_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .pname = pname, .numsamples = numsamples, .pixelindex = pixelindex, .size = size, .values = values };
+    struct glGetNamedFramebufferParameterfvAMD_params args = { .teb = NtCurrentTeb(), .pname = pname, .numsamples = numsamples, .pixelindex = pixelindex, .size = size, .values = values };
     NTSTATUS status;
     TRACE( "framebuffer %d, pname %d, numsamples %d, pixelindex %d, size %d, values %p\n", framebuffer, pname, numsamples, pixelindex, size, values );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glGetNamedFramebufferParameterfvAMD, &args ))) WARN( "glGetNamedFramebufferParameterfvAMD returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedFramebufferParameteriv( GLuint framebuffer, GLenum pname, GLint *param )
 {
-    struct glGetNamedFramebufferParameteriv_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .pname = pname, .param = param };
+    struct glGetNamedFramebufferParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "framebuffer %d, pname %d, param %p\n", framebuffer, pname, param );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glGetNamedFramebufferParameteriv, &args ))) WARN( "glGetNamedFramebufferParameteriv returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedFramebufferParameterivEXT( GLuint framebuffer, GLenum pname, GLint *params )
 {
-    struct glGetNamedFramebufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .pname = pname, .params = params };
+    struct glGetNamedFramebufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "framebuffer %d, pname %d, params %p\n", framebuffer, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glGetNamedFramebufferParameterivEXT, &args ))) WARN( "glGetNamedFramebufferParameterivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedProgramLocalParameterIivEXT( GLuint program, GLenum target, GLuint index, GLint *params )
 {
-    struct glGetNamedProgramLocalParameterIivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glGetNamedProgramLocalParameterIivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glGetNamedProgramLocalParameterIivEXT, &args ))) WARN( "glGetNamedProgramLocalParameterIivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedProgramLocalParameterIuivEXT( GLuint program, GLenum target, GLuint index, GLuint *params )
 {
-    struct glGetNamedProgramLocalParameterIuivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glGetNamedProgramLocalParameterIuivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glGetNamedProgramLocalParameterIuivEXT, &args ))) WARN( "glGetNamedProgramLocalParameterIuivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedProgramLocalParameterdvEXT( GLuint program, GLenum target, GLuint index, GLdouble *params )
 {
-    struct glGetNamedProgramLocalParameterdvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glGetNamedProgramLocalParameterdvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glGetNamedProgramLocalParameterdvEXT, &args ))) WARN( "glGetNamedProgramLocalParameterdvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedProgramLocalParameterfvEXT( GLuint program, GLenum target, GLuint index, GLfloat *params )
 {
-    struct glGetNamedProgramLocalParameterfvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glGetNamedProgramLocalParameterfvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glGetNamedProgramLocalParameterfvEXT, &args ))) WARN( "glGetNamedProgramLocalParameterfvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedProgramStringEXT( GLuint program, GLenum target, GLenum pname, void *string )
 {
-    struct glGetNamedProgramStringEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .pname = pname, .string = string };
+    struct glGetNamedProgramStringEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .string = string };
     NTSTATUS status;
     TRACE( "program %d, target %d, pname %d, string %p\n", program, target, pname, string );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glGetNamedProgramStringEXT, &args ))) WARN( "glGetNamedProgramStringEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedProgramivEXT( GLuint program, GLenum target, GLenum pname, GLint *params )
 {
-    struct glGetNamedProgramivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .pname = pname, .params = params };
+    struct glGetNamedProgramivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, pname %d, params %p\n", program, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glGetNamedProgramivEXT, &args ))) WARN( "glGetNamedProgramivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedRenderbufferParameteriv( GLuint renderbuffer, GLenum pname, GLint *params )
 {
-    struct glGetNamedRenderbufferParameteriv_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .pname = pname, .params = params };
+    struct glGetNamedRenderbufferParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "renderbuffer %d, pname %d, params %p\n", renderbuffer, pname, params );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glGetNamedRenderbufferParameteriv, &args ))) WARN( "glGetNamedRenderbufferParameteriv returned %#lx\n", status );
 }
 
 static void WINAPI glGetNamedRenderbufferParameterivEXT( GLuint renderbuffer, GLenum pname, GLint *params )
 {
-    struct glGetNamedRenderbufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .pname = pname, .params = params };
+    struct glGetNamedRenderbufferParameterivEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "renderbuffer %d, pname %d, params %p\n", renderbuffer, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glGetNamedRenderbufferParameterivEXT, &args ))) WARN( "glGetNamedRenderbufferParameterivEXT returned %#lx\n", status );
 }
 
@@ -9223,17 +9580,19 @@ static void WINAPI glGetNextPerfQueryIdINTEL( GLuint queryId, GLuint *nextQueryI
 
 static void WINAPI glGetObjectBufferfvATI( GLuint buffer, GLenum pname, GLfloat *params )
 {
-    struct glGetObjectBufferfvATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetObjectBufferfvATI_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetObjectBufferfvATI, &args ))) WARN( "glGetObjectBufferfvATI returned %#lx\n", status );
 }
 
 static void WINAPI glGetObjectBufferivATI( GLuint buffer, GLenum pname, GLint *params )
 {
-    struct glGetObjectBufferivATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .pname = pname, .params = params };
+    struct glGetObjectBufferivATI_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "buffer %d, pname %d, params %p\n", buffer, pname, params );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetObjectBufferivATI, &args ))) WARN( "glGetObjectBufferivATI returned %#lx\n", status );
 }
 
@@ -9319,74 +9678,92 @@ static void WINAPI glGetPathColorGenivNV( GLenum color, GLenum pname, GLint *val
 
 static void WINAPI glGetPathCommandsNV( GLuint path, GLubyte *commands )
 {
-    struct glGetPathCommandsNV_params args = { .teb = NtCurrentTeb(), .path = path, .commands = commands };
+    struct glGetPathCommandsNV_params args = { .teb = NtCurrentTeb(), .commands = commands };
     NTSTATUS status;
     TRACE( "path %d, commands %p\n", path, commands );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glGetPathCommandsNV, &args ))) WARN( "glGetPathCommandsNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetPathCoordsNV( GLuint path, GLfloat *coords )
 {
-    struct glGetPathCoordsNV_params args = { .teb = NtCurrentTeb(), .path = path, .coords = coords };
+    struct glGetPathCoordsNV_params args = { .teb = NtCurrentTeb(), .coords = coords };
     NTSTATUS status;
     TRACE( "path %d, coords %p\n", path, coords );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glGetPathCoordsNV, &args ))) WARN( "glGetPathCoordsNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetPathDashArrayNV( GLuint path, GLfloat *dashArray )
 {
-    struct glGetPathDashArrayNV_params args = { .teb = NtCurrentTeb(), .path = path, .dashArray = dashArray };
+    struct glGetPathDashArrayNV_params args = { .teb = NtCurrentTeb(), .dashArray = dashArray };
     NTSTATUS status;
     TRACE( "path %d, dashArray %p\n", path, dashArray );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glGetPathDashArrayNV, &args ))) WARN( "glGetPathDashArrayNV returned %#lx\n", status );
 }
 
 static GLfloat WINAPI glGetPathLengthNV( GLuint path, GLsizei startSegment, GLsizei numSegments )
 {
-    struct glGetPathLengthNV_params args = { .teb = NtCurrentTeb(), .path = path, .startSegment = startSegment, .numSegments = numSegments };
+    struct glGetPathLengthNV_params args = { .teb = NtCurrentTeb(), .startSegment = startSegment, .numSegments = numSegments };
     NTSTATUS status;
     TRACE( "path %d, startSegment %d, numSegments %d\n", path, startSegment, numSegments );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return args.ret;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glGetPathLengthNV, &args ))) WARN( "glGetPathLengthNV returned %#lx\n", status );
     return args.ret;
 }
 
 static void WINAPI glGetPathMetricRangeNV( GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics )
 {
-    struct glGetPathMetricRangeNV_params args = { .teb = NtCurrentTeb(), .metricQueryMask = metricQueryMask, .firstPathName = firstPathName, .numPaths = numPaths, .stride = stride, .metrics = metrics };
+    struct glGetPathMetricRangeNV_params args = { .teb = NtCurrentTeb(), .metricQueryMask = metricQueryMask, .numPaths = numPaths, .stride = stride, .metrics = metrics };
     NTSTATUS status;
     TRACE( "metricQueryMask %d, firstPathName %d, numPaths %d, stride %d, metrics %p\n", metricQueryMask, firstPathName, numPaths, stride, metrics );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &firstPathName, TRUE )) return;
+    args.firstPathName = *map_context_objects( OBJ_TYPE_PATH, 1, &firstPathName );
     if ((status = UNIX_CALL( glGetPathMetricRangeNV, &args ))) WARN( "glGetPathMetricRangeNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetPathMetricsNV( GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics )
 {
-    struct glGetPathMetricsNV_params args = { .teb = NtCurrentTeb(), .metricQueryMask = metricQueryMask, .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .stride = stride, .metrics = metrics };
+    struct glGetPathMetricsNV_params args = { .teb = NtCurrentTeb(), .metricQueryMask = metricQueryMask, .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .stride = stride, .metrics = metrics };
     NTSTATUS status;
     TRACE( "metricQueryMask %d, numPaths %d, pathNameType %d, paths %p, pathBase %d, stride %d, metrics %p\n", metricQueryMask, numPaths, pathNameType, paths, pathBase, stride, metrics );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glGetPathMetricsNV, &args ))) WARN( "glGetPathMetricsNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetPathParameterfvNV( GLuint path, GLenum pname, GLfloat *value )
 {
-    struct glGetPathParameterfvNV_params args = { .teb = NtCurrentTeb(), .path = path, .pname = pname, .value = value };
+    struct glGetPathParameterfvNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .value = value };
     NTSTATUS status;
     TRACE( "path %d, pname %d, value %p\n", path, pname, value );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glGetPathParameterfvNV, &args ))) WARN( "glGetPathParameterfvNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetPathParameterivNV( GLuint path, GLenum pname, GLint *value )
 {
-    struct glGetPathParameterivNV_params args = { .teb = NtCurrentTeb(), .path = path, .pname = pname, .value = value };
+    struct glGetPathParameterivNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .value = value };
     NTSTATUS status;
     TRACE( "path %d, pname %d, value %p\n", path, pname, value );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glGetPathParameterivNV, &args ))) WARN( "glGetPathParameterivNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetPathSpacingNV( GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing )
 {
-    struct glGetPathSpacingNV_params args = { .teb = NtCurrentTeb(), .pathListMode = pathListMode, .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .advanceScale = advanceScale, .kerningScale = kerningScale, .transformType = transformType, .returnedSpacing = returnedSpacing };
+    struct glGetPathSpacingNV_params args = { .teb = NtCurrentTeb(), .pathListMode = pathListMode, .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .advanceScale = advanceScale, .kerningScale = kerningScale, .transformType = transformType, .returnedSpacing = returnedSpacing };
     NTSTATUS status;
     TRACE( "pathListMode %d, numPaths %d, pathNameType %d, paths %p, pathBase %d, advanceScale %f, kerningScale %f, transformType %d, returnedSpacing %p\n", pathListMode, numPaths, pathNameType, paths, pathBase, advanceScale, kerningScale, transformType, returnedSpacing );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glGetPathSpacingNV, &args ))) WARN( "glGetPathSpacingNV returned %#lx\n", status );
 }
 
@@ -9530,24 +9907,30 @@ static void WINAPI glGetPointerIndexedvEXT( GLenum target, GLuint index, void **
 {
     struct glGetPointerIndexedvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetPointerIndexedvEXT, &args ))) WARN( "glGetPointerIndexedvEXT returned %#lx\n", status );
+    else if (get_integer( target, index, (UINT_PTR)*data, &integer )) *data = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetPointeri_vEXT( GLenum pname, GLuint index, void **params )
 {
     struct glGetPointeri_vEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .index = index, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, index %d, params %p\n", pname, index, params );
     if ((status = UNIX_CALL( glGetPointeri_vEXT, &args ))) WARN( "glGetPointeri_vEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, (UINT_PTR)*params, &integer )) *params = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetPointervEXT( GLenum pname, void **params )
 {
     struct glGetPointervEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, params %p\n", pname, params );
     if ((status = UNIX_CALL( glGetPointervEXT, &args ))) WARN( "glGetPointervEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, (UINT_PTR)*params, &integer )) *params = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetProgramBinary( GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary )
@@ -9640,17 +10023,19 @@ static void WINAPI glGetProgramLocalParameterfvARB( GLenum target, GLuint index,
 
 static void WINAPI glGetProgramNamedParameterdvNV( GLuint id, GLsizei len, const GLubyte *name, GLdouble *params )
 {
-    struct glGetProgramNamedParameterdvNV_params args = { .teb = NtCurrentTeb(), .id = id, .len = len, .name = name, .params = params };
+    struct glGetProgramNamedParameterdvNV_params args = { .teb = NtCurrentTeb(), .len = len, .name = name, .params = params };
     NTSTATUS status;
     TRACE( "id %d, len %d, name %p, params %p\n", id, len, name, params );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glGetProgramNamedParameterdvNV, &args ))) WARN( "glGetProgramNamedParameterdvNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetProgramNamedParameterfvNV( GLuint id, GLsizei len, const GLubyte *name, GLfloat *params )
 {
-    struct glGetProgramNamedParameterfvNV_params args = { .teb = NtCurrentTeb(), .id = id, .len = len, .name = name, .params = params };
+    struct glGetProgramNamedParameterfvNV_params args = { .teb = NtCurrentTeb(), .len = len, .name = name, .params = params };
     NTSTATUS status;
     TRACE( "id %d, len %d, name %p, params %p\n", id, len, name, params );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glGetProgramNamedParameterfvNV, &args ))) WARN( "glGetProgramNamedParameterfvNV returned %#lx\n", status );
 }
 
@@ -9682,8 +10067,10 @@ static void WINAPI glGetProgramPipelineiv( GLuint pipeline, GLenum pname, GLint
 {
     struct glGetProgramPipelineiv_params args = { .teb = NtCurrentTeb(), .pipeline = pipeline, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "pipeline %d, pname %d, params %p\n", pipeline, pname, params );
     if ((status = UNIX_CALL( glGetProgramPipelineiv, &args ))) WARN( "glGetProgramPipelineiv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static GLuint WINAPI glGetProgramResourceIndex( GLuint program, GLenum programInterface, const GLchar *name )
@@ -9755,9 +10142,10 @@ static void WINAPI glGetProgramStringARB( GLenum target, GLenum pname, void *str
 
 static void WINAPI glGetProgramStringNV( GLuint id, GLenum pname, GLubyte *program )
 {
-    struct glGetProgramStringNV_params args = { .teb = NtCurrentTeb(), .id = id, .pname = pname, .program = program };
+    struct glGetProgramStringNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .program = program };
     NTSTATUS status;
     TRACE( "id %d, pname %d, program %p\n", id, pname, program );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glGetProgramStringNV, &args ))) WARN( "glGetProgramStringNV returned %#lx\n", status );
 }
 
@@ -9781,47 +10169,54 @@ static void WINAPI glGetProgramivARB( GLenum target, GLenum pname, GLint *params
 {
     struct glGetProgramivARB_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, pname %d, params %p\n", target, pname, params );
     if ((status = UNIX_CALL( glGetProgramivARB, &args ))) WARN( "glGetProgramivARB returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetProgramivNV( GLuint id, GLenum pname, GLint *params )
 {
-    struct glGetProgramivNV_params args = { .teb = NtCurrentTeb(), .id = id, .pname = pname, .params = params };
+    struct glGetProgramivNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "id %d, pname %d, params %p\n", id, pname, params );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glGetProgramivNV, &args ))) WARN( "glGetProgramivNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetQueryBufferObjecti64v( GLuint id, GLuint buffer, GLenum pname, GLintptr offset )
 {
-    struct glGetQueryBufferObjecti64v_params args = { .teb = NtCurrentTeb(), .id = id, .buffer = buffer, .pname = pname, .offset = offset };
+    struct glGetQueryBufferObjecti64v_params args = { .teb = NtCurrentTeb(), .id = id, .pname = pname, .offset = offset };
     NTSTATUS status;
     TRACE( "id %d, buffer %d, pname %d, offset %Id\n", id, buffer, pname, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetQueryBufferObjecti64v, &args ))) WARN( "glGetQueryBufferObjecti64v returned %#lx\n", status );
 }
 
 static void WINAPI glGetQueryBufferObjectiv( GLuint id, GLuint buffer, GLenum pname, GLintptr offset )
 {
-    struct glGetQueryBufferObjectiv_params args = { .teb = NtCurrentTeb(), .id = id, .buffer = buffer, .pname = pname, .offset = offset };
+    struct glGetQueryBufferObjectiv_params args = { .teb = NtCurrentTeb(), .id = id, .pname = pname, .offset = offset };
     NTSTATUS status;
     TRACE( "id %d, buffer %d, pname %d, offset %Id\n", id, buffer, pname, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetQueryBufferObjectiv, &args ))) WARN( "glGetQueryBufferObjectiv returned %#lx\n", status );
 }
 
 static void WINAPI glGetQueryBufferObjectui64v( GLuint id, GLuint buffer, GLenum pname, GLintptr offset )
 {
-    struct glGetQueryBufferObjectui64v_params args = { .teb = NtCurrentTeb(), .id = id, .buffer = buffer, .pname = pname, .offset = offset };
+    struct glGetQueryBufferObjectui64v_params args = { .teb = NtCurrentTeb(), .id = id, .pname = pname, .offset = offset };
     NTSTATUS status;
     TRACE( "id %d, buffer %d, pname %d, offset %Id\n", id, buffer, pname, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetQueryBufferObjectui64v, &args ))) WARN( "glGetQueryBufferObjectui64v returned %#lx\n", status );
 }
 
 static void WINAPI glGetQueryBufferObjectuiv( GLuint id, GLuint buffer, GLenum pname, GLintptr offset )
 {
-    struct glGetQueryBufferObjectuiv_params args = { .teb = NtCurrentTeb(), .id = id, .buffer = buffer, .pname = pname, .offset = offset };
+    struct glGetQueryBufferObjectuiv_params args = { .teb = NtCurrentTeb(), .id = id, .pname = pname, .offset = offset };
     NTSTATUS status;
     TRACE( "id %d, buffer %d, pname %d, offset %Id\n", id, buffer, pname, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glGetQueryBufferObjectuiv, &args ))) WARN( "glGetQueryBufferObjectuiv returned %#lx\n", status );
 }
 
@@ -9931,49 +10326,55 @@ static void WINAPI glGetRenderbufferParameterivEXT( GLenum target, GLenum pname,
 
 static void WINAPI glGetSamplerParameterIiv( GLuint sampler, GLenum pname, GLint *params )
 {
-    struct glGetSamplerParameterIiv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .params = params };
+    struct glGetSamplerParameterIiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, params %p\n", sampler, pname, params );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glGetSamplerParameterIiv, &args ))) WARN( "glGetSamplerParameterIiv returned %#lx\n", status );
 }
 
 static void WINAPI glGetSamplerParameterIuiv( GLuint sampler, GLenum pname, GLuint *params )
 {
-    struct glGetSamplerParameterIuiv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .params = params };
+    struct glGetSamplerParameterIuiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, params %p\n", sampler, pname, params );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glGetSamplerParameterIuiv, &args ))) WARN( "glGetSamplerParameterIuiv returned %#lx\n", status );
 }
 
 static void WINAPI glGetSamplerParameterfv( GLuint sampler, GLenum pname, GLfloat *params )
 {
-    struct glGetSamplerParameterfv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .params = params };
+    struct glGetSamplerParameterfv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, params %p\n", sampler, pname, params );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glGetSamplerParameterfv, &args ))) WARN( "glGetSamplerParameterfv returned %#lx\n", status );
 }
 
 static void WINAPI glGetSamplerParameteriv( GLuint sampler, GLenum pname, GLint *params )
 {
-    struct glGetSamplerParameteriv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .params = params };
+    struct glGetSamplerParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, params %p\n", sampler, pname, params );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glGetSamplerParameteriv, &args ))) WARN( "glGetSamplerParameteriv returned %#lx\n", status );
 }
 
 static void WINAPI glGetSemaphoreParameterivNV( GLuint semaphore, GLenum pname, GLint *params )
 {
-    struct glGetSemaphoreParameterivNV_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .pname = pname, .params = params };
+    struct glGetSemaphoreParameterivNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "semaphore %d, pname %d, params %p\n", semaphore, pname, params );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glGetSemaphoreParameterivNV, &args ))) WARN( "glGetSemaphoreParameterivNV returned %#lx\n", status );
 }
 
 static void WINAPI glGetSemaphoreParameterui64vEXT( GLuint semaphore, GLenum pname, GLuint64 *params )
 {
-    struct glGetSemaphoreParameterui64vEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .pname = pname, .params = params };
+    struct glGetSemaphoreParameterui64vEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "semaphore %d, pname %d, params %p\n", semaphore, pname, params );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glGetSemaphoreParameterui64vEXT, &args ))) WARN( "glGetSemaphoreParameterui64vEXT returned %#lx\n", status );
 }
 
@@ -10145,8 +10546,10 @@ static void WINAPI glGetTexLevelParameterxvOES( GLenum target, GLint level, GLen
 {
     struct glGetTexLevelParameterxvOES_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, level %d, pname %d, params %p\n", target, level, pname, params );
     if ((status = UNIX_CALL( glGetTexLevelParameterxvOES, &args ))) WARN( "glGetTexLevelParameterxvOES returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetTexParameterIiv( GLenum target, GLenum pname, GLint *params )
@@ -10207,157 +10610,193 @@ static void WINAPI glGetTexParameterxvOES( GLenum target, GLenum pname, GLfixed
 
 static GLuint64 WINAPI glGetTextureHandleARB( GLuint texture )
 {
-    struct glGetTextureHandleARB_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glGetTextureHandleARB_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureHandleARB, &args ))) WARN( "glGetTextureHandleARB returned %#lx\n", status );
     return args.ret;
 }
 
 static GLuint64 WINAPI glGetTextureHandleNV( GLuint texture )
 {
-    struct glGetTextureHandleNV_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glGetTextureHandleNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureHandleNV, &args ))) WARN( "glGetTextureHandleNV returned %#lx\n", status );
     return args.ret;
 }
 
 static void WINAPI glGetTextureImage( GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels )
 {
-    struct glGetTextureImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .format = format, .type = type, .bufSize = bufSize, .pixels = pixels };
+    struct glGetTextureImage_params args = { .teb = NtCurrentTeb(), .level = level, .format = format, .type = type, .bufSize = bufSize, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, format %d, type %d, bufSize %d, pixels %p\n", texture, level, format, type, bufSize, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureImage, &args ))) WARN( "glGetTextureImage returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureImageEXT( GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels )
 {
-    struct glGetTextureImageEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .format = format, .type = type, .pixels = pixels };
+    struct glGetTextureImageEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, format %d, type %d, pixels %p\n", texture, target, level, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureImageEXT, &args ))) WARN( "glGetTextureImageEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureLevelParameterfv( GLuint texture, GLint level, GLenum pname, GLfloat *params )
 {
-    struct glGetTextureLevelParameterfv_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .pname = pname, .params = params };
+    struct glGetTextureLevelParameterfv_params args = { .teb = NtCurrentTeb(), .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "texture %d, level %d, pname %d, params %p\n", texture, level, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureLevelParameterfv, &args ))) WARN( "glGetTextureLevelParameterfv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetTextureLevelParameterfvEXT( GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params )
 {
-    struct glGetTextureLevelParameterfvEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .pname = pname, .params = params };
+    struct glGetTextureLevelParameterfvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "texture %d, target %d, level %d, pname %d, params %p\n", texture, target, level, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureLevelParameterfvEXT, &args ))) WARN( "glGetTextureLevelParameterfvEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetTextureLevelParameteriv( GLuint texture, GLint level, GLenum pname, GLint *params )
 {
-    struct glGetTextureLevelParameteriv_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .pname = pname, .params = params };
+    struct glGetTextureLevelParameteriv_params args = { .teb = NtCurrentTeb(), .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "texture %d, level %d, pname %d, params %p\n", texture, level, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureLevelParameteriv, &args ))) WARN( "glGetTextureLevelParameteriv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetTextureLevelParameterivEXT( GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params )
 {
-    struct glGetTextureLevelParameterivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .pname = pname, .params = params };
+    struct glGetTextureLevelParameterivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "texture %d, target %d, level %d, pname %d, params %p\n", texture, target, level, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureLevelParameterivEXT, &args ))) WARN( "glGetTextureLevelParameterivEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetTextureParameterIiv( GLuint texture, GLenum pname, GLint *params )
 {
-    struct glGetTextureParameterIiv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .params = params };
+    struct glGetTextureParameterIiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, params %p\n", texture, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterIiv, &args ))) WARN( "glGetTextureParameterIiv returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameterIivEXT( GLuint texture, GLenum target, GLenum pname, GLint *params )
 {
-    struct glGetTextureParameterIivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glGetTextureParameterIivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterIivEXT, &args ))) WARN( "glGetTextureParameterIivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameterIuiv( GLuint texture, GLenum pname, GLuint *params )
 {
-    struct glGetTextureParameterIuiv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .params = params };
+    struct glGetTextureParameterIuiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, params %p\n", texture, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterIuiv, &args ))) WARN( "glGetTextureParameterIuiv returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameterIuivEXT( GLuint texture, GLenum target, GLenum pname, GLuint *params )
 {
-    struct glGetTextureParameterIuivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glGetTextureParameterIuivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterIuivEXT, &args ))) WARN( "glGetTextureParameterIuivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameterfv( GLuint texture, GLenum pname, GLfloat *params )
 {
-    struct glGetTextureParameterfv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .params = params };
+    struct glGetTextureParameterfv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, params %p\n", texture, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterfv, &args ))) WARN( "glGetTextureParameterfv returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameterfvEXT( GLuint texture, GLenum target, GLenum pname, GLfloat *params )
 {
-    struct glGetTextureParameterfvEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glGetTextureParameterfvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterfvEXT, &args ))) WARN( "glGetTextureParameterfvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameteriv( GLuint texture, GLenum pname, GLint *params )
 {
-    struct glGetTextureParameteriv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .params = params };
+    struct glGetTextureParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, params %p\n", texture, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameteriv, &args ))) WARN( "glGetTextureParameteriv returned %#lx\n", status );
 }
 
 static void WINAPI glGetTextureParameterivEXT( GLuint texture, GLenum target, GLenum pname, GLint *params )
 {
-    struct glGetTextureParameterivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glGetTextureParameterivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureParameterivEXT, &args ))) WARN( "glGetTextureParameterivEXT returned %#lx\n", status );
 }
 
 static GLuint64 WINAPI glGetTextureSamplerHandleARB( GLuint texture, GLuint sampler )
 {
-    struct glGetTextureSamplerHandleARB_params args = { .teb = NtCurrentTeb(), .texture = texture, .sampler = sampler };
+    struct glGetTextureSamplerHandleARB_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d, sampler %d\n", texture, sampler );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glGetTextureSamplerHandleARB, &args ))) WARN( "glGetTextureSamplerHandleARB returned %#lx\n", status );
     return args.ret;
 }
 
 static GLuint64 WINAPI glGetTextureSamplerHandleNV( GLuint texture, GLuint sampler )
 {
-    struct glGetTextureSamplerHandleNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .sampler = sampler };
+    struct glGetTextureSamplerHandleNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d, sampler %d\n", texture, sampler );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glGetTextureSamplerHandleNV, &args ))) WARN( "glGetTextureSamplerHandleNV returned %#lx\n", status );
     return args.ret;
 }
 
 static void WINAPI glGetTextureSubImage( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels )
 {
-    struct glGetTextureSubImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .bufSize = bufSize, .pixels = pixels };
+    struct glGetTextureSubImage_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .bufSize = bufSize, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, format %d, type %d, bufSize %d, pixels %p\n", texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glGetTextureSubImage, &args ))) WARN( "glGetTextureSubImage returned %#lx\n", status );
 }
 
@@ -10397,24 +10836,30 @@ static void WINAPI glGetTransformFeedbacki64_v( GLuint xfb, GLenum pname, GLuint
 {
     struct glGetTransformFeedbacki64_v_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .pname = pname, .index = index, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "xfb %d, pname %d, index %d, param %p\n", xfb, pname, index, param );
     if ((status = UNIX_CALL( glGetTransformFeedbacki64_v, &args ))) WARN( "glGetTransformFeedbacki64_v returned %#lx\n", status );
+    else if (get_integer( pname, index, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetTransformFeedbacki_v( GLuint xfb, GLenum pname, GLuint index, GLint *param )
 {
     struct glGetTransformFeedbacki_v_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .pname = pname, .index = index, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "xfb %d, pname %d, index %d, param %p\n", xfb, pname, index, param );
     if ((status = UNIX_CALL( glGetTransformFeedbacki_v, &args ))) WARN( "glGetTransformFeedbacki_v returned %#lx\n", status );
+    else if (get_integer( pname, index, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetTransformFeedbackiv( GLuint xfb, GLenum pname, GLint *param )
 {
     struct glGetTransformFeedbackiv_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "xfb %d, pname %d, param %p\n", xfb, pname, param );
     if ((status = UNIX_CALL( glGetTransformFeedbackiv, &args ))) WARN( "glGetTransformFeedbackiv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *param, &integer )) *param = integer;
 }
 
 static GLuint WINAPI glGetUniformBlockIndex( GLuint program, const GLchar *uniformBlockName )
@@ -10570,16 +11015,20 @@ static void WINAPI glGetUnsignedBytei_vEXT( GLenum target, GLuint index, GLubyte
 {
     struct glGetUnsignedBytei_vEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "target %d, index %d, data %p\n", target, index, data );
     if ((status = UNIX_CALL( glGetUnsignedBytei_vEXT, &args ))) WARN( "glGetUnsignedBytei_vEXT returned %#lx\n", status );
+    else if (get_integer( target, index, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetUnsignedBytevEXT( GLenum pname, GLubyte *data )
 {
     struct glGetUnsignedBytevEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .data = data };
     NTSTATUS status;
+    int integer;
     TRACE( "pname %d, data %p\n", pname, data );
     if ((status = UNIX_CALL( glGetUnsignedBytevEXT, &args ))) WARN( "glGetUnsignedBytevEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, *data, &integer )) *data = integer;
 }
 
 static void WINAPI glGetVariantArrayObjectfvATI( GLuint id, GLenum pname, GLfloat *params )
@@ -10643,240 +11092,300 @@ static void WINAPI glGetVertexArrayIndexed64iv( GLuint vaobj, GLuint index, GLen
 {
     struct glGetVertexArrayIndexed64iv_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, index %d, pname %d, param %p\n", vaobj, index, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayIndexed64iv, &args ))) WARN( "glGetVertexArrayIndexed64iv returned %#lx\n", status );
+    else if (get_integer( pname, index, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetVertexArrayIndexediv( GLuint vaobj, GLuint index, GLenum pname, GLint *param )
 {
     struct glGetVertexArrayIndexediv_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, index %d, pname %d, param %p\n", vaobj, index, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayIndexediv, &args ))) WARN( "glGetVertexArrayIndexediv returned %#lx\n", status );
+    else if (get_integer( pname, index, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetVertexArrayIntegeri_vEXT( GLuint vaobj, GLuint index, GLenum pname, GLint *param )
 {
     struct glGetVertexArrayIntegeri_vEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, index %d, pname %d, param %p\n", vaobj, index, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayIntegeri_vEXT, &args ))) WARN( "glGetVertexArrayIntegeri_vEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetVertexArrayIntegervEXT( GLuint vaobj, GLenum pname, GLint *param )
 {
     struct glGetVertexArrayIntegervEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, pname %d, param %p\n", vaobj, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayIntegervEXT, &args ))) WARN( "glGetVertexArrayIntegervEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetVertexArrayPointeri_vEXT( GLuint vaobj, GLuint index, GLenum pname, void **param )
 {
     struct glGetVertexArrayPointeri_vEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, index %d, pname %d, param %p\n", vaobj, index, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayPointeri_vEXT, &args ))) WARN( "glGetVertexArrayPointeri_vEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, (UINT_PTR)*param, &integer )) *param = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetVertexArrayPointervEXT( GLuint vaobj, GLenum pname, void **param )
 {
     struct glGetVertexArrayPointervEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, pname %d, param %p\n", vaobj, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayPointervEXT, &args ))) WARN( "glGetVertexArrayPointervEXT returned %#lx\n", status );
+    else if (get_integer( pname, 0, (UINT_PTR)*param, &integer )) *param = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetVertexArrayiv( GLuint vaobj, GLenum pname, GLint *param )
 {
     struct glGetVertexArrayiv_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .pname = pname, .param = param };
     NTSTATUS status;
+    int integer;
     TRACE( "vaobj %d, pname %d, param %p\n", vaobj, pname, param );
     if ((status = UNIX_CALL( glGetVertexArrayiv, &args ))) WARN( "glGetVertexArrayiv returned %#lx\n", status );
+    else if (get_integer( pname, 0, *param, &integer )) *param = integer;
 }
 
 static void WINAPI glGetVertexAttribArrayObjectfvATI( GLuint index, GLenum pname, GLfloat *params )
 {
     struct glGetVertexAttribArrayObjectfvATI_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribArrayObjectfvATI, &args ))) WARN( "glGetVertexAttribArrayObjectfvATI returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribArrayObjectivATI( GLuint index, GLenum pname, GLint *params )
 {
     struct glGetVertexAttribArrayObjectivATI_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribArrayObjectivATI, &args ))) WARN( "glGetVertexAttribArrayObjectivATI returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribIiv( GLuint index, GLenum pname, GLint *params )
 {
     struct glGetVertexAttribIiv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribIiv, &args ))) WARN( "glGetVertexAttribIiv returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribIivEXT( GLuint index, GLenum pname, GLint *params )
 {
     struct glGetVertexAttribIivEXT_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribIivEXT, &args ))) WARN( "glGetVertexAttribIivEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribIuiv( GLuint index, GLenum pname, GLuint *params )
 {
     struct glGetVertexAttribIuiv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribIuiv, &args ))) WARN( "glGetVertexAttribIuiv returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribIuivEXT( GLuint index, GLenum pname, GLuint *params )
 {
     struct glGetVertexAttribIuivEXT_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribIuivEXT, &args ))) WARN( "glGetVertexAttribIuivEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribLdv( GLuint index, GLenum pname, GLdouble *params )
 {
     struct glGetVertexAttribLdv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribLdv, &args ))) WARN( "glGetVertexAttribLdv returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribLdvEXT( GLuint index, GLenum pname, GLdouble *params )
 {
     struct glGetVertexAttribLdvEXT_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribLdvEXT, &args ))) WARN( "glGetVertexAttribLdvEXT returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribLi64vNV( GLuint index, GLenum pname, GLint64EXT *params )
 {
     struct glGetVertexAttribLi64vNV_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribLi64vNV, &args ))) WARN( "glGetVertexAttribLi64vNV returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribLui64vARB( GLuint index, GLenum pname, GLuint64EXT *params )
 {
     struct glGetVertexAttribLui64vARB_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribLui64vARB, &args ))) WARN( "glGetVertexAttribLui64vARB returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribLui64vNV( GLuint index, GLenum pname, GLuint64EXT *params )
 {
     struct glGetVertexAttribLui64vNV_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribLui64vNV, &args ))) WARN( "glGetVertexAttribLui64vNV returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribPointerv( GLuint index, GLenum pname, void **pointer )
 {
     struct glGetVertexAttribPointerv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .pointer = pointer };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, pointer %p\n", index, pname, pointer );
     if ((status = UNIX_CALL( glGetVertexAttribPointerv, &args ))) WARN( "glGetVertexAttribPointerv returned %#lx\n", status );
+    else if (get_integer( pname, index, (UINT_PTR)*pointer, &integer )) *pointer = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetVertexAttribPointervARB( GLuint index, GLenum pname, void **pointer )
 {
     struct glGetVertexAttribPointervARB_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .pointer = pointer };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, pointer %p\n", index, pname, pointer );
     if ((status = UNIX_CALL( glGetVertexAttribPointervARB, &args ))) WARN( "glGetVertexAttribPointervARB returned %#lx\n", status );
+    else if (get_integer( pname, index, (UINT_PTR)*pointer, &integer )) *pointer = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetVertexAttribPointervNV( GLuint index, GLenum pname, void **pointer )
 {
     struct glGetVertexAttribPointervNV_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .pointer = pointer };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, pointer %p\n", index, pname, pointer );
     if ((status = UNIX_CALL( glGetVertexAttribPointervNV, &args ))) WARN( "glGetVertexAttribPointervNV returned %#lx\n", status );
+    else if (get_integer( pname, index, (UINT_PTR)*pointer, &integer )) *pointer = (void *)(UINT_PTR)integer;
 }
 
 static void WINAPI glGetVertexAttribdv( GLuint index, GLenum pname, GLdouble *params )
 {
     struct glGetVertexAttribdv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribdv, &args ))) WARN( "glGetVertexAttribdv returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribdvARB( GLuint index, GLenum pname, GLdouble *params )
 {
     struct glGetVertexAttribdvARB_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribdvARB, &args ))) WARN( "glGetVertexAttribdvARB returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribdvNV( GLuint index, GLenum pname, GLdouble *params )
 {
     struct glGetVertexAttribdvNV_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribdvNV, &args ))) WARN( "glGetVertexAttribdvNV returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribfv( GLuint index, GLenum pname, GLfloat *params )
 {
     struct glGetVertexAttribfv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribfv, &args ))) WARN( "glGetVertexAttribfv returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribfvARB( GLuint index, GLenum pname, GLfloat *params )
 {
     struct glGetVertexAttribfvARB_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribfvARB, &args ))) WARN( "glGetVertexAttribfvARB returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribfvNV( GLuint index, GLenum pname, GLfloat *params )
 {
     struct glGetVertexAttribfvNV_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribfvNV, &args ))) WARN( "glGetVertexAttribfvNV returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribiv( GLuint index, GLenum pname, GLint *params )
 {
     struct glGetVertexAttribiv_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribiv, &args ))) WARN( "glGetVertexAttribiv returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribivARB( GLuint index, GLenum pname, GLint *params )
 {
     struct glGetVertexAttribivARB_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribivARB, &args ))) WARN( "glGetVertexAttribivARB returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVertexAttribivNV( GLuint index, GLenum pname, GLint *params )
 {
     struct glGetVertexAttribivNV_params args = { .teb = NtCurrentTeb(), .index = index, .pname = pname, .params = params };
     NTSTATUS status;
+    int integer;
     TRACE( "index %d, pname %d, params %p\n", index, pname, params );
     if ((status = UNIX_CALL( glGetVertexAttribivNV, &args ))) WARN( "glGetVertexAttribivNV returned %#lx\n", status );
+    else if (get_integer( pname, index, *params, &integer )) *params = integer;
 }
 
 static void WINAPI glGetVideoCaptureStreamdvNV( GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params )
@@ -11386,33 +11895,39 @@ static void WINAPI glImageTransformParameterivHP( GLenum target, GLenum pname, c
 
 static void WINAPI glImportMemoryWin32HandleEXT( GLuint memory, GLuint64 size, GLenum handleType, void *handle )
 {
-    struct glImportMemoryWin32HandleEXT_params args = { .teb = NtCurrentTeb(), .memory = memory, .size = size, .handleType = handleType, .handle = handle };
+    struct glImportMemoryWin32HandleEXT_params args = { .teb = NtCurrentTeb(), .size = size, .handleType = handleType, .handle = handle };
     NTSTATUS status;
     TRACE( "memory %d, size %s, handleType %d, handle %p\n", memory, wine_dbgstr_longlong(size), handleType, handle );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glImportMemoryWin32HandleEXT, &args ))) WARN( "glImportMemoryWin32HandleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glImportMemoryWin32NameEXT( GLuint memory, GLuint64 size, GLenum handleType, const void *name )
 {
-    struct glImportMemoryWin32NameEXT_params args = { .teb = NtCurrentTeb(), .memory = memory, .size = size, .handleType = handleType, .name = name };
+    struct glImportMemoryWin32NameEXT_params args = { .teb = NtCurrentTeb(), .size = size, .handleType = handleType, .name = name };
     NTSTATUS status;
     TRACE( "memory %d, size %s, handleType %d, name %p\n", memory, wine_dbgstr_longlong(size), handleType, name );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glImportMemoryWin32NameEXT, &args ))) WARN( "glImportMemoryWin32NameEXT returned %#lx\n", status );
 }
 
 static void WINAPI glImportSemaphoreWin32HandleEXT( GLuint semaphore, GLenum handleType, void *handle )
 {
-    struct glImportSemaphoreWin32HandleEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .handleType = handleType, .handle = handle };
+    struct glImportSemaphoreWin32HandleEXT_params args = { .teb = NtCurrentTeb(), .handleType = handleType, .handle = handle };
     NTSTATUS status;
     TRACE( "semaphore %d, handleType %d, handle %p\n", semaphore, handleType, handle );
+    if (!alloc_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore, TRUE )) return;
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glImportSemaphoreWin32HandleEXT, &args ))) WARN( "glImportSemaphoreWin32HandleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glImportSemaphoreWin32NameEXT( GLuint semaphore, GLenum handleType, const void *name )
 {
-    struct glImportSemaphoreWin32NameEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .handleType = handleType, .name = name };
+    struct glImportSemaphoreWin32NameEXT_params args = { .teb = NtCurrentTeb(), .handleType = handleType, .name = name };
     NTSTATUS status;
     TRACE( "semaphore %d, handleType %d, name %p\n", semaphore, handleType, name );
+    if (!alloc_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore, TRUE )) return;
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glImportSemaphoreWin32NameEXT, &args ))) WARN( "glImportSemaphoreWin32NameEXT returned %#lx\n", status );
 }
 
@@ -11498,25 +12013,33 @@ static void WINAPI glInstrumentsBufferSGIX( GLsizei size, GLint *buffer )
 
 static void WINAPI glInterpolatePathsNV( GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight )
 {
-    struct glInterpolatePathsNV_params args = { .teb = NtCurrentTeb(), .resultPath = resultPath, .pathA = pathA, .pathB = pathB, .weight = weight };
+    struct glInterpolatePathsNV_params args = { .teb = NtCurrentTeb(), .weight = weight };
     NTSTATUS status;
     TRACE( "resultPath %d, pathA %d, pathB %d, weight %f\n", resultPath, pathA, pathB, weight );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &resultPath, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathA, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathB, TRUE )) return;
+    args.resultPath = *map_context_objects( OBJ_TYPE_PATH, 1, &resultPath );
+    args.pathA = *map_context_objects( OBJ_TYPE_PATH, 1, &pathA );
+    args.pathB = *map_context_objects( OBJ_TYPE_PATH, 1, &pathB );
     if ((status = UNIX_CALL( glInterpolatePathsNV, &args ))) WARN( "glInterpolatePathsNV returned %#lx\n", status );
 }
 
 static void WINAPI glInvalidateBufferData( GLuint buffer )
 {
-    struct glInvalidateBufferData_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glInvalidateBufferData_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glInvalidateBufferData, &args ))) WARN( "glInvalidateBufferData returned %#lx\n", status );
 }
 
 static void WINAPI glInvalidateBufferSubData( GLuint buffer, GLintptr offset, GLsizeiptr length )
 {
-    struct glInvalidateBufferSubData_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .length = length };
+    struct glInvalidateBufferSubData_params args = { .teb = NtCurrentTeb(), .offset = offset, .length = length };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, length %Id\n", buffer, offset, length );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glInvalidateBufferSubData, &args ))) WARN( "glInvalidateBufferSubData returned %#lx\n", status );
 }
 
@@ -11530,17 +12053,19 @@ static void WINAPI glInvalidateFramebuffer( GLenum target, GLsizei numAttachment
 
 static void WINAPI glInvalidateNamedFramebufferData( GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments )
 {
-    struct glInvalidateNamedFramebufferData_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .numAttachments = numAttachments, .attachments = attachments };
+    struct glInvalidateNamedFramebufferData_params args = { .teb = NtCurrentTeb(), .numAttachments = numAttachments, .attachments = attachments };
     NTSTATUS status;
     TRACE( "framebuffer %d, numAttachments %d, attachments %p\n", framebuffer, numAttachments, attachments );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glInvalidateNamedFramebufferData, &args ))) WARN( "glInvalidateNamedFramebufferData returned %#lx\n", status );
 }
 
 static void WINAPI glInvalidateNamedFramebufferSubData( GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height )
 {
-    struct glInvalidateNamedFramebufferSubData_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .numAttachments = numAttachments, .attachments = attachments, .x = x, .y = y, .width = width, .height = height };
+    struct glInvalidateNamedFramebufferSubData_params args = { .teb = NtCurrentTeb(), .numAttachments = numAttachments, .attachments = attachments, .x = x, .y = y, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "framebuffer %d, numAttachments %d, attachments %p, x %d, y %d, width %d, height %d\n", framebuffer, numAttachments, attachments, x, y, width, height );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glInvalidateNamedFramebufferSubData, &args ))) WARN( "glInvalidateNamedFramebufferSubData returned %#lx\n", status );
 }
 
@@ -11554,17 +12079,19 @@ static void WINAPI glInvalidateSubFramebuffer( GLenum target, GLsizei numAttachm
 
 static void WINAPI glInvalidateTexImage( GLuint texture, GLint level )
 {
-    struct glInvalidateTexImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level };
+    struct glInvalidateTexImage_params args = { .teb = NtCurrentTeb(), .level = level };
     NTSTATUS status;
     TRACE( "texture %d, level %d\n", texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glInvalidateTexImage, &args ))) WARN( "glInvalidateTexImage returned %#lx\n", status );
 }
 
 static void WINAPI glInvalidateTexSubImage( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth )
 {
-    struct glInvalidateTexSubImage_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth };
+    struct glInvalidateTexSubImage_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d\n", texture, level, xoffset, yoffset, zoffset, width, height, depth );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glInvalidateTexSubImage, &args ))) WARN( "glInvalidateTexSubImage returned %#lx\n", status );
 }
 
@@ -11579,18 +12106,20 @@ static GLboolean WINAPI glIsAsyncMarkerSGIX( GLuint marker )
 
 static GLboolean WINAPI glIsBuffer( GLuint buffer )
 {
-    struct glIsBuffer_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glIsBuffer_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glIsBuffer, &args ))) WARN( "glIsBuffer returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsBufferARB( GLuint buffer )
 {
-    struct glIsBufferARB_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glIsBufferARB_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glIsBufferARB, &args ))) WARN( "glIsBufferARB returned %#lx\n", status );
     return args.ret;
 }
@@ -11651,18 +12180,20 @@ static GLboolean WINAPI glIsFenceNV( GLuint fence )
 
 static GLboolean WINAPI glIsFramebuffer( GLuint framebuffer )
 {
-    struct glIsFramebuffer_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer };
+    struct glIsFramebuffer_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "framebuffer %d\n", framebuffer );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glIsFramebuffer, &args ))) WARN( "glIsFramebuffer returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsFramebufferEXT( GLuint framebuffer )
 {
-    struct glIsFramebufferEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer };
+    struct glIsFramebufferEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "framebuffer %d\n", framebuffer );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glIsFramebufferEXT, &args ))) WARN( "glIsFramebufferEXT returned %#lx\n", status );
     return args.ret;
 }
@@ -11687,9 +12218,10 @@ static GLboolean WINAPI glIsImageHandleResidentNV( GLuint64 handle )
 
 static GLboolean WINAPI glIsMemoryObjectEXT( GLuint memoryObject )
 {
-    struct glIsMemoryObjectEXT_params args = { .teb = NtCurrentTeb(), .memoryObject = memoryObject };
+    struct glIsMemoryObjectEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "memoryObject %d\n", memoryObject );
+    args.memoryObject = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memoryObject );
     if ((status = UNIX_CALL( glIsMemoryObjectEXT, &args ))) WARN( "glIsMemoryObjectEXT returned %#lx\n", status );
     return args.ret;
 }
@@ -11705,9 +12237,10 @@ static GLboolean WINAPI glIsNameAMD( GLenum identifier, GLuint name )
 
 static GLboolean WINAPI glIsNamedBufferResidentNV( GLuint buffer )
 {
-    struct glIsNamedBufferResidentNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glIsNamedBufferResidentNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glIsNamedBufferResidentNV, &args ))) WARN( "glIsNamedBufferResidentNV returned %#lx\n", status );
     return args.ret;
 }
@@ -11723,9 +12256,10 @@ static GLboolean WINAPI glIsNamedStringARB( GLint namelen, const GLchar *name )
 
 static GLboolean WINAPI glIsObjectBufferATI( GLuint buffer )
 {
-    struct glIsObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glIsObjectBufferATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glIsObjectBufferATI, &args ))) WARN( "glIsObjectBufferATI returned %#lx\n", status );
     return args.ret;
 }
@@ -11741,27 +12275,33 @@ static GLboolean WINAPI glIsOcclusionQueryNV( GLuint id )
 
 static GLboolean WINAPI glIsPathNV( GLuint path )
 {
-    struct glIsPathNV_params args = { .teb = NtCurrentTeb(), .path = path };
+    struct glIsPathNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "path %d\n", path );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return args.ret;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glIsPathNV, &args ))) WARN( "glIsPathNV returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsPointInFillPathNV( GLuint path, GLuint mask, GLfloat x, GLfloat y )
 {
-    struct glIsPointInFillPathNV_params args = { .teb = NtCurrentTeb(), .path = path, .mask = mask, .x = x, .y = y };
+    struct glIsPointInFillPathNV_params args = { .teb = NtCurrentTeb(), .mask = mask, .x = x, .y = y };
     NTSTATUS status;
     TRACE( "path %d, mask %d, x %f, y %f\n", path, mask, x, y );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return args.ret;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glIsPointInFillPathNV, &args ))) WARN( "glIsPointInFillPathNV returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsPointInStrokePathNV( GLuint path, GLfloat x, GLfloat y )
 {
-    struct glIsPointInStrokePathNV_params args = { .teb = NtCurrentTeb(), .path = path, .x = x, .y = y };
+    struct glIsPointInStrokePathNV_params args = { .teb = NtCurrentTeb(), .x = x, .y = y };
     NTSTATUS status;
     TRACE( "path %d, x %f, y %f\n", path, x, y );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return args.ret;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glIsPointInStrokePathNV, &args ))) WARN( "glIsPointInStrokePathNV returned %#lx\n", status );
     return args.ret;
 }
@@ -11777,18 +12317,20 @@ static GLboolean WINAPI glIsProgram( GLuint program )
 
 static GLboolean WINAPI glIsProgramARB( GLuint program )
 {
-    struct glIsProgramARB_params args = { .teb = NtCurrentTeb(), .program = program };
+    struct glIsProgramARB_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "program %d\n", program );
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glIsProgramARB, &args ))) WARN( "glIsProgramARB returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsProgramNV( GLuint id )
 {
-    struct glIsProgramNV_params args = { .teb = NtCurrentTeb(), .id = id };
+    struct glIsProgramNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "id %d\n", id );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glIsProgramNV, &args ))) WARN( "glIsProgramNV returned %#lx\n", status );
     return args.ret;
 }
@@ -11822,36 +12364,40 @@ static GLboolean WINAPI glIsQueryARB( GLuint id )
 
 static GLboolean WINAPI glIsRenderbuffer( GLuint renderbuffer )
 {
-    struct glIsRenderbuffer_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer };
+    struct glIsRenderbuffer_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "renderbuffer %d\n", renderbuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glIsRenderbuffer, &args ))) WARN( "glIsRenderbuffer returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsRenderbufferEXT( GLuint renderbuffer )
 {
-    struct glIsRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer };
+    struct glIsRenderbufferEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "renderbuffer %d\n", renderbuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glIsRenderbufferEXT, &args ))) WARN( "glIsRenderbufferEXT returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsSampler( GLuint sampler )
 {
-    struct glIsSampler_params args = { .teb = NtCurrentTeb(), .sampler = sampler };
+    struct glIsSampler_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "sampler %d\n", sampler );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glIsSampler, &args ))) WARN( "glIsSampler returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glIsSemaphoreEXT( GLuint semaphore )
 {
-    struct glIsSemaphoreEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore };
+    struct glIsSemaphoreEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "semaphore %d\n", semaphore );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glIsSemaphoreEXT, &args ))) WARN( "glIsSemaphoreEXT returned %#lx\n", status );
     return args.ret;
 }
@@ -11886,9 +12432,10 @@ static GLboolean WINAPI glIsSync( GLsync sync )
 
 static GLboolean WINAPI glIsTextureEXT( GLuint texture )
 {
-    struct glIsTextureEXT_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glIsTextureEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glIsTextureEXT, &args ))) WARN( "glIsTextureEXT returned %#lx\n", status );
     return args.ret;
 }
@@ -11967,9 +12514,11 @@ static GLboolean WINAPI glIsVertexAttribEnabledAPPLE( GLuint index, GLenum pname
 
 static void WINAPI glLGPUCopyImageSubDataNVX( GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth )
 {
-    struct glLGPUCopyImageSubDataNVX_params args = { .teb = NtCurrentTeb(), .sourceGpu = sourceGpu, .destinationGpuMask = destinationGpuMask, .srcName = srcName, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srxY = srxY, .srcZ = srcZ, .dstName = dstName, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .width = width, .height = height, .depth = depth };
+    struct glLGPUCopyImageSubDataNVX_params args = { .teb = NtCurrentTeb(), .sourceGpu = sourceGpu, .destinationGpuMask = destinationGpuMask, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srxY = srxY, .srcZ = srcZ, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .width = width, .height = height, .depth = depth };
     NTSTATUS status;
     TRACE( "sourceGpu %d, destinationGpuMask %d, srcName %d, srcTarget %d, srcLevel %d, srcX %d, srxY %d, srcZ %d, dstName %d, dstTarget %d, dstLevel %d, dstX %d, dstY %d, dstZ %d, width %d, height %d, depth %d\n", sourceGpu, destinationGpuMask, srcName, srcTarget, srcLevel, srcX, srxY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, width, height, depth );
+    args.srcName = *map_context_objects( srcTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &srcName );
+    args.dstName = *map_context_objects( dstTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &dstName );
     if ((status = UNIX_CALL( glLGPUCopyImageSubDataNVX, &args ))) WARN( "glLGPUCopyImageSubDataNVX returned %#lx\n", status );
 }
 
@@ -11983,9 +12532,10 @@ static void WINAPI glLGPUInterlockNVX(void)
 
 static void WINAPI glLGPUNamedBufferSubDataNVX( GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data )
 {
-    struct glLGPUNamedBufferSubDataNVX_params args = { .teb = NtCurrentTeb(), .gpuMask = gpuMask, .buffer = buffer, .offset = offset, .size = size, .data = data };
+    struct glLGPUNamedBufferSubDataNVX_params args = { .teb = NtCurrentTeb(), .gpuMask = gpuMask, .offset = offset, .size = size, .data = data };
     NTSTATUS status;
     TRACE( "gpuMask %d, buffer %d, offset %Id, size %Id, data %p\n", gpuMask, buffer, offset, size, data );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glLGPUNamedBufferSubDataNVX, &args ))) WARN( "glLGPUNamedBufferSubDataNVX returned %#lx\n", status );
 }
 
@@ -12103,41 +12653,49 @@ static void WINAPI glLinkProgramARB( GLhandleARB programObj )
 
 static void WINAPI glListDrawCommandsStatesClientNV( GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count )
 {
-    struct glListDrawCommandsStatesClientNV_params args = { .teb = NtCurrentTeb(), .list = list, .segment = segment, .indirects = indirects, .sizes = sizes, .states = states, .fbos = fbos, .count = count };
+    GLuint fbos_buf[64], *fbos_tmp;
+    struct glListDrawCommandsStatesClientNV_params args = { .teb = NtCurrentTeb(), .list = list, .segment = segment, .indirects = indirects, .sizes = sizes, .states = states, .count = count };
     NTSTATUS status;
     TRACE( "list %d, segment %d, indirects %p, sizes %p, states %p, fbos %p, count %d\n", list, segment, indirects, sizes, states, fbos, count );
+    fbos_tmp = count > 0 ? memdup_objects( count, fbos, fbos_buf, ARRAY_SIZE(fbos_buf) ) : NULL;
+    args.fbos = count > 0 ? map_context_objects( OBJ_TYPE_FRAMEBUFFER, count, fbos_tmp ) : NULL;
     if ((status = UNIX_CALL( glListDrawCommandsStatesClientNV, &args ))) WARN( "glListDrawCommandsStatesClientNV returned %#lx\n", status );
+    if (fbos_tmp != fbos_buf) free( fbos_tmp );
 }
 
 static void WINAPI glListParameterfSGIX( GLuint list, GLenum pname, GLfloat param )
 {
-    struct glListParameterfSGIX_params args = { .teb = NtCurrentTeb(), .list = list, .pname = pname, .param = param };
+    struct glListParameterfSGIX_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "list %d, pname %d, param %f\n", list, pname, param );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glListParameterfSGIX, &args ))) WARN( "glListParameterfSGIX returned %#lx\n", status );
 }
 
 static void WINAPI glListParameterfvSGIX( GLuint list, GLenum pname, const GLfloat *params )
 {
-    struct glListParameterfvSGIX_params args = { .teb = NtCurrentTeb(), .list = list, .pname = pname, .params = params };
+    struct glListParameterfvSGIX_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "list %d, pname %d, params %p\n", list, pname, params );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glListParameterfvSGIX, &args ))) WARN( "glListParameterfvSGIX returned %#lx\n", status );
 }
 
 static void WINAPI glListParameteriSGIX( GLuint list, GLenum pname, GLint param )
 {
-    struct glListParameteriSGIX_params args = { .teb = NtCurrentTeb(), .list = list, .pname = pname, .param = param };
+    struct glListParameteriSGIX_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "list %d, pname %d, param %d\n", list, pname, param );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glListParameteriSGIX, &args ))) WARN( "glListParameteriSGIX returned %#lx\n", status );
 }
 
 static void WINAPI glListParameterivSGIX( GLuint list, GLenum pname, const GLint *params )
 {
-    struct glListParameterivSGIX_params args = { .teb = NtCurrentTeb(), .list = list, .pname = pname, .params = params };
+    struct glListParameterivSGIX_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "list %d, pname %d, params %p\n", list, pname, params );
+    args.list = *map_context_objects( OBJ_TYPE_DISPLAY_LIST, 1, &list );
     if ((status = UNIX_CALL( glListParameterivSGIX, &args ))) WARN( "glListParameterivSGIX returned %#lx\n", status );
 }
 
@@ -12287,17 +12845,19 @@ static void WINAPI glMakeImageHandleResidentNV( GLuint64 handle, GLenum access )
 
 static void WINAPI glMakeNamedBufferNonResidentNV( GLuint buffer )
 {
-    struct glMakeNamedBufferNonResidentNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glMakeNamedBufferNonResidentNV_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMakeNamedBufferNonResidentNV, &args ))) WARN( "glMakeNamedBufferNonResidentNV returned %#lx\n", status );
 }
 
 static void WINAPI glMakeNamedBufferResidentNV( GLuint buffer, GLenum access )
 {
-    struct glMakeNamedBufferResidentNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .access = access };
+    struct glMakeNamedBufferResidentNV_params args = { .teb = NtCurrentTeb(), .access = access };
     NTSTATUS status;
     TRACE( "buffer %d, access %d\n", buffer, access );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMakeNamedBufferResidentNV, &args ))) WARN( "glMakeNamedBufferResidentNV returned %#lx\n", status );
 }
 
@@ -12402,45 +12962,52 @@ static void WINAPI glMapGrid2xOES( GLint n, GLfixed u1, GLfixed u2, GLfixed v1,
 
 static void * WINAPI glMapNamedBuffer( GLuint buffer, GLenum access )
 {
-    struct glMapNamedBuffer_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .access = access };
+    struct glMapNamedBuffer_params args = { .teb = NtCurrentTeb(), .access = access };
     NTSTATUS status;
     TRACE( "buffer %d, access %d\n", buffer, access );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMapNamedBuffer, &args ))) WARN( "glMapNamedBuffer returned %#lx\n", status );
     return args.ret;
 }
 
 static void * WINAPI glMapNamedBufferEXT( GLuint buffer, GLenum access )
 {
-    struct glMapNamedBufferEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .access = access };
+    struct glMapNamedBufferEXT_params args = { .teb = NtCurrentTeb(), .access = access };
     NTSTATUS status;
     TRACE( "buffer %d, access %d\n", buffer, access );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return args.ret;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMapNamedBufferEXT, &args ))) WARN( "glMapNamedBufferEXT returned %#lx\n", status );
     return args.ret;
 }
 
 static void * WINAPI glMapNamedBufferRange( GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access )
 {
-    struct glMapNamedBufferRange_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .length = length, .access = access };
+    struct glMapNamedBufferRange_params args = { .teb = NtCurrentTeb(), .offset = offset, .length = length, .access = access };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, length %Id, access %d\n", buffer, offset, length, access );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMapNamedBufferRange, &args ))) WARN( "glMapNamedBufferRange returned %#lx\n", status );
     return args.ret;
 }
 
 static void * WINAPI glMapNamedBufferRangeEXT( GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access )
 {
-    struct glMapNamedBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .length = length, .access = access };
+    struct glMapNamedBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .length = length, .access = access };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, length %Id, access %d\n", buffer, offset, length, access );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return args.ret;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMapNamedBufferRangeEXT, &args ))) WARN( "glMapNamedBufferRangeEXT returned %#lx\n", status );
     return args.ret;
 }
 
 static void * WINAPI glMapObjectBufferATI( GLuint buffer )
 {
-    struct glMapObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glMapObjectBufferATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMapObjectBufferATI, &args ))) WARN( "glMapObjectBufferATI returned %#lx\n", status );
     return args.ret;
 }
@@ -12463,9 +13030,10 @@ static void WINAPI glMapParameterivNV( GLenum target, GLenum pname, const GLint
 
 static void * WINAPI glMapTexture2DINTEL( GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout )
 {
-    struct glMapTexture2DINTEL_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .access = access, .stride = stride, .layout = layout };
+    struct glMapTexture2DINTEL_params args = { .teb = NtCurrentTeb(), .level = level, .access = access, .stride = stride, .layout = layout };
     NTSTATUS status;
     TRACE( "texture %d, level %d, access %d, stride %p, layout %p\n", texture, level, access, stride, layout );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glMapTexture2DINTEL, &args ))) WARN( "glMapTexture2DINTEL returned %#lx\n", status );
     return args.ret;
 }
@@ -12808,9 +13376,10 @@ static void WINAPI glMemoryBarrierEXT( GLbitfield barriers )
 
 static void WINAPI glMemoryObjectParameterivEXT( GLuint memoryObject, GLenum pname, const GLint *params )
 {
-    struct glMemoryObjectParameterivEXT_params args = { .teb = NtCurrentTeb(), .memoryObject = memoryObject, .pname = pname, .params = params };
+    struct glMemoryObjectParameterivEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "memoryObject %d, pname %d, params %p\n", memoryObject, pname, params );
+    args.memoryObject = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memoryObject );
     if ((status = UNIX_CALL( glMemoryObjectParameterivEXT, &args ))) WARN( "glMemoryObjectParameterivEXT returned %#lx\n", status );
 }
 
@@ -13104,9 +13673,11 @@ static void WINAPI glMultiModeDrawElementsIBM( const GLenum *mode, const GLsizei
 
 static void WINAPI glMultiTexBufferEXT( GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer )
 {
-    struct glMultiTexBufferEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target, .internalformat = internalformat, .buffer = buffer };
+    struct glMultiTexBufferEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target, .internalformat = internalformat };
     NTSTATUS status;
     TRACE( "texunit %d, target %d, internalformat %d, buffer %d\n", texunit, target, internalformat, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMultiTexBufferEXT, &args ))) WARN( "glMultiTexBufferEXT returned %#lx\n", status );
 }
 
@@ -14312,9 +14883,11 @@ static void WINAPI glMultiTexParameterivEXT( GLenum texunit, GLenum target, GLen
 
 static void WINAPI glMultiTexRenderbufferEXT( GLenum texunit, GLenum target, GLuint renderbuffer )
 {
-    struct glMultiTexRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target, .renderbuffer = renderbuffer };
+    struct glMultiTexRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .texunit = texunit, .target = target };
     NTSTATUS status;
     TRACE( "texunit %d, target %d, renderbuffer %d\n", texunit, target, renderbuffer );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glMultiTexRenderbufferEXT, &args ))) WARN( "glMultiTexRenderbufferEXT returned %#lx\n", status );
 }
 
@@ -14360,33 +14933,39 @@ static void WINAPI glMulticastBlitFramebufferNV( GLuint srcGpu, GLuint dstGpu, G
 
 static void WINAPI glMulticastBufferSubDataNV( GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data )
 {
-    struct glMulticastBufferSubDataNV_params args = { .teb = NtCurrentTeb(), .gpuMask = gpuMask, .buffer = buffer, .offset = offset, .size = size, .data = data };
+    struct glMulticastBufferSubDataNV_params args = { .teb = NtCurrentTeb(), .gpuMask = gpuMask, .offset = offset, .size = size, .data = data };
     NTSTATUS status;
     TRACE( "gpuMask %d, buffer %d, offset %Id, size %Id, data %p\n", gpuMask, buffer, offset, size, data );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glMulticastBufferSubDataNV, &args ))) WARN( "glMulticastBufferSubDataNV returned %#lx\n", status );
 }
 
 static void WINAPI glMulticastCopyBufferSubDataNV( GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size )
 {
-    struct glMulticastCopyBufferSubDataNV_params args = { .teb = NtCurrentTeb(), .readGpu = readGpu, .writeGpuMask = writeGpuMask, .readBuffer = readBuffer, .writeBuffer = writeBuffer, .readOffset = readOffset, .writeOffset = writeOffset, .size = size };
+    struct glMulticastCopyBufferSubDataNV_params args = { .teb = NtCurrentTeb(), .readGpu = readGpu, .writeGpuMask = writeGpuMask, .readOffset = readOffset, .writeOffset = writeOffset, .size = size };
     NTSTATUS status;
     TRACE( "readGpu %d, writeGpuMask %d, readBuffer %d, writeBuffer %d, readOffset %Id, writeOffset %Id, size %Id\n", readGpu, writeGpuMask, readBuffer, writeBuffer, readOffset, writeOffset, size );
+    args.readBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &readBuffer );
+    args.writeBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &writeBuffer );
     if ((status = UNIX_CALL( glMulticastCopyBufferSubDataNV, &args ))) WARN( "glMulticastCopyBufferSubDataNV returned %#lx\n", status );
 }
 
 static void WINAPI glMulticastCopyImageSubDataNV( GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth )
 {
-    struct glMulticastCopyImageSubDataNV_params args = { .teb = NtCurrentTeb(), .srcGpu = srcGpu, .dstGpuMask = dstGpuMask, .srcName = srcName, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstName = dstName, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .srcWidth = srcWidth, .srcHeight = srcHeight, .srcDepth = srcDepth };
+    struct glMulticastCopyImageSubDataNV_params args = { .teb = NtCurrentTeb(), .srcGpu = srcGpu, .dstGpuMask = dstGpuMask, .srcTarget = srcTarget, .srcLevel = srcLevel, .srcX = srcX, .srcY = srcY, .srcZ = srcZ, .dstTarget = dstTarget, .dstLevel = dstLevel, .dstX = dstX, .dstY = dstY, .dstZ = dstZ, .srcWidth = srcWidth, .srcHeight = srcHeight, .srcDepth = srcDepth };
     NTSTATUS status;
     TRACE( "srcGpu %d, dstGpuMask %d, srcName %d, srcTarget %d, srcLevel %d, srcX %d, srcY %d, srcZ %d, dstName %d, dstTarget %d, dstLevel %d, dstX %d, dstY %d, dstZ %d, srcWidth %d, srcHeight %d, srcDepth %d\n", srcGpu, dstGpuMask, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth );
+    args.srcName = *map_context_objects( srcTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &srcName );
+    args.dstName = *map_context_objects( dstTarget == GL_RENDERBUFFER ? OBJ_TYPE_RENDERBUFFER : OBJ_TYPE_TEXTURE, 1, &dstName );
     if ((status = UNIX_CALL( glMulticastCopyImageSubDataNV, &args ))) WARN( "glMulticastCopyImageSubDataNV returned %#lx\n", status );
 }
 
 static void WINAPI glMulticastFramebufferSampleLocationsfvNV( GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v )
 {
-    struct glMulticastFramebufferSampleLocationsfvNV_params args = { .teb = NtCurrentTeb(), .gpu = gpu, .framebuffer = framebuffer, .start = start, .count = count, .v = v };
+    struct glMulticastFramebufferSampleLocationsfvNV_params args = { .teb = NtCurrentTeb(), .gpu = gpu, .start = start, .count = count, .v = v };
     NTSTATUS status;
     TRACE( "gpu %d, framebuffer %d, start %d, count %d, v %p\n", gpu, framebuffer, start, count, v );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glMulticastFramebufferSampleLocationsfvNV, &args ))) WARN( "glMulticastFramebufferSampleLocationsfvNV returned %#lx\n", status );
 }
 
@@ -14456,401 +15035,501 @@ static void WINAPI glMulticastWaitSyncNV( GLuint signalGpu, GLbitfield waitGpuMa
 
 static void WINAPI glNamedBufferAttachMemoryNV( GLuint buffer, GLuint memory, GLuint64 offset )
 {
-    struct glNamedBufferAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .memory = memory, .offset = offset };
+    struct glNamedBufferAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .offset = offset };
     NTSTATUS status;
     TRACE( "buffer %d, memory %d, offset %s\n", buffer, memory, wine_dbgstr_longlong(offset) );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glNamedBufferAttachMemoryNV, &args ))) WARN( "glNamedBufferAttachMemoryNV returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferData( GLuint buffer, GLsizeiptr size, const void *data, GLenum usage )
 {
-    struct glNamedBufferData_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .size = size, .data = data, .usage = usage };
+    struct glNamedBufferData_params args = { .teb = NtCurrentTeb(), .size = size, .data = data, .usage = usage };
     NTSTATUS status;
     TRACE( "buffer %d, size %Id, data %p, usage %d\n", buffer, size, data, usage );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferData, &args ))) WARN( "glNamedBufferData returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferDataEXT( GLuint buffer, GLsizeiptr size, const void *data, GLenum usage )
 {
-    struct glNamedBufferDataEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .size = size, .data = data, .usage = usage };
+    struct glNamedBufferDataEXT_params args = { .teb = NtCurrentTeb(), .size = size, .data = data, .usage = usage };
     NTSTATUS status;
     TRACE( "buffer %d, size %Id, data %p, usage %d\n", buffer, size, data, usage );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferDataEXT, &args ))) WARN( "glNamedBufferDataEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferPageCommitmentARB( GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit )
 {
-    struct glNamedBufferPageCommitmentARB_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .commit = commit };
+    struct glNamedBufferPageCommitmentARB_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .commit = commit };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, commit %d\n", buffer, offset, size, commit );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferPageCommitmentARB, &args ))) WARN( "glNamedBufferPageCommitmentARB returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferPageCommitmentEXT( GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit )
 {
-    struct glNamedBufferPageCommitmentEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .commit = commit };
+    struct glNamedBufferPageCommitmentEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .commit = commit };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, commit %d\n", buffer, offset, size, commit );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferPageCommitmentEXT, &args ))) WARN( "glNamedBufferPageCommitmentEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferPageCommitmentMemNV( GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit )
 {
-    struct glNamedBufferPageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .memory = memory, .memOffset = memOffset, .commit = commit };
+    struct glNamedBufferPageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .memOffset = memOffset, .commit = commit };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, memory %d, memOffset %s, commit %d\n", buffer, offset, size, memory, wine_dbgstr_longlong(memOffset), commit );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glNamedBufferPageCommitmentMemNV, &args ))) WARN( "glNamedBufferPageCommitmentMemNV returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferStorage( GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags )
 {
-    struct glNamedBufferStorage_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .size = size, .data = data, .flags = flags };
+    struct glNamedBufferStorage_params args = { .teb = NtCurrentTeb(), .size = size, .data = data, .flags = flags };
     NTSTATUS status;
     TRACE( "buffer %d, size %Id, data %p, flags %d\n", buffer, size, data, flags );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferStorage, &args ))) WARN( "glNamedBufferStorage returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferStorageEXT( GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags )
 {
-    struct glNamedBufferStorageEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .size = size, .data = data, .flags = flags };
+    struct glNamedBufferStorageEXT_params args = { .teb = NtCurrentTeb(), .size = size, .data = data, .flags = flags };
     NTSTATUS status;
     TRACE( "buffer %d, size %Id, data %p, flags %d\n", buffer, size, data, flags );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferStorageEXT, &args ))) WARN( "glNamedBufferStorageEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferStorageExternalEXT( GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags )
 {
-    struct glNamedBufferStorageExternalEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .clientBuffer = clientBuffer, .flags = flags };
+    struct glNamedBufferStorageExternalEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .clientBuffer = clientBuffer, .flags = flags };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, clientBuffer %p, flags %d\n", buffer, offset, size, clientBuffer, flags );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferStorageExternalEXT, &args ))) WARN( "glNamedBufferStorageExternalEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferStorageMemEXT( GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset )
 {
-    struct glNamedBufferStorageMemEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .size = size, .memory = memory, .offset = offset };
+    struct glNamedBufferStorageMemEXT_params args = { .teb = NtCurrentTeb(), .size = size, .offset = offset };
     NTSTATUS status;
     TRACE( "buffer %d, size %Id, memory %d, offset %s\n", buffer, size, memory, wine_dbgstr_longlong(offset) );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glNamedBufferStorageMemEXT, &args ))) WARN( "glNamedBufferStorageMemEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferSubData( GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data )
 {
-    struct glNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .data = data };
+    struct glNamedBufferSubData_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, data %p\n", buffer, offset, size, data );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferSubData, &args ))) WARN( "glNamedBufferSubData returned %#lx\n", status );
 }
 
 static void WINAPI glNamedBufferSubDataEXT( GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data )
 {
-    struct glNamedBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .data = data };
+    struct glNamedBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .data = data };
     NTSTATUS status;
     TRACE( "buffer %d, offset %Id, size %Id, data %p\n", buffer, offset, size, data );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glNamedBufferSubDataEXT, &args ))) WARN( "glNamedBufferSubDataEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedCopyBufferSubDataEXT( GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size )
 {
-    struct glNamedCopyBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .readBuffer = readBuffer, .writeBuffer = writeBuffer, .readOffset = readOffset, .writeOffset = writeOffset, .size = size };
+    struct glNamedCopyBufferSubDataEXT_params args = { .teb = NtCurrentTeb(), .readOffset = readOffset, .writeOffset = writeOffset, .size = size };
     NTSTATUS status;
     TRACE( "readBuffer %d, writeBuffer %d, readOffset %Id, writeOffset %Id, size %Id\n", readBuffer, writeBuffer, readOffset, writeOffset, size );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &readBuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &writeBuffer, TRUE )) return;
+    args.readBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &readBuffer );
+    args.writeBuffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &writeBuffer );
     if ((status = UNIX_CALL( glNamedCopyBufferSubDataEXT, &args ))) WARN( "glNamedCopyBufferSubDataEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferDrawBuffer( GLuint framebuffer, GLenum buf )
 {
-    struct glNamedFramebufferDrawBuffer_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .buf = buf };
+    struct glNamedFramebufferDrawBuffer_params args = { .teb = NtCurrentTeb(), .buf = buf };
     NTSTATUS status;
     TRACE( "framebuffer %d, buf %d\n", framebuffer, buf );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferDrawBuffer, &args ))) WARN( "glNamedFramebufferDrawBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferDrawBuffers( GLuint framebuffer, GLsizei n, const GLenum *bufs )
 {
-    struct glNamedFramebufferDrawBuffers_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .n = n, .bufs = bufs };
+    struct glNamedFramebufferDrawBuffers_params args = { .teb = NtCurrentTeb(), .n = n, .bufs = bufs };
     NTSTATUS status;
     TRACE( "framebuffer %d, n %d, bufs %p\n", framebuffer, n, bufs );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferDrawBuffers, &args ))) WARN( "glNamedFramebufferDrawBuffers returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferParameteri( GLuint framebuffer, GLenum pname, GLint param )
 {
-    struct glNamedFramebufferParameteri_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .pname = pname, .param = param };
+    struct glNamedFramebufferParameteri_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "framebuffer %d, pname %d, param %d\n", framebuffer, pname, param );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferParameteri, &args ))) WARN( "glNamedFramebufferParameteri returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferParameteriEXT( GLuint framebuffer, GLenum pname, GLint param )
 {
-    struct glNamedFramebufferParameteriEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .pname = pname, .param = param };
+    struct glNamedFramebufferParameteriEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "framebuffer %d, pname %d, param %d\n", framebuffer, pname, param );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferParameteriEXT, &args ))) WARN( "glNamedFramebufferParameteriEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferReadBuffer( GLuint framebuffer, GLenum src )
 {
-    struct glNamedFramebufferReadBuffer_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .src = src };
+    struct glNamedFramebufferReadBuffer_params args = { .teb = NtCurrentTeb(), .src = src };
     NTSTATUS status;
     TRACE( "framebuffer %d, src %d\n", framebuffer, src );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferReadBuffer, &args ))) WARN( "glNamedFramebufferReadBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferRenderbuffer( GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer )
 {
-    struct glNamedFramebufferRenderbuffer_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .renderbuffertarget = renderbuffertarget, .renderbuffer = renderbuffer };
+    struct glNamedFramebufferRenderbuffer_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .renderbuffertarget = renderbuffertarget };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, renderbuffertarget %d, renderbuffer %d\n", framebuffer, attachment, renderbuffertarget, renderbuffer );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedFramebufferRenderbuffer, &args ))) WARN( "glNamedFramebufferRenderbuffer returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferRenderbufferEXT( GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer )
 {
-    struct glNamedFramebufferRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .renderbuffertarget = renderbuffertarget, .renderbuffer = renderbuffer };
+    struct glNamedFramebufferRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .renderbuffertarget = renderbuffertarget };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, renderbuffertarget %d, renderbuffer %d\n", framebuffer, attachment, renderbuffertarget, renderbuffer );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedFramebufferRenderbufferEXT, &args ))) WARN( "glNamedFramebufferRenderbufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferSampleLocationsfvARB( GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v )
 {
-    struct glNamedFramebufferSampleLocationsfvARB_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .start = start, .count = count, .v = v };
+    struct glNamedFramebufferSampleLocationsfvARB_params args = { .teb = NtCurrentTeb(), .start = start, .count = count, .v = v };
     NTSTATUS status;
     TRACE( "framebuffer %d, start %d, count %d, v %p\n", framebuffer, start, count, v );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferSampleLocationsfvARB, &args ))) WARN( "glNamedFramebufferSampleLocationsfvARB returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferSampleLocationsfvNV( GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v )
 {
-    struct glNamedFramebufferSampleLocationsfvNV_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .start = start, .count = count, .v = v };
+    struct glNamedFramebufferSampleLocationsfvNV_params args = { .teb = NtCurrentTeb(), .start = start, .count = count, .v = v };
     NTSTATUS status;
     TRACE( "framebuffer %d, start %d, count %d, v %p\n", framebuffer, start, count, v );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferSampleLocationsfvNV, &args ))) WARN( "glNamedFramebufferSampleLocationsfvNV returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferSamplePositionsfvAMD( GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values )
 {
-    struct glNamedFramebufferSamplePositionsfvAMD_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .numsamples = numsamples, .pixelindex = pixelindex, .values = values };
+    struct glNamedFramebufferSamplePositionsfvAMD_params args = { .teb = NtCurrentTeb(), .numsamples = numsamples, .pixelindex = pixelindex, .values = values };
     NTSTATUS status;
     TRACE( "framebuffer %d, numsamples %d, pixelindex %d, values %p\n", framebuffer, numsamples, pixelindex, values );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
     if ((status = UNIX_CALL( glNamedFramebufferSamplePositionsfvAMD, &args ))) WARN( "glNamedFramebufferSamplePositionsfvAMD returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTexture( GLuint framebuffer, GLenum attachment, GLuint texture, GLint level )
 {
-    struct glNamedFramebufferTexture_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .texture = texture, .level = level };
+    struct glNamedFramebufferTexture_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .level = level };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, texture %d, level %d\n", framebuffer, attachment, texture, level );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTexture, &args ))) WARN( "glNamedFramebufferTexture returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTexture1DEXT( GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
 {
-    struct glNamedFramebufferTexture1DEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level };
+    struct glNamedFramebufferTexture1DEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .textarget = textarget, .level = level };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, textarget %d, texture %d, level %d\n", framebuffer, attachment, textarget, texture, level );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTexture1DEXT, &args ))) WARN( "glNamedFramebufferTexture1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTexture2DEXT( GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
 {
-    struct glNamedFramebufferTexture2DEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level };
+    struct glNamedFramebufferTexture2DEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .textarget = textarget, .level = level };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, textarget %d, texture %d, level %d\n", framebuffer, attachment, textarget, texture, level );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTexture2DEXT, &args ))) WARN( "glNamedFramebufferTexture2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTexture3DEXT( GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset )
 {
-    struct glNamedFramebufferTexture3DEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .textarget = textarget, .texture = texture, .level = level, .zoffset = zoffset };
+    struct glNamedFramebufferTexture3DEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .textarget = textarget, .level = level, .zoffset = zoffset };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, textarget %d, texture %d, level %d, zoffset %d\n", framebuffer, attachment, textarget, texture, level, zoffset );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTexture3DEXT, &args ))) WARN( "glNamedFramebufferTexture3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTextureEXT( GLuint framebuffer, GLenum attachment, GLuint texture, GLint level )
 {
-    struct glNamedFramebufferTextureEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .texture = texture, .level = level };
+    struct glNamedFramebufferTextureEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .level = level };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, texture %d, level %d\n", framebuffer, attachment, texture, level );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTextureEXT, &args ))) WARN( "glNamedFramebufferTextureEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTextureFaceEXT( GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face )
 {
-    struct glNamedFramebufferTextureFaceEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .texture = texture, .level = level, .face = face };
+    struct glNamedFramebufferTextureFaceEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .level = level, .face = face };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, texture %d, level %d, face %d\n", framebuffer, attachment, texture, level, face );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTextureFaceEXT, &args ))) WARN( "glNamedFramebufferTextureFaceEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTextureLayer( GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer )
 {
-    struct glNamedFramebufferTextureLayer_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .texture = texture, .level = level, .layer = layer };
+    struct glNamedFramebufferTextureLayer_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .level = level, .layer = layer };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, texture %d, level %d, layer %d\n", framebuffer, attachment, texture, level, layer );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTextureLayer, &args ))) WARN( "glNamedFramebufferTextureLayer returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTextureLayerEXT( GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer )
 {
-    struct glNamedFramebufferTextureLayerEXT_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .texture = texture, .level = level, .layer = layer };
+    struct glNamedFramebufferTextureLayerEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .level = level, .layer = layer };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, texture %d, level %d, layer %d\n", framebuffer, attachment, texture, level, layer );
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTextureLayerEXT, &args ))) WARN( "glNamedFramebufferTextureLayerEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedFramebufferTextureMultiviewOVR( GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews )
 {
-    struct glNamedFramebufferTextureMultiviewOVR_params args = { .teb = NtCurrentTeb(), .framebuffer = framebuffer, .attachment = attachment, .texture = texture, .level = level, .baseViewIndex = baseViewIndex, .numViews = numViews };
+    struct glNamedFramebufferTextureMultiviewOVR_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .level = level, .baseViewIndex = baseViewIndex, .numViews = numViews };
     NTSTATUS status;
     TRACE( "framebuffer %d, attachment %d, texture %d, level %d, baseViewIndex %d, numViews %d\n", framebuffer, attachment, texture, level, baseViewIndex, numViews );
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &framebuffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glNamedFramebufferTextureMultiviewOVR, &args ))) WARN( "glNamedFramebufferTextureMultiviewOVR returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameter4dEXT( GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w )
 {
-    struct glNamedProgramLocalParameter4dEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
+    struct glNamedProgramLocalParameter4dEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, x %f, y %f, z %f, w %f\n", program, target, index, x, y, z, w );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameter4dEXT, &args ))) WARN( "glNamedProgramLocalParameter4dEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameter4dvEXT( GLuint program, GLenum target, GLuint index, const GLdouble *params )
 {
-    struct glNamedProgramLocalParameter4dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glNamedProgramLocalParameter4dvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameter4dvEXT, &args ))) WARN( "glNamedProgramLocalParameter4dvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameter4fEXT( GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w )
 {
-    struct glNamedProgramLocalParameter4fEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
+    struct glNamedProgramLocalParameter4fEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, x %f, y %f, z %f, w %f\n", program, target, index, x, y, z, w );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameter4fEXT, &args ))) WARN( "glNamedProgramLocalParameter4fEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameter4fvEXT( GLuint program, GLenum target, GLuint index, const GLfloat *params )
 {
-    struct glNamedProgramLocalParameter4fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glNamedProgramLocalParameter4fvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameter4fvEXT, &args ))) WARN( "glNamedProgramLocalParameter4fvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameterI4iEXT( GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w )
 {
-    struct glNamedProgramLocalParameterI4iEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
+    struct glNamedProgramLocalParameterI4iEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, x %d, y %d, z %d, w %d\n", program, target, index, x, y, z, w );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameterI4iEXT, &args ))) WARN( "glNamedProgramLocalParameterI4iEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameterI4ivEXT( GLuint program, GLenum target, GLuint index, const GLint *params )
 {
-    struct glNamedProgramLocalParameterI4ivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glNamedProgramLocalParameterI4ivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameterI4ivEXT, &args ))) WARN( "glNamedProgramLocalParameterI4ivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameterI4uiEXT( GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w )
 {
-    struct glNamedProgramLocalParameterI4uiEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
+    struct glNamedProgramLocalParameterI4uiEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, x %d, y %d, z %d, w %d\n", program, target, index, x, y, z, w );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameterI4uiEXT, &args ))) WARN( "glNamedProgramLocalParameterI4uiEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameterI4uivEXT( GLuint program, GLenum target, GLuint index, const GLuint *params )
 {
-    struct glNamedProgramLocalParameterI4uivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .params = params };
+    struct glNamedProgramLocalParameterI4uivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, params %p\n", program, target, index, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameterI4uivEXT, &args ))) WARN( "glNamedProgramLocalParameterI4uivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParameters4fvEXT( GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params )
 {
-    struct glNamedProgramLocalParameters4fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .count = count, .params = params };
+    struct glNamedProgramLocalParameters4fvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .count = count, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, count %d, params %p\n", program, target, index, count, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParameters4fvEXT, &args ))) WARN( "glNamedProgramLocalParameters4fvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParametersI4ivEXT( GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params )
 {
-    struct glNamedProgramLocalParametersI4ivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .count = count, .params = params };
+    struct glNamedProgramLocalParametersI4ivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .count = count, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, count %d, params %p\n", program, target, index, count, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParametersI4ivEXT, &args ))) WARN( "glNamedProgramLocalParametersI4ivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramLocalParametersI4uivEXT( GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params )
 {
-    struct glNamedProgramLocalParametersI4uivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .index = index, .count = count, .params = params };
+    struct glNamedProgramLocalParametersI4uivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .index = index, .count = count, .params = params };
     NTSTATUS status;
     TRACE( "program %d, target %d, index %d, count %d, params %p\n", program, target, index, count, params );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramLocalParametersI4uivEXT, &args ))) WARN( "glNamedProgramLocalParametersI4uivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedProgramStringEXT( GLuint program, GLenum target, GLenum format, GLsizei len, const void *string )
 {
-    struct glNamedProgramStringEXT_params args = { .teb = NtCurrentTeb(), .program = program, .target = target, .format = format, .len = len, .string = string };
+    struct glNamedProgramStringEXT_params args = { .teb = NtCurrentTeb(), .target = target, .format = format, .len = len, .string = string };
     NTSTATUS status;
     TRACE( "program %d, target %d, format %d, len %d, string %p\n", program, target, format, len, string );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glNamedProgramStringEXT, &args ))) WARN( "glNamedProgramStringEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedRenderbufferStorage( GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glNamedRenderbufferStorage_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .internalformat = internalformat, .width = width, .height = height };
+    struct glNamedRenderbufferStorage_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "renderbuffer %d, internalformat %d, width %d, height %d\n", renderbuffer, internalformat, width, height );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedRenderbufferStorage, &args ))) WARN( "glNamedRenderbufferStorage returned %#lx\n", status );
 }
 
 static void WINAPI glNamedRenderbufferStorageEXT( GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glNamedRenderbufferStorageEXT_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .internalformat = internalformat, .width = width, .height = height };
+    struct glNamedRenderbufferStorageEXT_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "renderbuffer %d, internalformat %d, width %d, height %d\n", renderbuffer, internalformat, width, height );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedRenderbufferStorageEXT, &args ))) WARN( "glNamedRenderbufferStorageEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedRenderbufferStorageMultisample( GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glNamedRenderbufferStorageMultisample_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .samples = samples, .internalformat = internalformat, .width = width, .height = height };
+    struct glNamedRenderbufferStorageMultisample_params args = { .teb = NtCurrentTeb(), .samples = samples, .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "renderbuffer %d, samples %d, internalformat %d, width %d, height %d\n", renderbuffer, samples, internalformat, width, height );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedRenderbufferStorageMultisample, &args ))) WARN( "glNamedRenderbufferStorageMultisample returned %#lx\n", status );
 }
 
 static void WINAPI glNamedRenderbufferStorageMultisampleAdvancedAMD( GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glNamedRenderbufferStorageMultisampleAdvancedAMD_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .samples = samples, .storageSamples = storageSamples, .internalformat = internalformat, .width = width, .height = height };
+    struct glNamedRenderbufferStorageMultisampleAdvancedAMD_params args = { .teb = NtCurrentTeb(), .samples = samples, .storageSamples = storageSamples, .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "renderbuffer %d, samples %d, storageSamples %d, internalformat %d, width %d, height %d\n", renderbuffer, samples, storageSamples, internalformat, width, height );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedRenderbufferStorageMultisampleAdvancedAMD, &args ))) WARN( "glNamedRenderbufferStorageMultisampleAdvancedAMD returned %#lx\n", status );
 }
 
 static void WINAPI glNamedRenderbufferStorageMultisampleCoverageEXT( GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glNamedRenderbufferStorageMultisampleCoverageEXT_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .coverageSamples = coverageSamples, .colorSamples = colorSamples, .internalformat = internalformat, .width = width, .height = height };
+    struct glNamedRenderbufferStorageMultisampleCoverageEXT_params args = { .teb = NtCurrentTeb(), .coverageSamples = coverageSamples, .colorSamples = colorSamples, .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "renderbuffer %d, coverageSamples %d, colorSamples %d, internalformat %d, width %d, height %d\n", renderbuffer, coverageSamples, colorSamples, internalformat, width, height );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedRenderbufferStorageMultisampleCoverageEXT, &args ))) WARN( "glNamedRenderbufferStorageMultisampleCoverageEXT returned %#lx\n", status );
 }
 
 static void WINAPI glNamedRenderbufferStorageMultisampleEXT( GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glNamedRenderbufferStorageMultisampleEXT_params args = { .teb = NtCurrentTeb(), .renderbuffer = renderbuffer, .samples = samples, .internalformat = internalformat, .width = width, .height = height };
+    struct glNamedRenderbufferStorageMultisampleEXT_params args = { .teb = NtCurrentTeb(), .samples = samples, .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "renderbuffer %d, samples %d, internalformat %d, width %d, height %d\n", renderbuffer, samples, internalformat, width, height );
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glNamedRenderbufferStorageMultisampleEXT, &args ))) WARN( "glNamedRenderbufferStorageMultisampleEXT returned %#lx\n", status );
 }
 
@@ -15188,17 +15867,21 @@ static void WINAPI glPathColorGenNV( GLenum color, GLenum genMode, GLenum colorF
 
 static void WINAPI glPathCommandsNV( GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords )
 {
-    struct glPathCommandsNV_params args = { .teb = NtCurrentTeb(), .path = path, .numCommands = numCommands, .commands = commands, .numCoords = numCoords, .coordType = coordType, .coords = coords };
+    struct glPathCommandsNV_params args = { .teb = NtCurrentTeb(), .numCommands = numCommands, .commands = commands, .numCoords = numCoords, .coordType = coordType, .coords = coords };
     NTSTATUS status;
     TRACE( "path %d, numCommands %d, commands %p, numCoords %d, coordType %d, coords %p\n", path, numCommands, commands, numCoords, coordType, coords );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathCommandsNV, &args ))) WARN( "glPathCommandsNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathCoordsNV( GLuint path, GLsizei numCoords, GLenum coordType, const void *coords )
 {
-    struct glPathCoordsNV_params args = { .teb = NtCurrentTeb(), .path = path, .numCoords = numCoords, .coordType = coordType, .coords = coords };
+    struct glPathCoordsNV_params args = { .teb = NtCurrentTeb(), .numCoords = numCoords, .coordType = coordType, .coords = coords };
     NTSTATUS status;
     TRACE( "path %d, numCoords %d, coordType %d, coords %p\n", path, numCoords, coordType, coords );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathCoordsNV, &args ))) WARN( "glPathCoordsNV returned %#lx\n", status );
 }
 
@@ -15212,9 +15895,11 @@ static void WINAPI glPathCoverDepthFuncNV( GLenum func )
 
 static void WINAPI glPathDashArrayNV( GLuint path, GLsizei dashCount, const GLfloat *dashArray )
 {
-    struct glPathDashArrayNV_params args = { .teb = NtCurrentTeb(), .path = path, .dashCount = dashCount, .dashArray = dashArray };
+    struct glPathDashArrayNV_params args = { .teb = NtCurrentTeb(), .dashCount = dashCount, .dashArray = dashArray };
     NTSTATUS status;
     TRACE( "path %d, dashCount %d, dashArray %p\n", path, dashCount, dashArray );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathDashArrayNV, &args ))) WARN( "glPathDashArrayNV returned %#lx\n", status );
 }
 
@@ -15228,9 +15913,11 @@ static void WINAPI glPathFogGenNV( GLenum genMode )
 
 static GLenum WINAPI glPathGlyphIndexArrayNV( GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale )
 {
-    struct glPathGlyphIndexArrayNV_params args = { .teb = NtCurrentTeb(), .firstPathName = firstPathName, .fontTarget = fontTarget, .fontName = fontName, .fontStyle = fontStyle, .firstGlyphIndex = firstGlyphIndex, .numGlyphs = numGlyphs, .pathParameterTemplate = pathParameterTemplate, .emScale = emScale };
+    struct glPathGlyphIndexArrayNV_params args = { .teb = NtCurrentTeb(), .fontTarget = fontTarget, .fontName = fontName, .fontStyle = fontStyle, .firstGlyphIndex = firstGlyphIndex, .numGlyphs = numGlyphs, .pathParameterTemplate = pathParameterTemplate, .emScale = emScale };
     NTSTATUS status;
     TRACE( "firstPathName %d, fontTarget %d, fontName %p, fontStyle %d, firstGlyphIndex %d, numGlyphs %d, pathParameterTemplate %d, emScale %f\n", firstPathName, fontTarget, fontName, fontStyle, firstGlyphIndex, numGlyphs, pathParameterTemplate, emScale );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &firstPathName, TRUE )) return args.ret;
+    args.firstPathName = *map_context_objects( OBJ_TYPE_PATH, 1, &firstPathName );
     if ((status = UNIX_CALL( glPathGlyphIndexArrayNV, &args ))) WARN( "glPathGlyphIndexArrayNV returned %#lx\n", status );
     return args.ret;
 }
@@ -15246,58 +15933,76 @@ static GLenum WINAPI glPathGlyphIndexRangeNV( GLenum fontTarget, const void *fon
 
 static void WINAPI glPathGlyphRangeNV( GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale )
 {
-    struct glPathGlyphRangeNV_params args = { .teb = NtCurrentTeb(), .firstPathName = firstPathName, .fontTarget = fontTarget, .fontName = fontName, .fontStyle = fontStyle, .firstGlyph = firstGlyph, .numGlyphs = numGlyphs, .handleMissingGlyphs = handleMissingGlyphs, .pathParameterTemplate = pathParameterTemplate, .emScale = emScale };
+    struct glPathGlyphRangeNV_params args = { .teb = NtCurrentTeb(), .fontTarget = fontTarget, .fontName = fontName, .fontStyle = fontStyle, .firstGlyph = firstGlyph, .numGlyphs = numGlyphs, .handleMissingGlyphs = handleMissingGlyphs, .emScale = emScale };
     NTSTATUS status;
     TRACE( "firstPathName %d, fontTarget %d, fontName %p, fontStyle %d, firstGlyph %d, numGlyphs %d, handleMissingGlyphs %d, pathParameterTemplate %d, emScale %f\n", firstPathName, fontTarget, fontName, fontStyle, firstGlyph, numGlyphs, handleMissingGlyphs, pathParameterTemplate, emScale );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &firstPathName, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathParameterTemplate, TRUE )) return;
+    args.firstPathName = *map_context_objects( OBJ_TYPE_PATH, 1, &firstPathName );
+    args.pathParameterTemplate = *map_context_objects( OBJ_TYPE_PATH, 1, &pathParameterTemplate );
     if ((status = UNIX_CALL( glPathGlyphRangeNV, &args ))) WARN( "glPathGlyphRangeNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathGlyphsNV( GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale )
 {
-    struct glPathGlyphsNV_params args = { .teb = NtCurrentTeb(), .firstPathName = firstPathName, .fontTarget = fontTarget, .fontName = fontName, .fontStyle = fontStyle, .numGlyphs = numGlyphs, .type = type, .charcodes = charcodes, .handleMissingGlyphs = handleMissingGlyphs, .pathParameterTemplate = pathParameterTemplate, .emScale = emScale };
+    struct glPathGlyphsNV_params args = { .teb = NtCurrentTeb(), .fontTarget = fontTarget, .fontName = fontName, .fontStyle = fontStyle, .numGlyphs = numGlyphs, .type = type, .charcodes = charcodes, .handleMissingGlyphs = handleMissingGlyphs, .emScale = emScale };
     NTSTATUS status;
     TRACE( "firstPathName %d, fontTarget %d, fontName %p, fontStyle %d, numGlyphs %d, type %d, charcodes %p, handleMissingGlyphs %d, pathParameterTemplate %d, emScale %f\n", firstPathName, fontTarget, fontName, fontStyle, numGlyphs, type, charcodes, handleMissingGlyphs, pathParameterTemplate, emScale );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &firstPathName, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathParameterTemplate, TRUE )) return;
+    args.firstPathName = *map_context_objects( OBJ_TYPE_PATH, 1, &firstPathName );
+    args.pathParameterTemplate = *map_context_objects( OBJ_TYPE_PATH, 1, &pathParameterTemplate );
     if ((status = UNIX_CALL( glPathGlyphsNV, &args ))) WARN( "glPathGlyphsNV returned %#lx\n", status );
 }
 
 static GLenum WINAPI glPathMemoryGlyphIndexArrayNV( GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale )
 {
-    struct glPathMemoryGlyphIndexArrayNV_params args = { .teb = NtCurrentTeb(), .firstPathName = firstPathName, .fontTarget = fontTarget, .fontSize = fontSize, .fontData = fontData, .faceIndex = faceIndex, .firstGlyphIndex = firstGlyphIndex, .numGlyphs = numGlyphs, .pathParameterTemplate = pathParameterTemplate, .emScale = emScale };
+    struct glPathMemoryGlyphIndexArrayNV_params args = { .teb = NtCurrentTeb(), .fontTarget = fontTarget, .fontSize = fontSize, .fontData = fontData, .faceIndex = faceIndex, .firstGlyphIndex = firstGlyphIndex, .numGlyphs = numGlyphs, .pathParameterTemplate = pathParameterTemplate, .emScale = emScale };
     NTSTATUS status;
     TRACE( "firstPathName %d, fontTarget %d, fontSize %Id, fontData %p, faceIndex %d, firstGlyphIndex %d, numGlyphs %d, pathParameterTemplate %d, emScale %f\n", firstPathName, fontTarget, fontSize, fontData, faceIndex, firstGlyphIndex, numGlyphs, pathParameterTemplate, emScale );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &firstPathName, TRUE )) return args.ret;
+    args.firstPathName = *map_context_objects( OBJ_TYPE_PATH, 1, &firstPathName );
     if ((status = UNIX_CALL( glPathMemoryGlyphIndexArrayNV, &args ))) WARN( "glPathMemoryGlyphIndexArrayNV returned %#lx\n", status );
     return args.ret;
 }
 
 static void WINAPI glPathParameterfNV( GLuint path, GLenum pname, GLfloat value )
 {
-    struct glPathParameterfNV_params args = { .teb = NtCurrentTeb(), .path = path, .pname = pname, .value = value };
+    struct glPathParameterfNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .value = value };
     NTSTATUS status;
     TRACE( "path %d, pname %d, value %f\n", path, pname, value );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathParameterfNV, &args ))) WARN( "glPathParameterfNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathParameterfvNV( GLuint path, GLenum pname, const GLfloat *value )
 {
-    struct glPathParameterfvNV_params args = { .teb = NtCurrentTeb(), .path = path, .pname = pname, .value = value };
+    struct glPathParameterfvNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .value = value };
     NTSTATUS status;
     TRACE( "path %d, pname %d, value %p\n", path, pname, value );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathParameterfvNV, &args ))) WARN( "glPathParameterfvNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathParameteriNV( GLuint path, GLenum pname, GLint value )
 {
-    struct glPathParameteriNV_params args = { .teb = NtCurrentTeb(), .path = path, .pname = pname, .value = value };
+    struct glPathParameteriNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .value = value };
     NTSTATUS status;
     TRACE( "path %d, pname %d, value %d\n", path, pname, value );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathParameteriNV, &args ))) WARN( "glPathParameteriNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathParameterivNV( GLuint path, GLenum pname, const GLint *value )
 {
-    struct glPathParameterivNV_params args = { .teb = NtCurrentTeb(), .path = path, .pname = pname, .value = value };
+    struct glPathParameterivNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .value = value };
     NTSTATUS status;
     TRACE( "path %d, pname %d, value %p\n", path, pname, value );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathParameterivNV, &args ))) WARN( "glPathParameterivNV returned %#lx\n", status );
 }
 
@@ -15319,25 +16024,31 @@ static void WINAPI glPathStencilFuncNV( GLenum func, GLint ref, GLuint mask )
 
 static void WINAPI glPathStringNV( GLuint path, GLenum format, GLsizei length, const void *pathString )
 {
-    struct glPathStringNV_params args = { .teb = NtCurrentTeb(), .path = path, .format = format, .length = length, .pathString = pathString };
+    struct glPathStringNV_params args = { .teb = NtCurrentTeb(), .format = format, .length = length, .pathString = pathString };
     NTSTATUS status;
     TRACE( "path %d, format %d, length %d, pathString %p\n", path, format, length, pathString );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathStringNV, &args ))) WARN( "glPathStringNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathSubCommandsNV( GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords )
 {
-    struct glPathSubCommandsNV_params args = { .teb = NtCurrentTeb(), .path = path, .commandStart = commandStart, .commandsToDelete = commandsToDelete, .numCommands = numCommands, .commands = commands, .numCoords = numCoords, .coordType = coordType, .coords = coords };
+    struct glPathSubCommandsNV_params args = { .teb = NtCurrentTeb(), .commandStart = commandStart, .commandsToDelete = commandsToDelete, .numCommands = numCommands, .commands = commands, .numCoords = numCoords, .coordType = coordType, .coords = coords };
     NTSTATUS status;
     TRACE( "path %d, commandStart %d, commandsToDelete %d, numCommands %d, commands %p, numCoords %d, coordType %d, coords %p\n", path, commandStart, commandsToDelete, numCommands, commands, numCoords, coordType, coords );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathSubCommandsNV, &args ))) WARN( "glPathSubCommandsNV returned %#lx\n", status );
 }
 
 static void WINAPI glPathSubCoordsNV( GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords )
 {
-    struct glPathSubCoordsNV_params args = { .teb = NtCurrentTeb(), .path = path, .coordStart = coordStart, .numCoords = numCoords, .coordType = coordType, .coords = coords };
+    struct glPathSubCoordsNV_params args = { .teb = NtCurrentTeb(), .coordStart = coordStart, .numCoords = numCoords, .coordType = coordType, .coords = coords };
     NTSTATUS status;
     TRACE( "path %d, coordStart %d, numCoords %d, coordType %d, coords %p\n", path, coordStart, numCoords, coordType, coords );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPathSubCoordsNV, &args ))) WARN( "glPathSubCoordsNV returned %#lx\n", status );
 }
 
@@ -15479,9 +16190,11 @@ static void WINAPI glPixelZoomxOES( GLfixed xfactor, GLfixed yfactor )
 
 static GLboolean WINAPI glPointAlongPathNV( GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY )
 {
-    struct glPointAlongPathNV_params args = { .teb = NtCurrentTeb(), .path = path, .startSegment = startSegment, .numSegments = numSegments, .distance = distance, .x = x, .y = y, .tangentX = tangentX, .tangentY = tangentY };
+    struct glPointAlongPathNV_params args = { .teb = NtCurrentTeb(), .startSegment = startSegment, .numSegments = numSegments, .distance = distance, .x = x, .y = y, .tangentX = tangentX, .tangentY = tangentY };
     NTSTATUS status;
     TRACE( "path %d, startSegment %d, numSegments %d, distance %f, x %p, y %p, tangentX %p, tangentY %p\n", path, startSegment, numSegments, distance, x, y, tangentX, tangentY );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return args.ret;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glPointAlongPathNV, &args ))) WARN( "glPointAlongPathNV returned %#lx\n", status );
     return args.ret;
 }
@@ -15754,18 +16467,26 @@ static void WINAPI glPrimitiveRestartNV(void)
 
 static void WINAPI glPrioritizeTexturesEXT( GLsizei n, const GLuint *textures, const GLclampf *priorities )
 {
-    struct glPrioritizeTexturesEXT_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures, .priorities = priorities };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glPrioritizeTexturesEXT_params args = { .teb = NtCurrentTeb(), .n = n, .priorities = priorities };
     NTSTATUS status;
     TRACE( "n %d, textures %p, priorities %p\n", n, textures, priorities );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glPrioritizeTexturesEXT, &args ))) WARN( "glPrioritizeTexturesEXT returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static void WINAPI glPrioritizeTexturesxOES( GLsizei n, const GLuint *textures, const GLfixed *priorities )
 {
-    struct glPrioritizeTexturesxOES_params args = { .teb = NtCurrentTeb(), .n = n, .textures = textures, .priorities = priorities };
+    GLuint textures_buf[64], *textures_tmp;
+    struct glPrioritizeTexturesxOES_params args = { .teb = NtCurrentTeb(), .n = n, .priorities = priorities };
     NTSTATUS status;
     TRACE( "n %d, textures %p, priorities %p\n", n, textures, priorities );
+    textures_tmp = n > 0 ? memdup_objects( n, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = n > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, n, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glPrioritizeTexturesxOES, &args ))) WARN( "glPrioritizeTexturesxOES returned %#lx\n", status );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static void WINAPI glProgramBinary( GLuint program, GLenum binaryFormat, const void *binary, GLsizei length )
@@ -15978,33 +16699,37 @@ static void WINAPI glProgramLocalParametersI4uivNV( GLenum target, GLuint index,
 
 static void WINAPI glProgramNamedParameter4dNV( GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w )
 {
-    struct glProgramNamedParameter4dNV_params args = { .teb = NtCurrentTeb(), .id = id, .len = len, .name = name, .x = x, .y = y, .z = z, .w = w };
+    struct glProgramNamedParameter4dNV_params args = { .teb = NtCurrentTeb(), .len = len, .name = name, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "id %d, len %d, name %p, x %f, y %f, z %f, w %f\n", id, len, name, x, y, z, w );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glProgramNamedParameter4dNV, &args ))) WARN( "glProgramNamedParameter4dNV returned %#lx\n", status );
 }
 
 static void WINAPI glProgramNamedParameter4dvNV( GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v )
 {
-    struct glProgramNamedParameter4dvNV_params args = { .teb = NtCurrentTeb(), .id = id, .len = len, .name = name, .v = v };
+    struct glProgramNamedParameter4dvNV_params args = { .teb = NtCurrentTeb(), .len = len, .name = name, .v = v };
     NTSTATUS status;
     TRACE( "id %d, len %d, name %p, v %p\n", id, len, name, v );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glProgramNamedParameter4dvNV, &args ))) WARN( "glProgramNamedParameter4dvNV returned %#lx\n", status );
 }
 
 static void WINAPI glProgramNamedParameter4fNV( GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w )
 {
-    struct glProgramNamedParameter4fNV_params args = { .teb = NtCurrentTeb(), .id = id, .len = len, .name = name, .x = x, .y = y, .z = z, .w = w };
+    struct glProgramNamedParameter4fNV_params args = { .teb = NtCurrentTeb(), .len = len, .name = name, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "id %d, len %d, name %p, x %f, y %f, z %f, w %f\n", id, len, name, x, y, z, w );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glProgramNamedParameter4fNV, &args ))) WARN( "glProgramNamedParameter4fNV returned %#lx\n", status );
 }
 
 static void WINAPI glProgramNamedParameter4fvNV( GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v )
 {
-    struct glProgramNamedParameter4fvNV_params args = { .teb = NtCurrentTeb(), .id = id, .len = len, .name = name, .v = v };
+    struct glProgramNamedParameter4fvNV_params args = { .teb = NtCurrentTeb(), .len = len, .name = name, .v = v };
     NTSTATUS status;
     TRACE( "id %d, len %d, name %p, v %p\n", id, len, name, v );
+    args.id = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &id );
     if ((status = UNIX_CALL( glProgramNamedParameter4fvNV, &args ))) WARN( "glProgramNamedParameter4fvNV returned %#lx\n", status );
 }
 
@@ -16114,9 +16839,11 @@ static void WINAPI glProgramUniform1d( GLuint program, GLint location, GLdouble
 
 static void WINAPI glProgramUniform1dEXT( GLuint program, GLint location, GLdouble x )
 {
-    struct glProgramUniform1dEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .x = x };
+    struct glProgramUniform1dEXT_params args = { .teb = NtCurrentTeb(), .location = location, .x = x };
     NTSTATUS status;
     TRACE( "program %d, location %d, x %f\n", program, location, x );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1dEXT, &args ))) WARN( "glProgramUniform1dEXT returned %#lx\n", status );
 }
 
@@ -16130,9 +16857,11 @@ static void WINAPI glProgramUniform1dv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform1dvEXT( GLuint program, GLint location, GLsizei count, const GLdouble *value )
 {
-    struct glProgramUniform1dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform1dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1dvEXT, &args ))) WARN( "glProgramUniform1dvEXT returned %#lx\n", status );
 }
 
@@ -16146,9 +16875,11 @@ static void WINAPI glProgramUniform1f( GLuint program, GLint location, GLfloat v
 
 static void WINAPI glProgramUniform1fEXT( GLuint program, GLint location, GLfloat v0 )
 {
-    struct glProgramUniform1fEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0 };
+    struct glProgramUniform1fEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %f\n", program, location, v0 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1fEXT, &args ))) WARN( "glProgramUniform1fEXT returned %#lx\n", status );
 }
 
@@ -16162,9 +16893,11 @@ static void WINAPI glProgramUniform1fv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform1fvEXT( GLuint program, GLint location, GLsizei count, const GLfloat *value )
 {
-    struct glProgramUniform1fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform1fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1fvEXT, &args ))) WARN( "glProgramUniform1fvEXT returned %#lx\n", status );
 }
 
@@ -16210,9 +16943,11 @@ static void WINAPI glProgramUniform1i64vNV( GLuint program, GLint location, GLsi
 
 static void WINAPI glProgramUniform1iEXT( GLuint program, GLint location, GLint v0 )
 {
-    struct glProgramUniform1iEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0 };
+    struct glProgramUniform1iEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d\n", program, location, v0 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1iEXT, &args ))) WARN( "glProgramUniform1iEXT returned %#lx\n", status );
 }
 
@@ -16226,9 +16961,11 @@ static void WINAPI glProgramUniform1iv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform1ivEXT( GLuint program, GLint location, GLsizei count, const GLint *value )
 {
-    struct glProgramUniform1ivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform1ivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1ivEXT, &args ))) WARN( "glProgramUniform1ivEXT returned %#lx\n", status );
 }
 
@@ -16274,9 +17011,11 @@ static void WINAPI glProgramUniform1ui64vNV( GLuint program, GLint location, GLs
 
 static void WINAPI glProgramUniform1uiEXT( GLuint program, GLint location, GLuint v0 )
 {
-    struct glProgramUniform1uiEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0 };
+    struct glProgramUniform1uiEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d\n", program, location, v0 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1uiEXT, &args ))) WARN( "glProgramUniform1uiEXT returned %#lx\n", status );
 }
 
@@ -16290,9 +17029,11 @@ static void WINAPI glProgramUniform1uiv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform1uivEXT( GLuint program, GLint location, GLsizei count, const GLuint *value )
 {
-    struct glProgramUniform1uivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform1uivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform1uivEXT, &args ))) WARN( "glProgramUniform1uivEXT returned %#lx\n", status );
 }
 
@@ -16306,9 +17047,11 @@ static void WINAPI glProgramUniform2d( GLuint program, GLint location, GLdouble
 
 static void WINAPI glProgramUniform2dEXT( GLuint program, GLint location, GLdouble x, GLdouble y )
 {
-    struct glProgramUniform2dEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .x = x, .y = y };
+    struct glProgramUniform2dEXT_params args = { .teb = NtCurrentTeb(), .location = location, .x = x, .y = y };
     NTSTATUS status;
     TRACE( "program %d, location %d, x %f, y %f\n", program, location, x, y );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2dEXT, &args ))) WARN( "glProgramUniform2dEXT returned %#lx\n", status );
 }
 
@@ -16322,9 +17065,11 @@ static void WINAPI glProgramUniform2dv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform2dvEXT( GLuint program, GLint location, GLsizei count, const GLdouble *value )
 {
-    struct glProgramUniform2dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform2dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2dvEXT, &args ))) WARN( "glProgramUniform2dvEXT returned %#lx\n", status );
 }
 
@@ -16338,9 +17083,11 @@ static void WINAPI glProgramUniform2f( GLuint program, GLint location, GLfloat v
 
 static void WINAPI glProgramUniform2fEXT( GLuint program, GLint location, GLfloat v0, GLfloat v1 )
 {
-    struct glProgramUniform2fEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1 };
+    struct glProgramUniform2fEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %f, v1 %f\n", program, location, v0, v1 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2fEXT, &args ))) WARN( "glProgramUniform2fEXT returned %#lx\n", status );
 }
 
@@ -16354,9 +17101,11 @@ static void WINAPI glProgramUniform2fv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform2fvEXT( GLuint program, GLint location, GLsizei count, const GLfloat *value )
 {
-    struct glProgramUniform2fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform2fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2fvEXT, &args ))) WARN( "glProgramUniform2fvEXT returned %#lx\n", status );
 }
 
@@ -16402,9 +17151,11 @@ static void WINAPI glProgramUniform2i64vNV( GLuint program, GLint location, GLsi
 
 static void WINAPI glProgramUniform2iEXT( GLuint program, GLint location, GLint v0, GLint v1 )
 {
-    struct glProgramUniform2iEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1 };
+    struct glProgramUniform2iEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d, v1 %d\n", program, location, v0, v1 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2iEXT, &args ))) WARN( "glProgramUniform2iEXT returned %#lx\n", status );
 }
 
@@ -16418,9 +17169,11 @@ static void WINAPI glProgramUniform2iv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform2ivEXT( GLuint program, GLint location, GLsizei count, const GLint *value )
 {
-    struct glProgramUniform2ivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform2ivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2ivEXT, &args ))) WARN( "glProgramUniform2ivEXT returned %#lx\n", status );
 }
 
@@ -16466,9 +17219,11 @@ static void WINAPI glProgramUniform2ui64vNV( GLuint program, GLint location, GLs
 
 static void WINAPI glProgramUniform2uiEXT( GLuint program, GLint location, GLuint v0, GLuint v1 )
 {
-    struct glProgramUniform2uiEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1 };
+    struct glProgramUniform2uiEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d, v1 %d\n", program, location, v0, v1 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2uiEXT, &args ))) WARN( "glProgramUniform2uiEXT returned %#lx\n", status );
 }
 
@@ -16482,9 +17237,11 @@ static void WINAPI glProgramUniform2uiv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform2uivEXT( GLuint program, GLint location, GLsizei count, const GLuint *value )
 {
-    struct glProgramUniform2uivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform2uivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform2uivEXT, &args ))) WARN( "glProgramUniform2uivEXT returned %#lx\n", status );
 }
 
@@ -16498,9 +17255,11 @@ static void WINAPI glProgramUniform3d( GLuint program, GLint location, GLdouble
 
 static void WINAPI glProgramUniform3dEXT( GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z )
 {
-    struct glProgramUniform3dEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .x = x, .y = y, .z = z };
+    struct glProgramUniform3dEXT_params args = { .teb = NtCurrentTeb(), .location = location, .x = x, .y = y, .z = z };
     NTSTATUS status;
     TRACE( "program %d, location %d, x %f, y %f, z %f\n", program, location, x, y, z );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3dEXT, &args ))) WARN( "glProgramUniform3dEXT returned %#lx\n", status );
 }
 
@@ -16514,9 +17273,11 @@ static void WINAPI glProgramUniform3dv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform3dvEXT( GLuint program, GLint location, GLsizei count, const GLdouble *value )
 {
-    struct glProgramUniform3dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform3dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3dvEXT, &args ))) WARN( "glProgramUniform3dvEXT returned %#lx\n", status );
 }
 
@@ -16530,9 +17291,11 @@ static void WINAPI glProgramUniform3f( GLuint program, GLint location, GLfloat v
 
 static void WINAPI glProgramUniform3fEXT( GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2 )
 {
-    struct glProgramUniform3fEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1, .v2 = v2 };
+    struct glProgramUniform3fEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1, .v2 = v2 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %f, v1 %f, v2 %f\n", program, location, v0, v1, v2 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3fEXT, &args ))) WARN( "glProgramUniform3fEXT returned %#lx\n", status );
 }
 
@@ -16546,9 +17309,11 @@ static void WINAPI glProgramUniform3fv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform3fvEXT( GLuint program, GLint location, GLsizei count, const GLfloat *value )
 {
-    struct glProgramUniform3fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform3fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3fvEXT, &args ))) WARN( "glProgramUniform3fvEXT returned %#lx\n", status );
 }
 
@@ -16594,9 +17359,11 @@ static void WINAPI glProgramUniform3i64vNV( GLuint program, GLint location, GLsi
 
 static void WINAPI glProgramUniform3iEXT( GLuint program, GLint location, GLint v0, GLint v1, GLint v2 )
 {
-    struct glProgramUniform3iEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1, .v2 = v2 };
+    struct glProgramUniform3iEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1, .v2 = v2 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d, v1 %d, v2 %d\n", program, location, v0, v1, v2 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3iEXT, &args ))) WARN( "glProgramUniform3iEXT returned %#lx\n", status );
 }
 
@@ -16610,9 +17377,11 @@ static void WINAPI glProgramUniform3iv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform3ivEXT( GLuint program, GLint location, GLsizei count, const GLint *value )
 {
-    struct glProgramUniform3ivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform3ivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3ivEXT, &args ))) WARN( "glProgramUniform3ivEXT returned %#lx\n", status );
 }
 
@@ -16658,9 +17427,11 @@ static void WINAPI glProgramUniform3ui64vNV( GLuint program, GLint location, GLs
 
 static void WINAPI glProgramUniform3uiEXT( GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2 )
 {
-    struct glProgramUniform3uiEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1, .v2 = v2 };
+    struct glProgramUniform3uiEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1, .v2 = v2 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d, v1 %d, v2 %d\n", program, location, v0, v1, v2 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3uiEXT, &args ))) WARN( "glProgramUniform3uiEXT returned %#lx\n", status );
 }
 
@@ -16674,9 +17445,11 @@ static void WINAPI glProgramUniform3uiv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform3uivEXT( GLuint program, GLint location, GLsizei count, const GLuint *value )
 {
-    struct glProgramUniform3uivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform3uivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform3uivEXT, &args ))) WARN( "glProgramUniform3uivEXT returned %#lx\n", status );
 }
 
@@ -16690,9 +17463,11 @@ static void WINAPI glProgramUniform4d( GLuint program, GLint location, GLdouble
 
 static void WINAPI glProgramUniform4dEXT( GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w )
 {
-    struct glProgramUniform4dEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .x = x, .y = y, .z = z, .w = w };
+    struct glProgramUniform4dEXT_params args = { .teb = NtCurrentTeb(), .location = location, .x = x, .y = y, .z = z, .w = w };
     NTSTATUS status;
     TRACE( "program %d, location %d, x %f, y %f, z %f, w %f\n", program, location, x, y, z, w );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4dEXT, &args ))) WARN( "glProgramUniform4dEXT returned %#lx\n", status );
 }
 
@@ -16706,9 +17481,11 @@ static void WINAPI glProgramUniform4dv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform4dvEXT( GLuint program, GLint location, GLsizei count, const GLdouble *value )
 {
-    struct glProgramUniform4dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform4dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4dvEXT, &args ))) WARN( "glProgramUniform4dvEXT returned %#lx\n", status );
 }
 
@@ -16722,9 +17499,11 @@ static void WINAPI glProgramUniform4f( GLuint program, GLint location, GLfloat v
 
 static void WINAPI glProgramUniform4fEXT( GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 )
 {
-    struct glProgramUniform4fEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3 };
+    struct glProgramUniform4fEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %f, v1 %f, v2 %f, v3 %f\n", program, location, v0, v1, v2, v3 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4fEXT, &args ))) WARN( "glProgramUniform4fEXT returned %#lx\n", status );
 }
 
@@ -16738,9 +17517,11 @@ static void WINAPI glProgramUniform4fv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform4fvEXT( GLuint program, GLint location, GLsizei count, const GLfloat *value )
 {
-    struct glProgramUniform4fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform4fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4fvEXT, &args ))) WARN( "glProgramUniform4fvEXT returned %#lx\n", status );
 }
 
@@ -16786,9 +17567,11 @@ static void WINAPI glProgramUniform4i64vNV( GLuint program, GLint location, GLsi
 
 static void WINAPI glProgramUniform4iEXT( GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3 )
 {
-    struct glProgramUniform4iEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3 };
+    struct glProgramUniform4iEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d, v1 %d, v2 %d, v3 %d\n", program, location, v0, v1, v2, v3 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4iEXT, &args ))) WARN( "glProgramUniform4iEXT returned %#lx\n", status );
 }
 
@@ -16802,9 +17585,11 @@ static void WINAPI glProgramUniform4iv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform4ivEXT( GLuint program, GLint location, GLsizei count, const GLint *value )
 {
-    struct glProgramUniform4ivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform4ivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4ivEXT, &args ))) WARN( "glProgramUniform4ivEXT returned %#lx\n", status );
 }
 
@@ -16850,9 +17635,11 @@ static void WINAPI glProgramUniform4ui64vNV( GLuint program, GLint location, GLs
 
 static void WINAPI glProgramUniform4uiEXT( GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3 )
 {
-    struct glProgramUniform4uiEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3 };
+    struct glProgramUniform4uiEXT_params args = { .teb = NtCurrentTeb(), .location = location, .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3 };
     NTSTATUS status;
     TRACE( "program %d, location %d, v0 %d, v1 %d, v2 %d, v3 %d\n", program, location, v0, v1, v2, v3 );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4uiEXT, &args ))) WARN( "glProgramUniform4uiEXT returned %#lx\n", status );
 }
 
@@ -16866,9 +17653,11 @@ static void WINAPI glProgramUniform4uiv( GLuint program, GLint location, GLsizei
 
 static void WINAPI glProgramUniform4uivEXT( GLuint program, GLint location, GLsizei count, const GLuint *value )
 {
-    struct glProgramUniform4uivEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .value = value };
+    struct glProgramUniform4uivEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, value %p\n", program, location, count, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniform4uivEXT, &args ))) WARN( "glProgramUniform4uivEXT returned %#lx\n", status );
 }
 
@@ -16914,9 +17703,11 @@ static void WINAPI glProgramUniformMatrix2dv( GLuint program, GLint location, GL
 
 static void WINAPI glProgramUniformMatrix2dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix2dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix2dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix2dvEXT, &args ))) WARN( "glProgramUniformMatrix2dvEXT returned %#lx\n", status );
 }
 
@@ -16930,9 +17721,11 @@ static void WINAPI glProgramUniformMatrix2fv( GLuint program, GLint location, GL
 
 static void WINAPI glProgramUniformMatrix2fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix2fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix2fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix2fvEXT, &args ))) WARN( "glProgramUniformMatrix2fvEXT returned %#lx\n", status );
 }
 
@@ -16946,9 +17739,11 @@ static void WINAPI glProgramUniformMatrix2x3dv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix2x3dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix2x3dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix2x3dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix2x3dvEXT, &args ))) WARN( "glProgramUniformMatrix2x3dvEXT returned %#lx\n", status );
 }
 
@@ -16962,9 +17757,11 @@ static void WINAPI glProgramUniformMatrix2x3fv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix2x3fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix2x3fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix2x3fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix2x3fvEXT, &args ))) WARN( "glProgramUniformMatrix2x3fvEXT returned %#lx\n", status );
 }
 
@@ -16978,9 +17775,11 @@ static void WINAPI glProgramUniformMatrix2x4dv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix2x4dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix2x4dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix2x4dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix2x4dvEXT, &args ))) WARN( "glProgramUniformMatrix2x4dvEXT returned %#lx\n", status );
 }
 
@@ -16994,9 +17793,11 @@ static void WINAPI glProgramUniformMatrix2x4fv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix2x4fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix2x4fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix2x4fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix2x4fvEXT, &args ))) WARN( "glProgramUniformMatrix2x4fvEXT returned %#lx\n", status );
 }
 
@@ -17010,9 +17811,11 @@ static void WINAPI glProgramUniformMatrix3dv( GLuint program, GLint location, GL
 
 static void WINAPI glProgramUniformMatrix3dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix3dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix3dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix3dvEXT, &args ))) WARN( "glProgramUniformMatrix3dvEXT returned %#lx\n", status );
 }
 
@@ -17026,9 +17829,11 @@ static void WINAPI glProgramUniformMatrix3fv( GLuint program, GLint location, GL
 
 static void WINAPI glProgramUniformMatrix3fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix3fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix3fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix3fvEXT, &args ))) WARN( "glProgramUniformMatrix3fvEXT returned %#lx\n", status );
 }
 
@@ -17042,9 +17847,11 @@ static void WINAPI glProgramUniformMatrix3x2dv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix3x2dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix3x2dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix3x2dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix3x2dvEXT, &args ))) WARN( "glProgramUniformMatrix3x2dvEXT returned %#lx\n", status );
 }
 
@@ -17058,9 +17865,11 @@ static void WINAPI glProgramUniformMatrix3x2fv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix3x2fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix3x2fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix3x2fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix3x2fvEXT, &args ))) WARN( "glProgramUniformMatrix3x2fvEXT returned %#lx\n", status );
 }
 
@@ -17074,9 +17883,11 @@ static void WINAPI glProgramUniformMatrix3x4dv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix3x4dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix3x4dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix3x4dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix3x4dvEXT, &args ))) WARN( "glProgramUniformMatrix3x4dvEXT returned %#lx\n", status );
 }
 
@@ -17090,9 +17901,11 @@ static void WINAPI glProgramUniformMatrix3x4fv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix3x4fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix3x4fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix3x4fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix3x4fvEXT, &args ))) WARN( "glProgramUniformMatrix3x4fvEXT returned %#lx\n", status );
 }
 
@@ -17106,9 +17919,11 @@ static void WINAPI glProgramUniformMatrix4dv( GLuint program, GLint location, GL
 
 static void WINAPI glProgramUniformMatrix4dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix4dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix4dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix4dvEXT, &args ))) WARN( "glProgramUniformMatrix4dvEXT returned %#lx\n", status );
 }
 
@@ -17122,9 +17937,11 @@ static void WINAPI glProgramUniformMatrix4fv( GLuint program, GLint location, GL
 
 static void WINAPI glProgramUniformMatrix4fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix4fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix4fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix4fvEXT, &args ))) WARN( "glProgramUniformMatrix4fvEXT returned %#lx\n", status );
 }
 
@@ -17138,9 +17955,11 @@ static void WINAPI glProgramUniformMatrix4x2dv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix4x2dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix4x2dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix4x2dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix4x2dvEXT, &args ))) WARN( "glProgramUniformMatrix4x2dvEXT returned %#lx\n", status );
 }
 
@@ -17154,9 +17973,11 @@ static void WINAPI glProgramUniformMatrix4x2fv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix4x2fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix4x2fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix4x2fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix4x2fvEXT, &args ))) WARN( "glProgramUniformMatrix4x2fvEXT returned %#lx\n", status );
 }
 
@@ -17170,9 +17991,11 @@ static void WINAPI glProgramUniformMatrix4x3dv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix4x3dvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value )
 {
-    struct glProgramUniformMatrix4x3dvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix4x3dvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix4x3dvEXT, &args ))) WARN( "glProgramUniformMatrix4x3dvEXT returned %#lx\n", status );
 }
 
@@ -17186,9 +18009,11 @@ static void WINAPI glProgramUniformMatrix4x3fv( GLuint program, GLint location,
 
 static void WINAPI glProgramUniformMatrix4x3fvEXT( GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value )
 {
-    struct glProgramUniformMatrix4x3fvEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .count = count, .transpose = transpose, .value = value };
+    struct glProgramUniformMatrix4x3fvEXT_params args = { .teb = NtCurrentTeb(), .location = location, .count = count, .transpose = transpose, .value = value };
     NTSTATUS status;
     TRACE( "program %d, location %d, count %d, transpose %d, value %p\n", program, location, count, transpose, value );
+    if (!alloc_context_objects( OBJ_TYPE_PROGRAM, 1, &program, TRUE )) return;
+    args.program = *map_context_objects( OBJ_TYPE_PROGRAM, 1, &program );
     if ((status = UNIX_CALL( glProgramUniformMatrix4x3fvEXT, &args ))) WARN( "glProgramUniformMatrix4x3fvEXT returned %#lx\n", status );
 }
 
@@ -17412,9 +18237,10 @@ static void WINAPI glReferencePlaneSGIX( const GLdouble *equation )
 
 static GLboolean WINAPI glReleaseKeyedMutexWin32EXT( GLuint memory, GLuint64 key )
 {
-    struct glReleaseKeyedMutexWin32EXT_params args = { .teb = NtCurrentTeb(), .memory = memory, .key = key };
+    struct glReleaseKeyedMutexWin32EXT_params args = { .teb = NtCurrentTeb(), .key = key };
     NTSTATUS status;
     TRACE( "memory %d, key %s\n", memory, wine_dbgstr_longlong(key) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glReleaseKeyedMutexWin32EXT, &args ))) WARN( "glReleaseKeyedMutexWin32EXT returned %#lx\n", status );
     return args.ret;
 }
@@ -17669,10 +18495,14 @@ static void WINAPI glReplacementCodeusvSUN( const GLushort *code )
 
 static void WINAPI glRequestResidentProgramsNV( GLsizei n, const GLuint *programs )
 {
-    struct glRequestResidentProgramsNV_params args = { .teb = NtCurrentTeb(), .n = n, .programs = programs };
+    GLuint programs_buf[64], *programs_tmp;
+    struct glRequestResidentProgramsNV_params args = { .teb = NtCurrentTeb(), .n = n };
     NTSTATUS status;
     TRACE( "n %d, programs %p\n", n, programs );
+    programs_tmp = n > 0 ? memdup_objects( n, programs, programs_buf, ARRAY_SIZE(programs_buf) ) : NULL;
+    args.programs = n > 0 ? map_context_objects( OBJ_TYPE_PROGRAM, n, programs_tmp ) : NULL;
     if ((status = UNIX_CALL( glRequestResidentProgramsNV, &args ))) WARN( "glRequestResidentProgramsNV returned %#lx\n", status );
+    if (programs_tmp != programs_buf) free( programs_tmp );
 }
 
 static void WINAPI glResetHistogram( GLenum target )
@@ -17693,9 +18523,10 @@ static void WINAPI glResetHistogramEXT( GLenum target )
 
 static void WINAPI glResetMemoryObjectParameterNV( GLuint memory, GLenum pname )
 {
-    struct glResetMemoryObjectParameterNV_params args = { .teb = NtCurrentTeb(), .memory = memory, .pname = pname };
+    struct glResetMemoryObjectParameterNV_params args = { .teb = NtCurrentTeb(), .pname = pname };
     NTSTATUS status;
     TRACE( "memory %d, pname %d\n", memory, pname );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glResetMemoryObjectParameterNV, &args ))) WARN( "glResetMemoryObjectParameterNV returned %#lx\n", status );
 }
 
@@ -17845,49 +18676,55 @@ static void WINAPI glSamplePatternSGIS( GLenum pattern )
 
 static void WINAPI glSamplerParameterIiv( GLuint sampler, GLenum pname, const GLint *param )
 {
-    struct glSamplerParameterIiv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .param = param };
+    struct glSamplerParameterIiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, param %p\n", sampler, pname, param );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glSamplerParameterIiv, &args ))) WARN( "glSamplerParameterIiv returned %#lx\n", status );
 }
 
 static void WINAPI glSamplerParameterIuiv( GLuint sampler, GLenum pname, const GLuint *param )
 {
-    struct glSamplerParameterIuiv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .param = param };
+    struct glSamplerParameterIuiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, param %p\n", sampler, pname, param );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glSamplerParameterIuiv, &args ))) WARN( "glSamplerParameterIuiv returned %#lx\n", status );
 }
 
 static void WINAPI glSamplerParameterf( GLuint sampler, GLenum pname, GLfloat param )
 {
-    struct glSamplerParameterf_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .param = param };
+    struct glSamplerParameterf_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, param %f\n", sampler, pname, param );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glSamplerParameterf, &args ))) WARN( "glSamplerParameterf returned %#lx\n", status );
 }
 
 static void WINAPI glSamplerParameterfv( GLuint sampler, GLenum pname, const GLfloat *param )
 {
-    struct glSamplerParameterfv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .param = param };
+    struct glSamplerParameterfv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, param %p\n", sampler, pname, param );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glSamplerParameterfv, &args ))) WARN( "glSamplerParameterfv returned %#lx\n", status );
 }
 
 static void WINAPI glSamplerParameteri( GLuint sampler, GLenum pname, GLint param )
 {
-    struct glSamplerParameteri_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .param = param };
+    struct glSamplerParameteri_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, param %d\n", sampler, pname, param );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glSamplerParameteri, &args ))) WARN( "glSamplerParameteri returned %#lx\n", status );
 }
 
 static void WINAPI glSamplerParameteriv( GLuint sampler, GLenum pname, const GLint *param )
 {
-    struct glSamplerParameteriv_params args = { .teb = NtCurrentTeb(), .sampler = sampler, .pname = pname, .param = param };
+    struct glSamplerParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "sampler %d, pname %d, param %p\n", sampler, pname, param );
+    args.sampler = *map_context_objects( OBJ_TYPE_SAMPLER, 1, &sampler );
     if ((status = UNIX_CALL( glSamplerParameteriv, &args ))) WARN( "glSamplerParameteriv returned %#lx\n", status );
 }
 
@@ -18293,17 +19130,19 @@ static void WINAPI glSelectTextureSGIS( GLenum target )
 
 static void WINAPI glSemaphoreParameterivNV( GLuint semaphore, GLenum pname, const GLint *params )
 {
-    struct glSemaphoreParameterivNV_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .pname = pname, .params = params };
+    struct glSemaphoreParameterivNV_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "semaphore %d, pname %d, params %p\n", semaphore, pname, params );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glSemaphoreParameterivNV, &args ))) WARN( "glSemaphoreParameterivNV returned %#lx\n", status );
 }
 
 static void WINAPI glSemaphoreParameterui64vEXT( GLuint semaphore, GLenum pname, const GLuint64 *params )
 {
-    struct glSemaphoreParameterui64vEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .pname = pname, .params = params };
+    struct glSemaphoreParameterui64vEXT_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "semaphore %d, pname %d, params %p\n", semaphore, pname, params );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
     if ((status = UNIX_CALL( glSemaphoreParameterui64vEXT, &args ))) WARN( "glSemaphoreParameterui64vEXT returned %#lx\n", status );
 }
 
@@ -18485,18 +19324,31 @@ static void WINAPI glSharpenTexFuncSGIS( GLenum target, GLsizei n, const GLfloat
 
 static void WINAPI glSignalSemaphoreEXT( GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts )
 {
-    struct glSignalSemaphoreEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .numBufferBarriers = numBufferBarriers, .buffers = buffers, .numTextureBarriers = numTextureBarriers, .textures = textures, .dstLayouts = dstLayouts };
+    GLuint buffers_buf[64], *buffers_tmp;
+    GLuint textures_buf[64], *textures_tmp;
+    struct glSignalSemaphoreEXT_params args = { .teb = NtCurrentTeb(), .numBufferBarriers = numBufferBarriers, .numTextureBarriers = numTextureBarriers, .dstLayouts = dstLayouts };
     NTSTATUS status;
     TRACE( "semaphore %d, numBufferBarriers %d, buffers %p, numTextureBarriers %d, textures %p, dstLayouts %p\n", semaphore, numBufferBarriers, buffers, numTextureBarriers, textures, dstLayouts );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
+    buffers_tmp = numBufferBarriers > 0 ? memdup_objects( numBufferBarriers, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = numBufferBarriers > 0 ? map_context_objects( OBJ_TYPE_BUFFER, numBufferBarriers, buffers_tmp ) : NULL;
+    textures_tmp = numTextureBarriers > 0 ? memdup_objects( numTextureBarriers, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = numTextureBarriers > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, numTextureBarriers, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glSignalSemaphoreEXT, &args ))) WARN( "glSignalSemaphoreEXT returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static void WINAPI glSignalSemaphoreui64NVX( GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray )
 {
-    struct glSignalSemaphoreui64NVX_params args = { .teb = NtCurrentTeb(), .signalGpu = signalGpu, .fenceObjectCount = fenceObjectCount, .semaphoreArray = semaphoreArray, .fenceValueArray = fenceValueArray };
+    GLuint semaphoreArray_buf[64], *semaphoreArray_tmp;
+    struct glSignalSemaphoreui64NVX_params args = { .teb = NtCurrentTeb(), .signalGpu = signalGpu, .fenceObjectCount = fenceObjectCount, .fenceValueArray = fenceValueArray };
     NTSTATUS status;
     TRACE( "signalGpu %d, fenceObjectCount %d, semaphoreArray %p, fenceValueArray %p\n", signalGpu, fenceObjectCount, semaphoreArray, fenceValueArray );
+    semaphoreArray_tmp = fenceObjectCount > 0 ? memdup_objects( fenceObjectCount, semaphoreArray, semaphoreArray_buf, ARRAY_SIZE(semaphoreArray_buf) ) : NULL;
+    args.semaphoreArray = fenceObjectCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, fenceObjectCount, semaphoreArray_tmp ) : NULL;
     if ((status = UNIX_CALL( glSignalSemaphoreui64NVX, &args ))) WARN( "glSignalSemaphoreui64NVX returned %#lx\n", status );
+    if (semaphoreArray_tmp != semaphoreArray_buf) free( semaphoreArray_tmp );
 }
 
 static void WINAPI glSignalVkFenceNV( GLuint64 vkFence )
@@ -18589,17 +19441,21 @@ static void WINAPI glStencilClearTagEXT( GLsizei stencilTagBits, GLuint stencilC
 
 static void WINAPI glStencilFillPathInstancedNV( GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glStencilFillPathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .fillMode = fillMode, .mask = mask, .transformType = transformType, .transformValues = transformValues };
+    struct glStencilFillPathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .fillMode = fillMode, .mask = mask, .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "numPaths %d, pathNameType %d, paths %p, pathBase %d, fillMode %d, mask %d, transformType %d, transformValues %p\n", numPaths, pathNameType, paths, pathBase, fillMode, mask, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glStencilFillPathInstancedNV, &args ))) WARN( "glStencilFillPathInstancedNV returned %#lx\n", status );
 }
 
 static void WINAPI glStencilFillPathNV( GLuint path, GLenum fillMode, GLuint mask )
 {
-    struct glStencilFillPathNV_params args = { .teb = NtCurrentTeb(), .path = path, .fillMode = fillMode, .mask = mask };
+    struct glStencilFillPathNV_params args = { .teb = NtCurrentTeb(), .fillMode = fillMode, .mask = mask };
     NTSTATUS status;
     TRACE( "path %d, fillMode %d, mask %d\n", path, fillMode, mask );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glStencilFillPathNV, &args ))) WARN( "glStencilFillPathNV returned %#lx\n", status );
 }
 
@@ -18653,49 +19509,61 @@ static void WINAPI glStencilOpValueAMD( GLenum face, GLuint value )
 
 static void WINAPI glStencilStrokePathInstancedNV( GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glStencilStrokePathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .reference = reference, .mask = mask, .transformType = transformType, .transformValues = transformValues };
+    struct glStencilStrokePathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .reference = reference, .mask = mask, .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "numPaths %d, pathNameType %d, paths %p, pathBase %d, reference %d, mask %d, transformType %d, transformValues %p\n", numPaths, pathNameType, paths, pathBase, reference, mask, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glStencilStrokePathInstancedNV, &args ))) WARN( "glStencilStrokePathInstancedNV returned %#lx\n", status );
 }
 
 static void WINAPI glStencilStrokePathNV( GLuint path, GLint reference, GLuint mask )
 {
-    struct glStencilStrokePathNV_params args = { .teb = NtCurrentTeb(), .path = path, .reference = reference, .mask = mask };
+    struct glStencilStrokePathNV_params args = { .teb = NtCurrentTeb(), .reference = reference, .mask = mask };
     NTSTATUS status;
     TRACE( "path %d, reference %d, mask %d\n", path, reference, mask );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glStencilStrokePathNV, &args ))) WARN( "glStencilStrokePathNV returned %#lx\n", status );
 }
 
 static void WINAPI glStencilThenCoverFillPathInstancedNV( GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glStencilThenCoverFillPathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .fillMode = fillMode, .mask = mask, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
+    struct glStencilThenCoverFillPathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .fillMode = fillMode, .mask = mask, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "numPaths %d, pathNameType %d, paths %p, pathBase %d, fillMode %d, mask %d, coverMode %d, transformType %d, transformValues %p\n", numPaths, pathNameType, paths, pathBase, fillMode, mask, coverMode, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glStencilThenCoverFillPathInstancedNV, &args ))) WARN( "glStencilThenCoverFillPathInstancedNV returned %#lx\n", status );
 }
 
 static void WINAPI glStencilThenCoverFillPathNV( GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode )
 {
-    struct glStencilThenCoverFillPathNV_params args = { .teb = NtCurrentTeb(), .path = path, .fillMode = fillMode, .mask = mask, .coverMode = coverMode };
+    struct glStencilThenCoverFillPathNV_params args = { .teb = NtCurrentTeb(), .fillMode = fillMode, .mask = mask, .coverMode = coverMode };
     NTSTATUS status;
     TRACE( "path %d, fillMode %d, mask %d, coverMode %d\n", path, fillMode, mask, coverMode );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glStencilThenCoverFillPathNV, &args ))) WARN( "glStencilThenCoverFillPathNV returned %#lx\n", status );
 }
 
 static void WINAPI glStencilThenCoverStrokePathInstancedNV( GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glStencilThenCoverStrokePathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .pathBase = pathBase, .reference = reference, .mask = mask, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
+    struct glStencilThenCoverStrokePathInstancedNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .pathNameType = pathNameType, .paths = paths, .reference = reference, .mask = mask, .coverMode = coverMode, .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "numPaths %d, pathNameType %d, paths %p, pathBase %d, reference %d, mask %d, coverMode %d, transformType %d, transformValues %p\n", numPaths, pathNameType, paths, pathBase, reference, mask, coverMode, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &pathBase, TRUE )) return;
+    args.pathBase = *map_context_objects( OBJ_TYPE_PATH, 1, &pathBase );
     if ((status = UNIX_CALL( glStencilThenCoverStrokePathInstancedNV, &args ))) WARN( "glStencilThenCoverStrokePathInstancedNV returned %#lx\n", status );
 }
 
 static void WINAPI glStencilThenCoverStrokePathNV( GLuint path, GLint reference, GLuint mask, GLenum coverMode )
 {
-    struct glStencilThenCoverStrokePathNV_params args = { .teb = NtCurrentTeb(), .path = path, .reference = reference, .mask = mask, .coverMode = coverMode };
+    struct glStencilThenCoverStrokePathNV_params args = { .teb = NtCurrentTeb(), .reference = reference, .mask = mask, .coverMode = coverMode };
     NTSTATUS status;
     TRACE( "path %d, reference %d, mask %d, coverMode %d\n", path, reference, mask, coverMode );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &path, TRUE )) return;
+    args.path = *map_context_objects( OBJ_TYPE_PATH, 1, &path );
     if ((status = UNIX_CALL( glStencilThenCoverStrokePathNV, &args ))) WARN( "glStencilThenCoverStrokePathNV returned %#lx\n", status );
 }
 
@@ -18733,9 +19601,10 @@ static void WINAPI glSwizzleEXT( GLuint res, GLuint in, GLenum outX, GLenum outY
 
 static void WINAPI glSyncTextureINTEL( GLuint texture )
 {
-    struct glSyncTextureINTEL_params args = { .teb = NtCurrentTeb(), .texture = texture };
+    struct glSyncTextureINTEL_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "texture %d\n", texture );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glSyncTextureINTEL, &args ))) WARN( "glSyncTextureINTEL returned %#lx\n", status );
 }
 
@@ -18888,41 +19757,46 @@ static GLboolean WINAPI glTestObjectAPPLE( GLenum object, GLuint name )
 
 static void WINAPI glTexAttachMemoryNV( GLenum target, GLuint memory, GLuint64 offset )
 {
-    struct glTexAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .target = target, .memory = memory, .offset = offset };
+    struct glTexAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .target = target, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, memory %d, offset %s\n", target, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexAttachMemoryNV, &args ))) WARN( "glTexAttachMemoryNV returned %#lx\n", status );
 }
 
 static void WINAPI glTexBuffer( GLenum target, GLenum internalformat, GLuint buffer )
 {
-    struct glTexBuffer_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .buffer = buffer };
+    struct glTexBuffer_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat };
     NTSTATUS status;
     TRACE( "target %d, internalformat %d, buffer %d\n", target, internalformat, buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTexBuffer, &args ))) WARN( "glTexBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glTexBufferARB( GLenum target, GLenum internalformat, GLuint buffer )
 {
-    struct glTexBufferARB_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .buffer = buffer };
+    struct glTexBufferARB_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat };
     NTSTATUS status;
     TRACE( "target %d, internalformat %d, buffer %d\n", target, internalformat, buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTexBufferARB, &args ))) WARN( "glTexBufferARB returned %#lx\n", status );
 }
 
 static void WINAPI glTexBufferEXT( GLenum target, GLenum internalformat, GLuint buffer )
 {
-    struct glTexBufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .buffer = buffer };
+    struct glTexBufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat };
     NTSTATUS status;
     TRACE( "target %d, internalformat %d, buffer %d\n", target, internalformat, buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTexBufferEXT, &args ))) WARN( "glTexBufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTexBufferRange( GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glTexBufferRange_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .buffer = buffer, .offset = offset, .size = size };
+    struct glTexBufferRange_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "target %d, internalformat %d, buffer %d, offset %Id, size %Id\n", target, internalformat, buffer, offset, size );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTexBufferRange, &args ))) WARN( "glTexBufferRange returned %#lx\n", status );
 }
 
@@ -19464,9 +20338,10 @@ static void WINAPI glTexPageCommitmentARB( GLenum target, GLint level, GLint xof
 
 static void WINAPI glTexPageCommitmentMemNV( GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit )
 {
-    struct glTexPageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .target = target, .layer = layer, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .memory = memory, .offset = offset, .commit = commit };
+    struct glTexPageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .target = target, .layer = layer, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .offset = offset, .commit = commit };
     NTSTATUS status;
     TRACE( "target %d, layer %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, memory %d, offset %s, commit %d\n", target, layer, level, xoffset, yoffset, zoffset, width, height, depth, memory, wine_dbgstr_longlong(offset), commit );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexPageCommitmentMemNV, &args ))) WARN( "glTexPageCommitmentMemNV returned %#lx\n", status );
 }
 
@@ -19536,9 +20411,10 @@ static void WINAPI glTexParameterxvOES( GLenum target, GLenum pname, const GLfix
 
 static void WINAPI glTexRenderbufferNV( GLenum target, GLuint renderbuffer )
 {
-    struct glTexRenderbufferNV_params args = { .teb = NtCurrentTeb(), .target = target, .renderbuffer = renderbuffer };
+    struct glTexRenderbufferNV_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "target %d, renderbuffer %d\n", target, renderbuffer );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glTexRenderbufferNV, &args ))) WARN( "glTexRenderbufferNV returned %#lx\n", status );
 }
 
@@ -19608,41 +20484,46 @@ static void WINAPI glTexStorage3DMultisample( GLenum target, GLsizei samples, GL
 
 static void WINAPI glTexStorageMem1DEXT( GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset )
 {
-    struct glTexStorageMem1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalFormat = internalFormat, .width = width, .memory = memory, .offset = offset };
+    struct glTexStorageMem1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalFormat = internalFormat, .width = width, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, levels %d, internalFormat %d, width %d, memory %d, offset %s\n", target, levels, internalFormat, width, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexStorageMem1DEXT, &args ))) WARN( "glTexStorageMem1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTexStorageMem2DEXT( GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset )
 {
-    struct glTexStorageMem2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .memory = memory, .offset = offset };
+    struct glTexStorageMem2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, levels %d, internalFormat %d, width %d, height %d, memory %d, offset %s\n", target, levels, internalFormat, width, height, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexStorageMem2DEXT, &args ))) WARN( "glTexStorageMem2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTexStorageMem2DMultisampleEXT( GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset )
 {
-    struct glTexStorageMem2DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations, .memory = memory, .offset = offset };
+    struct glTexStorageMem2DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, samples %d, internalFormat %d, width %d, height %d, fixedSampleLocations %d, memory %d, offset %s\n", target, samples, internalFormat, width, height, fixedSampleLocations, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexStorageMem2DMultisampleEXT, &args ))) WARN( "glTexStorageMem2DMultisampleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTexStorageMem3DEXT( GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset )
 {
-    struct glTexStorageMem3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .memory = memory, .offset = offset };
+    struct glTexStorageMem3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, levels %d, internalFormat %d, width %d, height %d, depth %d, memory %d, offset %s\n", target, levels, internalFormat, width, height, depth, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexStorageMem3DEXT, &args ))) WARN( "glTexStorageMem3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTexStorageMem3DMultisampleEXT( GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset )
 {
-    struct glTexStorageMem3DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations, .memory = memory, .offset = offset };
+    struct glTexStorageMem3DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations, .offset = offset };
     NTSTATUS status;
     TRACE( "target %d, samples %d, internalFormat %d, width %d, height %d, depth %d, fixedSampleLocations %d, memory %d, offset %s\n", target, samples, internalFormat, width, height, depth, fixedSampleLocations, memory, wine_dbgstr_longlong(offset) );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexStorageMem3DMultisampleEXT, &args ))) WARN( "glTexStorageMem3DMultisampleEXT returned %#lx\n", status );
 }
 
@@ -19696,9 +20577,11 @@ static void WINAPI glTexSubImage4DSGIS( GLenum target, GLint level, GLint xoffse
 
 static void WINAPI glTextureAttachMemoryNV( GLuint texture, GLuint memory, GLuint64 offset )
 {
-    struct glTextureAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .memory = memory, .offset = offset };
+    struct glTextureAttachMemoryNV_params args = { .teb = NtCurrentTeb(), .offset = offset };
     NTSTATUS status;
     TRACE( "texture %d, memory %d, offset %s\n", texture, memory, wine_dbgstr_longlong(offset) );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTextureAttachMemoryNV, &args ))) WARN( "glTextureAttachMemoryNV returned %#lx\n", status );
 }
 
@@ -19720,33 +20603,45 @@ static void WINAPI glTextureBarrierNV(void)
 
 static void WINAPI glTextureBuffer( GLuint texture, GLenum internalformat, GLuint buffer )
 {
-    struct glTextureBuffer_params args = { .teb = NtCurrentTeb(), .texture = texture, .internalformat = internalformat, .buffer = buffer };
+    struct glTextureBuffer_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat };
     NTSTATUS status;
     TRACE( "texture %d, internalformat %d, buffer %d\n", texture, internalformat, buffer );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTextureBuffer, &args ))) WARN( "glTextureBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glTextureBufferEXT( GLuint texture, GLenum target, GLenum internalformat, GLuint buffer )
 {
-    struct glTextureBufferEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .internalformat = internalformat, .buffer = buffer };
+    struct glTextureBufferEXT_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat };
     NTSTATUS status;
     TRACE( "texture %d, target %d, internalformat %d, buffer %d\n", texture, target, internalformat, buffer );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTextureBufferEXT, &args ))) WARN( "glTextureBufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureBufferRange( GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glTextureBufferRange_params args = { .teb = NtCurrentTeb(), .texture = texture, .internalformat = internalformat, .buffer = buffer, .offset = offset, .size = size };
+    struct glTextureBufferRange_params args = { .teb = NtCurrentTeb(), .internalformat = internalformat, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "texture %d, internalformat %d, buffer %d, offset %Id, size %Id\n", texture, internalformat, buffer, offset, size );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTextureBufferRange, &args ))) WARN( "glTextureBufferRange returned %#lx\n", status );
 }
 
 static void WINAPI glTextureBufferRangeEXT( GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glTextureBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .internalformat = internalformat, .buffer = buffer, .offset = offset, .size = size };
+    struct glTextureBufferRangeEXT_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "texture %d, target %d, internalformat %d, buffer %d, offset %Id, size %Id\n", texture, target, internalformat, buffer, offset, size );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTextureBufferRangeEXT, &args ))) WARN( "glTextureBufferRangeEXT returned %#lx\n", status );
 }
 
@@ -19760,57 +20655,67 @@ static void WINAPI glTextureColorMaskSGIS( GLboolean red, GLboolean green, GLboo
 
 static void WINAPI glTextureImage1DEXT( GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureImage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .width = width, .border = border, .format = format, .type = type, .pixels = pixels };
+    struct glTextureImage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .width = width, .border = border, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, width %d, border %d, format %d, type %d, pixels %p\n", texture, target, level, internalformat, width, border, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage1DEXT, &args ))) WARN( "glTextureImage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureImage2DEXT( GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureImage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .border = border, .format = format, .type = type, .pixels = pixels };
+    struct glTextureImage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .border = border, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, width %d, height %d, border %d, format %d, type %d, pixels %p\n", texture, target, level, internalformat, width, height, border, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage2DEXT, &args ))) WARN( "glTextureImage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureImage2DMultisampleCoverageNV( GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations )
 {
-    struct glTextureImage2DMultisampleCoverageNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .coverageSamples = coverageSamples, .colorSamples = colorSamples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations };
+    struct glTextureImage2DMultisampleCoverageNV_params args = { .teb = NtCurrentTeb(), .target = target, .coverageSamples = coverageSamples, .colorSamples = colorSamples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations };
     NTSTATUS status;
     TRACE( "texture %d, target %d, coverageSamples %d, colorSamples %d, internalFormat %d, width %d, height %d, fixedSampleLocations %d\n", texture, target, coverageSamples, colorSamples, internalFormat, width, height, fixedSampleLocations );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage2DMultisampleCoverageNV, &args ))) WARN( "glTextureImage2DMultisampleCoverageNV returned %#lx\n", status );
 }
 
 static void WINAPI glTextureImage2DMultisampleNV( GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations )
 {
-    struct glTextureImage2DMultisampleNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations };
+    struct glTextureImage2DMultisampleNV_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations };
     NTSTATUS status;
     TRACE( "texture %d, target %d, samples %d, internalFormat %d, width %d, height %d, fixedSampleLocations %d\n", texture, target, samples, internalFormat, width, height, fixedSampleLocations );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage2DMultisampleNV, &args ))) WARN( "glTextureImage2DMultisampleNV returned %#lx\n", status );
 }
 
 static void WINAPI glTextureImage3DEXT( GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureImage3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .border = border, .format = format, .type = type, .pixels = pixels };
+    struct glTextureImage3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .border = border, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, internalformat %d, width %d, height %d, depth %d, border %d, format %d, type %d, pixels %p\n", texture, target, level, internalformat, width, height, depth, border, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage3DEXT, &args ))) WARN( "glTextureImage3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureImage3DMultisampleCoverageNV( GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations )
 {
-    struct glTextureImage3DMultisampleCoverageNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .coverageSamples = coverageSamples, .colorSamples = colorSamples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations };
+    struct glTextureImage3DMultisampleCoverageNV_params args = { .teb = NtCurrentTeb(), .target = target, .coverageSamples = coverageSamples, .colorSamples = colorSamples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations };
     NTSTATUS status;
     TRACE( "texture %d, target %d, coverageSamples %d, colorSamples %d, internalFormat %d, width %d, height %d, depth %d, fixedSampleLocations %d\n", texture, target, coverageSamples, colorSamples, internalFormat, width, height, depth, fixedSampleLocations );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage3DMultisampleCoverageNV, &args ))) WARN( "glTextureImage3DMultisampleCoverageNV returned %#lx\n", status );
 }
 
 static void WINAPI glTextureImage3DMultisampleNV( GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations )
 {
-    struct glTextureImage3DMultisampleNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations };
+    struct glTextureImage3DMultisampleNV_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations };
     NTSTATUS status;
     TRACE( "texture %d, target %d, samples %d, internalFormat %d, width %d, height %d, depth %d, fixedSampleLocations %d\n", texture, target, samples, internalFormat, width, height, depth, fixedSampleLocations );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureImage3DMultisampleNV, &args ))) WARN( "glTextureImage3DMultisampleNV returned %#lx\n", status );
 }
 
@@ -19840,113 +20745,135 @@ static void WINAPI glTextureNormalEXT( GLenum mode )
 
 static void WINAPI glTexturePageCommitmentEXT( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit )
 {
-    struct glTexturePageCommitmentEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .commit = commit };
+    struct glTexturePageCommitmentEXT_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .commit = commit };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, commit %d\n", texture, level, xoffset, yoffset, zoffset, width, height, depth, commit );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTexturePageCommitmentEXT, &args ))) WARN( "glTexturePageCommitmentEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTexturePageCommitmentMemNV( GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit )
 {
-    struct glTexturePageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .texture = texture, .layer = layer, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .memory = memory, .offset = offset, .commit = commit };
+    struct glTexturePageCommitmentMemNV_params args = { .teb = NtCurrentTeb(), .layer = layer, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .offset = offset, .commit = commit };
     NTSTATUS status;
     TRACE( "texture %d, layer %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, memory %d, offset %s, commit %d\n", texture, layer, level, xoffset, yoffset, zoffset, width, height, depth, memory, wine_dbgstr_longlong(offset), commit );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTexturePageCommitmentMemNV, &args ))) WARN( "glTexturePageCommitmentMemNV returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterIiv( GLuint texture, GLenum pname, const GLint *params )
 {
-    struct glTextureParameterIiv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .params = params };
+    struct glTextureParameterIiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, params %p\n", texture, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterIiv, &args ))) WARN( "glTextureParameterIiv returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterIivEXT( GLuint texture, GLenum target, GLenum pname, const GLint *params )
 {
-    struct glTextureParameterIivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glTextureParameterIivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterIivEXT, &args ))) WARN( "glTextureParameterIivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterIuiv( GLuint texture, GLenum pname, const GLuint *params )
 {
-    struct glTextureParameterIuiv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .params = params };
+    struct glTextureParameterIuiv_params args = { .teb = NtCurrentTeb(), .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, params %p\n", texture, pname, params );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterIuiv, &args ))) WARN( "glTextureParameterIuiv returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterIuivEXT( GLuint texture, GLenum target, GLenum pname, const GLuint *params )
 {
-    struct glTextureParameterIuivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glTextureParameterIuivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterIuivEXT, &args ))) WARN( "glTextureParameterIuivEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterf( GLuint texture, GLenum pname, GLfloat param )
 {
-    struct glTextureParameterf_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .param = param };
+    struct glTextureParameterf_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, param %f\n", texture, pname, param );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterf, &args ))) WARN( "glTextureParameterf returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterfEXT( GLuint texture, GLenum target, GLenum pname, GLfloat param )
 {
-    struct glTextureParameterfEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .param = param };
+    struct glTextureParameterfEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, param %f\n", texture, target, pname, param );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterfEXT, &args ))) WARN( "glTextureParameterfEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterfv( GLuint texture, GLenum pname, const GLfloat *param )
 {
-    struct glTextureParameterfv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .param = param };
+    struct glTextureParameterfv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, param %p\n", texture, pname, param );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterfv, &args ))) WARN( "glTextureParameterfv returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterfvEXT( GLuint texture, GLenum target, GLenum pname, const GLfloat *params )
 {
-    struct glTextureParameterfvEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glTextureParameterfvEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterfvEXT, &args ))) WARN( "glTextureParameterfvEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameteri( GLuint texture, GLenum pname, GLint param )
 {
-    struct glTextureParameteri_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .param = param };
+    struct glTextureParameteri_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, param %d\n", texture, pname, param );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameteri, &args ))) WARN( "glTextureParameteri returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameteriEXT( GLuint texture, GLenum target, GLenum pname, GLint param )
 {
-    struct glTextureParameteriEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .param = param };
+    struct glTextureParameteriEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, param %d\n", texture, target, pname, param );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameteriEXT, &args ))) WARN( "glTextureParameteriEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameteriv( GLuint texture, GLenum pname, const GLint *param )
 {
-    struct glTextureParameteriv_params args = { .teb = NtCurrentTeb(), .texture = texture, .pname = pname, .param = param };
+    struct glTextureParameteriv_params args = { .teb = NtCurrentTeb(), .pname = pname, .param = param };
     NTSTATUS status;
     TRACE( "texture %d, pname %d, param %p\n", texture, pname, param );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameteriv, &args ))) WARN( "glTextureParameteriv returned %#lx\n", status );
 }
 
 static void WINAPI glTextureParameterivEXT( GLuint texture, GLenum target, GLenum pname, const GLint *params )
 {
-    struct glTextureParameterivEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .pname = pname, .params = params };
+    struct glTextureParameterivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .pname = pname, .params = params };
     NTSTATUS status;
     TRACE( "texture %d, target %d, pname %d, params %p\n", texture, target, pname, params );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureParameterivEXT, &args ))) WARN( "glTextureParameterivEXT returned %#lx\n", status );
 }
 
@@ -19960,193 +20887,234 @@ static void WINAPI glTextureRangeAPPLE( GLenum target, GLsizei length, const voi
 
 static void WINAPI glTextureRenderbufferEXT( GLuint texture, GLenum target, GLuint renderbuffer )
 {
-    struct glTextureRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .renderbuffer = renderbuffer };
+    struct glTextureRenderbufferEXT_params args = { .teb = NtCurrentTeb(), .target = target };
     NTSTATUS status;
     TRACE( "texture %d, target %d, renderbuffer %d\n", texture, target, renderbuffer );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.renderbuffer = *map_context_objects( OBJ_TYPE_RENDERBUFFER, 1, &renderbuffer );
     if ((status = UNIX_CALL( glTextureRenderbufferEXT, &args ))) WARN( "glTextureRenderbufferEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage1D( GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width )
 {
-    struct glTextureStorage1D_params args = { .teb = NtCurrentTeb(), .texture = texture, .levels = levels, .internalformat = internalformat, .width = width };
+    struct glTextureStorage1D_params args = { .teb = NtCurrentTeb(), .levels = levels, .internalformat = internalformat, .width = width };
     NTSTATUS status;
     TRACE( "texture %d, levels %d, internalformat %d, width %d\n", texture, levels, internalformat, width );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage1D, &args ))) WARN( "glTextureStorage1D returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage1DEXT( GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width )
 {
-    struct glTextureStorage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .levels = levels, .internalformat = internalformat, .width = width };
+    struct glTextureStorage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalformat = internalformat, .width = width };
     NTSTATUS status;
     TRACE( "texture %d, target %d, levels %d, internalformat %d, width %d\n", texture, target, levels, internalformat, width );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage1DEXT, &args ))) WARN( "glTextureStorage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage2D( GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glTextureStorage2D_params args = { .teb = NtCurrentTeb(), .texture = texture, .levels = levels, .internalformat = internalformat, .width = width, .height = height };
+    struct glTextureStorage2D_params args = { .teb = NtCurrentTeb(), .levels = levels, .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "texture %d, levels %d, internalformat %d, width %d, height %d\n", texture, levels, internalformat, width, height );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage2D, &args ))) WARN( "glTextureStorage2D returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage2DEXT( GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height )
 {
-    struct glTextureStorage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .levels = levels, .internalformat = internalformat, .width = width, .height = height };
+    struct glTextureStorage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalformat = internalformat, .width = width, .height = height };
     NTSTATUS status;
     TRACE( "texture %d, target %d, levels %d, internalformat %d, width %d, height %d\n", texture, target, levels, internalformat, width, height );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage2DEXT, &args ))) WARN( "glTextureStorage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage2DMultisample( GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations )
 {
-    struct glTextureStorage2DMultisample_params args = { .teb = NtCurrentTeb(), .texture = texture, .samples = samples, .internalformat = internalformat, .width = width, .height = height, .fixedsamplelocations = fixedsamplelocations };
+    struct glTextureStorage2DMultisample_params args = { .teb = NtCurrentTeb(), .samples = samples, .internalformat = internalformat, .width = width, .height = height, .fixedsamplelocations = fixedsamplelocations };
     NTSTATUS status;
     TRACE( "texture %d, samples %d, internalformat %d, width %d, height %d, fixedsamplelocations %d\n", texture, samples, internalformat, width, height, fixedsamplelocations );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage2DMultisample, &args ))) WARN( "glTextureStorage2DMultisample returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage2DMultisampleEXT( GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations )
 {
-    struct glTextureStorage2DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .samples = samples, .internalformat = internalformat, .width = width, .height = height, .fixedsamplelocations = fixedsamplelocations };
+    struct glTextureStorage2DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalformat = internalformat, .width = width, .height = height, .fixedsamplelocations = fixedsamplelocations };
     NTSTATUS status;
     TRACE( "texture %d, target %d, samples %d, internalformat %d, width %d, height %d, fixedsamplelocations %d\n", texture, target, samples, internalformat, width, height, fixedsamplelocations );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage2DMultisampleEXT, &args ))) WARN( "glTextureStorage2DMultisampleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage3D( GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth )
 {
-    struct glTextureStorage3D_params args = { .teb = NtCurrentTeb(), .texture = texture, .levels = levels, .internalformat = internalformat, .width = width, .height = height, .depth = depth };
+    struct glTextureStorage3D_params args = { .teb = NtCurrentTeb(), .levels = levels, .internalformat = internalformat, .width = width, .height = height, .depth = depth };
     NTSTATUS status;
     TRACE( "texture %d, levels %d, internalformat %d, width %d, height %d, depth %d\n", texture, levels, internalformat, width, height, depth );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage3D, &args ))) WARN( "glTextureStorage3D returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage3DEXT( GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth )
 {
-    struct glTextureStorage3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .levels = levels, .internalformat = internalformat, .width = width, .height = height, .depth = depth };
+    struct glTextureStorage3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .levels = levels, .internalformat = internalformat, .width = width, .height = height, .depth = depth };
     NTSTATUS status;
     TRACE( "texture %d, target %d, levels %d, internalformat %d, width %d, height %d, depth %d\n", texture, target, levels, internalformat, width, height, depth );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage3DEXT, &args ))) WARN( "glTextureStorage3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage3DMultisample( GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations )
 {
-    struct glTextureStorage3DMultisample_params args = { .teb = NtCurrentTeb(), .texture = texture, .samples = samples, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .fixedsamplelocations = fixedsamplelocations };
+    struct glTextureStorage3DMultisample_params args = { .teb = NtCurrentTeb(), .samples = samples, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .fixedsamplelocations = fixedsamplelocations };
     NTSTATUS status;
     TRACE( "texture %d, samples %d, internalformat %d, width %d, height %d, depth %d, fixedsamplelocations %d\n", texture, samples, internalformat, width, height, depth, fixedsamplelocations );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage3DMultisample, &args ))) WARN( "glTextureStorage3DMultisample returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorage3DMultisampleEXT( GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations )
 {
-    struct glTextureStorage3DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .samples = samples, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .fixedsamplelocations = fixedsamplelocations };
+    struct glTextureStorage3DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .target = target, .samples = samples, .internalformat = internalformat, .width = width, .height = height, .depth = depth, .fixedsamplelocations = fixedsamplelocations };
     NTSTATUS status;
     TRACE( "texture %d, target %d, samples %d, internalformat %d, width %d, height %d, depth %d, fixedsamplelocations %d\n", texture, target, samples, internalformat, width, height, depth, fixedsamplelocations );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorage3DMultisampleEXT, &args ))) WARN( "glTextureStorage3DMultisampleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorageMem1DEXT( GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset )
 {
-    struct glTextureStorageMem1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .levels = levels, .internalFormat = internalFormat, .width = width, .memory = memory, .offset = offset };
+    struct glTextureStorageMem1DEXT_params args = { .teb = NtCurrentTeb(), .levels = levels, .internalFormat = internalFormat, .width = width, .offset = offset };
     NTSTATUS status;
     TRACE( "texture %d, levels %d, internalFormat %d, width %d, memory %d, offset %s\n", texture, levels, internalFormat, width, memory, wine_dbgstr_longlong(offset) );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTextureStorageMem1DEXT, &args ))) WARN( "glTextureStorageMem1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorageMem2DEXT( GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset )
 {
-    struct glTextureStorageMem2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .memory = memory, .offset = offset };
+    struct glTextureStorageMem2DEXT_params args = { .teb = NtCurrentTeb(), .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .offset = offset };
     NTSTATUS status;
     TRACE( "texture %d, levels %d, internalFormat %d, width %d, height %d, memory %d, offset %s\n", texture, levels, internalFormat, width, height, memory, wine_dbgstr_longlong(offset) );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTextureStorageMem2DEXT, &args ))) WARN( "glTextureStorageMem2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorageMem2DMultisampleEXT( GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset )
 {
-    struct glTextureStorageMem2DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations, .memory = memory, .offset = offset };
+    struct glTextureStorageMem2DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .fixedSampleLocations = fixedSampleLocations, .offset = offset };
     NTSTATUS status;
     TRACE( "texture %d, samples %d, internalFormat %d, width %d, height %d, fixedSampleLocations %d, memory %d, offset %s\n", texture, samples, internalFormat, width, height, fixedSampleLocations, memory, wine_dbgstr_longlong(offset) );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTextureStorageMem2DMultisampleEXT, &args ))) WARN( "glTextureStorageMem2DMultisampleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorageMem3DEXT( GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset )
 {
-    struct glTextureStorageMem3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .memory = memory, .offset = offset };
+    struct glTextureStorageMem3DEXT_params args = { .teb = NtCurrentTeb(), .levels = levels, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .offset = offset };
     NTSTATUS status;
     TRACE( "texture %d, levels %d, internalFormat %d, width %d, height %d, depth %d, memory %d, offset %s\n", texture, levels, internalFormat, width, height, depth, memory, wine_dbgstr_longlong(offset) );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTextureStorageMem3DEXT, &args ))) WARN( "glTextureStorageMem3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorageMem3DMultisampleEXT( GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset )
 {
-    struct glTextureStorageMem3DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations, .memory = memory, .offset = offset };
+    struct glTextureStorageMem3DMultisampleEXT_params args = { .teb = NtCurrentTeb(), .samples = samples, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .fixedSampleLocations = fixedSampleLocations, .offset = offset };
     NTSTATUS status;
     TRACE( "texture %d, samples %d, internalFormat %d, width %d, height %d, depth %d, fixedSampleLocations %d, memory %d, offset %s\n", texture, samples, internalFormat, width, height, depth, fixedSampleLocations, memory, wine_dbgstr_longlong(offset) );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.memory = *map_context_objects( OBJ_TYPE_MEMORY, 1, &memory );
     if ((status = UNIX_CALL( glTextureStorageMem3DMultisampleEXT, &args ))) WARN( "glTextureStorageMem3DMultisampleEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureStorageSparseAMD( GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags )
 {
-    struct glTextureStorageSparseAMD_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .layers = layers, .flags = flags };
+    struct glTextureStorageSparseAMD_params args = { .teb = NtCurrentTeb(), .target = target, .internalFormat = internalFormat, .width = width, .height = height, .depth = depth, .layers = layers, .flags = flags };
     NTSTATUS status;
     TRACE( "texture %d, target %d, internalFormat %d, width %d, height %d, depth %d, layers %d, flags %d\n", texture, target, internalFormat, width, height, depth, layers, flags );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureStorageSparseAMD, &args ))) WARN( "glTextureStorageSparseAMD returned %#lx\n", status );
 }
 
 static void WINAPI glTextureSubImage1D( GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureSubImage1D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .width = width, .format = format, .type = type, .pixels = pixels };
+    struct glTextureSubImage1D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .width = width, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, width %d, format %d, type %d, pixels %p\n", texture, level, xoffset, width, format, type, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureSubImage1D, &args ))) WARN( "glTextureSubImage1D returned %#lx\n", status );
 }
 
 static void WINAPI glTextureSubImage1DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureSubImage1DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .width = width, .format = format, .type = type, .pixels = pixels };
+    struct glTextureSubImage1DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .width = width, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, width %d, format %d, type %d, pixels %p\n", texture, target, level, xoffset, width, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureSubImage1DEXT, &args ))) WARN( "glTextureSubImage1DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureSubImage2D( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureSubImage2D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .type = type, .pixels = pixels };
+    struct glTextureSubImage2D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, width %d, height %d, format %d, type %d, pixels %p\n", texture, level, xoffset, yoffset, width, height, format, type, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureSubImage2D, &args ))) WARN( "glTextureSubImage2D returned %#lx\n", status );
 }
 
 static void WINAPI glTextureSubImage2DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureSubImage2DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .type = type, .pixels = pixels };
+    struct glTextureSubImage2DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .width = width, .height = height, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, yoffset %d, width %d, height %d, format %d, type %d, pixels %p\n", texture, target, level, xoffset, yoffset, width, height, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureSubImage2DEXT, &args ))) WARN( "glTextureSubImage2DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureSubImage3D( GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureSubImage3D_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .pixels = pixels };
+    struct glTextureSubImage3D_params args = { .teb = NtCurrentTeb(), .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, format %d, type %d, pixels %p\n", texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureSubImage3D, &args ))) WARN( "glTextureSubImage3D returned %#lx\n", status );
 }
 
 static void WINAPI glTextureSubImage3DEXT( GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels )
 {
-    struct glTextureSubImage3DEXT_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .pixels = pixels };
+    struct glTextureSubImage3DEXT_params args = { .teb = NtCurrentTeb(), .target = target, .level = level, .xoffset = xoffset, .yoffset = yoffset, .zoffset = zoffset, .width = width, .height = height, .depth = depth, .format = format, .type = type, .pixels = pixels };
     NTSTATUS status;
     TRACE( "texture %d, target %d, level %d, xoffset %d, yoffset %d, zoffset %d, width %d, height %d, depth %d, format %d, type %d, pixels %p\n", texture, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels );
+    if (!alloc_context_objects( OBJ_TYPE_TEXTURE, 1, &texture, TRUE )) return;
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glTextureSubImage3DEXT, &args ))) WARN( "glTextureSubImage3DEXT returned %#lx\n", status );
 }
 
 static void WINAPI glTextureView( GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers )
 {
-    struct glTextureView_params args = { .teb = NtCurrentTeb(), .texture = texture, .target = target, .origtexture = origtexture, .internalformat = internalformat, .minlevel = minlevel, .numlevels = numlevels, .minlayer = minlayer, .numlayers = numlayers };
+    struct glTextureView_params args = { .teb = NtCurrentTeb(), .target = target, .internalformat = internalformat, .minlevel = minlevel, .numlevels = numlevels, .minlayer = minlayer, .numlayers = numlayers };
     NTSTATUS status;
     TRACE( "texture %d, target %d, origtexture %d, internalformat %d, minlevel %d, numlevels %d, minlayer %d, numlayers %d\n", texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
+    args.origtexture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &origtexture );
     if ((status = UNIX_CALL( glTextureView, &args ))) WARN( "glTextureView returned %#lx\n", status );
 }
 
@@ -20168,17 +21136,19 @@ static void WINAPI glTransformFeedbackAttribsNV( GLsizei count, const GLint *att
 
 static void WINAPI glTransformFeedbackBufferBase( GLuint xfb, GLuint index, GLuint buffer )
 {
-    struct glTransformFeedbackBufferBase_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .index = index, .buffer = buffer };
+    struct glTransformFeedbackBufferBase_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .index = index };
     NTSTATUS status;
     TRACE( "xfb %d, index %d, buffer %d\n", xfb, index, buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTransformFeedbackBufferBase, &args ))) WARN( "glTransformFeedbackBufferBase returned %#lx\n", status );
 }
 
 static void WINAPI glTransformFeedbackBufferRange( GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size )
 {
-    struct glTransformFeedbackBufferRange_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .index = index, .buffer = buffer, .offset = offset, .size = size };
+    struct glTransformFeedbackBufferRange_params args = { .teb = NtCurrentTeb(), .xfb = xfb, .index = index, .offset = offset, .size = size };
     NTSTATUS status;
     TRACE( "xfb %d, index %d, buffer %d, offset %Id, size %Id\n", xfb, index, buffer, offset, size );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glTransformFeedbackBufferRange, &args ))) WARN( "glTransformFeedbackBufferRange returned %#lx\n", status );
 }
 
@@ -20216,9 +21186,13 @@ static void WINAPI glTransformFeedbackVaryingsNV( GLuint program, GLsizei count,
 
 static void WINAPI glTransformPathNV( GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues )
 {
-    struct glTransformPathNV_params args = { .teb = NtCurrentTeb(), .resultPath = resultPath, .srcPath = srcPath, .transformType = transformType, .transformValues = transformValues };
+    struct glTransformPathNV_params args = { .teb = NtCurrentTeb(), .transformType = transformType, .transformValues = transformValues };
     NTSTATUS status;
     TRACE( "resultPath %d, srcPath %d, transformType %d, transformValues %p\n", resultPath, srcPath, transformType, transformValues );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &resultPath, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &srcPath, TRUE )) return;
+    args.resultPath = *map_context_objects( OBJ_TYPE_PATH, 1, &resultPath );
+    args.srcPath = *map_context_objects( OBJ_TYPE_PATH, 1, &srcPath );
     if ((status = UNIX_CALL( glTransformPathNV, &args ))) WARN( "glTransformPathNV returned %#lx\n", status );
 }
 
@@ -20952,9 +21926,10 @@ static void WINAPI glUniformBlockBinding( GLuint program, GLuint uniformBlockInd
 
 static void WINAPI glUniformBufferEXT( GLuint program, GLint location, GLuint buffer )
 {
-    struct glUniformBufferEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location, .buffer = buffer };
+    struct glUniformBufferEXT_params args = { .teb = NtCurrentTeb(), .program = program, .location = location };
     NTSTATUS status;
     TRACE( "program %d, location %d, buffer %d\n", program, location, buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glUniformBufferEXT, &args ))) WARN( "glUniformBufferEXT returned %#lx\n", status );
 }
 
@@ -21210,43 +22185,49 @@ static GLboolean WINAPI glUnmapBufferARB( GLenum target )
 
 static GLboolean WINAPI glUnmapNamedBuffer( GLuint buffer )
 {
-    struct glUnmapNamedBuffer_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glUnmapNamedBuffer_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glUnmapNamedBuffer, &args ))) WARN( "glUnmapNamedBuffer returned %#lx\n", status );
     return args.ret;
 }
 
 static GLboolean WINAPI glUnmapNamedBufferEXT( GLuint buffer )
 {
-    struct glUnmapNamedBufferEXT_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glUnmapNamedBufferEXT_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return args.ret;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glUnmapNamedBufferEXT, &args ))) WARN( "glUnmapNamedBufferEXT returned %#lx\n", status );
     return args.ret;
 }
 
 static void WINAPI glUnmapObjectBufferATI( GLuint buffer )
 {
-    struct glUnmapObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer };
+    struct glUnmapObjectBufferATI_params args = { .teb = NtCurrentTeb() };
     NTSTATUS status;
     TRACE( "buffer %d\n", buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glUnmapObjectBufferATI, &args ))) WARN( "glUnmapObjectBufferATI returned %#lx\n", status );
 }
 
 static void WINAPI glUnmapTexture2DINTEL( GLuint texture, GLint level )
 {
-    struct glUnmapTexture2DINTEL_params args = { .teb = NtCurrentTeb(), .texture = texture, .level = level };
+    struct glUnmapTexture2DINTEL_params args = { .teb = NtCurrentTeb(), .level = level };
     NTSTATUS status;
     TRACE( "texture %d, level %d\n", texture, level );
+    args.texture = *map_context_objects( OBJ_TYPE_TEXTURE, 1, &texture );
     if ((status = UNIX_CALL( glUnmapTexture2DINTEL, &args ))) WARN( "glUnmapTexture2DINTEL returned %#lx\n", status );
 }
 
 static void WINAPI glUpdateObjectBufferATI( GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve )
 {
-    struct glUpdateObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer, .offset = offset, .size = size, .pointer = pointer, .preserve = preserve };
+    struct glUpdateObjectBufferATI_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .pointer = pointer, .preserve = preserve };
     NTSTATUS status;
     TRACE( "buffer %d, offset %d, size %d, pointer %p, preserve %d\n", buffer, offset, size, pointer, preserve );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glUpdateObjectBufferATI, &args ))) WARN( "glUpdateObjectBufferATI returned %#lx\n", status );
 }
 
@@ -21333,28 +22314,40 @@ static void WINAPI glVDPAUMapSurfacesNV( GLsizei numSurfaces, const GLvdpauSurfa
 
 static GLvdpauSurfaceNV WINAPI glVDPAURegisterOutputSurfaceNV( const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames )
 {
-    struct glVDPAURegisterOutputSurfaceNV_params args = { .teb = NtCurrentTeb(), .vdpSurface = vdpSurface, .target = target, .numTextureNames = numTextureNames, .textureNames = textureNames };
+    GLuint textureNames_buf[64], *textureNames_tmp;
+    struct glVDPAURegisterOutputSurfaceNV_params args = { .teb = NtCurrentTeb(), .vdpSurface = vdpSurface, .target = target, .numTextureNames = numTextureNames };
     NTSTATUS status;
     TRACE( "vdpSurface %p, target %d, numTextureNames %d, textureNames %p\n", vdpSurface, target, numTextureNames, textureNames );
+    textureNames_tmp = numTextureNames > 0 ? memdup_objects( numTextureNames, textureNames, textureNames_buf, ARRAY_SIZE(textureNames_buf) ) : NULL;
+    args.textureNames = numTextureNames > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, numTextureNames, textureNames_tmp ) : NULL;
     if ((status = UNIX_CALL( glVDPAURegisterOutputSurfaceNV, &args ))) WARN( "glVDPAURegisterOutputSurfaceNV returned %#lx\n", status );
+    if (textureNames_tmp != textureNames_buf) free( textureNames_tmp );
     return args.ret;
 }
 
 static GLvdpauSurfaceNV WINAPI glVDPAURegisterVideoSurfaceNV( const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames )
 {
-    struct glVDPAURegisterVideoSurfaceNV_params args = { .teb = NtCurrentTeb(), .vdpSurface = vdpSurface, .target = target, .numTextureNames = numTextureNames, .textureNames = textureNames };
+    GLuint textureNames_buf[64], *textureNames_tmp;
+    struct glVDPAURegisterVideoSurfaceNV_params args = { .teb = NtCurrentTeb(), .vdpSurface = vdpSurface, .target = target, .numTextureNames = numTextureNames };
     NTSTATUS status;
     TRACE( "vdpSurface %p, target %d, numTextureNames %d, textureNames %p\n", vdpSurface, target, numTextureNames, textureNames );
+    textureNames_tmp = numTextureNames > 0 ? memdup_objects( numTextureNames, textureNames, textureNames_buf, ARRAY_SIZE(textureNames_buf) ) : NULL;
+    args.textureNames = numTextureNames > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, numTextureNames, textureNames_tmp ) : NULL;
     if ((status = UNIX_CALL( glVDPAURegisterVideoSurfaceNV, &args ))) WARN( "glVDPAURegisterVideoSurfaceNV returned %#lx\n", status );
+    if (textureNames_tmp != textureNames_buf) free( textureNames_tmp );
     return args.ret;
 }
 
 static GLvdpauSurfaceNV WINAPI glVDPAURegisterVideoSurfaceWithPictureStructureNV( const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure )
 {
-    struct glVDPAURegisterVideoSurfaceWithPictureStructureNV_params args = { .teb = NtCurrentTeb(), .vdpSurface = vdpSurface, .target = target, .numTextureNames = numTextureNames, .textureNames = textureNames, .isFrameStructure = isFrameStructure };
+    GLuint textureNames_buf[64], *textureNames_tmp;
+    struct glVDPAURegisterVideoSurfaceWithPictureStructureNV_params args = { .teb = NtCurrentTeb(), .vdpSurface = vdpSurface, .target = target, .numTextureNames = numTextureNames, .isFrameStructure = isFrameStructure };
     NTSTATUS status;
     TRACE( "vdpSurface %p, target %d, numTextureNames %d, textureNames %p, isFrameStructure %d\n", vdpSurface, target, numTextureNames, textureNames, isFrameStructure );
+    textureNames_tmp = numTextureNames > 0 ? memdup_objects( numTextureNames, textureNames, textureNames_buf, ARRAY_SIZE(textureNames_buf) ) : NULL;
+    args.textureNames = numTextureNames > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, numTextureNames, textureNames_tmp ) : NULL;
     if ((status = UNIX_CALL( glVDPAURegisterVideoSurfaceWithPictureStructureNV, &args ))) WARN( "glVDPAURegisterVideoSurfaceWithPictureStructureNV returned %#lx\n", status );
+    if (textureNames_tmp != textureNames_buf) free( textureNames_tmp );
     return args.ret;
 }
 
@@ -21408,9 +22401,10 @@ static void WINAPI glValidateProgramPipeline( GLuint pipeline )
 
 static void WINAPI glVariantArrayObjectATI( GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset )
 {
-    struct glVariantArrayObjectATI_params args = { .teb = NtCurrentTeb(), .id = id, .type = type, .stride = stride, .buffer = buffer, .offset = offset };
+    struct glVariantArrayObjectATI_params args = { .teb = NtCurrentTeb(), .id = id, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "id %d, type %d, stride %d, buffer %d, offset %d\n", id, type, stride, buffer, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVariantArrayObjectATI, &args ))) WARN( "glVariantArrayObjectATI returned %#lx\n", status );
 }
 
@@ -21664,9 +22658,11 @@ static void WINAPI glVertexArrayAttribLFormat( GLuint vaobj, GLuint attribindex,
 
 static void WINAPI glVertexArrayBindVertexBufferEXT( GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride )
 {
-    struct glVertexArrayBindVertexBufferEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .bindingindex = bindingindex, .buffer = buffer, .offset = offset, .stride = stride };
+    struct glVertexArrayBindVertexBufferEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .bindingindex = bindingindex, .offset = offset, .stride = stride };
     NTSTATUS status;
     TRACE( "vaobj %d, bindingindex %d, buffer %d, offset %Id, stride %d\n", vaobj, bindingindex, buffer, offset, stride );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayBindVertexBufferEXT, &args ))) WARN( "glVertexArrayBindVertexBufferEXT returned %#lx\n", status );
 }
 
@@ -21680,57 +22676,70 @@ static void WINAPI glVertexArrayBindingDivisor( GLuint vaobj, GLuint bindinginde
 
 static void WINAPI glVertexArrayColorOffsetEXT( GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayColorOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayColorOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayColorOffsetEXT, &args ))) WARN( "glVertexArrayColorOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayEdgeFlagOffsetEXT( GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayEdgeFlagOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .stride = stride, .offset = offset };
+    struct glVertexArrayEdgeFlagOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, stride %d, offset %Id\n", vaobj, buffer, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayEdgeFlagOffsetEXT, &args ))) WARN( "glVertexArrayEdgeFlagOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayElementBuffer( GLuint vaobj, GLuint buffer )
 {
-    struct glVertexArrayElementBuffer_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer };
+    struct glVertexArrayElementBuffer_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d\n", vaobj, buffer );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayElementBuffer, &args ))) WARN( "glVertexArrayElementBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayFogCoordOffsetEXT( GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayFogCoordOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayFogCoordOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, type %d, stride %d, offset %Id\n", vaobj, buffer, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayFogCoordOffsetEXT, &args ))) WARN( "glVertexArrayFogCoordOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayIndexOffsetEXT( GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayIndexOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayIndexOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, type %d, stride %d, offset %Id\n", vaobj, buffer, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayIndexOffsetEXT, &args ))) WARN( "glVertexArrayIndexOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayMultiTexCoordOffsetEXT( GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayMultiTexCoordOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .texunit = texunit, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayMultiTexCoordOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .texunit = texunit, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, texunit %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, texunit, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayMultiTexCoordOffsetEXT, &args ))) WARN( "glVertexArrayMultiTexCoordOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayNormalOffsetEXT( GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayNormalOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayNormalOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, type %d, stride %d, offset %Id\n", vaobj, buffer, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayNormalOffsetEXT, &args ))) WARN( "glVertexArrayNormalOffsetEXT returned %#lx\n", status );
 }
 
@@ -21760,17 +22769,21 @@ static void WINAPI glVertexArrayRangeNV( GLsizei length, const void *pointer )
 
 static void WINAPI glVertexArraySecondaryColorOffsetEXT( GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArraySecondaryColorOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArraySecondaryColorOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArraySecondaryColorOffsetEXT, &args ))) WARN( "glVertexArraySecondaryColorOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayTexCoordOffsetEXT( GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayTexCoordOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayTexCoordOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayTexCoordOffsetEXT, &args ))) WARN( "glVertexArrayTexCoordOffsetEXT returned %#lx\n", status );
 }
 
@@ -21808,9 +22821,11 @@ static void WINAPI glVertexArrayVertexAttribIFormatEXT( GLuint vaobj, GLuint att
 
 static void WINAPI glVertexArrayVertexAttribIOffsetEXT( GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayVertexAttribIOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .index = index, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayVertexAttribIOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, index %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, index, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayVertexAttribIOffsetEXT, &args ))) WARN( "glVertexArrayVertexAttribIOffsetEXT returned %#lx\n", status );
 }
 
@@ -21824,17 +22839,21 @@ static void WINAPI glVertexArrayVertexAttribLFormatEXT( GLuint vaobj, GLuint att
 
 static void WINAPI glVertexArrayVertexAttribLOffsetEXT( GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayVertexAttribLOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .index = index, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayVertexAttribLOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, index %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, index, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayVertexAttribLOffsetEXT, &args ))) WARN( "glVertexArrayVertexAttribLOffsetEXT returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayVertexAttribOffsetEXT( GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayVertexAttribOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .index = index, .size = size, .type = type, .normalized = normalized, .stride = stride, .offset = offset };
+    struct glVertexArrayVertexAttribOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .index = index, .size = size, .type = type, .normalized = normalized, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, index %d, size %d, type %d, normalized %d, stride %d, offset %Id\n", vaobj, buffer, index, size, type, normalized, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayVertexAttribOffsetEXT, &args ))) WARN( "glVertexArrayVertexAttribOffsetEXT returned %#lx\n", status );
 }
 
@@ -21848,25 +22867,32 @@ static void WINAPI glVertexArrayVertexBindingDivisorEXT( GLuint vaobj, GLuint bi
 
 static void WINAPI glVertexArrayVertexBuffer( GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride )
 {
-    struct glVertexArrayVertexBuffer_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .bindingindex = bindingindex, .buffer = buffer, .offset = offset, .stride = stride };
+    struct glVertexArrayVertexBuffer_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .bindingindex = bindingindex, .offset = offset, .stride = stride };
     NTSTATUS status;
     TRACE( "vaobj %d, bindingindex %d, buffer %d, offset %Id, stride %d\n", vaobj, bindingindex, buffer, offset, stride );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayVertexBuffer, &args ))) WARN( "glVertexArrayVertexBuffer returned %#lx\n", status );
 }
 
 static void WINAPI glVertexArrayVertexBuffers( GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides )
 {
-    struct glVertexArrayVertexBuffers_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .first = first, .count = count, .buffers = buffers, .offsets = offsets, .strides = strides };
+    GLuint buffers_buf[64], *buffers_tmp;
+    struct glVertexArrayVertexBuffers_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .first = first, .count = count, .offsets = offsets, .strides = strides };
     NTSTATUS status;
     TRACE( "vaobj %d, first %d, count %d, buffers %p, offsets %p, strides %p\n", vaobj, first, count, buffers, offsets, strides );
+    buffers_tmp = count > 0 ? memdup_objects( count, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = count > 0 ? map_context_objects( OBJ_TYPE_BUFFER, count, buffers_tmp ) : NULL;
     if ((status = UNIX_CALL( glVertexArrayVertexBuffers, &args ))) WARN( "glVertexArrayVertexBuffers returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
 }
 
 static void WINAPI glVertexArrayVertexOffsetEXT( GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset )
 {
-    struct glVertexArrayVertexOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .buffer = buffer, .size = size, .type = type, .stride = stride, .offset = offset };
+    struct glVertexArrayVertexOffsetEXT_params args = { .teb = NtCurrentTeb(), .vaobj = vaobj, .size = size, .type = type, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "vaobj %d, buffer %d, size %d, type %d, stride %d, offset %Id\n", vaobj, buffer, size, type, stride, offset );
+    if (!alloc_context_objects( OBJ_TYPE_BUFFER, 1, &buffer, TRUE )) return;
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexArrayVertexOffsetEXT, &args ))) WARN( "glVertexArrayVertexOffsetEXT returned %#lx\n", status );
 }
 
@@ -22720,9 +23746,10 @@ static void WINAPI glVertexAttrib4usvARB( GLuint index, const GLushort *v )
 
 static void WINAPI glVertexAttribArrayObjectATI( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset )
 {
-    struct glVertexAttribArrayObjectATI_params args = { .teb = NtCurrentTeb(), .index = index, .size = size, .type = type, .normalized = normalized, .stride = stride, .buffer = buffer, .offset = offset };
+    struct glVertexAttribArrayObjectATI_params args = { .teb = NtCurrentTeb(), .index = index, .size = size, .type = type, .normalized = normalized, .stride = stride, .offset = offset };
     NTSTATUS status;
     TRACE( "index %d, size %d, type %d, normalized %d, stride %d, buffer %d, offset %d\n", index, size, type, normalized, stride, buffer, offset );
+    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
     if ((status = UNIX_CALL( glVertexAttribArrayObjectATI, &args ))) WARN( "glVertexAttribArrayObjectATI returned %#lx\n", status );
 }
 
@@ -24137,18 +25164,31 @@ static void WINAPI glViewportSwizzleNV( GLuint index, GLenum swizzlex, GLenum sw
 
 static void WINAPI glWaitSemaphoreEXT( GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts )
 {
-    struct glWaitSemaphoreEXT_params args = { .teb = NtCurrentTeb(), .semaphore = semaphore, .numBufferBarriers = numBufferBarriers, .buffers = buffers, .numTextureBarriers = numTextureBarriers, .textures = textures, .srcLayouts = srcLayouts };
+    GLuint buffers_buf[64], *buffers_tmp;
+    GLuint textures_buf[64], *textures_tmp;
+    struct glWaitSemaphoreEXT_params args = { .teb = NtCurrentTeb(), .numBufferBarriers = numBufferBarriers, .numTextureBarriers = numTextureBarriers, .srcLayouts = srcLayouts };
     NTSTATUS status;
     TRACE( "semaphore %d, numBufferBarriers %d, buffers %p, numTextureBarriers %d, textures %p, srcLayouts %p\n", semaphore, numBufferBarriers, buffers, numTextureBarriers, textures, srcLayouts );
+    args.semaphore = *map_context_objects( OBJ_TYPE_SEMAPHORE, 1, &semaphore );
+    buffers_tmp = numBufferBarriers > 0 ? memdup_objects( numBufferBarriers, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL;
+    args.buffers = numBufferBarriers > 0 ? map_context_objects( OBJ_TYPE_BUFFER, numBufferBarriers, buffers_tmp ) : NULL;
+    textures_tmp = numTextureBarriers > 0 ? memdup_objects( numTextureBarriers, textures, textures_buf, ARRAY_SIZE(textures_buf) ) : NULL;
+    args.textures = numTextureBarriers > 0 ? map_context_objects( OBJ_TYPE_TEXTURE, numTextureBarriers, textures_tmp ) : NULL;
     if ((status = UNIX_CALL( glWaitSemaphoreEXT, &args ))) WARN( "glWaitSemaphoreEXT returned %#lx\n", status );
+    if (buffers_tmp != buffers_buf) free( buffers_tmp );
+    if (textures_tmp != textures_buf) free( textures_tmp );
 }
 
 static void WINAPI glWaitSemaphoreui64NVX( GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray )
 {
-    struct glWaitSemaphoreui64NVX_params args = { .teb = NtCurrentTeb(), .waitGpu = waitGpu, .fenceObjectCount = fenceObjectCount, .semaphoreArray = semaphoreArray, .fenceValueArray = fenceValueArray };
+    GLuint semaphoreArray_buf[64], *semaphoreArray_tmp;
+    struct glWaitSemaphoreui64NVX_params args = { .teb = NtCurrentTeb(), .waitGpu = waitGpu, .fenceObjectCount = fenceObjectCount, .fenceValueArray = fenceValueArray };
     NTSTATUS status;
     TRACE( "waitGpu %d, fenceObjectCount %d, semaphoreArray %p, fenceValueArray %p\n", waitGpu, fenceObjectCount, semaphoreArray, fenceValueArray );
+    semaphoreArray_tmp = fenceObjectCount > 0 ? memdup_objects( fenceObjectCount, semaphoreArray, semaphoreArray_buf, ARRAY_SIZE(semaphoreArray_buf) ) : NULL;
+    args.semaphoreArray = fenceObjectCount > 0 ? map_context_objects( OBJ_TYPE_SEMAPHORE, fenceObjectCount, semaphoreArray_tmp ) : NULL;
     if ((status = UNIX_CALL( glWaitSemaphoreui64NVX, &args ))) WARN( "glWaitSemaphoreui64NVX returned %#lx\n", status );
+    if (semaphoreArray_tmp != semaphoreArray_buf) free( semaphoreArray_tmp );
 }
 
 static void WINAPI glWaitSync( GLsync sync, GLbitfield flags, GLuint64 timeout )
@@ -24170,10 +25210,17 @@ static void WINAPI glWaitVkSemaphoreNV( GLuint64 vkSemaphore )
 
 static void WINAPI glWeightPathsNV( GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights )
 {
-    struct glWeightPathsNV_params args = { .teb = NtCurrentTeb(), .resultPath = resultPath, .numPaths = numPaths, .paths = paths, .weights = weights };
+    GLuint paths_buf[64], *paths_tmp;
+    struct glWeightPathsNV_params args = { .teb = NtCurrentTeb(), .numPaths = numPaths, .weights = weights };
     NTSTATUS status;
     TRACE( "resultPath %d, numPaths %d, paths %p, weights %p\n", resultPath, numPaths, paths, weights );
+    if (!alloc_context_objects( OBJ_TYPE_PATH, 1, &resultPath, TRUE )) return;
+    if (!alloc_context_objects( OBJ_TYPE_PATH, numPaths, paths, TRUE )) return;
+    args.resultPath = *map_context_objects( OBJ_TYPE_PATH, 1, &resultPath );
+    paths_tmp = numPaths > 0 ? memdup_objects( numPaths, paths, paths_buf, ARRAY_SIZE(paths_buf) ) : NULL;
+    args.paths = numPaths > 0 ? map_context_objects( OBJ_TYPE_PATH, numPaths, paths_tmp ) : NULL;
     if ((status = UNIX_CALL( glWeightPathsNV, &args ))) WARN( "glWeightPathsNV returned %#lx\n", status );
+    if (paths_tmp != paths_buf) free( paths_tmp );
 }
 
 static void WINAPI glWeightPointerARB( GLint size, GLenum type, GLsizei stride, const void *pointer )
diff --git a/dlls/opengl32/thunks.h b/dlls/opengl32/thunks.h
index 67d5691ed8ce..52a406e90ed6 100644
--- a/dlls/opengl32/thunks.h
+++ b/dlls/opengl32/thunks.h
@@ -3,6 +3,10 @@
 extern GLsync WINAPI glCreateSyncFromCLeventARB( struct _cl_context *context, struct _cl_event *event, GLbitfield flags );
 extern void WINAPI glDeleteSync( GLsync sync );
 extern GLsync WINAPI glFenceSync( GLenum condition, GLbitfield flags );
+extern void WINAPI glGetFramebufferAttachmentParameteriv( GLenum target, GLenum attachment, GLenum pname, GLint *params );
+extern void WINAPI glGetFramebufferAttachmentParameterivEXT( GLenum target, GLenum attachment, GLenum pname, GLint *params );
+extern void WINAPI glGetNamedFramebufferAttachmentParameteriv( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params );
+extern void WINAPI glGetNamedFramebufferAttachmentParameterivEXT( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params );
 extern const GLubyte * WINAPI glGetStringi( GLenum name, GLuint index );
 extern GLsync WINAPI glImportSyncEXT( GLenum external_sync_type, GLintptr external_sync, GLbitfield flags );
 extern BOOL WINAPI wglChoosePixelFormatARB( HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats );
diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c
index 762c31578d87..fbf5654aeaac 100644
--- a/dlls/opengl32/unix_wgl.c
+++ b/dlls/opengl32/unix_wgl.c
@@ -1083,6 +1083,7 @@ static void make_context_current( TEB *teb, const struct opengl_funcs *funcs, HD
     TRACE( "context %p version %d.%d\n", ctx, client->major_version, client->minor_version );
 
     funcs->p_init_extensions( client->extensions );
+    funcs->p_glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &client->profile_mask );
 
     if (client->major_version >= 3)
     {
diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c
index f4ca0e924b9c..d7f27dfab34e 100644
--- a/dlls/opengl32/wgl.c
+++ b/dlls/opengl32/wgl.c
@@ -101,6 +101,27 @@ static void cleanup_wow64_strings(void)
 
 #endif
 
+static const char *debugstr_object_type( enum object_type type )
+{
+    switch (type)
+    {
+    case OBJ_TYPE_BUFFER: return "buffer";
+    case OBJ_TYPE_DISPLAY_LIST: return "display list";
+    case OBJ_TYPE_FRAMEBUFFER: return "framebuffer";
+    case OBJ_TYPE_MEMORY: return "memory";
+    case OBJ_TYPE_PATH: return "path";
+    case OBJ_TYPE_PROGRAM: return "program";
+    case OBJ_TYPE_RENDERBUFFER: return "renderbuffer";
+    case OBJ_TYPE_SEMAPHORE: return "semaphore";
+    case OBJ_TYPE_SAMPLER: return "sampler";
+    case OBJ_TYPE_SHADER_ATI: return "fragment shader";
+    case OBJ_TYPE_SHADER_EXT: return "vertex shader";
+    case OBJ_TYPE_TEXTURE: return "texture";
+    case OBJ_TYPE_COUNT: break;
+    }
+    return wine_dbg_sprintf( "object (type %u)", type );
+}
+
 static void init_wgl_extensions( const BOOLEAN extensions[GL_EXTENSION_COUNT] )
 {
     UINT pos = 0, len = 0, ext;
@@ -259,10 +280,197 @@ BOOL WINAPI wglDestroyPbufferARB( HPBUFFERARB handle )
     return args.ret;
 }
 
+#define L1_COUNT   0x80
+#define L2_COUNT  0x400
+#define L3_COUNT 0x8000
+
+struct object_table
+{
+    enum object_type    type;                   /* object type of the id table */
+    SRWLOCK             lock;                   /* lock for accessing the table */
+    GLuint            **host_ids[L1_COUNT];     /* client -> host id mapping sparse array */
+    GLuint            **client_ids[L1_COUNT];   /* host -> client id mapping sparse array */
+    GLuint              min_free;               /* id to start looking for a free slot */
+    BOOL                implicit;               /* table allows implicit allocation */
+};
+
+static GLuint *find_object_id( GLuint **ids[L1_COUNT], GLuint client_id )
+{
+    GLuint i = client_id / L3_COUNT / L2_COUNT, j = (client_id / L3_COUNT) % L2_COUNT, k = client_id % L3_COUNT;
+    return ids[i] ? ids[i][j] ? ids[i][j] + k : NULL : NULL;
+}
+
+static GLuint *alloc_object_ids( GLuint **ids[L1_COUNT], GLuint client_id )
+{
+    GLuint i = client_id / L3_COUNT / L2_COUNT, j = (client_id / L3_COUNT) % L2_COUNT;
+    GLuint **ptr;
+
+    if (!(ptr = ids[i]) && !(ptr = ids[i] = calloc( L2_COUNT, sizeof(*ptr) ))) return NULL;
+    if (!ptr[j] && !(ptr[j] = calloc( L3_COUNT, sizeof(*ptr[j]) ))) return NULL;
+    return ptr[j];
+}
+
+static void free_object_ids( struct object_table *table, GLuint **ids[L1_COUNT] )
+{
+    GLuint **l1_block, *l2_block;
+
+    for (int i = 0; i < L1_COUNT; i++)
+    {
+        if (!(l1_block = ids[i])) continue;
+        for (int j = 0; j < L2_COUNT; j++)
+        {
+            if (!(l2_block = l1_block[j])) continue;
+            free( l2_block );
+        }
+        free( l1_block );
+    }
+}
+
+static GLuint alloc_client_id( struct object_table *table, GLuint host_id, UINT range )
+{
+    /* if we don't need implicit allocations, use the host allocated ids directly */
+    if (!table->implicit) return host_id;
+
+    /* otherwise we need to allocate client id ourselves, lookup for a free id */
+    for (GLuint id = table->min_free + 1, n = range, *ids; id != 0; id++)
+    {
+        if (!(ids = alloc_object_ids( table->host_ids, id ))) return 0;
+        if (ids[id % L3_COUNT]) n = range;
+        else if (!n--) return table->min_free = id - range;
+    }
+
+    return 0;
+}
+
+static GLuint set_object( struct object_table *table, GLuint client_id, GLuint host_id )
+{
+    GLuint *ids;
+
+    if (!(ids = alloc_object_ids( table->host_ids, client_id ))) goto failed;
+    ids[client_id % L3_COUNT] = host_id;
+
+    if (table->implicit)
+    {
+        if (!(ids = alloc_object_ids( table->client_ids, host_id ))) goto failed;
+        ids[host_id % L3_COUNT] = client_id;
+    }
+
+    TRACE( "Inserted %s client %#x, host %#x\n", debugstr_object_type(table->type), client_id, host_id );
+    return client_id;
+
+failed:
+    ERR( "Failed to allocate object id block\n" );
+    return -1;
+}
+
+static GLuint del_object( struct object_table *table, GLuint client_id )
+{
+    GLuint *object, host_id = 0;
+
+    if (!client_id || !(object = find_object_id( table->host_ids, client_id ))) return -1;
+    table->min_free = min( table->min_free, client_id - 1 );
+    host_id = *object;
+    *object = 0;
+    if (host_id && (object = find_object_id( table->client_ids, host_id ))) *object = 0;
+
+    TRACE( "Deleting %s client %#x, host %#x\n", debugstr_object_type( table->type ), client_id, host_id );
+    return host_id ? host_id : -1;
+}
+
+static GLuint get_object( struct object_table *table, GLuint client_id, BOOL check )
+{
+    const GLuint *object = find_object_id( table->host_ids, client_id );
+    GLuint host_id = object ? *object : 0;
+
+    TRACE( "Found %s client %#x, host %#x\n", debugstr_object_type( table->type ), client_id, host_id );
+    return check || host_id ? host_id : -1;
+}
+
+#define MAKE_OBJECT_CALL( func, ... )                                                              \
+    struct func##_params args = { .teb = NtCurrentTeb(), __VA_ARGS__ };                            \
+    UNIX_CALL( func, &args )
+
+static GLuint create_object( enum object_type type )
+{
+    GLuint object;
+
+    switch (type)
+    {
+    case OBJ_TYPE_BUFFER: { MAKE_OBJECT_CALL( glGenBuffers, .n = 1, .buffers = &object ); return object; }
+    case OBJ_TYPE_DISPLAY_LIST: { MAKE_OBJECT_CALL( glGenLists, .range = 1 ); return args.ret; }
+    case OBJ_TYPE_FRAMEBUFFER: { MAKE_OBJECT_CALL( glGenFramebuffers, .n = 1, .framebuffers = &object ); return object; }
+    case OBJ_TYPE_MEMORY: assert( 0 ); return 0;
+    case OBJ_TYPE_PATH: { MAKE_OBJECT_CALL( glGenPathsNV, .range = 1 ); return args.ret; }
+    case OBJ_TYPE_PROGRAM: { MAKE_OBJECT_CALL( glGenProgramsARB, .n = 1, .programs = &object ); return object; }
+    case OBJ_TYPE_RENDERBUFFER: { MAKE_OBJECT_CALL( glGenRenderbuffers, .n = 1, .renderbuffers = &object ); return object; }
+    case OBJ_TYPE_SAMPLER: { MAKE_OBJECT_CALL( glGenSamplers, .count = 1, .samplers = &object ); return object; }
+    case OBJ_TYPE_SEMAPHORE: { MAKE_OBJECT_CALL( glGenSemaphoresEXT, .n = 1, .semaphores = &object ); return object; }
+    case OBJ_TYPE_SHADER_ATI: { MAKE_OBJECT_CALL( glGenFragmentShadersATI, .range = 1 ); return args.ret; }
+    case OBJ_TYPE_SHADER_EXT: { MAKE_OBJECT_CALL( glGenVertexShadersEXT, .range = 1 ); return args.ret; }
+    case OBJ_TYPE_TEXTURE: { MAKE_OBJECT_CALL( glGenTextures, .n = 1, .textures = &object ); return object; }
+    case OBJ_TYPE_COUNT: break;
+    }
+
+    return 0;
+}
+
+#undef MAKE_OBJECT_CALL
+
+static void free_object_table( struct object_table *table )
+{
+    free_object_ids( table, table->host_ids );
+    free_object_ids( table, table->client_ids );
+}
+
+static void init_object_table( struct object_table *table, enum object_type type )
+{
+    InitializeSRWLock( &table->lock );
+    table->type = type;
+}
+
+struct display_lists
+{
+    LONG                refcount;
+    LONG                modified;
+    struct object_table tables[OBJ_TYPE_COUNT];
+};
+
+static struct display_lists *display_lists_create(void)
+{
+    struct display_lists *lists;
+
+    if (!(lists = calloc( 1, sizeof(*lists) ))) return NULL;
+    lists->refcount = 1;
+
+    for (UINT i = 0; i < OBJ_TYPE_COUNT; i++)
+        init_object_table( lists->tables + i, i );
+
+    return lists;
+}
+
+static struct display_lists *display_lists_acquire( struct display_lists *lists )
+{
+    InterlockedIncrement( &lists->refcount );
+    return lists;
+}
+
+static void display_lists_release( struct display_lists *lists )
+{
+    if (InterlockedDecrement( &lists->refcount )) return;
+
+    for (UINT i = 0; i < OBJ_TYPE_COUNT; i++)
+        free_object_table( lists->tables + i );
+
+    free( lists );
+}
+
 struct context
 {
     struct opengl_client_context base;
-    struct handle_table syncs;
+    struct handle_table          syncs;
+    struct display_lists        *lists;
+
+    GLuint list_base;
 };
 
 static struct context *context_from_opengl_client_context( struct opengl_client_context *base )
@@ -293,19 +501,23 @@ BOOL get_context_from_handle( HGLRC handle, HGLRC *obj )
     return context || !handle;
 }
 
-static struct handle_entry *alloc_client_context(void)
+static struct handle_entry *alloc_client_context( struct context *share )
 {
     struct context *context;
     struct handle_entry *ptr;
 
     if (!(context = calloc( 1, sizeof(*context) ))) return NULL;
+    if (!(context->lists = share ? display_lists_acquire( share->lists ) : display_lists_create())) goto failed;
 
     EnterCriticalSection( &wgl_cs );
     ptr = alloc_handle( &contexts, context );
     LeaveCriticalSection( &wgl_cs );
+    if (ptr) return ptr;
 
-    if (!ptr) free( context );
-    return ptr;
+    display_lists_release( context->lists );
+failed:
+    free( context );
+    return NULL;
 }
 
 static void free_client_context( struct handle_entry *ptr )
@@ -318,6 +530,7 @@ static void free_client_context( struct handle_entry *ptr )
         if (LOWORD(entry->handle) == 0xffff) continue;
         free( entry->user_data );
     }
+    display_lists_release( context->lists );
 
     EnterCriticalSection( &wgl_cs );
     free_handle( &contexts, ptr );
@@ -332,6 +545,300 @@ void set_gl_error( GLenum error )
     if (!context->last_error && !(context->last_error = glGetError())) context->last_error = error;
 }
 
+static struct object_table *get_object_table( struct context *ctx, enum object_type type, BOOL write )
+{
+    if (write) InterlockedExchange( &ctx->lists->modified, 1 );
+    return type < OBJ_TYPE_COUNT ? ctx->lists->tables + type : NULL;
+}
+
+void put_context_objects( enum object_type type, UINT n, GLuint *handles )
+{
+    struct object_table *table;
+    struct context *ctx;
+
+    if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return;
+    if (!(table = get_object_table( ctx, type, TRUE ))) return;
+
+    AcquireSRWLockExclusive( &table->lock );
+    for (UINT i = 0; i < n; i++) handles[i] = handles[i] ? set_object( table, alloc_client_id( table, handles[i], 0 ), handles[i] ) : 0;
+    ReleaseSRWLockExclusive( &table->lock );
+}
+
+GLuint put_context_object_range( enum object_type type, UINT range, GLuint base )
+{
+    struct object_table *table;
+    struct context *ctx;
+    GLuint first;
+
+    if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return base;
+    if (!(table = get_object_table( ctx, type, TRUE ))) return base;
+
+    AcquireSRWLockExclusive( &table->lock );
+    first = alloc_client_id( table, base, range );
+    for (UINT i = 0; i < range; i++) set_object( table, first + i, base + i );
+    ReleaseSRWLockExclusive( &table->lock );
+
+    return first;
+}
+
+static void alloc_client_objects( struct context *ctx, enum object_type type, UINT n, const GLuint *handles )
+{
+    struct object_table *table;
+
+    if (!(table = get_object_table( ctx, type, TRUE ))) return;
+
+    AcquireSRWLockExclusive( &table->lock );
+    for (UINT i = 0; i < n; i++)
+    {
+        if (!handles[i] || get_object( table, handles[i], TRUE )) continue;
+        WARN( "Creating implicit %s client %#x\n", debugstr_object_type( type ), handles[i] );
+        set_object( table, handles[i], create_object( table->type ) );
+        table->implicit = TRUE; /* from now on we cannot rely on host-allocated ids */
+    }
+    ReleaseSRWLockExclusive( &table->lock );
+}
+
+BOOL alloc_context_objects( enum object_type type, UINT n, const GLuint *handles, BOOL extension )
+{
+    BOOL alloc_client, needs_client = FALSE;
+    struct object_table *table;
+    struct context *ctx;
+
+    if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return TRUE;
+    if (!(table = get_object_table( ctx, type, FALSE ))) return TRUE;
+
+    /* only allow explicit allocation in some cases, use host allocated ids directly in that case */
+    switch (type)
+    {
+    case OBJ_TYPE_DISPLAY_LIST:
+        if (ctx->base.profile_mask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB) return FALSE;
+        alloc_client = TRUE;
+        break;
+    case OBJ_TYPE_FRAMEBUFFER:
+    case OBJ_TYPE_RENDERBUFFER:
+    case OBJ_TYPE_PROGRAM:
+    case OBJ_TYPE_SHADER_EXT:
+    case OBJ_TYPE_SHADER_ATI:
+    case OBJ_TYPE_SEMAPHORE:
+    case OBJ_TYPE_PATH:
+        alloc_client = extension;
+        break;
+    case OBJ_TYPE_SAMPLER:
+    case OBJ_TYPE_MEMORY:
+        alloc_client = FALSE;
+        break;
+    default:
+        alloc_client = !(ctx->base.profile_mask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
+        break;
+    }
+
+    AcquireSRWLockShared( &table->lock );
+    for (UINT i = 0; i < n && !needs_client; i++)
+        needs_client = handles[i] && !get_object( table, handles[i], TRUE );
+    ReleaseSRWLockShared( &table->lock );
+    if (!needs_client) return TRUE;
+
+    if (alloc_client) alloc_client_objects( ctx, type, n, handles );
+    else set_gl_error( GL_INVALID_OPERATION );
+
+    return alloc_client;
+}
+
+GLuint *del_context_objects( enum object_type type, UINT n, GLuint *handles )
+{
+    struct object_table *table;
+    struct context *ctx;
+
+    if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return handles;
+    if (!(table = get_object_table( ctx, type, FALSE ))) return handles;
+
+    AcquireSRWLockExclusive( &table->lock );
+    for (UINT i = 0; i < n; i++) handles[i] = del_object( table, handles[i] );
+    ReleaseSRWLockExclusive( &table->lock );
+
+    return handles;
+}
+
+GLuint *map_context_objects( enum object_type type, UINT n, GLuint *handles )
+{
+    struct object_table *table;
+    struct context *ctx;
+
+    if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return handles;
+    if (!(table = get_object_table( ctx, type, FALSE ))) return handles;
+
+    AcquireSRWLockShared( &table->lock );
+    while (n--) handles[n] = handles[n] ? get_object( table, handles[n], FALSE ) : 0;
+    ReleaseSRWLockShared( &table->lock );
+
+    return handles;
+}
+
+static GLuint get_pname_object_type( GLenum pname )
+{
+    switch (pname)
+    {
+    case GL_ARRAY_BUFFER_BINDING:
+    case GL_ATOMIC_COUNTER_BUFFER_BINDING:
+    case GL_COLOR_ARRAY_BUFFER_BINDING:
+    case GL_COPY_READ_BUFFER_BINDING:
+    case GL_COPY_WRITE_BUFFER_BINDING:
+    case GL_DISPATCH_INDIRECT_BUFFER_BINDING:
+    case GL_DRAW_INDIRECT_BUFFER_BINDING:
+    case GL_EDGE_FLAG_ARRAY_BUFFER_BINDING:
+    case GL_ELEMENT_ARRAY_BUFFER_BINDING:
+    case GL_FOG_COORD_ARRAY_BUFFER_BINDING:
+    case GL_INDEX_ARRAY_BUFFER_BINDING:
+    case GL_NORMAL_ARRAY_BUFFER_BINDING:
+    case GL_PARAMETER_BUFFER_BINDING:
+    case GL_PIXEL_PACK_BUFFER_BINDING:
+    case GL_PIXEL_UNPACK_BUFFER_BINDING:
+    case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES:
+    case GL_QUERY_BUFFER_BINDING:
+    case GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING:
+    case GL_SHADER_STORAGE_BUFFER_BINDING:
+    case GL_TEXTURE_BUFFER_DATA_STORE_BINDING:
+    case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
+    case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
+    case GL_UNIFORM_BLOCK_BINDING:
+    case GL_UNIFORM_BUFFER_BINDING:
+    case GL_UNIFORM_BUFFER_BINDING_EXT:
+    case GL_VERTEX_ARRAY_BUFFER_BINDING:
+    case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+    case GL_VERTEX_BINDING_BUFFER:
+    case GL_VIDEO_BUFFER_BINDING_NV:
+    case GL_WEIGHT_ARRAY_BUFFER_BINDING:
+    case GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES:
+        return OBJ_TYPE_BUFFER;
+    case GL_READ_FRAMEBUFFER_BINDING:
+    case GL_DRAW_FRAMEBUFFER_BINDING:
+        return OBJ_TYPE_FRAMEBUFFER;
+    case GL_RENDERBUFFER_BINDING:
+    case GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV:
+        return OBJ_TYPE_RENDERBUFFER;
+    case GL_TEXTURE_BINDING_1D:
+    case GL_TEXTURE_BINDING_1D_ARRAY:
+    case GL_TEXTURE_BINDING_2D:
+    case GL_TEXTURE_BINDING_2D_ARRAY:
+    case GL_TEXTURE_BINDING_2D_MULTISAMPLE:
+    case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY:
+    case GL_TEXTURE_BINDING_3D:
+    case GL_TEXTURE_BINDING_BUFFER:
+    case GL_TEXTURE_BINDING_CUBE_MAP:
+    case GL_TEXTURE_BINDING_CUBE_MAP_ARRAY:
+    case GL_TEXTURE_BINDING_EXTERNAL_OES:
+    case GL_TEXTURE_BINDING_RECTANGLE:
+    case GL_TEXTURE_BUFFER_BINDING:
+    case GL_TEXTURE_BINDING_RENDERBUFFER_NV:
+    case GL_TEXTURE_1D_STACK_BINDING_MESAX:
+    case GL_TEXTURE_2D_STACK_BINDING_MESAX:
+    case GL_TEXTURE_4D_BINDING_SGIS:
+    case GL_DETAIL_TEXTURE_2D_BINDING_SGIS:
+    case GL_SHADING_RATE_IMAGE_BINDING_NV:
+    case GL_IMAGE_BINDING_NAME:
+        return OBJ_TYPE_TEXTURE;
+    case GL_SAMPLER_BINDING:
+        return OBJ_TYPE_SAMPLER;
+    case GL_LIST_INDEX:
+        return OBJ_TYPE_DISPLAY_LIST;
+    case GL_PROGRAM_BINDING_ARB:
+    case GL_VERTEX_PROGRAM_BINDING_NV:
+    case GL_FRAGMENT_PROGRAM_BINDING_NV:
+        return OBJ_TYPE_PROGRAM;
+    case GL_VERTEX_SHADER_BINDING_EXT:
+        return OBJ_TYPE_SHADER_EXT;
+    }
+
+    return OBJ_TYPE_COUNT;
+}
+
+static BOOL map_client_objects( enum object_type type, GLuint host_id, GLuint *ret )
+{
+    GLuint *object, client_id = host_id;
+    struct object_table *table;
+    struct context *ctx;
+
+    if (!host_id || type == OBJ_TYPE_COUNT) return FALSE;
+    if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return FALSE;
+    if (!(table = get_object_table( ctx, type, FALSE ))) return FALSE;
+
+    AcquireSRWLockShared( &table->lock );
+    if ((object = find_object_id( table->client_ids, host_id ))) client_id = *object;
+    ReleaseSRWLockShared( &table->lock );
+
+    *ret = client_id;
+    return TRUE;
+}
+
+static void map_framebuffer_attachment_param( GLenum target, GLenum attachment, GLenum pname, GLint *params )
+{
+    GLint type, value;
+
+    if (pname != GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) return;
+    glGetFramebufferAttachmentParameteriv( target, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type );
+    if (type == GL_RENDERBUFFER && map_client_objects( OBJ_TYPE_RENDERBUFFER, *params, (GLuint *)&value )) *params = value;
+    if (type == GL_TEXTURE && map_client_objects( OBJ_TYPE_TEXTURE, *params, (GLuint *)&value )) *params = value;
+}
+
+void WINAPI glGetFramebufferAttachmentParameteriv( GLenum target, GLenum attachment, GLenum pname, GLint *params )
+{
+    struct glGetFramebufferAttachmentParameteriv_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .pname = pname, .params = params };
+    NTSTATUS status;
+
+    TRACE( "target %d, attachment %d, pname %d, params %p\n", target, attachment, pname, params );
+
+    if ((status = UNIX_CALL( glGetFramebufferAttachmentParameteriv, &args ))) WARN( "glGetFramebufferAttachmentParameteriv returned %#lx\n", status );
+    map_framebuffer_attachment_param( target, attachment, pname, params );
+}
+
+void WINAPI glGetFramebufferAttachmentParameterivEXT( GLenum target, GLenum attachment, GLenum pname, GLint *params )
+{
+    struct glGetFramebufferAttachmentParameterivEXT_params args = { .teb = NtCurrentTeb(), .target = target, .attachment = attachment, .pname = pname, .params = params };
+    NTSTATUS status;
+
+    TRACE( "target %d, attachment %d, pname %d, params %p\n", target, attachment, pname, params );
+
+    if ((status = UNIX_CALL( glGetFramebufferAttachmentParameterivEXT, &args ))) WARN( "glGetFramebufferAttachmentParameterivEXT returned %#lx\n", status );
+    map_framebuffer_attachment_param( target, attachment, pname, params );
+}
+
+static void map_named_framebuffer_attachment_param( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params )
+{
+    GLint type, value;
+
+    if (pname != GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) return;
+    glGetNamedFramebufferAttachmentParameteriv( framebuffer, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type );
+    if (type == GL_RENDERBUFFER && map_client_objects( OBJ_TYPE_RENDERBUFFER, *params, (GLuint *)&value )) *params = value;
+    if (type == GL_TEXTURE && map_client_objects( OBJ_TYPE_TEXTURE, *params, (GLuint *)&value )) *params = value;
+}
+
+void WINAPI glGetNamedFramebufferAttachmentParameteriv( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params )
+{
+    struct glGetNamedFramebufferAttachmentParameteriv_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .pname = pname, .params = params };
+    GLuint host_framebuffer = framebuffer;
+    NTSTATUS status;
+
+    TRACE( "framebuffer %d, attachment %d, pname %d, params %p\n", framebuffer, attachment, pname, params );
+
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &host_framebuffer );
+    if ((status = UNIX_CALL( glGetNamedFramebufferAttachmentParameteriv, &args ))) WARN( "glGetNamedFramebufferAttachmentParameteriv returned %#lx\n", status );
+    map_named_framebuffer_attachment_param( framebuffer, attachment, pname, params );
+}
+
+void WINAPI glGetNamedFramebufferAttachmentParameterivEXT( GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params )
+{
+    struct glGetNamedFramebufferAttachmentParameterivEXT_params args = { .teb = NtCurrentTeb(), .attachment = attachment, .pname = pname, .params = params };
+    GLuint host_framebuffer = framebuffer;
+    NTSTATUS status;
+
+    TRACE( "framebuffer %d, attachment %d, pname %d, params %p\n", framebuffer, attachment, pname, params );
+
+    if (!alloc_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &host_framebuffer, TRUE )) return;
+    args.framebuffer = *map_context_objects( OBJ_TYPE_FRAMEBUFFER, 1, &host_framebuffer );
+    if ((status = UNIX_CALL( glGetNamedFramebufferAttachmentParameterivEXT, &args ))) WARN( "glGetNamedFramebufferAttachmentParameterivEXT returned %#lx\n", status );
+    map_named_framebuffer_attachment_param( framebuffer, attachment, pname, params );
+}
+
 HGLRC WINAPI wglCreateContext( HDC hdc )
 {
     static const int attribs[] = { WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 0, 0 };
@@ -342,17 +849,20 @@ HGLRC WINAPI wglCreateContext( HDC hdc )
 HGLRC WINAPI wglCreateContextAttribsARB( HDC hdc, HGLRC share, const int *attribs )
 {
     struct wglCreateContextAttribsARB_params args = { .teb = NtCurrentTeb(), .hDC = hdc, .attribList = attribs };
+    struct context *share_context = NULL;
     struct handle_entry *ptr;
     NTSTATUS status;
 
     TRACE( "hdc %p, share %p, attribs %p\n", hdc, share, attribs );
 
-    if (!get_context_from_handle( share, &args.hShareContext ))
+    if (share && !(share_context = context_from_handle( share )))
     {
         SetLastError( ERROR_INVALID_OPERATION );
         return NULL;
     }
-    if (!(ptr = alloc_client_context())) return NULL;
+    if (share) args.hShareContext = &share_context->base.obj;
+
+    if (!(ptr = alloc_client_context( share_context ))) return NULL;
     args.ret = &ptr->context->obj;
 
     if ((status = UNIX_CALL( wglCreateContextAttribsARB, &args ))) WARN( "wglCreateContextAttribsARB returned %#lx\n", status );
@@ -432,6 +942,34 @@ BOOL WINAPI wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, HGLRC handle )
     return TRUE;
 }
 
+/***********************************************************************
+ *      wglShareLists
+ */
+BOOL WINAPI wglShareLists( HGLRC src_handle, HGLRC dst_handle )
+{
+    struct wglShareLists_params args = { .teb = NtCurrentTeb() };
+    struct context *src_context, *dst_context;
+    struct display_lists *lists;
+    NTSTATUS status;
+
+    TRACE( "src_handle %p, dst_handle %p\n", src_handle, dst_handle );
+
+    if (!(src_context = context_from_handle( src_handle ))) return FALSE;
+    if (!(dst_context = context_from_handle( dst_handle ))) return FALSE;
+    if (ReadNoFence( &dst_context->lists->modified )) return FALSE;
+
+    args.hrcSrvShare = &src_context->base.obj;
+    args.hrcSrvSource = &dst_context->base.obj;
+    if ((status = UNIX_CALL( wglShareLists, &args ))) WARN( "wglShareLists returned %#lx\n", status );
+    if (!args.ret) return FALSE;
+
+    lists = display_lists_acquire( src_context->lists );
+    lists = InterlockedExchangePointer( (void *)&dst_context->lists, lists );
+    display_lists_release( lists );
+
+    return TRUE;
+}
+
 /***********************************************************************
  *		wglGetCurrentReadDCARB
  *
@@ -2039,7 +2577,7 @@ GLsync WINAPI glImportSyncEXT( GLenum external_sync_type, GLintptr external_sync
     return NULL;
 }
 
-BOOL get_integer( GLenum name, GLint *data )
+BOOL get_integer( GLenum name, GLuint index, GLint value, GLint *data )
 {
     struct context *ctx;
 
@@ -2047,6 +2585,9 @@ BOOL get_integer( GLenum name, GLint *data )
 
     switch (name)
     {
+    case GL_CONTEXT_PROFILE_MASK:
+        *data = ctx->base.profile_mask;
+        return TRUE;
     case GL_MAJOR_VERSION:
         *data = ctx->base.major_version;
         return TRUE;
@@ -2058,7 +2599,7 @@ BOOL get_integer( GLenum name, GLint *data )
         return TRUE;
     }
 
-    return FALSE;
+    return map_client_objects( get_pname_object_type( name ), value, (GLuint *)data );
 }
 
 const GLubyte * WINAPI glGetStringi( GLenum name, GLuint index )
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c
index efe604530dbb..b449ef3c32cf 100644
--- a/dlls/quartz/filtergraph.c
+++ b/dlls/quartz/filtergraph.c
@@ -5057,6 +5057,17 @@ static HRESULT WINAPI MediaFilter_GetClassID(IMediaFilter *iface, CLSID * pClass
     return E_NOTIMPL;
 }
 
+static void graph_update_positions(struct filter_graph *graph)
+{
+    if (graph->state == State_Running && !graph->needs_async_run && graph->refClock)
+    {
+        REFERENCE_TIME time;
+        IReferenceClock_GetTime(graph->refClock, &time);
+        graph->stream_elapsed += time - graph->stream_start;
+        graph->current_pos += graph->stream_elapsed;
+    }
+}
+
 static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
 {
     struct filter_graph *graph = impl_from_IMediaFilter(iface);
@@ -5076,6 +5087,8 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface)
 
     sort_filters(graph);
 
+    graph_update_positions(graph);
+
     if (graph->state == State_Running)
     {
         LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
@@ -5135,13 +5148,7 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface)
     if (graph->defaultclock && !graph->refClock)
         IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
 
-    if (graph->state == State_Running && !graph->needs_async_run && graph->refClock)
-    {
-        REFERENCE_TIME time;
-        IReferenceClock_GetTime(graph->refClock, &time);
-        graph->stream_elapsed += time - graph->stream_start;
-        graph->current_pos += graph->stream_elapsed;
-    }
+    graph_update_positions(graph);
 
     LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
     {
diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c
index fad2d47990ce..dde4da0655ca 100644
--- a/dlls/quartz/tests/filtergraph.c
+++ b/dlls/quartz/tests/filtergraph.c
@@ -1750,8 +1750,10 @@ static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *curr
     if (winetest_debug > 1) trace("%p->SetPositions(%I64d, %#lx, %I64d, %#lx)\n",
             iface, *current, current_flags, *stop, stop_flags);
     ok(filter->state != State_Running, "Filter should be paused or stopped while seeking.\n");
-    filter->seek_current = *current;
-    filter->seek_stop = *stop;
+    if (current_flags & AM_SEEKING_AbsolutePositioning)
+        filter->seek_current = *current;
+    if (stop_flags & AM_SEEKING_AbsolutePositioning)
+        filter->seek_stop = *stop;
     *current = 12340000;
     *stop = 43210000;
     return filter->seek_hr;
@@ -5104,6 +5106,24 @@ static void test_graph_seeking(void)
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     ok(time == 12340000, "Got time %I64d.\n", time);
 
+    hr = IMediaControl_Run(control);
+    ok(hr == S_OK, "Got hr %#lx.\n", hr);
+
+    Sleep(100);
+
+    hr = IMediaControl_Stop(control);
+    ok(hr == S_OK, "Got hr %#lx.\n", hr);
+    hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
+    ok(hr == S_OK, "Got hr %#lx.\n", hr);
+    ok(time == 12340000, "Got time %I64d.\n", time);
+    if (winetest_interactive) /* Timing problems make this test too liable to fail. */
+    {
+        todo_wine ok(compare_time(filter1.seek_current, 1334 * 10000, 80 * 10000),
+                "Expected about 1334ms, got %I64d.\n", filter1.seek_current);
+        ok(compare_time(filter2.seek_current, 1334 * 10000, 80 * 10000),
+                "Expected about 1334ms, got %I64d.\n", filter2.seek_current);
+    }
+
     hr = IMediaFilter_SetSyncSource(filter, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
 
@@ -5113,12 +5133,12 @@ static void test_graph_seeking(void)
     Sleep(100);
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    todo_wine ok(!time, "Got time %I64d.\n", time);
+    todo_wine ok(time == 8000 * 10000, "Got time %I64d.\n", time);
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    todo_wine ok(!current, "Got time %I64d.\n", current);
-    ok(!stop, "Got time %I64d.\n", stop);
+    todo_wine ok(current == 8000 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 8000 * 10000, "Got time %I64d.\n", stop);
 
     hr = IMediaControl_Stop(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index e704508f2a5b..72d25d3946d6 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -1513,6 +1513,61 @@ static void ME_RTFReadParnumGroup( RTF_Info *info )
     RTFRouteToken( info );     /* feed "}" back to router */
 }
 
+static void ME_RTFReadPntextGroup( RTF_Info *info )
+{
+    int level = 1;
+    WCHAR buf[256];
+    int buf_len = 0;
+
+    ME_DestroyString( info->pntext );
+    info->pntext = NULL;
+
+    for (;;)
+    {
+        RTFGetToken( info );
+
+        if (info->rtfClass == rtfEOF)
+            return;
+
+        if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
+        {
+            level++;
+            continue;
+        }
+
+        if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
+        {
+            level--;
+            if (level == 0) break;
+            continue;
+        }
+
+        if (buf_len >= ARRAY_SIZE(buf) - 1)
+            continue;
+
+        if (info->rtfClass == rtfText)
+        {
+            buf[buf_len++] = (WCHAR)(unsigned char)info->rtfMajor;
+        }
+        else if (info->rtfClass == rtfControl && info->rtfMajor == rtfSpecialChar)
+        {
+            switch (info->rtfMinor)
+            {
+            case rtfTab:        buf[buf_len++] = '\t'; break;
+            case rtfBullet:     buf[buf_len++] = 0x2022; break;
+            case rtfNoBrkSpace: buf[buf_len++] = 0x00A0; break;
+            case rtfNoBrkHyphen:buf[buf_len++] = 0x2011; break;
+            case rtfOptDest:
+            default: break;
+            }
+        }
+    }
+
+    info->pntext = ME_MakeStringN( buf, buf_len );
+
+    RTFRouteToken( info );
+}
+
 static void ME_RTFReadHook(RTF_Info *info)
 {
   switch(info->rtfClass)
@@ -1670,6 +1725,7 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre
       RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup);
       RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup);
       RTFSetDestinationCallback(&parser, rtfParNumbering, ME_RTFReadParnumGroup);
+      RTFSetDestinationCallback(&parser, rtfParNumText, ME_RTFReadPntextGroup);
       if (!parser.editor->bEmulateVersion10) /* v4.1 */
       {
         RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup);
diff --git a/dlls/riched20/reader.c b/dlls/riched20/reader.c
index 95360d8b9d48..402335d4b68f 100644
--- a/dlls/riched20/reader.c
+++ b/dlls/riched20/reader.c
@@ -150,6 +150,7 @@ RTFDestroy(RTF_Info *info)
 	}
 	RTFDestroyAttrs(info);
 	free(info->cpOutputBuffer);
+        ME_DestroyString( info->pntext );
         while (info->tableDef)
         {
                 RTFTable *tableDef = info->tableDef;
@@ -252,6 +253,7 @@ void RTFInit(RTF_Info *info)
         info->nestingLevel = 0;
         info->canInheritInTbl = FALSE;
         info->borderType = 0;
+        info->pntext = NULL;
 
         memset(&info->fmt, 0, sizeof(info->fmt));
         info->fmt.cbSize = sizeof(info->fmt);
@@ -2498,13 +2500,42 @@ static void SpecialChar (RTF_Info *info)
 	case rtfPage:
 	case rtfSect:
 	case rtfPar:
+        {
+                ME_Paragraph *para;
                 RTFFlushOutputBuffer(info);
+                if ((info->fmt.dwMask & PFM_NUMBERING) && !info->pntext)
+                    info->fmt.wNumbering = 0;
+
+                if (info->pntext && info->pntext->nLen &&
+                    info->pntext->szData[info->pntext->nLen - 1] == '\t')
+                {
+                    info->pntext->szData[--info->pntext->nLen] = 0;
+                    if ((info->fmt.dwMask & PFM_NUMBERINGTAB) &&
+                        info->fmt.wNumberingTab == 0)
+                        info->fmt.wNumberingTab = lDefaultTab;
+                }
+
                 editor_set_selection_para_fmt( info->editor, &info->fmt );
+                para = info->editor->pCursors[0].para;
                 memset(&info->fmt, 0, sizeof(info->fmt));
                 info->fmt.cbSize = sizeof(info->fmt);
 		RTFPutUnicodeChar (info, '\r');
 		if (info->editor->bEmulateVersion10) RTFPutUnicodeChar (info, '\n');
+
+                if (info->pntext)
+                {
+                    if (info->pntext->nLen && para->fmt.wNumbering)
+                    {
+                        para_num_clear( &para->para_num );
+                        para->para_num.text = info->pntext;
+                        info->pntext = NULL;
+                        para_mark_rewrap( info->editor, para );
+                    }
+                    ME_DestroyString( info->pntext );
+                    info->pntext = NULL;
+                }
 		break;
+        }
 	case rtfNoBrkSpace:
 		RTFPutUnicodeChar (info, 0x00A0);
 		break;
diff --git a/dlls/riched20/rtf.h b/dlls/riched20/rtf.h
index f2d446ac5d35..69deb28ea0c6 100644
--- a/dlls/riched20/rtf.h
+++ b/dlls/riched20/rtf.h
@@ -1175,6 +1175,7 @@ struct _RTF_Info {
     int borderType; /* value corresponds to the RTFBorder constants. */
 
     PARAFORMAT2 fmt; /* Accumulated para fmt for current paragraph. */
+    ME_String *pntext; /* Explicit paragraph number text from \pntext destination. */
 };
 
 
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 0a7b8ec6ce47..2d654641ab64 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -9093,6 +9093,86 @@ static void test_para_numbering(void)
     ok( cf.yHeight == 200, "got %ld\n", cf.yHeight );
 
     DestroyWindow( edit );
+
+    /* \pn without \pntext should not produce a visible number: native
+     * richedit treats \pntext as the authoritative label, so its absence
+     * suppresses auto-numbering entirely. */
+    edit = new_richeditW( NULL );
+    {
+        const char *no_pntext =
+            "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}}"
+            "\\pard{\\*\\pn\\pnlvlbody\\pnf0\\pnindent1000\\pnstart1\\pndec{\\pntxta.}}"
+            "\\f0 Alpha\\par "
+            "Beta\\par}";
+        const WCHAR expect_no_pntext[] = {'A','l','p','h','a','\r','B','e','t','a',0};
+
+        es.dwCookie = (DWORD_PTR)&no_pntext;
+        es.dwError = 0;
+        es.pfnCallback = test_EM_STREAMIN_esCallback;
+        result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
+        ok( result == lstrlenW( expect_no_pntext ), "got %Id\n", result );
+
+        result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
+        ok( result == lstrlenW( expect_no_pntext ), "got %Id\n", result );
+        ok( !lstrcmpW( buf, expect_no_pntext ), "got %s\n", wine_dbgstr_w(buf) );
+    }
+    DestroyWindow( edit );
+
+    /* \pntext containing \bullet:
+     * the bullet character is the label and must not appear in EM_GETTEXT. */
+    edit = new_richeditW( NULL );
+    {
+        const char *bullet_rtf =
+            "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}"
+            "{\\f1\\fnil\\fcharset2 Symbol;}}"
+            "\\pard{\\pntext\\f1\\'b7\\tab}"
+            "{\\*\\pn\\pnlvlblt\\pnf1\\pnindent1000{\\pntxtb\\bullet}}"
+            "\\f0 Item one\\par"
+            "{\\pntext\\f1\\bullet\\tab}\\f0 Item two\\par}";
+        const WCHAR expect_bullet[] = {'I','t','e','m',' ','o','n','e','\r',
+                                        'I','t','e','m',' ','t','w','o',0};
+
+        es.dwCookie = (DWORD_PTR)&bullet_rtf;
+        es.dwError = 0;
+        es.pfnCallback = test_EM_STREAMIN_esCallback;
+        result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
+        ok( result == lstrlenW( expect_bullet ), "got %Id\n", result );
+
+        result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
+        ok( result == lstrlenW( expect_bullet ), "got %Id\n", result );
+        ok( !lstrcmpW( buf, expect_bullet ), "got %s\n", wine_dbgstr_w(buf) );
+
+        SendMessageW( edit, EM_SETSEL, 1, 1 );
+        memset( &fmt, 0, sizeof(fmt) );
+        fmt.cbSize = sizeof(fmt);
+        fmt.dwMask = PFM_ALL2;
+        SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
+        ok( fmt.wNumbering == PFN_BULLET, "got %d\n", fmt.wNumbering );
+    }
+    DestroyWindow( edit );
+
+    /* The trailing \tab inside \pntext is the label/body separator and
+     * should not be present in the document text. */
+    edit = new_richeditW( NULL );
+    {
+        const char *tab_rtf =
+            "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}}"
+            "\\pard{\\pntext\\f0 1.\\tab}"
+            "{\\*\\pn\\pnlvlbody\\pnf0\\pnindent1000\\pnstart1\\pndec{\\pntxta.}}"
+            "\\f0 Hello\\par}";
+        const WCHAR expect_tab[] = {'H','e','l','l','o',0};
+
+        es.dwCookie = (DWORD_PTR)&tab_rtf;
+        es.dwError = 0;
+        es.pfnCallback = test_EM_STREAMIN_esCallback;
+        result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
+        ok( result == lstrlenW( expect_tab ), "got %Id\n", result );
+
+        result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
+        ok( result == lstrlenW( expect_tab ), "got %Id\n", result );
+        ok( !lstrcmpW( buf, expect_tab ), "got %s\n", wine_dbgstr_w(buf) );
+    }
+    DestroyWindow( edit );
 }
 
 static void fill_reobject_struct(REOBJECT *reobj, LONG cp, LPOLEOBJECT poleobj,
diff --git a/dlls/rsaenh/Makefile.in b/dlls/rsaenh/Makefile.in
index ab53b2fe0ef2..6f3480c4509f 100644
--- a/dlls/rsaenh/Makefile.in
+++ b/dlls/rsaenh/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = rsaenh.dll
 IMPORTLIB = rsaenh
-IMPORTS   = $(TOMCRYPT_PE_LIBS) crypt32 advapi32
-EXTRAINCL = $(TOMCRYPT_PE_CFLAGS)
+IMPORTS   = $(SYMCRYPT_PE_LIBS) crypt32 advapi32
+EXTRAINCL = $(SYMCRYPT_PE_CFLAGS)
 
 VER_PRODUCTVERSION = 5,1,2600,2180
 
diff --git a/dlls/rsaenh/implglue.c b/dlls/rsaenh/implglue.c
index 900c620c9ef6..c6b47b60ef8e 100644
--- a/dlls/rsaenh/implglue.c
+++ b/dlls/rsaenh/implglue.c
@@ -27,444 +27,533 @@
 #include <stdio.h>
 #include "windef.h"
 #include "winbase.h"
+#include "ntsecapi.h"
 #include "wincrypt.h"
 
 #include "implglue.h"
 
-prng_state prng = { 0 };
-int wprng = 0;
+SYMCRYPT_ENVIRONMENT_DEFS( WindowsUsermodeWin8_1nLater );
+
+void * SYMCRYPT_CALL SymCryptCallbackAlloc( SIZE_T size )
+{
+    return _aligned_malloc( size, SYMCRYPT_ASYM_ALIGN_VALUE );
+}
+
+void SYMCRYPT_CALL SymCryptCallbackFree( void *ptr )
+{
+    _aligned_free( ptr );
+}
+
+void SYMCRYPT_CALL SymCryptRsaSelftest( void )
+{
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptRsaSignVerifyPct( const SYMCRYPT_RSAKEY *key )
+{
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptCallbackRandom( BYTE *buf, SIZE_T size )
+{
+    return RtlGenRandom( buf, size ) ? SYMCRYPT_NO_ERROR : SYMCRYPT_EXTERNAL_FAILURE;
+}
+
+UINT32 g_SymCryptFipsSelftestsPerformed;
 
 BOOL init_hash_impl( ALG_ID algid, struct hash *hash )
 {
+    const SYMCRYPT_HASH *algorithms[] =
+    {
+        [ALG_SID_MD2] = SymCryptMd2Algorithm,
+        [ALG_SID_MD4] = SymCryptMd4Algorithm,
+        [ALG_SID_MD5] = SymCryptMd5Algorithm,
+        [ALG_SID_SHA] = SymCryptSha1Algorithm,
+        [ALG_SID_SHA_256] = SymCryptSha256Algorithm,
+        [ALG_SID_SHA_384] = SymCryptSha384Algorithm,
+        [ALG_SID_SHA_512] = SymCryptSha512Algorithm,
+    };
+
     memset( hash, 0, sizeof(*hash) );
+    if (GET_ALG_CLASS(algid) != ALG_CLASS_HASH) return TRUE;
+    if (GET_ALG_SID(algid) >= ARRAY_SIZE(algorithms)) return TRUE;
+    if (!(hash->desc = algorithms[GET_ALG_SID(algid)])) return TRUE;
+    SymCryptHashInit( hash->desc, &hash->state );
+    return TRUE;
+}
+
+static SYMCRYPT_RSAKEY *alloc_rsa_key( DWORD bitlen, BOOL private )
+{
+    SYMCRYPT_RSA_PARAMS params;
 
+    params.version = 1;
+    params.nBitsOfModulus = bitlen;
+    params.nPrimes = private ? 2 : 0;
+    params.nPubExp = 1;
+    return SymCryptRsakeyAllocate( &params, 0 );
+}
+
+BOOL new_key_impl( ALG_ID algid, KEY_CONTEXT *ctx, DWORD keylen )
+{
     switch (algid)
     {
-    case CALG_MD2:
-        hash->desc = &md2_desc;
-        break;
+    case CALG_RSA_KEYX:
+    case CALG_RSA_SIGN:
+    {
+        UINT64 pubexp = 65537;
 
-    case CALG_MD4:
-        hash->desc = &md4_desc;
-        break;
+        ctx->rsa.flags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+        if (algid == CALG_RSA_KEYX) ctx->rsa.flags |= SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+        else ctx->rsa.flags |= SYMCRYPT_FLAG_RSAKEY_SIGN;
 
-    case CALG_MD5:
-        hash->desc = &md5_desc;
+        if (!(ctx->rsa.key = alloc_rsa_key( keylen * 8, TRUE )))
+        {
+            SetLastError( NTE_FAIL );
+            return FALSE;
+        }
+        if (SymCryptRsakeyGenerate( ctx->rsa.key, &pubexp, 1, ctx->rsa.flags ))
+        {
+            SymCryptRsakeyFree( ctx->rsa.key );
+            ctx->rsa.key = NULL;
+            SetLastError( NTE_FAIL );
+            return FALSE;
+        }
         break;
+    }
+    default:
+        SetLastError( NTE_BAD_ALGID );
+        return FALSE;
+    }
+    return TRUE;
+}
 
-    case CALG_SHA:
-        hash->desc = &sha1_desc;
+BOOL free_key_impl( ALG_ID algid, KEY_CONTEXT *ctx )
+{
+    switch (algid)
+    {
+    case CALG_RSA_KEYX:
+    case CALG_RSA_SIGN:
+        if (ctx->rsa.key)
+        {
+            SymCryptRsakeyFree( ctx->rsa.key );
+            ctx->rsa.key = NULL;
+        }
         break;
-
-    case CALG_SHA_256:
-        hash->desc = &sha256_desc;
+    default:
         break;
+    }
+    return TRUE;
+}
 
-    case CALG_SHA_384:
-        hash->desc = &sha384_desc;
+BOOL setup_key_impl( ALG_ID algid, KEY_CONTEXT *ctx, DWORD keylen, DWORD effective_keylen, DWORD saltlen,
+                     const BYTE *secret )
+{
+    switch (algid)
+    {
+    case CALG_RC4:
+        SymCryptRc4Init( &ctx->rc4, secret, keylen + saltlen );
         break;
-
-    case CALG_SHA_512:
-        hash->desc = &sha512_desc;
+    case CALG_RC2:
+        SymCryptRc2ExpandKeyEx( &ctx->rc2, secret, keylen + saltlen,
+                                effective_keylen ? effective_keylen : keylen << 3 );
+        break;
+    case CALG_3DES:
+        SymCrypt3DesExpandKey( &ctx->des3, secret, 24 );
+        break;
+    case CALG_3DES_112:
+        SymCrypt3DesExpandKey( &ctx->des3, secret, 16 );
+        break;
+    case CALG_DES:
+        SymCryptDesExpandKey( &ctx->des, secret, 8 );
+        break;
+    case CALG_AES:
+    case CALG_AES_128:
+        SymCryptAesExpandKey( &ctx->aes, secret, 16 );
+        break;
+    case CALG_AES_192:
+        SymCryptAesExpandKey( &ctx->aes, secret, 24 );
+        break;
+    case CALG_AES_256:
+        SymCryptAesExpandKey( &ctx->aes, secret, 32 );
         break;
-
-    default:
-        return TRUE;
     }
 
-    hash->desc->init( &hash->state );
     return TRUE;
 }
 
-BOOL update_hash_impl( struct hash *hash, const BYTE *data, DWORD len )
+BOOL duplicate_key_impl( ALG_ID algid, const KEY_CONTEXT *src, KEY_CONTEXT *dst )
 {
-    hash->desc->process( &hash->state, data, len );
+    switch (algid)
+    {
+    case CALG_RC4:
+    case CALG_RC2:
+    case CALG_3DES:
+    case CALG_3DES_112:
+    case CALG_DES:
+    case CALG_AES:
+    case CALG_AES_128:
+    case CALG_AES_192:
+    case CALG_AES_256:
+        *dst = *src;
+        break;
+    case CALG_RSA_KEYX:
+    case CALG_RSA_SIGN:
+    {
+        BYTE *blob;
+        DWORD pubexp, keylen = SymCryptRsakeySizeofModulus( src->rsa.key );
+        BOOLEAN private = SymCryptRsakeyHasPrivateKey( src->rsa.key );
+
+        if (!(dst->rsa.key = alloc_rsa_key( keylen * 8, private )) || !(blob = malloc( keylen * 2 )))
+        {
+            SetLastError( NTE_FAIL );
+            return FALSE;
+        }
+        if (private)
+        {
+            if (!export_private_key_impl( src, blob, &pubexp ) ||
+                !import_private_key_impl( algid, blob, keylen, pubexp, dst ))
+            {
+                free( blob );
+                return FALSE;
+            }
+        }
+        else
+        {
+            if (!export_public_key_impl( src, blob, &pubexp ) ||
+                !import_public_key_impl( algid, blob, keylen, pubexp, dst ))
+            {
+                free( blob );
+                return FALSE;
+            }
+        }
+        dst->rsa.flags = src->rsa.flags;
+        free( blob );
+        break;
+    }
+    default:
+        SetLastError( NTE_BAD_ALGID );
+        return FALSE;
+    }
     return TRUE;
 }
 
-BOOL finalize_hash_impl( struct hash *hash, BYTE *hash_value, DWORD hash_size )
+static BOOL verify_input( const SYMCRYPT_RSAKEY *key, const BYTE *src, SIZE_T src_size, SYMCRYPT_NUMBER_FORMAT format,
+                          BYTE *scratch, SIZE_T scratch_size )
 {
-    hash->desc->done( &hash->state, hash_value );
-    return TRUE;
+    SYMCRYPT_INT *tmp;
+    UINT32 tmp_size;
+
+    if (src_size > SymCryptRsakeySizeofModulus( key )) return FALSE;
+    if (src_size == SymCryptRsakeySizeofModulus( key ))
+    {
+        tmp_size = SymCryptSizeofIntFromDigits( key->nDigitsOfModulus );
+        SYMCRYPT_ASSERT( scratch_size >= tmp_size );
+        tmp = SymCryptIntCreate( scratch, tmp_size, key->nDigitsOfModulus );
+
+        if (SymCryptIntSetValue( src, src_size, format, tmp )) return FALSE;
+        if (!SymCryptIntIsLessThan( tmp, SymCryptIntFromModulus( key->pmModulus ))) return FALSE;
+        return TRUE;
+    }
+    return FALSE;
 }
 
-BOOL new_key_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen) 
+static BOOL rsa_encrypt( const SYMCRYPT_RSAKEY *key, const BYTE *in, SYMCRYPT_NUMBER_FORMAT in_format,
+                         BYTE *out, SYMCRYPT_NUMBER_FORMAT out_format )
 {
-    switch (aiAlgid)
+    BYTE buf[SYMCRYPT_SIZEOF_INT_FROM_BITS(64) + SYMCRYPT_ASYM_ALIGN_VALUE];
+    SYMCRYPT_MODELEMENT *result;
+    SIZE_T scratch_size, mod_size = SymCryptSizeofModElementFromModulus( key->pmModulus );
+    SIZE_T key_size = SymCryptRsakeySizeofModulus( key );
+    SYMCRYPT_INT *exp;
+    BYTE *scratch;
+
+    scratch_size = SymCryptSizeofModElementFromModulus( key->pmModulus ) +
+        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( key->nDigitsOfModulus ),
+                      SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( key->nDigitsOfModulus ) );
+
+    if (!(scratch = malloc( scratch_size ))) return FALSE;
+    if (!verify_input( key, in, key_size, in_format, scratch, scratch_size ))
     {
-        case CALG_RSA_KEYX:
-        case CALG_RSA_SIGN:
-            if (rsa_make_key( &prng, wprng, dwKeyLen, 65537, &pKeyContext->rsa ) != CRYPT_OK) {
-                SetLastError(NTE_FAIL);
-                return FALSE;
-            }
-            return TRUE;
+        free( scratch );
+        return FALSE;
     }
 
+    result = SymCryptModElementCreate( scratch, mod_size, key->pmModulus );
+
+    SymCryptModElementSetValue( in, key_size, in_format, key->pmModulus, result, scratch + mod_size,
+                                scratch_size - mod_size );
+
+    exp = SymCryptIntCreate( SYMCRYPT_ASYM_ALIGN_UP(buf), sizeof(buf) - SYMCRYPT_ASYM_ALIGN_VALUE, 1 );
+    SymCryptIntSetValueUint64( key->au64PubExp[0], exp );
+
+    SymCryptModExp( key->pmModulus, result, exp, SymCryptIntBitsizeOfValue(exp), SYMCRYPT_FLAG_DATA_PUBLIC,
+                    result, scratch + mod_size, scratch_size - mod_size );
+
+    SymCryptModElementGetValue( key->pmModulus, result, out, key_size, out_format, scratch + mod_size,
+                                scratch_size - mod_size );
+    free( scratch );
     return TRUE;
 }
 
-BOOL free_key_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext)
+BOOL encrypt_block_impl( ALG_ID algid, KEY_CONTEXT *ctx, const BYTE *in, BYTE *out )
 {
-    switch (aiAlgid)
+    switch (algid)
     {
-        case CALG_RSA_KEYX:
-        case CALG_RSA_SIGN:
-            rsa_free(&pKeyContext->rsa);
+    case CALG_RC2:
+        SymCryptRc2Encrypt( &ctx->rc2, in, out );
+        break;
+    case CALG_3DES:
+    case CALG_3DES_112:
+        SymCrypt3DesEncrypt( &ctx->des3, in, out );
+        break;
+    case CALG_DES:
+        SymCryptDesEncrypt( &ctx->des, in, out );
+        break;
+    case CALG_AES:
+    case CALG_AES_128:
+    case CALG_AES_192:
+    case CALG_AES_256:
+        SymCryptAesEncrypt( &ctx->aes, in, out );
+        break;
+    case CALG_RSA_KEYX:
+    case CALG_RSA_SIGN:
+    case CALG_SSL3_SHAMD5:
+        return rsa_encrypt( ctx->rsa.key, in, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, out, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST );
+    default:
+        SetLastError( NTE_BAD_ALGID );
+        return FALSE;
     }
-
     return TRUE;
 }
 
-BOOL setup_key_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                    DWORD dwEffectiveKeyLen, DWORD dwSaltLen, BYTE *abKeyValue)
+static BOOL rsa_decrypt( const SYMCRYPT_RSAKEY *key, const BYTE *in, SYMCRYPT_NUMBER_FORMAT in_format,
+                         BYTE *out, SYMCRYPT_NUMBER_FORMAT out_format )
 {
-    switch (aiAlgid) 
+    SIZE_T scratch_size, offset = 0, int_size, tmp_size, key_size = SymCryptRsakeySizeofModulus( key );
+    SYMCRYPT_INT *ciphertext, *plaintext, *tmp;
+    SYMCRYPT_MODELEMENT *crt_elements[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+    BYTE *scratch;
+    UINT32 i, crt_element_total = 0, crt_elements_size[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+
+    int_size = SymCryptSizeofIntFromDigits( key->nDigitsOfModulus );
+    tmp_size = SymCryptSizeofIntFromDigits( key->nMaxDigitsOfPrimes );
+
+    for (i = 0; i < key->nPrimes; i++)
     {
-        case CALG_RC4:
-            rc4_start(&pKeyContext->prng);
-            rc4_add_entropy(abKeyValue, dwKeyLen + dwSaltLen, &pKeyContext->prng);
-            /* bypass rc4_ready() to avoid the workaround for the Fluhrer, Mantin and Shamir attack,
-             * since Windows doesn't do that */
-            /* rc4_ready(&pKeyContext->prng); */
-            {
-                unsigned char buf[256];
-                ULONG len = MIN(pKeyContext->prng.rc4.s.x, sizeof(buf));
+        crt_elements_size[i] = SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( key->nBitsOfPrimes[i] );
+        crt_element_total += crt_elements_size[i];
+    }
 
-                memcpy(buf, pKeyContext->prng.rc4.s.buf, sizeof(buf));
-                rc4_stream_setup(&pKeyContext->prng.rc4.s, buf, len);
-                pKeyContext->prng.ready = 1;
-            }
-            break;
-
-        case CALG_RC2:
-            rc2_setup_ex(abKeyValue, dwKeyLen + dwSaltLen, dwEffectiveKeyLen ?
-                         dwEffectiveKeyLen : dwKeyLen << 3, 0, &pKeyContext->key);
-            break;
-
-        case CALG_3DES:
-            des3_setup(abKeyValue, 24, 0, &pKeyContext->key);
-            break;
-
-        case CALG_3DES_112:
-            memcpy(abKeyValue+16, abKeyValue, 8);
-            des3_setup(abKeyValue, 24, 0, &pKeyContext->key);
-            break;
-
-        case CALG_DES:
-            des_setup(abKeyValue, 8, 0, &pKeyContext->key);
-            break;
-
-        case CALG_AES:
-        case CALG_AES_128:
-            aes_setup(abKeyValue, 16, 0, &pKeyContext->key);
-            break;
-
-        case CALG_AES_192:
-            aes_setup(abKeyValue, 24, 0, &pKeyContext->key);
-            break;
-
-        case CALG_AES_256:
-            aes_setup(abKeyValue, 32, 0, &pKeyContext->key);
-            break;
+    scratch_size = 3 * int_size + tmp_size + crt_element_total +
+        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( key->nDigitsOfModulus ),
+            SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( key->nDigitsOfModulus ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( key->nDigitsOfModulus, key->nMaxDigitsOfPrimes ),
+                    SYMCRYPT_SCRATCH_BYTES_FOR_CRT_SOLUTION( key->nMaxDigitsOfPrimes ) ) ) );
+
+    if (!(scratch = malloc( scratch_size ))) return FALSE;
+
+    ciphertext = SymCryptIntCreate( scratch, scratch_size, key->nDigitsOfModulus );
+    offset += int_size;
+
+    plaintext = SymCryptIntCreate( scratch + offset, scratch_size - offset, key->nDigitsOfModulus );
+    offset += int_size;
+
+    tmp = SymCryptIntCreate( scratch + offset, scratch_size - offset, key->nMaxDigitsOfPrimes );
+    offset += tmp_size;
+
+    for (i = 0; i < key->nPrimes; i++)
+    {
+        crt_elements[i] = SymCryptModElementCreate( scratch + offset, scratch_size - offset, key->pmPrimes[i] );
+        offset += crt_elements_size[i];
     }
 
+    SymCryptIntSetValue( in, key_size, in_format, ciphertext );
+
+    for (i = 0; i < key->nPrimes; i++)
+    {
+        SymCryptIntDivMod( ciphertext, SymCryptDivisorFromModulus(key->pmPrimes[i]), NULL, tmp, scratch + offset,
+                           scratch_size - offset );
+
+        SymCryptIntToModElement( tmp, key->pmPrimes[i], crt_elements[i], scratch + offset, scratch_size - offset );
+
+        SymCryptModExp( key->pmPrimes[i], crt_elements[i], key->piCrtPrivExps[i], key->nBitsOfPrimes[i], 0,
+                        crt_elements[i], scratch + offset, scratch_size - offset );
+    }
+
+    SymCryptCrtSolve( key->nPrimes, (const SYMCRYPT_MODULUS **)key->pmPrimes,
+                      (SYMCRYPT_MODELEMENT **)key->peCrtInverses, crt_elements, 0, plaintext, scratch + offset,
+                      scratch_size - offset );
+
+    SymCryptIntGetValue( plaintext, out, key_size, out_format );
+    free( scratch );
     return TRUE;
 }
 
-BOOL duplicate_key_impl(ALG_ID aiAlgid, const KEY_CONTEXT *pSrcKeyContext,
-                        KEY_CONTEXT *pDestKeyContext) 
+BOOL decrypt_block_impl( ALG_ID algid, KEY_CONTEXT *ctx, const BYTE *in, BYTE *out )
 {
-    switch (aiAlgid) 
+    switch (algid)
     {
-        case CALG_RC4:
-        case CALG_RC2:
-        case CALG_3DES:
-        case CALG_3DES_112:
-        case CALG_DES:
-        case CALG_AES:
-        case CALG_AES_128:
-        case CALG_AES_192:
-        case CALG_AES_256:
-            *pDestKeyContext = *pSrcKeyContext;
-            break;
-        case CALG_RSA_KEYX:
-        case CALG_RSA_SIGN:
-            pDestKeyContext->rsa.type = pSrcKeyContext->rsa.type;
-            mp_init_copy(&pDestKeyContext->rsa.e, pSrcKeyContext->rsa.e);
-            mp_init_copy(&pDestKeyContext->rsa.d, pSrcKeyContext->rsa.d);
-            mp_init_copy(&pDestKeyContext->rsa.N, pSrcKeyContext->rsa.N);
-            mp_init_copy(&pDestKeyContext->rsa.p, pSrcKeyContext->rsa.p);
-            mp_init_copy(&pDestKeyContext->rsa.q, pSrcKeyContext->rsa.q);
-            mp_init_copy(&pDestKeyContext->rsa.qP, pSrcKeyContext->rsa.qP);
-            mp_init_copy(&pDestKeyContext->rsa.dP, pSrcKeyContext->rsa.dP);
-            mp_init_copy(&pDestKeyContext->rsa.dQ, pSrcKeyContext->rsa.dQ);
-            break;
-        
-        default:
-            SetLastError(NTE_BAD_ALGID);
-            return FALSE;
+    case CALG_RC2:
+        SymCryptRc2Decrypt( &ctx->rc2, in, out );
+        break;
+    case CALG_3DES:
+    case CALG_3DES_112:
+        SymCrypt3DesDecrypt( &ctx->des3, in, out );
+        break;
+    case CALG_DES:
+        SymCryptDesDecrypt( &ctx->des, in, out );
+        break;
+    case CALG_AES:
+    case CALG_AES_128:
+    case CALG_AES_192:
+    case CALG_AES_256:
+        SymCryptAesDecrypt( &ctx->aes, in, out );
+        break;
+    case CALG_RSA_KEYX:
+    case CALG_RSA_SIGN:
+    case CALG_SSL3_SHAMD5:
+        return rsa_decrypt( ctx->rsa.key, in, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, out, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST );
+    default:
+        SetLastError( NTE_BAD_ALGID );
+        return FALSE;
     }
-
     return TRUE;
 }
 
-static inline void reverse_bytes(BYTE *pbData, DWORD dwLen) {
-    BYTE swap;
-    DWORD i;
-
-    for (i=0; i<dwLen/2; i++) {
-        swap = pbData[i];
-        pbData[i] = pbData[dwLen-i-1];
-        pbData[dwLen-i-1] = swap;
-    }
+BOOL sign_hash_impl( KEY_CONTEXT *ctx, const BYTE *in, BYTE *out )
+{
+    return rsa_decrypt( ctx->rsa.key, in, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, out, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST );
 }
 
-BOOL encrypt_block_impl(ALG_ID aiAlgid, DWORD dwKeySpec, KEY_CONTEXT *pKeyContext, const BYTE *in,
-                        BYTE *out)
+BOOL verify_signature_impl( KEY_CONTEXT *ctx, const BYTE *in, BYTE *out )
 {
-    unsigned long inlen, outlen;
-
-    switch (aiAlgid) {
-        case CALG_RC2:
-            rc2_ecb_encrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_3DES:
-        case CALG_3DES_112:
-            des3_ecb_encrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_DES:
-            des_ecb_encrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_AES:
-        case CALG_AES_128:
-        case CALG_AES_192:
-        case CALG_AES_256:
-            aes_ecb_encrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_RSA_KEYX:
-        case CALG_RSA_SIGN:
-        case CALG_SSL3_SHAMD5:
-            outlen = inlen = (mp_count_bits(pKeyContext->rsa.N)+7)/8;
-            if (rsa_exptmod(in, inlen, out, &outlen, dwKeySpec, &pKeyContext->rsa) != CRYPT_OK) {
-                SetLastError(NTE_FAIL);
-                return FALSE;
-            }
-            reverse_bytes(out, outlen);
-            break;
+    return rsa_encrypt( ctx->rsa.key, in, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, out, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST );
+}
 
-        default:
-            SetLastError(NTE_BAD_ALGID);
-            return FALSE;
+BOOL encrypt_stream_impl( ALG_ID algid, KEY_CONTEXT *ctx, BYTE *stream, DWORD len )
+{
+    switch (algid)
+    {
+    case CALG_RC4:
+        SymCryptRc4Crypt( &ctx->rc4, stream, stream, len );
+        break;
+    default:
+        SetLastError( NTE_BAD_ALGID );
+        return FALSE;
     }
-
     return TRUE;
 }
 
-BOOL decrypt_block_impl(ALG_ID aiAlgid, DWORD dwKeySpec, KEY_CONTEXT *pKeyContext, const BYTE *in,
-                        BYTE *out)
+BOOL export_public_key_impl( const KEY_CONTEXT *ctx, BYTE *dst, DWORD *pubexp )
 {
-    unsigned long inlen, outlen;
-    BYTE *in_reversed;
-
-    switch (aiAlgid) {
-        case CALG_RC2:
-            rc2_ecb_decrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_3DES:
-        case CALG_3DES_112:
-            des3_ecb_decrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_DES:
-            des_ecb_decrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_AES:
-        case CALG_AES_128:
-        case CALG_AES_192:
-        case CALG_AES_256:
-            aes_ecb_decrypt(in, out, &pKeyContext->key);
-            break;
-
-        case CALG_RSA_KEYX:
-        case CALG_RSA_SIGN:
-        case CALG_SSL3_SHAMD5:
-            outlen = inlen = (mp_count_bits(pKeyContext->rsa.N)+7)/8;
-            in_reversed = malloc(inlen);
-            if (!in_reversed) {
-                SetLastError(NTE_NO_MEMORY);
-                return FALSE;
-            }
-            memcpy(in_reversed, in, inlen);
-            reverse_bytes(in_reversed, inlen);
-            if (rsa_exptmod(in_reversed, inlen, out, &outlen, dwKeySpec, &pKeyContext->rsa) != CRYPT_OK) {
-                free(in_reversed);
-                SetLastError(NTE_FAIL);
-                return FALSE;
-            }
-            free(in_reversed);
-            break;
+    BYTE *modulus = dst;
+    SIZE_T modulus_size;
+    UINT64 pubexp64;
 
-        default:
-            SetLastError(NTE_BAD_ALGID);
-            return FALSE;
-    }
+    if (!ctx->rsa.key) return FALSE;
+
+    modulus_size = SymCryptRsakeySizeofModulus( ctx->rsa.key );
 
+    if (SymCryptRsakeyGetValue( ctx->rsa.key, modulus, modulus_size, &pubexp64, 1, NULL, NULL, 0,
+                                SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, 0 ))
+    {
+        return FALSE;
+        SetLastError( NTE_FAIL );
+    }
+    *pubexp = pubexp64;
     return TRUE;
 }
 
-BOOL encrypt_stream_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext, BYTE *stream, DWORD dwLen)
+BOOL import_public_key_impl( ALG_ID algid, const BYTE *src, DWORD keylen, DWORD pubexp, KEY_CONTEXT *ctx )
 {
-    switch (aiAlgid) {
-        case CALG_RC4:
-            rc4_stream_crypt(&pKeyContext->prng.rc4.s, stream, dwLen, stream);
-            break;
+    const BYTE *modulus = src;
+    UINT64 exp64 = pubexp;
 
-        default:
-            SetLastError(NTE_BAD_ALGID);
-            return FALSE;
-    }
+    ctx->rsa.flags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+    if (algid == CALG_RSA_KEYX) ctx->rsa.flags |= SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+    else ctx->rsa.flags |= SYMCRYPT_FLAG_RSAKEY_SIGN | SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
 
+    if (!(ctx->rsa.key = alloc_rsa_key( keylen * 8, FALSE )))
+    {
+        SetLastError( NTE_FAIL );
+        return FALSE;
+    }
+    if (SymCryptRsakeySetValue( modulus, keylen, &exp64, 1, NULL, NULL, 0, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST,
+                                ctx->rsa.flags, ctx->rsa.key ))
+    {
+        SetLastError( NTE_FAIL );
+        return FALSE;
+    }
     return TRUE;
 }
 
-BOOL export_public_key_impl(BYTE *pbDest, const KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,DWORD *pdwPubExp)
+BOOL export_private_key_impl( const KEY_CONTEXT *ctx, BYTE *dst, DWORD *pubexp )
 {
-    mp_to_unsigned_bin(pKeyContext->rsa.N, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.N));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.N) < dwKeyLen)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.N), 0,
-               dwKeyLen - mp_unsigned_bin_size(pKeyContext->rsa.N));
-    *pdwPubExp = (DWORD)mp_get_int(pKeyContext->rsa.e);
-    return TRUE;
-}
+    BYTE *modulus, *primes[2], *exponents[2], *coefficient, *private_exp;
+    SIZE_T modulus_size, primes_sizes[2];
+    UINT64 pubexp64;
 
-BOOL import_public_key_impl(const BYTE *pbSrc, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                            DWORD dwPubExp)
-{
-    BYTE *pbTemp;
+    if (!ctx->rsa.key) return FALSE;
 
-    if (mp_init_multi(&pKeyContext->rsa.e, &pKeyContext->rsa.d, &pKeyContext->rsa.N, 
-                      &pKeyContext->rsa.dQ,&pKeyContext->rsa.dP,&pKeyContext->rsa.qP, 
-                      &pKeyContext->rsa.p, &pKeyContext->rsa.q, NULL))
+    modulus_size = SymCryptRsakeySizeofModulus( ctx->rsa.key );
+
+    primes_sizes[0] = SymCryptRsakeySizeofPrime( ctx->rsa.key, 0 );
+    primes_sizes[1] = SymCryptRsakeySizeofPrime( ctx->rsa.key, 1 );
+
+    modulus = dst;
+    primes[0] = modulus + modulus_size;
+    primes[1] = modulus + modulus_size + primes_sizes[0];
+
+    if (SymCryptRsakeyGetValue( ctx->rsa.key, modulus, modulus_size, &pubexp64, 1, primes, primes_sizes, 2,
+                                SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, 0 ))
     {
-        SetLastError(NTE_FAIL);
         return FALSE;
+        SetLastError( NTE_FAIL );
     }
 
-    pbTemp = malloc(dwKeyLen);
-    if (!pbTemp) return FALSE;
-    memcpy(pbTemp, pbSrc, dwKeyLen);
-    
-    pKeyContext->rsa.type = PK_PUBLIC;
-    reverse_bytes(pbTemp, dwKeyLen);
-    mp_read_unsigned_bin(pKeyContext->rsa.N, pbTemp, dwKeyLen);
-    free(pbTemp);
-    mp_set_int(pKeyContext->rsa.e, dwPubExp);
-
-    return TRUE;    
-}
+    exponents[0] = primes[1]    + primes_sizes[1];
+    exponents[1] = exponents[0] + primes_sizes[0];
+    coefficient  = exponents[1] + primes_sizes[1];
+    private_exp  = coefficient  + primes_sizes[0];
 
-BOOL export_private_key_impl(BYTE *pbDest, const KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                             DWORD *pdwPubExp)
-{
-    mp_to_unsigned_bin(pKeyContext->rsa.N, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.N));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.N) < dwKeyLen)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.N), 0,
-               dwKeyLen - mp_unsigned_bin_size(pKeyContext->rsa.N));
-    pbDest += dwKeyLen;
-    mp_to_unsigned_bin(pKeyContext->rsa.p, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.p));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.p) < (dwKeyLen+1)>>1)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.p), 0,
-               ((dwKeyLen+1)>>1) - mp_unsigned_bin_size(pKeyContext->rsa.p));
-    pbDest += (dwKeyLen+1)>>1;
-    mp_to_unsigned_bin(pKeyContext->rsa.q, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.q));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.q) < (dwKeyLen+1)>>1)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.q), 0,
-               ((dwKeyLen+1)>>1) - mp_unsigned_bin_size(pKeyContext->rsa.q));
-    pbDest += (dwKeyLen+1)>>1;
-    mp_to_unsigned_bin(pKeyContext->rsa.dP, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.dP));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.dP) < (dwKeyLen+1)>>1)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.dP), 0,
-               ((dwKeyLen+1)>>1) - mp_unsigned_bin_size(pKeyContext->rsa.dP));
-    pbDest += (dwKeyLen+1)>>1;
-    mp_to_unsigned_bin(pKeyContext->rsa.dQ, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.dQ));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.dQ) < (dwKeyLen+1)>>1)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.dQ), 0,
-               ((dwKeyLen+1)>>1) - mp_unsigned_bin_size(pKeyContext->rsa.dQ));
-    pbDest += (dwKeyLen+1)>>1;
-    mp_to_unsigned_bin(pKeyContext->rsa.qP, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.qP));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.qP) < (dwKeyLen+1)>>1)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.qP), 0,
-               ((dwKeyLen+1)>>1) - mp_unsigned_bin_size(pKeyContext->rsa.qP));
-    pbDest += (dwKeyLen+1)>>1;
-    mp_to_unsigned_bin(pKeyContext->rsa.d, pbDest);
-    reverse_bytes(pbDest, mp_unsigned_bin_size(pKeyContext->rsa.d));
-    if (mp_unsigned_bin_size(pKeyContext->rsa.d) < dwKeyLen)
-        memset(pbDest + mp_unsigned_bin_size(pKeyContext->rsa.d), 0,
-               dwKeyLen - mp_unsigned_bin_size(pKeyContext->rsa.d));
-    *pdwPubExp = (DWORD)mp_get_int(pKeyContext->rsa.e);
+    if (SymCryptRsakeyGetCrtValue( ctx->rsa.key, exponents, primes_sizes, 2, coefficient, primes_sizes[0],
+                                   private_exp, modulus_size, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, 0 ))
+    {
+        return FALSE;
+        SetLastError( NTE_FAIL );
+    }
 
+    *pubexp = pubexp64;
     return TRUE;
 }
 
-BOOL import_private_key_impl(const BYTE *pbSrc, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                             DWORD dwDataLen, DWORD dwPubExp)
+BOOL import_private_key_impl( ALG_ID algid, const BYTE *src, DWORD keylen, DWORD pubexp, KEY_CONTEXT *ctx )
 {
-    BYTE *pbTemp, *pbBigNum;
+    const BYTE *modulus = src, *primes[2];
+    SIZE_T primes_sizes[2];
+    UINT64 exp64 = pubexp;
+
+    ctx->rsa.flags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+    if (algid == CALG_RSA_KEYX) ctx->rsa.flags |= SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+    else ctx->rsa.flags |= SYMCRYPT_FLAG_RSAKEY_SIGN | SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+
+    if (!(ctx->rsa.key = alloc_rsa_key( keylen * 8, TRUE )))
+    {
+        SetLastError( NTE_FAIL );
+        return FALSE;
+    }
+
+    primes[0] = modulus + keylen;
+    primes[1] = primes[0] + keylen / 2;
+
+    primes_sizes[0] = keylen / 2;
+    primes_sizes[1] = keylen / 2;
 
-    if (mp_init_multi(&pKeyContext->rsa.e, &pKeyContext->rsa.d, &pKeyContext->rsa.N, 
-                      &pKeyContext->rsa.dQ,&pKeyContext->rsa.dP,&pKeyContext->rsa.qP, 
-                      &pKeyContext->rsa.p, &pKeyContext->rsa.q, NULL))
+    if (SymCryptRsakeySetValue( modulus, keylen, &exp64, 1, primes, primes_sizes, 2,
+                                SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, ctx->rsa.flags, ctx->rsa.key ))
     {
-        SetLastError(NTE_FAIL);
+        SetLastError( NTE_FAIL );
         return FALSE;
     }
 
-    pbTemp = malloc(2*dwKeyLen+5*((dwKeyLen+1)>>1));
-    if (!pbTemp) return FALSE;
-    memcpy(pbTemp, pbSrc, min(dwDataLen, 2*dwKeyLen+5*((dwKeyLen+1)>>1)));
-    pbBigNum = pbTemp;
-
-    pKeyContext->rsa.type = PK_PRIVATE;
-    reverse_bytes(pbBigNum, dwKeyLen);
-    mp_read_unsigned_bin(pKeyContext->rsa.N, pbBigNum, dwKeyLen);
-    pbBigNum += dwKeyLen;
-    reverse_bytes(pbBigNum, (dwKeyLen+1)>>1);
-    mp_read_unsigned_bin(pKeyContext->rsa.p, pbBigNum, (dwKeyLen+1)>>1);
-    pbBigNum += (dwKeyLen+1)>>1;
-    reverse_bytes(pbBigNum, (dwKeyLen+1)>>1);
-    mp_read_unsigned_bin(pKeyContext->rsa.q, pbBigNum, (dwKeyLen+1)>>1);
-    pbBigNum += (dwKeyLen+1)>>1;
-    reverse_bytes(pbBigNum, (dwKeyLen+1)>>1);
-    mp_read_unsigned_bin(pKeyContext->rsa.dP, pbBigNum, (dwKeyLen+1)>>1);
-    pbBigNum += (dwKeyLen+1)>>1;
-    reverse_bytes(pbBigNum, (dwKeyLen+1)>>1);
-    mp_read_unsigned_bin(pKeyContext->rsa.dQ, pbBigNum, (dwKeyLen+1)>>1);
-    pbBigNum += (dwKeyLen+1)>>1;
-    reverse_bytes(pbBigNum, (dwKeyLen+1)>>1);
-    mp_read_unsigned_bin(pKeyContext->rsa.qP, pbBigNum, (dwKeyLen+1)>>1);
-    pbBigNum += (dwKeyLen+1)>>1;
-    /* The size of the private exponent d is inferred from the remaining
-     * data length.
-     */
-    dwKeyLen = min(dwKeyLen, dwDataLen - (pbBigNum - pbTemp));
-    reverse_bytes(pbBigNum, dwKeyLen);
-    mp_read_unsigned_bin(pKeyContext->rsa.d, pbBigNum, dwKeyLen);
-    mp_set_int(pKeyContext->rsa.e, dwPubExp);
-    
-    free(pbTemp);
     return TRUE;
 }
diff --git a/dlls/rsaenh/implglue.h b/dlls/rsaenh/implglue.h
index b0a518830ac0..f9eb9ab6c8ee 100644
--- a/dlls/rsaenh/implglue.h
+++ b/dlls/rsaenh/implglue.h
@@ -24,50 +24,61 @@
 #ifndef __WINE_IMPLGLUE_H
 #define __WINE_IMPLGLUE_H
 
-#include "tomcrypt.h"
+#include "symcrypt.h"
+#include "symcrypt_low_level.h"
 
 #define RSAENH_MAX_HASH_SIZE        104
 
-typedef union tagKEY_CONTEXT {
-    symmetric_key key;
-    prng_state prng;
-    rsa_key rsa;
-} KEY_CONTEXT;
+struct rsa_key
+{
+    SYMCRYPT_RSAKEY *key;
+    UINT32           flags;
+};
 
-extern prng_state prng;
-extern int wprng;
+typedef union tagKEY_CONTEXT
+{
+    SYMCRYPT_DES_EXPANDED_KEY  des;
+    SYMCRYPT_3DES_EXPANDED_KEY des3;
+    SYMCRYPT_RC2_EXPANDED_KEY  rc2;
+    SYMCRYPT_RC4_STATE         rc4;
+    SYMCRYPT_AES_EXPANDED_KEY  aes;
+    struct rsa_key             rsa;
+} KEY_CONTEXT;
 
 struct hash
 {
-    const struct ltc_hash_descriptor *desc;
-    hash_state state;
+    const SYMCRYPT_HASH *desc;
+    SYMCRYPT_HASH_STATE  state;
 };
 
 BOOL init_hash_impl(ALG_ID algid, struct hash *hash);
-BOOL update_hash_impl(struct hash *hash, const BYTE *data, DWORD len);
-BOOL finalize_hash_impl(struct hash *hash, BYTE *hash_value, DWORD hash_size);
 
-BOOL new_key_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen);
-BOOL free_key_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext);
-BOOL setup_key_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                    DWORD dwEffectiveKeyLen, DWORD dwSaltLen, BYTE *abKeyValue);
-BOOL duplicate_key_impl(ALG_ID aiAlgid, const KEY_CONTEXT *pSrcKeyContext,
-                        KEY_CONTEXT *pDestKeyContext);
+static inline void update_hash_impl(struct hash *hash, const BYTE *data, DWORD len)
+{
+    SymCryptHashAppend( hash->desc, &hash->state, data, len );
+}
+
+static inline void finalize_hash_impl(struct hash *hash, BYTE *hash_value, DWORD hash_size)
+{
+    SymCryptHashResult( hash->desc, &hash->state, hash_value, hash_size );
+}
+
+BOOL new_key_impl(ALG_ID algid, KEY_CONTEXT *ctx, DWORD keylen);
+BOOL free_key_impl(ALG_ID algid, KEY_CONTEXT *ctx);
+BOOL setup_key_impl(ALG_ID algid, KEY_CONTEXT *ctx, DWORD keylen, DWORD effective_keylen, DWORD saltlen,
+                    const BYTE *keyvalue);
+BOOL duplicate_key_impl(ALG_ID algid, const KEY_CONTEXT *src, KEY_CONTEXT *dst);
+
+BOOL encrypt_block_impl(ALG_ID algid, KEY_CONTEXT *ctx, const BYTE *in, BYTE *out);
+BOOL decrypt_block_impl(ALG_ID algid, KEY_CONTEXT *ctx, const BYTE *in, BYTE *out);
+BOOL encrypt_stream_impl(ALG_ID algid, KEY_CONTEXT *ctx, BYTE *inout, DWORD len);
 
-/* dwKeySpec is optional for symmetric key algorithms */
-BOOL encrypt_block_impl(ALG_ID aiAlgid, DWORD dwKeySpec, KEY_CONTEXT *pKeyContext, const BYTE *pbIn,
-                        BYTE *pbOut);
-BOOL decrypt_block_impl(ALG_ID aiAlgid, DWORD dwKeySpec, KEY_CONTEXT *pKeyContext, const BYTE *pbIn,
-                        BYTE *pbOut);
-BOOL encrypt_stream_impl(ALG_ID aiAlgid, KEY_CONTEXT *pKeyContext, BYTE *pbInOut, DWORD dwLen);
+BOOL sign_hash_impl(KEY_CONTEXT *ctx, const BYTE *in, BYTE *out);
+BOOL verify_signature_impl(KEY_CONTEXT *ctx, const BYTE *in, BYTE *out);
 
-BOOL export_public_key_impl(BYTE *pbDest, const KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                            DWORD *pdwPubExp);
-BOOL import_public_key_impl(const BYTE *pbSrc, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                            DWORD dwPubExp);
-BOOL export_private_key_impl(BYTE *pbDest, const KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                             DWORD *pdwPubExp);
-BOOL import_private_key_impl(const BYTE* pbSrc, KEY_CONTEXT *pKeyContext, DWORD dwKeyLen,
-                             DWORD dwDataLen, DWORD dwPubExp);
+BOOL export_public_key_impl(const KEY_CONTEXT *ctx, BYTE *dst, DWORD *pubexp);
+BOOL import_public_key_impl(ALG_ID algid, const BYTE *src, DWORD keylen, DWORD pubexp, KEY_CONTEXT *ctx);
+BOOL export_private_key_impl(const KEY_CONTEXT *ctx, BYTE *dst, DWORD *pubexp);
+BOOL import_private_key_impl(ALG_ID algid, const BYTE *src, DWORD keylen, DWORD pubexp, KEY_CONTEXT *ctx);
 
 #endif /* __WINE_IMPLGLUE_H */
diff --git a/dlls/rsaenh/rsaenh.c b/dlls/rsaenh/rsaenh.c
index fbd500b7c43b..4de6a2d06175 100644
--- a/dlls/rsaenh/rsaenh.c
+++ b/dlls/rsaenh/rsaenh.c
@@ -354,10 +354,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID reserved)
         case DLL_PROCESS_ATTACH:
             DisableThreadLibraryCalls(hInstance);
             init_handle_table(&handle_table);
-            /* tomcrypt initialization */
-            init_LTM();
-            wprng = register_prng( &rc4_desc );
-            rng_make_prng( 1024, wprng, &prng, NULL );
+            SymCryptInit();
             break;
 
         case DLL_PROCESS_DETACH:
@@ -559,18 +556,18 @@ BOOL block_encrypt(CRYPTKEY *key, BYTE *data, DWORD *data_len, DWORD buf_len,
     {
         switch (key->dwMode) {
             case CRYPT_MODE_ECB:
-                encrypt_block_impl(key->aiAlgid, 0, context, in, out);
+                encrypt_block_impl(key->aiAlgid, context, in, out);
                 break;
             case CRYPT_MODE_CBC:
                 for (j = 0; j < key->dwBlockLen; j++)
                     in[j] ^= chain_vector[j];
-                encrypt_block_impl(key->aiAlgid, 0, context, in, out);
+                encrypt_block_impl(key->aiAlgid, context, in, out);
                 memcpy(chain_vector, out, key->dwBlockLen);
                 break;
             case CRYPT_MODE_CFB:
                 for (j = 0; j < key->dwBlockLen; j++)
                 {
-                    encrypt_block_impl(key->aiAlgid, 0, context, chain_vector, o);
+                    encrypt_block_impl(key->aiAlgid, context, chain_vector, o);
                     out[j] = in[j] ^ o[0];
                     for (k = 0; k < key->dwBlockLen - 1; k++)
                         chain_vector[k] = chain_vector[k+1];
@@ -681,13 +678,13 @@ static void destroy_hash(OBJECTHDR *pObject)
  */
 static inline BOOL init_hash(CRYPTHASH *pCryptHash) {
     DWORD dwLen;
-        
+
     switch (pCryptHash->aiAlgid) 
     {
         case CALG_HMAC:
             if (pCryptHash->pHMACInfo) { 
                 const PROV_ENUMALGS_EX *pAlgInfo;
-                
+
                 pAlgInfo = get_algid_info(pCryptHash->hProv, pCryptHash->pHMACInfo->HashAlgid);
                 if (!pAlgInfo)
                 {
@@ -702,7 +699,7 @@ static inline BOOL init_hash(CRYPTHASH *pCryptHash) {
                                  pCryptHash->pHMACInfo->cbInnerString);
             }
             return TRUE;
-            
+
         case CALG_MAC:
             dwLen = sizeof(DWORD);
             RSAENH_CPGetKeyParam(pCryptHash->hProv, pCryptHash->hKey, KP_BLOCKLEN, 
@@ -807,7 +804,7 @@ static inline void finalize_hash(CRYPTHASH *pCryptHash)
 {
     DWORD dwDataLen;
     CRYPTKEY *key;
-        
+
     switch (pCryptHash->aiAlgid)
     {
         case CALG_HMAC:
@@ -855,7 +852,7 @@ static inline void finalize_hash(CRYPTHASH *pCryptHash)
 static void destroy_key(OBJECTHDR *pObject)
 {
     CRYPTKEY *pCryptKey = (CRYPTKEY*)pObject;
-        
+
     free_key_impl(pCryptKey->aiAlgid, &pCryptKey->context);
     free_data_blob(&pCryptKey->siSChannelInfo.blobClientRandom);
     free_data_blob(&pCryptKey->siSChannelInfo.blobServerRandom);
@@ -989,7 +986,7 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
     const PROV_ENUMALGS_EX *peaAlgidInfo;
 
     *ppCryptKey = NULL;
-    
+
     /* 
      * Retrieve the CSP's capabilities for the given ALG_ID value
      */
@@ -1002,7 +999,7 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
      * Assume the default key length, if none is specified explicitly
      */
     if (dwKeyLen == 0) dwKeyLen = peaAlgidInfo->dwDefaultLen;
-    
+
     /*
      * Check if the requested key length is supported by the current CSP.
      * Adjust key length's for DES algorithms.
@@ -1533,7 +1530,7 @@ static HCRYPTPROV read_key_container(PCHAR pszContainerName, DWORD dwFlags, cons
         if (!lookup_handle(&handle_table, hKeyContainer, RSAENH_MAGIC_CONTAINER, 
                            (OBJECTHDR**)&pKeyContainer))
             return (HCRYPTPROV)INVALID_HANDLE_VALUE;
-    
+
         /* read_key_value calls import_key, which calls import_private_key,
          * which implicitly installs the key value into the appropriate key
          * container key.  Thus the ref count is incremented twice, once for
@@ -1603,7 +1600,7 @@ static BOOL build_hash_signature(BYTE *pbSignature, DWORD dwLen, ALG_ID aiAlgid,
     for (dwIdxOID = 0; aOIDDescriptor[dwIdxOID].aiAlgid; dwIdxOID++) {
         if (aOIDDescriptor[dwIdxOID].aiAlgid == aiAlgid) break;
     }
-    
+
     if (!aOIDDescriptor[dwIdxOID].aiAlgid) {
         SetLastError(NTE_BAD_ALGID);
         return FALSE;
@@ -1642,7 +1639,7 @@ static BOOL build_hash_signature(BYTE *pbSignature, DWORD dwLen, ALG_ID aiAlgid,
             pbSignature[i++] = abHashValue[j];
         }
     }
-    
+
     return TRUE;
 }
 
@@ -1675,7 +1672,7 @@ static BOOL tls1_p(HCRYPTHASH hHMAC, const PCRYPT_DATA_BLOB pblobSeed, BYTE *pbB
         SetLastError(NTE_BAD_HASH);
         return FALSE;
     }
-    
+
     /* compute A_1 = HMAC(seed) */
     init_hash(pHMAC);
     update_hash(pHMAC, pblobSeed->pbData, pblobSeed->cbData);
@@ -1743,13 +1740,13 @@ static BOOL tls1_prf(HCRYPTPROV hProv, HCRYPTPROV hSecret, const PCRYPT_DATA_BLO
     }
 
     dwHalfSecretLen = (pSecret->dwKeyLen+1)/2;
-    
+
     /* concatenation of the label and the seed */
     if (!concat_data_blobs(&blobLabelSeed, pblobLabel, pblobSeed)) goto exit;
-   
+
     /* zero out the buffer, since two random streams will be xor'ed into it. */
     memset(pbBuffer, 0, dwBufferLen);
-   
+
     /* build a 'fake' key, to hold the secret. CALG_SSL2_MASTER is used since it provides
      * the biggest range of valid key lengths. */
     hHalfSecret = new_key(hProv, CALG_SSL2_MASTER, MAKELONG(0,dwHalfSecretLen*8), &pHalfSecret);
@@ -1767,7 +1764,7 @@ static BOOL tls1_prf(HCRYPTPROV hProv, HCRYPTPROV hSecret, const PCRYPT_DATA_BLO
     hmacInfo.HashAlgid = CALG_SHA;
     if (!RSAENH_CPSetHashParam(hProv, hHMAC, HP_HMAC_INFO, (BYTE*)&hmacInfo, 0)) goto exit;
     if (!tls1_p(hHMAC, &blobLabelSeed, pbBuffer, dwBufferLen)) goto exit;
-    
+
     result = TRUE;
 exit:
     release_handle(&handle_table, hHalfSecret, RSAENH_MAGIC_KEY);
@@ -1795,7 +1792,7 @@ exit:
 static BOOL pad_data_pkcs1(const BYTE *abData, DWORD dwDataLen, BYTE *abBuffer, DWORD dwBufferLen, DWORD dwFlags)
 {
     DWORD i;
-    
+
     /* Ensure there is enough space for PKCS1 #2 padding */
     if (dwDataLen > dwBufferLen-11) {
         SetLastError(NTE_BAD_LEN);
@@ -1803,7 +1800,7 @@ static BOOL pad_data_pkcs1(const BYTE *abData, DWORD dwDataLen, BYTE *abBuffer,
     }
 
     memmove(abBuffer + dwBufferLen - dwDataLen, abData, dwDataLen);            
-    
+
     abBuffer[0] = 0x00;
     abBuffer[1] = RSAENH_PKC_BLOCKTYPE; 
     for (i=2; i < dwBufferLen - dwDataLen - 1; i++) 
@@ -1812,7 +1809,7 @@ static BOOL pad_data_pkcs1(const BYTE *abData, DWORD dwDataLen, BYTE *abBuffer,
         for (i-=8; i < dwBufferLen - dwDataLen - 1; i++) 
             abBuffer[i] = 0x03;
     abBuffer[i] = 0x00;
-    
+
     return TRUE; 
 }
 
@@ -2020,7 +2017,7 @@ static BOOL pad_data(HCRYPTPROV hProv, const BYTE *abData, DWORD dwDataLen, BYTE
 static BOOL unpad_data_pkcs1(const BYTE *abData, DWORD dwDataLen, BYTE *abBuffer, DWORD *dwBufferLen, DWORD dwFlags)
 {
     DWORD i;
-    
+
     if (dwDataLen < 3)
     {
         SetLastError(NTE_BAD_DATA);
@@ -2244,13 +2241,13 @@ BOOL WINAPI RSAENH_CPAcquireContext(HCRYPTPROV *phProv, LPSTR pszContainer,
             }
             *phProv = new_key_container("", dwFlags, pVTable);
             break;
-            
+
         default:
             *phProv = (HCRYPTPROV)INVALID_HANDLE_VALUE;
             SetLastError(NTE_BAD_FLAGS);
             return FALSE;
     }
-                
+
     if (*phProv != (HCRYPTPROV)INVALID_HANDLE_VALUE) {
         SetLastError(ERROR_SUCCESS);
         return TRUE;
@@ -2285,7 +2282,7 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
     CRYPTKEY *pCryptKey = NULL;
     CRYPTHASH *pCryptHash;
     const PROV_ENUMALGS_EX *peaAlgidInfo;
-        
+
     TRACE("(hProv=%08Ix, Algid=%08x, hKey=%08Ix, dwFlags=%08lx, phHash=%p)\n", hProv, Algid, hKey,
           dwFlags, phHash);
 
@@ -2359,7 +2356,7 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
         CRYPT_DATA_BLOB blobRandom, blobKeyExpansion = { 13, key_expansion };
 
         memcpy( key_expansion, keyex, sizeof keyex );
-        
+
         if (pCryptKey->dwState != RSAENH_KEYSTATE_MASTERKEY) {
             static const char msec[] = "master secret";
             BYTE master_secret[sizeof msec];
@@ -2367,7 +2364,7 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
             BYTE abKeyValue[48];
 
             memcpy( master_secret, msec, sizeof msec );
-    
+
             /* See RFC 2246, chapter 8.1 */
             if (!concat_data_blobs(&blobRandom, 
                                    &pCryptKey->siSChannelInfo.blobClientRandom, 
@@ -2413,19 +2410,19 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
 BOOL WINAPI RSAENH_CPDestroyHash(HCRYPTPROV hProv, HCRYPTHASH hHash)
 {
     TRACE("(hProv=%08Ix, hHash=%08Ix)\n", hProv, hHash);
-     
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
         return FALSE;
     }
-        
+
     if (!release_handle(&handle_table, hHash, RSAENH_MAGIC_HASH)) 
     {
         SetLastError(NTE_BAD_HASH);
         return FALSE;
     }
-    
+
     return TRUE;
 }
 
@@ -2446,19 +2443,19 @@ BOOL WINAPI RSAENH_CPDestroyHash(HCRYPTPROV hProv, HCRYPTHASH hHash)
 BOOL WINAPI RSAENH_CPDestroyKey(HCRYPTPROV hProv, HCRYPTKEY hKey)
 {
     TRACE("(hProv=%08Ix, hKey=%08Ix)\n", hProv, hKey);
-        
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
         return FALSE;
     }
-        
+
     if (!release_handle(&handle_table, hKey, RSAENH_MAGIC_KEY)) 
     {
         SetLastError(NTE_BAD_KEY);
         return FALSE;
     }
-    
+
     return TRUE;
 }
 
@@ -2482,7 +2479,7 @@ BOOL WINAPI RSAENH_CPDuplicateHash(HCRYPTPROV hUID, HCRYPTHASH hHash, DWORD *pdw
                                    DWORD dwFlags, HCRYPTHASH *phHash)
 {
     CRYPTHASH *pSrcHash, *pDestHash;
-    
+
     TRACE("(hUID=%08Ix, hHash=%08Ix, pdwReserved=%p, dwFlags=%08lx, phHash=%p)\n", hUID, hHash,
            pdwReserved, dwFlags, phHash);
 
@@ -2537,7 +2534,7 @@ BOOL WINAPI RSAENH_CPDuplicateKey(HCRYPTPROV hUID, HCRYPTKEY hKey, DWORD *pdwRes
                                   DWORD dwFlags, HCRYPTKEY *phKey)
 {
     CRYPTKEY *pSrcKey, *pDestKey;
-    
+
     TRACE("(hUID=%08Ix, hKey=%08Ix, pdwReserved=%p, dwFlags=%08lx, phKey=%p)\n", hUID, hKey,
           pdwReserved, dwFlags, phKey);
 
@@ -2602,15 +2599,15 @@ BOOL WINAPI RSAENH_CPDuplicateKey(HCRYPTPROV hUID, HCRYPTKEY hKey, DWORD *pdwRes
  *
  *  This function uses the standard WINAPI protocol for querying data of dynamic length. 
  */
-BOOL WINAPI RSAENH_CPEncrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, 
-                             DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen)
+BOOL WINAPI RSAENH_CPEncrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags,
+                             BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen)
 {
     CRYPTKEY *pCryptKey;
-        
+
     TRACE("(hProv=%08Ix, hKey=%08Ix, hHash=%08Ix, Final=%d, dwFlags=%08lx, pbData=%p, "
           "pdwDataLen=%p, dwBufLen=%ld)\n", hProv, hKey, hHash, Final, dwFlags, pbData, pdwDataLen,
           dwBufLen);
-    
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
@@ -2684,7 +2681,7 @@ BOOL WINAPI RSAENH_CPEncrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
             return FALSE;
         }
         if (!pad_data(hProv, pbData, *pdwDataLen, pbData, pCryptKey->dwBlockLen, dwFlags)) return FALSE;
-        encrypt_block_impl(pCryptKey->aiAlgid, PK_PUBLIC, &pCryptKey->context, pbData, pbData);
+        encrypt_block_impl(pCryptKey->aiAlgid, &pCryptKey->context, pbData, pbData);
         *pdwDataLen = pCryptKey->dwBlockLen;
         Final = TRUE;
     } else {
@@ -2731,7 +2728,7 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
 
     TRACE("(hProv=%08Ix, hKey=%08Ix, hHash=%08Ix, Final=%d, dwFlags=%08lx, pbData=%p, "
           "pdwDataLen=%p)\n", hProv, hKey, hHash, Final, dwFlags, pbData, pdwDataLen);
-    
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
@@ -2771,25 +2768,25 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
         for (i=0, in=pbData; i<*pdwDataLen; i+=pCryptKey->dwBlockLen, in+=pCryptKey->dwBlockLen) {
             switch (pCryptKey->dwMode) {
                 case CRYPT_MODE_ECB:
-                    decrypt_block_impl(pCryptKey->aiAlgid, 0, &pCryptKey->context, in, out);
+                    decrypt_block_impl(pCryptKey->aiAlgid, &pCryptKey->context, in, out);
                     break;
-                
+
                 case CRYPT_MODE_CBC:
-                    decrypt_block_impl(pCryptKey->aiAlgid, 0, &pCryptKey->context, in, out);
+                    decrypt_block_impl(pCryptKey->aiAlgid, &pCryptKey->context, in, out);
                     for (j=0; j<pCryptKey->dwBlockLen; j++) out[j] ^= pCryptKey->abChainVector[j];
                     memcpy(pCryptKey->abChainVector, in, pCryptKey->dwBlockLen);
                     break;
 
                 case CRYPT_MODE_CFB:
                     for (j=0; j<pCryptKey->dwBlockLen; j++) {
-                        encrypt_block_impl(pCryptKey->aiAlgid, 0, &pCryptKey->context, pCryptKey->abChainVector, o);
+                        encrypt_block_impl(pCryptKey->aiAlgid, &pCryptKey->context, pCryptKey->abChainVector, o);
                         out[j] = in[j] ^ o[0];
                         for (k=0; k<pCryptKey->dwBlockLen-1; k++) 
                             pCryptKey->abChainVector[k] = pCryptKey->abChainVector[k+1];
                         pCryptKey->abChainVector[k] = in[j];
                     }
                     break;
-                    
+
                 default:
                     SetLastError(NTE_BAD_ALGID);
                     return FALSE;
@@ -2828,21 +2825,21 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
             SetLastError(NTE_BAD_KEY);
             return FALSE;
         }
-        decrypt_block_impl(pCryptKey->aiAlgid, PK_PRIVATE, &pCryptKey->context, pbData, pbData);
+        decrypt_block_impl(pCryptKey->aiAlgid, &pCryptKey->context, pbData, pbData);
         if (!unpad_data(hProv, pbData, pCryptKey->dwBlockLen, pbData, pdwDataLen, dwFlags)) return FALSE;
         Final = TRUE;
     } else {
         SetLastError(NTE_BAD_TYPE);
         return FALSE;
     } 
-    
+
     if (Final) setup_key(pCryptKey);
 
     if (is_valid_handle(&handle_table, hHash, RSAENH_MAGIC_HASH)) {
         if (*pdwDataLen>dwMax ||
             !RSAENH_CPHashData(hProv, hHash, pbData, *pdwDataLen, 0)) return FALSE;
     }
-    
+
     return TRUE;
 }
 
@@ -2879,7 +2876,7 @@ static BOOL crypt_export_simple(CRYPTKEY *pCryptKey, CRYPTKEY *pPubKey,
             return FALSE;
         }
 
-        encrypt_block_impl(pPubKey->aiAlgid, PK_PUBLIC, &pPubKey->context, (BYTE*)(pAlgid+1), (BYTE*)(pAlgid+1));
+        encrypt_block_impl(pPubKey->aiAlgid, &pPubKey->context, (BYTE *)(pAlgid + 1), (BYTE *)(pAlgid + 1));
     }
     *pdwDataLen = dwDataLen;
     return TRUE;
@@ -2913,8 +2910,7 @@ static BOOL crypt_export_public_key(CRYPTKEY *pCryptKey, BYTE *pbData,
         pRSAPubKey->magic = RSAENH_MAGIC_RSA1;
         pRSAPubKey->bitlen = pCryptKey->dwKeyLen << 3;
 
-        export_public_key_impl((BYTE*)(pRSAPubKey+1), &pCryptKey->context,
-                               pCryptKey->dwKeyLen, &pRSAPubKey->pubexp);
+        export_public_key_impl(&pCryptKey->context, (BYTE *)(pRSAPubKey + 1), &pRSAPubKey->pubexp);
     }
     *pdwDataLen = dwDataLen;
     return TRUE;
@@ -2953,9 +2949,7 @@ static BOOL crypt_export_private_key(CRYPTKEY *pCryptKey, BOOL force,
 
         pRSAPubKey->magic = RSAENH_MAGIC_RSA2;
         pRSAPubKey->bitlen = pCryptKey->dwKeyLen << 3;
-
-        export_private_key_impl((BYTE*)(pRSAPubKey+1), &pCryptKey->context,
-                                pCryptKey->dwKeyLen, &pRSAPubKey->pubexp);
+        export_private_key_impl(&pCryptKey->context, (BYTE *)(pRSAPubKey + 1), &pRSAPubKey->pubexp);
     }
     *pdwDataLen = dwDataLen;
     return TRUE;
@@ -3014,14 +3008,14 @@ static BOOL crypt_export_key(CRYPTKEY *pCryptKey, HCRYPTKEY hPubKey,
                              BYTE *pbData, DWORD *pdwDataLen)
 {
     CRYPTKEY *pPubKey;
-    
+
     if (dwFlags & CRYPT_SSL2_FALLBACK) {
         if (pCryptKey->aiAlgid != CALG_SSL2_MASTER) {
             SetLastError(NTE_BAD_KEY);
             return FALSE;
         }
     }
-    
+
     switch ((BYTE)dwBlobType)
     {
         case SIMPLEBLOB:
@@ -3031,7 +3025,7 @@ static BOOL crypt_export_key(CRYPTKEY *pCryptKey, HCRYPTKEY hPubKey,
             }
             return crypt_export_simple(pCryptKey, pPubKey, dwFlags, pbData,
                                        pdwDataLen);
-            
+
         case PUBLICKEYBLOB:
             if (is_valid_handle(&handle_table, hPubKey, RSAENH_MAGIC_KEY)) {
                 SetLastError(NTE_BAD_KEY); /* FIXME: error code? */
@@ -3045,7 +3039,7 @@ static BOOL crypt_export_key(CRYPTKEY *pCryptKey, HCRYPTKEY hPubKey,
 
         case PLAINTEXTKEYBLOB:
             return crypt_export_plaintext_key(pCryptKey, pbData, pdwDataLen);
-            
+
         default:
             SetLastError(NTE_BAD_TYPE); /* FIXME: error code? */
             return FALSE;
@@ -3191,8 +3185,8 @@ static BOOL import_private_key(HCRYPTPROV hProv, const BYTE *pbData, DWORD dwDat
     *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, MAKELONG(0,pRSAPubKey->bitlen), &pCryptKey);
     if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
     setup_key(pCryptKey);
-    ret = import_private_key_impl((const BYTE*)(pRSAPubKey+1), &pCryptKey->context,
-                                   pRSAPubKey->bitlen/8, dwDataLen, pRSAPubKey->pubexp);
+    ret = import_private_key_impl(pBlobHeader->aiKeyAlg, (const BYTE *)(pRSAPubKey + 1),
+                                  pRSAPubKey->bitlen / 8, pRSAPubKey->pubexp, &pCryptKey->context);
     if (ret) {
         if (dwFlags & CRYPT_EXPORTABLE)
             pCryptKey->dwPermissions |= CRYPT_EXPORT;
@@ -3268,8 +3262,8 @@ static BOOL import_public_key(HCRYPTPROV hProv, const BYTE *pbData, DWORD dwData
     *phKey = new_key(hProv, algID, MAKELONG(0,pRSAPubKey->bitlen), &pCryptKey);
     if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
     setup_key(pCryptKey);
-    ret = import_public_key_impl((const BYTE*)(pRSAPubKey+1), &pCryptKey->context,
-                                  pRSAPubKey->bitlen >> 3, pRSAPubKey->pubexp);
+    ret = import_public_key_impl(algID, (const BYTE *)(pRSAPubKey + 1), pRSAPubKey->bitlen / 8,
+                                 pRSAPubKey->pubexp, &pCryptKey->context);
     if (ret) {
         if (dwFlags & CRYPT_EXPORTABLE)
             pCryptKey->dwPermissions |= CRYPT_EXPORT;
@@ -3331,7 +3325,7 @@ static BOOL import_symmetric_key(HCRYPTPROV hProv, const BYTE *pbData, DWORD dwD
 
     pbDecrypted = malloc(pPubKey->dwBlockLen);
     if (!pbDecrypted) return FALSE;
-    decrypt_block_impl(pPubKey->aiAlgid, PK_PRIVATE, &pPubKey->context, pbKeyStream, pbDecrypted);
+    decrypt_block_impl(pPubKey->aiAlgid, &pPubKey->context, pbKeyStream, pbDecrypted);
 
     dwKeyLen = RSAENH_MAX_KEY_SIZE;
     if (!unpad_data(hProv, pbDecrypted, pPubKey->dwBlockLen, pbDecrypted, &dwKeyLen, dwFlags)) {
@@ -3505,11 +3499,11 @@ static BOOL import_key(HCRYPTPROV hProv, const BYTE *pbData, DWORD dwDataLen, HC
         case PRIVATEKEYBLOB:    
             return import_private_key(hProv, pbData, dwDataLen, dwFlags,
                                       fStoreKey, phKey);
-                
+
         case PUBLICKEYBLOB:
             return import_public_key(hProv, pbData, dwDataLen, dwFlags,
                                      phKey);
-                
+
         case SIMPLEBLOB:
             return import_symmetric_key(hProv, pbData, dwDataLen, hPubKey,
                                         dwFlags, phKey);
@@ -3585,7 +3579,7 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
         /* MSDN: hProv not containing valid context handle */
         return FALSE;
     }
-    
+
     switch (Algid)
     {
         case AT_SIGNATURE:
@@ -3611,7 +3605,7 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
                                         FALSE);
             }
             break;
-            
+
         case CALG_RC2:
         case CALG_RC4:
         case CALG_DES:
@@ -3645,13 +3639,13 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
                 setup_key(pCryptKey);
             }
             break;
-            
+
         default:
             /* MSDN: Algorithm not supported specified by Algid */
             SetLastError(NTE_BAD_ALGID);
             return FALSE;
     }
-            
+
     return *phKey != (HCRYPTKEY)INVALID_HANDLE_VALUE;
 }
 
@@ -3672,7 +3666,7 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
 BOOL WINAPI RSAENH_CPGenRandom(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer)
 {
     TRACE("(hProv=%08Ix, dwLen=%ld, pbBuffer=%p)\n", hProv, dwLen, pbBuffer);
-    
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         /* MSDN: hProv not containing valid context handle */
@@ -3708,10 +3702,10 @@ BOOL WINAPI RSAENH_CPGetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
                                   DWORD *pdwDataLen, DWORD dwFlags) 
 {
     CRYPTHASH *pCryptHash;
-        
+
     TRACE("(hProv=%08Ix, hHash=%08Ix, dwParam=%08lx, pbData=%p, pdwDataLen=%p, dwFlags=%08lx)\n",
         hProv, hHash, dwParam, pbData, pdwDataLen, dwFlags);
-    
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
@@ -3723,7 +3717,7 @@ BOOL WINAPI RSAENH_CPGetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
         SetLastError(NTE_BAD_FLAGS);
         return FALSE;
     }
-    
+
     if (!lookup_handle(&handle_table, hHash, RSAENH_MAGIC_HASH,
                        (OBJECTHDR**)&pCryptHash))
     {
@@ -3736,7 +3730,7 @@ BOOL WINAPI RSAENH_CPGetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
-    
+
     switch (dwParam)
     {
         case HP_ALGID:
@@ -3982,7 +3976,7 @@ BOOL WINAPI RSAENH_CPSetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
 
         case KP_CLIENT_RANDOM:
             return copy_data_blob(&pCryptKey->siSChannelInfo.blobClientRandom, (PCRYPT_DATA_BLOB)pbData);
-            
+
         case KP_SERVER_RANDOM:
             return copy_data_blob(&pCryptKey->siSChannelInfo.blobServerRandom, (PCRYPT_DATA_BLOB)pbData);
 
@@ -4026,7 +4020,7 @@ BOOL WINAPI RSAENH_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
 {
     CRYPTKEY *pCryptKey;
     DWORD dwValue;
-        
+
     TRACE("(hProv=%08Ix, hKey=%08Ix, dwParam=%08lx, pbData=%p, pdwDataLen=%p dwFlags=%08lx)\n",
           hProv, hKey, dwParam, pbData, pdwDataLen, dwFlags);
 
@@ -4052,7 +4046,7 @@ BOOL WINAPI RSAENH_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
         case KP_IV:
             return copy_param(pbData, pdwDataLen, pCryptKey->abInitVector,
                               pCryptKey->dwBlockLen);
-        
+
         case KP_SALT:
             switch (pCryptKey->aiAlgid) {
                 case CALG_RC2:
@@ -4103,7 +4097,7 @@ BOOL WINAPI RSAENH_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
             return FALSE;
     }
 }
-                        
+
 /******************************************************************************
  * CPGetProvParam (RSAENH.@)
  *
@@ -4135,7 +4129,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
     PROV_ENUMALGS provEnumalgs;
     DWORD dwTemp;
     HKEY hKey;
-   
+
     /* This is for dwParam PP_CRYPT_COUNT_KEY_USE.
      * IE6 SP1 asks for it in the 'About' dialog.
      * Returning this BLOB seems to satisfy IE. The marked 0x00 seem 
@@ -4163,7 +4157,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
-    
+
     if (!(pKeyContainer = get_key_container(hProv)))
     {
         /* MSDN: hProv not containing valid context handle */
@@ -4217,7 +4211,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
                 *pdwDataLen = (DWORD)MAX_PATH + 1;
                 return TRUE;
             }
- 
+
             if (!open_container_key("", dwFlags, KEY_READ, &hKey))
             {
                 SetLastError(ERROR_NO_MORE_ITEMS);
@@ -4230,7 +4224,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
             {
                 case ERROR_MORE_DATA:
                     *pdwDataLen = (DWORD)MAX_PATH + 1;
- 
+
                 case ERROR_SUCCESS:
                     pKeyContainer->dwEnumContainersCtr++;
                     RegCloseKey(hKey);
@@ -4242,7 +4236,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
                     RegCloseKey(hKey);
                     return FALSE;
             }
- 
+
         case PP_ENUMALGS:
         case PP_ENUMALGS_EX:
             if (((pKeyContainer->dwEnumAlgsCtr >= RSAENH_MAX_ENUMALGS-1) ||
@@ -4258,7 +4252,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
                 if (pbData && (*pdwDataLen >= sizeof(PROV_ENUMALGS))) 
                     pKeyContainer->dwEnumAlgsCtr = ((dwFlags & CRYPT_FIRST) == CRYPT_FIRST) ? 
                         0 : pKeyContainer->dwEnumAlgsCtr+1;
-            
+
                 provEnumalgs.aiAlgid = aProvEnumAlgsEx
                     [pKeyContainer->dwPersonality][pKeyContainer->dwEnumAlgsCtr].aiAlgid;
                 provEnumalgs.dwBitLen = aProvEnumAlgsEx
@@ -4349,10 +4343,10 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
     CRYPTHASH *pCryptHash;
     BYTE abHashValue[RSAENH_MAX_HASH_SIZE*2];
     DWORD dwLen;
-    
+
     TRACE("(hProv=%08Ix, Algid=%d, hBaseData=%08Ix, dwFlags=%08lx phKey=%p)\n", hProv, Algid,
            hBaseData, dwFlags, phKey);
-    
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
@@ -4414,12 +4408,12 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
                 DWORD i;
 
                 memcpy(old_hashval, pCryptHash->abHashValue, RSAENH_MAX_HASH_SIZE);
-            
+
                 for (i=0; i<RSAENH_HMAC_DEF_PAD_LEN; i++) {
                     pad1[i] = RSAENH_HMAC_DEF_IPAD_CHAR ^ (i<dwLen ? abHashValue[i] : 0);
                     pad2[i] = RSAENH_HMAC_DEF_OPAD_CHAR ^ (i<dwLen ? abHashValue[i] : 0);
                 }
-                
+
                 init_hash(pCryptHash);
                 update_hash(pCryptHash, pad1, RSAENH_HMAC_DEF_PAD_LEN);
                 finalize_hash(pCryptHash);
@@ -4442,7 +4436,7 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
             {
                 copy_len += pCryptKey->dwSaltLen;
             }
-    
+
             memcpy(pCryptKey->abKeyValue, abHashValue, 
                    RSAENH_MIN(copy_len, sizeof(pCryptKey->abKeyValue)));
             break;
@@ -4454,7 +4448,7 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
                 SetLastError(NTE_FAIL); /* FIXME error code */
                 return FALSE;
             }
-                
+
             switch (Algid) 
             {
                 /* See RFC 2246, chapter 6.3 Key calculation */
@@ -4482,7 +4476,7 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
                                ((dwFlags & CRYPT_SERVER) ? pCryptKey->dwBlockLen : 0)),
                            pCryptKey->dwBlockLen);
                     break;
-                    
+
                 case CALG_SCHANNEL_MAC_KEY:
                     *phKey = new_key(hProv, Algid, 
                                      MAKELONG(LOWORD(dwFlags),pMasterKey->siSChannelInfo.saMACAlg.cBits),
@@ -4493,7 +4487,7 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
                                pMasterKey->siSChannelInfo.saMACAlg.cBits / 8 : 0),
                            pMasterKey->siSChannelInfo.saMACAlg.cBits / 8);
                     break;
-                    
+
                 default:
                     SetLastError(NTE_BAD_ALGID);
                     return FALSE;
@@ -4531,7 +4525,7 @@ BOOL WINAPI RSAENH_CPGetUserKey(HCRYPTPROV hProv, DWORD dwKeySpec, HCRYPTKEY *ph
     KEYCONTAINER *pKeyContainer;
 
     TRACE("(hProv=%08Ix, dwKeySpec=%08lx, phUserKey=%p)\n", hProv, dwKeySpec, phUserKey);
-    
+
     if (!(pKeyContainer = get_key_container(hProv)))
     {
         /* MSDN: hProv not containing valid context handle */
@@ -4588,7 +4582,7 @@ BOOL WINAPI RSAENH_CPHashData(HCRYPTPROV hProv, HCRYPTHASH hHash, const BYTE *pb
                               DWORD dwDataLen, DWORD dwFlags)
 {
     CRYPTHASH *pCryptHash;
-        
+
     TRACE("(hProv=%08Ix, hHash=%08Ix, pbData=%p, dwDataLen=%ld, dwFlags=%08lx)\n",
           hProv, hHash, pbData, dwDataLen, dwFlags);
 
@@ -4610,7 +4604,7 @@ BOOL WINAPI RSAENH_CPHashData(HCRYPTPROV hProv, HCRYPTHASH hHash, const BYTE *pb
         SetLastError(NTE_BAD_ALGID);
         return FALSE;
     }
-    
+
     if (pCryptHash->dwState != RSAENH_HASHSTATE_HASHING)
     {
         SetLastError(NTE_BAD_HASH_STATE);
@@ -4697,7 +4691,7 @@ BOOL WINAPI RSAENH_CPReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
         SetLastError(NTE_BAD_FLAGS);
         return FALSE;
     }
-    
+
     return TRUE;
 }
 
@@ -4742,14 +4736,14 @@ BOOL WINAPI RSAENH_CPSetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
         SetLastError(NTE_BAD_FLAGS);
         return FALSE;
     }
-    
+
     if (!lookup_handle(&handle_table, hHash, RSAENH_MAGIC_HASH,
                        (OBJECTHDR**)&pCryptHash))
     {
         SetLastError(NTE_BAD_HASH);
         return FALSE;
     }
-    
+
     switch (dwParam) {
         case HP_HMAC_INFO:
             free_hmac_info(pCryptHash->pHMACInfo);
@@ -4791,7 +4785,7 @@ BOOL WINAPI RSAENH_CPSetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
             for (i=0; i<RSAENH_MIN(pCryptKey->dwKeyLen,pCryptHash->pHMACInfo->cbOuterString); i++) {
                 pCryptHash->pHMACInfo->pbOuterString[i] ^= pCryptKey->abKeyValue[i];
             }
-            
+
             init_hash(pCryptHash);
             return TRUE;
 
@@ -4799,13 +4793,13 @@ BOOL WINAPI RSAENH_CPSetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
             memcpy(pCryptHash->abHashValue, pbData, pCryptHash->dwHashSize);
             pCryptHash->dwState = RSAENH_HASHSTATE_FINISHED;
             return TRUE;
-           
+
         case HP_TLS1PRF_SEED:
             return copy_data_blob(&pCryptHash->tpPRFParams.blobSeed, (PCRYPT_DATA_BLOB)pbData);
 
         case HP_TLS1PRF_LABEL:
             return copy_data_blob(&pCryptHash->tpPRFParams.blobLabel, (PCRYPT_DATA_BLOB)pbData);
-            
+
         default:
             SetLastError(NTE_BAD_TYPE);
             return FALSE;
@@ -4903,9 +4897,9 @@ BOOL WINAPI RSAENH_CPSignHash(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwKeySpe
         SetLastError(NTE_BAD_FLAGS);
         return FALSE;
     }
-    
+
     if (!RSAENH_CPGetUserKey(hProv, dwKeySpec, &hCryptKey)) return FALSE;
-            
+
     if (!lookup_handle(&handle_table, hCryptKey, RSAENH_MAGIC_KEY,
                        (OBJECTHDR**)&pCryptKey))
     {
@@ -4933,19 +4927,18 @@ BOOL WINAPI RSAENH_CPSignHash(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwKeySpe
             goto out;
         }
     }
-    
+
     dwHashLen = sizeof(DWORD);
     if (!RSAENH_CPGetHashParam(hProv, hHash, HP_ALGID, (BYTE*)&aiAlgid, &dwHashLen, 0)) goto out;
-    
+
     dwHashLen = RSAENH_MAX_HASH_SIZE;
     if (!RSAENH_CPGetHashParam(hProv, hHash, HP_HASHVAL, abHashValue, &dwHashLen, 0)) goto out;
- 
 
     if (!build_hash_signature(pbSignature, *pdwSigLen, aiAlgid, abHashValue, dwHashLen, dwFlags)) {
         goto out;
     }
 
-    ret = encrypt_block_impl(pCryptKey->aiAlgid, PK_PRIVATE, &pCryptKey->context, pbSignature, pbSignature);
+    ret = sign_hash_impl(&pCryptKey->context, pbSignature, pbSignature);
 out:
     RSAENH_CPDestroyKey(hProv, hCryptKey);
     return ret;
@@ -4970,8 +4963,7 @@ out:
  *  Failure: FALSE (GetLastError() == NTE_BAD_SIGNATURE, if signature is invalid)
  */
 BOOL WINAPI RSAENH_CPVerifySignature(HCRYPTPROV hProv, HCRYPTHASH hHash, const BYTE *pbSignature,
-                                     DWORD dwSigLen, HCRYPTKEY hPubKey, LPCWSTR sDescription, 
-                                     DWORD dwFlags)
+                                     DWORD dwSigLen, HCRYPTKEY hPubKey, LPCWSTR sDescription, DWORD dwFlags)
 {
     BYTE *pbConstructed = NULL, *pbDecrypted = NULL;
     CRYPTKEY *pCryptKey;
@@ -4983,18 +4975,18 @@ BOOL WINAPI RSAENH_CPVerifySignature(HCRYPTPROV hProv, HCRYPTHASH hHash, const B
     TRACE("(hProv=%08Ix, hHash=%08Ix, pbSignature=%p, dwSigLen=%ld, hPubKey=%08Ix, sDescription=%s, "
           "dwFlags=%08lx)\n", hProv, hHash, pbSignature, dwSigLen, hPubKey, debugstr_w(sDescription),
           dwFlags);
-        
+
     if (dwFlags & ~(CRYPT_NOHASHOID|CRYPT_X931_FORMAT)) {
         SetLastError(NTE_BAD_FLAGS);
         return FALSE;
     }
-    
+
     if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
     {
         SetLastError(NTE_BAD_UID);
         return FALSE;
     }
- 
+
     if (!lookup_handle(&handle_table, hPubKey, RSAENH_MAGIC_KEY,
                        (OBJECTHDR**)&pCryptKey))
     {
@@ -5024,10 +5016,10 @@ BOOL WINAPI RSAENH_CPVerifySignature(HCRYPTPROV hProv, HCRYPTHASH hHash, const B
             return FALSE;
         }
     }
-    
+
     dwHashLen = sizeof(DWORD);
     if (!RSAENH_CPGetHashParam(hProv, hHash, HP_ALGID, (BYTE*)&aiAlgid, &dwHashLen, 0)) return FALSE;
-    
+
     dwHashLen = RSAENH_MAX_HASH_SIZE;
     if (!RSAENH_CPGetHashParam(hProv, hHash, HP_HASHVAL, abHashValue, &dwHashLen, 0)) return FALSE;
 
@@ -5043,10 +5035,7 @@ BOOL WINAPI RSAENH_CPVerifySignature(HCRYPTPROV hProv, HCRYPTHASH hHash, const B
         goto cleanup;
     }
 
-    if (!decrypt_block_impl(pCryptKey->aiAlgid, PK_PUBLIC, &pCryptKey->context, pbSignature, pbDecrypted))
-    {
-        goto cleanup;
-    }
+    if (!verify_signature_impl(&pCryptKey->context, pbSignature, pbDecrypted)) goto cleanup;
 
     if (build_hash_signature(pbConstructed, dwSigLen, aiAlgid, abHashValue, dwHashLen, dwFlags) &&
         !memcmp(pbDecrypted, pbConstructed, dwSigLen)) {
diff --git a/dlls/sane.ds/ds_ctrl.c b/dlls/sane.ds/ds_ctrl.c
index 13055b9c30e2..9f18ff2fe977 100644
--- a/dlls/sane.ds/ds_ctrl.c
+++ b/dlls/sane.ds/ds_ctrl.c
@@ -239,6 +239,7 @@ TW_UINT16 SANE_PendingXfersEndXfer (pTW_IDENTITY pOrigin,
                 SANE_Cancel();
             }
         }
+        TRACE("Count %d, currentState %d\n", pPendingXfers->Count, activeDS.currentState);
         twRC = TWRC_SUCCESS;
         activeDS.twCC = TWCC_SUCCESS;
     }
@@ -355,7 +356,7 @@ TW_UINT16 SANE_GetDSStatus (pTW_IDENTITY pOrigin,
 {
     pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
 
-    TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
+    TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET => %u\n", activeDS.twCC);
     pSourceStatus->ConditionCode = activeDS.twCC;
     /* Reset the condition code */
     activeDS.twCC = TWCC_SUCCESS;
@@ -370,7 +371,7 @@ TW_UINT16 SANE_DisableDSUserInterface (pTW_IDENTITY pOrigin,
 
     TRACE ("DG_CONTROL/DAT_USERINTERFACE/MSG_DISABLEDS\n");
 
-    if (activeDS.currentState != 5)
+    if (activeDS.currentState != 5 && activeDS.currentState != 6)
     {
         twRC = TWRC_FAILURE;
         activeDS.twCC = TWCC_SEQERROR;
diff --git a/dlls/sane.ds/sane_main.c b/dlls/sane.ds/sane_main.c
index 2f31c89cd09c..54ebf7a633e4 100644
--- a/dlls/sane.ds/sane_main.c
+++ b/dlls/sane.ds/sane_main.c
@@ -387,6 +387,7 @@ DS_Entry ( pTW_IDENTITY pOrigin,
 
 void SANE_Notify (TW_UINT16 message)
 {
+    TRACE("%04x\n", message);
     SANE_dsmentry (&activeDS.identity, &activeDS.appIdentity, DG_CONTROL, DAT_NULL, message, NULL);
 }
 
diff --git a/dlls/scrobj/tests/scrobj.c b/dlls/scrobj/tests/scrobj.c
index 64b07832227d..21d8f0b704cd 100644
--- a/dlls/scrobj/tests/scrobj.c
+++ b/dlls/scrobj/tests/scrobj.c
@@ -95,7 +95,6 @@ DEFINE_EXPECT(InitNew);
 DEFINE_EXPECT(Close);
 DEFINE_EXPECT(SetScriptSite);
 DEFINE_EXPECT(QI_IActiveScriptParse);
-DEFINE_EXPECT(SetScriptState_INITIALIZED);
 DEFINE_EXPECT(SetScriptState_UNINITIALIZED);
 DEFINE_EXPECT(SetScriptState_STARTED);
 DEFINE_EXPECT(AddNamedItem_scriptlet);
@@ -445,9 +444,6 @@ static HRESULT WINAPI ActiveScript_SetScriptState(IActiveScript *iface, SCRIPTST
 {
     switch(ss)
     {
-    case SCRIPTSTATE_INITIALIZED:
-        CHECK_EXPECT(SetScriptState_INITIALIZED);
-        break;
     case SCRIPTSTATE_UNINITIALIZED:
         CHECK_EXPECT(SetScriptState_UNINITIALIZED);
         break;
diff --git a/dlls/scrrun/dictionary.c b/dlls/scrrun/dictionary.c
index 1b6188653605..c35ca6a44b81 100644
--- a/dlls/scrrun/dictionary.c
+++ b/dlls/scrrun/dictionary.c
@@ -119,6 +119,7 @@ static inline BOOL is_numeric_key(const VARIANT *key)
         case VT_DATE:
         case VT_R4:
         case VT_R8:
+        case VT_BOOL:
             return TRUE;
         default:
             return FALSE;
@@ -162,9 +163,38 @@ static inline BOOL numeric_key_eq(const VARIANT *key1, const VARIANT *key2)
     return V_R4(&v1) == V_R4(&v2);
 }
 
+/* Empty matches the zero/default value of the other key's type: any numeric
+ * zero, the empty string, or another Empty. */
+static BOOL empty_matches_key(const VARIANT *key)
+{
+    VARIANT v;
+
+    switch (V_VT(key))
+    {
+        case VT_EMPTY:
+            return TRUE;
+        case VT_BSTR:
+        {
+            const WCHAR *str = get_key_strptr(key);
+            return !str || !*str;
+        }
+        default:
+            if (!is_numeric_key(key))
+                return FALSE;
+            VariantInit(&v);
+            if (FAILED(VariantChangeType(&v, key, 0, VT_R4)))
+                return FALSE;
+            return V_R4(&v) == 0.0f;
+    }
+}
+
 static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_pair *pair, const VARIANT *key, DWORD hash)
 {
-    if (is_string_key(key) != is_string_key(&pair->key))
+    if (V_VT(key) == VT_EMPTY || V_VT(&pair->key) == VT_EMPTY)
+    {
+        return empty_matches_key(V_VT(key) == VT_EMPTY ? &pair->key : key);
+    }
+    else if (is_string_key(key) != is_string_key(&pair->key))
     {
         return FALSE;
     }
@@ -188,9 +218,9 @@ static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_
     {
         return hash == pair->hash && numeric_key_eq(key, &pair->key);
     }
-    else if (V_VT(&pair->key) == VT_EMPTY || V_VT(&pair->key) == VT_NULL)
+    else if (V_VT(&pair->key) == VT_NULL)
     {
-        return V_VT(&pair->key) == V_VT(key);
+        return V_VT(key) == VT_NULL;
     }
     else
     {
@@ -241,10 +271,19 @@ static HRESULT get_keyitem_pair(struct dictionary *dict, VARIANT *key, struct ke
     return S_OK;
 }
 
+static void link_keyitem_to_bucket(struct dictionary *dict, struct keyitem_pair *pair)
+{
+    struct list *head = get_bucket_head(dict, pair->hash);
+
+    if (!head->next)
+        /* this only happens once per bucket */
+        list_init(head);
+    list_add_tail(head, &pair->bucket);
+}
+
 static HRESULT add_keyitem_pair(struct dictionary *dict, VARIANT *key, VARIANT *item)
 {
     struct keyitem_pair *pair;
-    struct list *head;
     VARIANT hash;
     HRESULT hr;
 
@@ -267,13 +306,8 @@ static HRESULT add_keyitem_pair(struct dictionary *dict, VARIANT *key, VARIANT *
     if (FAILED(hr))
         goto failed;
 
-    head = get_bucket_head(dict, pair->hash);
-    if (!head->next)
-        /* this only happens once per bucket */
-        list_init(head);
-
     /* link to bucket list and to full list */
-    list_add_tail(head, &pair->bucket);
+    link_keyitem_to_bucket(dict, pair);
     list_add_tail(&dict->pairs, &pair->entry);
     dict->count++;
     return S_OK;
@@ -705,29 +739,41 @@ static HRESULT WINAPI dictionary_Items(IDictionary *iface, VARIANT *items)
 static HRESULT WINAPI dictionary_put_Key(IDictionary *iface, VARIANT *key, VARIANT *newkey)
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
-    struct keyitem_pair *pair;
-    VARIANT empty;
+    struct keyitem_pair *pair, *existing;
+    VARIANT new_key, hash;
     HRESULT hr;
 
     TRACE("%p, %s, %s.\n", iface, debugstr_variant(key), debugstr_variant(newkey));
 
     if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
         return hr;
+    if (!pair)
+        return CTL_E_ELEMENT_NOT_FOUND;
 
-    if (pair)
-    {
-        /* found existing pair for a key, add new pair with new key
-           and old item and remove old pair after that */
+    /* The key is renamed in place, preserving its value and enumeration
+       position; only collisions with a different pair are rejected. */
+    if (FAILED(hr = get_keyitem_pair(dictionary, newkey, &existing)))
+        return hr;
+    if (existing && existing != pair)
+        return CTL_E_KEY_ALREADY_EXISTS;
+
+    hr = IDictionary_get_HashVal(iface, newkey, &hash);
+    if (FAILED(hr))
+        return hr;
 
-        hr = IDictionary_Add(iface, newkey, &pair->item);
-        if (FAILED(hr))
-            return hr;
+    VariantInit(&new_key);
+    hr = VariantCopyInd(&new_key, newkey);
+    if (FAILED(hr))
+        return hr;
 
-        return IDictionary_Remove(iface, key);
-    }
+    VariantClear(&pair->key);
+    pair->key = new_key;
+    pair->hash = V_I4(&hash);
+
+    list_remove(&pair->bucket);
+    link_keyitem_to_bucket(dictionary, pair);
 
-    VariantInit(&empty);
-    return IDictionary_Add(iface, newkey, &empty);
+    return S_OK;
 }
 
 static HRESULT WINAPI dictionary_Keys(IDictionary *iface, VARIANT *keys)
@@ -950,6 +996,10 @@ static HRESULT WINAPI dictionary_get_HashVal(IDictionary *iface, VARIANT *key, V
     case VT_R8|VT_BYREF:
     case VT_R8:
         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R8REF(key) : V_R8(key), &V_I4(hash));
+    case VT_BOOL|VT_BYREF:
+    case VT_BOOL:
+        V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_BOOLREF(key) : V_BOOL(key));
+        break;
     case VT_EMPTY:
     case VT_NULL:
         V_I4(hash) = 0;
diff --git a/dlls/scrrun/tests/dictionary.c b/dlls/scrrun/tests/dictionary.c
index 8fc369e70623..ea0b2dd3622e 100644
--- a/dlls/scrrun/tests/dictionary.c
+++ b/dlls/scrrun/tests/dictionary.c
@@ -828,6 +828,22 @@ if (0) { /* crashes on native */
     ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash));
     ok(V_I4(&hash) == expected, "got hash %#lx, expected %#lx\n", V_I4(&hash), expected);
 
+    V_VT(&key) = VT_BOOL;
+    V_BOOL(&key) = VARIANT_FALSE;
+    V_I4(&hash) = 5678;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(V_VT(&hash) == VT_I4, "Unexpected hash type %d.\n", V_VT(&hash));
+    ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash value %#lx.\n", V_I4(&hash));
+
+    V_VT(&key) = VT_BOOL;
+    V_BOOL(&key) = VARIANT_TRUE;
+    V_I4(&hash) = 5678;
+    hr = IDictionary_get_HashVal(dict, &key, &hash);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(V_VT(&hash) == VT_I4, "Unexpected hash type %d.\n", V_VT(&hash));
+    ok(V_I4(&hash) == get_num_hash((FLOAT)VARIANT_TRUE), "Unexpected hash value %#lx.\n", V_I4(&hash));
+
     V_VT(&key) = VT_EMPTY;
     V_I4(&key) = 1234;
     V_I4(&hash) = 5678;
@@ -1134,6 +1150,306 @@ static void test_Add(void)
     IDictionary_Release(dict);
 }
 
+static void add_str_item(IDictionary *dict, const WCHAR *name, int value)
+{
+    VARIANT key, item;
+    HRESULT hr;
+
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = SysAllocString(name);
+    V_VT(&item) = VT_I2;
+    V_I2(&item) = value;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "Add %s: %#lx.\n", wine_dbgstr_w(name), hr);
+    VariantClear(&key);
+}
+
+static void check_key_at(IDictionary *dict, LONG index, const WCHAR *expect, BOOL todo)
+{
+    VARIANT keys, key;
+    HRESULT hr;
+
+    VariantInit(&keys);
+    hr = IDictionary_Keys(dict, &keys);
+    ok(hr == S_OK, "Keys: %#lx.\n", hr);
+
+    VariantInit(&key);
+    hr = SafeArrayGetElement(V_ARRAY(&keys), &index, &key);
+    ok(hr == S_OK, "GetElement %ld: %#lx.\n", index, hr);
+    todo_wine_if(todo) ok(V_VT(&key) == VT_BSTR && !lstrcmpW(V_BSTR(&key), expect),
+            "key at %ld: got %s, expected %s.\n", index, wine_dbgstr_variant(&key), wine_dbgstr_w(expect));
+    VariantClear(&key);
+    VariantClear(&keys);
+}
+
+static BOOL exists_str(IDictionary *dict, const WCHAR *name)
+{
+    VARIANT_BOOL exists = VARIANT_FALSE;
+    VARIANT key;
+    HRESULT hr;
+
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = SysAllocString(name);
+    hr = IDictionary_Exists(dict, &key, &exists);
+    ok(hr == S_OK, "Exists %s: %#lx.\n", wine_dbgstr_w(name), hr);
+    VariantClear(&key);
+    return exists == VARIANT_TRUE;
+}
+
+static void test_put_Key(void)
+{
+    VARIANT key, newkey, item;
+    IDictionary *dict;
+    BOOL has_y;
+    LONG count;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    add_str_item(dict, L"a", 1);
+    add_str_item(dict, L"b", 2);
+    add_str_item(dict, L"c", 3);
+
+    /* Renaming an existing key keeps its value and enumeration position. */
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = SysAllocString(L"b");
+    V_VT(&newkey) = VT_BSTR;
+    V_BSTR(&newkey) = SysAllocString(L"x");
+    hr = IDictionary_put_Key(dict, &key, &newkey);
+    ok(hr == S_OK, "put_Key: %#lx.\n", hr);
+    VariantClear(&key);
+    VariantClear(&newkey);
+
+    ok(!exists_str(dict, L"b"), "old key 'b' should be gone\n");
+    ok(exists_str(dict, L"x"), "new key 'x' should exist\n");
+
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = SysAllocString(L"x");
+    VariantInit(&item);
+    hr = IDictionary_get_Item(dict, &key, &item);
+    ok(hr == S_OK, "get_Item: %#lx.\n", hr);
+    ok(V_VT(&item) == VT_I2 && V_I2(&item) == 2, "renamed item: %s.\n", wine_dbgstr_variant(&item));
+    VariantClear(&key);
+    VariantClear(&item);
+
+    hr = IDictionary_get_Count(dict, &count);
+    ok(hr == S_OK, "get_Count: %#lx.\n", hr);
+    ok(count == 3, "Unexpected count %ld.\n", count);
+
+    check_key_at(dict, 0, L"a", FALSE);
+    check_key_at(dict, 1, L"x", FALSE);
+    check_key_at(dict, 2, L"c", FALSE);
+
+    /* Renaming a key that does not exist fails and changes nothing. */
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = SysAllocString(L"zzz");
+    V_VT(&newkey) = VT_BSTR;
+    V_BSTR(&newkey) = SysAllocString(L"y");
+    hr = IDictionary_put_Key(dict, &key, &newkey);
+    ok(hr == CTL_E_ELEMENT_NOT_FOUND, "put_Key of missing key: %#lx.\n", hr);
+    VariantClear(&key);
+    VariantClear(&newkey);
+
+    hr = IDictionary_get_Count(dict, &count);
+    ok(hr == S_OK, "get_Count: %#lx.\n", hr);
+    ok(count == 3, "Unexpected count %ld.\n", count);
+    has_y = exists_str(dict, L"y");
+    ok(!has_y, "missing-key rename should not create 'y'\n");
+
+    IDictionary_Release(dict);
+
+    /* Renaming onto an existing key fails and leaves the dictionary intact. */
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    add_str_item(dict, L"a", 1);
+    add_str_item(dict, L"b", 2);
+
+    V_VT(&key) = VT_BSTR;
+    V_BSTR(&key) = SysAllocString(L"b");
+    V_VT(&newkey) = VT_BSTR;
+    V_BSTR(&newkey) = SysAllocString(L"a");
+    hr = IDictionary_put_Key(dict, &key, &newkey);
+    ok(hr == CTL_E_KEY_ALREADY_EXISTS, "put_Key onto existing key: %#lx.\n", hr);
+    VariantClear(&key);
+    VariantClear(&newkey);
+
+    ok(exists_str(dict, L"b"), "key 'b' should be intact after failed rename\n");
+    hr = IDictionary_get_Count(dict, &count);
+    ok(hr == S_OK, "get_Count: %#lx.\n", hr);
+    ok(count == 2, "Unexpected count %ld.\n", count);
+
+    IDictionary_Release(dict);
+}
+
+/* Add two keys to a fresh dictionary; returns TRUE if the second key is
+ * treated as a duplicate of the first. */
+static BOOL keys_match(VARIANT *key1, VARIANT *key2)
+{
+    IDictionary *dict;
+    VARIANT item;
+    HRESULT hr;
+
+    if (FAILED(CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict)))
+        return FALSE;
+
+    V_VT(&item) = VT_I2;
+    V_I2(&item) = 1;
+    if (FAILED(IDictionary_Add(dict, key1, &item)))
+    {
+        IDictionary_Release(dict);
+        return FALSE;
+    }
+
+    hr = IDictionary_Add(dict, key2, &item);
+    IDictionary_Release(dict);
+    return hr == CTL_E_KEY_ALREADY_EXISTS;
+}
+
+static void test_empty_key(void)
+{
+    static const VARTYPE zero_keys[] = {
+        VT_I2, VT_I4, VT_UI1, VT_R4, VT_R8, VT_DATE,
+    };
+    VARIANT_BOOL exists;
+    IDictionary *dict;
+    VARIANT a, b, item;
+    HRESULT hr;
+    unsigned i;
+
+    /* Empty matches the zero/default value of any numeric type. */
+    for (i = 0; i < ARRAY_SIZE(zero_keys); i++)
+    {
+        V_VT(&a) = VT_EMPTY;
+        V_VT(&b) = zero_keys[i];
+        if (zero_keys[i] == VT_R8 || zero_keys[i] == VT_DATE)
+            V_R8(&b) = 0.0;
+        else if (zero_keys[i] == VT_R4)
+            V_R4(&b) = 0.0f;
+        else
+            V_I4(&b) = 0;
+        ok(keys_match(&a, &b), "Empty should match zero key vt %d\n", zero_keys[i]);
+    }
+
+    /* Empty matches the empty string, but not a non-empty one. */
+    V_VT(&a) = VT_EMPTY;
+    V_VT(&b) = VT_BSTR;
+    V_BSTR(&b) = SysAllocString(L"");
+    ok(keys_match(&a, &b), "Empty should match empty string\n");
+    VariantClear(&b);
+
+    V_VT(&b) = VT_BSTR;
+    V_BSTR(&b) = SysAllocString(L"0");
+    ok(!keys_match(&a, &b), "Empty should not match \"0\"\n");
+    VariantClear(&b);
+
+    /* Empty does not match a non-zero number or Null. */
+    V_VT(&b) = VT_I2;
+    V_I2(&b) = 5;
+    ok(!keys_match(&a, &b), "Empty should not match I2 5\n");
+
+    V_VT(&b) = VT_NULL;
+    ok(!keys_match(&a, &b), "Empty should not match Null\n");
+
+    /* A numeric zero does not match the empty string (only Empty bridges). */
+    V_VT(&a) = VT_I2;
+    V_I2(&a) = 0;
+    V_VT(&b) = VT_BSTR;
+    V_BSTR(&b) = SysAllocString(L"");
+    ok(!keys_match(&a, &b), "I2 0 should not match empty string\n");
+    VariantClear(&b);
+
+    /* Null matches only Null. */
+    V_VT(&a) = VT_NULL;
+    V_VT(&b) = VT_NULL;
+    ok(keys_match(&a, &b), "Null should match Null\n");
+
+    /* Exists and Remove accept an Empty key, matching a zero-valued entry. */
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    V_VT(&a) = VT_I2;
+    V_I2(&a) = 0;
+    VariantInit(&item);
+    hr = IDictionary_Add(dict, &a, &item);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    V_VT(&b) = VT_EMPTY;
+    exists = VARIANT_FALSE;
+    hr = IDictionary_Exists(dict, &b, &exists);
+    ok(hr == S_OK, "Exists with Empty key: %#lx.\n", hr);
+    ok(exists == VARIANT_TRUE, "Empty should find I2 0, got %x\n", exists);
+
+    hr = IDictionary_Remove(dict, &b);
+    ok(hr == S_OK, "Remove with Empty key: %#lx.\n", hr);
+
+    IDictionary_Release(dict);
+}
+
+static void test_bool_key(void)
+{
+    VARIANT_BOOL exists;
+    IDictionary *dict;
+    VARIANT a, b, item;
+    HRESULT hr;
+
+    /* Boolean keys match the equal numeric value across types: True is -1,
+     * False is 0. */
+    V_VT(&a) = VT_BOOL;
+    V_BOOL(&a) = VARIANT_TRUE;
+    V_VT(&b) = VT_I2;
+    V_I2(&b) = -1;
+    ok(keys_match(&a, &b), "True should match I2 -1\n");
+
+    V_VT(&a) = VT_BOOL;
+    V_BOOL(&a) = VARIANT_FALSE;
+    V_VT(&b) = VT_I2;
+    V_I2(&b) = 0;
+    ok(keys_match(&a, &b), "False should match I2 0\n");
+
+    /* False is the zero/default value, so it matches an Empty key. */
+    V_VT(&a) = VT_BOOL;
+    V_BOOL(&a) = VARIANT_FALSE;
+    V_VT(&b) = VT_EMPTY;
+    ok(keys_match(&a, &b), "False should match Empty\n");
+
+    /* True and False are distinct keys. */
+    V_VT(&a) = VT_BOOL;
+    V_BOOL(&a) = VARIANT_TRUE;
+    V_VT(&b) = VT_BOOL;
+    V_BOOL(&b) = VARIANT_FALSE;
+    ok(!keys_match(&a, &b), "True should not match False\n");
+
+    /* A script can use a comparison result (a Boolean) as the key to Exists
+     * and Remove. */
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    V_VT(&a) = VT_I2;
+    V_I2(&a) = -1;
+    VariantInit(&item);
+    hr = IDictionary_Add(dict, &a, &item);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    V_VT(&b) = VT_BOOL;
+    V_BOOL(&b) = VARIANT_TRUE;
+    exists = VARIANT_FALSE;
+    hr = IDictionary_Exists(dict, &b, &exists);
+    ok(hr == S_OK, "Exists with Boolean key: %#lx.\n", hr);
+    ok(exists == VARIANT_TRUE, "True should find I2 -1, got %x\n", exists);
+
+    hr = IDictionary_Remove(dict, &b);
+    ok(hr == S_OK, "Remove with Boolean key: %#lx.\n", hr);
+
+    IDictionary_Release(dict);
+}
+
 static void test_IEnumVARIANT(void)
 {
     IUnknown *enum1, *enum2;
@@ -1315,6 +1631,9 @@ START_TEST(dictionary)
     test_Item();
     test_Add();
     test_object_key_hashfail();
+    test_put_Key();
+    test_empty_key();
+    test_bool_key();
     test_IEnumVARIANT();
     test_putref_Item();
 
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c
index 19eda9e00261..1e3bbb9f31ce 100644
--- a/dlls/secur32/schannel.c
+++ b/dlls/secur32/schannel.c
@@ -841,6 +841,10 @@ static void dump_buffer_desc(SecBufferDesc *desc)
 
 #define HEADER_SIZE_TLS  5
 #define HEADER_SIZE_DTLS 13
+#define TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC 20
+#define TLS_CONTENT_TYPE_APPLICATION_DATA   23
+#define TLS_RECORD_TYPE_OFFSET          0
+#define TLS_RECORD_VERSION_MAJOR_OFFSET 1
 
 static inline SIZE_T read_record_size(const BYTE *buf, SIZE_T header_size)
 {
@@ -852,6 +856,13 @@ static inline BOOL is_dtls_context(const struct schan_context *ctx)
     return ctx->header_size == HEADER_SIZE_DTLS;
 }
 
+static inline BOOL is_tls_record_header(BYTE *ptr)
+{
+    return ptr[TLS_RECORD_TYPE_OFFSET] >= TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+           ptr[TLS_RECORD_TYPE_OFFSET] <= TLS_CONTENT_TYPE_APPLICATION_DATA &&
+           ptr[TLS_RECORD_VERSION_MAJOR_OFFSET] == 3;
+}
+
 static void fill_missing_sec_buffer(SecBufferDesc *input, DWORD size)
 {
     int idx = schan_find_sec_buffer_idx(input, 0, SECBUFFER_EMPTY);
@@ -1017,6 +1028,14 @@ static SECURITY_STATUS establish_context(
                 return SEC_E_INCOMPLETE_MESSAGE;
             }
 
+            if (!is_dtls_context(ctx) && !is_tls_record_header(ptr))
+            {
+                WARN("Invalid TLS record header: %02x %02x %02x %02x %02x.\n",
+                     ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
+                pOutput->pBuffers[idx].cbBuffer = 0;
+                return SEC_E_INVALID_TOKEN;
+            }
+
             while (buffer->cbBuffer >= expected_size + ctx->header_size)
             {
                 record_size = ctx->header_size + read_record_size(ptr, ctx->header_size);
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c
index 3c4f938f556a..b5736a2ce397 100644
--- a/dlls/secur32/tests/schannel.c
+++ b/dlls/secur32/tests/schannel.c
@@ -1040,9 +1040,7 @@ static void test_communication(void)
     status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost",
             ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM,
             0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL);
-    todo_wine
     ok(status == SEC_E_INVALID_TOKEN, "Expected SEC_E_INVALID_TOKEN, got %08lx\n", status);
-    todo_wine
     ok(buffers[0].pBuffers[0].cbBuffer == 0, "Output buffer size was not set to 0.\n");
 
     buffers[1].cBuffers = 1;
diff --git a/dlls/shcore/Makefile.in b/dlls/shcore/Makefile.in
index fd07a9e1b514..ab438e8c1910 100644
--- a/dlls/shcore/Makefile.in
+++ b/dlls/shcore/Makefile.in
@@ -3,7 +3,5 @@ IMPORTLIB = shcore
 IMPORTS   = advapi32
 DELAYIMPORTS = user32 ole32
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/shcore/tests/shcore.c b/dlls/shcore/tests/shcore.c
index bae7c332076d..acdeedd27371 100644
--- a/dlls/shcore/tests/shcore.c
+++ b/dlls/shcore/tests/shcore.c
@@ -60,7 +60,6 @@ static const char * test_envvar1 = "bar";
 static const char * test_envvar2 = "ImARatherLongButIndeedNeededString";
 static char test_exp_path1[MAX_PATH];
 static char test_exp_path2[MAX_PATH];
-static DWORD exp_len1;
 static DWORD exp_len2;
 static const char * initial_buffer ="0123456789";
 
@@ -378,7 +377,6 @@ static HKEY create_test_entries(void)
 {
     HKEY hKey;
     DWORD ret;
-    DWORD nExpectedLen1, nExpectedLen2;
 
     SetEnvironmentVariableA("LONGSYSTEMVAR", test_envvar1);
     SetEnvironmentVariableA("FOO", test_envvar2);
@@ -393,15 +391,10 @@ static HKEY create_test_entries(void)
         ok(!RegSetValueExA(hKey, "Test3", 0, REG_EXPAND_SZ, (BYTE *)test_path2, strlen(test_path2)+1), "RegSetValueExA failed\n");
     }
 
-    exp_len1 = ExpandEnvironmentStringsA(test_path1, test_exp_path1, sizeof(test_exp_path1));
-    exp_len2 = ExpandEnvironmentStringsA(test_path2, test_exp_path2, sizeof(test_exp_path2));
+    ExpandEnvironmentStringsA(test_path1, test_exp_path1, sizeof(test_exp_path1));
+    ExpandEnvironmentStringsA(test_path2, test_exp_path2, sizeof(test_exp_path2));
 
-    nExpectedLen1 = strlen(test_path1) - strlen("%LONGSYSTEMVAR%") + strlen(test_envvar1) + 1;
-    nExpectedLen2 = strlen(test_path2) - strlen("%FOO%") + strlen(test_envvar2) + 1;
-
-    /* Make sure we carry on with correct values */
-    exp_len1 = nExpectedLen1;
-    exp_len2 = nExpectedLen2;
+    exp_len2 = strlen(test_path2) - strlen("%FOO%") + strlen(test_envvar2) + 1;
 
     return hKey;
 }
diff --git a/dlls/srclient/Makefile.in b/dlls/srclient/Makefile.in
index 1d2391bf5828..d4119f729eda 100644
--- a/dlls/srclient/Makefile.in
+++ b/dlls/srclient/Makefile.in
@@ -1,3 +1 @@
 MODULE    = srclient.dll
-
-EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/ucrtbase/tests/environ.c b/dlls/ucrtbase/tests/environ.c
index bddf1f78f712..34575fc7fd57 100644
--- a/dlls/ucrtbase/tests/environ.c
+++ b/dlls/ucrtbase/tests/environ.c
@@ -71,17 +71,6 @@ static const char *a_very_long_env_string =
  "/usr/lib/mingw32/3.4.2/;"
  "/usr/lib/";
 
-static char ***(__cdecl *p__p__environ)(void);
-static WCHAR ***(__cdecl *p__p__wenviron)(void);
-static char** (__cdecl *p_get_initial_narrow_environment)(void);
-static wchar_t** (__cdecl *p_get_initial_wide_environment)(void);
-static errno_t (__cdecl *p_putenv_s)(const char*, const char*);
-static errno_t (__cdecl *p_wputenv_s)(const wchar_t*, const wchar_t*);
-static errno_t (__cdecl *p_getenv_s)(size_t*, char*, size_t, const char*);
-
-static char ***p_environ;
-static WCHAR ***p_wenviron;
-
 static void __cdecl test_invalid_parameter_handler(const wchar_t *expression,
                                                    const wchar_t *function, const wchar_t *file,
                                                    unsigned line, uintptr_t arg)
@@ -94,30 +83,6 @@ static void __cdecl test_invalid_parameter_handler(const wchar_t *expression,
     ok(arg == 0, "arg = %Ix\n", arg);
 }
 
-static BOOL init(void)
-{
-    HMODULE hmod = GetModuleHandleA( "ucrtbase.dll" );
-
-    p__p__environ = (void *)GetProcAddress( hmod, "__p__environ" );
-    p__p__wenviron = (void *)GetProcAddress( hmod, "__p__wenviron" );
-    p_get_initial_narrow_environment = (void *)GetProcAddress( hmod, "_get_initial_narrow_environment" );
-    p_get_initial_wide_environment = (void *)GetProcAddress( hmod, "_get_initial_wide_environment" );
-    p_putenv_s = (void *)GetProcAddress( hmod, "_putenv_s" );
-    p_wputenv_s = (void *)GetProcAddress( hmod, "_wputenv_s" );
-    p_getenv_s = (void *)GetProcAddress( hmod, "getenv_s" );
-
-    ok(p__p__environ != NULL, "Unexecped NULL pointer to environ\n" );
-    ok(p__p__wenviron != NULL, "Unexecped NULL pointer to environ\n" );
-    if (!p__p__environ || !p__p__wenviron)
-    {
-        skip( "NULL pointers for environment\n" );
-        return FALSE;
-    }
-    p_environ = p__p__environ();
-    p_wenviron = p__p__wenviron();
-    return TRUE;
-}
-
 static unsigned env_get_entry_countA( char **env )
 {
     unsigned count;
@@ -129,15 +94,13 @@ static unsigned env_get_entry_countA( char **env )
 
 static void test_initial_environ( void )
 {
-    ok( p__p__environ() != NULL, "Unexpected NULL _environ[]\n" );
-    ok( *p__p__environ() != NULL, "Unexpected empty _environ[]\n" );
-    ok( p_get_initial_narrow_environment() != NULL, "Unexpected empty narrow initial environment\n" );
-    ok( p_get_initial_narrow_environment() == *p__p__environ(), "Expecting _environ[] to match initial narrow environment\n" );
-
-    ok( p__p__wenviron() != NULL, "Unexpected NULL _wenviron[]\n" );
-    ok( *p__p__wenviron() == NULL, "Unexpected non empty _wenviron[]\n" );
-    ok( p_get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" );
-    ok( p_get_initial_wide_environment() == *p__p__wenviron(), "Expecting _wenviron[] to match initial wide environment\n" );
+    ok( _environ != NULL, "Unexpected empty _environ[]\n" );
+    ok( _get_initial_narrow_environment() != NULL, "Unexpected empty narrow initial environment\n" );
+    ok( _get_initial_narrow_environment() == _environ, "Expecting _environ[] to match initial narrow environment\n" );
+
+    ok( _wenviron == NULL, "Unexpected non empty _wenviron[]\n" );
+    ok( _get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" );
+    ok( _get_initial_wide_environment() == _wenviron, "Expecting _wenviron[] to match initial wide environment\n" );
 }
 
 static void test_environment_manipulation(void)
@@ -152,13 +115,12 @@ static void test_environment_manipulation(void)
     ok( _putenv( "cat=" ) == 0, "_putenv failed on deletion of nonexistent environment variable\n" );
     ok( _putenv( "cat=dog" ) == 0, "failed setting cat=dog\n" );
     ok( strcmp( getenv( "cat" ), "dog" ) == 0, "getenv did not return 'dog'\n" );
-    if (p_getenv_s)
-    {
-        ret = p_getenv_s( &len, buf, sizeof(buf), "cat" );
-        ok( !ret, "getenv_s returned %d\n", ret );
-        ok( len == 4, "getenv_s returned length is %Id\n", len);
-        ok( !strcmp(buf, "dog"), "getenv_s did not return 'dog'\n" );
-    }
+
+    ret = getenv_s( &len, buf, sizeof(buf), "cat" );
+    ok( !ret, "getenv_s returned %d\n", ret );
+    ok( len == 4, "getenv_s returned length is %Id\n", len);
+    ok( !strcmp(buf, "dog"), "getenv_s did not return 'dog'\n" );
+
     ok( _putenv("cat=") == 0, "failed deleting cat\n" );
 
     ok( _putenv("=") == -1, "should not accept '=' as input\n" );
@@ -167,95 +129,86 @@ static void test_environment_manipulation(void)
 
     ok( getenv("nonexistent") == NULL, "getenv should fail with nonexistent var name\n" );
 
-    if (p_putenv_s)
-    {
-        SET_EXPECT(invalid_parameter_handler);
-        ret = p_putenv_s( NULL, "dog" );
-        CHECK_CALLED(invalid_parameter_handler);
-        ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
-        SET_EXPECT(invalid_parameter_handler);
-        ret = p_putenv_s( "cat", NULL );
-        CHECK_CALLED(invalid_parameter_handler);
-        ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
-        SET_EXPECT(invalid_parameter_handler);
-        ret = p_putenv_s( "a=b", NULL );
-        CHECK_CALLED(invalid_parameter_handler);
-        ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
-        ret = p_putenv_s( "cat", "a=b" );
-        ok( !ret, "_putenv_s returned %d\n", ret );
-        ret = p_putenv_s( "cat", "" );
-        ok( !ret, "_putenv_s returned %d\n", ret );
-    }
-
-    if (p_wputenv_s)
-    {
-        SET_EXPECT(invalid_parameter_handler);
-        ret = p_wputenv_s( NULL, L"dog" );
-        CHECK_CALLED(invalid_parameter_handler);
-        ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
-        SET_EXPECT(invalid_parameter_handler);
-        ret = p_wputenv_s( L"cat", NULL );
-        CHECK_CALLED(invalid_parameter_handler);
-        ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
-        SET_EXPECT(invalid_parameter_handler);
-        ret = p_wputenv_s( L"a=b", NULL );
-        CHECK_CALLED(invalid_parameter_handler);
-        ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
-        ret = p_wputenv_s( L"cat", L"a=b" );
-        ok( !ret, "_wputenv_s returned %d\n", ret );
-        ret = p_wputenv_s( L"cat", L"" );
-        ok( !ret, "_wputenv_s returned %d\n", ret );
-    }
-
-    if (p_getenv_s)
-    {
-        buf[0] = 'x';
-        len = 1;
-        errno = 0xdeadbeef;
-        ret = p_getenv_s( &len, buf, sizeof(buf), "nonexistent" );
-        ok( !ret, "_getenv_s returned %d\n", ret );
-        ok( !len, "getenv_s returned length is %Id\n", len );
-        ok( !buf[0], "buf = %s\n", buf );
-        ok( errno == 0xdeadbeef, "errno = %d\n", errno );
-
-        buf[0] = 'x';
-        len = 1;
-        errno = 0xdeadbeef;
-        ret = p_getenv_s( &len, buf, sizeof(buf), NULL );
-        ok( !ret, "_getenv_s returned %d\n", ret );
-        ok( !len, "getenv_s returned length is %Id\n", len );
-        ok( !buf[0], "buf = %s\n", buf );
-        ok( errno == 0xdeadbeef, "errno = %d\n", errno );
-    }
+    SET_EXPECT(invalid_parameter_handler);
+    ret = _putenv_s( NULL, "dog" );
+    CHECK_CALLED(invalid_parameter_handler);
+    ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
+    SET_EXPECT(invalid_parameter_handler);
+    ret = _putenv_s( "cat", NULL );
+    CHECK_CALLED(invalid_parameter_handler);
+    ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
+    SET_EXPECT(invalid_parameter_handler);
+    ret = _putenv_s( "a=b", NULL );
+    CHECK_CALLED(invalid_parameter_handler);
+    ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
+    ret = _putenv_s( "cat", "a=b" );
+    ok( !ret, "_putenv_s returned %d\n", ret );
+    ret = _putenv_s( "cat", "" );
+    ok( !ret, "_putenv_s returned %d\n", ret );
+
+    SET_EXPECT(invalid_parameter_handler);
+    ret = _wputenv_s( NULL, L"dog" );
+    CHECK_CALLED(invalid_parameter_handler);
+    ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
+    SET_EXPECT(invalid_parameter_handler);
+    ret = _wputenv_s( L"cat", NULL );
+    CHECK_CALLED(invalid_parameter_handler);
+    ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
+    SET_EXPECT(invalid_parameter_handler);
+    ret = _wputenv_s( L"a=b", NULL );
+    CHECK_CALLED(invalid_parameter_handler);
+    ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
+    ret = _wputenv_s( L"cat", L"a=b" );
+    ok( !ret, "_wputenv_s returned %d\n", ret );
+    ret = _wputenv_s( L"cat", L"" );
+    ok( !ret, "_wputenv_s returned %d\n", ret );
+
+    buf[0] = 'x';
+    len = 1;
+    errno = 0xdeadbeef;
+    ret = getenv_s( &len, buf, sizeof(buf), "nonexistent" );
+    ok( !ret, "getenv_s returned %d\n", ret );
+    ok( !len, "getenv_s returned length is %Id\n", len );
+    ok( !buf[0], "buf = %s\n", buf );
+    ok( errno == 0xdeadbeef, "errno = %d\n", errno );
+
+    buf[0] = 'x';
+    len = 1;
+    errno = 0xdeadbeef;
+    ret = getenv_s( &len, buf, sizeof(buf), NULL );
+    ok( !ret, "getenv_s returned %d\n", ret );
+    ok( !len, "getenv_s returned length is %Id\n", len );
+    ok( !buf[0], "buf = %s\n", buf );
+    ok( errno == 0xdeadbeef, "errno = %d\n", errno );
 
     /* test stability of _environ[] pointers */
     ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" );
     ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
-    count = env_get_entry_countA( *p_environ );
+    count = env_get_entry_countA( _environ );
     ok( _putenv( "__winetest_cat=mew") == 0, "Couldn't set env var\n" );
-    ok( !strcmp( (*p_environ)[count], "__winetest_cat=mew"), "Unexpected env var value\n" );
-    first = (*p_environ)[count];
-    ok( getenv("__winetest_cat") == strchr( (*p_environ)[count], '=') + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
+    ok( !strcmp( _environ[count], "__winetest_cat=mew"), "Unexpected env var value\n" );
+    first = _environ[count];
+    ok( getenv("__winetest_cat") == strchr( _environ[count], '=') + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
     ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" );
-    ok( !strcmp( (*p_environ)[count + 1], "__winetest_dog=bark" ), "Unexpected env var value\n" );
-    ok( getenv( "__winetest_dog" ) == strchr( (*p_environ)[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
-    ok( first == (*p_environ)[count], "Expected stability of _environ[count] pointer\n" );
-    second = (*p_environ)[count + 1];
-    ok( count + 2 == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
+    ok( !strcmp( _environ[count + 1], "__winetest_dog=bark" ), "Unexpected env var value\n" );
+    ok( getenv( "__winetest_dog" ) == strchr( _environ[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
+    ok( first == _environ[count], "Expected stability of _environ[count] pointer\n" );
+    second = _environ[count + 1];
+    ok( count + 2 == env_get_entry_countA( _environ ), "Unexpected count\n" );
 
     ok( _putenv( "__winetest_cat=purr" ) == 0, "Couldn't set env var\n" );
-    ok( !strcmp( (*p_environ)[count], "__winetest_cat=purr" ), "Unexpected env var value\n" );
-    ok( getenv( "__winetest_cat" ) == strchr( (*p_environ)[count], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
-    ok( second == (*p_environ)[count + 1], "Expected stability of _environ[count] pointer\n" );
-    ok( !strcmp( (*p_environ)[count + 1], "__winetest_dog=bark" ), "Couldn't get env var value\n" );
-    ok( getenv( "__winetest_dog" ) == strchr( (*p_environ)[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
-    ok( count + 2 == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
+    ok( !strcmp( _environ[count], "__winetest_cat=purr" ), "Unexpected env var value\n" );
+    ok( getenv( "__winetest_cat" ) == strchr( _environ[count], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
+    ok( second == _environ[count + 1], "Expected stability of _environ[count] pointer\n" );
+    ok( !strcmp( _environ[count + 1], "__winetest_dog=bark" ), "Couldn't get env var value\n" );
+    ok( getenv( "__winetest_dog" ) == strchr( _environ[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
+    ok( count + 2 == env_get_entry_countA( _environ ), "Unexpected count\n" );
     ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env vat\n" );
-    ok( second == (*p_environ)[count], "Expected _environ[count] to be second\n" );
-    ok( !strcmp( (*p_environ)[count], "__winetest_dog=bark" ), "Unexpected env var value\n" );
-    ok( count + 1 == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
+    ok( second == _environ[count], "Expected _environ[count] to be second\n" );
+    ok( !strcmp( _environ[count], "__winetest_dog=bark" ), "Unexpected env var value\n" );
+    ok( count + 1 == env_get_entry_countA( _environ ), "Unexpected count\n" );
     ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
-    ok( count == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
+    ok( count == env_get_entry_countA( _environ ), "Unexpected count\n" );
 
     /* in putenv, only changed variable is updated (no other reload of kernel info is done) */
     ret = SetEnvironmentVariableA( "__winetest_cat", "meow" );
@@ -266,7 +219,7 @@ static void test_environment_manipulation(void)
     ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
 
     /* test setting unicode bits */
-    count = env_get_entry_countA( *p_environ );
+    count = env_get_entry_countA( _environ );
     ret = WideCharToMultiByte( CP_ACP, 0, L"\u263a", -1, buf, ARRAY_SIZE(buf), 0, 0 );
     ok( ret, "WideCharToMultiByte failed: %lu\n", GetLastError() );
     ok( _wputenv( L"__winetest_cat=\u263a" ) == 0, "Couldn't set env var\n" );
@@ -280,7 +233,7 @@ static void test_environment_manipulation(void)
     ok( _wgetenv( L"__winetest_\u263a" ) && !wcscmp( _wgetenv( L"__winetest_\u263a" ), L"bark"), "Couldn't retrieve env var\n" );
     ok( getenv( buf ) && !strcmp( getenv( buf ), "bark"), "Couldn't retrieve env var %s\n", wine_dbgstr_a(buf) );
     ok( _wputenv( L"__winetest_\u263a=" ) == 0, "Couldn't reset env var\n" );
-    ok( count == env_get_entry_countA( *p_environ ), "Unexpected modification of _environ[]\n" );
+    ok( count == env_get_entry_countA( _environ ), "Unexpected modification of _environ[]\n" );
 }
 
 static void test_child_env(char** argv)
@@ -321,8 +274,6 @@ START_TEST(environ)
     char **argv;
     int argc;
 
-    if (!init()) return;
-
     ok( _set_invalid_parameter_handler( test_invalid_parameter_handler ) == NULL,
        "Invalid parameter handler was already set\n" );
 
diff --git a/dlls/user.exe16/message.c b/dlls/user.exe16/message.c
index 584eba0c3966..a8531f2f9a7e 100644
--- a/dlls/user.exe16/message.c
+++ b/dlls/user.exe16/message.c
@@ -112,9 +112,9 @@ typedef struct
 } WINPROC_THUNK;
 #pragma pack(pop)
 
-#define WINPROC_HANDLE (~0u >> 16)
-#define MAX_WINPROCS32 4096
-#define MAX_WINPROCS16 1024
+#define MAKE_WNDPROC16(index)   ((WNDPROC)(UINT_PTR)(UINT)MAKELONG(index, 0xffff))
+#define MAX_WINPROCS32          4096
+#define MAX_WINPROCS16          1024
 
 static WNDPROC16 winproc16_array[MAX_WINPROCS16];
 static unsigned int winproc16_used;
@@ -140,7 +140,7 @@ static int winproc_to_index( WNDPROC16 handle )
     else
     {
         index = LOWORD(handle);
-        if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return -1;
+        if (handle != (WNDPROC16)MAKE_WNDPROC16(index)) return -1;
         /* check array limits */
         if (index >= winproc16_used + MAX_WINPROCS32) return -1;
     }
@@ -191,8 +191,7 @@ WNDPROC WINPROC_AllocProc16( WNDPROC16 func )
     if (!func) return NULL;
 
     /* check if the function is already a win proc */
-    if ((index = winproc_to_index( func )) != -1)
-        return (WNDPROC)(ULONG_PTR)(index | (WINPROC_HANDLE << 16));
+    if ((index = winproc_to_index( func )) != -1) return MAKE_WNDPROC16(index);
 
     /* then check if we already have a winproc for that function */
     for (index = 0; index < winproc16_used; index++)
@@ -206,7 +205,7 @@ WNDPROC WINPROC_AllocProc16( WNDPROC16 func )
     winproc16_array[winproc16_used++] = func;
 
 done:
-    ret = (WNDPROC)(ULONG_PTR)((index + MAX_WINPROCS32) | (WINPROC_HANDLE << 16));
+    ret = MAKE_WNDPROC16(index + MAX_WINPROCS32);
     TRACE( "returning %p for %p/16-bit (%d/%d used)\n",
            ret, func, winproc16_used, MAX_WINPROCS16 );
     return ret;
@@ -221,7 +220,7 @@ WNDPROC16 WINPROC_GetProc16( WNDPROC proc, BOOL unicode )
 {
     WNDPROC winproc = wow_handlers32.alloc_winproc( proc, unicode );
 
-    if ((ULONG_PTR)winproc >> 16 != WINPROC_HANDLE) return (WNDPROC16)winproc;
+    if (winproc != MAKE_WNDPROC16(LOWORD(winproc))) return (WNDPROC16)winproc;
     return alloc_win16_thunk( winproc );
 }
 
diff --git a/dlls/user32/defdlg.c b/dlls/user32/defdlg.c
index 1b50893c041a..69c573c61c09 100644
--- a/dlls/user32/defdlg.c
+++ b/dlls/user32/defdlg.c
@@ -316,6 +316,8 @@ static LRESULT USER_DefDlgProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar
     DIALOGINFO *dlgInfo;
     LRESULT result;
 
+    NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_DIALOG) );
+
     /* Perform DIALOGINFO initialization if not done */
     if(!(dlgInfo = DIALOG_get_info( hwnd, TRUE ))) return 0;
 
@@ -368,6 +370,8 @@ static LRESULT USER_DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar
     DIALOGINFO *dlgInfo;
     LRESULT result;
 
+    NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_DIALOG) );
+
     /* Perform DIALOGINFO initialization if not done */
     if(!(dlgInfo = DIALOG_get_info( hwnd, TRUE ))) return 0;
 
diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c
index d19990fbdb08..7e6a6054b18d 100644
--- a/dlls/user32/mdi.c
+++ b/dlls/user32/mdi.c
@@ -993,6 +993,8 @@ LRESULT MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM
 
     TRACE("%p %04x (%s) %08Ix %08Ix\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
 
+    NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_MDICLIENT) );
+
     if (!(ci = get_client_info( hwnd )))
     {
         if (message == WM_NCCREATE)
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index 00031f457303..318675428d3f 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -198,6 +198,7 @@ LRESULT WINAPI PopupMenuWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM
     case WM_ERASEBKGND:
     case WM_SHOWWINDOW:
     case MN_GETHMENU:
+    case WM_NCCREATE:
         return NtUserMessageCall( hwnd, message, wParam, lParam,
                                   NULL, NtUserPopupMenuWndProc, TRUE );
 
@@ -223,6 +224,7 @@ LRESULT WINAPI PopupMenuWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM
     case WM_ERASEBKGND:
     case WM_SHOWWINDOW:
     case MN_GETHMENU:
+    case WM_NCCREATE:
         return NtUserMessageCall( hwnd, message, wParam, lParam,
                                   NULL, NtUserPopupMenuWndProc, FALSE );
 
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c
index c7a99cc80d43..aa9a8e5003ef 100644
--- a/dlls/user32/tests/class.c
+++ b/dlls/user32/tests/class.c
@@ -2524,12 +2524,12 @@ static const struct real_class_test class_tests[] =
     { "ComboBox",          "ComboBox",         FALSE, TRUE,  TRUE,  TRUE,  FALSE },
     { "Edit",              "Edit",             FALSE, FALSE, TRUE,  TRUE,  FALSE },
     { "ListBox",           "ListBox",          FALSE, TRUE,  TRUE,  TRUE,  FALSE },
-    { "ScrollBar",         "ScrollBar",        FALSE, TRUE,  FALSE, TRUE,  TRUE },
+    { "ScrollBar",         "ScrollBar",        FALSE, TRUE,  FALSE, TRUE,  FALSE },
     { "Static",            "Static",           TRUE,  TRUE,  TRUE,  TRUE,  FALSE },
     { "ComboLBox",         "ListBox",          FALSE, TRUE,  TRUE,  TRUE,  FALSE },
-    { "MDIClient",         "MDIClient",        TRUE,  TRUE,  TRUE,  TRUE,  TRUE },
-    { "#32768",            "#32768",           FALSE, FALSE, TRUE,  TRUE,  TRUE },
-    { "#32770",            "#32770",           TRUE,  TRUE,  TRUE,  TRUE,  TRUE },
+    { "MDIClient",         "MDIClient",        TRUE,  TRUE,  TRUE,  TRUE,  FALSE },
+    { "#32768",            "#32768",           FALSE, FALSE, TRUE,  TRUE,  FALSE },
+    { "#32770",            "#32770",           TRUE,  TRUE,  TRUE,  TRUE,  FALSE },
     /* Not all built-in classes set real window class. */
     { "Message",           NULL,               FALSE, FALSE, FALSE, FALSE, FALSE },
 };
diff --git a/dlls/user32/tests/static.c b/dlls/user32/tests/static.c
index b803de1dc49c..e992130f9550 100644
--- a/dlls/user32/tests/static.c
+++ b/dlls/user32/tests/static.c
@@ -452,11 +452,13 @@ static void test_window_extra_data_proc(char *arg)
     SetLastError(0xdeadbeef);
     v = SetWindowLongA(hwnd, 0, 1);
     ok(!v, "v = %ld\n", v);
-    ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == ERROR_INVALID_INDEX) /* <= Win1607 */,
+       "GetLastError() = %ld\n", GetLastError());
 
     v = SetWindowLongA(hwnd, 0, 1);
     ok(!v, "v = %ld\n", v);
-    ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == ERROR_INVALID_INDEX) /* <= Win1607 */,
+       "GetLastError() = %ld\n", GetLastError());
 
     PostMessageA(hwnd, WM_QUIT, 0, 0);
 }
diff --git a/dlls/userenv/Makefile.in b/dlls/userenv/Makefile.in
index 193232e94289..aa94f7bc96c8 100644
--- a/dlls/userenv/Makefile.in
+++ b/dlls/userenv/Makefile.in
@@ -3,7 +3,5 @@ MODULE    = userenv.dll
 IMPORTS   = advapi32
 IMPORTLIB = userenv
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	userenv_main.c
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c
index 4a52079e1f0f..7e5278f91e9e 100644
--- a/dlls/vbscript/compile.c
+++ b/dlls/vbscript/compile.c
@@ -63,6 +63,7 @@ typedef struct {
 
     function_t *func;
     function_decl_t *func_decls;
+    dim_decl_t *class_props;
 } compile_ctx_t;
 
 static HRESULT compile_expression(compile_ctx_t*,expression_t*);
@@ -192,6 +193,32 @@ static HRESULT push_instr_uint(compile_ctx_t *ctx, vbsop_t op, unsigned arg)
     return S_OK;
 }
 
+static HRESULT push_instr_int_uint(compile_ctx_t *ctx, vbsop_t op, LONG arg1, unsigned arg2)
+{
+    unsigned ret;
+
+    ret = push_instr(ctx, op);
+    if(!ret)
+        return E_OUTOFMEMORY;
+
+    instr_ptr(ctx, ret)->arg1.lng = arg1;
+    instr_ptr(ctx, ret)->arg2.uint = arg2;
+    return S_OK;
+}
+
+static HRESULT push_instr_uint_uint(compile_ctx_t *ctx, vbsop_t op, unsigned arg1, unsigned arg2)
+{
+    unsigned ret;
+
+    ret = push_instr(ctx, op);
+    if(!ret)
+        return E_OUTOFMEMORY;
+
+    instr_ptr(ctx, ret)->arg1.uint = arg1;
+    instr_ptr(ctx, ret)->arg2.uint = arg2;
+    return S_OK;
+}
+
 static HRESULT push_instr_addr(compile_ctx_t *ctx, vbsop_t op, unsigned arg)
 {
     unsigned ret;
@@ -493,6 +520,54 @@ static BOOL lookup_func_decls(compile_ctx_t *ctx, const WCHAR *name)
     return FALSE;
 }
 
+/* Resolve an identifier to a local variable or argument index at compile time.
+ * Returns TRUE if bound, with *ret set to: non-negative = dim var index, negative = arg index (-1 = arg 0, etc.).
+ * This mirrors jscript's bind_local() / local_off() convention. */
+static BOOL bind_local(compile_ctx_t *ctx, const WCHAR *name, int *ret)
+{
+    dim_decl_t *dim_decl;
+    unsigned i;
+
+    if(ctx->func->type == FUNC_GLOBAL)
+        return FALSE;
+
+    for(dim_decl = ctx->dim_decls, i = 0; dim_decl; dim_decl = dim_decl->next, i++) {
+        if(!vbs_wcsicmp(dim_decl->name, name)) {
+            *ret = i;
+            return TRUE;
+        }
+    }
+
+    for(i = 0; i < ctx->func->arg_cnt; i++) {
+        if(!vbs_wcsicmp(ctx->func->args[i].name, name)) {
+            *ret = -(int)i - 1;
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/* Resolve an identifier to a class property index at compile time.
+ * Only valid when compiling inside a class method (ctx->class_props != NULL). */
+static BOOL bind_class_prop(compile_ctx_t *ctx, const WCHAR *name, unsigned *ret)
+{
+    dim_decl_t *prop;
+    unsigned i;
+
+    if(!ctx->class_props)
+        return FALSE;
+
+    for(prop = ctx->class_props, i = 0; prop; prop = prop->next, i++) {
+        if(!vbs_wcsicmp(prop->name, name)) {
+            *ret = i;
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
 static HRESULT compile_args(compile_ctx_t *ctx, expression_t *args, unsigned *ret)
 {
     unsigned arg_cnt = 0;
@@ -544,6 +619,8 @@ static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t
 {
     expression_t *const_expr;
     HRESULT hres;
+    unsigned prop_ref;
+    int local_ref;
 
     if (expr->obj_expr) {
         hres = compile_expression(ctx, expr->obj_expr);
@@ -552,11 +629,16 @@ static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t
         return push_instr_bstr(ctx, OP_mget, expr->identifier);
     }
 
-    if (!lookup_dim_decls(ctx, expr->identifier) && !lookup_args_name(ctx, expr->identifier)) {
-        const_expr = lookup_const_decls(ctx, expr->identifier, TRUE);
-        if(const_expr)
-            return compile_expression(ctx, const_expr);
-    }
+    if(bind_local(ctx, expr->identifier, &local_ref))
+        return push_instr_int(ctx, OP_local, local_ref);
+
+    if(bind_class_prop(ctx, expr->identifier, &prop_ref))
+        return push_instr_uint(ctx, OP_local_prop, prop_ref);
+
+    const_expr = lookup_const_decls(ctx, expr->identifier, TRUE);
+    if(const_expr)
+        return compile_expression(ctx, const_expr);
+
     return push_instr_bstr(ctx, OP_ident, expr->identifier);
 }
 
@@ -944,8 +1026,12 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st
     statement_ctx_t loop_ctx = {3};
     unsigned step_instr, instr, expr_err_label, past_err_label, body_label, from_offset;
     BSTR identifier;
+    int local_ref;
+    BOOL is_local;
     HRESULT hres;
 
+    is_local = bind_local(ctx, stat->identifier, &local_ref);
+
     identifier = alloc_bstr_arg(ctx, stat->identifier);
     if(!identifier)
         return E_OUTOFMEMORY;
@@ -1014,10 +1100,17 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st
     if(!loop_ctx.for_end_label)
         return E_OUTOFMEMORY;
 
-    step_instr = push_instr(ctx, OP_step);
-    if(!step_instr)
-        return E_OUTOFMEMORY;
-    instr_ptr(ctx, step_instr)->arg2.bstr = identifier;
+    if(is_local) {
+        step_instr = push_instr(ctx, OP_step_local);
+        if(!step_instr)
+            return E_OUTOFMEMORY;
+        instr_ptr(ctx, step_instr)->arg2.lng = local_ref;
+    }else {
+        step_instr = push_instr(ctx, OP_step);
+        if(!step_instr)
+            return E_OUTOFMEMORY;
+        instr_ptr(ctx, step_instr)->arg2.bstr = identifier;
+    }
     instr_ptr(ctx, step_instr)->arg1.uint = loop_ctx.for_end_label;
 
     if(!emit_catch(ctx, 3))
@@ -1029,11 +1122,19 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st
     if(FAILED(hres))
         return hres;
 
-
-    instr = push_instr(ctx, OP_incc);
-    if(!instr)
-        return E_OUTOFMEMORY;
-    instr_ptr(ctx, instr)->arg1.bstr = identifier;
+    /* We need a separated OP_step here so that errors jump to the end-of-loop catch. */
+    ctx->loc = stat->stat.loc;
+    if(is_local) {
+        instr = push_instr(ctx, OP_incc_local);
+        if(!instr)
+            return E_OUTOFMEMORY;
+        instr_ptr(ctx, instr)->arg1.lng = local_ref;
+    }else {
+        instr = push_instr(ctx, OP_incc);
+        if(!instr)
+            return E_OUTOFMEMORY;
+        instr_ptr(ctx, instr)->arg1.bstr = identifier;
+    }
 
     instr = push_instr(ctx, OP_step);
     if(!instr)
@@ -1268,6 +1369,33 @@ static HRESULT compile_assignment(compile_ctx_t *ctx, expression_t *left, expres
             return hres;
     }
 
+    if(!member_expr->obj_expr) {
+        int local_ref;
+        unsigned prop_ref;
+        if(bind_local(ctx, member_expr->identifier, &local_ref)) {
+            hres = push_instr_int_uint(ctx, is_set ? OP_set_local : OP_assign_local,
+                                       local_ref, args_cnt);
+            if(FAILED(hres))
+                return hres;
+
+            if(!emit_catch(ctx, 0))
+                return E_OUTOFMEMORY;
+
+            return S_OK;
+        }
+        if(bind_class_prop(ctx, member_expr->identifier, &prop_ref)) {
+            hres = push_instr_uint_uint(ctx, is_set ? OP_set_local_prop : OP_assign_local_prop,
+                                        prop_ref, args_cnt);
+            if(FAILED(hres))
+                return hres;
+
+            if(!emit_catch(ctx, 0))
+                return E_OUTOFMEMORY;
+
+            return S_OK;
+        }
+    }
+
     hres = push_instr_bstr_uint(ctx, op, member_expr->identifier, args_cnt);
     if(FAILED(hres))
         return hres;
@@ -2077,6 +2205,7 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl)
        before compiling the methods. Same-class collisions are caught below. */
     ctx->dim_decls = ctx->dim_decls_tail = NULL;
     ctx->const_decls = NULL;
+    ctx->class_props = class_decl->props;
 
     for(func_decl = class_decl->funcs, i=1; func_decl; func_decl = func_decl->next, i++) {
         for(func_prop_decl = func_decl; func_prop_decl; func_prop_decl = func_prop_decl->next_prop_func) {
@@ -2115,6 +2244,8 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl)
             return hres;
     }
 
+    ctx->class_props = NULL;
+
     for(prop_decl = class_decl->props; prop_decl; prop_decl = prop_decl->next)
         class_desc->prop_cnt++;
 
diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c
index 5cf167fc4f1c..1bd58335eb55 100644
--- a/dlls/vbscript/global.c
+++ b/dlls/vbscript/global.c
@@ -5058,7 +5058,7 @@ static HRESULT Err_Clear(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VA
 {
     TRACE("\n");
 
-    clear_ei(&This->ctx->ei);
+    clear_error(This->ctx);
     return S_OK;
 }
 
@@ -5088,6 +5088,9 @@ static HRESULT Err_Raise(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VA
     if(SUCCEEDED(hres)) {
         script_ctx_t *ctx = This->ctx;
 
+        SysFreeString(ctx->ei_identifier);
+        ctx->ei_identifier = NULL;
+
         if(source) {
             SysFreeString(ctx->ei.bstrSource);
             ctx->ei.bstrSource = source;
diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c
index 8f1688229a12..c57b28ab90e4 100644
--- a/dlls/vbscript/interp.c
+++ b/dlls/vbscript/interp.c
@@ -316,6 +316,13 @@ void clear_ei(EXCEPINFO *ei)
     memset(ei, 0, sizeof(*ei));
 }
 
+void clear_error(script_ctx_t *ctx)
+{
+    clear_ei(&ctx->ei);
+    SysFreeString(ctx->ei_identifier);
+    ctx->ei_identifier = NULL;
+}
+
 void clear_error_loc(script_ctx_t *ctx)
 {
     if(ctx->error_loc_code) {
@@ -324,34 +331,17 @@ void clear_error_loc(script_ctx_t *ctx)
     }
 }
 
+/* The identifier is not part of the error description visible to scripts
+   through the Err object; report_script_error appends it to the description
+   passed to the host. */
 static HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *identifier)
 {
-    BSTR desc, source;
-
-    desc = get_vbscript_string(HRESULT_CODE(error));
-    if(desc && identifier) {
-        unsigned desc_len = SysStringLen(desc);
-        unsigned ident_len = lstrlenW(identifier);
-        /* format: "Error text: 'identifier'" */
-        BSTR new_desc = SysAllocStringLen(NULL, desc_len + 3 + ident_len + 1);
-        if(new_desc) {
-            memcpy(new_desc, desc, desc_len * sizeof(WCHAR));
-            new_desc[desc_len] = ':';
-            new_desc[desc_len + 1] = ' ';
-            new_desc[desc_len + 2] = '\'';
-            memcpy(new_desc + desc_len + 3, identifier, ident_len * sizeof(WCHAR));
-            new_desc[desc_len + 3 + ident_len] = '\'';
-            SysFreeString(desc);
-            desc = new_desc;
-        }
-    }
-
-    source = get_vbscript_string(VBS_RUNTIME_ERROR);
-
-    clear_ei(&ctx->ei);
+    clear_error(ctx);
     ctx->ei.scode = error;
-    ctx->ei.bstrDescription = desc;
-    ctx->ei.bstrSource = source;
+    ctx->ei.bstrDescription = get_vbscript_string(HRESULT_CODE(error));
+    ctx->ei.bstrSource = get_vbscript_string(VBS_RUNTIME_ERROR);
+    if(identifier)
+        ctx->ei_identifier = SysAllocString(identifier);
     return SCRIPT_E_RECORDED;
 }
 
@@ -892,6 +882,42 @@ static HRESULT interp_mget(exec_ctx_t *ctx)
     return stack_push(ctx, &res);
 }
 
+static HRESULT interp_local(exec_ctx_t *ctx)
+{
+    const int arg = ctx->instr->arg1.lng;
+    VARIANT *v;
+    VARIANT r;
+
+    if(arg < 0)
+        v = ctx->args - arg - 1;
+    else
+        v = ctx->vars + arg;
+
+    TRACE("%s\n", debugstr_variant(v));
+
+    if(V_VT(v) == (VT_BYREF|VT_VARIANT))
+        v = V_VARIANTREF(v);
+
+    V_VT(&r) = VT_BYREF|VT_VARIANT;
+    V_BYREF(&r) = v;
+    return stack_push(ctx, &r);
+}
+
+static HRESULT interp_local_prop(exec_ctx_t *ctx)
+{
+    const unsigned arg = ctx->instr->arg1.uint;
+    VARIANT *v;
+    VARIANT r;
+
+    v = ctx->vbthis->props + arg;
+
+    TRACE("%s\n", debugstr_variant(v));
+
+    V_VT(&r) = VT_BYREF|VT_VARIANT;
+    V_BYREF(&r) = v;
+    return stack_push(ctx, &r);
+}
+
 static HRESULT interp_ident(exec_ctx_t *ctx)
 {
     BSTR identifier = ctx->instr->arg1.bstr;
@@ -940,61 +966,63 @@ static HRESULT assign_value(exec_ctx_t *ctx, VARIANT *dst, VARIANT *src, WORD fl
     return S_OK;
 }
 
-static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, WORD flags, DISPPARAMS *dp)
+static HRESULT assign_local_var(exec_ctx_t *ctx, VARIANT *v, WORD flags, DISPPARAMS *dp)
 {
-    ref_t ref;
     HRESULT hres;
 
-    hres = lookup_identifier(ctx, name, VBDISP_LET, &ref);
-    if(FAILED(hres))
-        return hres;
+    if(V_VT(v) == (VT_VARIANT|VT_BYREF))
+        v = V_VARIANTREF(v);
 
-    switch(ref.type) {
-    case REF_VAR: {
-        VARIANT *v = ref.u.v;
+    if(arg_cnt(dp)) {
+        SAFEARRAY *array;
 
-        if(V_VT(v) == (VT_VARIANT|VT_BYREF))
-            v = V_VARIANTREF(v);
+        if(V_VT(v) == VT_DISPATCH)
+            return disp_propput(ctx->script, V_DISPATCH(v), DISPID_VALUE, flags, dp);
 
-        if(arg_cnt(dp)) {
-            SAFEARRAY *array;
+        if(!(V_VT(v) & VT_ARRAY))
+            return DISP_E_TYPEMISMATCH;
 
-            if(V_VT(v) == VT_DISPATCH) {
-                hres = disp_propput(ctx->script, V_DISPATCH(v), DISPID_VALUE, flags, dp);
-                break;
-            }
+        switch(V_VT(v)) {
+        case VT_ARRAY|VT_BYREF|VT_VARIANT:
+            array = *V_ARRAYREF(v);
+            break;
+        case VT_ARRAY|VT_VARIANT:
+            array = V_ARRAY(v);
+            break;
+        default:
+            FIXME("Unsupported array type %x\n", V_VT(v));
+            return E_NOTIMPL;
+        }
 
-            if(!(V_VT(v) & VT_ARRAY))
-                return DISP_E_TYPEMISMATCH;
+        if(!array) {
+            WARN("null array\n");
+            return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
+        }
 
-            switch(V_VT(v)) {
-            case VT_ARRAY|VT_BYREF|VT_VARIANT:
-                array = *V_ARRAYREF(v);
-                break;
-            case VT_ARRAY|VT_VARIANT:
-                array = V_ARRAY(v);
-                break;
-            default:
-                FIXME("Unsupported array type %x\n", V_VT(v));
-                return E_NOTIMPL;
-            }
+        hres = array_access(array, dp, &v);
+        if(FAILED(hres))
+            return hres;
+    }else if(V_VT(v) == (VT_ARRAY|VT_BYREF|VT_VARIANT)) {
+        FIXME("non-array assign\n");
+        return E_NOTIMPL;
+    }
 
-            if(!array) {
-                WARN("null array\n");
-                return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
-            }
+    return assign_value(ctx, v, dp->rgvarg, flags);
+}
 
-            hres = array_access(array, dp, &v);
-            if(FAILED(hres))
-                return hres;
-        }else if(V_VT(v) == (VT_ARRAY|VT_BYREF|VT_VARIANT)) {
-            FIXME("non-array assign\n");
-            return E_NOTIMPL;
-        }
+static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, WORD flags, DISPPARAMS *dp)
+{
+    ref_t ref;
+    HRESULT hres;
 
-        hres = assign_value(ctx, v, dp->rgvarg, flags);
+    hres = lookup_identifier(ctx, name, VBDISP_LET, &ref);
+    if(FAILED(hres))
+        return hres;
+
+    switch(ref.type) {
+    case REF_VAR:
+        hres = assign_local_var(ctx, ref.u.v, flags, dp);
         break;
-    }
     case REF_DISP:
         hres = disp_propput(ctx->script, ref.u.d.disp, ref.u.d.id, flags, dp);
         break;
@@ -1066,6 +1094,92 @@ static HRESULT interp_set_ident(exec_ctx_t *ctx)
     return S_OK;
 }
 
+static HRESULT interp_assign_local(exec_ctx_t *ctx)
+{
+    const int ref = ctx->instr->arg1.lng;
+    const unsigned arg_cnt = ctx->instr->arg2.uint;
+    VARIANT *v;
+    DISPPARAMS dp;
+    HRESULT hres;
+
+    v = ref < 0 ? ctx->args - ref - 1 : ctx->vars + ref;
+
+    TRACE("%d\n", ref);
+
+    vbstack_to_dp(ctx, arg_cnt, TRUE, &dp);
+    hres = assign_local_var(ctx, v, DISPATCH_PROPERTYPUT, &dp);
+    if(FAILED(hres))
+        return hres;
+
+    stack_popn(ctx, arg_cnt+1);
+    return S_OK;
+}
+
+static HRESULT interp_set_local(exec_ctx_t *ctx)
+{
+    const int ref = ctx->instr->arg1.lng;
+    const unsigned arg_cnt = ctx->instr->arg2.uint;
+    VARIANT *v;
+    DISPPARAMS dp;
+    HRESULT hres;
+
+    v = ref < 0 ? ctx->args - ref - 1 : ctx->vars + ref;
+
+    TRACE("%d %u\n", ref, arg_cnt);
+
+    hres = stack_assume_disp(ctx, arg_cnt, NULL);
+    if(FAILED(hres))
+        return hres;
+
+    vbstack_to_dp(ctx, arg_cnt, TRUE, &dp);
+    hres = assign_local_var(ctx, v, DISPATCH_PROPERTYPUTREF, &dp);
+    if(FAILED(hres))
+        return hres;
+
+    stack_popn(ctx, arg_cnt + 1);
+    return S_OK;
+}
+
+static HRESULT interp_assign_local_prop(exec_ctx_t *ctx)
+{
+    const unsigned ref = ctx->instr->arg1.uint;
+    const unsigned arg_cnt = ctx->instr->arg2.uint;
+    DISPPARAMS dp;
+    HRESULT hres;
+
+    TRACE("%u\n", ref);
+
+    vbstack_to_dp(ctx, arg_cnt, TRUE, &dp);
+    hres = assign_local_var(ctx, ctx->vbthis->props + ref, DISPATCH_PROPERTYPUT, &dp);
+    if(FAILED(hres))
+        return hres;
+
+    stack_popn(ctx, arg_cnt+1);
+    return S_OK;
+}
+
+static HRESULT interp_set_local_prop(exec_ctx_t *ctx)
+{
+    const unsigned ref = ctx->instr->arg1.uint;
+    const unsigned arg_cnt = ctx->instr->arg2.uint;
+    DISPPARAMS dp;
+    HRESULT hres;
+
+    TRACE("%u %u\n", ref, arg_cnt);
+
+    hres = stack_assume_disp(ctx, arg_cnt, NULL);
+    if(FAILED(hres))
+        return hres;
+
+    vbstack_to_dp(ctx, arg_cnt, TRUE, &dp);
+    hres = assign_local_var(ctx, ctx->vbthis->props + ref, DISPATCH_PROPERTYPUTREF, &dp);
+    if(FAILED(hres))
+        return hres;
+
+    stack_popn(ctx, arg_cnt + 1);
+    return S_OK;
+}
+
 static HRESULT interp_assign_member(exec_ctx_t *ctx)
 {
     BSTR identifier = ctx->instr->arg1.bstr;
@@ -1675,11 +1789,42 @@ static HRESULT interp_erase(exec_ctx_t *ctx)
     return hres;
 }
 
-static HRESULT interp_step(exec_ctx_t *ctx)
+static HRESULT do_for_step(exec_ctx_t *ctx, VARIANT *loop_var)
 {
-    const BSTR ident = ctx->instr->arg2.bstr;
     BOOL gteq_zero;
     VARIANT zero;
+    HRESULT hres;
+
+    V_VT(&zero) = VT_I2;
+    V_I2(&zero) = 0;
+    hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0);
+    if(FAILED(hres))
+        goto loop_not_initialized;
+
+    gteq_zero = hres == VARCMP_GT || hres == VARCMP_EQ;
+
+    hres = VarCmp(loop_var, stack_top(ctx, 1), ctx->script->lcid, 0);
+    if(FAILED(hres))
+        goto loop_not_initialized;
+
+    if(hres == VARCMP_EQ || hres == (gteq_zero ? VARCMP_LT : VARCMP_GT)) {
+        ctx->instr++;
+    }else {
+        stack_popn(ctx, 3);
+        instr_jmp(ctx, ctx->instr->arg1.uint);
+    }
+    return S_OK;
+
+loop_not_initialized:
+    WARN("For loop not initialized\n");
+    stack_popn(ctx, 2);
+    instr_jmp(ctx, ctx->instr->arg1.uint);
+    return hres;
+}
+
+static HRESULT interp_step(exec_ctx_t *ctx)
+{
+    const BSTR ident = ctx->instr->arg2.bstr;
     ref_t ref;
     HRESULT hres;
 
@@ -1690,7 +1835,7 @@ static HRESULT interp_step(exec_ctx_t *ctx)
      * and exit the loop. */
     if(V_VT(stack_top(ctx, 0)) == VT_EMPTY && V_VT(stack_top(ctx, 1)) == VT_EMPTY) {
         WARN("For loop not initialized\n");
-        clear_ei(&ctx->script->ei);
+        clear_error(ctx->script);
         ctx->script->ei.scode = MAKE_VBSERROR(VBSE_FOR_LOOP_NOT_INITIALIZED);
         map_vbs_exception(&ctx->script->ei);
         stack_popn(ctx, 3);
@@ -1698,14 +1843,6 @@ static HRESULT interp_step(exec_ctx_t *ctx)
         return S_OK;
     }
 
-    V_VT(&zero) = VT_I2;
-    V_I2(&zero) = 0;
-    hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0);
-    if(FAILED(hres))
-        goto loop_not_initialized;
-
-    gteq_zero = hres == VARCMP_GT || hres == VARCMP_EQ;
-
     hres = lookup_identifier(ctx, ident, VBDISP_ANY, &ref);
     if(FAILED(hres))
         return hres;
@@ -1715,23 +1852,29 @@ static HRESULT interp_step(exec_ctx_t *ctx)
         return E_FAIL;
     }
 
-    hres = VarCmp(ref.u.v, stack_top(ctx, 1), ctx->script->lcid, 0);
-    if(FAILED(hres))
-        goto loop_not_initialized;
+    return do_for_step(ctx, ref.u.v);
+}
 
-    if(hres == VARCMP_EQ || hres == (gteq_zero ? VARCMP_LT : VARCMP_GT)) {
-        ctx->instr++;
-    }else {
+static HRESULT interp_step_local(exec_ctx_t *ctx)
+{
+    const int ref = ctx->instr->arg2.lng;
+    VARIANT *v;
+
+    TRACE("%d\n", ref);
+
+    v = ref < 0 ? ctx->args - ref - 1 : ctx->vars + ref;
+
+    if(V_VT(stack_top(ctx, 0)) == VT_EMPTY && V_VT(stack_top(ctx, 1)) == VT_EMPTY) {
+        WARN("For loop not initialized\n");
+        clear_error(ctx->script);
+        ctx->script->ei.scode = MAKE_VBSERROR(VBSE_FOR_LOOP_NOT_INITIALIZED);
+        map_vbs_exception(&ctx->script->ei);
         stack_popn(ctx, 3);
         instr_jmp(ctx, ctx->instr->arg1.uint);
+        return S_OK;
     }
-    return S_OK;
 
-loop_not_initialized:
-    WARN("For loop not initialized\n");
-    stack_popn(ctx, 2);
-    instr_jmp(ctx, ctx->instr->arg1.uint);
-    return hres;
+    return do_for_step(ctx, v);
 }
 
 static HRESULT interp_newenum(exec_ctx_t *ctx)
@@ -1970,7 +2113,7 @@ static HRESULT interp_errmode(exec_ctx_t *ctx)
     TRACE("%d\n", err_mode);
 
     ctx->resume_next = err_mode;
-    clear_ei(&ctx->script->ei);
+    clear_error(ctx->script);
     return S_OK;
 }
 
@@ -2822,10 +2965,23 @@ static HRESULT interp_neg(exec_ctx_t *ctx)
     return stack_push(ctx, &v);
 }
 
+static HRESULT do_incc(exec_ctx_t *ctx, VARIANT *v)
+{
+    VARIANT tmp;
+    HRESULT hres;
+
+    hres = VarAdd(stack_top(ctx, 0), v, &tmp);
+    if(FAILED(hres))
+        return hres;
+
+    VariantClear(v);
+    *v = tmp;
+    return S_OK;
+}
+
 static HRESULT interp_incc(exec_ctx_t *ctx)
 {
     const BSTR ident = ctx->instr->arg1.bstr;
-    VARIANT v;
     ref_t ref;
     HRESULT hres;
 
@@ -2840,13 +2996,16 @@ static HRESULT interp_incc(exec_ctx_t *ctx)
         return E_FAIL;
     }
 
-    hres = VarAdd(stack_top(ctx, 0), ref.u.v, &v);
-    if(FAILED(hres))
-        return hres;
+    return do_incc(ctx, ref.u.v);
+}
 
-    VariantClear(ref.u.v);
-    *ref.u.v = v;
-    return S_OK;
+static HRESULT interp_incc_local(exec_ctx_t *ctx)
+{
+    const int ref = ctx->instr->arg1.lng;
+
+    TRACE("%d\n", ref);
+
+    return do_incc(ctx, ref < 0 ? ctx->args - ref - 1 : ctx->vars + ref);
 }
 
 static HRESULT interp_with(exec_ctx_t *ctx)
@@ -3076,7 +3235,7 @@ HRESULT exec_script(script_ctx_t *ctx, BOOL extern_caller, function_t *func, vbd
         if(FAILED(hres)) {
             if(hres != SCRIPT_E_RECORDED) {
                 /* SCRIPT_E_RECORDED means ctx->ei is already populated */
-                clear_ei(&ctx->ei);
+                clear_error(ctx);
                 ctx->ei.scode = hres;
             }
 
diff --git a/dlls/vbscript/lex.c b/dlls/vbscript/lex.c
index db1b8b7b3553..ef20604c8dbf 100644
--- a/dlls/vbscript/lex.c
+++ b/dlls/vbscript/lex.c
@@ -534,7 +534,12 @@ static int parse_next_token(void *lval, unsigned *loc, parser_ctx_t *ctx)
          * We need to distinguish between '.' used as part of a member expression and
          * a beginning of a dot expression (a member expression accessing with statement
          * expression) and a floating point number like ".2" .
+         *
+         * A dot immediately followed by a digit is always a numeric literal, even
+         * right after an identifier: obj.method.5 parses as obj.method(0.5).
          */
+        if('0' <= ctx->ptr[1] && ctx->ptr[1] <= '9')
+            return parse_numeric_literal(ctx, lval);
         c = ctx->ptr > ctx->code ? ctx->ptr[-1] : '\n';
         if (is_identifier_char(c) || c == ')') {
             ctx->ptr++;
@@ -548,9 +553,6 @@ static int parse_next_token(void *lval, unsigned *loc, parser_ctx_t *ctx)
             ctx->ptr++;
             return '.';
         }
-        c = ctx->ptr[1];
-        if('0' <= c && c <= '9')
-            return parse_numeric_literal(ctx, lval);
         ctx->ptr++;
         return tDOT;
     case '-':
diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y
index ec67cde22157..2080126b2785 100644
--- a/dlls/vbscript/parser.y
+++ b/dlls/vbscript/parser.y
@@ -26,6 +26,7 @@
 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
 
 static int parser_error(unsigned*,parser_ctx_t*,const char*);
+static void override_generic_error(parser_ctx_t*,HRESULT);
 
 static void handle_isexpression_script(parser_ctx_t *ctx, expression_t *expr);
 
@@ -185,8 +186,13 @@ SourceElements
     : /* empty */
     | SourceElements GlobalDimDeclaration StSep
                                             { source_add_statement(ctx, $2); }
-    | SourceElements StatementNl            { source_add_statement(ctx, $2); }
-    | SourceElements ClassDeclaration       { source_add_class(ctx, $2); }
+    | SourceElements SimpleStatement StSep  { source_add_statement(ctx, $2); }
+    | SourceElements ClassDeclaration StSep { source_add_class(ctx, $2); }
+
+/* A Class declaration is reachable only here at script global scope, not from
+   the shared SimpleStatement used by every body, so a Class anywhere else is
+   rejected as a syntax error (see the tCLASS rule in SimpleStatement), while
+   still being allowed after another statement, e.g. Dim x : Class C. */
 
 GlobalDimDeclaration
     : tPRIVATE tCONST ConstDeclList         { $$ = new_const_statement(ctx, @$, $3); CHECK_ERROR; }
@@ -267,6 +273,12 @@ SimpleStatement
     | tDO StSep StatementsNl_opt error      { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_LOOP); YYABORT; }
     | tDO error                             { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_WHILE_UNTIL_EOS); YYABORT; }
     | FunctionDecl                          { $$ = new_function_statement(ctx, @$, $1); CHECK_ERROR; }
+    | tCLASS                                { /* A Class is only valid at global scope (see SourceElements). The real
+                                                ClassDeclaration wins the shift there, so this rule only matches in a
+                                                body, where it reports the syntax error native reports. It is needed:
+                                                without it, error recovery surfaces a different, context-dependent
+                                                error code instead of the plain syntax error. */
+                                              ctx->error_loc = @1; ctx->hres = MAKE_VBSERROR(VBSE_SYNTAX_ERROR); YYABORT; }
     | tEXIT tDO                             { $$ = new_statement(ctx, STAT_EXITDO, 0, @2); CHECK_ERROR; }
     | tEXIT tFOR                            { $$ = new_statement(ctx, STAT_EXITFOR, 0, @2); CHECK_ERROR; }
     | tEXIT tFUNCTION                       { $$ = new_statement(ctx, STAT_EXITFUNC, 0, @2); CHECK_ERROR; }
@@ -300,6 +312,8 @@ SimpleStatement
     | tFOR tEACH MemberExpression error     { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_IN); YYABORT; }
     | tFOR tEACH MemberExpression tIN Expression StSep StatementsNl_opt error
                                             { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_NEXT); YYABORT; }
+    | tFOR tEACH error                      { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
+    | tFOR error                            { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
     | tSELECT tCASE Expression StSep CaseClausules tEND tSELECT
                                             { $$ = new_select_statement(ctx, @$, $3, $5); }
     | tSELECT tCASE Expression StSep CaseClausules tEND error
@@ -316,8 +330,10 @@ SimpleStatement
 MemberExpression
     : Identifier                            { $$ = new_member_expression(ctx, NULL, $1); CHECK_ERROR; }
     | CallExpression '.' tIdentifier        { $$ = new_member_expression(ctx, $1, $3); CHECK_ERROR; }
+    | CallExpression '.' error              { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
     | tDOT tIdentifier                      { expression_t *dot_expr = new_expression(ctx, EXPR_DOT, sizeof(*dot_expr)); CHECK_ERROR;
                                               $$ = new_member_expression(ctx, dot_expr, $2); CHECK_ERROR; }
+    | tDOT error                            { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
 
 Preserve_opt
     : /* empty */                           { $$ = FALSE; }
@@ -338,14 +354,13 @@ ReDimDecl
 ReDimDeclList
     : ReDimDecl                             { $$ = $1; }
     | ReDimDecl ',' ReDimDeclList           { $1->next = $3; $$ = $1; }
+    | error                                 { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
 
 
 DimDeclList
     : DimDecl                               { $$ = $1; }
     | DimDecl ',' DimDeclList               { $1->next = $3; $$ = $1; }
-    | error                                 { if(ctx->hres == MAKE_VBSERROR(VBSE_SYNTAX_ERROR))
-                                                 ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER);
-                                               YYABORT; }
+    | error                                 { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
 
 DimDecl
     : Identifier                            { $$ = new_dim_decl(ctx, $1, @1, FALSE, NULL); CHECK_ERROR; }
@@ -360,6 +375,7 @@ DimList
 ConstDeclList
     : ConstDecl                             { $$ = $1; }
     | ConstDecl ',' ConstDeclList           { $1->next = $3; $$ = $1; }
+    | error                                 { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
 
 ConstDecl
     : Identifier '=' Expression             { $$ = new_const_decl(ctx, @1, $1, $3); CHECK_ERROR; }
@@ -574,9 +590,10 @@ PrimaryExpression
     | tME                           { $$ = new_expression(ctx, EXPR_ME, 0); CHECK_ERROR; }
 
 ClassDeclaration
-    : tCLASS Identifier StSep ClassBody tEND tCLASS StSep       { $4->name = $2; $4->loc = @2; $$ = $4; }
+    : tCLASS Identifier StSep ClassBody tEND tCLASS             { $4->name = $2; $4->loc = @2; $$ = $4; }
     | tCLASS Identifier tEND tCLASS         { ctx->error_loc = @3; ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_STATEMENT); YYABORT; }
     | tCLASS Identifier StSep ClassBody tEND error               { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_CLASS); YYABORT; }
+    | tCLASS error                          { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
 
 ClassBody
     : /* empty */                                 { $$ = new_class_decl(ctx); }
@@ -631,6 +648,8 @@ FunctionDecl
                                     { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_LPAREN); YYABORT; }
     | Storage_opt tFUNCTION Identifier error
                                     { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_LPAREN); YYABORT; }
+    | Storage_opt tSUB error        { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
+    | Storage_opt tFUNCTION error   { override_generic_error(ctx, MAKE_VBSERROR(VBSE_EXPECTED_IDENTIFIER)); YYABORT; }
 
 Storage_opt
     : /* empty*/                    { $$ = 0; }
@@ -703,6 +722,14 @@ static int parser_error(unsigned *loc, parser_ctx_t *ctx, const char *str)
         case tNEXT:
             ctx->hres = MAKE_VBSERROR(VBSE_UNEXPECTED_NEXT);
             break;
+        case tInt:
+        case tDouble:
+        case tString:
+            /* A literal can only start an expression, so a misplaced literal
+               means the statement should have ended before it. Error
+               productions override this in contexts expecting an identifier. */
+            ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_END_OF_STATEMENT);
+            break;
         default:
             WARN("%s: %s\n", debugstr_w(ctx->code + *loc), debugstr_a(str));
             ctx->hres = MAKE_VBSERROR(VBSE_SYNTAX_ERROR);
@@ -713,6 +740,15 @@ static int parser_error(unsigned *loc, parser_ctx_t *ctx, const char *str)
     return 0;
 }
 
+/* Replace the generic code chosen by parser_error with a context-specific
+   one, keeping specific errors reported by the lexer or other productions. */
+static void override_generic_error(parser_ctx_t *ctx, HRESULT hres)
+{
+    if(ctx->hres == MAKE_VBSERROR(VBSE_SYNTAX_ERROR)
+            || ctx->hres == MAKE_VBSERROR(VBSE_EXPECTED_END_OF_STATEMENT))
+        ctx->hres = hres;
+}
+
 static void source_add_statement(parser_ctx_t *ctx, statement_t *stat)
 {
     if(!stat)
@@ -733,8 +769,12 @@ static void source_add_statement(parser_ctx_t *ctx, statement_t *stat)
 
 static void source_add_class(parser_ctx_t *ctx, class_decl_t *class_decl)
 {
-    class_decl->next = ctx->class_decls;
-    ctx->class_decls = class_decl;
+    class_decl_t **iter;
+
+    /* Append to keep classes in source order, so a redefinition is reported at the later declaration. */
+    class_decl->next = NULL;
+    for(iter = &ctx->class_decls; *iter; iter = &(*iter)->next);
+    *iter = class_decl;
 }
 
 static void handle_isexpression_script(parser_ctx_t *ctx, expression_t *expr)
diff --git a/dlls/vbscript/tests/createobj.c b/dlls/vbscript/tests/createobj.c
index 10f114ad6e43..7065b5b9c816 100644
--- a/dlls/vbscript/tests/createobj.c
+++ b/dlls/vbscript/tests/createobj.c
@@ -84,7 +84,6 @@ DEFINE_EXPECT(ProcessUrlAction);
 DEFINE_EXPECT(QueryCustomPolicy);
 DEFINE_EXPECT(reportSuccess);
 DEFINE_EXPECT(Host_QS_SecMgr);
-DEFINE_EXPECT(Caller_QS_SecMgr);
 DEFINE_EXPECT(QI_IObjectWithSite);
 DEFINE_EXPECT(SetSite);
 
@@ -538,10 +537,8 @@ static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
         return E_NOINTERFACE;
 
     if(IsEqualGUID(&SID_SInternetHostSecurityManager, guidService)) {
-        if(iface == &ServiceProvider)
-            CHECK_EXPECT(Host_QS_SecMgr);
-        else
-            CHECK_EXPECT(Caller_QS_SecMgr);
+        ok(iface == &ServiceProvider, "called on non-host iface\n");
+        CHECK_EXPECT(Host_QS_SecMgr);
         ok(IsEqualGUID(&IID_IInternetHostSecurityManager, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid));
         if(SUCCEEDED(QS_SecMgr_hres))
             *ppv = &InternetHostSecurityManager;
diff --git a/dlls/vbscript/tests/error.vbs b/dlls/vbscript/tests/error.vbs
index 843c27a01bb3..f8747a2580be 100644
--- a/dlls/vbscript/tests/error.vbs
+++ b/dlls/vbscript/tests/error.vbs
@@ -492,6 +492,22 @@ end sub
 
 call testDivisionByZero
 
+sub testUndefinedVarDescription()
+    on error resume next
+
+    Err.Clear()
+    ExecuteGlobal "Option Explicit" & vbCrLf & "Dim q : q = undefinedReadVar"
+    call ok(Err.Number = 500, "read undefined Err.Number = " & Err.Number)
+    call ok(Err.Description = "Variable is undefined", "read undefined Err.Description = " & Err.Description)
+
+    Err.Clear()
+    ExecuteGlobal "Option Explicit" & vbCrLf & "undefinedAssignVar = 1"
+    call ok(Err.Number = 500, "assign undefined Err.Number = " & Err.Number)
+    call ok(Err.Description = "Variable is undefined", "assign undefined Err.Description = " & Err.Description)
+end sub
+
+call testUndefinedVarDescription
+
 on error resume next
 
 throwWithDesc
diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs
index acdd992cbcf0..6c9628d7e69e 100644
--- a/dlls/vbscript/tests/lang.vbs
+++ b/dlls/vbscript/tests/lang.vbs
@@ -1696,6 +1696,34 @@ x = false
 Call testsub_one_line
 Call ok(x, "x is false, testsub_one_line not called?")
 
+x = false
+Sub testsub_header_stmt() x = 1
+    x = x + 1
+End Sub
+Call ok(x = false, "testsub_header_stmt body executed at definition? x = " & x)
+Call testsub_header_stmt
+Call ok(x = 2, "testsub_header_stmt: x = " & x)
+
+Sub testsub_header_nospace()x = 3:x = x + 1
+    x = x + 1
+End Sub
+Call testsub_header_nospace
+Call ok(x = 5, "testsub_header_nospace: x = " & x)
+
+Sub testsub_header_set() Set x = Nothing : End Sub
+Call testsub_header_set
+Call ok(x Is Nothing, "testsub_header_set: TypeName(x) = " & TypeName(x))
+
+Sub testsub_header_colon_end() x = 6 : x = x + 1: End Sub
+Call testsub_header_colon_end
+Call ok(x = 7, "testsub_header_colon_end: x = " & x)
+
+Function testfunc_header_stmt() x = 8
+    x = x + 1
+End Function
+Call testfunc_header_stmt
+Call ok(x = 9, "testfunc_header_stmt: x = " & x)
+
 Sub SubSetTrue(v)
     Call ok(not v, "v is not true")
     v = true
@@ -1907,6 +1935,48 @@ Set npObj = New NpCls
 CheckNpS "npObj.Check(10)+5",        15
 CheckNpS "npObj.Check(10)*3",        30
 
+' A dot immediately followed by a digit is a numeric literal, not member access.
+CheckNpS "NpS.5",                    0.5
+CheckNpS "NpS.0",                    0
+CheckNpS "NpS.5E1",                  5
+CheckNpS "npObj.Check.5",            0.5
+CheckNpS "npObj.Check.0",            0
+CheckNpS "With npObj : .Check.5 : End With", 0.5
+CheckNpT "NpT.5,.25",                0.5,    0.25
+NpS.5
+Call ok(getVT(npArg) = "VT_R8*", "NpS.5: getVT(npArg) = " & getVT(npArg))
+
+Sub CheckParseErr(src, expected)
+    On Error Resume Next
+    Err.Clear
+    Execute src
+    Dim e : e = Err.Number
+    On Error GoTo 0
+    Call ok(e = expected, "parse error for " & src & ": err = " & e & " expected " & expected)
+End Sub
+
+CheckParseErr "npArg = npObj.Check.5",      1025
+CheckParseErr "NpS.5.5",                    1025
+CheckParseErr "npArg = (1).5",              1025
+CheckParseErr "npArg = 1.5.5",              1025
+CheckParseErr "npArg = 1 2",                1025
+CheckParseErr "NpS 1 2",                    1025
+CheckParseErr "npArg = 1 ""x""",            1025
+CheckParseErr "npObj.Check. 5",             1010
+CheckParseErr "npObj.Check .",              1010
+CheckParseErr "npArg = npObj.",             1010
+CheckParseErr "npArg = .",                  1010
+CheckParseErr "With npObj : . : End With",  1010
+CheckParseErr "Sub 5 : End Sub",            1010
+CheckParseErr "Function 5 : End Function",  1010
+CheckParseErr "Class 5 : End Class",        1010
+CheckParseErr "Const 5 = 1",                1010
+CheckParseErr "For 5 = 1 To 2 : Next",      1010
+CheckParseErr "For Each 5 In npArg : Next", 1010
+CheckParseErr "ReDim 5",                    1010
+CheckParseErr "Dim 5",                      1010
+CheckParseErr "Dim 1.5",                    1010
+
 Function ParenId(a)
     ParenId = a
 End Function
@@ -2915,6 +2985,50 @@ sub TestExecuteGlobalRedim
 end sub
 Call TestExecuteGlobalRedim
 
+' A Dim in a later compile unit may shadow a global Const from a prior one:
+' it creates a fresh variable that later compile units resolve to, while the
+' defining compile unit keeps the inlined const value.
+Dim egCrossVal
+Const egConst = 26
+ExecuteGlobal "Dim egConst"
+ExecuteGlobal "egCrossVal = IsEmpty(egConst) & "" "" & TypeName(egConst)"
+Call ok(egCrossVal = "True Empty", "dimmed-over-const fresh read: " & egCrossVal)
+ExecuteGlobal "egConst = 5"
+ExecuteGlobal "egCrossVal = egConst"
+Call ok(egCrossVal = 5, "dimmed-over-const after assign = " & egCrossVal)
+Call ok(egConst = 26, "egConst in defining compile unit = " & egConst)
+Call ok(Eval("egConst") = 5, "Eval from defining compile unit = " & Eval("egConst"))
+Execute "egCrossVal = egConst"
+Call ok(egCrossVal = 5, "Execute read from defining compile unit = " & egCrossVal)
+
+Const egConst2 = 7
+ExecuteGlobal "Dim egConst2 : egConst2 = 8 : egCrossVal = egConst2"
+Call ok(egCrossVal = 8, "dim+assign+read in one compile unit = " & egCrossVal)
+Call ok(egConst2 = 7, "egConst2 in defining compile unit = " & egConst2)
+
+Const egConstArr = 2
+ExecuteGlobal "Dim egConstArr(3)"
+ExecuteGlobal "egConstArr(0) = 11 : egCrossVal = egConstArr(0)"
+Call ok(egCrossVal = 11, "array dimmed over const element = " & egCrossVal)
+Call ok(egConstArr = 2, "egConstArr in defining compile unit = " & egConstArr)
+
+Sub TestDimOverConstErrors
+    on error resume next
+
+    err.clear : ExecuteGlobal "Const egConst = 27"
+    call ok(err.number = 1041, "Const over dimmed-over-const err=" & err.number)
+
+    err.clear : ExecuteGlobal "Const egCrossVal = 1"
+    call ok(err.number = 1041, "Const over prior Dim err=" & err.number)
+
+    err.clear : ExecuteGlobal "Const egSameParse = 1 : Dim egSameParse"
+    call ok(err.number = 1041, "same-unit Const+Dim err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egSameParse2 : Const egSameParse2 = 1"
+    call ok(err.number = 1041, "same-unit Dim+Const err=" & err.number)
+end sub
+Call TestDimOverConstErrors
+
 Class FixedClassArr
     Private mArr(2)
     Public LastErr
diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c
index d9d7084a8871..bee738d236c4 100644
--- a/dlls/vbscript/tests/run.c
+++ b/dlls/vbscript/tests/run.c
@@ -3674,6 +3674,77 @@ static void test_external_caller_method_error(void)
     CHECK_CALLED(OnScriptError);
 }
 
+static void test_class_decl_scope(void)
+{
+    static const struct {
+        const WCHAR *src;
+        BOOL expect_ok;     /* whether the script should compile */
+        USHORT error_code;  /* expected error number when it should not */
+        ULONG error_line;   /* expected 0-based error line when it should not */
+    } tests[] = {
+        { L"Dim x : Class C\nPublic v\nEnd Class\n", TRUE },
+        { L"Sub S\nClass C\nEnd Class\nEnd Sub\n", FALSE, 1002, 1 },
+        { L"If True Then\nClass C\nEnd Class\nEnd If\n", FALSE, 1002, 1 },
+        { L"Class C\nEnd Class\nDim x : Class C\nEnd Class\n", FALSE, 1041, 2 },
+    };
+    HRESULT hres;
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
+        error_line = ~0;
+        error_code = 0;
+        onerror_hres = S_OK;
+        SET_EXPECT(OnScriptError);
+        hres = parse_script_wr(tests[i].src);
+        CLEAR_CALLED(OnScriptError);
+
+        if (tests[i].expect_ok)
+            ok(hres == S_OK, "[%u] %s: hres=%08lx\n", i, wine_dbgstr_w(tests[i].src), hres);
+        else
+            ok(FAILED(hres) && error_code == tests[i].error_code && error_line == tests[i].error_line,
+               "[%u] %s: hres=%08lx code=%u line=%lu\n", i, wine_dbgstr_w(tests[i].src),
+               hres, error_code, error_line);
+    }
+}
+
+/* Like a Class, a Sub or Function is only valid at script global scope (a
+   global If/Select block hoists it, a loop or procedure body does not). Native
+   rejects the disallowed cases; the parser does not yet, so these are todo. */
+static void test_sub_decl_scope(void)
+{
+    static const struct {
+        const WCHAR *src;
+        BOOL expect_ok;     /* whether the script should compile */
+        USHORT error_code;  /* expected native error number when it should not */
+        ULONG error_line;   /* expected 0-based native error line when it should not */
+        BOOL todo;          /* the parser does not yet reject this case */
+    } tests[] = {
+        { L"If False Then\nSub S\nEnd Sub\nEnd If\nCall S\n", TRUE, 0, 0, FALSE },
+        { L"Select Case 1\nCase 1\nSub S\nEnd Sub\nEnd Select\nCall S\n", TRUE, 0, 0, FALSE },
+        { L"Sub S\nSub T\nEnd Sub\nEnd Sub\n", FALSE, 1002, 1, TRUE },
+        { L"For i = 1 To 1\nSub S\nEnd Sub\nNext\n", FALSE, 1002, 1, TRUE },
+    };
+    HRESULT hres;
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
+        error_line = ~0;
+        error_code = 0;
+        onerror_hres = S_OK;
+        SET_EXPECT(OnScriptError);
+        hres = parse_script_wr(tests[i].src);
+        CLEAR_CALLED(OnScriptError);
+
+        if (tests[i].expect_ok)
+            ok(hres == S_OK, "[%u] %s: hres=%08lx\n", i, wine_dbgstr_w(tests[i].src), hres);
+        else
+            todo_wine_if(tests[i].todo)
+            ok(FAILED(hres) && error_code == tests[i].error_code && error_line == tests[i].error_line,
+               "[%u] %s: hres=%08lx code=%u line=%lu\n", i, wine_dbgstr_w(tests[i].src),
+               hres, error_code, error_line);
+    }
+}
+
 static void test_msgbox(void)
 {
     HRESULT hres;
@@ -4370,6 +4441,8 @@ static void run_tests(void)
     test_isexpression();
     test_option_explicit_errors();
     test_parse_errors();
+    test_class_decl_scope();
+    test_sub_decl_scope();
     test_redefine_scope();
     test_getref_error_reporting();
     test_getref_external_caller_error();
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c
index 38e4ed140abe..d953c19bb304 100644
--- a/dlls/vbscript/vbdisp.c
+++ b/dlls/vbscript/vbdisp.c
@@ -2046,7 +2046,7 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, BOOL is_call, D
     }
 
     if(hres == DISP_E_EXCEPTION) {
-        clear_ei(&ctx->ei);
+        clear_error(ctx);
         ctx->ei = ei;
         hres = SCRIPT_E_RECORDED;
     }
@@ -2084,7 +2084,7 @@ HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags,
     }
 
     if(hres == DISP_E_EXCEPTION) {
-        clear_ei(&ctx->ei);
+        clear_error(ctx);
         ctx->ei = ei;
         hres = SCRIPT_E_RECORDED;
     }
diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c
index ff28b3b17ef5..43f8dd2fdd5d 100644
--- a/dlls/vbscript/vbscript.c
+++ b/dlls/vbscript/vbscript.c
@@ -140,7 +140,12 @@ HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res, BOOL
 
     for (i = 0; i < code->main_code.var_cnt; i++)
     {
-        if (script_disp_find_var(obj, code->main_code.vars[i].name))
+        dynamic_var_t *existing = script_disp_find_var(obj, code->main_code.vars[i].name);
+
+        /* A Dim may shadow a const from a previous compile unit: it creates a
+           fresh variable that name lookups resolve to from now on, while the
+           defining compile unit keeps using the inlined const value. */
+        if (existing && !existing->is_const)
             continue;
 
         if (!(var = heap_pool_alloc(&obj->heap, sizeof(*var))))
@@ -153,6 +158,8 @@ HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res, BOOL
         var->is_const = FALSE;
         var->array = NULL;
         var->index = obj->global_vars_cnt;
+        if (existing)
+            rb_remove(&obj->var_tree, &existing->entry);
         rb_put(&obj->var_tree, var->name, &var->entry);
 
         obj->global_vars[obj->global_vars_cnt++] = var;
@@ -284,7 +291,7 @@ static void release_script(script_ctx_t *ctx)
     vbscode_t *code, *code_next;
 
     collect_objects(ctx);
-    clear_ei(&ctx->ei);
+    clear_error(ctx);
 
     LIST_FOR_EACH_ENTRY_SAFE(code, code_next, &ctx->code_list, vbscode_t, entry)
     {
@@ -606,6 +613,28 @@ HRESULT report_script_error(script_ctx_t *ctx, vbscode_t *code, unsigned loc, BO
     memset(&ctx->ei, 0, sizeof(ctx->ei));
     result = error->ei.scode;
 
+    /* The description passed to the host names the offending identifier,
+       while the Err object exposes only the bare error text. */
+    if(ctx->ei_identifier) {
+        if(error->ei.bstrDescription) {
+            unsigned desc_len = SysStringLen(error->ei.bstrDescription);
+            unsigned ident_len = SysStringLen(ctx->ei_identifier);
+            BSTR new_desc = SysAllocStringLen(NULL, desc_len + 3 + ident_len + 1);
+            if(new_desc) {
+                memcpy(new_desc, error->ei.bstrDescription, desc_len * sizeof(WCHAR));
+                new_desc[desc_len] = ':';
+                new_desc[desc_len + 1] = ' ';
+                new_desc[desc_len + 2] = '\'';
+                memcpy(new_desc + desc_len + 3, ctx->ei_identifier, ident_len * sizeof(WCHAR));
+                new_desc[desc_len + 3 + ident_len] = '\'';
+                SysFreeString(error->ei.bstrDescription);
+                error->ei.bstrDescription = new_desc;
+            }
+        }
+        SysFreeString(ctx->ei_identifier);
+        ctx->ei_identifier = NULL;
+    }
+
     p = code->source;
     error->cookie = code->cookie;
     error->line = code->start_line;
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h
index 7e902badb0f5..9450851e4a4a 100644
--- a/dlls/vbscript/vbscript.h
+++ b/dlls/vbscript/vbscript.h
@@ -225,6 +225,7 @@ struct _script_ctx_t {
     unsigned call_depth;
 
     EXCEPINFO ei;
+    BSTR ei_identifier;
     vbscode_t *error_loc_code;
     unsigned error_loc_offset;
 
@@ -258,9 +259,11 @@ typedef enum {
 #define OP_LIST                                   \
     X(add,            1, 0,           0)          \
     X(and,            1, 0,           0)          \
-    X(assign_ident,   1, ARG_BSTR,    ARG_UINT)   \
-    X(assign_member,  1, ARG_BSTR,    ARG_UINT)   \
-    X(assign_call,    1, ARG_UINT,    0)          \
+    X(assign_ident,      1, ARG_BSTR,    ARG_UINT)   \
+    X(assign_local,      1, ARG_INT,     ARG_UINT)   \
+    X(assign_local_prop, 1, ARG_UINT,    ARG_UINT)   \
+    X(assign_member,     1, ARG_BSTR,    ARG_UINT)   \
+    X(assign_call,       1, ARG_UINT,    0)          \
     X(bool,           1, ARG_INT,     0)          \
     X(catch,          1, ARG_ADDR,    ARG_UINT)   \
     X(case,           0, ARG_ADDR,    ARG_UINT)   \
@@ -287,11 +290,14 @@ typedef enum {
     X(idiv,           1, 0,           0)          \
     X(imp,            1, 0,           0)          \
     X(incc,           1, ARG_BSTR,    0)          \
+    X(incc_local,     1, ARG_INT,     0)          \
     X(int,            1, ARG_INT,     0)          \
     X(is,             1, 0,           0)          \
     X(jmp,            0, ARG_ADDR,    0)          \
     X(jmp_false,      0, ARG_ADDR,    0)          \
     X(jmp_true,       0, ARG_ADDR,    0)          \
+    X(local,          1, ARG_INT,     0)          \
+    X(local_prop,     1, ARG_UINT,    0)          \
     X(lt,             1, ARG_UINT,    0)          \
     X(lteq,           1, ARG_UINT,    0)          \
     X(mcall,          1, ARG_BSTR,    ARG_UINT)   \
@@ -315,10 +321,13 @@ typedef enum {
     X(ret,            0, 0,           0)          \
     X(retval,         1, 0,           0)          \
     X(set_ident,      1, ARG_BSTR,    ARG_UINT)   \
+    X(set_local,      1, ARG_INT,     ARG_UINT)   \
+    X(set_local_prop, 1, ARG_UINT,    ARG_UINT)   \
     X(set_member,     1, ARG_BSTR,    ARG_UINT)   \
     X(set_call,       1, ARG_UINT,    0)          \
     X(stack,          1, ARG_UINT,    0)          \
     X(step,           0, ARG_ADDR,    ARG_BSTR)   \
+    X(step_local,     0, ARG_ADDR,    ARG_INT)    \
     X(stop,           1, 0,           0)          \
     X(string,         1, ARG_STR,     0)          \
     X(sub,            1, 0,           0)          \
@@ -430,6 +439,7 @@ named_item_t *lookup_named_item(script_ctx_t*,const WCHAR*,unsigned);
 void release_named_item(named_item_t*);
 void clear_error_loc(script_ctx_t*);
 void clear_ei(EXCEPINFO*);
+void clear_error(script_ctx_t*);
 HRESULT report_script_error(script_ctx_t*,vbscode_t*,unsigned,BOOL);
 void detach_global_objects(script_ctx_t*);
 HRESULT get_builtin_id(BuiltinDisp*,const WCHAR*,DISPID*);
diff --git a/dlls/vulkan-1/vulkan-1.spec b/dlls/vulkan-1/vulkan-1.spec
index 7a49ad30a7a0..14dfa1e8e927 100644
--- a/dlls/vulkan-1/vulkan-1.spec
+++ b/dlls/vulkan-1/vulkan-1.spec
@@ -4,7 +4,6 @@
 # by the following copyright and permission notice:
 #
 # Copyright 2015-2026 The Khronos Group Inc.
-#
 # SPDX-License-Identifier: Apache-2.0 OR MIT
 #
 # and from Vulkan video.xml file covered
diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c
index 8e83707c3cfd..f814dbdea5a6 100644
--- a/dlls/webservices/tests/writer.c
+++ b/dlls/webservices/tests/writer.c
@@ -2222,14 +2222,13 @@ static void test_text_types(void)
 
 static void test_double(void)
 {
-    static const BOOL is_win64 = sizeof(void*) > sizeof(int);
     WS_XML_STRING localname = {1, (BYTE *)"t"}, ns = {0, NULL};
     unsigned int fpword, fpword_orig;
     static const struct
     {
         double      val;
         const char *result;
-        BOOL        todo64;
+        BOOL        todo;
     }
     tests[] =
     {
@@ -2241,11 +2240,15 @@ static void test_double(void)
         {1.0000000000000003, "<t>1.0000000000000002</t>"},
         {1.0000000000000004, "<t>1.0000000000000004</t>"},
         {100000000000000, "<t>100000000000000</t>"},
+        {-100000000000000, "<t>-100000000000000</t>", TRUE},
         {1000000000000000, "<t>1E+15</t>"},
+        {-1000000000000000, "<t>-1E+15</t>"},
         {0.1, "<t>0.1</t>", TRUE},
-        {0.01, "<t>1E-2</t>", TRUE},
         {-0.1, "<t>-0.1</t>", TRUE},
+        {0.01, "<t>1E-2</t>", TRUE},
         {-0.01, "<t>-1E-2</t>", TRUE},
+        {9.9, "<t>9.9</t>", TRUE},
+        {-9.9, "<t>-9.9</t>", TRUE},
         {1.7976931348623158e308, "<t>1.7976931348623157E+308</t>", TRUE},
         {-1.7976931348623158e308, "<t>-1.7976931348623157E+308</t>", TRUE},
     };
@@ -2260,6 +2263,7 @@ static void test_double(void)
     text.text.textType = WS_XML_TEXT_TYPE_DOUBLE;
     for (i = 0; i < ARRAY_SIZE( tests ); i++)
     {
+        winetest_push_context("i=%ld expected=%s", i, tests[i].result);
         hr = set_output( writer );
         ok( hr == S_OK, "got %#lx\n", hr );
         hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
@@ -2271,7 +2275,7 @@ static void test_double(void)
 
         hr = WsWriteEndElement( writer, NULL );
         ok( hr == S_OK, "%lu: got %#lx\n", i, hr );
-        if (tests[i].todo64 && is_win64)
+        if (tests[i].todo)
         {
             WS_BYTES bytes;
             ULONG size = sizeof(bytes);
@@ -2281,12 +2285,13 @@ static void test_double(void)
             memset( &bytes, 0, sizeof(bytes) );
             hr = WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &bytes, size, NULL );
             ok( hr == S_OK, "%lu: got %#lx\n", i, hr );
-            todo_wine
+            todo_wine_if( bytes.length != len || memcmp( bytes.bytes, tests[i].result, len ) )
             ok( bytes.length == len && !memcmp( bytes.bytes, tests[i].result, len ),
                 "%lu: got %lu %s expected %d %s\n", i, bytes.length,
                 debugstr_bytes(bytes.bytes, bytes.length), len, tests[i].result );
         }
         else check_output( writer, tests[i].result, __LINE__ );
+        winetest_pop_context();
     }
 
     hr = set_output( writer );
diff --git a/dlls/wevtapi/Makefile.in b/dlls/wevtapi/Makefile.in
index ade3a98d7c04..7214a203056f 100644
--- a/dlls/wevtapi/Makefile.in
+++ b/dlls/wevtapi/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = wevtapi.dll
 IMPORTLIB = wevtapi
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c
index 708e0f04f0cf..24f7e64b46b9 100644
--- a/dlls/win32u/class.c
+++ b/dlls/win32u/class.c
@@ -46,7 +46,6 @@ SYSTEM_BASIC_INFORMATION system_info;
 typedef struct tagCLASS
 {
     struct list  entry;         /* Entry in class list */
-    BOOL         local;         /* Local class? */
     struct dce  *dce;           /* Opaque pointer to class DCE */
     HICON        icon_internal; /* internal small icon, derived from hIcon */
     const shared_object_t *shared; /* class object in session shared memory */
@@ -69,7 +68,7 @@ static struct builtin_class_descr builtin_classes[] =
     {
         .name = "ScrollBar",
         .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
-        .extra = sizeof(struct scroll_bar_win_data),
+        .extra = sizeof(struct scroll_info *),
         .cursor = IDC_ARROW,
     },
     [NTUSER_WNDPROC_MESSAGE] =
@@ -203,18 +202,12 @@ static WINDOWPROC *find_winproc( WNDPROC func, BOOL ansi )
 static WINDOWPROC *get_winproc_ptr( WNDPROC handle )
 {
     UINT index = LOWORD(handle);
-    if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return NULL;
+    if (handle != MAKE_WNDPROC(index)) return NULL;
     if (index >= MAX_WINPROCS) return WINPROC_PROC16;
     if (index >= winproc_used) return NULL;
     return &winproc_array[index];
 }
 
-/* create a handle for a given window proc */
-static inline WNDPROC proc_to_handle( WINDOWPROC *proc )
-{
-    return (WNDPROC)(ULONG_PTR)((proc - winproc_array) | (WINPROC_HANDLE << 16));
-}
-
 /* allocate and initialize a new winproc */
 static inline WINDOWPROC *alloc_winproc_ptr( WNDPROC func, BOOL ansi )
 {
@@ -234,13 +227,12 @@ static inline WINDOWPROC *alloc_winproc_ptr( WNDPROC func, BOOL ansi )
             proc = &winproc_array[winproc_used++];
             if (ansi) proc->procA = func;
             else proc->procW = func;
-            TRACE_(win)( "allocated %p for %c %p (%d/%d used)\n",
-                         proc_to_handle(proc), ansi ? 'A' : 'W', func,
-                         winproc_used, MAX_WINPROCS );
+            TRACE_(win)( "allocated %p for %c %p (%d/%d used)\n", MAKE_WNDPROC(proc - winproc_array),
+                         ansi ? 'A' : 'W', func, winproc_used, MAX_WINPROCS );
         }
         else WARN_(win)( "too many winprocs, cannot allocate one for %p\n", func );
     }
-    else TRACE_(win)( "reusing %p for %p\n", proc_to_handle(proc), func );
+    else TRACE_(win)( "reusing %p for %p\n", MAKE_WNDPROC(proc - winproc_array), func );
 
     pthread_mutex_unlock( &winproc_lock );
     return proc;
@@ -261,7 +253,7 @@ WNDPROC alloc_winproc( WNDPROC func, BOOL ansi )
 
     if (!(proc = alloc_winproc_ptr( func, ansi ))) return func;
     if (proc == WINPROC_PROC16) return func;
-    return proc_to_handle( proc );
+    return MAKE_WNDPROC(proc - winproc_array);
 }
 
 /* Get a window procedure pointer that can be passed to the Windows program. */
@@ -282,17 +274,6 @@ WNDPROC get_winproc( WNDPROC proc, BOOL ansi )
     }
 }
 
-/* Return the window procedure type, or the default value if not a winproc handle. */
-BOOL is_winproc_unicode( WNDPROC proc, BOOL def_val )
-{
-    WINDOWPROC *ptr = get_winproc_ptr( proc );
-
-    if (!ptr) return def_val;
-    if (ptr == WINPROC_PROC16) return FALSE;  /* 16-bit is always A */
-    if (ptr->procA && ptr->procW) return def_val;  /* can be both */
-    return ptr->procW != NULL;
-}
-
 void get_winproc_params( struct win_proc_params *params, BOOL fixup_ansi_dst )
 {
     WINDOWPROC *proc = get_winproc_ptr( params->func );
@@ -538,6 +519,12 @@ static BOOL class_name_matches( CLASS *class, UNICODE_STRING *name )
     return name->Length == len && !wcsnicmp( class_name, name->Buffer, len / sizeof(WCHAR) );
 }
 
+static BOOL is_local_class( CLASS *class )
+{
+    /* class local flag is safe to read without shared object locking as it is constant */
+    return class->shared->shm.class.local;
+}
+
 static UINT_PTR get_class_instance( CLASS *class )
 {
     struct object_lock lock = OBJECT_LOCK_INIT;
@@ -563,7 +550,7 @@ static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name )
         UINT_PTR class_instance = get_class_instance( class );
         if (!class_name_matches( class, name )) continue;
         is_win16 = !(class_instance >> 16);
-        if (!instance || !class->local || class_instance == instance ||
+        if (!instance || !is_local_class( class ) || class_instance == instance ||
             (!is_win16 && ((class_instance & ~0xffff) == (instance & ~0xffff))))
         {
             TRACE( "%s %lx -> %p\n", debugstr_us(name), instance, class );
@@ -574,22 +561,6 @@ static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name )
     return NULL;
 }
 
-/***********************************************************************
- *           get_class_winproc
- */
-WNDPROC get_class_winproc( CLASS *class )
-{
-    struct object_lock lock = OBJECT_LOCK_INIT;
-    const class_shm_t *class_shm;
-    WNDPROC wndproc = NULL;
-    NTSTATUS status;
-
-    while ((status = get_shared_class( class, &lock, &class_shm )) == STATUS_PENDING)
-        wndproc = wine_server_get_ptr( class_shm->info.wndproc );
-    if (status) return 0;
-    return wndproc;
-}
-
 /***********************************************************************
  *           get_class_dce
  */
@@ -614,7 +585,7 @@ struct dce *set_class_dce( CLASS *class, struct dce *dce )
 ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *name, UNICODE_STRING *version,
                                       struct client_menu_name *menu_name, DWORD fnid, DWORD flags, DWORD *wow )
 {
-    const BOOL is_builtin = fnid, ansi = flags;
+    const BOOL ansi = flags;
     const shared_object_t *shared;
     struct obj_locator locator;
     HICON icon_internal;
@@ -625,10 +596,10 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
     BOOL ret;
 
     /* create the desktop window to trigger builtin class registration */
-    if (!is_builtin) get_desktop_window();
+    if (!fnid) get_desktop_window();
 
     if (wc->cbSize != sizeof(*wc) || wc->cbClsExtra < 0 || wc->cbWndExtra < 0 ||
-        (!is_builtin && wc->hInstance == user32_module))  /* we can't register a class for user32 */
+        (!fnid && wc->hInstance == user32_module))  /* we can't register a class for user32 */
     {
          RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
          return 0;
@@ -648,8 +619,6 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
 
     if (!(class = calloc( 1, sizeof(*class) ))) return 0;
 
-    class->local      = !is_builtin && !(wc->style & CS_GLOBALCLASS);
-
     /* Other non-null values must be set by caller */
     icon_internal = wc->hIconSm ? 0 : create_small_icon( wc->hIcon );
 
@@ -670,9 +639,10 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
             .menu_name  = wine_server_client_ptr( menu_name ),
         };
         wine_server_add_data( req, &info, sizeof(info) );
-        req->local      = class->local;
         req->client_ptr = wine_server_client_ptr( class );
         req->atom       = wine_server_add_atom( req, name );
+        req->fnid       = fnid;
+        req->ansi       = ansi;
         req->name_offset = version->Length / sizeof(WCHAR);
         ret = !wine_server_call_err( req );
         locator = reply->locator;
@@ -695,15 +665,16 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
         goto failed;
     }
 
-    if (class->local) list_add_head( &class_list, &class->entry );
-    else list_add_tail( &class_list, &class->entry );
-
     TRACE( "name=%s->%s atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d class=%p\n",
            debugstr_w(wc->lpszClassName), debugstr_us(name), atom, wc->lpfnWndProc, instance,
            wc->hbrBackground, wc->style, wc->cbClsExtra, wc->cbWndExtra, class );
 
     class->icon_internal = icon_internal;
     class->shared        = shared;
+
+    if (is_local_class( class )) list_add_head( &class_list, &class->entry );
+    else list_add_tail( &class_list, &class->entry );
+
     release_class_ptr( class );
     return atom;
 
@@ -921,7 +892,7 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name )
 }
 
 /* Set class info with the wine server. */
-static BOOL set_server_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size, ULONG_PTR *oldval )
+static BOOL server_set_class_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size, ULONG_PTR *oldval, BOOL ansi )
 {
     BOOL ret;
 
@@ -931,6 +902,7 @@ static BOOL set_server_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size,
         req->offset = offset;
         req->size = size;
         req->new_info = newval;
+        req->ansi = ansi;
         ret = !wine_server_call_err( req );
         *oldval = reply->old_info;
     }
@@ -955,18 +927,18 @@ static ULONG_PTR set_class_long_size( HWND hwnd, INT offset, LONG_PTR newval, UI
     case GCLP_HICONSM:
     case GCLP_HMODULE:
     case GCLP_MENUNAME:
-        set_server_info( hwnd, offset, newval, size, &retval );
+        server_set_class_info( hwnd, offset, newval, size, &retval, ansi );
         break;
     case GCLP_WNDPROC:
         newval = (ULONG_PTR)alloc_winproc( (WNDPROC)newval, ansi );
-        if (!set_server_info( hwnd, offset, newval, size, &retval )) break;
+        if (!server_set_class_info( hwnd, offset, newval, size, &retval, ansi )) break;
         retval = (ULONG_PTR)get_winproc( (WNDPROC)retval, ansi );
         break;
     case GCL_CBCLSEXTRA:  /* cannot change this one */
         RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
         break;
     default:
-        if (offset >= 0) set_server_info( hwnd, offset, newval, size, &retval );
+        if (offset >= 0) server_set_class_info( hwnd, offset, newval, size, &retval, ansi );
         else RtlSetLastWin32Error( ERROR_INVALID_INDEX );
         break;
     }
@@ -1106,9 +1078,10 @@ static void register_builtin( enum ntuser_client_procs proc )
         .style = descr->style,
         .cbWndExtra = descr->extra,
         .hbrBackground = descr->brush,
-        .lpfnWndProc = BUILTIN_WINPROC( proc ),
+        .lpfnWndProc = MAKE_WNDPROC( proc ),
     };
 
+    if (descr->atom) return; /* already registered */
     if (descr->cursor)
         class.hCursor = LoadImageW( 0, (const WCHAR *)descr->cursor, IMAGE_CURSOR,
                                     0, 0, LR_SHARED | LR_DEFAULTSIZE );
@@ -1116,7 +1089,7 @@ static void register_builtin( enum ntuser_client_procs proc )
     asciiz_to_unicode( nameW, descr->name );
     RtlInitUnicodeString( &name, nameW );
 
-    if ((descr->atom = NtUserRegisterClassExWOW( &class, &name, &version, NULL, 1, 0, NULL ))) return;
+    if ((descr->atom = NtUserRegisterClassExWOW( &class, &name, &version, NULL, MAKE_FNID(proc), 0, NULL ))) return;
     if (class.hCursor) NtUserDestroyCursor( class.hCursor, 0 );
 }
 
diff --git a/dlls/win32u/clipboard.c b/dlls/win32u/clipboard.c
index 4667d72e6efa..ec48cd093818 100644
--- a/dlls/win32u/clipboard.c
+++ b/dlls/win32u/clipboard.c
@@ -37,6 +37,9 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
 
+#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
+#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
+
 static pthread_mutex_t clipboard_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 struct cached_format
@@ -755,7 +758,7 @@ LRESULT drag_drop_call( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, void
         return KeUserModeCallback( NtUserDragDropLeave, 0, 0, &ret_ptr, &ret_len );
     case WINE_DRAG_DROP_DRAG:
     {
-        RECT rect = {LOWORD(wparam), HIWORD(wparam), LOWORD(wparam), HIWORD(wparam)};
+        RECT rect = {GET_X_LPARAM(wparam), GET_Y_LPARAM(wparam), GET_X_LPARAM(wparam), GET_Y_LPARAM(wparam)};
         struct drag_drop_drag_params params =
         {
             .hwnd = hwnd,
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c
index 38624583f7dd..1b33976f333e 100644
--- a/dlls/win32u/d3dkmt.c
+++ b/dlls/win32u/d3dkmt.c
@@ -491,7 +491,7 @@ static void d3dkmt_init_vulkan(void)
     static const struct vulkan_instance_extensions extensions =
     {
         .has_VK_KHR_get_physical_device_properties2 = 1,
-        .has_VK_KHR_external_memory_capabilities = 1,
+        .has_VK_KHR_external_fence_capabilities = 1,
     };
 
     d3dkmt_vulkan_instance = vulkan_instance_create( &extensions );
diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c
index cf641c4ec2bf..ad27cb143ae2 100644
--- a/dlls/win32u/dce.c
+++ b/dlls/win32u/dce.c
@@ -416,20 +416,23 @@ static BOOL set_surface_shape( struct window_surface *surface, const RECT *rect,
     char shape_buf[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
     BITMAPINFO *shape_info = (BITMAPINFO *)shape_buf;
     COLORREF color_key = surface->color_key;
-    void *shape_bits, *old_shape;
+    void *shape_bits, *old_shape = NULL;
     RECT *shape_rect, tmp_rect;
     WINEREGION *data;
-    BOOL ret;
+    BOOL ret, is_new;
 
     width = color_info->bmiHeader.biWidth;
     height = abs( color_info->bmiHeader.biHeight );
     assert( !(width & 7) ); /* expect 1bpp bitmap to be aligned on bytes */
 
-    if (!surface->shape_bitmap) surface->shape_bitmap = NtGdiCreateBitmap( width, height, 1, 1, NULL );
+    if ((is_new = !surface->shape_bitmap)) surface->shape_bitmap = NtGdiCreateBitmap( width, height, 1, 1, NULL );
     if (!(shape_bits = window_surface_get_shape( surface, shape_info ))) return FALSE;
 
-    old_shape = malloc( shape_info->bmiHeader.biSizeImage );
-    memcpy( old_shape, shape_bits, shape_info->bmiHeader.biSizeImage );
+    if (!is_new)
+    {
+        old_shape = malloc( shape_info->bmiHeader.biSizeImage );
+        memcpy( old_shape, shape_bits, shape_info->bmiHeader.biSizeImage );
+    }
 
     color_stride = color_info->bmiHeader.biSizeImage / height;
     shape_stride = shape_info->bmiHeader.biSizeImage / abs( shape_info->bmiHeader.biHeight );
@@ -499,7 +502,7 @@ static BOOL set_surface_shape( struct window_surface *surface, const RECT *rect,
     }
     }
 
-    ret = memcmp( old_shape, shape_bits, shape_info->bmiHeader.biSizeImage );
+    ret = is_new || memcmp( old_shape, shape_bits, shape_info->bmiHeader.biSizeImage );
     free( old_shape );
     return ret;
 }
diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c
index a660b166a0fa..832043abaa34 100644
--- a/dlls/win32u/freetype.c
+++ b/dlls/win32u/freetype.c
@@ -3149,6 +3149,14 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT
         TEXTMETRICW *ptm = &base_font->otm.otmTextMetrics;
         INT top = min( metrics.horiBearingY, ptm->tmAscent << 6 );
         INT bottom = max( metrics.horiBearingY - metrics.height, -(ptm->tmDescent << 6) );
+
+        if (top < bottom)
+        {
+            INT t = top;
+            top = bottom;
+            bottom = t;
+        }
+
         metrics.horiBearingY = top;
         metrics.height = top - bottom;
 
diff --git a/dlls/win32u/menu.c b/dlls/win32u/menu.c
index dda7d119ecc2..a700f44af78b 100644
--- a/dlls/win32u/menu.c
+++ b/dlls/win32u/menu.c
@@ -160,6 +160,16 @@ static BOOL exit_menu = FALSE;
 static SIZE menucharsize;
 static UINT od_item_height; /* default owner drawn item height */
 
+static HMENU get_control_menu( HWND hwnd )
+{
+    return (HMENU)NtUserGetPrivateData( hwnd, 0, sizeof(HMENU) );
+}
+
+static HMENU set_control_menu( HWND hwnd, HMENU menu )
+{
+    return (HMENU)NtUserSetPrivateData( hwnd, 0, sizeof(HMENU), (LONG_PTR)menu );
+}
+
 /**********************************************************************
  *           NtUserCopyAcceleratorTable   (win32u.@)
  */
@@ -2909,7 +2919,7 @@ LRESULT popup_menu_window_proc( HWND hwnd, UINT message, WPARAM wparam, LPARAM l
     case WM_CREATE:
         {
             CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
-            NtUserSetWindowLongPtr( hwnd, 0, (LONG_PTR)cs->lpCreateParams, FALSE );
+            set_control_menu( hwnd, cs->lpCreateParams );
             return 0;
         }
 
@@ -2920,14 +2930,14 @@ LRESULT popup_menu_window_proc( HWND hwnd, UINT message, WPARAM wparam, LPARAM l
         {
             PAINTSTRUCT ps;
             NtUserBeginPaint( hwnd, &ps );
-            draw_popup_menu( hwnd, ps.hdc, (HMENU)get_window_long_ptr( hwnd, 0, FALSE ));
+            draw_popup_menu( hwnd, ps.hdc, get_control_menu( hwnd ) );
             NtUserEndPaint( hwnd, &ps );
             return 0;
         }
 
     case WM_PRINTCLIENT:
         {
-            draw_popup_menu( hwnd, (HDC)wparam, (HMENU)get_window_long_ptr( hwnd, 0, FALSE ));
+            draw_popup_menu( hwnd, (HDC)wparam, get_control_menu( hwnd ) );
             return 0;
         }
 
@@ -2944,17 +2954,16 @@ LRESULT popup_menu_window_proc( HWND hwnd, UINT message, WPARAM wparam, LPARAM l
         break;
 
     case WM_SHOWWINDOW:
-        if (wparam)
-        {
-            if (!get_window_long_ptr( hwnd, 0, FALSE )) ERR( "no menu to display\n" );
-        }
-        else
-            NtUserSetWindowLongPtr( hwnd, 0, 0, FALSE );
+        if (!wparam) NtUserSetPrivateData( hwnd, 0, sizeof(LONG_PTR), 0 );
+        else if (!get_control_menu( hwnd )) ERR( "no menu to display\n" );
         break;
 
     case MN_GETHMENU:
-        return get_window_long_ptr( hwnd, 0, FALSE );
+        return (LRESULT)get_control_menu( hwnd );
 
+    case WM_NCCREATE:
+        NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_MENU) );
+        /* fallthrough */
     default:
         return default_window_proc( hwnd, message, wparam, lparam, ansi );
     }
@@ -4599,7 +4608,7 @@ BOOL WINAPI NtUserGetMenuBarInfo( HWND hwnd, LONG id, LONG item, MENUBARINFO *in
             return FALSE;
         }
 
-        hmenu = (HMENU)get_window_long_ptr( hwnd, 0, FALSE );
+        hmenu = get_control_menu( hwnd );
         break;
     case OBJID_MENU:
         hmenu = get_menu( hwnd );
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c
index 1428ff586f4f..b6cdd3efa0ea 100644
--- a/dlls/win32u/message.c
+++ b/dlls/win32u/message.c
@@ -434,7 +434,7 @@ static BOOL init_win_proc_params( struct win_proc_params *params, HWND hwnd, UIN
 static BOOL init_window_call_params( struct win_proc_params *params, HWND hwnd, UINT msg, WPARAM wParam,
                                      LPARAM lParam, BOOL ansi, enum wm_char_mapping mapping )
 {
-    BOOL is_dialog;
+    BOOL is_dialog, is_ansi;
     WND *win;
 
     user_check_not_lock();
@@ -442,8 +442,8 @@ static BOOL init_window_call_params( struct win_proc_params *params, HWND hwnd,
     if (!is_current_thread_window( hwnd )) return FALSE;
     if (!(win = get_win_ptr( hwnd ))) return FALSE;
     if (win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
-    params->func = win->winproc;
-    params->ansi_dst = !(win->flags & WIN_ISUNICODE);
+    params->func = get_window_wndproc_handle( hwnd, &is_ansi );
+    params->ansi_dst = is_ansi;
     is_dialog = win->dlgInfo != NULL;
     release_win_ptr( win );
 
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index 1929a7e2cd60..978700a37aaa 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -47,8 +47,6 @@ typedef struct tagWND
     HWND               owner;         /* Window owner */
     struct tagCLASS   *class;         /* Window class */
     struct dce        *dce;           /* DCE pointer */
-    WNDPROC            winproc;       /* Window procedure */
-    HINSTANCE          hInstance;     /* Window hInstance (from CreateWindow) */
     struct window_rects rects;        /* window rects in window DPI, relative to the parent client area */
     RECT               normal_rect;   /* Normal window rect saved when maximized/minimized */
     RECT               present_rect;  /* present rect for exclusive fullscreen mode */
@@ -58,7 +56,6 @@ typedef struct tagWND
     struct win_scroll_bar_info *pScroll; /* Scroll-bar info */
     UINT               dwStyle;       /* Window style (from CreateWindow) */
     UINT               dwExStyle;     /* Extended style (from CreateWindowEx) */
-    UINT_PTR           wIDmenu;       /* ID or hmenu (from CreateWindow) */
     UINT               helpContext;   /* Help context ID */
     UINT               flags;         /* Misc. flags (see below) */
     HMENU              hSysMenu;      /* window's copy of System Menu */
@@ -74,9 +71,6 @@ typedef struct tagWND
     int                swap_interval; /* OpenGL surface swap interval */
     int                pixel_format;  /* Pixel format set by the graphics driver */
     int                clip_clients;  /* Has client surfaces that needs to be clipped out */
-    unsigned int       cbWndExtra;    /* class cbWndExtra at window creation */
-    DWORD_PTR          userdata;      /* User private data */
-    DWORD              wExtra[1];     /* Window extra bytes */
 } WND;
 
 /* WND flags values */
@@ -84,7 +78,6 @@ typedef struct tagWND
 #define WIN_NEED_SIZE             0x0002 /* Internal WM_SIZE is needed */
 #define WIN_NCACTIVATED           0x0004 /* last WM_NCACTIVATE was positive */
 #define WIN_ISMDICLIENT           0x0008 /* Window is an MDIClient */
-#define WIN_ISUNICODE             0x0010 /* Window is Unicode */
 #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */
 #define WIN_CHILDREN_MOVED        0x0040 /* children may have moved, ignore stored positions */
 #define WIN_HAS_IME_WIN           0x0080 /* the window has been registered with imm32 */
@@ -157,14 +150,8 @@ struct scroll_info
     BOOL  painted;  /* Whether the scroll bar is painted by DefWinProc() */
 };
 
-struct scroll_bar_win_data
-{
-    DWORD magic;
-    struct scroll_info info;
-};
-
-#define WINPROC_HANDLE (~0u >> 16)
-#define BUILTIN_WINPROC(index) ((WNDPROC)(ULONG_PTR)((index) | (WINPROC_HANDLE << 16)))
+/* also defined in server/class.c */
+#define MAKE_WNDPROC(index)     ((WNDPROC)(UINT_PTR)(UINT)MAKELONG(index, 0xffff))
 
 #define MAX_ATOM_LEN 255
 
@@ -184,9 +171,7 @@ extern void spy_exit_message( INT flag, HWND hwnd, UINT msg,
 /* class.c */
 extern HINSTANCE user32_module;
 WNDPROC alloc_winproc( WNDPROC func, BOOL ansi );
-BOOL is_winproc_unicode( WNDPROC proc, BOOL def_val );
 DWORD get_class_long( HWND hwnd, INT offset, BOOL ansi );
-WNDPROC get_class_winproc( struct tagCLASS *class );
 ULONG_PTR get_class_long_ptr( HWND hwnd, INT offset, BOOL ansi );
 WORD get_class_word( HWND hwnd, INT offset );
 DLGPROC get_dialog_proc( DLGPROC proc, BOOL ansi );
@@ -247,5 +232,6 @@ struct obj_locator get_window_class_locator( HWND hwnd );
 WND *get_win_ptr( HWND hwnd );
 BOOL is_child( HWND parent, HWND child );
 BOOL is_window( HWND hwnd );
+extern WNDPROC get_window_wndproc_handle( HWND hwnd, BOOL *ansi );
 
 #endif /* __WINE_NTUSER_PRIVATE_H */
diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c
index 052a571194f3..00450c20709e 100644
--- a/dlls/win32u/scroll.c
+++ b/dlls/win32u/scroll.c
@@ -29,8 +29,15 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(scroll);
 
+static struct scroll_info *get_control_state( HWND hwnd )
+{
+    return (struct scroll_info *)NtUserGetPrivateData( hwnd, 0, sizeof(struct scroll_info *) );
+}
 
-#define SCROLLBAR_MAGIC 0x5c6011ba
+static struct scroll_info *set_control_state( HWND hwnd, struct scroll_info *state )
+{
+    return (struct scroll_info *)NtUserSetPrivateData( hwnd, 0, sizeof(struct scroll_info *), (LONG_PTR)state );
+}
 
 /* Minimum size of the rectangle between the arrows */
 #define SCROLL_MIN_RECT  4
@@ -72,8 +79,6 @@ struct win_scroll_bar_info
     struct scroll_info vert;
 };
 
-#define SCROLLBAR_MAGIC 0x5c6011ba
-
 
 static struct scroll_info *get_scroll_info_ptr( HWND hwnd, int bar, BOOL alloc )
 {
@@ -91,12 +96,7 @@ static struct scroll_info *get_scroll_info_ptr( HWND hwnd, int bar, BOOL alloc )
         if (win->pScroll) info = &win->pScroll->vert;
         break;
     case SB_CTL:
-        if (win->cbWndExtra >= sizeof(struct scroll_bar_win_data))
-        {
-            struct scroll_bar_win_data *data = (struct scroll_bar_win_data *)win->wExtra;
-            if (data->magic == SCROLLBAR_MAGIC) info = &data->info;
-        }
-        if (!info) WARN( "window is not a scrollbar control\n" );
+        if (!(info = get_control_state( hwnd ))) WARN( "window is not a scrollbar control\n" );
         break;
     case SB_BOTH:
         WARN( "with SB_BOTH\n" );
@@ -1130,21 +1130,13 @@ static BOOL get_scroll_bar_info( HWND hwnd, LONG id, SCROLLBARINFO *info )
 
 static void create_scroll_bar( HWND hwnd, CREATESTRUCTW *create )
 {
-    struct scroll_info *info = NULL;
-    WND *win;
+    struct scroll_info *info;
 
     TRACE( "hwnd=%p create=%p\n", hwnd, create );
 
-    win = get_win_ptr( hwnd );
-    if (win->cbWndExtra >= sizeof(struct scroll_bar_win_data))
-    {
-        struct scroll_bar_win_data *data = (struct scroll_bar_win_data *)win->wExtra;
-        data->magic = SCROLLBAR_MAGIC;
-        info = &data->info;
-    }
-    else WARN( "Not enough extra data\n" );
-    release_win_ptr( win );
-    if (!info) return;
+    if (!(info = calloc( 1, sizeof(*info) ))) return;
+    NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_SCROLLBAR) );
+    set_control_state( hwnd, info );
 
     if (create->style & WS_DISABLED)
     {
diff --git a/dlls/win32u/spy.c b/dlls/win32u/spy.c
index 67c6b1269224..c86d79442572 100644
--- a/dlls/win32u/spy.c
+++ b/dlls/win32u/spy.c
@@ -1127,7 +1127,7 @@ static const char * const CCMMessageTypeNames[SPY_MAX_CCMMSGNUM + 1] =
     "CCM_SETNOTIFYWINDOW"
 };
 
-#define SPY_MAX_WINEMSGNUM   (WM_WINE_UPDATEWINDOWSTATE - WM_WINE_DESTROYWINDOW)
+#define SPY_MAX_WINEMSGNUM   (WM_WINE_SETPIXELFORMAT - WM_WINE_DESTROYWINDOW)
 static const char * const WINEMessageTypeNames[SPY_MAX_WINEMSGNUM + 1] =
 {
     "WM_WINE_DESTROYWINDOW",
@@ -1142,6 +1142,15 @@ static const char * const WINEMessageTypeNames[SPY_MAX_WINEMSGNUM + 1] =
     "WM_WINE_IME_NOTIFY",
     "WM_WINE_WINDOW_STATE_CHANGED",
     "WM_WINE_UPDATEWINDOWSTATE",
+    "WM_WINE_TRACKMOUSEEVENT",
+    "WM_WINE_SETPIXELFORMAT",
+};
+
+#define SPY_MAX_WINE_DRIVER_MSGNUM (WM_WINE_SETCURSOR - WM_WINE_CLIPCURSOR)
+static const char * const wine_driver_message_type_names[SPY_MAX_WINE_DRIVER_MSGNUM + 1] =
+{
+    "WM_WINE_CLIPCURSOR",
+    "WM_WINE_SETCURSOR",
 };
 
 /* Virtual key names */
@@ -2072,6 +2081,9 @@ static const char *SPY_GetMsgInternal( UINT msg )
     if (msg >= WM_WINE_DESTROYWINDOW && msg <= WM_WINE_DESTROYWINDOW + SPY_MAX_WINEMSGNUM)
         return WINEMessageTypeNames[msg-WM_WINE_DESTROYWINDOW];
 
+    if (msg >= WM_WINE_CLIPCURSOR && msg <= WM_WINE_CLIPCURSOR + SPY_MAX_WINE_DRIVER_MSGNUM)
+        return wine_driver_message_type_names[msg-WM_WINE_CLIPCURSOR];
+
     return NULL;
 }
 
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index 4b53686dba13..c3a32dde6cf2 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -185,6 +185,25 @@ struct obj_locator get_window_class_locator( HWND hwnd )
     return locator;
 }
 
+/***********************************************************************
+ *           get_window_wndproc_handle
+ */
+WNDPROC get_window_wndproc_handle( HWND hwnd, BOOL *ansi )
+{
+    struct object_lock lock = OBJECT_LOCK_INIT;
+    const window_shm_t *window_shm = NULL;
+    WNDPROC wndproc = NULL;
+    NTSTATUS status;
+
+    while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING)
+    {
+        wndproc = wine_server_get_ptr( window_shm->info.wndproc );
+        *ansi = window_shm->ansi;
+    }
+    if (status) return 0;
+    return wndproc;
+}
+
 /***********************************************************************
  *           get_user_handle_ptr
  */
@@ -980,28 +999,9 @@ BOOL is_window_drawable( HWND hwnd, BOOL icon )
 /* see IsWindowUnicode */
 BOOL is_window_unicode( HWND hwnd )
 {
-    WND *win;
-    BOOL ret = FALSE;
-
-    if (!(win = get_win_ptr(hwnd))) return FALSE;
-
-    if (win == WND_DESKTOP) return TRUE;
-
-    if (win != WND_OTHER_PROCESS)
-    {
-        ret = (win->flags & WIN_ISUNICODE) != 0;
-        release_win_ptr( win );
-    }
-    else
-    {
-        SERVER_START_REQ( get_window_info )
-        {
-            req->handle = wine_server_user_handle( hwnd );
-            if (!wine_server_call_err( req )) ret = reply->is_unicode;
-        }
-        SERVER_END_REQ;
-    }
-    return ret;
+    BOOL ansi = FALSE;
+    if (!get_window_wndproc_handle( hwnd, &ansi )) return FALSE;
+    return !ansi;
 }
 
 /*****************************************************************
@@ -1113,42 +1113,60 @@ static HWND get_last_active_popup( HWND hwnd )
     return retval;
 }
 
-static BOOL get_window_extra( HWND hwnd, WND *win, UINT offset, UINT size, LONG_PTR *ret, BOOL internal )
+static WNDPROC get_window_proc( WNDPROC wndproc, BOOL wndproc_ansi, BOOL ansi )
+{
+    /* This looks like a hack only for the edit control (see tests). This makes these controls
+     * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
+     * that the hack is in GetWindowLongPtr[AW], not in winprocs.
+     */
+    if (wndproc == MAKE_WNDPROC(NTUSER_WNDPROC_EDIT) && wndproc_ansi != ansi) return wndproc;
+    return get_winproc( wndproc, ansi );
+}
+
+static LONG_PTR get_window_long_shm( HWND hwnd, UINT offset, UINT size, BOOL ansi, BOOL internal )
 {
     struct object_lock lock = OBJECT_LOCK_INIT;
     const window_shm_t *window_shm = NULL;
-    BOOL valid = FALSE;
-    UINT status;
+    BOOL valid = TRUE, window_ansi = FALSE;
+    LONG_PTR ret = 0;
+    NTSTATUS status;
 
-    while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING)
+    if (offset == GWLP_WNDPROC && !is_current_process_window( hwnd ))
     {
-        valid = size <= win->cbWndExtra && offset <= win->cbWndExtra - size &&
-                (internal || offset >= window_shm->private_size);
-        if (valid) memcpy( ret, (char *)win->wExtra + offset, size );
+        RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
+        return 0;
     }
 
-    if (status) RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
-    else if (!valid) RtlSetLastWin32Error( ERROR_INVALID_INDEX );
-    return valid;
-}
-
-/* helper for set_window_long */
-static inline void set_win_data( void *ptr, LONG_PTR val, UINT size )
-{
-    if (size == sizeof(WORD))
+    while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING)
     {
-        WORD newval = val;
-        memcpy( ptr, &newval, sizeof(newval) );
+        switch (offset)
+        {
+        case GWLP_ID:        ret = window_shm->info.id; break;
+        case GWLP_HINSTANCE: ret = window_shm->info.instance; break;
+        case GWLP_USERDATA:  memcpy( &ret, (void *)&window_shm->info.user_data, size ); break;
+        case GWLP_WNDPROC:   ret = window_shm->info.wndproc; break;
+        default:
+            valid = size <= window_shm->extra_size && offset <= window_shm->extra_size - size &&
+                    (internal || offset >= window_shm->private_size);
+            if (valid) memcpy( &ret, (char *)window_shm->extra + offset, size );
+            break;
+        }
+        window_ansi = window_shm->ansi;
     }
-    else if (size == sizeof(DWORD))
+    if (status)
     {
-        DWORD newval = val;
-        memcpy( ptr, &newval, sizeof(newval) );
+        RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
+        return 0;
     }
-    else
+    if (!valid)
     {
-        memcpy( ptr, &val, sizeof(val) );
+        WARN( "Invalid window %p offset %d size %u\n", hwnd, offset, size );
+        RtlSetLastWin32Error( ERROR_INVALID_INDEX );
+        return 0;
     }
+
+    if (offset == GWLP_WNDPROC) return (ULONG_PTR)get_window_proc( (WNDPROC)ret, window_ansi, ansi );
+    return ret;
 }
 
 BOOL is_iconic( HWND hwnd )
@@ -1177,13 +1195,24 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans
     LONG_PTR retval = 0;
     WND *win;
 
-    if (offset == GWLP_HWNDPARENT)
+    switch (offset)
+    {
+    default:
+        if (offset < 0) break;
+        /* fallthrough */
+    case GWLP_ID:
+    case GWLP_HINSTANCE:
+    case GWLP_USERDATA:
+    case GWLP_WNDPROC:
+        return get_window_long_shm( hwnd, offset, size, ansi, internal );
+    case GWLP_HWNDPARENT:
     {
         HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
         if (parent == get_desktop_window())
             parent = get_window_relative( hwnd, GW_OWNER );
         return (ULONG_PTR)parent;
     }
+    }
 
     if (!(win = get_win_ptr( hwnd )))
     {
@@ -1201,12 +1230,6 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans
                 retval |= WS_VISIBLE;
             return retval;
         case GWL_EXSTYLE:
-        case GWLP_USERDATA:
-        case GWLP_ID:
-        case GWLP_HINSTANCE:
-            return 0;
-        case GWLP_WNDPROC:
-            RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
             return 0;
         }
         RtlSetLastWin32Error( ERROR_INVALID_INDEX );
@@ -1215,11 +1238,6 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans
 
     if (win == WND_OTHER_PROCESS)
     {
-        if (offset == GWLP_WNDPROC)
-        {
-            RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
-            return 0;
-        }
         SERVER_START_REQ( get_window_info )
         {
             req->handle = wine_server_user_handle( hwnd );
@@ -1233,34 +1251,10 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans
 
     /* now we have a valid win */
 
-    if (offset >= 0)
-    {
-        if (!get_window_extra( hwnd, win, offset, size, &retval, internal )) retval = 0;
-        release_win_ptr( win );
-        return retval;
-    }
-
     switch(offset)
     {
-    case GWLP_USERDATA:  retval = win->userdata; break;
     case GWL_STYLE:      retval = win->dwStyle; break;
     case GWL_EXSTYLE:    retval = win->dwExStyle; break;
-    case GWLP_ID:        retval = win->wIDmenu; break;
-    case GWLP_HINSTANCE: retval = (ULONG_PTR)win->hInstance; break;
-    case GWLP_WNDPROC:
-        /* This looks like a hack only for the edit control (see tests). This makes these controls
-         * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
-         * that the hack is in GetWindowLongPtr[AW], not in winprocs.
-         */
-        if (win->winproc == BUILTIN_WINPROC(NTUSER_WNDPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE)))
-            retval = (ULONG_PTR)win->winproc;
-        else
-            retval = (ULONG_PTR)get_winproc( win->winproc, ansi );
-        break;
-    default:
-        WARN("Unknown offset %d\n", offset );
-        RtlSetLastWin32Error( ERROR_INVALID_INDEX );
-        break;
     }
     release_win_ptr( win );
     return retval;
@@ -1278,6 +1272,11 @@ ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi )
     return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi, FALSE );
 }
 
+static HMENU get_window_menu( HWND hwnd )
+{
+    return (HMENU)get_window_long_ptr( hwnd, GWLP_ID, FALSE );
+}
+
 /* see GetWindowWord */
 static WORD get_window_word( HWND hwnd, INT offset )
 {
@@ -1289,10 +1288,33 @@ static WORD get_window_word( HWND hwnd, INT offset )
     return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE, FALSE );
 }
 
+/* Set window info with the wine server. */
+static BOOL server_set_window_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size,
+                                    LONG_PTR *oldval, BOOL *ansi, BOOL internal )
+{
+    BOOL ret;
+
+    SERVER_START_REQ( set_window_info )
+    {
+        req->handle = wine_server_user_handle( hwnd );
+        req->offset = offset;
+        req->size = size;
+        req->new_info = newval;
+        req->new_ansi = *ansi;
+        req->internal = internal;
+        ret = !wine_server_call_err( req );
+        *oldval = reply->old_info;
+        *ansi = reply->old_ansi;
+    }
+    SERVER_END_REQ;
+    return ret;
+}
+
 UINT set_window_style_bits( HWND hwnd, UINT set_bits, UINT clear_bits )
 {
-    BOOL ok, made_visible = FALSE;
+    BOOL ok, ansi = FALSE, made_visible = FALSE;
     STYLESTRUCT style;
+    LONG_PTR oldval;
     WND *win = get_win_ptr( hwnd );
 
     if (!win || win == WND_DESKTOP) return 0;
@@ -1309,18 +1331,11 @@ UINT set_window_style_bits( HWND hwnd, UINT set_bits, UINT clear_bits )
         release_win_ptr( win );
         return style.styleNew;
     }
-    SERVER_START_REQ( set_window_info )
+    if ((ok = server_set_window_info( hwnd, GWL_STYLE, style.styleNew, 0, &oldval, &ansi, FALSE )))
     {
-        req->handle = wine_server_user_handle( hwnd );
-        req->offset = GWL_STYLE;
-        req->new_info = style.styleNew;
-        if ((ok = !wine_server_call( req )))
-        {
-            style.styleOld = reply->old_info;
-            win->dwStyle = style.styleNew;
-        }
+        style.styleOld = oldval;
+        win->dwStyle = style.styleNew;
     }
-    SERVER_END_REQ;
 
     if (ok && ((style.styleOld ^ style.styleNew) & WS_VISIBLE))
     {
@@ -1389,8 +1404,8 @@ static HWND set_window_owner( HWND hwnd, HWND owner )
 static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size,
                                           LONG_PTR newval, BOOL ansi, BOOL internal )
 {
-    BOOL ok, made_visible = FALSE, layered = FALSE;
-    LONG_PTR retval = 0;
+    BOOL ok, made_visible = FALSE, layered = FALSE, old_ansi = ansi;
+    LONG_PTR retval = 0, oldval;
     STYLESTRUCT style;
     WND *win;
 
@@ -1415,11 +1430,6 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size,
     }
     if (win == WND_OTHER_PROCESS)
     {
-        if (offset == GWLP_WNDPROC)
-        {
-            RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
-            return 0;
-        }
         if (offset > 32767 || offset < -32767)
         {
             RtlSetLastWin32Error( ERROR_INVALID_INDEX );
@@ -1465,85 +1475,28 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size,
             return (ULONG_PTR)NtUserSetParent( hwnd, (HWND)newval );
         }
     case GWLP_WNDPROC:
-    {
-        WNDPROC proc;
-        UINT old_flags = win->flags;
-        retval = get_window_long_ptr( hwnd, offset, ansi );
-        proc = alloc_winproc( (WNDPROC)newval, ansi );
-        if (proc) win->winproc = proc;
-        if (is_winproc_unicode( proc, !ansi )) win->flags |= WIN_ISUNICODE;
-        else win->flags &= ~WIN_ISUNICODE;
-        if (!((old_flags ^ win->flags) & WIN_ISUNICODE))
-        {
-            release_win_ptr( win );
-            return retval;
-        }
-        /* update is_unicode flag on the server side */
+        newval = (ULONG_PTR)alloc_winproc( (WNDPROC)newval, ansi );
         break;
     }
-    case GWLP_ID:
-    case GWLP_HINSTANCE:
-    case GWLP_USERDATA:
-        break;
-    default:
-        if (!get_window_extra( hwnd, win, offset, size, &retval, internal ))
-        {
-            release_win_ptr( win );
-            return 0;
-        }
-        if (retval == newval)
-        {
-            /* already set to the same value */
-            release_win_ptr( win );
-            return newval;
-        }
-        break;
-    }
-
-    if (offset == GWLP_WNDPROC) newval = !!(win->flags & WIN_ISUNICODE);
-    if (offset == GWLP_USERDATA && size == sizeof(WORD)) newval = MAKELONG( newval, win->userdata >> 16 );
 
-    SERVER_START_REQ( set_window_info )
+    if ((ok = server_set_window_info( hwnd, offset, newval, size, &oldval, &old_ansi, internal )))
     {
-        req->handle = wine_server_user_handle( hwnd );
-        req->offset = offset;
-        req->new_info = newval;
-        req->size = size;
-        if ((ok = !wine_server_call_err( req )))
+        switch (offset)
         {
-            switch (offset)
-            {
-            case GWL_STYLE:
-                win->dwStyle = newval;
-                win->dwExStyle = fix_exstyle(win->dwStyle, win->dwExStyle);
-                retval = reply->old_info;
-                break;
-            case GWL_EXSTYLE:
-                win->dwExStyle = newval;
-                retval = reply->old_info;
-                break;
-            case GWLP_ID:
-                win->wIDmenu = newval;
-                retval = reply->old_info;
-                break;
-            case GWLP_HINSTANCE:
-                win->hInstance = (HINSTANCE)newval;
-                retval = reply->old_info;
-                break;
-            case GWLP_WNDPROC:
-                break;
-            case GWLP_USERDATA:
-                win->userdata = newval;
-                retval = reply->old_info;
-                break;
-            default:
-                set_win_data( (char *)win->wExtra + offset, newval, size );
-                retval = reply->old_info;
-                break;
-            }
+        case GWL_STYLE:
+            win->dwStyle = newval;
+            win->dwExStyle = fix_exstyle(win->dwStyle, win->dwExStyle);
+            retval = oldval;
+            break;
+        case GWL_EXSTYLE:
+            win->dwExStyle = newval;
+            retval = oldval;
+            break;
+        default:
+            retval = oldval;
+            break;
         }
     }
-    SERVER_END_REQ;
 
     if (offset == GWL_EXSTYLE && ((style.styleOld ^ style.styleNew) & WS_EX_LAYERED)) layered = TRUE;
     if ((offset == GWL_STYLE && ((style.styleOld ^ style.styleNew) & WS_VISIBLE)) || layered)
@@ -1564,6 +1517,7 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size,
         send_message( hwnd, WM_STYLECHANGED, offset, (LPARAM)&style );
     }
 
+    if (offset == GWLP_WNDPROC) return (ULONG_PTR)get_window_proc( (WNDPROC)retval, old_ansi, ansi );
     return retval;
 }
 
@@ -1594,7 +1548,6 @@ BOOL WINAPI NtUserSetWindowFNID( HWND hwnd, WORD fnid )
     {
         req->handle = wine_server_user_handle( hwnd );
         req->atom = get_builtin_class_atom( fnid & 0x7fff );
-        req->fnid = fnid;
         ret = !wine_server_call_err( req );
     }
     SERVER_END_REQ;
@@ -5319,7 +5272,7 @@ LRESULT destroy_window( HWND hwnd )
 
     if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
     if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
-        menu = (HMENU)win->wIDmenu;
+        menu = get_window_menu( hwnd );
     sys_menu = win->hSysMenu;
     free_dce( win->dce, hwnd, &drawables );
     win->dce = NULL;
@@ -5471,7 +5424,7 @@ void destroy_thread_windows(void)
         /* recycle the WND struct as a destroy_entry struct */
         entry = (struct destroy_entry *)win;
         tmp.handle = win->handle;
-        if (!is_child) tmp.menu = (HMENU)win->wIDmenu;
+        if (!is_child) tmp.menu = get_window_menu( handle );
         tmp.sys_menu = win->hSysMenu;
         tmp.current_drawable = win->current_drawable;
         tmp.unused_drawable = win->unused_drawable;
@@ -5526,7 +5479,6 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
     UINT dpi_context = get_thread_dpi_awareness_context();
     HWND handle = 0, full_parent = 0, full_owner = 0;
     struct tagCLASS *class = NULL;
-    int extra_bytes = 0;
     WND *win;
 
     SERVER_START_REQ( create_window )
@@ -5538,13 +5490,13 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
         req->dpi_context     = dpi_context;
         req->style           = style;
         req->ex_style        = ex_style;
+        req->ansi            = ansi;
         req->atom            = wine_server_add_atom( req, name );
         if (!wine_server_call_err( req ))
         {
             handle      = wine_server_ptr_handle( reply->handle );
             full_parent = wine_server_ptr_handle( reply->parent );
             full_owner  = wine_server_ptr_handle( reply->owner );
-            extra_bytes = reply->extra;
             class       = wine_server_get_ptr( reply->class_ptr );
         }
     }
@@ -5556,7 +5508,7 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
         return NULL;
     }
 
-    if (!(win = calloc( 1, FIELD_OFFSET(WND, wExtra) + extra_bytes )))
+    if (!(win = calloc( 1, sizeof(*win) )))
     {
         SERVER_START_REQ( destroy_window )
         {
@@ -5592,11 +5544,7 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
     win->parent     = full_parent;
     win->owner      = full_owner;
     win->class      = class;
-    win->winproc    = get_class_winproc( class );
-    win->hInstance  = instance;
-    win->cbWndExtra = extra_bytes;
     set_user_handle_ptr( handle, win );
-    if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE;
     return win;
 }
 
@@ -5771,10 +5719,8 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
     win->text        = NULL;
     win->dwStyle     = style;
     win->dwExStyle   = ex_style;
-    win->wIDmenu     = 0;
     win->helpContext = 0;
     win->pScroll     = NULL;
-    win->userdata    = 0;
     win->hIcon       = 0;
     win->hIconSmall  = 0;
     win->hIconSmall2 = 0;
@@ -5837,7 +5783,6 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
         req->handle     = wine_server_user_handle( hwnd );
         req->style      = win->dwStyle;
         req->ex_style   = win->dwExStyle;
-        req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
         wine_server_call( req );
     }
     SERVER_END_REQ;
diff --git a/dlls/windows.ui.core.textinput/tests/textinput.c b/dlls/windows.ui.core.textinput/tests/textinput.c
index 29b7496d1bba..df52bc33bdb6 100644
--- a/dlls/windows.ui.core.textinput/tests/textinput.c
+++ b/dlls/windows.ui.core.textinput/tests/textinput.c
@@ -156,15 +156,18 @@ static void test_CoreTextServicesManager(void)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
 
     hr = ICoreTextServicesManagerStatics_GetForCurrentView(core_text_manager_stat, &core_text_manager);
-    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(hr == S_OK || broken(hr == RPC_E_WRONG_THREAD) /* <= Win10 2009 */, "Got unexpected hr %#lx.\n", hr);
+    if (hr == S_OK)
+    {
+        check_interface(core_text_manager, &IID_IUnknown, TRUE);
+        check_interface(core_text_manager, &IID_IInspectable, TRUE);
+        check_interface(core_text_manager, &IID_IAgileObject, TRUE);
+        check_interface(core_text_manager, &IID_ICoreTextServicesManagerStatics, FALSE);
+        check_interface(core_text_manager, &IID_ICoreTextServicesManager, TRUE);
 
-    check_interface(core_text_manager, &IID_IUnknown, TRUE);
-    check_interface(core_text_manager, &IID_IInspectable, TRUE);
-    check_interface(core_text_manager, &IID_IAgileObject, TRUE);
-    check_interface(core_text_manager, &IID_ICoreTextServicesManagerStatics, FALSE);
-    check_interface(core_text_manager, &IID_ICoreTextServicesManager, TRUE);
+        ICoreTextServicesManager_Release(core_text_manager);
+    }
 
-    ICoreTextServicesManager_Release(core_text_manager);
     ref = ICoreTextServicesManagerStatics_Release(core_text_manager_stat);
     ok(ref == 2, "Got unexpected refcount %ld.\n", ref);
     ref = IActivationFactory_Release(factory);
diff --git a/dlls/wineandroid.drv/build.gradle.in b/dlls/wineandroid.drv/build.gradle.in
index d83082bb8c5d..56c5aca375d8 100644
--- a/dlls/wineandroid.drv/build.gradle.in
+++ b/dlls/wineandroid.drv/build.gradle.in
@@ -112,7 +112,7 @@ android
     defaultConfig
     {
         applicationId "org.winehq.wine"
-        minSdkVersion 26
+        minSdkVersion 28
         targetSdkVersion 28
         versionCode 1
         versionName "@PACKAGE_VERSION@"
diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c
index 55c7b1671563..79014b4c3315 100644
--- a/dlls/wined3d/adapter_vk.c
+++ b/dlls/wined3d/adapter_vk.c
@@ -1933,7 +1933,7 @@ static const struct
 vulkan_instance_extensions[] =
 {
     {VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1, FALSE},
-    {VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,                  VK_API_VERSION_1_1, FALSE},
+    {VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME,      VK_API_VERSION_1_1, FALSE},
     {VK_KHR_SURFACE_EXTENSION_NAME,                          ~0u,                TRUE},
     {VK_KHR_WIN32_SURFACE_EXTENSION_NAME,                    ~0u,                TRUE},
 };
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index e754cc633b72..8f195c6dee4a 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -4987,7 +4987,7 @@ static void update_swapchain_flags(struct wined3d_texture *texture)
 {
     unsigned int flags = texture->swapchain->state.desc.flags;
 
-    if (flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER)
+    if (flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER && !texture->swapchain->state.desc.multisample_type)
         texture->resource.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
     else
         texture->resource.access &= ~(WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W);
@@ -5082,6 +5082,9 @@ HRESULT CDECL wined3d_device_reset(struct wined3d_device *device,
     TRACE("refresh_rate %u\n", swapchain_desc->refresh_rate);
     TRACE("auto_restore_display_mode %#x\n", swapchain_desc->auto_restore_display_mode);
 
+    if (FAILED(hr = wined3d_swapchain_desc_validate_flags(swapchain_desc)))
+        return hr;
+
     if (swapchain_desc->backbuffer_bind_flags && swapchain_desc->backbuffer_bind_flags != WINED3D_BIND_RENDER_TARGET)
         FIXME("Got unexpected backbuffer bind flags %#x.\n", swapchain_desc->backbuffer_bind_flags);
 
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index 4ae80cf59f37..b84fb3958c5c 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -3537,6 +3537,37 @@ static DWORD shader_glsl_append_dst(struct wined3d_string_buffer *buffer, const
     return shader_glsl_append_dst_ext(buffer, ins, &ins->dst[0], 0, ins->dst[0].reg.data_type);
 }
 
+static void shader_glsl_get_coord_size(enum wined3d_shader_resource_type resource_type,
+        unsigned int *coord_size, unsigned int *deriv_size)
+{
+    const BOOL is_array = resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY
+            || resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY;
+
+    *coord_size = resource_type_info[resource_type].coord_size;
+    *deriv_size = *coord_size;
+    if (is_array)
+        --(*deriv_size);
+}
+
+static void shader_glsl_add_offset_immdata_param(const struct wined3d_shader_context *ctx,
+        const struct wined3d_shader_texel_offset *offset, enum wined3d_shader_resource_type resource_type,
+        struct glsl_src_param *glsl_src)
+{
+    struct shader_glsl_ctx_priv *priv = ctx->backend_data;
+    struct wined3d_string_buffer *imm_str = string_buffer_get(priv->string_buffers);
+    int offset_immdata[4] = {offset->u, offset->v, offset->w};
+    unsigned int coord_size, deriv_size;
+
+    shader_glsl_get_coord_size(resource_type, &coord_size, &deriv_size);
+
+    glsl_src->param_str[0] = '\0';
+
+    shader_glsl_append_imm_ivec(imm_str, offset_immdata, coord_size);
+    sprintf(glsl_src->param_str, "%s", imm_str->buffer);
+
+    string_buffer_release(priv->string_buffers, imm_str);
+}
+
 /** Process GLSL instruction modifiers */
 static void shader_glsl_add_instruction_modifiers(const struct wined3d_shader_instruction *ins)
 {
@@ -3602,18 +3633,6 @@ static BOOL shader_glsl_has_core_grad(const struct wined3d_gl_info *gl_info)
     return shader_glsl_get_version(gl_info) >= 130 || gl_info->supported[EXT_GPU_SHADER4];
 }
 
-static void shader_glsl_get_coord_size(enum wined3d_shader_resource_type resource_type,
-        unsigned int *coord_size, unsigned int *deriv_size)
-{
-    const BOOL is_array = resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_1DARRAY
-            || resource_type == WINED3D_SHADER_RESOURCE_TEXTURE_2DARRAY;
-
-    *coord_size = resource_type_info[resource_type].coord_size;
-    *deriv_size = *coord_size;
-    if (is_array)
-        --(*deriv_size);
-}
-
 static void shader_glsl_get_sample_function(const struct wined3d_shader_context *ctx,
         DWORD resource_idx, DWORD sampler_idx, uint32_t flags, struct glsl_sample_function *sample_function)
 {
@@ -3848,12 +3867,15 @@ static void PRINTF_ATTR(9, 10) shader_glsl_gen_sample_code(const struct wined3d_
         shader_addline(ins->ctx->buffer, ", %s, %s", dx, dy);
     else if (bias)
         shader_addline(ins->ctx->buffer, ", %s", bias);
+
+    assert(!sample_function->offset_size || offset);
     if (sample_function->offset_size)
     {
         int offset_immdata[4] = {offset->u, offset->v, offset->w};
         shader_addline(ins->ctx->buffer, ", ");
         shader_glsl_append_imm_ivec(ins->ctx->buffer, offset_immdata, sample_function->offset_size);
     }
+
     shader_addline(ins->ctx->buffer, ")");
 
     if (sample_function->output_single_component)
@@ -6192,15 +6214,12 @@ static void shader_glsl_interpolate(const struct wined3d_shader_instruction *ins
 
 static void shader_glsl_ld(const struct wined3d_shader_instruction *ins)
 {
+    struct glsl_src_param coord_param, lod_param, sample_param, offset_param;
     const struct wined3d_shader_reg_maps *reg_maps = ins->ctx->reg_maps;
-    struct glsl_src_param coord_param, lod_param, sample_param;
     unsigned int resource_idx, sampler_idx, sampler_bind_idx;
     struct glsl_sample_function sample_function;
+    BOOL has_lod_param, just_add_offset = false;
     DWORD flags = WINED3D_GLSL_SAMPLE_LOAD;
-    BOOL has_lod_param;
-
-    if (wined3d_shader_instruction_has_texel_offset(ins))
-        flags |= WINED3D_GLSL_SAMPLE_OFFSET;
 
     resource_idx = ins->src[1].reg.idx[0].offset;
     sampler_idx = WINED3D_SAMPLER_DEFAULT;
@@ -6212,11 +6231,42 @@ static void shader_glsl_ld(const struct wined3d_shader_instruction *ins)
     }
     has_lod_param = is_mipmapped(reg_maps->resource_info[resource_idx].type);
 
+    /* texelFetchOffset() is not defined for BUFFER, TEXTURE_2DMS, and
+     * TEXTURE_2DMSARRAY, so for these we just add the texel offset to the
+     * coordinates. */
+    if (wined3d_shader_instruction_has_texel_offset(ins))
+    {
+        if (has_lod_param)
+            flags |= WINED3D_GLSL_SAMPLE_OFFSET;
+        else
+            just_add_offset = true;
+    }
+
     shader_glsl_get_sample_function(ins->ctx, resource_idx, sampler_idx, flags, &sample_function);
     shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param);
     shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &lod_param);
     sampler_bind_idx = shader_glsl_find_sampler(&reg_maps->sampler_map, resource_idx, sampler_idx);
-    if (is_multisampled(reg_maps->resource_info[resource_idx].type))
+
+    if (just_add_offset)
+    {
+        shader_glsl_add_offset_immdata_param(ins->ctx, &ins->texel_offset,
+                reg_maps->resource_info[resource_idx].type, &offset_param);
+
+        if (is_multisampled(reg_maps->resource_info[resource_idx].type))
+        {
+            shader_glsl_add_src_param(ins, &ins->src[2], WINED3DSP_WRITEMASK_0, &sample_param);
+            shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle,
+                    NULL, NULL, NULL, NULL, "%s + %s, %s", coord_param.param_str,
+                    offset_param.param_str, sample_param.param_str);
+        }
+        else
+        {
+            shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle,
+                    NULL, NULL, has_lod_param ? lod_param.param_str : NULL, NULL,
+                    "%s + %s", coord_param.param_str, offset_param.param_str);
+        }
+    }
+    else if (is_multisampled(reg_maps->resource_info[resource_idx].type))
     {
         shader_glsl_add_src_param(ins, &ins->src[2], WINED3DSP_WRITEMASK_0, &sample_param);
         shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle,
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c
index 2bc544c1213b..c26df770cdb9 100644
--- a/dlls/wined3d/swapchain.c
+++ b/dlls/wined3d/swapchain.c
@@ -1506,7 +1506,7 @@ static HRESULT swapchain_create_texture(struct wined3d_swapchain *swapchain,
         texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU;
     else
         texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU;
-    if (!depth && (swapchain_desc->flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER))
+    if (!depth && (swapchain_desc->flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER) && !swapchain_desc->multisample_type)
         texture_desc.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
     texture_desc.width = swapchain_desc->backbuffer_width;
     texture_desc.height = swapchain_desc->backbuffer_height;
@@ -1536,6 +1536,16 @@ static HRESULT swapchain_create_texture(struct wined3d_swapchain *swapchain,
     return S_OK;
 }
 
+HRESULT wined3d_swapchain_desc_validate_flags(const struct wined3d_swapchain_desc *desc)
+{
+    /* d3d8 allows the lockable flag even though the backbuffer is not lockable. */
+    if ((desc->flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER) && desc->multisample_type
+            && !(desc->flags & WINED3D_SWAPCHAIN_ALLOW_MS_LOCKABLE_BACKBUFFER))
+        return WINED3DERR_INVALIDCALL;
+
+    return WINED3D_OK;
+}
+
 static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device,
         const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent,
         void *parent, const struct wined3d_parent_ops *parent_ops,
@@ -1554,6 +1564,9 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc
             && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY)
         FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect);
 
+    if (FAILED(hr = wined3d_swapchain_desc_validate_flags(desc)))
+        return hr;
+
     window = desc->device_window ? desc->device_window : device->create_parms.focus_window;
     TRACE("Using target window %p.\n", window);
 
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 313e441d7006..e4e0cf24dc7c 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -4063,6 +4063,8 @@ struct wined3d_decoder_output_view
 
 void wined3d_decoder_output_view_cleanup(struct wined3d_decoder_output_view *view);
 
+HRESULT wined3d_swapchain_desc_validate_flags(const struct wined3d_swapchain_desc *desc);
+
 struct wined3d_swapchain_state
 {
     struct wined3d *wined3d;
diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c
index 229fe0d482c7..301a76ca91ce 100644
--- a/dlls/winegstreamer/video_processor.c
+++ b/dlls/winegstreamer/video_processor.c
@@ -436,8 +436,6 @@ static HRESULT WINAPI video_processor_GetOutputAvailableType(IMFTransform *iface
         goto done;
     if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &subtype)))
         goto done;
-    if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, frame_size)))
-        goto done;
 
     IMFMediaType_AddRef((*type = media_type));
 
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 4c1ac93524f5..6b0ab117cd12 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -45,6 +45,25 @@ - (void)_setPreventsActivation:(BOOL)flag;
 
 @end
 
+typedef uint32_t CGSConnectionID;
+extern CGSConnectionID CGSMainConnectionID(void);
+
+typedef uint32_t CAContextID;
+
+@interface CAContext : NSObject
+
++ (id) contextWithCGSConnection:(CGSConnectionID)connection options:(NSDictionary*)options;
+@property(readonly) CAContextID contextId;
+@property(retain) CALayer* layer;
+
+@end
+
+@interface CALayerHost : CALayer
+
+- (void) setContextId:(CAContextID)contextId;
+
+@end
+
 
 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
 {
@@ -374,6 +393,7 @@ @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContent
     int backingSize[2];
 
     WineMetalView *_metalView;
+    NSMutableDictionary<NSNumber*, CALayerHost*>* _caLayerHosts;
 }
 
 @property (readonly, nonatomic) BOOL everHadGLContext;
@@ -386,6 +406,8 @@ - (void) wine_getBackingSize:(int*)outBackingSize;
     - (void) wine_setBackingSize:(const int*)newBackingSize;
 
     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
+    - (void) addCALayerHostViewWithContextId:(CAContextID)contextId;
+    - (void) removeCALayerHostView:(CAContextID)contextId;
 
 @end
 
@@ -496,6 +518,7 @@ - (void) dealloc
         [markedText release];
         [glContexts release];
         [pendingGlContexts release];
+        [_caLayerHosts release];
         CGImageRelease(colorImage);
         CGImageRelease(shapeImage);
         [super dealloc];
@@ -699,6 +722,33 @@ - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
         return _metalView;
     }
 
+    - (void) addCALayerHostViewWithContextId:(CAContextID)contextId
+    {
+        if (!_caLayerHosts)
+            _caLayerHosts = [[NSMutableDictionary alloc] init];
+
+        [self removeCALayerHostView:contextId];
+
+        CALayerHost* host = [CALayerHost layer];
+        [host setContextId:contextId];
+        host.magnificationFilter = kCAFilterNearest;
+        host.contentsScale = retina_on ? 2.0 : 1.0;
+        host.frame = self.layer.bounds;
+        host.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
+
+        [self.layer addSublayer:host];
+        [_caLayerHosts setObject:host forKey:@(contextId)];
+
+        [(WineWindow*)self.window windowDidDrawContent];
+    }
+
+    - (void) removeCALayerHostView:(CAContextID)contextId
+    {
+        NSNumber* key = @(contextId);
+        [[_caLayerHosts objectForKey:key] removeFromSuperlayer];
+        [_caLayerHosts removeObjectForKey:key];
+    }
+
     - (void) setLayerRetinaProperties:(BOOL)mode
     {
         [self layer].contentsScale = mode ? 2.0 : 1.0;
@@ -738,6 +788,9 @@ - (void) setRetinaMode:(BOOL)mode
         [self updateGLContexts];
         [self setLayerRetinaProperties:mode];
 
+        for (CALayerHost* host in [_caLayerHosts allValues])
+            host.contentsScale = mode ? 2.0 : 1.0;
+
         [super setRetinaMode:mode];
     }
 
@@ -3972,6 +4025,196 @@ void macdrv_view_release_metal_view(macdrv_metal_view v)
     });
 }
 
+@protocol WineMetalSwapChain <NSObject>
+
+- (CAMetalLayer*) layer;
+
+@end
+
+@interface MetalViewSwapChain : NSObject <WineMetalSwapChain>
+{
+    macdrv_metal_device device;
+    macdrv_metal_view metal_view;
+}
+
+- (instancetype) initWithView:(macdrv_view)view;
+
+@end
+
+@implementation MetalViewSwapChain
+
+- (instancetype) initWithView:(macdrv_view)view
+{
+    self = [super init];
+    if (!self) return nil;
+
+    if (!(device = macdrv_create_metal_device()))
+    {
+        [self release];
+        return nil;
+    }
+    if (!(metal_view = macdrv_view_create_metal_view(view, device)))
+    {
+        macdrv_release_metal_device(device);
+        device = NULL;
+        [self release];
+        return nil;
+    }
+    return self;
+}
+
+- (CAMetalLayer*) layer
+{
+    return (CAMetalLayer*)macdrv_view_get_metal_layer(metal_view);
+}
+
+- (void) dealloc
+{
+    if (metal_view) macdrv_view_release_metal_view(metal_view);
+    if (device) macdrv_release_metal_device(device);
+    [super dealloc];
+}
+
+@end
+
+@interface CAContextSwapChain : NSObject <WineMetalSwapChain>
+{
+    void* hwnd;
+    macdrv_metal_device device;
+    CAMetalLayer* offscreen_layer;
+    CAContext* remote_context;
+    CAContextID context_id;
+}
+
+- (instancetype) initWithHwnd:(void*)hwnd bounds:(CGRect)bounds;
+
+@end
+
+@implementation CAContextSwapChain
+
+- (instancetype) initWithHwnd:(void*)newHwnd bounds:(CGRect)bounds
+{
+    self = [super init];
+    if (!self) return nil;
+
+    hwnd = newHwnd;
+    if (!(device = macdrv_create_metal_device()))
+    {
+        [self release];
+        return nil;
+    }
+
+    offscreen_layer = [[CAMetalLayer alloc] init];
+    if (!offscreen_layer)
+    {
+        macdrv_release_metal_device(device);
+        device = NULL;
+        [self release];
+        return nil;
+    }
+
+    offscreen_layer.device = (id<MTLDevice>)device;
+    offscreen_layer.framebufferOnly = YES;
+    offscreen_layer.magnificationFilter = kCAFilterNearest;
+    offscreen_layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
+    offscreen_layer.contentsScale = retina_on ? 2.0 : 1.0;
+    [offscreen_layer setBounds:cgrect_mac_from_win(bounds)];
+    [offscreen_layer setAnchorPoint:CGPointMake(0, 0)];
+
+    /* Export the CAMetalLayer from the rendering process, then have the
+     * target HWND's owner host it using CALayerHost. */
+    OnMainThread(^{
+        remote_context = [[CAContext contextWithCGSConnection:CGSMainConnectionID()
+                                                      options:[NSDictionary dictionary]] retain];
+        [remote_context setLayer:offscreen_layer];
+        context_id = [remote_context contextId];
+    });
+
+    if (!remote_context || !context_id)
+    {
+        [self release];
+        return nil;
+    }
+
+    macdrv_create_remote_layer(hwnd, context_id);
+    return self;
+}
+
+- (CAMetalLayer*) layer
+{
+    return offscreen_layer;
+}
+
+- (void) dealloc
+{
+    if (context_id) macdrv_release_remote_layer(hwnd, context_id);
+    CAContext *context = remote_context;
+    CAMetalLayer *layer = offscreen_layer;
+    macdrv_metal_device dev = device;
+
+    OnMainThreadAsync(^{
+        [context setLayer:nil];
+        [context release];
+        [layer release];
+        if (dev) macdrv_release_metal_device(dev);
+    });
+
+    [super dealloc];
+}
+
+@end
+
+macdrv_metal_swapchain macdrv_create_view_swapchain(macdrv_view v)
+{
+    return (macdrv_metal_swapchain)[[MetalViewSwapChain alloc] initWithView:v];
+}
+
+macdrv_metal_swapchain macdrv_create_offscreen_swapchain(void* hwnd, CGRect bounds)
+{
+    return (macdrv_metal_swapchain)[[CAContextSwapChain alloc] initWithHwnd:hwnd bounds:bounds];
+}
+
+macdrv_metal_layer macdrv_swapchain_get_layer(macdrv_metal_swapchain swapchain)
+{
+    return (macdrv_metal_layer)[(id<WineMetalSwapChain>)swapchain layer];
+}
+
+
+void macdrv_destroy_swapchain(macdrv_metal_swapchain swapchain)
+{
+    [(id<WineMetalSwapChain>)swapchain release];
+}
+
+void macdrv_window_create_ca_layer_host_view(macdrv_window w, unsigned int context_id)
+{
+@autoreleasepool
+{
+    WineWindow* window = (WineWindow*)w;
+
+    OnMainThread(^{
+        NSView* content_view = [window contentView];
+
+        if ([content_view isKindOfClass:[WineContentView class]])
+            [(WineContentView*)content_view addCALayerHostViewWithContextId:context_id];
+    });
+}
+}
+
+void macdrv_window_release_ca_layer_host_view(macdrv_window w, unsigned int context_id)
+{
+@autoreleasepool
+{
+    WineWindow* window = (WineWindow*)w;
+
+    OnMainThread(^{
+        NSView* content_view = [window contentView];
+
+        if ([content_view isKindOfClass:[WineContentView class]])
+            [(WineContentView*)content_view removeCALayerHostView:context_id];
+    });
+}
+}
+
 bool macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
 {
     WineContentView* view = (WineContentView*)v;
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h
index 6316a0963652..4f7107baf682 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -96,6 +96,8 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
 {
     WM_MACDRV_SET_WIN_REGION = WM_WINE_FIRST_DRIVER_MSG,
     WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS,
+    WM_MACDRV_CREATE_REMOTE_LAYER,
+    WM_MACDRV_RELEASE_REMOTE_LAYER,
 };
 
 struct macdrv_thread_data
@@ -193,10 +195,9 @@ extern BOOL macdrv_SystemParametersInfo(UINT action, UINT int_param, void *ptr_p
 
 struct macdrv_client_surface
 {
-    struct client_surface client;
-    macdrv_view           cocoa_view;
-    macdrv_metal_device   metal_device;
-    macdrv_metal_view     metal_view;
+    struct client_surface   client;
+    macdrv_view             cocoa_view;
+    macdrv_metal_swapchain  metal_swapchain;
 };
 
 static inline struct macdrv_client_surface *impl_from_client_surface(struct client_surface *client)
@@ -205,6 +206,7 @@ extern BOOL macdrv_SystemParametersInfo(UINT action, UINT int_param, void *ptr_p
 }
 
 extern struct macdrv_client_surface *macdrv_client_surface_create(HWND hwnd);
+extern BOOL macdrv_client_surface_acquire_metal_swapchain(struct macdrv_client_surface *surface);
 
 extern struct macdrv_win_data *get_win_data(HWND hwnd);
 extern void release_win_data(struct macdrv_win_data *data);
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h
index 381808479ce9..9a91edbe8e31 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -106,6 +106,7 @@
 typedef struct macdrv_opaque_metal_device* macdrv_metal_device;
 typedef struct macdrv_opaque_metal_view* macdrv_metal_view;
 typedef struct macdrv_opaque_metal_layer* macdrv_metal_layer;
+typedef struct macdrv_opaque_metal_swapchain* macdrv_metal_swapchain;
 typedef struct macdrv_opaque_status_item* macdrv_status_item;
 struct macdrv_event;
 struct macdrv_query;
@@ -527,6 +528,14 @@ extern void macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
 extern macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d);
 extern macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v);
 extern void macdrv_view_release_metal_view(macdrv_metal_view v);
+extern macdrv_metal_swapchain macdrv_create_view_swapchain(macdrv_view v);
+extern macdrv_metal_swapchain macdrv_create_offscreen_swapchain(void* hwnd, CGRect bounds);
+extern macdrv_metal_layer macdrv_swapchain_get_layer(macdrv_metal_swapchain swapchain);
+extern void macdrv_destroy_swapchain(macdrv_metal_swapchain swapchain);
+extern void macdrv_window_create_ca_layer_host_view(macdrv_window w, unsigned int context_id);
+extern void macdrv_window_release_ca_layer_host_view(macdrv_window w, unsigned int context_id);
+extern void macdrv_create_remote_layer(void* hwnd, unsigned int context_id);
+extern void macdrv_release_remote_layer(void* hwnd, unsigned int context_id);
 extern bool macdrv_get_view_backing_size(macdrv_view v, int backing_size[2]);
 extern void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2]);
 extern uint32_t macdrv_window_background_color(void);
diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c
index b9b042bd85d1..f1643e3e5c4b 100644
--- a/dlls/winemac.drv/vulkan.c
+++ b/dlls/winemac.drv/vulkan.c
@@ -27,8 +27,6 @@
 
 #include "config.h"
 
-#include <stdarg.h>
-#include <stdio.h>
 #include <dlfcn.h>
 
 #include "ntstatus.h"
@@ -51,8 +49,8 @@ static VkResult macdrv_vulkan_surface_create(HWND hwnd, const struct vulkan_inst
     TRACE("%p %p %p %p\n", hwnd, instance, handle, client);
 
     if (!(surface = macdrv_client_surface_create(hwnd))) return VK_ERROR_OUT_OF_HOST_MEMORY;
-    if (!(surface->metal_device = macdrv_create_metal_device())) goto err;
-    if (!(surface->metal_view = macdrv_view_create_metal_view(surface->cocoa_view, surface->metal_device))) goto err;
+
+    if (!macdrv_client_surface_acquire_metal_swapchain(surface)) goto err;
 
     if (instance->p_vkCreateMetalSurfaceEXT)
     {
@@ -60,7 +58,7 @@ static VkResult macdrv_vulkan_surface_create(HWND hwnd, const struct vulkan_inst
         create_info_host.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
         create_info_host.pNext = NULL;
         create_info_host.flags = 0; /* reserved */
-        create_info_host.pLayer = macdrv_view_get_metal_layer(surface->metal_view);
+        create_info_host.pLayer = macdrv_swapchain_get_layer(surface->metal_swapchain);
 
         res = instance->p_vkCreateMetalSurfaceEXT(instance->host.instance, &create_info_host, NULL /* allocator */, handle);
     }
@@ -70,7 +68,7 @@ static VkResult macdrv_vulkan_surface_create(HWND hwnd, const struct vulkan_inst
         create_info_host.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
         create_info_host.pNext = NULL;
         create_info_host.flags = 0; /* reserved */
-        create_info_host.pView = macdrv_view_get_metal_layer(surface->metal_view);
+        create_info_host.pView = macdrv_swapchain_get_layer(surface->metal_swapchain);
 
         res = instance->p_vkCreateMacOSSurfaceMVK(instance->host.instance, &create_info_host, NULL /* allocator */, handle);
     }
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 7831eca732ba..adfc059e34fc 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -1076,8 +1076,7 @@ static void macdrv_client_surface_destroy(struct client_surface *client)
 
     TRACE("%s\n", debugstr_client_surface(client));
 
-    if (surface->metal_view) macdrv_view_release_metal_view(surface->metal_view);
-    if (surface->metal_device) macdrv_release_metal_device(surface->metal_device);
+    if (surface->metal_swapchain) macdrv_destroy_swapchain(surface->metal_swapchain);
 }
 
 static void macdrv_client_surface_detach(struct client_surface *client)
@@ -1167,6 +1166,35 @@ struct macdrv_client_surface *macdrv_client_surface_create(HWND hwnd)
     return surface;
 }
 
+BOOL macdrv_client_surface_acquire_metal_swapchain(struct macdrv_client_surface *surface)
+{
+    HWND hwnd = surface->client.hwnd;
+    struct macdrv_win_data *data;
+
+    if (surface->metal_swapchain) return TRUE;
+
+    if ((data = get_win_data(hwnd)))
+    {
+        release_win_data(data);
+        surface->metal_swapchain = macdrv_create_view_swapchain(surface->cocoa_view);
+    }
+    else
+    {
+        RECT rect;
+
+        if (NtUserGetAncestor(hwnd, GA_ROOT) != hwnd)
+        {
+            FIXME("Cross-process child window Metal swapchains are not implemented\n");
+            return FALSE;
+        }
+
+        if (!NtUserGetClientRect(hwnd, &rect, NtUserGetWinMonitorDpi(hwnd, MDT_RAW_DPI))) return FALSE;
+        surface->metal_swapchain = macdrv_create_offscreen_swapchain(hwnd, cgrect_from_rect(rect));
+    }
+
+    return surface->metal_swapchain != NULL;
+}
+
 /**********************************************************************
  *              SetDesktopWindow   (MACDRV.@)
  */
@@ -1531,6 +1559,22 @@ LRESULT macdrv_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
         activate_on_following_focus();
         TRACE("WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS time %u\n", activate_on_focus_time);
         return 0;
+    case WM_MACDRV_CREATE_REMOTE_LAYER:
+        if ((data = get_win_data(hwnd)))
+        {
+            TRACE("WM_MACDRV_CREATE_REMOTE_LAYER context_id %u\n", (unsigned int)lp);
+            if (data->cocoa_window) macdrv_window_create_ca_layer_host_view(data->cocoa_window, (unsigned int)lp);
+            release_win_data(data);
+        }
+        return 0;
+    case WM_MACDRV_RELEASE_REMOTE_LAYER:
+        if ((data = get_win_data(hwnd)))
+        {
+            TRACE("WM_MACDRV_RELEASE_REMOTE_LAYER context_id %u\n", (unsigned int)lp);
+            if (data->cocoa_window) macdrv_window_release_ca_layer_host_view(data->cocoa_window, (unsigned int)lp);
+            release_win_data(data);
+        }
+        return 0;
     }
 
     FIXME("unrecognized window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, (unsigned long)wp, lp);
@@ -1538,6 +1582,18 @@ LRESULT macdrv_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
 }
 
 
+void macdrv_create_remote_layer(void* hwnd_ptr, unsigned int context_id)
+{
+    NtUserPostMessage((HWND)hwnd_ptr, WM_MACDRV_CREATE_REMOTE_LAYER, 0, context_id);
+}
+
+
+void macdrv_release_remote_layer(void* hwnd_ptr, unsigned int context_id)
+{
+    NtUserPostMessage((HWND)hwnd_ptr, WM_MACDRV_RELEASE_REMOTE_LAYER, 0, context_id);
+}
+
+
 /***********************************************************************
  *              WindowPosChanging   (MACDRV.@)
  */
diff --git a/dlls/winevulkan/loader_thunks.c b/dlls/winevulkan/loader_thunks.c
index 2438a7ef2e9b..d8d2365a0e31 100644
--- a/dlls/winevulkan/loader_thunks.c
+++ b/dlls/winevulkan/loader_thunks.c
@@ -4,7 +4,6 @@
  * by the following copyright and permission notice:
  *
  * Copyright 2015-2026 The Khronos Group Inc.
- *
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  * and from Vulkan video.xml file covered
@@ -321,6 +320,30 @@ void WINAPI vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const Vk
     UNIX_CALL(vkCmdBeginDebugUtilsLabelEXT, &params);
 }
 
+VkResult WINAPI vkCmdBeginGpaSampleAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession, const VkGpaSampleBeginInfoAMD *pGpaSampleBeginInfo, uint32_t *pSampleID)
+{
+    struct vkCmdBeginGpaSampleAMD_params params;
+    NTSTATUS status;
+    params.commandBuffer = commandBuffer;
+    params.gpaSession = gpaSession;
+    params.pGpaSampleBeginInfo = pGpaSampleBeginInfo;
+    params.pSampleID = pSampleID;
+    status = UNIX_CALL(vkCmdBeginGpaSampleAMD, &params);
+    assert(!status && "vkCmdBeginGpaSampleAMD");
+    return params.result;
+}
+
+VkResult WINAPI vkCmdBeginGpaSessionAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession)
+{
+    struct vkCmdBeginGpaSessionAMD_params params;
+    NTSTATUS status;
+    params.commandBuffer = commandBuffer;
+    params.gpaSession = gpaSession;
+    status = UNIX_CALL(vkCmdBeginGpaSessionAMD, &params);
+    assert(!status && "vkCmdBeginGpaSessionAMD");
+    return params.result;
+}
+
 void WINAPI vkCmdBeginPerTileExecutionQCOM(VkCommandBuffer commandBuffer, const VkPerTileBeginInfoQCOM *pPerTileBeginInfo)
 {
     struct vkCmdBeginPerTileExecutionQCOM_params params;
@@ -892,6 +915,14 @@ void WINAPI vkCmdCopyBufferToImage2KHR(VkCommandBuffer commandBuffer, const VkCo
     UNIX_CALL(vkCmdCopyBufferToImage2KHR, &params);
 }
 
+void WINAPI vkCmdCopyGpaSessionResultsAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession)
+{
+    struct vkCmdCopyGpaSessionResultsAMD_params params;
+    params.commandBuffer = commandBuffer;
+    params.gpaSession = gpaSession;
+    UNIX_CALL(vkCmdCopyGpaSessionResultsAMD, &params);
+}
+
 void WINAPI vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions)
 {
     struct vkCmdCopyImage_params params;
@@ -1556,6 +1587,26 @@ void WINAPI vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer)
     UNIX_CALL(vkCmdEndDebugUtilsLabelEXT, &params);
 }
 
+void WINAPI vkCmdEndGpaSampleAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession, uint32_t sampleID)
+{
+    struct vkCmdEndGpaSampleAMD_params params;
+    params.commandBuffer = commandBuffer;
+    params.gpaSession = gpaSession;
+    params.sampleID = sampleID;
+    UNIX_CALL(vkCmdEndGpaSampleAMD, &params);
+}
+
+VkResult WINAPI vkCmdEndGpaSessionAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession)
+{
+    struct vkCmdEndGpaSessionAMD_params params;
+    NTSTATUS status;
+    params.commandBuffer = commandBuffer;
+    params.gpaSession = gpaSession;
+    status = UNIX_CALL(vkCmdEndGpaSessionAMD, &params);
+    assert(!status && "vkCmdEndGpaSessionAMD");
+    return params.result;
+}
+
 void WINAPI vkCmdEndPerTileExecutionQCOM(VkCommandBuffer commandBuffer, const VkPerTileEndInfoQCOM *pPerTileEndInfo)
 {
     struct vkCmdEndPerTileExecutionQCOM_params params;
@@ -3580,6 +3631,19 @@ VkResult WINAPI vkCreateFramebuffer(VkDevice device, const VkFramebufferCreateIn
     return params.result;
 }
 
+VkResult WINAPI vkCreateGpaSessionAMD(VkDevice device, const VkGpaSessionCreateInfoAMD *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkGpaSessionAMD *pGpaSession)
+{
+    struct vkCreateGpaSessionAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.pCreateInfo = pCreateInfo;
+    params.pAllocator = pAllocator;
+    params.pGpaSession = pGpaSession;
+    status = UNIX_CALL(vkCreateGpaSessionAMD, &params);
+    assert(!status && "vkCreateGpaSessionAMD");
+    return params.result;
+}
+
 VkResult WINAPI vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines)
 {
     struct vkCreateGraphicsPipelines_params params;
@@ -4253,6 +4317,17 @@ void WINAPI vkDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, con
     assert(!status && "vkDestroyFramebuffer");
 }
 
+void WINAPI vkDestroyGpaSessionAMD(VkDevice device, VkGpaSessionAMD gpaSession, const VkAllocationCallbacks *pAllocator)
+{
+    struct vkDestroyGpaSessionAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.gpaSession = gpaSession;
+    params.pAllocator = pAllocator;
+    status = UNIX_CALL(vkDestroyGpaSessionAMD, &params);
+    assert(!status && "vkDestroyGpaSessionAMD");
+}
+
 void WINAPI vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator)
 {
     struct vkDestroyImage_params params;
@@ -5447,6 +5522,42 @@ void WINAPI vkGetGeneratedCommandsMemoryRequirementsNV(VkDevice device, const Vk
     assert(!status && "vkGetGeneratedCommandsMemoryRequirementsNV");
 }
 
+VkResult WINAPI vkGetGpaDeviceClockInfoAMD(VkDevice device, VkGpaDeviceGetClockInfoAMD *pInfo)
+{
+    struct vkGetGpaDeviceClockInfoAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.pInfo = pInfo;
+    status = UNIX_CALL(vkGetGpaDeviceClockInfoAMD, &params);
+    assert(!status && "vkGetGpaDeviceClockInfoAMD");
+    return params.result;
+}
+
+VkResult WINAPI vkGetGpaSessionResultsAMD(VkDevice device, VkGpaSessionAMD gpaSession, uint32_t sampleID, size_t *pSizeInBytes, void *pData)
+{
+    struct vkGetGpaSessionResultsAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.gpaSession = gpaSession;
+    params.sampleID = sampleID;
+    params.pSizeInBytes = pSizeInBytes;
+    params.pData = pData;
+    status = UNIX_CALL(vkGetGpaSessionResultsAMD, &params);
+    assert(!status && "vkGetGpaSessionResultsAMD");
+    return params.result;
+}
+
+VkResult WINAPI vkGetGpaSessionStatusAMD(VkDevice device, VkGpaSessionAMD gpaSession)
+{
+    struct vkGetGpaSessionStatusAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.gpaSession = gpaSession;
+    status = UNIX_CALL(vkGetGpaSessionStatusAMD, &params);
+    assert(!status && "vkGetGpaSessionStatusAMD");
+    return params.result;
+}
+
 void WINAPI vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements *pMemoryRequirements)
 {
     struct vkGetImageMemoryRequirements_params params;
@@ -7273,6 +7384,17 @@ VkResult WINAPI vkResetFences(VkDevice device, uint32_t fenceCount, const VkFenc
     return params.result;
 }
 
+VkResult WINAPI vkResetGpaSessionAMD(VkDevice device, VkGpaSessionAMD gpaSession)
+{
+    struct vkResetGpaSessionAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.gpaSession = gpaSession;
+    status = UNIX_CALL(vkResetGpaSessionAMD, &params);
+    assert(!status && "vkResetGpaSessionAMD");
+    return params.result;
+}
+
 void WINAPI vkResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount)
 {
     struct vkResetQueryPool_params params;
@@ -7341,6 +7463,17 @@ VkResult WINAPI vkSetEvent(VkDevice device, VkEvent event)
     return params.result;
 }
 
+VkResult WINAPI vkSetGpaDeviceClockModeAMD(VkDevice device, VkGpaDeviceClockModeInfoAMD *pInfo)
+{
+    struct vkSetGpaDeviceClockModeAMD_params params;
+    NTSTATUS status;
+    params.device = device;
+    params.pInfo = pInfo;
+    status = UNIX_CALL(vkSetGpaDeviceClockModeAMD, &params);
+    assert(!status && "vkSetGpaDeviceClockModeAMD");
+    return params.result;
+}
+
 void WINAPI vkSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR *pSwapchains, const VkHdrMetadataEXT *pMetadata)
 {
     struct vkSetHdrMetadataEXT_params params;
@@ -7762,6 +7895,8 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkCmdBeginConditionalRenderingEXT", vkCmdBeginConditionalRenderingEXT},
     {"vkCmdBeginCustomResolveEXT", vkCmdBeginCustomResolveEXT},
     {"vkCmdBeginDebugUtilsLabelEXT", vkCmdBeginDebugUtilsLabelEXT},
+    {"vkCmdBeginGpaSampleAMD", vkCmdBeginGpaSampleAMD},
+    {"vkCmdBeginGpaSessionAMD", vkCmdBeginGpaSessionAMD},
     {"vkCmdBeginPerTileExecutionQCOM", vkCmdBeginPerTileExecutionQCOM},
     {"vkCmdBeginQuery", vkCmdBeginQuery},
     {"vkCmdBeginQueryIndexedEXT", vkCmdBeginQueryIndexedEXT},
@@ -7821,6 +7956,7 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkCmdCopyBufferToImage", vkCmdCopyBufferToImage},
     {"vkCmdCopyBufferToImage2", vkCmdCopyBufferToImage2},
     {"vkCmdCopyBufferToImage2KHR", vkCmdCopyBufferToImage2KHR},
+    {"vkCmdCopyGpaSessionResultsAMD", vkCmdCopyGpaSessionResultsAMD},
     {"vkCmdCopyImage", vkCmdCopyImage},
     {"vkCmdCopyImage2", vkCmdCopyImage2},
     {"vkCmdCopyImage2KHR", vkCmdCopyImage2KHR},
@@ -7888,6 +8024,8 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkCmdEncodeVideoKHR", vkCmdEncodeVideoKHR},
     {"vkCmdEndConditionalRenderingEXT", vkCmdEndConditionalRenderingEXT},
     {"vkCmdEndDebugUtilsLabelEXT", vkCmdEndDebugUtilsLabelEXT},
+    {"vkCmdEndGpaSampleAMD", vkCmdEndGpaSampleAMD},
+    {"vkCmdEndGpaSessionAMD", vkCmdEndGpaSessionAMD},
     {"vkCmdEndPerTileExecutionQCOM", vkCmdEndPerTileExecutionQCOM},
     {"vkCmdEndQuery", vkCmdEndQuery},
     {"vkCmdEndQueryIndexedEXT", vkCmdEndQueryIndexedEXT},
@@ -8097,6 +8235,7 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkCreateEvent", vkCreateEvent},
     {"vkCreateFence", vkCreateFence},
     {"vkCreateFramebuffer", vkCreateFramebuffer},
+    {"vkCreateGpaSessionAMD", vkCreateGpaSessionAMD},
     {"vkCreateGraphicsPipelines", vkCreateGraphicsPipelines},
     {"vkCreateImage", vkCreateImage},
     {"vkCreateImageView", vkCreateImageView},
@@ -8149,6 +8288,7 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkDestroyEvent", vkDestroyEvent},
     {"vkDestroyFence", vkDestroyFence},
     {"vkDestroyFramebuffer", vkDestroyFramebuffer},
+    {"vkDestroyGpaSessionAMD", vkDestroyGpaSessionAMD},
     {"vkDestroyImage", vkDestroyImage},
     {"vkDestroyImageView", vkDestroyImageView},
     {"vkDestroyIndirectCommandsLayoutEXT", vkDestroyIndirectCommandsLayoutEXT},
@@ -8247,6 +8387,9 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkGetFramebufferTilePropertiesQCOM", vkGetFramebufferTilePropertiesQCOM},
     {"vkGetGeneratedCommandsMemoryRequirementsEXT", vkGetGeneratedCommandsMemoryRequirementsEXT},
     {"vkGetGeneratedCommandsMemoryRequirementsNV", vkGetGeneratedCommandsMemoryRequirementsNV},
+    {"vkGetGpaDeviceClockInfoAMD", vkGetGpaDeviceClockInfoAMD},
+    {"vkGetGpaSessionResultsAMD", vkGetGpaSessionResultsAMD},
+    {"vkGetGpaSessionStatusAMD", vkGetGpaSessionStatusAMD},
     {"vkGetImageMemoryRequirements", vkGetImageMemoryRequirements},
     {"vkGetImageMemoryRequirements2", vkGetImageMemoryRequirements2},
     {"vkGetImageMemoryRequirements2KHR", vkGetImageMemoryRequirements2KHR},
@@ -8343,12 +8486,14 @@ static const struct vulkan_func vk_device_dispatch_table[] =
     {"vkResetDescriptorPool", vkResetDescriptorPool},
     {"vkResetEvent", vkResetEvent},
     {"vkResetFences", vkResetFences},
+    {"vkResetGpaSessionAMD", vkResetGpaSessionAMD},
     {"vkResetQueryPool", vkResetQueryPool},
     {"vkResetQueryPoolEXT", vkResetQueryPoolEXT},
     {"vkSetDebugUtilsObjectNameEXT", vkSetDebugUtilsObjectNameEXT},
     {"vkSetDebugUtilsObjectTagEXT", vkSetDebugUtilsObjectTagEXT},
     {"vkSetDeviceMemoryPriorityEXT", vkSetDeviceMemoryPriorityEXT},
     {"vkSetEvent", vkSetEvent},
+    {"vkSetGpaDeviceClockModeAMD", vkSetGpaDeviceClockModeAMD},
     {"vkSetHdrMetadataEXT", vkSetHdrMetadataEXT},
     {"vkSetLatencyMarkerNV", vkSetLatencyMarkerNV},
     {"vkSetLatencySleepModeNV", vkSetLatencySleepModeNV},
diff --git a/dlls/winevulkan/loader_thunks.h b/dlls/winevulkan/loader_thunks.h
index eee26cf2aeb6..069ce5ba4df1 100644
--- a/dlls/winevulkan/loader_thunks.h
+++ b/dlls/winevulkan/loader_thunks.h
@@ -4,7 +4,6 @@
  * by the following copyright and permission notice:
  *
  * Copyright 2015-2026 The Khronos Group Inc.
- *
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  * and from Vulkan video.xml file covered
@@ -50,6 +49,8 @@ enum unix_call
     unix_vkCmdBeginConditionalRenderingEXT,
     unix_vkCmdBeginCustomResolveEXT,
     unix_vkCmdBeginDebugUtilsLabelEXT,
+    unix_vkCmdBeginGpaSampleAMD,
+    unix_vkCmdBeginGpaSessionAMD,
     unix_vkCmdBeginPerTileExecutionQCOM,
     unix_vkCmdBeginQuery,
     unix_vkCmdBeginQueryIndexedEXT,
@@ -109,6 +110,7 @@ enum unix_call
     unix_vkCmdCopyBufferToImage,
     unix_vkCmdCopyBufferToImage2,
     unix_vkCmdCopyBufferToImage2KHR,
+    unix_vkCmdCopyGpaSessionResultsAMD,
     unix_vkCmdCopyImage,
     unix_vkCmdCopyImage2,
     unix_vkCmdCopyImage2KHR,
@@ -176,6 +178,8 @@ enum unix_call
     unix_vkCmdEncodeVideoKHR,
     unix_vkCmdEndConditionalRenderingEXT,
     unix_vkCmdEndDebugUtilsLabelEXT,
+    unix_vkCmdEndGpaSampleAMD,
+    unix_vkCmdEndGpaSessionAMD,
     unix_vkCmdEndPerTileExecutionQCOM,
     unix_vkCmdEndQuery,
     unix_vkCmdEndQueryIndexedEXT,
@@ -388,6 +392,7 @@ enum unix_call
     unix_vkCreateEvent,
     unix_vkCreateFence,
     unix_vkCreateFramebuffer,
+    unix_vkCreateGpaSessionAMD,
     unix_vkCreateGraphicsPipelines,
     unix_vkCreateImage,
     unix_vkCreateImageView,
@@ -445,6 +450,7 @@ enum unix_call
     unix_vkDestroyEvent,
     unix_vkDestroyFence,
     unix_vkDestroyFramebuffer,
+    unix_vkDestroyGpaSessionAMD,
     unix_vkDestroyImage,
     unix_vkDestroyImageView,
     unix_vkDestroyIndirectCommandsLayoutEXT,
@@ -554,6 +560,9 @@ enum unix_call
     unix_vkGetFramebufferTilePropertiesQCOM,
     unix_vkGetGeneratedCommandsMemoryRequirementsEXT,
     unix_vkGetGeneratedCommandsMemoryRequirementsNV,
+    unix_vkGetGpaDeviceClockInfoAMD,
+    unix_vkGetGpaSessionResultsAMD,
+    unix_vkGetGpaSessionStatusAMD,
     unix_vkGetImageMemoryRequirements,
     unix_vkGetImageMemoryRequirements2,
     unix_vkGetImageMemoryRequirements2KHR,
@@ -707,12 +716,14 @@ enum unix_call
     unix_vkResetDescriptorPool,
     unix_vkResetEvent,
     unix_vkResetFences,
+    unix_vkResetGpaSessionAMD,
     unix_vkResetQueryPool,
     unix_vkResetQueryPoolEXT,
     unix_vkSetDebugUtilsObjectNameEXT,
     unix_vkSetDebugUtilsObjectTagEXT,
     unix_vkSetDeviceMemoryPriorityEXT,
     unix_vkSetEvent,
+    unix_vkSetGpaDeviceClockModeAMD,
     unix_vkSetHdrMetadataEXT,
     unix_vkSetLatencyMarkerNV,
     unix_vkSetLatencySleepModeNV,
@@ -963,6 +974,22 @@ struct vkCmdBeginDebugUtilsLabelEXT_params
     const VkDebugUtilsLabelEXT *pLabelInfo;
 };
 
+struct vkCmdBeginGpaSampleAMD_params
+{
+    VkCommandBuffer commandBuffer;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    const VkGpaSampleBeginInfoAMD *pGpaSampleBeginInfo;
+    uint32_t *pSampleID;
+    VkResult result;
+};
+
+struct vkCmdBeginGpaSessionAMD_params
+{
+    VkCommandBuffer commandBuffer;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    VkResult result;
+};
+
 struct vkCmdBeginPerTileExecutionQCOM_params
 {
     VkCommandBuffer commandBuffer;
@@ -1416,6 +1443,12 @@ struct vkCmdCopyBufferToImage2KHR_params
     const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo;
 };
 
+struct vkCmdCopyGpaSessionResultsAMD_params
+{
+    VkCommandBuffer commandBuffer;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+};
+
 struct vkCmdCopyImage_params
 {
     VkCommandBuffer commandBuffer;
@@ -1946,6 +1979,20 @@ struct vkCmdEndDebugUtilsLabelEXT_params
     VkCommandBuffer commandBuffer;
 };
 
+struct vkCmdEndGpaSampleAMD_params
+{
+    VkCommandBuffer commandBuffer;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    uint32_t sampleID;
+};
+
+struct vkCmdEndGpaSessionAMD_params
+{
+    VkCommandBuffer commandBuffer;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    VkResult result;
+};
+
 struct vkCmdEndPerTileExecutionQCOM_params
 {
     VkCommandBuffer commandBuffer;
@@ -3494,6 +3541,15 @@ struct vkCreateFramebuffer_params
     VkResult result;
 };
 
+struct vkCreateGpaSessionAMD_params
+{
+    VkDevice device;
+    const VkGpaSessionCreateInfoAMD *pCreateInfo;
+    const VkAllocationCallbacks *pAllocator;
+    VkGpaSessionAMD *pGpaSession;
+    VkResult result;
+};
+
 struct vkCreateGraphicsPipelines_params
 {
     VkDevice device;
@@ -3972,6 +4028,13 @@ struct vkDestroyFramebuffer_params
     const VkAllocationCallbacks *pAllocator;
 };
 
+struct vkDestroyGpaSessionAMD_params
+{
+    VkDevice device;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    const VkAllocationCallbacks *pAllocator;
+};
+
 struct vkDestroyImage_params
 {
     VkDevice device;
@@ -4789,6 +4852,30 @@ struct vkGetGeneratedCommandsMemoryRequirementsNV_params
     VkMemoryRequirements2 *pMemoryRequirements;
 };
 
+struct vkGetGpaDeviceClockInfoAMD_params
+{
+    VkDevice device;
+    VkGpaDeviceGetClockInfoAMD *pInfo;
+    VkResult result;
+};
+
+struct vkGetGpaSessionResultsAMD_params
+{
+    VkDevice device;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    uint32_t sampleID;
+    size_t *pSizeInBytes;
+    void *pData;
+    VkResult result;
+};
+
+struct vkGetGpaSessionStatusAMD_params
+{
+    VkDevice device;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    VkResult result;
+};
+
 struct vkGetImageMemoryRequirements_params
 {
     VkDevice device;
@@ -6003,6 +6090,13 @@ struct vkResetFences_params
     VkResult result;
 };
 
+struct vkResetGpaSessionAMD_params
+{
+    VkDevice device;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    VkResult result;
+};
+
 struct vkResetQueryPool_params
 {
     VkDevice device;
@@ -6047,6 +6141,13 @@ struct vkSetEvent_params
     VkResult result;
 };
 
+struct vkSetGpaDeviceClockModeAMD_params
+{
+    VkDevice device;
+    VkGpaDeviceClockModeInfoAMD *pInfo;
+    VkResult result;
+};
+
 struct vkSetHdrMetadataEXT_params
 {
     VkDevice device;
diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan
index 90fb8d834bdf..70273a192235 100755
--- a/dlls/winevulkan/make_vulkan
+++ b/dlls/winevulkan/make_vulkan
@@ -62,7 +62,7 @@ from enum import Enum
 LOGGER = logging.Logger("vulkan")
 LOGGER.addHandler(logging.StreamHandler())
 
-VK_XML_VERSION = "1.4.350"
+VK_XML_VERSION = "1.4.353"
 
 # Filenames to create.
 WINE_VULKAN_H = "../../include/wine/vulkan.h"
diff --git a/dlls/winevulkan/vulkan_thunks.c b/dlls/winevulkan/vulkan_thunks.c
index d081d7c23d5b..7ed99c4ff3cc 100644
--- a/dlls/winevulkan/vulkan_thunks.c
+++ b/dlls/winevulkan/vulkan_thunks.c
@@ -4,7 +4,6 @@
  * by the following copyright and permission notice:
  *
  * Copyright 2015-2026 The Khronos Group Inc.
- *
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  * and from Vulkan video.xml file covered
@@ -2001,6 +2000,18 @@ typedef struct VkAccelerationStructureGeometryLinearSweptSpheresDataNV32
     VkRayTracingLssPrimitiveEndCapsModeNV endCapsMode;
 } VkAccelerationStructureGeometryLinearSweptSpheresDataNV32;
 
+typedef struct VkAccelerationStructureGeometryMicromapDataKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t usageCountsCount;
+    PTR32 pUsageCounts;
+    PTR32 ppUsageCounts;
+    VkDeviceAddress DECLSPEC_ALIGN(8) data;
+    VkDeviceAddress DECLSPEC_ALIGN(8) triangleArray;
+    VkDeviceSize DECLSPEC_ALIGN(8) triangleArrayStride;
+} VkAccelerationStructureGeometryMicromapDataKHR32;
+
 typedef struct VkAccelerationStructureGeometryMotionTrianglesDataNV32
 {
     VkStructureType sType;
@@ -2053,6 +2064,17 @@ typedef struct VkAccelerationStructureTrianglesOpacityMicromapEXT32
     VkMicromapEXT DECLSPEC_ALIGN(8) micromap;
 } VkAccelerationStructureTrianglesOpacityMicromapEXT32;
 
+typedef struct VkAccelerationStructureTrianglesOpacityMicromapKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkIndexType indexType;
+    VkDeviceAddress DECLSPEC_ALIGN(8) indexBuffer;
+    VkDeviceSize DECLSPEC_ALIGN(8) indexStride;
+    uint32_t baseTriangle;
+    VkAccelerationStructureKHR DECLSPEC_ALIGN(8) micromap;
+} VkAccelerationStructureTrianglesOpacityMicromapKHR32;
+
 typedef struct VkAccelerationStructureVersionInfoKHR32
 {
     VkStructureType sType;
@@ -3817,6 +3839,15 @@ typedef struct VkFormatProperties332
 } VkFormatProperties332;
 typedef VkFormatProperties332 VkFormatProperties3KHR32;
 
+typedef struct VkFormatProperties4KHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkFormatFeatureFlags4KHR DECLSPEC_ALIGN(8) linearTilingFeatures;
+    VkFormatFeatureFlags4KHR DECLSPEC_ALIGN(8) optimalTilingFeatures;
+    VkFormatFeatureFlags4KHR DECLSPEC_ALIGN(8) bufferFeatures;
+} VkFormatProperties4KHR32;
+
 typedef struct VkFragmentShadingRateAttachmentInfoKHR32
 {
     VkStructureType sType;
@@ -3958,6 +3989,52 @@ typedef struct VkGetLatencyMarkerInfoNV32
     PTR32 pTimings;
 } VkGetLatencyMarkerInfoNV32;
 
+typedef struct VkGpaDeviceClockModeInfoAMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkGpaDeviceClockModeAMD clockMode;
+    float memoryClockRatioToPeak;
+    float engineClockRatioToPeak;
+} VkGpaDeviceClockModeInfoAMD32;
+
+typedef struct VkGpaDeviceGetClockInfoAMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    float memoryClockRatioToPeak;
+    float engineClockRatioToPeak;
+    uint32_t memoryClockFrequency;
+    uint32_t engineClockFrequency;
+} VkGpaDeviceGetClockInfoAMD32;
+
+typedef struct VkGpaSampleBeginInfoAMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkGpaSampleTypeAMD sampleType;
+    VkBool32 sampleInternalOperations;
+    VkBool32 cacheFlushOnCounterCollection;
+    VkBool32 sqShaderMaskEnable;
+    VkGpaSqShaderStageFlagsAMD sqShaderMask;
+    uint32_t perfCounterCount;
+    PTR32 pPerfCounters;
+    uint32_t streamingPerfTraceSampleInterval;
+    VkDeviceSize DECLSPEC_ALIGN(8) perfCounterDeviceMemoryLimit;
+    VkBool32 sqThreadTraceEnable;
+    VkBool32 sqThreadTraceSuppressInstructionTokens;
+    VkDeviceSize DECLSPEC_ALIGN(8) sqThreadTraceDeviceMemoryLimit;
+    VkPipelineStageFlags timingPreSample;
+    VkPipelineStageFlags timingPostSample;
+} VkGpaSampleBeginInfoAMD32;
+
+typedef struct VkGpaSessionCreateInfoAMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkGpaSessionAMD DECLSPEC_ALIGN(8) secondaryCopySource;
+} VkGpaSessionCreateInfoAMD32;
+
 typedef struct VkGraphicsPipelineCreateInfo32
 {
     VkStructureType sType;
@@ -4077,6 +4154,13 @@ typedef struct VkImageCompressionPropertiesEXT32
     VkImageCompressionFixedRateFlagsEXT imageCompressionFixedRateFlags;
 } VkImageCompressionPropertiesEXT32;
 
+typedef struct VkImageCreateFlags2CreateInfoKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkImageCreateFlags2KHR DECLSPEC_ALIGN(8) flags;
+} VkImageCreateFlags2CreateInfoKHR32;
+
 typedef struct VkImageFormatListCreateInfo32
 {
     VkStructureType sType;
@@ -4132,6 +4216,13 @@ typedef struct VkImageSparseMemoryRequirementsInfo232
 } VkImageSparseMemoryRequirementsInfo232;
 typedef VkImageSparseMemoryRequirementsInfo232 VkImageSparseMemoryRequirementsInfo2KHR32;
 
+typedef struct VkImageStencilUsage2CreateInfoKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkImageUsageFlags2KHR DECLSPEC_ALIGN(8) stencilUsage;
+} VkImageStencilUsage2CreateInfoKHR32;
+
 typedef struct VkImageStencilUsageCreateInfo32
 {
     VkStructureType sType;
@@ -4147,6 +4238,13 @@ typedef struct VkImageSwapchainCreateInfoKHR32
     VkSwapchainKHR DECLSPEC_ALIGN(8) swapchain;
 } VkImageSwapchainCreateInfoKHR32;
 
+typedef struct VkImageUsageFlags2CreateInfoKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkImageUsageFlags2KHR DECLSPEC_ALIGN(8) usage;
+} VkImageUsageFlags2CreateInfoKHR32;
+
 typedef struct VkImageViewASTCDecodeModeEXT32
 {
     VkStructureType sType;
@@ -4202,6 +4300,13 @@ typedef struct VkImageViewSlicedCreateInfoEXT32
     uint32_t sliceCount;
 } VkImageViewSlicedCreateInfoEXT32;
 
+typedef struct VkImageViewUsage2CreateInfoKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkImageUsageFlags2KHR DECLSPEC_ALIGN(8) usage;
+} VkImageViewUsage2CreateInfoKHR32;
+
 typedef struct VkImageViewUsageCreateInfo32
 {
     VkStructureType sType;
@@ -5044,6 +5149,13 @@ typedef struct VkPhysicalDeviceCooperativeMatrixConversionFeaturesQCOM32
     VkBool32 cooperativeMatrixConversion;
 } VkPhysicalDeviceCooperativeMatrixConversionFeaturesQCOM32;
 
+typedef struct VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 cooperativeMatrixDecodeVector;
+} VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32;
+
 typedef struct VkPhysicalDeviceCooperativeMatrixFeaturesKHR32
 {
     VkStructureType sType;
@@ -5551,6 +5663,13 @@ typedef struct VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT32
     VkBool32 dynamicRenderingUnusedAttachments;
 } VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT32;
 
+typedef struct VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 elapsedTimerQuery;
+} VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32;
+
 typedef struct VkPhysicalDeviceExclusiveScissorFeaturesNV32
 {
     VkStructureType sType;
@@ -5618,6 +5737,13 @@ typedef struct VkPhysicalDeviceExtendedDynamicStateFeaturesEXT32
     VkBool32 extendedDynamicState;
 } VkPhysicalDeviceExtendedDynamicStateFeaturesEXT32;
 
+typedef struct VkPhysicalDeviceExtendedFlagsFeaturesKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 extendedFlags;
+} VkPhysicalDeviceExtendedFlagsFeaturesKHR32;
+
 typedef struct VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV32
 {
     VkStructureType sType;
@@ -5908,6 +6034,34 @@ typedef struct VkPhysicalDeviceGlobalPriorityQueryFeatures32
 } VkPhysicalDeviceGlobalPriorityQueryFeatures32;
 typedef VkPhysicalDeviceGlobalPriorityQueryFeatures32 VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR32, VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT32;
 
+typedef struct VkPhysicalDeviceGpaFeaturesAMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 perfCounters;
+    VkBool32 streamingPerfCounters;
+    VkBool32 sqThreadTracing;
+    VkBool32 clockModes;
+} VkPhysicalDeviceGpaFeaturesAMD32;
+
+typedef struct VkPhysicalDeviceGpaProperties2AMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t revisionId;
+} VkPhysicalDeviceGpaProperties2AMD32;
+
+typedef struct VkPhysicalDeviceGpaPropertiesAMD32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkPhysicalDeviceGpaPropertiesFlagsAMD flags;
+    VkDeviceSize DECLSPEC_ALIGN(8) maxSqttSeBufferSize;
+    uint32_t shaderEngineCount;
+    uint32_t perfBlockCount;
+    PTR32 pPerfBlocks;
+} VkPhysicalDeviceGpaPropertiesAMD32;
+
 typedef struct VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT32
 {
     VkStructureType sType;
@@ -6043,6 +6197,15 @@ typedef struct VkPhysicalDeviceImageProcessing2PropertiesQCOM32
     VkExtent2D maxBlockMatchWindow;
 } VkPhysicalDeviceImageProcessing2PropertiesQCOM32;
 
+typedef struct VkPhysicalDeviceImageProcessing3FeaturesQCOM32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 imageGatherLinear;
+    VkBool32 imageGatherExtendedModes;
+    VkBool32 blockMatchExtendedClampToEdge;
+} VkPhysicalDeviceImageProcessing3FeaturesQCOM32;
+
 typedef struct VkPhysicalDeviceImageProcessingFeaturesQCOM32
 {
     VkStructureType sType;
@@ -6483,6 +6646,13 @@ typedef struct VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT32
     VkBool32 multisampledRenderToSingleSampled;
 } VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT32;
 
+typedef struct VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 multisampledRenderToSwapchain;
+} VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32;
+
 typedef struct VkPhysicalDeviceMultiviewFeatures32
 {
     VkStructureType sType;
@@ -6556,6 +6726,13 @@ typedef struct VkPhysicalDeviceOpacityMicromapFeaturesEXT32
     VkBool32 micromapHostCommands;
 } VkPhysicalDeviceOpacityMicromapFeaturesEXT32;
 
+typedef struct VkPhysicalDeviceOpacityMicromapFeaturesKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 micromap;
+} VkPhysicalDeviceOpacityMicromapFeaturesKHR32;
+
 typedef struct VkPhysicalDeviceOpacityMicromapPropertiesEXT32
 {
     VkStructureType sType;
@@ -6564,6 +6741,16 @@ typedef struct VkPhysicalDeviceOpacityMicromapPropertiesEXT32
     uint32_t maxOpacity4StateSubdivisionLevel;
 } VkPhysicalDeviceOpacityMicromapPropertiesEXT32;
 
+typedef struct VkPhysicalDeviceOpacityMicromapPropertiesKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t maxOpacity2StateSubdivisionLevel;
+    uint32_t maxOpacity4StateSubdivisionLevel;
+    uint32_t maxOpacityLossy4StateSubdivisionLevel;
+    uint64_t DECLSPEC_ALIGN(8) maxMicromapTriangles;
+} VkPhysicalDeviceOpacityMicromapPropertiesKHR32;
+
 typedef struct VkPhysicalDeviceOpticalFlowFeaturesNV32
 {
     VkStructureType sType;
@@ -7518,6 +7705,20 @@ typedef struct VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT32
     uint8_t shaderModuleIdentifierAlgorithmUUID[VK_UUID_SIZE];
 } VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT32;
 
+typedef struct VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 shaderMultipleWaitQueues;
+} VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32;
+
+typedef struct VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t maxShaderWaitQueues;
+} VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM32;
+
 typedef struct VkPhysicalDeviceShaderObjectFeaturesEXT32
 {
     VkStructureType sType;
@@ -7569,6 +7770,20 @@ typedef struct VkPhysicalDeviceShaderSMBuiltinsPropertiesNV32
     uint32_t shaderWarpsPerSM;
 } VkPhysicalDeviceShaderSMBuiltinsPropertiesNV32;
 
+typedef struct VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 shaderSplitBarrier;
+} VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32;
+
+typedef struct VkPhysicalDeviceShaderSplitBarrierPropertiesEXT32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t splitBarrierReservedSharedMemory;
+} VkPhysicalDeviceShaderSplitBarrierPropertiesEXT32;
+
 typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures32
 {
     VkStructureType sType;
@@ -7996,6 +8211,13 @@ typedef struct VkPhysicalDeviceVideoEncodeAV1FeaturesKHR32
     VkBool32 videoEncodeAV1;
 } VkPhysicalDeviceVideoEncodeAV1FeaturesKHR32;
 
+typedef struct VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkBool32 videoEncodeFeedback2;
+} VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32;
+
 typedef struct VkPhysicalDeviceVideoEncodeIntraRefreshFeaturesKHR32
 {
     VkStructureType sType;
@@ -8925,6 +9147,14 @@ typedef struct VkQueryPoolVideoEncodeFeedbackCreateInfoKHR32
     VkVideoEncodeFeedbackFlagsKHR encodeFeedbackFlags;
 } VkQueryPoolVideoEncodeFeedbackCreateInfoKHR32;
 
+typedef struct VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t maxPerPartitionFeedbackEntries;
+    VkVideoEncodePerPartitionFeedbackFlagsKHR perPartitionEncodeFeedbackFlags;
+} VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR32;
+
 typedef struct VkQueueFamilyCheckpointProperties2NV32
 {
     VkStructureType sType;
@@ -9553,6 +9783,13 @@ typedef struct VkShaderModuleValidationCacheCreateInfoEXT32
     VkValidationCacheEXT DECLSPEC_ALIGN(8) validationCache;
 } VkShaderModuleValidationCacheCreateInfoEXT32;
 
+typedef struct VkSharedPresentSurfaceCapabilities2KHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkImageUsageFlags2KHR DECLSPEC_ALIGN(8) sharedPresentSupportedUsageFlags;
+} VkSharedPresentSurfaceCapabilities2KHR32;
+
 typedef struct VkSparseImageFormatProperties232
 {
     VkStructureType sType;
@@ -9754,6 +9991,13 @@ typedef struct VkSwapchainCreateInfoKHR32
     VkSwapchainKHR DECLSPEC_ALIGN(8) oldSwapchain;
 } VkSwapchainCreateInfoKHR32;
 
+typedef struct VkSwapchainFlagsSurfaceCapabilitiesEXT32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    VkSwapchainCreateFlagsKHR swapchainSupportedFlags;
+} VkSwapchainFlagsSurfaceCapabilitiesEXT32;
+
 typedef struct VkSwapchainLatencyCreateInfoNV32
 {
     VkStructureType sType;
@@ -10280,6 +10524,14 @@ typedef struct VkVideoEncodeCapabilitiesKHR32
     VkVideoEncodeFeedbackFlagsKHR supportedEncodeFeedbackFlags;
 } VkVideoEncodeCapabilitiesKHR32;
 
+typedef struct VkVideoEncodeFeedback2CapabilitiesKHR32
+{
+    VkStructureType sType;
+    PTR32 pNext;
+    uint32_t maxPerPartitionFeedbackEntries;
+    VkVideoEncodePerPartitionFeedbackFlagsKHR supportedPerPartitionEncodeFeedbackFlags;
+} VkVideoEncodeFeedback2CapabilitiesKHR32;
+
 typedef struct VkVideoEncodeH264CapabilitiesKHR32
 {
     VkStructureType sType;
@@ -12285,6 +12537,21 @@ static void convert_VkAccelerationStructureGeometryTrianglesDataKHR_win32_to_hos
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_TRIANGLES_OPACITY_MICROMAP_KHR:
+        {
+            VkAccelerationStructureTrianglesOpacityMicromapKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkAccelerationStructureTrianglesOpacityMicromapKHR32 *in_ext = (const VkAccelerationStructureTrianglesOpacityMicromapKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_TRIANGLES_OPACITY_MICROMAP_KHR;
+            out_ext->pNext = NULL;
+            out_ext->indexType = in_ext->indexType;
+            out_ext->indexBuffer = in_ext->indexBuffer;
+            out_ext->indexStride = in_ext->indexStride;
+            out_ext->baseTriangle = in_ext->baseTriangle;
+            out_ext->micromap = in_ext->micromap;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         default:
             FIXME("Unhandled sType %u.\n", in_header->sType);
             break;
@@ -12328,6 +12595,22 @@ static void convert_VkAccelerationStructureGeometryDataKHR_win32_to_host(struct
         convert_VkAccelerationStructureGeometryInstancesDataKHR_win32_to_host(&in->instances, &out->instances);
 }
 
+static const VkMicromapUsageKHR * const*convert_VkMicromapUsageKHR_pointer_array_win32_to_host(struct conversion_context *ctx, const PTR32 *in, uint32_t count)
+{
+    VkMicromapUsageKHR **out;
+    unsigned int i;
+
+    if (!in || !count) return NULL;
+
+    out = conversion_context_alloc(ctx, count * sizeof(*out));
+    for (i = 0; i < count; i++)
+    {
+        out[i] = UlongToPtr(in[i]);
+    }
+
+    return (void *)out;
+}
+
 static void convert_VkAccelerationStructureGeometryKHR_win32_to_host(struct conversion_context *ctx, const VkAccelerationStructureGeometryKHR32 *in, VkAccelerationStructureGeometryKHR *out)
 {
     const VkBaseInStructure32 *in_header;
@@ -12366,6 +12649,22 @@ static void convert_VkAccelerationStructureGeometryKHR_win32_to_host(struct conv
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_MICROMAP_DATA_KHR:
+        {
+            VkAccelerationStructureGeometryMicromapDataKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkAccelerationStructureGeometryMicromapDataKHR32 *in_ext = (const VkAccelerationStructureGeometryMicromapDataKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_MICROMAP_DATA_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usageCountsCount = in_ext->usageCountsCount;
+            out_ext->pUsageCounts = UlongToPtr(in_ext->pUsageCounts);
+            out_ext->ppUsageCounts = convert_VkMicromapUsageKHR_pointer_array_win32_to_host(ctx, (const PTR32 *)UlongToPtr(in_ext->ppUsageCounts), in_ext->usageCountsCount);
+            out_ext->data = in_ext->data;
+            out_ext->triangleArray = in_ext->triangleArray;
+            out_ext->triangleArrayStride = in_ext->triangleArrayStride;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_SPHERES_DATA_NV:
         {
             VkAccelerationStructureGeometrySpheresDataNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -12566,6 +12865,30 @@ static void convert_VkDebugUtilsLabelEXT_win32_to_host(const VkDebugUtilsLabelEX
         FIXME("Unexpected pNext\n");
 }
 
+static void convert_VkGpaSampleBeginInfoAMD_win32_to_host(const VkGpaSampleBeginInfoAMD32 *in, VkGpaSampleBeginInfoAMD *out)
+{
+    if (!in) return;
+
+    out->sType = in->sType;
+    out->pNext = NULL;
+    out->sampleType = in->sampleType;
+    out->sampleInternalOperations = in->sampleInternalOperations;
+    out->cacheFlushOnCounterCollection = in->cacheFlushOnCounterCollection;
+    out->sqShaderMaskEnable = in->sqShaderMaskEnable;
+    out->sqShaderMask = in->sqShaderMask;
+    out->perfCounterCount = in->perfCounterCount;
+    out->pPerfCounters = UlongToPtr(in->pPerfCounters);
+    out->streamingPerfTraceSampleInterval = in->streamingPerfTraceSampleInterval;
+    out->perfCounterDeviceMemoryLimit = in->perfCounterDeviceMemoryLimit;
+    out->sqThreadTraceEnable = in->sqThreadTraceEnable;
+    out->sqThreadTraceSuppressInstructionTokens = in->sqThreadTraceSuppressInstructionTokens;
+    out->sqThreadTraceDeviceMemoryLimit = in->sqThreadTraceDeviceMemoryLimit;
+    out->timingPreSample = in->timingPreSample;
+    out->timingPostSample = in->timingPostSample;
+    if (in->pNext)
+        FIXME("Unexpected pNext\n");
+}
+
 static void convert_VkPerTileBeginInfoQCOM_win32_to_host(const VkPerTileBeginInfoQCOM32 *in, VkPerTileBeginInfoQCOM *out)
 {
     if (!in) return;
@@ -19078,6 +19401,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV:
+        {
+            VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *in_ext = (const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV;
+            out_ext->pNext = NULL;
+            out_ext->cooperativeMatrixDecodeVector = in_ext->cooperativeMatrixDecodeVector;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR:
         {
             VkPhysicalDeviceCooperativeMatrixFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -19501,6 +19835,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *in_ext = (const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->elapsedTimerQuery = in_ext->elapsedTimerQuery;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV:
         {
             VkPhysicalDeviceExclusiveScissorFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -19577,6 +19922,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR:
+        {
+            VkPhysicalDeviceExtendedFlagsFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceExtendedFlagsFeaturesKHR *in_ext = (const VkPhysicalDeviceExtendedFlagsFeaturesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->extendedFlags = in_ext->extendedFlags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV:
         {
             VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -19754,6 +20110,20 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD:
+        {
+            VkPhysicalDeviceGpaFeaturesAMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceGpaFeaturesAMD *in_ext = (const VkPhysicalDeviceGpaFeaturesAMD *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD;
+            out_ext->pNext = NULL;
+            out_ext->perfCounters = in_ext->perfCounters;
+            out_ext->streamingPerfCounters = in_ext->streamingPerfCounters;
+            out_ext->sqThreadTracing = in_ext->sqThreadTracing;
+            out_ext->clockModes = in_ext->clockModes;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -19854,6 +20224,19 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceImageProcessing3FeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceImageProcessing3FeaturesQCOM *in_ext = (const VkPhysicalDeviceImageProcessing3FeaturesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->imageGatherLinear = in_ext->imageGatherLinear;
+            out_ext->imageGatherExtendedModes = in_ext->imageGatherExtendedModes;
+            out_ext->blockMatchExtendedClampToEdge = in_ext->blockMatchExtendedClampToEdge;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM:
         {
             VkPhysicalDeviceImageProcessingFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -20175,6 +20558,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT:
+        {
+            VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *in_ext = (const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT;
+            out_ext->pNext = NULL;
+            out_ext->multisampledRenderToSwapchain = in_ext->multisampledRenderToSwapchain;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES:
         {
             VkPhysicalDeviceMultiviewFeatures *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -20258,6 +20652,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceOpacityMicromapFeaturesKHR *in_ext = (const VkPhysicalDeviceOpacityMicromapFeaturesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->micromap = in_ext->micromap;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV:
         {
             VkPhysicalDeviceOpticalFlowFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -21169,6 +21574,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *in_ext = (const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->shaderMultipleWaitQueues = in_ext->shaderMultipleWaitQueues;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT:
         {
             VkPhysicalDeviceShaderObjectFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -21224,6 +21640,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *in_ext = (const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT;
+            out_ext->pNext = NULL;
+            out_ext->shaderSplitBarrier = in_ext->shaderSplitBarrier;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES:
         {
             VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -21603,6 +22030,17 @@ static void convert_VkDeviceCreateInfo_win64_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR:
+        {
+            VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *in_ext = (const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->videoEncodeFeedback2 = in_ext->videoEncodeFeedback2;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_INTRA_REFRESH_FEATURES_KHR:
         {
             VkPhysicalDeviceVideoEncodeIntraRefreshFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -22346,6 +22784,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV:
+        {
+            VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32 *in_ext = (const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV;
+            out_ext->pNext = NULL;
+            out_ext->cooperativeMatrixDecodeVector = in_ext->cooperativeMatrixDecodeVector;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR:
         {
             VkPhysicalDeviceCooperativeMatrixFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -22769,6 +23218,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32 *in_ext = (const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->elapsedTimerQuery = in_ext->elapsedTimerQuery;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV:
         {
             VkPhysicalDeviceExclusiveScissorFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -22845,6 +23305,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR:
+        {
+            VkPhysicalDeviceExtendedFlagsFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceExtendedFlagsFeaturesKHR32 *in_ext = (const VkPhysicalDeviceExtendedFlagsFeaturesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->extendedFlags = in_ext->extendedFlags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV:
         {
             VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -23022,6 +23493,20 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD:
+        {
+            VkPhysicalDeviceGpaFeaturesAMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceGpaFeaturesAMD32 *in_ext = (const VkPhysicalDeviceGpaFeaturesAMD32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD;
+            out_ext->pNext = NULL;
+            out_ext->perfCounters = in_ext->perfCounters;
+            out_ext->streamingPerfCounters = in_ext->streamingPerfCounters;
+            out_ext->sqThreadTracing = in_ext->sqThreadTracing;
+            out_ext->clockModes = in_ext->clockModes;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -23122,6 +23607,19 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceImageProcessing3FeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceImageProcessing3FeaturesQCOM32 *in_ext = (const VkPhysicalDeviceImageProcessing3FeaturesQCOM32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->imageGatherLinear = in_ext->imageGatherLinear;
+            out_ext->imageGatherExtendedModes = in_ext->imageGatherExtendedModes;
+            out_ext->blockMatchExtendedClampToEdge = in_ext->blockMatchExtendedClampToEdge;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM:
         {
             VkPhysicalDeviceImageProcessingFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -23443,6 +23941,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT:
+        {
+            VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32 *in_ext = (const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT;
+            out_ext->pNext = NULL;
+            out_ext->multisampledRenderToSwapchain = in_ext->multisampledRenderToSwapchain;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES:
         {
             VkPhysicalDeviceMultiviewFeatures *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -23526,6 +24035,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceOpacityMicromapFeaturesKHR32 *in_ext = (const VkPhysicalDeviceOpacityMicromapFeaturesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->micromap = in_ext->micromap;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV:
         {
             VkPhysicalDeviceOpticalFlowFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -24437,6 +24957,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32 *in_ext = (const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->shaderMultipleWaitQueues = in_ext->shaderMultipleWaitQueues;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT:
         {
             VkPhysicalDeviceShaderObjectFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -24492,6 +25023,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32 *in_ext = (const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT;
+            out_ext->pNext = NULL;
+            out_ext->shaderSplitBarrier = in_ext->shaderSplitBarrier;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES:
         {
             VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -24871,6 +25413,17 @@ static void convert_VkDeviceCreateInfo_win32_to_host(struct conversion_context *
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR:
+        {
+            VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32 *in_ext = (const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->videoEncodeFeedback2 = in_ext->videoEncodeFeedback2;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_INTRA_REFRESH_FEATURES_KHR:
         {
             VkPhysicalDeviceVideoEncodeIntraRefreshFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -25244,8 +25797,11 @@ static void convert_VkFenceCreateInfo_win32_to_host(struct conversion_context *c
     }
 }
 
-static void convert_VkFramebufferAttachmentImageInfo_win32_to_host(const VkFramebufferAttachmentImageInfo32 *in, VkFramebufferAttachmentImageInfo *out)
+static void convert_VkFramebufferAttachmentImageInfo_win32_to_host(struct conversion_context *ctx, const VkFramebufferAttachmentImageInfo32 *in, VkFramebufferAttachmentImageInfo *out)
 {
+    const VkBaseInStructure32 *in_header;
+    VkBaseOutStructure *out_header = (void *)out;
+
     if (!in) return;
 
     out->sType = in->sType;
@@ -25257,8 +25813,38 @@ static void convert_VkFramebufferAttachmentImageInfo_win32_to_host(const VkFrame
     out->layerCount = in->layerCount;
     out->viewFormatCount = in->viewFormatCount;
     out->pViewFormats = UlongToPtr(in->pViewFormats);
-    if (in->pNext)
-        FIXME("Unexpected pNext\n");
+
+    for (in_header = UlongToPtr(in->pNext); in_header; in_header = UlongToPtr(in_header->pNext))
+    {
+        switch (in_header->sType)
+        {
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageCreateFlags2CreateInfoKHR32 *in_ext = (const VkImageCreateFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->flags = in_ext->flags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
+        default:
+            FIXME("Unhandled sType %u.\n", in_header->sType);
+            break;
+        }
+    }
 }
 
 static const VkFramebufferAttachmentImageInfo *convert_VkFramebufferAttachmentImageInfo_array_win32_to_host(struct conversion_context *ctx, const VkFramebufferAttachmentImageInfo32 *in, uint32_t count)
@@ -25271,7 +25857,7 @@ static const VkFramebufferAttachmentImageInfo *convert_VkFramebufferAttachmentIm
     out = conversion_context_alloc(ctx, count * sizeof(*out));
     for (i = 0; i < count; i++)
     {
-        convert_VkFramebufferAttachmentImageInfo_win32_to_host(&in[i], &out[i]);
+        convert_VkFramebufferAttachmentImageInfo_win32_to_host(ctx, &in[i], &out[i]);
     }
 
     return out;
@@ -25317,6 +25903,17 @@ static void convert_VkFramebufferCreateInfo_win32_to_host(struct conversion_cont
     }
 }
 
+static void convert_VkGpaSessionCreateInfoAMD_win32_to_host(const VkGpaSessionCreateInfoAMD32 *in, VkGpaSessionCreateInfoAMD *out)
+{
+    if (!in) return;
+
+    out->sType = in->sType;
+    out->pNext = NULL;
+    out->secondaryCopySource = in->secondaryCopySource;
+    if (in->pNext)
+        FIXME("Unexpected pNext\n");
+}
+
 static const VkPipelineShaderStageCreateInfo *convert_VkPipelineShaderStageCreateInfo_array_win32_to_host(struct conversion_context *ctx, const VkPipelineShaderStageCreateInfo32 *in, uint32_t count)
 {
     VkPipelineShaderStageCreateInfo *out;
@@ -26394,6 +26991,17 @@ static void convert_VkImageCreateInfo_win64_to_host(struct conversion_context *c
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageCreateFlags2CreateInfoKHR *in_ext = (const VkImageCreateFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->flags = in_ext->flags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO:
         {
             VkImageFormatListCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -26406,6 +27014,17 @@ static void convert_VkImageCreateInfo_win64_to_host(struct conversion_context *c
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR:
+        {
+            VkImageStencilUsage2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageStencilUsage2CreateInfoKHR *in_ext = (const VkImageStencilUsage2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->stencilUsage = in_ext->stencilUsage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO:
         {
             VkImageStencilUsageCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -26428,6 +27047,17 @@ static void convert_VkImageCreateInfo_win64_to_host(struct conversion_context *c
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR *in_ext = (const VkImageUsageFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_OPAQUE_CAPTURE_DATA_CREATE_INFO_EXT:
         {
             VkOpaqueCaptureDataCreateInfoEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -26581,6 +27211,17 @@ static void convert_VkImageCreateInfo_win32_to_host(struct conversion_context *c
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageCreateFlags2CreateInfoKHR32 *in_ext = (const VkImageCreateFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->flags = in_ext->flags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO:
         {
             VkImageFormatListCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -26593,6 +27234,17 @@ static void convert_VkImageCreateInfo_win32_to_host(struct conversion_context *c
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR:
+        {
+            VkImageStencilUsage2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageStencilUsage2CreateInfoKHR32 *in_ext = (const VkImageStencilUsage2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->stencilUsage = in_ext->stencilUsage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO:
         {
             VkImageStencilUsageCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -26615,6 +27267,17 @@ static void convert_VkImageCreateInfo_win32_to_host(struct conversion_context *c
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_OPAQUE_CAPTURE_DATA_CREATE_INFO_EXT:
         {
             VkOpaqueCaptureDataCreateInfoEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -26734,6 +27397,17 @@ static void convert_VkImageViewCreateInfo_win32_to_host(struct conversion_contex
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_2_CREATE_INFO_KHR:
+        {
+            VkImageViewUsage2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageViewUsage2CreateInfoKHR32 *in_ext = (const VkImageViewUsage2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO:
         {
             VkImageViewUsageCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -27606,6 +28280,18 @@ static void convert_VkQueryPoolCreateInfo_win32_to_host(struct conversion_contex
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_QUERY_POOL_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_CREATE_INFO_KHR:
+        {
+            VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR32 *in_ext = (const VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_QUERY_POOL_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->maxPerPartitionFeedbackEntries = in_ext->maxPerPartitionFeedbackEntries;
+            out_ext->perPartitionEncodeFeedbackFlags = in_ext->perPartitionEncodeFeedbackFlags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR:
         {
             VkVideoDecodeAV1ProfileInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -28989,6 +29675,17 @@ static void convert_VkSwapchainCreateInfoKHR_win32_to_unwrapped_host(struct conv
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_SWAPCHAIN_LATENCY_CREATE_INFO_NV:
         {
             VkSwapchainLatencyCreateInfoNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -31437,6 +32134,30 @@ static void convert_VkGeneratedCommandsMemoryRequirementsInfoNV_win32_to_host(co
         FIXME("Unexpected pNext\n");
 }
 
+static void convert_VkGpaDeviceGetClockInfoAMD_win32_to_host(const VkGpaDeviceGetClockInfoAMD32 *in, VkGpaDeviceGetClockInfoAMD *out)
+{
+    if (!in) return;
+
+    out->sType = in->sType;
+    out->pNext = NULL;
+    out->memoryClockRatioToPeak = in->memoryClockRatioToPeak;
+    out->engineClockRatioToPeak = in->engineClockRatioToPeak;
+    out->memoryClockFrequency = in->memoryClockFrequency;
+    out->engineClockFrequency = in->engineClockFrequency;
+    if (in->pNext)
+        FIXME("Unexpected pNext\n");
+}
+
+static void convert_VkGpaDeviceGetClockInfoAMD_host_to_win32(const VkGpaDeviceGetClockInfoAMD *in, VkGpaDeviceGetClockInfoAMD32 *out)
+{
+    if (!in) return;
+
+    out->memoryClockRatioToPeak = in->memoryClockRatioToPeak;
+    out->engineClockRatioToPeak = in->engineClockRatioToPeak;
+    out->memoryClockFrequency = in->memoryClockFrequency;
+    out->engineClockFrequency = in->engineClockFrequency;
+}
+
 static void convert_VkImageMemoryRequirementsInfo2_win32_to_host(struct conversion_context *ctx, const VkImageMemoryRequirementsInfo232 *in, VkImageMemoryRequirementsInfo2 *out)
 {
     const VkBaseInStructure32 *in_header;
@@ -32610,6 +33331,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV:
+        {
+            VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32 *in_ext = (const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV;
+            out_ext->pNext = NULL;
+            out_ext->cooperativeMatrixDecodeVector = in_ext->cooperativeMatrixDecodeVector;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR:
         {
             VkPhysicalDeviceCooperativeMatrixFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -33033,6 +33765,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32 *in_ext = (const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->elapsedTimerQuery = in_ext->elapsedTimerQuery;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV:
         {
             VkPhysicalDeviceExclusiveScissorFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -33109,6 +33852,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR:
+        {
+            VkPhysicalDeviceExtendedFlagsFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceExtendedFlagsFeaturesKHR32 *in_ext = (const VkPhysicalDeviceExtendedFlagsFeaturesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->extendedFlags = in_ext->extendedFlags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV:
         {
             VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -33275,6 +34029,20 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD:
+        {
+            VkPhysicalDeviceGpaFeaturesAMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceGpaFeaturesAMD32 *in_ext = (const VkPhysicalDeviceGpaFeaturesAMD32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD;
+            out_ext->pNext = NULL;
+            out_ext->perfCounters = in_ext->perfCounters;
+            out_ext->streamingPerfCounters = in_ext->streamingPerfCounters;
+            out_ext->sqThreadTracing = in_ext->sqThreadTracing;
+            out_ext->clockModes = in_ext->clockModes;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -33375,6 +34143,19 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceImageProcessing3FeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceImageProcessing3FeaturesQCOM32 *in_ext = (const VkPhysicalDeviceImageProcessing3FeaturesQCOM32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->imageGatherLinear = in_ext->imageGatherLinear;
+            out_ext->imageGatherExtendedModes = in_ext->imageGatherExtendedModes;
+            out_ext->blockMatchExtendedClampToEdge = in_ext->blockMatchExtendedClampToEdge;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM:
         {
             VkPhysicalDeviceImageProcessingFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -33696,6 +34477,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT:
+        {
+            VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32 *in_ext = (const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT;
+            out_ext->pNext = NULL;
+            out_ext->multisampledRenderToSwapchain = in_ext->multisampledRenderToSwapchain;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES:
         {
             VkPhysicalDeviceMultiviewFeatures *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -33779,6 +34571,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceOpacityMicromapFeaturesKHR32 *in_ext = (const VkPhysicalDeviceOpacityMicromapFeaturesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->micromap = in_ext->micromap;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV:
         {
             VkPhysicalDeviceOpticalFlowFeaturesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -34690,6 +35493,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32 *in_ext = (const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM;
+            out_ext->pNext = NULL;
+            out_ext->shaderMultipleWaitQueues = in_ext->shaderMultipleWaitQueues;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT:
         {
             VkPhysicalDeviceShaderObjectFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -34745,6 +35559,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32 *in_ext = (const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT;
+            out_ext->pNext = NULL;
+            out_ext->shaderSplitBarrier = in_ext->shaderSplitBarrier;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES:
         {
             VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -35124,6 +35949,17 @@ static void convert_VkPhysicalDeviceFeatures2_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR:
+        {
+            VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32 *in_ext = (const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->videoEncodeFeedback2 = in_ext->videoEncodeFeedback2;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_INTRA_REFRESH_FEATURES_KHR:
         {
             VkPhysicalDeviceVideoEncodeIntraRefreshFeaturesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -35636,6 +36472,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV:
+        {
+            VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV);
+            const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *in_ext = (const VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV;
+            out_ext->cooperativeMatrixDecodeVector = in_ext->cooperativeMatrixDecodeVector;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR:
         {
             VkPhysicalDeviceCooperativeMatrixFeaturesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR);
@@ -35989,6 +36834,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM);
+            const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *in_ext = (const VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM;
+            out_ext->elapsedTimerQuery = in_ext->elapsedTimerQuery;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV:
         {
             VkPhysicalDeviceExclusiveScissorFeaturesNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV);
@@ -36057,6 +36911,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR:
+        {
+            VkPhysicalDeviceExtendedFlagsFeaturesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR);
+            const VkPhysicalDeviceExtendedFlagsFeaturesKHR *in_ext = (const VkPhysicalDeviceExtendedFlagsFeaturesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR;
+            out_ext->extendedFlags = in_ext->extendedFlags;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV:
         {
             VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV);
@@ -36195,6 +37058,18 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD:
+        {
+            VkPhysicalDeviceGpaFeaturesAMD32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD);
+            const VkPhysicalDeviceGpaFeaturesAMD *in_ext = (const VkPhysicalDeviceGpaFeaturesAMD *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD;
+            out_ext->perfCounters = in_ext->perfCounters;
+            out_ext->streamingPerfCounters = in_ext->streamingPerfCounters;
+            out_ext->sqThreadTracing = in_ext->sqThreadTracing;
+            out_ext->clockModes = in_ext->clockModes;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT);
@@ -36277,6 +37152,17 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceImageProcessing3FeaturesQCOM32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM);
+            const VkPhysicalDeviceImageProcessing3FeaturesQCOM *in_ext = (const VkPhysicalDeviceImageProcessing3FeaturesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM;
+            out_ext->imageGatherLinear = in_ext->imageGatherLinear;
+            out_ext->imageGatherExtendedModes = in_ext->imageGatherExtendedModes;
+            out_ext->blockMatchExtendedClampToEdge = in_ext->blockMatchExtendedClampToEdge;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM:
         {
             VkPhysicalDeviceImageProcessingFeaturesQCOM32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM);
@@ -36542,6 +37428,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT:
+        {
+            VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT);
+            const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *in_ext = (const VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT;
+            out_ext->multisampledRenderToSwapchain = in_ext->multisampledRenderToSwapchain;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES:
         {
             VkPhysicalDeviceMultiviewFeatures32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES);
@@ -36611,6 +37506,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapFeaturesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR);
+            const VkPhysicalDeviceOpacityMicromapFeaturesKHR *in_ext = (const VkPhysicalDeviceOpacityMicromapFeaturesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR;
+            out_ext->micromap = in_ext->micromap;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV:
         {
             VkPhysicalDeviceOpticalFlowFeaturesNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV);
@@ -37366,6 +38270,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM);
+            const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *in_ext = (const VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM;
+            out_ext->shaderMultipleWaitQueues = in_ext->shaderMultipleWaitQueues;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT:
         {
             VkPhysicalDeviceShaderObjectFeaturesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT);
@@ -37411,6 +38324,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierFeaturesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT);
+            const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *in_ext = (const VkPhysicalDeviceShaderSplitBarrierFeaturesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT;
+            out_ext->shaderSplitBarrier = in_ext->shaderSplitBarrier;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES:
         {
             VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES);
@@ -37726,6 +38648,15 @@ static void convert_VkPhysicalDeviceFeatures2_host_to_win32(const VkPhysicalDevi
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR:
+        {
+            VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR);
+            const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *in_ext = (const VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR;
+            out_ext->videoEncodeFeedback2 = in_ext->videoEncodeFeedback2;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_INTRA_REFRESH_FEATURES_KHR:
         {
             VkPhysicalDeviceVideoEncodeIntraRefreshFeaturesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_INTRA_REFRESH_FEATURES_KHR);
@@ -37986,6 +38917,15 @@ static void convert_VkFormatProperties2_win32_to_host(struct conversion_context
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_4_KHR:
+        {
+            VkFormatProperties4KHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_4_KHR;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_SUBPASS_RESOLVE_PERFORMANCE_QUERY_EXT:
         {
             VkSubpassResolvePerformanceQueryEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -38035,6 +38975,17 @@ static void convert_VkFormatProperties2_host_to_win32(const VkFormatProperties2
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_4_KHR:
+        {
+            VkFormatProperties4KHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_4_KHR);
+            const VkFormatProperties4KHR *in_ext = (const VkFormatProperties4KHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_4_KHR;
+            out_ext->linearTilingFeatures = in_ext->linearTilingFeatures;
+            out_ext->optimalTilingFeatures = in_ext->optimalTilingFeatures;
+            out_ext->bufferFeatures = in_ext->bufferFeatures;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_SUBPASS_RESOLVE_PERFORMANCE_QUERY_EXT:
         {
             VkSubpassResolvePerformanceQueryEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_SUBPASS_RESOLVE_PERFORMANCE_QUERY_EXT);
@@ -38161,6 +39112,17 @@ static void convert_VkPhysicalDeviceImageFormatInfo2_win64_to_host(struct conver
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageCreateFlags2CreateInfoKHR *in_ext = (const VkImageCreateFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->flags = in_ext->flags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO:
         {
             VkImageFormatListCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -38173,6 +39135,17 @@ static void convert_VkPhysicalDeviceImageFormatInfo2_win64_to_host(struct conver
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR:
+        {
+            VkImageStencilUsage2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageStencilUsage2CreateInfoKHR *in_ext = (const VkImageStencilUsage2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->stencilUsage = in_ext->stencilUsage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO:
         {
             VkImageStencilUsageCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -38184,6 +39157,17 @@ static void convert_VkPhysicalDeviceImageFormatInfo2_win64_to_host(struct conver
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR *in_ext = (const VkImageUsageFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_INFO_NV:
         {
             VkOpticalFlowImageFormatInfoNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -38280,6 +39264,17 @@ static void convert_VkPhysicalDeviceImageFormatInfo2_win32_to_host(struct conver
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageCreateFlags2CreateInfoKHR32 *in_ext = (const VkImageCreateFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->flags = in_ext->flags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO:
         {
             VkImageFormatListCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -38292,6 +39287,17 @@ static void convert_VkPhysicalDeviceImageFormatInfo2_win32_to_host(struct conver
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR:
+        {
+            VkImageStencilUsage2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageStencilUsage2CreateInfoKHR32 *in_ext = (const VkImageStencilUsage2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->stencilUsage = in_ext->stencilUsage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO:
         {
             VkImageStencilUsageCreateInfo *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -38303,6 +39309,17 @@ static void convert_VkPhysicalDeviceImageFormatInfo2_win32_to_host(struct conver
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_INFO_NV:
         {
             VkOpticalFlowImageFormatInfoNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -39199,6 +40216,24 @@ static void convert_VkPhysicalDeviceProperties2_win64_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD:
+        {
+            VkPhysicalDeviceGpaProperties2AMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD:
+        {
+            VkPhysicalDeviceGpaPropertiesAMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -39434,6 +40469,15 @@ static void convert_VkPhysicalDeviceProperties2_win64_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapPropertiesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV:
         {
             VkPhysicalDeviceOpticalFlowPropertiesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -39724,6 +40768,15 @@ static void convert_VkPhysicalDeviceProperties2_win64_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderObjectPropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -39742,6 +40795,15 @@ static void convert_VkPhysicalDeviceProperties2_win64_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderTileImagePropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -40483,6 +41545,28 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win64(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD:
+        {
+            VkPhysicalDeviceGpaProperties2AMD *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD);
+            const VkPhysicalDeviceGpaProperties2AMD *in_ext = (const VkPhysicalDeviceGpaProperties2AMD *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD;
+            out_ext->revisionId = in_ext->revisionId;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD:
+        {
+            VkPhysicalDeviceGpaPropertiesAMD *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD);
+            const VkPhysicalDeviceGpaPropertiesAMD *in_ext = (const VkPhysicalDeviceGpaPropertiesAMD *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD;
+            out_ext->flags = in_ext->flags;
+            out_ext->maxSqttSeBufferSize = in_ext->maxSqttSeBufferSize;
+            out_ext->shaderEngineCount = in_ext->shaderEngineCount;
+            out_ext->perfBlockCount = in_ext->perfBlockCount;
+            out_ext->pPerfBlocks = in_ext->pPerfBlocks;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT);
@@ -40786,6 +41870,18 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win64(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapPropertiesKHR *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR);
+            const VkPhysicalDeviceOpacityMicromapPropertiesKHR *in_ext = (const VkPhysicalDeviceOpacityMicromapPropertiesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR;
+            out_ext->maxOpacity2StateSubdivisionLevel = in_ext->maxOpacity2StateSubdivisionLevel;
+            out_ext->maxOpacity4StateSubdivisionLevel = in_ext->maxOpacity4StateSubdivisionLevel;
+            out_ext->maxOpacityLossy4StateSubdivisionLevel = in_ext->maxOpacityLossy4StateSubdivisionLevel;
+            out_ext->maxMicromapTriangles = in_ext->maxMicromapTriangles;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV:
         {
             VkPhysicalDeviceOpticalFlowPropertiesNV *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV);
@@ -41174,6 +42270,15 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win64(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM);
+            const VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *in_ext = (const VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM;
+            out_ext->maxShaderWaitQueues = in_ext->maxShaderWaitQueues;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderObjectPropertiesEXT *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_PROPERTIES_EXT);
@@ -41194,6 +42299,15 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win64(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT);
+            const VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *in_ext = (const VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT;
+            out_ext->splitBarrierReservedSharedMemory = in_ext->splitBarrierReservedSharedMemory;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderTileImagePropertiesEXT *out_ext = find_next_struct(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_PROPERTIES_EXT);
@@ -41905,6 +43019,24 @@ static void convert_VkPhysicalDeviceProperties2_win32_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD:
+        {
+            VkPhysicalDeviceGpaProperties2AMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD:
+        {
+            VkPhysicalDeviceGpaPropertiesAMD *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -42140,6 +43272,15 @@ static void convert_VkPhysicalDeviceProperties2_win32_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapPropertiesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV:
         {
             VkPhysicalDeviceOpticalFlowPropertiesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -42430,6 +43571,15 @@ static void convert_VkPhysicalDeviceProperties2_win32_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderObjectPropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -42448,6 +43598,15 @@ static void convert_VkPhysicalDeviceProperties2_win32_to_host(struct conversion_
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderTileImagePropertiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -43183,6 +44342,28 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win32(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD:
+        {
+            VkPhysicalDeviceGpaProperties2AMD32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD);
+            const VkPhysicalDeviceGpaProperties2AMD *in_ext = (const VkPhysicalDeviceGpaProperties2AMD *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD;
+            out_ext->revisionId = in_ext->revisionId;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD:
+        {
+            VkPhysicalDeviceGpaPropertiesAMD32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD);
+            const VkPhysicalDeviceGpaPropertiesAMD *in_ext = (const VkPhysicalDeviceGpaPropertiesAMD *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD;
+            out_ext->flags = in_ext->flags;
+            out_ext->maxSqttSeBufferSize = in_ext->maxSqttSeBufferSize;
+            out_ext->shaderEngineCount = in_ext->shaderEngineCount;
+            out_ext->perfBlockCount = in_ext->perfBlockCount;
+            out_ext->pPerfBlocks = PtrToUlong(in_ext->pPerfBlocks);
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT:
         {
             VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT);
@@ -43486,6 +44667,18 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win32(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR:
+        {
+            VkPhysicalDeviceOpacityMicromapPropertiesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR);
+            const VkPhysicalDeviceOpacityMicromapPropertiesKHR *in_ext = (const VkPhysicalDeviceOpacityMicromapPropertiesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR;
+            out_ext->maxOpacity2StateSubdivisionLevel = in_ext->maxOpacity2StateSubdivisionLevel;
+            out_ext->maxOpacity4StateSubdivisionLevel = in_ext->maxOpacity4StateSubdivisionLevel;
+            out_ext->maxOpacityLossy4StateSubdivisionLevel = in_ext->maxOpacityLossy4StateSubdivisionLevel;
+            out_ext->maxMicromapTriangles = in_ext->maxMicromapTriangles;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV:
         {
             VkPhysicalDeviceOpticalFlowPropertiesNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV);
@@ -43874,6 +45067,15 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win32(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM:
+        {
+            VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM);
+            const VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *in_ext = (const VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM;
+            out_ext->maxShaderWaitQueues = in_ext->maxShaderWaitQueues;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderObjectPropertiesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_PROPERTIES_EXT);
@@ -43894,6 +45096,15 @@ static void convert_VkPhysicalDeviceProperties2_host_to_win32(const VkPhysicalDe
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT:
+        {
+            VkPhysicalDeviceShaderSplitBarrierPropertiesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT);
+            const VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *in_ext = (const VkPhysicalDeviceShaderSplitBarrierPropertiesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT;
+            out_ext->splitBarrierReservedSharedMemory = in_ext->splitBarrierReservedSharedMemory;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_PROPERTIES_EXT:
         {
             VkPhysicalDeviceShaderTileImagePropertiesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_PROPERTIES_EXT);
@@ -44565,8 +45776,11 @@ static void convert_VkQueueFamilyProperties2_array_host_to_win32(const VkQueueFa
     }
 }
 
-static void convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host(const VkPhysicalDeviceSparseImageFormatInfo232 *in, VkPhysicalDeviceSparseImageFormatInfo2 *out)
+static void convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host(struct conversion_context *ctx, const VkPhysicalDeviceSparseImageFormatInfo232 *in, VkPhysicalDeviceSparseImageFormatInfo2 *out)
 {
+    const VkBaseInStructure32 *in_header;
+    VkBaseOutStructure *out_header = (void *)out;
+
     if (!in) return;
 
     out->sType = in->sType;
@@ -44576,8 +45790,27 @@ static void convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host(const V
     out->samples = in->samples;
     out->usage = in->usage;
     out->tiling = in->tiling;
-    if (in->pNext)
-        FIXME("Unexpected pNext\n");
+
+    for (in_header = UlongToPtr(in->pNext); in_header; in_header = UlongToPtr(in_header->pNext))
+    {
+        switch (in_header->sType)
+        {
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
+        default:
+            FIXME("Unhandled sType %u.\n", in_header->sType);
+            break;
+        }
+    }
 }
 
 static void convert_VkSparseImageFormatProperties2_win32_to_host(const VkSparseImageFormatProperties232 *in, VkSparseImageFormatProperties2 *out)
@@ -44720,6 +45953,17 @@ static void convert_VkSurfaceCapabilities2KHR_win32_to_host(struct conversion_co
     {
         switch (in_header->sType)
         {
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_LATENCY_SURFACE_CAPABILITIES_NV:
         {
             VkLatencySurfaceCapabilitiesNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -44741,6 +45985,15 @@ static void convert_VkSurfaceCapabilities2KHR_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_2_KHR:
+        {
+            VkSharedPresentSurfaceCapabilities2KHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_2_KHR;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_PRESENT_BARRIER_NV:
         {
             VkSurfaceCapabilitiesPresentBarrierNV *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -44799,6 +46052,15 @@ static void convert_VkSurfaceCapabilities2KHR_win32_to_host(struct conversion_co
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_SWAPCHAIN_FLAGS_SURFACE_CAPABILITIES_EXT:
+        {
+            VkSwapchainFlagsSurfaceCapabilitiesEXT *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            out_ext->sType = VK_STRUCTURE_TYPE_SWAPCHAIN_FLAGS_SURFACE_CAPABILITIES_EXT;
+            out_ext->pNext = NULL;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         default:
             FIXME("Unhandled sType %u.\n", in_header->sType);
             break;
@@ -44819,6 +46081,15 @@ static void convert_VkSurfaceCapabilities2KHR_host_to_win32(const VkSurfaceCapab
     {
         switch (in_header->sType)
         {
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR);
+            const VkImageUsageFlags2CreateInfoKHR *in_ext = (const VkImageUsageFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->usage = in_ext->usage;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_LATENCY_SURFACE_CAPABILITIES_NV:
         {
             VkLatencySurfaceCapabilitiesNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_LATENCY_SURFACE_CAPABILITIES_NV);
@@ -44841,6 +46112,15 @@ static void convert_VkSurfaceCapabilities2KHR_host_to_win32(const VkSurfaceCapab
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_2_KHR:
+        {
+            VkSharedPresentSurfaceCapabilities2KHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_2_KHR);
+            const VkSharedPresentSurfaceCapabilities2KHR *in_ext = (const VkSharedPresentSurfaceCapabilities2KHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_2_KHR;
+            out_ext->sharedPresentSupportedUsageFlags = in_ext->sharedPresentSupportedUsageFlags;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_PRESENT_BARRIER_NV:
         {
             VkSurfaceCapabilitiesPresentBarrierNV32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_PRESENT_BARRIER_NV);
@@ -44891,6 +46171,15 @@ static void convert_VkSurfaceCapabilities2KHR_host_to_win32(const VkSurfaceCapab
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_SWAPCHAIN_FLAGS_SURFACE_CAPABILITIES_EXT:
+        {
+            VkSwapchainFlagsSurfaceCapabilitiesEXT32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_SWAPCHAIN_FLAGS_SURFACE_CAPABILITIES_EXT);
+            const VkSwapchainFlagsSurfaceCapabilitiesEXT *in_ext = (const VkSwapchainFlagsSurfaceCapabilitiesEXT *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_SWAPCHAIN_FLAGS_SURFACE_CAPABILITIES_EXT;
+            out_ext->swapchainSupportedFlags = in_ext->swapchainSupportedFlags;
+            out_header = (void *)out_ext;
+            break;
+        }
         default:
             break;
         }
@@ -45110,6 +46399,18 @@ static void convert_VkVideoCapabilitiesKHR_win32_to_host(struct conversion_conte
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_VIDEO_ENCODE_FEEDBACK_2_CAPABILITIES_KHR:
+        {
+            VkVideoEncodeFeedback2CapabilitiesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkVideoEncodeFeedback2CapabilitiesKHR32 *in_ext = (const VkVideoEncodeFeedback2CapabilitiesKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_FEEDBACK_2_CAPABILITIES_KHR;
+            out_ext->pNext = NULL;
+            out_ext->maxPerPartitionFeedbackEntries = in_ext->maxPerPartitionFeedbackEntries;
+            out_ext->supportedPerPartitionEncodeFeedbackFlags = in_ext->supportedPerPartitionEncodeFeedbackFlags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_KHR:
         {
             VkVideoEncodeH264CapabilitiesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -45286,6 +46587,16 @@ static void convert_VkVideoCapabilitiesKHR_host_to_win32(const VkVideoCapabiliti
             out_header = (void *)out_ext;
             break;
         }
+        case VK_STRUCTURE_TYPE_VIDEO_ENCODE_FEEDBACK_2_CAPABILITIES_KHR:
+        {
+            VkVideoEncodeFeedback2CapabilitiesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_VIDEO_ENCODE_FEEDBACK_2_CAPABILITIES_KHR);
+            const VkVideoEncodeFeedback2CapabilitiesKHR *in_ext = (const VkVideoEncodeFeedback2CapabilitiesKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_FEEDBACK_2_CAPABILITIES_KHR;
+            out_ext->maxPerPartitionFeedbackEntries = in_ext->maxPerPartitionFeedbackEntries;
+            out_ext->supportedPerPartitionEncodeFeedbackFlags = in_ext->supportedPerPartitionEncodeFeedbackFlags;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_KHR:
         {
             VkVideoEncodeH264CapabilitiesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_KHR);
@@ -45493,6 +46804,17 @@ static void convert_VkPhysicalDeviceVideoFormatInfoKHR_win32_to_host(struct conv
     {
         switch (in_header->sType)
         {
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR:
         {
             VkVideoProfileListInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -45526,6 +46848,28 @@ static void convert_VkVideoFormatPropertiesKHR_win32_to_host(struct conversion_c
     {
         switch (in_header->sType)
         {
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageCreateFlags2CreateInfoKHR32 *in_ext = (const VkImageCreateFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->flags = in_ext->flags;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
+            const VkImageUsageFlags2CreateInfoKHR32 *in_ext = (const VkImageUsageFlags2CreateInfoKHR32 *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->pNext = NULL;
+            out_ext->usage = in_ext->usage;
+            out_header->pNext = (void *)out_ext;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_VIDEO_FORMAT_AV1_QUANTIZATION_MAP_PROPERTIES_KHR:
         {
             VkVideoFormatAV1QuantizationMapPropertiesKHR *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));
@@ -45594,6 +46938,24 @@ static void convert_VkVideoFormatPropertiesKHR_host_to_win32(const VkVideoFormat
     {
         switch (in_header->sType)
         {
+        case VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageCreateFlags2CreateInfoKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR);
+            const VkImageCreateFlags2CreateInfoKHR *in_ext = (const VkImageCreateFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->flags = in_ext->flags;
+            out_header = (void *)out_ext;
+            break;
+        }
+        case VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR:
+        {
+            VkImageUsageFlags2CreateInfoKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR);
+            const VkImageUsageFlags2CreateInfoKHR *in_ext = (const VkImageUsageFlags2CreateInfoKHR *)in_header;
+            out_ext->sType = VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR;
+            out_ext->usage = in_ext->usage;
+            out_header = (void *)out_ext;
+            break;
+        }
         case VK_STRUCTURE_TYPE_VIDEO_FORMAT_AV1_QUANTIZATION_MAP_PROPERTIES_KHR:
         {
             VkVideoFormatAV1QuantizationMapPropertiesKHR32 *out_ext = find_next_struct32(out_header, VK_STRUCTURE_TYPE_VIDEO_FORMAT_AV1_QUANTIZATION_MAP_PROPERTIES_KHR);
@@ -48154,6 +49516,28 @@ static void convert_VkDebugUtilsObjectTagInfoEXT_win32_to_host(const VkDebugUtil
         FIXME("Unexpected pNext\n");
 }
 
+static void convert_VkGpaDeviceClockModeInfoAMD_win32_to_host(const VkGpaDeviceClockModeInfoAMD32 *in, VkGpaDeviceClockModeInfoAMD *out)
+{
+    if (!in) return;
+
+    out->sType = in->sType;
+    out->pNext = NULL;
+    out->clockMode = in->clockMode;
+    out->memoryClockRatioToPeak = in->memoryClockRatioToPeak;
+    out->engineClockRatioToPeak = in->engineClockRatioToPeak;
+    if (in->pNext)
+        FIXME("Unexpected pNext\n");
+}
+
+static void convert_VkGpaDeviceClockModeInfoAMD_host_to_win32(const VkGpaDeviceClockModeInfoAMD *in, VkGpaDeviceClockModeInfoAMD32 *out)
+{
+    if (!in) return;
+
+    out->clockMode = in->clockMode;
+    out->memoryClockRatioToPeak = in->memoryClockRatioToPeak;
+    out->engineClockRatioToPeak = in->engineClockRatioToPeak;
+}
+
 #ifdef _WIN64
 static const VkSwapchainKHR *convert_VkSwapchainKHR_array_win64_to_host(struct conversion_context *ctx, const VkSwapchainKHR *in, uint32_t count)
 {
@@ -49738,6 +51122,64 @@ static void thunk32_vkCmdBeginDebugUtilsLabelEXT(void *args)
     vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdBeginDebugUtilsLabelEXT(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer, &pLabelInfo_host);
 }
 
+#ifdef _WIN64
+static NTSTATUS thunk64_vkCmdBeginGpaSampleAMD(void *args)
+{
+    struct vkCmdBeginGpaSampleAMD_params *params = args;
+
+    TRACE("%p, 0x%s, %p, %p\n", params->commandBuffer, wine_dbgstr_longlong(params->gpaSession), params->pGpaSampleBeginInfo, params->pSampleID);
+
+    params->result = vulkan_command_buffer_from_handle(params->commandBuffer)->device->p_vkCmdBeginGpaSampleAMD(vulkan_command_buffer_from_handle(params->commandBuffer)->host.command_buffer, params->gpaSession, params->pGpaSampleBeginInfo, params->pSampleID);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkCmdBeginGpaSampleAMD(void *args)
+{
+    struct
+    {
+        PTR32 commandBuffer;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        PTR32 pGpaSampleBeginInfo;
+        PTR32 pSampleID;
+        VkResult result;
+    } *params = args;
+    VkGpaSampleBeginInfoAMD pGpaSampleBeginInfo_host;
+
+    TRACE("%#x, 0x%s, %#x, %#x\n", params->commandBuffer, wine_dbgstr_longlong(params->gpaSession), params->pGpaSampleBeginInfo, params->pSampleID);
+
+    convert_VkGpaSampleBeginInfoAMD_win32_to_host((const VkGpaSampleBeginInfoAMD32 *)UlongToPtr(params->pGpaSampleBeginInfo), &pGpaSampleBeginInfo_host);
+    params->result = vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdBeginGpaSampleAMD(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer, params->gpaSession, &pGpaSampleBeginInfo_host, (uint32_t *)UlongToPtr(params->pSampleID));
+    return STATUS_SUCCESS;
+}
+
+#ifdef _WIN64
+static NTSTATUS thunk64_vkCmdBeginGpaSessionAMD(void *args)
+{
+    struct vkCmdBeginGpaSessionAMD_params *params = args;
+
+    TRACE("%p, 0x%s\n", params->commandBuffer, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_command_buffer_from_handle(params->commandBuffer)->device->p_vkCmdBeginGpaSessionAMD(vulkan_command_buffer_from_handle(params->commandBuffer)->host.command_buffer, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkCmdBeginGpaSessionAMD(void *args)
+{
+    struct
+    {
+        PTR32 commandBuffer;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        VkResult result;
+    } *params = args;
+
+    TRACE("%#x, 0x%s\n", params->commandBuffer, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdBeginGpaSessionAMD(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static void thunk64_vkCmdBeginPerTileExecutionQCOM(void *args)
 {
@@ -51232,6 +52674,26 @@ static void thunk32_vkCmdCopyBufferToImage2KHR(void *args)
     free_conversion_context(ctx);
 }
 
+#ifdef _WIN64
+static void thunk64_vkCmdCopyGpaSessionResultsAMD(void *args)
+{
+    struct vkCmdCopyGpaSessionResultsAMD_params *params = args;
+
+    vulkan_command_buffer_from_handle(params->commandBuffer)->device->p_vkCmdCopyGpaSessionResultsAMD(vulkan_command_buffer_from_handle(params->commandBuffer)->host.command_buffer, params->gpaSession);
+}
+#endif /* _WIN64 */
+
+static void thunk32_vkCmdCopyGpaSessionResultsAMD(void *args)
+{
+    struct
+    {
+        PTR32 commandBuffer;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+    } *params = args;
+
+    vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdCopyGpaSessionResultsAMD(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer, params->gpaSession);
+}
+
 #ifdef _WIN64
 static void thunk64_vkCmdCopyImage(void *args)
 {
@@ -52839,6 +54301,54 @@ static void thunk32_vkCmdEndDebugUtilsLabelEXT(void *args)
     vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdEndDebugUtilsLabelEXT(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer);
 }
 
+#ifdef _WIN64
+static void thunk64_vkCmdEndGpaSampleAMD(void *args)
+{
+    struct vkCmdEndGpaSampleAMD_params *params = args;
+
+    vulkan_command_buffer_from_handle(params->commandBuffer)->device->p_vkCmdEndGpaSampleAMD(vulkan_command_buffer_from_handle(params->commandBuffer)->host.command_buffer, params->gpaSession, params->sampleID);
+}
+#endif /* _WIN64 */
+
+static void thunk32_vkCmdEndGpaSampleAMD(void *args)
+{
+    struct
+    {
+        PTR32 commandBuffer;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        uint32_t sampleID;
+    } *params = args;
+
+    vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdEndGpaSampleAMD(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer, params->gpaSession, params->sampleID);
+}
+
+#ifdef _WIN64
+static NTSTATUS thunk64_vkCmdEndGpaSessionAMD(void *args)
+{
+    struct vkCmdEndGpaSessionAMD_params *params = args;
+
+    TRACE("%p, 0x%s\n", params->commandBuffer, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_command_buffer_from_handle(params->commandBuffer)->device->p_vkCmdEndGpaSessionAMD(vulkan_command_buffer_from_handle(params->commandBuffer)->host.command_buffer, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkCmdEndGpaSessionAMD(void *args)
+{
+    struct
+    {
+        PTR32 commandBuffer;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        VkResult result;
+    } *params = args;
+
+    TRACE("%#x, 0x%s\n", params->commandBuffer, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->device->p_vkCmdEndGpaSessionAMD(vulkan_command_buffer_from_handle((VkCommandBuffer)UlongToPtr(params->commandBuffer))->host.command_buffer, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static void thunk64_vkCmdEndPerTileExecutionQCOM(void *args)
 {
@@ -58070,6 +59580,37 @@ static NTSTATUS thunk32_vkCreateFramebuffer(void *args)
     return STATUS_SUCCESS;
 }
 
+#ifdef _WIN64
+static NTSTATUS thunk64_vkCreateGpaSessionAMD(void *args)
+{
+    struct vkCreateGpaSessionAMD_params *params = args;
+
+    TRACE("%p, %p, %p, %p\n", params->device, params->pCreateInfo, params->pAllocator, params->pGpaSession);
+
+    params->result = vulkan_device_from_handle(params->device)->p_vkCreateGpaSessionAMD(vulkan_device_from_handle(params->device)->host.device, params->pCreateInfo, NULL, params->pGpaSession);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkCreateGpaSessionAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        PTR32 pCreateInfo;
+        PTR32 pAllocator;
+        PTR32 pGpaSession;
+        VkResult result;
+    } *params = args;
+    VkGpaSessionCreateInfoAMD pCreateInfo_host;
+
+    TRACE("%#x, %#x, %#x, %#x\n", params->device, params->pCreateInfo, params->pAllocator, params->pGpaSession);
+
+    convert_VkGpaSessionCreateInfoAMD_win32_to_host((const VkGpaSessionCreateInfoAMD32 *)UlongToPtr(params->pCreateInfo), &pCreateInfo_host);
+    params->result = vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkCreateGpaSessionAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, &pCreateInfo_host, NULL, (VkGpaSessionAMD *)UlongToPtr(params->pGpaSession));
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static NTSTATUS thunk64_vkCreateGraphicsPipelines(void *args)
 {
@@ -59905,6 +61446,33 @@ static NTSTATUS thunk32_vkDestroyFramebuffer(void *args)
     return STATUS_SUCCESS;
 }
 
+#ifdef _WIN64
+static NTSTATUS thunk64_vkDestroyGpaSessionAMD(void *args)
+{
+    struct vkDestroyGpaSessionAMD_params *params = args;
+
+    TRACE("%p, 0x%s, %p\n", params->device, wine_dbgstr_longlong(params->gpaSession), params->pAllocator);
+
+    vulkan_device_from_handle(params->device)->p_vkDestroyGpaSessionAMD(vulkan_device_from_handle(params->device)->host.device, params->gpaSession, NULL);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkDestroyGpaSessionAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        PTR32 pAllocator;
+    } *params = args;
+
+    TRACE("%#x, 0x%s, %#x\n", params->device, wine_dbgstr_longlong(params->gpaSession), params->pAllocator);
+
+    vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkDestroyGpaSessionAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, params->gpaSession, NULL);
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static NTSTATUS thunk64_vkDestroyImage(void *args)
 {
@@ -63351,6 +64919,96 @@ static NTSTATUS thunk32_vkGetGeneratedCommandsMemoryRequirementsNV(void *args)
     return STATUS_SUCCESS;
 }
 
+#ifdef _WIN64
+static NTSTATUS thunk64_vkGetGpaDeviceClockInfoAMD(void *args)
+{
+    struct vkGetGpaDeviceClockInfoAMD_params *params = args;
+
+    TRACE("%p, %p\n", params->device, params->pInfo);
+
+    params->result = vulkan_device_from_handle(params->device)->p_vkGetGpaDeviceClockInfoAMD(vulkan_device_from_handle(params->device)->host.device, params->pInfo);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkGetGpaDeviceClockInfoAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        PTR32 pInfo;
+        VkResult result;
+    } *params = args;
+    VkGpaDeviceGetClockInfoAMD pInfo_host;
+
+    TRACE("%#x, %#x\n", params->device, params->pInfo);
+
+    convert_VkGpaDeviceGetClockInfoAMD_win32_to_host((VkGpaDeviceGetClockInfoAMD32 *)UlongToPtr(params->pInfo), &pInfo_host);
+    params->result = vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkGetGpaDeviceClockInfoAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, &pInfo_host);
+    convert_VkGpaDeviceGetClockInfoAMD_host_to_win32(&pInfo_host, (VkGpaDeviceGetClockInfoAMD32 *)UlongToPtr(params->pInfo));
+    return STATUS_SUCCESS;
+}
+
+#ifdef _WIN64
+static NTSTATUS thunk64_vkGetGpaSessionResultsAMD(void *args)
+{
+    struct vkGetGpaSessionResultsAMD_params *params = args;
+
+    TRACE("%p, 0x%s, %u, %p, %p\n", params->device, wine_dbgstr_longlong(params->gpaSession), params->sampleID, params->pSizeInBytes, params->pData);
+
+    params->result = vulkan_device_from_handle(params->device)->p_vkGetGpaSessionResultsAMD(vulkan_device_from_handle(params->device)->host.device, params->gpaSession, params->sampleID, params->pSizeInBytes, params->pData);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkGetGpaSessionResultsAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        uint32_t sampleID;
+        PTR32 pSizeInBytes;
+        PTR32 pData;
+        VkResult result;
+    } *params = args;
+    size_t pSizeInBytes_host;
+
+    TRACE("%#x, 0x%s, %u, %#x, %#x\n", params->device, wine_dbgstr_longlong(params->gpaSession), params->sampleID, params->pSizeInBytes, params->pData);
+
+    pSizeInBytes_host = *(PTR32 *)UlongToPtr(params->pSizeInBytes);
+    params->result = vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkGetGpaSessionResultsAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, params->gpaSession, params->sampleID, &pSizeInBytes_host, (void *)UlongToPtr(params->pData));
+    *(PTR32 *)UlongToPtr(params->pSizeInBytes) = pSizeInBytes_host;
+    return STATUS_SUCCESS;
+}
+
+#ifdef _WIN64
+static NTSTATUS thunk64_vkGetGpaSessionStatusAMD(void *args)
+{
+    struct vkGetGpaSessionStatusAMD_params *params = args;
+
+    TRACE("%p, 0x%s\n", params->device, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_device_from_handle(params->device)->p_vkGetGpaSessionStatusAMD(vulkan_device_from_handle(params->device)->host.device, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkGetGpaSessionStatusAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        VkResult result;
+    } *params = args;
+
+    TRACE("%#x, 0x%s\n", params->device, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkGetGpaSessionStatusAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static NTSTATUS thunk64_vkGetImageMemoryRequirements(void *args)
 {
@@ -65608,7 +67266,7 @@ static NTSTATUS thunk32_vkGetPhysicalDeviceSparseImageFormatProperties2(void *ar
     TRACE("%#x, %#x, %#x, %#x\n", params->physicalDevice, params->pFormatInfo, params->pPropertyCount, params->pProperties);
 
     init_conversion_context(ctx);
-    convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host((const VkPhysicalDeviceSparseImageFormatInfo232 *)UlongToPtr(params->pFormatInfo), &pFormatInfo_host);
+    convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host(ctx, (const VkPhysicalDeviceSparseImageFormatInfo232 *)UlongToPtr(params->pFormatInfo), &pFormatInfo_host);
     pProperties_host = convert_VkSparseImageFormatProperties2_array_win32_to_host(ctx, (VkSparseImageFormatProperties232 *)UlongToPtr(params->pProperties), *(uint32_t *)UlongToPtr(params->pPropertyCount));
     vulkan_physical_device_from_handle((VkPhysicalDevice)UlongToPtr(params->physicalDevice))->instance->p_vkGetPhysicalDeviceSparseImageFormatProperties2(vulkan_physical_device_from_handle((VkPhysicalDevice)UlongToPtr(params->physicalDevice))->host.physical_device, &pFormatInfo_host, (uint32_t *)UlongToPtr(params->pPropertyCount), pProperties_host);
     convert_VkSparseImageFormatProperties2_array_host_to_win32(pProperties_host, (VkSparseImageFormatProperties232 *)UlongToPtr(params->pProperties), *(uint32_t *)UlongToPtr(params->pPropertyCount));
@@ -65645,7 +67303,7 @@ static NTSTATUS thunk32_vkGetPhysicalDeviceSparseImageFormatProperties2KHR(void
     TRACE("%#x, %#x, %#x, %#x\n", params->physicalDevice, params->pFormatInfo, params->pPropertyCount, params->pProperties);
 
     init_conversion_context(ctx);
-    convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host((const VkPhysicalDeviceSparseImageFormatInfo232 *)UlongToPtr(params->pFormatInfo), &pFormatInfo_host);
+    convert_VkPhysicalDeviceSparseImageFormatInfo2_win32_to_host(ctx, (const VkPhysicalDeviceSparseImageFormatInfo232 *)UlongToPtr(params->pFormatInfo), &pFormatInfo_host);
     pProperties_host = convert_VkSparseImageFormatProperties2_array_win32_to_host(ctx, (VkSparseImageFormatProperties232 *)UlongToPtr(params->pProperties), *(uint32_t *)UlongToPtr(params->pPropertyCount));
     vulkan_physical_device_from_handle((VkPhysicalDevice)UlongToPtr(params->physicalDevice))->instance->p_vkGetPhysicalDeviceSparseImageFormatProperties2KHR(vulkan_physical_device_from_handle((VkPhysicalDevice)UlongToPtr(params->physicalDevice))->host.physical_device, &pFormatInfo_host, (uint32_t *)UlongToPtr(params->pPropertyCount), pProperties_host);
     convert_VkSparseImageFormatProperties2_array_host_to_win32(pProperties_host, (VkSparseImageFormatProperties232 *)UlongToPtr(params->pProperties), *(uint32_t *)UlongToPtr(params->pPropertyCount));
@@ -68386,6 +70044,33 @@ static NTSTATUS thunk32_vkResetFences(void *args)
     return STATUS_SUCCESS;
 }
 
+#ifdef _WIN64
+static NTSTATUS thunk64_vkResetGpaSessionAMD(void *args)
+{
+    struct vkResetGpaSessionAMD_params *params = args;
+
+    TRACE("%p, 0x%s\n", params->device, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_device_from_handle(params->device)->p_vkResetGpaSessionAMD(vulkan_device_from_handle(params->device)->host.device, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkResetGpaSessionAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        VkGpaSessionAMD DECLSPEC_ALIGN(8) gpaSession;
+        VkResult result;
+    } *params = args;
+
+    TRACE("%#x, 0x%s\n", params->device, wine_dbgstr_longlong(params->gpaSession));
+
+    params->result = vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkResetGpaSessionAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, params->gpaSession);
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static NTSTATUS thunk64_vkResetQueryPool(void *args)
 {
@@ -68558,6 +70243,36 @@ static NTSTATUS thunk32_vkSetEvent(void *args)
     return STATUS_SUCCESS;
 }
 
+#ifdef _WIN64
+static NTSTATUS thunk64_vkSetGpaDeviceClockModeAMD(void *args)
+{
+    struct vkSetGpaDeviceClockModeAMD_params *params = args;
+
+    TRACE("%p, %p\n", params->device, params->pInfo);
+
+    params->result = vulkan_device_from_handle(params->device)->p_vkSetGpaDeviceClockModeAMD(vulkan_device_from_handle(params->device)->host.device, params->pInfo);
+    return STATUS_SUCCESS;
+}
+#endif /* _WIN64 */
+
+static NTSTATUS thunk32_vkSetGpaDeviceClockModeAMD(void *args)
+{
+    struct
+    {
+        PTR32 device;
+        PTR32 pInfo;
+        VkResult result;
+    } *params = args;
+    VkGpaDeviceClockModeInfoAMD pInfo_host;
+
+    TRACE("%#x, %#x\n", params->device, params->pInfo);
+
+    convert_VkGpaDeviceClockModeInfoAMD_win32_to_host((VkGpaDeviceClockModeInfoAMD32 *)UlongToPtr(params->pInfo), &pInfo_host);
+    params->result = vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->p_vkSetGpaDeviceClockModeAMD(vulkan_device_from_handle((VkDevice)UlongToPtr(params->device))->host.device, &pInfo_host);
+    convert_VkGpaDeviceClockModeInfoAMD_host_to_win32(&pInfo_host, (VkGpaDeviceClockModeInfoAMD32 *)UlongToPtr(params->pInfo));
+    return STATUS_SUCCESS;
+}
+
 #ifdef _WIN64
 static NTSTATUS thunk64_vkSetHdrMetadataEXT(void *args)
 {
@@ -69651,6 +71366,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     (void *)thunk64_vkCmdBeginConditionalRenderingEXT,
     (void *)thunk64_vkCmdBeginCustomResolveEXT,
     (void *)thunk64_vkCmdBeginDebugUtilsLabelEXT,
+    thunk64_vkCmdBeginGpaSampleAMD,
+    thunk64_vkCmdBeginGpaSessionAMD,
     (void *)thunk64_vkCmdBeginPerTileExecutionQCOM,
     (void *)thunk64_vkCmdBeginQuery,
     (void *)thunk64_vkCmdBeginQueryIndexedEXT,
@@ -69710,6 +71427,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     (void *)thunk64_vkCmdCopyBufferToImage,
     (void *)thunk64_vkCmdCopyBufferToImage2,
     (void *)thunk64_vkCmdCopyBufferToImage2KHR,
+    (void *)thunk64_vkCmdCopyGpaSessionResultsAMD,
     (void *)thunk64_vkCmdCopyImage,
     (void *)thunk64_vkCmdCopyImage2,
     (void *)thunk64_vkCmdCopyImage2KHR,
@@ -69777,6 +71495,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     (void *)thunk64_vkCmdEncodeVideoKHR,
     (void *)thunk64_vkCmdEndConditionalRenderingEXT,
     (void *)thunk64_vkCmdEndDebugUtilsLabelEXT,
+    (void *)thunk64_vkCmdEndGpaSampleAMD,
+    thunk64_vkCmdEndGpaSessionAMD,
     (void *)thunk64_vkCmdEndPerTileExecutionQCOM,
     (void *)thunk64_vkCmdEndQuery,
     (void *)thunk64_vkCmdEndQueryIndexedEXT,
@@ -69989,6 +71709,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk64_vkCreateEvent,
     thunk64_vkCreateFence,
     thunk64_vkCreateFramebuffer,
+    thunk64_vkCreateGpaSessionAMD,
     thunk64_vkCreateGraphicsPipelines,
     thunk64_vkCreateImage,
     thunk64_vkCreateImageView,
@@ -70046,6 +71767,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk64_vkDestroyEvent,
     thunk64_vkDestroyFence,
     thunk64_vkDestroyFramebuffer,
+    thunk64_vkDestroyGpaSessionAMD,
     thunk64_vkDestroyImage,
     thunk64_vkDestroyImageView,
     thunk64_vkDestroyIndirectCommandsLayoutEXT,
@@ -70155,6 +71877,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk64_vkGetFramebufferTilePropertiesQCOM,
     thunk64_vkGetGeneratedCommandsMemoryRequirementsEXT,
     thunk64_vkGetGeneratedCommandsMemoryRequirementsNV,
+    thunk64_vkGetGpaDeviceClockInfoAMD,
+    thunk64_vkGetGpaSessionResultsAMD,
+    thunk64_vkGetGpaSessionStatusAMD,
     thunk64_vkGetImageMemoryRequirements,
     thunk64_vkGetImageMemoryRequirements2,
     thunk64_vkGetImageMemoryRequirements2KHR,
@@ -70308,12 +72033,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk64_vkResetDescriptorPool,
     thunk64_vkResetEvent,
     thunk64_vkResetFences,
+    thunk64_vkResetGpaSessionAMD,
     thunk64_vkResetQueryPool,
     thunk64_vkResetQueryPoolEXT,
     thunk64_vkSetDebugUtilsObjectNameEXT,
     thunk64_vkSetDebugUtilsObjectTagEXT,
     thunk64_vkSetDeviceMemoryPriorityEXT,
     thunk64_vkSetEvent,
+    thunk64_vkSetGpaDeviceClockModeAMD,
     thunk64_vkSetHdrMetadataEXT,
     thunk64_vkSetLatencyMarkerNV,
     thunk64_vkSetLatencySleepModeNV,
@@ -70388,6 +72115,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     (void *)thunk32_vkCmdBeginConditionalRenderingEXT,
     (void *)thunk32_vkCmdBeginCustomResolveEXT,
     (void *)thunk32_vkCmdBeginDebugUtilsLabelEXT,
+    thunk32_vkCmdBeginGpaSampleAMD,
+    thunk32_vkCmdBeginGpaSessionAMD,
     (void *)thunk32_vkCmdBeginPerTileExecutionQCOM,
     (void *)thunk32_vkCmdBeginQuery,
     (void *)thunk32_vkCmdBeginQueryIndexedEXT,
@@ -70447,6 +72176,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     (void *)thunk32_vkCmdCopyBufferToImage,
     (void *)thunk32_vkCmdCopyBufferToImage2,
     (void *)thunk32_vkCmdCopyBufferToImage2KHR,
+    (void *)thunk32_vkCmdCopyGpaSessionResultsAMD,
     (void *)thunk32_vkCmdCopyImage,
     (void *)thunk32_vkCmdCopyImage2,
     (void *)thunk32_vkCmdCopyImage2KHR,
@@ -70514,6 +72244,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     (void *)thunk32_vkCmdEncodeVideoKHR,
     (void *)thunk32_vkCmdEndConditionalRenderingEXT,
     (void *)thunk32_vkCmdEndDebugUtilsLabelEXT,
+    (void *)thunk32_vkCmdEndGpaSampleAMD,
+    thunk32_vkCmdEndGpaSessionAMD,
     (void *)thunk32_vkCmdEndPerTileExecutionQCOM,
     (void *)thunk32_vkCmdEndQuery,
     (void *)thunk32_vkCmdEndQueryIndexedEXT,
@@ -70726,6 +72458,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk32_vkCreateEvent,
     thunk32_vkCreateFence,
     thunk32_vkCreateFramebuffer,
+    thunk32_vkCreateGpaSessionAMD,
     thunk32_vkCreateGraphicsPipelines,
     thunk32_vkCreateImage,
     thunk32_vkCreateImageView,
@@ -70783,6 +72516,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk32_vkDestroyEvent,
     thunk32_vkDestroyFence,
     thunk32_vkDestroyFramebuffer,
+    thunk32_vkDestroyGpaSessionAMD,
     thunk32_vkDestroyImage,
     thunk32_vkDestroyImageView,
     thunk32_vkDestroyIndirectCommandsLayoutEXT,
@@ -70892,6 +72626,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk32_vkGetFramebufferTilePropertiesQCOM,
     thunk32_vkGetGeneratedCommandsMemoryRequirementsEXT,
     thunk32_vkGetGeneratedCommandsMemoryRequirementsNV,
+    thunk32_vkGetGpaDeviceClockInfoAMD,
+    thunk32_vkGetGpaSessionResultsAMD,
+    thunk32_vkGetGpaSessionStatusAMD,
     thunk32_vkGetImageMemoryRequirements,
     thunk32_vkGetImageMemoryRequirements2,
     thunk32_vkGetImageMemoryRequirements2KHR,
@@ -71045,12 +72782,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     thunk32_vkResetDescriptorPool,
     thunk32_vkResetEvent,
     thunk32_vkResetFences,
+    thunk32_vkResetGpaSessionAMD,
     thunk32_vkResetQueryPool,
     thunk32_vkResetQueryPoolEXT,
     thunk32_vkSetDebugUtilsObjectNameEXT,
     thunk32_vkSetDebugUtilsObjectTagEXT,
     thunk32_vkSetDeviceMemoryPriorityEXT,
     thunk32_vkSetEvent,
+    thunk32_vkSetGpaDeviceClockModeAMD,
     thunk32_vkSetHdrMetadataEXT,
     thunk32_vkSetLatencyMarkerNV,
     thunk32_vkSetLatencySleepModeNV,
diff --git a/dlls/winevulkan/vulkan_thunks.h b/dlls/winevulkan/vulkan_thunks.h
index f2d54c40d166..6e81be28d0d0 100644
--- a/dlls/winevulkan/vulkan_thunks.h
+++ b/dlls/winevulkan/vulkan_thunks.h
@@ -4,7 +4,6 @@
  * by the following copyright and permission notice:
  *
  * Copyright 2015-2026 The Khronos Group Inc.
- *
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  * and from Vulkan video.xml file covered
diff --git a/dlls/winevulkan/winevulkan.json b/dlls/winevulkan/winevulkan.json
index e33bf00caf61..4a2c53b7e1c8 100644
--- a/dlls/winevulkan/winevulkan.json
+++ b/dlls/winevulkan/winevulkan.json
@@ -2,6 +2,6 @@
     "file_format_version": "1.0.0",
     "ICD": {
         "library_path": ".\\winevulkan.dll",
-        "api_version": "1.4.350"
+        "api_version": "1.4.353"
     }
 }
diff --git a/dlls/winevulkan/winevulkan.spec b/dlls/winevulkan/winevulkan.spec
index a565652509ee..aa02fd2358c8 100644
--- a/dlls/winevulkan/winevulkan.spec
+++ b/dlls/winevulkan/winevulkan.spec
@@ -4,7 +4,6 @@
 # by the following copyright and permission notice:
 #
 # Copyright 2015-2026 The Khronos Group Inc.
-#
 # SPDX-License-Identifier: Apache-2.0 OR MIT
 #
 # and from Vulkan video.xml file covered
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in
index cbc49823cd6f..ef38cf272c90 100644
--- a/dlls/winewayland.drv/Makefile.in
+++ b/dlls/winewayland.drv/Makefile.in
@@ -7,6 +7,7 @@ IMPORTS = user32 win32u
 VER_FILEDESCRIPTION_STR = "Wine Wayland driver"
 
 SOURCES = \
+	alpha-modifier-v1.xml \
 	cursor-shape-v1.xml \
 	display.c \
 	dllmain.c \
diff --git a/dlls/winewayland.drv/alpha-modifier-v1.xml b/dlls/winewayland.drv/alpha-modifier-v1.xml
new file mode 100644
index 000000000000..ab0a401d1ab0
--- /dev/null
+++ b/dlls/winewayland.drv/alpha-modifier-v1.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="alpha_modifier_v1">
+  <copyright>
+    Copyright © 2024 Xaver Hugl
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="wp_alpha_modifier_v1" version="1">
+    <description summary="surface alpha modifier manager">
+      This interface allows a client to set a factor for the alpha values on a
+      surface, which can be used to offload such operations to the compositor,
+      which can in turn for example offload them to KMS.
+
+      Warning! The protocol described in this file is currently in the testing
+      phase. Backward compatible changes may be added together with the
+      corresponding interface version bump. Backward incompatible changes can
+      only be done by creating a new major version of the extension.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the alpha modifier manager object">
+        Destroy the alpha modifier manager. This doesn't destroy objects
+        created with the manager.
+      </description>
+    </request>
+
+    <enum name="error">
+      <entry name="already_constructed" value="0"
+             summary="wl_surface already has a alpha modifier object"/>
+    </enum>
+
+    <request name="get_surface">
+      <description summary="create a new alpha modifier surface object">
+        Create a new alpha modifier surface object associated with the
+        given wl_surface. If there is already such an object associated with
+        the wl_surface, the already_constructed error will be raised.
+      </description>
+      <arg name="id" type="new_id" interface="wp_alpha_modifier_surface_v1"/>
+      <arg name="surface" type="object" interface="wl_surface"/>
+    </request>
+  </interface>
+
+  <interface name="wp_alpha_modifier_surface_v1" version="1">
+    <description summary="alpha modifier object for a surface">
+      This interface allows the client to set a factor for the alpha values on
+      a surface, which can be used to offload such operations to the compositor.
+      The default factor is UINT32_MAX.
+
+      This object has to be destroyed before the associated wl_surface. Once the
+      wl_surface is destroyed, all request on this object will raise the
+      no_surface error.
+    </description>
+
+    <enum name="error">
+      <entry name="no_surface" value="0" summary="wl_surface was destroyed"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the alpha modifier object">
+        This destroys the object, and is equivalent to set_multiplier with
+        a value of UINT32_MAX, with the same double-buffered semantics as
+        set_multiplier.
+      </description>
+    </request>
+
+    <request name="set_multiplier">
+      <description summary="specify the alpha multiplier">
+        Sets the alpha multiplier for the surface. The alpha multiplier is
+        double-buffered state, see wl_surface.commit for details.
+
+        This factor is applied in the compositor's blending space, as an
+        additional step after the processing of per-pixel alpha values for the
+        wl_surface. The exact meaning of the factor is thus undefined, unless
+        the blending space is specified in a different extension.
+
+        This multiplier is applied even if the buffer attached to the
+        wl_surface doesn't have an alpha channel; in that case an alpha value
+        of one is used instead.
+
+        Zero means completely transparent, UINT32_MAX means completely opaque.
+      </description>
+      <arg name="factor" type="uint"/>
+    </request>
+  </interface>
+</protocol>
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c
index b3d6b3c89e76..ba098685c465 100644
--- a/dlls/winewayland.drv/wayland.c
+++ b/dlls/winewayland.drv/wayland.c
@@ -205,6 +205,11 @@ static void registry_handle_global(void *data, struct wl_registry *registry,
         process_wayland.wp_pointer_warp_v1 =
             wl_registry_bind(registry, id, &wp_pointer_warp_v1_interface, 1);
     }
+    else if (strcmp(interface, "wp_alpha_modifier_v1") == 0)
+    {
+        process_wayland.wp_alpha_modifier_v1 =
+            wl_registry_bind(registry, id, &wp_alpha_modifier_v1_interface, 1);
+    }
 }
 
 static void registry_handle_global_remove(void *data, struct wl_registry *registry,
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c
index 2f8275ddb529..b8749478c535 100644
--- a/dlls/winewayland.drv/wayland_surface.c
+++ b/dlls/winewayland.drv/wayland_surface.c
@@ -173,6 +173,18 @@ struct wayland_surface *wayland_surface_create(HWND hwnd)
         ERR("Failed to create wp_viewport Wayland surface\n");
         goto err;
     }
+    if (process_wayland.wp_alpha_modifier_v1)
+    {
+        COLORREF key;
+        DWORD flags;
+        BYTE alpha;
+
+        surface->wp_alpha_modifier_surface_v1 =
+            wp_alpha_modifier_v1_get_surface(process_wayland.wp_alpha_modifier_v1, surface->wl_surface);
+
+        if (!NtUserGetLayeredWindowAttributes(hwnd, &key, &alpha, &flags)) flags = 0;
+        wayland_surface_set_opacity(surface, alpha, flags);
+    }
 
     surface->window.scale = 1.0;
 
@@ -212,6 +224,12 @@ void wayland_surface_destroy(struct wayland_surface *surface)
 
     wayland_surface_clear_role(surface);
 
+    if (surface->wp_alpha_modifier_surface_v1)
+    {
+        wp_alpha_modifier_surface_v1_destroy(surface->wp_alpha_modifier_surface_v1);
+        surface->wp_alpha_modifier_surface_v1 = NULL;
+    }
+
     if (surface->wp_viewport)
     {
         wp_viewport_destroy(surface->wp_viewport);
@@ -466,6 +484,14 @@ BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf,
     static enum wayland_surface_config_state mask =
         WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED;
 
+    /* The fullscreen state requires a size smaller or equal to the configured
+     * size. If we have a larger size, we can use surface geometry during
+     * surface reconfiguration to provide the smaller size, so we are always
+     * compatible with a fullscreen state.
+     * NOTE: Fullscreen combined with maximized is the same as fullscreen. */
+    if (conf->state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)
+        return TRUE;
+
     /* We require the same state. */
     if ((state & mask) != (conf->state & mask)) return FALSE;
 
@@ -478,11 +504,6 @@ BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf,
         return FALSE;
     }
 
-    /* The fullscreen state requires a size smaller or equal to the configured
-     * size. If we have a larger size, we can use surface geometry during
-     * surface reconfiguration to provide the smaller size, so we are always
-     * compatible with a fullscreen state. */
-
     return TRUE;
 }
 
@@ -536,6 +557,7 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface
         /* If the window rect in the monitor is smaller than required,
          * fall back to an appropriately sized rect at the top-left. */
         if ((surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) &&
+            !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
             (rect.right - rect.left < surface->current.width ||
              rect.bottom - rect.top < surface->current.height))
         {
@@ -557,10 +579,20 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface
 
     if (!IsRectEmpty(&rect))
     {
+        int width = rect.right - rect.left, height = rect.bottom - rect.top;
         xdg_surface_set_window_geometry(surface->xdg_surface,
                                         rect.left, rect.top,
-                                        rect.right - rect.left,
-                                        rect.bottom - rect.top);
+                                        width, height);
+        if (surface->window.resizeable)
+        {
+            xdg_toplevel_set_min_size(surface->xdg_toplevel, 0, 0);
+            xdg_toplevel_set_max_size(surface->xdg_toplevel, 0, 0);
+        }
+        else
+        {
+            xdg_toplevel_set_min_size(surface->xdg_toplevel, width, height);
+            xdg_toplevel_set_max_size(surface->xdg_toplevel, width, height);
+        }
     }
 }
 
@@ -688,7 +720,7 @@ static void wayland_surface_reconfigure_subsurface(struct wayland_surface *surfa
         TRACE("hwnd=%p pos=%d,%d\n", surface->hwnd, x, y);
 
         wl_subsurface_set_position(surface->wl_subsurface, x, y);
-        if (toplevel_data->client_surface)
+        if (toplevel_data->client_surface && toplevel_data->client_surface->wl_subsurface)
             wl_subsurface_place_above(surface->wl_subsurface, toplevel_data->client_surface->wl_surface);
         else
             wl_subsurface_place_above(surface->wl_subsurface, toplevel_surface->wl_surface);
@@ -1362,3 +1394,14 @@ void wayland_surface_assign_icon(struct wayland_surface *surface)
                                               surface->xdg_toplevel, surface->xdg_toplevel_icon);
     }
 }
+
+void wayland_surface_set_opacity(struct wayland_surface *surface, BYTE alpha, UINT flags)
+{
+    if (surface->wp_alpha_modifier_surface_v1)
+    {
+        uint32_t opacity = (flags & LWA_ALPHA) ? (UINT32_MAX / 0xff) * alpha : UINT32_MAX;
+        wp_alpha_modifier_surface_v1_set_multiplier(surface->wp_alpha_modifier_surface_v1, opacity);
+        wl_surface_commit(surface->wl_surface);
+        wl_display_flush(process_wayland.wl_display);
+    }
+}
diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h
index 5f4bd6a7cf6a..30fb7f979698 100644
--- a/dlls/winewayland.drv/waylanddrv.h
+++ b/dlls/winewayland.drv/waylanddrv.h
@@ -39,6 +39,7 @@
 #include "wlr-data-control-unstable-v1-client-protocol.h"
 #include "xdg-toplevel-icon-v1-client-protocol.h"
 #include "pointer-warp-v1-client-protocol.h"
+#include "alpha-modifier-v1-client-protocol.h"
 
 #include "windef.h"
 #include "winbase.h"
@@ -180,6 +181,7 @@ struct wayland
     struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1;
     struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager_v1;
     struct wp_pointer_warp_v1 *wp_pointer_warp_v1;
+    struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1;
     struct wayland_seat seat;
     struct wayland_keyboard keyboard;
     struct wayland_pointer pointer;
@@ -238,6 +240,7 @@ struct wayland_window_config
     BOOL visible;
     BOOL managed;
     BOOL minimized;
+    BOOL resizeable;
 };
 
 struct wayland_client_surface
@@ -286,6 +289,7 @@ struct wayland_surface
             HWND toplevel_hwnd;
         };
     };
+    struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1;
 
     struct wayland_surface_config pending, requested, processing, current;
     BOOL resizing;
@@ -337,6 +341,7 @@ void wayland_surface_ensure_contents(struct wayland_surface *surface);
 void wayland_surface_set_title(struct wayland_surface *surface, LPCWSTR title);
 void wayland_surface_assign_icon(struct wayland_surface *surface);
 void wayland_surface_set_icon_buffer(struct wayland_surface *surface, UINT type, const ICONINFO *ii);
+void wayland_surface_set_opacity(struct wayland_surface *surface, BYTE alpha, UINT flags);
 
 static inline BOOL wayland_surface_is_toplevel(struct wayland_surface *surface)
 {
@@ -373,6 +378,7 @@ struct wayland_win_data
     /* window rects, relative to parent client area */
     struct window_rects rects;
     BOOL is_fullscreen;
+    BOOL resizeable;
     BOOL managed;
     BOOL layered_attribs_set;
 };
@@ -454,6 +460,7 @@ void WAYLAND_SetWindowIcons(HWND hwnd, HICON icon, const ICONINFO *ii, HICON ico
 void WAYLAND_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style);
 void WAYLAND_SetWindowText(HWND hwnd, LPCWSTR text);
 LRESULT WAYLAND_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam, const POINT *pos);
+void WAYLAND_UpdateLayeredWindow(HWND hwnd, BYTE alpha, UINT flags);
 UINT WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, void *param);
 LRESULT WAYLAND_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
 void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UINT swp_flags,
diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c
index b7a8a29be70c..5fae15c81ebc 100644
--- a/dlls/winewayland.drv/waylanddrv_main.c
+++ b/dlls/winewayland.drv/waylanddrv_main.c
@@ -48,6 +48,7 @@ static const struct user_driver_funcs waylanddrv_funcs =
     .pSetWindowStyle = WAYLAND_SetWindowStyle,
     .pSetWindowText = WAYLAND_SetWindowText,
     .pSysCommand = WAYLAND_SysCommand,
+    .pUpdateLayeredWindow = WAYLAND_UpdateLayeredWindow,
     .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices,
     .pWindowMessage = WAYLAND_WindowMessage,
     .pWindowPosChanged = WAYLAND_WindowPosChanged,
diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c
index 88eee3421d72..ff58a68809b2 100644
--- a/dlls/winewayland.drv/window.c
+++ b/dlls/winewayland.drv/window.c
@@ -174,6 +174,7 @@ static void wayland_win_data_get_config(struct wayland_win_data *data,
         window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED;
     }
 
+    conf->resizeable = data->resizeable;
     conf->state = window_state;
     conf->scale = NtUserGetSystemDpiForProcess(0) / 96.0;
     conf->visible = (style & WS_VISIBLE) == WS_VISIBLE;
@@ -465,6 +466,7 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN
 
     data->rects = *new_rects;
     data->is_fullscreen = fullscreen;
+    data->resizeable = swp_flags & WINE_SWP_RESIZABLE;
     data->managed = managed;
 
     if (!surface)
@@ -620,7 +622,8 @@ static void wayland_configure_window(HWND hwnd)
     if (window_width == 0 || window_height == 0) flags |= SWP_NOSIZE;
 
     style = NtUserGetWindowLongW(hwnd, GWL_STYLE);
-    if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE))
+    if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE)
+        && !(state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
         NtUserSetWindowLong(hwnd, GWL_STYLE, style ^ WS_MAXIMIZE, FALSE);
 
     /* The Wayland maximized and fullscreen states are very strict about
@@ -673,9 +676,14 @@ LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
 void WAYLAND_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWORD flags)
 {
     struct wayland_win_data *data;
+    struct wayland_surface *surface;
 
     if (!(data = wayland_win_data_get(hwnd))) return;
+
+    if ((surface = data->wayland_surface))
+        wayland_surface_set_opacity(surface, alpha, flags);
     data->layered_attribs_set = TRUE;
+
     wayland_win_data_release(data);
 }
 
@@ -727,6 +735,7 @@ void WAYLAND_SetWindowIcons(HWND hwnd, HICON icon, const ICONINFO *ii, HICON ico
 void WAYLAND_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style)
 {
     struct wayland_win_data *data;
+    struct wayland_surface *surface;
     DWORD changed = style->styleNew ^ style->styleOld;
 
     if (hwnd == NtUserGetDesktopWindow()) return;
@@ -734,7 +743,11 @@ void WAYLAND_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style)
 
     /* Changing WS_EX_LAYERED resets attributes */
     if (offset == GWL_EXSTYLE && (changed & WS_EX_LAYERED))
+    {
+        if ((surface = data->wayland_surface))
+            wayland_surface_set_opacity(surface, 0, 0);
         data->layered_attribs_set = FALSE;
+    }
 
     wayland_win_data_release(data);
 }
@@ -808,6 +821,22 @@ LRESULT WAYLAND_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam, const POINT
     return ret;
 }
 
+/***********************************************************************
+ *          WAYLAND_UpdateLayeredWindow
+ */
+void WAYLAND_UpdateLayeredWindow(HWND hwnd, BYTE alpha, UINT flags)
+{
+    struct wayland_win_data *data;
+    struct wayland_surface *surface;
+
+    if (!(data = wayland_win_data_get(hwnd))) return;
+
+    if ((surface = data->wayland_surface))
+        wayland_surface_set_opacity(surface, alpha, flags);
+
+    wayland_win_data_release(data);
+}
+
 void set_client_surface(HWND hwnd, struct wayland_client_surface *new_client)
 {
     HWND toplevel = NtUserGetAncestor(hwnd, GA_ROOT);
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c
index 4e6ccc11d65d..3d5d2c81f034 100644
--- a/dlls/winex11.drv/keyboard.c
+++ b/dlls/winex11.drv/keyboard.c
@@ -1547,35 +1547,6 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
     return TRUE;
 }
 
-static BOOL find_xkb_layout_variant( const char *name, const char **layout, const char **variant )
-{
-#ifdef SONAME_LIBXKBREGISTRY
-    struct rxkb_layout *iter;
-
-    if (rxkb_context)
-    {
-        for (iter = p_rxkb_layout_first( rxkb_context ); iter; iter = p_rxkb_layout_next( iter ))
-        {
-            const char *desc = p_rxkb_layout_get_description( iter );
-
-            if (desc && !strcmp( name, desc ))
-            {
-                *layout = p_rxkb_layout_get_name( iter );
-                *variant = p_rxkb_layout_get_variant( iter );
-                return TRUE;
-            }
-        }
-
-        WARN( "Unknown Xkb layout name %s\n", debugstr_a(name) );
-    }
-    else
-        WARN( "libxkbregistry not available, falling back to fuzzy layout detection\n" );
-#else
-    WARN( "libxkbregistry support not compiled in, falling back to fuzzy layout detection\n" );
-#endif
-    return FALSE;
-}
-
 static const struct layout_id_map_entry
 {
     const char *name;
@@ -1698,6 +1669,14 @@ static LANGID langid_from_xkb_layout( const char *layout )
     return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED);
 };
 
+static const char *xkb_layout_from_langid( LANGID langid )
+{
+    for (int i = 0; i < ARRAY_SIZE(layout_ids); i++)
+        if (langid == layout_ids[i].langid)
+            return layout_ids[i].name;
+    return NULL;
+}
+
 static const struct klid_map_entry
 {
     const char *layout;
@@ -1736,9 +1715,9 @@ static DWORD klid_from_xkb_layout( const char *layout, const char *variant )
 }
 
 /* fuzzy layout detection through keysym / keycode matching, kbd_section must be held */
-static void detect_keyboard_layout( Display *display, XModifierKeymap *modmap, unsigned int xkb_group )
+static unsigned int detect_keyboard_layout( Display *display, XModifierKeymap *modmap, unsigned int xkb_group )
 {
-  unsigned current, match, mismatch, seq, i, syms;
+  unsigned current, match, mismatch, seq, i, syms, best_layout = 0;
   int score, keyc, key, pkey, ok;
   KeySym keysym;
   const char (*lkey)[MAIN_LEN][4];
@@ -1843,7 +1822,7 @@ static void detect_keyboard_layout( Display *display, XModifierKeymap *modmap, u
 	   match, mismatch, seq, score);
     if (score + (int)seq > max_score + (int)max_seq) {
       /* best match so far */
-      kbd_layout = current;
+      best_layout = current;
       max_score = score;
       max_seq = seq;
       ismatch = !mismatch;
@@ -1852,9 +1831,10 @@ static void detect_keyboard_layout( Display *display, XModifierKeymap *modmap, u
   /* we're done, report results if necessary */
   if (!ismatch)
     WARN("Using closest match (%s) for scan/virtual codes mapping.\n",
-        main_key_tab[kbd_layout].comment);
+        main_key_tab[best_layout].comment);
 
-  TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment);
+  TRACE("detected layout is \"%s\"\n", main_key_tab[best_layout].comment);
+  return best_layout;
 }
 
 
@@ -2090,10 +2070,44 @@ static void init_keycode_mappings( Display *display )
       }
 }
 
+static void find_xkb_layout_variant( Display *display, XModifierKeymap *mmp, int group, const char *name,
+                                     const char **layout, const char **variant )
+{
+    unsigned int index;
+#ifdef SONAME_LIBXKBREGISTRY
+    struct rxkb_layout *iter;
+
+    if (!rxkb_context) WARN( "libxkbregistry not available, falling back to fuzzy layout detection\n" );
+    else if (!name) WARN( "Xkb layout name not found, falling back to fuzzy layout detection\n" );
+    else
+    {
+        for (iter = p_rxkb_layout_first( rxkb_context ); iter; iter = p_rxkb_layout_next( iter ))
+        {
+            const char *desc = p_rxkb_layout_get_description( iter );
+
+            if (desc && !strcmp( name, desc ))
+            {
+                *layout = p_rxkb_layout_get_name( iter );
+                *variant = p_rxkb_layout_get_variant( iter );
+                return;
+            }
+        }
+
+        WARN( "Unknown Xkb layout name %s\n", debugstr_a(name) );
+    }
+#else
+    WARN( "libxkbregistry support not compiled in, falling back to fuzzy layout detection\n" );
+#endif
+
+    index = detect_keyboard_layout( display, mmp, group );
+    *layout = xkb_layout_from_langid( main_key_tab[index].lcid );
+    if (strstr( main_key_tab[index].comment, "dvorak" )) *variant = "dvorak";
+}
 
 /* initialize or update keyboard layouts */
 void init_keyboard_layouts( Display *display )
 {
+    char *names[4] = { NULL };
     unsigned int xkb_group;
     XkbStateRec xkb_state;
     XModifierKeymap *mmp;
@@ -2102,6 +2116,7 @@ void init_keyboard_layouts( Display *display )
     LANGID xkb_lang = 0;
     Status status;
     KeyCode *kcp;
+    int count;
 
     pthread_mutex_lock( &kbd_mutex );
     XDisplayKeycodes( display, &min_keycode, &max_keycode );
@@ -2139,42 +2154,38 @@ void init_keyboard_layouts( Display *display )
 
     if ((xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd )))
     {
-        char *names[4];
-        int count;
-
         XkbGetNames( display, XkbGroupNamesMask, xkb_desc );
         for (count = 0; count < ARRAY_SIZE(xkb_desc->names->groups); count++)
             if (!xkb_desc->names->groups[count]) break;
 
         if (!XGetAtomNames( display, xkb_desc->names->groups, count, names )) count = 0;
-        TRACE("Found %u group names\n", count);
-        for (int i = 0; i < count; i++)
-        {
-            const char *layout, *variant = NULL;
-            char buffer[1024];
-            DWORD klid;
-            LANGID lang;
+        TRACE( "Found %u group names\n", count );
+        XkbFreeKeyboard( xkb_desc, 0, True );
+    }
+    else count = status ? 1 : 4;
 
-            if (!names[i]) continue;
-            if (find_xkb_layout_variant( names[i], &layout, &variant ))
-            {
-                lang = langid_from_xkb_layout( layout );
-                klid = klid_from_xkb_layout( layout, variant );
-                if (i == xkb_group) xkb_lang = lang;
+    for (int i = 0; i < count; i++)
+    {
+        const char *layout, *variant = NULL;
+        char buffer[1024];
+        DWORD klid;
+        LANGID lang;
 
-                TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x, klid %08x\n", i, debugstr_a(names[i]),
-                       debugstr_a(layout), debugstr_a(variant), lang, klid );
+        find_xkb_layout_variant( display, mmp, i, names[i], &layout, &variant );
+        lang = langid_from_xkb_layout( layout );
+        klid = klid_from_xkb_layout( layout, variant );
+        if (i == xkb_group) xkb_lang = lang;
 
-                snprintf( buffer, ARRAY_SIZE(buffer), "%s:%s", layout, variant );
-                create_layout_from_xkb( i, buffer, lang, klid );
-            }
-            XFree( names[i] );
-        }
+        TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x, klid %08x\n", i, debugstr_a(names[i]),
+               debugstr_a(layout), debugstr_a(variant), lang, klid );
 
-        XkbFreeKeyboard( xkb_desc, 0, True );
+        snprintf( buffer, ARRAY_SIZE(buffer), "%s:%s", layout, variant );
+        create_layout_from_xkb( i, buffer, lang, klid );
+
+        if (names[i]) XFree( names[i] );
     }
 
-    detect_keyboard_layout( display, mmp, xkb_group );
+    kbd_layout = detect_keyboard_layout( display, mmp, xkb_group );
     XFreeModifiermap( mmp );
 
     if (xkb_lang && xkb_lang != main_key_tab[kbd_layout].lcid)
diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c
index f8d1fab1332c..e79bb314c5bf 100644
--- a/dlls/winexinput.sys/main.c
+++ b/dlls/winexinput.sys/main.c
@@ -235,9 +235,9 @@ static void translate_report_to_xinput_state(struct func_device *fdo)
         fdo->xinput_state.buttons |= (1 << (usages[i] - 1));
     }
     fdo->xinput_state.lx_axis = scale_value(lx, &fdo->lx_caps, 0, 65535);
-    fdo->xinput_state.ly_axis = scale_value(-ly - 1, &fdo->ly_caps, 0, 65535);
+    fdo->xinput_state.ly_axis = 65535 - scale_value(ly, &fdo->ly_caps, 0, 65535);
     fdo->xinput_state.rx_axis = scale_value(rx, &fdo->rx_caps, 0, 65535);
-    fdo->xinput_state.ry_axis = scale_value(-ry - 1, &fdo->ry_caps, 0, 65535);
+    fdo->xinput_state.ry_axis = 65535 - scale_value(ry, &fdo->ry_caps, 0, 65535);
     rt = scale_value(rt, &fdo->rt_caps, 0, 255);
     lt = scale_value(lt, &fdo->lt_caps, 0, 255);
     fdo->xinput_state.trigger = 0x8000 + (lt - rt) * 128;
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index b1f13eafbb5e..64452f997278 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -3347,7 +3347,7 @@ BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
     return !ret;
 }
 
-#define RECURSION_LIMIT 5
+#define RECURSION_LIMIT 3
 static BOOL skip_async_queue( struct request *request, BOOL *wont_block, DWORD to_read )
 {
     to_read = min( to_read, request->content_length - request->content_read );
diff --git a/dlls/winsqlite3/make_sqlite.py b/dlls/winsqlite3/make_sqlite.py
index 4485c70fbe36..285311df6704 100755
--- a/dlls/winsqlite3/make_sqlite.py
+++ b/dlls/winsqlite3/make_sqlite.py
@@ -11,7 +11,7 @@ import urllib.request
 import zipfile
 from clang.cindex import Cursor, CursorKind, Index, TypeKind, TokenGroup
 
-SQLITE_VERSION = "3510100"
+SQLITE_VERSION = "3530200"
 
 REPLACE_STRINGS = [
     #Uppercase in Windows.h breaks clang compilation.
@@ -23,6 +23,9 @@ REPLACE_STRINGS = [
     #compiler warnings, unique cases which are harder to fix up in general code
     ("(sqlite3_uint64)pFile->h", "(sqlite3_uint64)(ULONG_PTR)pFile->h"),
     ("((int(*)(void *))(ap[0]))==xBusyHandler", "((int(SQLITE_APICALL *)(void *))(ap[0]))==xBusyHandler"),
+    #missing extern when debug code is disabled
+    ("SQLITE_API int sqlite3_fts3_may_be_corrupt = 1;",
+     "#endif\nSQLITE_API int sqlite3_fts3_may_be_corrupt = 1;\n#if 0"),
 ]
 
 EXPORTS = [
@@ -410,7 +413,7 @@ dir = "sqlite-amalgamation-" + SQLITE_VERSION;
 if not Path(dir).is_dir():
     fn = "sqlite-amalgamation-" + SQLITE_VERSION + ".zip";
     if not Path(fn).is_file():
-        url = "https://sqlite.org/2025/" + fn;
+        url = "https://sqlite.org/2026/" + fn;
         print("Dowloading", url);
         urllib.request.urlretrieve(url, fn);
     print("\nExtracting source");
@@ -438,7 +441,7 @@ out.write("/* Fixed up with dlls/winsqlite/make_sqlite.py */\n")
 args = [];
 args += ['-target', 'x86_64-pc-windows-gnu'];
 args += ['-Iinclude/msvcrt', '-Iinclude'];
-args += ["-DSQLITE_ENABLE_RTREE=1", "-DSQLITE_ENABLE_FTS4=1", "-DSQLITE_ENABLE_COLUMN_METADATA=1", "-DSQLITE_DEBUG=1", "-DSQLITE_SYSTEM_MALLOC=1", "-DSQLITE_OMIT_LOCALTIME=1" ];
+args += ["-DSQLITE_ENABLE_RTREE=1", "-DSQLITE_ENABLE_FTS4=1", "-DSQLITE_ENABLE_COLUMN_METADATA=1", "-DSQLITE_SYSTEM_MALLOC=1", "-DSQLITE_OMIT_LOCALTIME=1" ];
 args += ["-DSQLITE_ENABLE_MATH_FUNCTIONS", "-DSQLITE_ENABLE_PERCENTILE" ];
 
 index = Index.create()
diff --git a/dlls/wpcap/tests/wpcap.c b/dlls/wpcap/tests/wpcap.c
index a7abf8042f68..d0ce22a4842f 100644
--- a/dlls/wpcap/tests/wpcap.c
+++ b/dlls/wpcap/tests/wpcap.c
@@ -75,7 +75,6 @@ struct pcap_pkthdr
 };
 
 static int (CDECL *ppcap_activate)( pcap_t * );
-static void (CDECL *ppcap_breakloop)( pcap_t * );
 static int (CDECL *ppcap_bufsize)( pcap_t * );
 static int (CDECL *ppcap_can_set_rfmon)( pcap_t * );
 static void (CDECL *ppcap_close)( pcap_t * );
@@ -106,9 +105,6 @@ static int (CDECL *ppcap_list_datalinks)( pcap_t *, int ** );
 static int (CDECL *ppcap_list_tstamp_types)( pcap_t *, int ** );
 static char * (CDECL *ppcap_lookupdev)( char * );
 static int (CDECL *ppcap_lookupnet)( const char *, unsigned int *, unsigned int *, char * );
-static int (CDECL *ppcap_loop)( pcap_t *, int,
-                                void (CDECL *)(unsigned char *, const struct pcap_pkthdr *, const unsigned char *),
-                                unsigned char * );
 static int (CDECL *ppcap_set_buffer_size)( pcap_t *, int );
 static int (CDECL *ppcap_set_datalink)( pcap_t *, int );
 static int (CDECL *ppcap_set_immediate_mode)( pcap_t *, int );
@@ -118,9 +114,6 @@ static int (CDECL *ppcap_set_tstamp_precision)( pcap_t *, int );
 static int (CDECL *ppcap_setfilter)( pcap_t *, struct bpf_program * );
 static int (CDECL *ppcap_snapshot)( pcap_t * );
 static int (CDECL *ppcap_stats)( pcap_t *, struct pcap_stat * );
-static int CDECL (*ppcap_tstamp_type_name_to_val)( const char * );
-static const char * (CDECL *ppcap_tstamp_type_val_to_description)( int );
-static const char * (CDECL *ppcap_tstamp_type_val_to_name)( int );
 
 static void CDECL capture_callback( unsigned char *user, const struct pcap_pkthdr *hdr, const unsigned char *bytes )
 {
@@ -333,7 +326,6 @@ START_TEST( wpcap )
         return;
     }
     ppcap_activate = (void *)GetProcAddress( module, "pcap_activate" );
-    ppcap_breakloop = (void *)GetProcAddress( module, "pcap_breakloop" );
     ppcap_bufsize = (void *)GetProcAddress( module, "pcap_bufsize" );
     ppcap_can_set_rfmon = (void *)GetProcAddress( module, "pcap_can_set_rfmon" );
     ppcap_close = (void *)GetProcAddress( module, "pcap_close" );
@@ -362,7 +354,6 @@ START_TEST( wpcap )
     ppcap_list_tstamp_types = (void *)GetProcAddress( module, "pcap_list_tstamp_types" );
     ppcap_lookupdev = (void *)GetProcAddress( module, "pcap_lookupdev" );
     ppcap_lookupnet = (void *)GetProcAddress( module, "pcap_lookupnet" );
-    ppcap_loop = (void *)GetProcAddress( module, "pcap_loop" );
     ppcap_set_buffer_size = (void *)GetProcAddress( module, "pcap_set_buffer_size" );
     ppcap_set_datalink = (void *)GetProcAddress( module, "pcap_set_datalink" );
     ppcap_set_immediate_mode = (void *)GetProcAddress( module, "pcap_set_immediate_mode" );
@@ -372,9 +363,6 @@ START_TEST( wpcap )
     ppcap_setfilter = (void *)GetProcAddress( module, "pcap_setfilter" );
     ppcap_snapshot = (void *)GetProcAddress( module, "pcap_snapshot" );
     ppcap_stats = (void *)GetProcAddress( module, "pcap_stats" );
-    ppcap_tstamp_type_name_to_val = (void *)GetProcAddress( module, "pcap_tstamp_type_name_to_val" );
-    ppcap_tstamp_type_val_to_description = (void *)GetProcAddress( module, "pcap_tstamp_type_val_to_description" );
-    ppcap_tstamp_type_val_to_name = (void *)GetProcAddress( module, "pcap_tstamp_type_val_to_name" );
 
     trace( "lib version %s\n", ppcap_lib_version() );
     trace( "supports PCAP_MMAP_32BIT: %s\n", (ppcap_init(PCAP_MMAP_32BIT, errbuf) < 0) ? "no" : "yes" );
diff --git a/include/bcrypt.h b/include/bcrypt.h
index bf22f6dfb9b0..fe361307b6c2 100644
--- a/include/bcrypt.h
+++ b/include/bcrypt.h
@@ -94,6 +94,7 @@ extern "C" {
 
 #define BCRYPT_3DES_ALGORITHM       L"3DES"
 #define BCRYPT_AES_ALGORITHM        L"AES"
+#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
 #define BCRYPT_DES_ALGORITHM        L"DES"
 #define BCRYPT_DH_ALGORITHM         L"DH"
 #define BCRYPT_DSA_ALGORITHM        L"DSA"
@@ -108,6 +109,7 @@ extern "C" {
 #define BCRYPT_MD2_ALGORITHM        L"MD2"
 #define BCRYPT_MD4_ALGORITHM        L"MD4"
 #define BCRYPT_MD5_ALGORITHM        L"MD5"
+#define BCRYPT_PBKDF2_ALGORITHM     L"PBKDF2"
 #define BCRYPT_RC2_ALGORITHM        L"RC2"
 #define BCRYPT_RC4_ALGORITHM        L"RC4"
 #define BCRYPT_RNG_ALGORITHM        L"RNG"
@@ -117,7 +119,6 @@ extern "C" {
 #define BCRYPT_SHA256_ALGORITHM     L"SHA256"
 #define BCRYPT_SHA384_ALGORITHM     L"SHA384"
 #define BCRYPT_SHA512_ALGORITHM     L"SHA512"
-#define BCRYPT_PBKDF2_ALGORITHM     L"PBKDF2"
 
 #define BCRYPT_CHAIN_MODE_NA        L"ChainingModeN/A"
 #define BCRYPT_CHAIN_MODE_CBC       L"ChainingModeCBC"
@@ -129,6 +130,7 @@ extern "C" {
 #define BCRYPT_ECC_CURVE_NAME             L"ECCCurveName"
 #define BCRYPT_ECC_CURVE_25519            L"curve25519"
 #define BCRYPT_ECC_CURVE_BRAINPOOLP256R1  L"brainpoolP256r1"
+#define BCRYPT_ECC_CURVE_BRAINPOOLP384R1  L"brainpoolP384r1"
 #define BCRYPT_ECC_CURVE_SECP256R1        L"secP256r1"
 #define BCRYPT_ECC_CURVE_SECP384R1        L"secP384r1"
 #define BCRYPT_ECC_CURVE_SECP521R1        L"secP521r1"
@@ -188,6 +190,7 @@ static const WCHAR MS_PLATFORM_CRYPTO_PROVIDER[] = \
 
 static const WCHAR BCRYPT_3DES_ALGORITHM[] = {'3','D','E','S',0};
 static const WCHAR BCRYPT_AES_ALGORITHM[] = {'A','E','S',0};
+static const WCHAR BCRYPT_CHACHA20_POLY1305_ALGORITHM[] = {'C','H','A','C','H','A','2','0','_','P','O','L','Y','1','3','0','5',0};
 static const WCHAR BCRYPT_DES_ALGORITHM[] = {'D','E','S',0};
 static const WCHAR BCRYPT_DH_ALGORITHM[] = {'D','H',0};
 static const WCHAR BCRYPT_DSA_ALGORITHM[] = {'D','S','A',0};
@@ -202,6 +205,7 @@ static const WCHAR BCRYPT_ECDSA_P521_ALGORITHM[] = {'E','C','D','S','A','_','P',
 static const WCHAR BCRYPT_MD2_ALGORITHM[] = {'M','D','2',0};
 static const WCHAR BCRYPT_MD4_ALGORITHM[] = {'M','D','4',0};
 static const WCHAR BCRYPT_MD5_ALGORITHM[] = {'M','D','5',0};
+static const WCHAR BCRYPT_PBKDF2_ALGORITHM[] = {'P','B','K','D','F','2',0};
 static const WCHAR BCRYPT_RC2_ALGORITHM[] = {'R','C','2',0};
 static const WCHAR BCRYPT_RC4_ALGORITHM[] = {'R','C','4',0};
 static const WCHAR BCRYPT_RNG_ALGORITHM[] = {'R','N','G',0};
@@ -211,7 +215,6 @@ static const WCHAR BCRYPT_SHA1_ALGORITHM[] = {'S','H','A','1',0};
 static const WCHAR BCRYPT_SHA256_ALGORITHM[] = {'S','H','A','2','5','6',0};
 static const WCHAR BCRYPT_SHA384_ALGORITHM[] = {'S','H','A','3','8','4',0};
 static const WCHAR BCRYPT_SHA512_ALGORITHM[] = {'S','H','A','5','1','2',0};
-static const WCHAR BCRYPT_PBKDF2_ALGORITHM[] = {'P','B','K','D','F','2',0};
 
 static const WCHAR BCRYPT_CHAIN_MODE_NA[] = {'C','h','a','i','n','i','n','g','M','o','d','e','N','/','A',0};
 static const WCHAR BCRYPT_CHAIN_MODE_CBC[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','B','C',0};
@@ -223,6 +226,7 @@ static const WCHAR BCRYPT_CHAIN_MODE_GCM[] = {'C','h','a','i','n','i','n','g','M
 static const WCHAR BCRYPT_ECC_CURVE_NAME[] = {'E','C','C','C','u','r','v','e','N','a','m','e',0};
 static const WCHAR BCRYPT_ECC_CURVE_25519[] = {'c','u','r','v','e','2','5','5','1','9',0};
 static const WCHAR BCRYPT_ECC_CURVE_BRAINPOOLP256R1[] = {'b','r','a','i','n','p','o','o','l','P','2','5','6','r','1',0};
+static const WCHAR BCRYPT_ECC_CURVE_BRAINPOOLP384R1[] = {'b','r','a','i','n','p','o','o','l','P','3','8','4','r','1',0};
 static const WCHAR BCRYPT_ECC_CURVE_SECP256R1[] = {'s','e','c','P','2','5','6','r','1',0};
 static const WCHAR BCRYPT_ECC_CURVE_SECP384R1[] = {'s','e','c','P','3','8','4','r','1',0};
 static const WCHAR BCRYPT_ECC_CURVE_SECP521R1[] = {'s','e','c','P','5','2','1','r','1',0};
@@ -253,6 +257,9 @@ static const WCHAR BCRYPT_DH_PARAMETERS[] = {'D','H','P','a','r','a','m','e','t'
 #define BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC  0x504b4345
 #define BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC 0x564b4345
 
+#define BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC  0x50444345
+#define BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC 0x56444345
+
 #define BCRYPT_CIPHER_OPERATION                 0x00000001
 #define BCRYPT_HASH_OPERATION                   0x00000002
 #define BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION  0x00000004
@@ -549,6 +556,7 @@ typedef PVOID BCRYPT_SECRET_HANDLE;
 #define BCRYPT_TLS1_2_KDF_ALG_HANDLE        ((BCRYPT_ALG_HANDLE)0x00000371)
 #define BCRYPT_XTS_AES_ALG_HANDLE           ((BCRYPT_ALG_HANDLE)0x00000381)
 #define BCRYPT_HKDF_ALG_HANDLE              ((BCRYPT_ALG_HANDLE)0x00000391)
+#define BCRYPT_CHACHA20_POLY1305_ALG_HANDLE ((BCRYPT_ALG_HANDLE)0x000003a1)
 
 /* Flags for BCryptGenRandom */
 #define BCRYPT_RNG_USE_ENTROPY_IN_BUFFER 0x00000001
diff --git a/include/icm.h b/include/icm.h
index bcf79eb63bda..ec6987db6631 100644
--- a/include/icm.h
+++ b/include/icm.h
@@ -498,6 +498,7 @@ BOOL       WINAPI UninstallColorProfileW(PCWSTR,PCWSTR,BOOL);
 BOOL       WINAPI UnregisterCMMA(PCSTR,DWORD);
 BOOL       WINAPI UnregisterCMMW(PCWSTR,DWORD);
 #define    UnregisterCMM WINELIB_NAME_AW(UnregisterCMM)
+HPROFILE   WINAPI WcsCreateIccProfile(HPROFILE,DWORD);
 BOOL       WINAPI WcsEnumColorProfilesSize(WCS_PROFILE_MANAGEMENT_SCOPE,ENUMTYPEW*,DWORD*);
 BOOL       WINAPI WcsGetDefaultColorProfile(WCS_PROFILE_MANAGEMENT_SCOPE,const WCHAR*,COLORPROFILETYPE,COLORPROFILESUBTYPE,DWORD,DWORD,LPWSTR);
 BOOL       WINAPI WcsGetDefaultColorProfileSize(WCS_PROFILE_MANAGEMENT_SCOPE,const WCHAR*,COLORPROFILETYPE,COLORPROFILESUBTYPE,DWORD,DWORD*);
diff --git a/include/mfd3d12.idl b/include/mfd3d12.idl
index 2098e6d405ab..d6124d4a2797 100644
--- a/include/mfd3d12.idl
+++ b/include/mfd3d12.idl
@@ -40,8 +40,11 @@ interface IMFD3D12SynchronizationObjectCommands : IUnknown
 interface IMFD3D12SynchronizationObject : IUnknown
 {
     HRESULT SignalEventOnFinalResourceRelease(HANDLE hEvent);
+    HRESULT Reset();
 }
 
+cpp_quote("HRESULT WINAPI MFCreateD3D12SynchronizationObject(ID3D12Device *device, REFIID riid, void **obj);")
+
 typedef enum MF_MT_D3D_RESOURCE_VERSION_ENUM
 {
     MF_D3D11_RESOURCE,
@@ -58,6 +61,8 @@ cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, 0x82c85
 cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE, 0xba06bfac, 0xffe3, 0x474a, 0xab, 0x55, 0x16, 0x1e, 0xe4, 0x41, 0x7a, 0x2e);")
 cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER, 0xa6a1e439, 0x2f96, 0x4ab5, 0x98, 0xdc, 0xad, 0xf7, 0x49, 0x73, 0x50, 0x5d);")
 cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, 0xa4940b2, 0xcfd6, 0x4738, 0x9d, 0x02, 0x98, 0x11, 0x37, 0x34, 0x01, 0x5a);")
+cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_DIMENSION, 0x5f772624, 0x16ca, 0x4b89, 0x96, 0x51, 0x5d, 0xdf, 0x76, 0x9f, 0x8a, 0xb8);")
 cpp_quote("EXTERN_GUID(MF_SA_D3D12_HEAP_FLAGS, 0x496b3266, 0xd28f, 0x4f8c, 0x93, 0xa7, 0x4a, 0x59, 0x6b, 0x1a, 0x31, 0xa1);")
 cpp_quote("EXTERN_GUID(MF_SA_D3D12_HEAP_TYPE, 0x56f26a76, 0xbbc1, 0x4ce0, 0xbb, 0x11, 0xe2, 0x23, 0x68, 0xd8, 0x74, 0xed);")
 cpp_quote("EXTERN_GUID(MF_SA_D3D12_CLEAR_VALUE, 0x86ba9a39, 0x526, 0x495d, 0x9a, 0xb5, 0x54, 0xec, 0x9f, 0xad, 0x6f, 0xc3);")
+cpp_quote("EXTERN_GUID(MF_SA_D3D12_AWARE, 0x77f0bacb, 0x17a8, 0x4a50, 0x9a, 0x7d, 0xa5, 0xcc, 0x9, 0xd3, 0x9d, 0x44);")
diff --git a/include/msinkaut.idl b/include/msinkaut.idl
index 6459ec5798ad..cf1a4a81b6ae 100644
--- a/include/msinkaut.idl
+++ b/include/msinkaut.idl
@@ -26,6 +26,8 @@ library MSINKAUTLib
 {
     importlib("stdole2.tlb");
 
+    interface IInkRecognizerContext;
+
     typedef float single;
 
     typedef enum InkBoundingBoxMode {
@@ -141,6 +143,114 @@ library MSINKAUTLib
         SHR_Selection = 9
     } SelectionHitResult;
 
+    typedef enum
+    {
+        IRCACM_Full   = 0,
+        IRCACM_Prefix = 1,
+        IRCACM_Random = 2
+    } InkRecognizerCharacterAutoCompletionMode;
+
+    typedef struct tagtagRECT
+    {
+        LONG left;
+        LONG top;
+        LONG right;
+        LONG bottom;
+    } tagRECT;
+
+    typedef struct tag_InkRecoGuide
+    {
+        tagRECT rectWritingBox;
+        tagRECT rectDrawnBox;
+        long cRows;
+        long cColumns;
+        long Midline;
+    } _InkRecoGuide;
+
+    typedef enum
+    {
+        IRM_None                   = 0x00,
+        IRM_WordModeOnly           = 0x01,
+        IRM_Coerce                 = 0x02,
+        IRM_TopInkBreaksOnly       = 0x04,
+        IRM_PrefixOk               = 0x08,
+        IRM_LineMode               = 0x10,
+        IRM_DisablePersonalization = 0x20,
+        IRM_AutoSpace              = 0x40,
+        IRM_Max                    = 0x80
+    } InkRecognitionModes;
+
+    typedef [public] _InkRecoGuide InkRecoGuide;
+
+    typedef enum
+    {
+        IRC_DontCare                     = 0x00000001,
+        IRC_Object                       = 0x00000002,
+        IRC_FreeInput                    = 0x00000004,
+        IRC_LinedInput                   = 0x00000008,
+        IRC_BoxedInput                   = 0x00000010,
+        IRC_CharacterAutoCompletionInput = 0x00000020,
+        IRC_RightAndDown                 = 0x00000040,
+        IRC_LeftAndDown                  = 0x00000080,
+        IRC_DownAndLeft                  = 0x00000100,
+        IRC_DownAndRight                 = 0x00000200,
+        IRC_ArbitraryAngle               = 0x00000400,
+        IRC_Lattice                      = 0x00000800,
+        IRC_AdviseInkChange              = 0x00001000,
+        IRC_StrokeReorder                = 0x00002000,
+        IRC_Personalizable               = 0x00004000,
+        IRC_PrefersArbitraryAngle        = 0x00008000,
+        IRC_PrefersParagraphBreaking     = 0x00010000,
+        IRC_PrefersSegmentation          = 0x00020000,
+        IRC_Cursive                      = 0x00040000,
+        IRC_TextPrediction               = 0x00080000,
+        IRC_Alpha                        = 0x00100000,
+        IRC_Beta                         = 0x00200000
+    } InkRecognizerCapabilities;
+
+    typedef enum
+    {
+        IRS_NoError                     = 0x0000,
+        IRS_Interrupted                 = 0x0001,
+        IRS_ProcessFailed               = 0x0002,
+        IRS_InkAddedFailed              = 0x0004,
+        IRS_SetAutoCompletionModeFailed = 0x0008,
+        IRS_SetStrokesFailed            = 0x0010,
+        IRS_SetGuideFailed              = 0x0020,
+        IRS_SetFlagsFailed              = 0x0040,
+        IRS_SetFactoidFailed            = 0x0080,
+        IRS_SetPrefixSuffixFailed       = 0x0100,
+        IRS_SetWordListFailed           = 0x0200
+    } InkRecognitionStatus;
+
+     typedef enum
+     {
+        IMF_Left   = 1,
+        IMF_Right  = 2,
+        IMF_Middle = 4
+    } InkMouseButton;
+
+    typedef enum
+    {
+        IKM_Shift   = 1,
+        IKM_Control = 2,
+        IKM_Alt     = 4
+    } InkShiftKeyModifierFlags;
+
+    typedef enum
+    {
+        ISG_Tap        = 16,
+        ISG_DoubleTap  = 17,
+        ISG_RightTap   = 18,
+        ISG_Drag       = 19,
+        ISG_RightDrag  = 20,
+        ISG_HoldEnter  = 21,
+        ISG_HoldLeave  = 22,
+        ISG_HoverEnter = 23,
+        ISG_HoverLeave = 24,
+        ISG_Flick      = 31
+    } InkSystemGesture;
+
     [
         odl,
         uuid(DB489209-B7C3-411D-90F6-1548CFFF271E),
@@ -1190,6 +1300,234 @@ cpp_quote("#endif /* _WINGDI_ */")
         HRESULT SetEventInterest([in] InkCollectorEventInterest EventId, [in] VARIANT_BOOL Listen);
     }
 
+    [
+        odl,
+        dual,
+        oleautomation,
+        uuid(76ba3491-cb2f-406b-9961-0e0c4cdaaef2),
+    ]
+    interface IInkWordList : IDispatch
+    {
+        [id(00000000)]
+        HRESULT AddWord([in] BSTR NewWord);
+        [id(0x00000001)]
+        HRESULT RemoveWord([in] BSTR RemoveWord);
+        [id(0x00000002)]
+        HRESULT Merge([in] IInkWordList* MergeWordList);
+    };
+
+    [
+        uuid(17bce92f-2e21-47fd-9d33-3c6afbfd8c59)
+    ]
+    dispinterface _IInkRecognitionEvents
+    {
+        properties:
+        methods:
+            [id(0x00000001)]
+            void RecognitionWithAlternates( [in] IInkRecognitionResult* RecognitionResult,
+                            [in] VARIANT CustomData, [in] InkRecognitionStatus RecognitionStatus);
+            [id(0x00000002)]
+            void Recognition( [in] BSTR RecognizedString, [in] VARIANT CustomData, [in] InkRecognitionStatus RecognitionStatus);
+    };
+
+    [
+        uuid(11a583f2-712d-4fea-abcf-ab4af38ea06b)
+    ]
+    dispinterface _IInkCollectorEvents
+    {
+        properties:
+        methods:
+            [id(0x00000001)]
+            void Stroke([in] IInkCursor* Cursor, [in] IInkStrokeDisp* Stroke, [in, out] VARIANT_BOOL* Cancel);
+            [id(0x00000002)]
+            void CursorDown([in] IInkCursor* Cursor, [in] IInkStrokeDisp* Stroke);
+            [id(0x00000003)]
+            void NewPackets([in] IInkCursor* Cursor, [in] IInkStrokeDisp* Stroke, [in] long PacketCount,
+                            [in, out] VARIANT* PacketData);
+            [id(0x00000019)]
+            void DblClick([in, out] VARIANT_BOOL* Cancel);
+            [id(0x0000001f)]
+            void MouseMove([in] InkMouseButton Button, [in] InkShiftKeyModifierFlags Shift, [in] long pX,
+                            [in] long pY, [in, out] VARIANT_BOOL* Cancel);
+            [id(0x0000001b)]
+            void MouseDown([in] InkMouseButton Button, [in] InkShiftKeyModifierFlags Shift, [in] long pX, [in] long pY,
+                            [in, out] VARIANT_BOOL* Cancel);
+            [id(0x00000020)]
+            void MouseUp([in] InkMouseButton Button, [in] InkShiftKeyModifierFlags Shift, [in] long pX,
+                            [in] long pY, [in, out] VARIANT_BOOL* Cancel);
+            [id(0x00000021)]
+            void MouseWheel([in] InkMouseButton Button, [in] InkShiftKeyModifierFlags Shift, [in] long Delta,
+                            [in] long x, [in] long y, [in, out] VARIANT_BOOL* Cancel);
+            [id(0x00000004)]
+            void NewInAirPackets([in] IInkCursor* Cursor, [in] long lPacketCount, [in, out] VARIANT* PacketData);
+            [id(0x00000005)]
+            void CursorButtonDown([in] IInkCursor* Cursor, [in] IInkCursorButton* Button);
+            [id(0x00000006)]
+            void CursorButtonUp([in] IInkCursor* Cursor, [in] IInkCursorButton* Button);
+            [id(0x00000007)]
+            void CursorInRange([in] IInkCursor* Cursor, [in] VARIANT_BOOL NewCursor, [in] VARIANT ButtonsState);
+            [id(0x00000008)]
+            void CursorOutOfRange([in] IInkCursor* Cursor);
+            [id(0x00000009)]
+            void SystemGesture([in] IInkCursor* Cursor, [in] InkSystemGesture Id, [in] long x, [in] long y,
+                            [in] long Modifier, [in] BSTR Character, [in] long CursorMode);
+            [id(0x0000000a)]
+            void Gesture([in] IInkCursor* Cursor, [in] IInkStrokes* Strokes, [in] VARIANT Gestures,
+                            [in, out] VARIANT_BOOL* Cancel);
+            [id(0x0000000b)]
+            void TabletAdded([in] IInkTablet* Tablet);
+            [id(0x0000000c)]
+            void TabletRemoved([in] long TabletId);
+    };
+
+    [
+        odl,
+        dual,
+        oleautomation,
+        uuid(782bf7cf-034b-4396-8a32-3a1833cf6b56)
+    ]
+    interface IInkRecognizer : IDispatch
+    {
+        [id(0x00000002), propget]
+        HRESULT Name([out, retval] BSTR* Name);
+        [id(0x00000003), propget]
+        HRESULT Vendor([out, retval] BSTR* Vendor);
+        [id(0x00000004), propget]
+        HRESULT Capabilities([out, retval] InkRecognizerCapabilities* CapabilitiesFlags);
+        [id(0x00000005), propget]
+        HRESULT Languages([out, retval] VARIANT* Languages);
+        [id(0x00000008), propget]
+        HRESULT SupportedProperties([out, retval] VARIANT* SupportedProperties);
+        [id(0x00000006), propget]
+        HRESULT PreferredPacketDescription([out, retval] VARIANT* PreferredPacketDescription);
+        [id(0x00000007)]
+        HRESULT CreateRecognizerContext([out, retval] IInkRecognizerContext** Context);
+    };
+
+    [
+        odl,
+        dual,
+        oleautomation,
+        uuid(6110118A-3A75-4AD6-B2AA-04B2B72BBE65)
+    ]
+    interface IInkRecognizer2 : IDispatch
+    {
+        [id(00000000), propget]
+        HRESULT Id([out, retval] BSTR* pbstrId);
+        [id(0x00000001), propget]
+        HRESULT UnicodeRanges([out, retval] VARIANT* UnicodeRanges);
+    };
+
+    [
+        odl,
+        dual,
+        oleautomation,
+        uuid(d934be07-7b84-4208-9136-83c20994e905)
+    ]
+    interface IInkRecognizerGuide : IDispatch
+    {
+        [id(0x00000001), propget]
+        HRESULT WritingBox([out, retval] IInkRectangle** Rectangle);
+        [id(0x00000001), propput]
+        HRESULT WritingBox([in] IInkRectangle* Rectangle);
+        [id(0x00000002), propget]
+        HRESULT DrawnBox([out, retval] IInkRectangle** Rectangle);
+        [id(0x00000002), propput]
+        HRESULT DrawnBox([in] IInkRectangle* Rectangle);
+        [id(0x00000003), propget]
+        HRESULT Rows([out, retval] long* Units);
+        [id(0x00000003), propput]
+        HRESULT Rows([in] long Units);
+        [id(0x00000004), propget]
+        HRESULT Columns([out, retval] long* Units);
+        [id(0x00000004), propput]
+        HRESULT Columns([in] long Units);
+        [id(0x00000005), propget]
+        HRESULT Midline([out, retval] long* Units);
+        [id(0x00000005), propput]
+        HRESULT Midline([in] long Units);
+        [id(0x00000006), propget, hidden]
+        HRESULT GuideData([out, retval] InkRecoGuide* pRecoGuide);
+        [id(0x00000006), propput, hidden]
+        HRESULT GuideData([in] InkRecoGuide pRecoGuide);
+    };
+
+    [
+        odl,
+        dual,
+        oleautomation,
+        uuid(c68f52f9-32a3-4625-906c-44fc23b40958)
+    ]
+    interface IInkRecognizerContext : IDispatch
+    {
+        [id(0x00000001), propget]
+        HRESULT Strokes([out, retval] IInkStrokes** Strokes);
+        [id(0x00000001), propputref]
+        HRESULT Strokes([in] IInkStrokes* Strokes);
+        [id(0x00000002), propget]
+        HRESULT CharacterAutoCompletionMode([out, retval] InkRecognizerCharacterAutoCompletionMode* Mode);
+        [id(0x00000002), propput]
+        HRESULT CharacterAutoCompletionMode([in] InkRecognizerCharacterAutoCompletionMode Mode);
+        [id(0x00000003), propget]
+        HRESULT Factoid([out, retval] BSTR* Factoid);
+        [id(0x00000003), propput]
+        HRESULT Factoid([in] BSTR Factoid);
+        [id(0x00000006), propget]
+        HRESULT Guide([out, retval] IInkRecognizerGuide** RecognizerGuide);
+        [id(0x00000006), propputref]
+        HRESULT Guide([in] IInkRecognizerGuide* RecognizerGuide);
+        [id(0x00000008), propget]
+        HRESULT PrefixText([out, retval] BSTR* Prefix);
+        [id(0x00000008), propput]
+        HRESULT PrefixText([in] BSTR Prefix);
+        [id(0x00000009), propget]
+        HRESULT SuffixText([out, retval] BSTR* Suffix);
+        [id(0x00000009), propput]
+        HRESULT SuffixText([in] BSTR Suffix);
+        [id(0x00000007), propget]
+        HRESULT RecognitionFlags([out, retval] InkRecognitionModes* Modes);
+        [id(0x00000007), propput]
+        HRESULT RecognitionFlags([in] InkRecognitionModes Modes);
+        [id(0x00000004), propget]
+        HRESULT WordList([out, retval] IInkWordList** WordList);
+        [id(0x00000004), propputref]
+        HRESULT WordList([in] IInkWordList* WordList);
+        [id(0x00000005), propget]
+        HRESULT Recognizer([out, retval] IInkRecognizer** Recognizer);
+        [id(0x0000000c)]
+        HRESULT Recognize([in, out] InkRecognitionStatus* RecognitionStatus, [out, retval] IInkRecognitionResult** RecognitionResult);
+        [id(0x0000000d)]
+        HRESULT StopBackgroundRecognition();
+        [id(0x0000000e)]
+        HRESULT EndInkInput();
+        [id(0x0000000f)]
+        HRESULT BackgroundRecognize([in, optional] VARIANT CustomData);
+        [id(0x00000010)]
+        HRESULT BackgroundRecognizeWithAlternates([in, optional] VARIANT CustomData);
+        [id(0x0000000b)]
+        HRESULT Clone([out, retval] IInkRecognizerContext** RecoContext);
+        [id(0x00000011)]
+        HRESULT IsStringSupported( [in] BSTR String, [out, retval] VARIANT_BOOL* Supported);
+    }
+
+    [
+        odl,
+        dual,
+        oleautomation,
+        uuid(9ccc4f12-b0b7-4a8b-bf58-4aeca4e8cefd)
+    ]
+    interface IInkRecognizers : IDispatch
+    {
+        [id(0x00000001)]
+        HRESULT Count([out, retval] long* Count);
+        [id(DISPID_NEWENUM)]
+        HRESULT _NewEnum([out, retval] IUnknown** _NewEnum);
+        [id(0x00000002)]
+        HRESULT GetDefaultRecognizer( [in, optional, defaultvalue(0)] long lcid, [out, retval] IInkRecognizer** DefaultRecognizer);
+        [id(DISPID_VALUE)]
+        HRESULT Item([in] long Index, [out, retval] IInkRecognizer** InkRecognizer);
+    };
+
     [
         progid("msinkaut.InkObject.1"),
         vi_progid("msinkaut.InkObject"),
@@ -1211,4 +1549,35 @@ cpp_quote("#endif /* _WINGDI_ */")
     {
         [default] interface IInkOverlay;
     }
+
+    [
+        progid("msinkaut.InkRecognizerContext.1"),
+        vi_progid("msinkaut.InkRecognizerContext"),
+        threading(both),
+        uuid(aac46a37-9229-4fc0-8cce-4497569bf4d1)
+    ]
+    coclass InkRecognizerContext
+    {
+        [default] interface IInkRecognizerContext;
+        [default, source] dispinterface _IInkRecognitionEvents;
+    }
+
+    [
+        progid("msinkaut.InkRecognizers.1"),
+        vi_progid("msinkaut.InkRecognizers"),
+        uuid(9fd4e808-f6e6-4e65-98d3-aa39054c1255)
+    ]
+    coclass InkRecognizers
+    {
+        [default] interface IInkRecognizers;
+    }
+
+    [
+        uuid(43fb1553-ad74-4ee8-88e4-3e6daac915db)
+    ]
+    coclass InkCollector
+    {
+        [default] interface IInkCollector;
+        [default, source] dispinterface _IInkCollectorEvents;
+    };
 }
diff --git a/include/msvcrt/corecrt_wstdlib.h b/include/msvcrt/corecrt_wstdlib.h
index 594756d17aee..95e0637445ba 100644
--- a/include/msvcrt/corecrt_wstdlib.h
+++ b/include/msvcrt/corecrt_wstdlib.h
@@ -30,6 +30,7 @@ _ACRTIMP void          __cdecl _wmakepath(wchar_t*,const wchar_t*,const wchar_t*
 _ACRTIMP int           __cdecl _wmakepath_s(wchar_t*,size_t,const wchar_t*,const wchar_t*,const wchar_t*,const wchar_t*);
 _ACRTIMP void          __cdecl _wperror(const wchar_t*);
 _ACRTIMP int           __cdecl _wputenv(const wchar_t*);
+_ACRTIMP errno_t       __cdecl _wputenv_s(const wchar_t*,const wchar_t*);
 _ACRTIMP void          __cdecl _wsearchenv(const wchar_t*,const wchar_t*,wchar_t*);
 _ACRTIMP void          __cdecl _wsplitpath(const wchar_t*,wchar_t*,wchar_t*,wchar_t*,wchar_t*);
 _ACRTIMP errno_t       __cdecl _wsplitpath_s(const wchar_t*,wchar_t*,size_t,wchar_t*,size_t,
diff --git a/include/roapi.h b/include/roapi.h
index bcaa8e9fea0d..18baf9e20178 100644
--- a/include/roapi.h
+++ b/include/roapi.h
@@ -46,6 +46,7 @@ HRESULT WINAPI RoActivateInstance(HSTRING classid, IInspectable **instance);
 HRESULT WINAPI RoGetActivationFactory(HSTRING classid, REFIID iid, void **class_factory);
 HRESULT WINAPI RoInitialize(RO_INIT_TYPE type);
 void WINAPI RoUninitialize(void);
+HRESULT WINAPI RoGetApartmentIdentifier(UINT64 *identifier);
 
 #ifdef __cplusplus
 }
diff --git a/include/windows.storage.fileproperties.idl b/include/windows.storage.fileproperties.idl
index 77c17298d974..030dd908d68d 100644
--- a/include/windows.storage.fileproperties.idl
+++ b/include/windows.storage.fileproperties.idl
@@ -30,15 +30,68 @@ import "windows.devices.geolocation.idl";
 import "windows.storage.streams.idl";
 
 namespace Windows.Storage.FileProperties {
+    typedef enum PhotoOrientation PhotoOrientation;
+    typedef enum VideoOrientation VideoOrientation;
+
     interface IBasicProperties;
+    interface IDocumentProperties;
+    interface IImageProperties;
+    interface IMusicProperties;
+    interface IStorageItemExtraProperties;
+    interface IStorageItemContentProperties;
+    interface IVideoProperties;
 
     runtimeclass BasicProperties;
+    runtimeclass DocumentProperties;
+    runtimeclass ImageProperties;
+    runtimeclass MusicProperties;
+    runtimeclass StorageItemContentProperties;
+    runtimeclass VideoProperties;
 
     declare {
+        interface Windows.Foundation.Collections.IMap<HSTRING, IInspectable *>;
+        interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Foundation.Collections.IMap<HSTRING, IInspectable *> *>;
         interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Storage.FileProperties.BasicProperties *>;
+        interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Storage.FileProperties.DocumentProperties *>;
+        interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Storage.FileProperties.ImageProperties *>;
+        interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Storage.FileProperties.MusicProperties *>;
+        interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Storage.FileProperties.VideoProperties *>;
+        interface Windows.Foundation.Collections.IIterable<HSTRING>;
+        interface Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IMap<HSTRING, IInspectable *> *>;
         interface Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.BasicProperties *>;
+        interface Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.DocumentProperties *>;
+        interface Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.ImageProperties *>;
+        interface Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.MusicProperties *>;
+        interface Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.VideoProperties *>;
     }
 
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    enum PhotoOrientation
+    {
+        Unspecified    = 0,
+        Normal         = 1,
+        FlipHorizontal = 2,
+        Rotate180      = 3,
+        FlipVertical   = 4,
+        Transpose      = 5,
+        Rotate270      = 6,
+        Transverse     = 7,
+        Rotate90       = 8
+    };
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    enum VideoOrientation
+    {
+        Normal    = 0,
+        Rotate90  = 90,
+        Rotate180 = 180,
+        Rotate270 = 270
+    };
+
     [
         contract(Windows.Foundation.UniversalApiContract, 1.0),
         exclusiveto(Windows.Storage.FileProperties.BasicProperties),
@@ -51,12 +104,202 @@ namespace Windows.Storage.FileProperties {
         [propget] HRESULT ItemDate([out, retval] Windows.Foundation.DateTime *value);
     }
 
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0),
+        exclusiveto(Windows.Storage.FileProperties.DocumentProperties),
+        uuid(7eab19bc-1821-4923-b4a9-0aea404d0070)
+    ]
+    interface IDocumentProperties : IInspectable
+        requires Windows.Storage.FileProperties.IStorageItemExtraProperties
+    {
+        [propget] HRESULT Author([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Title([out, retval] HSTRING *value);
+        [propput] HRESULT Title([in] HSTRING value);
+        [propget] HRESULT Keywords([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Comment([out, retval] HSTRING *value);
+        [propput] HRESULT Comment([in] HSTRING value);
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0),
+        exclusiveto(Windows.Storage.FileProperties.ImageProperties),
+        uuid(523c9424-fcff-4275-afee-ecdb9ab47973)
+    ]
+    interface IImageProperties : IInspectable
+        requires Windows.Storage.FileProperties.IStorageItemExtraProperties
+    {
+        [propget] HRESULT Rating([out, retval] UINT32 *value);
+        [propput] HRESULT Rating([in] UINT32 value);
+        [propget] HRESULT Keywords([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT DateTaken([out, retval] Windows.Foundation.DateTime *value);
+        [propput] HRESULT DateTaken([in] Windows.Foundation.DateTime value);
+        [propget] HRESULT Width([out, retval] UINT32 *value);
+        [propget] HRESULT Height([out, retval] UINT32 *value);
+        [propget] HRESULT Title([out, retval] HSTRING *value);
+        [propput] HRESULT Title([in] HSTRING value);
+        [propget] HRESULT Latitude([out, retval] Windows.Foundation.IReference<DOUBLE> **value);
+        [propget] HRESULT Longitude([out, retval] Windows.Foundation.IReference<DOUBLE> **value);
+        [propget] HRESULT CameraManufacturer([out, retval] HSTRING *value);
+        [propput] HRESULT CameraManufacturer([in] HSTRING value);
+        [propget] HRESULT CameraModel([out, retval] HSTRING *value);
+        [propput] HRESULT CameraModel([in] HSTRING value);
+        [propget] HRESULT Orientation([out, retval] Windows.Storage.FileProperties.PhotoOrientation *value);
+        [propget] HRESULT PeopleNames([out, retval] Windows.Foundation.Collections.IVectorView<HSTRING> **value);
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0),
+        exclusiveto(Windows.Storage.FileProperties.MusicProperties),
+        uuid(bc8aab62-66ec-419a-bc5d-ca65a4cb46da)
+    ]
+    interface IMusicProperties : IInspectable
+        requires Windows.Storage.FileProperties.IStorageItemExtraProperties
+    {
+        [propget] HRESULT Album([out, retval] HSTRING *value);
+        [propput] HRESULT Album([in] HSTRING value);
+        [propget] HRESULT Artist([out, retval] HSTRING *value);
+        [propput] HRESULT Artist([in] HSTRING value);
+        [propget] HRESULT Genre([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT TrackNumber([out, retval] UINT32 *value);
+        [propput] HRESULT TrackNumber([in] UINT32 value);
+        [propget] HRESULT Title([out, retval] HSTRING *value);
+        [propput] HRESULT Title([in] HSTRING value);
+        [propget] HRESULT Rating([out, retval] UINT32 *value);
+        [propput] HRESULT Rating([in] UINT32 value);
+        [propget] HRESULT Duration([out, retval] Windows.Foundation.TimeSpan *value);
+        [propget] HRESULT Bitrate([out, retval] UINT32 *value);
+        [propget] HRESULT AlbumArtist([out, retval] HSTRING *value);
+        [propput] HRESULT AlbumArtist([in] HSTRING value);
+        [propget] HRESULT Composers([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Conductors([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Subtitle([out, retval] HSTRING *value);
+        [propput] HRESULT Subtitle([in] HSTRING value);
+        [propget] HRESULT Producers([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Publisher([out] [retval] HSTRING *value);
+        [propput] HRESULT Publisher([in] HSTRING value);
+        [propget] HRESULT Writers([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Year([out, retval] UINT32 *value);
+        [propput] HRESULT Year([in] UINT32 value);
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0),
+        uuid(c54361b2-54cd-432b-bdbc-4b19c4b470d7)
+    ]
+    interface IStorageItemExtraProperties : IInspectable
+    {
+        HRESULT RetrievePropertiesAsync(
+            [in] Windows.Foundation.Collections.IIterable<HSTRING> *propertiesToRetrieve,
+            [out, retval] Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IMap<HSTRING, IInspectable *> *> **operation);
+        [overload("SavePropertiesAsync")]
+        HRESULT SavePropertiesAsync(
+            [in] Windows.Foundation.Collections.IIterable<Windows.Foundation.Collections.IKeyValuePair<HSTRING, IInspectable *> *> *propertiesToSave,
+            [out, retval] Windows.Foundation.IAsyncAction **operation);
+        [overload("SavePropertiesAsync")]
+        HRESULT SavePropertiesAsyncOverloadDefault(
+            [out, retval] Windows.Foundation.IAsyncAction **operation);
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0),
+        exclusiveto(Windows.Storage.FileProperties.StorageItemContentProperties),
+        uuid(05294bad-bc38-48bf-85d7-770e0e2ae0ba)
+    ]
+    interface IStorageItemContentProperties : IInspectable
+        requires Windows.Storage.FileProperties.IStorageItemExtraProperties
+    {
+        HRESULT GetMusicPropertiesAsync(
+            [out, retval] Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.MusicProperties *> **operation);
+        HRESULT GetVideoPropertiesAsync(
+            [out, retval] Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.VideoProperties *> **operation);
+        HRESULT GetImagePropertiesAsync(
+            [out, retval] Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.ImageProperties *> **operation);
+        HRESULT GetDocumentPropertiesAsync(
+            [out, retval] Windows.Foundation.IAsyncOperation<Windows.Storage.FileProperties.DocumentProperties *> **operation);
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0),
+        exclusiveto(Windows.Storage.FileProperties.VideoProperties),
+        uuid(719ae507-68de-4db8-97de-49998c059f2f)
+    ]
+    interface IVideoProperties : IInspectable
+        requires Windows.Storage.FileProperties.IStorageItemExtraProperties
+    {
+        [propget] HRESULT Rating([out, retval] UINT32 *value);
+        [propput] HRESULT Rating([in] UINT32 value);
+        [propget] HRESULT Keywords([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Width([out, retval] UINT32 *value);
+        [propget] HRESULT Height([out, retval] UINT32 *value);
+        [propget] HRESULT Duration([out, retval] Windows.Foundation.TimeSpan *value);
+        [propget] HRESULT Latitude([out, retval] Windows.Foundation.IReference<DOUBLE> **value);
+        [propget] HRESULT Longitude([out, retval] Windows.Foundation.IReference<DOUBLE> **value);
+        [propget] HRESULT Title([out, retval] HSTRING *value);
+        [propput] HRESULT Title([in] HSTRING value);
+        [propget] HRESULT Subtitle([out, retval] HSTRING *value);
+        [propput] HRESULT Subtitle([in] HSTRING value);
+        [propget] HRESULT Producers([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Publisher([out, retval] HSTRING *value);
+        [propput] HRESULT Publisher([in] HSTRING value);
+        [propget] HRESULT Writers([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Year([out, retval] UINT32 *value);
+        [propput] HRESULT Year([in] UINT32 value);
+        [propget] HRESULT Bitrate([out, retval] UINT32 *value);
+        [propget] HRESULT Directors([out, retval] Windows.Foundation.Collections.IVector<HSTRING> **value);
+        [propget] HRESULT Orientation([out, retval] Windows.Storage.FileProperties.VideoOrientation *value);
+    }
+
     [
         contract(Windows.Foundation.UniversalApiContract, 1.0)
     ]
     runtimeclass BasicProperties
     {
         [default] interface Windows.Storage.FileProperties.IBasicProperties;
-        /* interface Windows.Storage.FileProperties.IStorageItemExtraProperties; */
+        interface Windows.Storage.FileProperties.IStorageItemExtraProperties;
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    runtimeclass DocumentProperties
+    {
+        [default] interface Windows.Storage.FileProperties.IDocumentProperties;
+        interface Windows.Storage.FileProperties.IStorageItemExtraProperties;
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    runtimeclass ImageProperties
+    {
+        [default] interface Windows.Storage.FileProperties.IImageProperties;
+        interface Windows.Storage.FileProperties.IStorageItemExtraProperties;
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    runtimeclass MusicProperties
+    {
+        [default] interface Windows.Storage.FileProperties.IMusicProperties;
+        interface Windows.Storage.FileProperties.IStorageItemExtraProperties;
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    runtimeclass StorageItemContentProperties
+    {
+        [default] interface Windows.Storage.FileProperties.IStorageItemContentProperties;
+        interface Windows.Storage.FileProperties.IStorageItemExtraProperties;
+    }
+
+    [
+        contract(Windows.Foundation.UniversalApiContract, 1.0)
+    ]
+    runtimeclass VideoProperties
+    {
+        [default] interface Windows.Storage.FileProperties.IVideoProperties;
+        interface Windows.Storage.FileProperties.IStorageItemExtraProperties;
     }
 }
diff --git a/include/wine/opengl_driver.h b/include/wine/opengl_driver.h
index e70c00151a90..af745e038a6b 100644
--- a/include/wine/opengl_driver.h
+++ b/include/wine/opengl_driver.h
@@ -75,6 +75,7 @@ struct opengl_client_context
     UINT64                      unix_funcs;
     DWORD                       current_tid;                            /* thread that the context is current in */
     GLenum                      last_error;
+    GLint                       profile_mask;
     int                         major_version;
     int                         minor_version;
     BOOLEAN                     extensions[GL_EXTENSION_COUNT];         /* exposed client extensions */
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 751bc09da744..7904152d18e1 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1042,17 +1042,30 @@ typedef volatile struct
     data_size_t          name_offset;
     data_size_t          name_len;
     WCHAR                name[MAX_ATOM_LEN];
-    unsigned short       __pad;
+    unsigned short       local;
     struct class_info    info;
     char                 extra[];
 } class_shm_t;
 
+struct window_info
+{
+    lparam_t             id;
+    mod_handle_t         instance;
+    lparam_t             user_data;
+    client_ptr_t         wndproc;
+};
+
 typedef volatile struct
 {
     struct obj_locator   class;
     unsigned int         dpi_context;
     unsigned int         fnid;
+    unsigned int         ansi;
+    int                  __pad;
     data_size_t          private_size;
+    data_size_t          extra_size;
+    struct window_info   info;
+    char                 extra[];
 } window_shm_t;
 
 typedef volatile union
@@ -3493,8 +3506,8 @@ struct create_window_request
     unsigned int   dpi_context;
     unsigned int   style;
     unsigned int   ex_style;
+    unsigned int   ansi;
     /* VARARG(class,unicode_str); */
-    char __pad_52[4];
 };
 struct create_window_reply
 {
@@ -3502,7 +3515,7 @@ struct create_window_reply
     user_handle_t  handle;
     user_handle_t  parent;
     user_handle_t  owner;
-    int            extra;
+    char __pad_20[4];
     client_ptr_t   class_ptr;
 };
 
@@ -3561,7 +3574,7 @@ struct get_window_info_reply
 {
     struct reply_header __header;
     user_handle_t  last_active;
-    int            is_unicode;
+    char __pad_12[4];
     lparam_t       info;
 };
 
@@ -3573,8 +3586,6 @@ struct init_window_info_request
     user_handle_t  handle;
     unsigned int   style;
     unsigned int   ex_style;
-    short int      is_unicode;
-    char __pad_26[6];
 };
 struct init_window_info_reply
 {
@@ -3590,11 +3601,15 @@ struct set_window_info_request
     int            offset;
     data_size_t    size;
     lparam_t       new_info;
+    unsigned int   new_ansi;
+    unsigned int   internal;
 };
 struct set_window_info_reply
 {
     struct reply_header __header;
     lparam_t       old_info;
+    unsigned int   old_ansi;
+    char __pad_20[4];
 };
 
 
@@ -3604,7 +3619,7 @@ struct set_window_fnid_request
     struct request_header __header;
     user_handle_t  handle;
     atom_t         atom;
-    unsigned int   fnid;
+    char __pad_20[4];
 };
 struct set_window_fnid_reply
 {
@@ -4530,9 +4545,9 @@ struct get_hook_info_reply
 struct create_class_request
 {
     struct request_header __header;
-    int            local;
     atom_t         atom;
-    char __pad_20[4];
+    unsigned int   fnid;
+    unsigned int   ansi;
     client_ptr_t   client_ptr;
     data_size_t    name_offset;
     /* VARARG(info,class_info); */
@@ -4574,6 +4589,8 @@ struct set_class_info_request
     int            offset;
     data_size_t    size;
     lparam_t       new_info;
+    unsigned int   ansi;
+    char __pad_36[4];
 };
 struct set_class_info_reply
 {
@@ -7131,6 +7148,6 @@ union generic_reply
     struct d3dkmt_mutex_release_reply d3dkmt_mutex_release_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 942
+#define SERVER_PROTOCOL_VERSION 951
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/wine/vulkan.h b/include/wine/vulkan.h
index 499be2d771f8..5ab758f03388 100644
--- a/include/wine/vulkan.h
+++ b/include/wine/vulkan.h
@@ -4,7 +4,6 @@
  * by the following copyright and permission notice:
  *
  * Copyright 2015-2026 The Khronos Group Inc.
- *
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  * and from Vulkan video.xml file covered
@@ -121,7 +120,7 @@ struct OH_NativeBuffer;
     #endif
 #endif
 #define VK_FALSE 0
-#define VK_HEADER_VERSION 350
+#define VK_HEADER_VERSION 353
 #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 4, VK_HEADER_VERSION)
 #define VK_LOD_CLAMP_NONE 1000.0F
 #define VK_LUID_SIZE 8
@@ -255,6 +254,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 2
 #define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader"
 #define VK_AMD_GCN_SHADER_SPEC_VERSION 1
+#define VK_AMD_GPA_INTERFACE_EXTENSION_NAME "VK_AMD_gpa_interface"
+#define VK_AMD_GPA_INTERFACE_SPEC_VERSION 1
 #define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
 #define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 2
 #define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16"
@@ -459,6 +460,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_EXT_METAL_SURFACE_SPEC_VERSION 1
 #define VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME "VK_EXT_multisampled_render_to_single_sampled"
 #define VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_SPEC_VERSION 1
+#define VK_EXT_MULTISAMPLED_RENDER_TO_SWAPCHAIN_EXTENSION_NAME "VK_EXT_multisampled_render_to_swapchain"
+#define VK_EXT_MULTISAMPLED_RENDER_TO_SWAPCHAIN_SPEC_VERSION 1
 #define VK_EXT_MULTI_DRAW_EXTENSION_NAME "VK_EXT_multi_draw"
 #define VK_EXT_MULTI_DRAW_SPEC_VERSION 1
 #define VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME "VK_EXT_mutable_descriptor_type"
@@ -539,6 +542,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_EXT_SHADER_OBJECT_SPEC_VERSION 1
 #define VK_EXT_SHADER_REPLICATED_COMPOSITES_EXTENSION_NAME "VK_EXT_shader_replicated_composites"
 #define VK_EXT_SHADER_REPLICATED_COMPOSITES_SPEC_VERSION 1
+#define VK_EXT_SHADER_SPLIT_BARRIER_EXTENSION_NAME "VK_EXT_shader_split_barrier"
+#define VK_EXT_SHADER_SPLIT_BARRIER_SPEC_VERSION 1
 #define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export"
 #define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1
 #define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
@@ -665,6 +670,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME "VK_KHR_dynamic_rendering_local_read"
 #define VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_SPEC_VERSION 1
 #define VK_KHR_DYNAMIC_RENDERING_SPEC_VERSION 1
+#define VK_KHR_EXTENDED_FLAGS_EXTENSION_NAME "VK_KHR_extended_flags"
+#define VK_KHR_EXTENDED_FLAGS_SPEC_VERSION 1
 #define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_fence_capabilities"
 #define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_SPEC_VERSION 1
 #define VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME "VK_KHR_external_fence"
@@ -749,6 +756,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_KHR_MAP_MEMORY_2_SPEC_VERSION 1
 #define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview"
 #define VK_KHR_MULTIVIEW_SPEC_VERSION 1
+#define VK_KHR_OPACITY_MICROMAP_EXTENSION_NAME "VK_KHR_opacity_micromap"
+#define VK_KHR_OPACITY_MICROMAP_SPEC_VERSION 1
 #define VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME "VK_KHR_performance_query"
 #define VK_KHR_PERFORMANCE_QUERY_SPEC_VERSION 1
 #define VK_KHR_PIPELINE_BINARY_EXTENSION_NAME "VK_KHR_pipeline_binary"
@@ -867,6 +876,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_KHR_VIDEO_DECODE_VP9_SPEC_VERSION 1
 #define VK_KHR_VIDEO_ENCODE_AV1_EXTENSION_NAME "VK_KHR_video_encode_av1"
 #define VK_KHR_VIDEO_ENCODE_AV1_SPEC_VERSION 1
+#define VK_KHR_VIDEO_ENCODE_FEEDBACK_2_EXTENSION_NAME "VK_KHR_video_encode_feedback2"
+#define VK_KHR_VIDEO_ENCODE_FEEDBACK_2_SPEC_VERSION 1
 #define VK_KHR_VIDEO_ENCODE_H264_EXTENSION_NAME "VK_KHR_video_encode_h264"
 #define VK_KHR_VIDEO_ENCODE_H264_SPEC_VERSION 14
 #define VK_KHR_VIDEO_ENCODE_INTRA_REFRESH_EXTENSION_NAME "VK_KHR_video_encode_intra_refresh"
@@ -921,6 +932,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_NV_COMPUTE_SHADER_DERIVATIVES_SPEC_VERSION 1
 #define VK_NV_COOPERATIVE_MATRIX_2_EXTENSION_NAME "VK_NV_cooperative_matrix2"
 #define VK_NV_COOPERATIVE_MATRIX_2_SPEC_VERSION 1
+#define VK_NV_COOPERATIVE_MATRIX_DECODE_VECTOR_EXTENSION_NAME "VK_NV_cooperative_matrix_decode_vector"
+#define VK_NV_COOPERATIVE_MATRIX_DECODE_VECTOR_SPEC_VERSION 1
 #define VK_NV_COOPERATIVE_MATRIX_EXTENSION_NAME "VK_NV_cooperative_matrix"
 #define VK_NV_COOPERATIVE_MATRIX_SPEC_VERSION 1
 #define VK_NV_COOPERATIVE_VECTOR_EXTENSION_NAME "VK_NV_cooperative_vector"
@@ -1023,6 +1036,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_QCOM_COOPERATIVE_MATRIX_CONVERSION_SPEC_VERSION 1
 #define VK_QCOM_DATA_GRAPH_MODEL_EXTENSION_NAME "VK_QCOM_data_graph_model"
 #define VK_QCOM_DATA_GRAPH_MODEL_SPEC_VERSION 1
+#define VK_QCOM_ELAPSED_TIMER_QUERY_EXTENSION_NAME "VK_QCOM_elapsed_timer_query"
+#define VK_QCOM_ELAPSED_TIMER_QUERY_SPEC_VERSION 1
 #define VK_QCOM_FILTER_CUBIC_CLAMP_EXTENSION_NAME "VK_QCOM_filter_cubic_clamp"
 #define VK_QCOM_FILTER_CUBIC_CLAMP_SPEC_VERSION 1
 #define VK_QCOM_FILTER_CUBIC_WEIGHTS_EXTENSION_NAME "VK_QCOM_filter_cubic_weights"
@@ -1031,6 +1046,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_QCOM_FRAGMENT_DENSITY_MAP_OFFSET_SPEC_VERSION 3
 #define VK_QCOM_IMAGE_PROCESSING_2_EXTENSION_NAME "VK_QCOM_image_processing2"
 #define VK_QCOM_IMAGE_PROCESSING_2_SPEC_VERSION 1
+#define VK_QCOM_IMAGE_PROCESSING_3_EXTENSION_NAME "VK_QCOM_image_processing3"
+#define VK_QCOM_IMAGE_PROCESSING_3_SPEC_VERSION 1
 #define VK_QCOM_IMAGE_PROCESSING_EXTENSION_NAME "VK_QCOM_image_processing"
 #define VK_QCOM_IMAGE_PROCESSING_SPEC_VERSION 1
 #define VK_QCOM_MULTIVIEW_PER_VIEW_RENDER_AREAS_EXTENSION_NAME "VK_QCOM_multiview_per_view_render_areas"
@@ -1047,6 +1064,8 @@ typedef void* VkRemoteAddressNV;
 #define VK_QCOM_RENDER_PASS_TRANSFORM_SPEC_VERSION 5
 #define VK_QCOM_ROTATED_COPY_COMMANDS_EXTENSION_NAME "VK_QCOM_rotated_copy_commands"
 #define VK_QCOM_ROTATED_COPY_COMMANDS_SPEC_VERSION 2
+#define VK_QCOM_SHADER_MULTIPLE_WAIT_QUEUES_EXTENSION_NAME "VK_QCOM_shader_multiple_wait_queues"
+#define VK_QCOM_SHADER_MULTIPLE_WAIT_QUEUES_SPEC_VERSION 1
 #define VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME "VK_QCOM_tile_memory_heap"
 #define VK_QCOM_TILE_MEMORY_HEAP_SPEC_VERSION 1
 #define VK_QCOM_TILE_PROPERTIES_EXTENSION_NAME "VK_QCOM_tile_properties"
@@ -1108,6 +1127,7 @@ VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkGpaSessionAMD)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutEXT)
@@ -1188,6 +1208,7 @@ typedef VkExternalMemoryHandleTypeFlags VkExternalMemoryHandleTypeFlagsKHR;
 typedef VkFlags VkFormatFeatureFlags;
 typedef VkFlags64 VkFormatFeatureFlags2;
 typedef VkFormatFeatureFlags2 VkFormatFeatureFlags2KHR;
+typedef VkFlags VkGpaPerfBlockPropertiesFlagsAMD;
 typedef VkFlags VkIndirectStateFlagsNV;
 typedef VkFlags VkPipelineCacheCreateFlags;
 typedef VkFlags VkPipelineColorBlendStateCreateFlags;
@@ -1258,14 +1279,18 @@ typedef VkExternalSemaphoreHandleTypeFlags VkExternalSemaphoreHandleTypeFlagsKHR
 typedef VkFlags VkFenceCreateFlags;
 typedef VkFlags VkFenceImportFlags;
 typedef VkFenceImportFlags VkFenceImportFlagsKHR;
+typedef VkFlags64 VkFormatFeatureFlags4KHR;
 typedef VkFlags VkFrameBoundaryFlagsEXT;
 typedef VkFlags VkFramebufferCreateFlags;
+typedef VkFlags VkGpaSqShaderStageFlagsAMD;
 typedef VkFlags VkGraphicsPipelineLibraryFlagsEXT;
 typedef VkFlags VkHeadlessSurfaceCreateFlagsEXT;
 typedef VkFlags VkHostImageCopyFlags;
 typedef VkHostImageCopyFlags VkHostImageCopyFlagsEXT;
 typedef VkFlags VkImageCompressionFixedRateFlagsEXT;
 typedef VkFlags VkImageCompressionFlagsEXT;
+typedef VkFlags64 VkImageCreateFlags2KHR;
+typedef VkFlags64 VkImageUsageFlags2KHR;
 typedef VkFlags VkIndirectCommandsInputModeFlagsEXT;
 typedef VkFlags VkIndirectCommandsLayoutUsageFlagsEXT;
 typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNV;
@@ -1288,6 +1313,7 @@ typedef VkFlags VkPartitionedAccelerationStructureInstanceFlagsNV;
 typedef VkFlags VkPastPresentationTimingFlagsEXT;
 typedef VkFlags VkPerformanceCounterDescriptionFlagsARM;
 typedef VkFlags VkPerformanceCounterDescriptionFlagsKHR;
+typedef VkFlags VkPhysicalDeviceGpaPropertiesFlagsAMD;
 typedef VkFlags64 VkPhysicalDeviceSchedulingControlsFlagsARM;
 typedef VkFlags VkPipelineCompilerControlFlagsAMD;
 typedef VkFlags VkPipelineCoverageModulationStateCreateFlagsNV;
@@ -1352,6 +1378,7 @@ typedef VkFlags VkVideoEncodeH264RateControlFlagsKHR;
 typedef VkFlags VkVideoEncodeH264StdFlagsKHR;
 typedef VkFlags VkVideoEncodeH265CtbSizeFlagsKHR;
 typedef VkFlags VkVideoEncodeIntraRefreshModeFlagsKHR;
+typedef VkFlags VkVideoEncodePerPartitionFeedbackFlagsKHR;
 typedef VkFlags VkVideoEncodeRateControlFlagsKHR;
 typedef VkFlags VkVideoEncodeRateControlModeFlagsKHR;
 typedef VkFlags VkVideoEncodeRgbChromaOffsetFlagsVALVE;
@@ -1872,11 +1899,18 @@ typedef enum VkAccelerationStructureMotionInstanceTypeNV
     VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_NV_MAX_ENUM = 0x7fffffff,
 } VkAccelerationStructureMotionInstanceTypeNV;
 
+typedef enum VkAccelerationStructureSerializedBlockTypeKHR
+{
+    VK_ACCELERATION_STRUCTURE_SERIALIZED_BLOCK_TYPE_OPACITY_MICROMAP_KHR = 0,
+    VK_ACCELERATION_STRUCTURE_SERIALIZED_BLOCK_TYPE_KHR_MAX_ENUM = 0x7fffffff,
+} VkAccelerationStructureSerializedBlockTypeKHR;
+
 typedef enum VkAccelerationStructureTypeKHR
 {
     VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR = 0,
     VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR = 1,
     VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR = 2,
+    VK_ACCELERATION_STRUCTURE_TYPE_OPACITY_MICROMAP_KHR = 1000623000,
     VK_ACCELERATION_STRUCTURE_TYPE_KHR_MAX_ENUM = 0x7fffffff,
     VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR,
     VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,
@@ -2287,9 +2321,10 @@ typedef enum VkBuildAccelerationStructureFlagBitsKHR
     VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR = 0x00000008,
     VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR = 0x00000010,
     VK_BUILD_ACCELERATION_STRUCTURE_MOTION_BIT_NV = 0x00000020,
-    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_UPDATE_BIT_EXT = 0x00000040,
-    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DISABLE_OPACITY_MICROMAPS_BIT_EXT = 0x00000080,
+    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_UPDATE_BIT_KHR = 0x00000040,
+    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DISABLE_OPACITY_MICROMAPS_BIT_KHR = 0x00000080,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_DATA_UPDATE_BIT_EXT = 0x00000100,
+    VK_BUILD_ACCELERATION_STRUCTURE_MICROMAP_LOSSY_BIT_KHR = 0x00000400,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DATA_ACCESS_BIT_KHR = 0x00000800,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_CLUSTER_OPACITY_MICROMAPS_BIT_NV = 0x00001000,
     VK_BUILD_ACCELERATION_STRUCTURE_FLAG_BITS_KHR_MAX_ENUM = 0x7fffffff,
@@ -2298,7 +2333,9 @@ typedef enum VkBuildAccelerationStructureFlagBitsKHR
     VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR,
     VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR,
     VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR,
+    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_UPDATE_BIT_EXT = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_UPDATE_BIT_KHR,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_UPDATE_EXT = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_UPDATE_BIT_EXT,
+    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DISABLE_OPACITY_MICROMAPS_BIT_EXT = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DISABLE_OPACITY_MICROMAPS_BIT_KHR,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DISABLE_OPACITY_MICROMAPS_EXT = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DISABLE_OPACITY_MICROMAPS_BIT_EXT,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_DATA_UPDATE_EXT = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_OPACITY_MICROMAP_DATA_UPDATE_BIT_EXT,
     VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DATA_ACCESS_KHR = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DATA_ACCESS_BIT_KHR,
@@ -3052,6 +3089,7 @@ typedef enum VkDriverId
     VK_DRIVER_ID_MESA_HONEYKRISP = 26,
     VK_DRIVER_ID_VULKAN_SC_EMULATION_ON_VULKAN = 27,
     VK_DRIVER_ID_MESA_KOSMICKRISP = 28,
+    VK_DRIVER_ID_MESA_GFXSTREAM = 29,
     VK_DRIVER_ID_MAX_ENUM = 0x7fffffff,
     VK_DRIVER_ID_AMD_PROPRIETARY_KHR = VK_DRIVER_ID_AMD_PROPRIETARY,
     VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR = VK_DRIVER_ID_AMD_OPEN_SOURCE,
@@ -3762,6 +3800,7 @@ static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_OPTICAL_FLOW_IMAGE_BIT
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_OPTICAL_FLOW_VECTOR_BIT_NV = 0x20000000000ull;
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_OPTICAL_FLOW_COST_BIT_NV = 0x40000000000ull;
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TENSOR_IMAGE_ALIASING_BIT_ARM = 0x80000000000ull;
+static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLOCK_MATCHING_SXD_BIT_QCOM = 0x100000000000ull;
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT = 0x400000000000ull;
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT = 0x400000000000ull;
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TENSOR_DATA_GRAPH_BIT_ARM = 0x1000000000000ull;
@@ -3778,6 +3817,9 @@ static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DATA_GRAPH_OPTICAL_FLO
 static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COPY_IMAGE_INDIRECT_DST_BIT_KHR = 0x800000000000000ull;
 typedef VkFormatFeatureFlagBits2 VkFormatFeatureFlagBits2KHR;
 
+typedef VkFlags64 VkFormatFeatureFlagBits4KHR;
+
+
 typedef enum VkFragmentShadingRateCombinerOpKHR
 {
     VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR = 0,
@@ -3848,15 +3890,17 @@ typedef enum VkGeometryInstanceFlagBitsKHR
     VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR = 0x00000002,
     VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR = 0x00000004,
     VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR = 0x00000008,
-    VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_BIT_EXT = 0x00000010,
-    VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_BIT_EXT = 0x00000020,
+    VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_BIT_KHR = 0x00000010,
+    VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_BIT_KHR = 0x00000020,
     VK_GEOMETRY_INSTANCE_FLAG_BITS_KHR_MAX_ENUM = 0x7fffffff,
     VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR = VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR,
     VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR,
     VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_NV = VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR,
     VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_NV = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR,
     VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_NV = VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR,
+    VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_BIT_EXT = VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_BIT_KHR,
     VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_EXT = VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_BIT_EXT,
+    VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_BIT_EXT = VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_BIT_KHR,
     VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_EXT = VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_BIT_EXT,
 } VkGeometryInstanceFlagBitsKHR;
 typedef VkGeometryInstanceFlagBitsKHR VkGeometryInstanceFlagBitsNV;
@@ -3868,12 +3912,108 @@ typedef enum VkGeometryTypeKHR
     VK_GEOMETRY_TYPE_INSTANCES_KHR = 2,
     VK_GEOMETRY_TYPE_SPHERES_NV = 1000429004,
     VK_GEOMETRY_TYPE_LINEAR_SWEPT_SPHERES_NV = 1000429005,
+    VK_GEOMETRY_TYPE_MICROMAP_KHR = 1000623000,
     VK_GEOMETRY_TYPE_KHR_MAX_ENUM = 0x7fffffff,
     VK_GEOMETRY_TYPE_TRIANGLES_NV = VK_GEOMETRY_TYPE_TRIANGLES_KHR,
     VK_GEOMETRY_TYPE_AABBS_NV = VK_GEOMETRY_TYPE_AABBS_KHR,
 } VkGeometryTypeKHR;
 typedef VkGeometryTypeKHR VkGeometryTypeNV;
 
+typedef enum VkGpaDeviceClockModeAMD
+{
+    VK_GPA_DEVICE_CLOCK_MODE_DEFAULT_AMD = 0,
+    VK_GPA_DEVICE_CLOCK_MODE_QUERY_AMD = 1,
+    VK_GPA_DEVICE_CLOCK_MODE_PROFILING_AMD = 2,
+    VK_GPA_DEVICE_CLOCK_MODE_MIN_MEMORY_AMD = 3,
+    VK_GPA_DEVICE_CLOCK_MODE_MIN_ENGINE_AMD = 4,
+    VK_GPA_DEVICE_CLOCK_MODE_PEAK_AMD = 5,
+    VK_GPA_DEVICE_CLOCK_MODE_AMD_MAX_ENUM = 0x7fffffff,
+} VkGpaDeviceClockModeAMD;
+
+typedef enum VkGpaPerfBlockAMD
+{
+    VK_GPA_PERF_BLOCK_CPF_AMD = 0,
+    VK_GPA_PERF_BLOCK_IA_AMD = 1,
+    VK_GPA_PERF_BLOCK_VGT_AMD = 2,
+    VK_GPA_PERF_BLOCK_PA_AMD = 3,
+    VK_GPA_PERF_BLOCK_SC_AMD = 4,
+    VK_GPA_PERF_BLOCK_SPI_AMD = 5,
+    VK_GPA_PERF_BLOCK_SQ_AMD = 6,
+    VK_GPA_PERF_BLOCK_SX_AMD = 7,
+    VK_GPA_PERF_BLOCK_TA_AMD = 8,
+    VK_GPA_PERF_BLOCK_TD_AMD = 9,
+    VK_GPA_PERF_BLOCK_TCP_AMD = 10,
+    VK_GPA_PERF_BLOCK_TCC_AMD = 11,
+    VK_GPA_PERF_BLOCK_TCA_AMD = 12,
+    VK_GPA_PERF_BLOCK_DB_AMD = 13,
+    VK_GPA_PERF_BLOCK_CB_AMD = 14,
+    VK_GPA_PERF_BLOCK_GDS_AMD = 15,
+    VK_GPA_PERF_BLOCK_SRBM_AMD = 16,
+    VK_GPA_PERF_BLOCK_GRBM_AMD = 17,
+    VK_GPA_PERF_BLOCK_GRBM_SE_AMD = 18,
+    VK_GPA_PERF_BLOCK_RLC_AMD = 19,
+    VK_GPA_PERF_BLOCK_DMA_AMD = 20,
+    VK_GPA_PERF_BLOCK_MC_AMD = 21,
+    VK_GPA_PERF_BLOCK_CPG_AMD = 22,
+    VK_GPA_PERF_BLOCK_CPC_AMD = 23,
+    VK_GPA_PERF_BLOCK_WD_AMD = 24,
+    VK_GPA_PERF_BLOCK_TCS_AMD = 25,
+    VK_GPA_PERF_BLOCK_ATC_AMD = 26,
+    VK_GPA_PERF_BLOCK_ATC_L2_AMD = 27,
+    VK_GPA_PERF_BLOCK_MC_VM_L2_AMD = 28,
+    VK_GPA_PERF_BLOCK_EA_AMD = 29,
+    VK_GPA_PERF_BLOCK_RPB_AMD = 30,
+    VK_GPA_PERF_BLOCK_RMI_AMD = 31,
+    VK_GPA_PERF_BLOCK_UMCCH_AMD = 32,
+    VK_GPA_PERF_BLOCK_GE_AMD = 33,
+    VK_GPA_PERF_BLOCK_GL1A_AMD = 34,
+    VK_GPA_PERF_BLOCK_GL1C_AMD = 35,
+    VK_GPA_PERF_BLOCK_GL1CG_AMD = 36,
+    VK_GPA_PERF_BLOCK_GL2A_AMD = 37,
+    VK_GPA_PERF_BLOCK_GL2C_AMD = 38,
+    VK_GPA_PERF_BLOCK_CHA_AMD = 39,
+    VK_GPA_PERF_BLOCK_CHC_AMD = 40,
+    VK_GPA_PERF_BLOCK_CHCG_AMD = 41,
+    VK_GPA_PERF_BLOCK_GUS_AMD = 42,
+    VK_GPA_PERF_BLOCK_GCR_AMD = 43,
+    VK_GPA_PERF_BLOCK_PH_AMD = 44,
+    VK_GPA_PERF_BLOCK_UTCL1_AMD = 45,
+    VK_GPA_PERF_BLOCK_GE_DIST_AMD = 46,
+    VK_GPA_PERF_BLOCK_GE_SE_AMD = 47,
+    VK_GPA_PERF_BLOCK_DF_MALL_AMD = 48,
+    VK_GPA_PERF_BLOCK_SQ_WGP_AMD = 49,
+    VK_GPA_PERF_BLOCK_PC_AMD = 50,
+    VK_GPA_PERF_BLOCK_GL1XA_AMD = 51,
+    VK_GPA_PERF_BLOCK_GL1XC_AMD = 52,
+    VK_GPA_PERF_BLOCK_WGS_AMD = 53,
+    VK_GPA_PERF_BLOCK_EACPWD_AMD = 54,
+    VK_GPA_PERF_BLOCK_EASE_AMD = 55,
+    VK_GPA_PERF_BLOCK_RLCUSER_AMD = 56,
+    VK_GPA_PERF_BLOCK_AMD_MAX_ENUM = 0x7fffffff,
+    VK_GPA_PERF_BLOCK_GE1_AMD = VK_GPA_PERF_BLOCK_GE_AMD,
+    VK_GPA_PERF_BLOCK_RLCLOCAL_AMD = VK_GPA_PERF_BLOCK_RLCUSER_AMD,
+} VkGpaPerfBlockAMD;
+
+typedef enum VkGpaSampleTypeAMD
+{
+    VK_GPA_SAMPLE_TYPE_CUMULATIVE_AMD = 0,
+    VK_GPA_SAMPLE_TYPE_TRACE_AMD = 1,
+    VK_GPA_SAMPLE_TYPE_TIMING_AMD = 2,
+    VK_GPA_SAMPLE_TYPE_AMD_MAX_ENUM = 0x7fffffff,
+} VkGpaSampleTypeAMD;
+
+typedef enum VkGpaSqShaderStageFlagBitsAMD
+{
+    VK_GPA_SQ_SHADER_STAGE_PS_BIT_AMD = 0x00000001,
+    VK_GPA_SQ_SHADER_STAGE_VS_BIT_AMD = 0x00000002,
+    VK_GPA_SQ_SHADER_STAGE_GS_BIT_AMD = 0x00000004,
+    VK_GPA_SQ_SHADER_STAGE_ES_BIT_AMD = 0x00000008,
+    VK_GPA_SQ_SHADER_STAGE_HS_BIT_AMD = 0x00000010,
+    VK_GPA_SQ_SHADER_STAGE_LS_BIT_AMD = 0x00000020,
+    VK_GPA_SQ_SHADER_STAGE_CS_BIT_AMD = 0x00000040,
+    VK_GPA_SQ_SHADER_STAGE_FLAG_BITS_AMD_MAX_ENUM = 0x7fffffff,
+} VkGpaSqShaderStageFlagBitsAMD;
+
 typedef enum VkGraphicsPipelineLibraryFlagBitsEXT
 {
     VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT = 0x00000001,
@@ -3987,6 +4127,30 @@ typedef enum VkImageCreateFlagBits
     VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM = VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_EXT,
 } VkImageCreateFlagBits;
 
+typedef VkFlags64 VkImageCreateFlagBits2KHR;
+
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_SPARSE_BINDING_BIT_KHR = 0x00000001ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_SPARSE_RESIDENCY_BIT_KHR = 0x00000002ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_SPARSE_ALIASED_BIT_KHR = 0x00000004ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_MUTABLE_FORMAT_BIT_KHR = 0x00000008ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_CUBE_COMPATIBLE_BIT_KHR = 0x00000010ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 0x00000040ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_EXTENDED_USAGE_BIT_KHR = 0x00000100ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_DISJOINT_BIT_KHR = 0x00000200ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_ALIAS_BIT_KHR = 0x00000400ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_PROTECTED_BIT_KHR = 0x00000800ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_CORNER_SAMPLED_BIT_NV = 0x00002000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_SUBSAMPLED_BIT_EXT = 0x00004000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_FRAGMENT_DENSITY_MAP_OFFSET_BIT_EXT = 0x00008000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT = 0x00010000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_2D_VIEW_COMPATIBLE_BIT_EXT = 0x00020000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT = 0x00040000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_VIDEO_PROFILE_INDEPENDENT_BIT_KHR = 0x00100000ull;
+static const VkImageCreateFlagBits2KHR VK_IMAGE_CREATE_2_ALIAS_SINGLE_LAYER_DESCRIPTOR_BIT_KHR = 0x00400000ull;
+
 typedef enum VkImageLayout
 {
     VK_IMAGE_LAYOUT_UNDEFINED = 0,
@@ -4081,6 +4245,34 @@ typedef enum VkImageUsageFlagBits
     VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT = VK_IMAGE_USAGE_HOST_TRANSFER_BIT,
 } VkImageUsageFlagBits;
 
+typedef VkFlags64 VkImageUsageFlagBits2KHR;
+
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_TRANSFER_SRC_BIT_KHR = 0x00000001ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_TRANSFER_DST_BIT_KHR = 0x00000002ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_SAMPLED_BIT_KHR = 0x00000004ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_STORAGE_BIT_KHR = 0x00000008ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_COLOR_ATTACHMENT_BIT_KHR = 0x00000010ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR = 0x00000020ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_TRANSIENT_ATTACHMENT_BIT_KHR = 0x00000040ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_INPUT_ATTACHMENT_BIT_KHR = 0x00000080ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00000100ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x00000200ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_DECODE_DST_BIT_KHR = 0x00000400ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_DECODE_SRC_BIT_KHR = 0x00000800ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_DECODE_DPB_BIT_KHR = 0x00001000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_ENCODE_DST_BIT_KHR = 0x00002000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_ENCODE_SRC_BIT_KHR = 0x00004000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_ENCODE_DPB_BIT_KHR = 0x00008000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_INVOCATION_MASK_BIT_HUAWEI = 0x00040000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 0x00080000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_SAMPLE_WEIGHT_BIT_QCOM = 0x00100000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_SAMPLE_BLOCK_MATCH_BIT_QCOM = 0x00200000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_HOST_TRANSFER_BIT_KHR = 0x00400000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_TENSOR_ALIASING_BIT_ARM = 0x00800000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR = 0x02000000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR = 0x04000000ull;
+static const VkImageUsageFlagBits2KHR VK_IMAGE_USAGE_2_TILE_MEMORY_BIT_QCOM = 0x08000000ull;
+
 typedef enum VkImageViewCreateFlagBits
 {
     VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT = 0x00000001,
@@ -4394,6 +4586,7 @@ typedef enum VkObjectType
     VK_OBJECT_TYPE_CU_FUNCTION_NVX = 1000029001,
     VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000,
     VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000,
+    VK_OBJECT_TYPE_GPA_SESSION_AMD = 1000133000,
     VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000,
     VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000,
     VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
@@ -4418,22 +4611,30 @@ typedef enum VkObjectType
     VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT = VK_OBJECT_TYPE_PRIVATE_DATA_SLOT,
 } VkObjectType;
 
-typedef enum VkOpacityMicromapFormatEXT
+typedef enum VkOpacityMicromapFormatKHR
 {
-    VK_OPACITY_MICROMAP_FORMAT_2_STATE_EXT = 1,
-    VK_OPACITY_MICROMAP_FORMAT_4_STATE_EXT = 2,
-    VK_OPACITY_MICROMAP_FORMAT_EXT_MAX_ENUM = 0x7fffffff,
-} VkOpacityMicromapFormatEXT;
+    VK_OPACITY_MICROMAP_FORMAT_2_STATE_KHR = 1,
+    VK_OPACITY_MICROMAP_FORMAT_4_STATE_KHR = 2,
+    VK_OPACITY_MICROMAP_FORMAT_KHR_MAX_ENUM = 0x7fffffff,
+    VK_OPACITY_MICROMAP_FORMAT_2_STATE_EXT = VK_OPACITY_MICROMAP_FORMAT_2_STATE_KHR,
+    VK_OPACITY_MICROMAP_FORMAT_4_STATE_EXT = VK_OPACITY_MICROMAP_FORMAT_4_STATE_KHR,
+} VkOpacityMicromapFormatKHR;
+typedef VkOpacityMicromapFormatKHR VkOpacityMicromapFormatEXT;
 
-typedef enum VkOpacityMicromapSpecialIndexEXT
+typedef enum VkOpacityMicromapSpecialIndexKHR
 {
     VK_OPACITY_MICROMAP_SPECIAL_INDEX_CLUSTER_GEOMETRY_DISABLE_OPACITY_MICROMAP_NV = -5,
-    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_OPAQUE_EXT = -4,
-    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_TRANSPARENT_EXT = -3,
-    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_OPAQUE_EXT = -2,
-    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_TRANSPARENT_EXT = -1,
-    VK_OPACITY_MICROMAP_SPECIAL_INDEX_EXT_MAX_ENUM = 0x7fffffff,
-} VkOpacityMicromapSpecialIndexEXT;
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_OPAQUE_KHR = -4,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_TRANSPARENT_KHR = -3,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_OPAQUE_KHR = -2,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_TRANSPARENT_KHR = -1,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_KHR_MAX_ENUM = 0x7fffffff,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_TRANSPARENT_EXT = VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_TRANSPARENT_KHR,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_OPAQUE_EXT = VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_OPAQUE_KHR,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_TRANSPARENT_EXT = VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_TRANSPARENT_KHR,
+    VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_OPAQUE_EXT = VK_OPACITY_MICROMAP_SPECIAL_INDEX_FULLY_UNKNOWN_OPAQUE_KHR,
+} VkOpacityMicromapSpecialIndexKHR;
+typedef VkOpacityMicromapSpecialIndexKHR VkOpacityMicromapSpecialIndexEXT;
 
 typedef enum VkOpticalFlowExecuteFlagBitsNV
 {
@@ -4733,7 +4934,7 @@ typedef enum VkPipelineCreateFlagBits
     VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00200000,
     VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 0x00400000,
     VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT = 0x00800000,
-    VK_PIPELINE_CREATE_RAY_TRACING_OPACITY_MICROMAP_BIT_EXT = 0x01000000,
+    VK_PIPELINE_CREATE_RAY_TRACING_OPACITY_MICROMAP_BIT_KHR = 0x01000000,
     VK_PIPELINE_CREATE_COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 0x02000000,
     VK_PIPELINE_CREATE_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 0x04000000,
     VK_PIPELINE_CREATE_NO_PROTECTED_ACCESS_BIT = 0x08000000,
@@ -4748,6 +4949,7 @@ typedef enum VkPipelineCreateFlagBits
     VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR,
     VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT = VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT,
     VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT = VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT,
+    VK_PIPELINE_CREATE_RAY_TRACING_OPACITY_MICROMAP_BIT_EXT = VK_PIPELINE_CREATE_RAY_TRACING_OPACITY_MICROMAP_BIT_KHR,
     VK_PIPELINE_CREATE_NO_PROTECTED_ACCESS_BIT_EXT = VK_PIPELINE_CREATE_NO_PROTECTED_ACCESS_BIT,
     VK_PIPELINE_CREATE_PROTECTED_ACCESS_ONLY_BIT_EXT = VK_PIPELINE_CREATE_PROTECTED_ACCESS_ONLY_BIT,
 } VkPipelineCreateFlagBits;
@@ -4787,6 +4989,7 @@ static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_RENDERING_FRAGMENT_S
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 0x00400000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT = 0x00800000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_RAY_TRACING_OPACITY_MICROMAP_BIT_EXT = 0x01000000ull;
+static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_RAY_TRACING_OPACITY_MICROMAP_BIT_KHR = 0x01000000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 0x02000000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 0x04000000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_NO_PROTECTED_ACCESS_BIT = 0x08000000ull;
@@ -4803,6 +5006,7 @@ static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_DISALLOW_OPACITY_MIC
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT = 0x4000000000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_INSTRUMENT_SHADERS_BIT_ARM = 0x8000000000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_PER_LAYER_FRAGMENT_DENSITY_BIT_VALVE = 0x10000000000ull;
+static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_OPACITY_MICROMAP_DISALLOW_MIXED_SPECIAL_INDEX_BIT_KHR = 0x20000000000ull;
 static const VkPipelineCreateFlagBits2 VK_PIPELINE_CREATE_2_64_BIT_INDEXING_BIT_EXT = 0x80000000000ull;
 typedef VkPipelineCreateFlagBits2 VkPipelineCreateFlagBits2KHR;
 
@@ -5163,6 +5367,7 @@ typedef enum VkQueryType
     VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR = 1000150000,
     VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR = 1000150001,
     VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV = 1000165000,
+    VK_QUERY_TYPE_TIME_ELAPSED_QCOM = 1000173000,
     VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL = 1000210000,
     VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR = 1000299000,
     VK_QUERY_TYPE_MESH_PRIMITIVES_GENERATED_EXT = 1000328000,
@@ -5514,6 +5719,7 @@ typedef enum VkShaderCreateFlagBitsEXT
     VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT = 0x00000080,
     VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT = 0x00000400,
     VK_SHADER_CREATE_INSTRUMENT_SHADER_BIT_ARM = 0x00000800,
+    VK_SHADER_CREATE_OPACITY_MICROMAP_DISALLOW_MIXED_SPECIAL_INDEX_BIT_EXT = 0x00001000,
     VK_SHADER_CREATE_64_BIT_INDEXING_BIT_EXT = 0x00008000,
     VK_SHADER_CREATE_INDEPENDENT_SETS_BIT_KHR = 0x00040000,
     VK_SHADER_CREATE_FLAG_BITS_EXT_MAX_ENUM = 0x7fffffff,
@@ -5908,6 +6114,13 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES = 1000130000,
     VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO = 1000130001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_FEATURES_AMD = 1000133000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_AMD = 1000133001,
+    VK_STRUCTURE_TYPE_GPA_SAMPLE_BEGIN_INFO_AMD = 1000133002,
+    VK_STRUCTURE_TYPE_GPA_SESSION_CREATE_INFO_AMD = 1000133003,
+    VK_STRUCTURE_TYPE_GPA_DEVICE_CLOCK_MODE_INFO_AMD = 1000133004,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GPA_PROPERTIES_2_AMD = 1000133005,
+    VK_STRUCTURE_TYPE_GPA_DEVICE_GET_CLOCK_INFO_AMD = 1000133006,
     VK_STRUCTURE_TYPE_TEXEL_BUFFER_DESCRIPTOR_INFO_EXT = 1000135000,
     VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT = 1000135001,
     VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT = 1000135002,
@@ -6011,6 +6224,7 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT = 1000170000,
     VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT = 1000170001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_CONVERSION_FEATURES_QCOM = 1000172000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ELAPSED_TIMER_QUERY_FEATURES_QCOM = 1000173000,
     VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO = 1000174000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES = 1000175000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES = 1000177000,
@@ -6211,6 +6425,11 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_PERF_HINT_INFO_QCOM = 1000302000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_QUEUE_PERF_HINT_FEATURES_QCOM = 1000302001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_QUEUE_PERF_HINT_PROPERTIES_QCOM = 1000302002,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_3_FEATURES_QCOM = 1000303000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_FEATURES_QCOM = 1000304000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MULTIPLE_WAIT_QUEUES_PROPERTIES_QCOM = 1000304001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_FEATURES_EXT = 1000305000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SPLIT_BARRIER_PROPERTIES_EXT = 1000305001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_SHADING_FEATURES_QCOM = 1000309000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_SHADING_PROPERTIES_QCOM = 1000309001,
     VK_STRUCTURE_TYPE_RENDER_PASS_TILE_SHADING_CREATE_INFO_QCOM = 1000309002,
@@ -6673,6 +6892,9 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_COOPERATIVE_MATRIX_FLEXIBLE_DIMENSIONS_PROPERTIES_NV = 1000593001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_2_PROPERTIES_NV = 1000593002,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_OPACITY_MICROMAP_FEATURES_ARM = 1000596000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_ENCODE_FEEDBACK_2_FEATURES_KHR = 1000598000,
+    VK_STRUCTURE_TYPE_VIDEO_ENCODE_FEEDBACK_2_CAPABILITIES_KHR = 1000598001,
+    VK_STRUCTURE_TYPE_QUERY_POOL_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_CREATE_INFO_KHR = 1000598002,
     VK_STRUCTURE_TYPE_IMPORT_MEMORY_METAL_HANDLE_INFO_EXT = 1000602000,
     VK_STRUCTURE_TYPE_MEMORY_METAL_HANDLE_PROPERTIES_EXT = 1000602001,
     VK_STRUCTURE_TYPE_MEMORY_GET_METAL_HANDLE_INFO_EXT = 1000602002,
@@ -6692,8 +6914,14 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_DENSITY_MAP_LAYERED_CREATE_INFO_VALVE = 1000611002,
     VK_STRUCTURE_TYPE_SET_PRESENT_CONFIG_NV = 1000613000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_METERING_FEATURES_NV = 1000613001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SWAPCHAIN_FEATURES_EXT = 1000616000,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_FLAGS_SURFACE_CAPABILITIES_EXT = 1000616001,
     VK_STRUCTURE_TYPE_RENDERING_END_INFO_KHR = 1000619003,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_DEVICE_MEMORY_FEATURES_EXT = 1000620000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_KHR = 1000623000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_KHR = 1000623001,
+    VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_MICROMAP_DATA_KHR = 1000623002,
+    VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_TRIANGLES_OPACITY_MICROMAP_KHR = 1000623003,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_64_BIT_INDEXING_FEATURES_EXT = 1000627000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_RESOLVE_FEATURES_EXT = 1000628000,
     VK_STRUCTURE_TYPE_BEGIN_CUSTOM_RESOLVE_INFO_EXT = 1000628001,
@@ -6722,6 +6950,13 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_11_FEATURES_KHR = 1000657000,
     VK_STRUCTURE_TYPE_QUEUE_FAMILY_OPTIMAL_IMAGE_TRANSFER_GRANULARITY_PROPERTIES_KHR = 1000657001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_PARTITIONED_FEATURES_EXT = 1000662000,
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_4_KHR = 1000668000,
+    VK_STRUCTURE_TYPE_IMAGE_CREATE_FLAGS_2_CREATE_INFO_KHR = 1000668001,
+    VK_STRUCTURE_TYPE_IMAGE_USAGE_FLAGS_2_CREATE_INFO_KHR = 1000668002,
+    VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_2_CREATE_INFO_KHR = 1000668003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_FLAGS_FEATURES_KHR = 1000668004,
+    VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_2_CREATE_INFO_KHR = 1000668005,
+    VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_2_KHR = 1000668006,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MIXED_FLOAT_DOT_PRODUCT_FEATURES_VALVE = 1000673000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_THROTTLE_HINT_FEATURES_SEC = 1000674000,
     VK_STRUCTURE_TYPE_THROTTLE_HINT_SUBMIT_INFO_SEC = 1000674001,
@@ -6729,6 +6964,7 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_DATA_GRAPH_PIPELINE_SESSION_NEURAL_STATISTICS_CREATE_INFO_ARM = 1000676001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DATA_GRAPH_NEURAL_ACCELERATOR_STATISTICS_FEATURES_ARM = 1000676002,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_RESTART_INDEX_FEATURES_EXT = 1000678000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_DECODE_VECTOR_FEATURES_NV = 1000689000,
     VK_STRUCTURE_TYPE_MAX_ENUM = 0x7fffffff,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES,
@@ -7077,6 +7313,7 @@ typedef enum VkSwapchainCreateFlagBitsKHR
     VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_KHR = 0x00000008,
     VK_SWAPCHAIN_CREATE_PRESENT_ID_2_BIT_KHR = 0x00000040,
     VK_SWAPCHAIN_CREATE_PRESENT_WAIT_2_BIT_KHR = 0x00000080,
+    VK_SWAPCHAIN_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT = 0x00000100,
     VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT = 0x00000200,
     VK_SWAPCHAIN_CREATE_FLAG_BITS_KHR_MAX_ENUM = 0x7fffffff,
     VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT = VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_KHR,
@@ -7378,6 +7615,13 @@ typedef enum VkVideoEncodeFeedbackFlagBitsKHR
     VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR = 0x00000001,
     VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR = 0x00000002,
     VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_HAS_OVERRIDES_BIT_KHR = 0x00000004,
+    VK_VIDEO_ENCODE_FEEDBACK_AVERAGE_QUANTIZATION_BIT_KHR = 0x00000008,
+    VK_VIDEO_ENCODE_FEEDBACK_MIN_QUANTIZATION_BIT_KHR = 0x00000010,
+    VK_VIDEO_ENCODE_FEEDBACK_MAX_QUANTIZATION_BIT_KHR = 0x00000020,
+    VK_VIDEO_ENCODE_FEEDBACK_INTRA_PIXELS_BIT_KHR = 0x00000040,
+    VK_VIDEO_ENCODE_FEEDBACK_INTER_PIXELS_BIT_KHR = 0x00000080,
+    VK_VIDEO_ENCODE_FEEDBACK_SKIPPED_PIXELS_BIT_KHR = 0x00000100,
+    VK_VIDEO_ENCODE_FEEDBACK_PICTURE_PARTITION_COUNT_BIT_KHR = 0x00000200,
     VK_VIDEO_ENCODE_FEEDBACK_FLAG_BITS_KHR_MAX_ENUM = 0x7fffffff,
 } VkVideoEncodeFeedbackFlagBitsKHR;
 
@@ -7458,6 +7702,14 @@ typedef enum VkVideoEncodeIntraRefreshModeFlagBitsKHR
     VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_FLAG_BITS_KHR_MAX_ENUM = 0x7fffffff,
 } VkVideoEncodeIntraRefreshModeFlagBitsKHR;
 
+typedef enum VkVideoEncodePerPartitionFeedbackFlagBitsKHR
+{
+    VK_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_STATUS_BIT_KHR = 0x00000001,
+    VK_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR = 0x00000002,
+    VK_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR = 0x00000004,
+    VK_VIDEO_ENCODE_PER_PARTITION_FEEDBACK_FLAG_BITS_KHR_MAX_ENUM = 0x7fffffff,
+} VkVideoEncodePerPartitionFeedbackFlagBitsKHR;
+
 typedef enum VkVideoEncodeRateControlModeFlagBitsKHR
 {
     VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR = 0,
@@ -10154,6 +10406,24 @@ typedef struct VkFramebufferAttachmentImageInfo
 } VkFramebufferAttachmentImageInfo;
 typedef VkFramebufferAttachmentImageInfo VkFramebufferAttachmentImageInfoKHR;
 
+typedef struct VkGpaPerfBlockPropertiesAMD
+{
+    VkGpaPerfBlockAMD blockType;
+    VkGpaPerfBlockPropertiesFlagsAMD flags;
+    uint32_t instanceCount;
+    uint32_t maxEventID;
+    uint32_t maxGlobalOnlyCounters;
+    uint32_t maxGlobalSharedCounters;
+    uint32_t maxStreamingCounters;
+} VkGpaPerfBlockPropertiesAMD;
+
+typedef struct VkGpaPerfCounterAMD
+{
+    VkGpaPerfBlockAMD blockType;
+    uint32_t blockInstance;
+    uint32_t eventID;
+} VkGpaPerfCounterAMD;
+
 typedef struct VkGraphicsShaderGroupCreateInfoNV
 {
     VkStructureType sType;
@@ -10381,6 +10651,13 @@ typedef struct VkMicromapUsageEXT
     uint32_t format;
 } VkMicromapUsageEXT;
 
+typedef struct VkMicromapUsageKHR
+{
+    uint32_t count;
+    uint32_t subdivisionLevel;
+    VkOpacityMicromapFormatKHR format;
+} VkMicromapUsageKHR;
+
 typedef struct VkMutableDescriptorTypeListEXT
 {
     uint32_t descriptorTypeCount;
@@ -11191,6 +11468,18 @@ typedef struct VkAccelerationStructureGeometryLinearSweptSpheresDataNV
     VkRayTracingLssPrimitiveEndCapsModeNV endCapsMode;
 } VkAccelerationStructureGeometryLinearSweptSpheresDataNV;
 
+typedef struct VkAccelerationStructureGeometryMicromapDataKHR
+{
+    VkStructureType sType;
+    const void *pNext;
+    uint32_t usageCountsCount;
+    const VkMicromapUsageKHR *pUsageCounts;
+    const VkMicromapUsageKHR * const*ppUsageCounts;
+    VkDeviceAddress WINE_VK_ALIGN(8) data;
+    VkDeviceAddress WINE_VK_ALIGN(8) triangleArray;
+    VkDeviceSize WINE_VK_ALIGN(8) triangleArrayStride;
+} VkAccelerationStructureGeometryMicromapDataKHR;
+
 typedef struct VkAccelerationStructureGeometryMotionTrianglesDataNV
 {
     VkStructureType sType;
@@ -11250,6 +11539,17 @@ typedef struct VkAccelerationStructureTrianglesOpacityMicromapEXT
     VkMicromapEXT WINE_VK_ALIGN(8) micromap;
 } VkAccelerationStructureTrianglesOpacityMicromapEXT;
 
+typedef struct VkAccelerationStructureTrianglesOpacityMicromapKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkIndexType indexType;
+    VkDeviceAddress WINE_VK_ALIGN(8) indexBuffer;
+    VkDeviceSize WINE_VK_ALIGN(8) indexStride;
+    uint32_t baseTriangle;
+    VkAccelerationStructureKHR WINE_VK_ALIGN(8) micromap;
+} VkAccelerationStructureTrianglesOpacityMicromapKHR;
+
 typedef struct VkAccelerationStructureVersionInfoKHR
 {
     VkStructureType sType;
@@ -13286,6 +13586,15 @@ typedef struct VkFormatProperties3
 } VkFormatProperties3;
 typedef VkFormatProperties3 VkFormatProperties3KHR;
 
+typedef struct VkFormatProperties4KHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkFormatFeatureFlags4KHR WINE_VK_ALIGN(8) linearTilingFeatures;
+    VkFormatFeatureFlags4KHR WINE_VK_ALIGN(8) optimalTilingFeatures;
+    VkFormatFeatureFlags4KHR WINE_VK_ALIGN(8) bufferFeatures;
+} VkFormatProperties4KHR;
+
 typedef struct VkFragmentShadingRateAttachmentInfoKHR
 {
     VkStructureType sType;
@@ -13427,6 +13736,52 @@ typedef struct VkGetLatencyMarkerInfoNV
     VkLatencyTimingsFrameReportNV *pTimings;
 } VkGetLatencyMarkerInfoNV;
 
+typedef struct VkGpaDeviceClockModeInfoAMD
+{
+    VkStructureType sType;
+    const void *pNext;
+    VkGpaDeviceClockModeAMD clockMode;
+    float memoryClockRatioToPeak;
+    float engineClockRatioToPeak;
+} VkGpaDeviceClockModeInfoAMD;
+
+typedef struct VkGpaDeviceGetClockInfoAMD
+{
+    VkStructureType sType;
+    void *pNext;
+    float memoryClockRatioToPeak;
+    float engineClockRatioToPeak;
+    uint32_t memoryClockFrequency;
+    uint32_t engineClockFrequency;
+} VkGpaDeviceGetClockInfoAMD;
+
+typedef struct VkGpaSampleBeginInfoAMD
+{
+    VkStructureType sType;
+    const void *pNext;
+    VkGpaSampleTypeAMD sampleType;
+    VkBool32 sampleInternalOperations;
+    VkBool32 cacheFlushOnCounterCollection;
+    VkBool32 sqShaderMaskEnable;
+    VkGpaSqShaderStageFlagsAMD sqShaderMask;
+    uint32_t perfCounterCount;
+    const VkGpaPerfCounterAMD *pPerfCounters;
+    uint32_t streamingPerfTraceSampleInterval;
+    VkDeviceSize WINE_VK_ALIGN(8) perfCounterDeviceMemoryLimit;
+    VkBool32 sqThreadTraceEnable;
+    VkBool32 sqThreadTraceSuppressInstructionTokens;
+    VkDeviceSize WINE_VK_ALIGN(8) sqThreadTraceDeviceMemoryLimit;
+    VkPipelineStageFlags timingPreSample;
+    VkPipelineStageFlags timingPostSample;
+} VkGpaSampleBeginInfoAMD;
+
+typedef struct VkGpaSessionCreateInfoAMD
+{
+    VkStructureType sType;
+    const void *pNext;
+    VkGpaSessionAMD WINE_VK_ALIGN(8) secondaryCopySource;
+} VkGpaSessionCreateInfoAMD;
+
 typedef struct VkGraphicsPipelineCreateInfo
 {
     VkStructureType sType;
@@ -13570,6 +13925,13 @@ typedef struct VkImageCopy
     VkExtent3D extent;
 } VkImageCopy;
 
+typedef struct VkImageCreateFlags2CreateInfoKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkImageCreateFlags2KHR WINE_VK_ALIGN(8) flags;
+} VkImageCreateFlags2CreateInfoKHR;
+
 typedef struct VkImageDrmFormatModifierExplicitCreateInfoEXT
 {
     VkStructureType sType;
@@ -13658,6 +14020,13 @@ typedef struct VkImageSparseMemoryRequirementsInfo2
 } VkImageSparseMemoryRequirementsInfo2;
 typedef VkImageSparseMemoryRequirementsInfo2 VkImageSparseMemoryRequirementsInfo2KHR;
 
+typedef struct VkImageStencilUsage2CreateInfoKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkImageUsageFlags2KHR WINE_VK_ALIGN(8) stencilUsage;
+} VkImageStencilUsage2CreateInfoKHR;
+
 typedef struct VkImageStencilUsageCreateInfo
 {
     VkStructureType sType;
@@ -13673,6 +14042,13 @@ typedef struct VkImageSwapchainCreateInfoKHR
     VkSwapchainKHR WINE_VK_ALIGN(8) swapchain;
 } VkImageSwapchainCreateInfoKHR;
 
+typedef struct VkImageUsageFlags2CreateInfoKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkImageUsageFlags2KHR WINE_VK_ALIGN(8) usage;
+} VkImageUsageFlags2CreateInfoKHR;
+
 typedef struct VkImageViewASTCDecodeModeEXT
 {
     VkStructureType sType;
@@ -13728,6 +14104,13 @@ typedef struct VkImageViewSlicedCreateInfoEXT
     uint32_t sliceCount;
 } VkImageViewSlicedCreateInfoEXT;
 
+typedef struct VkImageViewUsage2CreateInfoKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkImageUsageFlags2KHR WINE_VK_ALIGN(8) usage;
+} VkImageViewUsage2CreateInfoKHR;
+
 typedef struct VkImageViewUsageCreateInfo
 {
     VkStructureType sType;
@@ -14159,12 +14542,13 @@ typedef struct VkMicromapCreateInfoEXT
     VkDeviceAddress WINE_VK_ALIGN(8) deviceAddress;
 } VkMicromapCreateInfoEXT;
 
-typedef struct VkMicromapTriangleEXT
+typedef struct VkMicromapTriangleKHR
 {
     uint32_t dataOffset;
     uint16_t subdivisionLevel;
     uint16_t format;
-} VkMicromapTriangleEXT;
+} VkMicromapTriangleKHR;
+typedef VkMicromapTriangleKHR VkMicromapTriangleEXT;
 
 typedef struct VkMicromapVersionInfoEXT
 {
@@ -14730,6 +15114,13 @@ typedef struct VkPhysicalDeviceCooperativeMatrixConversionFeaturesQCOM
     VkBool32 cooperativeMatrixConversion;
 } VkPhysicalDeviceCooperativeMatrixConversionFeaturesQCOM;
 
+typedef struct VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 cooperativeMatrixDecodeVector;
+} VkPhysicalDeviceCooperativeMatrixDecodeVectorFeaturesNV;
+
 typedef struct VkPhysicalDeviceCooperativeMatrixFeaturesKHR
 {
     VkStructureType sType;
@@ -15237,6 +15628,13 @@ typedef struct VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT
     VkBool32 dynamicRenderingUnusedAttachments;
 } VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT;
 
+typedef struct VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 elapsedTimerQuery;
+} VkPhysicalDeviceElapsedTimerQueryFeaturesQCOM;
+
 typedef struct VkPhysicalDeviceExclusiveScissorFeaturesNV
 {
     VkStructureType sType;
@@ -15304,6 +15702,13 @@ typedef struct VkPhysicalDeviceExtendedDynamicStateFeaturesEXT
     VkBool32 extendedDynamicState;
 } VkPhysicalDeviceExtendedDynamicStateFeaturesEXT;
 
+typedef struct VkPhysicalDeviceExtendedFlagsFeaturesKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 extendedFlags;
+} VkPhysicalDeviceExtendedFlagsFeaturesKHR;
+
 typedef struct VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV
 {
     VkStructureType sType;
@@ -15594,6 +15999,34 @@ typedef struct VkPhysicalDeviceGlobalPriorityQueryFeatures
 } VkPhysicalDeviceGlobalPriorityQueryFeatures;
 typedef VkPhysicalDeviceGlobalPriorityQueryFeatures VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR, VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT;
 
+typedef struct VkPhysicalDeviceGpaFeaturesAMD
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 perfCounters;
+    VkBool32 streamingPerfCounters;
+    VkBool32 sqThreadTracing;
+    VkBool32 clockModes;
+} VkPhysicalDeviceGpaFeaturesAMD;
+
+typedef struct VkPhysicalDeviceGpaProperties2AMD
+{
+    VkStructureType sType;
+    void *pNext;
+    uint32_t revisionId;
+} VkPhysicalDeviceGpaProperties2AMD;
+
+typedef struct VkPhysicalDeviceGpaPropertiesAMD
+{
+    VkStructureType sType;
+    void *pNext;
+    VkPhysicalDeviceGpaPropertiesFlagsAMD flags;
+    VkDeviceSize WINE_VK_ALIGN(8) maxSqttSeBufferSize;
+    uint32_t shaderEngineCount;
+    uint32_t perfBlockCount;
+    VkGpaPerfBlockPropertiesAMD *pPerfBlocks;
+} VkPhysicalDeviceGpaPropertiesAMD;
+
 typedef struct VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT
 {
     VkStructureType sType;
@@ -15739,6 +16172,15 @@ typedef struct VkPhysicalDeviceImageProcessing2PropertiesQCOM
     VkExtent2D maxBlockMatchWindow;
 } VkPhysicalDeviceImageProcessing2PropertiesQCOM;
 
+typedef struct VkPhysicalDeviceImageProcessing3FeaturesQCOM
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 imageGatherLinear;
+    VkBool32 imageGatherExtendedModes;
+    VkBool32 blockMatchExtendedClampToEdge;
+} VkPhysicalDeviceImageProcessing3FeaturesQCOM;
+
 typedef struct VkPhysicalDeviceImageProcessingFeaturesQCOM
 {
     VkStructureType sType;
@@ -16195,6 +16637,13 @@ typedef struct VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT
     VkBool32 multisampledRenderToSingleSampled;
 } VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT;
 
+typedef struct VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 multisampledRenderToSwapchain;
+} VkPhysicalDeviceMultisampledRenderToSwapchainFeaturesEXT;
+
 typedef struct VkPhysicalDeviceMultiviewFeatures
 {
     VkStructureType sType;
@@ -16268,6 +16717,13 @@ typedef struct VkPhysicalDeviceOpacityMicromapFeaturesEXT
     VkBool32 micromapHostCommands;
 } VkPhysicalDeviceOpacityMicromapFeaturesEXT;
 
+typedef struct VkPhysicalDeviceOpacityMicromapFeaturesKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 micromap;
+} VkPhysicalDeviceOpacityMicromapFeaturesKHR;
+
 typedef struct VkPhysicalDeviceOpacityMicromapPropertiesEXT
 {
     VkStructureType sType;
@@ -16276,6 +16732,16 @@ typedef struct VkPhysicalDeviceOpacityMicromapPropertiesEXT
     uint32_t maxOpacity4StateSubdivisionLevel;
 } VkPhysicalDeviceOpacityMicromapPropertiesEXT;
 
+typedef struct VkPhysicalDeviceOpacityMicromapPropertiesKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    uint32_t maxOpacity2StateSubdivisionLevel;
+    uint32_t maxOpacity4StateSubdivisionLevel;
+    uint32_t maxOpacityLossy4StateSubdivisionLevel;
+    uint64_t WINE_VK_ALIGN(8) maxMicromapTriangles;
+} VkPhysicalDeviceOpacityMicromapPropertiesKHR;
+
 typedef struct VkPhysicalDeviceOpticalFlowFeaturesNV
 {
     VkStructureType sType;
@@ -17230,6 +17696,20 @@ typedef struct VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT
     uint8_t shaderModuleIdentifierAlgorithmUUID[VK_UUID_SIZE];
 } VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT;
 
+typedef struct VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 shaderMultipleWaitQueues;
+} VkPhysicalDeviceShaderMultipleWaitQueuesFeaturesQCOM;
+
+typedef struct VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM
+{
+    VkStructureType sType;
+    void *pNext;
+    uint32_t maxShaderWaitQueues;
+} VkPhysicalDeviceShaderMultipleWaitQueuesPropertiesQCOM;
+
 typedef struct VkPhysicalDeviceShaderObjectFeaturesEXT
 {
     VkStructureType sType;
@@ -17281,6 +17761,20 @@ typedef struct VkPhysicalDeviceShaderSMBuiltinsPropertiesNV
     uint32_t shaderWarpsPerSM;
 } VkPhysicalDeviceShaderSMBuiltinsPropertiesNV;
 
+typedef struct VkPhysicalDeviceShaderSplitBarrierFeaturesEXT
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 shaderSplitBarrier;
+} VkPhysicalDeviceShaderSplitBarrierFeaturesEXT;
+
+typedef struct VkPhysicalDeviceShaderSplitBarrierPropertiesEXT
+{
+    VkStructureType sType;
+    void *pNext;
+    uint32_t splitBarrierReservedSharedMemory;
+} VkPhysicalDeviceShaderSplitBarrierPropertiesEXT;
+
 typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures
 {
     VkStructureType sType;
@@ -17708,6 +18202,13 @@ typedef struct VkPhysicalDeviceVideoEncodeAV1FeaturesKHR
     VkBool32 videoEncodeAV1;
 } VkPhysicalDeviceVideoEncodeAV1FeaturesKHR;
 
+typedef struct VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkBool32 videoEncodeFeedback2;
+} VkPhysicalDeviceVideoEncodeFeedback2FeaturesKHR;
+
 typedef struct VkPhysicalDeviceVideoEncodeIntraRefreshFeaturesKHR
 {
     VkStructureType sType;
@@ -18653,6 +19154,14 @@ typedef struct VkQueryPoolVideoEncodeFeedbackCreateInfoKHR
     VkVideoEncodeFeedbackFlagsKHR encodeFeedbackFlags;
 } VkQueryPoolVideoEncodeFeedbackCreateInfoKHR;
 
+typedef struct VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR
+{
+    VkStructureType sType;
+    const void *pNext;
+    uint32_t maxPerPartitionFeedbackEntries;
+    VkVideoEncodePerPartitionFeedbackFlagsKHR perPartitionEncodeFeedbackFlags;
+} VkQueryPoolVideoEncodePerPartitionFeedbackCreateInfoKHR;
+
 typedef struct VkQueueFamilyCheckpointProperties2NV
 {
     VkStructureType sType;
@@ -19338,6 +19847,13 @@ typedef struct VkShaderStatisticsInfoAMD
     uint32_t computeWorkGroupSize[3];
 } VkShaderStatisticsInfoAMD;
 
+typedef struct VkSharedPresentSurfaceCapabilities2KHR
+{
+    VkStructureType sType;
+    void *pNext;
+    VkImageUsageFlags2KHR WINE_VK_ALIGN(8) sharedPresentSupportedUsageFlags;
+} VkSharedPresentSurfaceCapabilities2KHR;
+
 typedef struct VkSparseImageFormatProperties2
 {
     VkStructureType sType;
@@ -19539,6 +20055,13 @@ typedef struct VkSwapchainCreateInfoKHR
     VkSwapchainKHR WINE_VK_ALIGN(8) oldSwapchain;
 } VkSwapchainCreateInfoKHR;
 
+typedef struct VkSwapchainFlagsSurfaceCapabilitiesEXT
+{
+    VkStructureType sType;
+    void *pNext;
+    VkSwapchainCreateFlagsKHR swapchainSupportedFlags;
+} VkSwapchainFlagsSurfaceCapabilitiesEXT;
+
 typedef struct VkSwapchainLatencyCreateInfoNV
 {
     VkStructureType sType;
@@ -20090,6 +20613,14 @@ typedef struct VkVideoEncodeCapabilitiesKHR
     VkVideoEncodeFeedbackFlagsKHR supportedEncodeFeedbackFlags;
 } VkVideoEncodeCapabilitiesKHR;
 
+typedef struct VkVideoEncodeFeedback2CapabilitiesKHR
+{
+    VkStructureType sType;
+    void *pNext;
+    uint32_t maxPerPartitionFeedbackEntries;
+    VkVideoEncodePerPartitionFeedbackFlagsKHR supportedPerPartitionEncodeFeedbackFlags;
+} VkVideoEncodeFeedback2CapabilitiesKHR;
+
 typedef struct VkVideoEncodeH264CapabilitiesKHR
 {
     VkStructureType sType;
@@ -20599,6 +21130,8 @@ typedef void (VKAPI_PTR *PFN_vkCmdBeginConditionalRendering2EXT)(VkCommandBuffer
 typedef void (VKAPI_PTR *PFN_vkCmdBeginConditionalRenderingEXT)(VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT *pConditionalRenderingBegin);
 typedef void (VKAPI_PTR *PFN_vkCmdBeginCustomResolveEXT)(VkCommandBuffer commandBuffer, const VkBeginCustomResolveInfoEXT *pBeginCustomResolveInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdBeginDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT *pLabelInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkCmdBeginGpaSampleAMD)(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession, const VkGpaSampleBeginInfoAMD *pGpaSampleBeginInfo, uint32_t *pSampleID);
+typedef VkResult (VKAPI_PTR *PFN_vkCmdBeginGpaSessionAMD)(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession);
 typedef void (VKAPI_PTR *PFN_vkCmdBeginPerTileExecutionQCOM)(VkCommandBuffer commandBuffer, const VkPerTileBeginInfoQCOM *pPerTileBeginInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags);
 typedef void (VKAPI_PTR *PFN_vkCmdBeginQueryIndexedEXT)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags, uint32_t index);
@@ -20658,6 +21191,7 @@ typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer2KHR)(VkCommandBuffer commandBuffer,
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage2)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdCopyGpaSessionResultsAMD)(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImage2)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo);
@@ -20725,6 +21259,8 @@ typedef void (VKAPI_PTR *PFN_vkCmdDrawMultiIndexedEXT)(VkCommandBuffer commandBu
 typedef void (VKAPI_PTR *PFN_vkCmdEncodeVideoKHR)(VkCommandBuffer commandBuffer, const VkVideoEncodeInfoKHR *pEncodeInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdEndConditionalRenderingEXT)(VkCommandBuffer commandBuffer);
 typedef void (VKAPI_PTR *PFN_vkCmdEndDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer);
+typedef void (VKAPI_PTR *PFN_vkCmdEndGpaSampleAMD)(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession, uint32_t sampleID);
+typedef VkResult (VKAPI_PTR *PFN_vkCmdEndGpaSessionAMD)(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession);
 typedef void (VKAPI_PTR *PFN_vkCmdEndPerTileExecutionQCOM)(VkCommandBuffer commandBuffer, const VkPerTileEndInfoQCOM *pPerTileEndInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query);
 typedef void (VKAPI_PTR *PFN_vkCmdEndQueryIndexedEXT)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, uint32_t index);
@@ -20937,6 +21473,7 @@ typedef VkResult (VKAPI_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice
 typedef VkResult (VKAPI_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkEvent *pEvent);
 typedef VkResult (VKAPI_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence);
 typedef VkResult (VKAPI_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateGpaSessionAMD)(VkDevice device, const VkGpaSessionCreateInfoAMD *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkGpaSessionAMD *pGpaSession);
 typedef VkResult (VKAPI_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines);
 typedef VkResult (VKAPI_PTR *PFN_vkCreateHeadlessSurfaceEXT)(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
 typedef VkResult (VKAPI_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage);
@@ -20999,6 +21536,7 @@ typedef void (VKAPI_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocatio
 typedef void (VKAPI_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks *pAllocator);
 typedef void (VKAPI_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator);
 typedef void (VKAPI_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks *pAllocator);
+typedef void (VKAPI_PTR *PFN_vkDestroyGpaSessionAMD)(VkDevice device, VkGpaSessionAMD gpaSession, const VkAllocationCallbacks *pAllocator);
 typedef void (VKAPI_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator);
 typedef void (VKAPI_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks *pAllocator);
 typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutEXT)(VkDevice device, VkIndirectCommandsLayoutEXT indirectCommandsLayout, const VkAllocationCallbacks *pAllocator);
@@ -21111,6 +21649,9 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetFenceWin32HandleKHR)(VkDevice device, cons
 typedef VkResult (VKAPI_PTR *PFN_vkGetFramebufferTilePropertiesQCOM)(VkDevice device, VkFramebuffer framebuffer, uint32_t *pPropertiesCount, VkTilePropertiesQCOM *pProperties);
 typedef void (VKAPI_PTR *PFN_vkGetGeneratedCommandsMemoryRequirementsEXT)(VkDevice device, const VkGeneratedCommandsMemoryRequirementsInfoEXT *pInfo, VkMemoryRequirements2 *pMemoryRequirements);
 typedef void (VKAPI_PTR *PFN_vkGetGeneratedCommandsMemoryRequirementsNV)(VkDevice device, const VkGeneratedCommandsMemoryRequirementsInfoNV *pInfo, VkMemoryRequirements2 *pMemoryRequirements);
+typedef VkResult (VKAPI_PTR *PFN_vkGetGpaDeviceClockInfoAMD)(VkDevice device, VkGpaDeviceGetClockInfoAMD *pInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkGetGpaSessionResultsAMD)(VkDevice device, VkGpaSessionAMD gpaSession, uint32_t sampleID, size_t *pSizeInBytes, void *pData);
+typedef VkResult (VKAPI_PTR *PFN_vkGetGpaSessionStatusAMD)(VkDevice device, VkGpaSessionAMD gpaSession);
 typedef VkResult (VKAPI_PTR *PFN_vkGetImageDrmFormatModifierPropertiesEXT)(VkDevice device, VkImage image, VkImageDrmFormatModifierPropertiesEXT *pProperties);
 typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements *pMemoryRequirements);
 typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements2)(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements);
@@ -21275,12 +21816,14 @@ typedef VkResult (VKAPI_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandP
 typedef VkResult (VKAPI_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags);
 typedef VkResult (VKAPI_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event);
 typedef VkResult (VKAPI_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences);
+typedef VkResult (VKAPI_PTR *PFN_vkResetGpaSessionAMD)(VkDevice device, VkGpaSessionAMD gpaSession);
 typedef void (VKAPI_PTR *PFN_vkResetQueryPool)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
 typedef void (VKAPI_PTR *PFN_vkResetQueryPoolEXT)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
 typedef VkResult (VKAPI_PTR *PFN_vkSetDebugUtilsObjectNameEXT)(VkDevice device, const VkDebugUtilsObjectNameInfoEXT *pNameInfo);
 typedef VkResult (VKAPI_PTR *PFN_vkSetDebugUtilsObjectTagEXT)(VkDevice device, const VkDebugUtilsObjectTagInfoEXT *pTagInfo);
 typedef void (VKAPI_PTR *PFN_vkSetDeviceMemoryPriorityEXT)(VkDevice device, VkDeviceMemory memory, float priority);
 typedef VkResult (VKAPI_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event);
+typedef VkResult (VKAPI_PTR *PFN_vkSetGpaDeviceClockModeAMD)(VkDevice device, VkGpaDeviceClockModeInfoAMD *pInfo);
 typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR *pSwapchains, const VkHdrMetadataEXT *pMetadata);
 typedef void (VKAPI_PTR *PFN_vkSetLatencyMarkerNV)(VkDevice device, VkSwapchainKHR swapchain, const VkSetLatencyMarkerInfoNV *pLatencyMarkerInfo);
 typedef VkResult (VKAPI_PTR *PFN_vkSetLatencySleepModeNV)(VkDevice device, VkSwapchainKHR swapchain, const VkLatencySleepModeInfoNV *pSleepModeInfo);
@@ -21343,6 +21886,8 @@ void VKAPI_CALL vkCmdBeginConditionalRendering2EXT(VkCommandBuffer commandBuffer
 void VKAPI_CALL vkCmdBeginConditionalRenderingEXT(VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT *pConditionalRenderingBegin);
 void VKAPI_CALL vkCmdBeginCustomResolveEXT(VkCommandBuffer commandBuffer, const VkBeginCustomResolveInfoEXT *pBeginCustomResolveInfo);
 void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT *pLabelInfo);
+VkResult VKAPI_CALL vkCmdBeginGpaSampleAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession, const VkGpaSampleBeginInfoAMD *pGpaSampleBeginInfo, uint32_t *pSampleID);
+VkResult VKAPI_CALL vkCmdBeginGpaSessionAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession);
 void VKAPI_CALL vkCmdBeginPerTileExecutionQCOM(VkCommandBuffer commandBuffer, const VkPerTileBeginInfoQCOM *pPerTileBeginInfo);
 void VKAPI_CALL vkCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags);
 void VKAPI_CALL vkCmdBeginQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags, uint32_t index);
@@ -21402,6 +21947,7 @@ void VKAPI_CALL vkCmdCopyBuffer2KHR(VkCommandBuffer commandBuffer, const VkCopyB
 void VKAPI_CALL vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions);
 void VKAPI_CALL vkCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo);
 void VKAPI_CALL vkCmdCopyBufferToImage2KHR(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo);
+void VKAPI_CALL vkCmdCopyGpaSessionResultsAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession);
 void VKAPI_CALL vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions);
 void VKAPI_CALL vkCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo);
 void VKAPI_CALL vkCmdCopyImage2KHR(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo);
@@ -21469,6 +22015,8 @@ void VKAPI_CALL vkCmdDrawMultiIndexedEXT(VkCommandBuffer commandBuffer, uint32_t
 void VKAPI_CALL vkCmdEncodeVideoKHR(VkCommandBuffer commandBuffer, const VkVideoEncodeInfoKHR *pEncodeInfo);
 void VKAPI_CALL vkCmdEndConditionalRenderingEXT(VkCommandBuffer commandBuffer);
 void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer);
+void VKAPI_CALL vkCmdEndGpaSampleAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession, uint32_t sampleID);
+VkResult VKAPI_CALL vkCmdEndGpaSessionAMD(VkCommandBuffer commandBuffer, VkGpaSessionAMD gpaSession);
 void VKAPI_CALL vkCmdEndPerTileExecutionQCOM(VkCommandBuffer commandBuffer, const VkPerTileEndInfoQCOM *pPerTileEndInfo);
 void VKAPI_CALL vkCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query);
 void VKAPI_CALL vkCmdEndQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, uint32_t index);
@@ -21681,6 +22229,7 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi
 VkResult VKAPI_CALL vkCreateEvent(VkDevice device, const VkEventCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkEvent *pEvent);
 VkResult VKAPI_CALL vkCreateFence(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence);
 VkResult VKAPI_CALL vkCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer);
+VkResult VKAPI_CALL vkCreateGpaSessionAMD(VkDevice device, const VkGpaSessionCreateInfoAMD *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkGpaSessionAMD *pGpaSession);
 VkResult VKAPI_CALL vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines);
 VkResult VKAPI_CALL vkCreateHeadlessSurfaceEXT(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
 VkResult VKAPI_CALL vkCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage);
@@ -21743,6 +22292,7 @@ void VKAPI_CALL vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pA
 void VKAPI_CALL vkDestroyEvent(VkDevice device, VkEvent event, const VkAllocationCallbacks *pAllocator);
 void VKAPI_CALL vkDestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator);
 void VKAPI_CALL vkDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks *pAllocator);
+void VKAPI_CALL vkDestroyGpaSessionAMD(VkDevice device, VkGpaSessionAMD gpaSession, const VkAllocationCallbacks *pAllocator);
 void VKAPI_CALL vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator);
 void VKAPI_CALL vkDestroyImageView(VkDevice device, VkImageView imageView, const VkAllocationCallbacks *pAllocator);
 void VKAPI_CALL vkDestroyIndirectCommandsLayoutEXT(VkDevice device, VkIndirectCommandsLayoutEXT indirectCommandsLayout, const VkAllocationCallbacks *pAllocator);
@@ -21855,6 +22405,9 @@ VkResult VKAPI_CALL vkGetFenceWin32HandleKHR(VkDevice device, const VkFenceGetWi
 VkResult VKAPI_CALL vkGetFramebufferTilePropertiesQCOM(VkDevice device, VkFramebuffer framebuffer, uint32_t *pPropertiesCount, VkTilePropertiesQCOM *pProperties);
 void VKAPI_CALL vkGetGeneratedCommandsMemoryRequirementsEXT(VkDevice device, const VkGeneratedCommandsMemoryRequirementsInfoEXT *pInfo, VkMemoryRequirements2 *pMemoryRequirements);
 void VKAPI_CALL vkGetGeneratedCommandsMemoryRequirementsNV(VkDevice device, const VkGeneratedCommandsMemoryRequirementsInfoNV *pInfo, VkMemoryRequirements2 *pMemoryRequirements);
+VkResult VKAPI_CALL vkGetGpaDeviceClockInfoAMD(VkDevice device, VkGpaDeviceGetClockInfoAMD *pInfo);
+VkResult VKAPI_CALL vkGetGpaSessionResultsAMD(VkDevice device, VkGpaSessionAMD gpaSession, uint32_t sampleID, size_t *pSizeInBytes, void *pData);
+VkResult VKAPI_CALL vkGetGpaSessionStatusAMD(VkDevice device, VkGpaSessionAMD gpaSession);
 VkResult VKAPI_CALL vkGetImageDrmFormatModifierPropertiesEXT(VkDevice device, VkImage image, VkImageDrmFormatModifierPropertiesEXT *pProperties);
 void VKAPI_CALL vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements *pMemoryRequirements);
 void VKAPI_CALL vkGetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements);
@@ -22019,12 +22572,14 @@ VkResult VKAPI_CALL vkResetCommandPool(VkDevice device, VkCommandPool commandPoo
 VkResult VKAPI_CALL vkResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags);
 VkResult VKAPI_CALL vkResetEvent(VkDevice device, VkEvent event);
 VkResult VKAPI_CALL vkResetFences(VkDevice device, uint32_t fenceCount, const VkFence *pFences);
+VkResult VKAPI_CALL vkResetGpaSessionAMD(VkDevice device, VkGpaSessionAMD gpaSession);
 void VKAPI_CALL vkResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
 void VKAPI_CALL vkResetQueryPoolEXT(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
 VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT *pNameInfo);
 VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT(VkDevice device, const VkDebugUtilsObjectTagInfoEXT *pTagInfo);
 void VKAPI_CALL vkSetDeviceMemoryPriorityEXT(VkDevice device, VkDeviceMemory memory, float priority);
 VkResult VKAPI_CALL vkSetEvent(VkDevice device, VkEvent event);
+VkResult VKAPI_CALL vkSetGpaDeviceClockModeAMD(VkDevice device, VkGpaDeviceClockModeInfoAMD *pInfo);
 void VKAPI_CALL vkSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR *pSwapchains, const VkHdrMetadataEXT *pMetadata);
 void VKAPI_CALL vkSetLatencyMarkerNV(VkDevice device, VkSwapchainKHR swapchain, const VkSetLatencyMarkerInfoNV *pLatencyMarkerInfo);
 VkResult VKAPI_CALL vkSetLatencySleepModeNV(VkDevice device, VkSwapchainKHR swapchain, const VkLatencySleepModeInfoNV *pSleepModeInfo);
@@ -22088,6 +22643,8 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkCmdBeginConditionalRenderingEXT) \
     USE_VK_FUNC(vkCmdBeginCustomResolveEXT) \
     USE_VK_FUNC(vkCmdBeginDebugUtilsLabelEXT) \
+    USE_VK_FUNC(vkCmdBeginGpaSampleAMD) \
+    USE_VK_FUNC(vkCmdBeginGpaSessionAMD) \
     USE_VK_FUNC(vkCmdBeginPerTileExecutionQCOM) \
     USE_VK_FUNC(vkCmdBeginQuery) \
     USE_VK_FUNC(vkCmdBeginQueryIndexedEXT) \
@@ -22147,6 +22704,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkCmdCopyBufferToImage) \
     USE_VK_FUNC(vkCmdCopyBufferToImage2) \
     USE_VK_FUNC(vkCmdCopyBufferToImage2KHR) \
+    USE_VK_FUNC(vkCmdCopyGpaSessionResultsAMD) \
     USE_VK_FUNC(vkCmdCopyImage) \
     USE_VK_FUNC(vkCmdCopyImage2) \
     USE_VK_FUNC(vkCmdCopyImage2KHR) \
@@ -22214,6 +22772,8 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkCmdEncodeVideoKHR) \
     USE_VK_FUNC(vkCmdEndConditionalRenderingEXT) \
     USE_VK_FUNC(vkCmdEndDebugUtilsLabelEXT) \
+    USE_VK_FUNC(vkCmdEndGpaSampleAMD) \
+    USE_VK_FUNC(vkCmdEndGpaSessionAMD) \
     USE_VK_FUNC(vkCmdEndPerTileExecutionQCOM) \
     USE_VK_FUNC(vkCmdEndQuery) \
     USE_VK_FUNC(vkCmdEndQueryIndexedEXT) \
@@ -22423,6 +22983,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkCreateEvent) \
     USE_VK_FUNC(vkCreateFence) \
     USE_VK_FUNC(vkCreateFramebuffer) \
+    USE_VK_FUNC(vkCreateGpaSessionAMD) \
     USE_VK_FUNC(vkCreateGraphicsPipelines) \
     USE_VK_FUNC(vkCreateImage) \
     USE_VK_FUNC(vkCreateImageView) \
@@ -22475,6 +23036,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkDestroyEvent) \
     USE_VK_FUNC(vkDestroyFence) \
     USE_VK_FUNC(vkDestroyFramebuffer) \
+    USE_VK_FUNC(vkDestroyGpaSessionAMD) \
     USE_VK_FUNC(vkDestroyImage) \
     USE_VK_FUNC(vkDestroyImageView) \
     USE_VK_FUNC(vkDestroyIndirectCommandsLayoutEXT) \
@@ -22573,6 +23135,9 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkGetFramebufferTilePropertiesQCOM) \
     USE_VK_FUNC(vkGetGeneratedCommandsMemoryRequirementsEXT) \
     USE_VK_FUNC(vkGetGeneratedCommandsMemoryRequirementsNV) \
+    USE_VK_FUNC(vkGetGpaDeviceClockInfoAMD) \
+    USE_VK_FUNC(vkGetGpaSessionResultsAMD) \
+    USE_VK_FUNC(vkGetGpaSessionStatusAMD) \
     USE_VK_FUNC(vkGetImageDrmFormatModifierPropertiesEXT) \
     USE_VK_FUNC(vkGetImageMemoryRequirements) \
     USE_VK_FUNC(vkGetImageMemoryRequirements2) \
@@ -22677,12 +23242,14 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_FUNC(vkResetDescriptorPool) \
     USE_VK_FUNC(vkResetEvent) \
     USE_VK_FUNC(vkResetFences) \
+    USE_VK_FUNC(vkResetGpaSessionAMD) \
     USE_VK_FUNC(vkResetQueryPool) \
     USE_VK_FUNC(vkResetQueryPoolEXT) \
     USE_VK_FUNC(vkSetDebugUtilsObjectNameEXT) \
     USE_VK_FUNC(vkSetDebugUtilsObjectTagEXT) \
     USE_VK_FUNC(vkSetDeviceMemoryPriorityEXT) \
     USE_VK_FUNC(vkSetEvent) \
+    USE_VK_FUNC(vkSetGpaDeviceClockModeAMD) \
     USE_VK_FUNC(vkSetHdrMetadataEXT) \
     USE_VK_FUNC(vkSetLatencyMarkerNV) \
     USE_VK_FUNC(vkSetLatencySleepModeNV) \
@@ -22722,6 +23289,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_AMD_device_coherent_memory) \
     USE_VK_EXT(VK_AMD_draw_indirect_count) \
     USE_VK_EXT(VK_AMD_gcn_shader) \
+    USE_VK_EXT(VK_AMD_gpa_interface) \
     USE_VK_EXT(VK_AMD_gpu_shader_half_float) \
     USE_VK_EXT(VK_AMD_gpu_shader_int16) \
     USE_VK_EXT(VK_AMD_memory_overallocation_behavior) \
@@ -22816,6 +23384,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_EXT_mesh_shader) \
     USE_VK_EXT(VK_EXT_multi_draw) \
     USE_VK_EXT(VK_EXT_multisampled_render_to_single_sampled) \
+    USE_VK_EXT(VK_EXT_multisampled_render_to_swapchain) \
     USE_VK_EXT(VK_EXT_mutable_descriptor_type) \
     USE_VK_EXT(VK_EXT_nested_command_buffer) \
     USE_VK_EXT(VK_EXT_non_seamless_cube_map) \
@@ -22855,6 +23424,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_EXT_shader_module_identifier) \
     USE_VK_EXT(VK_EXT_shader_object) \
     USE_VK_EXT(VK_EXT_shader_replicated_composites) \
+    USE_VK_EXT(VK_EXT_shader_split_barrier) \
     USE_VK_EXT(VK_EXT_shader_stencil_export) \
     USE_VK_EXT(VK_EXT_shader_subgroup_ballot) \
     USE_VK_EXT(VK_EXT_shader_subgroup_partitioned) \
@@ -22912,6 +23482,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_KHR_driver_properties) \
     USE_VK_EXT(VK_KHR_dynamic_rendering) \
     USE_VK_EXT(VK_KHR_dynamic_rendering_local_read) \
+    USE_VK_EXT(VK_KHR_extended_flags) \
     USE_VK_EXT(VK_KHR_external_fence) \
     USE_VK_EXT(VK_KHR_external_fence_win32) \
     USE_VK_EXT(VK_KHR_external_memory) \
@@ -22943,6 +23514,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_KHR_maintenance9) \
     USE_VK_EXT(VK_KHR_map_memory2) \
     USE_VK_EXT(VK_KHR_multiview) \
+    USE_VK_EXT(VK_KHR_opacity_micromap) \
     USE_VK_EXT(VK_KHR_performance_query) \
     USE_VK_EXT(VK_KHR_pipeline_binary) \
     USE_VK_EXT(VK_KHR_pipeline_executable_properties) \
@@ -22999,6 +23571,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_KHR_video_decode_queue) \
     USE_VK_EXT(VK_KHR_video_decode_vp9) \
     USE_VK_EXT(VK_KHR_video_encode_av1) \
+    USE_VK_EXT(VK_KHR_video_encode_feedback2) \
     USE_VK_EXT(VK_KHR_video_encode_h264) \
     USE_VK_EXT(VK_KHR_video_encode_intra_refresh) \
     USE_VK_EXT(VK_KHR_video_encode_quantization_map) \
@@ -23020,6 +23593,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_NV_compute_shader_derivatives) \
     USE_VK_EXT(VK_NV_cooperative_matrix) \
     USE_VK_EXT(VK_NV_cooperative_matrix2) \
+    USE_VK_EXT(VK_NV_cooperative_matrix_decode_vector) \
     USE_VK_EXT(VK_NV_cooperative_vector) \
     USE_VK_EXT(VK_NV_copy_memory_indirect) \
     USE_VK_EXT(VK_NV_corner_sampled_image) \
@@ -23069,11 +23643,13 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_NV_viewport_swizzle) \
     USE_VK_EXT(VK_QCOM_cooperative_matrix_conversion) \
     USE_VK_EXT(VK_QCOM_data_graph_model) \
+    USE_VK_EXT(VK_QCOM_elapsed_timer_query) \
     USE_VK_EXT(VK_QCOM_filter_cubic_clamp) \
     USE_VK_EXT(VK_QCOM_filter_cubic_weights) \
     USE_VK_EXT(VK_QCOM_fragment_density_map_offset) \
     USE_VK_EXT(VK_QCOM_image_processing) \
     USE_VK_EXT(VK_QCOM_image_processing2) \
+    USE_VK_EXT(VK_QCOM_image_processing3) \
     USE_VK_EXT(VK_QCOM_multiview_per_view_render_areas) \
     USE_VK_EXT(VK_QCOM_multiview_per_view_viewports) \
     USE_VK_EXT(VK_QCOM_queue_perf_hint) \
@@ -23081,6 +23657,7 @@ VkResult VKAPI_CALL vkWriteSamplerDescriptorsEXT(VkDevice device, uint32_t sampl
     USE_VK_EXT(VK_QCOM_render_pass_store_ops) \
     USE_VK_EXT(VK_QCOM_render_pass_transform) \
     USE_VK_EXT(VK_QCOM_rotated_copy_commands) \
+    USE_VK_EXT(VK_QCOM_shader_multiple_wait_queues) \
     USE_VK_EXT(VK_QCOM_tile_memory_heap) \
     USE_VK_EXT(VK_QCOM_tile_properties) \
     USE_VK_EXT(VK_QCOM_tile_shading) \
diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h
index cfb098adfb0c..355140043db5 100644
--- a/include/wine/wined3d.h
+++ b/include/wine/wined3d.h
@@ -920,6 +920,8 @@ enum wined3d_memory_segment_group
 #define WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES                     0x00040000u
 #define WINED3D_SWAPCHAIN_RESTORE_WINDOW_STATE                  0x00080000u
 #define WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER                0x00100000u
+/* Allow the swapchain flag, but not actual locking */
+#define WINED3D_SWAPCHAIN_ALLOW_MS_LOCKABLE_BACKBUFFER          0x00200000u
 
 #define WINED3DDP_MAXTEXCOORD                                   8
 
diff --git a/libs/compiler-rt/Makefile.in b/libs/compiler-rt/Makefile.in
index 48f6762158f7..de2d2baa41f5 100644
--- a/libs/compiler-rt/Makefile.in
+++ b/libs/compiler-rt/Makefile.in
@@ -8,6 +8,7 @@ SOURCES = \
 	lib/builtins/arm/aeabi_uldivmod.S \
 	lib/builtins/arm/divmodsi4.S \
 	lib/builtins/arm/udivmodsi4.S \
+	lib/builtins/arm64ec/chkstk.S \
 	lib/builtins/ctzdi2.c \
 	lib/builtins/divdi3.c \
 	lib/builtins/divmoddi4.c \
diff --git a/libs/compiler-rt/lib/builtins/aarch64/chkstk.S b/libs/compiler-rt/lib/builtins/aarch64/chkstk.S
index 0a92e3057d08..89ec90b08a13 100644
--- a/libs/compiler-rt/lib/builtins/aarch64/chkstk.S
+++ b/libs/compiler-rt/lib/builtins/aarch64/chkstk.S
@@ -14,18 +14,12 @@
 //      bl      __chkstk
 //      sub     sp, sp, x15, lsl #4
 
-#if defined(__aarch64__) || defined(__arm64ec__)
-
-#ifdef __arm64ec__
-#define CHKSTK_FUNC __chkstk_arm64ec
-#else
-#define CHKSTK_FUNC __chkstk
-#endif
+#ifdef __aarch64__
 
 #define PAGE_SIZE 4096
 
         .p2align 2
-DEFINE_COMPILERRT_FUNCTION(CHKSTK_FUNC)
+DEFINE_COMPILERRT_FUNCTION(__chkstk)
         lsl    x16, x15, #4
         mov    x17, sp
 1:
@@ -35,6 +29,6 @@ DEFINE_COMPILERRT_FUNCTION(CHKSTK_FUNC)
         b.gt   1b
 
         ret
-END_COMPILERRT_FUNCTION(CHKSTK_FUNC)
+END_COMPILERRT_FUNCTION(__chkstk)
 
-#endif // defined(__aarch64__) || defined(__arm64ec__)
+#endif // __aarch64__
diff --git a/libs/compiler-rt/lib/builtins/arm64ec/chkstk.S b/libs/compiler-rt/lib/builtins/arm64ec/chkstk.S
new file mode 100644
index 000000000000..895dfb309f1b
--- /dev/null
+++ b/libs/compiler-rt/lib/builtins/arm64ec/chkstk.S
@@ -0,0 +1,30 @@
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+
+#include "../assembly.h"
+
+// __chkstk routine
+// This routine is windows specific.
+// http://msdn.microsoft.com/en-us/library/ms648426.aspx
+
+// This clobbers registers x16 and x17.
+// Does not modify any memory or the stack pointer.
+
+//      mov     x15, #256 // Number of bytes of stack, in units of 16 byte
+//      bl      __chkstk
+//      sub     sp, sp, x15, lsl #4
+
+#define PAGE_SIZE 4096
+
+        .p2align 2
+DEFINE_COMPILERRT_FUNCTION(__chkstk_arm64ec)
+        lsl    x16, x15, #4
+        mov    x17, sp
+1:
+        sub    x17, x17, #PAGE_SIZE
+        subs   x16, x16, #PAGE_SIZE
+        ldr    xzr, [x17]
+        b.gt   1b
+
+        ret
+END_COMPILERRT_FUNCTION(__chkstk_arm64ec)
diff --git a/libs/faudio/include/FAudio.h b/libs/faudio/include/FAudio.h
index 84eb610469dd..f8f0ee17fff0 100644
--- a/libs/faudio/include/FAudio.h
+++ b/libs/faudio/include/FAudio.h
@@ -496,7 +496,7 @@ extern FAudioGUID DATAFORMAT_SUBTYPE_IEEE_FLOAT;
 
 #define FAUDIO_ABI_VERSION	 0
 #define FAUDIO_MAJOR_VERSION	26
-#define FAUDIO_MINOR_VERSION	 2
+#define FAUDIO_MINOR_VERSION	 6
 #define FAUDIO_PATCH_VERSION	 0
 
 #define FAUDIO_COMPILED_VERSION ( \
diff --git a/libs/faudio/src/FACT_internal.c b/libs/faudio/src/FACT_internal.c
index eb2830104176..7f4669aef498 100644
--- a/libs/faudio/src/FACT_internal.c
+++ b/libs/faudio/src/FACT_internal.c
@@ -1824,7 +1824,7 @@ void FACT_INTERNAL_OnBufferEnd(FAudioVoiceCallback *callback, void* pContext)
 		c->wave->parentBank->parentEngine->pReadFile,
 		c->wave->parentBank->parentEngine->pGetOverlappedResult,
 		c->wave->parentBank->io,
-		c->wave->streamOffset,
+		c->wave->parentBank->file_offset + c->wave->streamOffset,
 		c->wave->parentBank->packetSize,
 		&c->wave->parentBank->packetBuffer,
 		&c->wave->parentBank->packetBufferLen,
@@ -3035,6 +3035,7 @@ uint32_t FACT_INTERNAL_ParseWaveBank(
 	wb->waveLock = FAudio_PlatformCreateMutex();
 	wb->packetSize = packetSize;
 	wb->io = io;
+	wb->file_offset = offset;
 
 	/* WaveBank Data */
 	SEEKSET(header.Segments[FACT_WAVEBANK_SEGIDX_BANKDATA].dwOffset)
diff --git a/libs/faudio/src/FACT_internal.h b/libs/faudio/src/FACT_internal.h
index 9601df0c162c..864d369ebc7a 100644
--- a/libs/faudio/src/FACT_internal.h
+++ b/libs/faudio/src/FACT_internal.h
@@ -571,6 +571,7 @@ struct FACTWaveBank
 	char *waveBankNames;
 
 	/* I/O information */
+	uint32_t file_offset;
 	uint32_t packetSize;
 	bool streaming;
 	uint8_t *packetBuffer;
diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c
index eb1621736b7c..dc19b5bbf9e7 100644
--- a/libs/faudio/src/FAudio.c
+++ b/libs/faudio/src/FAudio.c
@@ -59,9 +59,7 @@ static void FAudio_DUMPVOICE_WriteBuffer(
 	const FAudioSourceVoice *voice,
 	const FAudioBuffer *pBuffer,
 	const FAudioBufferWMA *pBufferWMA,
-	const uint32_t playBegin,
-	const uint32_t playLength
-);
+	const uint32_t size);
 #endif /* FAUDIO_DUMP_VOICES */
 
 /* FAudio Version */
@@ -130,6 +128,8 @@ uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
 #ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
 	FAudio_SetDebugConfiguration(*ppFAudio, &debugInit, NULL);
 #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+	(*ppFAudio)->refLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->refLock)
 	(*ppFAudio)->sourceLock = FAudio_PlatformCreateMutex();
 	LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->sourceLock)
 	(*ppFAudio)->submixLock = FAudio_PlatformCreateMutex();
@@ -147,10 +147,20 @@ uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
 
 uint32_t FAudio_AddRef(FAudio *audio)
 {
+	uint32_t refcount;
+
 	LOG_API_ENTER(audio)
+
+	// FIXME: This should be SDL_AtomicIncRef -flibit
+	FAudio_PlatformLockMutex(audio->refLock);
+	LOG_MUTEX_LOCK(audio, audio->refLock)
 	audio->refcount += 1;
+	refcount = audio->refcount;
+	FAudio_PlatformUnlockMutex(audio->refLock);
+	LOG_MUTEX_UNLOCK(audio, audio->refLock)
+
 	LOG_API_EXIT(audio)
-	return audio->refcount;
+	return refcount;
 }
 
 static void destroy_voice(FAudioVoice *voice);
@@ -161,8 +171,15 @@ uint32_t FAudio_Release(FAudio *audio)
 	FAudioVoice *voice;
 
 	LOG_API_ENTER(audio)
+
+	// FIXME: This should be SDL_AtomicDecRef -flibit
+	FAudio_PlatformLockMutex(audio->refLock);
+	LOG_MUTEX_LOCK(audio, audio->refLock)
 	audio->refcount -= 1;
 	refcount = audio->refcount;
+	FAudio_PlatformUnlockMutex(audio->refLock);
+	LOG_MUTEX_UNLOCK(audio, audio->refLock)
+
 	if (audio->refcount == 0)
 	{
 		while (audio->sources)
@@ -182,6 +199,8 @@ uint32_t FAudio_Release(FAudio *audio)
 		audio->pFree(audio->decodeCache);
 		audio->pFree(audio->resampleCache);
 		audio->pFree(audio->effectChainCache);
+		LOG_MUTEX_DESTROY(audio, audio->refLock)
+		FAudio_PlatformDestroyMutex(audio->refLock);
 		LOG_MUTEX_DESTROY(audio, audio->sourceLock)
 		FAudio_PlatformDestroyMutex(audio->sourceLock);
 		LOG_MUTEX_DESTROY(audio, audio->submixLock)
@@ -271,6 +290,68 @@ void FAudio_UnregisterForCallbacks(
 	LOG_API_EXIT(audio)
 }
 
+/* WMA decoding is dependent on the bitrate. Only a certain set of bitrates are
+ * actually documented as valid for xWMA.
+ *
+ * The documented bitrates for XACT (aWMAAvgBytesPerSec) as encoded into the
+ * wavebank are often incorrect, at least as late as the August 2008 SDK
+ * (XACT version 3.2). The actual WMA data is encoded at a different bitrate
+ * than the wavebank header apparently states. Similarly, the standalone xWMA
+ * encoder produces xWMA files with an incorrect bitrate in the format chunk.
+ *
+ * While we could fix this in XACT, testing shows that XAudio will do some
+ * degree of fixing up invalid bitrates itself.
+ * The algorithm doesn't seem to be quite the same as here, but this should
+ * at least take care of normalizing all invalid bitrates generated by the
+ * xWMA encoder into the correct valid bitrates that reflect the actual data. */
+static uint32_t fixup_wma_bitrate(FAudio *audio, const FAudioWaveFormatEx *format)
+{
+	static const struct
+	{
+		uint16_t channel_count;
+		uint32_t sample_rate;
+		uint32_t bytes_per_second[5];
+	}
+	rates[] =
+	{
+		{6, 48000, {16000, 24000}},
+		{2, 48000, {6000, 8000, 12000, 20000, 24000}},
+		{6, 44100, {16000, 24000}},
+		{2, 44100, {4000, 6000, 12000, 24000}},
+		{1, 44100, {4000, 6000}},
+		{2, 32000, {4000, 6000}},
+		{1, 32000, {2500}},
+		{2, 22050, {4000}},
+		{1, 22050, {2500}},
+	};
+
+	for (unsigned int i = 0; i < sizeof(rates)/sizeof(*rates); ++i)
+	{
+		if (format->nChannels == rates[i].channel_count
+			&& format->nSamplesPerSec == rates[i].sample_rate)
+		{
+			/* Return the highest valid rate that the input
+			 * sample rate is higher than. If it's lower than the
+			 * lowest valid rate, we return that instead. */
+			uint32_t ret = rates[i].bytes_per_second[0];
+
+			for (unsigned int j = 0; j < 5 && rates[i].bytes_per_second[j]; ++j)
+			{
+				if (format->nAvgBytesPerSec >= rates[i].bytes_per_second[j])
+					ret = rates[i].bytes_per_second[j];
+			}
+
+			if (ret != format->nAvgBytesPerSec)
+				LOG_WARNING(audio, "Fixing up invalid sample rate %u to %u.",
+					format->nAvgBytesPerSec, ret);
+			return ret;
+		}
+	}
+
+	LOG_ERROR(audio, "Unexpected channel count %u, sample rate %u.", format->nChannels, format->nSamplesPerSec);
+	return format->nAvgBytesPerSec;
+}
+
 uint32_t FAudio_CreateSourceVoice(
 	FAudio *audio,
 	FAudioSourceVoice **ppSourceVoice,
@@ -286,6 +367,12 @@ uint32_t FAudio_CreateSourceVoice(
 	LOG_API_ENTER(audio)
 	LOG_FORMAT(audio, pSourceFormat)
 
+	if (pSendList == NULL && audio->master == NULL)
+	{
+		LOG_ERROR(audio, "%s", "CreateSourceVoice called before mastering voice was initialized");
+		return FAUDIO_E_INVALID_CALL;
+	}
+
 	*ppSourceVoice = (FAudioSourceVoice*) audio->pMalloc(sizeof(FAudioVoice));
 	FAudio_zero(*ppSourceVoice, sizeof(FAudioSourceVoice));
 	(*ppSourceVoice)->audio = audio;
@@ -371,6 +458,8 @@ uint32_t FAudio_CreateSourceVoice(
 			fmtex->wfx.nBlockAlign / fmtex->wfx.nChannels
 		) - 6) * 2;
 		(*ppSourceVoice)->src.format = &fmtex->wfx;
+
+		(*ppSourceVoice)->src.samples_per_block = fmtex->wSamplesPerBlock;
 	}
 	else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
 	{
@@ -396,6 +485,8 @@ uint32_t FAudio_CreateSourceVoice(
 		/* Does XAudio2 validate this input?! */
 		fmtex->wfx.cbSize = sizeof(FAudioXMA2WaveFormat) - sizeof(FAudioWaveFormatEx);
 		(*ppSourceVoice)->src.format = &fmtex->wfx;
+
+		(*ppSourceVoice)->src.samples_per_block = fmtex->dwSamplesEncoded / fmtex->wBlockCount;
 	}
 	else
 	{
@@ -414,8 +505,6 @@ uint32_t FAudio_CreateSourceVoice(
 	(*ppSourceVoice)->src.active = 0;
 	(*ppSourceVoice)->src.freqRatio = 1.0f;
 	(*ppSourceVoice)->src.totalSamples = 0;
-	(*ppSourceVoice)->src.bufferList = NULL;
-	(*ppSourceVoice)->src.flushList = NULL;
 	(*ppSourceVoice)->src.bufferLock = FAudio_PlatformCreateMutex();
 	LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->src.bufferLock)
 
@@ -450,6 +539,8 @@ uint32_t FAudio_CreateSourceVoice(
 				FAudio_assert(0 && "Unrecognized wBitsPerSample!");
 			}
 			#undef DECODER
+
+			(*ppSourceVoice)->src.samples_per_block = 1;
 		}
 		else if (COMPARE_GUID(IEEE_FLOAT))
 		{
@@ -468,19 +559,25 @@ uint32_t FAudio_CreateSourceVoice(
 			{
 				(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM32F;
 			}
+
+			(*ppSourceVoice)->src.samples_per_block = 1;
 		}
 		else if (	COMPARE_GUID(WMAUDIO2) ||
 				COMPARE_GUID(WMAUDIO3) ||
 				COMPARE_GUID(WMAUDIO_LOSSLESS)	)
 		{
 #ifdef HAVE_WMADEC
-			if (FAudio_WMADEC_init(*ppSourceVoice, fmtex->SubFormat.Data1) != 0)
+			uint32_t hr;
+
+			fmtex->Format.nAvgBytesPerSec = fixup_wma_bitrate(audio, &fmtex->Format);
+
+			if ((hr = FAudio_WMADEC_init(*ppSourceVoice, fmtex->SubFormat.Data1)))
 			{
-				(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
+				audio->pFree(*ppSourceVoice);
+				return hr;
 			}
 #else
 			FAudio_assert(0 && "xWMA is not supported!");
-			(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
 #endif /* HAVE_WMADEC */
 		}
 		else
@@ -492,13 +589,15 @@ uint32_t FAudio_CreateSourceVoice(
 	else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
 	{
 #ifdef HAVE_WMADEC
-		if (FAudio_WMADEC_init(*ppSourceVoice, FAUDIO_FORMAT_XMAUDIO2) != 0)
+		uint32_t hr;
+
+		if ((hr = FAudio_WMADEC_init(*ppSourceVoice, FAUDIO_FORMAT_XMAUDIO2)))
 		{
-			(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
+			audio->pFree(*ppSourceVoice);
+			return hr;
 		}
 #else
 		FAudio_assert(0 && "XMA2 is not supported!");
-		(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
 #endif /* HAVE_WMADEC */
 	}
 	else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
@@ -599,6 +698,12 @@ uint32_t FAudio_CreateSubmixVoice(
 
 	LOG_API_ENTER(audio)
 
+	if (pSendList == NULL && audio->master == NULL)
+	{
+		LOG_ERROR(audio, "%s", "CreateSubmixVoice called before mastering voice was initialized");
+		return FAUDIO_E_INVALID_CALL;
+	}
+
 	*ppSubmixVoice = (FAudioSubmixVoice*) audio->pMalloc(sizeof(FAudioVoice));
 	FAudio_zero(*ppSubmixVoice, sizeof(FAudioSubmixVoice));
 	(*ppSubmixVoice)->audio = audio;
@@ -1113,7 +1218,7 @@ uint32_t FAudioVoice_SetOutputVoices(
 	const FAudioVoiceSends *pSendList
 ) {
 	uint32_t i;
-	uint32_t outChannels;
+	uint32_t sendRate, nextRate, outChannels;
 	FAudioVoiceSends defaultSends;
 	FAudioSendDescriptor defaultSend;
 
@@ -1125,6 +1230,29 @@ uint32_t FAudioVoice_SetOutputVoices(
 		return FAUDIO_E_INVALID_CALL;
 	}
 
+	if (pSendList != NULL && pSendList->SendCount > 1)
+	{
+		if (pSendList->pSends[0].pOutputVoice->type == FAUDIO_VOICE_SOURCE)
+		{
+			LOG_API_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+		sendRate = (pSendList->pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER) ?
+			pSendList->pSends[0].pOutputVoice->master.inputSampleRate :
+			pSendList->pSends[0].pOutputVoice->mix.inputSampleRate;
+		for (i = 0; i < pSendList->SendCount; i += 1)
+		{
+			nextRate = (pSendList->pSends[i].pOutputVoice->type == FAUDIO_VOICE_MASTER) ?
+				pSendList->pSends[i].pOutputVoice->master.inputSampleRate :
+				pSendList->pSends[i].pOutputVoice->mix.inputSampleRate;
+			if (nextRate != sendRate)
+			{
+				LOG_API_EXIT(voice->audio)
+				return FAUDIO_E_INVALID_CALL;
+			}
+		}
+	}
+
 	FAudio_PlatformLockMutex(voice->sendLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
 
@@ -2347,8 +2475,6 @@ static void destroy_voice(FAudioVoice *voice)
 
 	if (voice->type == FAUDIO_VOICE_SOURCE)
 	{
-		FAudioBufferEntry *entry, *next;
-
 #ifdef FAUDIO_DUMP_VOICES
 		FAudio_DUMPVOICE_Finalize((FAudioSourceVoice*) voice);
 #endif /* FAUDIO_DUMP_VOICES */
@@ -2371,22 +2497,8 @@ static void destroy_voice(FAudioVoice *voice)
 		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
 		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
 
-		entry = voice->src.bufferList;
-		while (entry != NULL)
-		{
-			next = entry->next;
-			voice->audio->pFree(entry);
-			entry = next;
-		}
-
-		entry = voice->src.flushList;
-		while (entry != NULL)
-		{
-			next = entry->next;
-			voice->audio->pFree(entry);
-			entry = next;
-		}
-
+		voice->audio->pFree(voice->src.queued_buffers);
+		voice->audio->pFree(voice->src.flush_buffers);
 		voice->audio->pFree(voice->src.format);
 		LOG_MUTEX_DESTROY(voice->audio, voice->src.bufferLock)
 		FAudio_PlatformDestroyMutex(voice->src.bufferLock);
@@ -2396,6 +2508,7 @@ static void destroy_voice(FAudioVoice *voice)
 			FAudio_WMADEC_free(voice);
 		}
 #endif /* HAVE_WMADEC */
+		voice->audio->pFree(voice->src.unaligned_data);
 	}
 	else if (voice->type == FAUDIO_VOICE_SUBMIX)
 	{
@@ -2609,9 +2722,11 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 	const FAudioBuffer *pBuffer,
 	const FAudioBufferWMA *pBufferWMA
 ) {
-	uint32_t adpcmMask, *adpcmByteCount;
+	const uint32_t samples_per_block = voice->src.samples_per_block;
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+	uint32_t adpcmMask;
 	uint32_t playBegin, playLength, loopBegin, loopLength, bufferLength;
-	FAudioBufferEntry *entry, *list;
+	struct queued_buffer *entry;
 
 	LOG_API_ENTER(voice->audio)
 	LOG_INFO(
@@ -2675,12 +2790,7 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 			voice->src.format->nBlockAlign;
 	}
 
-	/* PlayLength Default */
-	if (playLength == 0)
-	{
-		playLength = bufferLength - playBegin;
-	}
-	else if (playBegin + playLength > bufferLength || playBegin + playLength < playLength)
+	if (playBegin + playLength > bufferLength || playBegin + playLength < playLength)
 	{
 		/* Reading past the end of the buffer, or begin + length overflow uint32_t, which
 		 * would also read past the end of the buffer. */
@@ -2690,25 +2800,34 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 
 	if (pBuffer->LoopCount > 0 && pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)
 	{
-		/* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
-		if (loopBegin >= (playBegin + playLength))
+		uint32_t realPlayLength = playLength;
+		uint32_t realLoopLength = loopLength;
+
+		/* PlayLength Default */
+		if (realPlayLength == 0)
 		{
-			LOG_API_EXIT(voice->audio)
-			return FAUDIO_E_INVALID_CALL;
+			realPlayLength = bufferLength - playBegin;
 		}
 
 		/* LoopLength Default */
-		if (loopLength == 0)
+		if (realLoopLength == 0)
 		{
-			loopLength = playBegin + playLength - loopBegin;
+			realLoopLength = playBegin + realPlayLength - loopBegin;
+		}
+
+		/* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
+		if (loopBegin >= (playBegin + realPlayLength))
+		{
+			LOG_API_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
 		}
 
 		/* "The value of LoopBegin + LoopLength must be greater than PlayBegin
 		 * and less than PlayBegin + PlayLength"
 		 */
 		if (	voice->audio->version > 7 && (
-			(loopBegin + loopLength) <= playBegin ||
-			(loopBegin + loopLength) > (playBegin + playLength))	)
+			(loopBegin + realLoopLength) <= playBegin ||
+			(loopBegin + realLoopLength) > (playBegin + realPlayLength))	)
 		{
 			LOG_API_EXIT(voice->audio)
 			return FAUDIO_E_INVALID_CALL;
@@ -2723,12 +2842,6 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 		playLength -= playLength % adpcmMask;
 		loopBegin -= loopBegin % adpcmMask;
 		loopLength -= loopLength % adpcmMask;
-
-		/* This is basically a const_cast... */
-		adpcmByteCount = (uint32_t*) &pBuffer->AudioBytes;
-		*adpcmByteCount = (
-			pBuffer->AudioBytes / voice->src.format->nBlockAlign
-		) * voice->src.format->nBlockAlign;
 	}
 	else if (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
 	{
@@ -2737,8 +2850,14 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 		loopLength = playBegin + playLength;
 	}
 
-	/* Allocate, now that we have valid input */
-	entry = (FAudioBufferEntry*) voice->audio->pMalloc(sizeof(FAudioBufferEntry));
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	array_reserve(voice->audio, (void **)&voice->src.queued_buffers, &voice->src.queued_buffers_capacity,
+		voice->src.queued_buffer_count + 1, sizeof(*voice->src.queued_buffers));
+
+	entry = &voice->src.queued_buffers[voice->src.queued_buffer_count++];
+	FAudio_memset(entry, 0, sizeof(*entry));
 	FAudio_memcpy(&entry->buffer, pBuffer, sizeof(FAudioBuffer));
 	entry->buffer.PlayBegin = playBegin;
 	entry->buffer.PlayLength = playLength;
@@ -2748,10 +2867,24 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 	{
 		FAudio_memcpy(&entry->bufferWMA, pBufferWMA, sizeof(FAudioBufferWMA));
 	}
-	entry->next = NULL;
+	else
+	{
+		if (playLength)
+			entry->play_bytes = playLength / samples_per_block * block_size;
+		else
+			entry->play_bytes = pBuffer->AudioBytes - (playBegin / samples_per_block * block_size);
+
+		if (loopLength)
+			entry->loop_bytes = loopLength / samples_per_block * block_size;
+		else
+			entry->loop_bytes = entry->play_bytes
+				+ (playBegin / samples_per_block * block_size)
+				- (loopBegin / samples_per_block * block_size);
+	}
 
 	if (	voice->audio->version <= 7 && (
 		entry->buffer.LoopCount > 0 &&
+		entry->buffer.LoopLength &&
 		entry->buffer.LoopBegin + entry->buffer.LoopLength <= entry->buffer.PlayBegin))
 	{
 		entry->buffer.LoopCount = 0;
@@ -2759,36 +2892,15 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 
 #ifdef FAUDIO_DUMP_VOICES
 	/* dumping current buffer, append into "data" section */
-	if (pBuffer->pAudioData != NULL && playLength > 0)
+	if (pBuffer->pAudioData && entry->play_bytes)
 	{
-		FAudio_DUMPVOICE_WriteBuffer(voice, pBuffer, pBufferWMA, playBegin, playLength);
+		FAudio_DUMPVOICE_WriteBuffer(voice, pBuffer, pBufferWMA, entry->play_bytes);
 	}
 #endif /* FAUDIO_DUMP_VOICES */
 
-	/* Submit! */
-	FAudio_PlatformLockMutex(voice->src.bufferLock);
-	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-	if (voice->src.bufferList == NULL)
-	{
-		voice->src.bufferList = entry;
+	if (voice->src.queued_buffer_count == 1)
 		voice->src.curBufferOffset = entry->buffer.PlayBegin;
-		voice->src.newBuffer = 1;
-	}
-	else
-	{
-		list = voice->src.bufferList;
-		while (list->next != NULL)
-		{
-			list = list->next;
-		}
-		list->next = entry;
 
-		/* For some bizarre reason we get scenarios where a buffer is freed, only to
-		 * have the allocator give us the exact same address and somehow get a single
-		 * buffer referencing itself. I don't even know.
-		 */
-		FAudio_assert(list != entry);
-	}
 	LOG_INFO(
 		voice->audio,
 		"%p: appended buffer %p",
@@ -2804,7 +2916,7 @@ uint32_t FAudioSourceVoice_SubmitSourceBuffer(
 uint32_t FAudioSourceVoice_FlushSourceBuffers(
 	FAudioSourceVoice *voice
 ) {
-	FAudioBufferEntry *entry, *latest;
+	size_t offset = 0;
 
 	LOG_API_ENTER(voice->audio)
 	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
@@ -2812,38 +2924,30 @@ uint32_t FAudioSourceVoice_FlushSourceBuffers(
 	FAudio_PlatformLockMutex(voice->src.bufferLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
 
+	array_reserve(voice->audio, (void **)&voice->src.flush_buffers, &voice->src.flush_buffers_capacity,
+		voice->src.flush_buffer_count + voice->src.queued_buffer_count, sizeof(*voice->src.flush_buffers));
+
 	/* If the source is playing, don't flush the active buffer */
-	entry = voice->src.bufferList;
-	if ((voice->src.active == 1) && entry != NULL && !voice->src.newBuffer)
+	if (voice->src.active == 1 && voice->src.queued_buffer_count > 0
+		&& voice->src.queued_buffers[0].sent_OnStartBuffer)
 	{
-		entry = entry->next;
-		voice->src.bufferList->next = NULL;
+		offset = 1;
 	}
 	else
 	{
 		voice->src.curBufferOffset = 0;
-		voice->src.bufferList = NULL;
-		voice->src.newBuffer = 0;
 	}
 
-	/* Move them to the pending flush list */
-	if (entry != NULL)
+	if (voice->src.queued_buffer_count > offset)
 	{
-		if (voice->src.flushList == NULL)
-		{
-			voice->src.flushList = entry;
-		}
-		else
-		{
-			latest = voice->src.flushList;
-			while (latest->next != NULL)
-			{
-				latest = latest->next;
-			}
-			latest->next = entry;
-		}
+		FAudio_memcpy(voice->src.flush_buffers + voice->src.flush_buffer_count,
+			voice->src.queued_buffers + offset,
+			(voice->src.queued_buffer_count - offset) * sizeof(*voice->src.flush_buffers));
 	}
 
+	voice->src.flush_buffer_count += voice->src.queued_buffer_count - offset;
+	voice->src.queued_buffer_count = offset;
+
 	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
 	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
 	LOG_API_EXIT(voice->audio)
@@ -2853,19 +2957,14 @@ uint32_t FAudioSourceVoice_FlushSourceBuffers(
 uint32_t FAudioSourceVoice_Discontinuity(
 	FAudioSourceVoice *voice
 ) {
-	FAudioBufferEntry *buf;
-
 	LOG_API_ENTER(voice->audio)
 	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
 
 	FAudio_PlatformLockMutex(voice->src.bufferLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
 
-	if (voice->src.bufferList != NULL)
-	{
-		for (buf = voice->src.bufferList; buf->next != NULL; buf = buf->next);
-		buf->buffer.Flags |= FAUDIO_END_OF_STREAM;
-	}
+	if (voice->src.queued_buffer_count)
+		voice->src.queued_buffers[voice->src.queued_buffer_count - 1].buffer.Flags |= FAUDIO_END_OF_STREAM;
 
 	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
 	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
@@ -2894,10 +2993,8 @@ uint32_t FAudioSourceVoice_ExitLoop(
 	FAudio_PlatformLockMutex(voice->src.bufferLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
 
-	if (voice->src.bufferList != NULL)
-	{
-		voice->src.bufferList->buffer.LoopCount = 0;
-	}
+	if (voice->src.queued_buffer_count)
+		voice->src.queued_buffers[0].buffer.LoopCount = 0;
 
 	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
 	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
@@ -2910,8 +3007,6 @@ void FAudioSourceVoice_GetState(
 	FAudioVoiceState *pVoiceState,
 	uint32_t Flags
 ) {
-	FAudioBufferEntry *entry;
-
 	LOG_API_ENTER(voice->audio)
 	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
 
@@ -2925,27 +3020,13 @@ void FAudioSourceVoice_GetState(
 
 	pVoiceState->BuffersQueued = 0;
 	pVoiceState->pCurrentBufferContext = NULL;
-	if (voice->src.bufferList != NULL)
-	{
-		entry = voice->src.bufferList;
-		if (!voice->src.newBuffer)
-		{
-			pVoiceState->pCurrentBufferContext = entry->buffer.pContext;
-		}
-		do
-		{
-			pVoiceState->BuffersQueued += 1;
-			entry = entry->next;
-		} while (entry != NULL);
-	}
+
+	if (voice->src.queued_buffer_count)
+		pVoiceState->pCurrentBufferContext = voice->src.queued_buffers[0].buffer.pContext;
+	pVoiceState->BuffersQueued += voice->src.queued_buffer_count;
 
 	/* Pending flushed buffers also count */
-	entry = voice->src.flushList;
-	while (entry != NULL)
-	{
-		pVoiceState->BuffersQueued += 1;
-		entry = entry->next;
-	}
+	pVoiceState->BuffersQueued += voice->src.flush_buffer_count;
 
 	LOG_INFO(
 		voice->audio,
@@ -3018,8 +3099,7 @@ uint32_t FAudioSourceVoice_SetSourceSampleRate(
 
 	FAudio_PlatformLockMutex(voice->src.bufferLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-	if (	voice->audio->version > 7 &&
-		voice->src.bufferList != NULL	)
+	if (voice->audio->version > 7 && voice->src.queued_buffer_count > 0)
 	{
 		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
 		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
@@ -3341,8 +3421,7 @@ static void FAudio_DUMPVOICE_WriteBuffer(
 	const FAudioSourceVoice *voice,
 	const FAudioBuffer *pBuffer,
 	const FAudioBufferWMA *pBufferWMA,
-	const uint32_t playBegin,
-	const uint32_t playLength
+	const uint32_t size
 ) {
 	FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, voice->src.format, "ab", "data");
 	if (io_data == NULL)
@@ -3374,8 +3453,8 @@ static void FAudio_DUMPVOICE_WriteBuffer(
 		/* dump unencoded buffer contents */
 		uint16_t bytesPerFrame = (voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
 		FAudio_assert(bytesPerFrame > 0);
-		const void *pAudioDataBegin = pBuffer->pAudioData + playBegin*bytesPerFrame;
-		io_data->write(io_data->data, pAudioDataBegin, bytesPerFrame, playLength);
+		const void *pAudioDataBegin = pBuffer->pAudioData + pBuffer->PlayBegin * bytesPerFrame;
+		io_data->write(io_data->data, pAudioDataBegin, 1, size);
 	}
 	FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
 	FAudio_close_out(io_data);
diff --git a/libs/faudio/src/FAudio_internal.c b/libs/faudio/src/FAudio_internal.c
index 37609ae8886a..83fce9c399f0 100644
--- a/libs/faudio/src/FAudio_internal.c
+++ b/libs/faudio/src/FAudio_internal.c
@@ -163,6 +163,33 @@ void FAudio_INTERNAL_debug_fmt(
 }
 #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
 
+bool array_reserve(FAudio *audio, void **elements, size_t *capacity, size_t count, size_t size)
+{
+    unsigned int new_capacity, max_capacity;
+    void *new_elements;
+
+    if (count <= *capacity)
+        return true;
+
+    max_capacity = ~(size_t)0 / size;
+    if (count > max_capacity)
+        return false;
+
+    new_capacity = FAudio_max(4, *capacity);
+    while (new_capacity < count && new_capacity <= max_capacity / 2)
+        new_capacity *= 2;
+    if (new_capacity < count)
+        new_capacity = max_capacity;
+
+    if (!(new_elements = audio->pRealloc(*elements, new_capacity * size)))
+        return false;
+
+    *elements = new_elements;
+    *capacity = new_capacity;
+
+    return true;
+}
+
 void LinkedList_AddEntry(
 	LinkedList **start,
 	void* toAdd,
@@ -299,10 +326,10 @@ static uint32_t FAudio_INTERNAL_GetBytesRequested(
 	FAudioSourceVoice *voice,
 	uint32_t decoding
 ) {
-	uint32_t end, result;
-	FAudioBuffer *buffer;
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+	const uint32_t samples_per_block = voice->src.samples_per_block;
+	uint32_t result = (decoding * block_size / samples_per_block);
 	FAudioWaveFormatExtensible *fmt;
-	FAudioBufferEntry *list = voice->src.bufferList;
 
 	LOG_FUNC_ENTER(voice->audio)
 
@@ -314,321 +341,416 @@ static uint32_t FAudio_INTERNAL_GetBytesRequested(
 		return 0;
 	}
 #endif /* HAVE_WMADEC */
-	while (list != NULL && decoding > 0)
+
+	for (size_t i = 0; i < voice->src.queued_buffer_count; ++i)
 	{
-		buffer = &list->buffer;
-		if (buffer->LoopCount > 0)
+		const struct queued_buffer *buffer = &voice->src.queued_buffers[i];
+		uint32_t size = 0;
+
+		if (buffer->buffer.LoopCount > 0)
+			size += buffer->loop_bytes * buffer->buffer.LoopCount;
+		size += buffer->play_bytes;
+
+		size -= voice->src.curBufferOffset * block_size / samples_per_block;
+
+		if (size > result)
 		{
-			end = (
-				/* Current loop... */
-				((buffer->LoopBegin + buffer->LoopLength) - voice->src.curBufferOffset) +
-				/* Remaining loops... */
-				(buffer->LoopLength * buffer->LoopCount - 1) +
-				/* ... Final iteration */
-				buffer->PlayLength
-			);
+			LOG_FUNC_EXIT(voice->audio)
+			return 0;
+		}
+		result -= size;
+	}
+
+	LOG_FUNC_EXIT(voice->audio)
+	return result;
+}
+
+static uint32_t buffer_get_end(FAudioSourceVoice *voice, const struct queued_buffer *buffer)
+{
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+	const uint32_t samples_per_block = voice->src.samples_per_block;
+
+#ifdef HAVE_WMADEC
+	if (voice->src.wmadec)
+	{
+		uint32_t buffer_size;
+
+		if (voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
+		{
+			FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat *)voice->src.format;
+			buffer_size = fmtex->dwSamplesEncoded;
 		}
 		else
 		{
-			end = (buffer->PlayBegin + buffer->PlayLength) - voice->src.curBufferOffset;
+			buffer_size = buffer->bufferWMA.pDecodedPacketCumulativeBytes[buffer->bufferWMA.PacketCount - 1] /
+				(voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
 		}
-		if (end > decoding)
+
+		if (buffer->buffer.LoopCount)
 		{
-			decoding = 0;
-			break;
+			if (buffer->buffer.LoopLength)
+				return buffer->buffer.LoopBegin + buffer->buffer.LoopLength;
+			return buffer_size;
 		}
-		decoding -= end;
-		list = list->next;
+
+		if (buffer->buffer.PlayLength)
+			return buffer->buffer.PlayBegin + buffer->buffer.PlayLength;
+		return buffer_size;
 	}
+#endif
+
+	if (buffer->buffer.LoopCount)
+		return buffer->buffer.LoopBegin + ((buffer->loop_bytes - buffer->first_block_offset) / block_size * samples_per_block);
+
+	return buffer->buffer.PlayBegin + ((buffer->play_bytes - buffer->first_block_offset) / block_size * samples_per_block);
+}
+
+/* If there is any leftover unaligned data at the end of this buffer, stash it
+ * stash it in the voice.
+ * We will later combine it with the beginning of the next buffer. */
+static void save_unaligned_end_data(FAudioSourceVoice *voice, const struct queued_buffer *buffer)
+{
+	const uint32_t samples_per_block = voice->src.samples_per_block;
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+	uint32_t byte_pos, end_pos;
+
+#ifdef HAVE_WMADEC
+	if (voice->src.wmadec)
+		return;
+#endif
+
+	byte_pos = buffer->first_block_offset;
+	byte_pos += voice->src.curBufferOffset / samples_per_block * block_size;
 
-	/* Convert samples to bytes, factoring block alignment */
-	if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
+	if (buffer->buffer.LoopCount)
 	{
-		fmt = (FAudioWaveFormatExtensible*) voice->src.format;
-		result = (
-			(decoding / fmt->Samples.wSamplesPerBlock) +
-			((decoding % fmt->Samples.wSamplesPerBlock) > 0)
-		) * voice->src.format->nBlockAlign;
+		end_pos = buffer->buffer.LoopBegin / samples_per_block * block_size;
+		end_pos += buffer->loop_bytes;
 	}
 	else
 	{
-		result = decoding * voice->src.format->nBlockAlign;
+		end_pos = buffer->buffer.PlayBegin / samples_per_block * block_size;
+		end_pos += buffer->play_bytes;
 	}
 
-	LOG_FUNC_EXIT(voice->audio)
-	return result;
+	if (byte_pos == end_pos)
+		return;
+
+	/* If the last buffer was also unaligned, and this one
+	 * wasn't big enough to complete the block, then we'll
+	 * already have some unaligned bytes. If this one was
+	 * big enough to complete the block, we shouldn't have
+	 * any bytes left over from the last buffer. */
+	FAudio_assert(voice->src.unaligned_size + (end_pos - byte_pos) < block_size);
+
+	if (!voice->src.unaligned_data)
+		voice->src.unaligned_data = voice->audio->pMalloc(block_size);
+	FAudio_memcpy(voice->src.unaligned_data + voice->src.unaligned_size,
+		buffer->buffer.pAudioData + byte_pos, end_pos - byte_pos);
+	voice->src.unaligned_size += (end_pos - byte_pos);
+}
+
+static void start_buffer(FAudioSourceVoice *voice, struct queued_buffer *buffer)
+{
+	if (!buffer->sent_OnStartBuffer)
+	{
+		buffer->sent_OnStartBuffer = true;
+
+		if (	!buffer->internal &&
+			voice->src.callback != NULL &&
+			voice->src.callback->OnBufferStart != NULL	)
+		{
+			FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+			FAudio_PlatformUnlockMutex(voice->sendLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+			FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+			voice->src.callback->OnBufferStart(
+				voice->src.callback,
+				buffer->buffer.pContext
+			);
+
+			FAudio_PlatformLockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+			FAudio_PlatformLockMutex(voice->sendLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+			FAudio_PlatformLockMutex(voice->src.bufferLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+		}
+	}
+}
+
+static void end_buffer(FAudioSourceVoice *voice)
+{
+	struct queued_buffer *buffer = &voice->src.queued_buffers[0];
+	bool eos = buffer->buffer.Flags & FAUDIO_END_OF_STREAM;
+	FAudioVoiceCallback *callback = voice->src.callback;
+	void *context = buffer->buffer.pContext;
+	bool internal = buffer->internal;
+
+	save_unaligned_end_data(voice, buffer);
+
+	if (buffer->buffer.LoopCount > 0)
+	{
+		voice->src.curBufferOffset = buffer->buffer.LoopBegin;
+		if (buffer->buffer.LoopCount < FAUDIO_LOOP_INFINITE)
+			--buffer->buffer.LoopCount;
+
+		if (callback && callback->OnLoopEnd)
+		{
+			FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+			FAudio_PlatformUnlockMutex(voice->sendLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+			FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+			callback->OnLoopEnd(callback, context);
+
+			FAudio_PlatformLockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+			FAudio_PlatformLockMutex(voice->sendLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+			FAudio_PlatformLockMutex(voice->src.bufferLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+		}
+
+		buffer->first_block_offset = 0;
+		return;
+	}
+
+#ifdef HAVE_WMADEC
+	if (voice->src.wmadec)
+		FAudio_WMADEC_end_buffer(voice);
+#endif /* HAVE_WMADEC */
+
+	if (eos)
+	{
+		voice->src.curBufferOffsetDec = 0;
+		voice->src.totalSamples = 0;
+	}
+
+	LOG_INFO(voice->audio, "Voice %p, finished with buffer %p", voice, buffer)
+
+	if (buffer->internal)
+		voice->audio->pFree((void *)buffer->buffer.pAudioData);
+
+	FAudio_memmove(&voice->src.queued_buffers[0], &voice->src.queued_buffers[1],
+		(voice->src.queued_buffer_count - 1) * sizeof(*voice->src.queued_buffers));
+	--voice->src.queued_buffer_count;
+
+	if (voice->src.queued_buffer_count)
+		voice->src.curBufferOffset = voice->src.queued_buffers[0].buffer.PlayBegin;
+
+	if (callback && !internal)
+	{
+		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+		if (callback->OnBufferEnd)
+			callback->OnBufferEnd(callback, context);
+
+		if (eos && callback->OnStreamEnd)
+			callback->OnStreamEnd(callback);
+
+		FAudio_PlatformLockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+		FAudio_PlatformLockMutex(voice->sendLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+		FAudio_PlatformLockMutex(voice->src.bufferLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+	}
+}
+
+/* If we have saved unaligned data, and the next buffer has enough data to
+ * complete the block, then turn it into an internal queued buffer entry
+ * and offset the next buffer's byte position accordingly. */
+static void try_collect_unaligned_data(FAudioSourceVoice *voice)
+{
+	const uint32_t samples_per_block = voice->src.samples_per_block;
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+	struct queued_buffer *buffer;
+	uint32_t begin_bytes;
+
+	if (!voice->src.queued_buffer_count)
+		return;
+	buffer = &voice->src.queued_buffers[0];
+	if (!voice->src.unaligned_size)
+		return;
+
+	if (buffer->buffer.LoopCount)
+	{
+		/* If there's not enough data to complete the block, the buffer
+		 * will be immediately ended in end_buffer(), and its data
+		 * appended to the unaligned data we already have. */
+		if (voice->src.unaligned_size + buffer->loop_bytes < block_size)
+			return;
+		begin_bytes = buffer->buffer.LoopBegin / samples_per_block * block_size;
+	}
+	else
+	{
+		if (voice->src.unaligned_size + buffer->play_bytes < block_size)
+			return;
+		begin_bytes = buffer->buffer.PlayBegin / samples_per_block * block_size;
+	}
+
+	buffer->first_block_offset = block_size - voice->src.unaligned_size;
+	FAudio_memcpy(voice->src.unaligned_data + voice->src.unaligned_size,
+		buffer->buffer.pAudioData + begin_bytes, buffer->first_block_offset);
+
+	/* Put this data into a new internal buffer. */
+	array_reserve(voice->audio, (void **)&voice->src.queued_buffers, &voice->src.queued_buffers_capacity,
+		voice->src.queued_buffer_count + 1, sizeof(*voice->src.queued_buffers));
+	FAudio_memmove(&voice->src.queued_buffers[1], &voice->src.queued_buffers[0],
+		voice->src.queued_buffer_count * sizeof(*voice->src.queued_buffers));
+	++voice->src.queued_buffer_count;
+
+	buffer = &voice->src.queued_buffers[0];
+	FAudio_memset(buffer, 0, sizeof(*buffer));
+	buffer->buffer.pAudioData = voice->src.unaligned_data;
+	buffer->internal = true;
+	buffer->play_bytes = block_size;
+
+	voice->src.curBufferOffset = 0;
+
+	/* Reset the unaligned size for next time. */
+	voice->src.unaligned_data = NULL;
+	voice->src.unaligned_size = 0;
 }
 
 static void FAudio_INTERNAL_DecodeBuffers(
 	FAudioSourceVoice *voice,
 	uint64_t *toDecode
 ) {
-	uint32_t end, endRead, decoding, decoded = 0;
-	FAudioBuffer *buffer = &voice->src.bufferList->buffer;
-	FAudioBufferEntry *toDelete;
+	const uint32_t samples_per_block = voice->src.samples_per_block;
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+	uint32_t decoded = 0;
 
 	LOG_FUNC_ENTER(voice->audio)
 
 	/* This should never go past the max ratio size */
 	FAudio_assert(*toDecode <= voice->src.decodeSamples);
 
-	while (decoded < *toDecode && buffer != NULL)
+	while (decoded < *toDecode && voice->src.queued_buffer_count)
 	{
-		decoding = (uint32_t) *toDecode - decoded;
-
-		/* Start-of-buffer behavior */
-		if (voice->src.newBuffer)
-		{
-			voice->src.newBuffer = 0;
-			if (	voice->src.callback != NULL &&
-				voice->src.callback->OnBufferStart != NULL	)
-			{
-				FAudio_PlatformUnlockMutex(voice->src.bufferLock);
-				LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
-
-				FAudio_PlatformUnlockMutex(voice->sendLock);
-				LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		float *dst = voice->audio->decodeCache + (decoded * voice->src.format->nChannels);
+		struct queued_buffer *buffer = &voice->src.queued_buffers[0];
+		uint32_t decode_count;
 
-				FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
-				LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+		try_collect_unaligned_data(voice);
 
-				voice->src.callback->OnBufferStart(
-					voice->src.callback,
-					buffer->pContext
-				);
-
-				FAudio_PlatformLockMutex(voice->audio->sourceLock);
-				LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+		/* Start-of-buffer behavior */
+		start_buffer(voice, buffer);
 
-				FAudio_PlatformLockMutex(voice->sendLock);
-				LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+		/* Number of samples we are decoding in one call. */
+		decode_count = FAudio_min(*toDecode - decoded,
+			buffer_get_end(voice, buffer) - voice->src.curBufferOffset);
 
-				FAudio_PlatformLockMutex(voice->src.bufferLock);
-				LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-			}
+#ifdef HAVE_WMADEC
+		if (voice->src.wmadec)
+		{
+			decode_wma(voice, buffer, dst, decode_count);
 		}
+		else
+#endif
+		{
+			uint32_t block_offset = voice->src.curBufferOffset % samples_per_block;
+			const uint8_t *src = buffer->buffer.pAudioData + buffer->first_block_offset;
 
-		/* Check for end-of-buffer */
-		end = (buffer->LoopCount > 0) ?
-			(buffer->LoopBegin + buffer->LoopLength) :
-			buffer->PlayBegin + buffer->PlayLength;
-		endRead = FAudio_min(
-			end - voice->src.curBufferOffset,
-			decoding
-		);
+			src += (voice->src.curBufferOffset / samples_per_block) * block_size;
 
-		/* Decode... */
-		voice->src.decode(
-			voice,
-			buffer,
-			voice->audio->decodeCache + (
-				decoded * voice->src.format->nChannels
-			),
-			endRead
-		);
+			voice->src.decode(voice, src, dst, block_offset, decode_count);
+		}
 
 		LOG_INFO(
 			voice->audio,
 			"Voice %p, buffer %p, decoded %u samples from [%u,%u)",
 			(void*) voice,
 			(void*) buffer,
-			endRead,
+			decode_count,
 			voice->src.curBufferOffset,
-			voice->src.curBufferOffset + endRead
+			voice->src.curBufferOffset + decode_count
 		)
 
-		decoded += endRead;
-		voice->src.curBufferOffset += endRead;
-		voice->src.totalSamples += endRead;
+		decoded += decode_count;
+		voice->src.curBufferOffset += decode_count;
+		voice->src.totalSamples += decode_count;
 
 		/* End-of-buffer behavior */
-		if (endRead < decoding)
-		{
-			if (buffer->LoopCount > 0)
-			{
-				voice->src.curBufferOffset = buffer->LoopBegin;
-				if (buffer->LoopCount < FAUDIO_LOOP_INFINITE)
-				{
-					buffer->LoopCount -= 1;
-				}
-				if (	voice->src.callback != NULL &&
-					voice->src.callback->OnLoopEnd != NULL	)
-				{
-					FAudio_PlatformUnlockMutex(voice->src.bufferLock);
-					LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
-
-					FAudio_PlatformUnlockMutex(voice->sendLock);
-					LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		if (decoded < *toDecode)
+			end_buffer(voice);
+	}
 
-					FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
-					LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+	/* ... FIXME: I keep going past the buffer so fuck it */
 
-					voice->src.callback->OnLoopEnd(
-						voice->src.callback,
-						buffer->pContext
-					);
+	if (decoded < *toDecode)
+	{
+		FAudio_zero(
+			voice->audio->decodeCache + (
+				decoded *
+				voice->src.format->nChannels
+			),
+			sizeof(float) * (
+				(*toDecode - decoded) *
+				voice->src.format->nChannels
+			)
+		);
+	}
 
-					FAudio_PlatformLockMutex(voice->audio->sourceLock);
-					LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+	if (voice->src.queued_buffer_count)
+	{
+		float *dst = voice->audio->decodeCache + (decoded * voice->src.format->nChannels);
+		struct queued_buffer *buffer = &voice->src.queued_buffers[0];
+		uint32_t decode_count;
 
-					FAudio_PlatformLockMutex(voice->sendLock);
-					LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+		/* Number of samples we are decoding in one call. */
+		decode_count = FAudio_min(EXTRA_DECODE_PADDING,
+			buffer_get_end(voice, buffer) - voice->src.curBufferOffset);
 
-					FAudio_PlatformLockMutex(voice->src.bufferLock);
-					LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-				}
-			}
-			else
-			{
 #ifdef HAVE_WMADEC
-				if (voice->src.wmadec != NULL)
-				{
-					FAudio_WMADEC_end_buffer(voice);
-				}
-#endif /* HAVE_WMADEC */
-				/* For EOS we can stop storing fraction offsets */
-				if (buffer->Flags & FAUDIO_END_OF_STREAM)
-				{
-					voice->src.curBufferOffsetDec = 0;
-					voice->src.totalSamples = 0;
-				}
-
-				LOG_INFO(
-					voice->audio,
-					"Voice %p, finished with buffer %p",
-					(void*) voice,
-					(void*) buffer
-				)
-
-				/* Change active buffer, delete finished buffer */
-				toDelete = voice->src.bufferList;
-				voice->src.bufferList = voice->src.bufferList->next;
-				if (voice->src.bufferList != NULL)
-				{
-					buffer = &voice->src.bufferList->buffer;
-					voice->src.curBufferOffset = buffer->PlayBegin;
-				}
-				else
-				{
-					buffer = NULL;
-
-					/* FIXME: I keep going past the buffer so fuck it */
-					FAudio_zero(
-						voice->audio->decodeCache + (
-							decoded *
-							voice->src.format->nChannels
-						),
-						sizeof(float) * (
-							(*toDecode - decoded) *
-							voice->src.format->nChannels
-						)
-					);
-				}
+		if (voice->src.wmadec)
+		{
+			decode_wma(voice, buffer, dst, decode_count);
+		}
+		else
+#endif
+		{
+			uint32_t block_offset = voice->src.curBufferOffset % samples_per_block;
+			const uint8_t *src = buffer->buffer.pAudioData;
 
-				/* Callbacks */
-				if (voice->src.callback != NULL)
-				{
-					FAudio_PlatformUnlockMutex(voice->src.bufferLock);
-					LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
-
-					FAudio_PlatformUnlockMutex(voice->sendLock);
-					LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
-
-					FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
-					LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
-
-					if (voice->src.callback->OnBufferEnd != NULL)
-					{
-						voice->src.callback->OnBufferEnd(
-							voice->src.callback,
-							toDelete->buffer.pContext
-						);
-					}
-					if (	toDelete->buffer.Flags & FAUDIO_END_OF_STREAM &&
-						voice->src.callback->OnStreamEnd != NULL	)
-					{
-						voice->src.callback->OnStreamEnd(
-							voice->src.callback
-						);
-					}
-
-					FAudio_PlatformLockMutex(voice->audio->sourceLock);
-					LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
-
-					FAudio_PlatformLockMutex(voice->sendLock);
-					LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
-
-					FAudio_PlatformLockMutex(voice->src.bufferLock);
-					LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-
-					/* One last chance at redemption */
-					if (buffer == NULL && voice->src.bufferList != NULL)
-					{
-						buffer = &voice->src.bufferList->buffer;
-						voice->src.curBufferOffset = buffer->PlayBegin;
-					}
-
-					if (buffer != NULL && voice->src.callback->OnBufferStart != NULL)
-					{
-						FAudio_PlatformUnlockMutex(voice->src.bufferLock);
-						LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
-
-						FAudio_PlatformUnlockMutex(voice->sendLock);
-						LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
-
-						FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
-						LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
-
-						voice->src.callback->OnBufferStart(
-							voice->src.callback,
-							buffer->pContext
-						);
-
-						FAudio_PlatformLockMutex(voice->audio->sourceLock);
-						LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
-
-						FAudio_PlatformLockMutex(voice->sendLock);
-						LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
-
-						FAudio_PlatformLockMutex(voice->src.bufferLock);
-						LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-					}
-				}
+			src += (voice->src.curBufferOffset / samples_per_block) * block_size;
 
-				voice->audio->pFree(toDelete);
-			}
+			voice->src.decode(voice, src, dst, block_offset, decode_count);
 		}
-	}
-
-	/* ... FIXME: I keep going past the buffer so fuck it */
-	if (buffer)
-	{
-		end = (buffer->LoopCount > 0) ?
-			(buffer->LoopBegin + buffer->LoopLength) :
-			buffer->PlayBegin + buffer->PlayLength;
-		endRead = FAudio_min(
-			end - voice->src.curBufferOffset,
-			EXTRA_DECODE_PADDING
-		);
 
-		voice->src.decode(
-			voice,
-			buffer,
-			voice->audio->decodeCache + (
-				decoded * voice->src.format->nChannels
-			),
-			endRead
-		);
 		/* Do NOT increment curBufferOffset! */
 
-		if (endRead < EXTRA_DECODE_PADDING)
+		if (decode_count < EXTRA_DECODE_PADDING)
 		{
 			FAudio_zero(
 				voice->audio->decodeCache + (
 					decoded * voice->src.format->nChannels
 				),
 				sizeof(float) * (
-					(EXTRA_DECODE_PADDING - endRead) *
+					(EXTRA_DECODE_PADDING - decode_count) *
 					voice->src.format->nChannels
 				)
 			);
@@ -896,7 +1018,7 @@ static void FAudio_INTERNAL_MixSource(FAudioSourceVoice *voice)
 	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
 
 	/* Nothing to do? */
-	if (voice->src.bufferList == NULL)
+	if (!voice->src.queued_buffer_count)
 	{
 		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
 		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
@@ -1025,7 +1147,7 @@ static void FAudio_INTERNAL_MixSource(FAudioSourceVoice *voice)
 	}
 
 	/* Update buffer offsets */
-	if (voice->src.bufferList != NULL)
+	if (voice->src.queued_buffer_count)
 	{
 		/* Increment fixed offset by resample size, int to fixed... */
 		voice->src.curBufferOffsetDec += toResample * voice->src.resampleStep;
@@ -1291,39 +1413,43 @@ end:
 
 static void FAudio_INTERNAL_FlushPendingBuffers(FAudioSourceVoice *voice)
 {
-	FAudioBufferEntry *entry;
-
 	FAudio_PlatformLockMutex(voice->src.bufferLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
 
+	if (voice->src.callback == NULL || voice->src.callback->OnBufferEnd == NULL)
+	{
+		/* We can skip the memory churn below if nobody's looking */
+		voice->src.flush_buffer_count = 0;
+	}
+
 	/* Remove pending flushed buffers and send an event for each one */
-	while (voice->src.flushList != NULL)
+	else while (voice->src.flush_buffer_count > 0)
 	{
-		entry = voice->src.flushList;
-		voice->src.flushList = voice->src.flushList->next;
+		void* pContext = voice->src.flush_buffers[0].buffer.pContext;
 
-		if (voice->src.callback != NULL && voice->src.callback->OnBufferEnd != NULL)
-		{
-			FAudio_PlatformUnlockMutex(voice->src.bufferLock);
-			LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+		/* Subtract each one instead of setting 0 at the end; this is
+		 * needed to make GetState accurate inside this callback
+		 */
+		voice->src.flush_buffer_count -= 1;
+		FAudio_memmove(&voice->src.flush_buffers[0], &voice->src.flush_buffers[1],
+			voice->src.flush_buffer_count * sizeof(*voice->src.flush_buffers));
 
-			FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
-			LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
 
-			voice->src.callback->OnBufferEnd(
-				voice->src.callback,
-				entry->buffer.pContext
-			);
+		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
 
-			FAudio_PlatformLockMutex(voice->audio->sourceLock);
-			LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+		voice->src.callback->OnBufferEnd(voice->src.callback, pContext);
 
-			FAudio_PlatformLockMutex(voice->src.bufferLock);
-			LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
-		}
-		voice->audio->pFree(entry);
+		FAudio_PlatformLockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+		FAudio_PlatformLockMutex(voice->src.bufferLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
 	}
 
+
 	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
 	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
 }
@@ -1574,6 +1700,7 @@ void FAudio_INTERNAL_FreeEffectChain(FAudioVoice *voice)
 	{
 		voice->effects.desc[i].pEffect->UnlockForProcess(voice->effects.desc[i].pEffect);
 		voice->effects.desc[i].pEffect->Release(voice->effects.desc[i].pEffect);
+		voice->audio->pFree(voice->effects.parameters[i]);
 	}
 
 	voice->audio->pFree(voice->effects.desc);
@@ -1597,6 +1724,7 @@ uint32_t FAudio_INTERNAL_VoiceOutputFrequency(
 	if ((pSendList == NULL) || (pSendList->SendCount == 0))
 	{
 		/* When we're deliberately given no sends, use master rate! */
+		FAudio_assert(voice->audio->master != NULL);
 		outSampleRate = voice->audio->master->master.inputSampleRate;
 	}
 	else
@@ -1664,54 +1792,30 @@ const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64] =
 
 /* PCM Decoding */
 
-void FAudio_INTERNAL_DecodePCM8(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void FAudio_INTERNAL_DecodePCM8(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
 	LOG_FUNC_ENTER(voice->audio)
-	FAudio_INTERNAL_Convert_U8_To_F32(
-		((uint8_t*) buffer->pAudioData) + (
-			voice->src.curBufferOffset * voice->src.format->nChannels
-		),
-		decodeCache,
-		samples * voice->src.format->nChannels
-	);
+	FAudio_INTERNAL_Convert_U8_To_F32(src, decodeCache, samples * voice->src.format->nChannels);
 	LOG_FUNC_EXIT(voice->audio)
 }
 
-void FAudio_INTERNAL_DecodePCM16(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void FAudio_INTERNAL_DecodePCM16(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
 	LOG_FUNC_ENTER(voice->audio)
-	FAudio_INTERNAL_Convert_S16_To_F32(
-		((int16_t*) buffer->pAudioData) + (
-			voice->src.curBufferOffset * voice->src.format->nChannels
-		),
-		decodeCache,
-		samples * voice->src.format->nChannels
-	);
+	FAudio_INTERNAL_Convert_S16_To_F32(src, decodeCache, samples * voice->src.format->nChannels);
 	LOG_FUNC_EXIT(voice->audio)
 }
 
-void FAudio_INTERNAL_DecodePCM24(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void FAudio_INTERNAL_DecodePCM24(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
 	uint32_t i, j;
-	const uint8_t *buf;
+	const uint8_t *buf = src;
 	LOG_FUNC_ENTER(voice->audio)
 
 	/* FIXME: Uh... is this something that can be SIMD-ified? */
-	buf = buffer->pAudioData + (
-		voice->src.curBufferOffset * voice->src.format->nBlockAlign
-	);
 	for (i = 0; i < samples; i += 1, buf += voice->src.format->nBlockAlign)
 	for (j = 0; j < voice->src.format->nChannels; j += 1)
 	{
@@ -1725,43 +1829,25 @@ void FAudio_INTERNAL_DecodePCM24(
 	LOG_FUNC_EXIT(voice->audio)
 }
 
-void FAudio_INTERNAL_DecodePCM32(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void FAudio_INTERNAL_DecodePCM32(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
 	LOG_FUNC_ENTER(voice->audio)
-	FAudio_INTERNAL_Convert_S32_To_F32(
-		((int32_t*) buffer->pAudioData) + (
-			voice->src.curBufferOffset * voice->src.format->nChannels
-		),
-		decodeCache,
-		samples * voice->src.format->nChannels
-	);
+	FAudio_INTERNAL_Convert_S32_To_F32(src, decodeCache, samples * voice->src.format->nChannels);
 	LOG_FUNC_EXIT(voice->audio)
 }
 
-void FAudio_INTERNAL_DecodePCM32F(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void FAudio_INTERNAL_DecodePCM32F(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
 	LOG_FUNC_ENTER(voice->audio)
-	FAudio_memcpy(
-		decodeCache,
-		((float*) buffer->pAudioData) + (
-			voice->src.curBufferOffset * voice->src.format->nChannels
-		),
-		sizeof(float) * samples * voice->src.format->nChannels
-	);
+	FAudio_memcpy(decodeCache, src, sizeof(float) * samples * voice->src.format->nChannels);
 	LOG_FUNC_EXIT(voice->audio)
 }
 
 /* MSADPCM Decoding */
 
-static inline int16_t FAudio_INTERNAL_ParseNibble(
+static float FAudio_INTERNAL_ParseNibble(
 	uint8_t nibble,
 	uint8_t predictor,
 	int16_t *delta,
@@ -1806,18 +1892,11 @@ static inline int16_t FAudio_INTERNAL_ParseNibble(
 	{
 		*delta = 16;
 	}
-	return sample;
+	return sample / 32768.0;
 }
 
-#define READ(item, type) \
-	item = *((type*) *buf); \
-	*buf += sizeof(type);
-
-static inline void FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
-	uint8_t **buf,
-	int16_t *blockCache,
-	uint32_t align
-) {
+static void decode_mono_adpcm_block(const uint8_t *src, float *dst, uint32_t offset, uint32_t count)
+{
 	uint32_t i;
 
 	/* Temp storage for ADPCM blocks */
@@ -1826,40 +1905,51 @@ static inline void FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
 	int16_t sample1;
 	int16_t sample2;
 
-	/* Preamble */
-	READ(predictor, uint8_t)
-	READ(delta, int16_t)
-	READ(sample1, int16_t)
-	READ(sample2, int16_t)
-	align -= 7;
+	predictor = src[0];
+	delta = *(int16_t *)&src[1];
+	sample1 = *(int16_t *)&src[3];
+	sample2 = *(int16_t *)&src[5];
+	src += 7;
 
 	/* Samples */
-	*blockCache++ = sample2;
-	*blockCache++ = sample1;
-	for (i = 0; i < align; i += 1, *buf += 1)
+	for (i = 0; i < FAudio_min(2, offset + count); ++i)
+	{
+		if (i < offset) continue;
+		if (i == 0) *dst++ = sample2 / 32768.0;
+		if (i == 1) *dst++ = sample1 / 32768.0;
+	}
+	for (; i < offset + count; i += 2)
 	{
-		*blockCache++ = FAudio_INTERNAL_ParseNibble(
-			*(*buf) >> 4,
+		float high, low;
+
+		high = FAudio_INTERNAL_ParseNibble(
+			*src >> 4,
 			predictor,
 			&delta,
 			&sample1,
 			&sample2
 		);
-		*blockCache++ = FAudio_INTERNAL_ParseNibble(
-			*(*buf) & 0x0F,
+		low = FAudio_INTERNAL_ParseNibble(
+			*src & 0xf,
 			predictor,
 			&delta,
 			&sample1,
 			&sample2
 		);
+
+		if (i >= offset)
+		{
+			*dst++ = high;
+			if (i + 1 < offset + count)
+				*dst++ = low;
+		}
+
+		++src;
 	}
 }
 
-static inline void FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
-	uint8_t **buf,
-	int16_t *blockCache,
-	uint32_t align
-) {
+static void decode_stereo_adpcm_block(const uint8_t *src, float *dst, uint32_t offset, uint32_t count)
+{
 	uint32_t i;
 
 	/* Temp storage for ADPCM blocks */
@@ -1873,159 +1963,109 @@ static inline void FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
 	int16_t r_sample2;
 
 	/* Preamble */
-	READ(l_predictor, uint8_t)
-	READ(r_predictor, uint8_t)
-	READ(l_delta, int16_t)
-	READ(r_delta, int16_t)
-	READ(l_sample1, int16_t)
-	READ(r_sample1, int16_t)
-	READ(l_sample2, int16_t)
-	READ(r_sample2, int16_t)
-	align -= 14;
+	l_predictor = src[0];
+	r_predictor = src[1];
+	l_delta = *(int16_t *)&src[2];
+	r_delta = *(int16_t *)&src[4];
+	l_sample1 = *(int16_t *)&src[6];
+	r_sample1 = *(int16_t *)&src[8];
+	l_sample2 = *(int16_t *)&src[10];
+	r_sample2 = *(int16_t *)&src[12];
+	src += 14;
 
 	/* Samples */
-	*blockCache++ = l_sample2;
-	*blockCache++ = r_sample2;
-	*blockCache++ = l_sample1;
-	*blockCache++ = r_sample1;
-	for (i = 0; i < align; i += 1, *buf += 1)
-	{
-		*blockCache++ = FAudio_INTERNAL_ParseNibble(
-			*(*buf) >> 4,
+	for (i = 0; i < FAudio_min(2, offset + count); ++i)
+	{
+		if (i < offset) continue;
+		if (i == 0)
+		{
+			*dst++ = l_sample2 / 32768.0;
+			*dst++ = r_sample2 / 32768.0;
+		}
+		if (i == 1)
+		{
+			*dst++ = l_sample1 / 32768.0;
+			*dst++ = r_sample1 / 32768.0;
+		}
+	}
+	for (; i < offset + count; ++i)
+	{
+		float left, right;
+
+		left = FAudio_INTERNAL_ParseNibble(
+			*src >> 4,
 			l_predictor,
 			&l_delta,
 			&l_sample1,
 			&l_sample2
 		);
-		*blockCache++ = FAudio_INTERNAL_ParseNibble(
-			*(*buf) & 0x0F,
+		right = FAudio_INTERNAL_ParseNibble(
+			*src & 0xf,
 			r_predictor,
 			&r_delta,
 			&r_sample1,
 			&r_sample2
 		);
+
+		if (i >= offset)
+		{
+			*dst++ = left;
+			*dst++ = right;
+		}
+
+		++src;
 	}
 }
 
-#undef READ
+void FAudio_INTERNAL_DecodeMonoMSADPCM(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
+	const uint32_t block_size = voice->src.format->nBlockAlign;
 
-void FAudio_INTERNAL_DecodeMonoMSADPCM(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
 	/* Loop variables */
 	uint32_t copy, done = 0;
 
-	/* Read pointers */
-	uint8_t *buf;
-	int32_t midOffset;
-
-	/* PCM block cache */
-	int16_t *blockCache;
-
 	/* Block size */
-	uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
+	uint32_t samples_per_block = ((FAudioADPCMWaveFormat *)voice->src.format)->wSamplesPerBlock;
 
 	LOG_FUNC_ENTER(voice->audio)
 
-	/* Where are we starting? */
-	buf = (uint8_t*) buffer->pAudioData + (
-		(voice->src.curBufferOffset / bsize) *
-		voice->src.format->nBlockAlign
-	);
-
-	/* Are we starting in the middle? */
-	midOffset = (voice->src.curBufferOffset % bsize);
-
 	/* Read in each block directly to the decode cache */
-	blockCache = (int16_t*) FAudio_alloca(bsize * sizeof(int16_t));
 	while (done < samples)
 	{
-		copy = FAudio_min(samples - done, bsize - midOffset);
-		FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
-			&buf,
-			blockCache,
-			voice->src.format->nBlockAlign
-		);
-		FAudio_INTERNAL_Convert_S16_To_F32(
-			blockCache + midOffset,
-			decodeCache,
-			copy
-		);
+		copy = FAudio_min(samples - done, samples_per_block - block_offset);
+		decode_mono_adpcm_block(src, decodeCache, block_offset, copy);
+		src = (char *)src + block_size;
 		decodeCache += copy;
 		done += copy;
-		midOffset = 0;
+		block_offset = 0;
 	}
-	FAudio_dealloca(blockCache);
 	LOG_FUNC_EXIT(voice->audio)
 }
 
-void FAudio_INTERNAL_DecodeStereoMSADPCM(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void FAudio_INTERNAL_DecodeStereoMSADPCM(FAudioVoice *voice, const void *src,
+	float *decodeCache, uint32_t block_offset, uint32_t samples)
+{
+	const uint32_t block_size = voice->src.format->nBlockAlign;
+
 	/* Loop variables */
 	uint32_t copy, done = 0;
 
-	/* Read pointers */
-	uint8_t *buf;
-	int32_t midOffset;
-
-	/* PCM block cache */
-	int16_t *blockCache;
-
 	/* Align, block size */
-	uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
+	uint32_t samples_per_block = ((FAudioADPCMWaveFormat *)voice->src.format)->wSamplesPerBlock;
 
 	LOG_FUNC_ENTER(voice->audio)
 
-	/* Where are we starting? */
-	buf = (uint8_t*) buffer->pAudioData + (
-		(voice->src.curBufferOffset / bsize) *
-		voice->src.format->nBlockAlign
-	);
-
-	/* Are we starting in the middle? */
-	midOffset = (voice->src.curBufferOffset % bsize);
-
 	/* Read in each block directly to the decode cache */
-	blockCache = (int16_t*) FAudio_alloca(bsize * 2 * sizeof(int16_t));
 	while (done < samples)
 	{
-		copy = FAudio_min(samples - done, bsize - midOffset);
-		FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
-			&buf,
-			blockCache,
-			voice->src.format->nBlockAlign
-		);
-		FAudio_INTERNAL_Convert_S16_To_F32(
-			blockCache + (midOffset * 2),
-			decodeCache,
-			copy * 2
-		);
+		copy = FAudio_min(samples - done, samples_per_block - block_offset);
+		decode_stereo_adpcm_block(src, decodeCache, block_offset, copy);
+		src = (char *)src + block_size;
 		decodeCache += copy * 2;
 		done += copy;
-		midOffset = 0;
+		block_offset = 0;
 	}
-	FAudio_dealloca(blockCache);
-	LOG_FUNC_EXIT(voice->audio)
-}
-
-/* Fallback WMA decoder, get ready for spam! */
-
-void FAudio_INTERNAL_DecodeWMAERROR(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
-	LOG_FUNC_ENTER(voice->audio)
-	LOG_ERROR(voice->audio, "%s", "WMA IS NOT SUPPORTED IN THIS BUILD!")
-	FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
 	LOG_FUNC_EXIT(voice->audio)
 }
 
diff --git a/libs/faudio/src/FAudio_internal.h b/libs/faudio/src/FAudio_internal.h
index 7fb99666ba54..4aeb3b1776e0 100644
--- a/libs/faudio/src/FAudio_internal.h
+++ b/libs/faudio/src/FAudio_internal.h
@@ -163,7 +163,7 @@ extern void FAudio_Log(char const *msg);
 
 #define FAudio_strlen(ptr) SDL_strlen(ptr)
 #define FAudio_strcmp(str1, str2) SDL_strcmp(str1, str2)
-#define FAudio_strncmp(str1, str2, size) SDL_strncmp(str1, str1, size)
+#define FAudio_strncmp(str1, str2, size) SDL_strncmp(str1, str2, size)
 #define FAudio_strlcpy(ptr1, ptr2, size) SDL_strlcpy(ptr1, ptr2, size)
 
 #define FAudio_pow(x, y) SDL_pow(x, y)
@@ -299,20 +299,22 @@ typedef enum FAudioVoiceType
 	FAUDIO_VOICE_MASTER
 } FAudioVoiceType;
 
-typedef struct FAudioBufferEntry FAudioBufferEntry;
-struct FAudioBufferEntry
+struct queued_buffer
 {
 	FAudioBuffer buffer;
 	FAudioBufferWMA bufferWMA;
-	FAudioBufferEntry *next;
+	uint32_t loop_bytes, play_bytes;
+	bool sent_OnStartBuffer;
+	bool internal;
+
+	/* Byte offset of the first block in this buffer. This is usually zero,
+	 * but will be nonzero if the previous buffer did not have an aligned
+	 * size. */
+	uint32_t first_block_offset;
 };
 
-typedef void (FAUDIOCALL * FAudioDecodeCallback)(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,	/* Buffer to decode */
-	float *decodeCache,	/* Decode into here */
-	uint32_t samples	/* Samples to decode */
-);
+typedef void (FAUDIOCALL * FAudioDecodeCallback)(FAudioVoice *voice,
+	const void *src, float *dst, uint32_t block_offset, uint32_t sample_count);
 
 typedef void (FAUDIOCALL * FAudioResampleCallback)(
 	float *restrict dCache,
@@ -425,6 +427,7 @@ struct FAudio
 	LinkedList *sources;
 	LinkedList *submixes;
 	LinkedList *callbacks;
+	FAudioMutex refLock; // FIXME: refcount should be an SDL_AtomicInt instead -flibit
 	FAudioMutex sourceLock;
 	FAudioMutex submixLock;
 	FAudioMutex callbackLock;
@@ -524,13 +527,28 @@ struct FAudioVoice
 			FAudioResampleCallback resample;
 			FAudioVoiceCallback *callback;
 
+			/* Number of samples in a block, where the byte size of
+			 * a block is format->nBlockAlign.
+			 *
+			 * This is 1 for PCM formats, but depends on the format
+			 * for ADPCM. For WMV it is not used. */
+			uint32_t samples_per_block;
+
 			/* Dynamic */
 			uint8_t active;
 			float freqRatio;
-			uint8_t newBuffer;
 			uint64_t totalSamples;
-			FAudioBufferEntry *bufferList;
-			FAudioBufferEntry *flushList;
+
+			struct queued_buffer *queued_buffers;
+			size_t queued_buffer_count, queued_buffers_capacity;
+			struct queued_buffer *flush_buffers;
+			size_t flush_buffer_count, flush_buffers_capacity;
+
+			/* Data left over from one or more buffers whose size
+			 * was unaligned. */
+			uint8_t *unaligned_data;
+			uint32_t unaligned_size;
+
 			FAudioMutex bufferLock;
 		} src;
 		struct
@@ -582,6 +600,8 @@ uint32_t FAudio_INTERNAL_VoiceOutputFrequency(
 );
 extern const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64];
 
+bool array_reserve(FAudio *audio, void **elements, size_t *capacity, size_t count, size_t size);
+
 /* Debug */
 
 #ifdef FAUDIO_DISABLE_DEBUGCONFIGURATION
@@ -763,13 +783,8 @@ void FAudio_INTERNAL_InitSIMDFunctions(uint8_t hasSSE2, uint8_t hasNEON);
 
 /* Decoders */
 
-#define DECODE_FUNC(type) \
-	extern void FAudio_INTERNAL_Decode##type( \
-		FAudioVoice *voice, \
-		FAudioBuffer *buffer, \
-		float *decodeCache, \
-		uint32_t samples \
-	);
+#define DECODE_FUNC(type) extern void FAudio_INTERNAL_Decode##type(FAudioVoice *voice, \
+	const void *src, float *dst, uint32_t block_offset, uint32_t sample_count);
 DECODE_FUNC(PCM8)
 DECODE_FUNC(PCM16)
 DECODE_FUNC(PCM24)
@@ -782,6 +797,7 @@ DECODE_FUNC(WMAERROR)
 
 /* WMA decoding */
 
+void decode_wma(FAudioVoice *voice, struct queued_buffer *buffer, float *dst, uint32_t sample_count);
 #ifdef HAVE_WMADEC
 uint32_t FAudio_WMADEC_init(FAudioSourceVoice *pSourceVoice, uint32_t type);
 void FAudio_WMADEC_free(FAudioSourceVoice *voice);
diff --git a/libs/faudio/src/FAudio_platform_win32_wmadec.c b/libs/faudio/src/FAudio_platform_win32_wmadec.c
index dd1416a266e9..8d7b9185a944 100644
--- a/libs/faudio/src/FAudio_platform_win32_wmadec.c
+++ b/libs/faudio/src/FAudio_platform_win32_wmadec.c
@@ -1,9 +1,9 @@
 /* FAudio WMADEC implementation over Win32 MF */
 
-#ifdef HAVE_WMADEC
-
 #include "FAudio_internal.h"
 
+#ifdef HAVE_WMADEC
+
 #include <stddef.h>
 
 #define COBJMACROS
@@ -28,8 +28,21 @@ struct FAudioWMADEC
 	size_t output_size;
 	size_t input_pos;
 	size_t input_size;
+	bool eos;
 };
 
+static void send_eos(struct FAudioWMADEC *decoder)
+{
+	HRESULT hr;
+
+	if (decoder->eos)
+		return;
+
+	hr = IMFTransform_ProcessMessage(decoder->decoder, MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0);
+	FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
+	decoder->eos = true;
+}
+
 static HRESULT FAudio_WMAMF_ProcessInput(
 	FAudioVoice *voice,
 	FAudioBuffer *buffer
@@ -141,12 +154,8 @@ static HRESULT FAudio_WMAMF_ProcessOutput(
 	return S_OK;
 };
 
-static void FAudio_INTERNAL_DecodeWMAMF(
-	FAudioVoice *voice,
-	FAudioBuffer *buffer,
-	float *decodeCache,
-	uint32_t samples
-) {
+void decode_wma(FAudioVoice *voice, struct queued_buffer *buffer, float *decodeCache, uint32_t samples)
+{
 	const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
 	size_t samples_pos, samples_size, copy_size = 0;
 	struct FAudioWMADEC *impl = voice->src.wmadec;
@@ -158,7 +167,7 @@ static void FAudio_INTERNAL_DecodeWMAMF(
 	{
 		if (wfx->Format.wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
 		{
-			const FAudioBufferWMA *wma = &voice->src.bufferList->bufferWMA;
+			const FAudioBufferWMA *wma = &buffer->bufferWMA;
 			const UINT32 *output_sizes = wma->pDecodedPacketCumulativeBytes;
 
 			impl->input_size = wfx->Format.nBlockAlign;
@@ -193,7 +202,7 @@ static void FAudio_INTERNAL_DecodeWMAMF(
 			0
 		);
 		FAudio_assert(!FAILED(hr) && "Failed to notify decoder stream start!");
-		FAudio_WMAMF_ProcessInput(voice, buffer);
+		FAudio_WMAMF_ProcessInput(voice, &buffer->buffer);
 	}
 
 	samples_pos = voice->src.curBufferOffset * voice->src.format->nChannels * sizeof(float);
@@ -201,24 +210,18 @@ static void FAudio_INTERNAL_DecodeWMAMF(
 
 	while (impl->output_pos < samples_pos + samples_size)
 	{
-		hr = FAudio_WMAMF_ProcessOutput(voice, buffer);
+		hr = FAudio_WMAMF_ProcessOutput(voice, &buffer->buffer);
 		if (FAILED(hr)) goto error;
 		if (hr == S_OK) continue;
 
-		hr  = FAudio_WMAMF_ProcessInput(voice, buffer);
+		hr  = FAudio_WMAMF_ProcessInput(voice, &buffer->buffer);
 		if (FAILED(hr)) goto error;
 		if (hr == S_OK) continue;
 
-		if (!impl->input_size) break;
+		if (impl->eos)
+			break;
 
-		LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
-		hr = IMFTransform_ProcessMessage(
-			impl->decoder,
-			MFT_MESSAGE_NOTIFY_END_OF_STREAM,
-			0
-		);
-		FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
-		impl->input_size = 0;
+		send_eos(impl);
 	}
 
 	if (impl->output_pos > samples_pos)
@@ -545,7 +548,6 @@ next:
 	FAudio_assert(!FAILED(hr) && "Failed to start decoder stream!");
 
 	voice->src.wmadec = impl;
-	voice->src.decode = FAudio_INTERNAL_DecodeWMAMF;
 
 	LOG_FUNC_EXIT(voice->audio);
 	return 0;
@@ -560,17 +562,7 @@ void FAudio_WMADEC_free(FAudioSourceVoice *voice)
 	FAudio_PlatformLockMutex(voice->audio->sourceLock);
 	LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
 
-	if (impl->input_size)
-	{
-		LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
-		hr = IMFTransform_ProcessMessage(
-			impl->decoder,
-			MFT_MESSAGE_NOTIFY_END_OF_STREAM,
-			0
-		);
-		FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
-		impl->input_size = 0;
-	}
+	send_eos(impl);
 	if (impl->output_pos)
 	{
 		LOG_INFO(voice->audio, "sending DRAIN to %p", impl->decoder);
@@ -602,21 +594,23 @@ void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice)
 
 	LOG_FUNC_ENTER(voice->audio)
 
-	if (impl->input_size)
-	{
-		LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
-		hr = IMFTransform_ProcessMessage(
-			impl->decoder,
-			MFT_MESSAGE_NOTIFY_END_OF_STREAM,
-			0
-		);
-		FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
-		impl->input_size = 0;
-	}
+	send_eos(impl);
+
 	impl->output_pos = 0;
 	impl->input_pos = 0;
+	impl->eos = false;
 
 	LOG_FUNC_EXIT(voice->audio)
 }
 
+#else /* HAVE_WMADEC */
+
+void decode_wma(FAudioVoice *voice, struct queued_buffer *buffer, float *dst, uint32_t sample_count)
+{
+	LOG_FUNC_ENTER(voice->audio)
+	LOG_ERROR(voice->audio, "%s", "WMA IS NOT SUPPORTED IN THIS BUILD!")
+	FAudio_zero(dst, sample_count * voice->src.format->nChannels * sizeof(float));
+	LOG_FUNC_EXIT(voice->audio)
+}
+
 #endif /* HAVE_WMADEC */
diff --git a/libs/fluidsynth/Makefile.in b/libs/fluidsynth/Makefile.in
index 0620725b8ee4..0655d33fdee3 100644
--- a/libs/fluidsynth/Makefile.in
+++ b/libs/fluidsynth/Makefile.in
@@ -28,7 +28,6 @@ SOURCES = \
 	src/sfloader/fluid_sffile.c \
 	src/sfloader/fluid_sfont.c \
 	src/synth/fluid_chan.c \
-	src/synth/fluid_event.c \
 	src/synth/fluid_gen.c \
 	src/synth/fluid_mod.c \
 	src/synth/fluid_synth.c \
diff --git a/libs/fluidsynth/src/synth/fluid_event.c b/libs/fluidsynth/src/synth/fluid_event.c
deleted file mode 100644
index 88adb77b1d46..000000000000
--- a/libs/fluidsynth/src/synth/fluid_event.c
+++ /dev/null
@@ -1,863 +0,0 @@
-/* FluidSynth - A Software Synthesizer
- *
- * Copyright (C) 2003  Peter Hanappe and others.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-
-/*
- 2002 : API design by Peter Hanappe and Antoine Schmitt
- August 2002 : Implementation by Antoine Schmitt as@gratin.org
-               as part of the infiniteCD author project
-               http://www.infiniteCD.org/
- Oct4.2002 : AS : corrected bug in heap allocation, that caused a crash during sequencer free.
-*/
-
-
-#include "fluid_event.h"
-#include "fluidsynth_priv.h"
-#include "fluid_midi.h"
-
-/***************************************************************
- *
- *                           SEQUENCER EVENTS
- */
-
-/* Event alloc/free */
-
-void
-fluid_event_clear(fluid_event_t *evt)
-{
-    FLUID_MEMSET(evt, 0, sizeof(fluid_event_t));
-
-    // by default, no type
-    evt->dest = -1;
-    evt->src = -1;
-    evt->type = -1;
-    evt->id = -1;
-}
-
-/**
- * Create a new sequencer event structure.
- * @return New sequencer event structure or NULL if out of memory
- */
-fluid_event_t *
-new_fluid_event(void)
-{
-    fluid_event_t *evt;
-
-    evt = FLUID_NEW(fluid_event_t);
-
-    if(evt == NULL)
-    {
-        FLUID_LOG(FLUID_PANIC, "event: Out of memory\n");
-        return NULL;
-    }
-
-    fluid_event_clear(evt);
-
-    return(evt);
-}
-
-/**
- * Delete a sequencer event structure.
- * @param evt Sequencer event structure created by new_fluid_event().
- */
-void
-delete_fluid_event(fluid_event_t *evt)
-{
-    fluid_return_if_fail(evt != NULL);
-
-    FLUID_FREE(evt);
-}
-
-/**
- * Set the time field of a sequencer event.
- * @internal
- * @param evt Sequencer event structure
- * @param time Time value to assign
- */
-void
-fluid_event_set_time(fluid_event_t *evt, unsigned int time)
-{
-    evt->time = time;
-}
-
-void
-fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id)
-{
-    evt->id = id;
-}
-
-/**
- * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set.
- * @param evt Sequencer event structure
- * @param src Unique sequencer ID
- */
-void
-fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src)
-{
-    evt->src = src;
-}
-
-/**
- * Set destination of this sequencer event, i.e. the sequencer client this event will be sent to. \c dest must be a unique sequencer ID.
- * @param evt Sequencer event structure
- * @param dest The destination unique sequencer ID
- */
-void
-fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest)
-{
-    evt->dest = dest;
-}
-
-/**
- * Set a sequencer event to be a timer event.
- * @param evt Sequencer event structure
- * @param data User supplied data pointer
- */
-void
-fluid_event_timer(fluid_event_t *evt, void *data)
-{
-    evt->type = FLUID_SEQ_TIMER;
-    evt->data = data;
-}
-
-/**
- * Set a sequencer event to be a note on event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param key MIDI note number (0-127)
- * @param vel MIDI velocity value (0-127)
- * @note Since fluidsynth 2.2.2, this function will give you a #FLUID_SEQ_NOTEOFF when
- * called with @p vel being zero.
- */
-void
-fluid_event_noteon(fluid_event_t *evt, int channel, short key, short vel)
-{
-    if(vel == 0)
-    {
-        fluid_event_noteoff(evt, channel, key);
-        return;
-    }
-
-    evt->type = FLUID_SEQ_NOTEON;
-    evt->channel = channel;
-    evt->key = key;
-    evt->vel = vel;
-}
-
-/**
- * Set a sequencer event to be a note off event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param key MIDI note number (0-127)
- */
-void
-fluid_event_noteoff(fluid_event_t *evt, int channel, short key)
-{
-    evt->type = FLUID_SEQ_NOTEOFF;
-    evt->channel = channel;
-    evt->key = key;
-}
-
-/**
- * Set a sequencer event to be a note duration event.
- *
- * Before fluidsynth 2.2.0, this event type was naively implemented when used in conjunction with fluid_sequencer_register_fluidsynth(),
- * because it simply enqueued a fluid_event_noteon() and fluid_event_noteoff().
- * A handling for overlapping notes was not implemented. Starting with 2.2.0, this changes: If a fluid_event_note() is already playing,
- * while another fluid_event_note() arrives on the same @c channel and @c key, the earlier event will be canceled.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param key MIDI note number (0-127)
- * @param vel MIDI velocity value (1-127)
- * @param duration Duration of note in the time scale used by the sequencer
- *
- * @note The application should decide whether to use only Notes with duration, or separate NoteOn and NoteOff events.
- * @warning Calling this function with @p vel or @p duration being zero results in undefined behavior!
- */
-void
-fluid_event_note(fluid_event_t *evt, int channel, short key, short vel, unsigned int duration)
-{
-    evt->type = FLUID_SEQ_NOTE;
-    evt->channel = channel;
-    evt->key = key;
-    evt->vel = vel;
-    evt->duration = duration;
-}
-
-/**
- * Set a sequencer event to be an all sounds off event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- */
-void
-fluid_event_all_sounds_off(fluid_event_t *evt, int channel)
-{
-    evt->type = FLUID_SEQ_ALLSOUNDSOFF;
-    evt->channel = channel;
-}
-
-/**
- * Set a sequencer event to be a all notes off event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- */
-void
-fluid_event_all_notes_off(fluid_event_t *evt, int channel)
-{
-    evt->type = FLUID_SEQ_ALLNOTESOFF;
-    evt->channel = channel;
-}
-
-/**
- * Set a sequencer event to be a bank select event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param bank_num MIDI bank number (0-16383)
- */
-void
-fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num)
-{
-    evt->type = FLUID_SEQ_BANKSELECT;
-    evt->channel = channel;
-    evt->control = bank_num;
-}
-
-/**
- * Set a sequencer event to be a program change event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val MIDI program number (0-127)
- */
-void
-fluid_event_program_change(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_PROGRAMCHANGE;
-    evt->channel = channel;
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a program select event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param sfont_id SoundFont ID number
- * @param bank_num MIDI bank number (0-16383)
- * @param preset_num MIDI preset number (0-127)
- */
-void
-fluid_event_program_select(fluid_event_t *evt, int channel,
-                           unsigned int sfont_id, short bank_num, short preset_num)
-{
-    evt->type = FLUID_SEQ_PROGRAMSELECT;
-    evt->channel = channel;
-    evt->duration = sfont_id;
-    evt->value = preset_num;
-    evt->control = bank_num;
-}
-
-/**
- * Set a sequencer event to be a pitch bend event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend)
- */
-void
-fluid_event_pitch_bend(fluid_event_t *evt, int channel, int pitch)
-{
-    evt->type = FLUID_SEQ_PITCHBEND;
-    evt->channel = channel;
-
-    if(pitch < 0)
-    {
-        pitch = 0;
-    }
-
-    if(pitch > 16383)
-    {
-        pitch = 16383;
-    }
-
-    evt->pitch = pitch;
-}
-
-/**
- * Set a sequencer event to be a pitch wheel sensitivity event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param value MIDI pitch wheel sensitivity value in semitones
- */
-void
-fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int value)
-{
-    evt->type = FLUID_SEQ_PITCHWHEELSENS;
-    evt->channel = channel;
-    evt->value = value;
-}
-
-/**
- * Set a sequencer event to be a modulation event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val MIDI modulation value (0-127)
- */
-void
-fluid_event_modulation(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_MODULATION;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a MIDI sustain event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val MIDI sustain value (0-127)
- */
-void
-fluid_event_sustain(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_SUSTAIN;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a MIDI control change event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param control MIDI control number (0-127)
- * @param val MIDI control value (0-127)
- */
-void
-fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val)
-{
-    evt->type = FLUID_SEQ_CONTROLCHANGE;
-    evt->channel = channel;
-    evt->control = control;
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a stereo pan event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right)
- */
-void
-fluid_event_pan(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_PAN;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a volume event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val Volume value (0-127)
- */
-void
-fluid_event_volume(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_VOLUME;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a reverb send event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val Reverb amount (0-127)
- */
-void
-fluid_event_reverb_send(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_REVERBSEND;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a chorus send event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val Chorus amount (0-127)
- */
-void
-fluid_event_chorus_send(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_CHORUSSEND;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-
-/**
- * Set a sequencer event to be an unregistering event.
- * @param evt Sequencer event structure
- * @since 1.1.0
- */
-void
-fluid_event_unregistering(fluid_event_t *evt)
-{
-    evt->type = FLUID_SEQ_UNREGISTERING;
-}
-
-/**
- * Set a sequencer event to be a scale change event.
- * Useful for scheduling tempo changes.
- * @param evt Sequencer event structure
- * @param new_scale The new time scale to apply to the sequencer, see fluid_sequencer_set_time_scale()
- * @since 2.2.0
- */
-void
-fluid_event_scale(fluid_event_t *evt, double new_scale)
-{
-    evt->type = FLUID_SEQ_SCALE;
-    evt->scale = new_scale;
-}
-
-/**
- * Set a sequencer event to be a channel-wide aftertouch event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param val Aftertouch amount (0-127)
- * @since 1.1.0
- */
-void
-fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val)
-{
-    evt->type = FLUID_SEQ_CHANNELPRESSURE;
-    evt->channel = channel;
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a polyphonic aftertouch event.
- * @param evt Sequencer event structure
- * @param channel MIDI channel number
- * @param key MIDI note number (0-127)
- * @param val Aftertouch amount (0-127)
- * @since 2.0.0
- */
-void
-fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val)
-{
-    evt->type = FLUID_SEQ_KEYPRESSURE;
-    evt->channel = channel;
-
-    if(key < 0)
-    {
-        key = 0;
-    }
-
-    if(key > 127)
-    {
-        key = 127;
-    }
-
-    if(val < 0)
-    {
-        val = 0;
-    }
-
-    if(val > 127)
-    {
-        val = 127;
-    }
-
-    evt->key = key;
-    evt->value = val;
-}
-
-/**
- * Set a sequencer event to be a midi system reset event.
- * @param evt Sequencer event structure
- * @since 1.1.0
- */
-void
-fluid_event_system_reset(fluid_event_t *evt)
-{
-    evt->type = FLUID_SEQ_SYSTEMRESET;
-}
-
-/**
- * Transforms an incoming MIDI event (from a MIDI driver or MIDI router) to a
- * sequencer event.
- *
- * @param evt Sequencer event structure
- * @param event MIDI event
- * @return #FLUID_OK or #FLUID_FAILED
- *
- * @note This function copies the fields of the MIDI event into the provided
- * sequencer event. Calling applications must create the sequencer event and set
- * additional fields such as the source and destination of the sequencer event.
- *
- * @code{.cpp}
- * // ... get MIDI event, e.g. using player_callback()
- *
- * // Send MIDI event to sequencer to play
- * fluid_event_t *evt = new_fluid_event();
- * fluid_event_set_source(evt, -1);
- * fluid_event_set_dest(evt, seqid);
- * fluid_event_from_midi_event(evt, event);
- * fluid_sequencer_send_at(sequencer, evt, 50, 0); // relative time
- * delete_fluid_event(evt);
- * @endcode
- *
- * @since 2.2.7
- */
-int fluid_event_from_midi_event(fluid_event_t *evt, const fluid_midi_event_t *event)
-{
-    int chan;
-    fluid_return_val_if_fail(event != NULL, FLUID_FAILED);
-
-    chan = fluid_midi_event_get_channel(event);
-
-    switch (fluid_midi_event_get_type(event))
-    {
-        case NOTE_OFF:
-            fluid_event_noteoff(evt, chan, (short)fluid_midi_event_get_key(event));
-            break;
-
-        case NOTE_ON:
-            fluid_event_noteon(evt,
-                               fluid_midi_event_get_channel(event),
-                               (short)fluid_midi_event_get_key(event),
-                               (short)fluid_midi_event_get_velocity(event));
-            break;
-
-        case CONTROL_CHANGE:
-            fluid_event_control_change(evt,
-                                       chan,
-                                       (short)fluid_midi_event_get_control(event),
-                                       (short)fluid_midi_event_get_value(event));
-            break;
-
-        case PROGRAM_CHANGE:
-            fluid_event_program_change(evt, chan, (short)fluid_midi_event_get_program(event));
-            break;
-
-        case PITCH_BEND:
-            fluid_event_pitch_bend(evt, chan, fluid_midi_event_get_pitch(event));
-            break;
-
-        case CHANNEL_PRESSURE:
-            fluid_event_channel_pressure(evt, chan, (short)fluid_midi_event_get_program(event));
-            break;
-
-        case KEY_PRESSURE:
-            fluid_event_key_pressure(evt,
-                                     chan,
-                                     (short)fluid_midi_event_get_key(event),
-                                     (short)fluid_midi_event_get_value(event));
-            break;
-
-        case MIDI_SYSTEM_RESET:
-            fluid_event_system_reset(evt);
-            break;
-
-        default: /* Not yet implemented */
-            return FLUID_FAILED;
-    }
-
-    return FLUID_OK;
-}
-
-/*
- * Accessing event data
- */
-
-/**
- * Get the event type (#fluid_seq_event_type) field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return Event type (#fluid_seq_event_type).
- */
-int fluid_event_get_type(fluid_event_t *evt)
-{
-    return evt->type;
-}
-
-/**
- * @internal
- * Get the time field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return Time value
- */
-unsigned int fluid_event_get_time(fluid_event_t *evt)
-{
-    return evt->time;
-}
-
-/**
- * @internal
- * Get the time field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return Time value
- */
-fluid_note_id_t fluid_event_get_id(fluid_event_t *evt)
-{
-    return evt->id;
-}
-
-/**
- * Get the source sequencer client from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return source field of the sequencer event
- */
-fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt)
-{
-    return evt->src;
-}
-
-/**
- * Get the dest sequencer client from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return dest field of the sequencer event
- */
-fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt)
-{
-    return evt->dest;
-}
-
-/**
- * Get the MIDI channel field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI zero-based channel number
- */
-int fluid_event_get_channel(fluid_event_t *evt)
-{
-    return evt->channel;
-}
-
-/**
- * Get the MIDI note field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI note number (0-127)
- */
-short fluid_event_get_key(fluid_event_t *evt)
-{
-    return evt->key;
-}
-
-/**
- * Get the MIDI velocity field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI velocity value (0-127)
- */
-short fluid_event_get_velocity(fluid_event_t *evt)
-
-{
-    return evt->vel;
-}
-
-/**
- * Get the MIDI control number field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI control number (0-127)
- */
-short fluid_event_get_control(fluid_event_t *evt)
-{
-    return evt->control;
-}
-
-/**
- * Get the value field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return Value field of event.
- *
- * The Value field is used by the following event types:
- * #FLUID_SEQ_PROGRAMCHANGE, #FLUID_SEQ_PROGRAMSELECT (preset_num),
- * #FLUID_SEQ_PITCHWHEELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN,
- * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME,
- * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND.
- */
-int fluid_event_get_value(fluid_event_t *evt)
-{
-    return evt->value;
-}
-
-/**
- * Get the data field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return Data field of event.
- *
- * Used by the #FLUID_SEQ_TIMER event type.
- */
-void *fluid_event_get_data(fluid_event_t *evt)
-{
-    return evt->data;
-}
-
-/**
- * Get the duration field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return Note duration value in the time scale used by the sequencer (by default milliseconds)
- *
- * Used by the #FLUID_SEQ_NOTE event type.
- */
-unsigned int fluid_event_get_duration(fluid_event_t *evt)
-{
-    return evt->duration;
-}
-
-/**
- * Get the MIDI bank field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI bank number (0-16383)
- *
- * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT
- * event types.
- */
-short fluid_event_get_bank(fluid_event_t *evt)
-{
-    return evt->control;
-}
-
-/**
- * Get the pitch field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI pitch bend pitch value (0-16383, 8192 = no bend)
- *
- * Used by the #FLUID_SEQ_PITCHBEND event type.
- */
-int fluid_event_get_pitch(fluid_event_t *evt)
-{
-    return evt->pitch;
-}
-
-/**
- * Get the MIDI program field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return MIDI program number (0-127)
- *
- * Used by the #FLUID_SEQ_PROGRAMCHANGE and #FLUID_SEQ_PROGRAMSELECT
- * event types.
- */
-int
-fluid_event_get_program(fluid_event_t *evt)
-{
-    return evt->value;
-}
-
-/**
- * Get the SoundFont ID field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return SoundFont identifier value.
- *
- * Used by the #FLUID_SEQ_PROGRAMSELECT event type.
- */
-unsigned int
-fluid_event_get_sfont_id(fluid_event_t *evt)
-{
-    return evt->duration;
-}
-
-/**
- * Gets time scale field from a sequencer event structure.
- * @param evt Sequencer event structure
- * @return SoundFont identifier value.
- *
- * Used by the #FLUID_SEQ_SCALE event type.
- */
-double fluid_event_get_scale(fluid_event_t *evt)
-{
-    return evt->scale;
-}
diff --git a/libs/fluidsynth/src/synth/fluid_event.h b/libs/fluidsynth/src/synth/fluid_event.h
deleted file mode 100644
index 4a0cca5d93d1..000000000000
--- a/libs/fluidsynth/src/synth/fluid_event.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* FluidSynth - A Software Synthesizer
- *
- * Copyright (C) 2003  Peter Hanappe and others.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-
-#ifndef _FLUID_EVENT_PRIV_H
-#define _FLUID_EVENT_PRIV_H
-
-#include "fluidsynth.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef int fluid_note_id_t;
-
-/* Private data for event */
-/* ?? should be optimized in size, using unions */
-struct _fluid_event_t
-{
-    unsigned int time;
-    int type;
-    fluid_seq_id_t src;
-    fluid_seq_id_t dest;
-    int channel;
-    short key;
-    short vel;
-    short control;
-    int value;
-    fluid_note_id_t id;
-    int pitch;
-    unsigned int duration;
-    double scale;
-    void *data;
-};
-
-unsigned int fluid_event_get_time(fluid_event_t *evt);
-void fluid_event_set_time(fluid_event_t *evt, unsigned int time);
-
-fluid_note_id_t fluid_event_get_id(fluid_event_t *evt);
-void fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id);
-
-void fluid_event_clear(fluid_event_t *evt);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _FLUID_EVENT_PRIV_H */
diff --git a/libs/gsm/Makefile.in b/libs/gsm/Makefile.in
index 9f9aefb4e78a..586ad2064e06 100644
--- a/libs/gsm/Makefile.in
+++ b/libs/gsm/Makefile.in
@@ -11,7 +11,6 @@ SOURCES = \
 	src/gsm_destroy.c \
 	src/gsm_encode.c \
 	src/gsm_option.c \
-	src/gsm_print.c \
 	src/long_term.c \
 	src/lpc.c \
 	src/preprocess.c \
diff --git a/libs/gsm/inc/gsm.h b/libs/gsm/inc/gsm.h
index 55c02c452b9f..104aaab79511 100644
--- a/libs/gsm/inc/gsm.h
+++ b/libs/gsm/inc/gsm.h
@@ -21,6 +21,10 @@ extern "C" {
 #	define	NeedFunctionPrototypes	1
 #endif
 
+#ifdef _MSC_VER
+#	define	NeedFunctionPrototypes	1
+#endif
+
 #ifdef _NO_PROTO
 #	undef	NeedFunctionPrototypes
 #endif
diff --git a/libs/gsm/inc/proto.h b/libs/gsm/inc/proto.h
index 2851c086a2db..a81b7c033064 100644
--- a/libs/gsm/inc/proto.h
+++ b/libs/gsm/inc/proto.h
@@ -17,6 +17,10 @@
 #	define	NeedFunctionPrototypes	1
 #endif
 
+#ifdef _MSC_VER
+#	define	NeedFunctionPrototypes	1
+#endif
+
 #ifdef	_NO_PROTO
 #	undef	NeedFunctionPrototypes
 #endif
diff --git a/libs/gsm/src/gsm_print.c b/libs/gsm/src/gsm_print.c
deleted file mode 100644
index 47f753b1a308..000000000000
--- a/libs/gsm/src/gsm_print.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
- * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
- * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
- */
-
-/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_print.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */
-
-#include	<stdio.h>
-
-#include "private.h"
-
-#include "gsm.h"
-#include "proto.h"
-
-int gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c)
-{
-	word  	LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
-
-	/* GSM_MAGIC  = (*c >> 4) & 0xF; */
-
-	if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
-
-	LARc[0]  = (*c++ & 0xF) << 2;		/* 1 */
-	LARc[0] |= (*c >> 6) & 0x3;
-	LARc[1]  = *c++ & 0x3F;
-	LARc[2]  = (*c >> 3) & 0x1F;
-	LARc[3]  = (*c++ & 0x7) << 2;
-	LARc[3] |= (*c >> 6) & 0x3;
-	LARc[4]  = (*c >> 2) & 0xF;
-	LARc[5]  = (*c++ & 0x3) << 2;
-	LARc[5] |= (*c >> 6) & 0x3;
-	LARc[6]  = (*c >> 3) & 0x7;
-	LARc[7]  = *c++ & 0x7;
-
-
-	Nc[0]  = (*c >> 1) & 0x7F;
-	bc[0]  = (*c++ & 0x1) << 1;
-	bc[0] |= (*c >> 7) & 0x1;
-	Mc[0]  = (*c >> 5) & 0x3;
-	xmaxc[0]  = (*c++ & 0x1F) << 1;
-	xmaxc[0] |= (*c >> 7) & 0x1;
-	xmc[0]  = (*c >> 4) & 0x7;
-	xmc[1]  = (*c >> 1) & 0x7;
-	xmc[2]  = (*c++ & 0x1) << 2;
-	xmc[2] |= (*c >> 6) & 0x3;
-	xmc[3]  = (*c >> 3) & 0x7;
-	xmc[4]  = *c++ & 0x7;
-	xmc[5]  = (*c >> 5) & 0x7;
-	xmc[6]  = (*c >> 2) & 0x7;
-	xmc[7]  = (*c++ & 0x3) << 1;		/* 10 */
-	xmc[7] |= (*c >> 7) & 0x1;
-	xmc[8]  = (*c >> 4) & 0x7;
-	xmc[9]  = (*c >> 1) & 0x7;
-	xmc[10]  = (*c++ & 0x1) << 2;
-	xmc[10] |= (*c >> 6) & 0x3;
-	xmc[11]  = (*c >> 3) & 0x7;
-	xmc[12]  = *c++ & 0x7;
-
-	Nc[1]  = (*c >> 1) & 0x7F;
-	bc[1]  = (*c++ & 0x1) << 1;
-	bc[1] |= (*c >> 7) & 0x1;
-	Mc[1]  = (*c >> 5) & 0x3;
-	xmaxc[1]  = (*c++ & 0x1F) << 1;
-	xmaxc[1] |= (*c >> 7) & 0x1;
-	xmc[13]  = (*c >> 4) & 0x7;
-	xmc[14]  = (*c >> 1) & 0x7;
-	xmc[15]  = (*c++ & 0x1) << 2;
-	xmc[15] |= (*c >> 6) & 0x3;
-	xmc[16]  = (*c >> 3) & 0x7;
-	xmc[17]  = *c++ & 0x7;
-	xmc[18]  = (*c >> 5) & 0x7;
-	xmc[19]  = (*c >> 2) & 0x7;
-	xmc[20]  = (*c++ & 0x3) << 1;
-	xmc[20] |= (*c >> 7) & 0x1;
-	xmc[21]  = (*c >> 4) & 0x7;
-	xmc[22]  = (*c >> 1) & 0x7;
-	xmc[23]  = (*c++ & 0x1) << 2;
-	xmc[23] |= (*c >> 6) & 0x3;
-	xmc[24]  = (*c >> 3) & 0x7;
-	xmc[25]  = *c++ & 0x7;
-
-
-	Nc[2]  = (*c >> 1) & 0x7F;
-	bc[2]  = (*c++ & 0x1) << 1;		/* 20 */
-	bc[2] |= (*c >> 7) & 0x1;
-	Mc[2]  = (*c >> 5) & 0x3;
-	xmaxc[2]  = (*c++ & 0x1F) << 1;
-	xmaxc[2] |= (*c >> 7) & 0x1;
-	xmc[26]  = (*c >> 4) & 0x7;
-	xmc[27]  = (*c >> 1) & 0x7;
-	xmc[28]  = (*c++ & 0x1) << 2;
-	xmc[28] |= (*c >> 6) & 0x3;
-	xmc[29]  = (*c >> 3) & 0x7;
-	xmc[30]  = *c++ & 0x7;
-	xmc[31]  = (*c >> 5) & 0x7;
-	xmc[32]  = (*c >> 2) & 0x7;
-	xmc[33]  = (*c++ & 0x3) << 1;
-	xmc[33] |= (*c >> 7) & 0x1;
-	xmc[34]  = (*c >> 4) & 0x7;
-	xmc[35]  = (*c >> 1) & 0x7;
-	xmc[36]  = (*c++ & 0x1) << 2;
-	xmc[36] |= (*c >> 6) & 0x3;
-	xmc[37]  = (*c >> 3) & 0x7;
-	xmc[38]  = *c++ & 0x7;
-
-	Nc[3]  = (*c >> 1) & 0x7F;
-	bc[3]  = (*c++ & 0x1) << 1;
-	bc[3] |= (*c >> 7) & 0x1;
-	Mc[3]  = (*c >> 5) & 0x3;
-	xmaxc[3]  = (*c++ & 0x1F) << 1;
-	xmaxc[3] |= (*c >> 7) & 0x1;
-
-	xmc[39]  = (*c >> 4) & 0x7;
-	xmc[40]  = (*c >> 1) & 0x7;
-	xmc[41]  = (*c++ & 0x1) << 2;
-	xmc[41] |= (*c >> 6) & 0x3;
-	xmc[42]  = (*c >> 3) & 0x7;
-	xmc[43]  = *c++ & 0x7;			/* 30  */
-	xmc[44]  = (*c >> 5) & 0x7;
-	xmc[45]  = (*c >> 2) & 0x7;
-	xmc[46]  = (*c++ & 0x3) << 1;
-	xmc[46] |= (*c >> 7) & 0x1;
-	xmc[47]  = (*c >> 4) & 0x7;
-	xmc[48]  = (*c >> 1) & 0x7;
-	xmc[49]  = (*c++ & 0x1) << 2;
-	xmc[49] |= (*c >> 6) & 0x3;
-	xmc[50]  = (*c >> 3) & 0x7;
-	xmc[51]  = *c & 0x7;			/* 33 */
-
-	fprintf(f,
-	      "LARc:\t%2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d\n",
-	       LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]);
-
-	fprintf(f, "#1: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
-		Nc[0], bc[0], Mc[0], xmaxc[0]);
-	fprintf(f,
-"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
-		xmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6],
-		xmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] );
-
-	fprintf(f, "#2: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
-		Nc[1], bc[1], Mc[1], xmaxc[1]);
-	fprintf(f,
-"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
-		xmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5],
-		xmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11],
-		xmc[13+12] );
-
-	fprintf(f, "#3: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
-		Nc[2], bc[2], Mc[2], xmaxc[2]);
-	fprintf(f,
-"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
-		xmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5],
-		xmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11],
-		xmc[26+12] );
-
-	fprintf(f, "#4: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
-		Nc[3], bc[3], Mc[3], xmaxc[3]);
-	fprintf(f,
-"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
-		xmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5],
-		xmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11],
-		xmc[39+12] );
-
-	return 0;
-}
diff --git a/libs/icucommon/Makefile.in b/libs/icucommon/Makefile.in
index 9944522da081..5c4b4b4c9636 100644
--- a/libs/icucommon/Makefile.in
+++ b/libs/icucommon/Makefile.in
@@ -10,15 +10,12 @@ SOURCES = \
 	bytestream.cpp \
 	bytestrie.cpp \
 	bytestriebuilder.cpp \
-	bytestrieiterator.cpp \
 	caniter.cpp \
 	characterproperties.cpp \
 	chariter.cpp \
 	charstr.cpp \
 	cmemory.cpp \
-	cstr.cpp \
 	cstring.cpp \
-	cwchar.cpp \
 	dictbe.cpp \
 	dictionarydata.cpp \
 	dtintrv.cpp \
@@ -28,7 +25,6 @@ SOURCES = \
 	filteredbrk.cpp \
 	filterednormalizer2.cpp \
 	icudataver.cpp \
-	icuplug.cpp \
 	loadednormalizer2impl.cpp \
 	localebuilder.cpp \
 	localematcher.cpp \
@@ -49,10 +45,8 @@ SOURCES = \
 	messagepattern.cpp \
 	normalizer2.cpp \
 	normalizer2impl.cpp \
-	normlzr.cpp \
 	parsepos.cpp \
 	patternprops.cpp \
-	pluralmap.cpp \
 	propname.cpp \
 	propsvec.cpp \
 	punycode.cpp \
@@ -67,9 +61,7 @@ SOURCES = \
 	rbbistbl.cpp \
 	rbbitblb.cpp \
 	resbund.cpp \
-	resbund_cnv.cpp \
 	resource.cpp \
-	restrace.cpp \
 	ruleiter.cpp \
 	schriter.cpp \
 	serv.cpp \
@@ -134,7 +126,6 @@ SOURCES = \
 	uenum.cpp \
 	uhash.cpp \
 	uhash_us.cpp \
-	uidna.cpp \
 	uinit.cpp \
 	uinvchar.cpp \
 	uiter.cpp \
@@ -160,7 +151,6 @@ SOURCES = \
 	unistr_cnv.cpp \
 	unistr_props.cpp \
 	unistr_titlecase_brkiter.cpp \
-	unorm.cpp \
 	unormcmp.cpp \
 	uobject.cpp \
 	uprefs.cpp \
@@ -168,7 +158,6 @@ SOURCES = \
 	ures_cnv.cpp \
 	uresbund.cpp \
 	uresdata.cpp \
-	usc_impl.cpp \
 	uscript.cpp \
 	uscript_props.cpp \
 	uset.cpp \
diff --git a/libs/icucommon/bytestrieiterator.cpp b/libs/icucommon/bytestrieiterator.cpp
deleted file mode 100644
index eacb7eedb0d6..000000000000
--- a/libs/icucommon/bytestrieiterator.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*   Copyright (C) 2010-2012, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*******************************************************************************
-*   file name:  bytestrieiterator.cpp
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 2010nov03
-*   created by: Markus W. Scherer
-*/
-
-#include "unicode/utypes.h"
-#include "unicode/bytestrie.h"
-#include "unicode/stringpiece.h"
-#include "charstr.h"
-#include "uvectr32.h"
-
-U_NAMESPACE_BEGIN
-
-BytesTrie::Iterator::Iterator(const void *trieBytes, int32_t maxStringLength,
-                              UErrorCode &errorCode)
-        : bytes_(static_cast<const uint8_t *>(trieBytes)),
-          pos_(bytes_), initialPos_(bytes_),
-          remainingMatchLength_(-1), initialRemainingMatchLength_(-1),
-          str_(NULL), maxLength_(maxStringLength), value_(0), stack_(NULL) {
-    if(U_FAILURE(errorCode)) {
-        return;
-    }
-    // str_ and stack_ are pointers so that it's easy to turn bytestrie.h into
-    // a public API header for which we would want it to depend only on
-    // other public headers.
-    // Unlike BytesTrie itself, its Iterator performs memory allocations anyway
-    // via the CharString and UVector32 implementations, so this additional
-    // cost is minimal.
-    str_=new CharString();
-    stack_=new UVector32(errorCode);
-    if(U_SUCCESS(errorCode) && (str_==NULL || stack_==NULL)) {
-        errorCode=U_MEMORY_ALLOCATION_ERROR;
-    }
-}
-
-BytesTrie::Iterator::Iterator(const BytesTrie &trie, int32_t maxStringLength,
-                              UErrorCode &errorCode)
-        : bytes_(trie.bytes_), pos_(trie.pos_), initialPos_(trie.pos_),
-          remainingMatchLength_(trie.remainingMatchLength_),
-          initialRemainingMatchLength_(trie.remainingMatchLength_),
-          str_(NULL), maxLength_(maxStringLength), value_(0), stack_(NULL) {
-    if(U_FAILURE(errorCode)) {
-        return;
-    }
-    str_=new CharString();
-    stack_=new UVector32(errorCode);
-    if(U_FAILURE(errorCode)) {
-        return;
-    }
-    if(str_==NULL || stack_==NULL) {
-        errorCode=U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    int32_t length=remainingMatchLength_;  // Actual remaining match length minus 1.
-    if(length>=0) {
-        // Pending linear-match node, append remaining bytes to str_.
-        ++length;
-        if(maxLength_>0 && length>maxLength_) {
-            length=maxLength_;  // This will leave remainingMatchLength>=0 as a signal.
-        }
-        str_->append(reinterpret_cast<const char *>(pos_), length, errorCode);
-        pos_+=length;
-        remainingMatchLength_-=length;
-    }
-}
-
-BytesTrie::Iterator::~Iterator() {
-    delete str_;
-    delete stack_;
-}
-
-BytesTrie::Iterator &
-BytesTrie::Iterator::reset() {
-    pos_=initialPos_;
-    remainingMatchLength_=initialRemainingMatchLength_;
-    int32_t length=remainingMatchLength_+1;  // Remaining match length.
-    if(maxLength_>0 && length>maxLength_) {
-        length=maxLength_;
-    }
-    str_->truncate(length);
-    pos_+=length;
-    remainingMatchLength_-=length;
-    stack_->setSize(0);
-    return *this;
-}
-
-UBool
-BytesTrie::Iterator::hasNext() const { return pos_!=NULL || !stack_->isEmpty(); }
-
-UBool
-BytesTrie::Iterator::next(UErrorCode &errorCode) {
-    if(U_FAILURE(errorCode)) {
-        return false;
-    }
-    const uint8_t *pos=pos_;
-    if(pos==NULL) {
-        if(stack_->isEmpty()) {
-            return false;
-        }
-        // Pop the state off the stack and continue with the next outbound edge of
-        // the branch node.
-        int32_t stackSize=stack_->size();
-        int32_t length=stack_->elementAti(stackSize-1);
-        pos=bytes_+stack_->elementAti(stackSize-2);
-        stack_->setSize(stackSize-2);
-        str_->truncate(length&0xffff);
-        length=(int32_t)((uint32_t)length>>16);
-        if(length>1) {
-            pos=branchNext(pos, length, errorCode);
-            if(pos==NULL) {
-                return true;  // Reached a final value.
-            }
-        } else {
-            str_->append((char)*pos++, errorCode);
-        }
-    }
-    if(remainingMatchLength_>=0) {
-        // We only get here if we started in a pending linear-match node
-        // with more than maxLength remaining bytes.
-        return truncateAndStop();
-    }
-    for(;;) {
-        int32_t node=*pos++;
-        if(node>=kMinValueLead) {
-            // Deliver value for the byte sequence so far.
-            UBool isFinal=(UBool)(node&kValueIsFinal);
-            value_=readValue(pos, node>>1);
-            if(isFinal || (maxLength_>0 && str_->length()==maxLength_)) {
-                pos_=NULL;
-            } else {
-                pos_=skipValue(pos, node);
-            }
-            return true;
-        }
-        if(maxLength_>0 && str_->length()==maxLength_) {
-            return truncateAndStop();
-        }
-        if(node<kMinLinearMatch) {
-            if(node==0) {
-                node=*pos++;
-            }
-            pos=branchNext(pos, node+1, errorCode);
-            if(pos==NULL) {
-                return true;  // Reached a final value.
-            }
-        } else {
-            // Linear-match node, append length bytes to str_.
-            int32_t length=node-kMinLinearMatch+1;
-            if(maxLength_>0 && str_->length()+length>maxLength_) {
-                str_->append(reinterpret_cast<const char *>(pos),
-                            maxLength_-str_->length(), errorCode);
-                return truncateAndStop();
-            }
-            str_->append(reinterpret_cast<const char *>(pos), length, errorCode);
-            pos+=length;
-        }
-    }
-}
-
-StringPiece
-BytesTrie::Iterator::getString() const {
-    return str_ == NULL ? StringPiece() : str_->toStringPiece();
-}
-
-UBool
-BytesTrie::Iterator::truncateAndStop() {
-    pos_=NULL;
-    value_=-1;  // no real value for str
-    return true;
-}
-
-// Branch node, needs to take the first outbound edge and push state for the rest.
-const uint8_t *
-BytesTrie::Iterator::branchNext(const uint8_t *pos, int32_t length, UErrorCode &errorCode) {
-    while(length>kMaxBranchLinearSubNodeLength) {
-        ++pos;  // ignore the comparison byte
-        // Push state for the greater-or-equal edge.
-        stack_->addElement((int32_t)(skipDelta(pos)-bytes_), errorCode);
-        stack_->addElement(((length-(length>>1))<<16)|str_->length(), errorCode);
-        // Follow the less-than edge.
-        length>>=1;
-        pos=jumpByDelta(pos);
-    }
-    // List of key-value pairs where values are either final values or jump deltas.
-    // Read the first (key, value) pair.
-    uint8_t trieByte=*pos++;
-    int32_t node=*pos++;
-    UBool isFinal=(UBool)(node&kValueIsFinal);
-    int32_t value=readValue(pos, node>>1);
-    pos=skipValue(pos, node);
-    stack_->addElement((int32_t)(pos-bytes_), errorCode);
-    stack_->addElement(((length-1)<<16)|str_->length(), errorCode);
-    str_->append((char)trieByte, errorCode);
-    if(isFinal) {
-        pos_=NULL;
-        value_=value;
-        return NULL;
-    } else {
-        return pos+value;
-    }
-}
-
-U_NAMESPACE_END
diff --git a/libs/icucommon/cstr.cpp b/libs/icucommon/cstr.cpp
deleted file mode 100644
index 0114434329ad..000000000000
--- a/libs/icucommon/cstr.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*   Copyright (C) 2015-2016, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*******************************************************************************
-*   file name:  charstr.cpp
-*/
-#include "unicode/utypes.h"
-#include "unicode/putil.h"
-#include "unicode/unistr.h"
-
-#include "cstr.h"
-
-#include "charstr.h"
-#include "uinvchar.h"
-
-U_NAMESPACE_BEGIN
-
-CStr::CStr(const UnicodeString &in) {
-    UErrorCode status = U_ZERO_ERROR;
-#if !UCONFIG_NO_CONVERSION || U_CHARSET_IS_UTF8
-    int32_t length = in.extract(0, in.length(), static_cast<char *>(NULL), static_cast<uint32_t>(0));
-    int32_t resultCapacity = 0;
-    char *buf = s.getAppendBuffer(length, length, resultCapacity, status);
-    if (U_SUCCESS(status)) {
-        in.extract(0, in.length(), buf, resultCapacity);
-        s.append(buf, length, status);
-    }
-#else
-    // No conversion available. Convert any invariant characters; substitute '?' for the rest.
-    // Note: can't just call u_UCharsToChars() or CharString.appendInvariantChars() on the
-    //       whole string because they require that the entire input be invariant.
-    char buf[2];
-    for (int i=0; i<in.length(); i = in.moveIndex32(i, 1)) {
-        if (uprv_isInvariantUString(in.getBuffer()+i, 1)) {
-            u_UCharsToChars(in.getBuffer()+i, buf, 1);
-        } else {
-            buf[0] = '?';
-        }
-        s.append(buf, 1, status);
-    }
-#endif
-}
-
-CStr::~CStr() {
-}
-
-const char * CStr::operator ()() const {
-    return s.data();
-}
-
-U_NAMESPACE_END
diff --git a/libs/icucommon/cwchar.cpp b/libs/icucommon/cwchar.cpp
deleted file mode 100644
index 4fd531114e8d..000000000000
--- a/libs/icucommon/cwchar.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-******************************************************************************
-*
-*   Copyright (C) 2001, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-******************************************************************************
-*   file name:  cwchar.c
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 2001may25
-*   created by: Markus W. Scherer
-*/
-
-#include "unicode/utypes.h"
-
-#if !U_HAVE_WCSCPY
-
-#include "cwchar.h"
-
-U_CAPI wchar_t *uprv_wcscat(wchar_t *dst, const wchar_t *src) {
-    wchar_t *start=dst;
-    while(*dst!=0) {
-        ++dst;
-    }
-    while((*dst=*src)!=0) {
-        ++dst;
-        ++src;
-    }
-    return start;
-}
-
-U_CAPI wchar_t *uprv_wcscpy(wchar_t *dst, const wchar_t *src) {
-    wchar_t *start=dst;
-    while((*dst=*src)!=0) {
-        ++dst;
-        ++src;
-    }
-    return start;
-}
-
-U_CAPI size_t uprv_wcslen(const wchar_t *src) {
-    const wchar_t *start=src;
-    while(*src!=0) {
-        ++src;
-    }
-    return src-start;
-}
-
-#endif
diff --git a/libs/icucommon/icuplug.cpp b/libs/icucommon/icuplug.cpp
deleted file mode 100644
index a13b5f7faf33..000000000000
--- a/libs/icucommon/icuplug.cpp
+++ /dev/null
@@ -1,882 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-******************************************************************************
-*
-*   Copyright (C) 2009-2015, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-******************************************************************************
-*
-*  FILE NAME : icuplug.c
-*
-*   Date         Name        Description
-*   10/29/2009   sl          New.
-******************************************************************************
-*/
-
-#include "unicode/icuplug.h"
-
-
-#if UCONFIG_ENABLE_PLUGINS
-
-
-#include "icuplugimp.h"
-#include "cstring.h"
-#include "cmemory.h"
-#include "putilimp.h"
-#include "ucln.h"
-#include <stdio.h>
-#ifdef __MVS__  /* defined by z/OS compiler */
-#define _POSIX_SOURCE
-#include <cics.h> /* 12 Nov 2011 JAM iscics() function */
-#endif
-#include "charstr.h"
-
-using namespace icu;
-
-#ifndef UPLUG_TRACE
-#define UPLUG_TRACE 0
-#endif
-
-#if UPLUG_TRACE
-#include <stdio.h>
-#define DBG(x) fprintf(stderr, "%s:%d: ",__FILE__,__LINE__); fprintf x
-#endif
-
-/**
- * Internal structure of an ICU plugin.
- */
-
-struct UPlugData {
-  UPlugEntrypoint  *entrypoint; /**< plugin entrypoint */
-  uint32_t structSize;    /**< initialized to the size of this structure */
-  uint32_t token;         /**< must be U_PLUG_TOKEN */
-  void *lib;              /**< plugin library, or NULL */
-  char libName[UPLUG_NAME_MAX];   /**< library name */
-  char sym[UPLUG_NAME_MAX];        /**< plugin symbol, or NULL */
-  char config[UPLUG_NAME_MAX];     /**< configuration data */
-  void *context;          /**< user context data */
-  char name[UPLUG_NAME_MAX];   /**< name of plugin */
-  UPlugLevel  level; /**< level of plugin */
-  UBool   awaitingLoad; /**< true if the plugin is awaiting a load call */
-  UBool   dontUnload; /**< true if plugin must stay resident (leak plugin and lib) */
-  UErrorCode pluginStatus; /**< status code of plugin */
-};
-
-
-
-#define UPLUG_LIBRARY_INITIAL_COUNT 8
-#define UPLUG_PLUGIN_INITIAL_COUNT 12
-
-/**
- * Remove an item
- * @param list the full list
- * @param listSize the number of entries in the list
- * @param memberSize the size of one member
- * @param itemToRemove the item number of the member
- * @return the new listsize
- */
-static int32_t uplug_removeEntryAt(void *list, int32_t listSize, int32_t memberSize, int32_t itemToRemove) {
-  uint8_t *bytePtr = (uint8_t *)list;
-
-  /* get rid of some bad cases first */
-  if(listSize<1) {
-    return listSize;
-  }
-
-  /* is there anything to move? */
-  if(listSize > itemToRemove+1) {
-    memmove(bytePtr+(itemToRemove*memberSize), bytePtr+((itemToRemove+1)*memberSize), memberSize);
-  }
-
-  return listSize-1;
-}
-
-
-
-
-#if U_ENABLE_DYLOAD
-/**
- * Library management. Internal.
- * @internal
- */
-struct UPlugLibrary;
-
-/**
- * Library management. Internal.
- * @internal
- */
-typedef struct UPlugLibrary {
-  void *lib;                           /**< library ptr */
-  char name[UPLUG_NAME_MAX]; /**< library name */
-  uint32_t ref;                        /**< reference count */
-} UPlugLibrary;
-
-static UPlugLibrary   staticLibraryList[UPLUG_LIBRARY_INITIAL_COUNT];
-static UPlugLibrary * libraryList = staticLibraryList;
-static int32_t libraryCount = 0;
-static int32_t libraryMax = UPLUG_LIBRARY_INITIAL_COUNT;
-
-/**
- * Search for a library. Doesn't lock
- * @param libName libname to search for
- * @return the library's struct
- */
-static int32_t searchForLibraryName(const char *libName) {
-  int32_t i;
-
-  for(i=0;i<libraryCount;i++) {
-    if(!uprv_strcmp(libName, libraryList[i].name)) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-static int32_t searchForLibrary(void *lib) {
-  int32_t i;
-
-  for(i=0;i<libraryCount;i++) {
-    if(lib==libraryList[i].lib) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-U_CAPI char * U_EXPORT2
-uplug_findLibrary(void *lib, UErrorCode *status) {
-  int32_t libEnt;
-  char *ret = NULL;
-  if(U_FAILURE(*status)) {
-    return NULL;
-  }
-  libEnt = searchForLibrary(lib);
-  if(libEnt!=-1) {
-    ret = libraryList[libEnt].name;
-  } else {
-    *status = U_MISSING_RESOURCE_ERROR;
-  }
-  return ret;
-}
-
-U_CAPI void * U_EXPORT2
-uplug_openLibrary(const char *libName, UErrorCode *status) {
-  int32_t libEntry = -1;
-  void *lib = NULL;
-
-  if(U_FAILURE(*status)) return NULL;
-
-  libEntry = searchForLibraryName(libName);
-  if(libEntry == -1) {
-    libEntry = libraryCount++;
-    if(libraryCount >= libraryMax) {
-      /* Ran out of library slots. Statically allocated because we can't depend on allocating memory.. */
-      *status = U_MEMORY_ALLOCATION_ERROR;
-#if UPLUG_TRACE
-      DBG((stderr, "uplug_openLibrary() - out of library slots (max %d)\n", libraryMax));
-#endif
-      return NULL;
-    }
-    /* Some operating systems don't want
-       DL operations from multiple threads. */
-    libraryList[libEntry].lib = uprv_dl_open(libName, status);
-#if UPLUG_TRACE
-    DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib));
-#endif
-
-    if(libraryList[libEntry].lib == NULL || U_FAILURE(*status)) {
-      /* cleanup. */
-      libraryList[libEntry].lib = NULL; /* failure with open */
-      libraryList[libEntry].name[0] = 0;
-#if UPLUG_TRACE
-      DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib));
-#endif
-      /* no need to free - just won't increase the count. */
-      libraryCount--;
-    } else { /* is it still there? */
-      /* link it in */
-      uprv_strncpy(libraryList[libEntry].name,libName,UPLUG_NAME_MAX);
-      libraryList[libEntry].ref=1;
-      lib = libraryList[libEntry].lib;
-    }
-
-  } else {
-    lib = libraryList[libEntry].lib;
-    libraryList[libEntry].ref++;
-  }
-  return lib;
-}
-
-U_CAPI void U_EXPORT2
-uplug_closeLibrary(void *lib, UErrorCode *status) {
-  int32_t i;
-
-#if UPLUG_TRACE
-  DBG((stderr, "uplug_closeLibrary(%p,%s) list %p\n", lib, u_errorName(*status), (void*)libraryList));
-#endif
-  if(U_FAILURE(*status)) return;
-
-  for(i=0;i<libraryCount;i++) {
-    if(lib==libraryList[i].lib) {
-      if(--(libraryList[i].ref) == 0) {
-        uprv_dl_close(libraryList[i].lib, status);
-        libraryCount = uplug_removeEntryAt(libraryList, libraryCount, sizeof(*libraryList), i);
-      }
-      return;
-    }
-  }
-  *status = U_INTERNAL_PROGRAM_ERROR; /* could not find the entry! */
-}
-
-#endif
-
-static UPlugData pluginList[UPLUG_PLUGIN_INITIAL_COUNT];
-static int32_t pluginCount = 0;
-
-
-
-
-static int32_t uplug_pluginNumber(UPlugData* d) {
-  UPlugData *pastPlug = &pluginList[pluginCount];
-  if(d<=pluginList) {
-    return 0;
-  } else if(d>=pastPlug) {
-    return pluginCount;
-  } else {
-    return (d-pluginList)/sizeof(pluginList[0]);
-  }
-}
-
-
-U_CAPI UPlugData * U_EXPORT2
-uplug_nextPlug(UPlugData *prior) {
-  if(prior==NULL) {
-    return pluginList;
-  } else {
-    UPlugData *nextPlug = &prior[1];
-    UPlugData *pastPlug = &pluginList[pluginCount];
-
-    if(nextPlug>=pastPlug) {
-      return NULL;
-    } else {
-      return nextPlug;
-    }
-  }
-}
-
-
-
-/**
- * Call the plugin with some params
- */
-static void uplug_callPlug(UPlugData *plug, UPlugReason reason, UErrorCode *status) {
-  UPlugTokenReturn token;
-  if(plug==NULL||U_FAILURE(*status)) {
-    return;
-  }
-  token = (*(plug->entrypoint))(plug, reason, status);
-  if(token!=UPLUG_TOKEN) {
-    *status = U_INTERNAL_PROGRAM_ERROR;
-  }
-}
-
-
-static void uplug_unloadPlug(UPlugData *plug, UErrorCode *status) {
-  if(plug->awaitingLoad) {  /* shouldn't happen. Plugin hasn't been loaded yet.*/
-    *status = U_INTERNAL_PROGRAM_ERROR;
-    return;
-  }
-  if(U_SUCCESS(plug->pluginStatus)) {
-    /* Don't unload a plug which has a failing load status - means it didn't actually load. */
-    uplug_callPlug(plug, UPLUG_REASON_UNLOAD, status);
-  }
-}
-
-static void uplug_queryPlug(UPlugData *plug, UErrorCode *status) {
-  if(!plug->awaitingLoad || !(plug->level == UPLUG_LEVEL_UNKNOWN) ) {  /* shouldn't happen. Plugin hasn't been loaded yet.*/
-    *status = U_INTERNAL_PROGRAM_ERROR;
-    return;
-  }
-  plug->level = UPLUG_LEVEL_INVALID;
-  uplug_callPlug(plug, UPLUG_REASON_QUERY, status);
-  if(U_SUCCESS(*status)) {
-    if(plug->level == UPLUG_LEVEL_INVALID) {
-      plug->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL;
-      plug->awaitingLoad = false;
-    }
-  } else {
-    plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
-    plug->awaitingLoad = false;
-  }
-}
-
-
-static void uplug_loadPlug(UPlugData *plug, UErrorCode *status) {
-  if(U_FAILURE(*status)) {
-    return;
-  }
-  if(!plug->awaitingLoad || (plug->level < UPLUG_LEVEL_LOW) ) {  /* shouldn't happen. Plugin hasn't been loaded yet.*/
-    *status = U_INTERNAL_PROGRAM_ERROR;
-    return;
-  }
-  uplug_callPlug(plug, UPLUG_REASON_LOAD, status);
-  plug->awaitingLoad = false;
-  if(!U_SUCCESS(*status)) {
-    plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
-  }
-}
-
-static UPlugData *uplug_allocateEmptyPlug(UErrorCode *status)
-{
-  UPlugData *plug = NULL;
-
-  if(U_FAILURE(*status)) {
-    return NULL;
-  }
-
-  if(pluginCount == UPLUG_PLUGIN_INITIAL_COUNT) {
-    *status = U_MEMORY_ALLOCATION_ERROR;
-    return NULL;
-  }
-
-  plug = &pluginList[pluginCount++];
-
-  plug->token = UPLUG_TOKEN;
-  plug->structSize = sizeof(UPlugData);
-  plug->name[0]=0;
-  plug->level = UPLUG_LEVEL_UNKNOWN; /* initialize to null state */
-  plug->awaitingLoad = true;
-  plug->dontUnload = false;
-  plug->pluginStatus = U_ZERO_ERROR;
-  plug->libName[0] = 0;
-  plug->config[0]=0;
-  plug->sym[0]=0;
-  plug->lib=NULL;
-  plug->entrypoint=NULL;
-
-
-  return plug;
-}
-
-static UPlugData *uplug_allocatePlug(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *symName,
-                                     UErrorCode *status) {
-  UPlugData *plug = uplug_allocateEmptyPlug(status);
-  if(U_FAILURE(*status)) {
-    return NULL;
-  }
-
-  if(config!=NULL) {
-    uprv_strncpy(plug->config, config, UPLUG_NAME_MAX);
-  } else {
-    plug->config[0] = 0;
-  }
-
-  if(symName!=NULL) {
-    uprv_strncpy(plug->sym, symName, UPLUG_NAME_MAX);
-  } else {
-    plug->sym[0] = 0;
-  }
-
-  plug->entrypoint = entrypoint;
-  plug->lib = lib;
-  uplug_queryPlug(plug, status);
-
-  return plug;
-}
-
-static void uplug_deallocatePlug(UPlugData *plug, UErrorCode *status) {
-  UErrorCode subStatus = U_ZERO_ERROR;
-  if(!plug->dontUnload) {
-#if U_ENABLE_DYLOAD
-    uplug_closeLibrary(plug->lib, &subStatus);
-#endif
-  }
-  plug->lib = NULL;
-  if(U_SUCCESS(*status) && U_FAILURE(subStatus)) {
-    *status = subStatus;
-  }
-  /* shift plugins up and decrement count. */
-  if(U_SUCCESS(*status)) {
-    /* all ok- remove. */
-    pluginCount = uplug_removeEntryAt(pluginList, pluginCount, sizeof(plug[0]), uplug_pluginNumber(plug));
-  } else {
-    /* not ok- leave as a message. */
-    plug->awaitingLoad=false;
-    plug->entrypoint=0;
-    plug->dontUnload=true;
-  }
-}
-
-static void uplug_doUnloadPlug(UPlugData *plugToRemove, UErrorCode *status) {
-  if(plugToRemove != NULL) {
-    uplug_unloadPlug(plugToRemove, status);
-    uplug_deallocatePlug(plugToRemove, status);
-  }
-}
-
-U_CAPI void U_EXPORT2
-uplug_removePlug(UPlugData *plug, UErrorCode *status)  {
-  UPlugData *cursor = NULL;
-  UPlugData *plugToRemove = NULL;
-  if(U_FAILURE(*status)) return;
-
-  for(cursor=pluginList;cursor!=NULL;) {
-    if(cursor==plug) {
-      plugToRemove = plug;
-      cursor=NULL;
-    } else {
-      cursor = uplug_nextPlug(cursor);
-    }
-  }
-
-  uplug_doUnloadPlug(plugToRemove, status);
-}
-
-
-
-
-U_CAPI void U_EXPORT2
-uplug_setPlugNoUnload(UPlugData *data, UBool dontUnload)
-{
-  data->dontUnload = dontUnload;
-}
-
-
-U_CAPI void U_EXPORT2
-uplug_setPlugLevel(UPlugData *data, UPlugLevel level) {
-  data->level = level;
-}
-
-
-U_CAPI UPlugLevel U_EXPORT2
-uplug_getPlugLevel(UPlugData *data) {
-  return data->level;
-}
-
-
-U_CAPI void U_EXPORT2
-uplug_setPlugName(UPlugData *data, const char *name) {
-  uprv_strncpy(data->name, name, UPLUG_NAME_MAX);
-}
-
-
-U_CAPI const char * U_EXPORT2
-uplug_getPlugName(UPlugData *data) {
-  return data->name;
-}
-
-
-U_CAPI const char * U_EXPORT2
-uplug_getSymbolName(UPlugData *data) {
-  return data->sym;
-}
-
-U_CAPI const char * U_EXPORT2
-uplug_getLibraryName(UPlugData *data, UErrorCode *status) {
-  if(data->libName[0]) {
-    return data->libName;
-  } else {
-#if U_ENABLE_DYLOAD
-    return uplug_findLibrary(data->lib, status);
-#else
-    return NULL;
-#endif
-  }
-}
-
-U_CAPI void * U_EXPORT2
-uplug_getLibrary(UPlugData *data) {
-  return data->lib;
-}
-
-U_CAPI void * U_EXPORT2
-uplug_getContext(UPlugData *data) {
-  return data->context;
-}
-
-
-U_CAPI void U_EXPORT2
-uplug_setContext(UPlugData *data, void *context) {
-  data->context = context;
-}
-
-U_CAPI const char* U_EXPORT2
-uplug_getConfiguration(UPlugData *data) {
-  return data->config;
-}
-
-U_CAPI UPlugData* U_EXPORT2
-uplug_getPlugInternal(int32_t n) {
-  if(n <0 || n >= pluginCount) {
-    return NULL;
-  } else {
-    return &(pluginList[n]);
-  }
-}
-
-
-U_CAPI UErrorCode U_EXPORT2
-uplug_getPlugLoadStatus(UPlugData *plug) {
-  return plug->pluginStatus;
-}
-
-
-
-
-/**
- * Initialize a plugin from an entrypoint and library - but don't load it.
- */
-static UPlugData* uplug_initPlugFromEntrypointAndLibrary(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *sym,
-                                                         UErrorCode *status) {
-  UPlugData *plug = NULL;
-
-  plug = uplug_allocatePlug(entrypoint, config, lib, sym, status);
-
-  if(U_SUCCESS(*status)) {
-    return plug;
-  } else {
-    uplug_deallocatePlug(plug, status);
-    return NULL;
-  }
-}
-
-U_CAPI UPlugData* U_EXPORT2
-uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status) {
-  UPlugData* plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, NULL, NULL, status);
-  uplug_loadPlug(plug, status);
-  return plug;
-}
-
-#if U_ENABLE_DYLOAD
-
-static UPlugData*
-uplug_initErrorPlug(const char *libName, const char *sym, const char *config, const char *nameOrError, UErrorCode loadStatus, UErrorCode *status)
-{
-  UPlugData *plug = uplug_allocateEmptyPlug(status);
-  if(U_FAILURE(*status)) return NULL;
-
-  plug->pluginStatus = loadStatus;
-  plug->awaitingLoad = false; /* Won't load. */
-  plug->dontUnload = true; /* cannot unload. */
-
-  if(sym!=NULL) {
-    uprv_strncpy(plug->sym, sym, UPLUG_NAME_MAX);
-  }
-
-  if(libName!=NULL) {
-    uprv_strncpy(plug->libName, libName, UPLUG_NAME_MAX);
-  }
-
-  if(nameOrError!=NULL) {
-    uprv_strncpy(plug->name, nameOrError, UPLUG_NAME_MAX);
-  }
-
-  if(config!=NULL) {
-    uprv_strncpy(plug->config, config, UPLUG_NAME_MAX);
-  }
-
-  return plug;
-}
-
-/**
- * Fetch a plugin from DLL, and then initialize it from a library- but don't load it.
- */
-static UPlugData*
-uplug_initPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) {
-  void *lib = NULL;
-  UPlugData *plug = NULL;
-  if(U_FAILURE(*status)) { return NULL; }
-  lib = uplug_openLibrary(libName, status);
-  if(lib!=NULL && U_SUCCESS(*status)) {
-    UPlugEntrypoint *entrypoint = NULL;
-    entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status);
-
-    if(entrypoint!=NULL&&U_SUCCESS(*status)) {
-      plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, lib, sym, status);
-      if(plug!=NULL&&U_SUCCESS(*status)) {
-        plug->lib = lib; /* plug takes ownership of library */
-        lib = NULL; /* library is now owned by plugin. */
-      }
-    } else {
-      UErrorCode subStatus = U_ZERO_ERROR;
-      plug = uplug_initErrorPlug(libName,sym,config,"ERROR: Could not load entrypoint",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus);
-    }
-    if(lib!=NULL) { /* still need to close the lib */
-      UErrorCode subStatus = U_ZERO_ERROR;
-      uplug_closeLibrary(lib, &subStatus); /* don't care here */
-    }
-  } else {
-    UErrorCode subStatus = U_ZERO_ERROR;
-    plug = uplug_initErrorPlug(libName,sym,config,"ERROR: could not load library",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus);
-  }
-  return plug;
-}
-
-U_CAPI UPlugData* U_EXPORT2
-uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) {
-  UPlugData *plug = NULL;
-  if(U_FAILURE(*status)) { return NULL; }
-  plug = uplug_initPlugFromLibrary(libName, sym, config, status);
-  uplug_loadPlug(plug, status);
-
-  return plug;
-}
-
-#endif
-
-static UPlugLevel gCurrentLevel = UPLUG_LEVEL_LOW;
-
-U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() {
-  return gCurrentLevel;
-}
-
-static UBool U_CALLCONV uplug_cleanup(void)
-{
-  int32_t i;
-
-  UPlugData *pluginToRemove;
-  /* cleanup plugs */
-  for(i=0;i<pluginCount;i++) {
-    UErrorCode subStatus = U_ZERO_ERROR;
-    pluginToRemove = &pluginList[i];
-    /* unload and deallocate */
-    uplug_doUnloadPlug(pluginToRemove, &subStatus);
-  }
-  /* close other held libs? */
-  gCurrentLevel = UPLUG_LEVEL_LOW;
-  return true;
-}
-
-#if U_ENABLE_DYLOAD
-
-static void uplug_loadWaitingPlugs(UErrorCode *status) {
-  int32_t i;
-  UPlugLevel currentLevel = uplug_getCurrentLevel();
-
-  if(U_FAILURE(*status)) {
-    return;
-  }
-#if UPLUG_TRACE
-  DBG((stderr,  "uplug_loadWaitingPlugs() Level: %d\n", currentLevel));
-#endif
-  /* pass #1: low level plugs */
-  for(i=0;i<pluginCount;i++) {
-    UErrorCode subStatus = U_ZERO_ERROR;
-    UPlugData *pluginToLoad = &pluginList[i];
-    if(pluginToLoad->awaitingLoad) {
-      if(pluginToLoad->level == UPLUG_LEVEL_LOW) {
-        if(currentLevel > UPLUG_LEVEL_LOW) {
-          pluginToLoad->pluginStatus = U_PLUGIN_TOO_HIGH;
-        } else {
-          UPlugLevel newLevel;
-          uplug_loadPlug(pluginToLoad, &subStatus);
-          newLevel = uplug_getCurrentLevel();
-          if(newLevel > currentLevel) {
-            pluginToLoad->pluginStatus = U_PLUGIN_CHANGED_LEVEL_WARNING;
-            currentLevel = newLevel;
-          }
-        }
-        pluginToLoad->awaitingLoad = false;
-      }
-    }
-  }
-  for(i=0;i<pluginCount;i++) {
-    UErrorCode subStatus = U_ZERO_ERROR;
-    UPlugData *pluginToLoad = &pluginList[i];
-
-    if(pluginToLoad->awaitingLoad) {
-      if(pluginToLoad->level == UPLUG_LEVEL_INVALID) {
-        pluginToLoad->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL;
-      } else if(pluginToLoad->level == UPLUG_LEVEL_UNKNOWN) {
-        pluginToLoad->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
-      } else {
-        uplug_loadPlug(pluginToLoad, &subStatus);
-      }
-      pluginToLoad->awaitingLoad = false;
-    }
-  }
-
-#if UPLUG_TRACE
-  DBG((stderr,  " Done Loading Plugs. Level: %d\n", (int32_t)uplug_getCurrentLevel()));
-#endif
-}
-
-/* Name of the plugin config file */
-static char plugin_file[2048] = "";
-#endif
-
-U_CAPI const char* U_EXPORT2
-uplug_getPluginFile() {
-#if U_ENABLE_DYLOAD && !UCONFIG_NO_FILE_IO
-  return plugin_file;
-#else
-  return NULL;
-#endif
-}
-
-
-//  uplug_init()  is called first thing from u_init().
-
-U_CAPI void U_EXPORT2
-uplug_init(UErrorCode *status) {
-#if !U_ENABLE_DYLOAD
-  (void)status; /* unused */
-#elif !UCONFIG_NO_FILE_IO
-  CharString plugin_dir;
-  const char *env = getenv("ICU_PLUGINS");
-
-  if(U_FAILURE(*status)) return;
-  if(env != NULL) {
-    plugin_dir.append(env, -1, *status);
-  }
-  if(U_FAILURE(*status)) return;
-
-#if defined(DEFAULT_ICU_PLUGINS)
-  if(plugin_dir.isEmpty()) {
-    plugin_dir.append(DEFAULT_ICU_PLUGINS, -1, *status);
-  }
-#endif
-
-#if UPLUG_TRACE
-  DBG((stderr, "ICU_PLUGINS=%s\n", plugin_dir.data()));
-#endif
-
-  if(!plugin_dir.isEmpty()) {
-    FILE *f;
-
-    CharString pluginFile;
-#ifdef OS390BATCH
-/* There are potentially a lot of ways to implement a plugin directory on OS390/zOS  */
-/* Keeping in mind that unauthorized file access is logged, monitored, and enforced  */
-/* I've chosen to open a DDNAME if BATCH and leave it alone for (presumably) UNIX    */
-/* System Services.  Alternative techniques might be allocating a member in          */
-/* SYS1.PARMLIB or setting an environment variable "ICU_PLUGIN_PATH" (?).  The       */
-/* DDNAME can be connected to a file in the HFS if need be.                          */
-
-    pluginFile.append("//DD:ICUPLUG", -1, *status);        /* JAM 20 Oct 2011 */
-#else
-    pluginFile.append(plugin_dir, *status);
-    pluginFile.append(U_FILE_SEP_STRING, -1, *status);
-    pluginFile.append("icuplugins", -1, *status);
-    pluginFile.append(U_ICU_VERSION_SHORT, -1, *status);
-    pluginFile.append(".txt", -1, *status);
-#endif
-
-#if UPLUG_TRACE
-    DBG((stderr, "status=%s\n", u_errorName(*status)));
-#endif
-
-    if(U_FAILURE(*status)) {
-      return;
-    }
-    if((size_t)pluginFile.length() > (sizeof(plugin_file)-1)) {
-      *status = U_BUFFER_OVERFLOW_ERROR;
-#if UPLUG_TRACE
-      DBG((stderr, "status=%s\n", u_errorName(*status)));
-#endif
-      return;
-    }
-
-    /* plugin_file is not used for processing - it is only used
-       so that uplug_getPluginFile() works (i.e. icuinfo)
-    */
-    pluginFile.extract(plugin_file, sizeof(plugin_file), *status);
-
-#if UPLUG_TRACE
-    DBG((stderr, "pluginfile= %s len %d/%d\n", plugin_file, (int)strlen(plugin_file), (int)sizeof(plugin_file)));
-#endif
-
-#ifdef __MVS__
-    if (iscics()) /* 12 Nov 2011 JAM */
-    {
-        f = NULL;
-    }
-    else
-#endif
-    {
-        f = fopen(pluginFile.data(), "r");
-    }
-
-    if(f != NULL) {
-      char linebuf[1024];
-      char *p, *libName=NULL, *symName=NULL, *config=NULL;
-      int32_t line = 0;
-
-
-      while(fgets(linebuf,1023,f)) {
-        line++;
-
-        if(!*linebuf || *linebuf=='#') {
-          continue;
-        } else {
-          p = linebuf;
-          while(*p&&isspace((int)*p))
-            p++;
-          if(!*p || *p=='#') continue;
-          libName = p;
-          while(*p&&!isspace((int)*p)) {
-            p++;
-          }
-          if(!*p || *p=='#') continue; /* no tab after libname */
-          *p=0; /* end of libname */
-          p++;
-          while(*p&&isspace((int)*p)) {
-            p++;
-          }
-          if(!*p||*p=='#') continue; /* no symname after libname +tab */
-          symName = p;
-          while(*p&&!isspace((int)*p)) {
-            p++;
-          }
-
-          if(*p) { /* has config */
-            *p=0;
-            ++p;
-            while(*p&&isspace((int)*p)) {
-              p++;
-            }
-            if(*p) {
-              config = p;
-            }
-          }
-
-          /* chop whitespace at the end of the config */
-          if(config!=NULL&&*config!=0) {
-            p = config+strlen(config);
-            while(p>config&&isspace((int)*(--p))) {
-              *p=0;
-            }
-          }
-
-          /* OK, we're good. */
-          {
-            UErrorCode subStatus = U_ZERO_ERROR;
-            UPlugData *plug = uplug_initPlugFromLibrary(libName, symName, config, &subStatus);
-            if(U_FAILURE(subStatus) && U_SUCCESS(*status)) {
-              *status = subStatus;
-            }
-#if UPLUG_TRACE
-            DBG((stderr, "PLUGIN libName=[%s], sym=[%s], config=[%s]\n", libName, symName, config));
-            DBG((stderr, " -> %p, %s\n", (void*)plug, u_errorName(subStatus)));
-#else
-            (void)plug; /* unused */
-#endif
-          }
-        }
-      }
-      fclose(f);
-    } else {
-#if UPLUG_TRACE
-      DBG((stderr, "Can't open plugin file %s\n", plugin_file));
-#endif
-    }
-  }
-  uplug_loadWaitingPlugs(status);
-#endif /* U_ENABLE_DYLOAD */
-  gCurrentLevel = UPLUG_LEVEL_HIGH;
-  ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup);
-}
-
-#endif
diff --git a/libs/icucommon/normlzr.cpp b/libs/icucommon/normlzr.cpp
deleted file mode 100644
index 2bb45a4fcd0f..000000000000
--- a/libs/icucommon/normlzr.cpp
+++ /dev/null
@@ -1,529 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *************************************************************************
- * COPYRIGHT:
- * Copyright (c) 1996-2012, International Business Machines Corporation and
- * others. All Rights Reserved.
- *************************************************************************
- */
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_NORMALIZATION
-
-#include "unicode/uniset.h"
-#include "unicode/unistr.h"
-#include "unicode/chariter.h"
-#include "unicode/schriter.h"
-#include "unicode/uchriter.h"
-#include "unicode/normlzr.h"
-#include "unicode/utf16.h"
-#include "cmemory.h"
-#include "normalizer2impl.h"
-#include "uprops.h"  // for uniset_getUnicode32Instance()
-
-#if defined(move32)
- // System can define move32 intrinsics, but the char iters define move32 method
- // using same undef trick in headers, so undef here to re-enable the method.
-#undef move32
-#endif
-
-U_NAMESPACE_BEGIN
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Normalizer)
-
-//-------------------------------------------------------------------------
-// Constructors and other boilerplate
-//-------------------------------------------------------------------------
-
-Normalizer::Normalizer(const UnicodeString& str, UNormalizationMode mode) :
-    UObject(), fFilteredNorm2(NULL), fNorm2(NULL), fUMode(mode), fOptions(0),
-    text(new StringCharacterIterator(str)),
-    currentIndex(0), nextIndex(0),
-    buffer(), bufferPos(0)
-{
-    init();
-}
-
-Normalizer::Normalizer(ConstChar16Ptr str, int32_t length, UNormalizationMode mode) :
-    UObject(), fFilteredNorm2(NULL), fNorm2(NULL), fUMode(mode), fOptions(0),
-    text(new UCharCharacterIterator(str, length)),
-    currentIndex(0), nextIndex(0),
-    buffer(), bufferPos(0)
-{
-    init();
-}
-
-Normalizer::Normalizer(const CharacterIterator& iter, UNormalizationMode mode) :
-    UObject(), fFilteredNorm2(NULL), fNorm2(NULL), fUMode(mode), fOptions(0),
-    text(iter.clone()),
-    currentIndex(0), nextIndex(0),
-    buffer(), bufferPos(0)
-{
-    init();
-}
-
-Normalizer::Normalizer(const Normalizer &copy) :
-    UObject(copy), fFilteredNorm2(NULL), fNorm2(NULL), fUMode(copy.fUMode), fOptions(copy.fOptions),
-    text(copy.text->clone()),
-    currentIndex(copy.currentIndex), nextIndex(copy.nextIndex),
-    buffer(copy.buffer), bufferPos(copy.bufferPos)
-{
-    init();
-}
-
-void
-Normalizer::init() {
-    UErrorCode errorCode=U_ZERO_ERROR;
-    fNorm2=Normalizer2Factory::getInstance(fUMode, errorCode);
-    if(fOptions&UNORM_UNICODE_3_2) {
-        delete fFilteredNorm2;
-        fNorm2=fFilteredNorm2=
-            new FilteredNormalizer2(*fNorm2, *uniset_getUnicode32Instance(errorCode));
-    }
-    if(U_FAILURE(errorCode)) {
-        errorCode=U_ZERO_ERROR;
-        fNorm2=Normalizer2Factory::getNoopInstance(errorCode);
-    }
-}
-
-Normalizer::~Normalizer()
-{
-    delete fFilteredNorm2;
-    delete text;
-}
-
-Normalizer*
-Normalizer::clone() const
-{
-    return new Normalizer(*this);
-}
-
-/**
- * Generates a hash code for this iterator.
- */
-int32_t Normalizer::hashCode() const
-{
-    return text->hashCode() + fUMode + fOptions + buffer.hashCode() + bufferPos + currentIndex + nextIndex;
-}
-
-bool Normalizer::operator==(const Normalizer& that) const
-{
-    return
-        this==&that ||
-        (fUMode==that.fUMode &&
-        fOptions==that.fOptions &&
-        *text==*that.text &&
-        buffer==that.buffer &&
-        bufferPos==that.bufferPos &&
-        nextIndex==that.nextIndex);
-}
-
-//-------------------------------------------------------------------------
-// Static utility methods
-//-------------------------------------------------------------------------
-
-void U_EXPORT2
-Normalizer::normalize(const UnicodeString& source,
-                      UNormalizationMode mode, int32_t options,
-                      UnicodeString& result,
-                      UErrorCode &status) {
-    if(source.isBogus() || U_FAILURE(status)) {
-        result.setToBogus();
-        if(U_SUCCESS(status)) {
-            status=U_ILLEGAL_ARGUMENT_ERROR;
-        }
-    } else {
-        UnicodeString localDest;
-        UnicodeString *dest;
-
-        if(&source!=&result) {
-            dest=&result;
-        } else {
-            // the source and result strings are the same object, use a temporary one
-            dest=&localDest;
-        }
-        const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status);
-        if(U_SUCCESS(status)) {
-            if(options&UNORM_UNICODE_3_2) {
-                FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)).
-                    normalize(source, *dest, status);
-            } else {
-                n2->normalize(source, *dest, status);
-            }
-        }
-        if(dest==&localDest && U_SUCCESS(status)) {
-            result=*dest;
-        }
-    }
-}
-
-void U_EXPORT2
-Normalizer::compose(const UnicodeString& source,
-                    UBool compat, int32_t options,
-                    UnicodeString& result,
-                    UErrorCode &status) {
-    normalize(source, compat ? UNORM_NFKC : UNORM_NFC, options, result, status);
-}
-
-void U_EXPORT2
-Normalizer::decompose(const UnicodeString& source,
-                      UBool compat, int32_t options,
-                      UnicodeString& result,
-                      UErrorCode &status) {
-    normalize(source, compat ? UNORM_NFKD : UNORM_NFD, options, result, status);
-}
-
-UNormalizationCheckResult
-Normalizer::quickCheck(const UnicodeString& source,
-                       UNormalizationMode mode, int32_t options,
-                       UErrorCode &status) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status);
-    if(U_SUCCESS(status)) {
-        if(options&UNORM_UNICODE_3_2) {
-            return FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)).
-                quickCheck(source, status);
-        } else {
-            return n2->quickCheck(source, status);
-        }
-    } else {
-        return UNORM_MAYBE;
-    }
-}
-
-UBool
-Normalizer::isNormalized(const UnicodeString& source,
-                         UNormalizationMode mode, int32_t options,
-                         UErrorCode &status) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status);
-    if(U_SUCCESS(status)) {
-        if(options&UNORM_UNICODE_3_2) {
-            return FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)).
-                isNormalized(source, status);
-        } else {
-            return n2->isNormalized(source, status);
-        }
-    } else {
-        return false;
-    }
-}
-
-UnicodeString & U_EXPORT2
-Normalizer::concatenate(const UnicodeString &left, const UnicodeString &right,
-                        UnicodeString &result,
-                        UNormalizationMode mode, int32_t options,
-                        UErrorCode &errorCode) {
-    if(left.isBogus() || right.isBogus() || U_FAILURE(errorCode)) {
-        result.setToBogus();
-        if(U_SUCCESS(errorCode)) {
-            errorCode=U_ILLEGAL_ARGUMENT_ERROR;
-        }
-    } else {
-        UnicodeString localDest;
-        UnicodeString *dest;
-
-        if(&right!=&result) {
-            dest=&result;
-        } else {
-            // the right and result strings are the same object, use a temporary one
-            dest=&localDest;
-        }
-        *dest=left;
-        const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, errorCode);
-        if(U_SUCCESS(errorCode)) {
-            if(options&UNORM_UNICODE_3_2) {
-                FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(errorCode)).
-                    append(*dest, right, errorCode);
-            } else {
-                n2->append(*dest, right, errorCode);
-            }
-        }
-        if(dest==&localDest && U_SUCCESS(errorCode)) {
-            result=*dest;
-        }
-    }
-    return result;
-}
-
-//-------------------------------------------------------------------------
-// Iteration API
-//-------------------------------------------------------------------------
-
-/**
- * Return the current character in the normalized text.
- */
-UChar32 Normalizer::current() {
-    if(bufferPos<buffer.length() || nextNormalize()) {
-        return buffer.char32At(bufferPos);
-    } else {
-        return DONE;
-    }
-}
-
-/**
- * Return the next character in the normalized text and advance
- * the iteration position by one.  If the end
- * of the text has already been reached, {@link #DONE} is returned.
- */
-UChar32 Normalizer::next() {
-    if(bufferPos<buffer.length() ||  nextNormalize()) {
-        UChar32 c=buffer.char32At(bufferPos);
-        bufferPos+=U16_LENGTH(c);
-        return c;
-    } else {
-        return DONE;
-    }
-}
-
-/**
- * Return the previous character in the normalized text and decrement
- * the iteration position by one.  If the beginning
- * of the text has already been reached, {@link #DONE} is returned.
- */
-UChar32 Normalizer::previous() {
-    if(bufferPos>0 || previousNormalize()) {
-        UChar32 c=buffer.char32At(bufferPos-1);
-        bufferPos-=U16_LENGTH(c);
-        return c;
-    } else {
-        return DONE;
-    }
-}
-
-void Normalizer::reset() {
-    currentIndex=nextIndex=text->setToStart();
-    clearBuffer();
-}
-
-void
-Normalizer::setIndexOnly(int32_t index) {
-    text->setIndex(index);  // pins index
-    currentIndex=nextIndex=text->getIndex();
-    clearBuffer();
-}
-
-/**
- * Return the first character in the normalized text.  This resets
- * the <tt>Normalizer's</tt> position to the beginning of the text.
- */
-UChar32 Normalizer::first() {
-    reset();
-    return next();
-}
-
-/**
- * Return the last character in the normalized text.  This resets
- * the <tt>Normalizer's</tt> position to be just before the
- * the input text corresponding to that normalized character.
- */
-UChar32 Normalizer::last() {
-    currentIndex=nextIndex=text->setToEnd();
-    clearBuffer();
-    return previous();
-}
-
-/**
- * Retrieve the current iteration position in the input text that is
- * being normalized.  This method is useful in applications such as
- * searching, where you need to be able to determine the position in
- * the input text that corresponds to a given normalized output character.
- * <p>
- * <b>Note:</b> This method sets the position in the <em>input</em>, while
- * {@link #next} and {@link #previous} iterate through characters in the
- * <em>output</em>.  This means that there is not necessarily a one-to-one
- * correspondence between characters returned by <tt>next</tt> and
- * <tt>previous</tt> and the indices passed to and returned from
- * <tt>setIndex</tt> and {@link #getIndex}.
- *
- */
-int32_t Normalizer::getIndex() const {
-    if(bufferPos<buffer.length()) {
-        return currentIndex;
-    } else {
-        return nextIndex;
-    }
-}
-
-/**
- * Retrieve the index of the start of the input text.  This is the begin index
- * of the <tt>CharacterIterator</tt> or the start (i.e. 0) of the <tt>String</tt>
- * over which this <tt>Normalizer</tt> is iterating
- */
-int32_t Normalizer::startIndex() const {
-    return text->startIndex();
-}
-
-/**
- * Retrieve the index of the end of the input text.  This is the end index
- * of the <tt>CharacterIterator</tt> or the length of the <tt>String</tt>
- * over which this <tt>Normalizer</tt> is iterating
- */
-int32_t Normalizer::endIndex() const {
-    return text->endIndex();
-}
-
-//-------------------------------------------------------------------------
-// Property access methods
-//-------------------------------------------------------------------------
-
-void
-Normalizer::setMode(UNormalizationMode newMode)
-{
-    fUMode = newMode;
-    init();
-}
-
-UNormalizationMode
-Normalizer::getUMode() const
-{
-    return fUMode;
-}
-
-void
-Normalizer::setOption(int32_t option,
-                      UBool value)
-{
-    if (value) {
-        fOptions |= option;
-    } else {
-        fOptions &= (~option);
-    }
-    init();
-}
-
-UBool
-Normalizer::getOption(int32_t option) const
-{
-    return (fOptions & option) != 0;
-}
-
-/**
- * Set the input text over which this <tt>Normalizer</tt> will iterate.
- * The iteration position is set to the beginning of the input text.
- */
-void
-Normalizer::setText(const UnicodeString& newText,
-                    UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return;
-    }
-    CharacterIterator *newIter = new StringCharacterIterator(newText);
-    if (newIter == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    delete text;
-    text = newIter;
-    reset();
-}
-
-/**
- * Set the input text over which this <tt>Normalizer</tt> will iterate.
- * The iteration position is set to the beginning of the string.
- */
-void
-Normalizer::setText(const CharacterIterator& newText,
-                    UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return;
-    }
-    CharacterIterator *newIter = newText.clone();
-    if (newIter == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    delete text;
-    text = newIter;
-    reset();
-}
-
-void
-Normalizer::setText(ConstChar16Ptr newText,
-                    int32_t length,
-                    UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return;
-    }
-    CharacterIterator *newIter = new UCharCharacterIterator(newText, length);
-    if (newIter == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    delete text;
-    text = newIter;
-    reset();
-}
-
-/**
- * Copies the text under iteration into the UnicodeString referred to by "result".
- * @param result Receives a copy of the text under iteration.
- */
-void
-Normalizer::getText(UnicodeString&  result)
-{
-    text->getText(result);
-}
-
-//-------------------------------------------------------------------------
-// Private utility methods
-//-------------------------------------------------------------------------
-
-void Normalizer::clearBuffer() {
-    buffer.remove();
-    bufferPos=0;
-}
-
-UBool
-Normalizer::nextNormalize() {
-    clearBuffer();
-    currentIndex=nextIndex;
-    text->setIndex(nextIndex);
-    if(!text->hasNext()) {
-        return false;
-    }
-    // Skip at least one character so we make progress.
-    UnicodeString segment(text->next32PostInc());
-    while(text->hasNext()) {
-        UChar32 c;
-        if(fNorm2->hasBoundaryBefore(c=text->next32PostInc())) {
-            text->move32(-1, CharacterIterator::kCurrent);
-            break;
-        }
-        segment.append(c);
-    }
-    nextIndex=text->getIndex();
-    UErrorCode errorCode=U_ZERO_ERROR;
-    fNorm2->normalize(segment, buffer, errorCode);
-    return U_SUCCESS(errorCode) && !buffer.isEmpty();
-}
-
-UBool
-Normalizer::previousNormalize() {
-    clearBuffer();
-    nextIndex=currentIndex;
-    text->setIndex(currentIndex);
-    if(!text->hasPrevious()) {
-        return false;
-    }
-    UnicodeString segment;
-    while(text->hasPrevious()) {
-        UChar32 c=text->previous32();
-        segment.insert(0, c);
-        if(fNorm2->hasBoundaryBefore(c)) {
-            break;
-        }
-    }
-    currentIndex=text->getIndex();
-    UErrorCode errorCode=U_ZERO_ERROR;
-    fNorm2->normalize(segment, buffer, errorCode);
-    bufferPos=buffer.length();
-    return U_SUCCESS(errorCode) && !buffer.isEmpty();
-}
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_NORMALIZATION */
diff --git a/libs/icucommon/pluralmap.cpp b/libs/icucommon/pluralmap.cpp
deleted file mode 100644
index a85dd1c979ff..000000000000
--- a/libs/icucommon/pluralmap.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- * Copyright (C) 2015, International Business Machines Corporation and
- * others. All Rights Reserved.
- */
-
-#include "unicode/unistr.h"
-#include "charstr.h"
-#include "cstring.h"
-#include "pluralmap.h"
-
-U_NAMESPACE_BEGIN
-
-static const char * const gPluralForms[] = {
-        "other", "zero", "one", "two", "few", "many"};
-
-PluralMapBase::Category
-PluralMapBase::toCategory(const char *pluralForm) {
-    for (int32_t i = 0; i < UPRV_LENGTHOF(gPluralForms); ++i) {
-        if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
-            return static_cast<Category>(i);
-        }
-    }
-    return NONE;
-}
-
-PluralMapBase::Category
-PluralMapBase::toCategory(const UnicodeString &pluralForm) {
-    CharString cCategory;
-    UErrorCode status = U_ZERO_ERROR;
-    cCategory.appendInvariantChars(pluralForm, status);
-    return U_FAILURE(status) ? NONE : toCategory(cCategory.data());
-}
-
-const char *PluralMapBase::getCategoryName(Category c) {
-    int32_t index = c;
-    return (index < 0 || index >= UPRV_LENGTHOF(gPluralForms)) ?
-            NULL : gPluralForms[index];
-}
-
-
-U_NAMESPACE_END
diff --git a/libs/icucommon/pluralmap.h b/libs/icucommon/pluralmap.h
deleted file mode 100644
index 4988fd2699f3..000000000000
--- a/libs/icucommon/pluralmap.h
+++ /dev/null
@@ -1,292 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-******************************************************************************
-* Copyright (C) 2015, International Business Machines Corporation and
-* others. All Rights Reserved.
-******************************************************************************
-*
-* File pluralmap.h - PluralMap class that maps plural categories to values.
-******************************************************************************
-*/
-
-#ifndef __PLURAL_MAP_H__
-#define __PLURAL_MAP_H__
-
-#include "unicode/uobject.h"
-#include "cmemory.h"
-
-U_NAMESPACE_BEGIN
-
-class UnicodeString;
-
-class U_COMMON_API PluralMapBase : public UMemory {
-public:
-    /**
-     * The names of all the plural categories. NONE is not an actual plural
-     * category, but rather represents the absence of a plural category.
-     */
-    enum Category {
-        NONE = -1,
-        OTHER,
-        ZERO,
-        ONE,
-        TWO,
-        FEW,
-        MANY,
-        CATEGORY_COUNT
-    };
-
-    /**
-     * Converts a category name such as "zero", "one", "two", "few", "many"
-     * or "other" to a category enum. Returns NONE for an unrecognized
-     * category name.
-     */
-    static Category toCategory(const char *categoryName);
-
-    /**
-     * Converts a category name such as "zero", "one", "two", "few", "many"
-     * or "other" to a category enum.  Returns NONE for unrecognized
-     * category name.
-     */
-    static Category toCategory(const UnicodeString &categoryName);
-
-    /**
-     * Converts a category to a name.
-     * Passing NONE or CATEGORY_COUNT for category returns NULL.
-     */
-    static const char *getCategoryName(Category category);
-};
-
-/**
- * A Map of plural categories to values. It maintains ownership of the
- * values.
- *
- * Type T is the value type. T must provide the following:
- * 1) Default constructor
- * 2) Copy constructor
- * 3) Assignment operator
- * 4) Must extend UMemory
- */
-template<typename T>
-class PluralMap : public PluralMapBase {
-public:
-    /**
-     * Other category is maps to a copy of the default value.
-     */
-    PluralMap() : fOtherVariant() {
-        initializeNew();
-    }
-
-    /**
-     * Other category is mapped to otherVariant.
-     */
-    PluralMap(const T &otherVariant) : fOtherVariant(otherVariant) {
-        initializeNew();
-    }
-
-    PluralMap(const PluralMap<T> &other) : fOtherVariant(other.fOtherVariant) {
-        fVariants[0] = &fOtherVariant;
-        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
-            fVariants[i] = other.fVariants[i] ?
-                    new T(*other.fVariants[i]) : NULL;
-        }
-    }
-
-    PluralMap<T> &operator=(const PluralMap<T> &other) {
-        if (this == &other) {
-            return *this;
-        }
-        for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) {
-            if (fVariants[i] != NULL && other.fVariants[i] != NULL) {
-                *fVariants[i] = *other.fVariants[i];
-            } else if (fVariants[i] != NULL) {
-                delete fVariants[i];
-                fVariants[i] = NULL;
-            } else if (other.fVariants[i] != NULL) {
-                fVariants[i] = new T(*other.fVariants[i]);
-            } else {
-                // do nothing
-            }
-        }
-        return *this;
-    }
-
-    ~PluralMap() {
-        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
-            delete fVariants[i];
-        }
-    }
-
-    /**
-     * Removes all mappings and makes 'other' point to the default value.
-     */
-    void clear() {
-        *fVariants[0] = T();
-        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
-            delete fVariants[i];
-            fVariants[i] = NULL;
-        }
-    }
-
-    /**
-     * Iterates through the mappings in this instance, set index to NONE
-     * prior to using. Call next repeatedly to get the values until it
-     * returns NULL. Each time next returns, caller may pass index
-     * to getCategoryName() to get the name of the plural category.
-     * When this function returns NULL, index is CATEGORY_COUNT
-     */
-    const T *next(Category &index) const {
-        int32_t idx = index;
-        ++idx;
-        for (; idx < UPRV_LENGTHOF(fVariants); ++idx) {
-            if (fVariants[idx] != NULL) {
-                index = static_cast<Category>(idx);
-                return fVariants[idx];
-            }
-        }
-        index = static_cast<Category>(idx);
-        return NULL;
-    }
-
-    /**
-     * non const version of next.
-     */
-    T *nextMutable(Category &index) {
-        const T *result = next(index);
-        return const_cast<T *>(result);
-    }
-
-    /**
-     * Returns the 'other' variant.
-     * Same as calling get(OTHER).
-     */
-    const T &getOther() const {
-        return get(OTHER);
-    }
-
-    /**
-     * Returns the value associated with a category.
-     * If no value found, or v is NONE or CATEGORY_COUNT, falls
-     * back to returning the value for the 'other' category.
-     */
-    const T &get(Category v) const {
-        int32_t index = v;
-        if (index < 0 || index >= UPRV_LENGTHOF(fVariants) || fVariants[index] == NULL) {
-            return *fVariants[0];
-        }
-        return *fVariants[index];
-    }
-
-    /**
-     * Convenience routine to get the value by category name. Otherwise
-     * works just like get(Category).
-     */
-    const T &get(const char *category) const {
-        return get(toCategory(category));
-    }
-
-    /**
-     * Convenience routine to get the value by category name as a
-     * UnicodeString. Otherwise works just like get(category).
-     */
-    const T &get(const UnicodeString &category) const {
-        return get(toCategory(category));
-    }
-
-    /**
-     * Returns a pointer to the value associated with a category
-     * that caller can safely modify. If the value was defaulting to the 'other'
-     * variant because no explicit value was stored, this method creates a
-     * new value using the default constructor at the returned pointer.
-     *
-     * @param category the category with the value to change.
-     * @param status error returned here if index is NONE or CATEGORY_COUNT
-     *  or memory could not be allocated, or any other error happens.
-     */
-    T *getMutable(
-            Category category,
-            UErrorCode &status) {
-        return getMutable(category, NULL, status);
-    }
-
-    /**
-     * Convenience routine to get a mutable pointer to a value by category name.
-     * Otherwise works just like getMutable(Category, UErrorCode &).
-     * reports an error if the category name is invalid.
-     */
-    T *getMutable(
-            const char *category,
-            UErrorCode &status) {
-        return getMutable(toCategory(category), NULL, status);
-    }
-
-    /**
-     * Just like getMutable(Category, UErrorCode &) but copies defaultValue to
-     * returned pointer if it was defaulting to the 'other' variant
-     * because no explicit value was stored.
-     */
-    T *getMutableWithDefault(
-            Category category,
-            const T &defaultValue,
-            UErrorCode &status) {
-        return getMutable(category, &defaultValue, status);
-    }
-
-    /**
-     * Returns true if this object equals rhs.
-     */
-    UBool equals(
-            const PluralMap<T> &rhs,
-            UBool (*eqFunc)(const T &, const T &)) const {
-        for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) {
-            if (fVariants[i] == rhs.fVariants[i]) {
-                continue;
-            }
-            if (fVariants[i] == NULL || rhs.fVariants[i] == NULL) {
-                return false;
-            }
-            if (!eqFunc(*fVariants[i], *rhs.fVariants[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-private:
-    T fOtherVariant;
-    T* fVariants[6];
-
-    T *getMutable(
-            Category category,
-            const T *defaultValue,
-            UErrorCode &status) {
-        if (U_FAILURE(status)) {
-            return NULL;
-        }
-        int32_t index = category;
-        if (index < 0 || index >= UPRV_LENGTHOF(fVariants)) {
-            status = U_ILLEGAL_ARGUMENT_ERROR;
-            return NULL;
-        }
-        if (fVariants[index] == NULL) {
-            fVariants[index] = defaultValue == NULL ?
-                    new T() : new T(*defaultValue);
-        }
-        if (!fVariants[index]) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-        }
-        return fVariants[index];
-    }
-
-    void initializeNew() {
-        fVariants[0] = &fOtherVariant;
-        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
-            fVariants[i] = NULL;
-        }
-    }
-};
-
-U_NAMESPACE_END
-
-#endif
diff --git a/libs/icucommon/resbund_cnv.cpp b/libs/icucommon/resbund_cnv.cpp
deleted file mode 100644
index ae854fe739cd..000000000000
--- a/libs/icucommon/resbund_cnv.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 1997-2006, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*   file name:  resbund_cnv.cpp
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 2004aug25
-*   created by: Markus W. Scherer
-*
-*   Character conversion functions moved here from resbund.cpp
-*/
-
-#include "unicode/utypes.h"
-#include "unicode/resbund.h"
-#include "uinvchar.h"
-
-U_NAMESPACE_BEGIN
-
-ResourceBundle::ResourceBundle( const UnicodeString&    path,
-                                const Locale&           locale,
-                                UErrorCode&              error)
-                                :UObject(), fLocale(NULL)
-{
-    constructForLocale(path, locale, error);
-}
-
-ResourceBundle::ResourceBundle( const UnicodeString&    path,
-                                UErrorCode&              error)
-                                :UObject(), fLocale(NULL)
-{
-    constructForLocale(path, Locale::getDefault(), error);
-}
-
-void
-ResourceBundle::constructForLocale(const UnicodeString& path,
-                                   const Locale& locale,
-                                   UErrorCode& error)
-{
-    if (path.isEmpty()) {
-        fResource = ures_open(NULL, locale.getName(), &error);
-    }
-    else {
-        UnicodeString nullTerminatedPath(path);
-        nullTerminatedPath.append((UChar)0);
-        fResource = ures_openU(nullTerminatedPath.getBuffer(), locale.getName(), &error);
-    }
-}
-
-U_NAMESPACE_END
diff --git a/libs/icucommon/restrace.cpp b/libs/icucommon/restrace.cpp
deleted file mode 100644
index 1f83372d682a..000000000000
--- a/libs/icucommon/restrace.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-// © 2019 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-
-#include "unicode/utypes.h"
-
-#if U_ENABLE_TRACING
-
-#include "restrace.h"
-#include "charstr.h"
-#include "cstring.h"
-#include "utracimp.h"
-#include "uresimp.h"
-#include "uassert.h"
-#include "util.h"
-
-U_NAMESPACE_BEGIN
-
-ResourceTracer::~ResourceTracer() = default;
-
-void ResourceTracer::trace(const char* resType) const {
-    U_ASSERT(fResB || fParent);
-    UTRACE_ENTRY(UTRACE_UDATA_RESOURCE);
-    UErrorCode status = U_ZERO_ERROR;
-
-    CharString filePath;
-    getFilePath(filePath, status);
-
-    CharString resPath;
-    getResPath(resPath, status);
-
-    // The longest type ("intvector") is 9 chars
-    const char kSpaces[] = "         ";
-    CharString format;
-    format.append(kSpaces, sizeof(kSpaces) - 1 - uprv_strlen(resType), status);
-    format.append("(%s) %s @ %s", status);
-
-    UTRACE_DATA3(UTRACE_VERBOSE,
-        format.data(),
-        resType,
-        filePath.data(),
-        resPath.data());
-    UTRACE_EXIT_STATUS(status);
-}
-
-void ResourceTracer::traceOpen() const {
-    U_ASSERT(fResB);
-    UTRACE_ENTRY(UTRACE_UDATA_BUNDLE);
-    UErrorCode status = U_ZERO_ERROR;
-
-    CharString filePath;
-    UTRACE_DATA1(UTRACE_VERBOSE, "%s", getFilePath(filePath, status).data());
-    UTRACE_EXIT_STATUS(status);
-}
-
-CharString& ResourceTracer::getFilePath(CharString& output, UErrorCode& status) const {
-    if (fResB) {
-        // Note: if you get a segfault around here, check that ResourceTable and
-        // ResourceArray instances outlive ResourceValue instances referring to
-        // their contents:
-        output.append(fResB->fData->fPath, status);
-        output.append('/', status);
-        output.append(fResB->fData->fName, status);
-        output.append(".res", status);
-    } else {
-        fParent->getFilePath(output, status);
-    }
-    return output;
-}
-
-CharString& ResourceTracer::getResPath(CharString& output, UErrorCode& status) const {
-    if (fResB) {
-        output.append('/', status);
-        output.append(fResB->fResPath, status);
-        // removing the trailing /
-        U_ASSERT(output[output.length()-1] == '/');
-        output.truncate(output.length()-1);
-    } else {
-        fParent->getResPath(output, status);
-    }
-    if (fKey) {
-        output.append('/', status);
-        output.append(fKey, status);
-    }
-    if (fIndex != -1) {
-        output.append('[', status);
-        UnicodeString indexString;
-        ICU_Utility::appendNumber(indexString, fIndex);
-        output.appendInvariantChars(indexString, status);
-        output.append(']', status);
-    }
-    return output;
-}
-
-void FileTracer::traceOpen(const char* path, const char* type, const char* name) {
-    if (uprv_strcmp(type, "res") == 0) {
-        traceOpenResFile(path, name);
-    } else {
-        traceOpenDataFile(path, type, name);
-    }
-}
-
-void FileTracer::traceOpenDataFile(const char* path, const char* type, const char* name) {
-    UTRACE_ENTRY(UTRACE_UDATA_DATA_FILE);
-    UErrorCode status = U_ZERO_ERROR;
-
-    CharString filePath;
-    filePath.append(path, status);
-    filePath.append('/', status);
-    filePath.append(name, status);
-    filePath.append('.', status);
-    filePath.append(type, status);
-
-    UTRACE_DATA1(UTRACE_VERBOSE, "%s", filePath.data());
-    UTRACE_EXIT_STATUS(status);
-}
-
-void FileTracer::traceOpenResFile(const char* path, const char* name) {
-    UTRACE_ENTRY(UTRACE_UDATA_RES_FILE);
-    UErrorCode status = U_ZERO_ERROR;
-
-    CharString filePath;
-    filePath.append(path, status);
-    filePath.append('/', status);
-    filePath.append(name, status);
-    filePath.append(".res", status);
-
-    UTRACE_DATA1(UTRACE_VERBOSE, "%s", filePath.data());
-    UTRACE_EXIT_STATUS(status);
-}
-
-U_NAMESPACE_END
-
-#endif // U_ENABLE_TRACING
diff --git a/libs/icucommon/uidna.cpp b/libs/icucommon/uidna.cpp
deleted file mode 100644
index c5ce439ca32e..000000000000
--- a/libs/icucommon/uidna.cpp
+++ /dev/null
@@ -1,921 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- *
- *   Copyright (C) 2003-2014, International Business Machines
- *   Corporation and others.  All Rights Reserved.
- *
- *******************************************************************************
- *   file name:  uidna.cpp
- *   encoding:   UTF-8
- *   tab size:   8 (not used)
- *   indentation:4
- *
- *   created on: 2003feb1
- *   created by: Ram Viswanadha
- */
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_IDNA
-
-#include "unicode/uidna.h"
-#include "unicode/ustring.h"
-#include "unicode/usprep.h"
-#include "punycode.h"
-#include "ustr_imp.h"
-#include "cmemory.h"
-#include "uassert.h"
-#include "sprpimpl.h"
-
-/* it is official IDNA ACE Prefix is "xn--" */
-static const UChar ACE_PREFIX[] ={ 0x0078,0x006E,0x002d,0x002d } ;
-#define ACE_PREFIX_LENGTH 4
-
-#define MAX_LABEL_LENGTH 63
-/* The Max length of the labels should not be more than MAX_LABEL_LENGTH */
-#define MAX_LABEL_BUFFER_SIZE 100
-
-#define MAX_DOMAIN_NAME_LENGTH 255
-/* The Max length of the domain names should not be more than MAX_DOMAIN_NAME_LENGTH */
-#define MAX_IDN_BUFFER_SIZE   MAX_DOMAIN_NAME_LENGTH+1
-
-#define LOWER_CASE_DELTA 0x0020
-#define HYPHEN           0x002D
-#define FULL_STOP        0x002E
-#define CAPITAL_A        0x0041
-#define CAPITAL_Z        0x005A
-
-inline static UChar
-toASCIILower(UChar ch){
-    if(CAPITAL_A <= ch && ch <= CAPITAL_Z){
-        return ch + LOWER_CASE_DELTA;
-    }
-    return ch;
-}
-
-inline static UBool
-startsWithPrefix(const UChar* src , int32_t srcLength){
-    if(srcLength < ACE_PREFIX_LENGTH){
-        return false;
-    }
-
-    for(int8_t i=0; i< ACE_PREFIX_LENGTH; i++){
-        if(toASCIILower(src[i]) != ACE_PREFIX[i]){
-            return false;
-        }
-    }
-    return true;
-}
-
-
-inline static int32_t
-compareCaseInsensitiveASCII(const UChar* s1, int32_t s1Len,
-                            const UChar* s2, int32_t s2Len){
-
-    int32_t minLength;
-    int32_t lengthResult;
-
-    // are we comparing different lengths?
-    if(s1Len != s2Len) {
-        if(s1Len < s2Len) {
-            minLength = s1Len;
-            lengthResult = -1;
-        } else {
-            minLength = s2Len;
-            lengthResult = 1;
-        }
-    } else {
-        // ok the lengths are equal
-        minLength = s1Len;
-        lengthResult = 0;
-    }
-
-    UChar c1,c2;
-    int32_t rc;
-
-    for(int32_t i =0;/* no condition */;i++) {
-
-        /* If we reach the ends of both strings then they match */
-        if(i == minLength) {
-            return lengthResult;
-        }
-
-        c1 = s1[i];
-        c2 = s2[i];
-
-        /* Case-insensitive comparison */
-        if(c1!=c2) {
-            rc=(int32_t)toASCIILower(c1)-(int32_t)toASCIILower(c2);
-            if(rc!=0) {
-                lengthResult=rc;
-                break;
-            }
-        }
-    }
-    return lengthResult;
-}
-
-
-/**
- * Ascertain if the given code point is a label separator as
- * defined by the IDNA RFC
- *
- * @param ch The code point to be ascertained
- * @return true if the char is a label separator
- * @stable ICU 2.8
- */
-static inline UBool isLabelSeparator(UChar ch){
-    switch(ch){
-        case 0x002e:
-        case 0x3002:
-        case 0xFF0E:
-        case 0xFF61:
-            return true;
-        default:
-            return false;
-    }
-}
-
-// returns the length of the label excluding the separator
-// if *limit == separator then the length returned does not include
-// the separtor.
-static inline int32_t
-getNextSeparator(UChar *src, int32_t srcLength,
-                 UChar **limit, UBool *done){
-    if(srcLength == -1){
-        int32_t i;
-        for(i=0 ; ;i++){
-            if(src[i] == 0){
-                *limit = src + i; // point to null
-                *done = true;
-                return i;
-            }
-            if(isLabelSeparator(src[i])){
-                *limit = src + (i+1); // go past the delimiter
-                return i;
-
-            }
-        }
-    }else{
-        int32_t i;
-        for(i=0;i<srcLength;i++){
-            if(isLabelSeparator(src[i])){
-                *limit = src + (i+1); // go past the delimiter
-                return i;
-            }
-        }
-        // we have not found the delimiter
-        // if(i==srcLength)
-        *limit = src+srcLength;
-        *done = true;
-
-        return i;
-    }
-}
-static inline UBool isLDHChar(UChar ch){
-    // high runner case
-    if(ch>0x007A){
-        return false;
-    }
-    //[\\u002D \\u0030-\\u0039 \\u0041-\\u005A \\u0061-\\u007A]
-    if( (ch==0x002D) ||
-        (0x0030 <= ch && ch <= 0x0039) ||
-        (0x0041 <= ch && ch <= 0x005A) ||
-        (0x0061 <= ch && ch <= 0x007A)
-      ){
-        return true;
-    }
-    return false;
-}
-
-static int32_t
-_internal_toASCII(const UChar* src, int32_t srcLength,
-                  UChar* dest, int32_t destCapacity,
-                  int32_t options,
-                  UStringPrepProfile* nameprep,
-                  UParseError* parseError,
-                  UErrorCode* status)
-{
-
-    // TODO Revisit buffer handling. The label should not be over 63 ASCII characters. ICU4J may need to be updated too.
-    UChar b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE];
-    //initialize pointers to stack buffers
-    UChar  *b1 = b1Stack, *b2 = b2Stack;
-    int32_t b1Len=0, b2Len,
-            b1Capacity = MAX_LABEL_BUFFER_SIZE,
-            b2Capacity = MAX_LABEL_BUFFER_SIZE ,
-            reqLength=0;
-
-    int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0;
-    UBool* caseFlags = NULL;
-
-    // the source contains all ascii codepoints
-    UBool srcIsASCII  = true;
-    // assume the source contains all LDH codepoints
-    UBool srcIsLDH = true;
-
-    int32_t j=0;
-
-    //get the options
-    UBool useSTD3ASCIIRules = (UBool)((options & UIDNA_USE_STD3_RULES) != 0);
-
-    int32_t failPos = -1;
-
-    if(srcLength == -1){
-        srcLength = u_strlen(src);
-    }
-
-    if(srcLength > b1Capacity){
-        b1 = (UChar*) uprv_malloc(srcLength * U_SIZEOF_UCHAR);
-        if(b1==NULL){
-            *status = U_MEMORY_ALLOCATION_ERROR;
-            goto CLEANUP;
-        }
-        b1Capacity = srcLength;
-    }
-
-    // step 1
-    for( j=0;j<srcLength;j++){
-        if(src[j] > 0x7F){
-            srcIsASCII = false;
-        }
-        b1[b1Len++] = src[j];
-    }
-
-    // step 2 is performed only if the source contains non ASCII
-    if(srcIsASCII == false){
-
-        // step 2
-        b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status);
-
-        if(*status == U_BUFFER_OVERFLOW_ERROR){
-            // redo processing of string
-            // we do not have enough room so grow the buffer
-            if(b1 != b1Stack){
-                uprv_free(b1);
-            }
-            b1 = (UChar*) uprv_malloc(b1Len * U_SIZEOF_UCHAR);
-            if(b1==NULL){
-                *status = U_MEMORY_ALLOCATION_ERROR;
-                goto CLEANUP;
-            }
-
-            *status = U_ZERO_ERROR; // reset error
-
-            b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status);
-        }
-    }
-    // error bail out
-    if(U_FAILURE(*status)){
-        goto CLEANUP;
-    }
-    if(b1Len == 0){
-        *status = U_IDNA_ZERO_LENGTH_LABEL_ERROR;
-        goto CLEANUP;
-    }
-
-    // for step 3 & 4
-    srcIsASCII = true;
-    for( j=0;j<b1Len;j++){
-        // check if output of usprep_prepare is all ASCII
-        if(b1[j] > 0x7F){
-            srcIsASCII = false;
-        }else if(isLDHChar(b1[j])==false){  // if the char is in ASCII range verify that it is an LDH character
-            srcIsLDH = false;
-            failPos = j;
-        }
-    }
-    if(useSTD3ASCIIRules == true){
-        // verify 3a and 3b
-        // 3(a) Verify the absence of non-LDH ASCII code points; that is, the
-        //  absence of 0..2C, 2E..2F, 3A..40, 5B..60, and 7B..7F.
-        // 3(b) Verify the absence of leading and trailing hyphen-minus; that
-        //  is, the absence of U+002D at the beginning and end of the
-        //  sequence.
-        if( srcIsLDH == false /* source at this point should not contain anyLDH characters */
-            || b1[0] ==  HYPHEN || b1[b1Len-1] == HYPHEN){
-            *status = U_IDNA_STD3_ASCII_RULES_ERROR;
-
-            /* populate the parseError struct */
-            if(srcIsLDH==false){
-                // failPos is always set the index of failure
-                uprv_syntaxError(b1,failPos, b1Len,parseError);
-            }else if(b1[0] == HYPHEN){
-                // fail position is 0
-                uprv_syntaxError(b1,0,b1Len,parseError);
-            }else{
-                // the last index in the source is always length-1
-                uprv_syntaxError(b1, (b1Len>0) ? b1Len-1 : b1Len, b1Len,parseError);
-            }
-
-            goto CLEANUP;
-        }
-    }
-    // Step 4: if the source is ASCII then proceed to step 8
-    if(srcIsASCII){
-        if(b1Len <= destCapacity){
-            u_memmove(dest, b1, b1Len);
-            reqLength = b1Len;
-        }else{
-            reqLength = b1Len;
-            goto CLEANUP;
-        }
-    }else{
-        // step 5 : verify the sequence does not begin with ACE prefix
-        if(!startsWithPrefix(b1,b1Len)){
-
-            //step 6: encode the sequence with punycode
-
-            // do not preserve the case flags for now!
-            // TODO: Preserve the case while implementing the RFE
-            // caseFlags = (UBool*) uprv_malloc(b1Len * sizeof(UBool));
-            // uprv_memset(caseFlags,true,b1Len);
-
-            b2Len = u_strToPunycode(b1,b1Len,b2,b2Capacity,caseFlags, status);
-
-            if(*status == U_BUFFER_OVERFLOW_ERROR){
-                // redo processing of string
-                /* we do not have enough room so grow the buffer*/
-                b2 = (UChar*) uprv_malloc(b2Len * U_SIZEOF_UCHAR);
-                if(b2 == NULL){
-                    *status = U_MEMORY_ALLOCATION_ERROR;
-                    goto CLEANUP;
-                }
-
-                *status = U_ZERO_ERROR; // reset error
-
-                b2Len = u_strToPunycode(b1,b1Len,b2,b2Len,caseFlags, status);
-            }
-            //error bail out
-            if(U_FAILURE(*status)){
-                goto CLEANUP;
-            }
-            // TODO : Reconsider while implementing the case preserve RFE
-            // convert all codepoints to lower case ASCII
-            // toASCIILower(b2,b2Len);
-            reqLength = b2Len+ACE_PREFIX_LENGTH;
-
-            if(reqLength > destCapacity){
-                *status = U_BUFFER_OVERFLOW_ERROR;
-                goto CLEANUP;
-            }
-            //Step 7: prepend the ACE prefix
-            u_memcpy(dest, ACE_PREFIX, ACE_PREFIX_LENGTH);
-            //Step 6: copy the contents in b2 into dest
-            u_memcpy(dest+ACE_PREFIX_LENGTH, b2, b2Len);
-
-        }else{
-            *status = U_IDNA_ACE_PREFIX_ERROR;
-            //position of failure is 0
-            uprv_syntaxError(b1,0,b1Len,parseError);
-            goto CLEANUP;
-        }
-    }
-    // step 8: verify the length of label
-    if(reqLength > MAX_LABEL_LENGTH){
-        *status = U_IDNA_LABEL_TOO_LONG_ERROR;
-    }
-
-CLEANUP:
-    if(b1 != b1Stack){
-        uprv_free(b1);
-    }
-    if(b2 != b2Stack){
-        uprv_free(b2);
-    }
-    uprv_free(caseFlags);
-
-    return u_terminateUChars(dest, destCapacity, reqLength, status);
-}
-
-static int32_t
-_internal_toUnicode(const UChar* src, int32_t srcLength,
-                    UChar* dest, int32_t destCapacity,
-                    int32_t options,
-                    UStringPrepProfile* nameprep,
-                    UParseError* parseError,
-                    UErrorCode* status)
-{
-
-    //get the options
-    //UBool useSTD3ASCIIRules = (UBool)((options & UIDNA_USE_STD3_RULES) != 0);
-    int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0;
-
-    // TODO Revisit buffer handling. The label should not be over 63 ASCII characters. ICU4J may need to be updated too.
-    UChar b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE], b3Stack[MAX_LABEL_BUFFER_SIZE];
-
-    //initialize pointers to stack buffers
-    UChar  *b1 = b1Stack, *b2 = b2Stack, *b1Prime=NULL, *b3=b3Stack;
-    int32_t b1Len = 0, b2Len, b1PrimeLen, b3Len,
-            b1Capacity = MAX_LABEL_BUFFER_SIZE,
-            b2Capacity = MAX_LABEL_BUFFER_SIZE,
-            b3Capacity = MAX_LABEL_BUFFER_SIZE,
-            reqLength=0;
-
-    UBool* caseFlags = NULL;
-
-    UBool srcIsASCII = true;
-    /*UBool srcIsLDH = true;
-    int32_t failPos =0;*/
-
-    // step 1: find out if all the codepoints in src are ASCII
-    if(srcLength==-1){
-        srcLength = 0;
-        for(;src[srcLength]!=0;){
-            if(src[srcLength]> 0x7f){
-                srcIsASCII = false;
-            }/*else if(isLDHChar(src[srcLength])==false){
-                // here we do not assemble surrogates
-                // since we know that LDH code points
-                // are in the ASCII range only
-                srcIsLDH = false;
-                failPos = srcLength;
-            }*/
-            srcLength++;
-        }
-    }else if(srcLength > 0){
-        for(int32_t j=0; j<srcLength; j++){
-            if(src[j]> 0x7f){
-                srcIsASCII = false;
-                break;
-            }/*else if(isLDHChar(src[j])==false){
-                // here we do not assemble surrogates
-                // since we know that LDH code points
-                // are in the ASCII range only
-                srcIsLDH = false;
-                failPos = j;
-            }*/
-        }
-    }else{
-        return 0;
-    }
-
-    if(srcIsASCII == false){
-        // step 2: process the string
-        b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status);
-        if(*status == U_BUFFER_OVERFLOW_ERROR){
-            // redo processing of string
-            /* we do not have enough room so grow the buffer*/
-            b1 = (UChar*) uprv_malloc(b1Len * U_SIZEOF_UCHAR);
-            if(b1==NULL){
-                *status = U_MEMORY_ALLOCATION_ERROR;
-                goto CLEANUP;
-            }
-
-            *status = U_ZERO_ERROR; // reset error
-
-            b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status);
-        }
-        //bail out on error
-        if(U_FAILURE(*status)){
-            goto CLEANUP;
-        }
-    }else{
-
-        //just point src to b1
-        b1 = (UChar*) src;
-        b1Len = srcLength;
-    }
-
-    // The RFC states that
-    // <quote>
-    // ToUnicode never fails. If any step fails, then the original input
-    // is returned immediately in that step.
-    // </quote>
-
-    //step 3: verify ACE Prefix
-    if(startsWithPrefix(b1,b1Len)){
-
-        //step 4: Remove the ACE Prefix
-        b1Prime = b1 + ACE_PREFIX_LENGTH;
-        b1PrimeLen  = b1Len - ACE_PREFIX_LENGTH;
-
-        //step 5: Decode using punycode
-        b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Capacity, caseFlags,status);
-
-        if(*status == U_BUFFER_OVERFLOW_ERROR){
-            // redo processing of string
-            /* we do not have enough room so grow the buffer*/
-            b2 = (UChar*) uprv_malloc(b2Len * U_SIZEOF_UCHAR);
-            if(b2==NULL){
-                *status = U_MEMORY_ALLOCATION_ERROR;
-                goto CLEANUP;
-            }
-
-            *status = U_ZERO_ERROR; // reset error
-
-            b2Len =  u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Len, caseFlags, status);
-        }
-
-
-        //step 6:Apply toASCII
-        b3Len = uidna_toASCII(b2, b2Len, b3, b3Capacity, options, parseError, status);
-
-        if(*status == U_BUFFER_OVERFLOW_ERROR){
-            // redo processing of string
-            /* we do not have enough room so grow the buffer*/
-            b3 = (UChar*) uprv_malloc(b3Len * U_SIZEOF_UCHAR);
-            if(b3==NULL){
-                *status = U_MEMORY_ALLOCATION_ERROR;
-                goto CLEANUP;
-            }
-
-            *status = U_ZERO_ERROR; // reset error
-
-            b3Len =  uidna_toASCII(b2,b2Len,b3,b3Len,options,parseError, status);
-
-        }
-        //bail out on error
-        if(U_FAILURE(*status)){
-            goto CLEANUP;
-        }
-
-        //step 7: verify
-        if(compareCaseInsensitiveASCII(b1, b1Len, b3, b3Len) !=0){
-            // Cause the original to be returned.
-            *status = U_IDNA_VERIFICATION_ERROR;
-            goto CLEANUP;
-        }
-
-        //step 8: return output of step 5
-        reqLength = b2Len;
-        if(b2Len <= destCapacity) {
-            u_memmove(dest, b2, b2Len);
-        }
-    }
-    else{
-        // See the start of this if statement for why this is commented out.
-        // verify that STD3 ASCII rules are satisfied
-        /*if(useSTD3ASCIIRules == true){
-            if( srcIsLDH == false // source contains some non-LDH characters
-                || src[0] ==  HYPHEN || src[srcLength-1] == HYPHEN){
-                *status = U_IDNA_STD3_ASCII_RULES_ERROR;
-
-                // populate the parseError struct
-                if(srcIsLDH==false){
-                    // failPos is always set the index of failure
-                    uprv_syntaxError(src,failPos, srcLength,parseError);
-                }else if(src[0] == HYPHEN){
-                    // fail position is 0
-                    uprv_syntaxError(src,0,srcLength,parseError);
-                }else{
-                    // the last index in the source is always length-1
-                    uprv_syntaxError(src, (srcLength>0) ? srcLength-1 : srcLength, srcLength,parseError);
-                }
-
-                goto CLEANUP;
-            }
-        }*/
-        // just return the source
-        //copy the source to destination
-        if(srcLength <= destCapacity){
-            u_memmove(dest, src, srcLength);
-        }
-        reqLength = srcLength;
-    }
-
-
-CLEANUP:
-
-    if(b1 != b1Stack && b1!=src){
-        uprv_free(b1);
-    }
-    if(b2 != b2Stack){
-        uprv_free(b2);
-    }
-    uprv_free(caseFlags);
-
-    // The RFC states that
-    // <quote>
-    // ToUnicode never fails. If any step fails, then the original input
-    // is returned immediately in that step.
-    // </quote>
-    // So if any step fails lets copy source to destination
-    if(U_FAILURE(*status)){
-        //copy the source to destination
-        if(dest && srcLength <= destCapacity){
-            // srcLength should have already been set earlier.
-            U_ASSERT(srcLength >= 0);
-            u_memmove(dest, src, srcLength);
-        }
-        reqLength = srcLength;
-        *status = U_ZERO_ERROR;
-    }
-
-    return u_terminateUChars(dest, destCapacity, reqLength, status);
-}
-
-U_CAPI int32_t U_EXPORT2
-uidna_toASCII(const UChar* src, int32_t srcLength,
-              UChar* dest, int32_t destCapacity,
-              int32_t options,
-              UParseError* parseError,
-              UErrorCode* status){
-
-    if(status == NULL || U_FAILURE(*status)){
-        return 0;
-    }
-    if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
-
-    if(U_FAILURE(*status)){
-        return -1;
-    }
-
-    int32_t retLen = _internal_toASCII(src, srcLength, dest, destCapacity, options, nameprep, parseError, status);
-
-    /* close the profile*/
-    usprep_close(nameprep);
-
-    return retLen;
-}
-
-U_CAPI int32_t U_EXPORT2
-uidna_toUnicode(const UChar* src, int32_t srcLength,
-                UChar* dest, int32_t destCapacity,
-                int32_t options,
-                UParseError* parseError,
-                UErrorCode* status){
-
-    if(status == NULL || U_FAILURE(*status)){
-        return 0;
-    }
-    if( (src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
-
-    if(U_FAILURE(*status)){
-        return -1;
-    }
-
-    int32_t retLen = _internal_toUnicode(src, srcLength, dest, destCapacity, options, nameprep, parseError, status);
-
-    usprep_close(nameprep);
-
-    return retLen;
-}
-
-
-U_CAPI int32_t U_EXPORT2
-uidna_IDNToASCII(  const UChar *src, int32_t srcLength,
-                   UChar* dest, int32_t destCapacity,
-                   int32_t options,
-                   UParseError *parseError,
-                   UErrorCode *status){
-
-    if(status == NULL || U_FAILURE(*status)){
-        return 0;
-    }
-    if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    int32_t reqLength = 0;
-
-    UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
-
-    if(U_FAILURE(*status)){
-        return 0;
-    }
-
-    //initialize pointers
-    UChar *delimiter = (UChar*)src;
-    UChar *labelStart = (UChar*)src;
-    UChar *currentDest = (UChar*) dest;
-    int32_t remainingLen = srcLength;
-    int32_t remainingDestCapacity = destCapacity;
-    int32_t labelLen = 0, labelReqLength = 0;
-    UBool done = false;
-
-
-    for(;;){
-
-        labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done);
-        labelReqLength = 0;
-        if(!(labelLen==0 && done)){// make sure this is not a root label separator.
-
-            labelReqLength = _internal_toASCII( labelStart, labelLen,
-                                                currentDest, remainingDestCapacity,
-                                                options, nameprep,
-                                                parseError, status);
-
-            if(*status == U_BUFFER_OVERFLOW_ERROR){
-
-                *status = U_ZERO_ERROR; // reset error
-                remainingDestCapacity = 0;
-            }
-        }
-
-
-        if(U_FAILURE(*status)){
-            break;
-        }
-
-        reqLength +=labelReqLength;
-        // adjust the destination pointer
-        if(labelReqLength < remainingDestCapacity){
-            currentDest = currentDest + labelReqLength;
-            remainingDestCapacity -= labelReqLength;
-        }else{
-            // should never occur
-            remainingDestCapacity = 0;
-        }
-
-        if(done == true){
-            break;
-        }
-
-        // add the label separator
-        if(remainingDestCapacity > 0){
-            *currentDest++ = FULL_STOP;
-            remainingDestCapacity--;
-        }
-        reqLength++;
-
-        labelStart = delimiter;
-        if(remainingLen >0 ){
-            remainingLen = (int32_t)(srcLength - (delimiter - src));
-        }
-
-    }
-
-    if(reqLength > MAX_DOMAIN_NAME_LENGTH){
-        *status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR;
-    }
-
-    usprep_close(nameprep);
-
-    return u_terminateUChars(dest, destCapacity, reqLength, status);
-}
-
-U_CAPI int32_t U_EXPORT2
-uidna_IDNToUnicode(  const UChar* src, int32_t srcLength,
-                     UChar* dest, int32_t destCapacity,
-                     int32_t options,
-                     UParseError* parseError,
-                     UErrorCode* status){
-
-    if(status == NULL || U_FAILURE(*status)){
-        return 0;
-    }
-    if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    int32_t reqLength = 0;
-
-    UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
-
-    if(U_FAILURE(*status)){
-        return 0;
-    }
-
-    //initialize pointers
-    UChar *delimiter = (UChar*)src;
-    UChar *labelStart = (UChar*)src;
-    UChar *currentDest = (UChar*) dest;
-    int32_t remainingLen = srcLength;
-    int32_t remainingDestCapacity = destCapacity;
-    int32_t labelLen = 0, labelReqLength = 0;
-    UBool done = false;
-
-    for(;;){
-
-        labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done);
-
-        // The RFC states that
-        // <quote>
-        // ToUnicode never fails. If any step fails, then the original input
-        // is returned immediately in that step.
-        // </quote>
-        // _internal_toUnicode will copy the label.
-        /*if(labelLen==0 && done==false){
-            *status = U_IDNA_ZERO_LENGTH_LABEL_ERROR;
-            break;
-        }*/
-
-        labelReqLength = _internal_toUnicode(labelStart, labelLen,
-                                             currentDest, remainingDestCapacity,
-                                             options, nameprep,
-                                             parseError, status);
-
-        if(*status == U_BUFFER_OVERFLOW_ERROR){
-            *status = U_ZERO_ERROR; // reset error
-            remainingDestCapacity = 0;
-        }
-
-        if(U_FAILURE(*status)){
-            break;
-        }
-
-        reqLength +=labelReqLength;
-        // adjust the destination pointer
-        if(labelReqLength < remainingDestCapacity){
-            currentDest = currentDest + labelReqLength;
-            remainingDestCapacity -= labelReqLength;
-        }else{
-            // should never occur
-            remainingDestCapacity = 0;
-        }
-
-        if(done == true){
-            break;
-        }
-
-        // add the label separator
-        // Unlike the ToASCII operation we don't normalize the label separators
-        if(remainingDestCapacity > 0){
-            *currentDest++ = *(labelStart + labelLen);
-            remainingDestCapacity--;
-        }
-        reqLength++;
-
-        labelStart = delimiter;
-        if(remainingLen >0 ){
-            remainingLen = (int32_t)(srcLength - (delimiter - src));
-        }
-
-    }
-
-    if(reqLength > MAX_DOMAIN_NAME_LENGTH){
-        *status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR;
-    }
-
-    usprep_close(nameprep);
-
-    return u_terminateUChars(dest, destCapacity, reqLength, status);
-}
-
-U_CAPI int32_t U_EXPORT2
-uidna_compare(  const UChar *s1, int32_t length1,
-                const UChar *s2, int32_t length2,
-                int32_t options,
-                UErrorCode* status){
-
-    if(status == NULL || U_FAILURE(*status)){
-        return -1;
-    }
-
-    UChar b1Stack[MAX_IDN_BUFFER_SIZE], b2Stack[MAX_IDN_BUFFER_SIZE];
-    UChar *b1 = b1Stack, *b2 = b2Stack;
-    int32_t b1Len, b2Len, b1Capacity = MAX_IDN_BUFFER_SIZE, b2Capacity = MAX_IDN_BUFFER_SIZE;
-    int32_t result=-1;
-
-    UParseError parseError;
-
-    b1Len = uidna_IDNToASCII(s1, length1, b1, b1Capacity, options, &parseError, status);
-    if(*status == U_BUFFER_OVERFLOW_ERROR){
-        // redo processing of string
-        b1 = (UChar*) uprv_malloc(b1Len * U_SIZEOF_UCHAR);
-        if(b1==NULL){
-            *status = U_MEMORY_ALLOCATION_ERROR;
-            goto CLEANUP;
-        }
-
-        *status = U_ZERO_ERROR; // reset error
-
-        b1Len = uidna_IDNToASCII(s1,length1,b1,b1Len, options, &parseError, status);
-
-    }
-
-    b2Len = uidna_IDNToASCII(s2,length2, b2,b2Capacity, options, &parseError, status);
-    if(*status == U_BUFFER_OVERFLOW_ERROR){
-        // redo processing of string
-        b2 = (UChar*) uprv_malloc(b2Len * U_SIZEOF_UCHAR);
-        if(b2==NULL){
-            *status = U_MEMORY_ALLOCATION_ERROR;
-            goto CLEANUP;
-        }
-
-        *status = U_ZERO_ERROR; // reset error
-
-        b2Len = uidna_IDNToASCII(s2, length2, b2, b2Len, options, &parseError, status);
-
-    }
-    // when toASCII is applied all label separators are replaced with FULL_STOP
-    result = compareCaseInsensitiveASCII(b1,b1Len,b2,b2Len);
-
-CLEANUP:
-    if(b1 != b1Stack){
-        uprv_free(b1);
-    }
-
-    if(b2 != b2Stack){
-        uprv_free(b2);
-    }
-
-    return result;
-}
-
-#endif /* #if !UCONFIG_NO_IDNA */
diff --git a/libs/icucommon/unorm.cpp b/libs/icucommon/unorm.cpp
deleted file mode 100644
index 537fe19d16ff..000000000000
--- a/libs/icucommon/unorm.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-******************************************************************************
-* Copyright (c) 1996-2014, International Business Machines
-* Corporation and others. All Rights Reserved.
-******************************************************************************
-* File unorm.cpp
-*
-* Created by: Vladimir Weinstein 12052000
-*
-* Modification history :
-*
-* Date        Name        Description
-* 02/01/01    synwee      Added normalization quickcheck enum and method.
-* 02/12/01    synwee      Commented out quickcheck util api has been approved
-*                         Added private method for doing FCD checks
-* 02/23/01    synwee      Modified quickcheck and checkFCE to run through
-*                         string for codepoints < 0x300 for the normalization
-*                         mode NFC.
-* 05/25/01+   Markus Scherer total rewrite, implement all normalization here
-*                         instead of just wrappers around normlzr.cpp,
-*                         load unorm.dat, support Unicode 3.1 with
-*                         supplementary code points, etc.
-* 2009-nov..2010-jan  Markus Scherer  total rewrite, new Normalizer2 API & code
-*/
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_NORMALIZATION
-
-#include "unicode/udata.h"
-#include "unicode/ustring.h"
-#include "unicode/uiter.h"
-#include "unicode/unorm.h"
-#include "unicode/unorm2.h"
-#include "normalizer2impl.h"
-#include "unormimp.h"
-#include "uprops.h"
-#include "ustr_imp.h"
-
-U_NAMESPACE_USE
-
-/* quick check functions ---------------------------------------------------- */
-
-U_CAPI UNormalizationCheckResult U_EXPORT2
-unorm_quickCheck(const UChar *src,
-                 int32_t srcLength,
-                 UNormalizationMode mode,
-                 UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    return unorm2_quickCheck((const UNormalizer2 *)n2, src, srcLength, pErrorCode);
-}
-
-U_CAPI UNormalizationCheckResult U_EXPORT2
-unorm_quickCheckWithOptions(const UChar *src, int32_t srcLength,
-                            UNormalizationMode mode, int32_t options,
-                            UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    if(options&UNORM_UNICODE_3_2) {
-        FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode));
-        return unorm2_quickCheck(
-            reinterpret_cast<const UNormalizer2 *>(static_cast<Normalizer2 *>(&fn2)),
-            src, srcLength, pErrorCode);
-    } else {
-        return unorm2_quickCheck((const UNormalizer2 *)n2, src, srcLength, pErrorCode);
-    }
-}
-
-U_CAPI UBool U_EXPORT2
-unorm_isNormalized(const UChar *src, int32_t srcLength,
-                   UNormalizationMode mode,
-                   UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    return unorm2_isNormalized((const UNormalizer2 *)n2, src, srcLength, pErrorCode);
-}
-
-U_CAPI UBool U_EXPORT2
-unorm_isNormalizedWithOptions(const UChar *src, int32_t srcLength,
-                              UNormalizationMode mode, int32_t options,
-                              UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    if(options&UNORM_UNICODE_3_2) {
-        FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode));
-        return unorm2_isNormalized(
-            reinterpret_cast<const UNormalizer2 *>(static_cast<Normalizer2 *>(&fn2)),
-            src, srcLength, pErrorCode);
-    } else {
-        return unorm2_isNormalized((const UNormalizer2 *)n2, src, srcLength, pErrorCode);
-    }
-}
-
-/* normalize() API ---------------------------------------------------------- */
-
-/** Public API for normalizing. */
-U_CAPI int32_t U_EXPORT2
-unorm_normalize(const UChar *src, int32_t srcLength,
-                UNormalizationMode mode, int32_t options,
-                UChar *dest, int32_t destCapacity,
-                UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    if(options&UNORM_UNICODE_3_2) {
-        FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode));
-        return unorm2_normalize(
-            reinterpret_cast<const UNormalizer2 *>(static_cast<Normalizer2 *>(&fn2)),
-            src, srcLength, dest, destCapacity, pErrorCode);
-    } else {
-        return unorm2_normalize((const UNormalizer2 *)n2,
-            src, srcLength, dest, destCapacity, pErrorCode);
-    }
-}
-
-
-/* iteration functions ------------------------------------------------------ */
-
-static int32_t
-_iterate(UCharIterator *src, UBool forward,
-              UChar *dest, int32_t destCapacity,
-              const Normalizer2 *n2,
-              UBool doNormalize, UBool *pNeededToNormalize,
-              UErrorCode *pErrorCode) {
-    if(U_FAILURE(*pErrorCode)) {
-        return 0;
-    }
-    if(destCapacity<0 || (dest==NULL && destCapacity>0) || src==NULL) {
-        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    if(pNeededToNormalize!=NULL) {
-        *pNeededToNormalize=false;
-    }
-    if(!(forward ? src->hasNext(src) : src->hasPrevious(src))) {
-        return u_terminateUChars(dest, destCapacity, 0, pErrorCode);
-    }
-
-    UnicodeString buffer;
-    UChar32 c;
-    if(forward) {
-        /* get one character and ignore its properties */
-        buffer.append(uiter_next32(src));
-        /* get all following characters until we see a boundary */
-        while((c=uiter_next32(src))>=0) {
-            if(n2->hasBoundaryBefore(c)) {
-                /* back out the latest movement to stop at the boundary */
-                src->move(src, -U16_LENGTH(c), UITER_CURRENT);
-                break;
-            } else {
-                buffer.append(c);
-            }
-        }
-    } else {
-        while((c=uiter_previous32(src))>=0) {
-            /* always write this character to the front of the buffer */
-            buffer.insert(0, c);
-            /* stop if this just-copied character is a boundary */
-            if(n2->hasBoundaryBefore(c)) {
-                break;
-            }
-        }
-    }
-
-    UnicodeString destString(dest, 0, destCapacity);
-    if(buffer.length()>0 && doNormalize) {
-        n2->normalize(buffer, destString, *pErrorCode).extract(dest, destCapacity, *pErrorCode);
-        if(pNeededToNormalize!=NULL && U_SUCCESS(*pErrorCode)) {
-            *pNeededToNormalize= destString!=buffer;
-        }
-        return destString.length();
-    } else {
-        /* just copy the source characters */
-        return buffer.extract(dest, destCapacity, *pErrorCode);
-    }
-}
-
-static int32_t
-unorm_iterate(UCharIterator *src, UBool forward,
-              UChar *dest, int32_t destCapacity,
-              UNormalizationMode mode, int32_t options,
-              UBool doNormalize, UBool *pNeededToNormalize,
-              UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    if(options&UNORM_UNICODE_3_2) {
-        const UnicodeSet *uni32 = uniset_getUnicode32Instance(*pErrorCode);
-        if(U_FAILURE(*pErrorCode)) {
-            return 0;
-        }
-        FilteredNormalizer2 fn2(*n2, *uni32);
-        return _iterate(src, forward, dest, destCapacity,
-            &fn2, doNormalize, pNeededToNormalize, pErrorCode);
-    }
-    return _iterate(src, forward, dest, destCapacity,
-            n2, doNormalize, pNeededToNormalize, pErrorCode);
-}
-
-U_CAPI int32_t U_EXPORT2
-unorm_previous(UCharIterator *src,
-               UChar *dest, int32_t destCapacity,
-               UNormalizationMode mode, int32_t options,
-               UBool doNormalize, UBool *pNeededToNormalize,
-               UErrorCode *pErrorCode) {
-    return unorm_iterate(src, false,
-                         dest, destCapacity,
-                         mode, options,
-                         doNormalize, pNeededToNormalize,
-                         pErrorCode);
-}
-
-U_CAPI int32_t U_EXPORT2
-unorm_next(UCharIterator *src,
-           UChar *dest, int32_t destCapacity,
-           UNormalizationMode mode, int32_t options,
-           UBool doNormalize, UBool *pNeededToNormalize,
-           UErrorCode *pErrorCode) {
-    return unorm_iterate(src, true,
-                         dest, destCapacity,
-                         mode, options,
-                         doNormalize, pNeededToNormalize,
-                         pErrorCode);
-}
-
-/* Concatenation of normalized strings -------------------------------------- */
-
-static int32_t
-_concatenate(const UChar *left, int32_t leftLength,
-                  const UChar *right, int32_t rightLength,
-                  UChar *dest, int32_t destCapacity,
-                  const Normalizer2 *n2,
-                  UErrorCode *pErrorCode) {
-    if(U_FAILURE(*pErrorCode)) {
-        return 0;
-    }
-    if(destCapacity<0 || (dest==NULL && destCapacity>0) ||
-        left==NULL || leftLength<-1 || right==NULL || rightLength<-1) {
-        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    /* check for overlapping right and destination */
-    if( dest!=NULL &&
-        ((right>=dest && right<(dest+destCapacity)) ||
-         (rightLength>0 && dest>=right && dest<(right+rightLength)))
-    ) {
-        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
-        return 0;
-    }
-
-    /* allow left==dest */
-    UnicodeString destString;
-    if(left==dest) {
-        destString.setTo(dest, leftLength, destCapacity);
-    } else {
-        destString.setTo(dest, 0, destCapacity);
-        destString.append(left, leftLength);
-    }
-    return n2->append(destString, UnicodeString(rightLength<0, right, rightLength), *pErrorCode).
-           extract(dest, destCapacity, *pErrorCode);
-}
-
-U_CAPI int32_t U_EXPORT2
-unorm_concatenate(const UChar *left, int32_t leftLength,
-                  const UChar *right, int32_t rightLength,
-                  UChar *dest, int32_t destCapacity,
-                  UNormalizationMode mode, int32_t options,
-                  UErrorCode *pErrorCode) {
-    const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode);
-    if(options&UNORM_UNICODE_3_2) {
-        const UnicodeSet *uni32 = uniset_getUnicode32Instance(*pErrorCode);
-        if(U_FAILURE(*pErrorCode)) {
-            return 0;
-        }
-        FilteredNormalizer2 fn2(*n2, *uni32);
-        return _concatenate(left, leftLength, right, rightLength,
-            dest, destCapacity, &fn2, pErrorCode);
-    }
-    return _concatenate(left, leftLength, right, rightLength,
-        dest, destCapacity, n2, pErrorCode);
-}
-
-#endif /* #if !UCONFIG_NO_NORMALIZATION */
diff --git a/libs/icucommon/unormimp.h b/libs/icucommon/unormimp.h
deleted file mode 100644
index e09c5c1c9167..000000000000
--- a/libs/icucommon/unormimp.h
+++ /dev/null
@@ -1,488 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 2001-2011, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*   file name:  unormimp.h
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 2001may25
-*   created by: Markus W. Scherer
-*/
-
-#ifndef __UNORMIMP_H__
-#define __UNORMIMP_H__
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_NORMALIZATION
-
-#include "udataswp.h"
-
-/*
- * The 2001-2010 implementation of the normalization code loads its data from
- * unorm.icu, which is generated with the gennorm tool.
- * The format of that file is described at the end of this file.
- */
-
-/* norm32 value constants */
-enum {
-    /* quick check flags 0..3 set mean "no" for their forms */
-    _NORM_QC_NFC=0x11,          /* no|maybe */
-    _NORM_QC_NFKC=0x22,         /* no|maybe */
-    _NORM_QC_NFD=4,             /* no */
-    _NORM_QC_NFKD=8,            /* no */
-
-    _NORM_QC_ANY_NO=0xf,
-
-    /* quick check flags 4..5 mean "maybe" for their forms; test flags>=_NORM_QC_MAYBE */
-    _NORM_QC_MAYBE=0x10,
-    _NORM_QC_ANY_MAYBE=0x30,
-
-    _NORM_QC_MASK=0x3f,
-
-    _NORM_COMBINES_FWD=0x40,
-    _NORM_COMBINES_BACK=0x80,
-    _NORM_COMBINES_ANY=0xc0,
-
-    _NORM_CC_SHIFT=8,           /* UnicodeData.txt combining class in bits 15..8 */
-    _NORM_CC_MASK=0xff00,
-
-    _NORM_EXTRA_SHIFT=16,               /* 16 bits for the index to UChars and other extra data */
-    _NORM_EXTRA_INDEX_TOP=0xfc00,       /* start of surrogate specials after shift */
-
-    _NORM_EXTRA_SURROGATE_MASK=0x3ff,
-    _NORM_EXTRA_SURROGATE_TOP=0x3f0,    /* hangul etc. */
-
-    _NORM_EXTRA_HANGUL=_NORM_EXTRA_SURROGATE_TOP,
-    _NORM_EXTRA_JAMO_L,
-    _NORM_EXTRA_JAMO_V,
-    _NORM_EXTRA_JAMO_T
-};
-
-/* norm32 value constants using >16 bits */
-#define _NORM_MIN_SPECIAL       0xfc000000
-#define _NORM_SURROGATES_TOP    0xfff00000
-#define _NORM_MIN_HANGUL        0xfff00000
-#define _NORM_MIN_JAMO_V        0xfff20000
-#define _NORM_JAMO_V_TOP        0xfff30000
-
-/* value constants for auxTrie */
-enum {
-    _NORM_AUX_COMP_EX_SHIFT=10,
-    _NORM_AUX_UNSAFE_SHIFT=11,
-    _NORM_AUX_NFC_SKIPPABLE_F_SHIFT=12
-};
-
-#define _NORM_AUX_MAX_FNC           ((int32_t)1<<_NORM_AUX_COMP_EX_SHIFT)
-
-#define _NORM_AUX_FNC_MASK          (uint32_t)(_NORM_AUX_MAX_FNC-1)
-#define _NORM_AUX_COMP_EX_MASK      ((uint32_t)1<<_NORM_AUX_COMP_EX_SHIFT)
-#define _NORM_AUX_UNSAFE_MASK       ((uint32_t)1<<_NORM_AUX_UNSAFE_SHIFT)
-#define _NORM_AUX_NFC_SKIP_F_MASK   ((uint32_t)1<<_NORM_AUX_NFC_SKIPPABLE_F_SHIFT)
-
-/* canonStartSets[0..31] contains indexes for what is in the array */
-enum {
-    _NORM_SET_INDEX_CANON_SETS_LENGTH,      /* number of uint16_t in canonical starter sets */
-    _NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH, /* number of uint16_t in the BMP search table (contains pairs) */
-    _NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH,/* number of uint16_t in the supplementary search table (contains triplets) */
-
-    /* from formatVersion 2.3: */
-    _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET,   /* uint16_t offset from canonStartSets[0] to the
-                                               exclusion set for CJK compatibility characters */
-    _NORM_SET_INDEX_NX_UNICODE32_OFFSET,    /* uint16_t offset from canonStartSets[0] to the
-                                               exclusion set for Unicode 3.2 characters */
-    _NORM_SET_INDEX_NX_RESERVED_OFFSET,     /* uint16_t offset from canonStartSets[0] to the
-                                               end of the previous exclusion set */
-
-    _NORM_SET_INDEX_TOP=32                  /* changing this requires a new formatVersion */
-};
-
-/* more constants for canonical starter sets */
-
-/* 14 bit indexes to canonical USerializedSets */
-#define _NORM_MAX_CANON_SETS            0x4000
-
-/* single-code point BMP sets are encoded directly in the search table except if result=0x4000..0x7fff */
-#define _NORM_CANON_SET_BMP_MASK        0xc000
-#define _NORM_CANON_SET_BMP_IS_INDEX    0x4000
-
-/* indexes[] value names */
-enum {
-    _NORM_INDEX_TRIE_SIZE,              /* number of bytes in normalization trie */
-    _NORM_INDEX_UCHAR_COUNT,            /* number of UChars in extra data */
-
-    _NORM_INDEX_COMBINE_DATA_COUNT,     /* number of uint16_t words for combining data */
-    _NORM_INDEX_COMBINE_FWD_COUNT,      /* number of code points that combine forward */
-    _NORM_INDEX_COMBINE_BOTH_COUNT,     /* number of code points that combine forward and backward */
-    _NORM_INDEX_COMBINE_BACK_COUNT,     /* number of code points that combine backward */
-
-    _NORM_INDEX_MIN_NFC_NO_MAYBE,       /* first code point with quick check NFC NO/MAYBE */
-    _NORM_INDEX_MIN_NFKC_NO_MAYBE,      /* first code point with quick check NFKC NO/MAYBE */
-    _NORM_INDEX_MIN_NFD_NO_MAYBE,       /* first code point with quick check NFD NO/MAYBE */
-    _NORM_INDEX_MIN_NFKD_NO_MAYBE,      /* first code point with quick check NFKD NO/MAYBE */
-
-    _NORM_INDEX_FCD_TRIE_SIZE,          /* number of bytes in FCD trie */
-
-    _NORM_INDEX_AUX_TRIE_SIZE,          /* number of bytes in the auxiliary trie */
-    _NORM_INDEX_CANON_SET_COUNT,        /* number of uint16_t in the array of serialized USet */
-
-    _NORM_INDEX_TOP=32                  /* changing this requires a new formatVersion */
-};
-
-enum {
-    /* FCD check: everything below this code point is known to have a 0 lead combining class */
-    _NORM_MIN_WITH_LEAD_CC=0x300
-};
-
-enum {
-    /**
-     * Bit 7 of the length byte for a decomposition string in extra data is
-     * a flag indicating whether the decomposition string is
-     * preceded by a 16-bit word with the leading and trailing cc
-     * of the decomposition (like for A-umlaut);
-     * if not, then both cc's are zero (like for compatibility ideographs).
-     */
-    _NORM_DECOMP_FLAG_LENGTH_HAS_CC=0x80,
-    /**
-     * Bits 6..0 of the length byte contain the actual length.
-     */
-    _NORM_DECOMP_LENGTH_MASK=0x7f
-};
-
-/** Constants for options flags for normalization. */
-enum {
-    /** Options bit 0, do not decompose Hangul syllables. */
-    UNORM_NX_HANGUL=1,
-    /** Options bit 1, do not decompose CJK compatibility characters. */
-    UNORM_NX_CJK_COMPAT=2
-};
-
-/**
- * Description of the format of unorm.icu version 2.3.
- *
- * Main change from version 1 to version 2:
- * Use of new, common UTrie instead of normalization-specific tries.
- * Change to version 2.1: add third/auxiliary trie with associated data.
- * Change to version 2.2: add skippable (f) flag data (_NORM_AUX_NFC_SKIP_F_MASK).
- * Change to version 2.3: add serialized sets for normalization exclusions
- *                        stored inside canonStartSets[]
- *
- * For more details of how to use the data structures see the code
- * in unorm.cpp (runtime normalization code) and
- * in gennorm.c and gennorm/store.c (build-time data generation).
- *
- * For the serialized format of UTrie see utrie.c/UTrieHeader.
- *
- * - Overall partition
- *
- * unorm.dat customarily begins with a UDataInfo structure, see udata.h and .c.
- * After that there are the following structures:
- *
- * int32_t indexes[_NORM_INDEX_TOP];            -- _NORM_INDEX_TOP=32, see enum in this file
- *
- * UTrie normTrie;                              -- size in bytes=indexes[_NORM_INDEX_TRIE_SIZE]
- *
- * uint16_t extraData[extraDataTop];            -- extraDataTop=indexes[_NORM_INDEX_UCHAR_COUNT]
- *                                                 extraData[0] contains the number of units for
- *                                                 FC_NFKC_Closure (formatVersion>=2.1)
- *
- * uint16_t combiningTable[combiningTableTop];  -- combiningTableTop=indexes[_NORM_INDEX_COMBINE_DATA_COUNT]
- *                                                 combiningTableTop may include one 16-bit padding unit
- *                                                 to make sure that fcdTrie is 32-bit-aligned
- *
- * UTrie fcdTrie;                               -- size in bytes=indexes[_NORM_INDEX_FCD_TRIE_SIZE]
- *
- * UTrie auxTrie;                               -- size in bytes=indexes[_NORM_INDEX_AUX_TRIE_SIZE]
- *
- * uint16_t canonStartSets[canonStartSetsTop]   -- canonStartSetsTop=indexes[_NORM_INDEX_CANON_SET_COUNT]
- *                                                 serialized USets and binary search tables, see below
- *
- *
- * The indexes array contains lengths and sizes of the following arrays and structures
- * as well as the following values:
- *  indexes[_NORM_INDEX_COMBINE_FWD_COUNT]=combineFwdTop
- *      -- one more than the highest combining index computed for forward-only-combining characters
- *  indexes[_NORM_INDEX_COMBINE_BOTH_COUNT]=combineBothTop-combineFwdTop
- *      -- number of combining indexes computed for both-ways-combining characters
- *  indexes[_NORM_INDEX_COMBINE_BACK_COUNT]=combineBackTop-combineBothTop
- *      -- number of combining indexes computed for backward-only-combining characters
- *
- *  indexes[_NORM_INDEX_MIN_NF*_NO_MAYBE] (where *={ C, D, KC, KD })
- *      -- first code point with a quick check NF* value of NO/MAYBE
- *
- *
- * - Tries
- *
- * The main structures are two UTrie tables ("compact arrays"),
- * each with one index array and one data array.
- * See utrie.h and utrie.c.
- *
- *
- * - Tries in unorm.dat
- *
- * The first trie (normTrie above)
- * provides data for the NF* quick checks and normalization.
- * The second trie (fcdTrie above) provides data just for FCD checks.
- *
- *
- * - norm32 data words from the first trie
- *
- * The norm32Table contains one 32-bit word "norm32" per code point.
- * It contains the following bit fields:
- * 31..16   extra data index, _NORM_EXTRA_SHIFT is used to shift this field down
- *          if this index is <_NORM_EXTRA_INDEX_TOP then it is an index into
- *              extraData[] where variable-length normalization data for this
- *              code point is found
- *          if this index is <_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP
- *              then this is a norm32 for a leading surrogate, and the index
- *              value is used together with the following trailing surrogate
- *              code unit in the second trie access
- *          if this index is >=_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP
- *              then this is a norm32 for a "special" character,
- *              i.e., the character is a Hangul syllable or a Jamo
- *              see _NORM_EXTRA_HANGUL etc.
- *          generally, instead of extracting this index from the norm32 and
- *              comparing it with the above constants,
- *              the normalization code compares the entire norm32 value
- *              with _NORM_MIN_SPECIAL, _NORM_SURROGATES_TOP, _NORM_MIN_HANGUL etc.
- *
- * 15..8    combining class (cc) according to UnicodeData.txt
- *
- *  7..6    _NORM_COMBINES_ANY flags, used in composition to see if a character
- *              combines with any following or preceding character(s)
- *              at all
- *     7    _NORM_COMBINES_BACK
- *     6    _NORM_COMBINES_FWD
- *
- *  5..0    quick check flags, set for "no" or "maybe", with separate flags for
- *              each normalization form
- *              the higher bits are "maybe" flags; for NF*D there are no such flags
- *              the lower bits are "no" flags for all forms, in the same order
- *              as the "maybe" flags,
- *              which is (MSB to LSB): NFKD NFD NFKC NFC
- *  5..4    _NORM_QC_ANY_MAYBE
- *  3..0    _NORM_QC_ANY_NO
- *              see further related constants
- *
- *
- * - Extra data per code point
- *
- * "Extra data" is referenced by the index in norm32.
- * It is variable-length data. It is only present, and only those parts
- * of it are, as needed for a given character.
- * The norm32 extra data index is added to the beginning of extraData[]
- * to get to a vector of 16-bit words with data at the following offsets:
- *
- * [-1]     Combining index for composition.
- *              Stored only if norm32&_NORM_COMBINES_ANY .
- * [0]      Lengths of the canonical and compatibility decomposition strings.
- *              Stored only if there are decompositions, i.e.,
- *              if norm32&(_NORM_QC_NFD|_NORM_QC_NFKD)
- *          High byte: length of NFKD, or 0 if none
- *          Low byte: length of NFD, or 0 if none
- *          Each length byte also has another flag:
- *              Bit 7 of a length byte is set if there are non-zero
- *              combining classes (cc's) associated with the respective
- *              decomposition. If this flag is set, then the decomposition
- *              is preceded by a 16-bit word that contains the
- *              leading and trailing cc's.
- *              Bits 6..0 of a length byte are the length of the
- *              decomposition string, not counting the cc word.
- * [1..n]   NFD
- * [n+1..]  NFKD
- *
- * Each of the two decompositions consists of up to two parts:
- * - The 16-bit words with the leading and trailing cc's.
- *   This is only stored if bit 7 of the corresponding length byte
- *   is set. In this case, at least one of the cc's is not zero.
- *   High byte: leading cc==cc of the first code point in the decomposition string
- *   Low byte: trailing cc==cc of the last code point in the decomposition string
- * - The decomposition string in UTF-16, with length code units.
- *
- *
- * - Combining indexes and combiningTable[]
- *
- * Combining indexes are stored at the [-1] offset of the extra data
- * if the character combines forward or backward with any other characters.
- * They are used for (re)composition in NF*C.
- * Values of combining indexes are arranged according to whether a character
- * combines forward, backward, or both ways:
- *    forward-only < both ways < backward-only
- *
- * The index values for forward-only and both-ways combining characters
- * are indexes into the combiningTable[].
- * The index values for backward-only combining characters are simply
- * incremented from the preceding index values to be unique.
- *
- * In the combiningTable[], a variable-length list
- * of variable-length (back-index, code point) pair entries is stored
- * for each forward-combining character.
- *
- * These back-indexes are the combining indexes of both-ways or backward-only
- * combining characters that the forward-combining character combines with.
- *
- * Each list is sorted in ascending order of back-indexes.
- * Each list is terminated with the last back-index having bit 15 set.
- *
- * Each pair (back-index, code point) takes up either 2 or 3
- * 16-bit words.
- * The first word of a list entry is the back-index, with its bit 15 set if
- * this is the last pair in the list.
- *
- * The second word contains flags in bits 15..13 that determine
- * if there is a third word and how the combined character is encoded:
- * 15   set if there is a third word in this list entry
- * 14   set if the result is a supplementary character
- * 13   set if the result itself combines forward
- *
- * According to these bits 15..14 of the second word,
- * the result character is encoded as follows:
- * 00 or 01 The result is <=0x1fff and stored in bits 12..0 of
- *          the second word.
- * 10       The result is 0x2000..0xffff and stored in the third word.
- *          Bits 12..0 of the second word are not used.
- * 11       The result is a supplementary character.
- *          Bits 9..0 of the leading surrogate are in bits 9..0 of
- *          the second word.
- *          Add 0xd800 to these bits to get the complete surrogate.
- *          Bits 12..10 of the second word are not used.
- *          The trailing surrogate is stored in the third word.
- *
- *
- * - FCD trie
- *
- * The FCD trie is very simple.
- * It is a folded trie with 16-bit data words.
- * In each word, the high byte contains the leading cc of the character,
- * and the low byte contains the trailing cc of the character.
- * These cc's are the cc's of the first and last code points in the
- * canonical decomposition of the character.
- *
- * Since all 16 bits are used for cc's, lead surrogates must be tested
- * by checking the code unit instead of the trie data.
- * This is done only if the 16-bit data word is not zero.
- * If the code unit is a leading surrogate and the data word is not zero,
- * then instead of cc's it contains the offset for the second trie lookup.
- *
- *
- * - Auxiliary trie and data
- *
- * The auxiliary 16-bit trie contains data for additional properties.
- * Bits
- * 15..13   reserved
- *     12   not NFC_Skippable (f) (formatVersion>=2.2)
- *     11   flag: not a safe starter for canonical closure
- *     10   composition exclusion
- *  9.. 0   index into extraData[] to FC_NFKC_Closure string
- *          (not for lead surrogate),
- *          or lead surrogate offset (for lead surrogate, if 9..0 not zero)
- *
- * - FC_NFKC_Closure strings in extraData[]
- *
- * Strings are either stored as a single code unit or as the length
- * followed by that many units.
- *   const UChar *s=extraData+(index from auxTrie data bits 9..0);
- *   int32_t length;
- *   if(*s<0xff00) {
- *     // s points to the single-unit string
- *     length=1;
- *   } else {
- *     length=*s&0xff;
- *     ++s;
- *   }
- *
- * Conditions for "NF* Skippable" from Mark Davis' com.ibm.text.UCD.NFSkippable:
- * (used in NormalizerTransliterator)
- *
- * A skippable character is
- * a) unassigned, or ALL of the following:
- * b) of combining class 0.
- * c) not decomposed by this normalization form.
- * AND if NFC or NFKC,
- * d) can never compose with a previous character.
- * e) can never compose with a following character.
- * f) can never change if another character is added.
- *    Example: a-breve might satisfy all but f, but if you
- *    add an ogonek it changes to a-ogonek + breve
- *
- * a)..e) must be tested from norm32.
- * Since f) is more complicated, the (not-)NFC_Skippable flag (f) is built
- * into the auxiliary trie.
- * The same bit is used for NFC and NFKC; (c) differs for them.
- * As usual, we build the "not skippable" flags so that unassigned
- * code points get a 0 bit.
- * This bit is only valid after (a)..(e) test false; test NFD_NO before (f) as well.
- * Test Hangul LV syllables entirely in code.
- *
- *
- * - structure inside canonStartSets[]
- *
- * This array maps from code points c to sets of code points (USerializedSet).
- * The result sets are the code points whose canonical decompositions start
- * with c.
- *
- * canonStartSets[] contains the following sub-arrays:
- *
- * indexes[_NORM_SET_INDEX_TOP]
- *   - contains lengths of sub-arrays etc.
- *
- * startSets[indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH]-_NORM_SET_INDEX_TOP]
- *   - contains serialized sets (USerializedSet) of canonical starters for
- *     enumerating canonically equivalent strings
- *     indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH] includes _NORM_SET_INDEX_TOP
- *     for details about the structure see uset.c
- *
- * bmpTable[indexes[_NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH]]
- *   - a sorted search table for BMP code points whose results are
- *     either indexes to USerializedSets or single code points for
- *     single-code point sets;
- *     each entry is a pair of { code point, result } with result=(binary) yy xxxxxx xxxxxxxx
- *     if yy==01 then there is a USerializedSet at canonStartSets+x
- *     else build a USerializedSet with result as the single code point
- *
- * suppTable[indexes[_NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH]]
- *   - a sorted search table for supplementary code points whose results are
- *     either indexes to USerializedSets or single code points for
- *     single-code point sets;
- *     each entry is a triplet of { high16(cp), low16(cp), result }
- *     each code point's high-word may contain extra data in bits 15..5:
- *     if the high word has bit 15 set, then build a set with a single code point
- *     which is (((high16(cp)&0x1f00)<<8)|result;
- *     else there is a USerializedSet at canonStartSets+result
- *
- * FormatVersion 2.3 adds 2 serialized sets for normalization exclusions.
- * They are stored in the data file so that the runtime normalization code need
- * not depend on other properties and their data and implementation files.
- * The _NORM_SET_INDEX_NX_..._OFFSET offsets in the canonStartSets index table
- * give the location for each set.
- * There is no set stored for UNORM_NX_HANGUL because it's trivial to create
- * without using properties.
- *
- * Set contents:
- *
- * _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET (for UNORM_NX_CJK_COMPAT)
- *     [[:Ideographic:]&[:NFD_QC=No:]]
- *     =[CJK Ideographs]&[has canonical decomposition]
- *
- * _NORM_SET_INDEX_NX_UNICODE32_OFFSET (for UNORM_UNICODE_3_2)
- *     [:^Age=3.2:]
- *     =set with all code points that were not designated by the specified Unicode version
- *
- * _NORM_SET_INDEX_NX_RESERVED_OFFSET
- *     This is an offset that points to where the next, future set would start.
- *     Currently it indicates where the previous set ends, and thus its length.
- *     The name for this enum constant may in the future be applied to different
- *     index slots. In order to get the limit of a set, use its index slot and
- *     the immediately following one regardless of that one's enum name.
- */
-
-#endif /* #if !UCONFIG_NO_NORMALIZATION */
-
-#endif
diff --git a/libs/icucommon/usc_impl.cpp b/libs/icucommon/usc_impl.cpp
deleted file mode 100644
index 6824b33a056c..000000000000
--- a/libs/icucommon/usc_impl.cpp
+++ /dev/null
@@ -1,361 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-*   Copyright (C) 1999-2016, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-**********************************************************************
-*
-* File USC_IMPL.C
-*
-* Modification History:
-*
-*   Date        Name        Description
-*   07/08/2002  Eric Mader  Creation.
-******************************************************************************
-*/
-
-#include "unicode/uscript.h"
-#include "usc_impl.h"
-#include "cmemory.h"
-
-#define PAREN_STACK_DEPTH 32
-
-#define MOD(sp) ((sp) % PAREN_STACK_DEPTH)
-#define LIMIT_INC(sp) (((sp) < PAREN_STACK_DEPTH)? (sp) + 1 : PAREN_STACK_DEPTH)
-#define INC(sp,count) (MOD((sp) + (count)))
-#define INC1(sp) (INC(sp, 1))
-#define DEC(sp,count) (MOD((sp) + PAREN_STACK_DEPTH - (count)))
-#define DEC1(sp) (DEC(sp, 1))
-#define STACK_IS_EMPTY(scriptRun) ((scriptRun)->pushCount <= 0)
-#define STACK_IS_NOT_EMPTY(scriptRun) (! STACK_IS_EMPTY(scriptRun))
-#define TOP(scriptRun) ((scriptRun)->parenStack[(scriptRun)->parenSP])
-#define SYNC_FIXUP(scriptRun) ((scriptRun)->fixupCount = 0)
-
-struct ParenStackEntry
-{
-    int32_t pairIndex;
-    UScriptCode scriptCode;
-};
-
-struct UScriptRun
-{
-    int32_t textLength;
-    const UChar *textArray;
-
-    int32_t scriptStart;
-    int32_t scriptLimit;
-    UScriptCode scriptCode;
-
-    struct ParenStackEntry parenStack[PAREN_STACK_DEPTH];
-    int32_t parenSP;
-    int32_t pushCount;
-    int32_t fixupCount;
-};
-
-static int8_t highBit(int32_t value);
-
-static const UChar32 pairedChars[] = {
-    0x0028, 0x0029, /* ascii paired punctuation */
-    0x003c, 0x003e,
-    0x005b, 0x005d,
-    0x007b, 0x007d,
-    0x00ab, 0x00bb, /* guillemets */
-    0x2018, 0x2019, /* general punctuation */
-    0x201c, 0x201d,
-    0x2039, 0x203a,
-    0x3008, 0x3009, /* chinese paired punctuation */
-    0x300a, 0x300b,
-    0x300c, 0x300d,
-    0x300e, 0x300f,
-    0x3010, 0x3011,
-    0x3014, 0x3015,
-    0x3016, 0x3017,
-    0x3018, 0x3019,
-    0x301a, 0x301b
-};
-
-static void push(UScriptRun *scriptRun, int32_t pairIndex, UScriptCode scriptCode)
-{
-    scriptRun->pushCount  = LIMIT_INC(scriptRun->pushCount);
-    scriptRun->fixupCount = LIMIT_INC(scriptRun->fixupCount);
-
-    scriptRun->parenSP = INC1(scriptRun->parenSP);
-    scriptRun->parenStack[scriptRun->parenSP].pairIndex  = pairIndex;
-    scriptRun->parenStack[scriptRun->parenSP].scriptCode = scriptCode;
-}
-
-static void pop(UScriptRun *scriptRun)
-{
-    if (STACK_IS_EMPTY(scriptRun)) {
-        return;
-    }
-
-    if (scriptRun->fixupCount > 0) {
-        scriptRun->fixupCount -= 1;
-    }
-
-    scriptRun->pushCount -= 1;
-    scriptRun->parenSP = DEC1(scriptRun->parenSP);
-
-    /* If the stack is now empty, reset the stack
-       pointers to their initial values.
-     */
-    if (STACK_IS_EMPTY(scriptRun)) {
-        scriptRun->parenSP = -1;
-    }
-}
-
-static void fixup(UScriptRun *scriptRun, UScriptCode scriptCode)
-{
-    int32_t fixupSP = DEC(scriptRun->parenSP, scriptRun->fixupCount);
-
-    while (scriptRun->fixupCount-- > 0) {
-        fixupSP = INC1(fixupSP);
-        scriptRun->parenStack[fixupSP].scriptCode = scriptCode;
-    }
-}
-
-static int8_t
-highBit(int32_t value)
-{
-    int8_t bit = 0;
-
-    if (value <= 0) {
-        return -32;
-    }
-
-    if (value >= 1 << 16) {
-        value >>= 16;
-        bit += 16;
-    }
-
-    if (value >= 1 << 8) {
-        value >>= 8;
-        bit += 8;
-    }
-
-    if (value >= 1 << 4) {
-        value >>= 4;
-        bit += 4;
-    }
-
-    if (value >= 1 << 2) {
-        value >>= 2;
-        bit += 2;
-    }
-
-    if (value >= 1 << 1) {
-        //value >>= 1;
-        bit += 1;
-    }
-
-    return bit;
-}
-
-static int32_t
-getPairIndex(UChar32 ch)
-{
-    int32_t pairedCharCount = UPRV_LENGTHOF(pairedChars);
-    int32_t pairedCharPower = 1 << highBit(pairedCharCount);
-    int32_t pairedCharExtra = pairedCharCount - pairedCharPower;
-
-    int32_t probe = pairedCharPower;
-    int32_t pairIndex = 0;
-
-    if (ch >= pairedChars[pairedCharExtra]) {
-        pairIndex = pairedCharExtra;
-    }
-
-    while (probe > (1 << 0)) {
-        probe >>= 1;
-
-        if (ch >= pairedChars[pairIndex + probe]) {
-            pairIndex += probe;
-        }
-    }
-
-    if (pairedChars[pairIndex] != ch) {
-        pairIndex = -1;
-    }
-
-    return pairIndex;
-}
-
-static UBool
-sameScript(UScriptCode scriptOne, UScriptCode scriptTwo)
-{
-    return scriptOne <= USCRIPT_INHERITED || scriptTwo <= USCRIPT_INHERITED || scriptOne == scriptTwo;
-}
-
-U_CAPI UScriptRun * U_EXPORT2
-uscript_openRun(const UChar *src, int32_t length, UErrorCode *pErrorCode)
-{
-    UScriptRun *result = NULL;
-
-    if (pErrorCode == NULL || U_FAILURE(*pErrorCode)) {
-        return NULL;
-    }
-
-    result = (UScriptRun *)uprv_malloc(sizeof (UScriptRun));
-
-    if (result == NULL) {
-        *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-
-    uscript_setRunText(result, src, length, pErrorCode);
-
-    /* Release the UScriptRun if uscript_setRunText() returns an error */
-    if (U_FAILURE(*pErrorCode)) {
-        uprv_free(result);
-        result = NULL;
-    }
-
-    return result;
-}
-
-U_CAPI void U_EXPORT2
-uscript_closeRun(UScriptRun *scriptRun)
-{
-    if (scriptRun != NULL) {
-        uprv_free(scriptRun);
-    }
-}
-
-U_CAPI void U_EXPORT2
-uscript_resetRun(UScriptRun *scriptRun)
-{
-    if (scriptRun != NULL) {
-        scriptRun->scriptStart = 0;
-        scriptRun->scriptLimit = 0;
-        scriptRun->scriptCode  = USCRIPT_INVALID_CODE;
-        scriptRun->parenSP     = -1;
-        scriptRun->pushCount   =  0;
-        scriptRun->fixupCount  =  0;
-    }
-}
-
-U_CAPI void U_EXPORT2
-uscript_setRunText(UScriptRun *scriptRun, const UChar *src, int32_t length, UErrorCode *pErrorCode)
-{
-    if (pErrorCode == NULL || U_FAILURE(*pErrorCode)) {
-        return;
-    }
-
-    if (scriptRun == NULL || length < 0 || ((src == NULL) != (length == 0))) {
-        *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-
-    scriptRun->textArray  = src;
-    scriptRun->textLength = length;
-
-    uscript_resetRun(scriptRun);
-}
-
-U_CAPI UBool U_EXPORT2
-uscript_nextRun(UScriptRun *scriptRun, int32_t *pRunStart, int32_t *pRunLimit, UScriptCode *pRunScript)
-{
-    UErrorCode error = U_ZERO_ERROR;
-
-    /* if we've fallen off the end of the text, we're done */
-    if (scriptRun == NULL || scriptRun->scriptLimit >= scriptRun->textLength) {
-        return false;
-    }
-
-    SYNC_FIXUP(scriptRun);
-    scriptRun->scriptCode = USCRIPT_COMMON;
-
-    for (scriptRun->scriptStart = scriptRun->scriptLimit; scriptRun->scriptLimit < scriptRun->textLength; scriptRun->scriptLimit += 1) {
-        UChar   high = scriptRun->textArray[scriptRun->scriptLimit];
-        UChar32 ch   = high;
-        UScriptCode sc;
-        int32_t pairIndex;
-
-        /*
-         * if the character is a high surrogate and it's not the last one
-         * in the text, see if it's followed by a low surrogate
-         */
-        if (high >= 0xD800 && high <= 0xDBFF && scriptRun->scriptLimit < scriptRun->textLength - 1) {
-            UChar low = scriptRun->textArray[scriptRun->scriptLimit + 1];
-
-            /*
-             * if it is followed by a low surrogate,
-             * consume it and form the full character
-             */
-            if (low >= 0xDC00 && low <= 0xDFFF) {
-                ch = (high - 0xD800) * 0x0400 + low - 0xDC00 + 0x10000;
-                scriptRun->scriptLimit += 1;
-            }
-        }
-
-        sc = uscript_getScript(ch, &error);
-        pairIndex = getPairIndex(ch);
-
-        /*
-         * Paired character handling:
-         *
-         * if it's an open character, push it onto the stack.
-         * if it's a close character, find the matching open on the
-         * stack, and use that script code. Any non-matching open
-         * characters above it on the stack will be poped.
-         */
-        if (pairIndex >= 0) {
-            if ((pairIndex & 1) == 0) {
-                push(scriptRun, pairIndex, scriptRun->scriptCode);
-            } else {
-                int32_t pi = pairIndex & ~1;
-
-                while (STACK_IS_NOT_EMPTY(scriptRun) && TOP(scriptRun).pairIndex != pi) {
-                    pop(scriptRun);
-                }
-
-                if (STACK_IS_NOT_EMPTY(scriptRun)) {
-                    sc = TOP(scriptRun).scriptCode;
-                }
-            }
-        }
-
-        if (sameScript(scriptRun->scriptCode, sc)) {
-            if (scriptRun->scriptCode <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
-                scriptRun->scriptCode = sc;
-
-                fixup(scriptRun, scriptRun->scriptCode);
-            }
-
-            /*
-             * if this character is a close paired character,
-             * pop the matching open character from the stack
-             */
-            if (pairIndex >= 0 && (pairIndex & 1) != 0) {
-                pop(scriptRun);
-            }
-        } else {
-            /*
-             * if the run broke on a surrogate pair,
-             * end it before the high surrogate
-             */
-            if (ch >= 0x10000) {
-                scriptRun->scriptLimit -= 1;
-            }
-
-            break;
-        }
-    }
-
-
-    if (pRunStart != NULL) {
-        *pRunStart = scriptRun->scriptStart;
-    }
-
-    if (pRunLimit != NULL) {
-        *pRunLimit = scriptRun->scriptLimit;
-    }
-
-    if (pRunScript != NULL) {
-        *pRunScript = scriptRun->scriptCode;
-    }
-
-    return true;
-}
diff --git a/libs/icucommon/usc_impl.h b/libs/icucommon/usc_impl.h
deleted file mode 100644
index cd76990501ac..000000000000
--- a/libs/icucommon/usc_impl.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-*   Copyright (C) 1999-2011, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-**********************************************************************
-*
-* File USC_IMPL.H
-*
-* Modification History:
-*
-*   Date        Name        Description
-*   07/08/2002  Eric Mader  Creation.
-******************************************************************************
-*/
-
-#ifndef USC_IMPL_H
-#define USC_IMPL_H
-#include "unicode/utypes.h"
-#include "unicode/uscript.h"
-
-/**
- * <code>UScriptRun</code> is used to find runs of characters in
- * the same script. It implements a simple iterator over an array
- * of characters. The iterator will resolve script-neutral characters
- * like punctuation into the script of the surrounding characters.
- *
- * The iterator will try to match paired punctuation. If it sees an
- * opening punctuation character, it will remember the script that
- * was assigned to that character, and assign the same script to the
- * matching closing punctuation.
- *
- * Scripts are chosen based on the <code>UScriptCode</code> enumeration.
- * No attempt is made to combine related scripts into a single run. In
- * particular, Hiragana, Katakana, and Han characters will appear in separate
- * runs.
-
- * Here is an example of how to iterate over script runs:
- * <pre>
- * \code
- * void printScriptRuns(const UChar *text, int32_t length)
- * {
- *     UErrorCode error = U_ZERO_ERROR;
- *     UScriptRun *scriptRun = uscript_openRun(text, testLength, &error);
- *     int32_t start = 0, limit = 0;
- *     UScriptCode code = USCRIPT_INVALID_CODE;
- *
- *     while (uscript_nextRun(&start, &limit, &code)) {
- *         printf("Script '%s' from %d to %d.\n", uscript_getName(code), start, limit);
- *     }
- *
- *     uscript_closeRun(scriptRun);
- *  }
- * </pre>
- */
-struct UScriptRun;
-
-typedef struct UScriptRun UScriptRun;
-
-/**
- * Create a <code>UScriptRun</code> object for iterating over the given text. This object must
- * be freed using <code>uscript_closeRun()</code>. Note that this object does not copy the source text,
- * only the pointer to it. You must make sure that the pointer remains valid until you call
- * <code>uscript_closeRun()</code> or <code>uscript_setRunText()</code>.
- *
- * @param src is the address of the array of characters over which to iterate.
- *        if <code>src == NULL</code> and <code>length == 0</code>,
- *        an empty <code>UScriptRun</code> object will be returned.
- *
- * @param length is the number of characters over which to iterate.
- *
- * @param pErrorCode is a pointer to a valid <code>UErrorCode</code> value. If this value
- *        indicates a failure on entry, the function will immediately return.
- *        On exit the value will indicate the success of the operation.
- *
- * @return the address of <code>UScriptRun</code> object which will iterate over the text,
- *         or <code>NULL</code> if the operation failed.
- */
-U_CAPI UScriptRun * U_EXPORT2
-uscript_openRun(const UChar *src, int32_t length, UErrorCode *pErrorCode);
-
-/**
- * Frees the given <code>UScriptRun</code> object and any storage associated with it.
- * On return, scriptRun no longer points to a valid <code>UScriptRun</code> object.
- *
- * @param scriptRun is the <code>UScriptRun</code> object which will be freed.
- */
-U_CAPI void U_EXPORT2
-uscript_closeRun(UScriptRun *scriptRun);
-
-/**
- * Reset the <code>UScriptRun</code> object so that it will start iterating from
- * the beginning.
- *
- * @param scriptRun is the address of the <code>UScriptRun</code> object to be reset.
- */
-U_CAPI void U_EXPORT2
-uscript_resetRun(UScriptRun *scriptRun);
-
-/**
- * Change the text over which the given <code>UScriptRun</code> object iterates.
- *
- * @param scriptRun is the <code>UScriptRun</code> object which will be changed.
- *
- * @param src is the address of the new array of characters over which to iterate.
- *        If <code>src == NULL</code> and <code>length == 0</code>,
- *        the <code>UScriptRun</code> object will become empty.
- *
- * @param length is the new number of characters over which to iterate
- *
- * @param pErrorCode is a pointer to a valid <code>UErrorCode</code> value. If this value
- *        indicates a failure on entry, the function will immediately return.
- *        On exit the value will indicate the success of the operation.
- */
-U_CAPI void U_EXPORT2
-uscript_setRunText(UScriptRun *scriptRun, const UChar *src, int32_t length, UErrorCode *pErrorCode);
-
-/**
- * Advance the <code>UScriptRun</code> object to the next script run, return the start and limit
- * offsets, and the script of the run.
- *
- * @param scriptRun is the address of the <code>UScriptRun</code> object.
- *
- * @param pRunStart is a pointer to the variable to receive the starting offset of the next run.
- *        This pointer can be <code>NULL</code> if the value is not needed.
- *
- * @param pRunLimit is a pointer to the variable to receive the limit offset of the next run.
- *        This pointer can be <code>NULL</code> if the value is not needed.
- *
- * @param pRunScript is a pointer to the variable to receive the UScriptCode for the
- *        script of the current run. This pointer can be <code>NULL</code> if the value is not needed.
- *
- * @return true if there was another script run.
- */
-U_CAPI UBool U_EXPORT2
-uscript_nextRun(UScriptRun *scriptRun, int32_t *pRunStart, int32_t *pRunLimit, UScriptCode *pRunScript);
-
-#endif
diff --git a/libs/icui18n/Makefile.in b/libs/icui18n/Makefile.in
index b7c50bf58136..fccc0e26ddd6 100644
--- a/libs/icui18n/Makefile.in
+++ b/libs/icui18n/Makefile.in
@@ -3,7 +3,6 @@ EXTRAINCL = $(ICUCOMMON_PE_CFLAGS)
 EXTRADEFS = -DU_DISABLE_RENAMING=1 -DU_STATIC_IMPLEMENTATION -DU_I18N_IMPLEMENTATION -DU_ATTRIBUTE_DEPRECATED=
 
 SOURCES = \
-	alphaindex.cpp \
 	anytrans.cpp \
 	astro.cpp \
 	basictz.cpp \
@@ -48,7 +47,6 @@ SOURCES = \
 	csrucode.cpp \
 	csrutf8.cpp \
 	curramt.cpp \
-	currfmt.cpp \
 	currpinf.cpp \
 	currunit.cpp \
 	dangical.cpp \
@@ -75,7 +73,6 @@ SOURCES = \
 	esctrn.cpp \
 	ethpccal.cpp \
 	fmtable.cpp \
-	fmtable_cnv.cpp \
 	format.cpp \
 	formatted_string_builder.cpp \
 	formattedval_iterimpl.cpp \
@@ -93,7 +90,6 @@ SOURCES = \
 	islamcal.cpp \
 	japancal.cpp \
 	listformatter.cpp \
-	measfmt.cpp \
 	measunit.cpp \
 	measunit_extra.cpp \
 	measure.cpp \
@@ -169,9 +165,7 @@ SOURCES = \
 	remtrans.cpp \
 	repattrn.cpp \
 	rulebasedcollator.cpp \
-	scientificnumberformatter.cpp \
 	scriptset.cpp \
-	search.cpp \
 	selfmt.cpp \
 	sharedbreakiterator.cpp \
 	simpletz.cpp \
@@ -182,13 +176,9 @@ SOURCES = \
 	string_segment.cpp \
 	strmatch.cpp \
 	strrepl.cpp \
-	stsearch.cpp \
 	taiwncal.cpp \
 	timezone.cpp \
 	titletrn.cpp \
-	tmunit.cpp \
-	tmutamt.cpp \
-	tmutfmt.cpp \
 	tolowtrn.cpp \
 	toupptrn.cpp \
 	translit.cpp \
@@ -236,11 +226,7 @@ SOURCES = \
 	utf8collationiterator.cpp \
 	utmscale.cpp \
 	utrans.cpp \
-	vtzone.cpp \
-	vzone.cpp \
 	windtfmt.cpp \
 	winnmfmt.cpp \
 	wintzimpl.cpp \
-	zonemeta.cpp \
-	zrule.cpp \
-	ztrans.cpp
+	zonemeta.cpp
diff --git a/libs/icui18n/alphaindex.cpp b/libs/icui18n/alphaindex.cpp
deleted file mode 100644
index b980b326034c..000000000000
--- a/libs/icui18n/alphaindex.cpp
+++ /dev/null
@@ -1,1235 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 2009-2014, International Business Machines Corporation and
-* others. All Rights Reserved.
-*******************************************************************************
-*/
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_COLLATION
-
-#include "unicode/alphaindex.h"
-#include "unicode/coll.h"
-#include "unicode/localpointer.h"
-#include "unicode/normalizer2.h"
-#include "unicode/tblcoll.h"
-#include "unicode/uchar.h"
-#include "unicode/ulocdata.h"
-#include "unicode/uniset.h"
-#include "unicode/uobject.h"
-#include "unicode/usetiter.h"
-#include "unicode/utf16.h"
-
-#include "cmemory.h"
-#include "cstring.h"
-#include "uassert.h"
-#include "uvector.h"
-#include "uvectr64.h"
-
-//#include <string>
-//#include <iostream>
-
-U_NAMESPACE_BEGIN
-
-namespace {
-
-/**
- * Prefix string for Chinese index buckets.
- * See http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-collation.html#Collation_Indexes
- */
-const UChar BASE[1] = { 0xFDD0 };
-const int32_t BASE_LENGTH = 1;
-
-UBool isOneLabelBetterThanOther(const Normalizer2 &nfkdNormalizer,
-                                const UnicodeString &one, const UnicodeString &other);
-
-}  // namespace
-
-static int32_t U_CALLCONV
-collatorComparator(const void *context, const void *left, const void *right);
-
-static int32_t U_CALLCONV
-recordCompareFn(const void *context, const void *left, const void *right);
-
-//  UVector<Record *> support function, delete a Record.
-static void U_CALLCONV
-alphaIndex_deleteRecord(void *obj) {
-    delete static_cast<AlphabeticIndex::Record *>(obj);
-}
-
-namespace {
-
-UnicodeString *ownedString(const UnicodeString &s, LocalPointer<UnicodeString> &owned,
-                           UErrorCode &errorCode) {
-    if (U_FAILURE(errorCode)) { return NULL; }
-    if (owned.isValid()) {
-        return owned.orphan();
-    }
-    UnicodeString *p = new UnicodeString(s);
-    if (p == NULL) {
-        errorCode = U_MEMORY_ALLOCATION_ERROR;
-    }
-    return p;
-}
-
-inline UnicodeString *getString(const UVector &list, int32_t i) {
-    return static_cast<UnicodeString *>(list[i]);
-}
-
-inline AlphabeticIndex::Bucket *getBucket(const UVector &list, int32_t i) {
-    return static_cast<AlphabeticIndex::Bucket *>(list[i]);
-}
-
-inline AlphabeticIndex::Record *getRecord(const UVector &list, int32_t i) {
-    return static_cast<AlphabeticIndex::Record *>(list[i]);
-}
-
-/**
- * Like Java Collections.binarySearch(List, String, Comparator).
- *
- * @return the index>=0 where the item was found,
- *         or the index<0 for inserting the string at ~index in sorted order
- */
-int32_t binarySearch(const UVector &list, const UnicodeString &s, const Collator &coll) {
-    if (list.size() == 0) { return ~0; }
-    int32_t start = 0;
-    int32_t limit = list.size();
-    for (;;) {
-        int32_t i = (start + limit) / 2;
-        const UnicodeString *si = static_cast<UnicodeString *>(list.elementAt(i));
-        UErrorCode errorCode = U_ZERO_ERROR;
-        UCollationResult cmp = coll.compare(s, *si, errorCode);
-        if (cmp == UCOL_EQUAL) {
-            return i;
-        } else if (cmp < 0) {
-            if (i == start) {
-                return ~start;  // insert s before *si
-            }
-            limit = i;
-        } else {
-            if (i == start) {
-                return ~(start + 1);  // insert s after *si
-            }
-            start = i;
-        }
-    }
-}
-
-}  // namespace
-
-// The BucketList is not in the anonymous namespace because only Clang
-// seems to support its use in other classes from there.
-// However, we also don't need U_I18N_API because it is not used from outside the i18n library.
-class BucketList : public UObject {
-public:
-    BucketList(UVector *bucketList, UVector *publicBucketList)
-            : bucketList_(bucketList), immutableVisibleList_(publicBucketList) {
-        int32_t displayIndex = 0;
-        for (int32_t i = 0; i < publicBucketList->size(); ++i) {
-            getBucket(*publicBucketList, i)->displayIndex_ = displayIndex++;
-        }
-    }
-
-    // The virtual destructor must not be inline.
-    // See ticket #8454 for details.
-    virtual ~BucketList();
-
-    int32_t getBucketCount() const {
-        return immutableVisibleList_->size();
-    }
-
-    int32_t getBucketIndex(const UnicodeString &name, const Collator &collatorPrimaryOnly,
-                           UErrorCode &errorCode) {
-        // binary search
-        int32_t start = 0;
-        int32_t limit = bucketList_->size();
-        while ((start + 1) < limit) {
-            int32_t i = (start + limit) / 2;
-            const AlphabeticIndex::Bucket *bucket = getBucket(*bucketList_, i);
-            UCollationResult nameVsBucket =
-                collatorPrimaryOnly.compare(name, bucket->lowerBoundary_, errorCode);
-            if (nameVsBucket < 0) {
-                limit = i;
-            } else {
-                start = i;
-            }
-        }
-        const AlphabeticIndex::Bucket *bucket = getBucket(*bucketList_, start);
-        if (bucket->displayBucket_ != NULL) {
-            bucket = bucket->displayBucket_;
-        }
-        return bucket->displayIndex_;
-    }
-
-    /** All of the buckets, visible and invisible. */
-    UVector *bucketList_;
-    /** Just the visible buckets. */
-    UVector *immutableVisibleList_;
-};
-
-BucketList::~BucketList() {
-    delete bucketList_;
-    if (immutableVisibleList_ != bucketList_) {
-        delete immutableVisibleList_;
-    }
-}
-
-AlphabeticIndex::ImmutableIndex::~ImmutableIndex() {
-    delete buckets_;
-    delete collatorPrimaryOnly_;
-}
-
-int32_t
-AlphabeticIndex::ImmutableIndex::getBucketCount() const {
-    return buckets_->getBucketCount();
-}
-
-int32_t
-AlphabeticIndex::ImmutableIndex::getBucketIndex(
-        const UnicodeString &name, UErrorCode &errorCode) const {
-    return buckets_->getBucketIndex(name, *collatorPrimaryOnly_, errorCode);
-}
-
-const AlphabeticIndex::Bucket *
-AlphabeticIndex::ImmutableIndex::getBucket(int32_t index) const {
-    if (0 <= index && index < buckets_->getBucketCount()) {
-        return icu::getBucket(*buckets_->immutableVisibleList_, index);
-    } else {
-        return NULL;
-    }
-}
-
-AlphabeticIndex::AlphabeticIndex(const Locale &locale, UErrorCode &status)
-        : inputList_(NULL),
-          labelsIterIndex_(-1), itemsIterIndex_(0), currentBucket_(NULL),
-          maxLabelCount_(99),
-          initialLabels_(NULL), firstCharsInScripts_(NULL),
-          collator_(NULL), collatorPrimaryOnly_(NULL),
-          buckets_(NULL) {
-    init(&locale, status);
-}
-
-
-AlphabeticIndex::AlphabeticIndex(RuleBasedCollator *collator, UErrorCode &status)
-        : inputList_(NULL),
-          labelsIterIndex_(-1), itemsIterIndex_(0), currentBucket_(NULL),
-          maxLabelCount_(99),
-          initialLabels_(NULL), firstCharsInScripts_(NULL),
-          collator_(collator), collatorPrimaryOnly_(NULL),
-          buckets_(NULL) {
-    init(NULL, status);
-}
-
-
-
-AlphabeticIndex::~AlphabeticIndex() {
-    delete collator_;
-    delete collatorPrimaryOnly_;
-    delete firstCharsInScripts_;
-    delete buckets_;
-    delete inputList_;
-    delete initialLabels_;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::addLabels(const UnicodeSet &additions, UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return *this;
-    }
-    initialLabels_->addAll(additions);
-    clearBuckets();
-    return *this;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::addLabels(const Locale &locale, UErrorCode &status) {
-    addIndexExemplars(locale, status);
-    clearBuckets();
-    return *this;
-}
-
-
-AlphabeticIndex::ImmutableIndex *AlphabeticIndex::buildImmutableIndex(UErrorCode &errorCode) {
-    if (U_FAILURE(errorCode)) { return NULL; }
-    // In C++, the ImmutableIndex must own its copy of the BucketList,
-    // even if it contains no records, for proper memory management.
-    // We could clone the buckets_ if they are not NULL,
-    // but that would be worth it only if this method is called multiple times,
-    // or called after using the old-style bucket iterator API.
-    LocalPointer<BucketList> immutableBucketList(createBucketList(errorCode));
-    LocalPointer<RuleBasedCollator> coll(collatorPrimaryOnly_->clone());
-    if (immutableBucketList.isNull() || coll.isNull()) {
-        errorCode = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-    ImmutableIndex *immIndex = new ImmutableIndex(immutableBucketList.getAlias(), coll.getAlias());
-    if (immIndex == NULL) {
-        errorCode = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-    // The ImmutableIndex adopted its parameter objects.
-    immutableBucketList.orphan();
-    coll.orphan();
-    return immIndex;
-}
-
-int32_t AlphabeticIndex::getBucketCount(UErrorCode &status) {
-    initBuckets(status);
-    if (U_FAILURE(status)) {
-        return 0;
-    }
-    return buckets_->getBucketCount();
-}
-
-
-int32_t AlphabeticIndex::getRecordCount(UErrorCode &status) {
-    if (U_FAILURE(status) || inputList_ == NULL) {
-        return 0;
-    }
-    return inputList_->size();
-}
-
-void AlphabeticIndex::initLabels(UVector &indexCharacters, UErrorCode &errorCode) const {
-    U_ASSERT(indexCharacters.hasDeleter());
-    const Normalizer2 *nfkdNormalizer = Normalizer2::getNFKDInstance(errorCode);
-    if (U_FAILURE(errorCode)) { return; }
-
-    const UnicodeString &firstScriptBoundary = *getString(*firstCharsInScripts_, 0);
-    const UnicodeString &overflowBoundary =
-        *getString(*firstCharsInScripts_, firstCharsInScripts_->size() - 1);
-
-    // We make a sorted array of elements.
-    // Some of the input may be redundant.
-    // That is, we might have c, ch, d, where "ch" sorts just like "c", "h".
-    // We filter out those cases.
-    UnicodeSetIterator iter(*initialLabels_);
-    while (U_SUCCESS(errorCode) && iter.next()) {
-        const UnicodeString *item = &iter.getString();
-        LocalPointer<UnicodeString> ownedItem;
-        UBool checkDistinct;
-        int32_t itemLength = item->length();
-        if (!item->hasMoreChar32Than(0, itemLength, 1)) {
-            checkDistinct = false;
-        } else if(item->charAt(itemLength - 1) == 0x2a &&  // '*'
-                item->charAt(itemLength - 2) != 0x2a) {
-            // Use a label if it is marked with one trailing star,
-            // even if the label string sorts the same when all contractions are suppressed.
-            ownedItem.adoptInstead(new UnicodeString(*item, 0, itemLength - 1));
-            item = ownedItem.getAlias();
-            if (item == NULL) {
-                errorCode = U_MEMORY_ALLOCATION_ERROR;
-                return;
-            }
-            checkDistinct = false;
-        } else {
-            checkDistinct = true;
-        }
-        if (collatorPrimaryOnly_->compare(*item, firstScriptBoundary, errorCode) < 0) {
-            // Ignore a primary-ignorable or non-alphabetic index character.
-        } else if (collatorPrimaryOnly_->compare(*item, overflowBoundary, errorCode) >= 0) {
-            // Ignore an index character that will land in the overflow bucket.
-        } else if (checkDistinct &&
-                collatorPrimaryOnly_->compare(*item, separated(*item), errorCode) == 0) {
-            // Ignore a multi-code point index character that does not sort distinctly
-            // from the sequence of its separate characters.
-        } else {
-            int32_t insertionPoint = binarySearch(indexCharacters, *item, *collatorPrimaryOnly_);
-            if (insertionPoint < 0) {
-                indexCharacters.insertElementAt(
-                    ownedString(*item, ownedItem, errorCode), ~insertionPoint, errorCode);
-            } else {
-                const UnicodeString &itemAlreadyIn = *getString(indexCharacters, insertionPoint);
-                if (isOneLabelBetterThanOther(*nfkdNormalizer, *item, itemAlreadyIn)) {
-                    indexCharacters.setElementAt(
-                        ownedString(*item, ownedItem, errorCode), insertionPoint);
-                }
-            }
-        }
-    }
-    if (U_FAILURE(errorCode)) { return; }
-
-    // if the result is still too large, cut down to maxLabelCount_ elements, by removing every nth element
-
-    int32_t size = indexCharacters.size() - 1;
-    if (size > maxLabelCount_) {
-        int32_t count = 0;
-        int32_t old = -1;
-        for (int32_t i = 0; i < indexCharacters.size();) {
-            ++count;
-            int32_t bump = count * maxLabelCount_ / size;
-            if (bump == old) {
-                indexCharacters.removeElementAt(i);
-            } else {
-                old = bump;
-                ++i;
-            }
-        }
-    }
-}
-
-namespace {
-
-const UnicodeString &fixLabel(const UnicodeString &current, UnicodeString &temp) {
-    if (!current.startsWith(BASE, BASE_LENGTH)) {
-        return current;
-    }
-    UChar rest = current.charAt(BASE_LENGTH);
-    if (0x2800 < rest && rest <= 0x28FF) { // stroke count
-        int32_t count = rest-0x2800;
-        temp.setTo((UChar)(0x30 + count % 10));
-        if (count >= 10) {
-            count /= 10;
-            temp.insert(0, (UChar)(0x30 + count % 10));
-            if (count >= 10) {
-                count /= 10;
-                temp.insert(0, (UChar)(0x30 + count));
-            }
-        }
-        return temp.append((UChar)0x5283);
-    }
-    return temp.setTo(current, BASE_LENGTH);
-}
-
-UBool hasMultiplePrimaryWeights(
-        const RuleBasedCollator &coll, uint32_t variableTop,
-        const UnicodeString &s, UVector64 &ces, UErrorCode &errorCode) {
-    ces.removeAllElements();
-    coll.internalGetCEs(s, ces, errorCode);
-    if (U_FAILURE(errorCode)) { return false; }
-    UBool seenPrimary = false;
-    for (int32_t i = 0; i < ces.size(); ++i) {
-        int64_t ce = ces.elementAti(i);
-        uint32_t p = (uint32_t)(ce >> 32);
-        if (p > variableTop) {
-            // not primary ignorable
-            if (seenPrimary) {
-                return true;
-            }
-            seenPrimary = true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const {
-    // Initialize indexCharacters.
-    UVector indexCharacters(errorCode);
-    indexCharacters.setDeleter(uprv_deleteUObject);
-    initLabels(indexCharacters, errorCode);
-    if (U_FAILURE(errorCode)) { return NULL; }
-
-    // Variables for hasMultiplePrimaryWeights().
-    UVector64 ces(errorCode);
-    uint32_t variableTop;
-    if (collatorPrimaryOnly_->getAttribute(UCOL_ALTERNATE_HANDLING, errorCode) == UCOL_SHIFTED) {
-        variableTop = collatorPrimaryOnly_->getVariableTop(errorCode);
-    } else {
-        variableTop = 0;
-    }
-    UBool hasInvisibleBuckets = false;
-
-    // Helper arrays for Chinese Pinyin collation.
-    Bucket *asciiBuckets[26] = {
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-    };
-    Bucket *pinyinBuckets[26] = {
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-    };
-    UBool hasPinyin = false;
-
-    LocalPointer<UVector> bucketList(new UVector(errorCode), errorCode);
-    if (U_FAILURE(errorCode)) {
-        return NULL;
-    }
-    bucketList->setDeleter(uprv_deleteUObject);
-
-    // underflow bucket
-    LocalPointer<Bucket> bucket(new Bucket(getUnderflowLabel(), emptyString_, U_ALPHAINDEX_UNDERFLOW), errorCode);
-    if (U_FAILURE(errorCode)) {
-        return NULL;
-    }
-    bucketList->adoptElement(bucket.orphan(), errorCode);
-    if (U_FAILURE(errorCode)) { return NULL; }
-
-    UnicodeString temp;
-
-    // fix up the list, adding underflow, additions, overflow
-    // Insert inflow labels as needed.
-    int32_t scriptIndex = -1;
-    const UnicodeString *scriptUpperBoundary = &emptyString_;
-    for (int32_t i = 0; i < indexCharacters.size(); ++i) {
-        UnicodeString &current = *getString(indexCharacters, i);
-        if (collatorPrimaryOnly_->compare(current, *scriptUpperBoundary, errorCode) >= 0) {
-            // We crossed the script boundary into a new script.
-            const UnicodeString &inflowBoundary = *scriptUpperBoundary;
-            UBool skippedScript = false;
-            for (;;) {
-                scriptUpperBoundary = getString(*firstCharsInScripts_, ++scriptIndex);
-                if (collatorPrimaryOnly_->compare(current, *scriptUpperBoundary, errorCode) < 0) {
-                    break;
-                }
-                skippedScript = true;
-            }
-            if (skippedScript && bucketList->size() > 1) {
-                // We are skipping one or more scripts,
-                // and we are not just getting out of the underflow label.
-                bucket.adoptInsteadAndCheckErrorCode(
-                    new Bucket(getInflowLabel(), inflowBoundary, U_ALPHAINDEX_INFLOW), errorCode);
-                bucketList->adoptElement(bucket.orphan(), errorCode);
-                if (U_FAILURE(errorCode)) { return nullptr; }
-            }
-        }
-        // Add a bucket with the current label.
-        bucket.adoptInsteadAndCheckErrorCode(
-            new Bucket(fixLabel(current, temp), current, U_ALPHAINDEX_NORMAL), errorCode);
-        bucketList->adoptElement(bucket.orphan(), errorCode);
-        if (U_FAILURE(errorCode)) { return nullptr; }
-        // Remember ASCII and Pinyin buckets for Pinyin redirects.
-        UChar c;
-        if (current.length() == 1 && 0x41 <= (c = current.charAt(0)) && c <= 0x5A) {  // A-Z
-            asciiBuckets[c - 0x41] = (Bucket *)bucketList->lastElement();
-        } else if (current.length() == BASE_LENGTH + 1 && current.startsWith(BASE, BASE_LENGTH) &&
-                0x41 <= (c = current.charAt(BASE_LENGTH)) && c <= 0x5A) {
-            pinyinBuckets[c - 0x41] = (Bucket *)bucketList->lastElement();
-            hasPinyin = true;
-        }
-        // Check for multiple primary weights.
-        if (!current.startsWith(BASE, BASE_LENGTH) &&
-                hasMultiplePrimaryWeights(*collatorPrimaryOnly_, variableTop, current,
-                                          ces, errorCode) &&
-                current.charAt(current.length() - 1) != 0xFFFF /* !current.endsWith("\uffff") */) {
-            // "AE-ligature" or "Sch" etc.
-            for (int32_t j = bucketList->size() - 2;; --j) {
-                Bucket *singleBucket = getBucket(*bucketList, j);
-                if (singleBucket->labelType_ != U_ALPHAINDEX_NORMAL) {
-                    // There is no single-character bucket since the last
-                    // underflow or inflow label.
-                    break;
-                }
-                if (singleBucket->displayBucket_ == NULL &&
-                        !hasMultiplePrimaryWeights(*collatorPrimaryOnly_, variableTop,
-                                                   singleBucket->lowerBoundary_,
-                                                   ces, errorCode)) {
-                    // Add an invisible bucket that redirects strings greater than the expansion
-                    // to the previous single-character bucket.
-                    // For example, after ... Q R S Sch we add Sch\uFFFF->S
-                    // and after ... Q R S Sch Sch\uFFFF St we add St\uFFFF->S.
-                    bucket.adoptInsteadAndCheckErrorCode(new Bucket(emptyString_,
-                        UnicodeString(current).append((UChar)0xFFFF),
-                        U_ALPHAINDEX_NORMAL),
-                        errorCode);
-                    if (U_FAILURE(errorCode)) {
-                        return NULL;
-                    }
-                    bucket->displayBucket_ = singleBucket;
-                    bucketList->adoptElement(bucket.orphan(), errorCode);
-                    if (U_FAILURE(errorCode)) { return nullptr; }
-                    hasInvisibleBuckets = true;
-                    break;
-                }
-            }
-        }
-    }
-    if (U_FAILURE(errorCode)) { return NULL; }
-    if (bucketList->size() == 1) {
-        // No real labels, show only the underflow label.
-        BucketList *bl = new BucketList(bucketList.getAlias(), bucketList.getAlias());
-        if (bl == NULL) {
-            errorCode = U_MEMORY_ALLOCATION_ERROR;
-            return NULL;
-        }
-        bucketList.orphan();
-        return bl;
-    }
-    // overflow bucket
-    bucket.adoptInsteadAndCheckErrorCode(
-        new Bucket(getOverflowLabel(), *scriptUpperBoundary, U_ALPHAINDEX_OVERFLOW), errorCode);
-    bucketList->adoptElement(bucket.orphan(), errorCode); // final
-    if (U_FAILURE(errorCode)) { return nullptr; }
-
-    if (hasPinyin) {
-        // Redirect Pinyin buckets.
-        Bucket *asciiBucket = NULL;
-        for (int32_t i = 0; i < 26; ++i) {
-            if (asciiBuckets[i] != NULL) {
-                asciiBucket = asciiBuckets[i];
-            }
-            if (pinyinBuckets[i] != NULL && asciiBucket != NULL) {
-                pinyinBuckets[i]->displayBucket_ = asciiBucket;
-                hasInvisibleBuckets = true;
-            }
-        }
-    }
-
-    if (U_FAILURE(errorCode)) { return NULL; }
-    if (!hasInvisibleBuckets) {
-        BucketList *bl = new BucketList(bucketList.getAlias(), bucketList.getAlias());
-        if (bl == NULL) {
-            errorCode = U_MEMORY_ALLOCATION_ERROR;
-            return NULL;
-        }
-        bucketList.orphan();
-        return bl;
-    }
-    // Merge inflow buckets that are visually adjacent.
-    // Iterate backwards: Merge inflow into overflow rather than the other way around.
-    int32_t i = bucketList->size() - 1;
-    Bucket *nextBucket = getBucket(*bucketList, i);
-    while (--i > 0) {
-        Bucket *bucket = getBucket(*bucketList, i);
-        if (bucket->displayBucket_ != NULL) {
-            continue;  // skip invisible buckets
-        }
-        if (bucket->labelType_ == U_ALPHAINDEX_INFLOW) {
-            if (nextBucket->labelType_ != U_ALPHAINDEX_NORMAL) {
-                bucket->displayBucket_ = nextBucket;
-                continue;
-            }
-        }
-        nextBucket = bucket;
-    }
-
-    LocalPointer<UVector> publicBucketList(new UVector(errorCode), errorCode);
-    if (U_FAILURE(errorCode)) {
-        return NULL;
-    }
-    // Do not call publicBucketList->setDeleter():
-    // This vector shares its objects with the bucketList.
-    for (int32_t j = 0; j < bucketList->size(); ++j) {
-        Bucket *bucket = getBucket(*bucketList, j);
-        if (bucket->displayBucket_ == NULL) {
-            publicBucketList->addElement(bucket, errorCode);
-        }
-    }
-    if (U_FAILURE(errorCode)) { return NULL; }
-    BucketList *bl = new BucketList(bucketList.getAlias(), publicBucketList.getAlias());
-    if (bl == NULL) {
-        errorCode = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-    bucketList.orphan();
-    publicBucketList.orphan();
-    return bl;
-}
-
-/**
- * Creates an index, and buckets and sorts the list of records into the index.
- */
-void AlphabeticIndex::initBuckets(UErrorCode &errorCode) {
-    if (U_FAILURE(errorCode) || buckets_ != NULL) {
-        return;
-    }
-    buckets_ = createBucketList(errorCode);
-    if (U_FAILURE(errorCode) || inputList_ == NULL || inputList_->isEmpty()) {
-        return;
-    }
-
-    // Sort the records by name.
-    // Stable sort preserves input order of collation duplicates.
-    inputList_->sortWithUComparator(recordCompareFn, collator_, errorCode);
-
-    // Now, we traverse all of the input, which is now sorted.
-    // If the item doesn't go in the current bucket, we find the next bucket that contains it.
-    // This makes the process order n*log(n), since we just sort the list and then do a linear process.
-    // However, if the user adds an item at a time and then gets the buckets, this isn't efficient, so
-    // we need to improve it for that case.
-
-    Bucket *currentBucket = getBucket(*buckets_->bucketList_, 0);
-    int32_t bucketIndex = 1;
-    Bucket *nextBucket;
-    const UnicodeString *upperBoundary;
-    if (bucketIndex < buckets_->bucketList_->size()) {
-        nextBucket = getBucket(*buckets_->bucketList_, bucketIndex++);
-        upperBoundary = &nextBucket->lowerBoundary_;
-    } else {
-        nextBucket = NULL;
-        upperBoundary = NULL;
-    }
-    for (int32_t i = 0; i < inputList_->size(); ++i) {
-        Record *r = getRecord(*inputList_, i);
-        // if the current bucket isn't the right one, find the one that is
-        // We have a special flag for the last bucket so that we don't look any further
-        while (upperBoundary != NULL &&
-                collatorPrimaryOnly_->compare(r->name_, *upperBoundary, errorCode) >= 0) {
-            currentBucket = nextBucket;
-            // now reset the boundary that we compare against
-            if (bucketIndex < buckets_->bucketList_->size()) {
-                nextBucket = getBucket(*buckets_->bucketList_, bucketIndex++);
-                upperBoundary = &nextBucket->lowerBoundary_;
-            } else {
-                upperBoundary = NULL;
-            }
-        }
-        // now put the record into the bucket.
-        Bucket *bucket = currentBucket;
-        if (bucket->displayBucket_ != NULL) {
-            bucket = bucket->displayBucket_;
-        }
-        if (bucket->records_ == NULL) {
-            LocalPointer<UVector> records(new UVector(errorCode), errorCode);
-            if (U_FAILURE(errorCode)) {
-                return;
-            }
-            bucket->records_ = records.orphan();
-        }
-        bucket->records_->addElement(r, errorCode);
-    }
-}
-
-void AlphabeticIndex::clearBuckets() {
-    if (buckets_ != NULL) {
-        delete buckets_;
-        buckets_ = NULL;
-        internalResetBucketIterator();
-    }
-}
-
-void AlphabeticIndex::internalResetBucketIterator() {
-    labelsIterIndex_ = -1;
-    currentBucket_ = NULL;
-}
-
-
-void AlphabeticIndex::addIndexExemplars(const Locale &locale, UErrorCode &status) {
-    LocalULocaleDataPointer uld(ulocdata_open(locale.getName(), &status));
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    UnicodeSet exemplars;
-    ulocdata_getExemplarSet(uld.getAlias(), exemplars.toUSet(), 0, ULOCDATA_ES_INDEX, &status);
-    if (U_SUCCESS(status)) {
-        initialLabels_->addAll(exemplars);
-        return;
-    }
-    status = U_ZERO_ERROR;  // Clear out U_MISSING_RESOURCE_ERROR
-
-    // The locale data did not include explicit Index characters.
-    // Synthesize a set of them from the locale's standard exemplar characters.
-    ulocdata_getExemplarSet(uld.getAlias(), exemplars.toUSet(), 0, ULOCDATA_ES_STANDARD, &status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    // question: should we add auxiliary exemplars?
-    if (exemplars.containsSome(0x61, 0x7A) /* a-z */ || exemplars.isEmpty()) {
-        exemplars.add(0x61, 0x7A);
-    }
-    if (exemplars.containsSome(0xAC00, 0xD7A3)) {  // Hangul syllables
-        // cut down to small list
-        exemplars.remove(0xAC00, 0xD7A3).
-            add(0xAC00).add(0xB098).add(0xB2E4).add(0xB77C).
-            add(0xB9C8).add(0xBC14).add(0xC0AC).add(0xC544).
-            add(0xC790).add(0xCC28).add(0xCE74).add(0xD0C0).
-            add(0xD30C).add(0xD558);
-    }
-    if (exemplars.containsSome(0x1200, 0x137F)) {  // Ethiopic block
-        // cut down to small list
-        // make use of the fact that Ethiopic is allocated in 8's, where
-        // the base is 0 mod 8.
-        UnicodeSet ethiopic(UnicodeString(u"[ሀለሐመሠረሰሸቀቈቐቘበቨተቸኀኈነኘአከኰኸዀወዐዘዠየደዸጀገጐጘጠጨጰጸፀፈፐፘ]"), status);
-        ethiopic.retainAll(exemplars);
-        exemplars.remove(u'ሀ', 0x137F).addAll(ethiopic);
-    }
-
-    // Upper-case any that aren't already so.
-    //   (We only do this for synthesized index characters.)
-    UnicodeSetIterator it(exemplars);
-    UnicodeString upperC;
-    while (it.next()) {
-        const UnicodeString &exemplarC = it.getString();
-        upperC = exemplarC;
-        upperC.toUpper(locale);
-        initialLabels_->add(upperC);
-    }
-}
-
-UBool AlphabeticIndex::addChineseIndexCharacters(UErrorCode &errorCode) {
-    UnicodeSet contractions;
-    collatorPrimaryOnly_->internalAddContractions(BASE[0], contractions, errorCode);
-    if (U_FAILURE(errorCode) || contractions.isEmpty()) { return false; }
-    initialLabels_->addAll(contractions);
-    UnicodeSetIterator iter(contractions);
-    while (iter.next()) {
-        const UnicodeString &s = iter.getString();
-        U_ASSERT (s.startsWith(BASE, BASE_LENGTH));
-        UChar c = s.charAt(s.length() - 1);
-        if (0x41 <= c && c <= 0x5A) {  // A-Z
-            // There are Pinyin labels, add ASCII A-Z labels as well.
-            initialLabels_->add(0x41, 0x5A);  // A-Z
-            break;
-        }
-    }
-    return true;
-}
-
-
-/*
- * Return the string with interspersed CGJs. Input must have more than 2 codepoints.
- */
-static const UChar CGJ = 0x034F;
-UnicodeString AlphabeticIndex::separated(const UnicodeString &item) {
-    UnicodeString result;
-    if (item.length() == 0) {
-        return result;
-    }
-    int32_t i = 0;
-    for (;;) {
-        UChar32  cp = item.char32At(i);
-        result.append(cp);
-        i = item.moveIndex32(i, 1);
-        if (i >= item.length()) {
-            break;
-        }
-        result.append(CGJ);
-    }
-    return result;
-}
-
-
-bool AlphabeticIndex::operator==(const AlphabeticIndex& /* other */) const {
-    return false;
-}
-
-
-bool AlphabeticIndex::operator!=(const AlphabeticIndex& /* other */) const {
-    return false;
-}
-
-
-const RuleBasedCollator &AlphabeticIndex::getCollator() const {
-    return *collator_;
-}
-
-
-const UnicodeString &AlphabeticIndex::getInflowLabel() const {
-    return inflowLabel_;
-}
-
-const UnicodeString &AlphabeticIndex::getOverflowLabel() const {
-    return overflowLabel_;
-}
-
-
-const UnicodeString &AlphabeticIndex::getUnderflowLabel() const {
-    return underflowLabel_;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::setInflowLabel(const UnicodeString &label, UErrorCode &/*status*/) {
-    inflowLabel_ = label;
-    clearBuckets();
-    return *this;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::setOverflowLabel(const UnicodeString &label, UErrorCode &/*status*/) {
-    overflowLabel_ = label;
-    clearBuckets();
-    return *this;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::setUnderflowLabel(const UnicodeString &label, UErrorCode &/*status*/) {
-    underflowLabel_ = label;
-    clearBuckets();
-    return *this;
-}
-
-
-int32_t AlphabeticIndex::getMaxLabelCount() const {
-    return maxLabelCount_;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::setMaxLabelCount(int32_t maxLabelCount, UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return *this;
-    }
-    if (maxLabelCount <= 0) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return *this;
-    }
-    maxLabelCount_ = maxLabelCount;
-    clearBuckets();
-    return *this;
-}
-
-
-//
-//  init() - Common code for constructors.
-//
-
-void AlphabeticIndex::init(const Locale *locale, UErrorCode &status) {
-    if (U_FAILURE(status)) { return; }
-    if (locale == NULL && collator_ == NULL) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-
-    initialLabels_         = new UnicodeSet();
-    if (initialLabels_ == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-
-    inflowLabel_.setTo((UChar)0x2026);    // Ellipsis
-    overflowLabel_ = inflowLabel_;
-    underflowLabel_ = inflowLabel_;
-
-    if (collator_ == NULL) {
-        Collator *coll = Collator::createInstance(*locale, status);
-        if (U_FAILURE(status)) {
-            delete coll;
-            return;
-        }
-        if (coll == NULL) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return;
-        }
-        collator_ = dynamic_cast<RuleBasedCollator *>(coll);
-        if (collator_ == NULL) {
-            delete coll;
-            status = U_UNSUPPORTED_ERROR;
-            return;
-        }
-    }
-    collatorPrimaryOnly_ = collator_->clone();
-    if (collatorPrimaryOnly_ == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    collatorPrimaryOnly_->setAttribute(UCOL_STRENGTH, UCOL_PRIMARY, status);
-    firstCharsInScripts_ = firstStringsInScript(status);
-    if (U_FAILURE(status)) { return; }
-    firstCharsInScripts_->sortWithUComparator(collatorComparator, collatorPrimaryOnly_, status);
-    // Guard against a degenerate collator where
-    // some script boundary strings are primary ignorable.
-    for (;;) {
-        if (U_FAILURE(status)) { return; }
-        if (firstCharsInScripts_->isEmpty()) {
-            // AlphabeticIndex requires some non-ignorable script boundary strings.
-            status = U_ILLEGAL_ARGUMENT_ERROR;
-            return;
-        }
-        if (collatorPrimaryOnly_->compare(
-                *static_cast<UnicodeString *>(firstCharsInScripts_->elementAt(0)),
-                emptyString_, status) == UCOL_EQUAL) {
-            firstCharsInScripts_->removeElementAt(0);
-        } else {
-            break;
-        }
-    }
-
-    // Chinese index characters, which are specific to each of the several Chinese tailorings,
-    // take precedence over the single locale data exemplar set per language.
-    if (!addChineseIndexCharacters(status) && locale != NULL) {
-        addIndexExemplars(*locale, status);
-    }
-}
-
-
-//
-//  Comparison function for UVector<UnicodeString *> sorting with a collator.
-//
-static int32_t U_CALLCONV
-collatorComparator(const void *context, const void *left, const void *right) {
-    const UElement *leftElement = static_cast<const UElement *>(left);
-    const UElement *rightElement = static_cast<const UElement *>(right);
-    const UnicodeString *leftString  = static_cast<const UnicodeString *>(leftElement->pointer);
-    const UnicodeString *rightString = static_cast<const UnicodeString *>(rightElement->pointer);
-
-    if (leftString == rightString) {
-        // Catches case where both are NULL
-        return 0;
-    }
-    if (leftString == NULL) {
-        return 1;
-    }
-    if (rightString == NULL) {
-        return -1;
-    }
-    const Collator *col = static_cast<const Collator *>(context);
-    UErrorCode errorCode = U_ZERO_ERROR;
-    return col->compare(*leftString, *rightString, errorCode);
-}
-
-//
-//  Comparison function for UVector<Record *> sorting with a collator.
-//
-static int32_t U_CALLCONV
-recordCompareFn(const void *context, const void *left, const void *right) {
-    const UElement *leftElement = static_cast<const UElement *>(left);
-    const UElement *rightElement = static_cast<const UElement *>(right);
-    const AlphabeticIndex::Record *leftRec  = static_cast<const AlphabeticIndex::Record *>(leftElement->pointer);
-    const AlphabeticIndex::Record *rightRec = static_cast<const AlphabeticIndex::Record *>(rightElement->pointer);
-    const Collator *col = static_cast<const Collator *>(context);
-    UErrorCode errorCode = U_ZERO_ERROR;
-    return col->compare(leftRec->name_, rightRec->name_, errorCode);
-}
-
-UVector *AlphabeticIndex::firstStringsInScript(UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    LocalPointer<UVector> dest(new UVector(status), status);
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    dest->setDeleter(uprv_deleteUObject);
-    // Fetch the script-first-primary contractions which are defined in the root collator.
-    // They all start with U+FDD1.
-    UnicodeSet set;
-    collatorPrimaryOnly_->internalAddContractions(0xFDD1, set, status);
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    if (set.isEmpty()) {
-        status = U_UNSUPPORTED_ERROR;
-        return NULL;
-    }
-    UnicodeSetIterator iter(set);
-    while (iter.next()) {
-        const UnicodeString &boundary = iter.getString();
-        uint32_t gcMask = U_GET_GC_MASK(boundary.char32At(1));
-        if ((gcMask & (U_GC_L_MASK | U_GC_CN_MASK)) == 0) {
-            // Ignore boundaries for the special reordering groups.
-            // Take only those for "real scripts" (where the sample character is a Letter,
-            // and the one for unassigned implicit weights (Cn).
-            continue;
-        }
-        LocalPointer<UnicodeString> s(new UnicodeString(boundary), status);
-        dest->adoptElement(s.orphan(), status);
-        if (U_FAILURE(status)) {
-            return nullptr;
-        }
-    }
-    return dest.orphan();
-}
-
-
-namespace {
-
-/**
- * Returns true if one index character string is "better" than the other.
- * Shorter NFKD is better, and otherwise NFKD-binary-less-than is
- * better, and otherwise binary-less-than is better.
- */
-UBool isOneLabelBetterThanOther(const Normalizer2 &nfkdNormalizer,
-                                const UnicodeString &one, const UnicodeString &other) {
-    // This is called with primary-equal strings, but never with one.equals(other).
-    UErrorCode status = U_ZERO_ERROR;
-    UnicodeString n1 = nfkdNormalizer.normalize(one, status);
-    UnicodeString n2 = nfkdNormalizer.normalize(other, status);
-    if (U_FAILURE(status)) { return false; }
-    int32_t result = n1.countChar32() - n2.countChar32();
-    if (result != 0) {
-        return result < 0;
-    }
-    result = n1.compareCodePointOrder(n2);
-    if (result != 0) {
-        return result < 0;
-    }
-    return one.compareCodePointOrder(other) < 0;
-}
-
-}  // namespace
-
-//
-//  Constructor & Destructor for AlphabeticIndex::Record
-//
-//     Records are internal only, instances are not directly surfaced in the public API.
-//     This class is mostly struct-like, with all public fields.
-
-AlphabeticIndex::Record::Record(const UnicodeString &name, const void *data)
-        : name_(name), data_(data) {}
-
-AlphabeticIndex::Record::~Record() {
-}
-
-
-AlphabeticIndex & AlphabeticIndex::addRecord(const UnicodeString &name, const void *data, UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return *this;
-    }
-    if (inputList_ == NULL) {
-        LocalPointer<UVector> inputList(new UVector(status), status);
-        if (U_FAILURE(status)) {
-            return *this;
-        }
-        inputList_ = inputList.orphan();
-        inputList_->setDeleter(alphaIndex_deleteRecord);
-    }
-    LocalPointer<Record> r(new Record(name, data), status);
-    inputList_->adoptElement(r.orphan(), status);
-    if (U_FAILURE(status)) {
-        return *this;
-    }
-    clearBuckets();
-    //std::string ss;
-    //std::string ss2;
-    //std::cout << "added record: name = \"" << r->name_.toUTF8String(ss) << "\"" <<
-    //             "   sortingName = \"" << r->sortingName_.toUTF8String(ss2) << "\"" << std::endl;
-    return *this;
-}
-
-
-AlphabeticIndex &AlphabeticIndex::clearRecords(UErrorCode &status) {
-    if (U_SUCCESS(status) && inputList_ != NULL && !inputList_->isEmpty()) {
-        inputList_->removeAllElements();
-        clearBuckets();
-    }
-    return *this;
-}
-
-int32_t AlphabeticIndex::getBucketIndex(const UnicodeString &name, UErrorCode &status) {
-    initBuckets(status);
-    if (U_FAILURE(status)) {
-        return 0;
-    }
-    return buckets_->getBucketIndex(name, *collatorPrimaryOnly_, status);
-}
-
-
-int32_t AlphabeticIndex::getBucketIndex() const {
-    return labelsIterIndex_;
-}
-
-
-UBool AlphabeticIndex::nextBucket(UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return false;
-    }
-    if (buckets_ == NULL && currentBucket_ != NULL) {
-        status = U_ENUM_OUT_OF_SYNC_ERROR;
-        return false;
-    }
-    initBuckets(status);
-    if (U_FAILURE(status)) {
-        return false;
-    }
-    ++labelsIterIndex_;
-    if (labelsIterIndex_ >= buckets_->getBucketCount()) {
-        labelsIterIndex_ = buckets_->getBucketCount();
-        return false;
-    }
-    currentBucket_ = getBucket(*buckets_->immutableVisibleList_, labelsIterIndex_);
-    resetRecordIterator();
-    return true;
-}
-
-const UnicodeString &AlphabeticIndex::getBucketLabel() const {
-    if (currentBucket_ != NULL) {
-        return currentBucket_->label_;
-    } else {
-        return emptyString_;
-    }
-}
-
-
-UAlphabeticIndexLabelType AlphabeticIndex::getBucketLabelType() const {
-    if (currentBucket_ != NULL) {
-        return currentBucket_->labelType_;
-    } else {
-        return U_ALPHAINDEX_NORMAL;
-    }
-}
-
-
-int32_t AlphabeticIndex::getBucketRecordCount() const {
-    if (currentBucket_ != NULL && currentBucket_->records_ != NULL) {
-        return currentBucket_->records_->size();
-    } else {
-        return 0;
-    }
-}
-
-
-AlphabeticIndex &AlphabeticIndex::resetBucketIterator(UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return *this;
-    }
-    internalResetBucketIterator();
-    return *this;
-}
-
-
-UBool AlphabeticIndex::nextRecord(UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return false;
-    }
-    if (currentBucket_ == NULL) {
-        // We are trying to iterate over the items in a bucket, but there is no
-        // current bucket from the enumeration of buckets.
-        status = U_INVALID_STATE_ERROR;
-        return false;
-    }
-    if (buckets_ == NULL) {
-        status = U_ENUM_OUT_OF_SYNC_ERROR;
-        return false;
-    }
-    if (currentBucket_->records_ == NULL) {
-        return false;
-    }
-    ++itemsIterIndex_;
-    if (itemsIterIndex_ >= currentBucket_->records_->size()) {
-        itemsIterIndex_  = currentBucket_->records_->size();
-        return false;
-    }
-    return true;
-}
-
-
-const UnicodeString &AlphabeticIndex::getRecordName() const {
-    const UnicodeString *retStr = &emptyString_;
-    if (currentBucket_ != NULL && currentBucket_->records_ != NULL &&
-        itemsIterIndex_ >= 0 &&
-        itemsIterIndex_ < currentBucket_->records_->size()) {
-            Record *item = static_cast<Record *>(currentBucket_->records_->elementAt(itemsIterIndex_));
-            retStr = &item->name_;
-    }
-    return *retStr;
-}
-
-const void *AlphabeticIndex::getRecordData() const {
-    const void *retPtr = NULL;
-    if (currentBucket_ != NULL && currentBucket_->records_ != NULL &&
-        itemsIterIndex_ >= 0 &&
-        itemsIterIndex_ < currentBucket_->records_->size()) {
-            Record *item = static_cast<Record *>(currentBucket_->records_->elementAt(itemsIterIndex_));
-            retPtr = item->data_;
-    }
-    return retPtr;
-}
-
-
-AlphabeticIndex & AlphabeticIndex::resetRecordIterator() {
-    itemsIterIndex_ = -1;
-    return *this;
-}
-
-
-
-AlphabeticIndex::Bucket::Bucket(const UnicodeString &label,
-                                const UnicodeString &lowerBoundary,
-                                UAlphabeticIndexLabelType type)
-        : label_(label), lowerBoundary_(lowerBoundary), labelType_(type),
-          displayBucket_(NULL), displayIndex_(-1),
-          records_(NULL) {
-}
-
-
-AlphabeticIndex::Bucket::~Bucket() {
-    delete records_;
-}
-
-U_NAMESPACE_END
-
-#endif  // !UCONFIG_NO_COLLATION
diff --git a/libs/icui18n/currfmt.cpp b/libs/icui18n/currfmt.cpp
deleted file mode 100644
index 0ad0492ee7af..000000000000
--- a/libs/icui18n/currfmt.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-* Copyright (c) 2004-2014 International Business Machines
-* Corporation and others.  All Rights Reserved.
-**********************************************************************
-* Author: Alan Liu
-* Created: April 20, 2004
-* Since: ICU 3.0
-**********************************************************************
-*/
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "currfmt.h"
-#include "unicode/numfmt.h"
-#include "unicode/curramt.h"
-
-U_NAMESPACE_BEGIN
-
-CurrencyFormat::CurrencyFormat(const Locale& locale, UErrorCode& ec) :
-    MeasureFormat(locale, UMEASFMT_WIDTH_WIDE, ec)
-{
-}
-
-CurrencyFormat::CurrencyFormat(const CurrencyFormat& other) :
-    MeasureFormat(other)
-{
-}
-
-CurrencyFormat::~CurrencyFormat() {
-}
-
-CurrencyFormat* CurrencyFormat::clone() const {
-    return new CurrencyFormat(*this);
-}
-
-UnicodeString& CurrencyFormat::format(const Formattable& obj,
-                                      UnicodeString& appendTo,
-                                      FieldPosition& pos,
-                                      UErrorCode& ec) const
-{
-    return getCurrencyFormatInternal().format(obj, appendTo, pos, ec);
-}
-
-void CurrencyFormat::parseObject(const UnicodeString& source,
-                                 Formattable& result,
-                                 ParsePosition& pos) const
-{
-    CurrencyAmount* currAmt = getCurrencyFormatInternal().parseCurrency(source, pos);
-    if (currAmt != NULL) {
-        result.adoptObject(currAmt);
-    }
-}
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyFormat)
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/libs/icui18n/currfmt.h b/libs/icui18n/currfmt.h
deleted file mode 100644
index 2a75cae2bb39..000000000000
--- a/libs/icui18n/currfmt.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-* Copyright (c) 2004-2014, International Business Machines
-* Corporation and others.  All Rights Reserved.
-**********************************************************************
-* Author: Alan Liu
-* Created: April 20, 2004
-* Since: ICU 3.0
-**********************************************************************
-*/
-#ifndef CURRENCYFORMAT_H
-#define CURRENCYFORMAT_H
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/measfmt.h"
-
-U_NAMESPACE_BEGIN
-
-class NumberFormat;
-
-/**
- * Temporary internal concrete subclass of MeasureFormat implementing
- * parsing and formatting of currency amount objects.  This class is
- * likely to be redesigned and rewritten in the near future.
- *
- * <p>This class currently delegates to DecimalFormat for parsing and
- * formatting.
- *
- * @see MeasureFormat
- * @author Alan Liu
- * @internal
- */
-class CurrencyFormat : public MeasureFormat {
-
- public:
-
-    /**
-     * Construct a CurrencyFormat for the given locale.
-     */
-    CurrencyFormat(const Locale& locale, UErrorCode& ec);
-
-    /**
-     * Copy constructor.
-     */
-    CurrencyFormat(const CurrencyFormat& other);
-
-    /**
-     * Destructor.
-     */
-    virtual ~CurrencyFormat();
-
-    /**
-     * Override Format API.
-     */
-    virtual CurrencyFormat* clone() const override;
-
-
-    using MeasureFormat::format;
-
-    /**
-     * Override Format API.
-     */
-    virtual UnicodeString& format(const Formattable& obj,
-                                  UnicodeString& appendTo,
-                                  FieldPosition& pos,
-                                  UErrorCode& ec) const override;
-
-    /**
-     * Override Format API.
-     */
-    virtual void parseObject(const UnicodeString& source,
-                             Formattable& result,
-                             ParsePosition& pos) const override;
-
-    /**
-     * Override Format API.
-     */
-    virtual UClassID getDynamicClassID() const override;
-
-    /**
-     * Returns the class ID for this class.
-     */
-    static UClassID U_EXPORT2 getStaticClassID();
-};
-
-U_NAMESPACE_END
-
-#endif // #if !UCONFIG_NO_FORMATTING
-#endif // #ifndef CURRENCYFORMAT_H
diff --git a/libs/icui18n/fmtable_cnv.cpp b/libs/icui18n/fmtable_cnv.cpp
deleted file mode 100644
index bc3847b6963e..000000000000
--- a/libs/icui18n/fmtable_cnv.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 1997-2010, International Business Machines Corporation and    *
-* others. All Rights Reserved.                                                *
-*******************************************************************************
-*
-* File FMTABLE.CPP
-*
-* Modification History:
-*
-*   Date        Name        Description
-*   03/25/97    clhuang     Initial Implementation.
-********************************************************************************
-*/
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_CONVERSION
-
-#include "unicode/fmtable.h"
-
-// *****************************************************************************
-// class Formattable
-// *****************************************************************************
-
-U_NAMESPACE_BEGIN
-
-// -------------------------------------
-// Creates a formattable object with a char* string.
-// This API is useless. The API that takes a UnicodeString is actually just as good.
-Formattable::Formattable(const char* stringToCopy)
-{
-    init();
-    fType = kString;
-    fValue.fString = new UnicodeString(stringToCopy);
-}
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_FORMATTING || !UCONFIG_NO_CONVERSION */
-
-//eof
diff --git a/libs/icui18n/measfmt.cpp b/libs/icui18n/measfmt.cpp
deleted file mode 100644
index f4ea4f7dde61..000000000000
--- a/libs/icui18n/measfmt.cpp
+++ /dev/null
@@ -1,893 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-* Copyright (c) 2004-2016, International Business Machines
-* Corporation and others.  All Rights Reserved.
-**********************************************************************
-* Author: Alan Liu
-* Created: April 20, 2004
-* Since: ICU 3.0
-**********************************************************************
-*/
-#include "utypeinfo.h"  // for 'typeid' to work
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/measfmt.h"
-#include "unicode/numfmt.h"
-#include "currfmt.h"
-#include "unicode/localpointer.h"
-#include "resource.h"
-#include "unicode/simpleformatter.h"
-#include "quantityformatter.h"
-#include "unicode/plurrule.h"
-#include "unicode/decimfmt.h"
-#include "uresimp.h"
-#include "unicode/ures.h"
-#include "unicode/ustring.h"
-#include "ureslocs.h"
-#include "cstring.h"
-#include "mutex.h"
-#include "ucln_in.h"
-#include "unicode/listformatter.h"
-#include "charstr.h"
-#include "unicode/putil.h"
-#include "unicode/smpdtfmt.h"
-#include "uassert.h"
-#include "unicode/numberformatter.h"
-#include "number_longnames.h"
-#include "number_utypes.h"
-
-#include "sharednumberformat.h"
-#include "sharedpluralrules.h"
-#include "standardplural.h"
-#include "unifiedcache.h"
-
-
-U_NAMESPACE_BEGIN
-
-using number::impl::UFormattedNumberData;
-
-static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1;
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)
-
-// Used to format durations like 5:47 or 21:35:42.
-class NumericDateFormatters : public UMemory {
-public:
-    // Formats like H:mm
-    UnicodeString hourMinute;
-
-    // formats like M:ss
-    UnicodeString minuteSecond;
-
-    // formats like H:mm:ss
-    UnicodeString hourMinuteSecond;
-
-    // Constructor that takes the actual patterns for hour-minute,
-    // minute-second, and hour-minute-second respectively.
-    NumericDateFormatters(
-            const UnicodeString &hm,
-            const UnicodeString &ms,
-            const UnicodeString &hms) :
-            hourMinute(hm),
-            minuteSecond(ms),
-            hourMinuteSecond(hms) {
-    }
-private:
-    NumericDateFormatters(const NumericDateFormatters &other);
-    NumericDateFormatters &operator=(const NumericDateFormatters &other);
-};
-
-static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) {
-    if (width >= WIDTH_INDEX_COUNT) {
-        return UMEASFMT_WIDTH_NARROW;
-    }
-    return width;
-}
-
-static UNumberUnitWidth getUnitWidth(UMeasureFormatWidth width) {
-    switch (width) {
-    case UMEASFMT_WIDTH_WIDE:
-        return UNUM_UNIT_WIDTH_FULL_NAME;
-    case UMEASFMT_WIDTH_NARROW:
-    case UMEASFMT_WIDTH_NUMERIC:
-        return UNUM_UNIT_WIDTH_NARROW;
-    case UMEASFMT_WIDTH_SHORT:
-    default:
-        return UNUM_UNIT_WIDTH_SHORT;
-    }
-}
-
-/**
- * Instances contain all MeasureFormat specific data for a particular locale.
- * This data is cached. It is never copied, but is shared via shared pointers.
- *
- * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
- * complete sets of unit & per patterns,
- * to correspond to the resource data and its aliases.
- *
- * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
- */
-class MeasureFormatCacheData : public SharedObject {
-public:
-
-    /**
-     * Redirection data from root-bundle, top-level sideways aliases.
-     * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
-     * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
-     */
-    UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT];
-
-    MeasureFormatCacheData();
-    virtual ~MeasureFormatCacheData();
-
-    void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
-        delete currencyFormats[widthIndex];
-        currencyFormats[widthIndex] = nfToAdopt;
-    }
-    const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const {
-        return currencyFormats[getRegularWidth(width)];
-    }
-    void adoptIntegerFormat(NumberFormat *nfToAdopt) {
-        delete integerFormat;
-        integerFormat = nfToAdopt;
-    }
-    const NumberFormat *getIntegerFormat() const {
-        return integerFormat;
-    }
-    void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
-        delete numericDateFormatters;
-        numericDateFormatters = formattersToAdopt;
-    }
-    const NumericDateFormatters *getNumericDateFormatters() const {
-        return numericDateFormatters;
-    }
-
-private:
-    NumberFormat* currencyFormats[WIDTH_INDEX_COUNT];
-    NumberFormat* integerFormat;
-    NumericDateFormatters* numericDateFormatters;
-
-    MeasureFormatCacheData(const MeasureFormatCacheData &other);
-    MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
-};
-
-MeasureFormatCacheData::MeasureFormatCacheData()
-        : integerFormat(nullptr), numericDateFormatters(nullptr) {
-    for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
-        widthFallback[i] = UMEASFMT_WIDTH_COUNT;
-    }
-    memset(currencyFormats, 0, sizeof(currencyFormats));
-}
-
-MeasureFormatCacheData::~MeasureFormatCacheData() {
-    for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
-        delete currencyFormats[i];
-    }
-    // Note: the contents of 'dnams' are pointers into the resource bundle
-    delete integerFormat;
-    delete numericDateFormatters;
-}
-
-static UBool isCurrency(const MeasureUnit &unit) {
-    return (uprv_strcmp(unit.getType(), "currency") == 0);
-}
-
-static UBool getString(
-        const UResourceBundle *resource,
-        UnicodeString &result,
-        UErrorCode &status) {
-    int32_t len = 0;
-    const UChar *resStr = ures_getString(resource, &len, &status);
-    if (U_FAILURE(status)) {
-        return false;
-    }
-    result.setTo(true, resStr, len);
-    return true;
-}
-
-static UnicodeString loadNumericDateFormatterPattern(
-        const UResourceBundle *resource,
-        const char *pattern,
-        UErrorCode &status) {
-    UnicodeString result;
-    if (U_FAILURE(status)) {
-        return result;
-    }
-    CharString chs;
-    chs.append("durationUnits", status)
-            .append("/", status).append(pattern, status);
-    LocalUResourceBundlePointer patternBundle(
-            ures_getByKeyWithFallback(
-                resource,
-                chs.data(),
-                NULL,
-                &status));
-    if (U_FAILURE(status)) {
-        return result;
-    }
-    getString(patternBundle.getAlias(), result, status);
-    // Replace 'h' with 'H'
-    int32_t len = result.length();
-    UChar *buffer = result.getBuffer(len);
-    for (int32_t i = 0; i < len; ++i) {
-        if (buffer[i] == 0x68) { // 'h'
-            buffer[i] = 0x48; // 'H'
-        }
-    }
-    result.releaseBuffer(len);
-    return result;
-}
-
-static NumericDateFormatters *loadNumericDateFormatters(
-        const UResourceBundle *resource,
-        UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    NumericDateFormatters *result = new NumericDateFormatters(
-        loadNumericDateFormatterPattern(resource, "hm", status),
-        loadNumericDateFormatterPattern(resource, "ms", status),
-        loadNumericDateFormatterPattern(resource, "hms", status));
-    if (U_FAILURE(status)) {
-        delete result;
-        return NULL;
-    }
-    return result;
-}
-
-template<>
-const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject(
-        const void * /*unused*/, UErrorCode &status) const {
-    const char *localeId = fLoc.getName();
-    LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status));
-    static UNumberFormatStyle currencyStyles[] = {
-            UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
-    LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status);
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    result->adoptNumericDateFormatters(loadNumericDateFormatters(
-            unitsBundle.getAlias(), status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-
-    for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
-        // NumberFormat::createInstance can erase warning codes from status, so pass it
-        // a separate status instance
-        UErrorCode localStatus = U_ZERO_ERROR;
-        result->adoptCurrencyFormat(i, NumberFormat::createInstance(
-                localeId, currencyStyles[i], localStatus));
-        if (localStatus != U_ZERO_ERROR) {
-            status = localStatus;
-        }
-        if (U_FAILURE(status)) {
-            return NULL;
-        }
-    }
-    NumberFormat *inf = NumberFormat::createInstance(
-            localeId, UNUM_DECIMAL, status);
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    inf->setMaximumFractionDigits(0);
-    DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf);
-    if (decfmt != NULL) {
-        decfmt->setRoundingMode(DecimalFormat::kRoundDown);
-    }
-    result->adoptIntegerFormat(inf);
-    result->addRef();
-    return result.orphan();
-}
-
-static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) {
-    return uprv_strcmp(mu.getType(), "duration") == 0 &&
-            uprv_strcmp(mu.getSubtype(), tu) == 0;
-}
-
-// Converts a composite measure into hours-minutes-seconds and stores at hms
-// array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
-// units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
-// contains hours-minutes, this function would return 3.
-//
-// If measures cannot be converted into hours, minutes, seconds or if amounts
-// are negative, or if hours, minutes, seconds are out of order, returns 0.
-static int32_t toHMS(
-        const Measure *measures,
-        int32_t measureCount,
-        Formattable *hms,
-        UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return 0;
-    }
-    int32_t result = 0;
-    if (U_FAILURE(status)) {
-        return 0;
-    }
-    // We use copy constructor to ensure that both sides of equality operator
-    // are instances of MeasureUnit base class and not a subclass. Otherwise,
-    // operator== will immediately return false.
-    for (int32_t i = 0; i < measureCount; ++i) {
-        if (isTimeUnit(measures[i].getUnit(), "hour")) {
-            // hour must come first
-            if (result >= 1) {
-                return 0;
-            }
-            hms[0] = measures[i].getNumber();
-            if (hms[0].getDouble() < 0.0) {
-                return 0;
-            }
-            result |= 1;
-        } else if (isTimeUnit(measures[i].getUnit(), "minute")) {
-            // minute must come after hour
-            if (result >= 2) {
-                return 0;
-            }
-            hms[1] = measures[i].getNumber();
-            if (hms[1].getDouble() < 0.0) {
-                return 0;
-            }
-            result |= 2;
-        } else if (isTimeUnit(measures[i].getUnit(), "second")) {
-            // second must come after hour and minute
-            if (result >= 4) {
-                return 0;
-            }
-            hms[2] = measures[i].getNumber();
-            if (hms[2].getDouble() < 0.0) {
-                return 0;
-            }
-            result |= 4;
-        } else {
-            return 0;
-        }
-    }
-    return result;
-}
-
-
-MeasureFormat::MeasureFormat(
-        const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
-        : cache(NULL),
-          numberFormat(NULL),
-          pluralRules(NULL),
-          fWidth(w),
-          listFormatter(NULL) {
-    initMeasureFormat(locale, w, NULL, status);
-}
-
-MeasureFormat::MeasureFormat(
-        const Locale &locale,
-        UMeasureFormatWidth w,
-        NumberFormat *nfToAdopt,
-        UErrorCode &status)
-        : cache(NULL),
-          numberFormat(NULL),
-          pluralRules(NULL),
-          fWidth(w),
-          listFormatter(NULL) {
-    initMeasureFormat(locale, w, nfToAdopt, status);
-}
-
-MeasureFormat::MeasureFormat(const MeasureFormat &other) :
-        Format(other),
-        cache(other.cache),
-        numberFormat(other.numberFormat),
-        pluralRules(other.pluralRules),
-        fWidth(other.fWidth),
-        listFormatter(NULL) {
-    cache->addRef();
-    numberFormat->addRef();
-    pluralRules->addRef();
-    if (other.listFormatter != NULL) {
-        listFormatter = new ListFormatter(*other.listFormatter);
-    }
-}
-
-MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
-    if (this == &other) {
-        return *this;
-    }
-    Format::operator=(other);
-    SharedObject::copyPtr(other.cache, cache);
-    SharedObject::copyPtr(other.numberFormat, numberFormat);
-    SharedObject::copyPtr(other.pluralRules, pluralRules);
-    fWidth = other.fWidth;
-    delete listFormatter;
-    if (other.listFormatter != NULL) {
-        listFormatter = new ListFormatter(*other.listFormatter);
-    } else {
-        listFormatter = NULL;
-    }
-    return *this;
-}
-
-MeasureFormat::MeasureFormat() :
-        cache(NULL),
-        numberFormat(NULL),
-        pluralRules(NULL),
-        fWidth(UMEASFMT_WIDTH_SHORT),
-        listFormatter(NULL) {
-}
-
-MeasureFormat::~MeasureFormat() {
-    if (cache != NULL) {
-        cache->removeRef();
-    }
-    if (numberFormat != NULL) {
-        numberFormat->removeRef();
-    }
-    if (pluralRules != NULL) {
-        pluralRules->removeRef();
-    }
-    delete listFormatter;
-}
-
-bool MeasureFormat::operator==(const Format &other) const {
-    if (this == &other) { // Same object, equal
-        return true;
-    }
-    if (!Format::operator==(other)) {
-        return false;
-    }
-    const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other);
-
-    // Note: Since the ListFormatter depends only on Locale and width, we
-    // don't have to check it here.
-
-    // differing widths aren't equivalent
-    if (fWidth != rhs.fWidth) {
-        return false;
-    }
-    // Width the same check locales.
-    // We don't need to check locales if both objects have same cache.
-    if (cache != rhs.cache) {
-        UErrorCode status = U_ZERO_ERROR;
-        const char *localeId = getLocaleID(status);
-        const char *rhsLocaleId = rhs.getLocaleID(status);
-        if (U_FAILURE(status)) {
-            // On failure, assume not equal
-            return false;
-        }
-        if (uprv_strcmp(localeId, rhsLocaleId) != 0) {
-            return false;
-        }
-    }
-    // Locales same, check NumberFormat if shared data differs.
-    return (
-            numberFormat == rhs.numberFormat ||
-            **numberFormat == **rhs.numberFormat);
-}
-
-MeasureFormat *MeasureFormat::clone() const {
-    return new MeasureFormat(*this);
-}
-
-UnicodeString &MeasureFormat::format(
-        const Formattable &obj,
-        UnicodeString &appendTo,
-        FieldPosition &pos,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) return appendTo;
-    if (obj.getType() == Formattable::kObject) {
-        const UObject* formatObj = obj.getObject();
-        const Measure* amount = dynamic_cast<const Measure*>(formatObj);
-        if (amount != NULL) {
-            return formatMeasure(
-                    *amount, **numberFormat, appendTo, pos, status);
-        }
-    }
-    status = U_ILLEGAL_ARGUMENT_ERROR;
-    return appendTo;
-}
-
-void MeasureFormat::parseObject(
-        const UnicodeString & /*source*/,
-        Formattable & /*result*/,
-        ParsePosition& /*pos*/) const {
-    return;
-}
-
-UnicodeString &MeasureFormat::formatMeasurePerUnit(
-        const Measure &measure,
-        const MeasureUnit &perUnit,
-        UnicodeString &appendTo,
-        FieldPosition &pos,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    auto* df = dynamic_cast<const DecimalFormat*>(&getNumberFormatInternal());
-    if (df == nullptr) {
-        // Don't know how to handle other types of NumberFormat
-        status = U_UNSUPPORTED_ERROR;
-        return appendTo;
-    }
-    UFormattedNumberData result;
-    if (auto* lnf = df->toNumberFormatter(status)) {
-        result.quantity.setToDouble(measure.getNumber().getDouble(status));
-        lnf->unit(measure.getUnit())
-            .perUnit(perUnit)
-            .unitWidth(getUnitWidth(fWidth))
-            .formatImpl(&result, status);
-    }
-    DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status);
-    appendTo.append(result.toTempString(status));
-    return appendTo;
-}
-
-UnicodeString &MeasureFormat::formatMeasures(
-        const Measure *measures,
-        int32_t measureCount,
-        UnicodeString &appendTo,
-        FieldPosition &pos,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    if (measureCount == 0) {
-        return appendTo;
-    }
-    if (measureCount == 1) {
-        return formatMeasure(measures[0], **numberFormat, appendTo, pos, status);
-    }
-    if (fWidth == UMEASFMT_WIDTH_NUMERIC) {
-        Formattable hms[3];
-        int32_t bitMap = toHMS(measures, measureCount, hms, status);
-        if (bitMap > 0) {
-            return formatNumeric(hms, bitMap, appendTo, status);
-        }
-    }
-    if (pos.getField() != FieldPosition::DONT_CARE) {
-        return formatMeasuresSlowTrack(
-                measures, measureCount, appendTo, pos, status);
-    }
-    UnicodeString *results = new UnicodeString[measureCount];
-    if (results == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return appendTo;
-    }
-    for (int32_t i = 0; i < measureCount; ++i) {
-        const NumberFormat *nf = cache->getIntegerFormat();
-        if (i == measureCount - 1) {
-            nf = numberFormat->get();
-        }
-        formatMeasure(
-                measures[i],
-                *nf,
-                results[i],
-                pos,
-                status);
-    }
-    listFormatter->format(results, measureCount, appendTo, status);
-    delete [] results;
-    return appendTo;
-}
-
-UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& status) const {
-    return number::impl::LongNameHandler::getUnitDisplayName(
-        getLocale(status),
-        unit,
-        getUnitWidth(fWidth),
-        status);
-}
-
-void MeasureFormat::initMeasureFormat(
-        const Locale &locale,
-        UMeasureFormatWidth w,
-        NumberFormat *nfToAdopt,
-        UErrorCode &status) {
-    static const UListFormatterWidth listWidths[] = {
-        ULISTFMT_WIDTH_WIDE,
-        ULISTFMT_WIDTH_SHORT,
-        ULISTFMT_WIDTH_NARROW};
-    LocalPointer<NumberFormat> nf(nfToAdopt);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    const char *name = locale.getName();
-    setLocaleIDs(name, name);
-
-    UnifiedCache::getByLocale(locale, cache, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    const SharedPluralRules *pr = PluralRules::createSharedInstance(
-            locale, UPLURAL_TYPE_CARDINAL, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    SharedObject::copyPtr(pr, pluralRules);
-    pr->removeRef();
-    if (nf.isNull()) {
-        // TODO: Clean this up
-        const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
-                locale, UNUM_DECIMAL, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-        SharedObject::copyPtr(shared, numberFormat);
-        shared->removeRef();
-    } else {
-        adoptNumberFormat(nf.orphan(), status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    }
-    fWidth = w;
-    delete listFormatter;
-    listFormatter = ListFormatter::createInstance(
-            locale,
-            ULISTFMT_TYPE_UNITS,
-            listWidths[getRegularWidth(fWidth)],
-            status);
-}
-
-void MeasureFormat::adoptNumberFormat(
-        NumberFormat *nfToAdopt, UErrorCode &status) {
-    LocalPointer<NumberFormat> nf(nfToAdopt);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
-    if (shared == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    nf.orphan();
-    SharedObject::copyPtr(shared, numberFormat);
-}
-
-UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
-    if (U_FAILURE(status) || locale == getLocale(status)) {
-        return false;
-    }
-    initMeasureFormat(locale, fWidth, NULL, status);
-    return U_SUCCESS(status);
-}
-
-const NumberFormat &MeasureFormat::getNumberFormatInternal() const {
-    return **numberFormat;
-}
-
-const NumberFormat &MeasureFormat::getCurrencyFormatInternal() const {
-    return *cache->getCurrencyFormat(UMEASFMT_WIDTH_NARROW);
-}
-
-const PluralRules &MeasureFormat::getPluralRules() const {
-    return **pluralRules;
-}
-
-Locale MeasureFormat::getLocale(UErrorCode &status) const {
-    return Format::getLocale(ULOC_VALID_LOCALE, status);
-}
-
-const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
-    return Format::getLocaleID(ULOC_VALID_LOCALE, status);
-}
-
-UnicodeString &MeasureFormat::formatMeasure(
-        const Measure &measure,
-        const NumberFormat &nf,
-        UnicodeString &appendTo,
-        FieldPosition &pos,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    const Formattable& amtNumber = measure.getNumber();
-    const MeasureUnit& amtUnit = measure.getUnit();
-    if (isCurrency(amtUnit)) {
-        UChar isoCode[4];
-        u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
-        return cache->getCurrencyFormat(fWidth)->format(
-                new CurrencyAmount(amtNumber, isoCode, status),
-                appendTo,
-                pos,
-                status);
-    }
-    auto* df = dynamic_cast<const DecimalFormat*>(&nf);
-    if (df == nullptr) {
-        // Handle other types of NumberFormat using the ICU 63 code, modified to
-        // get the unitPattern from LongNameHandler and handle fallback to OTHER.
-        UnicodeString formattedNumber;
-        StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
-                amtNumber, nf, **pluralRules, formattedNumber, pos, status);
-        UnicodeString pattern = number::impl::LongNameHandler::getUnitPattern(getLocale(status),
-                amtUnit, getUnitWidth(fWidth), pluralForm, status);
-        // The above  handles fallback from other widths to short, and from other plural forms to OTHER
-        if (U_FAILURE(status)) {
-            return appendTo;
-        }
-        SimpleFormatter formatter(pattern, 0, 1, status);
-        return QuantityFormatter::format(formatter, formattedNumber, appendTo, pos, status);
-    }
-    UFormattedNumberData result;
-    if (auto* lnf = df->toNumberFormatter(status)) {
-        result.quantity.setToDouble(amtNumber.getDouble(status));
-        lnf->unit(amtUnit)
-            .unitWidth(getUnitWidth(fWidth))
-            .formatImpl(&result, status);
-    }
-    DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status);
-    appendTo.append(result.toTempString(status));
-    return appendTo;
-}
-
-
-// Formats numeric time duration as 5:00:47 or 3:54.
-UnicodeString &MeasureFormat::formatNumeric(
-        const Formattable *hms,  // always length 3
-        int32_t bitMap,   // 1=hour set, 2=minute set, 4=second set
-        UnicodeString &appendTo,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-
-    UnicodeString pattern;
-
-    double hours = hms[0].getDouble(status);
-    double minutes = hms[1].getDouble(status);
-    double seconds = hms[2].getDouble(status);
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-
-    // All possible combinations: "h", "m", "s", "hm", "hs", "ms", "hms"
-    if (bitMap == 5 || bitMap == 7) { // "hms" & "hs" (we add minutes if "hs")
-        pattern = cache->getNumericDateFormatters()->hourMinuteSecond;
-        hours = uprv_trunc(hours);
-        minutes = uprv_trunc(minutes);
-    } else if (bitMap == 3) { // "hm"
-        pattern = cache->getNumericDateFormatters()->hourMinute;
-        hours = uprv_trunc(hours);
-    } else if (bitMap == 6) { // "ms"
-        pattern = cache->getNumericDateFormatters()->minuteSecond;
-        minutes = uprv_trunc(minutes);
-    } else { // h m s, handled outside formatNumeric. No value is also an error.
-        status = U_INTERNAL_PROGRAM_ERROR;
-        return appendTo;
-    }
-
-    const DecimalFormat *numberFormatter = dynamic_cast<const DecimalFormat*>(numberFormat->get());
-    if (!numberFormatter) {
-        status = U_INTERNAL_PROGRAM_ERROR;
-        return appendTo;
-    }
-    number::LocalizedNumberFormatter numberFormatter2;
-    if (auto* lnf = numberFormatter->toNumberFormatter(status)) {
-        numberFormatter2 = lnf->integerWidth(number::IntegerWidth::zeroFillTo(2));
-    } else {
-        return appendTo;
-    }
-
-    FormattedStringBuilder fsb;
-
-    UBool protect = false;
-    const int32_t patternLength = pattern.length();
-    for (int32_t i = 0; i < patternLength; i++) {
-        char16_t c = pattern[i];
-
-        // Also set the proper field in this switch
-        // We don't use DateFormat.Field because this is not a date / time, is a duration.
-        double value = 0;
-        switch (c) {
-            case u'H': value = hours; break;
-            case u'm': value = minutes; break;
-            case u's': value = seconds; break;
-        }
-
-        // There is not enough info to add Field(s) for the unit because all we have are plain
-        // text patterns. For example in "21:51" there is no text for something like "hour",
-        // while in something like "21h51" there is ("h"). But we can't really tell...
-        switch (c) {
-            case u'H':
-            case u'm':
-            case u's':
-                if (protect) {
-                    fsb.appendChar16(c, kUndefinedField, status);
-                } else {
-                    UnicodeString tmp;
-                    if ((i + 1 < patternLength) && pattern[i + 1] == c) { // doubled
-                        tmp = numberFormatter2.formatDouble(value, status).toString(status);
-                        i++;
-                    } else {
-                        numberFormatter->format(value, tmp, status);
-                    }
-                    // TODO: Use proper Field
-                    fsb.append(tmp, kUndefinedField, status);
-                }
-                break;
-            case u'\'':
-                // '' is escaped apostrophe
-                if ((i + 1 < patternLength) && pattern[i + 1] == c) {
-                    fsb.appendChar16(c, kUndefinedField, status);
-                    i++;
-                } else {
-                    protect = !protect;
-                }
-                break;
-            default:
-                fsb.appendChar16(c, kUndefinedField, status);
-        }
-    }
-
-    appendTo.append(fsb.toTempUnicodeString());
-
-    return appendTo;
-}
-
-UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
-        const Measure *measures,
-        int32_t measureCount,
-        UnicodeString& appendTo,
-        FieldPosition& pos,
-        UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    FieldPosition dontCare(FieldPosition::DONT_CARE);
-    FieldPosition fpos(pos.getField());
-    LocalArray<UnicodeString> results(new UnicodeString[measureCount], status);
-    int32_t fieldPositionFoundIndex = -1;
-    for (int32_t i = 0; i < measureCount; ++i) {
-        const NumberFormat *nf = cache->getIntegerFormat();
-        if (i == measureCount - 1) {
-            nf = numberFormat->get();
-        }
-        if (fieldPositionFoundIndex == -1) {
-            formatMeasure(measures[i], *nf, results[i], fpos, status);
-            if (U_FAILURE(status)) {
-                return appendTo;
-            }
-            if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
-                fieldPositionFoundIndex = i;
-            }
-        } else {
-            formatMeasure(measures[i], *nf, results[i], dontCare, status);
-        }
-    }
-    int32_t offset;
-    listFormatter->format(
-            results.getAlias(),
-            measureCount,
-            appendTo,
-            fieldPositionFoundIndex,
-            offset,
-            status);
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    // Fix up FieldPosition indexes if our field is found.
-    if (fieldPositionFoundIndex != -1 && offset != -1) {
-        pos.setBeginIndex(fpos.getBeginIndex() + offset);
-        pos.setEndIndex(fpos.getEndIndex() + offset);
-    }
-    return appendTo;
-}
-
-MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
-                                                   UErrorCode& ec) {
-    if (U_FAILURE(ec)) {
-        return nullptr;
-    }
-    LocalPointer<CurrencyFormat> fmt(new CurrencyFormat(locale, ec), ec);
-    return fmt.orphan();
-}
-
-MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
-    if (U_FAILURE(ec)) {
-        return nullptr;
-    }
-    return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
-}
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/libs/icui18n/scientificnumberformatter.cpp b/libs/icui18n/scientificnumberformatter.cpp
deleted file mode 100644
index 99b990708ab6..000000000000
--- a/libs/icui18n/scientificnumberformatter.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-* Copyright (c) 2014, International Business Machines
-* Corporation and others.  All Rights Reserved.
-**********************************************************************
-*/
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/scientificnumberformatter.h"
-#include "unicode/dcfmtsym.h"
-#include "unicode/fpositer.h"
-#include "unicode/utf16.h"
-#include "unicode/uniset.h"
-#include "unicode/decimfmt.h"
-#include "static_unicode_sets.h"
-
-U_NAMESPACE_BEGIN
-
-static const UChar kSuperscriptDigits[] = {
-        0x2070,
-        0xB9,
-        0xB2,
-        0xB3,
-        0x2074,
-        0x2075,
-        0x2076,
-        0x2077,
-        0x2078,
-        0x2079};
-
-static const UChar kSuperscriptPlusSign = 0x207A;
-static const UChar kSuperscriptMinusSign = 0x207B;
-
-static UBool copyAsSuperscript(
-        const UnicodeString &s,
-        int32_t beginIndex,
-        int32_t endIndex,
-        UnicodeString &result,
-        UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return false;
-    }
-    for (int32_t i = beginIndex; i < endIndex;) {
-        UChar32 c = s.char32At(i);
-        int32_t digit = u_charDigitValue(c);
-        if (digit < 0) {
-            status = U_INVALID_CHAR_FOUND;
-            return false;
-        }
-        result.append(kSuperscriptDigits[digit]);
-        i += U16_LENGTH(c);
-    }
-    return true;
-}
-
-ScientificNumberFormatter *ScientificNumberFormatter::createSuperscriptInstance(
-            DecimalFormat *fmtToAdopt, UErrorCode &status) {
-    return createInstance(fmtToAdopt, new SuperscriptStyle(), status);
-}
-
-ScientificNumberFormatter *ScientificNumberFormatter::createSuperscriptInstance(
-            const Locale &locale, UErrorCode &status) {
-    return createInstance(
-            static_cast<DecimalFormat *>(
-                    DecimalFormat::createScientificInstance(locale, status)),
-            new SuperscriptStyle(),
-            status);
-}
-
-ScientificNumberFormatter *ScientificNumberFormatter::createMarkupInstance(
-        DecimalFormat *fmtToAdopt,
-        const UnicodeString &beginMarkup,
-        const UnicodeString &endMarkup,
-        UErrorCode &status) {
-    return createInstance(
-            fmtToAdopt,
-            new MarkupStyle(beginMarkup, endMarkup),
-            status);
-}
-
-ScientificNumberFormatter *ScientificNumberFormatter::createMarkupInstance(
-        const Locale &locale,
-        const UnicodeString &beginMarkup,
-        const UnicodeString &endMarkup,
-        UErrorCode &status) {
-    return createInstance(
-            static_cast<DecimalFormat *>(
-                    DecimalFormat::createScientificInstance(locale, status)),
-            new MarkupStyle(beginMarkup, endMarkup),
-            status);
-}
-
-ScientificNumberFormatter *ScientificNumberFormatter::createInstance(
-            DecimalFormat *fmtToAdopt,
-            Style *styleToAdopt,
-            UErrorCode &status) {
-    LocalPointer<DecimalFormat> fmt(fmtToAdopt);
-    LocalPointer<Style> style(styleToAdopt);
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    ScientificNumberFormatter *result =
-            new ScientificNumberFormatter(
-                    fmt.getAlias(),
-                    style.getAlias(),
-                    status);
-    if (result == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-    fmt.orphan();
-    style.orphan();
-    if (U_FAILURE(status)) {
-        delete result;
-        return NULL;
-    }
-    return result;
-}
-
-ScientificNumberFormatter::SuperscriptStyle *ScientificNumberFormatter::SuperscriptStyle::clone() const {
-    return new ScientificNumberFormatter::SuperscriptStyle(*this);
-}
-
-UnicodeString &ScientificNumberFormatter::SuperscriptStyle::format(
-        const UnicodeString &original,
-        FieldPositionIterator &fpi,
-        const UnicodeString &preExponent,
-        UnicodeString &appendTo,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    FieldPosition fp;
-    int32_t copyFromOffset = 0;
-    while (fpi.next(fp)) {
-        switch (fp.getField()) {
-        case UNUM_EXPONENT_SYMBOL_FIELD:
-            appendTo.append(
-                    original,
-                    copyFromOffset,
-                    fp.getBeginIndex() - copyFromOffset);
-            copyFromOffset = fp.getEndIndex();
-            appendTo.append(preExponent);
-            break;
-        case UNUM_EXPONENT_SIGN_FIELD:
-            {
-                using namespace icu::numparse::impl;
-                int32_t beginIndex = fp.getBeginIndex();
-                int32_t endIndex = fp.getEndIndex();
-                UChar32 aChar = original.char32At(beginIndex);
-                if (unisets::get(unisets::MINUS_SIGN)->contains(aChar)) {
-                    appendTo.append(
-                            original,
-                            copyFromOffset,
-                            beginIndex - copyFromOffset);
-                    appendTo.append(kSuperscriptMinusSign);
-                } else if (unisets::get(unisets::PLUS_SIGN)->contains(aChar)) {
-                    appendTo.append(
-                           original,
-                           copyFromOffset,
-                           beginIndex - copyFromOffset);
-                    appendTo.append(kSuperscriptPlusSign);
-                } else {
-                    status = U_INVALID_CHAR_FOUND;
-                    return appendTo;
-                }
-                copyFromOffset = endIndex;
-            }
-            break;
-        case UNUM_EXPONENT_FIELD:
-            appendTo.append(
-                    original,
-                    copyFromOffset,
-                    fp.getBeginIndex() - copyFromOffset);
-            if (!copyAsSuperscript(
-                    original,
-                    fp.getBeginIndex(),
-                    fp.getEndIndex(),
-                    appendTo,
-                    status)) {
-              return appendTo;
-            }
-            copyFromOffset = fp.getEndIndex();
-            break;
-        default:
-            break;
-        }
-    }
-    appendTo.append(
-            original, copyFromOffset, original.length() - copyFromOffset);
-    return appendTo;
-}
-
-ScientificNumberFormatter::MarkupStyle *ScientificNumberFormatter::MarkupStyle::clone() const {
-    return new ScientificNumberFormatter::MarkupStyle(*this);
-}
-
-UnicodeString &ScientificNumberFormatter::MarkupStyle::format(
-        const UnicodeString &original,
-        FieldPositionIterator &fpi,
-        const UnicodeString &preExponent,
-        UnicodeString &appendTo,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    FieldPosition fp;
-    int32_t copyFromOffset = 0;
-    while (fpi.next(fp)) {
-        switch (fp.getField()) {
-        case UNUM_EXPONENT_SYMBOL_FIELD:
-            appendTo.append(
-                    original,
-                    copyFromOffset,
-                    fp.getBeginIndex() - copyFromOffset);
-            copyFromOffset = fp.getEndIndex();
-            appendTo.append(preExponent);
-            appendTo.append(fBeginMarkup);
-            break;
-        case UNUM_EXPONENT_FIELD:
-            appendTo.append(
-                    original,
-                    copyFromOffset,
-                    fp.getEndIndex() - copyFromOffset);
-            copyFromOffset = fp.getEndIndex();
-            appendTo.append(fEndMarkup);
-            break;
-        default:
-            break;
-        }
-    }
-    appendTo.append(
-            original, copyFromOffset, original.length() - copyFromOffset);
-    return appendTo;
-}
-
-ScientificNumberFormatter::ScientificNumberFormatter(
-        DecimalFormat *fmtToAdopt, Style *styleToAdopt, UErrorCode &status)
-        : fPreExponent(),
-          fDecimalFormat(fmtToAdopt),
-          fStyle(styleToAdopt) {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (fDecimalFormat == NULL || fStyle == NULL) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-    const DecimalFormatSymbols *sym = fDecimalFormat->getDecimalFormatSymbols();
-    if (sym == NULL) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-    getPreExponent(*sym, fPreExponent);
-}
-
-ScientificNumberFormatter::ScientificNumberFormatter(
-        const ScientificNumberFormatter &other)
-        : UObject(other),
-          fPreExponent(other.fPreExponent),
-          fDecimalFormat(NULL),
-          fStyle(NULL) {
-    fDecimalFormat = static_cast<DecimalFormat *>(
-            other.fDecimalFormat->clone());
-    fStyle = other.fStyle->clone();
-}
-
-ScientificNumberFormatter::~ScientificNumberFormatter() {
-    delete fDecimalFormat;
-    delete fStyle;
-}
-
-UnicodeString &ScientificNumberFormatter::format(
-        const Formattable &number,
-        UnicodeString &appendTo,
-        UErrorCode &status) const {
-    if (U_FAILURE(status)) {
-        return appendTo;
-    }
-    UnicodeString original;
-    FieldPositionIterator fpi;
-    fDecimalFormat->format(number, original, &fpi, status);
-    return fStyle->format(
-            original,
-            fpi,
-            fPreExponent,
-            appendTo,
-            status);
-}
-
-void ScientificNumberFormatter::getPreExponent(
-        const DecimalFormatSymbols &dfs, UnicodeString &preExponent) {
-    preExponent.append(dfs.getConstSymbol(
-            DecimalFormatSymbols::kExponentMultiplicationSymbol));
-    preExponent.append(dfs.getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol));
-    preExponent.append(dfs.getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
-}
-
-U_NAMESPACE_END
-
-#endif /* !UCONFIG_NO_FORMATTING */
diff --git a/libs/icui18n/search.cpp b/libs/icui18n/search.cpp
deleted file mode 100644
index 4a30e5739845..000000000000
--- a/libs/icui18n/search.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-*   Copyright (C) 2001-2008,2010 IBM and others. All rights reserved.
-**********************************************************************
-*   Date        Name        Description
-*  03/22/2000   helena      Creation.
-**********************************************************************
-*/
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_COLLATION && !UCONFIG_NO_BREAK_ITERATION
-
-#include "unicode/brkiter.h"
-#include "unicode/schriter.h"
-#include "unicode/search.h"
-#include "usrchimp.h"
-#include "cmemory.h"
-
-// public constructors and destructors -----------------------------------
-U_NAMESPACE_BEGIN
-
-SearchIterator::SearchIterator(const SearchIterator &other)
-    : UObject(other)
-{
-    m_breakiterator_            = other.m_breakiterator_;
-    m_text_                     = other.m_text_;
-    m_search_                   = (USearch *)uprv_malloc(sizeof(USearch));
-    m_search_->breakIter        = other.m_search_->breakIter;
-    m_search_->isCanonicalMatch = other.m_search_->isCanonicalMatch;
-    m_search_->isOverlap        = other.m_search_->isOverlap;
-    m_search_->elementComparisonType = other.m_search_->elementComparisonType;
-    m_search_->matchedIndex     = other.m_search_->matchedIndex;
-    m_search_->matchedLength    = other.m_search_->matchedLength;
-    m_search_->text             = other.m_search_->text;
-    m_search_->textLength       = other.m_search_->textLength;
-}
-
-SearchIterator::~SearchIterator()
-{
-    if (m_search_ != NULL) {
-        uprv_free(m_search_);
-    }
-}
-
-// public get and set methods ----------------------------------------
-
-void SearchIterator::setAttribute(USearchAttribute       attribute,
-                                  USearchAttributeValue  value,
-                                  UErrorCode            &status)
-{
-    if (U_SUCCESS(status)) {
-        switch (attribute)
-        {
-        case USEARCH_OVERLAP :
-            m_search_->isOverlap = (value == USEARCH_ON ? true : false);
-            break;
-        case USEARCH_CANONICAL_MATCH :
-            m_search_->isCanonicalMatch = (value == USEARCH_ON ? true : false);
-            break;
-        case USEARCH_ELEMENT_COMPARISON :
-            if (value == USEARCH_PATTERN_BASE_WEIGHT_IS_WILDCARD || value == USEARCH_ANY_BASE_WEIGHT_IS_WILDCARD) {
-                m_search_->elementComparisonType = (int16_t)value;
-            } else {
-                m_search_->elementComparisonType = 0;
-            }
-            break;
-        default:
-            status = U_ILLEGAL_ARGUMENT_ERROR;
-        }
-    }
-    if (value == USEARCH_ATTRIBUTE_VALUE_COUNT) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-    }
-}
-
-USearchAttributeValue SearchIterator::getAttribute(
-                                          USearchAttribute  attribute) const
-{
-    switch (attribute) {
-    case USEARCH_OVERLAP :
-        return (m_search_->isOverlap == true ? USEARCH_ON : USEARCH_OFF);
-    case USEARCH_CANONICAL_MATCH :
-        return (m_search_->isCanonicalMatch == true ? USEARCH_ON :
-                                                                USEARCH_OFF);
-    case USEARCH_ELEMENT_COMPARISON :
-        {
-            int16_t value = m_search_->elementComparisonType;
-            if (value == USEARCH_PATTERN_BASE_WEIGHT_IS_WILDCARD || value == USEARCH_ANY_BASE_WEIGHT_IS_WILDCARD) {
-                return (USearchAttributeValue)value;
-            } else {
-                return USEARCH_STANDARD_ELEMENT_COMPARISON;
-            }
-        }
-    default :
-        return USEARCH_DEFAULT;
-    }
-}
-
-int32_t SearchIterator::getMatchedStart() const
-{
-    return m_search_->matchedIndex;
-}
-
-int32_t SearchIterator::getMatchedLength() const
-{
-    return m_search_->matchedLength;
-}
-
-void SearchIterator::getMatchedText(UnicodeString &result) const
-{
-    int32_t matchedindex  = m_search_->matchedIndex;
-    int32_t     matchedlength = m_search_->matchedLength;
-    if (matchedindex != USEARCH_DONE && matchedlength != 0) {
-        result.setTo(m_search_->text + matchedindex, matchedlength);
-    }
-    else {
-        result.remove();
-    }
-}
-
-void SearchIterator::setBreakIterator(BreakIterator *breakiter,
-                                      UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-#if 0
-        m_search_->breakIter = NULL;
-        // the c++ breakiterator may not make use of ubreakiterator.
-        // so we'll have to keep track of it ourselves.
-#else
-        // Well, gee... the Constructors that take a BreakIterator
-        // all cast the BreakIterator to a UBreakIterator and
-        // pass it to the corresponding usearch_openFromXXX
-        // routine, so there's no reason not to do this.
-        //
-        // Besides, a UBreakIterator is a BreakIterator, so
-        // any subclass of BreakIterator should work fine here...
-        m_search_->breakIter = (UBreakIterator *) breakiter;
-#endif
-
-        m_breakiterator_ = breakiter;
-    }
-}
-
-const BreakIterator * SearchIterator::getBreakIterator(void) const
-{
-    return m_breakiterator_;
-}
-
-void SearchIterator::setText(const UnicodeString &text, UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        if (text.length() == 0) {
-            status = U_ILLEGAL_ARGUMENT_ERROR;
-        }
-        else {
-            m_text_        = text;
-            m_search_->text = m_text_.getBuffer();
-            m_search_->textLength = m_text_.length();
-        }
-    }
-}
-
-void SearchIterator::setText(CharacterIterator &text, UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        text.getText(m_text_);
-        setText(m_text_, status);
-    }
-}
-
-const UnicodeString & SearchIterator::getText(void) const
-{
-    return m_text_;
-}
-
-// operator overloading ----------------------------------------------
-
-bool SearchIterator::operator==(const SearchIterator &that) const
-{
-    if (this == &that) {
-        return true;
-    }
-    return (m_breakiterator_            == that.m_breakiterator_ &&
-            m_search_->isCanonicalMatch == that.m_search_->isCanonicalMatch &&
-            m_search_->isOverlap        == that.m_search_->isOverlap &&
-            m_search_->elementComparisonType == that.m_search_->elementComparisonType &&
-            m_search_->matchedIndex     == that.m_search_->matchedIndex &&
-            m_search_->matchedLength    == that.m_search_->matchedLength &&
-            m_search_->textLength       == that.m_search_->textLength &&
-            getOffset() == that.getOffset() &&
-            (uprv_memcmp(m_search_->text, that.m_search_->text,
-                              m_search_->textLength * sizeof(UChar)) == 0));
-}
-
-// public methods ----------------------------------------------------
-
-int32_t SearchIterator::first(UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return USEARCH_DONE;
-    }
-    setOffset(0, status);
-    return handleNext(0, status);
-}
-
-int32_t SearchIterator::following(int32_t position,
-                                      UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return USEARCH_DONE;
-    }
-    setOffset(position, status);
-    return handleNext(position, status);
-}
-
-int32_t SearchIterator::last(UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return USEARCH_DONE;
-    }
-    setOffset(m_search_->textLength, status);
-    return handlePrev(m_search_->textLength, status);
-}
-
-int32_t SearchIterator::preceding(int32_t position,
-                                      UErrorCode &status)
-{
-    if (U_FAILURE(status)) {
-        return USEARCH_DONE;
-    }
-    setOffset(position, status);
-    return handlePrev(position, status);
-}
-
-int32_t SearchIterator::next(UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        int32_t offset = getOffset();
-        int32_t matchindex  = m_search_->matchedIndex;
-        int32_t     matchlength = m_search_->matchedLength;
-        m_search_->reset = false;
-        if (m_search_->isForwardSearching == true) {
-            int32_t textlength = m_search_->textLength;
-            if (offset == textlength || matchindex == textlength ||
-                (matchindex != USEARCH_DONE &&
-                matchindex + matchlength >= textlength)) {
-                // not enough characters to match
-                setMatchNotFound();
-                return USEARCH_DONE;
-            }
-        }
-        else {
-            // switching direction.
-            // if matchedIndex == USEARCH_DONE, it means that either a
-            // setOffset has been called or that previous ran off the text
-            // string. the iterator would have been set to offset 0 if a
-            // match is not found.
-            m_search_->isForwardSearching = true;
-            if (m_search_->matchedIndex != USEARCH_DONE) {
-                // there's no need to set the collation element iterator
-                // the next call to next will set the offset.
-                return matchindex;
-            }
-        }
-
-        if (matchlength > 0) {
-            // if matchlength is 0 we are at the start of the iteration
-            if (m_search_->isOverlap) {
-                offset ++;
-            }
-            else {
-                offset += matchlength;
-            }
-        }
-        return handleNext(offset, status);
-    }
-    return USEARCH_DONE;
-}
-
-int32_t SearchIterator::previous(UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        int32_t offset;
-        if (m_search_->reset) {
-            offset                       = m_search_->textLength;
-            m_search_->isForwardSearching = false;
-            m_search_->reset              = false;
-            setOffset(offset, status);
-        }
-        else {
-            offset = getOffset();
-        }
-
-        int32_t matchindex = m_search_->matchedIndex;
-        if (m_search_->isForwardSearching == true) {
-            // switching direction.
-            // if matchedIndex == USEARCH_DONE, it means that either a
-            // setOffset has been called or that next ran off the text
-            // string. the iterator would have been set to offset textLength if
-            // a match is not found.
-            m_search_->isForwardSearching = false;
-            if (matchindex != USEARCH_DONE) {
-                return matchindex;
-            }
-        }
-        else {
-            if (offset == 0 || matchindex == 0) {
-                // not enough characters to match
-                setMatchNotFound();
-                return USEARCH_DONE;
-            }
-        }
-
-        if (matchindex != USEARCH_DONE) {
-            if (m_search_->isOverlap) {
-                matchindex += m_search_->matchedLength - 2;
-            }
-
-            return handlePrev(matchindex, status);
-        }
-
-        return handlePrev(offset, status);
-    }
-
-    return USEARCH_DONE;
-}
-
-void SearchIterator::reset()
-{
-    UErrorCode status = U_ZERO_ERROR;
-    setMatchNotFound();
-    setOffset(0, status);
-    m_search_->isOverlap          = false;
-    m_search_->isCanonicalMatch   = false;
-    m_search_->elementComparisonType = 0;
-    m_search_->isForwardSearching = true;
-    m_search_->reset              = true;
-}
-
-// protected constructors and destructors -----------------------------
-
-SearchIterator::SearchIterator()
-{
-    m_search_                     = (USearch *)uprv_malloc(sizeof(USearch));
-    m_search_->breakIter          = NULL;
-    m_search_->isOverlap          = false;
-    m_search_->isCanonicalMatch   = false;
-    m_search_->elementComparisonType = 0;
-    m_search_->isForwardSearching = true;
-    m_search_->reset              = true;
-    m_search_->matchedIndex       = USEARCH_DONE;
-    m_search_->matchedLength      = 0;
-    m_search_->text               = NULL;
-    m_search_->textLength         = 0;
-    m_breakiterator_              = NULL;
-}
-
-SearchIterator::SearchIterator(const UnicodeString &text,
-                                     BreakIterator *breakiter) :
-                                     m_breakiterator_(breakiter),
-                                     m_text_(text)
-{
-    m_search_                     = (USearch *)uprv_malloc(sizeof(USearch));
-    m_search_->breakIter          = NULL;
-    m_search_->isOverlap          = false;
-    m_search_->isCanonicalMatch   = false;
-    m_search_->elementComparisonType = 0;
-    m_search_->isForwardSearching = true;
-    m_search_->reset              = true;
-    m_search_->matchedIndex       = USEARCH_DONE;
-    m_search_->matchedLength      = 0;
-    m_search_->text               = m_text_.getBuffer();
-    m_search_->textLength         = text.length();
-}
-
-SearchIterator::SearchIterator(CharacterIterator &text,
-                               BreakIterator     *breakiter) :
-                               m_breakiterator_(breakiter)
-{
-    m_search_                     = (USearch *)uprv_malloc(sizeof(USearch));
-    m_search_->breakIter          = NULL;
-    m_search_->isOverlap          = false;
-    m_search_->isCanonicalMatch   = false;
-    m_search_->elementComparisonType = 0;
-    m_search_->isForwardSearching = true;
-    m_search_->reset              = true;
-    m_search_->matchedIndex       = USEARCH_DONE;
-    m_search_->matchedLength      = 0;
-    text.getText(m_text_);
-    m_search_->text               = m_text_.getBuffer();
-    m_search_->textLength         = m_text_.length();
-    m_breakiterator_             = breakiter;
-}
-
-// protected methods ------------------------------------------------------
-
-SearchIterator & SearchIterator::operator=(const SearchIterator &that)
-{
-    if (this != &that) {
-        m_breakiterator_            = that.m_breakiterator_;
-        m_text_                     = that.m_text_;
-        m_search_->breakIter        = that.m_search_->breakIter;
-        m_search_->isCanonicalMatch = that.m_search_->isCanonicalMatch;
-        m_search_->isOverlap        = that.m_search_->isOverlap;
-        m_search_->elementComparisonType = that.m_search_->elementComparisonType;
-        m_search_->matchedIndex     = that.m_search_->matchedIndex;
-        m_search_->matchedLength    = that.m_search_->matchedLength;
-        m_search_->text             = that.m_search_->text;
-        m_search_->textLength       = that.m_search_->textLength;
-    }
-    return *this;
-}
-
-void SearchIterator::setMatchLength(int32_t length)
-{
-    m_search_->matchedLength = length;
-}
-
-void SearchIterator::setMatchStart(int32_t position)
-{
-    m_search_->matchedIndex = position;
-}
-
-void SearchIterator::setMatchNotFound()
-{
-    setMatchStart(USEARCH_DONE);
-    setMatchLength(0);
-    UErrorCode status = U_ZERO_ERROR;
-    // by default no errors should be returned here since offsets are within
-    // range.
-    if (m_search_->isForwardSearching) {
-        setOffset(m_search_->textLength, status);
-    }
-    else {
-        setOffset(0, status);
-    }
-}
-
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_COLLATION */
diff --git a/libs/icui18n/stsearch.cpp b/libs/icui18n/stsearch.cpp
deleted file mode 100644
index 393eee5acad6..000000000000
--- a/libs/icui18n/stsearch.cpp
+++ /dev/null
@@ -1,483 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-*   Copyright (C) 2001-2014 IBM and others. All rights reserved.
-**********************************************************************
-*   Date        Name        Description
-*  03/22/2000   helena      Creation.
-**********************************************************************
-*/
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_COLLATION && !UCONFIG_NO_BREAK_ITERATION
-
-#include "unicode/stsearch.h"
-#include "usrchimp.h"
-#include "cmemory.h"
-
-U_NAMESPACE_BEGIN
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringSearch)
-
-// public constructors and destructors -----------------------------------
-
-StringSearch::StringSearch(const UnicodeString &pattern,
-                           const UnicodeString &text,
-                           const Locale        &locale,
-                                 BreakIterator *breakiter,
-                                 UErrorCode    &status) :
-                           SearchIterator(text, breakiter),
-                           m_pattern_(pattern)
-{
-    if (U_FAILURE(status)) {
-        m_strsrch_ = NULL;
-        return;
-    }
-
-    m_strsrch_ = usearch_open(m_pattern_.getBuffer(), m_pattern_.length(),
-                              m_text_.getBuffer(), m_text_.length(),
-                              locale.getName(), (UBreakIterator *)breakiter,
-                              &status);
-    uprv_free(m_search_);
-    m_search_ = NULL;
-
-    if (U_SUCCESS(status)) {
-        // m_search_ has been created by the base SearchIterator class
-        m_search_        = m_strsrch_->search;
-    }
-}
-
-StringSearch::StringSearch(const UnicodeString     &pattern,
-                           const UnicodeString     &text,
-                                 RuleBasedCollator *coll,
-                                 BreakIterator     *breakiter,
-                                 UErrorCode        &status) :
-                           SearchIterator(text, breakiter),
-                           m_pattern_(pattern)
-{
-    if (U_FAILURE(status)) {
-        m_strsrch_ = NULL;
-        return;
-    }
-    if (coll == NULL) {
-        status     = U_ILLEGAL_ARGUMENT_ERROR;
-        m_strsrch_ = NULL;
-        return;
-    }
-    m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
-                                          m_pattern_.length(),
-                                          m_text_.getBuffer(),
-                                          m_text_.length(), coll->toUCollator(),
-                                          (UBreakIterator *)breakiter,
-                                          &status);
-    uprv_free(m_search_);
-    m_search_ = NULL;
-
-    if (U_SUCCESS(status)) {
-        // m_search_ has been created by the base SearchIterator class
-        m_search_ = m_strsrch_->search;
-    }
-}
-
-StringSearch::StringSearch(const UnicodeString     &pattern,
-                                 CharacterIterator &text,
-                           const Locale            &locale,
-                                 BreakIterator     *breakiter,
-                                 UErrorCode        &status) :
-                           SearchIterator(text, breakiter),
-                           m_pattern_(pattern)
-{
-    if (U_FAILURE(status)) {
-        m_strsrch_ = NULL;
-        return;
-    }
-    m_strsrch_ = usearch_open(m_pattern_.getBuffer(), m_pattern_.length(),
-                              m_text_.getBuffer(), m_text_.length(),
-                              locale.getName(), (UBreakIterator *)breakiter,
-                              &status);
-    uprv_free(m_search_);
-    m_search_ = NULL;
-
-    if (U_SUCCESS(status)) {
-        // m_search_ has been created by the base SearchIterator class
-        m_search_ = m_strsrch_->search;
-    }
-}
-
-StringSearch::StringSearch(const UnicodeString     &pattern,
-                                 CharacterIterator &text,
-                                 RuleBasedCollator *coll,
-                                 BreakIterator     *breakiter,
-                                 UErrorCode        &status) :
-                           SearchIterator(text, breakiter),
-                           m_pattern_(pattern)
-{
-    if (U_FAILURE(status)) {
-        m_strsrch_ = NULL;
-        return;
-    }
-    if (coll == NULL) {
-        status     = U_ILLEGAL_ARGUMENT_ERROR;
-        m_strsrch_ = NULL;
-        return;
-    }
-    m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
-                                          m_pattern_.length(),
-                                          m_text_.getBuffer(),
-                                          m_text_.length(), coll->toUCollator(),
-                                          (UBreakIterator *)breakiter,
-                                          &status);
-    uprv_free(m_search_);
-    m_search_ = NULL;
-
-    if (U_SUCCESS(status)) {
-        // m_search_ has been created by the base SearchIterator class
-        m_search_ = m_strsrch_->search;
-    }
-}
-
-StringSearch::StringSearch(const StringSearch &that) :
-                       SearchIterator(that.m_text_, that.m_breakiterator_),
-                       m_pattern_(that.m_pattern_)
-{
-    UErrorCode status = U_ZERO_ERROR;
-
-    // Free m_search_ from the superclass
-    uprv_free(m_search_);
-    m_search_ = NULL;
-
-    if (that.m_strsrch_ == NULL) {
-        // This was not a good copy
-        m_strsrch_ = NULL;
-    }
-    else {
-        // Make a deep copy
-        m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
-                                              m_pattern_.length(),
-                                              m_text_.getBuffer(),
-                                              m_text_.length(),
-                                              that.m_strsrch_->collator,
-                                             (UBreakIterator *)that.m_breakiterator_,
-                                              &status);
-        if (U_SUCCESS(status)) {
-            // m_search_ has been created by the base SearchIterator class
-            m_search_        = m_strsrch_->search;
-        }
-    }
-}
-
-StringSearch::~StringSearch()
-{
-    if (m_strsrch_ != NULL) {
-        usearch_close(m_strsrch_);
-        m_search_ = NULL;
-    }
-}
-
-StringSearch *
-StringSearch::clone() const {
-    return new StringSearch(*this);
-}
-
-// operator overloading ---------------------------------------------
-StringSearch & StringSearch::operator=(const StringSearch &that)
-{
-    if (this != &that) {
-        UErrorCode status = U_ZERO_ERROR;
-        m_text_          = that.m_text_;
-        m_breakiterator_ = that.m_breakiterator_;
-        m_pattern_       = that.m_pattern_;
-        // all m_search_ in the parent class is linked up with m_strsrch_
-        usearch_close(m_strsrch_);
-        m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
-                                              m_pattern_.length(),
-                                              m_text_.getBuffer(),
-                                              m_text_.length(),
-                                              that.m_strsrch_->collator,
-                                              NULL, &status);
-        // Check null pointer
-        if (m_strsrch_ != NULL) {
-            m_search_ = m_strsrch_->search;
-        }
-    }
-    return *this;
-}
-
-bool StringSearch::operator==(const SearchIterator &that) const
-{
-    if (this == &that) {
-        return true;
-    }
-    if (SearchIterator::operator ==(that)) {
-        StringSearch &thatsrch = (StringSearch &)that;
-        return (this->m_pattern_ == thatsrch.m_pattern_ &&
-                this->m_strsrch_->collator == thatsrch.m_strsrch_->collator);
-    }
-    return false;
-}
-
-// public get and set methods ----------------------------------------
-
-void StringSearch::setOffset(int32_t position, UErrorCode &status)
-{
-    // status checked in usearch_setOffset
-    usearch_setOffset(m_strsrch_, position, &status);
-}
-
-int32_t StringSearch::getOffset(void) const
-{
-    return usearch_getOffset(m_strsrch_);
-}
-
-void StringSearch::setText(const UnicodeString &text, UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        m_text_ = text;
-        usearch_setText(m_strsrch_, text.getBuffer(), text.length(), &status);
-    }
-}
-
-void StringSearch::setText(CharacterIterator &text, UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        text.getText(m_text_);
-        usearch_setText(m_strsrch_, m_text_.getBuffer(), m_text_.length(), &status);
-    }
-}
-
-RuleBasedCollator * StringSearch::getCollator() const
-{
-    // Note the const_cast. It would be cleaner if this const method returned a const collator.
-    return RuleBasedCollator::rbcFromUCollator(const_cast<UCollator *>(m_strsrch_->collator));
-}
-
-void StringSearch::setCollator(RuleBasedCollator *coll, UErrorCode &status)
-{
-    if (U_SUCCESS(status)) {
-        usearch_setCollator(m_strsrch_, coll->toUCollator(), &status);
-    }
-}
-
-void StringSearch::setPattern(const UnicodeString &pattern,
-                                    UErrorCode    &status)
-{
-    if (U_SUCCESS(status)) {
-        m_pattern_ = pattern;
-        usearch_setPattern(m_strsrch_, m_pattern_.getBuffer(), m_pattern_.length(),
-                           &status);
-    }
-}
-
-const UnicodeString & StringSearch::getPattern() const
-{
-    return m_pattern_;
-}
-
-// public methods ----------------------------------------------------
-
-void StringSearch::reset()
-{
-    usearch_reset(m_strsrch_);
-}
-
-StringSearch * StringSearch::safeClone() const
-{
-    UErrorCode status = U_ZERO_ERROR;
-    StringSearch *result = new StringSearch(m_pattern_, m_text_,
-                                            getCollator(),
-                                            m_breakiterator_,
-                                            status);
-    /* test for NULL */
-    if (result == 0) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return 0;
-    }
-    result->setOffset(getOffset(), status);
-    result->setMatchStart(m_strsrch_->search->matchedIndex);
-    result->setMatchLength(m_strsrch_->search->matchedLength);
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    return result;
-}
-
-// protected method -------------------------------------------------
-
-int32_t StringSearch::handleNext(int32_t position, UErrorCode &status)
-{
-    // values passed here are already in the pre-shift position
-    if (U_SUCCESS(status)) {
-        if (m_strsrch_->pattern.cesLength == 0) {
-            m_search_->matchedIndex =
-                                    m_search_->matchedIndex == USEARCH_DONE ?
-                                    getOffset() : m_search_->matchedIndex + 1;
-            m_search_->matchedLength = 0;
-            ucol_setOffset(m_strsrch_->textIter, m_search_->matchedIndex,
-                           &status);
-            if (m_search_->matchedIndex == m_search_->textLength) {
-                m_search_->matchedIndex = USEARCH_DONE;
-            }
-        }
-        else {
-            // looking at usearch.cpp, this part is shifted out to
-            // StringSearch instead of SearchIterator because m_strsrch_ is
-            // not accessible in SearchIterator
-#if 0
-            if (position + m_strsrch_->pattern.defaultShiftSize
-                > m_search_->textLength) {
-                setMatchNotFound();
-                return USEARCH_DONE;
-            }
-#endif
-            if (m_search_->matchedLength <= 0) {
-                // the flipping direction issue has already been handled
-                // in next()
-                // for boundary check purposes. this will ensure that the
-                // next match will not precede the current offset
-                // note search->matchedIndex will always be set to something
-                // in the code
-                m_search_->matchedIndex = position - 1;
-            }
-
-            ucol_setOffset(m_strsrch_->textIter, position, &status);
-
-#if 0
-            for (;;) {
-                if (m_search_->isCanonicalMatch) {
-                    // can't use exact here since extra accents are allowed.
-                    usearch_handleNextCanonical(m_strsrch_, &status);
-                }
-                else {
-                    usearch_handleNextExact(m_strsrch_, &status);
-                }
-                if (U_FAILURE(status)) {
-                    return USEARCH_DONE;
-                }
-                if (m_breakiterator_ == NULL
-#if !UCONFIG_NO_BREAK_ITERATION
-                    ||
-                    m_search_->matchedIndex == USEARCH_DONE ||
-                    (m_breakiterator_->isBoundary(m_search_->matchedIndex) &&
-                     m_breakiterator_->isBoundary(m_search_->matchedIndex +
-                                                  m_search_->matchedLength))
-#endif
-                ) {
-                    if (m_search_->matchedIndex == USEARCH_DONE) {
-                        ucol_setOffset(m_strsrch_->textIter,
-                                       m_search_->textLength, &status);
-                    }
-                    else {
-                        ucol_setOffset(m_strsrch_->textIter,
-                                       m_search_->matchedIndex, &status);
-                    }
-                    return m_search_->matchedIndex;
-                }
-            }
-#else
-            // if m_strsrch_->breakIter is always the same as m_breakiterator_
-            // then we don't need to check the match boundaries here because
-            // usearch_handleNextXXX will already have done it.
-            if (m_search_->isCanonicalMatch) {
-		// *could* actually use exact here 'cause no extra accents allowed...
-		usearch_handleNextCanonical(m_strsrch_, &status);
-            } else {
-		usearch_handleNextExact(m_strsrch_, &status);
-            }
-
-            if (U_FAILURE(status)) {
-		return USEARCH_DONE;
-            }
-
-            if (m_search_->matchedIndex == USEARCH_DONE) {
-		ucol_setOffset(m_strsrch_->textIter, m_search_->textLength, &status);
-            } else {
-		ucol_setOffset(m_strsrch_->textIter, m_search_->matchedIndex, &status);
-            }
-
-            return m_search_->matchedIndex;
-#endif
-        }
-    }
-    return USEARCH_DONE;
-}
-
-int32_t StringSearch::handlePrev(int32_t position, UErrorCode &status)
-{
-    // values passed here are already in the pre-shift position
-    if (U_SUCCESS(status)) {
-        if (m_strsrch_->pattern.cesLength == 0) {
-            m_search_->matchedIndex =
-                  (m_search_->matchedIndex == USEARCH_DONE ? getOffset() :
-                   m_search_->matchedIndex);
-            if (m_search_->matchedIndex == 0) {
-                setMatchNotFound();
-            }
-            else {
-                m_search_->matchedIndex --;
-                ucol_setOffset(m_strsrch_->textIter, m_search_->matchedIndex,
-                               &status);
-                m_search_->matchedLength = 0;
-            }
-        }
-        else {
-            // looking at usearch.cpp, this part is shifted out to
-            // StringSearch instead of SearchIterator because m_strsrch_ is
-            // not accessible in SearchIterator
-#if 0
-            if (!m_search_->isOverlap &&
-                position - m_strsrch_->pattern.defaultShiftSize < 0) {
-                setMatchNotFound();
-                return USEARCH_DONE;
-            }
-
-            for (;;) {
-                if (m_search_->isCanonicalMatch) {
-                    // can't use exact here since extra accents are allowed.
-                    usearch_handlePreviousCanonical(m_strsrch_, &status);
-                }
-                else {
-                    usearch_handlePreviousExact(m_strsrch_, &status);
-                }
-                if (U_FAILURE(status)) {
-                    return USEARCH_DONE;
-                }
-                if (m_breakiterator_ == NULL
-#if !UCONFIG_NO_BREAK_ITERATION
-                    ||
-                    m_search_->matchedIndex == USEARCH_DONE ||
-                    (m_breakiterator_->isBoundary(m_search_->matchedIndex) &&
-                     m_breakiterator_->isBoundary(m_search_->matchedIndex +
-                                                  m_search_->matchedLength))
-#endif
-                ) {
-                    return m_search_->matchedIndex;
-                }
-            }
-#else
-            ucol_setOffset(m_strsrch_->textIter, position, &status);
-
-            if (m_search_->isCanonicalMatch) {
-		// *could* use exact match here since extra accents *not* allowed!
-		usearch_handlePreviousCanonical(m_strsrch_, &status);
-            } else {
-		usearch_handlePreviousExact(m_strsrch_, &status);
-            }
-
-            if (U_FAILURE(status)) {
-		return USEARCH_DONE;
-            }
-
-            return m_search_->matchedIndex;
-#endif
-        }
-
-        return m_search_->matchedIndex;
-    }
-    return USEARCH_DONE;
-}
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_COLLATION */
diff --git a/libs/icui18n/tmunit.cpp b/libs/icui18n/tmunit.cpp
deleted file mode 100644
index 9d9ba46f203a..000000000000
--- a/libs/icui18n/tmunit.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- * Copyright (C) 2008-2014, Google, International Business Machines Corporation and
- * others. All Rights Reserved.
- *******************************************************************************
- */
-
-#include "unicode/tmunit.h"
-#include "uassert.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-U_NAMESPACE_BEGIN
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnit)
-
-
-/*
- * There are only 7 time units.
- * So, TimeUnit could be made as singleton
- * (similar to uniset_props.cpp, or unorm.cpp,
- * in which a static TimeUnit* array is created, and
- * the creatInstance() returns a const TimeUnit*).
- * But the constraint is TimeUnit is a data member of Measure.
- * But Measure (which is an existing API) does not expect it's "unit" member
- * as singleton. Meaure takes ownership of the "unit" member.
- * In its constructor, it does not take a const "unit" pointer.
- * Also, Measure can clone and destruct the "unit" pointer.
- * In order to preserve the old behavior and let Measure handle singleton "unit",
- * 1. a flag need to be added in Measure;
- * 2. a new constructor which takes const "unit" as parameter need to be added,
- *    and this new constructor will set the flag on.
- * 3. clone and destructor need to check upon this flag to distinguish on how
- *    to handle the "unit".
- *
- * Since TimeUnit is such a light weight object, comparing with the heavy weight
- * format operation, we decided to avoid the above complication.
- *
- * So, both TimeUnit and CurrencyUnit (the 2 subclasses of MeasureUnit) are
- * immutable and non-singleton.
- *
- * Currently, TimeUnitAmount and CurrencyAmount are immutable.
- * If an application needs to create a long list of TimeUnitAmount on the same
- * time unit but different number, for example,
- * 1 hour, 2 hour, 3 hour, ................. 10,000 hour,
- * there might be performance hit because 10,000 TimeUnit object,
- * although all are the same time unit, will be created in heap and deleted.
- *
- * To address this performance issue, if there is any in the future,
- * we should and need to change TimeUnitAmount and CurrencyAmount to be
- * immutable by allowing a setter on the number.
- * Or we need to add 2 parallel mutable classes in order to
- * preserve the existing API.
- * Or we can use freezable.
- */
-TimeUnit* U_EXPORT2
-TimeUnit::createInstance(TimeUnit::UTimeUnitFields timeUnitField,
-                         UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    if (timeUnitField < 0 || timeUnitField >= UTIMEUNIT_FIELD_COUNT) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return NULL;
-    }
-    return new TimeUnit(timeUnitField);
-}
-
-
-TimeUnit::TimeUnit(TimeUnit::UTimeUnitFields timeUnitField) {
-    fTimeUnitField = timeUnitField;
-    switch (fTimeUnitField) {
-    case UTIMEUNIT_YEAR:
-        initTime("year");
-        break;
-    case UTIMEUNIT_MONTH:
-        initTime("month");
-        break;
-    case UTIMEUNIT_DAY:
-        initTime("day");
-        break;
-    case UTIMEUNIT_WEEK:
-        initTime("week");
-        break;
-    case UTIMEUNIT_HOUR:
-        initTime("hour");
-        break;
-    case UTIMEUNIT_MINUTE:
-        initTime("minute");
-        break;
-    case UTIMEUNIT_SECOND:
-        initTime("second");
-        break;
-    default:
-        UPRV_UNREACHABLE_EXIT;
-    }
-}
-
-TimeUnit::TimeUnit(const TimeUnit& other)
-:   MeasureUnit(other), fTimeUnitField(other.fTimeUnitField) {
-}
-
-TimeUnit*
-TimeUnit::clone() const {
-    return new TimeUnit(*this);
-}
-
-TimeUnit&
-TimeUnit::operator=(const TimeUnit& other) {
-    if (this == &other) {
-        return *this;
-    }
-    MeasureUnit::operator=(other);
-    fTimeUnitField = other.fTimeUnitField;
-    return *this;
-}
-
-TimeUnit::UTimeUnitFields
-TimeUnit::getTimeUnitField() const {
-    return fTimeUnitField;
-}
-
-TimeUnit::~TimeUnit() {
-}
-
-
-U_NAMESPACE_END
-
-#endif
diff --git a/libs/icui18n/tmutamt.cpp b/libs/icui18n/tmutamt.cpp
deleted file mode 100644
index c324332a4849..000000000000
--- a/libs/icui18n/tmutamt.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- * Copyright (C) 2008, Google, International Business Machines Corporation and *
- * others. All Rights Reserved.                                                *
- *******************************************************************************
- */
-
-#include "unicode/tmutamt.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-U_NAMESPACE_BEGIN
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitAmount)
-
-
-TimeUnitAmount::TimeUnitAmount(const Formattable& number,
-                               TimeUnit::UTimeUnitFields timeUnitField,
-                               UErrorCode& status)
-:    Measure(number, TimeUnit::createInstance(timeUnitField, status), status) {
-}
-
-
-TimeUnitAmount::TimeUnitAmount(double amount,
-                               TimeUnit::UTimeUnitFields timeUnitField,
-                               UErrorCode& status)
-:   Measure(Formattable(amount),
-            TimeUnit::createInstance(timeUnitField, status),
-            status) {
-}
-
-
-TimeUnitAmount::TimeUnitAmount(const TimeUnitAmount& other)
-:   Measure(other)
-{
-}
-
-
-TimeUnitAmount&
-TimeUnitAmount::operator=(const TimeUnitAmount& other) {
-    Measure::operator=(other);
-    return *this;
-}
-
-
-bool
-TimeUnitAmount::operator==(const UObject& other) const {
-    return Measure::operator==(other);
-}
-
-TimeUnitAmount*
-TimeUnitAmount::clone() const {
-    return new TimeUnitAmount(*this);
-}
-
-
-TimeUnitAmount::~TimeUnitAmount() {
-}
-
-
-
-const TimeUnit&
-TimeUnitAmount::getTimeUnit() const {
-    return (const TimeUnit&) getUnit();
-}
-
-
-TimeUnit::UTimeUnitFields
-TimeUnitAmount::getTimeUnitField() const {
-    return getTimeUnit().getTimeUnitField();
-}
-
-
-U_NAMESPACE_END
-
-#endif
diff --git a/libs/icui18n/tmutfmt.cpp b/libs/icui18n/tmutfmt.cpp
deleted file mode 100644
index 37e56b26a10a..000000000000
--- a/libs/icui18n/tmutfmt.cpp
+++ /dev/null
@@ -1,806 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- * Copyright (C) 2008-2015, Google, International Business Machines Corporation
- * and others. All Rights Reserved.
- *******************************************************************************
- */
-
-#include "unicode/tmutfmt.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/decimfmt.h"
-#include "unicode/localpointer.h"
-#include "plurrule_impl.h"
-#include "uvector.h"
-#include "charstr.h"
-#include "cmemory.h"
-#include "cstring.h"
-#include "hash.h"
-#include "uresimp.h"
-#include "ureslocs.h"
-#include "unicode/msgfmt.h"
-#include "uassert.h"
-
-#define LEFT_CURLY_BRACKET  ((UChar)0x007B)
-#define RIGHT_CURLY_BRACKET ((UChar)0x007D)
-#define SPACE             ((UChar)0x0020)
-#define DIGIT_ZERO        ((UChar)0x0030)
-#define LOW_S             ((UChar)0x0073)
-#define LOW_M             ((UChar)0x006D)
-#define LOW_I             ((UChar)0x0069)
-#define LOW_N             ((UChar)0x006E)
-#define LOW_H             ((UChar)0x0068)
-#define LOW_W             ((UChar)0x0077)
-#define LOW_D             ((UChar)0x0064)
-#define LOW_Y             ((UChar)0x0079)
-#define LOW_Z             ((UChar)0x007A)
-#define LOW_E             ((UChar)0x0065)
-#define LOW_R             ((UChar)0x0072)
-#define LOW_O             ((UChar)0x006F)
-#define LOW_N             ((UChar)0x006E)
-#define LOW_T             ((UChar)0x0074)
-
-
-//TODO: define in compile time
-//#define TMUTFMT_DEBUG 1
-
-#ifdef TMUTFMT_DEBUG
-#include <iostream>
-#endif
-
-U_NAMESPACE_BEGIN
-
-
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
-
-static const char gUnitsTag[] = "units";
-static const char gShortUnitsTag[] = "unitsShort";
-static const char gTimeUnitYear[] = "year";
-static const char gTimeUnitMonth[] = "month";
-static const char gTimeUnitDay[] = "day";
-static const char gTimeUnitWeek[] = "week";
-static const char gTimeUnitHour[] = "hour";
-static const char gTimeUnitMinute[] = "minute";
-static const char gTimeUnitSecond[] = "second";
-static const char gPluralCountOther[] = "other";
-
-static const UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
-static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
-static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
-static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
-static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
-static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
-static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
-
-static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
-static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
-static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
-
-TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
-    initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status);
-    create(UTMUTFMT_FULL_STYLE, status);
-}
-
-
-TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
-    initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
-    create(UTMUTFMT_FULL_STYLE, status);
-}
-
-
-TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
-    switch (style) {
-    case UTMUTFMT_FULL_STYLE:
-        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
-        break;
-    case UTMUTFMT_ABBREVIATED_STYLE:
-        initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
-        break;
-    default:
-        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
-        break;
-    }
-    create(style, status);
-}
-
-TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
-:   MeasureFormat(other),
-    fStyle(other.fStyle)
-{
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        UErrorCode status = U_ZERO_ERROR;
-        fTimeUnitToCountToPatterns[i] = initHash(status);
-        if (U_SUCCESS(status)) {
-            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
-        } else {
-            delete fTimeUnitToCountToPatterns[i];
-            fTimeUnitToCountToPatterns[i] = NULL;
-        }
-    }
-}
-
-
-TimeUnitFormat::~TimeUnitFormat() {
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        deleteHash(fTimeUnitToCountToPatterns[i]);
-        fTimeUnitToCountToPatterns[i] = NULL;
-    }
-}
-
-
-TimeUnitFormat*
-TimeUnitFormat::clone() const {
-    return new TimeUnitFormat(*this);
-}
-
-
-TimeUnitFormat&
-TimeUnitFormat::operator=(const TimeUnitFormat& other) {
-    if (this == &other) {
-        return *this;
-    }
-    MeasureFormat::operator=(other);
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        deleteHash(fTimeUnitToCountToPatterns[i]);
-        fTimeUnitToCountToPatterns[i] = NULL;
-    }
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        UErrorCode status = U_ZERO_ERROR;
-        fTimeUnitToCountToPatterns[i] = initHash(status);
-        if (U_SUCCESS(status)) {
-            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
-        } else {
-            delete fTimeUnitToCountToPatterns[i];
-            fTimeUnitToCountToPatterns[i] = NULL;
-        }
-    }
-    fStyle = other.fStyle;
-    return *this;
-}
-
-void
-TimeUnitFormat::parseObject(const UnicodeString& source,
-                            Formattable& result,
-                            ParsePosition& pos) const {
-    Formattable resultNumber(0.0);
-    UBool withNumberFormat = false;
-    TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
-    int32_t oldPos = pos.getIndex();
-    int32_t newPos = -1;
-    int32_t longestParseDistance = 0;
-    UnicodeString* countOfLongestMatch = NULL;
-#ifdef TMUTFMT_DEBUG
-    char res[1000];
-    source.extract(0, source.length(), res, "UTF-8");
-    std::cout << "parse source: " << res << "\n";
-#endif
-    // parse by iterating through all available patterns
-    // and looking for the longest match.
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
-        int32_t elemPos = UHASH_FIRST;
-        const UHashElement* elem = NULL;
-        while ((elem = countToPatterns->nextElement(elemPos)) != NULL){
-            const UHashTok keyTok = elem->key;
-            UnicodeString* count = (UnicodeString*)keyTok.pointer;
-#ifdef TMUTFMT_DEBUG
-            count->extract(0, count->length(), res, "UTF-8");
-            std::cout << "parse plural count: " << res << "\n";
-#endif
-            const UHashTok valueTok = elem->value;
-            // the value is a pair of MessageFormat*
-            MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
-            for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
-                 style = (UTimeUnitFormatStyle)(style + 1)) {
-                MessageFormat* pattern = patterns[style];
-                pos.setErrorIndex(-1);
-                pos.setIndex(oldPos);
-                // see if we can parse
-                Formattable parsed;
-                pattern->parseObject(source, parsed, pos);
-                if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
-                    continue;
-                }
-    #ifdef TMUTFMT_DEBUG
-                std::cout << "parsed.getType: " << parsed.getType() << "\n";
-    #endif
-                Formattable tmpNumber(0.0);
-                if (pattern->getArgTypeCount() != 0) {
-                    Formattable& temp = parsed[0];
-                    if (temp.getType() == Formattable::kString) {
-                        UnicodeString tmpString;
-                        UErrorCode pStatus = U_ZERO_ERROR;
-                        getNumberFormatInternal().parse(temp.getString(tmpString), tmpNumber, pStatus);
-                        if (U_FAILURE(pStatus)) {
-                            continue;
-                        }
-                    } else if (temp.isNumeric()) {
-                        tmpNumber = temp;
-                    } else {
-                        continue;
-                    }
-                }
-                int32_t parseDistance = pos.getIndex() - oldPos;
-                if (parseDistance > longestParseDistance) {
-                    if (pattern->getArgTypeCount() != 0) {
-                        resultNumber = tmpNumber;
-                        withNumberFormat = true;
-                    } else {
-                        withNumberFormat = false;
-                    }
-                    resultTimeUnit = i;
-                    newPos = pos.getIndex();
-                    longestParseDistance = parseDistance;
-                    countOfLongestMatch = count;
-                }
-            }
-        }
-    }
-    /* After find the longest match, parse the number.
-     * Result number could be null for the pattern without number pattern.
-     * such as unit pattern in Arabic.
-     * When result number is null, use plural rule to set the number.
-     */
-    if (withNumberFormat == false && longestParseDistance != 0) {
-        // set the number using plurrual count
-        if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
-            resultNumber = Formattable(0.0);
-        } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
-            resultNumber = Formattable(1.0);
-        } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
-            resultNumber = Formattable(2.0);
-        } else {
-            // should not happen.
-            // TODO: how to handle?
-            resultNumber = Formattable(3.0);
-        }
-    }
-    if (longestParseDistance == 0) {
-        pos.setIndex(oldPos);
-        pos.setErrorIndex(0);
-    } else {
-        UErrorCode status = U_ZERO_ERROR;
-        LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status);
-        if (U_SUCCESS(status)) {
-            result.adoptObject(tmutamt.orphan());
-            pos.setIndex(newPos);
-            pos.setErrorIndex(-1);
-        } else {
-            pos.setIndex(oldPos);
-            pos.setErrorIndex(0);
-        }
-    }
-}
-
-void
-TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
-    // fTimeUnitToCountToPatterns[] must have its elements initialized to NULL first
-    // before checking for failure status.
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        fTimeUnitToCountToPatterns[i] = NULL;
-    }
-
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-    fStyle = style;
-
-    //TODO: format() and parseObj() are const member functions,
-    //so, can not do lazy initialization in C++.
-    //setup has to be done in constructors.
-    //and here, the behavior is not consistent with Java.
-    //In Java, create an empty instance does not setup locale as
-    //default locale. If it followed by setNumberFormat(),
-    //in format(), the locale will set up as the locale in fNumberFormat.
-    //But in C++, this sets the locale as the default locale.
-    setup(status);
-}
-
-void
-TimeUnitFormat::setup(UErrorCode& err) {
-    initDataMembers(err);
-
-    UVector pluralCounts(nullptr, uhash_compareUnicodeString, 6, err);
-    LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
-    if (U_FAILURE(err)) {
-        return;
-    }
-    UnicodeString* pluralCount;
-    while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) {
-      pluralCounts.addElement(pluralCount, err);
-    }
-    readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
-    checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
-    readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
-    checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
-}
-
-
-void
-TimeUnitFormat::initDataMembers(UErrorCode& err){
-    if (U_FAILURE(err)) {
-        return;
-    }
-    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
-         i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        deleteHash(fTimeUnitToCountToPatterns[i]);
-        fTimeUnitToCountToPatterns[i] = NULL;
-    }
-}
-
-struct TimeUnitFormatReadSink : public ResourceSink {
-    TimeUnitFormat *timeUnitFormatObj;
-    const UVector &pluralCounts;
-    UTimeUnitFormatStyle style;
-    UBool beenHere;
-
-    TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
-            const UVector &pluralCounts, UTimeUnitFormatStyle style) :
-            timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts),
-            style(style), beenHere(false){}
-
-    virtual ~TimeUnitFormatReadSink();
-
-    virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override {
-        // Skip all put() calls except the first one -- discard all fallback data.
-        if (beenHere) {
-            return;
-        } else {
-            beenHere = true;
-        }
-
-        ResourceTable units = value.getTable(errorCode);
-        if (U_FAILURE(errorCode)) { return; }
-
-        for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) {
-            const char* timeUnitName = key;
-            if (timeUnitName == NULL) {
-                continue;
-            }
-
-            TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
-            if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
-            } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
-            } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_DAY;
-            } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
-            } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
-            } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
-            } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
-                timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
-            } else {
-                continue;
-            }
-            LocalPointer<Hashtable> localCountToPatterns;
-            Hashtable *countToPatterns =
-                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField];
-            if (countToPatterns == NULL) {
-                localCountToPatterns.adoptInsteadAndCheckErrorCode(
-                    timeUnitFormatObj->initHash(errorCode), errorCode);
-                countToPatterns = localCountToPatterns.getAlias();
-                if (U_FAILURE(errorCode)) {
-                    return;
-                }
-            }
-
-            ResourceTable countsToPatternTable = value.getTable(errorCode);
-            if (U_FAILURE(errorCode)) {
-                continue;
-            }
-            for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
-                errorCode = U_ZERO_ERROR;
-                UnicodeString pattern = value.getUnicodeString(errorCode);
-                if (U_FAILURE(errorCode)) {
-                    continue;
-                }
-                UnicodeString pluralCountUniStr(key, -1, US_INV);
-                if (!pluralCounts.contains(&pluralCountUniStr)) {
-                    continue;
-                }
-                LocalPointer<MessageFormat> messageFormat(new MessageFormat(
-                    pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
-                if (U_FAILURE(errorCode)) {
-                    return;
-                }
-                MessageFormat** formatters =
-                    (MessageFormat**)countToPatterns->get(pluralCountUniStr);
-                if (formatters == NULL) {
-                    LocalMemory<MessageFormat *> localFormatters(
-                        (MessageFormat **)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
-                    if (localFormatters.isNull()) {
-                        errorCode = U_MEMORY_ALLOCATION_ERROR;
-                        return;
-                    }
-                    localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
-                    localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
-                    countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
-                    if (U_FAILURE(errorCode)) {
-                        return;
-                    }
-                    formatters = localFormatters.orphan();
-                }
-                formatters[style] = messageFormat.orphan();
-            }
-
-            if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
-                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
-            }
-        }
-    }
-
-};
-
-TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
-
-void
-TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
-                                      const UVector& pluralCounts, UErrorCode& err) {
-    if (U_FAILURE(err)) {
-        return;
-    }
-    // fill timeUnitToCountToPatterns from resource file
-    // err is used to indicate wrong status except missing resource.
-    // status is an error code used in resource lookup.
-    // status does not affect "err".
-    UErrorCode status = U_ZERO_ERROR;
-    LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status));
-
-    LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
-    ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    TimeUnitFormatReadSink sink(this, pluralCounts, style);
-    ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
-}
-
-void
-TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
-    if (U_FAILURE(err)) {
-        return;
-    }
-    // there should be patterns for each plural rule in each time unit.
-    // For each time unit,
-    //     for each plural rule, following is unit pattern fall-back rule:
-    //         ( for example: "one" hour )
-    //         look for its unit pattern in its locale tree.
-    //         if pattern is not found in its own locale, such as de_DE,
-    //         look for the pattern in its parent, such as de,
-    //         keep looking till found or till root.
-    //         if the pattern is not found in root either,
-    //         fallback to plural count "other",
-    //         look for the pattern of "other" in the locale tree:
-    //         "de_DE" to "de" to "root".
-    //         If not found, fall back to value of
-    //         static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
-    //
-    // Following is consistency check to create pattern for each
-    // plural rule in each time unit using above fall-back rule.
-    //
-    LocalPointer<StringEnumeration> keywords(
-            getPluralRules().getKeywords(err), err);
-    const UnicodeString* pluralCount;
-    while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != NULL) {
-        for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
-            // for each time unit,
-            // get all the patterns for each plural rule in this locale.
-            Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
-            if ( countToPatterns == NULL ) {
-                fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
-                if (U_FAILURE(err)) {
-                    return;
-                }
-            }
-            MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
-            if( formatters == NULL || formatters[style] == NULL ) {
-                // look through parents
-                const char* localeName = getLocaleID(err);
-                CharString pluralCountChars;
-                pluralCountChars.appendInvariantChars(*pluralCount, err);
-                searchInLocaleChain(style, key, localeName,
-                                    (TimeUnit::UTimeUnitFields)i,
-                                    *pluralCount, pluralCountChars.data(),
-                                    countToPatterns, err);
-            }
-            // TODO: what to do with U_FAILURE(err) at this point.
-            //       As is, the outer loop continues to run, but does nothing.
-        }
-    }
-}
-
-
-
-// srcPluralCount is the original plural count on which the pattern is
-// searched for.
-// searchPluralCount is the fallback plural count.
-// For example, to search for pattern for ""one" hour",
-// "one" is the srcPluralCount,
-// if the pattern is not found even in root, fallback to
-// using patterns of plural count "other",
-// then, "other" is the searchPluralCount.
-void
-TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
-                                TimeUnit::UTimeUnitFields srcTimeUnitField,
-                                const UnicodeString& srcPluralCount,
-                                const char* searchPluralCount,
-                                Hashtable* countToPatterns,
-                                UErrorCode& err) {
-    if (U_FAILURE(err)) {
-        return;
-    }
-    UErrorCode status = U_ZERO_ERROR;
-    char parentLocale[ULOC_FULLNAME_CAPACITY];
-    uprv_strcpy(parentLocale, localeName);
-    int32_t locNameLen;
-    U_ASSERT(countToPatterns != NULL);
-    while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
-                                        ULOC_FULLNAME_CAPACITY, &status)) >= 0){
-        // look for pattern for srcPluralCount in locale tree
-        LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale, &status));
-        LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
-        const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
-        LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, NULL, &status));
-        const UChar* pattern;
-        int32_t      ptLength;
-        pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
-        if (U_SUCCESS(status)) {
-            //found
-            LocalPointer<MessageFormat> messageFormat(
-                new MessageFormat(UnicodeString(true, pattern, ptLength), getLocale(err), err), err);
-            if (U_FAILURE(err)) {
-                return;
-            }
-            MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
-            if (formatters == NULL) {
-                LocalMemory<MessageFormat *> localFormatters(
-                        (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
-                formatters = localFormatters.getAlias();
-                localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
-                localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
-                countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
-                if (U_FAILURE(err)) {
-                    return;
-                }
-            }
-            //delete formatters[style];
-            formatters[style] = messageFormat.orphan();
-            return;
-        }
-        status = U_ZERO_ERROR;
-        if (locNameLen == 0) {
-            break;
-        }
-    }
-
-    // if no unitsShort resource was found even after fallback to root locale
-    // then search the units resource fallback from the current level to root
-    if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
-#ifdef TMUTFMT_DEBUG
-        std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
-#endif
-        CharString pLocale(localeName, -1, err);
-        // Add an underscore at the tail of locale name,
-        // so that searchInLocaleChain will check the current locale before falling back
-        pLocale.append('_', err);
-        searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount,
-                             searchPluralCount, countToPatterns, err);
-        if (U_FAILURE(err)) {
-            return;
-        }
-        MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
-        if (formatters != NULL && formatters[style] != NULL) {
-            return;
-        }
-    }
-
-    // if not found the pattern for this plural count at all,
-    // fall-back to plural count "other"
-    if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
-        // set default fall back the same as the resource in root
-        LocalPointer<MessageFormat> messageFormat;
-        const UChar *pattern = NULL;
-        if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
-            pattern = DEFAULT_PATTERN_FOR_SECOND;
-        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
-            pattern = DEFAULT_PATTERN_FOR_MINUTE;
-        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
-            pattern = DEFAULT_PATTERN_FOR_HOUR;
-        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
-            pattern = DEFAULT_PATTERN_FOR_WEEK;
-        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
-            pattern = DEFAULT_PATTERN_FOR_DAY;
-        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
-            pattern = DEFAULT_PATTERN_FOR_MONTH;
-        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
-            pattern = DEFAULT_PATTERN_FOR_YEAR;
-        }
-        if (pattern != NULL) {
-            messageFormat.adoptInsteadAndCheckErrorCode(
-                     new MessageFormat(UnicodeString(true, pattern, -1), getLocale(err), err), err);
-        }
-        if (U_FAILURE(err)) {
-            return;
-        }
-        MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
-        if (formatters == NULL) {
-            LocalMemory<MessageFormat *> localFormatters (
-                    (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
-            if (localFormatters.isNull()) {
-                err = U_MEMORY_ALLOCATION_ERROR;
-                return;
-            }
-            formatters = localFormatters.getAlias();
-            formatters[UTMUTFMT_FULL_STYLE] = NULL;
-            formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
-            countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
-        }
-        if (U_SUCCESS(err)) {
-            //delete formatters[style];
-            formatters[style] = messageFormat.orphan();
-        }
-    } else {
-        // fall back to rule "other", and search in parents
-        searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
-                            gPluralCountOther, countToPatterns, err);
-    }
-}
-
-void
-TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
-    if (setMeasureFormatLocale(locale, status)) {
-        setup(status);
-    }
-}
-
-
-void
-TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
-    if (U_FAILURE(status)) {
-        return;
-    }
-    adoptNumberFormat(format.clone(), status);
-}
-
-
-void
-TimeUnitFormat::deleteHash(Hashtable* htable) {
-    int32_t pos = UHASH_FIRST;
-    const UHashElement* element = NULL;
-    if ( htable ) {
-        while ( (element = htable->nextElement(pos)) != NULL ) {
-            const UHashTok valueTok = element->value;
-            const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
-            delete value[UTMUTFMT_FULL_STYLE];
-            delete value[UTMUTFMT_ABBREVIATED_STYLE];
-            //delete[] value;
-            uprv_free(value);
-        }
-    }
-    delete htable;
-}
-
-
-void
-TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
-    if ( U_FAILURE(status) ) {
-        return;
-    }
-    int32_t pos = UHASH_FIRST;
-    const UHashElement* element = NULL;
-    if ( source ) {
-        while ( (element = source->nextElement(pos)) != NULL ) {
-            const UHashTok keyTok = element->key;
-            const UnicodeString* key = (UnicodeString*)keyTok.pointer;
-            const UHashTok valueTok = element->value;
-            const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
-            MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
-            newVal[0] = value[0]->clone();
-            newVal[1] = value[1]->clone();
-            target->put(UnicodeString(*key), newVal, status);
-            if ( U_FAILURE(status) ) {
-                delete newVal[0];
-                delete newVal[1];
-                uprv_free(newVal);
-                return;
-            }
-        }
-    }
-}
-
-
-U_CDECL_BEGIN
-
-/**
- * set hash table value comparator
- *
- * @param val1  one value in comparison
- * @param val2  the other value in comparison
- * @return      true if 2 values are the same, false otherwise
- */
-static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
-
-static UBool
-U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
-    const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
-    const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
-    return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
-}
-
-U_CDECL_END
-
-Hashtable*
-TimeUnitFormat::initHash(UErrorCode& status) {
-    if ( U_FAILURE(status) ) {
-        return NULL;
-    }
-    Hashtable* hTable;
-    if ( (hTable = new Hashtable(true, status)) == NULL ) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-    if ( U_FAILURE(status) ) {
-        delete hTable;
-        return NULL;
-    }
-    hTable->setValueComparator(tmutfmtHashTableValueComparator);
-    return hTable;
-}
-
-
-const char*
-TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
-                                UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    switch (unitField) {
-      case TimeUnit::UTIMEUNIT_YEAR:
-        return gTimeUnitYear;
-      case TimeUnit::UTIMEUNIT_MONTH:
-        return gTimeUnitMonth;
-      case TimeUnit::UTIMEUNIT_DAY:
-        return gTimeUnitDay;
-      case TimeUnit::UTIMEUNIT_WEEK:
-        return gTimeUnitWeek;
-      case TimeUnit::UTIMEUNIT_HOUR:
-        return gTimeUnitHour;
-      case TimeUnit::UTIMEUNIT_MINUTE:
-        return gTimeUnitMinute;
-      case TimeUnit::UTIMEUNIT_SECOND:
-        return gTimeUnitSecond;
-      default:
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return NULL;
-    }
-}
-
-U_NAMESPACE_END
-
-#endif
diff --git a/libs/icui18n/unicode/alphaindex.h b/libs/icui18n/unicode/alphaindex.h
deleted file mode 100644
index 5cd307eba11c..000000000000
--- a/libs/icui18n/unicode/alphaindex.h
+++ /dev/null
@@ -1,766 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 2011-2014 International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*/
-
-#ifndef INDEXCHARS_H
-#define INDEXCHARS_H
-
-#include "unicode/utypes.h"
-
-#if U_SHOW_CPLUSPLUS_API
-
-#include "unicode/uobject.h"
-#include "unicode/locid.h"
-#include "unicode/unistr.h"
-
-#if !UCONFIG_NO_COLLATION
-
-/**
- * \file
- * \brief C++ API: Index Characters
- */
-
-U_CDECL_BEGIN
-
-/**
- * Constants for Alphabetic Index Label Types.
- * The form of these enum constants anticipates having a plain C API
- * for Alphabetic Indexes that will also use them.
- * @stable ICU 4.8
- */
-typedef enum UAlphabeticIndexLabelType {
-    /**
-     *  Normal Label, typically the starting letter of the names
-     *  in the bucket with this label.
-     * @stable ICU 4.8
-     */
-    U_ALPHAINDEX_NORMAL    = 0,
-
-    /**
-     * Underflow Label.  The bucket with this label contains names
-     * in scripts that sort before any of the bucket labels in this index.
-     * @stable ICU 4.8
-     */
-    U_ALPHAINDEX_UNDERFLOW = 1,
-
-    /**
-     * Inflow Label.  The bucket with this label contains names
-     * in scripts that sort between two of the bucket labels in this index.
-     * Inflow labels are created when an index contains normal labels for
-     * multiple scripts, and skips other scripts that sort between some of the
-     * included scripts.
-     * @stable ICU 4.8
-     */
-    U_ALPHAINDEX_INFLOW    = 2,
-
-    /**
-     * Overflow Label. The bucket with this label contains names in scripts
-     * that sort after all of the bucket labels in this index.
-     * @stable ICU 4.8
-     */
-    U_ALPHAINDEX_OVERFLOW  = 3
-} UAlphabeticIndexLabelType;
-
-
-struct UHashtable;
-U_CDECL_END
-
-U_NAMESPACE_BEGIN
-
-// Forward Declarations
-
-class BucketList;
-class Collator;
-class RuleBasedCollator;
-class StringEnumeration;
-class UnicodeSet;
-class UVector;
-
-/**
- * AlphabeticIndex supports the creation of a UI index appropriate for a given language.
- * It can support either direct use, or use with a client that doesn't support localized collation.
- * The following is an example of what an index might look like in a UI:
- *
- * <pre>
- *  <b>... A B C D E F G H I J K L M N O P Q R S T U V W X Y Z  ...</b>
- *
- *  <b>A</b>
- *     Addison
- *     Albertson
- *     Azensky
- *  <b>B</b>
- *     Baker
- *  ...
- * </pre>
- *
- * The class can generate a list of labels for use as a UI "index", that is, a list of
- * clickable characters (or character sequences) that allow the user to see a segment
- * (bucket) of a larger "target" list. That is, each label corresponds to a bucket in
- * the target list, where everything in the bucket is greater than or equal to the character
- * (according to the locale's collation). Strings can be added to the index;
- * they will be in sorted order in the right bucket.
- * <p>
- * The class also supports having buckets for strings before the first (underflow),
- * after the last (overflow), and between scripts (inflow). For example, if the index
- * is constructed with labels for Russian and English, Greek characters would fall
- * into an inflow bucket between the other two scripts.
- * <p>
- * The AlphabeticIndex class is not intended for public subclassing.
- *
- * <p><em>Note:</em> If you expect to have a lot of ASCII or Latin characters
- * as well as characters from the user's language,
- * then it is a good idea to call addLabels(Locale::getEnglish(), status).</p>
- *
- * <h2>Direct Use</h2>
- * <p>The following shows an example of building an index directly.
- *  The "show..." methods below are just to illustrate usage.
- *
- * <pre>
- * // Create a simple index.  "Item" is assumed to be an application
- * // defined type that the application's UI and other processing knows about,
- * //  and that has a name.
- *
- * UErrorCode status = U_ZERO_ERROR;
- * AlphabeticIndex index = new AlphabeticIndex(desiredLocale, status);
- * index->addLabels(additionalLocale, status);
- * for (Item *item in some source of Items ) {
- *     index->addRecord(item->name(), item, status);
- * }
- * ...
- * // Show index at top. We could skip or gray out empty buckets
- *
- * while (index->nextBucket(status)) {
- *     if (showAll || index->getBucketRecordCount() != 0) {
- *         showLabelAtTop(UI, index->getBucketLabel());
- *     }
- * }
- *  ...
- * // Show the buckets with their contents, skipping empty buckets
- *
- * index->resetBucketIterator(status);
- * while (index->nextBucket(status)) {
- *    if (index->getBucketRecordCount() != 0) {
- *        showLabelInList(UI, index->getBucketLabel());
- *        while (index->nextRecord(status)) {
- *            showIndexedItem(UI, static_cast<Item *>(index->getRecordData()))
- * </pre>
- *
- * The caller can build different UIs using this class.
- * For example, an index character could be omitted or grayed-out
- * if its bucket is empty. Small buckets could also be combined based on size, such as:
- *
- * <pre>
- * <b>... A-F G-N O-Z ...</b>
- * </pre>
- *
- * <h2>Client Support</h2>
- * <p>Callers can also use the AlphabeticIndex::ImmutableIndex, or the AlphabeticIndex itself,
- * to support sorting on a client that doesn't support AlphabeticIndex functionality.
- *
- * <p>The ImmutableIndex is both immutable and thread-safe.
- * The corresponding AlphabeticIndex methods are not thread-safe because
- * they "lazily" build the index buckets.
- * <ul>
- * <li>ImmutableIndex.getBucket(index) provides random access to all
- *     buckets and their labels and label types.
- * <li>The AlphabeticIndex bucket iterator or ImmutableIndex.getBucket(0..getBucketCount-1)
- *     can be used to get a list of the labels,
- *     such as "...", "A", "B",..., and send that list to the client.
- * <li>When the client has a new name, it sends that name to the server.
- * The server needs to call the following methods,
- * and communicate the bucketIndex and collationKey back to the client.
- *
- * <pre>
- * int32_t bucketIndex = index.getBucketIndex(name, status);
- * const UnicodeString &label = immutableIndex.getBucket(bucketIndex)->getLabel();  // optional
- * int32_t skLength = collator.getSortKey(name, sk, skCapacity);
- * </pre>
- *
- * <li>The client would put the name (and associated information) into its bucket for bucketIndex. The sort key sk is a
- * sequence of bytes that can be compared with a binary compare, and produce the right localized result.</li>
- * </ul>
- *
- * @stable ICU 4.8
- */
-class U_I18N_API AlphabeticIndex: public UObject {
-public:
-     /**
-      * An index "bucket" with a label string and type.
-      * It is referenced by getBucketIndex(),
-      * and returned by ImmutableIndex.getBucket().
-      *
-      * The Bucket class is not intended for public subclassing.
-      * @stable ICU 51
-      */
-     class U_I18N_API Bucket : public UObject {
-     public:
-        /**
-         * Destructor.
-         * @stable ICU 51
-         */
-        virtual ~Bucket();
-
-        /**
-         * Returns the label string.
-         *
-         * @return the label string for the bucket
-         * @stable ICU 51
-         */
-        const UnicodeString &getLabel() const { return label_; }
-        /**
-         * Returns whether this bucket is a normal, underflow, overflow, or inflow bucket.
-         *
-         * @return the bucket label type
-         * @stable ICU 51
-         */
-        UAlphabeticIndexLabelType getLabelType() const { return labelType_; }
-
-     private:
-        friend class AlphabeticIndex;
-        friend class BucketList;
-
-        UnicodeString label_;
-        UnicodeString lowerBoundary_;
-        UAlphabeticIndexLabelType labelType_;
-        Bucket *displayBucket_;
-        int32_t displayIndex_;
-        UVector *records_;  // Records are owned by the inputList_ vector.
-
-        Bucket(const UnicodeString &label,   // Parameter strings are copied.
-               const UnicodeString &lowerBoundary,
-               UAlphabeticIndexLabelType type);
-     };
-
-    /**
-     * Immutable, thread-safe version of AlphabeticIndex.
-     * This class provides thread-safe methods for bucketing,
-     * and random access to buckets and their properties,
-     * but does not offer adding records to the index.
-     *
-     * The ImmutableIndex class is not intended for public subclassing.
-     *
-     * @stable ICU 51
-     */
-    class U_I18N_API ImmutableIndex : public UObject {
-    public:
-        /**
-         * Destructor.
-         * @stable ICU 51
-         */
-        virtual ~ImmutableIndex();
-
-        /**
-         * Returns the number of index buckets and labels, including underflow/inflow/overflow.
-         *
-         * @return the number of index buckets
-         * @stable ICU 51
-         */
-        int32_t getBucketCount() const;
-
-        /**
-         * Finds the index bucket for the given name and returns the number of that bucket.
-         * Use getBucket() to get the bucket's properties.
-         *
-         * @param name the string to be sorted into an index bucket
-         * @param errorCode Error code, will be set with the reason if the
-         *                  operation fails.
-         * @return the bucket number for the name
-         * @stable ICU 51
-         */
-        int32_t getBucketIndex(const UnicodeString &name, UErrorCode &errorCode) const;
-
-        /**
-         * Returns the index-th bucket. Returns NULL if the index is out of range.
-         *
-         * @param index bucket number
-         * @return the index-th bucket
-         * @stable ICU 51
-         */
-        const Bucket *getBucket(int32_t index) const;
-
-    private:
-        friend class AlphabeticIndex;
-
-        ImmutableIndex(BucketList *bucketList, Collator *collatorPrimaryOnly)
-                : buckets_(bucketList), collatorPrimaryOnly_(collatorPrimaryOnly) {}
-
-        BucketList *buckets_;
-        Collator *collatorPrimaryOnly_;
-    };
-
-    /**
-     * Construct an AlphabeticIndex object for the specified locale.  If the locale's
-     * data does not include index characters, a set of them will be
-     * synthesized based on the locale's exemplar characters.  The locale
-     * determines the sorting order for both the index characters and the
-     * user item names appearing under each Index character.
-     *
-     * @param locale the desired locale.
-     * @param status Error code, will be set with the reason if the construction
-     *               of the AlphabeticIndex object fails.
-     * @stable ICU 4.8
-     */
-     AlphabeticIndex(const Locale &locale, UErrorCode &status);
-
-   /**
-     * Construct an AlphabeticIndex that uses a specific collator.
-     *
-     * The index will be created with no labels; the addLabels() function must be called
-     * after creation to add the desired labels to the index.
-     *
-     * The index adopts the collator, and is responsible for deleting it.
-     * The caller should make no further use of the collator after creating the index.
-     *
-     * @param collator The collator to use to order the contents of this index.
-     * @param status Error code, will be set with the reason if the
-     *               operation fails.
-     * @stable ICU 51
-     */
-    AlphabeticIndex(RuleBasedCollator *collator, UErrorCode &status);
-
-    /**
-     * Add Labels to this Index.  The labels are additions to those
-     * that are already in the index; they do not replace the existing
-     * ones.
-     * @param additions The additional characters to add to the index, such as A-Z.
-     * @param status Error code, will be set with the reason if the
-     *               operation fails.
-     * @return this, for chaining
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &addLabels(const UnicodeSet &additions, UErrorCode &status);
-
-    /**
-     * Add the index characters from a Locale to the index.  The labels
-     * are added to those that are already in the index; they do not replace the
-     * existing index characters.  The collation order for this index is not
-     * changed; it remains that of the locale that was originally specified
-     * when creating this Index.
-     *
-     * @param locale The locale whose index characters are to be added.
-     * @param status Error code, will be set with the reason if the
-     *               operation fails.
-     * @return this, for chaining
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &addLabels(const Locale &locale, UErrorCode &status);
-
-     /**
-      * Destructor
-      * @stable ICU 4.8
-      */
-    virtual ~AlphabeticIndex();
-
-    /**
-     * Builds an immutable, thread-safe version of this instance, without data records.
-     *
-     * @return an immutable index instance
-     * @stable ICU 51
-     */
-    ImmutableIndex *buildImmutableIndex(UErrorCode &errorCode);
-
-    /**
-     * Get the Collator that establishes the ordering of the items in this index.
-     * Ownership of the collator remains with the AlphabeticIndex instance.
-     *
-     * The returned collator is a reference to the internal collator used by this
-     * index.  It may be safely used to compare the names of items or to get
-     * sort keys for names.  However if any settings need to be changed,
-     * or other non-const methods called, a cloned copy must be made first.
-     *
-     * @return The collator
-     * @stable ICU 4.8
-     */
-    virtual const RuleBasedCollator &getCollator() const;
-
-
-   /**
-     * Get the default label used for abbreviated buckets *between* other index characters.
-     * For example, consider the labels when Latin (X Y Z) and Greek (Α Β Γ) are used:
-     *
-     *     X Y Z ... Α Β Γ.
-     *
-     * @return inflow label
-     * @stable ICU 4.8
-     */
-    virtual const UnicodeString &getInflowLabel() const;
-
-   /**
-     * Set the default label used for abbreviated buckets <i>between</i> other index characters.
-     * An inflow label will be automatically inserted if two otherwise-adjacent label characters
-     * are from different scripts, e.g. Latin and Cyrillic, and a third script, e.g. Greek,
-     * sorts between the two.  The default inflow character is an ellipsis (...)
-     *
-     * @param inflowLabel the new Inflow label.
-     * @param status Error code, will be set with the reason if the operation fails.
-     * @return this
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &setInflowLabel(const UnicodeString &inflowLabel, UErrorCode &status);
-
-
-   /**
-     * Get the special label used for items that sort after the last normal label,
-     * and that would not otherwise have an appropriate label.
-     *
-     * @return the overflow label
-     * @stable ICU 4.8
-     */
-    virtual const UnicodeString &getOverflowLabel() const;
-
-
-   /**
-     * Set the label used for items that sort after the last normal label,
-     * and that would not otherwise have an appropriate label.
-     *
-     * @param overflowLabel the new overflow label.
-     * @param status Error code, will be set with the reason if the operation fails.
-     * @return this
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &setOverflowLabel(const UnicodeString &overflowLabel, UErrorCode &status);
-
-   /**
-     * Get the special label used for items that sort before the first normal label,
-     * and that would not otherwise have an appropriate label.
-     *
-     * @return underflow label
-     * @stable ICU 4.8
-     */
-    virtual const UnicodeString &getUnderflowLabel() const;
-
-   /**
-     * Set the label used for items that sort before the first normal label,
-     * and that would not otherwise have an appropriate label.
-     *
-     * @param underflowLabel the new underflow label.
-     * @param status Error code, will be set with the reason if the operation fails.
-     * @return this
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &setUnderflowLabel(const UnicodeString &underflowLabel, UErrorCode &status);
-
-
-    /**
-     * Get the limit on the number of labels permitted in the index.
-     * The number does not include over, under and inflow labels.
-     *
-     * @return maxLabelCount maximum number of labels.
-     * @stable ICU 4.8
-     */
-    virtual int32_t getMaxLabelCount() const;
-
-    /**
-     * Set a limit on the number of labels permitted in the index.
-     * The number does not include over, under and inflow labels.
-     * Currently, if the number is exceeded, then every
-     * nth item is removed to bring the count down.
-     * A more sophisticated mechanism may be available in the future.
-     *
-     * @param maxLabelCount the maximum number of labels.
-     * @param status error code
-     * @return This, for chaining
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &setMaxLabelCount(int32_t maxLabelCount, UErrorCode &status);
-
-
-    /**
-     * Add a record to the index.  Each record will be associated with an index Bucket
-     *  based on the record's name.  The list of records for each bucket will be sorted
-     *  based on the collation ordering of the names in the index's locale.
-     *  Records with duplicate names are permitted; they will be kept in the order
-     *  that they were added.
-     *
-     * @param name The display name for the Record.  The Record will be placed in
-     *             a bucket based on this name.
-     * @param data An optional pointer to user data associated with this
-     *             item.  When iterating the contents of a bucket, both the
-     *             data pointer the name will be available for each Record.
-     * @param status  Error code, will be set with the reason if the operation fails.
-     * @return        This, for chaining.
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &addRecord(const UnicodeString &name, const void *data, UErrorCode &status);
-
-    /**
-     * Remove all Records from the Index.  The set of Buckets, which define the headings under
-     * which records are classified, is not altered.
-     *
-     * @param status  Error code, will be set with the reason if the operation fails.
-     * @return        This, for chaining.
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &clearRecords(UErrorCode &status);
-
-
-    /**  Get the number of labels in this index.
-     *      Note: may trigger lazy index construction.
-     *
-     * @param status  Error code, will be set with the reason if the operation fails.
-     * @return        The number of labels in this index, including any under, over or
-     *                in-flow labels.
-     * @stable ICU 4.8
-     */
-    virtual int32_t  getBucketCount(UErrorCode &status);
-
-
-    /**  Get the total number of Records in this index, that is, the number
-     *   of <name, data> pairs added.
-     *
-     * @param status  Error code, will be set with the reason if the operation fails.
-     * @return        The number of records in this index, that is, the total number
-     *                of (name, data) items added with addRecord().
-     * @stable ICU 4.8
-     */
-    virtual int32_t  getRecordCount(UErrorCode &status);
-
-
-
-    /**
-     *   Given the name of a record, return the zero-based index of the Bucket
-     *   in which the item should appear.  The name need not be in the index.
-     *   A Record will not be added to the index by this function.
-     *   Bucket numbers are zero-based, in Bucket iteration order.
-     *
-     * @param itemName  The name whose bucket position in the index is to be determined.
-     * @param status  Error code, will be set with the reason if the operation fails.
-     * @return The bucket number for this name.
-     * @stable ICU 4.8
-     *
-     */
-    virtual int32_t  getBucketIndex(const UnicodeString &itemName, UErrorCode &status);
-
-
-    /**
-     *   Get the zero based index of the current Bucket from an iteration
-     *   over the Buckets of this index.  Return -1 if no iteration is in process.
-     *   @return  the index of the current Bucket
-     *   @stable ICU 4.8
-     */
-    virtual int32_t  getBucketIndex() const;
-
-
-    /**
-     *   Advance the iteration over the Buckets of this index.  Return false if
-     *   there are no more Buckets.
-     *
-     *   @param status  Error code, will be set with the reason if the operation fails.
-     *   U_ENUM_OUT_OF_SYNC_ERROR will be reported if the index is modified while
-     *   an enumeration of its contents are in process.
-     *
-     *   @return true if success, false if at end of iteration
-     *   @stable ICU 4.8
-     */
-    virtual UBool nextBucket(UErrorCode &status);
-
-    /**
-     *   Return the name of the Label of the current bucket from an iteration over the buckets.
-     *   If the iteration is before the first Bucket (nextBucket() has not been called),
-     *   or after the last, return an empty string.
-     *
-     *   @return the bucket label.
-     *   @stable ICU 4.8
-     */
-    virtual const UnicodeString &getBucketLabel() const;
-
-    /**
-     *  Return the type of the label for the current Bucket (selected by the
-     *  iteration over Buckets.)
-     *
-     * @return the label type.
-     * @stable ICU 4.8
-     */
-    virtual UAlphabeticIndexLabelType getBucketLabelType() const;
-
-    /**
-      * Get the number of <name, data> Records in the current Bucket.
-      * If the current bucket iteration position is before the first label or after the
-      * last, return 0.
-      *
-      *  @return the number of Records.
-      *  @stable ICU 4.8
-      */
-    virtual int32_t getBucketRecordCount() const;
-
-
-    /**
-     *  Reset the Bucket iteration for this index.  The next call to nextBucket()
-     *  will restart the iteration at the first label.
-     *
-     * @param status  Error code, will be set with the reason if the operation fails.
-     * @return        this, for chaining.
-     * @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &resetBucketIterator(UErrorCode &status);
-
-    /**
-     * Advance to the next record in the current Bucket.
-     * When nextBucket() is called, Record iteration is reset to just before the
-     * first Record in the new Bucket.
-     *
-     *   @param status  Error code, will be set with the reason if the operation fails.
-     *   U_ENUM_OUT_OF_SYNC_ERROR will be reported if the index is modified while
-     *   an enumeration of its contents are in process.
-     *   @return true if successful, false when the iteration advances past the last item.
-     *   @stable ICU 4.8
-     */
-    virtual UBool nextRecord(UErrorCode &status);
-
-    /**
-     * Get the name of the current Record.
-     * Return an empty string if the Record iteration position is before first
-     * or after the last.
-     *
-     *  @return The name of the current index item.
-     *  @stable ICU 4.8
-     */
-    virtual const UnicodeString &getRecordName() const;
-
-
-    /**
-     * Return the data pointer of the Record currently being iterated over.
-     * Return NULL if the current iteration position before the first item in this Bucket,
-     * or after the last.
-     *
-     *  @return The current Record's data pointer.
-     *  @stable ICU 4.8
-     */
-    virtual const void *getRecordData() const;
-
-
-    /**
-     * Reset the Record iterator position to before the first Record in the current Bucket.
-     *
-     *  @return This, for chaining.
-     *  @stable ICU 4.8
-     */
-    virtual AlphabeticIndex &resetRecordIterator();
-
-private:
-     /**
-      * No Copy constructor.
-      * @internal (private)
-      */
-     AlphabeticIndex(const AlphabeticIndex &other) = delete;
-
-     /**
-      *   No assignment.
-      */
-     AlphabeticIndex &operator =(const AlphabeticIndex & /*other*/) { return *this;}
-
-    /**
-     * No Equality operators.
-     * @internal (private)
-     */
-     virtual bool operator==(const AlphabeticIndex& other) const;
-
-    /**
-     * Inequality operator.
-     * @internal (private)
-     */
-     virtual bool operator!=(const AlphabeticIndex& other) const;
-
-     // Common initialization, for use from all constructors.
-     void init(const Locale *locale, UErrorCode &status);
-
-    /**
-     * This method is called to get the index exemplars. Normally these come from the locale directly,
-     * but if they aren't available, we have to synthesize them.
-     */
-    void addIndexExemplars(const Locale &locale, UErrorCode &status);
-    /**
-     * Add Chinese index characters from the tailoring.
-     */
-    UBool addChineseIndexCharacters(UErrorCode &errorCode);
-
-    UVector *firstStringsInScript(UErrorCode &status);
-
-    static UnicodeString separated(const UnicodeString &item);
-
-    /**
-     * Determine the best labels to use.
-     * This is based on the exemplars, but we also process to make sure that they are unique,
-     * and sort differently, and that the overall list is small enough.
-     */
-    void initLabels(UVector &indexCharacters, UErrorCode &errorCode) const;
-    BucketList *createBucketList(UErrorCode &errorCode) const;
-    void initBuckets(UErrorCode &errorCode);
-    void clearBuckets();
-    void internalResetBucketIterator();
-
-public:
-
-    //  The Record is declared public only to allow access from
-    //  implementation code written in plain C.
-    //  It is not intended for public use.
-
-#ifndef U_HIDE_INTERNAL_API
-    /**
-     * A (name, data) pair, to be sorted by name into one of the index buckets.
-     * The user data is not used by the index implementation.
-     * \cond
-     * @internal
-     */
-    struct Record: public UMemory {
-        const UnicodeString  name_;
-        const void           *data_;
-        Record(const UnicodeString &name, const void *data);
-        ~Record();
-    };
-    /** \endcond */
-#endif  /* U_HIDE_INTERNAL_API */
-
-private:
-
-    /**
-     * Holds all user records before they are distributed into buckets.
-     * Type of contents is (Record *)
-     * @internal (private)
-     */
-    UVector  *inputList_;
-
-    int32_t  labelsIterIndex_;        // Index of next item to return.
-    int32_t  itemsIterIndex_;
-    Bucket   *currentBucket_;         // While an iteration of the index in underway,
-                                      //   point to the bucket for the current label.
-                                      // NULL when no iteration underway.
-
-    int32_t    maxLabelCount_;        // Limit on # of labels permitted in the index.
-
-    UnicodeSet *initialLabels_;       // Initial (unprocessed) set of Labels.  Union
-                                      //   of those explicitly set by the user plus
-                                      //   those from locales.  Raw values, before
-                                      //   crunching into bucket labels.
-
-    UVector *firstCharsInScripts_;    // The first character from each script,
-                                      //   in collation order.
-
-    RuleBasedCollator *collator_;
-    RuleBasedCollator *collatorPrimaryOnly_;
-
-    // Lazy evaluated: null means that we have not built yet.
-    BucketList *buckets_;
-
-    UnicodeString  inflowLabel_;
-    UnicodeString  overflowLabel_;
-    UnicodeString  underflowLabel_;
-    UnicodeString  overflowComparisonString_;
-
-    UnicodeString emptyString_;
-};
-
-U_NAMESPACE_END
-
-#endif  // !UCONFIG_NO_COLLATION
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif
diff --git a/libs/icui18n/unicode/measfmt.h b/libs/icui18n/unicode/measfmt.h
deleted file mode 100644
index 50f4ffab28d1..000000000000
--- a/libs/icui18n/unicode/measfmt.h
+++ /dev/null
@@ -1,398 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-* Copyright (c) 2004-2016, International Business Machines
-* Corporation and others.  All Rights Reserved.
-**********************************************************************
-* Author: Alan Liu
-* Created: April 20, 2004
-* Since: ICU 3.0
-**********************************************************************
-*/
-#ifndef MEASUREFORMAT_H
-#define MEASUREFORMAT_H
-
-#include "unicode/utypes.h"
-
-#if U_SHOW_CPLUSPLUS_API
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/format.h"
-#include "unicode/udat.h"
-
-/**
- * \file
- * \brief C++ API: Compatibility APIs for measure formatting.
- */
-
-/**
- * Constants for various widths.
- * There are 4 widths: Wide, Short, Narrow, Numeric.
- * For example, for English, when formatting "3 hours"
- * Wide is "3 hours"; short is "3 hrs"; narrow is "3h";
- * formatting "3 hours 17 minutes" as numeric give "3:17"
- * @stable ICU 53
- */
-enum UMeasureFormatWidth {
-
-    // Wide, short, and narrow must be first and in this order.
-    /**
-     * Spell out measure units.
-     * @stable ICU 53
-     */
-    UMEASFMT_WIDTH_WIDE,
-
-    /**
-     * Abbreviate measure units.
-     * @stable ICU 53
-     */
-    UMEASFMT_WIDTH_SHORT,
-
-    /**
-     * Use symbols for measure units when possible.
-     * @stable ICU 53
-     */
-    UMEASFMT_WIDTH_NARROW,
-
-    /**
-     * Completely omit measure units when possible. For example, format
-     * '5 hours, 37 minutes' as '5:37'
-     * @stable ICU 53
-     */
-    UMEASFMT_WIDTH_NUMERIC,
-
-#ifndef U_HIDE_DEPRECATED_API
-    /**
-     * One more than the highest normal UMeasureFormatWidth value.
-     * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
-     */
-    UMEASFMT_WIDTH_COUNT = 4
-#endif  // U_HIDE_DEPRECATED_API
-};
-/** @stable ICU 53 */
-typedef enum UMeasureFormatWidth UMeasureFormatWidth;
-
-U_NAMESPACE_BEGIN
-
-class Measure;
-class MeasureUnit;
-class NumberFormat;
-class PluralRules;
-class MeasureFormatCacheData;
-class SharedNumberFormat;
-class SharedPluralRules;
-class QuantityFormatter;
-class SimpleFormatter;
-class ListFormatter;
-class DateFormat;
-
-/**
- * <p><strong>IMPORTANT:</strong> New users are strongly encouraged to see if
- * numberformatter.h fits their use case.  Although not deprecated, this header
- * is provided for backwards compatibility only, and has much more limited
- * capabilities.
- *
- * @see Format
- * @author Alan Liu
- * @stable ICU 3.0
- */
-class U_I18N_API MeasureFormat : public Format {
- public:
-    using Format::parseObject;
-    using Format::format;
-
-    /**
-     * Constructor.
-     * <p>
-     * <strong>NOTE:</strong> New users are strongly encouraged to use
-     * {@link icu::number::NumberFormatter} instead of NumberFormat.
-     * @stable ICU 53
-     */
-    MeasureFormat(
-            const Locale &locale, UMeasureFormatWidth width, UErrorCode &status);
-
-    /**
-     * Constructor.
-     * <p>
-     * <strong>NOTE:</strong> New users are strongly encouraged to use
-     * {@link icu::number::NumberFormatter} instead of NumberFormat.
-     * @stable ICU 53
-     */
-    MeasureFormat(
-            const Locale &locale,
-            UMeasureFormatWidth width,
-            NumberFormat *nfToAdopt,
-            UErrorCode &status);
-
-    /**
-     * Copy constructor.
-     * @stable ICU 3.0
-     */
-    MeasureFormat(const MeasureFormat &other);
-
-    /**
-     * Assignment operator.
-     * @stable ICU 3.0
-     */
-    MeasureFormat &operator=(const MeasureFormat &rhs);
-
-    /**
-     * Destructor.
-     * @stable ICU 3.0
-     */
-    virtual ~MeasureFormat();
-
-    /**
-     * Return true if given Format objects are semantically equal.
-     * @stable ICU 53
-     */
-    virtual bool operator==(const Format &other) const override;
-
-    /**
-     * Clones this object polymorphically.
-     * @stable ICU 53
-     */
-    virtual MeasureFormat *clone() const override;
-
-    /**
-     * Formats object to produce a string.
-     * @stable ICU 53
-     */
-    virtual UnicodeString &format(
-            const Formattable &obj,
-            UnicodeString &appendTo,
-            FieldPosition &pos,
-            UErrorCode &status) const override;
-
-#ifndef U_FORCE_HIDE_DRAFT_API
-    /**
-     * Parse a string to produce an object. This implementation sets
-     * status to U_UNSUPPORTED_ERROR.
-     *
-     * @draft ICU 53
-     */
-    virtual void parseObject(
-            const UnicodeString &source,
-            Formattable &reslt,
-            ParsePosition &pos) const override;
-#endif  // U_FORCE_HIDE_DRAFT_API
-
-    /**
-     * Formats measure objects to produce a string. An example of such a
-     * formatted string is 3 meters, 3.5 centimeters. Measure objects appear
-     * in the formatted string in the same order they appear in the "measures"
-     * array. The NumberFormat of this object is used only to format the amount
-     * of the very last measure. The other amounts are formatted with zero
-     * decimal places while rounding toward zero.
-     * @param measures array of measure objects.
-     * @param measureCount the number of measure objects.
-     * @param appendTo formatted string appended here.
-     * @param pos the field position.
-     * @param status the error.
-     * @return appendTo reference
-     *
-     * @stable ICU 53
-     */
-    UnicodeString &formatMeasures(
-            const Measure *measures,
-            int32_t measureCount,
-            UnicodeString &appendTo,
-            FieldPosition &pos,
-            UErrorCode &status) const;
-
-    /**
-     * Formats a single measure per unit. An example of such a
-     * formatted string is 3.5 meters per second.
-     * @param measure The measure object. In above example, 3.5 meters.
-     * @param perUnit The per unit. In above example, it is
-     *        `*%MeasureUnit::createSecond(status)`.
-     * @param appendTo formatted string appended here.
-     * @param pos the field position.
-     * @param status the error.
-     * @return appendTo reference
-     *
-     * @stable ICU 55
-     */
-    UnicodeString &formatMeasurePerUnit(
-            const Measure &measure,
-            const MeasureUnit &perUnit,
-            UnicodeString &appendTo,
-            FieldPosition &pos,
-            UErrorCode &status) const;
-
-    /**
-     * Gets the display name of the specified {@link MeasureUnit} corresponding to the current
-     * locale and format width.
-     * @param unit  The unit for which to get a display name.
-     * @param status the error.
-     * @return  The display name in the locale and width specified in
-     *          the MeasureFormat constructor, or null if there is no display name available
-     *          for the specified unit.
-     *
-     * @stable ICU 58
-     */
-    UnicodeString getUnitDisplayName(const MeasureUnit& unit, UErrorCode &status) const;
-
-
-    /**
-     * Return a formatter for CurrencyAmount objects in the given
-     * locale.
-     * <p>
-     * <strong>NOTE:</strong> New users are strongly encouraged to use
-     * {@link icu::number::NumberFormatter} instead of NumberFormat.
-     * @param locale desired locale
-     * @param ec input-output error code
-     * @return a formatter object, or NULL upon error
-     * @stable ICU 3.0
-     */
-    static MeasureFormat* U_EXPORT2 createCurrencyFormat(const Locale& locale,
-                                               UErrorCode& ec);
-
-    /**
-     * Return a formatter for CurrencyAmount objects in the default
-     * locale.
-     * <p>
-     * <strong>NOTE:</strong> New users are strongly encouraged to use
-     * {@link icu::number::NumberFormatter} instead of NumberFormat.
-     * @param ec input-output error code
-     * @return a formatter object, or NULL upon error
-     * @stable ICU 3.0
-     */
-    static MeasureFormat* U_EXPORT2 createCurrencyFormat(UErrorCode& ec);
-
-    /**
-     * Return the class ID for this class. This is useful only for comparing to
-     * a return value from getDynamicClassID(). For example:
-     * <pre>
-     * .   Base* polymorphic_pointer = createPolymorphicObject();
-     * .   if (polymorphic_pointer->getDynamicClassID() ==
-     * .       erived::getStaticClassID()) ...
-     * </pre>
-     * @return          The class ID for all objects of this class.
-     * @stable ICU 53
-     */
-    static UClassID U_EXPORT2 getStaticClassID(void);
-
-    /**
-     * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This
-     * method is to implement a simple version of RTTI, since not all C++
-     * compilers support genuine RTTI. Polymorphic operator==() and clone()
-     * methods call this method.
-     *
-     * @return          The class ID for this object. All objects of a
-     *                  given class have the same class ID.  Objects of
-     *                  other classes have different class IDs.
-     * @stable ICU 53
-     */
-    virtual UClassID getDynamicClassID(void) const override;
-
- protected:
-    /**
-     * Default constructor.
-     * @stable ICU 3.0
-     */
-    MeasureFormat();
-
-#ifndef U_HIDE_INTERNAL_API
-
-    /**
-     * ICU use only.
-     * Initialize or change MeasureFormat class from subclass.
-     * @internal.
-     */
-    void initMeasureFormat(
-            const Locale &locale,
-            UMeasureFormatWidth width,
-            NumberFormat *nfToAdopt,
-            UErrorCode &status);
-    /**
-     * ICU use only.
-     * Allows subclass to change locale. Note that this method also changes
-     * the NumberFormat object. Returns true if locale changed; false if no
-     * change was made.
-     * @internal.
-     */
-    UBool setMeasureFormatLocale(const Locale &locale, UErrorCode &status);
-
-    /**
-     * ICU use only.
-     * Let subclass change NumberFormat.
-     * @internal.
-     */
-    void adoptNumberFormat(NumberFormat *nfToAdopt, UErrorCode &status);
-
-    /**
-     * ICU use only.
-     * @internal.
-     */
-    const NumberFormat &getNumberFormatInternal() const;
-
-    /**
-     * ICU use only.
-     * Always returns the short form currency formatter.
-     * @internal.
-     */
-    const NumberFormat& getCurrencyFormatInternal() const;
-
-    /**
-     * ICU use only.
-     * @internal.
-     */
-    const PluralRules &getPluralRules() const;
-
-    /**
-     * ICU use only.
-     * @internal.
-     */
-    Locale getLocale(UErrorCode &status) const;
-
-    /**
-     * ICU use only.
-     * @internal.
-     */
-    const char *getLocaleID(UErrorCode &status) const;
-
-#endif /* U_HIDE_INTERNAL_API */
-
- private:
-    const MeasureFormatCacheData *cache;
-    const SharedNumberFormat *numberFormat;
-    const SharedPluralRules *pluralRules;
-    UMeasureFormatWidth fWidth;
-
-    // Declared outside of MeasureFormatSharedData because ListFormatter
-    // objects are relatively cheap to copy; therefore, they don't need to be
-    // shared across instances.
-    ListFormatter *listFormatter;
-
-    UnicodeString &formatMeasure(
-        const Measure &measure,
-        const NumberFormat &nf,
-        UnicodeString &appendTo,
-        FieldPosition &pos,
-        UErrorCode &status) const;
-
-    UnicodeString &formatMeasuresSlowTrack(
-        const Measure *measures,
-        int32_t measureCount,
-        UnicodeString& appendTo,
-        FieldPosition& pos,
-        UErrorCode& status) const;
-
-    UnicodeString &formatNumeric(
-        const Formattable *hms,  // always length 3: [0] is hour; [1] is
-                                 // minute; [2] is second.
-        int32_t bitMap,   // 1=hour set, 2=minute set, 4=second set
-        UnicodeString &appendTo,
-        UErrorCode &status) const;
-};
-
-U_NAMESPACE_END
-
-#endif // #if !UCONFIG_NO_FORMATTING
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif // #ifndef MEASUREFORMAT_H
diff --git a/libs/icui18n/unicode/scientificnumberformatter.h b/libs/icui18n/unicode/scientificnumberformatter.h
deleted file mode 100644
index c5f8e996701b..000000000000
--- a/libs/icui18n/unicode/scientificnumberformatter.h
+++ /dev/null
@@ -1,222 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-* Copyright (c) 2014-2016, International Business Machines
-* Corporation and others.  All Rights Reserved.
-**********************************************************************
-*/
-#ifndef SCINUMBERFORMATTER_H
-#define SCINUMBERFORMATTER_H
-
-#include "unicode/utypes.h"
-
-#if U_SHOW_CPLUSPLUS_API
-
-#if !UCONFIG_NO_FORMATTING
-
-
-#include "unicode/unistr.h"
-
-/**
- * \file
- * \brief C++ API: Formats in scientific notation.
- */
-
-U_NAMESPACE_BEGIN
-
-class FieldPositionIterator;
-class DecimalFormatSymbols;
-class DecimalFormat;
-class Formattable;
-
-/**
- * A formatter that formats numbers in user-friendly scientific notation.
- *
- * Sample code:
- * <pre>
- * UErrorCode status = U_ZERO_ERROR;
- * LocalPointer<ScientificNumberFormatter> fmt(
- *         ScientificNumberFormatter::createMarkupInstance(
- *                 "en", "<sup>", "</sup>", status));
- * if (U_FAILURE(status)) {
- *     return;
- * }
- * UnicodeString appendTo;
- * // appendTo = "1.23456x10<sup>-78</sup>"
- * fmt->format(1.23456e-78, appendTo, status);
- * </pre>
- *
- * @stable ICU 55
- */
-class U_I18N_API ScientificNumberFormatter : public UObject {
-public:
-
-    /**
-     * Creates a ScientificNumberFormatter instance that uses
-     * superscript characters for exponents.
-     * @param fmtToAdopt The DecimalFormat which must be configured for
-     *   scientific notation.
-     * @param status error returned here.
-     * @return The new ScientificNumberFormatter instance.
-     *
-     * @stable ICU 55
-     */
-    static ScientificNumberFormatter *createSuperscriptInstance(
-            DecimalFormat *fmtToAdopt, UErrorCode &status);
-
-    /**
-     * Creates a ScientificNumberFormatter instance that uses
-     * superscript characters for exponents for this locale.
-     * @param locale The locale
-     * @param status error returned here.
-     * @return The ScientificNumberFormatter instance.
-     *
-     * @stable ICU 55
-     */
-    static ScientificNumberFormatter *createSuperscriptInstance(
-            const Locale &locale, UErrorCode &status);
-
-
-    /**
-     * Creates a ScientificNumberFormatter instance that uses
-     * markup for exponents.
-     * @param fmtToAdopt The DecimalFormat which must be configured for
-     *   scientific notation.
-     * @param beginMarkup the markup to start superscript.
-     * @param endMarkup the markup to end superscript.
-     * @param status error returned here.
-     * @return The new ScientificNumberFormatter instance.
-     *
-     * @stable ICU 55
-     */
-    static ScientificNumberFormatter *createMarkupInstance(
-            DecimalFormat *fmtToAdopt,
-            const UnicodeString &beginMarkup,
-            const UnicodeString &endMarkup,
-            UErrorCode &status);
-
-    /**
-     * Creates a ScientificNumberFormatter instance that uses
-     * markup for exponents for this locale.
-     * @param locale The locale
-     * @param beginMarkup the markup to start superscript.
-     * @param endMarkup the markup to end superscript.
-     * @param status error returned here.
-     * @return The ScientificNumberFormatter instance.
-     *
-     * @stable ICU 55
-     */
-    static ScientificNumberFormatter *createMarkupInstance(
-            const Locale &locale,
-            const UnicodeString &beginMarkup,
-            const UnicodeString &endMarkup,
-            UErrorCode &status);
-
-
-    /**
-     * Returns a copy of this object. Caller must free returned copy.
-     * @stable ICU 55
-     */
-    ScientificNumberFormatter *clone() const {
-        return new ScientificNumberFormatter(*this);
-    }
-
-    /**
-     * Destructor.
-     * @stable ICU 55
-     */
-    virtual ~ScientificNumberFormatter();
-
-    /**
-     * Formats a number into user friendly scientific notation.
-     *
-     * @param number the number to format.
-     * @param appendTo formatted string appended here.
-     * @param status any error returned here.
-     * @return appendTo
-     *
-     * @stable ICU 55
-     */
-    UnicodeString &format(
-            const Formattable &number,
-            UnicodeString &appendTo,
-            UErrorCode &status) const;
- private:
-    class U_I18N_API Style : public UObject {
-    public:
-        virtual Style *clone() const = 0;
-    protected:
-        virtual UnicodeString &format(
-                const UnicodeString &original,
-                FieldPositionIterator &fpi,
-                const UnicodeString &preExponent,
-                UnicodeString &appendTo,
-                UErrorCode &status) const = 0;
-    private:
-        friend class ScientificNumberFormatter;
-    };
-
-    class U_I18N_API SuperscriptStyle : public Style {
-    public:
-        virtual SuperscriptStyle *clone() const override;
-    protected:
-        virtual UnicodeString &format(
-                const UnicodeString &original,
-                FieldPositionIterator &fpi,
-                const UnicodeString &preExponent,
-                UnicodeString &appendTo,
-                UErrorCode &status) const override;
-    };
-
-    class U_I18N_API MarkupStyle : public Style {
-    public:
-        MarkupStyle(
-                const UnicodeString &beginMarkup,
-                const UnicodeString &endMarkup)
-                : Style(),
-                  fBeginMarkup(beginMarkup),
-                  fEndMarkup(endMarkup) { }
-        virtual MarkupStyle *clone() const override;
-    protected:
-        virtual UnicodeString &format(
-                const UnicodeString &original,
-                FieldPositionIterator &fpi,
-                const UnicodeString &preExponent,
-                UnicodeString &appendTo,
-                UErrorCode &status) const override;
-    private:
-        UnicodeString fBeginMarkup;
-        UnicodeString fEndMarkup;
-    };
-
-    ScientificNumberFormatter(
-            DecimalFormat *fmtToAdopt,
-            Style *styleToAdopt,
-            UErrorCode &status);
-
-    ScientificNumberFormatter(const ScientificNumberFormatter &other);
-    ScientificNumberFormatter &operator=(const ScientificNumberFormatter &) = delete;
-
-    static void getPreExponent(
-            const DecimalFormatSymbols &dfs, UnicodeString &preExponent);
-
-    static ScientificNumberFormatter *createInstance(
-            DecimalFormat *fmtToAdopt,
-            Style *styleToAdopt,
-            UErrorCode &status);
-
-    UnicodeString fPreExponent;
-    DecimalFormat *fDecimalFormat;
-    Style *fStyle;
-
-};
-
-U_NAMESPACE_END
-
-
-#endif /* !UCONFIG_NO_FORMATTING */
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif
diff --git a/libs/icui18n/unicode/stsearch.h b/libs/icui18n/unicode/stsearch.h
deleted file mode 100644
index aed60c4fc21b..000000000000
--- a/libs/icui18n/unicode/stsearch.h
+++ /dev/null
@@ -1,508 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-**********************************************************************
-*   Copyright (C) 2001-2014 IBM and others. All rights reserved.
-**********************************************************************
-*   Date        Name        Description
-*  03/22/2000   helena      Creation.
-**********************************************************************
-*/
-
-#ifndef STSEARCH_H
-#define STSEARCH_H
-
-#include "unicode/utypes.h"
-
-#if U_SHOW_CPLUSPLUS_API
-
-/**
- * \file
- * \brief C++ API: Service for searching text based on RuleBasedCollator.
- */
-
-#if !UCONFIG_NO_COLLATION && !UCONFIG_NO_BREAK_ITERATION
-
-#include "unicode/tblcoll.h"
-#include "unicode/coleitr.h"
-#include "unicode/search.h"
-
-U_NAMESPACE_BEGIN
-
-/**
- *
- * <tt>StringSearch</tt> is a <tt>SearchIterator</tt> that provides
- * language-sensitive text searching based on the comparison rules defined
- * in a {@link RuleBasedCollator} object.
- * StringSearch ensures that language eccentricity can be
- * handled, e.g. for the German collator, characters &szlig; and SS will be matched
- * if case is chosen to be ignored.
- * See the <a href="https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/collation/ICU_collation_design.htm">
- * "ICU Collation Design Document"</a> for more information.
- * <p>
- * There are 2 match options for selection:<br>
- * Let S' be the sub-string of a text string S between the offsets start and
- * end [start, end].
- * <br>
- * A pattern string P matches a text string S at the offsets [start, end]
- * if
- * <pre>
- * option 1. Some canonical equivalent of P matches some canonical equivalent
- *           of S'
- * option 2. P matches S' and if P starts or ends with a combining mark,
- *           there exists no non-ignorable combining mark before or after S?
- *           in S respectively.
- * </pre>
- * Option 2. will be the default.
- * <p>
- * This search has APIs similar to that of other text iteration mechanisms
- * such as the break iterators in <tt>BreakIterator</tt>. Using these
- * APIs, it is easy to scan through text looking for all occurrences of
- * a given pattern. This search iterator allows changing of direction by
- * calling a <tt>reset</tt> followed by a <tt>next</tt> or <tt>previous</tt>.
- * Though a direction change can occur without calling <tt>reset</tt> first,
- * this operation comes with some speed penalty.
- * Match results in the forward direction will match the result matches in
- * the backwards direction in the reverse order
- * <p>
- * <tt>SearchIterator</tt> provides APIs to specify the starting position
- * within the text string to be searched, e.g. <tt>setOffset</tt>,
- * <tt>preceding</tt> and <tt>following</tt>. Since the
- * starting position will be set as it is specified, please take note that
- * there are some danger points which the search may render incorrect
- * results:
- * <ul>
- * <li> The midst of a substring that requires normalization.
- * <li> If the following match is to be found, the position should not be the
- *      second character which requires to be swapped with the preceding
- *      character. Vice versa, if the preceding match is to be found,
- *      position to search from should not be the first character which
- *      requires to be swapped with the next character. E.g certain Thai and
- *      Lao characters require swapping.
- * <li> If a following pattern match is to be found, any position within a
- *      contracting sequence except the first will fail. Vice versa if a
- *      preceding pattern match is to be found, a invalid starting point
- *      would be any character within a contracting sequence except the last.
- * </ul>
- * <p>
- * A <tt>BreakIterator</tt> can be used if only matches at logical breaks are desired.
- * Using a <tt>BreakIterator</tt> will only give you results that exactly matches the
- * boundaries given by the breakiterator. For instance the pattern "e" will
- * not be found in the string "\u00e9" if a character break iterator is used.
- * <p>
- * Options are provided to handle overlapping matches.
- * E.g. In English, overlapping matches produces the result 0 and 2
- * for the pattern "abab" in the text "ababab", where else mutually
- * exclusive matches only produce the result of 0.
- * <p>
- * Though collator attributes will be taken into consideration while
- * performing matches, there are no APIs here for setting and getting the
- * attributes. These attributes can be set by getting the collator
- * from <tt>getCollator</tt> and using the APIs in <tt>coll.h</tt>.
- * Lastly to update <tt>StringSearch</tt> to the new collator attributes,
- * <tt>reset</tt> has to be called.
- * <p>
- * Restriction: <br>
- * Currently there are no composite characters that consists of a
- * character with combining class > 0 before a character with combining
- * class == 0. However, if such a character exists in the future,
- * <tt>StringSearch</tt> does not guarantee the results for option 1.
- * <p>
- * Consult the <tt>SearchIterator</tt> documentation for information on
- * and examples of how to use instances of this class to implement text
- * searching.
- * <pre><code>
- * UnicodeString target("The quick brown fox jumps over the lazy dog.");
- * UnicodeString pattern("fox");
- *
- * UErrorCode      error = U_ZERO_ERROR;
- * StringSearch iter(pattern, target, Locale::getUS(), NULL, status);
- * for (int pos = iter.first(error);
- *      pos != USEARCH_DONE;
- *      pos = iter.next(error))
- * {
- *     printf("Found match at %d pos, length is %d\n", pos, iter.getMatchedLength());
- * }
- * </code></pre>
- * <p>
- * Note, <tt>StringSearch</tt> is not to be subclassed.
- * </p>
- * @see SearchIterator
- * @see RuleBasedCollator
- * @since ICU 2.0
- */
-
-class U_I18N_API StringSearch U_FINAL : public SearchIterator
-{
-public:
-
-    // public constructors and destructors --------------------------------
-
-    /**
-     * Creating a <tt>StringSearch</tt> instance using the argument locale
-     * language rule set. A collator will be created in the process, which
-     * will be owned by this instance and will be deleted during
-     * destruction
-     * @param pattern The text for which this object will search.
-     * @param text    The text in which to search for the pattern.
-     * @param locale  A locale which defines the language-sensitive
-     *                comparison rules used to determine whether text in the
-     *                pattern and target matches.
-     * @param breakiter A <tt>BreakIterator</tt> object used to constrain
-     *                the matches that are found. Matches whose start and end
-     *                indices in the target text are not boundaries as
-     *                determined by the <tt>BreakIterator</tt> are
-     *                ignored. If this behavior is not desired,
-     *                <tt>NULL</tt> can be passed in instead.
-     * @param status  for errors if any. If pattern or text is NULL, or if
-     *               either the length of pattern or text is 0 then an
-     *               U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    StringSearch(const UnicodeString &pattern, const UnicodeString &text,
-                 const Locale        &locale,
-                       BreakIterator *breakiter,
-                       UErrorCode    &status);
-
-    /**
-     * Creating a <tt>StringSearch</tt> instance using the argument collator
-     * language rule set. Note, user retains the ownership of this collator,
-     * it does not get destroyed during this instance's destruction.
-     * @param pattern The text for which this object will search.
-     * @param text    The text in which to search for the pattern.
-     * @param coll    A <tt>RuleBasedCollator</tt> object which defines
-     *                the language-sensitive comparison rules used to
-     *                determine whether text in the pattern and target
-     *                matches. User is responsible for the clearing of this
-     *                object.
-     * @param breakiter A <tt>BreakIterator</tt> object used to constrain
-     *                the matches that are found. Matches whose start and end
-     *                indices in the target text are not boundaries as
-     *                determined by the <tt>BreakIterator</tt> are
-     *                ignored. If this behavior is not desired,
-     *                <tt>NULL</tt> can be passed in instead.
-     * @param status for errors if any. If either the length of pattern or
-     *               text is 0 then an U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    StringSearch(const UnicodeString     &pattern,
-                 const UnicodeString     &text,
-                       RuleBasedCollator *coll,
-                       BreakIterator     *breakiter,
-                       UErrorCode        &status);
-
-    /**
-     * Creating a <tt>StringSearch</tt> instance using the argument locale
-     * language rule set. A collator will be created in the process, which
-     * will be owned by this instance and will be deleted during
-     * destruction
-     * <p>
-     * Note: No parsing of the text within the <tt>CharacterIterator</tt>
-     * will be done during searching for this version. The block of text
-     * in <tt>CharacterIterator</tt> will be used as it is.
-     * @param pattern The text for which this object will search.
-     * @param text    The text iterator in which to search for the pattern.
-     * @param locale  A locale which defines the language-sensitive
-     *                comparison rules used to determine whether text in the
-     *                pattern and target matches. User is responsible for
-     *                the clearing of this object.
-     * @param breakiter A <tt>BreakIterator</tt> object used to constrain
-     *                the matches that are found. Matches whose start and end
-     *                indices in the target text are not boundaries as
-     *                determined by the <tt>BreakIterator</tt> are
-     *                ignored. If this behavior is not desired,
-     *                <tt>NULL</tt> can be passed in instead.
-     * @param status for errors if any. If either the length of pattern or
-     *               text is 0 then an U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    StringSearch(const UnicodeString &pattern, CharacterIterator &text,
-                 const Locale        &locale,
-                       BreakIterator *breakiter,
-                       UErrorCode    &status);
-
-    /**
-     * Creating a <tt>StringSearch</tt> instance using the argument collator
-     * language rule set. Note, user retains the ownership of this collator,
-     * it does not get destroyed during this instance's destruction.
-     * <p>
-     * Note: No parsing of the text within the <tt>CharacterIterator</tt>
-     * will be done during searching for this version. The block of text
-     * in <tt>CharacterIterator</tt> will be used as it is.
-     * @param pattern The text for which this object will search.
-     * @param text    The text in which to search for the pattern.
-     * @param coll    A <tt>RuleBasedCollator</tt> object which defines
-     *                the language-sensitive comparison rules used to
-     *                determine whether text in the pattern and target
-     *                matches. User is responsible for the clearing of this
-     *                object.
-     * @param breakiter A <tt>BreakIterator</tt> object used to constrain
-     *                the matches that are found. Matches whose start and end
-     *                indices in the target text are not boundaries as
-     *                determined by the <tt>BreakIterator</tt> are
-     *                ignored. If this behavior is not desired,
-     *                <tt>NULL</tt> can be passed in instead.
-     * @param status for errors if any. If either the length of pattern or
-     *               text is 0 then an U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    StringSearch(const UnicodeString     &pattern, CharacterIterator &text,
-                       RuleBasedCollator *coll,
-                       BreakIterator     *breakiter,
-                       UErrorCode        &status);
-
-    /**
-     * Copy constructor that creates a StringSearch instance with the same
-     * behavior, and iterating over the same text.
-     * @param that StringSearch instance to be copied.
-     * @stable ICU 2.0
-     */
-    StringSearch(const StringSearch &that);
-
-    /**
-    * Destructor. Cleans up the search iterator data struct.
-    * If a collator is created in the constructor, it will be destroyed here.
-    * @stable ICU 2.0
-    */
-    virtual ~StringSearch(void);
-
-    /**
-     * Clone this object.
-     * Clones can be used concurrently in multiple threads.
-     * If an error occurs, then NULL is returned.
-     * The caller must delete the clone.
-     *
-     * @return a clone of this object
-     *
-     * @see getDynamicClassID
-     * @stable ICU 2.8
-     */
-    StringSearch *clone() const;
-
-    // operator overloading ---------------------------------------------
-
-    /**
-     * Assignment operator. Sets this iterator to have the same behavior,
-     * and iterate over the same text, as the one passed in.
-     * @param that instance to be copied.
-     * @stable ICU 2.0
-     */
-    StringSearch & operator=(const StringSearch &that);
-
-    /**
-     * Equality operator.
-     * @param that instance to be compared.
-     * @return true if both instances have the same attributes,
-     *         breakiterators, collators and iterate over the same text
-     *         while looking for the same pattern.
-     * @stable ICU 2.0
-     */
-    virtual bool operator==(const SearchIterator &that) const override;
-
-    // public get and set methods ----------------------------------------
-
-    /**
-     * Sets the index to point to the given position, and clears any state
-     * that's affected.
-     * <p>
-     * This method takes the argument index and sets the position in the text
-     * string accordingly without checking if the index is pointing to a
-     * valid starting point to begin searching.
-     * @param position within the text to be set. If position is less
-     *          than or greater than the text range for searching,
-     *          an U_INDEX_OUTOFBOUNDS_ERROR will be returned
-     * @param status for errors if it occurs
-     * @stable ICU 2.0
-     */
-    virtual void setOffset(int32_t position, UErrorCode &status) override;
-
-    /**
-     * Return the current index in the text being searched.
-     * If the iteration has gone past the end of the text
-     * (or past the beginning for a backwards search), USEARCH_DONE
-     * is returned.
-     * @return current index in the text being searched.
-     * @stable ICU 2.0
-     */
-    virtual int32_t getOffset(void) const override;
-
-    /**
-     * Set the target text to be searched.
-     * Text iteration will hence begin at the start of the text string.
-     * This method is
-     * useful if you want to re-use an iterator to search for the same
-     * pattern within a different body of text.
-     * @param text text string to be searched
-     * @param status for errors if any. If the text length is 0 then an
-     *        U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    virtual void setText(const UnicodeString &text, UErrorCode &status) override;
-
-    /**
-     * Set the target text to be searched.
-     * Text iteration will hence begin at the start of the text string.
-     * This method is
-     * useful if you want to re-use an iterator to search for the same
-     * pattern within a different body of text.
-     * Note: No parsing of the text within the <tt>CharacterIterator</tt>
-     * will be done during searching for this version. The block of text
-     * in <tt>CharacterIterator</tt> will be used as it is.
-     * @param text text string to be searched
-     * @param status for errors if any. If the text length is 0 then an
-     *        U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    virtual void setText(CharacterIterator &text, UErrorCode &status) override;
-
-    /**
-     * Gets the collator used for the language rules.
-     * <p>
-     * Caller may modify but <b>must not</b> delete the <tt>RuleBasedCollator</tt>!
-     * Modifications to this collator will affect the original collator passed in to
-     * the <tt>StringSearch></tt> constructor or to setCollator, if any.
-     * @return collator used for string search
-     * @stable ICU 2.0
-     */
-    RuleBasedCollator * getCollator() const;
-
-    /**
-     * Sets the collator used for the language rules. User retains the
-     * ownership of this collator, thus the responsibility of deletion lies
-     * with the user. The iterator's position will not be changed by this method.
-     * @param coll    collator
-     * @param status  for errors if any
-     * @stable ICU 2.0
-     */
-    void setCollator(RuleBasedCollator *coll, UErrorCode &status);
-
-    /**
-     * Sets the pattern used for matching.
-     * The iterator's position will not be changed by this method.
-     * @param pattern search pattern to be found
-     * @param status for errors if any. If the pattern length is 0 then an
-     *               U_ILLEGAL_ARGUMENT_ERROR is returned.
-     * @stable ICU 2.0
-     */
-    void setPattern(const UnicodeString &pattern, UErrorCode &status);
-
-    /**
-     * Gets the search pattern.
-     * @return pattern used for matching
-     * @stable ICU 2.0
-     */
-    const UnicodeString & getPattern() const;
-
-    // public methods ----------------------------------------------------
-
-    /**
-     * Reset the iteration.
-     * Search will begin at the start of the text string if a forward
-     * iteration is initiated before a backwards iteration. Otherwise if
-     * a backwards iteration is initiated before a forwards iteration, the
-     * search will begin at the end of the text string.
-     * @stable ICU 2.0
-     */
-    virtual void reset() override;
-
-    /**
-     * Returns a copy of StringSearch with the same behavior, and
-     * iterating over the same text, as this one. Note that all data will be
-     * replicated, except for the user-specified collator and the
-     * breakiterator.
-     * @return cloned object
-     * @stable ICU 2.0
-     */
-    virtual StringSearch * safeClone() const override;
-
-    /**
-     * ICU "poor man's RTTI", returns a UClassID for the actual class.
-     *
-     * @stable ICU 2.2
-     */
-    virtual UClassID getDynamicClassID() const override;
-
-    /**
-     * ICU "poor man's RTTI", returns a UClassID for this class.
-     *
-     * @stable ICU 2.2
-     */
-    static UClassID U_EXPORT2 getStaticClassID();
-
-protected:
-
-    // protected method -------------------------------------------------
-
-    /**
-     * Search forward for matching text, starting at a given location.
-     * Clients should not call this method directly; instead they should
-     * call {@link SearchIterator#next }.
-     * <p>
-     * If a match is found, this method returns the index at which the match
-     * starts and calls {@link SearchIterator#setMatchLength } with the number
-     * of characters in the target text that make up the match. If no match
-     * is found, the method returns <tt>USEARCH_DONE</tt>.
-     * <p>
-     * The <tt>StringSearch</tt> is adjusted so that its current index
-     * (as returned by {@link #getOffset }) is the match position if one was
-     * found.
-     * If a match is not found, <tt>USEARCH_DONE</tt> will be returned and
-     * the <tt>StringSearch</tt> will be adjusted to the index USEARCH_DONE.
-     * @param position The index in the target text at which the search
-     *                 starts
-     * @param status for errors if any occurs
-     * @return The index at which the matched text in the target starts, or
-     *         USEARCH_DONE if no match was found.
-     * @stable ICU 2.0
-     */
-    virtual int32_t handleNext(int32_t position, UErrorCode &status) override;
-
-    /**
-     * Search backward for matching text, starting at a given location.
-     * Clients should not call this method directly; instead they should call
-     * <tt>SearchIterator.previous()</tt>, which this method overrides.
-     * <p>
-     * If a match is found, this method returns the index at which the match
-     * starts and calls {@link SearchIterator#setMatchLength } with the number
-     * of characters in the target text that make up the match. If no match
-     * is found, the method returns <tt>USEARCH_DONE</tt>.
-     * <p>
-     * The <tt>StringSearch</tt> is adjusted so that its current index
-     * (as returned by {@link #getOffset }) is the match position if one was
-     * found.
-     * If a match is not found, <tt>USEARCH_DONE</tt> will be returned and
-     * the <tt>StringSearch</tt> will be adjusted to the index USEARCH_DONE.
-     * @param position The index in the target text at which the search
-     *                 starts.
-     * @param status for errors if any occurs
-     * @return The index at which the matched text in the target starts, or
-     *         USEARCH_DONE if no match was found.
-     * @stable ICU 2.0
-     */
-    virtual int32_t handlePrev(int32_t position, UErrorCode &status) override;
-
-private :
-    StringSearch() = delete; // default constructor not implemented
-
-    // private data members ----------------------------------------------
-
-    /**
-    * Pattern text
-    * @stable ICU 2.0
-    */
-    UnicodeString      m_pattern_;
-    /**
-    * String search struct data
-    * @stable ICU 2.0
-    */
-    UStringSearch     *m_strsrch_;
-
-};
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_COLLATION */
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif
diff --git a/libs/icui18n/unicode/tmunit.h b/libs/icui18n/unicode/tmunit.h
deleted file mode 100644
index 24abb49f1982..000000000000
--- a/libs/icui18n/unicode/tmunit.h
+++ /dev/null
@@ -1,142 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- * Copyright (C) 2009-2016, International Business Machines Corporation,       *
- * Google, and others. All Rights Reserved.                                    *
- *******************************************************************************
- */
-
-#ifndef __TMUNIT_H__
-#define __TMUNIT_H__
-
-
-/**
- * \file
- * \brief C++ API: time unit object
- */
-
-#include "unicode/utypes.h"
-
-#if U_SHOW_CPLUSPLUS_API
-
-#include "unicode/measunit.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-U_NAMESPACE_BEGIN
-
-/**
- * Measurement unit for time units.
- * @see TimeUnitAmount
- * @see TimeUnit
- * @stable ICU 4.2
- */
-class U_I18N_API TimeUnit: public MeasureUnit {
-public:
-    /**
-     * Constants for all the time units we supported.
-     * @stable ICU 4.2
-     */
-    enum UTimeUnitFields {
-        UTIMEUNIT_YEAR,
-        UTIMEUNIT_MONTH,
-        UTIMEUNIT_DAY,
-        UTIMEUNIT_WEEK,
-        UTIMEUNIT_HOUR,
-        UTIMEUNIT_MINUTE,
-        UTIMEUNIT_SECOND,
-#ifndef U_HIDE_DEPRECATED_API
-        /**
-         * One more than the highest normal UTimeUnitFields value.
-         * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
-         */
-        UTIMEUNIT_FIELD_COUNT
-#endif  // U_HIDE_DEPRECATED_API
-    };
-
-    /**
-     * Create Instance.
-     * @param timeUnitField  time unit field based on which the instance
-     *                       is created.
-     * @param status         input-output error code.
-     *                       If the timeUnitField is invalid,
-     *                       then this will be set to U_ILLEGAL_ARGUMENT_ERROR.
-     * @return               a TimeUnit instance
-     * @stable ICU 4.2
-     */
-    static TimeUnit* U_EXPORT2 createInstance(UTimeUnitFields timeUnitField,
-                                              UErrorCode& status);
-
-
-    /**
-     * Override clone.
-     * @stable ICU 4.2
-     */
-    virtual TimeUnit* clone() const override;
-
-    /**
-     * Copy operator.
-     * @stable ICU 4.2
-     */
-    TimeUnit(const TimeUnit& other);
-
-    /**
-     * Assignment operator.
-     * @stable ICU 4.2
-     */
-    TimeUnit& operator=(const TimeUnit& other);
-
-    /**
-     * Returns a unique class ID for this object POLYMORPHICALLY.
-     * This method implements a simple form of RTTI used by ICU.
-     * @return The class ID for this object. All objects of a given
-     * class have the same class ID.  Objects of other classes have
-     * different class IDs.
-     * @stable ICU 4.2
-     */
-    virtual UClassID getDynamicClassID() const override;
-
-    /**
-     * Returns the class ID for this class. This is used to compare to
-     * the return value of getDynamicClassID().
-     * @return The class ID for all objects of this class.
-     * @stable ICU 4.2
-     */
-    static UClassID U_EXPORT2 getStaticClassID();
-
-
-    /**
-     * Get time unit field.
-     * @return time unit field.
-     * @stable ICU 4.2
-     */
-    UTimeUnitFields getTimeUnitField() const;
-
-    /**
-     * Destructor.
-     * @stable ICU 4.2
-     */
-    virtual ~TimeUnit();
-
-private:
-    UTimeUnitFields fTimeUnitField;
-
-    /**
-     * Constructor
-     * @internal (private)
-     */
-    TimeUnit(UTimeUnitFields timeUnitField);
-
-};
-
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_FORMATTING */
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif // __TMUNIT_H__
-//eof
-//
diff --git a/libs/icui18n/unicode/tmutamt.h b/libs/icui18n/unicode/tmutamt.h
deleted file mode 100644
index 9bc2b57f23c4..000000000000
--- a/libs/icui18n/unicode/tmutamt.h
+++ /dev/null
@@ -1,176 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- * Copyright (C) 2009-2010, Google, International Business Machines Corporation and *
- * others. All Rights Reserved.                                                *
- *******************************************************************************
- */
-
-#ifndef __TMUTAMT_H__
-#define __TMUTAMT_H__
-
-
-/**
- * \file
- * \brief C++ API: time unit amount object.
- */
-
-#include "unicode/utypes.h"
-
-#if U_SHOW_CPLUSPLUS_API
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/measure.h"
-#include "unicode/tmunit.h"
-
-U_NAMESPACE_BEGIN
-
-
-/**
- * Express a duration as a time unit and number. Patterned after Currency.
- * @see TimeUnitAmount
- * @see TimeUnitFormat
- * @stable ICU 4.2
- */
-class U_I18N_API TimeUnitAmount: public Measure {
-public:
-    /**
-     * Construct TimeUnitAmount object with the given number and the
-     * given time unit.
-     * @param number        a numeric object; number.isNumeric() must be true
-     * @param timeUnitField the time unit field of a time unit
-     * @param status        the input-output error code.
-     *                      If the number is not numeric or the timeUnitField
-     *                      is not valid,
-     *                      then this will be set to a failing value:
-     *                      U_ILLEGAL_ARGUMENT_ERROR.
-     * @stable ICU 4.2
-     */
-    TimeUnitAmount(const Formattable& number,
-                   TimeUnit::UTimeUnitFields timeUnitField,
-                   UErrorCode& status);
-
-    /**
-     * Construct TimeUnitAmount object with the given numeric amount and the
-     * given time unit.
-     * @param amount        a numeric amount.
-     * @param timeUnitField the time unit field on which a time unit amount
-     *                      object will be created.
-     * @param status        the input-output error code.
-     *                      If the timeUnitField is not valid,
-     *                      then this will be set to a failing value:
-     *                      U_ILLEGAL_ARGUMENT_ERROR.
-     * @stable ICU 4.2
-     */
-    TimeUnitAmount(double amount, TimeUnit::UTimeUnitFields timeUnitField,
-                   UErrorCode& status);
-
-
-    /**
-     * Copy constructor
-     * @stable ICU 4.2
-     */
-    TimeUnitAmount(const TimeUnitAmount& other);
-
-
-    /**
-     * Assignment operator
-     * @stable ICU 4.2
-     */
-    TimeUnitAmount& operator=(const TimeUnitAmount& other);
-
-
-    /**
-     * Clone.
-     * @return a polymorphic clone of this object. The result will have the same               class as returned by getDynamicClassID().
-     * @stable ICU 4.2
-     */
-    virtual TimeUnitAmount* clone() const override;
-
-
-    /**
-     * Destructor
-     * @stable ICU 4.2
-     */
-    virtual ~TimeUnitAmount();
-
-
-    /**
-     * Equality operator.
-     * @param other  the object to compare to.
-     * @return       true if this object is equal to the given object.
-     * @stable ICU 4.2
-     */
-    virtual bool operator==(const UObject& other) const;
-
-
-    /**
-     * Not-equality operator.
-     * @param other  the object to compare to.
-     * @return       true if this object is not equal to the given object.
-     * @stable ICU 4.2
-     */
-    bool operator!=(const UObject& other) const;
-
-
-    /**
-     * Return the class ID for this class. This is useful only for comparing to
-     * a return value from getDynamicClassID(). For example:
-     * <pre>
-     * .   Base* polymorphic_pointer = createPolymorphicObject();
-     * .   if (polymorphic_pointer->getDynamicClassID() ==
-     * .       erived::getStaticClassID()) ...
-     * </pre>
-     * @return          The class ID for all objects of this class.
-     * @stable ICU 4.2
-     */
-    static UClassID U_EXPORT2 getStaticClassID(void);
-
-
-    /**
-     * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This
-     * method is to implement a simple version of RTTI, since not all C++
-     * compilers support genuine RTTI. Polymorphic operator==() and clone()
-     * methods call this method.
-     *
-     * @return          The class ID for this object. All objects of a
-     *                  given class have the same class ID.  Objects of
-     *                  other classes have different class IDs.
-     * @stable ICU 4.2
-     */
-    virtual UClassID getDynamicClassID(void) const override;
-
-
-    /**
-     * Get the time unit.
-     * @return time unit object.
-     * @stable ICU 4.2
-     */
-    const TimeUnit& getTimeUnit() const;
-
-    /**
-     * Get the time unit field value.
-     * @return time unit field value.
-     * @stable ICU 4.2
-     */
-    TimeUnit::UTimeUnitFields getTimeUnitField() const;
-};
-
-
-
-inline bool
-TimeUnitAmount::operator!=(const UObject& other) const {
-    return !operator==(other);
-}
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_FORMATTING */
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif // __TMUTAMT_H__
-//eof
-//
diff --git a/libs/icui18n/unicode/tmutfmt.h b/libs/icui18n/unicode/tmutfmt.h
deleted file mode 100644
index 02e0563a010e..000000000000
--- a/libs/icui18n/unicode/tmutfmt.h
+++ /dev/null
@@ -1,238 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
- *******************************************************************************
- * Copyright (C) 2008-2014, Google, International Business Machines Corporation
- * and others. All Rights Reserved.
- *******************************************************************************
- */
-
-#ifndef __TMUTFMT_H__
-#define __TMUTFMT_H__
-
-#include "unicode/utypes.h"
-
-/**
- * \file
- * \brief C++ API: Format and parse duration in single time unit
- */
-
-
-#if U_SHOW_CPLUSPLUS_API
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/unistr.h"
-#include "unicode/tmunit.h"
-#include "unicode/tmutamt.h"
-#include "unicode/measfmt.h"
-#include "unicode/numfmt.h"
-#include "unicode/plurrule.h"
-
-#ifndef U_HIDE_DEPRECATED_API
-
-/**
- * Constants for various styles.
- * There are 2 styles: full name and abbreviated name.
- * For example, for English, the full name for hour duration is "3 hours",
- * and the abbreviated name is "3 hrs".
- * @deprecated ICU 53 Use MeasureFormat and UMeasureFormatWidth instead.
- */
-enum UTimeUnitFormatStyle {
-    /** @deprecated ICU 53 */
-    UTMUTFMT_FULL_STYLE,
-    /** @deprecated ICU 53 */
-    UTMUTFMT_ABBREVIATED_STYLE,
-    /** @deprecated ICU 53 */
-    UTMUTFMT_FORMAT_STYLE_COUNT
-};
-typedef enum UTimeUnitFormatStyle UTimeUnitFormatStyle; /**< @deprecated ICU 53 */
-
-
-U_NAMESPACE_BEGIN
-
-class Hashtable;
-class UVector;
-
-struct TimeUnitFormatReadSink;
-
-/**
- * Format or parse a TimeUnitAmount, using plural rules for the units where available.
- *
- * <P>
- * Code Sample:
- * <pre>
- *   // create time unit amount instance - a combination of Number and time unit
- *   UErrorCode status = U_ZERO_ERROR;
- *   TimeUnitAmount* source = new TimeUnitAmount(2, TimeUnit::UTIMEUNIT_YEAR, status);
- *   // create time unit format instance
- *   TimeUnitFormat* format = new TimeUnitFormat(Locale("en"), status);
- *   // format a time unit amount
- *   UnicodeString formatted;
- *   Formattable formattable;
- *   if (U_SUCCESS(status)) {
- *       formattable.adoptObject(source);
- *       formatted = ((Format*)format)->format(formattable, formatted, status);
- *       Formattable result;
- *       ((Format*)format)->parseObject(formatted, result, status);
- *       if (U_SUCCESS(status)) {
- *           assert (result == formattable);
- *       }
- *   }
- * </pre>
- *
- * <P>
- * @see TimeUnitAmount
- * @see TimeUnitFormat
- * @deprecated ICU 53 Use the MeasureFormat class instead.
- */
-class U_I18N_API TimeUnitFormat: public MeasureFormat {
-public:
-
-    /**
-     * Create TimeUnitFormat with default locale, and full name style.
-     * Use setLocale and/or setFormat to modify.
-     * @deprecated ICU 53
-     */
-    TimeUnitFormat(UErrorCode& status);
-
-    /**
-     * Create TimeUnitFormat given locale, and full name style.
-     * @deprecated ICU 53
-     */
-    TimeUnitFormat(const Locale& locale, UErrorCode& status);
-
-    /**
-     * Create TimeUnitFormat given locale and style.
-     * @deprecated ICU 53
-     */
-    TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status);
-
-    /**
-     * Copy constructor.
-     * @deprecated ICU 53
-     */
-    TimeUnitFormat(const TimeUnitFormat&);
-
-    /**
-     * deconstructor
-     * @deprecated ICU 53
-     */
-    virtual ~TimeUnitFormat();
-
-    /**
-     * Clone this Format object polymorphically. The caller owns the result and
-     * should delete it when done.
-     * @return    A copy of the object.
-     * @deprecated ICU 53
-     */
-    virtual TimeUnitFormat* clone() const override;
-
-    /**
-     * Assignment operator
-     * @deprecated ICU 53
-     */
-    TimeUnitFormat& operator=(const TimeUnitFormat& other);
-
-    /**
-     * Set the locale used for formatting or parsing.
-     * @param locale  the locale to be set
-     * @param status  output param set to success/failure code on exit
-     * @deprecated ICU 53
-     */
-    void setLocale(const Locale& locale, UErrorCode& status);
-
-
-    /**
-     * Set the number format used for formatting or parsing.
-     * @param format  the number formatter to be set
-     * @param status  output param set to success/failure code on exit
-     * @deprecated ICU 53
-     */
-    void setNumberFormat(const NumberFormat& format, UErrorCode& status);
-
-    /**
-     * Parse a TimeUnitAmount.
-     * @see Format#parseObject(const UnicodeString&, Formattable&, ParsePosition&) const;
-     * @deprecated ICU 53
-     */
-    virtual void parseObject(const UnicodeString& source,
-                             Formattable& result,
-                             ParsePosition& pos) const override;
-
-    /**
-     * Return the class ID for this class. This is useful only for comparing to
-     * a return value from getDynamicClassID(). For example:
-     * <pre>
-     * .   Base* polymorphic_pointer = createPolymorphicObject();
-     * .   if (polymorphic_pointer->getDynamicClassID() ==
-     * .       erived::getStaticClassID()) ...
-     * </pre>
-     * @return          The class ID for all objects of this class.
-     * @deprecated ICU 53
-     */
-    static UClassID U_EXPORT2 getStaticClassID(void);
-
-    /**
-     * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This
-     * method is to implement a simple version of RTTI, since not all C++
-     * compilers support genuine RTTI. Polymorphic operator==() and clone()
-     * methods call this method.
-     *
-     * @return          The class ID for this object. All objects of a
-     *                  given class have the same class ID.  Objects of
-     *                  other classes have different class IDs.
-     * @deprecated ICU 53
-     */
-    virtual UClassID getDynamicClassID(void) const override;
-
-private:
-    Hashtable*    fTimeUnitToCountToPatterns[TimeUnit::UTIMEUNIT_FIELD_COUNT];
-    UTimeUnitFormatStyle fStyle;
-
-    void create(UTimeUnitFormatStyle style, UErrorCode& status);
-
-    // it might actually be simpler to make them Decimal Formats later.
-    // initialize all private data members
-    void setup(UErrorCode& status);
-
-    // initialize data member without fill in data for fTimeUnitToCountToPattern
-    void initDataMembers(UErrorCode& status);
-
-    // initialize fTimeUnitToCountToPatterns from current locale's resource.
-    void readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key, const UVector& pluralCounts,
-                               UErrorCode& status);
-
-    // check completeness of fTimeUnitToCountToPatterns against all time units,
-    // and all plural rules, fill in fallback as necessary.
-    void checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& status);
-
-    // fill in fTimeUnitToCountToPatterns from locale fall-back chain
-    void searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
-                             TimeUnit::UTimeUnitFields field, const UnicodeString&,
-                             const char*, Hashtable*, UErrorCode&);
-
-    // initialize hash table
-    Hashtable* initHash(UErrorCode& status);
-
-    // delete hash table
-    void deleteHash(Hashtable* htable);
-
-    // copy hash table
-    void copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status);
-    // get time unit name, such as "year", from time unit field enum, such as
-    // UTIMEUNIT_YEAR.
-    static const char* getTimeUnitName(TimeUnit::UTimeUnitFields field, UErrorCode& status);
-
-    friend struct TimeUnitFormatReadSink;
-};
-
-U_NAMESPACE_END
-
-#endif /* U_HIDE_DEPRECATED_API */
-#endif /* #if !UCONFIG_NO_FORMATTING */
-
-#endif /* U_SHOW_CPLUSPLUS_API */
-
-#endif // __TMUTFMT_H__
-//eof
diff --git a/libs/icui18n/vtzone.cpp b/libs/icui18n/vtzone.cpp
deleted file mode 100644
index 3035106701e2..000000000000
--- a/libs/icui18n/vtzone.cpp
+++ /dev/null
@@ -1,2605 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 2007-2016, International Business Machines Corporation and
-* others. All Rights Reserved.
-*******************************************************************************
-*/
-
-#include "utypeinfo.h"  // for 'typeid' to work
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/vtzone.h"
-#include "unicode/rbtz.h"
-#include "unicode/ucal.h"
-#include "unicode/ures.h"
-#include "cmemory.h"
-#include "uvector.h"
-#include "gregoimp.h"
-#include "uassert.h"
-
-U_NAMESPACE_BEGIN
-
-// Smybol characters used by RFC2445 VTIMEZONE
-static const UChar COLON = 0x3A; /* : */
-static const UChar SEMICOLON = 0x3B; /* ; */
-static const UChar EQUALS_SIGN = 0x3D; /* = */
-static const UChar COMMA = 0x2C; /* , */
-static const UChar PLUS = 0x2B; /* + */
-static const UChar MINUS = 0x2D; /* - */
-
-// RFC2445 VTIMEZONE tokens
-static const UChar ICAL_BEGIN_VTIMEZONE[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "BEGIN:VTIMEZONE" */
-static const UChar ICAL_END_VTIMEZONE[] = {0x45, 0x4E, 0x44, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "END:VTIMEZONE" */
-static const UChar ICAL_BEGIN[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0}; /* "BEGIN" */
-static const UChar ICAL_END[] = {0x45, 0x4E, 0x44, 0}; /* "END" */
-static const UChar ICAL_VTIMEZONE[] = {0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "VTIMEZONE" */
-static const UChar ICAL_TZID[] = {0x54, 0x5A, 0x49, 0x44, 0}; /* "TZID" */
-static const UChar ICAL_STANDARD[] = {0x53, 0x54, 0x41, 0x4E, 0x44, 0x41, 0x52, 0x44, 0}; /* "STANDARD" */
-static const UChar ICAL_DAYLIGHT[] = {0x44, 0x41, 0x59, 0x4C, 0x49, 0x47, 0x48, 0x54, 0}; /* "DAYLIGHT" */
-static const UChar ICAL_DTSTART[] = {0x44, 0x54, 0x53, 0x54, 0x41, 0x52, 0x54, 0}; /* "DTSTART" */
-static const UChar ICAL_TZOFFSETFROM[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x46, 0x52, 0x4F, 0x4D, 0}; /* "TZOFFSETFROM" */
-static const UChar ICAL_TZOFFSETTO[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x54, 0x4F, 0}; /* "TZOFFSETTO" */
-static const UChar ICAL_RDATE[] = {0x52, 0x44, 0x41, 0x54, 0x45, 0}; /* "RDATE" */
-static const UChar ICAL_RRULE[] = {0x52, 0x52, 0x55, 0x4C, 0x45, 0}; /* "RRULE" */
-static const UChar ICAL_TZNAME[] = {0x54, 0x5A, 0x4E, 0x41, 0x4D, 0x45, 0}; /* "TZNAME" */
-static const UChar ICAL_TZURL[] = {0x54, 0x5A, 0x55, 0x52, 0x4C, 0}; /* "TZURL" */
-static const UChar ICAL_LASTMOD[] = {0x4C, 0x41, 0x53, 0x54, 0x2D, 0x4D, 0x4F, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0}; /* "LAST-MODIFIED" */
-
-static const UChar ICAL_FREQ[] = {0x46, 0x52, 0x45, 0x51, 0}; /* "FREQ" */
-static const UChar ICAL_UNTIL[] = {0x55, 0x4E, 0x54, 0x49, 0x4C, 0}; /* "UNTIL" */
-static const UChar ICAL_YEARLY[] = {0x59, 0x45, 0x41, 0x52, 0x4C, 0x59, 0}; /* "YEARLY" */
-static const UChar ICAL_BYMONTH[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0}; /* "BYMONTH" */
-static const UChar ICAL_BYDAY[] = {0x42, 0x59, 0x44, 0x41, 0x59, 0}; /* "BYDAY" */
-static const UChar ICAL_BYMONTHDAY[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0x44, 0x41, 0x59, 0}; /* "BYMONTHDAY" */
-
-static const UChar ICAL_NEWLINE[] = {0x0D, 0x0A, 0}; /* CRLF */
-
-static const UChar ICAL_DOW_NAMES[7][3] = {
-    {0x53, 0x55, 0}, /* "SU" */
-    {0x4D, 0x4F, 0}, /* "MO" */
-    {0x54, 0x55, 0}, /* "TU" */
-    {0x57, 0x45, 0}, /* "WE" */
-    {0x54, 0x48, 0}, /* "TH" */
-    {0x46, 0x52, 0}, /* "FR" */
-    {0x53, 0x41, 0}  /* "SA" */};
-
-// Month length for non-leap year
-static const int32_t MONTHLENGTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-// ICU custom property
-static const UChar ICU_TZINFO_PROP[] = {0x58, 0x2D, 0x54, 0x5A, 0x49, 0x4E, 0x46, 0x4F, 0x3A, 0}; /* "X-TZINFO:" */
-static const UChar ICU_TZINFO_PARTIAL[] = {0x2F, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6C, 0x40, 0}; /* "/Partial@" */
-static const UChar ICU_TZINFO_SIMPLE[] = {0x2F, 0x53, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x40, 0}; /* "/Simple@" */
-
-
-/*
- * Simple fixed digit ASCII number to integer converter
- */
-static int32_t parseAsciiDigits(const UnicodeString& str, int32_t start, int32_t length, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return 0;
-    }
-    if (length <= 0 || str.length() < start || (start + length) > str.length()) {
-        status = U_INVALID_FORMAT_ERROR;
-        return 0;
-    }
-    int32_t sign = 1;
-    if (str.charAt(start) == PLUS) {
-        start++;
-        length--;
-    } else if (str.charAt(start) == MINUS) {
-        sign = -1;
-        start++;
-        length--;
-    }
-    int32_t num = 0;
-    for (int32_t i = 0; i < length; i++) {
-        int32_t digit = str.charAt(start + i) - 0x0030;
-        if (digit < 0 || digit > 9) {
-            status = U_INVALID_FORMAT_ERROR;
-            return 0;
-        }
-        num = 10 * num + digit;
-    }
-    return sign * num;
-}
-
-static UnicodeString& appendAsciiDigits(int32_t number, uint8_t length, UnicodeString& str) {
-    UBool negative = false;
-    int32_t digits[10]; // max int32_t is 10 decimal digits
-    int32_t i;
-
-    if (number < 0) {
-        negative = true;
-        number *= -1;
-    }
-
-    length = length > 10 ? 10 : length;
-    if (length == 0) {
-        // variable length
-        i = 0;
-        do {
-            digits[i++] = number % 10;
-            number /= 10;
-        } while (number != 0);
-        length = static_cast<uint8_t>(i);
-    } else {
-        // fixed digits
-        for (i = 0; i < length; i++) {
-           digits[i] = number % 10;
-           number /= 10;
-        }
-    }
-    if (negative) {
-        str.append(MINUS);
-    }
-    for (i = length - 1; i >= 0; i--) {
-        str.append((UChar)(digits[i] + 0x0030));
-    }
-    return str;
-}
-
-static UnicodeString& appendMillis(UDate date, UnicodeString& str) {
-    UBool negative = false;
-    int32_t digits[20]; // max int64_t is 20 decimal digits
-    int32_t i;
-    int64_t number;
-
-    if (date < MIN_MILLIS) {
-        number = (int64_t)MIN_MILLIS;
-    } else if (date > MAX_MILLIS) {
-        number = (int64_t)MAX_MILLIS;
-    } else {
-        number = (int64_t)date;
-    }
-    if (number < 0) {
-        negative = true;
-        number *= -1;
-    }
-    i = 0;
-    do {
-        digits[i++] = (int32_t)(number % 10);
-        number /= 10;
-    } while (number != 0);
-
-    if (negative) {
-        str.append(MINUS);
-    }
-    i--;
-    while (i >= 0) {
-        str.append((UChar)(digits[i--] + 0x0030));
-    }
-    return str;
-}
-
-/*
- * Convert date/time to RFC2445 Date-Time form #1 DATE WITH LOCAL TIME
- */
-static UnicodeString& getDateTimeString(UDate time, UnicodeString& str) {
-    int32_t year, month, dom, dow, doy, mid;
-    Grego::timeToFields(time, year, month, dom, dow, doy, mid);
-
-    str.remove();
-    appendAsciiDigits(year, 4, str);
-    appendAsciiDigits(month + 1, 2, str);
-    appendAsciiDigits(dom, 2, str);
-    str.append((UChar)0x0054 /*'T'*/);
-
-    int32_t t = mid;
-    int32_t hour = t / U_MILLIS_PER_HOUR;
-    t %= U_MILLIS_PER_HOUR;
-    int32_t min = t / U_MILLIS_PER_MINUTE;
-    t %= U_MILLIS_PER_MINUTE;
-    int32_t sec = t / U_MILLIS_PER_SECOND;
-
-    appendAsciiDigits(hour, 2, str);
-    appendAsciiDigits(min, 2, str);
-    appendAsciiDigits(sec, 2, str);
-    return str;
-}
-
-/*
- * Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME
- */
-static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str) {
-    getDateTimeString(time, str);
-    str.append((UChar)0x005A /*'Z'*/);
-    return str;
-}
-
-/*
- * Parse RFC2445 Date-Time form #1 DATE WITH LOCAL TIME and
- * #2 DATE WITH UTC TIME
- */
-static UDate parseDateTimeString(const UnicodeString& str, int32_t offset, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return 0.0;
-    }
-
-    int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;
-    UBool isUTC = false;
-    UBool isValid = false;
-    do {
-        int length = str.length();
-        if (length != 15 && length != 16) {
-            // FORM#1 15 characters, such as "20060317T142115"
-            // FORM#2 16 characters, such as "20060317T142115Z"
-            break;
-        }
-        if (str.charAt(8) != 0x0054) {
-            // character "T" must be used for separating date and time
-            break;
-        }
-        if (length == 16) {
-            if (str.charAt(15) != 0x005A) {
-                // invalid format
-                break;
-            }
-            isUTC = true;
-        }
-
-        year = parseAsciiDigits(str, 0, 4, status);
-        month = parseAsciiDigits(str, 4, 2, status) - 1;  // 0-based
-        day = parseAsciiDigits(str, 6, 2, status);
-        hour = parseAsciiDigits(str, 9, 2, status);
-        min = parseAsciiDigits(str, 11, 2, status);
-        sec = parseAsciiDigits(str, 13, 2, status);
-
-        if (U_FAILURE(status)) {
-            break;
-        }
-
-        // check valid range
-        int32_t maxDayOfMonth = Grego::monthLength(year, month);
-        if (year < 0 || month < 0 || month > 11 || day < 1 || day > maxDayOfMonth ||
-                hour < 0 || hour >= 24 || min < 0 || min >= 60 || sec < 0 || sec >= 60) {
-            break;
-        }
-
-        isValid = true;
-    } while(false);
-
-    if (!isValid) {
-        status = U_INVALID_FORMAT_ERROR;
-        return 0.0;
-    }
-    // Calculate the time
-    UDate time = Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY;
-    time += (hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE + sec * U_MILLIS_PER_SECOND);
-    if (!isUTC) {
-        time -= offset;
-    }
-    return time;
-}
-
-/*
- * Convert RFC2445 utc-offset string to milliseconds
- */
-static int32_t offsetStrToMillis(const UnicodeString& str, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return 0;
-    }
-
-    UBool isValid = false;
-    int32_t sign = 0, hour = 0, min = 0, sec = 0;
-
-    do {
-        int length = str.length();
-        if (length != 5 && length != 7) {
-            // utf-offset must be 5 or 7 characters
-            break;
-        }
-        // sign
-        UChar s = str.charAt(0);
-        if (s == PLUS) {
-            sign = 1;
-        } else if (s == MINUS) {
-            sign = -1;
-        } else {
-            // utf-offset must start with "+" or "-"
-            break;
-        }
-        hour = parseAsciiDigits(str, 1, 2, status);
-        min = parseAsciiDigits(str, 3, 2, status);
-        if (length == 7) {
-            sec = parseAsciiDigits(str, 5, 2, status);
-        }
-        if (U_FAILURE(status)) {
-            break;
-        }
-        isValid = true;
-    } while(false);
-
-    if (!isValid) {
-        status = U_INVALID_FORMAT_ERROR;
-        return 0;
-    }
-    int32_t millis = sign * ((hour * 60 + min) * 60 + sec) * 1000;
-    return millis;
-}
-
-/*
- * Convert milliseconds to RFC2445 utc-offset string
- */
-static void millisToOffset(int32_t millis, UnicodeString& str) {
-    str.remove();
-    if (millis >= 0) {
-        str.append(PLUS);
-    } else {
-        str.append(MINUS);
-        millis = -millis;
-    }
-    int32_t hour, min, sec;
-    int32_t t = millis / 1000;
-
-    sec = t % 60;
-    t = (t - sec) / 60;
-    min = t % 60;
-    hour = t / 60;
-
-    appendAsciiDigits(hour, 2, str);
-    appendAsciiDigits(min, 2, str);
-    appendAsciiDigits(sec, 2, str);
-}
-
-/*
- * Create a default TZNAME from TZID
- */
-static void getDefaultTZName(const UnicodeString &tzid, UBool isDST, UnicodeString& zonename) {
-    zonename = tzid;
-    if (isDST) {
-        zonename += UNICODE_STRING_SIMPLE("(DST)");
-    } else {
-        zonename += UNICODE_STRING_SIMPLE("(STD)");
-    }
-}
-
-/*
- * Parse individual RRULE
- *
- * On return -
- *
- * month    calculated by BYMONTH-1, or -1 when not found
- * dow      day of week in BYDAY, or 0 when not found
- * wim      day of week ordinal number in BYDAY, or 0 when not found
- * dom      an array of day of month
- * domCount number of available days in dom (domCount is specifying the size of dom on input)
- * until    time defined by UNTIL attribute or MIN_MILLIS if not available
- */
-static void parseRRULE(const UnicodeString& rrule, int32_t& month, int32_t& dow, int32_t& wim,
-                       int32_t* dom, int32_t& domCount, UDate& until, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    int32_t numDom = 0;
-
-    month = -1;
-    dow = 0;
-    wim = 0;
-    until = MIN_MILLIS;
-
-    UBool yearly = false;
-    //UBool parseError = false;
-
-    int32_t prop_start = 0;
-    int32_t prop_end;
-    UnicodeString prop, attr, value;
-    UBool nextProp = true;
-
-    while (nextProp) {
-        prop_end = rrule.indexOf(SEMICOLON, prop_start);
-        if (prop_end == -1) {
-            prop.setTo(rrule, prop_start);
-            nextProp = false;
-        } else {
-            prop.setTo(rrule, prop_start, prop_end - prop_start);
-            prop_start = prop_end + 1;
-        }
-        int32_t eql = prop.indexOf(EQUALS_SIGN);
-        if (eql != -1) {
-            attr.setTo(prop, 0, eql);
-            value.setTo(prop, eql + 1);
-        } else {
-            goto rruleParseError;
-        }
-
-        if (attr.compare(ICAL_FREQ, -1) == 0) {
-            // only support YEARLY frequency type
-            if (value.compare(ICAL_YEARLY, -1) == 0) {
-                yearly = true;
-            } else {
-                goto rruleParseError;
-            }
-        } else if (attr.compare(ICAL_UNTIL, -1) == 0) {
-            // ISO8601 UTC format, for example, "20060315T020000Z"
-            until = parseDateTimeString(value, 0, status);
-            if (U_FAILURE(status)) {
-                goto rruleParseError;
-            }
-        } else if (attr.compare(ICAL_BYMONTH, -1) == 0) {
-            // Note: BYMONTH may contain multiple months, but only single month make sense for
-            // VTIMEZONE property.
-            if (value.length() > 2) {
-                goto rruleParseError;
-            }
-            month = parseAsciiDigits(value, 0, value.length(), status) - 1;
-            if (U_FAILURE(status) || month < 0 || month >= 12) {
-                goto rruleParseError;
-            }
-        } else if (attr.compare(ICAL_BYDAY, -1) == 0) {
-            // Note: BYDAY may contain multiple day of week separated by comma.  It is unlikely used for
-            // VTIMEZONE property.  We do not support the case.
-
-            // 2-letter format is used just for representing a day of week, for example, "SU" for Sunday
-            // 3 or 4-letter format is used for represeinging Nth day of week, for example, "-1SA" for last Saturday
-            int32_t length = value.length();
-            if (length < 2 || length > 4) {
-                goto rruleParseError;
-            }
-            if (length > 2) {
-                // Nth day of week
-                int32_t sign = 1;
-                if (value.charAt(0) == PLUS) {
-                    sign = 1;
-                } else if (value.charAt(0) == MINUS) {
-                    sign = -1;
-                } else if (length == 4) {
-                    goto rruleParseError;
-                }
-                int32_t n = parseAsciiDigits(value, length - 3, 1, status);
-                if (U_FAILURE(status) || n == 0 || n > 4) {
-                    goto rruleParseError;
-                }
-                wim = n * sign;
-                value.remove(0, length - 2);
-            }
-            int32_t wday;
-            for (wday = 0; wday < 7; wday++) {
-                if (value.compare(ICAL_DOW_NAMES[wday], 2) == 0) {
-                    break;
-                }
-            }
-            if (wday < 7) {
-                // Sunday(1) - Saturday(7)
-                dow = wday + 1;
-            } else {
-                goto rruleParseError;
-            }
-        } else if (attr.compare(ICAL_BYMONTHDAY, -1) == 0) {
-            // Note: BYMONTHDAY may contain multiple days delimited by comma
-            //
-            // A value of BYMONTHDAY could be negative, for example, -1 means
-            // the last day in a month
-            int32_t dom_idx = 0;
-            int32_t dom_start = 0;
-            int32_t dom_end;
-            UBool nextDOM = true;
-            while (nextDOM) {
-                dom_end = value.indexOf(COMMA, dom_start);
-                if (dom_end == -1) {
-                    dom_end = value.length();
-                    nextDOM = false;
-                }
-                if (dom_idx < domCount) {
-                    dom[dom_idx] = parseAsciiDigits(value, dom_start, dom_end - dom_start, status);
-                    if (U_FAILURE(status)) {
-                        goto rruleParseError;
-                    }
-                    dom_idx++;
-                } else {
-                    status = U_BUFFER_OVERFLOW_ERROR;
-                    goto rruleParseError;
-                }
-                dom_start = dom_end + 1;
-            }
-            numDom = dom_idx;
-        }
-    }
-    if (!yearly) {
-        // FREQ=YEARLY must be set
-        goto rruleParseError;
-    }
-    // Set actual number of parsed DOM (ICAL_BYMONTHDAY)
-    domCount = numDom;
-    return;
-
-rruleParseError:
-    if (U_SUCCESS(status)) {
-        // Set error status
-        status = U_INVALID_FORMAT_ERROR;
-    }
-}
-
-static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOffset, int dstSavings, UDate start,
-                                       UVector* dates, int fromOffset, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
-    if (dates == nullptr || dates->size() == 0) {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return nullptr;
-    }
-
-    int32_t i, j;
-    DateTimeRule *adtr = nullptr;
-
-    // Parse the first rule
-    UnicodeString rrule = *((UnicodeString*)dates->elementAt(0));
-    int32_t month, dayOfWeek, nthDayOfWeek, dayOfMonth = 0;
-    int32_t days[7];
-    int32_t daysCount = UPRV_LENGTHOF(days);
-    UDate until;
-
-    parseRRULE(rrule, month, dayOfWeek, nthDayOfWeek, days, daysCount, until, status);
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
-
-    if (dates->size() == 1) {
-        // No more rules
-        if (daysCount > 1) {
-            // Multiple BYMONTHDAY values
-            if (daysCount != 7 || month == -1 || dayOfWeek == 0) {
-                // Only support the rule using 7 continuous days
-                // BYMONTH and BYDAY must be set at the same time
-                goto unsupportedRRule;
-            }
-            int32_t firstDay = 31; // max possible number of dates in a month
-            for (i = 0; i < 7; i++) {
-                // Resolve negative day numbers.  A negative day number should
-                // not be used in February, but if we see such case, we use 28
-                // as the base.
-                if (days[i] < 0) {
-                    days[i] = MONTHLENGTH[month] + days[i] + 1;
-                }
-                if (days[i] < firstDay) {
-                    firstDay = days[i];
-                }
-            }
-            // Make sure days are continuous
-            for (i = 1; i < 7; i++) {
-                UBool found = false;
-                for (j = 0; j < 7; j++) {
-                    if (days[j] == firstDay + i) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found) {
-                    // days are not continuous
-                    goto unsupportedRRule;
-                }
-            }
-            // Use DOW_GEQ_DOM rule with firstDay as the start date
-            dayOfMonth = firstDay;
-        }
-    } else {
-        // Check if BYMONTH + BYMONTHDAY + BYDAY rule with multiple RRULE lines.
-        // Otherwise, not supported.
-        if (month == -1 || dayOfWeek == 0 || daysCount == 0) {
-            // This is not the case
-            goto unsupportedRRule;
-        }
-        // Parse the rest of rules if number of rules is not exceeding 7.
-        // We can only support 7 continuous days starting from a day of month.
-        if (dates->size() > 7) {
-            goto unsupportedRRule;
-        }
-
-        // Note: To check valid date range across multiple rule is a little
-        // bit complicated.  For now, this code is not doing strict range
-        // checking across month boundary
-
-        int32_t earliestMonth = month;
-        int32_t earliestDay = 31;
-        for (i = 0; i < daysCount; i++) {
-            int32_t dom = days[i];
-            dom = dom > 0 ? dom : MONTHLENGTH[month] + dom + 1;
-            earliestDay = dom < earliestDay ? dom : earliestDay;
-        }
-
-        int32_t anotherMonth = -1;
-        for (i = 1; i < dates->size(); i++) {
-            rrule = *((UnicodeString*)dates->elementAt(i));
-            UDate tmp_until;
-            int32_t tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek;
-            int32_t tmp_days[7];
-            int32_t tmp_daysCount = UPRV_LENGTHOF(tmp_days);
-            parseRRULE(rrule, tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek, tmp_days, tmp_daysCount, tmp_until, status);
-            if (U_FAILURE(status)) {
-                return nullptr;
-            }
-            // If UNTIL is newer than previous one, use the one
-            if (tmp_until > until) {
-                until = tmp_until;
-            }
-
-            // Check if BYMONTH + BYMONTHDAY + BYDAY rule
-            if (tmp_month == -1 || tmp_dayOfWeek == 0 || tmp_daysCount == 0) {
-                goto unsupportedRRule;
-            }
-            // Count number of BYMONTHDAY
-            if (daysCount + tmp_daysCount > 7) {
-                // We cannot support BYMONTHDAY more than 7
-                goto unsupportedRRule;
-            }
-            // Check if the same BYDAY is used.  Otherwise, we cannot
-            // support the rule
-            if (tmp_dayOfWeek != dayOfWeek) {
-                goto unsupportedRRule;
-            }
-            // Check if the month is same or right next to the primary month
-            if (tmp_month != month) {
-                if (anotherMonth == -1) {
-                    int32_t diff = tmp_month - month;
-                    if (diff == -11 || diff == -1) {
-                        // Previous month
-                        anotherMonth = tmp_month;
-                        earliestMonth = anotherMonth;
-                        // Reset earliest day
-                        earliestDay = 31;
-                    } else if (diff == 11 || diff == 1) {
-                        // Next month
-                        anotherMonth = tmp_month;
-                    } else {
-                        // The day range cannot exceed more than 2 months
-                        goto unsupportedRRule;
-                    }
-                } else if (tmp_month != month && tmp_month != anotherMonth) {
-                    // The day range cannot exceed more than 2 months
-                    goto unsupportedRRule;
-                }
-            }
-            // If earlier month, go through days to find the earliest day
-            if (tmp_month == earliestMonth) {
-                for (j = 0; j < tmp_daysCount; j++) {
-                    tmp_days[j] = tmp_days[j] > 0 ? tmp_days[j] : MONTHLENGTH[tmp_month] + tmp_days[j] + 1;
-                    earliestDay = tmp_days[j] < earliestDay ? tmp_days[j] : earliestDay;
-                }
-            }
-            daysCount += tmp_daysCount;
-        }
-        if (daysCount != 7) {
-            // Number of BYMONTHDAY entries must be 7
-            goto unsupportedRRule;
-        }
-        month = earliestMonth;
-        dayOfMonth = earliestDay;
-    }
-
-    // Calculate start/end year and missing fields
-    int32_t startYear, startMonth, startDOM, startDOW, startDOY, startMID;
-    Grego::timeToFields(start + fromOffset, startYear, startMonth, startDOM,
-        startDOW, startDOY, startMID);
-    if (month == -1) {
-        // If BYMONTH is not set, use the month of DTSTART
-        month = startMonth;
-    }
-    if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth == 0) {
-        // If only YEARLY is set, use the day of DTSTART as BYMONTHDAY
-        dayOfMonth = startDOM;
-    }
-
-    int32_t endYear;
-    if (until != MIN_MILLIS) {
-        int32_t endMonth, endDOM, endDOW, endDOY, endMID;
-        Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID);
-    } else {
-        endYear = AnnualTimeZoneRule::MAX_YEAR;
-    }
-
-    // Create the AnnualDateTimeRule
-    if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
-        // Day in month rule, for example, 15th day in the month
-        adtr = new DateTimeRule(month, dayOfMonth, startMID, DateTimeRule::WALL_TIME);
-    } else if (dayOfWeek != 0 && nthDayOfWeek != 0 && dayOfMonth == 0) {
-        // Nth day of week rule, for example, last Sunday
-        adtr = new DateTimeRule(month, nthDayOfWeek, dayOfWeek, startMID, DateTimeRule::WALL_TIME);
-    } else if (dayOfWeek != 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
-        // First day of week after day of month rule, for example,
-        // first Sunday after 15th day in the month
-        adtr = new DateTimeRule(month, dayOfMonth, dayOfWeek, true, startMID, DateTimeRule::WALL_TIME);
-    }
-    if (adtr == nullptr) {
-        goto unsupportedRRule;
-    }
-    return new AnnualTimeZoneRule(zonename, rawOffset, dstSavings, adtr, startYear, endYear);
-
-unsupportedRRule:
-    status = U_INVALID_STATE_ERROR;
-    return nullptr;
-}
-
-/*
- * Create a TimeZoneRule by the RDATE definition
- */
-static TimeZoneRule* createRuleByRDATE(const UnicodeString& zonename, int32_t rawOffset, int32_t dstSavings,
-                                       UDate start, UVector* dates, int32_t fromOffset, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
-    TimeArrayTimeZoneRule *retVal = nullptr;
-    if (dates == nullptr || dates->size() == 0) {
-        // When no RDATE line is provided, use start (DTSTART)
-        // as the transition time
-        retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings, &start, 1, DateTimeRule::UTC_TIME);
-    } else {
-        // Create an array of transition times
-        int32_t size = dates->size();
-        UDate* times = (UDate*)uprv_malloc(sizeof(UDate) * size);
-        if (times == nullptr) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return nullptr;
-        }
-        for (int32_t i = 0; i < size; i++) {
-            UnicodeString *datestr = (UnicodeString*)dates->elementAt(i);
-            times[i] = parseDateTimeString(*datestr, fromOffset, status);
-            if (U_FAILURE(status)) {
-                uprv_free(times);
-                return nullptr;
-            }
-        }
-        retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings, times, size, DateTimeRule::UTC_TIME);
-        uprv_free(times);
-    }
-    if (retVal == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-    }
-    return retVal;
-}
-
-/*
- * Check if the DOW rule specified by month, weekInMonth and dayOfWeek is equivalent
- * to the DateTimerule.
- */
-static UBool isEquivalentDateRule(int32_t month, int32_t weekInMonth, int32_t dayOfWeek, const DateTimeRule *dtrule) {
-    if (month != dtrule->getRuleMonth() || dayOfWeek != dtrule->getRuleDayOfWeek()) {
-        return false;
-    }
-    if (dtrule->getTimeRuleType() != DateTimeRule::WALL_TIME) {
-        // Do not try to do more intelligent comparison for now.
-        return false;
-    }
-    if (dtrule->getDateRuleType() == DateTimeRule::DOW
-            && dtrule->getRuleWeekInMonth() == weekInMonth) {
-        return true;
-    }
-    int32_t ruleDOM = dtrule->getRuleDayOfMonth();
-    if (dtrule->getDateRuleType() == DateTimeRule::DOW_GEQ_DOM) {
-        if (ruleDOM%7 == 1 && (ruleDOM + 6)/7 == weekInMonth) {
-            return true;
-        }
-        if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 6
-                && weekInMonth == -1*((MONTHLENGTH[month]-ruleDOM+1)/7)) {
-            return true;
-        }
-    }
-    if (dtrule->getDateRuleType() == DateTimeRule::DOW_LEQ_DOM) {
-        if (ruleDOM%7 == 0 && ruleDOM/7 == weekInMonth) {
-            return true;
-        }
-        if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 0
-                && weekInMonth == -1*((MONTHLENGTH[month] - ruleDOM)/7 + 1)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-/*
- * Convert the rule to its equivalent rule using WALL_TIME mode.
- * This function returns nullptr when the specified DateTimeRule is already
- * using WALL_TIME mode.
- */
-static DateTimeRule *toWallTimeRule(const DateTimeRule *rule, int32_t rawOffset, int32_t dstSavings, UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
-    if (rule->getTimeRuleType() == DateTimeRule::WALL_TIME) {
-        return nullptr;
-    }
-    int32_t wallt = rule->getRuleMillisInDay();
-    if (rule->getTimeRuleType() == DateTimeRule::UTC_TIME) {
-        wallt += (rawOffset + dstSavings);
-    } else if (rule->getTimeRuleType() == DateTimeRule::STANDARD_TIME) {
-        wallt += dstSavings;
-    }
-
-    int32_t month = -1, dom = 0, dow = 0;
-    DateTimeRule::DateRuleType dtype;
-    int32_t dshift = 0;
-    if (wallt < 0) {
-        dshift = -1;
-        wallt += U_MILLIS_PER_DAY;
-    } else if (wallt >= U_MILLIS_PER_DAY) {
-        dshift = 1;
-        wallt -= U_MILLIS_PER_DAY;
-    }
-
-    month = rule->getRuleMonth();
-    dom = rule->getRuleDayOfMonth();
-    dow = rule->getRuleDayOfWeek();
-    dtype = rule->getDateRuleType();
-
-    if (dshift != 0) {
-        if (dtype == DateTimeRule::DOW) {
-            // Convert to DOW_GEW_DOM or DOW_LEQ_DOM rule first
-            int32_t wim = rule->getRuleWeekInMonth();
-            if (wim > 0) {
-                dtype = DateTimeRule::DOW_GEQ_DOM;
-                dom = 7 * (wim - 1) + 1;
-            } else {
-                dtype = DateTimeRule::DOW_LEQ_DOM;
-                dom = MONTHLENGTH[month] + 7 * (wim + 1);
-            }
-        }
-        // Shift one day before or after
-        dom += dshift;
-        if (dom == 0) {
-            month--;
-            month = month < UCAL_JANUARY ? UCAL_DECEMBER : month;
-            dom = MONTHLENGTH[month];
-        } else if (dom > MONTHLENGTH[month]) {
-            month++;
-            month = month > UCAL_DECEMBER ? UCAL_JANUARY : month;
-            dom = 1;
-        }
-        if (dtype != DateTimeRule::DOM) {
-            // Adjust day of week
-            dow += dshift;
-            if (dow < UCAL_SUNDAY) {
-                dow = UCAL_SATURDAY;
-            } else if (dow > UCAL_SATURDAY) {
-                dow = UCAL_SUNDAY;
-            }
-        }
-    }
-    // Create a new rule
-    DateTimeRule *modifiedRule = nullptr;
-    if (dtype == DateTimeRule::DOM) {
-        modifiedRule = new DateTimeRule(month, dom, wallt, DateTimeRule::WALL_TIME);
-    } else {
-        modifiedRule = new DateTimeRule(month, dom, dow, (dtype == DateTimeRule::DOW_GEQ_DOM), wallt, DateTimeRule::WALL_TIME);
-    }
-    if (modifiedRule == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-    }
-    return modifiedRule;
-}
-
-/*
- * Minimum implementations of stream writer/reader, writing/reading
- * UnicodeString.  For now, we do not want to introduce the dependency
- * on the ICU I/O stream in this module.  But we want to keep the code
- * equivalent to the ICU4J implementation, which utilizes java.io.Writer/
- * Reader.
- */
-class VTZWriter {
-public:
-    VTZWriter(UnicodeString& out);
-    ~VTZWriter();
-
-    void write(const UnicodeString& str);
-    void write(UChar ch);
-    void write(const UChar* str);
-    //void write(const UChar* str, int32_t length);
-private:
-    UnicodeString* out;
-};
-
-VTZWriter::VTZWriter(UnicodeString& output) {
-    out = &output;
-}
-
-VTZWriter::~VTZWriter() {
-}
-
-void
-VTZWriter::write(const UnicodeString& str) {
-    out->append(str);
-}
-
-void
-VTZWriter::write(UChar ch) {
-    out->append(ch);
-}
-
-void
-VTZWriter::write(const UChar* str) {
-    out->append(str, -1);
-}
-
-/*
-void
-VTZWriter::write(const UChar* str, int32_t length) {
-    out->append(str, length);
-}
-*/
-
-class VTZReader {
-public:
-    VTZReader(const UnicodeString& input);
-    ~VTZReader();
-
-    UChar read(void);
-private:
-    const UnicodeString* in;
-    int32_t index;
-};
-
-VTZReader::VTZReader(const UnicodeString& input) {
-    in = &input;
-    index = 0;
-}
-
-VTZReader::~VTZReader() {
-}
-
-UChar
-VTZReader::read(void) {
-    UChar ch = 0xFFFF;
-    if (index < in->length()) {
-        ch = in->charAt(index);
-    }
-    index++;
-    return ch;
-}
-
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(VTimeZone)
-
-VTimeZone::VTimeZone()
-:   BasicTimeZone(), tz(nullptr), vtzlines(nullptr),
-    lastmod(MAX_MILLIS) {
-}
-
-VTimeZone::VTimeZone(const VTimeZone& source)
-:   BasicTimeZone(source), tz(nullptr), vtzlines(nullptr),
-    tzurl(source.tzurl), lastmod(source.lastmod),
-    olsonzid(source.olsonzid), icutzver(source.icutzver) {
-    if (source.tz != nullptr) {
-        tz = source.tz->clone();
-    }
-    if (source.vtzlines != nullptr) {
-        UErrorCode status = U_ZERO_ERROR;
-        int32_t size = source.vtzlines->size();
-        LocalPointer<UVector> lpVtzLines(
-            new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status), status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-        for (int32_t i = 0; i < size; i++) {
-            UnicodeString *line = ((UnicodeString*)source.vtzlines->elementAt(i))->clone();
-            lpVtzLines->adoptElement(line, status);
-            if (U_FAILURE(status) || line == nullptr) {
-                return;
-            }
-        }
-        vtzlines = lpVtzLines.orphan();
-    }
-}
-
-VTimeZone::~VTimeZone() {
-    if (tz != nullptr) {
-        delete tz;
-    }
-    if (vtzlines != nullptr) {
-        delete vtzlines;
-    }
-}
-
-VTimeZone&
-VTimeZone::operator=(const VTimeZone& right) {
-    if (this == &right) {
-        return *this;
-    }
-    if (*this != right) {
-        BasicTimeZone::operator=(right);
-        if (tz != nullptr) {
-            delete tz;
-            tz = nullptr;
-        }
-        if (right.tz != nullptr) {
-            tz = right.tz->clone();
-        }
-        if (vtzlines != nullptr) {
-            delete vtzlines;
-            vtzlines = nullptr;
-        }
-        if (right.vtzlines != nullptr) {
-            UErrorCode status = U_ZERO_ERROR;
-            int32_t size = right.vtzlines->size();
-            LocalPointer<UVector> lpVtzLines(
-                new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status), status);
-            if (U_SUCCESS(status)) {
-                for (int32_t i = 0; i < size; i++) {
-                    LocalPointer<UnicodeString> line(
-                        ((UnicodeString*)right.vtzlines->elementAt(i))->clone(), status);
-                    lpVtzLines->adoptElement(line.orphan(), status);
-                    if (U_FAILURE(status)) {
-                        break;
-                    }
-                }
-                if (U_SUCCESS(status)) {
-                    vtzlines = lpVtzLines.orphan();
-                }
-            }
-        }
-        tzurl = right.tzurl;
-        lastmod = right.lastmod;
-        olsonzid = right.olsonzid;
-        icutzver = right.icutzver;
-    }
-    return *this;
-}
-
-bool
-VTimeZone::operator==(const TimeZone& that) const {
-    if (this == &that) {
-        return true;
-    }
-    if (typeid(*this) != typeid(that) || !BasicTimeZone::operator==(that)) {
-        return false;
-    }
-    VTimeZone *vtz = (VTimeZone*)&that;
-    if (*tz == *(vtz->tz)
-        && tzurl == vtz->tzurl
-        && lastmod == vtz->lastmod
-        /* && olsonzid = that.olsonzid */
-        /* && icutzver = that.icutzver */) {
-        return true;
-    }
-    return false;
-}
-
-bool
-VTimeZone::operator!=(const TimeZone& that) const {
-    return !operator==(that);
-}
-
-VTimeZone*
-VTimeZone::createVTimeZoneByID(const UnicodeString& ID) {
-    VTimeZone *vtz = new VTimeZone();
-    if (vtz == nullptr) {
-        return nullptr;
-    }
-    vtz->tz = (BasicTimeZone*)TimeZone::createTimeZone(ID);
-    vtz->tz->getID(vtz->olsonzid);
-
-    // Set ICU tzdata version
-    UErrorCode status = U_ZERO_ERROR;
-    UResourceBundle *bundle = nullptr;
-    const UChar* versionStr = nullptr;
-    int32_t len = 0;
-    bundle = ures_openDirect(nullptr, "zoneinfo64", &status);
-    versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status);
-    if (U_SUCCESS(status)) {
-        vtz->icutzver.setTo(versionStr, len);
-    }
-    ures_close(bundle);
-    return vtz;
-}
-
-VTimeZone*
-VTimeZone::createVTimeZoneFromBasicTimeZone(const BasicTimeZone& basic_time_zone, UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
-    VTimeZone *vtz = new VTimeZone();
-    if (vtz == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return nullptr;
-    }
-    vtz->tz = basic_time_zone.clone();
-    if (vtz->tz == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        delete vtz;
-        return nullptr;
-    }
-    vtz->tz->getID(vtz->olsonzid);
-
-    // Set ICU tzdata version
-    UResourceBundle *bundle = nullptr;
-    const UChar* versionStr = nullptr;
-    int32_t len = 0;
-    bundle = ures_openDirect(nullptr, "zoneinfo64", &status);
-    versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status);
-    if (U_SUCCESS(status)) {
-        vtz->icutzver.setTo(versionStr, len);
-    }
-    ures_close(bundle);
-    return vtz;
-}
-
-VTimeZone*
-VTimeZone::createVTimeZone(const UnicodeString& vtzdata, UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
-    VTZReader reader(vtzdata);
-    VTimeZone *vtz = new VTimeZone();
-    if (vtz == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return nullptr;
-    }
-    vtz->load(reader, status);
-    if (U_FAILURE(status)) {
-        delete vtz;
-        return nullptr;
-    }
-    return vtz;
-}
-
-UBool
-VTimeZone::getTZURL(UnicodeString& url) const {
-    if (tzurl.length() > 0) {
-        url = tzurl;
-        return true;
-    }
-    return false;
-}
-
-void
-VTimeZone::setTZURL(const UnicodeString& url) {
-    tzurl = url;
-}
-
-UBool
-VTimeZone::getLastModified(UDate& lastModified) const {
-    if (lastmod != MAX_MILLIS) {
-        lastModified = lastmod;
-        return true;
-    }
-    return false;
-}
-
-void
-VTimeZone::setLastModified(UDate lastModified) {
-    lastmod = lastModified;
-}
-
-void
-VTimeZone::write(UnicodeString& result, UErrorCode& status) const {
-    result.remove();
-    VTZWriter writer(result);
-    write(writer, status);
-}
-
-void
-VTimeZone::write(UDate start, UnicodeString& result, UErrorCode& status) const {
-    result.remove();
-    VTZWriter writer(result);
-    write(start, writer, status);
-}
-
-void
-VTimeZone::writeSimple(UDate time, UnicodeString& result, UErrorCode& status) const {
-    result.remove();
-    VTZWriter writer(result);
-    writeSimple(time, writer, status);
-}
-
-VTimeZone*
-VTimeZone::clone() const {
-    return new VTimeZone(*this);
-}
-
-int32_t
-VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
-                     uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const {
-    return tz->getOffset(era, year, month, day, dayOfWeek, millis, status);
-}
-
-int32_t
-VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
-                     uint8_t dayOfWeek, int32_t millis,
-                     int32_t monthLength, UErrorCode& status) const {
-    return tz->getOffset(era, year, month, day, dayOfWeek, millis, monthLength, status);
-}
-
-void
-VTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
-                     int32_t& dstOffset, UErrorCode& status) const {
-    return tz->getOffset(date, local, rawOffset, dstOffset, status);
-}
-
-void VTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt,
-                                   UTimeZoneLocalOption duplicatedTimeOpt,
-                                   int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const {
-    tz->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status);
-}
-
-void
-VTimeZone::setRawOffset(int32_t offsetMillis) {
-    tz->setRawOffset(offsetMillis);
-}
-
-int32_t
-VTimeZone::getRawOffset(void) const {
-    return tz->getRawOffset();
-}
-
-UBool
-VTimeZone::useDaylightTime(void) const {
-    return tz->useDaylightTime();
-}
-
-UBool
-VTimeZone::inDaylightTime(UDate date, UErrorCode& status) const {
-    return tz->inDaylightTime(date, status);
-}
-
-UBool
-VTimeZone::hasSameRules(const TimeZone& other) const {
-    return tz->hasSameRules(other);
-}
-
-UBool
-VTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
-    return tz->getNextTransition(base, inclusive, result);
-}
-
-UBool
-VTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
-    return tz->getPreviousTransition(base, inclusive, result);
-}
-
-int32_t
-VTimeZone::countTransitionRules(UErrorCode& status) const {
-    return tz->countTransitionRules(status);
-}
-
-void
-VTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
-                            const TimeZoneRule* trsrules[], int32_t& trscount,
-                            UErrorCode& status) const {
-    tz->getTimeZoneRules(initial, trsrules, trscount, status);
-}
-
-void
-VTimeZone::load(VTZReader& reader, UErrorCode& status) {
-    U_ASSERT(vtzlines == nullptr);
-    LocalPointer<UVector> lpVtzLines(
-        new UVector(uprv_deleteUObject, uhash_compareUnicodeString, DEFAULT_VTIMEZONE_LINES, status), status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    UBool eol = false;
-    UBool start = false;
-    UBool success = false;
-    UnicodeString line;
-
-    while (true) {
-        UChar ch = reader.read();
-        if (ch == 0xFFFF) {
-            // end of file
-            if (start && line.startsWith(ICAL_END_VTIMEZONE, -1)) {
-                LocalPointer<UnicodeString> element(new UnicodeString(line), status);
-                lpVtzLines->adoptElement(element.orphan(), status);
-                if (U_FAILURE(status)) {
-                    return;
-                }
-                success = true;
-            }
-            break;
-        }
-        if (ch == 0x000D) {
-            // CR, must be followed by LF according to the definition in RFC2445
-            continue;
-        }
-        if (eol) {
-            if (ch != 0x0009 && ch != 0x0020) {
-                // NOT followed by TAB/SP -> new line
-                if (start) {
-                    if (line.length() > 0) {
-                        LocalPointer<UnicodeString> element(new UnicodeString(line), status);
-                        lpVtzLines->adoptElement(element.orphan(), status);
-                        if (U_FAILURE(status)) {
-                            return;
-                        }
-                    }
-                }
-                line.remove();
-                if (ch != 0x000A) {
-                    line.append(ch);
-                }
-            }
-            eol = false;
-        } else {
-            if (ch == 0x000A) {
-                // LF
-                eol = true;
-                if (start) {
-                    if (line.startsWith(ICAL_END_VTIMEZONE, -1)) {
-                        LocalPointer<UnicodeString> element(new UnicodeString(line), status);
-                        lpVtzLines->adoptElement(element.orphan(), status);
-                        if (U_FAILURE(status)) {
-                            return;
-                        }
-                        success = true;
-                        break;
-                    }
-                } else {
-                    if (line.startsWith(ICAL_BEGIN_VTIMEZONE, -1)) {
-                        LocalPointer<UnicodeString> element(new UnicodeString(line), status);
-                        lpVtzLines->adoptElement(element.orphan(), status);
-                        if (U_FAILURE(status)) {
-                            return;
-                        }
-                        line.remove();
-                        start = true;
-                        eol = false;
-                    }
-                }
-            } else {
-                line.append(ch);
-            }
-        }
-    }
-    if (!success) {
-        if (U_SUCCESS(status)) {
-            status = U_INVALID_STATE_ERROR;
-        }
-        return;
-    }
-    vtzlines = lpVtzLines.orphan();
-    parse(status);
-}
-
-// parser state
-#define INI 0   // Initial state
-#define VTZ 1   // In VTIMEZONE
-#define TZI 2   // In STANDARD or DAYLIGHT
-
-#define DEF_DSTSAVINGS (60*60*1000)
-#define DEF_TZSTARTTIME (0.0)
-
-void
-VTimeZone::parse(UErrorCode& status) {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (vtzlines == nullptr || vtzlines->size() == 0) {
-        status = U_INVALID_STATE_ERROR;
-        return;
-    }
-
-    // timezone ID
-    UnicodeString tzid;
-
-    int32_t state = INI;
-    int32_t n = 0;
-    UBool dst = false;      // current zone type
-    UnicodeString from;     // current zone from offset
-    UnicodeString to;       // current zone offset
-    UnicodeString zonename;   // current zone name
-    UnicodeString dtstart;  // current zone starts
-    UBool isRRULE = false;  // true if the rule is described by RRULE
-    int32_t initialRawOffset = 0;   // initial offset
-    int32_t initialDSTSavings = 0;  // initial offset
-    UDate firstStart = MAX_MILLIS;  // the earliest rule start time
-    UnicodeString name;     // RFC2445 prop name
-    UnicodeString value;    // RFC2445 prop value
-
-    int32_t finalRuleIdx = -1;
-    int32_t finalRuleCount = 0;
-
-    // Set the deleter on rules to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules.
-    UVector rules(uprv_deleteUObject, nullptr, status);
-
-    // list of RDATE or RRULE strings
-    UVector dates(uprv_deleteUObject, uhash_compareUnicodeString, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    for (n = 0; n < vtzlines->size(); n++) {
-        UnicodeString *line = (UnicodeString*)vtzlines->elementAt(n);
-        int32_t valueSep = line->indexOf(COLON);
-        if (valueSep < 0) {
-            continue;
-        }
-        name.setTo(*line, 0, valueSep);
-        value.setTo(*line, valueSep + 1);
-
-        switch (state) {
-        case INI:
-            if (name.compare(ICAL_BEGIN, -1) == 0
-                && value.compare(ICAL_VTIMEZONE, -1) == 0) {
-                state = VTZ;
-            }
-            break;
-
-        case VTZ:
-            if (name.compare(ICAL_TZID, -1) == 0) {
-                tzid = value;
-            } else if (name.compare(ICAL_TZURL, -1) == 0) {
-                tzurl = value;
-            } else if (name.compare(ICAL_LASTMOD, -1) == 0) {
-                // Always in 'Z' format, so the offset argument for the parse method
-                // can be any value.
-                lastmod = parseDateTimeString(value, 0, status);
-                if (U_FAILURE(status)) {
-                    return;
-                }
-            } else if (name.compare(ICAL_BEGIN, -1) == 0) {
-                UBool isDST = (value.compare(ICAL_DAYLIGHT, -1) == 0);
-                if (value.compare(ICAL_STANDARD, -1) == 0 || isDST) {
-                    // tzid must be ready at this point
-                    if (tzid.length() == 0) {
-                        return;
-                    }
-                    // initialize current zone properties
-                    if (dates.size() != 0) {
-                        dates.removeAllElements();
-                    }
-                    isRRULE = false;
-                    from.remove();
-                    to.remove();
-                    zonename.remove();
-                    dst = isDST;
-                    state = TZI;
-                } else {
-                    // BEGIN property other than STANDARD/DAYLIGHT
-                    // must not be there.
-                    return;
-                }
-            } else if (name.compare(ICAL_END, -1) == 0) {
-                break;
-            }
-            break;
-        case TZI:
-            if (name.compare(ICAL_DTSTART, -1) == 0) {
-                dtstart = value;
-            } else if (name.compare(ICAL_TZNAME, -1) == 0) {
-                zonename = value;
-            } else if (name.compare(ICAL_TZOFFSETFROM, -1) == 0) {
-                from = value;
-            } else if (name.compare(ICAL_TZOFFSETTO, -1) == 0) {
-                to = value;
-            } else if (name.compare(ICAL_RDATE, -1) == 0) {
-                // RDATE mixed with RRULE is not supported
-                if (isRRULE) {
-                    return;
-                }
-                // RDATE value may contain multiple date delimited
-                // by comma
-                UBool nextDate = true;
-                int32_t dstart = 0;
-                LocalPointer<UnicodeString> dstr;
-                while (nextDate) {
-                    int32_t dend = value.indexOf(COMMA, dstart);
-                    if (dend == -1) {
-                        dstr.adoptInsteadAndCheckErrorCode(new UnicodeString(value, dstart), status);
-                        nextDate = false;
-                    } else {
-                        dstr.adoptInsteadAndCheckErrorCode(new UnicodeString(value, dstart, dend - dstart), status);
-                    }
-                    dates.adoptElement(dstr.orphan(), status);
-                    if (U_FAILURE(status)) {
-                        return;
-                    }
-                    dstart = dend + 1;
-                }
-            } else if (name.compare(ICAL_RRULE, -1) == 0) {
-                // RRULE mixed with RDATE is not supported
-                if (!isRRULE && dates.size() != 0) {
-                    return;
-                }
-                isRRULE = true;
-                LocalPointer<UnicodeString> element(new UnicodeString(value), status);
-                dates.adoptElement(element.orphan(), status);
-                if (U_FAILURE(status)) {
-                    return;
-                }
-            } else if (name.compare(ICAL_END, -1) == 0) {
-                // Mandatory properties
-                if (dtstart.length() == 0 || from.length() == 0 || to.length() == 0) {
-                    return;
-                }
-                // if zonename is not available, create one from tzid
-                if (zonename.length() == 0) {
-                    getDefaultTZName(tzid, dst, zonename);
-                }
-
-                // create a time zone rule
-                LocalPointer<TimeZoneRule> rule;
-                int32_t fromOffset = 0;
-                int32_t toOffset = 0;
-                int32_t rawOffset = 0;
-                int32_t dstSavings = 0;
-                UDate start = 0;
-
-                // Parse TZOFFSETFROM/TZOFFSETTO
-                fromOffset = offsetStrToMillis(from, status);
-                toOffset = offsetStrToMillis(to, status);
-                if (U_FAILURE(status)) {
-                    return;
-                }
-
-                if (dst) {
-                    // If daylight, use the previous offset as rawoffset if positive
-                    if (toOffset - fromOffset > 0) {
-                        rawOffset = fromOffset;
-                        dstSavings = toOffset - fromOffset;
-                    } else {
-                        // This is rare case..  just use 1 hour DST savings
-                        rawOffset = toOffset - DEF_DSTSAVINGS;
-                        dstSavings = DEF_DSTSAVINGS;
-                    }
-                } else {
-                    rawOffset = toOffset;
-                    dstSavings = 0;
-                }
-
-                // start time
-                start = parseDateTimeString(dtstart, fromOffset, status);
-                if (U_FAILURE(status)) {
-                    return;
-                }
-
-                // Create the rule
-                UDate actualStart = MAX_MILLIS;
-                if (isRRULE) {
-                    rule.adoptInsteadAndCheckErrorCode(
-                        createRuleByRRULE(zonename, rawOffset, dstSavings, start, &dates, fromOffset, status), status);
-                } else {
-                    rule.adoptInsteadAndCheckErrorCode(
-                        createRuleByRDATE(zonename, rawOffset, dstSavings, start, &dates, fromOffset, status), status);
-                }
-                if (U_FAILURE(status)) {
-                    return;
-                } else {
-                    UBool startAvail = rule->getFirstStart(fromOffset, 0, actualStart);
-                    if (startAvail && actualStart < firstStart) {
-                        // save from offset information for the earliest rule
-                        firstStart = actualStart;
-                        // If this is STD, assume the time before this transition
-                        // is DST when the difference is 1 hour.  This might not be
-                        // accurate, but VTIMEZONE data does not have such info.
-                        if (dstSavings > 0) {
-                            initialRawOffset = fromOffset;
-                            initialDSTSavings = 0;
-                        } else {
-                            if (fromOffset - toOffset == DEF_DSTSAVINGS) {
-                                initialRawOffset = fromOffset - DEF_DSTSAVINGS;
-                                initialDSTSavings = DEF_DSTSAVINGS;
-                            } else {
-                                initialRawOffset = fromOffset;
-                                initialDSTSavings = 0;
-                            }
-                        }
-                    }
-                }
-                rules.adoptElement(rule.orphan(), status);
-                if (U_FAILURE(status)) {
-                    return;
-                }
-                state = VTZ;
-            }
-            break;
-        }
-    }
-    // Must have at least one rule
-    if (rules.size() == 0) {
-        return;
-    }
-
-    // Create a initial rule
-    getDefaultTZName(tzid, false, zonename);
-    LocalPointer<InitialTimeZoneRule> initialRule(
-        new InitialTimeZoneRule(zonename, initialRawOffset, initialDSTSavings), status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    // Finally, create the RuleBasedTimeZone
-    // C++ awkwardness on memory allocation failure: the constructor wont be run, meaning
-    // that initialRule wont be adopted/deleted, as it normally would be.
-    LocalPointer<RuleBasedTimeZone> rbtz(
-        new RuleBasedTimeZone(tzid, initialRule.getAlias()), status);
-    if (U_SUCCESS(status)) {
-        initialRule.orphan();
-    } else {
-        return;
-    }
-
-    for (n = 0; n < rules.size(); n++) {
-        TimeZoneRule *r = (TimeZoneRule*)rules.elementAt(n);
-        AnnualTimeZoneRule *atzrule = dynamic_cast<AnnualTimeZoneRule *>(r);
-        if (atzrule != nullptr) {
-            if (atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
-                finalRuleCount++;
-                finalRuleIdx = n;
-            }
-        }
-    }
-    if (finalRuleCount > 2) {
-        // Too many final rules
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-
-    if (finalRuleCount == 1) {
-        if (rules.size() == 1) {
-            // Only one final rule, only governs the initial rule,
-            // which is already initialized, thus, we do not need to
-            // add this transition rule
-            rules.removeAllElements();
-        } else {
-            // Normalize the final rule
-            AnnualTimeZoneRule *finalRule = (AnnualTimeZoneRule*)rules.elementAt(finalRuleIdx);
-            int32_t tmpRaw = finalRule->getRawOffset();
-            int32_t tmpDST = finalRule->getDSTSavings();
-
-            // Find the last non-final rule
-            UDate finalStart, start;
-            finalRule->getFirstStart(initialRawOffset, initialDSTSavings, finalStart);
-            start = finalStart;
-            for (n = 0; n < rules.size(); n++) {
-                if (finalRuleIdx == n) {
-                    continue;
-                }
-                TimeZoneRule *r = (TimeZoneRule*)rules.elementAt(n);
-                UDate lastStart;
-                r->getFinalStart(tmpRaw, tmpDST, lastStart);
-                if (lastStart > start) {
-                    finalRule->getNextStart(lastStart,
-                        r->getRawOffset(),
-                        r->getDSTSavings(),
-                        false,
-                        start);
-                }
-            }
-
-            LocalPointer<TimeZoneRule> newRule;
-            UnicodeString tznam;
-            if (start == finalStart) {
-                // Transform this into a single transition
-                newRule.adoptInsteadAndCheckErrorCode(
-                    new TimeArrayTimeZoneRule(
-                            finalRule->getName(tznam),
-                            finalRule->getRawOffset(),
-                            finalRule->getDSTSavings(),
-                            &finalStart,
-                            1,
-                            DateTimeRule::UTC_TIME),
-                    status);
-            } else {
-                // Update the end year
-                int32_t y, m, d, dow, doy, mid;
-                Grego::timeToFields(start, y, m, d, dow, doy, mid);
-                newRule.adoptInsteadAndCheckErrorCode(
-                    new AnnualTimeZoneRule(
-                            finalRule->getName(tznam),
-                            finalRule->getRawOffset(),
-                            finalRule->getDSTSavings(),
-                            *(finalRule->getRule()),
-                            finalRule->getStartYear(),
-                            y),
-                    status);
-            }
-            if (U_FAILURE(status)) {
-                return;
-            }
-            rules.removeElementAt(finalRuleIdx);
-            rules.adoptElement(newRule.orphan(), status);
-            if (U_FAILURE(status)) {
-                return;
-            }
-        }
-    }
-
-    while (!rules.isEmpty()) {
-        TimeZoneRule *tzr = (TimeZoneRule*)rules.orphanElementAt(0);
-        rbtz->addTransitionRule(tzr, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    }
-    rbtz->complete(status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    tz = rbtz.orphan();
-    setID(tzid);
-}
-
-void
-VTimeZone::write(VTZWriter& writer, UErrorCode& status) const {
-    if (vtzlines != nullptr) {
-        for (int32_t i = 0; i < vtzlines->size(); i++) {
-            UnicodeString *line = (UnicodeString*)vtzlines->elementAt(i);
-            if (line->startsWith(ICAL_TZURL, -1)
-                && line->charAt(u_strlen(ICAL_TZURL)) == COLON) {
-                writer.write(ICAL_TZURL);
-                writer.write(COLON);
-                writer.write(tzurl);
-                writer.write(ICAL_NEWLINE);
-            } else if (line->startsWith(ICAL_LASTMOD, -1)
-                && line->charAt(u_strlen(ICAL_LASTMOD)) == COLON) {
-                UnicodeString utcString;
-                writer.write(ICAL_LASTMOD);
-                writer.write(COLON);
-                writer.write(getUTCDateTimeString(lastmod, utcString));
-                writer.write(ICAL_NEWLINE);
-            } else {
-                writer.write(*line);
-                writer.write(ICAL_NEWLINE);
-            }
-        }
-    } else {
-        UnicodeString icutzprop;
-        UVector customProps(nullptr, uhash_compareUnicodeString, status);
-        if (olsonzid.length() > 0 && icutzver.length() > 0) {
-            icutzprop.append(olsonzid);
-            icutzprop.append(u'[');
-            icutzprop.append(icutzver);
-            icutzprop.append(u']');
-            customProps.addElement(&icutzprop, status);
-        }
-        writeZone(writer, *tz, &customProps, status);
-    }
-}
-
-void
-VTimeZone::write(UDate start, VTZWriter& writer, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    InitialTimeZoneRule *initial = nullptr;
-    UVector *transitionRules = nullptr;
-    UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
-    UnicodeString tzid;
-
-    // Extract rules applicable to dates after the start time
-    getTimeZoneRulesAfter(start, initial, transitionRules, status);
-    LocalPointer<InitialTimeZoneRule> lpInitial(initial);
-    LocalPointer<UVector> lpTransitionRules(transitionRules);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    // Create a RuleBasedTimeZone with the subset rule
-    getID(tzid);
-    RuleBasedTimeZone rbtz(tzid, lpInitial.orphan());
-    if (lpTransitionRules.isValid()) {
-        U_ASSERT(transitionRules->hasDeleter());  // Assumed for U_FAILURE early return, below.
-        while (!lpTransitionRules->isEmpty()) {
-            TimeZoneRule *tr = (TimeZoneRule*)lpTransitionRules->orphanElementAt(0);
-            rbtz.addTransitionRule(tr, status);
-            if (U_FAILURE(status)) {
-                return;
-            }
-        }
-    }
-    rbtz.complete(status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    if (olsonzid.length() > 0 && icutzver.length() > 0) {
-        UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP);
-        if (icutzprop == nullptr) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return;
-        }
-        icutzprop->append(olsonzid);
-        icutzprop->append((UChar)0x005B/*'['*/);
-        icutzprop->append(icutzver);
-        icutzprop->append(ICU_TZINFO_PARTIAL, -1);
-        appendMillis(start, *icutzprop);
-        icutzprop->append((UChar)0x005D/*']'*/);
-        customProps.adoptElement(icutzprop, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    }
-    writeZone(writer, rbtz, &customProps, status);
-}
-
-void
-VTimeZone::writeSimple(UDate time, VTZWriter& writer, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
-    UnicodeString tzid;
-
-    // Extract simple rules
-    InitialTimeZoneRule *initial = nullptr;
-    AnnualTimeZoneRule *std = nullptr, *dst = nullptr;
-    getSimpleRulesNear(time, initial, std, dst, status);
-    LocalPointer<InitialTimeZoneRule> lpInitial(initial);
-    LocalPointer<AnnualTimeZoneRule> lpStd(std);
-    LocalPointer<AnnualTimeZoneRule> lpDst(dst);
-    if (U_SUCCESS(status)) {
-        // Create a RuleBasedTimeZone with the subset rule
-        getID(tzid);
-        RuleBasedTimeZone rbtz(tzid, lpInitial.orphan());
-        if (lpStd.isValid() && lpDst.isValid()) {
-            rbtz.addTransitionRule(lpStd.orphan(), status);
-            rbtz.addTransitionRule(lpDst.orphan(), status);
-        }
-        if (U_FAILURE(status)) {
-            return;
-        }
-
-        if (olsonzid.length() > 0 && icutzver.length() > 0) {
-            LocalPointer<UnicodeString> icutzprop(new UnicodeString(ICU_TZINFO_PROP), status);
-            if (U_FAILURE(status)) {
-               return;
-            }
-            icutzprop->append(olsonzid);
-            icutzprop->append((UChar)0x005B/*'['*/);
-            icutzprop->append(icutzver);
-            icutzprop->append(ICU_TZINFO_SIMPLE, -1);
-            appendMillis(time, *icutzprop);
-            icutzprop->append((UChar)0x005D/*']'*/);
-            customProps.adoptElement(icutzprop.orphan(), status);
-        }
-        writeZone(writer, rbtz, &customProps, status);
-    }
-}
-
-void
-VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz,
-                     UVector* customProps, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    writeHeaders(w, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-
-    if (customProps != nullptr) {
-        for (int32_t i = 0; i < customProps->size(); i++) {
-            UnicodeString *custprop = (UnicodeString*)customProps->elementAt(i);
-            w.write(*custprop);
-            w.write(ICAL_NEWLINE);
-        }
-    }
-
-    UDate t = MIN_MILLIS;
-    UnicodeString dstName;
-    int32_t dstFromOffset = 0;
-    int32_t dstFromDSTSavings = 0;
-    int32_t dstToOffset = 0;
-    int32_t dstStartYear = 0;
-    int32_t dstMonth = 0;
-    int32_t dstDayOfWeek = 0;
-    int32_t dstWeekInMonth = 0;
-    int32_t dstMillisInDay = 0;
-    UDate dstStartTime = 0.0;
-    UDate dstUntilTime = 0.0;
-    int32_t dstCount = 0;
-    AnnualTimeZoneRule *finalDstRule = nullptr;
-
-    UnicodeString stdName;
-    int32_t stdFromOffset = 0;
-    int32_t stdFromDSTSavings = 0;
-    int32_t stdToOffset = 0;
-    int32_t stdStartYear = 0;
-    int32_t stdMonth = 0;
-    int32_t stdDayOfWeek = 0;
-    int32_t stdWeekInMonth = 0;
-    int32_t stdMillisInDay = 0;
-    UDate stdStartTime = 0.0;
-    UDate stdUntilTime = 0.0;
-    int32_t stdCount = 0;
-    AnnualTimeZoneRule *finalStdRule = nullptr;
-
-    int32_t year, month, dom, dow, doy, mid;
-    UBool hasTransitions = false;
-    TimeZoneTransition tzt;
-    UBool tztAvail;
-    UnicodeString name;
-    UBool isDst;
-
-    // Going through all transitions
-    while (true) {
-        tztAvail = basictz.getNextTransition(t, false, tzt);
-        if (!tztAvail) {
-            break;
-        }
-        hasTransitions = true;
-        t = tzt.getTime();
-        tzt.getTo()->getName(name);
-        isDst = (tzt.getTo()->getDSTSavings() != 0);
-        int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
-        int32_t fromDSTSavings = tzt.getFrom()->getDSTSavings();
-        int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
-        Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid);
-        int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
-        UBool sameRule = false;
-        const AnnualTimeZoneRule *atzrule;
-        if (isDst) {
-            if (finalDstRule == nullptr
-                && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != nullptr
-                && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
-            ) {
-                finalDstRule = atzrule->clone();
-            }
-            if (dstCount > 0) {
-                if (year == dstStartYear + dstCount
-                        && name.compare(dstName) == 0
-                        && dstFromOffset == fromOffset
-                        && dstToOffset == toOffset
-                        && dstMonth == month
-                        && dstDayOfWeek == dow
-                        && dstWeekInMonth == weekInMonth
-                        && dstMillisInDay == mid) {
-                    // Update until time
-                    dstUntilTime = t;
-                    dstCount++;
-                    sameRule = true;
-                }
-                if (!sameRule) {
-                    if (dstCount == 1) {
-                        writeZonePropsByTime(w, true, dstName, dstFromOffset, dstToOffset, dstStartTime,
-                                true, status);
-                    } else {
-                        writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
-                                dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
-                    }
-                    if (U_FAILURE(status)) {
-                        goto cleanupWriteZone;
-                    }
-                }
-            }
-            if (!sameRule) {
-                // Reset this DST information
-                dstName = name;
-                dstFromOffset = fromOffset;
-                dstFromDSTSavings = fromDSTSavings;
-                dstToOffset = toOffset;
-                dstStartYear = year;
-                dstMonth = month;
-                dstDayOfWeek = dow;
-                dstWeekInMonth = weekInMonth;
-                dstMillisInDay = mid;
-                dstStartTime = dstUntilTime = t;
-                dstCount = 1;
-            }
-            if (finalStdRule != nullptr && finalDstRule != nullptr) {
-                break;
-            }
-        } else {
-            if (finalStdRule == nullptr
-                && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != nullptr
-                && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
-            ) {
-                finalStdRule = atzrule->clone();
-            }
-            if (stdCount > 0) {
-                if (year == stdStartYear + stdCount
-                        && name.compare(stdName) == 0
-                        && stdFromOffset == fromOffset
-                        && stdToOffset == toOffset
-                        && stdMonth == month
-                        && stdDayOfWeek == dow
-                        && stdWeekInMonth == weekInMonth
-                        && stdMillisInDay == mid) {
-                    // Update until time
-                    stdUntilTime = t;
-                    stdCount++;
-                    sameRule = true;
-                }
-                if (!sameRule) {
-                    if (stdCount == 1) {
-                        writeZonePropsByTime(w, false, stdName, stdFromOffset, stdToOffset, stdStartTime,
-                                true, status);
-                    } else {
-                        writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
-                                stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
-                    }
-                    if (U_FAILURE(status)) {
-                        goto cleanupWriteZone;
-                    }
-                }
-            }
-            if (!sameRule) {
-                // Reset this STD information
-                stdName = name;
-                stdFromOffset = fromOffset;
-                stdFromDSTSavings = fromDSTSavings;
-                stdToOffset = toOffset;
-                stdStartYear = year;
-                stdMonth = month;
-                stdDayOfWeek = dow;
-                stdWeekInMonth = weekInMonth;
-                stdMillisInDay = mid;
-                stdStartTime = stdUntilTime = t;
-                stdCount = 1;
-            }
-            if (finalStdRule != nullptr && finalDstRule != nullptr) {
-                break;
-            }
-        }
-    }
-    if (!hasTransitions) {
-        // No transition - put a single non transition RDATE
-        int32_t raw, dst, offset;
-        basictz.getOffset(0.0/*any time*/, false, raw, dst, status);
-        if (U_FAILURE(status)) {
-            goto cleanupWriteZone;
-        }
-        offset = raw + dst;
-        isDst = (dst != 0);
-        UnicodeString tzid;
-        basictz.getID(tzid);
-        getDefaultTZName(tzid, isDst, name);
-        writeZonePropsByTime(w, isDst, name,
-                offset, offset, DEF_TZSTARTTIME - offset, false, status);
-        if (U_FAILURE(status)) {
-            goto cleanupWriteZone;
-        }
-    } else {
-        if (dstCount > 0) {
-            if (finalDstRule == nullptr) {
-                if (dstCount == 1) {
-                    writeZonePropsByTime(w, true, dstName, dstFromOffset, dstToOffset, dstStartTime,
-                            true, status);
-                } else {
-                    writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
-                            dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
-                }
-                if (U_FAILURE(status)) {
-                    goto cleanupWriteZone;
-                }
-            } else {
-                if (dstCount == 1) {
-                    writeFinalRule(w, true, finalDstRule,
-                            dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime, status);
-                } else {
-                    // Use a single rule if possible
-                    if (isEquivalentDateRule(dstMonth, dstWeekInMonth, dstDayOfWeek, finalDstRule->getRule())) {
-                        writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
-                                dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, MAX_MILLIS, status);
-                    } else {
-                        // Not equivalent rule - write out two different rules
-                        writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
-                                dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
-                        if (U_FAILURE(status)) {
-                            goto cleanupWriteZone;
-                        }
-                        UDate nextStart;
-                        UBool nextStartAvail = finalDstRule->getNextStart(dstUntilTime, dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, false, nextStart);
-                        U_ASSERT(nextStartAvail);
-                        if (nextStartAvail) {
-                            writeFinalRule(w, true, finalDstRule,
-                                    dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, nextStart, status);
-                        }
-                    }
-                }
-                if (U_FAILURE(status)) {
-                    goto cleanupWriteZone;
-                }
-            }
-        }
-        if (stdCount > 0) {
-            if (finalStdRule == nullptr) {
-                if (stdCount == 1) {
-                    writeZonePropsByTime(w, false, stdName, stdFromOffset, stdToOffset, stdStartTime,
-                            true, status);
-                } else {
-                    writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
-                            stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
-                }
-                if (U_FAILURE(status)) {
-                    goto cleanupWriteZone;
-                }
-            } else {
-                if (stdCount == 1) {
-                    writeFinalRule(w, false, finalStdRule,
-                            stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime, status);
-                } else {
-                    // Use a single rule if possible
-                    if (isEquivalentDateRule(stdMonth, stdWeekInMonth, stdDayOfWeek, finalStdRule->getRule())) {
-                        writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
-                                stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, MAX_MILLIS, status);
-                    } else {
-                        // Not equivalent rule - write out two different rules
-                        writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
-                                stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
-                        if (U_FAILURE(status)) {
-                            goto cleanupWriteZone;
-                        }
-                        UDate nextStart;
-                        UBool nextStartAvail = finalStdRule->getNextStart(stdUntilTime, stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, false, nextStart);
-                        U_ASSERT(nextStartAvail);
-                        if (nextStartAvail) {
-                            writeFinalRule(w, false, finalStdRule,
-                                    stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, nextStart, status);
-                        }
-                    }
-                }
-                if (U_FAILURE(status)) {
-                    goto cleanupWriteZone;
-                }
-            }
-        }
-    }
-    writeFooter(w, status);
-
-cleanupWriteZone:
-
-    if (finalStdRule != nullptr) {
-        delete finalStdRule;
-    }
-    if (finalDstRule != nullptr) {
-        delete finalDstRule;
-    }
-}
-
-void
-VTimeZone::writeHeaders(VTZWriter& writer, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    UnicodeString tzid;
-    tz->getID(tzid);
-
-    writer.write(ICAL_BEGIN);
-    writer.write(COLON);
-    writer.write(ICAL_VTIMEZONE);
-    writer.write(ICAL_NEWLINE);
-    writer.write(ICAL_TZID);
-    writer.write(COLON);
-    writer.write(tzid);
-    writer.write(ICAL_NEWLINE);
-    if (tzurl.length() != 0) {
-        writer.write(ICAL_TZURL);
-        writer.write(COLON);
-        writer.write(tzurl);
-        writer.write(ICAL_NEWLINE);
-    }
-    if (lastmod != MAX_MILLIS) {
-        UnicodeString lastmodStr;
-        writer.write(ICAL_LASTMOD);
-        writer.write(COLON);
-        writer.write(getUTCDateTimeString(lastmod, lastmodStr));
-        writer.write(ICAL_NEWLINE);
-    }
-}
-
-/*
- * Write the closing section of the VTIMEZONE definition block
- */
-void
-VTimeZone::writeFooter(VTZWriter& writer, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    writer.write(ICAL_END);
-    writer.write(COLON);
-    writer.write(ICAL_VTIMEZONE);
-    writer.write(ICAL_NEWLINE);
-}
-
-/*
- * Write a single start time
- */
-void
-VTimeZone::writeZonePropsByTime(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
-                                int32_t fromOffset, int32_t toOffset, UDate time, UBool withRDATE,
-                                UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, time, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (withRDATE) {
-        writer.write(ICAL_RDATE);
-        writer.write(COLON);
-        UnicodeString timestr;
-        writer.write(getDateTimeString(time + fromOffset, timestr));
-        writer.write(ICAL_NEWLINE);
-    }
-    endZoneProps(writer, isDst, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-}
-
-/*
- * Write start times defined by a DOM rule using VTIMEZONE RRULE
- */
-void
-VTimeZone::writeZonePropsByDOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
-                               int32_t fromOffset, int32_t toOffset,
-                               int32_t month, int32_t dayOfMonth, UDate startTime, UDate untilTime,
-                               UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    beginRRULE(writer, month, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    writer.write(ICAL_BYMONTHDAY);
-    writer.write(EQUALS_SIGN);
-    UnicodeString dstr;
-    appendAsciiDigits(dayOfMonth, 0, dstr);
-    writer.write(dstr);
-    if (untilTime != MAX_MILLIS) {
-        appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    }
-    writer.write(ICAL_NEWLINE);
-    endZoneProps(writer, isDst, status);
-}
-
-/*
- * Write start times defined by a DOW rule using VTIMEZONE RRULE
- */
-void
-VTimeZone::writeZonePropsByDOW(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
-                               int32_t fromOffset, int32_t toOffset,
-                               int32_t month, int32_t weekInMonth, int32_t dayOfWeek,
-                               UDate startTime, UDate untilTime, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    beginRRULE(writer, month, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    writer.write(ICAL_BYDAY);
-    writer.write(EQUALS_SIGN);
-    UnicodeString dstr;
-    appendAsciiDigits(weekInMonth, 0, dstr);
-    writer.write(dstr);    // -4, -3, -2, -1, 1, 2, 3, 4
-    writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]);    // SU, MO, TU...
-
-    if (untilTime != MAX_MILLIS) {
-        appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    }
-    writer.write(ICAL_NEWLINE);
-    endZoneProps(writer, isDst, status);
-}
-
-/*
- * Write start times defined by a DOW_GEQ_DOM rule using VTIMEZONE RRULE
- */
-void
-VTimeZone::writeZonePropsByDOW_GEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
-                                       int32_t fromOffset, int32_t toOffset,
-                                       int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
-                                       UDate startTime, UDate untilTime, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    // Check if this rule can be converted to DOW rule
-    if (dayOfMonth%7 == 1) {
-        // Can be represented by DOW rule
-        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
-                month, (dayOfMonth + 6)/7, dayOfWeek, startTime, untilTime, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 6) {
-        // Can be represented by DOW rule with negative week number
-        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
-                month, -1*((MONTHLENGTH[month] - dayOfMonth + 1)/7), dayOfWeek, startTime, untilTime, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    } else {
-        // Otherwise, use BYMONTHDAY to include all possible dates
-        beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-        // Check if all days are in the same month
-        int32_t startDay = dayOfMonth;
-        int32_t currentMonthDays = 7;
-
-        if (dayOfMonth <= 0) {
-            // The start day is in previous month
-            int32_t prevMonthDays = 1 - dayOfMonth;
-            currentMonthDays -= prevMonthDays;
-
-            int32_t prevMonth = (month - 1) < 0 ? 11 : month - 1;
-
-            // Note: When a rule is separated into two, UNTIL attribute needs to be
-            // calculated for each of them.  For now, we skip this, because we basically use this method
-            // only for final rules, which does not have the UNTIL attribute
-            writeZonePropsByDOW_GEQ_DOM_sub(writer, prevMonth, -prevMonthDays, dayOfWeek, prevMonthDays,
-                MAX_MILLIS /* Do not use UNTIL */, fromOffset, status);
-            if (U_FAILURE(status)) {
-                return;
-            }
-
-            // Start from 1 for the rest
-            startDay = 1;
-        } else if (dayOfMonth + 6 > MONTHLENGTH[month]) {
-            // Note: This code does not actually work well in February.  For now, days in month in
-            // non-leap year.
-            int32_t nextMonthDays = dayOfMonth + 6 - MONTHLENGTH[month];
-            currentMonthDays -= nextMonthDays;
-
-            int32_t nextMonth = (month + 1) > 11 ? 0 : month + 1;
-
-            writeZonePropsByDOW_GEQ_DOM_sub(writer, nextMonth, 1, dayOfWeek, nextMonthDays,
-                MAX_MILLIS /* Do not use UNTIL */, fromOffset, status);
-            if (U_FAILURE(status)) {
-                return;
-            }
-        }
-        writeZonePropsByDOW_GEQ_DOM_sub(writer, month, startDay, dayOfWeek, currentMonthDays,
-            untilTime, fromOffset, status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-        endZoneProps(writer, isDst, status);
-    }
-}
-
-/*
- * Called from writeZonePropsByDOW_GEQ_DOM
- */
-void
-VTimeZone::writeZonePropsByDOW_GEQ_DOM_sub(VTZWriter& writer, int32_t month, int32_t dayOfMonth,
-                                           int32_t dayOfWeek, int32_t numDays,
-                                           UDate untilTime, int32_t fromOffset, UErrorCode& status) const {
-
-    if (U_FAILURE(status)) {
-        return;
-    }
-    int32_t startDayNum = dayOfMonth;
-    UBool isFeb = (month == UCAL_FEBRUARY);
-    if (dayOfMonth < 0 && !isFeb) {
-        // Use positive number if possible
-        startDayNum = MONTHLENGTH[month] + dayOfMonth + 1;
-    }
-    beginRRULE(writer, month, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    writer.write(ICAL_BYDAY);
-    writer.write(EQUALS_SIGN);
-    writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]);    // SU, MO, TU...
-    writer.write(SEMICOLON);
-    writer.write(ICAL_BYMONTHDAY);
-    writer.write(EQUALS_SIGN);
-
-    UnicodeString dstr;
-    appendAsciiDigits(startDayNum, 0, dstr);
-    writer.write(dstr);
-    for (int32_t i = 1; i < numDays; i++) {
-        writer.write(COMMA);
-        dstr.remove();
-        appendAsciiDigits(startDayNum + i, 0, dstr);
-        writer.write(dstr);
-    }
-
-    if (untilTime != MAX_MILLIS) {
-        appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
-        if (U_FAILURE(status)) {
-            return;
-        }
-    }
-    writer.write(ICAL_NEWLINE);
-}
-
-/*
- * Write start times defined by a DOW_LEQ_DOM rule using VTIMEZONE RRULE
- */
-void
-VTimeZone::writeZonePropsByDOW_LEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
-                                       int32_t fromOffset, int32_t toOffset,
-                                       int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
-                                       UDate startTime, UDate untilTime, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    // Check if this rule can be converted to DOW rule
-    if (dayOfMonth%7 == 0) {
-        // Can be represented by DOW rule
-        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
-                month, dayOfMonth/7, dayOfWeek, startTime, untilTime, status);
-    } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 0){
-        // Can be represented by DOW rule with negative week number
-        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
-                month, -1*((MONTHLENGTH[month] - dayOfMonth)/7 + 1), dayOfWeek, startTime, untilTime, status);
-    } else if (month == UCAL_FEBRUARY && dayOfMonth == 29) {
-        // Special case for February
-        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
-                UCAL_FEBRUARY, -1, dayOfWeek, startTime, untilTime, status);
-    } else {
-        // Otherwise, convert this to DOW_GEQ_DOM rule
-        writeZonePropsByDOW_GEQ_DOM(writer, isDst, zonename, fromOffset, toOffset,
-                month, dayOfMonth - 6, dayOfWeek, startTime, untilTime, status);
-    }
-}
-
-/*
- * Write the final time zone rule using RRULE, with no UNTIL attribute
- */
-void
-VTimeZone::writeFinalRule(VTZWriter& writer, UBool isDst, const AnnualTimeZoneRule* rule,
-                          int32_t fromRawOffset, int32_t fromDSTSavings,
-                          UDate startTime, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    UBool modifiedRule = true;
-    const DateTimeRule *dtrule = toWallTimeRule(rule->getRule(), fromRawOffset, fromDSTSavings, status);
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (dtrule == nullptr) {
-        modifiedRule = false;
-        dtrule = rule->getRule();
-    }
-
-    // If the rule's mills in a day is out of range, adjust start time.
-    // Olson tzdata supports 24:00 of a day, but VTIMEZONE does not.
-    // See ticket#7008/#7518
-
-    int32_t timeInDay = dtrule->getRuleMillisInDay();
-    if (timeInDay < 0) {
-        startTime = startTime + (0 - timeInDay);
-    } else if (timeInDay >= U_MILLIS_PER_DAY) {
-        startTime = startTime - (timeInDay - (U_MILLIS_PER_DAY - 1));
-    }
-
-    int32_t toOffset = rule->getRawOffset() + rule->getDSTSavings();
-    UnicodeString name;
-    rule->getName(name);
-    switch (dtrule->getDateRuleType()) {
-    case DateTimeRule::DOM:
-        writeZonePropsByDOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
-                dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), startTime, MAX_MILLIS, status);
-        break;
-    case DateTimeRule::DOW:
-        writeZonePropsByDOW(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
-                dtrule->getRuleMonth(), dtrule->getRuleWeekInMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
-        break;
-    case DateTimeRule::DOW_GEQ_DOM:
-        writeZonePropsByDOW_GEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
-                dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
-        break;
-    case DateTimeRule::DOW_LEQ_DOM:
-        writeZonePropsByDOW_LEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
-                dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
-        break;
-    }
-    if (modifiedRule) {
-        delete dtrule;
-    }
-}
-
-/*
- * Write the opening section of zone properties
- */
-void
-VTimeZone::beginZoneProps(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
-                          int32_t fromOffset, int32_t toOffset, UDate startTime, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    writer.write(ICAL_BEGIN);
-    writer.write(COLON);
-    if (isDst) {
-        writer.write(ICAL_DAYLIGHT);
-    } else {
-        writer.write(ICAL_STANDARD);
-    }
-    writer.write(ICAL_NEWLINE);
-
-    UnicodeString dstr;
-
-    // TZOFFSETTO
-    writer.write(ICAL_TZOFFSETTO);
-    writer.write(COLON);
-    millisToOffset(toOffset, dstr);
-    writer.write(dstr);
-    writer.write(ICAL_NEWLINE);
-
-    // TZOFFSETFROM
-    writer.write(ICAL_TZOFFSETFROM);
-    writer.write(COLON);
-    millisToOffset(fromOffset, dstr);
-    writer.write(dstr);
-    writer.write(ICAL_NEWLINE);
-
-    // TZNAME
-    writer.write(ICAL_TZNAME);
-    writer.write(COLON);
-    writer.write(zonename);
-    writer.write(ICAL_NEWLINE);
-
-    // DTSTART
-    writer.write(ICAL_DTSTART);
-    writer.write(COLON);
-    writer.write(getDateTimeString(startTime + fromOffset, dstr));
-    writer.write(ICAL_NEWLINE);
-}
-
-/*
- * Writes the closing section of zone properties
- */
-void
-VTimeZone::endZoneProps(VTZWriter& writer, UBool isDst, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    // END:STANDARD or END:DAYLIGHT
-    writer.write(ICAL_END);
-    writer.write(COLON);
-    if (isDst) {
-        writer.write(ICAL_DAYLIGHT);
-    } else {
-        writer.write(ICAL_STANDARD);
-    }
-    writer.write(ICAL_NEWLINE);
-}
-
-/*
- * Write the beginning part of RRULE line
- */
-void
-VTimeZone::beginRRULE(VTZWriter& writer, int32_t month, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    UnicodeString dstr;
-    writer.write(ICAL_RRULE);
-    writer.write(COLON);
-    writer.write(ICAL_FREQ);
-    writer.write(EQUALS_SIGN);
-    writer.write(ICAL_YEARLY);
-    writer.write(SEMICOLON);
-    writer.write(ICAL_BYMONTH);
-    writer.write(EQUALS_SIGN);
-    appendAsciiDigits(month + 1, 0, dstr);
-    writer.write(dstr);
-    writer.write(SEMICOLON);
-}
-
-/*
- * Append the UNTIL attribute after RRULE line
- */
-void
-VTimeZone::appendUNTIL(VTZWriter& writer, const UnicodeString& until,  UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (until.length() > 0) {
-        writer.write(SEMICOLON);
-        writer.write(ICAL_UNTIL);
-        writer.write(EQUALS_SIGN);
-        writer.write(until);
-    }
-}
-
-U_NAMESPACE_END
-
-#endif /* #if !UCONFIG_NO_FORMATTING */
-
-//eof
diff --git a/libs/icui18n/vzone.cpp b/libs/icui18n/vzone.cpp
deleted file mode 100644
index 6db3ba04c581..000000000000
--- a/libs/icui18n/vzone.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 2009-2011, International Business Machines Corporation and
-* others. All Rights Reserved.
-*******************************************************************************
-*/
-
-/**
- * \file
- * \brief C API: VTimeZone classes
- */
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/uobject.h"
-#include "vzone.h"
-#include "unicode/vtzone.h"
-#include "cmemory.h"
-#include "unicode/ustring.h"
-#include "unicode/parsepos.h"
-
-U_NAMESPACE_USE
-
-U_CAPI VZone* U_EXPORT2
-vzone_openID(const UChar* ID, int32_t idLength){
-    UnicodeString s(idLength==-1, ID, idLength);
-    return (VZone*) (VTimeZone::createVTimeZoneByID(s));
-}
-
-U_CAPI VZone* U_EXPORT2
-vzone_openData(const UChar* vtzdata, int32_t vtzdataLength, UErrorCode& status) {
-    UnicodeString s(vtzdataLength==-1, vtzdata, vtzdataLength);
-    return (VZone*) (VTimeZone::createVTimeZone(s,status));
-}
-
-U_CAPI void U_EXPORT2
-vzone_close(VZone* zone) {
-    delete (VTimeZone*)zone;
-}
-
-U_CAPI VZone* U_EXPORT2
-vzone_clone(const VZone *zone) {
-    return (VZone*) (((VTimeZone*)zone)->VTimeZone::clone());
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_equals(const VZone* zone1, const VZone* zone2) {
-    return *(const VTimeZone*)zone1 == *(const VTimeZone*)zone2;
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_getTZURL(VZone* zone, UChar* & url, int32_t & urlLength) {
-    UnicodeString s;
-    UBool b = ((VTimeZone*)zone)->VTimeZone::getTZURL(s);
-
-    urlLength = s.length();
-    memcpy(url,s.getBuffer(),urlLength);
-
-    return b;
-}
-
-U_CAPI void U_EXPORT2
-vzone_setTZURL(VZone* zone, UChar* url, int32_t urlLength) {
-    UnicodeString s(urlLength==-1, url, urlLength);
-    ((VTimeZone*)zone)->VTimeZone::setTZURL(s);
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_getLastModified(VZone* zone, UDate& lastModified) {
-    return ((VTimeZone*)zone)->VTimeZone::getLastModified(lastModified);
-}
-
-U_CAPI void U_EXPORT2
-vzone_setLastModified(VZone* zone, UDate lastModified) {
-    return ((VTimeZone*)zone)->VTimeZone::setLastModified(lastModified);
-}
-
-U_CAPI void U_EXPORT2
-vzone_write(VZone* zone, UChar* & result, int32_t & resultLength, UErrorCode& status) {
-    UnicodeString s;
-    ((VTimeZone*)zone)->VTimeZone::write(s, status);
-
-    resultLength = s.length();
-    result = (UChar*)uprv_malloc(resultLength);
-    memcpy(result,s.getBuffer(),resultLength);
-
-    return;
-}
-
-U_CAPI void U_EXPORT2
-vzone_writeFromStart(VZone* zone, UDate start, UChar* & result, int32_t & resultLength, UErrorCode& status) {
-    UnicodeString s;
-    ((VTimeZone*)zone)->VTimeZone::write(start, s, status);
-
-    resultLength = s.length();
-    result = (UChar*)uprv_malloc(resultLength);
-    memcpy(result,s.getBuffer(),resultLength);
-
-    return;
-}
-
-U_CAPI void U_EXPORT2
-vzone_writeSimple(VZone* zone, UDate time, UChar* & result, int32_t & resultLength, UErrorCode& status) {
-    UnicodeString s;
-    ((VTimeZone*)zone)->VTimeZone::writeSimple(time, s, status);
-
-    resultLength = s.length();
-    result = (UChar*)uprv_malloc(resultLength);
-    memcpy(result,s.getBuffer(),resultLength);
-
-    return;
-}
-
-U_CAPI int32_t U_EXPORT2
-vzone_getOffset(VZone* zone, uint8_t era, int32_t year, int32_t month, int32_t day,
-                uint8_t dayOfWeek, int32_t millis, UErrorCode& status) {
-    return ((VTimeZone*)zone)->VTimeZone::getOffset(era, year, month, day, dayOfWeek, millis, status);
-}
-
-U_CAPI int32_t U_EXPORT2
-vzone_getOffset2(VZone* zone, uint8_t era, int32_t year, int32_t month, int32_t day,
-                uint8_t dayOfWeek, int32_t millis,
-                int32_t monthLength, UErrorCode& status) {
-    return ((VTimeZone*)zone)->VTimeZone::getOffset(era, year, month, day, dayOfWeek, millis, monthLength, status);
-}
-
-U_CAPI void U_EXPORT2
-vzone_getOffset3(VZone* zone, UDate date, UBool local, int32_t& rawOffset,
-                int32_t& dstOffset, UErrorCode& ec) {
-    return ((VTimeZone*)zone)->VTimeZone::getOffset(date, local, rawOffset, dstOffset, ec);
-}
-
-U_CAPI void U_EXPORT2
-vzone_setRawOffset(VZone* zone, int32_t offsetMillis) {
-    return ((VTimeZone*)zone)->VTimeZone::setRawOffset(offsetMillis);
-}
-
-U_CAPI int32_t U_EXPORT2
-vzone_getRawOffset(VZone* zone) {
-    return ((VTimeZone*)zone)->VTimeZone::getRawOffset();
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_useDaylightTime(VZone* zone) {
-    return ((VTimeZone*)zone)->VTimeZone::useDaylightTime();
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_inDaylightTime(VZone* zone, UDate date, UErrorCode& status) {
-    return ((VTimeZone*)zone)->VTimeZone::inDaylightTime(date, status);
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_hasSameRules(VZone* zone, const VZone* other) {
-    return ((VTimeZone*)zone)->VTimeZone::hasSameRules(*(VTimeZone*)other);
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_getNextTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result) {
-    return ((VTimeZone*)zone)->VTimeZone::getNextTransition(base, inclusive, *(TimeZoneTransition*)result);
-}
-
-U_CAPI UBool U_EXPORT2
-vzone_getPreviousTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result) {
-    return ((VTimeZone*)zone)->VTimeZone::getPreviousTransition(base, inclusive, *(TimeZoneTransition*)result);
-}
-
-U_CAPI int32_t U_EXPORT2
-vzone_countTransitionRules(VZone* zone, UErrorCode& status) {
-    return ((VTimeZone*)zone)->VTimeZone::countTransitionRules(status);
-}
-
-U_CAPI UClassID U_EXPORT2
-vzone_getStaticClassID(VZone* zone) {
-    return ((VTimeZone*)zone)->VTimeZone::getStaticClassID();
-}
-
-U_CAPI UClassID U_EXPORT2
-vzone_getDynamicClassID(VZone* zone) {
-    return ((VTimeZone*)zone)->VTimeZone::getDynamicClassID();
-}
-
-#endif
diff --git a/libs/icui18n/vzone.h b/libs/icui18n/vzone.h
deleted file mode 100644
index 0b1dd93bdb6d..000000000000
--- a/libs/icui18n/vzone.h
+++ /dev/null
@@ -1,361 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 2009-2016, International Business Machines Corporation and
-* others. All Rights Reserved.
-*******************************************************************************
-*/
-
-/**
-* \file
-* \brief C API: RFC2445 VTIMEZONE support
-*
-* <p>This is a C wrapper around the C++ VTimeZone class.</p>
-*/
-
-#ifndef __VZONE_H
-#define __VZONE_H
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/uobject.h"
-#include "ztrans.h"
-
-struct VZone;
-/**
- * A UnicodeSet.  Use the vzone_* API to manipulate.  Create with
- * vzone_open*, and destroy with vzone_close.
- */
-typedef struct VZone VZone;
-
-/*********************************************************************
- * VZone API
- *********************************************************************/
-
-/**
- * Creates a vzone from the given time zone ID.
- * @param ID The time zone ID, such as America/New_York
- * @param idLength, length of the ID parameter
- * @return A vzone object initialized by the time zone ID,
- * or NULL when the ID is unknown.
- */
-U_CAPI VZone* U_EXPORT2
-vzone_openID(const UChar* ID, int32_t idLength);
-
-/**
- * Create a vzone instance by RFC2445 VTIMEZONE data
- * @param vtzdata The string including VTIMEZONE data block
- * @param vtzdataLength, length of the vtzdata
- * @param status Output param to filled in with a success or an error.
- * @return A vzone initialized by the VTIMEZONE data or
- * NULL if failed to load the rule from the VTIMEZONE data.
- */
-U_CAPI VZone* U_EXPORT2
-vzone_openData(const UChar* vtzdata, int32_t vtzdataLength, UErrorCode& status);
-
-/**
- * Disposes of the storage used by a VZone object.  This function should
- * be called exactly once for objects returned by vzone_open*.
- * @param set the object to dispose of
- */
-U_CAPI void U_EXPORT2
-vzone_close(VZone* zone);
-
-/**
- * Returns a copy of this object.
- * @param zone the original vzone
- * @return the newly allocated copy of the vzone
- */
-U_CAPI VZone* U_EXPORT2
-vzone_clone(const VZone *zone);
-
-/**
- * Returns true if zone1 is identical to zone2
- * and vis versa.
- * @param zone1 to be checked for containment
- * @param zone2 to be checked for containment
- * @return true if the test condition is met
- */
-U_CAPI UBool U_EXPORT2
-vzone_equals(const VZone* zone1, const VZone* zone2);
-
-/**
- * Gets the RFC2445 TZURL property value.  When a vzone instance was
- * created from VTIMEZONE data, the initial value is set by the TZURL
- * property value in the data.  Otherwise, the initial value is not set.
- * @param zone, the vzone to use
- * @param url Receives the RFC2445 TZURL property value.
- * @param urlLength, length of the url
- * @return true if TZURL attribute is available and value is set.
- */
-U_CAPI UBool U_EXPORT2
-vzone_getTZURL(VZone* zone, UChar* & url, int32_t & urlLength);
-
-/**
- * Sets the RFC2445 TZURL property value.
- * @param zone, the vzone to use
- * @param url The TZURL property value.
- * @param urlLength, length of the url
- */
-U_CAPI void U_EXPORT2
-vzone_setTZURL(VZone* zone, UChar* url, int32_t urlLength);
-
-/**
- * Gets the RFC2445 LAST-MODIFIED property value.  When a vzone instance
- * was created from VTIMEZONE data, the initial value is set by the
- * LAST-MODIFIED property value in the data.  Otherwise, the initial value
- * is not set.
- * @param zone, the vzone to use
- * @param lastModified Receives the last modified date.
- * @return true if lastModified attribute is available and value is set.
- */
-U_CAPI UBool U_EXPORT2
-vzone_getLastModified(VZone* zone, UDate& lastModified);
-
-/**
- * Sets the RFC2445 LAST-MODIFIED property value.
- * @param zone, the vzone to use
- * @param lastModified The LAST-MODIFIED date.
- */
-U_CAPI void U_EXPORT2
-vzone_setLastModified(VZone* zone, UDate lastModified);
-
-/**
- * Writes RFC2445 VTIMEZONE data for this time zone
- * @param zone, the vzone to use
- * @param result Output param to filled in with the VTIMEZONE data.
- * @param resultLength, length of the result output
- * @param status Output param to filled in with a success or an error.
- */
-U_CAPI void U_EXPORT2
-vzone_write(VZone* zone, UChar* & result, int32_t & resultLength, UErrorCode& status);
-
-/**
- * Writes RFC2445 VTIMEZONE data for this time zone applicable
- * for dates after the specified start time.
- * @param zone, the vzone to use
- * @param start The start date.
- * @param result Output param to filled in with the VTIMEZONE data.
- * @param resultLength, length of the result output
- * @param status Output param to filled in with a success or an error.
- */
-U_CAPI void U_EXPORT2
-vzone_writeFromStart(VZone* zone, UDate start, UChar* & result, int32_t & resultLength, UErrorCode& status);
-
-/**
- * Writes RFC2445 VTIMEZONE data applicable for the specified date.
- * Some common iCalendar implementations can only handle a single time
- * zone property or a pair of standard and daylight time properties using
- * BYDAY rule with day of week (such as BYDAY=1SUN).  This method produce
- * the VTIMEZONE data which can be handled these implementations.  The rules
- * produced by this method can be used only for calculating time zone offset
- * around the specified date.
- * @param zone, the vzone to use
- * @param time The date used for rule extraction.
- * @param result Output param to filled in with the VTIMEZONE data.
- * @param status Output param to filled in with a success or an error.
- */
-U_CAPI void U_EXPORT2
-vzone_writeSimple(VZone* zone, UDate time, UChar* & result, int32_t & resultLength, UErrorCode& status);
-
-/**
- * Returns the TimeZone's adjusted GMT offset (i.e., the number of milliseconds to add
- * to GMT to get local time in this time zone, taking daylight savings time into
- * account) as of a particular reference date.  The reference date is used to determine
- * whether daylight savings time is in effect and needs to be figured into the offset
- * that is returned (in other words, what is the adjusted GMT offset in this time zone
- * at this particular date and time?).  For the time zones produced by createTimeZone(),
- * the reference data is specified according to the Gregorian calendar, and the date
- * and time fields are local standard time.
- *
- * <p>Note: Don't call this method. Instead, call the getOffset(UDate...) overload,
- * which returns both the raw and the DST offset for a given time. This method
- * is retained only for backward compatibility.
- *
- * @param zone, the vzone to use
- * @param era        The reference date's era
- * @param year       The reference date's year
- * @param month      The reference date's month (0-based; 0 is January)
- * @param day        The reference date's day-in-month (1-based)
- * @param dayOfWeek  The reference date's day-of-week (1-based; 1 is Sunday)
- * @param millis     The reference date's milliseconds in day, local standard time
- * @param status     Output param to filled in with a success or an error.
- * @return           The offset in milliseconds to add to GMT to get local time.
- */
-U_CAPI int32_t U_EXPORT2
-vzone_getOffset(VZone* zone, uint8_t era, int32_t year, int32_t month, int32_t day,
-                uint8_t dayOfWeek, int32_t millis, UErrorCode& status);
-
-/**
- * Gets the time zone offset, for current date, modified in case of
- * daylight savings. This is the offset to add *to* UTC to get local time.
- *
- * <p>Note: Don't call this method. Instead, call the getOffset(UDate...) overload,
- * which returns both the raw and the DST offset for a given time. This method
- * is retained only for backward compatibility.
- *
- * @param zone, the vzone to use
- * @param era        The reference date's era
- * @param year       The reference date's year
- * @param month      The reference date's month (0-based; 0 is January)
- * @param day        The reference date's day-in-month (1-based)
- * @param dayOfWeek  The reference date's day-of-week (1-based; 1 is Sunday)
- * @param millis     The reference date's milliseconds in day, local standard time
- * @param monthLength The length of the given month in days.
- * @param status     Output param to filled in with a success or an error.
- * @return           The offset in milliseconds to add to GMT to get local time.
- */
-U_CAPI int32_t U_EXPORT2
-vzone_getOffset2(VZone* zone, uint8_t era, int32_t year, int32_t month, int32_t day,
-                uint8_t dayOfWeek, int32_t millis,
-                int32_t monthLength, UErrorCode& status);
-
-/**
- * Returns the time zone raw and GMT offset for the given moment
- * in time.  Upon return, local-millis = GMT-millis + rawOffset +
- * dstOffset.  All computations are performed in the proleptic
- * Gregorian calendar.  The default implementation in the TimeZone
- * class delegates to the 8-argument getOffset().
- *
- * @param zone, the vzone to use
- * @param date moment in time for which to return offsets, in
- * units of milliseconds from January 1, 1970 0:00 GMT, either GMT
- * time or local wall time, depending on `local'.
- * @param local if true, `date' is local wall time; otherwise it
- * is in GMT time.
- * @param rawOffset output parameter to receive the raw offset, that
- * is, the offset not including DST adjustments
- * @param dstOffset output parameter to receive the DST offset,
- * that is, the offset to be added to `rawOffset' to obtain the
- * total offset between local and GMT time. If DST is not in
- * effect, this value is zero; otherwise it is a positive value,
- * typically one hour.
- * @param ec input-output error code
- */
-U_CAPI void U_EXPORT2
-vzone_getOffset3(VZone* zone, UDate date, UBool local, int32_t& rawOffset,
-                int32_t& dstOffset, UErrorCode& ec);
-
-/**
- * Sets the TimeZone's raw GMT offset (i.e., the number of milliseconds to add
- * to GMT to get local time, before taking daylight savings time into account).
- *
- * @param zone, the vzone to use
- * @param offsetMillis  The new raw GMT offset for this time zone.
- */
-U_CAPI void U_EXPORT2
-vzone_setRawOffset(VZone* zone, int32_t offsetMillis);
-
-/**
- * Returns the TimeZone's raw GMT offset (i.e., the number of milliseconds to add
- * to GMT to get local time, before taking daylight savings time into account).
- *
- * @param zone, the vzone to use
- * @return   The TimeZone's raw GMT offset.
- */
-U_CAPI int32_t U_EXPORT2
-vzone_getRawOffset(VZone* zone);
-
-/**
- * Queries if this time zone uses daylight savings time.
- * @param zone, the vzone to use
- * @return true if this time zone uses daylight savings time,
- * false, otherwise.
- */
-U_CAPI UBool U_EXPORT2
-vzone_useDaylightTime(VZone* zone);
-
-/**
- * Queries if the given date is in daylight savings time in
- * this time zone.
- * This method is wasteful since it creates a new GregorianCalendar and
- * deletes it each time it is called. This is a deprecated method
- * and provided only for Java compatibility.
- *
- * @param zone, the vzone to use
- * @param date the given UDate.
- * @param status Output param filled in with success/error code.
- * @return true if the given date is in daylight savings time,
- * false, otherwise.
- */
-U_CAPI UBool U_EXPORT2
-vzone_inDaylightTime(VZone* zone, UDate date, UErrorCode& status);
-
-/**
- * Returns true if this zone has the same rule and offset as another zone.
- * That is, if this zone differs only in ID, if at all.
- * @param zone, the vzone to use
- * @param other the <code>TimeZone</code> object to be compared with
- * @return true if the given zone is the same as this one,
- * with the possible exception of the ID
- */
-U_CAPI UBool U_EXPORT2
-vzone_hasSameRules(VZone* zone, const VZone* other);
-
-/**
- * Gets the first time zone transition after the base time.
- * @param zone, the vzone to use
- * @param base      The base time.
- * @param inclusive Whether the base time is inclusive or not.
- * @param result    Receives the first transition after the base time.
- * @return  true if the transition is found.
- */
-U_CAPI UBool U_EXPORT2
-vzone_getNextTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result);
-
-/**
- * Gets the most recent time zone transition before the base time.
- * @param zone, the vzone to use
- * @param base      The base time.
- * @param inclusive Whether the base time is inclusive or not.
- * @param result    Receives the most recent transition before the base time.
- * @return  true if the transition is found.
- */
-U_CAPI UBool U_EXPORT2
-vzone_getPreviousTransition(VZone* zone, UDate base, UBool inclusive, ZTrans* result);
-
-/**
- * Returns the number of <code>TimeZoneRule</code>s which represents time transitions,
- * for this time zone, that is, all <code>TimeZoneRule</code>s for this time zone except
- * <code>InitialTimeZoneRule</code>.  The return value range is 0 or any positive value.
- * @param zone, the vzone to use
- * @param status    Receives error status code.
- * @return The number of <code>TimeZoneRule</code>s representing time transitions.
- */
-U_CAPI int32_t U_EXPORT2
-vzone_countTransitionRules(VZone* zone, UErrorCode& status);
-
-/**
- * Return the class ID for this class. This is useful only for comparing to
- * a return value from getDynamicClassID(). For example:
- * <pre>
- * .   Base* polymorphic_pointer = createPolymorphicObject();
- * .   if (polymorphic_pointer->getDynamicClassID() ==
- * .       erived::getStaticClassID()) ...
- * </pre>
- * @param zone, the vzone to use
- * @return          The class ID for all objects of this class.
- */
-U_CAPI UClassID U_EXPORT2
-vzone_getStaticClassID(VZone* zone);
-
-/**
- * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This
- * method is to implement a simple version of RTTI, since not all C++
- * compilers support genuine RTTI. Polymorphic operator==() and clone()
- * methods call this method.
- *
- * @param zone, the vzone to use
- * @return          The class ID for this object. All objects of a
- *                  given class have the same class ID.  Objects of
- *                  other classes have different class IDs.
- */
-U_CAPI UClassID U_EXPORT2
-vzone_getDynamicClassID(VZone* zone);
-
-#endif // __VZONE_H
-
-#endif
diff --git a/libs/icui18n/zrule.cpp b/libs/icui18n/zrule.cpp
deleted file mode 100644
index c13411fc8e3f..000000000000
--- a/libs/icui18n/zrule.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 2009-2011, International Business Machines Corporation and
-* others. All Rights Reserved.
-*******************************************************************************
-*/
-
-/**
- * \file
- * \brief C API: Time zone rule classes
- */
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/uobject.h"
-#include "zrule.h"
-#include "unicode/tzrule.h"
-#include "cmemory.h"
-#include "unicode/ustring.h"
-#include "unicode/parsepos.h"
-
-U_NAMESPACE_USE
-
-/*********************************************************************
- * ZRule API
- *********************************************************************/
-
-U_CAPI void U_EXPORT2
-zrule_close(ZRule* rule) {
-    delete (TimeZoneRule*)rule;
-}
-
-U_CAPI UBool U_EXPORT2
-zrule_equals(const ZRule* rule1, const ZRule* rule2) {
-    return *(const TimeZoneRule*)rule1 == *(const TimeZoneRule*)rule2;
-}
-
-U_CAPI void U_EXPORT2
-zrule_getName(ZRule* rule, UChar* name, int32_t nameLength) {
-    UnicodeString s(nameLength==-1, name, nameLength);
-    s = ((TimeZoneRule*)rule)->TimeZoneRule::getName(s);
-    nameLength = s.length();
-    memcpy(name, s.getBuffer(), nameLength);
-    return;
-}
-
-U_CAPI int32_t U_EXPORT2
-zrule_getRawOffset(ZRule* rule) {
-    return ((TimeZoneRule*)rule)->TimeZoneRule::getRawOffset();
-}
-
-U_CAPI int32_t U_EXPORT2
-zrule_getDSTSavings(ZRule* rule) {
-    return ((TimeZoneRule*)rule)->TimeZoneRule::getDSTSavings();
-}
-
-U_CAPI UBool U_EXPORT2
-zrule_isEquivalentTo(ZRule* rule1,  ZRule* rule2) {
-    return ((TimeZoneRule*)rule1)->TimeZoneRule::isEquivalentTo(*(TimeZoneRule*)rule2);
-}
-
-/*********************************************************************
- * IZRule API
- *********************************************************************/
-
-U_CAPI IZRule* U_EXPORT2
-izrule_open(const UChar* name, int32_t nameLength, int32_t rawOffset, int32_t dstSavings) {
-    UnicodeString s(nameLength==-1, name, nameLength);
-    return (IZRule*) new InitialTimeZoneRule(s, rawOffset, dstSavings);
-}
-
-U_CAPI void U_EXPORT2
-izrule_close(IZRule* rule) {
-    delete (InitialTimeZoneRule*)rule;
-}
-
-U_CAPI IZRule* U_EXPORT2
-izrule_clone(IZRule *rule) {
-    return (IZRule*) (((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::clone());
-}
-
-U_CAPI UBool U_EXPORT2
-izrule_equals(const IZRule* rule1, const IZRule* rule2) {
-    return *(const InitialTimeZoneRule*)rule1 == *(const InitialTimeZoneRule*)rule2;
-}
-
-U_CAPI void U_EXPORT2
-izrule_getName(IZRule* rule, UChar* & name, int32_t & nameLength) {
-    // UnicodeString s(nameLength==-1, name, nameLength);
-    UnicodeString s;
-    ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getName(s);
-    nameLength = s.length();
-    name = (UChar*)uprv_malloc(nameLength);
-    memcpy(name, s.getBuffer(), nameLength);
-    return;
-}
-
-U_CAPI int32_t U_EXPORT2
-izrule_getRawOffset(IZRule* rule) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getRawOffset();
-}
-
-U_CAPI int32_t U_EXPORT2
-izrule_getDSTSavings(IZRule* rule) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getDSTSavings();
-}
-
-U_CAPI UBool U_EXPORT2
-izrule_isEquivalentTo(IZRule* rule1,  IZRule* rule2) {
-    return ((InitialTimeZoneRule*)rule1)->InitialTimeZoneRule::isEquivalentTo(*(InitialTimeZoneRule*)rule2);
-}
-
-U_CAPI UBool U_EXPORT2
-izrule_getFirstStart(IZRule* rule, int32_t prevRawOffset, int32_t prevDSTSavings,
-                    UDate& result) {
-    return ((const InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getFirstStart(prevRawOffset, prevDSTSavings, result);
-}
-
-U_CAPI UBool U_EXPORT2
-izrule_getFinalStart(IZRule* rule, int32_t prevRawOffset, int32_t prevDSTSavings,
-                    UDate& result) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getFinalStart(prevRawOffset, prevDSTSavings, result);
-}
-
-U_CAPI UBool U_EXPORT2
-izrule_getNextStart(IZRule* rule, UDate base, int32_t prevRawOffset,
-                   int32_t prevDSTSavings, UBool inclusive, UDate& result) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getNextStart(base, prevRawOffset, prevDSTSavings, inclusive, result);
-}
-
-U_CAPI UBool U_EXPORT2
-izrule_getPreviousStart(IZRule* rule, UDate base, int32_t prevRawOffset,
-                       int32_t prevDSTSavings, UBool inclusive, UDate& result) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getPreviousStart(base, prevRawOffset, prevDSTSavings, inclusive, result);
-}
-
-U_CAPI UClassID U_EXPORT2
-izrule_getStaticClassID(IZRule* rule) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getStaticClassID();
-}
-
-U_CAPI UClassID U_EXPORT2
-izrule_getDynamicClassID(IZRule* rule) {
-    return ((InitialTimeZoneRule*)rule)->InitialTimeZoneRule::getDynamicClassID();
-}
-
-#endif
diff --git a/libs/icui18n/ztrans.cpp b/libs/icui18n/ztrans.cpp
deleted file mode 100644
index d2d93da1334b..000000000000
--- a/libs/icui18n/ztrans.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-* Copyright (C) 2009-2010, International Business Machines Corporation and         *
-* others. All Rights Reserved.                                                *
-*******************************************************************************
-*/
-
-/**
- * \file
- * \brief C API: Time zone transition classes
- */
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/uobject.h"
-#include "ztrans.h"
-#include "unicode/tztrans.h"
-#include "cmemory.h"
-#include "unicode/ustring.h"
-#include "unicode/parsepos.h"
-
-U_NAMESPACE_USE
-
-U_CAPI ZTrans* U_EXPORT2
-ztrans_open(UDate time, const void* from, const void* to){
-    return (ZTrans*) new TimeZoneTransition(time,*(TimeZoneRule*)from,*(TimeZoneRule*)to);
-}
-
-U_CAPI ZTrans* U_EXPORT2
-ztrans_openEmpty() {
-    return (ZTrans*) new TimeZoneTransition();
-}
-
-U_CAPI void U_EXPORT2
-ztrans_close(ZTrans *trans) {
-    delete (TimeZoneTransition*)trans;
-}
-
-U_CAPI ZTrans* U_EXPORT2
-ztrans_clone(ZTrans *trans) {
-    return (ZTrans*) (((TimeZoneTransition*)trans)->TimeZoneTransition::clone());
-}
-
-U_CAPI UBool U_EXPORT2
-ztrans_equals(const ZTrans* trans1, const ZTrans* trans2){
-    return *(const TimeZoneTransition*)trans1 == *(const TimeZoneTransition*)trans2;
-}
-
-U_CAPI UDate U_EXPORT2
-ztrans_getTime(ZTrans* trans) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::getTime();
-}
-
-U_CAPI void U_EXPORT2
-ztrans_setTime(ZTrans* trans, UDate time) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::setTime(time);
-}
-
-U_CAPI void* U_EXPORT2
-ztrans_getFrom(ZTrans* & trans) {
-    return (void*) (((TimeZoneTransition*)trans)->TimeZoneTransition::getFrom());
-}
-
-U_CAPI void U_EXPORT2
-ztrans_setFrom(ZTrans* trans, const void* from) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::setFrom(*(TimeZoneRule*)from);
-}
-
-U_CAPI void U_EXPORT2
-ztrans_adoptFrom(ZTrans* trans, void* from) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::adoptFrom((TimeZoneRule*)from);
-}
-
-U_CAPI void* U_EXPORT2
-ztrans_getTo(ZTrans* trans){
-    return (void*) (((TimeZoneTransition*)trans)->TimeZoneTransition::getTo());
-}
-
-U_CAPI void U_EXPORT2
-ztrans_setTo(ZTrans* trans, const void* to) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::setTo(*(TimeZoneRule*)to);
-}
-
-U_CAPI void U_EXPORT2
-ztrans_adoptTo(ZTrans* trans, void* to) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::adoptTo((TimeZoneRule*)to);
-}
-
-U_CAPI UClassID U_EXPORT2
-ztrans_getStaticClassID(ZTrans* trans) {
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::getStaticClassID();
-}
-
-U_CAPI UClassID U_EXPORT2
-ztrans_getDynamicClassID(ZTrans* trans){
-    return ((TimeZoneTransition*)trans)->TimeZoneTransition::getDynamicClassID();
-}
-
-#endif
diff --git a/libs/lcms2/AUTHORS b/libs/lcms2/AUTHORS
index 107c1ffc5e6e..cab9193f9b34 100644
--- a/libs/lcms2/AUTHORS
+++ b/libs/lcms2/AUTHORS
@@ -43,6 +43,7 @@ Amyspark
 Lovell Fuller
 Eli Schwartz
 Diogo Teles Sant'Anna
+Vlad Erium
 
 Special Thanks 
 --------------
diff --git a/libs/lcms2/include/lcms2.h b/libs/lcms2/include/lcms2.h
index c4de8666a343..7e2b502eb3af 100644
--- a/libs/lcms2/include/lcms2.h
+++ b/libs/lcms2/include/lcms2.h
@@ -23,7 +23,7 @@
 //
 //---------------------------------------------------------------------------------
 //
-// Version 2.18
+// Version 2.19
 //
 
 #ifndef _lcms2_H
@@ -37,6 +37,13 @@
 // "long long" type.
 // #define CMS_DONT_USE_INT64        1
 
+// Uncomment this one to enable large file support on file/stream I/O.
+// LittleCMS is still limited by ICC 32-bit offsets and sizes, so the
+// practical maximum remains 4 GiB minus profile overhead. Be careful
+// because such huge profiles have to be loaded into memory and that's
+// usually a very bad idea.
+// #define CMS_LARGE_FILE_SUPPORT    1
+
 // Uncomment this if your compiler doesn't work with fast floor function
 // #define CMS_DONT_USE_FAST_FLOOR 1
 
@@ -87,7 +94,7 @@ extern "C" {
 #endif
 
 // Version/release
-#define LCMS_VERSION        2180
+#define LCMS_VERSION        2190
 
 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
 #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@@ -168,6 +175,9 @@ typedef double               cmsFloat64Number;
 #ifdef CMS_DONT_USE_INT64
     typedef cmsUInt32Number      cmsUInt64Number[2];
     typedef cmsInt32Number       cmsInt64Number[2];
+#   if defined(CMS_LARGE_FILE_SUPPORT)
+#      error "You need int64 for large file support"
+#   endif
 #endif
 
 // Derivative types
@@ -504,7 +514,13 @@ typedef enum {
     cmsSigLinkClass                         = 0x6C696E6B,  // 'link'
     cmsSigAbstractClass                     = 0x61627374,  // 'abst'
     cmsSigColorSpaceClass                   = 0x73706163,  // 'spac'
-    cmsSigNamedColorClass                   = 0x6e6d636c   // 'nmcl'
+    cmsSigNamedColorClass                   = 0x6e6d636c,  // 'nmcl'
+
+                                                            // iccMAX only
+    cmsSigColorEncodingSpaceClass           = 0x63656E63,  // 'cenc'
+    cmsSigMultiplexIdentificationClass      = 0x6D696420,  // 'mid '
+    cmsSigMultiplexLinkClass                = 0x6d6c6e6b,  // 'mlnk'
+    cmsSigMultiplexVisualizationClass       = 0x6d766973   // 'mvis'
 
 } cmsProfileClassSignature;
 
@@ -1078,8 +1094,12 @@ CMSAPI int               CMSEXPORT cmsGetEncodedCMMversion(void);
 // Support of non-standard functions --------------------------------------------------------------------------------------
 
 CMSAPI int               CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2);
-CMSAPI long int          CMSEXPORT cmsfilelength(FILE* f);
 
+#ifdef CMS_LARGE_FILE_SUPPORT
+CMSAPI long long int     CMSEXPORT cmsfilelength(FILE* f);
+#else
+CMSAPI long int          CMSEXPORT cmsfilelength(FILE* f);
+#endif
 
 // Context handling --------------------------------------------------------------------------------------------------------
 
@@ -1502,6 +1522,7 @@ CMSAPI cmsHPROFILE       CMSEXPORT cmsCreateProfilePlaceholder(cmsContext Contex
 CMSAPI cmsContext        CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile);
 CMSAPI cmsInt32Number    CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile);
 CMSAPI cmsTagSignature   CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n);
+CMSAPI cmsBool           CMSEXPORT cmsGetTagOffsetAndSize(cmsHPROFILE hProfile, cmsUInt32Number n, cmsUInt32Number* offset, cmsUInt32Number* size);
 CMSAPI cmsBool           CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig);
 
 // Read and write pre-formatted data
@@ -1525,6 +1546,8 @@ CMSAPI void              CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile,
 CMSAPI void              CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID);
 CMSAPI cmsBool           CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest);
 CMSAPI cmsUInt32Number   CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile);
+CMSAPI cmsUInt32Number   CMSEXPORT cmsGetHeaderCMM(cmsHPROFILE hProfile);
+
 
 CMSAPI void              CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags);
 CMSAPI cmsUInt32Number   CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile);
@@ -1867,6 +1890,13 @@ CMSAPI cmsContext       CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransf
 CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform);
 CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform);
 
+// Access the optimized pipeline and gamut-check pipeline inside a transform.
+CMSAPI cmsPipeline*    CMSEXPORT cmsGetTransformPipeline(cmsHTRANSFORM hTransform);
+CMSAPI cmsPipeline*    CMSEXPORT cmsGetTransformGamutCheckPipeline(cmsHTRANSFORM hTransform);
+// Grab colorants
+CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetTransformInputColorants(cmsHTRANSFORM hTransform);
+CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetTransformOutputColorants(cmsHTRANSFORM hTransform);
+
 // For backwards compatibility
 CMSAPI cmsBool          CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
                                                          cmsUInt32Number InputFormat,
diff --git a/libs/lcms2/src/cmserr.c b/libs/lcms2/src/cmserr.c
index e1e06bf6def1..22cce504cb0e 100644
--- a/libs/lcms2/src/cmserr.c
+++ b/libs/lcms2/src/cmserr.c
@@ -48,25 +48,64 @@ int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
     return (toupper(*us1) - toupper(*--us2));
 }
 
+#ifdef CMS_LARGE_FILE_SUPPORT
+
+long long int CMSEXPORT cmsfilelength(FILE* f)
+{
+    long long int p, n;
+
+#ifdef CMS_IS_WINDOWS_
+    p = _ftelli64(f);
+    if (p == -1LL)
+        return -1LL;
+
+    if (_fseeki64(f, 0, SEEK_END) != 0)
+        return -1LL;
+
+    n = _ftelli64(f);
+
+    if (_fseeki64(f, p, SEEK_SET) != 0)
+        return -1LL;
+#else
+    p = (long long int) ftello(f);
+    if (p < 0)
+        return -1LL;
+
+    if (fseeko(f, 0, SEEK_END) != 0)
+        return -1LL;
+
+    n = (long long int) ftello(f);
+
+    if (fseeko(f, (off_t) p, SEEK_SET) != 0)
+        return -1LL;
+#endif
+
+    return n;
+}
+
+#else
+
 // long int because C99 specifies ftell in such way (7.19.9.2)
 long int CMSEXPORT cmsfilelength(FILE* f)
 {
-    long int p , n;
+    long int p, n;
 
-    p = ftell(f); // register current file position
-    if (p == -1L) 
+    p = ftell(f);
+    if (p == -1L)
         return -1L;
 
-    if (fseek(f, 0, SEEK_END) != 0) {
+    if (fseek(f, 0, SEEK_END) != 0)
         return -1L;
-    }
 
     n = ftell(f);
-    fseek(f, p, SEEK_SET); // file position restored
+
+    if (fseek(f, p, SEEK_SET) != 0)
+        return -1L;
 
     return n;
 }
 
+#endif
 
 // Memory handling ------------------------------------------------------------------
 //
@@ -75,7 +114,11 @@ long int CMSEXPORT cmsfilelength(FILE* f)
 // amount of memory that can be reclaimed. This is mostly as a safety feature to prevent
 // bogus or evil code to allocate huge blocks that otherwise lcms would never need.
 
-#define MAX_MEMORY_FOR_ALLOC  ((cmsUInt32Number)(1024U*1024U*512U))
+#ifdef CMS_LARGE_FILE_SUPPORT
+#   define MAX_MEMORY_FOR_ALLOC  ((cmsUInt32Number)(1024U*1024U*2048U))
+#else
+#   define MAX_MEMORY_FOR_ALLOC  ((cmsUInt32Number)(1024U*1024U*512U))
+#endif
 
 // User may override this behaviour by using a memory plug-in, which basically replaces
 // the default memory management functions. In this case, no check is performed and it
diff --git a/libs/lcms2/src/cmsio0.c b/libs/lcms2/src/cmsio0.c
index aee9ba3464e6..9fe23a90c7e8 100644
--- a/libs/lcms2/src/cmsio0.c
+++ b/libs/lcms2/src/cmsio0.c
@@ -77,6 +77,9 @@ cmsBool  NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Pt
 {
     FILENULL* ResData = (FILENULL*) iohandler ->stream;
 
+    if (size > (cmsUInt32Number)(0xFFFFFFFFU - ResData->Pointer))
+        return FALSE;
+
     ResData ->Pointer += size;
     if (ResData ->Pointer > iohandler->UsedSpace)
         iohandler->UsedSpace = ResData ->Pointer;
@@ -130,7 +133,6 @@ Error:
 
 }
 
-
 // Memory-based stream --------------------------------------------------------------
 
 // Those functions implements an iohandler which takes a block of memory as storage medium.
@@ -150,12 +152,20 @@ cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsU
     cmsUInt8Number* Ptr;
     cmsUInt32Number len = size * count;
 
-    if (ResData -> Pointer + len > ResData -> Size){
-
-        len = (ResData -> Size - ResData -> Pointer);
-        cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size);
+    if (size == 0 || count == 0)
         return 0;
-    }
+
+    if ((len / count) != size)
+        goto ReadError;
+
+    if (Buffer == NULL)
+        goto ReadError;
+
+    if (len > ResData->Size)
+        goto ReadError;
+
+    if (ResData -> Pointer > ResData -> Size - len)
+        goto ReadError;
 
     Ptr  = ResData -> Block;
     Ptr += ResData -> Pointer;
@@ -163,18 +173,21 @@ cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsU
     ResData -> Pointer += len;
 
     return count;
+
+ReadError:
+    cmsSignalError(iohandler->ContextID, cmsERROR_READ, "Read from memory error");
+    return 0;
+
 }
 
 // SEEK_CUR is assumed
 static
-cmsBool  MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset)
+cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset)
 {
     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
 
-    if (offset > ResData ->Size) {
-        cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK,  "Too few data; probably corrupted profile");
+    if (offset > ResData ->Size)
         return FALSE;
-    }
 
     ResData ->Pointer = offset;
     return TRUE;
@@ -197,15 +210,17 @@ cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, con
 {
     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
 
-    if (ResData == NULL) return FALSE; // Housekeeping
+    if (ResData == NULL || Ptr == NULL) goto WriteError;
 
-    // Check for available space. Clip.
-    if (ResData->Pointer + size > ResData->Size) {
-        size = ResData ->Size - ResData->Pointer;
-    }
-      
     if (size == 0) return TRUE;     // Write zero bytes is ok, but does nothing
 
+    // Check for available space.  Truncate the output in case the space
+    // is not enough instead of erroring out.  See
+    // https://github.com/hughsie/colord/issues/147.
+
+    if (size > ResData->Size - ResData->Pointer)
+        size = ResData->Size - ResData->Pointer;
+
     memmove(ResData ->Block + ResData ->Pointer, Ptr, size);
     ResData ->Pointer += size;
 
@@ -213,6 +228,10 @@ cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, con
         iohandler->UsedSpace = ResData ->Pointer;
 
     return TRUE;
+
+WriteError:
+    cmsSignalError(iohandler->ContextID, cmsERROR_WRITE, "Write to memory error");
+    return FALSE;
 }
 
 
@@ -333,8 +352,15 @@ cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number
 static
 cmsBool  FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
 {
+#ifdef CMS_LARGE_FILE_SUPPORT
+#  ifdef CMS_IS_WINDOWS_
+    if (_fseeki64((FILE*) iohandler->stream, (long long int) offset, SEEK_SET) != 0) {
+#  else
+    if (fseeko((FILE*) iohandler->stream, (off_t) offset, SEEK_SET) != 0) {
+#  endif
+#else
     if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) {
-
+#endif
        cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file");
        return FALSE;
     }
@@ -346,13 +372,24 @@ cmsBool  FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
 static
 cmsUInt32Number FileTell(cmsIOHANDLER* iohandler)
 {
+#ifdef CMS_LARGE_FILE_SUPPORT
+#  ifdef CMS_IS_WINDOWS_
+    long long int t = _ftelli64((FILE*) iohandler->stream);
+#  else
+    long long int t = (long long int) ftello((FILE*) iohandler->stream);
+#  endif
+    if (t < 0) {
+        cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file");
+        return 0;
+    }
+#else
     long t = ftell((FILE*)iohandler ->stream);
     if (t == -1L) {
         cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file");
         return 0;
     }
-
-    return (cmsUInt32Number)t;
+#endif
+    return (cmsUInt32Number) t;
 }
 
 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
@@ -379,7 +416,11 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha
 {
     cmsIOHANDLER* iohandler = NULL;
     FILE* fm = NULL;
-    cmsInt32Number fileLen;
+#ifdef CMS_LARGE_FILE_SUPPORT
+    long long int fileLen;
+#else
+    long int fileLen;
+#endif
     char mode[4] = { 0,0,0,0 };
 
     _cmsAssert(FileName != NULL);
@@ -430,7 +471,7 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha
              cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
             return NULL;
         }                                     
-        fileLen = (cmsInt32Number)cmsfilelength(fm);
+        fileLen = cmsfilelength(fm);
         if (fileLen < 0)
         {
             fclose(fm);
@@ -438,6 +479,15 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha
             cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);
             return NULL;
         }
+#ifdef CMS_LARGE_FILE_SUPPORT
+        if (fileLen > (long long int) 0xFFFFFFFFLL)
+        {
+            fclose(fm);
+            _cmsFree(ContextID, iohandler);
+            cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' is too large", FileName);
+            return NULL;
+        }
+#endif
         iohandler -> ReportedSize = (cmsUInt32Number) fileLen;
         break;
 
@@ -477,14 +527,25 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha
 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream)
 {
     cmsIOHANDLER* iohandler = NULL;
-    cmsInt32Number fileSize;
+#ifdef CMS_LARGE_FILE_SUPPORT
+    long long int fileSize;
+#else
+    long int fileSize;
+#endif
 
-    fileSize = (cmsInt32Number)cmsfilelength(Stream);
+    fileSize = cmsfilelength(Stream);
     if (fileSize < 0)
     {
         cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");
         return NULL;
     }
+#ifdef CMS_LARGE_FILE_SUPPORT
+    if (fileSize > (long long int) 0xFFFFFFFFLL)
+    {
+        cmsSignalError(ContextID, cmsERROR_FILE, "Stream is too large");
+        return NULL;
+    }
+#endif
 
     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
     if (iohandler == NULL) return NULL;
@@ -597,6 +658,18 @@ cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Numb
     return Icc ->TagNames[n];
 }
 
+// Return location of the tag
+cmsBool CMSEXPORT cmsGetTagOffsetAndSize(cmsHPROFILE hProfile, cmsUInt32Number n, cmsUInt32Number* offset, cmsUInt32Number* size)
+{
+    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
+
+    if (n > Icc->TagCount) return FALSE;
+    if (n >= MAX_TABLE_TAG) return FALSE;
+
+    if (offset != NULL) *offset = Icc->TagOffsets[n];
+    if (size != NULL) *size = Icc->TagSizes[n];
+    return TRUE;
+}
 
 static
 int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig)
@@ -762,7 +835,8 @@ cmsUInt32Number _validatedVersion(cmsUInt32Number DWord)
 static
 cmsBool validDeviceClass(cmsProfileClassSignature cl)
 {
-    if ((int)cl == 0) return TRUE; // We allow zero because older lcms versions defaulted to that.
+    if (cl == (cmsProfileClassSignature)0)
+        return TRUE; // We allow zero because older lcms versions defaulted to that.
 
     switch (cl)
     {
@@ -773,6 +847,10 @@ cmsBool validDeviceClass(cmsProfileClassSignature cl)
     case cmsSigAbstractClass:
     case cmsSigColorSpaceClass:
     case cmsSigNamedColorClass:
+    case cmsSigColorEncodingSpaceClass:
+    case cmsSigMultiplexIdentificationClass:
+    case cmsSigMultiplexLinkClass:
+    case cmsSigMultiplexVisualizationClass:
         return TRUE;
 
     default:
@@ -981,6 +1059,17 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
 
 // ----------------------------------------------------------------------- Set/Get several struct members
 
+cmsUInt32Number CMSEXPORT cmsGetHeaderCMM(cmsHPROFILE hProfile)
+{
+    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
+    return Icc->CMM;
+}
+
+void CMSEXPORT _cmsSetHeaderCMM(cmsHPROFILE hProfile, cmsUInt32Number CMM)
+{
+    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
+    Icc->CMM = CMM;
+}
 
 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)
 {
@@ -1199,7 +1288,6 @@ Error:
     return NULL;
 }
 
-
 // Create profile from disk file
 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess)
 {
@@ -1267,7 +1355,6 @@ cmsHPROFILE  CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sA
     return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess);
 }
 
-
 // Open from memory block
 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize)
 {
@@ -1416,7 +1503,6 @@ cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)
     return TRUE;
 }
 
-
 // Fill the offset and size fields for all linked tags
 static
 cmsBool SetLinks( _cmsICCPROFILE* Icc)
@@ -1498,7 +1584,6 @@ Error:
     return 0;
 }
 
-
 // Low-level save to disk.
 cmsBool  CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)
 {
@@ -1634,21 +1719,23 @@ cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Typ
     return FALSE;
 }
 
-
 // That's the main read function
 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
 {
-    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
+    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
+    cmsBool avoidCheck;
     cmsIOHANDLER* io;
     cmsTagTypeHandler* TypeHandler;
     cmsTagTypeHandler LocalTypeHandler;
-    cmsTagDescriptor*  TagDescriptor;
+    cmsTagDescriptor* TagDescriptor = NULL;
     cmsTagTypeSignature BaseType;
     cmsUInt32Number Offset, TagSize;
     cmsUInt32Number ElemCount;
     int n;
 
-    if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL;
+    if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return NULL;
+
+    avoidCheck = _cmsAvoidTypeCheckOnTags(Icc->ContextID);
 
     n = _cmsSearchTag(Icc, sig, TRUE);
     if (n < 0)
@@ -1659,7 +1746,7 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
     }
 
     // If the element is already in memory, return the pointer
-    if (Icc -> TagPtrs[n]) {
+    if (Icc->TagPtrs[n]) {
 
         if (Icc->TagTypeHandlers[n] == NULL) goto Error;
 
@@ -1667,24 +1754,26 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
         BaseType = Icc->TagTypeHandlers[n]->Signature;
         if (BaseType == 0) goto Error;
 
-        TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig);
-        if (TagDescriptor == NULL) goto Error;
+        if (!avoidCheck) {
 
-        if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
+            TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig);
+            if (TagDescriptor == NULL) goto Error;
+            if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
+        }
 
-        if (Icc ->TagSaveAsRaw[n]) goto Error;  // We don't support read raw tags as cooked
+        if (Icc->TagSaveAsRaw[n]) goto Error;  // We don't support read raw tags as cooked
 
-        _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
-        return Icc -> TagPtrs[n];
+        _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
+        return Icc->TagPtrs[n];
     }
 
     // We need to read it. Get the offset and size to the file
-    Offset    = Icc -> TagOffsets[n];
-    TagSize   = Icc -> TagSizes[n];
+    Offset = Icc->TagOffsets[n];
+    TagSize = Icc->TagSizes[n];
 
     if (TagSize < 8) goto Error;
 
-    io = Icc ->IOhandler;
+    io = Icc->IOhandler;
 
     if (io == NULL) { // This is a built-in profile that has been manipulated, abort early
 
@@ -1693,79 +1782,84 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
     }
 
     // Seek to its location
-    if (!io -> Seek(io, Offset))
+    if (!io->Seek(io, Offset))
         goto Error;
 
-    // Search for support on this tag
-    TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
-    if (TagDescriptor == NULL) {
+    if (!avoidCheck) {
+        // Search for support on this tag
+        TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig);
+        if (TagDescriptor == NULL) {
 
-        char String[5];
+            char String[5];
 
-        _cmsTagSignature2String(String, sig);
+            _cmsTagSignature2String(String, sig);
 
-        // An unknown element was found.
-        cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String);
-        goto Error;     // Unsupported.
+            // An unknown element was found.
+            cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String);
+            goto Error;     // Unsupported.
+        }
     }
 
     // if supported, get type and check if in list
     BaseType = _cmsReadTypeBase(io);
     if (BaseType == 0) goto Error;
 
-    if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
-   
-    TagSize  -= 8;       // Already read by the type base logic
+    if (!avoidCheck) {
+        if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
+    }
+
+    TagSize -= 8;       // Already read by the type base logic
 
     // Get type handler
-    TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
+    TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, BaseType);
     if (TypeHandler == NULL) goto Error;
     LocalTypeHandler = *TypeHandler;
 
 
     // Read the tag
-    Icc -> TagTypeHandlers[n] = TypeHandler;
+    Icc->TagTypeHandlers[n] = TypeHandler;
 
-    LocalTypeHandler.ContextID = Icc ->ContextID;
-    LocalTypeHandler.ICCVersion = Icc ->Version;
-    Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);
+    LocalTypeHandler.ContextID = Icc->ContextID;
+    LocalTypeHandler.ICCVersion = Icc->Version;
+    Icc->TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);
 
     // The tag type is supported, but something wrong happened and we cannot read the tag.
     // let know the user about this (although it is just a warning)
-    if (Icc -> TagPtrs[n] == NULL) {
+    if (Icc->TagPtrs[n] == NULL) {
 
         char String[5];
 
         _cmsTagSignature2String(String, sig);
-        cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
+        cmsSignalError(Icc->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
         goto Error;
     }
 
-    // This is a weird error that may be a symptom of something more serious, the number of
-    // stored item is actually less than the number of required elements.
-    if (ElemCount < TagDescriptor ->ElemCount) {
+    if (!avoidCheck) {
+        // This is a weird error that may be a symptom of something more serious, the number of
+        // stored item is actually less than the number of required elements.
+        if (ElemCount < TagDescriptor->ElemCount) {
 
-        char String[5];
+            char String[5];
 
-        _cmsTagSignature2String(String, sig);
-        cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
-            String, TagDescriptor ->ElemCount, ElemCount);
-        goto Error;
+            _cmsTagSignature2String(String, sig);
+            cmsSignalError(Icc->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
+                String, TagDescriptor->ElemCount, ElemCount);
+            goto Error;
+        }
     }
 
-
     // Return the data
-    _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
-    return Icc -> TagPtrs[n];
+    _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
+    return Icc->TagPtrs[n];
 
 
     // Return error and unlock the data
 Error:
 
-    freeOneTag(Icc, n);    
+    freeOneTag(Icc, n);
     Icc->TagPtrs[n] = NULL;
-    
-    _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
+
+    _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
     return NULL;
 }
 
@@ -1783,6 +1877,8 @@ cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig
 
     // Get the handler. The true type is there
     TypeHandler =  Icc -> TagTypeHandlers[n];
+    if (TypeHandler == NULL) return (cmsTagTypeSignature) 0;
+
     return TypeHandler ->Signature;
 }
 
@@ -2093,7 +2189,6 @@ cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSi
     return TRUE;
 }
 
-
 // Returns the tag linked to sig, in the case two tags are sharing same resource
 cmsTagSignature  CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig)
 {
diff --git a/libs/lcms2/src/cmslut.c b/libs/lcms2/src/cmslut.c
index 380d31b27291..96ba08ec436a 100644
--- a/libs/lcms2/src/cmslut.c
+++ b/libs/lcms2/src/cmslut.c
@@ -460,25 +460,26 @@ void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[],
 static
 cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b)
 {
-    cmsUInt32Number rv, dim;
+    cmsUInt32Number dim;
+    cmsUInt64Number rv;
 
     _cmsAssert(Dims != NULL);
 
     for (rv = 1; b > 0; b--) {
 
         dim = Dims[b-1];
-        if (dim <= 1) return 0;  // Error
-
-        rv *= dim;
+        if (dim <= 1) return 0;
 
         // Check for overflow
         if (rv > UINT_MAX / dim) return 0;
+
+        rv *= dim;
     }
 
     // Again, prevent overflow
     if (rv > UINT_MAX / 15) return 0;
 
-    return rv;
+    return (cmsUInt32Number) rv;
 }
 
 static
diff --git a/libs/lcms2/src/cmsnamed.c b/libs/lcms2/src/cmsnamed.c
index 9dad6d1e7caf..baf72192e09c 100644
--- a/libs/lcms2/src/cmsnamed.c
+++ b/libs/lcms2/src/cmsnamed.c
@@ -87,8 +87,11 @@ cmsBool GrowMLUpool(cmsMLU* mlu)
     return TRUE;
 }
 
-
 // Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.
+// No need to check integer overflow since that is 2*16*count = 2^32-1 ; => count = 128 M entries,
+// That would be 2Gb, which is over MAX_MEMORY_FOR_ALLOC, even for large file size.
+// I added this check to silence the continuous spam reports of people using AI to catch what
+// they think are "vulnerabilities".
 static
 cmsBool GrowMLUtable(cmsMLU* mlu)
 {
@@ -100,8 +103,12 @@ cmsBool GrowMLUtable(cmsMLU* mlu)
 
     AllocatedEntries = mlu ->AllocatedEntries * 2;
 
-    // Check for overflow
-    if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
+    // Check for overflow in count doubling: if wrapped, result < original
+    if (AllocatedEntries < mlu->AllocatedEntries) return FALSE;
+
+    // Check for overflow in byte-size multiplication:
+    // dividing back by sizeof must recover the original count
+    if ((AllocatedEntries * sizeof(_cmsMLUentry)) / sizeof(_cmsMLUentry) != AllocatedEntries) return FALSE;
 
     // Reallocate the memory
     NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
@@ -805,7 +812,8 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
     memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
     memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
     NewNC ->ColorantCount = v ->ColorantCount;
-    memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
+    if (v->nColors > 0)
+        memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
     NewNC ->nColors = v ->nColors;
     return NewNC;
 }
diff --git a/libs/lcms2/src/cmsopt.c b/libs/lcms2/src/cmsopt.c
index 6d1ca89db5f1..b0ebca7d6556 100644
--- a/libs/lcms2/src/cmsopt.c
+++ b/libs/lcms2/src/cmsopt.c
@@ -1192,10 +1192,13 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
 
         if (Trans[t]) cmsFreeToneCurve(Trans[t]);
         if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]);
+
+        Trans[t] = NULL;
+        TransReverse[t] = NULL;
     }
 
     cmsPipelineFree(LutPlusCurves);
-
+    LutPlusCurves = NULL;
 
     OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe);
     OptimizedPrelinCLUT   = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data;
@@ -1206,7 +1209,7 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
         Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID,
                                                 OptimizedPrelinCLUT ->Params,
                                                 OptimizedPrelinCurves);
-        if (p8 == NULL) return FALSE;
+        if (p8 == NULL) goto Error;
 
         _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup);
 
@@ -1216,7 +1219,8 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
         Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID,
             OptimizedPrelinCLUT ->Params,
             3, OptimizedPrelinCurves, 3, NULL);
-        if (p16 == NULL) return FALSE;
+
+        if (p16 == NULL) goto Error;
 
         _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
 
@@ -1230,7 +1234,7 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
 
         if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) {
 
-            return FALSE;
+           goto Error;
         }
     }
 
diff --git a/libs/lcms2/src/cmssamp.c b/libs/lcms2/src/cmssamp.c
index b3e78ba4e4e5..9f22a3a58f28 100644
--- a/libs/lcms2/src/cmssamp.c
+++ b/libs/lcms2/src/cmssamp.c
@@ -185,6 +185,50 @@ cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfi
     return TRUE;
 }
 
+
+static
+cmsBool isInkColorspace(cmsColorSpaceSignature c)
+{
+    switch(c)
+    {
+    case cmsSigCmykData:
+    case cmsSigCmyData:
+    case cmsSigMCH1Data:
+    case cmsSigMCH2Data:
+    case cmsSigMCH3Data:
+    case cmsSigMCH4Data:
+    case cmsSigMCH5Data:
+    case cmsSigMCH6Data:
+    case cmsSigMCH7Data:
+    case cmsSigMCH8Data:
+    case cmsSigMCH9Data:
+    case cmsSigMCHAData:
+    case cmsSigMCHBData:
+    case cmsSigMCHCData:
+    case cmsSigMCHDData:
+    case cmsSigMCHEData:
+    case cmsSigMCHFData:
+    case cmsSig1colorData:
+    case cmsSig2colorData:
+    case cmsSig3colorData:
+    case cmsSig4colorData:
+    case cmsSig5colorData:
+    case cmsSig6colorData:
+    case cmsSig7colorData:
+    case cmsSig8colorData:
+    case cmsSig9colorData:
+    case cmsSig10colorData:
+    case cmsSig11colorData:
+    case cmsSig12colorData:
+    case cmsSig13colorData:
+    case cmsSig14colorData:
+    case cmsSig15colorData:
+            return TRUE;
+    default:
+        return FALSE;
+    }
+}
+
 // This function shouldn't exist at all -- there is such quantity of broken
 // profiles on black point tag, that we must somehow fix chromaticity to
 // avoid huge tint when doing Black point compensation. This function does
@@ -269,7 +313,7 @@ cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfil
     // If output profile, discount ink-limiting and that's all
     if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
         (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) &&
-        (cmsGetColorSpace(hProfile)  == cmsSigCmykData))
+        (isInkColorspace(cmsGetColorSpace(hProfile))))
         return BlackPointUsingPerceptualBlack(BlackPoint, hProfile);
 
     // Nope, compute BP using current intent.
@@ -406,7 +450,7 @@ cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROF
     if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) ||
         (ColorSpace != cmsSigGrayData &&
          ColorSpace != cmsSigRgbData  &&
-         ColorSpace != cmsSigCmykData)) {
+         !isInkColorspace(ColorSpace))) {
 
         // In this case, handle as input case
         return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags);
diff --git a/libs/lcms2/src/cmstypes.c b/libs/lcms2/src/cmstypes.c
index ee7923c0895c..e7b7eff51eef 100644
--- a/libs/lcms2/src/cmstypes.c
+++ b/libs/lcms2/src/cmstypes.c
@@ -6235,3 +6235,18 @@ cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig
 
     return NULL;
 }
+
+cmsBool _cmsAvoidTypeCheckOnTags(cmsContext ContextID)
+{
+    _cmsTagLinkedList* pt;
+    _cmsTagPluginChunkType* TagPluginChunk = (_cmsTagPluginChunkType*)_cmsContextGetClientChunk(ContextID, TagPlugin);
+
+    for (pt = TagPluginChunk->Tag;
+        pt != NULL;
+        pt = pt->Next) {
+
+        if (pt->Signature == (cmsTagSignature)0) return TRUE;
+    }
+
+    return FALSE;
+}
diff --git a/libs/lcms2/src/cmsvirt.c b/libs/lcms2/src/cmsvirt.c
index 8718e883fd89..f38554a60e8e 100644
--- a/libs/lcms2/src/cmsvirt.c
+++ b/libs/lcms2/src/cmsvirt.c
@@ -1319,12 +1319,23 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
     if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
 
 
-    if (xform -> InputColorant != NULL) {
+    // Colorant tables have special rules depening on deviceClass
+    if (xform -> InputColorant != NULL &&
+       (deviceClass == cmsSigLinkClass || deviceClass == cmsSigInputClass)) {
+
            if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
     }
 
     if (xform -> OutputColorant != NULL) {
-           if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
+
+        if (deviceClass == cmsSigLinkClass) {
+
+            if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
+        }
+        else
+        {
+            if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->OutputColorant)) goto Error;
+        }
     }
 
     if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
diff --git a/libs/lcms2/src/cmsxform.c b/libs/lcms2/src/cmsxform.c
index 600792e2e2af..c946eb932057 100644
--- a/libs/lcms2/src/cmsxform.c
+++ b/libs/lcms2/src/cmsxform.c
@@ -499,7 +499,6 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
     }
 }
 
-
 // No gamut check, Cache, 16 bits,
 static
 void CachedXFORM(_cmsTRANSFORM* p,
@@ -1442,6 +1441,22 @@ cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
     return xform->OutputFormat;
 }
 
+// Returns the optimized pipeline (Lut) inside a transform. Read-only; do not free.
+cmsPipeline* CMSEXPORT cmsGetTransformPipeline(cmsHTRANSFORM hTransform)
+{
+    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
+    if (xform == NULL) return NULL;
+    return xform->Lut;
+}
+
+// Returns the gamut-check pipeline inside a transform. Read-only; do not free.
+cmsPipeline* CMSEXPORT cmsGetTransformGamutCheckPipeline(cmsHTRANSFORM hTransform)
+{
+    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
+    if (xform == NULL) return NULL;
+    return xform->GamutCheck;
+}
+
 // For backwards compatibility
 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
                                          cmsUInt32Number InputFormat,
@@ -1473,3 +1488,19 @@ cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
     xform ->ToOutput     = ToOutput;
     return TRUE;
 }
+
+cmsNAMEDCOLORLIST* CMSEXPORT cmsGetTransformInputColorants(cmsHTRANSFORM hTransform)
+{
+    _cmsTRANSFORM* xform = (_cmsTRANSFORM*)hTransform;
+
+    if (xform == NULL) return NULL;
+    return xform->InputColorant;
+}
+
+cmsNAMEDCOLORLIST* CMSEXPORT cmsGetTransformOutputColorants(cmsHTRANSFORM hTransform)
+{
+    _cmsTRANSFORM* xform = (_cmsTRANSFORM*)hTransform;
+
+    if (xform == NULL) return NULL;
+    return xform->OutputColorant;
+}
diff --git a/libs/lcms2/src/lcms2_internal.h b/libs/lcms2/src/lcms2_internal.h
index 358308466142..17f93f923b71 100644
--- a/libs/lcms2/src/lcms2_internal.h
+++ b/libs/lcms2/src/lcms2_internal.h
@@ -858,6 +858,7 @@ int                  _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cms
 cmsTagTypeHandler*   _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig);
 cmsTagTypeSignature  _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig);
 cmsTagDescriptor*    _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig);
+cmsBool              _cmsAvoidTypeCheckOnTags(cmsContext ContextID);
 
 // Error logging ---------------------------------------------------------------------------------------------------------
 
diff --git a/libs/ldap/Makefile.in b/libs/ldap/Makefile.in
index 87d669fa2ca8..c53db741f5e9 100644
--- a/libs/ldap/Makefile.in
+++ b/libs/ldap/Makefile.in
@@ -8,13 +8,11 @@ SOURCES = \
 	liblber/encode.c \
 	liblber/io.c \
 	liblber/memory.c \
-	liblber/nt_err.c \
 	liblber/options.c \
 	liblber/sockbuf.c \
 	libldap/abandon.c \
 	libldap/add.c \
 	libldap/avl.c \
-	libldap/bind.c \
 	libldap/charray.c \
 	libldap/compare.c \
 	libldap/controls.c \
@@ -37,8 +35,6 @@ SOURCES = \
 	libldap/open.c \
 	libldap/options.c \
 	libldap/os-ip.c \
-	libldap/pagectrl.c \
-	libldap/print.c \
 	libldap/references.c \
 	libldap/request.c \
 	libldap/result.c \
@@ -47,7 +43,6 @@ SOURCES = \
 	libldap/schema.c \
 	libldap/search.c \
 	libldap/sortctrl.c \
-	libldap/string.c \
 	libldap/tavl.c \
 	libldap/thr_nt.c \
 	libldap/tls2.c \
diff --git a/libs/ldap/liblber/nt_err.c b/libs/ldap/liblber/nt_err.c
deleted file mode 100644
index a84c705262c1..000000000000
--- a/libs/ldap/liblber/nt_err.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2024 The OpenLDAP Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-#include "portable.h"
-
-#ifdef HAVE_WINSOCK2
-#include <winsock2.h>
-#elif defined(HAVE_WINSOCK)
-#include <winsock.h>
-#endif /* HAVE_WINSOCK(2) */
-
-#define LBER_RETSTR( x ) case x: return #x;
-
-char *ber_pvt_wsa_err2string( int err )
-{
-	switch( err ) {
-		LBER_RETSTR( WSAEINTR )
-		LBER_RETSTR( WSAEBADF )
-		LBER_RETSTR( WSAEACCES )
-		LBER_RETSTR( WSAEFAULT )
-		LBER_RETSTR( WSAEINVAL )
-		LBER_RETSTR( WSAEMFILE )
-		LBER_RETSTR( WSAEWOULDBLOCK )
-		LBER_RETSTR( WSAEINPROGRESS )
-		LBER_RETSTR( WSAEALREADY )
-		LBER_RETSTR( WSAENOTSOCK )
-		LBER_RETSTR( WSAEDESTADDRREQ )
-		LBER_RETSTR( WSAEMSGSIZE )
-		LBER_RETSTR( WSAEPROTOTYPE )
-		LBER_RETSTR( WSAENOPROTOOPT )
-		LBER_RETSTR( WSAEPROTONOSUPPORT )
-		LBER_RETSTR( WSAESOCKTNOSUPPORT )
-		LBER_RETSTR( WSAEOPNOTSUPP )
-		LBER_RETSTR( WSAEPFNOSUPPORT )
-		LBER_RETSTR( WSAEAFNOSUPPORT )
-		LBER_RETSTR( WSAEADDRINUSE )
-		LBER_RETSTR( WSAEADDRNOTAVAIL )
-		LBER_RETSTR( WSAENETDOWN )
-		LBER_RETSTR( WSAENETUNREACH )
-		LBER_RETSTR( WSAENETRESET )
-		LBER_RETSTR( WSAECONNABORTED )
-		LBER_RETSTR( WSAECONNRESET )
-		LBER_RETSTR( WSAENOBUFS )
-		LBER_RETSTR( WSAEISCONN )
-		LBER_RETSTR( WSAENOTCONN )
-		LBER_RETSTR( WSAESHUTDOWN )
-		LBER_RETSTR( WSAETOOMANYREFS )
-		LBER_RETSTR( WSAETIMEDOUT )
-		LBER_RETSTR( WSAECONNREFUSED )
-		LBER_RETSTR( WSAELOOP )
-		LBER_RETSTR( WSAENAMETOOLONG )
-		LBER_RETSTR( WSAEHOSTDOWN )
-		LBER_RETSTR( WSAEHOSTUNREACH )
-		LBER_RETSTR( WSAENOTEMPTY )
-		LBER_RETSTR( WSAEPROCLIM )
-		LBER_RETSTR( WSAEUSERS )
-		LBER_RETSTR( WSAEDQUOT )
-		LBER_RETSTR( WSAESTALE )
-		LBER_RETSTR( WSAEREMOTE )
-		LBER_RETSTR( WSASYSNOTREADY )
-		LBER_RETSTR( WSAVERNOTSUPPORTED )
-		LBER_RETSTR( WSANOTINITIALISED )
-		LBER_RETSTR( WSAEDISCON )
-
-#ifdef HAVE_WINSOCK2
-		LBER_RETSTR( WSAENOMORE )
-		LBER_RETSTR( WSAECANCELLED )
-		LBER_RETSTR( WSAEINVALIDPROCTABLE )
-		LBER_RETSTR( WSAEINVALIDPROVIDER )
-		LBER_RETSTR( WSASYSCALLFAILURE )
-		LBER_RETSTR( WSASERVICE_NOT_FOUND )
-		LBER_RETSTR( WSATYPE_NOT_FOUND )
-		LBER_RETSTR( WSA_E_NO_MORE )
-		LBER_RETSTR( WSA_E_CANCELLED )
-		LBER_RETSTR( WSAEREFUSED )
-#endif /* HAVE_WINSOCK2	*/
-
-		LBER_RETSTR( WSAHOST_NOT_FOUND )
-		LBER_RETSTR( WSATRY_AGAIN )
-		LBER_RETSTR( WSANO_RECOVERY )
-		LBER_RETSTR( WSANO_DATA )
-	}
-	return "unknown WSA error";
-}
diff --git a/libs/ldap/libldap/bind.c b/libs/ldap/libldap/bind.c
deleted file mode 100644
index 129a215bdf92..000000000000
--- a/libs/ldap/libldap/bind.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/* bind.c */
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2024 The OpenLDAP Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-/* Portions Copyright (c) 1990 Regents of the University of Michigan.
- * All rights reserved.
- */
-
-#include "portable.h"
-
-#include <stdio.h>
-
-#include <ac/stdlib.h>
-
-#include <ac/socket.h>
-#include <ac/string.h>
-#include <ac/time.h>
-
-#include "ldap-int.h"
-#include "ldap_log.h"
-
-/*
- *	BindRequest ::= SEQUENCE {
- *		version		INTEGER,
- *		name		DistinguishedName,	 -- who
- *		authentication	CHOICE {
- *			simple		[0] OCTET STRING -- passwd
- *			krbv42ldap	[1] OCTET STRING -- OBSOLETE
- *			krbv42dsa	[2] OCTET STRING -- OBSOLETE
- *			sasl		[3] SaslCredentials	-- LDAPv3
- *		}
- *	}
- *
- *	BindResponse ::= SEQUENCE {
- *		COMPONENTS OF LDAPResult,
- *		serverSaslCreds		OCTET STRING OPTIONAL -- LDAPv3
- *	}
- *
- * (Source: RFC 2251)
- */
-
-/*
- * ldap_bind - bind to the ldap server (and X.500).  The dn and password
- * of the entry to which to bind are supplied, along with the authentication
- * method to use.  The msgid of the bind request is returned on success,
- * -1 if there's trouble.  ldap_result() should be called to find out the
- * outcome of the bind request.
- *
- * Example:
- *	ldap_bind( ld, "cn=manager, o=university of michigan, c=us", "secret",
- *	    LDAP_AUTH_SIMPLE )
- */
-
-int
-ldap_bind( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd, int authmethod )
-{
-	Debug0( LDAP_DEBUG_TRACE, "ldap_bind\n" );
-
-	switch ( authmethod ) {
-	case LDAP_AUTH_SIMPLE:
-		return( ldap_simple_bind( ld, dn, passwd ) );
-
-	case LDAP_AUTH_SASL:
-		/* user must use ldap_sasl_bind */
-		/* FALL-THRU */
-
-	default:
-		ld->ld_errno = LDAP_AUTH_UNKNOWN;
-		return( -1 );
-	}
-}
-
-/*
- * ldap_bind_s - bind to the ldap server (and X.500).  The dn and password
- * of the entry to which to bind are supplied, along with the authentication
- * method to use.  This routine just calls whichever bind routine is
- * appropriate and returns the result of the bind (e.g. LDAP_SUCCESS or
- * some other error indication).
- *
- * Examples:
- *	ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us",
- *	    "secret", LDAP_AUTH_SIMPLE )
- *	ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us",
- *	    NULL, LDAP_AUTH_KRBV4 )
- */
-int
-ldap_bind_s(
-	LDAP *ld,
-	LDAP_CONST char *dn,
-	LDAP_CONST char *passwd,
-	int authmethod )
-{
-	Debug0( LDAP_DEBUG_TRACE, "ldap_bind_s\n" );
-
-	switch ( authmethod ) {
-	case LDAP_AUTH_SIMPLE:
-		return( ldap_simple_bind_s( ld, dn, passwd ) );
-
-	case LDAP_AUTH_SASL:
-		/* user must use ldap_sasl_bind */
-		/* FALL-THRU */
-
-	default:
-		return( ld->ld_errno = LDAP_AUTH_UNKNOWN );
-	}
-}
diff --git a/libs/ldap/libldap/pagectrl.c b/libs/ldap/libldap/pagectrl.c
deleted file mode 100644
index f716d08d119e..000000000000
--- a/libs/ldap/libldap/pagectrl.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2024 The OpenLDAP Foundation.
- * Copyright 2006 Hans Leidekker
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-#include "portable.h"
-
-#include <stdio.h>
-#include <ac/stdlib.h>
-#include <ac/string.h>
-#include <ac/time.h>
-
-#include "ldap-int.h"
-
-/* ---------------------------------------------------------------------------
-    ldap_create_page_control_value
-
-    Create and encode the value of the paged results control (RFC 2696).
-
-    ld          (IN) An LDAP session handle
-    pagesize    (IN) Page size requested
-    cookie      (IN) Opaque structure used by the server to track its
-                     location in the search results.  NULL on the
-                     first call.
-    value      (OUT) Control value, SHOULD be freed by calling
-					 ldap_memfree() when done.
-
-    pagedResultsControl ::= SEQUENCE {
-            controlType     1.2.840.113556.1.4.319,
-            criticality     BOOLEAN DEFAULT FALSE,
-            controlValue    searchControlValue }
-
-    searchControlValue ::= SEQUENCE {
-            size            INTEGER (0..maxInt),
-                                    -- requested page size from client
-                                    -- result set size estimate from server
-            cookie          OCTET STRING }
-
-   ---------------------------------------------------------------------------*/
-
-int
-ldap_create_page_control_value(
-	LDAP *ld,
-	ber_int_t pagesize,
-	struct berval	*cookie,
-	struct berval	*value )
-{
-	BerElement	*ber = NULL;
-	ber_tag_t	tag;
-	struct berval	null_cookie = { 0, NULL };
-
-	if ( ld == NULL || value == NULL ||
-		pagesize < 1 || pagesize > LDAP_MAXINT )
-	{
-		if ( ld )
-			ld->ld_errno = LDAP_PARAM_ERROR;
-		return LDAP_PARAM_ERROR;
-	}
-
-	assert( LDAP_VALID( ld ) );
-
-	value->bv_val = NULL;
-	value->bv_len = 0;
-	ld->ld_errno = LDAP_SUCCESS;
-
-	if ( cookie == NULL ) {
-		cookie = &null_cookie;
-	}
-
-	ber = ldap_alloc_ber_with_options( ld );
-	if ( ber == NULL ) {
-		ld->ld_errno = LDAP_NO_MEMORY;
-		return ld->ld_errno;
-	}
-
-	tag = ber_printf( ber, "{iO}", pagesize, cookie );
-	if ( tag == LBER_ERROR ) {
-		ld->ld_errno = LDAP_ENCODING_ERROR;
-		goto done;
-	}
-
-	if ( ber_flatten2( ber, value, 1 ) == -1 ) {
-		ld->ld_errno = LDAP_NO_MEMORY;
-	}
-
-done:;
-	if ( ber != NULL ) {
-		ber_free( ber, 1 );
-	}
-
-	return ld->ld_errno;
-}
-
-
-/* ---------------------------------------------------------------------------
-    ldap_create_page_control
-
-    Create and encode a page control.
-
-    ld          (IN) An LDAP session handle
-    pagesize    (IN) Page size requested
-    cookie      (IN) Opaque structure used by the server to track its
-                     location in the search results.  NULL on the
-                     first call.
-    value      (OUT) Control value, SHOULD be freed by calling
-					 ldap_memfree() when done.
-    iscritical  (IN) Criticality
-    ctrlp      (OUT) LDAP control, SHOULD be freed by calling
-					 ldap_control_free() when done.
-
-    pagedResultsControl ::= SEQUENCE {
-            controlType     1.2.840.113556.1.4.319,
-            criticality     BOOLEAN DEFAULT FALSE,
-            controlValue    searchControlValue }
-
-    searchControlValue ::= SEQUENCE {
-            size            INTEGER (0..maxInt),
-                                    -- requested page size from client
-                                    -- result set size estimate from server
-            cookie          OCTET STRING }
-
-   ---------------------------------------------------------------------------*/
-
-int
-ldap_create_page_control(
-	LDAP		*ld,
-	ber_int_t	pagesize,
-	struct berval	*cookie,
-	int		iscritical,
-	LDAPControl	**ctrlp )
-{
-	struct berval	value;
-
-	if ( ctrlp == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return ld->ld_errno;
-	}
-
-	ld->ld_errno = ldap_create_page_control_value( ld,
-		pagesize, cookie, &value );
-	if ( ld->ld_errno == LDAP_SUCCESS ) {
-		ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS,
-			iscritical, &value, 0, ctrlp );
-		if ( ld->ld_errno != LDAP_SUCCESS ) {
-			LDAP_FREE( value.bv_val );
-		}
-	}
-
-	return ld->ld_errno;
-}
-
-
-/* ---------------------------------------------------------------------------
-    ldap_parse_pageresponse_control
-
-    Decode a page control.
-
-    ld          (IN) An LDAP session handle
-    ctrl        (IN) The page response control
-    count      (OUT) The number of entries in the page.
-    cookie     (OUT) Opaque cookie.  Use ldap_memfree() to
-                     free the bv_val member of this structure.
-
-   ---------------------------------------------------------------------------*/
-
-int
-ldap_parse_pageresponse_control(
-	LDAP *ld,
-	LDAPControl *ctrl,
-	ber_int_t *countp,
-	struct berval *cookie )
-{
-	BerElement *ber;
-	ber_tag_t tag;
-	ber_int_t count;
-
-	if ( ld == NULL || ctrl == NULL || cookie == NULL ) {
-		if ( ld )
-			ld->ld_errno = LDAP_PARAM_ERROR;
-		return LDAP_PARAM_ERROR;
-	}
-
-	/* Create a BerElement from the berval returned in the control. */
-	ber = ber_init( &ctrl->ldctl_value );
-
-	if ( ber == NULL ) {
-		ld->ld_errno = LDAP_NO_MEMORY;
-		return ld->ld_errno;
-	}
-
-	/* Extract the count and cookie from the control. */
-	tag = ber_scanf( ber, "{io}", &count, cookie );
-        ber_free( ber, 1 );
-
-	if ( tag == LBER_ERROR ) {
-		ld->ld_errno = LDAP_DECODING_ERROR;
-	} else {
-		ld->ld_errno = LDAP_SUCCESS;
-
-		if ( countp != NULL ) {
-			*countp = (unsigned long)count;
-		}
-	}
-
-	return ld->ld_errno;
-}
-
-/* ---------------------------------------------------------------------------
-    ldap_parse_page_control
-
-    Decode a page control.
-
-    ld          (IN) An LDAP session handle
-    ctrls       (IN) Response controls
-    count      (OUT) The number of entries in the page.
-    cookie     (OUT) Opaque cookie.  Use ldap_memfree() to
-                     free the bv_val member of this structure.
-
-   ---------------------------------------------------------------------------*/
-
-int
-ldap_parse_page_control(
-	LDAP		*ld,
-	LDAPControl	**ctrls,
-	ber_int_t *countp,
-	struct berval	**cookiep )
-{
-	LDAPControl *c;
-	struct berval	cookie;
-
-	if ( cookiep == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return ld->ld_errno;
-	}
-
-	if ( ctrls == NULL ) {
-		ld->ld_errno =  LDAP_CONTROL_NOT_FOUND;
-		return ld->ld_errno;
-	}
-
-	c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
-	if ( c == NULL ) {
-		/* No page control was found. */
-		ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
-		return ld->ld_errno;
-	}
-
-	ld->ld_errno = ldap_parse_pageresponse_control( ld, c, countp, &cookie );
-	if ( ld->ld_errno == LDAP_SUCCESS ) {
-		*cookiep = LDAP_MALLOC( sizeof( struct berval ) );
-		if ( *cookiep == NULL ) {
-			ld->ld_errno = LDAP_NO_MEMORY;
-		} else {
-			**cookiep = cookie;
-		}
-	}
-
-	return ld->ld_errno;
-}
diff --git a/libs/ldap/libldap/print.c b/libs/ldap/libldap/print.c
deleted file mode 100644
index 0a57b1cbb91b..000000000000
--- a/libs/ldap/libldap/print.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2024 The OpenLDAP Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-#include "portable.h"
-
-#include <stdio.h>
-
-#include <ac/ctype.h>
-#include <ac/stdarg.h>
-#include <ac/string.h>
-#include <ac/time.h>
-
-#include "ldap-int.h"
-
-/*
- * ldap log
- */
-
-static int ldap_log_check( LDAP *ld, int loglvl )
-{
-	int errlvl;
-
-	if(ld == NULL) {
-		errlvl = ldap_debug;
-	} else {
-		errlvl = ld->ld_debug;
-	}
-
-	return errlvl & loglvl ? 1 : 0;
-}
-
-int ldap_log_printf( LDAP *ld, int loglvl, const char *fmt, ... )
-{
-	char buf[ 1024 ];
-	va_list ap;
-
-	if ( !ldap_log_check( ld, loglvl )) {
-		return 0;
-	}
-
-	va_start( ap, fmt );
-
-	buf[sizeof(buf) - 1] = '\0';
-	vsnprintf( buf, sizeof(buf)-1, fmt, ap );
-
-	va_end(ap);
-
-	(*ber_pvt_log_print)( buf );
-	return 1;
-}
diff --git a/libs/ldap/libldap/string.c b/libs/ldap/libldap/string.c
deleted file mode 100644
index 68967ef734bf..000000000000
--- a/libs/ldap/libldap/string.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/* $OpenLDAP$ */
-/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2024 The OpenLDAP Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted only as authorized by the OpenLDAP
- * Public License.
- *
- * A copy of this license is available in the file LICENSE in the
- * top-level directory of the distribution or, alternatively, at
- * <http://www.OpenLDAP.org/license.html>.
- */
-
-/*
- * Locale-specific 1-byte character versions
- * See utf-8.c for UTF-8 versions
- */
-
-#include "portable.h"
-
-#include <ac/stdlib.h>
-#include <ac/string.h>
-#include <ac/time.h>
-#include <ac/ctype.h>
-
-#include "ldap-int.h"
-
-
-#if defined ( HAVE_STRSPN )
-#define int_strspn strspn
-#else
-static int int_strspn( const char *str, const char *delim )
-{
-	int pos;
-	const char *p=delim;
-
-	for( pos=0; (*str) ; pos++,str++) {
-		if (*str!=*p) {
-			for( p=delim; (*p) ; p++ ) {
-				if (*str==*p) {
-					break;
-				}
-			}
-		}
-
-		if (*p=='\0') {
-			return pos;
-		}
-	}
-	return pos;
-}
-#endif
-
-#if defined( HAVE_STRPBRK )
-#define int_strpbrk strpbrk
-#else
-static char *(int_strpbrk)( const char *str, const char *accept )
-{
-	const char *p;
-
-	for( ; (*str) ; str++ ) {
-		for( p=accept; (*p) ; p++) {
-			if (*str==*p) {
-				return str;
-			}
-		}
-	}
-
-	return NULL;
-}
-#endif
-
-char *(ldap_pvt_strtok)( char *str, const char *delim, char **pos )
-{
-	char *p;
-
-	if (pos==NULL) {
-		return NULL;
-	}
-
-	if (str==NULL) {
-		if (*pos==NULL) {
-			return NULL;
-		}
-
-		str=*pos;
-	}
-
-	/* skip any initial delimiters */
-	str += int_strspn( str, delim );
-	if (*str == '\0') {
-		return NULL;
-	}
-
-	p = int_strpbrk( str, delim );
-	if (p==NULL) {
-		*pos = NULL;
-
-	} else {
-		*p ='\0';
-		*pos = p+1;
-	}
-
-	return str;
-}
-
-char *
-ldap_pvt_str2upper( char *str )
-{
-	char    *s;
-
-	/* to upper */
-	if ( str ) {
-		for ( s = str; *s; s++ ) {
-			*s = TOUPPER( (unsigned char) *s );
-		}
-	}
-
-	return( str );
-}
-
-struct berval *
-ldap_pvt_str2upperbv( char *str, struct berval *bv )
-{
-	char    *s = NULL;
-
-	assert( bv != NULL );
-
-	/* to upper */
-	if ( str ) {
-		for ( s = str; *s; s++ ) {
-			*s = TOUPPER( (unsigned char) *s );
-		}
-	}
-
-	bv->bv_val = str;
-	bv->bv_len = (ber_len_t)(s - str);
-
-	return( bv );
-}
-
-char *
-ldap_pvt_str2lower( char *str )
-{
-	char    *s;
-
-	/* to lower */
-	if ( str ) {
-		for ( s = str; *s; s++ ) {
-			*s = TOLOWER( (unsigned char) *s );
-		}
-	}
-
-	return( str );
-}
-
-struct berval *
-ldap_pvt_str2lowerbv( char *str, struct berval *bv )
-{
-	char    *s = NULL;
-
-	assert( bv != NULL );
-
-	/* to lower */
-	if ( str ) {
-		for ( s = str; *s; s++ ) {
-			*s = TOLOWER( (unsigned char) *s );
-		}
-	}
-
-	bv->bv_val = str;
-	bv->bv_len = (ber_len_t)(s - str);
-
-	return( bv );
-}
diff --git a/libs/mpg123/AUTHORS b/libs/mpg123/AUTHORS
index d322289d4d26..a01dfb068fa3 100644
--- a/libs/mpg123/AUTHORS
+++ b/libs/mpg123/AUTHORS
@@ -14,9 +14,11 @@ out ovbious syntax errors that make compilers cry do not land here.
 
 Current maintainers with various sorts of contributions:
 	Thomas Orgis <thomas@orgis.org>
+	Taihei Momma <tmkk@mac.com>
+
+Past maintainers mainly for MS Windows platform:
 	Patrick Dehne <patrick@steidle.net>
 	Jonathan Yong <10walls@gmail.com>
-	Taihei Momma <tmkk@mac.com>
 
 Co-initiator of the revived mpg123 project, but not that involved anymore:
 	Nicholas J Humfrey <njh@ecs.soton.ac.uk>
diff --git a/libs/mpg123/config.h b/libs/mpg123/config.h
index b7a117c83fc7..286ffd0cd9f9 100644
--- a/libs/mpg123/config.h
+++ b/libs/mpg123/config.h
@@ -445,7 +445,7 @@
 #define PACKAGE_NAME "mpg123"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "mpg123 1.33.0"
+#define PACKAGE_STRING "mpg123 1.33.5"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "mpg123"
@@ -454,7 +454,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.33.0"
+#define PACKAGE_VERSION "1.33.5"
 
 /* Define to only include portable library API (no off_t, no internal I/O). */
 /* #undef PORTABLE_API */
@@ -486,7 +486,7 @@
 /* #undef USE_YASM_FOR_AVX */
 
 /* Version number of package */
-#define VERSION "1.33.0"
+#define VERSION "1.33.5"
 
 /* Define to use Win32 named pipes */
 #define WANT_WIN32_FIFO 1
diff --git a/libs/mpg123/src/include/mpg123.h b/libs/mpg123/src/include/mpg123.h
index a335b5e65ae4..e7a97e73e612 100644
--- a/libs/mpg123/src/include/mpg123.h
+++ b/libs/mpg123/src/include/mpg123.h
@@ -23,7 +23,7 @@
  */
 #define MPG123_API_VERSION 49
 /** library patch level at client build time */
-#define MPG123_PATCHLEVEL  3
+#define MPG123_PATCHLEVEL  4
 
 #ifndef MPG123_EXPORT
 /** Defines needed for MS Visual Studio(tm) DLL builds.
@@ -338,7 +338,11 @@ enum mpg123_param_flags
 	 *  the stream is assumed as non-seekable unless overridden.
 	 */
 	,MPG123_FORCE_SEEKABLE = 0x40000 /**< 19th bit: Force the stream to be seekable. */
-	,MPG123_STORE_RAW_ID3  = 0x80000 /**< store raw ID3 data (even if skipping) */
+	,MPG123_STORE_RAW_ID3  = 0x80000 /**< Store raw ID3 data (even if skipping).
+	 *  Before mpg123 1.33.2 (libmpg123 API 49, patchlevel 4), this has to be combined with
+	 *  MPG123_SKIP_ID3 to avoid getting corrupted data due to the ID3 parser inserting
+	 *  encoding bytes for its own convenience.
+	 */
 	,MPG123_FORCE_ENDIAN   = 0x100000 /**< Enforce endianess of output samples.
 	 *  This is not reflected in the format codes. If this flag is set along with
 	 *  MPG123_BIG_ENDIAN, MPG123_ENC_SIGNED16 means s16be, without
diff --git a/libs/mpg123/src/libmpg123/id3.c b/libs/mpg123/src/libmpg123/id3.c
index 0bd5cda70b6e..cb73bd5fdc3b 100644
--- a/libs/mpg123/src/libmpg123/id3.c
+++ b/libs/mpg123/src/libmpg123/id3.c
@@ -330,9 +330,9 @@ void INT123_id3_link(mpg123_handle *fr)
 	Since we can overwrite strings with ID3 update frames, don't free
 	memory, just grow strings.
 */
-static void store_id3_text(mpg123_string *sb, unsigned char *source, size_t source_size, const int noquiet, const int notranslate)
+static void store_id3_text(mpg123_string *sb, unsigned char encoding, unsigned char *source
+,	size_t source_size, const int noquiet, const int notranslate)
 {
-	unsigned char encoding;
 	if(sb) // Always overwrite, even with nothing.
 		sb->fill = 0;
 	if(!source_size)
@@ -345,25 +345,25 @@ static void store_id3_text(mpg123_string *sb, unsigned char *source, size_t sour
 	if(notranslate)
 	{
 		/* Future: Add a path for ID3 errors. */
-		if(!mpg123_grow_string(sb, source_size))
+		if(!mpg123_grow_string(sb, source_size+1))
 		{
 			if(noquiet) error("Cannot resize target string, out of memory?");
 			return;
 		}
-		memcpy(sb->p, source, source_size);
-		sb->fill = source_size;
-		debug1("stored undecoded ID3 text of size %zu", source_size);
+		sb->p[0] = (char)encoding;
+		memcpy(sb->p+1, source, source_size);
+		sb->fill = source_size+1;
+		debug1("stored undecoded ID3 text of size %zu", source_size+1);
 		return;
 	}
 
-	encoding = source[0];
 	if(encoding > mpg123_id3_enc_max)
 	{
 		if(noquiet)
 			error1("Unknown text encoding %u, I take no chances, sorry!", encoding);
 		return;
 	}
-	INT123_id3_to_utf8(sb, encoding, source+1, source_size-1, noquiet);
+	INT123_id3_to_utf8(sb, encoding, source, source_size, noquiet);
 
 	if(sb->fill) debug1("UTF-8 string (the first one): %s", sb->p);
 	else if(noquiet) error("unable to convert string to UTF-8 (out of memory, junk input?)!");
@@ -449,6 +449,11 @@ static void process_text(mpg123_handle *fr, unsigned char *realdata, size_t real
 {
 	/* Text encoding          $xx */
 	/* The text (encoded) ... */
+	if(realsize < 1)
+	{
+		if(NOQUIET) error("Not even an encoding byte?");
+		return;
+	}
 	mpg123_text *t = add_text(fr, id);
 	if(VERBOSE4) fprintf(stderr, "Note: Storing text from %s encoding\n", enc_name(realdata[0]));
 	if(t == NULL)
@@ -460,7 +465,8 @@ static void process_text(mpg123_handle *fr, unsigned char *realdata, size_t real
 	?	(char[5]) { t->id[0], t->id[1], t->id[2], t->id[3], 0 }
 	:	"(nil)" );
 	memcpy(t->id, id, 4);
-	store_id3_text(&t->text, realdata, realsize, NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
+	store_id3_text( &t->text, realdata[0], realdata+1, realsize-1
+	,	NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
 	if(VERBOSE4) // Do not print unsanitized text to terminals!
 		fprintf(stderr, "Note: ID3v2 %c%c%c%c text frame stored\n", id[0], id[1], id[2], id[3]);
 }
@@ -577,8 +583,6 @@ static void process_comment(mpg123_handle *fr, enum frame_types tt, unsigned cha
 		return;
 	}
 	memcpy(lang, realdata+1, 3);
-	/* Now I can abuse a byte from lang for the encoding. */
-	descr[-1] = encoding;
 	/* Be careful with finding the end of description, I have to honor encoding here. */
 	text = next_text(descr, encoding, realsize-(descr-realdata));
 	if(text == NULL)
@@ -591,14 +595,14 @@ static void process_comment(mpg123_handle *fr, enum frame_types tt, unsigned cha
 		mpg123_string description;
 		mpg123_init_string(&description);
 		// Store the text, with desired encoding, but for comments always a local copy in UTF-8.
-		store_id3_text( &description, descr-1, text-descr+1
+		store_id3_text( &description, encoding, descr, text-descr
 		,	NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
 		if(tt == comment)
-			store_id3_text( &localcom.description, descr-1, text-descr+1
+			store_id3_text( &localcom.description, encoding, descr, text-descr
 			,	NOQUIET, 0 );
 		if(VERBOSE4)
 			fprintf( stderr, "Note: Storing comment from %s encoding\n"
-			,	enc_name(realdata[0]) );
+			,	enc_name(encoding) );
 		xcom = tt == uslt
 		?	add_uslt(fr, lang, &description)
 		:	add_comment(fr, lang, &description);
@@ -616,8 +620,8 @@ static void process_comment(mpg123_handle *fr, enum frame_types tt, unsigned cha
 		mpg123_move_string(&description, &xcom->description);
 	}
 
-	text[-1] = encoding; /* Byte abusal for encoding... */
-	store_id3_text(&xcom->text, text-1, realsize+1-(text-realdata), NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
+	store_id3_text( &xcom->text, encoding, text, realsize-(text-realdata)
+	,	NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
 	/* Remember: I will probably decode the above (again) for rva comment checking. So no messing around, please. */
 
 	if(VERBOSE4) /* Do _not_ print the verbatim text: The encoding might be funny! */
@@ -641,7 +645,8 @@ static void process_comment(mpg123_handle *fr, enum frame_types tt, unsigned cha
 		if((rva_mode > -1) && (fr->rva.level[rva_mode] <= rva_level))
 		{
 			/* Only translate the contents in here where we really need them. */
-			store_id3_text(&localcom.text, text-1, realsize+1-(text-realdata), NOQUIET, 0);
+			store_id3_text( &localcom.text, encoding, text, realsize-(text-realdata)
+			,	NOQUIET, 0 );
 			if(localcom.text.fill > 0)
 			{
 				fr->rva.gain[rva_mode] = (float) atof(localcom.text.p);
@@ -689,7 +694,7 @@ static void process_extra(mpg123_handle *fr, unsigned char* realdata, size_t rea
 		mpg123_init_string(&description);
 		/* The outside storage gets reencoded to UTF-8 only if not requested otherwise.
 		   Remember that we really need the -1 here to hand in the encoding byte!*/
-		store_id3_text( &description, descr-1, text-descr+1
+		store_id3_text( &description, encoding, descr, text-descr
 		,	NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
 		xex = add_extra(fr, &description);
 		if(xex)
@@ -706,10 +711,10 @@ static void process_extra(mpg123_handle *fr, unsigned char* realdata, size_t rea
 	init_mpg123_text(&localex); /* For our local copy. */
 
 	/* Our local copy is always stored in UTF-8! */
-	store_id3_text(&localex.description, descr-1, text-descr+1, NOQUIET, 0);
+	store_id3_text(&localex.description, encoding, descr, text-descr, NOQUIET, 0);
 	/* At first, only store the outside copy of the payload. We may not need the local copy. */
-	text[-1] = encoding;
-	store_id3_text(&xex->text, text-1, realsize-(text-realdata)+1, NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
+	store_id3_text( &xex->text, encoding, text, realsize-(text-realdata)
+	,	NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
 
 	/* Now check if we would like to interpret this extra info for RVA. */
 	if(localex.description.fill > 0)
@@ -737,7 +742,8 @@ static void process_extra(mpg123_handle *fr, unsigned char* realdata, size_t rea
 		if((rva_mode > -1) && (fr->rva.level[rva_mode] <= rva_level))
 		{
 			/* Now we need the translated copy of the data. */
-			store_id3_text(&localex.text, text-1, realsize-(text-realdata)+1, NOQUIET, 0);
+			store_id3_text( &localex.text, encoding, text, realsize-(text-realdata)
+			,	NOQUIET, 0 );
 			if(localex.text.fill > 0)
 			{
 				if(is_peak)
diff --git a/libs/mpg123/src/version.h b/libs/mpg123/src/version.h
index b0fe3a50fbac..7d622814f698 100644
--- a/libs/mpg123/src/version.h
+++ b/libs/mpg123/src/version.h
@@ -16,7 +16,7 @@
 // only single spaces as separator to ease parsing by build scripts
 #define MPG123_MAJOR 1
 #define MPG123_MINOR 33
-#define MPG123_PATCH 0
+#define MPG123_PATCH 5
 // Don't get too wild with that to avoid confusing m4. No brackets.
 // Also, it should fit well into a sane file name for the tarball.
 #define MPG123_SUFFIX ""
diff --git a/libs/png/AUTHORS b/libs/png/AUTHORS
index 8278a447dcc9..6726fc6a7ce2 100644
--- a/libs/png/AUTHORS
+++ b/libs/png/AUTHORS
@@ -27,6 +27,7 @@ Authors, for copyright and licensing purposes.
  * Mans Rullgard
  * Matt Sarett
  * Mike Klein
+ * Mohammad Seet
  * Pascal Massimino
  * Paul Schmidt
  * Petr Simecek
diff --git a/libs/png/png.c b/libs/png/png.c
index fda0a8e6ae06..cebe7af1f801 100644
--- a/libs/png/png.c
+++ b/libs/png/png.c
@@ -13,7 +13,7 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_56 Your_png_h_is_not_version_1_6_56;
+typedef png_libpng_version_1_6_58 Your_png_h_is_not_version_1_6_58;
 
 /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
  * corresponding macro definitions.  This causes a compile time failure if
@@ -820,7 +820,7 @@ png_get_copyright(png_const_structrp png_ptr)
    return PNG_STRING_COPYRIGHT
 #else
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.56" PNG_STRING_NEWLINE \
+      "libpng version 1.6.58" PNG_STRING_NEWLINE \
       "Copyright (c) 2018-2026 Cosmin Truta" PNG_STRING_NEWLINE \
       "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
       PNG_STRING_NEWLINE \
diff --git a/libs/png/png.h b/libs/png/png.h
index 385de4b42d4e..1d98cc53e1db 100644
--- a/libs/png/png.h
+++ b/libs/png/png.h
@@ -1,6 +1,6 @@
 /* png.h - header file for PNG reference library
  *
- * libpng version 1.6.56
+ * libpng version 1.6.58
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
@@ -14,7 +14,7 @@
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
  *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
  *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.56, March 2026:
+ *   libpng versions 1.6.36, December 2018, through 1.6.58, April 2026:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
@@ -238,7 +238,7 @@
  *    ...
  *    1.5.30                  15    10530  15.so.15.30[.0]
  *    ...
- *    1.6.56                  16    10656  16.so.16.56[.0]
+ *    1.6.58                  16    10658  16.so.16.58[.0]
  *
  *    Henceforth the source version will match the shared-library major and
  *    minor numbers; the shared-library major version number will be used for
@@ -274,7 +274,7 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.56"
+#define PNG_LIBPNG_VER_STRING "1.6.58"
 #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
 
 /* The versions of shared library builds should stay in sync, going forward */
@@ -285,7 +285,7 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 56
+#define PNG_LIBPNG_VER_RELEASE 58
 
 /* This should be zero for a public release, or non-zero for a
  * development version.
@@ -316,7 +316,7 @@
  * From version 1.0.1 it is:
  * XXYYZZ, where XX=major, YY=minor, ZZ=release
  */
-#define PNG_LIBPNG_VER 10656 /* 1.6.56 */
+#define PNG_LIBPNG_VER 10658 /* 1.6.58 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
@@ -426,7 +426,7 @@ extern "C" {
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char *png_libpng_version_1_6_56;
+typedef char *png_libpng_version_1_6_58;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
diff --git a/libs/png/pngconf.h b/libs/png/pngconf.h
index d878ee84143a..3eda9d047bd2 100644
--- a/libs/png/pngconf.h
+++ b/libs/png/pngconf.h
@@ -1,6 +1,6 @@
 /* pngconf.h - machine-configurable file for libpng
  *
- * libpng version 1.6.56
+ * libpng version 1.6.58
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/libs/png/pnglibconf.h b/libs/png/pnglibconf.h
index 686ba8f82f52..b562b799d164 100644
--- a/libs/png/pnglibconf.h
+++ b/libs/png/pnglibconf.h
@@ -1,6 +1,6 @@
 /* pnglibconf.h - library build configuration */
 
-/* libpng version 1.6.56 */
+/* libpng version 1.6.58 */
 
 /* Copyright (c) 2018-2026 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/libs/png/pngrtran.c b/libs/png/pngrtran.c
index 978dac588801..0ac8df749e9e 100644
--- a/libs/png/pngrtran.c
+++ b/libs/png/pngrtran.c
@@ -2070,19 +2070,15 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
 {
    png_debug(1, "in png_read_transform_info");
 
-   if (png_ptr->transformations != 0)
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+       info_ptr->palette != NULL && png_ptr->palette != NULL)
    {
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-          info_ptr->palette != NULL && png_ptr->palette != NULL)
-      {
-         /* Sync info_ptr->palette with png_ptr->palette.
-          * The function png_init_read_transformations may have modified
-          * png_ptr->palette in place (e.g. for gamma correction or for
-          * background compositing).
-          */
-         memcpy(info_ptr->palette, png_ptr->palette,
-             PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)));
-      }
+      /* Sync info_ptr->palette with png_ptr->palette, which may
+       * have been modified by png_init_read_transformations
+       * (e.g. for gamma correction or background compositing).
+       */
+      memcpy(info_ptr->palette, png_ptr->palette,
+          PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)));
    }
 
 #ifdef PNG_READ_EXPAND_SUPPORTED
@@ -2379,7 +2375,7 @@ png_do_unpack(png_row_infop row_info, png_bytep row)
       }
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_width * row_info->channels;
+      row_info->rowbytes = (size_t)row_width * row_info->channels;
    }
 }
 #endif
@@ -2581,7 +2577,7 @@ png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
 
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
+      row_info->rowbytes = (size_t)row_info->width * row_info->channels;
    }
 }
 #endif
@@ -2609,7 +2605,7 @@ png_do_chop(png_row_infop row_info, png_bytep row)
 
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
+      row_info->rowbytes = (size_t)row_info->width * row_info->channels;
    }
 }
 #endif
@@ -2845,7 +2841,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = lo_filler;
             row_info->channels = 2;
             row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
+            row_info->rowbytes = (size_t)row_width * 2;
          }
 
          else
@@ -2860,7 +2856,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 2;
             row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
+            row_info->rowbytes = (size_t)row_width * 2;
          }
       }
 
@@ -2883,7 +2879,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = hi_filler;
             row_info->channels = 2;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
 
          else
@@ -2900,7 +2896,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 2;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
       }
 #endif
@@ -2924,7 +2920,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = lo_filler;
             row_info->channels = 4;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
 
          else
@@ -2941,7 +2937,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 4;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
       }
 
@@ -2968,7 +2964,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = hi_filler;
             row_info->channels = 4;
             row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
+            row_info->rowbytes = (size_t)row_width * 8;
          }
 
          else
@@ -2990,7 +2986,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
 
             row_info->channels = 4;
             row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
+            row_info->rowbytes = (size_t)row_width * 8;
          }
       }
 #endif
@@ -4484,7 +4480,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
                }
                row_info->bit_depth = 8;
                row_info->pixel_depth = 32;
-               row_info->rowbytes = row_width * 4;
+               row_info->rowbytes = (size_t)row_width * 4;
                row_info->color_type = 6;
                row_info->channels = 4;
             }
@@ -4492,7 +4488,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
             else
             {
                sp = row + (size_t)row_width - 1;
-               dp = row + (size_t)(row_width * 3) - 1;
+               dp = row + (size_t)row_width * 3 - 1;
                i = 0;
 #ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
                i = png_do_expand_palette_rgb8_neon(png_ptr, row_info, row,
@@ -4511,7 +4507,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
 
                row_info->bit_depth = 8;
                row_info->pixel_depth = 24;
-               row_info->rowbytes = row_width * 3;
+               row_info->rowbytes = (size_t)row_width * 3;
                row_info->color_type = 2;
                row_info->channels = 3;
             }
diff --git a/libs/png/pngset.c b/libs/png/pngset.c
index b9ccb7fb156d..513c51eb4a8f 100644
--- a/libs/png/pngset.c
+++ b/libs/png/pngset.c
@@ -385,6 +385,7 @@ void PNGAPI
 png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
     png_const_uint_16p hist)
 {
+   png_uint_16 safe_hist[PNG_MAX_PALETTE_LENGTH];
    int i;
 
    png_debug1(1, "in %s storage function", "hIST");
@@ -401,6 +402,13 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
       return;
    }
 
+   /* Snapshot the caller's hist before freeing, in case it points to
+    * info_ptr->hist (getter-to-setter aliasing).
+    */
+   memcpy(safe_hist, hist, (unsigned int)info_ptr->num_palette *
+       (sizeof (png_uint_16)));
+   hist = safe_hist;
+
    png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
 
    /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
@@ -742,7 +750,7 @@ void PNGAPI
 png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
     png_const_colorp palette, int num_palette)
 {
-
+   png_color safe_palette[PNG_MAX_PALETTE_LENGTH];
    png_uint_32 max_palette_length;
 
    png_debug1(1, "in %s storage function", "PLTE");
@@ -776,6 +784,15 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
       png_error(png_ptr, "Invalid palette");
    }
 
+   /* Snapshot the caller's palette before freeing, in case it points to
+    * info_ptr->palette (getter-to-setter aliasing).
+    */
+   if (num_palette > 0)
+      memcpy(safe_palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
+
+   palette = safe_palette;
+
    png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
 
    /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
@@ -937,6 +954,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
     png_const_textp text_ptr, int num_text)
 {
    int i;
+   png_textp old_text = NULL;
 
    png_debug1(1, "in text storage function, chunk typeid = 0x%lx",
       png_ptr == NULL ? 0xabadca11UL : (unsigned long)png_ptr->chunk_name);
@@ -984,7 +1002,10 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
          return 1;
       }
 
-      png_free(png_ptr, info_ptr->text);
+      /* Defer freeing the old array until after the copy loop below,
+       * in case text_ptr aliases info_ptr->text (getter-to-setter).
+       */
+      old_text = info_ptr->text;
 
       info_ptr->text = new_text;
       info_ptr->free_me |= PNG_FREE_TEXT;
@@ -1069,6 +1090,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
       {
          png_chunk_report(png_ptr, "text chunk: out of memory",
              PNG_CHUNK_WRITE_ERROR);
+         png_free(png_ptr, old_text);
 
          return 1;
       }
@@ -1122,6 +1144,8 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
       png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
    }
 
+   png_free(png_ptr, old_text);
+
    return 0;
 }
 #endif
@@ -1165,6 +1189,16 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
 
    if (trans_alpha != NULL)
    {
+       /* Snapshot the caller's trans_alpha before freeing, in case it
+        * points to info_ptr->trans_alpha (getter-to-setter aliasing).
+        */
+       png_byte safe_trans[PNG_MAX_PALETTE_LENGTH];
+
+       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+          memcpy(safe_trans, trans_alpha, (size_t)num_trans);
+
+       trans_alpha = safe_trans;
+
        png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
 
        if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
@@ -1249,6 +1283,7 @@ png_set_sPLT(png_const_structrp png_ptr,
  */
 {
    png_sPLT_tp np;
+   png_sPLT_tp old_spalettes;
 
    png_debug1(1, "in %s storage function", "sPLT");
 
@@ -1269,7 +1304,10 @@ png_set_sPLT(png_const_structrp png_ptr,
       return;
    }
 
-   png_free(png_ptr, info_ptr->splt_palettes);
+   /* Defer freeing the old array until after the copy loop below,
+    * in case entries aliases info_ptr->splt_palettes (getter-to-setter).
+    */
+   old_spalettes = info_ptr->splt_palettes;
 
    info_ptr->splt_palettes = np;
    info_ptr->free_me |= PNG_FREE_SPLT;
@@ -1333,6 +1371,8 @@ png_set_sPLT(png_const_structrp png_ptr,
    }
    while (--nentries);
 
+   png_free(png_ptr, old_spalettes);
+
    if (nentries > 0)
       png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
 }
@@ -1381,6 +1421,7 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
     png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
 {
    png_unknown_chunkp np;
+   png_unknown_chunkp old_unknowns;
 
    if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||
        unknowns == NULL)
@@ -1427,7 +1468,10 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
       return;
    }
 
-   png_free(png_ptr, info_ptr->unknown_chunks);
+   /* Defer freeing the old array until after the copy loop below,
+    * in case unknowns aliases info_ptr->unknown_chunks (getter-to-setter).
+    */
+   old_unknowns = info_ptr->unknown_chunks;
 
    info_ptr->unknown_chunks = np; /* safe because it is initialized */
    info_ptr->free_me |= PNG_FREE_UNKN;
@@ -1473,6 +1517,8 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
       ++np;
       ++(info_ptr->unknown_chunks_num);
    }
+
+   png_free(png_ptr, old_unknowns);
 }
 
 void PNGAPI
diff --git a/libs/sqlite3/Makefile.in b/libs/sqlite3/Makefile.in
index b34e08b4a9f1..763cee343281 100644
--- a/libs/sqlite3/Makefile.in
+++ b/libs/sqlite3/Makefile.in
@@ -1,6 +1,6 @@
 STATICLIB = libsqlite3.a
 EXTRAINCL = $(SQLITE3_PE_CFLAGS)
-EXTRADEFS = -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_DEBUG=1 \
+EXTRADEFS = -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 \
 		-DSQLITE_SYSTEM_MALLOC=1 -DSQLITE_OMIT_LOCALTIME=1 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_PERCENTILE \
 		-DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=WINAPI
 SOURCES = \
diff --git a/libs/sqlite3/sqlite3.c b/libs/sqlite3/sqlite3.c
index facea8f68071..fdaf4a939468 100644
--- a/libs/sqlite3/sqlite3.c
+++ b/libs/sqlite3/sqlite3.c
@@ -1,7 +1,7 @@
 /* Fixed up with dlls/winsqlite/make_sqlite.py */
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
-** version 3.51.1.  By combining all the individual C code files into this
+** version 3.53.2.  By combining all the individual C code files into this
 ** single large file, the entire code can be compiled as a single translation
 ** unit.  This allows many compilers to do optimizations that would not be
 ** possible if the files were compiled separately.  Performance improvements
@@ -19,9 +19,9 @@
 ** separate file. This file contains only code for the core SQLite library.
 **
 ** The content in this amalgamation comes from Fossil check-in
-** 281fc0e9afc38674b9b0991943b9e9d1e64c with changes in files:
-**
+** d6e03d8c777cfa2d35e3b60d8ec3e0187f3e with changes in files:
 **
+**    
 */
 #ifndef SQLITE_AMALGAMATION
 #define SQLITE_CORE 1
@@ -468,12 +468,12 @@ extern "C" {
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.51.1"
-#define SQLITE_VERSION_NUMBER 3051001
-#define SQLITE_SOURCE_ID      "2025-11-28 17:28:25 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af38a88"
-#define SQLITE_SCM_BRANCH     "branch-3.51"
-#define SQLITE_SCM_TAGS       "release version-3.51.1"
-#define SQLITE_SCM_DATETIME   "2025-11-28T17:28:25.933Z"
+#define SQLITE_VERSION        "3.53.2"
+#define SQLITE_VERSION_NUMBER 3053002
+#define SQLITE_SOURCE_ID      "2026-06-03 19:12:13 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24"
+#define SQLITE_SCM_BRANCH     "branch-3.53"
+#define SQLITE_SCM_TAGS       "release version-3.53.2"
+#define SQLITE_SCM_DATETIME   "2026-06-03T19:12:13.350Z"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
@@ -900,7 +900,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_exec(
 #define SQLITE_WARNING_AUTOINDEX       (SQLITE_WARNING | (1<<8))
 #define SQLITE_AUTH_USER               (SQLITE_AUTH | (1<<8))
 #define SQLITE_OK_LOAD_PERMANENTLY     (SQLITE_OK | (1<<8))
-#define SQLITE_OK_SYMLINK              (SQLITE_OK | (2<<8)) /* internal use only */
+#define SQLITE_OK_SYMLINK              (SQLITE_OK | (2<<8)) /* internal only */
 
 /*
 ** CAPI3REF: Flags For File Open Operations
@@ -1612,6 +1612,12 @@ struct sqlite3_io_methods {
 #define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
 #define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO
 
+/* reserved file-control numbers:
+**                                         101
+**                                         102
+**                                         103
+*/
+
 
 /*
 ** CAPI3REF: Mutex Handle
@@ -1812,7 +1818,7 @@ typedef const char *sqlite3_filename;
 ** greater and the function pointer is not NULL) and will fall back
 ** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
 **
-** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
+** ^The xSetSystemCall(), xGetSystemCall(), and xNextSystemCall() interfaces
 ** are not used by the SQLite core.  These optional interfaces are provided
 ** by some VFSes to facilitate testing of the VFS code. By overriding
 ** system calls with functions under its control, a test program can
@@ -2033,7 +2039,8 @@ SQLITE_API int SQLITE_APICALL sqlite3_os_end(void);
 ** are called "anytime configuration options".
 ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
 ** [sqlite3_shutdown()] with a first argument that is not an anytime
-** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE.
+** configuration option, then the sqlite3_config() call will
+** return SQLITE_MISUSE.
 ** Note, however, that ^sqlite3_config() can be called as part of the
 ** implementation of an application-defined [sqlite3_os_init()].
 **
@@ -2599,9 +2606,10 @@ struct sqlite3_mem_methods {
 ** is less than 8.  The "sz" argument should be a multiple of 8 less than
 ** 65536.  If "sz" does not meet this constraint, it is reduced in size until
 ** it does.
-** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
-** if "cnt"is less than 1.  The "cnt" value will be reduced, if necessary, so
-** that the product of "sz" and "cnt" does not exceed 2,147,418,112.  The "cnt"
+** <li><p>The third argument ("cnt") is the number of slots.
+** Lookaside is disabled if "cnt"is less than 1.
+*  The "cnt" value will be reduced, if necessary, so
+** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
 ** parameter is usually chosen so that the product of "sz" and "cnt" is less
 ** than 1,000,000.
 ** </ol>
@@ -2889,12 +2897,15 @@ struct sqlite3_mem_methods {
 ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
 ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
 ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
-** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
-** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
-** statistics. For statistics to be collected, the flag must be set on
-** the database handle both when the SQL statement is prepared and when it
-** is stepped. The flag is set (collection of statistics is enabled)
-** by default. <p>This option takes two arguments: an integer and a pointer to
+** [SQLITE_ENABLE_STMT_SCANSTATUS] builds. In this case, it sets or clears
+** a flag that enables collection of run-time performance statistics
+** used by [sqlite3_stmt_scanstatus_v2()] and the [nexec and ncycle]
+** columns of the [bytecode virtual table].
+** For statistics to be collected, the flag must be set on
+** the database handle both when the SQL statement is
+** [sqlite3_prepare|prepared] and when it is [sqlite3_step|stepped].
+** The flag is set (collection of statistics is enabled) by default.
+** <p>This option takes two arguments: an integer and a pointer to
 ** an integer.  The first argument is 1, 0, or -1 to enable, disable, or
 ** leave unchanged the statement scanstatus option.  If the second argument
 ** is not NULL, then the value of the statement scanstatus setting after
@@ -2967,16 +2978,34 @@ struct sqlite3_mem_methods {
 ** comments are allowed in SQL text after processing the first argument.
 ** </dd>
 **
+** [[SQLITE_DBCONFIG_FP_DIGITS]]
+** <dt>SQLITE_DBCONFIG_FP_DIGITS</dt>
+** <dd>The SQLITE_DBCONFIG_FP_DIGITS setting is a small integer that determines
+** the number of significant digits that SQLite will attempt to preserve when
+** converting floating point numbers (IEEE 754 "doubles") into text.  The
+** default value 17, as of SQLite version 3.52.0.  The value was 15 in all
+** prior versions.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer.  The first argument is a small integer, between 3 and 23, or
+** zero.  The FP_DIGITS setting is changed to that small integer, or left
+** unaltered if the first argument is zero or out of range. The second argument
+** is a pointer to an integer.  If the pointer is not NULL, then the value of
+** the FP_DIGITS setting, after possibly being modified by the first
+** arguments, is written into the integer to which the second argument points.
+** </dd>
+**
 ** </dl>
 **
 ** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
 **
 ** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
 ** overall call to [sqlite3_db_config()] has a total of four parameters.
-** The first argument (the third parameter to sqlite3_db_config()) is an integer.
-** The second argument is a pointer to an integer.  If the first argument is 1,
-** then the option becomes enabled.  If the first integer argument is 0, then the
-** option is disabled.  If the first argument is -1, then the option setting
+** The first argument (the third parameter to sqlite3_db_config()) is
+** an integer.
+** The second argument is a pointer to an integer. If the first argument is 1,
+** then the option becomes enabled.  If the first integer argument is 0,
+** then the option is disabled.
+** If the first argument is -1, then the option setting
 ** is unchanged.  The second argument, the pointer to an integer, may be NULL.
 ** If the second argument is not NULL, then a value of 0 or 1 is written into
 ** the integer to which the second argument points, depending on whether the
@@ -2984,9 +3013,10 @@ struct sqlite3_mem_methods {
 ** the first argument.
 **
 ** <p>While most SQLITE_DBCONFIG options use the argument format
-** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
-** and [SQLITE_DBCONFIG_LOOKASIDE] options are different.  See the
-** documentation of those exceptional options for details.
+** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME],
+** [SQLITE_DBCONFIG_LOOKASIDE], and [SQLITE_DBCONFIG_FP_DIGITS] options
+** are different.  See the documentation of those exceptional options for
+** details.
 */
 #define SQLITE_DBCONFIG_MAINDBNAME            1000 /* const char* */
 #define SQLITE_DBCONFIG_LOOKASIDE             1001 /* void* int int */
@@ -3011,7 +3041,8 @@ struct sqlite3_mem_methods {
 #define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE  1020 /* int int* */
 #define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE   1021 /* int int* */
 #define SQLITE_DBCONFIG_ENABLE_COMMENTS       1022 /* int int* */
-#define SQLITE_DBCONFIG_MAX                   1022 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_FP_DIGITS             1023 /* int int* */
+#define SQLITE_DBCONFIG_MAX                   1023 /* Largest DBCONFIG */
 
 /*
 ** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -4493,6 +4524,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_free_filename(sqlite3_filename);
 ** <li> sqlite3_errmsg()
 ** <li> sqlite3_errmsg16()
 ** <li> sqlite3_error_offset()
+** <li> sqlite3_db_handle()
 ** </ul>
 **
 ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
@@ -4539,7 +4571,7 @@ SQLITE_API const char *SQLITE_APICALL sqlite3_errstr(int);
 SQLITE_API int SQLITE_APICALL sqlite3_error_offset(sqlite3 *db);
 
 /*
-** CAPI3REF: Set Error Codes And Message
+** CAPI3REF: Set Error Code And Message
 ** METHOD: sqlite3
 **
 ** Set the error code of the database handle passed as the first argument
@@ -4658,6 +4690,10 @@ SQLITE_API int SQLITE_APICALL sqlite3_limit(sqlite3*, int id, int newVal);
 ** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
 ** <dd>The maximum depth of the parse tree on any expression.</dd>)^
 **
+** [[SQLITE_LIMIT_PARSER_DEPTH]] ^(<dt>SQLITE_LIMIT_PARSER_DEPTH</dt>
+** <dd>The maximum depth of the LALR(1) parser stack used to analyze
+** input SQL statements.</dd>)^
+**
 ** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
 ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
 **
@@ -4702,6 +4738,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_limit(sqlite3*, int id, int newVal);
 #define SQLITE_LIMIT_VARIABLE_NUMBER           9
 #define SQLITE_LIMIT_TRIGGER_DEPTH            10
 #define SQLITE_LIMIT_WORKER_THREADS           11
+#define SQLITE_LIMIT_PARSER_DEPTH             12
 
 /*
 ** CAPI3REF: Prepare Flags
@@ -4746,12 +4783,29 @@ SQLITE_API int SQLITE_APICALL sqlite3_limit(sqlite3*, int id, int newVal);
 ** fails, the sqlite3_prepare_v3() call returns the same error indications
 ** with or without this flag; it just omits the call to [sqlite3_log()] that
 ** logs the error.
+**
+** [[SQLITE_PREPARE_FROM_DDL]] <dt>SQLITE_PREPARE_FROM_DDL</dt>
+** <dd>The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to enforce
+** security constraints that would otherwise only be enforced when parsing
+** the database schema.  In other words, the SQLITE_PREPARE_FROM_DDL flag
+** causes the SQL compiler to treat the SQL statement being prepared as if
+** it had come from an attacker.  When SQLITE_PREPARE_FROM_DDL is used and
+** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] is off, SQL functions may only be called
+** if they are tagged with [SQLITE_INNOCUOUS] and virtual tables may only
+** be used if they are tagged with [SQLITE_VTAB_INNOCUOUS].  Best practice
+** is to use the SQLITE_PREPARE_FROM_DDL option when preparing any SQL that
+** is derived from parts of the database schema. In particular, virtual
+** table implementations that run SQL statements that are derived from
+** arguments to their CREATE VIRTUAL TABLE statement should always use
+** [sqlite3_prepare_v3()] and set the SQLITE_PREPARE_FROM_DDL flag to
+** prevent bypass of the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks.
 ** </dl>
 */
 #define SQLITE_PREPARE_PERSISTENT              0x01
 #define SQLITE_PREPARE_NORMALIZE               0x02
 #define SQLITE_PREPARE_NO_VTAB                 0x04
 #define SQLITE_PREPARE_DONT_LOG                0x10
+#define SQLITE_PREPARE_FROM_DDL                0x20
 
 /*
 ** CAPI3REF: Compiling An SQL Statement
@@ -4765,8 +4819,9 @@ SQLITE_API int SQLITE_APICALL sqlite3_limit(sqlite3*, int id, int newVal);
 **
 ** The preferred routine to use is [sqlite3_prepare_v2()].  The
 ** [sqlite3_prepare()] interface is legacy and should be avoided.
-** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used
-** for special purposes.
+** [sqlite3_prepare_v3()] has an extra
+** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is sometimes
+** needed for special purpose or to pass along security restrictions.
 **
 ** The use of the UTF-8 interfaces is preferred, as SQLite currently
 ** does all parsing using UTF-8.  The UTF-16 interfaces are provided
@@ -5171,8 +5226,8 @@ typedef struct sqlite3_context sqlite3_context;
 ** it should be a pointer to well-formed UTF16 text.
 ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
 ** it should be a pointer to a well-formed unicode string that is
-** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
-** otherwise.
+** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT,
+** or UTF16 otherwise.
 **
 ** [[byte-order determination rules]] ^The byte-order of
 ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
@@ -5218,10 +5273,15 @@ typedef struct sqlite3_context sqlite3_context;
 ** object and pointer to it must remain valid until then. ^SQLite will then
 ** manage the lifetime of its private copy.
 **
-** ^The sixth argument to sqlite3_bind_text64() must be one of
-** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
-** to specify the encoding of the text in the third parameter.  If
-** the sixth argument to sqlite3_bind_text64() is not one of the
+** ^The sixth argument (the E argument)
+** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of
+** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE],
+** or [SQLITE_UTF16LE] to specify the encoding of the text in the
+** third parameter, Z.  The special value [SQLITE_UTF8_ZT] means that the
+** string argument is both UTF-8 encoded and is zero-terminated.  In other
+** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at
+** least N+1 bytes and that the Z&#91;N&#93; byte is zero.  If
+** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the
 ** allowed values shown above, or if the text encoding is different
 ** from the encoding specified by the sixth parameter, then the behavior
 ** is undefined.
@@ -6088,6 +6148,52 @@ SQLITE_API int SQLITE_APICALL sqlite3_create_window_function(
 **
 ** These constants define integer codes that represent the various
 ** text encodings supported by SQLite.
+**
+** <dl>
+** [[SQLITE_UTF8]] <dt>SQLITE_UTF8</dt><dd>Text is encoding as UTF-8</dd>
+**
+** [[SQLITE_UTF16LE]] <dt>SQLITE_UTF16LE</dt><dd>Text is encoding as UTF-16
+** with each code point being expressed "little endian" - the least significant
+** byte first.  This is the usual encoding, for example on Windows.</dd>
+**
+** [[SQLITE_UTF16BE]] <dt>SQLITE_UTF16BE</dt><dd>Text is encoding as UTF-16
+** with each code point being expressed "big endian" - the most significant
+** byte first.  This encoding is less common, but is still sometimes seen,
+** specially on older systems.
+**
+** [[SQLITE_UTF16]] <dt>SQLITE_UTF16</dt><dd>Text is encoding as UTF-16
+** with each code point being expressed either little endian or as big
+** endian, according to the native endianness of the host computer.
+**
+** [[SQLITE_ANY]] <dt>SQLITE_ANY</dt><dd>This encoding value may only be used
+** to declare the preferred text for [application-defined SQL functions]
+** created using [sqlite3_create_function()] and similar.  If the preferred
+** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep
+** parameter) is SQLITE_ANY, that indicates that the function does not have
+** a preference regarding the text encoding of its parameters and can take
+** any text encoding that the SQLite core find convenient to supply.  This
+** option is deprecated.  Please do not use it in new applications.
+**
+** [[SQLITE_UTF16_ALIGNED]] <dt>SQLITE_UTF16_ALIGNED</dt><dd>This encoding
+** value may be used as the 3rd parameter (the eTextRep parameter) to
+** [sqlite3_create_collation()] and similar.  This encoding value means
+** that the application-defined collating sequence created expects its
+** input strings to be in UTF16 in native byte order, and that the start
+** of the strings must be aligned to a 2-byte boundary.
+**
+** [[SQLITE_UTF8_ZT]] <dt>SQLITE_UTF8_ZT</dt><dd>This option can only be
+** used to specify the text encoding to strings input to
+** [sqlite3_result_text64()] and [sqlite3_bind_text64()].
+** The SQLITE_UTF8_ZT encoding means that the input string (call it "z")
+** is UTF-8 encoded and that it is zero-terminated.  If the length parameter
+** (call it "n") is non-negative, this encoding option means that the caller
+** guarantees that z array contains at least n+1 bytes and that the z&#91;n&#93;
+** byte has a value of zero.
+** This option gives the same output as SQLITE_UTF8, but can be more efficient
+** by avoiding the need to make a copy of the input string, in some cases.
+** However, if z is allocated to hold fewer than n+1 bytes or if the
+** z&#91;n&#93; byte is not zero, undefined behavior may result.
+** </dl>
 */
 #define SQLITE_UTF8           1    /* IMP: R-37514-35566 */
 #define SQLITE_UTF16LE        2    /* IMP: R-03371-37637 */
@@ -6095,6 +6201,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_create_window_function(
 #define SQLITE_UTF16          4    /* Use native byte order */
 #define SQLITE_ANY            5    /* Deprecated */
 #define SQLITE_UTF16_ALIGNED  8    /* sqlite3_create_collation only */
+#define SQLITE_UTF8_ZT       16    /* Zero-terminated UTF8 */
 
 /*
 ** CAPI3REF: Function Flags
@@ -6329,26 +6436,22 @@ SQLITE_API SQLITE_DEPRECATED int SQLITE_APICALL sqlite3_memory_alarm(void(SQLITE
 ** the SQL function that supplied the [sqlite3_value*] parameters.
 **
 ** As long as the input parameter is correct, these routines can only
-** fail if an out-of-memory error occurs during a format conversion.
-** Only the following subset of interfaces are subject to out-of-memory
-** errors:
-**
-** <ul>
-** <li> sqlite3_value_blob()
-** <li> sqlite3_value_text()
-** <li> sqlite3_value_text16()
-** <li> sqlite3_value_text16le()
-** <li> sqlite3_value_text16be()
-** <li> sqlite3_value_bytes()
-** <li> sqlite3_value_bytes16()
-** </ul>
-**
+** fail if an out-of-memory error occurs while trying to do a
+** UTF8&rarr;UTF16 or UTF16&rarr;UTF8 conversion.
 ** If an out-of-memory error occurs, then the return value from these
 ** routines is the same as if the column had contained an SQL NULL value.
-** Valid SQL NULL returns can be distinguished from out-of-memory errors
-** by invoking the [sqlite3_errcode()] immediately after the suspect
+** If the input sqlite3_value was not obtained from [sqlite3_value_dup()],
+** then valid SQL NULL returns can also be distinguished from
+** out-of-memory errors after extracting the value
+** by invoking the [sqlite3_errcode()] immediately after the suspicious
 ** return value is obtained and before any
 ** other SQLite interface is called on the same [database connection].
+** If the input sqlite3_value was obtained from sqlite3_value_dup() then
+** it is disconnected from the database connection and so sqlite3_errcode()
+** will not work.
+** In that case, the only way to distinguish an out-of-memory
+** condition from a true SQL NULL is to invoke sqlite3_value_type() on the
+** input to see if it is NULL prior to trying to extract the value.
 */
 SQLITE_API const void *SQLITE_APICALL sqlite3_value_blob(sqlite3_value*);
 SQLITE_API double SQLITE_APICALL sqlite3_value_double(sqlite3_value*);
@@ -6375,7 +6478,8 @@ SQLITE_API int SQLITE_APICALL sqlite3_value_frombind(sqlite3_value*);
 ** of the value X, assuming that X has type TEXT.)^  If sqlite3_value_type(X)
 ** returns something other than SQLITE_TEXT, then the return value from
 ** sqlite3_value_encoding(X) is meaningless.  ^Calls to
-** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)],
+** [sqlite3_value_text16be(X)],
 ** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
 ** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
 ** thus change the return from subsequent calls to sqlite3_value_encoding(X).
@@ -6506,17 +6610,17 @@ SQLITE_API sqlite3 *SQLITE_APICALL sqlite3_context_db_handle(sqlite3_context*);
 ** query execution, under some circumstances the associated auxiliary data
 ** might be preserved.  An example of where this might be useful is in a
 ** regular-expression matching function. The compiled version of the regular
-** expression can be stored as auxiliary data associated with the pattern string.
-** Then as long as the pattern string remains the same,
+** expression can be stored as auxiliary data associated with the pattern
+** string. Then as long as the pattern string remains the same,
 ** the compiled regular expression can be reused on multiple
 ** invocations of the same function.
 **
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
-** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
-** value to the application-defined function.  ^N is zero for the left-most
-** function argument.  ^If there is no auxiliary data
-** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
-** returns a NULL pointer.
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary
+** data associated by the sqlite3_set_auxdata(C,N,P,X) function with the
+** Nth argument value to the application-defined function.  ^N is zero
+** for the left-most function argument.  ^If there is no auxiliary data
+** associated with the function argument, the sqlite3_get_auxdata(C,N)
+** interface returns a NULL pointer.
 **
 ** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
 ** N-th argument of the application-defined function.  ^Subsequent
@@ -6600,10 +6704,14 @@ SQLITE_API void SQLITE_APICALL sqlite3_set_auxdata(sqlite3_context*, int N, void
 **
 ** There is no limit (other than available memory) on the number of different
 ** client data pointers (with different names) that can be attached to a
-** single database connection.  However, the implementation is optimized
-** for the case of having only one or two different client data names.
-** Applications and wrapper libraries are discouraged from using more than
-** one client data name each.
+** single database connection.  However, the current implementation stores
+** the content on a linked list.  Insert and retrieval performance will
+** be proportional to the number of entries.  The design use case, and
+** the use case for which the implementation is optimized, is
+** that an application will store only small number of client data names,
+** typically just one or two.  This interface is not intended to be a
+** generalized key/value store for thousands or millions of keys.  It
+** will work for that, but performance might be disappointing.
 **
 ** There is no way to enumerate the client data pointers
 ** associated with a database connection.  The N parameter can be thought
@@ -6711,10 +6819,14 @@ typedef void (SQLITE_APICALL *sqlite3_destructor_type)(void*);
 ** set the return value of the application-defined function to be
 ** a text string which is represented as UTF-8, UTF-16 native byte order,
 ** UTF-16 little endian, or UTF-16 big endian, respectively.
-** ^The sqlite3_result_text64() interface sets the return value of an
+** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an
 ** application-defined function to be a text string in an encoding
-** specified by the fifth (and last) parameter, which must be one
-** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
+** specified the E parameter, which must be one
+** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE],
+** or [SQLITE_UTF16LE].  ^The special value [SQLITE_UTF8_ZT] means that
+** the result text is both UTF-8 and zero-terminated.  In other words,
+** SQLITE_UTF8_ZT means that the Z array holds at least N+1 bytes and that
+** the Z&#91;N&#93; is zero.
 ** ^SQLite takes the text result from the application from
 ** the 2nd parameter of the sqlite3_result_text* interfaces.
 ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
@@ -6801,7 +6913,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_result_int(sqlite3_context*, int);
 SQLITE_API void SQLITE_APICALL sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
 SQLITE_API void SQLITE_APICALL sqlite3_result_null(sqlite3_context*);
 SQLITE_API void SQLITE_APICALL sqlite3_result_text(sqlite3_context*, const char*, int, void(SQLITE_APICALL *)(void*));
-SQLITE_API void SQLITE_APICALL sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,
+SQLITE_API void SQLITE_APICALL sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n,
                            void(SQLITE_APICALL *)(void*), unsigned char encoding);
 SQLITE_API void SQLITE_APICALL sqlite3_result_text16(sqlite3_context*, const void*, int, void(SQLITE_APICALL *)(void*));
 SQLITE_API void SQLITE_APICALL sqlite3_result_text16le(sqlite3_context*, const void*, int,void(SQLITE_APICALL *)(void*));
@@ -7740,7 +7852,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_table_column_metadata(
 ** ^The sqlite3_load_extension() interface attempts to load an
 ** [SQLite extension] library contained in the file zFile.  If
 ** the file cannot be loaded directly, attempts are made to load
-** with various operating-system specific extensions added.
+** with various operating-system specific filename extensions added.
 ** So for example, if "samplelib" cannot be loaded, then names like
 ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might
 ** be tried also.
@@ -7748,10 +7860,10 @@ SQLITE_API int SQLITE_APICALL sqlite3_table_column_metadata(
 ** ^The entry point is zProc.
 ** ^(zProc may be 0, in which case SQLite will try to come up with an
 ** entry point name on its own.  It first tries "sqlite3_extension_init".
-** If that does not work, it constructs a name "sqlite3_X_init" where
-** X consists of the lower-case equivalent of all ASCII alphabetic
-** characters in the filename from the last "/" to the first following
-** "." and omitting any initial "lib".)^
+** If that does not work, it tries names of the form "sqlite3_X_init"
+** where X consists of the lower-case equivalent of all ASCII alphabetic
+** characters or all ASCII alphanumeric characters in the filename from
+** the last "/" to the first following "." and omitting any initial "lib".)^
 ** ^The sqlite3_load_extension() interface returns
 ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
 ** ^If an error occurs and pzErrMsg is not 0, then the
@@ -7825,7 +7937,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_enable_load_extension(sqlite3 *db, int ono
 ** <blockquote><pre>
 ** &nbsp;  int xEntryPoint(
 ** &nbsp;    sqlite3 *db,
-** &nbsp;    const char **pzErrMsg,
+** &nbsp;    char **pzErrMsg,
 ** &nbsp;    const struct sqlite3_api_routines *pThunk
 ** &nbsp;  );
 ** </pre></blockquote>)^
@@ -8575,13 +8687,6 @@ SQLITE_API int SQLITE_APICALL sqlite3_vfs_unregister(sqlite3_vfs*);
 ** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
 ** and Windows.
 **
-** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
-** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
-** implementation is included with the library. In this case the
-** application must supply a custom mutex implementation using the
-** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function
-** before calling sqlite3_initialize() or any other public sqlite3_
-** function that calls sqlite3_initialize().
 **
 ** ^The sqlite3_mutex_alloc() routine allocates a new
 ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc()
@@ -8936,6 +9041,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
 #define SQLITE_TESTCTRL_TUNE                    32
 #define SQLITE_TESTCTRL_LOGEST                  33
 #define SQLITE_TESTCTRL_USELONGDOUBLE           34  /* NOT USED */
+#define SQLITE_TESTCTRL_ATOF                    34
 #define SQLITE_TESTCTRL_LAST                    34  /* Largest TESTCTRL */
 
 /*
@@ -9044,17 +9150,22 @@ SQLITE_API sqlite3_str *SQLITE_APICALL sqlite3_str_new(sqlite3*);
 ** pass the returned value to [sqlite3_free()] to avoid a memory leak.
 ** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
 ** errors were encountered during construction of the string.  ^The
-** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
+** [sqlite3_str_finish(X)] interface might also return a NULL pointer if the
 ** string in [sqlite3_str] object X is zero bytes long.
+**
+** ^The [sqlite3_str_free(X)] interface destroys both the sqlite3_str object
+** X and the string content it contains.  Calling sqlite3_str_free(X) is
+** the equivalent of calling [sqlite3_free](sqlite3_str_finish(X)).
 */
 SQLITE_API char *SQLITE_APICALL sqlite3_str_finish(sqlite3_str*);
+SQLITE_API void SQLITE_APICALL sqlite3_str_free(sqlite3_str*);
 
 /*
 ** CAPI3REF: Add Content To A Dynamic String
 ** METHOD: sqlite3_str
 **
-** These interfaces add content to an sqlite3_str object previously obtained
-** from [sqlite3_str_new()].
+** These interfaces add or remove content to an sqlite3_str object
+** previously obtained from [sqlite3_str_new()].
 **
 ** ^The [sqlite3_str_appendf(X,F,...)] and
 ** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
@@ -9077,6 +9188,10 @@ SQLITE_API char *SQLITE_APICALL sqlite3_str_finish(sqlite3_str*);
 ** ^The [sqlite3_str_reset(X)] method resets the string under construction
 ** inside [sqlite3_str] object X back to zero bytes in length.
 **
+** ^The [sqlite3_str_truncate(X,N)] method changes the length of the string
+** under construction to be N bytes or less.  This routine is a no-op if
+** N is negative or if the string is already N bytes or smaller in size.
+**
 ** These methods do not return a result code.  ^If an error occurs, that fact
 ** is recorded in the [sqlite3_str] object and can be recovered by a
 ** subsequent call to [sqlite3_str_errcode(X)].
@@ -9087,6 +9202,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_append(sqlite3_str*, const char *zIn,
 SQLITE_API void SQLITE_APICALL sqlite3_str_appendall(sqlite3_str*, const char *zIn);
 SQLITE_API void SQLITE_APICALL sqlite3_str_appendchar(sqlite3_str*, int N, char C);
 SQLITE_API void SQLITE_APICALL sqlite3_str_reset(sqlite3_str*);
+SQLITE_API void SQLITE_APICALL sqlite3_str_truncate(sqlite3_str*,int N);
 
 /*
 ** CAPI3REF: Status Of A Dynamic String
@@ -10617,7 +10733,8 @@ SQLITE_API const char *SQLITE_APICALL sqlite3_vtab_collation(sqlite3_index_info*
 ** <tr>
 ** <td valign="top">sqlite3_vtab_distinct() return value
 ** <td valign="top">Rows are returned in aOrderBy order
-** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent
+** <td valign="top">Rows with the same value in all aOrderBy columns are
+**                  adjacent
 ** <td valign="top">Duplicates over all colUsed columns may be omitted
 ** <tr><td>0<td>yes<td>yes<td>no
 ** <tr><td>1<td>no<td>yes<td>no
@@ -10626,8 +10743,8 @@ SQLITE_API const char *SQLITE_APICALL sqlite3_vtab_collation(sqlite3_index_info*
 ** </table>
 **
 ** ^For the purposes of comparing virtual table output values to see if the
-** values are the same value for sorting purposes, two NULL values are considered
-** to be the same.  In other words, the comparison operator is "IS"
+** values are the same value for sorting purposes, two NULL values are
+** considered to be the same.  In other words, the comparison operator is "IS"
 ** (or "IS NOT DISTINCT FROM") and not "==".
 **
 ** If a virtual table implementation is unable to meet the requirements
@@ -10920,9 +11037,9 @@ SQLITE_API int SQLITE_APICALL sqlite3_vtab_rhs_value(sqlite3_index_info*, int, s
 ** a variable pointed to by the "pOut" parameter.
 **
 ** The "flags" parameter must be passed a mask of flags. At present only
-** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
+** one flag is defined - [SQLITE_SCANSTAT_COMPLEX]. If SQLITE_SCANSTAT_COMPLEX
 ** is specified, then status information is available for all elements
-** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
+** of a query plan that are reported by "[EXPLAIN QUERY PLAN]" output. If
 ** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
 ** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
 ** the EXPLAIN QUERY PLAN output) are available. Invoking API
@@ -10936,7 +11053,8 @@ SQLITE_API int SQLITE_APICALL sqlite3_vtab_rhs_value(sqlite3_index_info*, int, s
 ** elements used to implement the statement - a non-zero value is returned and
 ** the variable that pOut points to is unchanged.
 **
-** See also: [sqlite3_stmt_scanstatus_reset()]
+** See also: [sqlite3_stmt_scanstatus_reset()] and the
+** [nexec and ncycle] columns of the [bytecode virtual table].
 */
 SQLITE_API int SQLITE_APICALL sqlite3_stmt_scanstatus(
   sqlite3_stmt *pStmt,      /* Prepared statement for which info desired */
@@ -11478,19 +11596,42 @@ SQLITE_API int SQLITE_APICALL sqlite3_deserialize(
 /*
 ** CAPI3REF: Bind array values to the CARRAY table-valued function
 **
-** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to
-** one of the first argument of the [carray() table-valued function].  The
-** S parameter is a pointer to the [prepared statement] that uses the carray()
-** functions.  I is the parameter index to be bound.  P is a pointer to the
-** array to be bound, and N is the number of eements in the array.  The
-** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
-** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to
-** indicate the datatype of the array being bound.  The X argument is not a
-** NULL pointer, then SQLite will invoke the function X on the P parameter
-** after it has finished using P, even if the call to
-** sqlite3_carray_bind() fails. The special-case finalizer
-** SQLITE_TRANSIENT has no effect here.
-*/
+** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to
+** parameter that is the first argument of the [carray() table-valued function].
+** The S parameter is a pointer to the [prepared statement] that uses the
+** carray() functions.  I is the parameter index to be bound.  I must be the
+** index of the parameter that is the first argument to the carray()
+** table-valued function. P is a pointer to the array to be bound, and N
+** is the number of elements in the array.  The F argument is one of
+** constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
+** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT],
+** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P.
+**
+** If the X argument is not a NULL pointer or one of the special
+** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke
+** the function X with argument D when it is finished using the data in P.
+** The call to X(D) is a destructor for the array P. The destructor X(D)
+** is invoked even if the call to sqlite3_carray_bind_v2() fails. If the X
+** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes
+** that the data static and the destructor is never invoked.  If the X
+** parameter is the special-case value [SQLITE_TRANSIENT], then
+** sqlite3_carray_bind_v2() makes its own private copy of the data prior
+** to returning and never invokes the destructor X.
+**
+** The sqlite3_carray_bind() function works the same as sqlite3_carray_bind_v2()
+** with a D parameter set to P.  In other words,
+** sqlite3_carray_bind(S,I,P,N,F,X) is same as
+** sqlite3_carray_bind_v2(S,I,P,N,F,X,P).
+*/
+SQLITE_API int SQLITE_APICALL sqlite3_carray_bind_v2(
+  sqlite3_stmt *pStmt,        /* Statement to be bound */
+  int i,                      /* Parameter index */
+  void *aData,                /* Pointer to array data */
+  int nData,                  /* Number of data elements */
+  int mFlags,                 /* CARRAY flags */
+  void (SQLITE_APICALL *xDel)(void*),        /* Destructor for aData */
+  void *pDel                  /* Optional argument to xDel() */
+);
 SQLITE_API int SQLITE_APICALL sqlite3_carray_bind(
   sqlite3_stmt *pStmt,        /* Statement to be bound */
   int i,                      /* Parameter index */
@@ -13034,11 +13175,23 @@ SQLITE_API int sqlite3changeset_apply_v3(
 **   database behave as if they were declared with "ON UPDATE NO ACTION ON
 **   DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
 **   or SET DEFAULT.
+**
+** <dt>SQLITE_CHANGESETAPPLY_NOUPDATELOOP <dd>
+**   Sometimes, a changeset contains two or more update statements such that
+**   although after applying all updates the database will contain no
+**   constraint violations, no single update can be applied before the others.
+**   The simplest example of this is a pair of UPDATEs that have "swapped"
+**   two column values with a UNIQUE constraint.
+**   <p>
+**   Usually, sqlite3changeset_apply() and similar functions work hard to try
+**   to find a way to apply such a changeset. However, if this flag is set,
+**   then all such updates are considered CONSTRAINT conflicts.
 */
 #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT   0x0001
 #define SQLITE_CHANGESETAPPLY_INVERT        0x0002
 #define SQLITE_CHANGESETAPPLY_IGNORENOOP    0x0004
 #define SQLITE_CHANGESETAPPLY_FKNOACTION    0x0008
+#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP  0x0010
 
 /*
 ** CAPI3REF: Constants Passed To The Conflict Handler
@@ -13521,6 +13674,232 @@ SQLITE_API int sqlite3session_config(int op, void *pArg);
 */
 #define SQLITE_SESSION_CONFIG_STRMSIZE 1
 
+/*
+** CAPI3REF: Configure a changegroup object
+**
+** Configure the changegroup object passed as the first argument.
+** At present the only valid value for the second parameter is
+** [SQLITE_CHANGEGROUP_CONFIG_PATCHSET].
+*/
+SQLITE_API int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg);
+
+/*
+** CAPI3REF: Options for sqlite3changegroup_config().
+**
+** The following values may be passed as the 2nd parameter to
+** sqlite3changegroup_config().
+**
+** <dt>SQLITE_CHANGEGROUP_CONFIG_PATCHSET <dd>
+**   A changegroup object generates either a changeset or patchset. Usually,
+**   this is determined by whether the first call to sqlite3changegroup_add()
+**   is passed a changeset or a patchset. Or, if the first changes are added
+**   to the changegroup object using the sqlite3changegroup_change_xxx()
+**   APIs, then this option may be used to configure whether the changegroup
+**   object generates a changeset or patchset.
+**
+**   When this option is invoked, parameter pArg must point to a value of
+**   type int. If the changegroup currently contains zero changes, and the
+**   value of the int variable is zero or greater than zero, then the
+**   changegroup is configured to generate a changeset or patchset,
+**   respectively. It is a no-op, not an error, if the changegroup is not
+**   configured because it has already started accumulating changes.
+**
+**   Before returning, the int variable is set to 0 if the changegroup is
+**   configured to generate a changeset, or 1 if it is configured to generate
+**   a patchset.
+*/
+#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1
+
+
+/*
+** CAPI3REF: Begin adding a change to a changegroup
+**
+** This API is used, in concert with other sqlite3changegroup_change_xxx()
+** APIs, to add changes to a changegroup object one at a time. To add a
+** single change, the caller must:
+**
+**   1. Invoke sqlite3changegroup_change_begin() to indicate the type of
+**      change (INSERT, UPDATE or DELETE), the affected table and whether
+**      or not the change should be marked as indirect.
+**
+**   2. Invoke sqlite3changegroup_change_int64() or one of the other four
+**      value functions - _null(), _double(), _text() or _blob() - one or
+**      more times to specify old.* and new.* values for the change being
+**      constructed.
+**
+**   3. Invoke sqlite3changegroup_change_finish() to either finish adding
+**      the change to the group, or to discard the change altogether.
+**
+** The first argument to this function must be a pointer to the existing
+** changegroup object that the change will be added to. The second argument
+** must be SQLITE_INSERT, SQLITE_UPDATE or SQLITE_DELETE. The third is the
+** name of the table that the change affects, and the fourth is a boolean
+** flag specifying whether the change should be marked as "indirect" (if
+** bIndirect is non-zero) or not indirect (if bIndirect is zero).
+**
+** Following a successful call to this function, this function may not be
+** called again on the same changegroup object until after
+** sqlite3changegroup_change_finish() has been called. Doing so is an
+** SQLITE_MISUSE error.
+**
+** The changegroup object passed as the first argument must be already
+** configured with schema data for the specified table. It may be configured
+** either by calling sqlite3changegroup_schema() with a database that contains
+** the table, or sqlite3changegroup_add() with a changeset that contains the
+** table. If the changegroup object has not been configured with a schema for
+** the specified table when this function is called, SQLITE_ERROR is returned.
+**
+** If successful, SQLITE_OK is returned. Otherwise, if an error occurs, an
+** SQLite error code is returned. In this case, if argument pzErr is non-NULL,
+** then (*pzErr) may be set to point to a buffer containing a utf-8 formated,
+** nul-terminated, English language error message. It is the responsibility
+** of the caller to eventually free this buffer using sqlite3_free().
+*/
+SQLITE_API int sqlite3changegroup_change_begin(
+  sqlite3_changegroup*,
+  int eOp,
+  const char *zTab,
+  int bIndirect,
+  char **pzErr
+);
+
+/*
+** CAPI3REF: Add a 64-bit integer to a changegroup
+**
+** This function may only be called between a successful call to
+** sqlite3changegroup_change_begin() and its matching
+** sqlite3changegroup_change_finish() call. If it is called at any
+** other time, it is an SQLITE_MISUSE error. Calling this function
+** specifies a 64-bit integer value to be used in the change currently being
+** added to the changegroup object passed as the first argument.
+**
+** The second parameter, bNew, specifies whether the value is to be part of
+** the new.* (if bNew is non-zero) or old.* (if bNew is zero) record of
+** the change under construction. If this does not match the type of change
+** specified by the preceding call to sqlite3changegroup_change_begin() (i.e.
+** an old.* value for an SQLITE_INSERT change, or a new.* value for an
+** SQLITE_DELETE), then SQLITE_ERROR is returned.
+**
+** The third parameter specifies the column of the old.* or new.* record that
+** the value will be a part of. If the specified table has an explicit primary
+** key, then this is the index of the table column, numbered from 0 in the order
+** specified within the CREATE TABLE statement. Or, if the table uses an
+** implicit rowid key, then the column 0 is the rowid and the explicit columns
+** are numbered starting from 1. If the iCol parameter is less than 0 or greater
+** than the index of the last column in the table, SQLITE_RANGE is returned.
+**
+** The fourth parameter is the integer value to use as part of the old.* or
+** new.* record.
+**
+** If this call is successful, SQLITE_OK is returned. Otherwise, if an
+** error occurs, an SQLite error code is returned.
+*/
+SQLITE_API int sqlite3changegroup_change_int64(
+  sqlite3_changegroup*,
+  int bNew,
+  int iCol,
+  sqlite3_int64 iVal
+);
+
+/*
+** CAPI3REF: Add a NULL to a changegroup
+**
+** This function is similar to sqlite3changegroup_change_int64(). Except that
+** it configures the change currently under construction with a NULL value
+** instead of a 64-bit integer.
+*/
+SQLITE_API int sqlite3changegroup_change_null(sqlite3_changegroup*, int, int);
+
+/*
+** CAPI3REF: Add an double to a changegroup
+**
+** This function is similar to sqlite3changegroup_change_int64(). Except that
+** it configures the change currently being constructed with a real value
+** instead of a 64-bit integer.
+*/
+SQLITE_API int sqlite3changegroup_change_double(sqlite3_changegroup*, int, int, double);
+
+/*
+** CAPI3REF: Add a text value to a changegroup
+**
+** This function is similar to sqlite3changegroup_change_int64(). It configures
+** the currently accumulated change with a text value instead of a 64-bit
+** integer. Parameter pVal points to a buffer containing the text encoded using
+** utf-8. Parameter nVal may either be the size of the text value in bytes, or
+** else a negative value, in which case the buffer pVal points to is assumed to
+** be nul-terminated.
+*/
+SQLITE_API int sqlite3changegroup_change_text(
+  sqlite3_changegroup*, int, int, const char *pVal, int nVal
+);
+
+/*
+** CAPI3REF: Add a blob to a changegroup
+**
+** This function is similar to sqlite3changegroup_change_int64(). It configures
+** the currently accumulated change with a blob value instead of a 64-bit
+** integer. Parameter pVal points to a buffer containing the blob. Parameter
+** nVal is the size of the blob in bytes.
+*/
+SQLITE_API int sqlite3changegroup_change_blob(
+    sqlite3_changegroup*, int, int, const void *pVal, int nVal
+);
+
+/*
+** CAPI3REF: Finish adding one-at-at-time changes to a changegroup
+**
+** This function may only be called following a successful call to
+** sqlite3changegroup_change_begin(). Otherwise, it is an SQLITE_MISUSE error.
+**
+** If parameter bDiscard is non-zero, then the current change is simply
+** discarded. In this case this function is always successful and SQLITE_OK
+** returned.
+**
+** If parameter bDiscard is zero, then an attempt is made to add the current
+** change to the changegroup. Assuming the changegroup is configured to
+** produce a changeset (not a patchset), this requires that:
+**
+**   *  If the change is an INSERT or DELETE, then a value must be specified
+**      for all columns of the new.* or old.* record, respectively.
+**
+**   *  If the change is an UPDATE record, then values must be provided for
+**      the PRIMARY KEY columns of the old.* record, but must not be provided
+**      for PRIMARY KEY columns of the new.* record.
+**
+**   *  If the change is an UPDATE record, then for each non-PRIMARY KEY
+**      column in the old.* record for which a value has been provided, a
+**      value must also be provided for the same column in the new.* record.
+**      Similarly, for each non-PK column in the old.* record for which
+**      a value is not provided, a value must not be provided for the same
+**      column in the new.* record.
+**
+**   *  All values specified for PRIMARY KEY columns must be non-NULL.
+**
+** Otherwise, it is an error.
+**
+** If the changegroup already contains a change for the same row (identified
+** by PRIMARY KEY columns), then the current change is combined with the
+** existing change in the same way as for sqlite3changegroup_add().
+**
+** For a patchset, all of the above rules apply except that it doesn't matter
+** whether or not values are provided for the non-PK old.* record columns
+** for an UPDATE or DELETE change. This means that code used to produce
+** a changeset using the sqlite3changegroup_change_xxx() APIs may also
+** be used to produce patchsets.
+**
+** If the call is successful, SQLITE_OK is returned. Otherwise, if an error
+** occurs, an SQLite error code is returned. If an error is returned and
+** parameter pzErr is not NULL, then (*pzErr) may be set to point to a buffer
+** containing a nul-terminated, utf-8 encoded, English language error message.
+** It is the responsibility of the caller to eventually free any such error
+** message buffer using sqlite3_free().
+*/
+SQLITE_API int sqlite3changegroup_change_finish(
+  sqlite3_changegroup*,
+  int bDiscard,
+  char **pzErr
+);
+
 /*
 ** Make sure we can call this stuff from C++.
 */
@@ -14335,6 +14714,27 @@ struct fts5_api {
 #endif
 #define SQLITE_MIN_LENGTH 30   /* Minimum value for the length limit */
 
+/*
+** Maximum size of any single memory allocation.
+**
+** This is not a limit on the total amount of memory used.  This is
+** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc().
+**
+** The upper bound is slightly less than 2GiB:  0x7ffffeff == 2,147,483,391
+** This provides a 256-byte safety margin for defense against 32-bit
+** signed integer overflow bugs when computing memory allocation sizes.
+** Paranoid applications might want to reduce the maximum allocation size
+** further for an even larger safety margin.  0x3fffffff or 0x0fffffff
+** or even smaller would be reasonable upper bounds on the size of a memory
+** allocations for most applications.
+*/
+#ifndef SQLITE_MAX_ALLOCATION_SIZE
+# define SQLITE_MAX_ALLOCATION_SIZE  2147483391
+#endif
+#if SQLITE_MAX_ALLOCATION_SIZE>2147483391
+# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391
+#endif
+
 /*
 ** This is the maximum number of
 **
@@ -14370,21 +14770,42 @@ struct fts5_api {
 ** It used to be the case that setting this value to zero would
 ** turn the limit off.  That is no longer true.  It is not possible
 ** to turn this limit off.
+**
+** The hard limit is the largest possible 32-bit signed integer less
+** 1024, or 2147482624.
 */
 #ifndef SQLITE_MAX_SQL_LENGTH
 # define SQLITE_MAX_SQL_LENGTH 1000000000
 #endif
 
 /*
-** The maximum depth of an expression tree. This is limited to
-** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
-** want to place more severe limits on the complexity of an
-** expression. A value of 0 means that there is no limit.
+** The maximum depth of an expression tree. The expression tree depth
+** is also limited indirectly by SQLITE_MAX_SQL_LENGTH and by
+** SQLITE_MAX_PARSER_DEPTH.  Reducing the maximum complexity of
+** expressions can help prevent excess memory usage by hostile SQL.
+**
+** A value of 0 for this compile-time option causes all expression
+** depth limiting code to be omitted.
 */
 #ifndef SQLITE_MAX_EXPR_DEPTH
 # define SQLITE_MAX_EXPR_DEPTH 1000
 #endif
 
+/*
+** The maximum depth of the LALR(1) stack used in the parser that
+** interprets SQL inputs. The parser stack depth can also be limited
+** indirectly by SQLITE_MAX_SQL_LENGTH.  Limiting the parser stack
+** depth can help prevent excess memory usage and excess CPU stack
+** usage when processing hostile SQL.
+**
+** Prior to version 3.45.0 (2024-01-15), the parser stack was
+** hard-coded to 100 entries, and that worked fine for almost all
+** applications.  So the upper bound on this limit need not be large.
+*/
+#ifndef SQLITE_MAX_PARSER_DEPTH
+# define SQLITE_MAX_PARSER_DEPTH 2500
+#endif
+
 /*
 ** The maximum number of terms in a compound SELECT statement.
 ** The code generator for compound SELECT statements does one
@@ -14500,6 +14921,10 @@ struct fts5_api {
 # undef SQLITE_MAX_DEFAULT_PAGE_SIZE
 # define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
 #endif
+#if SQLITE_MAX_DEFAULT_PAGE_SIZE<SQLITE_DEFAULT_PAGE_SIZE
+# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_DEFAULT_PAGE_SIZE
+#endif
 
 
 /*
@@ -14564,9 +14989,7 @@ struct fts5_api {
 /*
 ** Include standard header files as necessary
 */
-#ifdef HAVE_STDINT_H
 #include <stdint.h>
-#endif
 #ifdef HAVE_INTTYPES_H
 #include <inttypes.h>
 #endif
@@ -15271,6 +15694,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3HashClear(Hash*);
 # define float sqlite_int64
 # define fabs(X) ((X)<0?-(X):(X))
 # define sqlite3IsOverflow(X) 0
+# define INFINITY (9223372036854775807LL)
 # ifndef SQLITE_BIG_DBL
 #   define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50)
 # endif
@@ -15680,6 +16104,7 @@ typedef INT16_TYPE LogEst;
 #else
 # define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&7)==0)
 #endif
+#define TWO_BYTE_ALIGNMENT(X)      ((((uptr)(X) - (uptr)0)&1)==0)
 
 /*
 ** Disable MMAP on platforms where it is known to not work
@@ -16587,7 +17012,7 @@ SQLITE_PRIVATE   int sqlite3PagerWalFramesize(Pager *pPager);
 SQLITE_PRIVATE u8 SQLITE_APICALL sqlite3PagerIsreadonly(Pager*);
 SQLITE_PRIVATE u32 SQLITE_APICALL sqlite3PagerDataVersion(Pager*);
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3PagerRefcount(Pager*);
+SQLITE_PRIVATE   int sqlite3PagerRefcount(Pager*);
 #endif
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerMemUsed(Pager*);
 SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3PagerFilename(const Pager*, int);
@@ -16608,8 +17033,8 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3PagerRekey(DbPage*, Pgno, u16);
 
 /* Functions to support testing and debugging. */
 #if !defined(NDEBUG) || defined(SQLITE_TEST)
-SQLITE_PRIVATE   Pgno SQLITE_APICALL sqlite3PagerPagenumber(DbPage*);
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3PagerIswriteable(DbPage*);
+SQLITE_PRIVATE   Pgno sqlite3PagerPagenumber(DbPage*);
+SQLITE_PRIVATE   int sqlite3PagerIswriteable(DbPage*);
 #endif
 #ifdef SQLITE_TEST
 SQLITE_PRIVATE   int *sqlite3PagerStats(Pager*);
@@ -16874,7 +17299,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCursor(
 SQLITE_PRIVATE BtCursor *SQLITE_APICALL sqlite3BtreeFakeValidCursor(void);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCursorSize(void);
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeClosesWithCursor(Btree*,BtCursor*);
+SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*);
 #endif
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3BtreeCursorZero(BtCursor*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3BtreeCursorHintFlags(BtCursor*, unsigned);
@@ -16989,13 +17414,13 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeIsReadonly(Btree *pBt);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3HeaderSizeBtree(void);
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE sqlite3_uint64 SQLITE_APICALL sqlite3BtreeSeekCount(Btree*);
+SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree*);
 #else
 # define sqlite3BtreeSeekCount(X) 0
 #endif
 
 #ifndef NDEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCursorIsValid(BtCursor*);
+SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
 #endif
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCursorIsValidNN(BtCursor*);
 
@@ -17039,9 +17464,9 @@ SQLITE_PRIVATE   void SQLITE_APICALL sqlite3BtreeLeaveCursor(BtCursor*);
 SQLITE_PRIVATE   void SQLITE_APICALL sqlite3BtreeLeaveAll(sqlite3*);
 #ifndef NDEBUG
   /* These routines are used inside assert() statements only. */
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3BtreeHoldsMutex(Btree*);
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3BtreeHoldsAllMutexes(sqlite3*);
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
+SQLITE_PRIVATE   int sqlite3BtreeHoldsMutex(Btree*);
+SQLITE_PRIVATE   int sqlite3BtreeHoldsAllMutexes(sqlite3*);
+SQLITE_PRIVATE   int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
 #endif
 #else
 
@@ -17138,6 +17563,7 @@ struct VdbeOp {
     SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */
     Table *pTab;           /* Used when p4type is P4_TABLE */
     SubrtnSig *pSubrtnSig; /* Used when p4type is P4_SUBRTNSIG */
+    Index *pIdx;           /* Used when p4type is P4_INDEX */
 #ifdef SQLITE_ENABLE_CURSOR_HINTS
     Expr *pExpr;           /* Used when p4type is P4_EXPR */
 #endif
@@ -17192,20 +17618,21 @@ typedef struct VdbeOpList VdbeOpList;
 #define P4_INT32      (-3)  /* P4 is a 32-bit signed integer */
 #define P4_SUBPROGRAM (-4)  /* P4 is a pointer to a SubProgram structure */
 #define P4_TABLE      (-5)  /* P4 is a pointer to a Table structure */
+#define P4_INDEX      (-6)  /* P4 is a pointer to an Index structure */
 /* Above do not own any resources.  Must free those below */
-#define P4_FREE_IF_LE (-6)
-#define P4_DYNAMIC    (-6)  /* Pointer to memory from sqliteMalloc() */
-#define P4_FUNCDEF    (-7)  /* P4 is a pointer to a FuncDef structure */
-#define P4_KEYINFO    (-8)  /* P4 is a pointer to a KeyInfo structure */
-#define P4_EXPR       (-9) /* P4 is a pointer to an Expr tree */
-#define P4_MEM        (-10) /* P4 is a pointer to a Mem*    structure */
-#define P4_VTAB       (-11) /* P4 is a pointer to an sqlite3_vtab structure */
-#define P4_REAL       (-12) /* P4 is a 64-bit floating point value */
-#define P4_INT64      (-13) /* P4 is a 64-bit signed integer */
-#define P4_INTARRAY   (-14) /* P4 is a vector of 32-bit integers */
-#define P4_FUNCCTX    (-15) /* P4 is a pointer to an sqlite3_context object */
-#define P4_TABLEREF   (-16) /* Like P4_TABLE, but reference counted */
-#define P4_SUBRTNSIG  (-17) /* P4 is a SubrtnSig pointer */
+#define P4_FREE_IF_LE (-7)
+#define P4_DYNAMIC    (-7)  /* Pointer to memory from sqliteMalloc() */
+#define P4_FUNCDEF    (-8)  /* P4 is a pointer to a FuncDef structure */
+#define P4_KEYINFO    (-9)  /* P4 is a pointer to a KeyInfo structure */
+#define P4_EXPR       (-10) /* P4 is a pointer to an Expr tree */
+#define P4_MEM        (-11) /* P4 is a pointer to a Mem*    structure */
+#define P4_VTAB       (-12) /* P4 is a pointer to an sqlite3_vtab structure */
+#define P4_REAL       (-13) /* P4 is a 64-bit floating point value */
+#define P4_INT64      (-14) /* P4 is a 64-bit signed integer */
+#define P4_INTARRAY   (-15) /* P4 is a vector of 32-bit integers */
+#define P4_FUNCCTX    (-16) /* P4 is a pointer to an sqlite3_context object */
+#define P4_TABLEREF   (-17) /* Like P4_TABLE, but reference counted */
+#define P4_SUBRTNSIG  (-18) /* P4 is a SubrtnSig pointer */
 
 /* Error message codes for OP_Halt */
 #define P5_ConstraintNotNull 1
@@ -17294,10 +17721,10 @@ typedef struct VdbeOpList VdbeOpList;
 #define OP_And            44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
 #define OP_IdxLT          45 /* jump, synopsis: key=r[P3@P4]               */
 #define OP_IdxGE          46 /* jump, synopsis: key=r[P3@P4]               */
-#define OP_RowSetRead     47 /* jump, synopsis: r[P3]=rowset(P1)           */
-#define OP_RowSetTest     48 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
-#define OP_Program        49 /* jump0                                      */
-#define OP_FkIfZero       50 /* jump, synopsis: if fkctr[P1]==0 goto P2    */
+#define OP_IFindKey       47 /* jump                                       */
+#define OP_RowSetRead     48 /* jump, synopsis: r[P3]=rowset(P1)           */
+#define OP_RowSetTest     49 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
+#define OP_Program        50 /* jump0                                      */
 #define OP_IsNull         51 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
 #define OP_NotNull        52 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
 #define OP_Ne             53 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */
@@ -17307,49 +17734,49 @@ typedef struct VdbeOpList VdbeOpList;
 #define OP_Lt             57 /* jump, same as TK_LT, synopsis: IF r[P3]<r[P1] */
 #define OP_Ge             58 /* jump, same as TK_GE, synopsis: IF r[P3]>=r[P1] */
 #define OP_ElseEq         59 /* jump, same as TK_ESCAPE                    */
-#define OP_IfPos          60 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
-#define OP_IfNotZero      61 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
-#define OP_DecrJumpZero   62 /* jump, synopsis: if (--r[P1])==0 goto P2    */
-#define OP_IncrVacuum     63 /* jump                                       */
-#define OP_VNext          64 /* jump                                       */
-#define OP_Filter         65 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */
-#define OP_PureFunc       66 /* synopsis: r[P3]=func(r[P2@NP])             */
-#define OP_Function       67 /* synopsis: r[P3]=func(r[P2@NP])             */
-#define OP_Return         68
-#define OP_EndCoroutine   69
-#define OP_HaltIfNull     70 /* synopsis: if r[P3]=null halt               */
-#define OP_Halt           71
-#define OP_Integer        72 /* synopsis: r[P2]=P1                         */
-#define OP_Int64          73 /* synopsis: r[P2]=P4                         */
-#define OP_String         74 /* synopsis: r[P2]='P4' (len=P1)              */
-#define OP_BeginSubrtn    75 /* synopsis: r[P2]=NULL                       */
-#define OP_Null           76 /* synopsis: r[P2..P3]=NULL                   */
-#define OP_SoftNull       77 /* synopsis: r[P1]=NULL                       */
-#define OP_Blob           78 /* synopsis: r[P2]=P4 (len=P1)                */
-#define OP_Variable       79 /* synopsis: r[P2]=parameter(P1)              */
-#define OP_Move           80 /* synopsis: r[P2@P3]=r[P1@P3]                */
-#define OP_Copy           81 /* synopsis: r[P2@P3+1]=r[P1@P3+1]            */
-#define OP_SCopy          82 /* synopsis: r[P2]=r[P1]                      */
-#define OP_IntCopy        83 /* synopsis: r[P2]=r[P1]                      */
-#define OP_FkCheck        84
-#define OP_ResultRow      85 /* synopsis: output=r[P1@P2]                  */
-#define OP_CollSeq        86
-#define OP_AddImm         87 /* synopsis: r[P1]=r[P1]+P2                   */
-#define OP_RealAffinity   88
-#define OP_Cast           89 /* synopsis: affinity(r[P1])                  */
-#define OP_Permutation    90
-#define OP_Compare        91 /* synopsis: r[P1@P3] <-> r[P2@P3]            */
-#define OP_IsTrue         92 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */
-#define OP_ZeroOrNull     93 /* synopsis: r[P2] = 0 OR NULL                */
-#define OP_Offset         94 /* synopsis: r[P3] = sqlite_offset(P1)        */
-#define OP_Column         95 /* synopsis: r[P3]=PX cursor P1 column P2     */
-#define OP_TypeCheck      96 /* synopsis: typecheck(r[P1@P2])              */
-#define OP_Affinity       97 /* synopsis: affinity(r[P1@P2])               */
-#define OP_MakeRecord     98 /* synopsis: r[P3]=mkrec(r[P1@P2])            */
-#define OP_Count          99 /* synopsis: r[P2]=count()                    */
-#define OP_ReadCookie    100
-#define OP_SetCookie     101
-#define OP_ReopenIdx     102 /* synopsis: root=P2 iDb=P3                   */
+#define OP_FkIfZero       60 /* jump, synopsis: if fkctr[P1]==0 goto P2    */
+#define OP_IfPos          61 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#define OP_IfNotZero      62 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
+#define OP_DecrJumpZero   63 /* jump, synopsis: if (--r[P1])==0 goto P2    */
+#define OP_IncrVacuum     64 /* jump                                       */
+#define OP_VNext          65 /* jump                                       */
+#define OP_Filter         66 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */
+#define OP_PureFunc       67 /* synopsis: r[P3]=func(r[P2@NP])             */
+#define OP_Function       68 /* synopsis: r[P3]=func(r[P2@NP])             */
+#define OP_Return         69
+#define OP_EndCoroutine   70
+#define OP_HaltIfNull     71 /* synopsis: if r[P3]=null halt               */
+#define OP_Halt           72
+#define OP_Integer        73 /* synopsis: r[P2]=P1                         */
+#define OP_Int64          74 /* synopsis: r[P2]=P4                         */
+#define OP_String         75 /* synopsis: r[P2]='P4' (len=P1)              */
+#define OP_BeginSubrtn    76 /* synopsis: r[P2]=NULL                       */
+#define OP_Null           77 /* synopsis: r[P2..P3]=NULL                   */
+#define OP_SoftNull       78 /* synopsis: r[P1]=NULL                       */
+#define OP_Blob           79 /* synopsis: r[P2]=P4 (len=P1)                */
+#define OP_Variable       80 /* synopsis: r[P2]=parameter(P1)              */
+#define OP_Move           81 /* synopsis: r[P2@P3]=r[P1@P3]                */
+#define OP_Copy           82 /* synopsis: r[P2@P3+1]=r[P1@P3+1]            */
+#define OP_SCopy          83 /* synopsis: r[P2]=r[P1]                      */
+#define OP_IntCopy        84 /* synopsis: r[P2]=r[P1]                      */
+#define OP_FkCheck        85
+#define OP_ResultRow      86 /* synopsis: output=r[P1@P2]                  */
+#define OP_CollSeq        87
+#define OP_AddImm         88 /* synopsis: r[P1]=r[P1]+P2                   */
+#define OP_RealAffinity   89
+#define OP_Cast           90 /* synopsis: affinity(r[P1])                  */
+#define OP_Permutation    91
+#define OP_Compare        92 /* synopsis: r[P1@P3] <-> r[P2@P3]            */
+#define OP_IsTrue         93 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */
+#define OP_ZeroOrNull     94 /* synopsis: r[P2] = 0 OR NULL                */
+#define OP_Offset         95 /* synopsis: r[P3] = sqlite_offset(P1)        */
+#define OP_Column         96 /* synopsis: r[P3]=PX cursor P1 column P2     */
+#define OP_TypeCheck      97 /* synopsis: typecheck(r[P1@P2])              */
+#define OP_Affinity       98 /* synopsis: affinity(r[P1@P2])               */
+#define OP_MakeRecord     99 /* synopsis: r[P3]=mkrec(r[P1@P2])            */
+#define OP_Count         100 /* synopsis: r[P2]=count()                    */
+#define OP_ReadCookie    101
+#define OP_SetCookie     102
 #define OP_BitAnd        103 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
 #define OP_BitOr         104 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
 #define OP_ShiftLeft     105 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
@@ -17360,84 +17787,85 @@ typedef struct VdbeOpList VdbeOpList;
 #define OP_Divide        110 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
 #define OP_Remainder     111 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
 #define OP_Concat        112 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_OpenRead      113 /* synopsis: root=P2 iDb=P3                   */
-#define OP_OpenWrite     114 /* synopsis: root=P2 iDb=P3                   */
+#define OP_ReopenIdx     113 /* synopsis: root=P2 iDb=P3                   */
+#define OP_OpenRead      114 /* synopsis: root=P2 iDb=P3                   */
 #define OP_BitNot        115 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
-#define OP_OpenDup       116
-#define OP_OpenAutoindex 117 /* synopsis: nColumn=P2                       */
+#define OP_OpenWrite     116 /* synopsis: root=P2 iDb=P3                   */
+#define OP_OpenDup       117
 #define OP_String8       118 /* same as TK_STRING, synopsis: r[P2]='P4'    */
-#define OP_OpenEphemeral 119 /* synopsis: nColumn=P2                       */
-#define OP_SorterOpen    120
-#define OP_SequenceTest  121 /* synopsis: if( cursor[P1].ctr++ ) pc = P2   */
-#define OP_OpenPseudo    122 /* synopsis: P3 columns in r[P2]              */
-#define OP_Close         123
-#define OP_ColumnsUsed   124
-#define OP_SeekScan      125 /* synopsis: Scan-ahead up to P1 rows         */
-#define OP_SeekHit       126 /* synopsis: set P2<=seekHit<=P3              */
-#define OP_Sequence      127 /* synopsis: r[P2]=cursor[P1].ctr++           */
-#define OP_NewRowid      128 /* synopsis: r[P2]=rowid                      */
-#define OP_Insert        129 /* synopsis: intkey=r[P3] data=r[P2]          */
-#define OP_RowCell       130
-#define OP_Delete        131
-#define OP_ResetCount    132
-#define OP_SorterCompare 133 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData    134 /* synopsis: r[P2]=data                       */
-#define OP_RowData       135 /* synopsis: r[P2]=data                       */
-#define OP_Rowid         136 /* synopsis: r[P2]=PX rowid of P1             */
-#define OP_NullRow       137
-#define OP_SeekEnd       138
-#define OP_IdxInsert     139 /* synopsis: key=r[P2]                        */
-#define OP_SorterInsert  140 /* synopsis: key=r[P2]                        */
-#define OP_IdxDelete     141 /* synopsis: key=r[P2@P3]                     */
-#define OP_DeferredSeek  142 /* synopsis: Move P3 to P1.rowid if needed    */
-#define OP_IdxRowid      143 /* synopsis: r[P2]=rowid                      */
-#define OP_FinishSeek    144
-#define OP_Destroy       145
-#define OP_Clear         146
-#define OP_ResetSorter   147
-#define OP_CreateBtree   148 /* synopsis: r[P2]=root iDb=P1 flags=P3       */
-#define OP_SqlExec       149
-#define OP_ParseSchema   150
-#define OP_LoadAnalysis  151
-#define OP_DropTable     152
-#define OP_DropIndex     153
+#define OP_OpenAutoindex 119 /* synopsis: nColumn=P2                       */
+#define OP_OpenEphemeral 120 /* synopsis: nColumn=P2                       */
+#define OP_SorterOpen    121
+#define OP_SequenceTest  122 /* synopsis: if( cursor[P1].ctr++ ) pc = P2   */
+#define OP_OpenPseudo    123 /* synopsis: P3 columns in r[P2]              */
+#define OP_Close         124
+#define OP_ColumnsUsed   125
+#define OP_SeekScan      126 /* synopsis: Scan-ahead up to P1 rows         */
+#define OP_SeekHit       127 /* synopsis: set P2<=seekHit<=P3              */
+#define OP_Sequence      128 /* synopsis: r[P2]=cursor[P1].ctr++           */
+#define OP_NewRowid      129 /* synopsis: r[P2]=rowid                      */
+#define OP_Insert        130 /* synopsis: intkey=r[P3] data=r[P2]          */
+#define OP_RowCell       131
+#define OP_Delete        132
+#define OP_ResetCount    133
+#define OP_SorterCompare 134 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData    135 /* synopsis: r[P2]=data                       */
+#define OP_RowData       136 /* synopsis: r[P2]=data                       */
+#define OP_Rowid         137 /* synopsis: r[P2]=PX rowid of P1             */
+#define OP_NullRow       138
+#define OP_SeekEnd       139
+#define OP_IdxInsert     140 /* synopsis: key=r[P2]                        */
+#define OP_SorterInsert  141 /* synopsis: key=r[P2]                        */
+#define OP_IdxDelete     142 /* synopsis: key=r[P2@P3]                     */
+#define OP_DeferredSeek  143 /* synopsis: Move P3 to P1.rowid if needed    */
+#define OP_IdxRowid      144 /* synopsis: r[P2]=rowid                      */
+#define OP_FinishSeek    145
+#define OP_Destroy       146
+#define OP_Clear         147
+#define OP_ResetSorter   148
+#define OP_CreateBtree   149 /* synopsis: r[P2]=root iDb=P1 flags=P3       */
+#define OP_SqlExec       150
+#define OP_ParseSchema   151
+#define OP_LoadAnalysis  152
+#define OP_DropTable     153
 #define OP_Real          154 /* same as TK_FLOAT, synopsis: r[P2]=P4       */
-#define OP_DropTrigger   155
-#define OP_IntegrityCk   156
-#define OP_RowSetAdd     157 /* synopsis: rowset(P1)=r[P2]                 */
-#define OP_Param         158
-#define OP_FkCounter     159 /* synopsis: fkctr[P1]+=P2                    */
-#define OP_MemMax        160 /* synopsis: r[P1]=max(r[P1],r[P2])           */
-#define OP_OffsetLimit   161 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
-#define OP_AggInverse    162 /* synopsis: accum=r[P3] inverse(r[P2@P5])    */
-#define OP_AggStep       163 /* synopsis: accum=r[P3] step(r[P2@P5])       */
-#define OP_AggStep1      164 /* synopsis: accum=r[P3] step(r[P2@P5])       */
-#define OP_AggValue      165 /* synopsis: r[P3]=value N=P2                 */
-#define OP_AggFinal      166 /* synopsis: accum=r[P1] N=P2                 */
-#define OP_Expire        167
-#define OP_CursorLock    168
-#define OP_CursorUnlock  169
-#define OP_TableLock     170 /* synopsis: iDb=P1 root=P2 write=P3          */
-#define OP_VBegin        171
-#define OP_VCreate       172
-#define OP_VDestroy      173
-#define OP_VOpen         174
-#define OP_VCheck        175
-#define OP_VInitIn       176 /* synopsis: r[P2]=ValueList(P1,P3)           */
-#define OP_VColumn       177 /* synopsis: r[P3]=vcolumn(P2)                */
-#define OP_VRename       178
-#define OP_Pagecount     179
-#define OP_MaxPgcnt      180
-#define OP_ClrSubtype    181 /* synopsis: r[P1].subtype = 0                */
-#define OP_GetSubtype    182 /* synopsis: r[P2] = r[P1].subtype            */
-#define OP_SetSubtype    183 /* synopsis: r[P2].subtype = r[P1]            */
-#define OP_FilterAdd     184 /* synopsis: filter(P1) += key(P3@P4)         */
-#define OP_Trace         185
-#define OP_CursorHint    186
-#define OP_ReleaseReg    187 /* synopsis: release r[P1@P2] mask P3         */
-#define OP_Noop          188
-#define OP_Explain       189
-#define OP_Abortable     190
+#define OP_DropIndex     155
+#define OP_DropTrigger   156
+#define OP_IntegrityCk   157
+#define OP_RowSetAdd     158 /* synopsis: rowset(P1)=r[P2]                 */
+#define OP_Param         159
+#define OP_FkCounter     160 /* synopsis: fkctr[P1]+=P2                    */
+#define OP_MemMax        161 /* synopsis: r[P1]=max(r[P1],r[P2])           */
+#define OP_OffsetLimit   162 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggInverse    163 /* synopsis: accum=r[P3] inverse(r[P2@P5])    */
+#define OP_AggStep       164 /* synopsis: accum=r[P3] step(r[P2@P5])       */
+#define OP_AggStep1      165 /* synopsis: accum=r[P3] step(r[P2@P5])       */
+#define OP_AggValue      166 /* synopsis: r[P3]=value N=P2                 */
+#define OP_AggFinal      167 /* synopsis: accum=r[P1] N=P2                 */
+#define OP_Expire        168
+#define OP_CursorLock    169
+#define OP_CursorUnlock  170
+#define OP_TableLock     171 /* synopsis: iDb=P1 root=P2 write=P3          */
+#define OP_VBegin        172
+#define OP_VCreate       173
+#define OP_VDestroy      174
+#define OP_VOpen         175
+#define OP_VCheck        176
+#define OP_VInitIn       177 /* synopsis: r[P2]=ValueList(P1,P3)           */
+#define OP_VColumn       178 /* synopsis: r[P3]=vcolumn(P2)                */
+#define OP_VRename       179
+#define OP_Pagecount     180
+#define OP_MaxPgcnt      181
+#define OP_ClrSubtype    182 /* synopsis: r[P1].subtype = 0                */
+#define OP_GetSubtype    183 /* synopsis: r[P2] = r[P1].subtype            */
+#define OP_SetSubtype    184 /* synopsis: r[P2].subtype = r[P1]            */
+#define OP_FilterAdd     185 /* synopsis: filter(P1) += key(P3@P4)         */
+#define OP_Trace         186
+#define OP_CursorHint    187
+#define OP_ReleaseReg    188 /* synopsis: release r[P1@P2] mask P3         */
+#define OP_Noop          189
+#define OP_Explain       190
+#define OP_Abortable     191
 
 /* Properties such as "out2" or "jump" that are specified in
 ** comments following the "case" for each opcode in the vdbe.c
@@ -17457,25 +17885,26 @@ typedef struct VdbeOpList VdbeOpList;
 /*  16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\
 /*  24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\
 /*  32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x01, 0x41,\
-/*  40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x41, 0x23,\
-/*  48 */ 0x0b, 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\
-/*  56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01,\
-/*  64 */ 0x41, 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00,\
-/*  72 */ 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10,\
-/*  80 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02,\
-/*  88 */ 0x02, 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40,\
-/*  96 */ 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26,\
+/*  40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x41, 0x09,\
+/*  48 */ 0x23, 0x0b, 0x81, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\
+/*  56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x01, 0x03, 0x03, 0x03,\
+/*  64 */ 0x01, 0x41, 0x01, 0x00, 0x00, 0x02, 0x02, 0x08,\
+/*  72 */ 0x00, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10,\
+/*  80 */ 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00,\
+/*  88 */ 0x02, 0x02, 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20,\
+/*  96 */ 0x40, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x26,\
 /* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
-/* 112 */ 0x26, 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40,\
-/* 120 */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10,\
-/* 128 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,\
-/* 136 */ 0x50, 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50,\
-/* 144 */ 0x40, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\
-/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00,\
-/* 160 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10,\
-/* 176 */ 0x50, 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12,\
-/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
+/* 112 */ 0x26, 0x40, 0x40, 0x12, 0x00, 0x40, 0x10, 0x40,\
+/* 120 */ 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40,\
+/* 128 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,\
+/* 136 */ 0x00, 0x50, 0x00, 0x40, 0x04, 0x04, 0x00, 0x40,\
+/* 144 */ 0x50, 0x40, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,\
+/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x10,\
+/* 160 */ 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,\
+/* 176 */ 0x10, 0x50, 0x40, 0x00, 0x10, 0x10, 0x02, 0x12,\
+/* 184 */ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+}
 
 /* The resolve3P2Values() routine is able to run faster if it knows
 ** the value of the largest JUMP opcode.  The smaller the maximum
@@ -17483,7 +17912,7 @@ typedef struct VdbeOpList VdbeOpList;
 ** generated this include file strives to group all JUMP opcodes
 ** together near the beginning of the list.
 */
-#define SQLITE_MX_JUMP_OPCODE  65  /* Maximum JUMP opcode */
+#define SQLITE_MX_JUMP_OPCODE  66  /* Maximum JUMP opcode */
 
 /************** End of opcodes.h *********************************************/
 /************** Continuing where we left off in vdbe.h ***********************/
@@ -17492,7 +17921,7 @@ typedef struct VdbeOpList VdbeOpList;
 ** Additional non-public SQLITE_PREPARE_* flags
 */
 #define SQLITE_PREPARE_SAVESQL  0x80  /* Preserve SQL text */
-#define SQLITE_PREPARE_MASK     0x1f  /* Mask of public flags */
+#define SQLITE_PREPARE_MASK     0x3f  /* Mask of public flags */
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
@@ -17513,15 +17942,15 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeAddFunctionCall(Parse*,int,int,int,int,const FuncDef*,int);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeEndCoroutine(Vdbe*,int);
 #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeVerifyNoResultRow(Vdbe *p);
+SQLITE_PRIVATE   void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
+SQLITE_PRIVATE   void sqlite3VdbeVerifyNoResultRow(Vdbe *p);
 #else
 # define sqlite3VdbeVerifyNoMallocRequired(A,B)
 # define sqlite3VdbeVerifyNoResultRow(A)
 #endif
 #if defined(SQLITE_DEBUG)
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeVerifyAbortable(Vdbe *p, int);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int);
+SQLITE_PRIVATE   void sqlite3VdbeVerifyAbortable(Vdbe *p, int);
+SQLITE_PRIVATE   void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int);
 #else
 # define sqlite3VdbeVerifyAbortable(A,B)
 # define sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D)
@@ -17547,7 +17976,7 @@ SQLITE_PRIVATE   int SQLITE_APICALL sqlite3VdbeExplainParent(Parse*);
 # define sqlite3ExplainBreakpoint(A,B) /*no-op*/
 #endif
 #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ExplainBreakpoint(const char*,const char*);
+SQLITE_PRIVATE   void sqlite3ExplainBreakpoint(const char*,const char*);
 #else
 # define sqlite3ExplainBreakpoint(A,B) /*no-op*/
 #endif
@@ -17563,7 +17992,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr)
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeChangeToNoop(Vdbe*, int addr);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeReleaseRegisters(Parse*,int addr, int n, u32 mask, int);
+SQLITE_PRIVATE   void sqlite3VdbeReleaseRegisters(Parse*,int addr, int n, u32 mask, int);
 #else
 # define sqlite3VdbeReleaseRegisters(P,A,N,M,F)
 #endif
@@ -17582,7 +18011,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeFinalize(Vdbe*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeResolveLabel(Vdbe*, int);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeCurrentAddr(Vdbe*);
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3VdbeAssertMayAbort(Vdbe *, int);
+SQLITE_PRIVATE   int sqlite3VdbeAssertMayAbort(Vdbe *, int);
 #endif
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeResetStepResult(Vdbe*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeRewind(Vdbe*);
@@ -17730,7 +18159,7 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int);
 #endif
 
 #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbePrintOp(FILE*, int, VdbeOp*);
+SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*);
 #endif
 
 #if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG)
@@ -17774,10 +18203,10 @@ struct PgHdr {
   PCache *pCache;                /* PRIVATE: Cache that owns this page */
   PgHdr *pDirty;                 /* Transient list of dirty sorted by pgno */
   Pager *pPager;                 /* The pager this page is part of */
-  Pgno pgno;                     /* Page number for this page */
 #ifdef SQLITE_CHECK_PAGES
-  u32 pageHash;                  /* Hash of page content */
+  u64 pageHash;                  /* Hash of page content */
 #endif
+  Pgno pgno;                     /* Page number for this page */
   u16 flags;                     /* PGHDR flags defined below */
 
   /**********************************************************************
@@ -17881,12 +18310,12 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PcachePagecount(PCache*);
 ** interface is only available if SQLITE_CHECK_PAGES is defined when the
 ** library is built.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3PcacheIterateDirty(PCache *pCache, void (SQLITE_APICALL *xIter)(PgHdr *));
+SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
 #endif
 
 #if defined(SQLITE_DEBUG)
 /* Check invariants on a PgHdr object */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3PcachePageSanity(PgHdr*);
+SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr*);
 #endif
 
 /* Set and get the suggested cache-size for the specified pager-cache.
@@ -18117,7 +18546,7 @@ struct Schema {
 ** The number of different kinds of things that can be limited
 ** using the sqlite3_limit() interface.
 */
-#define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1)
+#define SQLITE_N_LIMIT (SQLITE_LIMIT_PARSER_DEPTH+1)
 
 /*
 ** Lookaside malloc is a set of fixed-size buffers that can be used
@@ -18271,6 +18700,7 @@ struct sqlite3 {
   u8 noSharedCache;             /* True if no shared-cache backends */
   u8 nSqlExec;                  /* Number of pending OP_SqlExec opcodes */
   u8 eOpenState;                /* Current condition of the connection */
+  u8 nFpDigit;                  /* Significant digits to keep on double->text */
   int nextPagesize;             /* Pagesize after VACUUM if >0 */
   i64 nChange;                  /* Value returned by sqlite3_changes() */
   i64 nTotalChange;             /* Value returned by sqlite3_total_changes() */
@@ -20165,19 +20595,6 @@ struct Upsert {
 /*
 ** An instance of the following structure contains all information
 ** needed to generate code for a single SELECT statement.
-**
-** See the header comment on the computeLimitRegisters() routine for a
-** detailed description of the meaning of the iLimit and iOffset fields.
-**
-** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes.
-** These addresses must be stored so that we can go back and fill in
-** the P4_KEYINFO and P2 parameters later.  Neither the KeyInfo nor
-** the number of columns in P2 can be computed at the same time
-** as the OP_OpenEphm instruction is coded because not
-** enough information about the compound query is known at that point.
-** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences
-** for the result set.  The KeyInfo for addrOpenEphm[2] contains collating
-** sequences for the ORDER BY clause.
 */
 struct Select {
   u8 op;                 /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
@@ -20185,7 +20602,6 @@ struct Select {
   u32 selFlags;          /* Various SF_* values */
   int iLimit, iOffset;   /* Memory registers holding LIMIT & OFFSET counters */
   u32 selId;             /* Unique identifier number for this SELECT */
-  int addrOpenEphm[2];   /* OP_OpenEphem opcodes related to this select */
   ExprList *pEList;      /* The fields of the result */
   SrcList *pSrc;         /* The FROM clause */
   Expr *pWhere;          /* The WHERE clause */
@@ -20217,7 +20633,7 @@ struct Select {
 #define SF_Resolved      0x0000004 /* Identifiers have been resolved */
 #define SF_Aggregate     0x0000008 /* Contains agg functions or a GROUP BY */
 #define SF_HasAgg        0x0000010 /* Contains aggregate functions */
-#define SF_UsesEphemeral 0x0000020 /* Uses the OpenEphemeral opcode */
+#define SF_ClonedRhsIn   0x0000020 /* Cloned RHS of an IN operator */
 #define SF_Expanded      0x0000040 /* sqlite3SelectExpand() called on this */
 #define SF_HasTypeInfo   0x0000080 /* FROM subqueries have Table metadata */
 #define SF_Compound      0x0000100 /* Part of a compound query */
@@ -20227,14 +20643,14 @@ struct Select {
 #define SF_MinMaxAgg     0x0001000 /* Aggregate containing min() or max() */
 #define SF_Recursive     0x0002000 /* The recursive part of a recursive CTE */
 #define SF_FixedLimit    0x0004000 /* nSelectRow set by a constant LIMIT */
-#define SF_MaybeConvert  0x0008000 /* Need convertCompoundSelectToSubquery() */
+/*                       0x0008000 // available for reuse */
 #define SF_Converted     0x0010000 /* By convertCompoundSelectToSubquery() */
 #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */
 #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */
 #define SF_WhereBegin    0x0080000 /* Really a WhereBegin() call.  Debug Only */
 #define SF_WinRewrite    0x0100000 /* Window function rewrite accomplished */
 #define SF_View          0x0200000 /* SELECT statement is a view */
-#define SF_NoopOrderBy   0x0400000 /* ORDER BY is ignored for this query */
+/*                       0x0400000 // available for reuse */
 #define SF_UFSrcCheck    0x0800000 /* Check pSrc as required by UPDATE...FROM */
 #define SF_PushDown      0x1000000 /* Modified by WHERE-clause push-down opt */
 #define SF_MultiPart     0x2000000 /* Has multiple incompatible PARTITIONs */
@@ -20254,11 +20670,6 @@ struct Select {
 ** by one of the following macros.  The "SRT" prefix means "SELECT Result
 ** Type".
 **
-**     SRT_Union       Store results as a key in a temporary index
-**                     identified by pDest->iSDParm.
-**
-**     SRT_Except      Remove results from the temporary index pDest->iSDParm.
-**
 **     SRT_Exists      Store a 1 in memory cell pDest->iSDParm if the result
 **                     set is not empty.
 **
@@ -20322,30 +20733,28 @@ struct Select {
 **                     table. (pDest->iSDParm) is the number of key columns in
 **                     each index record in this case.
 */
-#define SRT_Union        1  /* Store result as keys in an index */
-#define SRT_Except       2  /* Remove result from a UNION index */
-#define SRT_Exists       3  /* Store 1 if the result is not empty */
-#define SRT_Discard      4  /* Do not save the results anywhere */
-#define SRT_DistFifo     5  /* Like SRT_Fifo, but unique results only */
-#define SRT_DistQueue    6  /* Like SRT_Queue, but unique results only */
+#define SRT_Exists       1  /* Store 1 if the result is not empty */
+#define SRT_Discard      2  /* Do not save the results anywhere */
+#define SRT_DistFifo     3  /* Like SRT_Fifo, but unique results only */
+#define SRT_DistQueue    4  /* Like SRT_Queue, but unique results only */
 
 /* The DISTINCT clause is ignored for all of the above.  Not that
 ** IgnorableDistinct() implies IgnorableOrderby() */
 #define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue)
 
-#define SRT_Queue        7  /* Store result in an queue */
-#define SRT_Fifo         8  /* Store result as data with an automatic rowid */
+#define SRT_Queue        5  /* Store result in an queue */
+#define SRT_Fifo         6  /* Store result as data with an automatic rowid */
 
 /* The ORDER BY clause is ignored for all of the above */
 #define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo)
 
-#define SRT_Output       9  /* Output each row of result */
-#define SRT_Mem         10  /* Store result in a memory cell */
-#define SRT_Set         11  /* Store results as keys in an index */
-#define SRT_EphemTab    12  /* Create transient tab and store like SRT_Table */
-#define SRT_Coroutine   13  /* Generate a single row of result */
-#define SRT_Table       14  /* Store result as data with an automatic rowid */
-#define SRT_Upfrom      15  /* Store result as data with rowid */
+#define SRT_Output       7  /* Output each row of result */
+#define SRT_Mem          8  /* Store result in a memory cell */
+#define SRT_Set          9  /* Store results as keys in an index */
+#define SRT_EphemTab    10  /* Create transient tab and store like SRT_Table */
+#define SRT_Coroutine   11  /* Generate a single row of result */
+#define SRT_Table       12  /* Store result as data with an automatic rowid */
+#define SRT_Upfrom      13  /* Store result as data with rowid */
 
 /*
 ** An instance of this object describes where to put of the results of
@@ -20481,17 +20890,12 @@ struct Parse {
   u8 nested;           /* Number of nested calls to the parser/code generator */
   u8 nTempReg;         /* Number of temporary registers in aTempReg[] */
   u8 isMultiWrite;     /* True if statement may modify/insert multiple rows */
-  u8 mayAbort;         /* True if statement may throw an ABORT exception */
-  u8 hasCompound;      /* Need to invoke convertCompoundSelectToSubquery() */
   u8 disableLookaside; /* Number of times lookaside has been disabled */
   u8 prepFlags;        /* SQLITE_PREPARE_* flags */
   u8 withinRJSubrtn;   /* Nesting level for RIGHT JOIN body subroutines */
-  u8 bHasExists;       /* Has a correlated "EXISTS (SELECT ....)" expression */
   u8 mSubrtnSig;       /* mini Bloom filter on available SubrtnSig.selId */
   u8 eTriggerOp;       /* TK_UPDATE, TK_INSERT or TK_DELETE */
-  u8 bReturning;       /* Coding a RETURNING trigger */
   u8 eOrconf;          /* Default ON CONFLICT policy for trigger steps */
-  u8 disableTriggers;  /* True to disable triggers */
 #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
   u8 earlyCleanup;     /* OOM inside sqlite3ParserAddCleanup() */
 #endif
@@ -20500,10 +20904,15 @@ struct Parse {
   u8 isCreate;         /* CREATE TABLE, INDEX, or VIEW (but not TRIGGER)
                        ** and ALTER TABLE ADD COLUMN. */
 #endif
-  bft colNamesSet :1;   /* TRUE after OP_ColumnName has been issued to pVdbe */
-  bft bHasWith :1;      /* True if statement contains WITH */
-  bft okConstFactor :1; /* OK to factor out constants */
-  bft checkSchema :1;   /* Causes schema cookie check after an error */
+  bft disableTriggers:1; /* True to disable triggers */
+  bft mayAbort :1;     /* True if statement may throw an ABORT exception */
+  bft hasCompound :1;  /* Need to invoke convertCompoundSelectToSubquery() */
+  bft bReturning :1;   /* Coding a RETURNING trigger */
+  bft bHasExists :1;   /* Has a correlated "EXISTS (SELECT ....)" expression */
+  bft colNamesSet :1;  /* TRUE after OP_ColumnName has been issued to pVdbe */
+  bft bHasWith :1;     /* True if statement contains WITH */
+  bft okConstFactor:1; /* OK to factor out constants */
+  bft checkSchema :1;  /* Causes schema cookie check after an error */
   int nRangeReg;       /* Size of the temporary register block */
   int iRangeReg;       /* First register in temporary register block */
   int nErr;            /* Number of errors seen */
@@ -20732,19 +21141,19 @@ struct Trigger {
 ** orconf    -> stores the ON CONFLICT algorithm
 ** pSelect   -> The content to be inserted - either a SELECT statement or
 **              a VALUES clause.
-** zTarget   -> Dequoted name of the table to insert into.
+** pSrc      -> Table to insert into.
 ** pIdList   -> If this is an INSERT INTO ... (<column-names>) VALUES ...
 **              statement, then this stores the column-names to be
 **              inserted into.
 ** pUpsert   -> The ON CONFLICT clauses for an Upsert
 **
 ** (op == TK_DELETE)
-** zTarget   -> Dequoted name of the table to delete from.
+** pSrc      -> Table to delete from
 ** pWhere    -> The WHERE clause of the DELETE statement if one is specified.
 **              Otherwise NULL.
 **
 ** (op == TK_UPDATE)
-** zTarget   -> Dequoted name of the table to update.
+** pSrc      -> Table to update, followed by any FROM clause tables.
 ** pWhere    -> The WHERE clause of the UPDATE statement if one is specified.
 **              Otherwise NULL.
 ** pExprList -> A list of the columns to update and the expressions to update
@@ -20764,8 +21173,7 @@ struct TriggerStep {
   u8 orconf;           /* OE_Rollback etc. */
   Trigger *pTrig;      /* The trigger that this step is a part of */
   Select *pSelect;     /* SELECT statement or RHS of INSERT INTO SELECT ... */
-  char *zTarget;       /* Target table for DELETE, UPDATE, INSERT */
-  SrcList *pFrom;      /* FROM clause for UPDATE statement (if any) */
+  SrcList *pSrc;       /* Table to insert/update/delete */
   Expr *pWhere;        /* The WHERE clause for DELETE or UPDATE steps */
   ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */
   IdList *pIdList;     /* Column names for INSERT */
@@ -20848,10 +21256,11 @@ typedef struct {
 /*
 ** Allowed values for mInitFlags
 */
-#define INITFLAG_AlterMask     0x0003  /* Types of ALTER */
+#define INITFLAG_AlterMask     0x0007  /* Types of ALTER */
 #define INITFLAG_AlterRename   0x0001  /* Reparse after a RENAME */
 #define INITFLAG_AlterDrop     0x0002  /* Reparse after a DROP COLUMN */
 #define INITFLAG_AlterAdd      0x0003  /* Reparse after an ADD COLUMN */
+#define INITFLAG_AlterDropCons 0x0004  /* Reparse after an ADD COLUMN */
 
 /* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled
 ** on debug-builds of the CLI using ".testctrl tune ID VALUE".  Tuning
@@ -20981,6 +21390,7 @@ struct Walker {
     NameContext *pNC;                         /* Naming context */
     int n;                                    /* A counter */
     int iCur;                                 /* A cursor number */
+    int sz;                                   /* String literal length */
     SrcList *pSrcList;                        /* FROM clause */
     struct CCurHint *pCCurHint;               /* Used by codeCursorHint() */
     struct RefSrcList *pRefSrcList;           /* sqlite3ReferencesSrcList() */
@@ -21030,7 +21440,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WalkerDepthDecrease(Walker*,Select*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3WalkWinDefnDummyCallback(Walker*,Select*);
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectWalkAssert2(Walker*, Select*);
+SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*);
 #endif
 
 #ifndef SQLITE_OMIT_CTE
@@ -21233,8 +21643,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3CantopenError(int);
 #define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__)
 #define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__)
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3NomemError(int);
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3IoerrnomemError(int);
+SQLITE_PRIVATE   int sqlite3NomemError(int);
+SQLITE_PRIVATE   int sqlite3IoerrnomemError(int);
 # define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__)
 # define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__)
 #else
@@ -21242,7 +21652,7 @@ SQLITE_PRIVATE   int SQLITE_APICALL sqlite3IoerrnomemError(int);
 # define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM
 #endif
 #if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO)
-SQLITE_PRIVATE   int SQLITE_APICALL sqlite3CorruptPgnoError(int,Pgno);
+SQLITE_PRIVATE   int sqlite3CorruptPgnoError(int,Pgno);
 # define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P))
 #else
 # define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptError(__LINE__)
@@ -21385,7 +21795,20 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3LookasideUsed(sqlite3*,int*);
 SQLITE_PRIVATE sqlite3_mutex *SQLITE_APICALL sqlite3Pcache1Mutex(void);
 SQLITE_PRIVATE sqlite3_mutex *SQLITE_APICALL sqlite3MallocMutex(void);
 
-#if defined(SQLITE_ENABLE_MULTITHREADED_CHECKS) && !defined(SQLITE_MUTEX_OMIT)
+
+/* The SQLITE_THREAD_MISUSE_WARNINGS compile-time option used to be called
+** SQLITE_ENABLE_MULTITHREADED_CHECKS.  Keep that older macro for backwards
+** compatibility, at least for a while... */
+#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
+# define SQLITE_THREAD_MISUSE_WARNINGS 1
+#endif
+
+/* SQLITE_THREAD_MISUSE_ABORT implies SQLITE_THREAD_MISUSE_WARNINGS */
+#ifdef SQLITE_THREAD_MISUSE_ABORT
+# define SQLITE_THREAD_MISUSE_WARNINGS 1
+#endif
+
+#if defined(SQLITE_THREAD_MISUSE_WARNINGS) && !defined(SQLITE_MUTEX_OMIT)
 SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*);
 #else
 # define sqlite3MutexWarnOnContention(x)
@@ -21414,17 +21837,22 @@ struct PrintfArguments {
   sqlite3_value **apArg;   /* The argument values */
 };
 
+/*
+** Maxium number of base-10 digits in an unsigned 64-bit integer
+*/
+#define SQLITE_U64_DIGITS 20
+
 /*
 ** An instance of this object receives the decoding of a floating point
 ** value into an approximate decimal representation.
 */
 struct FpDecode {
-  char sign;           /* '+' or '-' */
-  char isSpecial;      /* 1: Infinity  2: NaN */
-  int n;               /* Significant digits in the decode */
-  int iDP;             /* Location of the decimal point */
-  char *z;             /* Start of significant digits */
-  char zBuf[24];       /* Storage for significant digits */
+  int n;                           /* Significant digits in the decode */
+  int iDP;                         /* Location of the decimal point */
+  char *z;                         /* Start of significant digits */
+  char zBuf[SQLITE_U64_DIGITS+1];  /* Storage for significant digits */
+  char sign;                       /* '+' or '-' */
+  char isSpecial;                  /* 1: Infinity  2: NaN */
 };
 
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3FpDecode(FpDecode*,double,int,int);
@@ -21439,16 +21867,16 @@ SQLITE_PRIVATE   void *sqlite3TestTextToPtr(const char*);
 
 #if defined(SQLITE_DEBUG)
 SQLITE_PRIVATE   void sqlite3TreeViewLine(TreeView*, const char *zFormat, ...);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewSrcList(TreeView*, const SrcList*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewSelect(TreeView*, const Select*, u8);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewWith(TreeView*, const With*, u8);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
+SQLITE_PRIVATE   void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
+SQLITE_PRIVATE   void sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*);
+SQLITE_PRIVATE   void sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*);
+SQLITE_PRIVATE   void sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewSrcList(TreeView*, const SrcList*);
+SQLITE_PRIVATE   void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewWith(TreeView*, const With*, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8);
 #if TREETRACE_ENABLED
 SQLITE_PRIVATE   void sqlite3TreeViewDelete(const With*, const SrcList*, const Expr*,
                              const ExprList*,const Expr*, const Trigger*);
@@ -21460,31 +21888,31 @@ SQLITE_PRIVATE   void sqlite3TreeViewUpdate(const With*, const SrcList*, const E
                              const Upsert*, const Trigger*);
 #endif
 #ifndef SQLITE_OMIT_TRIGGER
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8);
 #endif
 #ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewWindow(TreeView*, const Window*, u8);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3TreeViewWinFunc(TreeView*, const Window*, u8);
-#endif
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowExpr(const Expr*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowExprList(const ExprList*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowIdList(const IdList*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowSrcList(const SrcList*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowSelect(const Select*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowWith(const With*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowUpsert(const Upsert*);
+SQLITE_PRIVATE   void sqlite3TreeViewWindow(TreeView*, const Window*, u8);
+SQLITE_PRIVATE   void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8);
+#endif
+SQLITE_PRIVATE   void sqlite3ShowExpr(const Expr*);
+SQLITE_PRIVATE   void sqlite3ShowExprList(const ExprList*);
+SQLITE_PRIVATE   void sqlite3ShowIdList(const IdList*);
+SQLITE_PRIVATE   void sqlite3ShowSrcList(const SrcList*);
+SQLITE_PRIVATE   void sqlite3ShowSelect(const Select*);
+SQLITE_PRIVATE   void sqlite3ShowWith(const With*);
+SQLITE_PRIVATE   void sqlite3ShowUpsert(const Upsert*);
 #ifndef SQLITE_OMIT_TRIGGER
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowTriggerStep(const TriggerStep*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowTriggerStepList(const TriggerStep*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowTrigger(const Trigger*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowTriggerList(const Trigger*);
+SQLITE_PRIVATE   void sqlite3ShowTriggerStep(const TriggerStep*);
+SQLITE_PRIVATE   void sqlite3ShowTriggerStepList(const TriggerStep*);
+SQLITE_PRIVATE   void sqlite3ShowTrigger(const Trigger*);
+SQLITE_PRIVATE   void sqlite3ShowTriggerList(const Trigger*);
 #endif
 #ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowWindow(const Window*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowWinFunc(const Window*);
+SQLITE_PRIVATE   void sqlite3ShowWindow(const Window*);
+SQLITE_PRIVATE   void sqlite3ShowWinFunc(const Window*);
 #endif
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ShowBitvec(Bitvec*);
+SQLITE_PRIVATE   void sqlite3ShowBitvec(Bitvec*);
 #endif
 
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3SetString(char **, sqlite3*, const char*);
@@ -21506,13 +21934,14 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3ReleaseTempRange(Parse*,int,int);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3ClearTempRegCache(Parse*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3TouchRegister(Parse*,int);
 #if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG)
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3FirstAvailableRegister(Parse*,int);
+SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int);
 #endif
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3NoTempsInRange(Parse*,int,int);
+SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int);
 #endif
 SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
 SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3Expr(sqlite3*,int,const char*);
+SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3ExprInt32(sqlite3*,int);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
 SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3PExpr(Parse*, int, Expr*, Expr*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3PExprAddSelect(Parse*, Expr*, Select*);
@@ -21666,6 +22095,7 @@ SQLITE_PRIVATE Select *SQLITE_APICALL sqlite3SelectNew(Parse*,ExprList*,SrcList*
                          Expr*,ExprList*,u32,Expr*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectDelete(sqlite3*, Select*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectDeleteGeneric(sqlite3*,void*);
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectCheckOnClauses(Parse *pParse, Select *pSelect);
 SQLITE_PRIVATE Table *SQLITE_APICALL sqlite3SrcListLookup(Parse*, SrcList*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3IsReadOnly(Parse*, Table*, Trigger*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
@@ -21763,6 +22193,7 @@ SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3ExprIsInteger(const Expr*, int*, Parse*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3ExprCanBeNull(const Expr*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3ExprIsLikeOperator(const Expr*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3IsRowid(const char*);
 SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3RowidAlias(Table *pTab);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3GenerateRowDelete(
@@ -21831,17 +22262,16 @@ SQLITE_PRIVATE   void SQLITE_APICALL sqlite3CodeRowTriggerDirect(Parse *, Trigge
 SQLITE_PRIVATE   void SQLITE_APICALL sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
 SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerSelectStep(sqlite3*,Select*,
                                         const char*,const char*);
-SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerInsertStep(Parse*,Token*, IdList*,
+SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerInsertStep(Parse*,SrcList*, IdList*,
                                         Select*,u8,Upsert*,
                                         const char*,const char*);
-SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerUpdateStep(Parse*,Token*,SrcList*,ExprList*,
+SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerUpdateStep(Parse*,SrcList*,SrcList*,ExprList*,
                                         Expr*, u8, const char*,const char*);
-SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerDeleteStep(Parse*,Token*, Expr*,
+SQLITE_PRIVATE   TriggerStep *SQLITE_APICALL sqlite3TriggerDeleteStep(Parse*,SrcList*, Expr*,
                                         const char*,const char*);
 SQLITE_PRIVATE   void SQLITE_APICALL sqlite3DeleteTrigger(sqlite3*, Trigger*);
 SQLITE_PRIVATE   void SQLITE_APICALL sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
 SQLITE_PRIVATE   u32 SQLITE_APICALL sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
-SQLITE_PRIVATE   SrcList *SQLITE_APICALL sqlite3TriggerStepSrc(Parse*, TriggerStep*);
 # define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
 # define sqlite3IsToplevel(p) ((p)->pToplevel==0)
 #else
@@ -21855,7 +22285,6 @@ SQLITE_PRIVATE   SrcList *SQLITE_APICALL sqlite3TriggerStepSrc(Parse*, TriggerSt
 # define sqlite3ParseToplevel(p) p
 # define sqlite3IsToplevel(p) 1
 # define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
-# define sqlite3TriggerStepSrc(A,B) 0
 #endif
 
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3JoinType(Parse*, Token*, Token*, Token*);
@@ -21888,7 +22317,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3RealSameAsInt(double,sqlite3_int64);
 SQLITE_PRIVATE i64 SQLITE_APICALL sqlite3RealToI64(double);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3Int64ToText(i64,char*);
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3AtoF(const char *z, double*, int, u8);
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3AtoF(const char *z, double*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3GetInt32(const char *, int*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3GetUInt32(const char*, u32*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3Atoi(const char*);
@@ -22032,10 +22461,21 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3Reindex(Parse*, Token*, Token*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterFunctions(void);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterDropConstraint(Parse*,SrcList*,Token*,Token*);
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterAddConstraint(
+  Parse *pParse,           /* Parse context */
+  SrcList *pSrc,           /* Table to add constraint to */
+  Token *pFirst,           /* First token of new constraint */
+  Token *pName,            /* Name of new constraint. NULL if name omitted. */
+  const char *zExpr,       /* Text of CHECK expression */
+  int nExpr,               /* Size of pExpr in bytes */
+  Expr *pExpr              /* The parsed CHECK expression */
+);
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterSetNotNull(Parse*, SrcList*, Token*, Token*);
 SQLITE_PRIVATE i64 SQLITE_APICALL sqlite3GetToken(const unsigned char *, int *);
 SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3ExpirePreparedStatements(sqlite3*, int);
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3CodeRhsOfIN(Parse*, Expr*, int);
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3CodeRhsOfIN(Parse*, Expr*, int, int);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3CodeSubselect(Parse*, Expr*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectPrep(Parse*, Select*, NameContext*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3ExpandSubquery(Parse*, SrcItem*);
@@ -22085,7 +22525,7 @@ SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3SelectOpName(int);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3HasExplicitNulls(Parse*, ExprList*);
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3KeyInfoIsWriteable(KeyInfo*);
+SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
 #endif
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
   void (SQLITE_APICALL *)(sqlite3_context*,int,sqlite3_value **),
@@ -22108,6 +22548,7 @@ SQLITE_PRIVATE char *SQLITE_APICALL sqlite3RCStrResize(char*,u64);
 
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3StrAccumEnlarge(StrAccum*, i64);
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3StrAccumEnlargeIfNeeded(StrAccum*, i64);
 SQLITE_PRIVATE char *SQLITE_APICALL sqlite3StrAccumFinish(StrAccum*);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3StrAccumSetError(StrAccum*, u8);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3ResultStrAccum(sqlite3_context*,StrAccum*);
@@ -22374,7 +22815,7 @@ SQLITE_PRIVATE   void sqlite3ConnectionClosed(sqlite3 *db);
 #endif
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3ParserTrace(FILE*, char *);
+SQLITE_PRIVATE   void sqlite3ParserTrace(FILE*, char *);
 #endif
 #if defined(YYCOVERAGE)
 SQLITE_PRIVATE   int sqlite3ParserCoverage(FILE*);
@@ -23335,6 +23776,9 @@ static const char * const sqlite3azCompileOpt[] = {
 #ifdef SQLITE_STMTJRNL_SPILL
   "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL),
 #endif
+#ifdef SQLITE_STRICT_SUBTYPE
+  "STRICT_SUBTYPE",
+#endif
 #ifdef SQLITE_SUBSTR_COMPATIBILITY
   "SUBSTR_COMPATIBILITY",
 #endif
@@ -24437,7 +24881,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*
 SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeDisplayP4(sqlite3*,Op*);
 #endif
 #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
-SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*);
+SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*);
 #endif
 #if !defined(SQLITE_OMIT_EXPLAIN)
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeList(Vdbe*);
@@ -24450,6 +24894,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemShallowCopy(Mem*, const Mem*, i
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemMove(Mem*, Mem*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemNulTerminate(Mem*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(SQLITE_APICALL *)(void*));
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemSetText(Mem*, const char*, i64, void(SQLITE_APICALL *)(void*));
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemSetInt64(Mem*, i64);
 #ifdef SQLITE_OMIT_FLOATING_POINT
 # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64
@@ -24465,16 +24910,17 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemSetZeroBlob(Mem*,int);
 SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int);
 #endif
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemIsRowSet(const Mem*);
+SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*);
 #endif
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemSetRowSet(Mem*);
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemZeroTerminateIfAble(Mem*);
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemZeroTerminateIfAble(Mem*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemMakeWriteable(Mem*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemStringify(Mem*, u8, u8);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3IntFloatCompare(i64,double);
 SQLITE_PRIVATE i64 SQLITE_APICALL sqlite3VdbeIntValue(const Mem*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemIntegerify(Mem*);
 SQLITE_PRIVATE double SQLITE_APICALL sqlite3VdbeRealValue(Mem*);
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3MemRealValueRC(Mem*, double*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeBooleanValue(Mem*, int ifNull);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeIntegerAffinity(Mem*);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemRealify(Mem*);
@@ -24495,7 +24941,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemGrow(Mem *pMem, int n, int prese
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeCloseStatement(Vdbe *, int);
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeFrameIsValid(VdbeFrame*);
+SQLITE_PRIVATE int sqlite3VdbeFrameIsValid(VdbeFrame*);
 #endif
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeFrameMemDel(void*);      /* Destructor on Mem */
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */
@@ -24505,6 +24951,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
     Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int);
 #endif
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeTransferError(Vdbe *p);
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeFindIndexKey(BtCursor*, Index*, UnpackedRecord*, int*, int);
 
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
@@ -24518,8 +24965,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeSorterCompare(const VdbeCursor *, M
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeValueListFree(void*);
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeAssertAbortable(Vdbe*);
+SQLITE_PRIVATE   void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
+SQLITE_PRIVATE   void sqlite3VdbeAssertAbortable(Vdbe*);
 #else
 # define sqlite3VdbeIncrWriteCounter(V,C)
 # define sqlite3VdbeAssertAbortable(V)
@@ -24538,8 +24985,8 @@ SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeLeave(Vdbe*);
 #endif
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeCheckMemInvariants(Mem*);
+SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
+SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
 #endif
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
@@ -24551,8 +24998,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeCheckFkDeferred(Vdbe*);
 #endif
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbePrintSql(Vdbe*);
-SQLITE_PRIVATE   void SQLITE_APICALL sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
+SQLITE_PRIVATE   void sqlite3VdbePrintSql(Vdbe*);
+SQLITE_PRIVATE   void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
 #endif
 #ifndef SQLITE_OMIT_UTF16
 SQLITE_PRIVATE   int SQLITE_APICALL sqlite3VdbeMemTranslate(Mem*, u8);
@@ -25434,7 +25881,7 @@ static int SQLITE_APICALL parseDateOrTime(
     return 0;
   }else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
     return setDateTimeToCurrent(context, p);
-  }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){
+  }else if( sqlite3AtoF(zDate, &r)>0 ){
     setRawDateNumber(p, r);
     return 0;
   }else if( (sqlite3StrICmp(zDate,"subsec")==0
@@ -25880,7 +26327,7 @@ static int SQLITE_APICALL parseModifier(
       ** date is already on the appropriate weekday, this is a no-op.
       */
       if( sqlite3_strnicmp(z, "weekday ", 8)==0
-               && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0
+               && sqlite3AtoF(&z[8], &r)>0
                && r>=0.0 && r<7.0 && (n=(int)r)==r ){
         sqlite3_int64 Z;
         computeYMD_HMS(p);
@@ -25951,9 +26398,11 @@ static int SQLITE_APICALL parseModifier(
     case '8':
     case '9': {
       double rRounder;
-      int i;
+      int i, rx;
       int Y,M,D,h,m,x;
       const char *z2 = z;
+      char *zCopy;
+      sqlite3 *db = sqlite3_context_db_handle(pCtx);
       char z0 = z[0];
       for(n=1; z[n]; n++){
         if( z[n]==':' ) break;
@@ -25963,7 +26412,11 @@ static int SQLITE_APICALL parseModifier(
           if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break;
         }
       }
-      if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){
+      zCopy = sqlite3DbStrNDup(db, z, n);
+      if( zCopy==0 ) break;
+      rx = sqlite3AtoF(zCopy, &r)<=0;
+      sqlite3DbFree(db, zCopy);
+      if( rx ){
         assert( rc==1 );
         break;
       }
@@ -26773,7 +27226,7 @@ static void currentTimeFunc(
 ** This routine returns JSON that describes the internal DateTime object.
 ** Used for debugging and testing only.  Subject to change.
 */
-static void SQLITE_APICALL datedebugFunc(
+static void datedebugFunc(
   sqlite3_context *context,
   int argc,
   sqlite3_value **argv
@@ -26783,7 +27236,7 @@ static void SQLITE_APICALL datedebugFunc(
     char *zJson;
     zJson = sqlite3_mprintf(
       "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d,"
-      "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d,"
+      "s:%.3f,validJD:%d,validYMD:%d,validHMS:%d,"
       "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d,"
       "isUtc:%d,isLocal:%d}",
       x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz,
@@ -29562,23 +30015,28 @@ static SQLITE_WSD int mutexIsInit = 0;
 
 #ifndef SQLITE_MUTEX_OMIT
 
-#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
+#ifdef SQLITE_THREAD_MISUSE_WARNINGS
 /*
-** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains
+** This block (enclosed by SQLITE_THREAD_MISUSE_WARNINGS) contains
 ** the implementation of a wrapper around the system default mutex
 ** implementation (sqlite3DefaultMutex()).
 **
 ** Most calls are passed directly through to the underlying default
 ** mutex implementation. Except, if a mutex is configured by calling
 ** sqlite3MutexWarnOnContention() on it, then if contention is ever
-** encountered within xMutexEnter() a warning is emitted via sqlite3_log().
+** encountered within xMutexEnter() then a warning is emitted via
+** sqlite3_log().  Furthermore, if SQLITE_THREAD_MISUSE_ABORT is
+** defined then abort() is called after the sqlite3_log() warning.
 **
-** This type of mutex is used as the database handle mutex when testing
-** apps that usually use SQLITE_CONFIG_MULTITHREAD mode.
+** This type of mutex is used on the database handle mutex when testing
+** apps that usually use SQLITE_CONFIG_MULTITHREAD mode.  A failure
+** indicates that the app ought to be using SQLITE_OPEN_FULLMUTEX or
+** similar because it is trying to use the same database handle from
+** two different connections at the same time.
 */
 
 /*
-** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS
+** Type for all mutexes used when SQLITE_THREAD_MISUSE_WARNINGS
 ** is defined. Variable CheckMutex.mutex is a pointer to the real mutex
 ** allocated by the system mutex implementation. Variable iType is usually set
 ** to the type of mutex requested - SQLITE_MUTEX_RECURSIVE, SQLITE_MUTEX_FAST
@@ -29614,11 +30072,12 @@ static int checkMutexNotheld(sqlite3_mutex *p){
 */
 static int checkMutexInit(void){
   pGlobalMutexMethods = sqlite3DefaultMutex();
-  return SQLITE_OK;
+  return pGlobalMutexMethods->xMutexInit();
 }
 static int checkMutexEnd(void){
+  int rc = pGlobalMutexMethods->xMutexEnd();
   pGlobalMutexMethods = 0;
-  return SQLITE_OK;
+  return rc;
 }
 
 /*
@@ -29695,6 +30154,9 @@ static void checkMutexEnter(sqlite3_mutex *p){
     sqlite3_log(SQLITE_MISUSE,
         "illegal multi-threaded access to database connection"
     );
+#if SQLITE_THREAD_MISUSE_ABORT
+    abort();
+#endif
   }
   pGlobalMutexMethods->xMutexEnter(pCheck->mutex);
 }
@@ -29746,7 +30208,7 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex *p){
     pCheck->iType = SQLITE_MUTEX_WARNONCONTENTION;
   }
 }
-#endif   /* ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS */
+#endif   /* ifdef SQLITE_THREAD_MISUSE_WARNINGS */
 
 /*
 ** Initialize the mutex system.
@@ -29763,7 +30225,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3MutexInit(void){
     sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex;
 
     if( sqlite3GlobalConfig.bCoreMutex ){
-#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
+#ifdef SQLITE_THREAD_MISUSE_WARNINGS
       pFrom = multiThreadedCheckMutex();
 #else
       pFrom = sqlite3DefaultMutex();
@@ -29886,7 +30348,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_mutex_leave(sqlite3_mutex *p){
 ** Because these routines raise false-positive alerts in TSAN, disable
 ** them (make them always return 1) when compiling with TSAN.
 */
-SQLITE_API int SQLITE_APICALL sqlite3_mutex_held(sqlite3_mutex *p){
+SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){
 # if defined(__has_feature)
 #   if __has_feature(thread_sanitizer)
       p = 0;
@@ -29895,7 +30357,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_mutex_held(sqlite3_mutex *p){
   assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
   return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
 }
-SQLITE_API int SQLITE_APICALL sqlite3_mutex_notheld(sqlite3_mutex *p){
+SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
 # if defined(__has_feature)
 #   if __has_feature(thread_sanitizer)
       p = 0;
@@ -29947,21 +30409,21 @@ SQLITE_API int SQLITE_APICALL sqlite3_mutex_notheld(sqlite3_mutex *p){
 **
 ** This routines provide no mutual exclusion or error checking.
 */
-static int noopMutexInit(void){ return SQLITE_OK; }
-static int noopMutexEnd(void){ return SQLITE_OK; }
-static sqlite3_mutex *noopMutexAlloc(int id){
+static int SQLITE_APICALL noopMutexInit(void){ return SQLITE_OK; }
+static int SQLITE_APICALL noopMutexEnd(void){ return SQLITE_OK; }
+static sqlite3_mutex *SQLITE_APICALL noopMutexAlloc(int id){
   UNUSED_PARAMETER(id);
   return (sqlite3_mutex*)8;
 }
-static void noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
-static void noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
-static int noopMutexTry(sqlite3_mutex *p){
+static void SQLITE_APICALL noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
+static void SQLITE_APICALL noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
+static int SQLITE_APICALL noopMutexTry(sqlite3_mutex *p){
   UNUSED_PARAMETER(p);
   return SQLITE_OK;
 }
-static void noopMutexLeave(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
+static void SQLITE_APICALL noopMutexLeave(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
 
-SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){
+SQLITE_PRIVATE sqlite3_mutex_methods const *SQLITE_APICALL sqlite3NoopMutex(void){
   static const sqlite3_mutex_methods sMutex = {
     noopMutexInit,
     noopMutexEnd,
@@ -29998,11 +30460,11 @@ typedef struct sqlite3_debug_mutex {
 ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
 ** intended for use inside assert() statements.
 */
-static int SQLITE_APICALL debugMutexHeld(sqlite3_mutex *pX){
+static int debugMutexHeld(sqlite3_mutex *pX){
   sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
   return p==0 || p->cnt>0;
 }
-static int SQLITE_APICALL debugMutexNotheld(sqlite3_mutex *pX){
+static int debugMutexNotheld(sqlite3_mutex *pX){
   sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
   return p==0 || p->cnt==0;
 }
@@ -30010,15 +30472,15 @@ static int SQLITE_APICALL debugMutexNotheld(sqlite3_mutex *pX){
 /*
 ** Initialize and deinitialize the mutex subsystem.
 */
-static int SQLITE_APICALL debugMutexInit(void){ return SQLITE_OK; }
-static int SQLITE_APICALL debugMutexEnd(void){ return SQLITE_OK; }
+static int debugMutexInit(void){ return SQLITE_OK; }
+static int debugMutexEnd(void){ return SQLITE_OK; }
 
 /*
 ** The sqlite3_mutex_alloc() routine allocates a new
 ** mutex and returns a pointer to it.  If it returns NULL
 ** that means that a mutex could not be allocated.
 */
-static sqlite3_mutex *SQLITE_APICALL debugMutexAlloc(int id){
+static sqlite3_mutex *debugMutexAlloc(int id){
   static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1];
   sqlite3_debug_mutex *pNew = 0;
   switch( id ){
@@ -30049,7 +30511,7 @@ static sqlite3_mutex *SQLITE_APICALL debugMutexAlloc(int id){
 /*
 ** This routine deallocates a previously allocated mutex.
 */
-static void SQLITE_APICALL debugMutexFree(sqlite3_mutex *pX){
+static void debugMutexFree(sqlite3_mutex *pX){
   sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
   assert( p->cnt==0 );
   if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){
@@ -30072,12 +30534,12 @@ static void SQLITE_APICALL debugMutexFree(sqlite3_mutex *pX){
 ** can enter.  If the same thread tries to enter any other kind of mutex
 ** more than once, the behavior is undefined.
 */
-static void SQLITE_APICALL debugMutexEnter(sqlite3_mutex *pX){
+static void debugMutexEnter(sqlite3_mutex *pX){
   sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
   assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) );
   p->cnt++;
 }
-static int SQLITE_APICALL debugMutexTry(sqlite3_mutex *pX){
+static int debugMutexTry(sqlite3_mutex *pX){
   sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
   assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) );
   p->cnt++;
@@ -30090,14 +30552,14 @@ static int SQLITE_APICALL debugMutexTry(sqlite3_mutex *pX){
 ** is undefined if the mutex is not currently entered or
 ** is not currently allocated.  SQLite will never do either.
 */
-static void SQLITE_APICALL debugMutexLeave(sqlite3_mutex *pX){
+static void debugMutexLeave(sqlite3_mutex *pX){
   sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
   assert( debugMutexHeld(pX) );
   p->cnt--;
   assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) );
 }
 
-SQLITE_PRIVATE sqlite3_mutex_methods const *SQLITE_APICALL sqlite3NoopMutex(void){
+SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){
   static const sqlite3_mutex_methods sMutex = {
     debugMutexInit,
     debugMutexEnd,
@@ -30611,14 +31073,6 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
 # define SQLITE_OS_WINCE 0
 #endif
 
-/*
-** Determine if we are dealing with WinRT, which provides only a subset of
-** the full Win32 API.
-*/
-#if !defined(SQLITE_OS_WINRT)
-# define SQLITE_OS_WINRT 0
-#endif
-
 /*
 ** For WinCE, some API function parameters do not appear to be declared as
 ** volatile.
@@ -30633,7 +31087,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
 ** For some Windows sub-platforms, the _beginthreadex() / _endthreadex()
 ** functions are not available (e.g. those not using MSVC, Cygwin, etc).
 */
-#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
+#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && \
     SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
 # define SQLITE_OS_WIN_THREADS 1
 #else
@@ -30684,15 +31138,15 @@ struct sqlite3_mutex {
 ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
 ** intended for use only inside assert() statements.
 */
-static int SQLITE_APICALL winMutexHeld(sqlite3_mutex *p){
+static int winMutexHeld(sqlite3_mutex *p){
   return p->nRef!=0 && p->owner==GetCurrentThreadId();
 }
 
-static int SQLITE_APICALL winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
+static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
   return p->nRef==0 || p->owner!=tid;
 }
 
-static int SQLITE_APICALL winMutexNotheld(sqlite3_mutex *p){
+static int winMutexNotheld(sqlite3_mutex *p){
   DWORD tid = GetCurrentThreadId();
   return winMutexNotheld2(p, tid);
 }
@@ -30750,11 +31204,7 @@ static int SQLITE_APICALL winMutexInit(void){
   if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
     int i;
     for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
-#if SQLITE_OS_WINRT
-      InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0);
-#else
       InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
-#endif
     }
     winMutex_isInit = 1;
   }else{
@@ -30844,11 +31294,7 @@ static sqlite3_mutex *SQLITE_APICALL winMutexAlloc(int iType){
         p->trace = 1;
 #endif
 #endif
-#if SQLITE_OS_WINRT
-        InitializeCriticalSectionEx(&p->mutex, 0, 0);
-#else
         InitializeCriticalSection(&p->mutex);
-#endif
       }
       break;
     }
@@ -31251,7 +31697,7 @@ static void SQLITE_APICALL sqlite3MallocAlarm(int nByte){
 ** code debuggers when working on out-of-memory conditions, for example
 ** caused by PRAGMA hard_heap_limit=N.
 */
-static SQLITE_NOINLINE void SQLITE_APICALL test_oom_breakpoint(u64 n){
+static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){
   static u64 nOomFault = 0;
   nOomFault += n;
   /* The assert() is never reached in a human lifetime.  It  is here mostly
@@ -31312,27 +31758,6 @@ static void SQLITE_APICALL mallocWithAlarm(int n, void **pp){
   *pp = p;
 }
 
-/*
-** Maximum size of any single memory allocation.
-**
-** This is not a limit on the total amount of memory used.  This is
-** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc().
-**
-** The upper bound is slightly less than 2GiB:  0x7ffffeff == 2,147,483,391
-** This provides a 256-byte safety margin for defense against 32-bit
-** signed integer overflow bugs when computing memory allocation sizes.
-** Paranoid applications might want to reduce the maximum allocation size
-** further for an even larger safety margin.  0x3fffffff or 0x0fffffff
-** or even smaller would be reasonable upper bounds on the size of a memory
-** allocations for most applications.
-*/
-#ifndef SQLITE_MAX_ALLOCATION_SIZE
-# define SQLITE_MAX_ALLOCATION_SIZE  2147483391
-#endif
-#if SQLITE_MAX_ALLOCATION_SIZE>2147483391
-# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391
-#endif
-
 /*
 ** Allocate memory.  This routine is like sqlite3_malloc() except that it
 ** assumes the memory subsystem has already been initialized.
@@ -31556,8 +31981,7 @@ SQLITE_PRIVATE void *SQLITE_APICALL sqlite3Realloc(void *pOld, u64 nBytes){
     sqlite3_free(pOld); /* IMP: R-26507-47431 */
     return 0;
   }
-  if( nBytes>=0x7fffff00 ){
-    /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */
+  if( nBytes>SQLITE_MAX_ALLOCATION_SIZE ){
     return 0;
   }
   nOld = sqlite3MallocSize(pOld);
@@ -32111,7 +32535,7 @@ static char *SQLITE_APICALL printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){
     sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG);
     return 0;
   }
-  z = sqlite3DbMallocRaw(pAccum->db, n);
+  z = sqlite3_malloc(n);
   if( z==0 ){
     sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM);
   }
@@ -32442,9 +32866,11 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
           }while( longvalue>0 );
         }
         length = (int)(&zOut[nOut-1]-bufpt);
-        while( precision>length ){
-          *(--bufpt) = '0';                             /* Zero pad */
-          length++;
+        if( precision>length ){                         /* zero pad */
+          int nn = precision-length;
+          bufpt -= nn;
+          memset(bufpt,'0',nn);
+          length = precision;
         }
         if( cThousand ){
           int nn = (length - 1)/3;  /* Number of "," to insert */
@@ -32475,6 +32901,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
         FpDecode s;
         int iRound;
         int j;
+        i64 szBufNeeded;       /* Size needed to hold the output */
 
         if( bArgList ){
           realvalue = getDoubleArg(pArgList);
@@ -32495,7 +32922,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
         }else{
           iRound = precision+1;
         }
-        sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16);
+        sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 20 : 16);
         if( s.isSpecial ){
           if( s.isSpecial==2 ){
             bufpt = flag_zeropad ? "null" : "NaN";
@@ -32563,17 +32990,31 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
         }else{
           e2 = s.iDP - 1;
         }
-        bufpt = buf;
-        {
-          i64 szBufNeeded;           /* Size of a temporary buffer needed */
-          szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
-          if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3;
-          if( szBufNeeded > etBUFSIZE ){
-            bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
-            if( bufpt==0 ) return;
+
+        szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+10;
+        if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3;
+        if( szBufNeeded + pAccum->nChar >= pAccum->nAlloc ){
+          if( pAccum->mxAlloc==0 && pAccum->accError==0 ){
+            /* Unable to allocate space in pAccum, perhaps because it
+            ** is coming from sqlite3_snprintf() or similar.  We'll have
+            ** to render into temporary space and the memcpy() it over. */
+            bufpt = sqlite3_malloc(szBufNeeded);
+            if( bufpt==0 ){
+              sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM);
+              return;
+            }
+            zExtra = bufpt;
+          }else if( sqlite3StrAccumEnlarge(pAccum, szBufNeeded)<szBufNeeded ){
+            width = length = 0;
+            break;
+          }else{
+            bufpt = pAccum->zText + pAccum->nChar;
           }
+        }else{
+          bufpt = pAccum->zText + pAccum->nChar;
         }
         zOut = bufpt;
+
         flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
         /* The sign in front of the number */
         if( prefix ){
@@ -32581,12 +33022,24 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
         }
         /* Digits prior to the decimal point */
         j = 0;
+        assert( s.n>0 );
         if( e2<0 ){
           *(bufpt++) = '0';
-        }else{
+        }else if( cThousand ){
           for(; e2>=0; e2--){
             *(bufpt++) = j<s.n ? s.z[j++] : '0';
-            if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
+            if( (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
+          }
+        }else{
+          j = e2+1;
+          if( j>s.n ) j = s.n;
+          memcpy(bufpt, s.z, j);
+          bufpt += j;
+          e2 -= j;
+          if( e2>=0 ){
+            memset(bufpt, '0', e2+1);
+            bufpt += e2+1;
+            e2 = -1;
           }
         }
         /* The decimal point */
@@ -32595,12 +33048,26 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
         }
         /* "0" digits after the decimal point but before the first
         ** significant digit of the number */
-        for(e2++; e2<0 && precision>0; precision--, e2++){
-          *(bufpt++) = '0';
+        if( e2<(-1) && precision>0 ){
+          int nn = -1-e2;
+          if( nn>precision ) nn = precision;
+          memset(bufpt, '0', nn);
+          bufpt += nn;
+          precision -= nn;
         }
         /* Significant digits after the decimal point */
-        while( (precision--)>0 ){
-          *(bufpt++) = j<s.n ? s.z[j++] : '0';
+        if( precision>0 ){
+          int nn = s.n - j;
+          if( NEVER(nn>precision) ) nn = precision;
+          if( nn>0 ){
+            memcpy(bufpt, s.z+j, nn);
+            bufpt += nn;
+            precision -= nn;
+          }
+          if( precision>0 && !flag_rtz ){
+            memset(bufpt, '0', precision);
+            bufpt += precision;
+          }
         }
         /* Remove trailing zeros and the "." if no digits follow the "." */
         if( flag_rtz && flag_dp ){
@@ -32630,27 +33097,39 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
           *(bufpt++) = (char)(exp/10+'0');             /* 10's digit */
           *(bufpt++) = (char)(exp%10+'0');             /* 1's digit */
         }
-        *bufpt = 0;
 
-        /* The converted number is in buf[] and zero terminated. Output it.
-        ** Note that the number is in the usual order, not reversed as with
-        ** integer conversions. */
         length = (int)(bufpt-zOut);
-        bufpt = zOut;
-
-        /* Special case:  Add leading zeros if the flag_zeropad flag is
-        ** set and we are not left justified */
-        if( flag_zeropad && !flag_leftjustify && length < width){
-          int i;
-          int nPad = width - length;
-          for(i=width; i>=nPad; i--){
-            bufpt[i] = bufpt[i-nPad];
+        assert( length <= szBufNeeded );
+        if( length<width ){
+          i64 nPad = width - length;
+          if( flag_leftjustify ){
+            memset(bufpt, ' ', nPad);
+          }else if( !flag_zeropad ){
+            memmove(zOut+nPad, zOut, length);
+            memset(zOut, ' ', nPad);
+          }else{
+            int adj = prefix!=0;
+            memmove(zOut+nPad+adj, zOut+adj, length-adj);
+            memset(zOut+adj, '0', nPad);
           }
-          i = prefix!=0;
-          while( nPad-- ) bufpt[i++] = '0';
           length = width;
         }
-        break;
+
+        if( zExtra==0 ){
+          /* The result is being rendered directory into pAccum.  This
+          ** is the command and fast case */
+          pAccum->nChar += length;
+          zOut[length] = 0;
+          continue;
+        }else{
+          /* We were unable to render directly into pAccum because we
+          ** couldn't allocate sufficient memory.  We need to memcpy()
+          ** the rendering (or some prefix thereof) into the output
+          ** buffer. */
+          bufpt[0] = 0;
+          bufpt = zExtra;
+          break;
+        }
       }
       case etSIZE:
         if( !bArgList ){
@@ -32694,10 +33173,9 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
             i64 nCopyBytes;
             if( nPrior > precision-1 ) nPrior = precision - 1;
             nCopyBytes = length*nPrior;
-            if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){
-              sqlite3StrAccumEnlarge(pAccum, nCopyBytes);
+            if( sqlite3StrAccumEnlargeIfNeeded(pAccum, nCopyBytes) ){
+              break;
             }
-            if( pAccum->accError ) break;
             sqlite3_str_append(pAccum,
                  &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes);
             precision -= nPrior;
@@ -32801,8 +33279,8 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
           ** all control characters, and for backslash itself.
           ** For %#Q, do the same but only if there is at least
           ** one control character. */
-          u32 nBack = 0;
-          u32 nCtrl = 0;
+          i64 nBack = 0;
+          i64 nCtrl = 0;
           for(k=0; k<i; k++){
             if( escarg[k]=='\\' ){
               nBack++;
@@ -33044,6 +33522,13 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3StrAccumEnlarge(StrAccum *p, i64 N){
   return (int)N;
 }
 
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3StrAccumEnlargeIfNeeded(StrAccum *p, i64 N){
+  if( N + p->nChar >= p->nAlloc ){
+    sqlite3StrAccumEnlarge(p, N);
+  }
+  return p->accError;
+}
+
 /*
 ** Append N copies of character c to the given string buffer.
 */
@@ -33174,6 +33659,14 @@ SQLITE_API int SQLITE_APICALL sqlite3_str_length(sqlite3_str *p){
   return p ? p->nChar : 0;
 }
 
+/* Truncate the text of the string to be no more than N bytes. */
+SQLITE_API void SQLITE_APICALL sqlite3_str_truncate(sqlite3_str *p, int N){
+  if( p!=0 && N>=0 && (u32)N<p->nChar ){
+    p->nChar = N;
+    p->zText[p->nChar] = 0;
+  }
+}
+
 /* Return the current value for p */
 SQLITE_API char *SQLITE_APICALL sqlite3_str_value(sqlite3_str *p){
   if( p==0 || p->nChar==0 ) return 0;
@@ -33194,6 +33687,17 @@ SQLITE_API void SQLITE_APICALL sqlite3_str_reset(StrAccum *p){
   p->zText = 0;
 }
 
+/*
+** Destroy a dynamically allocate sqlite3_str object and all
+** of its content, all in one call.
+*/
+SQLITE_API void SQLITE_APICALL sqlite3_str_free(sqlite3_str *p){
+  if( p!=0 && p!=&sqlite3OomStr ){
+    sqlite3_str_reset(p);
+    sqlite3_free(p);
+  }
+}
+
 /*
 ** Initialize a string accumulator.
 **
@@ -33531,7 +34035,7 @@ SQLITE_PRIVATE char *SQLITE_APICALL sqlite3RCStrResize(char *z, u64 N){
 ** Add a new subitem to the tree.  The moreToFollow flag indicates that this
 ** is not the last item in the tree.
 */
-static void SQLITE_APICALL sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){
+static void sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){
   TreeView *p = *pp;
   if( p==0 ){
     *pp = p = sqlite3_malloc64( sizeof(*p) );
@@ -33547,7 +34051,7 @@ static void SQLITE_APICALL sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){
 /*
 ** Finished with one layer of the tree
 */
-static void SQLITE_APICALL sqlite3TreeViewPop(TreeView **pp){
+static void sqlite3TreeViewPop(TreeView **pp){
   TreeView *p = *pp;
   if( p==0 ) return;
   p->iLevel--;
@@ -33588,7 +34092,7 @@ SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
 /*
 ** Shorthand for starting a new tree item that consists of a single label
 */
-static void SQLITE_APICALL sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){
+static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){
   sqlite3TreeViewPush(&p, moreFollows);
   sqlite3TreeViewLine(p, "%s", zLabel);
 }
@@ -33596,7 +34100,7 @@ static void SQLITE_APICALL sqlite3TreeViewItem(TreeView *p, const char *zLabel,u
 /*
 ** Show a list of Column objects in tree format.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewColumnList(
+SQLITE_PRIVATE void sqlite3TreeViewColumnList(
   TreeView *pView,
   const Column *aCol,
   int nCol,
@@ -33643,7 +34147,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewColumnList(
 /*
 ** Generate a human-readable description of a WITH clause.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
   int i;
   if( pWith==0 ) return;
   if( pWith->nCte==0 ) return;
@@ -33689,7 +34193,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewWith(TreeView *pView, const Wi
 /*
 ** Generate a human-readable description of a SrcList object.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
+SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
   int i;
   if( pSrc==0 ) return;
   for(i=0; i<pSrc->nSrc; i++){
@@ -33772,7 +34276,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewSrcList(TreeView *pView, const
 /*
 ** Generate a human-readable description of a Select object.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
+SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
   int n = 0;
   int cnt = 0;
   if( p==0 ){
@@ -33888,7 +34392,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewSelect(TreeView *pView, const
 /*
 ** Generate a description of starting or stopping bounds
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewBound(
+SQLITE_PRIVATE void sqlite3TreeViewBound(
   TreeView *pView,        /* View context */
   u8 eBound,              /* UNBOUNDED, CURRENT, PRECEDING, FOLLOWING */
   Expr *pExpr,            /* Value for PRECEDING or FOLLOWING */
@@ -33925,7 +34429,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewBound(
 /*
 ** Generate a human-readable explanation for a Window object
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
+SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
   int nElement = 0;
   if( pWin==0 ) return;
   if( pWin->pFilter ){
@@ -33992,7 +34496,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewWindow(TreeView *pView, const
 /*
 ** Generate a human-readable explanation for a Window Function object
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){
+SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){
   if( pWin==0 ) return;
   sqlite3TreeViewPush(&pView, more);
   sqlite3TreeViewLine(pView, "WINFUNC %s(%d)",
@@ -34005,7 +34509,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewWinFunc(TreeView *pView, const
 /*
 ** Generate a human-readable explanation of an expression tree.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
+SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
   const char *zBinOp = 0;   /* Binary operator */
   const char *zUniOp = 0;   /* Unary operator */
   char zFlgs[200];
@@ -34404,7 +34908,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewExpr(TreeView *pView, const Ex
 /*
 ** Generate a human-readable explanation of an expression list.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewBareExprList(
+SQLITE_PRIVATE void sqlite3TreeViewBareExprList(
   TreeView *pView,
   const ExprList *pList,
   const char *zLabel
@@ -34458,7 +34962,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewBareExprList(
     }
   }
 }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewExprList(
+SQLITE_PRIVATE void sqlite3TreeViewExprList(
   TreeView *pView,
   const ExprList *pList,
   u8 moreToFollow,
@@ -34472,7 +34976,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewExprList(
 /*
 ** Generate a human-readable explanation of an id-list.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewBareIdList(
+SQLITE_PRIVATE void sqlite3TreeViewBareIdList(
   TreeView *pView,
   const IdList *pList,
   const char *zLabel
@@ -34494,7 +34998,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewBareIdList(
     }
   }
 }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewIdList(
+SQLITE_PRIVATE void sqlite3TreeViewIdList(
   TreeView *pView,
   const IdList *pList,
   u8 moreToFollow,
@@ -34508,7 +35012,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewIdList(
 /*
 ** Generate a human-readable explanation of a list of Upsert objects
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewUpsert(
+SQLITE_PRIVATE void sqlite3TreeViewUpsert(
   TreeView *pView,
   const Upsert *pUpsert,
   u8 moreToFollow
@@ -34742,7 +35246,7 @@ SQLITE_PRIVATE void sqlite3TreeViewUpdate(
 /*
 ** Show a human-readable graph of a TriggerStep
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewTriggerStep(
+SQLITE_PRIVATE void sqlite3TreeViewTriggerStep(
   TreeView *pView,
   const TriggerStep *pStep,
   u8 moreToFollow,
@@ -34765,7 +35269,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewTriggerStep(
 /*
 ** Show a human-readable graph of a Trigger
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewTrigger(
+SQLITE_PRIVATE void sqlite3TreeViewTrigger(
   TreeView *pView,
   const Trigger *pTrigger,
   u8 moreToFollow,
@@ -34804,26 +35308,32 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TreeViewTrigger(
 **
 **     sqlite3ShowWhereTerm() in where.c
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowUpsert(const Upsert *p){ sqlite3TreeViewUpsert(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
+SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
+SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){
+  TreeView *pView = 0;
+  sqlite3TreeViewPush(&pView, 0);
+  sqlite3TreeViewLine(pView, "SRCLIST");
+  sqlite3TreeViewSrcList(pView,p);
+  sqlite3TreeViewPop(&pView);
+}
+SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert *p){ sqlite3TreeViewUpsert(0,p,0); }
 #ifndef SQLITE_OMIT_TRIGGER
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowTriggerStep(const TriggerStep *p){
+SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep *p){
   sqlite3TreeViewTriggerStep(0,p,0,0);
 }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowTriggerStepList(const TriggerStep *p){
+SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep *p){
   sqlite3TreeViewTriggerStep(0,p,0,1);
 }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowTrigger(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,0); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowTriggerList(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,1);}
+SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,0); }
+SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,1);}
 #endif
 #ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowWindow(const Window *p){ sqlite3TreeViewWindow(0,p,0); }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowWinFunc(const Window *p){ sqlite3TreeViewWinFunc(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowWindow(const Window *p){ sqlite3TreeViewWindow(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window *p){ sqlite3TreeViewWinFunc(0,p,0); }
 #endif
 
 #endif /* SQLITE_DEBUG */
@@ -35188,6 +35698,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3ThreadJoin(SQLiteThread *p, void **ppOu
     rc = sqlite3Win32Wait((HANDLE)p->tid);
     assert( rc!=WAIT_IO_COMPLETION );
     bRc = CloseHandle((HANDLE)p->tid);
+    (void)bRc;  /* Prevent warning when assert() is a no-op */
     assert( bRc );
   }
   if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
@@ -36327,266 +36838,618 @@ SQLITE_PRIVATE u8 SQLITE_APICALL sqlite3StrIHash(const char *z){
   return h;
 }
 
-/* Double-Double multiplication.  (x[0],x[1]) *= (y,yy)
-**
-** Reference:
-**   T. J. Dekker, "A Floating-Point Technique for Extending the
-**   Available Precision".  1971-07-26.
+#if !defined(SQLITE_DISABLE_INTRINSIC)               \
+  && (defined(__GNUC__) || defined(__clang__))       \
+  && (defined(__x86_64__) || defined(__aarch64__) || \
+      (defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen>32)))
+#define SQLITE_USE_UINT128
+#endif
+
+/*
+** Two inputs are multiplied to get a 128-bit result.  Write the
+** lower 64-bits of the result into *pLo, and return the high-order
+** 64 bits.
 */
-static void SQLITE_APICALL dekkerMul2(volatile double *x, double y, double yy){
-  /*
-  ** The "volatile" keywords on parameter x[] and on local variables
-  ** below are needed force intermediate results to be truncated to
-  ** binary64 rather than be carried around in an extended-precision
-  ** format.  The truncation is necessary for the Dekker algorithm to
-  ** work.  Intel x86 floating point might omit the truncation without
-  ** the use of volatile.
-  */
-  volatile double tx, ty, p, q, c, cc;
-  double hx, hy;
-  u64 m;
-  memcpy(&m, (void*)&x[0], 8);
-  m &= 0xfffffffffc000000LL;
-  memcpy(&hx, &m, 8);
-  tx = x[0] - hx;
-  memcpy(&m, &y, 8);
-  m &= 0xfffffffffc000000LL;
-  memcpy(&hy, &m, 8);
-  ty = y - hy;
-  p = hx*hy;
-  q = hx*ty + tx*hy;
-  c = p+q;
-  cc = p - c + q + tx*ty;
-  cc = x[0]*yy + x[1]*y + cc;
-  x[0] = c + cc;
-  x[1] = c - x[0];
-  x[1] += cc;
+static u64 SQLITE_APICALL sqlite3Multiply128(u64 a, u64 b, u64 *pLo){
+#if defined(SQLITE_USE_UINT128)
+  __uint128_t r = (__uint128_t)a * b;
+  *pLo = (u64)r;
+  return (u64)(r>>64);
+#elif defined(_WIN64) && !defined(SQLITE_DISABLE_INTRINSIC)
+  *pLo = a*b;
+  return __umulh(a, b);
+#else
+  u64 a0 = (u32)a;
+  u64 a1 = a >> 32;
+  u64 b0 = (u32)b;
+  u64 b1 = b >> 32;
+  u64 a0b0 = a0 * b0;
+  u64 a1b1 = a1 * b1;
+  u64 a0b1 = a0 * b1;
+  u64 a1b0 = a1 * b0;
+  u64 t = (a0b0 >> 32) + (u32)a0b1 + (u32)a1b0;
+  *pLo = (a0b0 & UINT64_C(0xffffffff)) | (t << 32);
+  return a1b1 + (a0b1>>32) + (a1b0>>32) + (t>>32);
+#endif
+}
+
+/*
+** A is an unsigned 96-bit integer formed by (a<<32)+aLo.
+** B is an unsigned 64-bit integer.
+**
+** Compute the upper 96 bits of 160-bit result of A*B.
+**
+** Write ((A*B)>>64 & 0xffffffff) (the middle 32 bits of A*B)
+** into *pLo.  Return the upper 64 bits of A*B.
+**
+** The lower 64 bits of A*B are discarded.
+*/
+static u64 SQLITE_APICALL sqlite3Multiply160(u64 a, u32 aLo, u64 b, u32 *pLo){
+#if defined(SQLITE_USE_UINT128)
+  __uint128_t r = (__uint128_t)a * b;
+  r += ((__uint128_t)aLo * b) >> 32;
+  *pLo = (r>>32)&0xffffffff;
+  return r>>64;
+#elif defined(_WIN64) && !defined(SQLITE_DISABLE_INTRINSIC)
+  u64 r1_hi = __umulh(a,b);
+  u64 r1_lo = a*b;
+  u64 r2 = (__umulh((u64)aLo,b)<<32) + ((aLo*b)>>32);
+  u64 t = r1_lo + r2;
+  if( t<r1_lo ) r1_hi++;
+  *pLo = t>>32;
+  return r1_hi;
+#else
+  u64 x2 = a>>32;
+  u64 x1 = a&0xffffffff;
+  u64 x0 = aLo;
+  u64 y1 = b>>32;
+  u64 y0 = b&0xffffffff;
+  u64 x2y1 = x2*y1;
+  u64 r4 = x2y1>>32;
+  u64 x2y0 = x2*y0;
+  u64 x1y1 = x1*y1;
+  u64 r3 = (x2y1 & 0xffffffff) + (x2y0 >>32) + (x1y1 >>32);
+  u64 x1y0 = x1*y0;
+  u64 x0y1 = x0*y1;
+  u64 r2 = (x2y0 & 0xffffffff) + (x1y1 & 0xffffffff) +
+             (x1y0 >>32) + (x0y1>>32);
+  u64 x0y0 = x0*y0;
+  u64 r1 = (x1y0 & 0xffffffff) + (x0y1 & 0xffffffff) +
+             (x0y0 >>32);
+  r2 += r1>>32;
+  r3 += r2>>32;
+  *pLo = r2&0xffffffff;
+  return (r4<<32) + r3;
+#endif
 }
 
+#undef SQLITE_USE_UINT128
+
 /*
-** The string z[] is an text representation of a real number.
-** Convert this string to a double and write it into *pResult.
+** Return a u64 with the N-th bit set.
+*/
+#define U64_BIT(N)  (((u64)1)<<(N))
+
+/*
+** Range of powers of 10 that we need to deal with when converting
+** IEEE754 doubles to and from decimal.
+*/
+#define POWERSOF10_FIRST (-348)
+#define POWERSOF10_LAST  (+347)
+
+/*
+** For any p between -348 and +347, return the integer part of
 **
-** The string z[] is length bytes in length (bytes, not characters) and
-** uses the encoding enc.  The string is not necessarily zero-terminated.
+**    pow(10,p) * pow(2,63-pow10to2(p))
 **
-** Return TRUE if the result is a valid real number (or integer) and FALSE
-** if the string is empty or contains extraneous text.  More specifically
-** return
-**      1          =>  The input string is a pure integer
-**      2 or more  =>  The input has a decimal point or eNNN clause
-**      0 or less  =>  The input string is not a valid number
-**     -1          =>  Not a valid number, but has a valid prefix which
-**                     includes a decimal point and/or an eNNN clause
+** Or, in other words, for any p in range, return the most significant
+** 64 bits of pow(10,p).  The pow(10,p) value is shifted left or right,
+** as appropriate so the most significant 64 bits fit exactly into a
+** 64-bit unsigned integer.
 **
-** Valid numbers are in one of these formats:
+** Write into *pLo the next 32 significant bits of the answer after
+** the first 64.
 **
-**    [+-]digits[E[+-]digits]
-**    [+-]digits.[digits][E[+-]digits]
-**    [+-].digits[E[+-]digits]
+** Algorithm:
 **
-** Leading and trailing whitespace is ignored for the purpose of determining
-** validity.
+** (1) For p between 0 and 26, return the value directly from the aBase[]
+**     lookup table.
+**
+** (2) For p outside the range 0 to 26, use aScale[] for the initial value
+**     then refine that result (if necessary) by a single multiplication
+**     against aBase[].
+**
+** The constant tables aBase[], aScale[], and aScaleLo[] are generated
+** by the C program at ../tool/mkfptab.c run with the --round option.
+*/
+static u64 SQLITE_APICALL powerOfTen(int p, u32 *pLo){
+  static const u64 aBase[] = {
+    UINT64_C(0x8000000000000000), /*  0: 1.0e+0 << 63 */
+    UINT64_C(0xa000000000000000), /*  1: 1.0e+1 << 60 */
+    UINT64_C(0xc800000000000000), /*  2: 1.0e+2 << 57 */
+    UINT64_C(0xfa00000000000000), /*  3: 1.0e+3 << 54 */
+    UINT64_C(0x9c40000000000000), /*  4: 1.0e+4 << 50 */
+    UINT64_C(0xc350000000000000), /*  5: 1.0e+5 << 47 */
+    UINT64_C(0xf424000000000000), /*  6: 1.0e+6 << 44 */
+    UINT64_C(0x9896800000000000), /*  7: 1.0e+7 << 40 */
+    UINT64_C(0xbebc200000000000), /*  8: 1.0e+8 << 37 */
+    UINT64_C(0xee6b280000000000), /*  9: 1.0e+9 << 34 */
+    UINT64_C(0x9502f90000000000), /* 10: 1.0e+10 << 30 */
+    UINT64_C(0xba43b74000000000), /* 11: 1.0e+11 << 27 */
+    UINT64_C(0xe8d4a51000000000), /* 12: 1.0e+12 << 24 */
+    UINT64_C(0x9184e72a00000000), /* 13: 1.0e+13 << 20 */
+    UINT64_C(0xb5e620f480000000), /* 14: 1.0e+14 << 17 */
+    UINT64_C(0xe35fa931a0000000), /* 15: 1.0e+15 << 14 */
+    UINT64_C(0x8e1bc9bf04000000), /* 16: 1.0e+16 << 10 */
+    UINT64_C(0xb1a2bc2ec5000000), /* 17: 1.0e+17 << 7 */
+    UINT64_C(0xde0b6b3a76400000), /* 18: 1.0e+18 << 4 */
+    UINT64_C(0x8ac7230489e80000), /* 19: 1.0e+19 >> 0 */
+    UINT64_C(0xad78ebc5ac620000), /* 20: 1.0e+20 >> 3 */
+    UINT64_C(0xd8d726b7177a8000), /* 21: 1.0e+21 >> 6 */
+    UINT64_C(0x878678326eac9000), /* 22: 1.0e+22 >> 10 */
+    UINT64_C(0xa968163f0a57b400), /* 23: 1.0e+23 >> 13 */
+    UINT64_C(0xd3c21bcecceda100), /* 24: 1.0e+24 >> 16 */
+    UINT64_C(0x84595161401484a0), /* 25: 1.0e+25 >> 20 */
+    UINT64_C(0xa56fa5b99019a5c8), /* 26: 1.0e+26 >> 23 */
+  };
+  static const u64 aScale[] = {
+    UINT64_C(0x8049a4ac0c5811ae), /*  0: 1.0e-351 << 1229 */
+    UINT64_C(0xcf42894a5dce35ea), /*  1: 1.0e-324 << 1140 */
+    UINT64_C(0xa76c582338ed2621), /*  2: 1.0e-297 << 1050 */
+    UINT64_C(0x873e4f75e2224e68), /*  3: 1.0e-270 << 960 */
+    UINT64_C(0xda7f5bf590966848), /*  4: 1.0e-243 << 871 */
+    UINT64_C(0xb080392cc4349dec), /*  5: 1.0e-216 << 781 */
+    UINT64_C(0x8e938662882af53e), /*  6: 1.0e-189 << 691 */
+    UINT64_C(0xe65829b3046b0afa), /*  7: 1.0e-162 << 602 */
+    UINT64_C(0xba121a4650e4ddeb), /*  8: 1.0e-135 << 512 */
+    UINT64_C(0x964e858c91ba2655), /*  9: 1.0e-108 << 422 */
+    UINT64_C(0xf2d56790ab41c2a2), /* 10: 1.0e-81 << 333 */
+    UINT64_C(0xc428d05aa4751e4c), /* 11: 1.0e-54 << 243 */
+    UINT64_C(0x9e74d1b791e07e48), /* 12: 1.0e-27 << 153 */
+    UINT64_C(0xcccccccccccccccc), /* 13: 1.0e-1 << 67 (special case) */
+    UINT64_C(0xcecb8f27f4200f3a), /* 14: 1.0e+27 >> 26 */
+    UINT64_C(0xa70c3c40a64e6c51), /* 15: 1.0e+54 >> 116 */
+    UINT64_C(0x86f0ac99b4e8dafd), /* 16: 1.0e+81 >> 206 */
+    UINT64_C(0xda01ee641a708de9), /* 17: 1.0e+108 >> 295 */
+    UINT64_C(0xb01ae745b101e9e4), /* 18: 1.0e+135 >> 385 */
+    UINT64_C(0x8e41ade9fbebc27d), /* 19: 1.0e+162 >> 475 */
+    UINT64_C(0xe5d3ef282a242e81), /* 20: 1.0e+189 >> 564 */
+    UINT64_C(0xb9a74a0637ce2ee1), /* 21: 1.0e+216 >> 654 */
+    UINT64_C(0x95f83d0a1fb69cd9), /* 22: 1.0e+243 >> 744 */
+    UINT64_C(0xf24a01a73cf2dccf), /* 23: 1.0e+270 >> 833 */
+    UINT64_C(0xc3b8358109e84f07), /* 24: 1.0e+297 >> 923 */
+    UINT64_C(0x9e19db92b4e31ba9), /* 25: 1.0e+324 >> 1013 */
+  };
+  static const unsigned int aScaleLo[] = {
+    0x205b896d, /*  0: 1.0e-351 << 1229 */
+    0x52064cad, /*  1: 1.0e-324 << 1140 */
+    0xaf2af2b8, /*  2: 1.0e-297 << 1050 */
+    0x5a7744a7, /*  3: 1.0e-270 << 960 */
+    0xaf39a475, /*  4: 1.0e-243 << 871 */
+    0xbd8d794e, /*  5: 1.0e-216 << 781 */
+    0x547eb47b, /*  6: 1.0e-189 << 691 */
+    0x0cb4a5a3, /*  7: 1.0e-162 << 602 */
+    0x92f34d62, /*  8: 1.0e-135 << 512 */
+    0x3a6a07f9, /*  9: 1.0e-108 << 422 */
+    0xfae27299, /* 10: 1.0e-81 << 333 */
+    0xaa97e14c, /* 11: 1.0e-54 << 243 */
+    0x775ea265, /* 12: 1.0e-27 << 153 */
+    0xcccccccc, /* 13: 1.0e-1 << 67 (special case) */
+    0x00000000, /* 14: 1.0e+27 >> 26 */
+    0x999090b6, /* 15: 1.0e+54 >> 116 */
+    0x69a028bb, /* 16: 1.0e+81 >> 206 */
+    0xe80e6f48, /* 17: 1.0e+108 >> 295 */
+    0x5ec05dd0, /* 18: 1.0e+135 >> 385 */
+    0x14588f14, /* 19: 1.0e+162 >> 475 */
+    0x8f1668c9, /* 20: 1.0e+189 >> 564 */
+    0x6d953e2c, /* 21: 1.0e+216 >> 654 */
+    0x4abdaf10, /* 22: 1.0e+243 >> 744 */
+    0xbc633b39, /* 23: 1.0e+270 >> 833 */
+    0x0a862f81, /* 24: 1.0e+297 >> 923 */
+    0x6c07a2c2, /* 25: 1.0e+324 >> 1013 */
+  };
+  int g, n;
+  u64 s, x;
+  u32 lo;
+
+  assert( p>=POWERSOF10_FIRST && p<=POWERSOF10_LAST );
+  if( p<0 ){
+    if( p==(-1) ){
+      *pLo = aScaleLo[13];
+      return aScale[13];
+    }
+    g = p/27;
+    n = p%27;
+    if( n ){
+      g--;
+      n += 27;
+    }
+  }else if( p<27 ){
+    *pLo = 0;
+    return aBase[p];
+  }else{
+    g = p/27;
+    n = p%27;
+  }
+  s = aScale[g+13];
+  if( n==0 ){
+    *pLo = aScaleLo[g+13];
+    return s;
+  }
+  x = sqlite3Multiply160(s,aScaleLo[g+13],aBase[n],&lo);
+  if( (U64_BIT(63) & x)==0 ){
+    x  = x<<1 | ((lo>>31)&1);
+    lo = (lo<<1) | 1;
+  }
+  *pLo = lo;
+  return x;
+}
+
+/*
+** pow10to2(x) computes floor(log2(pow(10,x))).
+** pow2to10(y) computes floor(log10(pow(2,y))).
+**
+** Conceptually, pow10to2(p) converts a base-10 exponent p into
+** a corresponding base-2 exponent, and pow2to10(e) converts a base-2
+** exponent into a base-10 exponent.
 **
-** If some prefix of the input string is a valid number, this routine
-** returns FALSE but it still converts the prefix and writes the result
-** into *pResult.
+** The conversions are based on the observation that:
+**
+**     ln(10.0)/ln(2.0) == 108853/32768     (approximately)
+**     ln(2.0)/ln(10.0) == 78913/262144     (approximately)
+**
+** These ratios are approximate, but they are accurate to 5 digits,
+** which is close enough for the usage here.  Right-shift is used
+** for division so that rounding of negative numbers happens in the
+** right direction.
 */
-#if defined(_MSC_VER)
-#pragma warning(disable : 4756)
-#endif
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
-#ifndef SQLITE_OMIT_FLOATING_POINT
-  int incr;
-  const char *zEnd;
-  /* sign * significand * (10 ^ (esign * exponent)) */
-  int sign = 1;    /* sign of significand */
-  u64 s = 0;       /* significand */
-  int d = 0;       /* adjust exponent for shifting decimal point */
-  int esign = 1;   /* sign of exponent */
-  int e = 0;       /* exponent */
-  int eValid = 1;  /* True exponent is either not used or is well-formed */
-  int nDigit = 0;  /* Number of digits processed */
-  int eType = 1;   /* 1: pure integer,  2+: fractional  -1 or less: bad UTF16 */
-  u64 s2;          /* round-tripped significand */
-  double rr[2];
+static int SQLITE_APICALL pwr10to2(int p){ return (p*108853) >> 15; }
+static int SQLITE_APICALL pwr2to10(int p){ return (p*78913) >> 18; }
 
-  assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
-  *pResult = 0.0;   /* Default return value, in case of an error */
-  if( length==0 ) return 0;
+/*
+** Count leading zeros for a 64-bit unsigned integer.
+*/
+static int SQLITE_APICALL countLeadingZeros(u64 m){
+#if (defined(__GNUC__) || defined(__clang__)) \
+    && !defined(SQLITE_DISABLE_INTRINSIC)
+  return __builtin_clzll(m);
+#else
+  int n = 0;
+  if( m <= 0x00000000ffffffffULL) { n += 32; m <<= 32; }
+  if( m <= 0x0000ffffffffffffULL) { n += 16; m <<= 16; }
+  if( m <= 0x00ffffffffffffffULL) { n += 8;  m <<= 8;  }
+  if( m <= 0x0fffffffffffffffULL) { n += 4;  m <<= 4;  }
+  if( m <= 0x3fffffffffffffffULL) { n += 2;  m <<= 2;  }
+  if( m <= 0x7fffffffffffffffULL) { n += 1;            }
+  return n;
+#endif
+}
 
-  if( enc==SQLITE_UTF8 ){
-    incr = 1;
-    zEnd = z + length;
+/*
+** Given m and e, which represent a quantity r == m*pow(2,e),
+** return values *pD and *pP such that r == (*pD)*pow(10,*pP),
+** approximately.  *pD should contain at least n significant digits.
+**
+** The input m is required to have its highest bit set.  In other words,
+** m should be left-shifted, and e decremented, to maximize the value of m.
+*/
+static void SQLITE_APICALL sqlite3Fp2Convert10(u64 m, int e, int n, u64 *pD, int *pP){
+  int p;
+  u64 h, d1;
+  u32 d2;
+  assert( n>=1 && n<=18 );
+  p = n - 1 - pwr2to10(e+63);
+  h = sqlite3Multiply128(m, powerOfTen(p,&d2), &d1);
+  assert( -(e + pwr10to2(p) + 2) >= 0  );
+  assert( -(e + pwr10to2(p) + 1) <= 63 );
+  if( n==18 ){
+    h >>= -(e + pwr10to2(p) + 2);
+    *pD = (h + ((h<<1)&2))>>1;
   }else{
-    int i;
-    incr = 2;
-    length &= ~1;
-    assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
-    testcase( enc==SQLITE_UTF16LE );
-    testcase( enc==SQLITE_UTF16BE );
-    for(i=3-enc; i<length && z[i]==0; i+=2){}
-    if( i<length ) eType = -100;
-    zEnd = &z[i^1];
-    z += (enc&1);
+    *pD = h >> -(e + pwr10to2(p) + 1);
   }
+  *pP = -p;
+}
 
-  /* skip leading spaces */
-  while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
-  if( z>=zEnd ) return 0;
-
-  /* get sign of significand */
-  if( *z=='-' ){
-    sign = -1;
-    z+=incr;
-  }else if( *z=='+' ){
-    z+=incr;
-  }
+/*
+** Return an IEEE754 floating point value that approximates d*pow(10,p).
+**
+** The (current) algorithm is adapted from the work of Ross Cox at
+** https://github.com/rsc/fpfmt
+*/
+static double SQLITE_APICALL sqlite3Fp10Convert2(u64 d, int p){
+  int b, lp, e, adj, s;
+  u32 pwr10l, mid1;
+  u64 pwr10h, x, hi, lo, sticky, u, m;
+  double r;
+  if( p<POWERSOF10_FIRST ) return 0.0;
+  if( p>POWERSOF10_LAST ) return INFINITY;
+  b = 64 - countLeadingZeros(d);
+  lp = pwr10to2(p);
+  e = 53 - b - lp;
+  if( e > 1074 ){
+    if( e>=1130 ) return 0.0;
+    e = 1074;
+  }
+  s = -(e-(64-b) + lp + 3);
+  pwr10h = powerOfTen(p, &pwr10l);
+  if( pwr10l!=0 ){
+    pwr10h++;
+    pwr10l = ~pwr10l;
+  }
+  x = d<<(64-b);
+  hi = sqlite3Multiply128(x,pwr10h,&lo);
+  mid1 = lo>>32;
+  sticky = 1;
+  if( (hi & (U64_BIT(s)-1))==0 ) {
+    u32 mid2 = sqlite3Multiply128(x,((u64)pwr10l)<<32,&lo)>>32;
+    sticky = (mid1-mid2 > 1);
+    hi -= mid1 < mid2;
+  }
+  u = (hi>>s) | sticky;
+  adj = (u >= U64_BIT(55)-2);
+  if( adj ){
+    u = (u>>adj) | (u&1);
+    e -= adj;
+  }
+  m = (u + 1 + ((u>>2)&1)) >> 2;
+  if( e<=(-972) ) return INFINITY;
+  if((m & U64_BIT(52)) != 0){
+    m = (m & ~U64_BIT(52)) | ((u64)(1075-e)<<52);
+  }
+  memcpy(&r,&m,8);
+  return r;
+}
 
-  /* copy max significant digits to significand */
-  while( z<zEnd && sqlite3Isdigit(*z) ){
-    s = s*10 + (*z - '0');
-    z+=incr; nDigit++;
-    if( s>=((LARGEST_UINT64-9)/10) ){
-      /* skip non-significant significand digits
-      ** (increase exponent by d to shift decimal left) */
-      while( z<zEnd && sqlite3Isdigit(*z) ){ z+=incr; d++; }
+/*
+** The string z[] is an text representation of a real number.
+** Convert this string to a double and write it into *pResult.
+**
+** z[] must be UTF-8 and zero-terminated.
+**
+** Return positive if the result is a valid real number (or integer) and
+** zero or negative if the string is empty or contains extraneous text.
+** Lower bits of the return value contain addition information about the
+** parse:
+**
+**   bit 0       =>   Set if any prefix of the input is valid.  Clear if
+**                    there is no prefix of the input that can be seen as
+**                    a valid floating point number.
+**   bit 1       =>   Set if the input contains a decimal point or eNNN
+**                    clause.  Zero if the input is an integer.
+**   bit 2       =>   The input is exactly 0.0, not an underflow from
+**                    some value near zero.
+**   bit 3       =>   Set if there are more than about 19 significant
+**                    digits in the input.
+**
+** If the input contains a syntax error but begins with text that might
+** be a valid number of some kind, then the result is negative.  The
+** result is only zero if no prefix of the input could be interpreted as
+** a number.
+**
+** Leading and trailing whitespace is ignored.  Valid numbers are in
+** one of the formats below:
+**
+**    [+-]digits[E[+-]digits]
+**    [+-]digits.[digits][E[+-]digits]
+**    [+-].digits[E[+-]digits]
+**
+** Algorithm sketch:  Compute an unsigned 64-bit integer s and a base-10
+** exponent d such that the value encoding by the input is s*pow(10,d).
+** Then invoke sqlite3Fp10Convert2() to calculated the closest possible
+** IEEE754 double.  The sign is added back afterwards, if the input string
+** starts with a "-".  The use of an unsigned 64-bit s mantissa means that
+** only about the first 19 significant digits of the input can contribute
+** to the result.  This can result in suboptimal rounding decisions when
+** correct rounding requires more than 19 input digits.  For example,
+** this routine renders "3500000000000000.2500001" as
+** 3500000000000000.0 instead of 3500000000000000.5 because the decision
+** to round up instead of using banker's rounding to round down is determined
+** by the 23rd significant digit, which this routine ignores. It is not
+** possible to do better without some kind of BigNum.
+*/
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3AtoF(const char *zIn, double *pResult){
+#ifndef SQLITE_OMIT_FLOATING_POINT
+  const unsigned char *z = (const unsigned char*)zIn;
+  int neg = 0;       /* True for a negative value */
+  u64 s = 0;         /* mantissa */
+  int d = 0;         /* Value is s * pow(10,d) */
+  int mState = 0;    /* 1: digit seen 2: fp 4: hard-zero */
+  unsigned v;        /* Value of a single digit */
+
+  start_of_text:
+  if( (v = (unsigned)z[0] - '0')<10 ){
+    parse_integer_part:
+    mState = 1;
+    s = v;
+    z++;
+    while( (v = (unsigned)z[0] - '0')<10 ){
+      s = s*10 + v;
+      z++;
+      if( s>=(LARGEST_UINT64-9)/10 ){
+        mState = 9;
+        while( sqlite3Isdigit(z[0]) ){ z++; d++; }
+        break;
+      }
     }
+  }else if( z[0]=='-' ){
+    neg = 1;
+    z++;
+    if( (v = (unsigned)z[0] - '0')<10 ) goto parse_integer_part;
+  }else if( z[0]=='+' ){
+    z++;
+    if( (v = (unsigned)z[0] - '0')<10 ) goto parse_integer_part;
+  }else if( sqlite3Isspace(z[0]) ){
+    do{ z++; }while( sqlite3Isspace(z[0]) );
+    goto start_of_text;
+  }else{
+    s = 0;
   }
-  if( z>=zEnd ) goto do_atof_calc;
 
   /* if decimal point is present */
   if( *z=='.' ){
-    z+=incr;
-    eType++;
-    /* copy digits from after decimal to significand
-    ** (decrease exponent by d to shift decimal right) */
-    while( z<zEnd && sqlite3Isdigit(*z) ){
-      if( s<((LARGEST_UINT64-9)/10) ){
-        s = s*10 + (*z - '0');
-        d--;
-        nDigit++;
-      }
-      z+=incr;
+    z++;
+    if( sqlite3Isdigit(z[0]) ){
+      mState |= 1;
+      do{
+        if( s<(LARGEST_UINT64-9)/10 ){
+          s = s*10 + z[0] - '0';
+          d--;
+        }else{
+          mState = 11;
+        }
+      }while( sqlite3Isdigit(*++z) );
+    }else if( mState==0 ){
+      *pResult = 0.0;
+      return 0;
     }
+    mState |= 2;
+  }else if( mState==0 ){
+    *pResult = 0.0;
+    return 0;
   }
-  if( z>=zEnd ) goto do_atof_calc;
 
   /* if exponent is present */
   if( *z=='e' || *z=='E' ){
-    z+=incr;
-    eValid = 0;
-    eType++;
-
-    /* This branch is needed to avoid a (harmless) buffer overread.  The
-    ** special comment alerts the mutation tester that the correct answer
-    ** is obtained even if the branch is omitted */
-    if( z>=zEnd ) goto do_atof_calc;              /*PREVENTS-HARMLESS-OVERREAD*/
+    int esign;
+    z++;
 
     /* get sign of exponent */
     if( *z=='-' ){
       esign = -1;
-      z+=incr;
-    }else if( *z=='+' ){
-      z+=incr;
+      z++;
+    }else{
+      esign = +1;
+      if( *z=='+' ){
+        z++;
+      }
     }
     /* copy digits to exponent */
-    while( z<zEnd && sqlite3Isdigit(*z) ){
-      e = e<10000 ? (e*10 + (*z - '0')) : 10000;
-      z+=incr;
-      eValid = 1;
+    if( (v = (unsigned)z[0] - '0')<10 ){
+      int exp = v;
+      z++;
+      mState |= 2;
+      while( (v = (unsigned)z[0] - '0')<10 ){
+        exp = exp<10000 ? (exp*10 + v) : 10000;
+        z++;
+      }
+      d += esign*exp;
+    }else{
+      z--;  /* Leave z[0] at 'e' or '+' or '-',
+            ** so that the return is 0 or -1 */
     }
   }
 
-  /* skip trailing spaces */
-  while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
-
-do_atof_calc:
-  /* Zero is a special case */
+  /* Convert s*pow(10,d) into real */
   if( s==0 ){
-    *pResult = sign<0 ? -0.0 : +0.0;
-    goto atof_return;
-  }
-
-  /* adjust exponent by d, and update sign */
-  e = (e*esign) + d;
-
-  /* Try to adjust the exponent to make it smaller */
-  while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
-    s *= 10;
-    e--;
-  }
-  while( e<0 && (s%10)==0 ){
-    s /= 10;
-    e++;
-  }
-
-  rr[0] = (double)s;
-  assert( sizeof(s2)==sizeof(rr[0]) );
-#ifdef SQLITE_DEBUG
-  rr[1] = 18446744073709549568.0;
-  memcpy(&s2, &rr[1], sizeof(s2));
-  assert( s2==0x43efffffffffffffLL );
-#endif
-  /* Largest double that can be safely converted to u64
-  **         vvvvvvvvvvvvvvvvvvvvvv   */
-  if( rr[0]<=18446744073709549568.0 ){
-    s2 = (u64)rr[0];
-    rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
+    *pResult = 0.0;
+    mState |= 4;
   }else{
-    rr[1] = 0.0;
+    *pResult = sqlite3Fp10Convert2(s,d);
   }
-  assert( rr[1]<=1.0e-10*rr[0] );  /* Equal only when rr[0]==0.0 */
-
-  if( e>0 ){
-    while( e>=100  ){
-      e -= 100;
-      dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
-    }
-    while( e>=10   ){
-      e -= 10;
-      dekkerMul2(rr, 1.0e+10, 0.0);
-    }
-    while( e>=1    ){
-      e -= 1;
-      dekkerMul2(rr, 1.0e+01, 0.0);
-    }
-  }else{
-    while( e<=-100 ){
-      e += 100;
-      dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
-    }
-    while( e<=-10  ){
-      e += 10;
-      dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
-    }
-    while( e<=-1   ){
-      e += 1;
-      dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
-    }
-  }
-  *pResult = rr[0]+rr[1];
-  if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300;
-  if( sign<0 ) *pResult = -*pResult;
+  if( neg ) *pResult = -*pResult;
   assert( !sqlite3IsNaN(*pResult) );
 
-atof_return:
   /* return true if number and no extra non-whitespace characters after */
-  if( z==zEnd && nDigit>0 && eValid && eType>0 ){
-    return eType;
-  }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){
-    return -1;
-  }else{
-    return 0;
+  if( z[0]==0 ){
+    return mState;
+  }
+  if( sqlite3Isspace(z[0]) ){
+    do{ z++; }while( sqlite3Isspace(*z) );
+    if( z[0]==0 ){
+      return mState;
+    }
   }
+  return 0xfffffff0 | mState;
 #else
-  return !sqlite3Atoi64(z, pResult, length, enc);
+  return sqlite3Atoi64(z, pResult, strlen(z), SQLITE_UTF8)==0;
 #endif /* SQLITE_OMIT_FLOATING_POINT */
 }
-#if defined(_MSC_VER)
-#pragma warning(default : 4756)
+
+/*
+** Digit pairs used to convert a U64 or I64 into text, two digits
+** at a time.
+*/
+static const union {
+  char a[201];
+  short int forceAlignment;
+} sqlite3DigitPairs = {
+  "00010203040506070809"
+  "10111213141516171819"
+  "20212223242526272829"
+  "30313233343536373839"
+  "40414243444546474849"
+  "50515253545556575859"
+  "60616263646566676869"
+  "70717273747576777879"
+  "80818283848586878889"
+  "90919293949596979899"
+};
+
+/*
+** ARMv6, ARMv7, PPC32 are known to not support hardware u64 division.
+*/
+#if (defined(__arm__) && !defined(__aarch64__)) || \
+    (defined(__ppc__) && !defined(__ppc64__))
+# define SQLITE_AVOID_U64_DIVIDE 1
 #endif
 
+#ifdef SQLITE_AVOID_U64_DIVIDE
+/*
+** Render an unsigned 64-bit integer as text onto the end of a 2-byte
+** aligned buffer that is SQLITE_U64_DIGIT+1 bytes long.  The last byte
+** of the buffer will be filled with a \000 byte.
+**
+** Return the index into the buffer of the first byte.
+**
+** This routine is used on platforms where u64-division is slow because
+** it is not available in hardware and has to be emulated in software.
+** It seeks to minimize the number of u64 divisions and use u32 divisions
+** instead.  It is slower on platforms that have hardware u64 division,
+** but much faster on platforms that do not.
+*/
+static int sqlite3UInt64ToText(u64 v, char *zOut){
+  u32 x32, kk;
+  int i;
+  zOut[SQLITE_U64_DIGITS] = 0;
+  i = SQLITE_U64_DIGITS;
+  assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[0]) );
+  assert( TWO_BYTE_ALIGNMENT(zOut) );
+  while( (v>>32)!=0 ){
+    u32 y, x0, x1, y0, y1;
+    x32 = v % 100000000;
+    v   = v / 100000000;
+    y = x32 % 10000;
+    x32 /= 10000;
+    x1 = x32 / 100;
+    x0 = x32 % 100;
+    y1 = y / 100;
+    y0 = y % 100;
+    assert( i>=8 );
+    i -= 8;
+    *(u16*)(&zOut[i]) = *(u16*)&sqlite3DigitPairs.a[x1*2];
+    *(u16*)(&zOut[i+2]) = *(u16*)&sqlite3DigitPairs.a[x0*2];
+    *(u16*)(&zOut[i+4]) = *(u16*)&sqlite3DigitPairs.a[y1*2];
+    *(u16*)(&zOut[i+6]) = *(u16*)&sqlite3DigitPairs.a[y0*2];
+  }
+  x32 = v;
+  while( x32>=10 ){
+    kk  = x32 % 100;
+    x32 = x32 / 100;
+    assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[kk*2]) );
+    assert( i>=2 );
+    i -= 2;
+    assert( TWO_BYTE_ALIGNMENT(&zOut[i]) );
+    *(u16*)(&zOut[i]) = *(u16*)&sqlite3DigitPairs.a[kk*2];
+  }
+  if( x32 ){
+    assert( i>0 );
+    zOut[--i] = x32 + '0';
+  }
+  return i;
+}
+#endif /* defined(SQLITE_AVOID_U64_DIVIDE) */
+
 /*
 ** Render an signed 64-bit integer as text.  Store the result in zOut[] and
 ** return the length of the string that was stored, in bytes.  The value
@@ -36598,23 +37461,39 @@ atof_return:
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3Int64ToText(i64 v, char *zOut){
   int i;
   u64 x;
-  char zTemp[22];
-  if( v<0 ){
-    x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v;
-  }else{
+  union {
+    char a[SQLITE_U64_DIGITS+1];
+    u16 forceAlignment;
+  } u;
+  if( v>0 ){
     x = v;
+  }else if( v==0 ){
+    zOut[0] = '0';
+    zOut[1] = 0;
+    return 1;
+  }else{
+    x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v;
   }
-  i = sizeof(zTemp)-2;
-  zTemp[sizeof(zTemp)-1] = 0;
-  while( 1 /*exit-by-break*/ ){
-    zTemp[i] = (x%10) + '0';
-    x = x/10;
-    if( x==0 ) break;
-    i--;
-  };
-  if( v<0 ) zTemp[--i] = '-';
-  memcpy(zOut, &zTemp[i], sizeof(zTemp)-i);
-  return sizeof(zTemp)-1-i;
+#ifdef SQLITE_AVOID_U64_DIVIDE
+  i = sqlite3UInt64ToText(x, u.a);
+#else
+  i = sizeof(u.a)-1;
+  u.a[i] = 0;
+  while( x>=10 ){
+    int kk = (x%100)*2;
+    assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[kk]) );
+    assert( TWO_BYTE_ALIGNMENT(&u.a[i-2]) );
+    *(u16*)(&u.a[i-2]) = *(u16*)&sqlite3DigitPairs.a[kk];
+    i -= 2;
+    x /= 100;
+  }
+  if( x ){
+    u.a[--i] = x + '0';
+  }
+#endif /* SQLITE_AVOID_U64_DIVIDE */
+  if( v<0 ) u.a[--i] = '-';
+  memcpy(zOut, &u.a[i], sizeof(u.a)-i);
+  return sizeof(u.a)-1-i;
 }
 
 /*
@@ -36668,8 +37547,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Atoi64(const char *zNum, i64 *pNum, int
   int incr;
   u64 u = 0;
   int neg = 0; /* assume positive */
-  int i;
-  int c = 0;
+  int i, j;
+  unsigned int c = 0;
   int nonNum = 0;  /* True if input contains UTF16 with high byte non-zero */
   int rc;          /* Baseline return code */
   const char *zStart;
@@ -36697,8 +37576,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Atoi64(const char *zNum, i64 *pNum, int
   }
   zStart = zNum;
   while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */
-  for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
-    u = u*10 + c - '0';
+  for(i=0; &zNum[i]<zEnd && (c=(unsigned)zNum[i]-'0')<=9; i+=incr){
+    u = u*10 + c;
   }
   testcase( i==18*incr );
   testcase( i==19*incr );
@@ -36735,14 +37614,14 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Atoi64(const char *zNum, i64 *pNum, int
     return rc;
   }else{
     /* zNum is a 19-digit numbers.  Compare it against 9223372036854775808. */
-    c = i>19*incr ? 1 : compare2pow63(zNum, incr);
-    if( c<0 ){
+    j = i>19*incr ? 1 : compare2pow63(zNum, incr);
+    if( j<0 ){
       /* zNum is less than 9223372036854775808 so it fits */
       assert( u<=LARGEST_INT64 );
       return rc;
     }else{
       *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
-      if( c>0 ){
+      if( j>0 ){
         /* zNum is greater than 9223372036854775808 so it overflows */
         return 2;
       }else{
@@ -36871,7 +37750,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Atoi(const char *z){
 ** representation.
 **
 ** If iRound<=0 then round to -iRound significant digits to the
-** the left of the decimal point, or to a maximum of mxRound total
+** the right of the decimal point, or to a maximum of mxRound total
 ** significant digits.
 **
 ** If iRound>0 round to min(iRound,mxRound) significant digits total.
@@ -36884,13 +37763,14 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Atoi(const char *z){
 ** The p->z[] array is *not* zero-terminated.
 */
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
-  int i;
-  u64 v;
-  int e, exp = 0;
-  double rr[2];
+  int i;               /* Index into zBuf[] where to put next character */
+  int n;               /* Number of digits */
+  u64 v;               /* mantissa */
+  int e, exp = 0;      /* Base-2 and base-10 exponent */
+  char *zBuf;          /* Local alias for p->zBuf */
+  char *z;             /* Local alias for p->z */
 
   p->isSpecial = 0;
-  p->z = p->zBuf;
   assert( mxRound>0 );
 
   /* Convert negative numbers to positive.  Deal with Infinity, 0.0, and
@@ -36908,78 +37788,100 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3FpDecode(FpDecode *p, double r, int iR
     p->sign = '+';
   }
   memcpy(&v,&r,8);
-  e = v>>52;
-  if( (e&0x7ff)==0x7ff ){
+  e = (v>>52)&0x7ff;
+  if( e==0x7ff ){
     p->isSpecial = 1 + (v!=0x7ff0000000000000LL);
     p->n = 0;
     p->iDP = 0;
+    p->z = p->zBuf;
     return;
   }
-
-  /* Multiply r by powers of ten until it lands somewhere in between
-  ** 1.0e+19 and 1.0e+17.
-  **
-  ** Use Dekker-style double-double computation to increase the
-  ** precision.
-  **
-  ** The error terms on constants like 1.0e+100 computed using the
-  ** decimal extension, for example as follows:
-  **
-  **   SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100)));
-  */
-  rr[0] = r;
-  rr[1] = 0.0;
-  if( rr[0]>9.223372036854774784e+18 ){
-    while( rr[0]>9.223372036854774784e+118 ){
-      exp += 100;
-      dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
-    }
-    while( rr[0]>9.223372036854774784e+28 ){
-      exp += 10;
-      dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
-    }
-    while( rr[0]>9.223372036854774784e+18 ){
-      exp += 1;
-      dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
-    }
+  v &= 0x000fffffffffffffULL;
+  if( e==0 ){
+    int nn = countLeadingZeros(v);
+    v <<= nn;
+    e = -1074 - nn;
   }else{
-    while( rr[0]<9.223372036854774784e-83  ){
-      exp -= 100;
-      dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
-    }
-    while( rr[0]<9.223372036854774784e+07  ){
-      exp -= 10;
-      dekkerMul2(rr, 1.0e+10, 0.0);
-    }
-    while( rr[0]<9.22337203685477478e+17  ){
-      exp -= 1;
-      dekkerMul2(rr, 1.0e+01, 0.0);
-    }
+    v = (v<<11) | U64_BIT(63);
+    e -= 1086;
   }
-  v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1];
+  sqlite3Fp2Convert10(v, e, (iRound<=0||iRound>=18)?18:iRound+1, &v, &exp);
 
-  /* Extract significant digits. */
-  i = sizeof(p->zBuf)-1;
+  /* Extract significant digits, start at the right-most slot in p->zBuf
+  ** and working back to the right.  "i" keeps track of the next slot in
+  ** which to store a digit. */
+  assert( sizeof(p->zBuf)==SQLITE_U64_DIGITS+1 );
   assert( v>0 );
-  while( v ){  p->zBuf[i--] = (v%10) + '0'; v /= 10; }
-  assert( i>=0 && i<sizeof(p->zBuf)-1 );
-  p->n = sizeof(p->zBuf) - 1 - i;
-  assert( p->n>0 );
-  assert( p->n<sizeof(p->zBuf) );
-  p->iDP = p->n + exp;
+  zBuf = p->zBuf;
+#ifdef SQLITE_AVOID_U64_DIVIDE
+  i = sqlite3UInt64ToText(v, zBuf);
+#else
+  i = SQLITE_U64_DIGITS;
+  while( v>=10 ){
+    int kk = (v%100)*2;
+    assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[kk]) );
+    assert( TWO_BYTE_ALIGNMENT(&zBuf[i]) );
+    assert( i-2>=0 );
+    *(u16*)(&zBuf[i-2]) = *(u16*)&sqlite3DigitPairs.a[kk];
+    i -= 2;
+    v /= 100;
+  }
+  if( v ){
+    assert( v<10 );
+    assert( i>0 );
+    zBuf[--i] = v + '0';
+  }
+#endif /* SQLITE_AVOID_U64_DIVIDE */
+  assert( i>=0 && i<SQLITE_U64_DIGITS );
+  n = SQLITE_U64_DIGITS - i;  /* Total number of digits extracted */
+  assert( n>0 );
+  assert( n<=SQLITE_U64_DIGITS );
+  p->iDP = n + exp;
   if( iRound<=0 ){
     iRound = p->iDP - iRound;
-    if( iRound==0 && p->zBuf[i+1]>='5' ){
+    if( iRound==0 && zBuf[i]>='5' ){
       iRound = 1;
-      p->zBuf[i--] = '0';
-      p->n++;
+      zBuf[--i] = '0';
+      n++;
       p->iDP++;
     }
   }
-  if( iRound>0 && (iRound<p->n || p->n>mxRound) ){
-    char *z = &p->zBuf[i+1];
+  z = &zBuf[i];  /* z points to the first digit */
+  if( iRound>0 && (iRound<n || n>mxRound) ){
     if( iRound>mxRound ) iRound = mxRound;
-    p->n = iRound;
+    if( iRound==17 ){
+      /* If the precision is exactly 17, which only happens with the "!"
+      ** flag (ex: "%!.17g") then try to reduce the precision if that
+      ** yields text that will round-trip to the original floating-point.
+      ** value.  Thus, for exaple, 49.47 will render as 49.47, rather than
+      ** as 49.469999999999999. */
+      if( z[15]=='9' && z[14]=='9' ){
+        int jj, kk;
+        u64 v2;
+        for(jj=14; jj>0 && z[jj-1]=='9'; jj--){}
+        if( jj==0 ){
+          v2 = 1;
+        }else{
+          v2 = z[0] - '0';
+          for(kk=1; kk<jj; kk++) v2 = (v2*10) + z[kk] - '0';
+          v2++;
+        }
+        if( r==sqlite3Fp10Convert2(v2, exp + n - jj) ){
+          iRound = jj+1;
+        }
+      }else if( p->iDP>=n || (z[15]=='0' && z[14]=='0' && z[13]=='0') ){
+        int jj, kk;
+        u64 v2;
+        assert( z[0]!='0' );
+        for(jj=13; z[jj-1]=='0'; jj--){}
+        v2 = z[0] - '0';
+        for(kk=1; kk<jj; kk++) v2 = (v2*10) + z[kk] - '0';
+        if( r==sqlite3Fp10Convert2(v2, exp + n - jj) ){
+          iRound = jj+1;
+        }
+      }
+    }
+    n = iRound;
     if( z[iRound]>='5' ){
       int j = iRound-1;
       while( 1 /*exit-by-break*/ ){
@@ -36987,8 +37889,9 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3FpDecode(FpDecode *p, double r, int iR
         if( z[j]<='9' ) break;
         z[j] = '0';
         if( j==0 ){
-          p->z[i--] = '1';
-          p->n++;
+          z--;
+          z[0] = '1';
+          n++;
           p->iDP++;
           break;
         }else{
@@ -36997,13 +37900,13 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3FpDecode(FpDecode *p, double r, int iR
       }
     }
   }
-  p->z = &p->zBuf[i+1];
-  assert( i+p->n < sizeof(p->zBuf) );
-  assert( p->n>0 );
-  while( p->z[p->n-1]=='0' ){
-    p->n--;
-    assert( p->n>0 );
+  assert( n>0 );
+  while( z[n-1]=='0' ){
+    n--;
+    assert( n>0 );
   }
+  p->n = n;
+  p->z = z;
 }
 
 /*
@@ -38068,10 +38971,10 @@ SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3OpcodeName(int i){
     /*  44 */ "And"              OpHelp("r[P3]=(r[P1] && r[P2])"),
     /*  45 */ "IdxLT"            OpHelp("key=r[P3@P4]"),
     /*  46 */ "IdxGE"            OpHelp("key=r[P3@P4]"),
-    /*  47 */ "RowSetRead"       OpHelp("r[P3]=rowset(P1)"),
-    /*  48 */ "RowSetTest"       OpHelp("if r[P3] in rowset(P1) goto P2"),
-    /*  49 */ "Program"          OpHelp(""),
-    /*  50 */ "FkIfZero"         OpHelp("if fkctr[P1]==0 goto P2"),
+    /*  47 */ "IFindKey"         OpHelp(""),
+    /*  48 */ "RowSetRead"       OpHelp("r[P3]=rowset(P1)"),
+    /*  49 */ "RowSetTest"       OpHelp("if r[P3] in rowset(P1) goto P2"),
+    /*  50 */ "Program"          OpHelp(""),
     /*  51 */ "IsNull"           OpHelp("if r[P1]==NULL goto P2"),
     /*  52 */ "NotNull"          OpHelp("if r[P1]!=NULL goto P2"),
     /*  53 */ "Ne"               OpHelp("IF r[P3]!=r[P1]"),
@@ -38081,49 +38984,49 @@ SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3OpcodeName(int i){
     /*  57 */ "Lt"               OpHelp("IF r[P3]<r[P1]"),
     /*  58 */ "Ge"               OpHelp("IF r[P3]>=r[P1]"),
     /*  59 */ "ElseEq"           OpHelp(""),
-    /*  60 */ "IfPos"            OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
-    /*  61 */ "IfNotZero"        OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
-    /*  62 */ "DecrJumpZero"     OpHelp("if (--r[P1])==0 goto P2"),
-    /*  63 */ "IncrVacuum"       OpHelp(""),
-    /*  64 */ "VNext"            OpHelp(""),
-    /*  65 */ "Filter"           OpHelp("if key(P3@P4) not in filter(P1) goto P2"),
-    /*  66 */ "PureFunc"         OpHelp("r[P3]=func(r[P2@NP])"),
-    /*  67 */ "Function"         OpHelp("r[P3]=func(r[P2@NP])"),
-    /*  68 */ "Return"           OpHelp(""),
-    /*  69 */ "EndCoroutine"     OpHelp(""),
-    /*  70 */ "HaltIfNull"       OpHelp("if r[P3]=null halt"),
-    /*  71 */ "Halt"             OpHelp(""),
-    /*  72 */ "Integer"          OpHelp("r[P2]=P1"),
-    /*  73 */ "Int64"            OpHelp("r[P2]=P4"),
-    /*  74 */ "String"           OpHelp("r[P2]='P4' (len=P1)"),
-    /*  75 */ "BeginSubrtn"      OpHelp("r[P2]=NULL"),
-    /*  76 */ "Null"             OpHelp("r[P2..P3]=NULL"),
-    /*  77 */ "SoftNull"         OpHelp("r[P1]=NULL"),
-    /*  78 */ "Blob"             OpHelp("r[P2]=P4 (len=P1)"),
-    /*  79 */ "Variable"         OpHelp("r[P2]=parameter(P1)"),
-    /*  80 */ "Move"             OpHelp("r[P2@P3]=r[P1@P3]"),
-    /*  81 */ "Copy"             OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
-    /*  82 */ "SCopy"            OpHelp("r[P2]=r[P1]"),
-    /*  83 */ "IntCopy"          OpHelp("r[P2]=r[P1]"),
-    /*  84 */ "FkCheck"          OpHelp(""),
-    /*  85 */ "ResultRow"        OpHelp("output=r[P1@P2]"),
-    /*  86 */ "CollSeq"          OpHelp(""),
-    /*  87 */ "AddImm"           OpHelp("r[P1]=r[P1]+P2"),
-    /*  88 */ "RealAffinity"     OpHelp(""),
-    /*  89 */ "Cast"             OpHelp("affinity(r[P1])"),
-    /*  90 */ "Permutation"      OpHelp(""),
-    /*  91 */ "Compare"          OpHelp("r[P1@P3] <-> r[P2@P3]"),
-    /*  92 */ "IsTrue"           OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"),
-    /*  93 */ "ZeroOrNull"       OpHelp("r[P2] = 0 OR NULL"),
-    /*  94 */ "Offset"           OpHelp("r[P3] = sqlite_offset(P1)"),
-    /*  95 */ "Column"           OpHelp("r[P3]=PX cursor P1 column P2"),
-    /*  96 */ "TypeCheck"        OpHelp("typecheck(r[P1@P2])"),
-    /*  97 */ "Affinity"         OpHelp("affinity(r[P1@P2])"),
-    /*  98 */ "MakeRecord"       OpHelp("r[P3]=mkrec(r[P1@P2])"),
-    /*  99 */ "Count"            OpHelp("r[P2]=count()"),
-    /* 100 */ "ReadCookie"       OpHelp(""),
-    /* 101 */ "SetCookie"        OpHelp(""),
-    /* 102 */ "ReopenIdx"        OpHelp("root=P2 iDb=P3"),
+    /*  60 */ "FkIfZero"         OpHelp("if fkctr[P1]==0 goto P2"),
+    /*  61 */ "IfPos"            OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+    /*  62 */ "IfNotZero"        OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
+    /*  63 */ "DecrJumpZero"     OpHelp("if (--r[P1])==0 goto P2"),
+    /*  64 */ "IncrVacuum"       OpHelp(""),
+    /*  65 */ "VNext"            OpHelp(""),
+    /*  66 */ "Filter"           OpHelp("if key(P3@P4) not in filter(P1) goto P2"),
+    /*  67 */ "PureFunc"         OpHelp("r[P3]=func(r[P2@NP])"),
+    /*  68 */ "Function"         OpHelp("r[P3]=func(r[P2@NP])"),
+    /*  69 */ "Return"           OpHelp(""),
+    /*  70 */ "EndCoroutine"     OpHelp(""),
+    /*  71 */ "HaltIfNull"       OpHelp("if r[P3]=null halt"),
+    /*  72 */ "Halt"             OpHelp(""),
+    /*  73 */ "Integer"          OpHelp("r[P2]=P1"),
+    /*  74 */ "Int64"            OpHelp("r[P2]=P4"),
+    /*  75 */ "String"           OpHelp("r[P2]='P4' (len=P1)"),
+    /*  76 */ "BeginSubrtn"      OpHelp("r[P2]=NULL"),
+    /*  77 */ "Null"             OpHelp("r[P2..P3]=NULL"),
+    /*  78 */ "SoftNull"         OpHelp("r[P1]=NULL"),
+    /*  79 */ "Blob"             OpHelp("r[P2]=P4 (len=P1)"),
+    /*  80 */ "Variable"         OpHelp("r[P2]=parameter(P1)"),
+    /*  81 */ "Move"             OpHelp("r[P2@P3]=r[P1@P3]"),
+    /*  82 */ "Copy"             OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
+    /*  83 */ "SCopy"            OpHelp("r[P2]=r[P1]"),
+    /*  84 */ "IntCopy"          OpHelp("r[P2]=r[P1]"),
+    /*  85 */ "FkCheck"          OpHelp(""),
+    /*  86 */ "ResultRow"        OpHelp("output=r[P1@P2]"),
+    /*  87 */ "CollSeq"          OpHelp(""),
+    /*  88 */ "AddImm"           OpHelp("r[P1]=r[P1]+P2"),
+    /*  89 */ "RealAffinity"     OpHelp(""),
+    /*  90 */ "Cast"             OpHelp("affinity(r[P1])"),
+    /*  91 */ "Permutation"      OpHelp(""),
+    /*  92 */ "Compare"          OpHelp("r[P1@P3] <-> r[P2@P3]"),
+    /*  93 */ "IsTrue"           OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"),
+    /*  94 */ "ZeroOrNull"       OpHelp("r[P2] = 0 OR NULL"),
+    /*  95 */ "Offset"           OpHelp("r[P3] = sqlite_offset(P1)"),
+    /*  96 */ "Column"           OpHelp("r[P3]=PX cursor P1 column P2"),
+    /*  97 */ "TypeCheck"        OpHelp("typecheck(r[P1@P2])"),
+    /*  98 */ "Affinity"         OpHelp("affinity(r[P1@P2])"),
+    /*  99 */ "MakeRecord"       OpHelp("r[P3]=mkrec(r[P1@P2])"),
+    /* 100 */ "Count"            OpHelp("r[P2]=count()"),
+    /* 101 */ "ReadCookie"       OpHelp(""),
+    /* 102 */ "SetCookie"        OpHelp(""),
     /* 103 */ "BitAnd"           OpHelp("r[P3]=r[P1]&r[P2]"),
     /* 104 */ "BitOr"            OpHelp("r[P3]=r[P1]|r[P2]"),
     /* 105 */ "ShiftLeft"        OpHelp("r[P3]=r[P2]<<r[P1]"),
@@ -38134,84 +39037,85 @@ SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3OpcodeName(int i){
     /* 110 */ "Divide"           OpHelp("r[P3]=r[P2]/r[P1]"),
     /* 111 */ "Remainder"        OpHelp("r[P3]=r[P2]%r[P1]"),
     /* 112 */ "Concat"           OpHelp("r[P3]=r[P2]+r[P1]"),
-    /* 113 */ "OpenRead"         OpHelp("root=P2 iDb=P3"),
-    /* 114 */ "OpenWrite"        OpHelp("root=P2 iDb=P3"),
+    /* 113 */ "ReopenIdx"        OpHelp("root=P2 iDb=P3"),
+    /* 114 */ "OpenRead"         OpHelp("root=P2 iDb=P3"),
     /* 115 */ "BitNot"           OpHelp("r[P2]= ~r[P1]"),
-    /* 116 */ "OpenDup"          OpHelp(""),
-    /* 117 */ "OpenAutoindex"    OpHelp("nColumn=P2"),
+    /* 116 */ "OpenWrite"        OpHelp("root=P2 iDb=P3"),
+    /* 117 */ "OpenDup"          OpHelp(""),
     /* 118 */ "String8"          OpHelp("r[P2]='P4'"),
-    /* 119 */ "OpenEphemeral"    OpHelp("nColumn=P2"),
-    /* 120 */ "SorterOpen"       OpHelp(""),
-    /* 121 */ "SequenceTest"     OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
-    /* 122 */ "OpenPseudo"       OpHelp("P3 columns in r[P2]"),
-    /* 123 */ "Close"            OpHelp(""),
-    /* 124 */ "ColumnsUsed"      OpHelp(""),
-    /* 125 */ "SeekScan"         OpHelp("Scan-ahead up to P1 rows"),
-    /* 126 */ "SeekHit"          OpHelp("set P2<=seekHit<=P3"),
-    /* 127 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
-    /* 128 */ "NewRowid"         OpHelp("r[P2]=rowid"),
-    /* 129 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),
-    /* 130 */ "RowCell"          OpHelp(""),
-    /* 131 */ "Delete"           OpHelp(""),
-    /* 132 */ "ResetCount"       OpHelp(""),
-    /* 133 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
-    /* 134 */ "SorterData"       OpHelp("r[P2]=data"),
-    /* 135 */ "RowData"          OpHelp("r[P2]=data"),
-    /* 136 */ "Rowid"            OpHelp("r[P2]=PX rowid of P1"),
-    /* 137 */ "NullRow"          OpHelp(""),
-    /* 138 */ "SeekEnd"          OpHelp(""),
-    /* 139 */ "IdxInsert"        OpHelp("key=r[P2]"),
-    /* 140 */ "SorterInsert"     OpHelp("key=r[P2]"),
-    /* 141 */ "IdxDelete"        OpHelp("key=r[P2@P3]"),
-    /* 142 */ "DeferredSeek"     OpHelp("Move P3 to P1.rowid if needed"),
-    /* 143 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
-    /* 144 */ "FinishSeek"       OpHelp(""),
-    /* 145 */ "Destroy"          OpHelp(""),
-    /* 146 */ "Clear"            OpHelp(""),
-    /* 147 */ "ResetSorter"      OpHelp(""),
-    /* 148 */ "CreateBtree"      OpHelp("r[P2]=root iDb=P1 flags=P3"),
-    /* 149 */ "SqlExec"          OpHelp(""),
-    /* 150 */ "ParseSchema"      OpHelp(""),
-    /* 151 */ "LoadAnalysis"     OpHelp(""),
-    /* 152 */ "DropTable"        OpHelp(""),
-    /* 153 */ "DropIndex"        OpHelp(""),
+    /* 119 */ "OpenAutoindex"    OpHelp("nColumn=P2"),
+    /* 120 */ "OpenEphemeral"    OpHelp("nColumn=P2"),
+    /* 121 */ "SorterOpen"       OpHelp(""),
+    /* 122 */ "SequenceTest"     OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
+    /* 123 */ "OpenPseudo"       OpHelp("P3 columns in r[P2]"),
+    /* 124 */ "Close"            OpHelp(""),
+    /* 125 */ "ColumnsUsed"      OpHelp(""),
+    /* 126 */ "SeekScan"         OpHelp("Scan-ahead up to P1 rows"),
+    /* 127 */ "SeekHit"          OpHelp("set P2<=seekHit<=P3"),
+    /* 128 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
+    /* 129 */ "NewRowid"         OpHelp("r[P2]=rowid"),
+    /* 130 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),
+    /* 131 */ "RowCell"          OpHelp(""),
+    /* 132 */ "Delete"           OpHelp(""),
+    /* 133 */ "ResetCount"       OpHelp(""),
+    /* 134 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+    /* 135 */ "SorterData"       OpHelp("r[P2]=data"),
+    /* 136 */ "RowData"          OpHelp("r[P2]=data"),
+    /* 137 */ "Rowid"            OpHelp("r[P2]=PX rowid of P1"),
+    /* 138 */ "NullRow"          OpHelp(""),
+    /* 139 */ "SeekEnd"          OpHelp(""),
+    /* 140 */ "IdxInsert"        OpHelp("key=r[P2]"),
+    /* 141 */ "SorterInsert"     OpHelp("key=r[P2]"),
+    /* 142 */ "IdxDelete"        OpHelp("key=r[P2@P3]"),
+    /* 143 */ "DeferredSeek"     OpHelp("Move P3 to P1.rowid if needed"),
+    /* 144 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
+    /* 145 */ "FinishSeek"       OpHelp(""),
+    /* 146 */ "Destroy"          OpHelp(""),
+    /* 147 */ "Clear"            OpHelp(""),
+    /* 148 */ "ResetSorter"      OpHelp(""),
+    /* 149 */ "CreateBtree"      OpHelp("r[P2]=root iDb=P1 flags=P3"),
+    /* 150 */ "SqlExec"          OpHelp(""),
+    /* 151 */ "ParseSchema"      OpHelp(""),
+    /* 152 */ "LoadAnalysis"     OpHelp(""),
+    /* 153 */ "DropTable"        OpHelp(""),
     /* 154 */ "Real"             OpHelp("r[P2]=P4"),
-    /* 155 */ "DropTrigger"      OpHelp(""),
-    /* 156 */ "IntegrityCk"      OpHelp(""),
-    /* 157 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
-    /* 158 */ "Param"            OpHelp(""),
-    /* 159 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
-    /* 160 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
-    /* 161 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
-    /* 162 */ "AggInverse"       OpHelp("accum=r[P3] inverse(r[P2@P5])"),
-    /* 163 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2@P5])"),
-    /* 164 */ "AggStep1"         OpHelp("accum=r[P3] step(r[P2@P5])"),
-    /* 165 */ "AggValue"         OpHelp("r[P3]=value N=P2"),
-    /* 166 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
-    /* 167 */ "Expire"           OpHelp(""),
-    /* 168 */ "CursorLock"       OpHelp(""),
-    /* 169 */ "CursorUnlock"     OpHelp(""),
-    /* 170 */ "TableLock"        OpHelp("iDb=P1 root=P2 write=P3"),
-    /* 171 */ "VBegin"           OpHelp(""),
-    /* 172 */ "VCreate"          OpHelp(""),
-    /* 173 */ "VDestroy"         OpHelp(""),
-    /* 174 */ "VOpen"            OpHelp(""),
-    /* 175 */ "VCheck"           OpHelp(""),
-    /* 176 */ "VInitIn"          OpHelp("r[P2]=ValueList(P1,P3)"),
-    /* 177 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
-    /* 178 */ "VRename"          OpHelp(""),
-    /* 179 */ "Pagecount"        OpHelp(""),
-    /* 180 */ "MaxPgcnt"         OpHelp(""),
-    /* 181 */ "ClrSubtype"       OpHelp("r[P1].subtype = 0"),
-    /* 182 */ "GetSubtype"       OpHelp("r[P2] = r[P1].subtype"),
-    /* 183 */ "SetSubtype"       OpHelp("r[P2].subtype = r[P1]"),
-    /* 184 */ "FilterAdd"        OpHelp("filter(P1) += key(P3@P4)"),
-    /* 185 */ "Trace"            OpHelp(""),
-    /* 186 */ "CursorHint"       OpHelp(""),
-    /* 187 */ "ReleaseReg"       OpHelp("release r[P1@P2] mask P3"),
-    /* 188 */ "Noop"             OpHelp(""),
-    /* 189 */ "Explain"          OpHelp(""),
-    /* 190 */ "Abortable"        OpHelp(""),
+    /* 155 */ "DropIndex"        OpHelp(""),
+    /* 156 */ "DropTrigger"      OpHelp(""),
+    /* 157 */ "IntegrityCk"      OpHelp(""),
+    /* 158 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
+    /* 159 */ "Param"            OpHelp(""),
+    /* 160 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
+    /* 161 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
+    /* 162 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+    /* 163 */ "AggInverse"       OpHelp("accum=r[P3] inverse(r[P2@P5])"),
+    /* 164 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2@P5])"),
+    /* 165 */ "AggStep1"         OpHelp("accum=r[P3] step(r[P2@P5])"),
+    /* 166 */ "AggValue"         OpHelp("r[P3]=value N=P2"),
+    /* 167 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
+    /* 168 */ "Expire"           OpHelp(""),
+    /* 169 */ "CursorLock"       OpHelp(""),
+    /* 170 */ "CursorUnlock"     OpHelp(""),
+    /* 171 */ "TableLock"        OpHelp("iDb=P1 root=P2 write=P3"),
+    /* 172 */ "VBegin"           OpHelp(""),
+    /* 173 */ "VCreate"          OpHelp(""),
+    /* 174 */ "VDestroy"         OpHelp(""),
+    /* 175 */ "VOpen"            OpHelp(""),
+    /* 176 */ "VCheck"           OpHelp(""),
+    /* 177 */ "VInitIn"          OpHelp("r[P2]=ValueList(P1,P3)"),
+    /* 178 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
+    /* 179 */ "VRename"          OpHelp(""),
+    /* 180 */ "Pagecount"        OpHelp(""),
+    /* 181 */ "MaxPgcnt"         OpHelp(""),
+    /* 182 */ "ClrSubtype"       OpHelp("r[P1].subtype = 0"),
+    /* 183 */ "GetSubtype"       OpHelp("r[P2] = r[P1].subtype"),
+    /* 184 */ "SetSubtype"       OpHelp("r[P2].subtype = r[P1]"),
+    /* 185 */ "FilterAdd"        OpHelp("filter(P1) += key(P3@P4)"),
+    /* 186 */ "Trace"            OpHelp(""),
+    /* 187 */ "CursorHint"       OpHelp(""),
+    /* 188 */ "ReleaseReg"       OpHelp("release r[P1@P2] mask P3"),
+    /* 189 */ "Noop"             OpHelp(""),
+    /* 190 */ "Explain"          OpHelp(""),
+    /* 191 */ "Abortable"        OpHelp(""),
   };
   return azName[i];
 }
@@ -38242,7 +39146,7 @@ SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3OpcodeName(int i){
 ** Debugging logic
 */
 
-/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */
+/* SQLITE_KV_TRACE() is used for tracing calls to kvrecord routines. */
 #if 0
 #define SQLITE_KV_TRACE(X)  printf X
 #else
@@ -38256,7 +39160,6 @@ SQLITE_PRIVATE const char *SQLITE_APICALL sqlite3OpcodeName(int i){
 #define SQLITE_KV_LOG(X)
 #endif
 
-
 /*
 ** Forward declaration of objects used by this VFS implementation
 */
@@ -38264,6 +39167,11 @@ typedef struct KVVfsFile KVVfsFile;
 
 /* A single open file.  There are only two files represented by this
 ** VFS - the database and the rollback journal.
+**
+** Maintenance reminder: if this struct changes in any way, the JSON
+** rendering of its structure must be updated in
+** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary
+** compatibility concerns, so it does not need an iVersion member.
 */
 struct KVVfsFile {
   sqlite3_file base;              /* IO methods */
@@ -38313,7 +39221,7 @@ static int kvvfsCurrentTime(sqlite3_vfs*, double*);
 static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
 
 static sqlite3_vfs sqlite3OsKvvfsObject = {
-  1,                              /* iVersion */
+  2,                              /* iVersion */
   sizeof(KVVfsFile),              /* szOsFile */
   1024,                           /* mxPathname */
   0,                              /* pNext */
@@ -38389,23 +39297,37 @@ static sqlite3_io_methods kvvfs_jrnl_io_methods = {
 
 /* Forward declarations for the low-level storage engine
 */
-static int kvstorageWrite(const char*, const char *zKey, const char *zData);
-static int kvstorageDelete(const char*, const char *zKey);
-static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
-#define KVSTORAGE_KEY_SZ  32
+#ifndef SQLITE_WASM
+/* In WASM builds these are implemented in JS. */
+static int kvrecordWrite(const char*, const char *zKey, const char *zData);
+static int kvrecordDelete(const char*, const char *zKey);
+static int kvrecordRead(const char*, const char *zKey, char *zBuf, int nBuf);
+#endif
+#ifndef KVRECORD_KEY_SZ
+#define KVRECORD_KEY_SZ 32
+#endif
 
 /* Expand the key name with an appropriate prefix and put the result
 ** in zKeyOut[].  The zKeyOut[] buffer is assumed to hold at least
-** KVSTORAGE_KEY_SZ bytes.
+** KVRECORD_KEY_SZ bytes.
 */
-static void kvstorageMakeKey(
+static void kvrecordMakeKey(
   const char *zClass,
   const char *zKeyIn,
   char *zKeyOut
 ){
-  sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn);
+  assert( zKeyIn );
+  assert( zKeyOut );
+  assert( zClass );
+  sqlite3_snprintf(KVRECORD_KEY_SZ, zKeyOut, "kvvfs-%s-%s",
+                   zClass, zKeyIn);
 }
 
+#ifndef SQLITE_WASM
+/* In WASM builds do not define APIs which use fopen(), fwrite(),
+** and the like because those APIs are a portability issue for
+** WASM.
+*/
 /* Write content into a key.  zClass is the particular namespace of the
 ** underlying key/value store to use - either "local" or "session".
 **
@@ -38413,14 +39335,14 @@ static void kvstorageMakeKey(
 **
 ** Return the number of errors.
 */
-static int kvstorageWrite(
+static int kvrecordWrite(
   const char *zClass,
   const char *zKey,
   const char *zData
 ){
   FILE *fd;
-  char zXKey[KVSTORAGE_KEY_SZ];
-  kvstorageMakeKey(zClass, zKey, zXKey);
+  char zXKey[KVRECORD_KEY_SZ];
+  kvrecordMakeKey(zClass, zKey, zXKey);
   fd = fopen(zXKey, "wb");
   if( fd ){
     SQLITE_KV_TRACE(("KVVFS-WRITE  %-15s (%d) %.50s%s\n", zXKey,
@@ -38438,9 +39360,9 @@ static int kvstorageWrite(
 ** namespace given by zClass.  If the key does not previously exist,
 ** this routine is a no-op.
 */
-static int kvstorageDelete(const char *zClass, const char *zKey){
-  char zXKey[KVSTORAGE_KEY_SZ];
-  kvstorageMakeKey(zClass, zKey, zXKey);
+static int kvrecordDelete(const char *zClass, const char *zKey){
+  char zXKey[KVRECORD_KEY_SZ];
+  kvrecordMakeKey(zClass, zKey, zXKey);
   unlink(zXKey);
   SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey));
   return 0;
@@ -38461,7 +39383,7 @@ static int kvstorageDelete(const char *zClass, const char *zKey){
 ** zero-terminates zBuf at zBuf[0] and returns the size of the data
 ** without reading it.
 */
-static int kvstorageRead(
+static int kvrecordRead(
   const char *zClass,
   const char *zKey,
   char *zBuf,
@@ -38469,8 +39391,8 @@ static int kvstorageRead(
 ){
   FILE *fd;
   struct stat buf;
-  char zXKey[KVSTORAGE_KEY_SZ];
-  kvstorageMakeKey(zClass, zKey, zXKey);
+  char zXKey[KVRECORD_KEY_SZ];
+  kvrecordMakeKey(zClass, zKey, zXKey);
   if( access(zXKey, R_OK)!=0
    || stat(zXKey, &buf)!=0
    || !S_ISREG(buf.st_mode)
@@ -38502,6 +39424,8 @@ static int kvstorageRead(
     return (int)n;
   }
 }
+#endif /* #ifndef SQLITE_WASM */
+
 
 /*
 ** An internal level of indirection which enables us to replace the
@@ -38509,17 +39433,27 @@ static int kvstorageRead(
 ** Maintenance reminder: if this struct changes in any way, the JSON
 ** rendering of its structure must be updated in
 ** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary
-** compatibility concerns, so it does not need an iVersion
-** member.
+** compatibility concerns, so it does not need an iVersion member.
 */
 typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
 struct sqlite3_kvvfs_methods {
-  int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
-  int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
-  int (*xDelete)(const char *zClass, const char *zKey);
+  int (*xRcrdRead)(const char*, const char *zKey, char *zBuf, int nBuf);
+  int (*xRcrdWrite)(const char*, const char *zKey, const char *zData);
+  int (*xRcrdDelete)(const char*, const char *zKey);
   const int nKeySize;
+  const int nBufferSize;
+#ifndef SQLITE_WASM
+#  define MAYBE_CONST const
+#else
+#  define MAYBE_CONST
+#endif
+  MAYBE_CONST sqlite3_vfs * pVfs;
+  MAYBE_CONST sqlite3_io_methods *pIoDb;
+  MAYBE_CONST sqlite3_io_methods *pIoJrnl;
+#undef MAYBE_CONST
 };
 
+
 /*
 ** This object holds the kvvfs I/O methods which may be swapped out
 ** for JavaScript-side implementations in WASM builds. In such builds
@@ -38534,10 +39468,20 @@ struct sqlite3_kvvfs_methods {
 const
 #endif
 SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
-kvstorageRead,
-kvstorageWrite,
-kvstorageDelete,
-KVSTORAGE_KEY_SZ
+#ifndef SQLITE_WASM
+  .xRcrdRead       = kvrecordRead,
+  .xRcrdWrite      = kvrecordWrite,
+  .xRcrdDelete     = kvrecordDelete,
+#else
+  .xRcrdRead       = 0,
+  .xRcrdWrite      = 0,
+  .xRcrdDelete     = 0,
+#endif
+  .nKeySize        = KVRECORD_KEY_SZ,
+  .nBufferSize     = SQLITE_KVOS_SZ,
+  .pVfs            = &sqlite3OsKvvfsObject,
+  .pIoDb           = &kvvfs_db_io_methods,
+  .pIoJrnl         = &kvvfs_jrnl_io_methods
 };
 
 /****** Utility subroutines ************************************************/
@@ -38564,7 +39508,10 @@ KVSTORAGE_KEY_SZ
 **      of hexadecimal and base-26 numbers, it is always clear where
 **      one stops and the next begins.
 */
-static int kvvfsEncode(const char *aData, int nData, char *aOut){
+#ifndef SQLITE_WASM
+static
+#endif
+int kvvfsEncode(const char *aData, int nData, char *aOut){
   int i, j;
   const unsigned char *a = (const unsigned char*)aData;
   for(i=j=0; i<nData; i++){
@@ -38615,9 +39562,13 @@ static const signed char kvvfsHexValue[256] = {
 ** Decode the text encoding back to binary.  The binary content is
 ** written into pOut, which must be at least nOut bytes in length.
 **
-** The return value is the number of bytes actually written into aOut[].
+** The return value is the number of bytes actually written into aOut[], or
+** -1 for malformed inputs.
 */
-static int kvvfsDecode(const char *a, char *aOut, int nOut){
+#ifndef SQLITE_WASM
+static
+#endif
+int kvvfsDecode(const char *a, char *aOut, int nOut){
   int i, j;
   int c;
   const unsigned char *aIn = (const unsigned char*)a;
@@ -38642,7 +39593,7 @@ static int kvvfsDecode(const char *a, char *aOut, int nOut){
     }else{
       aOut[j] = c<<4;
       c = kvvfsHexValue[aIn[++i]];
-      if( c<0 ) break;
+      if( c<0 ) return -1 /* hex bytes are always in pairs */;
       aOut[j++] += c;
       i++;
     }
@@ -38695,13 +39646,14 @@ static void kvvfsDecodeJournal(
 static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){
   char zData[50];
   zData[0] = 0;
-  sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1);
+  sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, "sz", zData,
+                                sizeof(zData)-1);
   return strtoll(zData, 0, 0);
 }
 static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){
   char zData[50];
   sqlite3_snprintf(sizeof(zData), zData, "%lld", sz);
-  return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData);
+  return sqlite3KvvfsMethods.xRcrdWrite(pFile->zClass, "sz", zData);
 }
 
 /****** sqlite3_io_methods methods ******************************************/
@@ -38716,6 +39668,9 @@ static int kvvfsClose(sqlite3_file *pProtoFile){
              pFile->isJournal ? "journal" : "db"));
   sqlite3_free(pFile->aJrnl);
   sqlite3_free(pFile->aData);
+#ifdef SQLITE_WASM
+  memset(pFile, 0, sizeof(*pFile));
+#endif
   return SQLITE_OK;
 }
 
@@ -38732,16 +39687,22 @@ static int kvvfsReadJrnl(
   assert( pFile->isJournal );
   SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
   if( pFile->aJrnl==0 ){
-    int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0);
+    int rc;
+    int szTxt = sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, "jrnl",
+                                              0, 0);
     char *aTxt;
     if( szTxt<=4 ){
       return SQLITE_IOERR;
     }
     aTxt = sqlite3_malloc64( szTxt+1 );
     if( aTxt==0 ) return SQLITE_NOMEM;
-    kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1);
-    kvvfsDecodeJournal(pFile, aTxt, szTxt);
+    rc = sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, "jrnl",
+                                       aTxt, szTxt+1);
+    if( rc>=0 ){
+      kvvfsDecodeJournal(pFile, aTxt, szTxt);
+    }
     sqlite3_free(aTxt);
+    if( rc ) return rc;
     if( pFile->aJrnl==0 ) return SQLITE_IOERR;
   }
   if( iOfst+iAmt>pFile->nJrnl ){
@@ -38781,8 +39742,8 @@ static int kvvfsReadDb(
     pgno = 1;
   }
   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
-  got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey,
-                                  aData, SQLITE_KVOS_SZ-1);
+  got = sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, zKey,
+                                      aData, SQLITE_KVOS_SZ-1);
   if( got<0 ){
     n = 0;
   }else{
@@ -38850,6 +39811,7 @@ static int kvvfsWriteDb(
   unsigned int pgno;
   char zKey[30];
   char *aData = pFile->aData;
+  int rc;
   SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
   assert( iAmt>=512 && iAmt<=65536 );
   assert( (iAmt & (iAmt-1))==0 );
@@ -38858,13 +39820,13 @@ static int kvvfsWriteDb(
   pgno = 1 + iOfst/iAmt;
   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
   kvvfsEncode(zBuf, iAmt, aData);
-  if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){
-    return SQLITE_IOERR;
-  }
-  if( iOfst+iAmt > pFile->szDb ){
-    pFile->szDb = iOfst + iAmt;
+  rc = sqlite3KvvfsMethods.xRcrdWrite(pFile->zClass, zKey, aData);
+  if( 0==rc ){
+    if( iOfst+iAmt > pFile->szDb ){
+      pFile->szDb = iOfst + iAmt;
+    }
   }
-  return SQLITE_OK;
+  return rc;
 }
 
 /*
@@ -38874,7 +39836,7 @@ static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){
   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
   SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size));
   assert( size==0 );
-  sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl");
+  sqlite3KvvfsMethods.xRcrdDelete(pFile->zClass, "jrnl");
   sqlite3_free(pFile->aJrnl);
   pFile->aJrnl = 0;
   pFile->nJrnl = 0;
@@ -38893,7 +39855,7 @@ static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){
     pgnoMax = 2 + pFile->szDb/pFile->szPage;
     while( pgno<=pgnoMax ){
       sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
-      sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey);
+      sqlite3KvvfsMethods.xRcrdDelete(pFile->zClass, zKey);
       pgno++;
     }
     pFile->szDb = size;
@@ -38925,7 +39887,7 @@ static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){
   }while( n>0 );
   zOut[i++] = ' ';
   kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]);
-  i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut);
+  i = sqlite3KvvfsMethods.xRcrdWrite(pFile->zClass, "jrnl", zOut);
   sqlite3_free(zOut);
   return i ? SQLITE_IOERR : SQLITE_OK;
 }
@@ -39039,33 +40001,32 @@ static int kvvfsOpen(
   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
   if( zName==0 ) zName = "";
   SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName));
-  if( strcmp(zName, "local")==0
-   || strcmp(zName, "session")==0
-  ){
-    pFile->isJournal = 0;
-    pFile->base.pMethods = &kvvfs_db_io_methods;
-  }else
-  if( strcmp(zName, "local-journal")==0
-   || strcmp(zName, "session-journal")==0
-  ){
+  assert(!pFile->zClass);
+  assert(!pFile->aData);
+  assert(!pFile->aJrnl);
+  assert(!pFile->nJrnl);
+  assert(!pFile->base.pMethods);
+  pFile->szPage = -1;
+  pFile->szDb = -1;
+  if( 0==sqlite3_strglob("*-journal", zName) ){
     pFile->isJournal = 1;
     pFile->base.pMethods = &kvvfs_jrnl_io_methods;
+    if( 0==strcmp("session-journal",zName) ){
+      pFile->zClass = "session";
+    }else if( 0==strcmp("local-journal",zName) ){
+      pFile->zClass = "local";
+    }
   }else{
-    return SQLITE_CANTOPEN;
+    pFile->isJournal = 0;
+    pFile->base.pMethods = &kvvfs_db_io_methods;
   }
-  if( zName[0]=='s' ){
-    pFile->zClass = "session";
-  }else{
-    pFile->zClass = "local";
+  if( !pFile->zClass ){
+    pFile->zClass = zName;
   }
   pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ);
   if( pFile->aData==0 ){
     return SQLITE_NOMEM;
   }
-  pFile->aJrnl = 0;
-  pFile->nJrnl = 0;
-  pFile->szPage = -1;
-  pFile->szDb = -1;
   return SQLITE_OK;
 }
 
@@ -39075,13 +40036,17 @@ static int kvvfsOpen(
 ** returning.
 */
 static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+  int rc /* The JS impl can fail with OOM in argument conversion */;
   if( strcmp(zPath, "local-journal")==0 ){
-    sqlite3KvvfsMethods.xDelete("local", "jrnl");
+    rc = sqlite3KvvfsMethods.xRcrdDelete("local", "jrnl");
   }else
   if( strcmp(zPath, "session-journal")==0 ){
-    sqlite3KvvfsMethods.xDelete("session", "jrnl");
+    rc = sqlite3KvvfsMethods.xRcrdDelete("session", "jrnl");
   }
-  return SQLITE_OK;
+  else{
+    rc = 0;
+  }
+  return rc;
 }
 
 /*
@@ -39095,21 +40060,42 @@ static int kvvfsAccess(
   int *pResOut
 ){
   SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath));
+#if 0 && defined(SQLITE_WASM)
+  /*
+  ** This is not having the desired effect in the JS bindings.
+  ** It's ostensibly the same logic as the #else block, but
+  ** it's not behaving that way.
+  **
+  ** In JS we map all zPaths to Storage objects, and -journal files
+  ** are mapped to the storage for the main db (which is is exactly
+  ** what the mapping of "local-journal" -> "local" is doing).
+  */
+  const char *zKey = (0==sqlite3_strglob("*-journal", zPath))
+    ? "jrnl" : "sz";
+  *pResOut =
+    sqlite3KvvfsMethods.xRcrdRead(zPath, zKey, 0, 0)>0;
+#else
   if( strcmp(zPath, "local-journal")==0 ){
-    *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0;
+    *pResOut =
+      sqlite3KvvfsMethods.xRcrdRead("local", "jrnl", 0, 0)>0;
   }else
   if( strcmp(zPath, "session-journal")==0 ){
-    *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0;
+    *pResOut =
+      sqlite3KvvfsMethods.xRcrdRead("session", "jrnl", 0, 0)>0;
   }else
   if( strcmp(zPath, "local")==0 ){
-    *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0;
+    *pResOut =
+      sqlite3KvvfsMethods.xRcrdRead("local", "sz", 0, 0)>0;
   }else
   if( strcmp(zPath, "session")==0 ){
-    *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0;
+    *pResOut =
+      sqlite3KvvfsMethods.xRcrdRead("session", "sz", 0, 0)>0;
   }else
   {
     *pResOut = 0;
   }
+  /*all current JS tests avoid triggering: assert( *pResOut == 0 ); */
+#endif
   SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut));
   return SQLITE_OK;
 }
@@ -41230,12 +42216,18 @@ static int unixLock(sqlite3_file *id, int eFileLock){
       pInode->nLock++;
       pInode->nShared = 1;
     }
-  }else if( (eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1)
-         || unixIsSharingShmNode(pFile)
-  ){
+  }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
     /* We are trying for an exclusive lock but another thread in this
     ** same process is still holding a shared lock. */
     rc = SQLITE_BUSY;
+  }else if( unixIsSharingShmNode(pFile) ){
+    /* We are in WAL mode and attempting to delete the SHM and WAL
+    ** files due to closing the connection or changing out of WAL mode,
+    ** but another process still holds locks on the SHM file, thus
+    ** indicating that database locks have been broken, perhaps due
+    ** to a rogue close(open(dbFile)) or similar.
+    */
+    rc = SQLITE_BUSY;
   }else{
     /* The request was for a RESERVED or EXCLUSIVE lock.  It is
     ** assumed that there is a SHARED or greater lock on the file
@@ -43874,26 +44866,21 @@ static int unixFcntlExternalReader(unixFile *pFile, int *piOut){
 ** still not a disaster.
 */
 static int unixIsSharingShmNode(unixFile *pFile){
-  int rc;
   unixShmNode *pShmNode;
+  struct flock lock;
   if( pFile->pShm==0 ) return 0;
   if( pFile->ctrlFlags & UNIXFILE_EXCL ) return 0;
   pShmNode = pFile->pShm->pShmNode;
-  rc = 1;
-  unixEnterMutex();
-  if( ALWAYS(pShmNode->nRef==1) ){
-    struct flock lock;
-    lock.l_whence = SEEK_SET;
-    lock.l_start = UNIX_SHM_DMS;
-    lock.l_len = 1;
-    lock.l_type = F_WRLCK;
-    osFcntl(pShmNode->hShm, F_GETLK, &lock);
-    if( lock.l_type==F_UNLCK ){
-      rc = 0;
-    }
-  }
-  unixLeaveMutex();
-  return rc;
+#if SQLITE_ATOMIC_INTRINSICS
+  assert( AtomicLoad(&pShmNode->nRef)==1 );
+#endif
+  memset(&lock, 0, sizeof(lock));
+  lock.l_whence = SEEK_SET;
+  lock.l_start = UNIX_SHM_DMS;
+  lock.l_len = 1;
+  lock.l_type = F_WRLCK;
+  osFcntl(pShmNode->hShm, F_GETLK, &lock);
+  return (lock.l_type!=F_UNLCK);
 }
 
 /*
@@ -44342,9 +45329,9 @@ static int unixShmMap(
   nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
 
   if( pShmNode->nRegion<nReqRegion ){
-    char **apNew;                      /* New apRegion[] array */
-    int nByte = nReqRegion*szRegion;   /* Minimum required file size */
-    struct stat sStat;                 /* Used by fstat() */
+    char **apNew;                         /* New apRegion[] array */
+    i64 nByte = nReqRegion*(i64)szRegion; /* Minimum required file size */
+    struct stat sStat;                    /* Used by fstat() */
 
     pShmNode->szRegion = szRegion;
 
@@ -44375,7 +45362,7 @@ static int unixShmMap(
         */
         else{
           static const int pgsz = 4096;
-          int iPg;
+          i64 iPg;
 
           /* Write to the last byte of each newly allocated or extended page */
           assert( (nByte % pgsz)==0 );
@@ -44392,7 +45379,7 @@ static int unixShmMap(
     }
 
     /* Map the requested memory region into this processes address space. */
-    apNew = (char **)sqlite3_realloc(
+    apNew = (char **)sqlite3_realloc64(
         pShmNode->apRegion, nReqRegion*sizeof(char *)
     );
     if( !apNew ){
@@ -44401,8 +45388,8 @@ static int unixShmMap(
     }
     pShmNode->apRegion = apNew;
     while( pShmNode->nRegion<nReqRegion ){
-      int nMap = szRegion*nShmPerMap;
-      int i;
+      i64 nMap = (i64)szRegion*(i64)nShmPerMap;
+      i64 i;
       void *pMem;
       if( pShmNode->hShm>=0 ){
         pMem = osMmap(0, nMap,
@@ -47837,7 +48824,7 @@ SQLITE_API int sqlite3_os_end(void){
 ** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
 ** based on the sub-platform)?
 */
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_ANSI)
 #  define SQLITE_WIN32_HAS_ANSI
 #endif
 
@@ -47845,7 +48832,7 @@ SQLITE_API int sqlite3_os_end(void){
 ** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
 ** based on the sub-platform)?
 */
-#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT) && \
     !defined(SQLITE_WIN32_NO_WIDE)
 #  define SQLITE_WIN32_HAS_WIDE
 #endif
@@ -47984,16 +48971,7 @@ SQLITE_API int sqlite3_os_end(void){
 */
 #if SQLITE_WIN32_FILEMAPPING_API && \
         (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
-/*
-** Two of the file mapping APIs are different under WinRT.  Figure out which
-** set we need.
-*/
-#if SQLITE_OS_WINRT
-WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \
-        LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR);
 
-WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T);
-#else
 #if defined(SQLITE_WIN32_HAS_ANSI)
 WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \
         DWORD, DWORD, DWORD, LPCSTR);
@@ -48005,7 +48983,6 @@ WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \
 #endif /* defined(SQLITE_WIN32_HAS_WIDE) */
 
 WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
-#endif /* SQLITE_OS_WINRT */
 
 /*
 ** These file mapping APIs are common to both Win32 and WinRT.
@@ -48296,7 +49273,7 @@ static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
 ** This function is not available on Windows CE or WinRT.
  */
 
-#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
+#if SQLITE_OS_WINCE
 #  define osAreFileApisANSI()       1
 #endif
 
@@ -48311,7 +49288,7 @@ static struct win_syscall {
   sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
   sqlite3_syscall_ptr pDefault; /* Default value */
 } aSyscall[] = {
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
   { "AreFileApisANSI",         (SYSCALL)AreFileApisANSI,         0 },
 #else
   { "AreFileApisANSI",         (SYSCALL)0,                       0 },
@@ -48350,7 +49327,7 @@ static struct win_syscall {
 #define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \
         LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if defined(SQLITE_WIN32_HAS_WIDE)
   { "CreateFileW",             (SYSCALL)CreateFileW,             0 },
 #else
   { "CreateFileW",             (SYSCALL)0,                       0 },
@@ -48359,7 +49336,7 @@ static struct win_syscall {
 #define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
         LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
+#if defined(SQLITE_WIN32_HAS_ANSI) && \
         (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \
         SQLITE_WIN32_CREATEFILEMAPPINGA
   { "CreateFileMappingA",      (SYSCALL)CreateFileMappingA,      0 },
@@ -48370,8 +49347,8 @@ static struct win_syscall {
 #define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
         DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent)
 
-#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
-        (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
+#if (SQLITE_OS_WINCE || defined(SQLITE_WIN32_HAS_WIDE)) && \
+        (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
   { "CreateFileMappingW",      (SYSCALL)CreateFileMappingW,      0 },
 #else
   { "CreateFileMappingW",      (SYSCALL)0,                       0 },
@@ -48380,7 +49357,7 @@ static struct win_syscall {
 #define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
         DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if defined(SQLITE_WIN32_HAS_WIDE)
   { "CreateMutexW",            (SYSCALL)CreateMutexW,            0 },
 #else
   { "CreateMutexW",            (SYSCALL)0,                       0 },
@@ -48466,7 +49443,7 @@ static struct win_syscall {
 #define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \
         LPDWORD))aSyscall[18].pCurrent)
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
   { "GetDiskFreeSpaceW",       (SYSCALL)GetDiskFreeSpaceW,       0 },
 #else
   { "GetDiskFreeSpaceW",       (SYSCALL)0,                       0 },
@@ -48483,7 +49460,7 @@ static struct win_syscall {
 
 #define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if defined(SQLITE_WIN32_HAS_WIDE)
   { "GetFileAttributesW",      (SYSCALL)GetFileAttributesW,      0 },
 #else
   { "GetFileAttributesW",      (SYSCALL)0,                       0 },
@@ -48500,11 +49477,7 @@ static struct win_syscall {
 #define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \
         LPVOID))aSyscall[22].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "GetFileSize",             (SYSCALL)GetFileSize,             0 },
-#else
-  { "GetFileSize",             (SYSCALL)0,                       0 },
-#endif
 
 #define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent)
 
@@ -48517,7 +49490,7 @@ static struct win_syscall {
 #define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \
         LPSTR*))aSyscall[24].pCurrent)
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
   { "GetFullPathNameW",        (SYSCALL)GetFullPathNameW,        0 },
 #else
   { "GetFullPathNameW",        (SYSCALL)0,                       0 },
@@ -48552,16 +49525,10 @@ static struct win_syscall {
 #define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \
         LPCSTR))aSyscall[27].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "GetSystemInfo",           (SYSCALL)GetSystemInfo,           0 },
-#else
-  { "GetSystemInfo",           (SYSCALL)0,                       0 },
-#endif
-
 #define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent)
 
   { "GetSystemTime",           (SYSCALL)GetSystemTime,           0 },
-
 #define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent)
 
 #if !SQLITE_OS_WINCE
@@ -48581,7 +49548,7 @@ static struct win_syscall {
 
 #define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if defined(SQLITE_WIN32_HAS_WIDE)
   { "GetTempPathW",            (SYSCALL)GetTempPathW,            0 },
 #else
   { "GetTempPathW",            (SYSCALL)0,                       0 },
@@ -48589,11 +49556,7 @@ static struct win_syscall {
 
 #define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "GetTickCount",            (SYSCALL)GetTickCount,            0 },
-#else
-  { "GetTickCount",            (SYSCALL)0,                       0 },
-#endif
 
 #define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
 
@@ -48606,7 +49569,7 @@ static struct win_syscall {
 #define osGetVersionExA ((BOOL(WINAPI*)( \
         LPOSVERSIONINFOA))aSyscall[34].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+#if defined(SQLITE_WIN32_HAS_WIDE) && \
         SQLITE_WIN32_GETVERSIONEX
   { "GetVersionExW",           (SYSCALL)GetVersionExW,           0 },
 #else
@@ -48621,20 +49584,12 @@ static struct win_syscall {
 #define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
         SIZE_T))aSyscall[36].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "HeapCreate",              (SYSCALL)HeapCreate,              0 },
-#else
-  { "HeapCreate",              (SYSCALL)0,                       0 },
-#endif
 
 #define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
         SIZE_T))aSyscall[37].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "HeapDestroy",             (SYSCALL)HeapDestroy,             0 },
-#else
-  { "HeapDestroy",             (SYSCALL)0,                       0 },
-#endif
 
 #define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent)
 
@@ -48652,16 +49607,12 @@ static struct win_syscall {
 #define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
         LPCVOID))aSyscall[41].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "HeapValidate",            (SYSCALL)HeapValidate,            0 },
-#else
-  { "HeapValidate",            (SYSCALL)0,                       0 },
-#endif
 
 #define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
         LPCVOID))aSyscall[42].pCurrent)
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
   { "HeapCompact",             (SYSCALL)HeapCompact,             0 },
 #else
   { "HeapCompact",             (SYSCALL)0,                       0 },
@@ -48677,7 +49628,7 @@ static struct win_syscall {
 
 #define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent)
 
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+#if defined(SQLITE_WIN32_HAS_WIDE) && \
         !defined(SQLITE_OMIT_LOAD_EXTENSION)
   { "LoadLibraryW",            (SYSCALL)LoadLibraryW,            0 },
 #else
@@ -48686,15 +49637,11 @@ static struct win_syscall {
 
 #define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "LocalFree",               (SYSCALL)LocalFree,               0 },
-#else
-  { "LocalFree",               (SYSCALL)0,                       0 },
-#endif
 
 #define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
   { "LockFile",                (SYSCALL)LockFile,                0 },
 #else
   { "LockFile",                (SYSCALL)0,                       0 },
@@ -48716,8 +49663,7 @@ static struct win_syscall {
         LPOVERLAPPED))aSyscall[48].pCurrent)
 #endif
 
-#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \
-        (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
+#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
   { "MapViewOfFile",           (SYSCALL)MapViewOfFile,           0 },
 #else
   { "MapViewOfFile",           (SYSCALL)0,                       0 },
@@ -48745,20 +49691,12 @@ static struct win_syscall {
 
 #define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "SetFilePointer",          (SYSCALL)SetFilePointer,          0 },
-#else
-  { "SetFilePointer",          (SYSCALL)0,                       0 },
-#endif
 
 #define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
         DWORD))aSyscall[54].pCurrent)
 
-#if !SQLITE_OS_WINRT
   { "Sleep",                   (SYSCALL)Sleep,                   0 },
-#else
-  { "Sleep",                   (SYSCALL)0,                       0 },
-#endif
 
 #define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
 
@@ -48767,7 +49705,7 @@ static struct win_syscall {
 #define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \
         LPFILETIME))aSyscall[56].pCurrent)
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
   { "UnlockFile",              (SYSCALL)UnlockFile,              0 },
 #else
   { "UnlockFile",              (SYSCALL)0,                       0 },
@@ -48805,15 +49743,6 @@ static struct win_syscall {
 #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
         LPOVERLAPPED))aSyscall[61].pCurrent)
 
-#if SQLITE_OS_WINRT
-  { "CreateEventExW",          (SYSCALL)CreateEventExW,          0 },
-#else
-  { "CreateEventExW",          (SYSCALL)0,                       0 },
-#endif
-
-#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
-        DWORD,DWORD))aSyscall[62].pCurrent)
-
 /*
 ** For WaitForSingleObject(), MSDN says:
 **
@@ -48823,7 +49752,7 @@ static struct win_syscall {
   { "WaitForSingleObject",     (SYSCALL)WaitForSingleObject,     0 },
 
 #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
-        DWORD))aSyscall[63].pCurrent)
+        DWORD))aSyscall[62].pCurrent)
 
 #if !SQLITE_OS_WINCE
   { "WaitForSingleObjectEx",   (SYSCALL)WaitForSingleObjectEx,   0 },
@@ -48832,69 +49761,12 @@ static struct win_syscall {
 #endif
 
 #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
-        BOOL))aSyscall[64].pCurrent)
-
-#if SQLITE_OS_WINRT
-  { "SetFilePointerEx",        (SYSCALL)SetFilePointerEx,        0 },
-#else
-  { "SetFilePointerEx",        (SYSCALL)0,                       0 },
-#endif
+        BOOL))aSyscall[63].pCurrent)
 
-#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
-        PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent)
-
-#if SQLITE_OS_WINRT
-  { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
-#else
-  { "GetFileInformationByHandleEx", (SYSCALL)0,                  0 },
-#endif
-
-#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
-        FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent)
-
-#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
-  { "MapViewOfFileFromApp",    (SYSCALL)MapViewOfFileFromApp,    0 },
-#else
-  { "MapViewOfFileFromApp",    (SYSCALL)0,                       0 },
-#endif
-
-#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
-        SIZE_T))aSyscall[67].pCurrent)
-
-#if SQLITE_OS_WINRT
-  { "CreateFile2",             (SYSCALL)CreateFile2,             0 },
-#else
-  { "CreateFile2",             (SYSCALL)0,                       0 },
-#endif
-
-#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
-        LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent)
-
-#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
-  { "LoadPackagedLibrary",     (SYSCALL)LoadPackagedLibrary,     0 },
-#else
-  { "LoadPackagedLibrary",     (SYSCALL)0,                       0 },
-#endif
-
-#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
-        DWORD))aSyscall[69].pCurrent)
-
-#if SQLITE_OS_WINRT
-  { "GetTickCount64",          (SYSCALL)GetTickCount64,          0 },
-#else
-  { "GetTickCount64",          (SYSCALL)0,                       0 },
-#endif
-
-#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent)
-
-#if SQLITE_OS_WINRT
   { "GetNativeSystemInfo",     (SYSCALL)GetNativeSystemInfo,     0 },
-#else
-  { "GetNativeSystemInfo",     (SYSCALL)0,                       0 },
-#endif
 
 #define osGetNativeSystemInfo ((VOID(WINAPI*)( \
-        LPSYSTEM_INFO))aSyscall[71].pCurrent)
+        LPSYSTEM_INFO))aSyscall[64].pCurrent)
 
 #if defined(SQLITE_WIN32_HAS_ANSI)
   { "OutputDebugStringA",      (SYSCALL)OutputDebugStringA,      0 },
@@ -48902,7 +49774,7 @@ static struct win_syscall {
   { "OutputDebugStringA",      (SYSCALL)0,                       0 },
 #endif
 
-#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent)
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[65].pCurrent)
 
 #if defined(SQLITE_WIN32_HAS_WIDE)
   { "OutputDebugStringW",      (SYSCALL)OutputDebugStringW,      0 },
@@ -48910,20 +49782,11 @@ static struct win_syscall {
   { "OutputDebugStringW",      (SYSCALL)0,                       0 },
 #endif
 
-#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent)
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[66].pCurrent)
 
   { "GetProcessHeap",          (SYSCALL)GetProcessHeap,          0 },
 
-#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent)
-
-#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
-  { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
-#else
-  { "CreateFileMappingFromApp", (SYSCALL)0,                      0 },
-#endif
-
-#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
-        LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent)
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[67].pCurrent)
 
 /*
 ** NOTE: On some sub-platforms, the InterlockedCompareExchange "function"
@@ -48938,25 +49801,25 @@ static struct win_syscall {
   { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 },
 
 #define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \
-        SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent)
+        SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[68].pCurrent)
 #endif /* defined(InterlockedCompareExchange) */
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
+#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID
   { "UuidCreate",               (SYSCALL)UuidCreate,             0 },
 #else
   { "UuidCreate",               (SYSCALL)0,                      0 },
 #endif
 
-#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent)
+#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[69].pCurrent)
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
+#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID
   { "UuidCreateSequential",     (SYSCALL)UuidCreateSequential,   0 },
 #else
   { "UuidCreateSequential",     (SYSCALL)0,                      0 },
 #endif
 
 #define osUuidCreateSequential \
-        ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent)
+        ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[70].pCurrent)
 
 #if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0
   { "FlushViewOfFile",          (SYSCALL)FlushViewOfFile,        0 },
@@ -48965,7 +49828,7 @@ static struct win_syscall {
 #endif
 
 #define osFlushViewOfFile \
-        ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)
+        ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[71].pCurrent)
 
 /*
 ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent()
@@ -48982,7 +49845,7 @@ static struct win_syscall {
 
 #define osCreateEvent ( \
     (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \
-    aSyscall[80].pCurrent \
+    aSyscall[72].pCurrent \
 )
 
 /*
@@ -48999,7 +49862,7 @@ static struct win_syscall {
   { "CancelIo",                 (SYSCALL)0,                      0 },
 #endif
 
-#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent)
+#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[73].pCurrent)
 
 #if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32)
   { "GetModuleHandleW",         (SYSCALL)GetModuleHandleW,       0 },
@@ -49007,7 +49870,7 @@ static struct win_syscall {
   { "GetModuleHandleW",         (SYSCALL)0,                      0 },
 #endif
 
-#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent)
+#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[74].pCurrent)
 
 #ifndef _WIN32
   { "getenv",                   (SYSCALL)getenv,                 0 },
@@ -49015,7 +49878,7 @@ static struct win_syscall {
   { "getenv",                   (SYSCALL)0,                      0 },
 #endif
 
-#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent)
+#define osGetenv ((const char *(*)(const char *))aSyscall[75].pCurrent)
 
 #ifndef _WIN32
   { "getcwd",                   (SYSCALL)getcwd,                 0 },
@@ -49023,7 +49886,7 @@ static struct win_syscall {
   { "getcwd",                   (SYSCALL)0,                      0 },
 #endif
 
-#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent)
+#define osGetcwd ((char*(*)(char*,size_t))aSyscall[76].pCurrent)
 
 #ifndef _WIN32
   { "readlink",                 (SYSCALL)readlink,               0 },
@@ -49031,7 +49894,7 @@ static struct win_syscall {
   { "readlink",                 (SYSCALL)0,                      0 },
 #endif
 
-#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent)
+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[77].pCurrent)
 
 #ifndef _WIN32
   { "lstat",                    (SYSCALL)lstat,                  0 },
@@ -49039,7 +49902,7 @@ static struct win_syscall {
   { "lstat",                    (SYSCALL)0,                      0 },
 #endif
 
-#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent)
+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[78].pCurrent)
 
 #ifndef _WIN32
   { "__errno",                  (SYSCALL)__errno,                0 },
@@ -49047,7 +49910,7 @@ static struct win_syscall {
   { "__errno",                  (SYSCALL)0,                      0 },
 #endif
 
-#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)())
+#define osErrno (*((int*(*)(void))aSyscall[79].pCurrent)())
 
 #ifndef _WIN32
   { "cygwin_conv_path",         (SYSCALL)cygwin_conv_path,       0 },
@@ -49056,7 +49919,7 @@ static struct win_syscall {
 #endif
 
 #define osCygwin_conv_path ((size_t(*)(unsigned int, \
-    const void *, void *, size_t))aSyscall[88].pCurrent)
+    const void *, void *, size_t))aSyscall[80].pCurrent)
 
 }; /* End of the overrideable system calls */
 
@@ -49160,10 +50023,10 @@ SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){
   hHeap = winMemGetHeap();
   assert( hHeap!=0 );
   assert( hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
   assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
 #endif
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
   if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){
     DWORD lastErrno = osGetLastError();
     if( lastErrno==NO_ERROR ){
@@ -49276,28 +50139,11 @@ SQLITE_API void SQLITE_APICALL sqlite3_win32_write_debug(const char *zBuf, int n
 }
 #endif /* _WIN32 */
 
-/*
-** The following routine suspends the current thread for at least ms
-** milliseconds.  This is equivalent to the Win32 Sleep() interface.
-*/
-#if SQLITE_OS_WINRT
-static HANDLE sleepObj = NULL;
-#endif
-
 SQLITE_API void SQLITE_APICALL sqlite3_win32_sleep(DWORD milliseconds){
-#if SQLITE_OS_WINRT
-  if ( sleepObj==NULL ){
-    sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET,
-                                SYNCHRONIZE);
-  }
-  assert( sleepObj!=NULL );
-  osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
-#else
   osSleep(milliseconds);
-#endif
 }
 
-#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
+#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && \
         SQLITE_THREADSAFE>0
 SQLITE_PRIVATE DWORD SQLITE_APICALL sqlite3Win32Wait(HANDLE hObject){
   DWORD rc;
@@ -49321,7 +50167,7 @@ SQLITE_PRIVATE DWORD SQLITE_APICALL sqlite3Win32Wait(HANDLE hObject){
 
 #if !SQLITE_WIN32_GETVERSIONEX
 # define osIsNT()  (1)
-#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
+#elif SQLITE_OS_WINCE || !defined(SQLITE_WIN32_HAS_ANSI)
 # define osIsNT()  (1)
 #elif !defined(SQLITE_WIN32_HAS_WIDE)
 # define osIsNT()  (0)
@@ -49334,13 +50180,7 @@ SQLITE_PRIVATE DWORD SQLITE_APICALL sqlite3Win32Wait(HANDLE hObject){
 ** based on the NT kernel.
 */
 SQLITE_API int SQLITE_APICALL sqlite3_win32_is_nt(void){
-#if SQLITE_OS_WINRT
-  /*
-  ** NOTE: The WinRT sub-platform is always assumed to be based on the NT
-  **       kernel.
-  */
-  return 1;
-#elif SQLITE_WIN32_GETVERSIONEX
+#if SQLITE_WIN32_GETVERSIONEX
   if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
 #if defined(SQLITE_WIN32_HAS_ANSI)
     OSVERSIONINFOA sInfo;
@@ -49382,7 +50222,7 @@ static void *winMemMalloc(int nBytes){
   hHeap = winMemGetHeap();
   assert( hHeap!=0 );
   assert( hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
   assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
 #endif
   assert( nBytes>=0 );
@@ -49404,7 +50244,7 @@ static void winMemFree(void *pPrior){
   hHeap = winMemGetHeap();
   assert( hHeap!=0 );
   assert( hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
   assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
 #endif
   if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
@@ -49425,7 +50265,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){
   hHeap = winMemGetHeap();
   assert( hHeap!=0 );
   assert( hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
   assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
 #endif
   assert( nBytes>=0 );
@@ -49453,7 +50293,7 @@ static int winMemSize(void *p){
   hHeap = winMemGetHeap();
   assert( hHeap!=0 );
   assert( hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
   assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) );
 #endif
   if( !p ) return 0;
@@ -49483,7 +50323,7 @@ static int winMemInit(void *pAppData){
   assert( pWinMemData->magic1==WINMEM_MAGIC1 );
   assert( pWinMemData->magic2==WINMEM_MAGIC2 );
 
-#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
+#if SQLITE_WIN32_HEAP_CREATE
   if( !pWinMemData->hHeap ){
     DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE;
     DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap;
@@ -49516,7 +50356,7 @@ static int winMemInit(void *pAppData){
 #endif
   assert( pWinMemData->hHeap!=0 );
   assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
   assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
 #endif
   return SQLITE_OK;
@@ -49534,7 +50374,7 @@ static void winMemShutdown(void *pAppData){
 
   if( pWinMemData->hHeap ){
     assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+#if defined(SQLITE_WIN32_MALLOC_VALIDATE)
     assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
 #endif
     if( pWinMemData->bOwned ){
@@ -49915,17 +50755,6 @@ static int SQLITE_APICALL winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zB
   char *zOut = 0;
 
   if( osIsNT() ){
-#if SQLITE_OS_WINRT
-    WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1];
-    dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
-                             FORMAT_MESSAGE_IGNORE_INSERTS,
-                             NULL,
-                             lastErrno,
-                             0,
-                             zTempWide,
-                             SQLITE_WIN32_MAX_ERRMSG_CHARS,
-                             0);
-#else
     LPWSTR zTempWide = NULL;
     dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                              FORMAT_MESSAGE_FROM_SYSTEM |
@@ -49936,16 +50765,13 @@ static int SQLITE_APICALL winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zB
                              (LPWSTR) &zTempWide,
                              0,
                              0);
-#endif
     if( dwLen > 0 ){
       /* allocate a buffer and convert to UTF8 */
       sqlite3BeginBenignMalloc();
       zOut = winUnicodeToUtf8(zTempWide);
       sqlite3EndBenignMalloc();
-#if !SQLITE_OS_WINRT
       /* free the system buffer allocated by FormatMessage */
       osLocalFree(zTempWide);
-#endif
     }
   }
 #ifdef SQLITE_WIN32_HAS_ANSI
@@ -50606,7 +51432,6 @@ static int SQLITE_APICALL winHandleUnlock(HANDLE h, int iOff, int nByte){
 static int SQLITE_APICALL winHandleSeek(HANDLE h, sqlite3_int64 iOffset){
   int rc = SQLITE_OK;             /* Return value */
 
-#if !SQLITE_OS_WINRT
   LONG upperBits;                 /* Most sig. 32 bits of new offset */
   LONG lowerBits;                 /* Least sig. 32 bits of new offset */
   DWORD dwRet;                    /* Value returned by SetFilePointer() */
@@ -50628,20 +51453,7 @@ static int SQLITE_APICALL winHandleSeek(HANDLE h, sqlite3_int64 iOffset){
       rc = SQLITE_IOERR_SEEK;
     }
   }
-#else
-  /* This implementation works for WinRT. */
-  LARGE_INTEGER x;                /* The new offset */
-  BOOL bRet;                      /* Value returned by SetFilePointerEx() */
-
-  x.QuadPart = iOffset;
-  bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN);
-
-  if(!bRet){
-    rc = SQLITE_IOERR_SEEK;
-  }
-#endif
-
-  OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc)));
+  OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset,sqlite3ErrName(rc)));
   return rc;
 }
 
@@ -50942,17 +51754,6 @@ static int SQLITE_APICALL winHandleTruncate(HANDLE h, sqlite3_int64 nByte){
 */
 static int SQLITE_APICALL winHandleSize(HANDLE h, sqlite3_int64 *pnByte){
   int rc = SQLITE_OK;
-
-#if SQLITE_OS_WINRT
-  FILE_STANDARD_INFO info;
-  BOOL b;
-  b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info));
-  if( b ){
-    *pnByte = info.EndOfFile.QuadPart;
-  }else{
-    rc = SQLITE_IOERR_FSTAT;
-  }
-#else
   DWORD upperBits = 0;
   DWORD lowerBits = 0;
 
@@ -50962,8 +51763,6 @@ static int SQLITE_APICALL winHandleSize(HANDLE h, sqlite3_int64 *pnByte){
   if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){
     rc = SQLITE_IOERR_FSTAT;
   }
-#endif
-
   return rc;
 }
 
@@ -51162,20 +51961,6 @@ static int SQLITE_APICALL winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
   assert( pSize!=0 );
   SimulateIOError(return SQLITE_IOERR_FSTAT);
   OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize));
-
-#if SQLITE_OS_WINRT
-  {
-    FILE_STANDARD_INFO info;
-    if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo,
-                                     &info, sizeof(info)) ){
-      *pSize = info.EndOfFile.QuadPart;
-    }else{
-      pFile->lastErrno = osGetLastError();
-      rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
-                       "winFileSize", pFile->zPath);
-    }
-  }
-#else
   {
     DWORD upperBits;
     DWORD lowerBits;
@@ -51190,7 +51975,6 @@ static int SQLITE_APICALL winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
                        "winFileSize", pFile->zPath);
     }
   }
-#endif
   OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n",
            pFile->h, pSize, *pSize, sqlite3ErrName(rc)));
   return rc;
@@ -51932,7 +52716,7 @@ static void SQLITE_APICALL winShmLeaveMutex(void){
   sqlite3_mutex_leave(winBigLock);
 }
 #ifndef NDEBUG
-static int SQLITE_APICALL winShmMutexHeld(void) {
+static int winShmMutexHeld(void) {
   return sqlite3_mutex_held(winBigLock);
 }
 #endif
@@ -52152,20 +52936,6 @@ static int SQLITE_APICALL winHandleOpen(
   ** TODO: retry-on-ioerr.
   */
   if( osIsNT() ){
-#if SQLITE_OS_WINRT
-    CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
-    memset(&extendedParameters, 0, sizeof(extendedParameters));
-    extendedParameters.dwSize = sizeof(extendedParameters);
-    extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
-    extendedParameters.dwFileFlags = flag_overlapped;
-    extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
-    h = osCreateFile2((LPCWSTR)zConverted,
-        (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */
-        FILE_SHARE_READ | FILE_SHARE_WRITE,      /* dwShareMode */
-        OPEN_ALWAYS,                             /* dwCreationDisposition */
-        &extendedParameters
-    );
-#else
     h = osCreateFileW((LPCWSTR)zConverted,         /* lpFileName */
         (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),  /* dwDesiredAccess */
         FILE_SHARE_READ | FILE_SHARE_WRITE,        /* dwShareMode */
@@ -52174,7 +52944,6 @@ static int SQLITE_APICALL winHandleOpen(
         FILE_ATTRIBUTE_NORMAL|flag_overlapped,
         NULL
     );
-#endif
   }else{
     /* Due to pre-processor directives earlier in this file,
     ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */
@@ -52594,7 +53363,7 @@ static int SQLITE_APICALL winShmMap(
   if( pShmNode->nRegion<=iRegion ){
     HANDLE hShared = pShmNode->hSharedShm;
     struct ShmRegion *apNew;           /* New aRegion[] array */
-    int nByte = (iRegion+1)*szRegion;  /* Minimum required file size */
+    i64 nByte = ((i64)iRegion+1)*(i64)szRegion;  /* Minimum file size */
     sqlite3_int64 sz;                  /* Current size of wal-index file */
 
     pShmNode->szRegion = szRegion;
@@ -52625,7 +53394,7 @@ static int SQLITE_APICALL winShmMap(
 
     /* Map the requested memory region into this processes address space. */
     apNew = (struct ShmRegion*)sqlite3_realloc64(
-        pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
+        pShmNode->aRegion, ((i64)iRegion+1)*sizeof(apNew[0])
     );
     if( !apNew ){
       rc = SQLITE_IOERR_NOMEM_BKPT;
@@ -52642,29 +53411,20 @@ static int SQLITE_APICALL winShmMap(
       HANDLE hMap = NULL;         /* file-mapping handle */
       void *pMap = 0;             /* Mapped memory region */
 
-#if SQLITE_OS_WINRT
-      hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL);
-#elif defined(SQLITE_WIN32_HAS_WIDE)
+#if defined(SQLITE_WIN32_HAS_WIDE)
       hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL);
 #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
       hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL);
 #endif
-
-      OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
+      OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%lld, rc=%s\n",
                osGetCurrentProcessId(), pShmNode->nRegion, nByte,
                hMap ? "ok" : "failed"));
       if( hMap ){
-        int iOffset = pShmNode->nRegion*szRegion;
+        i64 iOffset = pShmNode->nRegion*szRegion;
         int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
-#if SQLITE_OS_WINRT
-        pMap = osMapViewOfFileFromApp(hMap, flags,
-            iOffset - iOffsetShift, szRegion + iOffsetShift
-        );
-#else
         pMap = osMapViewOfFile(hMap, flags,
-            0, iOffset - iOffsetShift, szRegion + iOffsetShift
+            0, iOffset - iOffsetShift, (i64)szRegion + iOffsetShift
         );
-#endif
         OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n",
                  osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
                  szRegion, pMap ? "ok" : "failed"));
@@ -52685,7 +53445,7 @@ static int SQLITE_APICALL winShmMap(
 
 shmpage_out:
   if( pShmNode->nRegion>iRegion ){
-    int iOffset = iRegion*szRegion;
+    i64 iOffset = (i64)iRegion*(i64)szRegion;
     int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
     char *p = (char *)pShmNode->aRegion[iRegion].pMap;
     *pp = (void *)&p[iOffsetShift];
@@ -52797,9 +53557,7 @@ static int SQLITE_APICALL winMapfile(winFile *pFd, sqlite3_int64 nByte){
       flags |= FILE_MAP_WRITE;
     }
 #endif
-#if SQLITE_OS_WINRT
-    pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL);
-#elif defined(SQLITE_WIN32_HAS_WIDE)
+#if defined(SQLITE_WIN32_HAS_WIDE)
     pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect,
                                 (DWORD)((nMap>>32) & 0xffffffff),
                                 (DWORD)(nMap & 0xffffffff), NULL);
@@ -52819,11 +53577,7 @@ static int SQLITE_APICALL winMapfile(winFile *pFd, sqlite3_int64 nByte){
     }
     assert( (nMap % winSysInfo.dwPageSize)==0 );
     assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
-#if SQLITE_OS_WINRT
-    pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap);
-#else
     pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap);
-#endif
     if( pNew==NULL ){
       osCloseHandle(pFd->hMap);
       pFd->hMap = NULL;
@@ -53158,7 +53912,6 @@ static int SQLITE_APICALL winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
   }
 #endif
 
-#if !SQLITE_OS_WINRT && defined(_WIN32)
   else if( osIsNT() ){
     char *zMulti;
     LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
@@ -53212,7 +53965,6 @@ static int SQLITE_APICALL winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
     }
   }
 #endif /* SQLITE_WIN32_HAS_ANSI */
-#endif /* !SQLITE_OS_WINRT */
 
   /*
   ** Check to make sure the temporary directory ends with an appropriate
@@ -53387,13 +54139,6 @@ static int SQLITE_APICALL winOpen(
   memset(pFile, 0, sizeof(winFile));
   pFile->h = INVALID_HANDLE_VALUE;
 
-#if SQLITE_OS_WINRT
-  if( !zUtf8Name && !sqlite3_temp_directory ){
-    sqlite3_log(SQLITE_ERROR,
-        "sqlite3_temp_directory variable should be set for WinRT");
-  }
-#endif
-
   /* If the second argument to this function is NULL, generate a
   ** temporary file name to use
   */
@@ -53476,31 +54221,6 @@ static int SQLITE_APICALL winOpen(
 #endif
 
   if( osIsNT() ){
-#if SQLITE_OS_WINRT
-    CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
-    extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
-    extendedParameters.dwFileAttributes =
-            dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK;
-    extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK;
-    extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
-    extendedParameters.lpSecurityAttributes = NULL;
-    extendedParameters.hTemplateFile = NULL;
-    do{
-      h = osCreateFile2((LPCWSTR)zConverted,
-                        dwDesiredAccess,
-                        dwShareMode,
-                        dwCreationDisposition,
-                        &extendedParameters);
-      if( h!=INVALID_HANDLE_VALUE ) break;
-      if( isReadWrite ){
-        int rc2;
-        sqlite3BeginBenignMalloc();
-        rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
-        sqlite3EndBenignMalloc();
-        if( rc2==SQLITE_OK && isRO ) break;
-      }
-    }while( winRetryIoerr(&cnt, &lastErrno) );
-#else
     do{
       h = osCreateFileW((LPCWSTR)zConverted,
                         dwDesiredAccess,
@@ -53517,7 +54237,6 @@ static int SQLITE_APICALL winOpen(
         if( rc2==SQLITE_OK && isRO ) break;
       }
     }while( winRetryIoerr(&cnt, &lastErrno) );
-#endif
   }
 #ifdef SQLITE_WIN32_HAS_ANSI
   else{
@@ -53654,25 +54373,7 @@ static int SQLITE_APICALL winDelete(
   }
   if( osIsNT() ){
     do {
-#if SQLITE_OS_WINRT
-      WIN32_FILE_ATTRIBUTE_DATA sAttrData;
-      memset(&sAttrData, 0, sizeof(sAttrData));
-      if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard,
-                                  &sAttrData) ){
-        attr = sAttrData.dwFileAttributes;
-      }else{
-        lastErrno = osGetLastError();
-        if( lastErrno==ERROR_FILE_NOT_FOUND
-         || lastErrno==ERROR_PATH_NOT_FOUND ){
-          rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
-        }else{
-          rc = SQLITE_ERROR;
-        }
-        break;
-      }
-#else
       attr = osGetFileAttributesW(zConverted);
-#endif
       if ( attr==INVALID_FILE_ATTRIBUTES ){
         lastErrno = osGetLastError();
         if( lastErrno==ERROR_FILE_NOT_FOUND
@@ -53795,6 +54496,7 @@ static int SQLITE_APICALL winAccess(
         attr = sAttrData.dwFileAttributes;
       }
     }else{
+      if( noRetry ) lastErrno = osGetLastError();
       winLogIoerr(cnt, __LINE__);
       if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
         sqlite3_free(zConverted);
@@ -53963,7 +54665,7 @@ static int SQLITE_APICALL winFullPathnameNoMutex(
   int nFull,                    /* Size of output buffer in bytes */
   char *zFull                   /* Output buffer */
 ){
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
   int nByte;
   void *zConverted;
   char *zOut;
@@ -54052,7 +54754,7 @@ static int SQLITE_APICALL winFullPathnameNoMutex(
   }
 #endif /* __CYGWIN__ */
 
-#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32)
+#if SQLITE_OS_WINCE && defined(_WIN32)
   SimulateIOError( return SQLITE_ERROR );
   /* WinCE has no concept of a relative pathname, or so I am told. */
   /* WinRT has no way to convert a relative path to an absolute one. */
@@ -54071,7 +54773,7 @@ static int SQLITE_APICALL winFullPathnameNoMutex(
   return SQLITE_OK;
 #endif
 
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE
 #if defined(_WIN32)
   /* It's odd to simulate an io-error here, but really this is just
   ** using the io-error infrastructure to test that SQLite handles this
@@ -54203,11 +54905,7 @@ static void *SQLITE_APICALL winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
     return 0;
   }
   if( osIsNT() ){
-#if SQLITE_OS_WINRT
-    h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
-#else
     h = osLoadLibraryW((LPCWSTR)zConverted);
-#endif
   }
 #ifdef SQLITE_WIN32_HAS_ANSI
   else{
@@ -54289,23 +54987,16 @@ static int SQLITE_APICALL winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf)
     DWORD pid = osGetCurrentProcessId();
     xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD));
   }
-#if SQLITE_OS_WINRT
-  {
-    ULONGLONG cnt = osGetTickCount64();
-    xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG));
-  }
-#else
   {
     DWORD cnt = osGetTickCount();
     xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD));
   }
-#endif /* SQLITE_OS_WINRT */
   {
     LARGE_INTEGER i;
     osQueryPerformanceCounter(&i);
     xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER));
   }
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
+#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID
   {
     UUID id;
     memset(&id, 0, sizeof(UUID));
@@ -54315,7 +55006,7 @@ static int SQLITE_APICALL winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf)
     osUuidCreateSequential(&id);
     xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
   }
-#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */
+#endif /* !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID */
   return e.nXor>nBuf ? nBuf : e.nXor;
 #endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */
 }
@@ -54546,15 +55237,16 @@ SQLITE_API int SQLITE_APICALL sqlite3_os_init(void){
 
   /* Double-check that the aSyscall[] array has been constructed
   ** correctly.  See ticket [bb3a86e890c8e96ab] */
-  assert( ArraySize(aSyscall)==89 );
+  assert( ArraySize(aSyscall)==81 );
+  assert( strcmp(aSyscall[0].zName,"AreFileApisANSI")==0 );
+  assert( strcmp(aSyscall[20].zName,"GetFileAttributesA")==0 );
+  assert( strcmp(aSyscall[40].zName,"HeapReAlloc")==0 );
+  assert( strcmp(aSyscall[60].zName,"WideCharToMultiByte")==0 );
+  assert( strcmp(aSyscall[80].zName,"cygwin_conv_path")==0 );
 
   /* get memory map allocation granularity */
   memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
-#if SQLITE_OS_WINRT
-  osGetNativeSystemInfo(&winSysInfo);
-#else
   osGetSystemInfo(&winSysInfo);
-#endif
   assert( winSysInfo.dwAllocationGranularity>0 );
   assert( winSysInfo.dwPageSize>0 );
 
@@ -54578,17 +55270,9 @@ SQLITE_API int SQLITE_APICALL sqlite3_os_init(void){
 }
 
 SQLITE_API int SQLITE_APICALL sqlite3_os_end(void){
-#if SQLITE_OS_WINRT
-  if( sleepObj!=NULL ){
-    osCloseHandle(sleepObj);
-    sleepObj = NULL;
-  }
-#endif
-
 #ifndef SQLITE_OMIT_WAL
   winBigLock = 0;
 #endif
-
   return SQLITE_OK;
 }
 
@@ -55474,10 +56158,10 @@ SQLITE_API int SQLITE_APICALL sqlite3_deserialize(
   if( rc ) goto end_deserialize;
   db->init.iDb = (u8)iDb;
   db->init.reopenMemdb = 1;
-  rc = sqlite3_step(pStmt);
+  sqlite3_step(pStmt);
   db->init.reopenMemdb = 0;
-  if( rc!=SQLITE_DONE ){
-    rc = SQLITE_ERROR;
+  rc = sqlite3_finalize(pStmt);
+  if( rc!=SQLITE_OK ){
     goto end_deserialize;
   }
   p = memdbFromDbSchema(db, zSchema);
@@ -55498,7 +56182,6 @@ SQLITE_API int SQLITE_APICALL sqlite3_deserialize(
   }
 
 end_deserialize:
-  sqlite3_finalize(pStmt);
   if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){
     sqlite3_free(pData);
   }
@@ -55844,7 +56527,7 @@ SQLITE_PRIVATE u32 SQLITE_APICALL sqlite3BitvecSize(Bitvec *p){
 **
 ** For some Bitvec p and see a recursive view of the Bitvec's content.
 */
-static void SQLITE_APICALL showBitvec(Bitvec *p, int n, unsigned x){
+static void showBitvec(Bitvec *p, int n, unsigned x){
   int i;
   if( p==0 ){
     printf("NULL\n");
@@ -55874,7 +56557,7 @@ static void SQLITE_APICALL showBitvec(Bitvec *p, int n, unsigned x){
     }
   }
 }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowBitvec(Bitvec *p){
+SQLITE_PRIVATE void sqlite3ShowBitvec(Bitvec *p){
   showBitvec(p, 0, 0);
 }
 #endif
@@ -56172,7 +56855,7 @@ static int pageNotOnDirtyList(PCache *pCache, PgHdr *pPg){
 **          assert( sqlite3PcachePageSanity(pPg) );
 */
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3PcachePageSanity(PgHdr *pPg){
+SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){
   PCache *pCache;
   assert( pPg!=0 );
   assert( pPg->pgno>0 || pPg->pPager==0 );    /* Page number is 1 or more */
@@ -56962,7 +57645,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PCacheIsDirty(PCache *pCache){
 ** callback. This is only used if the SQLITE_CHECK_PAGES macro is
 ** defined.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3PcacheIterateDirty(PCache *pCache, void (SQLITE_APICALL *xIter)(PgHdr *)){
+SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){
   PgHdr *pDirty;
   for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){
     xIter(pDirty);
@@ -59737,6 +60420,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerDirectReadOk(Pager *pPager, Pgno p
     (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
     if( iRead ) return 0;  /* Case (4) */
   }
+#else
+  UNUSED_PARAMETER(pgno);
 #endif
   assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 );
   if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd)
@@ -59766,7 +60451,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerDirectReadOk(Pager *pPager, Pgno p
 ** This function runs many asserts to try to find inconsistencies in
 ** the internal state of the Pager object.
 */
-static int SQLITE_APICALL assert_pager_state(Pager *p){
+static int assert_pager_state(Pager *p){
   Pager *pPager = p;
 
   /* State must be valid. */
@@ -59911,7 +60596,7 @@ static int SQLITE_APICALL assert_pager_state(Pager *p){
 ** about an unused function.  It is enclosed within SQLITE_DEBUG and so does
 ** not appear in normal builds.
 */
-char *SQLITE_APICALL print_pager_state(Pager *p){
+char *print_pager_state(Pager *p){
   static char zRet[1024];
 
   sqlite3_snprintf(1024, zRet,
@@ -60006,7 +60691,7 @@ static int SQLITE_APICALL subjRequiresPage(PgHdr *pPg){
 /*
 ** Return true if the page is already in the journal file.
 */
-static int SQLITE_APICALL pageInJournal(Pager *pPager, PgHdr *pPg){
+static int pageInJournal(Pager *pPager, PgHdr *pPg){
   return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
 }
 #endif
@@ -60157,17 +60842,17 @@ static int SQLITE_APICALL jrnlBufferSize(Pager *pPager){
 */
 #ifdef SQLITE_CHECK_PAGES
 /*
-** Return a 32-bit hash of the page data for pPage.
+** Return a 64-bit hash of the page data for pPage.
 */
-static u32 pager_datahash(int nByte, unsigned char *pData){
-  u32 hash = 0;
+static u64 pager_datahash(int nByte, unsigned char *pData){
+  u64 hash = 0;
   int i;
   for(i=0; i<nByte; i++){
     hash = (hash*1039) + pData[i];
   }
   return hash;
 }
-static u32 pager_pagehash(PgHdr *pPage){
+static u64 pager_pagehash(PgHdr *pPage){
   return pager_datahash(pPage->pPager->pageSize, (unsigned char *)pPage->pData);
 }
 static void pager_set_pagehash(PgHdr *pPage){
@@ -61488,7 +62173,7 @@ static int SQLITE_APICALL pager_delsuper(Pager *pPager, const char *zSuper){
   if( rc!=SQLITE_OK ) goto delsuper_out;
   nSuperPtr = 1 + (i64)pVfs->mxPathname;
   assert( nSuperJournal>=0 && nSuperPtr>0 );
-  zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2);
+  zFree = sqlite3Malloc(4 + nSuperJournal + 2 + nSuperPtr + 2);
   if( !zFree ){
     rc = SQLITE_NOMEM_BKPT;
     goto delsuper_out;
@@ -61749,10 +62434,10 @@ static int SQLITE_APICALL pager_playback(Pager *pPager, int isHot){
   **
   ** TODO: Technically the following is an error because it assumes that
   ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that
-  ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c,
+  ** ((pPager->pageSize+8) >= pPager->pVfs->mxPathname+1). Using os_unix.c,
   ** mxPathname is 512, which is the same as the minimum allowable value
-  ** for pageSize.
-  */
+  ** for pageSize, and so this assumption holds. But it might not for some
+  ** custom VFS.  */
   zSuper = pPager->pTmpSpace;
   rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname);
   if( rc==SQLITE_OK && zSuper[0] ){
@@ -62906,7 +63591,7 @@ static int SQLITE_APICALL pager_wait_on_lock(Pager *pPager, int locktype){
 ** this circumstance cannot arise.
 */
 #if defined(SQLITE_DEBUG)
-static void SQLITE_APICALL assertTruncateConstraintCb(PgHdr *pPg){
+static void assertTruncateConstraintCb(PgHdr *pPg){
   Pager *pPager = pPg->pPager;
   assert( pPg->flags&PGHDR_DIRTY );
   if( pPg->pgno>pPager->dbSize ){      /* if (a) is false */
@@ -62918,7 +63603,7 @@ static void SQLITE_APICALL assertTruncateConstraintCb(PgHdr *pPg){
     }
   }
 }
-static void SQLITE_APICALL assertTruncateConstraint(Pager *pPager){
+static void assertTruncateConstraint(Pager *pPager){
   sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb);
 }
 #else
@@ -63116,6 +63801,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerClose(Pager *pPager, sqlite3 *db){
     sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
     pPager->pWal = 0;
   }
+#else
+  UNUSED_PARAMETER(db);
 #endif
   pager_reset(pPager);
   if( MEMDB ){
@@ -63156,7 +63843,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerClose(Pager *pPager, sqlite3 *db){
 /*
 ** Return the page number for page pPg.
 */
-SQLITE_PRIVATE Pgno SQLITE_APICALL sqlite3PagerPagenumber(DbPage *pPg){
+SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage *pPg){
   return pPg->pgno;
 }
 #endif
@@ -65175,7 +65862,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerWrite(PgHdr *pPg){
 ** to change the content of the page.
 */
 #ifndef NDEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerIswriteable(DbPage *pPg){
+SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
   return pPg->flags & PGHDR_WRITEABLE;
 }
 #endif
@@ -65743,7 +66430,7 @@ SQLITE_PRIVATE u8 SQLITE_APICALL sqlite3PagerIsreadonly(Pager *pPager){
 /*
 ** Return the sum of the reference counts for all pages held by pPager.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3PagerRefcount(Pager *pPager){
+SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){
   return sqlite3PcacheRefCount(pPager->pPCache);
 }
 #endif
@@ -67883,7 +68570,7 @@ static void SQLITE_APICALL walUnlockExclusive(Wal *pWal, int lockIdx, int n){
 
 /*
 ** Compute a hash on a page number.  The resulting hash value must land
-** between 0 and (HASHTABLE_NSLOT-1).  The walHashNext() function advances
+** between 0 and (HASHTABLE_NSLOT-1).  The walNextHash() function advances
 ** the hash to the next value in the event of a collision.
 */
 static int SQLITE_APICALL walHash(u32 iPage){
@@ -68091,7 +68778,7 @@ static int SQLITE_APICALL walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
     for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
       if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT;
     }
-    sLoc.aPgno[idx-1] = iPage;
+    sLoc.aPgno[(idx-1)&(HASHTABLE_NPAGE-1)] = iPage;
     AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx);
 
 #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
@@ -69011,68 +69698,82 @@ static int SQLITE_APICALL walCheckpoint(
      && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK
     ){
       u32 nBackfill = pInfo->nBackfill;
-      pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT;
-
-      /* Sync the WAL to disk */
-      rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
-
-      /* If the database may grow as a result of this checkpoint, hint
-      ** about the eventual size of the db file to the VFS layer.
-      */
-      if( rc==SQLITE_OK ){
-        i64 nReq = ((i64)mxPage * szPage);
-        i64 nSize;                    /* Current size of database file */
-        sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
-        rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
-        if( rc==SQLITE_OK && nSize<nReq ){
-          if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){
-            /* If the size of the final database is larger than the current
-            ** database plus the amount of data in the wal file, plus the
-            ** maximum size of the pending-byte page (65536 bytes), then
-            ** must be corruption somewhere.  */
-            rc = SQLITE_CORRUPT_BKPT;
-          }else{
-            sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq);
+      WalIndexHdr *pLive = (WalIndexHdr*)walIndexHdr(pWal);
+
+      /* Now that read-lock slot 0 is locked, check that the wal has not been
+      ** wrapped since the header was read for this checkpoint. If it was, then
+      ** there was no work to do anyway.  In this case the
+      ** (pInfo->nBackfill<pWal->hdr.mxFrame) test above only passed because
+      ** pInfo->nBackfill had already been set to 0 by the writer that wrapped
+      ** the wal file. It would also be dangerous to proceed, as there may be
+      ** fewer than pWal->hdr.mxFrame valid frames in the wal file.  */
+      int bChg = memcmp(pLive->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt));
+      if( 0==bChg ){
+        pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT;
+
+        /* Sync the WAL to disk */
+        rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
+
+        /* If the database may grow as a result of this checkpoint, hint
+        ** about the eventual size of the db file to the VFS layer.
+        */
+        if( rc==SQLITE_OK ){
+          i64 nReq = ((i64)mxPage * szPage);
+          i64 nSize;                    /* Current size of database file */
+          sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
+          rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
+          if( rc==SQLITE_OK && nSize<nReq ){
+            if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){
+              /* If the size of the final database is larger than the current
+              ** database plus the amount of data in the wal file, plus the
+              ** maximum size of the pending-byte page (65536 bytes), then
+              ** must be corruption somewhere.  */
+              rc = SQLITE_CORRUPT_BKPT;
+            }else{
+              sqlite3OsFileControlHint(
+                  pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
+            }
           }
-        }
-
-      }
 
-      /* Iterate through the contents of the WAL, copying data to the db file */
-      while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
-        i64 iOffset;
-        assert( walFramePgno(pWal, iFrame)==iDbpage );
-        SEH_INJECT_FAULT;
-        if( AtomicLoad(&db->u1.isInterrupted) ){
-          rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
-          break;
-        }
-        if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
-          continue;
         }
-        iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
-        /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
-        rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
-        if( rc!=SQLITE_OK ) break;
-        iOffset = (iDbpage-1)*(i64)szPage;
-        testcase( IS_BIG_INT(iOffset) );
-        rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
-        if( rc!=SQLITE_OK ) break;
-      }
-      sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
 
-      /* If work was actually accomplished... */
-      if( rc==SQLITE_OK ){
-        if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
-          i64 szDb = pWal->hdr.nPage*(i64)szPage;
-          testcase( IS_BIG_INT(szDb) );
-          rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
-          if( rc==SQLITE_OK ){
-            rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
+        /* Iterate through the contents of the WAL, copying data to the
+        ** db file */
+        while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
+          i64 iOffset;
+          assert( walFramePgno(pWal, iFrame)==iDbpage );
+          SEH_INJECT_FAULT;
+          if( AtomicLoad(&db->u1.isInterrupted) ){
+            rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
+            break;
+          }
+          if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
+            continue;
           }
+          iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
+          /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
+          rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
+          if( rc!=SQLITE_OK ) break;
+          iOffset = (iDbpage-1)*(i64)szPage;
+          testcase( IS_BIG_INT(iOffset) );
+          rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
+          if( rc!=SQLITE_OK ) break;
         }
+        sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
+
+        /* If work was actually accomplished... */
         if( rc==SQLITE_OK ){
-          AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT;
+          if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
+            i64 szDb = pWal->hdr.nPage*(i64)szPage;
+            testcase( IS_BIG_INT(szDb) );
+            rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
+            if( rc==SQLITE_OK ){
+              rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
+            }
+          }
+          if( rc==SQLITE_OK ){
+            AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT;
+          }
         }
       }
 
@@ -70325,7 +71026,10 @@ static int SQLITE_APICALL walFindFrame(
     SEH_INJECT_FAULT;
     while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
       u32 iFrame = iH + sLoc.iZero;
-      if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
+      if( iFrame<=iLast
+       && iFrame>=pWal->minFrame
+       && sLoc.aPgno[(iH-1)&(HASHTABLE_NPAGE-1)]==pgno
+      ){
         assert( iFrame>iRead || CORRUPT_DB );
         iRead = iFrame;
       }
@@ -71122,6 +71826,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3WalCheckpoint(
 
     /* Copy data from the log to the database file. */
     if( rc==SQLITE_OK ){
+      sqlite3FaultSim(660);
       if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
         rc = SQLITE_CORRUPT_BKPT;
       }else if( eMode2!=SQLITE_CHECKPOINT_NOOP ){
@@ -72283,7 +72988,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3BtreeLeave(Btree *p){
 **
 ** This routine is used only from within assert() statements.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeHoldsMutex(Btree *p){
+SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){
   assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 );
   assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db );
   assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->pBt->mutex) );
@@ -72345,7 +73050,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3BtreeLeaveAll(sqlite3 *db){
 **
 ** This routine is used inside assert() statements only.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
+SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
   int i;
   if( !sqlite3_mutex_held(db->mutex) ){
     return 0;
@@ -72374,7 +73079,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
 ** If pSchema is not NULL, then iDb is computed from pSchema and
 ** db using sqlite3SchemaToIndex().
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){
+SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){
   Btree *p;
   assert( db!=0 );
   if( db->pVfs==0 && db->nDb==0 ) return 1;
@@ -72553,7 +73258,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_enable_shared_cache(int enable){
 /*
 ** Return and reset the seek counter for a Btree object.
 */
-SQLITE_PRIVATE sqlite3_uint64 SQLITE_APICALL sqlite3BtreeSeekCount(Btree *pBt){
+SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){
   u64 n =  pBt->nSeek;
   pBt->nSeek = 0;
   return n;
@@ -72570,7 +73275,7 @@ SQLITE_PRIVATE sqlite3_uint64 SQLITE_APICALL sqlite3BtreeSeekCount(Btree *pBt){
 ** with the page number and filename associated with the (MemPage*).
 */
 #ifdef SQLITE_DEBUG
-int SQLITE_APICALL corruptPageError(int lineno, MemPage *p){
+int corruptPageError(int lineno, MemPage *p){
   char *zMsg;
   sqlite3BeginBenignMalloc();
   zMsg = sqlite3_mprintf("database corruption page %u of %s",
@@ -72652,7 +73357,7 @@ static void sharedLockTrace(
 ** hold a write-lock on the schema table (root page 1). This is also
 ** acceptable.
 */
-static int SQLITE_APICALL hasSharedCacheTableLock(
+static int hasSharedCacheTableLock(
   Btree *pBtree,         /* Handle that must hold lock */
   Pgno iRoot,            /* Root page of b-tree */
   int isIndex,           /* True if iRoot is the root of an index b-tree */
@@ -72743,7 +73448,7 @@ static int SQLITE_APICALL hasSharedCacheTableLock(
 **
 **    assert( !hasReadConflicts(pBtree, iRoot) );
 */
-static int SQLITE_APICALL hasReadConflicts(Btree *pBtree, Pgno iRoot){
+static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
   BtCursor *p;
   for(p=pBtree->pBt->pCursor; p; p=p->pNext){
     if( p->pgnoRoot==iRoot
@@ -72975,7 +73680,7 @@ static void SQLITE_APICALL releasePageNotNull(MemPage *pPage);  /* Forward refer
 ** Verify that the cursor holds the mutex on its BtShared
 */
 #ifdef SQLITE_DEBUG
-static int SQLITE_APICALL cursorHoldsMutex(BtCursor *p){
+static int cursorHoldsMutex(BtCursor *p){
   return sqlite3_mutex_held(p->pBt->mutex);
 }
 
@@ -72987,7 +73692,7 @@ static int SQLITE_APICALL cursorHoldsMutex(BtCursor *p){
 ** statements only and for the purpose of double-checking that the btree code
 ** does keep the database connection pointers up-to-date.
 */
-static int SQLITE_APICALL cursorOwnsBtShared(BtCursor *p){
+static int cursorOwnsBtShared(BtCursor *p){
   assert( cursorHoldsMutex(p) );
   return (p->pBtree->db==p->pBt->db);
 }
@@ -73699,7 +74404,7 @@ static void SQLITE_APICALL btreeParseCellPtr(
   CellInfo *pInfo         /* Fill in this structure */
 ){
   u8 *pIter;              /* For scanning through pCell */
-  u32 nPayload;           /* Number of bytes of cell payload */
+  u64 nPayload;           /* Number of bytes of cell payload */
   u64 iKey;               /* Extracted Key value */
 
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@@ -73721,6 +74426,7 @@ static void SQLITE_APICALL btreeParseCellPtr(
     do{
       nPayload = (nPayload<<7) | (*++pIter & 0x7f);
     }while( (*pIter)>=0x80 && pIter<pEnd );
+    nPayload &= 0xffffffff;
   }
   pIter++;
 
@@ -73764,11 +74470,10 @@ static void SQLITE_APICALL btreeParseCellPtr(
   pIter++;
 
   pInfo->nKey = *(i64*)&iKey;
-  pInfo->nPayload = nPayload;
+  pInfo->nPayload = (u32)nPayload;
   pInfo->pPayload = pIter;
   testcase( nPayload==pPage->maxLocal );
   testcase( nPayload==(u32)pPage->maxLocal+1 );
-  assert( nPayload>=0 );
   assert( pPage->maxLocal <= BT_MAX_LOCAL );
   if( nPayload<=pPage->maxLocal ){
     /* This is the (easy) common case where the entire payload fits
@@ -74004,7 +74709,7 @@ static u16 SQLITE_APICALL cellSizePtrTableLeaf(MemPage *pPage, u8 *pCell){
 #ifdef SQLITE_DEBUG
 /* This variation on cellSizePtr() is used inside of assert() statements
 ** only. */
-static u16 SQLITE_APICALL cellSize(MemPage *pPage, int iCell){
+static u16 cellSize(MemPage *pPage, int iCell){
   return pPage->xCellSize(pPage, findCell(pPage, iCell));
 }
 #endif
@@ -75904,7 +76609,7 @@ page1_init_failed:
 ** is capable of reading or writing to the database.  Cursors that
 ** have been tripped into the CURSOR_FAULT state are not counted.
 */
-static int SQLITE_APICALL countValidCursors(BtShared *pBt, int wrOnly){
+static int countValidCursors(BtShared *pBt, int wrOnly){
   BtCursor *pCur;
   int r = 0;
   for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
@@ -76110,6 +76815,30 @@ static SQLITE_NOINLINE int SQLITE_APICALL btreeBeginTrans(
     }
 #endif
 
+#ifdef SQLITE_EXPERIMENTAL_PRAGMA_20251114
+    /* If both a read and write transaction will be opened by this call,
+    ** then issue a file-control as if the following pragma command had
+    ** been evaluated:
+    **
+    **     PRAGMA experimental_pragma_20251114 = 1|2
+    **
+    ** where the RHS is "1" if wrflag is 1 (RESERVED lock), or "2" if wrflag
+    ** is 2 (EXCLUSIVE lock). Ignore any result or error returned by the VFS.
+    **
+    ** WARNING: This code will likely remain part of SQLite only temporarily -
+    ** it exists to allow users to experiment with certain types of blocking
+    ** locks in custom VFS implementations. It MAY BE REMOVED AT ANY TIME.  */
+    if( pBt->pPage1==0 && wrflag ){
+      sqlite3_file *fd = sqlite3PagerFile(pPager);
+      char *aFcntl[3] = {0,0,0};
+      aFcntl[1] = "experimental_pragma_20251114";
+      assert( wrflag==1 || wrflag==2 );
+      aFcntl[2] = (wrflag==1 ? "1" : "2");
+      sqlite3OsFileControlHint(fd, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
+      sqlite3_free(aFcntl[0]);
+    }
+#endif
+
     /* Call lockBtree() until either pBt->pPage1 is populated or
     ** lockBtree() returns something other than SQLITE_OK. lockBtree()
     ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
@@ -77207,7 +77936,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCursorSize(void){
 ** closed with the BtCursor closes.  This is used within assert() statements
 ** only.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeClosesWithCursor(
+SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(
   Btree *pBtree,       /* the btree object */
   BtCursor *pCur       /* Corresponding cursor */
 ){
@@ -77280,7 +78009,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCloseCursor(BtCursor *pCur){
 ** Using this cache reduces the number of calls to btreeParseCell().
 */
 #ifndef NDEBUG
-  static int SQLITE_APICALL cellInfoEqual(CellInfo *a, CellInfo *b){
+  static int cellInfoEqual(CellInfo *a, CellInfo *b){
     if( a->nKey!=b->nKey ) return 0;
     if( a->pPayload!=b->pPayload ) return 0;
     if( a->nPayload!=b->nPayload ) return 0;
@@ -77288,7 +78017,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCloseCursor(BtCursor *pCur){
     if( a->nSize!=b->nSize ) return 0;
     return 1;
   }
-  static void SQLITE_APICALL assertCellInfo(BtCursor *pCur){
+  static void assertCellInfo(BtCursor *pCur){
     CellInfo info;
     memset(&info, 0, sizeof(info));
     btreeParseCell(pCur->pPage, pCur->ix, &info);
@@ -77312,7 +78041,7 @@ static SQLITE_NOINLINE void SQLITE_APICALL getCellInfo(BtCursor *pCur){
 ** that is currently pointing to a row in a (non-empty) table.
 ** This is a verification routine is used only within assert() statements.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeCursorIsValid(BtCursor *pCur){
+SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor *pCur){
   return pCur && pCur->eState==CURSOR_VALID;
 }
 #endif /* NDEBUG */
@@ -77557,7 +78286,7 @@ static int SQLITE_APICALL accessPayload(
 
   getCellInfo(pCur);
   aPayload = pCur->info.pPayload;
-  assert( offset+amt <= pCur->info.nPayload );
+  assert( (u64)offset+(u64)amt <= (u64)pCur->info.nPayload );
 
   assert( aPayload > pPage->aData );
   if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){
@@ -77598,7 +78327,9 @@ static int SQLITE_APICALL accessPayload(
     ** means "not yet known" (the cache is lazily populated).
     */
     if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){
-      int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
+      i64 nOvfl = pCur->info.nPayload;
+      testcase( nOvfl - pCur->info.nLocal + ovflSize - 1 > 0xffffffffU );
+      nOvfl = (nOvfl - pCur->info.nLocal + ovflSize-1)/ovflSize;
       if( pCur->aOverflow==0
        || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
       ){
@@ -77703,6 +78434,12 @@ static int SQLITE_APICALL accessPayload(
               (eOp==0 ? PAGER_GET_READONLY : 0)
           );
           if( rc==SQLITE_OK ){
+            if( eOp!=0
+             && (sqlite3PagerPageRefcount(pDbPage)!=1
+                 || NEVER(((MemPage*)sqlite3PagerGetExtra(pDbPage))->isInit)) ){
+              sqlite3PagerUnref(pDbPage);
+              return SQLITE_CORRUPT_PAGE(pPage);
+            }
             aPayload = sqlite3PagerGetData(pDbPage);
             nextPage = get4byte(aPayload);
             rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
@@ -77889,7 +78626,7 @@ static int SQLITE_APICALL moveToChild(BtCursor *pCur, u32 newPgno){
 ** cells in pParent, that page number iChild is the right-child of
 ** the page.
 */
-static void SQLITE_APICALL assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){
+static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){
   if( CORRUPT_DB ) return;  /* The conditions tested below might not be true
                             ** in a corrupt database */
   assert( iIdx<=pParent->nCell );
@@ -78114,7 +78851,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes)
 
   assert( cursorOwnsBtShared(pCur) );
   assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
-  if( pCur->eState==CURSOR_VALID ){
+  if( NEVER(pCur->eState==CURSOR_VALID) ){
     *pRes = 0;
     return SQLITE_OK;
   }
@@ -78136,7 +78873,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes)
 ** It is an internal verification routine and does not appear in production
 ** builds.
 */
-static int SQLITE_APICALL cursorIsAtLastEntry(BtCursor *pCur){
+static int cursorIsAtLastEntry(BtCursor *pCur){
   int ii;
   for(ii=0; ii<pCur->iPage; ii++){
     if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0;
@@ -82195,7 +82932,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3BtreeTransferRow(BtCursor *pDest, BtCur
       }while( rc==SQLITE_OK && nOut>0 );
 
       if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){
-        Pgno pgnoNew;
+        Pgno pgnoNew = 0;  /* Prevent harmless static-analyzer warning */
         MemPage *pNew = 0;
         rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
         put4byte(pPgnoOut, pgnoNew);
@@ -84784,7 +85521,7 @@ copy_finished:
 ** This routine is intended for use inside of assert() statements, like
 ** this:    assert( sqlite3VdbeCheckMemInvariants(pMem) );
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeCheckMemInvariants(Mem *p){
+SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
   /* If MEM_Dyn is set then Mem.xDel!=0.
   ** Mem.xDel might not be initialized if MEM_Dyn is clear.
   */
@@ -84861,21 +85598,27 @@ static void SQLITE_APICALL vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
   StrAccum acc;
   assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) );
   assert( sz>22 );
-  if( p->flags & MEM_Int ){
-#if GCC_VERSION>=7000000
-    /* Work-around for GCC bug
-    ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */
+  if( p->flags & (MEM_Int|MEM_IntReal) ){
+#if GCC_VERSION>=7000000 && GCC_VERSION<15000000 && defined(__i386__)
+    /* Work-around for GCC bug or bugs:
+    ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270
+    ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114659
+    ** The problem appears to be fixed in GCC 15 */
     i64 x;
-    assert( (p->flags&MEM_Int)*2==sizeof(x) );
-    memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2);
+    assert( (MEM_Str&~p->flags)*4==sizeof(x) );
+    memcpy(&x, (char*)&p->u, (MEM_Str&~p->flags)*4);
     p->n = sqlite3Int64ToText(x, zBuf);
 #else
     p->n = sqlite3Int64ToText(p->u.i, zBuf);
 #endif
+    if( p->flags & MEM_IntReal ){
+      memcpy(zBuf+p->n,".0", 3);
+      p->n += 2;
+    }
   }else{
     sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0);
-    sqlite3_str_appendf(&acc, "%!.15g",
-         (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r);
+    sqlite3_str_appendf(&acc, "%!.*g",
+         (p->db ? p->db->nFpDigit : 17), p->u.r);
     assert( acc.zText==zBuf && acc.mxAlloc<=0 );
     zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */
     p->n = acc.nChar;
@@ -84905,7 +85648,7 @@ static void SQLITE_APICALL vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
 **
 ** This routine is for use inside of assert() statements only.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemValidStrRep(Mem *p){
+SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){
   Mem tmp;
   char zBuf[100];
   char *z;
@@ -84924,6 +85667,9 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemValidStrRep(Mem *p){
     assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 );
   }
   if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1;
+  if( p->db==0 ){
+    return 1;  /* db->nFpDigit required to validate p->z[] */
+  }
   memcpy(&tmp, p, sizeof(tmp));
   vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp);
   z = p->z;
@@ -85074,13 +85820,16 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemClearAndResize(Mem *pMem, int sz
 **
 ** This is an optimization.  Correct operation continues even if
 ** this routine is a no-op.
+**
+** Return true if the strig is zero-terminated after this routine is
+** called and false if it is not.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
   if( (pMem->flags & (MEM_Str|MEM_Term|MEM_Ephem|MEM_Static))!=MEM_Str ){
     /* pMem must be a string, and it cannot be an ephemeral or static string */
-    return;
+    return 0;
   }
-  if( pMem->enc!=SQLITE_UTF8 ) return;
+  if( pMem->enc!=SQLITE_UTF8 ) return 0;
   assert( pMem->z!=0 );
   if( pMem->flags & MEM_Dyn ){
     if( pMem->xDel==sqlite3_free
@@ -85088,18 +85837,19 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
     ){
       pMem->z[pMem->n] = 0;
       pMem->flags |= MEM_Term;
-      return;
+      return 1;
     }
     if( pMem->xDel==sqlite3RCStrUnref ){
       /* Blindly assume that all RCStr objects are zero-terminated */
       pMem->flags |= MEM_Term;
-      return;
+      return 1;
     }
   }else if( pMem->szMalloc >= pMem->n+1 ){
     pMem->z[pMem->n] = 0;
     pMem->flags |= MEM_Term;
-    return;
+    return 1;
   }
+  return 0;
 }
 
 /*
@@ -85397,18 +86147,117 @@ SQLITE_PRIVATE i64 SQLITE_APICALL sqlite3VdbeIntValue(const Mem *pMem){
   }
 }
 
+/*
+** This routine implements the uncommon and slower path for
+** sqlite3MemRealValueRC() that has to deal with input strings
+** that are not UTF8 or that are not zero-terminated.  It is
+** broken out into a separate no-inline routine so that the
+** main sqlite3MemRealValueRC() routine can avoid unnecessary
+** stack pushes.
+**
+** A text->float translation of pMem->z is written into *pValue.
+**
+** Result code invariants:
+**
+**    rc==0         =>   ERROR: Input string not well-formed, or OOM
+**    rc<0          =>   Some prefix of the input is well-formed
+**    rc>0          =>   All of the input is well-formed
+**    (rc&2)==0     =>   The number is expressed as an integer, with no
+**                       decimal point or eNNN suffix.
+*/
+static SQLITE_NOINLINE int SQLITE_APICALL sqlite3MemRealValueRCSlowPath(
+  Mem *pMem,
+  double *pValue
+){
+  int rc = SQLITE_OK;
+  *pValue = 0.0;
+  if( pMem->enc==SQLITE_UTF8 ){
+    char *zCopy = sqlite3DbStrNDup(pMem->db, pMem->z, pMem->n);
+    if( zCopy ){
+      rc = sqlite3AtoF(zCopy, pValue);
+      sqlite3DbFree(pMem->db, zCopy);
+    }
+    return rc;
+  }else{
+    int n, i, j;
+    char *zCopy;
+    const char *z;
+
+    n = pMem->n & ~1;
+    zCopy = sqlite3DbMallocRaw(pMem->db, n/2 + 2);
+    if( zCopy ){
+      z = pMem->z;
+      if( pMem->enc==SQLITE_UTF16LE ){
+        for(i=j=0; i<n-1; i+=2, j++){
+          zCopy[j] = z[i];
+          if( z[i+1]!=0 ) break;
+        }
+      }else{
+        for(i=j=0; i<n-1; i+=2, j++){
+          if( z[i]!=0 ) break;
+          zCopy[j] = z[i+1];
+        }
+      }
+      assert( j<=n/2 );
+      zCopy[j] = 0;
+      rc = sqlite3AtoF(zCopy, pValue);
+      if( i<n ) rc = -100;
+      sqlite3DbFree(pMem->db, zCopy);
+    }
+    return rc;
+  }
+}
+
+/*
+** Invoke sqlite3AtoF() on the text value of pMem.  Write the
+** translation of the text input into *pValue.
+**
+** The caller must ensure that pMem->db!=0 and that pMem is in
+** mode MEM_Str or MEM_Blob.
+**
+** Result code invariants:
+**
+**    rc==0         =>   ERROR: Input string not well-formed, or OOM
+**    rc<0          =>   Some prefix of the input is well-formed
+**    rc>0          =>   All of the input is well-formed
+**    (rc&2)==0     =>   The number is expressed as an integer, with no
+**                       decimal point or eNNN suffix.
+*/
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3MemRealValueRC(Mem *pMem, double *pValue){
+  testcase( pMem->db==0 );
+  assert( pMem->flags & (MEM_Str|MEM_Blob) );
+  if( pMem->z==0 ){
+    *pValue = 0.0;
+    return 0;
+  }else if( pMem->enc==SQLITE_UTF8
+   && ((pMem->flags & MEM_Term)!=0 || sqlite3VdbeMemZeroTerminateIfAble(pMem))
+  ){
+    return sqlite3AtoF(pMem->z, pValue);
+  }else if( pMem->n==0 ){
+    *pValue = 0.0;
+    return 0;
+  }else{
+    return sqlite3MemRealValueRCSlowPath(pMem, pValue);
+  }
+}
+
+/*
+** This routine acts as a bridge from sqlite3VdbeRealValue() to
+** sqlite3VdbeRealValueRC, allowing sqlite3VdbeRealValue() to avoid
+** stuffing values onto the stack.
+*/
+static SQLITE_NOINLINE double SQLITE_APICALL sqlite3MemRealValueNoRC(Mem *pMem){
+  double r;
+  (void)sqlite3MemRealValueRC(pMem, &r);
+  return r;
+}
+
 /*
 ** Return the best representation of pMem that we can get into a
 ** double.  If pMem is already a double or an integer, return its
 ** value.  If it is a string or blob, try to convert it to a double.
 ** If it is a NULL, return 0.0.
 */
-static SQLITE_NOINLINE double SQLITE_APICALL memRealValue(Mem *pMem){
-  /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
-  double val = (double)0;
-  sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc);
-  return val;
-}
 SQLITE_PRIVATE double SQLITE_APICALL sqlite3VdbeRealValue(Mem *pMem){
   assert( pMem!=0 );
   assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
@@ -85419,7 +86268,7 @@ SQLITE_PRIVATE double SQLITE_APICALL sqlite3VdbeRealValue(Mem *pMem){
     testcase( pMem->flags & MEM_IntReal );
     return (double)pMem->u.i;
   }else if( pMem->flags & (MEM_Str|MEM_Blob) ){
-    return memRealValue(pMem);
+    return sqlite3MemRealValueNoRC(pMem);
   }else{
     /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
     return (double)0;
@@ -85543,8 +86392,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemNumerify(Mem *pMem){
     sqlite3_int64 ix;
     assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
     assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
-    rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
-    if( ((rc==0 || rc==1) && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1)
+    rc = sqlite3MemRealValueRC(pMem, &pMem->u.r);
+    if( ((rc&2)==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<2)
      || sqlite3RealSameAsInt(pMem->u.r, (ix = sqlite3RealToI64(pMem->u.r)))
     ){
       pMem->u.i = ix;
@@ -85745,7 +86594,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemSetDouble(Mem *pMem, double val
 ** Return true if the Mem holds a RowSet object.  This routine is intended
 ** for use inside of assert() statements.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemIsRowSet(const Mem *pMem){
+SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem *pMem){
   return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn)
          && pMem->xDel==sqlite3RowSetDelete;
 }
@@ -85797,7 +86646,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemTooBig(Mem *p){
 ** This is used for testing and debugging only - to help ensure that shallow
 ** copies (created by OP_SCopy) are not misused.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
+SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
   int i;
   Mem *pX;
   if( pMem->bScopy ){
@@ -86008,6 +86857,84 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemSetStr(
   return SQLITE_OK;
 }
 
+/* Like sqlite3VdbeMemSetStr() except:
+**
+**   enc is always SQLITE_UTF8
+**   pMem->db is always non-NULL
+*/
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemSetText(
+  Mem *pMem,          /* Memory cell to set to string value */
+  const char *z,      /* String pointer */
+  i64 n,              /* Bytes in string, or negative */
+  void (SQLITE_APICALL *xDel)(void*) /* Destructor function */
+){
+  i64 nByte = n;      /* New value for pMem->n */
+  u16 flags;
+
+  assert( pMem!=0 );
+  assert( pMem->db!=0 );
+  assert( sqlite3_mutex_held(pMem->db->mutex) );
+  assert( !sqlite3VdbeMemIsRowSet(pMem) );
+
+  /* If z is a NULL pointer, set pMem to contain an SQL NULL. */
+  if( !z ){
+    sqlite3VdbeMemSetNull(pMem);
+    return SQLITE_OK;
+  }
+
+  if( nByte<0 ){
+    nByte = strlen(z);
+    flags = MEM_Str|MEM_Term;
+  }else{
+    flags = MEM_Str;
+  }
+  if( nByte>(i64)pMem->db->aLimit[SQLITE_LIMIT_LENGTH] ){
+    if( xDel && xDel!=SQLITE_TRANSIENT ){
+      if( xDel==SQLITE_DYNAMIC ){
+        sqlite3DbFree(pMem->db, (void*)z);
+      }else{
+        xDel((void*)z);
+      }
+    }
+    sqlite3VdbeMemSetNull(pMem);
+    return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG);
+  }
+
+  /* The following block sets the new values of Mem.z and Mem.xDel. It
+  ** also sets a flag in local variable "flags" to indicate the memory
+  ** management (one of MEM_Dyn or MEM_Static).
+  */
+  if( xDel==SQLITE_TRANSIENT ){
+    i64 nAlloc = nByte + 1;
+    testcase( nAlloc==31 );
+    testcase( nAlloc==32 );
+    if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){
+      return SQLITE_NOMEM_BKPT;
+    }
+    assert( pMem->z!=0 );
+    memcpy(pMem->z, z, nByte);
+    pMem->z[nByte] = 0;
+  }else{
+    sqlite3VdbeMemRelease(pMem);
+    pMem->z = (char *)z;
+    if( xDel==SQLITE_DYNAMIC ){
+      pMem->zMalloc = pMem->z;
+      pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
+      pMem->xDel = 0;
+    }else if( xDel==SQLITE_STATIC ){
+      pMem->xDel = xDel;
+      flags |= MEM_Static;
+    }else{
+      pMem->xDel = xDel;
+      flags |= MEM_Dyn;
+    }
+  }
+  pMem->flags = flags;
+  pMem->n = (int)(nByte & 0x7fffffff);
+  pMem->enc = SQLITE_UTF8;
+  return SQLITE_OK;
+}
+
 /*
 ** Move data out of a btree key or data field and into a Mem structure.
 ** The data is payload from the entry that pCur is currently pointing
@@ -86031,7 +86958,12 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeMemFromBtree(
 ){
   int rc;
   pMem->flags = MEM_Null;
-  if( sqlite3BtreeMaxRecordSize(pCur)<offset+amt ){
+  testcase( amt==SQLITE_MAX_ALLOCATION_SIZE-1 );
+  testcase( amt==SQLITE_MAX_ALLOCATION_SIZE );
+  if( amt>=SQLITE_MAX_ALLOCATION_SIZE ){
+    return SQLITE_NOMEM_BKPT;
+  }
+  if( (u64)amt + (u64)offset > (u64)sqlite3BtreeMaxRecordSize(pCur) ){
     return SQLITE_CORRUPT_BKPT;
   }
   if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){
@@ -86431,7 +87363,7 @@ static int SQLITE_APICALL valueFromExpr(
     if( affinity==SQLITE_AFF_BLOB ){
       if( op==TK_FLOAT ){
         assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) );
-        sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8);
+        sqlite3AtoF(pVal->z, &pVal->u.r);
         pVal->flags = MEM_Real;
       }else if( op==TK_INTEGER ){
         /* This case is required by -9223372036854775808 and other strings
@@ -86699,6 +87631,11 @@ SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(
 **
 ** If *ppVal is initially NULL then the caller is responsible for
 ** ensuring that the value written into *ppVal is eventually freed.
+**
+** If the buffer does not contain a well-formed record, this routine may
+** read several bytes past the end of the buffer. Callers must therefore
+** ensure that any buffer which may contain a corrupt record is padded
+** with at least 8 bytes of addressable memory.
 */
 SQLITE_PRIVATE int sqlite3Stat4Column(
   sqlite3 *db,                    /* Database handle */
@@ -87024,7 +87961,7 @@ static int SQLITE_APICALL growOpArray(Vdbe *v, int nOp){
 **   sqlite3MisuseError(lineno)
 **   sqlite3CantopenError(lineno)
 */
-static void SQLITE_APICALL test_addop_breakpoint(int pc, Op *pOp){
+static void test_addop_breakpoint(int pc, Op *pOp){
   static u64 n = 0;
   (void)pc;
   (void)pOp;
@@ -87316,7 +88253,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeExplainParent(Parse *pParse){
 ** monitor the EXPLAIN QUERY PLAN code generation.
 */
 #if defined(SQLITE_DEBUG)
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ExplainBreakpoint(const char *z1, const char *z2){
+SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){
   (void)z1;
   (void)z2;
 }
@@ -87513,7 +88450,7 @@ struct VdbeOpIter {
   int iAddr;                 /* Address of next instruction to return */
   int iSub;                  /* 0 = main program, 1 = first sub-program etc. */
 };
-static Op *SQLITE_APICALL opIterNext(VdbeOpIter *p){
+static Op *opIterNext(VdbeOpIter *p){
   Vdbe *v = p->v;
   Op *pRet = 0;
   Op *aOp;
@@ -87580,7 +88517,7 @@ static Op *SQLITE_APICALL opIterNext(VdbeOpIter *p){
 **
 **   assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) );
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
+SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
   int hasAbort = 0;
   int hasFkCounter = 0;
   int hasCreateTable = 0;
@@ -87640,7 +88577,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbor
 ** Increment the nWrite counter in the VDBE if the cursor is not an
 ** ephemeral cursor, or if the cursor argument is NULL.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCursor *pC){
+SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCursor *pC){
   if( pC==0
    || (pC->eCurType!=CURTYPE_SORTER
        && pC->eCurType!=CURTYPE_PSEUDO
@@ -87656,7 +88593,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCurs
 ** Assert if an Abort at this point in time might result in a corrupt
 ** database.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeAssertAbortable(Vdbe *p){
+SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){
   assert( p->nWrite==0 || p->usesStmtJournal );
 }
 #endif
@@ -87806,7 +88743,7 @@ resolve_p2_values_loop_exit:
 ** is generated rather than an assert() or other error, so that ".eqp full"
 ** will still work to show the original bytecode, to aid in debugging.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeNoJumpsOutsideSubrtn(
+SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(
   Vdbe *v,          /* The byte-code program under construction */
   int iFirst,       /* First opcode of the subroutine */
   int iLast,        /* Last opcode of the subroutine */
@@ -87890,7 +88827,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeCurrentAddr(Vdbe *p){
 ** sqlite3VdbeAddOpList() will always be non-NULL.
 */
 #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){
+SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){
   assert( p->nOp + N <= p->nOpAlloc );
 }
 #endif
@@ -87903,7 +88840,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, in
 ** script.
 */
 #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeVerifyNoResultRow(Vdbe *p){
+SQLITE_PRIVATE void sqlite3VdbeVerifyNoResultRow(Vdbe *p){
   int i;
   for(i=0; i<p->nOp; i++){
     assert( p->aOp[i].opcode!=OP_ResultRow );
@@ -87917,7 +88854,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeVerifyNoResultRow(Vdbe *p){
 ** context.
 */
 #if defined(SQLITE_DEBUG)
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){
+SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){
   if( onError==OE_Abort ) sqlite3VdbeAddOp0(p, OP_Abortable);
 }
 #endif
@@ -88312,7 +89249,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
 ** Generate an OP_ReleaseReg opcode to indicate that a range of
 ** registers, except any identified by mask, are no longer in use.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeReleaseRegisters(
+SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters(
   Parse *pParse,       /* Parsing context */
   int iFirst,          /* Index of first register to be released */
   int N,               /* Number of registers to release */
@@ -88456,7 +89393,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index
 ** makes the code easier to read during debugging.  None of this happens
 ** in a production build.
 */
-static void SQLITE_APICALL vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){
+static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){
   assert( p->nOp>0 || p->aOp==0 );
   assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 );
   if( p->nOp ){
@@ -88530,7 +89467,7 @@ SQLITE_PRIVATE VdbeOp *SQLITE_APICALL sqlite3VdbeGetLastOp(Vdbe *p){
 ** Return an integer value for one of the parameters to the opcode pOp
 ** determined by character c.
 */
-static int SQLITE_APICALL translateP(char c, const Op *pOp){
+static int translateP(char c, const Op *pOp){
   if( c=='1' ) return pOp->p1;
   if( c=='2' ) return pOp->p2;
   if( c=='3' ) return pOp->p3;
@@ -88551,7 +89488,7 @@ static int SQLITE_APICALL translateP(char c, const Op *pOp){
 **       "PX@PY+1" ->  "r[X..X+Y]"    or "r[x]" if y is 0
 **       "PY..PY"  ->  "r[X..Y]"      or "r[x]" if y<=x
 */
-SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeDisplayComment(
+SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(
   sqlite3 *db,       /* Optional - Oom error reporting only */
   const Op *pOp,     /* The opcode to be commented */
   const char *zP4    /* Previously obtained value for P4 */
@@ -88817,6 +89754,10 @@ SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){
       zP4 = pOp->p4.pTab->zName;
       break;
     }
+    case P4_INDEX: {
+      zP4 = pOp->p4.pIdx->zName;
+      break;
+    }
     case P4_SUBRTNSIG: {
       SubrtnSig *pSig = pOp->p4.pSubrtnSig;
       sqlite3_str_appendf(&x, "subrtnsig:%d,%s", pSig->selId, pSig->zAff);
@@ -88918,7 +89859,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeLeave(Vdbe *p){
 /*
 ** Print a single opcode.  This routine is used for debugging only.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
+SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
   char *zP4;
   char *zCom;
   sqlite3 dummyDb;
@@ -89040,7 +89981,7 @@ static void SQLITE_APICALL releaseMemArray(Mem *p, int N){
 **
 ** This routine is intended for use inside of assert() statements only.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeFrameIsValid(VdbeFrame *pFrame){
+SQLITE_PRIVATE int sqlite3VdbeFrameIsValid(VdbeFrame *pFrame){
   if( pFrame->iFrameMagic!=SQLITE_FRAME_MAGIC ) return 0;
   return 1;
 }
@@ -89308,7 +90249,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeList(
 /*
 ** Print the SQL that was used to generate a VDBE program.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbePrintSql(Vdbe *p){
+SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe *p){
   const char *z = 0;
   if( p->zSql ){
     z = p->zSql;
@@ -89715,7 +90656,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeSetColName(
   }
   assert( p->aColName!=0 );
   pColName = &(p->aColName[idx+var*p->nResAlloc]);
-  rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel);
+  rc = sqlite3VdbeMemSetText(pColName, zName, -1, xDel);
   assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 );
   return rc;
 }
@@ -89994,7 +90935,7 @@ static int SQLITE_APICALL vdbeCommit(sqlite3 *db, Vdbe *p){
 ** This is a no-op if NDEBUG is defined.
 */
 #ifndef NDEBUG
-static void SQLITE_APICALL checkActiveVdbeCnt(sqlite3 *db){
+static void checkActiveVdbeCnt(sqlite3 *db){
   Vdbe *p;
   int cnt = 0;
   int nWrite = 0;
@@ -91106,7 +92047,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeRecordUnpack(
 ** Return true if the result of comparison is equivalent to desiredResult.
 ** Return false if there is a disagreement.
 */
-static int SQLITE_APICALL vdbeRecordCompareDebug(
+static int vdbeRecordCompareDebug(
   int nKey1, const void *pKey1, /* Left key */
   const UnpackedRecord *pPKey2, /* Right key */
   int desiredResult             /* Correct answer */
@@ -91227,7 +92168,7 @@ debugCompareEnd:
 ** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed
 ** incorrectly.
 */
-static void SQLITE_APICALL vdbeAssertFieldCountWithinLimits(
+static void vdbeAssertFieldCountWithinLimits(
   int nKey, const void *pKey,   /* The record to verify */
   const KeyInfo *pKeyInfo       /* Compare size with this KeyInfo */
 ){
@@ -91349,8 +92290,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int SQLITE_APICALL sqlite3BlobCompare(const Mem *
 ** optimizer code movement causes gcov to become very confused.
 */
 #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
-static int SQLITE_NOINLINE SQLITE_APICALL doubleLt(double a, double b){ return a<b; }
-static int SQLITE_NOINLINE SQLITE_APICALL doubleEq(double a, double b){ return a==b; }
+static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; }
+static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; }
 #endif
 
 /*
@@ -92207,6 +93148,223 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
   }
 }
 
+/*
+** Helper function for vdbeIsMatchingIndexKey(). Return true if column
+** iCol should be ignored when comparing a record with a record from
+** an index on disk. The field should be ignored if:
+**
+**   * the corresponding bit in mask is set, and
+**   * either:
+**       - bIntegrity is false, or
+**       - the two Mem values are both real values that differ by
+**         BTREE_ULPDISTORTION or fewer ULPs.
+*/
+static int SQLITE_APICALL vdbeSkipField(
+  Bitmask mask,                   /* Mask of indexed expression fields */
+  int iCol,                       /* Column of index being considered */
+  Mem *pMem1,                     /* Expected index value */
+  Mem *pMem2,                     /* Actual indexed value */
+  int bIntegrity                  /* True if running PRAGMA integrity_check */
+){
+#define BTREE_ULPDISTORTION 2
+  if( iCol>=BMS || (mask & MASKBIT(iCol))==0 ) return 0;
+  if( bIntegrity==0 ) return 1;
+  if( (pMem1->flags & MEM_Real) && (pMem2->flags & MEM_Real) ){
+    u64 m1, m2;
+    memcpy(&m1,&pMem1->u.r,8);
+    memcpy(&m2,&pMem2->u.r,8);
+    if( (m1<m2 ? m2-m1 : m1-m2) <= BTREE_ULPDISTORTION ){
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/*
+** This function compares the unpacked record with the current key that
+** cursor pCur points to. If bInt is false, all fields for which the
+** corresponding bit in parameter "mask" is set are ignored. Or, if
+** bInt is true, then a difference of BTREE_ULPDISTORTION or fewer ULPs
+** in real values is overlooked for fields with the corresponding bit
+** set in mask.
+**
+** Return the usual less than zero, zero, or greater than zero if the
+** remaining fields of the cursor cursor key are less than, equal to or
+** greater than those in (*p).
+*/
+static int SQLITE_APICALL vdbeIsMatchingIndexKey(
+  BtCursor *pCur,            /* Cursor open on index */
+  int bInt,                  /* True for integrity_check-style search */
+  Bitmask mask,              /* Mask of columns to skip */
+  UnpackedRecord *p,         /* Index key being deleted */
+  int *piRes                 /* 0 for a match, non-zero for not a match */
+){
+  u8 *aRec = 0;
+  u32 nRec = 0;
+  Mem mem;
+  int rc = SQLITE_OK;
+
+  memset(&mem, 0, sizeof(mem));
+  mem.enc = p->pKeyInfo->enc;
+  mem.db = p->pKeyInfo->db;
+  nRec = sqlite3BtreePayloadSize(pCur);
+  if( nRec>0x7fffffff ){
+    return SQLITE_CORRUPT_BKPT;
+  }
+
+  /* Allocate 5 extra bytes at the end of the buffer. This allows the
+  ** getVarint32() call below to read slightly past the end of the buffer
+  ** if the record is corrupt. */
+  aRec = sqlite3MallocZero(nRec+5);
+  if( aRec==0 ){
+    rc = SQLITE_NOMEM_BKPT;
+  }else{
+    rc = sqlite3BtreePayload(pCur, 0, nRec, aRec);
+  }
+
+  if( rc==SQLITE_OK ){
+    u32 szHdr = 0;                /* Size of record header in bytes */
+    u32 idxHdr = 0;               /* Current index in header */
+
+    idxHdr = getVarint32(aRec, szHdr);
+    if( szHdr>98307 ){
+      rc = SQLITE_CORRUPT;
+    }else{
+      int res = 0;                /* Result of this function call */
+      u32 idxRec = szHdr;         /* Index of next field in record body */
+      int ii = 0;                 /* Iterator variable */
+
+      int nCol = p->pKeyInfo->nAllField;
+      for(ii=0; ii<nCol && rc==SQLITE_OK; ii++){
+        u32 iSerial = 0;
+        int nSerial = 0;
+
+        if( idxHdr>=szHdr ){
+          rc = SQLITE_CORRUPT_BKPT;
+          break;
+        }
+        idxHdr += getVarint32(&aRec[idxHdr], iSerial);
+        nSerial = sqlite3VdbeSerialTypeLen(iSerial);
+        if( (idxRec+nSerial)>nRec ){
+          rc = SQLITE_CORRUPT_BKPT;
+        }else{
+          sqlite3VdbeSerialGet(&aRec[idxRec], iSerial, &mem);
+          if( vdbeSkipField(mask, ii, &p->aMem[ii], &mem, bInt)==0 ){
+            res = sqlite3MemCompare(&mem, &p->aMem[ii], p->pKeyInfo->aColl[ii]);
+            if( res!=0 ) break;
+          }
+        }
+        idxRec += sqlite3VdbeSerialTypeLen(iSerial);
+      }
+
+      *piRes = res;
+    }
+  }
+
+  sqlite3_free(aRec);
+  return rc;
+}
+
+/*
+** This is called when the record in (*p) should be found in the index
+** opened by cursor pCur, but was not. This may happen as part of a DELETE
+** operation or an integrity check.
+**
+** One reason that an exact match was not found may be the EIIB bug - that
+** a text-to-float conversion may have caused a real value in record (*p)
+** to be slightly different from its counterpart on disk. This function
+** attempts to find the right index record. If it does find the right
+** record, it leaves *pCur pointing to it and sets (*pRes) to 0 before
+** returning. Otherwise, (*pRes) is set to non-zero and an SQLite error
+** code returned.
+**
+** The algorithm used to find the correct record is:
+**
+**   * Scan up to BTREE_FDK_RANGE entries either side of the current entry.
+**     If parameter bIntegrity is false, then all fields that are indexed
+**     expressions or virtual table columns are omitted from the comparison.
+**     If bIntegrity is true, then small differences in real values in
+**     such fields are overlooked, but they are not omitted from the comparison
+**     altogether.
+**
+**   * If the above fails to find an entry and bIntegrity is false, search
+**     the entire index.
+*/
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3VdbeFindIndexKey(
+  BtCursor *pCur,
+  Index *pIdx,
+  UnpackedRecord *p,
+  int *pRes,
+  int bIntegrity
+){
+#define BTREE_FDK_RANGE 10
+  int nStep = 0;
+  int res = 1;
+  int rc = SQLITE_OK;
+  int ii = 0;
+
+  /* Calculate a mask based on the first 64 columns of the index. The mask
+  ** bit is set if the corresponding index field is either an expression
+  ** or a virtual column of the table.  */
+  Bitmask mask = 0;
+  for(ii=0; ii<MIN(pIdx->nColumn, BMS); ii++){
+    int iCol = pIdx->aiColumn[ii];
+    if( (iCol==XN_EXPR)
+     || (iCol>=0 && (pIdx->pTable->aCol[iCol].colFlags & COLFLAG_VIRTUAL))
+    ){
+      mask |= MASKBIT(ii);
+    }
+  }
+
+  /* If the mask is 0 at this point, then the index contains no expressions
+  ** or virtual columns. So do not search for a match - return so that the
+  ** caller may declare the db corrupt immediately. Or, if mask is non-zero,
+  ** proceed.  */
+  if( mask!=0 ){
+
+    /* Move the cursor back BTREE_FDK_RANGE entries. If this hits an EOF,
+    ** position the cursor at the first entry in the index and set nStep
+    ** to -1 so that the first loop below scans the entire index. Otherwise,
+    ** set nStep to BTREE_FDK_RANGE*2 so that the first loop below scans
+    ** just that many entries.  */
+    for(ii=0; sqlite3BtreeEof(pCur)==0 && ii<BTREE_FDK_RANGE; ii++){
+      rc = sqlite3BtreePrevious(pCur, 0);
+    }
+    if( rc==SQLITE_DONE ){
+      rc = sqlite3BtreeFirst(pCur, &res);
+      nStep = -1;
+    }else{
+      nStep = BTREE_FDK_RANGE*2;
+    }
+
+    /* This loop runs at most twice to search for a key with matching PK
+    ** fields in the index. The second iteration always searches the entire
+    ** index. The first iteration searches nStep entries starting with the
+    ** current cursor entry if (nStep>=0), or the entire index if (nStep<0).  */
+    while( sqlite3BtreeCursorIsValidNN(pCur) ){
+      for(ii=0; rc==SQLITE_OK && (ii<nStep || nStep<0); ii++){
+        rc = vdbeIsMatchingIndexKey(pCur, bIntegrity, mask, p, &res);
+        if( res==0 || rc!=SQLITE_OK ) break;
+        rc = sqlite3BtreeNext(pCur, 0);
+      }
+      if( rc==SQLITE_DONE ){
+        rc = SQLITE_OK;
+        assert( res!=0 );
+      }
+      if( nStep<0 || rc!=SQLITE_OK || res==0 || bIntegrity ) break;
+
+      /* The first, non-exhaustive, search failed to find an entry with
+      ** matching PK fields. So restart for an exhaustive search of the
+      ** entire index.  */
+      nStep = -1;
+      rc = sqlite3BtreeFirst(pCur, &res);
+    }
+  }
+
+  *pRes = res;
+  return rc;
+}
+
 #ifndef SQLITE_OMIT_DATETIME_FUNCS
 /*
 ** Cause a function to throw an error if it was call from OP_PureFunc
@@ -92793,7 +93951,23 @@ static void SQLITE_APICALL setResultStrOrError(
   void (SQLITE_APICALL *xDel)(void*)     /* Destructor function */
 ){
   Mem *pOut = pCtx->pOut;
-  int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel);
+  int rc;
+  if( enc==SQLITE_UTF8 ){
+    rc = sqlite3VdbeMemSetText(pOut, z, n, xDel);
+  }else if( enc==SQLITE_UTF8_ZT ){
+    /* It is usually considered improper to assert() on an input. However,
+    ** the following assert() is checking for inputs that are documented
+    ** to result in undefined behavior. */
+    assert( z==0
+         || n<0
+         || n>pOut->db->aLimit[SQLITE_LIMIT_LENGTH]
+         || z[n]==0
+    );
+    rc = sqlite3VdbeMemSetText(pOut, z, n, xDel);
+    pOut->flags |= MEM_Term;
+  }else{
+    rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel);
+  }
   if( rc ){
     if( rc==SQLITE_TOOBIG ){
       sqlite3_result_error_toobig(pCtx);
@@ -92986,7 +94160,7 @@ SQLITE_API void SQLITE_APICALL sqlite3_result_text64(
 #endif
   assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
   assert( xDel!=SQLITE_DYNAMIC );
-  if( enc!=SQLITE_UTF8 ){
+  if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){
     if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
     n &= ~(u64)1;
   }
@@ -93137,6 +94311,8 @@ static int SQLITE_APICALL doWalCallbacks(sqlite3 *db){
       }
     }
   }
+#else
+  UNUSED_PARAMETER(db);
 #endif
   return rc;
 }
@@ -93444,7 +94620,7 @@ static int SQLITE_APICALL valueFromValueList(
     Mem sMem;     /* Raw content of current row */
     memset(&sMem, 0, sizeof(sMem));
     sz = sqlite3BtreePayloadSize(pRhs->pCsr);
-    rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem);
+    rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,sz,&sMem);
     if( rc==SQLITE_OK ){
       u8 *zBuf = (u8*)sMem.z;
       u32 iSerial;
@@ -94093,13 +95269,25 @@ static int SQLITE_APICALL bindText(
     assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
     if( zData!=0 ){
       pVar = &p->aVar[i-1];
-      rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
-      if( rc==SQLITE_OK ){
-        if( encoding==0 ){
-          pVar->enc = ENC(p->db);
-        }else{
-          rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
-        }
+      if( encoding==SQLITE_UTF8 ){
+        rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel);
+      }else if( encoding==SQLITE_UTF8_ZT ){
+        /* It is usually consider improper to assert() on an input.
+        ** However, the following assert() is checking for inputs
+        ** that are documented to result in undefined behavior. */
+        assert( zData==0
+             || nData<0
+             || nData>pVar->db->aLimit[SQLITE_LIMIT_LENGTH]
+             || ((u8*)zData)[nData]==0
+        );
+        rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel);
+        pVar->flags |= MEM_Term;
+      }else{
+        rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
+        if( encoding==0 ) pVar->enc = ENC(p->db);
+      }
+      if( rc==SQLITE_OK && encoding!=0 ){
+        rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
       }
       if( rc ){
         sqlite3Error(p->db, rc);
@@ -94211,7 +95399,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_bind_text64(
   unsigned char enc
 ){
   assert( xDel!=SQLITE_DYNAMIC );
-  if( enc!=SQLITE_UTF8 ){
+  if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){
     if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
     nData &= ~(u64)1;
   }
@@ -95248,17 +96436,19 @@ SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeExpandSql(
 #ifndef SQLITE_HWTIME_H
 #define SQLITE_HWTIME_H
 
-/*
-** The following routine only works on Pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value.  This can be used for high-res
-** profiling.
-*/
-#if !defined(__STRICT_ANSI__) && \
-    (defined(__GNUC__) || defined(_MSC_VER)) && \
-    (defined(i386) || defined(__i386__) || defined(_M_IX86))
+#if defined(_MSC_VER) && defined(_WIN32)
+
+/*   #include "windows.h" */
+  #include <profileapi.h>
+
+  __inline sqlite3_uint64 sqlite3Hwtime(void){
+    LARGE_INTEGER tm;
+    QueryPerformanceCounter(&tm);
+    return (sqlite3_uint64)tm.QuadPart;
+  }
 
-  #if defined(__GNUC__)
+#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && \
+    (defined(i386) || defined(__i386__) || defined(_M_IX86))
 
   __inline__ sqlite_uint64 sqlite3Hwtime(void){
      unsigned int lo, hi;
@@ -95266,17 +96456,6 @@ SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeExpandSql(
      return (sqlite_uint64)hi << 32 | lo;
   }
 
-  #elif defined(_MSC_VER)
-
-  __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
-     __asm {
-        rdtsc
-        ret       ; return value at EDX:EAX
-     }
-  }
-
-  #endif
-
 #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
 
   __inline__ sqlite_uint64 sqlite3Hwtime(void){
@@ -95285,6 +96464,14 @@ SQLITE_PRIVATE char *SQLITE_APICALL sqlite3VdbeExpandSql(
      return (sqlite_uint64)hi << 32 | lo;
   }
 
+#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) &&  defined(__aarch64__)
+
+  __inline__ sqlite_uint64 sqlite3Hwtime(void){
+     sqlite3_uint64 cnt;
+     __asm__ __volatile__ ("mrs %0, cntvct_el0" : "=r" (cnt));
+     return cnt;
+  }
+
 #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))
 
   __inline__ sqlite_uint64 sqlite3Hwtime(void){
@@ -95430,7 +96617,7 @@ SQLITE_API int sqlite3_found_count = 0;
 **   sqlite3MisuseError(lineno)
 **   sqlite3CantopenError(lineno)
 */
-static void SQLITE_APICALL test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
+static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
   static u64 n = 0;
   (void)pc;
   (void)pOp;
@@ -95643,12 +96830,11 @@ static int SQLITE_APICALL alsoAnInt(Mem *pRec, double rValue, i64 *piValue){
 */
 static void SQLITE_APICALL applyNumericAffinity(Mem *pRec, int bTryForInt){
   double rValue;
-  u8 enc = pRec->enc;
   int rc;
   assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_IntReal))==MEM_Str );
-  rc = sqlite3AtoF(pRec->z, &rValue, pRec->n, enc);
+  rc = sqlite3MemRealValueRC(pRec, &rValue);
   if( rc<=0 ) return;
-  if( rc==1 && alsoAnInt(pRec, rValue, &pRec->u.i) ){
+  if( (rc&2)==0 && alsoAnInt(pRec, rValue, &pRec->u.i) ){
     pRec->flags |= MEM_Int;
   }else{
     pRec->u.r = rValue;
@@ -95728,7 +96914,10 @@ SQLITE_API int SQLITE_APICALL sqlite3_value_numeric_type(sqlite3_value *pVal){
   int eType = sqlite3_value_type(pVal);
   if( eType==SQLITE_TEXT ){
     Mem *pMem = (Mem*)pVal;
+    assert( pMem->db!=0 );
+    sqlite3_mutex_enter(pMem->db->mutex);
     applyNumericAffinity(pMem, 0);
+    sqlite3_mutex_leave(pMem->db->mutex);
     eType = sqlite3_value_type(pVal);
   }
   return eType;
@@ -95761,15 +96950,15 @@ static u16 SQLITE_NOINLINE SQLITE_APICALL computeNumericType(Mem *pMem){
     pMem->u.i = 0;
     return MEM_Int;
   }
-  rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
+  rc = sqlite3MemRealValueRC(pMem, &pMem->u.r);
   if( rc<=0 ){
-    if( rc==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){
+    if( (rc&2)==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){
       pMem->u.i = ix;
       return MEM_Int;
     }else{
       return MEM_Real;
     }
-  }else if( rc==1 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){
+  }else if( (rc&2)==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){
     pMem->u.i = ix;
     return MEM_Int;
   }
@@ -95804,7 +96993,7 @@ static u16 SQLITE_APICALL numericType(Mem *pMem){
 ** Write a nice string representation of the contents of cell pMem
 ** into buffer zBuf, length nBuf.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){
+SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){
   int f = pMem->flags;
   static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"};
   if( f&MEM_Blob ){
@@ -95867,7 +97056,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum
 /*
 ** Print the value of a register for tracing purposes:
 */
-static void SQLITE_APICALL memTracePrint(Mem *p){
+static void memTracePrint(Mem *p){
   if( p->flags & MEM_Undefined ){
     printf(" undefined");
   }else if( p->flags & MEM_Null ){
@@ -95893,7 +97082,7 @@ static void SQLITE_APICALL memTracePrint(Mem *p){
   }
   if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype);
 }
-static void SQLITE_APICALL registerTrace(int iReg, Mem *p){
+static void registerTrace(int iReg, Mem *p){
   printf("R[%d] = ", iReg);
   memTracePrint(p);
   if( p->pScopyFrom ){
@@ -95903,7 +97092,7 @@ static void SQLITE_APICALL registerTrace(int iReg, Mem *p){
   printf("\n");
   sqlite3VdbeCheckMemInvariants(p);
 }
-/**/ void SQLITE_APICALL sqlite3PrintMem(Mem *pMem){
+/**/ void sqlite3PrintMem(Mem *pMem){
   memTracePrint(pMem);
   printf("\n");
   fflush(stdout);
@@ -95915,7 +97104,7 @@ static void SQLITE_APICALL registerTrace(int iReg, Mem *p){
 ** Show the values of all registers in the virtual machine.  Used for
 ** interactive debugging.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeRegisterDump(Vdbe *v){
+SQLITE_PRIVATE void sqlite3VdbeRegisterDump(Vdbe *v){
   int i;
   for(i=1; i<v->nMem; i++) registerTrace(i, v->aMem+i);
 }
@@ -95939,7 +97128,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3VdbeRegisterDump(Vdbe *v){
 **
 **     assert( checkSavepointCount(db) );
 */
-static int SQLITE_APICALL checkSavepointCount(sqlite3 *db){
+static int checkSavepointCount(sqlite3 *db){
   int n = 0;
   Savepoint *p;
   for(p=db->pSavepoint; p; p=p->pNext) n++;
@@ -101910,20 +103099,17 @@ case OP_SorterInsert: {     /* in2 */
   break;
 }
 
-/* Opcode: IdxDelete P1 P2 P3 * P5
+/* Opcode: IdxDelete P1 P2 P3 P4 *
 ** Synopsis: key=r[P2@P3]
 **
 ** The content of P3 registers starting at register P2 form
 ** an unpacked index key. This opcode removes that entry from the
 ** index opened by cursor P1.
 **
-** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error
-** if no matching index entry is found.  This happens when running
-** an UPDATE or DELETE statement and the index entry to be updated
-** or deleted is not found.  For some uses of IdxDelete
-** (example:  the EXCEPT operator) it does not matter that no matching
-** entry is found.  For those cases, P5 is zero.  Also, do not raise
-** this (self-correcting and non-critical) error if in writable_schema mode.
+** P4 is a pointer to an Index structure.
+**
+** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found
+** and not in writable_schema mode.
 */
 case OP_IdxDelete: {
   VdbeCursor *pC;
@@ -101946,13 +103132,22 @@ case OP_IdxDelete: {
   r.aMem = &aMem[pOp->p2];
   rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
   if( rc ) goto abort_due_to_error;
-  if( res==0 ){
-    rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
-    if( rc ) goto abort_due_to_error;
-  }else if( pOp->p5 && !sqlite3WritableSchema(db) ){
-    rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
-    goto abort_due_to_error;
+  if( res!=0 ){
+    rc = sqlite3VdbeFindIndexKey(pCrsr, pOp->p4.pIdx, &r, &res, 0);
+    if( rc!=SQLITE_OK ) goto abort_due_to_error;
+    if( res!=0 ){
+      if( !sqlite3WritableSchema(db) ){
+        rc = sqlite3ReportError(
+            SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
+        goto abort_due_to_error;
+      }
+      pC->cacheStatus = CACHE_STALE;
+      pC->seekResult = 0;
+      break;
+    }
   }
+  rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
+  if( rc ) goto abort_due_to_error;
   assert( pC->deferredMoveto==0 );
   pC->cacheStatus = CACHE_STALE;
   pC->seekResult = 0;
@@ -102579,6 +103774,58 @@ case OP_IntegrityCk: {
   sqlite3VdbeChangeEncoding(pIn1, encoding);
   goto check_for_interrupt;
 }
+
+/* Opcode: IFindKey P1 P2 P3 P4 *
+**
+** This instruction always follows an OP_Found with the same P1, P2 and P3
+** values as this instruction and a non-zero P4 value. The P4 value to
+** this opcode is of type P4_INDEX and contains a pointer to the Index
+** object of for the index being searched.
+**
+** This opcode uses sqlite3VdbeFindIndexKey() to search around the current
+** cursor location for an index key that exactly matches all fields that
+** are not indexed expressions or references to VIRTUAL generated columns,
+** and either exactly match or are real numbers that are within 2 ULPs of
+** each other if the don't match.
+**
+** To put it another way, this opcode looks for nearby index entries that
+** are very close to the search key, but which might have small differences
+** in floating-point values that come via an expression.
+**
+** If no nearby alternative entry is found in cursor P1, then jump to P2.
+** But if a close match is found, fall through.
+**
+** This opcode is used by PRAGMA integrity_check to help distinguish
+** between truely corrupt indexes and expression indexes that are holding
+** floating-point values that are off by one or two ULPs.
+*/
+case OP_IFindKey: {     /* jump, in3 */
+  VdbeCursor *pC;
+  int res;
+  UnpackedRecord r;
+
+  assert( pOp[-1].opcode==OP_Found );
+  assert( pOp[-1].p1==pOp->p1 );
+  assert( pOp[-1].p3==pOp->p3 );
+  pC = p->apCsr[pOp->p1];
+  assert( pOp->p4type==P4_INDEX );
+  assert( pC->eCurType==CURTYPE_BTREE );
+  assert( pC->uc.pCursor!=0 );
+  assert( pC->isTable==0 );
+
+  memset(&r, 0, sizeof(r));
+  r.aMem = &aMem[pOp->p3];
+  r.nField = pOp->p4.pIdx->nColumn;
+  r.pKeyInfo = pC->pKeyInfo;
+
+  rc = sqlite3VdbeFindIndexKey(pC->uc.pCursor, pOp->p4.pIdx, &r, &res, 1);
+  if( rc || res!=0 ){
+    rc = SQLITE_OK;
+    goto jump_to_p2;
+  }
+  pC->nullRow = 0;
+  break;
+};
 #endif /* SQLITE_OMIT_INTEGRITY_CHECK */
 
 /* Opcode: RowSetAdd P1 P2 * * *
@@ -109881,6 +111128,7 @@ static int SQLITE_APICALL lookupName(
         pExpr->op = TK_FUNCTION;
         pExpr->u.zToken = "coalesce";
         pExpr->x.pList = pFJMatch;
+        pExpr->affExpr = SQLITE_AFF_DEFER;
         cnt = 1;
         goto lookupname_end;
       }else{
@@ -110043,12 +111291,32 @@ static int SQLITE_APICALL exprProbability(Expr *p){
   double r = -1.0;
   if( p->op!=TK_FLOAT ) return -1;
   assert( !ExprHasProperty(p, EP_IntValue) );
-  sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
+  sqlite3AtoF(p->u.zToken, &r);
   assert( r>=0.0 );
   if( r>1.0 ) return -1;
   return (int)(r*134217728.0);
 }
 
+/*
+** Set the EP_SubtArg property on every expression inside of
+** pList.  If any subexpression is actually a subquery, then
+** also set the EP_SubtArg property on the first result-set
+** column of that subquery.
+*/
+static SQLITE_NOINLINE void SQLITE_APICALL resolveSetExprSubtypeArg(ExprList *pList){
+  int nn, ii;
+  nn = pList ? pList->nExpr : 0;
+  for(ii=0; ii<nn; ii++){
+    Expr *pExpr = pList->a[ii].pExpr;
+    ExprSetProperty(pExpr, EP_SubtArg);
+    if( pExpr->op==TK_SELECT ){
+      assert( ExprUseXSelect(pExpr) );
+      assert( pExpr->x.pSelect!=0 );
+      resolveSetExprSubtypeArg(pExpr->x.pSelect->pEList);
+    }
+  }
+}
+
 /*
 ** This routine is callback for sqlite3WalkExpr().
 **
@@ -110293,10 +111561,7 @@ static int SQLITE_APICALL resolveExprStep(Walker *pWalker, Expr *pExpr){
         if( (pDef->funcFlags & SQLITE_SUBTYPE)
          || ExprHasProperty(pExpr, EP_SubtArg)
         ){
-          int ii;
-          for(ii=0; ii<n; ii++){
-            ExprSetProperty(pList->a[ii].pExpr, EP_SubtArg);
-          }
+          resolveSetExprSubtypeArg(pList);
         }
 
         if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
@@ -110763,10 +112028,8 @@ static int SQLITE_APICALL resolveCompoundOrderBy(
         /* Convert the ORDER BY term into an integer column number iCol,
         ** taking care to preserve the COLLATE clause if it exists. */
         if( !IN_RENAME_OBJECT ){
-          Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
+          Expr *pNew = sqlite3ExprInt32(db, iCol);
           if( pNew==0 ) return 1;
-          pNew->flags |= EP_IntValue;
-          pNew->u.iValue = iCol;
           if( pItem->pExpr==pE ){
             pItem->pExpr = pNew;
           }else{
@@ -111120,10 +112383,6 @@ static int SQLITE_APICALL resolveSelectStep(Walker *pWalker, Select *p){
     }
 #endif
 
-    /* The ORDER BY and GROUP BY clauses may not refer to terms in
-    ** outer queries
-    */
-    sNC.pNext = 0;
     sNC.ncFlags |= NC_AllowAgg|NC_AllowWin;
 
     /* If this is a converted compound query, move the ORDER BY clause from
@@ -111186,6 +112445,14 @@ static int SQLITE_APICALL resolveSelectStep(Walker *pWalker, Select *p){
       return WRC_Abort;
     }
 
+    /* If the SELECT statement contains ON clauses that were moved into
+    ** the WHERE clause, go through and verify that none of the terms
+    ** in the ON clauses reference tables to the right of the ON clause. */
+    if( (p->selFlags & SF_OnToWhere) ){
+      sqlite3SelectCheckOnClauses(pParse, p);
+      if( pParse->nErr ) return WRC_Abort;
+    }
+
     /* Advance to the next term of the compound
     */
     p = p->pPrior;
@@ -112362,34 +113629,22 @@ SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3ExprAlloc(
   int dequote             /* True to dequote */
 ){
   Expr *pNew;
-  int nExtra = 0;
-  int iValue = 0;
+  int nExtra = pToken ? pToken->n+1 : 0;
 
   assert( db!=0 );
-  if( pToken ){
-    if( op!=TK_INTEGER || pToken->z==0
-          || sqlite3GetInt32(pToken->z, &iValue)==0 ){
-      nExtra = pToken->n+1;  /* tag-20240227-a */
-      assert( iValue>=0 );
-    }
-  }
   pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra);
   if( pNew ){
     memset(pNew, 0, sizeof(Expr));
     pNew->op = (u8)op;
     pNew->iAgg = -1;
-    if( pToken ){
-      if( nExtra==0 ){
-        pNew->flags |= EP_IntValue|EP_Leaf|(iValue?EP_IsTrue:EP_IsFalse);
-        pNew->u.iValue = iValue;
-      }else{
-        pNew->u.zToken = (char*)&pNew[1];
-        assert( pToken->z!=0 || pToken->n==0 );
-        if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n);
-        pNew->u.zToken[pToken->n] = 0;
-        if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){
-          sqlite3DequoteExpr(pNew);
-        }
+    if( nExtra ){
+      assert( pToken!=0 );
+      pNew->u.zToken = (char*)&pNew[1];
+      assert( pToken->z!=0 || pToken->n==0 );
+      if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n);
+      pNew->u.zToken[pToken->n] = 0;
+      if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){
+        sqlite3DequoteExpr(pNew);
       }
     }
 #if SQLITE_MAX_EXPR_DEPTH>0
@@ -112414,6 +113669,24 @@ SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3Expr(
   return sqlite3ExprAlloc(db, op, &x, 0);
 }
 
+/*
+** Allocate an expression for a 32-bit signed integer literal.
+*/
+SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3ExprInt32(sqlite3 *db, int iVal){
+  Expr *pNew = sqlite3DbMallocRawNN(db, sizeof(Expr));
+  if( pNew ){
+    memset(pNew, 0, sizeof(Expr));
+    pNew->op = TK_INTEGER;
+    pNew->iAgg = -1;
+    pNew->flags = EP_IntValue|EP_Leaf|(iVal?EP_IsTrue:EP_IsFalse);
+    pNew->u.iValue = iVal;
+#if SQLITE_MAX_EXPR_DEPTH>0
+    pNew->nHeight = 1;
+#endif
+  }
+  return pNew;
+}
+
 /*
 ** Attach subtrees pLeft and pRight to the Expr node pRoot.
 **
@@ -112576,7 +113849,7 @@ SQLITE_PRIVATE Expr *SQLITE_APICALL sqlite3ExprAnd(Parse *pParse, Expr *pLeft, E
     ){
       sqlite3ExprDeferredDelete(pParse, pLeft);
       sqlite3ExprDeferredDelete(pParse, pRight);
-      return sqlite3Expr(db, TK_INTEGER, "0");
+      return sqlite3ExprInt32(db, 0);
     }else{
       return sqlite3PExpr(pParse, TK_AND, pLeft, pRight);
     }
@@ -112701,7 +113974,9 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3ExprFunctionUsable(
 ){
   assert( !IN_RENAME_OBJECT );
   assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 );
-  if( ExprHasProperty(pExpr, EP_FromDDL) ){
+  if( ExprHasProperty(pExpr, EP_FromDDL)
+   || pParse->prepFlags & SQLITE_PREPARE_FROM_DDL
+  ){
     if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
      || (pParse->db->flags & SQLITE_TrustedSchema)==0
     ){
@@ -113397,9 +114672,7 @@ SQLITE_PRIVATE Select *SQLITE_APICALL sqlite3SelectDup(sqlite3 *db, const Select
     pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
     pNew->iLimit = 0;
     pNew->iOffset = 0;
-    pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral;
-    pNew->addrOpenEphm[0] = -1;
-    pNew->addrOpenEphm[1] = -1;
+    pNew->selFlags = p->selFlags;
     pNew->nSelectRow = p->nSelectRow;
     pNew->pWith = sqlite3WithDup(db, p->pWith);
 #ifndef SQLITE_OMIT_WINDOWFUNC
@@ -114051,7 +115324,7 @@ static int SQLITE_APICALL exprIsConst(Parse *pParse, Expr *p, int initFlag){
 
 /*
 ** Walk an expression tree.  Return non-zero if the expression is constant
-** and 0 if it involves variables or function calls.
+** or return zero if the expression involves variables or function calls.
 **
 ** For the purposes of this function, a double-quoted string (ex: "abc")
 ** is considered a variable but a single-quoted string (ex: 'abc') is
@@ -114841,6 +116114,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3FindInIndex(
     */
     u32 savedNQueryLoop = pParse->nQueryLoop;
     int rMayHaveNull = 0;
+    int bloomOk = (inFlags & IN_INDEX_MEMBERSHIP)!=0;
     eType = IN_INDEX_EPH;
     if( inFlags & IN_INDEX_LOOP ){
       pParse->nQueryLoop = 0;
@@ -114848,7 +116122,13 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3FindInIndex(
       *prRhsHasNull = rMayHaveNull = ++pParse->nMem;
     }
     assert( pX->op==TK_IN );
-    sqlite3CodeRhsOfIN(pParse, pX, iTab);
+    if( !bloomOk
+     && ExprUseXSelect(pX)
+     && (pX->x.pSelect->selFlags & SF_ClonedRhsIn)!=0
+    ){
+      bloomOk = 1;
+    }
+    sqlite3CodeRhsOfIN(pParse, pX, iTab, bloomOk);
     if( rMayHaveNull ){
       sqlite3SetHasNullFlag(v, iTab, rMayHaveNull);
     }
@@ -115006,7 +116286,8 @@ static int SQLITE_APICALL findCompatibleInRhsSubrtn(
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3CodeRhsOfIN(
   Parse *pParse,          /* Parsing context */
   Expr *pExpr,            /* The IN operator */
-  int iTab                /* Use this cursor number */
+  int iTab,               /* Use this cursor number */
+  int allowBloom          /* True to allow the use of a Bloom filter */
 ){
   int addrOnce = 0;           /* Address of the OP_Once instruction at top */
   int addr;                   /* Address of OP_OpenEphemeral instruction */
@@ -115128,7 +116409,10 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3CodeRhsOfIN(
       sqlite3SelectDestInit(&dest, SRT_Set, iTab);
       dest.zAffSdst = exprINAffinity(pParse, pExpr);
       pSelect->iLimit = 0;
-      if( addrOnce && OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){
+      if( addrOnce
+       && allowBloom
+       && OptimizationEnabled(pParse->db, SQLITE_BloomFilter)
+      ){
         int regBloom = ++pParse->nMem;
         addrBloom = sqlite3VdbeAddOp2(v, OP_Blob, 10000, regBloom);
         VdbeComment((v, "Bloom filter"));
@@ -115318,9 +116602,22 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3CodeSubselect(Parse *pParse, Expr *pExp
   pParse->nMem += nReg;
   if( pExpr->op==TK_SELECT ){
     dest.eDest = SRT_Mem;
-    dest.iSdst = dest.iSDParm;
+    if( (pSel->selFlags&SF_Distinct) && pSel->pLimit && pSel->pLimit->pRight ){
+      /* If there is both a DISTINCT and an OFFSET clause, then allocate
+      ** a separate dest.iSdst array for sqlite3Select() and other
+      ** routines to populate. In this case results will be copied over
+      ** into the dest.iSDParm array only after OFFSET processing. This
+      ** ensures that in the case where OFFSET excludes all rows, the
+      ** dest.iSDParm array is not left populated with the contents of the
+      ** last row visited - it should be all NULLs if all rows were
+      ** excluded by OFFSET.  */
+      dest.iSdst = pParse->nMem+1;
+      pParse->nMem += nReg;
+    }else{
+      dest.iSdst = dest.iSDParm;
+    }
     dest.nSdst = nReg;
-    sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
+    sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, pParse->nMem);
     VdbeComment((v, "Init subquery result"));
   }else{
     dest.eDest = SRT_Exists;
@@ -115336,7 +116633,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3CodeSubselect(Parse *pParse, Expr *pExp
      || (pLeft->u.iValue!=1 && pLeft->u.iValue!=0)
     ){
       sqlite3 *db = pParse->db;
-      pLimit = sqlite3Expr(db, TK_INTEGER, "0");
+      pLimit = sqlite3ExprInt32(db, 0);
       if( pLimit ){
         pLimit->affExpr = SQLITE_AFF_NUMERIC;
         pLimit = sqlite3PExpr(pParse, TK_NE,
@@ -115347,7 +116644,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3CodeSubselect(Parse *pParse, Expr *pExp
     }
   }else{
     /* If there is no pre-existing limit add a limit of 1 */
-    pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1");
+    pLimit = sqlite3ExprInt32(pParse->db, 1);
     pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
   }
   pSel->iLimit = 0;
@@ -115608,8 +116905,9 @@ static void SQLITE_APICALL sqlite3ExprCodeIN(
       if( ExprHasProperty(pExpr, EP_Subrtn) ){
         const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
         assert( pOp->opcode==OP_Once || pParse->nErr );
-        if( pOp->opcode==OP_Once && pOp->p3>0 ){  /* tag-202407032019 */
-          assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
+        if( pOp->p3>0 ){  /* tag-202407032019 */
+          assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter)
+                 || pParse->nErr );
           sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse,
                                rLhs, nVector); VdbeCoverage(v);
         }
@@ -115658,7 +116956,16 @@ static void SQLITE_APICALL sqlite3ExprCodeIN(
     CollSeq *pColl;
     int r3 = sqlite3GetTempReg(pParse);
     p = sqlite3VectorFieldSubexpr(pLeft, i);
-    pColl = sqlite3ExprCollSeq(pParse, p);
+    if( ExprUseXSelect(pExpr) ){
+      Expr *pRhs = pExpr->x.pSelect->pEList->a[i].pExpr;
+      pColl = sqlite3BinaryCompareCollSeq(pParse, p, pRhs);
+    }else{
+      /* If the RHS of the IN(...) expression are scalar expressions, do
+      ** not consider their collation sequences. The documentation says
+      ** "The collating sequence used for expressions of the form "x IN (y, z,
+      ** ...)" is the collating sequence of x.".  */
+      pColl = sqlite3ExprCollSeq(pParse, p);
+    }
     sqlite3VdbeAddOp3(v, OP_Column, iTab, i, r3);
     sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3,
                       (void*)pColl, P4_COLLSEQ);
@@ -115699,7 +117006,7 @@ sqlite3ExprCodeIN_oom_error:
 static void SQLITE_APICALL codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
   if( ALWAYS(z!=0) ){
     double value;
-    sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
+    sqlite3AtoF(z, &value);
     assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
     if( negateFlag ) value = -value;
     sqlite3VdbeAddOp4Dup8(v, OP_Real, 0, iMem, 0, (u8*)&value, P4_REAL);
@@ -116081,26 +117388,37 @@ static int SQLITE_APICALL exprCodeInlineFunction(
 }
 
 /*
-** Expression Node callback for sqlite3ExprCanReturnSubtype().
+** Expression Node callback for sqlite3ExprCanReturnSubtype().  If
+** pExpr is able to return a subtype, set pWalker->eCode and abort
+** the search.  If pExpr can never return a subtype, prune search.
 **
-** Only a function call is able to return a subtype.  So if the node
-** is not a function call, return WRC_Prune immediately.
+** The only expressions that can return a subtype are:
 **
-** A function call is able to return a subtype if it has the
-** SQLITE_RESULT_SUBTYPE property.
+**    1.  A function
+**    2.  The no-op "+" operator
+**    3.  A CASE...END expression
+**    4.  A CAST() expression
+**    5.  A "expr COLLATE colseq" expression.
 **
-** Assume that every function is able to pass-through a subtype from
-** one of its argument (using sqlite3_result_value()).  Most functions
-** are not this way, but we don't have a mechanism to distinguish those
-** that are from those that are not, so assume they all work this way.
-** That means that if one of its arguments is another function and that
-** other function is able to return a subtype, then this function is
-** able to return a subtype.
+** For any other kind of expression, prune the search.
+**
+** For case 1, the expression can yield a subtype if the function has
+** the SQLITE_RESULT_SUBTYPE property.  Functions can also return
+** a subtype (via sqlite3_result_value()) if any of the arguments can
+** return a subtype.
+**
+** In all cases 1 through 5, the expression might also return a subtype
+** if any operand can return a subtype.
 */
 static int SQLITE_APICALL exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){
   int n;
   FuncDef *pDef;
   sqlite3 *db;
+  if( pExpr->op==TK_CASE || pExpr->op==TK_UPLUS
+   || pExpr->op==TK_COLLATE || pExpr->op==TK_CAST
+  ){
+    return WRC_Continue;
+  }
   if( pExpr->op!=TK_FUNCTION ){
     return WRC_Prune;
   }
@@ -116110,7 +117428,7 @@ static int SQLITE_APICALL exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr)
   pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
   if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
     pWalker->eCode = 1;
-    return WRC_Prune;
+    return WRC_Abort;
   }
   return WRC_Continue;
 }
@@ -116558,7 +117876,7 @@ expr_code_doover:
     case TK_ISNOT:
       op = (op==TK_IS) ? TK_EQ : TK_NE;
       p5 = SQLITE_NULLEQ;
-      /* fall-through */
+      /* no break */ deliberate_fall_through
     case TK_LT:
     case TK_LE:
     case TK_GT:
@@ -118800,7 +120118,10 @@ static int SQLITE_APICALL analyzeAggregate(Walker *pWalker, Expr *pExpr){
       if( pIEpr==0 ) break;
       if( NEVER(!ExprUseYTab(pExpr)) ) break;
       for(i=0; i<pSrcList->nSrc; i++){
-         if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break;
+         if( pSrcList->a[i].iCursor==pIEpr->iDataCur ){
+           testcase( i>0 );
+           break;
+         }
       }
       if( i>=pSrcList->nSrc ) break;
       if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */
@@ -119047,7 +120368,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3TouchRegister(Parse *pParse, int iReg)
 ** greater is in permanent use, then return one more than that last
 ** permanent register.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3FirstAvailableRegister(Parse *pParse, int iMin){
+SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){
   const ExprList *pList = pParse->pConstExpr;
   if( pList ){
     int i;
@@ -119069,7 +120390,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3FirstAvailableRegister(Parse *pParse, i
 ** statements.
 */
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
+SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
   int i;
   if( pParse->nRangeReg>0
    && pParse->iRangeReg+pParse->nRangeReg > iFirst
@@ -119589,7 +120910,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterBeginAddColumn(Parse *pParse, Src
   /* Look up the table being altered. */
   assert( pParse->pNewTable==0 );
   assert( sqlite3BtreeHoldsAllMutexes(db) );
-  if( db->mallocFailed ) goto exit_begin_add_column;
+  if( NEVER(db->mallocFailed) ) goto exit_begin_add_column;
   pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
   if( !pTab ) goto exit_begin_add_column;
 
@@ -119661,7 +120982,7 @@ exit_begin_add_column:
 ** Or, if pTab is not a view or virtual table, zero is returned.
 */
 #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
-static int SQLITE_APICALL isRealTable(Parse *pParse, Table *pTab, int bDrop){
+static int SQLITE_APICALL isRealTable(Parse *pParse, Table *pTab, int iOp){
   const char *zType = 0;
 #ifndef SQLITE_OMIT_VIEW
   if( IsView(pTab) ){
@@ -119674,9 +120995,12 @@ static int SQLITE_APICALL isRealTable(Parse *pParse, Table *pTab, int bDrop){
   }
 #endif
   if( zType ){
+    const char *azMsg[] = {
+      "rename columns of", "drop column from", "edit constraints of"
+    };
+    assert( iOp>=0 && iOp<ArraySize(azMsg) );
     sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
-        (bDrop ? "drop column from" : "rename columns of"),
-        zType, pTab->zName
+        azMsg[iOp], zType, pTab->zName
     );
     return 1;
   }
@@ -119837,7 +121161,7 @@ struct RenameCtx {
 ** Technically, as x no longer points into a valid object or to the byte
 ** following a valid object, it may not be used in comparison operations.
 */
-static void SQLITE_APICALL renameTokenCheckAll(Parse *pParse, const void *pPtr){
+static void renameTokenCheckAll(Parse *pParse, const void *pPtr){
   assert( pParse==pParse->db->pParse );
   assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
   if( pParse->nErr==0 ){
@@ -120147,6 +121471,25 @@ static RenameToken *SQLITE_APICALL renameColumnTokenNext(RenameCtx *pCtx){
   return pBest;
 }
 
+/*
+** Set the error message of the context passed as the first argument to
+** the result of formatting zFmt using printf() style formatting.
+*/
+static void errorMPrintf(sqlite3_context *pCtx, const char *zFmt, ...){
+  sqlite3 *db = sqlite3_context_db_handle(pCtx);
+  char *zErr = 0;
+  va_list ap;
+  va_start(ap, zFmt);
+  zErr = sqlite3VMPrintf(db, zFmt, ap);
+  va_end(ap);
+  if( zErr ){
+    sqlite3_result_error(pCtx, zErr, -1);
+    sqlite3DbFree(db, zErr);
+  }else{
+    sqlite3_result_error_nomem(pCtx);
+  }
+}
+
 /*
 ** An error occurred while parsing or otherwise processing a database
 ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an
@@ -120444,8 +121787,8 @@ static int SQLITE_APICALL renameResolveTrigger(Parse *pParse){
       sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
       if( pParse->nErr ) rc = pParse->rc;
     }
-    if( rc==SQLITE_OK && pStep->zTarget ){
-      SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
+    if( rc==SQLITE_OK && pStep->pSrc ){
+      SrcList *pSrc = sqlite3SrcListDup(db, pStep->pSrc, 0);
       if( pSrc ){
         Select *pSel = sqlite3SelectNew(
             pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
@@ -120473,10 +121816,10 @@ static int SQLITE_APICALL renameResolveTrigger(Parse *pParse){
           pSel->pSrc = 0;
           sqlite3SelectDelete(db, pSel);
         }
-        if( pStep->pFrom ){
+        if( ALWAYS(pStep->pSrc) ){
           int i;
-          for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
-            SrcItem *p = &pStep->pFrom->a[i];
+          for(i=0; i<pStep->pSrc->nSrc && rc==SQLITE_OK; i++){
+            SrcItem *p = &pStep->pSrc->a[i];
             if( p->fg.isSubquery ){
               assert( p->u4.pSubq!=0 );
               sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0);
@@ -120545,13 +121888,13 @@ static void SQLITE_APICALL renameWalkTrigger(Walker *pWalker, Trigger *pTrigger)
       sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
       sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
     }
-    if( pStep->pFrom ){
+    if( pStep->pSrc ){
       int i;
-      SrcList *pFrom = pStep->pFrom;
-      for(i=0; i<pFrom->nSrc; i++){
-        if( pFrom->a[i].fg.isSubquery ){
-          assert( pFrom->a[i].u4.pSubq!=0 );
-          sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect);
+      SrcList *pSrc = pStep->pSrc;
+      for(i=0; i<pSrc->nSrc; i++){
+        if( pSrc->a[i].fg.isSubquery ){
+          assert( pSrc->a[i].u4.pSubq!=0 );
+          sqlite3WalkSelect(pWalker, pSrc->a[i].u4.pSubq->pSelect);
         }
       }
     }
@@ -120722,8 +122065,8 @@ static void SQLITE_APICALL renameColumnFunc(
     if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
 
     for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
-      if( pStep->zTarget ){
-        Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
+      if( pStep->pSrc ){
+        Table *pTarget = sqlite3LocateTableItem(&sParse, 0, &pStep->pSrc->a[0]);
         if( pTarget==pTab ){
           if( pStep->pUpsert ){
             ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
@@ -120735,7 +122078,6 @@ static void SQLITE_APICALL renameColumnFunc(
       }
     }
 
-
     /* Find tokens to edit in UPDATE OF clause */
     if( sParse.pTriggerTab==pTab ){
       renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
@@ -120937,13 +122279,10 @@ static void SQLITE_APICALL renameTableFunc(
           if( rc==SQLITE_OK ){
             renameWalkTrigger(&sWalker, pTrigger);
             for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
-              if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
-                renameTokenFind(&sParse, &sCtx, pStep->zTarget);
-              }
-              if( pStep->pFrom ){
+              if( pStep->pSrc ){
                 int i;
-                for(i=0; i<pStep->pFrom->nSrc; i++){
-                  SrcItem *pItem = &pStep->pFrom->a[i];
+                for(i=0; i<pStep->pSrc->nSrc; i++){
+                  SrcItem *pItem = &pStep->pSrc->a[i];
                   if( 0==sqlite3_stricmp(pItem->zName, zOld) ){
                     renameTokenFind(&sParse, &sCtx, pItem->zName);
                   }
@@ -121190,6 +122529,57 @@ static void SQLITE_APICALL renameTableTest(
 #endif
 }
 
+
+/*
+** Return the number of bytes until the end of the next non-whitespace and
+** non-comment token.  For the purpose of this function, a "(" token includes
+** all of the bytes through and including the matching ")", or until the
+** first illegal token, whichever comes first.
+**
+** Write the token type into *piToken.
+**
+** The value returned is the number of bytes in the token itself plus
+** the number of bytes of leading whitespace and comments skipped plus
+** all bytes through the next matching ")" if the token is TK_LP.
+**
+** Example:    (Note: '.' used in place of '*' in the example z[] text)
+**
+**                                    ,--------- *piToken := TK_RP
+**                                    v
+**    z[] = " /.comment./ --comment\n (two three four) five"
+**          |                                        |
+**          |<-------------------------------------->|
+**                              |
+**                              `--- return value
+*/
+static int SQLITE_APICALL getConstraintToken(const u8 *z, int *piToken){
+  int iOff = 0;
+  int t = 0;
+  do {
+    iOff += sqlite3GetToken(&z[iOff], &t);
+  }while( t==TK_SPACE || t==TK_COMMENT );
+
+  *piToken = t;
+
+  if( t==TK_LP ){
+    int nNest = 1;
+    while( nNest>0 ){
+      iOff += sqlite3GetToken(&z[iOff], &t);
+      if( t==TK_LP ){
+        nNest++;
+      }else if( t==TK_RP ){
+        t = TK_LP;
+        nNest--;
+      }else if( t==TK_ILLEGAL ){
+        break;
+      }
+    }
+  }
+
+  *piToken = t;
+  return iOff;
+}
+
 /*
 ** The implementation of internal UDF sqlite_drop_column().
 **
@@ -121234,15 +122624,24 @@ static void SQLITE_APICALL dropColumnFunc(
     goto drop_column_done;
   }
 
-  pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName);
   if( iCol<pTab->nCol-1 ){
     RenameToken *pEnd;
+    pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName);
     pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName);
     zEnd = (const char*)pEnd->t.z;
   }else{
+    int eTok;
     assert( IsOrdinaryTable(pTab) );
+    assert( iCol!=0 );
+    /* Point pCol->t.z at the "," immediately preceding the definition of
+    ** the column being dropped. To do this, start at the name of the
+    ** previous column, and tokenize until the next ",".  */
+    pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol-1].zCnName);
+    do {
+      pCol->t.z += getConstraintToken((const u8*)pCol->t.z, &eTok);
+    }while( eTok!=TK_COMMA );
+    pCol->t.z--;
     zEnd = (const char*)&zSql[pTab->u.tab.addColOffset];
-    while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
   }
 
   zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd);
@@ -121411,6 +122810,665 @@ exit_drop_column:
   sqlite3SrcListDelete(db, pSrc);
 }
 
+/*
+** Return the number of bytes of leading whitespace/comments in string z[].
+*/
+static int SQLITE_APICALL getWhitespace(const u8 *z){
+  int nRet = 0;
+  while( 1 ){
+    int t = 0;
+    int n = sqlite3GetToken(&z[nRet], &t);
+    if( t!=TK_SPACE && t!=TK_COMMENT ) break;
+    nRet += n;
+  }
+  return nRet;
+}
+
+
+/*
+** Argument z points into the body of a constraint - specifically the
+** second token of the constraint definition.  For a named constraint,
+** z points to the first token past the CONSTRAINT keyword.  For an
+** unnamed NOT NULL constraint, z points to the first byte past the NOT
+** keyword.
+**
+** Return the number of bytes until the end of the constraint.
+*/
+static int SQLITE_APICALL getConstraint(const u8 *z){
+  int iOff = 0;
+  int t = 0;
+
+  /* Now, the current constraint proceeds until the next occurence of one
+  ** of the following tokens:
+  **
+  **   CONSTRAINT, PRIMARY, NOT, UNIQUE, CHECK, DEFAULT,
+  **   COLLATE, REFERENCES, FOREIGN, GENERATED, AS, RP, or COMMA
+  **
+  ** Also exit the loop if ILLEGAL turns up.
+  */
+  while( 1 ){
+    int n = getConstraintToken(&z[iOff], &t);
+    if( t==TK_CONSTRAINT || t==TK_PRIMARY || t==TK_NOT || t==TK_UNIQUE
+     || t==TK_CHECK || t==TK_DEFAULT || t==TK_COLLATE || t==TK_REFERENCES
+     || t==TK_FOREIGN || t==TK_RP || t==TK_COMMA || t==TK_ILLEGAL
+     || t==TK_AS || t==TK_GENERATED
+    ){
+      break;
+    }
+    iOff += n;
+  }
+
+  return iOff;
+}
+
+/*
+** Compare two constraint names.
+**
+** Summary:   *pRes := zQuote != zCmp
+**
+** Details:
+** Compare the (possibly quoted) constraint name zQuote[0..nQuote-1]
+** against zCmp[].  Write zero into *pRes if they are the same and
+** non-zero if they differ.  Normally return SQLITE_OK, except if there
+** is an OOM, set the OOM error condition on ctx and return SQLITE_NOMEM.
+*/
+static int SQLITE_APICALL quotedCompare(
+  sqlite3_context *ctx,  /* Function context on which to report errors */
+  int t,                 /* Token type */
+  const u8 *zQuote,      /* Possibly quoted text.  Not zero-terminated. */
+  int nQuote,            /* Length of zQuote in bytes */
+  const u8 *zCmp,        /* Zero-terminated, unquoted name to compare against */
+  int *pRes              /* OUT: Set to 0 if equal, non-zero if unequal */
+){
+  char *zCopy = 0;       /* De-quoted, zero-terminated copy of zQuote[] */
+
+  if( t==TK_ILLEGAL ){
+    *pRes = 1;
+    return SQLITE_OK;
+  }
+  zCopy = sqlite3MallocZero(nQuote+1);
+  if( zCopy==0 ){
+    sqlite3_result_error_nomem(ctx);
+    return SQLITE_NOMEM_BKPT;
+  }
+  memcpy(zCopy, zQuote, nQuote);
+  sqlite3Dequote(zCopy);
+  *pRes = sqlite3_stricmp((const char*)zCopy, (const char*)zCmp);
+  sqlite3_free(zCopy);
+  return SQLITE_OK;
+}
+
+/*
+** zSql[] is a CREATE TABLE statement, supposedly.  Find the offset
+** into zSql[] of the first character past the first "(" and write
+** that offset into *piOff and return SQLITE_OK.  Or, if not found,
+** set the SQLITE_CORRUPT error code and return SQLITE_ERROR.
+*/
+static int SQLITE_APICALL skipCreateTable(sqlite3_context *ctx, const u8 *zSql, int *piOff){
+  int iOff = 0;
+
+  if( zSql==0 ) return SQLITE_ERROR;
+
+  /* Jump past the "CREATE TABLE" bit. */
+  while( 1 ){
+    int t = 0;
+    iOff += sqlite3GetToken(&zSql[iOff], &t);
+    if( t==TK_LP ) break;
+    if( t==TK_ILLEGAL ){
+      sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT);
+      return SQLITE_ERROR;
+    }
+  }
+
+  *piOff = iOff;
+  return SQLITE_OK;
+}
+
+/*
+** Internal SQL function sqlite3_drop_constraint():  Given an input
+** CREATE TABLE statement, return a revised CREATE TABLE statement
+** with a constraint removed.  Two forms, depending on the datatype
+** of argv[2]:
+**
+**   sqlite_drop_constraint(SQL, INT)  -- Omit NOT NULL from the INT-th column
+**   sqlite_drop_constraint(SQL, TEXT) -- OMIT constraint with name TEXT
+**
+** In the first case, the left-most column is 0.
+*/
+static void SQLITE_APICALL dropConstraintFunc(
+  sqlite3_context *ctx,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  const u8 *zSql = sqlite3_value_text(argv[0]);
+  const u8 *zCons = 0;
+  int iNotNull = -1;
+  int ii;
+  int iOff = 0;
+  int iStart = 0;
+  int iEnd = 0;
+  char *zNew = 0;
+  int t = 0;
+  sqlite3 *db;
+  UNUSED_PARAMETER(NotUsed);
+
+  if( zSql==0 ) return;
+
+  /* Jump past the "CREATE TABLE" bit. */
+  if( skipCreateTable(ctx, zSql, &iOff) ) return;
+
+  if( sqlite3_value_type(argv[1])==SQLITE_INTEGER ){
+    iNotNull = sqlite3_value_int(argv[1]);
+  }else{
+    zCons = sqlite3_value_text(argv[1]);
+  }
+
+  /* Search for the named constraint within column definitions. */
+  for(ii=0; iEnd==0; ii++){
+
+    /* Now parse the column or table constraint definition. Search
+    ** for the token CONSTRAINT if this is a DROP CONSTRAINT command, or
+    ** NOT in the right column if this is a DROP NOT NULL. */
+    while( 1 ){
+      iStart = iOff;
+      iOff += getConstraintToken(&zSql[iOff], &t);
+      if( t==TK_CONSTRAINT && (zCons || iNotNull==ii) ){
+        /* Check if this is the constraint we are searching for. */
+        int nTok = 0;
+        int cmp = 1;
+
+        /* Skip past any whitespace. */
+        iOff += getWhitespace(&zSql[iOff]);
+
+        /* Compare the next token - which may be quoted - with the name of
+        ** the constraint being dropped.  */
+        nTok = getConstraintToken(&zSql[iOff], &t);
+        if( zCons ){
+          if( quotedCompare(ctx, t, &zSql[iOff], nTok, zCons, &cmp) ) return;
+        }
+        iOff += nTok;
+
+        /* The next token is usually the first token of the constraint
+        ** definition. This is enough to tell the type of the constraint -
+        ** TK_NOT means it is a NOT NULL, TK_CHECK a CHECK constraint etc.
+        **
+        ** There is also the chance that the next token is TK_CONSTRAINT
+        ** (or TK_DEFAULT or TK_COLLATE), for example if a table has been
+        ** created as follows:
+        **
+        **    CREATE TABLE t1(cols, CONSTRAINT one CONSTRAINT two NOT NULL);
+        **
+        ** In this case, allow the "CONSTRAINT one" bit to be dropped by
+        ** this command if that is what is requested, or to advance to
+        ** the next iteration of the loop with &zSql[iOff] still pointing
+        ** to the CONSTRAINT keyword.  */
+        nTok = getConstraintToken(&zSql[iOff], &t);
+        if( t==TK_CONSTRAINT || t==TK_DEFAULT || t==TK_COLLATE
+         || t==TK_COMMA || t==TK_RP || t==TK_GENERATED || t==TK_AS
+        ){
+          t = TK_CHECK;
+        }else{
+          iOff += nTok;
+          iOff += getConstraint(&zSql[iOff]);
+        }
+
+        if( cmp==0 || (iNotNull>=0 && t==TK_NOT) ){
+          if( t!=TK_NOT && t!=TK_CHECK ){
+            errorMPrintf(ctx, "constraint may not be dropped: %s", zCons);
+            return;
+          }
+          iEnd = iOff;
+          break;
+        }
+
+      }else if( t==TK_NOT && iNotNull==ii ){
+        iEnd = iOff + getConstraint(&zSql[iOff]);
+        break;
+      }else if( t==TK_RP || t==TK_ILLEGAL ){
+        iEnd = -1;
+        break;
+      }else if( t==TK_COMMA ){
+        break;
+      }
+    }
+  }
+
+  /* If the constraint has not been found it is an error. */
+  if( iEnd<=0 ){
+    if( zCons ){
+      errorMPrintf(ctx, "no such constraint: %s", zCons);
+    }else{
+      /* SQLite follows postgres in that a DROP NOT NULL on a column that is
+      ** not NOT NULL is not an error. So just return the original SQL here. */
+      sqlite3_result_text(ctx, (const char*)zSql, -1, SQLITE_TRANSIENT);
+    }
+  }else{
+
+    /* Figure out if an extra space should be inserted after the constraint
+    ** is removed. And if an additional comma preceding the constraint
+    ** should be removed. */
+    const char *zSpace = " ";
+    iEnd += getWhitespace(&zSql[iEnd]);
+    sqlite3GetToken(&zSql[iEnd], &t);
+    if( t==TK_RP || t==TK_COMMA ){
+      zSpace = "";
+      if( zSql[iStart-1]==',' ) iStart--;
+    }
+
+    db = sqlite3_context_db_handle(ctx);
+    zNew = sqlite3MPrintf(db, "%.*s%s%s", iStart, zSql, zSpace, &zSql[iEnd]);
+    sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC);
+  }
+}
+
+/*
+** Internal SQL function:
+**
+**     sqlite_add_constraint(SQL, CONSTRAINT-TEXT, ICOL)
+**
+** SQL is a CREATE TABLE statement.  Return a modified version of
+** SQL that adds CONSTRAINT-TEXT at the end of the ICOL-th column
+** definition.  (The left-most column defintion is 0.)
+*/
+static void SQLITE_APICALL addConstraintFunc(
+  sqlite3_context *ctx,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  const u8 *zSql = sqlite3_value_text(argv[0]);
+  const char *zCons = (const char*)sqlite3_value_text(argv[1]);
+  int iCol = sqlite3_value_int(argv[2]);
+  int iOff = 0;
+  int ii;
+  char *zNew = 0;
+  int t = 0;
+  sqlite3 *db;
+  UNUSED_PARAMETER(NotUsed);
+
+  if( skipCreateTable(ctx, zSql, &iOff) ) return;
+
+  for(ii=0; ii<=iCol || (iCol<0 && t!=TK_RP); ii++){
+    iOff += getConstraintToken(&zSql[iOff], &t);
+    while( 1 ){
+      int nTok = getConstraintToken(&zSql[iOff], &t);
+      if( t==TK_COMMA || t==TK_RP ) break;
+      if( t==TK_ILLEGAL ){
+        sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT);
+        return;
+      }
+      iOff += nTok;
+    }
+  }
+
+  iOff += getWhitespace(&zSql[iOff]);
+
+  db = sqlite3_context_db_handle(ctx);
+  if( iCol<0 ){
+    zNew = sqlite3MPrintf(db, "%.*s, %s%s", iOff, zSql, zCons, &zSql[iOff]);
+  }else{
+    zNew = sqlite3MPrintf(db, "%.*s %s%s", iOff, zSql, zCons, &zSql[iOff]);
+  }
+  sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC);
+}
+
+/*
+** Find a column named pCol in table pTab. If successful, set output
+** parameter *piCol to the index of the column in the table and return
+** SQLITE_OK. Otherwise, set *piCol to -1 and return an SQLite error
+** code.
+*/
+static int SQLITE_APICALL alterFindCol(Parse *pParse, Table *pTab, Token *pCol, int *piCol){
+  sqlite3 *db = pParse->db;
+  char *zName = sqlite3NameFromToken(db, pCol);
+  int rc = SQLITE_NOMEM;
+  int iCol = -1;
+
+  if( zName ){
+    iCol = sqlite3ColumnIndex(pTab, zName);
+    if( iCol<0 ){
+      sqlite3ErrorMsg(pParse, "no such column: %s", zName);
+      rc = SQLITE_ERROR;
+    }else{
+      rc = SQLITE_OK;
+    }
+  }
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+  if( rc==SQLITE_OK ){
+    const char *zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
+    const char *zCol = pTab->aCol[iCol].zCnName;
+    if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){
+      pTab = 0;
+    }
+  }
+#endif
+
+  sqlite3DbFree(db, zName);
+  *piCol = iCol;
+  return rc;
+}
+
+
+/*
+** Find the table named by the first entry in source list pSrc. If successful,
+** return a pointer to the Table structure and set output variable (*pzDb)
+** to point to the name of the database containin the table (i.e. "main",
+** "temp" or the name of an attached database).
+**
+** If the table cannot be located, return NULL. The value of the two output
+** parameters is undefined in this case.
+*/
+static Table *SQLITE_APICALL alterFindTable(
+  Parse *pParse,        /* Parsing context */
+  SrcList *pSrc,        /* Name of the table to look for */
+  int *piDb,            /* OUT: write the iDb here */
+  const char **pzDb,    /* OUT: write name of schema here */
+  int bAuth             /* Do ALTER TABLE authorization checks if true */
+){
+  sqlite3 *db = pParse->db;
+  Table *pTab = 0;
+  assert( sqlite3BtreeHoldsAllMutexes(db) );
+  pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
+  if( pTab ){
+    int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+    *pzDb = db->aDb[iDb].zDbSName;
+    *piDb = iDb;
+
+    if( SQLITE_OK!=isRealTable(pParse, pTab, 2)
+     || SQLITE_OK!=isAlterableTable(pParse, pTab)
+    ){
+      pTab = 0;
+    }
+  }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+  if( pTab && bAuth ){
+    if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, *pzDb, pTab->zName, 0) ){
+      pTab = 0;
+    }
+  }
+#endif
+  sqlite3SrcListDelete(db, pSrc);
+  return pTab;
+}
+
+/*
+** Generate bytecode for one of:
+**
+**  (1)   ALTER TABLE pSrc DROP CONSTRAINT pCons
+**  (2)   ALTER TABLE pSrc ALTER pCol DROP NOT NULL
+**
+** One of pCons and pCol must be NULL and the other non-null.
+*/
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterDropConstraint(
+  Parse *pParse,    /* Parsing context */
+  SrcList *pSrc,    /* The table being altered */
+  Token *pCons,     /* Name of the constraint to drop */
+  Token *pCol       /* Name of the column from which to remove the NOT NULL */
+){
+  sqlite3 *db = pParse->db;
+  Table *pTab = 0;
+  int iDb = 0;
+  const char *zDb = 0;
+  char *zArg = 0;
+
+  assert( (pCol==0)!=(pCons==0) );
+  assert( pSrc->nSrc==1 );
+  pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, pCons!=0);
+  if( !pTab ) return;
+
+  if( pCons ){
+    char *z = sqlite3NameFromToken(db, pCons);
+    zArg = sqlite3MPrintf(db, "%Q", z);
+    sqlite3DbFree(db, z);
+  }else{
+    int iCol;
+    if( alterFindCol(pParse, pTab, pCol, &iCol) ) return;
+    zArg = sqlite3MPrintf(db, "%d", iCol);
+  }
+
+  /* Edit the SQL for the named table. */
+  sqlite3NestedParse(pParse,
+      "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
+      "sql = sqlite_drop_constraint(sql, %s) "
+      "WHERE type='table' AND tbl_name=%Q COLLATE nocase"
+      , zDb, zArg, pTab->zName
+  );
+  sqlite3DbFree(db, zArg);
+
+  /* Finally, reload the database schema. */
+  renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
+}
+
+/*
+** The implementation of SQL function sqlite_fail(MSG). This takes a single
+** argument, and returns it as an error message with the error code set to
+** SQLITE_CONSTRAINT.
+*/
+static void SQLITE_APICALL failConstraintFunc(
+  sqlite3_context *ctx,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  const char *zText = (const char*)sqlite3_value_text(argv[0]);
+  int err = sqlite3_value_int(argv[1]);
+  (void)NotUsed;
+  sqlite3_result_error(ctx, zText, -1);
+  sqlite3_result_error_code(ctx, err);
+}
+
+/*
+** Buffer pCons, which is nCons bytes in size, contains the text of a
+** NOT NULL or CHECK constraint that will be inserted into a CREATE TABLE
+** statement. If successful, this function returns the size of the buffer in
+** bytes not including any trailing whitespace or "--" style comments. Or,
+** if an OOM occurs, it returns 0 and sets db->mallocFailed to true.
+**
+** C-style comments at the end are preserved.  "--" style comments are
+** removed because the comment terminator might be \000, and we are about
+** to insert the pCons[] text into the middle of a larger string, and that
+** will have the effect of removing the comment terminator and messing up
+** the syntax.
+*/
+static int SQLITE_APICALL alterRtrimConstraint(
+  sqlite3 *db,                    /* used to record OOM error */
+  const char *pCons,              /* Buffer containing constraint */
+  int nCons                       /* Size of pCons in bytes */
+){
+  u8 *zTmp = (u8*)sqlite3MPrintf(db, "%.*s", nCons, pCons);
+  int iOff = 0;
+  int iEnd = 0;
+
+  if( zTmp==0 ) return 0;
+
+  while( 1 ){
+    int t = 0;
+    int nToken = sqlite3GetToken(&zTmp[iOff], &t);
+    if( t==TK_ILLEGAL ) break;
+    if( t!=TK_SPACE && (t!=TK_COMMENT || zTmp[iOff]!='-') ){
+      iEnd = iOff+nToken;
+    }
+    iOff += nToken;
+  }
+
+  sqlite3DbFree(db, zTmp);
+  return iEnd;
+}
+
+/*
+** Prepare a statement of the form:
+**
+**   ALTER TABLE pSrc ALTER pCol SET NOT NULL
+*/
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterSetNotNull(
+  Parse *pParse,   /* Parsing context */
+  SrcList *pSrc,   /* Name of the table being altered */
+  Token *pCol,     /* Name of the column to add a NOT NULL constraint to */
+  Token *pFirst    /* The NOT token of the NOT NULL constraint text */
+){
+  Table *pTab = 0;
+  int iCol = 0;
+  int iDb = 0;
+  const char *zDb = 0;
+  const char *pCons = 0;
+  int nCons = 0;
+
+  /* Look up the table being altered. */
+  assert( pSrc->nSrc==1 );
+  pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 0);
+  if( !pTab ) return;
+
+  /* Find the column being altered. */
+  if( alterFindCol(pParse, pTab, pCol, &iCol) ){
+    return;
+  }
+
+  /* Find the length in bytes of the constraint definition */
+  pCons = pFirst->z;
+  nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons);
+
+  /* Search for a constraint violation. Throw an exception if one is found. */
+  sqlite3NestedParse(pParse,
+      "SELECT sqlite_fail('constraint failed', %d) "
+      "FROM %Q.%Q AS x WHERE x.%.*s IS NULL",
+      SQLITE_CONSTRAINT, zDb, pTab->zName, (int)pCol->n, pCol->z
+  );
+
+  /* Edit the SQL for the named table. */
+  sqlite3NestedParse(pParse,
+      "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
+      "sql = sqlite_add_constraint(sqlite_drop_constraint(sql, %d), %.*Q, %d) "
+      "WHERE type='table' AND tbl_name=%Q COLLATE nocase"
+      , zDb, iCol, nCons, pCons, iCol, pTab->zName
+  );
+
+  /* Finally, reload the database schema. */
+  renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
+}
+
+/*
+** Implementation of internal SQL function:
+**
+**     sqlite_find_constraint(SQL, CONSTRAINT-NAME)
+**
+** This function returns true if the SQL passed as the first argument is a
+** CREATE TABLE that contains a constraint with the name CONSTRAINT-NAME,
+** or false otherwise.
+*/
+static void SQLITE_APICALL findConstraintFunc(
+  sqlite3_context *ctx,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  const u8 *zSql = 0;
+  const u8 *zCons = 0;
+  int iOff = 0;
+  int t = 0;
+
+  (void)NotUsed;
+  zSql = sqlite3_value_text(argv[0]);
+  zCons = sqlite3_value_text(argv[1]);
+
+  if( zSql==0 || zCons==0 ) return;
+  while( t!=TK_LP && t!=TK_ILLEGAL ){
+    iOff += sqlite3GetToken(&zSql[iOff], &t);
+  }
+
+  while( 1 ){
+    iOff += getConstraintToken(&zSql[iOff], &t);
+    if( t==TK_CONSTRAINT ){
+      int nTok = 0;
+      int cmp = 0;
+      iOff += getWhitespace(&zSql[iOff]);
+      nTok = getConstraintToken(&zSql[iOff], &t);
+      if( quotedCompare(ctx, t, &zSql[iOff], nTok, zCons, &cmp) ) return;
+      if( cmp==0 ){
+        sqlite3_result_int(ctx, 1);
+        return;
+      }
+    }else if( t==TK_ILLEGAL ){
+      break;
+    }
+  }
+
+  sqlite3_result_int(ctx, 0);
+}
+
+/*
+** Generate bytecode to implement:
+**
+**    ALTER TABLE pSrc ADD [CONSTRAINT pName] CHECK(pExpr)
+**
+** Any "ON CONFLICT" text that occurs after the "CHECK(...)", up
+** until pParse->sLastToken, is included as part of the new constraint.
+*/
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterAddConstraint(
+  Parse *pParse,           /* Parse context */
+  SrcList *pSrc,           /* Table to add constraint to */
+  Token *pFirst,           /* First token of new constraint */
+  Token *pName,            /* Name of new constraint. NULL if name omitted. */
+  const char *zExpr,       /* Text of CHECK expression */
+  int nExpr,               /* Size of pExpr in bytes */
+  Expr *pExpr              /* The parsed CHECK expression */
+){
+  Table *pTab = 0;         /* Table identified by pSrc */
+  int iDb = 0;             /* Which schema does pTab live in */
+  const char *zDb = 0;     /* Name of the schema in which pTab lives */
+  const char *pCons = 0;   /* Text of the constraint */
+  int nCons;               /* Bytes of text to use from pCons[] */
+  int rc;                  /* Result from error checking pExpr */
+
+  /* Look up the table being altered. */
+  assert( pSrc->nSrc==1 );
+  pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 1);
+  if( !pTab ){
+    sqlite3ExprDelete(pParse->db, pExpr);
+    return;
+  }
+
+  /* Verify that the new CHECK constraint does not contain any
+  ** internal-use-only function.  Forum post 2026-05-10T01:11:28Z
+  */
+  rc = sqlite3ResolveSelfReference(pParse, pTab, NC_IsCheck, pExpr, 0);
+  sqlite3ExprDelete(pParse->db, pExpr);
+  if( rc ) return;
+
+  /* If this new constraint has a name, check that it is not a duplicate of
+  ** an existing constraint. It is an error if it is.  */
+  if( pName ){
+    char *zName = sqlite3NameFromToken(pParse->db, pName);
+
+    sqlite3NestedParse(pParse,
+        "SELECT sqlite_fail('constraint %q already exists', %d) "
+        "FROM \"%w\"." LEGACY_SCHEMA_TABLE " "
+        "WHERE type='table' AND tbl_name=%Q COLLATE nocase "
+        "AND sqlite_find_constraint(sql, %Q)",
+        zName, SQLITE_ERROR, zDb, pTab->zName, zName
+    );
+    sqlite3DbFree(pParse->db, zName);
+  }
+
+  /* Search for a constraint violation. Throw an exception if one is found. */
+  sqlite3NestedParse(pParse,
+      "SELECT sqlite_fail('constraint failed', %d) "
+      "FROM %Q.%Q WHERE (%.*s) IS NOT TRUE",
+      SQLITE_CONSTRAINT, zDb, pTab->zName, nExpr, zExpr
+  );
+
+  /* Edit the SQL for the named table. */
+  pCons = pFirst->z;
+  nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons);
+
+  sqlite3NestedParse(pParse,
+      "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
+      "sql = sqlite_add_constraint(sql, %.*Q, -1) "
+      "WHERE type='table' AND tbl_name=%Q COLLATE nocase"
+      , zDb, nCons, pCons, pTab->zName
+  );
+
+  /* Finally, reload the database schema. */
+  renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
+}
+
 /*
 ** Register built-in functions used to help implement ALTER TABLE
 */
@@ -121421,6 +123479,10 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3AlterFunctions(void){
     INTERNAL_FUNCTION(sqlite_rename_test,    7, renameTableTest),
     INTERNAL_FUNCTION(sqlite_drop_column,    3, dropColumnFunc),
     INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc),
+    INTERNAL_FUNCTION(sqlite_drop_constraint,2, dropConstraintFunc),
+    INTERNAL_FUNCTION(sqlite_fail,           2, failConstraintFunc),
+    INTERNAL_FUNCTION(sqlite_add_constraint, 3, addConstraintFunc),
+    INTERNAL_FUNCTION(sqlite_find_constraint,2, findConstraintFunc),
   };
   sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
 }
@@ -122379,7 +124441,7 @@ static void SQLITE_APICALL callStatGet(Parse *pParse, int regStat, int iParam, i
 /* Add a comment to the most recent VDBE opcode that is the name
 ** of the k-th column of the pIdx index.
 */
-static void SQLITE_APICALL analyzeVdbeCommentIndexWithColumnName(
+static void analyzeVdbeCommentIndexWithColumnName(
   Vdbe *v,         /* Prepared statement under construction */
   Index *pIdx,     /* Index whose column is being loaded */
   int k            /* Which column index */
@@ -123551,6 +125613,16 @@ static void SQLITE_APICALL attachFunc(
     ** from sqlite3_deserialize() to close database db->init.iDb and
     ** reopen it as a MemDB */
     Btree *pNewBt = 0;
+
+    pNew = &db->aDb[db->init.iDb];
+    assert( pNew->pBt!=0 );
+    if( sqlite3BtreeTxnState(pNew->pBt)!=SQLITE_TXN_NONE
+     || sqlite3BtreeIsInBackup(pNew->pBt)
+    ){
+      rc = SQLITE_BUSY;
+      goto attach_error;
+    }
+
     pVfs = sqlite3_vfs_find("memdb");
     if( pVfs==0 ) return;
     rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB);
@@ -123560,8 +125632,7 @@ static void SQLITE_APICALL attachFunc(
         /* Both the Btree and the new Schema were allocated successfully.
         ** Close the old db and update the aDb[] slot with the new memdb
         ** values.  */
-        pNew = &db->aDb[db->init.iDb];
-        if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt);
+        sqlite3BtreeClose(pNew->pBt);
         pNew->pBt = pNewBt;
         pNew->pSchema = pNewSchema;
       }else{
@@ -124041,7 +126112,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3FixTriggerStep(
     if( sqlite3WalkSelect(&pFix->w, pStep->pSelect)
      || sqlite3WalkExpr(&pFix->w, pStep->pWhere)
      || sqlite3WalkExprList(&pFix->w, pStep->pExprList)
-     || sqlite3FixSrcList(pFix, pStep->pFrom)
+     || sqlite3FixSrcList(pFix, pStep->pSrc)
     ){
       return 1;
     }
@@ -124148,7 +126219,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_set_authorizer(
   sqlite3_mutex_enter(db->mutex);
   db->xAuth = (sqlite3_xauth)xAuth;
   db->pAuthArg = pArg;
-  if( db->xAuth ) sqlite3ExpirePreparedStatements(db, 1);
+  sqlite3ExpirePreparedStatements(db, 1);
   sqlite3_mutex_leave(db->mutex);
   return SQLITE_OK;
 }
@@ -124819,6 +126890,7 @@ SQLITE_PRIVATE Table *SQLITE_APICALL sqlite3LocateTableItem(
   const char *zDb;
   if( p->fg.fixedSchema ){
     int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema);
+    assert( iDb>=0 && iDb<pParse->db->nDb );
     zDb = pParse->db->aDb[iDb].zDbSName;
   }else{
     assert( !p->fg.isSubquery );
@@ -126392,8 +128464,8 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3ChangeCookie(Parse *pParse, int iDb){
 ** The estimate is conservative.  It might be larger that what is
 ** really needed.
 */
-static int SQLITE_APICALL identLength(const char *z){
-  int n;
+static i64 SQLITE_APICALL identLength(const char *z){
+  i64 n;
   for(n=0; *z; n++, z++){
     if( *z=='"' ){ n++; }
   }
@@ -126826,9 +128898,10 @@ static void SQLITE_APICALL convertToWithoutRowidTable(Parse *pParse, Table *pTab
     if( !hasColumn(pPk->aiColumn, j, i)
      && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
     ){
+      const char *zColl = sqlite3ColumnColl(&pTab->aCol[i]);
       assert( j<pPk->nColumn );
       pPk->aiColumn[j] = i;
-      pPk->azColl[j] = sqlite3StrBINARY;
+      pPk->azColl[j] = zColl ? zColl : sqlite3StrBINARY;
       j++;
     }
   }
@@ -126903,13 +128976,14 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3MarkAllShadowTablesOf(sqlite3 *db, Tab
 ** restored to its original value prior to this routine returning.
 */
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3ShadowTableName(sqlite3 *db, const char *zName){
-  char *zTail;                  /* Pointer to the last "_" in zName */
+  const char *zTail;            /* Pointer to the last "_" in zName */
   Table *pTab;                  /* Table that zName is a shadow of */
+  char *zCopy;
   zTail = strrchr(zName, '_');
   if( zTail==0 ) return 0;
-  *zTail = 0;
-  pTab = sqlite3FindTable(db, zName, 0);
-  *zTail = '_';
+  zCopy = sqlite3DbStrNDup(db, zName, (int)(zTail-zName));
+  pTab = zCopy ? sqlite3FindTable(db, zCopy, 0) : 0;
+  sqlite3DbFree(db, zCopy);
   if( pTab==0 ) return 0;
   if( !IsVirtual(pTab) ) return 0;
   return sqlite3IsShadowTableOf(db, pTab, zName);
@@ -126924,12 +128998,12 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3ShadowTableName(sqlite3 *db, const char
 ** index definition are tagged this way to help ensure that we do
 ** not pass them into code generator routines by mistake.
 */
-static int SQLITE_APICALL markImmutableExprStep(Walker *pWalker, Expr *pExpr){
+static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){
   (void)pWalker;
   ExprSetVVAProperty(pExpr, EP_Immutable);
   return WRC_Continue;
 }
-static void SQLITE_APICALL markExprListImmutable(ExprList *pList){
+static void markExprListImmutable(ExprList *pList){
   if( pList ){
     Walker w;
     memset(&w, 0, sizeof(w));
@@ -127062,6 +129136,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3EndTable(
     convertToWithoutRowidTable(pParse, p);
   }
   iDb = sqlite3SchemaToIndex(db, p->pSchema);
+  assert( iDb>=0 && iDb<=db->nDb );
 
 #ifndef SQLITE_OMIT_CHECK
   /* Resolve names in all CHECK constraint expressions.
@@ -127357,6 +129432,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3CreateView(
 
   sqlite3TwoPartName(pParse, pName1, pName2, &pName);
   iDb = sqlite3SchemaToIndex(db, p->pSchema);
+  assert( iDb>=0 && iDb<db->nDb );
   sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
   if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail;
 
@@ -128953,6 +131029,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3DropIndex(Parse *pParse, SrcList *pNam
     goto exit_drop_index;
   }
   iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
+  assert( iDb>=0 && iDb<db->nDb );
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code = SQLITE_DROP_INDEX;
@@ -129850,8 +131927,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3RowidConstraint(
 }
 
 /*
-** Check to see if pIndex uses the collating sequence pColl.  Return
-** true if it does and false if it does not.
+** Return true if any column of pIndex uses the zColl collation
 */
 #ifndef SQLITE_OMIT_REINDEX
 static int SQLITE_APICALL collationMatch(const char *zColl, Index *pIndex){
@@ -129859,8 +131935,8 @@ static int SQLITE_APICALL collationMatch(const char *zColl, Index *pIndex){
   assert( zColl!=0 );
   for(i=0; i<pIndex->nColumn; i++){
     const char *z = pIndex->azColl[i];
-    assert( z!=0 || pIndex->aiColumn[i]<0 );
-    if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){
+    assert( z!=0 );
+    if( 0==sqlite3StrICmp(z, zColl) ){
       return 1;
     }
   }
@@ -129868,73 +131944,39 @@ static int SQLITE_APICALL collationMatch(const char *zColl, Index *pIndex){
 }
 #endif
 
-/*
-** Recompute all indices of pTab that use the collating sequence pColl.
-** If pColl==0 then recompute all indices of pTab.
-*/
-#ifndef SQLITE_OMIT_REINDEX
-static void SQLITE_APICALL reindexTable(Parse *pParse, Table *pTab, char const *zColl){
-  if( !IsVirtual(pTab) ){
-    Index *pIndex;              /* An index associated with pTab */
-
-    for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
-      if( zColl==0 || collationMatch(zColl, pIndex) ){
-        int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
-        sqlite3BeginWriteOperation(pParse, 0, iDb);
-        sqlite3RefillIndex(pParse, pIndex, -1);
-      }
-    }
-  }
-}
-#endif
-
-/*
-** Recompute all indices of all tables in all databases where the
-** indices use the collating sequence pColl.  If pColl==0 then recompute
-** all indices everywhere.
-*/
-#ifndef SQLITE_OMIT_REINDEX
-static void SQLITE_APICALL reindexDatabases(Parse *pParse, char const *zColl){
-  Db *pDb;                    /* A single database */
-  int iDb;                    /* The database index number */
-  sqlite3 *db = pParse->db;   /* The database connection */
-  HashElem *k;                /* For looping over tables in pDb */
-  Table *pTab;                /* A table in the database */
-
-  assert( sqlite3BtreeHoldsAllMutexes(db) );  /* Needed for schema access */
-  for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
-    assert( pDb!=0 );
-    for(k=sqliteHashFirst(&pDb->pSchema->tblHash);  k; k=sqliteHashNext(k)){
-      pTab = (Table*)sqliteHashData(k);
-      reindexTable(pParse, pTab, zColl);
-    }
-  }
-}
-#endif
-
 /*
 ** Generate code for the REINDEX command.
 **
 **        REINDEX                            -- 1
 **        REINDEX  <collation>               -- 2
-**        REINDEX  ?<database>.?<tablename>  -- 3
-**        REINDEX  ?<database>.?<indexname>  -- 4
+**        REINDEX  ?<database>.?<indexname>  -- 3
+**        REINDEX  ?<database>.?<tablename>  -- 4
+**        REINDEX  EXPRESSIONS               -- 5
 **
-** Form 1 causes all indices in all attached databases to be rebuilt.
-** Form 2 rebuilds all indices in all databases that use the named
+** Form 1 causes all indexes in all attached databases to be rebuilt.
+** Form 2 rebuilds all indexes in all databases that use the named
 ** collating function.  Forms 3 and 4 rebuild the named index or all
-** indices associated with the named table.
+** indexes associated with the named table, respectively.  Form 5
+** rebuilds all expression indexes in addition to all collations,
+** indexes, or tables named "EXPRESSIONS".
+**
+** If the name is ambiguous such that it matches two or more of
+** forms 2 through 5, then rebuild the union of all matching indexes,
+** taken care to avoid rebuilding the same index more than once.
 */
 #ifndef SQLITE_OMIT_REINDEX
 SQLITE_PRIVATE void SQLITE_APICALL sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
-  CollSeq *pColl;             /* Collating sequence to be reindexed, or NULL */
-  char *z;                    /* Name of a table or index */
-  const char *zDb;            /* Name of the database */
-  Table *pTab;                /* A table in the database */
-  Index *pIndex;              /* An index associated with pTab */
-  int iDb;                    /* The database index number */
+  char *z = 0;                /* Name of a table or index or collation */
+  const char *zDb = 0;        /* Name of the database */
+  int iReDb = -1;             /* The database index number */
   sqlite3 *db = pParse->db;   /* The database connection */
   Token *pObjName;            /* Name of the table or index to be reindexed */
+  int bMatch = 0;             /* At least one name match */
+  const char *zColl = 0;      /* Rebuild indexes using this collation */
+  Table *pReTab = 0;          /* Rebuild all indexes of this table */
+  Index *pReIndex = 0;        /* Rebuild this index */
+  int isExprIdx = 0;          /* Rebuild all expression indexes */
+  int bAll = 0;               /* Rebuild all indexes */
 
   /* Read the database schema. If an error occurs, leave an error message
   ** and code in pParse and return NULL. */
@@ -129943,41 +131985,66 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3Reindex(Parse *pParse, Token *pName1,
   }
 
   if( pName1==0 ){
-    reindexDatabases(pParse, 0);
-    return;
+    /* rebuild all indexes */
+    bMatch = 1;
+    bAll = 1;
   }else if( NEVER(pName2==0) || pName2->z==0 ){
-    char *zColl;
     assert( pName1->z );
-    zColl = sqlite3NameFromToken(pParse->db, pName1);
-    if( !zColl ) return;
-    pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
-    if( pColl ){
-      reindexDatabases(pParse, zColl);
-      sqlite3DbFree(db, zColl);
-      return;
+    z = sqlite3NameFromToken(pParse->db, pName1);
+    if( z==0 ) return;
+  }else{
+    iReDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
+    if( iReDb<0 ) return;
+    z = sqlite3NameFromToken(db, pObjName);
+    if( z==0 ) return;
+    zDb = db->aDb[iReDb].zDbSName;
+  }
+  if( !bAll ){
+    if( zDb==0 && sqlite3StrICmp(z, "expressions")==0 ){
+      isExprIdx = 1;
+      bMatch = 1;
+    }
+    if( zDb==0 && sqlite3FindCollSeq(db, ENC(db), z, 0)!=0 ){
+      zColl = z;
+      bMatch = 1;
+    }
+    if( zColl==0 && (pReTab = sqlite3FindTable(db, z, zDb))!=0 ){
+      bMatch = 1;
+    }
+    if( zColl==0 && (pReIndex = sqlite3FindIndex(db, z, zDb))!=0 ){
+      bMatch = 1;
     }
-    sqlite3DbFree(db, zColl);
   }
-  iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
-  if( iDb<0 ) return;
-  z = sqlite3NameFromToken(db, pObjName);
-  if( z==0 ) return;
-  zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
-  pTab = sqlite3FindTable(db, z, zDb);
-  if( pTab ){
-    reindexTable(pParse, pTab, 0);
-    sqlite3DbFree(db, z);
-    return;
+  if( bMatch ){
+    int iDb;
+    HashElem *k;
+    Table *pTab;
+    Index *pIdx;
+    Db *pDb;
+    for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
+      assert( pDb!=0 );
+      if( iReDb>=0 && iReDb!=iDb ) continue;
+      for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
+        pTab = (Table*)sqliteHashData(k);
+        if( IsVirtual(pTab) ) continue;
+        for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+          if( bAll
+           || pTab==pReTab
+           || pIdx==pReIndex
+           || (isExprIdx && pIdx->bHasExpr)
+           || (zColl!=0 && collationMatch(zColl,pIdx))
+          ){
+            sqlite3BeginWriteOperation(pParse, 0, iDb);
+            sqlite3RefillIndex(pParse, pIdx, -1);
+          }
+        } /* End loop over indexes of pTab */
+      } /* End loop over tables of iDb */
+    } /* End loop over databases */
+  }else{
+    sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
   }
-  pIndex = sqlite3FindIndex(db, z, zDb);
   sqlite3DbFree(db, z);
-  if( pIndex ){
-    iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
-    sqlite3BeginWriteOperation(pParse, 0, iDb);
-    sqlite3RefillIndex(pParse, pIndex, -1);
-    return;
-  }
-  sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
+  return;
 }
 #endif
 
@@ -130787,7 +132854,7 @@ static int SQLITE_APICALL vtabIsReadOnly(Parse *pParse, Table *pTab){
   **   *  Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS
   **      virtual tables if PRAGMA trusted_schema=ON.
   */
-  if( pParse->pToplevel!=0
+  if( (pParse->pToplevel!=0 || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL))
    && pTab->u.vtab.p->eVtabRisk >
            ((pParse->db->flags & SQLITE_TrustedSchema)!=0)
   ){
@@ -131624,8 +133691,9 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3GenerateRowIndexDelete(
     r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
         &iPartIdxLabel, pPrior, r1);
     sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
-        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
-    sqlite3VdbeChangeP5(v, 1);  /* Cause IdxDelete to error if no entry found */
+        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn
+    );
+    sqlite3VdbeChangeP4(v, -1, (const char*)pIdx, P4_INDEX);
     sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
     pPrior = pIdx;
   }
@@ -132060,9 +134128,18 @@ static void SQLITE_APICALL printfFunc(
     sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
     str.printfFlags = SQLITE_PRINTF_SQLFUNC;
     sqlite3_str_appendf(&str, zFormat, &x);
-    n = str.nChar;
-    sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
-                        SQLITE_DYNAMIC);
+    if( str.accError==SQLITE_OK ){
+      n = str.nChar;
+      sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
+                          SQLITE_DYNAMIC);
+    }else{
+      if( str.accError==SQLITE_NOMEM ){
+        sqlite3_result_error_nomem(context);
+      }else{
+        sqlite3_result_error_toobig(context);
+      }
+      sqlite3_str_reset(&str);
+    }
   }
 }
 
@@ -132200,7 +134277,7 @@ static void SQLITE_APICALL roundFunc(sqlite3_context *context, int argc, sqlite3
       sqlite3_result_error_nomem(context);
       return;
     }
-    sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8);
+    sqlite3AtoF(zBuf, &r);
     sqlite3_free(zBuf);
   }
   sqlite3_result_double(context, r);
@@ -132832,18 +134909,11 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3QuoteValue(StrAccum *pStr, sqlite3_val
 
   switch( sqlite3_value_type(pValue) ){
     case SQLITE_FLOAT: {
-      double r1, r2;
-      const char *zVal;
-      r1 = sqlite3_value_double(pValue);
-      sqlite3_str_appendf(pStr, "%!0.15g", r1);
-      zVal = sqlite3_str_value(pStr);
-      if( zVal ){
-        sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
-        if( r1!=r2 ){
-          sqlite3_str_reset(pStr);
-          sqlite3_str_appendf(pStr, "%!0.20e", r1);
-        }
-      }
+                             /*    ,---  Show infinity as 9.0e+999
+                             **    |
+                             **    | ,--- 17 precision guarantees round-trip
+                             **    v v                                       */
+      sqlite3_str_appendf(pStr, "%!0.17g", sqlite3_value_double(pValue));
       break;
     }
     case SQLITE_INTEGER: {
@@ -132935,7 +135005,7 @@ static void SQLITE_APICALL unistrFunc(
   }
   i = j = 0;
   while( i<nIn ){
-    char *z = strchr(&zIn[i],'\\');
+    const char *z = strchr(&zIn[i],'\\');
     if( z==0 ){
       n = nIn - i;
       memmove(&zOut[j], &zIn[i], n);
@@ -132972,7 +135042,7 @@ static void SQLITE_APICALL unistrFunc(
     }
   }
   zOut[j] = 0;
-  sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8);
+  sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8_ZT);
   return;
 
 unistr_error:
@@ -133065,7 +135135,7 @@ static void SQLITE_APICALL charFunc(
     }                                                    \
   }
   *zOut = 0;
-  sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8);
+  sqlite3_result_text64(context, (char*)z, zOut-z,sqlite3_free,SQLITE_UTF8_ZT);
 }
 
 /*
@@ -133094,7 +135164,7 @@ static void SQLITE_APICALL hexFunc(
     }
     *z = 0;
     sqlite3_result_text64(context, zHex, (u64)(z-zHex),
-                          sqlite3_free, SQLITE_UTF8);
+                          sqlite3_free, SQLITE_UTF8_ZT);
   }
 }
 
@@ -133432,7 +135502,7 @@ static void SQLITE_APICALL concatFuncCore(
   }
   z[j] = 0;
   assert( j<=n );
-  sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8);
+  sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8_ZT);
 }
 
 /*
@@ -133707,11 +135777,16 @@ static void SQLITE_APICALL sumInverse(sqlite3_context *context, int argc, sqlite
     assert( p->cnt>0 );
     p->cnt--;
     if( !p->approx ){
-      if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){
-        p->ovrfl = 1;
-        p->approx = 1;
+      i64 x = p->iSum;
+      if( sqlite3SubInt64(&x, sqlite3_value_int64(argv[0]))==0 ){
+        p->iSum = x;
+        return;
       }
-    }else if( type==SQLITE_INTEGER ){
+      p->ovrfl = 1;
+      p->approx = 1;
+      kahanBabuskaNeumaierInit(p, p->iSum);
+    }
+    if( type==SQLITE_INTEGER ){
       i64 iVal = sqlite3_value_int64(argv[0]);
       if( iVal!=SMALLEST_INT64 ){
         kahanBabuskaNeumaierStepInt64(p, -iVal);
@@ -134098,6 +136173,8 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3RegisterLikeFunctions(sqlite3 *db, int
     sqlite3CreateFunc(db, "like", nArg, SQLITE_UTF8, pInfo, likeFunc,
                       0, 0, 0, 0, 0);
     pDef = sqlite3FindFunction(db, "like", nArg, SQLITE_UTF8, 0);
+    assert( pDef!=0 ); /* The sqlite3CreateFunc() call above cannot fail
+                       ** because the "like" SQL-function already exists */
     pDef->funcFlags |= flags;
     pDef->funcFlags &= ~SQLITE_FUNC_UNSAFE;
   }
@@ -134682,47 +136759,46 @@ static void SQLITE_APICALL percentSort(double *a, unsigned int n){
   int i;         /* Loop counter */
   double rPivot; /* The pivot value */
 
-  assert( n>=2 );
-  if( a[0]>a[n-1] ){
-    SWAP_DOUBLE(a[0],a[n-1])
-  }
-  if( n==2 ) return;
-  iGt = n-1;
-  i = n/2;
-  if( a[0]>a[i] ){
-    SWAP_DOUBLE(a[0],a[i])
-  }else if( a[i]>a[iGt] ){
-    SWAP_DOUBLE(a[i],a[iGt])
-  }
-  if( n==3 ) return;
-  rPivot = a[i];
-  iLt = i = 1;
-  do{
-    if( a[i]<rPivot ){
-      if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
-      iLt++;
-      i++;
-    }else if( a[i]>rPivot ){
-      do{
-        iGt--;
-      }while( iGt>i && a[iGt]>rPivot );
+  while( n>=2 ){
+    if( a[0]>a[n-1] ){
+      SWAP_DOUBLE(a[0],a[n-1])
+    }
+    if( n==2 ) return;
+    iGt = n-1;
+    i = n/2;
+    if( a[0]>a[i] ){
+      SWAP_DOUBLE(a[0],a[i])
+    }else if( a[i]>a[iGt] ){
       SWAP_DOUBLE(a[i],a[iGt])
+    }
+    if( n==3 ) return;
+    rPivot = a[i];
+    iLt = i = 1;
+    do{
+      if( a[i]<rPivot ){
+        if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
+        iLt++;
+        i++;
+      }else if( a[i]>rPivot ){
+        do{
+          iGt--;
+        }while( iGt>i && a[iGt]>rPivot );
+        SWAP_DOUBLE(a[i],a[iGt])
+      }else{
+        i++;
+      }
+    }while( i<iGt );
+    if( iLt>n/2 ){
+      if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
+      n = iLt;
     }else{
-      i++;
+      if( iLt>=2 ) percentSort(a, iLt);
+      a += iGt;
+      n -= iGt;
     }
-  }while( i<iGt );
-  if( iLt>=2 ) percentSort(a, iLt);
-  if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
-
-/* Uncomment for testing */
-#if 0
-  for(i=0; i<n-1; i++){
-    assert( a[i]<=a[i+1] );
   }
-#endif
 }
 
-
 /*
 ** The "inverse" function for percentile(Y,P) is called to remove a
 ** row that was previously inserted by "step".
@@ -134824,7 +136900,7 @@ static void SQLITE_APICALL percentValue(sqlite3_context *pCtx){
 ** Return JSON text that describes low-level debug/diagnostic information
 ** about the sqlite3_file object associated with SCHEMA.
 */
-static void SQLITE_APICALL filestatFunc(
+static void filestatFunc(
   sqlite3_context *context,
   int argc,
   sqlite3_value **argv
@@ -134881,7 +136957,7 @@ static void SQLITE_APICALL filestatFunc(
 ** in production builds.  This function is not an API and is subject to
 ** modification or removal in future versions of SQLite.
 */
-static void SQLITE_APICALL fpdecodeFunc(
+static void fpdecodeFunc(
   sqlite3_context *context,
   int argc,
   sqlite3_value **argv
@@ -134925,7 +137001,7 @@ static void SQLITE_APICALL fpdecodeFunc(
 ** in production builds.  This function is not an API and is subject to
 ** modification or removal in future versions of SQLite.
 */
-static void SQLITE_APICALL parseuriFunc(
+static void parseuriFunc(
   sqlite3_context *ctx,
   int argc,
   sqlite3_value **argv
@@ -135886,6 +137962,7 @@ SQLITE_PRIVATE FKey *SQLITE_APICALL sqlite3FkReferences(Table *pTab){
 static void SQLITE_APICALL fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
   if( p ){
     TriggerStep *pStep = p->step_list;
+    sqlite3SrcListDelete(dbMem, pStep->pSrc);
     sqlite3ExprDelete(dbMem, pStep->pWhere);
     sqlite3ExprListDelete(dbMem, pStep->pExprList);
     sqlite3SelectDelete(dbMem, pStep->pSelect);
@@ -136105,6 +138182,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3FkCheck(
   if( !IsOrdinaryTable(pTab) ) return;
 
   iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+  assert( iDb>=00 && iDb<db->nDb );
   zDb = db->aDb[iDb].zDbSName;
 
   /* Loop through all the foreign key constraints for which pTab is the
@@ -136522,7 +138600,6 @@ static Trigger *SQLITE_APICALL fkActionTrigger(
     nFrom = sqlite3Strlen30(zFrom);
 
     if( action==OE_Restrict ){
-      int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
       SrcList *pSrc;
       Expr *pRaise;
 
@@ -136533,10 +138610,10 @@ static Trigger *SQLITE_APICALL fkActionTrigger(
       }
       pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
       if( pSrc ){
-        assert( pSrc->nSrc==1 );
-        pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom);
-        assert( pSrc->a[0].fg.fixedSchema==0 && pSrc->a[0].fg.isSubquery==0 );
-        pSrc->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
+        SrcItem *pItem = &pSrc->a[0];
+        pItem->zName = sqlite3DbStrDup(db, zFrom);
+        pItem->fg.fixedSchema = 1;
+        pItem->u4.pSchema = pTab->pSchema;
       }
       pSelect = sqlite3SelectNew(pParse,
           sqlite3ExprListAppend(pParse, 0, pRaise),
@@ -136552,14 +138629,17 @@ static Trigger *SQLITE_APICALL fkActionTrigger(
 
     pTrigger = (Trigger *)sqlite3DbMallocZero(db,
         sizeof(Trigger) +         /* struct Trigger */
-        sizeof(TriggerStep) +     /* Single step in trigger program */
-        nFrom + 1                 /* Space for pStep->zTarget */
+        sizeof(TriggerStep)       /* Single step in trigger program */
     );
     if( pTrigger ){
       pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
-      pStep->zTarget = (char *)&pStep[1];
-      memcpy((char *)pStep->zTarget, zFrom, nFrom);
-
+      pStep->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
+      if( pStep->pSrc ){
+        SrcItem *pItem = &pStep->pSrc->a[0];
+        pItem->zName = sqlite3DbStrNDup(db, zFrom, nFrom);
+        pItem->u4.pSchema = pTab->pSchema;
+        pItem->fg.fixedSchema = 1;
+      }
       pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
       pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
       pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
@@ -140616,7 +142696,11 @@ struct sqlite3_api_routines {
   /* Version 3.51.0 and later */
   int (SQLITE_APICALL *set_errmsg)(sqlite3*,int,const char*);
   int (SQLITE_APICALL *db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
-
+  /* Version 3.52.0 and later */
+  void (SQLITE_APICALL *str_truncate)(sqlite3_str*,int);
+  void (SQLITE_APICALL *str_free)(sqlite3_str*);
+  int (SQLITE_APICALL *carray_bind)(sqlite3_stmt*,int,void*,int,int,void(SQLITE_APICALL *)(void*));
+  int (SQLITE_APICALL *carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(SQLITE_APICALL *)(void*),void*);
 };
 
 /*
@@ -140955,6 +143039,11 @@ typedef int (SQLITE_APICALL *sqlite3_loadext_entry)(
 /* Version 3.51.0 and later */
 #define sqlite3_set_errmsg             sqlite3_api->set_errmsg
 #define sqlite3_db_status64            sqlite3_api->db_status64
+/* Version 3.52.0 and later */
+#define sqlite3_str_truncate           sqlite3_api->str_truncate
+#define sqlite3_str_free               sqlite3_api->str_free
+#define sqlite3_carray_bind            sqlite3_api->carray_bind
+#define sqlite3_carray_bind_v2         sqlite3_api->carray_bind_v2
 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
 
 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -141481,7 +143570,17 @@ static const sqlite3_api_routines sqlite3Apis = {
   sqlite3_setlk_timeout,
   /* Version 3.51.0 and later */
   sqlite3_set_errmsg,
-  sqlite3_db_status64
+  sqlite3_db_status64,
+  /* Version 3.52.0 and later */
+  sqlite3_str_truncate,
+  sqlite3_str_free,
+#ifdef SQLITE_ENABLE_CARRAY
+  sqlite3_carray_bind,
+  sqlite3_carray_bind_v2
+#else
+  0,
+  0
+#endif
 };
 
 /* True if x is the directory separator character
@@ -141583,33 +143682,42 @@ static int SQLITE_APICALL sqlite3LoadExtension(
   ** entry point name "sqlite3_extension_init" was not found, then
   ** construct an entry point name "sqlite3_X_init" where the X is
   ** replaced by the lowercase value of every ASCII alphabetic
-  ** character in the filename after the last "/" upto the first ".",
-  ** and eliding the first three characters if they are "lib".
+  ** character in the filename after the last "/" up to the first ".",
+  ** and skipping the first three characters if they are "lib".
   ** Examples:
   **
   **    /usr/local/lib/libExample5.4.3.so ==>  sqlite3_example_init
   **    C:/lib/mathfuncs.dll              ==>  sqlite3_mathfuncs_init
+  **
+  ** If that still finds no entry point, repeat a second time but this
+  ** time include both alphabetic and numeric characters up to the first
+  ** ".".  Example:
+  **
+  **    /usr/local/lib/libExample5.4.3.so ==>  sqlite3_example5_init
   */
   if( xInit==0 && zProc==0 ){
     int iFile, iEntry, c;
     int ncFile = sqlite3Strlen30(zFile);
+    int cnt = 0;
     zAltEntry = sqlite3_malloc64(ncFile+30);
     if( zAltEntry==0 ){
       sqlite3OsDlClose(pVfs, handle);
       return SQLITE_NOMEM_BKPT;
     }
-    memcpy(zAltEntry, "sqlite3_", 8);
-    for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
-    iFile++;
-    if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
-    for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
-      if( sqlite3Isalpha(c) ){
-        zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
+    do{
+      memcpy(zAltEntry, "sqlite3_", 8);
+      for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
+      iFile++;
+      if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
+      for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
+        if( sqlite3Isalpha(c) || (cnt && sqlite3Isdigit(c)) ){
+          zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
+        }
       }
-    }
-    memcpy(zAltEntry+iEntry, "_init", 6);
-    zEntry = zAltEntry;
-    xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
+      memcpy(zAltEntry+iEntry, "_init", 6);
+      zEntry = zAltEntry;
+      xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
+    }while( xInit==0 && (++cnt)<2 );
   }
   if( xInit==0 ){
     if( pzErrMsg ){
@@ -144619,8 +146727,20 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3Pragma(
             pPrior = pIdx;
             sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1);/* increment entry count */
             /* Verify that an index entry exists for the current table row */
-            jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
+            sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
                                         pIdx->nColumn); VdbeCoverage(v);
+            jmp2 = sqlite3VdbeAddOp3(v, OP_IFindKey, iIdxCur+j, ckUniq, r1);
+            VdbeCoverage(v);
+            sqlite3VdbeChangeP4(v, -1, (const char*)pIdx, P4_INDEX);
+            sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
+              sqlite3MPrintf(db, "index %s stores an imprecise floating-point "
+                                 "value for row ", pIdx->zName),
+              P4_DYNAMIC);
+            sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+            integrityCheckResultRow(v);
+            sqlite3VdbeAddOp2(v, OP_Goto, 0, ckUniq);
+
+            sqlite3VdbeJumpHere(v, jmp2);
             sqlite3VdbeLoadString(v, 3, "row ");
             sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
             sqlite3VdbeLoadString(v, 4, " missing from index ");
@@ -144628,7 +146748,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3Pragma(
             jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
             sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
             jmp4 = integrityCheckResultRow(v);
-            sqlite3VdbeJumpHere(v, jmp2);
+            sqlite3VdbeResolveLabel(v, ckUniq);
 
             /* The OP_IdxRowid opcode is an optimized version of OP_Column
             ** that extracts the rowid off the end of the index record.
@@ -145681,7 +147801,8 @@ static void SQLITE_APICALL corruptSchema(
     static const char *azAlterType[] = {
        "rename",
        "drop column",
-       "add column"
+       "add column",
+       "drop constraint"
     };
     *pData->pzErrMsg = sqlite3MPrintf(db,
         "error in %s %s after %s: %s", azObj[0], azObj[1],
@@ -146764,7 +148885,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_prepare16_v3(
 */
 typedef struct DistinctCtx DistinctCtx;
 struct DistinctCtx {
-  u8 isTnct;      /* 0: Not distinct. 1: DISTICT  2: DISTINCT and ORDER BY */
+  u8 isTnct;      /* 0: Not distinct. 1: DISTINCT  2: DISTINCT and ORDER BY */
   u8 eTnctType;   /* One of the WHERE_DISTINCT_* operators */
   int tabTnct;    /* Ephemeral table used for DISTINCT processing */
   int addrTnct;   /* Address of OP_OpenEphemeral opcode for tabTnct */
@@ -146894,8 +149015,6 @@ SQLITE_PRIVATE Select *SQLITE_APICALL sqlite3SelectNew(
   pNew->iLimit = 0;
   pNew->iOffset = 0;
   pNew->selId = ++pParse->nSelect;
-  pNew->addrOpenEphm[0] = -1;
-  pNew->addrOpenEphm[1] = -1;
   pNew->nSelectRow = 0;
   if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
   pNew->pSrc = pSrc;
@@ -147404,6 +149523,10 @@ static int SQLITE_APICALL sqlite3ProcessJoin(Parse *pParse, Select *p){
       pRight->fg.isOn = 1;
       p->selFlags |= SF_OnToWhere;
     }
+
+    if( IsVirtual(pRightTab) && joinType==EP_OuterON && pRight->u1.pFuncArg ){
+      p->selFlags |= SF_OnToWhere;
+    }
   }
   return 0;
 }
@@ -148043,29 +150166,6 @@ static void SQLITE_APICALL selectInnerLoop(
   }
 
   switch( eDest ){
-    /* In this mode, write each query result to the key of the temporary
-    ** table iParm.
-    */
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
-    case SRT_Union: {
-      int r1;
-      r1 = sqlite3GetTempReg(pParse);
-      sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
-      sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol);
-      sqlite3ReleaseTempReg(pParse, r1);
-      break;
-    }
-
-    /* Construct a record from the query result, but instead of
-    ** saving that record, use it as a key to delete elements from
-    ** the temporary table iParm.
-    */
-    case SRT_Except: {
-      sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol);
-      break;
-    }
-#endif /* SQLITE_OMIT_COMPOUND_SELECT */
-
     /* Store the result as data using a unique key.
     */
     case SRT_Fifo:
@@ -148188,9 +150288,14 @@ static void SQLITE_APICALL selectInnerLoop(
         assert( nResultCol<=pDest->nSdst );
         pushOntoSorter(
             pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
+        pDest->iSDParm = regResult;
       }else{
         assert( nResultCol==pDest->nSdst );
-        assert( regResult==iParm );
+        if( regResult!=iParm ){
+          /* This occurs in cases where the SELECT had both a DISTINCT and
+          ** an OFFSET clause.  */
+          sqlite3VdbeAddOp3(v, OP_Copy, regResult, iParm, nResultCol-1);
+        }
         /* The LIMIT clause will jump out of the loop for us */
       }
       break;
@@ -148337,7 +150442,7 @@ SQLITE_PRIVATE KeyInfo *SQLITE_APICALL sqlite3KeyInfoRef(KeyInfo *p){
 **
 ** This routine is used only inside of assert() statements.
 */
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
+SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
 #endif /* SQLITE_DEBUG */
 
 /*
@@ -149173,8 +151278,8 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3SubqueryColumnTypes(
       }
     }
     if( zType ){
-      const i64 k = sqlite3Strlen30(zType);
-      n = sqlite3Strlen30(pCol->zCnName);
+      const i64 k = strlen(zType);
+      n = strlen(pCol->zCnName);
       pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2);
       pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
       if( pCol->zCnName ){
@@ -149347,9 +151452,9 @@ static CollSeq *SQLITE_APICALL multiSelectCollSeq(Parse *pParse, Select *p, int
 ** function is responsible for ensuring that this structure is eventually
 ** freed.
 */
-static KeyInfo *SQLITE_APICALL multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
+static KeyInfo *SQLITE_APICALL multiSelectByMergeKeyInfo(Parse *pParse, Select *p, int nExtra){
   ExprList *pOrderBy = p->pOrderBy;
-  int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0;
+  int nOrderBy = (pOrderBy!=0) ? pOrderBy->nExpr : 0;
   sqlite3 *db = pParse->db;
   KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1);
   if( pRet ){
@@ -149482,7 +151587,7 @@ static void SQLITE_APICALL generateWithRecursiveQuery(
   regCurrent = ++pParse->nMem;
   sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol);
   if( pOrderBy ){
-    KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1);
+    KeyInfo *pKeyInfo = multiSelectByMergeKeyInfo(pParse, p, 1);
     sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0,
                       (char*)pKeyInfo, P4_KEYINFO);
     destQueue.pOrderBy = pOrderBy;
@@ -149491,8 +151596,28 @@ static void SQLITE_APICALL generateWithRecursiveQuery(
   }
   VdbeComment((v, "Queue table"));
   if( iDistinct ){
-    p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0);
-    p->selFlags |= SF_UsesEphemeral;
+    /* Generate an ephemeral table used to enforce distinctness on the
+    ** output of the recursive part of the CTE.
+    */
+    KeyInfo *pKeyInfo;            /* Collating sequence for the result set */
+    CollSeq **apColl;             /* For looping through pKeyInfo->aColl[] */
+
+    assert( p->pNext==0 );
+    assert( p->pEList!=0 );
+    nCol = p->pEList->nExpr;
+    pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nCol, 1);
+    if( pKeyInfo ){
+      for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
+        *apColl = multiSelectCollSeq(pParse, p, i);
+        if( 0==*apColl ){
+          *apColl = pParse->db->pDfltColl;
+        }
+      }
+      sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iDistinct, nCol, 0,
+                           (void*)pKeyInfo, P4_KEYINFO);
+    }else{
+      assert( pParse->nErr>0 );
+    }
   }
 
   /* Detach the ORDER BY clause from the compound SELECT */
@@ -149567,7 +151692,7 @@ end_of_recursive_query:
 #endif /* SQLITE_OMIT_CTE */
 
 /* Forward references */
-static int SQLITE_APICALL multiSelectOrderBy(
+static int SQLITE_APICALL multiSelectByMerge(
   Parse *pParse,        /* Parsing context */
   Select *p,            /* The right-most of SELECTs to be coded */
   SelectDest *pDest     /* What to do with query results */
@@ -149716,12 +151841,26 @@ static int SQLITE_APICALL multiSelect(
     generateWithRecursiveQuery(pParse, p, &dest);
   }else
 #endif
-
-  /* Compound SELECTs that have an ORDER BY clause are handled separately.
-  */
   if( p->pOrderBy ){
-    return multiSelectOrderBy(pParse, p, pDest);
-  }else{
+    /* If the compound has an ORDER BY clause, then always use the merge
+    ** algorithm. */
+    return multiSelectByMerge(pParse, p, pDest);
+  }else if( p->op!=TK_ALL ){
+    /* If the compound is EXCEPT, INTERSECT, or UNION (anything other than
+    ** UNION ALL) then also always use the merge algorithm.  However, the
+    ** multiSelectByMerge() routine requires that the compound have an
+    ** ORDER BY clause, and it doesn't right now.  So invent one first. */
+    Expr *pOne = sqlite3ExprInt32(db, 1);
+    p->pOrderBy = sqlite3ExprListAppend(pParse, 0, pOne);
+    if( pParse->nErr ) goto multi_select_end;
+    assert( p->pOrderBy!=0 );
+    p->pOrderBy->a[0].u.x.iOrderByCol = 1;
+    return multiSelectByMerge(pParse, p, pDest);
+  }else{
+    /* For a UNION ALL compound without ORDER BY, simply run the left
+    ** query, then run the right query */
+    int addr = 0;
+    int nLimit = 0;  /* Initialize to suppress harmless compiler warning */
 
 #ifndef SQLITE_OMIT_EXPLAIN
     if( pPrior->pPrior==0 ){
@@ -149729,300 +151868,49 @@ static int SQLITE_APICALL multiSelect(
       ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY"));
     }
 #endif
-
-    /* Generate code for the left and right SELECT statements.
-    */
-    switch( p->op ){
-      case TK_ALL: {
-        int addr = 0;
-        int nLimit = 0;  /* Initialize to suppress harmless compiler warning */
-        assert( !pPrior->pLimit );
-        pPrior->iLimit = p->iLimit;
-        pPrior->iOffset = p->iOffset;
-        pPrior->pLimit = p->pLimit;
-        TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n"));
-        rc = sqlite3Select(pParse, pPrior, &dest);
-        pPrior->pLimit = 0;
-        if( rc ){
-          goto multi_select_end;
-        }
-        p->pPrior = 0;
-        p->iLimit = pPrior->iLimit;
-        p->iOffset = pPrior->iOffset;
-        if( p->iLimit ){
-          addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
-          VdbeComment((v, "Jump ahead if LIMIT reached"));
-          if( p->iOffset ){
-            sqlite3VdbeAddOp3(v, OP_OffsetLimit,
-                              p->iLimit, p->iOffset+1, p->iOffset);
-          }
-        }
-        ExplainQueryPlan((pParse, 1, "UNION ALL"));
-        TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n"));
-        rc = sqlite3Select(pParse, p, &dest);
-        testcase( rc!=SQLITE_OK );
-        pDelete = p->pPrior;
-        p->pPrior = pPrior;
-        p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
-        if( p->pLimit
-         && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse)
-         && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
-        ){
-          p->nSelectRow = sqlite3LogEst((u64)nLimit);
-        }
-        if( addr ){
-          sqlite3VdbeJumpHere(v, addr);
-        }
-        break;
-      }
-      case TK_EXCEPT:
-      case TK_UNION: {
-        int unionTab;    /* Cursor number of the temp table holding result */
-        u8 op = 0;       /* One of the SRT_ operations to apply to self */
-        int priorOp;     /* The SRT_ operation to apply to prior selects */
-        Expr *pLimit;    /* Saved values of p->nLimit  */
-        int addr;
-        int emptyBypass = 0;   /* IfEmpty opcode to bypass RHS */
-        SelectDest uniondest;
-
-
-        testcase( p->op==TK_EXCEPT );
-        testcase( p->op==TK_UNION );
-        priorOp = SRT_Union;
-        if( dest.eDest==priorOp ){
-          /* We can reuse a temporary table generated by a SELECT to our
-          ** right.
-          */
-          assert( p->pLimit==0 );      /* Not allowed on leftward elements */
-          unionTab = dest.iSDParm;
-        }else{
-          /* We will need to create our own temporary table to hold the
-          ** intermediate results.
-          */
-          unionTab = pParse->nTab++;
-          assert( p->pOrderBy==0 );
-          addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
-          assert( p->addrOpenEphm[0] == -1 );
-          p->addrOpenEphm[0] = addr;
-          findRightmost(p)->selFlags |= SF_UsesEphemeral;
-          assert( p->pEList );
-        }
-
-
-        /* Code the SELECT statements to our left
-        */
-        assert( !pPrior->pOrderBy );
-        sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
-        TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
-        rc = sqlite3Select(pParse, pPrior, &uniondest);
-        if( rc ){
-          goto multi_select_end;
-        }
-
-        /* Code the current SELECT statement
-        */
-        if( p->op==TK_EXCEPT ){
-          op = SRT_Except;
-          emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab);
-          VdbeCoverage(v);
-        }else{
-          assert( p->op==TK_UNION );
-          op = SRT_Union;
-        }
-        p->pPrior = 0;
-        pLimit = p->pLimit;
-        p->pLimit = 0;
-        uniondest.eDest = op;
-        ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
-                          sqlite3SelectOpName(p->op)));
-        TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
-        rc = sqlite3Select(pParse, p, &uniondest);
-        testcase( rc!=SQLITE_OK );
-        assert( p->pOrderBy==0 );
-        pDelete = p->pPrior;
-        p->pPrior = pPrior;
-        p->pOrderBy = 0;
-        if( p->op==TK_UNION ){
-          p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
-        }
-        if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass);
-        sqlite3ExprDelete(db, p->pLimit);
-        p->pLimit = pLimit;
-        p->iLimit = 0;
-        p->iOffset = 0;
-
-        /* Convert the data in the temporary table into whatever form
-        ** it is that we currently need.
-        */
-        assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
-        assert( p->pEList || db->mallocFailed );
-        if( dest.eDest!=priorOp && db->mallocFailed==0 ){
-          int iCont, iBreak, iStart;
-          iBreak = sqlite3VdbeMakeLabel(pParse);
-          iCont = sqlite3VdbeMakeLabel(pParse);
-          computeLimitRegisters(pParse, p, iBreak);
-          sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
-          iStart = sqlite3VdbeCurrentAddr(v);
-          selectInnerLoop(pParse, p, unionTab,
-                          0, 0, &dest, iCont, iBreak);
-          sqlite3VdbeResolveLabel(v, iCont);
-          sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
-          sqlite3VdbeResolveLabel(v, iBreak);
-          sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
-        }
-        break;
-      }
-      default: assert( p->op==TK_INTERSECT ); {
-        int tab1, tab2;
-        int iCont, iBreak, iStart;
-        Expr *pLimit;
-        int addr, iLimit, iOffset;
-        SelectDest intersectdest;
-        int r1;
-        int emptyBypass;
-
-        /* INTERSECT is different from the others since it requires
-        ** two temporary tables.  Hence it has its own case.  Begin
-        ** by allocating the tables we will need.
-        */
-        tab1 = pParse->nTab++;
-        tab2 = pParse->nTab++;
-        assert( p->pOrderBy==0 );
-
-        addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
-        assert( p->addrOpenEphm[0] == -1 );
-        p->addrOpenEphm[0] = addr;
-        findRightmost(p)->selFlags |= SF_UsesEphemeral;
-        assert( p->pEList );
-
-        /* Code the SELECTs to our left into temporary table "tab1".
-        */
-        sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
-        TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n"));
-        rc = sqlite3Select(pParse, pPrior, &intersectdest);
-        if( rc ){
-          goto multi_select_end;
-        }
-
-        /* Initialize LIMIT counters before checking to see if the LHS
-        ** is empty, in case the jump is taken */
-        iBreak = sqlite3VdbeMakeLabel(pParse);
-        computeLimitRegisters(pParse, p, iBreak);
-        emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v);
-
-        /* Code the current SELECT into temporary table "tab2"
-        */
-        addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
-        assert( p->addrOpenEphm[1] == -1 );
-        p->addrOpenEphm[1] = addr;
-
-        /* Disable prior SELECTs and the LIMIT counters during the computation
-        ** of the RHS select */
-        pLimit = p->pLimit;
-        iLimit = p->iLimit;
-        iOffset = p->iOffset;
-        p->pPrior = 0;
-        p->pLimit = 0;
-        p->iLimit = 0;
-        p->iOffset = 0;
-
-        intersectdest.iSDParm = tab2;
-        ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
-                          sqlite3SelectOpName(p->op)));
-        TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n"));
-        rc = sqlite3Select(pParse, p, &intersectdest);
-        testcase( rc!=SQLITE_OK );
-        pDelete = p->pPrior;
-        p->pPrior = pPrior;
-        if( p->nSelectRow>pPrior->nSelectRow ){
-          p->nSelectRow = pPrior->nSelectRow;
-        }
-        sqlite3ExprDelete(db, p->pLimit);
-
-        /* Reinstate the LIMIT counters prior to running the final intersect */
-        p->pLimit = pLimit;
-        p->iLimit = iLimit;
-        p->iOffset = iOffset;
-
-        /* Generate code to take the intersection of the two temporary
-        ** tables.
-        */
-        if( rc ) break;
-        assert( p->pEList );
-        sqlite3VdbeAddOp1(v, OP_Rewind, tab1);
-        r1 = sqlite3GetTempReg(pParse);
-        iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
-        iCont = sqlite3VdbeMakeLabel(pParse);
-        sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
-        VdbeCoverage(v);
-        sqlite3ReleaseTempReg(pParse, r1);
-        selectInnerLoop(pParse, p, tab1,
-                        0, 0, &dest, iCont, iBreak);
-        sqlite3VdbeResolveLabel(v, iCont);
-        sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
-        sqlite3VdbeResolveLabel(v, iBreak);
-        sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
-        sqlite3VdbeJumpHere(v, emptyBypass);
-        sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
-        break;
-      }
-    }
-
-  #ifndef SQLITE_OMIT_EXPLAIN
-    if( p->pNext==0 ){
-      ExplainQueryPlanPop(pParse);
-    }
-  #endif
-  }
-  if( pParse->nErr ) goto multi_select_end;
-
-  /* Compute collating sequences used by
-  ** temporary tables needed to implement the compound select.
-  ** Attach the KeyInfo structure to all temporary tables.
-  **
-  ** This section is run by the right-most SELECT statement only.
-  ** SELECT statements to the left always skip this part.  The right-most
-  ** SELECT might also skip this part if it has no ORDER BY clause and
-  ** no temp tables are required.
-  */
-  if( p->selFlags & SF_UsesEphemeral ){
-    int i;                        /* Loop counter */
-    KeyInfo *pKeyInfo;            /* Collating sequence for the result set */
-    Select *pLoop;                /* For looping through SELECT statements */
-    CollSeq **apColl;             /* For looping through pKeyInfo->aColl[] */
-    int nCol;                     /* Number of columns in result set */
-
-    assert( p->pNext==0 );
-    assert( p->pEList!=0 );
-    nCol = p->pEList->nExpr;
-    pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
-    if( !pKeyInfo ){
-      rc = SQLITE_NOMEM_BKPT;
+    assert( !pPrior->pLimit );
+    pPrior->iLimit = p->iLimit;
+    pPrior->iOffset = p->iOffset;
+    pPrior->pLimit = sqlite3ExprDup(db, p->pLimit, 0);
+    TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n"));
+    rc = sqlite3Select(pParse, pPrior, &dest);
+    sqlite3ExprDelete(db, pPrior->pLimit);
+    pPrior->pLimit = 0;
+    if( rc ){
       goto multi_select_end;
     }
-    for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
-      *apColl = multiSelectCollSeq(pParse, p, i);
-      if( 0==*apColl ){
-        *apColl = db->pDfltColl;
-      }
+    p->pPrior = 0;
+    p->iLimit = pPrior->iLimit;
+    p->iOffset = pPrior->iOffset;
+    if( p->iLimit ){
+      addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
+      VdbeComment((v, "Jump ahead if LIMIT reached"));
+      if( p->iOffset ){
+        sqlite3VdbeAddOp3(v, OP_OffsetLimit,
+                          p->iLimit, p->iOffset+1, p->iOffset);
+      }
+    }
+    ExplainQueryPlan((pParse, 1, "UNION ALL"));
+    TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n"));
+    rc = sqlite3Select(pParse, p, &dest);
+    testcase( rc!=SQLITE_OK );
+    pDelete = p->pPrior;
+    p->pPrior = pPrior;
+    p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+    if( p->pLimit
+     && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse)
+     && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
+    ){
+      p->nSelectRow = sqlite3LogEst((u64)nLimit);
     }
-
-    for(pLoop=p; pLoop; pLoop=pLoop->pPrior){
-      for(i=0; i<2; i++){
-        int addr = pLoop->addrOpenEphm[i];
-        if( addr<0 ){
-          /* If [0] is unused then [1] is also unused.  So we can
-          ** always safely abort as soon as the first unused slot is found */
-          assert( pLoop->addrOpenEphm[1]<0 );
-          break;
-        }
-        sqlite3VdbeChangeP2(v, addr, nCol);
-        sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo),
-                            P4_KEYINFO);
-        pLoop->addrOpenEphm[i] = -1;
-      }
+    if( addr ){
+      sqlite3VdbeJumpHere(v, addr);
+    }
+#ifndef SQLITE_OMIT_EXPLAIN
+    if( p->pNext==0 ){
+      ExplainQueryPlanPop(pParse);
     }
-    sqlite3KeyInfoUnref(pKeyInfo);
+#endif
   }
 
 multi_select_end:
@@ -150054,8 +151942,8 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectWrongNumTermsError(Parse *pParse
 ** Code an output subroutine for a coroutine implementation of a
 ** SELECT statement.
 **
-** The data to be output is contained in pIn->iSdst.  There are
-** pIn->nSdst columns to be output.  pDest is where the output should
+** The data to be output is contained in an array of pIn->nSdst registers
+** starting at register pIn->iSdst.  pDest is where the output should
 ** be sent.
 **
 ** regReturn is the number of the register holding the subroutine
@@ -150084,6 +151972,8 @@ static int SQLITE_APICALL generateOutputSubroutine(
   int iContinue;
   int addr;
 
+  assert( pIn->eDest==SRT_Coroutine );
+
   addr = sqlite3VdbeCurrentAddr(v);
   iContinue = sqlite3VdbeMakeLabel(pParse);
 
@@ -150105,23 +151995,60 @@ static int SQLITE_APICALL generateOutputSubroutine(
   */
   codeOffset(v, p->iOffset, iContinue);
 
-  assert( pDest->eDest!=SRT_Exists );
-  assert( pDest->eDest!=SRT_Table );
   switch( pDest->eDest ){
     /* Store the result as data using a unique key.
     */
+    case SRT_Fifo:
+    case SRT_DistFifo:
+    case SRT_Table:
     case SRT_EphemTab: {
       int r1 = sqlite3GetTempReg(pParse);
       int r2 = sqlite3GetTempReg(pParse);
+      int iParm = pDest->iSDParm;
+      testcase( pDest->eDest==SRT_Table );
+      testcase( pDest->eDest==SRT_EphemTab );
+      testcase( pDest->eDest==SRT_Fifo );
+      testcase( pDest->eDest==SRT_DistFifo );
       sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1);
-      sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2);
-      sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2);
+#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG)
+      /* A destination of SRT_Table and a non-zero iSDParm2 parameter means
+      ** that this is an "UPDATE ... FROM" on a virtual table or view. In this
+      ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC.
+      ** This does not affect operation in any way - it just allows MakeRecord
+      ** to process OPFLAG_NOCHANGE values without an assert() failing. */
+      if( pDest->eDest==SRT_Table && pDest->iSDParm2 ){
+        sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
+      }
+#endif
+#ifndef SQLITE_OMIT_CTE
+      if( pDest->eDest==SRT_DistFifo ){
+        /* If the destination is DistFifo, then cursor (iParm+1) is open
+        ** on an ephemeral index that is used to enforce uniqueness on the
+        ** total result.  At this point, we are processing the setup portion
+        ** of the recursive CTE using the merge algorithm, so the results are
+        ** guaranteed to be unique anyhow.  But we still need to populate the
+        ** (iParm+1) cursor for use by the subsequent recursive phase.
+        */
+        sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm+1, r1,
+                             pIn->iSdst, pIn->nSdst);
+      }
+#endif
+      sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
+      sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2);
       sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
       sqlite3ReleaseTempReg(pParse, r2);
       sqlite3ReleaseTempReg(pParse, r1);
       break;
     }
 
+    /* If any row exist in the result set, record that fact and abort.
+    */
+    case SRT_Exists: {
+      sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm);
+      /* The LIMIT clause will terminate the loop for us */
+      break;
+    }
+
 #ifndef SQLITE_OMIT_SUBQUERY
     /* If we are creating a set for an "expr IN (SELECT ...)".
     */
@@ -150168,9 +152095,51 @@ static int SQLITE_APICALL generateOutputSubroutine(
       break;
     }
 
+#ifndef SQLITE_OMIT_CTE
+    /* Write the results into a priority queue that is order according to
+    ** pDest->pOrderBy (in pSO).  pDest->iSDParm (in iParm) is the cursor for an
+    ** index with pSO->nExpr+2 columns.  Build a key using pSO for the first
+    ** pSO->nExpr columns, then make sure all keys are unique by adding a
+    ** final OP_Sequence column.  The last column is the record as a blob.
+    */
+    case SRT_DistQueue:
+    case SRT_Queue: {
+      int nKey;
+      int r1, r2, r3, ii;
+      ExprList *pSO;
+      int iParm = pDest->iSDParm;
+      pSO = pDest->pOrderBy;
+      assert( pSO );
+      nKey = pSO->nExpr;
+      r1 = sqlite3GetTempReg(pParse);
+      r2 = sqlite3GetTempRange(pParse, nKey+2);
+      r3 = r2+nKey+1;
+
+      sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r3);
+      if( pDest->eDest==SRT_DistQueue ){
+        sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3);
+      }
+      for(ii=0; ii<nKey; ii++){
+        sqlite3VdbeAddOp2(v, OP_SCopy,
+                          pIn->iSdst + pSO->a[ii].u.x.iOrderByCol - 1,
+                          r2+ii);
+      }
+      sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey);
+      sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1);
+      sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, r2, nKey+2);
+      sqlite3ReleaseTempReg(pParse, r1);
+      sqlite3ReleaseTempRange(pParse, r2, nKey+2);
+      break;
+    }
+#endif /* SQLITE_OMIT_CTE */
+
+    /* Ignore the output */
+    case SRT_Discard: {
+      break;
+    }
+
     /* If none of the above, then the result destination must be
-    ** SRT_Output.  This routine is never called with any other
-    ** destination other than the ones handled above or SRT_Output.
+    ** SRT_Output.
     **
     ** For SRT_Output, results are stored in a sequence of registers.
     ** Then the OP_ResultRow opcode is used to cause sqlite3_step() to
@@ -150198,8 +152167,9 @@ static int SQLITE_APICALL generateOutputSubroutine(
 }
 
 /*
-** Alternative compound select code generator for cases when there
-** is an ORDER BY clause.
+** Generate code for a compound SELECT statement using a merge
+** algorithm.  The compound must have an ORDER BY clause for this
+** to work.
 **
 ** We assume a query of the following form:
 **
@@ -150216,7 +152186,7 @@ static int SQLITE_APICALL generateOutputSubroutine(
 **
 **    outB:    Move the output of the selectB coroutine into the output
 **             of the compound query.  (Only generated for UNION and
-**             UNION ALL.  EXCEPT and INSERTSECT never output a row that
+**             UNION ALL.  EXCEPT and INTERSECT never output a row that
 **             appears only in B.)
 **
 **    AltB:    Called when there is data from both coroutines and A<B.
@@ -150269,10 +152239,8 @@ static int SQLITE_APICALL generateOutputSubroutine(
 **    AeqB: ...
 **    AgtB: ...
 **    Init: initialize coroutine registers
-**          yield coA
-**          if eof(A) goto EofA
-**          yield coB
-**          if eof(B) goto EofB
+**          yield coA, on eof goto EofA
+**          yield coB, on eof goto EofB
 **    Cmpr: Compare A, B
 **          Jump AltB, AeqB, AgtB
 **     End: ...
@@ -150280,10 +152248,10 @@ static int SQLITE_APICALL generateOutputSubroutine(
 ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not
 ** actually called using Gosub and they do not Return.  EofA and EofB loop
 ** until all data is exhausted then jump to the "end" label.  AltB, AeqB,
-** and AgtB jump to either L2 or to one of EofA or EofB.
+** and AgtB jump to either Cmpr or to one of EofA or EofB.
 */
 #ifndef SQLITE_OMIT_COMPOUND_SELECT
-static int SQLITE_APICALL multiSelectOrderBy(
+static int SQLITE_APICALL multiSelectByMerge(
   Parse *pParse,        /* Parsing context */
   Select *p,            /* The right-most of SELECTs to be coded */
   SelectDest *pDest     /* What to do with query results */
@@ -150355,10 +152323,8 @@ static int SQLITE_APICALL multiSelectOrderBy(
         if( pItem->u.x.iOrderByCol==i ) break;
       }
       if( j==nOrderBy ){
-        Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
+        Expr *pNew = sqlite3ExprInt32(db, i);
         if( pNew==0 ) return SQLITE_NOMEM_BKPT;
-        pNew->flags |= EP_IntValue;
-        pNew->u.iValue = i;
         p->pOrderBy = pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
         if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i;
       }
@@ -150366,26 +152332,29 @@ static int SQLITE_APICALL multiSelectOrderBy(
   }
 
   /* Compute the comparison permutation and keyinfo that is used with
-  ** the permutation used to determine if the next
-  ** row of results comes from selectA or selectB.  Also add explicit
-  ** collations to the ORDER BY clause terms so that when the subqueries
-  ** to the right and the left are evaluated, they use the correct
-  ** collation.
+  ** the permutation to determine if the next row of results comes
+  ** from selectA or selectB.  Also add literal collations to the
+  ** ORDER BY clause terms so that when selectA and selectB are
+  ** evaluated, they use the correct collation.
   */
   aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1));
   if( aPermute ){
     struct ExprList_item *pItem;
+    int bKeep = 0;
     aPermute[0] = nOrderBy;
     for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
       assert( pItem!=0 );
       assert( pItem->u.x.iOrderByCol>0 );
       assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
       aPermute[i] = pItem->u.x.iOrderByCol - 1;
+      if( aPermute[i]!=(u32)i-1 ) bKeep = 1;
+    }
+     if( bKeep==0 ){
+      sqlite3DbFreeNN(db, aPermute);
+      aPermute = 0;
     }
-    pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
-  }else{
-    pKeyMerge = 0;
   }
+  pKeyMerge = multiSelectByMergeKeyInfo(pParse, p, 1);
 
   /* Allocate a range of temporary registers and the KeyInfo needed
   ** for the logic that removes duplicate result rows when the
@@ -150464,7 +152433,7 @@ static int SQLITE_APICALL multiSelectOrderBy(
   */
   addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
   addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
-  VdbeComment((v, "left SELECT"));
+  VdbeComment((v, "SUBR: next-A"));
   pPrior->iLimit = regLimitA;
   ExplainQueryPlan((pParse, 1, "LEFT"));
   sqlite3Select(pParse, pPrior, &destA);
@@ -150476,7 +152445,7 @@ static int SQLITE_APICALL multiSelectOrderBy(
   */
   addrSelectB = sqlite3VdbeCurrentAddr(v) + 1;
   addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
-  VdbeComment((v, "right SELECT"));
+  VdbeComment((v, "SUBR: next-B"));
   savedLimit = p->iLimit;
   savedOffset = p->iOffset;
   p->iLimit = regLimitB;
@@ -150490,7 +152459,7 @@ static int SQLITE_APICALL multiSelectOrderBy(
   /* Generate a subroutine that outputs the current row of the A
   ** select as the next output row of the compound select.
   */
-  VdbeNoopComment((v, "Output routine for A"));
+  VdbeNoopComment((v, "SUBR: out-A"));
   addrOutA = generateOutputSubroutine(pParse,
                  p, &destA, pDest, regOutA,
                  regPrev, pKeyDup, labelEnd);
@@ -150499,7 +152468,7 @@ static int SQLITE_APICALL multiSelectOrderBy(
   ** select as the next output row of the compound select.
   */
   if( op==TK_ALL || op==TK_UNION ){
-    VdbeNoopComment((v, "Output routine for B"));
+    VdbeNoopComment((v, "SUBR: out-B"));
     addrOutB = generateOutputSubroutine(pParse,
                  p, &destB, pDest, regOutB,
                  regPrev, pKeyDup, labelEnd);
@@ -150512,10 +152481,12 @@ static int SQLITE_APICALL multiSelectOrderBy(
   if( op==TK_EXCEPT || op==TK_INTERSECT ){
     addrEofA_noB = addrEofA = labelEnd;
   }else{
-    VdbeNoopComment((v, "eof-A subroutine"));
+    VdbeNoopComment((v, "SUBR: eof-A"));
     addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
+    VdbeComment((v, "out-B"));
     addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
                                      VdbeCoverage(v);
+    VdbeComment((v, "next-B"));
     sqlite3VdbeGoto(v, addrEofA);
     p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
   }
@@ -150527,17 +152498,20 @@ static int SQLITE_APICALL multiSelectOrderBy(
     addrEofB = addrEofA;
     if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
   }else{
-    VdbeNoopComment((v, "eof-B subroutine"));
+    VdbeNoopComment((v, "SUBR: eof-B"));
     addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
+    VdbeComment((v, "out-A"));
     sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v);
+    VdbeComment((v, "next-A"));
     sqlite3VdbeGoto(v, addrEofB);
   }
 
   /* Generate code to handle the case of A<B
   */
-  VdbeNoopComment((v, "A-lt-B subroutine"));
   addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
+  VdbeComment((v, "out-A"));
   sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
+  VdbeComment((v, "next-A"));
   sqlite3VdbeGoto(v, labelCmpr);
 
   /* Generate code to handle the case of A==B
@@ -150548,36 +152522,48 @@ static int SQLITE_APICALL multiSelectOrderBy(
     addrAeqB = addrAltB;
     addrAltB++;
   }else{
-    VdbeNoopComment((v, "A-eq-B subroutine"));
-    addrAeqB =
-    sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
-    sqlite3VdbeGoto(v, labelCmpr);
+    addrAeqB = addrAltB + 1;
   }
 
   /* Generate code to handle the case of A>B
   */
-  VdbeNoopComment((v, "A-gt-B subroutine"));
   addrAgtB = sqlite3VdbeCurrentAddr(v);
   if( op==TK_ALL || op==TK_UNION ){
     sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
+    VdbeComment((v, "out-B"));
+    sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
+    VdbeComment((v, "next-B"));
+    sqlite3VdbeGoto(v, labelCmpr);
+  }else{
+    addrAgtB++; /* Just do next-B.  Might as well use the next-B call
+                ** in the next code block */
   }
-  sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
-  sqlite3VdbeGoto(v, labelCmpr);
 
   /* This code runs once to initialize everything.
   */
   sqlite3VdbeJumpHere(v, addr1);
   sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v);
+  VdbeComment((v, "next-A"));
+  /* v---  Also the A>B case for EXCEPT and INTERSECT */
   sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
+  VdbeComment((v, "next-B"));
 
   /* Implement the main merge loop
   */
+  if( aPermute!=0 ){
+    sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
+  }
   sqlite3VdbeResolveLabel(v, labelCmpr);
-  sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
   sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy,
                          (char*)pKeyMerge, P4_KEYINFO);
-  sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE);
-  sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v);
+  if( aPermute!=0 ){
+    sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE);
+  }
+  sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB);
+  VdbeCoverageIf(v, op==TK_ALL);
+  VdbeCoverageIf(v, op==TK_UNION);
+  VdbeCoverageIf(v, op==TK_EXCEPT);
+  VdbeCoverageIf(v, op==TK_INTERSECT);
 
   /* Jump to the this point in order to terminate the query.
   */
@@ -151494,7 +153480,7 @@ static int SQLITE_APICALL flattenSubquery(
     }
     pSubitem->fg.jointype |= jointype;
 
-    /* Now begin substituting subquery result set expressions for
+    /* Begin substituting subquery result set expressions for
     ** references to the iParent in the outer query.
     **
     ** Example:
@@ -151506,7 +153492,7 @@ static int SQLITE_APICALL flattenSubquery(
     ** We look at every expression in the outer query and every place we see
     ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10".
     */
-    if( pSub->pOrderBy && (pParent->selFlags & SF_NoopOrderBy)==0 ){
+    if( pSub->pOrderBy ){
       /* At this point, any non-zero iOrderByCol values indicate that the
       ** ORDER BY column expression is identical to the iOrderByCol'th
       ** expression returned by SELECT statement pSub. Since these values
@@ -151514,9 +153500,9 @@ static int SQLITE_APICALL flattenSubquery(
       ** zero them before transferring the ORDER BY clause.
       **
       ** Not doing this may cause an error if a subsequent call to this
-      ** function attempts to flatten a compound sub-query into pParent
-      ** (the only way this can happen is if the compound sub-query is
-      ** currently part of pSub->pSrc). See ticket [d11a6e908f].  */
+      ** function attempts to flatten a compound sub-query into pParent.
+      ** See ticket [d11a6e908f].
+      */
       ExprList *pOrderBy = pSub->pOrderBy;
       for(i=0; i<pOrderBy->nExpr; i++){
         pOrderBy->a[i].u.x.iOrderByCol = 0;
@@ -152134,6 +154120,16 @@ static int SQLITE_APICALL pushDownWhereTerms(
       x.pEList = pSubq->pEList;
       x.pCList = findLeftmostExprlist(pSubq);
       pNew = substExpr(&x, pNew);
+      assert( pNew!=0 || pParse->nErr!=0 );
+      if( pParse->nErr==0 && pNew->op==TK_IN && ExprUseXSelect(pNew) ){
+        assert( pNew->x.pSelect!=0 );
+        pNew->x.pSelect->selFlags |= SF_ClonedRhsIn;
+        assert( pWhere!=0 );
+        assert( pWhere->op==TK_IN );
+        assert( ExprUseXSelect(pWhere) );
+        assert( pWhere->x.pSelect!=0 );
+        pWhere->x.pSelect->selFlags |= SF_ClonedRhsIn;
+      }
 #ifndef SQLITE_OMIT_WINDOWFUNC
       if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
         /* Restriction 6c has prevented push-down in this case */
@@ -152368,14 +154364,14 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3IndexedByLookup(Parse *pParse, SrcItem
 **    SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2)
 **     ORDER BY ... COLLATE ...
 **
-** This transformation is necessary because the multiSelectOrderBy() routine
+** This transformation is necessary because the multiSelectByMerge() routine
 ** above that generates the code for a compound SELECT with an ORDER BY clause
 ** uses a merge algorithm that requires the same collating sequence on the
 ** result columns as on the ORDER BY clause.  See ticket
 ** http://sqlite.org/src/info/6709574d2a
 **
 ** This transformation is only needed for EXCEPT, INTERSECT, and UNION.
-** The UNION ALL operator works fine with multiSelectOrderBy() even when
+** The UNION ALL operator works fine with multiSelectByMerge() even when
 ** there are COLLATE terms in the ORDER BY.
 */
 static int SQLITE_APICALL convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
@@ -152921,7 +154917,7 @@ static int SQLITE_APICALL selectExpander(Walker *pWalker, Select *p){
         }
 #ifndef SQLITE_OMIT_VIRTUALTABLE
         else if( ALWAYS(IsVirtual(pTab))
-         && pFrom->fg.fromDDL
+         && (pFrom->fg.fromDDL || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL))
          && ALWAYS(pTab->u.vtab.p!=0)
          && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0)
         ){
@@ -153216,7 +155212,7 @@ static int SQLITE_APICALL selectExpander(Walker *pWalker, Select *p){
 ** Always assert.  This xSelectCallback2 implementation proves that the
 ** xSelectCallback2 is never invoked.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectWalkAssert2(Walker *NotUsed, Select *NotUsed2){
+SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker *NotUsed, Select *NotUsed2){
   UNUSED_PARAMETER2(NotUsed, NotUsed2);
   assert( 0 );
 }
@@ -153874,7 +155870,7 @@ static int SQLITE_APICALL havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
      && pExpr->pAggInfo==0
     ){
       sqlite3 *db = pWalker->pParse->db;
-      Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
+      Expr *pNew = sqlite3ExprInt32(db, 1);
       if( pNew ){
         Expr *pWhere = pS->pWhere;
         SWAP(Expr, *pNew, *pExpr);
@@ -154192,6 +156188,7 @@ static SQLITE_NOINLINE void SQLITE_APICALL existsToJoin(
    && !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON)
    && ALWAYS(p->pSrc!=0)
    && p->pSrc->nSrc<BMS
+   && (p->pLimit==0 || p->pLimit->pRight==0)
   ){
     if( pWhere->op==TK_AND ){
       Expr *pRight = pWhere->pRight;
@@ -154205,15 +156202,26 @@ static SQLITE_NOINLINE void SQLITE_APICALL existsToJoin(
        && (pSub->selFlags & SF_Aggregate)==0
        && !pSub->pSrc->a[0].fg.isSubquery
        && pSub->pLimit==0
+       && pSub->pPrior==0
       ){
+        /* Before combining the sub-select with the parent, renumber the
+        ** cursor used by the subselect. This is because the EXISTS expression
+        ** might be a copy of another EXISTS expression from somewhere
+        ** else in the tree, and in this case it is important that it use
+        ** a unique cursor number.  */
+        sqlite3 *db = pParse->db;
+        int *aCsrMap = sqlite3DbMallocZero(db, (pParse->nTab+2)*sizeof(int));
+        if( aCsrMap==0 ) return;
+        aCsrMap[0] = (pParse->nTab+1);
+        renumberCursors(pParse, pSub, -1, aCsrMap);
+        sqlite3DbFree(db, aCsrMap);
+
         memset(pWhere, 0, sizeof(*pWhere));
         pWhere->op = TK_INTEGER;
         pWhere->u.iValue = 1;
         ExprSetProperty(pWhere, EP_IntValue);
-
         assert( p->pWhere!=0 );
         pSub->pSrc->a[0].fg.fromExists = 1;
-        pSub->pSrc->a[0].fg.jointype |= JT_CROSS;
         p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc);
         if( pSubWhere ){
           p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere);
@@ -154228,7 +156236,6 @@ static SQLITE_NOINLINE void SQLITE_APICALL existsToJoin(
           sqlite3TreeViewSelect(0, p, 0);
         }
 #endif
-        existsToJoin(pParse, p, pSubWhere);
       }
     }
   }
@@ -154241,6 +156248,7 @@ typedef struct CheckOnCtx CheckOnCtx;
 struct CheckOnCtx {
   SrcList *pSrc;                  /* SrcList for this context */
   int iJoin;                      /* Cursor numbers must be =< than this */
+  int bFuncArg;                   /* True for table-function arg */
   CheckOnCtx *pParent;            /* Parent context */
 };
 
@@ -154288,11 +156296,16 @@ static int SQLITE_APICALL selectCheckOnClausesExpr(Walker *pWalker, Expr *pExpr)
     ** does not refer to a table to the right of CheckOnCtx.iJoin. */
     do {
       SrcList *pSrc = pCtx->pSrc;
+      int nSrc = pSrc->nSrc;
       int iTab = pExpr->iTable;
-      if( iTab>=pSrc->a[0].iCursor && iTab<=pSrc->a[pSrc->nSrc-1].iCursor ){
+      int ii;
+      for(ii=0; ii<nSrc && pSrc->a[ii].iCursor!=iTab; ii++){}
+      if( ii<nSrc ){
         if( pCtx->iJoin && iTab>pCtx->iJoin ){
           sqlite3ErrorMsg(pWalker->pParse,
-              "ON clause references tables to its right");
+              "%s references tables to its right",
+              (pCtx->bFuncArg ? "table-function argument" : "ON clause")
+          );
           return WRC_Abort;
         }
         break;
@@ -154327,9 +156340,10 @@ static int SQLITE_APICALL selectCheckOnClausesSelect(Walker *pWalker, Select *pS
 ** Check all ON clauses in pSelect to verify that they do not reference
 ** columns to the right.
 */
-static void SQLITE_APICALL selectCheckOnClauses(Parse *pParse, Select *pSelect){
+SQLITE_PRIVATE void SQLITE_APICALL sqlite3SelectCheckOnClauses(Parse *pParse, Select *pSelect){
   Walker w;
   CheckOnCtx sCtx;
+  int ii;
   assert( pSelect->selFlags & SF_OnToWhere );
   assert( pSelect->pSrc!=0 && pSelect->pSrc->nSrc>=2 );
   memset(&w, 0, sizeof(w));
@@ -154339,8 +156353,46 @@ static void SQLITE_APICALL selectCheckOnClauses(Parse *pParse, Select *pSelect){
   w.u.pCheckOnCtx = &sCtx;
   memset(&sCtx, 0, sizeof(sCtx));
   sCtx.pSrc = pSelect->pSrc;
-  sqlite3WalkExprNN(&w, pSelect->pWhere);
+  sqlite3WalkExpr(&w, pSelect->pWhere);
   pSelect->selFlags &= ~SF_OnToWhere;
+
+  /* Check for any table-function args that are attached to virtual tables
+  ** on the RHS of an outer join. They are subject to the same constraints
+  ** as ON clauses. */
+  sCtx.bFuncArg = 1;
+  for(ii=0; ii<pSelect->pSrc->nSrc; ii++){
+    SrcItem *pItem = &pSelect->pSrc->a[ii];
+    if( pItem->fg.isTabFunc
+     && (pItem->fg.jointype & JT_OUTER)
+    ){
+      sCtx.iJoin = pItem->iCursor;
+      sqlite3WalkExprList(&w, pItem->u1.pFuncArg);
+    }
+  }
+}
+
+/*
+** If p2 exists and p1 and p2 have the same number of terms, then change
+** every term of p1 to have the same sort order as p2 and return true.
+**
+** If p2 is NULL or p1 and p2 are different lengths, then make no changes
+** and return false.
+**
+** p1 must be non-NULL.
+*/
+static int SQLITE_APICALL sqlite3CopySortOrder(ExprList *p1, ExprList *p2){
+  assert( p1 );
+  if( p2 && p1->nExpr==p2->nExpr ){
+    int ii;
+    for(ii=0; ii<p1->nExpr; ii++){
+      u8 sortFlags;
+      sortFlags = p2->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC;
+      p1->a[ii].fg.sortFlags = sortFlags;
+    }
+    return 1;
+  }else{
+    return 0;
+  }
 }
 
 /*
@@ -154438,8 +156490,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Select(
   assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
   assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue );
   if( IgnorableDistinct(pDest) ){
-    assert(pDest->eDest==SRT_Exists     || pDest->eDest==SRT_Union ||
-           pDest->eDest==SRT_Except     || pDest->eDest==SRT_Discard ||
+    assert(pDest->eDest==SRT_Exists     || pDest->eDest==SRT_Discard ||
            pDest->eDest==SRT_DistQueue  || pDest->eDest==SRT_DistFifo );
     /* All of these destinations are also able to ignore the ORDER BY clause */
     if( p->pOrderBy ){
@@ -154455,7 +156506,6 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Select(
       p->pOrderBy = 0;
     }
     p->selFlags &= ~(u32)SF_Distinct;
-    p->selFlags |= SF_NoopOrderBy;
   }
   sqlite3SelectPrep(pParse, p, 0);
   if( pParse->nErr ){
@@ -154470,18 +156520,6 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Select(
   }
 #endif
 
-  /* If the SELECT statement contains ON clauses that were moved into
-  ** the WHERE clause, go through and verify that none of the terms
-  ** in the ON clauses reference tables to the right of the ON clause.
-  ** Do this now, after name resolution, but before query flattening
-  */
-  if( p->selFlags & SF_OnToWhere ){
-    selectCheckOnClauses(pParse, p);
-    if( pParse->nErr ){
-      goto select_end;
-    }
-  }
-
   /* If the SF_UFSrcCheck flag is set, then this function is being called
   ** as part of populating the temp table for an UPDATE...FROM statement.
   ** In this case, it is an error if the target object (pSrc->a[0]) name
@@ -154995,7 +157033,8 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Select(
   ** BY and DISTINCT, and an index or separate temp-table for the other.
   */
   if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
-   && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
+   && sqlite3CopySortOrder(pEList, sSort.pOrderBy)
+   && sqlite3ExprListCompare(pEList, sSort.pOrderBy, -1)==0
    && OptimizationEnabled(db, SQLITE_GroupByOrder)
 #ifndef SQLITE_OMIT_WINDOWFUNC
    && p->pWin==0
@@ -155209,21 +157248,10 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Select(
       ** but not actually sorted. Either way, record the fact that the
       ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
       ** variable.  */
-      if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){
-        int ii;
-        /* The GROUP BY processing doesn't care whether rows are delivered in
-        ** ASC or DESC order - only that each group is returned contiguously.
-        ** So set the ASC/DESC flags in the GROUP BY to match those in the
-        ** ORDER BY to maximize the chances of rows being delivered in an
-        ** order that makes the ORDER BY redundant.  */
-        for(ii=0; ii<pGroupBy->nExpr; ii++){
-          u8 sortFlags;
-          sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC;
-          pGroupBy->a[ii].fg.sortFlags = sortFlags;
-        }
-        if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
-          orderByGrp = 1;
-        }
+      if( sqlite3CopySortOrder(pGroupBy, sSort.pOrderBy)
+       && sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0
+      ){
+        orderByGrp = 1;
       }
     }else{
       assert( 0==sqlite3LogEst(1) );
@@ -156033,7 +158061,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3DeleteTriggerStep(sqlite3 *db, Trigger
     sqlite3SelectDelete(db, pTmp->pSelect);
     sqlite3IdListDelete(db, pTmp->pIdList);
     sqlite3UpsertDelete(db, pTmp->pUpsert);
-    sqlite3SrcListDelete(db, pTmp->pFrom);
+    sqlite3SrcListDelete(db, pTmp->pSrc);
     sqlite3DbFree(db, pTmp->zSpan);
 
     sqlite3DbFree(db, pTmp);
@@ -156222,11 +158250,16 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3BeginTrigger(
     }
   }
 
+  /* NB: The SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES compile-time option is
+  ** experimental and unsupported. Do not use it unless understand the
+  ** implications and you cannot get by without this capability. */
+#if !defined(SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES) /* Experimental */
   /* Do not create a trigger on a system table */
   if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
     sqlite3ErrorMsg(pParse, "cannot create trigger on system table");
     goto trigger_cleanup;
   }
+#endif
 
   /* INSTEAD of triggers are only for views and views only support INSTEAD
   ** of triggers.
@@ -156338,6 +158371,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3FinishTrigger(
   if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup;
   zName = pTrig->zName;
   iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
+  assert( iDb>=00 && iDb<db->nDb );
   pTrig->step_list = pStepList;
   while( pStepList ){
     pStepList->pTrig = pTrig;
@@ -156372,12 +158406,12 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3FinishTrigger(
     if( sqlite3ReadOnlyShadowTables(db) ){
       TriggerStep *pStep;
       for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){
-        if( pStep->zTarget!=0
-         && sqlite3ShadowTableName(db, pStep->zTarget)
+        if( pStep->pSrc!=0
+         && sqlite3ShadowTableName(db, pStep->pSrc->a[0].zName)
         ){
           sqlite3ErrorMsg(pParse,
             "trigger \"%s\" may not write to shadow table \"%s\"",
-            pTrig->zName, pStep->zTarget);
+            pTrig->zName, pStep->pSrc->a[0].zName);
           goto triggerfinish_cleanup;
         }
       }
@@ -156468,26 +158502,39 @@ SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerSelectStep(
 static TriggerStep *SQLITE_APICALL triggerStepAllocate(
   Parse *pParse,              /* Parser context */
   u8 op,                      /* Trigger opcode */
-  Token *pName,               /* The target name */
+  SrcList *pTabList,          /* Target table */
   const char *zStart,         /* Start of SQL text */
   const char *zEnd            /* End of SQL text */
 ){
+  Trigger *pNew = pParse->pNewTrigger;
   sqlite3 *db = pParse->db;
-  TriggerStep *pTriggerStep;
+  TriggerStep *pTriggerStep = 0;
 
-  if( pParse->nErr ) return 0;
-  pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
-  if( pTriggerStep ){
-    char *z = (char*)&pTriggerStep[1];
-    memcpy(z, pName->z, pName->n);
-    sqlite3Dequote(z);
-    pTriggerStep->zTarget = z;
-    pTriggerStep->op = op;
-    pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
-    if( IN_RENAME_OBJECT ){
-      sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
+  if( pParse->nErr==0 ){
+    if( pNew
+     && pNew->pSchema!=db->aDb[1].pSchema
+     && pTabList->a[0].u4.zDatabase
+    ){
+      sqlite3ErrorMsg(pParse,
+          "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
+          "statements within triggers");
+    }else{
+      pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
+      if( pTriggerStep ){
+        pTriggerStep->pSrc = sqlite3SrcListDup(db, pTabList, EXPRDUP_REDUCE);
+        pTriggerStep->op = op;
+        pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
+        if( pTriggerStep->pSrc && IN_RENAME_OBJECT ){
+          sqlite3RenameTokenRemap(pParse,
+              pTriggerStep->pSrc->a[0].zName,
+              pTabList->a[0].zName
+          );
+        }
+      }
     }
   }
+
+  sqlite3SrcListDelete(db, pTabList);
   return pTriggerStep;
 }
 
@@ -156500,7 +158547,7 @@ static TriggerStep *SQLITE_APICALL triggerStepAllocate(
 */
 SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerInsertStep(
   Parse *pParse,      /* Parser */
-  Token *pTableName,  /* Name of the table into which we insert */
+  SrcList *pTabList,  /* Table to INSERT into */
   IdList *pColumn,    /* List of columns in pTableName to insert into */
   Select *pSelect,    /* A SELECT statement that supplies values */
   u8 orconf,          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
@@ -156513,7 +158560,7 @@ SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerInsertStep(
 
   assert(pSelect != 0 || db->mallocFailed);
 
-  pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
+  pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTabList, zStart, zEnd);
   if( pTriggerStep ){
     if( IN_RENAME_OBJECT ){
       pTriggerStep->pSelect = pSelect;
@@ -156545,7 +158592,7 @@ SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerInsertStep(
 */
 SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerUpdateStep(
   Parse *pParse,          /* Parser */
-  Token *pTableName,   /* Name of the table to be updated */
+  SrcList *pTabList,   /* Name of the table to be updated */
   SrcList *pFrom,      /* FROM clause for an UPDATE-FROM, or NULL */
   ExprList *pEList,    /* The SET clause: list of column and new values */
   Expr *pWhere,        /* The WHERE clause */
@@ -156556,21 +158603,36 @@ SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerUpdateStep(
   sqlite3 *db = pParse->db;
   TriggerStep *pTriggerStep;
 
-  pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
+  pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTabList, zStart, zEnd);
   if( pTriggerStep ){
+    SrcList *pFromDup = 0;
     if( IN_RENAME_OBJECT ){
       pTriggerStep->pExprList = pEList;
       pTriggerStep->pWhere = pWhere;
-      pTriggerStep->pFrom = pFrom;
+      pFromDup = pFrom;
       pEList = 0;
       pWhere = 0;
       pFrom = 0;
     }else{
       pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
       pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
-      pTriggerStep->pFrom = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE);
+      pFromDup = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE);
     }
     pTriggerStep->orconf = orconf;
+
+    if( pFromDup && !IN_RENAME_OBJECT){
+      Select *pSub;
+      Token as = {0, 0};
+      pSub = sqlite3SelectNew(pParse, 0, pFromDup, 0,0,0,0, SF_NestedFrom, 0);
+      pFromDup = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &as, pSub ,0);
+    }
+    if( pFromDup && pTriggerStep->pSrc ){
+      pTriggerStep->pSrc = sqlite3SrcListAppendList(
+          pParse, pTriggerStep->pSrc, pFromDup
+      );
+    }else{
+      sqlite3SrcListDelete(db, pFromDup);
+    }
   }
   sqlite3ExprListDelete(db, pEList);
   sqlite3ExprDelete(db, pWhere);
@@ -156585,7 +158647,7 @@ SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerUpdateStep(
 */
 SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerDeleteStep(
   Parse *pParse,          /* Parser */
-  Token *pTableName,      /* The table from which rows are deleted */
+  SrcList *pTabList,      /* The table from which rows are deleted */
   Expr *pWhere,           /* The WHERE clause */
   const char *zStart,     /* Start of SQL text */
   const char *zEnd        /* End of SQL text */
@@ -156593,7 +158655,7 @@ SQLITE_PRIVATE TriggerStep *SQLITE_APICALL sqlite3TriggerDeleteStep(
   sqlite3 *db = pParse->db;
   TriggerStep *pTriggerStep;
 
-  pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
+  pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTabList, zStart, zEnd);
   if( pTriggerStep ){
     if( IN_RENAME_OBJECT ){
       pTriggerStep->pWhere = pWhere;
@@ -156793,6 +158855,7 @@ static SQLITE_NOINLINE Trigger *SQLITE_APICALL triggersReallyExist(
     p = pList;
     if( (pParse->db->flags & SQLITE_EnableTrigger)==0
      && pTab->pTrigger!=0
+     && sqlite3SchemaToIndex(pParse->db, pTab->pTrigger->pSchema)!=1
     ){
       /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off.  That means that
       ** only TEMP triggers are allowed.  Truncate the pList so that it
@@ -156855,52 +158918,6 @@ SQLITE_PRIVATE Trigger *SQLITE_APICALL sqlite3TriggersExist(
   return triggersReallyExist(pParse,pTab,op,pChanges,pMask);
 }
 
-/*
-** Convert the pStep->zTarget string into a SrcList and return a pointer
-** to that SrcList.
-**
-** This routine adds a specific database name, if needed, to the target when
-** forming the SrcList.  This prevents a trigger in one database from
-** referring to a target in another database.  An exception is when the
-** trigger is in TEMP in which case it can refer to any other database it
-** wants.
-*/
-SQLITE_PRIVATE SrcList *SQLITE_APICALL sqlite3TriggerStepSrc(
-  Parse *pParse,       /* The parsing context */
-  TriggerStep *pStep   /* The trigger containing the target token */
-){
-  sqlite3 *db = pParse->db;
-  SrcList *pSrc;                  /* SrcList to be returned */
-  char *zName = sqlite3DbStrDup(db, pStep->zTarget);
-  pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
-  assert( pSrc==0 || pSrc->nSrc==1 );
-  assert( zName || pSrc==0 );
-  if( pSrc ){
-    Schema *pSchema = pStep->pTrig->pSchema;
-    pSrc->a[0].zName = zName;
-    if( pSchema!=db->aDb[1].pSchema ){
-      assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 );
-      pSrc->a[0].u4.pSchema = pSchema;
-      pSrc->a[0].fg.fixedSchema = 1;
-    }
-    if( pStep->pFrom ){
-      SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0);
-      if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){
-        Select *pSubquery;
-        Token as;
-        pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0);
-        as.n = 0;
-        as.z = 0;
-        pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
-      }
-      pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup);
-    }
-  }else{
-    sqlite3DbFree(db, zName);
-  }
-  return pSrc;
-}
-
 /*
 ** Return true if the pExpr term from the RETURNING clause argument
 ** list is of the form "*".  Raise an error if the terms if of the
@@ -157166,7 +159183,7 @@ static int SQLITE_APICALL codeTriggerProgram(
     switch( pStep->op ){
       case TK_UPDATE: {
         sqlite3Update(pParse,
-          sqlite3TriggerStepSrc(pParse, pStep),
+          sqlite3SrcListDup(db, pStep->pSrc, 0),
           sqlite3ExprListDup(db, pStep->pExprList, 0),
           sqlite3ExprDup(db, pStep->pWhere, 0),
           pParse->eOrconf, 0, 0, 0
@@ -157176,7 +159193,7 @@ static int SQLITE_APICALL codeTriggerProgram(
       }
       case TK_INSERT: {
         sqlite3Insert(pParse,
-          sqlite3TriggerStepSrc(pParse, pStep),
+          sqlite3SrcListDup(db, pStep->pSrc, 0),
           sqlite3SelectDup(db, pStep->pSelect, 0),
           sqlite3IdListDup(db, pStep->pIdList),
           pParse->eOrconf,
@@ -157187,7 +159204,7 @@ static int SQLITE_APICALL codeTriggerProgram(
       }
       case TK_DELETE: {
         sqlite3DeleteFrom(pParse,
-          sqlite3TriggerStepSrc(pParse, pStep),
+          sqlite3SrcListDup(db, pStep->pSrc, 0),
           sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
         );
         sqlite3VdbeAddOp0(v, OP_ResetCount);
@@ -157212,7 +159229,7 @@ static int SQLITE_APICALL codeTriggerProgram(
 ** This function is used to add VdbeComment() annotations to a VDBE
 ** program. It is not used in production code, only for debugging.
 */
-static const char *SQLITE_APICALL onErrorText(int onError){
+static const char *onErrorText(int onError){
   switch( onError ){
     case OE_Abort:    return "abort";
     case OE_Rollback: return "rollback";
@@ -159509,9 +161526,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int SQLITE_APICALL sqlite3RunVacuum(
   pDb = &db->aDb[nDb];
   assert( strcmp(pDb->zDbSName,zDbVacuum)==0 );
   pTemp = pDb->pBt;
+  nRes = sqlite3BtreeGetRequestedReserve(pMain);
   if( pOut ){
     sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp));
     i64 sz = 0;
+    const char *zFilename;
     if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){
       rc = SQLITE_ERROR;
       sqlite3SetString(pzErrMsg, db, "output file already exists");
@@ -159523,8 +161542,16 @@ SQLITE_PRIVATE SQLITE_NOINLINE int SQLITE_APICALL sqlite3RunVacuum(
     ** they are for the database being vacuumed, except that PAGER_CACHESPILL
     ** is always set. */
     pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK);
+
+    /* If the VACUUM INTO target file is a URI filename and if the
+    ** "reserve=N" query parameter is present, reset the reserve to the
+    ** amount specified, if the amount is within range */
+    zFilename = sqlite3BtreeGetFilename(pTemp);
+    if( ALWAYS(zFilename) ){
+      int nNew = (int)sqlite3_uri_int64(zFilename, "reserve", nRes);
+      if( nNew>=0 && nNew<=255 ) nRes = nNew;
+    }
   }
-  nRes = sqlite3BtreeGetRequestedReserve(pMain);
 
   sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
   sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
@@ -163329,7 +165356,7 @@ SQLITE_PRIVATE Bitmask SQLITE_APICALL sqlite3WhereCodeOneLoopStart(
         if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){
           int iTab = pParse->nTab++;
           int iCache = ++pParse->nMem;
-          sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab);
+          sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab, 0);
           sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache);
         }else{
           codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
@@ -164053,7 +166080,7 @@ SQLITE_PRIVATE Bitmask SQLITE_APICALL sqlite3WhereCodeOneLoopStart(
     ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
     ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
     */
-    if( pWInfo->nLevel>1 ){
+    if( pWInfo->nLevel>1 || pTabItem->fg.fromExists ){
       int nNotReady;                 /* The number of notReady tables */
       SrcItem *origSrc;              /* Original list of tables */
       nNotReady = pWInfo->nLevel - iLevel - 1;
@@ -164066,6 +166093,13 @@ SQLITE_PRIVATE Bitmask SQLITE_APICALL sqlite3WhereCodeOneLoopStart(
       for(k=1; k<=nNotReady; k++){
         memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
       }
+
+      /* Clear the fromExists flag on the OR-optimized table entry so that
+      ** the calls to sqlite3WhereEnd() do not code early-exits after the
+      ** first row is visited. The early exit applies to this table's
+      ** overall loop - including the multiple OR branches and any WHERE
+      ** conditions not passed to the sub-loops - not to the sub-loops.  */
+      pOrTab->a[0].fg.fromExists = 0;
     }else{
       pOrTab = pWInfo->pTabList;
     }
@@ -164309,7 +166343,7 @@ SQLITE_PRIVATE Bitmask SQLITE_APICALL sqlite3WhereCodeOneLoopStart(
     assert( pLevel->op==OP_Return );
     pLevel->p2 = sqlite3VdbeCurrentAddr(v);
 
-    if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); }
+    if( pWInfo->pTabList!=pOrTab ){ sqlite3DbFreeNN(db, pOrTab); }
     if( !untestedTerms ) disableTerm(pLevel, pTerm);
   }else
 #endif /* SQLITE_OMIT_OR_OPTIMIZATION */
@@ -164466,6 +166500,7 @@ SQLITE_PRIVATE Bitmask SQLITE_APICALL sqlite3WhereCodeOneLoopStart(
                     WO_EQ|WO_IN|WO_IS, 0);
     if( pAlt==0 ) continue;
     if( pAlt->wtFlags & (TERM_CODED) ) continue;
+    if( ExprHasProperty(pAlt->pExpr, EP_Collate) ) continue;
     if( (pAlt->eOperator & WO_IN)
      && ExprUseXSelect(pAlt->pExpr)
      && (pAlt->pExpr->x.pSelect->pEList->nExpr>1)
@@ -164648,6 +166683,15 @@ SQLITE_PRIVATE SQLITE_NOINLINE void SQLITE_APICALL sqlite3WhereRightJoinLoop(
                                  sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
     }
   }
+  if( pLevel->iIdxCur ){
+    /* pSubWhere may contain expressions that read from an index on the
+    ** table on the RHS of the right join. All such expressions first test
+    ** if the index is pointing at a NULL row, and if so, read from the
+    ** table cursor instead. So ensure that the index cursor really is
+    ** pointing at a NULL row here, so that no values are read from it during
+    ** the scan of the RHS of the RIGHT join below.  */
+    sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
+  }
   pFrom = &uSrc.sSrc;
   pFrom->nSrc = 1;
   pFrom->nAlloc = 1;
@@ -164988,13 +167032,14 @@ static int SQLITE_APICALL isLikeOrGlob(
         ){
           int isNum;
           double rDummy;
-          isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
+          assert( zNew[iTo]==0 );
+          isNum = sqlite3AtoF(zNew, &rDummy);
           if( isNum<=0 ){
             if( iTo==1 && zNew[0]=='-' ){
               isNum = +1;
             }else{
               zNew[iTo-1]++;
-              isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
+              isNum = sqlite3AtoF(zNew, &rDummy);
               zNew[iTo-1]--;
             }
           }
@@ -165037,6 +167082,34 @@ static int SQLITE_APICALL isLikeOrGlob(
 }
 #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
 
+/*
+** If pExpr is one of "like", "glob", "match", or "regexp", then
+** return the corresponding SQLITE_INDEX_CONSTRAINT_xxxx value.
+** If not, return 0.
+**
+** pExpr is guaranteed to be a TK_FUNCTION.
+*/
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3ExprIsLikeOperator(const Expr *pExpr){
+  static const struct {
+    const char *zOp;
+    unsigned char eOp;
+  } aOp[] = {
+    { "match",  SQLITE_INDEX_CONSTRAINT_MATCH },
+    { "glob",   SQLITE_INDEX_CONSTRAINT_GLOB },
+    { "like",   SQLITE_INDEX_CONSTRAINT_LIKE },
+    { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
+  };
+  int i;
+  assert( pExpr->op==TK_FUNCTION );
+  assert( !ExprHasProperty(pExpr, EP_IntValue) );
+  for(i=0; i<ArraySize(aOp); i++){
+    if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
+      return aOp[i].eOp;
+    }
+  }
+  return 0;
+}
+
 
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 /*
@@ -165073,15 +167146,6 @@ static int SQLITE_APICALL isAuxiliaryVtabOperator(
   Expr **ppRight                  /* Expression to left of MATCH/op2 */
 ){
   if( pExpr->op==TK_FUNCTION ){
-    static const struct Op2 {
-      const char *zOp;
-      unsigned char eOp2;
-    } aOp[] = {
-      { "match",  SQLITE_INDEX_CONSTRAINT_MATCH },
-      { "glob",   SQLITE_INDEX_CONSTRAINT_GLOB },
-      { "like",   SQLITE_INDEX_CONSTRAINT_LIKE },
-      { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
-    };
     ExprList *pList;
     Expr *pCol;                     /* Column reference */
     int i;
@@ -165101,16 +167165,11 @@ static int SQLITE_APICALL isAuxiliaryVtabOperator(
     */
     pCol = pList->a[1].pExpr;
     assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) );
-    if( ExprIsVtab(pCol) ){
-      for(i=0; i<ArraySize(aOp); i++){
-        assert( !ExprHasProperty(pExpr, EP_IntValue) );
-        if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
-          *peOp2 = aOp[i].eOp2;
-          *ppRight = pList->a[0].pExpr;
-          *ppLeft = pCol;
-          return 1;
-        }
-      }
+    if( ExprIsVtab(pCol) && (i = sqlite3ExprIsLikeOperator(pExpr))!=0 ){
+      *peOp2 = i;
+      *ppRight = pList->a[0].pExpr;
+      *ppLeft = pCol;
+      return 1;
     }
 
     /* We can also match against the first column of overloaded
@@ -165244,16 +167303,22 @@ static void SQLITE_APICALL whereCombineDisjuncts(
   Expr *pNew;            /* New virtual expression */
   int op;                /* Operator for the combined expression */
   int idxNew;            /* Index in pWC of the next virtual term */
+  Expr *pA, *pB;         /* Expressions associated with pOne and pTwo */
 
   if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return;
   if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
   if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
   if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp
    && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return;
-  assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 );
-  assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 );
-  if( sqlite3ExprCompare(0,pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return;
-  if( sqlite3ExprCompare(0,pOne->pExpr->pRight, pTwo->pExpr->pRight,-1) )return;
+  pA = pOne->pExpr;
+  pB = pTwo->pExpr;
+  assert( pA->pLeft!=0 && pA->pRight!=0 );
+  assert( pB->pLeft!=0 && pB->pRight!=0 );
+  if( sqlite3ExprCompare(0,pA->pLeft, pB->pLeft, -1) )  return;
+  if( sqlite3ExprCompare(0,pA->pRight, pB->pRight,-1) ) return;
+  if( ExprHasProperty(pA,EP_Commuted)!=ExprHasProperty(pB,EP_Commuted) ){
+    return;
+  }
   /* If we reach this point, it means the two subterms can be combined */
   if( (eOp & (eOp-1))!=0 ){
     if( eOp & (WO_LT|WO_LE) ){
@@ -165264,7 +167329,7 @@ static void SQLITE_APICALL whereCombineDisjuncts(
     }
   }
   db = pWC->pWInfo->pParse->db;
-  pNew = sqlite3ExprDup(db, pOne->pExpr, 0);
+  pNew = sqlite3ExprDup(db, pA, 0);
   if( pNew==0 ) return;
   for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); }
   pNew->op = op;
@@ -165628,8 +167693,8 @@ static void SQLITE_APICALL exprAnalyzeOrTerm(
 **   3.  Not originating in the ON clause of an OUTER JOIN
 **   4.  The operator is not IS or else the query does not contain RIGHT JOIN
 **   5.  The affinities of A and B must be compatible
-**   6a. Both operands use the same collating sequence OR
-**   6b. The overall collating sequence is BINARY
+**   6.  Both operands use the same collating sequence, and they must not
+**       use explicit COLLATE clauses.
 ** If this routine returns TRUE, that means that the RHS can be substituted
 ** for the LHS anyplace else in the WHERE clause where the LHS column occurs.
 ** This is an optimization.  No harm comes from returning 0.  But if 1 is
@@ -165637,10 +167702,9 @@ static void SQLITE_APICALL exprAnalyzeOrTerm(
 */
 static int SQLITE_APICALL termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){
   char aff1, aff2;
-  CollSeq *pColl;
   if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;  /* (1) */
   if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;                 /* (2) */
-  if( ExprHasProperty(pExpr, EP_OuterON) ) return 0;                   /* (3) */
+  if( ExprHasProperty(pExpr, EP_OuterON|EP_Collate) ) return 0;        /* (3) */
   assert( pSrc!=0 );
   if( pExpr->op==TK_IS
    && pSrc->nSrc>=2
@@ -165655,10 +167719,7 @@ static int SQLITE_APICALL termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList
   ){
     return 0;                                                          /* (5) */
   }
-  pColl = sqlite3ExprCompareCollSeq(pParse, pExpr);
-  if( !sqlite3IsBinary(pColl)
-   && !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight)
-  ){
+  if( !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight) ){
     return 0;                                                          /* (6) */
   }
   return 1;
@@ -165990,7 +168051,7 @@ static void SQLITE_APICALL exprAnalyze(
   /* Analyze a term that is composed of two or more subterms connected by
   ** an OR operator.
   */
-  else if( pExpr->op==TK_OR ){
+  else if( pExpr->op==TK_OR && !ExprHasProperty(pExpr, EP_Collate) ){
     assert( pWC->op==TK_AND );
     exprAnalyzeOrTerm(pSrc, pWC, idxTerm);
     pTerm = &pWC->a[idxTerm];
@@ -166304,13 +168365,11 @@ static void SQLITE_APICALL whereAddLimitExpr(
   int iVal = 0;
 
   if( sqlite3ExprIsInteger(pExpr, &iVal, pParse) && iVal>=0 ){
-    Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0);
+    Expr *pVal = sqlite3ExprInt32(db, iVal);
     if( pVal==0 ) return;
-    ExprSetProperty(pVal, EP_IntValue);
-    pVal->u.iValue = iVal;
     pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal);
   }else{
-    Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0);
+    Expr *pVal = sqlite3ExprAlloc(db, TK_REGISTER, 0, 0);
     if( pVal==0 ) return;
     pVal->iTable = iReg;
     pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal);
@@ -168137,11 +170196,14 @@ static sqlite3_index_info *SQLITE_APICALL allocateIndexInfo(
       break;
     }
     if( i==n ){
+      int bSortByGroup = (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0;
       nOrderBy = n;
       if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){
-        eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0);
+        eDistinct = 2 + bSortByGroup;
       }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){
-        eDistinct = 1;
+        eDistinct = 1 - bSortByGroup;
+      }else if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
+        eDistinct = 3;
       }
     }
   }
@@ -168992,7 +171054,7 @@ static int whereInScanEst(
 /*
 ** Print the content of a WhereTerm object
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
+SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
   if( pTerm==0 ){
     sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm);
   }else{
@@ -169033,7 +171095,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereTermPrint(WhereTerm *pTerm, int i
     sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
   }
 }
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ShowWhereTerm(WhereTerm *pTerm){
+SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){
   sqlite3WhereTermPrint(pTerm, 0);
 }
 #endif
@@ -169069,11 +171131,16 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
 SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
   WhereInfo *pWInfo;
   if( pWC ){
+    int nb;
+    SrcItem *pItem;
+    Table *pTab;
+    Bitmask mAll;
+
     pWInfo = pWC->pWInfo;
-    int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
-    SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
-    Table *pTab = pItem->pSTab;
-    Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
+    nb = 1+(pWInfo->pTabList->nSrc+3)/4;
+    pItem = pWInfo->pTabList->a + p->iTab;
+    pTab = pItem->pSTab;
+    mAll = (((Bitmask)1)<<(nb*4)) - 1;
     sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
                        p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
     sqlite3DebugPrintf(" %12s",
@@ -169552,6 +171619,67 @@ static int SQLITE_APICALL whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop
   return rc;
 }
 
+/*
+** Callback for estLikePatternLength().
+**
+** If this node is a string literal that is longer pWalker->sz, then set
+** pWalker->sz to the byte length of that string literal.
+**
+** pWalker->eCode indicates how to count characters:
+**
+**    eCode==0     Count as a GLOB pattern
+**    eCode==1     Count as a LIKE pattern
+*/
+static int SQLITE_APICALL exprNodePatternLengthEst(Walker *pWalker, Expr *pExpr){
+  if( pExpr->op==TK_STRING ){
+    int sz = 0;                    /* Pattern size in bytes */
+    u8 *z = (u8*)pExpr->u.zToken;  /* The pattern */
+    u8 c;                          /* Next character of the pattern */
+    u8 c1, c2, c3;                 /* Wildcards */
+    if( pWalker->eCode ){
+      c1 = '%';
+      c2 = '_';
+      c3 = 0;
+    }else{
+      c1 = '*';
+      c2 = '?';
+      c3 = '[';
+    }
+    while( (c = *(z++))!=0 ){
+      if( c==c3 ){
+        if( *z ) z++;
+        while( *z && *z!=']' ) z++;
+      }else if( c!=c1 && c!=c2 ){
+        sz++;
+      }
+    }
+    if( sz>pWalker->u.sz ) pWalker->u.sz = sz;
+  }
+  return WRC_Continue;
+}
+
+/*
+** Return the length of the longest string literal in the given
+** expression.
+**
+** eCode indicates how to count characters:
+**
+**    eCode==0     Count as a GLOB pattern
+**    eCode==1     Count as a LIKE pattern
+*/
+static int SQLITE_APICALL estLikePatternLength(Expr *p, u16 eCode){
+  Walker w;
+  w.u.sz = 0;
+  w.eCode = eCode;
+  w.xExprCallback = exprNodePatternLengthEst;
+  w.xSelectCallback = sqlite3SelectWalkFail;
+#ifdef SQLITE_DEBUG
+  w.xSelectCallback2 = sqlite3SelectWalkAssert2;
+#endif
+  sqlite3WalkExpr(&w, p);
+  return w.u.sz;
+}
+
 /*
 ** Adjust the WhereLoop.nOut value downward to account for terms of the
 ** WHERE clause that reference the loop but which are not used by an
@@ -169580,6 +171708,13 @@ static int SQLITE_APICALL whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop
 ** "x" column is boolean or else -1 or 0 or 1 is a common default value
 ** on the "x" column and so in that case only cap the output row estimate
 ** at 1/2 instead of 1/4.
+**
+** Heuristic 3:  If there is a LIKE or GLOB (or REGEXP or MATCH) operator
+** with a large constant pattern, then reduce the size of the search
+** space according to the length of the pattern, under the theory that
+** longer patterns are less likely to match.  This heuristic was added
+** to give better output-row count estimates when preparing queries for
+** the Join-Order Benchmarks.  See forum thread 2026-01-30T09:57:54z
 */
 static void SQLITE_APICALL whereLoopOutputAdjust(
   WhereClause *pWC,      /* The WHERE clause */
@@ -169629,13 +171764,14 @@ static void SQLITE_APICALL whereLoopOutputAdjust(
       }else{
         /* In the absence of explicit truth probabilities, use heuristics to
         ** guess a reasonable truth probability. */
+        Expr *pOpExpr = pTerm->pExpr;
         pLoop->nOut--;
         if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0
          && (pTerm->wtFlags & TERM_HIGHTRUTH)==0  /* tag-20200224-1 */
         ){
-          Expr *pRight = pTerm->pExpr->pRight;
+          Expr *pRight = pOpExpr->pRight;
           int k = 0;
-          testcase( pTerm->pExpr->op==TK_IS );
+          testcase( pOpExpr->op==TK_IS );
           if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){
             k = 10;
           }else{
@@ -169645,6 +171781,23 @@ static void SQLITE_APICALL whereLoopOutputAdjust(
             pTerm->wtFlags |= TERM_HEURTRUTH;
             iReduce = k;
           }
+        }else
+        if( ExprHasProperty(pOpExpr, EP_InfixFunc)
+         && pOpExpr->op==TK_FUNCTION
+        ){
+          int eOp;
+          assert( ExprUseXList(pOpExpr) );
+          assert( pOpExpr->x.pList->nExpr>=2 );
+          eOp = sqlite3ExprIsLikeOperator(pOpExpr);
+          if( ALWAYS(eOp>0) ){
+            int szPattern;
+            Expr *pRHS = pOpExpr->x.pList->a[0].pExpr;
+            eOp = eOp==SQLITE_INDEX_CONSTRAINT_LIKE;
+            szPattern = estLikePatternLength(pRHS, eOp);
+            if( szPattern>0 ){
+              pLoop->nOut -= szPattern*2;
+            }
+          }
         }
       }
     }
@@ -169716,6 +171869,7 @@ static int SQLITE_APICALL whereRangeVectorLen(
     idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn);
     if( aff!=idxaff ) break;
 
+    if( ExprHasProperty(pTerm->pExpr, EP_Commuted) ) SWAP(Expr*, pRhs, pLhs);
     pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
     if( pColl==0 ) break;
     if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break;
@@ -170105,6 +172259,7 @@ static int SQLITE_APICALL whereLoopAddBtreeIndex(
     pNew->rRun += nInMul + nIn;
     pNew->nOut += nInMul + nIn;
     whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize);
+    if( pSrc->fg.fromExists ) pNew->nOut = 0;
     rc = whereLoopInsert(pBuilder, pNew);
 
     if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
@@ -170700,7 +172855,14 @@ static int SQLITE_APICALL whereLoopAddBtree(
       whereLoopOutputAdjust(pWC, pNew, rSize);
       if( pSrc->fg.isSubquery ){
         if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE;
-        pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy;
+        /* Do not set btree.pOrderBy for a recursive CTE. In this case
+        ** the ORDER BY clause does not determine the overall order that
+        ** rows are emitted from the CTE in.  */
+        if( (pSrc->u4.pSubq->pSelect->selFlags & SF_Recursive)==0 ){
+          pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy;
+        }
+      }else if( pSrc->fg.fromExists ){
+        pNew->nOut = 0;
       }
       rc = whereLoopInsert(pBuilder, pNew);
       pNew->nOut = rSize;
@@ -170803,6 +172965,7 @@ static int SQLITE_APICALL whereLoopAddBtree(
           ** positioned to the correct row during the right-join no-match
           ** loop. */
         }else{
+          if( pSrc->fg.fromExists ) pNew->nOut = 0;
           rc = whereLoopInsert(pBuilder, pNew);
         }
         pNew->nOut = rSize;
@@ -171465,7 +173628,7 @@ static int SQLITE_APICALL whereLoopAddAll(WhereLoopBuilder *pBuilder){
   sqlite3 *db = pWInfo->pParse->db;
   int rc = SQLITE_OK;
   int bFirstPastRJ = 0;
-  int hasRightJoin = 0;
+  int hasRightCrossJoin = 0;
   WhereLoop *pNew;
 
 
@@ -171492,15 +173655,34 @@ static int SQLITE_APICALL whereLoopAddAll(WhereLoopBuilder *pBuilder){
       ** prevents the right operand of a RIGHT JOIN from being swapped with
       ** other elements even further to the right.
       **
-      ** The JT_LTORJ case and the hasRightJoin flag work together to
-      ** prevent FROM-clause terms from moving from the right side of
-      ** a LEFT JOIN over to the left side of that join if the LEFT JOIN
-      ** is itself on the left side of a RIGHT JOIN.
+      ** The hasRightCrossJoin flag prevent FROM-clause terms from moving
+      ** from the right side of a LEFT JOIN or CROSS JOIN over to the
+      ** left side of that same join.  This is a required restriction in
+      ** the case of LEFT JOIN - an incorrect answer may results if it is
+      ** not enforced.  This restriction is not required for CROSS JOIN.
+      ** It is provided merely as a means of controlling join order, under
+      ** the theory that no real-world queries that care about performance
+      ** actually use the CROSS JOIN syntax.
       */
-      if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1;
+      if( pItem->fg.jointype & (JT_LTORJ|JT_CROSS) ){
+        testcase( pItem->fg.jointype & JT_LTORJ );
+        testcase( pItem->fg.jointype & JT_CROSS );
+        hasRightCrossJoin = 1;
+      }
       mPrereq |= mPrior;
       bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0;
-    }else if( !hasRightJoin ){
+    }else if( pItem->fg.fromExists ){
+      /* joins that result from the EXISTS-to-JOIN optimization should not
+      ** be moved to the left of any of their dependencies */
+      WhereClause *pWC = &pWInfo->sWC;
+      WhereTerm *pTerm;
+      int i;
+      for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){
+        if( (pNew->maskSelf & pTerm->prereqAll)!=0 ){
+          mPrereq |= (pTerm->prereqAll & (pNew->maskSelf-1));
+        }
+      }
+    }else if( !hasRightCrossJoin ){
       mPrereq = 0;
     }
 #ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -171723,9 +173905,7 @@ static i8 SQLITE_APICALL wherePathSatisfiesOrderBy(
       pLoop = pLast;
     }
     if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
-      if( pLoop->u.vtab.isOrdered
-       && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
-      ){
+      if( pLoop->u.vtab.isOrdered && pWInfo->pOrderBy==pOrderBy ){
         obSat = obDone;
       }else{
         /* No further ORDER BY terms may be matched. So this call should
@@ -172101,12 +174281,21 @@ static LogEst SQLITE_APICALL whereSortingCost(
 **     12    otherwise
 **
 ** For the purposes of this heuristic, a star-query is defined as a query
-** with a large central table that is joined using an INNER JOIN,
-** not CROSS or OUTER JOINs, against four or more smaller tables.
-** The central table is called the "fact" table.  The smaller tables
-** that get joined are "dimension tables".  Also, any table that is
-** self-joined cannot be a dimension table; we assume that dimension
-** tables may only be joined against fact tables.
+** with a central "fact" table that is joined against multiple
+** "dimension" tables, subject to the following constraints:
+**
+**   (aa)  Only a five-way or larger join is considered for this
+**         optimization.  If there are fewer than four terms in the FROM
+**         clause, this heuristic does not apply.
+**
+**   (bb)  The join between the fact table and the dimension tables must
+**         be an INNER join.  CROSS and OUTER JOINs do not qualify.
+**
+**   (cc)  A table must have 3 or more dimension tables in order to be
+**         considered a fact table. (Was 4 prior to 2026-02-10.)
+**
+**   (dd)  A table that is a self-join cannot be a dimension table.
+**         Dimension tables are joined against fact tables.
 **
 ** SIDE EFFECT:  (and really the whole point of this subroutine)
 **
@@ -172159,7 +174348,7 @@ static int SQLITE_APICALL computeMxChoice(WhereInfo *pWInfo){
   }
 #endif /* SQLITE_DEBUG */
 
-  if( nLoop>=5
+  if( nLoop>=4  /* Constraint (aa) */
    && !pWInfo->bStarDone
    && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery)
   ){
@@ -172171,7 +174360,7 @@ static int SQLITE_APICALL computeMxChoice(WhereInfo *pWInfo){
 
     pWInfo->bStarDone = 1; /* Only do this computation once */
 
-    /* Look for fact tables with four or more dimensions where the
+    /* Look for fact tables with three or more dimensions where the
     ** dimension tables are not separately from the fact tables by an outer
     ** or cross join.  Adjust cost weights if found.
     */
@@ -172188,18 +174377,17 @@ static int SQLITE_APICALL computeMxChoice(WhereInfo *pWInfo){
       if( (pFactTab->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
         /* If the candidate fact-table is the right table of an outer join
         ** restrict the search for dimension-tables to be tables to the right
-        ** of the fact-table. */
-        if( iFromIdx+4 > nLoop ) break;  /* Impossible to reach nDep>=4 */
+        ** of the fact-table.  Constraint (bb) */
+        if( iFromIdx+3 > nLoop ){
+          break;  /* ^-- Impossible to reach nDep>=2 - Constraint (cc) */
+        }
         while( pStart && pStart->iTab<=iFromIdx ){
           pStart = pStart->pNextLoop;
         }
       }
       for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){
         if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
-          /* Fact-tables and dimension-tables cannot be separated by an
-          ** outer join (at least for the definition of fact- and dimension-
-          ** used by this heuristic). */
-          break;
+          break; /* Constraint (bb) */
         }
         if( (pWLoop->prereq & m)!=0        /* pWInfo depends on iFromIdx */
          && (pWLoop->maskSelf & mSeen)==0  /* pWInfo not already a dependency */
@@ -172213,7 +174401,9 @@ static int SQLITE_APICALL computeMxChoice(WhereInfo *pWInfo){
           }
         }
       }
-      if( nDep<=3 ) continue;
+      if( nDep<=2 ){
+        continue; /* Constraint (cc) */
+      }
 
       /* If we reach this point, it means that pFactTab is a fact table
       ** with four or more dimensions connected by inner joins.  Proceed
@@ -172226,6 +174416,23 @@ static int SQLITE_APICALL computeMxChoice(WhereInfo *pWInfo){
           pWLoop->rStarDelta = 0;
         }
       }
+#endif
+#ifdef WHERETRACE_ENABLED /* 0x80000 */
+      if( sqlite3WhereTrace & 0x80000 ){
+        Bitmask mShow = mSeen;
+        sqlite3DebugPrintf("Fact table %s(%d), dimensions:",
+            pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName,
+            iFromIdx);
+        for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){
+          if( mShow & pWLoop->maskSelf ){
+            SrcItem *pDim = aFromTabs + pWLoop->iTab;
+            mShow &= ~pWLoop->maskSelf;
+            sqlite3DebugPrintf(" %s(%d)",
+              pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab);
+          }
+        }
+        sqlite3DebugPrintf("\n");
+      }
 #endif
       pWInfo->bStarUsed = 1;
 
@@ -172249,10 +174456,8 @@ static int SQLITE_APICALL computeMxChoice(WhereInfo *pWInfo){
           if( sqlite3WhereTrace & 0x80000 ){
             SrcItem *pDim = aFromTabs + pWLoop->iTab;
             sqlite3DebugPrintf(
-              "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n",
-              pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab,
-              pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName,
-              iFromIdx, mxRun
+              "Increase SCAN cost of %s to %d\n",
+              pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, mxRun
             );
           }
           pWLoop->rStarDelta = mxRun - pWLoop->rRun;
@@ -173066,6 +175271,7 @@ static SQLITE_NOINLINE Bitmask SQLITE_APICALL whereOmitNoopJoin(
     for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
       if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
         pTerm->wtFlags |= TERM_CODED;
+        pTerm->prereqAll = 0;
       }
     }
     if( i!=pWInfo->nLevel-1 ){
@@ -173978,7 +176184,7 @@ whereBeginError:
 # define OpcodeRewriteTrace(D,K,P) /* no-op */
 #else
 # define OpcodeRewriteTrace(D,K,P) sqlite3WhereOpcodeRewriteTrace(D,K,P)
-  static void SQLITE_APICALL sqlite3WhereOpcodeRewriteTrace(
+  static void sqlite3WhereOpcodeRewriteTrace(
     sqlite3 *db,
     int pc,
     VdbeOp *pOp
@@ -174003,6 +176209,9 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereEnd(WhereInfo *pWInfo){
   sqlite3 *db = pParse->db;
   int iEnd = sqlite3VdbeCurrentAddr(v);
   int nRJ = 0;
+#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
+  int addrSeek = 0;
+#endif
 
   /* Generate loop termination code.
   */
@@ -174015,7 +176224,10 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereEnd(WhereInfo *pWInfo){
       ** the RIGHT JOIN table */
       WhereRightJoin *pRJ = pLevel->pRJ;
       sqlite3VdbeResolveLabel(v, pLevel->addrCont);
-      pLevel->addrCont = 0;
+      /* Replace addrCont with a new label that will never be used, just so
+      ** the subsequent call to resolve pLevel->addrCont will have something
+      ** to resolve. */
+      pLevel->addrCont = sqlite3VdbeMakeLabel(pParse);
       pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v);
       sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1);
       VdbeCoverage(v);
@@ -174024,7 +176236,6 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereEnd(WhereInfo *pWInfo){
     pLoop = pLevel->pWLoop;
     if( pLevel->op!=OP_Noop ){
 #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
-      int addrSeek = 0;
       Index *pIdx;
       int n;
       if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED
@@ -174036,6 +176247,10 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereEnd(WhereInfo *pWInfo){
       ){
         int r1 = pParse->nMem+1;
         int j, op;
+        int addrIfNull = 0; /* Init to avoid false-positive compiler warning */
+        if( pLevel->iLeftJoin ){
+          addrIfNull = sqlite3VdbeAddOp2(v, OP_IfNullRow, pLevel->iIdxCur, r1);
+        }
         for(j=0; j<n; j++){
           sqlite3VdbeAddOp3(v, OP_Column, pLevel->iIdxCur, j, r1+j);
         }
@@ -174045,27 +176260,20 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereEnd(WhereInfo *pWInfo){
         VdbeCoverageIf(v, op==OP_SeekLT);
         VdbeCoverageIf(v, op==OP_SeekGT);
         sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2);
+        if( pLevel->iLeftJoin ){
+          sqlite3VdbeJumpHere(v, addrIfNull);
+        }
       }
 #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
-      if( pTabList->a[pLevel->iFrom].fg.fromExists && i==pWInfo->nLevel-1 ){
-        /* If the EXISTS-to-JOIN optimization was applied, then the EXISTS
-        ** loop(s) will be the inner-most loops of the join. There might be
-        ** multiple EXISTS loops, but they will all be nested, and the join
-        ** order will not have been changed by the query planner.  If the
-        ** inner-most EXISTS loop sees a single successful row, it should
-        ** break out of *all* EXISTS loops.  But only the inner-most of the
-        ** nested EXISTS loops should do this breakout. */
-        int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */
-        while( nOuter<i ){
-          if( !pTabList->a[pLevel[-nOuter-1].iFrom].fg.fromExists ) break;
-          nOuter++;
-        }
-        testcase( nOuter>0 );
-        sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk);
-        VdbeComment((v, "EXISTS break"));
-      }
-      /* The common case: Advance to the next row */
-      if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
+    }
+    if( pTabList->a[pLevel->iFrom].fg.fromExists ){
+      /* This is an EXISTS-to-JOIN optimization loop. If this loop sees a
+      ** successful row, it should break out of itself. */
+      sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
+      VdbeComment((v, "EXISTS break %d", i));
+    }
+    sqlite3VdbeResolveLabel(v, pLevel->addrCont);
+    if( pLevel->op!=OP_Noop ){
       sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
       sqlite3VdbeChangeP5(v, pLevel->p5);
       VdbeCoverage(v);
@@ -174078,10 +176286,11 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WhereEnd(WhereInfo *pWInfo){
         VdbeCoverage(v);
       }
 #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
-      if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
+      if( addrSeek ){
+        sqlite3VdbeJumpHere(v, addrSeek);
+        addrSeek = 0;
+      }
 #endif
-    }else if( pLevel->addrCont ){
-      sqlite3VdbeResolveLabel(v, pLevel->addrCont);
     }
     if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){
       struct InLoop *pIn;
@@ -175078,7 +177287,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3WindowUpdate(
           pWin->eEnd = aUp[i].eEnd;
           pWin->eExclude = 0;
           if( pWin->eStart==TK_FOLLOWING ){
-            pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1");
+            pWin->pStart = sqlite3ExprInt32(db, 1);
           }
           break;
         }
@@ -175423,9 +177632,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3WindowRewrite(Parse *pParse, Select *p)
     ** keep everything legal in this case.
     */
     if( pSublist==0 ){
-      pSublist = sqlite3ExprListAppend(pParse, 0,
-        sqlite3Expr(db, TK_INTEGER, "0")
-      );
+      pSublist = sqlite3ExprListAppend(pParse, 0, sqlite3ExprInt32(db, 0));
     }
 
     pSub = sqlite3SelectNew(
@@ -177649,8 +179856,23 @@ static void updateDeleteLimitError(
   ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate
   ** testing.
   */
-  static void *SQLITE_APICALL parserStackRealloc(void *pOld, sqlite3_uint64 newSize){
-    return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
+  static void *SQLITE_APICALL parserStackRealloc(
+    void *pOld,               /* Prior allocation */
+    sqlite3_uint64 newSize,   /* Requested new alloation size */
+    Parse *pParse             /* Parsing context */
+  ){
+    void *p = sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
+    if( p==0 ) sqlite3OomFault(pParse->db);
+    return p;
+  }
+  static void SQLITE_APICALL parserStackFree(void *pOld, Parse *pParse){
+    (void)pParse;
+    sqlite3_free(pOld);
+  }
+
+  /* Return an integer that is the maximum allowed stack size */
+  static int SQLITE_APICALL parserStackSizeLimit(Parse *pParse){
+    return pParse->db->aLimit[SQLITE_LIMIT_PARSER_DEPTH];
   }
 
 
@@ -177689,15 +179911,46 @@ static void updateDeleteLimitError(
   }
 
 
-  /* A routine to convert a binary TK_IS or TK_ISNOT expression into a
-  ** unary TK_ISNULL or TK_NOTNULL expression. */
-  static void SQLITE_APICALL binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){
-    sqlite3 *db = pParse->db;
-    if( pA && pY && pY->op==TK_NULL && !IN_RENAME_OBJECT ){
-      pA->op = (u8)op;
-      sqlite3ExprDelete(db, pA->pRight);
-      pA->pRight = 0;
+  /* Create a TK_ISNULL or TK_NOTNULL expression, perhaps optimized to
+  ** to TK_TRUEFALSE, if possible */
+  static Expr *SQLITE_APICALL sqlite3PExprIsNull(
+    Parse *pParse,  /* Parsing context */
+    int op,         /* TK_ISNULL or TK_NOTNULL */
+    Expr *pLeft     /* Operand */
+  ){
+    Expr *p = pLeft;
+    assert( op==TK_ISNULL || op==TK_NOTNULL );
+    assert( pLeft!=0 );
+    while( p->op==TK_UPLUS || p->op==TK_UMINUS ){
+      p = p->pLeft;
+      assert( p!=0 );
+    }
+    switch( p->op ){
+      case TK_INTEGER:
+      case TK_STRING:
+      case TK_FLOAT:
+      case TK_BLOB:
+        sqlite3ExprDeferredDelete(pParse, pLeft);
+        return sqlite3ExprInt32(pParse->db, op==TK_NOTNULL);
+      default:
+        break;
     }
+    return sqlite3PExpr(pParse, op, pLeft, 0);
+  }
+
+  /* Create a TK_IS or TK_ISNOT operator, perhaps optimized to
+  ** TK_ISNULL or TK_NOTNULL or TK_TRUEFALSE. */
+  static Expr *SQLITE_APICALL sqlite3PExprIs(
+    Parse *pParse,  /* Parsing context */
+    int op,         /* TK_IS or TK_ISNOT */
+    Expr *pLeft,    /* Left operand */
+    Expr *pRight    /* Right operand */
+  ){
+    if( pRight && pRight->op==TK_NULL ){
+      sqlite3ExprDeferredDelete(pParse, pRight);
+      return sqlite3PExprIsNull(pParse, op==TK_IS ? TK_ISNULL : TK_NOTNULL, pLeft);
+    }
+    return sqlite3PExpr(pParse, op, pLeft, pRight);
   }
 
   /* Add a single new term to an ExprList that is used to store a
@@ -177980,63 +180233,72 @@ static void updateDeleteLimitError(
 #endif
 /************* Begin control #defines *****************************************/
 #define YYCODETYPE unsigned short int
-#define YYNOCODE 323
+#define YYNOCODE 322
 #define YYACTIONTYPE unsigned short int
 #define YYWILDCARD 102
 #define sqlite3ParserTOKENTYPE Token
 typedef union {
   int yyinit;
   sqlite3ParserTOKENTYPE yy0;
-  u32 yy9;
-  struct TrigEvent yy28;
-  With* yy125;
-  IdList* yy204;
-  struct FrameBound yy205;
-  TriggerStep* yy319;
-  const char* yy342;
-  Cte* yy361;
-  ExprList* yy402;
-  Upsert* yy403;
-  OnOrUsing yy421;
-  u8 yy444;
-  struct {int value; int mask;} yy481;
-  Window* yy483;
-  int yy502;
-  SrcList* yy563;
-  Expr* yy590;
-  Select* yy637;
+  ExprList* yy14;
+  With* yy59;
+  Cte* yy67;
+  Upsert* yy122;
+  IdList* yy132;
+  int yy144;
+  const char* yy168;
+  SrcList* yy203;
+  Window* yy211;
+  OnOrUsing yy269;
+  struct TrigEvent yy286;
+  struct {int value; int mask;} yy383;
+  u32 yy391;
+  TriggerStep* yy427;
+  Expr* yy454;
+  u8 yy462;
+  struct FrameBound yy509;
+  Select* yy555;
 } YYMINORTYPE;
 #ifndef YYSTACKDEPTH
-#define YYSTACKDEPTH 100
+#define YYSTACKDEPTH 50
 #endif
 #define sqlite3ParserARG_SDECL
 #define sqlite3ParserARG_PDECL
 #define sqlite3ParserARG_PARAM
 #define sqlite3ParserARG_FETCH
 #define sqlite3ParserARG_STORE
+#undef YYREALLOC
 #define YYREALLOC parserStackRealloc
-#define YYFREE sqlite3_free
+#undef YYFREE
+#define YYFREE parserStackFree
+#undef YYDYNSTACK
 #define YYDYNSTACK 1
+#undef YYSIZELIMIT
+#define YYSIZELIMIT parserStackSizeLimit
+#define sqlite3ParserCTX(P) ((P)->pParse)
 #define sqlite3ParserCTX_SDECL Parse *pParse;
 #define sqlite3ParserCTX_PDECL ,Parse *pParse
 #define sqlite3ParserCTX_PARAM ,pParse
 #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
 #define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
+#undef YYERRORSYMBOL
+#undef YYERRSYMDT
+#undef YYFALLBACK
 #define YYFALLBACK 1
-#define YYNSTATE             583
-#define YYNRULE              409
-#define YYNRULE_WITH_ACTION  344
+#define YYNSTATE             600
+#define YYNRULE              412
+#define YYNRULE_WITH_ACTION  348
 #define YYNTOKEN             187
-#define YY_MAX_SHIFT         582
-#define YY_MIN_SHIFTREDUCE   845
-#define YY_MAX_SHIFTREDUCE   1253
-#define YY_ERROR_ACTION      1254
-#define YY_ACCEPT_ACTION     1255
-#define YY_NO_ACTION         1256
-#define YY_MIN_REDUCE        1257
-#define YY_MAX_REDUCE        1665
+#define YY_MAX_SHIFT         599
+#define YY_MIN_SHIFTREDUCE   867
+#define YY_MAX_SHIFTREDUCE   1278
+#define YY_ERROR_ACTION      1279
+#define YY_ACCEPT_ACTION     1280
+#define YY_NO_ACTION         1281
+#define YY_MIN_REDUCE        1282
+#define YY_MAX_REDUCE        1693
 #define YY_MIN_DSTRCTR       206
-#define YY_MAX_DSTRCTR       320
+#define YY_MAX_DSTRCTR       319
 /************* End control #defines *******************************************/
 #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
 
@@ -178119,643 +180381,680 @@ typedef union {
 **  yy_default[]       Default action for each state.
 **
 *********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2207)
+#define YY_ACTTAB_COUNT (2379)
 static const YYACTIONTYPE yy_action[] = {
- /*     0 */   130,  127,  234,  282,  282, 1328,  576, 1307,  460,  289,
- /*    10 */   289,  576, 1622,  381,  576, 1328,  573,  576,  562,  413,
- /*    20 */  1300, 1542,  573,  481,  562,  524,  460,  459,  558,   82,
- /*    30 */    82,  983,  294,  375,   51,   51,  498,   61,   61,  984,
- /*    40 */    82,   82, 1577,  137,  138,   91,    7, 1228, 1228, 1063,
- /*    50 */  1066, 1053, 1053,  135,  135,  136,  136,  136,  136,  413,
- /*    60 */   288,  288,  182,  288,  288,  481,  536,  288,  288,  130,
- /*    70 */   127,  234,  432,  573,  525,  562,  573,  557,  562, 1290,
- /*    80 */   573,  421,  562,  137,  138,   91,  559, 1228, 1228, 1063,
- /*    90 */  1066, 1053, 1053,  135,  135,  136,  136,  136,  136,  296,
- /*   100 */   460,  398, 1249,  134,  134,  134,  134,  133,  133,  132,
- /*   110 */   132,  132,  131,  128,  451,  451, 1050, 1050, 1064, 1067,
- /*   120 */  1255,    1,    1,  582,    2, 1259,  581, 1174, 1259, 1174,
- /*   130 */   321,  413,  155,  321, 1584,  155,  379,  112,  481, 1341,
- /*   140 */   456,  299, 1341,  134,  134,  134,  134,  133,  133,  132,
- /*   150 */   132,  132,  131,  128,  451,  137,  138,   91,  498, 1228,
- /*   160 */  1228, 1063, 1066, 1053, 1053,  135,  135,  136,  136,  136,
- /*   170 */   136, 1204,  862, 1281,  288,  288,  283,  288,  288,  523,
- /*   180 */   523, 1250,  139,  578,    7,  578, 1345,  573, 1169,  562,
- /*   190 */   573, 1054,  562,  136,  136,  136,  136,  129,  573,  547,
- /*   200 */   562, 1169,  245, 1541, 1169,  245,  133,  133,  132,  132,
- /*   210 */   132,  131,  128,  451,  302,  134,  134,  134,  134,  133,
- /*   220 */   133,  132,  132,  132,  131,  128,  451, 1575, 1204, 1205,
- /*   230 */  1204,    7,  470,  550,  455,  413,  550,  455,  130,  127,
- /*   240 */   234,  134,  134,  134,  134,  133,  133,  132,  132,  132,
- /*   250 */   131,  128,  451,  136,  136,  136,  136,  538,  483,  137,
- /*   260 */   138,   91, 1019, 1228, 1228, 1063, 1066, 1053, 1053,  135,
- /*   270 */   135,  136,  136,  136,  136, 1085,  576, 1204,  132,  132,
- /*   280 */   132,  131,  128,  451,   93,  214,  134,  134,  134,  134,
- /*   290 */   133,  133,  132,  132,  132,  131,  128,  451,  401,   19,
- /*   300 */    19,  134,  134,  134,  134,  133,  133,  132,  132,  132,
- /*   310 */   131,  128,  451, 1498,  426,  267,  344,  467,  332,  134,
- /*   320 */   134,  134,  134,  133,  133,  132,  132,  132,  131,  128,
- /*   330 */   451, 1281,  576,    6, 1204, 1205, 1204,  257,  576,  413,
- /*   340 */   511,  508,  507, 1279,   94, 1019,  464, 1204,  551,  551,
- /*   350 */   506, 1224, 1571,   44,   38,   51,   51,  411,  576,  413,
- /*   360 */    45,   51,   51,  137,  138,   91,  530, 1228, 1228, 1063,
- /*   370 */  1066, 1053, 1053,  135,  135,  136,  136,  136,  136,  398,
- /*   380 */  1148,   82,   82,  137,  138,   91,   39, 1228, 1228, 1063,
- /*   390 */  1066, 1053, 1053,  135,  135,  136,  136,  136,  136,  344,
- /*   400 */    44,  288,  288,  375, 1204, 1205, 1204,  209, 1204, 1224,
- /*   410 */   320,  567,  471,  576,  573,  576,  562,  576,  316,  264,
- /*   420 */   231,   46,  160,  134,  134,  134,  134,  133,  133,  132,
- /*   430 */   132,  132,  131,  128,  451,  303,   82,   82,   82,   82,
- /*   440 */    82,   82,  442,  134,  134,  134,  134,  133,  133,  132,
- /*   450 */   132,  132,  131,  128,  451, 1582,  544,  320,  567, 1250,
- /*   460 */   874, 1582,  380,  382,  413, 1204, 1205, 1204,  360,  182,
- /*   470 */   288,  288, 1576,  557, 1339,  557,    7,  557, 1277,  472,
- /*   480 */   346,  526,  531,  573,  556,  562,  439, 1511,  137,  138,
- /*   490 */    91,  219, 1228, 1228, 1063, 1066, 1053, 1053,  135,  135,
- /*   500 */   136,  136,  136,  136,  465, 1511, 1513,  532,  413,  288,
- /*   510 */   288,  423,  512,  288,  288,  411,  288,  288,  874,  130,
- /*   520 */   127,  234,  573, 1107,  562, 1204,  573, 1107,  562,  573,
- /*   530 */   560,  562,  137,  138,   91, 1293, 1228, 1228, 1063, 1066,
- /*   540 */  1053, 1053,  135,  135,  136,  136,  136,  136,  134,  134,
- /*   550 */   134,  134,  133,  133,  132,  132,  132,  131,  128,  451,
- /*   560 */   493,  503, 1292, 1204,  257,  288,  288,  511,  508,  507,
- /*   570 */  1204, 1628, 1169,  123,  568,  275,    4,  506,  573, 1511,
- /*   580 */   562,  331, 1204, 1205, 1204, 1169,  548,  548, 1169,  261,
- /*   590 */   571,    7,  134,  134,  134,  134,  133,  133,  132,  132,
- /*   600 */   132,  131,  128,  451,  108,  533,  130,  127,  234, 1204,
- /*   610 */   448,  447,  413, 1451,  452,  983,  886,   96, 1598, 1233,
- /*   620 */  1204, 1205, 1204,  984, 1235, 1450,  565, 1204, 1205, 1204,
- /*   630 */   229,  522, 1234,  534, 1333, 1333,  137,  138,   91, 1449,
- /*   640 */  1228, 1228, 1063, 1066, 1053, 1053,  135,  135,  136,  136,
- /*   650 */   136,  136,  373, 1595,  971, 1040,  413, 1236,  418, 1236,
- /*   660 */   879,  121,  121,  948,  373, 1595, 1204, 1205, 1204,  122,
- /*   670 */  1204,  452,  577,  452,  363,  417, 1028,  882,  373, 1595,
- /*   680 */   137,  138,   91,  462, 1228, 1228, 1063, 1066, 1053, 1053,
- /*   690 */   135,  135,  136,  136,  136,  136,  134,  134,  134,  134,
- /*   700 */   133,  133,  132,  132,  132,  131,  128,  451, 1028, 1028,
- /*   710 */  1030, 1031,   35,  570,  570,  570,  197,  423, 1040,  198,
- /*   720 */  1204,  123,  568, 1204,    4,  320,  567, 1204, 1205, 1204,
- /*   730 */    40,  388,  576,  384,  882, 1029,  423, 1188,  571, 1028,
- /*   740 */   134,  134,  134,  134,  133,  133,  132,  132,  132,  131,
- /*   750 */   128,  451,  529, 1568, 1204,   19,   19, 1204,  575,  492,
- /*   760 */   413,  157,  452,  489, 1187, 1331, 1331,    5, 1204,  949,
- /*   770 */   431, 1028, 1028, 1030,  565,   22,   22, 1204, 1205, 1204,
- /*   780 */  1204, 1205, 1204,  477,  137,  138,   91,  212, 1228, 1228,
- /*   790 */  1063, 1066, 1053, 1053,  135,  135,  136,  136,  136,  136,
- /*   800 */  1188,   48,  111, 1040,  413, 1204,  213,  970, 1041,  121,
- /*   810 */   121, 1204, 1205, 1204, 1204, 1205, 1204,  122,  221,  452,
- /*   820 */   577,  452,   44,  487, 1028, 1204, 1205, 1204,  137,  138,
- /*   830 */    91,  378, 1228, 1228, 1063, 1066, 1053, 1053,  135,  135,
- /*   840 */   136,  136,  136,  136,  134,  134,  134,  134,  133,  133,
- /*   850 */   132,  132,  132,  131,  128,  451, 1028, 1028, 1030, 1031,
- /*   860 */    35,  461, 1204, 1205, 1204, 1569, 1040,  377,  214, 1149,
- /*   870 */  1657,  535, 1657,  437,  902,  320,  567, 1568,  364,  320,
- /*   880 */   567,  412,  329, 1029,  519, 1188,    3, 1028,  134,  134,
- /*   890 */   134,  134,  133,  133,  132,  132,  132,  131,  128,  451,
- /*   900 */  1659,  399, 1169,  307,  893,  307,  515,  576,  413,  214,
- /*   910 */   498,  944, 1024,  540,  903, 1169,  943,  392, 1169, 1028,
- /*   920 */  1028, 1030,  406,  298, 1204,   50, 1149, 1658,  413, 1658,
- /*   930 */   145,  145,  137,  138,   91,  293, 1228, 1228, 1063, 1066,
- /*   940 */  1053, 1053,  135,  135,  136,  136,  136,  136, 1188, 1147,
- /*   950 */   514, 1568,  137,  138,   91, 1505, 1228, 1228, 1063, 1066,
- /*   960 */  1053, 1053,  135,  135,  136,  136,  136,  136,  434,  323,
- /*   970 */   435,  539,  111, 1506,  274,  291,  372,  517,  367,  516,
- /*   980 */   262, 1204, 1205, 1204, 1574,  481,  363,  576,    7, 1569,
- /*   990 */  1568,  377,  134,  134,  134,  134,  133,  133,  132,  132,
- /*  1000 */   132,  131,  128,  451, 1568,  576, 1147,  576,  232,  576,
- /*  1010 */    19,   19,  134,  134,  134,  134,  133,  133,  132,  132,
- /*  1020 */   132,  131,  128,  451, 1169,  433,  576, 1207,   19,   19,
- /*  1030 */    19,   19,   19,   19, 1627,  576,  911, 1169,   47,  120,
- /*  1040 */  1169,  117,  413,  306,  498,  438, 1125,  206,  336,   19,
- /*  1050 */    19, 1435,   49,  449,  449,  449, 1368,  315,   81,   81,
- /*  1060 */   576,  304,  413, 1570,  207,  377,  137,  138,   91,  115,
- /*  1070 */  1228, 1228, 1063, 1066, 1053, 1053,  135,  135,  136,  136,
- /*  1080 */   136,  136,  576,   82,   82, 1207,  137,  138,   91, 1340,
- /*  1090 */  1228, 1228, 1063, 1066, 1053, 1053,  135,  135,  136,  136,
- /*  1100 */   136,  136, 1569,  386,  377,   82,   82,  463, 1126, 1552,
- /*  1110 */   333,  463,  335,  131,  128,  451, 1569,  161,  377,   16,
- /*  1120 */   317,  387,  428, 1127,  448,  447,  134,  134,  134,  134,
- /*  1130 */   133,  133,  132,  132,  132,  131,  128,  451, 1128,  576,
- /*  1140 */  1105,   10,  445,  267,  576, 1554,  134,  134,  134,  134,
- /*  1150 */   133,  133,  132,  132,  132,  131,  128,  451,  532,  576,
- /*  1160 */   922,  576,   19,   19,  576, 1573,  576,  147,  147,    7,
- /*  1170 */   923, 1236,  498, 1236,  576,  487,  413,  552,  285, 1224,
- /*  1180 */   969,  215,   82,   82,   66,   66, 1435,   67,   67,   21,
- /*  1190 */    21, 1110, 1110,  495,  334,  297,  413,   53,   53,  297,
- /*  1200 */   137,  138,   91,  119, 1228, 1228, 1063, 1066, 1053, 1053,
- /*  1210 */   135,  135,  136,  136,  136,  136,  413, 1336, 1311,  446,
- /*  1220 */   137,  138,   91,  227, 1228, 1228, 1063, 1066, 1053, 1053,
- /*  1230 */   135,  135,  136,  136,  136,  136,  574, 1224,  936,  936,
- /*  1240 */   137,  126,   91,  141, 1228, 1228, 1063, 1066, 1053, 1053,
- /*  1250 */   135,  135,  136,  136,  136,  136,  533,  429,  472,  346,
- /*  1260 */   134,  134,  134,  134,  133,  133,  132,  132,  132,  131,
- /*  1270 */   128,  451,  576,  457,  233,  343, 1435,  403,  498, 1550,
- /*  1280 */   134,  134,  134,  134,  133,  133,  132,  132,  132,  131,
- /*  1290 */   128,  451,  576,  324,  576,   82,   82,  487,  576,  969,
- /*  1300 */   134,  134,  134,  134,  133,  133,  132,  132,  132,  131,
- /*  1310 */   128,  451,  288,  288,  546,   68,   68,   54,   54,  553,
- /*  1320 */   413,   69,   69,  351,    6,  573,  944,  562,  410,  409,
- /*  1330 */  1435,  943,  450,  545,  260,  259,  258,  576,  158,  576,
- /*  1340 */   413,  222, 1180,  479,  969,  138,   91,  430, 1228, 1228,
- /*  1350 */  1063, 1066, 1053, 1053,  135,  135,  136,  136,  136,  136,
- /*  1360 */    70,   70,   71,   71,  576, 1126,   91,  576, 1228, 1228,
- /*  1370 */  1063, 1066, 1053, 1053,  135,  135,  136,  136,  136,  136,
- /*  1380 */  1127,  166,  850,  851,  852, 1282,  419,   72,   72,  108,
- /*  1390 */    73,   73, 1310,  358, 1180, 1128,  576,  305,  576,  123,
- /*  1400 */   568,  494,    4,  488,  134,  134,  134,  134,  133,  133,
- /*  1410 */   132,  132,  132,  131,  128,  451,  571,  564,  534,   55,
- /*  1420 */    55,   56,   56,  576,  134,  134,  134,  134,  133,  133,
- /*  1430 */   132,  132,  132,  131,  128,  451,  576, 1104,  233, 1104,
- /*  1440 */   452, 1602,  582,    2, 1259,  576,   57,   57,  576,  321,
- /*  1450 */   576,  155,  565, 1435,  485,  353,  576,  356, 1341,   59,
- /*  1460 */    59,  576,   44,  969,  569,  419,  576,  238,   60,   60,
- /*  1470 */   261,   74,   74,   75,   75,  287,  231,  576, 1366,   76,
- /*  1480 */    76, 1040,  420,  184,   20,   20,  576,  121,  121,   77,
- /*  1490 */    77,   97,  218,  288,  288,  122,  125,  452,  577,  452,
- /*  1500 */   143,  143, 1028,  576,  520,  576,  573,  576,  562,  144,
- /*  1510 */   144,  474,  227, 1244,  478,  123,  568,  576,    4,  320,
- /*  1520 */   567,  245,  411,  576,  443,  411,   78,   78,   62,   62,
- /*  1530 */    79,   79,  571,  319, 1028, 1028, 1030, 1031,   35,  418,
- /*  1540 */    63,   63,  576,  290,  411,    9,   80,   80, 1144,  576,
- /*  1550 */   400,  576,  486,  455,  576, 1223,  452,  576,  325,  342,
- /*  1560 */   576,  111,  576, 1188,  242,   64,   64,  473,  565,  576,
- /*  1570 */    23,  576,  170,  170,  171,  171,  576,   87,   87,  328,
- /*  1580 */    65,   65,  542,   83,   83,  146,  146,  541,  123,  568,
- /*  1590 */   341,    4,   84,   84,  168,  168,  576, 1040,  576,  148,
- /*  1600 */   148,  576, 1380,  121,  121,  571, 1021,  576,  266,  576,
- /*  1610 */   424,  122,  576,  452,  577,  452,  576,  553, 1028,  142,
- /*  1620 */   142,  169,  169,  576,  162,  162,  528,  889,  371,  452,
- /*  1630 */   152,  152,  151,  151, 1379,  149,  149,  109,  370,  150,
- /*  1640 */   150,  565,  576,  480,  576,  266,   86,   86,  576, 1092,
- /*  1650 */  1028, 1028, 1030, 1031,   35,  542,  482,  576,  266,  466,
- /*  1660 */   543,  123,  568, 1616,    4,   88,   88,   85,   85,  475,
- /*  1670 */  1040,   52,   52,  222,  901,  900,  121,  121,  571, 1188,
- /*  1680 */    58,   58,  244, 1032,  122,  889,  452,  577,  452,  908,
- /*  1690 */   909, 1028,  300,  347,  504,  111,  263,  361,  165,  111,
- /*  1700 */   111, 1088,  452,  263,  974, 1153,  266, 1092,  986,  987,
- /*  1710 */   942,  939,  125,  125,  565, 1103,  872, 1103,  159,  941,
- /*  1720 */  1309,  125, 1557, 1028, 1028, 1030, 1031,   35,  542,  337,
- /*  1730 */  1530,  205, 1529,  541,  499, 1589,  490,  348, 1376,  352,
- /*  1740 */   355, 1032,  357, 1040,  359, 1324, 1308,  366,  563,  121,
- /*  1750 */   121,  376, 1188, 1389, 1434, 1362,  280,  122, 1374,  452,
- /*  1760 */   577,  452,  167, 1439, 1028, 1289, 1280, 1268, 1267, 1269,
- /*  1770 */  1609, 1359,  312,  313,  314,  397,   12,  237,  224, 1421,
- /*  1780 */   295, 1416, 1409, 1426,  339,  484,  340,  509, 1371, 1612,
- /*  1790 */  1372, 1425, 1244,  404,  301,  228, 1028, 1028, 1030, 1031,
- /*  1800 */    35, 1601, 1192,  454,  345, 1307,  292,  369, 1502, 1501,
- /*  1810 */   270,  396,  396,  395,  277,  393, 1370, 1369,  859, 1549,
- /*  1820 */   186,  123,  568,  235,    4, 1188,  391,  210,  211,  223,
- /*  1830 */  1547,  239, 1241,  327,  422,   96,  220,  195,  571,  180,
- /*  1840 */   188,  326,  468,  469,  190,  191,  502,  192,  193,  566,
- /*  1850 */   247,  109, 1430,  491,  199,  251,  102,  281,  402,  476,
- /*  1860 */   405, 1496,  452,  497,  253, 1422,   13, 1428,   14, 1427,
- /*  1870 */   203, 1507,  241,  500,  565,  354,  407,   92,   95, 1270,
- /*  1880 */   175,  254,  518,   43, 1327,  255, 1326, 1325,  436, 1518,
- /*  1890 */   350, 1318,  104,  229,  893, 1626,  440,  441, 1625,  408,
- /*  1900 */   240, 1296,  268, 1040,  310,  269, 1297,  527,  444,  121,
- /*  1910 */   121,  368, 1295, 1594, 1624,  311, 1394,  122, 1317,  452,
- /*  1920 */   577,  452,  374, 1580, 1028, 1393,  140,  553,   11,   90,
- /*  1930 */   568,  385,    4,  116,  318,  414, 1579,  110, 1483,  537,
- /*  1940 */   320,  567, 1350,  555,   42,  579,  571, 1349, 1198,  383,
- /*  1950 */   276,  390,  216,  389,  278,  279, 1028, 1028, 1030, 1031,
- /*  1960 */    35,  172,  580, 1265,  458, 1260,  415,  416,  185,  156,
- /*  1970 */   452, 1534, 1535,  173, 1533, 1532,   89,  308,  225,  226,
- /*  1980 */   846,  174,  565,  453,  217, 1188,  322,  236, 1102,  154,
- /*  1990 */  1100,  330,  187,  176, 1223,  243,  189,  925,  338,  246,
- /*  2000 */  1116,  194,  177,  425,  178,  427,   98,  196,   99,  100,
- /*  2010 */   101, 1040,  179, 1119, 1115,  248,  249,  121,  121,  163,
- /*  2020 */    24,  250,  349, 1238,  496,  122, 1108,  452,  577,  452,
- /*  2030 */  1192,  454, 1028,  266,  292,  200,  252,  201,  861,  396,
- /*  2040 */   396,  395,  277,  393,   15,  501,  859,  370,  292,  256,
- /*  2050 */   202,  554,  505,  396,  396,  395,  277,  393,  103,  239,
- /*  2060 */   859,  327,   25,   26, 1028, 1028, 1030, 1031,   35,  326,
- /*  2070 */   362,  510,  891,  239,  365,  327,  513,  904,  105,  309,
- /*  2080 */   164,  181,   27,  326,  106,  521,  107, 1185, 1069, 1155,
- /*  2090 */    17, 1154,  230, 1188,  284,  286,  265,  204,  125, 1171,
- /*  2100 */   241,   28,  978,  972,   29,   41, 1175, 1179,  175, 1173,
- /*  2110 */    30,   43,   31,    8,  241, 1178,   32, 1160,  208,  549,
- /*  2120 */    33,  111,  175, 1083, 1070,   43, 1068, 1072,  240,  113,
- /*  2130 */   114,   34,  561,  118, 1124,  271, 1073,   36,   18,  572,
- /*  2140 */  1033,  873,  240,  124,   37,  935,  272,  273, 1617,  183,
- /*  2150 */   153,  394, 1194, 1193, 1256, 1256, 1256, 1256, 1256, 1256,
- /*  2160 */  1256, 1256, 1256,  414, 1256, 1256, 1256, 1256,  320,  567,
- /*  2170 */  1256, 1256, 1256, 1256, 1256, 1256, 1256,  414, 1256, 1256,
- /*  2180 */  1256, 1256,  320,  567, 1256, 1256, 1256, 1256, 1256, 1256,
- /*  2190 */  1256, 1256,  458, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
- /*  2200 */  1256, 1256, 1256, 1256, 1256, 1256,  458,
+ /*     0 */   134,  131,  238,  290,  290, 1353,  593, 1332,  478, 1606,
+ /*    10 */   593, 1315,  593,    7,  593, 1353,  590,  593,  579,  424,
+ /*    20 */  1566,  134,  131,  238, 1318,  541,  478,  477,  575,   84,
+ /*    30 */    84, 1005,  303,   84,   84,   51,   51,   63,   63, 1006,
+ /*    40 */    84,   84,  498,  141,  142,   93,  442, 1254, 1254, 1085,
+ /*    50 */  1088, 1075, 1075,  139,  139,  140,  140,  140,  140,  424,
+ /*    60 */   296,  296,  498,  296,  296,  567,  553,  296,  296, 1306,
+ /*    70 */   574, 1358, 1358,  590,  542,  579,  590,  574,  579,  548,
+ /*    80 */   590, 1304,  579,  141,  142,   93,  576, 1254, 1254, 1085,
+ /*    90 */  1088, 1075, 1075,  139,  139,  140,  140,  140,  140,  399,
+ /*   100 */   478,  395,    6,  138,  138,  138,  138,  137,  137,  136,
+ /*   110 */   136,  136,  135,  132,  463,   44,  342,  593,  305, 1127,
+ /*   120 */  1280,    1,    1,  599,    2, 1284,  598, 1200, 1284, 1200,
+ /*   130 */   330,  424,  158,  330, 1613,  158,  390,  116,  308, 1366,
+ /*   140 */    51,   51, 1366,  138,  138,  138,  138,  137,  137,  136,
+ /*   150 */   136,  136,  135,  132,  463,  141,  142,   93,  515, 1254,
+ /*   160 */  1254, 1085, 1088, 1075, 1075,  139,  139,  140,  140,  140,
+ /*   170 */   140, 1230,  329,  584,  296,  296,  212,  296,  296,  568,
+ /*   180 */   568,  488,  143, 1072, 1072, 1086, 1089,  590, 1195,  579,
+ /*   190 */   590,  340,  579,  140,  140,  140,  140,  133,  392,  564,
+ /*   200 */   536, 1195,  250,  425, 1195,  250,  137,  137,  136,  136,
+ /*   210 */   136,  135,  132,  463,  291,  138,  138,  138,  138,  137,
+ /*   220 */   137,  136,  136,  136,  135,  132,  463,  966, 1230, 1231,
+ /*   230 */  1230,  412,  965,  467,  412,  424,  467,  489,  357, 1611,
+ /*   240 */   391,  138,  138,  138,  138,  137,  137,  136,  136,  136,
+ /*   250 */   135,  132,  463,  463,  134,  131,  238,  555, 1076,  141,
+ /*   260 */   142,   93,  593, 1254, 1254, 1085, 1088, 1075, 1075,  139,
+ /*   270 */   139,  140,  140,  140,  140, 1317,  134,  131,  238,  424,
+ /*   280 */   549, 1597, 1531,  333,   97,   83,   83,  140,  140,  140,
+ /*   290 */   140,  138,  138,  138,  138,  137,  137,  136,  136,  136,
+ /*   300 */   135,  132,  463,  141,  142,   93, 1657, 1254, 1254, 1085,
+ /*   310 */  1088, 1075, 1075,  139,  139,  140,  140,  140,  140,  138,
+ /*   320 */   138,  138,  138,  137,  137,  136,  136,  136,  135,  132,
+ /*   330 */   463,  591, 1230,  958,  958,  138,  138,  138,  138,  137,
+ /*   340 */   137,  136,  136,  136,  135,  132,  463,   44,  398,  547,
+ /*   350 */  1306,  136,  136,  136,  135,  132,  463,  386,  593,  442,
+ /*   360 */   595,  145,  595,  138,  138,  138,  138,  137,  137,  136,
+ /*   370 */   136,  136,  135,  132,  463,  500, 1230,  112,  550,  460,
+ /*   380 */   459,   51,   51,  424,  296,  296,  479,  334, 1259, 1230,
+ /*   390 */  1231, 1230, 1599, 1261,  388,  312,  444,  590,  246,  579,
+ /*   400 */   546, 1260,  271,  235,  329,  584,  551,  141,  142,   93,
+ /*   410 */   429, 1254, 1254, 1085, 1088, 1075, 1075,  139,  139,  140,
+ /*   420 */   140,  140,  140,   22,   22, 1230, 1262,  424, 1262,  216,
+ /*   430 */   296,  296,   98, 1230, 1231, 1230,  264,  884,   45,  528,
+ /*   440 */   525,  524, 1041,  590, 1269,  579,  421,  420,  393,  523,
+ /*   450 */    44,  141,  142,   93,  498, 1254, 1254, 1085, 1088, 1075,
+ /*   460 */  1075,  139,  139,  140,  140,  140,  140,  138,  138,  138,
+ /*   470 */   138,  137,  137,  136,  136,  136,  135,  132,  463,  593,
+ /*   480 */  1611,  561, 1230, 1231, 1230,   23,  264,  515,  200,  528,
+ /*   490 */   525,  524,  127,  585,  509,    4,  355,  487,  506,  523,
+ /*   500 */   593,  498,   84,   84,  134,  131,  238,  329,  584,  588,
+ /*   510 */  1627,  138,  138,  138,  138,  137,  137,  136,  136,  136,
+ /*   520 */   135,  132,  463,   19,   19,  435, 1230, 1460,  297,  297,
+ /*   530 */   311,  424, 1565,  464, 1631,  599,    2, 1284,  437,  574,
+ /*   540 */  1107,  590,  330,  579,  158,  582,  489,  357,  573,  593,
+ /*   550 */   592, 1366,  409, 1274, 1230,  141,  142,   93, 1364, 1254,
+ /*   560 */  1254, 1085, 1088, 1075, 1075,  139,  139,  140,  140,  140,
+ /*   570 */   140,  389,   84,   84, 1062,  567, 1230,  313, 1523,  593,
+ /*   580 */   125,  125,  970, 1230, 1231, 1230,  296,  296,  126,   46,
+ /*   590 */   464,  594,  464,  296,  296, 1050, 1230,  218,  439,  590,
+ /*   600 */  1604,  579,   84,   84,    7,  403,  590,  515,  579,  325,
+ /*   610 */   417, 1230, 1231, 1230,  250,  138,  138,  138,  138,  137,
+ /*   620 */   137,  136,  136,  136,  135,  132,  463, 1050, 1050, 1052,
+ /*   630 */  1053,   35, 1275, 1230, 1231, 1230,  424, 1370,  993,  574,
+ /*   640 */   371,  414,  274,  412, 1597,  467, 1302,  552,  451,  590,
+ /*   650 */   543,  579, 1530, 1230, 1231, 1230, 1214,  201,  409, 1174,
+ /*   660 */   141,  142,   93,  223, 1254, 1254, 1085, 1088, 1075, 1075,
+ /*   670 */   139,  139,  140,  140,  140,  140,  296,  296, 1250,  593,
+ /*   680 */   424,  296,  296,  236,  529,  296,  296,  515,  100,  590,
+ /*   690 */  1600,  579,   48, 1605,  590, 1230,  579,    7,  590,  577,
+ /*   700 */   579,  904,   84,   84,  141,  142,   93,  496, 1254, 1254,
+ /*   710 */  1085, 1088, 1075, 1075,  139,  139,  140,  140,  140,  140,
+ /*   720 */   138,  138,  138,  138,  137,  137,  136,  136,  136,  135,
+ /*   730 */   132,  463, 1365, 1230,  296,  296, 1250,  115, 1275,  326,
+ /*   740 */   233,  539, 1062,   40,  282,  127,  585,  590,    4,  579,
+ /*   750 */   329,  584, 1230, 1231, 1230, 1598,  593,  388,  904, 1051,
+ /*   760 */  1356, 1356,  588, 1050,  138,  138,  138,  138,  137,  137,
+ /*   770 */   136,  136,  136,  135,  132,  463,  185,  593, 1230,   19,
+ /*   780 */    19, 1230,  971, 1597,  424, 1651,  464,  129,  908, 1195,
+ /*   790 */  1230, 1231, 1230, 1325,  443, 1050, 1050, 1052,  582, 1603,
+ /*   800 */   149,  149, 1195,    7,    5, 1195, 1687,  410,  141,  142,
+ /*   810 */    93, 1536, 1254, 1254, 1085, 1088, 1075, 1075,  139,  139,
+ /*   820 */   140,  140,  140,  140, 1214,  397,  593, 1062,  424, 1536,
+ /*   830 */  1538,   50,  901,  125,  125, 1230, 1231, 1230, 1230, 1231,
+ /*   840 */  1230,  126, 1230,  464,  594,  464,  515, 1230, 1050,   84,
+ /*   850 */    84,    3,  141,  142,   93,  924, 1254, 1254, 1085, 1088,
+ /*   860 */  1075, 1075,  139,  139,  140,  140,  140,  140,  138,  138,
+ /*   870 */   138,  138,  137,  137,  136,  136,  136,  135,  132,  463,
+ /*   880 */  1050, 1050, 1052, 1053,   35,  442,  457,  532,  433, 1230,
+ /*   890 */  1062, 1361,  540,  540, 1598,  925,  388,    7, 1129, 1230,
+ /*   900 */  1231, 1230, 1129, 1536, 1230, 1231, 1230, 1051,  570, 1214,
+ /*   910 */   593, 1050,  138,  138,  138,  138,  137,  137,  136,  136,
+ /*   920 */   136,  135,  132,  463,    6,  185, 1195, 1230,  231,  593,
+ /*   930 */   382,  992,  424,  151,  151,  510, 1213,  557,  482, 1195,
+ /*   940 */   381,  160, 1195, 1050, 1050, 1052, 1230, 1231, 1230,  422,
+ /*   950 */   593,  447,   84,   84,  593,  217,  141,  142,   93,  593,
+ /*   960 */  1254, 1254, 1085, 1088, 1075, 1075,  139,  139,  140,  140,
+ /*   970 */   140,  140, 1214,   19,   19,  593,  424,   19,   19,  442,
+ /*   980 */  1063,  442,   19,   19, 1230, 1231, 1230,  515,  445,  458,
+ /*   990 */  1597,  386,  315, 1175, 1685,  556, 1685,  450,   84,   84,
+ /*  1000 */   141,  142,   93,  505, 1254, 1254, 1085, 1088, 1075, 1075,
+ /*  1010 */   139,  139,  140,  140,  140,  140,  138,  138,  138,  138,
+ /*  1020 */   137,  137,  136,  136,  136,  135,  132,  463,  442, 1147,
+ /*  1030 */   454, 1597,  362, 1041,  593,  462, 1460, 1233,   47, 1393,
+ /*  1040 */   324,  565,  565,  115, 1148,  449,    7,  460,  459,  307,
+ /*  1050 */   375,  354,  593,  113,  593,  329,  584,   19,   19, 1149,
+ /*  1060 */   138,  138,  138,  138,  137,  137,  136,  136,  136,  135,
+ /*  1070 */   132,  463,  209, 1173,  563,   19,   19,   19,   19,   49,
+ /*  1080 */   424,  944, 1175, 1686, 1046, 1686,  218,  355,  484,  343,
+ /*  1090 */   210,  945,  569,  562, 1262, 1233, 1262,  490,  314,  423,
+ /*  1100 */   424, 1598, 1206,  388,  141,  142,   93,  440, 1254, 1254,
+ /*  1110 */  1085, 1088, 1075, 1075,  139,  139,  140,  140,  140,  140,
+ /*  1120 */   352,  316,  531,  316,  141,  142,   93,  549, 1254, 1254,
+ /*  1130 */  1085, 1088, 1075, 1075,  139,  139,  140,  140,  140,  140,
+ /*  1140 */   446,   10, 1598,  274,  388,  915,  281,  299,  383,  534,
+ /*  1150 */   378,  533,  269,  593, 1206,  587,  587,  587,  374,  293,
+ /*  1160 */  1579,  991, 1173,  302,  138,  138,  138,  138,  137,  137,
+ /*  1170 */   136,  136,  136,  135,  132,  463,   53,   53,  520, 1250,
+ /*  1180 */   593, 1147, 1576,  431,  138,  138,  138,  138,  137,  137,
+ /*  1190 */   136,  136,  136,  135,  132,  463, 1148,  301,  593, 1577,
+ /*  1200 */   593, 1307,  431,   54,   54,  593,  268,  593,  461,  461,
+ /*  1210 */   461, 1149,  347,  492,  424,  135,  132,  463, 1146, 1195,
+ /*  1220 */   474,   68,   68,   69,   69,  550,  332,  287,   21,   21,
+ /*  1230 */    55,   55, 1195,  581,  424, 1195,  309, 1250,  141,  142,
+ /*  1240 */    93,  119, 1254, 1254, 1085, 1088, 1075, 1075,  139,  139,
+ /*  1250 */   140,  140,  140,  140,  593,  237,  480, 1476,  141,  142,
+ /*  1260 */    93,  593, 1254, 1254, 1085, 1088, 1075, 1075,  139,  139,
+ /*  1270 */   140,  140,  140,  140,  344,  430,  346,   70,   70,  494,
+ /*  1280 */   991, 1132, 1132,  512,   56,   56, 1269,  593,  268,  593,
+ /*  1290 */   369,  374,  593,  481,  215,  384, 1624,  481,  138,  138,
+ /*  1300 */   138,  138,  137,  137,  136,  136,  136,  135,  132,  463,
+ /*  1310 */    71,   71,   72,   72,  225,   73,   73,  593,  138,  138,
+ /*  1320 */   138,  138,  137,  137,  136,  136,  136,  135,  132,  463,
+ /*  1330 */   586,  431,  593,  872,  873,  874,  593,  911,  593, 1602,
+ /*  1340 */    74,   74,  593,    7, 1460,  242,  593,  306,  424, 1578,
+ /*  1350 */   472,  306,  364,  219,  367,   75,   75,  430,  345,   57,
+ /*  1360 */    57,   58,   58,  432,  187,   59,   59,  593,  424,   61,
+ /*  1370 */    61, 1475,  141,  142,   93,  123, 1254, 1254, 1085, 1088,
+ /*  1380 */  1075, 1075,  139,  139,  140,  140,  140,  140,  424,  570,
+ /*  1390 */    62,   62,  141,  142,   93,  911, 1254, 1254, 1085, 1088,
+ /*  1400 */  1075, 1075,  139,  139,  140,  140,  140,  140,  161,  384,
+ /*  1410 */  1624, 1474,  141,  130,   93,  441, 1254, 1254, 1085, 1088,
+ /*  1420 */  1075, 1075,  139,  139,  140,  140,  140,  140,  267,  266,
+ /*  1430 */   265, 1460,  138,  138,  138,  138,  137,  137,  136,  136,
+ /*  1440 */   136,  135,  132,  463,  593, 1336,  593, 1269, 1460,  384,
+ /*  1450 */  1624,  231,  138,  138,  138,  138,  137,  137,  136,  136,
+ /*  1460 */   136,  135,  132,  463,  593,  163,  593,   76,   76,   77,
+ /*  1470 */    77,  593,  138,  138,  138,  138,  137,  137,  136,  136,
+ /*  1480 */   136,  135,  132,  463,  475,  593,  483,   78,   78,   20,
+ /*  1490 */    20, 1249,  424,  491,   79,   79,  495,  422,  295,  235,
+ /*  1500 */  1574,   38,  511,  896,  422,  335,  240,  422,  147,  147,
+ /*  1510 */   112,  593,  424,  593,  101,  222,  991,  142,   93,  455,
+ /*  1520 */  1254, 1254, 1085, 1088, 1075, 1075,  139,  139,  140,  140,
+ /*  1530 */   140,  140,  593,   39,  148,  148,   80,   80,   93,  551,
+ /*  1540 */  1254, 1254, 1085, 1088, 1075, 1075,  139,  139,  140,  140,
+ /*  1550 */   140,  140,  328,  923,  922,   64,   64,  502, 1656, 1005,
+ /*  1560 */   933,  896,  124,  422,  121,  254,  593, 1006,  593,  226,
+ /*  1570 */   593,  127,  585,  164,    4,   16,  138,  138,  138,  138,
+ /*  1580 */   137,  137,  136,  136,  136,  135,  132,  463,  588,   81,
+ /*  1590 */    81,   65,   65,   82,   82,  593,  138,  138,  138,  138,
+ /*  1600 */   137,  137,  136,  136,  136,  135,  132,  463,  593,  226,
+ /*  1610 */   237,  966,  464,  593,  298,  593,  965,  593,   66,   66,
+ /*  1620 */   593, 1170,  593,  411,  582,  353,  469,  115,  593,  471,
+ /*  1630 */   169,  173,  173,  593,   44,  991,  174,  174,   89,   89,
+ /*  1640 */    67,   67,  593,   85,   85,  150,  150, 1114, 1043,  593,
+ /*  1650 */   273,   86,   86, 1062,  593,  503,  171,  171,  593,  125,
+ /*  1660 */   125,  497,  593,  273,  336,  152,  152,  126, 1335,  464,
+ /*  1670 */   594,  464,  146,  146, 1050,  593,  545,  172,  172,  593,
+ /*  1680 */  1054,  165,  165,  256,  339,  156,  156,  127,  585, 1586,
+ /*  1690 */     4,  329,  584,  499,  358,  273,  115,  348,  155,  155,
+ /*  1700 */   930,  931,  153,  153,  588, 1114, 1050, 1050, 1052, 1053,
+ /*  1710 */    35, 1554,  521,  593,  270, 1008, 1009,    9,  593,  372,
+ /*  1720 */   593,  115,  593,  168,  593,  115,  593, 1110,  464,  270,
+ /*  1730 */   996,  964,  273,  129, 1645, 1214,  154,  154, 1054, 1404,
+ /*  1740 */   582,   88,   88,   90,   90,   87,   87,   52,   52,   60,
+ /*  1750 */    60, 1405,  504,  537,  559, 1179,  961,  507,  129,  558,
+ /*  1760 */   127,  585, 1126,    4, 1126, 1125,  894, 1125,  162, 1062,
+ /*  1770 */   963,  359,  129, 1401,  363,  125,  125,  588,  366,  368,
+ /*  1780 */   370, 1349, 1334,  126, 1333,  464,  594,  464,  377,  387,
+ /*  1790 */  1050, 1391, 1414, 1618, 1459, 1387, 1399,  208,  580, 1464,
+ /*  1800 */  1314,  464,  243,  516, 1305, 1293, 1384, 1292, 1294, 1638,
+ /*  1810 */   288,  170,  228,  582,   12,  408,  321,  322,  241,  323,
+ /*  1820 */   245, 1446, 1050, 1050, 1052, 1053,   35,  559,  304,  350,
+ /*  1830 */   351,  501,  560,  127,  585, 1441,    4, 1451, 1434,  310,
+ /*  1840 */  1450,  526, 1062, 1332,  415,  380,  232, 1527,  125,  125,
+ /*  1850 */   588, 1214, 1396,  356, 1526,  583,  126, 1397,  464,  594,
+ /*  1860 */   464, 1641,  535, 1050, 1581, 1395, 1269, 1583, 1582,  213,
+ /*  1870 */   402,  277,  214,  227,  464, 1573,  239, 1571, 1266, 1394,
+ /*  1880 */   434,  198,  100,  224,   96,  183,  582,  191,  485,  193,
+ /*  1890 */   486,  194,  195,  196,  519, 1050, 1050, 1052, 1053,   35,
+ /*  1900 */   559,  113,  252,  413, 1447,  558,  493,   13, 1455,  416,
+ /*  1910 */  1453, 1452,   14,  202, 1521, 1062, 1532,  508,  258,  106,
+ /*  1920 */   514,  125,  125,   99, 1214, 1543,  289,  260,  206,  126,
+ /*  1930 */   365,  464,  594,  464,  361,  517, 1050,  261,  448, 1295,
+ /*  1940 */   262,  418, 1352, 1351,  108, 1350, 1655, 1654, 1343,  915,
+ /*  1950 */   419, 1322,  233,  452,  319,  379, 1321,  453, 1623,  320,
+ /*  1960 */  1320,  275, 1653,  544,  276, 1609, 1608, 1342, 1050, 1050,
+ /*  1970 */  1052, 1053,   35, 1630, 1218,  466,  385,  456,  300, 1419,
+ /*  1980 */   144, 1418,  570,  407,  407,  406,  284,  404,   11, 1508,
+ /*  1990 */   881,  396,  120,  127,  585,  394,    4, 1214,  327,  114,
+ /*  2000 */  1375, 1374,  220,  247,  400,  338,  401,  554,   42, 1224,
+ /*  2010 */   588,  596,  283,  337,  285,  286,  188,  597, 1290, 1285,
+ /*  2020 */   175, 1558,  176, 1559, 1557, 1556,  159,  317,  229,  177,
+ /*  2030 */   868,  230,   91,  465,  464,  221,  331,  468, 1165,  470,
+ /*  2040 */   473,   94,  244,   95,  249,  189,  582, 1124, 1122,  341,
+ /*  2050 */   427,  190,  178, 1249,  179,   43,  192,  947,  349,  428,
+ /*  2060 */  1138,  197,  251,  180,  181,  436,  102,  182,  438,  103,
+ /*  2070 */   104,  199,  248, 1140,  253, 1062,  105,  255, 1137,  166,
+ /*  2080 */    24,  125,  125,  257, 1264,  273,  360,  513,  259,  126,
+ /*  2090 */    15,  464,  594,  464,  204,  883, 1050,  518,  263,  373,
+ /*  2100 */   381,   92,  585, 1130,    4,  203,  205,  426,  107,  522,
+ /*  2110 */    25,   26,  329,  584,  913,  572,  527,  376,  588,  926,
+ /*  2120 */   530,  109,  184,  318,  167,  110,   27,  538, 1050, 1050,
+ /*  2130 */  1052, 1053,   35, 1211, 1091,   17,  476,  111, 1181,  234,
+ /*  2140 */   292, 1180,  464,  294,  207,  994,  129, 1201,  272, 1000,
+ /*  2150 */    28, 1197,   29,   30,  582, 1199, 1205, 1214,   31, 1204,
+ /*  2160 */    32, 1186,   41,  566,   33, 1105,  211,    8,  115, 1092,
+ /*  2170 */  1090, 1094,   34,  278,  578, 1095,  117,  122,  118, 1145,
+ /*  2180 */    36,   18,  128, 1062, 1055,  895,  957,   37,  589,  125,
+ /*  2190 */   125,  279,  186,  280, 1646,  157,  405,  126, 1220,  464,
+ /*  2200 */   594,  464, 1218,  466, 1050, 1219,  300, 1281, 1281, 1281,
+ /*  2210 */  1281,  407,  407,  406,  284,  404, 1281, 1281,  881, 1281,
+ /*  2220 */   300, 1281, 1281,  571, 1281,  407,  407,  406,  284,  404,
+ /*  2230 */  1281,  247,  881,  338, 1281, 1281, 1050, 1050, 1052, 1053,
+ /*  2240 */    35,  337, 1281, 1281, 1281,  247, 1281,  338, 1281, 1281,
+ /*  2250 */  1281, 1281, 1281, 1281, 1281,  337, 1281, 1281, 1281, 1281,
+ /*  2260 */  1281, 1281, 1281, 1281, 1281, 1214, 1281, 1281, 1281, 1281,
+ /*  2270 */  1281, 1281,  249, 1281, 1281, 1281, 1281, 1281, 1281, 1281,
+ /*  2280 */   178, 1281, 1281,   43, 1281, 1281,  249, 1281, 1281, 1281,
+ /*  2290 */  1281, 1281, 1281, 1281,  178, 1281, 1281,   43, 1281, 1281,
+ /*  2300 */   248, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281,
+ /*  2310 */  1281, 1281, 1281, 1281,  248, 1281, 1281, 1281, 1281, 1281,
+ /*  2320 */  1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281,
+ /*  2330 */  1281, 1281, 1281, 1281, 1281,  426, 1281, 1281, 1281, 1281,
+ /*  2340 */   329,  584, 1281, 1281, 1281, 1281, 1281, 1281, 1281,  426,
+ /*  2350 */  1281, 1281, 1281, 1281,  329,  584, 1281, 1281, 1281, 1281,
+ /*  2360 */  1281, 1281, 1281, 1281,  476, 1281, 1281, 1281, 1281, 1281,
+ /*  2370 */  1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281,  476,
 };
 static const YYCODETYPE yy_lookahead[] = {
- /*     0 */   277,  278,  279,  241,  242,  225,  195,  227,  195,  241,
- /*    10 */   242,  195,  217,  221,  195,  235,  254,  195,  256,   19,
- /*    20 */   225,  298,  254,  195,  256,  206,  213,  214,  206,  218,
- /*    30 */   219,   31,  206,  195,  218,  219,  195,  218,  219,   39,
- /*    40 */   218,  219,  313,   43,   44,   45,  317,   47,   48,   49,
+ /*     0 */   277,  278,  279,  241,  242,  225,  195,  227,  195,  312,
+ /*    10 */   195,  218,  195,  316,  195,  235,  254,  195,  256,   19,
+ /*    20 */   297,  277,  278,  279,  218,  206,  213,  214,  206,  218,
+ /*    30 */   219,   31,  206,  218,  219,  218,  219,  218,  219,   39,
+ /*    40 */   218,  219,  195,   43,   44,   45,  195,   47,   48,   49,
  /*    50 */    50,   51,   52,   53,   54,   55,   56,   57,   58,   19,
- /*    60 */   241,  242,  195,  241,  242,  195,  255,  241,  242,  277,
- /*    70 */   278,  279,  234,  254,  255,  256,  254,  255,  256,  218,
- /*    80 */   254,  240,  256,   43,   44,   45,  264,   47,   48,   49,
- /*    90 */    50,   51,   52,   53,   54,   55,   56,   57,   58,  271,
- /*   100 */   287,   22,   23,  103,  104,  105,  106,  107,  108,  109,
- /*   110 */   110,  111,  112,  113,  114,  114,   47,   48,   49,   50,
+ /*    60 */   241,  242,  195,  241,  242,  195,  255,  241,  242,  195,
+ /*    70 */   255,  237,  238,  254,  255,  256,  254,  255,  256,  264,
+ /*    80 */   254,  207,  256,   43,   44,   45,  264,   47,   48,   49,
+ /*    90 */    50,   51,   52,   53,   54,   55,   56,   57,   58,  251,
+ /*   100 */   287,  253,  215,  103,  104,  105,  106,  107,  108,  109,
+ /*   110 */   110,  111,  112,  113,  114,   82,  265,  195,  271,   11,
  /*   120 */   187,  188,  189,  190,  191,  192,  190,   87,  192,   89,
- /*   130 */   197,   19,  199,  197,  318,  199,  320,   25,  195,  206,
- /*   140 */   299,  271,  206,  103,  104,  105,  106,  107,  108,  109,
+ /*   130 */   197,   19,  199,  197,  317,  199,  319,   25,  271,  206,
+ /*   140 */   218,  219,  206,  103,  104,  105,  106,  107,  108,  109,
  /*   150 */   110,  111,  112,  113,  114,   43,   44,   45,  195,   47,
  /*   160 */    48,   49,   50,   51,   52,   53,   54,   55,   56,   57,
- /*   170 */    58,   60,   21,  195,  241,  242,  215,  241,  242,  312,
- /*   180 */   313,  102,   70,  205,  317,  207,  242,  254,   77,  256,
- /*   190 */   254,  122,  256,   55,   56,   57,   58,   59,  254,   88,
- /*   200 */   256,   90,  269,  240,   93,  269,  107,  108,  109,  110,
- /*   210 */   111,  112,  113,  114,  271,  103,  104,  105,  106,  107,
- /*   220 */   108,  109,  110,  111,  112,  113,  114,  313,  117,  118,
- /*   230 */   119,  317,   81,  195,  301,   19,  195,  301,  277,  278,
- /*   240 */   279,  103,  104,  105,  106,  107,  108,  109,  110,  111,
- /*   250 */   112,  113,  114,   55,   56,   57,   58,  146,  195,   43,
- /*   260 */    44,   45,   74,   47,   48,   49,   50,   51,   52,   53,
- /*   270 */    54,   55,   56,   57,   58,  124,  195,   60,  109,  110,
- /*   280 */   111,  112,  113,  114,   68,  195,  103,  104,  105,  106,
- /*   290 */   107,  108,  109,  110,  111,  112,  113,  114,  208,  218,
- /*   300 */   219,  103,  104,  105,  106,  107,  108,  109,  110,  111,
- /*   310 */   112,  113,  114,  162,  233,   24,  128,  129,  130,  103,
+ /*   170 */    58,   60,  139,  140,  241,  242,  289,  241,  242,  309,
+ /*   180 */   310,  294,   70,   47,   48,   49,   50,  254,   77,  256,
+ /*   190 */   254,  195,  256,   55,   56,   57,   58,   59,  221,   88,
+ /*   200 */   109,   90,  269,  240,   93,  269,  107,  108,  109,  110,
+ /*   210 */   111,  112,  113,  114,  215,  103,  104,  105,  106,  107,
+ /*   220 */   108,  109,  110,  111,  112,  113,  114,  136,  117,  118,
+ /*   230 */   119,  298,  141,  300,  298,   19,  300,  129,  130,  317,
+ /*   240 */   318,  103,  104,  105,  106,  107,  108,  109,  110,  111,
+ /*   250 */   112,  113,  114,  114,  277,  278,  279,  146,  122,   43,
+ /*   260 */    44,   45,  195,   47,   48,   49,   50,   51,   52,   53,
+ /*   270 */    54,   55,   56,   57,   58,  218,  277,  278,  279,   19,
+ /*   280 */    19,  195,  286,   23,   68,  218,  219,   55,   56,   57,
+ /*   290 */    58,  103,  104,  105,  106,  107,  108,  109,  110,  111,
+ /*   300 */   112,  113,  114,   43,   44,   45,  232,   47,   48,   49,
+ /*   310 */    50,   51,   52,   53,   54,   55,   56,   57,   58,  103,
  /*   320 */   104,  105,  106,  107,  108,  109,  110,  111,  112,  113,
- /*   330 */   114,  195,  195,  215,  117,  118,  119,  120,  195,   19,
- /*   340 */   123,  124,  125,  207,   24,   74,  246,   60,  310,  311,
- /*   350 */   133,   60,  311,   82,   22,  218,  219,  257,  195,   19,
- /*   360 */    73,  218,  219,   43,   44,   45,  206,   47,   48,   49,
- /*   370 */    50,   51,   52,   53,   54,   55,   56,   57,   58,   22,
- /*   380 */    23,  218,  219,   43,   44,   45,   54,   47,   48,   49,
- /*   390 */    50,   51,   52,   53,   54,   55,   56,   57,   58,  128,
- /*   400 */    82,  241,  242,  195,  117,  118,  119,  289,   60,  118,
- /*   410 */   139,  140,  294,  195,  254,  195,  256,  195,  255,  259,
- /*   420 */   260,   73,   22,  103,  104,  105,  106,  107,  108,  109,
- /*   430 */   110,  111,  112,  113,  114,  206,  218,  219,  218,  219,
- /*   440 */   218,  219,  234,  103,  104,  105,  106,  107,  108,  109,
- /*   450 */   110,  111,  112,  113,  114,  318,  319,  139,  140,  102,
- /*   460 */    60,  318,  319,  221,   19,  117,  118,  119,   23,  195,
- /*   470 */   241,  242,  313,  255,  206,  255,  317,  255,  206,  129,
- /*   480 */   130,  206,  264,  254,  264,  256,  264,  195,   43,   44,
- /*   490 */    45,  151,   47,   48,   49,   50,   51,   52,   53,   54,
- /*   500 */    55,   56,   57,   58,  246,  213,  214,   19,   19,  241,
- /*   510 */   242,  195,   23,  241,  242,  257,  241,  242,  118,  277,
- /*   520 */   278,  279,  254,   29,  256,   60,  254,   33,  256,  254,
- /*   530 */   206,  256,   43,   44,   45,  218,   47,   48,   49,   50,
- /*   540 */    51,   52,   53,   54,   55,   56,   57,   58,  103,  104,
- /*   550 */   105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
- /*   560 */    66,   19,  218,   60,  120,  241,  242,  123,  124,  125,
- /*   570 */    60,  232,   77,   19,   20,   26,   22,  133,  254,  287,
- /*   580 */   256,  265,  117,  118,  119,   90,  312,  313,   93,   47,
- /*   590 */    36,  317,  103,  104,  105,  106,  107,  108,  109,  110,
- /*   600 */   111,  112,  113,  114,  116,  117,  277,  278,  279,   60,
- /*   610 */   107,  108,   19,  276,   60,   31,   23,  152,  195,  116,
- /*   620 */   117,  118,  119,   39,  121,  276,   72,  117,  118,  119,
- /*   630 */   166,  167,  129,  145,  237,  238,   43,   44,   45,  276,
- /*   640 */    47,   48,   49,   50,   51,   52,   53,   54,   55,   56,
- /*   650 */    57,   58,  315,  316,  144,  101,   19,  154,  116,  156,
- /*   660 */    23,  107,  108,  109,  315,  316,  117,  118,  119,  115,
- /*   670 */    60,  117,  118,  119,  132,  200,  122,   60,  315,  316,
- /*   680 */    43,   44,   45,  272,   47,   48,   49,   50,   51,   52,
- /*   690 */    53,   54,   55,   56,   57,   58,  103,  104,  105,  106,
- /*   700 */   107,  108,  109,  110,  111,  112,  113,  114,  154,  155,
- /*   710 */   156,  157,  158,  212,  213,  214,   22,  195,  101,   22,
- /*   720 */    60,   19,   20,   60,   22,  139,  140,  117,  118,  119,
- /*   730 */    22,  251,  195,  253,  117,  118,  195,  183,   36,  122,
- /*   740 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
- /*   750 */   113,  114,  195,  195,   60,  218,  219,   60,  195,  284,
- /*   760 */    19,   25,   60,  288,   23,  237,  238,   22,   60,  109,
- /*   770 */   233,  154,  155,  156,   72,  218,  219,  117,  118,  119,
- /*   780 */   117,  118,  119,  116,   43,   44,   45,  265,   47,   48,
- /*   790 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
- /*   800 */   183,  243,   25,  101,   19,   60,  265,  144,   23,  107,
- /*   810 */   108,  117,  118,  119,  117,  118,  119,  115,  151,  117,
- /*   820 */   118,  119,   82,  195,  122,  117,  118,  119,   43,   44,
- /*   830 */    45,  195,   47,   48,   49,   50,   51,   52,   53,   54,
- /*   840 */    55,   56,   57,   58,  103,  104,  105,  106,  107,  108,
- /*   850 */   109,  110,  111,  112,  113,  114,  154,  155,  156,  157,
- /*   860 */   158,  121,  117,  118,  119,  307,  101,  309,  195,   22,
- /*   870 */    23,  195,   25,   19,   35,  139,  140,  195,   24,  139,
- /*   880 */   140,  208,  195,  118,  109,  183,   22,  122,  103,  104,
- /*   890 */   105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
- /*   900 */   304,  305,   77,  230,  127,  232,   67,  195,   19,  195,
- /*   910 */   195,  136,   23,   88,   75,   90,  141,  203,   93,  154,
- /*   920 */   155,  156,  208,  295,   60,  243,   22,   23,   19,   25,
- /*   930 */   218,  219,   43,   44,   45,  100,   47,   48,   49,   50,
- /*   940 */    51,   52,   53,   54,   55,   56,   57,   58,  183,  102,
- /*   950 */    96,  195,   43,   44,   45,  240,   47,   48,   49,   50,
- /*   960 */    51,   52,   53,   54,   55,   56,   57,   58,  114,  134,
- /*   970 */   131,  146,   25,  286,  120,  121,  122,  123,  124,  125,
- /*   980 */   126,  117,  118,  119,  313,  195,  132,  195,  317,  307,
- /*   990 */   195,  309,  103,  104,  105,  106,  107,  108,  109,  110,
- /*  1000 */   111,  112,  113,  114,  195,  195,  102,  195,  195,  195,
- /*  1010 */   218,  219,  103,  104,  105,  106,  107,  108,  109,  110,
- /*  1020 */   111,  112,  113,  114,   77,  233,  195,   60,  218,  219,
- /*  1030 */   218,  219,  218,  219,   23,  195,   25,   90,  243,  159,
- /*  1040 */    93,  161,   19,  233,  195,  233,   23,  233,   16,  218,
- /*  1050 */   219,  195,  243,  212,  213,  214,  262,  263,  218,  219,
- /*  1060 */   195,  271,   19,  307,  233,  309,   43,   44,   45,  160,
- /*  1070 */    47,   48,   49,   50,   51,   52,   53,   54,   55,   56,
- /*  1080 */    57,   58,  195,  218,  219,  118,   43,   44,   45,  240,
- /*  1090 */    47,   48,   49,   50,   51,   52,   53,   54,   55,   56,
- /*  1100 */    57,   58,  307,  195,  309,  218,  219,  263,   12,  195,
- /*  1110 */    78,  267,   80,  112,  113,  114,  307,   22,  309,   24,
- /*  1120 */   255,  281,  266,   27,  107,  108,  103,  104,  105,  106,
- /*  1130 */   107,  108,  109,  110,  111,  112,  113,  114,   42,  195,
- /*  1140 */    11,   22,  255,   24,  195,  195,  103,  104,  105,  106,
- /*  1150 */   107,  108,  109,  110,  111,  112,  113,  114,   19,  195,
- /*  1160 */    64,  195,  218,  219,  195,  313,  195,  218,  219,  317,
- /*  1170 */    74,  154,  195,  156,  195,  195,   19,  233,   23,   60,
- /*  1180 */    25,   24,  218,  219,  218,  219,  195,  218,  219,  218,
- /*  1190 */   219,  128,  129,  130,  162,  263,   19,  218,  219,  267,
- /*  1200 */    43,   44,   45,  160,   47,   48,   49,   50,   51,   52,
- /*  1210 */    53,   54,   55,   56,   57,   58,   19,  240,  228,  255,
- /*  1220 */    43,   44,   45,   25,   47,   48,   49,   50,   51,   52,
- /*  1230 */    53,   54,   55,   56,   57,   58,  135,  118,  137,  138,
- /*  1240 */    43,   44,   45,   22,   47,   48,   49,   50,   51,   52,
- /*  1250 */    53,   54,   55,   56,   57,   58,  117,  266,  129,  130,
- /*  1260 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
- /*  1270 */   113,  114,  195,  195,  119,  295,  195,  206,  195,  195,
- /*  1280 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
- /*  1290 */   113,  114,  195,  195,  195,  218,  219,  195,  195,  144,
- /*  1300 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
- /*  1310 */   113,  114,  241,  242,   67,  218,  219,  218,  219,  146,
- /*  1320 */    19,  218,  219,  240,  215,  254,  136,  256,  107,  108,
- /*  1330 */   195,  141,  255,   86,  128,  129,  130,  195,  165,  195,
- /*  1340 */    19,  143,   95,  272,   25,   44,   45,  266,   47,   48,
- /*  1350 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
- /*  1360 */   218,  219,  218,  219,  195,   12,   45,  195,   47,   48,
- /*  1370 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
- /*  1380 */    27,   23,    7,    8,    9,  210,  211,  218,  219,  116,
- /*  1390 */   218,  219,  228,   16,  147,   42,  195,  295,  195,   19,
- /*  1400 */    20,  266,   22,  294,  103,  104,  105,  106,  107,  108,
- /*  1410 */   109,  110,  111,  112,  113,  114,   36,   64,  145,  218,
- /*  1420 */   219,  218,  219,  195,  103,  104,  105,  106,  107,  108,
- /*  1430 */   109,  110,  111,  112,  113,  114,  195,  154,  119,  156,
- /*  1440 */    60,  189,  190,  191,  192,  195,  218,  219,  195,  197,
- /*  1450 */   195,  199,   72,  195,   19,   78,  195,   80,  206,  218,
- /*  1460 */   219,  195,   82,  144,  210,  211,  195,   15,  218,  219,
- /*  1470 */    47,  218,  219,  218,  219,  259,  260,  195,  261,  218,
- /*  1480 */   219,  101,  302,  303,  218,  219,  195,  107,  108,  218,
- /*  1490 */   219,  150,  151,  241,  242,  115,   25,  117,  118,  119,
- /*  1500 */   218,  219,  122,  195,  146,  195,  254,  195,  256,  218,
- /*  1510 */   219,  246,   25,   61,  246,   19,   20,  195,   22,  139,
- /*  1520 */   140,  269,  257,  195,  266,  257,  218,  219,  218,  219,
- /*  1530 */   218,  219,   36,  246,  154,  155,  156,  157,  158,  116,
- /*  1540 */   218,  219,  195,   22,  257,   49,  218,  219,   23,  195,
- /*  1550 */    25,  195,  117,  301,  195,   25,   60,  195,  195,   23,
- /*  1560 */   195,   25,  195,  183,   24,  218,  219,  130,   72,  195,
- /*  1570 */    22,  195,  218,  219,  218,  219,  195,  218,  219,  195,
- /*  1580 */   218,  219,   86,  218,  219,  218,  219,   91,   19,   20,
- /*  1590 */   153,   22,  218,  219,  218,  219,  195,  101,  195,  218,
- /*  1600 */   219,  195,  195,  107,  108,   36,   23,  195,   25,  195,
- /*  1610 */    62,  115,  195,  117,  118,  119,  195,  146,  122,  218,
- /*  1620 */   219,  218,  219,  195,  218,  219,   19,   60,  122,   60,
- /*  1630 */   218,  219,  218,  219,  195,  218,  219,  150,  132,  218,
- /*  1640 */   219,   72,  195,   23,  195,   25,  218,  219,  195,   60,
- /*  1650 */   154,  155,  156,  157,  158,   86,   23,  195,   25,  195,
- /*  1660 */    91,   19,   20,  142,   22,  218,  219,  218,  219,  130,
- /*  1670 */   101,  218,  219,  143,  121,  122,  107,  108,   36,  183,
- /*  1680 */   218,  219,  142,   60,  115,  118,  117,  118,  119,    7,
- /*  1690 */     8,  122,  153,   23,   23,   25,   25,   23,   23,   25,
- /*  1700 */    25,   23,   60,   25,   23,   98,   25,  118,   84,   85,
- /*  1710 */    23,   23,   25,   25,   72,  154,   23,  156,   25,   23,
- /*  1720 */   228,   25,  195,  154,  155,  156,  157,  158,   86,  195,
- /*  1730 */   195,  258,  195,   91,  291,  322,  195,  195,  195,  195,
- /*  1740 */   195,  118,  195,  101,  195,  195,  195,  195,  238,  107,
- /*  1750 */   108,  195,  183,  195,  195,  195,  290,  115,  195,  117,
- /*  1760 */   118,  119,  244,  195,  122,  195,  195,  195,  195,  195,
- /*  1770 */   195,  258,  258,  258,  258,  193,  245,  300,  216,  274,
- /*  1780 */   247,  270,  270,  274,  296,  296,  248,  222,  262,  198,
- /*  1790 */   262,  274,   61,  274,  248,  231,  154,  155,  156,  157,
- /*  1800 */   158,    0,    1,    2,  247,  227,    5,  221,  221,  221,
- /*  1810 */   142,   10,   11,   12,   13,   14,  262,  262,   17,  202,
- /*  1820 */   300,   19,   20,  300,   22,  183,  247,  251,  251,  245,
- /*  1830 */   202,   30,   38,   32,  202,  152,  151,   22,   36,   43,
- /*  1840 */   236,   40,   18,  202,  239,  239,   18,  239,  239,  283,
- /*  1850 */   201,  150,  236,  202,  236,  201,  159,  202,  248,  248,
- /*  1860 */   248,  248,   60,   63,  201,  275,  273,  275,  273,  275,
- /*  1870 */    22,  286,   71,  223,   72,  202,  223,  297,  297,  202,
- /*  1880 */    79,  201,  116,   82,  220,  201,  220,  220,   65,  293,
- /*  1890 */   292,  229,   22,  166,  127,  226,   24,  114,  226,  223,
- /*  1900 */    99,  222,  202,  101,  285,   92,  220,  308,   83,  107,
- /*  1910 */   108,  220,  220,  316,  220,  285,  268,  115,  229,  117,
- /*  1920 */   118,  119,  223,  321,  122,  268,  149,  146,   22,   19,
- /*  1930 */    20,  202,   22,  159,  282,  134,  321,  148,  280,  147,
- /*  1940 */   139,  140,  252,  141,   25,  204,   36,  252,   13,  251,
- /*  1950 */   196,  248,  250,  249,  196,    6,  154,  155,  156,  157,
- /*  1960 */   158,  209,  194,  194,  163,  194,  306,  306,  303,  224,
- /*  1970 */    60,  215,  215,  209,  215,  215,  215,  224,  216,  216,
- /*  1980 */     4,  209,   72,    3,   22,  183,  164,   15,   23,   16,
- /*  1990 */    23,  140,  152,  131,   25,   24,  143,   20,   16,  145,
- /*  2000 */     1,  143,  131,   62,  131,   37,   54,  152,   54,   54,
- /*  2010 */    54,  101,  131,  117,    1,   34,  142,  107,  108,    5,
- /*  2020 */    22,  116,  162,   76,   41,  115,   69,  117,  118,  119,
- /*  2030 */     1,    2,  122,   25,    5,   69,  142,  116,   20,   10,
- /*  2040 */    11,   12,   13,   14,   24,   19,   17,  132,    5,  126,
- /*  2050 */    22,  141,   68,   10,   11,   12,   13,   14,   22,   30,
- /*  2060 */    17,   32,   22,   22,  154,  155,  156,  157,  158,   40,
- /*  2070 */    23,   68,   60,   30,   24,   32,   97,   28,   22,   68,
- /*  2080 */    23,   37,   34,   40,  150,   22,   25,   23,   23,   23,
- /*  2090 */    22,   98,  142,  183,   23,   23,   34,   22,   25,   89,
- /*  2100 */    71,   34,  117,  144,   34,   22,   76,   76,   79,   87,
- /*  2110 */    34,   82,   34,   44,   71,   94,   34,   23,   25,   24,
- /*  2120 */    34,   25,   79,   23,   23,   82,   23,   23,   99,  143,
- /*  2130 */   143,   22,   25,   25,   23,   22,   11,   22,   22,   25,
- /*  2140 */    23,   23,   99,   22,   22,  136,  142,  142,  142,   25,
- /*  2150 */    23,   15,    1,    1,  323,  323,  323,  323,  323,  323,
- /*  2160 */   323,  323,  323,  134,  323,  323,  323,  323,  139,  140,
- /*  2170 */   323,  323,  323,  323,  323,  323,  323,  134,  323,  323,
- /*  2180 */   323,  323,  139,  140,  323,  323,  323,  323,  323,  323,
- /*  2190 */   323,  323,  163,  323,  323,  323,  323,  323,  323,  323,
- /*  2200 */   323,  323,  323,  323,  323,  323,  163,  323,  323,  323,
- /*  2210 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2220 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2230 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2240 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2250 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2260 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2270 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2280 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2290 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2300 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2310 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2320 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2330 */   323,  323,  323,  323,  323,  323,  323,  323,  323,  323,
- /*  2340 */   323,  187,  187,  187,  187,  187,  187,  187,  187,  187,
- /*  2350 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
- /*  2360 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
- /*  2370 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
- /*  2380 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
- /*  2390 */   187,  187,  187,  187,
+ /*   330 */   114,  135,   60,  137,  138,  103,  104,  105,  106,  107,
+ /*   340 */   108,  109,  110,  111,  112,  113,  114,   82,  281,  206,
+ /*   350 */   195,  109,  110,  111,  112,  113,  114,  195,  195,  195,
+ /*   360 */   205,   22,  207,  103,  104,  105,  106,  107,  108,  109,
+ /*   370 */   110,  111,  112,  113,  114,  195,   60,  116,  117,  107,
+ /*   380 */   108,  218,  219,   19,  241,  242,  121,   23,  116,  117,
+ /*   390 */   118,  119,  306,  121,  308,  206,  234,  254,   15,  256,
+ /*   400 */   195,  129,  259,  260,  139,  140,  145,   43,   44,   45,
+ /*   410 */   200,   47,   48,   49,   50,   51,   52,   53,   54,   55,
+ /*   420 */    56,   57,   58,  218,  219,   60,  154,   19,  156,  265,
+ /*   430 */   241,  242,   24,  117,  118,  119,  120,   21,   73,  123,
+ /*   440 */   124,  125,   74,  254,   61,  256,  107,  108,  221,  133,
+ /*   450 */    82,   43,   44,   45,  195,   47,   48,   49,   50,   51,
+ /*   460 */    52,   53,   54,   55,   56,   57,   58,  103,  104,  105,
+ /*   470 */   106,  107,  108,  109,  110,  111,  112,  113,  114,  195,
+ /*   480 */   317,  318,  117,  118,  119,   22,  120,  195,   22,  123,
+ /*   490 */   124,  125,   19,   20,  284,   22,  128,   81,  288,  133,
+ /*   500 */   195,  195,  218,  219,  277,  278,  279,  139,  140,   36,
+ /*   510 */   195,  103,  104,  105,  106,  107,  108,  109,  110,  111,
+ /*   520 */   112,  113,  114,  218,  219,   62,   60,  195,  241,  242,
+ /*   530 */   271,   19,  240,   60,  189,  190,  191,  192,  233,  255,
+ /*   540 */   124,  254,  197,  256,  199,   72,  129,  130,  264,  195,
+ /*   550 */   195,  206,   22,   23,   60,   43,   44,   45,  206,   47,
+ /*   560 */    48,   49,   50,   51,   52,   53,   54,   55,   56,   57,
+ /*   570 */    58,  195,  218,  219,  101,  195,   60,  271,  162,  195,
+ /*   580 */   107,  108,  109,  117,  118,  119,  241,  242,  115,   73,
+ /*   590 */   117,  118,  119,  241,  242,  122,   60,  195,  266,  254,
+ /*   600 */   312,  256,  218,  219,  316,  203,  254,  195,  256,  255,
+ /*   610 */   208,  117,  118,  119,  269,  103,  104,  105,  106,  107,
+ /*   620 */   108,  109,  110,  111,  112,  113,  114,  154,  155,  156,
+ /*   630 */   157,  158,  102,  117,  118,  119,   19,  242,  144,  255,
+ /*   640 */    23,  206,   24,  298,  195,  300,  206,  195,  264,  254,
+ /*   650 */   206,  256,  240,  117,  118,  119,  183,   22,   22,   23,
+ /*   660 */    43,   44,   45,  151,   47,   48,   49,   50,   51,   52,
+ /*   670 */    53,   54,   55,   56,   57,   58,  241,  242,   60,  195,
+ /*   680 */    19,  241,  242,  195,   23,  241,  242,  195,  152,  254,
+ /*   690 */   310,  256,  243,  312,  254,   60,  256,  316,  254,  206,
+ /*   700 */   256,   60,  218,  219,   43,   44,   45,  272,   47,   48,
+ /*   710 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
+ /*   720 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
+ /*   730 */   113,  114,  240,   60,  241,  242,  118,   25,  102,  255,
+ /*   740 */   166,  167,  101,   22,   26,   19,   20,  254,   22,  256,
+ /*   750 */   139,  140,  117,  118,  119,  306,  195,  308,  117,  118,
+ /*   760 */   237,  238,   36,  122,  103,  104,  105,  106,  107,  108,
+ /*   770 */   109,  110,  111,  112,  113,  114,  195,  195,   60,  218,
+ /*   780 */   219,   60,  109,  195,   19,  217,   60,   25,   23,   77,
+ /*   790 */   117,  118,  119,  225,  233,  154,  155,  156,   72,  312,
+ /*   800 */   218,  219,   90,  316,   22,   93,  303,  304,   43,   44,
+ /*   810 */    45,  195,   47,   48,   49,   50,   51,   52,   53,   54,
+ /*   820 */    55,   56,   57,   58,  183,  195,  195,  101,   19,  213,
+ /*   830 */   214,  243,   23,  107,  108,  117,  118,  119,  117,  118,
+ /*   840 */   119,  115,   60,  117,  118,  119,  195,   60,  122,  218,
+ /*   850 */   219,   22,   43,   44,   45,   35,   47,   48,   49,   50,
+ /*   860 */    51,   52,   53,   54,   55,   56,   57,   58,  103,  104,
+ /*   870 */   105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
+ /*   880 */   154,  155,  156,  157,  158,  195,  255,   67,  195,   60,
+ /*   890 */   101,  240,  311,  312,  306,   75,  308,  316,   29,  117,
+ /*   900 */   118,  119,   33,  287,  117,  118,  119,  118,  146,  183,
+ /*   910 */   195,  122,  103,  104,  105,  106,  107,  108,  109,  110,
+ /*   920 */   111,  112,  113,  114,  215,  195,   77,   60,   25,  195,
+ /*   930 */   122,  144,   19,  218,  219,   66,   23,   88,  246,   90,
+ /*   940 */   132,   25,   93,  154,  155,  156,  117,  118,  119,  257,
+ /*   950 */   195,  131,  218,  219,  195,  265,   43,   44,   45,  195,
+ /*   960 */    47,   48,   49,   50,   51,   52,   53,   54,   55,   56,
+ /*   970 */    57,   58,  183,  218,  219,  195,   19,  218,  219,  195,
+ /*   980 */    23,  195,  218,  219,  117,  118,  119,  195,  233,  255,
+ /*   990 */   195,  195,  233,   22,   23,  146,   25,  233,  218,  219,
+ /*  1000 */    43,   44,   45,  294,   47,   48,   49,   50,   51,   52,
+ /*  1010 */    53,   54,   55,   56,   57,   58,  103,  104,  105,  106,
+ /*  1020 */   107,  108,  109,  110,  111,  112,  113,  114,  195,   12,
+ /*  1030 */   234,  195,  240,   74,  195,  255,  195,   60,  243,  262,
+ /*  1040 */   263,  311,  312,   25,   27,   19,  316,  107,  108,  265,
+ /*  1050 */    24,  265,  195,  150,  195,  139,  140,  218,  219,   42,
+ /*  1060 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
+ /*  1070 */   113,  114,  233,  102,   67,  218,  219,  218,  219,  243,
+ /*  1080 */    19,   64,   22,   23,   23,   25,  195,  128,  129,  130,
+ /*  1090 */   233,   74,  233,   86,  154,  118,  156,  130,  265,  208,
+ /*  1100 */    19,  306,   95,  308,   43,   44,   45,  266,   47,   48,
+ /*  1110 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
+ /*  1120 */   153,  230,   96,  232,   43,   44,   45,   19,   47,   48,
+ /*  1130 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
+ /*  1140 */   114,   22,  306,   24,  308,  127,  120,  121,  122,  123,
+ /*  1150 */   124,  125,  126,  195,  147,  212,  213,  214,  132,   23,
+ /*  1160 */   195,   25,  102,  100,  103,  104,  105,  106,  107,  108,
+ /*  1170 */   109,  110,  111,  112,  113,  114,  218,  219,   19,   60,
+ /*  1180 */   195,   12,  210,  211,  103,  104,  105,  106,  107,  108,
+ /*  1190 */   109,  110,  111,  112,  113,  114,   27,  134,  195,  195,
+ /*  1200 */   195,  210,  211,  218,  219,  195,   47,  195,  212,  213,
+ /*  1210 */   214,   42,   16,  130,   19,  112,  113,  114,   23,   77,
+ /*  1220 */   195,  218,  219,  218,  219,  117,  163,  164,  218,  219,
+ /*  1230 */   218,  219,   90,   64,   19,   93,  153,  118,   43,   44,
+ /*  1240 */    45,  160,   47,   48,   49,   50,   51,   52,   53,   54,
+ /*  1250 */    55,   56,   57,   58,  195,  119,  272,  276,   43,   44,
+ /*  1260 */    45,  195,   47,   48,   49,   50,   51,   52,   53,   54,
+ /*  1270 */    55,   56,   57,   58,   78,  116,   80,  218,  219,  116,
+ /*  1280 */   144,  128,  129,  130,  218,  219,   61,  195,   47,  195,
+ /*  1290 */    16,  132,  195,  263,  195,  314,  315,  267,  103,  104,
+ /*  1300 */   105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
+ /*  1310 */   218,  219,  218,  219,  151,  218,  219,  195,  103,  104,
+ /*  1320 */   105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
+ /*  1330 */   210,  211,  195,    7,    8,    9,  195,   60,  195,  312,
+ /*  1340 */   218,  219,  195,  316,  195,  120,  195,  263,   19,  195,
+ /*  1350 */   125,  267,   78,   24,   80,  218,  219,  116,  162,  218,
+ /*  1360 */   219,  218,  219,  301,  302,  218,  219,  195,   19,  218,
+ /*  1370 */   219,  276,   43,   44,   45,  160,   47,   48,   49,   50,
+ /*  1380 */    51,   52,   53,   54,   55,   56,   57,   58,   19,  146,
+ /*  1390 */   218,  219,   43,   44,   45,  118,   47,   48,   49,   50,
+ /*  1400 */    51,   52,   53,   54,   55,   56,   57,   58,  165,  314,
+ /*  1410 */   315,  276,   43,   44,   45,  266,   47,   48,   49,   50,
+ /*  1420 */    51,   52,   53,   54,   55,   56,   57,   58,  128,  129,
+ /*  1430 */   130,  195,  103,  104,  105,  106,  107,  108,  109,  110,
+ /*  1440 */   111,  112,  113,  114,  195,  228,  195,   61,  195,  314,
+ /*  1450 */   315,   25,  103,  104,  105,  106,  107,  108,  109,  110,
+ /*  1460 */   111,  112,  113,  114,  195,   22,  195,  218,  219,  218,
+ /*  1470 */   219,  195,  103,  104,  105,  106,  107,  108,  109,  110,
+ /*  1480 */   111,  112,  113,  114,  195,  195,  246,  218,  219,  218,
+ /*  1490 */   219,   25,   19,  246,  218,  219,  246,  257,  259,  260,
+ /*  1500 */   195,   22,  266,   60,  257,  195,  120,  257,  218,  219,
+ /*  1510 */   116,  195,   19,  195,  150,  151,   25,   44,   45,  266,
+ /*  1520 */    47,   48,   49,   50,   51,   52,   53,   54,   55,   56,
+ /*  1530 */    57,   58,  195,   54,  218,  219,  218,  219,   45,  145,
+ /*  1540 */    47,   48,   49,   50,   51,   52,   53,   54,   55,   56,
+ /*  1550 */    57,   58,  246,  121,  122,  218,  219,   19,   23,   31,
+ /*  1560 */    25,  118,  159,  257,  161,   24,  195,   39,  195,  143,
+ /*  1570 */   195,   19,   20,   22,   22,   24,  103,  104,  105,  106,
+ /*  1580 */   107,  108,  109,  110,  111,  112,  113,  114,   36,  218,
+ /*  1590 */   219,  218,  219,  218,  219,  195,  103,  104,  105,  106,
+ /*  1600 */   107,  108,  109,  110,  111,  112,  113,  114,  195,  143,
+ /*  1610 */   119,  136,   60,  195,   22,  195,  141,  195,  218,  219,
+ /*  1620 */   195,   23,  195,   25,   72,   23,  131,   25,  195,  134,
+ /*  1630 */    23,  218,  219,  195,   82,  144,  218,  219,  218,  219,
+ /*  1640 */   218,  219,  195,  218,  219,  218,  219,   60,   23,  195,
+ /*  1650 */    25,  218,  219,  101,  195,  117,  218,  219,  195,  107,
+ /*  1660 */   108,   23,  195,   25,  195,  218,  219,  115,  228,  117,
+ /*  1670 */   118,  119,  218,  219,  122,  195,   19,  218,  219,  195,
+ /*  1680 */    60,  218,  219,  142,  195,  218,  219,   19,   20,  195,
+ /*  1690 */    22,  139,  140,   23,   23,   25,   25,  195,  218,  219,
+ /*  1700 */     7,    8,  218,  219,   36,  118,  154,  155,  156,  157,
+ /*  1710 */   158,  195,   23,  195,   25,   84,   85,   49,  195,   23,
+ /*  1720 */   195,   25,  195,   23,  195,   25,  195,   23,   60,   25,
+ /*  1730 */    23,   23,   25,   25,  142,  183,  218,  219,  118,  195,
+ /*  1740 */    72,  218,  219,  218,  219,  218,  219,  218,  219,  218,
+ /*  1750 */   219,  195,  195,  146,   86,   98,   23,  195,   25,   91,
+ /*  1760 */    19,   20,  154,   22,  156,  154,   23,  156,   25,  101,
+ /*  1770 */    23,  195,   25,  195,  195,  107,  108,   36,  195,  195,
+ /*  1780 */   195,  195,  228,  115,  195,  117,  118,  119,  195,  195,
+ /*  1790 */   122,  261,  195,  321,  195,  195,  195,  258,  238,  195,
+ /*  1800 */   195,   60,  299,  291,  195,  195,  258,  195,  195,  195,
+ /*  1810 */   290,  244,  216,   72,  245,  193,  258,  258,  299,  258,
+ /*  1820 */   299,  274,  154,  155,  156,  157,  158,   86,  247,  295,
+ /*  1830 */   248,  295,   91,   19,   20,  270,   22,  274,  270,  248,
+ /*  1840 */   274,  222,  101,  227,  274,  221,  231,  221,  107,  108,
+ /*  1850 */    36,  183,  262,  247,  221,  283,  115,  262,  117,  118,
+ /*  1860 */   119,  198,  116,  122,  220,  262,   61,  220,  220,  251,
+ /*  1870 */   247,  142,  251,  245,   60,  202,  299,  202,   38,  262,
+ /*  1880 */   202,   22,  152,  151,  296,   43,   72,  236,   18,  239,
+ /*  1890 */   202,  239,  239,  239,   18,  154,  155,  156,  157,  158,
+ /*  1900 */    86,  150,  201,  248,  275,   91,  248,  273,  236,  248,
+ /*  1910 */   275,  275,  273,  236,  248,  101,  286,  202,  201,  159,
+ /*  1920 */    63,  107,  108,  296,  183,  293,  202,  201,   22,  115,
+ /*  1930 */   202,  117,  118,  119,  292,  223,  122,  201,   65,  202,
+ /*  1940 */   201,  223,  220,  220,   22,  220,  226,  226,  229,  127,
+ /*  1950 */   223,  220,  166,   24,  285,  220,  222,  114,  315,  285,
+ /*  1960 */   220,  202,  220,  307,   92,  320,  320,  229,  154,  155,
+ /*  1970 */   156,  157,  158,    0,    1,    2,  223,   83,    5,  268,
+ /*  1980 */   149,  268,  146,   10,   11,   12,   13,   14,   22,  280,
+ /*  1990 */    17,  202,  159,   19,   20,  251,   22,  183,  282,  148,
+ /*  2000 */   252,  252,  250,   30,  249,   32,  248,  147,   25,   13,
+ /*  2010 */    36,  204,  196,   40,  196,    6,  302,  194,  194,  194,
+ /*  2020 */   209,  215,  209,  215,  215,  215,  224,  224,  216,  209,
+ /*  2030 */     4,  216,  215,    3,   60,   22,  122,   19,  122,   19,
+ /*  2040 */   125,   22,   15,   22,   71,   16,   72,   23,   23,  140,
+ /*  2050 */   305,  152,   79,   25,  131,   82,  143,   20,   16,  305,
+ /*  2060 */     1,  143,  145,  131,  131,   62,   54,  131,   37,   54,
+ /*  2070 */    54,  152,   99,  117,   34,  101,   54,   24,    1,    5,
+ /*  2080 */    22,  107,  108,  116,   76,   25,  162,   41,  142,  115,
+ /*  2090 */    24,  117,  118,  119,  116,   20,  122,   19,  126,   23,
+ /*  2100 */   132,   19,   20,   69,   22,   69,   22,  134,   22,   68,
+ /*  2110 */    22,   22,  139,  140,   60,  141,   68,   24,   36,   28,
+ /*  2120 */    97,   22,   37,   68,   23,  150,   34,   22,  154,  155,
+ /*  2130 */   156,  157,  158,   23,   23,   22,  163,   25,   23,  142,
+ /*  2140 */    23,   98,   60,   23,   22,  144,   25,   76,   34,  117,
+ /*  2150 */    34,   89,   34,   34,   72,   87,   76,  183,   34,   94,
+ /*  2160 */    34,   23,   22,   24,   34,   23,   25,   44,   25,   23,
+ /*  2170 */    23,   23,   22,   22,   25,   11,  143,   25,  143,   23,
+ /*  2180 */    22,   22,   22,  101,   23,   23,  136,   22,   25,  107,
+ /*  2190 */   108,  142,   25,  142,  142,   23,   15,  115,    1,  117,
+ /*  2200 */   118,  119,    1,    2,  122,    1,    5,  322,  322,  322,
+ /*  2210 */   322,   10,   11,   12,   13,   14,  322,  322,   17,  322,
+ /*  2220 */     5,  322,  322,  141,  322,   10,   11,   12,   13,   14,
+ /*  2230 */   322,   30,   17,   32,  322,  322,  154,  155,  156,  157,
+ /*  2240 */   158,   40,  322,  322,  322,   30,  322,   32,  322,  322,
+ /*  2250 */   322,  322,  322,  322,  322,   40,  322,  322,  322,  322,
+ /*  2260 */   322,  322,  322,  322,  322,  183,  322,  322,  322,  322,
+ /*  2270 */   322,  322,   71,  322,  322,  322,  322,  322,  322,  322,
+ /*  2280 */    79,  322,  322,   82,  322,  322,   71,  322,  322,  322,
+ /*  2290 */   322,  322,  322,  322,   79,  322,  322,   82,  322,  322,
+ /*  2300 */    99,  322,  322,  322,  322,  322,  322,  322,  322,  322,
+ /*  2310 */   322,  322,  322,  322,   99,  322,  322,  322,  322,  322,
+ /*  2320 */   322,  322,  322,  322,  322,  322,  322,  322,  322,  322,
+ /*  2330 */   322,  322,  322,  322,  322,  134,  322,  322,  322,  322,
+ /*  2340 */   139,  140,  322,  322,  322,  322,  322,  322,  322,  134,
+ /*  2350 */   322,  322,  322,  322,  139,  140,  322,  322,  322,  322,
+ /*  2360 */   322,  322,  322,  322,  163,  322,  322,  322,  322,  322,
+ /*  2370 */   322,  322,  322,  322,  322,  322,  322,  322,  163,  322,
+ /*  2380 */   322,  322,  322,  322,  322,  322,  322,  322,  322,  322,
+ /*  2390 */   322,  322,  322,  322,  322,  322,  322,  322,  322,  322,
+ /*  2400 */   322,  322,  322,  322,  322,  322,  322,  322,  187,  187,
+ /*  2410 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2420 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2430 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2440 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2450 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2460 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2470 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2480 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2490 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2500 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2510 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2520 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2530 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2540 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2550 */   187,  187,  187,  187,  187,  187,  187,  187,  187,  187,
+ /*  2560 */   187,  187,  187,  187,  187,  187,
 };
-#define YY_SHIFT_COUNT    (582)
+#define YY_SHIFT_COUNT    (599)
 #define YY_SHIFT_MIN      (0)
-#define YY_SHIFT_MAX      (2152)
+#define YY_SHIFT_MAX      (2215)
 static const unsigned short int yy_shift_ofst[] = {
- /*     0 */  2029, 1801, 2043, 1380, 1380,  318,  271, 1496, 1569, 1642,
- /*    10 */   702,  702,  702,  740,  318,  318,  318,  318,  318,    0,
- /*    20 */     0,  216, 1177,  702,  702,  702,  702,  702,  702,  702,
- /*    30 */   702,  702,  702,  702,  702,  702,  702,  702,  503,  503,
- /*    40 */   111,  111,  217,  287,  348,  610,  610,  736,  736,  736,
- /*    50 */   736,   40,  112,  320,  340,  445,  489,  593,  637,  741,
- /*    60 */   785,  889,  909, 1023, 1043, 1157, 1177, 1177, 1177, 1177,
- /*    70 */  1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177,
- /*    80 */  1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321,  554,
- /*    90 */  1802, 1910,  702,  702,  702,  702,  702,  702,  702,  702,
- /*   100 */   702,  702,  702,  702,  702,  702,  702,  702,  702,  702,
- /*   110 */   702,  702,  702,  702,  702,  702,  702,  702,  702,  702,
- /*   120 */   702,  702,  702,  702,  702,  702,  702,  702,  702,  702,
- /*   130 */   702,  702,  702,  702,  702,  702,  702,  702,  702,  702,
- /*   140 */   702,  702,  138,  198,  198,  198,  198,  198,  198,  198,
- /*   150 */   183,   99,  169,  549,  610,  151,  542,  610,  610, 1017,
- /*   160 */  1017,  610, 1001,  350,  464,  464,  464,  586,    1,    1,
- /*   170 */  2207, 2207,  854,  854,  854,  465,  694,  694,  694,  694,
- /*   180 */  1096, 1096,  825,  549,  847,  904,  610,  610,  610,  610,
- /*   190 */   610,  610,  610,  610,  610,  610,  610,  610,  610,  610,
- /*   200 */   610,  610,  610,  610,  610,  488,  947,  947,  610, 1129,
- /*   210 */   495,  495, 1139, 1139,  967,  967, 1173, 2207, 2207, 2207,
- /*   220 */  2207, 2207, 2207, 2207,  617,  765,  765,  697,  444,  708,
- /*   230 */   660,  745,  510,  663,  864,  610,  610,  610,  610,  610,
- /*   240 */   610,  610,  610,  610,  610,  188,  610,  610,  610,  610,
- /*   250 */   610,  610,  610,  610,  610,  610,  610,  610,  839,  839,
- /*   260 */   839,  610,  610,  610, 1155,  610,  610,  610, 1119, 1247,
- /*   270 */   610, 1353,  610,  610,  610,  610,  610,  610,  610,  610,
- /*   280 */  1063,  494, 1101,  291,  291,  291,  291, 1319, 1101, 1101,
- /*   290 */   775, 1221, 1375, 1452,  667, 1341, 1198, 1341, 1435, 1487,
- /*   300 */   667,  667, 1487,  667, 1198, 1435,  777, 1011, 1423,  584,
- /*   310 */   584,  584, 1273, 1273, 1273, 1273, 1471, 1471,  880, 1530,
- /*   320 */  1190, 1095, 1731, 1731, 1668, 1668, 1794, 1794, 1668, 1683,
- /*   330 */  1685, 1815, 1796, 1824, 1824, 1824, 1824, 1668, 1828, 1701,
- /*   340 */  1685, 1685, 1701, 1815, 1796, 1701, 1796, 1701, 1668, 1828,
- /*   350 */  1697, 1800, 1668, 1828, 1848, 1668, 1828, 1668, 1828, 1848,
- /*   360 */  1766, 1766, 1766, 1823, 1870, 1870, 1848, 1766, 1767, 1766,
- /*   370 */  1823, 1766, 1766, 1727, 1872, 1783, 1783, 1848, 1668, 1813,
- /*   380 */  1813, 1825, 1825, 1777, 1781, 1906, 1668, 1774, 1777, 1789,
- /*   390 */  1792, 1701, 1919, 1935, 1935, 1949, 1949, 1949, 2207, 2207,
- /*   400 */  2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
- /*   410 */  2207, 2207, 2207,   69, 1032,   79,  357, 1377, 1206,  400,
- /*   420 */  1525,  835,  332, 1540, 1437, 1539, 1536, 1548, 1583, 1620,
- /*   430 */  1633, 1670, 1671, 1674, 1567, 1553, 1682, 1506, 1675, 1358,
- /*   440 */  1607, 1589, 1678, 1681, 1624, 1687, 1688, 1283, 1561, 1693,
- /*   450 */  1696, 1623, 1521, 1976, 1980, 1962, 1822, 1972, 1973, 1965,
- /*   460 */  1967, 1851, 1840, 1862, 1969, 1969, 1971, 1853, 1977, 1854,
- /*   470 */  1982, 1999, 1858, 1871, 1969, 1873, 1941, 1968, 1969, 1855,
- /*   480 */  1952, 1954, 1955, 1956, 1881, 1896, 1981, 1874, 2013, 2014,
- /*   490 */  1998, 1905, 1860, 1957, 2008, 1966, 1947, 1983, 1894, 1921,
- /*   500 */  2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047,
- /*   510 */  2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057,
- /*   520 */  2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1950,
- /*   530 */  2071, 2072, 1985, 2062, 2075, 1959, 2073, 2067, 2070, 2076,
- /*   540 */  2078, 2010, 2030, 2022, 2069, 2031, 2021, 2082, 2094, 2083,
- /*   550 */  2095, 2093, 2096, 2086, 1986, 1987, 2100, 2073, 2101, 2103,
- /*   560 */  2104, 2109, 2107, 2108, 2111, 2113, 2125, 2115, 2116, 2117,
- /*   570 */  2118, 2121, 2122, 2114, 2009, 2004, 2005, 2006, 2124, 2127,
- /*   580 */  2136, 2151, 2152,
+ /*     0 */  2201, 1973, 2215, 1552, 1552,   33,  368, 1668, 1741, 1814,
+ /*    10 */   726,  726,  726,  265,   33,   33,   33,   33,   33,    0,
+ /*    20 */     0,  216, 1349,  726,  726,  726,  726,  726,  726,  726,
+ /*    30 */   726,  726,  726,  726,  726,  726,  726,  726,  272,  272,
+ /*    40 */   111,  111,  316,  365,  516,  867,  867,  916,  916,  916,
+ /*    50 */   916,   40,  112,  260,  364,  408,  512,  617,  661,  765,
+ /*    60 */   809,  913,  957, 1061, 1081, 1195, 1215, 1329, 1349, 1349,
+ /*    70 */  1349, 1349, 1349, 1349, 1349, 1349, 1349, 1349, 1349, 1349,
+ /*    80 */  1349, 1349, 1349, 1349, 1349, 1349, 1369, 1349, 1473, 1493,
+ /*    90 */  1493,  473, 1974, 2082,  726,  726,  726,  726,  726,  726,
+ /*   100 */   726,  726,  726,  726,  726,  726,  726,  726,  726,  726,
+ /*   110 */   726,  726,  726,  726,  726,  726,  726,  726,  726,  726,
+ /*   120 */   726,  726,  726,  726,  726,  726,  726,  726,  726,  726,
+ /*   130 */   726,  726,  726,  726,  726,  726,  726,  726,  726,  726,
+ /*   140 */   726,  726,  726,  726,  726,  726,  138,  232,  232,  232,
+ /*   150 */   232,  232,  232,  232,  188,   99,  242,  718,  416, 1159,
+ /*   160 */   867,  867,  940,  940,  867, 1103,  417,  574,  574,  574,
+ /*   170 */   611,  139,  139, 2379, 2379, 1026, 1026, 1026,  536,  466,
+ /*   180 */   466,  466,  466, 1017, 1017,  849,  718,  971, 1060,  867,
+ /*   190 */   867,  867,  867,  867,  867,  867,  867,  867,  867,  867,
+ /*   200 */   867,  867,  867,  867,  867,  867,  867,  867,  261,  712,
+ /*   210 */   712,  867,  108, 1142, 1142,  977, 1108, 1108,  977,  977,
+ /*   220 */  1243, 2379, 2379, 2379, 2379, 2379, 2379, 2379,  641,  789,
+ /*   230 */   789,  635,  366,  721,  673,  782,  494,  787,  829,  867,
+ /*   240 */   867,  867,  867,  867,  867,  867,  867,  867,  867,  867,
+ /*   250 */   959,  867,  867,  867,  867,  867,  867,  867,  867,  867,
+ /*   260 */   867,  867,  867,  867,  867,  820,  820,  820,  867,  867,
+ /*   270 */   867, 1136,  867,  867,  867, 1119, 1007,  867, 1169,  867,
+ /*   280 */   867,  867,  867,  867,  867,  867,  867, 1225, 1153,  869,
+ /*   290 */   196,  618,  618,  618,  618, 1491,  196,  196,   91,  339,
+ /*   300 */  1326, 1386,  383, 1163, 1364, 1426, 1364, 1538,  903, 1163,
+ /*   310 */  1163,  903, 1163, 1426, 1538, 1018, 1535, 1241, 1528, 1528,
+ /*   320 */  1528, 1394, 1394, 1394, 1394,  762,  762, 1403, 1466, 1475,
+ /*   330 */  1551, 1746, 1805, 1746, 1746, 1729, 1729, 1840, 1840, 1729,
+ /*   340 */  1730, 1732, 1859, 1842, 1870, 1870, 1870, 1870, 1729, 1876,
+ /*   350 */  1751, 1732, 1732, 1751, 1859, 1842, 1751, 1842, 1751, 1729,
+ /*   360 */  1876, 1760, 1857, 1729, 1876, 1906, 1729, 1876, 1729, 1876,
+ /*   370 */  1906, 1746, 1746, 1746, 1873, 1922, 1922, 1906, 1746, 1822,
+ /*   380 */  1746, 1873, 1746, 1746, 1786, 1929, 1843, 1843, 1906, 1729,
+ /*   390 */  1872, 1872, 1894, 1894, 1831, 1836, 1966, 1729, 1833, 1831,
+ /*   400 */  1851, 1860, 1751, 1983, 1996, 1996, 2009, 2009, 2009, 2379,
+ /*   410 */  2379, 2379, 2379, 2379, 2379, 2379, 2379, 2379, 2379, 2379,
+ /*   420 */  2379, 2379, 2379, 2379,  136, 1063, 1196,  530,  636, 1274,
+ /*   430 */  1300, 1443, 1598, 1495, 1479,  967, 1083, 1602,  463, 1625,
+ /*   440 */  1638, 1670, 1541, 1671, 1689, 1696, 1277, 1432, 1693,  808,
+ /*   450 */  1700, 1607, 1657, 1587, 1704, 1707, 1631, 1708, 1733, 1608,
+ /*   460 */  1611, 1743, 1747, 1620, 1592, 2026, 2030, 2013, 1914, 2018,
+ /*   470 */  1916, 2020, 2019, 2021, 1915, 2027, 2029, 2024, 2025, 1909,
+ /*   480 */  1899, 1923, 2028, 2028, 1913, 2037, 1917, 2042, 2059, 1918,
+ /*   490 */  1932, 2028, 1933, 2003, 2031, 2028, 1919, 2012, 2015, 2016,
+ /*   500 */  2022, 1936, 1956, 2040, 2053, 2077, 2074, 2058, 1967, 1924,
+ /*   510 */  2034, 2060, 2036, 2008, 2046, 1946, 1978, 2066, 2075, 2078,
+ /*   520 */  1968, 1972, 2084, 2041, 2086, 2088, 2076, 2089, 2048, 2054,
+ /*   530 */  2093, 2023, 2091, 2099, 2055, 2085, 2101, 2092, 1975, 2105,
+ /*   540 */  2110, 2111, 2112, 2115, 2113, 2043, 1997, 2117, 2120, 2032,
+ /*   550 */  2114, 2122, 2001, 2121, 2116, 2118, 2119, 2124, 2062, 2071,
+ /*   560 */  2068, 2123, 2080, 2065, 2126, 2138, 2140, 2139, 2141, 2143,
+ /*   570 */  2130, 2033, 2035, 2142, 2121, 2146, 2147, 2148, 2150, 2149,
+ /*   580 */  2152, 2156, 2151, 2164, 2158, 2159, 2161, 2162, 2160, 2165,
+ /*   590 */  2163, 2050, 2049, 2051, 2052, 2167, 2172, 2181, 2197, 2204,
 };
-#define YY_REDUCE_COUNT (412)
-#define YY_REDUCE_MIN   (-277)
-#define YY_REDUCE_MAX   (1772)
+#define YY_REDUCE_COUNT (423)
+#define YY_REDUCE_MIN   (-303)
+#define YY_REDUCE_MAX   (1825)
 static const short yy_reduce_ofst[] = {
- /*     0 */   -67, 1252,  -64, -178, -181,  160, 1071,  143, -184,  137,
- /*    10 */   218,  220,  222, -174,  229,  268,  272,  275,  324, -208,
- /*    20 */   242, -277,  -39,   81,  537,  792,  810,  812, -189,  814,
- /*    30 */   831,  163,  865,  944,  887,  840,  964, 1077, -187,  292,
- /*    40 */  -133,  274,  673,  558,  682,  795,  809, -238, -232, -238,
- /*    50 */  -232,  329,  329,  329,  329,  329,  329,  329,  329,  329,
- /*    60 */   329,  329,  329,  329,  329,  329,  329,  329,  329,  329,
- /*    70 */   329,  329,  329,  329,  329,  329,  329,  329,  329,  329,
- /*    80 */   329,  329,  329,  329,  329,  329,  329,  329,  329,  557,
- /*    90 */   712,  949,  966,  969,  971,  979, 1097, 1099, 1103, 1142,
- /*   100 */  1144, 1169, 1172, 1201, 1203, 1228, 1241, 1250, 1253, 1255,
- /*   110 */  1261, 1266, 1271, 1282, 1291, 1308, 1310, 1312, 1322, 1328,
- /*   120 */  1347, 1354, 1356, 1359, 1362, 1365, 1367, 1374, 1376, 1381,
- /*   130 */  1401, 1403, 1406, 1412, 1414, 1417, 1421, 1428, 1447, 1449,
- /*   140 */  1453, 1462,  329,  329,  329,  329,  329,  329,  329,  329,
- /*   150 */   329,  329,  329,  -22, -159,  475, -220,  756,   38,  501,
- /*   160 */   841,  714,  329,  118,  337,  349,  363,  -56,  329,  329,
- /*   170 */   329,  329, -205, -205, -205,  687, -172, -130,  -57,  790,
- /*   180 */   397,  528, -271,  136,  596,  596,   90,  316,  522,  541,
- /*   190 */   -37,  715,  849,  977,  628,  856,  980,  991, 1081, 1102,
- /*   200 */  1135, 1083, -162,  208, 1258,  794,  -86,  159,   41, 1109,
- /*   210 */   671,  852,  844,  932, 1175, 1254,  480, 1180,  100,  258,
- /*   220 */  1265, 1268, 1216, 1287, -139,  317,  344,   63,  339,  423,
- /*   230 */   563,  636,  676,  813,  908,  914,  950, 1078, 1084, 1098,
- /*   240 */  1363, 1384, 1407, 1439, 1464,  411, 1527, 1534, 1535, 1537,
- /*   250 */  1541, 1542, 1543, 1544, 1545, 1547, 1549, 1550,  990, 1164,
- /*   260 */  1492, 1551, 1552, 1556, 1217, 1558, 1559, 1560, 1473, 1413,
- /*   270 */  1563, 1510, 1568,  563, 1570, 1571, 1572, 1573, 1574, 1575,
- /*   280 */  1443, 1466, 1518, 1513, 1514, 1515, 1516, 1217, 1518, 1518,
- /*   290 */  1531, 1562, 1582, 1477, 1505, 1511, 1533, 1512, 1488, 1538,
- /*   300 */  1509, 1517, 1546, 1519, 1557, 1489, 1565, 1564, 1578, 1586,
- /*   310 */  1587, 1588, 1526, 1528, 1554, 1555, 1576, 1577, 1566, 1579,
- /*   320 */  1584, 1591, 1520, 1523, 1617, 1628, 1580, 1581, 1632, 1585,
- /*   330 */  1590, 1593, 1604, 1605, 1606, 1608, 1609, 1641, 1649, 1610,
- /*   340 */  1592, 1594, 1611, 1595, 1616, 1612, 1618, 1613, 1651, 1654,
- /*   350 */  1596, 1598, 1655, 1663, 1650, 1673, 1680, 1677, 1684, 1653,
- /*   360 */  1664, 1666, 1667, 1662, 1669, 1672, 1676, 1686, 1679, 1691,
- /*   370 */  1689, 1692, 1694, 1597, 1599, 1619, 1630, 1699, 1700, 1602,
- /*   380 */  1615, 1648, 1657, 1690, 1698, 1658, 1729, 1652, 1695, 1702,
- /*   390 */  1704, 1703, 1741, 1754, 1758, 1768, 1769, 1771, 1660, 1661,
- /*   400 */  1665, 1752, 1756, 1757, 1759, 1760, 1764, 1745, 1753, 1762,
- /*   410 */  1763, 1761, 1772,
+ /*     0 */   -67,  345,  -64, -178, -181,  143,  435,  -78, -183,  163,
+ /*    10 */  -185,  284,  384, -174,  189,  352,  440,  444,  493,  -23,
+ /*    20 */   227, -277,   -1,  305,  561,  755,  759,  764, -189,  839,
+ /*    30 */   857,  354,  484,  859,  631,   67,  734,  780, -187,  616,
+ /*    40 */   581,  730,  891,  449,  588,  795,  836, -238,  287, -238,
+ /*    50 */   287, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /*    60 */  -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /*    70 */  -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /*    80 */  -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /*    90 */  -256,  205,  582,  715,  958,  985, 1003, 1005, 1010, 1012,
+ /*   100 */  1059, 1066, 1092, 1094, 1097, 1122, 1137, 1141, 1143, 1147,
+ /*   110 */  1151, 1172, 1249, 1251, 1269, 1271, 1276, 1290, 1316, 1318,
+ /*   120 */  1337, 1371, 1373, 1375, 1400, 1413, 1418, 1420, 1422, 1425,
+ /*   130 */  1427, 1433, 1438, 1447, 1454, 1459, 1463, 1467, 1480, 1484,
+ /*   140 */  1518, 1523, 1525, 1527, 1529, 1531, -256, -256, -256, -256,
+ /*   150 */  -256, -256, -256, -256, -256, -256, -256,  155,  210, -220,
+ /*   160 */    86, -130,  943,  996,  402, -256, -113,  981, 1095, 1135,
+ /*   170 */   395, -256, -256, -256, -256,  568,  568,  568,   -4, -153,
+ /*   180 */  -133,  259,  306, -166,  523, -303, -126,  503,  503,  -37,
+ /*   190 */  -149,  164,  690,  292,  412,  492,  651,  784,  332,  786,
+ /*   200 */   841, 1149,  833, 1236,  792,  162,  796, 1253,  777,  288,
+ /*   210 */   381,  380,  709,  487, 1027,  972, 1030, 1084,  991, 1120,
+ /*   220 */  -152, 1062,  692, 1240, 1247, 1250, 1239, 1306, -207, -194,
+ /*   230 */    57,  180,   74,  315,  355,  376,  452,  488,  630,  693,
+ /*   240 */   965, 1004, 1025, 1099, 1154, 1289, 1305, 1310, 1469, 1489,
+ /*   250 */   984, 1494, 1502, 1516, 1544, 1556, 1557, 1562, 1576, 1578,
+ /*   260 */  1579, 1583, 1584, 1585, 1586, 1217, 1440, 1554, 1589, 1593,
+ /*   270 */  1594, 1530, 1597, 1599, 1600, 1539, 1472, 1601, 1560, 1604,
+ /*   280 */   355, 1605, 1609, 1610, 1612, 1613, 1614, 1503, 1512, 1520,
+ /*   290 */  1567, 1548, 1558, 1559, 1561, 1530, 1567, 1567, 1569, 1596,
+ /*   300 */  1622, 1519, 1521, 1547, 1565, 1581, 1568, 1534, 1582, 1563,
+ /*   310 */  1566, 1591, 1570, 1606, 1536, 1619, 1615, 1616, 1624, 1626,
+ /*   320 */  1633, 1590, 1595, 1603, 1617, 1618, 1621, 1572, 1623, 1628,
+ /*   330 */  1663, 1644, 1577, 1647, 1648, 1673, 1675, 1588, 1627, 1678,
+ /*   340 */  1630, 1629, 1634, 1651, 1650, 1652, 1653, 1654, 1688, 1701,
+ /*   350 */  1655, 1635, 1636, 1658, 1639, 1672, 1661, 1677, 1666, 1715,
+ /*   360 */  1717, 1632, 1642, 1724, 1726, 1712, 1728, 1736, 1737, 1739,
+ /*   370 */  1718, 1722, 1723, 1725, 1719, 1720, 1721, 1727, 1731, 1734,
+ /*   380 */  1735, 1738, 1740, 1742, 1643, 1656, 1669, 1674, 1753, 1759,
+ /*   390 */  1645, 1646, 1711, 1713, 1748, 1744, 1709, 1789, 1716, 1749,
+ /*   400 */  1752, 1755, 1758, 1807, 1816, 1818, 1823, 1824, 1825, 1745,
+ /*   410 */  1754, 1714, 1811, 1806, 1808, 1809, 1810, 1813, 1802, 1803,
+ /*   420 */  1812, 1815, 1817, 1820,
 };
 static const YYACTIONTYPE yy_default[] = {
- /*     0 */  1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254,
- /*    10 */  1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397,
- /*    20 */  1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*    30 */  1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254,
- /*    40 */  1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254,
- /*    50 */  1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254,
- /*    60 */  1492, 1493, 1254, 1254, 1254, 1254, 1543, 1545, 1508, 1420,
- /*    70 */  1419, 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488,
- /*    80 */  1486, 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254,
- /*    90 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   100 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   110 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   120 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   130 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   140 */  1254, 1254, 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457,
- /*   150 */  1456, 1458, 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254,
- /*   160 */  1254, 1254, 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461,
- /*   170 */  1473, 1472, 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254,
- /*   180 */  1254, 1254, 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   190 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   200 */  1254, 1254, 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287,
- /*   210 */  1578, 1578, 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358,
- /*   220 */  1358, 1358, 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   230 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548,
- /*   240 */  1546, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   250 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   260 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254,
- /*   270 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608,
- /*   280 */  1254, 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342,
- /*   290 */  1357, 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410,
- /*   300 */  1423, 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397,
- /*   310 */  1397, 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364,
- /*   320 */  1357, 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509,
- /*   330 */  1638, 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410,
- /*   340 */  1638, 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272,
- /*   350 */  1525, 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499,
- /*   360 */  1330, 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330,
- /*   370 */  1319, 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588,
- /*   380 */  1588, 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403,
- /*   390 */  1401, 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660,
- /*   400 */  1558, 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288,
- /*   410 */  1288, 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618,
- /*   420 */  1254, 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   430 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   440 */  1564, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   450 */  1254, 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254,
- /*   460 */  1254, 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254,
- /*   470 */  1254, 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254,
- /*   480 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254,
- /*   490 */  1254, 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254,
- /*   500 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   510 */  1254, 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   520 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   530 */  1254, 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254,
- /*   540 */  1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   550 */  1254, 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254,
- /*   560 */  1254, 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
- /*   570 */  1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254,
- /*   580 */  1266, 1254, 1254,
+ /*     0 */  1691, 1691, 1691, 1516, 1279, 1392, 1279, 1279, 1279, 1279,
+ /*    10 */  1516, 1516, 1516, 1279, 1279, 1279, 1279, 1279, 1279, 1422,
+ /*    20 */  1422, 1568, 1312, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*    30 */  1279, 1279, 1279, 1279, 1279, 1515, 1279, 1279, 1279, 1279,
+ /*    40 */  1607, 1607, 1279, 1279, 1279, 1279, 1279, 1592, 1591, 1279,
+ /*    50 */  1279, 1279, 1431, 1279, 1279, 1279, 1438, 1279, 1279, 1279,
+ /*    60 */  1279, 1279, 1517, 1518, 1279, 1279, 1279, 1279, 1567, 1569,
+ /*    70 */  1533, 1445, 1444, 1443, 1442, 1551, 1410, 1436, 1429, 1433,
+ /*    80 */  1512, 1513, 1511, 1670, 1518, 1517, 1279, 1432, 1480, 1496,
+ /*    90 */  1479, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   100 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   110 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   120 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   130 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   140 */  1279, 1279, 1279, 1279, 1279, 1279, 1488, 1495, 1494, 1493,
+ /*   150 */  1502, 1492, 1489, 1482, 1481, 1483, 1484, 1303, 1300, 1354,
+ /*   160 */  1279, 1279, 1279, 1279, 1279, 1485, 1312, 1473, 1472, 1471,
+ /*   170 */  1279, 1499, 1486, 1498, 1497, 1575, 1644, 1643, 1534, 1279,
+ /*   180 */  1279, 1279, 1279, 1279, 1279, 1607, 1279, 1279, 1279, 1279,
+ /*   190 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   200 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1412, 1607,
+ /*   210 */  1607, 1279, 1312, 1607, 1607, 1308, 1413, 1413, 1308, 1308,
+ /*   220 */  1416, 1587, 1383, 1383, 1383, 1383, 1392, 1383, 1279, 1279,
+ /*   230 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   240 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1572, 1570, 1279,
+ /*   250 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   260 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   270 */  1279, 1279, 1279, 1279, 1279, 1388, 1279, 1279, 1279, 1279,
+ /*   280 */  1279, 1279, 1279, 1279, 1279, 1279, 1637, 1683, 1279, 1546,
+ /*   290 */  1368, 1388, 1388, 1388, 1388, 1390, 1369, 1367, 1382, 1313,
+ /*   300 */  1286, 1683, 1683, 1448, 1437, 1389, 1437, 1680, 1435, 1448,
+ /*   310 */  1448, 1435, 1448, 1389, 1680, 1329, 1659, 1324, 1422, 1422,
+ /*   320 */  1422, 1412, 1412, 1412, 1412, 1416, 1416, 1514, 1389, 1382,
+ /*   330 */  1279, 1355, 1683, 1355, 1355, 1398, 1398, 1682, 1682, 1398,
+ /*   340 */  1534, 1667, 1457, 1357, 1363, 1363, 1363, 1363, 1398, 1297,
+ /*   350 */  1435, 1667, 1667, 1435, 1457, 1357, 1435, 1357, 1435, 1398,
+ /*   360 */  1297, 1550, 1678, 1398, 1297, 1524, 1398, 1297, 1398, 1297,
+ /*   370 */  1524, 1355, 1355, 1355, 1344, 1279, 1279, 1524, 1355, 1329,
+ /*   380 */  1355, 1344, 1355, 1355, 1625, 1279, 1528, 1528, 1524, 1398,
+ /*   390 */  1617, 1617, 1425, 1425, 1430, 1416, 1519, 1398, 1279, 1430,
+ /*   400 */  1428, 1426, 1435, 1347, 1640, 1640, 1636, 1636, 1636, 1688,
+ /*   410 */  1688, 1587, 1652, 1312, 1312, 1312, 1312, 1652, 1331, 1331,
+ /*   420 */  1313, 1313, 1312, 1652, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   430 */  1279, 1647, 1279, 1279, 1535, 1279, 1279, 1279, 1279, 1279,
+ /*   440 */  1279, 1279, 1402, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   450 */  1279, 1279, 1593, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   460 */  1279, 1279, 1279, 1279, 1462, 1279, 1282, 1584, 1279, 1279,
+ /*   470 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   480 */  1279, 1279, 1439, 1440, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   490 */  1279, 1454, 1279, 1279, 1279, 1449, 1279, 1279, 1279, 1279,
+ /*   500 */  1279, 1279, 1279, 1279, 1403, 1279, 1279, 1279, 1279, 1279,
+ /*   510 */  1279, 1549, 1548, 1279, 1279, 1400, 1279, 1279, 1279, 1279,
+ /*   520 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1327,
+ /*   530 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   540 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   550 */  1279, 1279, 1279, 1427, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   560 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1622, 1417,
+ /*   570 */  1279, 1279, 1279, 1279, 1671, 1279, 1279, 1279, 1279, 1377,
+ /*   580 */  1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279,
+ /*   590 */  1663, 1371, 1463, 1279, 1466, 1301, 1279, 1291, 1279, 1279,
 };
 /********** End of lemon-generated parsing tables *****************************/
 
@@ -179033,7 +181332,7 @@ static char *yyTracePrompt = 0;
 ** Outputs:
 ** None.
 */
-SQLITE_PRIVATE void SQLITE_APICALL sqlite3ParserTrace(FILE *TraceFILE, char *zTracePrompt){
+SQLITE_PRIVATE void sqlite3ParserTrace(FILE *TraceFILE, char *zTracePrompt){
   yyTraceFILE = TraceFILE;
   yyTracePrompt = zTracePrompt;
   if( yyTraceFILE==0 ) yyTracePrompt = 0;
@@ -179340,34 +181639,33 @@ static const char *const yyTokenName[] = {
   /*  292 */ "foreach_clause",
   /*  293 */ "when_clause",
   /*  294 */ "trigger_cmd",
-  /*  295 */ "trnm",
-  /*  296 */ "tridxby",
-  /*  297 */ "database_kw_opt",
-  /*  298 */ "key_opt",
-  /*  299 */ "add_column_fullname",
-  /*  300 */ "kwcolumn_opt",
-  /*  301 */ "create_vtab",
-  /*  302 */ "vtabarglist",
-  /*  303 */ "vtabarg",
-  /*  304 */ "vtabargtoken",
-  /*  305 */ "lp",
-  /*  306 */ "anylist",
-  /*  307 */ "wqitem",
-  /*  308 */ "wqas",
-  /*  309 */ "withnm",
-  /*  310 */ "windowdefn_list",
-  /*  311 */ "windowdefn",
-  /*  312 */ "window",
-  /*  313 */ "frame_opt",
-  /*  314 */ "part_opt",
-  /*  315 */ "filter_clause",
-  /*  316 */ "over_clause",
-  /*  317 */ "range_or_rows",
-  /*  318 */ "frame_bound",
-  /*  319 */ "frame_bound_s",
-  /*  320 */ "frame_bound_e",
-  /*  321 */ "frame_exclude_opt",
-  /*  322 */ "frame_exclude",
+  /*  295 */ "tridxby",
+  /*  296 */ "database_kw_opt",
+  /*  297 */ "key_opt",
+  /*  298 */ "alter_add",
+  /*  299 */ "kwcolumn_opt",
+  /*  300 */ "create_vtab",
+  /*  301 */ "vtabarglist",
+  /*  302 */ "vtabarg",
+  /*  303 */ "vtabargtoken",
+  /*  304 */ "lp",
+  /*  305 */ "anylist",
+  /*  306 */ "wqitem",
+  /*  307 */ "wqas",
+  /*  308 */ "withnm",
+  /*  309 */ "windowdefn_list",
+  /*  310 */ "windowdefn",
+  /*  311 */ "window",
+  /*  312 */ "frame_opt",
+  /*  313 */ "part_opt",
+  /*  314 */ "filter_clause",
+  /*  315 */ "over_clause",
+  /*  316 */ "range_or_rows",
+  /*  317 */ "frame_bound",
+  /*  318 */ "frame_bound_s",
+  /*  319 */ "frame_bound_e",
+  /*  320 */ "frame_exclude_opt",
+  /*  321 */ "frame_exclude",
 };
 #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
 
@@ -179497,8 +181795,8 @@ static const char *const yyRuleName[] = {
  /* 119 */ "fullname ::= nm DOT nm",
  /* 120 */ "xfullname ::= nm",
  /* 121 */ "xfullname ::= nm DOT nm",
- /* 122 */ "xfullname ::= nm DOT nm AS nm",
- /* 123 */ "xfullname ::= nm AS nm",
+ /* 122 */ "xfullname ::= nm AS nm",
+ /* 123 */ "xfullname ::= nm DOT nm AS nm",
  /* 124 */ "joinop ::= COMMA|JOIN",
  /* 125 */ "joinop ::= JOIN_KW JOIN",
  /* 126 */ "joinop ::= JOIN_KW nm JOIN",
@@ -179647,143 +181945,146 @@ static const char *const yyRuleName[] = {
  /* 269 */ "when_clause ::= WHEN expr",
  /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
  /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 272 */ "trnm ::= nm DOT nm",
- /* 273 */ "tridxby ::= INDEXED BY nm",
- /* 274 */ "tridxby ::= NOT INDEXED",
- /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
- /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 278 */ "trigger_cmd ::= scanpt select scanpt",
- /* 279 */ "expr ::= RAISE LP IGNORE RP",
- /* 280 */ "expr ::= RAISE LP raisetype COMMA expr RP",
- /* 281 */ "raisetype ::= ROLLBACK",
- /* 282 */ "raisetype ::= ABORT",
- /* 283 */ "raisetype ::= FAIL",
- /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 286 */ "cmd ::= DETACH database_kw_opt expr",
- /* 287 */ "key_opt ::=",
- /* 288 */ "key_opt ::= KEY expr",
- /* 289 */ "cmd ::= REINDEX",
- /* 290 */ "cmd ::= REINDEX nm dbnm",
- /* 291 */ "cmd ::= ANALYZE",
- /* 292 */ "cmd ::= ANALYZE nm dbnm",
- /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 272 */ "tridxby ::= INDEXED BY nm",
+ /* 273 */ "tridxby ::= NOT INDEXED",
+ /* 274 */ "trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt",
+ /* 275 */ "trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt",
+ /* 276 */ "trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt",
+ /* 277 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 278 */ "expr ::= RAISE LP IGNORE RP",
+ /* 279 */ "expr ::= RAISE LP raisetype COMMA expr RP",
+ /* 280 */ "raisetype ::= ROLLBACK",
+ /* 281 */ "raisetype ::= ABORT",
+ /* 282 */ "raisetype ::= FAIL",
+ /* 283 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 284 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 285 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 286 */ "key_opt ::=",
+ /* 287 */ "key_opt ::= KEY expr",
+ /* 288 */ "cmd ::= REINDEX",
+ /* 289 */ "cmd ::= REINDEX nm dbnm",
+ /* 290 */ "cmd ::= ANALYZE",
+ /* 291 */ "cmd ::= ANALYZE nm dbnm",
+ /* 292 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 293 */ "cmd ::= alter_add carglist",
+ /* 294 */ "alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken",
  /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
- /* 296 */ "add_column_fullname ::= fullname",
- /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 298 */ "cmd ::= create_vtab",
- /* 299 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 301 */ "vtabarg ::=",
- /* 302 */ "vtabargtoken ::= ANY",
- /* 303 */ "vtabargtoken ::= lp anylist RP",
- /* 304 */ "lp ::= LP",
- /* 305 */ "with ::= WITH wqlist",
- /* 306 */ "with ::= WITH RECURSIVE wqlist",
- /* 307 */ "wqas ::= AS",
- /* 308 */ "wqas ::= AS MATERIALIZED",
- /* 309 */ "wqas ::= AS NOT MATERIALIZED",
- /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP",
- /* 311 */ "withnm ::= nm",
- /* 312 */ "wqlist ::= wqitem",
- /* 313 */ "wqlist ::= wqlist COMMA wqitem",
- /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 315 */ "windowdefn ::= nm AS LP window RP",
- /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
- /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
- /* 318 */ "window ::= ORDER BY sortlist frame_opt",
- /* 319 */ "window ::= nm ORDER BY sortlist frame_opt",
- /* 320 */ "window ::= nm frame_opt",
- /* 321 */ "frame_opt ::=",
- /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
- /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
- /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
- /* 325 */ "frame_bound_s ::= frame_bound",
- /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 327 */ "frame_bound_e ::= frame_bound",
- /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
- /* 330 */ "frame_bound ::= CURRENT ROW",
- /* 331 */ "frame_exclude_opt ::=",
- /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
- /* 333 */ "frame_exclude ::= NO OTHERS",
- /* 334 */ "frame_exclude ::= CURRENT ROW",
- /* 335 */ "frame_exclude ::= GROUP|TIES",
- /* 336 */ "window_clause ::= WINDOW windowdefn_list",
- /* 337 */ "filter_over ::= filter_clause over_clause",
- /* 338 */ "filter_over ::= over_clause",
- /* 339 */ "filter_over ::= filter_clause",
- /* 340 */ "over_clause ::= OVER LP window RP",
- /* 341 */ "over_clause ::= OVER nm",
- /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP",
- /* 343 */ "term ::= QNUMBER",
- /* 344 */ "input ::= cmdlist",
- /* 345 */ "cmdlist ::= cmdlist ecmd",
- /* 346 */ "cmdlist ::= ecmd",
- /* 347 */ "ecmd ::= SEMI",
- /* 348 */ "ecmd ::= cmdx SEMI",
- /* 349 */ "ecmd ::= explain cmdx SEMI",
- /* 350 */ "trans_opt ::=",
- /* 351 */ "trans_opt ::= TRANSACTION",
- /* 352 */ "trans_opt ::= TRANSACTION nm",
- /* 353 */ "savepoint_opt ::= SAVEPOINT",
- /* 354 */ "savepoint_opt ::=",
- /* 355 */ "cmd ::= create_table create_table_args",
- /* 356 */ "table_option_set ::= table_option",
- /* 357 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 358 */ "columnlist ::= columnname carglist",
- /* 359 */ "nm ::= ID|INDEXED|JOIN_KW",
- /* 360 */ "nm ::= STRING",
- /* 361 */ "typetoken ::= typename",
- /* 362 */ "typename ::= ID|STRING",
- /* 363 */ "signed ::= plus_num",
- /* 364 */ "signed ::= minus_num",
- /* 365 */ "carglist ::= carglist ccons",
- /* 366 */ "carglist ::=",
- /* 367 */ "ccons ::= NULL onconf",
- /* 368 */ "ccons ::= GENERATED ALWAYS AS generated",
- /* 369 */ "ccons ::= AS generated",
- /* 370 */ "conslist_opt ::= COMMA conslist",
- /* 371 */ "conslist ::= conslist tconscomma tcons",
- /* 372 */ "conslist ::= tcons",
- /* 373 */ "tconscomma ::=",
- /* 374 */ "defer_subclause_opt ::= defer_subclause",
- /* 375 */ "resolvetype ::= raisetype",
- /* 376 */ "selectnowith ::= oneselect",
- /* 377 */ "oneselect ::= values",
- /* 378 */ "sclp ::= selcollist COMMA",
- /* 379 */ "as ::= ID|STRING",
- /* 380 */ "indexed_opt ::= indexed_by",
- /* 381 */ "returning ::=",
- /* 382 */ "expr ::= term",
- /* 383 */ "likeop ::= LIKE_KW|MATCH",
- /* 384 */ "case_operand ::= expr",
- /* 385 */ "exprlist ::= nexprlist",
- /* 386 */ "nmnum ::= plus_num",
- /* 387 */ "nmnum ::= nm",
- /* 388 */ "nmnum ::= ON",
- /* 389 */ "nmnum ::= DELETE",
- /* 390 */ "nmnum ::= DEFAULT",
- /* 391 */ "plus_num ::= INTEGER|FLOAT",
- /* 392 */ "foreach_clause ::=",
- /* 393 */ "foreach_clause ::= FOR EACH ROW",
- /* 394 */ "trnm ::= nm",
- /* 395 */ "tridxby ::=",
- /* 396 */ "database_kw_opt ::= DATABASE",
- /* 397 */ "database_kw_opt ::=",
- /* 398 */ "kwcolumn_opt ::=",
- /* 399 */ "kwcolumn_opt ::= COLUMNKW",
- /* 400 */ "vtabarglist ::= vtabarg",
- /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 402 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 403 */ "anylist ::=",
- /* 404 */ "anylist ::= anylist LP anylist RP",
- /* 405 */ "anylist ::= anylist ANY",
- /* 406 */ "with ::=",
- /* 407 */ "windowdefn_list ::= windowdefn",
- /* 408 */ "window ::= frame_opt",
+ /* 296 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 297 */ "cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm",
+ /* 298 */ "cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL",
+ /* 299 */ "cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf",
+ /* 300 */ "cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf",
+ /* 301 */ "cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf",
+ /* 302 */ "cmd ::= create_vtab",
+ /* 303 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 304 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 305 */ "vtabarg ::=",
+ /* 306 */ "vtabargtoken ::= ANY",
+ /* 307 */ "vtabargtoken ::= lp anylist RP",
+ /* 308 */ "lp ::= LP",
+ /* 309 */ "with ::= WITH wqlist",
+ /* 310 */ "with ::= WITH RECURSIVE wqlist",
+ /* 311 */ "wqas ::= AS",
+ /* 312 */ "wqas ::= AS MATERIALIZED",
+ /* 313 */ "wqas ::= AS NOT MATERIALIZED",
+ /* 314 */ "wqitem ::= withnm eidlist_opt wqas LP select RP",
+ /* 315 */ "withnm ::= nm",
+ /* 316 */ "wqlist ::= wqitem",
+ /* 317 */ "wqlist ::= wqlist COMMA wqitem",
+ /* 318 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 319 */ "windowdefn ::= nm AS LP window RP",
+ /* 320 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 321 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 322 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 323 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 324 */ "window ::= nm frame_opt",
+ /* 325 */ "frame_opt ::=",
+ /* 326 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 327 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 328 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 329 */ "frame_bound_s ::= frame_bound",
+ /* 330 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 331 */ "frame_bound_e ::= frame_bound",
+ /* 332 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 333 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 334 */ "frame_bound ::= CURRENT ROW",
+ /* 335 */ "frame_exclude_opt ::=",
+ /* 336 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 337 */ "frame_exclude ::= NO OTHERS",
+ /* 338 */ "frame_exclude ::= CURRENT ROW",
+ /* 339 */ "frame_exclude ::= GROUP|TIES",
+ /* 340 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 341 */ "filter_over ::= filter_clause over_clause",
+ /* 342 */ "filter_over ::= over_clause",
+ /* 343 */ "filter_over ::= filter_clause",
+ /* 344 */ "over_clause ::= OVER LP window RP",
+ /* 345 */ "over_clause ::= OVER nm",
+ /* 346 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 347 */ "term ::= QNUMBER",
+ /* 348 */ "input ::= cmdlist",
+ /* 349 */ "cmdlist ::= cmdlist ecmd",
+ /* 350 */ "cmdlist ::= ecmd",
+ /* 351 */ "ecmd ::= SEMI",
+ /* 352 */ "ecmd ::= cmdx SEMI",
+ /* 353 */ "ecmd ::= explain cmdx SEMI",
+ /* 354 */ "trans_opt ::=",
+ /* 355 */ "trans_opt ::= TRANSACTION",
+ /* 356 */ "trans_opt ::= TRANSACTION nm",
+ /* 357 */ "savepoint_opt ::= SAVEPOINT",
+ /* 358 */ "savepoint_opt ::=",
+ /* 359 */ "cmd ::= create_table create_table_args",
+ /* 360 */ "table_option_set ::= table_option",
+ /* 361 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 362 */ "columnlist ::= columnname carglist",
+ /* 363 */ "nm ::= ID|INDEXED|JOIN_KW",
+ /* 364 */ "nm ::= STRING",
+ /* 365 */ "typetoken ::= typename",
+ /* 366 */ "typename ::= ID|STRING",
+ /* 367 */ "signed ::= plus_num",
+ /* 368 */ "signed ::= minus_num",
+ /* 369 */ "carglist ::= carglist ccons",
+ /* 370 */ "carglist ::=",
+ /* 371 */ "ccons ::= NULL onconf",
+ /* 372 */ "ccons ::= GENERATED ALWAYS AS generated",
+ /* 373 */ "ccons ::= AS generated",
+ /* 374 */ "conslist_opt ::= COMMA conslist",
+ /* 375 */ "conslist ::= conslist tconscomma tcons",
+ /* 376 */ "conslist ::= tcons",
+ /* 377 */ "tconscomma ::=",
+ /* 378 */ "defer_subclause_opt ::= defer_subclause",
+ /* 379 */ "resolvetype ::= raisetype",
+ /* 380 */ "selectnowith ::= oneselect",
+ /* 381 */ "oneselect ::= values",
+ /* 382 */ "sclp ::= selcollist COMMA",
+ /* 383 */ "as ::= ID|STRING",
+ /* 384 */ "indexed_opt ::= indexed_by",
+ /* 385 */ "returning ::=",
+ /* 386 */ "expr ::= term",
+ /* 387 */ "likeop ::= LIKE_KW|MATCH",
+ /* 388 */ "case_operand ::= expr",
+ /* 389 */ "exprlist ::= nexprlist",
+ /* 390 */ "nmnum ::= plus_num",
+ /* 391 */ "nmnum ::= nm",
+ /* 392 */ "nmnum ::= ON",
+ /* 393 */ "nmnum ::= DELETE",
+ /* 394 */ "nmnum ::= DEFAULT",
+ /* 395 */ "plus_num ::= INTEGER|FLOAT",
+ /* 396 */ "foreach_clause ::=",
+ /* 397 */ "foreach_clause ::= FOR EACH ROW",
+ /* 398 */ "tridxby ::=",
+ /* 399 */ "database_kw_opt ::= DATABASE",
+ /* 400 */ "database_kw_opt ::=",
+ /* 401 */ "kwcolumn_opt ::=",
+ /* 402 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 403 */ "vtabarglist ::= vtabarg",
+ /* 404 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 405 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 406 */ "anylist ::=",
+ /* 407 */ "anylist ::= anylist LP anylist RP",
+ /* 408 */ "anylist ::= anylist ANY",
+ /* 409 */ "with ::=",
+ /* 410 */ "windowdefn_list ::= windowdefn",
+ /* 411 */ "window ::= frame_opt",
 };
 #endif /* NDEBUG */
 
@@ -179798,15 +182099,24 @@ static int SQLITE_APICALL yyGrowStack(yyParser *p){
   int newSize;
   int idx;
   yyStackEntry *pNew;
+#ifdef YYSIZELIMIT
+  int nLimit = YYSIZELIMIT(sqlite3ParserCTX(p));
+#endif
 
   newSize = oldSize*2 + 100;
+#ifdef YYSIZELIMIT
+  if( newSize>nLimit ){
+    newSize = nLimit;
+    if( newSize<=oldSize ) return 1;
+  }
+#endif
   idx = (int)(p->yytos - p->yystack);
   if( p->yystack==p->yystk0 ){
-    pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
+    pNew = YYREALLOC(0, newSize*sizeof(pNew[0]), sqlite3ParserCTX(p));
     if( pNew==0 ) return 1;
     memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
   }else{
-    pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
+    pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]), sqlite3ParserCTX(p));
     if( pNew==0 ) return 1;
   }
   p->yystack = pNew;
@@ -179913,7 +182223,7 @@ static void SQLITE_APICALL yy_destructor(
     case 254: /* values */
     case 256: /* mvalues */
 {
-sqlite3SelectDelete(pParse->db, (yypminor->yy637));
+sqlite3SelectDelete(pParse->db, (yypminor->yy555));
 }
       break;
     case 218: /* term */
@@ -179925,10 +182235,10 @@ sqlite3SelectDelete(pParse->db, (yypminor->yy637));
     case 283: /* case_else */
     case 286: /* vinto */
     case 293: /* when_clause */
-    case 298: /* key_opt */
-    case 315: /* filter_clause */
+    case 297: /* key_opt */
+    case 314: /* filter_clause */
 {
-sqlite3ExprDelete(pParse->db, (yypminor->yy590));
+sqlite3ExprDelete(pParse->db, (yypminor->yy454));
 }
       break;
     case 223: /* eidlist_opt */
@@ -179943,9 +182253,9 @@ sqlite3ExprDelete(pParse->db, (yypminor->yy590));
     case 271: /* setlist */
     case 280: /* paren_exprlist */
     case 282: /* case_exprlist */
-    case 314: /* part_opt */
+    case 313: /* part_opt */
 {
-sqlite3ExprListDelete(pParse->db, (yypminor->yy402));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy14));
 }
       break;
     case 240: /* fullname */
@@ -179954,51 +182264,51 @@ sqlite3ExprListDelete(pParse->db, (yypminor->yy402));
     case 260: /* stl_prefix */
     case 265: /* xfullname */
 {
-sqlite3SrcListDelete(pParse->db, (yypminor->yy563));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy203));
 }
       break;
     case 243: /* wqlist */
 {
-sqlite3WithDelete(pParse->db, (yypminor->yy125));
+sqlite3WithDelete(pParse->db, (yypminor->yy59));
 }
       break;
     case 253: /* window_clause */
-    case 310: /* windowdefn_list */
+    case 309: /* windowdefn_list */
 {
-sqlite3WindowListDelete(pParse->db, (yypminor->yy483));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy211));
 }
       break;
     case 266: /* idlist */
     case 273: /* idlist_opt */
 {
-sqlite3IdListDelete(pParse->db, (yypminor->yy204));
+sqlite3IdListDelete(pParse->db, (yypminor->yy132));
 }
       break;
     case 276: /* filter_over */
-    case 311: /* windowdefn */
-    case 312: /* window */
-    case 313: /* frame_opt */
-    case 316: /* over_clause */
+    case 310: /* windowdefn */
+    case 311: /* window */
+    case 312: /* frame_opt */
+    case 315: /* over_clause */
 {
-sqlite3WindowDelete(pParse->db, (yypminor->yy483));
+sqlite3WindowDelete(pParse->db, (yypminor->yy211));
 }
       break;
     case 289: /* trigger_cmd_list */
     case 294: /* trigger_cmd */
 {
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy319));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427));
 }
       break;
     case 291: /* trigger_event */
 {
-sqlite3IdListDelete(pParse->db, (yypminor->yy28).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy286).b);
 }
       break;
-    case 318: /* frame_bound */
-    case 319: /* frame_bound_s */
-    case 320: /* frame_bound_e */
+    case 317: /* frame_bound */
+    case 318: /* frame_bound_s */
+    case 319: /* frame_bound_e */
 {
-sqlite3ExprDelete(pParse->db, (yypminor->yy205).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr);
 }
       break;
 /********* End destructor definitions *****************************************/
@@ -180051,7 +182361,9 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3ParserFinalize(void *p){
   }
 
 #if YYGROWABLESTACK
-  if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
+  if( pParser->yystack!=pParser->yystk0 ){
+    YYFREE(pParser->yystack, sqlite3ParserCTX(pParser));
+  }
 #endif
 }
 
@@ -180234,7 +182546,7 @@ static void SQLITE_APICALL yyStackOverflow(yyParser *yypParser){
    ** stack every overflows */
 /******** Begin %stack_overflow code ******************************************/
 
-  sqlite3OomFault(pParse->db);
+  if( pParse->nErr==0 ) sqlite3ErrorMsg(pParse, "Recursion limit");
 /******** End %stack_overflow code ********************************************/
    sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */
    sqlite3ParserCTX_STORE
@@ -180244,7 +182556,7 @@ static void SQLITE_APICALL yyStackOverflow(yyParser *yypParser){
 ** Print tracing information for a SHIFT action
 */
 #ifndef NDEBUG
-static void SQLITE_APICALL yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
+static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
   if( yyTraceFILE ){
     if( yyNewState<YYNSTATE ){
       fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
@@ -180422,8 +182734,8 @@ static const YYCODETYPE yyRuleInfoLhs[] = {
    240,  /* (119) fullname ::= nm DOT nm */
    265,  /* (120) xfullname ::= nm */
    265,  /* (121) xfullname ::= nm DOT nm */
-   265,  /* (122) xfullname ::= nm DOT nm AS nm */
-   265,  /* (123) xfullname ::= nm AS nm */
+   265,  /* (122) xfullname ::= nm AS nm */
+   265,  /* (123) xfullname ::= nm DOT nm AS nm */
    261,  /* (124) joinop ::= COMMA|JOIN */
    261,  /* (125) joinop ::= JOIN_KW JOIN */
    261,  /* (126) joinop ::= JOIN_KW nm JOIN */
@@ -180572,143 +182884,146 @@ static const YYCODETYPE yyRuleInfoLhs[] = {
    293,  /* (269) when_clause ::= WHEN expr */
    289,  /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
    289,  /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
-   295,  /* (272) trnm ::= nm DOT nm */
-   296,  /* (273) tridxby ::= INDEXED BY nm */
-   296,  /* (274) tridxby ::= NOT INDEXED */
-   294,  /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-   294,  /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
-   294,  /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-   294,  /* (278) trigger_cmd ::= scanpt select scanpt */
-   219,  /* (279) expr ::= RAISE LP IGNORE RP */
-   219,  /* (280) expr ::= RAISE LP raisetype COMMA expr RP */
-   238,  /* (281) raisetype ::= ROLLBACK */
-   238,  /* (282) raisetype ::= ABORT */
-   238,  /* (283) raisetype ::= FAIL */
-   192,  /* (284) cmd ::= DROP TRIGGER ifexists fullname */
-   192,  /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
-   192,  /* (286) cmd ::= DETACH database_kw_opt expr */
-   298,  /* (287) key_opt ::= */
-   298,  /* (288) key_opt ::= KEY expr */
-   192,  /* (289) cmd ::= REINDEX */
-   192,  /* (290) cmd ::= REINDEX nm dbnm */
-   192,  /* (291) cmd ::= ANALYZE */
-   192,  /* (292) cmd ::= ANALYZE nm dbnm */
-   192,  /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
-   192,  /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+   295,  /* (272) tridxby ::= INDEXED BY nm */
+   295,  /* (273) tridxby ::= NOT INDEXED */
+   294,  /* (274) trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt */
+   294,  /* (275) trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt */
+   294,  /* (276) trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt */
+   294,  /* (277) trigger_cmd ::= scanpt select scanpt */
+   219,  /* (278) expr ::= RAISE LP IGNORE RP */
+   219,  /* (279) expr ::= RAISE LP raisetype COMMA expr RP */
+   238,  /* (280) raisetype ::= ROLLBACK */
+   238,  /* (281) raisetype ::= ABORT */
+   238,  /* (282) raisetype ::= FAIL */
+   192,  /* (283) cmd ::= DROP TRIGGER ifexists fullname */
+   192,  /* (284) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+   192,  /* (285) cmd ::= DETACH database_kw_opt expr */
+   297,  /* (286) key_opt ::= */
+   297,  /* (287) key_opt ::= KEY expr */
+   192,  /* (288) cmd ::= REINDEX */
+   192,  /* (289) cmd ::= REINDEX nm dbnm */
+   192,  /* (290) cmd ::= ANALYZE */
+   192,  /* (291) cmd ::= ANALYZE nm dbnm */
+   192,  /* (292) cmd ::= ALTER TABLE fullname RENAME TO nm */
+   192,  /* (293) cmd ::= alter_add carglist */
+   298,  /* (294) alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken */
    192,  /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
-   299,  /* (296) add_column_fullname ::= fullname */
-   192,  /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
-   192,  /* (298) cmd ::= create_vtab */
-   192,  /* (299) cmd ::= create_vtab LP vtabarglist RP */
-   301,  /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
-   303,  /* (301) vtabarg ::= */
-   304,  /* (302) vtabargtoken ::= ANY */
-   304,  /* (303) vtabargtoken ::= lp anylist RP */
-   305,  /* (304) lp ::= LP */
-   269,  /* (305) with ::= WITH wqlist */
-   269,  /* (306) with ::= WITH RECURSIVE wqlist */
-   308,  /* (307) wqas ::= AS */
-   308,  /* (308) wqas ::= AS MATERIALIZED */
-   308,  /* (309) wqas ::= AS NOT MATERIALIZED */
-   307,  /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
-   309,  /* (311) withnm ::= nm */
-   243,  /* (312) wqlist ::= wqitem */
-   243,  /* (313) wqlist ::= wqlist COMMA wqitem */
-   310,  /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
-   311,  /* (315) windowdefn ::= nm AS LP window RP */
-   312,  /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
-   312,  /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
-   312,  /* (318) window ::= ORDER BY sortlist frame_opt */
-   312,  /* (319) window ::= nm ORDER BY sortlist frame_opt */
-   312,  /* (320) window ::= nm frame_opt */
-   313,  /* (321) frame_opt ::= */
-   313,  /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
-   313,  /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
-   317,  /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
-   319,  /* (325) frame_bound_s ::= frame_bound */
-   319,  /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
-   320,  /* (327) frame_bound_e ::= frame_bound */
-   320,  /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
-   318,  /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
-   318,  /* (330) frame_bound ::= CURRENT ROW */
-   321,  /* (331) frame_exclude_opt ::= */
-   321,  /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
-   322,  /* (333) frame_exclude ::= NO OTHERS */
-   322,  /* (334) frame_exclude ::= CURRENT ROW */
-   322,  /* (335) frame_exclude ::= GROUP|TIES */
-   253,  /* (336) window_clause ::= WINDOW windowdefn_list */
-   276,  /* (337) filter_over ::= filter_clause over_clause */
-   276,  /* (338) filter_over ::= over_clause */
-   276,  /* (339) filter_over ::= filter_clause */
-   316,  /* (340) over_clause ::= OVER LP window RP */
-   316,  /* (341) over_clause ::= OVER nm */
-   315,  /* (342) filter_clause ::= FILTER LP WHERE expr RP */
-   218,  /* (343) term ::= QNUMBER */
-   187,  /* (344) input ::= cmdlist */
-   188,  /* (345) cmdlist ::= cmdlist ecmd */
-   188,  /* (346) cmdlist ::= ecmd */
-   189,  /* (347) ecmd ::= SEMI */
-   189,  /* (348) ecmd ::= cmdx SEMI */
-   189,  /* (349) ecmd ::= explain cmdx SEMI */
-   194,  /* (350) trans_opt ::= */
-   194,  /* (351) trans_opt ::= TRANSACTION */
-   194,  /* (352) trans_opt ::= TRANSACTION nm */
-   196,  /* (353) savepoint_opt ::= SAVEPOINT */
-   196,  /* (354) savepoint_opt ::= */
-   192,  /* (355) cmd ::= create_table create_table_args */
-   205,  /* (356) table_option_set ::= table_option */
-   203,  /* (357) columnlist ::= columnlist COMMA columnname carglist */
-   203,  /* (358) columnlist ::= columnname carglist */
-   195,  /* (359) nm ::= ID|INDEXED|JOIN_KW */
-   195,  /* (360) nm ::= STRING */
-   210,  /* (361) typetoken ::= typename */
-   211,  /* (362) typename ::= ID|STRING */
-   212,  /* (363) signed ::= plus_num */
-   212,  /* (364) signed ::= minus_num */
-   209,  /* (365) carglist ::= carglist ccons */
-   209,  /* (366) carglist ::= */
-   217,  /* (367) ccons ::= NULL onconf */
-   217,  /* (368) ccons ::= GENERATED ALWAYS AS generated */
-   217,  /* (369) ccons ::= AS generated */
-   204,  /* (370) conslist_opt ::= COMMA conslist */
-   230,  /* (371) conslist ::= conslist tconscomma tcons */
-   230,  /* (372) conslist ::= tcons */
-   231,  /* (373) tconscomma ::= */
-   235,  /* (374) defer_subclause_opt ::= defer_subclause */
-   237,  /* (375) resolvetype ::= raisetype */
-   241,  /* (376) selectnowith ::= oneselect */
-   242,  /* (377) oneselect ::= values */
-   257,  /* (378) sclp ::= selcollist COMMA */
-   258,  /* (379) as ::= ID|STRING */
-   267,  /* (380) indexed_opt ::= indexed_by */
-   275,  /* (381) returning ::= */
-   219,  /* (382) expr ::= term */
-   277,  /* (383) likeop ::= LIKE_KW|MATCH */
-   281,  /* (384) case_operand ::= expr */
-   264,  /* (385) exprlist ::= nexprlist */
-   287,  /* (386) nmnum ::= plus_num */
-   287,  /* (387) nmnum ::= nm */
-   287,  /* (388) nmnum ::= ON */
-   287,  /* (389) nmnum ::= DELETE */
-   287,  /* (390) nmnum ::= DEFAULT */
-   213,  /* (391) plus_num ::= INTEGER|FLOAT */
-   292,  /* (392) foreach_clause ::= */
-   292,  /* (393) foreach_clause ::= FOR EACH ROW */
-   295,  /* (394) trnm ::= nm */
-   296,  /* (395) tridxby ::= */
-   297,  /* (396) database_kw_opt ::= DATABASE */
-   297,  /* (397) database_kw_opt ::= */
-   300,  /* (398) kwcolumn_opt ::= */
-   300,  /* (399) kwcolumn_opt ::= COLUMNKW */
-   302,  /* (400) vtabarglist ::= vtabarg */
-   302,  /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
-   303,  /* (402) vtabarg ::= vtabarg vtabargtoken */
-   306,  /* (403) anylist ::= */
-   306,  /* (404) anylist ::= anylist LP anylist RP */
-   306,  /* (405) anylist ::= anylist ANY */
-   269,  /* (406) with ::= */
-   310,  /* (407) windowdefn_list ::= windowdefn */
-   312,  /* (408) window ::= frame_opt */
+   192,  /* (296) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+   192,  /* (297) cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm */
+   192,  /* (298) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL */
+   192,  /* (299) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf */
+   192,  /* (300) cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf */
+   192,  /* (301) cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf */
+   192,  /* (302) cmd ::= create_vtab */
+   192,  /* (303) cmd ::= create_vtab LP vtabarglist RP */
+   300,  /* (304) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+   302,  /* (305) vtabarg ::= */
+   303,  /* (306) vtabargtoken ::= ANY */
+   303,  /* (307) vtabargtoken ::= lp anylist RP */
+   304,  /* (308) lp ::= LP */
+   269,  /* (309) with ::= WITH wqlist */
+   269,  /* (310) with ::= WITH RECURSIVE wqlist */
+   307,  /* (311) wqas ::= AS */
+   307,  /* (312) wqas ::= AS MATERIALIZED */
+   307,  /* (313) wqas ::= AS NOT MATERIALIZED */
+   306,  /* (314) wqitem ::= withnm eidlist_opt wqas LP select RP */
+   308,  /* (315) withnm ::= nm */
+   243,  /* (316) wqlist ::= wqitem */
+   243,  /* (317) wqlist ::= wqlist COMMA wqitem */
+   309,  /* (318) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+   310,  /* (319) windowdefn ::= nm AS LP window RP */
+   311,  /* (320) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+   311,  /* (321) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+   311,  /* (322) window ::= ORDER BY sortlist frame_opt */
+   311,  /* (323) window ::= nm ORDER BY sortlist frame_opt */
+   311,  /* (324) window ::= nm frame_opt */
+   312,  /* (325) frame_opt ::= */
+   312,  /* (326) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+   312,  /* (327) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+   316,  /* (328) range_or_rows ::= RANGE|ROWS|GROUPS */
+   318,  /* (329) frame_bound_s ::= frame_bound */
+   318,  /* (330) frame_bound_s ::= UNBOUNDED PRECEDING */
+   319,  /* (331) frame_bound_e ::= frame_bound */
+   319,  /* (332) frame_bound_e ::= UNBOUNDED FOLLOWING */
+   317,  /* (333) frame_bound ::= expr PRECEDING|FOLLOWING */
+   317,  /* (334) frame_bound ::= CURRENT ROW */
+   320,  /* (335) frame_exclude_opt ::= */
+   320,  /* (336) frame_exclude_opt ::= EXCLUDE frame_exclude */
+   321,  /* (337) frame_exclude ::= NO OTHERS */
+   321,  /* (338) frame_exclude ::= CURRENT ROW */
+   321,  /* (339) frame_exclude ::= GROUP|TIES */
+   253,  /* (340) window_clause ::= WINDOW windowdefn_list */
+   276,  /* (341) filter_over ::= filter_clause over_clause */
+   276,  /* (342) filter_over ::= over_clause */
+   276,  /* (343) filter_over ::= filter_clause */
+   315,  /* (344) over_clause ::= OVER LP window RP */
+   315,  /* (345) over_clause ::= OVER nm */
+   314,  /* (346) filter_clause ::= FILTER LP WHERE expr RP */
+   218,  /* (347) term ::= QNUMBER */
+   187,  /* (348) input ::= cmdlist */
+   188,  /* (349) cmdlist ::= cmdlist ecmd */
+   188,  /* (350) cmdlist ::= ecmd */
+   189,  /* (351) ecmd ::= SEMI */
+   189,  /* (352) ecmd ::= cmdx SEMI */
+   189,  /* (353) ecmd ::= explain cmdx SEMI */
+   194,  /* (354) trans_opt ::= */
+   194,  /* (355) trans_opt ::= TRANSACTION */
+   194,  /* (356) trans_opt ::= TRANSACTION nm */
+   196,  /* (357) savepoint_opt ::= SAVEPOINT */
+   196,  /* (358) savepoint_opt ::= */
+   192,  /* (359) cmd ::= create_table create_table_args */
+   205,  /* (360) table_option_set ::= table_option */
+   203,  /* (361) columnlist ::= columnlist COMMA columnname carglist */
+   203,  /* (362) columnlist ::= columnname carglist */
+   195,  /* (363) nm ::= ID|INDEXED|JOIN_KW */
+   195,  /* (364) nm ::= STRING */
+   210,  /* (365) typetoken ::= typename */
+   211,  /* (366) typename ::= ID|STRING */
+   212,  /* (367) signed ::= plus_num */
+   212,  /* (368) signed ::= minus_num */
+   209,  /* (369) carglist ::= carglist ccons */
+   209,  /* (370) carglist ::= */
+   217,  /* (371) ccons ::= NULL onconf */
+   217,  /* (372) ccons ::= GENERATED ALWAYS AS generated */
+   217,  /* (373) ccons ::= AS generated */
+   204,  /* (374) conslist_opt ::= COMMA conslist */
+   230,  /* (375) conslist ::= conslist tconscomma tcons */
+   230,  /* (376) conslist ::= tcons */
+   231,  /* (377) tconscomma ::= */
+   235,  /* (378) defer_subclause_opt ::= defer_subclause */
+   237,  /* (379) resolvetype ::= raisetype */
+   241,  /* (380) selectnowith ::= oneselect */
+   242,  /* (381) oneselect ::= values */
+   257,  /* (382) sclp ::= selcollist COMMA */
+   258,  /* (383) as ::= ID|STRING */
+   267,  /* (384) indexed_opt ::= indexed_by */
+   275,  /* (385) returning ::= */
+   219,  /* (386) expr ::= term */
+   277,  /* (387) likeop ::= LIKE_KW|MATCH */
+   281,  /* (388) case_operand ::= expr */
+   264,  /* (389) exprlist ::= nexprlist */
+   287,  /* (390) nmnum ::= plus_num */
+   287,  /* (391) nmnum ::= nm */
+   287,  /* (392) nmnum ::= ON */
+   287,  /* (393) nmnum ::= DELETE */
+   287,  /* (394) nmnum ::= DEFAULT */
+   213,  /* (395) plus_num ::= INTEGER|FLOAT */
+   292,  /* (396) foreach_clause ::= */
+   292,  /* (397) foreach_clause ::= FOR EACH ROW */
+   295,  /* (398) tridxby ::= */
+   296,  /* (399) database_kw_opt ::= DATABASE */
+   296,  /* (400) database_kw_opt ::= */
+   299,  /* (401) kwcolumn_opt ::= */
+   299,  /* (402) kwcolumn_opt ::= COLUMNKW */
+   301,  /* (403) vtabarglist ::= vtabarg */
+   301,  /* (404) vtabarglist ::= vtabarglist COMMA vtabarg */
+   302,  /* (405) vtabarg ::= vtabarg vtabargtoken */
+   305,  /* (406) anylist ::= */
+   305,  /* (407) anylist ::= anylist LP anylist RP */
+   305,  /* (408) anylist ::= anylist ANY */
+   269,  /* (409) with ::= */
+   309,  /* (410) windowdefn_list ::= windowdefn */
+   311,  /* (411) window ::= frame_opt */
 };
 
 /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -180836,8 +183151,8 @@ static const signed char yyRuleInfoNRhs[] = {
    -3,  /* (119) fullname ::= nm DOT nm */
    -1,  /* (120) xfullname ::= nm */
    -3,  /* (121) xfullname ::= nm DOT nm */
-   -5,  /* (122) xfullname ::= nm DOT nm AS nm */
-   -3,  /* (123) xfullname ::= nm AS nm */
+   -3,  /* (122) xfullname ::= nm AS nm */
+   -5,  /* (123) xfullname ::= nm DOT nm AS nm */
    -1,  /* (124) joinop ::= COMMA|JOIN */
    -2,  /* (125) joinop ::= JOIN_KW JOIN */
    -3,  /* (126) joinop ::= JOIN_KW nm JOIN */
@@ -180986,143 +183301,146 @@ static const signed char yyRuleInfoNRhs[] = {
    -2,  /* (269) when_clause ::= WHEN expr */
    -3,  /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
    -2,  /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
-   -3,  /* (272) trnm ::= nm DOT nm */
-   -3,  /* (273) tridxby ::= INDEXED BY nm */
-   -2,  /* (274) tridxby ::= NOT INDEXED */
-   -9,  /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-   -8,  /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
-   -6,  /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-   -3,  /* (278) trigger_cmd ::= scanpt select scanpt */
-   -4,  /* (279) expr ::= RAISE LP IGNORE RP */
-   -6,  /* (280) expr ::= RAISE LP raisetype COMMA expr RP */
-   -1,  /* (281) raisetype ::= ROLLBACK */
-   -1,  /* (282) raisetype ::= ABORT */
-   -1,  /* (283) raisetype ::= FAIL */
-   -4,  /* (284) cmd ::= DROP TRIGGER ifexists fullname */
-   -6,  /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
-   -3,  /* (286) cmd ::= DETACH database_kw_opt expr */
-    0,  /* (287) key_opt ::= */
-   -2,  /* (288) key_opt ::= KEY expr */
-   -1,  /* (289) cmd ::= REINDEX */
-   -3,  /* (290) cmd ::= REINDEX nm dbnm */
-   -1,  /* (291) cmd ::= ANALYZE */
-   -3,  /* (292) cmd ::= ANALYZE nm dbnm */
-   -6,  /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
-   -7,  /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+   -3,  /* (272) tridxby ::= INDEXED BY nm */
+   -2,  /* (273) tridxby ::= NOT INDEXED */
+   -9,  /* (274) trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt */
+   -8,  /* (275) trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt */
+   -6,  /* (276) trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt */
+   -3,  /* (277) trigger_cmd ::= scanpt select scanpt */
+   -4,  /* (278) expr ::= RAISE LP IGNORE RP */
+   -6,  /* (279) expr ::= RAISE LP raisetype COMMA expr RP */
+   -1,  /* (280) raisetype ::= ROLLBACK */
+   -1,  /* (281) raisetype ::= ABORT */
+   -1,  /* (282) raisetype ::= FAIL */
+   -4,  /* (283) cmd ::= DROP TRIGGER ifexists fullname */
+   -6,  /* (284) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+   -3,  /* (285) cmd ::= DETACH database_kw_opt expr */
+    0,  /* (286) key_opt ::= */
+   -2,  /* (287) key_opt ::= KEY expr */
+   -1,  /* (288) cmd ::= REINDEX */
+   -3,  /* (289) cmd ::= REINDEX nm dbnm */
+   -1,  /* (290) cmd ::= ANALYZE */
+   -3,  /* (291) cmd ::= ANALYZE nm dbnm */
+   -6,  /* (292) cmd ::= ALTER TABLE fullname RENAME TO nm */
+   -2,  /* (293) cmd ::= alter_add carglist */
+   -7,  /* (294) alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken */
    -6,  /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
-   -1,  /* (296) add_column_fullname ::= fullname */
-   -8,  /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
-   -1,  /* (298) cmd ::= create_vtab */
-   -4,  /* (299) cmd ::= create_vtab LP vtabarglist RP */
-   -8,  /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
-    0,  /* (301) vtabarg ::= */
-   -1,  /* (302) vtabargtoken ::= ANY */
-   -3,  /* (303) vtabargtoken ::= lp anylist RP */
-   -1,  /* (304) lp ::= LP */
-   -2,  /* (305) with ::= WITH wqlist */
-   -3,  /* (306) with ::= WITH RECURSIVE wqlist */
-   -1,  /* (307) wqas ::= AS */
-   -2,  /* (308) wqas ::= AS MATERIALIZED */
-   -3,  /* (309) wqas ::= AS NOT MATERIALIZED */
-   -6,  /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
-   -1,  /* (311) withnm ::= nm */
-   -1,  /* (312) wqlist ::= wqitem */
-   -3,  /* (313) wqlist ::= wqlist COMMA wqitem */
-   -3,  /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
-   -5,  /* (315) windowdefn ::= nm AS LP window RP */
-   -5,  /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
-   -6,  /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
-   -4,  /* (318) window ::= ORDER BY sortlist frame_opt */
-   -5,  /* (319) window ::= nm ORDER BY sortlist frame_opt */
-   -2,  /* (320) window ::= nm frame_opt */
-    0,  /* (321) frame_opt ::= */
-   -3,  /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
-   -6,  /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
-   -1,  /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
-   -1,  /* (325) frame_bound_s ::= frame_bound */
-   -2,  /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
-   -1,  /* (327) frame_bound_e ::= frame_bound */
-   -2,  /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
-   -2,  /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
-   -2,  /* (330) frame_bound ::= CURRENT ROW */
-    0,  /* (331) frame_exclude_opt ::= */
-   -2,  /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
-   -2,  /* (333) frame_exclude ::= NO OTHERS */
-   -2,  /* (334) frame_exclude ::= CURRENT ROW */
-   -1,  /* (335) frame_exclude ::= GROUP|TIES */
-   -2,  /* (336) window_clause ::= WINDOW windowdefn_list */
-   -2,  /* (337) filter_over ::= filter_clause over_clause */
-   -1,  /* (338) filter_over ::= over_clause */
-   -1,  /* (339) filter_over ::= filter_clause */
-   -4,  /* (340) over_clause ::= OVER LP window RP */
-   -2,  /* (341) over_clause ::= OVER nm */
-   -5,  /* (342) filter_clause ::= FILTER LP WHERE expr RP */
-   -1,  /* (343) term ::= QNUMBER */
-   -1,  /* (344) input ::= cmdlist */
-   -2,  /* (345) cmdlist ::= cmdlist ecmd */
-   -1,  /* (346) cmdlist ::= ecmd */
-   -1,  /* (347) ecmd ::= SEMI */
-   -2,  /* (348) ecmd ::= cmdx SEMI */
-   -3,  /* (349) ecmd ::= explain cmdx SEMI */
-    0,  /* (350) trans_opt ::= */
-   -1,  /* (351) trans_opt ::= TRANSACTION */
-   -2,  /* (352) trans_opt ::= TRANSACTION nm */
-   -1,  /* (353) savepoint_opt ::= SAVEPOINT */
-    0,  /* (354) savepoint_opt ::= */
-   -2,  /* (355) cmd ::= create_table create_table_args */
-   -1,  /* (356) table_option_set ::= table_option */
-   -4,  /* (357) columnlist ::= columnlist COMMA columnname carglist */
-   -2,  /* (358) columnlist ::= columnname carglist */
-   -1,  /* (359) nm ::= ID|INDEXED|JOIN_KW */
-   -1,  /* (360) nm ::= STRING */
-   -1,  /* (361) typetoken ::= typename */
-   -1,  /* (362) typename ::= ID|STRING */
-   -1,  /* (363) signed ::= plus_num */
-   -1,  /* (364) signed ::= minus_num */
-   -2,  /* (365) carglist ::= carglist ccons */
-    0,  /* (366) carglist ::= */
-   -2,  /* (367) ccons ::= NULL onconf */
-   -4,  /* (368) ccons ::= GENERATED ALWAYS AS generated */
-   -2,  /* (369) ccons ::= AS generated */
-   -2,  /* (370) conslist_opt ::= COMMA conslist */
-   -3,  /* (371) conslist ::= conslist tconscomma tcons */
-   -1,  /* (372) conslist ::= tcons */
-    0,  /* (373) tconscomma ::= */
-   -1,  /* (374) defer_subclause_opt ::= defer_subclause */
-   -1,  /* (375) resolvetype ::= raisetype */
-   -1,  /* (376) selectnowith ::= oneselect */
-   -1,  /* (377) oneselect ::= values */
-   -2,  /* (378) sclp ::= selcollist COMMA */
-   -1,  /* (379) as ::= ID|STRING */
-   -1,  /* (380) indexed_opt ::= indexed_by */
-    0,  /* (381) returning ::= */
-   -1,  /* (382) expr ::= term */
-   -1,  /* (383) likeop ::= LIKE_KW|MATCH */
-   -1,  /* (384) case_operand ::= expr */
-   -1,  /* (385) exprlist ::= nexprlist */
-   -1,  /* (386) nmnum ::= plus_num */
-   -1,  /* (387) nmnum ::= nm */
-   -1,  /* (388) nmnum ::= ON */
-   -1,  /* (389) nmnum ::= DELETE */
-   -1,  /* (390) nmnum ::= DEFAULT */
-   -1,  /* (391) plus_num ::= INTEGER|FLOAT */
-    0,  /* (392) foreach_clause ::= */
-   -3,  /* (393) foreach_clause ::= FOR EACH ROW */
-   -1,  /* (394) trnm ::= nm */
-    0,  /* (395) tridxby ::= */
-   -1,  /* (396) database_kw_opt ::= DATABASE */
-    0,  /* (397) database_kw_opt ::= */
-    0,  /* (398) kwcolumn_opt ::= */
-   -1,  /* (399) kwcolumn_opt ::= COLUMNKW */
-   -1,  /* (400) vtabarglist ::= vtabarg */
-   -3,  /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
-   -2,  /* (402) vtabarg ::= vtabarg vtabargtoken */
-    0,  /* (403) anylist ::= */
-   -4,  /* (404) anylist ::= anylist LP anylist RP */
-   -2,  /* (405) anylist ::= anylist ANY */
-    0,  /* (406) with ::= */
-   -1,  /* (407) windowdefn_list ::= windowdefn */
-   -1,  /* (408) window ::= frame_opt */
+   -8,  /* (296) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+   -6,  /* (297) cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm */
+   -9,  /* (298) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL */
+  -10,  /* (299) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf */
+  -11,  /* (300) cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf */
+   -9,  /* (301) cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf */
+   -1,  /* (302) cmd ::= create_vtab */
+   -4,  /* (303) cmd ::= create_vtab LP vtabarglist RP */
+   -8,  /* (304) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+    0,  /* (305) vtabarg ::= */
+   -1,  /* (306) vtabargtoken ::= ANY */
+   -3,  /* (307) vtabargtoken ::= lp anylist RP */
+   -1,  /* (308) lp ::= LP */
+   -2,  /* (309) with ::= WITH wqlist */
+   -3,  /* (310) with ::= WITH RECURSIVE wqlist */
+   -1,  /* (311) wqas ::= AS */
+   -2,  /* (312) wqas ::= AS MATERIALIZED */
+   -3,  /* (313) wqas ::= AS NOT MATERIALIZED */
+   -6,  /* (314) wqitem ::= withnm eidlist_opt wqas LP select RP */
+   -1,  /* (315) withnm ::= nm */
+   -1,  /* (316) wqlist ::= wqitem */
+   -3,  /* (317) wqlist ::= wqlist COMMA wqitem */
+   -3,  /* (318) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+   -5,  /* (319) windowdefn ::= nm AS LP window RP */
+   -5,  /* (320) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+   -6,  /* (321) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+   -4,  /* (322) window ::= ORDER BY sortlist frame_opt */
+   -5,  /* (323) window ::= nm ORDER BY sortlist frame_opt */
+   -2,  /* (324) window ::= nm frame_opt */
+    0,  /* (325) frame_opt ::= */
+   -3,  /* (326) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+   -6,  /* (327) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+   -1,  /* (328) range_or_rows ::= RANGE|ROWS|GROUPS */
+   -1,  /* (329) frame_bound_s ::= frame_bound */
+   -2,  /* (330) frame_bound_s ::= UNBOUNDED PRECEDING */
+   -1,  /* (331) frame_bound_e ::= frame_bound */
+   -2,  /* (332) frame_bound_e ::= UNBOUNDED FOLLOWING */
+   -2,  /* (333) frame_bound ::= expr PRECEDING|FOLLOWING */
+   -2,  /* (334) frame_bound ::= CURRENT ROW */
+    0,  /* (335) frame_exclude_opt ::= */
+   -2,  /* (336) frame_exclude_opt ::= EXCLUDE frame_exclude */
+   -2,  /* (337) frame_exclude ::= NO OTHERS */
+   -2,  /* (338) frame_exclude ::= CURRENT ROW */
+   -1,  /* (339) frame_exclude ::= GROUP|TIES */
+   -2,  /* (340) window_clause ::= WINDOW windowdefn_list */
+   -2,  /* (341) filter_over ::= filter_clause over_clause */
+   -1,  /* (342) filter_over ::= over_clause */
+   -1,  /* (343) filter_over ::= filter_clause */
+   -4,  /* (344) over_clause ::= OVER LP window RP */
+   -2,  /* (345) over_clause ::= OVER nm */
+   -5,  /* (346) filter_clause ::= FILTER LP WHERE expr RP */
+   -1,  /* (347) term ::= QNUMBER */
+   -1,  /* (348) input ::= cmdlist */
+   -2,  /* (349) cmdlist ::= cmdlist ecmd */
+   -1,  /* (350) cmdlist ::= ecmd */
+   -1,  /* (351) ecmd ::= SEMI */
+   -2,  /* (352) ecmd ::= cmdx SEMI */
+   -3,  /* (353) ecmd ::= explain cmdx SEMI */
+    0,  /* (354) trans_opt ::= */
+   -1,  /* (355) trans_opt ::= TRANSACTION */
+   -2,  /* (356) trans_opt ::= TRANSACTION nm */
+   -1,  /* (357) savepoint_opt ::= SAVEPOINT */
+    0,  /* (358) savepoint_opt ::= */
+   -2,  /* (359) cmd ::= create_table create_table_args */
+   -1,  /* (360) table_option_set ::= table_option */
+   -4,  /* (361) columnlist ::= columnlist COMMA columnname carglist */
+   -2,  /* (362) columnlist ::= columnname carglist */
+   -1,  /* (363) nm ::= ID|INDEXED|JOIN_KW */
+   -1,  /* (364) nm ::= STRING */
+   -1,  /* (365) typetoken ::= typename */
+   -1,  /* (366) typename ::= ID|STRING */
+   -1,  /* (367) signed ::= plus_num */
+   -1,  /* (368) signed ::= minus_num */
+   -2,  /* (369) carglist ::= carglist ccons */
+    0,  /* (370) carglist ::= */
+   -2,  /* (371) ccons ::= NULL onconf */
+   -4,  /* (372) ccons ::= GENERATED ALWAYS AS generated */
+   -2,  /* (373) ccons ::= AS generated */
+   -2,  /* (374) conslist_opt ::= COMMA conslist */
+   -3,  /* (375) conslist ::= conslist tconscomma tcons */
+   -1,  /* (376) conslist ::= tcons */
+    0,  /* (377) tconscomma ::= */
+   -1,  /* (378) defer_subclause_opt ::= defer_subclause */
+   -1,  /* (379) resolvetype ::= raisetype */
+   -1,  /* (380) selectnowith ::= oneselect */
+   -1,  /* (381) oneselect ::= values */
+   -2,  /* (382) sclp ::= selcollist COMMA */
+   -1,  /* (383) as ::= ID|STRING */
+   -1,  /* (384) indexed_opt ::= indexed_by */
+    0,  /* (385) returning ::= */
+   -1,  /* (386) expr ::= term */
+   -1,  /* (387) likeop ::= LIKE_KW|MATCH */
+   -1,  /* (388) case_operand ::= expr */
+   -1,  /* (389) exprlist ::= nexprlist */
+   -1,  /* (390) nmnum ::= plus_num */
+   -1,  /* (391) nmnum ::= nm */
+   -1,  /* (392) nmnum ::= ON */
+   -1,  /* (393) nmnum ::= DELETE */
+   -1,  /* (394) nmnum ::= DEFAULT */
+   -1,  /* (395) plus_num ::= INTEGER|FLOAT */
+    0,  /* (396) foreach_clause ::= */
+   -3,  /* (397) foreach_clause ::= FOR EACH ROW */
+    0,  /* (398) tridxby ::= */
+   -1,  /* (399) database_kw_opt ::= DATABASE */
+    0,  /* (400) database_kw_opt ::= */
+    0,  /* (401) kwcolumn_opt ::= */
+   -1,  /* (402) kwcolumn_opt ::= COLUMNKW */
+   -1,  /* (403) vtabarglist ::= vtabarg */
+   -3,  /* (404) vtabarglist ::= vtabarglist COMMA vtabarg */
+   -2,  /* (405) vtabarg ::= vtabarg vtabargtoken */
+    0,  /* (406) anylist ::= */
+   -4,  /* (407) anylist ::= anylist LP anylist RP */
+   -2,  /* (408) anylist ::= anylist ANY */
+    0,  /* (409) with ::= */
+   -1,  /* (410) windowdefn_list ::= windowdefn */
+   -1,  /* (411) window ::= frame_opt */
 };
 
 static void SQLITE_APICALL yy_accept(yyParser*);  /* Forward Declaration */
@@ -181174,16 +183492,16 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
 { sqlite3FinishCoding(pParse); }
         break;
       case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy502);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);}
         break;
       case 4: /* transtype ::= */
-{yymsp[1].minor.yy502 = TK_DEFERRED;}
+{yymsp[1].minor.yy144 = TK_DEFERRED;}
         break;
       case 5: /* transtype ::= DEFERRED */
       case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
       case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
-      case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324);
-{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/}
+      case 328: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==328);
+{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/}
         break;
       case 8: /* cmd ::= COMMIT|END trans_opt */
       case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -181206,7 +183524,7 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
         break;
       case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
 {
-   sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy502,0,0,yymsp[-2].minor.yy502);
+   sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144);
 }
         break;
       case 14: /* createkw ::= CREATE */
@@ -181222,38 +183540,38 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
       case 100: /* distinct ::= */ yytestcase(yyruleno==100);
       case 246: /* collate ::= */ yytestcase(yyruleno==246);
-{yymsp[1].minor.yy502 = 0;}
+{yymsp[1].minor.yy144 = 0;}
         break;
       case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy502 = 1;}
+{yymsp[-2].minor.yy144 = 1;}
         break;
       case 17: /* temp ::= TEMP */
-{yymsp[0].minor.yy502 = pParse->db->init.busy==0;}
+{yymsp[0].minor.yy144 = pParse->db->init.busy==0;}
         break;
       case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */
 {
-  sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy9,0);
+  sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0);
 }
         break;
       case 20: /* create_table_args ::= AS select */
 {
-  sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy637);
-  sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637);
+  sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555);
+  sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
 }
         break;
       case 21: /* table_option_set ::= */
-{yymsp[1].minor.yy9 = 0;}
+{yymsp[1].minor.yy391 = 0;}
         break;
       case 22: /* table_option_set ::= table_option_set COMMA table_option */
-{yylhsminor.yy9 = yymsp[-2].minor.yy9|yymsp[0].minor.yy9;}
-  yymsp[-2].minor.yy9 = yylhsminor.yy9;
+{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;}
+  yymsp[-2].minor.yy391 = yylhsminor.yy391;
         break;
       case 23: /* table_option ::= WITHOUT nm */
 {
   if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
-    yymsp[-1].minor.yy9 = TF_WithoutRowid | TF_NoVisibleRowid;
+    yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid;
   }else{
-    yymsp[-1].minor.yy9 = 0;
+    yymsp[-1].minor.yy391 = 0;
     sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
   }
 }
@@ -181261,13 +183579,13 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       case 24: /* table_option ::= nm */
 {
   if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){
-    yylhsminor.yy9 = TF_Strict;
+    yylhsminor.yy391 = TF_Strict;
   }else{
-    yylhsminor.yy9 = 0;
+    yylhsminor.yy391 = 0;
     sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
   }
 }
-  yymsp[0].minor.yy9 = yylhsminor.yy9;
+  yymsp[0].minor.yy391 = yylhsminor.yy391;
         break;
       case 25: /* columnname ::= nm typetoken */
 {sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);}
@@ -181293,7 +183611,7 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       case 30: /* scanpt ::= */
 {
   assert( yyLookahead!=YYNOCODE );
-  yymsp[1].minor.yy342 = yyLookaheadToken.z;
+  yymsp[1].minor.yy168 = yyLookaheadToken.z;
 }
         break;
       case 31: /* scantok ::= */
@@ -181307,17 +183625,17 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
 {ASSERT_IS_CREATE; pParse->u1.cr.constraintName = yymsp[0].minor.yy0;}
         break;
       case 33: /* ccons ::= DEFAULT scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
         break;
       case 34: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
         break;
       case 35: /* ccons ::= DEFAULT PLUS scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
         break;
       case 36: /* ccons ::= DEFAULT MINUS scantok term */
 {
-  Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy590, 0);
+  Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0);
   sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
 }
         break;
@@ -181332,133 +183650,133 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
 }
         break;
       case 38: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy502);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);}
         break;
       case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy502,yymsp[0].minor.yy502,yymsp[-2].minor.yy502);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);}
         break;
       case 40: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy502,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0,
                                    SQLITE_IDXTYPE_UNIQUE);}
         break;
       case 41: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
         break;
       case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy402,yymsp[0].minor.yy502);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);}
         break;
       case 43: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy502);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);}
         break;
       case 44: /* ccons ::= COLLATE ID|STRING */
 {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
         break;
       case 45: /* generated ::= LP expr RP */
-{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy590,0);}
+{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);}
         break;
       case 46: /* generated ::= LP expr RP ID */
-{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy590,&yymsp[0].minor.yy0);}
+{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);}
         break;
       case 48: /* autoinc ::= AUTOINCR */
-{yymsp[0].minor.yy502 = 1;}
+{yymsp[0].minor.yy144 = 1;}
         break;
       case 49: /* refargs ::= */
-{ yymsp[1].minor.yy502 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */}
         break;
       case 50: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy502 = (yymsp[-1].minor.yy502 & ~yymsp[0].minor.yy481.mask) | yymsp[0].minor.yy481.value; }
+{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; }
         break;
       case 51: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy481.value = 0;     yymsp[-1].minor.yy481.mask = 0x000000; }
+{ yymsp[-1].minor.yy383.value = 0;     yymsp[-1].minor.yy383.mask = 0x000000; }
         break;
       case 52: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy481.value = 0;     yymsp[-2].minor.yy481.mask = 0x000000; }
+{ yymsp[-2].minor.yy383.value = 0;     yymsp[-2].minor.yy383.mask = 0x000000; }
         break;
       case 53: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502;     yymsp[-2].minor.yy481.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144;     yymsp[-2].minor.yy383.mask = 0x0000ff; }
         break;
       case 54: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502<<8;  yymsp[-2].minor.yy481.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8;  yymsp[-2].minor.yy383.mask = 0x00ff00; }
         break;
       case 55: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy502 = OE_SetNull;  /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_SetNull;  /* EV: R-33326-45252 */}
         break;
       case 56: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy502 = OE_SetDflt;  /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_SetDflt;  /* EV: R-33326-45252 */}
         break;
       case 57: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy502 = OE_Cascade;  /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy144 = OE_Cascade;  /* EV: R-33326-45252 */}
         break;
       case 58: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy502 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */}
         break;
       case 59: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy502 = OE_None;     /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_None;     /* EV: R-33326-45252 */}
         break;
       case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy502 = 0;}
+{yymsp[-2].minor.yy144 = 0;}
         break;
       case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
       case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76);
       case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173);
-{yymsp[-1].minor.yy502 = yymsp[0].minor.yy502;}
+{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;}
         break;
       case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
       case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
       case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219);
       case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222);
       case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247);
-{yymsp[-1].minor.yy502 = 1;}
+{yymsp[-1].minor.yy144 = 1;}
         break;
       case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy502 = 0;}
+{yymsp[-1].minor.yy144 = 0;}
         break;
       case 66: /* tconscomma ::= COMMA */
 {ASSERT_IS_CREATE; pParse->u1.cr.constraintName.n = 0;}
         break;
       case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy402,yymsp[0].minor.yy502,yymsp[-2].minor.yy502,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);}
         break;
       case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy402,yymsp[0].minor.yy502,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0,
                                        SQLITE_IDXTYPE_UNIQUE);}
         break;
       case 70: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy590,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
         break;
       case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
 {
-    sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy402, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[-1].minor.yy502);
-    sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy502);
+    sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144);
+    sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144);
 }
         break;
       case 73: /* onconf ::= */
       case 75: /* orconf ::= */ yytestcase(yyruleno==75);
-{yymsp[1].minor.yy502 = OE_Default;}
+{yymsp[1].minor.yy144 = OE_Default;}
         break;
       case 74: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy502 = yymsp[0].minor.yy502;}
+{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;}
         break;
       case 77: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy502 = OE_Ignore;}
+{yymsp[0].minor.yy144 = OE_Ignore;}
         break;
       case 78: /* resolvetype ::= REPLACE */
       case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174);
-{yymsp[0].minor.yy502 = OE_Replace;}
+{yymsp[0].minor.yy144 = OE_Replace;}
         break;
       case 79: /* cmd ::= DROP TABLE ifexists fullname */
 {
-  sqlite3DropTable(pParse, yymsp[0].minor.yy563, 0, yymsp[-1].minor.yy502);
+  sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144);
 }
         break;
       case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
 {
-  sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[0].minor.yy637, yymsp[-7].minor.yy502, yymsp[-5].minor.yy502);
+  sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144);
 }
         break;
       case 83: /* cmd ::= DROP VIEW ifexists fullname */
 {
-  sqlite3DropTable(pParse, yymsp[0].minor.yy563, 1, yymsp[-1].minor.yy502);
+  sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
 }
         break;
       case 84: /* cmd ::= select */
@@ -181467,20 +183785,20 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
   if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0
    || sqlite3ReadSchema(pParse)==SQLITE_OK
   ){
-    sqlite3Select(pParse, yymsp[0].minor.yy637, &dest);
+    sqlite3Select(pParse, yymsp[0].minor.yy555, &dest);
   }
-  sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637);
+  sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
 }
         break;
       case 85: /* select ::= WITH wqlist selectnowith */
-{yymsp[-2].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);}
+{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
         break;
       case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
-{yymsp[-3].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);}
+{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
         break;
       case 87: /* select ::= selectnowith */
 {
-  Select *p = yymsp[0].minor.yy637;
+  Select *p = yymsp[0].minor.yy555;
   if( p ){
     parserDoubleLinkSelect(pParse, p);
   }
@@ -181488,8 +183806,8 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
         break;
       case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */
 {
-  Select *pRhs = yymsp[0].minor.yy637;
-  Select *pLhs = yymsp[-2].minor.yy637;
+  Select *pRhs = yymsp[0].minor.yy555;
+  Select *pLhs = yymsp[-2].minor.yy555;
   if( pRhs && pRhs->pPrior ){
     SrcList *pFrom;
     Token x;
@@ -181499,60 +183817,60 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
     pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
   }
   if( pRhs ){
-    pRhs->op = (u8)yymsp[-1].minor.yy502;
+    pRhs->op = (u8)yymsp[-1].minor.yy144;
     pRhs->pPrior = pLhs;
     if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue;
     pRhs->selFlags &= ~(u32)SF_MultiValue;
-    if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1;
+    if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1;
   }else{
     sqlite3SelectDelete(pParse->db, pLhs);
   }
-  yymsp[-2].minor.yy637 = pRhs;
+  yymsp[-2].minor.yy555 = pRhs;
 }
         break;
       case 89: /* multiselect_op ::= UNION */
       case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91);
-{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/}
         break;
       case 90: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy502 = TK_ALL;}
+{yymsp[-1].minor.yy144 = TK_ALL;}
         break;
       case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
 {
-  yymsp[-8].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy402,yymsp[-5].minor.yy563,yymsp[-4].minor.yy590,yymsp[-3].minor.yy402,yymsp[-2].minor.yy590,yymsp[-1].minor.yy402,yymsp[-7].minor.yy502,yymsp[0].minor.yy590);
+  yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454);
 }
         break;
       case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
 {
-  yymsp[-9].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy402,yymsp[-6].minor.yy563,yymsp[-5].minor.yy590,yymsp[-4].minor.yy402,yymsp[-3].minor.yy590,yymsp[-1].minor.yy402,yymsp[-8].minor.yy502,yymsp[0].minor.yy590);
-  if( yymsp[-9].minor.yy637 ){
-    yymsp[-9].minor.yy637->pWinDefn = yymsp[-2].minor.yy483;
+  yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454);
+  if( yymsp[-9].minor.yy555 ){
+    yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211;
   }else{
-    sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy483);
+    sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211);
   }
 }
         break;
       case 94: /* values ::= VALUES LP nexprlist RP */
 {
-  yymsp[-3].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy402,0,0,0,0,0,SF_Values,0);
+  yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0);
 }
         break;
       case 95: /* oneselect ::= mvalues */
 {
-  sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy637);
+  sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555);
 }
         break;
       case 96: /* mvalues ::= values COMMA LP nexprlist RP */
       case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97);
 {
-  yymsp[-4].minor.yy637 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy637, yymsp[-1].minor.yy402);
+  yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14);
 }
         break;
       case 98: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy502 = SF_Distinct;}
+{yymsp[0].minor.yy144 = SF_Distinct;}
         break;
       case 99: /* distinct ::= ALL */
-{yymsp[0].minor.yy502 = SF_All;}
+{yymsp[0].minor.yy144 = SF_All;}
         break;
       case 101: /* sclp ::= */
       case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134);
@@ -181560,20 +183878,20 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       case 234: /* exprlist ::= */ yytestcase(yyruleno==234);
       case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237);
       case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242);
-{yymsp[1].minor.yy402 = 0;}
+{yymsp[1].minor.yy14 = 0;}
         break;
       case 102: /* selcollist ::= sclp scanpt expr scanpt as */
 {
-   yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[-2].minor.yy590);
-   if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[0].minor.yy0, 1);
-   sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy402,yymsp[-3].minor.yy342,yymsp[-1].minor.yy342);
+   yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
+   if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1);
+   sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168);
 }
         break;
       case 103: /* selcollist ::= sclp scanpt STAR */
 {
   Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
   sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
-  yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy402, p);
+  yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p);
 }
         break;
       case 104: /* selcollist ::= sclp scanpt nm DOT STAR */
@@ -181583,7 +183901,7 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
   sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
   pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
   pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
-  yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, pDot);
+  yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot);
 }
         break;
       case 105: /* as ::= AS nm */
@@ -181594,50 +183912,50 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
         break;
       case 107: /* from ::= */
       case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110);
-{yymsp[1].minor.yy563 = 0;}
+{yymsp[1].minor.yy203 = 0;}
         break;
       case 108: /* from ::= FROM seltablist */
 {
-  yymsp[-1].minor.yy563 = yymsp[0].minor.yy563;
-  sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy563);
+  yymsp[-1].minor.yy203 = yymsp[0].minor.yy203;
+  sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203);
 }
         break;
       case 109: /* stl_prefix ::= seltablist joinop */
 {
-   if( ALWAYS(yymsp[-1].minor.yy563 && yymsp[-1].minor.yy563->nSrc>0) ) yymsp[-1].minor.yy563->a[yymsp[-1].minor.yy563->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy502;
+   if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144;
 }
         break;
       case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */
 {
-  yymsp[-4].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy563,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421);
+  yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
 }
         break;
       case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
 {
-  yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy421);
-  sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-1].minor.yy0);
+  yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269);
+  sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0);
 }
         break;
       case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
 {
-  yymsp[-7].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy563,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421);
-  sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy563, yymsp[-3].minor.yy402);
+  yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+  sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14);
 }
         break;
       case 114: /* seltablist ::= stl_prefix LP select RP as on_using */
 {
-    yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy637,&yymsp[0].minor.yy421);
+    yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269);
   }
         break;
       case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
 {
-    if( yymsp[-5].minor.yy563==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy421.pOn==0 && yymsp[0].minor.yy421.pUsing==0 ){
-      yymsp[-5].minor.yy563 = yymsp[-3].minor.yy563;
-    }else if( ALWAYS(yymsp[-3].minor.yy563!=0) && yymsp[-3].minor.yy563->nSrc==1 ){
-      yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421);
-      if( yymsp[-5].minor.yy563 ){
-        SrcItem *pNew = &yymsp[-5].minor.yy563->a[yymsp[-5].minor.yy563->nSrc-1];
-        SrcItem *pOld = yymsp[-3].minor.yy563->a;
+    if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){
+      yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203;
+    }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){
+      yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+      if( yymsp[-5].minor.yy203 ){
+        SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1];
+        SrcItem *pOld = yymsp[-3].minor.yy203->a;
         assert( pOld->fg.fixedSchema==0 );
         pNew->zName = pOld->zName;
         assert( pOld->fg.fixedSchema==0 );
@@ -181662,12 +183980,12 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
         }
         pOld->zName = 0;
       }
-      sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy563);
+      sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203);
     }else{
       Select *pSubquery;
-      sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy563);
-      pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy563,0,0,0,0,SF_NestedFrom,0);
-      yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy421);
+      sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203);
+      pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0);
+      yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269);
     }
   }
         break;
@@ -181676,57 +183994,67 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
 {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
         break;
       case 118: /* fullname ::= nm */
+      case 120: /* xfullname ::= nm */ yytestcase(yyruleno==120);
 {
-  yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
-  if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0);
+  yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+  if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
 }
-  yymsp[0].minor.yy563 = yylhsminor.yy563;
+  yymsp[0].minor.yy203 = yylhsminor.yy203;
         break;
       case 119: /* fullname ::= nm DOT nm */
+      case 121: /* xfullname ::= nm DOT nm */ yytestcase(yyruleno==121);
 {
-  yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
-  if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0);
+  yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+  if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
 }
-  yymsp[-2].minor.yy563 = yylhsminor.yy563;
-        break;
-      case 120: /* xfullname ::= nm */
-{yymsp[0].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+  yymsp[-2].minor.yy203 = yylhsminor.yy203;
         break;
-      case 121: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
-        break;
-      case 122: /* xfullname ::= nm DOT nm AS nm */
+      case 122: /* xfullname ::= nm AS nm */
 {
-   yymsp[-4].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
-   if( yymsp[-4].minor.yy563 ) yymsp[-4].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+  yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0);
+  if( yylhsminor.yy203 ){
+    if( IN_RENAME_OBJECT ){
+      sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[-2].minor.yy0);
+    }else{
+      yylhsminor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+    }
+  }
 }
+  yymsp[-2].minor.yy203 = yylhsminor.yy203;
         break;
-      case 123: /* xfullname ::= nm AS nm */
+      case 123: /* xfullname ::= nm DOT nm AS nm */
 {
-   yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
-   if( yymsp[-2].minor.yy563 ) yymsp[-2].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+  yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0);
+  if( yylhsminor.yy203 ){
+    if( IN_RENAME_OBJECT ){
+      sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[-2].minor.yy0);
+    }else{
+      yylhsminor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+    }
+  }
 }
+  yymsp[-4].minor.yy203 = yylhsminor.yy203;
         break;
       case 124: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy502 = JT_INNER; }
+{ yymsp[0].minor.yy144 = JT_INNER; }
         break;
       case 125: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0);  /*X-overwrites-A*/}
+{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0);  /*X-overwrites-A*/}
         break;
       case 126: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
         break;
       case 127: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
         break;
       case 128: /* on_using ::= ON expr */
-{yymsp[-1].minor.yy421.pOn = yymsp[0].minor.yy590; yymsp[-1].minor.yy421.pUsing = 0;}
+{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;}
         break;
       case 129: /* on_using ::= USING LP idlist RP */
-{yymsp[-3].minor.yy421.pOn = 0; yymsp[-3].minor.yy421.pUsing = yymsp[-1].minor.yy204;}
+{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;}
         break;
       case 130: /* on_using ::= */
-{yymsp[1].minor.yy421.pOn = 0; yymsp[1].minor.yy421.pUsing = 0;}
+{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;}
         break;
       case 132: /* indexed_by ::= INDEXED BY nm */
 {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
@@ -181736,35 +184064,35 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
         break;
       case 135: /* orderby_opt ::= ORDER BY sortlist */
       case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145);
-{yymsp[-2].minor.yy402 = yymsp[0].minor.yy402;}
+{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;}
         break;
       case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */
 {
-  yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402,yymsp[-2].minor.yy590);
-  sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502);
+  yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454);
+  sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
 }
         break;
       case 137: /* sortlist ::= expr sortorder nulls */
 {
-  yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy590); /*A-overwrites-Y*/
-  sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502);
+  yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/
+  sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
 }
         break;
       case 138: /* sortorder ::= ASC */
-{yymsp[0].minor.yy502 = SQLITE_SO_ASC;}
+{yymsp[0].minor.yy144 = SQLITE_SO_ASC;}
         break;
       case 139: /* sortorder ::= DESC */
-{yymsp[0].minor.yy502 = SQLITE_SO_DESC;}
+{yymsp[0].minor.yy144 = SQLITE_SO_DESC;}
         break;
       case 140: /* sortorder ::= */
       case 143: /* nulls ::= */ yytestcase(yyruleno==143);
-{yymsp[1].minor.yy502 = SQLITE_SO_UNDEFINED;}
+{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;}
         break;
       case 141: /* nulls ::= NULLS FIRST */
-{yymsp[-1].minor.yy502 = SQLITE_SO_ASC;}
+{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;}
         break;
       case 142: /* nulls ::= NULLS LAST */
-{yymsp[-1].minor.yy502 = SQLITE_SO_DESC;}
+{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;}
         break;
       case 146: /* having_opt ::= */
       case 148: /* limit_opt ::= */ yytestcase(yyruleno==148);
@@ -181773,42 +184101,42 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       case 232: /* case_else ::= */ yytestcase(yyruleno==232);
       case 233: /* case_operand ::= */ yytestcase(yyruleno==233);
       case 252: /* vinto ::= */ yytestcase(yyruleno==252);
-{yymsp[1].minor.yy590 = 0;}
+{yymsp[1].minor.yy454 = 0;}
         break;
       case 147: /* having_opt ::= HAVING expr */
       case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154);
       case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156);
       case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231);
       case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251);
-{yymsp[-1].minor.yy590 = yymsp[0].minor.yy590;}
+{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;}
         break;
       case 149: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,0);}
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);}
         break;
       case 150: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);}
+{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
         break;
       case 151: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,yymsp[-2].minor.yy590);}
+{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);}
         break;
       case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
 {
-  sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy563, &yymsp[-1].minor.yy0);
-  sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy563,yymsp[0].minor.yy590,0,0);
+  sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0);
+  sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0);
 }
         break;
       case 157: /* where_opt_ret ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-1].minor.yy590 = 0;}
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;}
         break;
       case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-3].minor.yy590 = yymsp[-2].minor.yy590;}
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;}
         break;
       case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
 {
-  sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-4].minor.yy0);
-  sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy402,"set list");
-  if( yymsp[-1].minor.yy563 ){
-    SrcList *pFromClause = yymsp[-1].minor.yy563;
+  sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0);
+  sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list");
+  if( yymsp[-1].minor.yy203 ){
+    SrcList *pFromClause = yymsp[-1].minor.yy203;
     if( pFromClause->nSrc>1 ){
       Select *pSubquery;
       Token as;
@@ -181817,90 +184145,90 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       as.z = 0;
       pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
     }
-    yymsp[-5].minor.yy563 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy563, pFromClause);
+    yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause);
   }
-  sqlite3Update(pParse,yymsp[-5].minor.yy563,yymsp[-2].minor.yy402,yymsp[0].minor.yy590,yymsp[-6].minor.yy502,0,0,0);
+  sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0);
 }
         break;
       case 160: /* setlist ::= setlist COMMA nm EQ expr */
 {
-  yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[0].minor.yy590);
-  sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, 1);
+  yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
+  sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1);
 }
         break;
       case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
 {
-  yymsp[-6].minor.yy402 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy402, yymsp[-3].minor.yy204, yymsp[0].minor.yy590);
+  yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
 }
         break;
       case 162: /* setlist ::= nm EQ expr */
 {
-  yylhsminor.yy402 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy590);
-  sqlite3ExprListSetName(pParse, yylhsminor.yy402, &yymsp[-2].minor.yy0, 1);
+  yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454);
+  sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1);
 }
-  yymsp[-2].minor.yy402 = yylhsminor.yy402;
+  yymsp[-2].minor.yy14 = yylhsminor.yy14;
         break;
       case 163: /* setlist ::= LP idlist RP EQ expr */
 {
-  yymsp[-4].minor.yy402 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy204, yymsp[0].minor.yy590);
+  yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
 }
         break;
       case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
 {
-  sqlite3Insert(pParse, yymsp[-3].minor.yy563, yymsp[-1].minor.yy637, yymsp[-2].minor.yy204, yymsp[-5].minor.yy502, yymsp[0].minor.yy403);
+  sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122);
 }
         break;
       case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
 {
-  sqlite3Insert(pParse, yymsp[-4].minor.yy563, 0, yymsp[-3].minor.yy204, yymsp[-6].minor.yy502, 0);
+  sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0);
 }
         break;
       case 166: /* upsert ::= */
-{ yymsp[1].minor.yy403 = 0; }
+{ yymsp[1].minor.yy122 = 0; }
         break;
       case 167: /* upsert ::= RETURNING selcollist */
-{ yymsp[-1].minor.yy403 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy402); }
+{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); }
         break;
       case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
-{ yymsp[-11].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy402,yymsp[-6].minor.yy590,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,yymsp[0].minor.yy403);}
+{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);}
         break;
       case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
-{ yymsp[-8].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy402,yymsp[-3].minor.yy590,0,0,yymsp[0].minor.yy403); }
+{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); }
         break;
       case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */
-{ yymsp[-4].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
+{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
         break;
       case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
-{ yymsp[-7].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,0);}
+{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);}
         break;
       case 172: /* returning ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy402);}
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);}
         break;
       case 175: /* idlist_opt ::= */
-{yymsp[1].minor.yy204 = 0;}
+{yymsp[1].minor.yy132 = 0;}
         break;
       case 176: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy204 = yymsp[-1].minor.yy204;}
+{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;}
         break;
       case 177: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy204 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy204,&yymsp[0].minor.yy0);}
+{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);}
         break;
       case 178: /* idlist ::= nm */
-{yymsp[0].minor.yy204 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
         break;
       case 179: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy590 = yymsp[-1].minor.yy590;}
+{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;}
         break;
       case 180: /* expr ::= ID|INDEXED|JOIN_KW */
-{yymsp[0].minor.yy590=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
         break;
       case 181: /* expr ::= nm DOT nm */
 {
   Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
   Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
-  yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+  yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
 }
-  yymsp[-2].minor.yy590 = yylhsminor.yy590;
+  yymsp[-2].minor.yy454 = yylhsminor.yy454;
         break;
       case 182: /* expr ::= nm DOT nm DOT nm */
 {
@@ -181911,27 +184239,32 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
   if( IN_RENAME_OBJECT ){
     sqlite3RenameTokenRemap(pParse, 0, temp1);
   }
-  yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+  yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
 }
-  yymsp[-4].minor.yy590 = yylhsminor.yy590;
+  yymsp[-4].minor.yy454 = yylhsminor.yy454;
         break;
       case 183: /* term ::= NULL|FLOAT|BLOB */
       case 184: /* term ::= STRING */ yytestcase(yyruleno==184);
-{yymsp[0].minor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
         break;
       case 185: /* term ::= INTEGER */
 {
-  yylhsminor.yy590 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
-  if( yylhsminor.yy590 ) yylhsminor.yy590->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
+  int iValue;
+  if( sqlite3GetInt32(yymsp[0].minor.yy0.z, &iValue)==0 ){
+    yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 0);
+  }else{
+    yylhsminor.yy454 = sqlite3ExprInt32(pParse->db, iValue);
+  }
+  if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
 }
-  yymsp[0].minor.yy590 = yylhsminor.yy590;
+  yymsp[0].minor.yy454 = yylhsminor.yy454;
         break;
       case 186: /* expr ::= VARIABLE */
 {
   if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
     u32 n = yymsp[0].minor.yy0.n;
-    yymsp[0].minor.yy590 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
-    sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy590, n);
+    yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+    sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n);
   }else{
     /* When doing a nested parse, one can include terms in an expression
     ** that look like this:   #1 #2 ...  These terms refer to registers
@@ -181940,80 +184273,82 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
     assert( t.n>=2 );
     if( pParse->nested==0 ){
       parserSyntaxError(pParse, &t);
-      yymsp[0].minor.yy590 = 0;
+      yymsp[0].minor.yy454 = 0;
     }else{
-      yymsp[0].minor.yy590 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
-      if( yymsp[0].minor.yy590 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy590->iTable);
+      yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+      if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
     }
   }
 }
         break;
       case 187: /* expr ::= expr COLLATE ID|STRING */
 {
-  yymsp[-2].minor.yy590 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy590, &yymsp[0].minor.yy0, 1);
+  yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1);
 }
         break;
       case 188: /* expr ::= CAST LP expr AS typetoken RP */
 {
-  yymsp[-5].minor.yy590 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
-  sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy590, yymsp[-3].minor.yy590, 0);
+  yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+  sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0);
 }
         break;
       case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy502);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144);
 }
-  yymsp[-4].minor.yy590 = yylhsminor.yy590;
+  yymsp[-4].minor.yy454 = yylhsminor.yy454;
         break;
       case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy402, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy502);
-  sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-1].minor.yy402);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144);
+  sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14);
 }
-  yymsp[-7].minor.yy590 = yylhsminor.yy590;
+  yymsp[-7].minor.yy454 = yylhsminor.yy454;
         break;
       case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
 }
-  yymsp[-3].minor.yy590 = yylhsminor.yy590;
+  yymsp[-3].minor.yy454 = yylhsminor.yy454;
         break;
       case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy402, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy502);
-  sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144);
+  sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
 }
-  yymsp[-5].minor.yy590 = yylhsminor.yy590;
+  yymsp[-5].minor.yy454 = yylhsminor.yy454;
         break;
       case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy402, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy502);
-  sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483);
-  sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-2].minor.yy402);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144);
+  sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
+  sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14);
 }
-  yymsp[-8].minor.yy590 = yylhsminor.yy590;
+  yymsp[-8].minor.yy454 = yylhsminor.yy454;
         break;
       case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
-  sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+  sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
 }
-  yymsp[-4].minor.yy590 = yylhsminor.yy590;
+  yymsp[-4].minor.yy454 = yylhsminor.yy454;
         break;
       case 195: /* term ::= CTIME_KW */
 {
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
 }
-  yymsp[0].minor.yy590 = yylhsminor.yy590;
+  yymsp[0].minor.yy454 = yylhsminor.yy454;
         break;
       case 196: /* expr ::= LP nexprlist COMMA expr RP */
 {
-  ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590);
-  yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
-  if( yymsp[-4].minor.yy590 ){
-    yymsp[-4].minor.yy590->x.pList = pList;
-    if( ALWAYS(pList->nExpr) ){
-      yymsp[-4].minor.yy590->flags |= pList->a[0].pExpr->flags & EP_Propagate;
+  ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454);
+  yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+  if( yymsp[-4].minor.yy454 ){
+    int i;
+    yymsp[-4].minor.yy454->x.pList = pList;
+    for(i=0; i<pList->nExpr; i++){
+      assert( pList->a[i].pExpr!=0 );
+      yymsp[-4].minor.yy454->flags |= pList->a[i].pExpr->flags & EP_Propagate;
     }
   }else{
     sqlite3ExprListDelete(pParse->db, pList);
@@ -182021,7 +184356,7 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
 }
         break;
       case 197: /* expr ::= expr AND expr */
-{yymsp[-2].minor.yy590=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);}
+{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
         break;
       case 198: /* expr ::= expr OR expr */
       case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199);
@@ -182030,7 +184365,7 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202);
       case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203);
       case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204);
-{yymsp[-2].minor.yy590=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);}
+{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
         break;
       case 205: /* likeop ::= NOT LIKE_KW|MATCH */
 {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
@@ -182040,11 +184375,11 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
   ExprList *pList;
   int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
   yymsp[-1].minor.yy0.n &= 0x7fffffff;
-  pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy590);
-  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy590);
-  yymsp[-2].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
-  if( bNot ) yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy590, 0);
-  if( yymsp[-2].minor.yy590 ) yymsp[-2].minor.yy590->flags |= EP_InfixFunc;
+  pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454);
+  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454);
+  yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+  if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0);
+  if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc;
 }
         break;
       case 207: /* expr ::= expr likeop expr ESCAPE expr */
@@ -182052,91 +184387,88 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
   ExprList *pList;
   int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
   yymsp[-3].minor.yy0.n &= 0x7fffffff;
-  pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590);
-  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy590);
-  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590);
-  yymsp[-4].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
-  if( bNot ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
-  if( yymsp[-4].minor.yy590 ) yymsp[-4].minor.yy590->flags |= EP_InfixFunc;
+  pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454);
+  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
+  yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+  if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+  if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc;
 }
         break;
       case 208: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy590,0);}
+{yymsp[-1].minor.yy454 = sqlite3PExprIsNull(pParse,yymsp[0].major,yymsp[-1].minor.yy454);}
         break;
       case 209: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy590,0);}
+{yymsp[-2].minor.yy454 = sqlite3PExprIsNull(pParse,TK_NOTNULL,yymsp[-2].minor.yy454);}
         break;
       case 210: /* expr ::= expr IS expr */
 {
-  yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);
-  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-2].minor.yy590, TK_ISNULL);
+  yymsp[-2].minor.yy454 = sqlite3PExprIs(pParse, TK_IS, yymsp[-2].minor.yy454, yymsp[0].minor.yy454);
 }
         break;
       case 211: /* expr ::= expr IS NOT expr */
 {
-  yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy590,yymsp[0].minor.yy590);
-  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-3].minor.yy590, TK_NOTNULL);
+  yymsp[-3].minor.yy454 = sqlite3PExprIs(pParse, TK_ISNOT, yymsp[-3].minor.yy454, yymsp[0].minor.yy454);
 }
         break;
       case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */
 {
-  yymsp[-5].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy590,yymsp[0].minor.yy590);
-  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-5].minor.yy590, TK_ISNULL);
+  yymsp[-5].minor.yy454 = sqlite3PExprIs(pParse, TK_IS, yymsp[-5].minor.yy454, yymsp[0].minor.yy454);
 }
         break;
       case 213: /* expr ::= expr IS DISTINCT FROM expr */
 {
-  yymsp[-4].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy590,yymsp[0].minor.yy590);
-  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-4].minor.yy590, TK_NOTNULL);
+  yymsp[-4].minor.yy454 = sqlite3PExprIs(pParse, TK_ISNOT, yymsp[-4].minor.yy454, yymsp[0].minor.yy454);
 }
         break;
       case 214: /* expr ::= NOT expr */
       case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215);
-{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy590, 0);/*A-overwrites-B*/}
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/}
         break;
       case 216: /* expr ::= PLUS|MINUS expr */
 {
-  Expr *p = yymsp[0].minor.yy590;
+  Expr *p = yymsp[0].minor.yy454;
   u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS);
   assert( TK_UPLUS>TK_PLUS );
   assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) );
   if( p && p->op==TK_UPLUS ){
     p->op = op;
-    yymsp[-1].minor.yy590 = p;
+    yymsp[-1].minor.yy454 = p;
   }else{
-    yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, op, p, 0);
+    yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0);
     /*A-overwrites-B*/
   }
 }
         break;
       case 217: /* expr ::= expr PTR expr */
 {
-  ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy590);
-  pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy590);
-  yylhsminor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+  ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454);
+  pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454);
+  yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
 }
-  yymsp[-2].minor.yy590 = yylhsminor.yy590;
+  yymsp[-2].minor.yy454 = yylhsminor.yy454;
         break;
       case 218: /* between_op ::= BETWEEN */
       case 221: /* in_op ::= IN */ yytestcase(yyruleno==221);
-{yymsp[0].minor.yy502 = 0;}
+{yymsp[0].minor.yy144 = 0;}
         break;
       case 220: /* expr ::= expr between_op expr AND expr */
 {
-  ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590);
-  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590);
-  yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy590, 0);
-  if( yymsp[-4].minor.yy590 ){
-    yymsp[-4].minor.yy590->x.pList = pList;
+  ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
+  yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0);
+  if( yymsp[-4].minor.yy454 ){
+    yymsp[-4].minor.yy454->x.pList = pList;
+    sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
   }else{
     sqlite3ExprListDelete(pParse->db, pList);
   }
-  if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
+  if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
 }
         break;
       case 223: /* expr ::= expr in_op LP exprlist RP */
 {
-    if( yymsp[-1].minor.yy402==0 ){
+    if( yymsp[-1].minor.yy14==0 ){
       /* Expressions of the form
       **
       **      expr1 IN ()
@@ -182149,145 +184481,145 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
       ** it is or not) and if it is an aggregate, that could change the meaning
       ** of the whole query.
       */
-      Expr *pB = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy502 ? "true" : "false");
+      Expr *pB = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false");
       if( pB ) sqlite3ExprIdToTrueFalse(pB);
-      if( !ExprHasProperty(yymsp[-4].minor.yy590, EP_HasFunc) ){
-        sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy590);
-        yymsp[-4].minor.yy590 = pB;
+      if( !ExprHasProperty(yymsp[-4].minor.yy454, EP_HasFunc) ){
+        sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454);
+        yymsp[-4].minor.yy454 = pB;
       }else{
-        yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, yymsp[-3].minor.yy502 ? TK_OR : TK_AND, pB, yymsp[-4].minor.yy590);
+        yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, yymsp[-3].minor.yy144 ? TK_OR : TK_AND, pB, yymsp[-4].minor.yy454);
       }
     }else{
-      Expr *pRHS = yymsp[-1].minor.yy402->a[0].pExpr;
-      if( yymsp[-1].minor.yy402->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy590->op!=TK_VECTOR ){
-        yymsp[-1].minor.yy402->a[0].pExpr = 0;
-        sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402);
+      Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr;
+      if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){
+        yymsp[-1].minor.yy14->a[0].pExpr = 0;
+        sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
         pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
-        yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy590, pRHS);
-      }else if( yymsp[-1].minor.yy402->nExpr==1 && pRHS->op==TK_SELECT ){
-        yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
-        sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pRHS->x.pSelect);
+        yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS);
+      }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){
+        yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+        sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect);
         pRHS->x.pSelect = 0;
-        sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402);
+        sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
       }else{
-        yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
-        if( yymsp[-4].minor.yy590==0 ){
-          sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402);
-        }else if( yymsp[-4].minor.yy590->pLeft->op==TK_VECTOR ){
-          int nExpr = yymsp[-4].minor.yy590->pLeft->x.pList->nExpr;
-          Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy402);
+        yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+        if( yymsp[-4].minor.yy454==0 ){
+          sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+        }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){
+          int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr;
+          Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14);
           if( pSelectRHS ){
             parserDoubleLinkSelect(pParse, pSelectRHS);
-            sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelectRHS);
+            sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS);
           }
         }else{
-          yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy402;
-          sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590);
+          yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14;
+          sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
         }
       }
-      if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
+      if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
     }
   }
         break;
       case 224: /* expr ::= LP select RP */
 {
-    yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
-    sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy590, yymsp[-1].minor.yy637);
+    yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+    sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555);
   }
         break;
       case 225: /* expr ::= expr in_op LP select RP */
 {
-    yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
-    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, yymsp[-1].minor.yy637);
-    if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
+    yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555);
+    if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
   }
         break;
       case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */
 {
     SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
     Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
-    if( yymsp[0].minor.yy402 )  sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy402);
-    yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
-    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelect);
-    if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
+    if( yymsp[0].minor.yy14 )  sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14);
+    yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect);
+    if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
   }
         break;
       case 227: /* expr ::= EXISTS LP select RP */
 {
     Expr *p;
-    p = yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
-    sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy637);
+    p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+    sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555);
   }
         break;
       case 228: /* expr ::= CASE case_operand case_exprlist case_else END */
 {
-  yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy590, 0);
-  if( yymsp[-4].minor.yy590 ){
-    yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy590 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590) : yymsp[-2].minor.yy402;
-    sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590);
+  yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0);
+  if( yymsp[-4].minor.yy454 ){
+    yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14;
+    sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
   }else{
-    sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy402);
-    sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590);
+    sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14);
+    sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
   }
 }
         break;
       case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
 {
-  yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[-2].minor.yy590);
-  yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[0].minor.yy590);
+  yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
+  yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
 }
         break;
       case 230: /* case_exprlist ::= WHEN expr THEN expr */
 {
-  yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590);
-  yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy402, yymsp[0].minor.yy590);
+  yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+  yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454);
 }
         break;
       case 235: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[0].minor.yy590);}
+{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);}
         break;
       case 236: /* nexprlist ::= expr */
-{yymsp[0].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy590); /*A-overwrites-Y*/}
+{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/}
         break;
       case 238: /* paren_exprlist ::= LP exprlist RP */
       case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243);
-{yymsp[-2].minor.yy402 = yymsp[-1].minor.yy402;}
+{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;}
         break;
       case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
 {
   sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
-                     sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy402, yymsp[-10].minor.yy502,
-                      &yymsp[-11].minor.yy0, yymsp[0].minor.yy590, SQLITE_SO_ASC, yymsp[-8].minor.yy502, SQLITE_IDXTYPE_APPDEF);
+                     sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144,
+                      &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF);
   if( IN_RENAME_OBJECT && pParse->pNewIndex ){
     sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
   }
 }
         break;
       case 240: /* uniqueflag ::= UNIQUE */
-      case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282);
-{yymsp[0].minor.yy502 = OE_Abort;}
+      case 281: /* raisetype ::= ABORT */ yytestcase(yyruleno==281);
+{yymsp[0].minor.yy144 = OE_Abort;}
         break;
       case 241: /* uniqueflag ::= */
-{yymsp[1].minor.yy502 = OE_None;}
+{yymsp[1].minor.yy144 = OE_None;}
         break;
       case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */
 {
-  yymsp[-4].minor.yy402 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502);
+  yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144);
 }
         break;
       case 245: /* eidlist ::= nm collate sortorder */
 {
-  yymsp[-2].minor.yy402 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); /*A-overwrites-Y*/
+  yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/
 }
         break;
       case 248: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy563, yymsp[-1].minor.yy502);}
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);}
         break;
       case 249: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy590);}
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);}
         break;
       case 250: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy590);}
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);}
         break;
       case 253: /* cmd ::= PRAGMA nm dbnm */
 {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
@@ -182309,12 +184641,12 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
   Token all;
   all.z = yymsp[-3].minor.yy0.z;
   all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
-  sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy319, &all);
+  sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all);
 }
         break;
       case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
 {
-  sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy502, yymsp[-4].minor.yy28.a, yymsp[-4].minor.yy28.b, yymsp[-2].minor.yy563, yymsp[0].minor.yy590, yymsp[-10].minor.yy502, yymsp[-8].minor.yy502);
+  sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144);
   yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
 #ifdef SQLITE_DEBUG
   assert( pParse->isCreate ); /* Set by createkw reduce action */
@@ -182323,421 +184655,437 @@ static YYACTIONTYPE SQLITE_APICALL yy_reduce(
 }
         break;
       case 262: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/ }
+{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ }
         break;
       case 263: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy502 = TK_INSTEAD;}
+{ yymsp[-1].minor.yy144 = TK_INSTEAD;}
         break;
       case 264: /* trigger_time ::= */
-{ yymsp[1].minor.yy502 = TK_BEFORE; }
+{ yymsp[1].minor.yy144 = TK_BEFORE; }
         break;
       case 265: /* trigger_event ::= DELETE|INSERT */
       case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266);
-{yymsp[0].minor.yy28.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy28.b = 0;}
+{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;}
         break;
       case 267: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy28.a = TK_UPDATE; yymsp[-2].minor.yy28.b = yymsp[0].minor.yy204;}
+{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;}
         break;
       case 268: /* when_clause ::= */
-      case 287: /* key_opt ::= */ yytestcase(yyruleno==287);
-{ yymsp[1].minor.yy590 = 0; }
+      case 286: /* key_opt ::= */ yytestcase(yyruleno==286);
+{ yymsp[1].minor.yy454 = 0; }
         break;
       case 269: /* when_clause ::= WHEN expr */
-      case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288);
-{ yymsp[-1].minor.yy590 = yymsp[0].minor.yy590; }
+      case 287: /* key_opt ::= KEY expr */ yytestcase(yyruleno==287);
+{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; }
         break;
       case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
 {
-  assert( yymsp[-2].minor.yy319!=0 );
-  yymsp[-2].minor.yy319->pLast->pNext = yymsp[-1].minor.yy319;
-  yymsp[-2].minor.yy319->pLast = yymsp[-1].minor.yy319;
+  yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427;
+  yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427;
 }
         break;
       case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */
 {
-  assert( yymsp[-1].minor.yy319!=0 );
-  yymsp[-1].minor.yy319->pLast = yymsp[-1].minor.yy319;
-}
-        break;
-      case 272: /* trnm ::= nm DOT nm */
-{
-  yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
-  sqlite3ErrorMsg(pParse,
-        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
-        "statements within triggers");
+  yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427;
 }
         break;
-      case 273: /* tridxby ::= INDEXED BY nm */
+      case 272: /* tridxby ::= INDEXED BY nm */
 {
   sqlite3ErrorMsg(pParse,
         "the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
         "within triggers");
 }
         break;
-      case 274: /* tridxby ::= NOT INDEXED */
+      case 273: /* tridxby ::= NOT INDEXED */
 {
   sqlite3ErrorMsg(pParse,
         "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
         "within triggers");
 }
         break;
-      case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-{yylhsminor.yy319 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy563, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590, yymsp[-7].minor.yy502, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy342);}
-  yymsp[-8].minor.yy319 = yylhsminor.yy319;
+      case 274: /* trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt */
+{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, yymsp[-6].minor.yy203, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);}
+  yymsp[-8].minor.yy427 = yylhsminor.yy427;
         break;
-      case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+      case 275: /* trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt */
 {
-   yylhsminor.yy319 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy204,yymsp[-2].minor.yy637,yymsp[-6].minor.yy502,yymsp[-1].minor.yy403,yymsp[-7].minor.yy342,yymsp[0].minor.yy342);/*yylhsminor.yy319-overwrites-yymsp[-6].minor.yy502*/
+   yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,yymsp[-4].minor.yy203,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/
 }
-  yymsp[-7].minor.yy319 = yylhsminor.yy319;
+  yymsp[-7].minor.yy427 = yylhsminor.yy427;
         break;
-      case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy319 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy590, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy342);}
-  yymsp[-5].minor.yy319 = yylhsminor.yy319;
+      case 276: /* trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt */
+{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);}
+  yymsp[-5].minor.yy427 = yylhsminor.yy427;
         break;
-      case 278: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy319 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy637, yymsp[-2].minor.yy342, yymsp[0].minor.yy342); /*yylhsminor.yy319-overwrites-yymsp[-1].minor.yy637*/}
-  yymsp[-2].minor.yy319 = yylhsminor.yy319;
+      case 277: /* trigger_cmd ::= scanpt select scanpt */
+{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/}
+  yymsp[-2].minor.yy427 = yylhsminor.yy427;
         break;
-      case 279: /* expr ::= RAISE LP IGNORE RP */
+      case 278: /* expr ::= RAISE LP IGNORE RP */
 {
-  yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
-  if( yymsp[-3].minor.yy590 ){
-    yymsp[-3].minor.yy590->affExpr = OE_Ignore;
+  yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+  if( yymsp[-3].minor.yy454 ){
+    yymsp[-3].minor.yy454->affExpr = OE_Ignore;
   }
 }
         break;
-      case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */
+      case 279: /* expr ::= RAISE LP raisetype COMMA expr RP */
 {
-  yymsp[-5].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy590, 0);
-  if( yymsp[-5].minor.yy590 ) {
-    yymsp[-5].minor.yy590->affExpr = (char)yymsp[-3].minor.yy502;
+  yymsp[-5].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy454, 0);
+  if( yymsp[-5].minor.yy454 ) {
+    yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144;
   }
 }
         break;
-      case 281: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy502 = OE_Rollback;}
+      case 280: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy144 = OE_Rollback;}
         break;
-      case 283: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy502 = OE_Fail;}
+      case 282: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy144 = OE_Fail;}
         break;
-      case 284: /* cmd ::= DROP TRIGGER ifexists fullname */
+      case 283: /* cmd ::= DROP TRIGGER ifexists fullname */
 {
-  sqlite3DropTrigger(pParse,yymsp[0].minor.yy563,yymsp[-1].minor.yy502);
+  sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144);
 }
         break;
-      case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+      case 284: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
 {
-  sqlite3Attach(pParse, yymsp[-3].minor.yy590, yymsp[-1].minor.yy590, yymsp[0].minor.yy590);
+  sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454);
 }
         break;
-      case 286: /* cmd ::= DETACH database_kw_opt expr */
+      case 285: /* cmd ::= DETACH database_kw_opt expr */
 {
-  sqlite3Detach(pParse, yymsp[0].minor.yy590);
+  sqlite3Detach(pParse, yymsp[0].minor.yy454);
 }
         break;
-      case 289: /* cmd ::= REINDEX */
+      case 288: /* cmd ::= REINDEX */
 {sqlite3Reindex(pParse, 0, 0);}
         break;
-      case 290: /* cmd ::= REINDEX nm dbnm */
+      case 289: /* cmd ::= REINDEX nm dbnm */
 {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
         break;
-      case 291: /* cmd ::= ANALYZE */
+      case 290: /* cmd ::= ANALYZE */
 {sqlite3Analyze(pParse, 0, 0);}
         break;
-      case 292: /* cmd ::= ANALYZE nm dbnm */
+      case 291: /* cmd ::= ANALYZE nm dbnm */
 {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
         break;
-      case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+      case 292: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
 {
-  sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy563,&yymsp[0].minor.yy0);
+  sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0);
 }
         break;
-      case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+      case 293: /* cmd ::= alter_add carglist */
 {
   yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
   sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
+}
+        break;
+      case 294: /* alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken */
+{
+  disableLookaside(pParse);
+  sqlite3AlterBeginAddColumn(pParse, yymsp[-4].minor.yy203);
+  sqlite3AddColumn(pParse, yymsp[-1].minor.yy0, yymsp[0].minor.yy0);
+  yymsp[-6].minor.yy0 = yymsp[-1].minor.yy0;
 }
         break;
       case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
 {
-  sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy563, &yymsp[0].minor.yy0);
+  sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0);
 }
         break;
-      case 296: /* add_column_fullname ::= fullname */
+      case 296: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
 {
-  disableLookaside(pParse);
-  sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy563);
+  sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
 }
         break;
-      case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+      case 297: /* cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm */
 {
-  sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy563, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+  sqlite3AlterDropConstraint(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0, 0);
 }
         break;
-      case 298: /* cmd ::= create_vtab */
+      case 298: /* cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL */
+{
+  sqlite3AlterDropConstraint(pParse, yymsp[-6].minor.yy203, 0, &yymsp[-3].minor.yy0);
+}
+        break;
+      case 299: /* cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf */
+{
+  sqlite3AlterSetNotNull(pParse, yymsp[-7].minor.yy203, &yymsp[-4].minor.yy0, &yymsp[-2].minor.yy0);
+}
+        break;
+      case 300: /* cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf */
+{
+  sqlite3AlterAddConstraint(pParse, yymsp[-8].minor.yy203, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy0.z+1, (yymsp[-1].minor.yy0.z-yymsp[-3].minor.yy0.z-1), yymsp[-2].minor.yy454);
+}
+        break;
+      case 301: /* cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf */
+{
+  sqlite3AlterAddConstraint(pParse, yymsp[-6].minor.yy203, &yymsp[-4].minor.yy0, 0, yymsp[-3].minor.yy0.z+1, (yymsp[-1].minor.yy0.z-yymsp[-3].minor.yy0.z-1), yymsp[-2].minor.yy454);
+}
+        break;
+      case 302: /* cmd ::= create_vtab */
 {sqlite3VtabFinishParse(pParse,0);}
         break;
-      case 299: /* cmd ::= create_vtab LP vtabarglist RP */
+      case 303: /* cmd ::= create_vtab LP vtabarglist RP */
 {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
         break;
-      case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+      case 304: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
 {
-    sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy502);
+    sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144);
 }
         break;
-      case 301: /* vtabarg ::= */
+      case 305: /* vtabarg ::= */
 {sqlite3VtabArgInit(pParse);}
         break;
-      case 302: /* vtabargtoken ::= ANY */
-      case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303);
-      case 304: /* lp ::= LP */ yytestcase(yyruleno==304);
+      case 306: /* vtabargtoken ::= ANY */
+      case 307: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==307);
+      case 308: /* lp ::= LP */ yytestcase(yyruleno==308);
 {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
         break;
-      case 305: /* with ::= WITH wqlist */
-      case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy125, 1); }
+      case 309: /* with ::= WITH wqlist */
+      case 310: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==310);
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); }
         break;
-      case 307: /* wqas ::= AS */
-{yymsp[0].minor.yy444 = M10d_Any;}
+      case 311: /* wqas ::= AS */
+{yymsp[0].minor.yy462 = M10d_Any;}
         break;
-      case 308: /* wqas ::= AS MATERIALIZED */
-{yymsp[-1].minor.yy444 = M10d_Yes;}
+      case 312: /* wqas ::= AS MATERIALIZED */
+{yymsp[-1].minor.yy462 = M10d_Yes;}
         break;
-      case 309: /* wqas ::= AS NOT MATERIALIZED */
-{yymsp[-2].minor.yy444 = M10d_No;}
+      case 313: /* wqas ::= AS NOT MATERIALIZED */
+{yymsp[-2].minor.yy462 = M10d_No;}
         break;
-      case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */
+      case 314: /* wqitem ::= withnm eidlist_opt wqas LP select RP */
 {
-  yymsp[-5].minor.yy361 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy402, yymsp[-1].minor.yy637, yymsp[-3].minor.yy444); /*A-overwrites-X*/
+  yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/
 }
         break;
-      case 311: /* withnm ::= nm */
+      case 315: /* withnm ::= nm */
 {pParse->bHasWith = 1;}
         break;
-      case 312: /* wqlist ::= wqitem */
+      case 316: /* wqlist ::= wqitem */
 {
-  yymsp[0].minor.yy125 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy361); /*A-overwrites-X*/
+  yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/
 }
         break;
-      case 313: /* wqlist ::= wqlist COMMA wqitem */
+      case 317: /* wqlist ::= wqlist COMMA wqitem */
 {
-  yymsp[-2].minor.yy125 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy125, yymsp[0].minor.yy361);
+  yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67);
 }
         break;
-      case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+      case 318: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
 {
-  assert( yymsp[0].minor.yy483!=0 );
-  sqlite3WindowChain(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy483);
-  yymsp[0].minor.yy483->pNextWin = yymsp[-2].minor.yy483;
-  yylhsminor.yy483 = yymsp[0].minor.yy483;
+  assert( yymsp[0].minor.yy211!=0 );
+  sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211);
+  yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211;
+  yylhsminor.yy211 = yymsp[0].minor.yy211;
 }
-  yymsp[-2].minor.yy483 = yylhsminor.yy483;
+  yymsp[-2].minor.yy211 = yylhsminor.yy211;
         break;
-      case 315: /* windowdefn ::= nm AS LP window RP */
+      case 319: /* windowdefn ::= nm AS LP window RP */
 {
-  if( ALWAYS(yymsp[-1].minor.yy483) ){
-    yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
+  if( ALWAYS(yymsp[-1].minor.yy211) ){
+    yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
   }
-  yylhsminor.yy483 = yymsp[-1].minor.yy483;
+  yylhsminor.yy211 = yymsp[-1].minor.yy211;
 }
-  yymsp[-4].minor.yy483 = yylhsminor.yy483;
+  yymsp[-4].minor.yy211 = yylhsminor.yy211;
         break;
-      case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+      case 320: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
 {
-  yymsp[-4].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, 0);
+  yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0);
 }
         break;
-      case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+      case 321: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
 {
-  yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, &yymsp[-5].minor.yy0);
+  yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0);
 }
-  yymsp[-5].minor.yy483 = yylhsminor.yy483;
+  yymsp[-5].minor.yy211 = yylhsminor.yy211;
         break;
-      case 318: /* window ::= ORDER BY sortlist frame_opt */
+      case 322: /* window ::= ORDER BY sortlist frame_opt */
 {
-  yymsp[-3].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, 0);
+  yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0);
 }
         break;
-      case 319: /* window ::= nm ORDER BY sortlist frame_opt */
+      case 323: /* window ::= nm ORDER BY sortlist frame_opt */
 {
-  yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0);
+  yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0);
 }
-  yymsp[-4].minor.yy483 = yylhsminor.yy483;
+  yymsp[-4].minor.yy211 = yylhsminor.yy211;
         break;
-      case 320: /* window ::= nm frame_opt */
+      case 324: /* window ::= nm frame_opt */
 {
-  yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, 0, &yymsp[-1].minor.yy0);
+  yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0);
 }
-  yymsp[-1].minor.yy483 = yylhsminor.yy483;
+  yymsp[-1].minor.yy211 = yylhsminor.yy211;
         break;
-      case 321: /* frame_opt ::= */
+      case 325: /* frame_opt ::= */
 {
-  yymsp[1].minor.yy483 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
+  yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
 }
         break;
-      case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+      case 326: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
 {
-  yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy502, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy444);
+  yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462);
 }
-  yymsp[-2].minor.yy483 = yylhsminor.yy483;
+  yymsp[-2].minor.yy211 = yylhsminor.yy211;
         break;
-      case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+      case 327: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
 {
-  yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy502, yymsp[-3].minor.yy205.eType, yymsp[-3].minor.yy205.pExpr, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, yymsp[0].minor.yy444);
+  yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462);
 }
-  yymsp[-5].minor.yy483 = yylhsminor.yy483;
+  yymsp[-5].minor.yy211 = yylhsminor.yy211;
         break;
-      case 325: /* frame_bound_s ::= frame_bound */
-      case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327);
-{yylhsminor.yy205 = yymsp[0].minor.yy205;}
-  yymsp[0].minor.yy205 = yylhsminor.yy205;
+      case 329: /* frame_bound_s ::= frame_bound */
+      case 331: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==331);
+{yylhsminor.yy509 = yymsp[0].minor.yy509;}
+  yymsp[0].minor.yy509 = yylhsminor.yy509;
         break;
-      case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */
-      case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328);
-      case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330);
-{yylhsminor.yy205.eType = yymsp[-1].major; yylhsminor.yy205.pExpr = 0;}
-  yymsp[-1].minor.yy205 = yylhsminor.yy205;
+      case 330: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+      case 332: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==332);
+      case 334: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==334);
+{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;}
+  yymsp[-1].minor.yy509 = yylhsminor.yy509;
         break;
-      case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */
-{yylhsminor.yy205.eType = yymsp[0].major; yylhsminor.yy205.pExpr = yymsp[-1].minor.yy590;}
-  yymsp[-1].minor.yy205 = yylhsminor.yy205;
+      case 333: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;}
+  yymsp[-1].minor.yy509 = yylhsminor.yy509;
         break;
-      case 331: /* frame_exclude_opt ::= */
-{yymsp[1].minor.yy444 = 0;}
+      case 335: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy462 = 0;}
         break;
-      case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
-{yymsp[-1].minor.yy444 = yymsp[0].minor.yy444;}
+      case 336: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;}
         break;
-      case 333: /* frame_exclude ::= NO OTHERS */
-      case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334);
-{yymsp[-1].minor.yy444 = yymsp[-1].major; /*A-overwrites-X*/}
+      case 337: /* frame_exclude ::= NO OTHERS */
+      case 338: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==338);
+{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/}
         break;
-      case 335: /* frame_exclude ::= GROUP|TIES */
-{yymsp[0].minor.yy444 = yymsp[0].major; /*A-overwrites-X*/}
+      case 339: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/}
         break;
-      case 336: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; }
+      case 340: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; }
         break;
-      case 337: /* filter_over ::= filter_clause over_clause */
+      case 341: /* filter_over ::= filter_clause over_clause */
 {
-  if( yymsp[0].minor.yy483 ){
-    yymsp[0].minor.yy483->pFilter = yymsp[-1].minor.yy590;
+  if( yymsp[0].minor.yy211 ){
+    yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454;
   }else{
-    sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590);
+    sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
   }
-  yylhsminor.yy483 = yymsp[0].minor.yy483;
+  yylhsminor.yy211 = yymsp[0].minor.yy211;
 }
-  yymsp[-1].minor.yy483 = yylhsminor.yy483;
+  yymsp[-1].minor.yy211 = yylhsminor.yy211;
         break;
-      case 338: /* filter_over ::= over_clause */
+      case 342: /* filter_over ::= over_clause */
 {
-  yylhsminor.yy483 = yymsp[0].minor.yy483;
+  yylhsminor.yy211 = yymsp[0].minor.yy211;
 }
-  yymsp[0].minor.yy483 = yylhsminor.yy483;
+  yymsp[0].minor.yy211 = yylhsminor.yy211;
         break;
-      case 339: /* filter_over ::= filter_clause */
+      case 343: /* filter_over ::= filter_clause */
 {
-  yylhsminor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
-  if( yylhsminor.yy483 ){
-    yylhsminor.yy483->eFrmType = TK_FILTER;
-    yylhsminor.yy483->pFilter = yymsp[0].minor.yy590;
+  yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+  if( yylhsminor.yy211 ){
+    yylhsminor.yy211->eFrmType = TK_FILTER;
+    yylhsminor.yy211->pFilter = yymsp[0].minor.yy454;
   }else{
-    sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy590);
+    sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454);
   }
 }
-  yymsp[0].minor.yy483 = yylhsminor.yy483;
+  yymsp[0].minor.yy211 = yylhsminor.yy211;
         break;
-      case 340: /* over_clause ::= OVER LP window RP */
+      case 344: /* over_clause ::= OVER LP window RP */
 {
-  yymsp[-3].minor.yy483 = yymsp[-1].minor.yy483;
-  assert( yymsp[-3].minor.yy483!=0 );
+  yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211;
+  assert( yymsp[-3].minor.yy211!=0 );
 }
         break;
-      case 341: /* over_clause ::= OVER nm */
+      case 345: /* over_clause ::= OVER nm */
 {
-  yymsp[-1].minor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
-  if( yymsp[-1].minor.yy483 ){
-    yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+  yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+  if( yymsp[-1].minor.yy211 ){
+    yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
   }
 }
         break;
-      case 342: /* filter_clause ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy590 = yymsp[-1].minor.yy590; }
+      case 346: /* filter_clause ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; }
         break;
-      case 343: /* term ::= QNUMBER */
+      case 347: /* term ::= QNUMBER */
 {
-  yylhsminor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0);
-  sqlite3DequoteNumber(pParse, yylhsminor.yy590);
+  yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0);
+  sqlite3DequoteNumber(pParse, yylhsminor.yy454);
 }
-  yymsp[0].minor.yy590 = yylhsminor.yy590;
+  yymsp[0].minor.yy454 = yylhsminor.yy454;
         break;
       default:
-      /* (344) input ::= cmdlist */ yytestcase(yyruleno==344);
-      /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345);
-      /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346);
-      /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347);
-      /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348);
-      /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349);
-      /* (350) trans_opt ::= */ yytestcase(yyruleno==350);
-      /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351);
-      /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352);
-      /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353);
-      /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354);
-      /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355);
-      /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356);
-      /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357);
-      /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358);
-      /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359);
-      /* (360) nm ::= STRING */ yytestcase(yyruleno==360);
-      /* (361) typetoken ::= typename */ yytestcase(yyruleno==361);
-      /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362);
-      /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363);
-      /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364);
-      /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365);
-      /* (366) carglist ::= */ yytestcase(yyruleno==366);
-      /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367);
-      /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368);
-      /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369);
-      /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370);
-      /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371);
-      /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372);
-      /* (373) tconscomma ::= */ yytestcase(yyruleno==373);
-      /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374);
-      /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375);
-      /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376);
-      /* (377) oneselect ::= values */ yytestcase(yyruleno==377);
-      /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378);
-      /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379);
-      /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380);
-      /* (381) returning ::= */ yytestcase(yyruleno==381);
-      /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382);
-      /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383);
-      /* (384) case_operand ::= expr */ yytestcase(yyruleno==384);
-      /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385);
-      /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386);
-      /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387);
-      /* (388) nmnum ::= ON */ yytestcase(yyruleno==388);
-      /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389);
-      /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390);
-      /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391);
-      /* (392) foreach_clause ::= */ yytestcase(yyruleno==392);
-      /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393);
-      /* (394) trnm ::= nm */ yytestcase(yyruleno==394);
-      /* (395) tridxby ::= */ yytestcase(yyruleno==395);
-      /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396);
-      /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397);
-      /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398);
-      /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399);
-      /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400);
-      /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401);
-      /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402);
-      /* (403) anylist ::= */ yytestcase(yyruleno==403);
-      /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404);
-      /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405);
-      /* (406) with ::= */ yytestcase(yyruleno==406);
-      /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407);
-      /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408);
+      /* (348) input ::= cmdlist */ yytestcase(yyruleno==348);
+      /* (349) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==349);
+      /* (350) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=350);
+      /* (351) ecmd ::= SEMI */ yytestcase(yyruleno==351);
+      /* (352) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==352);
+      /* (353) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=353);
+      /* (354) trans_opt ::= */ yytestcase(yyruleno==354);
+      /* (355) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==355);
+      /* (356) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==356);
+      /* (357) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==357);
+      /* (358) savepoint_opt ::= */ yytestcase(yyruleno==358);
+      /* (359) cmd ::= create_table create_table_args */ yytestcase(yyruleno==359);
+      /* (360) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=360);
+      /* (361) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==361);
+      /* (362) columnlist ::= columnname carglist */ yytestcase(yyruleno==362);
+      /* (363) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==363);
+      /* (364) nm ::= STRING */ yytestcase(yyruleno==364);
+      /* (365) typetoken ::= typename */ yytestcase(yyruleno==365);
+      /* (366) typename ::= ID|STRING */ yytestcase(yyruleno==366);
+      /* (367) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=367);
+      /* (368) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=368);
+      /* (369) carglist ::= carglist ccons */ yytestcase(yyruleno==369);
+      /* (370) carglist ::= */ yytestcase(yyruleno==370);
+      /* (371) ccons ::= NULL onconf */ yytestcase(yyruleno==371);
+      /* (372) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==372);
+      /* (373) ccons ::= AS generated */ yytestcase(yyruleno==373);
+      /* (374) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==374);
+      /* (375) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==375);
+      /* (376) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=376);
+      /* (377) tconscomma ::= */ yytestcase(yyruleno==377);
+      /* (378) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=378);
+      /* (379) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=379);
+      /* (380) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=380);
+      /* (381) oneselect ::= values */ yytestcase(yyruleno==381);
+      /* (382) sclp ::= selcollist COMMA */ yytestcase(yyruleno==382);
+      /* (383) as ::= ID|STRING */ yytestcase(yyruleno==383);
+      /* (384) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=384);
+      /* (385) returning ::= */ yytestcase(yyruleno==385);
+      /* (386) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=386);
+      /* (387) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==387);
+      /* (388) case_operand ::= expr */ yytestcase(yyruleno==388);
+      /* (389) exprlist ::= nexprlist */ yytestcase(yyruleno==389);
+      /* (390) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=390);
+      /* (391) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=391);
+      /* (392) nmnum ::= ON */ yytestcase(yyruleno==392);
+      /* (393) nmnum ::= DELETE */ yytestcase(yyruleno==393);
+      /* (394) nmnum ::= DEFAULT */ yytestcase(yyruleno==394);
+      /* (395) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==395);
+      /* (396) foreach_clause ::= */ yytestcase(yyruleno==396);
+      /* (397) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==397);
+      /* (398) tridxby ::= */ yytestcase(yyruleno==398);
+      /* (399) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==399);
+      /* (400) database_kw_opt ::= */ yytestcase(yyruleno==400);
+      /* (401) kwcolumn_opt ::= */ yytestcase(yyruleno==401);
+      /* (402) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==402);
+      /* (403) vtabarglist ::= vtabarg */ yytestcase(yyruleno==403);
+      /* (404) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==404);
+      /* (405) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==405);
+      /* (406) anylist ::= */ yytestcase(yyruleno==406);
+      /* (407) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==407);
+      /* (408) anylist ::= anylist ANY */ yytestcase(yyruleno==408);
+      /* (409) with ::= */ yytestcase(yyruleno==409);
+      /* (410) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=410);
+      /* (411) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=411);
         break;
 /********** End reduce actions ************************************************/
   };
@@ -183516,8 +185864,8 @@ static const unsigned char aKWCode[148] = {0,
 /* Check to see if z[0..n-1] is a keyword. If it is, write the
 ** parser symbol code for that keyword into *pType.  Always
 ** return the integer n (the length of the token). */
-static int SQLITE_APICALL keywordCode(const char *z, int n, int *pType){
-  int i, j;
+static i64 SQLITE_APICALL keywordCode(const char *z, i64 n, int *pType){
+  i64 i, j;
   const char *zKW;
   assert( n>=2 );
   i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127;
@@ -184071,7 +186419,7 @@ SQLITE_PRIVATE i64 SQLITE_APICALL sqlite3GetToken(const unsigned char *z, int *t
     }
     case CC_DOLLAR:
     case CC_VARALPHA: {
-      int n = 0;
+      i64 n = 0;
       testcase( z[0]=='$' );  testcase( z[0]=='@' );
       testcase( z[0]==':' );  testcase( z[0]=='#' );
       *tokenType = TK_VARIABLE;
@@ -184167,7 +186515,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3RunParser(Parse *pParse, const char *zS
   int tokenType;                  /* type of the next token */
   int lastTokenParsed = -1;       /* type of the previous token */
   sqlite3 *db = pParse->db;       /* The database connection */
-  int mxSqlLen;                   /* Max length of an SQL string */
+  i64 mxSqlLen;                   /* Max length of an SQL string */
   Parse *pParentParse = 0;        /* Outer parse context, if any */
 #ifdef sqlite3Parser_ENGINEALWAYSONSTACK
   yyParser sEngine;    /* Space to hold the Lemon-generated Parser object */
@@ -184297,7 +186645,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3RunParser(Parse *pParse, const char *zS
   }
   if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
     if( pParse->zErrMsg==0 ){
-      pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
+      pParse->zErrMsg = sqlite3DbStrDup(db, sqlite3ErrStr(pParse->rc));
     }
     if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){
       sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
@@ -184378,7 +186726,7 @@ SQLITE_PRIVATE char *sqlite3Normalize(
           sqlite3_str_append(pStr, " NULL", 5);
           break;
         }
-        /* Fall through */
+        /* no break */ deliberate_fall_through
       }
       case TK_STRING:
       case TK_INTEGER:
@@ -184442,7 +186790,7 @@ SQLITE_PRIVATE char *sqlite3Normalize(
       }
       case TK_SELECT: {
         iStartIN = 0;
-        /* fall through */
+        /* no break */ deliberate_fall_through
       }
       default: {
         if( sqlite3IsIdChar(zSql[i]) ) addSpaceSeparator(pStr);
@@ -185823,6 +188171,14 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
       rc = setupLookaside(db, pBuf, sz, cnt);
       break;
     }
+    case SQLITE_DBCONFIG_FP_DIGITS: {
+      int nIn = va_arg(ap, int);
+      int *pOut = va_arg(ap, int*);
+      if( nIn>3 && nIn<24 ) db->nFpDigit = (u8)nIn;
+      if( pOut ) *pOut = db->nFpDigit;
+      rc = SQLITE_OK;
+      break;
+    }
     default: {
       static const struct {
         int op;      /* The opcode */
@@ -187378,6 +189734,9 @@ SQLITE_API void *SQLITE_APICALL sqlite3_wal_hook(
   sqlite3_mutex_leave(db->mutex);
   return pRet;
 #else
+  UNUSED_PARAMETER(db);
+  UNUSED_PARAMETER(xCallback);
+  UNUSED_PARAMETER(pArg);
   return 0;
 #endif
 }
@@ -187393,6 +189752,11 @@ SQLITE_API int SQLITE_APICALL sqlite3_wal_checkpoint_v2(
   int *pnCkpt                     /* OUT: Total number of frames checkpointed */
 ){
 #ifdef SQLITE_OMIT_WAL
+  UNUSED_PARAMETER(db);
+  UNUSED_PARAMETER(zDb);
+  UNUSED_PARAMETER(eMode);
+  UNUSED_PARAMETER(pnLog);
+  UNUSED_PARAMETER(pnCkpt);
   return SQLITE_OK;
 #else
   int rc;                         /* Return code */
@@ -187406,11 +189770,12 @@ SQLITE_API int SQLITE_APICALL sqlite3_wal_checkpoint_v2(
   if( pnLog ) *pnLog = -1;
   if( pnCkpt ) *pnCkpt = -1;
 
+  assert( SQLITE_CHECKPOINT_NOOP==-1 );
   assert( SQLITE_CHECKPOINT_PASSIVE==0 );
   assert( SQLITE_CHECKPOINT_FULL==1 );
   assert( SQLITE_CHECKPOINT_RESTART==2 );
   assert( SQLITE_CHECKPOINT_TRUNCATE==3 );
-  if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
+  if( eMode<SQLITE_CHECKPOINT_NOOP || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
     /* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
     ** mode: */
     return SQLITE_MISUSE_BKPT;
@@ -187774,6 +190139,7 @@ static const int aHardLimit[] = {
   SQLITE_MAX_VARIABLE_NUMBER,      /* IMP: R-38091-32352 */
   SQLITE_MAX_TRIGGER_DEPTH,
   SQLITE_MAX_WORKER_THREADS,
+  SQLITE_MAX_PARSER_DEPTH,
 };
 
 /*
@@ -187788,6 +190154,9 @@ static const int aHardLimit[] = {
 #if SQLITE_MAX_SQL_LENGTH>SQLITE_MAX_LENGTH
 # error SQLITE_MAX_SQL_LENGTH must not be greater than SQLITE_MAX_LENGTH
 #endif
+#if SQLITE_MAX_SQL_LENGTH>2147482624     /* 1024 less than 2^31 */
+# error SQLITE_MAX_SQL_LENGTH must not be greater than 2147482624
+#endif
 #if SQLITE_MAX_COMPOUND_SELECT<2
 # error SQLITE_MAX_COMPOUND_SELECT must be at least 2
 #endif
@@ -187843,6 +190212,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_limit(sqlite3 *db, int limitId, int newLim
   assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH );
   assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN );
   assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH );
+  assert( aHardLimit[SQLITE_LIMIT_PARSER_DEPTH]==SQLITE_MAX_PARSER_DEPTH );
   assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT);
   assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP );
   assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG );
@@ -187852,7 +190222,7 @@ SQLITE_API int SQLITE_APICALL sqlite3_limit(sqlite3 *db, int limitId, int newLim
   assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
   assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
   assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS );
-  assert( SQLITE_LIMIT_WORKER_THREADS==(SQLITE_N_LIMIT-1) );
+  assert( SQLITE_LIMIT_PARSER_DEPTH==(SQLITE_N_LIMIT-1) );
 
 
   if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
@@ -188216,7 +190586,7 @@ static int SQLITE_APICALL openDatabase(
   db = sqlite3MallocZero( sizeof(sqlite3) );
   if( db==0 ) goto opendb_out;
   if( isThreadsafe
-#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
+#if defined(SQLITE_THREAD_MISUSE_WARNINGS)
    || sqlite3GlobalConfig.bCoreMutex
 #endif
   ){
@@ -188237,6 +190607,7 @@ static int SQLITE_APICALL openDatabase(
   db->aDb = db->aDbStatic;
   db->lookaside.bDisable = 1;
   db->lookaside.sz = 0;
+  db->nFpDigit = 17;
 
   assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
   memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
@@ -188682,6 +191053,12 @@ SQLITE_API int SQLITE_APICALL sqlite3_collation_needed16(
 */
 SQLITE_API void *SQLITE_APICALL sqlite3_get_clientdata(sqlite3 *db, const char *zName){
   DbClientData *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+  if( !zName || !sqlite3SafetyCheckOk(db) ){
+    (void)SQLITE_MISUSE_BKPT;
+    return 0;
+  }
+#endif
   sqlite3_mutex_enter(db->mutex);
   for(p=db->pDbData; p; p=p->pNext){
     if( strcmp(p->zName, zName)==0 ){
@@ -188795,7 +191172,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3CantopenError(int lineno){
   return sqlite3ReportError(SQLITE_CANTOPEN, lineno, "cannot open file");
 }
 #if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO)
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3CorruptPgnoError(int lineno, Pgno pgno){
+SQLITE_PRIVATE int sqlite3CorruptPgnoError(int lineno, Pgno pgno){
   char zMsg[100];
   sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno);
   testcase( sqlite3GlobalConfig.xLog!=0 );
@@ -188803,11 +191180,11 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3CorruptPgnoError(int lineno, Pgno pgno)
 }
 #endif
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3NomemError(int lineno){
+SQLITE_PRIVATE int sqlite3NomemError(int lineno){
   testcase( sqlite3GlobalConfig.xLog!=0 );
   return sqlite3ReportError(SQLITE_NOMEM, lineno, "OOM");
 }
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3IoerrnomemError(int lineno){
+SQLITE_PRIVATE int sqlite3IoerrnomemError(int lineno){
   testcase( sqlite3GlobalConfig.xLog!=0 );
   return sqlite3ReportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
 }
@@ -189539,6 +191916,17 @@ SQLITE_API int sqlite3_test_control(int op, ...){
       break;
     }
 
+    /* sqlite3_test_control(SQLITE_TESTCTRL_ATOF, const char *z, double *p);
+    **
+    ** Test access to the sqlite3AtoF() routine.
+    */
+    case SQLITE_TESTCTRL_ATOF: {
+      const char *z = va_arg(ap,const char*);
+      double *pR = va_arg(ap,double*);
+      rc = sqlite3AtoF(z,pR);
+      break;
+    }
+
 #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
     /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue)
     **
@@ -189755,6 +192143,7 @@ SQLITE_API const char *SQLITE_APICALL sqlite3_filename_journal(const char *zFile
 }
 SQLITE_API const char *SQLITE_APICALL sqlite3_filename_wal(const char *zFilename){
 #ifdef SQLITE_OMIT_WAL
+  UNUSED_PARAMETER(zFilename);
   return 0;
 #else
   zFilename = sqlite3_filename_journal(zFilename);
@@ -191128,7 +193517,16 @@ typedef sqlite3_int64 i64;        /* 8-byte signed integer */
 #define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
 #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
 
-#define deliberate_fall_through
+#if !defined(deliberate_fall_through)
+# if defined(__has_attribute)
+#  if __has_attribute(fallthrough)
+#    define deliberate_fall_through __attribute__((fallthrough));
+#  endif
+# endif
+#endif
+#if !defined(deliberate_fall_through)
+# define deliberate_fall_through
+#endif
 
 /*
 ** Macros needed to provide flexible arrays in a portable way
@@ -191146,7 +193544,7 @@ typedef sqlite3_int64 i64;        /* 8-byte signed integer */
 #endif /* SQLITE_AMALGAMATION */
 
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3Corrupt(void);
+SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
 # define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt()
 #else
 # define FTS_CORRUPT_VTAB SQLITE_CORRUPT_VTAB
@@ -191526,6 +193924,15 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3Incrmerge(Fts3Table*,int,int);
   (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
 )
 
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3PrepareStmt(
+  Fts3Table *p,                   /* Prepare for this connection */
+  const char *zSql,               /* SQL to prepare */
+  int bPersist,                   /* True to set SQLITE_PREPARE_PERSISTENT */
+  int bAllowVtab,                 /* True to omit SQLITE_PREPARE_NO_VTAB */
+  sqlite3_stmt **pp               /* OUT: Prepared statement */
+);
+
+
 /* fts3.c */
 SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...);
 SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3PutVarint(char *, sqlite3_int64);
@@ -191619,6 +194026,12 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3IntegrityCheck(Fts3Table *p, int *p
   SQLITE_EXTENSION_INIT1
 #endif
 
+
+/*
+** Assume any b-tree layer with more levels than this is corrupt.
+*/
+#define FTS3_MAX_BTREE_HEIGHT 48
+
 typedef struct Fts3HashWrapper Fts3HashWrapper;
 struct Fts3HashWrapper {
   Fts3Hash hash;                  /* Hash table */
@@ -191637,7 +194050,9 @@ static int SQLITE_APICALL fts3TermSegReaderCursor(
 ** only true if it is guaranteed that the fts3 database is not corrupt.
 */
 #ifdef SQLITE_DEBUG
+#endif
 SQLITE_API int sqlite3_fts3_may_be_corrupt = 1;
+#if 0
 #endif
 
 /*
@@ -193133,9 +195548,7 @@ static int SQLITE_APICALL fts3CursorSeekStmt(Fts3Cursor *pCsr){
       zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
       if( !zSql ) return SQLITE_NOMEM;
       p->bLock++;
-      rc = sqlite3_prepare_v3(
-          p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
-      );
+      rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
       p->bLock--;
       sqlite3_free(zSql);
     }
@@ -193337,7 +195750,11 @@ static int SQLITE_APICALL fts3SelectLeaf(
   assert( piLeaf || piLeaf2 );
 
   fts3GetVarint32(zNode, &iHeight);
-  rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
+  if( iHeight>FTS3_MAX_BTREE_HEIGHT ){
+    rc = FTS_CORRUPT_VTAB;
+  }else{
+    rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
+  }
   assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
 
   if( rc==SQLITE_OK && iHeight>1 ){
@@ -194710,9 +197127,7 @@ static int SQLITE_APICALL fts3FilterMethod(
     }
     if( zSql ){
       p->bLock++;
-      rc = sqlite3_prepare_v3(
-          p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
-      );
+      rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
       p->bLock--;
       sqlite3_free(zSql);
     }else{
@@ -195335,6 +197750,7 @@ static int SQLITE_APICALL fts3IntegrityMethod(
 
   UNUSED_PARAMETER(isQuick);
   rc = sqlite3Fts3IntegrityCheck(p, &bOk);
+  assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK );
   assert( rc!=SQLITE_CORRUPT_VTAB );
   if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
     *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
@@ -197498,7 +199914,7 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhr
 ** Return SQLITE_CORRUPT_VTAB.
 */
 #ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3Corrupt(){
+SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
   return SQLITE_CORRUPT_VTAB;
 }
 #endif
@@ -197851,7 +200267,7 @@ static int SQLITE_APICALL fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
             pCsr->aStat[1].nDoc++;
           }
           eState = 2;
-          /* fall through */
+          /* no break */ deliberate_fall_through
 
         case 2:
           if( v==0 ){       /* 0x00. Next integer will be a docid. */
@@ -197867,7 +200283,7 @@ static int SQLITE_APICALL fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
         /* State 3. The integer just read is a column number. */
         default: assert( eState==3 );
           iCol = (int)v;
-          if( iCol<1 ){
+          if( iCol<1 || iCol>(pFts3->nColumn+1) ){
             rc = SQLITE_CORRUPT_VTAB;
             break;
           }
@@ -198557,6 +200973,7 @@ static int SQLITE_APICALL getNextNode(
         assert( nKey==4 );
         if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
           nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
+          if( nNear>=1000000000 ) nNear = 1000000000;
         }
       }
 
@@ -201772,9 +204189,9 @@ typedef struct SegmentWriter SegmentWriter;
 ** incrementally. See function fts3PendingListAppend() for details.
 */
 struct PendingList {
-  int nData;
+  sqlite3_int64 nData;
   char *aData;
-  int nSpace;
+  sqlite3_int64 nSpace;
   sqlite3_int64 iLastDocid;
   sqlite3_int64 iLastCol;
   sqlite3_int64 iLastPos;
@@ -201947,6 +204364,24 @@ struct SegmentNode {
 #define SQL_UPDATE_LEVEL_IDX          38
 #define SQL_UPDATE_LEVEL              39
 
+/*
+** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL
+** is always set.
+*/
+SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3PrepareStmt(
+  Fts3Table *p,                   /* Prepare for this connection */
+  const char *zSql,               /* SQL to prepare */
+  int bPersist,                   /* True to set SQLITE_PREPARE_PERSISTENT */
+  int bAllowVtab,                 /* True to omit SQLITE_PREPARE_NO_VTAB */
+  sqlite3_stmt **pp               /* OUT: Prepared statement */
+){
+  int f = SQLITE_PREPARE_FROM_DDL
+         |((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0)
+         |(bPersist ? SQLITE_PREPARE_PERSISTENT : 0);
+
+  return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL);
+}
+
 /*
 ** This function is used to obtain an SQLite prepared statement handle
 ** for the statement identified by the second argument. If successful,
@@ -202072,12 +204507,12 @@ static int SQLITE_APICALL fts3SqlStmt(
 
   pStmt = p->aStmt[eStmt];
   if( !pStmt ){
-    int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
+    int bAllowVtab = 0;
     char *zSql;
     if( eStmt==SQL_CONTENT_INSERT ){
       zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
     }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
-      f &= ~SQLITE_PREPARE_NO_VTAB;
+      bAllowVtab = 1;
       zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
     }else{
       zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
@@ -202085,7 +204520,7 @@ static int SQLITE_APICALL fts3SqlStmt(
     if( !zSql ){
       rc = SQLITE_NOMEM;
     }else{
-      rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
+      rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt);
       sqlite3_free(zSql);
       assert( rc==SQLITE_OK || pStmt==0 );
       p->aStmt[eStmt] = pStmt;
@@ -202434,7 +204869,9 @@ static int SQLITE_APICALL fts3PendingTermsAddOne(
 
   pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
   if( pList ){
-    p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
+    assert( (i64)pList->nData+(i64)nToken+(i64)sizeof(Fts3HashElem)
+             <= (i64)p->nPendingData );
+    p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
   }
   if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
     if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
@@ -202447,7 +204884,9 @@ static int SQLITE_APICALL fts3PendingTermsAddOne(
     }
   }
   if( rc==SQLITE_OK ){
-    p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
+    assert( (i64)p->nPendingData + pList->nData + nToken
+             + sizeof(Fts3HashElem) <= 0x3fffffff );
+    p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
   }
   return rc;
 }
@@ -205248,7 +207687,7 @@ static int SQLITE_APICALL fts3DoRebuild(Fts3Table *p){
     if( !zSql ){
       rc = SQLITE_NOMEM;
     }else{
-      rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+      rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
       sqlite3_free(zSql);
     }
 
@@ -207001,7 +209440,7 @@ SQLITE_PRIVATE int SQLITE_APICALL sqlite3Fts3IntegrityCheck(Fts3Table *p, int *p
     if( !zSql ){
       rc = SQLITE_NOMEM;
     }else{
-      rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+      rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
       sqlite3_free(zSql);
     }
 
@@ -207131,7 +209570,7 @@ static int SQLITE_APICALL fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
       v = atoi(&zVal[9]);
       if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
       rc = SQLITE_OK;
-    }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
+    }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
       v = atoi(&zVal[11]);
       if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
       rc = SQLITE_OK;
@@ -208398,7 +210837,7 @@ static int SQLITE_APICALL fts3ExprLHits(
       if( p->flag==FTS3_MATCHINFO_LHITS ){
         p->aMatchinfo[iStart + iCol] = (u32)nHit;
       }else if( nHit ){
-        p->aMatchinfo[iStart + (iCol+1)/32] |= (1 << (iCol&0x1F));
+        p->aMatchinfo[iStart + iCol/32] |= (1U << (iCol&0x1F));
       }
     }
     assert( *pIter==0x00 || *pIter==0x01 );
@@ -210391,7 +212830,8 @@ struct JsonString {
 /* Allowed values for JsonString.eErr */
 #define JSTRING_OOM         0x01   /* Out of memory */
 #define JSTRING_MALFORMED   0x02   /* Malformed JSONB */
-#define JSTRING_ERR         0x04   /* Error already sent to sqlite3_result */
+#define JSTRING_TOODEEP     0x04   /* JSON nested too deep */
+#define JSTRING_ERR         0x08   /* Error already sent to sqlite3_result */
 
 /* The "subtype" set for text JSON values passed through using
 ** sqlite3_result_subtype() and sqlite3_value_subtype().
@@ -210406,7 +212846,10 @@ struct JsonString {
 #define JSON_SQL       0x02        /* Result is always SQL */
 #define JSON_ABPATH    0x03        /* Allow abbreviated JSON path specs */
 #define JSON_ISSET     0x04        /* json_set(), not json_insert() */
-#define JSON_BLOB      0x08        /* Use the BLOB output format */
+#define JSON_AINS      0x08        /* json_array_insert(), not json_insert() */
+#define JSON_BLOB      0x10        /* Use the BLOB output format */
+
+#define JSON_INSERT_TYPE(X)  (((X)&0xC)>>2)
 
 
 /* A parsed JSON value.  Lifecycle:
@@ -210452,6 +212895,7 @@ struct JsonParse {
 #define JEDIT_REPL  2   /* Overwrite if exists */
 #define JEDIT_INS   3   /* Insert if not exists */
 #define JEDIT_SET   4   /* Insert or overwrite */
+#define JEDIT_AINS  5   /* array_insert() */
 
 /*
 ** Maximum nesting depth of JSON for this implementation.
@@ -210477,7 +212921,7 @@ struct JsonParse {
 **************************************************************************/
 static void SQLITE_APICALL jsonReturnStringAsBlob(JsonString*);
 static int SQLITE_APICALL jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p);
-static u32 SQLITE_APICALL jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
+static u32 SQLITE_APICALL jsonTranslateBlobToText(JsonParse*,u32,JsonString*);
 static void SQLITE_APICALL jsonReturnParse(sqlite3_context*,JsonParse*);
 static JsonParse *SQLITE_APICALL jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
 static void SQLITE_APICALL jsonParseFree(JsonParse*);
@@ -210635,6 +213079,15 @@ static void SQLITE_APICALL jsonStringOom(JsonString *p){
   jsonStringReset(p);
 }
 
+/* Report JSON nested too deep
+*/
+static void SQLITE_APICALL jsonStringTooDeep(JsonString *p){
+  p->eErr |= JSTRING_TOODEEP;
+  assert( p->pCtx!=0 );
+  sqlite3_result_error(p->pCtx, "JSON nested too deep", -1);
+  jsonStringReset(p);
+}
+
 /* Enlarge pJson->zBuf so that it can hold at least N more bytes.
 ** Return zero on success.  Return non-zero on an OOM error
 */
@@ -210874,7 +213327,7 @@ static void SQLITE_APICALL jsonAppendSqlValue(
       break;
     }
     case SQLITE_FLOAT: {
-      jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue));
+      jsonPrintf(100, p, "%!0.17g", sqlite3_value_double(pValue));
       break;
     }
     case SQLITE_INTEGER: {
@@ -210924,6 +213377,7 @@ static void SQLITE_APICALL jsonReturnString(
 ){
   assert( (pParse!=0)==(ctx!=0) );
   assert( ctx==0 || ctx==p->pCtx );
+  jsonStringTerminate(p);
   if( p->eErr==0 ){
     int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
     if( flags & JSON_BLOB ){
@@ -210931,7 +213385,7 @@ static void SQLITE_APICALL jsonReturnString(
     }else if( p->bStatic ){
       sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
                             SQLITE_TRANSIENT, SQLITE_UTF8);
-    }else if( jsonStringTerminate(p) ){
+    }else{
       if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
         int rc;
         pParse->zJson = sqlite3RCStrRef(p->zBuf);
@@ -210947,11 +213401,11 @@ static void SQLITE_APICALL jsonReturnString(
       sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
                             sqlite3RCStrUnref,
                             SQLITE_UTF8);
-    }else{
-      sqlite3_result_error_nomem(p->pCtx);
     }
   }else if( p->eErr & JSTRING_OOM ){
     sqlite3_result_error_nomem(p->pCtx);
+  }else if( p->eErr & JSTRING_TOODEEP ){
+    /* error already in p->pCtx */
   }else if( p->eErr & JSTRING_MALFORMED ){
     sqlite3_result_error(p->pCtx, "malformed JSON", -1);
   }
@@ -211282,11 +213736,11 @@ static void SQLITE_APICALL jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
 /* Slow version of jsonBlobAppendNode() that first resizes the
 ** pParse->aBlob structure.
 */
-static void SQLITE_APICALL jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
+static void SQLITE_APICALL jsonBlobAppendNode(JsonParse*,u8,u64,const void*);
 static SQLITE_NOINLINE void SQLITE_APICALL jsonBlobExpandAndAppendNode(
   JsonParse *pParse,
   u8 eType,
-  u32 szPayload,
+  u64 szPayload,
   const void *aPayload
 ){
   if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
@@ -211306,7 +213760,7 @@ static SQLITE_NOINLINE void SQLITE_APICALL jsonBlobExpandAndAppendNode(
 static void SQLITE_APICALL jsonBlobAppendNode(
   JsonParse *pParse,          /* The JsonParse object under construction */
   u8 eType,                   /* Node type.  One of JSONB_* */
-  u32 szPayload,              /* Number of bytes of payload */
+  u64 szPayload,              /* Number of bytes of payload */
   const void *aPayload        /* The payload.  Might be NULL */
 ){
   u8 *a;
@@ -212162,12 +214616,8 @@ static int SQLITE_APICALL jsonConvertTextToBlob(
 */
 static void SQLITE_APICALL jsonReturnStringAsBlob(JsonString *pStr){
   JsonParse px;
+  assert( pStr->eErr==0 );
   memset(&px, 0, sizeof(px));
-  jsonStringTerminate(pStr);
-  if( pStr->eErr ){
-    sqlite3_result_error_nomem(pStr->pCtx);
-    return;
-  }
   px.zJson = pStr->zBuf;
   px.nJson = pStr->nUsed;
   px.db = sqlite3_context_db_handle(pStr->pCtx);
@@ -212191,9 +214641,10 @@ static u32 SQLITE_APICALL jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *
   u8 x;
   u32 sz;
   u32 n;
-  assert( i<=pParse->nBlob );
-  x = pParse->aBlob[i]>>4;
-  if( x<=11 ){
+  if( i>=pParse->nBlob ){
+    *pSz = 0;
+    return 0;
+  }else if( (x = pParse->aBlob[i]>>4)<=11 ){
     sz = x;
     n = 1;
   }else if( x==12 ){
@@ -212257,7 +214708,7 @@ static u32 SQLITE_APICALL jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *
 ** The pOut->eErr JSTRING_OOM flag is set on a OOM.
 */
 static u32 SQLITE_APICALL jsonTranslateBlobToText(
-  const JsonParse *pParse,       /* the complete parse of the JSON */
+  JsonParse *pParse,             /* the complete parse of the JSON */
   u32 i,                         /* Start rendering at this index */
   JsonString *pOut               /* Write JSON here */
 ){
@@ -212439,10 +214890,14 @@ static u32 SQLITE_APICALL jsonTranslateBlobToText(
       jsonAppendChar(pOut, '[');
       j = i+n;
       iEnd = j+sz;
+      if( ++pParse->iDepth > JSON_MAX_DEPTH ){
+        jsonStringTooDeep(pOut);
+      }
       while( j<iEnd && pOut->eErr==0 ){
         j = jsonTranslateBlobToText(pParse, j, pOut);
         jsonAppendChar(pOut, ',');
       }
+      pParse->iDepth--;
       if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
       if( sz>0 ) jsonStringTrimOneChar(pOut);
       jsonAppendChar(pOut, ']');
@@ -212453,10 +214908,14 @@ static u32 SQLITE_APICALL jsonTranslateBlobToText(
       jsonAppendChar(pOut, '{');
       j = i+n;
       iEnd = j+sz;
+      if( ++pParse->iDepth > JSON_MAX_DEPTH ){
+        jsonStringTooDeep(pOut);
+      }
       while( j<iEnd && pOut->eErr==0 ){
         j = jsonTranslateBlobToText(pParse, j, pOut);
         jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
       }
+      pParse->iDepth--;
       if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
       if( sz>0 ) jsonStringTrimOneChar(pOut);
       jsonAppendChar(pOut, '}');
@@ -212513,7 +214972,7 @@ static u32 SQLITE_APICALL jsonTranslateBlobToPrettyText(
   u32 i                      /* Start rendering at this index */
 ){
   u32 sz, n, j, iEnd;
-  const JsonParse *pParse = pPretty->pParse;
+  JsonParse *pParse = pPretty->pParse;
   JsonString *pOut = pPretty->pOut;
   n = jsonbPayloadSize(pParse, i, &sz);
   if( n==0 ){
@@ -212528,6 +214987,9 @@ static u32 SQLITE_APICALL jsonTranslateBlobToPrettyText(
       if( j<iEnd ){
         jsonAppendChar(pOut, '\n');
         pPretty->nIndent++;
+        if( pPretty->nIndent >= JSON_MAX_DEPTH ){
+          jsonStringTooDeep(pOut);
+        }
         while( pOut->eErr==0 ){
           jsonPrettyIndent(pPretty);
           j = jsonTranslateBlobToPrettyText(pPretty, j);
@@ -212549,6 +215011,10 @@ static u32 SQLITE_APICALL jsonTranslateBlobToPrettyText(
       if( j<iEnd ){
         jsonAppendChar(pOut, '\n');
         pPretty->nIndent++;
+        if( pPretty->nIndent >= JSON_MAX_DEPTH ){
+          jsonStringTooDeep(pOut);
+        }
+        pParse->iDepth = pPretty->nIndent;
         while( pOut->eErr==0 ){
           jsonPrettyIndent(pPretty);
           j = jsonTranslateBlobToText(pParse, j, pOut);
@@ -212706,6 +215172,7 @@ static void SQLITE_APICALL jsonBlobEdit(
   u32 nIns               /* Bytes of content to insert */
 ){
   i64 d = (i64)nIns - (i64)nDel;
+  assert( pParse->nBlob >= (u64)iDel + (u64)nDel );
   if( d<0 && d>=(-8) && aIns!=0
    && jsonBlobOverwrite(&pParse->aBlob[iDel], aIns, nIns, (int)-d)
   ){
@@ -212948,7 +215415,9 @@ static int SQLITE_APICALL jsonLabelCompare(
 */
 #define JSON_LOOKUP_ERROR      0xffffffff
 #define JSON_LOOKUP_NOTFOUND   0xfffffffe
-#define JSON_LOOKUP_PATHERROR  0xfffffffd
+#define JSON_LOOKUP_NOTARRAY   0xfffffffd
+#define JSON_LOOKUP_TOODEEP    0xfffffffc
+#define JSON_LOOKUP_PATHERROR  0xfffffffb
 #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)
 
 /* Forward declaration */
@@ -212977,7 +215446,7 @@ static u32 SQLITE_APICALL jsonLookupStep(JsonParse*,u32,const char*,u32);
 static u32 SQLITE_APICALL jsonCreateEditSubstructure(
   JsonParse *pParse,  /* The original JSONB that is being edited */
   JsonParse *pIns,    /* Populate this with the blob data to insert */
-  const char *zTail   /* Tail of the path that determins substructure */
+  const char *zTail   /* Tail of the path that determines substructure */
 ){
   static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
   int rc;
@@ -212995,7 +215464,12 @@ static u32 SQLITE_APICALL jsonCreateEditSubstructure(
     pIns->eEdit = pParse->eEdit;
     pIns->nIns = pParse->nIns;
     pIns->aIns = pParse->aIns;
+    pIns->iDepth = pParse->iDepth+1;
+    if( pIns->iDepth >= JSON_MAX_DEPTH ){
+      return JSON_LOOKUP_TOODEEP;
+    }
     rc = jsonLookupStep(pIns, 0, zTail, 0);
+    pParse->iDepth--;
     pParse->oom |= pIns->oom;
   }
   return rc;  /* Error code only */
@@ -213012,9 +215486,9 @@ static u32 SQLITE_APICALL jsonCreateEditSubstructure(
 ** Return one of the JSON_LOOKUP error codes if problems are seen.
 **
 ** This routine will also modify the blob.  If pParse->eEdit is one of
-** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
-** made to the selected value.  If an edit is performed, then the return
-** value does not necessarily point to the select element.  If an edit
+** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, JEDIT_SET, or JEDIT_AINS, then changes
+** might be made to the selected value. If an edit is performed, then the
+** return value does not necessarily point to the select element. If an edit
 ** is performed, the return value is only useful for detecting error
 ** conditions.
 */
@@ -213040,6 +215514,13 @@ static u32 SQLITE_APICALL jsonLookupStep(
         jsonBlobEdit(pParse, iRoot, sz, 0, 0);
       }else if( pParse->eEdit==JEDIT_INS ){
         /* Already exists, so json_insert() is a no-op */
+      }else if( pParse->eEdit==JEDIT_AINS ){
+        /* json_array_insert() */
+        if( zPath[-1]!=']' ){
+          return JSON_LOOKUP_NOTARRAY;
+        }else{
+          jsonBlobEdit(pParse, iRoot, 0, pParse->aIns, pParse->nIns);
+        }
       }else{
         /* json_set() or json_replace() */
         jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
@@ -213094,7 +215575,11 @@ static u32 SQLITE_APICALL jsonLookupStep(
         n = jsonbPayloadSize(pParse, v, &sz);
         if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
         assert( j>0 );
+        if( ++pParse->iDepth >= JSON_MAX_DEPTH ){
+          return JSON_LOOKUP_TOODEEP;
+        }
         rc = jsonLookupStep(pParse, v, &zPath[i], j);
+        pParse->iDepth--;
         if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
         return rc;
       }
@@ -213111,6 +215596,10 @@ static u32 SQLITE_APICALL jsonLookupStep(
       JsonParse ix;      /* Header of the label to be inserted */
       testcase( pParse->eEdit==JEDIT_INS );
       testcase( pParse->eEdit==JEDIT_SET );
+      testcase( pParse->eEdit==JEDIT_AINS );
+      if( pParse->eEdit==JEDIT_AINS && sqlite3_strglob("*]",&zPath[i])!=0 ){
+        return JSON_LOOKUP_NOTARRAY;
+      }
       memset(&ix, 0, sizeof(ix));
       ix.db = pParse->db;
       jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
@@ -213138,28 +215627,32 @@ static u32 SQLITE_APICALL jsonLookupStep(
       return rc;
     }
   }else if( zPath[0]=='[' ){
+    u64 kk = 0;
     x = pParse->aBlob[iRoot] & 0x0f;
     if( x!=JSONB_ARRAY )  return JSON_LOOKUP_NOTFOUND;
     n = jsonbPayloadSize(pParse, iRoot, &sz);
-    k = 0;
     i = 1;
     while( sqlite3Isdigit(zPath[i]) ){
-      k = k*10 + zPath[i] - '0';
+      if( kk<0xffffffff ) kk = kk*10 + zPath[i] - '0';
+      /*     ^^^^^^^^^^--- Allow kk to be bigger than any JSON array so that
+      ** we get NOTFOUND instead of PATHERROR, without overflowing kk. */
       i++;
     }
     if( i<2 || zPath[i]!=']' ){
       if( zPath[1]=='#' ){
-        k = jsonbArrayCount(pParse, iRoot);
+        kk = jsonbArrayCount(pParse, iRoot);
         i = 2;
         if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
-          unsigned int nn = 0;
+          u64 nn = 0;
           i = 3;
           do{
-            nn = nn*10 + zPath[i] - '0';
+            if( nn<0xffffffff ) nn = nn*10 + zPath[i] - '0';
+            /*     ^^^^^^^^^^--- Allow nn to be bigger than any JSON array to
+            ** get NOTFOUND instead of PATHERROR, without overflowing nn. */
             i++;
           }while( sqlite3Isdigit(zPath[i]) );
-          if( nn>k ) return JSON_LOOKUP_NOTFOUND;
-          k -= nn;
+          if( nn>kk ) return JSON_LOOKUP_NOTFOUND;
+          kk -= nn;
         }
         if( zPath[i]!=']' ){
           return JSON_LOOKUP_PATHERROR;
@@ -213171,21 +215664,26 @@ static u32 SQLITE_APICALL jsonLookupStep(
     j = iRoot+n;
     iEnd = j+sz;
     while( j<iEnd ){
-      if( k==0 ){
+      if( kk==0 ){
+        if( ++pParse->iDepth >= JSON_MAX_DEPTH ){
+          return JSON_LOOKUP_TOODEEP;
+        }
         rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
+        pParse->iDepth--;
         if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
         return rc;
       }
-      k--;
+      kk--;
       n = jsonbPayloadSize(pParse, j, &sz);
       if( n==0 ) return JSON_LOOKUP_ERROR;
       j += n+sz;
     }
     if( j>iEnd ) return JSON_LOOKUP_ERROR;
-    if( k>0 ) return JSON_LOOKUP_NOTFOUND;
+    if( kk>0 ) return JSON_LOOKUP_NOTFOUND;
     if( pParse->eEdit>=JEDIT_INS ){
       JsonParse v;
       testcase( pParse->eEdit==JEDIT_INS );
+      testcase( pParse->eEdit==JEDIT_AINS );
       testcase( pParse->eEdit==JEDIT_SET );
       rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
       if( !JSON_LOOKUP_ISERROR(rc)
@@ -213323,7 +215821,7 @@ static void SQLITE_APICALL jsonReturnFromBlob(
     to_double:
       z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
       if( z==0 ) goto returnfromblob_oom;
-      rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+      rc = sqlite3AtoF(z, &r);
       sqlite3DbFree(db, z);
       if( rc<=0 ) goto returnfromblob_malformed;
       sqlite3_result_double(pCtx, r);
@@ -213503,16 +216001,35 @@ static int SQLITE_APICALL jsonFunctionArgToBlob(
 }
 
 /*
-** Generate a bad path error.
+** Generate a path error.
+**
+** The specifics of the error are determined by the rc argument.
+**
+**          rc                        error
+**  -----------------       ----------------------
+**  JSON_LOOKUP_ARRAY       "not an array"
+**  JSON_LOOKUP_TOODEEP     "JSON nested too deep"
+**  JSON_LOOKUP_ERROR       "malformed JSON"
+**  otherwise...            "bad JSON path"
 **
 ** If ctx is not NULL then push the error message into ctx and return NULL.
 ** If ctx is NULL, then return the text of the error message.
 */
 static char *SQLITE_APICALL jsonBadPathError(
   sqlite3_context *ctx,     /* The function call containing the error */
-  const char *zPath         /* The path with the problem */
+  const char *zPath,        /* The path with the problem */
+  int rc                    /* Maybe JSON_LOOKUP_NOTARRAY */
 ){
-  char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
+  char *zMsg;
+  if( rc==(int)JSON_LOOKUP_NOTARRAY ){
+    zMsg = sqlite3_mprintf("not an array element: %Q", zPath);
+  }else if( rc==(int)JSON_LOOKUP_ERROR ){
+    zMsg = sqlite3_mprintf("malformed JSON");
+  }else if( rc==(int)JSON_LOOKUP_TOODEEP ){
+    zMsg = sqlite3_mprintf("JSON path too deep");
+  }else{
+    zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
+  }
   if( ctx==0 ) return zMsg;
   if( zMsg ){
     sqlite3_result_error(ctx, zMsg, -1);
@@ -213529,13 +216046,13 @@ static char *SQLITE_APICALL jsonBadPathError(
 ** and return the result.
 **
 ** The specific operation is determined by eEdit, which can be one
-** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
+** of JEDIT_INS, JEDIT_REPL, JEDIT_SET, or JEDIT_AINS.
 */
 static void SQLITE_APICALL jsonInsertIntoBlob(
   sqlite3_context *ctx,
   int argc,
   sqlite3_value **argv,
-  int eEdit                /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
+  int eEdit                /* JEDIT_INS, JEDIT_REPL, JEDIT_SET, JEDIT_AINS */
 ){
   int i;
   u32 rc = 0;
@@ -213572,6 +216089,7 @@ static void SQLITE_APICALL jsonInsertIntoBlob(
       p->nIns = ax.nBlob;
       p->aIns = ax.aBlob;
       p->delta = 0;
+      p->iDepth = 0;
       rc = jsonLookupStep(p, 0, zPath+1, 0);
     }
     jsonParseReset(&ax);
@@ -213584,11 +216102,7 @@ static void SQLITE_APICALL jsonInsertIntoBlob(
 
 jsonInsertIntoBlob_patherror:
   jsonParseFree(p);
-  if( rc==JSON_LOOKUP_ERROR ){
-    sqlite3_result_error(ctx, "malformed JSON", -1);
-  }else{
-    jsonBadPathError(ctx, zPath);
-  }
+  jsonBadPathError(ctx, zPath, rc);
   return;
 }
 
@@ -213813,7 +216327,7 @@ static void SQLITE_APICALL jsonReturnParse(
 ** including iEnd.  Indent the
 ** content by nIndent spaces.
 */
-static void SQLITE_APICALL jsonDebugPrintBlob(
+static void jsonDebugPrintBlob(
   JsonParse *pParse, /* JSON content */
   u32 iStart,        /* Start rendering here */
   u32 iEnd,          /* Do not render this byte or any byte after this one */
@@ -213901,7 +216415,7 @@ static void SQLITE_APICALL jsonDebugPrintBlob(
     iStart += n + sz;
   }
 }
-static void SQLITE_APICALL jsonShowParse(JsonParse *pParse){
+static void jsonShowParse(JsonParse *pParse){
   sqlite3_str out;
   char zBuf[1000];
   if( pParse==0 ){
@@ -213928,7 +216442,7 @@ static void SQLITE_APICALL jsonShowParse(JsonParse *pParse){
 ** Parse JSON using jsonParseFuncArg().  Return text that is a
 ** human-readable dump of the binary JSONB for the input parameter.
 */
-static void SQLITE_APICALL jsonParseFunc(
+static void jsonParseFunc(
   sqlite3_context *ctx,
   int argc,
   sqlite3_value **argv
@@ -214028,10 +216542,8 @@ static void SQLITE_APICALL jsonArrayLengthFunc(
     if( JSON_LOOKUP_ISERROR(i) ){
       if( i==JSON_LOOKUP_NOTFOUND ){
         /* no-op */
-      }else if( i==JSON_LOOKUP_PATHERROR ){
-        jsonBadPathError(ctx, zPath);
       }else{
-        sqlite3_result_error(ctx, "malformed JSON", -1);
+        jsonBadPathError(ctx, zPath, i);
       }
       eErr = 1;
       i = 0;
@@ -214134,7 +216646,7 @@ static void SQLITE_APICALL jsonExtractFunc(
       j = jsonLookupStep(p, 0, jx.zBuf, 0);
       jsonStringReset(&jx);
     }else{
-      jsonBadPathError(ctx, zPath);
+      jsonBadPathError(ctx, zPath, 0);
       goto json_extract_error;
     }
     if( j<p->nBlob ){
@@ -214165,11 +216677,8 @@ static void SQLITE_APICALL jsonExtractFunc(
         jsonAppendSeparator(&jx);
         jsonAppendRawNZ(&jx, "null", 4);
       }
-    }else if( j==JSON_LOOKUP_ERROR ){
-      sqlite3_result_error(ctx, "malformed JSON", -1);
-      goto json_extract_error;
     }else{
-      jsonBadPathError(ctx, zPath);
+      jsonBadPathError(ctx, zPath, j);
       goto json_extract_error;
     }
   }
@@ -214193,6 +216702,7 @@ json_extract_error:
 #define JSON_MERGE_BADTARGET   1     /* Malformed TARGET blob */
 #define JSON_MERGE_BADPATCH    2     /* Malformed PATCH blob */
 #define JSON_MERGE_OOM         3     /* Out-of-memory condition */
+#define JSON_MERGE_TOODEEP     4     /* Nested too deep */
 
 /*
 ** RFC-7396 MergePatch for two JSONB blobs.
@@ -214244,7 +216754,8 @@ static int SQLITE_APICALL jsonMergePatch(
   JsonParse *pTarget,      /* The JSON parser that contains the TARGET */
   u32 iTarget,             /* Index of TARGET in pTarget->aBlob[] */
   const JsonParse *pPatch, /* The PATCH */
-  u32 iPatch               /* Index of PATCH in pPatch->aBlob[] */
+  u32 iPatch,              /* Index of PATCH in pPatch->aBlob[] */
+  u32 iDepth               /* Nesting depth */
 ){
   u8 x;             /* Type of a single node */
   u32 n, sz=0;      /* Return values from jsonbPayloadSize() */
@@ -214353,7 +216864,8 @@ static int SQLITE_APICALL jsonMergePatch(
         /* Algorithm line 12 */
         int rc, savedDelta = pTarget->delta;
         pTarget->delta = 0;
-        rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);
+        if( iDepth>=JSON_MAX_DEPTH ) return JSON_MERGE_TOODEEP;
+        rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue, iDepth+1);
         if( rc ) return rc;
         pTarget->delta += savedDelta;
       }
@@ -214374,7 +216886,8 @@ static int SQLITE_APICALL jsonMergePatch(
         pTarget->aBlob[iTEnd+szNew] = 0x00;
         savedDelta = pTarget->delta;
         pTarget->delta = 0;
-        rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
+        if( iDepth>=JSON_MAX_DEPTH ) return JSON_MERGE_TOODEEP;
+        rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue,iDepth+1);
         if( rc ) return rc;
         pTarget->delta += savedDelta;
       }
@@ -214405,11 +216918,13 @@ static void SQLITE_APICALL jsonPatchFunc(
   if( pTarget==0 ) return;
   pPatch = jsonParseFuncArg(ctx, argv[1], 0);
   if( pPatch ){
-    rc = jsonMergePatch(pTarget, 0, pPatch, 0);
+    rc = jsonMergePatch(pTarget, 0, pPatch, 0, 0);
     if( rc==JSON_MERGE_OK ){
       jsonReturnParse(ctx, pTarget);
     }else if( rc==JSON_MERGE_OOM ){
       sqlite3_result_error_nomem(ctx);
+    }else if( rc==JSON_MERGE_TOODEEP ){
+      sqlite3_result_error(ctx, "JSON nested too deep", -1);
     }else{
       sqlite3_result_error(ctx, "malformed JSON", -1);
     }
@@ -214497,10 +217012,8 @@ static void SQLITE_APICALL jsonRemoveFunc(
     if( JSON_LOOKUP_ISERROR(rc) ){
       if( rc==JSON_LOOKUP_NOTFOUND ){
         continue;  /* No-op */
-      }else if( rc==JSON_LOOKUP_PATHERROR ){
-        jsonBadPathError(ctx, zPath);
       }else{
-        sqlite3_result_error(ctx, "malformed JSON", -1);
+        jsonBadPathError(ctx, zPath, rc);
       }
       goto json_remove_done;
     }
@@ -214510,7 +217023,7 @@ static void SQLITE_APICALL jsonRemoveFunc(
   return;
 
 json_remove_patherror:
-  jsonBadPathError(ctx, zPath);
+  jsonBadPathError(ctx, zPath, 0);
 
 json_remove_done:
   jsonParseFree(p);
@@ -214554,16 +217067,18 @@ static void SQLITE_APICALL jsonSetFunc(
   int argc,
   sqlite3_value **argv
 ){
-
   int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
-  int bIsSet = (flags&JSON_ISSET)!=0;
+  int eInsType = JSON_INSERT_TYPE(flags);
+  static const char *azInsType[] = { "insert", "set", "array_insert" };
+  static const u8 aEditType[] = { JEDIT_INS, JEDIT_SET, JEDIT_AINS };
 
   if( argc<1 ) return;
+  assert( eInsType>=0 && eInsType<=2 );
   if( (argc&1)==0 ) {
-    jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
+    jsonWrongNumArgs(ctx, azInsType[eInsType]);
     return;
   }
-  jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);
+  jsonInsertIntoBlob(ctx, argc, argv, aEditType[eInsType]);
 }
 
 /*
@@ -214588,17 +217103,15 @@ static void SQLITE_APICALL jsonTypeFunc(
     zPath = (const char*)sqlite3_value_text(argv[1]);
     if( zPath==0 ) goto json_type_done;
     if( zPath[0]!='$' ){
-      jsonBadPathError(ctx, zPath);
+      jsonBadPathError(ctx, zPath, 0);
       goto json_type_done;
     }
     i = jsonLookupStep(p, 0, zPath+1, 0);
     if( JSON_LOOKUP_ISERROR(i) ){
       if( i==JSON_LOOKUP_NOTFOUND ){
         /* no-op */
-      }else if( i==JSON_LOOKUP_PATHERROR ){
-        jsonBadPathError(ctx, zPath);
       }else{
-        sqlite3_result_error(ctx, "malformed JSON", -1);
+        jsonBadPathError(ctx, zPath, i);
       }
       goto json_type_done;
     }
@@ -214852,12 +217365,12 @@ static void SQLITE_APICALL jsonArrayStep(
 }
 static void SQLITE_APICALL jsonArrayCompute(sqlite3_context *ctx, int isFinal){
   JsonString *pStr;
+  int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
   pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
   if( pStr ){
-    int flags;
     pStr->pCtx = ctx;
-    jsonAppendChar(pStr, ']');
-    flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+    jsonAppendRawNZ(pStr, "]", 2);
+    jsonStringTrimOneChar(pStr);
     if( pStr->eErr ){
       jsonReturnString(pStr, 0, 0);
       return;
@@ -214878,6 +217391,9 @@ static void SQLITE_APICALL jsonArrayCompute(sqlite3_context *ctx, int isFinal){
       sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
       jsonStringTrimOneChar(pStr);
     }
+  }else if( flags & JSON_BLOB ){
+    static const u8 emptyArray = 0x0b;
+    sqlite3_result_blob(ctx, &emptyArray, 1, SQLITE_STATIC);
   }else{
     sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
   }
@@ -214911,11 +217427,9 @@ static void SQLITE_APICALL jsonGroupInverse(
   UNUSED_PARAMETER(argc);
   UNUSED_PARAMETER(argv);
   pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
-#ifdef NEVER
   /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will
   ** always have been called to initialize it */
   if( NEVER(!pStr) ) return;
-#endif
   z = pStr->zBuf;
   for(i=1; i<pStr->nUsed && ((c = z[i])!=',' || inStr || nNest); i++){
     if( c=='"' ){
@@ -214944,6 +217458,13 @@ static void SQLITE_APICALL jsonGroupInverse(
 ** json_group_obj(NAME,VALUE)
 **
 ** Return a JSON object composed of all names and values in the aggregate.
+**
+** Rows for which NAME is NULL do not result in a new entry.  However, we
+** do initially insert a "@" entry into the growing string for each null entry
+** and change the first character of the string to "@" to signal that the
+** string contains null entries.  The "@" markers are needed in order to
+** correctly process xInverse() requests.  The initial "@" is converted
+** back into "{" and the "@" null values are removed by jsonObjectCompute().
 */
 static void SQLITE_APICALL jsonObjectStep(
   sqlite3_context *ctx,
@@ -214961,7 +217482,7 @@ static void SQLITE_APICALL jsonObjectStep(
     if( pStr->zBuf==0 ){
       jsonStringInit(pStr, ctx);
       jsonAppendChar(pStr, '{');
-    }else if( pStr->nUsed>1 && z!=0 ){
+    }else if( pStr->nUsed>1 ){
       jsonAppendChar(pStr, ',');
     }
     pStr->pCtx = ctx;
@@ -214969,28 +217490,75 @@ static void SQLITE_APICALL jsonObjectStep(
       jsonAppendString(pStr, z, n);
       jsonAppendChar(pStr, ':');
       jsonAppendSqlValue(pStr, argv[1]);
+    }else{
+      pStr->zBuf[0] = '@';
+      jsonAppendRawNZ(pStr, "@", 1);
     }
   }
 }
 static void SQLITE_APICALL jsonObjectCompute(sqlite3_context *ctx, int isFinal){
   JsonString *pStr;
+  int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
   pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
   if( pStr ){
-    int flags;
-    jsonAppendChar(pStr, '}');
+    JsonString *pOgStr = pStr;
+    JsonString tmpStr;
+    jsonAppendRawNZ(pOgStr, "}", 2);  /* Ensure it is zero-terminated */
+    jsonStringTrimOneChar(pOgStr);    /* Remove the zero terminator */
     pStr->pCtx = ctx;
-    flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
     if( pStr->eErr ){
       jsonReturnString(pStr, 0, 0);
       return;
-    }else if( flags & JSON_BLOB ){
+    }
+    if( pStr->zBuf[0]!='{' ){
+      /* The string contains null entries that need to be removed */
+      u64 i, j;
+      int inStr = 0;
+      if( !isFinal ){
+        /* Work with a temporary copy of the string if this is not the
+        ** final result */
+        jsonStringInit(&tmpStr, ctx);
+        jsonAppendRawNZ(&tmpStr, pStr->zBuf, pStr->nUsed+1);
+        pStr = &tmpStr;
+        if( pStr->eErr ){
+          jsonReturnString(pStr, 0, 0);
+          return;
+        }
+        jsonStringTrimOneChar(pStr);  /* Remove zero terminator */
+      }
+      /* Fix up the string by changing the initial "@" flag back to
+      ** to "{" and removing all subsequence "@" entries, with their
+      ** associated comma delimeters. */
+      pStr->zBuf[0] = '{';
+      for(i=j=1; i<pStr->nUsed; i++){
+        char c = pStr->zBuf[i];
+        if( c=='"' ){
+          inStr = !inStr;
+          pStr->zBuf[j++] = '"';
+        }else if( c=='\\' ){
+          pStr->zBuf[j++] = '\\';
+          pStr->zBuf[j++] = pStr->zBuf[++i];
+        }else if( c=='@' && !inStr ){
+          assert( i+1<pStr->nUsed );
+          if( pStr->zBuf[i+1]==',' ){
+            i++;
+          }else if( pStr->zBuf[j-1]==',' ){
+            j--;
+          }
+        }else{
+          pStr->zBuf[j++] = c;
+        }
+      }
+      pStr->zBuf[j] = 0;  /* Restore zero terminator */
+      pStr->nUsed = j;    /* Truncate the string */
+    }
+    if( flags & JSON_BLOB ){
       jsonReturnStringAsBlob(pStr);
       if( isFinal ){
         if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
       }else{
-        jsonStringTrimOneChar(pStr);
+        jsonStringTrimOneChar(pOgStr);
       }
-      return;
     }else if( isFinal ){
       sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                           pStr->bStatic ? SQLITE_TRANSIENT :
@@ -214998,8 +217566,12 @@ static void SQLITE_APICALL jsonObjectCompute(sqlite3_context *ctx, int isFinal){
       pStr->bStatic = 1;
     }else{
       sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
-      jsonStringTrimOneChar(pStr);
+      jsonStringTrimOneChar(pOgStr);
     }
+    if( pStr!=pOgStr ) jsonStringReset(pStr);
+  }else if( flags & JSON_BLOB ){
+    static const unsigned char emptyObject = 0x0c;
+    sqlite3_result_blob(ctx, &emptyObject, 1, SQLITE_STATIC);
   }else{
     sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
   }
@@ -215500,7 +218072,7 @@ static int SQLITE_APICALL jsonEachFilter(
     if( zRoot==0 ) return SQLITE_OK;
     if( zRoot[0]!='$' ){
       sqlite3_free(cur->pVtab->zErrMsg);
-      cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
+      cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0);
       jsonEachCursorReset(p);
       return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
     }
@@ -215518,7 +218090,7 @@ static int SQLITE_APICALL jsonEachFilter(
           return SQLITE_OK;
         }
         sqlite3_free(cur->pVtab->zErrMsg);
-        cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
+        cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0);
         jsonEachCursorReset(p);
         return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
       }
@@ -215608,6 +218180,8 @@ SQLITE_PRIVATE void SQLITE_APICALL sqlite3RegisterJsonFunctions(void){
     JFUNCTION(jsonb,              1,1,0, 0,1,0,          jsonRemoveFunc),
     JFUNCTION(json_array,        -1,0,1, 1,0,0,          jsonArrayFunc),
     JFUNCTION(jsonb_array,       -1,0,1, 1,1,0,          jsonArrayFunc),
+    JFUNCTION(json_array_insert, -1,1,1, 1,0,JSON_AINS,  jsonSetFunc),
+    JFUNCTION(jsonb_array_insert,-1,1,0, 1,1,JSON_AINS,  jsonSetFunc),
     JFUNCTION(json_array_length,  1,1,0, 0,0,0,          jsonArrayLengthFunc),
     JFUNCTION(json_array_length,  2,1,0, 0,0,0,          jsonArrayLengthFunc),
     JFUNCTION(json_error_position,1,1,0, 0,0,0,          jsonErrorFunc),
@@ -215846,7 +218420,7 @@ struct Rtree {
   u8 eCoordType;              /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
   u8 nBytesPerCell;           /* Bytes consumed per cell */
   u8 inWrTrans;               /* True if inside write transaction */
-  u8 nAux;                    /* # of auxiliary columns in %_rowid */
+  u16 nAux;                   /* # of auxiliary columns in %_rowid */
 #ifdef SQLITE_ENABLE_GEOPOLY
   u8 nAuxNotNull;             /* Number of initial not-null aux columns */
 #endif
@@ -216717,7 +219291,17 @@ static void SQLITE_APICALL rtreeRelease(Rtree *pRtree){
     pRtree->inWrTrans = 0;
     assert( pRtree->nCursor==0 );
     nodeBlobReset(pRtree);
-    assert( pRtree->nNodeRef==0 || pRtree->bCorrupt );
+    if( pRtree->nNodeRef ){
+      int i;
+      assert( pRtree->bCorrupt );
+      for(i=0; i<HASHSIZE; i++){
+        while( pRtree->aHash[i] ){
+          RtreeNode *pNext = pRtree->aHash[i]->pNext;
+          sqlite3_free(pRtree->aHash[i]);
+          pRtree->aHash[i] = pNext;
+        }
+      }
+    }
     sqlite3_finalize(pRtree->pWriteNode);
     sqlite3_finalize(pRtree->pDeleteNode);
     sqlite3_finalize(pRtree->pReadRowid);
@@ -217072,7 +219656,7 @@ static int SQLITE_APICALL nodeRowidIndex(
 ){
   int ii;
   int nCell = NCELL(pNode);
-  assert( nCell<200 );
+  assert( nCell<65536 && nCell>=0 );
   for(ii=0; ii<nCell; ii++){
     if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
       *piIndex = ii;
@@ -217334,7 +219918,10 @@ static int SQLITE_APICALL rtreeStepToLeaf(RtreeCursor *pCur){
     pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
     if( rc ) return rc;
     nCell = NCELL(pNode);
-    assert( nCell<200 );
+    if( nCell>RTREE_MAXCELLS ){
+      RTREE_IS_CORRUPT(pRtree);
+      return SQLITE_CORRUPT_VTAB;
+    }
     pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
     while( p->iCell<nCell ){
       sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
@@ -218009,7 +220596,7 @@ static int SQLITE_APICALL AdjustTree(
     int iCell;
 
     cnt++;
-    if( NEVER(cnt>100) ){
+    if( cnt>100 ){
       RTREE_IS_CORRUPT(pRtree);
       return SQLITE_CORRUPT_VTAB;
     }
@@ -218367,15 +220954,6 @@ static int SQLITE_APICALL SplitNode(
     rc = updateMapping(pRtree, pCell->iRowid, pLeft, iHeight);
   }
 
-  if( rc==SQLITE_OK ){
-    rc = nodeRelease(pRtree, pRight);
-    pRight = 0;
-  }
-  if( rc==SQLITE_OK ){
-    rc = nodeRelease(pRtree, pLeft);
-    pLeft = 0;
-  }
-
 splitnode_out:
   nodeRelease(pRtree, pRight);
   nodeRelease(pRtree, pLeft);
@@ -218560,7 +221138,7 @@ static int SQLITE_APICALL rtreeInsertCell(
     rc = SplitNode(pRtree, pNode, pCell, iHeight);
   }else{
     rc = AdjustTree(pRtree, pNode, pCell);
-    if( ALWAYS(rc==SQLITE_OK) ){
+    if( rc==SQLITE_OK ){
       if( iHeight==0 ){
         rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode);
       }else{
@@ -219320,7 +221898,7 @@ static int SQLITE_APICALL rtreeInit(
     "Auxiliary rtree columns must be last"                /* 4 */
   };
 
-  assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */
+  assert( RTREE_MAX_AUX_COLUMN<256 );
   if( argc<6 || argc>RTREE_MAX_AUX_COLUMN+3 ){
     *pzErr = sqlite3_mprintf("%s", aErrMsg[2 + (argc>=6)]);
     return SQLITE_ERROR;
@@ -219455,7 +222033,7 @@ static void SQLITE_APICALL rtreenode(sqlite3_context *ctx, int nArg, sqlite3_val
   if( node.zData==0 ) return;
   nData = sqlite3_value_bytes(apArg[1]);
   if( nData<4 ) return;
-  if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
+  if( nData<4+NCELL(&node)*tree.nBytesPerCell ) return;
 
   pOut = sqlite3_str_new(0);
   for(ii=0; ii<NCELL(&node); ii++){
@@ -220188,7 +222766,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
      /* The sqlite3AtoF() routine is much much faster than atof(), if it
      ** is available */
      double r;
-     (void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
+     (void)sqlite3AtoF((const char*)p->z, &r);
      *pVal = r;
 #else
      *pVal = (GeoCoord)atof((const char*)p->z);
@@ -221239,6 +223817,11 @@ static int geopolyInit(
   int ii;
   (void)pAux;
 
+  if( argc>=RTREE_MAX_AUX_COLUMN+4 ){
+    *pzErr = sqlite3_mprintf("Too many columns for a geopoly table");
+    return SQLITE_ERROR;
+  }
+
   sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
   sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
 
@@ -222373,7 +224956,7 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
   const UChar *zInput;            /* Pointer to input string */
   UChar *zOutput = 0;             /* Pointer to output buffer */
   int nInput;                     /* Size of utf-16 input string in bytes */
-  int nOut;                       /* Size of output buffer in bytes */
+  sqlite3_int64 nOut;             /* Size of output buffer in bytes */
   int cnt;
   int bToUpper;                   /* True for toupper(), false for tolower() */
   UErrorCode status;
@@ -222396,7 +224979,7 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
   }
 
   for(cnt=0; cnt<2; cnt++){
-    UChar *zNew = sqlite3_realloc(zOutput, nOut);
+    UChar *zNew = sqlite3_realloc64(zOutput, nOut);
     if( zNew==0 ){
       sqlite3_free(zOutput);
       sqlite3_result_error_nomem(p);
@@ -222405,9 +224988,9 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
     zOutput = zNew;
     status = U_ZERO_ERROR;
     if( bToUpper ){
-      nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+      nOut = 2LL*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
     }else{
-      nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+      nOut = 2LL*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
     }
 
     if( U_SUCCESS(status) ){
@@ -224127,7 +226710,7 @@ static int rbuDeltaApply(
           /* ERROR: copy exceeds output file size */
           return -1;
         }
-        if( (int)(ofst+cnt) > lenSrc ){
+        if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
           /* ERROR: copy extends past end of input */
           return -1;
         }
@@ -225773,8 +228356,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
 
           /* If necessary, grow the pIter->aIdxCol[] array */
           if( iIdxCol==nIdxAlloc ){
-            RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
-                pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
+            RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc64(
+                pIter->aIdxCol, nIdxAlloc*sizeof(RbuSpan) + 16*sizeof(RbuSpan)
             );
             if( aIdxCol==0 ){
               rc = SQLITE_NOMEM;
@@ -230122,12 +232705,13 @@ static int dbpageFilter(
   pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
   pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
   if( idxNum & 1 ){
+    i64 iPg = sqlite3_value_int64(argv[idxNum>>1]);
     assert( argc>(idxNum>>1) );
-    pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
-    if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
+    if( iPg<1 || iPg>pCsr->mxPgno ){
       pCsr->pgno = 1;
       pCsr->mxPgno = 0;
     }else{
+      pCsr->pgno = (Pgno)iPg;
       pCsr->mxPgno = pCsr->pgno;
     }
   }else{
@@ -230447,6 +233031,7 @@ struct carray_bind {
   int nData;                  /* Number of elements */
   int mFlags;                 /* Control flags */
   void (*xDel)(void*);        /* Destructor for aData */
+  void *pDel;                 /* Alternative argument to xDel() */
 };
 
 
@@ -230779,7 +233364,7 @@ static sqlite3_module carrayModule = {
 static void carrayBindDel(void *pPtr){
   carray_bind *p = (carray_bind*)pPtr;
   if( p->xDel!=SQLITE_STATIC ){
-     p->xDel(p->aData);
+    p->xDel(p->pDel);
   }
   sqlite3_free(p);
 }
@@ -230787,14 +233372,26 @@ static void carrayBindDel(void *pPtr){
 /*
 ** Invoke this interface in order to bind to the single-argument
 ** version of CARRAY().
+**
+**    pStmt        The prepared statement to which to bind
+**    idx          The index of the parameter of pStmt to which to bind
+**    aData        The data to be bound
+**    nData        The number of elements in aData
+**    mFlags       One of SQLITE_CARRAY_xxxx indicating datatype of aData
+**    xDestroy     Destructor for pDestroy or aData if pDestroy==NULL.
+**    pDestroy     Invoke xDestroy on this pointer if not NULL
+**
+** The destructor is called pDestroy if pDestroy!=NULL, or against
+** aData if pDestroy==NULL.
 */
-SQLITE_API int sqlite3_carray_bind(
+SQLITE_API int sqlite3_carray_bind_v2(
   sqlite3_stmt *pStmt,
   int idx,
   void *aData,
   int nData,
   int mFlags,
-  void (*xDestroy)(void*)
+  void (*xDestroy)(void*),
+  void *pDestroy
 ){
   carray_bind *pNew = 0;
   int i;
@@ -230871,20 +233468,38 @@ SQLITE_API int sqlite3_carray_bind(
       memcpy(pNew->aData, aData, sz);
     }
     pNew->xDel = sqlite3_free;
+    pNew->pDel = pNew->aData;
   }else{
     pNew->aData = aData;
     pNew->xDel = xDestroy;
+    pNew->pDel = pDestroy;
   }
   return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
 
  carray_bind_error:
   if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
-    xDestroy(aData);
+    xDestroy(pDestroy);
   }
   sqlite3_free(pNew);
   return rc;
 }
 
+/*
+** Invoke this interface in order to bind to the single-argument
+** version of CARRAY().  Same as sqlite3_carray_bind_v2() with the
+** pDestroy parameter set to NULL.
+*/
+SQLITE_API int sqlite3_carray_bind(
+  sqlite3_stmt *pStmt,
+  int idx,
+  void *aData,
+  int nData,
+  int mFlags,
+  void (*xDestroy)(void*)
+){
+  return sqlite3_carray_bind_v2(pStmt,idx,aData,nData,mFlags,xDestroy,aData);
+}
+
 /*
 ** Invoke this routine to register the carray() function.
 */
@@ -231247,6 +233862,21 @@ static int sessionVarintGet(const u8 *aBuf, int *piVal){
   return getVarint32(aBuf, *piVal);
 }
 
+/*
+** Read a varint value from buffer aBuf[], size nBuf bytes, into *piVal.
+** Return the number of bytes read.
+*/
+static int sessionVarintGetSafe(const u8 *aBuf, int nBuf, int *piVal){
+  u8 aCopy[9];
+  const u8 *aRead = aBuf;
+  memset(aCopy, 0, sizeof(aCopy));
+  if( nBuf<sizeof(aCopy) ){
+    memcpy(aCopy, aBuf, nBuf);
+    aRead = aCopy;
+  }
+  return getVarint32(aRead, *piVal);
+}
+
 /* Load an unaligned and unsigned 32-bit integer */
 #define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
 
@@ -231275,6 +233905,19 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
   aBuf[7] = (i>> 0) & 0xFF;
 }
 
+/*
+** Write a double value to the buffer aBuf[].
+*/
+static void sessionPutDouble(u8 *aBuf, double r){
+  /* TODO: SQLite does something special to deal with mixed-endian
+  ** floating point values (e.g. ARM7). This code probably should
+  ** too.  */
+  u64 i;
+  assert( sizeof(double)==8 && sizeof(u64)==8 );
+  memcpy(&i, &r, 8);
+  sessionPutI64(aBuf, i);
+}
+
 /*
 ** This function is used to serialize the contents of value pValue (see
 ** comment titled "RECORD FORMAT" above).
@@ -231312,16 +233955,13 @@ static int sessionSerializeValue(
           /* TODO: SQLite does something special to deal with mixed-endian
           ** floating point values (e.g. ARM7). This code probably should
           ** too.  */
-          u64 i;
           if( eType==SQLITE_INTEGER ){
-            i = (u64)sqlite3_value_int64(pValue);
+            u64 i = (u64)sqlite3_value_int64(pValue);
+            sessionPutI64(&aBuf[1], i);
           }else{
-            double r;
-            assert( sizeof(double)==8 && sizeof(u64)==8 );
-            r = sqlite3_value_double(pValue);
-            memcpy(&i, &r, 8);
+            double r = sqlite3_value_double(pValue);
+            sessionPutDouble(&aBuf[1], r);
           }
-          sessionPutI64(&aBuf[1], i);
         }
         nByte = 9;
         break;
@@ -231511,10 +234151,11 @@ static int sessionSerialLen(const u8 *a){
   int n;
   assert( a!=0 );
   e = *a;
-  if( e==0 || e==0xFF ) return 1;
-  if( e==SQLITE_NULL ) return 1;
   if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
-  return sessionVarintGet(&a[1], &n) + 1 + n;
+  if( e==SQLITE_TEXT || e==SQLITE_BLOB ){
+    return sessionVarintGet(&a[1], &n) + 1 + n;
+  }
+  return 1;
 }
 
 /*
@@ -231537,31 +234178,31 @@ static unsigned int sessionChangeHash(
   u8 *a = aRecord;                /* Used to iterate through change record */
 
   for(i=0; i<pTab->nCol; i++){
-    int eType = *a;
     int isPK = pTab->abPK[i];
     if( bPkOnly && isPK==0 ) continue;
 
-    /* It is not possible for eType to be SQLITE_NULL here. The session
-    ** module does not record changes for rows with NULL values stored in
-    ** primary key columns. */
-    assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
-         || eType==SQLITE_TEXT || eType==SQLITE_BLOB
-         || eType==SQLITE_NULL || eType==0
-    );
-    assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
-
     if( isPK ){
-      a++;
+      int eType = *a++;
+
+      assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
+           || eType==SQLITE_TEXT || eType==SQLITE_BLOB
+           || eType==SQLITE_NULL || eType==0
+      );
+
       h = sessionHashAppendType(h, eType);
       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
         h = sessionHashAppendI64(h, sessionGetI64(a));
         a += 8;
-      }else{
+      }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
         int n;
         a += sessionVarintGet(a, &n);
         h = sessionHashAppendBlob(h, n, a);
         a += n;
       }
+      /* It should not be possible for eType to be SQLITE_NULL or 0x00 here,
+      ** as the session module does not record changes for rows with NULL
+      ** values stored in primary key columns. But a corrupt changesets
+      ** may contain such a value.  */
     }else{
       a += sessionSerialLen(a);
     }
@@ -232253,9 +234894,7 @@ static void sessionUpdateOneChange(
 
         case SQLITE_FLOAT: {
           double rVal = sqlite3_column_double(pDflt, iField);
-          i64 iVal = 0;
-          memcpy(&iVal, &rVal, sizeof(rVal));
-          sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+          sessionPutDouble(&pNew->aRecord[pNew->nRecord], rVal);
           pNew->nRecord += 8;
           break;
         }
@@ -232419,6 +235058,16 @@ static int sessionPrepareDfltStmt(
   return rc;
 }
 
+/*
+** Finalize statement pStmt. If (*pRc) is SQLITE_OK when this function is
+** called, set it to the results of the sqlite3_finalize() call. Or, if
+** it is already set to an error code, leave it as is.
+*/
+static void sessionFinalizeStmt(sqlite3_stmt *pStmt, int *pRc){
+  int rc = sqlite3_finalize(pStmt);
+  if( *pRc==SQLITE_OK ) *pRc = rc;
+}
+
 /*
 ** Table pTab has one or more existing change-records with old.* records
 ** with fewer than pTab->nCol columns. This function updates all such
@@ -232441,9 +235090,8 @@ static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){
     }
   }
 
+  sessionFinalizeStmt(pStmt, &rc);
   pSession->rc = rc;
-  rc = sqlite3_finalize(pStmt);
-  if( pSession->rc==SQLITE_OK ) pSession->rc = rc;
   return pSession->rc;
 }
 
@@ -233011,7 +235659,7 @@ static int sessionDiffFindNew(
     rc = SQLITE_NOMEM;
   }else{
     sqlite3_stmt *pStmt;
-    rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
+    rc = sqlite3_prepare_v2(pSession->db, zStmt, -1, &pStmt, 0);
     if( rc==SQLITE_OK ){
       SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
       pDiffCtx->pStmt = pStmt;
@@ -233074,7 +235722,7 @@ static int sessionDiffFindModified(
       rc = SQLITE_NOMEM;
     }else{
       sqlite3_stmt *pStmt;
-      rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
+      rc = sqlite3_prepare_v2(pSession->db, zStmt, -1, &pStmt, 0);
 
       if( rc==SQLITE_OK ){
         SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
@@ -233512,15 +236160,14 @@ static void sessionAppendCol(
     int eType = sqlite3_column_type(pStmt, iCol);
     sessionAppendByte(p, (u8)eType, pRc);
     if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
-      sqlite3_int64 i;
       u8 aBuf[8];
       if( eType==SQLITE_INTEGER ){
-        i = sqlite3_column_int64(pStmt, iCol);
+        sqlite3_int64 i = sqlite3_column_int64(pStmt, iCol);
+        sessionPutI64(aBuf, i);
       }else{
         double r = sqlite3_column_double(pStmt, iCol);
-        memcpy(&i, &r, 8);
+        sessionPutDouble(aBuf, r);
       }
-      sessionPutI64(aBuf, i);
       sessionAppendBlob(p, aBuf, 8, pRc);
     }
     if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
@@ -233770,11 +236417,11 @@ static int sessionSelectStmt(
     );
     sessionAppendStr(&cols, "tbl, ?2, stat", &rc);
   }else{
-  #if 0
+#if 0
     if( bRowid ){
       sessionAppendStr(&cols, SESSIONS_ROWID, &rc);
     }
-    #endif
+#endif
     for(i=0; i<nCol; i++){
       if( cols.nBuf ) sessionAppendStr(&cols, ", ", &rc);
       sessionAppendIdent(&cols, azCol[i], &rc);
@@ -233970,10 +236617,13 @@ static int sessionGenerateChangeset(
   }
 
   if( pSession->rc ) return pSession->rc;
-  rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
-  if( rc!=SQLITE_OK ) return rc;
 
   sqlite3_mutex_enter(sqlite3_db_mutex(db));
+  rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
+  if( rc!=SQLITE_OK ){
+    sqlite3_mutex_leave(sqlite3_db_mutex(db));
+    return rc;
+  }
 
   for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
     if( pTab->nEntry ){
@@ -234456,7 +237106,8 @@ static int sessionReadRecord(
       u8 *aVal = &pIn->aData[pIn->iNext];
       if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
         int nByte;
-        pIn->iNext += sessionVarintGet(aVal, &nByte);
+        int nRem = pIn->nData - pIn->iNext;
+        pIn->iNext += sessionVarintGetSafe(aVal, nRem, &nByte);
         rc = sessionInputBuffer(pIn, nByte);
         if( rc==SQLITE_OK ){
           if( nByte<0 || nByte>pIn->nData-pIn->iNext ){
@@ -234509,7 +237160,8 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
 
   rc = sessionInputBuffer(pIn, 9);
   if( rc==SQLITE_OK ){
-    nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
+    int nBuf = pIn->nData - pIn->iNext;
+    nRead += sessionVarintGetSafe(&pIn->aData[pIn->iNext], nBuf, &nCol);
     /* The hard upper limit for the number of columns in an SQLite
     ** database table is, according to sqliteLimit.h, 32676. So
     ** consider any table-header that purports to have more than 65536
@@ -234529,8 +237181,15 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
     while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){
       nRead++;
     }
+
+    /* Break out of the loop if if the nul-terminator byte has been found.
+    ** Otherwise, read some more input data and keep seeking. If there is
+    ** no more input data, consider the changeset corrupt.  */
     if( (pIn->iNext + nRead)<pIn->nData ) break;
     rc = sessionInputBuffer(pIn, nRead + 100);
+    if( rc==SQLITE_OK && (pIn->iNext + nRead)>=pIn->nData ){
+      rc = SQLITE_CORRUPT_BKPT;
+    }
   }
   *pnByte = nRead+1;
   return rc;
@@ -234551,7 +237210,7 @@ static int sessionChangesetBufferRecord(
   int *pnByte                     /* OUT: Size of record in bytes */
 ){
   int rc = SQLITE_OK;
-  int nByte = 0;
+  i64 nByte = 0;
   int i;
   for(i=0; rc==SQLITE_OK && i<nCol; i++){
     int eType;
@@ -234560,13 +237219,19 @@ static int sessionChangesetBufferRecord(
       eType = pIn->aData[pIn->iNext + nByte++];
       if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
         int n;
-        nByte += sessionVarintGet(&pIn->aData[pIn->iNext+nByte], &n);
+        int nRem = pIn->nData - (pIn->iNext + nByte);
+        nByte += sessionVarintGetSafe(&pIn->aData[pIn->iNext+nByte], nRem, &n);
         nByte += n;
         rc = sessionInputBuffer(pIn, nByte);
       }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
         nByte += 8;
+      }else if( eType!=0 && eType!=SQLITE_NULL ){
+        rc = SQLITE_CORRUPT_BKPT;
       }
     }
+    if( rc==SQLITE_OK && (pIn->iNext+nByte)>pIn->nData ){
+      rc = SQLITE_CORRUPT_BKPT;
+    }
   }
   *pnByte = nByte;
   return rc;
@@ -234662,10 +237327,10 @@ static int sessionChangesetNextOne(
     memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
   }
 
-  /* Make sure the buffer contains at least 10 bytes of input data, or all
-  ** remaining data if there are less than 10 bytes available. This is
-  ** sufficient either for the 'T' or 'P' byte and the varint that follows
-  ** it, or for the two single byte values otherwise. */
+  /* Make sure the buffer contains at least 2 bytes of input data, or all
+  ** remaining data if there are less than 2 bytes available. This is
+  ** sufficient either for the 'T' or 'P' byte that begins a new table,
+  ** or for the "op" and "bIndirect" single bytes otherwise. */
   p->rc = sessionInputBuffer(&p->in, 2);
   if( p->rc!=SQLITE_OK ) return p->rc;
 
@@ -234695,11 +237360,13 @@ static int sessionChangesetNextOne(
     return (p->rc = SQLITE_CORRUPT_BKPT);
   }
 
-  p->op = op;
-  p->bIndirect = p->in.aData[p->in.iNext++];
-  if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
+  if( (op!=SQLITE_UPDATE && op!=SQLITE_DELETE && op!=SQLITE_INSERT)
+   || (p->in.iNext>=p->in.nData)
+  ){
     return (p->rc = SQLITE_CORRUPT_BKPT);
   }
+  p->op = op;
+  p->bIndirect = p->in.aData[p->in.iNext++];
 
   if( paRec ){
     int nVal;                     /* Number of values to buffer */
@@ -235008,7 +237675,13 @@ static int sessionChangesetInvert(
 
     /* Test for EOF. */
     if( (rc = sessionInputBuffer(pInput, 2)) ) goto finished_invert;
-    if( pInput->iNext>=pInput->nData ) break;
+    if( pInput->iNext+1>=pInput->nData ){
+      if( pInput->iNext!=pInput->nData ){
+        rc = SQLITE_CORRUPT_BKPT;
+        goto finished_invert;
+      }
+      break;
+    }
     eType = pInput->aData[pInput->iNext];
 
     switch( eType ){
@@ -235204,6 +237877,7 @@ struct SessionApplyCtx {
   u8 bRebaseStarted;              /* If table header is already in rebase */
   u8 bRebase;                     /* True to collect rebase information */
   u8 bIgnoreNoop;                 /* True to ignore no-op conflicts */
+  u8 bNoUpdateLoop;               /* No update-loop processing */
   int bRowid;
   char *zErr;                     /* Error message, if any */
 };
@@ -235777,7 +238451,7 @@ static int sessionConflictHandler(
       u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
       int nBlob = pIter->in.iNext - pIter->in.iCurrent;
       sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
-      return SQLITE_OK;
+      return rc;
     }else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE
            || eType==SQLITE_CHANGESET_CONFLICT
     ){
@@ -235899,7 +238573,7 @@ static int sessionApplyOneOp(
     for(i=0; rc==SQLITE_OK && i<nCol; i++){
       sqlite3_value *pOld = sessionChangesetOld(pIter, i);
       sqlite3_value *pNew = sessionChangesetNew(pIter, i);
-      if( p->abPK[i] || (bPatchset==0 && pOld) ){
+      if( pOld && (p->abPK[i] || bPatchset==0) ){
         rc = sessionBindValue(pUp, i*2+2, pOld);
       }
       if( rc==SQLITE_OK && pNew ){
@@ -236025,7 +238699,264 @@ static int sessionApplyOneWithRetry(
 }
 
 /*
-** Retry the changes accumulated in the pApply->constraints buffer.
+** Create an iterator to iterate through the retry buffer pRetry.
+*/
+static int sessionRetryIterInit(
+  SessionBuffer *pRetry,          /* Buffer to iterate through */
+  int bPatchset,                  /* True for patchset, false for changeset */
+  const char *zTab,               /* Table name */
+  SessionApplyCtx *pApply,        /* Session apply context */
+  sqlite3_changeset_iter **ppIter /* OUT: New iterator */
+){
+  sqlite3_changeset_iter *pRet = 0;
+  int rc = SQLITE_OK;
+
+  rc = sessionChangesetStart(
+      &pRet, 0, 0, pRetry->nBuf, pRetry->aBuf, pApply->bInvertConstraints, 1
+  );
+  if( rc==SQLITE_OK ){
+    size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
+    pRet->bPatchset = bPatchset;
+    pRet->zTab = (char*)zTab;
+    pRet->nCol = pApply->nCol;
+    pRet->abPK = pApply->abPK;
+    sessionBufferGrow(&pRet->tblhdr, nByte, &rc);
+    pRet->apValue = (sqlite3_value**)pRet->tblhdr.aBuf;
+    if( rc==SQLITE_OK ){
+      memset(pRet->apValue, 0, nByte);
+    }else{
+      sqlite3changeset_finalize(pRet);
+      pRet = 0;
+    }
+  }
+
+  *ppIter = pRet;
+  return rc;
+}
+
+/*
+** Attempt to apply all the changes in retry buffer pRetry to the database.
+** Except, if parameter iSkip is greater than or equal to 0, skip change
+** iSkip.
+*/
+static int sessionApplyRetryBuffer(
+  SessionBuffer *pRetry,          /* Buffer to apply changes from */
+  int iSkip,                      /* If >=0, index of change to omit */
+  sqlite3 *db,                    /* Database handle */
+  int bPatchset,                  /* True for patchset, false for changeset */
+  const char *zTab,               /* Name of table to write to */
+  SessionApplyCtx *pApply,        /* Apply context */
+  int(*xConflict)(void*, int, sqlite3_changeset_iter*),
+  void *pCtx                      /* First argument passed to xConflict */
+){
+  int rc = SQLITE_OK;
+  int rc2 = SQLITE_OK;
+  int ii = 0;
+  sqlite3_changeset_iter *pIter = 0;
+
+  assert( pApply->constraints.nBuf==0 );
+
+  rc = sessionRetryIterInit(pRetry, bPatchset, zTab, pApply, &pIter);
+
+  for(ii=0; rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter); ii++){
+    if( ii!=iSkip ){
+      rc = sessionApplyOneWithRetry(db, pIter, pApply, xConflict, pCtx);
+    }
+  }
+
+  rc2 = sqlite3changeset_finalize(pIter);
+  if( rc==SQLITE_OK ) rc = rc2;
+  assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 );
+
+  return rc;
+}
+
+/*
+** Check if table zTab in the "main" database of db is a WITHOUT ROWID
+** table.
+**
+** If no error occurs, return SQLITE_OK and set output variable (*pbWR) to
+** true if zTab is a WITHOUT ROWID table, or false otherwise. Or, if an
+** error does occur, return an SQLite error code. The final value of (*pbWR)
+** is undefined in this case.
+*/
+static int sessionTableIsWithoutRowid(sqlite3 *db, const char *zTab, int *pbWR){
+  sqlite3_stmt *pList = 0;
+  char *zSql = 0;
+  int rc = SQLITE_OK;
+
+  zSql = sqlite3_mprintf("PRAGMA table_list = %Q", zTab);
+  if( zSql==0 ){
+    rc = SQLITE_NOMEM;
+  }else{
+    rc = sqlite3_prepare_v2(db, zSql, -1, &pList, 0);
+    sqlite3_free(zSql);
+  }
+
+  if( rc==SQLITE_OK ){
+    sqlite3_step(pList);
+    *pbWR = sqlite3_column_int(pList, 4);
+    rc = sqlite3_finalize(pList);
+  }
+
+  return rc;
+}
+
+/*
+** Iterator pUp points to an UPDATE change. This function deletes the
+** affected row from the database and creates an INSERT statement that
+** may be used to reinsert the row as it is after the UPDATE change
+** has been applied.
+**
+** If successful, SQLITE_OK is returned and output variable (*ppInsert)
+** is left pointing to a prepared INSERT statement. It is the responsibility
+** of the caller to eventually free this statement using sqlite3_finalize().
+** Or, if an error occurs, an SQLite error code is returned and (*ppInsert)
+** set to NULL. pApply->zErr may be set to an error message in this case.
+*/
+static int sessionUpdateToDeleteInsert(
+  sqlite3 *db,                    /* Database to write to */
+  const char *zTab,               /* Table name */
+  SessionApplyCtx *pApply,        /* Apply context */
+  sqlite3_changeset_iter *pUp,    /* Iterator pointing to UPDATE change */
+  sqlite3_stmt **ppInsert         /* OUT: INSERT statement */
+){
+  sqlite3_stmt *pRet = 0;         /* The INSERT statement */
+  sqlite3_stmt *pSelect = 0;      /* SELECT to read current values of row */
+  int rc = SQLITE_OK;
+  int bWR = 0;
+
+  rc = sessionTableIsWithoutRowid(db, zTab, &bWR);
+  if( rc==SQLITE_OK ){
+    char *zSelect = 0;
+    char *zInsert = 0;
+    SessionBuffer cols = {0, 0, 0};
+    SessionBuffer insbind = {0, 0, 0};
+    SessionBuffer pkcols = {0, 0, 0};
+    SessionBuffer selbind = {0, 0, 0};
+
+    const char *zComma = "";
+    const char *zComma2 = "";
+    int ii;
+    for(ii=0; ii<pApply->nCol; ii++){
+      sessionAppendStr(&cols, zComma, &rc);
+      sessionAppendIdent(&cols, pApply->azCol[ii], &rc);
+      sessionAppendStr(&insbind, zComma, &rc);
+      sessionAppendStr(&insbind, "?", &rc);
+      zComma = ", ";
+
+      if( pApply->abPK[ii] ){
+        sessionAppendStr(&pkcols, zComma2, &rc);
+        sessionAppendIdent(&pkcols, pApply->azCol[ii], &rc);
+        sessionAppendStr(&selbind, zComma2, &rc);
+        sessionAppendPrintf(&selbind, &rc, "?%d", ii+1);
+        zComma2 = ", ";
+      }
+    }
+    if( bWR==0 ){
+      sessionAppendStr(&cols, zComma, &rc);
+      sessionAppendStr(&cols, SESSIONS_ROWID, &rc);
+      sessionAppendStr(&insbind, zComma, &rc);
+      sessionAppendStr(&insbind, "?", &rc);
+    }
+
+    if( rc==SQLITE_OK ){
+      zSelect = sqlite3_mprintf("SELECT %s FROM %Q WHERE (%s) IS (%s)",
+          cols.aBuf, zTab, pkcols.aBuf, selbind.aBuf
+      );
+      if( zSelect==0 ) rc = SQLITE_NOMEM;
+    }
+    if( rc==SQLITE_OK ){
+      zInsert = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)",
+          zTab, cols.aBuf, insbind.aBuf
+      );
+      if( zInsert==0 ) rc = SQLITE_NOMEM;
+    }
+
+    if( rc==SQLITE_OK ){
+      rc = sessionPrepare(db, &pSelect, &pApply->zErr, zSelect);
+    }
+    if( rc==SQLITE_OK ){
+      rc = sessionPrepare(db, &pRet, &pApply->zErr, zInsert);
+    }
+
+    sqlite3_free(zSelect);
+    sqlite3_free(zInsert);
+    sqlite3_free(cols.aBuf);
+    sqlite3_free(insbind.aBuf);
+    sqlite3_free(pkcols.aBuf);
+    sqlite3_free(selbind.aBuf);
+  }
+
+  if( rc==SQLITE_OK ){
+    rc = sessionBindRow(
+        pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pSelect
+    );
+  }
+
+  if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){
+    int iCol;
+    for(iCol=0; iCol<pApply->nCol; iCol++){
+      sqlite3_value *pVal = pUp->apValue[iCol+pApply->nCol];
+      if( pVal==0 ){
+        pVal = sqlite3_column_value(pSelect, iCol);
+      }
+      rc = sqlite3_bind_value(pRet, iCol+1, pVal);
+    }
+    if( bWR==0 ){
+      sqlite3_bind_int64(pRet, iCol+1, sqlite3_column_int64(pSelect, iCol));
+    }
+  }
+  sessionFinalizeStmt(pSelect, &rc);
+
+  /* Delete the row from the database. */
+  if( rc==SQLITE_OK ){
+    rc = sessionBindRow(
+        pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pApply->pDelete
+    );
+    sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
+  }
+  if( rc==SQLITE_OK ){
+    sqlite3_step(pApply->pDelete);
+    rc = sqlite3_reset(pApply->pDelete);
+  }
+
+  if( rc!=SQLITE_OK ){
+    sqlite3_finalize(pRet);
+    pRet = 0;
+  }
+
+  *ppInsert = pRet;
+  return rc;
+}
+
+/*
+** Retry the changes accumulated in the pApply->constraints buffer. The
+** pApply->constraints buffer contains all changes to table zTab that
+** could not be applied due to SQLITE_CONSTRAINT errors. This function
+** attempts to apply them as follows:
+**
+**   1) It runs through the buffer and attempts to retry each change,
+**      removing any that are successfully applied from the buffer. This
+**      is repeated until no further progress can be made.
+**
+**   2) For each UPDATE change in the buffer, try the following in a
+**      savepoint transaction:
+**
+**      a) DELETE the affected row,
+**      b) Attempt step (1) with remaining changes,
+**      c) Attempt to INSERT a row equivalent to the one that would be
+**         created by applying this UPDATE change.
+**
+**      If the INSERT in (c) succeeds, the savepoint is committed and all
+**      successfully applied changes are removed from the buffer. Step (2)
+**      is then repeated.
+**
+**   3) Once step (2) has been attempted for each UPDATE in the change,
+**      a final attempt is made to apply each remaining change. This time,
+**      if an SQLITE_CONSTRAINT error is encountered, the conflict handler
+**      is invoked and the user has to decide whether to omit the change
+**      or rollback the entire _apply() operation.
 */
 static int sessionRetryConstraints(
   sqlite3 *db,
@@ -236036,41 +238967,101 @@ static int sessionRetryConstraints(
   void *pCtx                      /* First argument passed to xConflict */
 ){
   int rc = SQLITE_OK;
+  int iUpdate = 0;
 
+  /* Step (1) */
   while( pApply->constraints.nBuf ){
-    sqlite3_changeset_iter *pIter2 = 0;
     SessionBuffer cons = pApply->constraints;
     memset(&pApply->constraints, 0, sizeof(SessionBuffer));
 
-    rc = sessionChangesetStart(
-        &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1
+    rc = sessionApplyRetryBuffer(
+        &cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx
+    );
+
+    sqlite3_free(cons.aBuf);
+    if( rc!=SQLITE_OK ) break;
+
+    /* If no progress has been made this round, break out of the loop. */
+    if( pApply->constraints.nBuf>=cons.nBuf ) break;
+  }
+
+  /* Step (2) */
+  while( rc==SQLITE_OK && pApply->constraints.nBuf && !pApply->bNoUpdateLoop ){
+    SessionBuffer cons = {0, 0, 0};
+    sqlite3_changeset_iter *pUp = 0;
+    sqlite3_stmt *pInsert = 0;
+    int iSkip = 0;
+
+    rc = sessionRetryIterInit(
+        &pApply->constraints, bPatchset, zTab, pApply, &pUp
     );
     if( rc==SQLITE_OK ){
-      size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
-      int rc2;
-      pIter2->bPatchset = bPatchset;
-      pIter2->zTab = (char*)zTab;
-      pIter2->nCol = pApply->nCol;
-      pIter2->abPK = pApply->abPK;
-      sessionBufferGrow(&pIter2->tblhdr, nByte, &rc);
-      pIter2->apValue = (sqlite3_value**)pIter2->tblhdr.aBuf;
-      if( rc==SQLITE_OK ) memset(pIter2->apValue, 0, nByte);
+      int iThis = -1;
+      while( SQLITE_ROW==sqlite3changeset_next(pUp) ){
+        if( pUp->op==SQLITE_UPDATE ) iThis++;
+        if( iThis==iUpdate ) break;
+        iSkip++;
+      }
+      if( iThis==iUpdate ){
+        rc = sqlite3_exec(db, "SAVEPOINT update_op", 0, 0, 0);
+        if( rc==SQLITE_OK ){
+          rc = sessionUpdateToDeleteInsert(db, zTab, pApply, pUp, &pInsert);
+        }
+      }
+      sqlite3changeset_finalize(pUp);
+      if( iThis!=iUpdate ) break;
+    }
 
-      while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter2) ){
-        rc = sessionApplyOneWithRetry(db, pIter2, pApply, xConflict, pCtx);
+    if( rc==SQLITE_OK ){
+      cons = pApply->constraints;
+
+      while( rc==SQLITE_OK && pApply->constraints.nBuf>0 ){
+        SessionBuffer app = pApply->constraints;
+        memset(&pApply->constraints, 0, sizeof(SessionBuffer));
+        rc = sessionApplyRetryBuffer(
+            &app, iSkip, db, bPatchset, zTab, pApply, xConflict, pCtx
+        );
+        if( app.aBuf!=cons.aBuf ){
+          sqlite3_free(app.aBuf);
+        }
+        if( pApply->constraints.nBuf>=app.nBuf ){
+          break;
+        }
+        iSkip = -1;
       }
+    }
 
-      rc2 = sqlite3changeset_finalize(pIter2);
-      if( rc==SQLITE_OK ) rc = rc2;
+    iUpdate++;
+    if( rc==SQLITE_OK ){
+      sqlite3_step(pInsert);
+      rc = sqlite3_finalize(pInsert);
+      if( rc==SQLITE_CONSTRAINT ){
+        rc = sqlite3_exec(db, "ROLLBACK TO update_op", 0, 0, 0);
+        sqlite3_free(pApply->constraints.aBuf);
+        pApply->constraints = cons;
+        memset(&cons, 0, sizeof(cons));
+      }else if( rc==SQLITE_OK ){
+        iUpdate = 0;
+      }
+      if( rc==SQLITE_OK ){
+        rc = sqlite3_exec(db, "RELEASE update_op", 0, 0, 0);
+      }
+    }else{
+      sqlite3_finalize(pInsert);
     }
-    assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 );
 
     sqlite3_free(cons.aBuf);
-    if( rc!=SQLITE_OK ) break;
-    if( pApply->constraints.nBuf>=cons.nBuf ){
-      /* No progress was made on the last round. */
-      pApply->bDeferConstraints = 0;
-    }
+  }
+
+  /* Step (3) */
+  if( rc==SQLITE_OK && pApply->constraints.nBuf ){
+    SessionBuffer cons = pApply->constraints;
+    memset(&pApply->constraints, 0, sizeof(SessionBuffer));
+    pApply->bDeferConstraints = 0;
+    rc = sessionApplyRetryBuffer(
+        &cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx
+    );
+    sqlite3_free(cons.aBuf);
   }
 
   return rc;
@@ -236124,6 +239115,7 @@ static int sessionChangesetApply(
   sApply.bRebase = (ppRebase && pnRebase);
   sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
   sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
+  sApply.bNoUpdateLoop = !!(flags & SQLITE_CHANGESETAPPLY_NOUPDATELOOP);
   if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
     rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
   }
@@ -236547,6 +239539,21 @@ SQLITE_API int sqlite3changeset_apply_strm(
   );
 }
 
+/*
+** The parts of the sqlite3_changegroup structure used by the
+** sqlite3changegroup_change_xxx() APIs.
+*/
+typedef struct ChangeData ChangeData;
+struct ChangeData {
+  SessionTable *pTab;
+  int bIndirect;
+  int eOp;
+
+  int nBufAlloc;
+  SessionBuffer *aBuf;
+  SessionBuffer record;
+};
+
 /*
 ** sqlite3_changegroup handle.
 */
@@ -236558,12 +239565,17 @@ struct sqlite3_changegroup {
 
   sqlite3 *db;                    /* Configured by changegroup_schema() */
   char *zDb;                      /* Configured by changegroup_schema() */
+  ChangeData cd;                  /* Used by changegroup_change_xxx() APIs. */
 };
 
 /*
 ** This function is called to merge two changes to the same row together as
 ** part of an sqlite3changeset_concat() operation. A new change object is
 ** allocated and a pointer to it stored in *ppNew.
+**
+** Because they have been vetted by sqlite3changegroup_add() or similar,
+** both the aRec[] change and the pExist change are safe to use without
+** checking for buffer overflows.
 */
 static int sessionChangeMerge(
   SessionTable *pTab,             /* Table structure */
@@ -236704,7 +239716,7 @@ static int sessionChangeMerge(
           memcpy(aCsr, aRec, nRec);
           aCsr += nRec;
         }else{
-          if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0,aRec,0) ){
+          if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist,0,aRec,0) ){
             sqlite3_free(pNew);
             pNew = 0;
           }
@@ -236797,15 +239809,14 @@ static int sessionChangesetExtendRecord(
       switch( eType ){
         case SQLITE_FLOAT:
         case SQLITE_INTEGER: {
-          i64 iVal;
-          if( eType==SQLITE_INTEGER ){
-            iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
-          }else{
-            double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
-            memcpy(&iVal, &rVal, sizeof(i64));
-          }
           if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
-            sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
+            if( eType==SQLITE_INTEGER ){
+              sqlite3_int64 iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+              sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
+            }else{
+              double rVal = sqlite3_column_double(pTab->pDfltStmt, ii);
+              sessionPutDouble(&pOut->aBuf[pOut->nBuf], rVal);
+            }
             pOut->nBuf += 8;
           }
           break;
@@ -236876,13 +239887,19 @@ static int sessionChangesetFindTable(
   int nCol = 0;
 
   *ppTab = 0;
-  sqlite3changeset_pk(pIter, &abPK, &nCol);
 
   /* Search the list for an existing table */
   for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
     if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
   }
 
+
+  if( pIter ){
+    sqlite3changeset_pk(pIter, &abPK, &nCol);
+  }else if( !pTab && !pGrp->db ){
+    return SQLITE_OK;
+  }
+
   /* If one was not found above, create a new table now */
   if( !pTab ){
     SessionTable **ppNew;
@@ -236894,15 +239911,17 @@ static int sessionChangesetFindTable(
     memset(pTab, 0, sizeof(SessionTable));
     pTab->nCol = nCol;
     pTab->abPK = (u8*)&pTab[1];
-    memcpy(pTab->abPK, abPK, nCol);
+    if( nCol>0 ){
+      memcpy(pTab->abPK, abPK, nCol);
+    }
     pTab->zName = (char*)&pTab->abPK[nCol];
     memcpy(pTab->zName, zTab, nTab+1);
 
     if( pGrp->db ){
       pTab->nCol = 0;
       rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
-      if( rc ){
-        assert( pTab->azCol==0 );
+      if( rc || pTab->nCol==0 ){
+        sqlite3_free(pTab->azCol);
         sqlite3_free(pTab);
         return rc;
       }
@@ -236917,7 +239936,7 @@ static int sessionChangesetFindTable(
   }
 
   /* Check that the table is compatible. */
-  if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
+  if( pIter && !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
     rc = SQLITE_SCHEMA;
   }
 
@@ -236926,44 +239945,27 @@ static int sessionChangesetFindTable(
 }
 
 /*
-** Add the change currently indicated by iterator pIter to the hash table
-** belonging to changegroup pGrp.
+** Add a single change to the changegroup pGrp.
 */
 static int sessionOneChangeToHash(
-  sqlite3_changegroup *pGrp,
-  sqlite3_changeset_iter *pIter,
-  int bRebase
+  sqlite3_changegroup *pGrp,      /* Changegroup to update */
+  SessionTable *pTab,             /* Table change pertains to */
+  int op,                         /* One of SQLITE_INSERT, UPDATE, DELETE */
+  int bIndirect,                  /* True to flag change as "indirect" */
+  int nCol,                       /* Number of columns in record(s) */
+  u8 *aRec,                       /* Serialized change record(s) */
+  int nRec,                       /* Size of aRec[] in bytes */
+  int bRebase                     /* True if this is a rebase blob */
 ){
   int rc = SQLITE_OK;
-  int nCol = 0;
-  int op = 0;
   int iHash = 0;
-  int bIndirect = 0;
   SessionChange *pChange = 0;
   SessionChange *pExist = 0;
   SessionChange **pp = 0;
-  SessionTable *pTab = 0;
-  u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
-  int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
 
   assert( nRec>0 );
 
-  /* Ensure that only changesets, or only patchsets, but not a mixture
-  ** of both, are being combined. It is an error to try to combine a
-  ** changeset and a patchset.  */
-  if( pGrp->pList==0 ){
-    pGrp->bPatch = pIter->bPatchset;
-  }else if( pIter->bPatchset!=pGrp->bPatch ){
-    rc = SQLITE_ERROR;
-  }
-
-  if( rc==SQLITE_OK ){
-    const char *zTab = 0;
-    sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
-    rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
-  }
-
-  if( rc==SQLITE_OK && nCol<pTab->nCol ){
+  if( nCol<pTab->nCol ){
     SessionBuffer *pBuf = &pGrp->rec;
     rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
     aRec = pBuf->aBuf;
@@ -236971,7 +239973,7 @@ static int sessionOneChangeToHash(
     assert( pGrp->db );
   }
 
-  if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
+  if( rc==SQLITE_OK && sessionGrowHash(0, pGrp->bPatch, pTab) ){
     rc = SQLITE_NOMEM;
   }
 
@@ -236979,12 +239981,12 @@ static int sessionOneChangeToHash(
     /* Search for existing entry. If found, remove it from the hash table.
     ** Code below may link it back in.  */
     iHash = sessionChangeHash(
-        pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
+        pTab, (pGrp->bPatch && op==SQLITE_DELETE), aRec, pTab->nChange
     );
     for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
       int bPkOnly1 = 0;
       int bPkOnly2 = 0;
-      if( pIter->bPatchset ){
+      if( pGrp->bPatch ){
         bPkOnly1 = (*pp)->op==SQLITE_DELETE;
         bPkOnly2 = op==SQLITE_DELETE;
       }
@@ -236999,7 +240001,7 @@ static int sessionOneChangeToHash(
 
   if( rc==SQLITE_OK ){
     rc = sessionChangeMerge(pTab, bRebase,
-        pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
+        pGrp->bPatch, pExist, op, bIndirect, aRec, nRec, &pChange
     );
   }
   if( rc==SQLITE_OK && pChange ){
@@ -237008,6 +240010,47 @@ static int sessionOneChangeToHash(
     pTab->nEntry++;
   }
 
+  return rc;
+}
+
+/*
+** Add the change currently indicated by iterator pIter to the hash table
+** belonging to changegroup pGrp.
+*/
+static int sessionOneChangeIterToHash(
+  sqlite3_changegroup *pGrp,
+  sqlite3_changeset_iter *pIter,
+  int bRebase
+){
+  u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
+  int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
+  const char *zTab = 0;
+  int nCol = 0;
+  int op = 0;
+  int bIndirect = 0;
+  int rc = SQLITE_OK;
+  SessionTable *pTab = 0;
+
+  /* Ensure that only changesets, or only patchsets, but not a mixture
+  ** of both, are being combined. It is an error to try to combine a
+  ** changeset and a patchset.  */
+  if( pGrp->pList==0 ){
+    pGrp->bPatch = pIter->bPatchset;
+  }else if( pIter->bPatchset!=pGrp->bPatch ){
+    rc = SQLITE_ERROR;
+  }
+
+  if( rc==SQLITE_OK ){
+    sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
+    rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
+  }
+
+  if( rc==SQLITE_OK ){
+    rc = sessionOneChangeToHash(
+        pGrp, pTab, op, bIndirect, nCol, aRec, nRec, bRebase
+    );
+  }
+
   if( rc==SQLITE_OK ) rc = pIter->rc;
   return rc;
 }
@@ -237027,7 +240070,7 @@ static int sessionChangesetToHash(
 
   pIter->in.bNoDiscard = 1;
   while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
-    rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
+    rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase);
     if( rc!=SQLITE_OK ) break;
   }
 
@@ -237117,6 +240160,33 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){
   return rc;
 }
 
+/*
+** Configure a changegroup object.
+*/
+SQLITE_API int sqlite3changegroup_config(
+  sqlite3_changegroup *pGrp,
+  int op,
+  void *pArg
+){
+  int rc = SQLITE_OK;
+
+  switch( op ){
+    case SQLITE_CHANGEGROUP_CONFIG_PATCHSET: {
+      int arg = *(int*)pArg;
+      if( pGrp->pList==0  && arg>=0 ){
+        pGrp->bPatch = (arg>0);
+      }
+      *(int*)pArg = pGrp->bPatch;
+      break;
+    }
+    default:
+      rc = SQLITE_MISUSE;
+      break;
+  }
+
+  return rc;
+}
+
 /*
 ** Provide a database schema to the changegroup object.
 */
@@ -237175,7 +240245,7 @@ SQLITE_API int sqlite3changegroup_add_change(
     rc = SQLITE_ERROR;
   }else{
     pIter->in.bNoDiscard = 1;
-    rc = sessionOneChangeToHash(pGrp, pIter, 0);
+    rc = sessionOneChangeIterToHash(pGrp, pIter, 0);
   }
   return rc;
 }
@@ -237227,6 +240297,12 @@ SQLITE_API int sqlite3changegroup_output_strm(
 */
 SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
   if( pGrp ){
+    int ii;
+    for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
+      sqlite3_free(pGrp->cd.aBuf[ii].aBuf);
+    }
+    sqlite3_free(pGrp->cd.record.aBuf);
+    sqlite3_free(pGrp->cd.aBuf);
     sqlite3_free(pGrp->zDb);
     sessionDeleteTable(0, pGrp->pList);
     sqlite3_free(pGrp->rec.aBuf);
@@ -237657,6 +240733,328 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){
   return rc;
 }
 
+/*
+** Begin adding a change to a changegroup object.
+*/
+SQLITE_API int sqlite3changegroup_change_begin(
+  sqlite3_changegroup *pGrp,
+  int eOp,
+  const char *zTab,
+  int bIndirect,
+  char **pzErr
+){
+  SessionTable *pTab = 0;
+  int rc = SQLITE_OK;
+
+  if( pGrp->cd.pTab ){
+    rc = SQLITE_MISUSE;
+  }else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_UPDATE && eOp!=SQLITE_DELETE ){
+    rc = SQLITE_ERROR;
+  }else{
+    rc = sessionChangesetFindTable(pGrp, zTab, 0, &pTab);
+  }
+  if( rc==SQLITE_OK ){
+    if( pTab==0 ){
+      if( pzErr ){
+        *pzErr = sqlite3_mprintf("no such table: %s", zTab);
+      }
+      rc = SQLITE_ERROR;
+    }else{
+      int nReq = pTab->nCol * (eOp==SQLITE_UPDATE ? 2 : 1);
+      pGrp->cd.pTab = pTab;
+      pGrp->cd.eOp = eOp;
+      pGrp->cd.bIndirect = bIndirect;
+
+      if( pGrp->cd.nBufAlloc<nReq ){
+        SessionBuffer *aBuf = (SessionBuffer*)sqlite3_realloc(
+            pGrp->cd.aBuf, nReq * sizeof(SessionBuffer)
+        );
+        if( aBuf==0 ){
+          rc = SQLITE_NOMEM;
+        }else{
+          memset(&aBuf[pGrp->cd.nBufAlloc], 0,
+              sizeof(SessionBuffer) * (nReq - pGrp->cd.nBufAlloc)
+          );
+          pGrp->cd.aBuf = aBuf;
+          pGrp->cd.nBufAlloc = nReq;
+        }
+      }
+
+#ifdef SQLITE_DEBUG
+      {
+        /* Assert that all column values are currently undefined */
+        int ii;
+        for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
+          assert( pGrp->cd.aBuf[ii].nBuf==0 );
+        }
+      }
+#endif
+    }
+  }
+
+  return rc;
+}
+
+/*
+** This function does processing common to the _change_int64(), _change_text()
+** and other similar APIs.
+*/
+static int checkChangeParams(
+  sqlite3_changegroup *pGrp,
+  int bNew,
+  int iCol,
+  sqlite3_int64 nReq,
+  SessionBuffer **ppBuf
+){
+  int rc = SQLITE_OK;
+  if( pGrp->cd.pTab==0 ){
+    rc = SQLITE_MISUSE;
+  }else if( iCol<0 || iCol>=pGrp->cd.pTab->nCol ){
+    rc = SQLITE_RANGE;
+  }else if(
+      (bNew && pGrp->cd.eOp==SQLITE_DELETE)
+   || (!bNew && pGrp->cd.eOp==SQLITE_INSERT)
+  ){
+    rc = SQLITE_ERROR;
+  }else{
+    SessionBuffer *pBuf = &pGrp->cd.aBuf[iCol];
+    if( pGrp->cd.eOp==SQLITE_UPDATE && bNew ){
+      pBuf += pGrp->cd.pTab->nCol;
+    }
+    pBuf->nBuf = 0;
+    sessionBufferGrow(pBuf, nReq, &rc);
+    pBuf->nBuf = nReq;
+    *ppBuf = pBuf;
+  }
+  return rc;
+}
+
+/*
+** Configure the change currently under construction with an integer value.
+*/
+SQLITE_API int sqlite3changegroup_change_int64(
+  sqlite3_changegroup *pGrp,
+  int bNew,
+  int iCol,
+  sqlite3_int64 iVal
+){
+  int rc = SQLITE_OK;
+  SessionBuffer *pBuf = 0;
+
+  if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
+    return rc;
+  }
+
+  pBuf->aBuf[0] = SQLITE_INTEGER;
+  sessionPutI64(&pBuf->aBuf[1], iVal);
+  return SQLITE_OK;
+}
+
+/*
+** Configure the change currently under construction with a null value.
+*/
+SQLITE_API int sqlite3changegroup_change_null(
+  sqlite3_changegroup *pGrp,
+  int bNew,
+  int iCol
+){
+  int rc = SQLITE_OK;
+  SessionBuffer *pBuf = 0;
+
+  if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 1, &pBuf)) ){
+    return rc;
+  }
+
+  pBuf->aBuf[0] = SQLITE_NULL;
+  return SQLITE_OK;
+}
+
+/*
+** Configure the change currently under construction with a real value.
+*/
+SQLITE_API int sqlite3changegroup_change_double(
+  sqlite3_changegroup *pGrp,
+  int bNew,
+  int iCol,
+  double fVal
+){
+  int rc = SQLITE_OK;
+  SessionBuffer *pBuf = 0;
+
+  if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
+    return rc;
+  }
+
+  pBuf->aBuf[0] = SQLITE_FLOAT;
+  sessionPutDouble(&pBuf->aBuf[1], fVal);
+  return SQLITE_OK;
+}
+
+/*
+** Configure the change currently under construction with a text value.
+*/
+SQLITE_API int sqlite3changegroup_change_text(
+  sqlite3_changegroup *pGrp,
+  int bNew,
+  int iCol,
+  const char *pVal,
+  int nVal
+){
+  int nText = nVal>=0 ? nVal : strlen(pVal);
+  sqlite3_int64 nByte = 1 + sessionVarintLen(nText) + nText;
+  int rc = SQLITE_OK;
+  SessionBuffer *pBuf = 0;
+
+  if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
+    return rc;
+  }
+
+  pBuf->aBuf[0] = SQLITE_TEXT;
+  pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nText));
+  memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nText);
+  pBuf->nBuf += nText;
+
+  return SQLITE_OK;
+}
+
+/*
+** Configure the change currently under construction with a blob value.
+*/
+SQLITE_API int sqlite3changegroup_change_blob(
+  sqlite3_changegroup *pGrp,
+  int bNew,
+  int iCol,
+  const void *pVal,
+  int nVal
+){
+  sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + (i64)nVal;
+  int rc = SQLITE_OK;
+  SessionBuffer *pBuf = 0;
+
+  if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
+    return rc;
+  }
+
+  pBuf->aBuf[0] = SQLITE_BLOB;
+  pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nVal));
+  memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nVal);
+  pBuf->nBuf += nVal;
+
+  return SQLITE_OK;
+}
+
+/*
+** Finish any change currently being constructed by the changegroup object.
+*/
+SQLITE_API int sqlite3changegroup_change_finish(
+  sqlite3_changegroup *pGrp,
+  int bDiscard,
+  char **pzErr
+){
+  int rc = SQLITE_OK;
+  if( pGrp->cd.pTab ){
+    SessionBuffer *aBuf = pGrp->cd.aBuf;
+    int ii;
+
+    if( bDiscard==0 ){
+      int nBuf = pGrp->cd.pTab->nCol;
+      u8 eUndef = SQLITE_NULL;
+      if( pGrp->cd.eOp==SQLITE_UPDATE ){
+        for(ii=0; ii<nBuf; ii++){
+          if( pGrp->cd.pTab->abPK[ii] ){
+            if( aBuf[ii].nBuf<=1 ){
+              *pzErr = sqlite3_mprintf(
+                  "invalid change: %s value in PK of old.* record",
+                  aBuf[ii].nBuf==1 ? "null" : "undefined"
+              );
+              rc = SQLITE_ERROR;
+              break;
+            }else if( aBuf[ii + nBuf].nBuf>0 ){
+              *pzErr = sqlite3_mprintf(
+                  "invalid change: defined value in PK of new.* record"
+              );
+              rc = SQLITE_ERROR;
+              break;
+            }
+          }else
+          if( pGrp->bPatch==0 && (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){
+            *pzErr = sqlite3_mprintf(
+                "invalid change: column %d "
+                "- old.* value is %sdefined but new.* is %sdefined",
+                ii, aBuf[ii].nBuf ? "" : "un", aBuf[ii+nBuf].nBuf ? "" : "un"
+            );
+            rc = SQLITE_ERROR;
+            break;
+          }
+        }
+        eUndef = 0x00;
+        if( pGrp->bPatch==0 ) nBuf = nBuf * 2;
+      }else{
+        for(ii=0; ii<nBuf; ii++){
+          int isPK = pGrp->cd.pTab->abPK[ii];
+          if( (pGrp->cd.eOp==SQLITE_INSERT || pGrp->bPatch==0 || isPK)
+           && aBuf[ii].nBuf==0
+          ){
+            *pzErr = sqlite3_mprintf(
+                "invalid change: column %d is undefined", ii
+            );
+            rc = SQLITE_ERROR;
+            break;
+          }
+          if( aBuf[ii].nBuf==1 && isPK ){
+            *pzErr = sqlite3_mprintf(
+                "invalid change: null value in PK"
+            );
+            rc = SQLITE_ERROR;
+            break;
+          }
+        }
+      }
+
+      pGrp->cd.record.nBuf = 0;
+      for(ii=0; ii<nBuf; ii++){
+        SessionBuffer *p = &pGrp->cd.aBuf[ii];
+        if( pGrp->bPatch ){
+          if( pGrp->cd.pTab->abPK[ii]==0 ){
+            if( pGrp->cd.eOp==SQLITE_UPDATE ){
+              p += pGrp->cd.pTab->nCol;
+            }else if( pGrp->cd.eOp==SQLITE_DELETE ){
+              continue;
+            }
+          }
+        }
+        if( 0==sessionBufferGrow(&pGrp->cd.record, p->nBuf?p->nBuf:1, &rc) ){
+          if( p->nBuf ){
+            memcpy(&pGrp->cd.record.aBuf[pGrp->cd.record.nBuf],p->aBuf,p->nBuf);
+            pGrp->cd.record.nBuf += p->nBuf;
+          }else{
+            pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef;
+          }
+        }
+      }
+      if( rc==SQLITE_OK ){
+        rc = sessionOneChangeToHash(
+            pGrp, pGrp->cd.pTab,
+            pGrp->cd.eOp, pGrp->cd.bIndirect, pGrp->cd.pTab->nCol,
+            pGrp->cd.record.aBuf, pGrp->cd.record.nBuf, 0
+        );
+      }
+    }
+
+    /* Reset all aBuf[] entries to "undefined". */
+    {
+      int nZero = pGrp->cd.pTab->nCol;
+      if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero;
+      for(ii=0; ii<nZero; ii++){
+        pGrp->cd.aBuf[ii].nBuf = 0;
+      }
+    }
+    pGrp->cd.pTab = 0;
+  }
+
+  return rc;
+}
+
 #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
 
 /************** End of sqlite3session.c **************************************/
@@ -238536,7 +241934,13 @@ typedef sqlite3_uint64 u64;
 # define FLEXARRAY 1
 #endif
 
-#endif
+#endif /* SQLITE_AMALGAMATION */
+
+/*
+** Constants for the largest and smallest possible 32-bit signed integers.
+*/
+# define LARGEST_INT32  ((int)(0x7fffffff))
+# define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32))
 
 /* Truncate very long tokens to this many bytes. Hard limit is
 ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
@@ -239581,14 +242985,22 @@ typedef union {
 #define sqlite3Fts5ParserARG_PARAM ,pParse
 #define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse;
 #define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse;
+#undef fts5YYREALLOC
 #define fts5YYREALLOC realloc
+#undef fts5YYFREE
 #define fts5YYFREE free
+#undef fts5YYDYNSTACK
 #define fts5YYDYNSTACK 0
+#undef fts5YYSIZELIMIT
+#define sqlite3Fts5ParserCTX(P) 0
 #define sqlite3Fts5ParserCTX_SDECL
 #define sqlite3Fts5ParserCTX_PDECL
 #define sqlite3Fts5ParserCTX_PARAM
 #define sqlite3Fts5ParserCTX_FETCH
 #define sqlite3Fts5ParserCTX_STORE
+#undef fts5YYERRORSYMBOL
+#undef fts5YYERRSYMDT
+#undef fts5YYFALLBACK
 #define fts5YYNSTATE             35
 #define fts5YYNRULE              28
 #define fts5YYNRULE_WITH_ACTION  28
@@ -239913,15 +243325,24 @@ static int fts5yyGrowStack(fts5yyParser *p){
   int newSize;
   int idx;
   fts5yyStackEntry *pNew;
+#ifdef fts5YYSIZELIMIT
+  int nLimit = fts5YYSIZELIMIT(sqlite3Fts5ParserCTX(p));
+#endif
 
   newSize = oldSize*2 + 100;
+#ifdef fts5YYSIZELIMIT
+  if( newSize>nLimit ){
+    newSize = nLimit;
+    if( newSize<=oldSize ) return 1;
+  }
+#endif
   idx = (int)(p->fts5yytos - p->fts5yystack);
   if( p->fts5yystack==p->fts5yystk0 ){
-    pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0]));
+    pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0]), sqlite3Fts5ParserCTX(p));
     if( pNew==0 ) return 1;
     memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0]));
   }else{
-    pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0]));
+    pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0]), sqlite3Fts5ParserCTX(p));
     if( pNew==0 ) return 1;
   }
   p->fts5yystack = pNew;
@@ -240101,7 +243522,9 @@ static void sqlite3Fts5ParserFinalize(void *p){
   }
 
 #if fts5YYGROWABLESTACK
-  if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack);
+  if( pParser->fts5yystack!=pParser->fts5yystk0 ){
+    fts5YYFREE(pParser->fts5yystack, sqlite3Fts5ParserCTX(pParser));
+  }
 #endif
 }
 
@@ -241383,7 +244806,7 @@ static void fts5SnippetFunction(
 
   iBestCol = (iCol>=0 ? iCol : 0);
   nPhrase = pApi->xPhraseCount(pFts);
-  aSeen = sqlite3_malloc(nPhrase);
+  aSeen = sqlite3_malloc64(nPhrase);
   if( aSeen==0 ){
     rc = SQLITE_NOMEM;
   }
@@ -242038,7 +245461,7 @@ static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
     if( nIn<0 ){
       nIn = (int)strlen(pIn);
     }
-    zRet = (char*)sqlite3_malloc(nIn+1);
+    zRet = (char*)sqlite3_malloc64((i64)nIn+1);
     if( zRet ){
       memcpy(zRet, pIn, nIn);
       zRet[nIn] = '\0';
@@ -242738,7 +246161,7 @@ static int sqlite3Fts5ConfigParse(
   sqlite3_int64 nByte;
   int bUnindexed = 0;             /* True if there are one or more UNINDEXED */
 
-  *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
+  *ppOut = pRet = (Fts5Config*)sqlite3_malloc64(sizeof(Fts5Config));
   if( pRet==0 ) return SQLITE_NOMEM;
   memset(pRet, 0, sizeof(Fts5Config));
   pRet->pGlobal = pGlobal;
@@ -243286,8 +246709,6 @@ static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){
   va_end(ap);
 }
 
-
-
 /*
 ** 2014 May 31
 **
@@ -243604,7 +247025,7 @@ static int sqlite3Fts5ExprNew(
 
   assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
   if( sParse.rc==SQLITE_OK ){
-    *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
+    *ppNew = pNew = sqlite3_malloc64(sizeof(Fts5Expr));
     if( pNew==0 ){
       sParse.rc = SQLITE_NOMEM;
       sqlite3Fts5ParseNodeFree(sParse.pExpr);
@@ -243756,7 +247177,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
     p2->pRoot = 0;
 
     if( sParse.rc==SQLITE_OK ){
-      Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
+      Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64(
           p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
       );
       if( ap==0 ){
@@ -246668,7 +250089,7 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte
   int rc = SQLITE_OK;
   Fts5Hash *pNew;
 
-  *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
+  *ppNew = pNew = (Fts5Hash*)sqlite3_malloc64(sizeof(Fts5Hash));
   if( pNew==0 ){
     rc = SQLITE_NOMEM;
   }else{
@@ -248090,7 +251511,7 @@ static void fts5DataRelease(Fts5Data *pData){
 static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
   Fts5Data *pRet = fts5DataRead(p, iRowid);
   if( pRet ){
-    if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
+    if( pRet->szLeaf<4 || pRet->szLeaf>pRet->nn ){
       FTS5_CORRUPT_ROWID(p, iRowid);
       fts5DataRelease(pRet);
       pRet = 0;
@@ -249261,7 +252682,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
 
     /* If necessary, grow the pIter->aRowidOffset[] array. */
     if( iRowidOffset>=pIter->nRowidOffset ){
-      int nNew = pIter->nRowidOffset + 8;
+      i64 nNew = pIter->nRowidOffset + 8;
       int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int));
       if( aNew==0 ){
         p->rc = SQLITE_NOMEM;
@@ -249744,6 +253165,10 @@ static void fts5LeafSeek(
     if( nKeep<nMatch ){
       goto search_failed;
     }
+    if( (iOff+nNew)>n ){
+      FTS5_CORRUPT_ITER(p, pIter);
+      return;
+    }
 
     assert( nKeep>=nMatch );
     if( nKeep==nMatch ){
@@ -250720,8 +254145,7 @@ static void fts5PoslistFilterCallback(
 
     do {
       while( i<nChunk && pChunk[i]!=0x01 ){
-        while( pChunk[i] & 0x80 ) i++;
-        i++;
+        fts5IndexSkipVarint(pChunk, i);
       }
       if( pCtx->eState ){
         fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
@@ -250870,7 +254294,7 @@ static void fts5IndexExtractColset(
       /* Advance pointer p until it points to pEnd or an 0x01 byte that is
       ** not part of a varint */
       while( p<pEnd && *p!=0x01 ){
-        while( *p++ & 0x80 );
+        while( p<pEnd && (*p++ & 0x80) );
       }
 
       if( pColset->aiCol[i]==iCurrent ){
@@ -250967,8 +254391,11 @@ static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
 
   assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
   assert( pIter->pColset );
+  assert( pIter->poslist.nSpace>=pIter->pIndex->pConfig->nCol );
 
-  if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){
+  if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf
+   || pSeg->nPos>pIter->pIndex->pConfig->nCol
+  ){
     fts5IterSetOutputs_Col(pIter, pSeg);
   }else{
     u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset];
@@ -252408,7 +255835,7 @@ static void fts5DoSecureDelete(
   int iSegid = pSeg->pSeg->iSegid;
   u8 *aPg = pSeg->pLeaf->p;
   int nPg = pSeg->pLeaf->nn;
-  int iPgIdx = pSeg->pLeaf->szLeaf;
+  int iPgIdx = pSeg->pLeaf->szLeaf;         /* Offset of page footer */
 
   u64 iDelta = 0;
   int iNextOff = 0;
@@ -252462,6 +255889,11 @@ static void fts5DoSecureDelete(
     }else{
       iStart = fts5GetU16(&aPg[0]);
     }
+    if( iStart>nPg ){
+      FTS5_CORRUPT_IDX(p);
+      sqlite3_free(aIdx);
+      return;
+    }
 
     iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
     assert_nc( iSOP<=pSeg->iLeafOffset );
@@ -252487,7 +255919,7 @@ static void fts5DoSecureDelete(
         iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
       }
       assert_nc( iSOP==pSeg->iLeafOffset );
-      iNextOff = pSeg->iLeafOffset + pSeg->nPos;
+      iNextOff = iSOP + pSeg->nPos;
     }
   }
 
@@ -252567,31 +255999,31 @@ static void fts5DoSecureDelete(
       ** is another term following it on this page. So the subsequent term
       ** needs to be moved to replace the term associated with the entry
       ** being removed. */
-      int nPrefix = 0;
-      int nSuffix = 0;
-      int nPrefix2 = 0;
-      int nSuffix2 = 0;
+      u64 nPrefix = 0;
+      u64 nSuffix = 0;
+      u64 nPrefix2 = 0;
+      u64 nSuffix2 = 0;
 
       iDelKeyOff = iNextOff;
-      iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2);
-      iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2);
+      iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2);
+      iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2);
 
       if( iKey!=1 ){
-        iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix);
+        iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix);
       }
-      iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix);
+      iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix);
 
       nPrefix = MIN(nPrefix, nPrefix2);
       nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
 
-      if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
+      if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){
         FTS5_CORRUPT_IDX(p);
       }else{
         if( iKey!=1 ){
           iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
         }
         iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
-        if( nPrefix2>pSeg->term.n ){
+        if( nPrefix2>(u64)pSeg->term.n ){
           FTS5_CORRUPT_IDX(p);
         }else if( nPrefix2>nPrefix ){
           memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
@@ -252622,7 +256054,7 @@ static void fts5DoSecureDelete(
         u8 *aTermIdx = &pTerm->p[pTerm->szLeaf];
         int nTermIdx = pTerm->nn - pTerm->szLeaf;
         int iTermIdx = 0;
-        int iTermOff = 0;
+        i64 iTermOff = 0;
 
         while( 1 ){
           u32 iVal = 0;
@@ -252633,12 +256065,15 @@ static void fts5DoSecureDelete(
         }
         nTermIdx = iTermIdx;
 
-        memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
-        fts5PutU16(&pTerm->p[2], iTermOff);
-
-        fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
-        if( nTermIdx==0 ){
-          fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
+        if( iTermOff>pTerm->szLeaf ){
+          FTS5_CORRUPT_IDX(p);
+        }else{
+          memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
+          fts5PutU16(&pTerm->p[2], iTermOff);
+          fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
+          if( nTermIdx==0 ){
+            fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
+          }
         }
       }
       fts5DataRelease(pTerm);
@@ -252661,7 +256096,9 @@ static void fts5DoSecureDelete(
     int iPrevKeyOut = 0;
     int iKeyIn = 0;
 
-    memmove(&aPg[iOff], &aPg[iNextOff], nMove);
+    if( nMove>0 ){
+      memmove(&aPg[iOff], &aPg[iNextOff], nMove);
+    }
     iPgIdx -= nShift;
     nPg = iPgIdx;
     fts5PutU16(&aPg[2], iPgIdx);
@@ -253099,7 +256536,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
       fts5StructureRelease(pStruct);
       pStruct = pNew;
       nMin = 1;
-      nMerge = nMerge*-1;
+      nMerge = (nMerge==SMALLEST_INT32 ? LARGEST_INT32 : (nMerge*-1));
     }
     if( pStruct && pStruct->nLevel ){
       if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){
@@ -253581,16 +257018,16 @@ struct Fts5TokenDataMap {
 ** aMap[] variables.
 */
 struct Fts5TokenDataIter {
-  int nMapAlloc;                  /* Allocated size of aMap[] in entries */
-  int nMap;                       /* Number of valid entries in aMap[] */
+  i64 nMapAlloc;                  /* Allocated size of aMap[] in entries */
+  i64 nMap;                       /* Number of valid entries in aMap[] */
   Fts5TokenDataMap *aMap;         /* Array of (rowid+pos -> token) mappings */
 
   /* The following are used for prefix-queries only. */
   Fts5Buffer terms;
 
   /* The following are used for other full-token tokendata queries only. */
-  int nIter;
-  int nIterAlloc;
+  i64 nIter;
+  i64 nIterAlloc;
   Fts5PoslistReader *aPoslistReader;
   int *aPoslistToIter;
   Fts5Iter *apIter[FLEXARRAY];
@@ -253646,11 +257083,11 @@ static void fts5TokendataIterAppendMap(
 ){
   if( p->rc==SQLITE_OK ){
     if( pT->nMap==pT->nMapAlloc ){
-      int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
-      int nAlloc = nNew * sizeof(Fts5TokenDataMap);
+      i64 nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
+      i64 nAlloc = nNew * sizeof(Fts5TokenDataMap);
       Fts5TokenDataMap *aNew;
 
-      aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
+      aNew = (Fts5TokenDataMap*)sqlite3_realloc64(pT->aMap, nAlloc);
       if( aNew==0 ){
         p->rc = SQLITE_NOMEM;
         return;
@@ -253676,7 +257113,7 @@ static void fts5TokendataIterAppendMap(
 */
 static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
   Fts5TokenDataMap *aTmp = 0;
-  int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
+  i64 nByte = pT->nMap * sizeof(Fts5TokenDataMap);
 
   aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
   if( aTmp ){
@@ -254210,9 +257647,10 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
 
   if( p->rc==SQLITE_OK ){
     if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
-      int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
-      int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
-      Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
+      i64 nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
+      i64 nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
+      Fts5TokenDataIter *pNew;
+      pNew = (Fts5TokenDataIter*)sqlite3_realloc64(pIn, nByte);
 
       if( pNew==0 ){
         p->rc = SQLITE_NOMEM;
@@ -254309,8 +257747,8 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
 
       /* Ensure the token-mapping is large enough */
       if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
-        int nNew = (pT->nMapAlloc + nByte) * 2;
-        Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
+        i64 nNew = (pT->nMapAlloc + nByte) * 2;
+        Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc64(
             pT->aMap, nNew*sizeof(Fts5TokenDataMap)
         );
         if( aNew==0 ){
@@ -257225,7 +260663,7 @@ static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
   if( sqlite3_libversion_number()>=3008002 )
 #endif
   {
-    pIdxInfo->estimatedRows = nRow;
+    pIdxInfo->estimatedRows = MAX(1, nRow);
   }
 #endif
 }
@@ -257294,19 +260732,30 @@ static int fts5UsePatternMatch(
 **  a) If a MATCH operator is present, the cost depends on the other
 **     constraints also present. As follows:
 **
-**       * No other constraints:         cost=1000.0
-**       * One rowid range constraint:   cost=750.0
-**       * Both rowid range constraints: cost=500.0
-**       * An == rowid constraint:       cost=100.0
+**       * No other constraints:         cost=50000.0
+**       * One rowid range constraint:   cost=37500.0
+**       * Both rowid range constraints: cost=30000.0
+**       * An == rowid constraint:       cost=25000.0
 **
 **  b) Otherwise, if there is no MATCH:
 **
-**       * No other constraints:         cost=1000000.0
-**       * One rowid range constraint:   cost=750000.0
-**       * Both rowid range constraints: cost=250000.0
-**       * An == rowid constraint:       cost=10.0
+**       * No other constraints:         cost=3000000.0
+**       * One rowid range constraints:  cost=2250000.0
+**       * Both rowid range constraint:  cost=750000.0
+**       * An == rowid constraint:       cost=25.0
 **
 ** Costs are not modified by the ORDER BY clause.
+**
+** The ratios used in case (a) are based on informal results obtained from
+** the tool/fts5cost.tcl script. The "MATCH and ==" combination has the
+** cost set quite high because the query may be a prefix query. Unless
+** there is a prefix index, prefix queries with rowid constraints are much
+** more expensive than non-prefix queries with rowid constraints.
+**
+** The estimated rows returned is set to the cost/40. For simple queries,
+** experimental results show that cost/4 might be about right. But for
+** more complex queries that use multiple terms the number of rows might
+** be far fewer than this. So we compromise and use cost/40.
 */
 static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
   Fts5Table *pTab = (Fts5Table*)pVTab;
@@ -257339,7 +260788,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
     return SQLITE_ERROR;
   }
 
-  idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
+  idxStr = (char*)sqlite3_malloc64((i64)pInfo->nConstraint * 8 + 1);
   if( idxStr==0 ) return SQLITE_NOMEM;
   pInfo->idxStr = idxStr;
   pInfo->needToFreeIdxStr = 1;
@@ -257432,21 +260881,35 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
 
   /* Calculate the estimated cost based on the flags set in idxFlags. */
   if( bSeenEq ){
-    pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
-    fts5SetUniqueFlag(pInfo);
+    pInfo->estimatedCost = nSeenMatch ? 25000.0 : 25.0;
     fts5SetEstimatedRows(pInfo, 1);
+    fts5SetUniqueFlag(pInfo);
   }else{
-    if( bSeenLt && bSeenGt ){
-      pInfo->estimatedCost = nSeenMatch ? 5000.0 :   750000.0;
-    }else if( bSeenLt || bSeenGt ){
-      pInfo->estimatedCost = nSeenMatch ? 7500.0 :  2250000.0;
+    i64 nEstRows;
+    if( nSeenMatch ){
+      if( bSeenLt && bSeenGt ){
+        pInfo->estimatedCost = 50000.0;
+      }else if( bSeenLt || bSeenGt ){
+        pInfo->estimatedCost = 37500.0;
+      }else{
+        pInfo->estimatedCost = 50000.0;
+      }
+      nEstRows = (i64)(pInfo->estimatedCost / 40.0);
+      for(i=1; i<nSeenMatch; i++){
+        pInfo->estimatedCost *= 2.5;
+        nEstRows = nEstRows / 2;
+      }
     }else{
-      pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
-    }
-    for(i=1; i<nSeenMatch; i++){
-      pInfo->estimatedCost *= 0.4;
+      if( bSeenLt && bSeenGt ){
+        pInfo->estimatedCost = 750000.0;
+      }else if( bSeenLt || bSeenGt ){
+        pInfo->estimatedCost = 2250000.0;
+      }else{
+        pInfo->estimatedCost = 3000000.0;
+      }
+      nEstRows = (i64)(pInfo->estimatedCost / 4.0);
     }
-    fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
+    fts5SetEstimatedRows(pInfo, nEstRows);
   }
 
   pInfo->idxNum = idxFlags;
@@ -258789,6 +262252,7 @@ static int fts5UpdateMethod(
   }
 
  update_out:
+  sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
   pTab->p.pConfig->pzErrmsg = 0;
   return rc;
 }
@@ -260306,7 +263770,7 @@ static void fts5SourceIdFunc(
 ){
   assert( nArg==0 );
   UNUSED_PARAM2(nArg, apUnused);
-  sqlite3_result_text(pCtx, "fts5: 2025-11-28 17:28:25 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af38a88", -1, SQLITE_TRANSIENT);
+  sqlite3_result_text(pCtx, "fts5: 2026-06-03 19:12:13 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24", -1, SQLITE_TRANSIENT);
 }
 
 /*
@@ -260470,7 +263934,7 @@ static int fts5Init(sqlite3 *db){
   int rc;
   Fts5Global *pGlobal = 0;
 
-  pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
+  pGlobal = (Fts5Global*)sqlite3_malloc64(sizeof(Fts5Global));
   if( pGlobal==0 ){
     rc = SQLITE_NOMEM;
   }else{
@@ -262186,7 +265650,7 @@ static int fts5AsciiCreate(
   if( nArg%2 ){
     rc = SQLITE_ERROR;
   }else{
-    p = sqlite3_malloc(sizeof(AsciiTokenizer));
+    p = sqlite3_malloc64(sizeof(AsciiTokenizer));
     if( p==0 ){
       rc = SQLITE_NOMEM;
     }else{
@@ -262481,7 +265945,7 @@ static int fts5UnicodeCreate(
   if( nArg%2 ){
     rc = SQLITE_ERROR;
   }else{
-    p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
+    p = (Unicode61Tokenizer*)sqlite3_malloc64(sizeof(Unicode61Tokenizer));
     if( p ){
       const char *zCat = "L* N* Co";
       int i;
@@ -262700,11 +266164,17 @@ static int fts5PorterCreate(
   const char *zBase = "unicode61";
   fts5_tokenizer_v2 *pV2 = 0;
 
-  if( nArg>0 ){
-    zBase = azArg[0];
+  while( nArg>0 ){
+    if( sqlite3_stricmp(azArg[0],"porter")==0 ){
+      nArg--;
+      azArg++;
+    }else{
+      zBase = azArg[0];
+      break;
+    }
   }
 
-  pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
+  pRet = (PorterTokenizer*)sqlite3_malloc64(sizeof(PorterTokenizer));
   if( pRet ){
     memset(pRet, 0, sizeof(PorterTokenizer));
     rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2);
@@ -263411,7 +266881,7 @@ static int fts5TriCreate(
     rc = SQLITE_ERROR;
   }else{
     int i;
-    pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
+    pNew = (TrigramTokenizer*)sqlite3_malloc64(sizeof(*pNew));
     if( pNew==0 ){
       rc = SQLITE_NOMEM;
     }else{
@@ -265397,7 +268867,7 @@ static int fts5VocabFilterMethod(
       const char *zCopy = (const char *)sqlite3_value_text(pLe);
       if( zCopy==0 ) zCopy = "";
       pCsr->nLeTerm = sqlite3_value_bytes(pLe);
-      pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
+      pCsr->zLeTerm = sqlite3_malloc64((i64)pCsr->nLeTerm+1);
       if( pCsr->zLeTerm==0 ){
         rc = SQLITE_NOMEM;
       }else{
diff --git a/libs/symcrypt/LICENSE.txt b/libs/symcrypt/LICENSE.txt
new file mode 100644
index 000000000000..8cb179cdb694
--- /dev/null
+++ b/libs/symcrypt/LICENSE.txt
@@ -0,0 +1,21 @@
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/libs/symcrypt/Makefile.in b/libs/symcrypt/Makefile.in
new file mode 100644
index 000000000000..e53c39cee57e
--- /dev/null
+++ b/libs/symcrypt/Makefile.in
@@ -0,0 +1,141 @@
+STATICLIB = libsymcrypt.a
+EXTRAINCL = $(SYMCRYPT_PE_CFLAGS) -I$(srcdir)/lib -DSYMCRYPT_PLATFORM_WINE=1
+
+SOURCES = \
+	lib/3des.c \
+	lib/AesTables.c \
+	lib/DesTables.c \
+	lib/FatalIntercept.c \
+	lib/IEEE802_11SaeCustom.c \
+	lib/ScsTable.c \
+	lib/a_dispatch.c \
+	lib/aes-asm.c \
+	lib/aes-c.c \
+	lib/aes-default-bc.c \
+	lib/aes-default.c \
+	lib/aes-key.c \
+	lib/aes-neon.c \
+	lib/aes-xmm.c \
+	lib/aes-ymm.c \
+	lib/aesCtrDrbg.c \
+	lib/aescmac.c \
+	lib/aeskw.c \
+	lib/amd64/aesasm.S \
+	lib/amd64/fdef369_asm.S \
+	lib/amd64/fdef_asm.S \
+	lib/amd64/fdef_mulx.S \
+	lib/amd64/sha256xmm_asm.S \
+	lib/amd64/wipe.S \
+	lib/arm/aesasm.S \
+	lib/arm/fdef_asm.S \
+	lib/arm/wipe.S \
+	lib/arm64/fdef369_asm.S \
+	lib/arm64/fdef_asm.S \
+	lib/arm64/wipe.S \
+	lib/arm64ec/fdef369_asm.S \
+	lib/arm64ec/fdef_asm.S \
+	lib/arm64ec/wipe.S \
+	lib/blockciphermodes.c \
+	lib/ccm.c \
+	lib/chacha20.c \
+	lib/chacha20_poly1305.c \
+	lib/cpuid.c \
+	lib/cpuid_um.c \
+	lib/crt.c \
+	lib/desx.c \
+	lib/dh.c \
+	lib/dl_internal_groups.c \
+	lib/dlgroup.c \
+	lib/dlkey.c \
+	lib/dsa.c \
+	lib/ec_dh.c \
+	lib/ec_dispatch.c \
+	lib/ec_dsa.c \
+	lib/ec_internal_curve_params.c \
+	lib/ec_internal_curves.c \
+	lib/ec_montgomery.c \
+	lib/ec_mul.c \
+	lib/ec_short_weierstrass.c \
+	lib/ec_twisted_edwards.c \
+	lib/eckey.c \
+	lib/ecpoint.c \
+	lib/ecurve.c \
+	lib/env_windowsUserModeWin8_1.c \
+	lib/equal.c \
+	lib/fdef369_mod.c \
+	lib/fdef_general.c \
+	lib/fdef_int.c \
+	lib/fdef_mod.c \
+	lib/gcm.c \
+	lib/gen_int.c \
+	lib/ghash.c \
+	lib/hash.c \
+	lib/hkdf.c \
+	lib/hmac.c \
+	lib/hmacmd5.c \
+	lib/hmacsha1.c \
+	lib/hmacsha224.c \
+	lib/hmacsha256.c \
+	lib/hmacsha384.c \
+	lib/hmacsha3_224.c \
+	lib/hmacsha3_256.c \
+	lib/hmacsha3_384.c \
+	lib/hmacsha3_512.c \
+	lib/hmacsha512.c \
+	lib/hmacsha512_224.c \
+	lib/hmacsha512_256.c \
+	lib/i386/aesasm.S \
+	lib/i386/fdef_asm.S \
+	lib/i386/wipe.S \
+	lib/kmac.c \
+	lib/libmain.c \
+	lib/lms.c \
+	lib/marvin32.c \
+	lib/md2.c \
+	lib/md4.c \
+	lib/md5.c \
+	lib/mldsa.c \
+	lib/mldsa_primitives.c \
+	lib/mlkem.c \
+	lib/mlkem_primitives.c \
+	lib/modexp.c \
+	lib/paddingPkcs7.c \
+	lib/pbkdf2.c \
+	lib/pbkdf2_hmacsha1.c \
+	lib/pbkdf2_hmacsha256.c \
+	lib/poly1305.c \
+	lib/primes.c \
+	lib/rc2.c \
+	lib/rc4.c \
+	lib/rdrand.c \
+	lib/rdseed.c \
+	lib/recoding.c \
+	lib/rsa_enc.c \
+	lib/rsa_padding.c \
+	lib/rsakey.c \
+	lib/scsTools.c \
+	lib/selftest.c \
+	lib/session.c \
+	lib/sha1.c \
+	lib/sha256-xmm.c \
+	lib/sha256.c \
+	lib/sha3.c \
+	lib/sha3_224.c \
+	lib/sha3_256.c \
+	lib/sha3_384.c \
+	lib/sha3_512.c \
+	lib/sha512.c \
+	lib/shake.c \
+	lib/sp800_108.c \
+	lib/sp800_108_hmacsha1.c \
+	lib/sp800_108_hmacsha256.c \
+	lib/sp800_108_hmacsha512.c \
+	lib/srtp_kdf.c \
+	lib/ssh_kdf.c \
+	lib/ssh_kdf_sha256.c \
+	lib/ssh_kdf_sha512.c \
+	lib/sskdf.c \
+	lib/tlsCbcVerify.c \
+	lib/tlsprf.c \
+	lib/xmss.c \
+	lib/xtsaes.c
diff --git a/libs/symcrypt/inc/C_asm_shared.inc b/libs/symcrypt/inc/C_asm_shared.inc
new file mode 100644
index 000000000000..25b33560bfb6
--- /dev/null
+++ b/libs/symcrypt/inc/C_asm_shared.inc
@@ -0,0 +1,74 @@
+/*
+ C_asm_shared.inc  file to synchronize C and Asm information
+ Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+ This is a file that is included in both C and ASM such that the values are the same on both sides.
+ We use the C preprocessor to set ASM constants, as we already need to use the C preprocessor for
+ symcryptasm processing (see scripts/symcryptasm_processor.py).
+ We use this to define the structure offsets that the ASM code uses.
+ By having equivalent C constants we can add checks to the C code to ensure they are correct.
+
+*/
+
+#include "symcrypt_internal_shared.inc"
+
+#if defined(SYMCRYPT_MASM)
+#define SET(_variable, _value) _variable EQU _value
+#elif defined(SYMCRYPT_GAS)
+#define SET(_variable, _value) .set _variable, _value
+#else // assume C
+#define SET(_variable, _value) const SIZE_T _variable = _value;
+#endif
+
+SET(SymCryptModulusNdigitsOffsetAmd64,          4);
+SET(SymCryptModulusInv64OffsetAmd64,            24);
+SET(SymCryptModulusValueOffsetAmd64,            128);
+SET(SymCryptNegDivisorSingleDigitOffsetAmd64,   256);
+
+SET(SymCryptModulusNdigitsOffsetX86,            4);
+SET(SymCryptModulusInv64OffsetX86,              24);
+SET(SymCryptModulusValueOffsetX86,              96);
+
+SET(SymCryptModulusNdigitsOffsetArm64,          4);
+SET(SymCryptModulusInv64OffsetArm64,            24);
+SET(SymCryptModulusValueOffsetArm64,            128);
+
+SET(SymCryptModulusNdigitsOffsetArm,            4);
+SET(SymCryptModulusInv64OffsetArm,              24);
+SET(SymCryptModulusValueOffsetArm,              96);
+
+#if !defined(SYMCRYPT_MASM) && !defined(SYMCRYPT_GAS)
+// Preserve the definition of SET for use in symcryptasm processing
+#undef SET
+#endif
+
+#if SYMCRYPT_CPU_AMD64
+#define SYMCRYPT_CHECK_ASM_OFFSETS \
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusNdigitsOffsetAmd64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, nDigits ) );\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusInv64OffsetAmd64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, inv64 ));\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusValueOffsetAmd64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor.Int.ti.fdef.uint32 ));\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptNegDivisorSingleDigitOffsetAmd64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor.Int.ti.fdef.uint32 ) + (2*SYMCRYPT_FDEF_DIGIT_SIZE) );\
+
+#elif SYMCRYPT_CPU_X86
+#define SYMCRYPT_CHECK_ASM_OFFSETS \
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusNdigitsOffsetX86, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, nDigits ) );\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusInv64OffsetX86, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, inv64 ));\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusValueOffsetX86, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor.Int.ti.fdef.uint32 ));\
+
+#elif SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_CHECK_ASM_OFFSETS \
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusNdigitsOffsetArm64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, nDigits ) );\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusInv64OffsetArm64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, inv64 ));\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusValueOffsetArm64, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor.Int.ti.fdef.uint32 ));\
+
+#elif SYMCRYPT_CPU_ARM
+#define SYMCRYPT_CHECK_ASM_OFFSETS \
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusNdigitsOffsetArm, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, nDigits ) );\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusInv64OffsetArm, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, inv64 ));\
+    SYMCRYPT_CHECK_ASM_OFFSET( SymCryptModulusValueOffsetArm, SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor.Int.ti.fdef.uint32 ));\
+
+#endif // CPU_*
+
+#if !defined(SYMCRYPT_CHECK_ASM_OFFSETS)
+#define SYMCRYPT_CHECK_ASM_OFFSETS
+#endif
diff --git a/libs/symcrypt/inc/buildInfo.h b/libs/symcrypt/inc/buildInfo.h
new file mode 100644
index 000000000000..c31e04a64203
--- /dev/null
+++ b/libs/symcrypt/inc/buildInfo.h
@@ -0,0 +1,8 @@
+#include "symcrypt_internal_shared.inc"
+
+#define _SYMCRYPT_STRING_INT(a) #a
+#define _SYMCRYPT_STRING(a) _SYMCRYPT_STRING_INT(a)
+#define SYMCRYPT_BUILD_INFO_BRANCH    ""
+#define SYMCRYPT_BUILD_INFO_COMMIT    "2026-03-28T00:56:29+01:00_748c20f1fc48"
+#define SYMCRYPT_BUILD_INFO_VERSION   _SYMCRYPT_STRING(SYMCRYPT_CODE_VERSION_API) "." _SYMCRYPT_STRING(SYMCRYPT_CODE_VERSION_MINOR) "." _SYMCRYPT_STRING(SYMCRYPT_CODE_VERSION_PATCH)
+#define SYMCRYPT_BUILD_INFO_TIMESTAMP ""
diff --git a/libs/symcrypt/inc/symcrypt.h b/libs/symcrypt/inc/symcrypt.h
new file mode 100644
index 000000000000..b650608d5257
--- /dev/null
+++ b/libs/symcrypt/inc/symcrypt.h
@@ -0,0 +1,10814 @@
+//
+// SymCrypt.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#pragma once
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "symcrypt_internal_shared.inc"
+
+#define SYMCRYPT_API_VERSION ((SYMCRYPT_CODE_VERSION_API << 16) | SYMCRYPT_CODE_VERSION_MINOR)
+
+//
+// This is the header file for the SymCrypt library which contains
+// implementations of cryptographic algorithms.
+//
+// All API information is in this file. Information in the
+// other include files (symcrypt_internal.h) is subject
+// to change at any time. Please use only the information in this file.
+// The header file symcrypt_low_level contains low-level API functions that
+// are sometimes needed. That API surface is not stable across releases.
+//
+
+;   // <-- non-functional semicolon that makes the editor's indent work properly.
+
+//
+// General information about SymCrypt:
+//
+//
+// CPU
+// This library is built and tested for: X86, AMD64, ARM, and ARM64.
+//
+// ENVIRONMENT
+// SymCrypt can run in different environments, such as kernel mode, user mode,
+// etc.
+// In earlier versions of the library, the caller specified the environment by passing a
+// pointer to the SymCryptInit function.
+// It turns out that that model no longer scales with the use of new extended register sets
+// or it introduces too much overhead.
+// The current library uses a different model. The user of the library invokes one of the
+// environment macros inside a C file in the calling process.
+// SymCrypt defines macros for each environment.
+// The same mechanism will also be used to select between different implementations of a single
+// algorithm. For example, a caller might use
+//      SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE
+//      SYMCRYPT_SELECT_SHA256_COMPACT
+// to indicate that the environment is kernel mode and the compact SHA-256 implementation is to
+// be used.
+// There are optimized environments for various Windows use cases.
+//
+//
+// CHECKED BUILDS
+// For each CPU, SymCrypt is available in both a checked build and a fre build. The
+// checked build includes additional error checking which catches the most common
+// errors. Please make sure you build a checked version of your binary and test with
+// that regularly.
+//
+//
+// MEMORY STRUCTURES
+// Most SymCrypt functions do not allocate any memory; all memory is provided by the caller.
+// However, callers may not copy, move, or otherwise manipulate the SymCrypt
+// data structures. In particular, a memcpy of a SymCrypt data structure is not allowed.
+// When necessary SymCrypt provides functions to perform the necessary manipulations.
+// If you are missing one, please ask us.
+//
+//
+// MULTI_THREADING
+// The routines in this library are multi-thread safe, taking into account the usual
+// rules of multiple threads accessing the same data structures.
+// Any function that accepts a pointer-to-const argument must be assumed to read the
+// corresponding data. If the function accepts a pointer-to-non-const it must be
+// assumed to both read and write the data.
+// It is safe for two threads to use the same data element as long as both of them
+// are only reading from it. For example, an expanded key is typically passed as
+// a pointer-to-const to the encryption and decryption routines. Thus, multiple
+// threads can perform multiple encryptions/decryptions in parallel using the
+// same expanded key.
+//
+// The normal memory re-order issues apply as well. If one thread initializes a
+// data structure and the initialization function returns, it is NOT safe for
+// another thread to read the data structure without a suitable memory barrier or
+// synchronization primitive.
+//
+//
+// SIDE CHANNELS
+// Side channels are ways in which an attacker can receive information about what
+// a target process is doing using other aspects than just the input/output behaviour
+// of the target. For example, the memory subsystem, CPU load modulation, disk usage,
+// and many other aspects can provide side-channels to an attacker.
+//
+// Wherever possible the implementations in SymCrypt have been hardened against side channels.
+// The most important rules are that the instruction sequence and the memory addresses
+// accessed do not depend on any of the data being processed.
+// As a general rule, the actual data being processed is protected, but the
+// length of the data (i.e. the number of bytes) is not protected in this way and
+// is treated as public information.
+//
+// The implementation of the following algorithms are NOT side-channel safe:
+//  - non-AES-NI based AES
+//      used on CPUs that don't have AES-NI, or in kernel mode on x86 Win8 and below.
+//  - DES, 3DES, DESX
+//  - RC4
+// Making these algorithms side-channel safe would incur an overhead that is too large.
+//
+//
+// FATAL ERRORS
+// This is a high-performance library with a minimum of error checking.
+// Many functions do not return an error code; this avoids the cost of
+// having any error checking on the caller's side for error situations that
+// can never occur. However, this does assume that the caller is calling
+// SymCrypt using a valid calling sequence with proper parameters.
+// In some situations this library will detect improper parameters or
+// calling sequences. In those situations the library will generate a fatal
+// error, which leads to an abrupt termination of the process (bugcheck in
+// kernel mode). Exceptional circumstances may also induce fatal errors within
+// the library (i.e. a caller provided buffer causes an access violation when
+// it is read, or the library is called without sufficient stack space for the
+// requested operation).
+// If a fatal error is generated within the library, the internal state of the
+// library may be inconsistent (i.e. there may be outstanding memory allocations
+// that will never be freed, or a lock may have been taken which will never be
+// released). Callers should not catch fatal errors and continue executing, as
+// there is no guarantee of stability.
+// The checked version of the library has additional error checking which detects
+// the most common errors. We strongly recommend that callers build and test a
+// checked version of their binary to catch these common errors.
+//
+//
+// ALGORITHM SELF TEST
+// SymCrypt includes functions that perform simple self-tests on the algorithm
+// implementations. These functions are designed to be used for FIPS certification
+// of crypto binaries. They should never fail, and they generate a fatal error
+// if they do fail.
+// If you are not FIPS-certifying your binaries, you can ignore the self test functions.
+//
+//
+// CHANGES FROM RSA32.LIB
+// This library replaces the venerable rsa32(k).lib. The major changes are:
+//
+// - SymCrypt requires the caller to call a library initialization function
+//   before calling the various algorithm implementations.
+// - SymCrypt requires the caller to specify the environment in which the library
+//   is running.
+// - SymCrypt has a CHKed and FRE version for use in CHKed and FRE builds.
+// - The API has been updated. The API is more consistent and has better support
+//   for 64-bit platforms (use of SIZE_T rather than UINT32 for lengths).
+// - All algorithm implementations have been updated to reflect the
+//   latest cryptographic coding guidelines. Several security weaknesses
+//   in the RSA32.lib code have been fixed.
+// - Code has been optimized for the newer CPUs.
+//   This includes support for AES-NI, PCLMULQDQ, AVX2, etc.
+//   Most algorithms are faster, especially the recommended algorithms.
+//   Some legacy algorithms are somewhat slower due to removal of assembler support.
+//   Note: performance on older CPUs, like the Pentium 4, is reduced in some places.
+// - Code and data now go into their default segments.
+//   RSA32 has a kernel-mode version where the code and data go into
+//   special segments. This allows the crypto code to be made pageable or
+//   nonpageable separate from the rest of the executable. This feature is
+//   error-prone, and not widely used. Furthermore, it switches on a per-lib
+//   basis, rather than a per-functionality basis, which is the wrong granularity.
+// - Added native support for HMAC-SHA256 and HMAC-SHA512.
+// - Support for parallel hashing, improves throughput up to 500%.
+// - SymCrypt does not support binary copying of internal state information, because
+//   it imposes restrictions on what the library can do.
+//   Thus, you may NOT do a memcpy or remote copy on any SymCrypt data structure.
+//   SymCrypt provides copy functions where necessary, if you need others please ask.
+//
+
+//
+// Error codes
+//
+// This is a high-performance library with a minimum of error checking. Most
+// routines do not perform any error checking at all.
+// Some routines perform internal consistency checks and will cause a fatal
+// error if the library is used incorrectly.
+//
+// In a few cases routines return an error code when they are called incorrectly.
+// Mostly this is for key expansion routines which return an error code when the key
+// size is wrong. This allows a higher-level library to be agnostic as to the proper
+// key sizes for an algorithm and use the SymCrypt library to detect key size errors.
+//
+// For performance reasons this library avoids per-message error codes wherever possible.
+//
+// As this library can be used in many different contexts---kernel mode, user mode,
+// WinCE, Xbox, etc.---we don't use one of the standard error types but use our own.
+// Callers should not depend on the integer value of any of these enums.
+//
+// Error codes will signal the cause of the error, but callers should not rely on the
+// exact symbolic error code returned. Especially in situations where multiple errors
+// occur at once (e.g. multiple invalid parameters) the exact error symbol returned
+// could change between versions of the library.
+//
+
+#ifndef _Return_type_success_
+#define _Return_type_success_(expr)
+#endif
+
+typedef _Return_type_success_( return == SYMCRYPT_NO_ERROR ) enum {
+    SYMCRYPT_NO_ERROR = 0,
+    SYMCRYPT_UNUSED = 0x8000, // Start our error codes here so they're easier to distinguish
+    SYMCRYPT_WRONG_KEY_SIZE,
+    SYMCRYPT_WRONG_BLOCK_SIZE,
+    SYMCRYPT_WRONG_DATA_SIZE,
+    SYMCRYPT_WRONG_NONCE_SIZE,
+    SYMCRYPT_WRONG_TAG_SIZE,
+    SYMCRYPT_WRONG_ITERATION_COUNT,
+    SYMCRYPT_AUTHENTICATION_FAILURE,
+    SYMCRYPT_EXTERNAL_FAILURE,
+    SYMCRYPT_FIPS_FAILURE,
+    SYMCRYPT_HARDWARE_FAILURE,
+    SYMCRYPT_NOT_IMPLEMENTED,
+    SYMCRYPT_INVALID_BLOB,
+    SYMCRYPT_BUFFER_TOO_SMALL,
+    SYMCRYPT_INVALID_ARGUMENT,
+    SYMCRYPT_MEMORY_ALLOCATION_FAILURE,
+    SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE,
+    SYMCRYPT_INCOMPATIBLE_FORMAT,
+    SYMCRYPT_VALUE_TOO_LARGE,
+    SYMCRYPT_SESSION_REPLAY_FAILURE,
+    SYMCRYPT_HBS_NO_OTS_KEYS_LEFT,
+    SYMCRYPT_HBS_PUBLIC_ROOT_MISMATCH,
+} SYMCRYPT_ERROR;
+
+// SYMCRYPT_ECURVE_TYPE needs to be completely defined before including
+// symcrypt_internal.h because it's a member of another type in there.
+typedef enum _SYMCRYPT_ECURVE_TYPE {
+    SYMCRYPT_ECURVE_TYPE_NULL               = 0,
+    SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS  = 1,
+    SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS    = 2,
+    SYMCRYPT_ECURVE_TYPE_MONTGOMERY         = 3,
+} SYMCRYPT_ECURVE_TYPE;
+//
+// SYMCRYPT_ECURVE_TYPE is used to specify the type of the curve.
+//
+
+// SYMCRYPT_DLGROUP_FIPS needs to be completely defined before including
+// symcrypt_internal.h because it's a member of another type in there.
+
+//=====================================================
+// DL group operations
+
+typedef enum _SYMCRYPT_DLGROUP_FIPS {
+    SYMCRYPT_DLGROUP_FIPS_NONE  = 0,
+    SYMCRYPT_DLGROUP_FIPS_186_2 = 1,
+    SYMCRYPT_DLGROUP_FIPS_186_3 = 2,
+} SYMCRYPT_DLGROUP_FIPS;
+//
+// Dlgroup enums for the generation and verification of the group parameters.
+// These are used in:
+//  - SymCryptDlgroupGenerate function to specify the appropriate standard to
+//    be used.
+//  - SymCryptDlgroupSetValue function to verify that the input parameters were
+//    properly generated.
+//
+
+typedef enum _SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE {
+    SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_NONE = 0,
+    SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_IKE_3526 = 1,
+    SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919 = 2,
+} SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE;
+#define SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_DEFAULT   SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919
+//
+// Dlgroup enums for the specification and verification of the named safe prime group parameters.
+// These are used in:
+//  - SymCryptDlgroupGenerateSafePrime function to specify the appropriate group to
+//    be used.
+//
+
+//
+// The symcrypt_internal.h file contains information only relevant to the internals
+// of the library, but they have to be exposed to the compiler of the caller.
+// We put those in a separate file to make this file easier to read
+// for users of the library.
+// The details in the symcrypt_internal.h file can change at any time;
+// users should only rely on the information in this header file.
+//
+#include "symcrypt_internal.h"
+
+//
+// Useful macros
+//
+// A variety of useful macros.
+//
+// The load/store macros convert from integer types to an array of bytes and vice versa.
+// LOAD<n>_* (p) loads a value of <n> bits from the byte pointer p.
+// STORE<n>_* (p,v) stores the n-bit value v to byte pointer p.
+// The macros can either do Most Significant Byte first (big-endian) or
+// Least Significant Byte first.
+// The actual definitions are in the symcrypt_internal.h file because they contain
+// items that are not part of the stable public API of SymCrypt.
+//
+
+#define SYMCRYPT_LOAD_LSBFIRST16( p )   SYMCRYPT_INTERNAL_LOAD_LSBFIRST16( p )
+#define SYMCRYPT_LOAD_LSBFIRST32( p )   SYMCRYPT_INTERNAL_LOAD_LSBFIRST32( p )
+#define SYMCRYPT_LOAD_LSBFIRST64( p )   SYMCRYPT_INTERNAL_LOAD_LSBFIRST64( p )
+
+#define SYMCRYPT_LOAD_MSBFIRST16( p )   SYMCRYPT_INTERNAL_LOAD_MSBFIRST16( p )
+#define SYMCRYPT_LOAD_MSBFIRST32( p )   SYMCRYPT_INTERNAL_LOAD_MSBFIRST32( p )
+#define SYMCRYPT_LOAD_MSBFIRST64( p )   SYMCRYPT_INTERNAL_LOAD_MSBFIRST64( p )
+
+#define SYMCRYPT_STORE_LSBFIRST16( p, v )   SYMCRYPT_INTERNAL_STORE_LSBFIRST16( p, v )
+#define SYMCRYPT_STORE_LSBFIRST32( p, v )   SYMCRYPT_INTERNAL_STORE_LSBFIRST32( p, v )
+#define SYMCRYPT_STORE_LSBFIRST64( p, v )   SYMCRYPT_INTERNAL_STORE_LSBFIRST64( p, v )
+
+#define SYMCRYPT_STORE_MSBFIRST16( p, v )   SYMCRYPT_INTERNAL_STORE_MSBFIRST16( p, v )
+#define SYMCRYPT_STORE_MSBFIRST32( p, v )   SYMCRYPT_INTERNAL_STORE_MSBFIRST32( p, v )
+#define SYMCRYPT_STORE_MSBFIRST64( p, v )   SYMCRYPT_INTERNAL_STORE_MSBFIRST64( p, v )
+
+//
+// Convert between UINT32/UINT64 and variable-sized byte buffers
+//
+// The load functions take any size input array, and will return an error if the value
+// encoded in the array exceeds the range of the target type (UINT32 or UINT64).
+// The store functions will return an error if the destination buffer is too small
+// to encode the actual value passed.
+// An empty buffer (length = 0) encodes the value 0, and the value 0 can be encoded
+// in the empty buffer.
+// These functions are not side-channel safe.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadLsbFirstUint32(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT32 pDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadLsbFirstUint64(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT64 pDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadMsbFirstUint32(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT32 pDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadMsbFirstUint64(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT64 pDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreLsbFirstUint32(
+                            UINT32  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreLsbFirstUint64(
+                            UINT64  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreMsbFirstUint32(
+                            UINT32  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreMsbFirstUint64(
+                            UINT64  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst );
+
+//
+// Functions to retrieve the bitsize/bytesize of UINT32/UINT64 values
+// Note: the bitsize/bytesize of the value 0 is defined as 0.
+// Some data formats don't allow empty encodings, so the caller
+// should ensure they handle the 0-case properly.
+// These functions are NOT side-channel safe.
+//
+UINT32
+SymCryptUint32Bitsize( UINT32 value );
+
+UINT32
+SymCryptUint64Bitsize( UINT64 value );
+
+UINT32
+SymCryptUint32Bytesize( UINT32 value );
+
+UINT32
+SymCryptUint64Bytesize( UINT64 value );
+
+
+//
+// FORCED MEMORY ACCESS
+//
+// These macros force a memory access. That is, they require that the memory
+// read or write takes place, and do not allow the compiler to optimize the access
+// away. This is useful for wiping memory even if the compiler knows the memory will not be used in future.
+//
+// The READ<n> macros read an n-bit value from a PBYTE and return a BYTE if n=8 and an UINT<n> otherwise.
+// The WRITE<n> macros write a value to a PBYTE using the same types as the corresponding READ<n>
+//
+// These macros provide no other memory ordering requirements, so there are no acquire/release
+// semantics, memory barriers, etc.
+//
+
+#define SYMCRYPT_FORCE_READ8(  _p )     SYMCRYPT_INTERNAL_FORCE_READ8(  _p )
+#define SYMCRYPT_FORCE_READ16( _p )     SYMCRYPT_INTERNAL_FORCE_READ16( _p )
+#define SYMCRYPT_FORCE_READ32( _p )     SYMCRYPT_INTERNAL_FORCE_READ32( _p )
+#define SYMCRYPT_FORCE_READ64( _p )     SYMCRYPT_INTERNAL_FORCE_READ64( _p )
+
+#define SYMCRYPT_FORCE_WRITE8(  _p, _v )    SYMCRYPT_INTERNAL_FORCE_WRITE8(  _p, _v )
+#define SYMCRYPT_FORCE_WRITE16( _p, _v )    SYMCRYPT_INTERNAL_FORCE_WRITE16( _p, _v )
+#define SYMCRYPT_FORCE_WRITE32( _p, _v )    SYMCRYPT_INTERNAL_FORCE_WRITE32( _p, _v )
+#define SYMCRYPT_FORCE_WRITE64( _p, _v )    SYMCRYPT_INTERNAL_FORCE_WRITE64( _p, _v )
+
+//==========================================================================
+//   TYPE MODIFIERS
+//==========================================================================
+//
+// The SymCrypt library uses the following type modifiers
+//
+// SYMCRYPT_CALL
+//
+//      The calling-convention used by SymCrypt functions.
+//      Some platforms have multiple calling conventions which differ in the
+//      way arguments are passed and the stack is handled
+//      The SYMCRYPT_CALL type modifier selects the correct calling convention.
+//      The current implementation uses __fastcall on the x86 platform, which
+//      passes arguments in registers and is generally faster than the __stdcall
+//      calling convention.
+//
+//
+// SYMCRYPT_ALIGN
+//
+//      On platforms that support alignment declaration this macro expands to
+//      __declspec(align(<n>)) where <n> is platform-dependent.
+//      Many data types that SymCrypt defines are SYMCRYPT_ALIGNed.
+//      When allocating memory for any SymCrypt data type the caller
+//      has to ensure that the memory is aligned to the natural alignment for
+//      that platform. (e.g. 4 for x86, 16 for x64)
+//      Memory allocation functions typically return properly aligned memory blocks.
+//      The macro SYMCRYPT_ALIGN_VALUE contains the actual value of <n>.
+//
+
+//==========================================================================
+// LIBRARY MANAGEMENT
+//==========================================================================
+//
+// SymCrypt runs in many different environments. Boot library, kernel, user mode,
+// (for each of x86, amd64, arm), and possibly WinCE, Mobile, Zune, Xbox, etc.
+// These different environments can have different requirements.
+//
+// Creating different libraries for each environment has huge testing and maintenance
+// costs. Instead, the user of the library invokes a pre-defined macro in their own code
+// that contains the necessary adoptions to that environment.
+// Using a macro makes the selection static, which allows the compiler to optimize
+// away a lot of the overhead.
+// (e.g. if XMM register saving is not needed, the stub function declared by the macro
+// will always succeed, and the compiler will inline it and optimize it away.)
+//
+// Warning: due to recent changes in the Visual Studio C runtime, we cannot test saving
+// of the YMM registers in Windows user mode. Because we do not have a kernel mode test
+// for saving/restoring the YMM registers, this functionality is currently not tested.
+// Before using SymCrypt in Windows 7 kernel mode, additional kernel mode tests should be
+// added to verify this functionality.
+//
+
+//
+// The following environment macros are available. Callers should invoke one of these
+// in their own code.
+//
+// SYMCRYPT_ENVIRONMENT_WINDOWS_BOOTLIBRARY                 // only for the current OS release
+//
+// SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_LEGACY           // Use for any version of Windows.
+// SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_WIN7_N_LATER     // Only for Win7 and later
+// SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_WIN8_1_N_LATER   // Only for WinBlue and later
+// SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_LATEST           // use for latest OS
+//
+// SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_LEGACY             // use for any version of Windows
+// SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_WIN7_N_LATER       // Only for Win7 and later (cannot use AVX2 instructions)
+// SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_WIN8_1_N_LATER     // Only for Win8.1 and later
+// SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_LATEST             // use for latest OS
+//
+// SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELDEBUGGER
+//
+// SYMCRYPT_ENVIRONMENT_LINUX_USERMODE                      // use for Linux
+//
+// SYMCRYPT_ENVIRONMENT_OPTEE_TA                            // use for OPTEE
+//
+// SYMCRYPT_ENVIRONMENT_GENERIC                             // use for all other situations
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptInit(void);
+//
+// Initialize the static library.
+// This function MUST be called before any other function in the library.
+// It is not necessary to call this function when using the shared object library.
+//
+// This function does not perform the self tests in the library.
+// Doing so would force the linking of all the algorithm in the library,
+// which is obviously not desirable for applications that want to link in
+// only one or two algorithms.
+// If self test are required (e.g. for FIPS certification) they have to be
+// called separately for each algorithm.
+//
+// It is safe to call this function multiple times.
+// The library initialization is done in the first call; subsequent calls are no-ops.
+//
+// If you get an 'undefined symbol' error on this function name, then you forgot
+// to invoke one of the environment macros documented above.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModuleInit(
+    _In_ UINT32 api,
+    _In_ UINT32 minor);
+
+#define SYMCRYPT_MODULE_INIT() SymCryptModuleInit( SYMCRYPT_CODE_VERSION_API, SYMCRYPT_CODE_VERSION_MINOR );
+//
+// Initialize the SymCrypt shared object module/dynamic-link library. This function verifies
+// that the module version supports the version requested by the application. If the version
+// is unsupported, a fatal error will occur. Rather than explicitly calling SymCryptModuleInit,
+// the macro SYMCRYPT_MODULE_INIT should be used to call it with the correct arguments.
+//
+
+//==========================================================================
+//   DATA MANIPULATION
+//==========================================================================
+//
+// This library provides some data manipulation functions that commonly occur
+// in cryptographic code.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptWipe(
+    _Out_writes_bytes_( cbData )    PVOID   pbData,
+                                    SIZE_T  cbData );
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptWipeKnownSize(
+    _Out_writes_bytes_( cbData )    PVOID   pbData,
+                                    SIZE_T  cbData );
+
+//
+// The SymCryptWipe and SymCryptWipeKnownSize functions wipe memory.
+// They work for any size and any alignment.
+// Wiping is faster on x86 and x64 if the data buffer is 16-aligned,
+// and the size is a multiple of 16.
+//
+// The SymCryptWipe function is optimized for the case where the size of the buffer
+// is not known at compile time.
+//
+// The SymCryptWipeKnownSize function is optimized for the case where the
+// cbData parameter is a compile-time known value.
+//
+// The two functions are functionally equivalent, but there can be a significant performance
+// differences:
+//  - calling SymCryptWipeKnownSize when the size is not known at compile time incurs a
+//      code size penalty.
+//  - calling SymCryptWipeKnownSize when the size is not known at compile time and is sometimes <= 64
+//      incurs a performance penalty.
+//      (The code assumes that the compiler can optimize all the conditional jumps away.
+//      Conditional jumps can be very expensive if they are not predicted correctly.)
+//  - calling SymCryptWipe when the buffer is small and has a compile-time known size incurs
+//      a performance penalty.
+// When in doubt, use SymCryptWipe.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXorBytes(
+    _In_reads_( cbBytes )   PCBYTE  pbSrc1,
+    _In_reads_( cbBytes )   PCBYTE  pbSrc2,
+    _Out_writes_( cbBytes ) PBYTE   pbResult,
+                            SIZE_T  cbBytes );
+//
+// Xor two strings of bytes together.
+//
+// The result buffer can be the same as Src1 or Src2, or can be non-overlapping
+// with the inputs. However, the result buffer may not partially overlap with
+// one of the inputs.
+//
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEqual(
+    _In_reads_( cbBytes )   PCBYTE pbSrc1,
+    _In_reads_( cbBytes )   PCBYTE pbSrc2,
+                            SIZE_T cbBytes );
+//
+// Compare two regions of memory and return TRUE if they are equal, FALSE otherwise.
+//
+// This function compares all the bytes without an early-out mechanism.
+// An early-out implementation, such as memcmp, reveals through side channels
+// the position of the first byte where the inputs differ, which leaks information.
+//
+
+
+//==========================================================================
+//   HASH FUNCTIONS
+//==========================================================================
+//
+// All hash functions have a similar interface. For consistency we describe
+// the generic parts of the interface once.
+// Algorithm-specific comments are given with the API functions of each algorithm separately.
+//
+// For an algorithm called XXX the following functions, types, and constants are defined:
+//
+//
+// SYMCRYPT_XXX_RESULT_SIZE
+//
+//      A constant giving the size, in bytes, of the result of the hash function.
+//
+//
+// SYMCRYPT_XXX_INPUT_BLOCK_SIZE
+//
+//      A constant giving the natural input block size for the hash function.
+//      Most callers don't need to know this, but some uses, like the HMAC construction
+//      adapt to this size to improve efficiency.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxx( _In_reads_( cbData )                        PCBYTE pbData,
+//                                                          SIZE_T cbData,
+//              _Out_writes_( SYMCRYPT_XXX_RESULT_SIZE )    PBYTE pbResult );
+//
+//      Computes the hash value of the data buffer.
+//      If you have all the data to be hashed in a single buffer this is the simplest function to use.
+//
+//
+// SYMCRYPT_XXX_STATE
+//
+//      Type to store the intermediate state of a hash computation.
+//      This is an opaque type whose structure can change at will.
+//      It should only be used for transient computations in a single executable
+//      and not be stored  or transferred to a different process.
+//      The pointer version is also defined (PSYMCRYPT_XXX_STATE)
+//
+//      The SYMCRYPT_XXX_STATE structure contains the entire state of an ongoing
+//      hash computation. If you want to compute the hash on several strings that
+//      have the same prefix, the caller may hash the prefix first, then create
+//      multiple copies using the supplied state copy function,
+//      and continue hashing the different states with different postfix strings.
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxInit( _Out_ PSYMCRYPT_XXX_STATE pState );
+//
+//      Initialize a SYMCRYPT_XXX_STATE for subsequent use.
+//
+//      The state encodes an ongoing hash computation and allows incremental
+//      computation of a hash function.
+//      At any point in time the state object encodes a state that is equivalent to
+//      the hash computation of a data string.
+//      This function can be called at any time and resets the state to correspond
+//      to the empty data string.
+//      The SymCryptXxxAppend function appends data to the data string
+//      encoded by the state.
+//      The SymCryptXxxResult function finalizes the computation and
+//      returns the actual hash result.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxAppend( _Inout_               PSYMCRYPT_XXX_STATE pState,
+//                    _In_reads_( cbData )  PCBYTE              pbData,
+//                                          SIZE_T              cbData );
+//
+//      Provide more data to the ongoing hash computation specified by the state.
+//      The state must have been initialized by SymCryptXxxInit.
+//      This function can be called multiple times on the same state
+//      to append more data to the encoded data string.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxResult(
+//      _Inout_                                PSYMCRYPT_XXX_STATE pState,
+//     _Out_writes_( SYMCRYPT_XXX_RESULT_SIZE )PBYTE               pbResult );
+//
+//      Returns the hash of the data string encoded by the state.
+//      If the state was newly initialized this returns the hash of the empty string.
+//      If one or more SymCryptXxxAppend function calls were made on this state
+//      it returns the hash of the concatenation of all the data strings
+//      passed to SymCryptXxxAppend.
+//
+//      The state is re-initialized and ready for re-use; you do not have to call
+//      SymCryptXxxInit on the state to start another fresh hash computation.
+//      The state is also wiped of any traces of old data to prevent accidental data leakage.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxStateCopy( _In_ PCSYMCRYPT_XXX_STATE pSrc, _Out_ PSYMCRYPT_XXX_STATE pDst );
+//
+//      Create a new copy of the state object.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxStateExport(
+//      _In_                                                    PCSYMCRYPT_XXX_STATE    pState,
+//      _Out_writes_bytes_( SYMCRYPT_XXX_STATE_EXPORT_SIZE )    PBYTE                   pbBlob );
+//
+//      Converts a hash state to an exported format that can be persisted and re-imported.
+//      The exported blob is compatible across CPU architectures, and across different
+//      versions of SymCrypt.
+//
+//      pState must point to a valid initialized hash state.
+//
+//
+// SYMCRYPT_ERROR
+// SYMCRYPT_CALL
+// SymCryptXxxStateImport(
+//      _Out_                                               PSYMCRYPT_XXX_STATE pState,
+//      _In_reads_bytes_( SYMCRYPT_XXX_STATE_EXPORT_SIZE)   PCBYTE              pbBlob );
+//
+//      Imports a hash state that was previously exported with SymCryptXxxStateExport.
+//      After this call, the effective state of *pState is identical to the effective
+//      state of *pState that was passed to the SymCryptXxxStateExport function which
+//      created this blob.
+//
+//      This function returns an error if the blob is incorrectly formatted.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxSelftest(void);
+//
+//      Perform a minimal self-test on the XXX algorithm.
+//      This function is designed to be used for achieving FIPS 140-2 compliance or
+//      to provide a simple self-test when an application starts.
+//
+//      If an error is detected, a platform-specific fatal error action is taken.
+//      Callers do not need to handle any error conditions.
+//
+//
+//
+//
+// There are also generic Hash functions that use a virtual table and work
+// for any hash algorithm.
+// Virtual table addresses that callers can use are supplied through a const-ptr-const definition.
+// This supports an application switching the underlying implementation of one algorithm
+// without the need to re-compile all the intermediate libraries in between.
+// For example, you could use the same signature verification library with the fast hash implementation in one binary,
+// and with a compact hash implementation in a second binary, without needing a different
+// signature verification library.
+//
+
+typedef enum _SYMCRYPT_HASH_ID
+{
+    SYMCRYPT_HASH_ID_NULL                = 0,
+    SYMCRYPT_HASH_ID_MD2                 = 1,
+    SYMCRYPT_HASH_ID_MD4                 = 2,
+    SYMCRYPT_HASH_ID_MD5                 = 3,
+    SYMCRYPT_HASH_ID_SHA1                = 4,
+    SYMCRYPT_HASH_ID_SHA224              = 5,
+    SYMCRYPT_HASH_ID_SHA256              = 6,
+    SYMCRYPT_HASH_ID_SHA384              = 7,
+    SYMCRYPT_HASH_ID_SHA512              = 8,
+    SYMCRYPT_HASH_ID_SHA512_224          = 9,
+    SYMCRYPT_HASH_ID_SHA512_256          = 10,
+    SYMCRYPT_HASH_ID_SHA3_224            = 11,
+    SYMCRYPT_HASH_ID_SHA3_256            = 12,
+    SYMCRYPT_HASH_ID_SHA3_384            = 13,
+    SYMCRYPT_HASH_ID_SHA3_512            = 14,
+    SYMCRYPT_HASH_ID_SHAKE128            = 15,
+    SYMCRYPT_HASH_ID_SHAKE256            = 16
+} SYMCRYPT_HASH_ID;
+
+PCSYMCRYPT_HASH
+SYMCRYPT_CALL
+SymCryptGetHashAlgorithm( SYMCRYPT_HASH_ID hashId );
+//
+// Returns a pointer to the hash algorithm structure for the specified hash ID.
+// Returns NULL if the hash ID is invalid.
+//
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHashResultSize( _In_ PCSYMCRYPT_HASH pHash );
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHashInputBlockSize( _In_ PCSYMCRYPT_HASH pHash );
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHashStateSize( _In_ PCSYMCRYPT_HASH pHash );
+//
+// SymCryptHashStateSize
+//
+// Returns the size, in bytes, of the hash state for this hash algorithm.
+// Note that the state must be SYMCRYPT_ALIGNed.
+// Alternatively, the SYMCRYPT_HASH_STATE structure is large enough to contain
+// any Symcrypt-implemented hash state, so sizeof( SYMCRYPT_HASH_STATE ) is always
+// large enough to contain a hash state.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHash(
+    _In_                                                         PCSYMCRYPT_HASH pHash,
+    _In_reads_( cbData )                                         PCBYTE          pbData,
+                                                                 SIZE_T          cbData,
+    _Out_writes_( SYMCRYPT_MIN( cbResult, pHash->resultSize ) )  PBYTE           pbResult,
+                                                                 SIZE_T          cbResult );
+//
+// SymCryptHash
+//
+// Compute a hash value using any hash function.
+// The number of bytes written to the pbResult buffer is
+//      min( cbResult, SymCryptHashResultSize( pHash ) )
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashInit(
+    _In_                                        PCSYMCRYPT_HASH pHash,
+    _Out_writes_bytes_( pHash->stateSize )      PVOID           pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashAppend(
+    _In_                                        PCSYMCRYPT_HASH pHash,
+    _Inout_updates_bytes_( pHash->stateSize )   PVOID           pState,
+    _In_reads_( cbData )                        PCBYTE          pbData,
+                                                SIZE_T          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashResult(
+    _In_                                                         PCSYMCRYPT_HASH pHash,
+    _Inout_updates_bytes_( pHash->stateSize )                    PVOID           pState,
+    _Out_writes_( SYMCRYPT_MIN( cbResult, pHash->resultSize ) )  PBYTE           pbResult,
+                                                                 SIZE_T          cbResult );
+//
+// SymCryptHashResult
+//
+// Finalizes the hash computation by calling the resultFunc member
+// of pHash.
+// The hash result is produced to an internal buffer and
+// the number of bytes written to the pbResult buffer is
+//      min( cbResult, SymCryptHashResultSize( pHash ) )
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashStateCopy(
+    _In_                            PCSYMCRYPT_HASH pHash,
+    _In_reads_(pHash->stateSize)    PCVOID          pSrc,
+    _Out_writes_(pHash->stateSize)  PVOID           pDst);
+//
+// SymCryptHashStateCopy
+//
+// Copies the hash state from pSrc to pDst.
+
+////////////////////////////////////////////////////////////////////////////
+//   MD2
+//
+// Tha MD2 hash algorithm per RFC1319.
+//
+// The MD2 hash function has not received widespread analysis and is very slow
+// compared to contemporary algorithms.
+//
+// The SymCrypt implementation of MD2 uses table lookups which leads to a side-channel
+// vulnerability.
+//
+// Per the Crypto SDL, any use of this algorithm in Microsoft code requires
+// a Crypto board exemption. Whenever possible, please use SHA-256 or SHA-512.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_MD2_RESULT_SIZE         (16)
+#define SYMCRYPT_MD2_INPUT_BLOCK_SIZE    (16)
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_MD2_RESULT_SIZE )    PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Init( _Out_ PSYMCRYPT_MD2_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Append(
+    _Inout_                 PSYMCRYPT_MD2_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbData,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Result(
+    _Inout_                                  PSYMCRYPT_MD2_STATE pState,
+    _Out_writes_( SYMCRYPT_MD2_RESULT_SIZE ) PBYTE               pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2StateCopy( _In_ PCSYMCRYPT_MD2_STATE pSrc, _Out_ PSYMCRYPT_MD2_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2StateExport(
+    _In_                                                    PCSYMCRYPT_MD2_STATE    pState,
+    _Out_writes_bytes_( SYMCRYPT_MD2_STATE_EXPORT_SIZE )    PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMd2StateImport(
+    _Out_                                               PSYMCRYPT_MD2_STATE     pState,
+    _In_reads_bytes_( SYMCRYPT_MD2_STATE_EXPORT_SIZE)   PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptMd2Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   MD4
+//
+// Tha MD4 hash algorithm per RFC1320.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The MD4 hash function has been badly broken and is not considered secure.
+// Per the Crypto SDL, any use of this algorithm in Microsoft code requires
+// a Crypto board exemption. Whenever possible, please use SHA-256 or SHA-512.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_MD4_RESULT_SIZE         (16)
+#define SYMCRYPT_MD4_INPUT_BLOCK_SIZE    (64)
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_MD4_RESULT_SIZE )    PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Init( _Out_ PSYMCRYPT_MD4_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Append(
+    _Inout_                 PSYMCRYPT_MD4_STATE  pState,
+    _In_reads_( cbData )    PCBYTE               pbData,
+                            SIZE_T               cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Result(
+    _Inout_                                  PSYMCRYPT_MD4_STATE  pState,
+    _Out_writes_( SYMCRYPT_MD4_RESULT_SIZE ) PBYTE                pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4StateCopy( _In_ PCSYMCRYPT_MD4_STATE pSrc, _Out_ PSYMCRYPT_MD4_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4StateExport(
+    _In_                                                    PCSYMCRYPT_MD4_STATE    pState,
+    _Out_writes_bytes_( SYMCRYPT_MD4_STATE_EXPORT_SIZE )    PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMd4StateImport(
+    _Out_                                               PSYMCRYPT_MD4_STATE     pState,
+    _In_reads_bytes_( SYMCRYPT_MD4_STATE_EXPORT_SIZE)   PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptMd4Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   MD5
+//
+// Tha MD5 hash algorithm per RFC1321.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The MD5 hash function has been badly broken and is not considered secure.
+// Per the Crypto SDL, any use of this algorithm in Microsoft code requires
+// a Crypto board exemption. Whenever possible, please use SHA-256 or SHA-512.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_MD5_RESULT_SIZE        (16)
+#define SYMCRYPT_MD5_INPUT_BLOCK_SIZE   (64)
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_MD5_RESULT_SIZE )    PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Init( _Out_ PSYMCRYPT_MD5_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Append(
+    _Inout_                 PSYMCRYPT_MD5_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                pbData,
+                            SIZE_T                cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Result(
+    _Inout_                                  PSYMCRYPT_MD5_STATE  pState,
+    _Out_writes_( SYMCRYPT_MD5_RESULT_SIZE ) PBYTE                pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5StateCopy( _In_ PCSYMCRYPT_MD5_STATE pSrc, _Out_ PSYMCRYPT_MD5_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5StateExport(
+    _In_                                                    PCSYMCRYPT_MD5_STATE    pState,
+    _Out_writes_bytes_( SYMCRYPT_MD5_STATE_EXPORT_SIZE )    PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMd5StateImport(
+    _Out_                                               PSYMCRYPT_MD5_STATE     pState,
+    _In_reads_bytes_( SYMCRYPT_MD5_STATE_EXPORT_SIZE)   PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptMd5Algorithm;
+
+
+///////////////////////////////////////////////////////////////////////////////
+//      SHA-1
+//
+// The SHA-1 hash algorithm per FIPS 180-4.
+//
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-1 standard limits data inputs to a maximum of 2^61-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// The SHA-1 hash algorithm has been broken in a technical sense, and future
+// attacks can only get better.
+// This algorithm is not recommended for new applications and should only be used
+// for backward compatibility.
+// Per the Crypto SDL, new uses of this algorithm in Microsoft code require
+// a Crypto board exemption. Whenever possible, please use SHA-256 or SHA-512.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA1_RESULT_SIZE       (20)
+#define SYMCRYPT_SHA1_INPUT_BLOCK_SIZE  (64)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA1_RESULT_SIZE )   PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Init( _Out_ PSYMCRYPT_SHA1_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Append(
+    _Inout_                 PSYMCRYPT_SHA1_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Result(
+    _Inout_                                  PSYMCRYPT_SHA1_STATE pState,
+    _Out_writes_( SYMCRYPT_SHA1_RESULT_SIZE )PBYTE                pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1StateCopy( _In_ PCSYMCRYPT_SHA1_STATE pSrc, _Out_ PSYMCRYPT_SHA1_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1StateExport(
+    _In_                                                    PCSYMCRYPT_SHA1_STATE   pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA1_STATE_EXPORT_SIZE )   PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha1StateImport(
+    _Out_                                               PSYMCRYPT_SHA1_STATE    pState,
+    _In_reads_bytes_( SYMCRYPT_SHA1_STATE_EXPORT_SIZE)  PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha1Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-224
+//
+//
+// The SHA-224 hash algorithm per FIPS 180-4.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-224 standard limits data inputs to a maximum of 2^61-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA224_RESULT_SIZE         (28)
+#define SYMCRYPT_SHA224_INPUT_BLOCK_SIZE    (64)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA224_RESULT_SIZE ) PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Init( _Out_ PSYMCRYPT_SHA224_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Append(
+    _Inout_                 PSYMCRYPT_SHA224_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Result(
+    _Inout_                                       PSYMCRYPT_SHA224_STATE pState,
+    _Out_writes_( SYMCRYPT_SHA224_RESULT_SIZE )   PBYTE                  pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224StateCopy( _In_ PCSYMCRYPT_SHA224_STATE pSrc, _Out_ PSYMCRYPT_SHA224_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224StateExport(
+    _In_                                                    PCSYMCRYPT_SHA224_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA224_STATE_EXPORT_SIZE ) PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha224StateImport(
+    _Out_                                                   PSYMCRYPT_SHA224_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA224_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha224Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-256
+//
+//
+// The SHA-256 hash algorithm per FIPS 180-4.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-256 standard limits data inputs to a maximum of 2^61-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA256_RESULT_SIZE         (32)
+#define SYMCRYPT_SHA256_INPUT_BLOCK_SIZE    (64)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA256_RESULT_SIZE ) PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Init( _Out_ PSYMCRYPT_SHA256_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Append(
+    _Inout_                 PSYMCRYPT_SHA256_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Result(
+    _Inout_                                       PSYMCRYPT_SHA256_STATE pState,
+    _Out_writes_( SYMCRYPT_SHA256_RESULT_SIZE )   PBYTE                  pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256StateCopy( _In_ PCSYMCRYPT_SHA256_STATE pSrc, _Out_ PSYMCRYPT_SHA256_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256StateExport(
+    _In_                                                    PCSYMCRYPT_SHA256_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE ) PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha256StateImport(
+    _Out_                                                   PSYMCRYPT_SHA256_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha256Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-384
+//
+//
+// The SHA-384 hash algorithm per FIPS 180-4.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-384 standard limits data inputs to a maximum of 2^125-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA384_RESULT_SIZE         (48)
+#define SYMCRYPT_SHA384_INPUT_BLOCK_SIZE    (128)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA384_RESULT_SIZE ) PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Init( _Out_ PSYMCRYPT_SHA384_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Append(
+    _Inout_                 PSYMCRYPT_SHA384_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Result(
+    _Inout_                                     PSYMCRYPT_SHA384_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA384_RESULT_SIZE ) PBYTE                  pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384StateCopy( _In_ PCSYMCRYPT_SHA384_STATE pSrc, _Out_ PSYMCRYPT_SHA384_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384StateExport(
+    _In_                                                    PCSYMCRYPT_SHA384_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA384_STATE_EXPORT_SIZE ) PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha384StateImport(
+    _Out_                                                   PSYMCRYPT_SHA384_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA384_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha384Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-512
+//
+//
+// The SHA-512 hash algorithm per FIPS 180-4.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-512 standard limits data inputs to a maximum of 2^125-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA512_RESULT_SIZE         (64)
+#define SYMCRYPT_SHA512_INPUT_BLOCK_SIZE    (128)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512(
+    _In_reads_( cbData )                        PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Init( _Out_ PSYMCRYPT_SHA512_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Append(
+    _Inout_                 PSYMCRYPT_SHA512_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Result(
+    _Inout_                                     PSYMCRYPT_SHA512_STATE pState,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE                  pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512StateCopy( _In_ PCSYMCRYPT_SHA512_STATE pSrc, _Out_ PSYMCRYPT_SHA512_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512StateExport(
+    _In_                                                    PCSYMCRYPT_SHA512_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_STATE_EXPORT_SIZE ) PBYTE                   pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512StateImport(
+    _Out_                                                   PSYMCRYPT_SHA512_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha512Algorithm;
+
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-512/224
+//
+//
+// The SHA-512/224 hash algorithm per FIPS 180-4.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-512/224 standard limits data inputs to a maximum of 2^125-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA512_224_RESULT_SIZE         (28)
+#define SYMCRYPT_SHA512_224_INPUT_BLOCK_SIZE    (128)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224(
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA512_224_RESULT_SIZE ) PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Init( _Out_ PSYMCRYPT_SHA512_224_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Append(
+    _Inout_                 PSYMCRYPT_SHA512_224_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Result(
+    _Inout_                                         PSYMCRYPT_SHA512_224_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA512_224_RESULT_SIZE ) PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224StateCopy( _In_ PCSYMCRYPT_SHA512_224_STATE pSrc, _Out_ PSYMCRYPT_SHA512_224_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224StateExport(
+    _In_                                                        PCSYMCRYPT_SHA512_224_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_224_STATE_EXPORT_SIZE ) PBYTE                       pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512_224StateImport(
+    _Out_                                                       PSYMCRYPT_SHA512_224_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_224_STATE_EXPORT_SIZE)    PCBYTE                      pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha512_224Algorithm;
+
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-512/256
+//
+//
+// The SHA-512/256 hash algorithm per FIPS 180-4.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// The SHA-512/256 standard limits data inputs to a maximum of 2^125-1 bytes.
+// This implementation supports larger inputs, and simply wraps the internal message
+// length counter. Note that the security properties are unknown for
+// such long messages, and their use is not recommended.
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+#define SYMCRYPT_SHA512_256_RESULT_SIZE         (32)
+#define SYMCRYPT_SHA512_256_INPUT_BLOCK_SIZE    (128)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256(
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA512_256_RESULT_SIZE ) PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Init( _Out_ PSYMCRYPT_SHA512_256_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Append(
+    _Inout_                 PSYMCRYPT_SHA512_256_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Result(
+    _Inout_                                         PSYMCRYPT_SHA512_256_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA512_256_RESULT_SIZE ) PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256StateCopy( _In_ PCSYMCRYPT_SHA512_256_STATE pSrc, _Out_ PSYMCRYPT_SHA512_256_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256StateExport(
+    _In_                                                        PCSYMCRYPT_SHA512_256_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_256_STATE_EXPORT_SIZE ) PBYTE                       pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512_256StateImport(
+    _Out_                                                       PSYMCRYPT_SHA512_256_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_256_STATE_EXPORT_SIZE)    PCBYTE                      pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha512_256Algorithm;
+
+
+////////////////////////////////////////////////////////////////////////////
+//   SHA-3
+//
+// The SHA-3 family of hash algorithms per FIPS 202.
+// This implementation is limited to data strings that are in whole bytes.
+// Odd bit length are not supported.
+//
+// SHA3-224 is meant for interoperability and is not recommended for use.
+//
+// SHA3-224(M) = KECCAK[448](M || 01, 224)
+// SHA3-256(M) = KECCAK[512](M || 01, 256)
+// SHA3-384(M) = KECCAK[768](M || 01, 384)
+// SHA3-512(M) = KECCAK[1024](M || 01, 512)
+//
+// For details on this API see the description above about the generic hash function API.
+//
+
+
+//
+// SHA-3-224
+//
+
+#define SYMCRYPT_SHA3_224_RESULT_SIZE         (28)
+#define SYMCRYPT_SHA3_224_INPUT_BLOCK_SIZE    (144)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224(
+    _In_reads_(cbData)                          PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_(SYMCRYPT_SHA3_224_RESULT_SIZE) PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Init(_Out_ PSYMCRYPT_SHA3_224_STATE pState);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Append(
+    _Inout_                 PSYMCRYPT_SHA3_224_STATE    pState,
+    _In_reads_(cbData)      PCBYTE                      pbData,
+                            SIZE_T                      cbData);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Result(
+    _Inout_                                     PSYMCRYPT_SHA3_224_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_224_RESULT_SIZE) PBYTE                       pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224StateCopy(_In_ PCSYMCRYPT_SHA3_224_STATE pSrc, _Out_ PSYMCRYPT_SHA3_224_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_224_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE) PBYTE                       pbBlob);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_224StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_224_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha3_224Algorithm;
+
+
+//
+// SHA-3-256
+//
+
+#define SYMCRYPT_SHA3_256_RESULT_SIZE         (32)
+#define SYMCRYPT_SHA3_256_INPUT_BLOCK_SIZE    (136)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256(
+    _In_reads_(cbData)                          PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_(SYMCRYPT_SHA3_256_RESULT_SIZE) PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Init(_Out_ PSYMCRYPT_SHA3_256_STATE pState);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Append(
+    _Inout_                 PSYMCRYPT_SHA3_256_STATE    pState,
+    _In_reads_(cbData)      PCBYTE                      pbData,
+                            SIZE_T                      cbData);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Result(
+    _Inout_                                     PSYMCRYPT_SHA3_256_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_256_RESULT_SIZE) PBYTE                       pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256StateCopy(_In_ PCSYMCRYPT_SHA3_256_STATE pSrc, _Out_ PSYMCRYPT_SHA3_256_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_256_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE) PBYTE                       pbBlob);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_256StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_256_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha3_256Algorithm;
+
+
+//
+// SHA-3-384
+//
+
+#define SYMCRYPT_SHA3_384_RESULT_SIZE         (48)
+#define SYMCRYPT_SHA3_384_INPUT_BLOCK_SIZE    (104)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384(
+    _In_reads_(cbData)                          PCBYTE  pbData,
+                                                SIZE_T  cbData,
+    _Out_writes_(SYMCRYPT_SHA3_384_RESULT_SIZE) PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Init(_Out_ PSYMCRYPT_SHA3_384_STATE pState);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Append(
+    _Inout_                 PSYMCRYPT_SHA3_384_STATE    pState,
+    _In_reads_(cbData)      PCBYTE                      pbData,
+                            SIZE_T                      cbData);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Result(
+    _Inout_                                     PSYMCRYPT_SHA3_384_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_384_RESULT_SIZE) PBYTE                       pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384StateCopy(_In_ PCSYMCRYPT_SHA3_384_STATE pSrc, _Out_ PSYMCRYPT_SHA3_384_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_384_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE) PBYTE                       pbBlob);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_384StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_384_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha3_384Algorithm;
+
+
+//
+// SHA-3-512
+//
+
+#define SYMCRYPT_SHA3_512_RESULT_SIZE         (64)
+#define SYMCRYPT_SHA3_512_INPUT_BLOCK_SIZE    (72)
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512(
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHA3_512_RESULT_SIZE )   PBYTE   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Init( _Out_ PSYMCRYPT_SHA3_512_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Append(
+    _Inout_                 PSYMCRYPT_SHA3_512_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Result(
+    _Inout_                                         PSYMCRYPT_SHA3_512_STATE    pState,
+    _Out_writes_( SYMCRYPT_SHA3_512_RESULT_SIZE )   PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512StateCopy( _In_ PCSYMCRYPT_SHA3_512_STATE pSrc, _Out_ PSYMCRYPT_SHA3_512_STATE pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512StateExport(
+    _In_                                                        PCSYMCRYPT_SHA3_512_STATE   pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE )   PBYTE                       pbBlob );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_512StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_512_STATE    pState,
+    _In_reads_bytes_( SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE)  PCBYTE                      pbBlob );
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptSha3_512Algorithm;
+
+
+//==========================================================================
+//   Extendable-Output Functions (XOFs)
+//==========================================================================
+//
+//  XOFs are similar to hash functions except that the output can be arbitrary length.
+//  SHAKE128 and SHAKE256 are XOFs specified in FIPS 202.
+//
+//  SHAKE128(M, d) = KECCAK[256] (M || 1111, d)
+//  SHAKE256(M, d) = KECCAK[512] (M || 1111, d)
+//
+//  SHAKEs share the same Keccak state as the other Keccak based algorithms under
+//  the name SYMCRYPT_SHAKEXxx_STATE.
+//
+//  Both SHAKE128 and SHAKE256 have default result sizes (32- and 64-bytes resp.)
+//  that allows them to be used as substitutes for hash functions with the Init-Append-Result
+//  pattern.
+//
+//  Extract is a new type of function that does not exist in hash functions, which can
+//  be called multiple times to successively generate output from the state. Extract
+//  function also provides the caller with a flag to wipe the state when no further Extract
+//  calls will be made. If the caller does not know in advance whether an Extract call is
+//  the final one, wiping can be performed later with an Init call or an Extract call with
+//  zero bytes output.
+//
+//  If Append is called after an Extract call which did not wipe the state (i.e., the state
+//  is still in 'extract' mode), Append will notice this and switch from 'extract' mode to
+//  'append' mode by wiping and initializing the state. This Append call effectively appends
+//  data for a fresh computation, saving an additional call to wipe/initialize the state.
+//
+//
+//  SYMCRYPT_SHAKEXXX_RESULT_SIZE
+//
+//      Default output size, used by the SymCryptShakeXxxResult function.
+//
+//  SYMCRYPT_SHAKEXXX_INPUT_BLOCK_SIZE
+//
+//      Rate for the Keccak permutation.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxxDefault(
+//      _In_reads_( cbData )                            PCBYTE  pbData,
+//                                                      SIZE_T  cbData,
+//      _Out_writes_( SYMCRYPT_SHAKEXXX_RESULT_SIZE )   PBYTE   pbResult);
+//
+//      SHAKE single-call function that produces default output size defined by
+//      SYMCRYPT_SHAKEXXX_RESULT_SIZE.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxx(
+//      _In_reads_( cbData )        PCBYTE  pbData,
+//                                  SIZE_T  cbData,
+//      _Out_writes_( cbResult )    PBYTE   pbResult,
+//                                  SIZE_T  cbResult);
+//
+//      SHAKE single-call function that produces variable-length output specified
+//      by the cbResult parameter.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxxInit( _Out_ PSYMCRYPT_XXX_STATE pState );
+//
+//      Initializes the SHAKE state.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxxAppend(
+//      _Inout_                 PSYMCRYPT_XXX_STATE pState,
+//      _In_reads_( cbData )    PCBYTE              pbData,
+//                              SIZE_T              cbData );
+//
+//      Appends data to the SHAKE state.
+//
+//      Append cannot be the first call to an uninitialized SHAKE state. All
+//      other uses independent of whether the state is in 'append' mode or 'extract'
+//      mode are well defined. If the state was previously in 'extract' mode, (i.e., after
+//      an Extract call with bWipe=FALSE) it wipes/resets the state and the data is
+//      appended to a fresh state.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxxExtract(
+//      _Inout_                 PSYMCRYPT_XXX_STATE pState,
+//      _Out_writes_(cbResult)  PBYTE               pbResult,
+//                              SIZE_T              cbResult,
+//                              BOOLEAN             bWipe);
+//
+//      Generates output from the SHAKE state.
+//
+//      Extract cannot be the first call to an uninitialized SHAKE state. All
+//      other uses independent of whether the state is in 'append' mode or 'extract' mode
+//      are well defined.
+//
+//      If the state was in 'append' mode before the Extract call, Extract switches
+//      the state to 'extract' mode and generates the requested number of bytes from
+//      the state. Extract wipes/resets the state and transitions the state to 'append'
+//      mode if bWipe=TRUE, otherwise leaving the state in 'extract' mode, available for
+//      further extractions.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxxResult(
+//      _Inout_                                     PSYMCRYPT_XXX_STATE pState,
+//      _Out_writes_(SYMCRYPT_SHAKEXXX_RESULT_SIZE) PBYTE               pbResult );
+//
+//      Extracts SYMCRYPT_SHAKEXXX_RESULT_SIZE bytes from the state and wipes/resets
+//      it for a new computation.
+//
+//      Result cannot be called with an uninitialized state. All other uses are well
+//      defined. If it is called after an Extract call with bWipe=FALSE, it does the
+//      final extraction from the state for SYMCRYPT_SHAKEXXX_RESULT_SIZE bytes,
+//      effectively calling Extract with cbResult=SYMCRYPT_SHAKEXXX_RESULT_SIZE and
+//      bWipe=TRUE.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptShakeXxxStateCopy(_In_ PCSYMCRYPT_SHAKEXXX_STATE pSrc, _Out_ PSYMCRYPT_SHAKEXXX_STATE pDst);
+//
+//      Create a new copy of the state object.
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptShakeXxxSelftest(void);
+//
+//      Perform a minimal self-test on the ShakeXxx algorithm.
+//      This function is designed to be used for achieving FIPS 140-2 compliance or
+//      to provide a simple self-test when an application starts.
+//
+//      If an error is detected, a platform-specific fatal error action is taken.
+//      Callers do not need to handle any error conditions.
+
+
+//
+// SHAKE128
+//
+#define SYMCRYPT_SHAKE128_RESULT_SIZE           (32)
+#define SYMCRYPT_SHAKE128_INPUT_BLOCK_SIZE      (168)
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Default(
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHAKE128_RESULT_SIZE )   PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128(
+    _In_reads_( cbData )        PCBYTE  pbData,
+                                SIZE_T  cbData,
+    _Out_writes_( cbResult )    PBYTE   pbResult,
+                                SIZE_T  cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Init( _Out_ PSYMCRYPT_SHAKE128_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Append(
+    _Inout_                 PSYMCRYPT_SHAKE128_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Extract(
+    _Inout_                 PSYMCRYPT_SHAKE128_STATE    pState,
+    _Out_writes_(cbResult)  PBYTE                       pbResult,
+                            SIZE_T                      cbResult,
+                            BOOLEAN                     bWipe);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Result(
+    _Inout_                                     PSYMCRYPT_SHAKE128_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHAKE128_RESULT_SIZE) PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128StateCopy(_In_ PCSYMCRYPT_SHAKE128_STATE pSrc, _Out_ PSYMCRYPT_SHAKE128_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptShake128HashAlgorithm;
+
+//
+// SHAKE256
+//
+#define SYMCRYPT_SHAKE256_RESULT_SIZE           (64)
+#define SYMCRYPT_SHAKE256_INPUT_BLOCK_SIZE      (136)
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Default(
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHAKE256_RESULT_SIZE )   PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256(
+    _In_reads_( cbData )        PCBYTE  pbData,
+                                SIZE_T  cbData,
+    _Out_writes_( cbResult )    PBYTE   pbResult,
+                                SIZE_T  cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Init( _Out_ PSYMCRYPT_SHAKE256_STATE pState );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Append(
+    _Inout_                 PSYMCRYPT_SHAKE256_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Extract(
+    _Inout_                 PSYMCRYPT_SHAKE256_STATE    pState,
+    _Out_writes_(cbResult)  PBYTE                       pbResult,
+                            SIZE_T                      cbResult,
+                            BOOLEAN                     bWipe);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Result(
+    _Inout_                                     PSYMCRYPT_SHAKE256_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHAKE256_RESULT_SIZE) PBYTE                       pbResult );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256StateCopy(_In_ PCSYMCRYPT_SHAKE256_STATE pSrc, _Out_ PSYMCRYPT_SHAKE256_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Selftest(void);
+
+extern const PCSYMCRYPT_HASH SymCryptShake256HashAlgorithm;
+
+//==========================================================================
+//   Customizable Extendable-Output Functions (XOFs)
+//==========================================================================
+//
+//  cSHAKE128 and cSHAKE256 are customizable SHAKE functions specified in NIST SP 800-185.
+//
+//  When cSHAKE input strings N (function name string) and S (customization string) are
+//  both empty, cSHAKE is equivalent to SHAKE:
+//
+//  cSHAKE128(X, L, "", "") = SHAKE128(X, L)
+//  cSHAKE256(X, L, "", "") = SHAKE256(X, L)
+//
+//  If at least one of N and S is non-empty, cSHAKE is defined as follows:
+//
+//  cSHAKE128(X, L, N, S) = KECCAK[256](bytepad(encode_string(N) || encode_string(S), 168) || X || 00, L)
+//  cSHAKE256(X, L, N, S) = KECCAK[512](bytepad(encode_string(N) || encode_string(S), 136) || X || 00, L)
+//
+//  The following functions are equivalent to their SHAKE counterparts.
+//  SymCryptCShakeXxxExtract with bWipe=TRUE and SymCryptCShakeXxxResult functions reset
+//  the cSHAKE state to an empty SHAKE state after generating output. This behavior is
+//  equivalent to calling SymCryptCShakeXxxInit with empty input strings.
+//
+//      SymCryptCShakeXxxAppend
+//      SymCryptCShakeXxxExtract
+//      SymCryptCShakeXxxResult
+//
+//  Calling SymCryptCShakeXxxAppend when cSHAKE state is in 'extract' mode results
+//  in the same behavior described above: the state is wiped and initialized with
+//  empty input strings, after which the data is appended to the empty state. This
+//  converts the state to a SHAKE state since cSHAKE with empty input strings is
+//  equivalent to SHAKE. This is a consequence of not being able to store the input
+//  strings to cSHAKE and re-initialize it with them. Thus, if multiple cSHAKE
+//  computations with the same input strings are to be carried out, cSHAKE state must
+//  be initialized with the input strings each time.
+//
+//  The following functions differ from the SHAKE by the introduction of customization
+//  strings:
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptCShakeXxx(
+//      _In_reads_( cbFunctionNameString )  PCBYTE  pbFunctionNameString,
+//                                          SIZE_T  cbFunctionNameString,
+//      _In_reads_( cbCustomizationString ) PCBYTE  pbCustomizationString,
+//                                          SIZE_T  cbCustomizationString,
+//      _In_reads_( cbData )                PCBYTE  pbData,
+//                                          SIZE_T  cbData,
+//      _Out_writes_( cbResult )            PBYTE   pbResult,
+//                                          SIZE_T  cbResult);
+//
+//      Single-call cSHAKE computation.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptCShakeXxxInit(
+//      _Out_                               PSYMCRYPT_CSHAKEXXX_STATE   pState,
+//      _In_reads_( cbFunctionNameString )  PCBYTE                      pbFunctionNameString,
+//                                          SIZE_T                      cbFunctionNameString,
+//      _In_reads_( cbCustomizationString ) PCBYTE                      pbCustomizationString,
+//                                          SIZE_T                      cbCustomizationString);
+//
+//      Initializes the cSHAKE state with the provided input strings. If both of
+//      the input strings are empty, the call is equivalent to SymCryptShakeXxxInit,
+//      otherwise the input strings will be encoded and appended to the state.
+
+
+//
+// cSHAKE128
+//
+#define SYMCRYPT_CSHAKE128_RESULT_SIZE           SYMCRYPT_SHAKE128_RESULT_SIZE
+#define SYMCRYPT_CSHAKE128_INPUT_BLOCK_SIZE      SYMCRYPT_SHAKE128_INPUT_BLOCK_SIZE
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128(
+    _In_reads_( cbFunctionNameString )  PCBYTE  pbFunctionNameString,
+                                        SIZE_T  cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE  pbCustomizationString,
+                                        SIZE_T  cbCustomizationString,
+    _In_reads_( cbData )                PCBYTE  pbData,
+                                        SIZE_T  cbData,
+    _Out_writes_( cbResult )            PBYTE   pbResult,
+                                        SIZE_T  cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128Init(
+    _Out_                               PSYMCRYPT_CSHAKE128_STATE   pState,
+    _In_reads_( cbFunctionNameString )  PCBYTE                      pbFunctionNameString,
+                                        SIZE_T                      cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE                      pbCustomizationString,
+                                        SIZE_T                      cbCustomizationString);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128Append(
+    _Inout_                 PSYMCRYPT_CSHAKE128_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128Extract(
+    _Inout_                 PSYMCRYPT_CSHAKE128_STATE   pState,
+    _Out_writes_(cbResult)  PBYTE                       pbResult,
+                            SIZE_T                      cbResult,
+                            BOOLEAN                     bWipe);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128Result(
+    _Inout_                                         PSYMCRYPT_CSHAKE128_STATE   pState,
+    _Out_writes_( SYMCRYPT_CSHAKE128_RESULT_SIZE )  PBYTE                       pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128StateCopy(_In_ PCSYMCRYPT_CSHAKE128_STATE pSrc, _Out_ PSYMCRYPT_CSHAKE128_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128Selftest(void);
+
+
+//
+// cSHAKE256
+//
+#define SYMCRYPT_CSHAKE256_RESULT_SIZE           SYMCRYPT_SHAKE256_RESULT_SIZE
+#define SYMCRYPT_CSHAKE256_INPUT_BLOCK_SIZE      SYMCRYPT_SHAKE256_INPUT_BLOCK_SIZE
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256(
+    _In_reads_( cbFunctionNameString )  PCBYTE  pbFunctionNameString,
+                                        SIZE_T  cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE  pbCustomizationString,
+                                        SIZE_T  cbCustomizationString,
+    _In_reads_( cbData )                PCBYTE  pbData,
+                                        SIZE_T  cbData,
+    _Out_writes_( cbResult )            PBYTE   pbResult,
+                                        SIZE_T  cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256Init(
+    _Out_                               PSYMCRYPT_CSHAKE256_STATE   pState,
+    _In_reads_( cbFunctionNameString )  PCBYTE                      pbFunctionNameString,
+                                        SIZE_T                      cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE                      pbCustomizationString,
+                                        SIZE_T                      cbCustomizationString);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256Append(
+    _Inout_                 PSYMCRYPT_CSHAKE256_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256Extract(
+    _Inout_                 PSYMCRYPT_CSHAKE256_STATE   pState,
+    _Out_writes_(cbResult)  PBYTE                       pbResult,
+                            SIZE_T                      cbResult,
+                            BOOLEAN                     bWipe);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256Result(
+    _Inout_                                         PSYMCRYPT_CSHAKE256_STATE   pState,
+    _Out_writes_( SYMCRYPT_CSHAKE256_RESULT_SIZE )  PBYTE                      pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256StateCopy(_In_ PCSYMCRYPT_CSHAKE256_STATE pSrc, _Out_ PSYMCRYPT_CSHAKE256_STATE pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256Selftest(void);
+
+
+
+//==========================================================================
+//   PARALLELISED HASH FUNCTIONS
+//==========================================================================
+//
+// On some platforms it is possible to parallelize the hash function
+// computation to achieve a higher throughput.
+// The parallel hash APIs support this.
+// The parallel implementation tries to perform the computations as efficiently
+// as possible. Applications that have many hashes to compute can always call these
+// functions; the library will optimize the computation to the current situation.
+// For example, if only a single hash is computed using these APIs, the
+// single-hash version is used to achieve full single-hash speed.
+// On platforms that do not support parallel hash implementations, these functions
+// are still available, and will implement the parallel hashing by computing the
+// hashes one at a time.
+//
+//
+// SYMCRYPT_PARALLEL_XXX_MIN_PARALLELISM
+//
+//      Compile-time constant, but can vary per platform.
+//      Minimum number of parallel computations at which
+//      the parallel implementation is faster on at least some CPU versions.
+//      Applications can safely ask for parallel computations with fewer hashes,
+//      but there will be no speed gain.
+//
+// SYMCRYPT_PARALLEL_XXX_MAX_PARALLELISM
+//
+//      Maximum internal parallelism that the library uses internally on at least one
+//      CPU version of this architecture.
+//      If all hash computations are the same length, then there is no significant
+//      benefit to providing more than this number of hash requests in parallel.
+//      However, if the hash computations are of different lengths then the library
+//      overlaps various hash computations and still gains efficiency when the
+//      number of parallel hash computations increases past this bound.
+//      Note that the internal parallelism that can be used might depend
+//      on the CPU features available, so this value is only an upper bound.
+//      We recommend that callers provide as much parallelism as practical,
+//      and let the library perform the optimal sequence of computations.
+//
+// SYMCRYPT_HASH_OPERATION_TYPE
+//
+//      An enum that specifies which operation is to be performed in a command
+//      structure passed to a parallel hash operations function.
+//      Defined values:
+//          SYMCRYPT_HASH_OPERATION_APPEND;
+//          SYMCRYPT_HASH_OPERATION_RESULT;
+//
+// SYMCRYPT_PARALLEL_HASH_OPERATION
+//
+//      Structure that contains a command to be performed on a single item in a
+//      parallel hash state array. Visible fields are:
+//
+//      SIZE_T                          iHash;          // index of hash object into the state array
+//      SYMCRYPT_HASH_OPERATION_TYPE    hashOperation;  // operation to be performed
+//      PBYTE                           pbBuffer;       // data to be hashed, or result buffer
+//      SIZE_T                          cbBuffer;
+//
+//      There might be other fields in this structure that the caller should not use or assume anything about.
+//
+// SymCryptParallelXxxInit(
+//          _Out_writes_( nStates ) PSYMCRYPT_XXX_STATE pStates,
+//                                  SIZE_T              nStates );
+//      Initialize an array of hash states.
+//      The elements of the array are normal hash states, and they can be
+//      manipulated individually using the standard functions for the hash
+//      algorithm.
+//
+//      Functionally equivalent to:
+//          for( i=0; i<nStates; i++ ) {
+//              SymCryptXxxInit( &pStates[i] );
+//          }
+//
+//      It is not necessary to use this function to initialize a state array;
+//      the normal initialization function can also be used, but this function might
+//      be faster.
+//
+// SymCryptParallelXxxProcess(
+//          _Inout_updates_( nStates )      PSYMCRYPT_XXX_STATE                 pStates,
+//                                          SIZE_T                              nStates,
+//          _Inout_updates_( nOperations )  PSYMCRYPT_PARALLEL_HASH_OPERATION   pOperation,
+//                                          SIZE_T                              nOperations,
+//          _Out_writes_( cbScratch )       PBYTE                               pbScratch,
+//                                          SIZE_T                              cbScratch );
+//
+//      Perform optionally parallel processing of hashes.
+//      This is functionally equivalent to iterating over the pOperations array in order,
+//      and executing the command in each PARALLEL_HASH_OPERATION one at a time.
+//      For each command:
+//          iHash           Which hash state this operation applies to; must be < nStates.
+//          hashOperation   Specifies whether this is an append or result operation.
+//          pbBuffer        The buffer that contains the data to be hashed, or that will receive the result.
+//          cbBuffer        The size of pbBuffer. (Must be equal to the hash algorithm result size for RESULT operations.)
+//      As the SAL annotations document, the pOperations array is updated by this function, and therefore
+//      it cannot be in read-only memory.
+//      The updates modify only to the internal scratch space that is reserved
+//      in the SYMCRYPT_PARALLEL_HASH_OPERATION structure; none of the documented fields
+//      (iHash, hashOperation, pbBuffer, cbBuffer) are modified.
+//      The scratch fields are used purely within one call to this function, their value does not have to be
+//      maintained between function calls. The scratch fields do not have to be initialized by the caller
+//      of this function,
+//      THREAD SAFETY: as the pOperations array is updated, it CANNOT be shared between different threads.
+//      Obviously, the same is true of pStates and pbScratch.
+//
+//      The pbScratch pointer provides a scratch buffer for the parallel processing function.
+//      This is used to organize the request and perform the functions in an optimal order for
+//      maximum parallelism, and for storing intermediate results that are too large
+//      to fit on the stack. The scratch buffer must be at least
+//      SYMCRYPT_PARALLEL_XXX_FIXED_SCRATCH + nStates * SYMCRYPT_PARALLEL_HASH_PER_STATE_SCRATCH
+//      bytes in size.
+//
+//      For incremental hashing, we recommend that callers process data sizes that are
+//      a multiple of the SYMCRYPT_XXX_INPUT_BLOCK_LEN.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha256Init(
+    _Out_writes_( nStates ) PSYMCRYPT_SHA256_STATE pStates,
+                            SIZE_T                 nStates );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptParallelSha256Process(
+    _Inout_updates_( nStates )      PSYMCRYPT_SHA256_STATE              pStates,
+                                    SIZE_T                              nStates,
+    _Inout_updates_( nOperations )  PSYMCRYPT_PARALLEL_HASH_OPERATION   pOperations,
+                                    SIZE_T                              nOperations,
+    _Out_writes_( cbScratch )       PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha384Init(
+    _Out_writes_( nStates ) PSYMCRYPT_SHA384_STATE pStates,
+                            SIZE_T                 nStates );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptParallelSha384Process(
+    _Inout_updates_( nStates )      PSYMCRYPT_SHA384_STATE              pStates,
+                                    SIZE_T                              nStates,
+    _Inout_updates_( nOperations )  PSYMCRYPT_PARALLEL_HASH_OPERATION   pOperations,
+                                    SIZE_T                              nOperations,
+    _Out_writes_( cbScratch )       PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha512Init(
+    _Out_writes_( nStates ) PSYMCRYPT_SHA512_STATE pStates,
+                            SIZE_T                 nStates );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptParallelSha512Process(
+    _Inout_updates_( nStates )      PSYMCRYPT_SHA512_STATE              pStates,
+                                    SIZE_T                              nStates,
+    _Inout_updates_( nOperations )  PSYMCRYPT_PARALLEL_HASH_OPERATION   pOperations,
+                                    SIZE_T                              nOperations,
+    _Out_writes_( cbScratch )       PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha256Selftest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha384Selftest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha512Selftest(void);
+
+
+
+//==========================================================================
+//   MESSAGE AUTHENTICATION CODE (MAC)
+//==========================================================================
+//
+// All MAC functions have a similar interface. For consistency we describe
+// the generic parts of the interface once.
+// Algorithm-specific comments are given with the API functions of each algorithm separately.
+//
+// For a MAC algorithm called XXX the following functions, types, and constants are defined:
+//
+//
+// SYMCRYPT_XXX_RESULT_SIZE
+//
+//      A constant giving is the size, in bytes, of the result of the MAC function.
+//      Some applications use truncated MAC functions. These are not directly supported
+//      by this library. Applications will have to perform the truncation themselves.
+//
+//
+// SYMCRYPT_XXX_INPUT_BLOCK_SIZE
+//
+//      A constant giving the natural input block size for the MAC function.
+//      Most callers don't need to know this, but in some cases it can be useful
+//      for optimizations.
+//
+//
+// SYMCRYPT_XXX_EXPANDED_KEY
+//
+//      Type which contains a key with all the pre-computations performed.
+//      This is an opaque type whose structure can change at will.
+//      It should only be used for transient computations in a single executable
+//      and not be stored or transferred to a different environment.
+//      The pointer and const-pointer versions are also declared
+//      (PSYMCRYPOT_XXX_EXPANDED_KEY and PCSYMCRYPT_XXX_EXPANDED_KEY).
+//
+//      The EXPANDED_KEY structure contains keying material and should be wiped
+//      once it is no longer used. (See SymCryptWipe & SymCryptWipeKnownSize)
+//
+//      Once a key has been expanded, multiple threads can simultaneously use the same expanded key
+//      object for different MAC computations that use the same key as the expanded key
+//      object does not change value.
+//
+//
+// SYMCRYPT_ERROR
+// SYMCRYPT_CALL
+// SymCryptXxxExpandKey(   _Out_                PSYMCRYPT_XXX_EXPANDED_KEY  pExpandedKey,
+//                         _In_reads_(cbKey)    PCBYTE                      pbKey,
+//                                              SIZE_T                      cbKey );
+//
+//      Prepare a key for future use by the Xxx algorithm.
+//      This function performs pre-computations on the key
+//      to speed up the actual MAC computations later, and stores the result as an expanded key.
+//      The expanded key must be kept unchanged until all MAC computations that use the key are finished.
+//      When the key is no longer needed the expanded key structure should be wiped.
+//
+//      Different algorithms pose different requirements on the length of the key.
+//      If the key that is provided is of an unsupported length the SYMCRYPT_WRONG_KEY_SIZE error is returned.
+//      In this case the expanded key structure will not contain any keying material and does not have to be wiped.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxKeyCopy( _In_ PCSYMCRYPT_XXX_EXPANDED_KEY pSrc,
+//                     _Out_ PSYMCRYPT_XXX_EXPANDED_KEY pDst );
+//
+//      Create a copy of an expanded key.
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxx( _In_                                        PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//              _In_reads_( cbData )                        PCBYTE                      pbData,
+//                                                          SIZE_T                      cbData,
+//              _Out_writes_( SYMCRYPT_XXX_RESULT_SIZE )    PBYTE                       pbResult );
+//
+//      Computes the MAC value of the data buffer with a given key.
+//      If you have all the data to be MACed in a single buffer this is the simplest function to use.
+//
+//
+// SYMCRYPT_XXX_STATE
+//
+//      The state encodes an ongoing MAC computation and allows incremental
+//      computation of a MAC function.
+//      At any point in time the state encodes a state that is equivalent to
+//      the MAC computation of a data string X with the key specified during initialization of the state.
+//      The SymCryptXxxInit() function initializes a state.
+//      The SymCryptXxxAppend() function appends data to the data string X.
+//      The SymCryptXxxResult() function returns the final MAC result.
+//
+//      The state is an opaque type whose structure can change at will.
+//      It should only be used for transient computations in a single executable
+//      and not be stored or transferred to a different environment.
+//
+//      Once initialized using SymCryptXxxInit, the state contains sensitive keying information.
+//      The SymCryptXxxResult function wipes the sensitive information from the state.
+//      Callers can also wipe the structure themselves if it is no longer needed.
+//
+//      The state can be duplicated using the SymCryptXxxStateCopy function. This supports
+//      applications that compute the MAC over a prefix and then duplicate the state to
+//      compute the MAC using multiple different continuations.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxStateCopy(
+//      _In_        PCSYMCRYPT_XXX_STATE        pSrc,
+//      _In_opt_    PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//      _Out_       PSYMCRYPT_XXX_STATE         pDst );
+//
+//      Create a copy of the pSrc state in pDst. If pExpandedKey is NULL, the pDst state
+//      uses the same expanded key as the pSrc state did. If pExpandedKey is not NULL,
+//      it must point to an expanded key that contains the same key material as the key
+//      used by pSrc. (For example, a copy of the expanded key that pSrc uses.)
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxInit( _Out_   PSYMCRYPT_XXX_STATE         pState,
+//                  _In_    PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey);
+//
+//      Initialize a SYMCRYPT_XXX_STATE for subsequent use with the provided key.
+//
+//      This function can be called at any time and resets the state to correspond
+//      to the empty data string with the newly specified key.
+//      The SymCryptXxxAppend function appends data to the data string
+//      encoded by the state.
+//      The SymCryptXxxResult function finalizes the computation and
+//      returns the actual MAC result.
+//
+//      This function typically stores a pointer to the expanded key in the state.
+//      The expanded key must remain unchanged in
+//      memory until the SYMCRYPT_XXX_STATE structure is no longer used.
+//
+//      After initialization the state contains sensitive keying materials, and should
+//      be wiped when the state is no longer used. The SymCryptXxxResult() function
+//      also wipes the state, so this is only a concern for aborted MAC computations.
+//      Note that SymCryptXxxResult() does not wipe the expanded key; callers are always
+//      responsible for wiping the expanded key.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxAppend( _Inout_               PSYMCRYPT_XXX_STATE   pState,
+//                    _In_reads_( cbData )  PCBYTE                pbData,
+//                                          SIZE_T                cbData );
+//
+//      Provide more data to the ongoing MAC computation specified by the state.
+//      The state must have been initialized by SymCryptXxxInit.
+//      This function can be called multiple times on the same state
+//      to append more data to the encoded data string.
+//
+//      The SYMCRYPT_XXX_STATE structure contains the entire state of an ongoing
+//      MAC computation. If you want to MAC some data and then continue with
+//      multiple other strings you may create one or more copies of the state.
+//      (The expanded key must remain unchanged in memory until all copies of the state
+//      are no longer used.)
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxResult(
+//      _Inout_                                     PSYMCRYPT_XXX_STATE  pState,
+//      _Out_writes_( SYMCRYPT_XXX_RESULT_SIZE )    PBYTE                pbResult );
+//
+//      Returns the MAC result of the state.
+//      If the state was newly initialized this returns the MAC of the empty string
+//      using the key specified in the SymCryptXxxInit call.
+//      If one or more SymCryptXxxAppend function calls were made on this state
+//      it returns the MAC of the concatenation of all the data strings
+//      passed to SymCryptXxxAppend using the specified key.
+//
+//      The state is wiped to remove any traces of sensitive data.
+//      To use the same state for another MAC computation you must call
+//      SymCryptXxxInit again to re-initialize the state.
+//      This behaviour is different from hash function states that are re-initialized for
+//      use by the Result routine. This difference is by design; re-initializing a hash
+//      state is a safe operation. Re-initializing a MAC state puts keying information
+//      in the state, and callers would have to wipe the MAC state explicitly.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxSelftest(void);
+//
+//      Perform a minimal self-test on the XXX algorithm.
+//      This function is designed to be used for achieving FIPS 140-2 compliance or
+//      to provide a simple self-test when an application starts.
+//
+//      If an error is detected, a platform-specific fatal error action is taken.
+//      Callers do not need to handle any error conditions.
+//
+//
+// We also have the Generic HMAC API where the hash function to be used in the HMAC
+// computation can be selected at runtime.
+//
+
+typedef enum _SYMCRYPT_MAC_ID
+{
+    SYMCRYPT_MAC_ID_NULL                = 0,
+    SYMCRYPT_MAC_ID_HMAC_MD5            = 1,
+    SYMCRYPT_MAC_ID_HMAC_SHA1           = 2,
+    SYMCRYPT_MAC_ID_HMAC_SHA224         = 3,
+    SYMCRYPT_MAC_ID_HMAC_SHA256         = 4,
+    SYMCRYPT_MAC_ID_HMAC_SHA384         = 5,
+    SYMCRYPT_MAC_ID_HMAC_SHA512         = 6,
+    SYMCRYPT_MAC_ID_HMAC_SHA512_224     = 7,
+    SYMCRYPT_MAC_ID_HMAC_SHA512_256     = 8,
+    SYMCRYPT_MAC_ID_HMAC_SHA3_224       = 9,
+    SYMCRYPT_MAC_ID_HMAC_SHA3_256       = 10,
+    SYMCRYPT_MAC_ID_HMAC_SHA3_384       = 11,
+    SYMCRYPT_MAC_ID_HMAC_SHA3_512       = 12,
+    SYMCRYPT_MAC_ID_AES_CMAC            = 13,
+    SYMCRYPT_MAC_ID_KMAC_128            = 14,
+    SYMCRYPT_MAC_ID_KMAC_256            = 15
+} SYMCRYPT_MAC_ID;
+
+PCSYMCRYPT_MAC
+SYMCRYPT_CALL
+SymCryptGetMacAlgorithm( SYMCRYPT_MAC_ID macId );
+//
+// Returns a pointer to the MAC algorithm structure for the specified MAC ID.
+// Returns NULL if the MAC ID is invalid.
+//
+
+//
+// Generic HMAC API with parametrized hash function
+//
+VOID
+SYMCRYPT_CALL
+SymCryptHmacStateCopy(
+    _In_        PCSYMCRYPT_HMAC_STATE           pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_EXPANDED_KEY    pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_STATE            pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacKeyCopy(
+    _In_    PCSYMCRYPT_HMAC_EXPANDED_KEY    pSrc,
+    _Out_   PSYMCRYPT_HMAC_EXPANDED_KEY     pDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacExpandKey(
+    _In_                    PCSYMCRYPT_HASH             pHash,
+    _Out_                   PSYMCRYPT_HMAC_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                      pbKey,
+                            SIZE_T                      cbKey );
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptHmacInit(
+    _Out_   PSYMCRYPT_HMAC_STATE            pState,
+    _In_    PCSYMCRYPT_HMAC_EXPANDED_KEY    pExpandedKey );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacAppend(
+    _Inout_                 PSYMCRYPT_HMAC_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptHmacResult(
+    _Inout_                                         PSYMCRYPT_HMAC_STATE    pState,
+    _Out_writes_( pState->pKey->pHash->resultSize ) PBYTE                   pbResult );
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptHmac(
+    _In_                                            PCSYMCRYPT_HMAC_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                          pbData,
+                                                    SIZE_T                          cbData,
+    _Out_writes_( pExpandedKey->pHash->resultSize ) PBYTE                           pbResult );
+
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-MD5
+//
+//
+
+#define SYMCRYPT_HMAC_MD5_RESULT_SIZE       SYMCRYPT_MD5_RESULT_SIZE
+#define SYMCRYPT_HMAC_MD5_INPUT_BLOCK_SIZE  SYMCRYPT_MD5_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacMd5ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_MD5_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                          pbKey,
+                            SIZE_T                          cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_MD5_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_MD5_EXPANDED_KEY  pDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5(
+    _In_                                            PCSYMCRYPT_HMAC_MD5_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                           pbData,
+                                                    SIZE_T                           cbData,
+    _Out_writes_( SYMCRYPT_HMAC_MD5_RESULT_SIZE )   PBYTE                            pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5StateCopy(
+    _In_        PCSYMCRYPT_HMAC_MD5_STATE           pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_MD5_EXPANDED_KEY    pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_MD5_STATE            pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5Init(
+    _Out_   PSYMCRYPT_HMAC_MD5_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_MD5_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5Append(
+    _Inout_                 PSYMCRYPT_HMAC_MD5_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5Result(
+    _Inout_                                      PSYMCRYPT_HMAC_MD5_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_MD5_RESULT_SIZE )PBYTE                      pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5Selftest(void);
+
+extern const PCSYMCRYPT_MAC SymCryptHmacMd5Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-1
+//
+//
+
+#define SYMCRYPT_HMAC_SHA1_RESULT_SIZE       SYMCRYPT_SHA1_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA1_INPUT_BLOCK_SIZE  SYMCRYPT_SHA1_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha1ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA1_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                              pbKey,
+                            SIZE_T                              cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA1_EXPANDED_KEY  pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA1_EXPANDED_KEY   pDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1(
+    _In_                                            PCSYMCRYPT_HMAC_SHA1_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA1_RESULT_SIZE )  PBYTE                               pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA1_STATE          pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA1_EXPANDED_KEY   pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA1_STATE           pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1Init(
+    _Out_   PSYMCRYPT_HMAC_SHA1_STATE           pState,
+    _In_    PCSYMCRYPT_HMAC_SHA1_EXPANDED_KEY   pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA1_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1Result(
+    _Inout_                                         PSYMCRYPT_HMAC_SHA1_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA1_RESULT_SIZE )  PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1Selftest(void);
+
+extern const PCSYMCRYPT_MAC SymCryptHmacSha1Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-224
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+//
+
+#define SYMCRYPT_HMAC_SHA224_RESULT_SIZE       SYMCRYPT_SHA224_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA224_INPUT_BLOCK_SIZE  SYMCRYPT_SHA224_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha224ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA224_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                              pbKey,
+                            SIZE_T                              cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA224_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA224_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224(
+    _In_                                            PCSYMCRYPT_HMAC_SHA224_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA224_RESULT_SIZE )PBYTE                               pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA224_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA224_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA224_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224Init(
+    _Out_   PSYMCRYPT_HMAC_SHA224_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA224_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA224_STATE pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224Result(
+    _Inout_                                         PSYMCRYPT_HMAC_SHA224_STATE pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA224_RESULT_SIZE )PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha224Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-256
+//
+//
+
+#define SYMCRYPT_HMAC_SHA256_RESULT_SIZE       SYMCRYPT_SHA256_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA256_INPUT_BLOCK_SIZE  SYMCRYPT_SHA256_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha256ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA256_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                              pbKey,
+                            SIZE_T                              cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA256_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA256_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256(
+    _In_                                            PCSYMCRYPT_HMAC_SHA256_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA256_RESULT_SIZE )PBYTE                               pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA256_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA256_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA256_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256Init(
+    _Out_   PSYMCRYPT_HMAC_SHA256_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA256_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA256_STATE pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256Result(
+    _Inout_                                         PSYMCRYPT_HMAC_SHA256_STATE pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA256_RESULT_SIZE )PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha256Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-384
+//
+//
+
+#define SYMCRYPT_HMAC_SHA384_RESULT_SIZE       SYMCRYPT_SHA384_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA384_INPUT_BLOCK_SIZE  SYMCRYPT_SHA384_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha384ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA384_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                              pbKey,
+                            SIZE_T                              cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA384_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA384_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384(
+    _In_                                            PCSYMCRYPT_HMAC_SHA384_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA384_RESULT_SIZE )PBYTE                               pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA384_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA384_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA384_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384Init(
+    _Out_   PSYMCRYPT_HMAC_SHA384_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA384_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA384_STATE pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384Result(
+    _Inout_                                         PSYMCRYPT_HMAC_SHA384_STATE pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA384_RESULT_SIZE )PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha384Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-512
+//
+//
+
+#define SYMCRYPT_HMAC_SHA512_RESULT_SIZE       SYMCRYPT_SHA512_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA512_INPUT_BLOCK_SIZE  SYMCRYPT_SHA512_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha512ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA512_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                              pbKey,
+                            SIZE_T                              cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA512_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA512_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512(
+    _In_                                            PCSYMCRYPT_HMAC_SHA512_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA512_RESULT_SIZE )PBYTE                               pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA512_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA512_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA512_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512Init(
+    _Out_   PSYMCRYPT_HMAC_SHA512_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA512_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA512_STATE pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512Result(
+    _Inout_                                         PSYMCRYPT_HMAC_SHA512_STATE pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA512_RESULT_SIZE )PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha512Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-512_224
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+//
+
+#define SYMCRYPT_HMAC_SHA512_224_RESULT_SIZE       SYMCRYPT_SHA512_224_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA512_224_INPUT_BLOCK_SIZE  SYMCRYPT_SHA512_224_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha512_224ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224(
+    _In_                                                    PCSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                                    PCBYTE                                  pbData,
+                                                            SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA512_224_RESULT_SIZE )    PBYTE                                   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA512_224_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA512_224_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224Init(
+    _Out_   PSYMCRYPT_HMAC_SHA512_224_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA512_224_STATE pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224Result(
+    _Inout_                                                 PSYMCRYPT_HMAC_SHA512_224_STATE pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA512_224_RESULT_SIZE )    PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha512_224Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA-512_256
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+//
+
+#define SYMCRYPT_HMAC_SHA512_256_RESULT_SIZE       SYMCRYPT_SHA512_256_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA512_256_INPUT_BLOCK_SIZE  SYMCRYPT_SHA512_256_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha512_256ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256(
+    _In_                                                    PCSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                                    PCBYTE                                  pbData,
+                                                            SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA512_256_RESULT_SIZE )    PBYTE                                   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA512_256_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA512_256_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256Init(
+    _Out_   PSYMCRYPT_HMAC_SHA512_256_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA512_256_STATE pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256Result(
+    _Inout_                                                 PSYMCRYPT_HMAC_SHA512_256_STATE pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA512_256_RESULT_SIZE )    PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha512_256Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA3-224
+//
+// This implementation is meant for interoperability and is not recommended for use.
+//
+//
+
+#define SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE       SYMCRYPT_SHA3_224_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA3_224_INPUT_BLOCK_SIZE  SYMCRYPT_SHA3_224_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_224ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE )  PBYTE                                   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_224_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_224_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_224_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_224_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_224_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE )  PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha3_224Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA3-256
+//
+//
+
+#define SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE       SYMCRYPT_SHA3_256_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA3_256_INPUT_BLOCK_SIZE  SYMCRYPT_SHA3_256_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_256ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE )  PBYTE                                   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_256_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_256_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_256_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_256_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_256_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE )  PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha3_256Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA3-384
+//
+//
+
+#define SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE       SYMCRYPT_SHA3_384_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA3_384_INPUT_BLOCK_SIZE  SYMCRYPT_SHA3_384_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_384ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE )  PBYTE                                   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_384_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_384_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_384_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_384_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_384_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE )  PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha3_384Algorithm;
+
+////////////////////////////////////////////////////////////////////////////
+//   HMAC-SHA3-512
+//
+//
+
+#define SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE       SYMCRYPT_SHA3_512_RESULT_SIZE
+#define SYMCRYPT_HMAC_SHA3_512_INPUT_BLOCK_SIZE  SYMCRYPT_SHA3_512_INPUT_BLOCK_SIZE
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_512ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey );
+//
+// Supports all key lengths; never returns an error.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE )  PBYTE                                   pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_512_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_512_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_512_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_512_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_512_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE )  PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Selftest(void);
+
+extern const PCSYMCRYPT_MAC  SymCryptHmacSha3_512Algorithm;
+
+
+////////////////////////////////////////////////////////////////////////////
+//   AES-CMAC
+//
+// This is the AES-CMAC algorithm per SP 800-38B & RFC 4493.
+// It is also known as AES-OMAC1.
+//
+
+#define SYMCRYPT_AES_CMAC_RESULT_SIZE        (16)
+#define SYMCRYPT_AES_CMAC_INPUT_BLOCK_SIZE   (16)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesCmacExpandKey(
+    _Out_               PSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                          pbKey,
+                        SIZE_T                          cbKey );
+//
+// Key size must be a valid AES key (16, 24, or 32 bytes)
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacKeyCopy(
+    _In_    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_AES_CMAC_EXPANDED_KEY  pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmac(
+    _In_                                            PSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                          pbData,
+                                                    SIZE_T                          cbData,
+    _Out_writes_( SYMCRYPT_AES_CMAC_RESULT_SIZE )   PBYTE                           pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacStateCopy(
+    _In_        PCSYMCRYPT_AES_CMAC_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_AES_CMAC_STATE         pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacInit(
+    _Out_   PSYMCRYPT_AES_CMAC_STATE        pState,
+    _In_    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacAppend(
+    _Inout_                 PSYMCRYPT_AES_CMAC_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacResult(
+    _Inout_                                         PSYMCRYPT_AES_CMAC_STATE    pState,
+    _Out_writes_( SYMCRYPT_AES_CMAC_RESULT_SIZE )   PBYTE                       pbResult );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacSelftest(void);
+
+extern const PCSYMCRYPT_MAC SymCryptAesCmacAlgorithm;
+
+////////////////////////////////////////////////////////////////////////////
+// KMAC
+//
+// Keccak Message Authentication Code (KMAC) is specified in NIST SP 800-185
+// and has two variants; KMAC128 and KMAC256, using cSHAKE128 and cSHAKE256
+// as the underlying functions, respectively.
+//
+// KMAC128(K, X, L, S) = cSHAKE128(bytepad(encode_string(K), 168) || X || right_encode(L), L, "KMAC", S)
+// KMAC256(K, X, L, S) = cSHAKE256(bytepad(encode_string(K), 136) || X || right_encode(L), L, "KMAC", S)
+//
+// KMAC accepts a variable-size key. There's no restriction on the size of the key.
+//
+// KMAC differs from other MAC algorithms in SymCrypt by having two additional input
+// parameters; a customization string and the length of the output. Output generated
+// by KMAC also depends on the specified output length, i.e., outputs generated from
+// two KMAC calls with the same key, message, customization string, but different output
+// lengths will be unrelated/uncorrelated. This differs from SHAKE and cSHAKE where an
+// output of size N bytes from the algorithm is a prefix of the output of size M bytes
+// where N < M, when the inputs are the same.
+//
+// KMAC works in two modes; fixed-length mode and XOF mode. XOF variants are named KMACXOF128
+// and  KMACXOF256. SymCrypt does not provide a separate KMACXOF API but supports them via
+// the KMAC interface.
+//
+// KMACXOF128(K, X, L, S) = cSHAKE128(bytepad(encode_string(K), 168) || X || right_encode(0), L, "KMAC", S)
+// KMACXOF256(K, X, L, S) = cSHAKE256(bytepad(encode_string(K), 136) || X || right_encode(0), L, "KMAC", S)
+//
+// KMAC output generation mode is determined by the output length parameter
+// L in SP 800-185; if it is non-zero then KMAC works in fixed-length mode, otherwise (i.e., L=0)
+// it works in XOF mode.
+//  - Fixed-length mode generates result with SymCryptKmacXxxResult or SymCryptKmacXxxResultEx.
+//    These functions wipe the state after generating output, thus can only be used
+//    once per initialized state. The result size is SYMCRYPT_KMAC_XXX_RESULT
+//    for SymCryptKmacXxxResult and specified by the caller for SymCryptKmacXxxResultEx.
+//  - XOF mode can produce arbitrary length output. SymCryptKmacXxxExtract function puts KMAC
+//    state into XOF mode and all the successive calls that generate output from the KMAC state will be
+//    from the XOF mode. SymCryptKmacXxxResult and SymCryptKmacXxxResultEx functions
+//    will also generate output in XOF mode IF they are called after a SymCryptKmacXxxExtract
+//    function with bWipe=FALSE (so that the state remains in XOF mode). Note that
+//    SymCryptKmacXxxResult and SymCryptKmacXxxResultEx functions wipe the state afterwards,
+//    thus KMAC state can only be used to generate output in XOF mode once with these two functions.
+//
+//  SYMCRYPT_KMACXXX_RESULT_SIZE
+//
+//      Default result size when KMAC is used with the existing MAC interface.
+//      Equals to twice the SYMCRYPT_KMACXXX_KEY_SIZE.
+//
+//  SYMCRYPT_ERROR
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxExpandKey(
+//          _Out_                                       PSYMCRYPT_KMACXXX_EXPANDED_KEY pExpandedKey,
+//          _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+//                                                      SIZE_T  cbKey);
+//
+//      Performs key expansion with empty customization string.
+//      There's no restriction on the size of the key.
+//
+//  SYMCRYPT_ERROR
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxExpandKeyEx(
+//          _Out_                                       PSYMCRYPT_KMAXXX_EXPANDED_KEY pExpandedKey,
+//          _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+//                                                      SIZE_T  cbKey,
+//          _In_reads_bytes_( cbCustomizationString )   PCBYTE  pbCustomizationString,
+//                                                      SIZE_T  cbCustomizationString);
+//
+//      Performs key expansion for the provided key and customization string.
+//      There's no restriction on the size of the key.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxx(
+//          _In_                                                PCSYMCRYPT_KMACXXX_EXPANDED_KEY pExpandedKey,
+//          _In_reads_bytes_( cbInput )                         PCBYTE  pbInput,
+//                                                              SIZE_T  cbInput,
+//          _Out_writes_bytes_( SYMCRYPT_KMACXXX_RESULT_SIZE )  PBYTE   pbResult);
+//
+//      Single-call KMAC computation for the given input producing default result
+//      size SYMCRYPT_KMACXXX_RESULT_SIZE.
+//
+//      pExpandedKey must be initialized before the call. This function is equivalent
+//      to SymCryptKmacXxxEx with output size set to SYMCRYPT_KMACXXX_RESULT_SIZE.
+//      If a result size different than the default value is desired, SymCryptKmacXxxEx
+//      must be called.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxEx(
+//          _In_                            PCSYMCRYPT_KMACXXX_EXPANDED_KEY pExpandedKey,
+//          _In_reads_bytes_( cbInput )     PCBYTE  pbInput,
+//                                          SIZE_T  cbInput,
+//          _Out_writes_bytes_( cbResult )  PBYTE   pbResult,
+//                                          SIZE_T  cbResult);
+//
+//      Single-call KMAC computation for the given input producing cbResult bytes result.
+//      pExpandedKey must be initialized before the call.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxInit(
+//      _Out_   PSYMCRYPT_KMACXXX_STATE         pState,
+//      _In_    PCSYMCRYPT_KMACXXX_EXPANDED_KEY pExpandedKey);
+//
+//      Initializes KMAC state for appending data for the provided key. Expanded
+//      key must be generated prior to this call.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxAppend(
+//      _Inout_                 PSYMCRYPT_KMACXXX_STATE pState,
+//      _In_reads_( cbData )    PCBYTE                  pbData,
+//                              SIZE_T                  cbData );
+//
+//      Appends data to the KMAC state.
+//
+//      This function must only be called after SymCryptKmacXxxInit or SymCryptKmacXxxAppend.
+//      Calling SymCryptKmacXxxAppend after SymCryptKmacXxxExtract with bWipe=FALSE
+//      is not well-defined. KMAC state must be initialized with SymCryptKmacXxxInit before
+//      the first call to SymCryptKmacXxxAppend.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxExtract(
+//      _Inout_                     PSYMCRYPT_KMACXXX_STATE pState,
+//      _Out_writes_( cbOutput )    PBYTE                   pbOutput,
+//                                  SIZE_T                  cbOutput,
+//                                  BOOLEAN                 bWipe);
+//
+//      Generates KMAC output in XOF mode.
+//
+//      Extract can only be called after an Init, Append or Extract call.
+//      The state is cleared if bWipe=TRUE, otherwise further Extract calls
+//      can be made to generate more output.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxResult(
+//      _Inout_                                         PSYMCRYPT_KMACXXX_STATE pState,
+//      _Out_writes_( SYMCRYPT_KMACXXX_RESULT_SIZE )    PBYTE   pbResult);
+//
+//      Produces SYMCRYPT_KMACXXX_RESULT_SIZE bytes of output from the KMAC state.
+//      The state is wiped on return.
+//
+//      This function internally calls SymCryptKmacXxxResultEx with result size
+//      SYMCRYPT_KMACXXX_RESULT_SIZE.
+//      If Result is called in XOF mode (i.e., after an Extract with bWipe=FALSE), it
+//      performs a final extraction of SYMCRYPT_KMACXXX_RESULT_SIZE bytes in XOF mode
+//      and clears the state afterwards.
+//      Result function does not re-initialize the state for a new computation like
+//      the Result for hash functions do. Computing a new MAC with the same key
+//      requires calling the SymCryptKmacXxxInit function first.
+//
+//  VOID
+//  SYMCRYPT_CALL
+//  SymCryptKmacXxxResultEx(
+//      _Inout_                     PSYMCRYPT_KMACXXX_STATE pState,
+//      _Out_writes_( cbResult )    PBYTE   pbResult,
+//                                  SIZE_T  cbResult);
+//
+//      Produces cbResult bytes of output from the KMAC state. The state is
+//      wiped on return.
+//
+//      If ResultEx is called in XOF mode (i.e., after an Extract with bWipe=FALSE), it
+//      performs a final extraction of cbResult bytes in XOF mode and clears the state
+//      afterwards.
+//      ResultEx function does not re-initialize the state for a new computation like
+//      the Result for hash functions do. Computing a new MAC with the same key
+//      requires calling the SymCryptKmacXxxInit function first.
+//
+
+
+//
+//  KMAC128
+//
+#define SYMCRYPT_KMAC128_RESULT_SIZE        SYMCRYPT_CSHAKE128_RESULT_SIZE
+#define SYMCRYPT_KMAC128_INPUT_BLOCK_SIZE   SYMCRYPT_CSHAKE128_INPUT_BLOCK_SIZE
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128(
+        _In_                                                PCSYMCRYPT_KMAC128_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbInput )                         PCBYTE  pbInput,
+                                                            SIZE_T  cbInput,
+        _Out_writes_bytes_( SYMCRYPT_KMAC128_RESULT_SIZE )  PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Ex(
+        _In_                            PCSYMCRYPT_KMAC128_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbInput )     PCBYTE  pbInput,
+                                        SIZE_T  cbInput,
+        _Out_writes_bytes_( cbResult )  PBYTE   pbResult,
+                                        SIZE_T  cbResult);
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptKmac128ExpandKey(
+        _Out_                                       PSYMCRYPT_KMAC128_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+                                                    SIZE_T  cbKey);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptKmac128ExpandKeyEx(
+        _Out_                                       PSYMCRYPT_KMAC128_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+                                                    SIZE_T  cbKey,
+        _In_reads_bytes_( cbCustomizationString )   PCBYTE  pbCustomizationString,
+                                                    SIZE_T  cbCustomizationString);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Init(
+    _Out_   PSYMCRYPT_KMAC128_STATE         pState,
+    _In_    PCSYMCRYPT_KMAC128_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Append(
+    _Inout_                 PSYMCRYPT_KMAC128_STATE pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Extract(
+    _Inout_                     PSYMCRYPT_KMAC128_STATE pState,
+    _Out_writes_( cbOutput )    PBYTE                   pbOutput,
+                                SIZE_T                  cbOutput,
+                                BOOLEAN                 bWipe);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Result(
+    _Inout_                                         PSYMCRYPT_KMAC128_STATE pState,
+    _Out_writes_( SYMCRYPT_KMAC128_RESULT_SIZE )    PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128ResultEx(
+    _Inout_                     PSYMCRYPT_KMAC128_STATE pState,
+    _Out_writes_( cbResult )    PBYTE   pbResult,
+                                SIZE_T  cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128KeyCopy(_In_ PCSYMCRYPT_KMAC128_EXPANDED_KEY pSrc, _Out_ PSYMCRYPT_KMAC128_EXPANDED_KEY pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128StateCopy(_In_ const SYMCRYPT_KMAC128_STATE* pSrc, _Out_ SYMCRYPT_KMAC128_STATE* pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Selftest(void);
+
+extern const PCSYMCRYPT_MAC SymCryptKmac128Algorithm;
+
+//
+//  KMAC256
+//
+#define SYMCRYPT_KMAC256_RESULT_SIZE        SYMCRYPT_CSHAKE256_RESULT_SIZE
+#define SYMCRYPT_KMAC256_INPUT_BLOCK_SIZE   SYMCRYPT_CSHAKE256_INPUT_BLOCK_SIZE
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256(
+        _In_                                                PCSYMCRYPT_KMAC256_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbInput )                         PCBYTE  pbInput,
+                                                            SIZE_T  cbInput,
+        _Out_writes_bytes_( SYMCRYPT_KMAC256_RESULT_SIZE )  PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Ex(
+        _In_                            PCSYMCRYPT_KMAC256_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbInput )     PCBYTE  pbInput,
+                                        SIZE_T  cbInput,
+        _Out_writes_bytes_( cbResult )  PBYTE   pbResult,
+                                        SIZE_T  cbResult);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptKmac256ExpandKey(
+        _Out_                                       PSYMCRYPT_KMAC256_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+                                                    SIZE_T  cbKey);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptKmac256ExpandKeyEx(
+        _Out_                                       PSYMCRYPT_KMAC256_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+                                                    SIZE_T  cbKey,
+        _In_reads_bytes_( cbCustomizationString )   PCBYTE  pbCustomizationString,
+                                                    SIZE_T  cbCustomizationString);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Init(
+    _Out_   PSYMCRYPT_KMAC256_STATE         pState,
+    _In_    PCSYMCRYPT_KMAC256_EXPANDED_KEY pExpandedKey);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Append(
+    _Inout_                 PSYMCRYPT_KMAC256_STATE pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Extract(
+    _Inout_                     PSYMCRYPT_KMAC256_STATE pState,
+    _Out_writes_( cbOutput )    PBYTE                   pbOutput,
+                                SIZE_T                  cbOutput,
+                                BOOLEAN                 bWipe);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Result(
+    _Inout_                                         PSYMCRYPT_KMAC256_STATE pState,
+    _Out_writes_( SYMCRYPT_KMAC256_RESULT_SIZE )    PBYTE   pbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256ResultEx(
+    _Inout_                     PSYMCRYPT_KMAC256_STATE pState,
+    _Out_writes_( cbResult )    PBYTE                   pbResult,
+                                SIZE_T                  cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256KeyCopy(_In_ PCSYMCRYPT_KMAC256_EXPANDED_KEY pSrc, _Out_ PSYMCRYPT_KMAC256_EXPANDED_KEY pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256StateCopy(_In_ const SYMCRYPT_KMAC256_STATE* pSrc, _Out_ SYMCRYPT_KMAC256_STATE* pDst);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Selftest(void);
+
+extern const PCSYMCRYPT_MAC SymCryptKmac256Algorithm;
+
+
+////////////////////////////////////////////////////////////////////////////
+// POLY1305
+//
+// Poly1305 is different from other MAC functions because a key can only
+// be used safely for a single message.
+// We do not follow the default API pattern for MAC functions as that invites
+// callers to compute multiple MACs per key.
+//
+
+#define SYMCRYPT_POLY1305_RESULT_SIZE   (16)
+#define SYMCRYPT_POLY1305_BLOCK_SIZE    (16)
+#define SYMCRYPT_POLY1305_KEY_SIZE      (32)
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305(
+    _In_reads_( SYMCRYPT_POLY1305_KEY_SIZE )        PCBYTE  pbKey,
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE )   PBYTE   pbResult );
+// Compute a Poly1305 authentication with the provided key on the data buffer.
+// Note: A Poly1305 key may only be used for a single message.
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Init(
+    _Out_                                       PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_( SYMCRYPT_POLY1305_KEY_SIZE )    PCBYTE                      pbKey );
+// Starts an incremental Poly1305 computation.
+// Note: A Poly1305 key may only be used for a single message.
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Append(
+    _Inout_                 PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Result(
+    _Inout_                                         PSYMCRYPT_POLY1305_STATE    pState,
+    _Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE )   PBYTE                       pbResult );
+// The state is wiped and not suitable for re-use.
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Selftest(void);
+
+//
+// We do NOT define a SYMCRYPT_MAC structure SymCryptPoly1305Algorithm
+// for Poly1305 as it is a 1-time MAC function and cannot safely be used
+// by any KDF we have
+//
+// NOT DEFINED: extern const PCSYMCRYPT_MAC SymCryptPoly1305Algorithm;
+//
+
+////////////////////////////////////////////////////////////////////////////
+// CHACHA20_POLY1305
+//
+// This algorithm combines the CHACHA20 symmetric key stream cipher with
+// the POLY1305 MAC function as per RFC 8439.
+// The POLY1305 authenticator key is generated from the first 32 bytes
+// of the CHACHA20 keystream and is only valid for a single message.
+// For this reason each key and nonce combination passed to
+// SymCryptChaCha20Poly1305Encrypt MUST only be used once.
+//
+// The Src and Dst buffers can be identical or non-overlapping; partial overlaps
+// are not supported.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305Encrypt(
+    _In_reads_( cbKey )             PCBYTE    pbKey,
+                                    SIZE_T    cbKey,      // Required. Key size MUST be 32 bytes.
+    _In_reads_( cbNonce )           PCBYTE    pbNonce,
+                                    SIZE_T    cbNonce,    // Required. Nonce size MUST be 12 bytes.
+    _In_reads_opt_( cbAuthData )    PCBYTE    pbAuthData,
+                                    SIZE_T    cbAuthData, // Optional. Can be any size.
+    _In_reads_( cbData )            PCBYTE    pbSrc,
+    _Out_writes_( cbData )          PBYTE     pbDst,
+                                    SIZE_T    cbData,     // Required. Max size is 274,877,906,880 bytes.
+    _Out_writes_( cbTag )           PBYTE     pbTag,
+                                    SIZE_T    cbTag );    // Required. Tag size MUST be 16 bytes.
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305Decrypt(
+    _In_reads_( cbKey )             PCBYTE    pbKey,
+                                    SIZE_T    cbKey,      // Required. Key size MUST be 32 bytes.
+    _In_reads_( cbNonce )           PCBYTE    pbNonce,
+                                    SIZE_T    cbNonce,    // Required. Nonce size MUST be 12 bytes.
+    _In_reads_opt_( cbAuthData )    PCBYTE    pbAuthData,
+                                    SIZE_T    cbAuthData, // Optional. Can be any size.
+    _In_reads_( cbData )            PCBYTE    pbSrc,
+    _Out_writes_( cbData )          PBYTE     pbDst,
+                                    SIZE_T    cbData,     // Required. Max size is 274,877,906,880 bytes.
+    _In_reads_( cbTag )             PCBYTE    pbTag,
+                                    SIZE_T    cbTag );    // Required. Tag size MUST be 16 bytes.
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305Selftest(void);
+
+////////////////////////////////////////////////////////////////////////////
+//   MARVIN32
+//
+// Marvin is a checksum function optimized for speed on small inputs.
+// IT IS NOT A CRYPTOGRAPHIC HASH FUNCTION.
+// Marvin lacks the security properties of a cryptographic hash function.
+// DO NOT USE FOR ANY SECURITY USE.
+//
+// A randomizable checksum function has essentially the same API as a MAC
+// function. We use the SymCrypt MAC API here, with the difference
+// that we use the word 'seed' rather than 'key'.
+//
+// See the description above of the generic MAC API for details on how
+// these functions are used. Wherever the MAC API talks about keys, this
+// applies to the seed for Marvin32.
+//
+// The randomization is useful for hash tables.
+// There are DOS attacks where an attacker generates many inputs that
+// hash to the same location in the hash table. Some hash table implementations
+// then use O(n^2) CPU time, allowing a DOS attack.
+// The randomization provided by the seed avoids this attack if:
+// - The seed is unpredictable and unknown to the attacker.
+// - The attacker cannot learn information about the output of the checksum function.
+// In particular, if an attacker can measure how long it takes to add each
+// element in a hash table, they might be able to determine enough information about
+// the output of the checksum function to recover the seed. Of course,
+// once that is done the DOS attack is once again possible.
+//
+// SymCrypt provides a default seed for applications that don't need a seed.
+//
+// FUTURE IMPROVEMENTS:
+// At the moment it is relatively expensive to change the seed.
+// If needed, we can add a facility to modify the seed faster than
+// re-running the ExpandSeed function.
+//
+
+#define SYMCRYPT_MARVIN32_RESULT_SIZE       (8)
+#define SYMCRYPT_MARVIN32_SEED_SIZE         (8)
+#define SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE  (4)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMarvin32ExpandSeed(
+    _Out_               PSYMCRYPT_MARVIN32_EXPANDED_SEED    pExpandedSeed,
+    _In_reads_(cbSeed)  PCBYTE                              pbSeed,
+                        SIZE_T                              cbSeed );
+//
+// The seed must be 8 bytes (= SYMCRYPT_MARVIN32_SEED_SIZE).
+// Use of the all-zero seed is not recommended as it has some undesirable properties.
+// Note that a pre-expanded default seed is provided for applications that do not wish to control
+// their seed. Such applications do not need to call SymCryptMarvin32ExpandSeed
+//
+
+extern PCSYMCRYPT_MARVIN32_EXPANDED_SEED const SymCryptMarvin32DefaultSeed;
+
+PCSYMCRYPT_MARVIN32_EXPANDED_SEED
+SYMCRYPT_CALL
+SymCryptGetMarvin32DefaultSeed( void );
+//
+// Returns a pointer to the default Marvin32 seed.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32SeedCopy(   _In_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pSrc,
+                            _Out_   PSYMCRYPT_MARVIN32_EXPANDED_SEED    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32(
+    _In_                                            PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE )   PBYTE                               pbResult );
+//
+// If the application does not wish to use a seed, a default expanded seed is provided.
+// Callers can pass SymCryptMarvin32DefaultSeed as the first argument.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32StateCopy(
+    _In_        PCSYMCRYPT_MARVIN32_STATE           pSrc,
+    _In_opt_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed,
+    _Out_       PSYMCRYPT_MARVIN32_STATE            pDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Init(   _Out_   PSYMCRYPT_MARVIN32_STATE            pState,
+                        _In_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed);
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Append(     _Inout_                 PSYMCRYPT_MARVIN32_STATE    pState,
+                            _In_reads_( cbData )    PCBYTE                      pbData,
+                                                    SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Result(
+     _Inout_                                        PSYMCRYPT_MARVIN32_STATE    pState,
+     _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE )  PBYTE                       pbResult );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Selftest(void);
+
+
+//==========================================================================
+//   BLOCK CIPHERS
+//==========================================================================
+//
+// For a block cipher XXX the following minimal functions, types, and constants are defined:
+//
+// SYMCRYPT_XXX_BLOCK_SIZE
+//
+//      A constant giving is the block size, in bytes, of the algorithm.
+//
+//
+// SYMCRYPT_XXX_EXPANDED_KEY
+//      Type which contains a key with all the pre-computations performed.
+//      This is an opaque type whose structure can change at will.
+//      It should only be used for transient computations in a single executable
+//      and not be stored or transferred to a different environment.
+//      The pointer and const-pointer versions are also declared
+//      (PSYMCRYPOT_XXX_EXPANDED_KEY and PCSYMCRYPT_XXX_EXPANDED_KEY).
+//
+//      The EXPANDED_KEY structure contains keying material and should be wiped
+//      once it is no longer used. (See SymCryptWipe & SymCryptWipeKnownSize)
+//
+//      Once initialized, multiple threads can use the same expanded key object simultaneously
+//      for different block cipher computations as the expanded key is not modified once initialized.
+//
+// SymCryptXxxBlockCipher
+//      A SYMCRYPT_BLOCKCIPHER structure that provides a description
+//      of the block cipher and its primary functions. This is used by cipher modes to pass
+//      all the block-cipher specific information in a single structure.
+//
+//
+// SYMCRYPT_ERROR
+// SYMCRYPT_CALL
+// SymCryptXxxExpandKey(    _Out_               PSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//                          _In_reads_(cbKey)   PCBYTE                     pbKey,
+//                                              SIZE_T                     cbKey );
+//
+//      Prepare a key for future use by the Xxx algorithm.
+//      This function performs pre-computations on the key
+//      to speed up the actual block cipher computations later, and stores the result as an expanded key.
+//      The expanded key must be kept unchanged until all computations that use the key are finished.
+//      When the key is no longer needed the expanded key structure should be wiped.
+//
+//      Different algorithms pose different requirements on the length of the key.
+//      If the key that is provided is of an unsupported length the SYMCRYPT_WRONG_KEY_SIZE error is returned.
+//      In this case the expanded key structure will not contain any keying material and does not have to be wiped.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxEncrypt(  _In_                                    PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//                      _In_reads_( SYMCRYPT_XXX_BLOCK_SIZE )   PCBYTE                      pbSrc,
+//                      _Out_writes_( SYMCRYPT_XXX_BLOCK_SIZE ) PBYTE                       pbDst );
+//
+//      Encrypt a single block.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxDecrypt(  _In_                                    PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//                      _In_reads_( SYMCRYPT_XXX_BLOCK_SIZE )   PCBYTE                      pbSrc,
+//                      _Out_writes_( SYMCRYPT_XXX_BLOCK_SIZE ) PBYTE                       pbDst );
+//
+//      Decrypt a single block.
+//
+//
+// --------------------------------------------------------------------------------------------------------------
+//      In addition to these elementary encrypt block/decrypt block functions a block cipher may also implement
+//      optimized versions of CBC encryption, CBC decryption, CBC-MAC, and CTR encryption. Not all block ciphers
+//      do implement these.
+//      All block cipher modes are always available through the generic block cipher mode functions.
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxCbcEncrypt(
+//      _In_                                        PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//      _Inout_updates_( SYMCRYPT_XXX_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//      _In_reads_( cbData )                        PCBYTE                      pbSrc,
+//      _Out_writes_( cbData )                      PBYTE                       pbDst,
+//                                                  SIZE_T                      cbData );
+//
+//      Encrypt data using the CBC chaining mode.
+//      On entry the pbChainingValue is the IV which is xorred into the first plaintext block of the CBC encryption.
+//      On exit the pbChainingValue is updated to the last ciphertext block of the result.
+//      This allows a longer CBC encryption to be done incrementally.
+//
+//      cbData must be a multiple of the block size. For efficiency reasons this routine does not return an error
+//      if cbData is not a proper multiple; instead the result is undefined. The routine might hang,
+//      round cbData down to a multiple of the block size, or return random data that cannot be decrypted.
+//
+//      The pbSrc and pbDst buffers may be the same, or they may be non-overlapping. However, they may
+//      not be partially overlapping.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxCbcDecrypt(
+//      _In_                                        PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//      _Inout_updates_( SYMCRYPT_XXX_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//      _In_reads_( cbData )                        PCBYTE                      pbSrc,
+//      _Out_writes_( cbData )                      PBYTE                       pbDst,
+//                                                  SIZE_T                      cbData );
+//
+//      Decrypt data using the CBC chaining mode.
+//      On entry the pbChainingValue is the IV to be xorred into the first plaintext block of the CBC decryption.
+//      On exit the pbChainingValue is updated to the last ciphertext block of the input.
+//      This allows a longer CBC decryption to be done incrementally.
+//
+//      cbData must be a multiple of the block size. For efficiency reasons this routine does not return an error
+//      if cbData is not a proper multiple; instead the result is undefined. The routine might hang,
+//      round cbData down to a multiple of the block size, or return random data.
+//
+//      The pbSrc and pbDst buffers may be the same, or they may be non-overlapping. However, they may
+//      not be partially overlapping.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxCbcMac(
+//      _In_                                        PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//      _Inout_updates_( SYMCRYPT_XXX_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//      _In_reads_( cbData )                        PCBYTE                      pbData,
+//                                                  SIZE_T                      cbData );
+//
+//      Compute a CBC-MAC on the input data.
+//      On entry the pbChainingValue is the current chaining state of the CBC-MAC computation; this routine
+//      updates the state to reflect the chaining state after MACing the data.
+//      cbData must be a multiple of the block size.
+//      This function is NOT intended for general use; rather it is a high-performance primitive to support
+//      implementations of other cipher modes like CCM and CMAC.
+//      Note: If a key is used for CBC-MAC computations it should NOT be used for any encryptions.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxCtrMsb64(
+//      _In_                                        PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+//      _Inout_updates_( SYMCRYPT_XXX_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//      _In_reads_( cbData )                        PCBYTE                      pbSrc,
+//      _Out_writes_( cbData )                      PBYTE                       pbDst,
+//                                                  SIZE_T                      cbData );
+//
+//      Perform a CTR encryption on the data. (Note: CTR encryption and decryption are the same operation.)
+//      On entry pbChainingValue contains the first counter value to be used. On exit it contains
+//      the next counter value to be used.
+//      The increment function treats the last 8 bytes of the pbChainingValue string as an integer
+//      in most-significant-byte-first format, and increments this integer.
+//      Thus, the last byte is incremented the fastest.
+//      The pbSrc and pbDst buffers may be identical or non-overlapping, but they may not partially overlap.
+//      cbData must be a multiple of the block size.
+//
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxSelftest(void);
+//
+//      Perform a minimal self-test on the XXX algorithm.
+//      This function is designed to be used for achieving FIPS 140-2 compliance or
+//      to provide a simple self-test when an application starts.
+//
+//      If an error is detected the fatal callback routine is called.
+//
+//      We do not provide self-tests for the various cipher modes. There are too many
+//      (block cipher, key size, cipher mode) combinations and CNG performs the self tests
+//      on the outside APIs, not on the internal APIs.
+//      We retain a self test on the basic algorithm to help internal library testing.
+
+
+
+////////////////////////////////////////////////////////////////////////////
+//   AES
+//
+// The AES block cipher per FIPS 197
+//
+// WARNING:
+// Unless this code is running on a CPU with AES-NI instructions,
+// the AES implementation makes extensive use of table lookups to implement the S-boxes of the algorithm.
+// This violates our current crypto implementation guidelines and opens up a possible side-channel attack
+// through information leakage via the memory caching system of the CPU.
+//
+// Unfortunately there is no known software fix for this that does not lead to an order of magnitude performance loss.
+// An implementation that is 10x slower will not be used by anybody and is useless, so we implement a fast
+// version that uses table lookups. (Just like all other systems we know of.)
+//
+// The risk of this type of side-channel attack is limited as it requires malicious code to run on the same
+// machine as the code being attacked.
+//
+// At the time of writing (Apr 2007) there are no approved alternative encryption algorithms that do not
+// use table lookups. NIST and NSA are aware of this problem, but so far we have not seen any indication
+// that they consider this important enough to create an alternative encryption algorithm that does not
+// rely on table lookups as much.
+//
+
+#define SYMCRYPT_AES_BLOCK_SIZE  (16)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesExpandKey(
+    _Out_               PSYMCRYPT_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey );
+
+//
+// The SymCryptAesExpandKeyEncryptOnly creates an AES-expanded key that can ONLY be used
+// for AES encryption operations. There are no safeguards when you use it for decryption; you get the wrong
+// result if you try.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesExpandKeyEncryptOnly(
+    _Out_               PSYMCRYPT_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesKeyCopy( _In_ PCSYMCRYPT_AES_EXPANDED_KEY pSrc,
+                    _Out_ PSYMCRYPT_AES_EXPANDED_KEY pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncrypt(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecrypt(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbDecrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcMac(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb64(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+//
+// There are many optimized implementations for various AES modes.
+// To test them all would pull in all the code for these modes.
+// We solve this by letting the caller specify a bitmask of modes to be tested.
+// Under the following circumstances this will avoid pulling in unnecessary code:
+// - The argument is a compile-time constant.
+// - The compiler implements the usual constant propagation optimizations.
+//
+// Note: GCM, CCM, and XTS are NOT tested by this function.
+
+#define SYMCRYPT_AES_SELFTEST_BASE      0x01        // tests AesEncrypt & AesDecrypt
+#define SYMCRYPT_AES_SELFTEST_ECB       0x02        // ECB mode
+#define SYMCRYPT_AES_SELFTEST_CBC       0x04        // CBC mode
+#define SYMCRYPT_AES_SELFTEST_CBCMAC    0x08        // CBC-mac
+#define SYMCRYPT_AES_SELFTEST_CTR       0x10        // all CTR modes
+
+#define SYMCRYPT_AES_SELFTEST_ALL       0x1f
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesSelftest( UINT32 maskTestsToRun );
+
+extern const PCSYMCRYPT_BLOCKCIPHER SymCryptAesBlockCipher;
+
+
+////////////////////////////////////////////////////////////////////////////
+//   DES
+//
+// The DES block cipher per FIPS-46-3
+//
+// WARNING:
+// DES is no longer considered secure and should not be used.
+// Per the Crypto SDL, any use of DES in Microsoft code requires a Crypto board exemption
+//
+// The DES implementation makes extensive use of table lookups to implement the S-boxes of the algorithm.
+// This violates our current crypto implementation guidelines and opens up a possible side-channel attack
+// through information leakage via the memory caching system of the CPU.
+//
+
+#define SYMCRYPT_DES_BLOCK_SIZE  (8)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDesExpandKey(
+    _Out_               PSYMCRYPT_DES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey );
+//
+// The key must be 8 bytes long. The parity bits in the key are ignored and can be any value.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesEncrypt(
+    _In_                                    PCSYMCRYPT_DES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_DES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_DES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesDecrypt(
+    _In_                                    PCSYMCRYPT_DES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_DES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_DES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesSetOddParity(
+    _Inout_updates_( cbData ) PBYTE   pbData,
+    _In_                            SIZE_T  cbData );
+//
+// Set each byte to have odd parity by possibly flipping bit 0.
+// This is the parity used by DES, and is needed for compatibility.
+// The parity bit is ignored by the DES key expansion.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesSelftest(void);
+
+extern const PCSYMCRYPT_BLOCKCIPHER SymCryptDesBlockCipher;
+
+////////////////////////////////////////////////////////////////////////////
+//   3DES
+//
+// The triple-DES block cipher
+//
+// WARNING:
+// The DES implementation makes extensive use of table lookups to implement the S-boxes of the algorithm.
+// This violates our current crypto implementation guidelines and opens up a possible side-channel attack
+// through information leakage via the memory caching system of the CPU.
+//
+
+#define SYMCRYPT_3DES_BLOCK_SIZE  (8)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCrypt3DesExpandKey(
+    _Out_               PSYMCRYPT_3DES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey );
+//
+// If the provided key is 24 bytes long this expands a 3-key 3DES key. If 16 bytes are provided it
+// expands a 2-key 3DES. If 8 bytes are provided it creates the 3-key equivalent of the single
+// key des encryption. The parity bits in the key are ignored.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesEncrypt(
+    _In_                                    PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_3DES_BLOCK_SIZE )  PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_3DES_BLOCK_SIZE )PBYTE                           pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesDecrypt(
+    _In_                                    PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_3DES_BLOCK_SIZE )  PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_3DES_BLOCK_SIZE )PBYTE                           pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesCbcEncrypt(
+    _In_                                        PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _Inout_updates_( SYMCRYPT_3DES_BLOCK_SIZE ) PBYTE                           pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                      PBYTE                           pbDst,
+                                                SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesCbcDecrypt(
+    _In_                                        PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _Inout_updates_( SYMCRYPT_3DES_BLOCK_SIZE ) PBYTE                           pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                      PBYTE                           pbDst,
+                                                SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesSelftest(void);
+
+extern const PCSYMCRYPT_BLOCKCIPHER SymCrypt3DesBlockCipher;
+
+////////////////////////////////////////////////////////////////////////////
+//   DESX
+//
+// The DESX block cipher.
+//
+// Use of DESX is not recommended.
+//
+
+#define SYMCRYPT_DESX_BLOCK_SIZE  (8)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDesxExpandKey(
+    _Out_               PSYMCRYPT_DESX_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesxEncrypt(
+    _In_                                    PCSYMCRYPT_DESX_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_DESX_BLOCK_SIZE )  PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_DESX_BLOCK_SIZE )PBYTE                           pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesxDecrypt(
+    _In_                                    PCSYMCRYPT_DESX_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_DESX_BLOCK_SIZE )  PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_DESX_BLOCK_SIZE )PBYTE                           pbDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesxSelftest(void);
+
+extern const PCSYMCRYPT_BLOCKCIPHER SymCryptDesxBlockCipher;
+
+////////////////////////////////////////////////////////////////////////////
+//   RC2
+//
+// The RC2 block cipher
+//
+// WARNING:
+// Use of RC2 is not recommended for many reasons.
+//
+// The RC2 implementation makes extensive use of table lookups to implement the S-boxes of the algorithm.
+// This violates our current crypto implementation guidelines and opens up a possible side-channel attack
+// through information leakage via the memory caching system of the CPU.
+//
+
+#define SYMCRYPT_RC2_BLOCK_SIZE  (8)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRc2ExpandKey(
+    _Out_               PSYMCRYPT_RC2_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey );
+//
+// The default effective key size is 8*cbKey. Note that this is NOT the default used in
+// the old RSA32 library which used a default effective key size of 40 bits.
+// That is too dangerous a default to implement. We chose 8*cbKey rather than 1024 as
+// our choice provides slightly better mixing of the key bytes into the expanded key.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRc2ExpandKeyEx(
+    _Out_               PSYMCRYPT_RC2_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey,
+                        UINT32                      effectiveKeySizeInBits );
+//
+// Rc2 has an option to limit the effective key size, which means the key expansion function has an extra
+// parameter.
+//
+// The effective key size in bits may be any value from 9..1024. If it is larger than 8*cbKey it does
+// not significantly affect the key strength. However, the expanded key will always depend on the
+// effective key size; expanding the same string of key bytes with differ effective key sizes leads
+// to different expanded keys and different encryption functions.
+//
+// The original default was an effective key size of 40 bits.
+//
+// Do not allow your attacker to choose the effective key size. RC2 seems vulnerable to
+// related-effective-key-size attacks.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc2Encrypt(
+    _In_                                    PCSYMCRYPT_RC2_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_RC2_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_RC2_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc2Decrypt(
+    _In_                                    PCSYMCRYPT_RC2_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_RC2_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_RC2_BLOCK_SIZE ) PBYTE                       pbDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc2Selftest(void);
+
+extern const PCSYMCRYPT_BLOCKCIPHER SymCryptRc2BlockCipher;
+
+
+//==========================================================================
+//   BLOCK CIPHER MODES
+//==========================================================================
+//
+// Block cipher modes use the block cipher description tables to implement
+// the various modes in a block-cipher independent way.
+//
+// Some block ciphers implement optimized versions of the block cipher modes.
+// These functions call that optimized version, but calling the block-cipher specific
+// function has less overhead.
+//
+// Note that these functions will only work with SymCrypt-provided block ciphers.
+// They are not designed to be used with externally provided block ciphers.
+// (The SYMCRYPT_BLOCKCIPHER structure is a private one not available to callers.)
+//
+
+typedef enum _SYMCRYPT_BLOCKCIPHER_ID
+{
+    SYMCRYPT_BLOCKCIPHER_ID_NULL        = 0,
+    SYMCRYPT_BLOCKCIPHER_ID_AES         = 1,
+    SYMCRYPT_BLOCKCIPHER_ID_DES         = 2,
+    SYMCRYPT_BLOCKCIPHER_ID_3DES        = 3,
+    SYMCRYPT_BLOCKCIPHER_ID_DESX        = 4,
+    SYMCRYPT_BLOCKCIPHER_ID_RC2         = 5
+} SYMCRYPT_BLOCKCIPHER_ID;
+
+PCSYMCRYPT_BLOCKCIPHER
+SYMCRYPT_CALL
+SymCryptGetBlockCipher( SYMCRYPT_BLOCKCIPHER_ID blockCipherId );
+//
+// Returns a pointer to the block cipher structure for the specified block cipher ID.
+// Returns NULL if the block cipher ID is invalid.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcbEncrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+//
+// Generic ECB encryption routine for block ciphers.
+//
+// - pBlockCipher is a pointer to the block cipher description table.
+//      Suitable description tables for all ciphers in this library have been pre-defined.
+// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there
+//      is no type safety to ensure that the expanded key and the encryption function match.
+// - pbSrc is the plaintext input buffer. The plaintext and ciphertext buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbData. Number of bytes to encrypt. This must be a multiple of the block size.
+// - pbDst is the result buffer. It may be identical to pbPlaintext or non-overlapping,
+//      but it may not partially overlap with the pbPlaintext buffer.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcbDecrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+//
+// Generic ECB decryption routine for block ciphers.
+//
+// - pBlockCipher is a pointer to the block cipher description table.
+//      Suitable description tables for all ciphers in this library have been pre-defined.
+// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there
+//      is no type safety to ensure that the expanded key and the encryption function match.
+// - pbSrc is the plaintext input buffer. The plaintext and ciphertext buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbData. Number of bytes to encrypt. This must be a multiple of the block size.
+// - pbDst is the result buffer. It may be identical to pbPlaintext or non-overlapping,
+//      but it may not partially overlap with the pbPlaintext buffer.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCbcEncrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+
+//
+// Generic CBC encryption routine for block ciphers.
+//
+// - pBlockCipher is a pointer to the block cipher description table.
+//      Suitable description tables for all ciphers in this library have been pre-defined.
+// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there
+//      is no type safety to ensure that the expanded key and the encryption function match.
+// - pbChainingValue points to the chaining value. On entry it is the IV value for the CBC
+//      encryption, on return it is the last ciphertext block. A long message can be encrypted
+//      piecewise in multiple calls; at the end of one call the pbChainingValue buffer will contain
+//      the correct chaining value for encrypting the next piece of the message.
+//      Once the encryption is finished the value in the chaining buffer is no longer needed.
+// - pbSrc is the plaintext input buffer. The plaintext and ciphertext buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbData. Number of bytes to encrypt. This must be a multiple of the block size.
+// - pbDst is the result buffer. It may be identical to pbPlaintext or non-overlapping,
+//      but it may not partially overlap with the pbPlaintext buffer.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCbcDecrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+
+//
+// This is the decryption version of SymCryptCbcEncrypt.
+// All parameters have the same explanation and restrictions.:
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCbcMac(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+                                SIZE_T                  cbData );
+//
+// This function implements the same function as SymCryptCbcEncrypt except that
+// it does not produce a ciphertext output.
+// All other restrictions apply.
+// The pbChainingValue is the only output provided.
+//
+// This is the primitive operation used by other modes of operation,
+// and some platforms have special optimizations for this primitive.
+// As we expose special APIs for some algorithms, we provide the generic function so that it
+// can be used for all algorithms.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCtrMsb64(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+//
+// This function implements the CTR cipher mode.
+// It is not intended to be used as-is, rather it is a building block for modes like CCM.
+// On some platforms we have optimized code for AES-CTR, on other platforms
+// we use this generic construction to achieve the same effect.
+//
+// Note that in CTR mode encryption and decryption are the same operation.
+//
+// - pBlockCipher is a pointer to the block cipher description table.
+//      Suitable description tables for all ciphers in this library have been pre-defined.
+// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there
+//      is no type safety to ensure that the expanded key and the encryption function match.
+// - pbChainingValue points to the chaining value. On entry it is the first counter value to be
+//      used. On exit is the next counter value to be used.
+//      The pbChainingValue is incremented by cbData/blockSize.
+//      The increment function treats the last 8 bytes of pbChaining a MSBfirst integer
+//      and increments the integer representation by one for each block.
+// - pbSrc is the input data buffer that will be encrypted/decrypted.
+// - cbData. Number of bytes to encrypt/decrypt. This must be a multiple of the block size.
+// - pbDst is the output buffer that receives the encrypted/decrypted data. The input and output
+//      buffers may be the same or non-overlapping, but may not partially overlap.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCfbEncrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+                                SIZE_T                  cbShift,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+//
+// Encrypt a buffer using the CFB cipher mode.
+//
+// This implements the CFB mode, with selected shift amount (in bytes).
+// In general, one block cipher encryption is used for each cbShift bytes
+// of plaintext, which can be slow.
+// Use of this cipher mode is not recommended.
+//
+// - pBlockCipher is a pointer to the block cipher description table.
+//      Suitable description tables for all ciphers in this library have been pre-defined.
+// - cbShift is the shift value (in bytes) of the CFB mode.
+//      The only supported values are 1 and the block size.
+// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there
+//      is no type safety to ensure that the expanded key and the encryption function match.
+// - pbChainingValue points to the chaining value. On entry and exit it
+//      contains the last blockSize ciphertext bytes.
+// - pbSrc is the input data buffer that will be encrypted/decrypted.
+// - cbData. Number of bytes to encrypt/decrypt.
+//      Must be a multiple of cbShift, or a multiple of the block size if cbShift = 0.
+// - pbDst is the output buffer that receives the encrypted/decrypted data. The input and output
+//      buffers may be the same or non-overlapping, but may not partially overlap.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCfbDecrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+                                SIZE_T                  cbShift,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+//
+// The corresponding decryption routine.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptPaddingPkcs7Add(
+                                            SIZE_T  cbBlockSize,
+    _In_reads_(cbSrc)                       PCBYTE  pbSrc,
+                                            SIZE_T  cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)      PBYTE   pbDst,
+                                            SIZE_T  cbDst,
+                                            SIZE_T* pcbResult);
+//
+// Prerequisites:
+//  cbBlockSize is a power of 2 and < 256
+//  cbDst >= cbSrc - cbSrc % cbBlockSize + cbBlockSize
+//
+// Add PKCS7 block padding to a message
+// The input data (pbSrc,cbSrc) is padded with between 1 and cbBlockSize bytes so that
+// the length of the result is a multiple of cbBlockSize.
+// The padded message is written to the pbDst buffer.
+// The length of the padded message is returned in *pcbResult.
+//
+// If pbSrc == pbDst this function avoids copying all the data.
+// Note that cbSrc == cbDst is not valid as it violates the prerequisites.
+// Padding a message with cbSrc == 0 is valid.
+//
+// Note:
+// Any whole blocks in Src are merely copied to Dst.
+// Callers can either process the whole message in this call,
+// or handle the whole blocks themselves and only pass the last few bytes of the message to this function.
+//
+// Note: the prerequisites are not checked by this function; if they are not satisfied
+// the behaviour of the function is undefined.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPaddingPkcs7Remove(
+                                            SIZE_T  cbBlockSize,
+    _In_reads_(cbSrc)                       PCBYTE  pbSrc,
+                                            SIZE_T  cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)      PBYTE   pbDst,
+                                            SIZE_T  cbDst,
+                                            SIZE_T* pcbResult);
+//
+// Prerequisites:
+//  - cbBlockSize is a power of 2 and < 256
+//  - cbSrc is a multiple of cbBlockSize
+//  - cbSrc is greater than zero (at least equals to cbBlockSize)
+//
+// Remove PKCS7 block padding from a message in a side-channel safe way.
+//  *** see below for important rules the caller should follow w.r.t. side-channel safety ***
+// The input data (pbSrc, cbSrc) is a valid PKCS7 padded message for the given blocksize.
+// This function removes the padding, copies the result to the (pbDst, cbDst) buffer,
+// and returns the size of the result in *pcbResult.
+//
+// This function only supports padding with a size up to the block size.
+//
+// If pbSrc == pbDst this function avoids copying data.
+//
+// The following errors are returned:
+//  - SYMCRYPT_INVALID_ARGUMENT if cbSrc or the padding is invalid
+//  - SYMCRYPT_BUFFER_TOO_SMALL if cbDst < size of the unpadded message
+// If cbDst >= cbSrc the SYMCRYPT_BUFFER_TOO_SMALL error will not be returned.
+// Even if an error is returned, the pbDst buffer may or may not contain data from the message.
+// Callers should wipe the buffer even if an error is returned.
+//
+// Note: Removal of PKCS7 padding is extremely sensitive to side channels.
+// For example, if a message is encrypted with AES-CBC and the attacker can modify
+// the ciphertext and then determine whether a padding error occurs during decryption,
+// then the attacker can use the presence or absence of the error to decrypt the message itself.
+// This function takes great care not to reveal whether an error occurred, and hides
+// the size of the unpadded message. This is even true when writing to pbDst. If cbDst is large
+// enough, the code will write cbSrc-1 bytes to pbDst, using masking to only update the bytes of the
+// message and leaving the other bytes in pbDst unchanged.
+// Callers should take great care not to reveal the returned error or success,
+// or the size of the returned message, until they have authenticated
+// the source of the data.
+//
+// In particular, any mapping of the error code should be done in a side-channel safe way.
+// See the SymCryptMapUint32() function for a side-channel safe way to map error codes.
+//
+// The error caused by an invalid cbSrc value is not hidden from side channels as this does not reveal any
+// secret information.
+//
+// Note: callers can either process the whole message in this call,
+// or process the whole blocks themselves and only pass the last block to this function.
+
+////////////////////////////
+// CCM
+////////////////////////////
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCcmValidateParameters(
+    _In_    PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_    SIZE_T                  cbNonce,
+    _In_    SIZE_T                  cbAssociatedData,
+    _In_    UINT64                  cbData,
+    _In_    SIZE_T                  cbTag
+   );
+//
+// To achieve maximum performance, CCM functions do not check for valid parameters.
+// Passing invalid parameters can lead to buffer overflows.
+// Callers who want to validate their CCM parameters can call this function.
+// Note: In Checked builds some CCM functions might fatal out when invalid parameters are
+// passed.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncrypt(
+     _In_                           PCSYMCRYPT_BLOCKCIPHER     pBlockCipher,
+     _In_                           PCVOID                     pExpandedKey,
+     _In_reads_( cbNonce )          PCBYTE                     pbNonce,
+                                    SIZE_T                     cbNonce,
+     _In_reads_opt_( cbAuthData )   PCBYTE                     pbAuthData,
+                                    SIZE_T                     cbAuthData,
+     _In_reads_( cbData )           PCBYTE                     pbSrc,
+     _Out_writes_( cbData )         PBYTE                      pbDst,
+                                    SIZE_T                     cbData,
+     _Out_writes_( cbTag )          PBYTE                      pbTag,
+                                    SIZE_T                     cbTag );
+
+//
+//  Encrypt a buffer using the block cipher in CCM mode.
+//      - pBlockCipher points to the block cipher description table.
+//      - pExpandedKey points to the expanded key for the block cipher.
+//      - pbNonce: Pointer to the nonce for this encryption. For a single key, each nonce
+//          value may be used at most once to encrypt data. Re-using nonce values leads
+//          to catastrophic loss of security.
+//      - cbNonce: number of bytes in the nonce: 7 <= cbNonce <= 13.
+//      - pbAuthData: pointer to the associated authentication data. This data is not encrypted
+//          but it is included in the authentication. Use NULL if not used.
+//      - cbAuthData: # bytes of associated authentication data. (0 if not used)
+//      - pbSrc: plaintext input
+//      - pbDst: ciphertext output. The ciphertext buffer may be identical to the plaintext
+//          buffer, or non-overlapping. The ciphertext is also cbData bytes long.
+//      - cbData: # bytes of plaintext input. The maximum length is 2^{8(15-cbNonce)} - 1 bytes.
+//      - pbTag: buffer that will receive the authentication tag.
+//      - cbTag: size of tag. cbTag must be one of {4, 6, 8, 10, 12, 14, 16}.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCcmDecrypt(
+     _In_                           PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+     _In_                           PCVOID                  pExpandedKey,
+     _In_reads_( cbNonce )          PCBYTE                  pbNonce,
+                                    SIZE_T                  cbNonce,
+     _In_reads_opt_( cbAuthData )   PCBYTE                  pbAuthData,
+                                    SIZE_T                  cbAuthData,
+     _In_reads_( cbData )           PCBYTE                  pbSrc,
+     _Out_writes_( cbData )         PBYTE                   pbDst,
+                                    SIZE_T                  cbData,
+     _In_reads_( cbTag )            PCBYTE                  pbTag,
+                                    SIZE_T                  cbTag );
+//
+// Decrypt a buffer using the block cipher in CCM mode.
+// See SymCryptCcmEncrypt for a description of the parameters. This function decrypts rather than
+// encrypts, and as a result the pbTag parameter is read rather than filled.
+//
+// If the tag value is not correct the SYMCRYPT_AUTHENTICATION_FAILURE error is returned and the pbDst buffer
+// is wiped of any plaintext.
+// Note: While checking the authentication the purported plaintext is stored in pbDst. It is not safe to reveal
+// purported plaintext when the authentication has not been checked. (Doing so would reveal key stream information
+// that can be used to decrypt any message encrypted with the same nonce value.) Thus, users should be careful
+// to not reveal the pbDst buffer until this function returns (e.g. through other threads or sharing memory).
+//
+
+//
+// We also provide functions for incremental computation of CCM encryption and decryption. See the functions
+// above for a description of the parameters and restrictions.
+// In particular, note that the restriction on revealing the plaintext for unauthenticated decryptions holds
+// for all the decrypted data, even when the decryption is done incrementally.
+//
+// SYMCRYPT_CCM_STATE
+//      Ongoing state of an incremental CCM encryption or decryption operation.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmInit(
+    _Out_                           PSYMCRYPT_CCM_STATE     pState,
+    _In_                            PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                            PCVOID                  pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                  pbNonce,
+                                    SIZE_T                  cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                  pbAuthData,
+                                    SIZE_T                  cbAuthData,
+                                    UINT64                  cbData,
+                                    SIZE_T                  cbTag );
+//
+// Initialize a CCM computation. Note that the ultimate data length has to be provided.
+// The pBlockCipher and pExpandedKey structures must remain unchanged until the CCM computation is finished.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncryptPart(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncryptFinal(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _Out_writes_( cbTag )   PBYTE               pbTag,
+                            SIZE_T              cbTag );
+//
+// Note: passing cbTag is redundant but necessary for SAL purposes.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmDecryptPart(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCcmDecryptFinal(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbTag )     PCBYTE              pbTag,
+                            SIZE_T              cbTag );
+//
+// WARNING: When the authentication fails the data already decrypted may not be revealed.
+// This function cannot wipe the plaintext buffers; the caller is responsible for ensuring
+// the plaintext is not revealed.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmSelftest(void);
+//
+// Self test for CCM cipher mode
+//
+
+///////////////////////////////////////
+// GCM
+///////////////////////////////////////
+//
+// The GCM algorithm per SP 800-38D.
+// GMAC is just GCM with an empty data string; all the data is put in the pbAuthData buffer.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmValidateParameters(
+    _In_    PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_    SIZE_T                  cbNonce,
+    _In_    UINT64                  cbAssociatedData,
+    _In_    UINT64                  cbData,
+    _In_    SIZE_T                  cbTag
+   );
+//
+// To achieve maximum performance, GCM functions do not check for valid parameters.
+// Passing invalid parameters can lead to buffer overflows.
+// Callers who want to validate their GCM parameters can call this function.
+// Note: In Checked builds some GCM functions might fatal out when invalid parameters are
+// passed.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmExpandKey(
+    _Out_                   PSYMCRYPT_GCM_EXPANDED_KEY  pExpandedKey,
+    _In_                    PCSYMCRYPT_BLOCKCIPHER      pBlockCipher,
+    _In_reads_( cbKey )     PCBYTE                      pbKey,
+                            SIZE_T                      cbKey );
+//
+// Create an expanded key suitable for GCM
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmKeyCopy( _In_ PCSYMCRYPT_GCM_EXPANDED_KEY pSrc, _Out_ PSYMCRYPT_GCM_EXPANDED_KEY pDst );
+
+//
+// Create a copy of an expanded key
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncrypt(
+     _In_                           PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+     _In_reads_( cbNonce )          PCBYTE                      pbNonce,
+                                    SIZE_T                      cbNonce,
+     _In_reads_opt_( cbAuthData )   PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+     _In_reads_( cbData )           PCBYTE                      pbSrc,
+     _Out_writes_( cbData )         PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+     _Out_writes_( cbTag )          PBYTE                       pbTag,
+                                    SIZE_T                      cbTag );
+
+//
+//  Encrypt a buffer using the block cipher in GCM mode.
+//      - pExpandedKey points to the expanded key for GCM.
+//      - pbNonce: Pointer to the nonce for this encryption. For a single key, each nonce
+//          value may be used at most once to encrypt data. Re-using nonce values leads
+//          to catastrophic loss of security. Only 12-byte nonces are supported,
+//          per the SP800-38D section 5.2.1.1 recommendation.
+//      - cbNonce: number of bytes in the nonce, must be 12.
+//      - pbAuthData: pointer to the associated authentication data. This data is not encrypted
+//          but it is included in the authentication. Use NULL if not used.
+//      - cbAuthData: # bytes of associated authentication data. (0 if not used)
+//      - pbSrc: plaintext input
+//      - pbDst: ciphertext output. The ciphertext buffer may be identical to the plaintext
+//          buffer, or non-overlapping. The ciphertext is also cbData bytes long.
+//      - cbData: # bytes of plaintext input. The maximum length is 2^{36} - 32 bytes.
+//      - pbTag: buffer that will receive the authentication tag.
+//      - cbTag: size of tag. cbTag must be one of {12, 13, 14, 15, 16} per SP800-38D
+//          section 5.2.1.2. The optional shorter tag sizes (4 and 8) are not supported.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmDecrypt(
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                      pbNonce,
+                                    SIZE_T                      cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _In_reads_( cbTag )             PCBYTE                      pbTag,
+                                    SIZE_T                      cbTag );
+//
+// Decrypt a buffer using the block cipher in GCM mode.
+// See SymCryptGcmEncrypt for a description of the parameters. This function decrypts rather than
+// encrypts, and as a result the pbTag parameter is read rather than filled.
+// If the tag value is not correct the SYMCRYPT_AUTHENTICATION_FAILURE error is returned and the pbDst buffer
+// is wiped of any plaintext.
+// Note: While checking the authentication the purported plaintext is stored in pbDst. It is not safe to reveal
+// purported plaintext when the authentication has not been checked. (Doing so would reveal key stream information
+// that can be used to decrypt any message encrypted with the same nonce value.) Thus, users should be careful
+// to not reveal the pbDst buffer until this function returns (e.g. through other threads or sharing memory).
+//
+
+//
+// We also provide functions for incremental computation of GCM encryption and decryption. See the functions
+// above for a description of the parameters and restrictions.
+// In particular, note that the restriction on revealing the plaintext for unauthenticated decryptions holds
+// for all the decrypted data, even when the decryption is done incrementally.
+//
+//
+// SYMCRYPT_GCM_STATE
+//      Ongoing state of an incremental GCM encryption or decryption operation.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmInit(
+    _Out_                       PSYMCRYPT_GCM_STATE         pState,
+    _In_                        PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbNonce )       PCBYTE                      pbNonce,
+                                SIZE_T                      cbNonce );
+//
+// Initialize a GCM computation.
+// The pBlockCipher and pExpandedKey structures must remain unchanged until the GCM computation is finished.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmStateCopy(
+    _In_        PCSYMCRYPT_GCM_STATE            pSrc,
+    _In_opt_    PCSYMCRYPT_GCM_EXPANDED_KEY     pExpandedKeyCopy,
+    _Out_       PSYMCRYPT_GCM_STATE             pDst );
+//
+// Copy a GCM state.
+// If pExpandedKeyCopy is NULL, then the new pDst state uses the same expanded key as pSrc.
+// If pExpandedKeyCopy is not NULL, it must point to a copy of the expanded key of the pSrc state.
+// This new expanded key will be used as the expanded key for pDst.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmAuthPart(
+    _Inout_                     PSYMCRYPT_GCM_STATE pState,
+    _In_reads_opt_( cbData )    PCBYTE              pbAuthData,
+                                SIZE_T              cbData );
+//
+// Incrementally process the authentication data. This function can be called multiple times
+// after the SymCryptGcmInit function. It may not be called after any encrypt or decrypt
+// function has been called on the GCM state.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptFinal(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _Out_writes_( cbTag )   PBYTE               pbTag,
+                            SIZE_T              cbTag );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmDecryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmDecryptFinal(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbTag )     PCBYTE              pbTag,
+                            SIZE_T              cbTag );
+//
+// Returns SYMCRYPT_AUTHENTICATION_FAILURE if the tag value does not match.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmSelftest(void);
+//
+// Self test for GCM cipher mode
+//
+
+
+//==========================================================================
+//   SESSION BASED APIs
+//==========================================================================
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionSenderInit(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT32              senderId,
+            UINT32              flags );
+//
+//  Initialize an encryption session object. The default nonce size of 12B is used - 8B are provided
+//  by message number, 4B by senderId.
+//      - pSession: Pointer to an uninitialized session object.
+//      - senderId: The id of the sender (must be unique for each user of a given key).
+//        Callers should either choose a senderId which is specific to the sender, or
+//        at least to the software and role in a system in which a key is being used.
+//        Two encryption sessions using the same key and senderId leads to catastrophic loss of security.
+//      - No flags are specified for this function
+//
+//  Remarks:
+//  On some platforms use of a session object requires use of a mutex. On those platforms this
+//  function will call SymCryptCallbackAllocateMutexFastInproc and may indicate failure by returning
+//  SYMCRYPT_MEMORY_ALLOCATION_FAILURE if a mutex object cannot be created.
+//  Callers must call SymCryptSessionDestroy to ensure any associated allocated mutex object is freed
+//  either before calling another Init function on the SYMCRYPT_SESSION object, and instead of directly
+//  calling SymCryptWipeKnownSize on the object.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionReceiverInit(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT32              senderId,
+            UINT32              flags );
+//
+//  Initialize an decryption session object. The default nonce size of 12B is used - 8B are provided
+//  by message number, 4B by senderId.
+//      - pSession: Pointer to an uninitialized session object.
+//      - senderId: The id of the sender (must be unique for each user of a given key).
+//        Callers should either choose a senderId which is specific to the sender, or
+//        at least to the software and role in a system in which a key is being used.
+//        The id used in a decryption session must be the same as the id used in the corresponding
+//        encryption session (i.e. sender and receiver must agree upon a senderId for their
+//        communication session)
+//      - No flags are specified for this function
+//
+//  Remarks:
+//  On some platforms use of a session object requires use of a mutex. On those platforms this
+//  function will call SymCryptCallbackAllocateMutexFastInproc and may indicate failure by returning
+//  SYMCRYPT_MEMORY_ALLOCATION_FAILURE if a mutex object cannot be created.
+//  Callers must call SymCryptSessionDestroy to ensure any associated allocated mutex object is freed
+//  either before calling another Init function on the SYMCRYPT_SESSION object, and instead of directly
+//  calling SymCryptWipeKnownSize on the object.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptSessionDestroy(
+    _Inout_ PSYMCRYPT_SESSION   pSession );
+//
+//  Clear session object and free any data associated with the object (i.e. allocated locks)
+//  After this call the memory used for pSession is uninitialized and can be used for other purposes.
+//  Note that it is not safe to just wipe the memory of the session object as the session
+//  object contains pointers to other allocations.
+//  The only way to safely destroy a session is to use this function.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionGcmEncrypt(
+    _Inout_                         PSYMCRYPT_SESSION           pSession,
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _Out_writes_( cbTag )           PBYTE                       pbTag,
+                                    SIZE_T                      cbTag,
+    _Out_opt_                       PUINT64                     pu64MessageNumber );
+//
+//  Encrypt a buffer, in a series, using the block cipher in GCM mode.
+//      - pSession points to the session object for this series of GCM encryptions. It handles
+//        ensuring Nonce uniqueness across several encryption calls using the same key. The message
+//        number in the pSession object is atomically incremented by this call.
+//        If too many messages (2^64 - 2^32) have been encrypted with the same session object,
+//        SYMCRYPT_INVALID_ARGUMENT is returned and no encryption takes place. This should never
+//        occur in real use!
+//      - pExpandedKey points to the expanded key for GCM.
+//      - pbAuthData: pointer to the associated authentication data. This data is not encrypted
+//          but it is included in the authentication. Use NULL if not used.
+//      - cbAuthData: # bytes of associated authentication data. (0 if not used)
+//      - pbSrc: plaintext input
+//      - pbDst: ciphertext output. The ciphertext buffer may be identical to the plaintext
+//          buffer, or non-overlapping. The ciphertext is also cbData bytes long.
+//      - cbData: # bytes of plaintext input. The maximum length is 2^{36} - 32 bytes.
+//      - pbTag: buffer that will receive the authentication tag.
+//      - cbTag: size of tag. cbTag must be one of {12, 13, 14, 15, 16} per SP800-38D
+//          section 5.2.1.2. The optional shorter tag sizes (4 and 8) are not supported.
+//      - pu64MessageNumber: Optional message number output for this encryption. A unique message
+//          number is extracted from the pSession object, this output is set to the value used in
+//          the encryption. The first message number generated in a session will have the value 1,
+//          and subsequent message numbers will be taken by atomically incrementing the counter.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionGcmDecrypt(
+    _Inout_                         PSYMCRYPT_SESSION           pSession,
+                                    UINT64                      messageNumber,
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _In_reads_( cbTag )             PCBYTE                      pbTag,
+                                    SIZE_T                      cbTag );
+//
+// Decrypt a buffer, in a series, using the block cipher in GCM mode.
+//      - pSession points to the session object for this series of GCM decryptions. It handles
+//          ensuring Nonce uniqueness across several decryption calls using the same key, particularly
+//          ensuring there are no replays.
+//      - messageNumber: The message number to be used for this decryption, forming part of the Nonce.
+//          When performing decryption in a session, it is guaranteed that no 2 decryptions using the
+//          same session and same message number can succeed. This is to provide protection against
+//          replay attacks.
+//          In order to provide this guarantee, pSession tracks a window of used message numbers
+//          preceding the largest messageNumber successfully used so far in the decryption session.
+//          A SYMCRYPT_SESSION_REPLAY_FAILURE error will be returned if either:
+//          a) messageNumber is less than the smallest message number that can be tracked for replays
+//          b) messageNumber is within the window that can be tracked for replays, and the message
+//             number is marked as already having been used in a successful decryption in this session
+//          In either case, the destination buffer is wiped.
+// See SymCryptSessionGcmEncrypt for a description of the other parameters. This function decrypts
+// rather than encrypts, and as a result the pbTag parameter is read rather than filled.
+// If the tag value is not correct the SYMCRYPT_AUTHENTICATION_FAILURE error is returned and the
+// pbDst buffer is wiped of any plaintext.
+// Note: While checking the authentication the purported plaintext is stored in pbDst. It is not safe to reveal
+// purported plaintext when the authentication has not been checked. (Doing so would reveal key stream information
+// that can be used to decrypt any message encrypted with the same nonce value.) Thus, users should be careful
+// to not reveal the pbDst buffer until this function returns (e.g. through other threads or sharing memory).
+//
+
+
+//==========================================================================
+//   STREAM CIPHERS
+//==========================================================================
+
+////////////////////////////////////////////////////////////////////////////
+//   RC4
+//
+// The RC4 stream cipher
+//
+// Use of RC4 is not recommended.
+//
+// The RC4 implementation makes extensive use of table lookups to implement the S-boxes of the algorithm.
+// This violates our current crypto implementation guidelines and opens up a possible side-channel attack
+// through information leakage via the memory caching system of the CPU.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRc4Init(
+    _Out_                   PSYMCRYPT_RC4_STATE pState,
+    _In_reads_( cbKey )     PCBYTE              pbKey,
+    _In_                    SIZE_T              cbKey );
+//
+// Initialize an RC4 encryption/decryption state.
+// WARNING: the most common error in using RC4 is to use the same key to encrypt two different pieces of data.
+// This is insecure and should never be done; you need a unique key for each data element that is encrypted.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc4Crypt(
+    _Inout_                 PSYMCRYPT_RC4_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+    _In_                    SIZE_T              cbData );
+//
+// Encrypt or Decrypt data using the RC4 state. Note that the RC4 state is updated and therefore this
+// function cannot be used by two threads simultaneously using the same state object.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc4Selftest(void);
+
+
+//
+// ChaCha20
+//
+// The ChaCha20 stream cipher is specified in RFC 7539 and referenced by RFC 7905
+// which specifies the ChaCha20-Poly1305 TLS cipher suite.
+//
+// ChaCha is a random-access stream cipher. It is possible to jump to any part of
+// the key stream and start en/decrypting there.
+// We support this by allowing the caller to select the position in the key stream
+// to use.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptChaCha20Init(
+    _Out_                   PSYMCRYPT_CHACHA20_STATE    pState,
+    _In_reads_( cbKey )     PCBYTE                      pbKey,
+    _In_                    SIZE_T                      cbKey,
+    _In_reads_( cbNonce )   PCBYTE                      pbNonce,
+                            SIZE_T                      cbNonce,
+                            UINT64                      offset );
+//
+// Initialize a ChaCha20 en/decryption state.
+// Key must be 32 bytes
+// Nonce must be 12 bytes
+// offset is the position into the key stream that the next encrypt/decrypt
+// operation will use. Requirement: 0 <= offset < 2^38
+// The ChaCha documentation is formulated in terms of a 'counter' or 'initial counter'.
+// Callers can set offset = 64 * <counter> to achieve the same results.
+//
+// An error is returned only for invalid key or nonce sizes.
+//
+// A single (key,nonce) pair defines a key stream of 256 GB.
+// Any part of that key stream can be used to encrypt a message, or part of a
+// message.
+// Note that it is critical that each key stream byte is used only once; thus
+// callers have to ensure that for any key, each nonce is used at most once for
+// a message, and messages cannot use any part of the 256 GB key stream more than
+// once.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20SetOffset(
+    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
+                            UINT64                      offset );
+//
+// Specify the offset into the key stream where the next encrypt/decrypt operation
+// will start.
+// Requirement: 0 <= offset < 2^38
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Crypt(
+    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )  PBYTE                       pbDst,
+                            SIZE_T                      cbData );
+//
+// Encrypt or Decrypt data using the CHACHA20 state.
+// The Src data is xorred with the key stream generated from the state, and the result stored
+// in the Dst buffer. The Src and Dst buffer can be identical or non-overlapping; partial overlaps
+// are not supported.
+// As the state is updated two threads cannot en/decrypt with the same state at the same time.
+// The key stream used is the one generated from the key and nonce, starting at the specified
+// offset into the key stream. This function updates the offset of the state by adding cbData to
+// it so that the next call will use the next part of the key stream.
+// Any attempt to use the key stream at offset >= 2^38 will result in catastrophic loss of security.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Selftest(void);
+
+
+
+
+//==========================================================================
+//   KEY DERIVATION ALGORITHMS
+//==========================================================================
+
+////////////////////////////////////////////////////////////////////////////
+// PBKDF2
+//
+// Generic KDF parameter handling:
+//  - Generic parameter is passed in the Salt input;
+//  - iterationCnt is set to 1.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPbkdf2ExpandKey(
+    _Out_               PSYMCRYPT_PBKDF2_EXPANDED_KEY   pExpandedKey,
+    _In_                PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbKey)   PCBYTE                          pbKey,
+                        SIZE_T                          cbKey );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPbkdf2Derive(
+    _In_                    PCSYMCRYPT_PBKDF2_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbSalt)  PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt,
+                            UINT64                          iterationCnt,
+    _Out_writes_(cbResult)  PBYTE                           pbResult,
+                            SIZE_T                          cbResult);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPbkdf2(
+                            PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbKey)       PCBYTE          pbKey,
+                            SIZE_T          cbKey,
+    _In_reads_opt_(cbSalt)  PCBYTE          pbSalt,
+                            SIZE_T          cbSalt,
+                            UINT64          iterationCnt,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult);
+
+//
+// Because the self-test pulls in the associated MAC function,
+// we have several self-tests; each of which tests the PBKDF2 implementation
+// using the specified MAC function.
+// This allows a FIPS module to run the self-test with the MAC function it already
+// uses internally.
+//
+// More can be added when needed.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptPbkdf2_HmacSha1SelfTest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptPbkdf2_HmacSha256SelfTest(void);
+
+////////////////////////////////////////////////////////////////////////////
+// SP800-108 Counter mode
+//
+// Generic KDF parameter handling:
+// Generic parameter contains the concatenation of the Label, a zero byte, and the Context.
+// To pass a generic parameter do the following:
+//  - pbLabel = NULL
+//  - cbLabel = (SIZE_T) -1;
+//  - pbContext/cbContext = generic parameter
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSp800_108ExpandKey(
+    _Out_               PSYMCRYPT_SP800_108_EXPANDED_KEY    pExpandedKey,
+    _In_                PCSYMCRYPT_MAC                      macAlgorithm,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSp800_108Derive(
+    _In_                        PCSYMCRYPT_SP800_108_EXPANDED_KEY   pExpandedKey,
+    _In_reads_opt_(cbLabel)     PCBYTE                              pbLabel,
+                                SIZE_T                              cbLabel,
+    _In_reads_opt_(cbContext)   PCBYTE                              pbContext,
+                                SIZE_T                              cbContext,
+    _Out_writes_(cbResult)      PBYTE                               pbResult,
+                                SIZE_T                              cbResult);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSp800_108(
+                                PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbKey)           PCBYTE          pbKey,
+                                SIZE_T          cbKey,
+    _In_reads_opt_(cbLabel)     PCBYTE          pbLabel,
+                                SIZE_T          cbLabel,
+    _In_reads_opt_(cbContext)   PCBYTE          pbContext,
+                                SIZE_T          cbContext,
+    _Out_writes_(cbResult)      PBYTE           pbResult,
+                                SIZE_T          cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha1SelfTest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha256SelfTest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha384SelfTest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha512SelfTest(void);
+
+////////////////////////////////////////////////////////////////////////////
+// TLS Key Derivation PRFs
+//
+// PRFs used in the key derivation functions of the TLS protocol, versions
+// 1.0, 1.1, and 1.2. These are defined in RFC 2246, 4346, and 5246,
+// respectively.
+// Note: The PRFs for versions 1.0 and 1.1 are identical.
+//
+
+// Maximum sizes (in bytes) for the label and the seed inputs. See the
+// above RFCs 2246, 4346, and 5246 for more details.
+#define SYMCRYPT_TLS_MAX_LABEL_SIZE 256
+#define SYMCRYPT_TLS_MAX_SEED_SIZE  256
+
+//
+//  Version 1.0/1.1
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1ExpandKey(
+    _Out_               PSYMCRYPT_TLSPRF1_1_EXPANDED_KEY    pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1Derive(
+    _In_                    PCSYMCRYPT_TLSPRF1_1_EXPANDED_KEY   pExpandedKey,
+    _In_reads_opt_(cbLabel) PCBYTE                              pbLabel,
+    _In_                    SIZE_T                              cbLabel,        // Up to SYMCRYPT_TLS_MAX_LABEL_SIZE
+    _In_reads_(cbSeed)      PCBYTE                              pbSeed,
+    _In_                    SIZE_T                              cbSeed,         // Up to SYMCRYPT_TLS_MAX_SEED_SIZE
+    _Out_writes_(cbResult)  PBYTE                               pbResult,
+                            SIZE_T                              cbResult);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1(
+    _In_reads_(cbKey)       PCBYTE   pbKey,
+    _In_                    SIZE_T   cbKey,
+    _In_reads_opt_(cbLabel) PCBYTE   pbLabel,
+    _In_                    SIZE_T   cbLabel,
+    _In_reads_(cbSeed)      PCBYTE   pbSeed,
+    _In_                    SIZE_T   cbSeed,
+    _Out_writes_(cbResult)  PBYTE    pbResult,
+                            SIZE_T   cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1SelfTest(void);
+
+//
+//  Version 1.2
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2ExpandKey(
+    _Out_               PSYMCRYPT_TLSPRF1_2_EXPANDED_KEY    pExpandedKey,
+    _In_                PCSYMCRYPT_MAC                      macAlgorithm,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2Derive(
+    _In_                    PCSYMCRYPT_TLSPRF1_2_EXPANDED_KEY   pExpandedKey,
+    _In_reads_opt_(cbLabel) PCBYTE                              pbLabel,
+    _In_                    SIZE_T                              cbLabel,    // Up to SYMCRYPT_TLS_MAX_LABEL_SIZE
+    _In_reads_(cbSeed)      PCBYTE                              pbSeed,
+    _In_                    SIZE_T                              cbSeed,     // Up to SYMCRYPT_TLS_MAX_SEED_SIZE
+    _Out_writes_(cbResult)  PBYTE                               pbResult,
+                            SIZE_T                              cbResult);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2(
+    _In_                    PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbKey)       PCBYTE          pbKey,
+    _In_                    SIZE_T          cbKey,
+    _In_reads_opt_(cbLabel) PCBYTE          pbLabel,
+    _In_                    SIZE_T          cbLabel,
+    _In_reads_(cbSeed)      PCBYTE          pbSeed,
+    _In_                    SIZE_T          cbSeed,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2SelfTest(void);
+
+
+////////////////////////////////////////////////////////////////////////////
+// SSH-KDF as specified in RFC 4253 Section 7.2.
+//
+
+
+// Labels defined in RFC 4253
+#define SYMCRYPT_SSHKDF_IV_CLIENT_TO_SERVER                0x41        // 'A'
+#define SYMCRYPT_SSHKDF_IV_SERVER_TO_CLIENT                0x42        // 'B'
+#define SYMCRYPT_SSHKDF_ENCRYPTION_KEY_CLIENT_TO_SERVER    0x43        // 'C'
+#define SYMCRYPT_SSHKDF_ENCRYPTION_KEY_SERVER_TO_CLIENT    0x44        // 'D'
+#define SYMCRYPT_SSHKDF_INTEGRITY_KEY_CLIENT_TO_SERVER     0x45        // 'E'
+#define SYMCRYPT_SSHKDF_INTEGRITY_KEY_SERVER_TO_CLIENT     0x46        // 'F'
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSshKdfExpandKey(
+    _Out_               PSYMCRYPT_SSHKDF_EXPANDED_KEY   pExpandedKey,
+    _In_                PCSYMCRYPT_HASH                 pHashFunc,
+    _In_reads_(cbKey)   PCBYTE                          pbKey,
+                        SIZE_T                          cbKey);
+//
+// Process the key using the specified hash function and store the result in
+// SYMCRYPT_SSHKDF_EXPANDED_KEY structure. Once the key is expanded,
+// SymCryptSshKdfDerive can be called multiple times to generate keys for
+// different uses/labels.
+//
+// After all the keys are derived from a particular "shared secret" key,
+// SYMCRYPT_SSHKDF_EXPANDED_KEY structure must be wiped.
+//
+// Parameters:
+//      - pExpandedKey  :   Pointer to a SYMCRYPT_SSHKDF_EXPANDED_KEY structure that
+//                          will contain the expanded key after the function returns.
+//      - pHashFunc     :   Hash function that will be used in the key derivation.
+//                          This function is saved in SYMCRYPT_SSHKDF_EXPANDED_KEY
+//                          so that it is also used by the SymCryptSshKdfDerive function.
+//      - pbKey, cbKey  :   Buffer containing the secret key for the KDF.
+//
+// Returns SYMCRYPT_NO_ERROR
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSshKdfDerive(
+    _In_                        PCSYMCRYPT_SSHKDF_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbHashValue)     PCBYTE                          pbHashValue,
+                                SIZE_T                          cbHashValue,
+                                BYTE                            label,
+    _In_reads_(cbSessionId)     PCBYTE                          pbSessionId,
+                                SIZE_T                          cbSessionId,
+    _Inout_updates_(cbOutput)   PBYTE                           pbOutput,
+                                SIZE_T                          cbOutput);
+//
+// Derive keys using the expanded key that was initialized with SymCryptSshKdfExpandKey
+// along with other inputs. This function can be called consecutively with varying label
+// values to generate keys for different purposes as defined in the RFC.
+//
+// Parameters:
+//      - pExpandedKey              :   Pointer to a SYMCRYPT_SSHKDF_EXPANDED_KEY structure that is
+//                                      initialized by a prior call to SymCryptSshKdfExpandKey.
+//                                      Must be wiped when SymCryptSshKdfDerive is not going to be called
+//                                      again with the same expanded key.
+//      - pbHashValue, cbHashValue  :   Buffer pointing to "exchange hash" value. cbHashValue must be equal
+//                                      to the output size of the hash function passed to SymCryptSshKdfExpandKey.
+//      - label                     :   Label value used to indicate the type of the derived key.
+//      - pbSessionId, cbSessionId  :   Buffer pointing to the session identifier. cbSessionId must be equal
+//                                      to the output size of the hash function passed to SymCryptSshKdfExpandKey.
+//      - pbOutput, cbOutput        :   Buffer to store the derived key. Exactly cbOutput bytes of output will be generated.
+//
+// Returns SYMCRYPT_NO_ERROR
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSshKdf(
+    _In_                    PCSYMCRYPT_HASH     pHashFunc,
+    _In_reads_(cbKey)       PCBYTE              pbKey,
+                            SIZE_T              cbKey,
+    _In_reads_(cbHashValue) PCBYTE              pbHashValue,
+                            SIZE_T              cbHashValue,
+                            BYTE                label,
+    _In_reads_(cbSessionId) PCBYTE              pbSessionId,
+                            SIZE_T              cbSessionId,
+    _Out_writes_(cbOutput)  PBYTE               pbOutput,
+                            SIZE_T              cbOutput);
+//
+// This function is a wrapper for using SymCryptSshKdfExpandKey followed by SymCryptSshKdfDerive
+// in order to produce SSH-KDF output.
+//
+// All of the function arguments are forwarded to SymCryptSshKdfExpandKey and SymCryptSshKdfDerive
+// functions, hence the documentation on those functions apply here as well.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSshKdfSha256SelfTest(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptSshKdfSha512SelfTest(void);
+
+
+////////////////////////////////////////////////////////////////////////////
+// SRTP-KDF as specified in RFC 3711 Section 4.3.1.
+//
+
+
+// Labels defined in RFC 3711
+#define SYMCRYPT_SRTP_ENCRYPTION_KEY        0x00
+#define SYMCRYPT_SRTP_AUTHENTICATION_KEY    0x01
+#define SYMCRYPT_SRTP_SALTING_KEY           0x02
+#define SYMCRYPT_SRTCP_ENCRYPTION_KEY       0x03
+#define SYMCRYPT_SRTCP_AUTHENTICATION_KEY   0x04
+#define SYMCRYPT_SRTCP_SALTING_KEY          0x05
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSrtpKdfExpandKey(
+    _Out_                PSYMCRYPT_SRTPKDF_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)    PCBYTE                          pbKey,
+                         SIZE_T                          cbKey);
+//
+// Process the key and store the result in SYMCRYPT_SRTPKDF_EXPANDED_KEY structure.
+// Once the key is expanded, SymCryptSrtpKdfDerive can be called multiple times to
+// generate keys for different uses/labels.
+//
+// After all the keys are derived from a particular "shared secret" key,
+// SYMCRYPT_SRTPKDF_EXPANDED_KEY structure must be wiped.
+//
+// Parameters:
+//      - pExpandedKey  :   Pointer to a SYMCRYPT_SRTPKDF_EXPANDED_KEY structure that
+//                          will contain the expanded key after the function returns.
+//      - pbKey, cbKey  :   Buffer containing the secret key for the KDF. cbKey must be
+//                          a valid AES key size (16-, 24-, or 32-bytes).
+//
+// Returns:
+//      SYMCRYPT_WRONG_KEY_SIZE : If cbKey is not a valid AES key size
+//      SYMCRYPT_NO_ERROR       : On success
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSrtpKdfDerive(
+    _In_                    PCSYMCRYPT_SRTPKDF_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSalt)      PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt,
+                            UINT32                          uKeyDerivationRate,
+                            UINT64                          uIndex,
+                            UINT32                          uIndexWidth,
+                            BYTE                            label,
+    _Out_writes_(cbOutput)  PBYTE                           pbOutput,
+                            SIZE_T                          cbOutput);
+//
+// Derive keys using the expanded key that was initialized with SymCryptSrtpKdfExpandKey
+// along with other inputs. This function can be called consecutively with varying label
+// values to generate keys for different purposes as defined in the RFC.
+//
+// Parameters:
+//      - pExpandedKey              :   Pointer to a SYMCRYPT_SRTPKDF_EXPANDED_KEY structure that is
+//                                      initialized by a prior call to SymCryptSrtpKdfExpandKey.
+//                                      Must be wiped when SymCryptSrtpKdfDerive is not going to be called
+//                                      again with the same expanded key.
+//      - pbSalt, cbSalt            :   Buffer pointing to the salt value. cbSalt must always be 14 (112-bits).
+//      - uKeyDerivationRate        :   Key derivation rate; must be zero or 2^i for 0 <= i <= 24.
+//      - uIndex                    :   Denotes an SRTP index value when label is 0x00, 0x01, or 0x02, otherwise
+//                                      denotes an SRTCP index value.
+//      - uIndexWidth               :   Denotes how wide uIndex value is. Must be one of 0, 32, or 48. By default,
+//                                      (when uIndexWidth = 0) uIndex is treated as 48-bits.
+//                                      RFC 3711 initially defined SRTCP indices to be 32-bit values. It was updated
+//                                      to be 48-bits by Errata ID 3712. SRTP index values are defined to be 48-bits.
+//      - label                     :   Label value used to indicate the type of the derived key.
+//      - pbOutput, cbOutput        :   Buffer to store the derived key. Exactly cbOutput bytes of output will be generated.
+//
+// Returns:
+//      SYMCRYPT_INVALID_ARGUMENT   :   If cbSalt is not 14-bytes, or uKeyDerivationRate in invalid.
+//      SYMCRYPT_NO_ERROR           :   On success.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSrtpKdf(
+    _In_reads_(cbKey)           PCBYTE  pbKey,
+                                SIZE_T  cbKey,
+    _In_reads_(cbSalt)          PCBYTE  pbSalt,
+                                SIZE_T  cbSalt,
+                                UINT32  uKeyDerivationRate,
+                                UINT64  uIndex,
+                                UINT32  uIndexWidth,
+                                BYTE    label,
+    _Out_writes_(cbOutput)      PBYTE   pbOutput,
+                                SIZE_T  cbOutput);
+//
+// This function is a wrapper for using SymCryptSrtpKdfExpandKey followed by SymCryptSrtpKdfDerive
+// in order to produce SRTP-KDF output.
+//
+// All of the function arguments are forwarded to SymCryptSrtpKdfExpandKey and SymCryptSrtpKdfDerive
+// functions, hence the documentation on those functions apply here as well.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSrtpKdfSelfTest(void);
+
+
+////////////////////////////////////////////////////////////////////////////
+// HKDF
+//
+// PRF used in the key derivation functions of the TLS protocol, version
+// 1.3. It is defined in RFC 5869.
+//
+// The SymCrypt ExtractPrk function corresponds to the "HKDF-Extract" function
+// of the RFC 5869, while the SymCrypt PrkExpandKey and Derive functions
+// correspond to the "HKDF-Expand" function of the RFC.
+//
+// SymCryptHkdfExtractPrk takes as inputs the MAC algorithm, the IKM (input
+// keying material), and the optional salt. It executes the full "HKDF-Extract"
+// function to produce the PRK (pseudorandom key).
+//
+// SymCryptHkdfPrkExpandKey takes as inputs just the MAC algorithm and the PRK.
+// It produces the final (MAC) key to be used by the "HKDF-Expand" function.
+//
+// SymCryptHkdfExpandKey performs SymCryptHkdfExtractPrk followed by
+// SymCryptHkdfPrkExpandKey to produce the final (MAC) key to be used by the
+// "HKDF-Expand" function, without exposing the PRK to the caller.
+//
+// SymCryptHkdfDerive takes as input the final MAC key and the optional info. It
+// performs the rest of the "HKDF-Expand" function to produce the HKDF result.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfExpandKey(
+    _Out_                   PSYMCRYPT_HKDF_EXPANDED_KEY     pExpandedKey,
+    _In_                    PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbIkm)       PCBYTE                          pbIkm,
+                            SIZE_T                          cbIkm,
+    _In_reads_opt_(cbSalt)  PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfExtractPrk(
+    _In_                    PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbIkm)       PCBYTE                          pbIkm,
+                            SIZE_T                          cbIkm,
+    _In_reads_opt_(cbSalt)  PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt,
+    _Out_writes_(cbPrk)     PBYTE                           pbPrk,
+                            SIZE_T                          cbPrk );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfPrkExpandKey(
+    _Out_                   PSYMCRYPT_HKDF_EXPANDED_KEY     pExpandedKey,
+    _In_                    PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbPrk)       PCBYTE                          pbPrk,
+                            SIZE_T                          cbPrk );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfDerive(
+    _In_                    PCSYMCRYPT_HKDF_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbInfo)  PCBYTE                          pbInfo,
+                            SIZE_T                          cbInfo,
+    _Out_writes_(cbResult)  PBYTE                           pbResult,
+                            SIZE_T                          cbResult);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdf(
+                            PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbIkm)       PCBYTE          pbIkm,
+                            SIZE_T          cbIkm,
+    _In_reads_opt_(cbSalt)  PCBYTE          pbSalt,
+                            SIZE_T          cbSalt,
+    _In_reads_opt_(cbInfo)  PCBYTE          pbInfo,
+                            SIZE_T          cbInfo,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult);
+
+VOID
+SYMCRYPT_CALL
+SymCryptHkdfSelfTest(void);
+
+////////////////////////////////////////////////////////////////////////////
+// SSKDF
+//
+// Single-Step KDF as specified in SP800-56C section 4.
+//
+// SSKDF requires an auxiliary function H. This can be approved hash function,
+// HMAC with an approved hash function, or KMAC. The approved hash functions
+// are listed in SP800-56C section 7.
+//
+// A salt value may be optionally provided if either HMAC or KMAC is used for H.
+// When no salt is provided, an all-zero default salt is used instead. For HMAC,
+// the default salt is the length of an input block of the HMAC's hash function.
+// For KMAC128, the default salt is 164 bytes. For KMAC256, the default salt is 132 bytes.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfMacExpandSalt(
+    _Out_                   PSYMCRYPT_SSKDF_MAC_EXPANDED_SALT   pExpandedSalt,
+    _In_                    PCSYMCRYPT_MAC                      macAlgorithm,
+    _In_reads_opt_(cbSalt)  PCBYTE                              pbSalt,
+                            SIZE_T                              cbSalt);
+//
+// Initializes *pExpandedSalt with the macAlgorithm, and optionally the salt. Used
+// for SSKDF when H is a MAC function. After calling SymCryptSskdfMacExpandSalt,
+// SymCryptSskdfMacDerive can be called multiple times to generate keys for different
+// uses, fixed infos, and shared secrets. For multiple KDFs using the same MAC and salt,
+// calling SymCryptSskdfMacExpandSalt once and SymCryptSskdfMacDerive multiple times
+// is more efficient than calling SymCryptSskdfMac multiple times.
+//
+// The expanded salt contains no secrets and does not need to be wiped.
+//
+// Parameters:
+//    - pExpandedSalt   :   Pointer to a SYMCRYPT_SSKDF_MAC_EXPANDED_SALT structure that
+//                          will contain the expanded salt after the function returns.
+//    - macAlgorithm    :   MAC algorithm that will be used in the key derivation.
+//                          This function is saved in SYMCRYPT_SSKDF_MAC_EXPANDED_SALT.
+//    - pbSalt, cbSalt  :   Buffer containing the salt for the KDF. cbSalt must be a valid
+//                          key size for the MAC algorithm. If pbSalt is NULL, the default
+//                          all zero-byte salt is used.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfMacDerive(
+    _In_                    PCSYMCRYPT_SSKDF_MAC_EXPANDED_SALT  pExpandedSalt,
+                            SIZE_T                              cbMacOutputSize,
+    _In_reads_(cbSecret)    PCBYTE                              pbSecret,
+                            SIZE_T                              cbSecret,
+    _In_reads_opt_(cbInfo)  PCBYTE                              pbInfo,
+                            SIZE_T                              cbInfo,
+    _Out_writes_(cbResult)  PBYTE                               pbResult,
+                            SIZE_T                              cbResult);
+//
+// Derive keys using the expanded salt that was initialized with SymCryptSskdfMacExpandSalt
+// along with other inputs. This function can be called consecutively with varying fixed infos
+// and shared secrets to generate keys for different purposes as defined in the SP800-56C.
+// The same pbExpandedKey can be used simultaneously by multiple threads.
+//
+// Parameters:
+//    - pExpandedSalt       :   Pointer to a SYMCRYPT_SSKDF_MAC_EXPANDED_SALT structure that is
+//                              initialized by a prior call to SymCryptSskdfMacExpandSalt.
+//    - cbMacOutputSize     :   Output size used by the MAC algorithm for intermediate computations. Must not be
+//                              greater than 64 bytes. Set to 0 for MACs that don't support variable output sizes,
+//                              or to use the default output size. The default output size when KMAC is used is cbResult.
+//    - pbSecret, cbSecret  :   Buffer containing the shared secret.
+//    - pbInfo, cbInfo      :   Buffer containing the fixed info.
+//    - pbResult, cbResult  :   Buffer to store the derived key. Exactly cbResult bytes of output will be generated.
+//                              Must not exceed 2^{32} - 1 times the result size of the MAC algorithm.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfMac(
+    _In_                    PCSYMCRYPT_MAC  macAlgorithm,
+                            SIZE_T          cbMacOutputSize,
+    _In_reads_(cbSecret)    PCBYTE          pbSecret,
+                            SIZE_T          cbSecret,
+    _In_reads_opt_(cbSalt)  PCBYTE          pbSalt,
+                            SIZE_T          cbSalt,
+    _In_reads_opt_(cbInfo)  PCBYTE          pbInfo,
+                            SIZE_T          cbInfo,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult);
+//
+// This function is a wrapper for using SymCryptSskdfMacExpandSalt followed by SymCryptSskdfMacDerive
+// in order to produce SSKDF output.
+//
+// All of the function arguments are forwarded to SymCryptSskdfMacExpandSalt and SymCryptSskdfMacDerive
+// functions, hence the documentation on those functions apply here as well.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfHash(
+    _In_                    PCSYMCRYPT_HASH hashAlgorithm,
+                            SIZE_T          cbHashOutputSize,
+    _In_reads_(cbSecret)    PCBYTE          pbSecret,
+                            SIZE_T          cbSecret,
+    _In_reads_opt_(cbInfo)  PCBYTE          pbInfo,
+                            SIZE_T          cbInfo,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult);
+//
+// Derive keys using the specified hash algorithm as H.
+//
+// Parameters:
+//    - hashAlgorithm       :   Hash algorithm that will be used in the key derivation.
+//    - cbHashOutputSize    :   Output size used by the hash algorithm for intermediate computations.
+//                              Set to 0 for hashes that don't support variable output sizes, or to use
+//                              the default output size. Currently, no allowed hash algorithms support
+//                              variable output sizes, so this should always be set to 0.
+//    - pbSecret, cbSecret  :   Buffer containing the shared secret.
+//    - pbInfo, cbInfo      :   Buffer containing the fixed info.
+//    - pbResult, cbResult  :   Buffer to store the derived key. Exactly cbResult bytes of output will be generated.
+//                              Must not exceed 2^{32} - 1 times the result size of hashAlgorithm.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptSskdfSelfTest(void);
+
+//==========================================================================
+//   RNG ALGORITHMS
+//==========================================================================
+
+////////////////////////////////////////////////////////////////////////////
+// AES-CTR-DRBG
+//
+// This is an implementation of AES-CTR_DRBG as specified in SP 800-90.
+// It always uses a 256-bit security strength.
+//
+// Note: This RNG is NOT compliant with FIPS 140-2 as it lacks the continuous
+//   self test required by FIPS 140-2. See the AES-FIPS RNG algorithm below.
+//
+// SYMCRYPT_RNG_AES_STATE
+//      State of an AES-CTR_DRBG instance.
+//
+
+#define SYMCRYPT_RNG_AES_MIN_INSTANTIATE_SIZE   (32 + 16)
+#define SYMCRYPT_RNG_AES_MIN_RESEED_SIZE   (32)
+#define SYMCRYPT_RNG_AES_MAX_SEED_SIZE   (256)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesInstantiate(
+    _Out_                       PSYMCRYPT_RNG_AES_STATE pRngState,
+    _In_reads_(cbSeedMaterial)  PCBYTE                  pcbSeedMaterial,
+
+    _In_range_(SYMCRYPT_RNG_AES_MIN_INSTANTIATE_SIZE, SYMCRYPT_RNG_AES_MAX_SEED_SIZE)
+                                SIZE_T                  cbSeedMaterial );
+//
+// Initialize a new SYMCRYPT_RNG_AES_STATE, and seed it with the seed material.
+//
+// 'Instantiate' is the SP800-90 terminology.
+// The seed material must be at least SYMCRYPT_RNG_AES_MIN_INSTANTIATE_SIZE bytes,
+// and at most SYMCRYPT_RNG_AES_MAX_SEED_SIZE bytes.
+//
+// This implementation always uses 256-bit security strength, and
+// does not support 'prediction resistance' as defined in SP 800-90.
+//
+// SP 800-90 specifies three inputs to the instantiation:
+// - entropy
+// - nonce
+// - personalization string
+// This function takes only a single input, which is the concatenation of these three:
+//   seed material := entropy | nonce | personalization string
+//
+// The following are the requirements on the three inputs:
+//  Entropy: must have at least 256 bits of entropy
+//  Nonce: must either be a random value with 128-bits of entropy, or a value that does not
+//      repeat with a probability of more than 2^{-128}.
+// Together these requirements imply that cbSeedMaterial should be at least
+//  SYMCRYPT_RNG_AES_MIN_INSTANTIATE_SIZE
+//
+// This function only returns an error if the cbSeedMaterial value is out of range.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesGenerate(
+    _Inout_                 PSYMCRYPT_RNG_AES_STATE pRngState,
+    _Out_writes_(cbRandom)  PBYTE                   pbRandom,
+                            SIZE_T                  cbRandom );
+//
+// Generate random output from the state.
+//
+// Callers do not need to limit themselves to requests of 64 kB or less;
+// large requests are split internally to follow the request size limitations of SP 800-90.
+//
+// SP 800-90 also requires a limit on the # generate calls that can be done between reseeds.
+// For AES-CTR_DRBG this limit is 2^48, which means it is all but impossible to hit this limit.
+// If the caller were to succeed, the 2^48'th call will result in a fatal error.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesReseed(
+    _Inout_                     PSYMCRYPT_RNG_AES_STATE pRngState,
+    _In_reads_(cbSeedMaterial)  PCBYTE                  pcbSeedMaterial,
+
+    _In_range_(SYMCRYPT_RNG_AES_MIN_RESEED_SIZE, SYMCRYPT_RNG_AES_MAX_SEED_SIZE)
+                                SIZE_T                  cbSeedMaterial );
+//
+// Reseed the PRNG state.
+//
+// The seed material consists of the concatenation of the following SP800-90 fields:
+// - entropy
+// - additional input
+//
+// The entropy input should have at least 256 bits of entropy.
+// This function only returns an error if the cbSeedMaterial value is out of range.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesUninstantiate(
+    _Inout_                 PSYMCRYPT_RNG_AES_STATE pRngState );
+//
+// Uninstantiate (clean up) the PRNG state
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesInstantiateSelftest(void);
+//
+// For FIPS-certified modules, this function should be called before every instantiation.
+// If multiple DRBGs are instantiated 'in quick succession', a single self-test is sufficient
+//  (see SP 800-90 11.3.2).
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesReseedSelftest(void);
+//
+// FIPS-certified modules should call this function before every call to the reseed function.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesGenerateSelftest(void);
+//
+// FIPS-certified modules should call this function at least once on startup, and whenever
+// they want to re-test the generate function.
+//
+
+////////////////////////////////////////////////////////////////////////////
+// AES-CTR-DRBG with FIPS 140-2 continuous self-test
+//
+// This is a straightforward wrapper around the AES-CTR-DRBG implementation
+// that adds the FIPS 140-2 continuous self-test.
+// At the moment, it looks like this test will not be present in FIPS 140-3 so
+// this RNG will be dropped when FIPS 140-3 comes out.
+// The self-test requirements are met by calling the selftest functions of the
+// AES-CTR_DRBG implementation directly.
+//
+// These functions are functionally equivalent to the ones for AES-CTR_DRBG.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Instantiate(
+    _Out_                       PSYMCRYPT_RNG_AES_FIPS140_2_STATE   pRngState,
+    _In_reads_(cbSeedMaterial)  PCBYTE                              pcbSeedMaterial,
+
+    _In_range_(SYMCRYPT_RNG_AES_MIN_INSTANTIATE_SIZE, SYMCRYPT_RNG_AES_MAX_SEED_SIZE)
+                                SIZE_T                              cbSeedMaterial );
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Generate(
+    _Inout_                 PSYMCRYPT_RNG_AES_FIPS140_2_STATE       pRngState,
+    _Out_writes_(cbRandom)  PBYTE                                   pbRandom,
+                            SIZE_T                                  cbRandom );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Reseed(
+    _Inout_                     PSYMCRYPT_RNG_AES_FIPS140_2_STATE   pRngState,
+    _In_reads_(cbSeedMaterial)  PCBYTE                              pcbSeedMaterial,
+
+    _In_range_(SYMCRYPT_RNG_AES_MIN_RESEED_SIZE, SYMCRYPT_RNG_AES_MAX_SEED_SIZE)
+                                SIZE_T                              cbSeedMaterial );
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Uninstantiate(
+    _Inout_                 PSYMCRYPT_RNG_AES_FIPS140_2_STATE pRngState );
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Internal RNG functions
+//
+// To satisfy FIPS 140-3 and SP 800-90B, certain modules of SymCrypt may set up internal
+// RNG state(s) to keep random bit generation behind the module's FIPS boundary.
+// These functions allow the caller to get random bits and provide entropy, respectively,
+// to SymCrypt's internal RNG state(s).
+// Implementation is module dependent, and these functions may not be defined
+// for certain modules. Check before using.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRandom(
+    _Out_writes_(cbRandom)  PBYTE   pbRandom,
+                            SIZE_T  cbRandom );
+// Fills pbRandom with cbRandom random bytes
+
+VOID
+SYMCRYPT_CALL
+SymCryptProvideEntropy(
+    _In_reads_(cbEntropy)   PCBYTE  pbEntropy,
+                            SIZE_T  cbEntropy );
+// Mixes pbEntropy into the internal RNG state. There may be module-specific limits on
+// cbEntropy - check module before use
+
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// RdRand support
+// These functions provide access to the RdRand random number generator in
+// the latest Intel CPUs.
+// The DRBG that underlies the RdRand instruction is limited to 128-bit security.
+// The seed for each consecutive 8 kB of data can be recovered in 2^128 work.
+// Therefore, we allow for multiple blocks of 8 kB to be gathered in an attempt to
+// extract 256-bit security from the hardware.
+// In general, to achieve N*128 bits of security, you should use a buffer of
+// (N+1)*SYMCRYPT_RDRAND_RESEED_SIZE bytes.
+//
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+// The RdRand instruction reseeds its internal DRBG every 8 kB (or faster)
+#define SYMCRYPT_RDRAND_RESEED_SIZE (1<<13)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdrandStatus(void);
+//
+// Returns SYMCRYPT_NO_ERROR if RdRand is available.
+// returns SYMCRYPT_NOT_IMPLEMENTED if RdRand is not available.
+// Note: the library must be initialized before you call this function.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdrandGetBytes(
+    _Out_writes_( cbBuffer )                    PBYTE   pbBuffer,
+                                                SIZE_T  cbBuffer,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE   pbResult );
+//
+// Gets cbBuffer bytes from the RdRand instruction and hashes them to the pbResult buffer.
+// pbBuffer points to a scratch buffer that is used internally, but wiped upon exit.
+// cbBuffer must be a multiple of 16.
+// Fatal error if SymCryptRdrandStatus indicates that Rdrand is not available.
+// Returns an error if the RdRand instruction failed consistently.
+// Note: SymCrypt only checks whether RdRand self-reports as failing. SymCrypt does NOT attempt
+// to validate that the values returned in successful RdRand calls are in fact random.
+// See SymCryptRdrandGet for a version that does not return an error but fatals instead.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRdrandGet(
+    _Out_writes_( cbBuffer )                    PBYTE   pbBuffer,
+                                                SIZE_T  cbBuffer,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE   pbResult );
+//
+// Gets cbBuffer bytes from the RdRand instruction and hashes them to the pbResult buffer.
+// pbBuffer points to a scratch buffer that is used internally, but wiped upon exit.
+// cbBuffer must be a multiple of 16.
+// Fatal error if the RdRand instruction fails.
+// Note: SymCrypt only checks whether RdRand self-reports as failing. SymCrypt does NOT attempt
+// to validate that the values returned in successful RdRand calls are in fact random.
+//
+
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// RdSeed support
+// These functions provide access to the RdSeed random number generator in
+// recent Intel CPUs.
+//
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdseedStatus(void);
+//
+// Returns SYMCRYPT_NO_ERROR if RdSeed is available.
+// returns SYMCRYPT_NOT_IMPLEMENTED if RdSeed is not available.
+// Note: the library must be initialized before you call this function.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdseedGetBytes(
+    _Out_writes_( cbResult )                    PBYTE   pbResult,
+                                                SIZE_T  cbResult );
+//
+// Queries cbResult bytes from the Rdseed instruction and puts them in the buffer.
+// The number of bytes (cbResult) must be a multiple of 16.
+// Fatal error if the Rdseed instruction is not present.
+// Returns an error if the Rdseed instruction fails consistently.
+// Note: SymCrypt only checks whether Rdseed self-reports as failing. SymCrypt does NOT attempt
+// to validate that the values returned in successful Rdseed calls are in fact random.
+// See SymCryptRdseedGet for a version that does not return an error but fatals instead.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRdseedGet(
+    _Out_writes_( cbResult )                    PBYTE   pbResult,
+                                                SIZE_T  cbResult );
+//
+// Queries cbResult bytes from the Rdseed instruction and puts them in the buffer.
+// The number of bytes (cbResult) must be a multiple of 16.
+// Fatal error if the Rdseed instruction is not present, or the instruction fails consistently.
+// Note: SymCrypt only checks whether Rdseed self-reports as failing. SymCrypt does NOT attempt
+// to validate that the values returned in successful Rdseed calls are in fact random.
+//
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// AES-XTS
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXtsAesExpandKey(
+    _Out_               PSYMCRYPT_XTS_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_( cbKey ) PCBYTE                          pbKey,
+                        SIZE_T                          cbKey );
+// Note that this key expansion function does not perform FIPS checks for backwards compatibility.
+// Use SymCryptXtsAesExpandKeyEx for FIPS-approved XTS key expansion.
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXtsAesExpandKeyEx(
+    _Out_               PSYMCRYPT_XTS_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_( cbKey ) PCBYTE                          pbKey,
+                        SIZE_T                          cbKey,
+                        UINT32                          flags );
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS.
+//   Currently this is just checking that 2 AES keys used in XTS are non-equal.
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesKeyCopy(
+    _In_    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_XTS_AES_EXPANDED_KEY  pDst );
+//
+// Create a copy of an expanded key
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncrypt(
+    _In_                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                            SIZE_T                          cbDataUnit,
+                            UINT64                          tweak,
+    _In_reads_( cbData )    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )  PBYTE                           pbDst,
+                            SIZE_T                          cbData );
+//
+// Encrypt a buffer using XTS-AES and 64 bit tweak.
+//      - pExpandedKey points to the expanded key for XTS.
+//      - cbDataUnit: size of each data unit, must be at least 16 and cannot exceed 2^{24} bytes. Typically 512.
+//      - tweak: 64 bit tweak value used for the first data unit in the buffer, incremented for subsequent data units.
+//      - pbSrc: plaintext input
+//      - pbDst: ciphertext output. The ciphertext buffer may be identical to the plaintext
+//          buffer, or non-overlapping. The ciphertext is also cbData bytes long.
+//      - cbData: # bytes of plaintext input. Must be a multiple of cbDataUnit.
+//
+// XTS-AES works on equal-sized data units, with each data unit being uniquely encrypted using a combination of
+// an integer "tweak" value and the XTS key (a pair of AES keys). A data unit typically corresponds to a sector
+// size on a disk.
+//
+// This API encrypts a buffer consisting of several consecutive data units, which use consecutive tweak values.
+// As the tweak is 64 bits, if there is an overflow of 64 bits, the value of the tweak will wrap to 0.
+//
+// i.e. encryption with tweak 0xffffffffffffffff for a buffer consisting of 2 data units will correspond to:
+// encryption using tweak 0xffffffffffffffff for the first data unit,
+// encryption using tweak 0x0000000000000000 for the second data unit
+//
+// Note, using cbDataUnit which is a power of 2 >= 256, will likely be more performant.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecrypt(
+    _In_                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                            SIZE_T                          cbDataUnit,
+                            UINT64                          tweak,
+    _In_reads_( cbData )    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )  PBYTE                           pbDst,
+                            SIZE_T                          cbData );
+//
+// Decrypt a buffer using XTS-AES and 64 bit tweak.
+// See SymCryptXtsAesEncrypt for a more in depth description, everything is the same, only this decrypts rather than encrypts.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptWith128bTweak(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData );
+//
+// Encrypt a buffer using XTS-AES and 128 bit tweak.
+//      - pExpandedKey points to the expanded key for XTS.
+//      - cbDataUnit: size of each data unit, must be at least 16 and cannot exceed 2^{24} bytes. Typically 512.
+//      - pbTweak: 128 bit tweak value used for the first data unit in the buffer, incremented for subsequent data units.
+//      - pbSrc: plaintext input
+//      - pbDst: ciphertext output. The ciphertext buffer may be identical to the plaintext
+//          buffer, or non-overlapping. The ciphertext is also cbData bytes long.
+//      - cbData: # bytes of plaintext input. Must be a multiple of cbDataUnit.
+//
+// XTS-AES works on equal-sized data units, with each data unit being uniquely encrypted using a combination of
+// an integer "tweak" value and the XTS key (a pair of AES keys). A data unit typically corresponds to a sector
+// size on a disk.
+//
+// This API encrypts a buffer consisting of several consecutive data units, which use consecutive tweak values.
+// As the tweak is 128 bits, if there is an overflow of 128 bits, the value of the tweak will wrap to 0.
+//
+// i.e. encryption with tweak 0x0000000000000000ffffffffffffffff for a buffer consisting of 2 data units will correspond to:
+// encryption using tweak 0x0000000000000000ffffffffffffffff for the first data unit,
+// encryption using tweak 0x00000000000000010000000000000000 for the second data unit
+//  but encryption with tweak 0xffffffffffffffffffffffffffffffff for a buffer consisting of 2 data units will correspond to:
+// encryption using tweak 0xffffffffffffffffffffffffffffffff for the first data unit,
+// encryption using tweak 0x00000000000000000000000000000000 for the second data unit
+//
+// Note, using cbDataUnit which is a power of 2 >= 256, will likely be more performant.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptWith128bTweak(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData );
+//
+// Decrypt a buffer using XTS-AES and 128 bit tweak.
+// See SymCryptXtsAesEncryptWith128bTweak for a more in depth description, everything is the same, only this decrypts rather than encrypts.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesSelftest(void);
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// AES-KW and AES-KWP
+//
+// These are the AES-KW and AES-KWP algorithms per SP 800-38F.
+//
+// These are very slow compared to most AES modes, requiring a long serial chain of AES
+// block encryption/decryptions, with a best case cost comparable to ~12x AES-CBC encryption
+// for a given buffer size. In practice the cost is often higher.
+// These cipher modes are not recommended.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwEncrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult );
+//
+// Encrypt a buffer using AES-KW mode.
+//
+// - pExpandedKey points to the expanded key to use.
+// - pbSrc is the plaintext source buffer. The source and destination buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbSrc. # bytes of plaintext. This must be a multiple of 8, >=16, and <2^31.
+// - pbDst is the ciphertext destination buffer. The source and destination buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbDst. # bytes in the destination buffer. This must be >= cbSrc+8.
+// - pcbResult pointer to a variable which receives the length of the ciphertext written to pbDst.
+//
+// Returns:
+//      SYMCRYPT_INVALID_ARGUMENT           :   If cbSrc is an invalid size
+//      SYMCRYPT_BUFFER_TOO_SMALL           :   If cbDst is not large enough
+//                                              (this can always be avoided if cbDst >= cbSrc+8)
+//      SYMCRYPT_MEMORY_ALLOCATION_FAILURE  :   If there is insufficient memory for the operation
+//      SYMCRYPT_NO_ERROR                   :   On success
+//
+// Remarks:
+//  The standard allows larger plaintexts but there is no requirement to support them, we only support
+//  plaintext up to 2^31 bytes because it avoids complexity in handling overflow of 32b buffer sizes, and
+//  is larger than practically necessary.
+//  The output parameters (pbDst and pcbResult) are only set on success.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwDecrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult );
+//
+// Decrypt a buffer using AES-KW mode.
+//
+// - pExpandedKey points to the expanded key to use.
+// - pbSrc is the ciphertext source buffer. The source and destination buffers may be
+//      identical (in-place decryption) or non-overlapping, but they may not partially overlap.
+// - cbSrc. # bytes of ciphertext. This must be a multiple of 8, >=24, and <=2^31.
+// - pbDst is the plaintext destination buffer. The source and destination buffers may be
+//      identical (in-place decryption) or non-overlapping, but they may not partially overlap.
+// - cbDst. # bytes in the destination buffer. This must be >= cbSrc-8.
+// - pcbResult pointer to a variable which receives the length of the plaintext written to pbDst.
+//
+// Returns:
+//      SYMCRYPT_INVALID_ARGUMENT           :   If cbSrc is an invalid size
+//      SYMCRYPT_BUFFER_TOO_SMALL           :   If cbDst is not large enough
+//                                              (this can always be avoided if cbDst >= cbSrc-8)
+//      SYMCRYPT_AUTHENTICATION_FAILURE     :   If pbSrc does not decrypt successfully
+//      SYMCRYPT_MEMORY_ALLOCATION_FAILURE  :   If there is insufficient memory for the operation
+//      SYMCRYPT_NO_ERROR                   :   On success
+//
+// Remarks:
+//  The standard allows larger plaintexts but there is no requirement to support them, we only support
+//  plaintext up to 2^31 bytes because it avoids complexity in handling overflow of 32b buffer sizes, and
+//  is larger than practically necessary.
+//  The output parameters (pbDst and pcbResult) are only set on success.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwpEncrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult );
+//
+// Encrypt a buffer using AES-KWP mode.
+//
+// - pExpandedKey points to the expanded key to use.
+// - pbSrc is the plaintext source buffer. The source and destination buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbSrc. # bytes of plaintext. This must be >0 and <=2^31-8.
+// - pbDst is the ciphertext destination buffer. The source and destination buffers may be
+//      identical (in-place encryption) or non-overlapping, but they may not partially overlap.
+// - cbDst. # bytes in the destination buffer. This must be >= cbSrc + 16 - (cbSrc%8) - ((cbSrc%8)==0 ? 8 : 0)
+// - pcbResult pointer to a variable which receives the length of the ciphertext written to pbDst.
+//
+// Returns:
+//      SYMCRYPT_INVALID_ARGUMENT           :   If cbSrc is an invalid size
+//      SYMCRYPT_BUFFER_TOO_SMALL           :   If cbDst is not large enough
+//                                              (this can always be avoided if cbDst >= cbSrc+15)
+//      SYMCRYPT_MEMORY_ALLOCATION_FAILURE  :   If there is insufficient memory for the operation
+//      SYMCRYPT_NO_ERROR                   :   On success
+//
+// Remarks:
+//  The standard allows larger plaintexts but there is no requirement to support them, we only support
+//  plaintext up to 2^31 bytes because it avoids complexity in handling overflow of 32b buffer sizes, and
+//  is larger than practically necessary.
+//  The output parameters (pbDst and pcbResult) are only set on success.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwpDecrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult );
+//
+// Decrypt a buffer using AES-KWP mode.
+//
+// - pExpandedKey points to the expanded key to use.
+// - pbSrc is the ciphertext source buffer. The source and destination buffers may be
+//      identical (in-place decryption) or non-overlapping, but they may not partially overlap.
+// - cbSrc. # bytes of ciphertext. This must be a multiple of 8, >=16, and <=2^31.
+// - pbDst is the plaintext destination buffer. The source and destination buffers may be
+//      identical (in-place decryption) or non-overlapping, but they may not partially overlap.
+// - cbDst. # bytes in the destination buffer. This must be large enough to fit the plaintext,
+//      a valid plaintext length is in the range [cbSrc-15, cbSrc-8]. If cbDst >= cbSrc-8 then the
+//      destination buffer is guaranteed to be large enough.
+// - pcbResult pointer to a variable which receives the length of the plaintext written to pbDst.
+//
+// Returns:
+//      SYMCRYPT_INVALID_ARGUMENT           :   If cbSrc is an invalid size
+//      SYMCRYPT_BUFFER_TOO_SMALL           :   If cbDst is not large enough
+//                                              (this can always be avoided if cbDst >= cbSrc-8)
+//      SYMCRYPT_AUTHENTICATION_FAILURE     :   If pbSrc does not decrypt successfully
+//      SYMCRYPT_MEMORY_ALLOCATION_FAILURE  :   If there is insufficient memory for the operation
+//      SYMCRYPT_NO_ERROR                   :   On success
+//
+// Remarks:
+//  The standard allows larger plaintexts but there is no requirement to support them, we only support
+//  plaintext up to 2^31 bytes because it avoids complexity in handling overflow of 32b buffer sizes, and
+//  is larger than practically necessary.
+//  The output parameters (pbDst and pcbResult) are only set on success.
+//
+//  If we fail to decrypt due to bad data, we return SYMCRYPT_AUTHENTICATION_FAILURE in constant time with
+//  respect to how the decrypted data is corrupted. While there is no known attack on AES-KWP abusing
+//  differential timing of different failure cases, being constant time for this is cheap, so is a reasonable
+//  hardening measure.
+//
+//  On success we do not attempt to hide the plaintext length from sidechannels, as this could make it hard
+//  for callers with known plaintext length to use precisely sized buffers to decrypt into (i.e. caller
+//  knows the valid plaintext is 15 bytes but the API would require caller to provide a 16 byte pbDst). It
+//  is expected that in any real use case the length of the plaintext would immediately be used to import the
+//  unwrapped key into some other piece of code - so attempting to obscure the plaintext length would not be
+//  of any benefit.
+//
+
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// TLS CBC cipher suites HMAC verification
+//
+// The TLS cipher suites for block cipher modes (typically CBC) are designed in an unfortunate way.
+// The format is:
+//      Plaintext | MAC | <padding> | <padding_length>
+// Which is then encrypted by the block cipher.
+// Plaintext is the data being transferred. MAC is the HMAC value over some header data and the plaintext.
+// The padding_length is a byte (range 0-255) that specifies the length of the padding.
+// The padding consists of padding_length bytes (up to 255) Each byte is equal to padding_length.
+// The padding_length is chosen so that length of the whole structure is a multiple of the block cipher block
+// size, so that it can be encrypted with CBC.
+//
+// The problem is that when decrypting this, the natural code will take actions that depend on the padding_length
+// byte before it has been authenticated, and those actions might reveal information about padding_byte. This
+// in turn can be used in an attack that lets the attacker decrypt data.
+// We are particularly concerned with software side channels, where another thread infers information about what the
+// active thread is doing through cache state and other shared CPU state.
+//
+// To address this issue once and for all, we created an implementation of the HMAC verification with the following
+// properties:
+// - It verifies the HMAC in the data structure above.
+// - This is done in a side-channel safe manner, not revealing anything except whether the structure is valid or not.
+// This means that the HMAC computation over the plaintext is constant-time and constant-memory-access pattern
+// irrespective of the padding_length; thus this is a fixed-time implementation for variable-sized inputs.
+// Similarly, the MAC value has to be extracted from a variable location in the input using a fixed memory access
+// pattern.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsCbcHmacVerify(
+    _In_                PCSYMCRYPT_MAC  pMacAlgorithm,
+    _In_                PVOID           pExpandedKey,
+    _Inout_             PVOID           pState,
+    _In_reads_(cbData)  PCBYTE          pbData,
+                        SIZE_T          cbData);
+// Verify a TLS CBC cipher suite MAC value
+//  - macAlgorithm: one of SymCryptHmacSha1Algorithm, SymCryptHmacSha256Algorithm, or SymCryptHmacSha384Algorithm.
+//      Other MAC algorithms are not supported.
+//  - pState points to an SYMCRYPT_HMAC_SHAXXX_STATE. It is allowed to process data into the state before this call,
+//      but the total # bytes processed must be < 2^16.
+//  - pbData points to a buffer containing the concatenation of plaintext, MAC, padding, and padding_length.
+//  - cbData is the size of the buffer.
+// Note: callers should pass the entire (plaintext | MAC | padding | padding_length) in a single call to get
+// the full side-channel protection.
+// This function returns success if the HMAC verification is successful.
+// It returns an error if the padding or HMAC verification fails.
+// After the call pState is wiped of any sensitive data, just like the SymCryptHmacXxxResult function.
+// Callers have to check the padding_length byte pbData[cbData-1] to determine the size of the plaintext.
+//
+
+
+
+/*
+
+Yes, despite its name, SymCrypt supports asymmetric cryptographic algorithms.
+The asymmetric implementations have the following primary design goals:
+    - Implement asymmetric cryptographic algorithms like RSA, DSA, DH, ECDSA, ECDH, etc.
+    - Protect against all software-based side-channel attacks
+    - Protect against those hardware-based side-channel attacks that can be practically protected against in software.
+    - High performance, dynamically using CPU features that are available on the current CPU stepping.
+    - Support small code and small memory environments.
+    - Support environments that need to control memory allocations.
+
+The primary use-case is for SymCrypt to be the crypto library for MS products. This includes high-performance
+scenarios such as TLS server termination, and low-footprint uses such as Bootmgr.
+SymCrypt supports applications such as firmware updates for embedded CPUs where code and memory
+footprint are of overriding importance.
+
+Side channel attacks:
+Defence against side channel attacks play an important part in the design and implementation of
+SymCrypt. Side channel attacks are a class of attacks on cryptographic systems where the attacker
+gets some information about a cryptographic computation in addition to the inputs and outputs.
+For example, any of the following information could be retrieved by the attacker:
+- The time it takes to perform a computation (either exactly or approximately)
+- The power usage over time of the CPU.
+- The noise made by the computer's power supply (a function of the CPU power consumption)
+- Which cache lines are evicted from the attacker's thread A by a computation in thread B.
+These may sound like esoteric attacks, but all of them have been used in practical demonstrations
+to attack cryptographic systems.
+
+SymCrypt uses the following API rules to protect against side-channel attacks:
+- Information is divided into two classes: public information and private information.
+- Public information is allowed to leak through side channels, and the library makes no attempt to hide
+    public information.
+- Private information is protected against side-channel attacks to the best ability of the library.
+Unless otherwise documented, all information is treated as private.
+Functions may document that a particular value is "published". This means that the function may use
+the value in a way that is not side-channel safe, so any security analysis that considers
+side-channel attacks must assume that the published value is public and known by the attacker.
+
+The following information is always assumed to be public, and thus known to any side-channel attacker:
+- Which SymCrypt function is being called.
+- The location of any of the buffers passed as arguments.
+- The size parameter of any buffer passed as an argument.
+- Any details that cause a function to return an error.
+Thus, it is important that callers who wish to be side-channel safe ensure that their buffer locations and sizes
+do not reveal any information, and that they do not make any calls that result in an error, unless there is no
+need for secrecy when an error occurs.
+
+Because pointer values are all public (the memory address cannot be hidden on modern CPUs if the buffer is accessed)
+side-channel safe code ends up using masked operations, such as masked-copy where the copy is done or not done
+depending on a mask parameter to the function.
+SymCrypt exposes a set of masked functions that applications can use for their own side-channel safe operations.
+
+The following coding rules are used to protect private information:
+- The sequence of instructions executed is independent of private information.
+- The sequence of memory operations (read/write) and memory addresses accessed is independent of private information.
+- Private information is not used in instructions whose timing may depend on the data being processed.
+As far as we know these rules stop all software-based side-channel attacks, and many hardware-based ones.
+
+One remaining line of attack is to feed the algorithm with values that are special. For example, an RSA
+decryption may receive a value that contains many zeroes modulo one prime. If the power consumption of the
+multiply instruction reveals whether one of the multiplicands is zero, then the attacker might learn
+useful information. Note that this is a pure hardware attack, it is not applicable to software attackers.
+Protecting against this style of attack is an area that still needs more research. Where applicable we
+document the additional protections that SymCrypt provides.
+
+
+Running with CHKed code:
+All binaries that use SymCrypt must build CHKed versions of the binary (linking the CHKed version of SymCrypt)
+and perform full test runs on the CHKed version.
+Due to the performance and operational requirements, the production-optimized SymCrypt library API cannot
+check all buffer sizes or even be fully SAL-annotated.
+The necessary size information is simply not available at every call point, and passing
+the size information around would add too much overhead.
+The CHKed version of the library adds additional code & per-object storage to be able to implement check that
+are broadly equivalent to what SAL would normally check.
+SAL checks are part of the SDL requirements and need to be done on all Microsoft products.
+Though this requirement cannot strictly speaking be satisfied with the SymCrypt library, running the CHKed
+version through full validation is the best equivalent, and therefore should be considered mandatory.
+
+Please ensure that the validation runs exercise all the border-cases of largest and smallest sizes, as well as
+intermediate sizes for the parameters.
+
+*/
+
+
+//
+// Caller-provided functions
+//
+// Some of the large-integer and asymmetric algorithm functions use callbacks.
+// The callback functions do not have to be functional for binaries that only use the symmetric algorithm
+// implementations.
+// Use of callbacks is documented in each function that uses them.
+//
+
+PVOID
+SYMCRYPT_CALL
+SymCryptCallbackAlloc( SIZE_T nBytes );
+//
+// Allocate a buffer of nBytes; returns NULL on failure.
+// Returned pointer must be aligned to a multiple of SYMCRYPT_ASYM_ALIGN_VALUE.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCallbackFree( PVOID pMem );
+//
+// Called by SymCrypt to free a buffer previously allocated by SymCryptCallbackAlloc().
+// Note that callers should never call these functions directly. Buffers that were returned
+// from the SymCrypt API are freed with SymCryptFree* functions, not this function.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SYMCRYPT_WEAK_SYMBOL
+SymCryptCallbackRandom(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer );
+//
+// Fill the buffer with uniformly distributed random bytes from a cryptographically strong RNG source.
+//
+
+PVOID
+SYMCRYPT_CALL
+SymCryptCallbackAllocateMutexFastInproc(void);
+//
+// Allocate and initialize a mutex object; returns NULL on failure.
+//
+// Fast indicates that users of the mutex will only hold it for a short period of time, so it
+// is not expected that threads should need to sleep before acquiring the mutex. (i.e. can be
+// implemented by a spinlock in kernel mode).
+// Inproc indicates the mutex is only used for synchronization between threads in a single process.
+//
+// Users of the library in contexts where mutexes are not available can set this callback to always
+// return NULL, and attempts to use APIs requiring it will fail at runtime.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCallbackFreeMutexFastInproc( _Inout_ PVOID pMutex );
+//
+// Free a mutex object previously created by SymCryptCallbackAllocateMutexFastInproc
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCallbackAcquireMutexFastInproc( _Inout_ PVOID pMutex );
+//
+// Take exclusive ownership of a mutex object allocated by SymCryptCallbackAllocateMutexFastInproc.
+//
+// This call must also ensure memory ordering such that stores before the previous call to
+// SymCryptCallbackReleaseMutexFastInproc with this mutex are observable by loads after this call.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCallbackReleaseMutexFastInproc( _Inout_ PVOID pMutex );
+//
+// Relinquish ownership of a mutex object allocated by SymCryptCallbackAllocateMutexFastInproc and
+// acquired by SymCryptCallbackAcquireMutexFastInproc.
+//
+
+//==============================================================================================
+// Object types for high-level API
+//
+// SYMCRYPT_RSAKEY       A key that stores the information for the RSA algorithms (encryption and signing).
+//                       It always contains the RSA parameters / public key, and may or may not contain
+//                       the associated private key.
+// SYMCRYPT_DLGROUP      A discrete log group to be used for the DSA and DH algorithms. It contains the
+//                       group parameters (P,[Q],G) (The prime Q is optional).
+// SYMCRYPT_DLKEY        A "discrete log" key that stores the information for the DSA and DH algorithms. It
+//                       always contains a public key, and may or may not contain the associated private key.
+// SYMCRYPT_ECURVE       An elliptic curve over a prime field. Contains field prime, curve parameters,
+//                       and distinguished point (generator).
+// SYMCRYPT_ECKEY        An elliptic curve key for the ECDH and ECDSA algorithms. It always contains a
+//                       public key, and may or may not contain the associated private key.
+//
+// See symcrypt_internal.h for structure definitions.
+//
+
+//==============================================================================================
+// Supported formats and parameters
+//
+
+typedef enum _SYMCRYPT_NUMBER_FORMAT {
+    SYMCRYPT_NUMBER_FORMAT_LSB_FIRST = 1,
+    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST = 2,
+} SYMCRYPT_NUMBER_FORMAT;
+//
+// SYMCRYPT_NUMBER_FORMAT is used to specify the number format for import and export
+// of BYTE arrays. We support the following two number formats:
+// Let p[0], ..., p[n-1] be an array containing n bytes:
+// LSB_FIRST:
+//      Value   = \sum_{i=0}^{n-1} p[i] * 2^{8*i}
+//              = p[0] + 2^8 * p[1] + 2^{16} * p[2] + ...
+//
+// MSB_FIRST:
+//      Value   = \sum_{i=0}^{n-1} p[n-1-i] * 2^{8*i}
+//              = p[n-1] + 2^8 * p[n-2] + 2^{16} * p[n-3] + ...
+//
+
+typedef struct _SYMCRYPT_RSA_PARAMS {
+    UINT32              version;            // Version of the parameters structure
+    UINT32              nBitsOfModulus;     // Number of bits in the modulus
+    UINT32              nPrimes;            // Number of primes, 0 if object is only for public key
+    UINT32              nPubExp;            // Number of public exponents (typically 1)
+} SYMCRYPT_RSA_PARAMS, *PSYMCRYPT_RSA_PARAMS;
+typedef const SYMCRYPT_RSA_PARAMS * PCSYMCRYPT_RSA_PARAMS;
+//
+// SYMCRYPT_RSA_PARAMS is used to specify all the parameters needed for creation of an
+// RSA key object. The above is version 1 of the parameters.
+// Currently, we only support nPubExp = 1 and nPrimes = 0 or 2.
+// Note: nPrimes > 2 and nPubExp > 1 allow faster and more flexible
+// RSA functionality. Though currently not supported, these parameters make it easy to add
+// support in the future.
+//
+
+// Notation for elliptic curve parameters and functions
+// ====================================================
+
+//  E       The elliptic curve group. This is typically represented as the set of 2D points (with
+//          coordinates from a finite field) that satisfy a specific curve equation.
+//          An example equation is y^2 = x^3 + Ax + B for A,B. The set E also
+//          contains a special "zero" point denoted by O.
+//  |E|     The total number of points on the elliptic curve group E.
+//  G       A special point in E which generates a (prime) order subgroup.
+//  GOrd    The (prime) order of the generator point G. Therefore, GOrd * G = O.
+//  h       The cofactor of the curve. It is defined as h = |E| / GOrd. Typical
+//          cofactors are 4 (NUMS curves), and 8 (curve 25519).
+
+// Definitions
+// ===========
+
+// A "proper public key" (PPK) on the curve E is defined to be an arbitrary nonzero point of the
+// subgroup generated by the point G.
+
+// A "proper secret key" (PSK) is the logarithm of a "proper public key" with
+// respect to G. Therefore, if Q is the PPK, then the corresponding PSK is the unique
+// integer s with 0 < s < GOrd such that s*G = Q.
+
+// If the cofactor of the curve is equal to 1, then the entire group E is generated by
+// the point G and all nonzero points in E are "proper public keys".
+
+// Otherwise, an arbitrary point on the curve might or might not belong to the subgroup
+// generated by G. Furthermore, in this case, an arbitrary point P may have order equal
+// to the cofactor (or smaller), i.e. h*P=O, or an order larger than GOrd.
+
+// To securely handle the cases where "non-proper" public keys are imported from possibly malicious
+// sources, the creators of curve parameters impose several restrictions on the secret keys
+// and the algorithms used. For example, the scalar multiplication algorithm for NUMS curves
+// always pre-multiplies a point by the cofactor; in order to zero-out any possible
+// components of lower order ("low-order clearing"). Curve 25519 imposes this by asserting
+// that all secret keys have the 3 lowest bits set to 0, which is equivalent to multiplying
+// by h=8.
+
+typedef enum _SYMCRYPT_ECURVE_GEN_ALG_ID {
+    SYMCRYPT_ECURVE_GEN_ALG_ID_NULL = 0,
+} SYMCRYPT_ECURVE_GEN_ALG_ID;
+//
+// SYMCRYPT_ECURVE_GEN_ALG_ID is used to specify (if available) the algorithm that
+// generates the curve parameters from the provided seed.
+//
+
+
+typedef struct _SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION {
+    UINT32  PrivateKeyDefaultFormat;
+    UINT32  HighBitRestrictionNumOfBits;
+    UINT32  HighBitRestrictionPosition;
+    UINT32  HighBitRestrictionValue;
+} SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION, *PSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION;
+typedef const SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION * PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION;
+//
+// SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION is used to specify restrictions and default formats
+// for known curves. The possible formats and restriction are explained below.
+//
+
+// Secret key formats
+// ==================
+// The possible secret key formats in SymCrypt are shown below. For all formats, s denotes
+// a "proper secret key" defined as above. I.e. 0 < s < GOrd.
+//
+//  1. "Canonical":         s
+//  2. "DivH":              s/h mod GOrd
+//  3. "DivHTimesH":        h*(s/h mod GOrd)
+//  4. "TimesH":            h*s                 <-- This format is currently unsupported
+//
+// Remarks:
+//  -   The above formats apply **only to external formats**: When somebody is
+//      importing a secret key (from test vectors, for example) or exporting a key.
+//      The internal format of the secret keys might be one of them or something totally
+//      different; the internal format is not visible to the caller.
+//  -   Formats 3 and 4 have bigger storage requirements compared to 1 and 2, as
+//      the key can be up to |E|.
+//  -   When h=1 all formats are identical. This is the case for NIST curves.
+//  -   The NUMS curves use the "DivH" secret key format in the test vectors and the
+//      multiplication algorithm implicitly multiplies by h.
+//  -   Curve 25519 uses the "DivHTimesH" secret key format in the test vectors.
+typedef enum _SYMCRYPT_ECKEY_PRIVATE_FORMAT {
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_NULL           = 0,
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL      = 1,
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH           = 2,
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH_TIMESH    = 3,
+} SYMCRYPT_ECKEY_PRIVATE_FORMAT;
+
+// High bit restrictions
+// =====================
+// A high bit restriction is a requirement for some of the high bits of the secret keys
+// (usually the most significant bits of the curve).
+// Currently only curve 25519 imposes such a restriction: That the bits 255 and 254 of the
+// secret key in the "DivHTimesH" format are 0 and 1, respectively.
+//
+// The high bit restrictions specification takes the following form:
+//     - Number of bits that are specified
+//     - Bit position of the lowest bit to be specified (starting from 0 for the LSB)
+//     - The bit values
+// The bits that are specified refer to the relevant secret key format.
+// For Canonical and DivH formats the total number of bits is the # bits of GOrd-1.
+// For DivHTimesH and TimesH formats the total number of bits is the # bits of |E|-1.
+//
+// Note: as GOrd must be prime, #bits(Gord) == #bits(Gord-1). The same is true
+// for |E|=h*GOrd as it cannot be a power of 2.
+//
+// The HighBitRestrictionNumOfBits field is a value between 0 and 32 (inclusive)
+// and specifies how many bits of the HighBitRestrictionValue are used (starting
+// from the least significant bit of the value). The bits that are restricted are
+// the bits [HighBitRestrictionPosition+HighBitRestrictionNumOfBits-1, ..., HighBitRestrictionPosition]
+//
+//      For example, let's assume it is required that the bits [104, 103, ..., 100]
+//      of all private keys of a curve are always 11011.
+//      Then the parameters should be set to
+//              HighBitRestrictionNumOfBits = 5
+//              HighBitRestrictionPosition = 100
+//              HighBitRestrictionValue = 0x1B
+//
+
+
+typedef struct _SYMCRYPT_ECURVE_PARAMS {
+    UINT32                      version;            // Version of the parameters structure (see comment below)
+    SYMCRYPT_ECURVE_TYPE        type;               // Type of the curve
+    SYMCRYPT_ECURVE_GEN_ALG_ID  algId;              // Algorithm ID for generation of parameters from seed
+    UINT32                      cbFieldLength;      // Length of the field elements in bytes
+    UINT32                      cbSubgroupOrder;    // Length of the subgroup in bytes
+    UINT32                      cbCofactor;         // Length of the cofactor in bytes
+    UINT32                      cbSeed;             // Length of the seed
+    // This struct is followed in memory by:
+    //P[cbFieldLength]      Prime of the base field
+    //A[cbFieldLength]      Coefficient A of all three types of curves
+    //B[cbFieldLength]      Coefficient B of Weierstrass and Montgomery curves and D for Twisted Edwards curves
+    //Gx[cbFieldLength]     X-coordinate of the distinguished point (assuming SYMCRYPT_ECPOINT_FORMAT_XY)
+    //Gy[cbFieldLength]     Y-coordinate of the distinguished point (assuming SYMCRYPT_ECPOINT_FORMAT_XY)
+    //n[cbSubGroupOrder]    Order of the subgroup generated by the distinguished point
+    //h[cbCofactor]         Cofactor of the distinguished point
+    //S[cbSeed]             Seed of the curve
+
+    //ParamsV2Extension[sizeof(SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION)];  // Only on version 2 of the parameters
+} SYMCRYPT_ECURVE_PARAMS, *PSYMCRYPT_ECURVE_PARAMS;
+typedef const SYMCRYPT_ECURVE_PARAMS * PCSYMCRYPT_ECURVE_PARAMS;
+//
+// SYMCRYPT_ECURVE_PARAMS is used to specify all the parameters needed for the curve generation. The above
+// are versions 1 and 2 of the curve parameters.
+//
+
+typedef enum _SYMCRYPT_ECPOINT_FORMAT {
+    SYMCRYPT_ECPOINT_FORMAT_X   = 1,   // One value, encoding the X coordinate only of a point
+    SYMCRYPT_ECPOINT_FORMAT_XY  = 2,   // Two equally-sized values, the first one encoding X and the second one encoding Y
+} SYMCRYPT_ECPOINT_FORMAT;
+//
+// SYMCRYPT_ECPOINT_FORMAT is used to support different elliptic curve point formats, including possible point compression.
+//
+
+//========================================================================
+//========================================================================
+// Main schema for object creation, deletion, and management.
+//
+// Object management is the same for most object types. For an object type XXX we have
+// the following functions:
+//
+// PSYMCRYPT_XXX
+// SYMCRYPT_CALL
+// SymCryptXxxAllocate( <size parameters> )
+//  Allocates an object of type XXX according to the specified size parameters.
+//  If the allocation fails, NULL is returned.
+//  If the allocation succeeds, an XXX pointer is returned, and the caller is responsible
+//  for freeing the result using SymCryptXxxFree().
+//  The value of the new object is undefined.
+//  All the parameters to this function are published. (Object sizes cannot be private information.)
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxFree( _Inout_ PSYMCRYPT_XXX p )
+//  Free an XXX object allocated with SymCryptAllocateXxx().
+//  Any storage location in the object that might have contained private information is wiped.
+//
+// UINT32
+// SYMCRYPT_CALL
+// SymCryptSizeofXxxFromYyy( <size parameters> );
+//  Memory size that is sufficient to store an XXX object with size defined by the <size parameters>.
+//  The Yyy specifies the form of the size parameters, for example Ecurve.
+//  This is a runtime function as the size of an object is a run-time decision dependent on the CPU stepping.
+//  The result is always a multiple of the alignment requirements of this object type, so arrays can be built
+//  using this element size.
+//
+// SYMCRYPT_SIZEOF_XXX_FROM_YYY( <size parameters> )
+//  This is a compile-time macro that computes a value not less than the SymCryptSizeofXxxFromYyy function, and
+//  is suitable to statically compute the size of a memory buffer for an object.
+//  (Not defined for all types.)
+//
+// PSYMCRYPT_XXX
+// SYMCRYPT_CALL
+// SymCryptXxxCreate(
+//      _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+//                                      SIZE_T  cbBuffer,
+//                                      <size parameters> );
+//  Create an XXX object from the provided (pbBuffer, cbBuffer) space.
+//  This function performs the necessary initializations of the object, but does not assign or set a value.
+//  The object will be able to store values up to size determined by the <size parameters>.
+//  Requirement:
+//      - pbBuffer is aligned to SYMCRYPT_ASYM_ALIGN_VALUE. Note that this can be a stricter requirement than
+//          SYMCRYPT_ALIGNED, and memory allocation functions might not return pointers that are suitably
+//          aligned. For some object types and some CPUs, the alignment requirements might be less strict.
+//          The main purpose of this relaxation is to always allow objects that are spaced
+//          SymCryptSizeofXxxFromYyy apart. The common usage is to create an array of objects. The array
+//          starts at a SYMCRYPT_ASYM_ALIGNed location, with each element SymCryptSizeofXxxFromYyy(..) bytes long.
+//      - cbBuffer >= SymCryptSizeofXxxFromYyy( <size parameters> )
+//      - (pbBuffer,cbBuffer) memory must be exclusively used by this object.
+//  The last requirement ensures that all objects are non-overlapping (except for API functions
+//  that explicitly create overlapping objects).
+//  All parameters are published.
+//  It is always safe to choose
+//      cbBuffer = SymCryptSizeofXxxFromYyy( <size parameters> )
+//  The returned object pointer is simply a cast of the pbBuffer pointer.
+//  Callers that manage arrays of objects can reconstruct the PSYMCRYPT_XXX by casting the buffer pointer
+//  to the right type.
+//  An object that is created with this function should be wiped, even if it doesn't contain private data.
+//  The SymCryptXxxWipe() function also frees any associated data that the library may maintain.
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxWipe( _Out_ PSYMCRYPT_XXX  Dst )
+//  All private information in the Dst object is wiped, and any associated data is freed.
+//  Unless otherwise specified, the Dst object is left in an undefined state.
+//  An SymCryptXxxAllocate-d object does not have to be wiped before it is freed
+//  because the SymCryptXxxFree function will perform the wipe.
+//  However, SymCryptXxxCreate-d objects should always be wiped even if they don't contain
+//  secret data, as the wipe also frees any associated data the library may maintain.
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptXxxCopy(
+//      _In_ PCSYMCRYPT_XXX    pxSrc,
+//      _Out_PSYMCRYPT_XXX     pxDst );
+// Dst = Src.
+//  Requirement: The <size parameters> of both objects should the same.
+//  Src must be in a defined state, it is not valid to copy an undefined object.
+//  Src and Dst may be the same object (though that is a no-op).
+//
+
+//========================================================================
+// RSAKEY objects' API
+//
+
+#define SYMCRYPT_SIZEOF_RSAKEY_FROM_PARAMS( modBits, nPrimes, nPubExps ) \
+    SYMCRYPT_INTERNAL_SIZEOF_RSAKEY_FROM_PARAMS( modBits, nPrimes, nPubExps )
+// Return a buffer size large enough to create an RSA key in which the specified
+// modulus size, # primes, # public exponents, and upper bound for the bitsize of each public exponent.
+// If the object will only contain a public key, nPrimes can be set to 0
+
+PSYMCRYPT_RSAKEY
+SYMCRYPT_CALL
+SymCryptRsakeyAllocate(
+    _In_    PCSYMCRYPT_RSA_PARAMS   pParams,
+    _In_    UINT32                  flags );
+//
+// Allocate and create a new RSAKEY object sized according to the parameters.
+// If the SYMCRYPT_RSAKEY object will only be used for a public key, the
+// SYMCRYPT_RSA_PARAMS structure may set nPrimes = 0. Use of
+// SymCryptRsakeySetValueFromPrivateExponent requires nPrimes = 2.
+//
+// This call does not initialize the key. It should be
+// followed by a call to SymCryptRsakeyGenerate or
+// SymCryptRsakeySetValue*.
+//
+// No flags are specified for this function.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsakeyFree( _Out_ PSYMCRYPT_RSAKEY pkObj );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofRsakeyFromParams( _In_ PCSYMCRYPT_RSA_PARAMS pParams );
+// If the to-be-allocated SYMCRYPT_RSAKEY object will only be used for a public key, the
+// SYMCRYPT_RSA_PARAMS structure may set nPrimes = 0.
+
+PSYMCRYPT_RSAKEY
+SYMCRYPT_CALL
+SymCryptRsakeyCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE                   pbBuffer,
+                                    SIZE_T                  cbBuffer,
+    _In_                            PCSYMCRYPT_RSA_PARAMS   pParams );
+//
+// Create an RSAKEY object from a buffer, but does not initialize it.
+// If the SYMCRYPT_RSAKEY object will only be used for a public key, the
+// SYMCRYPT_RSA_PARAMS structure may set nPrimes = 0. Use of
+// SymCryptRsakeySetValueFromPrivateExponent requires nPrimes = 2.
+//
+// This call does not initialize the key. It should be
+// followed by a call to SymCryptRsakeyGenerate or
+// SymCryptRsakeySetValue*.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsakeyWipe( _Out_ PSYMCRYPT_RSAKEY pkDst );
+
+//
+//VOID
+//SYMCRYPT_CALL
+//SymCryptRsakeyCopy(
+//    _In_    PCSYMCRYPT_RSAKEY  pkSrc,
+//    _Out_   PSYMCRYPT_RSAKEY   pkDst );
+//
+// This function is currently not available.
+//
+
+//========================================================================
+// DLGROUP objects' API
+//
+
+PSYMCRYPT_DLGROUP
+SYMCRYPT_CALL
+SymCryptDlgroupAllocate( UINT32  nBitsOfP, UINT32  nBitsOfQ );
+//
+// Allocate a Discrete Logarithm group object suitable for the given sizes.
+//
+// nBitsOfP: Maximum number of bits of the field prime P. Specifying a value larger
+//  than the actual size is allowed, but inefficient.
+// nBitsOfQ: Maximum number of bits of the group order Q. Specify the size of Q,
+//  or 0 if the size of Q is not (yet) known.
+//
+// This call does not initialize the DLGROUP. It should be followed
+// by a call to SymCryptDlgroupGenerate or SymCryptDlgroupSetValue.
+//
+// nBitsOfQ is allowed to be equal to 0 and signifies that the size of Q
+// is unknown or Q does not exist. This may be used when creating a DLGROUP
+// for the DH algorithm which does not use a prime Q.
+//
+// Setting nBitsOfQ to something bigger than 0 signifies that the size of
+// the prime Q is known and if a future caller tries to import a bigger Q then
+// the SymCryptDlgroupSetValue call will fail.
+//
+// Technically nBitsOfQ should always be strictly less than nBitsOfP, as Q divides
+// P-1. For simplicity, it is allowed that callers specify nBitsOfQ equal to nBitsOfP
+// in this call, but SymCrypt will treat this as setting nBitsOfQ to (nBitsOfP-1).
+//
+// Setting nBitsOfQ to 0 might result in a bigger size of the DLGROUP object
+// compared to setting it to a specific size (see SymCryptSizeofDlgroupFromBitsizes).
+//
+// Requirements:
+//  - nBitsOfP >= nBitsOfQ
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupFree( _Out_ PSYMCRYPT_DLGROUP pgObj );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofDlgroupFromBitsizes( UINT32 nBitsOfP, UINT32 nBitsOfQ );
+//
+// This call returns the memory size that is sufficient to store a
+// DLGROUP object with primes P,Q of size nBitsOfP and nBitsOfQ,
+// respectively (L,N parameters in FIPS 186-3 specs).
+//
+// Requirements:
+//  - nBitsOfP >= nBitsOfQ
+//
+// Remarks:
+//  - The value in nBitsOfQ is allowed to be equal to 0
+//  (see SymCryptDlgroupAllocate).
+//
+//  - When nBitsOfQ!=0 this is a monotonic function w.r.t. a partial order on N^2.
+//    I.e. for all fixed (nBitsOfP_0,nBitsOfQ_0) and (nBitsOfP_1,nBitsOfQ_1) with
+//       nBitsOfQ_0>0 and nBitsOfQ_1>0,
+//
+//      (nBitsOfP_0<=nBitsOfP_1 AND nBitsOfQ_0<=nBitsOfQ_1) implies that
+//          F(nBitsOfP_0,nBitsOfQ_0) <= F(nBitsOfP_1,nBitsOfQ_1)
+//      where F is the function SymCryptSizeofDlgroupFromBitsizes.
+//
+//  - F(nBitsOfP, 0)=F(nBitsOfP, nBitsOfP-1). Thus when nBitsOfQ==0 the
+//    function takes the maximum value for a fixed nBitsOfP.
+//
+
+PSYMCRYPT_DLGROUP
+SYMCRYPT_CALL
+SymCryptDlgroupCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    UINT32              nBitsOfP,
+                                    UINT32              nBitsOfQ );
+//
+// Creates a DL group object, but does not initialize it. It must be followed
+// by a call to SymCryptDlgroupGenerate or SymCryptDlgroupSetValue.
+//
+// - pbBuffer,cbBuffer: memory buffer to create the object out of. The required size
+//  can be computed with SymCryptSizeofDlgroupFromBitsizes().
+// - nBitsOfP: number of bits of the field prime P.
+// - nBitsOfQ: number of bits of the group order Q, or 0 if the size of Q is not (yet) known.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupWipe( _Out_ PSYMCRYPT_DLGROUP pgDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupCopy(
+    _In_    PCSYMCRYPT_DLGROUP   pgSrc,
+    _Out_   PSYMCRYPT_DLGROUP    pgDst );
+
+//========================================================================
+// DLKEY objects' API
+//
+
+PSYMCRYPT_DLKEY
+SYMCRYPT_CALL
+SymCryptDlkeyAllocate( _In_ PCSYMCRYPT_DLGROUP pDlgroup );
+//
+// This call does not initialize the key. It should be
+// followed by a call to SymCryptDlkeyGenerate or
+// SymCryptDlkeySetValue.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlkeyFree( _Out_ PSYMCRYPT_DLKEY pkObj );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofDlkeyFromDlgroup( _In_ PCSYMCRYPT_DLGROUP pDlgroup );
+
+PSYMCRYPT_DLKEY
+SYMCRYPT_CALL
+SymCryptDlkeyCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+    _In_                            PCSYMCRYPT_DLGROUP  pDlgroup );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlkeyWipe( _Out_ PSYMCRYPT_DLKEY pkDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlkeyCopy(
+    _In_    PCSYMCRYPT_DLKEY   pkSrc,
+    _Out_   PSYMCRYPT_DLKEY    pkDst );
+
+//========================================================================
+// ECURVE objects' API is slightly different than the above API schema because of the close
+// relation to multiple parameters, the fact that they contain public information,
+// and that they are persisted by the callers.
+// Thus, the Allocate function takes in all the curve parameters and there are no Create,
+// Wipe, or Copy functions.
+//
+
+PSYMCRYPT_ECURVE
+SYMCRYPT_CALL
+SymCryptEcurveAllocate(
+    _In_    PCSYMCRYPT_ECURVE_PARAMS    pParams,
+    _In_    UINT32                      flags );
+//
+// Allocate memory and create an ECURVE object which is defined
+// by the parameters in pParams.
+//
+// - pParams: parameters that define the curve
+// - flags: Not used, must be zero.
+//
+// Future versions might use the flags to enable different features/tradeoffs.
+// There are a number of interesting memory/speed/pre-computation cost trades that can be made.
+// For example, pre-computing multiples of the distinguished point, or (parallel?) pre-computation
+// of (r, rG) pairs for random r values.
+//
+// This function applies limited validation of the pParams. The validation is intended to eliminate
+// the threat of denial-of-service when hostile parameters are presented. It does not ensure that
+// the parameters make sense, define a proper curve, or that any elliptic-curve operations made on
+// the curve built from these parameters will fail, succeed or provide any security.
+// The only guarantee provided for invalid parameters is that all operations on this curve will
+// not crash and will return in some reasonable amount of time.
+//
+// Returns NULL if out of memory or the parameters are deemed invalid.
+// If the return value is not NULL, the object must later be freed with SymCryptEcurveFree().
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcurveFree( _Out_ PSYMCRYPT_ECURVE pCurve );
+
+//========================================================================
+// ECKEY objects' API is slightly different than the above API schema in the sense that they
+// take as input an ECURVE object pointer instead of the number of digits.
+//
+
+PSYMCRYPT_ECKEY
+SYMCRYPT_CALL
+SymCryptEckeyAllocate( _In_  PCSYMCRYPT_ECURVE pCurve );
+
+VOID
+SYMCRYPT_CALL
+SymCryptEckeyFree( _Out_ PSYMCRYPT_ECKEY pkObj );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofEckeyFromCurve( _In_ PCSYMCRYPT_ECURVE pCurve );
+
+PSYMCRYPT_ECKEY
+SYMCRYPT_CALL
+SymCryptEckeyCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    PCSYMCRYPT_ECURVE   pCurve );
+
+VOID
+SYMCRYPT_CALL
+SymCryptEckeyWipe( _Out_ PSYMCRYPT_ECKEY pkDst );
+
+VOID
+SymCryptEckeyCopy(
+    _In_    PCSYMCRYPT_ECKEY  pkSrc,
+    _Out_   PSYMCRYPT_ECKEY   pkDst );
+
+
+//=====================================================
+// Flags for asymmetric key generation and import
+
+// These flags are introduced primarily for FIPS purposes. For FIPS 140-3 rather than expose to the
+// caller the specifics of what tests will be run with various algorithms, we are sanitizing flags
+// provided on asymmetric key generation and import to enable the caller to indicate their intent,
+// and for SymCrypt to perform the required testing.
+// Below we define the flags that can be passed and when a caller should set them.
+// The specifics of what tests will be run are likely to change over time, as FIPS requirements and
+// our understanding of how best to implement them, change over time. Callers should not rely on
+// specific behavior.
+
+
+// Validation required by FIPS is enabled by default. This flag enables a caller to opt out of this
+// validation.
+#define SYMCRYPT_FLAG_KEY_NO_FIPS               (0x100)
+
+// When opting out of FIPS, SymCrypt may still perform some sanity checks on key import
+// In very performance sensitive situations where a caller strongly trusts the values it is passing
+// to SymCrypt and does not care about FIPS (or can statically prove properties about the imported
+// keys), a caller may specify SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION in addition to
+// SYMCRYPT_FLAG_KEY_NO_FIPS to skip costly checks
+#define SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION    (0x200)
+
+// Callers must specify what algorithm(s) a given asymmetric key will be used for.
+// This information will be tracked by SymCrypt, and attempting to use the key in an algorithm it
+// was not generated or imported for will result in failure.
+// If no algorithm is specified then the key generation or import function will fail.
+#define SYMCRYPT_FLAG_DLKEY_DSA         (0x1000)
+#define SYMCRYPT_FLAG_DLKEY_DH          (0x2000)
+
+#define SYMCRYPT_FLAG_ECKEY_ECDSA       (0x1000)
+#define SYMCRYPT_FLAG_ECKEY_ECDH        (0x2000)
+
+#define SYMCRYPT_FLAG_RSAKEY_SIGN       (0x1000)
+#define SYMCRYPT_FLAG_RSAKEY_ENCRYPT    (0x2000)
+
+//=====================================================
+// RSA key operations
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptRsakeyHasPrivateKey( _In_ PCSYMCRYPT_RSAKEY pkRsakey );
+//
+// Returns TRUE if the pkRsakey object has private key information.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeySizeofModulus( _In_ PCSYMCRYPT_RSAKEY pkRsakey );
+//
+//  Returns the (tight) size in bytes of a byte array big enough to store
+//  the modulus of the key.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeyModulusBits( _In_ PCSYMCRYPT_RSAKEY pkRsakey );
+//
+// Return the number of bits in the RSA modulus
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeySizeofPublicExponent(
+    _In_    PCSYMCRYPT_RSAKEY pRsakey,
+            UINT32            index );
+//
+// Returns the (tight) size in bytes of a byte array big enough to store
+// the public exponent. The index specifies the index
+// of the public exponent, starting with 0.
+//
+// Remarks:
+// - Currently, only one public exponent is supported, i.e. the only
+//  valid index is 0.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeySizeofPrime(
+    _In_    PCSYMCRYPT_RSAKEY pkRsakey,
+            UINT32            index );
+//
+//  Returns the (tight) size in bytes of a byte array big enough to store
+//  the selected prime of the key. The index specifies the index of the
+//  prime, starting at 0.
+//
+//  Remarks:
+//  - Currently, only two prime RSA is supported, i.e. the only
+//  valid indexes are 0 and 1.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeyGetNumberOfPublicExponents( _In_ PCSYMCRYPT_RSAKEY pkRsakey );
+//
+//  Returns the number of public exponents stored in the key.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeyGetNumberOfPrimes( _In_ PCSYMCRYPT_RSAKEY pkRsakey );
+//
+//  Returns the number of primes stored in the key.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyGenerate(
+    _Inout_                     PSYMCRYPT_RSAKEY    pkRsakey,
+    _In_reads_opt_( nPubExp )   PCUINT64            pu64PubExp,
+                                UINT32              nPubExp,
+    _In_                        UINT32              flags );
+//
+// Generate a new random RSA key using the information from the
+// parameters passed to SymCryptRsaKeyAllocate/SymCryptRsaKeyCreate.
+// PubExp is the array of nPubExp public exponent values, specifying
+// the public exponents for the key.
+// nPubExp must match the # public exponents in the parameters.
+// If pu64PubExp == NULL, nPubExp == 0, and the key requires only one
+// public exponent, then the default exponent 2^16 + 1 is used.
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - At least one of the flags indicating what the Rsakey is to be used for must be specified:
+//      SYMCRYPT_FLAG_RSAKEY_SIGN
+//      SYMCRYPT_FLAG_RSAKEY_ENCRYPT
+
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeySetValue(
+    _In_reads_bytes_( cbModulus )   PCBYTE                  pbModulus,
+                                    SIZE_T                  cbModulus,
+    _In_reads_( nPubExp )           PCUINT64                pu64PubExp,
+                                    UINT32                  nPubExp,
+    _In_reads_opt_( nPrimes )       PCBYTE *                ppPrimes,
+    _In_reads_opt_( nPrimes )       SIZE_T *                pcbPrimes,
+                                    UINT32                  nPrimes,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                    UINT32                  flags,
+    _Inout_                         PSYMCRYPT_RSAKEY        pkRsakey );
+//
+// Import key material to an RSAKEY object. The arguments are the following:
+//  - pbModulus is a pointer to a byte buffer of cbModulus bytes. It cannot be NULL.
+//  - pu64PubExp is a pointer to an array of nPubExp UINT64 exponent values.
+//    nPubExp must match the RSA parameters used to create the key object.
+//  - ppPrimes is an array of nPrimes pointers that point to byte buffers storing
+//    the primes. pcbPrimes is an array of nPrimes sizes such that
+//    the size of ppPrimes[i] is equal to pcbPrimes[i] for each i in [0, nPrimes-1].
+//  - numFormat specifies the number format for all inputs
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION
+//   Opt-out of performing almost all validation - must be specified with SYMCRYPT_FLAG_KEY_NO_FIPS
+//
+// - At least one of the flags indicating what the Rsakey is to be used for must be specified:
+//      SYMCRYPT_FLAG_RSAKEY_SIGN
+//      SYMCRYPT_FLAG_RSAKEY_ENCRYPT
+//
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+//  Remarks:
+//  - Modulus and all primes are stored in the same format specified by numFormat.
+//  - ppPrimes, pcbPrimes, and nPrimes can be NULL, NULL, and 0 respectively, when
+//    importing a public key.
+//  - Currently, the only acceptable value of nPubExps is 1.
+//  - Currently, the only acceptable value of nPrimes is 2 or 0.
+//  - Elements of ppPrimes must represent prime numbers.
+// We allow separate sizes for each prime. This seems redundant because all primes
+// are approximately the same size. However, some storage/encoding formats, such as ASN.1,
+// strip leading zeroes, or add an additional leading zero depending on the situation.
+// Allowing separate sizes avoids the need for the caller to make a copy of the data
+// into a possibly slightly larger buffer.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeySetValueFromPrivateExponent(
+    _In_reads_bytes_( cbModulus )           PCBYTE                  pbModulus,
+                                            SIZE_T                  cbModulus,
+                                            UINT64                  u64PubExp,
+    _In_reads_bytes_( cbPrivateExponent )   PCBYTE                  pbPrivateExponent,
+                                            SIZE_T                  cbPrivateExponent,
+                                            SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                            UINT32                  flags,
+    _Inout_                                 PSYMCRYPT_RSAKEY        pkRsakey );
+//
+// Import private key to an RSAKEY object using a private exponent. This is not generally
+// recommended - where possible it is more efficient to import a private key using primes
+// with SymCryptRsakeySetValue.
+//
+// The arguments are the following:
+//  - pbModulus is a pointer to a byte buffer of cbModulus bytes. It cannot be NULL.
+//  - u64PubExp is a UINT64 public exponent value.
+//  - pbPrivateExponent is a pointer to a byte buffer of cbPrivateExponent bytes. It
+//    cannot be NULL.
+//  - numFormat specifies the number format for all inputs
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION
+//   Opt-out of performing almost all validation - must be specified with SYMCRYPT_FLAG_KEY_NO_FIPS
+//
+// - At least one of the flags indicating what the Rsakey is to be used for must be specified:
+//      SYMCRYPT_FLAG_RSAKEY_SIGN
+//      SYMCRYPT_FLAG_RSAKEY_ENCRYPT
+//
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+// Remarks:
+//
+// Modulus and Private exponent are stored in the same format specified by numFormat.
+//
+// Internally this attempts to recover a pair of primes (p1, p2) that factorize Modulus.
+// This procedure has following assumptions:
+//      Modulus (n) is the product of two prime factors, p1 and p2
+//      e*d == 1 modulo LCM(p1-1, p2-1)
+//      e*d != 1 modulo 2^64
+// If any of these assumptions are not met, then the method may fail.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyGetValue(
+    _In_                            PCSYMCRYPT_RSAKEY       pkRsakey,
+    _Out_writes_bytes_( cbModulus ) PBYTE                   pbModulus,
+                                    SIZE_T                  cbModulus,
+    _Out_writes_opt_( nPubExp )     PUINT64                 pu64PubExp,
+                                    UINT32                  nPubExp,
+    _Out_writes_opt_( nPrimes )     PBYTE *                 ppPrimes,
+    _In_reads_opt_( nPrimes )       SIZE_T *                pcbPrimes,
+                                    UINT32                  nPrimes,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                    UINT32                  flags );
+//
+// Export key material from an RSAKEY object. The arguments are the following:
+//  - pbModulus is a pointer to a byte buffer of cbModulus bytes.
+//  - pu64PubExp is an pointer to an array of nPubExp elements that receives the public exponent values.
+//    nPubExp must match the # public exponents in pkRsaKey.
+//  - ppPrimes is an array of nPrimes pointers that point to byte buffers storing
+//    the primes. pcbPrimes is an array of nPrimes sizes such that
+//    the size of ppPrimes[i] is equal to pcbPrimes[i] for each i in [0, nPrimes-1].
+//  Remarks:
+//  - All parameters are stored in the same format specified by numFormat.
+//  - ppPrimes, pcbPrimes, and nPrimes can be NULL, NULL, and 0 respectively, when
+//    exporting a public key.
+//  - Currently, the only acceptable value of nPubExp is 1 or 0.
+//  - Currently, the only acceptable value of nPrimes is 2 or 0.
+// We use separate sizes for each prime. This supports the tight encoding
+// used by CNG export blobs, and uses the same format as RsakeySetValue
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyGetCrtValue(
+    _In_                                        PCSYMCRYPT_RSAKEY       pkRsakey,
+    _Out_writes_opt_(nCrtExponents)             PBYTE *                 ppCrtExponents,
+    _In_reads_(nCrtExponents)                   SIZE_T *                pcbCrtExponents,
+                                                UINT32                  nCrtExponents,
+    _Out_writes_bytes_opt_(cbCrtCoefficient)    PBYTE                   pbCrtCoefficient,
+                                                SIZE_T                  cbCrtCoefficient,
+    _Out_writes_bytes_opt_(cbPrivateExponent)   PBYTE                   pbPrivateExponent,
+                                                SIZE_T                  cbPrivateExponent,
+                                                SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                                UINT32                  flags);
+//
+// Export Crt key material from an RSAKEY object. The arguments are the following:
+//    ppCrtExponents is an array of nCrtExponent pointers that point to byte buffers
+//    storing the Crt exponents. That is,  d mod p-1, d mod q-1.
+//    pcbCrtExponents is an array of nCrtExponent sizes such that
+//    the size of ppCrtExponents[i] is equal to pcbCrtExponents[i] for each i in [0, nCrtExponent-1]
+//    pbCrtCoefficient is a pointer to a byte buffer of cbCrtCoefficient bytes, that is q^{-1} mod p
+//    pbPrivateExponent is a pointer to a byte buffer of cbPrivateExponent bytes, that is, d.
+
+//  Remarks:
+//  - All parameters are stored in the same format specified by numFormat.
+//  - ppCrtExponents, pcbCrtExponents, and nCrtExponent can be NULL, NULL, and 0 respectively
+//  - Currently, the only acceptable value of nCrtExponent is 2 or 0.
+//    pbCrtCoefficient, pbPrivateExponent can be NULL;
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyExtendKeyUsage(
+    _Inout_ PSYMCRYPT_RSAKEY    pkRsakey,
+            UINT32              flags );
+//
+// Enable an existing key which has been generated or imported to be used in specified algorithms.
+// Some callers may not know at key generation or import time what algorithms a key will be used for
+// and this API allows the key to be extended for use in additional algorithms. Use of this API may
+// not be compliant with FIPS 140-3
+//
+// - flags must be some bitwise OR of the following flags:
+//      SYMCRYPT_FLAG_RSAKEY_SIGN
+//      SYMCRYPT_FLAG_RSAKEY_ENCRYPT
+
+#define SYMCRYPT_DLGROUP_FIPS_LATEST    (SYMCRYPT_DLGROUP_FIPS_186_3)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGenerate(
+    _In_    PCSYMCRYPT_HASH         hashAlgorithm,
+    _In_    SYMCRYPT_DLGROUP_FIPS   fipsStandard,
+    _Inout_ PSYMCRYPT_DLGROUP       pDlgroup );
+//
+// Generate a Discrete Logarithm Group for use in Diffie-Hellman and DSA.
+//
+// - hashAlgorithm: Hash algorithm to be used for generating the group (if required by the algorithm)
+// - fipsStandard: Which FIPS standard algorithm to use for generating the group.
+// - pDlgroup: group object that will be initialized with a newly generated group.
+//
+// pDlGroup must have been created with SymCryptDlgroupAllocate() or SymCryptDlgroupCreate().
+//
+// If nBitsOfQ was equal to 0 when the DLGROUP was Allocate-d/Create-d
+// (and only in this case), then this function picks a default size
+// for the prime Q according to the following table:
+//      - If        nBitsOfP <=  160 then the function fails with SYMCRYPT_FIPS_FAILURE
+//      - If  160 < nBitsOfP <= 1024 then nBitsOfQ = 160
+//      - If 1024 < nBitsOfP <= 2048 then nBitsOfQ = 256
+//      - If 2048 < nBitsOfP         then nBitsOfQ = 256
+//
+// If fipsStandard == SYMCRYPT_DLGROUP_FIPS_NONE then no FIPS compliance is requested.
+// The code defaults to SYMCRYPT_DLGROUP_FIPS_LATEST.
+//
+// The requirements below address the parameter values after the defaults have been substituted
+// for nBitsOfQ and fipsStandard.
+//
+// Requirements:
+//  - pDlgroup!=NULL. Otherwise it returns SYMCRYPT_INVALID_ARGUMENT.
+//
+//  - If fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2, hashAlgorithm MUST be equal to
+//    NULL, and nBitsOfQ <= 160 or nBitsOfQ = 0 && nBitsOfP <= 1024.
+//
+//  - If fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_3, then hashAlgorithm MUST NOT be equal
+//    to NULL.
+//
+//  - If nBitsOfHash is the number of bits of the output block of hashAlgorithm,
+//    it is required that:
+//              nBitsOfQ <= nBitsOfHash <= nBitsOfP
+//    (where nBitsOfQ>0 was either provided by the caller of Allocate/Create
+//    or it was picked from the above table).
+//
+//  - For FIPS 186-2, we have that nBitsOfHash == 160 (SHA1 output size). Therefore
+//    this flag can only work with nBitsOfQ up to 160 bits. Anything else will
+//    return SYMCRYPT_INVALID_ARGUMENT.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupSetValueSafePrime(
+            SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE   dhSafePrimeType,
+    _Inout_ PSYMCRYPT_DLGROUP                   pDlgroup );
+//
+// Sets a Discrete Logarithm Group for use in Diffie-Hellman using a named safe-prime group.
+//
+// - dhSafePrimeType: The type of named safe-prime group to use
+//
+// pDlGroup must have been created with SymCryptDlgroupAllocate() or SymCryptDlgroupCreate().
+//
+// Selects the largest named safe-prime group that will fit in the allocated Dlgroup (based on the
+// values of nBitsOfP and nBitsOfQ used in allocation). It is recommended that callers set nBitsOfQ
+// to 0 in allocation (equivalent to nBitsOfQ = (nBitsOfP-1)) when creating a safe-prime group.
+//
+// Requirements:
+//  - pDlgroup was allocated with sufficient bits for the selected P (and Q) to fit. If there is no
+//    named safe-prime group with bit size <= the allocated size, it returns SYMCRYPT_INVALID_ARGUMENT.
+//    The minimum currently supported bitsize of named safe-prime groups is nBitsOfP = 2048.
+//
+//  - dhSafePrimeType!=SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_NONE. Otherwise it returns SYMCRYPT_INVALID_ARGUMENT.
+//
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptDlgroupIsSame(
+    _In_    PCSYMCRYPT_DLGROUP  pDlgroup1,
+    _In_    PCSYMCRYPT_DLGROUP  pDlgroup2 );
+//
+// Returns true if pDlgroup1 and pDlgroup2 have same set of P and G, false otherwise.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupGetSizes(
+    _In_    PCSYMCRYPT_DLGROUP  pDlgroup,
+    _Out_   SIZE_T*             pcbPrimeP,
+    _Out_   SIZE_T*             pcbPrimeQ,
+    _Out_   SIZE_T*             pcbGenG,
+    _Out_   SIZE_T*             pcbSeed );
+//
+// It returns the tight byte-sizes of each parameter of the group: prime P,
+// prime Q, generator G, and the FIPS domain_parameter_seed.
+//
+// If one of the pointers is NULL then the corresponding size is ignored.
+//
+// Remarks:
+//  - If the group has no prime Q, then the returned sizes in *pcbPrimeQ and
+//    *pcbSeed will be 0.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupSetValue(
+    _In_reads_bytes_( cbPrimeP )    PCBYTE                  pbPrimeP,
+                                    SIZE_T                  cbPrimeP,
+    _In_reads_bytes_( cbPrimeQ )    PCBYTE                  pbPrimeQ,
+                                    SIZE_T                  cbPrimeQ,
+    _In_reads_bytes_( cbGenG )      PCBYTE                  pbGenG,
+                                    SIZE_T                  cbGenG,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+    _In_opt_                        PCSYMCRYPT_HASH         pHashAlgorithm,
+    _In_reads_bytes_( cbSeed )      PCBYTE                  pbSeed,
+                                    SIZE_T                  cbSeed,
+                                    UINT32                  genCounter,
+                                    SYMCRYPT_DLGROUP_FIPS   fipsStandard,
+    _Inout_                         PSYMCRYPT_DLGROUP       pDlgroup );
+//
+// Import key material to a DLGROUP object.
+//  - Prime P is NOT optional and should always be imported.
+//  - Prime Q is an optional parameter that may or may not be imported. If not
+//    the group will not have a prime Q.
+//  - Generator G is an optional parameter. However, if not present, the
+//    algorithm will generate a random G of order Q. If both Q and G are missing
+//    the calls fails with SYMCRYPT_INVALID_ARGUMENT.
+//  - The parameters pHashAlgorithm, pbSeed, cbSeed and genCounter are the generation
+//    parameters of the FIPS standards. If fipsStandard is not equal to
+//    SYMCRYPT_DLGROUP_FIPS_NONE, the algorithm verifies that the input P,Q,G parameters are properly
+//    generated by the corresponding standard.
+//    If there is any discrepancy the function returns SYMCRYPT_AUTHENTICATION_FAILURE.
+//    Notice that these parameters are imported even if they aren't verified.
+//
+// Requirements:
+//  - The number stored in pbPrimeP and pbGenG must have at most nBitsOfP significant bits.
+//    Otherwise the function returns SYMCRYPT_INVALID_ARGUMENT.
+//  - The number stored in pbPrimeQ must have at most nBitsOfQ where nBitsOfQ is either
+//    the **non-zero** value input in the call of Allocate/Create or equal to nBitsOfP if
+//    0 was input.
+//    Otherwise the function returns SYMCRYPT_INVALID_ARGUMENT.
+//  - The size of the seed cbSeed must be **exactly** equal to the byte-size of the imported
+//    modulus Q. Otherwise the function returns SYMCRYPT_INVALID_ARGUMENT.
+//
+// Remarks:
+//  - The buffers pbPrimeP, pbPrimeQ, pbGenG must all have the same number
+//    format defined by numFormat.
+//  - Primes P and (when provided) Q must represent prime numbers.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGetValue(
+    _In_                            PCSYMCRYPT_DLGROUP      pDlgroup,
+    _Out_writes_bytes_( cbPrimeP )  PBYTE                   pbPrimeP,
+                                    SIZE_T                  cbPrimeP,
+    _Out_writes_bytes_( cbPrimeQ )  PBYTE                   pbPrimeQ,
+                                    SIZE_T                  cbPrimeQ,
+    _Out_writes_bytes_( cbGenG )    PBYTE                   pbGenG,
+                                    SIZE_T                  cbGenG,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+    _Out_                           PCSYMCRYPT_HASH *       ppHashAlgorithm,
+    _Out_writes_bytes_( cbSeed )    PBYTE                   pbSeed,
+                                    SIZE_T                  cbSeed,
+    _Out_                           PUINT32                 pGenCounter );
+
+//
+// Retrieve the group parameters from a DLGROUP. The buffers should be
+// allocated by the caller. If a pbXXX parameter is NULL (and the cbXXX==0)
+// then this parameter is not returned.
+//
+// Requirements:
+//  - All the buffers must have size at least equal to the corresponding
+//    size returned by SymCryptDlgroupGetSizes. For the pbSeed buffer the
+//    size must be **exactly** equal to the size returned from SymCryptDlgroupGetSizes.
+//
+// Remarks:
+//  - If the caller requests a Q but the group does not have one, this function
+//    will fail with SYMCRYPT_INVALID_BLOB.
+//  - The return value of *ppHashAlgorithm can be NULL if the group was generated
+//    by FIPS 186-2.
+//
+
+//=====================================================
+// DL flags
+//
+// Also see Generic key validation flags above
+
+// SYMCRYPT_FLAG_DLKEY_GEN_MODP:
+// When set on SymCryptDlkeyGenerate call, generate a private key between 1 and P-2.
+// When Q is known, this overrides the default behavior of generating a private key between 1 and Q-1,
+// or 1 and min(2^nBitsPriv-1, Q-1) for named safe-prime groups
+// When Q is not known, this does not affect the behavior
+#define SYMCRYPT_FLAG_DLKEY_GEN_MODP  (0x01)
+
+//=====================================================
+// DL key operations
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeySetPrivateKeyLength( _Inout_ PSYMCRYPT_DLKEY pkDlkey, UINT32 nBitsPriv, UINT32 flags );
+//
+// Sets the number of bits that this dlkey can have in its private key
+// The set value is only used for when the dlkey is a named safe-prime dlgroup, otherwise the value
+// is ignored.
+//
+// Requirements:
+//  - pkDlkey->pDlgroup->nBitsOfQ >= nBitsPriv >= pkDlkey->pDlgroup->nMinBitsPriv
+//    Otherwise SYMCRYPT_INVALID_ARGUMENT is returned
+//
+// Allowed flags:
+//  - None.
+
+PCSYMCRYPT_DLGROUP
+SYMCRYPT_CALL
+SymCryptDlkeyGetGroup( _In_ PCSYMCRYPT_DLKEY pkDlkey );
+//
+// Returns a pointer to the dlgroup object associated with the key.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDlkeySizeofPublicKey( _In_ PCSYMCRYPT_DLKEY pkDlkey );
+//
+// Returns the size in bytes of a blob big enough to retrieve the public key.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDlkeySizeofPrivateKey( _In_ PCSYMCRYPT_DLKEY pkDlkey );
+//
+// Returns the size in bytes of a blob big enough to retrieve the private key.
+//
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptDlkeyHasPrivateKey( _In_ PCSYMCRYPT_DLKEY pkDlkey );
+//
+// Returns TRUE if the pkDlkey object has a private key set.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyGenerate(
+    _In_    UINT32                      flags,
+    _Inout_ PSYMCRYPT_DLKEY             pkDlkey );
+//
+// Allowed flags:
+//  - SYMCRYPT_FLAG_DLKEY_GEN_MODP
+//  When set, generate a private key between 1 and P-2.
+//  When Q is known, this overrides the default behavior of generating a private key between 1 and Q-1,
+//  or 1 and min(2^nBitsPriv-1, Q-1) for named safe-prime groups
+//  When Q is not known, this does not affect the behavior
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - At least one of the flags indicating what the Dlkey is to be used for must be specified:
+//      SYMCRYPT_FLAG_DLKEY_DSA
+//      SYMCRYPT_FLAG_DLKEY_DH
+//
+// Note:
+// If SYMCRYPT_FLAG_DLKEY_GEN_MODP is specified then SYMCRYPT_FLAG_KEY_NO_FIPS must also be
+// specified to avoid SYMCRYPT_INVALID_ARGUMENT, as FIPS requires the default generation behavior
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeySetValue(
+    _In_reads_bytes_( cbPrivateKey )    PCBYTE                  pbPrivateKey,
+                                        SIZE_T                  cbPrivateKey,
+    _In_reads_bytes_( cbPublicKey )     PCBYTE                  pbPublicKey,
+                                        SIZE_T                  cbPublicKey,
+                                        SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                        UINT32                  flags,
+    _Inout_                             PSYMCRYPT_DLKEY         pkDlkey );
+//
+// Import key material to a DLKEY object.
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION
+//   Opt-out of performing almost all validation - must be specified with SYMCRYPT_FLAG_KEY_NO_FIPS
+//
+// - At least one of the flags indicating what the Dlkey is to be used for must be specified:
+//      SYMCRYPT_FLAG_DLKEY_DSA
+//      SYMCRYPT_FLAG_DLKEY_DH
+//
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyGetValue(
+    _In_    PCSYMCRYPT_DLKEY        pkDlkey,
+    _Out_writes_bytes_( cbPrivateKey )
+            PBYTE                   pbPrivateKey,
+            SIZE_T                  cbPrivateKey,
+    _Out_writes_bytes_( cbPublicKey )
+            PBYTE                   pbPublicKey,
+            SIZE_T                  cbPublicKey,
+            SYMCRYPT_NUMBER_FORMAT  numFormat,
+            UINT32                  flags );
+//
+// Retrieve the public or the private key (or both) from a DLKEY. The buffers should be
+// allocated by the caller.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyExtendKeyUsage(
+    _Inout_ PSYMCRYPT_DLKEY pkDlkey,
+            UINT32          flags );
+//
+// Enable an existing key which has been generated or imported to be used in specified algorithms.
+// Some callers may not know at key generation or import time what algorithms a key will be used for
+// and this API allows the key to be extended for use in additional algorithms. Use of this API may
+// not be compliant with FIPS 140-3.
+//
+// - flags must be some bitwise OR of the following flags:
+//      SYMCRYPT_FLAG_DLKEY_DSA
+//      SYMCRYPT_FLAG_DLKEY_DH
+
+//=====================================================
+// Elliptic curve operations and supported curves
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurvePrivateKeyDefaultFormat( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the private key default format of the input curve.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveHighBitRestrictionNumOfBits( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the number of bits specified by the high bit restriction
+// value of the input curve.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveHighBitRestrictionPosition( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the position of the high bit restriction
+// value of the input curve.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveHighBitRestrictionValue( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the high bit restriction value of the input curve.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveBitsizeofFieldModulus( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the number of bits of a field element on which
+// the curve is defined.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveBitsizeofGroupOrder( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the number of bits of the order of the subgroup generated by
+// the distinguished point of the curve.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveSizeofFieldElement( _In_    PCSYMCRYPT_ECURVE   pCurve );
+//
+// This function returns the number of bytes of a field element. It is used to
+// construct buffers for setting and getting the value of elliptic curve points (most
+// notably the public key of an ECKEY object).
+//
+// The result is equal to the cbFieldLength field of the parameters that created the curve.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveSizeofScalarMultiplier( _In_    PCSYMCRYPT_ECURVE   pCurve );
+//
+// This function returns the number of bytes of a scalar integer that is big enough to
+// store a private key (or a multiplier of an elliptic curve point). It is used to
+// construct buffers for setting and getting the value of a scalar multiplier (most
+// notably the private key of an ECKEY object - see SymCryptEckeySetValue and
+// SymCryptEckeyGetValue).
+//
+// The result is equal to sizeof( subgroupOrder * co-factor ).
+//
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEcurveIsSame(
+    _In_    PCSYMCRYPT_ECURVE  pCurve1,
+    _In_    PCSYMCRYPT_ECURVE  pCurve2);
+//
+// Returns true if pCurve1 and pCurve2 have same type, P, A, and B - false otherwise.
+//
+// Note: This does not check that the curves have the same G set, callers may additionally
+// consider calling SymCryptEcpointIsEqual to compare the curves' distinguished points.
+//
+
+// Internally supported curves
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNistP192;
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNistP224;
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNistP256;
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNistP384;
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNistP521;
+
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNumsP256t1;
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNumsP384t1;
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsNumsP512t1;
+
+extern const PCSYMCRYPT_ECURVE_PARAMS    SymCryptEcurveParamsCurve25519;
+
+typedef enum _SYMCRYPT_ECURVE_ID
+{
+    SYMCRYPT_ECURVE_ID_NULL             = 0,
+    SYMCRYPT_ECURVE_ID_NIST_P192        = 1,
+    SYMCRYPT_ECURVE_ID_NIST_P224        = 2,
+    SYMCRYPT_ECURVE_ID_NIST_P256        = 3,
+    SYMCRYPT_ECURVE_ID_NIST_P384        = 4,
+    SYMCRYPT_ECURVE_ID_NIST_P521        = 5,
+    SYMCRYPT_ECURVE_ID_NUMS_P256T1      = 6,
+    SYMCRYPT_ECURVE_ID_NUMS_P384T1      = 7,
+    SYMCRYPT_ECURVE_ID_NUMS_P512T1      = 8,
+    SYMCRYPT_ECURVE_ID_CURVE25519       = 9
+} SYMCRYPT_ECURVE_ID;
+
+PCSYMCRYPT_ECURVE_PARAMS
+SYMCRYPT_CALL
+SymCryptGetEcurveParams( SYMCRYPT_ECURVE_ID ecurveId );
+//
+// Returns a pointer to the elliptic curve parameters structure for the specified curve ID.
+// Returns NULL if the curve ID is invalid.
+//
+
+//=====================================================
+// ECC flags
+//
+// Also see Generic key validation flags above
+
+// SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION: This flag applies only to the ECDSA algorithm. When set, the sign
+//      and verify algorithms will not do hash truncation. The caller can use their own truncation method in such case.
+//      (default: according to the ECDSA standard)
+#define SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION        (0x08)
+
+//=====================================================
+// EC key operations
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEckeySizeofPublicKey(
+    _In_ PCSYMCRYPT_ECKEY           pkEckey,
+    _In_ SYMCRYPT_ECPOINT_FORMAT    ecPointFormat );
+//
+// Returns the size in bytes of a blob big enough to retrieve the public key in
+// the specified ECPOINT format.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEckeySizeofPrivateKey( _In_ PCSYMCRYPT_ECKEY pkEckey );
+//
+// Returns the size in bytes of a blob big enough to retrieve the private key.
+// It is equal to SymCryptEcurveSizeofScalarMultiplier( pCurve ) where pCurve is the
+// curve that created the key.
+//
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEckeyHasPrivateKey( _In_ PCSYMCRYPT_ECKEY pkEckey );
+//
+// Returns TRUE if the pkEckey object has a private key set.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeySetValue(
+    _In_reads_bytes_( cbPrivateKey )    PCBYTE                  pbPrivateKey,
+                                        SIZE_T                  cbPrivateKey,
+    _In_reads_bytes_( cbPublicKey )     PCBYTE                  pbPublicKey,
+                                        SIZE_T                  cbPublicKey,
+                                        SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                        SYMCRYPT_ECPOINT_FORMAT ecPointFormat,
+                                        UINT32                  flags,
+    _Inout_                             PSYMCRYPT_ECKEY         pEckey );
+//
+// Import key material to an ECKEY object.
+//
+// Requirements:
+//      (pbPrivateKey, cbPrivateKey): a buffer that contains the private key, encoded
+//      in the format specified by the numFormat parameter.
+//      Note that the integer encoded in (pbPrivateKey, cbPrivateKey) is taken modulo the order of the
+//      subgroup generated by the curve generator. Callers that want a uniform private key value
+//      should ensure that the input is uniform in the range [1..GOrd-1].
+//
+//      Requirements: cbPrivateKey == SymCryptEckeySizeofPrivateKey( pEckey )
+//
+//      If pbPrivateKey == NULL && cbPrivateKey == 0, then no private key is imported, and the
+//      resulting ECKEY object will not have a private key.
+//
+//      (pbPublicKey, cbPublicKey): buffer that contains the public key, encoded in the format
+//      specified by the format parameter, the buffer length, and the curve properties.
+//
+//      Requirements: cbPublicKey == SymCryptEckeySizeofPublicKey( pEckey, ecPointFormat )
+//
+//      If no public key is presented (pbPublicKey == NULL && cbPublicKey == 0) then the public
+//      key is computed from the provided private key.
+//
+//      At least one of the public and private keys must be provided.
+//
+//      If both are provided, then they must match.
+//
+//      The algorithm always sets the corresponding public key
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION
+//   Opt-out of performing almost all validation - must be specified with SYMCRYPT_FLAG_KEY_NO_FIPS
+//
+// - At least one of the flags indicating what the Eckey is to be used for must be specified:
+//      SYMCRYPT_FLAG_ECKEY_ECDSA
+//      SYMCRYPT_FLAG_ECKEY_ECDH
+//
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeySetRandom(
+    _In_    UINT32                  flags,
+    _Inout_ PSYMCRYPT_ECKEY         pEckey );
+//
+// Generates a new Eckey public/private key pair using the specified curve. The public key
+// is a uniformly random non-zero point of the subgroup generated by the distinguished point
+// of the curve. This complies with the FIPS 186-4 standard.
+//
+// Remarks:
+//  - In the case that the highbit restrictions on the curve are unsatisfiable, i.e.
+//    there is no private key smaller than the group order it returns
+//    SYMCRYPT_INVALID_ARGUMENT.
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - At least one of the flags indicating what the Eckey is to be used for must be specified:
+//      SYMCRYPT_FLAG_ECKEY_ECDSA
+//      SYMCRYPT_FLAG_ECKEY_ECDH
+
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyGetValue(
+    _In_    PCSYMCRYPT_ECKEY        pEckey,
+    _Out_writes_bytes_( cbPrivateKey )
+            PBYTE                   pbPrivateKey,
+            SIZE_T                  cbPrivateKey,
+    _Out_writes_bytes_( cbPublicKey )
+            PBYTE                   pbPublicKey,
+            SIZE_T                  cbPublicKey,
+            SYMCRYPT_NUMBER_FORMAT  numFormat,
+            SYMCRYPT_ECPOINT_FORMAT ecPointFormat,
+            UINT32                  flags );
+//
+// Retrieve the public or the private key (or both) from an ECKEY. The buffers should be
+// allocated by the caller.
+//
+// If (pbPrivateKey != NULL), then the function will return the private key in pbPrivateKey
+// in the format specified by the numFormat parameter **as long as** the following three
+// requirements are satisfied:
+//      1. cbPrivateKey >= SymCryptEckeySizeofPrivateKey( pEckey )
+//      2. pEckey contains a private key part (If this fails the function returns SYMCRYPT_INVALID_BLOB)
+// If (pbPrivateKey == NULL) and (cbPrivateKey == 0), then these parameters are ignored
+// and no private key is returned.
+//
+// If (pbPublicKey != NULL), then the function will return the public key in pbPublicKey
+// in the format specified by the numFormat and the ecPointFormat parameters
+// **as long as** the following requirement is satisfied:
+//      1. cbPublicKey >= SymCryptEckeySizeofPublicKey( pEckey, ecPointFormat )
+// If (pbPublicKey == NULL) and (cbPublicKey == 0), then these parameters are ignored
+// and no public key is returned.
+//
+// Allowed flags:
+//      - None.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyExtendKeyUsage(
+    _Inout_ PSYMCRYPT_ECKEY pEckey,
+            UINT32          flags );
+//
+// Enable an existing key which has been generated or imported to be used in specified algorithms.
+// Some callers may not know at key generation or import time what algorithms a key will be used for
+// and this API allows the key to be extended for use in additional algorithms. Use of this API may
+// not be compliant with FIPS 140-3
+//
+// - flags must be some bitwise OR of the following flags:
+//      SYMCRYPT_FLAG_ECKEY_ECDSA
+//      SYMCRYPT_FLAG_ECKEY_ECDH
+
+/************************
+ * Crypto algorithm API *
+ ************************/
+
+//
+// The Crypto algorithm API implements various cryptographic algorithms that use large-integer arithmetic.
+//
+
+//
+// RSA Encryption Algorithms
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaRawEncrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst );
+//
+// This function encrypts the buffer pbSrc (of size cbSrc bytes) under the pkRsakey key using textbook RSA.
+// The output is stored in the pbDst buffer (of size cbDst bytes).
+// For in place encryption pbSrc = pbDst.
+//
+// Both input and output buffers store a number in the number format numFormat.
+//
+// Requirements:
+//  - If cbDst is too small for the result then SYMCRYPT_BUFFER_TOO_SMALL is returned.
+//    Safe size is cbDst = SymCryptRsakeySizeofModulus(pkRsakey).
+//  - The number stored in the pbSrc buffer must be strictly smaller than the value
+//    of the public modulus in pkRsakey.
+//
+// Allowed flags:
+//      None
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaRawDecrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst );
+//
+// This function decrypts the buffer pbSrc (of size cbSrc bytes) with the pkRsakey key using textbook RSA.
+// The output is stored in the pbDst buffer (of size cbDst bytes).
+// For in place decryption pbSrc = pbDst.
+//
+// Both input and output buffers store a number in the number format numFormat.
+//
+// Requirements:
+//  - If cbDst is too small for the result then SYMCRYPT_BUFFER_TOO_SMALL is returned.
+//    Safe size is cbDst = SymCryptRsakeySizeofModulus(pkRsakey).
+//  - The number stored in the pbSrc buffer must be strictly smaller than the value
+//    of the public modulus in pkRsakey.
+//  - The RSAKEY pkRsakey must have a private key part. Otherwise SYMCRYPT_INVALID_ARGUMENT is returned.
+//
+// Allowed flags:
+//      None
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Encrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                UINT32                      flags,
+                                SYMCRYPT_NUMBER_FORMAT      nfDst,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst );
+//
+// This function encrypts the buffer pbSrc under the pkRsakey key using RSA PKCS1 v1.5.
+// The output is stored in the pbDst buffer and the number of bytes written in *pcbDst.
+//
+// If pbDst == NULL then only the *pcbDst is output.
+//
+// nfDst is the number format of the ciphertext (i.e. the pbDst buffer).
+//
+// Allowed flags:
+//      None
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Decrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      nfSrc,
+                                UINT32                      flags,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst );
+//
+// Perform an RSA-PKCS1 decryption.
+//  - pbSrc/cbSrc: source buffer
+//  - nfSrc: format of source buffer
+//  - flags: must be 0
+//  - pbDst/cbDst: destination buffer
+//  - pcbDst: receives the size of the decrypted data.
+//
+// If the data in improperly formatted, an error is returned.
+// If pbDst == NULL, then *pcbDst is set to the decrypted data length, and the functions succeeds.
+//      This is not recommended as retrieving the actual data requires a second RSA decryption,
+//      which is expensive. We recommend that callers provide a large enough buffer the first time.
+// If pbDst != NULL and cbDst is too small, then *pcbDst is set to the required size of pbDst
+//      and SYMCRYPT_BUFFER_TOO_SMALL is returned.
+//
+// Allowed flags:
+//      None
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepEncrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_( cbLabel ) PCBYTE                      pbLabel,
+                                SIZE_T                      cbLabel,
+                                UINT32                      flags,
+                                SYMCRYPT_NUMBER_FORMAT      nfDst,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst );
+//
+// This function encrypts the buffer pbSrc under the pkRsakey key using RSA OAEP.
+// The output is stored in the pbDst buffer and the number of bytes written in *pcbDst.
+//
+// If pbDst == NULL then only the *pcbDst is output.
+//
+// nfDst is the number format of the ciphertext (i.e. the pbDst buffer).
+//
+// Allowed flags:
+//      None
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepDecrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      nfSrc,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_( cbLabel ) PCBYTE                      pbLabel,
+                                SIZE_T                      cbLabel,
+                                UINT32                      flags,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst );
+//
+// This function decrypts the buffer pbSrc with the pkRsakey key using RSA OAEP.
+// The output is stored in the pbDst buffer and the number of bytes written in *pcbDst.
+//
+// If pbDst == NULL then only the *pcbDst is output.
+//
+// nfSrc is the number format of the ciphertext (i.e. the pbSrc buffer).
+//
+// Requirement:
+//  - cbSrc <= SymCryptRsakeySizeofModulus( pkRsakey ). Otherwise the function
+//    returns SYMCRYPT_INVALID_ARGUMENT.
+//
+// Allowed flags:
+//      None
+//
+
+//
+// RSA Signing Algorithms
+//
+
+#define SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1                 (0x01)
+#define SYMCRYPT_FLAG_RSA_PKCS1_OPTIONAL_HASH_OID       (0x02)
+
+#define SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT  (0x04)
+
+//
+// SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1: For RSA PKCS1 to not use the OID on signing or verifying.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Sign(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_                                PCSYMCRYPT_OID              pHashOIDs,
+    _In_                                SIZE_T                      nOIDCount,
+                                        UINT32                      flags,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _Out_writes_opt_( cbSignature )     PBYTE                       pbSignature,
+                                        SIZE_T                      cbSignature,
+    _Out_                               SIZE_T                      *pcbSignature );
+//
+// This function signs a message (its hash value is stored in pbHashValue) with
+// the pkRsakey key using RSA PKCS1 v1.5. The signature is stored in the pbSignature
+// buffer and the number of bytes written in *pcbSignature.
+//
+// pHashOIDs points to an array of SYMCRYPT_OID and the array size is nOIDCount
+//
+// If pbSignature == NULL then only the *pcbSignature is output.
+//
+// nfSignature is the number format of the signature (i.e. the pbSignature buffer). Currently
+// only SYMCRYPT_NUMBER_FORMAT_MSB_FIRST is supported.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Verify(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                      pbSignature,
+                                        SIZE_T                      cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _In_reads_opt_( nOIDCount )         PCSYMCRYPT_OID              pHashOID,
+    _In_                                SIZE_T                      nOIDCount,
+                                        UINT32                      flags );
+//
+// This function verifies the signature of a message (its hash value is input in
+// pbHashValue) with the pkRsakey key using RSA PKCS1 v1.5. The signature is input
+// in the pbSignature buffer.
+//
+// pHashOIDs points to an array of SYMCRYPT_OID and the array size is nOIDCount
+//
+// It returns SYMCRYPT_NO_ERROR if the verification succeeded or SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE
+// if it failed.
+//
+// nfSignature is the number format of the signature (i.e. the pbSignature buffer). Currently
+// only SYMCRYPT_NUMBER_FORMAT_MSB_FIRST is supported.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_RSA_PKCS1_OPTIONAL_HASH_OID
+//
+//      When the flag is set, this function will do signature verification by not using hash OID when needed
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssSign(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_                                PCSYMCRYPT_HASH             hashAlgorithm,
+                                        SIZE_T                      cbSalt,
+                                        UINT32                      flags,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _Out_writes_opt_( cbSignature )     PBYTE                       pbSignature,
+                                        SIZE_T                      cbSignature,
+    _Out_                               SIZE_T                      *pcbSignature );
+//
+// Sign a message using RSA-PSS
+// - pkRsaKey: Key to sign with; must contain a private key
+// - pbHashValue/cbHashValue: Value to sign
+// - hashAlgorithm: Hash algorithm to use in the MGF of PSS
+// - cbSalt: # bytes of salt to use (typically equal to size of hash value)
+// - flags: must be 0
+// - nfSignature: Number format of signature. Typically SYMCRYPT_NUMBER_FORMAT_MSB_FIRST
+// - pbSignature/cbSignature: buffer that receives the signature.
+//              If pbSignature == NULL< only *pcbSignature is returned.
+//              Note: pbSignature receives an integer, so if the buffer is larger than the modulus size
+//              it will be padded with zeroes. For MSB-first format the zeroes are at the start of the buffer.
+//              Typically this buffer is the same size as the RSA modulus.
+// - pcbSignature: receives the size of the signature.
+//
+// Return value:
+//  If cbHashValue + cbSalt is too large (above modulus size minus 2 or 3 depending on details) then
+//  signature generation fails.
+//
+// Allowed flags:
+//      None
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssVerify(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                      pbSignature,
+                                        SIZE_T                      cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _In_                                PCSYMCRYPT_HASH             hashAlgorithm,
+                                        SIZE_T                      cbSalt,
+                                        UINT32                      flags );
+//
+// This function verifies the signature of a message (its hash value is input in
+// pbHashValue) with the pkRsakey key using RSA PSS. The signature is input
+// in the pbSignature buffer.
+//
+// It returns SYMCRYPT_NO_ERROR if the verification succeeded or SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE
+// if it failed.
+//
+// nfSignature is the number format of the signature (i.e. the pbSignature buffer). Currently
+// only SYMCRYPT_NUMBER_FORMAT_MSB_FIRST is supported.
+//
+// Requirements:
+//  - cbHashValue <= SymCryptRsakeySizeofModulus( pkRsakey )
+//  - cbSalt <= SymCryptRsakeySizeofModulus( pkRsakey )
+//  - cbSignature <= SymCryptRsakeySizeofModulus( pkRsakey )
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT
+//
+//      When the flag is set, this function will do signature verification using the cbSalt parameter as
+//      a minimum value for the salt length, rather than using it as an exact value. Specifying this and
+//      setting cbSalt = 0 allows callers to verify a signature which has a valid encoding with any salt
+//      length using a single call.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsaSelftest(void);
+//
+// FIPS self-test for RSA sign/verify. This function uses a hardcoded key to perform the self-test
+// without having to generate a key. If the self-test fails, SymCryptFatal will be called to
+// fastfail.
+// The self-test will automatically be performed before first operational use of RSA if using a key
+// with FIPS validation, so most callers should never use this function.
+//
+
+
+//
+// DSA
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaSign(
+    _In_                                PCSYMCRYPT_DLKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature );
+//
+// Sign a message using the DSA signature algorithm.
+// (pbHashValue,cbHashValue) is the output of the hash function that hashed the message to be signed.
+// (pbSignature,cbSignature) is the output buffer that receives the signature.
+// The signature is encoded as two integers (R,S) mod Q in the format specified by the 'format' parameter.
+//
+// Allowed flags:
+//      None
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaVerify(
+    _In_                                PCSYMCRYPT_DLKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                  pbSignature,
+                                        SIZE_T                  cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags );
+//
+// Verifies a DSA signature using the public part of Key.
+//
+// It returns SYMCRYPT_NO_ERROR if the verification succeeded or SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE
+// if it failed.
+//
+// Allowed flags:
+//      None
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDsaSelftest(void);
+//
+// FIPS self-test for DSA sign/verify. This function uses a hardcoded key to perform the self-test
+// without having to generate a key. If the self-test fails, SymCryptFatal will be called to
+// fastfail.
+// The self-test will automatically be performed before first operational use of DSA if using a key
+// with FIPS validation, so most callers should never use this function.
+//
+
+//
+// DH
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDhSecretAgreement(
+    _In_                            PCSYMCRYPT_DLKEY        pkPrivate,
+    _In_                            PCSYMCRYPT_DLKEY        pkPublic,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+                                    UINT32                  flags,
+    _Out_writes_( cbAgreedSecret )  PBYTE                   pbAgreedSecret,
+                                    SIZE_T                  cbAgreedSecret );
+//
+// Calculates the agreed secret of a DH key exchange and stores it
+// in the pbAgreedSecret buffer under the specified number format.
+//
+// format is the number format of the agreed secret (pbAgreedSecret buffer).
+//
+// Allowed flags:
+//      - None
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptDhSecretAgreementSelftest(void);
+//
+// FIPS self-test for DH secret agreement. This function uses two hardcoded keys and a precalculated
+// known answer to perform the self-test without having to generate a key. If the self-test fails,
+// SymCryptFatal will be called to fastfail.
+// The self-test will automatically be performed before first operational use of DH if using keys
+// with FIPS validation, so most callers should never use this function.
+//
+
+//
+// For both ECDSA and ECDH algorithms the key generation and management is the same. The main algorithms are:
+//      - SymCryptEckeyAllocate or SymCryptEckeyCreate for creation of the ECKEY object.
+//      - SymCryptEckeySetValue or SymCryptEckeySetRandom for filling the key with the preferred key material.
+//      - SymCryptEckeyFree or SymCryptEckeyWipe for freeing or wiping the key.
+//
+
+//
+// ECDSA
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaSign(
+    _In_                                PCSYMCRYPT_ECKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature );
+//
+// Sign a message using the ECDSA signature algorithm.
+// (pbHashValue,cbHashValue) is the output of the hash function that hashed the message to be signed.
+// (pbSignature,cbSignature) is the output buffer that receives the signature.
+// The signature is encoded as two integers in the format specified by the 'format' parameter.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION: If set then the hash value will
+//      not be truncated.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaVerify(
+    _In_                                PCSYMCRYPT_ECKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                  pbSignature,
+                                        SIZE_T                  cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags );
+
+//
+// Verifies an ECDSA signature using the public part of Key.
+//
+// It returns SYMCRYPT_NO_ERROR if the verification succeeded or SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE
+// if it failed.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION: If set then the hash value will
+//      not be truncated.
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcDsaSelftest(void);
+//
+// FIPS self-test for ECDSA sign/verify. This function uses a hardcoded key to perform the self-test
+// without having to generate a key. If the self-test fails, SymCryptFatal will be called to
+// fastfail.
+// The self-test will automatically be performed before first operational use of ECDSA if using a
+// key with FIPS validation, so most callers should never use this function.
+//
+
+//
+// ECDH
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDhSecretAgreement(
+    _In_                            PCSYMCRYPT_ECKEY        pkPrivate,
+    _In_                            PCSYMCRYPT_ECKEY        pkPublic,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+                                    UINT32                  flags,
+    _Out_writes_( cbAgreedSecret )  PBYTE                   pbAgreedSecret,
+                                    SIZE_T                  cbAgreedSecret );
+
+//
+// Calculates the agreed secret of a DH key exchange and stores it
+// in the pbAgreedSecret buffer under the specified number format.
+//
+// Allowed flags:
+//      - None
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcDhSecretAgreementSelftest(void);
+//
+// FIPS self-test for ECDH secret agreement. This function uses two hardcoded keys and a
+// precalculated known answer to perform the self-test without having to generate a key. If the
+// self-test fails, SymCryptFatal will be called to fastfail.
+// The self-test will automatically be performed before first operational use of ECDH if using keys
+// with FIPS validation, so most callers should never use this function.
+//
+
+//========================================================================
+//
+// Stateful Hash-based Signatures
+//
+// Hash-based signature schemes are digital signature schemes built out of hash
+// functions. Stateful hash-based signatures are many-time signature schemes
+// composed of a one-time-signature (OTS) scheme and a Merkle-tree representing
+// multiple OTS with a public root value. At each signing operation, one of the
+// (unused) OTS keys is used to sign the message, and the private key is updated
+// so that the same OTS is not used again. Because there is a limited number of
+// OTS keys determined at key generation time, signing cannot be performed after
+// all OTSs are used. This is an important distinction from other digital signature
+// schemes such as RSA or ECDSA.
+//
+// It is crucial for the security of the *stateful* hash-based signatures that the
+// same private key state NOT be used more than once to sign messages, otherwise all
+// security is lost.
+//
+
+
+//========================================================================
+// XMSS API
+//
+//  XMSS is a stateful hash-based signature scheme specified in RFC 8391. The
+//  multi-tree variant is named XMSS^MT.
+//
+//  XMSS uses WOTS+ as the one-time-signature (OTS) scheme. Public key consists
+//  of two parts; Merkle-tree hash of OTS public keys called the Root, and a Seed value
+//  used in in hash computations. The private key consists of SK_XMSS which is
+//  used to deterministically create OTS keys, SK_PRF which is used to generate
+//  the randomizer for hashing, and an integer Idx is used to select the next OTS key
+//  for signing.
+//
+
+typedef enum _SYMCRYPT_XMSS_ALGID
+{
+    //                                              Hash Fn.    RFC-8391    SP800-208
+    SYMCRYPT_XMSS_SHA2_10_256       = 0x00000001,   // SHA-256      X           X
+    SYMCRYPT_XMSS_SHA2_16_256       = 0x00000002,   // SHA-256      X           X
+    SYMCRYPT_XMSS_SHA2_20_256       = 0x00000003,   // SHA-256      X           X
+    SYMCRYPT_XMSS_SHA2_10_512       = 0x00000004,   // SHA-512      X
+    SYMCRYPT_XMSS_SHA2_16_512       = 0x00000005,   // SHA-512      X
+    SYMCRYPT_XMSS_SHA2_20_512       = 0x00000006,   // SHA-512      X
+    SYMCRYPT_XMSS_SHAKE_10_256      = 0x00000007,   // SHAKE128     X
+    SYMCRYPT_XMSS_SHAKE_16_256      = 0x00000008,   // SHAKE128     X
+    SYMCRYPT_XMSS_SHAKE_20_256      = 0x00000009,   // SHAKE128     X
+    SYMCRYPT_XMSS_SHAKE_10_512      = 0x0000000A,   // SHAKE256     X
+    SYMCRYPT_XMSS_SHAKE_16_512      = 0x0000000B,   // SHAKE256     X
+    SYMCRYPT_XMSS_SHAKE_20_512      = 0x0000000C,   // SHAKE256     X
+    SYMCRYPT_XMSS_SHA2_10_192       = 0x0000000D,   // SHA-256                  X
+    SYMCRYPT_XMSS_SHA2_16_192       = 0x0000000E,   // SHA-256                  X
+    SYMCRYPT_XMSS_SHA2_20_192       = 0x0000000F,   // SHA-256                  X
+    SYMCRYPT_XMSS_SHAKE256_10_256   = 0x00000010,   // SHAKE256                 X
+    SYMCRYPT_XMSS_SHAKE256_16_256   = 0x00000011,   // SHAKE256                 X
+    SYMCRYPT_XMSS_SHAKE256_20_256   = 0x00000012,   // SHAKE256                 X
+    SYMCRYPT_XMSS_SHAKE256_10_192   = 0x00000013,   // SHAKE256                 X
+    SYMCRYPT_XMSS_SHAKE256_16_192   = 0x00000014,   // SHAKE256                 X
+    SYMCRYPT_XMSS_SHAKE256_20_192   = 0x00000015,   // SHAKE256                 X
+
+} SYMCRYPT_XMSS_ALGID;
+
+typedef enum _SYMCRYPT_XMSSMT_ALGID
+{
+    //                                                  Hash Fn.    RFC-8391    SP800-208
+    //                                                  SHA-256         X           X
+    SYMCRYPT_XMSSMT_SHA2_20_2_256       = 0x00000001,
+    SYMCRYPT_XMSSMT_SHA2_20_4_256       = 0x00000002,
+    SYMCRYPT_XMSSMT_SHA2_40_2_256       = 0x00000003,
+    SYMCRYPT_XMSSMT_SHA2_40_4_256       = 0x00000004,
+    SYMCRYPT_XMSSMT_SHA2_40_8_256       = 0x00000005,
+    SYMCRYPT_XMSSMT_SHA2_60_3_256       = 0x00000006,
+    SYMCRYPT_XMSSMT_SHA2_60_6_256       = 0x00000007,
+    SYMCRYPT_XMSSMT_SHA2_60_12_256      = 0x00000008,
+
+    //                                                  SHA-512         X
+    SYMCRYPT_XMSSMT_SHA2_20_2_512       = 0x00000009,
+    SYMCRYPT_XMSSMT_SHA2_20_4_512       = 0x0000000A,
+    SYMCRYPT_XMSSMT_SHA2_40_2_512       = 0x0000000B,
+    SYMCRYPT_XMSSMT_SHA2_40_4_512       = 0x0000000C,
+    SYMCRYPT_XMSSMT_SHA2_40_8_512       = 0x0000000D,
+    SYMCRYPT_XMSSMT_SHA2_60_3_512       = 0x0000000E,
+    SYMCRYPT_XMSSMT_SHA2_60_6_512       = 0x0000000F,
+    SYMCRYPT_XMSSMT_SHA2_60_12_512      = 0x00000010,
+
+    //                                                  SHAKE128        X
+    SYMCRYPT_XMSSMT_SHAKE_20_2_256      = 0x00000011,
+    SYMCRYPT_XMSSMT_SHAKE_20_4_256      = 0x00000012,
+    SYMCRYPT_XMSSMT_SHAKE_40_2_256      = 0x00000013,
+    SYMCRYPT_XMSSMT_SHAKE_40_4_256      = 0x00000014,
+    SYMCRYPT_XMSSMT_SHAKE_40_8_256      = 0x00000015,
+    SYMCRYPT_XMSSMT_SHAKE_60_3_256      = 0x00000016,
+    SYMCRYPT_XMSSMT_SHAKE_60_6_256      = 0x00000017,
+    SYMCRYPT_XMSSMT_SHAKE_60_12_256     = 0x00000018,
+
+    //                                                  SHAKE256        X
+    SYMCRYPT_XMSSMT_SHAKE_20_2_512      = 0x00000019,
+    SYMCRYPT_XMSSMT_SHAKE_20_4_512      = 0x0000001A,
+    SYMCRYPT_XMSSMT_SHAKE_40_2_512      = 0x0000001B,
+    SYMCRYPT_XMSSMT_SHAKE_40_4_512      = 0x0000001C,
+    SYMCRYPT_XMSSMT_SHAKE_40_8_512      = 0x0000001D,
+    SYMCRYPT_XMSSMT_SHAKE_60_3_512      = 0x0000001E,
+    SYMCRYPT_XMSSMT_SHAKE_60_6_512      = 0x0000001F,
+    SYMCRYPT_XMSSMT_SHAKE_60_12_512     = 0x00000020,
+
+    //                                                  SHA-256                     X
+    SYMCRYPT_XMSSMT_SHA2_20_2_192       = 0x00000021,
+    SYMCRYPT_XMSSMT_SHA2_20_4_192       = 0x00000022,
+    SYMCRYPT_XMSSMT_SHA2_40_2_192       = 0x00000023,
+    SYMCRYPT_XMSSMT_SHA2_40_4_192       = 0x00000024,
+    SYMCRYPT_XMSSMT_SHA2_40_8_192       = 0x00000025,
+    SYMCRYPT_XMSSMT_SHA2_60_3_192       = 0x00000026,
+    SYMCRYPT_XMSSMT_SHA2_60_6_192       = 0x00000027,
+    SYMCRYPT_XMSSMT_SHA2_60_12_192      = 0x00000028,
+
+    //                                                  SHAKE256                    X
+    SYMCRYPT_XMSSMT_SHAKE256_20_2_256   = 0x00000029,
+    SYMCRYPT_XMSSMT_SHAKE256_20_4_256   = 0x0000002A,
+    SYMCRYPT_XMSSMT_SHAKE256_40_2_256   = 0x0000002B,
+    SYMCRYPT_XMSSMT_SHAKE256_40_4_256   = 0x0000002C,
+    SYMCRYPT_XMSSMT_SHAKE256_40_8_256   = 0x0000002D,
+    SYMCRYPT_XMSSMT_SHAKE256_60_3_256   = 0x0000002E,
+    SYMCRYPT_XMSSMT_SHAKE256_60_6_256   = 0x0000002F,
+    SYMCRYPT_XMSSMT_SHAKE256_60_12_256  = 0x00000030,
+
+    //                                                  SHAKE256                    X
+    SYMCRYPT_XMSSMT_SHAKE256_20_2_192   = 0x00000031,
+    SYMCRYPT_XMSSMT_SHAKE256_20_4_192   = 0x00000032,
+    SYMCRYPT_XMSSMT_SHAKE256_40_2_192   = 0x00000033,
+    SYMCRYPT_XMSSMT_SHAKE256_40_4_192   = 0x00000034,
+    SYMCRYPT_XMSSMT_SHAKE256_40_8_192   = 0x00000035,
+    SYMCRYPT_XMSSMT_SHAKE256_60_3_192   = 0x00000036,
+    SYMCRYPT_XMSSMT_SHAKE256_60_6_192   = 0x00000037,
+    SYMCRYPT_XMSSMT_SHAKE256_60_12_192  = 0x00000038,
+
+} SYMCRYPT_XMSSMT_ALGID;
+
+
+typedef enum _SYMCRYPT_XMSSKEY_TYPE
+{
+    SYMCRYPT_XMSSKEY_TYPE_NONE      = 0,
+    SYMCRYPT_XMSSKEY_TYPE_PUBLIC    = 1,    // Key object contains only public key
+    SYMCRYPT_XMSSKEY_TYPE_PRIVATE   = 2,    // Key object contains both public key and private key
+} SYMCRYPT_XMSSKEY_TYPE;
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssParamsFromAlgId(
+            SYMCRYPT_XMSS_ALGID     id,
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams);
+//
+// Populate SYMCRYPT_XMSS_PARAMS structure for the specified XMSS algorithm identifier
+// using the predefined parameter sets from RFC 8391 and NIST SP800-208
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssMtParamsFromAlgId(
+            SYMCRYPT_XMSSMT_ALGID   id,
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams);
+//
+// Populate SYMCRYPT_XMSS_PARAMS structure for the specified XMSS^MT algorithm identifier
+// using the predefined parameter sets from RFC 8391 and NIST SP800-208
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssSetParams(
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams,
+            UINT32                  id,                 // algorithm identifier
+    _In_    PCSYMCRYPT_HASH         pHash,              // hash algorithm
+            UINT32                  cbHashOutput,       // hash output size
+            UINT32                  nWinternitzWidth,   // Winternitz parameter (width of digits)
+            UINT32                  nTotalTreeHeight,   // total tree height
+            UINT32                  nLayers,            // number of levels
+            UINT32                  cbPrefix            // domain separator prefix length
+    );
+//
+// Populates SYMCRYPT_XMSS_PARAMS structure by user defined parameters
+//
+//
+// Parameters:
+//
+//		pParams. Pointer to the structure that will be populated with the
+//		supplied parameters.
+//
+//		id. Algorithm identifier, will be embedded in key and signature objects.
+//
+//		pHash. Pointer to a hash object that implements a hash function which
+//		will be used in XMSS/XMSS^MT operations.
+//
+//		cbHashOutput. Output size of the hash function in bytes. Leading cbHashOutput
+//      bytes are taken as hash output if the hash algorithm's actual output size is larger.
+//
+//		nWinternitzWidth. Winternitz parameter, width of digits in byte sequences.
+//      See remark below for more explanation.
+//
+//		nTotalTreeHeight. Height of the XMSS/XMSS^MT tree. In a multi-tree setting,
+//      it is the sum of the tree heights of each layer.
+//
+//		nLayers. Number of layers. For XMSS nLayers=1, otherwise nLayers > 1. When nLayers > 1,
+//      it must divide nTotalTreeHeight without remainder, so that each layer has height
+//      nTotalTreeHeight/nLayers.
+//
+//		cbPrefix. Number of bytes in the prefix to the hash inputs used to domain separate
+//		PRF functions.
+//
+// Requirements:
+//
+//      cbHashOutput must be nonzero, must be less than or equal to pHash->resultSize,
+//      and must be less than or equal to SYMCRYPT_HASH_MAX_RESULT_SIZE
+//
+//      nWinternitzWidth must be one of 1, 2, 4, or 8
+//
+//      nTotalTreeHeight must be non-zero, it must be less than or equal to 32 for
+//      single-tree (nLayers = 1), and must be less than 64 for multi-tree (nLayers > 1)
+//
+//      nLayers must be non-zero and must divide nTotalTreeHeight without remainder
+//
+//      cbPrefix must be non-zero
+//
+// Remarks:
+//
+//      RFC 8391 specifies w as the length of the Winternitz chains. Here,
+//      it is used as the width of the digits in an octet string, i.e.,
+//		base2 logarithm of the chain length, which is similar to its use
+//		in LMS/HSS in RFC 8554.
+//
+
+
+#define SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT           (0x00000001)
+// Verifies the public root value when importing a private key
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssSizeofKeyBlobFromParams(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+            SYMCRYPT_XMSSKEY_TYPE   keyType,
+    _Out_   SIZE_T*                 pcbKey );
+//
+// Return the size of an XMSS/XMSS^MT key blob associated with the provided XMSS parameters
+//
+//
+// Parameters:
+//
+//		pParams. Pointer to an XMSS parameters structure that has been properly
+//      initialized before this call.
+//
+//      keyType. SYMCRYPT_XMSSKEY_TYPE_PUBLIC (resp. SYMCRYPT_XMSSKEY_TYPE_PRIVATE) to
+//      retrieve the size of the public key (resp. private key) blob.
+//
+//      pcbKey. Pointer to the variable to store the size of a public/private
+//      key blob associated with the XMSS parameters.
+//
+
+PSYMCRYPT_XMSS_KEY
+SYMCRYPT_CALL
+SymCryptXmsskeyAllocate(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+            UINT32                  flags );
+//
+// Allocate an XMSS/XMSS^MT key object and initialize it
+//
+// After this call, the key object does not contain a key yet. It must be
+// followed by a call to SymCryptXmsskeyGenerate or SymCryptXmsskeySetValue.
+//
+// Allowed flags:
+//
+//      No flags defined for this function
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeyGenerate(
+    _Inout_ PSYMCRYPT_XMSS_KEY pKey,
+            UINT32             flags );
+//
+// Generate a public/private XMSS/XMSS^MT key-pair
+//
+// Parameters:
+//
+//      pKey. Key object to store the public/private key-pair
+//
+//      flags. No flags defined for this function
+//
+// Return values:
+//
+//      - SYMCRYPT_NO_ERROR
+//      On successful key generation
+//
+//      - SYMCRYPT_MEMORY_ALLOCATION_FAILURE
+//      If there is not enough memory to perform key generation
+//
+// Remarks:
+//
+//      - Generates a random private key (SK_XMSS, SK_PRF) and a random
+//      public seed SEED, and computes the public value Root from it.
+//      - If the function fails, the key object will be in an invalid state.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeySetValue(
+    _In_reads_bytes_( cbInput ) PCBYTE                  pbInput,
+                                SIZE_T                  cbInput,
+                                SYMCRYPT_XMSSKEY_TYPE   keyType,
+                                UINT32                  flags,
+    _Inout_                     PSYMCRYPT_XMSS_KEY      pKey );
+//
+//  Set an XMSS/XMSS^MT public/private key from key blob
+//
+//  Key formats:
+//
+//      PubKey: algId | Root | Seed
+//      PrvKey: algId | Root | Seed | Idx | SK_XMSS | SK_PRF
+//
+//  algId and Idx are 32-bit and 64-bit integers respectively, stored in big-endian format.
+//  Other values are n-bytes where n is the output size (in bytes) of the hash
+//  algorithm (or the truncated size if the hash output is truncated).
+//
+//  Public-key format is specified in RFC 8391, whereas private-key format is not.
+//  We define the private-key as an extension of the public-key with the private key
+//  material.
+//
+//  Parameters:
+//
+//      (pbInput, cbInput). Input key blob to import the key from
+//
+//      keyType. Indicates whether (pbInput, cbInput) contains a public or a private key.
+//      Must be one of SYMCRYPT_XMSSKEY_TYPE_PUBLIC, or SYMCRYPT_XMSSKEY_TYPE_PRIVATE.
+//
+//      flags. See below
+//
+//      pKey. Pointer to the XMSS key object to be initialized from the key blob
+//
+//  Allowed flags:
+//
+//      - SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT
+//      Can only be specified when importing a private key. Recomputes the
+//      public root value and compares it to the one that is imported from the
+//      key blob.
+//
+//  Return values:
+//
+//      - SYMCRYPT_NO_ERROR
+//       On successfully updating the key object from the provided key blob
+//
+//      - SYMCRYPT_INVALID_ARGUMENT
+//       If cbInput does not match a public/private key size indicated by keyType parameter
+//       If an invalid flag is specified, or SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT is
+//      specified when setting a public key
+//
+//      - SYMCRYPT_INVALID_BLOB
+//       If the XMSS algorithm ID in the key blob does not match the algorithm ID
+//      used in creating the key object pointed to by pKey
+//
+//      - SYMCRYPT_MEMORY_ALLOCATION_FAILURE
+//       If there is not sufficient memory for public root verification (only if
+//      SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT is set in flags)
+//
+//      - SYMCRYPT_HBS_PUBLIC_ROOT_MISMATCH
+//       If public root value in the key blob does not match the recomputed root value
+//      (only if key blob is for a private key and SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT is
+//      specified)
+//
+//  Remarks:
+//
+//      - The key blob size pbInput must match the size returned by SymCryptXmssSizeofKeyBlobFromParams
+//      for the same keyType and XMSS parameters the key object is created with.
+//      - If the function fails, the key object will be in an invalid state.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeyGetValue(
+    _In_                            PCSYMCRYPT_XMSS_KEY     pKey,
+                                    SYMCRYPT_XMSSKEY_TYPE   keyType,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbOutput )  PBYTE                   pbOutput,
+                                    SIZE_T                  cbOutput );
+//
+//  Get public/private key value from an XMSS/XMSS^MT key object
+//
+//  Key formats:
+//
+//      PubKey: algId | Root | Seed
+//      PrvKey: algId | Root | Seed | Idx | SK_XMSS | SK_PRF
+//
+//  algId and Idx are 32-bit and 64-bit integers respectively, stored in big-endian format.
+//  Other values are n-bytes where n is the output size (in bytes) of the hash
+//  algorithm (or the truncated size if the hash output is truncated).
+//
+//  Public-key format is specified in RFC 8391, whereas private-key format is not.
+//  We define the private-key as an extension of the public-key with the private key
+//  material.
+//
+// Parameters:
+//
+//      pKey. The key object to export the key material from
+//
+//      keyType. Type of the key (public or private) to get. If the key object
+//      contains a public key, keyType must be SYMCRYPT_XMSSKEY_TYPE_PUBLIC. If
+//      the key object contains a private key, keyType can be one of
+//      SYMCRYPT_XMSSKEY_TYPE_PUBLIC or SYMCRYPT_XMSSKEY_TYPE_PRIVATE
+//
+//      flags. No flags defined for this function
+//
+//      (pbOutput, cbOutput). Buffer to store the exported key blob. cbOutput must match
+//      the size of the key to be exported, which can be queried by calling
+//      SymCryptXmssSizeofKeyBlobFromParams.
+//
+// Return values:
+//
+//      - SYMCRYPT_NO_ERROR
+//       On successful exporting of the key
+//
+//      - SYMCRYPT_INVALID_ARGUMENT
+//       If cbOutput does not match the exact size of the key blob for the specified
+//      keyType
+//       If the key object does not contain private key material when keyType
+//      equals SYMCRYPT_XMSSKEY_TYPE_PRIVATE
+//       If unsupported flags are specified in flags parameter
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmsskeyFree(
+    _Inout_ PSYMCRYPT_XMSS_KEY pKey);
+//
+// Free an allocated XMSS/XMSS^MT key object
+//
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptXmssSizeofSignatureFromParams(
+    _In_ PCSYMCRYPT_XMSS_PARAMS pParams );
+//
+// Return the size of the signature for given XMSS parameters
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssSign(
+    _Inout_                             PSYMCRYPT_XMSS_KEY  pKey,
+    _In_reads_bytes_( cbMessage )       PCBYTE              pbMessage,
+                                        SIZE_T              cbMessage,
+                                        UINT32              flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE               pbSignature,
+                                        SIZE_T              cbSignature );
+//
+// Sign a message using XMSS/XMSS^MT
+//
+//  Parameters:
+//
+//      pKey. Private XMSS/XMSS^MT key used in signing
+//
+//      (pbMessage, cbMessage). Message to be signed
+//
+//      flags. No flags defined for this function
+//
+//      (pbSignature, cbSignature). Buffer to store the generated signature
+//
+//  Requirements:
+//
+//      pKey must contain the private key
+//
+//      cbSignature must be equal to the generated signature size
+//
+//  Return values:
+//
+//      - SYMCRYPT_NO_ERROR on successful signature generation
+//
+//      - SYMCRYPT_INVALID_ARGUMENT
+//      If flags parameter is invalid,
+//      or if the key object does not contain private key,
+//      or cbSignature is not of correct size
+//
+//      - SYMCRYPT_HBS_NO_OTS_KEYS_LEFT
+//      If the key doesn't have any one-time-signatures left for signing
+//
+//  Remarks:
+//
+//      The input pbMessage can be of arbitrary length and its randomized hash will be the actual
+//      value that is going to be signed with a WOTSP signature. Applications wanting to pass the hash
+//      value of a message to be signed as opposed to the message itself must make sure to have
+//      domain separation between the space of messages and the hashes of the messages.
+//
+//      The signature size can be queried with SymCryptSizeofXmssSignatureFromParams function.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssVerify(
+    _Inout_                         PSYMCRYPT_XMSS_KEY  pKey,
+    _In_reads_bytes_( cbMessage )   PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_( cbSignature ) PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature );
+//
+//  Verify an XMSS/XMSS^MT signature on a message
+//
+//  Parameters:
+//
+//      pKey. XMSS key used to verify the signature
+//
+//      (pbMessage, cbMessage) Message for which the signature was created
+//
+//      flags. No flags defined for this function
+//
+//      (pbSignature, cbSignature) XMSS or XMSS^MT signature
+//
+//  Return values:
+//
+//      - SYMCRYPT_NO_ERROR
+//      If signature verification succeeds
+//
+//		- SYMCRYPT_INVALID_ARGUMENT
+//		If flags is invalid or cbSignature is of incorrect size
+//
+//      - SYMCRYPT_SIGNATURE_VERIFICATION_ERROR
+//      If the signature is not valid
+//
+//  Requirements:
+//
+//      cbSignature must be equal to the exact signature size associated with
+//      the XMSS parameters.
+//
+//  Remarks:
+//
+//      In XMSS, the message can be arbitrarily long and a randomized hash of the message
+//      will be computed first to be signed by the WOTSP internally.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssSelftest(void);
+//
+//  FIPS self-test for signature verification
+//
+
+//========================================================================
+// Leighton-Micali Hash-Based Signatures (LMS) - LMS external struct definitions - implementing
+// RFC8554/NIST Special Publication 800-208
+//
+typedef enum _SYMCRYPT_LMS_ALGID
+{
+    //
+    // Algorithm IDs for Leighton-Micali Hash-Based Signatures (LMS)
+    // M equals the output length of the hash function, where H is the tree height.
+    // The M parameter primarily affects the security and size of the signatures, while the H parameter
+    // impacts the number of possible signatures and the computational cost for signing and verification.
+    // Larger M increases security and signature size, but increases the computational cost
+    // Higher H means more signatures but higher computational cost for signing and verification
+    //
+    SYMCRYPT_LMS_SHA256_M32_H5      = 0x00000005,
+    SYMCRYPT_LMS_SHA256_M32_H10     = 0x00000006,
+    SYMCRYPT_LMS_SHA256_M32_H15     = 0x00000007,
+    SYMCRYPT_LMS_SHA256_M32_H20     = 0x00000008,
+    SYMCRYPT_LMS_SHA256_M32_H25     = 0x00000009,
+    SYMCRYPT_LMS_SHA256_M24_H5      = 0x0000000A,
+    SYMCRYPT_LMS_SHA256_M24_H10     = 0x0000000B,
+    SYMCRYPT_LMS_SHA256_M24_H15     = 0x0000000C,
+    SYMCRYPT_LMS_SHA256_M24_H20     = 0x0000000D,
+    SYMCRYPT_LMS_SHA256_M24_H25     = 0x0000000E,
+    SYMCRYPT_LMS_SHAKE_M32_H5       = 0x0000000F,
+    SYMCRYPT_LMS_SHAKE_M32_H10      = 0x00000010,
+    SYMCRYPT_LMS_SHAKE_M32_H15      = 0x00000011,
+    SYMCRYPT_LMS_SHAKE_M32_H20      = 0x00000012,
+    SYMCRYPT_LMS_SHAKE_M32_H25      = 0x00000013,
+    SYMCRYPT_LMS_SHAKE_M24_H5       = 0x00000014,
+    SYMCRYPT_LMS_SHAKE_M24_H10      = 0x00000015,
+    SYMCRYPT_LMS_SHAKE_M24_H15      = 0x00000016,
+    SYMCRYPT_LMS_SHAKE_M24_H20      = 0x00000017,
+    SYMCRYPT_LMS_SHAKE_M24_H25      = 0x00000018,
+} SYMCRYPT_LMS_ALGID;
+
+typedef enum _SYMCRYPT_LMS_OTS_ALGID
+{
+    // Algorithm IDs for Leighton-Micali Hash-Based Signatures (LMS) One-Time-Signature (OTS)
+    // N parameter represents the number of bytes in the hash function output. It determines the size of the hash values used in
+    // the LMS OTS scheme.
+    // W parameter represents the width of the Winternitz parameter used in LMS OTS. A larger value of w results in shorter
+    // signatures but requires more computation during key generation, signature generation, and signature verification.
+    //
+    SYMCRYPT_LMS_OTS_SHA256_N32_W1      = 0x00000001,
+    SYMCRYPT_LMS_OTS_SHA256_N32_W2      = 0x00000002,
+    SYMCRYPT_LMS_OTS_SHA256_N32_W4      = 0x00000003,
+    SYMCRYPT_LMS_OTS_SHA256_N32_W8      = 0x00000004,
+    SYMCRYPT_LMS_OTS_SHA256_N24_W1      = 0x00000005,
+    SYMCRYPT_LMS_OTS_SHA256_N24_W2      = 0x00000006,
+    SYMCRYPT_LMS_OTS_SHA256_N24_W4      = 0x00000007,
+    SYMCRYPT_LMS_OTS_SHA256_N24_W8      = 0x00000008,
+    SYMCRYPT_LMS_OTS_SHAKE_N32_W1       = 0x00000009,
+    SYMCRYPT_LMS_OTS_SHAKE_N32_W2       = 0x0000000A,
+    SYMCRYPT_LMS_OTS_SHAKE_N32_W4       = 0x0000000B,
+    SYMCRYPT_LMS_OTS_SHAKE_N32_W8       = 0x0000000C,
+    SYMCRYPT_LMS_OTS_SHAKE_N24_W1       = 0x0000000D,
+    SYMCRYPT_LMS_OTS_SHAKE_N24_W2       = 0x0000000E,
+    SYMCRYPT_LMS_OTS_SHAKE_N24_W4       = 0x0000000F,
+    SYMCRYPT_LMS_OTS_SHAKE_N24_W8       = 0x00000010,
+} SYMCRYPT_LMS_OTS_ALGID;
+
+// Verifies the public key root value when importing a private key
+#define SYMCRYPT_FLAG_LMSKEY_VERIFY_ROOT           (0x00000001)
+
+typedef enum _SYMCRYPT_LMSKEY_TYPE
+{
+    SYMCRYPT_LMSKEY_TYPE_NONE       = 0,   // Key object does not contain any key material
+    SYMCRYPT_LMSKEY_TYPE_PUBLIC     = 1,   // Key object contains only public key
+    SYMCRYPT_LMSKEY_TYPE_PRIVATE    = 2,   // Key object contains both public key and private key
+} SYMCRYPT_LMSKEY_TYPE;
+//      The format of the private key blob is as follows:
+//                                        [         Public key parts                   ||      Private key parts       ]
+//                                        [    4     ||      4      ||  16 ||    m     ||      4         ||      m     ]
+//                                        [ LmsAlgId || LmsOtsAlgId ||  I  || RootNode || NextUnusedLeaf || Seed ]
+//
+//      The format of the public key blob is as follows:
+//                                        [     4    ||      4      ||  16 ||    m     ]
+//                                        [ LmsAlgId || LmsOtsAlgId ||  I  || RootNode ]
+
+//=====================================================
+// LMS operations
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsParamsFromAlgId(
+            SYMCRYPT_LMS_ALGID      lmsAlgID,
+            SYMCRYPT_LMS_OTS_ALGID  lmsOtsAlgID,
+    _Out_   PSYMCRYPT_LMS_PARAMS    pParams);
+//
+// This function populates a SYMCRYPT_LMS_PARAMS structure with the predefined parameter sets for a given LMS
+// algorithm identifier and LMS OTS algorithm identifier. The resulting structure can be used to create LMS key objects.
+// The values defined by SYMCRYPT_LMS_OTS_ALGID and SYMCRYPT_LMS_ALGID are all of the NIST SP 800-208 parameter
+// sets supported by SymCrypt.
+//
+// Parameters:
+//      lmsAlgID: The LMS algorithm identifier to use
+//
+//      lmsOtsAlgID: The LMS OTS algorithm identifier to use
+//
+//      pParams: A pointer to a SYMCRYPT_LMS_PARAMS structure that will be populated with the predefined parameter sets
+//
+// Return value:
+//      If the function succeeds, it returns SYMCRYPT_NO_ERROR
+//      If the function fails, it returns SYMCRYPT_INVALID_ARGUMENT
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsSetParams(
+    _Out_   PSYMCRYPT_LMS_PARAMS    pParams,
+            UINT32                  lmsAlgID,
+            UINT32                  lmsOtsAlgID,
+    _In_    PCSYMCRYPT_HASH         pLmsHashFunction,
+            UINT32                  cbHashOutput,
+            UINT32                  nTreeHeight,
+            UINT32                  nWinternitzChainWidth);
+//
+// This function allows for the customization of non-standard parameter sets, which cannot be set using LmsParamsFromAlgId.
+//
+// Parameters:
+//      pParams: A pointer to a SYMCRYPT_LMS_PARAMS structure to be initialized.
+//
+//      lmsAlgID: LMS algorithm identifier, will be embedded in key and signature objects.
+//
+//      lmsOtsAlgID: LMS OTS algorithm identifier, will be embedded in key and signature objects.
+//
+//      pLmsHashFunction: A pointer to the hash function to be used for the LMS system.
+//
+//      cbHashOutput: The number of bytes for each tree node, equal to the output length of the hash function.
+//      Must be less than or equal to 32.
+//
+//      nTreeHeight: The height of the LMS tree. Must be < 32, there are (2^nTreeHeight) leaves in the tree.
+//
+//      nWinternitzChainWidth: An integer that specifies the base2 logarithm of Winternitz chain lengths.
+//      Must be one of 1, 2, 4, or 8
+//
+// Return value:
+//      If the function succeeds, it fills PSYMCRYPT_LMS_PARAMS structure by user defined values and return SYMCRYPT_NO_ERROR.
+//      Otherwise, it sets the values of PSYMCRYPT_LMS_PARAMS to 0 and returns SYMCRYPT_INVALID_ARGUMENT.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsSizeofKeyBlobFromParams(
+    _In_    PCSYMCRYPT_LMS_PARAMS   pParams,
+            SYMCRYPT_LMSKEY_TYPE    keyType,
+    _Out_   SIZE_T*                 pcbKey);
+//
+// Returns the size of an LMS key blob based on the provided LMS parameters and keyType.
+//
+// Parameters:
+//      pParams: A pointer to a SYMCRYPT_LMS_PARAMS structure that specifies the parameters of the LMS key.
+//
+//      keyType: Specifies the type of blob for which to retrieve the size.
+//      Must be one of SYMCRYPT_LMSKEY_TYPE_PUBLIC or SYMCRYPT_LMSKEY_TYPE_PRIVATE.
+//
+//      pcbKey: Pointer to the variable to store the size of a public/private
+//      key blob associated with the LMS parameters.
+//
+// Return value:
+//      If the function succeeds, it returns SYMCRYPT_NO_ERROR. In case keyType is not recognized
+//      it returns SYMCRYPT_INVALID_ARGUMENT.
+//
+
+PSYMCRYPT_LMS_KEY
+SYMCRYPT_CALL
+SymCryptLmskeyAllocate(
+    _In_    PCSYMCRYPT_LMS_PARAMS   pParams,
+            UINT32                  flags);
+//
+// This function allocates a new SYMCRYPT_LMS_KEY object, which represents a key for the Leighton-Micali Signature (LMS)
+// scheme, based on the given PCSYMCRYPT_LMS_PARAMS. The function allocates memory for the key object, and returns a pointer to it.
+// The caller is responsible for freeing the memory when the key is no longer needed, using the SymCryptLmskeyFree function.
+//
+// Parameters:
+//      pParams: A pointer to a constant SYMCRYPT_LMS_PARAMS structure that describes
+//          the LMS parameters to be used for the key.
+//          The structure must be non-null, and must be initialized by one of the initialization functions:
+//          SymCryptLmsParamsFromAlgId or SymCryptLmsSetParams.
+//
+//      flags: Currently not used. Must be set to 0.
+//
+// Return value:
+//      If the function succeeds, it returns a pointer to the newly created SYMCRYPT_LMS_KEY object.
+//      Otherwise, it returns NULL, indicating an error that should be handled by the caller.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeyGenerate(
+    _Inout_ PSYMCRYPT_LMS_KEY   pKey,
+            UINT32              flags);
+//
+// This function generates an LMS public/private key pair in the pKey object.
+//
+// Parameters:
+//      pKey: A pointer to a SYMCRYPT_LMS_KEY structure that represents the LMS key object to be initialized. The structure
+//      must be valid and non-null, and must have been previously created using the SymCryptLmskeyAllocate
+//      function. If the key object already contains key values, they will be overwritten by the generated values.
+//
+//      flags: Currently not used. Must be set to 0.
+//
+// Return value:
+//      If the function succeeds, it returns SYMCRYPT_NO_ERROR. Otherwise, it returns an error code that describes the nature of the
+//      failure, such as SYMCRYPT_INVALID_ARGUMENT.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeySetValue(
+    _In_reads_bytes_(cbInput)   PCBYTE                  pbInput,
+                                SIZE_T                  cbInput,
+                                SYMCRYPT_LMSKEY_TYPE    keyType,
+                                UINT32                  flags,
+    _Inout_                     PSYMCRYPT_LMS_KEY       pKey);
+//
+// This function imports an LMS key from a buffer, setting the key object with the provided data.
+//
+// Parameters:
+//      pbInput: A pointer to a byte buffer containing the key data to be imported into the LMS key object
+//      The format of the input buffer is specified by the SYMCRYPT_LMSKEY_TYPE enumeration.
+//
+//      cbInput: The size, in bytes, of the key data buffer pointed to by pbInput
+//
+//      keyType: Indicates whether (pbInput, cbInput) contains a public or a private key.
+//      Must be one of SYMCRYPT_LMSKEY_TYPE_PUBLIC, or SYMCRYPT_LMSKEY_TYPE_PRIVATE.
+//
+//      flags: See allowed flags below.
+//
+//      pKey: A pointer to a SYMCRYPT_LMS_KEY structure that will receive the imported key from the buffer pbInput
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_LMSKEY_VERIFY_ROOT: Can only be specified when importing a private key. Recomputes the
+//      public root value and compares it to the one that is imported from the key blob.
+//
+// Return value:
+//      If the function succeeds, it returns SYMCRYPT_NO_ERROR and the SYMCRYPT_LMS_KEY structure is set with the imported key data.
+//      If the function fails, it returns an error code that describes the nature of the failure, such as SYMCRYPT_INVALID_ARGUMENT.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeyGetValue(
+    _In_                                PCSYMCRYPT_LMS_KEY      pKey,
+                                        SYMCRYPT_LMSKEY_TYPE    keyType,
+                                        UINT32                  flags,
+    _Out_writes_bytes_(cbOutput)        PBYTE                   pbOutput,
+                                        SIZE_T                  cbOutput);
+//
+// This function retrieves the public or private key value from an LMS key object, depending on the keyType parameter
+//
+// Parameters:
+//      pKey: A pointer to a SYMCRYPT_LMS_KEY structure that represents the LMS key object to retrieve the key value from.
+//      The structure must be valid and non-null, and must contain the key values to retrieve
+//
+//      keyType: Type of the key (public or private) to get. If the key object only
+//      contains a public key, keyType must be SYMCRYPT_LMSKEY_TYPE_PUBLIC. If
+//      the key object contains a private key, keyType can be one of
+//      SYMCRYPT_LMSKEY_TYPE_PUBLIC or SYMCRYPT_LMSKEY_TYPE_PRIVATE
+//
+//      flags: Currently not used. Must be set to 0.
+//
+//      pbOutput: A buffer to hold the key value. The buffer must be large enough to hold the key value.
+//      The format of the output buffer is specified by the SYMCRYPT_LMSKEY_TYPE enumeration.
+//
+//      cbOutput: The size of the pbOutput buffer in bytes
+//
+// Return value:
+//      If the function succeeds, it returns SYMCRYPT_NO_ERROR. Otherwise, it returns SYMCRYPT_INVALID_ARGUMENT.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptLmskeyFree(
+    _Inout_ PSYMCRYPT_LMS_KEY   pKey);
+//
+// This function frees the memory that was allocated for the given LMS key object, which was previously created using the
+// SymCryptLmskeyAllocate function. The function wipes and deallocates the memory.
+//
+// Parameters:
+//      pKey: A pointer to a SYMCRYPT_LMS_KEY structure that represents the LMS key object to be freed. The structure
+//      must be valid and non-null, and must have been previously created using the SymCryptLmskeyAllocate function.
+//
+// Return value:
+//      The function does not return a value.
+//
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptLmsSizeofSignatureFromParams(
+    _In_ PCSYMCRYPT_LMS_PARAMS pParams);
+//
+// This function returns the size, in bytes, of the signature that will be generated by the LMS signature scheme, based on the
+// specified LMS parameters.
+//
+// Parameters:
+//      pParams: A pointer to a SYMCRYPT_LMS_PARAMS structure that represents the parameters associated with the LMS key to
+//      use for computing the signature size. The structure must be valid and non-null.
+//
+// Return value:
+//      Signature size in bytes.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsSign(
+    _Inout_                             PSYMCRYPT_LMS_KEY   pKey,
+    _In_reads_bytes_(cbMessage)         PCBYTE              pbMessage,
+                                        SIZE_T              cbMessage,
+                                        UINT32              flags,
+    _Out_writes_bytes_(cbSignature)     PBYTE               pbSignature,
+                                        SIZE_T              cbSignature);
+//
+// This function generates an LMS signature for the given message, using the private key in the given LMS key object.
+// The function fills the buffer pointed to by pbSignature with the LMS signature. It uses the LMS parameters
+// and key values that were specified when the key object was created to generate the signature.
+// Stateful hash-based signatures are not approved by FIPS for key generation and signature generation in software
+// modules. Special care must be taken to ensure that the same private key state is not used more than once to
+// sign messages. This can be done, for instance, by releasing a signature only after verifying that the private
+// key has been updated and serialized to a physical storage.
+//
+// Parameters:
+//      pKey: A pointer to a SYMCRYPT_LMS_KEY structure that represents the LMS key object to be used for signing the message.
+//      The structure must be valid and non-null, and must contain the private key values for the LMS scheme. The private key
+//      must have been initialized previously using the SymCryptLmsKeyGenerate or SymCryptLmskeySetValue function.
+//
+//      pbMessage: A pointer to a buffer that contains the message to be signed.
+//
+//      cbMessage: The length in bytes of the message to be signed.
+//
+//      flags: Currently not used. Must be set to 0.
+//
+//      pbSignature: A pointer to the buffer that receives the computed signature. It must be large enough to hold the
+//      generated signature. The required size can be retrieved using: SymCryptLmsSizeofSignatureFromParams.
+//
+//      cbSignature: The size of the signature buffer pbSignature. If the passed size is different than the
+//      required signature size an error will be returned.
+//
+// Return value:
+//      SYMCRYPT_NO_ERROR - If the function succeeds
+//
+//      SYMCRYPT_HBS_NO_OTS_KEYS_LEFT - If the key has run out of available OTS keys
+//
+//      SYMCRYPT_INVALID_ARGUMENT - If one of the input parameters is invalid
+//
+// Remarks:
+//      The LMS signing process inherits its signature from the LMS OTS, which means that it will always compute a digest of the
+//      given message before signing, even if a hash value is provided as the message.
+//      Developers should always be consistent with the input to the LMS sign and verify functions and ensure that the input message
+//      is in the correct format before passing it to these functions
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsVerify(
+    _In_                            PCSYMCRYPT_LMS_KEY  pKey,
+    _In_reads_bytes_(cbMessage)     PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_(cbSignature)   PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature);
+//
+// This function verifies the given LMS signature (pbSignature) for the given message (pbMessage), using the public key
+// in the given LMS key object. The function returns SYMCRYPT_NO_ERROR if the signature is valid, and an error code otherwise.
+//
+// Parameters:
+//      pKey: A pointer to a SYMCRYPT_LMS_KEY structure that represents the LMS key object to be used for verifying
+//      the signature. The structure must be valid and non-null, and must contain the public or private key values for the LMS scheme.
+//      The public key must have been generated previously using the SymCryptLmsKeyGenerate or SymCryptLmskeySetValue functions, and must match the
+//      private key that was used to generate the signature.
+//
+//      pbMessage: A pointer to a buffer that contains the message that was signed.The buffer must be valid and non-null, and
+//      must contain at least cbMessage bytes of data.
+//
+//      cbMessage: The length in bytes of the message that was signed. The length should be set to the actual size of the message.
+//      If the message is larger than the maximum size allowed by the LMS parameters, the function will return an error.
+//
+//      flags: Currently not used. Must be set to 0.
+//
+//      pbSignature: A pointer to a buffer that contains the signature that was generated for the message. The buffer must
+//      be valid and non-null, and must contain at least cbSignature bytes.
+//
+//      cbSignature: The length in bytes of the signature buffer that contains the signature. The length must be
+//      equal to the exact signature size associated with the given LMS parameters and key values.
+//
+// Return value:
+//      SYMCRYPT_NO_ERROR - If the function succeeds
+//
+//      SYMCRYPT_INVALID_ARGUMENT - If the signature structure is not correct or if there is a mismatch between the
+//          input parameters.
+//
+//      SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE - If the signature verification fails
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptLmsSelftest(void);
+
+
+// MLKEMKEY objects' API
+//
+
+// MLKEM key formats
+// ==================
+// The below formats apply **only to external formats**: When somebody is importing or exporting
+// a key. The internal format of the keys is not visible to the caller.
+typedef enum _SYMCRYPT_MLKEMKEY_FORMAT {
+    SYMCRYPT_MLKEMKEY_FORMAT_NULL               = 0,
+    SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED       = 1,
+        // 64-byte concatenation of d || z from FIPS 203. Smallest representation of a full
+        // ML-KEM key.
+        // On its own it is ambiguous which ML-KEM parameter set this represents; callers wanting to
+        // store this format must track the parameter set alongside the key.
+    SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY  = 2,
+        // Standard byte encoding of an ML-KEM Decapsulation key, per FIPS 203.
+        // Size is 1632, 2400, or 3168 bytes for ML-KEM 512, 768, and 1024 respectively.
+    SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY  = 3,
+        // Standard byte encoding of an ML-KEM Encapsulation key, per FIPS 203.
+        // Size is 800, 1184, or 1568 bytes for ML-KEM 512, 768, and 1024 respectively.
+} SYMCRYPT_MLKEMKEY_FORMAT;
+
+
+typedef enum _SYMCRYPT_MLKEM_PARAMS {
+    SYMCRYPT_MLKEM_PARAMS_NULL          = 0,
+    SYMCRYPT_MLKEM_PARAMS_MLKEM512      = 1,
+    SYMCRYPT_MLKEM_PARAMS_MLKEM768      = 2,
+    SYMCRYPT_MLKEM_PARAMS_MLKEM1024     = 3,
+} SYMCRYPT_MLKEM_PARAMS;
+//
+// Currently supported ML-KEM parameter sets are represented externally only by the enum
+//
+
+PSYMCRYPT_MLKEMKEY
+SYMCRYPT_CALL
+SymCryptMlKemkeyAllocate(
+    SYMCRYPT_MLKEM_PARAMS   params );
+//
+// Allocate and create a new MLKEMKEY object sized according to the specified parameters.
+//
+// This call does not initialize the key. It should be
+// followed by a call to SymCryptMlKemkeyGenerate or
+// SymCryptMlKemkeySetValue.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyFree(
+    _Inout_ PSYMCRYPT_MLKEMKEY  pkMlKemkey );
+
+
+// d and z are each 32 bytes
+#define SYMCRYPT_MLKEM_PRIVATE_SEED_SIZE    (2*32)
+
+#define SYMCRYPT_MLKEM_ENCAPSULATION_KEY_SIZE_MLKEM512   (800)
+#define SYMCRYPT_MLKEM_ENCAPSULATION_KEY_SIZE_MLKEM768   (1184)
+#define SYMCRYPT_MLKEM_ENCAPSULATION_KEY_SIZE_MLKEM1024  (1568)
+
+#define SYMCRYPT_MLKEM_DECAPSULATION_KEY_SIZE_MLKEM512   (1632)
+#define SYMCRYPT_MLKEM_DECAPSULATION_KEY_SIZE_MLKEM768   (2400)
+#define SYMCRYPT_MLKEM_DECAPSULATION_KEY_SIZE_MLKEM1024  (3168)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemSizeofKeyFormatFromParams(
+            SYMCRYPT_MLKEM_PARAMS       params,
+            SYMCRYPT_MLKEMKEY_FORMAT    mlKemkeyformat,
+    _Out_   SIZE_T*                     pcbKeyFormat );
+//
+// Gives the size in bytes of the blob of the given format for the given ML-KEM
+// parameters via pcbKeyFormat output.
+// Returns SYMCRYPT_INCOMPATIBLE_FORMAT if mlKemkeyFormat is an unsupported value,
+// or SYMCRYPT_INVALID_ARGUMENT if other parameters are invalid.
+//
+
+#define SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_MLKEM512    (768)
+#define SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_MLKEM768    (1088)
+#define SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_MLKEM1024   (1568)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemSizeofCiphertextFromParams(
+            SYMCRYPT_MLKEM_PARAMS       params,
+    _Out_   SIZE_T*                     pcbCiphertext );
+//
+// Gives the size in bytes of the ciphertext for the given ML-KEM parameters.
+// Returns SYMCRYPT_INVALID_ARGUMENT if parameters are invalid.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeyGenerate(
+    _Inout_                     PSYMCRYPT_MLKEMKEY  pkMlKemkey,
+                                UINT32              flags );
+//
+// Generate a new random ML-KEM key using the information from the
+// parameters passed to SymCryptMlKemkeyAllocate.
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeySetValue(
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_MLKEMKEY_FORMAT    mlKemkeyFormat,
+                                UINT32                      flags,
+    _Inout_                     PSYMCRYPT_MLKEMKEY          pkMlKemkey );
+//
+// Import key material to an ML-KEM key object. The arguments are the following:
+//  - (pbSrc, cbSrc): a buffer containing a representation of an ML-KEM key,
+//    in format specified by mlKemkeyFormat.
+//  - mlKemkeyFormat format of the input
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION
+//   Opt-out of performing almost all validation - must be specified with SYMCRYPT_FLAG_KEY_NO_FIPS
+//
+// Remarks:
+// - cbSrc must be equal to the cbKeyFormat returned from
+//   SymCryptMlKemSizeofKeyFormatFromParams(params, mlKemkeyFormat, &cbKeyFormat), though typically this
+//   value can be known statically (see definition of SYMCRYPT_MLKEMKEY_FORMAT)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeyGetValue(
+    _In_                        PCSYMCRYPT_MLKEMKEY         pkMlKemkey,
+    _Out_writes_bytes_( cbDst ) PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+                                SYMCRYPT_MLKEMKEY_FORMAT    mlKemkeyFormat,
+                                UINT32                      flags );
+//
+// Export key material from an ML-KEM key object. The arguments are the following:
+//  - (pbDst, cbDst): a buffer into which a representation of an ML-KEM key is
+//    written, in the format specified by mlKemkeyFormat.
+//  - mlKemkeyFormat format of the output
+//
+// Allowed flags:
+//      - None.
+//
+//  Remarks:
+//  - If the key object does not have the information required to export to the format
+//    specified by mlKemkeyFormat this function will return SYMCRYPT_INCOMPATIBLE_FORMAT.
+//  - cbDst must be equal to the cbKeyFormat returned from
+//    SymCryptMlKemSizeofKeyFormatFromParams(params, mlKemkeyFormat, &cbKeyFormat), though typically this
+//    value can be known statically (see definition of SYMCRYPT_MLKEMKEY_FORMAT)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemEncapsulate(
+    _In_                                    PCSYMCRYPT_MLKEMKEY pkMlKemkey,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE               pbAgreedSecret,
+                                            SIZE_T              cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )      PBYTE               pbCiphertext,
+                                            SIZE_T              cbCiphertext );
+//
+// Performs the Encapsulate operation of ML-KEM.
+// This uses the public information of an ML-KEM keypair to generate an agreed secret
+// and a ciphertext. Only a peer with the private information of an ML-KEM keypair can
+// decapsulate the ciphertext to compute the agreed secret.
+//
+// The arguments are the following:
+// - pkMlKemkey: a key which contains public information required for encapsulation.
+// - (pbAgreedSecret, cbAgreedSecret): a buffer into which the generated secret is written.
+//   Currently cbAgreedSecret must be 32 for all parameterizations of ML-KEM.
+// - (pbCiphertext, cbCiphertext): a buffer into which the encapsulated secret is written.
+//   cbCiphertext must equal cbCiphertext given by SymCryptMlKemSizeofCiphertextFromParams,
+//   though typically this value can be known statically (see definition of
+//   SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_*).
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemDecapsulate(
+    _In_                                    PCSYMCRYPT_MLKEMKEY pkMlKemkey,
+    _In_reads_bytes_( cbCiphertext )        PCBYTE              pbCiphertext,
+                                            SIZE_T              cbCiphertext,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE               pbAgreedSecret,
+                                            SIZE_T              cbAgreedSecret );
+//
+// Performs the Decapsulate operation of ML-KEM.
+// This uses the private information of an ML-KEM keypair to generate an agreed
+// secret from a ciphertext.
+//
+// The arguments are the following:
+// - pkMlKemkey: a key which contains private information required for decapsulation.
+// - (pbCiphertext, cbCiphertext): a buffer containing an encapsulated secret.
+//   cbCiphertext must equal cbCiphertext given by SymCryptMlKemSizeofCiphertextFromParams,
+//   though typically this value can be known statically (see definition of
+//   SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_*).
+// - (pbAgreedSecret, cbAgreedSecret): a buffer into which the generated secret is written.
+//   Currently cbAgreedSecret must be 32 for all parameterizations of ML-KEM.
+//
+// Note: Given an invalid, but correctly-sized, ciphertext, the ML-KEM Decapsulation operation
+// will "implicitly reject" the ciphertext, by returning success in equal time to a valid
+// decapsulation operation, with pseudo-random agreed secret output. This forces higher
+// level protocols to fail later when symmetric keys of peers do not match.
+// So decapsulate will only ever return an error if there are programming errors (e.g. incorrect size),
+// or something fundamentally goes wrong with the environment (e.g. internal memory allocation fails).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemSelftest(void);
+//
+// FIPS self-test for ML-KEM. If the self-test fails, SymCryptFatal will be called to fastfail.
+// The self-test will automatically be performed before first operational use of ML-KEM if using
+// keys with FIPS validation, so most callers should never use this function.
+//
+
+//
+// COMPOSITE MLKEMKEY objects' API
+//
+// The below formats apply **only to external formats**: When somebody is importing or exporting
+// a key. The internal format of the keys is not visible to the caller.
+typedef enum _SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT {
+    SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT_NULL              = 0,
+    SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT_IRTF_PRIVATE_SEED = 1,
+        // 32-byte seed for deriving Composite ML-KEM key, per irtf-cfrg-hybrid-kems CG framework
+    SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT_LAMPS_PRIVATE_KEY = 2,
+        // Standard byte encoding of a Composite ML-KEM private key, per LAMPS composite ML-KEM draft 12.
+        // Concatenation of ML-KEM private seed and private key of the traditional component:
+        //   mlkemSeed || tradSK
+        // Size in bytes are MLKEM768_P256: 115, MLKEM768_X25519: 96, MLKEM1024_P384: 128
+    SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT_PUBLIC_KEY        = 3,
+        // Standard byte encoding of a Composite ML-KEM public key, per irtf-cfrg-hybrid-kems CG framework
+        // and LAMPS composite ML-KEM draft 12.
+        // Concatenation of ML-KEM encapsulation key and public key of the traditional component:
+        //   mlkemPK || tradPK
+        // Size in bytes are MLKEM768_P256: 1249, MLKEM768_X25519: 1216, MLKEM1024_P384: 1665
+} SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT;
+
+
+typedef enum _SYMCRYPT_COMPOSITE_MLKEM_PARAMS {
+    SYMCRYPT_COMPOSITE_MLKEM_PARAMS_NULL            = 0,
+    SYMCRYPT_COMPOSITE_MLKEM_PARAMS_MLKEM768_P256   = 1,
+    SYMCRYPT_COMPOSITE_MLKEM_PARAMS_MLKEM768_X25519 = 2,
+    SYMCRYPT_COMPOSITE_MLKEM_PARAMS_MLKEM1024_P384  = 3,
+} SYMCRYPT_COMPOSITE_MLKEM_PARAMS;
+//
+// Currently supported Composite ML-KEM parameter sets are represented externally only by the enum
+//
+
+PSYMCRYPT_COMPOSITE_MLKEMKEY
+SYMCRYPT_CALL
+SymCryptCompositeMlKemkeyAllocate(
+    SYMCRYPT_COMPOSITE_MLKEM_PARAMS   params );
+//
+// Allocate and create a new COMPOSITE_MLKEMKEY object sized according to the specified parameters.
+//
+// This call does not initialize the key. It should be
+// followed by a call to SymCryptCompositeMlKemkeyGenerate or
+// SymCryptCompositeMlKemkeySetValue.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptCompositeMlKemkeyFree(
+    _Inout_ PSYMCRYPT_COMPOSITE_MLKEMKEY    pkCompositeMlKemkey );
+
+
+#define SYMCRYPT_COMPOSITE_MLKEM_IRTF_PRIVATE_SEED_SIZE (32)
+
+#define SYMCRYPT_COMPOSITE_MLKEM_LAMPS_PRIVATE_KEY_SIZE_MLKEM768_P256   (115)
+#define SYMCRYPT_COMPOSITE_MLKEM_LAMPS_PRIVATE_KEY_SIZE_MLKEM768_X25519 (96)
+#define SYMCRYPT_COMPOSITE_MLKEM_LAMPS_PRIVATE_KEY_SIZE_MLKEM1024_P384  (128)
+
+#define SYMCRYPT_COMPOSITE_MLKEM_PUBLIC_KEY_SIZE_MLKEM768_P256      (1249)
+#define SYMCRYPT_COMPOSITE_MLKEM_PUBLIC_KEY_SIZE_MLKEM768_X25519    (1216)
+#define SYMCRYPT_COMPOSITE_MLKEM_PUBLIC_KEY_SIZE_MLKEM1024_P384     (1665)
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemSizeofKeyFormatFromParams(
+            SYMCRYPT_COMPOSITE_MLKEM_PARAMS     params,
+            SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT  compositeMlKemkeyformat,
+    _Out_   SIZE_T*                             pcbKeyFormat );
+//
+// Gives the size in bytes of the blob of the given format for the given Composite ML-KEM
+// parameters via pcbKeyFormat output.
+// Returns SYMCRYPT_INCOMPATIBLE_FORMAT if compositeMlKemkeyformat is an unsupported value,
+// or SYMCRYPT_INVALID_ARGUMENT if other parameters are invalid.
+//
+
+#define SYMCRYPT_COMPOSITE_MLKEM_CIPHERTEXT_SIZE_MLKEM768_P256    (1153)
+#define SYMCRYPT_COMPOSITE_MLKEM_CIPHERTEXT_SIZE_MLKEM768_X25519  (1120)
+#define SYMCRYPT_COMPOSITE_MLKEM_CIPHERTEXT_SIZE_MLKEM1024_P384   (1665)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemSizeofCiphertextFromParams(
+            SYMCRYPT_COMPOSITE_MLKEM_PARAMS params,
+    _Out_   SIZE_T*                         pcbCiphertext );
+//
+// Gives the size in bytes of the ciphertext for the given Composite ML-KEM parameters.
+// Returns SYMCRYPT_INVALID_ARGUMENT if parameters are invalid.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemkeyGenerate(
+    _Inout_ PSYMCRYPT_COMPOSITE_MLKEMKEY    pkCompositeMlKemkey,
+            UINT32                          flags );
+//
+// Generate a new random Composite ML-KEM key using the information from the
+// parameters passed to SymCryptCompositeMlKemkeyAllocate.
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// Described in more detail in the "Flags for asymmetric key generation and import" section above
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemkeySetValue(
+    _In_reads_bytes_( cbSrc )   PCBYTE                              pbSrc,
+                                SIZE_T                              cbSrc,
+                                SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT  compositeMlKemkeyFormat,
+                                UINT32                              flags,
+    _Inout_                     PSYMCRYPT_COMPOSITE_MLKEMKEY        pkCompositeMlKemkey );
+//
+// Import key material to a Composite ML-KEM key object. The arguments are the following:
+//  - (pbSrc, cbSrc): a buffer containing a representation of a Composite ML-KEM key,
+//    in format specified by compositeMlKemkeyFormat.
+//  - compositeMlKemkeyFormat format of the input
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// - SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION
+//   Opt-out of performing almost all validation - must be specified with SYMCRYPT_FLAG_KEY_NO_FIPS
+//
+// Remarks:
+// - cbSrc must be equal to the cbKeyFormat returned from
+//   SymCryptCompositeMlKemSizeofKeyFormatFromParams(params, compositeMlKemkeyFormat, &cbKeyFormat), though
+//   typically this value can be known statically (see definition of SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemkeyGetValue(
+    _In_                        PCSYMCRYPT_COMPOSITE_MLKEMKEY       pkCompositeMlKemkey,
+    _Out_writes_bytes_( cbDst ) PBYTE                               pbDst,
+                                SIZE_T                              cbDst,
+                                SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT  compositeMlKemkeyFormat,
+                                UINT32                              flags );
+//
+// Export key material from a Composite ML-KEM key object. The arguments are the following:
+//  - (pbDst, cbDst): a buffer into which a representation of a Composite ML-KEM key is
+//    written, in the format specified by compositeMlKemkeyFormat.
+//  - compositeMlKemkeyFormat format of the output
+//
+// Allowed flags:
+//      - None.
+//
+//  Remarks:
+//  - If the key object does not have the information required to export to the format
+//    specified by compositeMlKemkeyFormat this function will return SYMCRYPT_INCOMPATIBLE_FORMAT.
+//  - cbDst must be equal to the cbKeyFormat returned from
+//    SymCryptCompositeMlKemSizeofKeyFormatFromParams(params, compositeMlKemkeyFormat, &cbKeyFormat), though typically this
+//    value can be known statically (see definition of SYMCRYPT_COMPOSITE_MLKEMKEY_FORMAT)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemEncapsulate(
+    _In_                                    PCSYMCRYPT_COMPOSITE_MLKEMKEY   pkCompositeMlKemkey,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE                           pbAgreedSecret,
+                                            SIZE_T                          cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )      PBYTE                           pbCiphertext,
+                                            SIZE_T                          cbCiphertext );
+//
+// Performs the Encapsulate operation of Composite ML-KEM.
+// This uses the public information of a Composite ML-KEM keypair to generate an agreed secret
+// and a ciphertext. Only a peer with the private information of a Composite ML-KEM keypair can
+// decapsulate the ciphertext to compute the agreed secret.
+//
+// The arguments are the following:
+// - pkCompositeMlKemkey: a key which contains public information required for encapsulation.
+// - (pbAgreedSecret, cbAgreedSecret): a buffer into which the generated secret is written.
+//   Currently cbAgreedSecret must be 32 for all parameterizations of Composite ML-KEM.
+// - (pbCiphertext, cbCiphertext): a buffer into which the encapsulated secret is written.
+//   cbCiphertext must equal cbCiphertext given by SymCryptCompositeMlKemSizeofCiphertextFromParams,
+//   though typically this value can be known statically (see definition of
+//   SYMCRYPT_COMPOSITE_MLKEM_CIPHERTEXT_SIZE_*).
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemDecapsulate(
+    _In_                                    PCSYMCRYPT_COMPOSITE_MLKEMKEY   pkCompositeMlKemkey,
+    _In_reads_bytes_( cbCiphertext )        PCBYTE                          pbCiphertext,
+                                            SIZE_T                          cbCiphertext,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE                           pbAgreedSecret,
+                                            SIZE_T                          cbAgreedSecret );
+//
+// Performs the Decapsulate operation of Composite ML-KEM.
+// This uses the private information of a Composite ML-KEM keypair to generate an agreed
+// secret from a ciphertext.
+//
+// The arguments are the following:
+// - pkCompositeMlKemkey: a key which contains private information required for decapsulation.
+// - (pbCiphertext, cbCiphertext): a buffer containing an encapsulated secret.
+//   cbCiphertext must equal cbCiphertext given by SymCryptCompositeMlKemSizeofCiphertextFromParams,
+//   though typically this value can be known statically (see definition of
+//   SYMCRYPT_COMPOSITE_MLKEM_CIPHERTEXT_SIZE_*).
+// - (pbAgreedSecret, cbAgreedSecret): a buffer into which the generated secret is written.
+//   Currently cbAgreedSecret must be 32 for all parameterizations of Composite ML-KEM.
+//
+// Note: Given an invalid, but correctly-sized, ciphertext, the Composite ML-KEM Decapsulation operation
+// will "implicitly reject" the ciphertext, by returning success in equal time to a valid
+// decapsulation operation, with pseudo-random agreed secret output. This forces higher
+// level protocols to fail later when symmetric keys of peers do not match.
+// So decapsulate will only ever return an error if there are programming errors (e.g. incorrect size),
+// or something fundamentally goes wrong with the environment (e.g. internal memory allocation fails).
+//
+
+////////////////////////////////////////////////////////////
+// Module-Lattice-Based Digital Signature Algorithm (ML-DSA)
+////////////////////////////////////////////////////////////
+
+// Maximum length of the context string used in signing and verification
+#define SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH   (255)
+
+// ML-DSA key formats
+// ==================
+// The below formats apply **only to external formats**: When somebody is importing or exporting
+// a key. The internal format of the keys is not visible to the caller.
+typedef enum _SYMCRYPT_MLDSAKEY_FORMAT {
+    SYMCRYPT_MLDSAKEY_FORMAT_NULL               = 0,
+    SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED       = 1,
+        // 32-byte private root seed xi from which all other parameters can be derived.
+        // On its own it is ambiguous which ML-DSA parameter set this represents; callers wanting to
+        // store this format must track the parameter set alongside the key.
+    SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_KEY        = 2,
+        // Standard byte encoding of an ML-DSA private key, per FIPS 204.
+        // Size is 2560, 4032, or 4896 bytes for ML-DSA 44, 65, and 87 respectively.
+    SYMCRYPT_MLDSAKEY_FORMAT_PUBLIC_KEY         = 3,
+        // Standard byte encoding of an ML-DSA public key, per FIPS 204.
+        // Size is 1312, 1952, or 2592 bytes for ML-DSA 44, 65, and 87 respectively.
+} SYMCRYPT_MLDSAKEY_FORMAT;
+
+typedef enum _SYMCRYPT_MLDSA_PARAMS {
+    SYMCRYPT_MLDSA_PARAMS_NULL                  = 0,
+    SYMCRYPT_MLDSA_PARAMS_MLDSA44               = 1,
+    SYMCRYPT_MLDSA_PARAMS_MLDSA65               = 2,
+    SYMCRYPT_MLDSA_PARAMS_MLDSA87               = 3,
+} SYMCRYPT_MLDSA_PARAMS;
+// Currently supported ML-DSA parameter sets are represented externally only by the enum
+
+typedef enum _SYMCRYPT_PQDSA_HASH_ID {
+    SYMCRYPT_PQDSA_HASH_ID_NULL                = 0,
+    SYMCRYPT_PQDSA_HASH_ID_SHA256              = 1,
+    SYMCRYPT_PQDSA_HASH_ID_SHA384              = 2,
+    SYMCRYPT_PQDSA_HASH_ID_SHA512              = 3,
+    SYMCRYPT_PQDSA_HASH_ID_SHA512_256          = 4,
+    SYMCRYPT_PQDSA_HASH_ID_SHA3_256            = 5,
+    SYMCRYPT_PQDSA_HASH_ID_SHA3_384            = 6,
+    SYMCRYPT_PQDSA_HASH_ID_SHA3_512            = 7,
+    SYMCRYPT_PQDSA_HASH_ID_SHAKE128            = 8,
+    SYMCRYPT_PQDSA_HASH_ID_SHAKE256            = 9,
+} SYMCRYPT_PQDSA_HASH_ID;
+// Supported hash algorithms for use with Hash-ML-DSA
+
+//========================================================================
+// MLDSAKEY objects' API
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSizeofKeyFormatFromParams(
+            SYMCRYPT_MLDSA_PARAMS       params,
+            SYMCRYPT_MLDSAKEY_FORMAT    mlDsakeyFormat,
+    _Out_   SIZE_T*                     pcbKeyFormat );
+//
+// Gives the size in bytes of the blob of the given format for the given ML-DSA
+// parameters and the specified format via pcbKeyFormat output.
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_INCOMPATIBLE_FORMAT if mlDsakeyFormat is an unsupported value.
+// - SYMCRYPT_INVALID_ARGUMENT if other parameters are invalid.
+//
+
+#define SYMCRYPT_MLDSA_SIGNATURE_SIZE_MLDSA44    (2420)
+#define SYMCRYPT_MLDSA_SIGNATURE_SIZE_MLDSA65    (3309)
+#define SYMCRYPT_MLDSA_SIGNATURE_SIZE_MLDSA87    (4627)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSizeofSignatureFromParams(
+            SYMCRYPT_MLDSA_PARAMS       params,
+    _Out_   SIZE_T*                     pcbSignature );
+//
+// Gives the size in bytes of the signature for the given ML-DSA parameters.
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_INVALID_ARGUMENT if parameters are invalid.
+//
+
+_Success_( return != NULL )
+PSYMCRYPT_MLDSAKEY
+SYMCRYPT_CALL
+SymCryptMlDsakeyAllocate(
+            SYMCRYPT_MLDSA_PARAMS       params );
+//
+// Allocate a new ML-DSA key object sized according to the parameters.
+//
+// This call does not generate key material. It should be followed by a call to
+// SymCryptMlDsakeyGenerate or SymCryptMlDsakeySetValue.
+//
+// May return NULL if memory allocation fails.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsakeyFree(
+    _Post_invalid_  PSYMCRYPT_MLDSAKEY  pkMlDsakey );
+//
+// Free an ML-DSA key object that was allocated with SymCryptMlDsakeyAllocate.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsakeyGenerate(
+    _Inout_     PSYMCRYPT_MLDSAKEY      pkMlDsakey,
+                UINT32                  flags );
+//
+// Generate a new random ML-DSA key using the information from the
+// parameters passed to SymCryptMlDsakeyAllocate.
+//
+// Parameters:
+// - pkMlDsakey: a pointer to an ML-DSA key object allocated with SymCryptMlDsakeyAllocate
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_MEMORY_ALLOCATION_FAILURE if memory allocation fails.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsakeySetValue(
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_MLDSAKEY_FORMAT    mlDsakeyFormat,
+                                UINT32                      flags,
+    _Inout_                     PSYMCRYPT_MLDSAKEY          pkMlDsakey );
+//
+// Import key material to an ML-DSA key object from a byte blob.
+//
+// Parameters:
+// - (pbSrc, cbSrc): a buffer containing a representation of an ML-DSA key, in the format specified
+//   by the format parameter.
+// - mlDsakeyFormat: format of the input
+// - pkMlDsakey: a pointer to an ML-DSA key object allocated with SymCryptMlDsakeyAllocate.
+//
+// Allowed flags:
+//
+// - SYMCRYPT_FLAG_KEY_NO_FIPS
+//   Opt-out of performing validation required for FIPS
+//
+// Remarks:
+// - cbSrc must be equal to the cbKeyFormat returned from
+//   SymCryptMlDsaSizeofKeyFormatFromParams(params, format, &cbKeyFormat), though typically this
+//   value can be known statically (see definition of SYMCRYPT_MLDSAKEY_FORMAT)
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_INCOMPATIBLE_FORMAT if the key format is invalid.
+// - SYMCRYPT_INVALID_ARGUMENT if other arguments are invalid.
+// - SYMCRYPT_WRONG_KEY_SIZE if cbSrc does not match the expected size for the key format.
+// - SYMCRYPT_INVALID_BLOB if the encoded key is invalid.
+// - SYMCRYPT_MEMORY_ALLOCATION_FAILURE if memory allocation fails.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsakeyGetValue(
+    _In_                        PCSYMCRYPT_MLDSAKEY         pkMlDsakey,
+    _Out_writes_bytes_( cbDst ) PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+                                SYMCRYPT_MLDSAKEY_FORMAT    mlDsakeyFormat,
+                                UINT32                      flags );
+//
+// Export key material from an ML-DSA key object to a byte blob.
+//
+// Parameters:
+// - pkMlDsakey: pointer to a valid ML-DSA key object.
+// - (pbDst, cbDst): buffer for the exported ML-DSA key, in the format specified by the format
+//   parameter.
+// - mlDsakeyFormat: format of the output
+// - flags: no flags are currently defined; must be set to 0
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_INCOMPATIBLE_FORMAT if the key object does not have the information required to export
+//   the format specified by mlDsakeyFormat.
+// - SYMCRYPT_INVALID_ARGUMENT if the output buffer size or other arguments are incorrect.
+//
+// Remarks:
+// - cbDst must be equal to the cbKeyFormat returned from
+//   SymCryptMlDsaSizeofKeyFormatFromParams(params, format, &cbKeyFormat), though typically this
+//   value can be known statically (see definition of SYMCRYPT_MLDSAKEY_FORMAT)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSign(
+    _In_                                                PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _In_reads_bytes_( cbMessage )                       PCBYTE              pbMessage,
+                                                        SIZE_T              cbMessage,
+    _In_reads_bytes_opt_( cbContext )                   PCBYTE              pbContext,
+    _In_range_( 0, SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH )  SIZE_T              cbContext,
+                                                        UINT32              flags,
+    _Out_writes_bytes_( cbSignature )                   PBYTE               pbSignature,
+                                                        SIZE_T              cbSignature );
+//
+// Sign a message using "pure" ML-DSA. The message can be of arbitrary length.
+//
+// Parameters:
+// - pkMlDsakey: an ML-DSA key object. Must contain the private key material.
+// - (pbMessage, cbMessage): the message to sign. May be of arbitrary length.
+// - (pbContext, cbContext): an optional context string which will be included in the message
+//   representative to be signed. Length must be <= SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH.
+// - flags: no flags are currently defined; must be set to 0
+// - (pbSignature, cbSignature): the buffer into which the signature is written.
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_INVALID_ARGUMENT if the key object does not contain a private key, or if other
+//   parameters are invalid.
+// - SYMCRYPT_MEMORY_ALLOCATION_FAILURE if memory allocation fails.
+//
+// Remarks:
+//   cbSignature must be equal to the cbKeyFormat returned from
+//   SymCryptMlDsaSizeofSignatureFromParams( params, &cbSignature ), though typically this
+//   value can be known statically (see definition of SYMCRYPT_MLDSA_SIGNATURE_SIZE_*).
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptExternalMuMlDsaSign(
+    _In_                                                PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _In_reads_bytes_( cbMu )                            PCBYTE              pbMu,
+                                                        SIZE_T              cbMu,
+                                                        UINT32              flags,
+    _Out_writes_bytes_( cbSignature )                   PBYTE               pbSignature,
+                                                        SIZE_T              cbSignature );
+//
+// Sign a precomputed message representative Mu.
+//
+// Parameters:
+// - (pbMu, cbMu): the message representative to sign,
+//   which must be of size 64 (SYMCRYPT_SHAKE256_RESULT_SIZE).
+// - All other parameters are the same as for SymCryptMlDsaSign.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHashMlDsaSign(
+    _In_                                                PCSYMCRYPT_MLDSAKEY     pkMlDsakey,
+                                                        SYMCRYPT_PQDSA_HASH_ID  hashAlg,
+    _In_reads_bytes_( cbHash )                          PCBYTE                  pbHash,
+                                                        SIZE_T                  cbHash,
+    _In_reads_bytes_opt_( cbContext )                   PCBYTE                  pbContext,
+    _In_range_( 0, SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH )  SIZE_T                  cbContext,
+                                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )                   PBYTE                   pbSignature,
+                                                        SIZE_T                  cbSignature );
+//
+// Sign a message using "pre-hash" ML-DSA. The caller precomputes the hash of the message.
+//
+// Parameters:
+// - hashAlg: the ID of the hash algorithm used to compute pbHash.
+// - (pbHash, cbHash): the hash of the message to sign.
+// - All other parameters are the same as for SymCryptMlDsaSign.
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR on success.
+// - SYMCRYPT_INVALID_ARGUMENT if the key object does not contain a private key, or if other
+//   parameters are invalid.
+// - SYMCRYPT_MEMORY_ALLOCATION_FAILURE if memory allocation fails.
+//
+// Remarks:
+//   The hash algorithm provided must meet the minimum required collision strength defined for the
+//   chosen ML-DSA parameter set. This is the lambda parameter in FIPS 204. This means that the
+//   following hash algorithms are supported:
+//
+//   ML-DSA-44 (lambda = 128): SHA-256, SHA-384, SHA-512, SHA-512/256, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256
+//   ML-DSA-65 (lambda = 192): SHA-384, SHA-512, SHA3-384, SHA3-512, SHAKE256
+//   ML-DSA-87 (lambda = 256): SHA-512, SHA3-512, SHAKE256
+//
+//   Additionally, cbHash must match the output length of the hash algorithm.
+//   For XOFs, the any output length >= the minimum collision strength is acceptable. If this
+//   requirement is not met, the function returns SYMCRYPT_INVALID_ARGUMENT.
+//
+//   As with SymCryptMlDsaSign, cbSignature must be equal to the cbKeyFormat returned from
+//   SymCryptMlDsaSizeofSignatureFromParams( params, &cbSignature ), though typically this
+//   value can be known statically (see definition of SYMCRYPT_MLDSA_SIGNATURE_SIZE_*).
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaVerify(
+    _In_                                                PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _In_reads_bytes_( cbMessage )                       PCBYTE              pbMessage,
+                                                        SIZE_T              cbMessage,
+    _In_reads_bytes_opt_( cbContext )                   PCBYTE              pbContext,
+    _In_range_( 0, SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH )  SIZE_T              cbContext,
+    _In_reads_bytes_( cbSignature )                     PCBYTE              pbSignature,
+                                                        SIZE_T              cbSignature,
+                                                        UINT32              flags );
+//
+// Verify a signature using "pure" ML-DSA. The message can be of arbitrary length.
+//
+// Parameters:
+// - pkMlDsakey: the ML-DSA key object used to verify the signature.
+// - (pbMessage, cbMessage): the message that the signature was generated from.
+// - (pbContext, cbContext): an optional context string which will be included in the message
+//   representative to be signed. Length must be <= SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH.
+// - (pbSignature, cbSignature): the signature to verify.
+// - flags: no flags are currently defined; must be set to 0
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR if the signature was verified successfully.
+// - SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE if the signature is invalid.
+// - SYMCRYPT_INVALID_ARGUMENT if the parameters are invalid.
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptExternalMuMlDsaVerify(
+    _In_                                                PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _In_reads_bytes_( cbMu )                            PCBYTE              pbMu,
+                                                        SIZE_T              cbMu,
+    _In_reads_bytes_( cbSignature )                     PCBYTE              pbSignature,
+                                                        SIZE_T              cbSignature,
+                                                        UINT32              flags );
+//
+// Verify a signature of a precomputed message representative Mu.
+//
+// Parameters:
+// - (pbMu, cbMu): the message representative that was signed,
+//   which must be of size 64 (SYMCRYPT_SHAKE256_RESULT_SIZE).
+// - All other parameters are the same as for SymCryptMlDsaVerify.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHashMlDsaVerify(
+    _In_                                                PCSYMCRYPT_MLDSAKEY     pkMlDsakey,
+                                                        SYMCRYPT_PQDSA_HASH_ID  hashAlg,
+    _In_reads_bytes_( cbHash )                          PCBYTE                  pbHash,
+                                                        SIZE_T                  cbHash,
+    _In_reads_bytes_opt_( cbContext )                   PCBYTE                  pbContext,
+    _In_range_( 0, SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH )  SIZE_T                  cbContext,
+    _In_reads_bytes_( cbSignature )                     PCBYTE                  pbSignature,
+                                                        SIZE_T                  cbSignature,
+                                                        UINT32                  flags );
+//
+// Verify a signature using "pre-hash" ML-DSA. The caller precomputes the hash of the message.
+//
+// Parameters:
+// - hashAlg: the ID of the hash algorithm used to compute pbHash.
+// - (pbHash, cbHash): the hash of the message that the signature was generated from.
+// - All other parameters are the same as for SymCryptMlDsaVerify.
+//
+// Return values:
+// - SYMCRYPT_NO_ERROR if the signature was validated successfully.
+// - SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE if the signature is invalid.
+// - SYMCRYPT_INVALID_ARGUMENT if the parameters are invalid.
+//
+// Remarks:
+//   See the remarks for SymCryptHashMlDsaSign regarding the required security strength of the hash
+//   algorithm. For unsupported hash algorithms, the function will return SYMCRYPT_INVALID_ARGUMENT.
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaSelftest( void );
+//
+// FIPS selftest for ML-DSA
+//
+
+_Analysis_noreturn_
+VOID
+SYMCRYPT_CALL
+SymCryptFatal( UINT32 fatalCode );
+//
+// Call the Fatal routine passed to the library upon initialization
+// We use the SYMCRYPT_ASSERT macro to catch problems in Debug builds
+//
+
+
+typedef struct _SYMCRYPT_UINT32_MAP {
+    UINT32              from;       // map this value...
+    UINT32              to;         // ...into this value
+} SYMCRYPT_UINT32_MAP, *PSYMCRYPT_UINT32_MAP;
+typedef const SYMCRYPT_UINT32_MAP * PCSYMCRYPT_UINT32_MAP;
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMapUint32(
+                        UINT32                  u32Input,
+                        UINT32                  u32Default,
+    _In_reads_( nMap )  PCSYMCRYPT_UINT32_MAP   pcMap,
+                        SIZE_T                  nMap );
+//
+// Map values in a side-channel safe way, typically used for mapping error codes.
+//
+// (pcMap, nMap) point to an array of nMap entries of type SYMCRYPT_UINT32_MAP;
+// each entry specifies a single mapping. If u32Input matches the
+// 'from' field, the return value will be the 'to' field value.
+// If u32Input is not equal to any 'from' field values, the return value is u32Default.
+// Both u32Input and the return value are treated as secrets w.r.t. side channels.
+//
+// If multiple map entries have the same 'from' field value, then the return value
+// is one of the several 'to' field values; which one is not defined.
+//
+// This function is particularly useful when mapping error codes in situations where
+// the actual error cannot be revealed through side channels.
+
+#if SYMCRYPT_DEBUG
+#define SYMCRYPT_ASSERT( _x ) \
+    {\
+        if( !(_x) ){ SymCryptFatal( 'asrt' ); }\
+    }\
+    _Analysis_assume_( _x )
+#else
+#define SYMCRYPT_ASSERT( _x ) \
+    _Analysis_assume_( _x )
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libs/symcrypt/inc/symcrypt_internal.h b/libs/symcrypt/inc/symcrypt_internal.h
new file mode 100644
index 000000000000..488711b1036c
--- /dev/null
+++ b/libs/symcrypt/inc/symcrypt_internal.h
@@ -0,0 +1,3767 @@
+//
+// SymCrypt_internal.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This file contains information that is internal to the symcrypt library,
+// but which still needs to be known to the compiler to be able to use the library.
+// This includes structure declarations and all support for inline implementations
+// of some of the library functions.
+// Information in this file is not part of the API and can change at any time.
+//
+
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+
+//
+// We use Prefast pragmas, but they are not recognized by the compiler.
+// We disable the 'unknown pragma' warning if we are not in prefast mode.
+//
+#ifndef _PREFAST_
+#pragma warning(disable:4068)
+#endif
+
+//==============================================================================================
+//  PLATFORM/COMPILER DETECTION
+//==============================================================================================
+
+#define SYMCRYPT_PLATFORM_WINDOWS 0
+#define SYMCRYPT_PLATFORM_APPLE   0 // macOS and other Apple platforms
+#define SYMCRYPT_PLATFORM_UNIX    0 // Linux and other Unix-likes, besides macOS. Must support POSIX.
+
+#if defined(_WIN32)
+    #undef  SYMCRYPT_PLATFORM_WINDOWS
+    #define SYMCRYPT_PLATFORM_WINDOWS 1
+#elif defined(__APPLE__)
+    #undef  SYMCRYPT_PLATFORM_APPLE
+    #define SYMCRYPT_PLATFORM_APPLE 1
+#elif (defined(linux) || defined(__unix__))
+    #undef  SYMCRYPT_PLATFORM_UNIX
+    #define SYMCRYPT_PLATFORM_UNIX 1
+#endif
+
+#define SYMCRYPT_MS_VC       0 // Microsoft compiler (cl.exe - Visual Studio/MSBuild)
+#define SYMCRYPT_GNUC        0 // GCC and compatible compilers (including Clang)
+
+#if defined(_MSC_VER)
+    #undef  SYMCRYPT_MS_VC
+    #define SYMCRYPT_MS_VC  1
+#elif defined(__GNUC__)
+    #undef  SYMCRYPT_GNUC
+    #define SYMCRYPT_GNUC 1
+#else
+    #error Unsupported compiler
+#endif
+
+#if SYMCRYPT_MS_VC
+
+// This should go somewhere else. Same in the other #if branches.
+#define SYMCRYPT_ANYSIZE_ARRAY               1
+#define SYMCRYPT_NOINLINE __declspec(noinline)
+#define SYMCRYPT_CDECL __cdecl
+#define SYMCRYPT_FASTCALL __fastcall
+
+#define SYMCRYPT_UNALIGNED
+
+#elif SYMCRYPT_GNUC
+
+// Ignore the multi-character character constant warnings
+#pragma GCC diagnostic ignored "-Wmultichar"
+#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
+
+#define SYMCRYPT_ANYSIZE_ARRAY               1
+#define SYMCRYPT_NOINLINE           __attribute__ ((noinline))
+#define SYMCRYPT_UNALIGNED
+#define SYMCRYPT_CDECL
+#define SYMCRYPT_FASTCALL           __attribute__((fastcall))
+
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wmultichar"
+#pragma clang diagnostic ignored "-Wincompatible-function-pointer-types"
+#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
+#endif
+
+//==============================================================================================
+//  PLATFORM SPECIFICS
+//==============================================================================================
+
+//
+// SYMCRYPT_CALL & SYMCRYPT_ALIGN
+//
+// SYMCRYPT_CALL is a macro that selects the calling convention used by the library.
+// Crypto functions often have to perform very many small operations, and a fast calling convention is
+// preferable. We use __fastcall on platforms that support it.
+//
+// SYMCRYPT_ALIGN is the default alignment for the platform.
+// On platforms that have alignment restrictions the default alignment should be large enough that
+// an aligned BYTE * can be cast to a pointer to a UINT32 and be used.
+//
+//
+// The SYMCRYPT_IGNORE_PLATFORM macro can be defined to switch off any platform-specific
+// optimizations and run just the C implementations.
+// The rest of the library uses SYMCRYPT_CPU_* macros to make platform decisions.
+//
+//
+// WARNING: both the library and the calling application must be compiled with the same
+// set of flags, as the flags affect things like the structure layout and size and
+// the calling convention, both of which need to be in sync between the lib and the caller.
+//
+
+//#define SYMCRYPT_IGNORE_PLATFORM        // #defining this flag disables all platform optimizations.
+
+#define SYMCRYPT_CPU_X86            0
+#define SYMCRYPT_CPU_AMD64          0
+#define SYMCRYPT_CPU_ARM            0
+#define SYMCRYPT_CPU_ARM64          0
+#define SYMCRYPT_CPU_UNKNOWN        0
+
+#if (defined( _X86_ ) || defined( _M_IX86 ) || defined( __i386__ )) && !defined ( SYMCRYPT_IGNORE_PLATFORM )
+
+#undef  SYMCRYPT_CPU_X86
+#define SYMCRYPT_CPU_X86        1
+
+#define SYMCRYPT_CALL           SYMCRYPT_FASTCALL
+#define SYMCRYPT_ALIGN_VALUE    4
+
+#ifndef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:4359)   // *** Alignment specifier is less than actual alignment
+#endif
+
+#elif (defined( _ARM64_ ) || defined( _ARM64EC_ ) || defined( _M_ARM64 ) || defined( __aarch64__ ) || defined(__arm64ec__)) && !defined( SYMCRYPT_IGNORE_PLATFORM )
+
+#undef  SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_CPU_ARM64      1
+#define SYMCRYPT_CALL
+#define SYMCRYPT_ALIGN_VALUE    16
+
+#elif (defined( _AMD64_ ) || defined( _M_AMD64 ) || defined( __amd64__ )) && !defined ( SYMCRYPT_IGNORE_PLATFORM )
+
+#undef  SYMCRYPT_CPU_AMD64
+#define SYMCRYPT_CPU_AMD64      1
+
+#define SYMCRYPT_CALL
+#define SYMCRYPT_ALIGN_VALUE    16
+
+#elif (defined( _ARM_ ) || defined( _M_ARM ) || defined( __arm__ )) && !defined( SYMCRYPT_IGNORE_PLATFORM )
+
+#undef  SYMCRYPT_CPU_ARM
+#define SYMCRYPT_CPU_ARM        1
+#define SYMCRYPT_CALL
+#define SYMCRYPT_ALIGN_VALUE    8
+
+#elif defined( SYMCRYPT_IGNORE_PLATFORM )
+
+#undef  SYMCRYPT_CPU_UNKNOWN
+#define SYMCRYPT_CPU_UNKNOWN    1
+#define SYMCRYPT_CALL
+#define SYMCRYPT_ALIGN_VALUE    16
+
+#ifndef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:4359)   // *** Alignment specifier is less than actual alignment
+#endif
+
+#else
+
+#error Unknown CPU platform
+
+#endif   // SYMCRYPT_CALL platforms switch
+
+
+//
+// Datatypes used by the SymCrypt library. This ensures compatibility
+// with multiple environments, such as Windows, iOS, and Android.
+//
+
+#if SYMCRYPT_PLATFORM_WINDOWS
+
+    //
+    // Types included in intsafe.h:
+    //      BYTE,
+    //      INT16, UINT16,
+    //      INT32, UINT32,
+    //      INT64, UINT64,
+    //      UINT_PTR
+    // and macro:
+    //      UINT32_MAX
+    //
+#include <intsafe.h>
+
+#else
+
+#include <stdint.h>
+
+typedef uint8_t         BYTE;
+
+#ifndef UINT32_MAX
+#define UINT32_MAX      (0xffffffff)
+#endif
+
+#ifndef TRUE
+#define TRUE            0x01
+#endif
+
+#ifndef FALSE
+#define FALSE           0x00
+#endif
+
+// Size_t
+typedef size_t          SIZE_T;
+
+#ifndef SIZE_T_MAX
+#define SIZE_T_MAX      SIZE_MAX
+#endif
+
+typedef int                 BOOL;
+
+typedef int8_t              INT8, *PINT8;
+typedef int16_t             INT16, *PINT16;
+typedef int32_t             INT32, *PINT32;
+typedef int64_t             INT64, *PINT64;
+typedef uint8_t             UINT8, *PUINT8;
+typedef uint16_t            UINT16, *PUINT16;
+typedef uint32_t            UINT32, *PUINT32;
+typedef uint64_t            UINT64, *PUINT64;
+
+// minwindef.h
+typedef char CHAR;
+
+#endif //WIN32
+
+#include <stddef.h>
+
+//
+// Pointer types
+//
+typedef BYTE *          PBYTE;
+typedef const BYTE *    PCBYTE;
+
+typedef UINT16 *        PUINT16;
+typedef const UINT16 *  PCUINT16;
+
+typedef UINT32 *        PUINT32;
+typedef const UINT32 *  PCUINT32;
+
+typedef UINT64 *        PUINT64;
+typedef const UINT64 *  PCUINT64;
+
+// Void
+
+#ifndef VOID
+#define VOID void
+#endif
+
+typedef void *          PVOID;
+typedef const void *    PCVOID;
+
+// winnt.h
+typedef BYTE  BOOLEAN;
+
+// Useful macros for structs
+#define SYMCRYPT_FIELD_OFFSET(type, field)      (offsetof(type, field))
+#define SYMCRYPT_FIELD_SIZE(type, field)        (sizeof( ((type *)0)->field ))
+
+#if SYMCRYPT_MS_VC
+
+#ifndef FORCEINLINE
+#if (_MSC_VER >= 1200)
+#define FORCEINLINE __forceinline
+#else
+#define FORCEINLINE __inline
+#endif
+#endif
+
+#else
+
+#undef FORCEINLINE
+#define FORCEINLINE static inline
+
+#endif
+
+C_ASSERT( (SYMCRYPT_ALIGN_VALUE & (SYMCRYPT_ALIGN_VALUE - 1 )) == 0 );
+#define SYMCRYPT_ALIGN_UP( _p ) ((PBYTE) ( ((SIZE_T) (_p) + SYMCRYPT_ALIGN_VALUE - 1) & ~(SYMCRYPT_ALIGN_VALUE - 1 ) ) )
+
+#if SYMCRYPT_MS_VC
+    #define SYMCRYPT_ALIGN_AT(alignment)                 __declspec(align(alignment))
+    #define SYMCRYPT_WEAK_SYMBOL
+#elif SYMCRYPT_GNUC
+    #define SYMCRYPT_ALIGN_AT(alignment)                 __attribute__((aligned(alignment)))
+    #define SYMCRYPT_WEAK_SYMBOL                         __attribute__((weak))
+#else
+    #define SYMCRYPT_ALIGN_AT(alignment)
+    #define SYMCRYPT_WEAK_SYMBOL
+#endif
+#define SYMCRYPT_ALIGN_TYPE_AT(typename, alignment)  typename SYMCRYPT_ALIGN_AT(alignment)
+#define SYMCRYPT_ALIGN          SYMCRYPT_ALIGN_AT(SYMCRYPT_ALIGN_VALUE)
+#define SYMCRYPT_ALIGN_STRUCT   SYMCRYPT_ALIGN_TYPE_AT(struct, SYMCRYPT_ALIGN_VALUE)
+#define SYMCRYPT_ALIGN_UNION    SYMCRYPT_ALIGN_TYPE_AT(union, SYMCRYPT_ALIGN_VALUE)
+
+
+#define SYMCRYPT_MAX( _a, _b )  ((_a)>(_b)?(_a):(_b))
+#define SYMCRYPT_MIN( _a, _b )  ((_a)<(_b)?(_a):(_b))
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+//
+// XMM related declarations, used in data structures.
+//
+#pragma prefast(push)
+#pragma prefast(disable: 28251, "Windows headers define _mm_clflush with SAL annotation, Intel header doesn't have SAL annotation leading to inconsistent annotation errors")
+#include <emmintrin.h>
+#pragma prefast(pop)
+#endif
+
+
+//
+// To provide quick error detection we have magic values in all
+// our data structures, but only in CHKed builds.
+// Our magic value depends on the address of the structure.
+// This has the advantage that we detect blind memcpy's of our data structures.
+// Memcpy is not supported as it limits what the library is allowed to do.
+// Where needed the library provides for copy functions of its internal data structures.
+//
+#if SYMCRYPT_DEBUG
+    #define SYMCRYPT_MAGIC_ENABLED
+#endif
+
+#if defined(SYMCRYPT_MAGIC_ENABLED )
+
+#define SYMCRYPT_MAGIC_FIELD        SIZE_T   magic;
+#define SYMCRYPT_MAGIC_VALUE( p )   ((SIZE_T) p + 'S1mv' + SYMCRYPT_API_VERSION)
+
+
+#define SYMCRYPT_SET_MAGIC( p )     {(p)->magic = SYMCRYPT_MAGIC_VALUE( p );}
+#define SYMCRYPT_CHECK_MAGIC( p )   {if((p)->magic!=SYMCRYPT_MAGIC_VALUE(p)) SymCryptFatal('magc');}
+#define SYMCRYPT_WIPE_MAGIC( p )    {(p)->magic = 0;}
+
+#else
+
+//
+// We define the magic field even for FRE builds, because we get too many
+// hard-to-debug problems with people who accidentally mix FRE headers with CHKed libraries,
+// or the other way around.
+// E.g. BitLocker only publishes the FRE version of their library, and building a CHKed binary with
+// that FRE lib crashes
+//
+
+#define SYMCRYPT_MAGIC_FIELD        SIZE_T   magic;
+#define SYMCRYPT_SET_MAGIC( p )
+#define SYMCRYPT_CHECK_MAGIC( p )
+#define SYMCRYPT_WIPE_MAGIC( p )
+
+#endif
+
+//
+// CPU feature detection infrastructure
+//
+
+#if !SYMCRYPT_PLATFORM_WINDOWS
+    // Forward declarations for CPUID intrinsic replacements
+    void __cpuidex(int CPUInfo[4], int InfoType, int ECXValue);
+#endif
+
+#if SYMCRYPT_CPU_ARM || SYMCRYPT_CPU_ARM64
+
+#define SYMCRYPT_CPU_FEATURE_NEON           0x01
+#define SYMCRYPT_CPU_FEATURE_NEON_AES       0x02
+#define SYMCRYPT_CPU_FEATURE_NEON_PMULL     0x04
+#define SYMCRYPT_CPU_FEATURE_NEON_SHA256    0x08
+
+#elif SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+//
+// We keep the most commonly tested bits in the least significant byte, to make it easier for the compiler to optimize
+// There is a many to one relationship between CPUID feature flags and SYMCRYPT_CPU_FEATURE_XXX bits
+// since a SYMCRYPT_CPU_FEATURE_XXX could require multiple CPUID features.
+
+#define SYMCRYPT_CPU_FEATURE_SSE2               0x0001          // includes SSE, SSE2
+#define SYMCRYPT_CPU_FEATURE_SSSE3              0x0002          // includes SSE, SSE2, SSE3, SSSE3
+#define SYMCRYPT_CPU_FEATURE_AESNI              0x0004
+#define SYMCRYPT_CPU_FEATURE_PCLMULQDQ          0x0008
+#define SYMCRYPT_CPU_FEATURE_AVX2               0x0010          // includes AVX, AVX2 - also indicates support for saving/restoring Ymm registers
+#define SYMCRYPT_CPU_FEATURE_SAVEXMM_NOFAIL     0x0020          // if SymCryptSaveXmm() will never fail
+#define SYMCRYPT_CPU_FEATURE_SHANI              0x0040
+#define SYMCRYPT_CPU_FEATURE_BMI2               0x0080          // MULX, RORX, SARX, SHLX, SHRX
+
+#define SYMCRYPT_CPU_FEATURE_ADX                0x0100          // ADCX, ADOX
+#define SYMCRYPT_CPU_FEATURE_RDRAND             0x0200
+#define SYMCRYPT_CPU_FEATURE_RDSEED             0x0400
+#define SYMCRYPT_CPU_FEATURE_VAES               0x0800          // support for VAES and VPCLMULQDQ (may only be supported on Ymm registers (i.e. Zen3))
+#define SYMCRYPT_CPU_FEATURE_AVX512             0x1000          // includes F, VL, DQ, BW (VL allows AVX-512 instructions to be used on Xmm and Ymm registers)
+                                                                // also indicates support for saving/restoring additional AVX-512 state
+
+#define SYMCRYPT_CPU_FEATURE_CMPXCHG16B         0x2000          // Compare and Swap 128b value
+
+#endif
+
+typedef UINT32 SYMCRYPT_CPU_FEATURES;
+
+//
+// We have two feature fields.
+// g_SymCryptCpuFeaturesNotPresent reports with features are not present on the current CPU
+// SymCryptCpuFeaturesNeverPresent() is a function that returns a static (compiler-predictable) value,
+//  and allows the environment to lock out features in a way that the compiler can optimize away all the code that uses these features.
+// Using a function allows the environment macro to forward it to an environment-specific function.
+//
+
+extern SYMCRYPT_CPU_FEATURES g_SymCryptCpuFeaturesNotPresent;
+
+SYMCRYPT_CPU_FEATURES
+SYMCRYPT_CALL
+SymCryptCpuFeaturesNeverPresent(void);
+
+#define SYMCRYPT_CPU_FEATURES_PRESENT( x )   ( ((x) & SymCryptCpuFeaturesNeverPresent()) == 0 && ( (x) & g_SymCryptCpuFeaturesNotPresent ) == 0 )
+
+//
+// VOLATILE MEMORY ACCESS
+//
+// These macros are used to explicitly handle volatile memory access independent of compiler settings.
+// If volatile memory is accessed directly without using the appropriate macro, MSVC may emit warning
+// C4746, because the volatile semantics depend on the value of the /volatile flag, which can result in
+// undesired hardware memory barriers that impact performance.
+//
+// More info:
+// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-c4746?view=msvc-170
+// https://docs.microsoft.com/en-us/cpp/build/reference/volatile-volatile-keyword-interpretation?view=msvc-170
+//
+
+#if SYMCRYPT_MS_VC  // Microsoft VC++ Compiler
+
+    #if SYMCRYPT_CPU_ARM || SYMCRYPT_CPU_ARM64
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ8( _p )    ( __iso_volatile_load8( (const volatile char*)(_p) ) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ16( _p )   ( __iso_volatile_load16( (const volatile short*)(_p) ) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ32( _p )   ( __iso_volatile_load32( (const volatile int*)(_p) ) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ64( _p )   ( __iso_volatile_load64( (const volatile __int64*)(_p) ) )
+
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE8( _p, _v )  ( __iso_volatile_store8( (volatile char*)(_p), (_v) ) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE16( _p, _v ) ( __iso_volatile_store16( (volatile short*)(_p), (_v) ) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE32( _p, _v ) ( __iso_volatile_store32( (volatile int*)(_p), (_v) ) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE64( _p, _v ) ( __iso_volatile_store64( (volatile __int64*)(_p), (_v) ) )
+    #elif SYMCRYPT_CPU_X86 || SYMCRYPT_CPU_AMD64
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ8( _p )    ( *((const volatile BYTE*)  (_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ16( _p )   ( *((const volatile UINT16*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ32( _p )   ( *((const volatile UINT32*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ64( _p )   ( *((const volatile UINT64*)(_p)) )
+
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE8( _p, _v )  ( *((volatile BYTE*)  (_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE16( _p, _v ) ( *((volatile UINT16*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE32( _p, _v ) ( *((volatile UINT32*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE64( _p, _v ) ( *((volatile UINT64*)(_p)) = (_v) )
+    #else // Temporary workaround for CMake compilation issues on Windows. Assume X86/ADM64.
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ8( _p )    ( *((const volatile BYTE*)  (_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ16( _p )   ( *((const volatile UINT16*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ32( _p )   ( *((const volatile UINT32*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ64( _p )   ( *((const volatile UINT64*)(_p)) )
+
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE8( _p, _v )  ( *((volatile BYTE*)  (_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE16( _p, _v ) ( *((volatile UINT16*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE32( _p, _v ) ( *((volatile UINT32*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE64( _p, _v ) ( *((volatile UINT64*)(_p)) = (_v) )
+    #endif
+
+#elif SYMCRYPT_GNUC
+
+    #if !SYMCRYPT_CPU_ARM
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ8( _p )    ( *((const volatile BYTE*)  (_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ16( _p )   ( *((const volatile UINT16*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ32( _p )   ( *((const volatile UINT32*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ64( _p )   ( *((const volatile UINT64*)(_p)) )
+
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE8( _p, _v )  ( *((volatile BYTE*)  (_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE16( _p, _v ) ( *((volatile UINT16*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE32( _p, _v ) ( *((volatile UINT32*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE64( _p, _v ) ( *((volatile UINT64*)(_p)) = (_v) )
+    #else // SYMCRYPT_CPU_ARM
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ8( _p )    ( *((const volatile BYTE*)  (_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ16( _p )   ( *((const volatile UINT16*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ32( _p )   ( *((const volatile UINT32*)(_p)) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_READ64( p ) ( (UINT64)SYMCRYPT_INTERNAL_VOLATILE_READ32(&((PBYTE)p)[4]) << 32 |  SYMCRYPT_INTERNAL_VOLATILE_READ32(&((PBYTE)p)[0]) )
+
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE8( _p, _v )  ( *((volatile BYTE*)  (_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE16( _p, _v ) ( *((volatile UINT16*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE32( _p, _v ) ( *((volatile UINT32*)(_p)) = (_v) )
+        #define SYMCRYPT_INTERNAL_VOLATILE_WRITE64( p, x ) { \
+            SYMCRYPT_INTERNAL_VOLATILE_WRITE32( &((PBYTE)p)[0], (UINT32)((x)    ) );\
+            SYMCRYPT_INTERNAL_VOLATILE_WRITE32( &((PBYTE)p)[4], (UINT32)(((UINT64)(x))>>32) );\
+            }
+    #endif
+
+#else
+
+    #error Unknown compiler
+
+#endif
+
+//
+// FORCED MEMORY ACCESS
+//
+// These macros force a memory access. That is, they require that the memory
+// read or write takes place, and do not allow the compiler to optimize the access
+// away.
+// They provide no other memory ordering requirements, so there are no acquire/release
+// semantics, memory barriers, etc.
+//
+// The generic versions are implemented with a volatile access, but that is inefficient on some platforms
+// because it might introduce memory ordering requirements.
+//
+
+#define SYMCRYPT_INTERNAL_FORCE_READ8( _p )    SYMCRYPT_INTERNAL_VOLATILE_READ8( _p )
+#define SYMCRYPT_INTERNAL_FORCE_READ16( _p )   SYMCRYPT_INTERNAL_VOLATILE_READ16( _p )
+#define SYMCRYPT_INTERNAL_FORCE_READ32( _p )   SYMCRYPT_INTERNAL_VOLATILE_READ32( _p )
+#define SYMCRYPT_INTERNAL_FORCE_READ64( _p )   SYMCRYPT_INTERNAL_VOLATILE_READ64( _p )
+
+#define SYMCRYPT_INTERNAL_FORCE_WRITE8( _p, _v )  SYMCRYPT_INTERNAL_VOLATILE_WRITE8( _p, _v )
+#define SYMCRYPT_INTERNAL_FORCE_WRITE16( _p, _v ) SYMCRYPT_INTERNAL_VOLATILE_WRITE16( _p, _v )
+#define SYMCRYPT_INTERNAL_FORCE_WRITE32( _p, _v ) SYMCRYPT_INTERNAL_VOLATILE_WRITE32( _p, _v )
+#define SYMCRYPT_INTERNAL_FORCE_WRITE64( _p, _v ) SYMCRYPT_INTERNAL_VOLATILE_WRITE64( _p, _v )
+
+//
+// FIXED ENDIANNESS ACCESS
+//
+// Fixed endianness load and store
+// We do this by platform because it affected by both endianness and alignment requirements
+// The p pointer is always a pointer to BYTE
+//
+#if SYMCRYPT_MS_VC  // Microsoft VC++ Compiler
+    #define SYMCRYPT_BSWAP16( x ) _byteswap_ushort(x)
+    #define SYMCRYPT_BSWAP32( x ) _byteswap_ulong(x)
+    #define SYMCRYPT_BSWAP64( x ) _byteswap_uint64(x)
+#elif SYMCRYPT_GNUC
+    #define SYMCRYPT_BSWAP16( x ) __builtin_bswap16(x)
+    #define SYMCRYPT_BSWAP32( x ) __builtin_bswap32(x)
+    #define SYMCRYPT_BSWAP64( x ) __builtin_bswap64(x)
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+
+
+//
+// X86, AMD64, ARM, and ARM64 have no alignment restrictions, and are little-endian.
+// We do straight store/loads with BSWAPs where required.
+// This technically relies upon on undefined behavior, as we assume the compiler will translate
+// operations on unaligned pointers to 2, 4, and 8 bytes types to appropriately unaligned store/load
+// instructions on these platforms (not just in these macros). This works for all compilers we
+// currently use.
+//
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST16( p ) SYMCRYPT_BSWAP16( *((UINT16 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST16( p )                 ( *((UINT16 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST32( p ) SYMCRYPT_BSWAP32( *((UINT32 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST32( p )                 ( *((UINT32 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST64( p ) SYMCRYPT_BSWAP64( *((UINT64 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST64( p )                 ( *((UINT64 *)(p)) )
+
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST16( p, x ) ( *(UINT16 *)(p) = SYMCRYPT_BSWAP16(x) )
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST16( p, x ) ( *(UINT16 *)(p) =                 (x) )
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST32( p, x ) ( *(UINT32 *)(p) = SYMCRYPT_BSWAP32(x) )
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST32( p, x ) ( *(UINT32 *)(p) =                 (x) )
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST64( p, x ) ( *(UINT64 *)(p) = SYMCRYPT_BSWAP64(x) )
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST64( p, x ) ( *(UINT64 *)(p) =                 (x) )
+
+#elif SYMCRYPT_CPU_ARM
+
+//
+// Only 64 bit accesses need to be aligned.
+//
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST16( p ) SYMCRYPT_BSWAP16( *((UINT16 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST16( p )                 ( *((UINT16 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST32( p ) SYMCRYPT_BSWAP32( *((UINT32 *)(p)) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST32( p )                 ( *((UINT32 *)(p)) )
+
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST64( p ) ( (UINT64)SYMCRYPT_INTERNAL_LOAD_MSBFIRST32(&((PBYTE)p)[0]) << 32 | SYMCRYPT_INTERNAL_LOAD_MSBFIRST32(&((PBYTE)p)[4]) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST64( p ) ( (UINT64)SYMCRYPT_INTERNAL_LOAD_LSBFIRST32(&((PBYTE)p)[4]) << 32 | SYMCRYPT_INTERNAL_LOAD_LSBFIRST32(&((PBYTE)p)[0]) )
+
+
+
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST16( p, x ) ( *(UINT16 *)(p) = SYMCRYPT_BSWAP16(x) )
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST16( p, x ) ( *(UINT16 *)(p) =                 (x) )
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST32( p, x ) ( *(UINT32 *)(p) = SYMCRYPT_BSWAP32(x) )
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST32( p, x ) ( *(UINT32 *)(p) =                 (x) )
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST64( p, x ) { \
+    SYMCRYPT_INTERNAL_STORE_MSBFIRST32( &((PBYTE)p)[0],(UINT32)(((UINT64)(x))>>32) );\
+    SYMCRYPT_INTERNAL_STORE_MSBFIRST32( &((PBYTE)p)[4],(UINT32)(x));\
+    }
+
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST64( p, x ) { \
+    SYMCRYPT_INTERNAL_STORE_LSBFIRST32( &((PBYTE)p)[0], (UINT32)((x)    ) );\
+    SYMCRYPT_INTERNAL_STORE_LSBFIRST32( &((PBYTE)p)[4], (UINT32)(((UINT64)(x))>>32) );\
+    }
+#else // unknown platform
+
+//
+// These functions have to handle arbitrary alignments too, so we do them byte-by-byte in the
+// generic case.
+// So far these macros have not been fully tested
+//
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST16( p ) ( ((UINT16)((PBYTE)p)[0]) << 8 | ((PBYTE)p)[1] )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST16( p ) ( ((UINT16)((PBYTE)p)[1]) << 8 | ((PBYTE)p)[0] )
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST32( p ) ( (UINT32)SYMCRYPT_INTERNAL_LOAD_MSBFIRST16(&((PBYTE)p)[0]) << 16 | SYMCRYPT_INTERNAL_LOAD_MSBFIRST16(&((PBYTE)p)[2]) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST32( p ) ( (UINT32)SYMCRYPT_INTERNAL_LOAD_LSBFIRST16(&((PBYTE)p)[2]) << 16 | SYMCRYPT_INTERNAL_LOAD_LSBFIRST16(&((PBYTE)p)[0]) )
+#define SYMCRYPT_INTERNAL_LOAD_MSBFIRST64( p ) ( (UINT64)SYMCRYPT_INTERNAL_LOAD_MSBFIRST32(&((PBYTE)p)[0]) << 32 | SYMCRYPT_INTERNAL_LOAD_MSBFIRST32(&((PBYTE)p)[4]) )
+#define SYMCRYPT_INTERNAL_LOAD_LSBFIRST64( p ) ( (UINT64)SYMCRYPT_INTERNAL_LOAD_LSBFIRST32(&((PBYTE)p)[4]) << 32 | SYMCRYPT_INTERNAL_LOAD_LSBFIRST32(&((PBYTE)p)[0]) )
+
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST16( p, x ) { \
+    ((PBYTE)p)[0] = (BYTE)((x)>> 8);\
+    ((PBYTE)p)[1] = (BYTE)((x)    );\
+    }
+
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST16( p, x ) { \
+    ((PBYTE)p)[0] = (BYTE)((x)    );\
+    ((PBYTE)p)[1] = (BYTE)((x)>> 8);\
+    }
+
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST32( p, x ) { \
+    ((PBYTE)p)[0] = (BYTE)((x)>>24);\
+    ((PBYTE)p)[1] = (BYTE)((x)>>16);\
+    ((PBYTE)p)[2] = (BYTE)((x)>> 8);\
+    ((PBYTE)p)[3] = (BYTE)((x)    );\
+    }
+
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST32( p, x ) { \
+    ((PBYTE)p)[0] = (BYTE)((x)    );\
+    ((PBYTE)p)[1] = (BYTE)((x)>> 8);\
+    ((PBYTE)p)[2] = (BYTE)((x)>>16);\
+    ((PBYTE)p)[3] = (BYTE)((x)>>24);\
+    }
+
+#define SYMCRYPT_INTERNAL_STORE_MSBFIRST64( p, x ) { \
+    SYMCRYPT_INTERNAL_STORE_MSBFIRST32( &((PBYTE)p)[0],(UINT32)(((UINT64)(x))>>32) );\
+    SYMCRYPT_INTERNAL_STORE_MSBFIRST32( &((PBYTE)p)[4],(UINT32)(x));\
+    }
+
+#define SYMCRYPT_INTERNAL_STORE_LSBFIRST64( p, x ) { \
+    SYMCRYPT_INTERNAL_STORE_LSBFIRST32( &((PBYTE)p)[0], (UINT32)((x)    ) );\
+    SYMCRYPT_INTERNAL_STORE_LSBFIRST32( &((PBYTE)p)[4], (UINT32)(((UINT64)(x))>>32) );\
+    }
+
+#endif // platform switch for load/store macros
+
+
+//==============================================================================================
+//  INTERNAL DATA STRUCTURES
+//==============================================================================================
+//
+// Note: we do not use the symbolic names like SYMCRYPT_SHA1_INPUT_BLOCK_SIZE as this
+// file is included before that name is defined. Fixing that would make the public API header
+// file harder to read by moving the constant away from the associated functions, or forcing
+// the header file to use the struct name rather than the typedef. The current solution
+// works quite well.
+//
+
+//-----------------------------------------------------------------
+//     Block cipher description table
+// Below are the typedefs for the block cipher description table type
+// Callers can use this to define their own block cipher and use the block cipher
+// modes.
+//
+
+typedef struct _SYMCRYPT_BLOCKCIPHER    SYMCRYPT_BLOCKCIPHER, *PSYMCRYPT_BLOCKCIPHER;
+typedef const SYMCRYPT_BLOCKCIPHER  * PCSYMCRYPT_BLOCKCIPHER;
+
+//
+// Note that blockSize must be <= 32 and must be a power of two. This is true for all the block ciphers
+// implemented in SymCrypt.
+//
+
+//
+// HASH STATES
+//
+// All hash states have the same basic structure. This allows all hash implementations to share
+// the same buffer management code. Some algorithms might still have optimized buffer management code
+// specific for their algorithm, but most algs use the generic code.
+// This is especially important for parallel hashing, where the buffer management & parallel organizational
+// code are tightly coupled.
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_COMMON_HASH_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[SYMCRYPT_ANYSIZE_ARRAY];  // Size depends on algorithm
+    // ...
+    // Chaining state                                       // type/location depends on algorithm
+    //
+} SYMCRYPT_COMMON_HASH_STATE, *PSYMCRYPT_COMMON_HASH_STATE;
+
+
+//
+// SYMCRYPT_MD2_STATE
+//
+// Data structure that stores the state of an ongoing MD2 computation.
+//
+// The field names are from RFC 1319.
+// It would be more efficient to store only the first 16 bytes of the X array,
+// but that would complicate the code and MD2 isn't important enough to add
+// extra complications.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MD2_CHAINING_STATE
+{
+    SYMCRYPT_ALIGN  BYTE    C[16];      // State for internal checksum computation
+                    BYTE    X[48];      // State for actual hash chaining
+} SYMCRYPT_MD2_CHAINING_STATE, *PSYMCRYPT_MD2_CHAINING_STATE;
+
+//
+// MD2 hash computation state.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MD2_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[16];             // buffer to keep one input block in
+                    SYMCRYPT_MD2_CHAINING_STATE     chain;
+} SYMCRYPT_MD2_STATE, *PSYMCRYPT_MD2_STATE;
+typedef const SYMCRYPT_MD2_STATE *PCSYMCRYPT_MD2_STATE;
+
+//
+// SYMCRYPT_MD4_STATE
+//
+// Data structure that stores the state of an ongoing MD4 computation.
+// The buffer contains dataLength % 64 bytes of data.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MD4_CHAINING_STATE
+{
+    UINT32   H[4];
+} SYMCRYPT_MD4_CHAINING_STATE, *PSYMCRYPT_MD4_CHAINING_STATE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MD4_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[64];             // buffer to keep one input block in
+                    SYMCRYPT_MD4_CHAINING_STATE     chain;      // chaining state
+} SYMCRYPT_MD4_STATE, *PSYMCRYPT_MD4_STATE;
+typedef const SYMCRYPT_MD4_STATE *PCSYMCRYPT_MD4_STATE;
+
+
+//
+// SYMCRYPT_MD5_STATE
+//
+// Data structure that stores the state of an ongoing MD5 computation.
+// The buffer contains dataLength % 64 bytes of data.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MD5_CHAINING_STATE
+{
+    UINT32   H[4];
+} SYMCRYPT_MD5_CHAINING_STATE, *PSYMCRYPT_MD5_CHAINING_STATE;
+
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MD5_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[64];             // buffer to keep one input block in
+                    SYMCRYPT_MD5_CHAINING_STATE     chain;      // chaining state
+} SYMCRYPT_MD5_STATE, *PSYMCRYPT_MD5_STATE;
+typedef const SYMCRYPT_MD5_STATE *PCSYMCRYPT_MD5_STATE;
+
+
+//
+// SYMCRYPT_SHA1_STATE
+//
+// Data structure that stores the state of an ongoing SHA1 computation.
+// The buffer contains dataLength % 64 bytes of data.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA1_CHAINING_STATE
+{
+    UINT32   H[5];
+} SYMCRYPT_SHA1_CHAINING_STATE, *PSYMCRYPT_SHA1_CHAINING_STATE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA1_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[64];             // buffer to keep one input block in
+                    SYMCRYPT_SHA1_CHAINING_STATE    chain;      // chaining state
+} SYMCRYPT_SHA1_STATE, *PSYMCRYPT_SHA1_STATE;
+typedef const SYMCRYPT_SHA1_STATE *PCSYMCRYPT_SHA1_STATE;
+
+
+//
+// SYMCRYPT_SHA256_STATE
+//
+// Data structure that stores the state of an ongoing SHA256 computation.
+// The buffer contains dataLength % 64 bytes of data.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA256_CHAINING_STATE
+{
+    SYMCRYPT_ALIGN  UINT32   H[8];
+} SYMCRYPT_SHA256_CHAINING_STATE, * PSYMCRYPT_SHA256_CHAINING_STATE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA256_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[64];             // buffer to keep one input block in
+                    SYMCRYPT_SHA256_CHAINING_STATE  chain;      // chaining state
+} SYMCRYPT_SHA256_STATE, *PSYMCRYPT_SHA256_STATE;
+typedef const SYMCRYPT_SHA256_STATE *PCSYMCRYPT_SHA256_STATE;
+
+
+//
+// SYMCRYPT_SHA224_STATE
+//
+// This is identical to the SHA256 state.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA224_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[64];             // buffer to keep one input block in
+                    SYMCRYPT_SHA256_CHAINING_STATE  chain;      // chaining state
+} SYMCRYPT_SHA224_STATE, *PSYMCRYPT_SHA224_STATE;
+typedef const SYMCRYPT_SHA224_STATE *PCSYMCRYPT_SHA224_STATE;
+
+
+//
+// SYMCRYPT_SHA512_STATE
+//
+// Data structure that stores the state of an ongoing SHA512 computation.
+// The buffer contains dataLength % 128 bytes of data.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA512_CHAINING_STATE
+{
+    UINT64   H[8];
+} SYMCRYPT_SHA512_CHAINING_STATE, *PSYMCRYPT_SHA512_CHAINING_STATE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA512_STATE
+{
+                    UINT32                          bytesInBuffer;
+                    SYMCRYPT_MAGIC_FIELD
+                    UINT64                          dataLengthL;            // lower part of msg length
+                    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE                            buffer[128];            // buffer to keep one input block in
+                    SYMCRYPT_SHA512_CHAINING_STATE  chain;          // chaining state
+} SYMCRYPT_SHA512_STATE, *PSYMCRYPT_SHA512_STATE;
+typedef const SYMCRYPT_SHA512_STATE *PCSYMCRYPT_SHA512_STATE;
+
+
+//
+// SYMCRYPT_SHA384_STATE
+//
+// This is identical to the SHA512.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA384_STATE
+{
+    UINT32                          bytesInBuffer;
+    SYMCRYPT_MAGIC_FIELD
+    UINT64                          dataLengthL;            // lower part of msg length
+    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE            buffer[128];            // buffer to keep one input block in
+    SYMCRYPT_SHA512_CHAINING_STATE  chain;          // chaining state
+} SYMCRYPT_SHA384_STATE, *PSYMCRYPT_SHA384_STATE;
+typedef const SYMCRYPT_SHA384_STATE *PCSYMCRYPT_SHA384_STATE;
+
+
+//
+// SYMCRYPT_SHA512_224_STATE
+//
+// This is identical to the SHA512.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA512_224_STATE
+{
+    UINT32                          bytesInBuffer;
+    SYMCRYPT_MAGIC_FIELD
+    UINT64                          dataLengthL;            // lower part of msg length
+    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE            buffer[128];            // buffer to keep one input block in
+    SYMCRYPT_SHA512_CHAINING_STATE  chain;          // chaining state
+} SYMCRYPT_SHA512_224_STATE, *PSYMCRYPT_SHA512_224_STATE;
+typedef const SYMCRYPT_SHA512_224_STATE *PCSYMCRYPT_SHA512_224_STATE;
+
+
+//
+// SYMCRYPT_SHA512_256_STATE
+//
+// This is identical to the SHA512.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA512_256_STATE
+{
+    UINT32                          bytesInBuffer;
+    SYMCRYPT_MAGIC_FIELD
+    UINT64                          dataLengthL;            // lower part of msg length
+    UINT64                          dataLengthH;            // upper part of msg length
+    SYMCRYPT_ALIGN  BYTE            buffer[128];            // buffer to keep one input block in
+    SYMCRYPT_SHA512_CHAINING_STATE  chain;          // chaining state
+} SYMCRYPT_SHA512_256_STATE, *PSYMCRYPT_SHA512_256_STATE;
+typedef const SYMCRYPT_SHA512_256_STATE *PCSYMCRYPT_SHA512_256_STATE;
+
+
+//
+// SYMCRYPT_KECCAK_STATE
+//
+// Data structure that stores the state of an ongoing SHA-3 derived algorithm computation.
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_KECCAK_STATE
+{
+    SYMCRYPT_ALIGN  UINT64  state[25];      // state for Keccak-f[1600] permutation
+    UINT32                  inputBlockSize; // rate
+    UINT32                  stateIndex;     // position in the state for next merge/extract operation
+    UINT8                   paddingValue;   // Keccak padding value
+    BOOLEAN                 squeezeMode;    // denotes whether the state is in squeeze mode
+} SYMCRYPT_KECCAK_STATE, *PSYMCRYPT_KECCAK_STATE;
+typedef const SYMCRYPT_KECCAK_STATE *PCSYMCRYPT_KECCAK_STATE;
+
+//
+// SYMCRYPT_SHA3_224_STATE
+//
+// Data structure that stores the state of an ongoing SHA3-224 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA3_224_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_SHA3_224_STATE, * PSYMCRYPT_SHA3_224_STATE;
+typedef const SYMCRYPT_SHA3_224_STATE* PCSYMCRYPT_SHA3_224_STATE;
+
+//
+// SYMCRYPT_SHA3_256_STATE
+//
+// Data structure that stores the state of an ongoing SHA3-256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA3_256_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_SHA3_256_STATE, * PSYMCRYPT_SHA3_256_STATE;
+typedef const SYMCRYPT_SHA3_256_STATE* PCSYMCRYPT_SHA3_256_STATE;
+
+//
+// SYMCRYPT_SHA3_384_STATE
+//
+// Data structure that stores the state of an ongoing SHA3-384 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA3_384_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_SHA3_384_STATE, * PSYMCRYPT_SHA3_384_STATE;
+typedef const SYMCRYPT_SHA3_384_STATE* PCSYMCRYPT_SHA3_384_STATE;
+
+//
+// SYMCRYPT_SHA3_512_STATE
+//
+// Data structure that stores the state of an ongoing SHA3-512 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHA3_512_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_SHA3_512_STATE, * PSYMCRYPT_SHA3_512_STATE;
+typedef const SYMCRYPT_SHA3_512_STATE* PCSYMCRYPT_SHA3_512_STATE;
+
+//
+// SYMCRYPT_SHAKE128_STATE
+//
+// Data structure that stores the state of an ongoing SHAKE128 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHAKE128_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_SHAKE128_STATE, * PSYMCRYPT_SHAKE128_STATE;
+typedef const SYMCRYPT_SHAKE128_STATE* PCSYMCRYPT_SHAKE128_STATE;
+
+//
+// SYMCRYPT_SHAKE256_STATE
+//
+// Data structure that stores the state of an ongoing SHAKE256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_SHAKE256_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_SHAKE256_STATE, * PSYMCRYPT_SHAKE256_STATE;
+typedef const SYMCRYPT_SHAKE256_STATE* PCSYMCRYPT_SHAKE256_STATE;
+
+//
+// SYMCRYPT_CSHAKE128_STATE
+//
+// Data structure that stores the state of an ongoing CSHAKE128 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_CSHAKE128_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_CSHAKE128_STATE, * PSYMCRYPT_CSHAKE128_STATE;
+typedef const SYMCRYPT_CSHAKE128_STATE* PCSYMCRYPT_CSHAKE128_STATE;
+
+//
+// SYMCRYPT_CSHAKE256_STATE
+//
+// Data structure that stores the state of an ongoing CSHAKE256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_CSHAKE256_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_CSHAKE256_STATE, * PSYMCRYPT_CSHAKE256_STATE;
+typedef const SYMCRYPT_CSHAKE256_STATE* PCSYMCRYPT_CSHAKE256_STATE;
+
+//
+// SYMCRYPT_KMAC128_EXPANDED_KEY
+//
+// Data structure that stores the expanded key for KMAC128.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_KMAC128_EXPANDED_KEY
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_KMAC128_EXPANDED_KEY, * PSYMCRYPT_KMAC128_EXPANDED_KEY;
+typedef const SYMCRYPT_KMAC128_EXPANDED_KEY* PCSYMCRYPT_KMAC128_EXPANDED_KEY;
+
+//
+// SYMCRYPT_KMAC128_STATE
+//
+// Data structure that stores the state of an ongoing KMAC128 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_KMAC128_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_KMAC128_STATE, * PSYMCRYPT_KMAC128_STATE;
+typedef const SYMCRYPT_KMAC128_STATE* PCSYMCRYPT_KMAC128_STATE;
+
+//
+// SYMCRYPT_KMAC256_EXPANDED_KEY
+//
+// Data structure that stores the expanded key for KMAC256.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_KMAC256_EXPANDED_KEY
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_KMAC256_EXPANDED_KEY, * PSYMCRYPT_KMAC256_EXPANDED_KEY;
+typedef const SYMCRYPT_KMAC256_EXPANDED_KEY* PCSYMCRYPT_KMAC256_EXPANDED_KEY;
+
+//
+// SYMCRYPT_KMAC256_STATE
+//
+// Data structure that stores the state of an ongoing KMAC256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_KMAC256_STATE
+{
+    SYMCRYPT_KECCAK_STATE   ks;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_KMAC256_STATE, * PSYMCRYPT_KMAC256_STATE;
+typedef const SYMCRYPT_KMAC256_STATE* PCSYMCRYPT_KMAC256_STATE;
+
+
+//
+// Generic hashing
+//
+
+typedef struct _SYMCRYPT_OID {
+    UINT32  cbOID;
+    _Field_size_( cbOID ) PCBYTE  pbOID;
+} SYMCRYPT_OID, *PSYMCRYPT_OID;
+typedef const SYMCRYPT_OID *PCSYMCRYPT_OID;
+
+//
+// OID lists for the most commonly used hash functions
+//
+
+#define SYMCRYPT_MD5_OID_COUNT         (2)
+extern const SYMCRYPT_OID SymCryptMd5OidList[SYMCRYPT_MD5_OID_COUNT];
+
+#define SYMCRYPT_SHA1_OID_COUNT        (2)
+extern const SYMCRYPT_OID SymCryptSha1OidList[SYMCRYPT_SHA1_OID_COUNT];
+
+#define SYMCRYPT_SHA224_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha224OidList[SYMCRYPT_SHA224_OID_COUNT];
+
+#define SYMCRYPT_SHA256_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha256OidList[SYMCRYPT_SHA256_OID_COUNT];
+
+#define SYMCRYPT_SHA384_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha384OidList[SYMCRYPT_SHA384_OID_COUNT];
+
+#define SYMCRYPT_SHA512_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha512OidList[SYMCRYPT_SHA512_OID_COUNT];
+
+#define SYMCRYPT_SHA512_224_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha512_224OidList[SYMCRYPT_SHA512_224_OID_COUNT];
+
+#define SYMCRYPT_SHA512_256_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha512_256OidList[SYMCRYPT_SHA512_256_OID_COUNT];
+
+#define SYMCRYPT_SHA3_224_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha3_224OidList[SYMCRYPT_SHA3_224_OID_COUNT];
+
+#define SYMCRYPT_SHA3_256_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha3_256OidList[SYMCRYPT_SHA3_256_OID_COUNT];
+
+#define SYMCRYPT_SHA3_384_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha3_384OidList[SYMCRYPT_SHA3_384_OID_COUNT];
+
+#define SYMCRYPT_SHA3_512_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptSha3_512OidList[SYMCRYPT_SHA3_512_OID_COUNT];
+
+#define SYMCRYPT_SHAKE128_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptShake128OidList[SYMCRYPT_SHAKE128_OID_COUNT];
+
+#define SYMCRYPT_SHAKE256_OID_COUNT      (2)
+extern const SYMCRYPT_OID SymCryptShake256OidList[SYMCRYPT_SHAKE256_OID_COUNT];
+
+typedef enum _SYMCRYPT_OID_LIST_ID
+{
+    SYMCRYPT_OID_LIST_ID_NULL           = 0,
+    SYMCRYPT_OID_LIST_ID_MD5            = 1,
+    SYMCRYPT_OID_LIST_ID_SHA1           = 2,
+    SYMCRYPT_OID_LIST_ID_SHA224         = 3,
+    SYMCRYPT_OID_LIST_ID_SHA256         = 4,
+    SYMCRYPT_OID_LIST_ID_SHA384         = 5,
+    SYMCRYPT_OID_LIST_ID_SHA512         = 6,
+    SYMCRYPT_OID_LIST_ID_SHA512_224     = 7,
+    SYMCRYPT_OID_LIST_ID_SHA512_256     = 8,
+    SYMCRYPT_OID_LIST_ID_SHA3_224       = 9,
+    SYMCRYPT_OID_LIST_ID_SHA3_256       = 10,
+    SYMCRYPT_OID_LIST_ID_SHA3_384       = 11,
+    SYMCRYPT_OID_LIST_ID_SHA3_512       = 12,
+    SYMCRYPT_OID_LIST_ID_SHAKE128       = 13,
+    SYMCRYPT_OID_LIST_ID_SHAKE256       = 14
+} SYMCRYPT_OID_LIST_ID;
+
+PCSYMCRYPT_OID
+SYMCRYPT_CALL
+SymCryptGetOidList( SYMCRYPT_OID_LIST_ID oidId, _Out_opt_ SIZE_T* pCount );
+//
+// Returns a pointer to the OID list for the specified OID list ID. If pCount is non-NULL, the
+// pointed-to value will be set to the number of elements in the OID list.
+// Returns NULL if the OID list ID is invalid.
+//
+
+typedef union _SYMCRYPT_HASH_STATE
+{
+    SYMCRYPT_MD2_STATE          md2State;
+    SYMCRYPT_MD4_STATE          md4State;
+    SYMCRYPT_MD5_STATE          md5State;
+    SYMCRYPT_SHA1_STATE         sha1State;
+    SYMCRYPT_SHA224_STATE       sha224State;
+    SYMCRYPT_SHA256_STATE       sha256State;
+    SYMCRYPT_SHA384_STATE       sha384State;
+    SYMCRYPT_SHA512_STATE       sha512State;
+    SYMCRYPT_SHA512_224_STATE   sha512_224State;
+    SYMCRYPT_SHA512_256_STATE   sha512_256State;
+    SYMCRYPT_SHA3_224_STATE     sha3_224State;
+    SYMCRYPT_SHA3_256_STATE     sha3_256State;
+    SYMCRYPT_SHA3_384_STATE     sha3_384State;
+    SYMCRYPT_SHA3_512_STATE     sha3_512State;
+} SYMCRYPT_HASH_STATE, *PSYMCRYPT_HASH_STATE;
+typedef const SYMCRYPT_HASH_STATE *PCSYMCRYPT_HASH_STATE;
+
+#define SYMCRYPT_HASH_MAX_RESULT_SIZE    SYMCRYPT_SHA512_RESULT_SIZE
+
+SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HASH;
+SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_PARALLEL_HASH;
+
+typedef struct _SYMCRYPT_HASH SYMCRYPT_HASH, *PSYMCRYPT_HASH;
+typedef const SYMCRYPT_HASH  *PCSYMCRYPT_HASH;
+typedef struct _SYMCRYPT_PARALLEL_HASH SYMCRYPT_PARALLEL_HASH, *PSYMCRYPT_PARALLEL_HASH;
+typedef const SYMCRYPT_PARALLEL_HASH  *PCSYMCRYPT_PARALLEL_HASH;
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_HASH_INIT_FUNC)             ( PVOID pState );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_HASH_APPEND_FUNC)           ( PVOID pState, PCBYTE pbData, SIZE_T cbData );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_HASH_RESULT_FUNC)           ( PVOID pState, PVOID pbResult );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_HASH_APPEND_BLOCKS_FUNC)    ( PVOID pChain, PCBYTE pbData, SIZE_T cbData, SIZE_T * pcbRemaining );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_HASH_STATE_COPY_FUNC)       ( PCVOID pStateSrc, PVOID pStateDst );
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HASH
+{
+    PSYMCRYPT_HASH_INIT_FUNC            initFunc;
+    PSYMCRYPT_HASH_APPEND_FUNC          appendFunc;
+    PSYMCRYPT_HASH_RESULT_FUNC          resultFunc;
+    PSYMCRYPT_HASH_APPEND_BLOCKS_FUNC   appendBlockFunc;
+    PSYMCRYPT_HASH_STATE_COPY_FUNC      stateCopyFunc;
+    UINT32                              stateSize;          // sizeof( hash state )
+    UINT32                              resultSize;         // size of hash result
+    UINT32                              inputBlockSize;
+    UINT32                              chainOffset;        // offset into state structure of the chaining state
+    UINT32                              chainSize;          // size of chaining state
+} SYMCRYPT_HASH, *PSYMCRYPT_HASH;
+
+
+//
+// Parallel hashing
+//
+
+#if SYMCRYPT_CPU_ARM
+#define SYMCRYPT_PARALLEL_SHA256_MIN_PARALLELISM    (3)
+#define SYMCRYPT_PARALLEL_SHA256_MAX_PARALLELISM    (4)
+#else
+#define SYMCRYPT_PARALLEL_SHA256_MIN_PARALLELISM    (2)
+#define SYMCRYPT_PARALLEL_SHA256_MAX_PARALLELISM    (8)
+#endif
+
+typedef enum _SYMCRYPT_HASH_OPERATION_TYPE {
+    SYMCRYPT_HASH_OPERATION_APPEND = 1,
+    SYMCRYPT_HASH_OPERATION_RESULT = 2,
+} SYMCRYPT_HASH_OPERATION_TYPE;
+
+typedef struct _SYMCRYPT_PARALLEL_HASH_OPERATION    SYMCRYPT_PARALLEL_HASH_OPERATION, *PSYMCRYPT_PARALLEL_HASH_OPERATION;
+typedef const SYMCRYPT_PARALLEL_HASH_OPERATION *PCSYMRYPT_PARALLEL_HASH_OPERATION;
+
+struct _SYMCRYPT_PARALLEL_HASH_OPERATION {
+                                SIZE_T                              iHash;          // index of hash object into the state array
+                                SYMCRYPT_HASH_OPERATION_TYPE        hashOperation;  // operation to be performed
+    _Field_size_( cbBuffer )    PBYTE                               pbBuffer;       // data to be hashed, or result buffer
+                                SIZE_T                              cbBuffer;       // size of pbData buffer.
+                                PSYMCRYPT_PARALLEL_HASH_OPERATION   next;           // internal scratch space; do not use.
+};
+
+
+SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_PARALLEL_HASH_SCRATCH_OPERATION; // as yet unspecified struct
+typedef struct _SYMCRYPT_PARALLEL_HASH_SCRATCH_OPERATION
+        SYMCRYPT_PARALLEL_HASH_SCRATCH_OPERATION, *PSYMCRYPT_PARALLEL_HASH_SCRATCH_OPERATION;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_PARALLEL_HASH_SCRATCH_STATE {
+    PVOID                               hashState;          // the actual hash state
+    BYTE                                processingState;
+    BYTE                                bytesAlreadyProcessed;  // of the next Append operation
+    UINT64                              bytes;              // # bytes left to process on this state
+    PSYMCRYPT_PARALLEL_HASH_OPERATION   next;               // next operation to be performed.
+    PCBYTE                              pbData;             // data/size of ongoing append operation; this op has already been removed from the next linked list
+    SIZE_T                              cbData;
+}SYMCRYPT_PARALLEL_HASH_SCRATCH_STATE, *PSYMCRYPT_PARALLEL_HASH_SCRATCH_STATE;
+
+
+//
+// The scratch space used by parallel SHA-256 consists of three regions:
+// - an array of SYMCRYPT_PARALLEL_HASH_SCRATCH_STATE structures, aligned to SYMCRYPT_ALIGN_VALUE.
+// - the work array, an array of pointers to SYMCRYPT_PARALLEL_HASH_SCRATCH_STATEs.
+// - an array of 4 + 8 + 64 SIMD vector elements, aligned to the size of those elements.
+//
+//
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+#define SYMCRYPT_SIMD_ELEMENT_SIZE  32
+#elif SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_SIMD_ELEMENT_SIZE  16
+#elif SYMCRYPT_CPU_UNKNOWN
+#define SYMCRYPT_SIMD_ELEMENT_SIZE  0
+#else
+#error Unknown CPU
+#endif
+
+#define SYMCRYPT_PARALLEL_SHA256_FIXED_SCRATCH  ( (4 + 8 + 64) * SYMCRYPT_SIMD_ELEMENT_SIZE + SYMCRYPT_SIMD_ELEMENT_SIZE - 1  + SYMCRYPT_ALIGN_VALUE - 1 )
+#define SYMCRYPT_PARALLEL_SHA384_FIXED_SCRATCH  ( (4 + 8 + 80) * SYMCRYPT_SIMD_ELEMENT_SIZE + SYMCRYPT_SIMD_ELEMENT_SIZE - 1  + SYMCRYPT_ALIGN_VALUE - 1 )
+#define SYMCRYPT_PARALLEL_SHA512_FIXED_SCRATCH  ( (4 + 8 + 80) * SYMCRYPT_SIMD_ELEMENT_SIZE + SYMCRYPT_SIMD_ELEMENT_SIZE - 1  + SYMCRYPT_ALIGN_VALUE - 1 )
+#define SYMCRYPT_PARALLEL_HASH_PER_STATE_SCRATCH  (sizeof( SYMCRYPT_PARALLEL_HASH_SCRATCH_STATE ) + sizeof( PSYMCRYPT_PARALLEL_HASH_SCRATCH_STATE ) )
+
+SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_PARALLEL_HASH;
+typedef struct _SYMCRYPT_PARALLEL_HASH SYMCRYPT_PARALLEL_HASH, *PSYMCRYPT_PARALLEL_HASH;
+typedef const SYMCRYPT_PARALLEL_HASH  *PCSYMCRYPT_PARALLEL_HASH;
+
+typedef BOOLEAN (SYMCRYPT_CALL * PSYMCRYPT_PARALLEL_HASH_RESULT_FUNC) (PCSYMCRYPT_PARALLEL_HASH pParHash, PSYMCRYPT_COMMON_HASH_STATE pState, PSYMCRYPT_PARALLEL_HASH_SCRATCH_STATE pScratch, BOOLEAN *pRes );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_PARALLEL_HASH_RESULT_DONE_FUNC ) (PCSYMCRYPT_PARALLEL_HASH pParHash, PSYMCRYPT_COMMON_HASH_STATE pState, PCSYMRYPT_PARALLEL_HASH_OPERATION pOp);
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_PARALLEL_APPEND_FUNC) (
+    _Inout_updates_( nPar )                 PSYMCRYPT_PARALLEL_HASH_SCRATCH_STATE * pWork,
+                                            SIZE_T                                  nPar,
+                                            SIZE_T                                  nBytes,
+    _Out_writes_( cbSimdScratch )           PBYTE                                   pbSimdScratch,
+                                            SIZE_T                                  cbSimdScratch );
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_PARALLEL_HASH
+{
+    PCSYMCRYPT_HASH                             pHash;
+    UINT32                                      parScratchFixed;    // fixed scratch size for parallel hash
+    PSYMCRYPT_PARALLEL_HASH_RESULT_FUNC         parResult1Func;
+    PSYMCRYPT_PARALLEL_HASH_RESULT_FUNC         parResult2Func;
+    PSYMCRYPT_PARALLEL_HASH_RESULT_DONE_FUNC    parResultDoneFunc;
+
+    PSYMCRYPT_PARALLEL_APPEND_FUNC              parAppendFunc;
+} SYMCRYPT_PARALLEL_HASH, *PSYMCRYPT_PARALLEL_HASH;
+
+
+//======================================================================================================
+// MAC
+//
+
+
+//
+// SYMCRYPT_HMAC_MD5_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-MD5.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_MD5_EXPANDED_KEY
+{
+    SYMCRYPT_MD5_CHAINING_STATE     innerState;
+    SYMCRYPT_MD5_CHAINING_STATE     outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_MD5_EXPANDED_KEY, *PSYMCRYPT_HMAC_MD5_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_MD5_EXPANDED_KEY * PCSYMCRYPT_HMAC_MD5_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_MD5_STATE
+//
+// Data structure that encodes an ongoing HMAC-MD5 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_MD5_STATE
+{
+    SYMCRYPT_MD5_STATE                 hash;
+    PCSYMCRYPT_HMAC_MD5_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_MD5_STATE, *PSYMCRYPT_HMAC_MD5_STATE;
+typedef const SYMCRYPT_HMAC_MD5_STATE *PCSYMCRYPT_HMAC_MD5_STATE;
+
+
+//
+// SYMCRYPT_HMAC_SHA1_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA1.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA1_EXPANDED_KEY
+{
+    SYMCRYPT_SHA1_CHAINING_STATE    innerState;
+    SYMCRYPT_SHA1_CHAINING_STATE    outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA1_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA1_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA1_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA1_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA1_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA1 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA1_STATE
+{
+    SYMCRYPT_SHA1_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA1_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA1_STATE, *PSYMCRYPT_HMAC_SHA1_STATE;
+typedef const SYMCRYPT_HMAC_SHA1_STATE *PCSYMCRYPT_HMAC_SHA1_STATE;
+
+
+//
+// SYMCRYPT_HMAC_SHA224_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA224.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA224_EXPANDED_KEY
+{
+    SYMCRYPT_SHA256_CHAINING_STATE  innerState;
+    SYMCRYPT_SHA256_CHAINING_STATE  outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA224_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA224_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA224_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA224_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA224_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA224 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA224_STATE
+{
+    SYMCRYPT_SHA224_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA224_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA224_STATE, *PSYMCRYPT_HMAC_SHA224_STATE;
+typedef const SYMCRYPT_HMAC_SHA224_STATE *PCSYMCRYPT_HMAC_SHA224_STATE;
+
+
+//
+// SYMCRYPT_HMAC_SHA256_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA256.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA256_EXPANDED_KEY
+{
+    SYMCRYPT_SHA256_CHAINING_STATE  innerState;
+    SYMCRYPT_SHA256_CHAINING_STATE  outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA256_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA256_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA256_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA256_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA256_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA256_STATE
+{
+    SYMCRYPT_SHA256_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA256_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA256_STATE, *PSYMCRYPT_HMAC_SHA256_STATE;
+typedef const SYMCRYPT_HMAC_SHA256_STATE *PCSYMCRYPT_HMAC_SHA256_STATE;
+
+
+//
+// SYMCRYPT_HMAC_SHA384_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA384.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA384_EXPANDED_KEY
+{
+    SYMCRYPT_SHA512_CHAINING_STATE  innerState;
+    SYMCRYPT_SHA512_CHAINING_STATE  outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA384_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA384_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA384_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA384_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA384_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA384 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA384_STATE
+{
+    SYMCRYPT_SHA384_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA384_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA384_STATE, *PSYMCRYPT_HMAC_SHA384_STATE;
+typedef const SYMCRYPT_HMAC_SHA384_STATE *PCSYMCRYPT_HMAC_SHA384_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA512_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA512.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA512_EXPANDED_KEY
+{
+    SYMCRYPT_SHA512_CHAINING_STATE  innerState;
+    SYMCRYPT_SHA512_CHAINING_STATE  outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA512_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA512_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA512_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA512_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA512_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA512 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA512_STATE
+{
+    SYMCRYPT_SHA512_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA512_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA512_STATE, *PSYMCRYPT_HMAC_SHA512_STATE;
+typedef const SYMCRYPT_HMAC_SHA512_STATE *PCSYMCRYPT_HMAC_SHA512_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA512_224.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY
+{
+    SYMCRYPT_SHA512_CHAINING_STATE  innerState;
+    SYMCRYPT_SHA512_CHAINING_STATE  outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA512_224_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA512_224 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA512_224_STATE
+{
+    SYMCRYPT_SHA512_224_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA512_224_STATE, *PSYMCRYPT_HMAC_SHA512_224_STATE;
+typedef const SYMCRYPT_HMAC_SHA512_224_STATE *PCSYMCRYPT_HMAC_SHA512_224_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA512_256.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY
+{
+    SYMCRYPT_SHA512_CHAINING_STATE  innerState;
+    SYMCRYPT_SHA512_CHAINING_STATE  outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA512_256_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA512_256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA512_256_STATE
+{
+    SYMCRYPT_SHA512_256_STATE                 hash;
+    PCSYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY   pKey;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_SHA512_256_STATE, *PSYMCRYPT_HMAC_SHA512_256_STATE;
+typedef const SYMCRYPT_HMAC_SHA512_256_STATE *PCSYMCRYPT_HMAC_SHA512_256_STATE;
+
+//
+// SYMCRYPT_HMAC_EXPANDED_KEY
+//
+// Generic HMAC Expanded Key data structure
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_EXPANDED_KEY
+{
+    PCSYMCRYPT_HASH     pHash;
+    SYMCRYPT_HASH_STATE innerState;
+    SYMCRYPT_HASH_STATE outerState;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_EXPANDED_KEY, * PSYMCRYPT_HMAC_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_EXPANDED_KEY* PCSYMCRYPT_HMAC_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_STATE
+//
+// Generic HMAC data structure
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_STATE
+{
+    PCSYMCRYPT_HMAC_EXPANDED_KEY    pKey;
+    SYMCRYPT_HASH_STATE             hash;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_HMAC_STATE, * PSYMCRYPT_HMAC_STATE;
+typedef const SYMCRYPT_HMAC_STATE* PCSYMCRYPT_HMAC_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA3-224
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY
+{
+    SYMCRYPT_HMAC_EXPANDED_KEY  generic;
+
+} SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA3_224_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA3-224 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_224_STATE
+{
+    SYMCRYPT_HMAC_STATE generic;
+
+} SYMCRYPT_HMAC_SHA3_224_STATE, *PSYMCRYPT_HMAC_SHA3_224_STATE;
+typedef const SYMCRYPT_HMAC_SHA3_224_STATE *PCSYMCRYPT_HMAC_SHA3_224_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA3-256
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY
+{
+    SYMCRYPT_HMAC_EXPANDED_KEY  generic;
+
+} SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA3_256_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA3-256 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_256_STATE
+{
+    SYMCRYPT_HMAC_STATE generic;
+
+} SYMCRYPT_HMAC_SHA3_256_STATE, *PSYMCRYPT_HMAC_SHA3_256_STATE;
+typedef const SYMCRYPT_HMAC_SHA3_256_STATE *PCSYMCRYPT_HMAC_SHA3_256_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA3-384
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY
+{
+    SYMCRYPT_HMAC_EXPANDED_KEY  generic;
+
+} SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA3_384_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA3-384 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_384_STATE
+{
+    SYMCRYPT_HMAC_STATE generic;
+
+} SYMCRYPT_HMAC_SHA3_384_STATE, *PSYMCRYPT_HMAC_SHA3_384_STATE;
+typedef const SYMCRYPT_HMAC_SHA3_384_STATE *PCSYMCRYPT_HMAC_SHA3_384_STATE;
+
+//
+// SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY
+//
+// Data structure to store an expanded key for HMAC-SHA3-512
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY
+{
+    SYMCRYPT_HMAC_EXPANDED_KEY  generic;
+
+} SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY, *PSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY;
+typedef const SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY * PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY;
+
+//
+// SYMCRYPT_HMAC_SHA3_512_STATE
+//
+// Data structure that encodes an ongoing HMAC-SHA3-512 computation.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_HMAC_SHA3_512_STATE
+{
+    SYMCRYPT_HMAC_STATE generic;
+
+} SYMCRYPT_HMAC_SHA3_512_STATE, *PSYMCRYPT_HMAC_SHA3_512_STATE;
+typedef const SYMCRYPT_HMAC_SHA3_512_STATE *PCSYMCRYPT_HMAC_SHA3_512_STATE;
+
+//
+// SYMCRYPT_AES_EXPANDED_KEY
+//
+// Expanded key for AES operations.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_AES_EXPANDED_KEY {
+    SYMCRYPT_ALIGN BYTE RoundKey[29][4][4];
+        // Round keys, first the encryption round keys in encryption order,
+        // followed by the decryption round keys in decryption order.
+        // The first decryption round key is the last encryption round key.
+        // AES-256 has 14 rounds and thus 15 round keys for encryption and 15
+        // for decryption. As they share one round key, we need room for 29.
+    BYTE   (*lastEncRoundKey)[4][4];    // Pointer to last encryption round key
+                                        // also the first round key for decryption
+    BYTE   (*lastDecRoundKey)[4][4];    // Pointer to last decryption round key.
+
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_AES_EXPANDED_KEY, *PSYMCRYPT_AES_EXPANDED_KEY;
+typedef const SYMCRYPT_AES_EXPANDED_KEY * PCSYMCRYPT_AES_EXPANDED_KEY;
+
+//
+// AES-CMAC
+//
+// Note: SYMCRYPT_AES_BLOCK_SIZE is not yet defined, so we use
+// literal constants instead.
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_AES_CMAC_EXPANDED_KEY
+{
+    SYMCRYPT_AES_EXPANDED_KEY   aesKey;
+    BYTE                        K1[16];
+    BYTE                        K2[16];
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_AES_CMAC_EXPANDED_KEY, *PSYMCRYPT_AES_CMAC_EXPANDED_KEY;
+typedef const SYMCRYPT_AES_CMAC_EXPANDED_KEY * PCSYMCRYPT_AES_CMAC_EXPANDED_KEY;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_AES_CMAC_STATE
+{
+    BYTE                                chain[16];
+    BYTE                                buf[16];
+    SIZE_T                              bytesInBuf;
+    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY    pKey;
+
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_AES_CMAC_STATE, *PSYMCRYPT_AES_CMAC_STATE;
+typedef const SYMCRYPT_AES_CMAC_STATE * PCSYMCRYPT_AES_CMAC_STATE;
+
+//
+// POLY1305
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_POLY1305_STATE
+{
+    UINT32  r[4];       // R := \sum 2^{32*i} r[i]. R is already clamped.
+    UINT32  s[4];       // S := \sum 2^{32*i} s[i]
+    UINT32  a[5];       // Accumulator := sum 2^{32*i} a[i], a[4] <= approx 8
+    SIZE_T  bytesInBuffer;
+    BYTE    buf[16];    // Partial block buffer
+
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_POLY1305_STATE, *PSYMCRYPT_POLY1305_STATE;
+
+//
+// XTS-AES
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_XTS_AES_EXPANDED_KEY
+{
+    SYMCRYPT_AES_EXPANDED_KEY   key1;
+    SYMCRYPT_AES_EXPANDED_KEY   key2;
+} SYMCRYPT_XTS_AES_EXPANDED_KEY, *PSYMCRYPT_XTS_AES_EXPANDED_KEY;
+typedef const SYMCRYPT_XTS_AES_EXPANDED_KEY * PCSYMCRYPT_XTS_AES_EXPANDED_KEY;
+
+
+//-----------------------------------------------------------------
+//     Mac description table
+// Below are the typedefs for the Mac description table type
+// Callers can use this to define Mac algorithm they want to use
+//
+
+#define SYMCRYPT_MAC_MAX_RESULT_SIZE    SYMCRYPT_HMAC_SHA512_RESULT_SIZE
+
+typedef union _SYMCRYPT_MAC_STATE
+{
+    SYMCRYPT_HMAC_MD5_STATE         md5State;
+    SYMCRYPT_HMAC_SHA1_STATE        sha1State;
+    SYMCRYPT_HMAC_SHA224_STATE      sha224State;
+    SYMCRYPT_HMAC_SHA256_STATE      sha256State;
+    SYMCRYPT_HMAC_SHA384_STATE      sha384State;
+    SYMCRYPT_HMAC_SHA512_STATE      sha512State;
+    SYMCRYPT_HMAC_SHA512_224_STATE  sha512_224State;
+    SYMCRYPT_HMAC_SHA512_256_STATE  sha512_256State;
+    SYMCRYPT_HMAC_SHA3_224_STATE    sha3_224State;
+    SYMCRYPT_HMAC_SHA3_256_STATE    sha3_256State;
+    SYMCRYPT_HMAC_SHA3_384_STATE    sha3_384State;
+    SYMCRYPT_HMAC_SHA3_512_STATE    sha3_512State;
+    SYMCRYPT_AES_CMAC_STATE         aescmacState;
+    SYMCRYPT_KMAC128_STATE          kmac128State;
+    SYMCRYPT_KMAC256_STATE          kmac256State;
+} SYMCRYPT_MAC_STATE, *PSYMCRYPT_MAC_STATE;
+typedef const SYMCRYPT_MAC_STATE *PCSYMCRYPT_MAC_STATE;
+
+typedef union _SYMCRYPT_MAC_EXPANDED_KEY
+{
+    SYMCRYPT_HMAC_MD5_EXPANDED_KEY          md5Key;
+    SYMCRYPT_HMAC_SHA1_EXPANDED_KEY         sha1Key;
+    SYMCRYPT_HMAC_SHA224_EXPANDED_KEY       sha224Key;
+    SYMCRYPT_HMAC_SHA256_EXPANDED_KEY       sha256Key;
+    SYMCRYPT_HMAC_SHA384_EXPANDED_KEY       sha384Key;
+    SYMCRYPT_HMAC_SHA512_EXPANDED_KEY       sha512Key;
+    SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY   sha512_224Key;
+    SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY   sha512_256Key;
+    SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY     sha3_224Key;
+    SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY     sha3_256Key;
+    SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY     sha3_384Key;
+    SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY     sha3_512Key;
+    SYMCRYPT_AES_CMAC_EXPANDED_KEY          aescmacKey;
+    SYMCRYPT_KMAC128_EXPANDED_KEY           kmac128Key;
+    SYMCRYPT_KMAC256_EXPANDED_KEY           kmac256Key;
+} SYMCRYPT_MAC_EXPANDED_KEY, *PSYMCRYPT_MAC_EXPANDED_KEY;
+typedef const SYMCRYPT_MAC_EXPANDED_KEY *PCSYMCRYPT_MAC_EXPANDED_KEY;
+
+typedef SYMCRYPT_ERROR (SYMCRYPT_CALL * PSYMCRYPT_MAC_EXPAND_KEY)
+                                        ( PVOID pExpandedKey, PCBYTE pbKey, SIZE_T cbKey );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_MAC_INIT)     ( PVOID pState,  PCVOID pExpandedKey );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_MAC_APPEND)( PVOID pState, PCBYTE pbData, SIZE_T cbData );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_MAC_RESULT)  ( PVOID pState, PVOID pbResult );
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_MAC_RESULT_EX)  ( PVOID pState, PVOID pbResult, SIZE_T cbResult );
+
+typedef struct _SYMCRYPT_MAC
+{
+    PSYMCRYPT_MAC_EXPAND_KEY    expandKeyFunc;
+    PSYMCRYPT_MAC_INIT          initFunc;
+    PSYMCRYPT_MAC_APPEND        appendFunc;
+    PSYMCRYPT_MAC_RESULT        resultFunc;
+    SIZE_T                      expandedKeySize;
+    SIZE_T                      stateSize;
+    SIZE_T                      resultSize;
+    const PCSYMCRYPT_HASH     * ppHashAlgorithm;            // NULL for MACs not based on hashes
+    UINT32                      outerChainingStateOffset;   // Offset into expanded key of outer chaining state; 0 for non-HMAC algorithms
+} SYMCRYPT_MAC, *PSYMCRYPT_MAC;
+typedef const SYMCRYPT_MAC  *PCSYMCRYPT_MAC;
+
+
+
+//
+// 3DES
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_3DES_EXPANDED_KEY {
+    UINT32  roundKey[3][16][2];     // 3 keys, 16 rounds, 2 UINT32s/round
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_3DES_EXPANDED_KEY, *PSYMCRYPT_3DES_EXPANDED_KEY;
+typedef const SYMCRYPT_3DES_EXPANDED_KEY * PCSYMCRYPT_3DES_EXPANDED_KEY;
+
+//
+// DES
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_DES_EXPANDED_KEY {
+    SYMCRYPT_3DES_EXPANDED_KEY threeDes;
+} SYMCRYPT_DES_EXPANDED_KEY, *PSYMCRYPT_DES_EXPANDED_KEY;
+typedef const SYMCRYPT_DES_EXPANDED_KEY * PCSYMCRYPT_DES_EXPANDED_KEY;
+
+//
+// DESX
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_DESX_EXPANDED_KEY {
+    SYMCRYPT_DES_EXPANDED_KEY   desKey;
+    BYTE                        inputWhitening[8];
+    BYTE                        outputWhitening[8];
+} SYMCRYPT_DESX_EXPANDED_KEY, *PSYMCRYPT_DESX_EXPANDED_KEY;
+typedef const SYMCRYPT_DESX_EXPANDED_KEY * PCSYMCRYPT_DESX_EXPANDED_KEY;
+
+//
+// RC2
+//
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_RC2_EXPANDED_KEY {
+    UINT16  K[64];
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_RC2_EXPANDED_KEY, *PSYMCRYPT_RC2_EXPANDED_KEY;
+typedef const SYMCRYPT_RC2_EXPANDED_KEY * PCSYMCRYPT_RC2_EXPANDED_KEY;
+
+
+//
+// CCM states for incremental computations
+//
+#define SYMCRYPT_CCM_BLOCK_SIZE (16)
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_CCM_STATE {
+                                                    PCSYMCRYPT_BLOCKCIPHER  pBlockCipher;
+                                                    PCVOID                  pExpandedKey;
+                                                    UINT64                  cbData;                                     // exact length of data
+                                                    SIZE_T                  cbTag;
+                                                    SIZE_T                  cbNonce;
+                                                    SIZE_T                  cbCounter;                                  // # bytes in counter field
+                                                    UINT64                  bytesProcessed;                             // data bytes processed so far
+    _Field_range_( 0, SYMCRYPT_CCM_BLOCK_SIZE-1 )   SIZE_T                  bytesInMacBlock;
+                                                    SYMCRYPT_ALIGN BYTE     counterBlock[SYMCRYPT_CCM_BLOCK_SIZE];      // Current counter block value
+                                                    SYMCRYPT_ALIGN BYTE     macBlock[SYMCRYPT_CCM_BLOCK_SIZE];          // Current state of the CBC-MAC part of CCM
+                                                    SYMCRYPT_ALIGN BYTE     keystreamBlock[SYMCRYPT_CCM_BLOCK_SIZE];    // Remaining key stream if partial block has been processed
+                                                    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_CCM_STATE, *PSYMCRYPT_CCM_STATE;
+
+
+//
+// GHash & GCM
+//
+
+typedef union _SYMCRYPT_GCM_SUPPORTED_BLOCKCIPHER_KEYS
+{
+    SYMCRYPT_AES_EXPANDED_KEY aes;
+} SYMCRYPT_GCM_SUPPORTED_BLOCKCIPHER_KEYS;
+
+#define SYMCRYPT_GCM_BLOCKCIPHER_KEY_SIZE sizeof( union _SYMCRYPT_GCM_SUPPORTED_BLOCKCIPHER_KEYS )
+
+#define SYMCRYPT_GF128_FIELD_SIZE   (128)
+#define SYMCRYPT_GF128_BLOCK_SIZE   (16)        // # bytes in a field element/block
+#define SYMCRYPT_GCM_BLOCK_SIZE     (16)
+#define SYMCRYPT_GCM_MAX_KEY_SIZE   (32)
+
+
+#define SYMCRYPT_GCM_MAX_DATA_SIZE           (((UINT64)1 << 36) - 32)
+
+#define SYMCRYPT_GCM_BLOCK_MOD_MASK      (SYMCRYPT_GCM_BLOCK_SIZE - 1)
+#define SYMCRYPT_GCM_BLOCK_ROUND_MASK    (~SYMCRYPT_GCM_BLOCK_MOD_MASK)
+
+#if SYMCRYPT_CPU_X86
+    //
+    // x86 needs extra alignment of the GHASH expanded key to support
+    // aligned (fast) XMM access. AMD64 has enough natural alignment to
+    // achieve this.
+    //
+    #define SYMCRYPT_GHASH_EXTRA_KEY_ALIGNMENT
+#endif
+
+#define SYMCRYPT_GHASH_ALLOW_XMM    (SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64)
+#define SYMCRYPT_GHASH_ALLOW_NEON   (SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64)
+
+
+#if SYMCRYPT_CPU_ARM
+#include <arm_neon.h>
+#if SYMCRYPT_GNUC || defined(__clang__)
+    #define __n128 uint32x4_t
+    #define __n64 uint64x1_t
+#endif
+
+#elif SYMCRYPT_CPU_ARM64
+
+    #if SYMCRYPT_MS_VC && !defined(__clang__)
+        #include <arm64_neon.h>
+
+        // See section 6.7.8 of the C standard for details on this initializer usage.
+        #define SYMCRYPT_SET_N128_U64(d0, d1) \
+            ((__n128) {.n128_u64 = {d0, d1}})
+        #define SYMCRYPT_SET_N64_U64(d0) \
+            ((__n64) {.n64_u64 = {d0}})
+        #define SYMCRYPT_SET_N128_U8(b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15) \
+            ((__n128) {.n128_u8 = {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}})
+    #else
+        #include <arm_neon.h>
+
+        #define __n128 uint8x16_t
+        #define __n64 uint8x8_t
+
+        #define SYMCRYPT_SET_N128_U64(d0, d1) \
+            ((__n128) ((uint64x2_t) {d0, d1}))
+        #define SYMCRYPT_SET_N64_U64(d0) \
+            ((__n64) ((uint64x1_t) {d0}))
+        #define SYMCRYPT_SET_N128_U8(b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15) \
+            ((__n128) ((uint8x16_t) {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}))
+
+        #define vmullq_p64( a, b )      ((__n128) vmull_p64(vgetq_lane_p64((poly64x2_t)a, 0), vgetq_lane_p64((poly64x2_t)b, 0)))
+        #define vmull_p64( a, b )       ((__n128) vmull_p64( (poly64_t)a, (poly64_t)b ))
+        #define vmull_high_p64( a, b )  ((__n128) vmull_high_p64( (poly64x2_t)a, (poly64x2_t)b ))
+    #endif
+
+#endif
+
+//
+// All platforms use the same in-memory representation:
+// elements of GF(2^128) stored as two 64-bit integers which are best
+// interpreted as a single 128-bit integer, least significant half first.
+// Note: the actual GF(2^128) bit order is reversed in the standard
+// for some reason; the
+// polynomial \sum b_i x^i is represented by integer \sum b_i 2^{127-i})
+// On x86/amd64 the same in-memory byte structure is also accessed as an
+// __m128i, which works as both the UINT64s, UINT32s, and the __m128i use
+// LSBfirst convention.
+//
+typedef SYMCRYPT_ALIGN_UNION _SYMCRYPT_GF128_ELEMENT {
+    UINT64 ull[2];
+#if SYMCRYPT_GHASH_ALLOW_XMM
+    //
+    // The XMM code accesses this both as UINT32[] and __m128i
+    // This is safe as XMM code only runs on little endian machines so the
+    // ordering is known.
+    //
+    __m128i     m128i;
+    UINT32      ul[4];
+#endif
+#if SYMCRYPT_GHASH_ALLOW_NEON
+    __n128      n128;
+    UINT32      ul[4];
+#endif
+} SYMCRYPT_GF128_ELEMENT, *PSYMCRYPT_GF128_ELEMENT;
+typedef const SYMCRYPT_GF128_ELEMENT * PCSYMCRYPT_GF128_ELEMENT;
+
+
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_GHASH_EXPANDED_KEY {
+#if defined( SYMCRYPT_GHASH_EXTRA_KEY_ALIGNMENT )
+    UINT32  tableOffset;
+    BYTE    tableSpace[ (SYMCRYPT_GF128_FIELD_SIZE + 1) * sizeof( SYMCRYPT_GF128_ELEMENT ) ];
+#else
+    SYMCRYPT_GF128_ELEMENT  table[ SYMCRYPT_GF128_FIELD_SIZE ];
+#endif
+} SYMCRYPT_GHASH_EXPANDED_KEY, *PSYMCRYPT_GHASH_EXPANDED_KEY;
+typedef const SYMCRYPT_GHASH_EXPANDED_KEY * PCSYMCRYPT_GHASH_EXPANDED_KEY;
+
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_GCM_EXPANDED_KEY {
+    SYMCRYPT_GHASH_EXPANDED_KEY             ghashKey;
+    PCSYMCRYPT_BLOCKCIPHER                  pBlockCipher;
+    SYMCRYPT_GCM_SUPPORTED_BLOCKCIPHER_KEYS blockcipherKey;
+    SIZE_T                                  cbKey;
+    BYTE                                    abKey[SYMCRYPT_GCM_MAX_KEY_SIZE];
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_GCM_EXPANDED_KEY, * PSYMCRYPT_GCM_EXPANDED_KEY;
+typedef const SYMCRYPT_GCM_EXPANDED_KEY * PCSYMCRYPT_GCM_EXPANDED_KEY;
+
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_GCM_STATE {
+                                                    PCSYMCRYPT_GCM_EXPANDED_KEY pKey;
+                                                    UINT64                      cbData;         // Number of data bytes
+                                                    UINT64                      cbAuthData;     // Number of AAD bytes
+    _Field_range_( 0, SYMCRYPT_GCM_BLOCK_SIZE-1 )   SIZE_T                      bytesInMacBlock;
+                                                    SYMCRYPT_GF128_ELEMENT      ghashState;
+                                                    SYMCRYPT_ALIGN BYTE         counterBlock[SYMCRYPT_GCM_BLOCK_SIZE];
+                                                    SYMCRYPT_ALIGN BYTE         macBlock[SYMCRYPT_GCM_BLOCK_SIZE];
+                                                    SYMCRYPT_ALIGN BYTE         keystreamBlock[SYMCRYPT_GCM_BLOCK_SIZE];
+                                                    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_GCM_STATE, * PSYMCRYPT_GCM_STATE;
+typedef const SYMCRYPT_GCM_STATE * PCSYMCRYPT_GCM_STATE;
+
+
+//
+// Block ciphers
+//
+#define SYMCRYPT_MAX_BLOCK_SIZE  (32)        // max block length of a block cipher.
+
+typedef SYMCRYPT_ERROR( SYMCRYPT_CALL * PSYMCRYPT_BLOCKCIPHER_EXPAND_KEY )
+(PVOID pExpandedKey, PCBYTE pbKey, SIZE_T cbKey);
+typedef VOID( SYMCRYPT_CALL * PSYMCRYPT_BLOCKCIPHER_CRYPT )         (PCVOID pExpandedKey, PCBYTE pbSrc, PBYTE pbDst);
+typedef VOID( SYMCRYPT_CALL * PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB )     (PCVOID pExpandedKey, PCBYTE pbSrc, PBYTE pbDst, SIZE_T cbData);
+typedef VOID( SYMCRYPT_CALL * PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE )    (PCVOID pExpandedKey, PBYTE pbChainingValue, PCBYTE pbSrc, PBYTE pbDst, SIZE_T cbData);
+typedef VOID( SYMCRYPT_CALL * PSYMCRYPT_BLOCKCIPHER_MAC_MODE )      (PCVOID pExpandedKey, PBYTE pbChainingValue, PCBYTE pbSrc, SIZE_T cbData);
+typedef VOID( SYMCRYPT_CALL * PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE ) (PVOID pState, PCBYTE pbSrc, PBYTE pbDst, SIZE_T cbData);
+
+struct _SYMCRYPT_BLOCKCIPHER {
+                                                PSYMCRYPT_BLOCKCIPHER_EXPAND_KEY    expandKeyFunc;      // mandatory
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT         encryptFunc;        // mandatory
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT         decryptFunc;        // mandatory
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbEncryptFunc;     // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbDecryptFunc;     // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcEncryptFunc;     // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcDecryptFunc;     // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_MAC_MODE      cbcMacFunc;         // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    ctrMsb64Func;       // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmEncryptPartFunc; // NULL if no optimized version available
+                                                PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmDecryptPartFunc; // NULL if no optimized version available
+    _Field_range_( 1, SYMCRYPT_MAX_BLOCK_SIZE ) SIZE_T                              blockSize;          // = SYMCRYPT_XXX_BLOCK_SIZE, power of 2, 1 <= value <= 32.
+                                                SIZE_T                              expandedKeySize;    // = sizeof( SYMCRYPT_XXX_EXPANDED_KEY )
+};
+
+
+
+//
+// Session structs
+//
+
+#define SYMCRYPT_FLAG_SESSION_ENCRYPT       (0x1)
+
+//
+// SYMCRYPT_SESSION tracks the Nonces being used in a session. It is used differently depending on
+// whether the session is an Encryption session or a Decryption session.
+//
+// In Encryption sessions, SYMCRYPT_SESSION tracks the Nonce which was used in the most recent
+// attempted encryption in the session.
+// messageNumber is atomically incremented by each encryption call, and the encryption method uses
+// the messageNumber value that is the _result_ of the increment.
+//
+// In Decryption sessions, SYMCRYPT_SESSION tracks the most recently received Nonces in a series of
+// successful decryptions. Nonces used in unsuccessful decryption calls do not update SYMCRYPT_SESSION.
+// Information is tracked such that the decryption function can detect repeated Nonce values and
+// fail decryption in this case. In order for this to work the message numbers that are provided
+// to decrypt calls must be somewhat ordered. Provided message numbers may be arbitrarily far ahead
+// of previously successfully decrypted message numbers, but may only be up to 63 behind the highest
+// message number successfully decrypted so far.
+// messageNumber normally represents the highest message number used in a successful decryption in
+// this session. (The exception is at initialization, where messageNumber is initialized to 64
+// without the corresponding 0th bit in the replayMask being set - this initial state represents
+// there have been no successful decryptions yet, and that the earliest messageNumber that can be
+// successfully received is 1)
+// replayMask represents whether a window of 64 message numbers up to messageNumber have already been
+// successfully used;
+// bit n of replayMask (from n=0 to n=63) represents message number = (messageNumber-n), 0 means not
+// yet used, and 1 means already used in a successful decryption call
+//
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_USE_CAS128 (1)
+
+// For CompareAndSwap128 method, SYMCRYPT_SESSION must be aligned to 16B
+#define SYMCRYPT_ALIGN_SESSION SYMCRYPT_ALIGN_TYPE_AT(struct, 16)
+#else
+#define SYMCRYPT_USE_CAS128 (0)
+
+// For method with only 64-bit atomics, SYMCRYPT_SESSION must be aligned to 8B
+#define SYMCRYPT_ALIGN_SESSION SYMCRYPT_ALIGN_TYPE_AT(struct, 8)
+#endif
+
+// Nested struct used within SYMCRYPT_SESSION
+typedef SYMCRYPT_ALIGN_SESSION _SYMCRYPT_SESSION_REPLAY_STATE {
+    UINT64  replayMask;
+    // 64 bit mask representing message numbers previously successfully decrypted up to 63
+    // before the most recent message number.
+
+    UINT64  messageNumber;
+    // the last 8 bytes of the Nonce (MSB-first)
+} SYMCRYPT_SESSION_REPLAY_STATE, * PSYMCRYPT_SESSION_REPLAY_STATE;
+typedef const SYMCRYPT_SESSION_REPLAY_STATE * PCSYMCRYPT_SESSION_REPLAY_STATE;
+
+typedef SYMCRYPT_ALIGN_SESSION _SYMCRYPT_SESSION {
+    SYMCRYPT_SESSION_REPLAY_STATE replayState;
+    // nested replayState struct is to improve code clarity in SymCryptSessionDecryptUpdate*
+
+    UINT32  senderId;
+    // the first 4 bytes of the Nonce (MSB-first)
+    // (set by the caller and constant for the lifetime of a session)
+
+    UINT32  flags;
+    // SYMCRYPT_FLAG_SESSION_ENCRYPT indicates the struct is to be used for an encryption session,
+    // otherwise the struct is to be used for a decryption session
+
+    PVOID   pMutex;
+    // Pointer to a fast single-process mutex object used to enable atomic update of replayMask and
+    // messageNumber in the absence of support for a 128b CAS operation
+} SYMCRYPT_SESSION, * PSYMCRYPT_SESSION;
+
+#define SYMCRYPT_SESSION_MAX_MESSAGE_NUMBER (0xffffffff00000000ull)
+// We do not allow messageNumber to go above some maximum value (currently 2^64 - 2^32)
+// This gives us a large window to prevent many concurrent encryption threads from updating the
+// session such that the messageNumber overflows and the same IV is used in many encryptions
+// (i.e. we would only potentially get a spurious success using a repeated IV when there are
+// >2^32 concurrent threads!)
+
+#if SYMCRYPT_USE_CAS128
+C_ASSERT(SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SESSION, replayState.replayMask) == 0);
+C_ASSERT(SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SESSION, replayState.messageNumber) == 8);
+// For CompareAndSwap128 method, replayMask and messageNumber must be tightly packed
+#endif
+
+//
+// RC4
+//
+
+//
+// Some CPUs like the S array type to be larger than BYTE. We abstract the data type
+// of the S array to accommodate such CPUs in future.
+//
+
+typedef BYTE    SYMCRYPT_RC4_S_TYPE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_RC4_STATE {
+    SYMCRYPT_RC4_S_TYPE  S[256];
+    BYTE i;
+    BYTE j;
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_RC4_STATE, *PSYMCRYPT_RC4_STATE;
+
+//
+// ChaCha20
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_CHACHA20_STATE {
+    UINT32      key[8];
+    UINT32      nonce[3];
+    UINT64      offset;                 // offset to use for next operation
+    BOOLEAN     keystreamBufferValid;   // keystream buffer matches offset value
+    BYTE        keystream[64];
+} SYMCRYPT_CHACHA20_STATE, *PSYMCRYPT_CHACHA20_STATE;
+
+
+//
+// AES_CTR_DRBG
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_RNG_AES_STATE {
+    //
+    // Key and V value are in one array, to allow fast generation of both of them
+    // in a single call.
+    //
+    BYTE        keyAndV[32 + 16];
+    BYTE        previousBlock[16];
+    UINT64      requestCounter;         // called reseed_counter in SP 800-90
+    BOOLEAN     fips140_2Check;         // set if the FIPS 140-2 continuous self-test is required
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_RNG_AES_STATE, * PSYMCRYPT_RNG_AES_STATE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_RNG_AES_FIPS140_2_STATE {
+    SYMCRYPT_RNG_AES_STATE  rng;
+} SYMCRYPT_RNG_AES_FIPS140_2_STATE, *PSYMCRYPT_RNG_AES_FIPS140_2_STATE;
+
+
+//
+// MARVIN32
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MARVIN32_EXPANDED_SEED
+{
+    UINT32   s[2];
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_MARVIN32_EXPANDED_SEED, *PSYMCRYPT_MARVIN32_EXPANDED_SEED;
+typedef const SYMCRYPT_MARVIN32_EXPANDED_SEED * PCSYMCRYPT_MARVIN32_EXPANDED_SEED;
+
+
+typedef SYMCRYPT_MARVIN32_EXPANDED_SEED SYMCRYPT_MARVIN32_CHAINING_STATE, * PSYMCRYPT_MARVIN32_CHAINING_STATE;
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MARVIN32_STATE
+{
+    SYMCRYPT_ALIGN  BYTE                                buffer[8];  // 4 bytes of data, 4 more bytes for final padding
+                    SYMCRYPT_MARVIN32_CHAINING_STATE    chain;      // chaining state
+                    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pSeed;      //
+                    UINT32                              dataLength; // length of the data processed so far, mod 2^32
+                    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_MARVIN32_STATE, *PSYMCRYPT_MARVIN32_STATE;
+typedef const SYMCRYPT_MARVIN32_STATE *PCSYMCRYPT_MARVIN32_STATE;
+
+
+//
+// Export blob sizes
+//
+
+#define SYMCRYPT_MD2_STATE_EXPORT_SIZE          (80)
+#define SYMCRYPT_MD4_STATE_EXPORT_SIZE          (116)
+#define SYMCRYPT_MD5_STATE_EXPORT_SIZE          (116)
+#define SYMCRYPT_SHA1_STATE_EXPORT_SIZE         (120)
+#define SYMCRYPT_SHA224_STATE_EXPORT_SIZE       (132)
+#define SYMCRYPT_SHA256_STATE_EXPORT_SIZE       (132)
+#define SYMCRYPT_SHA384_STATE_EXPORT_SIZE       (236)
+#define SYMCRYPT_SHA512_STATE_EXPORT_SIZE       (236)
+#define SYMCRYPT_SHA512_224_STATE_EXPORT_SIZE   (236)
+#define SYMCRYPT_SHA512_256_STATE_EXPORT_SIZE   (236)
+
+#define SYMCRYPT_KECCAK_STATE_EXPORT_SIZE   (234)
+#define SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE SYMCRYPT_KECCAK_STATE_EXPORT_SIZE
+#define SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE SYMCRYPT_KECCAK_STATE_EXPORT_SIZE
+#define SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE SYMCRYPT_KECCAK_STATE_EXPORT_SIZE
+#define SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE SYMCRYPT_KECCAK_STATE_EXPORT_SIZE
+
+
+//
+// KDF algorithms
+//
+
+//
+// PBKDF2
+//
+
+typedef struct _SYMCRYPT_PBKDF2_EXPANDED_KEY {
+    SYMCRYPT_MAC_EXPANDED_KEY   macKey;
+    PCSYMCRYPT_MAC              macAlg;
+} SYMCRYPT_PBKDF2_EXPANDED_KEY, *PSYMCRYPT_PBKDF2_EXPANDED_KEY;
+typedef const SYMCRYPT_PBKDF2_EXPANDED_KEY *PCSYMCRYPT_PBKDF2_EXPANDED_KEY;
+
+//
+// SP 800-108
+//
+
+typedef struct _SYMCRYPT_SP800_108_EXPANDED_KEY {
+    SYMCRYPT_MAC_EXPANDED_KEY   macKey;
+    PCSYMCRYPT_MAC              macAlg;
+} SYMCRYPT_SP800_108_EXPANDED_KEY, *PSYMCRYPT_SP800_108_EXPANDED_KEY;
+typedef const SYMCRYPT_SP800_108_EXPANDED_KEY *PCSYMCRYPT_SP800_108_EXPANDED_KEY;
+
+//
+// TLS PRF 1.1
+//
+
+typedef struct _SYMCRYPT_TLSPRF1_1_EXPANDED_KEY {
+    SYMCRYPT_HMAC_MD5_EXPANDED_KEY   macMd5Key;
+    SYMCRYPT_HMAC_SHA1_EXPANDED_KEY   macSha1Key;
+} SYMCRYPT_TLSPRF1_1_EXPANDED_KEY, *PSYMCRYPT_TLSPRF1_1_EXPANDED_KEY;
+typedef const SYMCRYPT_TLSPRF1_1_EXPANDED_KEY *PCSYMCRYPT_TLSPRF1_1_EXPANDED_KEY;
+
+//
+// TLS PRF 1.2
+//
+
+typedef struct _SYMCRYPT_TLSPRF1_2_EXPANDED_KEY {
+    SYMCRYPT_MAC_EXPANDED_KEY   macKey;
+    PCSYMCRYPT_MAC              macAlg;
+} SYMCRYPT_TLSPRF1_2_EXPANDED_KEY, *PSYMCRYPT_TLSPRF1_2_EXPANDED_KEY;
+typedef const SYMCRYPT_TLSPRF1_2_EXPANDED_KEY *PCSYMCRYPT_TLSPRF1_2_EXPANDED_KEY;
+
+//
+// SSH-KDF
+//
+typedef struct _SYMCRYPT_SSHKDF_EXPANDED_KEY {
+    PCSYMCRYPT_HASH     pHashFunc;
+    SYMCRYPT_HASH_STATE hashState;
+} SYMCRYPT_SSHKDF_EXPANDED_KEY, *PSYMCRYPT_SSHKDF_EXPANDED_KEY;
+typedef const SYMCRYPT_SSHKDF_EXPANDED_KEY *PCSYMCRYPT_SSHKDF_EXPANDED_KEY;
+
+//
+// SRTP-KDF
+//
+typedef struct _SYMCRYPT_SRTPKDF_EXPANDED_KEY {
+    SYMCRYPT_AES_EXPANDED_KEY     aesExpandedKey;
+} SYMCRYPT_SRTPKDF_EXPANDED_KEY, *PSYMCRYPT_SRTPKDF_EXPANDED_KEY;
+typedef const SYMCRYPT_SRTPKDF_EXPANDED_KEY *PCSYMCRYPT_SRTPKDF_EXPANDED_KEY;
+
+//
+// HKDF
+//
+
+typedef struct _SYMCRYPT_HKDF_EXPANDED_KEY {
+    SYMCRYPT_MAC_EXPANDED_KEY   macKey;
+    PCSYMCRYPT_MAC              macAlg;
+} SYMCRYPT_HKDF_EXPANDED_KEY, *PSYMCRYPT_HKDF_EXPANDED_KEY;
+typedef const SYMCRYPT_HKDF_EXPANDED_KEY *PCSYMCRYPT_HKDF_EXPANDED_KEY;
+
+//
+// SSKDF
+//
+typedef struct _SYMCRYPT_SSKDF_MAC_EXPANDED_SALT {
+    SYMCRYPT_MAC_EXPANDED_KEY   macKey;
+    PCSYMCRYPT_MAC              macAlg;
+} SYMCRYPT_SSKDF_MAC_EXPANDED_SALT, *PSYMCRYPT_SSKDF_MAC_EXPANDED_SALT;
+typedef const SYMCRYPT_SSKDF_MAC_EXPANDED_SALT *PCSYMCRYPT_SSKDF_MAC_EXPANDED_SALT;
+
+//
+// Digit & alignment sizes.
+//
+// WARNING: do not change these without updating all the optimized code,
+// including assembler code.
+// The FDEF_DIGIT_SIZE is the digit size used by the FDEF format.
+//
+#if SYMCRYPT_CPU_AMD64
+
+#define SYMCRYPT_FDEF_DIGIT_SIZE    64
+#define SYMCRYPT_ASYM_ALIGN_VALUE   32
+
+#elif SYMCRYPT_CPU_ARM64
+
+#define SYMCRYPT_FDEF_DIGIT_SIZE    32
+#define SYMCRYPT_ASYM_ALIGN_VALUE   32
+
+#else
+
+#define SYMCRYPT_FDEF_DIGIT_SIZE    16
+#define SYMCRYPT_ASYM_ALIGN_VALUE   16              // We have some bugs when ASYM_ALIGN_VALUE > DIGIT_SIZE; need to fix them if we implement AVX2-based x86 code.
+
+#endif
+
+#define SYMCRYPT_ASYM_ALIGN_UP( _p ) ((PBYTE) ( ((SIZE_T) (_p) + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1 ) ) )
+
+
+//==============================================================================================
+// Object types for low-level API
+//
+// INT          integer in range 0..N for some N
+// DIVISOR      an integer > 0 that can be used to divide with.
+// MODULUS      a value M > 1 to use in modulo-M computations
+// MODELEMENT   An element in a modulo-M ring.
+// ECPOINT      A point on an elliptic curve.
+//
+// These objects are all aligned to SYMCRYPT_ASYM_ALIGN
+//
+#define SYMCRYPT_ASYM_ALIGN SYMCRYPT_ALIGN_AT(SYMCRYPT_ASYM_ALIGN_VALUE)
+#if SYMCRYPT_MS_VC
+#define SYMCRYPT_ASYM_ALIGN_STRUCT SYMCRYPT_ASYM_ALIGN struct
+#elif SYMCRYPT_GNUC
+#define SYMCRYPT_ASYM_ALIGN_STRUCT struct SYMCRYPT_ASYM_ALIGN
+#else
+#error Unknown compiler
+#endif
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_INT;
+typedef struct _SYMCRYPT_INT   SYMCRYPT_INT;
+typedef       SYMCRYPT_INT * PSYMCRYPT_INT;
+typedef const SYMCRYPT_INT * PCSYMCRYPT_INT;
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_DIVISOR;
+typedef struct _SYMCRYPT_DIVISOR   SYMCRYPT_DIVISOR;
+typedef       SYMCRYPT_DIVISOR * PSYMCRYPT_DIVISOR;
+typedef const SYMCRYPT_DIVISOR * PCSYMCRYPT_DIVISOR;
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MODULUS;
+typedef struct _SYMCRYPT_MODULUS   SYMCRYPT_MODULUS;
+typedef       SYMCRYPT_MODULUS * PSYMCRYPT_MODULUS;
+typedef const SYMCRYPT_MODULUS * PCSYMCRYPT_MODULUS;
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MODELEMENT;
+typedef  struct _SYMCRYPT_MODELEMENT   SYMCRYPT_MODELEMENT;
+typedef       SYMCRYPT_MODELEMENT * PSYMCRYPT_MODELEMENT;
+typedef const SYMCRYPT_MODELEMENT * PCSYMCRYPT_MODELEMENT;
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_ECPOINT;
+typedef struct _SYMCRYPT_ECPOINT   SYMCRYPT_ECPOINT;
+typedef       SYMCRYPT_ECPOINT * PSYMCRYPT_ECPOINT;
+typedef const SYMCRYPT_ECPOINT * PCSYMCRYPT_ECPOINT;
+
+
+//
+// Arithmetic formats
+//
+
+#define SYMCRYPT_ANYSIZE    1       // used to mark arrays of arbitrary size
+
+#define SYMCRYPT_FDEF_DIGIT_BITS    (8*SYMCRYPT_FDEF_DIGIT_SIZE)
+#define SYMCRYPT_FDEF_DIGITS_FROM_BITS( _bits )   ( \
+                                                   ((_bits)/ SYMCRYPT_FDEF_DIGIT_BITS) + \
+                                                   (( ((_bits) & (SYMCRYPT_FDEF_DIGIT_BITS-1)) + (SYMCRYPT_FDEF_DIGIT_BITS - 1) )/SYMCRYPT_FDEF_DIGIT_BITS) \
+                                                  )
+
+#define SYMCRYPT_BYTES_FROM_BITS(bits)          ( ( (bits) + 7 ) / 8 )
+
+// The maximum number of bits in any integer value that the library supports. If the
+// caller's input exceed this bound then the integer object will not be created.
+// The caller either must ensure the bound is not exceeded, or check for NULL before
+// using created SymCrypt objects.
+// The primary purpose of this limit is to avoid integer overflows in size computations.
+// Having a reasonable upper bound avoids all size overflows, even on 32-bit CPUs
+#define SYMCRYPT_INT_MAX_BITS       ((UINT32)(1 << 20))
+
+//
+// Upper bound for the number of digits: this MUST be enforced on runtime
+// on all Allocate, SizeOf, and Create calls which take as input a digit number.
+//
+// Using this upper bound and the SYMCRYPT_INT_MAX_BITS upper bound we can argue
+// that no integer overflow on 32-bit sizes can happen. Note that the computed upper
+// bounds are very loose and the actual values are much smaller.
+//
+#define SYMCRYPT_FDEF_UPB_DIGITS    (SYMCRYPT_FDEF_DIGITS_FROM_BITS(SYMCRYPT_INT_MAX_BITS))
+
+
+
+
+//
+// All of the following SYMCRYPT_FDEF_SIZEOF_XXX_FROM_YYY computations for the four
+// main SymCrypt objects (INT, DIVISOR, MODULUS, MODELEMENT) return a value not
+// larger than 2^19 if the inputs _nDigits and _bits are not larger than
+// SYMCRYPT_FDEF_UPB_DIGITS and SYMCRYPT_INT_MAX_BITS respectively (For MODELEMENT this bound
+// is 2^17). The latter bounds must be enforced on runtime for all calculations taking as inputs
+// number of digits or bits.
+//
+// The 2^19 upper bound is derived from:
+//      - the maximum (byte) size of an "integer": 2^20 bits / 8 = 2^17 bytes
+//      - "sizeof" computations add up to less than 2^18 bytes ~ 262 Kb
+//      - the modulus object contains two "integers"
+//
+
+//
+// Type fields contain the following:
+// lower 16 bits: offset into virtual table (if any)
+// upper 16 bits: bits 16-23: 1-character object type. Bits 24-31: 1 char implementation type
+// The upper bits allow objects to be recognized in memory, making debugging easier.
+//
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_INT {
+                                                    UINT32  type;
+    _Field_range_( 1, SYMCRYPT_FDEF_UPB_DIGITS )    UINT32  nDigits;    // digit size depends on run-time decisions...
+                                                    UINT32  cbSize;
+
+    SYMCRYPT_MAGIC_FIELD
+    SYMCRYPT_ASYM_ALIGN union {
+        struct {
+            UINT32          uint32[SYMCRYPT_ANYSIZE];   // FDEF: array UINT32[nDigits * # uint32 per digit]
+        } fdef;
+    } ti;                   // we must have a name here. 'ti' stands for 'Type-Int', it helps catch type errors when type-casting macros are used.
+};
+
+#define SYMCRYPT_FDEF_INT_PUINT32( p )  (&(p)->ti.fdef.uint32[0])
+
+
+#define SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( _nDigits )    ((_nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE + sizeof( SYMCRYPT_INT ) )
+#define SYMCRYPT_FDEF_SIZEOF_INT_FROM_BITS( _bits )         SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( SYMCRYPT_FDEF_DIGITS_FROM_BITS( _bits ))
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_DIVISOR {
+                                                    UINT32  type;
+    _Field_range_( 1, SYMCRYPT_FDEF_UPB_DIGITS )    UINT32  nDigits;    // digit size depends on run-time decisions...
+                                                    UINT32  cbSize;
+
+                                                    UINT32  nBits;      // # bits in divisor
+
+    SYMCRYPT_MAGIC_FIELD
+    union{
+        struct {
+            UINT64                  W;              // approximate inverse of the divisor. Some implementations will use 64 bits, others 32 bits.
+        } fdef;
+    } td;
+    SYMCRYPT_INT            Int;                    // Having a full Int here uses more space, but allows any Divisor to still be used as an Int.
+    // This structure is directly followed by the Int extension
+};
+
+#define SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_DIGITS( _nDigits ) ((_nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE + sizeof( SYMCRYPT_DIVISOR ) )
+#define SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_BITS( _bits ) SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_DIGITS( SYMCRYPT_FDEF_DIGITS_FROM_BITS( _bits ))
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MODULUS {
+                                                    UINT32  type;
+    _Field_range_( 1, SYMCRYPT_FDEF_UPB_DIGITS )    UINT32  nDigits;        // digit size depends on run-time decisions...
+                                                    UINT32  cbSize;         // Size of modulus object
+
+                                                    UINT32  flags;          // The flags the modulus was created with
+                                                    UINT32  cbModElement;   // Size of one modElement
+                                                    UINT64  inv64;          // -1/modulus mod 2^64 (always set but only to a useful value when the modulus is odd)
+
+    SYMCRYPT_MAGIC_FIELD
+    union{
+        struct {
+            //UINT32          nUint32Used;    // # 32-bit words used in representing numbers. modulus < 2^{32*nUint32Used}.
+                                            // only values used are nDigits * uint32-per-digit or specific smaller values for optimized implementations
+            PCUINT32        Rsqr;           // R^2 mod modulus, in uint32 form, nUint32Used words. Stored after Divisor. R = 2^{32*nUint32Used}
+        } montgomery;
+        struct {
+            UINT32          k;              // modulus = 2^<bitsize of modelement> - k
+        } pseudoMersenne;
+    } tm;                                   // type specific data. Every Modulus can be used as a generic modulus, so no type-specific data for generic.
+
+    SYMCRYPT_DIVISOR        Divisor;
+    // This structure is directly followed by:
+    //  The extensions of the Divisor object
+    // and after that:
+    // FDEF: Rsqr as an array of UINT32, size = nDigits * digitsize
+    // FDEF: negDivisor as an array of UINT32, size = nDigits * digitsize
+};
+
+#define SYMCRYPT_FDEF_SIZEOF_MODULUS_FROM_DIGITS( _nDigits )    (sizeof( SYMCRYPT_MODULUS ) + SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_DIGITS( _nDigits ) + (2 * _nDigits * SYMCRYPT_FDEF_DIGIT_SIZE) )
+#define SYMCRYPT_FDEF_SIZEOF_MODULUS_FROM_BITS( _bits )         SYMCRYPT_FDEF_SIZEOF_MODULUS_FROM_DIGITS(SYMCRYPT_FDEF_DIGITS_FROM_BITS( _bits ))
+
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MODELEMENT {
+    // ModElements just store the information without any header. This union makes this well-defined, and allows easy access.
+    union{
+        UINT32    uint32[SYMCRYPT_ANYSIZE];
+    } d;
+};
+
+#define SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( _nDigits )    ((_nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE)
+#define SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_BITS( _bits )          SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( SYMCRYPT_FDEF_DIGITS_FROM_BITS( _bits ) )
+
+//
+// Upper bound for scratch size computations for FDEF objects depending only on digits
+//
+// The following 14 scratch size computation macros are all of the form:
+//      Some SIZEOF macros + max( some other scratch macros )
+// and all depend on some number of digits. (Slight exceptions are
+// INT_TO_MODULUS and INT_PRIME_GEN but they can fit into the below
+// rationale.)
+//
+// One can see that the deepest recursion in these macros and the biggest
+// return value is for
+//      INT_PRIME_GEN -> INT_MILLER_RABIN -> MODEXP ->
+//      COMMON_MOD_OPERATIONS -> SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD
+//
+// Using the 2^19 (2^17) bound on the sizeof computations the biggest contribution on the above chain is for MODEXP:
+//      ((1 << SYMCRYPT_FDEF_MAX_WINDOW_MODEXP) + 2) * SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( _nModDigits )
+// which is bounded above by
+//      (2^6 + 2) * 2^17 < 2^24
+//
+// By doubling on each subsequent recursive call we get the conservative
+// upper bound for all scratch size computation macros of 2^26.
+//
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits )  (16 * (_nDigits))   // unused currently, but this catches errors
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MUL( _nDigits )         (16 * (_nDigits))   // unused currently, but nonzero size catches errors
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( _nSrcDigits, _nDivisorDigits )  ( (_nSrcDigits + 1) * SYMCRYPT_FDEF_DIGIT_SIZE )
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_EXTENDED_GCD( _nDigits ) ( \
+            4 * SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( _nDigits ) + \
+            SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( 2 * _nDigits ) + \
+            2 * SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_DIGITS( _nDigits ) + \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( 2 * _nDigits, _nDigits ), \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MUL( 2 * _nDigits ), \
+                 SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits ) )) )
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nModDigits ) \
+            ( (2*(_nModDigits) * SYMCRYPT_FDEF_DIGIT_SIZE) + \
+            SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( 2*(_nModDigits), _nModDigits )) // for mult: tmp product + divmod scratch
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_CRT_GENERATION( _nDigits ) ( \
+            2*SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( _nDigits ) + \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_EXTENDED_GCD( _nDigits ), \
+                 SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nDigits ) ))
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_CRT_SOLUTION( _nDigits ) ( \
+            SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( _nDigits ) + \
+            SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( _nDigits ) + \
+            SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( 2*_nDigits ) + \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nDigits ), \
+                 SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MUL( 2*_nDigits ) ))
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_MODULUS( _nDigits )  ( \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits ),\
+                (2*_nDigits+1) * SYMCRYPT_FDEF_DIGIT_SIZE + SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( 2*_nDigits + 1, nDigits )) )
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODINV( _nModDigits ) ( \
+            4 * SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( _nModDigits ) + \
+            3 * SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( _nModDigits ) + \
+            SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nModDigits ) )
+
+#define SYMCRYPT_FDEF_MAX_WINDOW_MODEXP         (6)
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODEXP( _nModDigits ) ( \
+            ((1 << SYMCRYPT_FDEF_MAX_WINDOW_MODEXP) + 2) * SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( _nModDigits ) + \
+            SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nModDigits ) )
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_IS_POTENTIAL_PRIME( _nDigits )  (0)
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MILLER_RABIN( _nDigits ) ( \
+            SYMCRYPT_FDEF_SIZEOF_MODULUS_FROM_DIGITS(_nDigits) + \
+            3*SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS(_nDigits) + \
+            SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS(_nDigits) + \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_MODULUS(_nDigits), \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(_nDigits), \
+                 SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODEXP( _nDigits ) )) )
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_IS_PRIME( _nDigits ) ( \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_IS_POTENTIAL_PRIME( _nDigits ), \
+                 SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MILLER_RABIN( _nDigits ) ))
+
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_PRIME_GEN( _nDigits ) ( \
+            SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS * SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_DIGITS( 1 ) + \
+            SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( 1 ) + \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( 1 ), \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( _nDigits, 1 ), \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SIZEOF_INT_FROM_DIGITS( _nDigits ), \
+            SYMCRYPT_MAX( SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_IS_POTENTIAL_PRIME( _nDigits ), \
+                 SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MILLER_RABIN( _nDigits ) )))))
+
+//
+// Upper bound for SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODMULTIEXP
+//
+// _nBase and _nBitsExp are bounded by SYMCRYPT_MODMULTIEXP_MAX_NBASES = 8 and
+// SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP = 2^20. Therefore the upper bound on this computation
+// is
+//      2^21 + 2^3*(2^6+4)*2^17 + 2^3*2^20*4 < 2^27
+//
+#define SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODMULTIEXP( _nModDigits, _nBases, _nBitsExp ) ( \
+    SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nModDigits ) + \
+    ((_nBases)*(1<<SYMCRYPT_FDEF_MAX_WINDOW_MODEXP) + 4)*SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS( _nModDigits ) + \
+    (((_nBases)*(_nBitsExp)*sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1)) )
+// Note: We need +4 multiplied with SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_DIGITS so that SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODMULTIEXP
+// is always at least 2 modelements bigger than SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODEXP (see modexp.c)
+
+//
+// Support for masked operations
+
+#define SYMCRYPT_MASK32_SET             ((UINT32)-1)
+#define SYMCRYPT_MASK32_NONZERO( _v )   ((UINT32)(((UINT64)0 - (_v)) >> 32))
+#define SYMCRYPT_MASK32_ZERO( _v )      (~SYMCRYPT_MASK32_NONZERO( _v ))
+#define SYMCRYPT_MASK32_EQ( _a, _b )    (~SYMCRYPT_MASK32_NONZERO( (_a) ^ (_b) ))
+#define SYMCRYPT_MASK32_LT( _a, _b )    ((UINT32)( ((UINT64)(_a) - (_b)) >> 32 ))
+
+
+//
+// Dispatch definitions
+// When multiple formats are supported, this is where the information of the multiple formats is combined.
+//
+//  See the comments in SYMCRYPT_FDEF_SCRATCH_XXX regarding 32 bit overflow protection. All results
+//  are bounded above by 2^27.
+//
+
+#define SYMCRYPT_INTERNAL_SIZEOF_INT_FROM_BITS( _bitsize )          SYMCRYPT_FDEF_SIZEOF_INT_FROM_BITS( _bitsize )
+#define SYMCRYPT_INTERNAL_SIZEOF_DIVISOR_FROM_BITS( _bitsize )      SYMCRYPT_FDEF_SIZEOF_DIVISOR_FROM_BITS( _bitsize )
+#define SYMCRYPT_INTERNAL_SIZEOF_MODULUS_FROM_BITS( _bitsize )      SYMCRYPT_FDEF_SIZEOF_MODULUS_FROM_BITS( _bitsize )
+#define SYMCRYPT_INTERNAL_SIZEOF_MODELEMENT_FROM_BITS( _bitsize )   SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_BITS( _bitsize )
+
+#define SYMCRYPT_INTERNAL_SIZEOF_RSAKEY_FROM_PARAMS( modBits, nPrimes, nPubExps ) SYMCRYPT_FDEF_SIZEOF_RSAKEY_FROM_PARAMS( modBits, nPrimes, nPubExps )
+// For now we don't need the pubExpBits so we drop them, but we might use them later.
+
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits )                      SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_MUL( _nDigits )                             SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MUL( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_DIVMOD( _nSrcDigits, _nDivisorDigits )      SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( _nSrcDigits, _nDivisorDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_EXTENDED_GCD( _nDigits )                        SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_EXTENDED_GCD( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nModDigits )            SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nModDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_CRT_GENERATION( _nDigits )                      SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_CRT_GENERATION( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_CRT_SOLUTION( _nDigits )                        SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_CRT_SOLUTION( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_TO_MODULUS( _nDigits )                      SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_MODULUS( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_MODINV( _nModDigits )                           SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODINV( _nModDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_MODEXP( _nModDigits )                           SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODEXP( _nModDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_IS_PRIME( _nDigits )                        SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_IS_PRIME( _nDigits )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_PRIME_GEN( _nDigits )                       SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_PRIME_GEN( _nDigits )
+
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_MODMULTIEXP( _nModDigits, _nBases, _nBitsExp )  SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_MODMULTIEXP( _nModDigits, _nBases, _nBitsExp )
+
+//
+// Forward declarations for MlKemkey types
+//
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEMKEY;
+typedef struct _SYMCRYPT_MLKEMKEY SYMCRYPT_MLKEMKEY;
+typedef       SYMCRYPT_MLKEMKEY * PSYMCRYPT_MLKEMKEY;
+typedef const SYMCRYPT_MLKEMKEY * PCSYMCRYPT_MLKEMKEY;
+
+//
+// Forward declarations for MlDsakey types
+//
+struct _SYMCRYPT_MLDSAKEY;
+typedef struct _SYMCRYPT_MLDSAKEY SYMCRYPT_MLDSAKEY;
+typedef       SYMCRYPT_MLDSAKEY * PSYMCRYPT_MLDSAKEY;
+typedef const SYMCRYPT_MLDSAKEY * PCSYMCRYPT_MLDSAKEY;
+
+//
+// Forward declarations for CompositeMlKemkey types
+//
+SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_COMPOSITE_MLKEMKEY;
+typedef struct _SYMCRYPT_COMPOSITE_MLKEMKEY SYMCRYPT_COMPOSITE_MLKEMKEY;
+typedef       SYMCRYPT_COMPOSITE_MLKEMKEY * PSYMCRYPT_COMPOSITE_MLKEMKEY;
+typedef const SYMCRYPT_COMPOSITE_MLKEMKEY * PCSYMCRYPT_COMPOSITE_MLKEMKEY;
+
+//
+// RSA padding scratch definitions
+//
+// The maximum sizes of the state and the result for all hash algorithms are
+// sizeof(SYMCRYPT_HASH_STATE) and SYMCRYPT_HASH_MAX_RESULT_SIZE, both not bigger
+// 2^20. All the nBytes inputs are bounded by 2^17 (the maximum byte-size
+// of the RSA modulus).
+//
+// Thus a total upper bound on these results is 2^20.
+//
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_RSA_OAEP( _hashAlgorithm, _nBytesOAEP ) ( SymCryptHashStateSize( _hashAlgorithm ) + \
+                                                                                      SymCryptHashResultSize( _hashAlgorithm ) + \
+                                                                                      2*(_nBytesOAEP - 1) )
+
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_RSA_PKCS1( _nBytesPKCS1 ) ( _nBytesPKCS1 )
+
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_RSA_PSS( _hashAlgorithm, _nBytesMessage, _nBytesPSS ) ( SymCryptHashStateSize( _hashAlgorithm ) + \
+                                                                                                    _nBytesMessage + \
+                                                                                                    3*(_nBytesPSS) + 5 )
+
+//
+// RSAKEY Type
+//
+
+#define SYMCRYPT_FDEF_SIZEOF_RSAKEY_FROM_PARAMS( modBits, nPrimes, nPubExps ) \
+    sizeof( SYMCRYPT_RSAKEY ) + \
+    (nPrimes + 1) * SYMCRYPT_FDEF_SIZEOF_MODULUS_FROM_BITS( modBits ) + \
+    nPrimes * SYMCRYPT_FDEF_SIZEOF_MODELEMENT_FROM_BITS( modBits ) + \
+    (nPrimes + 1) * nPubExps * SYMCRYPT_FDEF_SIZEOF_INT_FROM_BITS( modBits )
+// 1 modulus object per prime + 1 for the RSA modulus
+// 1 modelement for every crtInverse
+// 1 int per pubexp for each privexp +  1 int per prime*pubexp for each crtprivexp
+
+#define SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES            (2)
+#define SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS           (1)
+
+#define SYMCRYPT_RSAKEY_MIN_BITSIZE_MODULUS         (256)               // Some of our SCS code requires at least 32 bytes...
+#define SYMCRYPT_RSAKEY_MAX_BITSIZE_MODULUS         (1 << 16)           // Avoid any integer overflows in size calculations
+
+// RSA FIPS self-tests require at least 496 bits to avoid fatal
+// Require caller to specify NO_FIPS for up to 1024 bits as running FIPS tests on too-small keys
+// does not make it FIPS certifiable and gives the wrong impression to callers
+#define SYMCRYPT_RSAKEY_FIPS_MIN_BITSIZE_MODULUS    (1024)
+
+#define SYMCRYPT_RSAKEY_MIN_BITSIZE_PRIME           (128)
+#define SYMCRYPT_RSAKEY_MAX_BITSIZE_PRIME           (SYMCRYPT_RSAKEY_MAX_BITSIZE_MODULUS / 2)
+
+// Minimum allowable bit sizes for generated and imported parameters for
+// the RSA modulus and each prime.
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_RSAKEY {
+                    UINT32              fAlgorithmInfo;     // Tracks which algorithms the key can be used in
+                                                            // Also tracks which per-key selftests have been performed on this key
+                                                            // A bitwise OR of SYMCRYPT_FLAG_KEY_*, SYMCRYPT_FLAG_RSAKEY_*, and
+                                                            // SYMCRYPT_PCT_* values
+
+                    UINT32              cbTotalSize;        // Total size of the rsa key
+                    BOOLEAN             hasPrivateKey;      // Set to true if there is private key information set
+
+                    UINT32              nSetBitsOfModulus;  // Bits of modulus specified during creation
+
+                    UINT32              nBitsOfModulus;     // Number of bits of the value of the modulus (not the object's size)
+                    UINT32              nDigitsOfModulus;   // Number of digits of the modulus object (always equal to SymCryptDigitsFromBits(nSetBitsOfModulus))
+
+                    UINT32              nPubExp;            // Number of public exponents
+
+                    UINT32              nPrimes;            // Number of primes, can be 0 if the object only supports public keys
+                    UINT32              nBitsOfPrimes[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                                                            // Number of bits of the value of each prime (not the object's size)
+                    UINT32              nDigitsOfPrimes[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                                                            // Number of digits of each prime object
+                    UINT32              nMaxDigitsOfPrimes; // Maximum number of digits in nDigitsOfPrimes
+
+                    UINT64              au64PubExp[SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS];
+                    // SYMCRYPT_ASYM_ALIGN'ed buffers that point to memory allocated for each object
+                    PBYTE               pbPrimes[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                    PBYTE               pbCrtInverses[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                    PBYTE               pbPrivExps[SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS];
+                    PBYTE               pbCrtPrivExps[SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS * SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+
+                    // SymCryptObjects
+                    PSYMCRYPT_MODULUS   pmModulus;          // The modulus N=p*q
+                    PSYMCRYPT_MODULUS   pmPrimes[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                                                            // Pointers to the secret primes
+                    PSYMCRYPT_MODELEMENT peCrtInverses[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                                                            // Pointers to the CRT inverses of the primes
+                    PSYMCRYPT_INT       piPrivExps[SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS];
+                                                            // Pointers to the corresponding private exponents
+                    PSYMCRYPT_INT       piCrtPrivExps[SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS * SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES];
+                                                            // Pointers to the private exponents modulo each prime minus 1 (for CRT)
+
+                    SYMCRYPT_MAGIC_FIELD
+                    // Followed by:
+                    // Modulus
+                    // Primes
+                    // CrtInverses
+                    // PrivExps
+                    // CrtPrivExps
+} SYMCRYPT_RSAKEY;
+typedef       SYMCRYPT_RSAKEY * PSYMCRYPT_RSAKEY;
+typedef const SYMCRYPT_RSAKEY * PCSYMCRYPT_RSAKEY;
+
+//
+// The following definitions relating to trial division are not needed by normal callers
+// but are used by the test program to measure performance of components.
+//
+
+typedef struct _SYMCRYPT_TRIALDIVISION_PRIME {
+    UINT64  invMod2e64;         // Inverse of prime modulo 2^64
+    UINT64  compareLimit;       // floor( (2^{64}-1)/ prime )
+} SYMCRYPT_TRIALDIVISION_PRIME, *PSYMCRYPT_TRIALDIVISION_PRIME;
+typedef const SYMCRYPT_TRIALDIVISION_PRIME * PCSYMCRYPT_TRIALDIVISION_PRIME;
+//
+// This structure is used to test whether a UINT64 is a multiple of a (small) prime.
+// Let V be the input value, P the small prime, and W the inverse of P modulo 2^64.
+// If V = k*P then V * M mod 2^64 = V/P mod 2^64 = k.
+// This holds for k = 0, 1, ..., floor( (2^{64}-1)/p ).
+// If V is not a multiple of P then the result of the multiplication must be larger than that.
+//
+
+typedef struct _SYMCRYPT_TRIALDIVISION_GROUP {
+    UINT32  nPrimes;       // # primes are in this group (use the next ones)
+    UINT32  factor[9];     // factors[i] = 2^{32*(i+1)} mod Prod where Prod = product of the primes
+                           // It is guaranteed that Prod <= (2^{32}-1)/9
+} SYMCRYPT_TRIALDIVISION_GROUP, *PSYMCRYPT_TRIALDIVISION_GROUP;
+typedef const SYMCRYPT_TRIALDIVISION_GROUP * PCSYMCRYPT_TRIALDIVISION_GROUP;
+
+
+typedef struct _SYMCRYPT_TRIALDIVISION_CONTEXT {
+    SIZE_T                          nBytesAlloc;
+    UINT32                          maxTrialPrime;
+    PSYMCRYPT_TRIALDIVISION_GROUP   pGroupList; // terminated with 0 record
+    PSYMCRYPT_TRIALDIVISION_PRIME   pPrimeList; // terminated with 0 record
+    PUINT32                         pPrimes;    // terminated with a 0.
+    SYMCRYPT_TRIALDIVISION_PRIME    Primes3_5_17[3];    // Structures for 3, 5 and 17 in that order
+} SYMCRYPT_TRIALDIVISION_CONTEXT, *PSYMCRYPT_TRIALDIVISION_CONTEXT;
+typedef const SYMCRYPT_TRIALDIVISION_CONTEXT * PCSYMCRYPT_TRIALDIVISION_CONTEXT;
+
+UINT32
+SymCryptTestTrialdivisionMaxSmallPrime( PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext );   // Expose small prime limit to help test code
+
+//
+// DLGROUP type
+//
+
+#define SYMCRYPT_DLGROUP_MIN_BITSIZE_P          (32)
+#define SYMCRYPT_DLGROUP_MIN_BITSIZE_Q          (31)  // Q must always be at least 1 bit shorter than P
+// Minimum allowable bit sizes for generated and imported parameters for both P and
+// Q primes.
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_DLGROUP {
+    UINT32                  cbTotalSize;    // Total size of the dl group object
+    BOOLEAN                 fHasPrimeQ;     // Flag that specifies whether the object has a Q parameter
+
+    UINT32                  nBitsOfP;       // Number of bits of the value of P (not the object's size)
+    UINT32                  cbPrimeP;       // Number of bytes of the value of P (not the object's size), equal to ceil(nBitsOfP/8)
+    UINT32                  nDigitsOfP;     // Number of digits of the object of prime P
+    UINT32                  nMaxBitsOfP;    // Maximum number of bits of the value of P
+
+    UINT32                  nBitsOfQ;       // Number of bits of the value of Q (not the object's bits)
+    UINT32                  cbPrimeQ;       // Number of bytes of the value of Q (not the object's size), equal to ceil(nBitsOfQ/8)
+    UINT32                  nDigitsOfQ;     // Number of digits of the object of prime Q
+    UINT32                  nMaxBitsOfQ;    // Maximum number of bits of the value of Q
+
+    BOOLEAN                 isSafePrimeGroup;   // Boolean indicating if this is a Safe Prime group
+    UINT32                  nMinBitsPriv;   // Minimum number of bits to be used in private keys for this group
+                                            // This only applies to named Safe Prime groups where this is related to the security strength
+                                            // i.e. this corresponds to 2s in SP800-56arev3 5.6.1.1.1 / 5.6.2.1.2
+    UINT32                  nDefaultBitsPriv;   // Default number of bits used in private keys for this group
+                                                // Normally equals nBitsOfQ, but may be further restricted (i.e. for named Safe Prime groups)
+                                                // i.e. this corresponds to a default value of N in SP800-56arev3 5.6.1.1.1 / 5.6.2.1.2
+
+    UINT32                  nBitsOfSeed;    // Number of bits of the seed used for generation (seedlen in FIPS 186-3)
+    UINT32                  cbSeed;         // Number of bytes of the seed, equal to ceil(nBitsOfSeed/8)
+
+    SYMCRYPT_DLGROUP_FIPS   eFipsStandard;  // Code specifying the FIPS standard used to create the keys. If 0 the group is unverified.
+
+    PCSYMCRYPT_HASH         pHashAlgorithm; // Hash algorithm used for the generation of parameters
+    UINT32                  dwGenCounter;   // Number of iterations used for the generation of parameters
+    BYTE                    bIndexGenG;     // Index for the generation of generator G (FIPS 186-3) (Always 1 for now)
+
+    PBYTE                   pbQ;            // SYMCRYPT_ASYM_ALIGN'ed buffer that points to the memory allocated for modulus Q
+
+    PSYMCRYPT_MODULUS       pmP;            // Pointer to the prime P
+    PSYMCRYPT_MODULUS       pmQ;            // Pointer to the prime Q
+
+    PSYMCRYPT_MODELEMENT    peG;            // Pointer to the generator G
+
+    PBYTE                   pbSeed;         // Buffer that will hold the seed (this is padded at the end so that the entire structure
+                                            // has size a multiple of SYMCRYPT_ASYM_ALIGN_VALUE)
+
+    SYMCRYPT_MAGIC_FIELD
+
+    // P
+    // Q
+    // G
+    // Seed
+} SYMCRYPT_DLGROUP;
+typedef       SYMCRYPT_DLGROUP * PSYMCRYPT_DLGROUP;
+typedef const SYMCRYPT_DLGROUP * PCSYMCRYPT_DLGROUP;
+
+//
+// DLKEY type
+//
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_DLKEY {
+                    UINT32                  fAlgorithmInfo; // Tracks which algorithms the key can be used in
+                                                            // Also tracks which per-key selftests have been performed on this key
+                                                            // A bitwise OR of SYMCRYPT_FLAG_KEY_*, SYMCRYPT_FLAG_DLKEY_*, and
+                                                            // SYMCRYPT_PCT_* values
+
+                    BOOLEAN                 fHasPrivateKey; // Set to true if there is a private key set
+                    BOOLEAN                 fPrivateModQ;   // Set to true if the private key is at most Q-1, otherwise it is at most P-2
+                    UINT32                  nBitsPriv;      // Number of bits used in private keys
+
+                    PCSYMCRYPT_DLGROUP      pDlgroup;       // Handle to the group which created the key
+
+                    PBYTE                   pbPrivate;      // SYMCRYPT_ASYM_ALIGN'ed buffer that points to the memory allocated for the private key
+
+                    PSYMCRYPT_MODELEMENT    pePublicKey;    // Public key (modelement modulo P)
+                    PSYMCRYPT_INT           piPrivateKey;   // Private key (integer up to 2^nBitsPriv-1, Q-1 or P-2)
+
+                    SYMCRYPT_MAGIC_FIELD
+
+                    // PublicKey
+                    // PrivateKey                           // The size of this must always be the same as the size of P
+} SYMCRYPT_DLKEY;
+typedef       SYMCRYPT_DLKEY * PSYMCRYPT_DLKEY;
+typedef const SYMCRYPT_DLKEY * PCSYMCRYPT_DLKEY;
+
+//
+// Elliptic Curve Function Types
+//
+
+#define SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH                      4   // Number of MODELEMENTs for the largest ECPOINT format
+
+// Coordinate representations for ECPOINTs
+// NOTE: The value masked with 0xf gives you the number of coordinates
+typedef enum _SYMCRYPT_ECPOINT_COORDINATES {
+    SYMCRYPT_ECPOINT_COORDINATES_INVALID             = 0x00,   // Invalid point representation
+    SYMCRYPT_ECPOINT_COORDINATES_SINGLE              = 0x11,   // Representation with only X
+    SYMCRYPT_ECPOINT_COORDINATES_AFFINE              = 0x22,   // Affine representation (X,Y)
+    SYMCRYPT_ECPOINT_COORDINATES_PROJECTIVE          = 0x33,   // Three equally-sized values where the triple (X,Y,Z) represents the affine point (X/Z, Y/Z)
+    SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN            = 0x43,   // Three equally-sized values where the triple (X,Y,Z) represents the affine point (X/Z^2, Y/Z^3)
+    SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE = 0x54,   // Four equally-sized values where (X,Y,Z,T) represents the affine point (X/Z, Y/Z) with T=X*Y*Z
+    SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE   = 0x62,   // Two equally-sized values where (X,Z) represents the point (X/Z)
+} SYMCRYPT_ECPOINT_COORDINATES;
+
+#define SYMCRYPT_INTERNAL_NUMOF_COORDINATES( _eCoordinates )              ((_eCoordinates) & 0xf)
+
+
+//
+// Curve-type-dependent information
+//
+
+// Short-Weierstrass
+
+#define SYMCRYPT_ECURVE_SW_DEF_WINDOW               (6)         // Default window size for the windowed methods
+
+#define SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS      (64)        // Maximum number of precomputed points
+
+typedef struct _SYMCRYPT_ECURVE_INFO_PRECOMP {
+                    UINT32              window;         // Window size
+                    UINT32              nPrecompPoints; // Number of precomputed points
+                    UINT32              nRecodedDigits; // Number of recoded digits
+                    PSYMCRYPT_ECPOINT   poPrecompPoints[SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS];
+                                                        // Table of pointers to precomputed powers of the distinguished point
+} SYMCRYPT_ECURVE_INFO_PRECOMP;
+
+//
+//  ECURVE object
+//
+
+#define SYMCRYPT_ECURVE_MIN_BITSIZE_FMOD        (32)
+#define SYMCRYPT_ECURVE_MIN_BITSIZE_GORD        (32)
+#define SYMCRYPT_ECURVE_MAX_COFACTOR_POWER      (8)
+// Minimum (maximum for cofactor) allowable bit sizes for imported
+// parameters for field modulus, group order of curve (and cofactor).
+
+#define SYMCRYPT_INTERNAL_ECURVE_VERSION_LATEST                         1
+
+typedef enum _SYMCRYPT_INTERNAL_ECURVE_TYPE {
+    SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS     = 1,
+    SYMCRYPT_INTERNAL_ECURVE_TYPE_TWISTED_EDWARDS       = 2,
+    SYMCRYPT_INTERNAL_ECURVE_TYPE_MONTGOMERY            = 3,
+    SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS_AM3 = 4,// This type is a specialization of Short-Weierstrass when A == -3
+                                                            // This condition is detected and used for all NIST prime curves
+} SYMCRYPT_INTERNAL_ECURVE_TYPE;
+
+C_ASSERT((UINT32)SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS   == (UINT32)SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS );
+C_ASSERT((UINT32)SYMCRYPT_INTERNAL_ECURVE_TYPE_TWISTED_EDWARDS     == (UINT32)SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS );
+C_ASSERT((UINT32)SYMCRYPT_INTERNAL_ECURVE_TYPE_MONTGOMERY          == (UINT32)SYMCRYPT_ECURVE_TYPE_MONTGOMERY );
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_ECURVE {
+                    UINT32                  version;        // Version #
+                    SYMCRYPT_INTERNAL_ECURVE_TYPE
+                                            type;           // Internal type of the curve
+                    SYMCRYPT_ECPOINT_COORDINATES
+                                            eCoordinates;   // Default representation of the EC points
+
+                    UINT32                  FModBitsize;    // Bitsize of the field modulus
+                    UINT32                  FModDigits;     // Number of digits of the field modulus
+                    UINT32                  FModBytesize;   // Bytesize of the field modulus (specified in the curve parameters as cbFieldLength)
+
+                    UINT32                  GOrdBitsize;    // Bitsize of the (sub)group order
+                    UINT32                  GOrdDigits;     // Number of digits of the (sub)group order
+                    UINT32                  GOrdBytesize;   // Bytesize of the (sub)group order (specified in the curve parameters as cbSubgroupOrder)
+
+                    UINT32                  cbModElement;   // (Internal) bytesize of one mod element
+
+                    UINT32                  cbAlloc;        // Bytesize of the total curve blob
+
+                    UINT32                  cbScratchCommon;        // Size of scratch space for common ecurve operations
+                    UINT32                  cbScratchScalar;        // Size of constant scratch space for scalar ecurve operations (without the nPoints dependence)
+                    UINT32                  cbScratchScalarMulti;   // Dependence of scratch space for scalar ecurve operations from nPoints
+                    UINT32                  cbScratchGetSetValue;   // Size of scratch space for get set value ecpoint operations
+                    UINT32                  cbScratchEckey;         // Size of scratch space for eckey operations
+
+                    UINT32                  coFactorPower;  // The cofactor of the curve will be equal to 2^coFactorPower
+
+                    // Parameters V2 Extensions
+                    UINT32                  PrivateKeyDefaultFormat;
+                    UINT32                  HighBitRestrictionNumOfBits;
+                    UINT32                  HighBitRestrictionPosition;
+                    UINT32                  HighBitRestrictionValue;
+
+                    union {
+
+                        SYMCRYPT_ECURVE_INFO_PRECOMP sw;    // Info for short Weierstrass curves (only the precomputation parameters are needed now)
+
+                    } info;                                 // Precomputed information related to each curve
+
+                    PSYMCRYPT_MODULUS       FMod;           // Field modulus
+                    PSYMCRYPT_MODULUS       GOrd;           // Order of the subgroup
+
+                    PSYMCRYPT_MODELEMENT    A;              // Parameter A
+                    PSYMCRYPT_MODELEMENT    B;              // Parameter B
+                    PSYMCRYPT_ECPOINT       G;              // Distinguished point (generator of the subgroup)
+                    PSYMCRYPT_INT           H;              // Cofactor of the curve
+
+                    SYMCRYPT_MAGIC_FIELD
+
+                    // FMod
+                    // A
+                    // B
+                    // GOrd
+                    // H
+                    // G
+} SYMCRYPT_ECURVE;
+typedef       SYMCRYPT_ECURVE * PSYMCRYPT_ECURVE;
+typedef const SYMCRYPT_ECURVE * PCSYMCRYPT_ECURVE;
+
+#define SYMCRYPT_INTERNAL_ECPOINT_COORDINATE_OFFSET( _pCurve, _ord )        ( sizeof(SYMCRYPT_ECPOINT) + (_ord) * (_pCurve)->cbModElement )
+#define SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( _ord, _pCurve, _pEcpoint )    (PSYMCRYPT_MODELEMENT)( (PBYTE)(_pEcpoint) + SYMCRYPT_INTERNAL_ECPOINT_COORDINATE_OFFSET( (_pCurve), _ord ) )
+
+// Convenience macros to make adding internal specializations easier
+#define SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE( _pCurve ) \
+    ( _pCurve->type == SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS || \
+      _pCurve->type == SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS_AM3 )
+
+#define SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE( _pCurve ) \
+    ( _pCurve->type == SYMCRYPT_INTERNAL_ECURVE_TYPE_TWISTED_EDWARDS )
+
+#define SYMCRYPT_CURVE_IS_MONTGOMERY_TYPE( _pCurve ) \
+    ( _pCurve->type == SYMCRYPT_INTERNAL_ECURVE_TYPE_MONTGOMERY )
+
+//
+// Scratch space sizes for ECURVE operations
+//
+//  Overflow protection is enforced when creating the ECURVE objects on
+//  the cbScratchCommon, cbScratchScalar, cbScratchScalarMulti, and cbScratchEckey fields.
+//
+//  All of them are upper bounded by 2^26 (see SymCrypt<CurveType>FillScratchSpaces functions)
+//  and since _nPoints is bounded by SYMCRYPT_ECURVE_MULTI_SCALAR_MUL_MAX_NPOINTS = 2, all
+//  the macros are bounded by 2^27.
+//
+
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( _pCurve )                 ( (_pCurve)->cbScratchCommon)
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( _pCurve, _nPoints )       ( (_pCurve)->cbScratchScalar + \
+                                                                                                (_nPoints) * (_pCurve)->cbScratchScalarMulti )
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( _pCurve )           ( (_pCurve)->cbScratchGetSetValue)
+#define SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_ECKEY_ECURVE_OPERATIONS( _pCurve )                  ( (_pCurve)->cbScratchEckey)
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_ECPOINT {
+                    BOOLEAN normalized;     // A flag specifying whether the point is normalized or not. This flag
+                                            // makes sense only for PROJECTIVE, JACOBIAN, EXTENDED_PROJECTIVE, and
+                                            // SINGLE_PROJECTIVE coordinates. If set to TRUE (non-zero), it means
+                                            // that the Z coordinate of the point is equal to 1.
+                    PCSYMCRYPT_ECURVE   pCurve; // Handle to the curve which the point is on. Only used in CHKed builds for ASSERTs
+                    SYMCRYPT_MAGIC_FIELD
+                    // An array of MODELEMENTs. The total size will depend on the MODELEMENT size and the number of MODELEMENTs.
+} SYMCRYPT_ECPOINT, *PSYMCRYPT_ECPOINT;
+typedef const SYMCRYPT_ECPOINT * PCSYMCRYPT_ECPOINT;
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_ECKEY {
+                    UINT32              fAlgorithmInfo; // Tracks which algorithms the key can be used in
+                                                        // Also tracks which per-key selftests have been performed on this key
+                                                        // A bitwise OR of SYMCRYPT_FLAG_KEY_*, SYMCRYPT_FLAG_ECKEY_*, and
+                                                        // SYMCRYPT_PCT_* values
+                    BOOLEAN             hasPrivateKey;  // Set to true if there is a private key set
+                    PCSYMCRYPT_ECURVE   pCurve;         // Handle to the curve which created the key
+
+                    PSYMCRYPT_ECPOINT   poPublicKey;    // Public key (ECPOINT)
+                    PSYMCRYPT_INT       piPrivateKey;   // Private key
+
+                    SYMCRYPT_MAGIC_FIELD
+
+                    // PublicKey
+                    // PrivateKey
+} SYMCRYPT_ECKEY;
+typedef       SYMCRYPT_ECKEY * PSYMCRYPT_ECKEY;
+typedef const SYMCRYPT_ECKEY * PCSYMCRYPT_ECKEY;
+
+SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_802_11_SAE_CUSTOM_STATE {
+    PSYMCRYPT_ECURVE        pCurve;
+    PCSYMCRYPT_MAC          macAlgorithm;
+    PSYMCRYPT_MODELEMENT    peRand;
+    PSYMCRYPT_MODELEMENT    peMask;
+    PSYMCRYPT_ECPOINT       poPWE;
+    BYTE                    counter;
+};
+
+//
+// XMSS
+//
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_XMSS_PARAMS
+{
+    PCSYMCRYPT_HASH hash;           // hash function
+    UINT32          id;             // algorithm identifier
+    UINT32          cbHashOutput;   // hash function output size, must be less than or equal to hash->resultSize
+    UINT32          nWinternitzWidth;// Winternitz coefficient, width of digits in bits (chain length = 2^nWinternitzWidth)
+    UINT32          nTotalTreeHeight;// number of layers times the tree height of one layer (each layer has the same height)
+    UINT32          nLayers;        // hyper-tree layers, 1 for single tree
+    UINT32          cbPrefix;       // length of the domain separator prefix in PRFs
+
+    //
+    // The following are derived from the above
+    //
+    UINT32          len1;           // number of w-bit digits in the hash output to be signed ( len1 = ceil(8n / w) )
+    UINT32          len2;           // number of w-bit digits in the checksum
+    UINT32          len;            // len1 + len2
+    UINT32          nLayerHeight;   // tree height of a single layer (h / d)
+    UINT32          cbIdx;          // size of leaf counter in bytes (for single trees cbIdx = 4)
+    UINT32          nLeftShift32;   // left shift count to align the checksum digits to MSB of a 32-bit word
+
+    BYTE            Reserved[16];   // Reserved for future use
+} SYMCRYPT_XMSS_PARAMS;
+
+typedef SYMCRYPT_XMSS_PARAMS* PSYMCRYPT_XMSS_PARAMS;
+typedef const SYMCRYPT_XMSS_PARAMS* PCSYMCRYPT_XMSS_PARAMS;
+
+struct _SYMCRYPT_XMSS_KEY;
+typedef struct _SYMCRYPT_XMSS_KEY SYMCRYPT_XMSS_KEY;
+typedef       SYMCRYPT_XMSS_KEY* PSYMCRYPT_XMSS_KEY;
+typedef const SYMCRYPT_XMSS_KEY* PCSYMCRYPT_XMSS_KEY;
+
+
+//==========================================================================
+//   LMS internal structures
+//==========================================================================
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_LMS_PARAMS
+{
+    // algorithm ID of the LMS signature scheme
+    UINT32                  lmsAlgID;
+
+    // algorithm ID of the LM-OTS signature scheme
+    UINT32                  lmsOtsAlgID;
+
+    // hash function pointer to be used as part of the LMS operations
+    PCSYMCRYPT_HASH         pLmsHashFunction;
+
+    // the height of the LMS tree. There are 2^h leaves in the tree - h
+    UINT32                  nTreeHeight;
+
+    // the number of bytes for each tree node, equals to the output length of the hash function - m, n
+    UINT32                  cbHashOutput;
+
+    // Winternitz coefficient, width of digits in bits (chain length = 2^w) - w
+    UINT32                  nWinternitzChainWidth;
+
+    // the number of n-byte string elements that make up the LM-OTS signature - p
+    UINT32                  nByteStringCount;
+
+    // the number of left-shift bits used in the checksum function Cksm - ls
+    UINT32                  nChecksumLShiftBits;
+} SYMCRYPT_LMS_PARAMS;
+typedef       SYMCRYPT_LMS_PARAMS* PSYMCRYPT_LMS_PARAMS;
+typedef const SYMCRYPT_LMS_PARAMS* PCSYMCRYPT_LMS_PARAMS;
+
+struct _SYMCRYPT_LMS_KEY;
+typedef struct _SYMCRYPT_LMS_KEY SYMCRYPT_LMS_KEY;
+typedef        SYMCRYPT_LMS_KEY* PSYMCRYPT_LMS_KEY;
+typedef const  SYMCRYPT_LMS_KEY* PCSYMCRYPT_LMS_KEY;
+
+#ifndef _PREFAST_
+#if SYMCRYPT_CPU_X86
+#pragma warning(pop)
+#endif
+#endif
+
+
+
+//////////////////////////////////////////////////////////
+//
+// Environment macros
+//
+
+#ifdef __cplusplus
+#define SYMCRYPT_EXTERN_C extern "C" {
+#define SYMCRYPT_EXTERN_C_END }
+#else
+#define SYMCRYPT_EXTERN_C
+#define SYMCRYPT_EXTERN_C_END
+#endif
+
+//
+// Callers of SymCrypt should NOT depend on the function names in these macros.
+// The definition of these macros can change in future releases of the library.
+//
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+typedef struct _SYMCRYPT_EXTENDED_SAVE_DATA      SYMCRYPT_EXTENDED_SAVE_DATA, *PSYMCRYPT_EXTENDED_SAVE_DATA;
+
+#define SYMCRYPT_ENVIRONMENT_DEFS_SAVEYMM( envName ) \
+    SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptSaveYmmEnv##envName( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ); \
+    SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptSaveYmm( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ) \
+        { return SymCryptSaveYmmEnv##envName( pSaveArea ); } \
+    \
+    VOID SYMCRYPT_CALL SymCryptRestoreYmmEnv##envName( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ); \
+    VOID SYMCRYPT_CALL SymCryptRestoreYmm( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ) \
+        { SymCryptRestoreYmmEnv##envName( pSaveArea ); } \
+
+#define SYMCRYPT_ENVIRONMENT_DEFS_SAVEXMM( envName ) \
+    SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptSaveXmmEnv##envName( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ); \
+    SYMCRYPT_ERROR SYMCRYPT_CALL SymCryptSaveXmm( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ) \
+        { return SymCryptSaveXmmEnv##envName( pSaveArea ); } \
+    \
+    VOID SYMCRYPT_CALL SymCryptRestoreXmmEnv##envName( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ); \
+    VOID SYMCRYPT_CALL SymCryptRestoreXmm( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea ) \
+        { SymCryptRestoreXmmEnv##envName( pSaveArea ); } \
+
+
+#else
+
+#define SYMCRYPT_ENVIRONMENT_DEFS_SAVEYMM( envName )
+#define SYMCRYPT_ENVIRONMENT_DEFS_SAVEXMM( envName )
+
+#endif
+
+// Environment forwarding functions.
+// CPUIDEX is only forwarded on CPUs that have it.
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86
+#define SYMCRYPT_ENVIRONMENT_FORWARD_CPUIDEX( envName ) \
+    VOID SYMCRYPT_CALL SymCryptCpuidExFuncEnv##envName( int cpuInfo[4], int function_id, int subfunction_id ); \
+    VOID SYMCRYPT_CALL SymCryptCpuidExFunc( int cpuInfo[4], int function_id, int subfunction_id ) \
+        { SymCryptCpuidExFuncEnv##envName( cpuInfo, function_id, subfunction_id ); }
+#else
+#define SYMCRYPT_ENVIRONMENT_FORWARD_CPUIDEX( envName )
+#endif
+
+#define SYMCRYPT_ENVIRONMENT_DEFS( envName ) \
+SYMCRYPT_EXTERN_C \
+    VOID SYMCRYPT_CALL SymCryptInitEnv##envName( UINT32 version ); \
+    VOID SYMCRYPT_CALL SymCryptInit(void) \
+        { SymCryptInitEnv##envName( SYMCRYPT_API_VERSION ); } \
+    \
+    _Analysis_noreturn_ VOID SYMCRYPT_CALL SymCryptFatalEnv##envName( UINT32 fatalCode ); \
+    _Analysis_noreturn_ VOID SYMCRYPT_CALL SymCryptFatal( UINT32 fatalCode ) \
+        { SymCryptFatalEnv##envName( fatalCode ); } \
+    SYMCRYPT_CPU_FEATURES SYMCRYPT_CALL SymCryptCpuFeaturesNeverPresentEnv##envName(void); \
+    SYMCRYPT_CPU_FEATURES SYMCRYPT_CALL SymCryptCpuFeaturesNeverPresent(void) \
+        { return SymCryptCpuFeaturesNeverPresentEnv##envName(); } \
+    \
+    SYMCRYPT_ENVIRONMENT_DEFS_SAVEXMM( envName ) \
+    SYMCRYPT_ENVIRONMENT_DEFS_SAVEYMM( envName ) \
+    \
+    VOID SYMCRYPT_CALL SymCryptTestInjectErrorEnv##envName( PBYTE pbBuf, SIZE_T cbBuf ); \
+    VOID SYMCRYPT_CALL SymCryptInjectError( PBYTE pbBuf, SIZE_T cbBuf ) \
+        { SymCryptTestInjectErrorEnv##envName( pbBuf, cbBuf ); } \
+    SYMCRYPT_ENVIRONMENT_FORWARD_CPUIDEX( envName ) \
+SYMCRYPT_EXTERN_C_END
+
+//
+// To avoid hard-do-diagnose mistakes, we skip defining environment macros in those cases where we
+// know they cannot or should not be used.
+//
+
+#define SYMCRYPT_ENVIRONMENT_GENERIC                            SYMCRYPT_ENVIRONMENT_DEFS( Generic )
+
+#if defined(EFI) | defined(PCAT) | defined(DIRECT)
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_BOOTLIBRARY                SYMCRYPT_ENVIRONMENT_DEFS( WindowsBootlibrary )
+#endif
+
+//
+// There are no defined symbols that we can use to detect that we are in debugger code
+// But this is unlikely to be misused.
+//
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELDEBUGGER             SYMCRYPT_ENVIRONMENT_DEFS( WindowsKernelDebugger )
+
+
+
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_LEGACY          SYMCRYPT_ENVIRONMENT_GENERIC
+
+#ifdef NTDDI_VERSION
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_WIN7_N_LATER    SYMCRYPT_ENVIRONMENT_DEFS( WindowsKernelmodeWin7nLater )
+#endif
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_WIN8_1_N_LATER  SYMCRYPT_ENVIRONMENT_DEFS( WindowsKernelmodeWin8_1nLater )
+#endif
+
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_LATEST          SYMCRYPT_ENVIRONMENT_WINDOWS_KERNELMODE_WIN8_1_N_LATER
+
+
+
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_LEGACY            SYMCRYPT_ENVIRONMENT_GENERIC
+
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_WIN7_N_LATER      SYMCRYPT_ENVIRONMENT_DEFS( WindowsUsermodeWin7nLater )
+#endif
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_WIN8_1_N_LATER    SYMCRYPT_ENVIRONMENT_DEFS( WindowsUsermodeWin8_1nLater )
+#endif
+
+#if (NTDDI_VERSION >= NTDDI_WIN10)
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_WIN10_SGX         SYMCRYPT_ENVIRONMENT_DEFS( Win10Sgx )
+#endif
+#endif // NTDDI_VERSION
+
+#define SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_LATEST            SYMCRYPT_ENVIRONMENT_WINDOWS_USERMODE_WIN8_1_N_LATER
+
+
+#define SYMCRYPT_ENVIRONMENT_POSIX_USERMODE                     SYMCRYPT_ENVIRONMENT_DEFS( PosixUsermode )
+
+// For backwards compatibility with previous macro name
+#define SYMCRYPT_ENVIRONMENT_LINUX_USERMODE                     SYMCRYPT_ENVIRONMENT_POSIX_USERMODE
+
+
+#define SYMCRYPT_ENVIRONMENT_OPTEE_TA                           SYMCRYPT_ENVIRONMENT_DEFS( OpteeTa )
+
+//////////////////////////////////////////////////////////
+//
+// SymCryptWipe & SymCryptWipeKnownSize
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptWipe(
+    _Out_writes_bytes_(cbData)    PVOID   pbData,
+    SIZE_T  cbData);
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+
+//
+// If the known size is large we call the generic wipe function anyway.
+// For small known sizes we perform the wipe inline.
+// This is a tradeoff between speed and code size and there are diminishing returns to supporting
+// increasingly large sizes.
+// We currently put the limit at ~8 native writes, which varies by platform.
+//
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM
+#define SYMCRYPT_WIPE_FUNCTION_LIMIT (32)   // If this is increased beyond 127 the code below must be updated.
+#elif SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_WIPE_FUNCTION_LIMIT (64)   // If this is increased beyond 127 the code below must be updated.
+#else
+#error ??
+#endif
+
+//
+// The buffer analysis code doesn't understand our optimized in-line wiping code
+// well enough to conclude it is safe.
+//
+#pragma prefast(push)
+#pragma prefast( disable: 26001 )
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+#pragma prefast( suppress: 6101, "Logic why this properly initializes the pbData buffer is too complicated for prefast" )
+SymCryptWipeKnownSize(_Out_writes_bytes_(cbData) PVOID pbData, SIZE_T cbData)
+{
+    volatile BYTE * pb = (volatile BYTE *)pbData;
+
+    if (cbData > SYMCRYPT_WIPE_FUNCTION_LIMIT)
+    {
+        SymCryptWipe(pbData, cbData);
+    }
+    else
+    {
+        //
+        // We assume that pb is aligned, so we wipe from the end to the front to keep alignment.
+        //
+        if (cbData & 1)
+        {
+            cbData--;
+            SYMCRYPT_INTERNAL_FORCE_WRITE8((volatile BYTE *)&pb[cbData], 0);
+        }
+        if (cbData & 2)
+        {
+            cbData -= 2;
+            SYMCRYPT_INTERNAL_FORCE_WRITE16((volatile UINT16 *)&pb[cbData], 0);
+        }
+        if (cbData & 4)
+        {
+            cbData -= 4;
+            SYMCRYPT_INTERNAL_FORCE_WRITE32((volatile UINT32 *)&pb[cbData], 0);
+        }
+        if (cbData & 8)
+        {
+            cbData -= 8;
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData], 0);
+        }
+        if (cbData & 16)
+        {
+            cbData -= 16;
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 8], 0);
+        }
+        if (cbData & 32)
+        {
+            cbData -= 32;
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 8], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 16], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 24], 0);
+        }
+#if SYMCRYPT_WIPE_FUNCTION_LIMIT >= 64
+        if (cbData & 64)
+        {
+            cbData -= 64;
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 8], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 16], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 24], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 32], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 40], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 48], 0);
+            SYMCRYPT_INTERNAL_FORCE_WRITE64((volatile UINT64 *)&pb[cbData + 56], 0);
+        }
+#endif
+    }
+}
+
+#pragma prefast(pop)
+
+#else // Platform switch for SymCryptWipeKnownSize
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptWipeKnownSize(_Out_writes_bytes_(cbData) PVOID pbData, SIZE_T cbData)
+{
+    SymCryptWipe(pbData, cbData);
+}
+
+#endif  // Platform switch for SymCryptWipeKnownSize
+
+#define SYMCRYPT_FIPS_ASSERT(x) { if(!(x)){ SymCryptFatal('FIPS'); } }
+
+// Flags for FIPS on-demand selftests. When an on-demand selftest succeeds, the corresponding flag
+// will be set in g_SymCryptFipsSelftestsPerformed. Other selftests are performed automatically
+// when the module is loaded, so they don't have a corresponding flag.
+typedef enum _SYMCRYPT_SELFTEST_ALGORITHM {
+    SYMCRYPT_SELFTEST_ALGORITHM_NONE    =   0x0,
+    SYMCRYPT_SELFTEST_ALGORITHM_STARTUP =   0x1,
+    SYMCRYPT_SELFTEST_ALGORITHM_DSA     =   0x2,
+    SYMCRYPT_SELFTEST_ALGORITHM_ECDSA   =   0x4,
+    SYMCRYPT_SELFTEST_ALGORITHM_RSA     =   0x8,
+    SYMCRYPT_SELFTEST_ALGORITHM_DH      =  0x10,
+    SYMCRYPT_SELFTEST_ALGORITHM_ECDH    =  0x20,
+    SYMCRYPT_SELFTEST_ALGORITHM_MLKEM   =  0x40,
+    SYMCRYPT_SELFTEST_ALGORITHM_XMSS    =  0x80,
+    SYMCRYPT_SELFTEST_ALGORITHM_LMS     = 0x100,
+    SYMCRYPT_SELFTEST_ALGORITHM_MLDSA   = 0x200,
+} SYMCRYPT_SELFTEST_ALGORITHM;
+
+// Takes values which are some bitwise OR combination of SYMCRYPT_SELFTEST_ALGORITHM values
+// Specified as UINT32 as we will update with 32 bit atomics, and compilers may choose to make enum
+// types smaller than 32 bits.
+extern UINT32 g_SymCryptFipsSelftestsPerformed;
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFipsGetSelftestsPerformed(void);
+// Returns current value of g_SymCryptFipsSelftestsPerformed so callers may inspect which FIPS
+// algorithm selftests have run
+
+// Flags for per-key selftests.
+// When an asymmetric key is generated or imported, and SYMCRYPT_FLAG_KEY_NO_FIPS is not specified,
+// some selftests must be performed on the key, before its operational use in an algorithm, to
+// comply with FIPS.
+// The algorithms the key may be used in will be tracked in the key's fAlgorithmInfo field, as a
+// bitwise OR of SYMCRYPT_FLAG_<keytype>_<algorithm> (e.g. SYMCRYPT_FLAG_DLKEY_DH).
+// This field will also track which per-key selftests have been run on the key using the below flags
+// We want to track which selftests have been run independently of which algorithms the key may be
+// used in as in some scenarios at key generation / import time we may not know what algorithm the
+// key will actually be used in. Tracking the run per-key selftests in fAlgorithmInfo allows us to
+// defer running expensive tests until we know they are required (e.g. if we generate an Eckey which
+// may be used in ECDH or ECDSA, and only use it for ECDH, the ECDSA PCT is deferred until we first
+// attempt to use the key in ECDSA, or export the private key).
+//
+// For clarity, SYMCRYPT_PCT_* should be used instead of SYMCRYPT_SELFTEST_KEY_* going forward.
+// The latter is retained for compatibility with existing code, but may be removed in a future
+// breaking change.
+
+// Dlkey selftest flags
+// DSA Pairwise Consistency Test to be run on generated keys
+#define SYMCRYPT_SELFTEST_KEY_DSA       (0x1)
+#define SYMCRYPT_PCT_DSA                SYMCRYPT_SELFTEST_KEY_DSA
+
+// Eckey selftest flags
+// ECDSA Pairwise Consistency Test to be run on generated keys
+#define SYMCRYPT_SELFTEST_KEY_ECDSA     (0x1)
+#define SYMCRYPT_PCT_ECDSA              SYMCRYPT_SELFTEST_KEY_ECDSA
+
+// Rsakey selftest flags
+// RSA Pairwise Consistency Test to be run on generated keys
+#define SYMCRYPT_SELFTEST_KEY_RSA_SIGN  (0x1)
+#define SYMCRYPT_PCT_RSA_SIGN           SYMCRYPT_SELFTEST_KEY_RSA_SIGN
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDeprecatedStatusIndicator(PBYTE pbOutput, UINT32 cbOutput);
+//
+// Returns the FIPS Approved Services Status Indicator as an ASCII string.
+// This API is required to satisfy FIPS 140-3 requirements, but is *not* recommended
+// to be used in production code. It should be considered unstable,
+// and may be removed at any time.
+//
+// The output string will be copied to pbOutput if the size of the buffer
+// cbOutput is large enough. The function returns the required buffer size
+// when pbOutput is passed as NULL. If pbOutput is not NULL, the function
+// returns the number of bytes copied to pbOutput.
+//
+
+
+
+typedef enum _SYMCRYPT_SI_TYPE {
+
+    // Algorithm types (specific algorithms are represented as a bitmask of a type)
+    SYMCRYPT_SI_TYPE_CIPHER             = 0x01,
+    SYMCRYPT_SI_TYPE_HASH               = 0x02,
+    SYMCRYPT_SI_TYPE_MAC                = 0x03,
+    SYMCRYPT_SI_TYPE_KDF                = 0x04,
+    SYMCRYPT_SI_TYPE_DRBG               = 0x05,
+    SYMCRYPT_SI_TYPE_ASYM_ALG           = 0x06,
+    SYMCRYPT_SI_TYPE_KAS                = 0x07,
+    SYMCRYPT_SI_TYPE_KEM                = 0x08,
+
+    // Other types where elements are a bitmask
+    SYMCRYPT_SI_TYPE_ECURVE             = 0x40,
+    SYMCRYPT_SI_TYPE_KAS_SCHEME         = 0x41,
+    SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP   = 0x42,
+
+    // Non-bitmask types
+    SYMCRYPT_SI_TYPE_INTRANGE           = 0x80,
+    SYMCRYPT_SI_TYPE_INTPAIR            = 0x81,
+    SYMCRYPT_SI_TYPE_SIZERANGE          = 0x82,
+
+    SYMCRYPT_SI_TYPE_MAX                = 0xFF
+} SYMCRYPT_SI_TYPE;
+
+#define SYMCRYPT_SI_CREATE_ID(type, index)  (((UINT64)(type) << 56) + (1ULL << (index)))
+
+#define SYMCRYPT_SI_INTBITS                 ((64 - 8) / 2)  // 8-bits for type, remaining bits shared by two integers
+#define SYMCRYPT_SI_INTMASK                 ((1ULL << SYMCRYPT_SI_INTBITS) - 1) // typically should be 0x0FFFFFFF with 28 1s
+#define SYMCRYPT_SI_INTPACK(High, Low)      (((((UINT64)High) & SYMCRYPT_SI_INTMASK) << SYMCRYPT_SI_INTBITS) | (((UINT64)Low) & SYMCRYPT_SI_INTMASK))
+#define SYMCRYPT_SI_INTUNPACKLO(X)          ((X) & SYMCRYPT_SI_INTMASK)
+#define SYMCRYPT_SI_INTUNPACKHI(X)          (((X) >> SYMCRYPT_SI_INTBITS) & SYMCRYPT_SI_INTMASK)
+
+#define SYMCRYPT_SI_INTRANGE(Low, High)     (((UINT64)SYMCRYPT_SI_TYPE_INTRANGE << 56) | SYMCRYPT_SI_INTPACK(High, Low))
+#define SYMCRYPT_SI_INTPAIR(X, Y)           (((UINT64)SYMCRYPT_SI_TYPE_INTPAIR << 56) | SYMCRYPT_SI_INTPACK(Y, X))
+#define SYMCRYPT_SI_SIZERANGE(Low, High)    (((UINT64)SYMCRYPT_SI_TYPE_SIZERANGE << 56) | SYMCRYPT_SI_INTPACK(High, Low))
+
+#define SYMCRYPT_SI_CHECK_INT(L)            C_ASSERT(L <= SYMCRYPT_SI_INTMASK)
+
+#define SYMCRYPT_SI_KEYBITS(L)              SYMCRYPT_SI_SIZERANGE(L, L)
+#define SYMCRYPT_SI_MODULUS(L)              SYMCRYPT_SI_SIZERANGE(L, L)
+#define SYMCRYPT_SI_DSAPARAMS(N, L)         SYMCRYPT_SI_INTPAIR(N, L)
+
+
+// Services
+#define SYMCRYPT_SI_SVC_ENCRYPTION                      0x00000001
+#define SYMCRYPT_SI_SVC_DECRYPTION                      0x00000002
+#define SYMCRYPT_SI_SVC_HASHING                         0x00000004
+#define SYMCRYPT_SI_SVC_MESSAGE_AUTHENTICATION          0x00000008
+#define SYMCRYPT_SI_SVC_KEY_DERIVATION                  0x00000010
+#define SYMCRYPT_SI_SVC_ASYMMETRIC_KEY_GENERATION       0x00000020
+#define SYMCRYPT_SI_SVC_ASYMMETRIC_KEY_VERIFICATION     0x00000080
+#define SYMCRYPT_SI_SVC_RANDOM_NUMBER_GENERATION        0x00000400
+#define SYMCRYPT_SI_SVC_SECRET_AGREEMENT                0x00000800
+#define SYMCRYPT_SI_SVC_SIGNATURE_GENERATION            0x00001000
+#define SYMCRYPT_SI_SVC_SIGNATURE_VERIFICATION          0x00002000
+#define SYMCRYPT_SI_SVC_KEY_ENCAPSULATION               0x00004000
+#define SYMCRYPT_SI_SVC_KEY_DECAPSULATION               0x00008000
+
+// Ciphers
+#define SYMCRYPT_SI_AES_CBC                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 0)
+#define SYMCRYPT_SI_AES_CCM                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 1)
+#define SYMCRYPT_SI_AES_CFB128              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 2)
+#define SYMCRYPT_SI_AES_CFB8                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 3)
+#define SYMCRYPT_SI_AES_CTR                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 4)
+#define SYMCRYPT_SI_AES_ECB                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 5)
+#define SYMCRYPT_SI_AES_GCM                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 6)
+#define SYMCRYPT_SI_AES_XTS                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 7)
+#define SYMCRYPT_SI_RC2                     SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 8)
+#define SYMCRYPT_SI_RC4                     SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 9)
+#define SYMCRYPT_SI_CHACHA                  SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 10)
+#define SYMCRYPT_SI_DES                     SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 11)
+#define SYMCRYPT_SI_TRIPLEDES               SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 12)
+#define SYMCRYPT_SI_CHACHA20                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 13)
+#define SYMCRYPT_SI_CHACHA20_POLY1305       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 14)
+#define SYMCRYPT_SI_AES_KW                  SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 15)
+#define SYMCRYPT_SI_AES_KWP                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_CIPHER, 16)
+
+// Hash Functions
+#define SYMCRYPT_SI_MD2                     SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 0)
+#define SYMCRYPT_SI_MD4                     SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 1)
+#define SYMCRYPT_SI_MD5                     SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 2)
+#define SYMCRYPT_SI_SHA1                    SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 3)
+#define SYMCRYPT_SI_SHA2_224                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 4)
+#define SYMCRYPT_SI_SHA2_256                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 5)
+#define SYMCRYPT_SI_SHA2_384                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 6)
+#define SYMCRYPT_SI_SHA2_512                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 7)
+#define SYMCRYPT_SI_SHA2_512_224            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 8)
+#define SYMCRYPT_SI_SHA2_512_256            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 9)
+#define SYMCRYPT_SI_SHA3_224                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 10)
+#define SYMCRYPT_SI_SHA3_256                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 11)
+#define SYMCRYPT_SI_SHA3_384                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 12)
+#define SYMCRYPT_SI_SHA3_512                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 13)
+#define SYMCRYPT_SI_SHAKE128                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 14)
+#define SYMCRYPT_SI_SHAKE256                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 15)
+#define SYMCRYPT_SI_CSHAKE128               SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 16)
+#define SYMCRYPT_SI_CSHAKE256               SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 17)
+#define SYMCRYPT_SI_MARVIN32                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_HASH, 18)
+
+// MAC
+#define SYMCRYPT_SI_HMAC_MD2                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 0)
+#define SYMCRYPT_SI_HMAC_MD4                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 1)
+#define SYMCRYPT_SI_HMAC_MD5                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 2)
+#define SYMCRYPT_SI_HMAC_SHA1               SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 3)
+#define SYMCRYPT_SI_HMAC_SHA2_224           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 4)
+#define SYMCRYPT_SI_HMAC_SHA2_256           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 5)
+#define SYMCRYPT_SI_HMAC_SHA2_384           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 6)
+#define SYMCRYPT_SI_HMAC_SHA2_512           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 7)
+#define SYMCRYPT_SI_HMAC_SHA2_512_224       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 8)
+#define SYMCRYPT_SI_HMAC_SHA2_512_256       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 9)
+#define SYMCRYPT_SI_HMAC_SHA3_224           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 10)
+#define SYMCRYPT_SI_HMAC_SHA3_256           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 11)
+#define SYMCRYPT_SI_HMAC_SHA3_384           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 12)
+#define SYMCRYPT_SI_HMAC_SHA3_512           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 13)
+#define SYMCRYPT_SI_KMAC128                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 14)
+#define SYMCRYPT_SI_KMAC256                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 15)
+#define SYMCRYPT_SI_AES_GMAC                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 16)
+#define SYMCRYPT_SI_AES_CMAC                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 17)
+#define SYMCRYPT_SI_AES_CBCMAC              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 18)
+#define SYMCRYPT_SI_POLY1305                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_MAC, 19)
+
+// KDF
+#define SYMCRYPT_SI_HKDF                    SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 0)
+#define SYMCRYPT_SI_PBKDF                   SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 1)
+#define SYMCRYPT_SI_KDA_ONESTEP             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 2)
+#define SYMCRYPT_SI_KDF_IKEV1               SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 3)
+#define SYMCRYPT_SI_KDF_IKEV2               SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 4)
+#define SYMCRYPT_SI_KDF_SP800_108_CTR       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 5)
+#define SYMCRYPT_SI_KDF_SRTP                SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 6)
+#define SYMCRYPT_SI_KDF_SSH                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 7)
+#define SYMCRYPT_SI_KDF_TLS                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 8)
+#define SYMCRYPT_SI_KDF_TLS_V12             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KDF, 9)
+
+// DRBG
+#define SYMCRYPT_SI_CTR_DRBG_AES256         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_DRBG, 0)
+
+// Asymmetric Algorithms
+#define SYMCRYPT_SI_SAFE_PRIME_KEYGEN       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 0)
+#define SYMCRYPT_SI_DSA_KEYGEN              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 1)
+#define SYMCRYPT_SI_DSA_PQGGEN              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 2)
+#define SYMCRYPT_SI_DSA_PQGVER              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 3)
+#define SYMCRYPT_SI_DSA_SIGVER              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 4)
+
+#define SYMCRYPT_SI_ECDSA_KEYGEN            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 5)
+#define SYMCRYPT_SI_ECDSA_KEYVER            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 6)
+#define SYMCRYPT_SI_ECDSA_SIGGEN            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 7)
+#define SYMCRYPT_SI_ECDSA_SIGVER            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 8)
+#define SYMCRYPT_SI_ECDSA_SIGGEN_COMP       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 9)
+
+#define SYMCRYPT_SI_RSA_KEYGEN              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 10)
+#define SYMCRYPT_SI_RSA_DEC_PRIM            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 12)
+#define SYMCRYPT_SI_RSA_SIG_PRIM            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 13)
+#define SYMCRYPT_SI_RSA_SIGGEN_PKCS15       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 14)
+#define SYMCRYPT_SI_RSA_SIGGEN_PKCSPSS      SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 15)
+#define SYMCRYPT_SI_RSA_SIGVER_PKCS15       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 16)
+#define SYMCRYPT_SI_RSA_SIGVER_PKCSPSS      SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 17)
+
+#define SYMCRYPT_SI_KAS_ECC                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 18)
+#define SYMCRYPT_SI_KAS_ECC_SSC             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 19)
+#define SYMCRYPT_SI_KAS_FFC                 SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 20)
+#define SYMCRYPT_SI_KAS_FFC_SSC             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 21)
+
+// PQ Algorithms
+
+// Asym Alg IDs for PQC algorithms in range 22-26 are replaced with more granular
+// algorithms as below.
+// Keeping this range reserved until there's a need to use it in the future.
+
+#define SYMCRYPT_SI_MLDSA_KEYGEN            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 27)
+#define SYMCRYPT_SI_MLDSA_SIGGEN            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 28)
+#define SYMCRYPT_SI_MLDSA_SIGVER            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 29)
+#define SYMCRYPT_SI_LMS_KEYGEN              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 30)
+#define SYMCRYPT_SI_LMS_SIGGEN              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 31)
+#define SYMCRYPT_SI_LMS_SIGVER              SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 32)
+#define SYMCRYPT_SI_XMSS_KEYGEN             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 33)
+#define SYMCRYPT_SI_XMSS_SIGGEN             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 34)
+#define SYMCRYPT_SI_XMSS_SIGVER             SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 35)
+#define SYMCRYPT_SI_XMSS_MT_KEYGEN          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 36)
+#define SYMCRYPT_SI_XMSS_MT_SIGGEN          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 37)
+#define SYMCRYPT_SI_XMSS_MT_SIGVER          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ASYM_ALG, 38)
+
+#define SYMCRYPT_SI_MLKEM_KEYGEN            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KEM, 0)
+#define SYMCRYPT_SI_MLKEM_ENCAPS            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KEM, 1)
+#define SYMCRYPT_SI_MLKEM_DECAPS            SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KEM, 2)
+
+
+// Elliptic Curves
+#define SYMCRYPT_SI_ECURVE_NISTP192         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 0)
+#define SYMCRYPT_SI_ECURVE_NISTP224         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 1)
+#define SYMCRYPT_SI_ECURVE_NISTP256         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 2)
+#define SYMCRYPT_SI_ECURVE_NISTP384         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 3)
+#define SYMCRYPT_SI_ECURVE_NISTP521         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 4)
+#define SYMCRYPT_SI_ECURVE_NUMSP256T1       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 5)
+#define SYMCRYPT_SI_ECURVE_NUMSP384T1       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 6)
+#define SYMCRYPT_SI_ECURVE_NUMSP512T1       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 7)
+#define SYMCRYPT_SI_ECURVE_CURVE25519       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_ECURVE, 8)
+
+// Safe Prime Groups
+#define SYMCRYPT_SI_SPG_FFDHE_2048          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 0)
+#define SYMCRYPT_SI_SPG_FFDHE_3072          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 1)
+#define SYMCRYPT_SI_SPG_FFDHE_4096          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 2)
+#define SYMCRYPT_SI_SPG_FFDHE_6144          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 3)
+#define SYMCRYPT_SI_SPG_FFDHE_8192          SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 4)
+#define SYMCRYPT_SI_SPG_MODP_2048           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 5)
+#define SYMCRYPT_SI_SPG_MODP_3072           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 6)
+#define SYMCRYPT_SI_SPG_MODP_4096           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 7)
+#define SYMCRYPT_SI_SPG_MODP_6144           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 8)
+#define SYMCRYPT_SI_SPG_MODP_8192           SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_SAFE_PRIME_GROUP, 9)
+
+// KAS Schemes
+#define SYMCRYPT_SI_SCHEME_EPHEM_UNIFIED    SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KAS_SCHEME, 0)
+#define SYMCRYPT_SI_SCHEME_DH_EPHEM         SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KAS_SCHEME, 1)
+#define SYMCRYPT_SI_SCHEME_DH_ONEFLOW       SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KAS_SCHEME, 2)
+#define SYMCRYPT_SI_SCHEME_DH_STATIC        SYMCRYPT_SI_CREATE_ID(SYMCRYPT_SI_TYPE_KAS_SCHEME, 3)
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDeprecatedServiceIndicator(
+    UINT32 Service,
+    UINT64 Alg,
+    UINT64 Param1,
+    UINT64 Param2,
+    UINT64 Param3);
+//
+// Returns FIPS 140 Approved Services Indicator for an algorithm.
+//
+// Parameters:
+//  - Service. Service identifier, one of SYMCRYPT_SI_SVC_XXX.
+//  - Alg. Identifier of the algorithm for which the status is being queried. This must be
+//  exactly one of the algorithm identifiers defined above.
+//  - Param1, Param2, Param3. Depending on the Alg parameter, these parameters provide
+//  additional information about the capabilities and parameters associated with an
+//  algorithm. For each algorithm, the number and type of the parameters must be provided
+//  as specified below. Any unused parameters must be passed as 0. The algorithms that require
+//  parameters to be specified are listed below, the remaining algorithms do not have any parameters.
+//
+//      Alg Id                          Param1                              Param2
+//      -----------------------------   --------------------------------    ---------------
+//      SYMCRYPT_SI_AES_XTS             SYMCRYPT_SI_KEYBITS(int)                -
+//      SYMCRYPT_SI_DSA_PQGVER          SYMCRYPT_SI_DSAPARAMS(int, int)         -
+//      SYMCRYPT_SI_DSA_SIGVER          SYMCRYPT_SI_DSAPARAMS(int, int)         -
+//      SYMCRYPT_SI_ECDSA_KEYGEN        SYMCRYPT_SI_ECURVE_XXX                  -
+//      SYMCRYPT_SI_ECDSA_KEYVER        SYMCRYPT_SI_ECURVE_XXX                  -
+//      SYMCRYPT_SI_ECDSA_SIGGEN        SYMCRYPT_SI_ECURVE_XXX                  Hash Alg Id
+//      SYMCRYPT_SI_ECDSA_SIGGEN_COMP   SYMCRYPT_SI_ECURVE_XXX                  Hash Alg Id
+//      SYMCRYPT_SI_ECDSA_SIGVER        SYMCRYPT_SI_ECURVE_XXX                  Hash Alg Id
+//      SYMCRYPT_SI_RSA_DEC_PRIM        SYMCRYPT_SI_MODULUS(int)                -
+//      SYMCRYPT_SI_RSA_KEYGEN          SYMCRYPT_SI_MODULUS(int)                -
+//      SYMCRYPT_SI_RSA_SIGGEN_PKCS15   SYMCRYPT_SI_MODULUS(int)                Hash Alg Id
+//      SYMCRYPT_SI_RSA_SIGVER_PKCS15   SYMCRYPT_SI_MODULUS(int)                Hash Alg Id
+//      SYMCRYPT_SI_RSA_SIGGEN_PKCSPSS  SYMCRYPT_SI_MODULUS(int)                Hash Alg Id
+//      SYMCRYPT_SI_RSA_SIGVER_PKCSPSS  SYMCRYPT_SI_MODULUS(int)                Hash Alg Id
+//      SYMCRYPT_SI_SAFE_PRIME_KEYGEN   SYMCRYPT_SI_SPG_XXX                     Hash Alg Id
+//      SYMCRYPT_SI_HMAC_XXX            SYMCRYPT_SI_KEYBITS(int)                -
+//      SYMCRYPT_SI_KDA_ONESTEP         Hash Alg Id or MAC alg Id               -
+//      SYMCRYPT_SI_PBKDF               MAC Alg Id                              -
+//      SYMCRYPT_SI_KDF_SP800_108_CTR   MAC Alg Id                              -
+//      SYMCRYPT_SI_KDF_SSH             Hash Alg Id                             -
+//      SYMCRYPT_SI_TLS_V12_KDF         Hash Alg Id                             -
+//      SYMCRYPT_SI_KAS_ECC             SYMCRYPT_SI_ECURVE_XXX                  Hash Alg Id
+//      SYMCRYPT_SI_KAS_ECC_SSC         SYMCRYPT_SI_ECURVE_XXX                  SYMCRYPT_SI_SCHEME_XXX
+//      SYMCRYPT_SI_KAS_FFC             SYMCRYPT_SI_SPG_XXX                     Hash Alg Id
+//      SYMCRYPT_SI_KAS_FFC_SSC         SYMCRYPT_SI_SPG_XXX                     SYMCRYPT_SI_SCHEME_XXX
+//      SYMCRYPT_SI_LMS_SIGVER          SYMCRYPT_LMS_XXX                        -
+//      SYMCRYPT_SI_XMSS_SIGVER         SYMCRYPT_XMSS_XXX                       -
+//      SYMCRYPT_SI_XMSS_MT_SIGVER      SYMCRYPT_XMSSMT_XXX                     -
+//
+//
+// Return value:
+//  For the specified service and algorithm (and parameters if any), the function
+//  returns 0 if SymCrypt implements the algorithm in an approved manner. A non-zero
+//  value indicates either the algorithm is non-approved or the parameters were invalid.
+//
+// Remarks:
+// - For parameters that contain integer values, the callers must ensure that the values
+// are within the acceptable limits by using the SYMCRYPT_SI_CHECK_INT(L) macro.
diff --git a/libs/symcrypt/inc/symcrypt_internal_shared.inc b/libs/symcrypt/inc/symcrypt_internal_shared.inc
new file mode 100644
index 000000000000..03eae5bf7c06
--- /dev/null
+++ b/libs/symcrypt/inc/symcrypt_internal_shared.inc
@@ -0,0 +1,33 @@
+//
+// symcrypt_internal_shared.inc
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This is the file that contains the SymCrypt version information and defines SYMCRYPT_DEBUG.
+// It is included in both C and ASM such that the values are the same on both sides.
+// We use the C preprocessor to set ASM constants, as we already need to use the C preprocessor for
+// symcryptasm processing (see scripts/symcryptasm_processor.py).
+//
+// In previous releases we had a numbering system with  major/minor version number.
+// This worked well with the sequential servicing imposed by SourceDepot.
+// With the switch to Git this no longer works due to having multiple branches.
+// We move to having the version here only specify the API and minor version number
+// These will NOT be changed for every build. The API version only changes when there are
+// breaking changes to the API in symcrypt.h. (Note: symcrypt_low_level.h is not stable and can change
+// at any time.) The minor version is changed at regular intervals, but not necessarily at
+// every build of the library.
+//
+// Separate from these numbers the build system includes information about the branch,
+// last commit, build time, etc.
+//
+// The API numbering starts at 100 to avoid number conflicts with the old system.
+//
+
+#define SYMCRYPT_CODE_VERSION_API       103
+#define SYMCRYPT_CODE_VERSION_MINOR     11
+#define SYMCRYPT_CODE_VERSION_PATCH     0
+
+#if defined(DBG)
+#define SYMCRYPT_DEBUG 1
+#else
+#define SYMCRYPT_DEBUG 0
+#endif
diff --git a/libs/symcrypt/inc/symcrypt_low_level.h b/libs/symcrypt/inc/symcrypt_low_level.h
new file mode 100644
index 000000000000..296fbd48bf5f
--- /dev/null
+++ b/libs/symcrypt/inc/symcrypt_low_level.h
@@ -0,0 +1,3137 @@
+//
+// SymCrypt_low_level.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#pragma once
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//=======================================================================================
+// WARNING: The low-level APIs are not stable, and can change from release to release.
+// The low-level APIs are only provided for certain exceptional use cases.
+// All aspects of the low-level API can change in any release.
+// Users are strongly advised to only rely on the API surface defined in symcrypt.h
+//=======================================================================================
+
+
+//
+// Low level asymmetric algorithm API. This is not to be used by external callers.
+//
+
+/**************************************************************************************************
+    Low-level Integer API
+ **************************************************************************************************
+The low-level API allows manipulation of arbitrarily large integers.
+
+The internal representation of large integers is not fixed. It depends on CPU architecture and
+on the CPU features available on the exact CPU stepping the current software is running on.
+In other words, it can change between different executions of the same binary.
+Therefore it is critical that callers refrain from making assumptions about the internal
+data format used. SymCrypt numbers should only be manipulated through the SymCrypt
+API.
+
+The low-level API allows the caller to allocate the necessary memory for all objects.
+This is typically necessary for high-IRQL level callers, callers running in low-memory
+environments, and high-performance scenarios where memory has to be pre-allocated.
+SymCrypt also provides routines for allocating objects, which makes the API easier
+to use. The caller has to provide the allocation functions that SymCrypt uses.
+
+Internal data representation, and consequently the size of objects, can depend on the
+exact CPU stepping the code is running on.
+For robustness, the allocation size requirements are compile-time properties;
+they vary per CPU architecture but do not depend on the exact available CPU features.
+
+General rules:
+The functions in the low-level API can impose requirements on their inputs. It is imperative that
+these requirements are satisfied for every call; failing to satisfy the requirements leads to undefined
+behaviour, including bugchecks, access violations, wrong results, or sometimes even the right result.
+CHKed versions of the library add more low-level consistency checks; all binaries should be tested
+with a CHKed version of the library to detect any errors that might go unnoticed on FRE versions.
+
+Scratch space:
+Many functions in the API require temporary storage for intermediate results.
+Some function simply allocate the necessary memory using the caller-provided allocation routines.
+Other low-level functions are so fast that the overhead of the allocations would significantly slow
+down the computations. These functions require the caller to allocate the memory; this memory
+is called the scratch space.
+
+For each function that requires scratch space, there is a macro that determines how much scratch space
+must be provided. This macro is a compile-time function of its arguments; if the parameters to the macro
+are compile-time constants, then the result is also a compile-time constant. Therefore that
+the macro can be used for statically sizing arrays.
+The scratch space macros are all non-decreasing in each argument.
+Callers that perform multiple operations can use a single scratch space, sized for the largest
+argument(s) used. Note that the SYMCRYPT_C_MAX macro implements a compile-time MAX function suitable
+for combining different scratch space sizes at compile time.
+
+The scratch space is always passed as a pair or arguments: (pbScratch, cbScratch).
+cbScratch needs to be at least as large as the macro definition requires, but may be larger.
+
+Functions that take scratch parameters do not require memory allocation, and will not fail due to
+low-memory conditions.
+All functions that use memory allocation will return an error indication if a necessary memory
+allocation fails. These functions return an error code, or an object pointer which will be NULL if
+the allocation fails.
+Functions that do not return an error code or object pointer do not use memory allocation.
+
+SymCrypt uses several implementation techniques to minimize the cost of the scratch space parameters.
+This is necessary because the cost of the parameter passing by itself is significant in scenarios
+such as elliptic-curve operations.
+In a FRE build, some functions will ignore the cbScratch parameter and simply assume they get enough space;
+in this case the SymCrypt may provide an inline-able function that allow the compiler to optimize the cbScratch parameter
+away, completely removing it from the actual code.
+In environments where some functions don't need any scratch space, similar optimizations are possible for the
+pbScratch parameter.
+
+Scratch and object buffers must all be aligned to SYMCRYPT_ALIGN.
+
+*/
+
+//
+// General flags
+//
+// SYMCRYPT_FLAG_DATA_PUBLIC is used to signal that the data being processed is public, and does not have
+// to be protected from side-channel attacks.
+#define SYMCRYPT_FLAG_DATA_PUBLIC   (0x01)
+
+/*
+INTEGERS
+
+Integers are internally represented as a sequence of Digits. An INT object with n digits can store
+numbers up to (but not including) R^n where R is the _radix_ of the representation.
+
+The radix R, as well as the size and format of a Digit, are internal to the library,
+and can depend on CPU architecture, CPU stepping and other run-time decisions. Therefore, callers
+need to be especially careful not to make any assumptions about the size of a digit, or the number
+of digits needed for any particular computation.
+
+At the same time, most INT operations are defined in terms of Digit sizes, so the caller has to
+be aware of digits. This becomes important in the following example. Suppose the radix R =2^256,
+and a caller wants to multiply two 384-bit numbers. It takes 2 digits to store a 384-bit number.
+The caller knows that the product is 768 bits, which can fit in 3 digits. So the caller might try
+to multiply two 2-digit numbers into a 3-digit result, which will not work as the result is 4 digits.
+
+For an INT object of n digits we call the value R^n the capacity of the object. It is the upper bound of
+the values that can be stored in the object.
+
+Additionally, there is a maximum number of bits for any integer value that the library supports (2^20 bits
+in the current version). This bound is used to ensure that no object sizes and scratch space computations
+have a value of magnitude more than 32 bits. Note that the computed upper bounds are very loose and the
+actual values are much smaller.
+
+Attempts to create objects larger than this bound will result in NULL being returned. Callers either have
+to ensure they do not exceed the bounds, or check that create objects are not NULL before using them. The
+rationale behind this approach is to avoid any potential route for malicious inputs to trigger DoS by
+taking excessive CPU time which would be indistinguishable from an application hang.
+
+
+Digit size and radix can vary widely; on some CPU steppings the library might use a digit that contains
+128 bits are requires 16 bytes of memory, on another CPU stepping it might use a digit that contains
+416 bits and uses 64 bytes of memory.
+
+SAL annotations:
+Because the different run-time selected implementations underneath this API might use
+different size memory buffers for any one operation, fully accurate SAL annotations are not possible as SAL only
+performs static analysis.
+Furthermore, adding size parameters to every function would add too much overhead, and sizes are often passed
+implicitly. Together with the fact that the same API can be implemented by different implementations, this means
+that it isn't possible to write the actual size used in a form that SAL can understand.
+Instead we use the following conventions:
+-   Pointers to SYMCRYPT_* objects can only be created with functions that provide the right memory buffer size.
+-   We annotate each object-pointer with _In_ our _Out_. The SAL engine treats this as just a read/write to
+    a single object at the pointer location
+-   The CHKed version of SymCrypt adds run-time checking that the various size parameters are correct.
+This allows us to have both high performance and good checking of our memory management.
+
+API rationale:
+One important choice in this API is whether to pass a (ptr,len) for each INT or just a pointer.
+We investigated this issue. The ptr-based API means that there are fewer parameters to pass around,
+and generally makes the API simpler. The downside of a ptr-based API is that each INT object has some overhead
+and this makes arrays of large integers less efficient, especially since the overhead can be a whole alignment
+block.
+The problem with the (ptr,len) format is that it isn't clear what length measure to use.
+Using the bitsize is inefficient; the internal format might store 29 bits of the number in each 32-bit word,
+and that means that the code would have to divide the bitsize by 29 just to find the size of the number.
+Division is slow, and therefore this is not a good choice.
+Another idea is to have the len parameter be the length of the INT object, in bytes.
+But some APIs get really messy. For example, we need an API function to do a multiplication of two same-sized
+numbers into a double-sized number. This is such a common operation that we want a separate function for it.
+But the storage size of the result might not be twice the storage size of the inputs; if each number has some
+fixed overhead then the output object might be smaller than the two times the size of the input objects.
+This makes it impossible to write suitable SAL annotations.
+For this reason, we use a ptr-based API for integers.
+
+Most crypto algorithms that wish to store arrays of values actually want to store arrays of elements in a
+ring modulo an modulus. And for modular operations the caller is already passing the modulus separately, so
+there isn't any need to store per-object size information. The API is designed to allow the ModElement for bitsize
+B to be smaller than an INT for bitsize B so that implementations can choose to not store any length information in
+a ModElement object.
+*/
+
+//========================================================================
+//========================================================================
+// Main schema for object creation, deletion, and management (low - level calls).
+//
+// The following are descriptions of some of the generic functions specifically
+// modified for the INT, DIVISOR, MODULUS, and MODELEMENT objects.
+
+//
+// PSYMCRYPT_XXX
+// SYMCRYPT_CALL
+// SymCryptXxxCreate(
+//      _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+//                                      SIZE_T  cbBuffer,
+//                                      UINT32  nDigits );
+//  Create an XXX object from the provided (pbBuffer, cbBuffer) space.
+//  The object will be able to store values up to R^nDigits where R is the digit radix.
+//  Requirement:
+//      - 1 <= nDigits <= SymCryptDigitsFromBits(SYMCRYPT_INT_MAX_BITS)
+//          If the value is outside these bounds it will return NULL
+//      - cbBuffer >= SymCryptSizeofXxxFromDigits( nDigits )
+//      - (pbBuffer,cbBuffer) memory must be exclusively used by this object.
+//  The last requirement ensures that all objects are non-overlapping (except for API functions
+//  that explicitly create overlapping objects).
+//  All parameters are published.
+//  It is always safe to choose
+//      cbBuffer = SYMCRYPT_SIZEOF_XXX_FROM_BITS( nBits )
+//      nDigits  = SymCryptDigitsFromBits( nBits )
+//  if the caller wants to be able to store numbers up to 2^nBits. However, it is frequently more
+//  efficient to use cbBuffer = SymCryptSizeofXxxFromDigits( nDigits ) as that gives the exact size for the
+//  current CPU stepping rather than the compile-time largest size that might be needed on any stepping.
+//
+// PSYMCRYPT_XXX
+// SYMCRYPT_CALL
+// SymCryptXxxRetrieveHandle( _In_  PBYTE   pbBuffer );
+//  Retrieve the object's handle from the pointer to the memory space in which the object was created via
+//  a call to SymCryptXxxCreate. This function allows callers to tightly store arrays of objects without having
+//  to keep track of each object handle.
+//  Requirement:
+//      - A call to SymCryptXxxRetrieveHandle( pbBuffer1 ) must be preceded by at least one call to
+//        SymCryptXxxCreate( pbBuffer2, cbBuffer2, nDigits ) with ( pbBuffer1 == pbBuffer2 )
+//  If the requirement is not satisfied the result is undefined.
+//
+// #define SYMCRYPT_SIZEOF_XXX_FROM_BITS( nBits ) ...
+//  Returns a memory size that is always sufficient to create an XXX object that can handle
+//  values of size nBits bits, irrespective of the run-time decision of digit size.
+//  This is a non-decreasing compile-time function of its inputs, suitable for computing static memory allocations.
+//  It is always true that
+//    SYMCRYPT_SIZEOF_XXX_FROM_BITS( n ) >= SymCryptSizeofXxxFromDigits( SymCryptDigitsFromBits( n ) )
+//  which guarantees that the n-bit XXX can be stored in a memory area of SYMCRYPT_SIZEOF_XXX_FROM_BITS(n) bytes.
+//  Warning: It is possible that
+//    SYMCRYPT_SIZEOF_XXX_FROM_BITS( n+m ) < SymCryptSizeofXxxFromDigits( SymCryptDigitsFromBits( n ) + SymCryptDigitsFromBits( m ) )
+//  for some inputs n and m. This is easy to see if you choose n = m = 1; each represents a 1-digit value, but an n+m bit (i.e. a 2-bit ) value is
+//  also 1 digit.
+//  In particular, you cannot use SYMCRYPT_SIZEOF_XXX_FROM_BITS( n + m ) to compute the size
+//  necessary to store the product of two numbers with bitsize n and m respectively.
+//  It is guaranteed that
+//    SymCryptSizeofXxxFromDigits( SymCryptDigitsFromBits( n ) + SymCryptDigitsFromBits( m ) ) <=
+//      SYMCRYPT_SIZEOF_XXX_FROM_BITS( n ) + SYMCRYPT_SIZEOF_XXX_FROM_BITS( m )
+//  This is the proper way to statically compute the size needed to store the product of an n- and m-bit value.
+//
+// UINT32
+// SYMCRYPT_CALL
+// SymCryptSizeofXxxFromDigits( UINT32 nDigits );
+//  Memory size that is sufficient to store an XXX object with nDigits digits.
+//  This is a runtime function as the # digits and size of a digit are run-time decision that depend on the CPU stepping.
+//  Requirement:
+//      - 1 <= nDigits <= SymCryptDigitsFromBits(SYMCRYPT_INT_MAX_BITS)
+//          If the value is outside these bounds the returned value will be 0 indicating failure.
+//  This function is has the following property:
+//    SymCryptSizeofXxxFromDigits( a + b ) <= SymCryptSizeofXxxFromDigits( a ) + SymCryptSizeofXxxFromDigits( b )
+//  for all a and b.
+//
+// UINT32
+// SYMCRYPT_CALL
+// SymCryptXxxBitsizeOfObject( PCSYMCRYPT_XXX pObj )
+//  Return the number of bits of the object.
+//
+// UINT32
+// SYMCRYPT_CALL
+// SymCryptXxxDigitsizeOfObject( PCSYMCRYPT_XXX pObj )
+//  Return the number of digits of the object.
+//
+
+//==============================================================================================
+// Object types for low-level API
+//
+// SYMCRYPT_INT          integer in range 0..N for some N
+// SYMCRYPT_DIVISOR      an integer > 0 that can be used to divide with.
+// SYMCRYPT_MODULUS      a value M > 1 to use in modulo-M computations
+// SYMCRYPT_MODELEMENT   An element in a modulo-M ring.
+// SYMCRYPT_ECPOINT      A point on an elliptic curve.
+//
+// See symcrypt_internal.h for definitions.
+//
+
+//========================================================================
+//========================================================================
+// General functions for integers
+//
+
+UINT32
+SymCryptDigitsFromBits( UINT32 nBits );
+//
+// Returns the # digits needed to store values (INT, DIVISOR, MODULUS, MODELEMENT)
+// in the range 0..(2^nBits - 1).
+//
+// Remarks:
+//  If nBits==0 the returned number is 1.
+//
+//  If nBits exceeds SYMCRYPT_INT_MAX_BITS the function will return 0 to indicate an object with
+//  this many bits is not supported.
+//
+//  This is a run-time decision; the return value can depend on the exact CPU stepping
+//  the program is running on, or run-time configurations.
+//  For a and b in the range 0..SYMCRYPT_INT_MAX_BITS, it is always true that
+//  SymCryptDigitsFromBits( a + b ) <= SymCryptDigitsFromBits( a ) + SymCryptDigitsFromBits( b )
+//
+
+//========================================================================
+// INT objects
+//
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntAllocate( UINT32 nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntFree( _Out_ PSYMCRYPT_INT piObj );
+
+#define SYMCRYPT_SIZEOF_INT_FROM_BITS( _bitsize )    SYMCRYPT_INTERNAL_SIZEOF_INT_FROM_BITS( _bitsize )
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofIntFromDigits( UINT32 nDigits );
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntWipe( _Out_ PSYMCRYPT_INT piObj );
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );        // **** Documentation lacking: requires same size
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMaskedCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          mask );
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntConditionalCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          cond );
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntConditionalSwap(
+    _Inout_ PSYMCRYPT_INT   piSrc1,
+    _Inout_ PSYMCRYPT_INT   piSrc2,
+            UINT32          cond );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntBitsizeOfObject( _In_ PCSYMCRYPT_INT piSrc );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntDigitsizeOfObject( _In_ PCSYMCRYPT_INT piSrc );
+
+//========================================================================
+// DIVISOR objects
+//
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptDivisorAllocate( UINT32 nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDivisorFree( _Out_ PSYMCRYPT_DIVISOR pdObj );
+
+#define SYMCRYPT_SIZEOF_DIVISOR_FROM_BITS( _bitsize )    SYMCRYPT_INTERNAL_SIZEOF_DIVISOR_FROM_BITS( _bitsize )
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofDivisorFromDigits( UINT32 nDigits );
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptDivisorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDivisorWipe( _Out_ PSYMCRYPT_DIVISOR pdObj );
+
+VOID
+SymCryptDivisorCopy(
+    _In_    PCSYMCRYPT_DIVISOR  pdSrc,
+    _Out_   PSYMCRYPT_DIVISOR   pdDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDivisorDigitsizeOfObject( _In_ PCSYMCRYPT_DIVISOR pdSrc );
+
+//========================================================================
+// MODULUS objects
+//
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptModulusAllocate( UINT32 nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptModulusFree( _Out_ PSYMCRYPT_MODULUS pmObj );
+
+#define SYMCRYPT_SIZEOF_MODULUS_FROM_BITS( _bitsize )    SYMCRYPT_INTERNAL_SIZEOF_MODULUS_FROM_BITS( _bitsize )
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofModulusFromDigits( UINT32 nDigits );
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptModulusCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptModulusWipe( _Out_ PSYMCRYPT_MODULUS pmObj );
+
+VOID
+SymCryptModulusCopy(
+    _In_    PCSYMCRYPT_MODULUS  pmSrc,
+    _Out_   PSYMCRYPT_MODULUS   pmDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptModulusDigitsizeOfObject( _In_ PCSYMCRYPT_MODULUS pmSrc );
+
+//========================================================================
+// MODELEMENT objects are treated slightly differently because it does not store its own size.
+// This allows a MODELEMENT to be more compact which makes large arrays of ModElements more efficient
+// and avoids checking that ModElements have the same size.
+// All operations require a modulus to be passed.
+//
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptModElementAllocate( _In_ PCSYMCRYPT_MODULUS pmMod );
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementFree(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,      // only used to determine the digit size of peObj.
+    _Out_   PSYMCRYPT_MODELEMENT    peObj );
+
+#define SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( _bitsize )    SYMCRYPT_INTERNAL_SIZEOF_MODELEMENT_FROM_BITS( _bitsize )
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofModElementFromModulus( PCSYMCRYPT_MODULUS pmMod );
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptModElementCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+    _In_                            PCSYMCRYPT_MODULUS  pmMod );
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementWipe(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SymCryptModElementCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SymCryptModElementMaskedCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst,
+            UINT32                  mask );
+
+VOID
+SymCryptModElementConditionalSwap(
+     _In_       PCSYMCRYPT_MODULUS    pmMod,
+     _Inout_    PSYMCRYPT_MODELEMENT  peData1,
+     _Inout_    PSYMCRYPT_MODELEMENT  peData2,
+     _In_       UINT32                cond );
+
+//========================================================================
+// ECURVE objects
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEcurveBufferSizesFromParams(
+    _In_    PCSYMCRYPT_ECURVE_PARAMS    pParams,
+    _Out_   SIZE_T *                    pcbCurve,
+    _Out_   SIZE_T *                    pcbScratch );
+//
+// This call computes the memory size necessary to create the ECURVE object described by pParams,
+// including the amount of scratch space needed for the operation.
+//
+// Returns FALSE if the given parameters are deemed invalid.
+//
+
+PSYMCRYPT_ECURVE
+SYMCRYPT_CALL
+SymCryptEcurveCreate(
+    _In_                                PSYMCRYPT_ECURVE_PARAMS pParams,
+    _In_                                UINT32                  flags,
+    _Out_writes_bytes_( cbCurve )       PBYTE                   pbCurve,
+                                        SIZE_T                  cbCurve,
+    _Out_writes_bytes_( cbScratch )     PBYTE                   pbScratch,
+                                        SIZE_T                  cbScratch );
+//
+// Use caller-allocated memory to create an ECURVE object which
+// is defined by the parameters in pParams.
+//
+// - pParams: parameters that define the curve
+// - flags: Not used, must be zero.
+// - pbCurve: caller-allocated memory region to hold the curve object
+// - cbCurve: size of memory region to hold the curve object
+// - pbScratch: caller-allocated memory region used as scratch space to create the curve
+// - cbScratch: size of scratch space memory region
+//
+// Caller should use SymCryptSizeofEcurveBuffersFromParams to determine the necessary sizes for
+// pbCurve and pbScratch. These buffers must be SYMCRYPT_ALIGNed.
+//
+// Future versions might use the flags to enable different features/tradeoffs.
+// There are a number of interesting memory/speed/pre-computation cost trades that can be made.
+// For example, pre-computing multiples of the distinguished point, or (parallel?) pre-computation
+// of (r, rG) pairs for random r values.
+//
+// This function applies limited validation of the pParams. The validation is intended to eliminate
+// the threat of denial-of-service when hostile parameters are presented. It does not ensure that
+// the parameters make sense, define a proper curve, or that any elliptic-curve operations made on
+// the curve built from these parameters will fail, succeed or provide any security.
+// The only guarantee provided for invalid parameters is that all operations on this curve will
+// not crash and will return in some reasonable amount of time.
+//
+// Returns NULL if the given memory regions are not large enough or the
+// parameters are deemed invalid. If the return value is not NULL, then
+// pbCurve buffer must later be wiped with SymCryptWipe(). And as with all
+// pbScratch buffers, it is the caller's responsibility to wipe after
+// completing all operations that require scratch space.
+//
+
+//========================================================================
+// ECPOINT objects' API is slightly different than the above API schema in the sense that they
+// take as input an ECURVE object pointer instead of the number of digits.
+//
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointAllocate( _In_ PCSYMCRYPT_ECURVE pCurve );
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointFree(
+     _In_   PCSYMCRYPT_ECURVE    pCurve,
+     _Out_  PSYMCRYPT_ECPOINT    poDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofEcpointFromCurve( PCSYMCRYPT_ECURVE pCurve );
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+    _In_                            PCSYMCRYPT_ECURVE   pCurve );
+// The above can take as input a pointer to a curve that has only the FMod, cbModElement, and the
+// eformat fields set
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointRetrieveHandle( _In_  PBYTE   pbBuffer );
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointWipe(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst );
+
+VOID
+SymCryptEcpointCopy(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst );
+
+VOID
+SymCryptEcpointMaskedCopy(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              mask );
+
+
+//========================================
+// Integer operations
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntCopyMixedSize(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = Src, but allows Dst and Src to have different # digits.
+//
+// Copy the value from piSrc to piDst.
+// Returns success if  Src < R^Dst.nDigits
+// If Src >= R^Dst.nDigits then the value in Src is published and an error is returned.
+// Warning: it is not side-channel safe to use this function with a Src value that can't fit in Dst.
+// Src and Dst may be the same object.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntBitsizeOfValue( _In_ PCSYMCRYPT_INT piSrc );
+//
+// Returns the number of bits necessary to store the value of Src.
+//
+// Let V be the value of Src.
+// Then this function returns
+//  0 if Src == 0
+//  1 + floor( log(Src)/log(2) )  if V > 0
+// Note that there is no defined relationship between the result of this function and the bitsize used to allocate Src.
+// Digits can be large, so the value Src might be able to store values much larger than 2^b where b is the bitsize
+// used when creating Src.
+// This function is side-channel safe, and as a result might be slower than expected.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSetValueUint32(
+            UINT32          u32Src,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = Src
+// This always succeeds as R >= 2^32 on all implementations.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSetValueUint64(
+            UINT64          u64Src,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = Src
+// This always succeeds as R >= 2^64 on all implementations.
+//
+
+
+//========================================================================================
+// Read/write INTegers in defined formats
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntSetValue(
+    _In_reads_bytes_(cbSrc)     PCBYTE                  pbSrc,
+                                SIZE_T                  cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_                       PSYMCRYPT_INT           piDst );
+//
+// Set the value of an INT object from an array of bytes
+//
+// (pbSrc,cbSrc): buffer that contains the bytes that encode the value in the specified format.
+// format: specifies the format of the pbBytes/cbBytes buffer.
+// Dst : INT object that receives the value; must previously have been created/allocated.
+//
+// Return value:
+//   If the value encoded in the (pbSrc,cbSrc) buffer fits in Dst, then the
+//   function succeeds. If the value does not fit, then the function
+//   returns an error. Note that the error condition is only dependent on the value in the input,
+//   and not on how many bytes are in the input. Importing a very large (pbSrc,cbSrc) buffer
+//   into a small piDst is fine as long as the value fits in the number (i.e. enough of the most significant
+//   bytes in the buffer are zero).
+//
+// Warning:
+//  Error return values are always published, so if this function fails it is visible to the attacker.
+//
+// Rationale:
+//   Because the size of a digit can be any size (even odd) there are always scenarios in which the
+//   caller can provide an input that is too large for the INT to store. (Restricting only the size of
+//   the input buffer is not sufficient.) And if we have to handle this
+//   in one case, we might as well handle it in all cases.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntGetValue(
+    _In_                        PCSYMCRYPT_INT          piSrc,
+    _Out_writes_bytes_( cbDst)  PBYTE                   pbDst,
+                                SIZE_T                  cbDst,
+                                SYMCRYPT_NUMBER_FORMAT  format );
+//
+// Convert a value from the internal number representation to a byte array.
+//
+// Src is the number whose value is to be stored in a byte array
+// (pbDst, cbDst) the destination buffer
+// format: the destination format.
+// Return value: if the value of Src when encoded in the format fits in the output buffer then the function succeeds.
+// If the encoded value does not fit, the function returns an error. (Note: All errors are published.)
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntGetValueLsbits32( _In_  PCSYMCRYPT_INT piSrc );
+//
+// Returns Src mod 2^32
+//
+// Usecase: there are many number-theoretic algorithms where the algorithm
+// depends on (n mod 8) or similar values.
+//
+
+UINT64
+SYMCRYPT_CALL
+SymCryptIntGetValueLsbits64( _In_  PCSYMCRYPT_INT piSrc );
+//
+// Returns Src mod 2^64
+//
+// Usecase: RSA public exponents can be 64 bits, and validating that
+// a candidate prime is suitable uses this function
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntIsEqualUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    UINT32          u32Src2 );
+//
+// Returns a mask value which is 0xffffffff if Src1 = Src2 and 0 otherwise.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntIsEqual(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 );
+//
+// Returns a mask value which is 0xffffffff if Src1 = Src2 and 0 otherwise.
+//
+// Note that Src1 and Src2 can be of different sizes.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntIsLessThan(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 );
+//
+// Returns a mask value which is 0xffffffff if Src1 < Src2 and 0 otherwise.
+//
+// Note that a <= b is equivalent to NOT( b < a ) so all possible comparisons
+// can be made using the < and = comparison primitive.
+//
+
+
+//=============================================================
+// Addition & subtraction
+// For all addition and subtraction operations, the destination may be
+// the same object as one of the inputs if the other requirements of the function
+// allow that.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntAddUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = Src1 + Src2.
+// Requirement: Dst.nDigits == Src1.nDigits
+// If the result is larger than the capacity of Dst, then
+// Dst is set to the result minus the capacity and the value 1 is returned.
+// Otherwise the Dst is set to the sum and the value 0 is returned.
+// The return value is thus a carry output of the addition.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntAddSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst );
+//
+// Dst = Src1 + Src2.
+// Requirement: Src1.nDigits == Src2.nDigits == Dst.nDigits
+// In more detail:
+//  if Src1 + Src2 < Dst.capacity:
+//      Dst = Src1 + Src2
+//      return 0
+//  else
+//      Dst = Src1 + Src2 - Dst.capacity
+//      return 1
+// The return value is a carry output of the addition.
+//
+// Dst may be the same object as Src1, Src2, or both.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntAddMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst );
+//
+// Dst = Src1 + Src2.
+// Requirement: Dst.nDigits >= max( Src1.nDigits, Src2.nDigits )
+// In more detail:
+//  if Src1 + Src2 < Dst.capacity:
+//      Dst = Src1 + Src2
+//      return 0
+//  else
+//      Dst = Src1 + Src2 - Dst.capacity
+//      return 1
+// The return value is a carry output of the addition.
+//
+// Dst may be the same object as Src1, Src2, or both.
+//
+
+//
+// Subtraction
+// Subtraction functions are the equivalent of addition functions.
+// The return value is 1 if an underflow occurred (borrow), and 0 if no underflow/borrow occurred.
+// On underflow, the value of the result is the result of the subtraction plus Dst.capacity.
+//
+// Rationale: For an underflow we could also return (UINT32)-1 or return -1 on a INT32.
+// -1 in an unsigned type is actually 2^32 -1 which makes no sense.
+// Returning a signed type is somewhat neater, but all other values are unsigned, and mixing
+// signed and unsigned types is always error-prone. Furthermore, converting from a signed integer
+// to a mask is also error-prone (at least within the behaviour guaranteed by the C standard.)
+// Returning an unsigned 1 is therefore preferred.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntSubUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          Src2,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntSubSameSize(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntSubMixedSize(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntNeg(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+//
+// Dst = (- Src) mod Dst.Capacity;
+// Requirement:
+//  - Dst.nDigits == Src.nDigits;
+// This is a negate modulo the capacity.
+// Useful when you want the absolute value of a difference.
+// Compute the difference, and if the subtraction yields a carry, negate the result.
+//
+
+//===================================================================
+// Shifts
+// Note that the shift amount is always published.
+// If the need arises, we can define variants that are side-channel safe
+// w.r.t. the shift size, but that incurs a significant performance cost.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMulPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = (Src * 2^Exp ) mod R^n  where n = Dst.nDigits.
+// Requirement: Dst.nDigits == Src.nDigits, Dst == Src is allowed
+// Exp is published.
+//
+// A variant that keeps Exp private is currently not available, but can be added to the API if needed.
+// (A side-channel safe variant might require scratch space.)
+//
+// Dst may be the same object as Src1.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntDivPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = (Src div 2^Exp )
+// Requirement: Dst.nDigits == Src.nDigits, Dst == Src is allowed
+// Exp is published
+//
+// A variant that keeps Exp private is currently not available, but can be added to the API if needed.
+// (A side-channel safe variant might require scratch space.)
+//
+// Dst may be the same object as Src1.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntShr1(
+            UINT32          highestBit,
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = (Src + highestBit * Src.Capacity) div 2
+//
+// Requirements:
+//  Src.nDigits == Dst.nDigits
+//  highestBit <= 1
+//
+// This is the Int equivalent of the 'shift right 1' instruction.
+// Shifting by one can be implemented faster than variable sized shifts.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntModPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst );
+//
+// Dst = Src mod 2^Exp
+// Requirement: Dst.nDigits == Src.nDigits, Dst == Src is allowed
+// Exp is published
+//
+// Dst may be the same object as Src1.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntGetBit(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit );
+//
+// Returns the i-th bit (starting from 0 for the LSB) of piSrc.
+// Therefore the only possible return values are 0 and 1.
+//
+// Requirements:
+//  - iBit < SymCryptIntBitsizeOfObject( piSrc )
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntGetBits(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit,
+            UINT32          nBits );
+//
+// Returns the bits from position iBit up to (iBit + nBits - 1)
+// (starting from 0 for the LSB). Total of nBits. The 0-th bit of
+// the return value corresponds to the iBit-th bit of the source.
+//
+// Requirements:
+//  -   1 <= nBits <= 32
+//  -   iBit + nBits <= SymCryptIntBitsizeOfObject( piSrc )
+//
+// Remarks:
+//      - The values iBit and nBits are not protected by side-channel attacks,
+//        therefore they should be treated as published.
+//      - The bits of the return value after the (nBits)-th bit are zero.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSetBits(
+    _In_    PSYMCRYPT_INT   piDst,
+            UINT32          value,
+            UINT32          iBit,
+            UINT32          nBits );
+//
+// Sets the bits from position iBit up to (iBit + nBits - 1)
+// (starting from 0 for the LSB). Total of nBits. The 0-th bit of
+// the input value corresponds to the iBit-th bit of the destination.
+//
+// Requirements:
+//  -   1 <= nBits <= 32
+//  -   iBit + nBits <= SymCryptIntBitsizeOfObject( piSrc )
+//
+// Remarks:
+//      - The values iBit and nBits are not protected by side-channel attacks,
+//        therefore they should be treated as published.
+//      - The bits of the value after the (nBits)-th bit are ignored.
+//
+
+//===========================================================
+// Mul & div
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntMulUint32(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+                                    UINT32          Src2,
+    _Out_                           PSYMCRYPT_INT   piDst );
+//
+// Dst = Src1 * Src2 mod Dst.capacity; return value = Src1 * Src2 div Dst.capacity
+// Requirement: piDst.nDigits == piSrc1.nDigits, Dst == Src is allowed
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( _nResultDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_MUL( _nResultDigits )
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMulSameSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+//
+// Dst = Src1 * Src2.
+// Requirement:
+//  - Src1.nDigits == Src2.nDigits; Dst.nDigits == Src1.nDigits + Src2.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( Dst.nDigits )
+//
+// Note that Dst cannot be the same object as Src1 or Src2 because of the size restrictions.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSquare(
+    _In_                            PCSYMCRYPT_INT  piSrc,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+//
+// Dst = Src^2
+// Requirement:
+//  - Dst.nDigits == 2 * Src.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( Dst.nDigits )
+//
+// Note that Dst cannot be the same object as Src1 or Src2 because of the size restrictions.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMulMixedSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+//
+// Dst = Src1 * Src2.
+// Requirement:
+//  - Dst.nDigits >= Src1.nDigits + Src2.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( Dst.nDigits )
+//
+// Note that Dst cannot be the same object as Src1 or Src2 because of the size restrictions.
+//
+
+
+//
+// Division
+// For all division and modulo operations, there are pre-computations that have to be done
+// on the divisor. The pre-computed divisor information is stored in a DIVISOR object.
+// Note that the bitsize of the value of the divisor is published.
+// Therefore, a generic division is not side-channel safe.
+// Rationale: Hiding the bitsize of the value of the divisor is quite expensive,
+// and we have no cryptographic algorithms that require it.
+//
+
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntFromDivisor( _In_ PSYMCRYPT_DIVISOR pdSrc );
+//
+// Returns the INT object inside the DIVISOR object.
+// Digit size of the INT object is equal to the digit size of the DIVISOR object.
+// This object has two uses:
+// - On an uninitialized DIVISOR object it is a suitable place to put a value before calling
+//   SymCryptIntToDivisor.
+// - On an initialized DIVISOR object the function returns a pointer to the INT that contains
+//   the divisor value. Modifying the INT value from an initialized DIVISOR value corrupts
+//   the divisor value.
+//
+// This is typically a very fast function, with a run-time cost that is zero or only one instruction.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( _nDigits )
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntToDivisor(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_DIVISOR   pdDst,
+                                    UINT32              totalOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+//
+// Create a DIVISOR object from an INT.
+// Requirement:
+//  - Dst.nDigits == Src.nDigits
+//  - Src != 0
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( Src.nDigits )
+// SymCryptIntBitsizeOfValue( Src ) is published.
+// Src may be equal to SymCryptIntFromDivisor( Dst ).
+// totalOperations is an estimate of how many divide/modulo operations will be performed with this divisor.
+//   An implementation may use this to decide how much pre-computations to do.
+// flags: any combination of the following flag values:
+//  - SYMCRYPT_FLAG_DATA_PUBLIC
+//      Signals that the Src value is public.
+//      Implementations can use this to use more efficient divisor algorithms depending on the actual value of Src.
+//      For example, if Src is very close to a power of 2, division can be implemented more efficiently.
+//
+// Once a divisor object has been created, it is immutable.
+// Multiple threads can use the same divisor object for different division operations in parallel.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( _nSrcDigits, _nDivisorDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_DIVMOD( _nSrcDigits, _nDivisorDigits )
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntDivMod(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _In_                            PCSYMCRYPT_DIVISOR  pdDivisor,
+    _Out_opt_                       PSYMCRYPT_INT       piQuotient,
+    _Out_opt_                       PSYMCRYPT_INT       piRemainder,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+//
+// Quotient = Src div Divisor
+// Remainder = Src mod Divisor
+// Quotient & Remainder may be NULL in which case that result is not returned.
+// Requirements:
+//  - Quotient.nDigits >= Src.nDigits
+//  - Remainder.nDigits >= Divisor.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( Src.nDigits, Divisor.nDigits )
+// Quotient and Remainder must be different objects.
+// Src may be the same object as either Quotient or Remainder.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_EXTENDED_GCD( _nDigits  )  SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_EXTENDED_GCD( _nDigits )
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntExtendedGcd(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+                                    UINT32          flags,
+    _Out_opt_                       PSYMCRYPT_INT   piGcd,
+    _Out_opt_                       PSYMCRYPT_INT   piLcm,
+    _Out_opt_                       PSYMCRYPT_INT   piInvSrc1ModSrc2,
+    _Out_opt_                       PSYMCRYPT_INT   piInvSrc2ModSrc1,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+//
+// Compute up to four results from Src1 and Src2.
+//   GCD is the greatest common divisor of Src1 and Src2.
+//   LCM is the Least Common Multiple of Src1 and Src2.
+//   InvSrc1ModSrc2 is the smallest value such that (InvSrc1ModSrc2 * Src1) mod Src2= GCD( Src1, Src2 )
+//      UNLESS Src1 is a multiple of Src2, i.e. when Src1 = 0 mod Src2. In this case the result is
+//      undefined.
+//   InvSrc2ModSrc1 is the smallest value such that (InvSrc2ModSrc1 * Src2) mod Src1= GCD( Src1, Src2 )
+//      UNLESS Src2 is a multiple of Src1, i.e. when Src2 = 0 mod Src1. In this case the result is
+//      undefined.
+//
+// The last two modular inverse values are not true modular inverses unless GCD( Src1, Src2 ) = 1.
+//
+// Any of the output pointers can be NULL and then that result is not returned.
+// Requirements:
+//  - Src1 > 0
+//  - Src2 > 0 and  Src2 odd
+//  - Gcd.nDigits >= min( Src1.nDigits, Src2.nDigits )
+//  - Lcm.nDigits >= Src1.nDigits + Src2.nDigits
+//  - InvSrc1ModSrc2.nDigits >= max(Src1.nDigits, Src2.nDigits)     // Future work: Make these bounds Src2 and Src1 respectively.
+//  - InvSrc2ModSrc1.nDigits >= max(Src1.nDigits, Src2.nDigits)
+//  - if piInvSrc2ModSrc1 is not NULL, max( Src1.nDigits, Src2.nDigits ) * 2 <= SymCryptDigitsFromBits(SYMCRYPT_INT_MAX_BITS)
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_EXTENDED_GCD( max( Src1.nDigits, Src2.nDigits ) )
+//
+// If only one inverse value is needed, it is most efficient to use only InvSrc1ModSrc2.
+//
+// The restriction that Src2 must be odd can be removed in a future version.
+// The SYMCRYPT_FLAG_DATA_PUBLIC flag signals that the inputs are public information and do not have
+// to be side-channel protected.
+// The SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN signals that at least one input is odd. This speeds up the
+// side-channel safe implementation; this flag is not needed if the inputs are signaled as public as the code can then
+// afford to check that condition and change use a optimized algorithm.
+// The SYMCRYPT_FLAG_GCD_PUBLIC signals that the GCD value is public. This can make some computations
+// (of the inverses) more efficient when GCD = 1.
+//
+
+#define SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN      (0x02)
+#define SYMCRYPT_FLAG_GCD_PUBLIC                    (0x04)
+
+
+UINT64
+SYMCRYPT_CALL
+SymCryptUint64Gcd( UINT64 a, UINT64 b, UINT32 flags );
+//
+// Return GCD of two 64-bit integers.
+//  a, b : inputs to the GCD
+//  flags:
+//      - SYMCRYPT_FLAG_DATA_PUBLIC signals that a and b are public values (w.r.t. side-channel safety)
+//          This may improve performance.
+//      - SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN: signals that at least one of (a,b) is odd. This
+//          simplifies & speeds up the GCD computation.
+//
+// Note:
+// The current implementation requires that the INPUTS_NOT_BOTH_EVEN flag is set (and at least one input be odd).
+// Also note that GCD(x, 0) == GCD(0, x) == x
+//
+
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_CRT_GENERATION( _nDigits )  SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_CRT_GENERATION( _nDigits )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCrtGenerateInverses(
+                                    UINT32                  nCoprimes,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODULUS *    ppmCoprimes,
+                                    UINT32                  flags,
+    _Out_writes_( nCoprimes )       PSYMCRYPT_MODELEMENT *  ppeCrtInverses,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Compute the Chinese Remainder Theorem (CRT) constants for a set of nCoprimes
+// pairwise coprime moduli. Pointers to the input numbers are stored in the array of
+// pointers ppmCoprimes, while the outputs are stored to the locations pointed by
+// ppeCrtInverses.
+//
+// For input numbers Src1, Src2, ..., SrcK where K = nCoprimes, let N = Src1*Src2*...*SrcK.
+// Then this function outputs the constants:
+//      (( Src1 / N ) mod Src1), (( Src2 / N ) mod Src2), ..., (( SrcK / N ) mod SrcK)
+//
+// The most common case is for the RSA algorithm where the inputs are 2 prime numbers P and Q
+// and only Q^{-1} mod P is needed (i.e. only the first term of the output array).
+//
+// Any of the output pointers in the ppeCrtInverses can be NULL and then that result
+// is not returned (resulting in a faster total running time).
+//
+// The number of inputs nCoprimes and which outputs are returned is public.
+//
+// Requirements:
+//  - nCoprimes >= 2
+//  - Both ppmCoprimes and ppeCrtInverses must be arrays of pointers of exactly nCoprimes pointers.
+//  - ppmCoprimes[i] != NULL for all i in [0, nCoprimes-1].
+//  - The input moduli must be pairwise coprime.
+//  - The number of digits of all input moduli must match the number of digits of the corresponding
+//    output modelements.
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_CRT_GENERATION( nDigits ) where nDigits is the maximum number
+//    of digits of the inputs and outputs.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_CRT_SOLUTION( _nDigits )  SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_CRT_SOLUTION( _nDigits )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCrtSolve(
+                                    UINT32                  nCoprimes,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODULUS *    ppmCoprimes,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODELEMENT * ppeCrtInverses,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODELEMENT * ppeCrtRemainders,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_INT           piSolution,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Solve for x the system of nCoprimes congruences of the form
+//      x = ppeCrtRemainders[0] (mod ppmCoprimes[0])
+//      x = ppeCrtRemainders[1] (mod ppmCoprimes[1])
+//      ...
+//      x = ppeCrtRemainders[nCoprimes-1] (mod ppmCoprimes[nCoprimes-1])
+//
+// The input array ppeCrtInverses must have been pre-computed by a call to SymCryptCrtGenerateInverses.
+//
+// The number of inputs nCoprimes is public.
+//
+// Requirements:
+//  - nCoprimes == 2
+//  - ppmCoprimes, ppeCrtInverses, and ppeCrtRemainders must be arrays of pointers of exactly nCoprimes elements. All
+//    of them non-NULL.
+//  - piSolution must be large enough to hold the result modulo the product of all the coprimes.
+//  - max( ppmCoprimes[0].nDigits, ppmCoprimes[1].nDigits ) * 2 <= SymCryptDigitsFromBits(SYMCRYPT_INT_MAX_BITS)
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_CRT_SOLUTION( nDigits ) where nDigits is the maximum number
+//    of digits of the input moduli.
+//
+
+
+typedef const struct _SYMCRYPT_TRIALDIVISION_CONTEXT *PCSYMCRYPT_TRIALDIVISION_CONTEXT;
+
+PCSYMCRYPT_TRIALDIVISION_CONTEXT
+SYMCRYPT_CALL
+SymCryptCreateTrialDivisionContext( UINT32 nDigits );
+//
+// Create a trial division context that can be used for integers up to and including nDigits digits.
+// The Trial division context can be used in multiple threads in parallel.
+// The context should be freed with SymCryptFreeTrialDivisionContext after use.
+// A context can be fairly large (100 kB) so freeing it is important.
+// Returns NULL if out of memory or an invalid digit count is provided.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptFreeTrialDivisionContext( PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext );
+//
+// Free the trial division context after use.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntFindSmallDivisor(
+    _In_                            PCSYMCRYPT_TRIALDIVISION_CONTEXT    pContext,
+    _In_                            PCSYMCRYPT_INT                      piSrc,
+    _Out_writes_bytes_( cbScratch ) PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch );
+//
+// Returns a divisor of piSrc, or zero.
+// Requirements:
+// Requirement:
+//  - pContext is a valid trial division context, and Context.nDigits >= Src.nDigits
+//  - Src >= 2
+// Note:
+//  - Src is published if this function returns a divisor.
+//
+// There is no guarantee that this function finds small divisors;
+// it is valid for the implementation to always return 0.
+// Any nonzero return value is always >= 2 and an actual divisor of Src.
+// Note: this function might not find 2 as a small divisor.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_IS_PRIME( _nDigits )
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntMillerRabinPrimalityTest(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+                                    UINT32              nBitsSrc,
+                                    UINT32              nIterations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+//
+// Applies the Miller-Rabin prime testing algorithm using nIterations on the integer
+// piSrc.
+//
+// The maximum bitsize of the value of piSrc is equal to nBitsSrc and it is public.
+// The number of iterations nIterations is also public.
+//
+// If the return value is 0, then Src is guaranteed to be a composite value.
+//      In this case, the value of Src is treated as public.
+//
+// If the return value is 0xffffffff, then Src might be prime.
+//      In this case, the value of Src is treated as private except when the
+//      SYMCRYPT_FLAG_DATA_PUBLIC flag is specified.
+//
+//      If the flag SYMCRYPT_FLAG_DATA_PUBLIC is specified the
+//      algorithm leaks the number of trailing zeros of Src-1. The reason for
+//      not having a fully side-channel safe implementation for arbitrary
+//      numbers is that such a function would be prohibitively slow.
+//
+// Requirements:
+//  - SymCryptIntBitsizeOfValue( piSrc ) <= nBitsSrc <= SymCryptIntBitsizeOfObject( piSrc )
+//  - Src is odd and greater than 3.
+//  - If flags == 0 then Src must be 3 modulo 4. (See the comment above for
+//    the SYMCRYPT_FLAG_DATA_PUBLIC flag)
+//  - cbScratch >=  SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( Src.nDigits )
+//
+
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_INT_PRIME_GEN( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_PRIME_GEN( _nDigits )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntGenerateRandomPrime(
+    _In_                            PCSYMCRYPT_INT      piLow,
+    _In_                            PCSYMCRYPT_INT      piHigh,
+    _In_reads_opt_( nPubExp )       PCUINT64            pu64PubExp,
+                                    UINT32              nPubExp,
+                                    UINT32              nTries,
+                                    UINT32              flags,
+    _Inout_                         PSYMCRYPT_INT       piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+//
+// This function generates a random prime Dst such that
+//          Dst == 3 mod 4      and
+//          Low <= Dst < High
+//          for e in PubExp[]: GCD( Dst - 1, e ) == 1
+//
+// (pu64PubExp, nPubExp) can be (NULL, 0) if no pubexp restriction is needed.
+// The nTries parameter specifies the maximum number of candidate numbers
+// until a prime number is found satisfying the above restrictions.
+// If the function cannot find one after nTries, it returns SYMCRYPT_INVALID_ARGUMENT
+// (For example, if the caller passes in a Low bound bigger than the High bound,
+// or if there are no primes between Low and High).
+//
+// The values of the pubexps, piLow and piHigh are public.
+//
+// flags: None
+//
+// Requirements:
+//  - SymCryptIntBitsizeOfValue( piHigh ) <= SymCryptIntBitsizeOfObject(piDst)
+//  - piLow > 3
+//  - Each public exponent must be greater than 0
+//  - 0 <= nPubExp <= SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS
+//  - cbScratch >=  SYMCRYPT_SCRATCH_BYTES_FOR_INT_PRIME_GEN( Dst.nDigits )
+//
+
+//=====================================================
+// Modular arithmetic
+//
+// To perform modular arithmetic the modulus has to be prepared into a Modulus object.
+// Arithmetic in the ring modulo the modulus can then be done using ModElement objects.
+//
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptDivisorFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc );
+//
+// Returns the DIVISOR object inside the MODULUS object.
+//
+// Digit size of the DIVISOR object is equal to the digit size of the MODULUS object.
+// This object has one use:
+// - On an initialized MODULUS object the function returns a pointer to the DIVISOR that contains
+//   the modulus value. Modifying the DIVISOR value from an initialized MODULUS value corrupts
+//   the modulus.
+//
+// This is typically a very fast function, with a run-time cost that is zero or one instruction.
+//
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc );
+//
+// Returns the INT object inside the MODULUS object.
+//
+// Digit size of the INT object is equal to the digit size of the MODULUS object.
+// This object has two uses:
+// - On an uninitialized MODULUS object it is a suitable place to put a value before calling
+//   SymCryptIntToModulus.
+// - On an initialized MODULUS object the function returns a pointer to the INT that contains
+//   the modulus value. Modifying the INT value from an initialized MODULUS value corrupts
+//   the modulus.
+//
+// This is typically a very fast function, with a run-time cost that is zero or one instruction.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_INT_TO_MODULUS( _nDigits )
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntToModulus(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_MODULUS   pmDst,
+                                    UINT32              averageOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+//
+// Create a modulus from an INT.
+// Requirements:
+//  - Src != 0
+//  - Dst.nDigits == Src.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS( Src.nDigits )
+// SymCryptIntBitsizeOfValue( Src ) is published.
+// averageOperations is the average number of multiplications that are performed on a ModElement created with this modulus between the time that the value is
+//      created as a ModElement and the time it is exported out of modElement form.
+//      There are multiple ways of doing modular computations; some of them are faster but have an overhead for converting into and out of modular form.
+//      For example, for RSA verification the # operations is small and conversion overhead should be avoided.
+//      For RSA signatures, the # operations is large and the fastest per-operation form should be used.
+//      This parameter allows the library to select the right kind of modular arithmetic for this modulus.
+// The following flags are supported:
+//  SYMCRYPT_FLAG_DATA_PUBLIC
+//      Signals the code that the Src value is public. This may improve performance because it allows further optimizations that
+//      depend on the value. (For example, if Src is close to a power of 2, the modulo reduction can be made significantly faster.)
+//  SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC
+//      Signals that the parity of Src (whether it is even or odd) may be treated as a public value.
+//      There are some algorithms that can speed up operations on odd moduli, but their use publishes the fact that the modulus is odd.
+//  SYMCRYPT_FLAG_MODULUS_ADDITIVE_ONLY
+//      The modulus will only be used for addition and subtraction, not for multiplication or division.
+//      This can significantly reduce the cost of this function as there is no need to pre-compute the divisor information.
+//  SYMCRYPT_FLAG_MODULUS_PRIME
+//      Signals that the modulus is a prime. Some algorithms can be more efficient for prime moduli. Note that setting this flag
+//      for a non-prime modulus can result in incorrect answers.
+// The flags and averageOperations parameters are published.
+//
+
+#define SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC     (0x02)
+#define SYMCRYPT_FLAG_MODULUS_ADDITIVE_ONLY     (0x04)
+#define SYMCRYPT_FLAG_MODULUS_PRIME             (0x08)
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _nDigits )
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntToModElement(
+    _In_                            PCSYMCRYPT_INT          piSrc,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src mod Mod
+// Requirements:
+//  - Dst.nDigits == Mod.nDigits
+//  - piSrc.nDigits <= 2 * Mod.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+// Note: the input is limited in size to be no more than twice the modulus size (in digits).
+// This should be a rare case, and it simplifies the scratch space handling significantly.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementToInt(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_INT           piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src
+//
+// Requirement:
+//  - Dst.nDigits >= Mod.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+// Convert a ModElement to an Int.
+// The internal format in which a ModElement is stored might be different
+// from the format of an Int; this function converts the value to the INT format.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModElementSetValue(
+    _In_reads_bytes_( cbSrc )       PCBYTE                  pbSrc,
+                                    SIZE_T                  cbSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+                                    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = decode( pbSrc, cbSrc, format ) mod Mod
+// Requirement:
+//  - SymCryptDigitsFromBits( 8 * cbSrc ) <= Mod.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+// This is a separate function as it is frequently used, and does not require the allocation of an INT object.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementSetValueUint32(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = value mod Mod
+// value is published.
+// Requirement:
+//  - value < Mod
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+// Note: this function does NOT hide the value.
+// Rationale: typically the value parameter is known, either 0 or 1.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementSetValueNegUint32(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = -value mod Mod
+// value is published.
+// Requirement:
+//  - 0 < value < Mod
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+// Note: this function does NOT hide the value.
+// Rationale: typically the value parameter is known, either 0 or 1.
+//
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModElementGetValue(
+                                    PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_writes_bytes_( cbDst )     PBYTE                   pbDst,
+                                    SIZE_T                  cbDst,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// (pbDst, cbDst) = encode( format, cbDst, Src )
+// Requirement:
+//  - SymCryptDigitsFromBits( 8 * cbDst ) <= Mod.nDigits
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+// Retrieve the value of a ModElement as an array of bytes
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptModElementIsEqual(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc1,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc2 );
+//
+// Returns a mask value which is 0xffffffff if Src1 = Src2 and 0 otherwise.
+//
+// Both SYMCRYPT_MODELEMENTs should have been created using the modulus pmMod. Otherwise
+// the result is undefined.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptModElementIsZero(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc );
+//
+// Returns a mask value which is 0xffffffff if Src = 0 and 0 otherwise.
+//
+// Useful for quickly checking if a ModElement is 0.
+//
+
+
+//===============================
+// Modular arithmetic.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModSetRandom(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = random value modulus Mod.
+// Requirement:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+// Random value is chosen uniformly from the set of allowed values.
+// By default this function does not return the values 0, 1, or -1 (see below NOTE for small moduli exception)
+// Flags parameter can signal that these special values are allowed.
+// flags parameter is published.
+//
+// Rationale: these values cause problems in many situations, and for all commonly used cryptographic modulo sizes
+// the absence of these values is statistically undetectable even if they are allowed.
+// For completeness of the API, the flags parameter can be used to allow these three values.
+// flags is a bitmask containing a combination of the following bit values:
+//  SYMCRYPT_FLAG_MODRANDOM_ALLOW_ZERO
+//  SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE
+//  SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE
+// Specifying ALLOW_ZERO implies ALLOW_ONE, there is no way to allow 0 and disallow 1.
+//
+// NOTE:
+// For very small moduli (1, 2, and 3), not allowing 0, 1, or -1 by default does not make sense because this would
+// exclude all possible values! Instead the default behavior is to allow -1 for these moduli.
+//  Modulo 1 => return 0 by default
+//  Modulo 2 => return 1 by default
+//      may also return 0 if SYMCRYPT_FLAG_MODRANDOM_ALLOW_ZERO is specified
+//  Modulo 3 => return 2 by default
+//      may also return 1 if SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE is specified, and
+//      may also return 0 or 1 if SYMCRYPT_FLAG_MODRANDOM_ALLOW_ZERO is specified,
+//
+// Callers relying on not having 0, 1, or -1 are required to pass a larger modulus.
+
+#define SYMCRYPT_FLAG_MODRANDOM_ALLOW_ZERO      (0x01)
+#define SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE       (0x02)
+#define SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE  (0x04)
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptModNeg(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = -Src mod Mod
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModAdd(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src1 + Src2 mod Mod
+// Requirement:
+//  - Src1.modulus == Src2.modulus == Mod.
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+// Dst == Src1, Dst == Src2, and Src1 == Src2 are all allowed.
+// Rationale:
+//  scratch space can make the mod-add faster for side-channel safe implementations.
+//  It allows:
+//      Dst = Src1 + Src2;
+//      Tmp = Dst - Mod;
+//      Dst = choose( Dst, Tmp, carry_bits )
+//  And the choose() operation is fast because it does not require carry propagation.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptModSub(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src1 - Src2 mod Mod
+// Requirement:
+//  Same as SymCryptModAdd
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptModMul(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src1 * Src2 mod Mod
+// Requirement:
+//      - Src1.modulus == Src2.modulus == Mod.
+//      - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModSquare(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src1^2 mod Mod
+// Requirement:
+//      - Src.modulus == Mod.
+//      - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptModDivPow2(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+                                    UINT32                  exp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Src / 2^exp mod Mod
+// Requirements:
+//      - Mod is odd.
+//      - Src.modulus == Dst.modulus == Mod.
+//      - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( Mod.nDigits )
+//
+// Remarks:
+//  - The value exp is *** public ***; hence it should be treated as known to the attacker.
+//  - This function may write intermediate values to peDst and read them back, violating the
+//    read-once/write-once rule, so the caller must ensure that the peDst buffer is trusted.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_MODINV( _nDigits )
+
+// SYMCRYPT_FLAG_DATA_PUBLIC signals that the Src element is public and does not have to be protected
+// against side-channel attacks. The public-ness of the Modulus is part of the Modulus object, specified when the
+// modulus value was set.
+// Marking the source value as public has very little effect on performance, but it removes the random blinding used.
+// The main goal of this flag is to allow ECDSA verification without a source of random numbers.
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModInv(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = 1/Src mod Mod.
+//
+// - pmMod: Modulus, must have the SYMCRYPT_FLAG_MODULUS_PRIME and SYMCRYPT_FLAG_DATA_PUBLIC flag set.
+//      Non-prime or non-public moduli are currently not supported.
+// - peSrc: Source value, modulo pmMod
+// - peDst: Destination value, mod element modulo pmMod
+// - flags: SYMCRYPT_FLAG_DATA_PUBLIC signals that peSrc is a public value.
+// - pbScratch/cbScratch: scratch space >= SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( nDigits( pmMod ) )
+//
+// Returns an error if
+//  - GCD( Src, Mod ) != 1
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( _nDigits ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_MODEXP( _nDigits )
+
+VOID
+SYMCRYPT_CALL
+SymCryptModExp(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
+    _In_                            PCSYMCRYPT_INT          piExp,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = Base ^ Exp mod Mod
+// where only the least significant (nBitsExp) bits of the exponent are used.
+//
+// Requirements:
+//  - nBitsExp != 0
+//  - Mod > 1
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( Mod.nDigits )
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_DATA_PUBLIC: If set then the algorithm
+//      is not side-channel safe (For use in RSA encryption - exponentiation
+//      with a public exponent). The default behaviour is side channel safety.
+//
+// Remarks:
+//  - The undefined operation 0^0 will return 1.
+//  - The value nBitsExp is *** public ***; hence it should be treated as known to the attacker.
+//      Examples:
+//          -   nBitsExp = SymCryptIntBitsizeOfObject( piExp ) => This processes all the
+//              bits of the exponent object.
+//          -   nBitsExp = number of bits of modulus ==> This processes the same
+//              number of bits (even leading zeros) as the modulus. In this case
+//              the exponent should have a value with bitsize less or equal to the
+//              bitsize of the modulus.
+//          -   nBitsExp = max(1, SymCryptIntBitsizeOfValue( piExp )) => This processes
+//              the bits of the exponent ignoring the leading zeros. Therefore, this
+//              option leaks the bitsize of the value of the exponent.
+//
+
+// SYMCRYPT_MODMULTIEXP_MAX_NBASES, _NBITSEXP: The maximum number of bases
+// and exponent bits allowed for the multi-exponentiation operation.
+#define SYMCRYPT_MODMULTIEXP_MAX_NBASES         (8)
+#define SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP       (SYMCRYPT_INT_MAX_BITS)
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_MODMULTIEXP( _nDigits, _nBases, _nBitsExp ) SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_MODMULTIEXP( _nDigits, _nBases, _nBitsExp )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModMultiExp(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
+    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
+                                    UINT32                  nBases,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Dst = ( peBaseArray[0]^piExpArray[0] * peBaseArray[1]^piExpArray[1] * ... *
+//         peBaseArray[nBases-1]^piExpArray[nBases-1] ) mod Mod
+// where only the least significant (nBitsExp) bits of the exponents are used.
+//
+// Requirements:
+//  - 1<= nBitsExp <= SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP
+//  - Mod > 1
+//  - 1<= nBases <= SYMCRYPT_MODMULTIEXP_MAX_NBASES
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_MODMULTIEXP( Mod.nDigits, nBases, nBitsExp )
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_DATA_PUBLIC: If set then the algorithm
+//      is not side-channel safe (For use in DSA verification).
+//      The default behaviour is side channel safety.
+//
+
+// =========================================
+// Tools for side-channel safety
+// ========================================
+
+//
+//Side-channel safe lookup table
+//
+
+typedef struct _SYMCRYPT_SCSTABLE {
+    UINT32  groupSize;
+    UINT32  interleaveSize;
+    UINT32  nElements;          // must be multiple of groupSize
+    UINT32  elementSize;        // # bytes in each element, note: limited to UINT32 for efficiency
+    PBYTE   pbTableData;
+    UINT32  cbTableData;
+} SYMCRYPT_SCSTABLE, *PSYMCRYPT_SCSTABLE;
+
+UINT32
+SYMCRYPT_CALL
+SymCryptScsTableInit(
+    _Out_   PSYMCRYPT_SCSTABLE  pScsTable,
+            UINT32              nElements,
+            UINT32              elementSize );
+// Initializes an ScsTable for nElements elements each of elementSize bytes.
+// nElements and elementSize are limited to less than 2^16.
+// Return value is the size of the buffer that the caller needs to provide.
+//
+// Requirements:
+//  -   nElements must be a multiple of groupSize and elementSize must be a
+//      multiple of interleaveSize. Currently all implementations have as
+//      defaults
+//              groupSize = 4
+//              interleaveSize = 8
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableSetBuffer(
+    _Inout_                             PSYMCRYPT_SCSTABLE  pScsTable,
+    _Inout_updates_bytes_( cbBuffer )   PBYTE               pbBuffer,
+                                        UINT32              cbBuffer );
+// Sets the caller-provided buffer on the ScsTable.
+// cbBuffer should be >= the size returned by the SymCryptScsTableInit function
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableStore(
+    _Inout_                     PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _In_reads_bytes_( cbData )  PCBYTE              pbData,
+                                UINT32              cbData );
+// Not side-channel safe; publishes iIndex.
+// cbData must match the elementSize i.e. the size of a single element.
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableLoad(
+    _In_                        PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _Out_writes_bytes_(cbData)  PBYTE               pbData,
+                                UINT32              cbData );
+// Side-channel safe fetching of data; iIndex is kept secret.
+// cbData must match the elementSize i.e. the size of a single element.
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableWipe(
+    _Inout_ PSYMCRYPT_SCSTABLE pScsTable );
+// Wipes the part of the buffer that the table used
+
+// Other Side-channel safety tools
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsRotateBuffer(
+    _Inout_updates_( cbBuffer ) PBYTE   pbBuffer,
+                                SIZE_T  cbBuffer,
+                                SIZE_T  lshift );
+// Rotates buffer left by lshift without revealing lshift
+// through side channels.
+// - pbBuffer/cbBuffer: buffer to rotate
+//   pbBuffer must be aligned to the native integer of the platform (4 or 8 bytes)
+//   cbBuffer must be a power of two >= 32
+// - lshift: # bytes to left rotate the buffer
+//      pbBuffer[0] will get the value pbBuffer[ lshift % cbBuffer ]
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsCopy(
+    _In_reads_( cbDst )     PCBYTE  pbSrc,
+                            SIZE_T  cbSrc,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst );
+// Copy cbSrc bytes of pbSrc into pbDst without revealing cbSrc
+// through side channels.
+//
+// WARNING: pbSrc buffer must be at least cbDst bytes long; not cbSrc!
+//
+// - pbSrc  pointer to buffer to copy data from
+//          This buffer must be at least cbDst bytes long
+// - cbSrc  number of bytes to be copied, must be <= 2^31
+// - pbDst  destination buffer
+// - pbDst  size of the destination buffer, must be <= 2^31
+// Equivalent to:
+//      n = min( cbSrc, cbDst )
+//      pbDst[ 0.. n-1 ] = pbSrc[ 0 .. n - 1 ]
+// cbSrc is protected from side-channels; cbDst is public.
+
+
+//
+// Mask generation functions.
+// All these functions are side-channel safe in all parameters.
+// Naming convention:
+// SymCrypt <MaskType> <Op> <ParameterType>
+// <MaskType> is the type of the function result:
+//      Mask32      UINT32 mask that is 0 or -1
+//      Mask64      UINT64 mask that is 0 or -1
+// <Op> is the boolean operation performed on the parameters
+//      IsZero      v == 0
+//      IsNonzero   v != 0
+//      Eq          a == b
+//      Neq         a != b
+// <ParameterType> is an indication of the parameter type supported.
+//      U31     UINT32 which is limited to values < 2^31
+//              This allows more efficient masking functions.
+//      U32     UINT32
+// Other mask types, operations, and parameter types may be defined in future.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32IsZeroU31( UINT32 v );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32IsNonzeroU31( UINT32 v );
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32EqU32( UINT32 a, UINT32 b );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32NeqU31( UINT32 a, UINT32 b );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32LtU31( UINT32 a, UINT32 b );
+
+
+//
+// Other helper functions
+//
+SIZE_T
+SYMCRYPT_CALL
+SymCryptRoundUpPow2Sizet( SIZE_T v );
+// Round up to the next power of 2
+//
+// Requirements:
+//  v <= (SIZE_T_MAX / 2) + 1
+//    i.e. rounding v up to the next power of 2 fits within SIZE_T, so v is
+//    less than or equal to the maximum power of 2 representable in SIZE_T
+
+
+//=====================================================
+//=====================================================
+// RSA padding operations
+//=====================================================
+//=====================================================
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1ApplyEncryptionPadding(
+    _In_reads_bytes_( cbPlaintext )     PCBYTE      pbPlaintext,
+                                        SIZE_T      cbPlaintext,
+    _Out_writes_bytes_( cbPkcs1Format ) PBYTE       pbPkcs1Format,
+                                        SIZE_T      cbPkcs1Format );
+//
+// Applies the RSA PKCS1 v1.5 encryption padding to the plaintext buffer.
+// - Plaintext      buffer containing plaintext to be encoded
+// - Pkcs1Format    Output buffer, typically the size of the RSA modulus
+// Requirement: cbPkcs1Format >= cbPlaintext + 11 due to the PKCS1 overhead.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1RemoveEncryptionPadding(
+    _Inout_updates_bytes_( cbPkcs1Buffer )  PBYTE       pbPkcs1Format,
+                                            SIZE_T      cbPkcs1Format,
+                                            SIZE_T      cbPkcs1Buffer,
+    _Out_writes_bytes_opt_( cbPlaintext )   PBYTE       pbPlaintext,
+                                            SIZE_T      cbPlaintext,
+    _Out_                                   SIZE_T     *pcbPlaintext );
+//
+// Remove the PKCS1 encryption padding and extract the message plaintext.
+// This function is side-channel safe w.r.t. the data in the Pkcs1Format buffer.
+// - pbPkcs1Format points to a buffer containing the raw RSA decrypted data.
+//      This buffer will be modified by this function.
+// - cbPkcs1Format is the # bytes of the buffer that were decrypted with raw RSA
+// - cbPkcs1Buffer is the size of the buffer that pbPkcs1Format points to
+//      cbPkcs1Buffer must be a power of 2 and >= cbPkcs1Format and >= 32
+//      cbPkcs1Buffer must be <= 2^30
+// - pbPlaintext/cbPlaintext is the output buffer that will receive the data.
+//      if pbPlaintext == NULL no message is output, but *pcbPlaintext is still set.
+// - pcbPlaintext receives the # bytes in the actual decrypted message.
+//      set to 0 if an error occurred.
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP( _hashAlgorithm, _nBytesOAEP )      SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_RSA_OAEP( _hashAlgorithm, _nBytesOAEP )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepApplyEncryptionPadding(
+    _In_reads_bytes_( cbPlaintext )     PCBYTE          pbPlaintext,
+                                        SIZE_T          cbPlaintext,
+    _In_                                PCSYMCRYPT_HASH hashAlgorithm,
+    _In_reads_bytes_( cbLabel )         PCBYTE          pbLabel,
+                                        SIZE_T          cbLabel,
+    _In_reads_bytes_opt_( cbSeed )      PCBYTE          pbSeed,
+                                        SIZE_T          cbSeed,
+    _Out_writes_bytes_( cbOaepFormat )  PBYTE           pbOaepFormat,
+                                        SIZE_T          cbOaepFormat,
+    _Out_writes_bytes_( cbScratch )     PBYTE           pbScratch,
+                                        SIZE_T          cbScratch );
+//
+// Apply the RSA OAEP encryption padding to the plaintext buffer.
+// - Plaintext      Plaintext to be encoded
+// - hashAlgorithm  Hash algorithm to use during padding
+// - Label          Label input for OAEP
+// - Seed           Specified seed value. 0 <= cbSeed < hash size
+// - OaepFormat     Output buffer, typically the size of the RSA modulus
+//
+// Remarks:
+//      - If pbSeed == NULL and cbSeed != 0, then the function picks
+//        a uniformly random seed of size cbSeed bytes.
+//
+// Requirements:
+//      cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP( hashAlgorithm, cbOAEPFormat )
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepRemoveEncryptionPadding(
+    _In_reads_bytes_( cbOAEPFormat )
+                                PCBYTE                      pbOAEPFormat,
+                                SIZE_T                      cbOAEPFormat,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_( cbLabel ) PCBYTE                      pbLabel,
+                                SIZE_T                      cbLabel,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPlaintext )
+                                PBYTE                       pbPlaintext,
+                                SIZE_T                      cbPlaintext,
+    _Out_                       SIZE_T                      *pcbPlaintext,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch );
+//
+// Removes the RSA OAEP encryption padding from the OAEP formatted buffer
+// after it checks the validity of the format.
+//
+// *pcbPlaintext is the number of bytes output. If pbPlaintext == NULL then this
+// is the only output value.
+//
+// Allowed flags:
+//      None
+//
+// Requirements:
+//      cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP( hashAlgorithm, cbOAEPFormat )
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PKCS1( _nBytesPKCS1 )    SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_RSA_PKCS1( _nBytesPKCS1 )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1ApplySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_reads_bytes_( cbHashOid )
+                                PCBYTE                      pbHashOid,
+                                SIZE_T                      cbHashOid,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPKCS1Format )
+                                PBYTE                       pbPKCS1Format,
+                                SIZE_T                      cbPKCS1Format );
+//
+// Applies the RSA PKCS1 v1.5 signature padding to the source buffer, which typically contains the
+// hash of the message.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1VerifySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_reads_( nOIDCount )     PCSYMCRYPT_OID              pHashOIDs,
+    _In_                        SIZE_T                      nOIDCount,
+    _In_reads_bytes_( cbPKCS1Format )
+                                PCBYTE                      pbPKCS1Format,
+                                SIZE_T                      cbPKCS1Format,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch );
+//
+// Verifies that the RSA PKCS1 v1.5 signature padding is valid.
+//
+// It returns SYMCRYPT_NO_ERROR if the verification succeeded or SYMCRYPT_VERIFICATION_FAIL
+// if it failed.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_RSA_PKCS1_OPTIONAL_HASH_OID
+//
+// Requirements:
+//      cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PKCS1( cbPKCS1Format )
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS( _hashAlgorithm, _nBytesMessage, _nBytesPSS )    SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_RSA_PSS( _hashAlgorithm, _nBytesMessage, _nBytesPSS )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssApplySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_opt_( cbSalt )
+                                PCBYTE                      pbSalt,
+    _In_range_(0, cbPSSFormat)  SIZE_T                      cbSalt,
+                                UINT32                      nBitsOfModulus,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPSSFormat )
+                                PBYTE                       pbPSSFormat,
+                                SIZE_T                      cbPSSFormat,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch );
+//
+// Applies the RSA PSS signature padding to the source buffer, which typically contains the
+// hash of the message.
+//
+// Remarks:
+//      - If pbSalt == NULL and cbSalt != 0, then the function picks
+//        a uniformly random salt of size cbSalt bytes.
+//
+// Allowed flags:
+//      None
+//
+// Requirements:
+//      cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS( hashAlgorithm, cbHash, cbPSSFormat )
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssVerifySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_range_(0, cbPSSFormat)  SIZE_T                      cbSalt,
+    _In_reads_bytes_( cbPSSFormat )
+                                PCBYTE                      pbPSSFormat,
+                                SIZE_T                      cbPSSFormat,
+                                UINT32                      nBitsOfModulus,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch );
+//
+// Verifies that the RSA PSS signature padding is valid.
+//
+// It returns SYMCRYPT_NO_ERROR if the verification succeeded or SYMCRYPT_VERIFICATION_FAIL
+// if it failed.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT
+//
+//      When the flag is set, this function will do signature verification using the cbSalt parameter as
+//      a minimum value for the salt length, rather than using it as an exact value. Specifying this and
+//      setting cbSalt = 0 allows callers to verify a signature which has a valid encoding with any salt
+//      length using a single call.
+//
+//
+// Requirements:
+//      cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS( hashAlgorithm, cbHash, cbPSSFormat )
+//
+
+//=====================================================
+//=====================================================
+// EC point operations
+//=====================================================
+//=====================================================
+
+PCSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptEcurveGroupOrder( _In_    PCSYMCRYPT_ECURVE   pCurve );
+//
+// This function returns a pointer to the group order of the curve's subgroup.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveDigitsofScalarMultiplier( _In_    PCSYMCRYPT_ECURVE   pCurve );
+//
+// This function returns the number of digits of a scalar that is big enough to
+// store a multiplier of an elliptic curve point.
+// See also, SymCryptEcurveSizeofScalarMultiplier.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveDigitsofFieldElement( _In_ PCSYMCRYPT_ECURVE pCurve );
+//
+// This function returns the number of digits for one coordinate of the public key.
+//
+
+//=====================================================
+//  GETSET_VALUE_ECURVE_OPERATIONS
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( _pCurve )    SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( _pCurve )
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointSetValue(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_reads_bytes_(cbSrc)         PCBYTE                  pbSrc,
+                                    SIZE_T                  cbSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  nformat,
+                                    SYMCRYPT_ECPOINT_FORMAT eformat,
+    _Out_                           PSYMCRYPT_ECPOINT       poDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Set the value of an ECPOINT object from a source buffer pbSrc of size cbSrc. The buffer
+// will contain the necessary coordinates of the ECPOINT in the format specified by nformat
+// and eformat. The nformat determines the format of the integers in the buffer while the
+// eformat determines the layout (and the number) of the coordinates.
+//
+// Requirements:
+//  - cbSrc = X * SymCryptEcurveSizeofFieldElement( pCurve ) where X depends on the
+//    eformat specified and denotes the number of coordinates. For example, for
+//    SYMCRYPT_ECPOINT_FORMAT_XY it is equal to 2.
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve )
+//
+// Flag values:
+//      SYMCRYPT_FLAG_DATA_PUBLIC   data is public (no side-channel protection needed)
+//
+// Rationale:
+//    Scratch space provides room for conversion of point representations.
+//
+// Example:
+//    Set an ECPOINT to (X,Y) point in affine coordinates where the size of each coordinate
+//    is t = SymCryptEcurveSizeofFieldElement( pCurve ) bytes. The coordinates are
+//    X=(X_(t-1), ... , X_1, X_0) and Y=(Y_(t-1), ... , Y_1, Y_0) with t-1 the
+//    most significant byte. Then the function can be called with
+//      pbSrc = { X_(t-1), ... , X_1, X_0, Y_(t-1), ... , Y_1, Y_0 }
+//      cbSrc = 2 * t
+//      nformat = SYMCRYPT_NUMBER_FORMAT_MSB_FIRST
+//      eformat = SYMCRYPT_ECPOINT_FORMAT_XY
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointGetValue(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_                            PCSYMCRYPT_ECPOINT      poSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  nformat,
+                                    SYMCRYPT_ECPOINT_FORMAT eformat,
+    _Out_writes_bytes_(cbDst)       PBYTE                   pbDst,
+                                    SIZE_T                  cbDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+//
+// Retrieve the value of an ECPOINT object into a destination buffer pbDst of size cbDst. The buffer
+// will contain the necessary coordinates of the ECPOINT in the format specified by nformat
+// and eformat. The nformat determines the format of the integers in the buffer while the
+// eformat determines the layout (and the number) of the coordinates.
+//
+// Flag values:
+//      SYMCRYPT_FLAG_DATA_PUBLIC   data is public (no side-channel protection needed)
+//
+// Remarks:
+//  - If the source point is the "zero" point and it cannot be exported into the
+//    required ECPOINT_FORMAT (XY or X), the function fails with SYMCRYPT_INCOMPATIBLE_FORMAT.
+//
+// Requirements:
+//  - cbDst = X * SymCryptEcurveSizeofFieldElement( pCurve ) where X depends on the
+//    eformat specified and denotes the number of coordinates. For example for SYMCRYPT_ECPOINT_FORMAT_XY it is equal to 2.
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve )
+//
+// Rationale:
+// Scratch space provides room for conversion of point representations.
+//
+
+//
+// Low-level flags for ECC operations
+//
+// SYMCRYPT_FLAG_DATA_PUBLIC: When set, the operation will not be side-channel safe.
+//      It is used to speed up operation on public data. (default: side-channel safe)
+//
+// SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL: When set, the underlying operation will multiply
+//      by the cofactor of the curve. (default: no multiplication by the cofactor)
+//      Remark: **Notice that the default behaviour is the opposite of the higher-level
+//      functions in symcrypt.h.**
+#define SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL          (0x20)
+
+//=====================================================
+//  COMMON_ECURVE_OPERATIONS
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( _pCurve )      SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( _pCurve )
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointSetZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Set the destination point poDst to the zero
+// element of the additive group defined by the
+// elliptic curve addition rule.
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointSetDistinguishedPoint(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Set the destination point poDst to the
+// distinguished point of the curve.
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+#define SYMCRYPT_FLAG_ECPOINT_EQUAL             (0x01)
+#define SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL         (0x02)
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcpointIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// If the flags argument is equal to 0 (default) or SYMCRYPT_FLAG_ECPOINT_EQUAL, it returns a mask value which is
+// 0xffffffff if poSrc1 = poSrc2 and 0 otherwise.
+// If the flags argument is equal to SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL, it returns a mask value which is
+// 0xffffffff if poSrc1 = -poSrc2 and 0 otherwise.
+// If the flags argument is equal to SYMCRYPT_FLAG_ECPOINT_EQUAL | SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL,
+// it returns a mask value which is 0xffffffff if (poSrc1 = poSrc2) or (poSrc1 = -poSrc2) and 0 otherwise.
+//
+// The points should have been created with the same curve pCurve. Otherwise the result is undefined.
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcpointIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Returns a mask value which is 0xffffffff if the point is the zero point of the group.
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcpointOnCurve(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Returns a mask value which is 0xffffffff if the point is on curve.
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointAdd(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Point addition over the curve pCurve.
+//      poDst = poSrc1 + poSrc2
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_DATA_PUBLIC: If set then the algorithm
+//      is not side-channel safe (and faster). The default behaviour
+//      is side-channel safety.
+//
+// Remarks:
+//      - Complete (i.e. works for all points)
+//      - Writes intermediate results to poDst breaking the read-once/write-once rule
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointAddDiffNonZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Point addition when *peSrc1 != +- *peSrc2
+// and none of them is equal to the zero point.
+//
+// Remarks:
+//      - Side-channel safe
+//      - Complete (i.e. works for all points)
+//      - Writes intermediate results to poDst breaking the read-once/write-once rule
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointDouble(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Point doubling.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_DATA_PUBLIC: If set then the algorithm
+//      is not side-channel safe (and faster). The default behaviour
+//      is side-channel safety.
+//
+// Remarks:
+//      - Side-channel safe
+//      - Writes intermediate results to poDst breaking the read-once/write-once rule
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointNegate(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Negates (in place) the source point poSrc if mask == 0xffffffff.
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ).
+//
+
+//=====================================================
+//  SCALAR_ECURVE_OPERATIONS
+//
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( _pCurve )                  SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( (_pCurve), 1 )
+#define SYMCRYPT_SCRATCH_BYTES_FOR_MULTI_SCALAR_ECURVE_OPERATIONS( _pCurve, _nPoints )  SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( (_pCurve), (_nPoints) )
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointSetRandom(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _Out_   PSYMCRYPT_INT           piScalar,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch );
+//
+// Set the destination point poDst to a random non-zero point
+// of the subgroup generated by the distinguished point.
+// The function outputs the integer k and the point kG
+// where k is picked uniformly at random from the set
+// [1, SubgroupOrder-1] ( 0 is excluded).
+//
+// Requirements:
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( pCurve ).
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointScalarMul(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_INT      piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT  poSrc,
+            UINT32              flags,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+//
+// Multiplication of point by scalar.
+//  poDst = piScalar x poSrc
+//
+// If poSrc == NULL the algorithm uses the distinguished point of the curve as source
+// point and it might be faster (depending on the curve optimizations).
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL: If set then
+//      the scalar is multiplied by the cofactor of
+//      the curve. The default behaviour is to not multiply
+//      by the cofactor.
+//
+// Remarks:
+//      - Complete
+//      - Side-channel safe
+//
+// Requirements:
+//  - The piScalar must have SymCryptEcurveDigitsofScalarMultiplier( pCurve ) digits.
+//  - For Non-Montgomery curves, the piScalar must be in the range [0, SubgroupOrder].
+//      - This is the caller's responsibility, it is not checked.
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( pCurve ).
+//
+
+// SYMCRYPT_ECURVE_MULTI_SCALAR_MUL_MAX_NPOINTS: The maximum number of points allowed for the
+// multi-scalar multiplication operation.
+#define SYMCRYPT_ECURVE_MULTI_SCALAR_MUL_MAX_NPOINTS        (2)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointMultiScalarMul(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _In_    PCSYMCRYPT_INT *        piSrcScalarArray,
+    _In_    PCSYMCRYPT_ECPOINT *    poSrcEcpointArray,
+            UINT32                  nPoints,
+            UINT32                  flags,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch );
+//
+// It executes the multi scalar - add operation for nPoints
+// pairs of (exponents, points) in (piSrcScalarArray, poSrcEcpointArray).
+//
+// If poSrcEcpointArray[0] == NULL the algorithm uses the distinguished point of the curve as
+// the first source point and it might be faster (depending on the curve optimizations).
+// Only the first source point can be NULL.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL: If set then
+//      the scalar is multiplied by the cofactor of
+//      the curve. The default behaviour is to not multiply
+//      by the cofactor.
+//      SYMCRYPT_FLAG_DATA_PUBLIC: If set then the algorithm
+//      is not side-channel safe (For use in the ECDSA
+//      verification with public information). The default behaviour
+//      is side channel safe.
+//
+// Requirements:
+//  - 1<= nPoints <= SYMCRYPT_ECURVE_MULTI_SCALAR_MUL_MAX_NPOINTS
+//  - Each piScalar must have SymCryptEcurveDigitsofScalarMultiplier( pCurve ) digits.
+//  - cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_MULTI_SCALAR_ECURVE_OPERATIONS( pCurve, nPoints ).
+//
+
+
+////////////////////////////////////////////////////////////////////////////
+// AES-CTR-DRBG
+//
+
+#define SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE         (32 + 16)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesGenerateSmall(
+    _Inout_                             PSYMCRYPT_RNG_AES_STATE pRngState,
+    _Out_writes_( cbRandom )            PBYTE                   pbRandom,
+                                        SIZE_T                  cbRandom,
+    _In_reads_opt_( cbAdditionalInput ) PCBYTE                  pbAdditionalInput,
+                                        SIZE_T                  cbAdditionalInput );
+//
+// Generate random output from the state per SP 800-90.
+// Callers should almost always use SymCryptRngAesGenerate from symcrypt.h instead.
+//
+// This is the core generation function that produces up to 64 kB at a time
+// This function returns an error code so that we can test the
+// error handling of having done more than 2^48 requests between reseeds,
+// as required by SP 800-90.
+// This is also the Generate function of our SP800-90 compliant implementation.
+// If pRngState->fips140-2Check is true, this function runs the continuous self test
+// required by FIPS 140-2 (but not by FIPS 140-3 as far as we know).
+// pbAdditionalInput is optional.
+//
+
+//=====================================================
+// ECDSA-EX
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaSignEx(
+    _In_                                PCSYMCRYPT_ECKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_opt_                            PCSYMCRYPT_INT          piK,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature );
+//
+// This algorithm is the same as SymCryptEcDsaSign except that the caller can specify
+// a value of k in piK. It is used in verifying test vectors of ECDSA.
+//
+// Requirements:
+//  - If piK is not NULL it must have SymCryptEcurveDigitsofScalarMultiplier( pCurve ) digits, and
+//    must be in range [1, SubgroupOrder-1].
+//  - If piK is not NULL and the generated signature would be 0, SYMCRYPT_INVALID_ARGUMENT is
+//    returned.
+//
+// Allowed flags:
+//      SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION: If set then the hash value will
+//      not be truncated.
+//
+//      SYMCRYPT_FLAG_DATA_PUBLIC: If specified, all inputs, including the private key, are
+//      considered as public information and are not protected against side channel attacks.
+//      This should only be used when signing with a publicly known private key (i.e. in the ECDSA self-test)
+//
+
+//=====================================================
+// ML-KEM-EX
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemEncapsulateEx(
+    _In_                                    PCSYMCRYPT_MLKEMKEY pkMlKemkey,
+    _In_reads_bytes_( cbRandom )            PCBYTE              pbRandom,
+                                            SIZE_T              cbRandom,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE               pbAgreedSecret,
+                                            SIZE_T              cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )      PBYTE               pbCiphertext,
+                                            SIZE_T              cbCiphertext );
+//
+// Performs the Encapsulate operation of ML-KEM using caller-provided random input.
+// It is used in verifying test vectors of ML-KEM.
+//
+// This uses the public information of an ML-KEM keypair to generate an agreed secret
+// and a ciphertext. Only a peer with the private information of an ML-KEM keypair can
+// decapsulate the ciphertext to compute the agreed secret.
+//
+// The arguments are the following:
+// - pkMlKemkey: a key which contains public information required for encapsulation.
+// - (pbRandom, cbRandom): a buffer containing the input random.
+//   Currently cbRandom must be 32 for all parameterizations of ML-KEM.
+// - (pbAgreedSecret, cbAgreedSecret): a buffer into which the generated secret is written.
+//   Currently cbAgreedSecret must be 32 for all parameterizations of ML-KEM.
+// - (pbCiphertext, cbCiphertext): a buffer into which the encapsulated secret is written.
+//   cbCiphertext must equal cbCiphertext given by SymCryptMlKemSizeofCiphertextFromParams,
+//   though typically this value can be known statically (see definition of
+//   SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_*).
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemEncapsulateEx(
+    _In_                                    PCSYMCRYPT_COMPOSITE_MLKEMKEY   pkCompositeMlKemkey,
+    _In_reads_bytes_opt_( cbMlKemRandom )   PCBYTE                          pbMlKemRandom,
+                                            SIZE_T                          cbMlKemRandom,
+    _In_reads_bytes_opt_( cbTradRandom )    PCBYTE                          pbTradRandom,
+                                            SIZE_T                          cbTradRandom,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE                           pbAgreedSecret,
+                                            SIZE_T                          cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )      PBYTE                           pbCiphertext,
+                                            SIZE_T                          cbCiphertext );
+
+//
+// Performs the Encapsulate operation of Composite ML-KEM using caller-provided random input.
+// It is used in verifying test vectors of Composite ML-KEM.
+//
+// This uses the public information of a Composite ML-KEM keypair to generate an agreed secret
+// and a ciphertext. Only a peer with the private information of a Composite ML-KEM keypair can
+// decapsulate the ciphertext to compute the agreed secret.
+//
+// The arguments are the following:
+// - pkCompositeMlKemkey: a key which contains public information required for encapsulation.
+// - (pbMlKemRandom, cbMlKemRandom): a buffer containing the input random for the ML-KEM component.
+//   When pbMlKemRandom is NULL, cbMlKemRandom should be 0, and the function will generate the necessary random input internally.
+//   Currently when pbMlKemRandom is not NULL, cbMlKemRandom must be 32 for all parameterizations of Composite ML-KEM.
+// - (pbTradRandom, cbTradRandom): a buffer containing the input random for the traditional component.
+//   When the traditional portion is an EC key, cbTradRandom must be equal to the private key size of the EC key.
+//   If pbTradRandom is NULL, cbTradRandom should be 0, and the function will generate the necessary random input internally.
+//   Currently, only EC keys are supported for the traditional component.
+// - (pbAgreedSecret, cbAgreedSecret): a buffer into which the generated secret is written.
+//   Currently cbAgreedSecret must be 32 for all parameterizations of Composite ML-KEM.
+// - (pbCiphertext, cbCiphertext): a buffer into which the encapsulated secret is written.
+//   cbCiphertext must equal cbCiphertext given by SymCryptCompositeMlKemSizeofCiphertextFromParams,
+//   though typically this value can be known statically (see definition of
+//   SYMCRYPT_COMPOSITE_MLKEM_CIPHERTEXT_SIZE_*).
+//
+
+//===================================================================
+// 802.11 SAE protocol
+//===================================================================
+//
+// WARNING: These functions are NOT part of the stable SymCrypt API. They are a private
+// interface for the Windows WiFi driver. These functions can change or disappear
+// at any time as we update our WiFi solutions.
+//
+// These functions implement the non-standard or 'custom' parts of the SAE protocol for
+// 802.11 SAE as specified in IEEE 801.11-2016 12.4
+//
+// Parts of the protocol that are easy to implement with conventional crypto functions are
+// not included in this custom part.
+//
+// Limitation: The Hunting-and-Pecking method supports only NIST P256 curve. The Hash-to-Element
+//             method supports curves NIST P256 and NIST P384.
+//
+
+//
+// IANA Group numbers identify the elliptic curve and associated parameters to be used in the SAE method.
+//
+typedef enum _SYMCRYPT_802_11_SAE_GROUP {
+    SYMCRYPT_SAE_GROUP_19 = 19,        // NIST P256
+    SYMCRYPT_SAE_GROUP_20,             // NIST P384
+} SYMCRYPT_802_11_SAE_GROUP;
+
+// The sizes of scalars, elliptic curve points, and HMAC outputs will vary depending on which group is selected.
+// The following macros define the largest possible sizes supported.
+#define SYMCRYPT_SAE_MAX_MOD_SIZE_BITS          384
+#define SYMCRYPT_SAE_MAX_MOD_SIZE_BYTES         SYMCRYPT_BYTES_FROM_BITS( SYMCRYPT_SAE_MAX_MOD_SIZE_BITS )
+#define SYMCRYPT_SAE_MAX_EC_POINT_SIZE_BYTES    ( 2 * SYMCRYPT_SAE_MAX_MOD_SIZE_BYTES )
+#define SYMCRYPT_SAE_MAX_HMAC_OUTPUT_SIZE_BYTES SYMCRYPT_BYTES_FROM_BITS( 384 )
+
+
+typedef struct _SYMCRYPT_802_11_SAE_CUSTOM_STATE SYMCRYPT_802_11_SAE_CUSTOM_STATE, *PSYMCRYPT_802_11_SAE_CUSTOM_STATE;
+typedef const SYMCRYPT_802_11_SAE_CUSTOM_STATE *PCSYMCRYPT_802_11_SAE_CUSTOM_STATE;
+//
+// The struct itself is opaque and is defined elsewhere.
+// Caller may not rely on the internal fields of the structure as they can
+// change at any time.
+//
+
+VOID SymCrypt802_11SaeGetGroupSizes(
+                                SYMCRYPT_802_11_SAE_GROUP       group,
+                    _Out_opt_   SIZE_T*                         pcbScalar,
+                    _Out_opt_   SIZE_T*                         pcbPoint );
+//
+// Helper function that returns the sizes of the field elements and elliptic curve points in bytes
+// for a given IANA group number. Both output parameters are optional.
+//
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomInit(
+    _Out_                       PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+    _In_reads_( 6 )             PCBYTE                              pbMacA,
+    _In_reads_( 6 )             PCBYTE                              pbMacB,
+    _In_reads_( cbPassword )    PCBYTE                              pbPassword,
+                                SIZE_T                              cbPassword,
+    _Out_opt_                   PBYTE                               pbCounter,
+    _Inout_updates_opt_( 32 )   PBYTE                               pbRand,
+    _Inout_updates_opt_( 32 )   PBYTE                               pbMask );
+//
+// Initialize the state object with the MAC addresses and password.
+// All choices for the protocol (i.e. rand and mask) are made at this time.
+//
+// - State                      Protocol state to initialize
+// - pbMacA, pbMacB             Two 6-byte MAC addresses with MacA >= MacB.
+// - pbPassword, cbPassword     The password buffer
+// - pbCounter                  If not NULL, receives the counter value of the
+//                              successful PWE generation per section 12.4.4.2.2
+// - pbRand                     Optional pointer to Rand buffer (see below)
+// - pbMask                     Optional pointer to Mask buffer (see below)
+//
+// The Rand and Mask buffers are optional. If a pointer is not provided then the caller
+// has no access to the corresponding value.
+// For either of these pointers there are three cases:
+//  - If a NULL pointer is provided, the function generates an appropriate value internally,
+//      but does not return it to the caller.
+//  - If a buffer is provided and the buffer is all-zero, the function generates an appropriate
+//      value internally and returns it in the buffer.
+//  - If a buffer is provided and the buffer is nonzero, the value in the buffer is used for
+//      the corresponding protocol parameter without further validation.
+// This last option is useful for testing as it lets the caller specify all the random choices.
+// Rand and Mask buffers are MSByte first.
+//
+// Note: currently this method only supports the NIST P256 curve. If we ever want to support other curves
+// we'll update this function to accept a curve parameter and update the SAL annotations
+// of the other functions.
+//
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCreatePT(
+    _In_reads_( cbSsid )                                    PCBYTE                              pbSsid,
+                                                            SIZE_T                              cbSsid,
+    _In_reads_( cbPassword )                                PCBYTE                              pbPassword,
+                                                            SIZE_T                              cbPassword,
+    _In_reads_opt_( cbPasswordIdentifier )                  PCBYTE                              pbPasswordIdentifier,
+                                                            SIZE_T                              cbPasswordIdentifier,
+    _Out_writes_( 64 )                                      PBYTE                               pbPT );
+//
+// Generate the PT secret element for use with the SAE Hash-to-Element algorithm, as described in
+// section 12.4.4.2.3 of the 802.11 spec ("Hash-to-curve generation of the password element with
+// ECC groups"). The PT value can be "stored until needed to generate a session specific PWE."
+//
+// - pbSsid, cbSsid                              SSID for the connection as a string of bytes
+// - pbPassword, cbPassword                      Password buffer
+// - pbPasswordIdentifier, cbPasswordIdentifier  Optional password identifier, as a string of bytes
+// - pbPT                                        Out pointer to PT (as a byte buffer)
+//
+// This function uses the NIST P256 curve.
+//
+
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCreatePTGeneric(
+                                                            SYMCRYPT_802_11_SAE_GROUP           group,
+    _In_reads_( cbSsid )                                    PCBYTE                              pbSsid,
+                                                            SIZE_T                              cbSsid,
+    _In_reads_( cbPassword )                                PCBYTE                              pbPassword,
+                                                            SIZE_T                              cbPassword,
+    _In_reads_opt_( cbPasswordIdentifier )                  PCBYTE                              pbPasswordIdentifier,
+                                                            SIZE_T                              cbPasswordIdentifier,
+    _Out_writes_( cbPT )                                    PBYTE                               pbPT,
+                                                            SIZE_T                              cbPT );
+//
+// Generic version of the SymCrypt802_11SaeCustomCreatePT() function that allows elliptic curve
+// group selection.
+// Generate the PT secret element for use with the SAE Hash-to-Element algorithm, as described in
+// section 12.4.4.2.3 of the 802.11 spec ("Hash-to-curve generation of the password element with
+// ECC groups"). The PT value can be "stored until needed to generate a session specific PWE."
+//
+// - group                                       Group number for the elliptic curve selection
+// - pbSsid, cbSsid                              SSID for the connection as a string of bytes
+// - pbPassword, cbPassword                      Password buffer
+// - pbPasswordIdentifier, cbPasswordIdentifier  Optional password identifier, as a string of bytes
+// - pbPT, cbPt                                  PT (as a byte buffer)
+//
+
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomInitH2E(
+    _Out_                                                   PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+    _In_reads_( 64 )                                        PCBYTE                              pbPT,
+    _In_reads_( 6 )                                         PCBYTE                              pbMacA,
+    _In_reads_( 6 )                                         PCBYTE                              pbMacB,
+    _Inout_updates_opt_( 32 )                               PBYTE                               pbRand,
+    _Inout_updates_opt_( 32 )                               PBYTE                               pbMask );
+//
+// Initialize the state object  using the Hash-to-Element algorithm, using the PT value calculated
+// by SymCrypt802_11SaeCustomCreatePT.
+//
+// - pState                     Protocol state
+// - pbPT                       PT value calculated using SymCrypt802_11SaeCustomCreatePT()
+// - pbMacA, pbMacB             Two 6-byte MAC addresses
+// - pbRand                     Optional pointer to Rand buffer. See SymCrypt802_11SaeCustomInit() documentation for the use of this parameter.
+// - pbMask                     Optional pointer to Mask buffer. See SymCrypt802_11SaeCustomInit() documentation for the use of this parameter.
+//
+// See the comment on SymCrypt802_11SaeCustomInit() for more details about the pbRand and pbMask
+// parameters.
+//
+
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomInitH2EGeneric(
+    _Out_                           PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+                                    SYMCRYPT_802_11_SAE_GROUP           group,
+    _In_reads_( cbPT )              PCBYTE                              pbPT,
+                                    SIZE_T                              cbPT,
+    _In_reads_( 6 )                 PCBYTE                              pbMacA,
+    _In_reads_( 6 )                 PCBYTE                              pbMacB,
+    _Inout_updates_opt_( cbRand )   PBYTE                               pbRand,
+                                    SIZE_T                              cbRand,
+    _Inout_updates_opt_( cbMask )   PBYTE                               pbMask,
+                                    SIZE_T                              cbMask );
+//
+// Generic version of the SymCrypt802_11SaeCustomInitH2E() function that allows elliptic curve
+// group selection.
+// Initialize the state object  using the Hash-to-Element algorithm, using the PT value calculated
+// by SymCrypt802_11SaeCustomCreatePT.
+//
+// - pState                     Protocol state
+// - group                      Group number for the elliptic curve selection
+// - pbPT, cbPT                 PT value (as a byte array) calculated using SymCrypt802_11SaeCustomCreatePTGeneric().
+//                              PT must be generated on the same elliptic curve as the one supplied in the group parameter.
+// - pbMacA, pbMacB             Two 6-byte MAC addresses
+// - pbRand, cbRand             Optional Rand buffer. See SymCrypt802_11SaeCustomInit() documentation for the use of this parameter.
+// - pbMask, cbMask             Optional Mask buffer. See SymCrypt802_11SaeCustomInit() documentation for the use of this parameter.
+//
+// See the comment on SymCrypt802_11SaeCustomInit() for more details about the pbRand and pbMask
+// parameters.
+//
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitCreate(
+    _In_                    PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _Out_writes_( 32 )      PBYTE                               pbCommitScalar,
+    _Out_writes_( 64 )      PBYTE                               pbCommitElement );
+//
+// Compute the commit-scalar and commit-element values for the Commit message.
+// This function does not update the pState and is multi-thread safe w.r.t. the pState object.
+//
+//  - pState            Protocol state that was initialized with SymCrypt802_11SaeCustomInit().
+//  - pCommitScalar     Buffer that receives the commit-scalar value, MSByte first.
+//  - pCommitElement    Buffer that receives the commit-element value encoded as two values
+//                      (x,y) in order, each value in MSByte first.
+//
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitCreateGeneric(
+    _In_                                PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _Out_writes_( cbCommitScalar )      PBYTE                               pbCommitScalar,
+                                        SIZE_T                              cbCommitScalar,
+    _Out_writes_( cbCommitElement )     PBYTE                               pbCommitElement,
+                                        SIZE_T                              cbCommitElement);
+//
+// Generic version of the SymCrypt802_11SaeCustomCommitCreate() function that uses the
+// state object to determine which elliptic curve group is selected.
+// Compute the commit-scalar and commit-element values for the Commit message.
+// This function does not update the pState and is multi-thread safe w.r.t. the pState object.
+//
+//  - pState                            Protocol state that was initialized with SymCrypt802_11SaeCustomInit().
+//  - pbCommitScalar, cbCommitScalar    Buffer that receives the commit-scalar value, MSByte first.
+//  - pbCommitElement, cbCommitElement  Buffer that receives the commit-element value encoded as two values
+//                                      (x,y) in order, each value in MSByte first.
+//
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitProcess(
+    _In_                    PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _In_reads_( 32 )        PCBYTE                              pbPeerCommitScalar,
+    _In_reads_( 64 )        PCBYTE                              pbPeerCommitElement,
+    _Out_writes_( 32 )      PBYTE                               pbSharedSecret,
+    _Out_writes_( 32 )      PBYTE                               pbScalarSum );
+//
+// Process the commit message received from the peer.
+// This function does not update pState and is multi-thread safe w.r.t. the pState object.
+//
+// - pState                 pointer to the protocol state.
+// - pbPeerCommitScalar     pointer to the peer's commit scalar value, MSByte first.
+// - pbPeerCommitElement    pointer to the peer's commit element, see CommitCreate for format.
+// - pbSharedSecret         buffer that receives the 'k' value that is the shared secret, MSByte first
+// - pbScalarSum            buffer that receives the sum of the two commit scalars, MSByte first
+//
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitProcessGeneric(
+    _In_                                    PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _In_reads_( cbPeerCommitScalar )        PCBYTE                              pbPeerCommitScalar,
+                                            SIZE_T                              cbPeerCommitScalar,
+    _In_reads_( cbPeerCommitElement )       PCBYTE                              pbPeerCommitElement,
+                                            SIZE_T                              cbPeerCommitElement,
+    _Out_writes_( cbSharedSecret )          PBYTE                               pbSharedSecret,
+                                            SIZE_T                              cbSharedSecret,
+    _Out_writes_( cbScalarSum )             PBYTE                               pbScalarSum,
+                                            SIZE_T                              cbScalarSum );
+//
+// Generic version of the SymCrypt802_11SaeCustomCommitProcess() function that uses the
+// state object to determine which elliptic curve group is selected.
+// Process the commit message received from the peer.
+// This function does not update pState and is multi-thread safe w.r.t. the pState object.
+//
+// - pState                                     pointer to the protocol state.
+// - pbPeerCommitScalar, cbPeerCommitScalar     pointer to the peer's commit scalar value, MSByte first.
+// - pbPeerCommitElement, cbPeerCommitElement   pointer to the peer's commit element, see CommitCreate for format.
+// - pbSharedSecret, cbSharedSecret             buffer that receives the 'k' value that is the shared secret, MSByte first
+// - pbScalarSum, pbSharedSecret                buffer that receives the sum of the two commit scalars, MSByte first
+//
+
+
+VOID
+SymCrypt802_11SaeCustomDestroy(
+    _Inout_   PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState );
+//
+// Wipe a state object.
+// After this call the memory used for pState is uninitialized and can be used for other purposes.
+// Note that it is not safe to just wipe the memory of the state object as the state
+// object contains pointers to other allocations, which can contain secret information.
+// The only way to safely destroy a state is to use this function.
+//
+
+//===================================================================
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libs/symcrypt/lib/3des.c b/libs/symcrypt/lib/3des.c
new file mode 100644
index 000000000000..6a4091a90ca0
--- /dev/null
+++ b/libs/symcrypt/lib/3des.c
@@ -0,0 +1,831 @@
+//
+// 3des.c Routines for DES and 3DES
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This is an updated implementation that is carefully reviewed to be fully copyrighted by
+// Microsoft. Our previous implementation was partially based on a very old public domain
+// implementation.
+// According to Andrew Tucker (atucker) there was a claim many years ago about the copyright of
+// our DES code. Working with LCA (legal) it was determined that the DES implementation in RSA32.lib
+// was a Microsoft derivative of a public-domain implementation, and therefore is clear of
+// any IP issues. To avoid any further claims we have now scrubbed this implementation from all
+// copyrightable elements derived from outside sources.
+//
+// We take non-copyrightable items from the old implementations such as
+// - lookup tables
+// - algorithm for various bit permutations
+// - Any variable & function names that are already MS-generated.
+// - Other MS-generated code elements (e.g. SymCrypt integration)
+// Some of the considerations we made are:
+// Most of the functionality of DES is required by the FIPS standard and there is not much
+// choice on how to code it; elements required by the standard are not copyright protected.
+// The lookup tables themselves are not copyrightable as they have no artistic expression.
+// The format of the lookup tables is almost completely determined by the standard and the algorithm
+// used to access them. Any further layout and structure are all standard C conventions.
+// Algorithm tricks such as Hoey's IP implementation are not copyrightable but are patentable.
+// Fortunately all the techniques we use have been around long enough that any patents have expired.
+//
+
+//
+// Feb 2018, Niels Ferguson
+//
+
+#include "precomp.h"
+
+//
+// Tables to describe the DES and 3DES block ciphers so that the generic
+// chaining mode functions can use them.
+// We have no optimized mode-specific code as the DES block is so slow that there is
+// very little to be gained.
+//
+
+const SYMCRYPT_BLOCKCIPHER SymCrypt3DesBlockCipher_default = {
+    SymCrypt3DesExpandKey,  // PSYMCRYPT_BLOCKCIPHER_EXPAND_KEY    expandKeyFunc;
+    SymCrypt3DesEncrypt,    // PSYMCRYPT_BLOCKCIPHER_CRYPT         encryptFunc;
+    SymCrypt3DesDecrypt,    // PSYMCRYPT_BLOCKCIPHER_CRYPT         decryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_MAC_MODE      cbcMacFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    ctrMsbFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmEncryptPartFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmDecryptPartFunc;
+    8,                      // SIZE_T                              blockSize;
+    sizeof( SYMCRYPT_3DES_EXPANDED_KEY ), // SIZE_T  expandedKeySize;    // = sizeof( SYMCRYPT_XXX_EXPANDED_KEY )
+};
+
+const SYMCRYPT_BLOCKCIPHER SymCryptDesBlockCipher_default = {
+    SymCryptDesExpandKey,   // PSYMCRYPT_BLOCKCIPHER_EXPAND_KEY    expandKeyFunc;
+    SymCryptDesEncrypt,     // PSYMCRYPT_BLOCKCIPHER_CRYPT         encryptFunc;
+    SymCryptDesDecrypt,     // PSYMCRYPT_BLOCKCIPHER_CRYPT         decryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_MAC_MODE      cbcMacFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    ctrMsbFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmEncryptPartFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmDecryptPartFunc;
+    8,                      // SIZE_T                              blockSize;
+    sizeof( SYMCRYPT_DES_EXPANDED_KEY ), // SIZE_T  expandedKeySize;    // = sizeof( SYMCRYPT_XXX_EXPANDED_KEY )
+};
+
+const PCSYMCRYPT_BLOCKCIPHER SymCrypt3DesBlockCipher = &SymCrypt3DesBlockCipher_default;
+const PCSYMCRYPT_BLOCKCIPHER SymCryptDesBlockCipher = &SymCryptDesBlockCipher_default;
+
+extern SYMCRYPT_ALIGN_AT(256) const UINT32 SymCryptDesSpbox[8][64];      // Combined S and P tables
+extern SYMCRYPT_ALIGN_AT(256) const UINT32 SymCryptDesKeySelect[8][64];
+
+
+//
+// The SWAP_BITS_WITHIN_UINT32 macro swaps bits within a UINT32 value
+// SWAP_BITS_WITHIN_UINT32( _value, _shift, _mask )
+// swaps each bit in _value selected by _mask with the bit _shift positions to the left
+// Thus it swaps (_value & _mask) with (_value & (_mask << _shift))
+//
+
+#define SWAP_BITS_WITHIN_UINT32( _value, _shift, _mask ) \
+{ \
+    UINT32  _tmp; \
+    _tmp = ((_value) ^ ((_value) >> (_shift))) & (_mask ); \
+    _value = (_value) ^ _tmp ^ (_tmp << (_shift)); \
+}
+
+//
+// The SWAP_BITS_BETWEEN_UINT32 macro swaps bits between two UINT32 values
+// SWAP_BITS_BETWEEN_UINT32( _v1, _v2, _shift, _mask )
+// swaps bits in _v1 selected by _mask with bits in _v2 selected by _mask << _shift
+//
+
+#define SWAP_BITS_BETWEEN_UINT32( _v1, _v2, _shift, _mask ) \
+{ \
+    UINT32  _tmp; \
+    _tmp = ((_v1) ^ ((_v2) >> (_shift))) & (_mask); \
+    _v1 ^= _tmp; \
+    _v2 ^= (_tmp << (_shift )); \
+}
+
+//
+// For each round, a bit that states whether the key schedule shift registers are clocked twice
+// The data is straight from the standard.
+//
+static const BYTE SymCryptDesDoubleShift[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0};
+
+//////////////////////////
+//  DES
+// We just implement DES as 3DES.
+// People using DES have bigger problems than bad performance.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDesExpandKey(
+    _Out_               PSYMCRYPT_DES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_( cbKey ) PCBYTE                      pbKey,
+                        SIZE_T                      cbKey )
+{
+    if( cbKey != 8 )
+    {
+        //
+        // cbKey should be a compile-time constant in most cases,
+        // so this should be optimized away
+        //
+        return SYMCRYPT_WRONG_KEY_SIZE;
+    }
+    return SymCrypt3DesExpandKey( &pExpandedKey->threeDes, pbKey, cbKey );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesEncrypt(
+    _In_                                    PCSYMCRYPT_DES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_DES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_DES_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    SymCrypt3DesEncrypt( &pExpandedKey->threeDes, pbSrc, pbDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesDecrypt(
+    _In_                                    PCSYMCRYPT_DES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_DES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_DES_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    SymCrypt3DesDecrypt( &pExpandedKey->threeDes, pbSrc, pbDst );
+}
+
+//
+// The 3DesCbcEncrypt/Decrypt functions are used to make converting code from
+// older libraries to SymCrypt easier.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesCbcEncrypt(
+    _In_                                        PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _Inout_updates_( SYMCRYPT_3DES_BLOCK_SIZE ) PBYTE                           pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                      PBYTE                           pbDst,
+                                                SIZE_T                          cbData )
+{
+    SYMCRYPT_ASSERT( SymCrypt3DesBlockCipher->blockSize == SYMCRYPT_3DES_BLOCK_SIZE );
+    SymCryptCbcEncrypt( SymCrypt3DesBlockCipher, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesCbcDecrypt(
+    _In_                                        PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _Inout_updates_( SYMCRYPT_3DES_BLOCK_SIZE ) PBYTE                           pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                      PBYTE                           pbDst,
+                                                SIZE_T                          cbData )
+{
+    SYMCRYPT_ASSERT( SymCrypt3DesBlockCipher->blockSize == SYMCRYPT_3DES_BLOCK_SIZE );
+    SymCryptCbcDecrypt( SymCrypt3DesBlockCipher, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesExpandSingleKey(
+        _Out_writes_bytes_(128) UINT32  expandedKeyTable[16][2],
+        _In_reads_(8)           PCBYTE  pKey )
+{
+    UINT32 Cr, Dr;      // The C_r D_r values of FIPS 43 for round value r
+    UINT32 r;           // round
+    UINT32 K1, K2;      // round keys after the permuted choice 2
+    UINT32 tmp;
+
+    //
+    // We follow the FIPS 43 flow quite closely and have not optimized the key expansion much.
+    // Key expansion is not performance-critical.
+    //
+
+    // Load the key
+    Cr = SYMCRYPT_LOAD_LSBFIRST32( pKey );
+    Dr = SYMCRYPT_LOAD_LSBFIRST32( pKey + 4 );
+
+    //
+    // The Permuted Choice 1 can be done mostly with a sequence of bit swaps.
+    // The algorithm we use is derived from our earlier implementation and might potentially
+    // derive from an external source.
+    // But the algorithm cannot be copyrighted, only patented, and if there were any patents
+    // they have expired by now.
+    // The expression of the algorithm in code is purely MS generated, and so not encumbered
+    // by external copyrights.
+    // This algorithm is really just a transposition of the bits when viewed as an 8x8 matrix
+    // with an additional permutation on the output side.
+    //
+    SWAP_BITS_BETWEEN_UINT32( Cr, Dr, 4, 0x0f0f0f0f );
+    SWAP_BITS_WITHIN_UINT32( Dr, 18, 0x00003333 );
+    SWAP_BITS_WITHIN_UINT32( Cr, 18, 0x00003333 );
+    SWAP_BITS_BETWEEN_UINT32( Cr, Dr, 1, 0x55555555 );
+    SWAP_BITS_BETWEEN_UINT32( Dr, Cr, 8, 0x00ff00ff );
+    SWAP_BITS_BETWEEN_UINT32( Cr, Dr, 1, 0x55555555 );
+    SWAP_BITS_WITHIN_UINT32( Dr, 16, 0xff );
+
+    // Have to re-arrange C and D a tiny bit so that each contains 28 bits and we throw away 8 bits
+    Dr = (Dr & 0x00ffffff) | ((Cr & 0xf0000000 ) >> 4 );
+    Cr = (Cr & 0x0fffffff);
+
+    for( r = 0; r < 16; r++)
+    {
+        //
+        // Cr and Dr are the two key shift registers, they are rotated once or twice for each round.
+        //
+
+        if( SymCryptDesDoubleShift[ r ] ) {
+            Cr = ((Cr >> 2) | (Cr << 26));
+            Dr = ((Dr >> 2) | (Dr << 26));
+        } else {
+            Cr = ((Cr >> 1) | (Cr << 27));
+            Dr = ((Dr >> 1) | (Dr << 27));
+        }
+
+        Cr &= 0x0fffffff;
+        Dr &= 0x0fffffff;
+
+        //
+        // The Permuted Choice 2 is done using table lookups
+        // Not all bits of C and D are used, so we cut those out using shifts and masks,
+        // and then index 6 bits at a time into lookup tables that implement the bit relocation.
+        //
+
+        K1 = SymCryptDesKeySelect[0][ (Cr      )&0x3f                     ] |
+             SymCryptDesKeySelect[1][((Cr >>  6)&0x03) | ((Cr >>  7)&0x3c)] |
+             SymCryptDesKeySelect[2][((Cr >> 13)&0x0f) | ((Cr >> 14)&0x30)] |
+             SymCryptDesKeySelect[3][((Cr >> 20)&0x01) | ((Cr >> 21)&0x06) | ((Cr >> 22)&0x38)];
+
+        K2 = SymCryptDesKeySelect[4][ (Dr      )&0x3f                     ] |
+             SymCryptDesKeySelect[5][((Dr >>  7)&0x03) | ((Dr >>  8)&0x3c)] |
+             SymCryptDesKeySelect[6][ (Dr >> 15)&0x3f                     ] |
+             SymCryptDesKeySelect[7][((Dr >> 21)&0x0f) | ((Dr >> 22)&0x30)];
+
+        //
+        // After this we still have to swap the halves of K1 and K2, that is done below
+        // as part of the formatting of the round key
+        //
+
+        //
+        // So far we have recreated the round keys per the standard.
+        // The round keys are stored rotated by 2 as the encrypt/decrypt code finds that easier.
+        // We could update the tables to do this, but key expansion is not used that frequently,
+        // and it is not worth the effort to update the tables.
+        //
+        // We don't worry about extraneous bits in unused positions as the F function masks out unused bits.
+        //
+
+        tmp = ((K2 << 16) | (K1 & 0x0000ffff)) ;
+        expandedKeyTable[r][0] = ROL32(tmp, 2);
+
+        tmp = ((K1 >> 16) | (K2 & 0xffff0000));
+        expandedKeyTable[r][1] = ROL32(tmp, 6);
+    }
+}
+
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCrypt3DesExpandKey(
+    _Out_               PSYMCRYPT_3DES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey )
+{
+    SIZE_T keyIndex = 0;
+    int i;
+
+    if( cbKey != 8 && cbKey != 16 && cbKey != 24 )
+    {
+        return SYMCRYPT_WRONG_KEY_SIZE;
+    }
+
+    //
+    // A loop that goes over the provided key as a circular buffer provides
+    // the right result with the least complexity.
+    // This is inefficient for the cases cbKey=8 and cbKey=16, but those should
+    // not be used anyway.
+    //
+    for( i=0; i<3; i++ )
+    {
+        SYMCRYPT_ASSERT( keyIndex <= cbKey - 8 );       // help PreFast
+        SymCryptDesExpandSingleKey( pExpandedKey->roundKey[i], pbKey + keyIndex );
+        keyIndex = (keyIndex + 8) % cbKey;
+    }
+
+    SYMCRYPT_SET_MAGIC( pExpandedKey );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+//
+// The DES round function
+// This is straight from the standard.
+// Ta and Tb each contain 4 sets of 6 bits that are S-box inputs
+// We interleave the use of Ta and Tb to provide better CPU scheduling on weak compilers.
+// We ensure that the input bits appear in bits 2-7 of the index to avoid a scaled index
+// which can be slower on some CPUs.
+//
+#define F(L, R, keyptr) { \
+    Ta = keyptr[0] ^ R; \
+    Tb = keyptr[1] ^ R; \
+    Tb = ROR32(Tb, 4); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[0] + ( Ta     & 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[1] + ( Tb     & 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[2] + ((Ta>> 8)& 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[3] + ((Tb>> 8)& 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[4] + ((Ta>>16)& 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[5] + ((Tb>>16)& 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[6] + ((Ta>>24)& 0xfc)); \
+    L ^= *(UINT32 *)((PBYTE)SymCryptDesSpbox[7] + ((Tb>>24)& 0xfc)); }
+
+
+
+//
+// Block encryption.
+// The noinline stops the compiler from inlining the code and creating additional
+// implementations which would require separate FIPS selftests.
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesEncrypt(
+    _In_                                        PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_3DES_BLOCK_SIZE )      PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_3DES_BLOCK_SIZE )    PBYTE                           pbDst )
+{
+    UINT32   L, R, Ta, Tb;
+    int r;
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    R = SYMCRYPT_LOAD_LSBFIRST32( pbSrc );
+    L = SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 4 );
+
+    //
+    // Hoey's wonderful initial permutation algorithm, from Outerbridge
+    // (see Schneier p 478)
+    //
+    // The algorithm we use is derived (through several intermediate forms) from the mentioned source.
+    // But the algorithm cannot be copyrighted, only patented, and if there were any patents
+    // they have expired by now.
+    // The expression of the algorithm in code is purely MS generated,
+    // within the confines of implementing the algorithm in the best way such that even a simple
+    // compiler will create good code.
+    //
+
+    R = ROL32(R, 4);
+    Ta = (L ^ R) & 0xf0f0f0f0;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROL32(L, 20);
+    Ta = (L ^ R) & 0xfff0000f;
+    R ^= Ta;
+    L ^= Ta;
+
+    L = ROL32(L,14);
+    Ta = (L ^ R) & 0x33333333;
+    R ^= Ta;
+    L ^= Ta;
+
+    R = ROL32(R, 22);
+    Ta = (L ^ R) & 0x03fc03fc;
+    R ^= Ta;
+    L ^= Ta;
+
+    R = ROL32(R, 9);
+    Ta = (L ^ R) & 0xaaaaaaaa;
+    R ^= Ta;
+    L ^= Ta;
+
+    L = ROL32(L, 1);
+
+    //
+    // First: encryption
+    //
+    for( r=0; r<16; r += 2 )
+    {
+        F( L, R, pExpandedKey->roundKey[0][r  ] );
+        F( R, L, pExpandedKey->roundKey[0][r+1] );
+    }
+
+    //
+    // Second: decryption
+    // Note that L and R are swapped here, and the round counter counts down.
+    //
+    for( r=14; r>=0; r -= 2 )
+    {
+        F( R, L, pExpandedKey->roundKey[1][r+1] );
+        F( L, R, pExpandedKey->roundKey[1][r  ] );
+    }
+
+    //
+    // Third: encryption
+    //
+    for( r=0; r<16; r += 2 )
+    {
+        F( L, R, pExpandedKey->roundKey[2][r  ] );
+        F( R, L, pExpandedKey->roundKey[2][r+1] );
+    }
+
+    R = ROR32(R, 1);
+    Ta = (L ^ R) & 0xaaaaaaaa;
+    R ^= Ta;
+    L ^= Ta;
+
+    L = ROR32(L, 9);
+    Ta = (L ^ R) & 0x03fc03fc;
+    R ^= Ta;
+    L ^= Ta;
+
+    L = ROR32(L, 22);
+    Ta = (L ^ R) & 0x33333333;
+    R ^= Ta;
+    L ^= Ta;
+
+    R = ROR32(R, 14);
+    Ta = (L ^ R) & 0xfff0000f;
+    R ^= Ta;
+    L ^= Ta;
+
+    R = ROR32(R, 20);
+    Ta = (L ^ R) & 0xf0f0f0f0;
+    R ^= Ta;
+    L ^= Ta;
+
+    L = ROR32(L, 4);
+
+    SYMCRYPT_STORE_LSBFIRST32( pbDst, L );
+    SYMCRYPT_STORE_LSBFIRST32( pbDst + 4, R );
+}
+
+
+//
+// Block decrypt
+// The noinline stops the compiler from inlining the code and creating additional
+// implementations which would require separate FIPS selftests.
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesDecrypt(
+    _In_                                        PCSYMCRYPT_3DES_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_3DES_BLOCK_SIZE )      PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_3DES_BLOCK_SIZE )    PBYTE                           pbDst )
+{
+    UINT32   L, R, Ta, Tb;
+    int r;
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    R = SYMCRYPT_LOAD_LSBFIRST32( pbSrc );
+    L = SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 4 );
+
+    R = ROL32(R, 4);
+    Ta = (L ^ R) & 0xf0f0f0f0;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROL32(L, 20);
+    Ta = (L ^ R) & 0xfff0000f;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROL32(L, 14);
+    Ta = (L ^ R) & 0x33333333;
+    L ^= Ta;
+    R ^= Ta;
+
+    R = ROL32(R, 22);
+    Ta = (L ^ R) & 0x03fc03fc;
+    L ^= Ta;
+    R ^= Ta;
+
+    R = ROL32(R, 9);
+    Ta = (L ^ R) & 0xaaaaaaaa;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROL32(L, 1);
+
+
+    // Decrypt with key 2
+    for( r=14; r>=0; r -= 2 )
+    {
+        F( L, R, pExpandedKey->roundKey[2][r+1] );
+        F( R, L, pExpandedKey->roundKey[2][r  ] );
+    }
+
+    // Encrypt with key 1
+    for( r=0; r<16; r += 2 )
+    {
+        F( R, L, pExpandedKey->roundKey[1][r  ] );
+        F( L, R, pExpandedKey->roundKey[1][r+1] );
+    }
+
+    // Decrypt with key 0
+    for( r=14; r>=0; r -= 2 )
+    {
+        F( L, R, pExpandedKey->roundKey[0][r+1] );
+        F( R, L, pExpandedKey->roundKey[0][r  ] );
+    }
+
+    /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
+
+    R = ROR32(R, 1);
+    Ta = (L ^ R) & 0xaaaaaaaa;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROR32(L, 9);
+    Ta = (L ^ R) & 0x03fc03fc;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROR32(L, 22);
+    Ta = (L ^ R) & 0x33333333;
+    L ^= Ta;
+    R ^= Ta;
+
+    R = ROR32(R, 14);
+    Ta = (L ^ R) & 0xfff0000f;
+    L ^= Ta;
+    R ^= Ta;
+
+    R = ROR32(R, 20);
+    Ta = (L ^ R) & 0xf0f0f0f0;
+    L ^= Ta;
+    R ^= Ta;
+
+    L = ROR32(L, 4);
+
+    SYMCRYPT_STORE_LSBFIRST32( pbDst, L );
+    SYMCRYPT_STORE_LSBFIRST32( pbDst + 4, R );
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesSetOddParity(
+    _Inout_updates_( cbData )   PBYTE   pbData,
+    _In_                        SIZE_T  cbData )
+//
+// For each byte, set bit 0 such that the byte parity is odd.
+// This function is side-channel safe
+//
+{
+    SIZE_T i;
+    BYTE b, t;
+    for( i=0; i<cbData; i++ )
+    {
+        // We obey the read-once write-once rule
+        b = *pbData;
+
+        t = b ^ (b>>4);     // parity(b) = parity( t & 0xf )
+        t ^= t>>2;          //           = parity( t & 0x3 )
+        t ^= t>>1;          //           = parity( t & 0x1 )
+        *pbData++ = b ^ (t&1) ^ 1;
+    }
+}
+
+
+//
+// Test vectors for self test
+//
+static const BYTE SP800_67Key[24] = {
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
+    0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01,
+    0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
+};
+
+static const BYTE des3KnownPlaintext[8] = {
+    0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74,
+};
+
+static const BYTE des3KnownCiphertext[8] = {
+    0x31, 0x4F, 0x83, 0x27, 0xFA, 0x7A, 0x09, 0xA8,
+};
+
+static const BYTE desKnownCiphertext[8] = {
+    0x3F, 0xA4, 0x0E, 0x8A, 0x98, 0x4D, 0x48, 0x15,
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesSelftest(void)
+{
+    BYTE                        buf[SYMCRYPT_DES_BLOCK_SIZE];
+    SYMCRYPT_DES_EXPANDED_KEY   key;
+
+    if( SymCryptDesExpandKey( &key, SP800_67Key, 8 ) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'desa' );
+    }
+
+    SymCryptDesEncrypt( &key, des3KnownPlaintext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_DES_BLOCK_SIZE );
+
+    if( memcmp( buf, desKnownCiphertext, SYMCRYPT_DES_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'desb' );
+    }
+
+    SymCryptDesDecrypt( &key, desKnownCiphertext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_DES_BLOCK_SIZE );
+
+    if( memcmp( buf, des3KnownPlaintext, SYMCRYPT_DES_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'desc' );
+    }
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCrypt3DesSelftest(void)
+{
+    BYTE                        buf[SYMCRYPT_3DES_BLOCK_SIZE];
+    SYMCRYPT_3DES_EXPANDED_KEY  key;
+
+    if( SymCrypt3DesExpandKey( &key, SP800_67Key, 24 ) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'des3' );
+    }
+
+    SymCrypt3DesEncrypt( &key, des3KnownPlaintext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_3DES_BLOCK_SIZE );
+
+    if( memcmp( buf, des3KnownCiphertext, SYMCRYPT_3DES_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'des4' );
+    }
+
+    SymCrypt3DesDecrypt( &key, des3KnownCiphertext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_3DES_BLOCK_SIZE );
+
+    if( memcmp( buf, des3KnownPlaintext, SYMCRYPT_3DES_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'des5' );
+    }
+}
+
+
+
+
+
+#if 0
+////////////////////////////////////////////
+// Useful tables, kept for future reference.
+//
+
+// Tables defined in the Data Encryption Standard documents
+// Three of these tables, the initial permutation, the final
+// permutation and the expansion operator, are regular enough that
+// for speed, we hard-code them. They're here for reference only.
+// Also, the S and P boxes are used by a separate program, gensp.c,
+// to build the combined SP box, Spbox[]. They're also here just
+// for reference.
+//
+// initial permutation IP
+static unsigned BYTE ip[] = {
+    58, 50, 42, 34, 26, 18, 10,  2,
+    60, 52, 44, 36, 28, 20, 12,  4,
+    62, 54, 46, 38, 30, 22, 14,  6,
+    64, 56, 48, 40, 32, 24, 16,  8,
+    57, 49, 41, 33, 25, 17,  9,  1,
+    59, 51, 43, 35, 27, 19, 11,  3,
+    61, 53, 45, 37, 29, 21, 13,  5,
+    63, 55, 47, 39, 31, 23, 15,  7
+};
+
+// final permutation IP^-1
+static unsigned BYTE fp[] = {
+    40,  8, 48, 16, 56, 24, 64, 32,
+    39,  7, 47, 15, 55, 23, 63, 31,
+    38,  6, 46, 14, 54, 22, 62, 30,
+    37,  5, 45, 13, 53, 21, 61, 29,
+    36,  4, 44, 12, 52, 20, 60, 28,
+    35,  3, 43, 11, 51, 19, 59, 27,
+    34,  2, 42, 10, 50, 18, 58, 26,
+    33,  1, 41,  9, 49, 17, 57, 25
+};
+
+// expansion operation matrix
+static unsigned BYTE ei[] = {
+    32,  1,  2,  3,  4,  5,
+     4,  5,  6,  7,  8,  9,
+     8,  9, 10, 11, 12, 13,
+    12, 13, 14, 15, 16, 17,
+    16, 17, 18, 19, 20, 21,
+    20, 21, 22, 23, 24, 25,
+    24, 25, 26, 27, 28, 29,
+    28, 29, 30, 31, 32,  1
+};
+
+// The (in)famous S-boxes
+static unsigned BYTE sbox[8][64] = {
+    // S1
+    14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
+     0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
+     4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
+    15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13,
+
+    // S2
+    15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
+     3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
+     0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
+    13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9,
+
+    // S3
+    10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
+    13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
+    13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
+     1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12,
+
+    // S4
+     7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
+    13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
+    10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
+     3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14,
+
+    // S5
+     2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
+    14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
+     4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
+    11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3,
+
+    // S6
+    12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
+    10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
+     9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
+     4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13,
+
+    // S7
+     4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
+    13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
+     1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
+     6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12,
+
+    // S8
+    13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
+     1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
+     7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
+     2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
+};
+
+// 32-bit permutation function P used on the output of the S-boxes
+static unsigned BYTE p32i[] = {
+    16,  7, 20, 21,
+    29, 12, 28, 17,
+     1, 15, 23, 26,
+     5, 18, 31, 10,
+     2,  8, 24, 14,
+    32, 27,  3,  9,
+    19, 13, 30,  6,
+    22, 11,  4, 25
+};
+
+// permuted choice table (key)
+static unsigned BYTE pc1[] = {
+    57, 49, 41, 33, 25, 17,  9,
+     1, 58, 50, 42, 34, 26, 18,
+    10,  2, 59, 51, 43, 35, 27,
+    19, 11,  3, 60, 52, 44, 36,
+
+    63, 55, 47, 39, 31, 23, 15,
+     7, 62, 54, 46, 38, 30, 22,
+    14,  6, 61, 53, 45, 37, 29,
+    21, 13,  5, 28, 20, 12,  4
+};
+
+// number left rotations of pc1
+static unsigned BYTE totrot[] = {
+    1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
+};
+
+// permuted choice key (table)
+static unsigned BYTE pc2[] = {
+    14, 17, 11, 24,  1,  5,
+     3, 28, 15,  6, 21, 10,
+    23, 19, 12,  4, 26,  8,
+    16,  7, 27, 20, 13,  2,
+    41, 52, 31, 37, 47, 55,
+    30, 40, 51, 45, 33, 48,
+    44, 49, 39, 56, 34, 53,
+    46, 42, 50, 36, 29, 32
+};
+
+#endif
diff --git a/libs/symcrypt/lib/AesTables.c b/libs/symcrypt/lib/AesTables.c
new file mode 100644
index 000000000000..ceda0c8a342a
--- /dev/null
+++ b/libs/symcrypt/lib/AesTables.c
@@ -0,0 +1,899 @@
+//
+// AesTables.c  lookup tables for the AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+// We put these in a separate source file to keep the code file uncluttered.
+//
+
+#include "precomp.h"
+
+//
+// Alignments are chosen to reduce side-channel attacks through the TLB cache.
+// We align each table to a multiple of the size within which we do data-dependent
+// lookups. For example, the table below is aligned to 1024. It is not a secret
+// that the 4 sub-tables are accessed, but which value inside each sub-table is a secret.
+// Aligning to 1024 still leaves the data cache line leakage, but avoids any TLB-related leakage.
+//
+SYMCRYPT_ALIGN_AT(1024) const BYTE SymCryptAesSboxMatrixMult[4][256][4] = {{   // Main encryption tables
+0xc6,0x63,0x63,0xa5, 0xf8,0x7c,0x7c,0x84, 0xee,0x77,0x77,0x99, 0xf6,0x7b,0x7b,0x8d,
+0xff,0xf2,0xf2,0x0d, 0xd6,0x6b,0x6b,0xbd, 0xde,0x6f,0x6f,0xb1, 0x91,0xc5,0xc5,0x54,
+0x60,0x30,0x30,0x50, 0x02,0x01,0x01,0x03, 0xce,0x67,0x67,0xa9, 0x56,0x2b,0x2b,0x7d,
+0xe7,0xfe,0xfe,0x19, 0xb5,0xd7,0xd7,0x62, 0x4d,0xab,0xab,0xe6, 0xec,0x76,0x76,0x9a,
+0x8f,0xca,0xca,0x45, 0x1f,0x82,0x82,0x9d, 0x89,0xc9,0xc9,0x40, 0xfa,0x7d,0x7d,0x87,
+0xef,0xfa,0xfa,0x15, 0xb2,0x59,0x59,0xeb, 0x8e,0x47,0x47,0xc9, 0xfb,0xf0,0xf0,0x0b,
+0x41,0xad,0xad,0xec, 0xb3,0xd4,0xd4,0x67, 0x5f,0xa2,0xa2,0xfd, 0x45,0xaf,0xaf,0xea,
+0x23,0x9c,0x9c,0xbf, 0x53,0xa4,0xa4,0xf7, 0xe4,0x72,0x72,0x96, 0x9b,0xc0,0xc0,0x5b,
+0x75,0xb7,0xb7,0xc2, 0xe1,0xfd,0xfd,0x1c, 0x3d,0x93,0x93,0xae, 0x4c,0x26,0x26,0x6a,
+0x6c,0x36,0x36,0x5a, 0x7e,0x3f,0x3f,0x41, 0xf5,0xf7,0xf7,0x02, 0x83,0xcc,0xcc,0x4f,
+0x68,0x34,0x34,0x5c, 0x51,0xa5,0xa5,0xf4, 0xd1,0xe5,0xe5,0x34, 0xf9,0xf1,0xf1,0x08,
+0xe2,0x71,0x71,0x93, 0xab,0xd8,0xd8,0x73, 0x62,0x31,0x31,0x53, 0x2a,0x15,0x15,0x3f,
+0x08,0x04,0x04,0x0c, 0x95,0xc7,0xc7,0x52, 0x46,0x23,0x23,0x65, 0x9d,0xc3,0xc3,0x5e,
+0x30,0x18,0x18,0x28, 0x37,0x96,0x96,0xa1, 0x0a,0x05,0x05,0x0f, 0x2f,0x9a,0x9a,0xb5,
+0x0e,0x07,0x07,0x09, 0x24,0x12,0x12,0x36, 0x1b,0x80,0x80,0x9b, 0xdf,0xe2,0xe2,0x3d,
+0xcd,0xeb,0xeb,0x26, 0x4e,0x27,0x27,0x69, 0x7f,0xb2,0xb2,0xcd, 0xea,0x75,0x75,0x9f,
+0x12,0x09,0x09,0x1b, 0x1d,0x83,0x83,0x9e, 0x58,0x2c,0x2c,0x74, 0x34,0x1a,0x1a,0x2e,
+0x36,0x1b,0x1b,0x2d, 0xdc,0x6e,0x6e,0xb2, 0xb4,0x5a,0x5a,0xee, 0x5b,0xa0,0xa0,0xfb,
+0xa4,0x52,0x52,0xf6, 0x76,0x3b,0x3b,0x4d, 0xb7,0xd6,0xd6,0x61, 0x7d,0xb3,0xb3,0xce,
+0x52,0x29,0x29,0x7b, 0xdd,0xe3,0xe3,0x3e, 0x5e,0x2f,0x2f,0x71, 0x13,0x84,0x84,0x97,
+0xa6,0x53,0x53,0xf5, 0xb9,0xd1,0xd1,0x68, 0x00,0x00,0x00,0x00, 0xc1,0xed,0xed,0x2c,
+0x40,0x20,0x20,0x60, 0xe3,0xfc,0xfc,0x1f, 0x79,0xb1,0xb1,0xc8, 0xb6,0x5b,0x5b,0xed,
+0xd4,0x6a,0x6a,0xbe, 0x8d,0xcb,0xcb,0x46, 0x67,0xbe,0xbe,0xd9, 0x72,0x39,0x39,0x4b,
+0x94,0x4a,0x4a,0xde, 0x98,0x4c,0x4c,0xd4, 0xb0,0x58,0x58,0xe8, 0x85,0xcf,0xcf,0x4a,
+0xbb,0xd0,0xd0,0x6b, 0xc5,0xef,0xef,0x2a, 0x4f,0xaa,0xaa,0xe5, 0xed,0xfb,0xfb,0x16,
+0x86,0x43,0x43,0xc5, 0x9a,0x4d,0x4d,0xd7, 0x66,0x33,0x33,0x55, 0x11,0x85,0x85,0x94,
+0x8a,0x45,0x45,0xcf, 0xe9,0xf9,0xf9,0x10, 0x04,0x02,0x02,0x06, 0xfe,0x7f,0x7f,0x81,
+0xa0,0x50,0x50,0xf0, 0x78,0x3c,0x3c,0x44, 0x25,0x9f,0x9f,0xba, 0x4b,0xa8,0xa8,0xe3,
+0xa2,0x51,0x51,0xf3, 0x5d,0xa3,0xa3,0xfe, 0x80,0x40,0x40,0xc0, 0x05,0x8f,0x8f,0x8a,
+0x3f,0x92,0x92,0xad, 0x21,0x9d,0x9d,0xbc, 0x70,0x38,0x38,0x48, 0xf1,0xf5,0xf5,0x04,
+0x63,0xbc,0xbc,0xdf, 0x77,0xb6,0xb6,0xc1, 0xaf,0xda,0xda,0x75, 0x42,0x21,0x21,0x63,
+0x20,0x10,0x10,0x30, 0xe5,0xff,0xff,0x1a, 0xfd,0xf3,0xf3,0x0e, 0xbf,0xd2,0xd2,0x6d,
+0x81,0xcd,0xcd,0x4c, 0x18,0x0c,0x0c,0x14, 0x26,0x13,0x13,0x35, 0xc3,0xec,0xec,0x2f,
+0xbe,0x5f,0x5f,0xe1, 0x35,0x97,0x97,0xa2, 0x88,0x44,0x44,0xcc, 0x2e,0x17,0x17,0x39,
+0x93,0xc4,0xc4,0x57, 0x55,0xa7,0xa7,0xf2, 0xfc,0x7e,0x7e,0x82, 0x7a,0x3d,0x3d,0x47,
+0xc8,0x64,0x64,0xac, 0xba,0x5d,0x5d,0xe7, 0x32,0x19,0x19,0x2b, 0xe6,0x73,0x73,0x95,
+0xc0,0x60,0x60,0xa0, 0x19,0x81,0x81,0x98, 0x9e,0x4f,0x4f,0xd1, 0xa3,0xdc,0xdc,0x7f,
+0x44,0x22,0x22,0x66, 0x54,0x2a,0x2a,0x7e, 0x3b,0x90,0x90,0xab, 0x0b,0x88,0x88,0x83,
+0x8c,0x46,0x46,0xca, 0xc7,0xee,0xee,0x29, 0x6b,0xb8,0xb8,0xd3, 0x28,0x14,0x14,0x3c,
+0xa7,0xde,0xde,0x79, 0xbc,0x5e,0x5e,0xe2, 0x16,0x0b,0x0b,0x1d, 0xad,0xdb,0xdb,0x76,
+0xdb,0xe0,0xe0,0x3b, 0x64,0x32,0x32,0x56, 0x74,0x3a,0x3a,0x4e, 0x14,0x0a,0x0a,0x1e,
+0x92,0x49,0x49,0xdb, 0x0c,0x06,0x06,0x0a, 0x48,0x24,0x24,0x6c, 0xb8,0x5c,0x5c,0xe4,
+0x9f,0xc2,0xc2,0x5d, 0xbd,0xd3,0xd3,0x6e, 0x43,0xac,0xac,0xef, 0xc4,0x62,0x62,0xa6,
+0x39,0x91,0x91,0xa8, 0x31,0x95,0x95,0xa4, 0xd3,0xe4,0xe4,0x37, 0xf2,0x79,0x79,0x8b,
+0xd5,0xe7,0xe7,0x32, 0x8b,0xc8,0xc8,0x43, 0x6e,0x37,0x37,0x59, 0xda,0x6d,0x6d,0xb7,
+0x01,0x8d,0x8d,0x8c, 0xb1,0xd5,0xd5,0x64, 0x9c,0x4e,0x4e,0xd2, 0x49,0xa9,0xa9,0xe0,
+0xd8,0x6c,0x6c,0xb4, 0xac,0x56,0x56,0xfa, 0xf3,0xf4,0xf4,0x07, 0xcf,0xea,0xea,0x25,
+0xca,0x65,0x65,0xaf, 0xf4,0x7a,0x7a,0x8e, 0x47,0xae,0xae,0xe9, 0x10,0x08,0x08,0x18,
+0x6f,0xba,0xba,0xd5, 0xf0,0x78,0x78,0x88, 0x4a,0x25,0x25,0x6f, 0x5c,0x2e,0x2e,0x72,
+0x38,0x1c,0x1c,0x24, 0x57,0xa6,0xa6,0xf1, 0x73,0xb4,0xb4,0xc7, 0x97,0xc6,0xc6,0x51,
+0xcb,0xe8,0xe8,0x23, 0xa1,0xdd,0xdd,0x7c, 0xe8,0x74,0x74,0x9c, 0x3e,0x1f,0x1f,0x21,
+0x96,0x4b,0x4b,0xdd, 0x61,0xbd,0xbd,0xdc, 0x0d,0x8b,0x8b,0x86, 0x0f,0x8a,0x8a,0x85,
+0xe0,0x70,0x70,0x90, 0x7c,0x3e,0x3e,0x42, 0x71,0xb5,0xb5,0xc4, 0xcc,0x66,0x66,0xaa,
+0x90,0x48,0x48,0xd8, 0x06,0x03,0x03,0x05, 0xf7,0xf6,0xf6,0x01, 0x1c,0x0e,0x0e,0x12,
+0xc2,0x61,0x61,0xa3, 0x6a,0x35,0x35,0x5f, 0xae,0x57,0x57,0xf9, 0x69,0xb9,0xb9,0xd0,
+0x17,0x86,0x86,0x91, 0x99,0xc1,0xc1,0x58, 0x3a,0x1d,0x1d,0x27, 0x27,0x9e,0x9e,0xb9,
+0xd9,0xe1,0xe1,0x38, 0xeb,0xf8,0xf8,0x13, 0x2b,0x98,0x98,0xb3, 0x22,0x11,0x11,0x33,
+0xd2,0x69,0x69,0xbb, 0xa9,0xd9,0xd9,0x70, 0x07,0x8e,0x8e,0x89, 0x33,0x94,0x94,0xa7,
+0x2d,0x9b,0x9b,0xb6, 0x3c,0x1e,0x1e,0x22, 0x15,0x87,0x87,0x92, 0xc9,0xe9,0xe9,0x20,
+0x87,0xce,0xce,0x49, 0xaa,0x55,0x55,0xff, 0x50,0x28,0x28,0x78, 0xa5,0xdf,0xdf,0x7a,
+0x03,0x8c,0x8c,0x8f, 0x59,0xa1,0xa1,0xf8, 0x09,0x89,0x89,0x80, 0x1a,0x0d,0x0d,0x17,
+0x65,0xbf,0xbf,0xda, 0xd7,0xe6,0xe6,0x31, 0x84,0x42,0x42,0xc6, 0xd0,0x68,0x68,0xb8,
+0x82,0x41,0x41,0xc3, 0x29,0x99,0x99,0xb0, 0x5a,0x2d,0x2d,0x77, 0x1e,0x0f,0x0f,0x11,
+0x7b,0xb0,0xb0,0xcb, 0xa8,0x54,0x54,0xfc, 0x6d,0xbb,0xbb,0xd6, 0x2c,0x16,0x16,0x3a
+},{
+0xa5,0xc6,0x63,0x63, 0x84,0xf8,0x7c,0x7c, 0x99,0xee,0x77,0x77, 0x8d,0xf6,0x7b,0x7b,
+0x0d,0xff,0xf2,0xf2, 0xbd,0xd6,0x6b,0x6b, 0xb1,0xde,0x6f,0x6f, 0x54,0x91,0xc5,0xc5,
+0x50,0x60,0x30,0x30, 0x03,0x02,0x01,0x01, 0xa9,0xce,0x67,0x67, 0x7d,0x56,0x2b,0x2b,
+0x19,0xe7,0xfe,0xfe, 0x62,0xb5,0xd7,0xd7, 0xe6,0x4d,0xab,0xab, 0x9a,0xec,0x76,0x76,
+0x45,0x8f,0xca,0xca, 0x9d,0x1f,0x82,0x82, 0x40,0x89,0xc9,0xc9, 0x87,0xfa,0x7d,0x7d,
+0x15,0xef,0xfa,0xfa, 0xeb,0xb2,0x59,0x59, 0xc9,0x8e,0x47,0x47, 0x0b,0xfb,0xf0,0xf0,
+0xec,0x41,0xad,0xad, 0x67,0xb3,0xd4,0xd4, 0xfd,0x5f,0xa2,0xa2, 0xea,0x45,0xaf,0xaf,
+0xbf,0x23,0x9c,0x9c, 0xf7,0x53,0xa4,0xa4, 0x96,0xe4,0x72,0x72, 0x5b,0x9b,0xc0,0xc0,
+0xc2,0x75,0xb7,0xb7, 0x1c,0xe1,0xfd,0xfd, 0xae,0x3d,0x93,0x93, 0x6a,0x4c,0x26,0x26,
+0x5a,0x6c,0x36,0x36, 0x41,0x7e,0x3f,0x3f, 0x02,0xf5,0xf7,0xf7, 0x4f,0x83,0xcc,0xcc,
+0x5c,0x68,0x34,0x34, 0xf4,0x51,0xa5,0xa5, 0x34,0xd1,0xe5,0xe5, 0x08,0xf9,0xf1,0xf1,
+0x93,0xe2,0x71,0x71, 0x73,0xab,0xd8,0xd8, 0x53,0x62,0x31,0x31, 0x3f,0x2a,0x15,0x15,
+0x0c,0x08,0x04,0x04, 0x52,0x95,0xc7,0xc7, 0x65,0x46,0x23,0x23, 0x5e,0x9d,0xc3,0xc3,
+0x28,0x30,0x18,0x18, 0xa1,0x37,0x96,0x96, 0x0f,0x0a,0x05,0x05, 0xb5,0x2f,0x9a,0x9a,
+0x09,0x0e,0x07,0x07, 0x36,0x24,0x12,0x12, 0x9b,0x1b,0x80,0x80, 0x3d,0xdf,0xe2,0xe2,
+0x26,0xcd,0xeb,0xeb, 0x69,0x4e,0x27,0x27, 0xcd,0x7f,0xb2,0xb2, 0x9f,0xea,0x75,0x75,
+0x1b,0x12,0x09,0x09, 0x9e,0x1d,0x83,0x83, 0x74,0x58,0x2c,0x2c, 0x2e,0x34,0x1a,0x1a,
+0x2d,0x36,0x1b,0x1b, 0xb2,0xdc,0x6e,0x6e, 0xee,0xb4,0x5a,0x5a, 0xfb,0x5b,0xa0,0xa0,
+0xf6,0xa4,0x52,0x52, 0x4d,0x76,0x3b,0x3b, 0x61,0xb7,0xd6,0xd6, 0xce,0x7d,0xb3,0xb3,
+0x7b,0x52,0x29,0x29, 0x3e,0xdd,0xe3,0xe3, 0x71,0x5e,0x2f,0x2f, 0x97,0x13,0x84,0x84,
+0xf5,0xa6,0x53,0x53, 0x68,0xb9,0xd1,0xd1, 0x00,0x00,0x00,0x00, 0x2c,0xc1,0xed,0xed,
+0x60,0x40,0x20,0x20, 0x1f,0xe3,0xfc,0xfc, 0xc8,0x79,0xb1,0xb1, 0xed,0xb6,0x5b,0x5b,
+0xbe,0xd4,0x6a,0x6a, 0x46,0x8d,0xcb,0xcb, 0xd9,0x67,0xbe,0xbe, 0x4b,0x72,0x39,0x39,
+0xde,0x94,0x4a,0x4a, 0xd4,0x98,0x4c,0x4c, 0xe8,0xb0,0x58,0x58, 0x4a,0x85,0xcf,0xcf,
+0x6b,0xbb,0xd0,0xd0, 0x2a,0xc5,0xef,0xef, 0xe5,0x4f,0xaa,0xaa, 0x16,0xed,0xfb,0xfb,
+0xc5,0x86,0x43,0x43, 0xd7,0x9a,0x4d,0x4d, 0x55,0x66,0x33,0x33, 0x94,0x11,0x85,0x85,
+0xcf,0x8a,0x45,0x45, 0x10,0xe9,0xf9,0xf9, 0x06,0x04,0x02,0x02, 0x81,0xfe,0x7f,0x7f,
+0xf0,0xa0,0x50,0x50, 0x44,0x78,0x3c,0x3c, 0xba,0x25,0x9f,0x9f, 0xe3,0x4b,0xa8,0xa8,
+0xf3,0xa2,0x51,0x51, 0xfe,0x5d,0xa3,0xa3, 0xc0,0x80,0x40,0x40, 0x8a,0x05,0x8f,0x8f,
+0xad,0x3f,0x92,0x92, 0xbc,0x21,0x9d,0x9d, 0x48,0x70,0x38,0x38, 0x04,0xf1,0xf5,0xf5,
+0xdf,0x63,0xbc,0xbc, 0xc1,0x77,0xb6,0xb6, 0x75,0xaf,0xda,0xda, 0x63,0x42,0x21,0x21,
+0x30,0x20,0x10,0x10, 0x1a,0xe5,0xff,0xff, 0x0e,0xfd,0xf3,0xf3, 0x6d,0xbf,0xd2,0xd2,
+0x4c,0x81,0xcd,0xcd, 0x14,0x18,0x0c,0x0c, 0x35,0x26,0x13,0x13, 0x2f,0xc3,0xec,0xec,
+0xe1,0xbe,0x5f,0x5f, 0xa2,0x35,0x97,0x97, 0xcc,0x88,0x44,0x44, 0x39,0x2e,0x17,0x17,
+0x57,0x93,0xc4,0xc4, 0xf2,0x55,0xa7,0xa7, 0x82,0xfc,0x7e,0x7e, 0x47,0x7a,0x3d,0x3d,
+0xac,0xc8,0x64,0x64, 0xe7,0xba,0x5d,0x5d, 0x2b,0x32,0x19,0x19, 0x95,0xe6,0x73,0x73,
+0xa0,0xc0,0x60,0x60, 0x98,0x19,0x81,0x81, 0xd1,0x9e,0x4f,0x4f, 0x7f,0xa3,0xdc,0xdc,
+0x66,0x44,0x22,0x22, 0x7e,0x54,0x2a,0x2a, 0xab,0x3b,0x90,0x90, 0x83,0x0b,0x88,0x88,
+0xca,0x8c,0x46,0x46, 0x29,0xc7,0xee,0xee, 0xd3,0x6b,0xb8,0xb8, 0x3c,0x28,0x14,0x14,
+0x79,0xa7,0xde,0xde, 0xe2,0xbc,0x5e,0x5e, 0x1d,0x16,0x0b,0x0b, 0x76,0xad,0xdb,0xdb,
+0x3b,0xdb,0xe0,0xe0, 0x56,0x64,0x32,0x32, 0x4e,0x74,0x3a,0x3a, 0x1e,0x14,0x0a,0x0a,
+0xdb,0x92,0x49,0x49, 0x0a,0x0c,0x06,0x06, 0x6c,0x48,0x24,0x24, 0xe4,0xb8,0x5c,0x5c,
+0x5d,0x9f,0xc2,0xc2, 0x6e,0xbd,0xd3,0xd3, 0xef,0x43,0xac,0xac, 0xa6,0xc4,0x62,0x62,
+0xa8,0x39,0x91,0x91, 0xa4,0x31,0x95,0x95, 0x37,0xd3,0xe4,0xe4, 0x8b,0xf2,0x79,0x79,
+0x32,0xd5,0xe7,0xe7, 0x43,0x8b,0xc8,0xc8, 0x59,0x6e,0x37,0x37, 0xb7,0xda,0x6d,0x6d,
+0x8c,0x01,0x8d,0x8d, 0x64,0xb1,0xd5,0xd5, 0xd2,0x9c,0x4e,0x4e, 0xe0,0x49,0xa9,0xa9,
+0xb4,0xd8,0x6c,0x6c, 0xfa,0xac,0x56,0x56, 0x07,0xf3,0xf4,0xf4, 0x25,0xcf,0xea,0xea,
+0xaf,0xca,0x65,0x65, 0x8e,0xf4,0x7a,0x7a, 0xe9,0x47,0xae,0xae, 0x18,0x10,0x08,0x08,
+0xd5,0x6f,0xba,0xba, 0x88,0xf0,0x78,0x78, 0x6f,0x4a,0x25,0x25, 0x72,0x5c,0x2e,0x2e,
+0x24,0x38,0x1c,0x1c, 0xf1,0x57,0xa6,0xa6, 0xc7,0x73,0xb4,0xb4, 0x51,0x97,0xc6,0xc6,
+0x23,0xcb,0xe8,0xe8, 0x7c,0xa1,0xdd,0xdd, 0x9c,0xe8,0x74,0x74, 0x21,0x3e,0x1f,0x1f,
+0xdd,0x96,0x4b,0x4b, 0xdc,0x61,0xbd,0xbd, 0x86,0x0d,0x8b,0x8b, 0x85,0x0f,0x8a,0x8a,
+0x90,0xe0,0x70,0x70, 0x42,0x7c,0x3e,0x3e, 0xc4,0x71,0xb5,0xb5, 0xaa,0xcc,0x66,0x66,
+0xd8,0x90,0x48,0x48, 0x05,0x06,0x03,0x03, 0x01,0xf7,0xf6,0xf6, 0x12,0x1c,0x0e,0x0e,
+0xa3,0xc2,0x61,0x61, 0x5f,0x6a,0x35,0x35, 0xf9,0xae,0x57,0x57, 0xd0,0x69,0xb9,0xb9,
+0x91,0x17,0x86,0x86, 0x58,0x99,0xc1,0xc1, 0x27,0x3a,0x1d,0x1d, 0xb9,0x27,0x9e,0x9e,
+0x38,0xd9,0xe1,0xe1, 0x13,0xeb,0xf8,0xf8, 0xb3,0x2b,0x98,0x98, 0x33,0x22,0x11,0x11,
+0xbb,0xd2,0x69,0x69, 0x70,0xa9,0xd9,0xd9, 0x89,0x07,0x8e,0x8e, 0xa7,0x33,0x94,0x94,
+0xb6,0x2d,0x9b,0x9b, 0x22,0x3c,0x1e,0x1e, 0x92,0x15,0x87,0x87, 0x20,0xc9,0xe9,0xe9,
+0x49,0x87,0xce,0xce, 0xff,0xaa,0x55,0x55, 0x78,0x50,0x28,0x28, 0x7a,0xa5,0xdf,0xdf,
+0x8f,0x03,0x8c,0x8c, 0xf8,0x59,0xa1,0xa1, 0x80,0x09,0x89,0x89, 0x17,0x1a,0x0d,0x0d,
+0xda,0x65,0xbf,0xbf, 0x31,0xd7,0xe6,0xe6, 0xc6,0x84,0x42,0x42, 0xb8,0xd0,0x68,0x68,
+0xc3,0x82,0x41,0x41, 0xb0,0x29,0x99,0x99, 0x77,0x5a,0x2d,0x2d, 0x11,0x1e,0x0f,0x0f,
+0xcb,0x7b,0xb0,0xb0, 0xfc,0xa8,0x54,0x54, 0xd6,0x6d,0xbb,0xbb, 0x3a,0x2c,0x16,0x16
+},{
+0x63,0xa5,0xc6,0x63, 0x7c,0x84,0xf8,0x7c, 0x77,0x99,0xee,0x77, 0x7b,0x8d,0xf6,0x7b,
+0xf2,0x0d,0xff,0xf2, 0x6b,0xbd,0xd6,0x6b, 0x6f,0xb1,0xde,0x6f, 0xc5,0x54,0x91,0xc5,
+0x30,0x50,0x60,0x30, 0x01,0x03,0x02,0x01, 0x67,0xa9,0xce,0x67, 0x2b,0x7d,0x56,0x2b,
+0xfe,0x19,0xe7,0xfe, 0xd7,0x62,0xb5,0xd7, 0xab,0xe6,0x4d,0xab, 0x76,0x9a,0xec,0x76,
+0xca,0x45,0x8f,0xca, 0x82,0x9d,0x1f,0x82, 0xc9,0x40,0x89,0xc9, 0x7d,0x87,0xfa,0x7d,
+0xfa,0x15,0xef,0xfa, 0x59,0xeb,0xb2,0x59, 0x47,0xc9,0x8e,0x47, 0xf0,0x0b,0xfb,0xf0,
+0xad,0xec,0x41,0xad, 0xd4,0x67,0xb3,0xd4, 0xa2,0xfd,0x5f,0xa2, 0xaf,0xea,0x45,0xaf,
+0x9c,0xbf,0x23,0x9c, 0xa4,0xf7,0x53,0xa4, 0x72,0x96,0xe4,0x72, 0xc0,0x5b,0x9b,0xc0,
+0xb7,0xc2,0x75,0xb7, 0xfd,0x1c,0xe1,0xfd, 0x93,0xae,0x3d,0x93, 0x26,0x6a,0x4c,0x26,
+0x36,0x5a,0x6c,0x36, 0x3f,0x41,0x7e,0x3f, 0xf7,0x02,0xf5,0xf7, 0xcc,0x4f,0x83,0xcc,
+0x34,0x5c,0x68,0x34, 0xa5,0xf4,0x51,0xa5, 0xe5,0x34,0xd1,0xe5, 0xf1,0x08,0xf9,0xf1,
+0x71,0x93,0xe2,0x71, 0xd8,0x73,0xab,0xd8, 0x31,0x53,0x62,0x31, 0x15,0x3f,0x2a,0x15,
+0x04,0x0c,0x08,0x04, 0xc7,0x52,0x95,0xc7, 0x23,0x65,0x46,0x23, 0xc3,0x5e,0x9d,0xc3,
+0x18,0x28,0x30,0x18, 0x96,0xa1,0x37,0x96, 0x05,0x0f,0x0a,0x05, 0x9a,0xb5,0x2f,0x9a,
+0x07,0x09,0x0e,0x07, 0x12,0x36,0x24,0x12, 0x80,0x9b,0x1b,0x80, 0xe2,0x3d,0xdf,0xe2,
+0xeb,0x26,0xcd,0xeb, 0x27,0x69,0x4e,0x27, 0xb2,0xcd,0x7f,0xb2, 0x75,0x9f,0xea,0x75,
+0x09,0x1b,0x12,0x09, 0x83,0x9e,0x1d,0x83, 0x2c,0x74,0x58,0x2c, 0x1a,0x2e,0x34,0x1a,
+0x1b,0x2d,0x36,0x1b, 0x6e,0xb2,0xdc,0x6e, 0x5a,0xee,0xb4,0x5a, 0xa0,0xfb,0x5b,0xa0,
+0x52,0xf6,0xa4,0x52, 0x3b,0x4d,0x76,0x3b, 0xd6,0x61,0xb7,0xd6, 0xb3,0xce,0x7d,0xb3,
+0x29,0x7b,0x52,0x29, 0xe3,0x3e,0xdd,0xe3, 0x2f,0x71,0x5e,0x2f, 0x84,0x97,0x13,0x84,
+0x53,0xf5,0xa6,0x53, 0xd1,0x68,0xb9,0xd1, 0x00,0x00,0x00,0x00, 0xed,0x2c,0xc1,0xed,
+0x20,0x60,0x40,0x20, 0xfc,0x1f,0xe3,0xfc, 0xb1,0xc8,0x79,0xb1, 0x5b,0xed,0xb6,0x5b,
+0x6a,0xbe,0xd4,0x6a, 0xcb,0x46,0x8d,0xcb, 0xbe,0xd9,0x67,0xbe, 0x39,0x4b,0x72,0x39,
+0x4a,0xde,0x94,0x4a, 0x4c,0xd4,0x98,0x4c, 0x58,0xe8,0xb0,0x58, 0xcf,0x4a,0x85,0xcf,
+0xd0,0x6b,0xbb,0xd0, 0xef,0x2a,0xc5,0xef, 0xaa,0xe5,0x4f,0xaa, 0xfb,0x16,0xed,0xfb,
+0x43,0xc5,0x86,0x43, 0x4d,0xd7,0x9a,0x4d, 0x33,0x55,0x66,0x33, 0x85,0x94,0x11,0x85,
+0x45,0xcf,0x8a,0x45, 0xf9,0x10,0xe9,0xf9, 0x02,0x06,0x04,0x02, 0x7f,0x81,0xfe,0x7f,
+0x50,0xf0,0xa0,0x50, 0x3c,0x44,0x78,0x3c, 0x9f,0xba,0x25,0x9f, 0xa8,0xe3,0x4b,0xa8,
+0x51,0xf3,0xa2,0x51, 0xa3,0xfe,0x5d,0xa3, 0x40,0xc0,0x80,0x40, 0x8f,0x8a,0x05,0x8f,
+0x92,0xad,0x3f,0x92, 0x9d,0xbc,0x21,0x9d, 0x38,0x48,0x70,0x38, 0xf5,0x04,0xf1,0xf5,
+0xbc,0xdf,0x63,0xbc, 0xb6,0xc1,0x77,0xb6, 0xda,0x75,0xaf,0xda, 0x21,0x63,0x42,0x21,
+0x10,0x30,0x20,0x10, 0xff,0x1a,0xe5,0xff, 0xf3,0x0e,0xfd,0xf3, 0xd2,0x6d,0xbf,0xd2,
+0xcd,0x4c,0x81,0xcd, 0x0c,0x14,0x18,0x0c, 0x13,0x35,0x26,0x13, 0xec,0x2f,0xc3,0xec,
+0x5f,0xe1,0xbe,0x5f, 0x97,0xa2,0x35,0x97, 0x44,0xcc,0x88,0x44, 0x17,0x39,0x2e,0x17,
+0xc4,0x57,0x93,0xc4, 0xa7,0xf2,0x55,0xa7, 0x7e,0x82,0xfc,0x7e, 0x3d,0x47,0x7a,0x3d,
+0x64,0xac,0xc8,0x64, 0x5d,0xe7,0xba,0x5d, 0x19,0x2b,0x32,0x19, 0x73,0x95,0xe6,0x73,
+0x60,0xa0,0xc0,0x60, 0x81,0x98,0x19,0x81, 0x4f,0xd1,0x9e,0x4f, 0xdc,0x7f,0xa3,0xdc,
+0x22,0x66,0x44,0x22, 0x2a,0x7e,0x54,0x2a, 0x90,0xab,0x3b,0x90, 0x88,0x83,0x0b,0x88,
+0x46,0xca,0x8c,0x46, 0xee,0x29,0xc7,0xee, 0xb8,0xd3,0x6b,0xb8, 0x14,0x3c,0x28,0x14,
+0xde,0x79,0xa7,0xde, 0x5e,0xe2,0xbc,0x5e, 0x0b,0x1d,0x16,0x0b, 0xdb,0x76,0xad,0xdb,
+0xe0,0x3b,0xdb,0xe0, 0x32,0x56,0x64,0x32, 0x3a,0x4e,0x74,0x3a, 0x0a,0x1e,0x14,0x0a,
+0x49,0xdb,0x92,0x49, 0x06,0x0a,0x0c,0x06, 0x24,0x6c,0x48,0x24, 0x5c,0xe4,0xb8,0x5c,
+0xc2,0x5d,0x9f,0xc2, 0xd3,0x6e,0xbd,0xd3, 0xac,0xef,0x43,0xac, 0x62,0xa6,0xc4,0x62,
+0x91,0xa8,0x39,0x91, 0x95,0xa4,0x31,0x95, 0xe4,0x37,0xd3,0xe4, 0x79,0x8b,0xf2,0x79,
+0xe7,0x32,0xd5,0xe7, 0xc8,0x43,0x8b,0xc8, 0x37,0x59,0x6e,0x37, 0x6d,0xb7,0xda,0x6d,
+0x8d,0x8c,0x01,0x8d, 0xd5,0x64,0xb1,0xd5, 0x4e,0xd2,0x9c,0x4e, 0xa9,0xe0,0x49,0xa9,
+0x6c,0xb4,0xd8,0x6c, 0x56,0xfa,0xac,0x56, 0xf4,0x07,0xf3,0xf4, 0xea,0x25,0xcf,0xea,
+0x65,0xaf,0xca,0x65, 0x7a,0x8e,0xf4,0x7a, 0xae,0xe9,0x47,0xae, 0x08,0x18,0x10,0x08,
+0xba,0xd5,0x6f,0xba, 0x78,0x88,0xf0,0x78, 0x25,0x6f,0x4a,0x25, 0x2e,0x72,0x5c,0x2e,
+0x1c,0x24,0x38,0x1c, 0xa6,0xf1,0x57,0xa6, 0xb4,0xc7,0x73,0xb4, 0xc6,0x51,0x97,0xc6,
+0xe8,0x23,0xcb,0xe8, 0xdd,0x7c,0xa1,0xdd, 0x74,0x9c,0xe8,0x74, 0x1f,0x21,0x3e,0x1f,
+0x4b,0xdd,0x96,0x4b, 0xbd,0xdc,0x61,0xbd, 0x8b,0x86,0x0d,0x8b, 0x8a,0x85,0x0f,0x8a,
+0x70,0x90,0xe0,0x70, 0x3e,0x42,0x7c,0x3e, 0xb5,0xc4,0x71,0xb5, 0x66,0xaa,0xcc,0x66,
+0x48,0xd8,0x90,0x48, 0x03,0x05,0x06,0x03, 0xf6,0x01,0xf7,0xf6, 0x0e,0x12,0x1c,0x0e,
+0x61,0xa3,0xc2,0x61, 0x35,0x5f,0x6a,0x35, 0x57,0xf9,0xae,0x57, 0xb9,0xd0,0x69,0xb9,
+0x86,0x91,0x17,0x86, 0xc1,0x58,0x99,0xc1, 0x1d,0x27,0x3a,0x1d, 0x9e,0xb9,0x27,0x9e,
+0xe1,0x38,0xd9,0xe1, 0xf8,0x13,0xeb,0xf8, 0x98,0xb3,0x2b,0x98, 0x11,0x33,0x22,0x11,
+0x69,0xbb,0xd2,0x69, 0xd9,0x70,0xa9,0xd9, 0x8e,0x89,0x07,0x8e, 0x94,0xa7,0x33,0x94,
+0x9b,0xb6,0x2d,0x9b, 0x1e,0x22,0x3c,0x1e, 0x87,0x92,0x15,0x87, 0xe9,0x20,0xc9,0xe9,
+0xce,0x49,0x87,0xce, 0x55,0xff,0xaa,0x55, 0x28,0x78,0x50,0x28, 0xdf,0x7a,0xa5,0xdf,
+0x8c,0x8f,0x03,0x8c, 0xa1,0xf8,0x59,0xa1, 0x89,0x80,0x09,0x89, 0x0d,0x17,0x1a,0x0d,
+0xbf,0xda,0x65,0xbf, 0xe6,0x31,0xd7,0xe6, 0x42,0xc6,0x84,0x42, 0x68,0xb8,0xd0,0x68,
+0x41,0xc3,0x82,0x41, 0x99,0xb0,0x29,0x99, 0x2d,0x77,0x5a,0x2d, 0x0f,0x11,0x1e,0x0f,
+0xb0,0xcb,0x7b,0xb0, 0x54,0xfc,0xa8,0x54, 0xbb,0xd6,0x6d,0xbb, 0x16,0x3a,0x2c,0x16
+},{
+0x63,0x63,0xa5,0xc6, 0x7c,0x7c,0x84,0xf8, 0x77,0x77,0x99,0xee, 0x7b,0x7b,0x8d,0xf6,
+0xf2,0xf2,0x0d,0xff, 0x6b,0x6b,0xbd,0xd6, 0x6f,0x6f,0xb1,0xde, 0xc5,0xc5,0x54,0x91,
+0x30,0x30,0x50,0x60, 0x01,0x01,0x03,0x02, 0x67,0x67,0xa9,0xce, 0x2b,0x2b,0x7d,0x56,
+0xfe,0xfe,0x19,0xe7, 0xd7,0xd7,0x62,0xb5, 0xab,0xab,0xe6,0x4d, 0x76,0x76,0x9a,0xec,
+0xca,0xca,0x45,0x8f, 0x82,0x82,0x9d,0x1f, 0xc9,0xc9,0x40,0x89, 0x7d,0x7d,0x87,0xfa,
+0xfa,0xfa,0x15,0xef, 0x59,0x59,0xeb,0xb2, 0x47,0x47,0xc9,0x8e, 0xf0,0xf0,0x0b,0xfb,
+0xad,0xad,0xec,0x41, 0xd4,0xd4,0x67,0xb3, 0xa2,0xa2,0xfd,0x5f, 0xaf,0xaf,0xea,0x45,
+0x9c,0x9c,0xbf,0x23, 0xa4,0xa4,0xf7,0x53, 0x72,0x72,0x96,0xe4, 0xc0,0xc0,0x5b,0x9b,
+0xb7,0xb7,0xc2,0x75, 0xfd,0xfd,0x1c,0xe1, 0x93,0x93,0xae,0x3d, 0x26,0x26,0x6a,0x4c,
+0x36,0x36,0x5a,0x6c, 0x3f,0x3f,0x41,0x7e, 0xf7,0xf7,0x02,0xf5, 0xcc,0xcc,0x4f,0x83,
+0x34,0x34,0x5c,0x68, 0xa5,0xa5,0xf4,0x51, 0xe5,0xe5,0x34,0xd1, 0xf1,0xf1,0x08,0xf9,
+0x71,0x71,0x93,0xe2, 0xd8,0xd8,0x73,0xab, 0x31,0x31,0x53,0x62, 0x15,0x15,0x3f,0x2a,
+0x04,0x04,0x0c,0x08, 0xc7,0xc7,0x52,0x95, 0x23,0x23,0x65,0x46, 0xc3,0xc3,0x5e,0x9d,
+0x18,0x18,0x28,0x30, 0x96,0x96,0xa1,0x37, 0x05,0x05,0x0f,0x0a, 0x9a,0x9a,0xb5,0x2f,
+0x07,0x07,0x09,0x0e, 0x12,0x12,0x36,0x24, 0x80,0x80,0x9b,0x1b, 0xe2,0xe2,0x3d,0xdf,
+0xeb,0xeb,0x26,0xcd, 0x27,0x27,0x69,0x4e, 0xb2,0xb2,0xcd,0x7f, 0x75,0x75,0x9f,0xea,
+0x09,0x09,0x1b,0x12, 0x83,0x83,0x9e,0x1d, 0x2c,0x2c,0x74,0x58, 0x1a,0x1a,0x2e,0x34,
+0x1b,0x1b,0x2d,0x36, 0x6e,0x6e,0xb2,0xdc, 0x5a,0x5a,0xee,0xb4, 0xa0,0xa0,0xfb,0x5b,
+0x52,0x52,0xf6,0xa4, 0x3b,0x3b,0x4d,0x76, 0xd6,0xd6,0x61,0xb7, 0xb3,0xb3,0xce,0x7d,
+0x29,0x29,0x7b,0x52, 0xe3,0xe3,0x3e,0xdd, 0x2f,0x2f,0x71,0x5e, 0x84,0x84,0x97,0x13,
+0x53,0x53,0xf5,0xa6, 0xd1,0xd1,0x68,0xb9, 0x00,0x00,0x00,0x00, 0xed,0xed,0x2c,0xc1,
+0x20,0x20,0x60,0x40, 0xfc,0xfc,0x1f,0xe3, 0xb1,0xb1,0xc8,0x79, 0x5b,0x5b,0xed,0xb6,
+0x6a,0x6a,0xbe,0xd4, 0xcb,0xcb,0x46,0x8d, 0xbe,0xbe,0xd9,0x67, 0x39,0x39,0x4b,0x72,
+0x4a,0x4a,0xde,0x94, 0x4c,0x4c,0xd4,0x98, 0x58,0x58,0xe8,0xb0, 0xcf,0xcf,0x4a,0x85,
+0xd0,0xd0,0x6b,0xbb, 0xef,0xef,0x2a,0xc5, 0xaa,0xaa,0xe5,0x4f, 0xfb,0xfb,0x16,0xed,
+0x43,0x43,0xc5,0x86, 0x4d,0x4d,0xd7,0x9a, 0x33,0x33,0x55,0x66, 0x85,0x85,0x94,0x11,
+0x45,0x45,0xcf,0x8a, 0xf9,0xf9,0x10,0xe9, 0x02,0x02,0x06,0x04, 0x7f,0x7f,0x81,0xfe,
+0x50,0x50,0xf0,0xa0, 0x3c,0x3c,0x44,0x78, 0x9f,0x9f,0xba,0x25, 0xa8,0xa8,0xe3,0x4b,
+0x51,0x51,0xf3,0xa2, 0xa3,0xa3,0xfe,0x5d, 0x40,0x40,0xc0,0x80, 0x8f,0x8f,0x8a,0x05,
+0x92,0x92,0xad,0x3f, 0x9d,0x9d,0xbc,0x21, 0x38,0x38,0x48,0x70, 0xf5,0xf5,0x04,0xf1,
+0xbc,0xbc,0xdf,0x63, 0xb6,0xb6,0xc1,0x77, 0xda,0xda,0x75,0xaf, 0x21,0x21,0x63,0x42,
+0x10,0x10,0x30,0x20, 0xff,0xff,0x1a,0xe5, 0xf3,0xf3,0x0e,0xfd, 0xd2,0xd2,0x6d,0xbf,
+0xcd,0xcd,0x4c,0x81, 0x0c,0x0c,0x14,0x18, 0x13,0x13,0x35,0x26, 0xec,0xec,0x2f,0xc3,
+0x5f,0x5f,0xe1,0xbe, 0x97,0x97,0xa2,0x35, 0x44,0x44,0xcc,0x88, 0x17,0x17,0x39,0x2e,
+0xc4,0xc4,0x57,0x93, 0xa7,0xa7,0xf2,0x55, 0x7e,0x7e,0x82,0xfc, 0x3d,0x3d,0x47,0x7a,
+0x64,0x64,0xac,0xc8, 0x5d,0x5d,0xe7,0xba, 0x19,0x19,0x2b,0x32, 0x73,0x73,0x95,0xe6,
+0x60,0x60,0xa0,0xc0, 0x81,0x81,0x98,0x19, 0x4f,0x4f,0xd1,0x9e, 0xdc,0xdc,0x7f,0xa3,
+0x22,0x22,0x66,0x44, 0x2a,0x2a,0x7e,0x54, 0x90,0x90,0xab,0x3b, 0x88,0x88,0x83,0x0b,
+0x46,0x46,0xca,0x8c, 0xee,0xee,0x29,0xc7, 0xb8,0xb8,0xd3,0x6b, 0x14,0x14,0x3c,0x28,
+0xde,0xde,0x79,0xa7, 0x5e,0x5e,0xe2,0xbc, 0x0b,0x0b,0x1d,0x16, 0xdb,0xdb,0x76,0xad,
+0xe0,0xe0,0x3b,0xdb, 0x32,0x32,0x56,0x64, 0x3a,0x3a,0x4e,0x74, 0x0a,0x0a,0x1e,0x14,
+0x49,0x49,0xdb,0x92, 0x06,0x06,0x0a,0x0c, 0x24,0x24,0x6c,0x48, 0x5c,0x5c,0xe4,0xb8,
+0xc2,0xc2,0x5d,0x9f, 0xd3,0xd3,0x6e,0xbd, 0xac,0xac,0xef,0x43, 0x62,0x62,0xa6,0xc4,
+0x91,0x91,0xa8,0x39, 0x95,0x95,0xa4,0x31, 0xe4,0xe4,0x37,0xd3, 0x79,0x79,0x8b,0xf2,
+0xe7,0xe7,0x32,0xd5, 0xc8,0xc8,0x43,0x8b, 0x37,0x37,0x59,0x6e, 0x6d,0x6d,0xb7,0xda,
+0x8d,0x8d,0x8c,0x01, 0xd5,0xd5,0x64,0xb1, 0x4e,0x4e,0xd2,0x9c, 0xa9,0xa9,0xe0,0x49,
+0x6c,0x6c,0xb4,0xd8, 0x56,0x56,0xfa,0xac, 0xf4,0xf4,0x07,0xf3, 0xea,0xea,0x25,0xcf,
+0x65,0x65,0xaf,0xca, 0x7a,0x7a,0x8e,0xf4, 0xae,0xae,0xe9,0x47, 0x08,0x08,0x18,0x10,
+0xba,0xba,0xd5,0x6f, 0x78,0x78,0x88,0xf0, 0x25,0x25,0x6f,0x4a, 0x2e,0x2e,0x72,0x5c,
+0x1c,0x1c,0x24,0x38, 0xa6,0xa6,0xf1,0x57, 0xb4,0xb4,0xc7,0x73, 0xc6,0xc6,0x51,0x97,
+0xe8,0xe8,0x23,0xcb, 0xdd,0xdd,0x7c,0xa1, 0x74,0x74,0x9c,0xe8, 0x1f,0x1f,0x21,0x3e,
+0x4b,0x4b,0xdd,0x96, 0xbd,0xbd,0xdc,0x61, 0x8b,0x8b,0x86,0x0d, 0x8a,0x8a,0x85,0x0f,
+0x70,0x70,0x90,0xe0, 0x3e,0x3e,0x42,0x7c, 0xb5,0xb5,0xc4,0x71, 0x66,0x66,0xaa,0xcc,
+0x48,0x48,0xd8,0x90, 0x03,0x03,0x05,0x06, 0xf6,0xf6,0x01,0xf7, 0x0e,0x0e,0x12,0x1c,
+0x61,0x61,0xa3,0xc2, 0x35,0x35,0x5f,0x6a, 0x57,0x57,0xf9,0xae, 0xb9,0xb9,0xd0,0x69,
+0x86,0x86,0x91,0x17, 0xc1,0xc1,0x58,0x99, 0x1d,0x1d,0x27,0x3a, 0x9e,0x9e,0xb9,0x27,
+0xe1,0xe1,0x38,0xd9, 0xf8,0xf8,0x13,0xeb, 0x98,0x98,0xb3,0x2b, 0x11,0x11,0x33,0x22,
+0x69,0x69,0xbb,0xd2, 0xd9,0xd9,0x70,0xa9, 0x8e,0x8e,0x89,0x07, 0x94,0x94,0xa7,0x33,
+0x9b,0x9b,0xb6,0x2d, 0x1e,0x1e,0x22,0x3c, 0x87,0x87,0x92,0x15, 0xe9,0xe9,0x20,0xc9,
+0xce,0xce,0x49,0x87, 0x55,0x55,0xff,0xaa, 0x28,0x28,0x78,0x50, 0xdf,0xdf,0x7a,0xa5,
+0x8c,0x8c,0x8f,0x03, 0xa1,0xa1,0xf8,0x59, 0x89,0x89,0x80,0x09, 0x0d,0x0d,0x17,0x1a,
+0xbf,0xbf,0xda,0x65, 0xe6,0xe6,0x31,0xd7, 0x42,0x42,0xc6,0x84, 0x68,0x68,0xb8,0xd0,
+0x41,0x41,0xc3,0x82, 0x99,0x99,0xb0,0x29, 0x2d,0x2d,0x77,0x5a, 0x0f,0x0f,0x11,0x1e,
+0xb0,0xb0,0xcb,0x7b, 0x54,0x54,0xfc,0xa8, 0xbb,0xbb,0xd6,0x6d, 0x16,0x16,0x3a,0x2c
+}};
+
+SYMCRYPT_ALIGN_AT(1024) const BYTE SymCryptAesInvSboxMatrixMult[4][256][4] = {{// Main decryption tables
+0x51,0xf4,0xa7,0x50, 0x7e,0x41,0x65,0x53, 0x1a,0x17,0xa4,0xc3, 0x3a,0x27,0x5e,0x96,
+0x3b,0xab,0x6b,0xcb, 0x1f,0x9d,0x45,0xf1, 0xac,0xfa,0x58,0xab, 0x4b,0xe3,0x03,0x93,
+0x20,0x30,0xfa,0x55, 0xad,0x76,0x6d,0xf6, 0x88,0xcc,0x76,0x91, 0xf5,0x02,0x4c,0x25,
+0x4f,0xe5,0xd7,0xfc, 0xc5,0x2a,0xcb,0xd7, 0x26,0x35,0x44,0x80, 0xb5,0x62,0xa3,0x8f,
+0xde,0xb1,0x5a,0x49, 0x25,0xba,0x1b,0x67, 0x45,0xea,0x0e,0x98, 0x5d,0xfe,0xc0,0xe1,
+0xc3,0x2f,0x75,0x02, 0x81,0x4c,0xf0,0x12, 0x8d,0x46,0x97,0xa3, 0x6b,0xd3,0xf9,0xc6,
+0x03,0x8f,0x5f,0xe7, 0x15,0x92,0x9c,0x95, 0xbf,0x6d,0x7a,0xeb, 0x95,0x52,0x59,0xda,
+0xd4,0xbe,0x83,0x2d, 0x58,0x74,0x21,0xd3, 0x49,0xe0,0x69,0x29, 0x8e,0xc9,0xc8,0x44,
+0x75,0xc2,0x89,0x6a, 0xf4,0x8e,0x79,0x78, 0x99,0x58,0x3e,0x6b, 0x27,0xb9,0x71,0xdd,
+0xbe,0xe1,0x4f,0xb6, 0xf0,0x88,0xad,0x17, 0xc9,0x20,0xac,0x66, 0x7d,0xce,0x3a,0xb4,
+0x63,0xdf,0x4a,0x18, 0xe5,0x1a,0x31,0x82, 0x97,0x51,0x33,0x60, 0x62,0x53,0x7f,0x45,
+0xb1,0x64,0x77,0xe0, 0xbb,0x6b,0xae,0x84, 0xfe,0x81,0xa0,0x1c, 0xf9,0x08,0x2b,0x94,
+0x70,0x48,0x68,0x58, 0x8f,0x45,0xfd,0x19, 0x94,0xde,0x6c,0x87, 0x52,0x7b,0xf8,0xb7,
+0xab,0x73,0xd3,0x23, 0x72,0x4b,0x02,0xe2, 0xe3,0x1f,0x8f,0x57, 0x66,0x55,0xab,0x2a,
+0xb2,0xeb,0x28,0x07, 0x2f,0xb5,0xc2,0x03, 0x86,0xc5,0x7b,0x9a, 0xd3,0x37,0x08,0xa5,
+0x30,0x28,0x87,0xf2, 0x23,0xbf,0xa5,0xb2, 0x02,0x03,0x6a,0xba, 0xed,0x16,0x82,0x5c,
+0x8a,0xcf,0x1c,0x2b, 0xa7,0x79,0xb4,0x92, 0xf3,0x07,0xf2,0xf0, 0x4e,0x69,0xe2,0xa1,
+0x65,0xda,0xf4,0xcd, 0x06,0x05,0xbe,0xd5, 0xd1,0x34,0x62,0x1f, 0xc4,0xa6,0xfe,0x8a,
+0x34,0x2e,0x53,0x9d, 0xa2,0xf3,0x55,0xa0, 0x05,0x8a,0xe1,0x32, 0xa4,0xf6,0xeb,0x75,
+0x0b,0x83,0xec,0x39, 0x40,0x60,0xef,0xaa, 0x5e,0x71,0x9f,0x06, 0xbd,0x6e,0x10,0x51,
+0x3e,0x21,0x8a,0xf9, 0x96,0xdd,0x06,0x3d, 0xdd,0x3e,0x05,0xae, 0x4d,0xe6,0xbd,0x46,
+0x91,0x54,0x8d,0xb5, 0x71,0xc4,0x5d,0x05, 0x04,0x06,0xd4,0x6f, 0x60,0x50,0x15,0xff,
+0x19,0x98,0xfb,0x24, 0xd6,0xbd,0xe9,0x97, 0x89,0x40,0x43,0xcc, 0x67,0xd9,0x9e,0x77,
+0xb0,0xe8,0x42,0xbd, 0x07,0x89,0x8b,0x88, 0xe7,0x19,0x5b,0x38, 0x79,0xc8,0xee,0xdb,
+0xa1,0x7c,0x0a,0x47, 0x7c,0x42,0x0f,0xe9, 0xf8,0x84,0x1e,0xc9, 0x00,0x00,0x00,0x00,
+0x09,0x80,0x86,0x83, 0x32,0x2b,0xed,0x48, 0x1e,0x11,0x70,0xac, 0x6c,0x5a,0x72,0x4e,
+0xfd,0x0e,0xff,0xfb, 0x0f,0x85,0x38,0x56, 0x3d,0xae,0xd5,0x1e, 0x36,0x2d,0x39,0x27,
+0x0a,0x0f,0xd9,0x64, 0x68,0x5c,0xa6,0x21, 0x9b,0x5b,0x54,0xd1, 0x24,0x36,0x2e,0x3a,
+0x0c,0x0a,0x67,0xb1, 0x93,0x57,0xe7,0x0f, 0xb4,0xee,0x96,0xd2, 0x1b,0x9b,0x91,0x9e,
+0x80,0xc0,0xc5,0x4f, 0x61,0xdc,0x20,0xa2, 0x5a,0x77,0x4b,0x69, 0x1c,0x12,0x1a,0x16,
+0xe2,0x93,0xba,0x0a, 0xc0,0xa0,0x2a,0xe5, 0x3c,0x22,0xe0,0x43, 0x12,0x1b,0x17,0x1d,
+0x0e,0x09,0x0d,0x0b, 0xf2,0x8b,0xc7,0xad, 0x2d,0xb6,0xa8,0xb9, 0x14,0x1e,0xa9,0xc8,
+0x57,0xf1,0x19,0x85, 0xaf,0x75,0x07,0x4c, 0xee,0x99,0xdd,0xbb, 0xa3,0x7f,0x60,0xfd,
+0xf7,0x01,0x26,0x9f, 0x5c,0x72,0xf5,0xbc, 0x44,0x66,0x3b,0xc5, 0x5b,0xfb,0x7e,0x34,
+0x8b,0x43,0x29,0x76, 0xcb,0x23,0xc6,0xdc, 0xb6,0xed,0xfc,0x68, 0xb8,0xe4,0xf1,0x63,
+0xd7,0x31,0xdc,0xca, 0x42,0x63,0x85,0x10, 0x13,0x97,0x22,0x40, 0x84,0xc6,0x11,0x20,
+0x85,0x4a,0x24,0x7d, 0xd2,0xbb,0x3d,0xf8, 0xae,0xf9,0x32,0x11, 0xc7,0x29,0xa1,0x6d,
+0x1d,0x9e,0x2f,0x4b, 0xdc,0xb2,0x30,0xf3, 0x0d,0x86,0x52,0xec, 0x77,0xc1,0xe3,0xd0,
+0x2b,0xb3,0x16,0x6c, 0xa9,0x70,0xb9,0x99, 0x11,0x94,0x48,0xfa, 0x47,0xe9,0x64,0x22,
+0xa8,0xfc,0x8c,0xc4, 0xa0,0xf0,0x3f,0x1a, 0x56,0x7d,0x2c,0xd8, 0x22,0x33,0x90,0xef,
+0x87,0x49,0x4e,0xc7, 0xd9,0x38,0xd1,0xc1, 0x8c,0xca,0xa2,0xfe, 0x98,0xd4,0x0b,0x36,
+0xa6,0xf5,0x81,0xcf, 0xa5,0x7a,0xde,0x28, 0xda,0xb7,0x8e,0x26, 0x3f,0xad,0xbf,0xa4,
+0x2c,0x3a,0x9d,0xe4, 0x50,0x78,0x92,0x0d, 0x6a,0x5f,0xcc,0x9b, 0x54,0x7e,0x46,0x62,
+0xf6,0x8d,0x13,0xc2, 0x90,0xd8,0xb8,0xe8, 0x2e,0x39,0xf7,0x5e, 0x82,0xc3,0xaf,0xf5,
+0x9f,0x5d,0x80,0xbe, 0x69,0xd0,0x93,0x7c, 0x6f,0xd5,0x2d,0xa9, 0xcf,0x25,0x12,0xb3,
+0xc8,0xac,0x99,0x3b, 0x10,0x18,0x7d,0xa7, 0xe8,0x9c,0x63,0x6e, 0xdb,0x3b,0xbb,0x7b,
+0xcd,0x26,0x78,0x09, 0x6e,0x59,0x18,0xf4, 0xec,0x9a,0xb7,0x01, 0x83,0x4f,0x9a,0xa8,
+0xe6,0x95,0x6e,0x65, 0xaa,0xff,0xe6,0x7e, 0x21,0xbc,0xcf,0x08, 0xef,0x15,0xe8,0xe6,
+0xba,0xe7,0x9b,0xd9, 0x4a,0x6f,0x36,0xce, 0xea,0x9f,0x09,0xd4, 0x29,0xb0,0x7c,0xd6,
+0x31,0xa4,0xb2,0xaf, 0x2a,0x3f,0x23,0x31, 0xc6,0xa5,0x94,0x30, 0x35,0xa2,0x66,0xc0,
+0x74,0x4e,0xbc,0x37, 0xfc,0x82,0xca,0xa6, 0xe0,0x90,0xd0,0xb0, 0x33,0xa7,0xd8,0x15,
+0xf1,0x04,0x98,0x4a, 0x41,0xec,0xda,0xf7, 0x7f,0xcd,0x50,0x0e, 0x17,0x91,0xf6,0x2f,
+0x76,0x4d,0xd6,0x8d, 0x43,0xef,0xb0,0x4d, 0xcc,0xaa,0x4d,0x54, 0xe4,0x96,0x04,0xdf,
+0x9e,0xd1,0xb5,0xe3, 0x4c,0x6a,0x88,0x1b, 0xc1,0x2c,0x1f,0xb8, 0x46,0x65,0x51,0x7f,
+0x9d,0x5e,0xea,0x04, 0x01,0x8c,0x35,0x5d, 0xfa,0x87,0x74,0x73, 0xfb,0x0b,0x41,0x2e,
+0xb3,0x67,0x1d,0x5a, 0x92,0xdb,0xd2,0x52, 0xe9,0x10,0x56,0x33, 0x6d,0xd6,0x47,0x13,
+0x9a,0xd7,0x61,0x8c, 0x37,0xa1,0x0c,0x7a, 0x59,0xf8,0x14,0x8e, 0xeb,0x13,0x3c,0x89,
+0xce,0xa9,0x27,0xee, 0xb7,0x61,0xc9,0x35, 0xe1,0x1c,0xe5,0xed, 0x7a,0x47,0xb1,0x3c,
+0x9c,0xd2,0xdf,0x59, 0x55,0xf2,0x73,0x3f, 0x18,0x14,0xce,0x79, 0x73,0xc7,0x37,0xbf,
+0x53,0xf7,0xcd,0xea, 0x5f,0xfd,0xaa,0x5b, 0xdf,0x3d,0x6f,0x14, 0x78,0x44,0xdb,0x86,
+0xca,0xaf,0xf3,0x81, 0xb9,0x68,0xc4,0x3e, 0x38,0x24,0x34,0x2c, 0xc2,0xa3,0x40,0x5f,
+0x16,0x1d,0xc3,0x72, 0xbc,0xe2,0x25,0x0c, 0x28,0x3c,0x49,0x8b, 0xff,0x0d,0x95,0x41,
+0x39,0xa8,0x01,0x71, 0x08,0x0c,0xb3,0xde, 0xd8,0xb4,0xe4,0x9c, 0x64,0x56,0xc1,0x90,
+0x7b,0xcb,0x84,0x61, 0xd5,0x32,0xb6,0x70, 0x48,0x6c,0x5c,0x74, 0xd0,0xb8,0x57,0x42
+},{
+0x50,0x51,0xf4,0xa7, 0x53,0x7e,0x41,0x65, 0xc3,0x1a,0x17,0xa4, 0x96,0x3a,0x27,0x5e,
+0xcb,0x3b,0xab,0x6b, 0xf1,0x1f,0x9d,0x45, 0xab,0xac,0xfa,0x58, 0x93,0x4b,0xe3,0x03,
+0x55,0x20,0x30,0xfa, 0xf6,0xad,0x76,0x6d, 0x91,0x88,0xcc,0x76, 0x25,0xf5,0x02,0x4c,
+0xfc,0x4f,0xe5,0xd7, 0xd7,0xc5,0x2a,0xcb, 0x80,0x26,0x35,0x44, 0x8f,0xb5,0x62,0xa3,
+0x49,0xde,0xb1,0x5a, 0x67,0x25,0xba,0x1b, 0x98,0x45,0xea,0x0e, 0xe1,0x5d,0xfe,0xc0,
+0x02,0xc3,0x2f,0x75, 0x12,0x81,0x4c,0xf0, 0xa3,0x8d,0x46,0x97, 0xc6,0x6b,0xd3,0xf9,
+0xe7,0x03,0x8f,0x5f, 0x95,0x15,0x92,0x9c, 0xeb,0xbf,0x6d,0x7a, 0xda,0x95,0x52,0x59,
+0x2d,0xd4,0xbe,0x83, 0xd3,0x58,0x74,0x21, 0x29,0x49,0xe0,0x69, 0x44,0x8e,0xc9,0xc8,
+0x6a,0x75,0xc2,0x89, 0x78,0xf4,0x8e,0x79, 0x6b,0x99,0x58,0x3e, 0xdd,0x27,0xb9,0x71,
+0xb6,0xbe,0xe1,0x4f, 0x17,0xf0,0x88,0xad, 0x66,0xc9,0x20,0xac, 0xb4,0x7d,0xce,0x3a,
+0x18,0x63,0xdf,0x4a, 0x82,0xe5,0x1a,0x31, 0x60,0x97,0x51,0x33, 0x45,0x62,0x53,0x7f,
+0xe0,0xb1,0x64,0x77, 0x84,0xbb,0x6b,0xae, 0x1c,0xfe,0x81,0xa0, 0x94,0xf9,0x08,0x2b,
+0x58,0x70,0x48,0x68, 0x19,0x8f,0x45,0xfd, 0x87,0x94,0xde,0x6c, 0xb7,0x52,0x7b,0xf8,
+0x23,0xab,0x73,0xd3, 0xe2,0x72,0x4b,0x02, 0x57,0xe3,0x1f,0x8f, 0x2a,0x66,0x55,0xab,
+0x07,0xb2,0xeb,0x28, 0x03,0x2f,0xb5,0xc2, 0x9a,0x86,0xc5,0x7b, 0xa5,0xd3,0x37,0x08,
+0xf2,0x30,0x28,0x87, 0xb2,0x23,0xbf,0xa5, 0xba,0x02,0x03,0x6a, 0x5c,0xed,0x16,0x82,
+0x2b,0x8a,0xcf,0x1c, 0x92,0xa7,0x79,0xb4, 0xf0,0xf3,0x07,0xf2, 0xa1,0x4e,0x69,0xe2,
+0xcd,0x65,0xda,0xf4, 0xd5,0x06,0x05,0xbe, 0x1f,0xd1,0x34,0x62, 0x8a,0xc4,0xa6,0xfe,
+0x9d,0x34,0x2e,0x53, 0xa0,0xa2,0xf3,0x55, 0x32,0x05,0x8a,0xe1, 0x75,0xa4,0xf6,0xeb,
+0x39,0x0b,0x83,0xec, 0xaa,0x40,0x60,0xef, 0x06,0x5e,0x71,0x9f, 0x51,0xbd,0x6e,0x10,
+0xf9,0x3e,0x21,0x8a, 0x3d,0x96,0xdd,0x06, 0xae,0xdd,0x3e,0x05, 0x46,0x4d,0xe6,0xbd,
+0xb5,0x91,0x54,0x8d, 0x05,0x71,0xc4,0x5d, 0x6f,0x04,0x06,0xd4, 0xff,0x60,0x50,0x15,
+0x24,0x19,0x98,0xfb, 0x97,0xd6,0xbd,0xe9, 0xcc,0x89,0x40,0x43, 0x77,0x67,0xd9,0x9e,
+0xbd,0xb0,0xe8,0x42, 0x88,0x07,0x89,0x8b, 0x38,0xe7,0x19,0x5b, 0xdb,0x79,0xc8,0xee,
+0x47,0xa1,0x7c,0x0a, 0xe9,0x7c,0x42,0x0f, 0xc9,0xf8,0x84,0x1e, 0x00,0x00,0x00,0x00,
+0x83,0x09,0x80,0x86, 0x48,0x32,0x2b,0xed, 0xac,0x1e,0x11,0x70, 0x4e,0x6c,0x5a,0x72,
+0xfb,0xfd,0x0e,0xff, 0x56,0x0f,0x85,0x38, 0x1e,0x3d,0xae,0xd5, 0x27,0x36,0x2d,0x39,
+0x64,0x0a,0x0f,0xd9, 0x21,0x68,0x5c,0xa6, 0xd1,0x9b,0x5b,0x54, 0x3a,0x24,0x36,0x2e,
+0xb1,0x0c,0x0a,0x67, 0x0f,0x93,0x57,0xe7, 0xd2,0xb4,0xee,0x96, 0x9e,0x1b,0x9b,0x91,
+0x4f,0x80,0xc0,0xc5, 0xa2,0x61,0xdc,0x20, 0x69,0x5a,0x77,0x4b, 0x16,0x1c,0x12,0x1a,
+0x0a,0xe2,0x93,0xba, 0xe5,0xc0,0xa0,0x2a, 0x43,0x3c,0x22,0xe0, 0x1d,0x12,0x1b,0x17,
+0x0b,0x0e,0x09,0x0d, 0xad,0xf2,0x8b,0xc7, 0xb9,0x2d,0xb6,0xa8, 0xc8,0x14,0x1e,0xa9,
+0x85,0x57,0xf1,0x19, 0x4c,0xaf,0x75,0x07, 0xbb,0xee,0x99,0xdd, 0xfd,0xa3,0x7f,0x60,
+0x9f,0xf7,0x01,0x26, 0xbc,0x5c,0x72,0xf5, 0xc5,0x44,0x66,0x3b, 0x34,0x5b,0xfb,0x7e,
+0x76,0x8b,0x43,0x29, 0xdc,0xcb,0x23,0xc6, 0x68,0xb6,0xed,0xfc, 0x63,0xb8,0xe4,0xf1,
+0xca,0xd7,0x31,0xdc, 0x10,0x42,0x63,0x85, 0x40,0x13,0x97,0x22, 0x20,0x84,0xc6,0x11,
+0x7d,0x85,0x4a,0x24, 0xf8,0xd2,0xbb,0x3d, 0x11,0xae,0xf9,0x32, 0x6d,0xc7,0x29,0xa1,
+0x4b,0x1d,0x9e,0x2f, 0xf3,0xdc,0xb2,0x30, 0xec,0x0d,0x86,0x52, 0xd0,0x77,0xc1,0xe3,
+0x6c,0x2b,0xb3,0x16, 0x99,0xa9,0x70,0xb9, 0xfa,0x11,0x94,0x48, 0x22,0x47,0xe9,0x64,
+0xc4,0xa8,0xfc,0x8c, 0x1a,0xa0,0xf0,0x3f, 0xd8,0x56,0x7d,0x2c, 0xef,0x22,0x33,0x90,
+0xc7,0x87,0x49,0x4e, 0xc1,0xd9,0x38,0xd1, 0xfe,0x8c,0xca,0xa2, 0x36,0x98,0xd4,0x0b,
+0xcf,0xa6,0xf5,0x81, 0x28,0xa5,0x7a,0xde, 0x26,0xda,0xb7,0x8e, 0xa4,0x3f,0xad,0xbf,
+0xe4,0x2c,0x3a,0x9d, 0x0d,0x50,0x78,0x92, 0x9b,0x6a,0x5f,0xcc, 0x62,0x54,0x7e,0x46,
+0xc2,0xf6,0x8d,0x13, 0xe8,0x90,0xd8,0xb8, 0x5e,0x2e,0x39,0xf7, 0xf5,0x82,0xc3,0xaf,
+0xbe,0x9f,0x5d,0x80, 0x7c,0x69,0xd0,0x93, 0xa9,0x6f,0xd5,0x2d, 0xb3,0xcf,0x25,0x12,
+0x3b,0xc8,0xac,0x99, 0xa7,0x10,0x18,0x7d, 0x6e,0xe8,0x9c,0x63, 0x7b,0xdb,0x3b,0xbb,
+0x09,0xcd,0x26,0x78, 0xf4,0x6e,0x59,0x18, 0x01,0xec,0x9a,0xb7, 0xa8,0x83,0x4f,0x9a,
+0x65,0xe6,0x95,0x6e, 0x7e,0xaa,0xff,0xe6, 0x08,0x21,0xbc,0xcf, 0xe6,0xef,0x15,0xe8,
+0xd9,0xba,0xe7,0x9b, 0xce,0x4a,0x6f,0x36, 0xd4,0xea,0x9f,0x09, 0xd6,0x29,0xb0,0x7c,
+0xaf,0x31,0xa4,0xb2, 0x31,0x2a,0x3f,0x23, 0x30,0xc6,0xa5,0x94, 0xc0,0x35,0xa2,0x66,
+0x37,0x74,0x4e,0xbc, 0xa6,0xfc,0x82,0xca, 0xb0,0xe0,0x90,0xd0, 0x15,0x33,0xa7,0xd8,
+0x4a,0xf1,0x04,0x98, 0xf7,0x41,0xec,0xda, 0x0e,0x7f,0xcd,0x50, 0x2f,0x17,0x91,0xf6,
+0x8d,0x76,0x4d,0xd6, 0x4d,0x43,0xef,0xb0, 0x54,0xcc,0xaa,0x4d, 0xdf,0xe4,0x96,0x04,
+0xe3,0x9e,0xd1,0xb5, 0x1b,0x4c,0x6a,0x88, 0xb8,0xc1,0x2c,0x1f, 0x7f,0x46,0x65,0x51,
+0x04,0x9d,0x5e,0xea, 0x5d,0x01,0x8c,0x35, 0x73,0xfa,0x87,0x74, 0x2e,0xfb,0x0b,0x41,
+0x5a,0xb3,0x67,0x1d, 0x52,0x92,0xdb,0xd2, 0x33,0xe9,0x10,0x56, 0x13,0x6d,0xd6,0x47,
+0x8c,0x9a,0xd7,0x61, 0x7a,0x37,0xa1,0x0c, 0x8e,0x59,0xf8,0x14, 0x89,0xeb,0x13,0x3c,
+0xee,0xce,0xa9,0x27, 0x35,0xb7,0x61,0xc9, 0xed,0xe1,0x1c,0xe5, 0x3c,0x7a,0x47,0xb1,
+0x59,0x9c,0xd2,0xdf, 0x3f,0x55,0xf2,0x73, 0x79,0x18,0x14,0xce, 0xbf,0x73,0xc7,0x37,
+0xea,0x53,0xf7,0xcd, 0x5b,0x5f,0xfd,0xaa, 0x14,0xdf,0x3d,0x6f, 0x86,0x78,0x44,0xdb,
+0x81,0xca,0xaf,0xf3, 0x3e,0xb9,0x68,0xc4, 0x2c,0x38,0x24,0x34, 0x5f,0xc2,0xa3,0x40,
+0x72,0x16,0x1d,0xc3, 0x0c,0xbc,0xe2,0x25, 0x8b,0x28,0x3c,0x49, 0x41,0xff,0x0d,0x95,
+0x71,0x39,0xa8,0x01, 0xde,0x08,0x0c,0xb3, 0x9c,0xd8,0xb4,0xe4, 0x90,0x64,0x56,0xc1,
+0x61,0x7b,0xcb,0x84, 0x70,0xd5,0x32,0xb6, 0x74,0x48,0x6c,0x5c, 0x42,0xd0,0xb8,0x57
+},{
+0xa7,0x50,0x51,0xf4, 0x65,0x53,0x7e,0x41, 0xa4,0xc3,0x1a,0x17, 0x5e,0x96,0x3a,0x27,
+0x6b,0xcb,0x3b,0xab, 0x45,0xf1,0x1f,0x9d, 0x58,0xab,0xac,0xfa, 0x03,0x93,0x4b,0xe3,
+0xfa,0x55,0x20,0x30, 0x6d,0xf6,0xad,0x76, 0x76,0x91,0x88,0xcc, 0x4c,0x25,0xf5,0x02,
+0xd7,0xfc,0x4f,0xe5, 0xcb,0xd7,0xc5,0x2a, 0x44,0x80,0x26,0x35, 0xa3,0x8f,0xb5,0x62,
+0x5a,0x49,0xde,0xb1, 0x1b,0x67,0x25,0xba, 0x0e,0x98,0x45,0xea, 0xc0,0xe1,0x5d,0xfe,
+0x75,0x02,0xc3,0x2f, 0xf0,0x12,0x81,0x4c, 0x97,0xa3,0x8d,0x46, 0xf9,0xc6,0x6b,0xd3,
+0x5f,0xe7,0x03,0x8f, 0x9c,0x95,0x15,0x92, 0x7a,0xeb,0xbf,0x6d, 0x59,0xda,0x95,0x52,
+0x83,0x2d,0xd4,0xbe, 0x21,0xd3,0x58,0x74, 0x69,0x29,0x49,0xe0, 0xc8,0x44,0x8e,0xc9,
+0x89,0x6a,0x75,0xc2, 0x79,0x78,0xf4,0x8e, 0x3e,0x6b,0x99,0x58, 0x71,0xdd,0x27,0xb9,
+0x4f,0xb6,0xbe,0xe1, 0xad,0x17,0xf0,0x88, 0xac,0x66,0xc9,0x20, 0x3a,0xb4,0x7d,0xce,
+0x4a,0x18,0x63,0xdf, 0x31,0x82,0xe5,0x1a, 0x33,0x60,0x97,0x51, 0x7f,0x45,0x62,0x53,
+0x77,0xe0,0xb1,0x64, 0xae,0x84,0xbb,0x6b, 0xa0,0x1c,0xfe,0x81, 0x2b,0x94,0xf9,0x08,
+0x68,0x58,0x70,0x48, 0xfd,0x19,0x8f,0x45, 0x6c,0x87,0x94,0xde, 0xf8,0xb7,0x52,0x7b,
+0xd3,0x23,0xab,0x73, 0x02,0xe2,0x72,0x4b, 0x8f,0x57,0xe3,0x1f, 0xab,0x2a,0x66,0x55,
+0x28,0x07,0xb2,0xeb, 0xc2,0x03,0x2f,0xb5, 0x7b,0x9a,0x86,0xc5, 0x08,0xa5,0xd3,0x37,
+0x87,0xf2,0x30,0x28, 0xa5,0xb2,0x23,0xbf, 0x6a,0xba,0x02,0x03, 0x82,0x5c,0xed,0x16,
+0x1c,0x2b,0x8a,0xcf, 0xb4,0x92,0xa7,0x79, 0xf2,0xf0,0xf3,0x07, 0xe2,0xa1,0x4e,0x69,
+0xf4,0xcd,0x65,0xda, 0xbe,0xd5,0x06,0x05, 0x62,0x1f,0xd1,0x34, 0xfe,0x8a,0xc4,0xa6,
+0x53,0x9d,0x34,0x2e, 0x55,0xa0,0xa2,0xf3, 0xe1,0x32,0x05,0x8a, 0xeb,0x75,0xa4,0xf6,
+0xec,0x39,0x0b,0x83, 0xef,0xaa,0x40,0x60, 0x9f,0x06,0x5e,0x71, 0x10,0x51,0xbd,0x6e,
+0x8a,0xf9,0x3e,0x21, 0x06,0x3d,0x96,0xdd, 0x05,0xae,0xdd,0x3e, 0xbd,0x46,0x4d,0xe6,
+0x8d,0xb5,0x91,0x54, 0x5d,0x05,0x71,0xc4, 0xd4,0x6f,0x04,0x06, 0x15,0xff,0x60,0x50,
+0xfb,0x24,0x19,0x98, 0xe9,0x97,0xd6,0xbd, 0x43,0xcc,0x89,0x40, 0x9e,0x77,0x67,0xd9,
+0x42,0xbd,0xb0,0xe8, 0x8b,0x88,0x07,0x89, 0x5b,0x38,0xe7,0x19, 0xee,0xdb,0x79,0xc8,
+0x0a,0x47,0xa1,0x7c, 0x0f,0xe9,0x7c,0x42, 0x1e,0xc9,0xf8,0x84, 0x00,0x00,0x00,0x00,
+0x86,0x83,0x09,0x80, 0xed,0x48,0x32,0x2b, 0x70,0xac,0x1e,0x11, 0x72,0x4e,0x6c,0x5a,
+0xff,0xfb,0xfd,0x0e, 0x38,0x56,0x0f,0x85, 0xd5,0x1e,0x3d,0xae, 0x39,0x27,0x36,0x2d,
+0xd9,0x64,0x0a,0x0f, 0xa6,0x21,0x68,0x5c, 0x54,0xd1,0x9b,0x5b, 0x2e,0x3a,0x24,0x36,
+0x67,0xb1,0x0c,0x0a, 0xe7,0x0f,0x93,0x57, 0x96,0xd2,0xb4,0xee, 0x91,0x9e,0x1b,0x9b,
+0xc5,0x4f,0x80,0xc0, 0x20,0xa2,0x61,0xdc, 0x4b,0x69,0x5a,0x77, 0x1a,0x16,0x1c,0x12,
+0xba,0x0a,0xe2,0x93, 0x2a,0xe5,0xc0,0xa0, 0xe0,0x43,0x3c,0x22, 0x17,0x1d,0x12,0x1b,
+0x0d,0x0b,0x0e,0x09, 0xc7,0xad,0xf2,0x8b, 0xa8,0xb9,0x2d,0xb6, 0xa9,0xc8,0x14,0x1e,
+0x19,0x85,0x57,0xf1, 0x07,0x4c,0xaf,0x75, 0xdd,0xbb,0xee,0x99, 0x60,0xfd,0xa3,0x7f,
+0x26,0x9f,0xf7,0x01, 0xf5,0xbc,0x5c,0x72, 0x3b,0xc5,0x44,0x66, 0x7e,0x34,0x5b,0xfb,
+0x29,0x76,0x8b,0x43, 0xc6,0xdc,0xcb,0x23, 0xfc,0x68,0xb6,0xed, 0xf1,0x63,0xb8,0xe4,
+0xdc,0xca,0xd7,0x31, 0x85,0x10,0x42,0x63, 0x22,0x40,0x13,0x97, 0x11,0x20,0x84,0xc6,
+0x24,0x7d,0x85,0x4a, 0x3d,0xf8,0xd2,0xbb, 0x32,0x11,0xae,0xf9, 0xa1,0x6d,0xc7,0x29,
+0x2f,0x4b,0x1d,0x9e, 0x30,0xf3,0xdc,0xb2, 0x52,0xec,0x0d,0x86, 0xe3,0xd0,0x77,0xc1,
+0x16,0x6c,0x2b,0xb3, 0xb9,0x99,0xa9,0x70, 0x48,0xfa,0x11,0x94, 0x64,0x22,0x47,0xe9,
+0x8c,0xc4,0xa8,0xfc, 0x3f,0x1a,0xa0,0xf0, 0x2c,0xd8,0x56,0x7d, 0x90,0xef,0x22,0x33,
+0x4e,0xc7,0x87,0x49, 0xd1,0xc1,0xd9,0x38, 0xa2,0xfe,0x8c,0xca, 0x0b,0x36,0x98,0xd4,
+0x81,0xcf,0xa6,0xf5, 0xde,0x28,0xa5,0x7a, 0x8e,0x26,0xda,0xb7, 0xbf,0xa4,0x3f,0xad,
+0x9d,0xe4,0x2c,0x3a, 0x92,0x0d,0x50,0x78, 0xcc,0x9b,0x6a,0x5f, 0x46,0x62,0x54,0x7e,
+0x13,0xc2,0xf6,0x8d, 0xb8,0xe8,0x90,0xd8, 0xf7,0x5e,0x2e,0x39, 0xaf,0xf5,0x82,0xc3,
+0x80,0xbe,0x9f,0x5d, 0x93,0x7c,0x69,0xd0, 0x2d,0xa9,0x6f,0xd5, 0x12,0xb3,0xcf,0x25,
+0x99,0x3b,0xc8,0xac, 0x7d,0xa7,0x10,0x18, 0x63,0x6e,0xe8,0x9c, 0xbb,0x7b,0xdb,0x3b,
+0x78,0x09,0xcd,0x26, 0x18,0xf4,0x6e,0x59, 0xb7,0x01,0xec,0x9a, 0x9a,0xa8,0x83,0x4f,
+0x6e,0x65,0xe6,0x95, 0xe6,0x7e,0xaa,0xff, 0xcf,0x08,0x21,0xbc, 0xe8,0xe6,0xef,0x15,
+0x9b,0xd9,0xba,0xe7, 0x36,0xce,0x4a,0x6f, 0x09,0xd4,0xea,0x9f, 0x7c,0xd6,0x29,0xb0,
+0xb2,0xaf,0x31,0xa4, 0x23,0x31,0x2a,0x3f, 0x94,0x30,0xc6,0xa5, 0x66,0xc0,0x35,0xa2,
+0xbc,0x37,0x74,0x4e, 0xca,0xa6,0xfc,0x82, 0xd0,0xb0,0xe0,0x90, 0xd8,0x15,0x33,0xa7,
+0x98,0x4a,0xf1,0x04, 0xda,0xf7,0x41,0xec, 0x50,0x0e,0x7f,0xcd, 0xf6,0x2f,0x17,0x91,
+0xd6,0x8d,0x76,0x4d, 0xb0,0x4d,0x43,0xef, 0x4d,0x54,0xcc,0xaa, 0x04,0xdf,0xe4,0x96,
+0xb5,0xe3,0x9e,0xd1, 0x88,0x1b,0x4c,0x6a, 0x1f,0xb8,0xc1,0x2c, 0x51,0x7f,0x46,0x65,
+0xea,0x04,0x9d,0x5e, 0x35,0x5d,0x01,0x8c, 0x74,0x73,0xfa,0x87, 0x41,0x2e,0xfb,0x0b,
+0x1d,0x5a,0xb3,0x67, 0xd2,0x52,0x92,0xdb, 0x56,0x33,0xe9,0x10, 0x47,0x13,0x6d,0xd6,
+0x61,0x8c,0x9a,0xd7, 0x0c,0x7a,0x37,0xa1, 0x14,0x8e,0x59,0xf8, 0x3c,0x89,0xeb,0x13,
+0x27,0xee,0xce,0xa9, 0xc9,0x35,0xb7,0x61, 0xe5,0xed,0xe1,0x1c, 0xb1,0x3c,0x7a,0x47,
+0xdf,0x59,0x9c,0xd2, 0x73,0x3f,0x55,0xf2, 0xce,0x79,0x18,0x14, 0x37,0xbf,0x73,0xc7,
+0xcd,0xea,0x53,0xf7, 0xaa,0x5b,0x5f,0xfd, 0x6f,0x14,0xdf,0x3d, 0xdb,0x86,0x78,0x44,
+0xf3,0x81,0xca,0xaf, 0xc4,0x3e,0xb9,0x68, 0x34,0x2c,0x38,0x24, 0x40,0x5f,0xc2,0xa3,
+0xc3,0x72,0x16,0x1d, 0x25,0x0c,0xbc,0xe2, 0x49,0x8b,0x28,0x3c, 0x95,0x41,0xff,0x0d,
+0x01,0x71,0x39,0xa8, 0xb3,0xde,0x08,0x0c, 0xe4,0x9c,0xd8,0xb4, 0xc1,0x90,0x64,0x56,
+0x84,0x61,0x7b,0xcb, 0xb6,0x70,0xd5,0x32, 0x5c,0x74,0x48,0x6c, 0x57,0x42,0xd0,0xb8
+},{
+0xf4,0xa7,0x50,0x51, 0x41,0x65,0x53,0x7e, 0x17,0xa4,0xc3,0x1a, 0x27,0x5e,0x96,0x3a,
+0xab,0x6b,0xcb,0x3b, 0x9d,0x45,0xf1,0x1f, 0xfa,0x58,0xab,0xac, 0xe3,0x03,0x93,0x4b,
+0x30,0xfa,0x55,0x20, 0x76,0x6d,0xf6,0xad, 0xcc,0x76,0x91,0x88, 0x02,0x4c,0x25,0xf5,
+0xe5,0xd7,0xfc,0x4f, 0x2a,0xcb,0xd7,0xc5, 0x35,0x44,0x80,0x26, 0x62,0xa3,0x8f,0xb5,
+0xb1,0x5a,0x49,0xde, 0xba,0x1b,0x67,0x25, 0xea,0x0e,0x98,0x45, 0xfe,0xc0,0xe1,0x5d,
+0x2f,0x75,0x02,0xc3, 0x4c,0xf0,0x12,0x81, 0x46,0x97,0xa3,0x8d, 0xd3,0xf9,0xc6,0x6b,
+0x8f,0x5f,0xe7,0x03, 0x92,0x9c,0x95,0x15, 0x6d,0x7a,0xeb,0xbf, 0x52,0x59,0xda,0x95,
+0xbe,0x83,0x2d,0xd4, 0x74,0x21,0xd3,0x58, 0xe0,0x69,0x29,0x49, 0xc9,0xc8,0x44,0x8e,
+0xc2,0x89,0x6a,0x75, 0x8e,0x79,0x78,0xf4, 0x58,0x3e,0x6b,0x99, 0xb9,0x71,0xdd,0x27,
+0xe1,0x4f,0xb6,0xbe, 0x88,0xad,0x17,0xf0, 0x20,0xac,0x66,0xc9, 0xce,0x3a,0xb4,0x7d,
+0xdf,0x4a,0x18,0x63, 0x1a,0x31,0x82,0xe5, 0x51,0x33,0x60,0x97, 0x53,0x7f,0x45,0x62,
+0x64,0x77,0xe0,0xb1, 0x6b,0xae,0x84,0xbb, 0x81,0xa0,0x1c,0xfe, 0x08,0x2b,0x94,0xf9,
+0x48,0x68,0x58,0x70, 0x45,0xfd,0x19,0x8f, 0xde,0x6c,0x87,0x94, 0x7b,0xf8,0xb7,0x52,
+0x73,0xd3,0x23,0xab, 0x4b,0x02,0xe2,0x72, 0x1f,0x8f,0x57,0xe3, 0x55,0xab,0x2a,0x66,
+0xeb,0x28,0x07,0xb2, 0xb5,0xc2,0x03,0x2f, 0xc5,0x7b,0x9a,0x86, 0x37,0x08,0xa5,0xd3,
+0x28,0x87,0xf2,0x30, 0xbf,0xa5,0xb2,0x23, 0x03,0x6a,0xba,0x02, 0x16,0x82,0x5c,0xed,
+0xcf,0x1c,0x2b,0x8a, 0x79,0xb4,0x92,0xa7, 0x07,0xf2,0xf0,0xf3, 0x69,0xe2,0xa1,0x4e,
+0xda,0xf4,0xcd,0x65, 0x05,0xbe,0xd5,0x06, 0x34,0x62,0x1f,0xd1, 0xa6,0xfe,0x8a,0xc4,
+0x2e,0x53,0x9d,0x34, 0xf3,0x55,0xa0,0xa2, 0x8a,0xe1,0x32,0x05, 0xf6,0xeb,0x75,0xa4,
+0x83,0xec,0x39,0x0b, 0x60,0xef,0xaa,0x40, 0x71,0x9f,0x06,0x5e, 0x6e,0x10,0x51,0xbd,
+0x21,0x8a,0xf9,0x3e, 0xdd,0x06,0x3d,0x96, 0x3e,0x05,0xae,0xdd, 0xe6,0xbd,0x46,0x4d,
+0x54,0x8d,0xb5,0x91, 0xc4,0x5d,0x05,0x71, 0x06,0xd4,0x6f,0x04, 0x50,0x15,0xff,0x60,
+0x98,0xfb,0x24,0x19, 0xbd,0xe9,0x97,0xd6, 0x40,0x43,0xcc,0x89, 0xd9,0x9e,0x77,0x67,
+0xe8,0x42,0xbd,0xb0, 0x89,0x8b,0x88,0x07, 0x19,0x5b,0x38,0xe7, 0xc8,0xee,0xdb,0x79,
+0x7c,0x0a,0x47,0xa1, 0x42,0x0f,0xe9,0x7c, 0x84,0x1e,0xc9,0xf8, 0x00,0x00,0x00,0x00,
+0x80,0x86,0x83,0x09, 0x2b,0xed,0x48,0x32, 0x11,0x70,0xac,0x1e, 0x5a,0x72,0x4e,0x6c,
+0x0e,0xff,0xfb,0xfd, 0x85,0x38,0x56,0x0f, 0xae,0xd5,0x1e,0x3d, 0x2d,0x39,0x27,0x36,
+0x0f,0xd9,0x64,0x0a, 0x5c,0xa6,0x21,0x68, 0x5b,0x54,0xd1,0x9b, 0x36,0x2e,0x3a,0x24,
+0x0a,0x67,0xb1,0x0c, 0x57,0xe7,0x0f,0x93, 0xee,0x96,0xd2,0xb4, 0x9b,0x91,0x9e,0x1b,
+0xc0,0xc5,0x4f,0x80, 0xdc,0x20,0xa2,0x61, 0x77,0x4b,0x69,0x5a, 0x12,0x1a,0x16,0x1c,
+0x93,0xba,0x0a,0xe2, 0xa0,0x2a,0xe5,0xc0, 0x22,0xe0,0x43,0x3c, 0x1b,0x17,0x1d,0x12,
+0x09,0x0d,0x0b,0x0e, 0x8b,0xc7,0xad,0xf2, 0xb6,0xa8,0xb9,0x2d, 0x1e,0xa9,0xc8,0x14,
+0xf1,0x19,0x85,0x57, 0x75,0x07,0x4c,0xaf, 0x99,0xdd,0xbb,0xee, 0x7f,0x60,0xfd,0xa3,
+0x01,0x26,0x9f,0xf7, 0x72,0xf5,0xbc,0x5c, 0x66,0x3b,0xc5,0x44, 0xfb,0x7e,0x34,0x5b,
+0x43,0x29,0x76,0x8b, 0x23,0xc6,0xdc,0xcb, 0xed,0xfc,0x68,0xb6, 0xe4,0xf1,0x63,0xb8,
+0x31,0xdc,0xca,0xd7, 0x63,0x85,0x10,0x42, 0x97,0x22,0x40,0x13, 0xc6,0x11,0x20,0x84,
+0x4a,0x24,0x7d,0x85, 0xbb,0x3d,0xf8,0xd2, 0xf9,0x32,0x11,0xae, 0x29,0xa1,0x6d,0xc7,
+0x9e,0x2f,0x4b,0x1d, 0xb2,0x30,0xf3,0xdc, 0x86,0x52,0xec,0x0d, 0xc1,0xe3,0xd0,0x77,
+0xb3,0x16,0x6c,0x2b, 0x70,0xb9,0x99,0xa9, 0x94,0x48,0xfa,0x11, 0xe9,0x64,0x22,0x47,
+0xfc,0x8c,0xc4,0xa8, 0xf0,0x3f,0x1a,0xa0, 0x7d,0x2c,0xd8,0x56, 0x33,0x90,0xef,0x22,
+0x49,0x4e,0xc7,0x87, 0x38,0xd1,0xc1,0xd9, 0xca,0xa2,0xfe,0x8c, 0xd4,0x0b,0x36,0x98,
+0xf5,0x81,0xcf,0xa6, 0x7a,0xde,0x28,0xa5, 0xb7,0x8e,0x26,0xda, 0xad,0xbf,0xa4,0x3f,
+0x3a,0x9d,0xe4,0x2c, 0x78,0x92,0x0d,0x50, 0x5f,0xcc,0x9b,0x6a, 0x7e,0x46,0x62,0x54,
+0x8d,0x13,0xc2,0xf6, 0xd8,0xb8,0xe8,0x90, 0x39,0xf7,0x5e,0x2e, 0xc3,0xaf,0xf5,0x82,
+0x5d,0x80,0xbe,0x9f, 0xd0,0x93,0x7c,0x69, 0xd5,0x2d,0xa9,0x6f, 0x25,0x12,0xb3,0xcf,
+0xac,0x99,0x3b,0xc8, 0x18,0x7d,0xa7,0x10, 0x9c,0x63,0x6e,0xe8, 0x3b,0xbb,0x7b,0xdb,
+0x26,0x78,0x09,0xcd, 0x59,0x18,0xf4,0x6e, 0x9a,0xb7,0x01,0xec, 0x4f,0x9a,0xa8,0x83,
+0x95,0x6e,0x65,0xe6, 0xff,0xe6,0x7e,0xaa, 0xbc,0xcf,0x08,0x21, 0x15,0xe8,0xe6,0xef,
+0xe7,0x9b,0xd9,0xba, 0x6f,0x36,0xce,0x4a, 0x9f,0x09,0xd4,0xea, 0xb0,0x7c,0xd6,0x29,
+0xa4,0xb2,0xaf,0x31, 0x3f,0x23,0x31,0x2a, 0xa5,0x94,0x30,0xc6, 0xa2,0x66,0xc0,0x35,
+0x4e,0xbc,0x37,0x74, 0x82,0xca,0xa6,0xfc, 0x90,0xd0,0xb0,0xe0, 0xa7,0xd8,0x15,0x33,
+0x04,0x98,0x4a,0xf1, 0xec,0xda,0xf7,0x41, 0xcd,0x50,0x0e,0x7f, 0x91,0xf6,0x2f,0x17,
+0x4d,0xd6,0x8d,0x76, 0xef,0xb0,0x4d,0x43, 0xaa,0x4d,0x54,0xcc, 0x96,0x04,0xdf,0xe4,
+0xd1,0xb5,0xe3,0x9e, 0x6a,0x88,0x1b,0x4c, 0x2c,0x1f,0xb8,0xc1, 0x65,0x51,0x7f,0x46,
+0x5e,0xea,0x04,0x9d, 0x8c,0x35,0x5d,0x01, 0x87,0x74,0x73,0xfa, 0x0b,0x41,0x2e,0xfb,
+0x67,0x1d,0x5a,0xb3, 0xdb,0xd2,0x52,0x92, 0x10,0x56,0x33,0xe9, 0xd6,0x47,0x13,0x6d,
+0xd7,0x61,0x8c,0x9a, 0xa1,0x0c,0x7a,0x37, 0xf8,0x14,0x8e,0x59, 0x13,0x3c,0x89,0xeb,
+0xa9,0x27,0xee,0xce, 0x61,0xc9,0x35,0xb7, 0x1c,0xe5,0xed,0xe1, 0x47,0xb1,0x3c,0x7a,
+0xd2,0xdf,0x59,0x9c, 0xf2,0x73,0x3f,0x55, 0x14,0xce,0x79,0x18, 0xc7,0x37,0xbf,0x73,
+0xf7,0xcd,0xea,0x53, 0xfd,0xaa,0x5b,0x5f, 0x3d,0x6f,0x14,0xdf, 0x44,0xdb,0x86,0x78,
+0xaf,0xf3,0x81,0xca, 0x68,0xc4,0x3e,0xb9, 0x24,0x34,0x2c,0x38, 0xa3,0x40,0x5f,0xc2,
+0x1d,0xc3,0x72,0x16, 0xe2,0x25,0x0c,0xbc, 0x3c,0x49,0x8b,0x28, 0x0d,0x95,0x41,0xff,
+0xa8,0x01,0x71,0x39, 0x0c,0xb3,0xde,0x08, 0xb4,0xe4,0x9c,0xd8, 0x56,0xc1,0x90,0x64,
+0xcb,0x84,0x61,0x7b, 0x32,0xb6,0x70,0xd5, 0x6c,0x5c,0x74,0x48, 0xb8,0x57,0x42,0xd0
+}};
+
+SYMCRYPT_ALIGN_AT(1024) const BYTE SymCryptAesInvMatrixMult[4][256][4] = {{    // For computing decryption round keys
+0x00,0x00,0x00,0x00, 0x0e,0x09,0x0d,0x0b, 0x1c,0x12,0x1a,0x16, 0x12,0x1b,0x17,0x1d,
+0x38,0x24,0x34,0x2c, 0x36,0x2d,0x39,0x27, 0x24,0x36,0x2e,0x3a, 0x2a,0x3f,0x23,0x31,
+0x70,0x48,0x68,0x58, 0x7e,0x41,0x65,0x53, 0x6c,0x5a,0x72,0x4e, 0x62,0x53,0x7f,0x45,
+0x48,0x6c,0x5c,0x74, 0x46,0x65,0x51,0x7f, 0x54,0x7e,0x46,0x62, 0x5a,0x77,0x4b,0x69,
+0xe0,0x90,0xd0,0xb0, 0xee,0x99,0xdd,0xbb, 0xfc,0x82,0xca,0xa6, 0xf2,0x8b,0xc7,0xad,
+0xd8,0xb4,0xe4,0x9c, 0xd6,0xbd,0xe9,0x97, 0xc4,0xa6,0xfe,0x8a, 0xca,0xaf,0xf3,0x81,
+0x90,0xd8,0xb8,0xe8, 0x9e,0xd1,0xb5,0xe3, 0x8c,0xca,0xa2,0xfe, 0x82,0xc3,0xaf,0xf5,
+0xa8,0xfc,0x8c,0xc4, 0xa6,0xf5,0x81,0xcf, 0xb4,0xee,0x96,0xd2, 0xba,0xe7,0x9b,0xd9,
+0xdb,0x3b,0xbb,0x7b, 0xd5,0x32,0xb6,0x70, 0xc7,0x29,0xa1,0x6d, 0xc9,0x20,0xac,0x66,
+0xe3,0x1f,0x8f,0x57, 0xed,0x16,0x82,0x5c, 0xff,0x0d,0x95,0x41, 0xf1,0x04,0x98,0x4a,
+0xab,0x73,0xd3,0x23, 0xa5,0x7a,0xde,0x28, 0xb7,0x61,0xc9,0x35, 0xb9,0x68,0xc4,0x3e,
+0x93,0x57,0xe7,0x0f, 0x9d,0x5e,0xea,0x04, 0x8f,0x45,0xfd,0x19, 0x81,0x4c,0xf0,0x12,
+0x3b,0xab,0x6b,0xcb, 0x35,0xa2,0x66,0xc0, 0x27,0xb9,0x71,0xdd, 0x29,0xb0,0x7c,0xd6,
+0x03,0x8f,0x5f,0xe7, 0x0d,0x86,0x52,0xec, 0x1f,0x9d,0x45,0xf1, 0x11,0x94,0x48,0xfa,
+0x4b,0xe3,0x03,0x93, 0x45,0xea,0x0e,0x98, 0x57,0xf1,0x19,0x85, 0x59,0xf8,0x14,0x8e,
+0x73,0xc7,0x37,0xbf, 0x7d,0xce,0x3a,0xb4, 0x6f,0xd5,0x2d,0xa9, 0x61,0xdc,0x20,0xa2,
+0xad,0x76,0x6d,0xf6, 0xa3,0x7f,0x60,0xfd, 0xb1,0x64,0x77,0xe0, 0xbf,0x6d,0x7a,0xeb,
+0x95,0x52,0x59,0xda, 0x9b,0x5b,0x54,0xd1, 0x89,0x40,0x43,0xcc, 0x87,0x49,0x4e,0xc7,
+0xdd,0x3e,0x05,0xae, 0xd3,0x37,0x08,0xa5, 0xc1,0x2c,0x1f,0xb8, 0xcf,0x25,0x12,0xb3,
+0xe5,0x1a,0x31,0x82, 0xeb,0x13,0x3c,0x89, 0xf9,0x08,0x2b,0x94, 0xf7,0x01,0x26,0x9f,
+0x4d,0xe6,0xbd,0x46, 0x43,0xef,0xb0,0x4d, 0x51,0xf4,0xa7,0x50, 0x5f,0xfd,0xaa,0x5b,
+0x75,0xc2,0x89,0x6a, 0x7b,0xcb,0x84,0x61, 0x69,0xd0,0x93,0x7c, 0x67,0xd9,0x9e,0x77,
+0x3d,0xae,0xd5,0x1e, 0x33,0xa7,0xd8,0x15, 0x21,0xbc,0xcf,0x08, 0x2f,0xb5,0xc2,0x03,
+0x05,0x8a,0xe1,0x32, 0x0b,0x83,0xec,0x39, 0x19,0x98,0xfb,0x24, 0x17,0x91,0xf6,0x2f,
+0x76,0x4d,0xd6,0x8d, 0x78,0x44,0xdb,0x86, 0x6a,0x5f,0xcc,0x9b, 0x64,0x56,0xc1,0x90,
+0x4e,0x69,0xe2,0xa1, 0x40,0x60,0xef,0xaa, 0x52,0x7b,0xf8,0xb7, 0x5c,0x72,0xf5,0xbc,
+0x06,0x05,0xbe,0xd5, 0x08,0x0c,0xb3,0xde, 0x1a,0x17,0xa4,0xc3, 0x14,0x1e,0xa9,0xc8,
+0x3e,0x21,0x8a,0xf9, 0x30,0x28,0x87,0xf2, 0x22,0x33,0x90,0xef, 0x2c,0x3a,0x9d,0xe4,
+0x96,0xdd,0x06,0x3d, 0x98,0xd4,0x0b,0x36, 0x8a,0xcf,0x1c,0x2b, 0x84,0xc6,0x11,0x20,
+0xae,0xf9,0x32,0x11, 0xa0,0xf0,0x3f,0x1a, 0xb2,0xeb,0x28,0x07, 0xbc,0xe2,0x25,0x0c,
+0xe6,0x95,0x6e,0x65, 0xe8,0x9c,0x63,0x6e, 0xfa,0x87,0x74,0x73, 0xf4,0x8e,0x79,0x78,
+0xde,0xb1,0x5a,0x49, 0xd0,0xb8,0x57,0x42, 0xc2,0xa3,0x40,0x5f, 0xcc,0xaa,0x4d,0x54,
+0x41,0xec,0xda,0xf7, 0x4f,0xe5,0xd7,0xfc, 0x5d,0xfe,0xc0,0xe1, 0x53,0xf7,0xcd,0xea,
+0x79,0xc8,0xee,0xdb, 0x77,0xc1,0xe3,0xd0, 0x65,0xda,0xf4,0xcd, 0x6b,0xd3,0xf9,0xc6,
+0x31,0xa4,0xb2,0xaf, 0x3f,0xad,0xbf,0xa4, 0x2d,0xb6,0xa8,0xb9, 0x23,0xbf,0xa5,0xb2,
+0x09,0x80,0x86,0x83, 0x07,0x89,0x8b,0x88, 0x15,0x92,0x9c,0x95, 0x1b,0x9b,0x91,0x9e,
+0xa1,0x7c,0x0a,0x47, 0xaf,0x75,0x07,0x4c, 0xbd,0x6e,0x10,0x51, 0xb3,0x67,0x1d,0x5a,
+0x99,0x58,0x3e,0x6b, 0x97,0x51,0x33,0x60, 0x85,0x4a,0x24,0x7d, 0x8b,0x43,0x29,0x76,
+0xd1,0x34,0x62,0x1f, 0xdf,0x3d,0x6f,0x14, 0xcd,0x26,0x78,0x09, 0xc3,0x2f,0x75,0x02,
+0xe9,0x10,0x56,0x33, 0xe7,0x19,0x5b,0x38, 0xf5,0x02,0x4c,0x25, 0xfb,0x0b,0x41,0x2e,
+0x9a,0xd7,0x61,0x8c, 0x94,0xde,0x6c,0x87, 0x86,0xc5,0x7b,0x9a, 0x88,0xcc,0x76,0x91,
+0xa2,0xf3,0x55,0xa0, 0xac,0xfa,0x58,0xab, 0xbe,0xe1,0x4f,0xb6, 0xb0,0xe8,0x42,0xbd,
+0xea,0x9f,0x09,0xd4, 0xe4,0x96,0x04,0xdf, 0xf6,0x8d,0x13,0xc2, 0xf8,0x84,0x1e,0xc9,
+0xd2,0xbb,0x3d,0xf8, 0xdc,0xb2,0x30,0xf3, 0xce,0xa9,0x27,0xee, 0xc0,0xa0,0x2a,0xe5,
+0x7a,0x47,0xb1,0x3c, 0x74,0x4e,0xbc,0x37, 0x66,0x55,0xab,0x2a, 0x68,0x5c,0xa6,0x21,
+0x42,0x63,0x85,0x10, 0x4c,0x6a,0x88,0x1b, 0x5e,0x71,0x9f,0x06, 0x50,0x78,0x92,0x0d,
+0x0a,0x0f,0xd9,0x64, 0x04,0x06,0xd4,0x6f, 0x16,0x1d,0xc3,0x72, 0x18,0x14,0xce,0x79,
+0x32,0x2b,0xed,0x48, 0x3c,0x22,0xe0,0x43, 0x2e,0x39,0xf7,0x5e, 0x20,0x30,0xfa,0x55,
+0xec,0x9a,0xb7,0x01, 0xe2,0x93,0xba,0x0a, 0xf0,0x88,0xad,0x17, 0xfe,0x81,0xa0,0x1c,
+0xd4,0xbe,0x83,0x2d, 0xda,0xb7,0x8e,0x26, 0xc8,0xac,0x99,0x3b, 0xc6,0xa5,0x94,0x30,
+0x9c,0xd2,0xdf,0x59, 0x92,0xdb,0xd2,0x52, 0x80,0xc0,0xc5,0x4f, 0x8e,0xc9,0xc8,0x44,
+0xa4,0xf6,0xeb,0x75, 0xaa,0xff,0xe6,0x7e, 0xb8,0xe4,0xf1,0x63, 0xb6,0xed,0xfc,0x68,
+0x0c,0x0a,0x67,0xb1, 0x02,0x03,0x6a,0xba, 0x10,0x18,0x7d,0xa7, 0x1e,0x11,0x70,0xac,
+0x34,0x2e,0x53,0x9d, 0x3a,0x27,0x5e,0x96, 0x28,0x3c,0x49,0x8b, 0x26,0x35,0x44,0x80,
+0x7c,0x42,0x0f,0xe9, 0x72,0x4b,0x02,0xe2, 0x60,0x50,0x15,0xff, 0x6e,0x59,0x18,0xf4,
+0x44,0x66,0x3b,0xc5, 0x4a,0x6f,0x36,0xce, 0x58,0x74,0x21,0xd3, 0x56,0x7d,0x2c,0xd8,
+0x37,0xa1,0x0c,0x7a, 0x39,0xa8,0x01,0x71, 0x2b,0xb3,0x16,0x6c, 0x25,0xba,0x1b,0x67,
+0x0f,0x85,0x38,0x56, 0x01,0x8c,0x35,0x5d, 0x13,0x97,0x22,0x40, 0x1d,0x9e,0x2f,0x4b,
+0x47,0xe9,0x64,0x22, 0x49,0xe0,0x69,0x29, 0x5b,0xfb,0x7e,0x34, 0x55,0xf2,0x73,0x3f,
+0x7f,0xcd,0x50,0x0e, 0x71,0xc4,0x5d,0x05, 0x63,0xdf,0x4a,0x18, 0x6d,0xd6,0x47,0x13,
+0xd7,0x31,0xdc,0xca, 0xd9,0x38,0xd1,0xc1, 0xcb,0x23,0xc6,0xdc, 0xc5,0x2a,0xcb,0xd7,
+0xef,0x15,0xe8,0xe6, 0xe1,0x1c,0xe5,0xed, 0xf3,0x07,0xf2,0xf0, 0xfd,0x0e,0xff,0xfb,
+0xa7,0x79,0xb4,0x92, 0xa9,0x70,0xb9,0x99, 0xbb,0x6b,0xae,0x84, 0xb5,0x62,0xa3,0x8f,
+0x9f,0x5d,0x80,0xbe, 0x91,0x54,0x8d,0xb5, 0x83,0x4f,0x9a,0xa8, 0x8d,0x46,0x97,0xa3
+},{
+0x00,0x00,0x00,0x00, 0x0b,0x0e,0x09,0x0d, 0x16,0x1c,0x12,0x1a, 0x1d,0x12,0x1b,0x17,
+0x2c,0x38,0x24,0x34, 0x27,0x36,0x2d,0x39, 0x3a,0x24,0x36,0x2e, 0x31,0x2a,0x3f,0x23,
+0x58,0x70,0x48,0x68, 0x53,0x7e,0x41,0x65, 0x4e,0x6c,0x5a,0x72, 0x45,0x62,0x53,0x7f,
+0x74,0x48,0x6c,0x5c, 0x7f,0x46,0x65,0x51, 0x62,0x54,0x7e,0x46, 0x69,0x5a,0x77,0x4b,
+0xb0,0xe0,0x90,0xd0, 0xbb,0xee,0x99,0xdd, 0xa6,0xfc,0x82,0xca, 0xad,0xf2,0x8b,0xc7,
+0x9c,0xd8,0xb4,0xe4, 0x97,0xd6,0xbd,0xe9, 0x8a,0xc4,0xa6,0xfe, 0x81,0xca,0xaf,0xf3,
+0xe8,0x90,0xd8,0xb8, 0xe3,0x9e,0xd1,0xb5, 0xfe,0x8c,0xca,0xa2, 0xf5,0x82,0xc3,0xaf,
+0xc4,0xa8,0xfc,0x8c, 0xcf,0xa6,0xf5,0x81, 0xd2,0xb4,0xee,0x96, 0xd9,0xba,0xe7,0x9b,
+0x7b,0xdb,0x3b,0xbb, 0x70,0xd5,0x32,0xb6, 0x6d,0xc7,0x29,0xa1, 0x66,0xc9,0x20,0xac,
+0x57,0xe3,0x1f,0x8f, 0x5c,0xed,0x16,0x82, 0x41,0xff,0x0d,0x95, 0x4a,0xf1,0x04,0x98,
+0x23,0xab,0x73,0xd3, 0x28,0xa5,0x7a,0xde, 0x35,0xb7,0x61,0xc9, 0x3e,0xb9,0x68,0xc4,
+0x0f,0x93,0x57,0xe7, 0x04,0x9d,0x5e,0xea, 0x19,0x8f,0x45,0xfd, 0x12,0x81,0x4c,0xf0,
+0xcb,0x3b,0xab,0x6b, 0xc0,0x35,0xa2,0x66, 0xdd,0x27,0xb9,0x71, 0xd6,0x29,0xb0,0x7c,
+0xe7,0x03,0x8f,0x5f, 0xec,0x0d,0x86,0x52, 0xf1,0x1f,0x9d,0x45, 0xfa,0x11,0x94,0x48,
+0x93,0x4b,0xe3,0x03, 0x98,0x45,0xea,0x0e, 0x85,0x57,0xf1,0x19, 0x8e,0x59,0xf8,0x14,
+0xbf,0x73,0xc7,0x37, 0xb4,0x7d,0xce,0x3a, 0xa9,0x6f,0xd5,0x2d, 0xa2,0x61,0xdc,0x20,
+0xf6,0xad,0x76,0x6d, 0xfd,0xa3,0x7f,0x60, 0xe0,0xb1,0x64,0x77, 0xeb,0xbf,0x6d,0x7a,
+0xda,0x95,0x52,0x59, 0xd1,0x9b,0x5b,0x54, 0xcc,0x89,0x40,0x43, 0xc7,0x87,0x49,0x4e,
+0xae,0xdd,0x3e,0x05, 0xa5,0xd3,0x37,0x08, 0xb8,0xc1,0x2c,0x1f, 0xb3,0xcf,0x25,0x12,
+0x82,0xe5,0x1a,0x31, 0x89,0xeb,0x13,0x3c, 0x94,0xf9,0x08,0x2b, 0x9f,0xf7,0x01,0x26,
+0x46,0x4d,0xe6,0xbd, 0x4d,0x43,0xef,0xb0, 0x50,0x51,0xf4,0xa7, 0x5b,0x5f,0xfd,0xaa,
+0x6a,0x75,0xc2,0x89, 0x61,0x7b,0xcb,0x84, 0x7c,0x69,0xd0,0x93, 0x77,0x67,0xd9,0x9e,
+0x1e,0x3d,0xae,0xd5, 0x15,0x33,0xa7,0xd8, 0x08,0x21,0xbc,0xcf, 0x03,0x2f,0xb5,0xc2,
+0x32,0x05,0x8a,0xe1, 0x39,0x0b,0x83,0xec, 0x24,0x19,0x98,0xfb, 0x2f,0x17,0x91,0xf6,
+0x8d,0x76,0x4d,0xd6, 0x86,0x78,0x44,0xdb, 0x9b,0x6a,0x5f,0xcc, 0x90,0x64,0x56,0xc1,
+0xa1,0x4e,0x69,0xe2, 0xaa,0x40,0x60,0xef, 0xb7,0x52,0x7b,0xf8, 0xbc,0x5c,0x72,0xf5,
+0xd5,0x06,0x05,0xbe, 0xde,0x08,0x0c,0xb3, 0xc3,0x1a,0x17,0xa4, 0xc8,0x14,0x1e,0xa9,
+0xf9,0x3e,0x21,0x8a, 0xf2,0x30,0x28,0x87, 0xef,0x22,0x33,0x90, 0xe4,0x2c,0x3a,0x9d,
+0x3d,0x96,0xdd,0x06, 0x36,0x98,0xd4,0x0b, 0x2b,0x8a,0xcf,0x1c, 0x20,0x84,0xc6,0x11,
+0x11,0xae,0xf9,0x32, 0x1a,0xa0,0xf0,0x3f, 0x07,0xb2,0xeb,0x28, 0x0c,0xbc,0xe2,0x25,
+0x65,0xe6,0x95,0x6e, 0x6e,0xe8,0x9c,0x63, 0x73,0xfa,0x87,0x74, 0x78,0xf4,0x8e,0x79,
+0x49,0xde,0xb1,0x5a, 0x42,0xd0,0xb8,0x57, 0x5f,0xc2,0xa3,0x40, 0x54,0xcc,0xaa,0x4d,
+0xf7,0x41,0xec,0xda, 0xfc,0x4f,0xe5,0xd7, 0xe1,0x5d,0xfe,0xc0, 0xea,0x53,0xf7,0xcd,
+0xdb,0x79,0xc8,0xee, 0xd0,0x77,0xc1,0xe3, 0xcd,0x65,0xda,0xf4, 0xc6,0x6b,0xd3,0xf9,
+0xaf,0x31,0xa4,0xb2, 0xa4,0x3f,0xad,0xbf, 0xb9,0x2d,0xb6,0xa8, 0xb2,0x23,0xbf,0xa5,
+0x83,0x09,0x80,0x86, 0x88,0x07,0x89,0x8b, 0x95,0x15,0x92,0x9c, 0x9e,0x1b,0x9b,0x91,
+0x47,0xa1,0x7c,0x0a, 0x4c,0xaf,0x75,0x07, 0x51,0xbd,0x6e,0x10, 0x5a,0xb3,0x67,0x1d,
+0x6b,0x99,0x58,0x3e, 0x60,0x97,0x51,0x33, 0x7d,0x85,0x4a,0x24, 0x76,0x8b,0x43,0x29,
+0x1f,0xd1,0x34,0x62, 0x14,0xdf,0x3d,0x6f, 0x09,0xcd,0x26,0x78, 0x02,0xc3,0x2f,0x75,
+0x33,0xe9,0x10,0x56, 0x38,0xe7,0x19,0x5b, 0x25,0xf5,0x02,0x4c, 0x2e,0xfb,0x0b,0x41,
+0x8c,0x9a,0xd7,0x61, 0x87,0x94,0xde,0x6c, 0x9a,0x86,0xc5,0x7b, 0x91,0x88,0xcc,0x76,
+0xa0,0xa2,0xf3,0x55, 0xab,0xac,0xfa,0x58, 0xb6,0xbe,0xe1,0x4f, 0xbd,0xb0,0xe8,0x42,
+0xd4,0xea,0x9f,0x09, 0xdf,0xe4,0x96,0x04, 0xc2,0xf6,0x8d,0x13, 0xc9,0xf8,0x84,0x1e,
+0xf8,0xd2,0xbb,0x3d, 0xf3,0xdc,0xb2,0x30, 0xee,0xce,0xa9,0x27, 0xe5,0xc0,0xa0,0x2a,
+0x3c,0x7a,0x47,0xb1, 0x37,0x74,0x4e,0xbc, 0x2a,0x66,0x55,0xab, 0x21,0x68,0x5c,0xa6,
+0x10,0x42,0x63,0x85, 0x1b,0x4c,0x6a,0x88, 0x06,0x5e,0x71,0x9f, 0x0d,0x50,0x78,0x92,
+0x64,0x0a,0x0f,0xd9, 0x6f,0x04,0x06,0xd4, 0x72,0x16,0x1d,0xc3, 0x79,0x18,0x14,0xce,
+0x48,0x32,0x2b,0xed, 0x43,0x3c,0x22,0xe0, 0x5e,0x2e,0x39,0xf7, 0x55,0x20,0x30,0xfa,
+0x01,0xec,0x9a,0xb7, 0x0a,0xe2,0x93,0xba, 0x17,0xf0,0x88,0xad, 0x1c,0xfe,0x81,0xa0,
+0x2d,0xd4,0xbe,0x83, 0x26,0xda,0xb7,0x8e, 0x3b,0xc8,0xac,0x99, 0x30,0xc6,0xa5,0x94,
+0x59,0x9c,0xd2,0xdf, 0x52,0x92,0xdb,0xd2, 0x4f,0x80,0xc0,0xc5, 0x44,0x8e,0xc9,0xc8,
+0x75,0xa4,0xf6,0xeb, 0x7e,0xaa,0xff,0xe6, 0x63,0xb8,0xe4,0xf1, 0x68,0xb6,0xed,0xfc,
+0xb1,0x0c,0x0a,0x67, 0xba,0x02,0x03,0x6a, 0xa7,0x10,0x18,0x7d, 0xac,0x1e,0x11,0x70,
+0x9d,0x34,0x2e,0x53, 0x96,0x3a,0x27,0x5e, 0x8b,0x28,0x3c,0x49, 0x80,0x26,0x35,0x44,
+0xe9,0x7c,0x42,0x0f, 0xe2,0x72,0x4b,0x02, 0xff,0x60,0x50,0x15, 0xf4,0x6e,0x59,0x18,
+0xc5,0x44,0x66,0x3b, 0xce,0x4a,0x6f,0x36, 0xd3,0x58,0x74,0x21, 0xd8,0x56,0x7d,0x2c,
+0x7a,0x37,0xa1,0x0c, 0x71,0x39,0xa8,0x01, 0x6c,0x2b,0xb3,0x16, 0x67,0x25,0xba,0x1b,
+0x56,0x0f,0x85,0x38, 0x5d,0x01,0x8c,0x35, 0x40,0x13,0x97,0x22, 0x4b,0x1d,0x9e,0x2f,
+0x22,0x47,0xe9,0x64, 0x29,0x49,0xe0,0x69, 0x34,0x5b,0xfb,0x7e, 0x3f,0x55,0xf2,0x73,
+0x0e,0x7f,0xcd,0x50, 0x05,0x71,0xc4,0x5d, 0x18,0x63,0xdf,0x4a, 0x13,0x6d,0xd6,0x47,
+0xca,0xd7,0x31,0xdc, 0xc1,0xd9,0x38,0xd1, 0xdc,0xcb,0x23,0xc6, 0xd7,0xc5,0x2a,0xcb,
+0xe6,0xef,0x15,0xe8, 0xed,0xe1,0x1c,0xe5, 0xf0,0xf3,0x07,0xf2, 0xfb,0xfd,0x0e,0xff,
+0x92,0xa7,0x79,0xb4, 0x99,0xa9,0x70,0xb9, 0x84,0xbb,0x6b,0xae, 0x8f,0xb5,0x62,0xa3,
+0xbe,0x9f,0x5d,0x80, 0xb5,0x91,0x54,0x8d, 0xa8,0x83,0x4f,0x9a, 0xa3,0x8d,0x46,0x97
+},{
+0x00,0x00,0x00,0x00, 0x0d,0x0b,0x0e,0x09, 0x1a,0x16,0x1c,0x12, 0x17,0x1d,0x12,0x1b,
+0x34,0x2c,0x38,0x24, 0x39,0x27,0x36,0x2d, 0x2e,0x3a,0x24,0x36, 0x23,0x31,0x2a,0x3f,
+0x68,0x58,0x70,0x48, 0x65,0x53,0x7e,0x41, 0x72,0x4e,0x6c,0x5a, 0x7f,0x45,0x62,0x53,
+0x5c,0x74,0x48,0x6c, 0x51,0x7f,0x46,0x65, 0x46,0x62,0x54,0x7e, 0x4b,0x69,0x5a,0x77,
+0xd0,0xb0,0xe0,0x90, 0xdd,0xbb,0xee,0x99, 0xca,0xa6,0xfc,0x82, 0xc7,0xad,0xf2,0x8b,
+0xe4,0x9c,0xd8,0xb4, 0xe9,0x97,0xd6,0xbd, 0xfe,0x8a,0xc4,0xa6, 0xf3,0x81,0xca,0xaf,
+0xb8,0xe8,0x90,0xd8, 0xb5,0xe3,0x9e,0xd1, 0xa2,0xfe,0x8c,0xca, 0xaf,0xf5,0x82,0xc3,
+0x8c,0xc4,0xa8,0xfc, 0x81,0xcf,0xa6,0xf5, 0x96,0xd2,0xb4,0xee, 0x9b,0xd9,0xba,0xe7,
+0xbb,0x7b,0xdb,0x3b, 0xb6,0x70,0xd5,0x32, 0xa1,0x6d,0xc7,0x29, 0xac,0x66,0xc9,0x20,
+0x8f,0x57,0xe3,0x1f, 0x82,0x5c,0xed,0x16, 0x95,0x41,0xff,0x0d, 0x98,0x4a,0xf1,0x04,
+0xd3,0x23,0xab,0x73, 0xde,0x28,0xa5,0x7a, 0xc9,0x35,0xb7,0x61, 0xc4,0x3e,0xb9,0x68,
+0xe7,0x0f,0x93,0x57, 0xea,0x04,0x9d,0x5e, 0xfd,0x19,0x8f,0x45, 0xf0,0x12,0x81,0x4c,
+0x6b,0xcb,0x3b,0xab, 0x66,0xc0,0x35,0xa2, 0x71,0xdd,0x27,0xb9, 0x7c,0xd6,0x29,0xb0,
+0x5f,0xe7,0x03,0x8f, 0x52,0xec,0x0d,0x86, 0x45,0xf1,0x1f,0x9d, 0x48,0xfa,0x11,0x94,
+0x03,0x93,0x4b,0xe3, 0x0e,0x98,0x45,0xea, 0x19,0x85,0x57,0xf1, 0x14,0x8e,0x59,0xf8,
+0x37,0xbf,0x73,0xc7, 0x3a,0xb4,0x7d,0xce, 0x2d,0xa9,0x6f,0xd5, 0x20,0xa2,0x61,0xdc,
+0x6d,0xf6,0xad,0x76, 0x60,0xfd,0xa3,0x7f, 0x77,0xe0,0xb1,0x64, 0x7a,0xeb,0xbf,0x6d,
+0x59,0xda,0x95,0x52, 0x54,0xd1,0x9b,0x5b, 0x43,0xcc,0x89,0x40, 0x4e,0xc7,0x87,0x49,
+0x05,0xae,0xdd,0x3e, 0x08,0xa5,0xd3,0x37, 0x1f,0xb8,0xc1,0x2c, 0x12,0xb3,0xcf,0x25,
+0x31,0x82,0xe5,0x1a, 0x3c,0x89,0xeb,0x13, 0x2b,0x94,0xf9,0x08, 0x26,0x9f,0xf7,0x01,
+0xbd,0x46,0x4d,0xe6, 0xb0,0x4d,0x43,0xef, 0xa7,0x50,0x51,0xf4, 0xaa,0x5b,0x5f,0xfd,
+0x89,0x6a,0x75,0xc2, 0x84,0x61,0x7b,0xcb, 0x93,0x7c,0x69,0xd0, 0x9e,0x77,0x67,0xd9,
+0xd5,0x1e,0x3d,0xae, 0xd8,0x15,0x33,0xa7, 0xcf,0x08,0x21,0xbc, 0xc2,0x03,0x2f,0xb5,
+0xe1,0x32,0x05,0x8a, 0xec,0x39,0x0b,0x83, 0xfb,0x24,0x19,0x98, 0xf6,0x2f,0x17,0x91,
+0xd6,0x8d,0x76,0x4d, 0xdb,0x86,0x78,0x44, 0xcc,0x9b,0x6a,0x5f, 0xc1,0x90,0x64,0x56,
+0xe2,0xa1,0x4e,0x69, 0xef,0xaa,0x40,0x60, 0xf8,0xb7,0x52,0x7b, 0xf5,0xbc,0x5c,0x72,
+0xbe,0xd5,0x06,0x05, 0xb3,0xde,0x08,0x0c, 0xa4,0xc3,0x1a,0x17, 0xa9,0xc8,0x14,0x1e,
+0x8a,0xf9,0x3e,0x21, 0x87,0xf2,0x30,0x28, 0x90,0xef,0x22,0x33, 0x9d,0xe4,0x2c,0x3a,
+0x06,0x3d,0x96,0xdd, 0x0b,0x36,0x98,0xd4, 0x1c,0x2b,0x8a,0xcf, 0x11,0x20,0x84,0xc6,
+0x32,0x11,0xae,0xf9, 0x3f,0x1a,0xa0,0xf0, 0x28,0x07,0xb2,0xeb, 0x25,0x0c,0xbc,0xe2,
+0x6e,0x65,0xe6,0x95, 0x63,0x6e,0xe8,0x9c, 0x74,0x73,0xfa,0x87, 0x79,0x78,0xf4,0x8e,
+0x5a,0x49,0xde,0xb1, 0x57,0x42,0xd0,0xb8, 0x40,0x5f,0xc2,0xa3, 0x4d,0x54,0xcc,0xaa,
+0xda,0xf7,0x41,0xec, 0xd7,0xfc,0x4f,0xe5, 0xc0,0xe1,0x5d,0xfe, 0xcd,0xea,0x53,0xf7,
+0xee,0xdb,0x79,0xc8, 0xe3,0xd0,0x77,0xc1, 0xf4,0xcd,0x65,0xda, 0xf9,0xc6,0x6b,0xd3,
+0xb2,0xaf,0x31,0xa4, 0xbf,0xa4,0x3f,0xad, 0xa8,0xb9,0x2d,0xb6, 0xa5,0xb2,0x23,0xbf,
+0x86,0x83,0x09,0x80, 0x8b,0x88,0x07,0x89, 0x9c,0x95,0x15,0x92, 0x91,0x9e,0x1b,0x9b,
+0x0a,0x47,0xa1,0x7c, 0x07,0x4c,0xaf,0x75, 0x10,0x51,0xbd,0x6e, 0x1d,0x5a,0xb3,0x67,
+0x3e,0x6b,0x99,0x58, 0x33,0x60,0x97,0x51, 0x24,0x7d,0x85,0x4a, 0x29,0x76,0x8b,0x43,
+0x62,0x1f,0xd1,0x34, 0x6f,0x14,0xdf,0x3d, 0x78,0x09,0xcd,0x26, 0x75,0x02,0xc3,0x2f,
+0x56,0x33,0xe9,0x10, 0x5b,0x38,0xe7,0x19, 0x4c,0x25,0xf5,0x02, 0x41,0x2e,0xfb,0x0b,
+0x61,0x8c,0x9a,0xd7, 0x6c,0x87,0x94,0xde, 0x7b,0x9a,0x86,0xc5, 0x76,0x91,0x88,0xcc,
+0x55,0xa0,0xa2,0xf3, 0x58,0xab,0xac,0xfa, 0x4f,0xb6,0xbe,0xe1, 0x42,0xbd,0xb0,0xe8,
+0x09,0xd4,0xea,0x9f, 0x04,0xdf,0xe4,0x96, 0x13,0xc2,0xf6,0x8d, 0x1e,0xc9,0xf8,0x84,
+0x3d,0xf8,0xd2,0xbb, 0x30,0xf3,0xdc,0xb2, 0x27,0xee,0xce,0xa9, 0x2a,0xe5,0xc0,0xa0,
+0xb1,0x3c,0x7a,0x47, 0xbc,0x37,0x74,0x4e, 0xab,0x2a,0x66,0x55, 0xa6,0x21,0x68,0x5c,
+0x85,0x10,0x42,0x63, 0x88,0x1b,0x4c,0x6a, 0x9f,0x06,0x5e,0x71, 0x92,0x0d,0x50,0x78,
+0xd9,0x64,0x0a,0x0f, 0xd4,0x6f,0x04,0x06, 0xc3,0x72,0x16,0x1d, 0xce,0x79,0x18,0x14,
+0xed,0x48,0x32,0x2b, 0xe0,0x43,0x3c,0x22, 0xf7,0x5e,0x2e,0x39, 0xfa,0x55,0x20,0x30,
+0xb7,0x01,0xec,0x9a, 0xba,0x0a,0xe2,0x93, 0xad,0x17,0xf0,0x88, 0xa0,0x1c,0xfe,0x81,
+0x83,0x2d,0xd4,0xbe, 0x8e,0x26,0xda,0xb7, 0x99,0x3b,0xc8,0xac, 0x94,0x30,0xc6,0xa5,
+0xdf,0x59,0x9c,0xd2, 0xd2,0x52,0x92,0xdb, 0xc5,0x4f,0x80,0xc0, 0xc8,0x44,0x8e,0xc9,
+0xeb,0x75,0xa4,0xf6, 0xe6,0x7e,0xaa,0xff, 0xf1,0x63,0xb8,0xe4, 0xfc,0x68,0xb6,0xed,
+0x67,0xb1,0x0c,0x0a, 0x6a,0xba,0x02,0x03, 0x7d,0xa7,0x10,0x18, 0x70,0xac,0x1e,0x11,
+0x53,0x9d,0x34,0x2e, 0x5e,0x96,0x3a,0x27, 0x49,0x8b,0x28,0x3c, 0x44,0x80,0x26,0x35,
+0x0f,0xe9,0x7c,0x42, 0x02,0xe2,0x72,0x4b, 0x15,0xff,0x60,0x50, 0x18,0xf4,0x6e,0x59,
+0x3b,0xc5,0x44,0x66, 0x36,0xce,0x4a,0x6f, 0x21,0xd3,0x58,0x74, 0x2c,0xd8,0x56,0x7d,
+0x0c,0x7a,0x37,0xa1, 0x01,0x71,0x39,0xa8, 0x16,0x6c,0x2b,0xb3, 0x1b,0x67,0x25,0xba,
+0x38,0x56,0x0f,0x85, 0x35,0x5d,0x01,0x8c, 0x22,0x40,0x13,0x97, 0x2f,0x4b,0x1d,0x9e,
+0x64,0x22,0x47,0xe9, 0x69,0x29,0x49,0xe0, 0x7e,0x34,0x5b,0xfb, 0x73,0x3f,0x55,0xf2,
+0x50,0x0e,0x7f,0xcd, 0x5d,0x05,0x71,0xc4, 0x4a,0x18,0x63,0xdf, 0x47,0x13,0x6d,0xd6,
+0xdc,0xca,0xd7,0x31, 0xd1,0xc1,0xd9,0x38, 0xc6,0xdc,0xcb,0x23, 0xcb,0xd7,0xc5,0x2a,
+0xe8,0xe6,0xef,0x15, 0xe5,0xed,0xe1,0x1c, 0xf2,0xf0,0xf3,0x07, 0xff,0xfb,0xfd,0x0e,
+0xb4,0x92,0xa7,0x79, 0xb9,0x99,0xa9,0x70, 0xae,0x84,0xbb,0x6b, 0xa3,0x8f,0xb5,0x62,
+0x80,0xbe,0x9f,0x5d, 0x8d,0xb5,0x91,0x54, 0x9a,0xa8,0x83,0x4f, 0x97,0xa3,0x8d,0x46
+},{
+0x00,0x00,0x00,0x00, 0x09,0x0d,0x0b,0x0e, 0x12,0x1a,0x16,0x1c, 0x1b,0x17,0x1d,0x12,
+0x24,0x34,0x2c,0x38, 0x2d,0x39,0x27,0x36, 0x36,0x2e,0x3a,0x24, 0x3f,0x23,0x31,0x2a,
+0x48,0x68,0x58,0x70, 0x41,0x65,0x53,0x7e, 0x5a,0x72,0x4e,0x6c, 0x53,0x7f,0x45,0x62,
+0x6c,0x5c,0x74,0x48, 0x65,0x51,0x7f,0x46, 0x7e,0x46,0x62,0x54, 0x77,0x4b,0x69,0x5a,
+0x90,0xd0,0xb0,0xe0, 0x99,0xdd,0xbb,0xee, 0x82,0xca,0xa6,0xfc, 0x8b,0xc7,0xad,0xf2,
+0xb4,0xe4,0x9c,0xd8, 0xbd,0xe9,0x97,0xd6, 0xa6,0xfe,0x8a,0xc4, 0xaf,0xf3,0x81,0xca,
+0xd8,0xb8,0xe8,0x90, 0xd1,0xb5,0xe3,0x9e, 0xca,0xa2,0xfe,0x8c, 0xc3,0xaf,0xf5,0x82,
+0xfc,0x8c,0xc4,0xa8, 0xf5,0x81,0xcf,0xa6, 0xee,0x96,0xd2,0xb4, 0xe7,0x9b,0xd9,0xba,
+0x3b,0xbb,0x7b,0xdb, 0x32,0xb6,0x70,0xd5, 0x29,0xa1,0x6d,0xc7, 0x20,0xac,0x66,0xc9,
+0x1f,0x8f,0x57,0xe3, 0x16,0x82,0x5c,0xed, 0x0d,0x95,0x41,0xff, 0x04,0x98,0x4a,0xf1,
+0x73,0xd3,0x23,0xab, 0x7a,0xde,0x28,0xa5, 0x61,0xc9,0x35,0xb7, 0x68,0xc4,0x3e,0xb9,
+0x57,0xe7,0x0f,0x93, 0x5e,0xea,0x04,0x9d, 0x45,0xfd,0x19,0x8f, 0x4c,0xf0,0x12,0x81,
+0xab,0x6b,0xcb,0x3b, 0xa2,0x66,0xc0,0x35, 0xb9,0x71,0xdd,0x27, 0xb0,0x7c,0xd6,0x29,
+0x8f,0x5f,0xe7,0x03, 0x86,0x52,0xec,0x0d, 0x9d,0x45,0xf1,0x1f, 0x94,0x48,0xfa,0x11,
+0xe3,0x03,0x93,0x4b, 0xea,0x0e,0x98,0x45, 0xf1,0x19,0x85,0x57, 0xf8,0x14,0x8e,0x59,
+0xc7,0x37,0xbf,0x73, 0xce,0x3a,0xb4,0x7d, 0xd5,0x2d,0xa9,0x6f, 0xdc,0x20,0xa2,0x61,
+0x76,0x6d,0xf6,0xad, 0x7f,0x60,0xfd,0xa3, 0x64,0x77,0xe0,0xb1, 0x6d,0x7a,0xeb,0xbf,
+0x52,0x59,0xda,0x95, 0x5b,0x54,0xd1,0x9b, 0x40,0x43,0xcc,0x89, 0x49,0x4e,0xc7,0x87,
+0x3e,0x05,0xae,0xdd, 0x37,0x08,0xa5,0xd3, 0x2c,0x1f,0xb8,0xc1, 0x25,0x12,0xb3,0xcf,
+0x1a,0x31,0x82,0xe5, 0x13,0x3c,0x89,0xeb, 0x08,0x2b,0x94,0xf9, 0x01,0x26,0x9f,0xf7,
+0xe6,0xbd,0x46,0x4d, 0xef,0xb0,0x4d,0x43, 0xf4,0xa7,0x50,0x51, 0xfd,0xaa,0x5b,0x5f,
+0xc2,0x89,0x6a,0x75, 0xcb,0x84,0x61,0x7b, 0xd0,0x93,0x7c,0x69, 0xd9,0x9e,0x77,0x67,
+0xae,0xd5,0x1e,0x3d, 0xa7,0xd8,0x15,0x33, 0xbc,0xcf,0x08,0x21, 0xb5,0xc2,0x03,0x2f,
+0x8a,0xe1,0x32,0x05, 0x83,0xec,0x39,0x0b, 0x98,0xfb,0x24,0x19, 0x91,0xf6,0x2f,0x17,
+0x4d,0xd6,0x8d,0x76, 0x44,0xdb,0x86,0x78, 0x5f,0xcc,0x9b,0x6a, 0x56,0xc1,0x90,0x64,
+0x69,0xe2,0xa1,0x4e, 0x60,0xef,0xaa,0x40, 0x7b,0xf8,0xb7,0x52, 0x72,0xf5,0xbc,0x5c,
+0x05,0xbe,0xd5,0x06, 0x0c,0xb3,0xde,0x08, 0x17,0xa4,0xc3,0x1a, 0x1e,0xa9,0xc8,0x14,
+0x21,0x8a,0xf9,0x3e, 0x28,0x87,0xf2,0x30, 0x33,0x90,0xef,0x22, 0x3a,0x9d,0xe4,0x2c,
+0xdd,0x06,0x3d,0x96, 0xd4,0x0b,0x36,0x98, 0xcf,0x1c,0x2b,0x8a, 0xc6,0x11,0x20,0x84,
+0xf9,0x32,0x11,0xae, 0xf0,0x3f,0x1a,0xa0, 0xeb,0x28,0x07,0xb2, 0xe2,0x25,0x0c,0xbc,
+0x95,0x6e,0x65,0xe6, 0x9c,0x63,0x6e,0xe8, 0x87,0x74,0x73,0xfa, 0x8e,0x79,0x78,0xf4,
+0xb1,0x5a,0x49,0xde, 0xb8,0x57,0x42,0xd0, 0xa3,0x40,0x5f,0xc2, 0xaa,0x4d,0x54,0xcc,
+0xec,0xda,0xf7,0x41, 0xe5,0xd7,0xfc,0x4f, 0xfe,0xc0,0xe1,0x5d, 0xf7,0xcd,0xea,0x53,
+0xc8,0xee,0xdb,0x79, 0xc1,0xe3,0xd0,0x77, 0xda,0xf4,0xcd,0x65, 0xd3,0xf9,0xc6,0x6b,
+0xa4,0xb2,0xaf,0x31, 0xad,0xbf,0xa4,0x3f, 0xb6,0xa8,0xb9,0x2d, 0xbf,0xa5,0xb2,0x23,
+0x80,0x86,0x83,0x09, 0x89,0x8b,0x88,0x07, 0x92,0x9c,0x95,0x15, 0x9b,0x91,0x9e,0x1b,
+0x7c,0x0a,0x47,0xa1, 0x75,0x07,0x4c,0xaf, 0x6e,0x10,0x51,0xbd, 0x67,0x1d,0x5a,0xb3,
+0x58,0x3e,0x6b,0x99, 0x51,0x33,0x60,0x97, 0x4a,0x24,0x7d,0x85, 0x43,0x29,0x76,0x8b,
+0x34,0x62,0x1f,0xd1, 0x3d,0x6f,0x14,0xdf, 0x26,0x78,0x09,0xcd, 0x2f,0x75,0x02,0xc3,
+0x10,0x56,0x33,0xe9, 0x19,0x5b,0x38,0xe7, 0x02,0x4c,0x25,0xf5, 0x0b,0x41,0x2e,0xfb,
+0xd7,0x61,0x8c,0x9a, 0xde,0x6c,0x87,0x94, 0xc5,0x7b,0x9a,0x86, 0xcc,0x76,0x91,0x88,
+0xf3,0x55,0xa0,0xa2, 0xfa,0x58,0xab,0xac, 0xe1,0x4f,0xb6,0xbe, 0xe8,0x42,0xbd,0xb0,
+0x9f,0x09,0xd4,0xea, 0x96,0x04,0xdf,0xe4, 0x8d,0x13,0xc2,0xf6, 0x84,0x1e,0xc9,0xf8,
+0xbb,0x3d,0xf8,0xd2, 0xb2,0x30,0xf3,0xdc, 0xa9,0x27,0xee,0xce, 0xa0,0x2a,0xe5,0xc0,
+0x47,0xb1,0x3c,0x7a, 0x4e,0xbc,0x37,0x74, 0x55,0xab,0x2a,0x66, 0x5c,0xa6,0x21,0x68,
+0x63,0x85,0x10,0x42, 0x6a,0x88,0x1b,0x4c, 0x71,0x9f,0x06,0x5e, 0x78,0x92,0x0d,0x50,
+0x0f,0xd9,0x64,0x0a, 0x06,0xd4,0x6f,0x04, 0x1d,0xc3,0x72,0x16, 0x14,0xce,0x79,0x18,
+0x2b,0xed,0x48,0x32, 0x22,0xe0,0x43,0x3c, 0x39,0xf7,0x5e,0x2e, 0x30,0xfa,0x55,0x20,
+0x9a,0xb7,0x01,0xec, 0x93,0xba,0x0a,0xe2, 0x88,0xad,0x17,0xf0, 0x81,0xa0,0x1c,0xfe,
+0xbe,0x83,0x2d,0xd4, 0xb7,0x8e,0x26,0xda, 0xac,0x99,0x3b,0xc8, 0xa5,0x94,0x30,0xc6,
+0xd2,0xdf,0x59,0x9c, 0xdb,0xd2,0x52,0x92, 0xc0,0xc5,0x4f,0x80, 0xc9,0xc8,0x44,0x8e,
+0xf6,0xeb,0x75,0xa4, 0xff,0xe6,0x7e,0xaa, 0xe4,0xf1,0x63,0xb8, 0xed,0xfc,0x68,0xb6,
+0x0a,0x67,0xb1,0x0c, 0x03,0x6a,0xba,0x02, 0x18,0x7d,0xa7,0x10, 0x11,0x70,0xac,0x1e,
+0x2e,0x53,0x9d,0x34, 0x27,0x5e,0x96,0x3a, 0x3c,0x49,0x8b,0x28, 0x35,0x44,0x80,0x26,
+0x42,0x0f,0xe9,0x7c, 0x4b,0x02,0xe2,0x72, 0x50,0x15,0xff,0x60, 0x59,0x18,0xf4,0x6e,
+0x66,0x3b,0xc5,0x44, 0x6f,0x36,0xce,0x4a, 0x74,0x21,0xd3,0x58, 0x7d,0x2c,0xd8,0x56,
+0xa1,0x0c,0x7a,0x37, 0xa8,0x01,0x71,0x39, 0xb3,0x16,0x6c,0x2b, 0xba,0x1b,0x67,0x25,
+0x85,0x38,0x56,0x0f, 0x8c,0x35,0x5d,0x01, 0x97,0x22,0x40,0x13, 0x9e,0x2f,0x4b,0x1d,
+0xe9,0x64,0x22,0x47, 0xe0,0x69,0x29,0x49, 0xfb,0x7e,0x34,0x5b, 0xf2,0x73,0x3f,0x55,
+0xcd,0x50,0x0e,0x7f, 0xc4,0x5d,0x05,0x71, 0xdf,0x4a,0x18,0x63, 0xd6,0x47,0x13,0x6d,
+0x31,0xdc,0xca,0xd7, 0x38,0xd1,0xc1,0xd9, 0x23,0xc6,0xdc,0xcb, 0x2a,0xcb,0xd7,0xc5,
+0x15,0xe8,0xe6,0xef, 0x1c,0xe5,0xed,0xe1, 0x07,0xf2,0xf0,0xf3, 0x0e,0xff,0xfb,0xfd,
+0x79,0xb4,0x92,0xa7, 0x70,0xb9,0x99,0xa9, 0x6b,0xae,0x84,0xbb, 0x62,0xa3,0x8f,0xb5,
+0x5d,0x80,0xbe,0x9f, 0x54,0x8d,0xb5,0x91, 0x4f,0x9a,0xa8,0x83, 0x46,0x97,0xa3,0x8d
+}};
+
+/*
+//
+// The AES S-box values can be retrieved from the AesSboxMatrixMult table.
+// S[x] = AesSboxMatrixMult[0][x][1].
+// We save codespace by not having a separate table for the S-box.
+// This trick doesn't work for the inverse S-box as the
+// Inverse MDS matrix does not have a coefficient equal to 1.
+//
+
+SYMCRYPT_ALIGN_AT(256) const BYTE SymCryptAesSbox[256] = {
+ 99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 118,
+202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
+183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 216,  49,  21,
+  4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 235,  39, 178, 117,
+  9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 179,  41, 227,  47, 132,
+ 83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 190,  57,  74,  76,  88, 207,
+208, 239, 170, 251,  67,  77,  51, 133,  69, 249,   2, 127,  80,  60, 159, 168,
+ 81, 163,  64, 143, 146, 157,  56, 245, 188, 182, 218,  33,  16, 255, 243, 210,
+205,  12,  19, 236,  95, 151,  68,  23, 196, 167, 126,  61, 100,  93,  25, 115,
+ 96, 129,  79, 220,  34,  42, 144, 136,  70, 238, 184,  20, 222,  94,  11, 219,
+224,  50,  58,  10,  73,   6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121,
+231, 200,  55, 109, 141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 174,   8,
+186, 120,  37,  46,  28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138,
+112,  62, 181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158,
+225, 248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
+140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,  22
+};
+*/
+
+SYMCRYPT_ALIGN_AT(256) const BYTE SymCryptAesInvSbox[256] = {                // For final round in decryption
+0x52,0x09,0x6a,0xd5,
+0x30,0x36,0xa5,0x38,
+0xbf,0x40,0xa3,0x9e,
+0x81,0xf3,0xd7,0xfb,
+0x7c,0xe3,0x39,0x82,
+0x9b,0x2f,0xff,0x87,
+0x34,0x8e,0x43,0x44,
+0xc4,0xde,0xe9,0xcb,
+0x54,0x7b,0x94,0x32,
+0xa6,0xc2,0x23,0x3d,
+0xee,0x4c,0x95,0x0b,
+0x42,0xfa,0xc3,0x4e,
+0x08,0x2e,0xa1,0x66,
+0x28,0xd9,0x24,0xb2,
+0x76,0x5b,0xa2,0x49,
+0x6d,0x8b,0xd1,0x25,
+0x72,0xf8,0xf6,0x64,
+0x86,0x68,0x98,0x16,
+0xd4,0xa4,0x5c,0xcc,
+0x5d,0x65,0xb6,0x92,
+0x6c,0x70,0x48,0x50,
+0xfd,0xed,0xb9,0xda,
+0x5e,0x15,0x46,0x57,
+0xa7,0x8d,0x9d,0x84,
+0x90,0xd8,0xab,0x00,
+0x8c,0xbc,0xd3,0x0a,
+0xf7,0xe4,0x58,0x05,
+0xb8,0xb3,0x45,0x06,
+0xd0,0x2c,0x1e,0x8f,
+0xca,0x3f,0x0f,0x02,
+0xc1,0xaf,0xbd,0x03,
+0x01,0x13,0x8a,0x6b,
+0x3a,0x91,0x11,0x41,
+0x4f,0x67,0xdc,0xea,
+0x97,0xf2,0xcf,0xce,
+0xf0,0xb4,0xe6,0x73,
+0x96,0xac,0x74,0x22,
+0xe7,0xad,0x35,0x85,
+0xe2,0xf9,0x37,0xe8,
+0x1c,0x75,0xdf,0x6e,
+0x47,0xf1,0x1a,0x71,
+0x1d,0x29,0xc5,0x89,
+0x6f,0xb7,0x62,0x0e,
+0xaa,0x18,0xbe,0x1b,
+0xfc,0x56,0x3e,0x4b,
+0xc6,0xd2,0x79,0x20,
+0x9a,0xdb,0xc0,0xfe,
+0x78,0xcd,0x5a,0xf4,
+0x1f,0xdd,0xa8,0x33,
+0x88,0x07,0xc7,0x31,
+0xb1,0x12,0x10,0x59,
+0x27,0x80,0xec,0x5f,
+0x60,0x51,0x7f,0xa9,
+0x19,0xb5,0x4a,0x0d,
+0x2d,0xe5,0x7a,0x9f,
+0x93,0xc9,0x9c,0xef,
+0xa0,0xe0,0x3b,0x4d,
+0xae,0x2a,0xf5,0xb0,
+0xc8,0xeb,0xbb,0x3c,
+0x83,0x53,0x99,0x61,
+0x17,0x2b,0x04,0x7e,
+0xba,0x77,0xd6,0x26,
+0xe1,0x69,0x14,0x63,
+0x55,0x21,0x0c,0x7d
+};
diff --git a/libs/symcrypt/lib/DesTables.c b/libs/symcrypt/lib/DesTables.c
new file mode 100644
index 000000000000..d39375b8ad56
--- /dev/null
+++ b/libs/symcrypt/lib/DesTables.c
@@ -0,0 +1,280 @@
+//
+// DesTables.c  static lookup tables for DES
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+// These tables were copies
+// from the RSA32 DES implementation. See 3des.c for details.
+//
+
+#include "precomp.h"
+
+//
+// Alignments are chosen to reduce side-channel attacks through the TLB cache.
+// We align each table to a multiple of the size within which we do data-dependent
+// lookups. For example, the table below is aligned to 256. It is not a secret
+// that the 8 sub-tables are accessed, but which value inside each sub-table is a secret.
+// Aligning to 256 still leaves the data cache line leakage, but avoids any TLB-related leakage.
+//
+SYMCRYPT_ALIGN_AT( 256 ) const UINT32 SymCryptDesSpbox[8][64] = {
+0x02080800,0x00080000,0x02000002,0x02080802,
+0x02000000,0x00080802,0x00080002,0x02000002,
+0x00080802,0x02080800,0x02080000,0x00000802,
+0x02000802,0x02000000,0x00000000,0x00080002,
+0x00080000,0x00000002,0x02000800,0x00080800,
+0x02080802,0x02080000,0x00000802,0x02000800,
+0x00000002,0x00000800,0x00080800,0x02080002,
+0x00000800,0x02000802,0x02080002,0x00000000,
+0x00000000,0x02080802,0x02000800,0x00080002,
+0x02080800,0x00080000,0x00000802,0x02000800,
+0x02080002,0x00000800,0x00080800,0x02000002,
+0x00080802,0x00000002,0x02000002,0x02080000,
+0x02080802,0x00080800,0x02080000,0x02000802,
+0x02000000,0x00000802,0x00080002,0x00000000,
+0x00080000,0x02000000,0x02000802,0x02080800,
+0x00000002,0x02080002,0x00000800,0x00080802,
+0x40108010,0x00000000,0x00108000,0x40100000,
+0x40000010,0x00008010,0x40008000,0x00108000,
+0x00008000,0x40100010,0x00000010,0x40008000,
+0x00100010,0x40108000,0x40100000,0x00000010,
+0x00100000,0x40008010,0x40100010,0x00008000,
+0x00108010,0x40000000,0x00000000,0x00100010,
+0x40008010,0x00108010,0x40108000,0x40000010,
+0x40000000,0x00100000,0x00008010,0x40108010,
+0x00100010,0x40108000,0x40008000,0x00108010,
+0x40108010,0x00100010,0x40000010,0x00000000,
+0x40000000,0x00008010,0x00100000,0x40100010,
+0x00008000,0x40000000,0x00108010,0x40008010,
+0x40108000,0x00008000,0x00000000,0x40000010,
+0x00000010,0x40108010,0x00108000,0x40100000,
+0x40100010,0x00100000,0x00008010,0x40008000,
+0x40008010,0x00000010,0x40100000,0x00108000,
+0x04000001,0x04040100,0x00000100,0x04000101,
+0x00040001,0x04000000,0x04000101,0x00040100,
+0x04000100,0x00040000,0x04040000,0x00000001,
+0x04040101,0x00000101,0x00000001,0x04040001,
+0x00000000,0x00040001,0x04040100,0x00000100,
+0x00000101,0x04040101,0x00040000,0x04000001,
+0x04040001,0x04000100,0x00040101,0x04040000,
+0x00040100,0x00000000,0x04000000,0x00040101,
+0x04040100,0x00000100,0x00000001,0x00040000,
+0x00000101,0x00040001,0x04040000,0x04000101,
+0x00000000,0x04040100,0x00040100,0x04040001,
+0x00040001,0x04000000,0x04040101,0x00000001,
+0x00040101,0x04000001,0x04000000,0x04040101,
+0x00040000,0x04000100,0x04000101,0x00040100,
+0x04000100,0x00000000,0x04040001,0x00000101,
+0x04000001,0x00040101,0x00000100,0x04040000,
+0x00401008,0x10001000,0x00000008,0x10401008,
+0x00000000,0x10400000,0x10001008,0x00400008,
+0x10401000,0x10000008,0x10000000,0x00001008,
+0x10000008,0x00401008,0x00400000,0x10000000,
+0x10400008,0x00401000,0x00001000,0x00000008,
+0x00401000,0x10001008,0x10400000,0x00001000,
+0x00001008,0x00000000,0x00400008,0x10401000,
+0x10001000,0x10400008,0x10401008,0x00400000,
+0x10400008,0x00001008,0x00400000,0x10000008,
+0x00401000,0x10001000,0x00000008,0x10400000,
+0x10001008,0x00000000,0x00001000,0x00400008,
+0x00000000,0x10400008,0x10401000,0x00001000,
+0x10000000,0x10401008,0x00401008,0x00400000,
+0x10401008,0x00000008,0x10001000,0x00401008,
+0x00400008,0x00401000,0x10400000,0x10001008,
+0x00001008,0x10000000,0x10000008,0x10401000,
+0x08000000,0x00010000,0x00000400,0x08010420,
+0x08010020,0x08000400,0x00010420,0x08010000,
+0x00010000,0x00000020,0x08000020,0x00010400,
+0x08000420,0x08010020,0x08010400,0x00000000,
+0x00010400,0x08000000,0x00010020,0x00000420,
+0x08000400,0x00010420,0x00000000,0x08000020,
+0x00000020,0x08000420,0x08010420,0x00010020,
+0x08010000,0x00000400,0x00000420,0x08010400,
+0x08010400,0x08000420,0x00010020,0x08010000,
+0x00010000,0x00000020,0x08000020,0x08000400,
+0x08000000,0x00010400,0x08010420,0x00000000,
+0x00010420,0x08000000,0x00000400,0x00010020,
+0x08000420,0x00000400,0x00000000,0x08010420,
+0x08010020,0x08010400,0x00000420,0x00010000,
+0x00010400,0x08010020,0x08000400,0x00000420,
+0x00000020,0x00010420,0x08010000,0x08000020,
+0x80000040,0x00200040,0x00000000,0x80202000,
+0x00200040,0x00002000,0x80002040,0x00200000,
+0x00002040,0x80202040,0x00202000,0x80000000,
+0x80002000,0x80000040,0x80200000,0x00202040,
+0x00200000,0x80002040,0x80200040,0x00000000,
+0x00002000,0x00000040,0x80202000,0x80200040,
+0x80202040,0x80200000,0x80000000,0x00002040,
+0x00000040,0x00202000,0x00202040,0x80002000,
+0x00002040,0x80000000,0x80002000,0x00202040,
+0x80202000,0x00200040,0x00000000,0x80002000,
+0x80000000,0x00002000,0x80200040,0x00200000,
+0x00200040,0x80202040,0x00202000,0x00000040,
+0x80202040,0x00202000,0x00200000,0x80002040,
+0x80000040,0x80200000,0x00202040,0x00000000,
+0x00002000,0x80000040,0x80002040,0x80202000,
+0x80200000,0x00002040,0x00000040,0x80200040,
+0x00004000,0x00000200,0x01000200,0x01000004,
+0x01004204,0x00004004,0x00004200,0x00000000,
+0x01000000,0x01000204,0x00000204,0x01004000,
+0x00000004,0x01004200,0x01004000,0x00000204,
+0x01000204,0x00004000,0x00004004,0x01004204,
+0x00000000,0x01000200,0x01000004,0x00004200,
+0x01004004,0x00004204,0x01004200,0x00000004,
+0x00004204,0x01004004,0x00000200,0x01000000,
+0x00004204,0x01004000,0x01004004,0x00000204,
+0x00004000,0x00000200,0x01000000,0x01004004,
+0x01000204,0x00004204,0x00004200,0x00000000,
+0x00000200,0x01000004,0x00000004,0x01000200,
+0x00000000,0x01000204,0x01000200,0x00004200,
+0x00000204,0x00004000,0x01004204,0x01000000,
+0x01004200,0x00000004,0x00004004,0x01004204,
+0x01000004,0x01004200,0x01004000,0x00004004,
+0x20800080,0x20820000,0x00020080,0x00000000,
+0x20020000,0x00800080,0x20800000,0x20820080,
+0x00000080,0x20000000,0x00820000,0x00020080,
+0x00820080,0x20020080,0x20000080,0x20800000,
+0x00020000,0x00820080,0x00800080,0x20020000,
+0x20820080,0x20000080,0x00000000,0x00820000,
+0x20000000,0x00800000,0x20020080,0x20800080,
+0x00800000,0x00020000,0x20820000,0x00000080,
+0x00800000,0x00020000,0x20000080,0x20820080,
+0x00020080,0x20000000,0x00000000,0x00820000,
+0x20800080,0x20020080,0x20020000,0x00800080,
+0x20820000,0x00000080,0x00800080,0x20020000,
+0x20820080,0x00800000,0x20800000,0x20000080,
+0x00820000,0x00020080,0x20020080,0x20800000,
+0x00000080,0x20820000,0x00820080,0x00000000,
+0x20000000,0x20800080,0x00020000,0x00820080,
+};
+
+SYMCRYPT_ALIGN_AT(256) const UINT32 SymCryptDesKeySelect[8][64]={
+0x00000000,0x00000010,0x20000000,0x20000010,
+0x00010000,0x00010010,0x20010000,0x20010010,
+0x00000800,0x00000810,0x20000800,0x20000810,
+0x00010800,0x00010810,0x20010800,0x20010810,
+0x00000020,0x00000030,0x20000020,0x20000030,
+0x00010020,0x00010030,0x20010020,0x20010030,
+0x00000820,0x00000830,0x20000820,0x20000830,
+0x00010820,0x00010830,0x20010820,0x20010830,
+0x00080000,0x00080010,0x20080000,0x20080010,
+0x00090000,0x00090010,0x20090000,0x20090010,
+0x00080800,0x00080810,0x20080800,0x20080810,
+0x00090800,0x00090810,0x20090800,0x20090810,
+0x00080020,0x00080030,0x20080020,0x20080030,
+0x00090020,0x00090030,0x20090020,0x20090030,
+0x00080820,0x00080830,0x20080820,0x20080830,
+0x00090820,0x00090830,0x20090820,0x20090830,
+0x00000000,0x02000000,0x00002000,0x02002000,
+0x00200000,0x02200000,0x00202000,0x02202000,
+0x00000004,0x02000004,0x00002004,0x02002004,
+0x00200004,0x02200004,0x00202004,0x02202004,
+0x00000400,0x02000400,0x00002400,0x02002400,
+0x00200400,0x02200400,0x00202400,0x02202400,
+0x00000404,0x02000404,0x00002404,0x02002404,
+0x00200404,0x02200404,0x00202404,0x02202404,
+0x10000000,0x12000000,0x10002000,0x12002000,
+0x10200000,0x12200000,0x10202000,0x12202000,
+0x10000004,0x12000004,0x10002004,0x12002004,
+0x10200004,0x12200004,0x10202004,0x12202004,
+0x10000400,0x12000400,0x10002400,0x12002400,
+0x10200400,0x12200400,0x10202400,0x12202400,
+0x10000404,0x12000404,0x10002404,0x12002404,
+0x10200404,0x12200404,0x10202404,0x12202404,
+0x00000000,0x00000001,0x00040000,0x00040001,
+0x01000000,0x01000001,0x01040000,0x01040001,
+0x00000002,0x00000003,0x00040002,0x00040003,
+0x01000002,0x01000003,0x01040002,0x01040003,
+0x00000200,0x00000201,0x00040200,0x00040201,
+0x01000200,0x01000201,0x01040200,0x01040201,
+0x00000202,0x00000203,0x00040202,0x00040203,
+0x01000202,0x01000203,0x01040202,0x01040203,
+0x08000000,0x08000001,0x08040000,0x08040001,
+0x09000000,0x09000001,0x09040000,0x09040001,
+0x08000002,0x08000003,0x08040002,0x08040003,
+0x09000002,0x09000003,0x09040002,0x09040003,
+0x08000200,0x08000201,0x08040200,0x08040201,
+0x09000200,0x09000201,0x09040200,0x09040201,
+0x08000202,0x08000203,0x08040202,0x08040203,
+0x09000202,0x09000203,0x09040202,0x09040203,
+0x00000000,0x00100000,0x00000100,0x00100100,
+0x00000008,0x00100008,0x00000108,0x00100108,
+0x00001000,0x00101000,0x00001100,0x00101100,
+0x00001008,0x00101008,0x00001108,0x00101108,
+0x04000000,0x04100000,0x04000100,0x04100100,
+0x04000008,0x04100008,0x04000108,0x04100108,
+0x04001000,0x04101000,0x04001100,0x04101100,
+0x04001008,0x04101008,0x04001108,0x04101108,
+0x00020000,0x00120000,0x00020100,0x00120100,
+0x00020008,0x00120008,0x00020108,0x00120108,
+0x00021000,0x00121000,0x00021100,0x00121100,
+0x00021008,0x00121008,0x00021108,0x00121108,
+0x04020000,0x04120000,0x04020100,0x04120100,
+0x04020008,0x04120008,0x04020108,0x04120108,
+0x04021000,0x04121000,0x04021100,0x04121100,
+0x04021008,0x04121008,0x04021108,0x04121108,
+0x00000000,0x10000000,0x00010000,0x10010000,
+0x00000004,0x10000004,0x00010004,0x10010004,
+0x20000000,0x30000000,0x20010000,0x30010000,
+0x20000004,0x30000004,0x20010004,0x30010004,
+0x00100000,0x10100000,0x00110000,0x10110000,
+0x00100004,0x10100004,0x00110004,0x10110004,
+0x20100000,0x30100000,0x20110000,0x30110000,
+0x20100004,0x30100004,0x20110004,0x30110004,
+0x00001000,0x10001000,0x00011000,0x10011000,
+0x00001004,0x10001004,0x00011004,0x10011004,
+0x20001000,0x30001000,0x20011000,0x30011000,
+0x20001004,0x30001004,0x20011004,0x30011004,
+0x00101000,0x10101000,0x00111000,0x10111000,
+0x00101004,0x10101004,0x00111004,0x10111004,
+0x20101000,0x30101000,0x20111000,0x30111000,
+0x20101004,0x30101004,0x20111004,0x30111004,
+0x00000000,0x08000000,0x00000008,0x08000008,
+0x00000400,0x08000400,0x00000408,0x08000408,
+0x00020000,0x08020000,0x00020008,0x08020008,
+0x00020400,0x08020400,0x00020408,0x08020408,
+0x00000001,0x08000001,0x00000009,0x08000009,
+0x00000401,0x08000401,0x00000409,0x08000409,
+0x00020001,0x08020001,0x00020009,0x08020009,
+0x00020401,0x08020401,0x00020409,0x08020409,
+0x02000000,0x0A000000,0x02000008,0x0A000008,
+0x02000400,0x0A000400,0x02000408,0x0A000408,
+0x02020000,0x0A020000,0x02020008,0x0A020008,
+0x02020400,0x0A020400,0x02020408,0x0A020408,
+0x02000001,0x0A000001,0x02000009,0x0A000009,
+0x02000401,0x0A000401,0x02000409,0x0A000409,
+0x02020001,0x0A020001,0x02020009,0x0A020009,
+0x02020401,0x0A020401,0x02020409,0x0A020409,
+0x00000000,0x00000100,0x00080000,0x00080100,
+0x01000000,0x01000100,0x01080000,0x01080100,
+0x00000010,0x00000110,0x00080010,0x00080110,
+0x01000010,0x01000110,0x01080010,0x01080110,
+0x00200000,0x00200100,0x00280000,0x00280100,
+0x01200000,0x01200100,0x01280000,0x01280100,
+0x00200010,0x00200110,0x00280010,0x00280110,
+0x01200010,0x01200110,0x01280010,0x01280110,
+0x00000200,0x00000300,0x00080200,0x00080300,
+0x01000200,0x01000300,0x01080200,0x01080300,
+0x00000210,0x00000310,0x00080210,0x00080310,
+0x01000210,0x01000310,0x01080210,0x01080310,
+0x00200200,0x00200300,0x00280200,0x00280300,
+0x01200200,0x01200300,0x01280200,0x01280300,
+0x00200210,0x00200310,0x00280210,0x00280310,
+0x01200210,0x01200310,0x01280210,0x01280310,
+0x00000000,0x04000000,0x00040000,0x04040000,
+0x00000002,0x04000002,0x00040002,0x04040002,
+0x00002000,0x04002000,0x00042000,0x04042000,
+0x00002002,0x04002002,0x00042002,0x04042002,
+0x00000020,0x04000020,0x00040020,0x04040020,
+0x00000022,0x04000022,0x00040022,0x04040022,
+0x00002020,0x04002020,0x00042020,0x04042020,
+0x00002022,0x04002022,0x00042022,0x04042022,
+0x00000800,0x04000800,0x00040800,0x04040800,
+0x00000802,0x04000802,0x00040802,0x04040802,
+0x00002800,0x04002800,0x00042800,0x04042800,
+0x00002802,0x04002802,0x00042802,0x04042802,
+0x00000820,0x04000820,0x00040820,0x04040820,
+0x00000822,0x04000822,0x00040822,0x04040822,
+0x00002820,0x04002820,0x00042820,0x04042820,
+0x00002822,0x04002822,0x00042822,0x04042822,
+};
diff --git a/libs/symcrypt/lib/FatalIntercept.c b/libs/symcrypt/lib/FatalIntercept.c
new file mode 100644
index 000000000000..b0e11e527c0b
--- /dev/null
+++ b/libs/symcrypt/lib/FatalIntercept.c
@@ -0,0 +1,23 @@
+//
+// FatalIntercept.C
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// Empty function which our test code can replace to intercept any Fatal calls.
+// Used in Kernel-mode tests so that an error doesn't bugcheck the machine.
+// Rather, it can kill the current thread and not take down the machine.
+//
+// This is in its own C file so that it is only linked in when the caller doesn't have
+// a function by this name.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptFatalIntercept( UINT32 fatalCode )
+{
+    UNREFERENCED_PARAMETER( fatalCode );
+}
diff --git a/libs/symcrypt/lib/IEEE802_11SaeCustom.c b/libs/symcrypt/lib/IEEE802_11SaeCustom.c
new file mode 100644
index 000000000000..574b53400c42
--- /dev/null
+++ b/libs/symcrypt/lib/IEEE802_11SaeCustom.c
@@ -0,0 +1,1585 @@
+//
+// IEEE802_11SaeCustom.c  Implementation of the custom crypto of IEEE 802.11 SAE
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Used in SAE Hunting and Pecking methods where NIST P256 is hardcoded
+#define PRIME_LENGTH_BITS 256
+
+//
+// This data structure is used to store the associated elliptic curve and the z value corresponding to
+// each IANA group mappings for each elliptic
+// curve defined in IEEE Std 802.11 SAE method.
+//
+typedef struct _SYMCRYPT_SAE_GROUP_DATA {
+    SYMCRYPT_802_11_SAE_GROUP       group;
+    const PCSYMCRYPT_ECURVE_PARAMS *pCurveParams;
+    const PCSYMCRYPT_MAC           *macAlgorithm;
+    INT32                           z;
+} SYMCRYPT_SAE_GROUP_DATA, *PSYMCRYPT_SAE_GROUP_DATA;
+
+typedef const SYMCRYPT_SAE_GROUP_DATA* PCSYMCRYPT_SAE_GROUP_DATA;
+
+//
+// Data based on IEEE Std 802.11-2020
+// Table 12.1 - Hash algorithm based on length of prime
+// Table 12.2 - Unique curve parameter
+//
+const SYMCRYPT_SAE_GROUP_DATA g_ianaData[] = {
+    { SYMCRYPT_SAE_GROUP_19, &SymCryptEcurveParamsNistP256, &SymCryptHmacSha256Algorithm, -10},
+    { SYMCRYPT_SAE_GROUP_20, &SymCryptEcurveParamsNistP384, &SymCryptHmacSha384Algorithm, -12},
+};
+
+//
+// Helper function that finds the associated IANA group data entry for a given group number
+// Searches the global variable g_ianaData where the data for supported groups are stored
+//
+PCSYMCRYPT_SAE_GROUP_DATA SymCryptSaeFindGroupData(SYMCRYPT_802_11_SAE_GROUP ianaGroup)
+{
+    for (UINT32 index = 0; index < SYMCRYPT_ARRAY_SIZE(g_ianaData); index++ )
+    {
+        if ( g_ianaData[index].group == ianaGroup )
+        {
+            return &g_ianaData[index];
+        }
+    }
+
+    return NULL;
+}
+
+//
+// Helper function that returns the sizes of the field elements and elliptic curve points in bytes
+// for a given IANA group number. Both output parameters are optional.
+//
+VOID SymCrypt802_11SaeGetGroupSizes(
+                                SYMCRYPT_802_11_SAE_GROUP       group,
+                    _Out_opt_   SIZE_T*                         pcbScalar,
+                    _Out_opt_   SIZE_T*                         pcbPoint )
+{
+    PCSYMCRYPT_SAE_GROUP_DATA pGroupData = NULL;
+    SIZE_T cbScalar = 0;
+    SIZE_T cbPoint = 0;
+
+    pGroupData = SymCryptSaeFindGroupData( group );
+
+    if ( pGroupData != NULL )
+    {
+        cbScalar = ( *( pGroupData->pCurveParams ) )->cbFieldLength;
+        cbPoint = 2 * cbScalar;
+    }
+
+    if ( pcbScalar != NULL )
+    {
+        *pcbScalar = cbScalar;
+    }
+
+    if ( pcbPoint != NULL )
+    {
+        *pcbPoint = cbPoint;
+    }
+}
+
+//
+// Calculate sqrt(peVal) if it exists. If so, *puIsQuadraticResidue is set to 0xFFFF`FFFF.
+// Otherwise, *puIsQuadraticResidue is set to 0.
+// WARNING: *peSqrtArg is set even if the square root doesn't exist. Use masked copy functions
+// with *puIsQuadraticResidue so as to use the value of *peSqrtArg only if the square root exists.
+//
+// - pmMod: Modulus of the curve. Must equal 3 mod 4, which holds for all NIST Prime curves except P224
+// - peVal: Value to calculate the square root of
+// - puIsQuadraticResidue: mask value, true if sqrt(peVal) exists, false otherwise
+// - peSqrtArg: optional out argument for square root value
+// - pbScratch, cbScratch: scratch space >= SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pmMod->nDigits )
+//
+SYMCRYPT_ERROR
+SymCryptModSqrt(
+    _In_                            PSYMCRYPT_MODULUS    pmMod,
+    _In_                            PSYMCRYPT_MODELEMENT peVal,
+    _Out_                           PUINT32              puIsQuadraticResidue,
+    _Out_opt_                       PSYMCRYPT_MODELEMENT peSqrtArg,
+    _Out_writes_bytes_( cbScratch ) PBYTE                pbScratch,
+                                    SIZE_T               cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_INT piTmp = SymCryptIntAllocate( SymCryptDigitsFromBits( pmMod->Divisor.nBits ) );
+    PSYMCRYPT_MODELEMENT peSqrt = SymCryptModElementAllocate( pmMod );
+    PSYMCRYPT_MODELEMENT peTmp = SymCryptModElementAllocate( pmMod );
+
+    if( piTmp == NULL || peSqrt == NULL || peTmp == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Sqrt( v ) = v^{(P+1)/4} mod P when P = 3 mod 4 as it is here
+    SymCryptIntCopy( SymCryptIntFromModulus( pmMod ), piTmp );
+    SymCryptIntAddUint32( piTmp, 1, piTmp );      // No overflow as our prime is not 2^256 - 1
+
+    SYMCRYPT_ASSERT( (SymCryptIntGetValueLsbits32(piTmp) & 3) == 0);
+    SymCryptIntDivPow2(piTmp, 2, piTmp);
+    // iX = (P+1)/4
+
+    // Compute Sqrt( v ) if it exists
+    SymCryptModExp( pmMod, peVal, piTmp, pmMod->Divisor.nBits - 2, 0, peSqrt, pbScratch, cbScratch );
+
+    SymCryptModSquare( pmMod, peSqrt, peTmp, pbScratch, cbScratch );
+    *puIsQuadraticResidue = SymCryptModElementIsEqual( pmMod, peTmp, peVal );
+
+    if( peSqrtArg != NULL )
+    {
+        SymCryptModElementCopy( pmMod, peSqrt, peSqrtArg );
+    }
+
+cleanup:
+
+    if( piTmp != NULL )
+    {
+        SymCryptIntFree( piTmp );
+        piTmp = NULL;
+    }
+
+    if( peSqrt != NULL )
+    {
+        SymCryptModElementFree( pmMod, peSqrt );
+        peSqrt = NULL;
+    }
+
+    if( peTmp != NULL )
+    {
+        SymCryptModElementFree( pmMod, peTmp );
+        peTmp = NULL;
+    }
+
+    return scError;
+
+}
+
+//
+// Calculates SSWU( u ) as described in 12.4.4.2.3
+//
+// - pCurve: The curve object to use.
+// - z: z value used in the SSWU calculation. Currently we assume this value to be negative.
+// - peU: Value to calculate SSWU of.
+// - popP: point on the curve found by SSWU.
+// - pbScratch, cbScratch: scratch space >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve )
+//
+SYMCRYPT_ERROR
+SymCryptSswu(
+    _In_                            PSYMCRYPT_ECURVE     pCurve,
+    _In_                            INT32                z,
+    _In_                            PSYMCRYPT_MODELEMENT peU,
+    _Out_                           PSYMCRYPT_ECPOINT    poP,
+    _Out_writes_bytes_( cbScratch ) PBYTE                pbScratch,
+                                    SIZE_T               cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    UINT32 selectionMask = 0; // Mask variable for masked copy operations. "l" in the spec
+
+    PSYMCRYPT_INT        piTmp = NULL;
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+
+    PSYMCRYPT_MODELEMENT peZ = NULL;
+    PSYMCRYPT_MODELEMENT peM = NULL;
+    PSYMCRYPT_MODELEMENT peT = NULL;
+    PSYMCRYPT_MODELEMENT peX1 = NULL;
+    PSYMCRYPT_MODELEMENT peX2 = NULL;
+    PSYMCRYPT_MODELEMENT peGX1 = NULL;
+    PSYMCRYPT_MODELEMENT peGX2 = NULL;
+
+    BYTE pointBuf[SYMCRYPT_SAE_MAX_EC_POINT_SIZE_BYTES] = { 0 };
+
+    SYMCRYPT_ASSERT( z < 0 );
+
+    piTmp = SymCryptIntAllocate( SymCryptDigitsFromBits( pCurve->FModBitsize ) );
+
+    peTmp = SymCryptModElementAllocate( pCurve->FMod );
+    peZ = SymCryptModElementAllocate( pCurve->FMod );
+    peM = SymCryptModElementAllocate( pCurve->FMod );
+    peT = SymCryptModElementAllocate( pCurve->FMod );
+    peX1 = SymCryptModElementAllocate( pCurve->FMod );
+    peX2 = SymCryptModElementAllocate( pCurve->FMod );
+    peGX1 = SymCryptModElementAllocate( pCurve->FMod );
+    peGX2 = SymCryptModElementAllocate( pCurve->FMod );
+
+    if( piTmp == NULL|| peTmp == NULL || peZ == NULL || peM == NULL || peT == NULL ||
+        peX1 == NULL || peX2 == NULL || peGX1 == NULL || peGX2 == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Convert z to mod element
+    // Currently we avoid a branching based on the sign of z to make the assignment and assume it will
+    // be negative which holds for the set of possible values as of now (NIST P256 and NIST P384).
+    // There is no direct function to create a SYMCRYPT_INT from a signed INT32, so when z is negative
+    // we change its sign and call SymCryptModElementSetValueNegUInt32
+    SymCryptModElementSetValueNegUint32(-z, pCurve->FMod, peZ, pbScratch, cbScratch);
+
+    // Set peTmp to 1 for convenience later
+    SymCryptModElementSetValueUint32( 1, pCurve->FMod, peTmp, pbScratch, cbScratch );
+
+    // m = ( z^2 * u^4 + z * u^2 ) = (z * u^2)(z * u^2 + 1) modulo p
+    SymCryptModSquare( pCurve->FMod, peU, peM, pbScratch, cbScratch ); // M = u^2
+    SymCryptModMul( pCurve->FMod, peM, peZ, peM, pbScratch, cbScratch ); // M = z * u^2
+    SymCryptModAdd( pCurve->FMod, peM, peTmp, peTmp, pbScratch, cbScratch ); // tmp = (z * u^2 + 1)
+    SymCryptModMul( pCurve->FMod, peM, peTmp, peM, pbScratch, cbScratch ); // M = M * tmp = (z * u^2)(z * u^2 + 1)
+
+    // l = CEQ( m, 0 )
+    selectionMask = SymCryptModElementIsZero( pCurve->FMod, peM );
+
+    // t = inverse( m ) where inverse ( m ) = m^( p-2 ) modulo p
+    SymCryptIntSubUint32( SymCryptIntFromModulus( pCurve->FMod ), 2, piTmp );
+    SymCryptModExp( pCurve->FMod, peM, piTmp, pCurve->FModBitsize, 0, peT, pbScratch, cbScratch );
+
+    //x1 = CSEL( l, ( b / ( z * a ) modulo p ), ( ( - b / a ) * ( 1 + t ) ) modulo p )
+    // where CSEL(x,y,z) operates in constant time and returns y if x is true and z otherwise.
+    SymCryptModMul( pCurve->FMod, peZ, pCurve->A, peTmp, pbScratch, cbScratch ); // tmp = z * a
+    SymCryptModInv( pCurve->FMod, peTmp, peTmp, SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME, pbScratch, cbScratch ); // tmp = 1/(z * a)
+    SymCryptModMul( pCurve->FMod, pCurve->B, peTmp, peX1, pbScratch, cbScratch ); // x1A = B * 1/(z * a)
+
+    SymCryptModInv( pCurve->FMod, pCurve->A, peTmp, SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME, pbScratch, cbScratch ); // tmp = 1/a
+    SymCryptModMul( pCurve->FMod, pCurve->B, peTmp, peTmp, pbScratch, cbScratch ); // tmp = b * 1/a
+    SymCryptModNeg( pCurve->FMod, peTmp, peTmp, pbScratch, cbScratch ); // tmp = -(b * 1/a)
+
+    // NB: in this block we're using X2 as the second candidate for CSEL. This allows us to choose the
+    // correct X1 by copying X2 to X1 if l is false
+    SymCryptIntSetValueUint32( 1, piTmp );
+    SymCryptIntToModElement( piTmp, pCurve->FMod, peX2, pbScratch, cbScratch ); // X1B = 1
+    SymCryptModAdd( pCurve->FMod, peX2, peT, peX2, pbScratch, cbScratch ); // X1B = 1 + t
+    SymCryptModMul( pCurve->FMod, peX2, peTmp, peX2, pbScratch, cbScratch ); // X1B = -(b * 1/a)(1 + t)
+
+    // Note: we need the binary complement of l since MaskedCopy copies only if the mask is 0xFFFFFFFF,
+    // and we want the second X1 candidate iff l is false
+    SymCryptModElementMaskedCopy( pCurve->FMod, peX2, peX1, ~selectionMask );
+
+    // gx1 = ( x1^3 + a * x1 + b ) = (x1^2 + a)*x1 + b modulo p
+    SymCryptModSquare( pCurve->FMod, peX1, peGX1, pbScratch, cbScratch ); // gx1 = x1^2
+    SymCryptModAdd( pCurve->FMod, peGX1, pCurve->A, peGX1, pbScratch, cbScratch ); // gx1 = x1^2 + a
+    SymCryptModMul( pCurve->FMod, peGX1, peX1, peGX1, pbScratch, cbScratch ); // gx1 = (x1^2 + a)*x1
+    SymCryptModAdd( pCurve->FMod, peGX1, pCurve->B, peGX1, pbScratch, cbScratch ); // gx1 = (x1^2 + a)*x1 + b
+
+    //x2 = ( z * u^2 * x1 ) modulo p
+    SymCryptModSquare( pCurve->FMod, peU, peX2, pbScratch, cbScratch ); // x2 = u^2
+    SymCryptModMul( pCurve->FMod, peX2, peZ, peX2, pbScratch, cbScratch ); // x2 = u^2 * z
+    SymCryptModMul( pCurve->FMod, peX2, peX1, peX2, pbScratch, cbScratch ); // x2 = u^2 * z * x1
+
+    //gx2 = ( x2^3 + a * x2 + b ) = (x2^2 + a)*x2 + b modulo p
+    SymCryptModSquare( pCurve->FMod, peX2, peGX2, pbScratch, cbScratch ); // gx2 = x2^2
+    SymCryptModAdd( pCurve->FMod, peGX2, pCurve->A, peGX2, pbScratch, cbScratch ); // gx2 = x2^2 + a
+    SymCryptModMul( pCurve->FMod, peGX2, peX2, peGX2, pbScratch, cbScratch ); // gx2 = (x2^2 + a)*x2
+    SymCryptModAdd( pCurve->FMod, peGX2, pCurve->B, peGX2, pbScratch, cbScratch ); // gx2 = (x2^2 + a)*x2 + b
+
+    //l = gx1 is a quadratic residue modulo p
+    scError = SymCryptModSqrt( pCurve->FMod, peGX1, &selectionMask, NULL, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // v = CSEL( l, gx1, gx2 )
+    // (Using gx1 as a temporary for v)
+    SymCryptModElementMaskedCopy( pCurve->FMod, peGX2, peGX1, ~selectionMask );
+
+    // x = CSEL( l, x1, x2 )
+    // (Using x1 as a temporary for x)
+    SymCryptModElementMaskedCopy( pCurve->FMod, peX2, peX1, ~selectionMask );
+
+    // y = sqrt( v ) = v^{(P+1)/4}
+    // (Using gx1 as a temporary for y)
+    scError = SymCryptModSqrt( pCurve->FMod, peGX1, &selectionMask, peGX1, pbScratch, cbScratch );
+
+    // l = CEQ( LSB( u ), LSB( y ) )
+    // LSB returns the least significant *BIT* of its argument
+    SymCryptModElementToInt( pCurve->FMod, peU, piTmp, pbScratch, cbScratch );
+    UINT32 u = SymCryptIntGetValueLsbits32( piTmp );
+
+    SymCryptModElementToInt( pCurve->FMod, peGX1, piTmp, pbScratch, cbScratch );
+    UINT32 y = SymCryptIntGetValueLsbits32( piTmp );
+
+    selectionMask = SYMCRYPT_MASK32_EQ( u & 1, y & 1 );
+
+    // P = CSEL( l, ( x, y ), ( x, p - y ) )
+    // equivalently, y = CSEL( l, y, p - y )
+    // (p - y) mod p is equivalent to -y mod p, so we end up with
+    // y = CSEL(l, y, -y)
+    // We use gx1 for y
+    SymCryptModNeg( pCurve->FMod, peGX1, peTmp, pbScratch, cbScratch );
+    SymCryptModElementMaskedCopy( pCurve->FMod, peTmp, peGX1, ~selectionMask );
+
+    SymCryptModElementGetValue( pCurve->FMod, peX1, &pointBuf[0], pCurve->FModBytesize, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+    SymCryptModElementGetValue( pCurve->FMod, peGX1, &pointBuf[pCurve->FModBytesize], pCurve->FModBytesize, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+
+    scError = SymCryptEcpointSetValue( pCurve,
+        pointBuf,
+        2 * pCurve->FModBytesize,
+        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+        SYMCRYPT_ECPOINT_FORMAT_XY,
+        poP,
+        0,
+        pbScratch,
+        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    if( peGX2 != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peGX2 );
+        peGX2 = NULL;
+    }
+
+    if( peGX1 != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peGX1 );
+        peGX1 = NULL;
+    }
+
+    if( peX2 != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peX2 );
+        peX2 = NULL;
+    }
+
+    if( peX1 != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peX1 );
+        peX1 = NULL;
+    }
+
+    if( peT != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peT );
+        peT = NULL;
+    }
+
+    if( peM != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peM );
+        peM = NULL;
+    }
+
+    if( peZ != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peZ );
+        peZ = NULL;
+    }
+
+    if( peTmp != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peTmp );
+        peTmp = NULL;
+    }
+
+    if( piTmp != NULL )
+    {
+        SymCryptIntFree( piTmp );
+        piTmp = NULL;
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomSetRandMask(
+    _Inout_                             PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+    _Inout_updates_opt_( cbRand )       PBYTE                               pbRand,
+                                        SIZE_T                              cbRand,
+    _Inout_updates_opt_( cbMask)        PBYTE                               pbMask,
+                                        SIZE_T                              cbMask,
+    _Out_writes_bytes_( cbScratch )     PBYTE                               pbScratch,
+                                        SIZE_T                              cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_ECURVE pcCurve = pState->pCurve;
+
+    SymCryptModElementSetValueUint32( 0, pcCurve->GOrd, pState->peRand, pbScratch, cbScratch );
+    if( pbRand != NULL )
+    {
+        scError = SymCryptModElementSetValue( pbRand, cbRand, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pcCurve->GOrd, pState->peRand, pbScratch, cbScratch );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    if( SymCryptModElementIsZero( pcCurve->GOrd, pState->peRand ) )
+    {
+        SymCryptModSetRandom( pcCurve->GOrd, pState->peRand, SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE, pbScratch, cbScratch );
+    }
+
+    if( pbRand != NULL )
+    {
+        scError = SymCryptModElementGetValue( pcCurve->GOrd, pState->peRand, pbRand, cbRand, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    SymCryptModElementSetValueUint32( 0, pcCurve->GOrd, pState->peMask, pbScratch, cbScratch );
+    if( pbMask != NULL )
+    {
+        scError = SymCryptModElementSetValue( pbMask, cbMask, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pcCurve->GOrd, pState->peMask, pbScratch, cbScratch );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    if( SymCryptModElementIsZero( pcCurve->GOrd, pState->peMask ) )
+    {
+        SymCryptModSetRandom( pcCurve->GOrd, pState->peMask, SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE, pbScratch, cbScratch );
+    }
+
+    if( pbMask != NULL )
+    {
+        scError = SymCryptModElementGetValue( pcCurve->GOrd, pState->peMask, pbMask, cbMask, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    //
+    // The standard calls for checking that peRand and peMask are not 0 or 1, and peRand + peMask is not 0 or 1.
+    // When the caller specifies the values we don't want to do any checking as they might be helpful in test vectors.
+    // When this code generates the random values, we avoid 0 or 1 (by not passing the flags allowing 0 and 1).
+    // We don't check that peRand + peMask > 1 because the probability of that occurring randomly is about 2^{-254} so the
+    // risk of this happening on any machine ever in the world is much smaller than the risk associated with adding several lines of code.
+    //
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomInit(
+    _Out_                       PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+    _In_reads_( 6 )             PCBYTE                              pbMac1,
+    _In_reads_( 6 )             PCBYTE                              pbMac2,
+    _In_reads_( cbPassword )    PCBYTE                              pbPassword,
+                                SIZE_T                              cbPassword,
+    _Out_opt_                   PBYTE                               pbCounter,
+    _Inout_updates_opt_( 32 )   PBYTE                               pbRand,
+    _Inout_updates_opt_( 32 )   PBYTE                               pbMask )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    BYTE counter;
+    UINT32 notFoundMask;
+    UINT32 solutionMask;
+    UINT32 negMask;
+    BYTE abSeed[SYMCRYPT_HMAC_SHA256_RESULT_SIZE];
+    BYTE abValue[SYMCRYPT_HMAC_SHA256_RESULT_SIZE];
+    BYTE abSeedKey[16];     // Need only 12, but the extra bytes make the code easier.
+    SYMCRYPT_HMAC_SHA256_EXPANDED_KEY hmacSeedKey;
+    SYMCRYPT_HMAC_SHA256_EXPANDED_KEY hmacValueKey;
+    SYMCRYPT_HMAC_SHA256_STATE hmacState;
+    BYTE abTmp[2];
+    BYTE pointBuf[ 64 ];
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    UINT64 minMac;
+    UINT64 maxMac;
+
+    UINT32  nDigits;
+    PSYMCRYPT_ECURVE        pCurve;             // Only a cache, pState->pCurve owns the allocation
+    PSYMCRYPT_INT           piTmp = NULL;
+    PSYMCRYPT_MODELEMENT    peX = NULL;
+    PSYMCRYPT_MODELEMENT    peY = NULL;
+    PSYMCRYPT_MODELEMENT    peCubic = NULL;
+    PSYMCRYPT_MODELEMENT    peTmp = NULL;
+    PSYMCRYPT_ECPOINT       poPWECandidate = NULL;
+
+    // Set state to 0 so that our pointers have valid values.
+    SymCryptWipe( pState, sizeof( *pState ) );
+
+    // Per IEEE 802.11-2016 section 12.4.4.1 the mandatory-to-implement curve is
+    // number 19 from the IANA Group description for RFC 2409 (IKE)
+    // The IANA website maps this to a 256-bit Random ECP group in RFC 5903.
+    // RFC 5903 specifies this group to be identical to the NIST P256 curve.
+    pCurve = SymCryptEcurveAllocate( SymCryptEcurveParamsNistP256, 0 );
+    pState->pCurve = pCurve;
+    if( pCurve == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pState->macAlgorithm = SymCryptHmacSha256Algorithm;
+
+    pState->peRand = SymCryptModElementAllocate( pCurve->GOrd );
+    if( pState->peRand == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pState->peMask = SymCryptModElementAllocate( pCurve->GOrd );
+    if( pState->peMask == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pState->poPWE = SymCryptEcpointAllocate( pCurve );
+    if( pState->poPWE == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    nDigits = SymCryptDigitsFromBits( PRIME_LENGTH_BITS );
+
+    cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigits ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) ) );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+
+    piTmp = SymCryptIntAllocate( nDigits );
+    peX = SymCryptModElementAllocate( pCurve->FMod );
+    peY = SymCryptModElementAllocate( pCurve->FMod );
+    peCubic = SymCryptModElementAllocate( pCurve->FMod );
+    peTmp = SymCryptModElementAllocate( pCurve->FMod );
+    poPWECandidate = SymCryptEcpointAllocate( pCurve );
+
+    if( pbScratch == NULL || piTmp == NULL || peX == NULL || peY == NULL || peCubic == NULL || peTmp == NULL || poPWECandidate == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    SymCryptWipeKnownSize( abSeedKey, sizeof( abSeedKey ) );
+    memcpy( &abSeedKey[0], pbMac1, 6 );
+    minMac = SYMCRYPT_LOAD_MSBFIRST64( abSeedKey );
+    memcpy( &abSeedKey[0], pbMac2, 6 );
+    maxMac = SYMCRYPT_LOAD_MSBFIRST64( abSeedKey );
+
+    if( minMac > maxMac )
+    {
+        // MAC values are public, no side-channel issues with this if()
+        // Swap the two values
+        minMac ^= maxMac;
+        maxMac ^= minMac;
+        minMac ^= maxMac;
+    }
+
+    // Now we write the two MACs into the buffer.
+    // Note the slight overlap, and the use of 14 bytes rather than 12
+    SYMCRYPT_STORE_MSBFIRST64( &abSeedKey[0], maxMac );
+    SYMCRYPT_STORE_MSBFIRST64( &abSeedKey[6], minMac );         // This writes up to abSeedKey[14]
+
+    SymCryptHmacSha256ExpandKey( &hmacSeedKey, abSeedKey, 12 );
+    SymCryptWipeKnownSize( abSeedKey, sizeof( abSeedKey ) );    // Not strictly speaking a secret, but good general hygiene
+
+    notFoundMask = (UINT32)-1;
+    counter = 0;
+
+    // We exit the loop only after 40 or more iterations
+    // This greatly reduces the side-channel of how often we run this loop.
+    while( notFoundMask != 0 || counter < 40 )
+    {
+        counter += 1;
+        if( counter == 0 )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        // pwd-seed = Hmac-sha256( MacA || MacB , Password || counter )
+        SymCryptHmacSha256Init( &hmacState, &hmacSeedKey );
+        SymCryptHmacSha256Append( &hmacState, pbPassword, cbPassword );
+        SymCryptHmacSha256Append( &hmacState, &counter, 1 );
+        SymCryptHmacSha256Result( &hmacState, abSeed );
+
+        // pwd-value
+        SymCryptHmacSha256ExpandKey( &hmacValueKey, abSeed, sizeof( abSeed ) );
+        SymCryptHmacSha256Init( &hmacState, &hmacValueKey );
+
+        SYMCRYPT_STORE_LSBFIRST16( abTmp, 1 );
+        SymCryptHmacSha256Append( &hmacState, abTmp, 2 );   // i value = 1
+        // Spec is unclear on whether there should be a terminating 0 on the context
+        // There are 23 characters in the string, so using len=24 gives us a zero
+        SymCryptHmacSha256Append( &hmacState, (PCBYTE) "SAE Hunting and Pecking", 23 );
+
+        // Pick up the byte representation of p from the parameters
+        SymCryptHmacSha256Append( &hmacState, (BYTE *)(SymCryptEcurveParamsNistP256 + 1), 32 );
+
+        SYMCRYPT_STORE_LSBFIRST16( abTmp, 256 );
+        SymCryptHmacSha256Append( &hmacState, abTmp, 2 );   // Length value = 256
+        SymCryptHmacSha256Result( &hmacState, abValue );
+
+        // Get the pwd-value into an integer
+        scError = SymCryptIntSetValue( abValue, sizeof( abValue ), SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piTmp );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Check that it is less than P
+        if( !SymCryptIntIsLessThan( piTmp, SymCryptIntFromModulus( pCurve->FMod ) ) )
+        {
+            // This is a slight side-channel, but our prime P starts with FFFFFFFF so the probability of
+            // hitting this case is < 2^-32.
+            continue;
+        }
+
+        // Compute x^3 + A*x + B
+        SymCryptIntToModElement( piTmp, pCurve->FMod, peX, pbScratch, cbScratch );
+        SymCryptModSquare( pCurve->FMod, peX, peCubic, pbScratch, cbScratch );
+        SymCryptModAdd( pCurve->FMod, peCubic, pCurve->A, peCubic, pbScratch, cbScratch );
+        SymCryptModMul( pCurve->FMod, peCubic, peX, peCubic, pbScratch, cbScratch );
+        SymCryptModAdd( pCurve->FMod, peCubic, pCurve->B, peCubic, pbScratch, cbScratch );
+
+        // Get the quadratic residue of (x^3 + A*x + B) modulo P if it exists
+        scError = SymCryptModSqrt( pCurve->FMod, peCubic, &solutionMask, peY, pbScratch, cbScratch );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        solutionMask &= notFoundMask;
+
+        // Pick Y or -Y according to the LSbits
+        SymCryptModElementToInt( pCurve->FMod, peY, piTmp, pbScratch, cbScratch );
+        SymCryptModNeg( pCurve->FMod, peY, peTmp, pbScratch, cbScratch );
+
+        negMask = 0 - ((abSeed[ sizeof( abSeed ) - 1 ] ^ SymCryptIntGetValueLsbits32( piTmp ) ) & 1);
+        SymCryptModElementMaskedCopy( pCurve->FMod, peTmp, peY, negMask );
+
+        SymCryptModElementGetValue( pCurve->FMod, peX, &pointBuf[ 0], 32, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+        SymCryptModElementGetValue( pCurve->FMod, peY, &pointBuf[32], 32, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+        scError = SymCryptEcpointSetValue(  pCurve,
+                                            pointBuf,
+                                            sizeof( pointBuf ),
+                                            SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                            SYMCRYPT_ECPOINT_FORMAT_XY,
+                                            poPWECandidate,
+                                            0,
+                                            pbScratch,
+                                            cbScratch );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        SymCryptEcpointMaskedCopy( pCurve, poPWECandidate, pState->poPWE, solutionMask );
+        pState->counter |= (BYTE)(counter & solutionMask);
+
+        notFoundMask &= ~solutionMask;
+    }
+
+    scError = SymCrypt802_11SaeCustomSetRandMask( pState, pbRand, 32, pbMask, 32, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if( pbCounter != NULL )
+    {
+        *pbCounter = pState->counter;
+    }
+
+cleanup:
+
+    SymCryptWipe( &hmacSeedKey, sizeof( hmacSeedKey ) );
+    SymCryptWipe( &hmacValueKey, sizeof( hmacValueKey ) );
+    SymCryptWipe( abSeed, sizeof( abSeed ) );
+    SymCryptWipe( abValue, sizeof( abValue ) );
+    SymCryptWipe( pointBuf, sizeof( pointBuf ) );
+
+    if( piTmp != NULL )
+    {
+        SymCryptIntFree( piTmp );
+        piTmp = NULL;
+    }
+
+    if( peX != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peX );
+        peX = NULL;
+    }
+
+    if( peY != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peY );
+        peY = NULL;
+    }
+
+    if( peCubic != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peCubic );
+        peCubic = NULL;
+    }
+
+    if( peTmp != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peTmp );
+        peTmp = NULL;
+    }
+
+    if( poPWECandidate != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poPWECandidate );
+        poPWECandidate = NULL;
+    }
+
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        SymCrypt802_11SaeCustomDestroy( pState );
+    }
+
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+        pbScratch = NULL;
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCreatePTGeneric(
+                                                            SYMCRYPT_802_11_SAE_GROUP           group,
+    _In_reads_( cbSsid )                                    PCBYTE                              pbSsid,
+                                                            SIZE_T                              cbSsid,
+    _In_reads_( cbPassword )                                PCBYTE                              pbPassword,
+                                                            SIZE_T                              cbPassword,
+    _In_reads_opt_( cbPasswordIdentifier )                  PCBYTE                              pbPasswordIdentifier,
+                                                            SIZE_T                              cbPasswordIdentifier,
+    _Out_writes_( cbPT )                                    PBYTE                               pbPT,
+                                                            SIZE_T                              cbPT)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SIZE_T cbIkm = 0;
+    SIZE_T cbScratch = 0;
+
+    PBYTE pbPwdValue = NULL;
+    UINT32 cbPwdValue = 0;
+    PBYTE pbScratch = NULL;
+    SYMCRYPT_HKDF_EXPANDED_KEY hkdfKey;
+
+    PSYMCRYPT_ECURVE          pCurve = NULL;
+    PCSYMCRYPT_MAC            pMacAlgorithm = NULL;
+    PSYMCRYPT_INT             piU1 = NULL;
+    PSYMCRYPT_INT             piU2 = NULL;
+    PSYMCRYPT_MODELEMENT      peU1 = NULL;
+    PSYMCRYPT_MODELEMENT      peU2 = NULL;
+
+    PSYMCRYPT_ECPOINT         poP1 = NULL;
+    PSYMCRYPT_ECPOINT         poP2 = NULL;
+    PSYMCRYPT_ECPOINT         poPT = NULL;
+
+    PCSYMCRYPT_SAE_GROUP_DATA pGroupData = NULL;
+
+
+    pGroupData = SymCryptSaeFindGroupData( group );
+
+    // Provided IANA group number must match one of the supported groups
+    if ( pGroupData == NULL)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Construct the objects associated with the IANA group number
+    pCurve = SymCryptEcurveAllocate( *( pGroupData->pCurveParams), 0 );
+    if( pCurve == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pMacAlgorithm = *( pGroupData->macAlgorithm );
+
+    const UINT32 nDigits = SymCryptEcurveDigitsofFieldElement( pCurve );
+
+    cbIkm = cbPassword + cbPasswordIdentifier;
+    cbScratch =  SYMCRYPT_MAX( cbIkm,
+                      SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ),
+                          SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigits ),
+                              SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ),
+                                  SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) ) ) ) );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+
+    // len = olen( p ) + floor( olen( p ) / 2 )
+    cbPwdValue = SYMCRYPT_BYTES_FROM_BITS(pCurve->FModBitsize) + SYMCRYPT_BYTES_FROM_BITS(pCurve->FModBitsize) / 2;
+
+    pbPwdValue = SymCryptCallbackAlloc( cbPwdValue );
+
+    piU1 = SymCryptIntAllocate( SymCryptDigitsFromBits( cbPwdValue * 8 ) );
+    piU2 = SymCryptIntAllocate( SymCryptDigitsFromBits( cbPwdValue * 8 ) );
+    peU1 = SymCryptModElementAllocate( pCurve->FMod );
+    peU2 = SymCryptModElementAllocate( pCurve->FMod );
+
+    poP1 = SymCryptEcpointAllocate( pCurve );
+    poP2 = SymCryptEcpointAllocate( pCurve );
+    poPT = SymCryptEcpointAllocate( pCurve );
+
+    if( pbScratch == NULL || pbPwdValue == NULL || piU1 == NULL || piU2 == NULL ||
+        peU1 == NULL || peU2 == NULL || poP1 == NULL || poP2 == NULL || poPT == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // pwd-seed = HKDF-Extract( ssid, password [|| identifier] )
+    // Note that SymCryptHkdfExpandKey corresponds to HKDF-Extract
+    memcpy( pbScratch, pbPassword, cbPassword );
+    if( pbPasswordIdentifier )
+    {
+        memcpy( pbScratch + cbPassword, pbPasswordIdentifier, cbPasswordIdentifier );
+    }
+
+    scError = SymCryptHkdfExpandKey( &hkdfKey, pMacAlgorithm, pbScratch, cbIkm, pbSsid, cbSsid );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // pwd-value = HKDF-Expand( pwd-seed, "SAE Hash to Element u1 P1", len )
+    // Note that SymCryptHkdf derive corresponds to HKDF-Expand
+    // Salt does not include a null terminator, so the length is 25 chars
+    scError = SymCryptHkdfDerive( &hkdfKey, (PCBYTE) "SAE Hash to Element u1 P1", 25, pbPwdValue, cbPwdValue );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // u1 = pwd-value modulo p
+    scError = SymCryptIntSetValue( pbPwdValue, cbPwdValue, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piU1 );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptIntToModElement( piU1, pCurve->FMod, peU1, pbScratch, cbScratch );
+
+    // P1 = SSWU( u1 )
+    SymCryptSswu( pCurve, pGroupData->z, peU1, poP1, pbScratch, cbScratch );
+
+    // pwd-value = HKDF-Expand( pwd-seed, "SAE Hash to Element u2 P2", len )
+    scError = SymCryptHkdfDerive( &hkdfKey, (PCBYTE) "SAE Hash to Element u2 P2", 25, pbPwdValue, cbPwdValue );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // u2 = pwd-value modulo p
+    scError = SymCryptIntSetValue( pbPwdValue, cbPwdValue, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piU2 );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptIntToModElement( piU2, pCurve->FMod, peU2, pbScratch, cbScratch );
+
+    // P2 = SSWU( u2 )
+    scError = SymCryptSswu( pCurve, pGroupData->z, peU2, poP2, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // PT = P1 + P2
+    SymCryptEcpointAdd( pCurve, poP1, poP2, poPT, 0, pbScratch, cbScratch );
+
+    scError = SymCryptEcpointGetValue( pCurve,
+                                       poPT,
+                                       SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                       SYMCRYPT_ECPOINT_FORMAT_XY,
+                                       pbPT,
+                                       cbPT,
+                                       0,
+                                       pbScratch,
+                                       cbScratch );
+    SYMCRYPT_ASSERT( scError == SYMCRYPT_NO_ERROR );
+
+cleanup:
+
+    if( poP2 != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poP2 );
+        poP2 = NULL;
+    }
+
+    if( poP1 != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poP1 );
+        poP1 = NULL;
+    }
+
+    if( poPT != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poPT );
+        poPT = NULL;
+    }
+
+    if( peU2 != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peU2 );
+        peU2 = NULL;
+    }
+
+    if( peU1 != NULL )
+    {
+        SymCryptModElementFree( pCurve->FMod, peU1 );
+        peU1 = NULL;
+    }
+
+    if( piU2 != NULL )
+    {
+        SymCryptIntFree( piU2 );
+        piU2 = NULL;
+    }
+
+    if( piU1 != NULL )
+    {
+        SymCryptIntFree( piU1 );
+        piU1 = NULL;
+    }
+
+    if( pbPwdValue != NULL )
+    {
+        SymCryptWipe( pbPwdValue, cbPwdValue );
+        SymCryptCallbackFree( pbPwdValue );
+        pbPwdValue = NULL;
+    }
+
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+        pbScratch = NULL;
+    }
+
+    if ( pCurve != NULL )
+    {
+        SymCryptEcurveFree( pCurve );
+        pCurve = NULL;
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCreatePT(
+    _In_reads_( cbSsid )                                    PCBYTE                              pbSsid,
+                                                            SIZE_T                              cbSsid,
+    _In_reads_( cbPassword )                                PCBYTE                              pbPassword,
+                                                            SIZE_T                              cbPassword,
+    _In_reads_opt_( cbPasswordIdentifier )                  PCBYTE                              pbPasswordIdentifier,
+                                                            SIZE_T                              cbPasswordIdentifier,
+    _Out_writes_( 64 )                                      PBYTE                               pbPT )
+{
+    return SymCrypt802_11SaeCustomCreatePTGeneric(  SYMCRYPT_SAE_GROUP_19,
+                                                    pbSsid,
+                                                    cbSsid,
+                                                    pbPassword,
+                                                    cbPassword,
+                                                    pbPasswordIdentifier,
+                                                    cbPasswordIdentifier,
+                                                    pbPT,
+                                                    64 );
+}
+
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomInitH2EGeneric(
+    _Out_                                                       PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+                                                                SYMCRYPT_802_11_SAE_GROUP           group,
+    _In_reads_( cbPT )                                          PCBYTE                              pbPT,
+                                                                SIZE_T                              cbPT,
+    _In_reads_( 6 )                                             PCBYTE                              pbMacA,
+    _In_reads_( 6 )                                             PCBYTE                              pbMacB,
+    _Inout_updates_opt_( cbRand )                               PBYTE                               pbRand,
+                                                                SIZE_T                              cbRand,
+    _Inout_updates_opt_( cbMask )                               PBYTE                               pbMask,
+                                                                SIZE_T                              cbMask)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    BYTE hmacKeyBytes[SYMCRYPT_SAE_MAX_HMAC_OUTPUT_SIZE_BYTES] = { 0 };
+    BYTE valBytes[SYMCRYPT_SAE_MAX_HMAC_OUTPUT_SIZE_BYTES] = { 0 };
+    BYTE macBuffer[16] = { 0 }; // Need only 12, but the extra bytes make the code easier.
+    SYMCRYPT_MAC_EXPANDED_KEY hmacKey = { 0 };
+    SYMCRYPT_MAC_STATE hmacState = { 0 };
+
+    SIZE_T cbScratch = 0;
+    PBYTE pbScratch = NULL;
+
+    UINT64 minMac = 0;
+    UINT64 maxMac = 0;
+
+    UINT32 nDigits = 0;
+
+    PSYMCRYPT_INT piTmp = NULL;
+    PSYMCRYPT_MODULUS pmMod = NULL;
+    PSYMCRYPT_MODELEMENT peVal = NULL;
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+    PSYMCRYPT_ECPOINT poPT = NULL;
+    PCSYMCRYPT_SAE_GROUP_DATA pGroupData = NULL;
+    PCSYMCRYPT_MAC pMacAlgorithm = NULL;
+
+    // Set state to 0 so that our pointers have valid values.
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+
+    PSYMCRYPT_ECURVE pCurve = NULL; // Weak reference; curve is owned by pState
+
+    pGroupData = SymCryptSaeFindGroupData( group );
+
+    // Provided IANA group number must match one of the supported groups
+    if ( pGroupData == NULL )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Construct the objects associated with the IANA group number
+    pCurve = SymCryptEcurveAllocate( *( pGroupData->pCurveParams ), 0 );
+    if ( pCurve == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pState->pCurve = pCurve;
+
+    pMacAlgorithm = *( pGroupData->macAlgorithm );
+
+    SIZE_T cbHMACOutputSize = pMacAlgorithm->resultSize;
+
+    pState->peRand = SymCryptModElementAllocate( pCurve->GOrd );
+    if( pState->peRand == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pState->peMask = SymCryptModElementAllocate( pCurve->GOrd );
+    if( pState->peMask == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pState->poPWE = SymCryptEcpointAllocate( pCurve );
+    if( pState->poPWE == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    nDigits = SymCryptDigitsFromBits( pCurve->GOrdBitsize );
+
+    piTmp = SymCryptIntAllocate( nDigits );
+    pmMod = SymCryptModulusAllocate( nDigits );
+    poPT = SymCryptEcpointAllocate( pCurve );
+
+    cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ),
+        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ),
+            SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ),
+                 SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS ( pCurve, 1 ) ) ) );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+
+    if( piTmp == NULL || pmMod == NULL || poPT == NULL || pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    memcpy( &macBuffer[0], pbMacA, 6 );
+    minMac = SYMCRYPT_LOAD_MSBFIRST64( macBuffer );
+    memcpy( &macBuffer[0], pbMacB, 6 );
+    maxMac = SYMCRYPT_LOAD_MSBFIRST64( macBuffer );
+
+    if( minMac > maxMac )
+    {
+        // MAC values are public, no side-channel issues with this if()
+        // Swap the two values
+        minMac ^= maxMac;
+        maxMac ^= minMac;
+        minMac ^= maxMac;
+    }
+
+    // Now we write the two MACs into the buffer.
+    // Note the slight overlap, and the use of 14 bytes rather than 12
+    SYMCRYPT_STORE_MSBFIRST64( &macBuffer[0], maxMac );
+    SYMCRYPT_STORE_MSBFIRST64( &macBuffer[6], minMac ); // This writes up to macBuffer[14]
+
+    // val = hmac-sha256( 0^n, maxMac || minMac )
+    // The HMAC key is is a buffer of all zeros whose length equals the length of the digest from the hash function
+    pMacAlgorithm->expandKeyFunc(&hmacKey, hmacKeyBytes, cbHMACOutputSize);
+
+    pMacAlgorithm->initFunc( &hmacState, &hmacKey );
+    pMacAlgorithm->appendFunc( &hmacState, macBuffer, 12 );
+    pMacAlgorithm->resultFunc( &hmacState, valBytes );
+
+    // val = val (#4666)modulo (q - 1) + 1
+    SymCryptIntSubUint32( SymCryptIntFromModulus(pCurve->GOrd), 1, piTmp );
+    SymCryptIntToModulus( piTmp, pmMod, 1, SYMCRYPT_FLAG_DATA_PUBLIC, pbScratch, cbScratch );
+
+    peVal = SymCryptModElementAllocate( pmMod );
+    peTmp = SymCryptModElementAllocate( pmMod );
+
+    if( peVal == NULL || peTmp == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    scError = SymCryptModElementSetValue( valBytes, cbHMACOutputSize, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pmMod, peVal, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptModElementSetValueUint32( 1, pmMod, peTmp, pbScratch, cbScratch );
+    SymCryptModAdd( pmMod, peVal, peTmp, peVal, pbScratch, cbScratch );
+
+    SymCryptModElementToInt( pmMod, peVal, piTmp, pbScratch, cbScratch );
+
+    scError = SymCryptEcpointSetValue( pCurve,
+                                       pbPT,
+                                       cbPT,
+                                       SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                       SYMCRYPT_ECPOINT_FORMAT_XY,
+                                       poPT,
+                                       0,
+                                       pbScratch,
+                                       cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptEcpointScalarMul( pCurve, piTmp, poPT, 0, pState->poPWE, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCrypt802_11SaeCustomSetRandMask( pState, pbRand, cbRand, pbMask, cbMask, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    if( peTmp != NULL )
+    {
+        SymCryptModElementFree( pmMod, peTmp );
+        peTmp = NULL;
+    }
+
+    if( peVal != NULL )
+    {
+        SymCryptModElementFree( pmMod, peVal );
+        peVal = NULL;
+    }
+
+    if( poPT != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poPT );
+        poPT = NULL;
+    }
+
+    if( pmMod != NULL )
+    {
+        SymCryptModulusFree( pmMod );
+        pmMod = NULL;
+    }
+
+    if( piTmp != NULL )
+    {
+        SymCryptIntFree( piTmp );
+        piTmp = NULL;
+    }
+
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+        pbScratch = NULL;
+    }
+
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        SymCrypt802_11SaeCustomDestroy( pState );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomInitH2E(
+    _Out_                           PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState,
+    _In_reads_( 64 )                PCBYTE                              pbPT,
+    _In_reads_( 6 )                 PCBYTE                              pbMacA,
+    _In_reads_( 6 )                 PCBYTE                              pbMacB,
+    _Inout_updates_opt_( 32 )       PBYTE                               pbRand,
+    _Inout_updates_opt_( 32 )       PBYTE                               pbMask )
+{
+    return SymCrypt802_11SaeCustomInitH2EGeneric(   pState,
+                                                    SYMCRYPT_SAE_GROUP_19,
+                                                    pbPT,
+                                                    64,
+                                                    pbMacA,
+                                                    pbMacB,
+                                                    pbRand,
+                                                    32,
+                                                    pbMask,
+                                                    32 );
+}
+
+
+VOID
+SymCrypt802_11SaeCustomDestroy(
+    _Inout_   PSYMCRYPT_802_11_SAE_CUSTOM_STATE   pState )
+{
+    PSYMCRYPT_ECURVE pCurve = pState->pCurve;
+
+    if( pState->poPWE != NULL )
+    {
+        SymCryptEcpointFree( pCurve, pState->poPWE );
+    }
+
+    if( pState->peMask != NULL )
+    {
+        SymCryptModElementFree( pCurve->GOrd, pState->peMask );
+    }
+
+    if( pState->peRand != NULL )
+    {
+        SymCryptModElementFree( pCurve->GOrd, pState->peRand );
+    }
+
+    if( pCurve != NULL )
+    {
+        SymCryptEcurveFree( pCurve );
+    }
+
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitCreateGeneric(
+    _In_                                PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _Out_writes_( cbCommitScalar )      PBYTE                               pbCommitScalar,
+                                        SIZE_T                              cbCommitScalar,
+    _Out_writes_( cbCommitElement )     PBYTE                               pbCommitElement,
+                                        SIZE_T                              cbCommitElement)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+    PSYMCRYPT_INT piTmp = NULL;
+    PSYMCRYPT_ECPOINT poPoint = NULL;
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch;
+    SIZE_T nDigits;
+
+    PCSYMCRYPT_ECURVE pCurve = pState->pCurve;
+
+    nDigits = SymCryptDigitsFromBits( pCurve->FModBitsize );
+    cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( pCurve ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) ) );
+
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+
+    peTmp = SymCryptModElementAllocate( pCurve->GOrd );
+    piTmp = SymCryptIntAllocate( SymCryptEcurveDigitsofScalarMultiplier( pCurve ) );
+    poPoint = SymCryptEcpointAllocate( pCurve );
+
+    if( peTmp == NULL || piTmp == NULL || poPoint == NULL || pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    SymCryptModAdd( pCurve->GOrd, pState->peRand, pState->peMask, peTmp, pbScratch, cbScratch );
+    scError = SymCryptModElementGetValue( pCurve->GOrd, peTmp, pbCommitScalar, cbCommitScalar, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptModElementToInt( pCurve->GOrd, pState->peMask, piTmp, pbScratch, cbScratch );
+    scError = SymCryptEcpointScalarMul( pCurve,
+                                        piTmp,
+                                        pState->poPWE,
+                                        0,
+                                        poPoint,
+                                        pbScratch,
+                                        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Now we have mask * PWE, but we need the negative...
+    SymCryptEcpointNegate( pCurve, poPoint, (UINT32)-1, pbScratch, cbScratch );
+
+    scError = SymCryptEcpointGetValue(  pCurve,
+                                        poPoint,
+                                        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                        SYMCRYPT_ECPOINT_FORMAT_XY,
+                                        pbCommitElement,
+                                        cbCommitElement,
+                                        0,
+                                        pbScratch,
+                                        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    if( piTmp != NULL )
+    {
+        SymCryptIntFree( piTmp );
+        piTmp = NULL;
+    }
+
+    if( peTmp != NULL )
+    {
+        SymCryptModElementFree( pCurve->GOrd, peTmp );
+        peTmp = NULL;
+    }
+
+    if( poPoint != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poPoint );
+        poPoint = NULL;
+    }
+
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+        pbScratch = NULL;
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitCreate(
+    _In_                  PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _Out_writes_( 32 )    PBYTE                               pbCommitScalar,
+    _Out_writes_( 64 )    PBYTE                               pbCommitElement )
+{
+    return SymCrypt802_11SaeCustomCommitCreateGeneric(  pState,
+                                                        pbCommitScalar,
+                                                        32,
+                                                        pbCommitElement,
+                                                        64 );
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitProcessGeneric(
+    _In_                                    PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _In_reads_( cbPeerCommitScalar )        PCBYTE                              pbPeerCommitScalar,
+                                            SIZE_T                              cbPeerCommitScalar,
+    _In_reads_( cbPeerCommitElement )       PCBYTE                              pbPeerCommitElement,
+                                            SIZE_T                              cbPeerCommitElement,
+    _Out_writes_( cbSharedSecret )          PBYTE                               pbSharedSecret,
+                                            SIZE_T                              cbSharedSecret,
+    _Out_writes_( cbScalarSum )             PBYTE                               pbScalarSum,
+                                            SIZE_T                              cbScalarSum )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_ECURVE        pCurve = pState->pCurve;
+    PSYMCRYPT_MODELEMENT    peCommitScalarSum = NULL;
+    PSYMCRYPT_ECPOINT       poPeerCommitElement = NULL;
+    PSYMCRYPT_ECPOINT       poTmp = NULL;
+    PSYMCRYPT_INT           piTmp = NULL;
+    UINT32                  nDigits;
+
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch;
+
+    nDigits = SymCryptDigitsFromBits( pCurve->FModBitsize );
+    cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( pCurve ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) ) ) );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+
+    peCommitScalarSum = SymCryptModElementAllocate( pCurve->GOrd );
+    poPeerCommitElement = SymCryptEcpointAllocate( pCurve );
+    poTmp = SymCryptEcpointAllocate( pCurve );
+    piTmp = SymCryptIntAllocate( SymCryptEcurveDigitsofScalarMultiplier( pCurve ) );
+
+    if( pbScratch == NULL || peCommitScalarSum == NULL || poPeerCommitElement == NULL || poTmp == NULL || piTmp == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // piTmp = peer commit value
+    scError = SymCryptIntSetValue( pbPeerCommitScalar, cbPeerCommitScalar, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piTmp );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // The Standard requires a check that the Peer commit value must be 1 < peer-commit < r where r is the group order.
+    if( !SymCryptIntIsLessThan( piTmp, SymCryptIntFromModulus( pCurve->GOrd ) ) ||
+        SymCryptIntIsEqualUint32( piTmp, 0 ) ||
+        SymCryptIntIsEqualUint32( piTmp, 1 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptIntToModElement( piTmp, pCurve->GOrd, peCommitScalarSum, pbScratch, cbScratch );
+
+    // Now compute the sum of the scalar commit values
+    SymCryptModAdd( pCurve->GOrd, peCommitScalarSum, pState->peRand, peCommitScalarSum, pbScratch, cbScratch );
+    SymCryptModAdd( pCurve->GOrd, peCommitScalarSum, pState->peMask, peCommitScalarSum, pbScratch, cbScratch );
+
+    scError = SymCryptEcpointSetValue(  pCurve,
+                                        pbPeerCommitElement,
+                                        cbPeerCommitElement,
+                                        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                        SYMCRYPT_ECPOINT_FORMAT_XY,
+                                        poPeerCommitElement,
+                                        0,
+                                        pbScratch,
+                                        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // The EcPointSetValue routine returns an error if either coordinate is >= P.
+    // We need to check that the point is on the curve and not the zero point of the curve
+    // (The zero point is sometimes called the 'point at infinity'.)
+    if( !SymCryptEcpointOnCurve( pCurve, poPeerCommitElement, pbScratch, cbScratch ) ||
+        SymCryptEcpointIsZero( pCurve, poPeerCommitElement, pbScratch, cbScratch ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+
+    scError = SymCryptEcpointScalarMul( pCurve,
+                                        piTmp,
+                                        pState->poPWE,
+                                        0,
+                                        poTmp,
+                                        pbScratch,
+                                        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptEcpointAdd( pCurve, poTmp, poPeerCommitElement, poTmp, 0, pbScratch, cbScratch );
+
+    SymCryptModElementToInt( pCurve->GOrd, pState->peRand, piTmp, pbScratch, cbScratch );
+    scError = SymCryptEcpointScalarMul( pCurve,
+                                        piTmp,
+                                        poTmp,
+                                        0,
+                                        poTmp,
+                                        pbScratch,
+                                        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptEcpointGetValue(  pCurve,
+                                        poTmp,
+                                        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                                        SYMCRYPT_ECPOINT_FORMAT_X,
+                                        pbSharedSecret,
+                                        cbSharedSecret,
+                                        0,
+                                        pbScratch,
+                                        cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptModElementGetValue( pCurve->GOrd, peCommitScalarSum, pbScalarSum, cbScalarSum, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pbScratch, cbScratch );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    if( peCommitScalarSum != NULL )
+    {
+        SymCryptModElementFree( pCurve->GOrd, peCommitScalarSum );
+        peCommitScalarSum = NULL;
+    }
+
+    if( poPeerCommitElement != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poPeerCommitElement );
+        poPeerCommitElement = NULL;
+    }
+
+    if( poTmp != NULL )
+    {
+        SymCryptEcpointFree( pCurve, poTmp );
+        poTmp = NULL;
+    }
+
+    if( piTmp != NULL )
+    {
+        SymCryptIntFree( piTmp );
+        piTmp = NULL;
+    }
+
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+        pbScratch = NULL;
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SymCrypt802_11SaeCustomCommitProcess(
+    _In_                    PCSYMCRYPT_802_11_SAE_CUSTOM_STATE  pState,
+    _In_reads_( 32 )        PCBYTE                              pbPeerCommitScalar,
+    _In_reads_( 64 )        PCBYTE                              pbPeerCommitElement,
+    _Out_writes_( 32 )      PBYTE                               pbSharedSecret,
+    _Out_writes_( 32 )      PBYTE                               pbScalarSum )
+{
+    return SymCrypt802_11SaeCustomCommitProcessGeneric( pState,
+                                                        pbPeerCommitScalar,
+                                                        32,
+                                                        pbPeerCommitElement,
+                                                        64,
+                                                        pbSharedSecret,
+                                                        32,
+                                                        pbScalarSum,
+                                                        32 );
+}
diff --git a/libs/symcrypt/lib/ScsTable.c b/libs/symcrypt/lib/ScsTable.c
new file mode 100644
index 000000000000..e9e782d4f43c
--- /dev/null
+++ b/libs/symcrypt/lib/ScsTable.c
@@ -0,0 +1,388 @@
+//
+// ScsTable.c
+// Side-channel safe table
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+// These functions implement an table of large elements.
+// Reading an element from the table is done in a way that does not reveal the
+// element accessed through memory side channels.
+// Basically, the whole table is read by the CPU, and the required data is selected
+// using boolean operations.
+//
+
+#include "precomp.h"
+
+//
+// Items are multiple of SYMCRYPT_DIGIT_SIZE long.
+//
+// Format:
+// The memory format is parameterized for optimal implementations on several
+// different architectures.
+//
+// The following parameters define the format:
+//  - group_size
+//  - interleave_size
+//
+// Let nElements be the number of elements in the table.
+// If necessary, the size of each element in the table is rounded up to a multiple of interleave_size.
+// Each whole group of group_size elements is interleaved with each other.
+// The last (nElements % group_size) elements are simply stored consecutively.
+// (For now we simply require that nElements is a multiple of group_size.)
+// Within each group of group_size, the data for the elements are interleaved in natural order
+// using chunks of interleave_size bytes.
+//
+// The choice of group_size and interleave_size depends on the CPU architecture, CPU features,
+// and even the element size. (E.g. 1024-bit elements might interleave @ 64 bytes on an AVX512
+// capable CPU, but 256-bit elements would have to interleave at 16 or 32 bytes on that same CPU.)
+//
+
+// Currently these are constants as that allows easier optimizations...
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_SCSTABLE_USE64             1
+#define SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE   32
+#define SYMCRYPT_SCSTABLE_GROUP_SIZE        4
+typedef UINT64 SYMCRYPT_SCSTABLE_TYPE;
+#else
+#define SYMCRYPT_SCSTABLE_USE64             0
+#define SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE   16
+#define SYMCRYPT_SCSTABLE_GROUP_SIZE        4
+typedef UINT32 SYMCRYPT_SCSTABLE_TYPE;
+#endif
+
+UINT32
+SYMCRYPT_CALL
+SymCryptScsTableInit(
+    _Out_   PSYMCRYPT_SCSTABLE  pScsTable,
+            UINT32              nElements,
+            UINT32              elementSize )
+{
+    UINT32  groupSize;
+    UINT32  interleaveSize;
+    UINT32  cbBuffer;
+
+    SYMCRYPT_ASSERT( nElements > 0 );
+
+#pragma warning( suppress: 4127 )       // conditional expression is constant
+    if( SYMCRYPT_CPU_AMD64 && elementSize == 128 )
+    {
+        // Highly optimized assembler mode for 1024-bit entries for RSA-2048...
+        interleaveSize = 128;
+        groupSize = 1;
+    } else {
+        // Standard C implementation
+        interleaveSize = SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE;
+        groupSize = SYMCRYPT_SCSTABLE_GROUP_SIZE;
+    }
+
+    // Right now, we limit ourselves to element sizes that are a multiple of the interleaveSize and
+    // # elements that are a multiple of the group size.
+    // We also limit ourselves to sensible input sizes
+    SYMCRYPT_ASSERT( elementSize % interleaveSize == 0 && nElements % groupSize == 0 && (elementSize | nElements) < (1 << 16) && elementSize > 0 );
+
+    cbBuffer = elementSize * nElements; // Each factor is < 2^16, so there is no overflow in the mul
+
+    pScsTable->groupSize = groupSize;
+    pScsTable->interleaveSize = interleaveSize;
+    pScsTable->nElements = nElements;
+    pScsTable->elementSize = elementSize;
+    pScsTable->cbTableData = cbBuffer;
+    pScsTable->pbTableData = NULL;
+
+    return cbBuffer;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableSetBuffer(
+    _Inout_                             PSYMCRYPT_SCSTABLE  pScsTable,
+    _Inout_updates_bytes_( cbBuffer )   PBYTE               pbBuffer,
+                                        UINT32              cbBuffer )
+{
+    SYMCRYPT_ASSERT(cbBuffer >= pScsTable->cbTableData);
+    UNREFERENCED_PARAMETER( cbBuffer );
+
+    pScsTable->pbTableData = pbBuffer;
+}
+
+
+C_ASSERT( SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE == 16 || SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE == 32 );
+// check that an interleave size is exactly 4 words
+C_ASSERT( SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE == 4 * sizeof( SYMCRYPT_SCSTABLE_TYPE ) );
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableStoreC(
+    _Inout_                     PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _In_reads_bytes_( cbData )  PCBYTE              pbData,
+                                UINT32              cbData )
+{
+    UINT32 groupSize = SYMCRYPT_SCSTABLE_GROUP_SIZE;
+    UINT32 interleaveSize = SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE;
+    UINT32 elementSize = pScsTable->elementSize;
+    UINT32 groupOffset;
+
+    SYMCRYPT_ASSERT( groupSize ==  pScsTable->groupSize );
+    SYMCRYPT_ASSERT( interleaveSize == pScsTable->interleaveSize );
+
+    SYMCRYPT_ASSERT( cbData == elementSize );
+    UNREFERENCED_PARAMETER( cbData );
+
+    SYMCRYPT_ASSERT(iIndex < pScsTable->nElements);
+
+    groupOffset = iIndex % groupSize;
+
+	// dcl - document why this can't be an integer overflow
+    SYMCRYPT_SCSTABLE_TYPE * pDst = (SYMCRYPT_SCSTABLE_TYPE *) (pScsTable->pbTableData + (iIndex - groupOffset) * elementSize + groupOffset * interleaveSize);
+    SYMCRYPT_SCSTABLE_TYPE * pSrc = (SYMCRYPT_SCSTABLE_TYPE *) pbData;
+
+    UINT32 nInterleaves = elementSize / interleaveSize;
+
+    do
+    {
+        pDst[0] = pSrc[0];
+        pDst[1] = pSrc[1];
+        pDst[2] = pSrc[2];
+        pDst[3] = pSrc[3];
+
+        pDst += interleaveSize * groupSize / sizeof( *pDst );
+        pSrc += interleaveSize / sizeof( *pSrc );
+        nInterleaves--;
+    } while( nInterleaves > 0 );
+
+}
+
+#if SYMCRYPT_CPU_AMD64
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableStore128Xmm(
+    _Inout_                     PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _In_reads_bytes_( cbData )  PCBYTE              pbData,
+                                UINT32              cbData )
+{
+    __m128i * pDst = (__m128i *) (pScsTable->pbTableData + iIndex * 128);
+    __m128i * pSrc = (__m128i *) pbData;
+
+    SYMCRYPT_ASSERT( cbData == 128 && pScsTable->elementSize == 128 && iIndex < pScsTable->nElements && pScsTable->groupSize == 1 );
+    UNREFERENCED_PARAMETER( cbData );
+
+    pDst[0] = pSrc[0];
+    pDst[1] = pSrc[1];
+    pDst[2] = pSrc[2];
+    pDst[3] = pSrc[3];
+    pDst[4] = pSrc[4];
+    pDst[5] = pSrc[5];
+    pDst[6] = pSrc[6];
+    pDst[7] = pSrc[7];
+}
+#endif // AMD64
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableLoadC(
+    _In_                        PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _Out_writes_bytes_(cbData)  PBYTE               pbData,
+                                UINT32              cbData )
+{
+    UINT32 groupSize = SYMCRYPT_SCSTABLE_GROUP_SIZE;
+    UINT32 interleaveSize = SYMCRYPT_SCSTABLE_INTERLEAVE_SIZE;
+    UINT32 elementSize = pScsTable->elementSize;
+
+    SYMCRYPT_SCSTABLE_TYPE mask0, mask1, mask2, mask3;
+    UINT32 i;
+    UINT32 j;
+    UINT32 nElements = pScsTable->nElements;
+
+    const SYMCRYPT_SCSTABLE_TYPE * pSrc = (SYMCRYPT_SCSTABLE_TYPE *) pScsTable->pbTableData;
+    SYMCRYPT_SCSTABLE_TYPE * pDst = (SYMCRYPT_SCSTABLE_TYPE *) pbData;
+    SYMCRYPT_SCSTABLE_TYPE * pD;
+
+    UINT32 nInterleaves = elementSize / interleaveSize;
+
+
+    SYMCRYPT_ASSERT( groupSize ==  pScsTable->groupSize );
+    SYMCRYPT_ASSERT( interleaveSize == pScsTable->interleaveSize );
+
+    SYMCRYPT_ASSERT( cbData >= sizeof( SYMCRYPT_SCSTABLE_TYPE ) * SYMCRYPT_SCSTABLE_GROUP_SIZE );
+    SYMCRYPT_ASSERT( cbData == pScsTable->elementSize );
+    UNREFERENCED_PARAMETER( cbData );
+
+#if SYMCRYPT_SCSTABLE_USE64
+#define SCS_MASK_EQUAL32( _a, _b )  ( ~(UINT64) ((INT64) ((UINT64)0 - (_a ^ _b)) >> 32 ) )
+#else
+#define SCS_MASK_EQUAL32( _a, _b )  (SYMCRYPT_MASK32_EQ( _a, _b ))
+#endif
+
+    i = 0;
+
+    mask0 = SCS_MASK_EQUAL32( i+0, iIndex );
+    mask1 = SCS_MASK_EQUAL32( i+1, iIndex );
+    mask2 = SCS_MASK_EQUAL32( i+2, iIndex );
+    mask3 = SCS_MASK_EQUAL32( i+3, iIndex );
+
+    j = nInterleaves;
+    pD = pDst;
+
+    do {
+        pD[0] = (mask0 & pSrc[0]) | (mask1 & pSrc[4]) | (mask2 & pSrc[ 8]) | (mask3 & pSrc[12]);
+        pD[1] = (mask0 & pSrc[1]) | (mask1 & pSrc[5]) | (mask2 & pSrc[ 9]) | (mask3 & pSrc[13]);
+        pD[2] = (mask0 & pSrc[2]) | (mask1 & pSrc[6]) | (mask2 & pSrc[10]) | (mask3 & pSrc[14]);
+        pD[3] = (mask0 & pSrc[3]) | (mask1 & pSrc[7]) | (mask2 & pSrc[11]) | (mask3 & pSrc[15]);
+        pD += interleaveSize / sizeof( *pD );
+        pSrc += interleaveSize * groupSize / sizeof( *pSrc );
+        j--;
+    } while( j > 0 );
+
+    i += groupSize;
+
+    while (i + groupSize <= nElements)
+    {
+
+        mask0 = SCS_MASK_EQUAL32( i+0, iIndex );
+        mask1 = SCS_MASK_EQUAL32( i+1, iIndex );
+        mask2 = SCS_MASK_EQUAL32( i+2, iIndex );
+        mask3 = SCS_MASK_EQUAL32( i+3, iIndex );
+
+        j = nInterleaves;
+        pD = pDst;
+
+        do {
+            pD[0] |= (mask0 & pSrc[0]) | (mask1 & pSrc[4]) | (mask2 & pSrc[ 8]) | (mask3 & pSrc[12]);
+            pD[1] |= (mask0 & pSrc[1]) | (mask1 & pSrc[5]) | (mask2 & pSrc[ 9]) | (mask3 & pSrc[13]);
+            pD[2] |= (mask0 & pSrc[2]) | (mask1 & pSrc[6]) | (mask2 & pSrc[10]) | (mask3 & pSrc[14]);
+            pD[3] |= (mask0 & pSrc[3]) | (mask1 & pSrc[7]) | (mask2 & pSrc[11]) | (mask3 & pSrc[15]);
+            pD += interleaveSize / sizeof( *pD );
+            pSrc += interleaveSize * groupSize / sizeof( *pSrc );
+            j--;
+        } while( j > 0 );
+
+        i += groupSize;
+    }
+}
+
+#if SYMCRYPT_CPU_AMD64
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableLoad128Xmm(
+    _In_                        PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _Out_writes_bytes_(cbData)  PBYTE               pbData,
+                                UINT32              cbData )
+{
+    UINT32 nElements = pScsTable->nElements;
+
+    __m128i R0, R1, R2, R3, R4, R5, R6, R7;
+    __m128i T0, T1;
+
+    __m128i Count = _mm_setzero_si128();
+    __m128i Ones = _mm_set_epi32( 1, 1, 1, 1 );
+    __m128i Entry = _mm_set_epi32( iIndex, iIndex, iIndex, iIndex );
+    __m128i Mask;
+    __m128i * pSrc = (__m128i *) pScsTable->pbTableData;
+    __m128i * pDst = (__m128i *) pbData;
+
+    SYMCRYPT_ASSERT( cbData == 128 && pScsTable->elementSize == 128 && iIndex < pScsTable->nElements && pScsTable->groupSize == 1 );
+    UNREFERENCED_PARAMETER( cbData );
+
+    Mask = _mm_cmpeq_epi32( Count, Entry );
+    Count = _mm_add_epi32( Count, Ones );
+
+    R0 = _mm_and_si128( Mask, pSrc[0] );
+    R1 = _mm_and_si128( Mask, pSrc[1] );
+    R2 = _mm_and_si128( Mask, pSrc[2] );
+    R3 = _mm_and_si128( Mask, pSrc[3] );
+    R4 = _mm_and_si128( Mask, pSrc[4] );
+    R5 = _mm_and_si128( Mask, pSrc[5] );
+    R6 = _mm_and_si128( Mask, pSrc[6] );
+    R7 = _mm_and_si128( Mask, pSrc[7] );
+
+    pSrc += 8;
+
+    while( --nElements > 0 )
+    {
+        Mask = _mm_cmpeq_epi32( Count, Entry );
+        Count = _mm_add_epi32( Count, Ones );
+
+        T0 = _mm_and_si128( Mask, pSrc[0] );        R0 = _mm_or_si128( R0, T0 );
+        T1 = _mm_and_si128( Mask, pSrc[1] );        R1 = _mm_or_si128( R1, T1 );
+        T0 = _mm_and_si128( Mask, pSrc[2] );        R2 = _mm_or_si128( R2, T0 );
+        T1 = _mm_and_si128( Mask, pSrc[3] );        R3 = _mm_or_si128( R3, T1 );
+        T0 = _mm_and_si128( Mask, pSrc[4] );        R4 = _mm_or_si128( R4, T0 );
+        T1 = _mm_and_si128( Mask, pSrc[5] );        R5 = _mm_or_si128( R5, T1 );
+        T0 = _mm_and_si128( Mask, pSrc[6] );        R6 = _mm_or_si128( R6, T0 );
+        T1 = _mm_and_si128( Mask, pSrc[7] );        R7 = _mm_or_si128( R7, T1 );
+        pSrc += 8;
+    }
+
+    pDst[0] = R0;
+    pDst[1] = R1;
+    pDst[2] = R2;
+    pDst[3] = R3;
+    pDst[4] = R4;
+    pDst[5] = R5;
+    pDst[6] = R6;
+    pDst[7] = R7;
+}
+#endif // AMD64
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableStore(
+    _Inout_                     PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _In_reads_bytes_( cbData )  PCBYTE              pbData,
+                                UINT32              cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+
+    if( pScsTable->elementSize == 128 )
+    {
+        SymCryptScsTableStore128Xmm( pScsTable, iIndex, pbData, cbData );
+    } else {
+        SymCryptScsTableStoreC( pScsTable, iIndex, pbData, cbData );
+    }
+
+#else
+
+    SymCryptScsTableStoreC( pScsTable, iIndex, pbData, cbData );
+
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableLoad(
+    _In_                        PSYMCRYPT_SCSTABLE  pScsTable,
+                                UINT32              iIndex,
+    _Out_writes_bytes_(cbData)  PBYTE               pbData,
+                                UINT32              cbData )
+{
+    // This is the side-channel safe routine
+
+#if SYMCRYPT_CPU_AMD64
+
+    if( pScsTable->elementSize == 128 )
+    {
+        SymCryptScsTableLoad128Xmm( pScsTable, iIndex, pbData, cbData );
+    } else {
+        SymCryptScsTableLoadC( pScsTable, iIndex, pbData, cbData );
+    }
+
+#else
+
+    SymCryptScsTableLoadC( pScsTable, iIndex, pbData, cbData );
+
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsTableWipe(
+    _Inout_ PSYMCRYPT_SCSTABLE pScsTable )
+{
+    SymCryptWipe( pScsTable->pbTableData, pScsTable->cbTableData );
+}
diff --git a/libs/symcrypt/lib/a_dispatch.c b/libs/symcrypt/lib/a_dispatch.c
new file mode 100644
index 000000000000..361df3051b75
--- /dev/null
+++ b/libs/symcrypt/lib/a_dispatch.c
@@ -0,0 +1,1028 @@
+//
+// a_dispatch.c   Dispatch between different arithmetic format implementations.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// SymCrypt can have multiple implementations of the arithmetic operations, and these can
+// have incompatible formats used to store the integers.
+// This file contains logic to dispatch between these incompatible formats.
+// Currently all implementations use the default format, or "Fdef".
+//
+
+#include "precomp.h"
+
+//
+// Define the FDEF dispatch table here.
+// This should eventually be split out so that different users of the library can use different
+// table sets & implementation choice functions.
+//
+
+
+const SYMCRYPT_MODULAR_FUNCTIONS g_SymCryptModFns[] = {
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_GENERIC,                // Handles any type of modulus
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY,             // Montgomery, only for odd parity-public moduli
+
+#if SYMCRYPT_CPU_AMD64 && defined(__WINE_PE_BUILD)
+
+    SYMCRYPT_MOD_FUNCTIONS_FDEF369_MONTGOMERY,          // optimized for 384 and 576-bit moduli
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX256,     // Special faster code for 256-bit Montgomery moduli, MULX-based code
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULXP384,    // Special faster code for P-384 field modulus, MULX-based code
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX,        // MULX-based code, for any size (digit size = 512 bits)
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX1024,    // Special faster code for 1024-bit Montgomery moduli, MULX-based code
+    {NULL,},
+
+    // SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULXP256,    // Special faster code for P-256 field modulus, MULX-based code
+    // SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX384,     // Special faster code for 384-bit Montgomery moduli, MULX-based code
+    // SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY256,          // Special faster code for 256-bit Montgomery moduli
+    // SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY512,          // Special faster code for 512-bit Montgomery moduli
+    // SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY1024,         // Special faster code for 1024-bit Montgomery moduli
+
+#elif SYMCRYPT_CPU_ARM64
+
+    SYMCRYPT_MOD_FUNCTIONS_FDEF369_MONTGOMERY,
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_ARM64256,
+    SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_ARM64P384,
+    {NULL,},
+    {NULL,},
+    {NULL,},
+
+#endif
+};
+
+#define SymCryptModLabel(_label)                (_label << 16)
+#define SymCryptModFntableGeneric               (SymCryptModLabel('gM') + (0 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntableMontgomery            (SymCryptModLabel('mM') + (1 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntable369Montgomery         (SymCryptModLabel('9m') + (2 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntableMontgomeryMulx256     (SymCryptModLabel('2x') + (3 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntableMontgomeryMulxP384    (SymCryptModLabel('3n') + (4 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntableMontgomeryMulx        (SymCryptModLabel('xM') + (5 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntableMontgomeryMulx1024    (SymCryptModLabel('1x') + (6 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+
+#define SymCryptModFntableMontgomeryArm64256    (SymCryptModLabel('2m') + (3 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+#define SymCryptModFntableMontgomeryArm64P384   (SymCryptModLabel('3n') + (4 * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+
+// #define SymCryptModFntableMontgomeryMulxP256    (SymCryptModLabel('2n') + (xx * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+// #define SymCryptModFntableMontgomeryMulx384     (SymCryptModLabel('3x') + (xx * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+// #define SymCryptModFntableMontgomery256         (SymCryptModLabel('2m') + (xx * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+// #define SymCryptModFntableMontgomery512         (SymCryptModLabel('5m') + (xx * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+// #define SymCryptModFntableMontgomery1024        (SymCryptModLabel('1m') + (xx * SYMCRYPT_MODULAR_FUNCTIONS_SIZE))
+
+C_ASSERT( (sizeof( g_SymCryptModFns ) & (sizeof( g_SymCryptModFns) - 1 )) == 0 ); // size of the table must be a power of 2 to be CFG-safe.
+
+const UINT32 g_SymCryptModFnsMask = sizeof( g_SymCryptModFns ) - sizeof( g_SymCryptModFns[0] );
+
+//
+// Tweaking the selection & function tables allows different tradeoffs of performance vs codesize
+//
+const SYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY SymCryptModulusTypeSelections[] =
+{
+#if SYMCRYPT_CPU_AMD64 && defined(__WINE_PE_BUILD)
+    // Mulx used for 0-512 and 577-... bits
+    {SymCryptModFntableMontgomeryMulxP384,  SYMCRYPT_CPU_FEATURES_FOR_MULX,  384,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY | SYMCRYPT_MODULUS_FEATURE_NISTP384 },
+    {SymCryptModFntableMontgomeryMulx256,   SYMCRYPT_CPU_FEATURES_FOR_MULX,  256,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntableMontgomeryMulx,      SYMCRYPT_CPU_FEATURES_FOR_MULX,  512,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntable369Montgomery,       0,                               384,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntableMontgomery,          0,                               512,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntable369Montgomery,       0,                               576,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntableMontgomeryMulx1024,  SYMCRYPT_CPU_FEATURES_FOR_MULX, 1024,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntableMontgomeryMulx,      SYMCRYPT_CPU_FEATURES_FOR_MULX,    0,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+
+#elif SYMCRYPT_CPU_ARM64
+
+    {SymCryptModFntableMontgomeryArm64P384, 0,                               384,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY | SYMCRYPT_MODULUS_FEATURE_NISTP384 },
+    {SymCryptModFntableMontgomeryArm64256,  0,                               256,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntable369Montgomery,       0,                               384,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntableMontgomery,          0,                               512,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntable369Montgomery,       0,                               576,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+
+#endif
+
+    {SymCryptModFntableMontgomery,          0,                                 0,   SYMCRYPT_MODULUS_FEATURE_MONTGOMERY },
+    {SymCryptModFntableGeneric,             0,                                 0,   0 },
+    // This last entry always matches, so the code never falls off the end of this table.
+};
+
+
+//
+// At the moment there is only the default number format.
+//
+
+UINT32
+SymCryptDigitsFromBits( UINT32 nBits )
+{
+    return SymCryptFdefDigitsFromBits( nBits );
+}
+
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntAllocate( UINT32 nDigits )
+{
+    return SymCryptFdefIntAllocate( nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntFree( _Out_ PSYMCRYPT_INT piObj )
+{
+    SymCryptIntWipe( piObj );
+    SymCryptCallbackFree( piObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofIntFromDigits( UINT32 nDigits )
+{
+    return SymCryptFdefSizeofIntFromDigits( nDigits );
+}
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits )
+{
+    return SymCryptFdefIntCreate( pbBuffer, cbBuffer, nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntWipe( _Out_ PSYMCRYPT_INT piDst )
+{
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    // Wipe the whole structure in one go;
+    SymCryptWipe( piDst, piDst->cbSize );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntCopy( piSrc, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMaskedCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          mask )
+{
+    SymCryptFdefIntMaskedCopy( piSrc, piDst, mask );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntConditionalCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          cond )
+{
+    SymCryptFdefIntConditionalCopy( piSrc, piDst, cond );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntConditionalSwap(
+    _Inout_ PSYMCRYPT_INT   piSrc1,
+    _Inout_ PSYMCRYPT_INT   piSrc2,
+            UINT32          cond )
+{
+    SymCryptFdefIntConditionalSwap( piSrc1, piSrc2, cond );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntBitsizeOfObject( _In_ PCSYMCRYPT_INT  piSrc )
+{
+    return SymCryptFdefIntBitsizeOfObject( piSrc );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntDigitsizeOfObject( _In_ PCSYMCRYPT_INT piSrc )
+{
+    return piSrc->nDigits;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntCopyMixedSize(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    return SymCryptFdefIntCopyMixedSize( piSrc, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntBitsizeOfValue( _In_ PCSYMCRYPT_INT piSrc )
+{
+    return SymCryptFdefIntBitsizeOfValue( piSrc );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSetValueUint32(
+            UINT32          u32Src,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntSetValueUint32( u32Src, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSetValueUint64(
+            UINT64          u64Src,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntSetValueUint64( u64Src, piDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntSetValue(
+    _In_reads_bytes_(cbSrc)     PCBYTE                  pbSrc,
+                                SIZE_T                  cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_                       PSYMCRYPT_INT           piDst )
+{
+    return SymCryptFdefIntSetValue( pbSrc, cbSrc, format, piDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntGetValue(
+    _In_                        PCSYMCRYPT_INT          piSrc,
+    _Out_writes_bytes_( cbDst ) PBYTE                   pbDst,
+                                SIZE_T                  cbDst,
+                                SYMCRYPT_NUMBER_FORMAT  format )
+{
+    return SymCryptFdefIntGetValue( piSrc, pbDst, cbDst, format );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntGetValueLsbits32( _In_  PCSYMCRYPT_INT piSrc )
+{
+    return SymCryptFdefIntGetValueLsbits32( piSrc );
+}
+
+UINT64
+SYMCRYPT_CALL
+SymCryptIntGetValueLsbits64( _In_  PCSYMCRYPT_INT piSrc )
+{
+    return SymCryptFdefIntGetValueLsbits64( piSrc );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntAddUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    return SymCryptFdefIntAddUint32( piSrc1, u32Src2, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntAddSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    return SymCryptFdefIntAddSameSize( piSrc1, piSrc2, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntAddMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    return SymCryptFdefIntAddMixedSize( piSrc1, piSrc2, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntSubUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    return SymCryptFdefIntSubUint32( piSrc1, u32Src2, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntSubSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    return SymCryptFdefIntSubSameSize( piSrc1, piSrc2, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntSubMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    return SymCryptFdefIntSubMixedSize( piSrc1, piSrc2, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntNeg(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntNeg( piSrc, piDst );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMulPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntMulPow2( piSrc, exp, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntDivPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntDivPow2( piSrc, exp, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntShr1(
+            UINT32          highestBit,
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntShr1( highestBit, piSrc, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntModPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SymCryptFdefIntModPow2( piSrc, exp, piDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntGetBit(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit )
+{
+    return SymCryptFdefIntGetBit( piSrc, iBit );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntGetBits(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit,
+            UINT32          nBits )
+{
+    return SymCryptFdefIntGetBits( piSrc, iBit, nBits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSetBits(
+    _In_    PSYMCRYPT_INT   piDst,
+            UINT32          value,
+            UINT32          iBit,
+            UINT32          nBits )
+{
+    SymCryptFdefIntSetBits( piDst, value, iBit, nBits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntIsEqualUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    UINT32          u32Src2 )
+{
+    return SymCryptFdefIntIsEqualUint32( piSrc1, u32Src2 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntIsEqual(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 )
+{
+    return SymCryptFdefIntIsEqual( piSrc1, piSrc2 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntIsLessThan(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 )
+{
+    return SymCryptFdefIntIsLessThan( piSrc1, piSrc2 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntMulUint32(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+                                    UINT32          Src2,
+    _Out_                           PSYMCRYPT_INT   piDst )
+{
+    return SymCryptFdefIntMulUint32( piSrc1, Src2, piDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMulSameSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    SymCryptFdefIntMulSameSize( piSrc1, piSrc2, piDst, pbScratch, cbScratch );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntSquare(
+    _In_                            PCSYMCRYPT_INT  piSrc,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    SymCryptFdefIntSquare( piSrc, piDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntMulMixedSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    SymCryptFdefIntMulMixedSize( piSrc1, piSrc2, piDst, pbScratch, cbScratch );
+}
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptDivisorAllocate( UINT32 nDigits )
+{
+    return SymCryptFdefDivisorAllocate( nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDivisorFree( _Out_ PSYMCRYPT_DIVISOR pdObj )
+{
+    SymCryptDivisorWipe( pdObj );
+    SymCryptCallbackFree( pdObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofDivisorFromDigits( UINT32 nDigits )
+{
+    return SymCryptFdefSizeofDivisorFromDigits( nDigits );
+}
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptDivisorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits )
+{
+    return SymCryptFdefDivisorCreate( pbBuffer, cbBuffer, nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDivisorWipe( _Out_ PSYMCRYPT_DIVISOR pdObj )
+{
+    SYMCRYPT_CHECK_MAGIC( pdObj );
+
+    SymCryptWipe( pdObj, pdObj->cbSize );
+}
+
+VOID
+SymCryptDivisorCopy(
+    _In_    PCSYMCRYPT_DIVISOR  pdSrc,
+    _Out_   PSYMCRYPT_DIVISOR   pdDst )
+{
+    SymCryptFdefDivisorCopy( pdSrc, pdDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDivisorDigitsizeOfObject( _In_ PCSYMCRYPT_DIVISOR pdSrc )
+{
+    return pdSrc->nDigits;
+}
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntFromDivisor( _In_ PSYMCRYPT_DIVISOR pdSrc )
+{
+    return SymCryptFdefIntFromDivisor( pdSrc );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntToDivisor(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_DIVISOR   pdDst,
+                                    UINT32              totalOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    SymCryptFdefIntToDivisor( piSrc, pdDst, totalOperations, flags, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntDivMod(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _In_                            PCSYMCRYPT_DIVISOR  pdDivisor,
+    _Out_opt_                       PSYMCRYPT_INT       piQuotient,
+    _Out_opt_                       PSYMCRYPT_INT       piRemainder,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    SymCryptFdefIntDivMod( piSrc, pdDivisor, piQuotient, piRemainder, pbScratch, cbScratch );
+}
+
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptModulusAllocate( UINT32 nDigits )
+{
+    return SymCryptFdefModulusAllocate( nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModulusFree( _Out_ PSYMCRYPT_MODULUS pmObj )
+{
+    SymCryptFdefModulusFree( pmObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofModulusFromDigits( UINT32 nDigits )
+{
+    return SymCryptFdefSizeofModulusFromDigits( nDigits );
+}
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptModulusCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits )
+{
+    return SymCryptFdefModulusCreate( pbBuffer, cbBuffer, nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModulusWipe( _Out_ PSYMCRYPT_MODULUS pmObj )
+{
+    SYMCRYPT_CHECK_MAGIC( pmObj );
+
+    SymCryptWipe( pmObj, pmObj->cbSize );
+}
+
+VOID
+SymCryptModulusCopy(
+    _In_    PCSYMCRYPT_MODULUS  pmSrc,
+    _Out_   PSYMCRYPT_MODULUS   pmDst )
+{
+    SymCryptFdefModulusCopy( pmSrc, pmDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptModulusDigitsizeOfObject( _In_ PCSYMCRYPT_MODULUS pmSrc )
+{
+    return pmSrc->nDigits;
+}
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptModElementAllocate( _In_ PCSYMCRYPT_MODULUS pmMod )
+{
+    return SymCryptFdefModElementAllocate( pmMod );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementFree(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peObj )
+{
+    SymCryptFdefModElementFree( pmMod, peObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofModElementFromModulus( PCSYMCRYPT_MODULUS pmMod )
+{
+    return SymCryptFdefSizeofModElementFromModulus( pmMod );
+}
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptModElementCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+    _In_                            PCSYMCRYPT_MODULUS  pmMod )
+{
+    return SymCryptFdefModElementCreate( pbBuffer, cbBuffer, pmMod );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementWipe(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst )
+{
+    SymCryptFdefModElementWipe( pmMod, peDst );
+}
+
+VOID
+SymCryptModElementCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst )
+{
+    SymCryptFdefModElementCopy( pmMod, peSrc, peDst );
+}
+
+VOID
+SymCryptModElementMaskedCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst,
+            UINT32                  mask )
+{
+    SymCryptFdefModElementMaskedCopy( pmMod, peSrc, peDst, mask );
+}
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptDivisorFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc )
+{
+    return SymCryptFdefDivisorFromModulus( pmSrc );
+}
+
+VOID
+SymCryptModElementConditionalSwap(
+    _In_     PCSYMCRYPT_MODULUS    pmMod,
+    _Inout_  PSYMCRYPT_MODELEMENT  peData1,
+    _Inout_  PSYMCRYPT_MODELEMENT  peData2,
+    _In_     UINT32                cond )
+{
+     SymCryptFdefModElementConditionalSwap( pmMod, peData1, peData2, cond );
+}
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptIntFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc )
+{
+    return SymCryptFdefIntFromModulus( pmSrc );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntToModulus(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_MODULUS   pmDst,
+                                    UINT32              averageOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    PSYMCRYPT_INT piSrcTweak = (PSYMCRYPT_INT) piSrc;
+
+    // In CHKed build, we'll verify that the modulus is not prime, or that it is 2 or odd
+    // (Some inversion algorithms fail hard when one input isn't 2 or odd.)
+    // We are constant-time w.r.t. piSrc being odd or =2. We don't hide the size of any input,
+    // but inputs 2 and 3 are handled with the same code path.
+    SYMCRYPT_ASSERT( ((flags & SYMCRYPT_FLAG_MODULUS_PRIME) == 0) ||
+        (((SymCryptIntGetValueLsbits32( piSrc ) & 1) | SymCryptIntIsEqualUint32( piSrc, 2 )) != 0) );
+
+    SymCryptFdefIntToModulus( piSrcTweak, pmDst, averageOperations, flags, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptIntToModElement(
+    _In_                            PCSYMCRYPT_INT          piSrc,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefIntToModElement( piSrc, pmMod, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModElementToInt(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_INT           piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    PCUINT32 pData;
+
+    SYMCRYPT_ASSERT( piDst->nDigits >= pmMod->nDigits );
+
+    pData = SYMCRYPT_MOD_CALL( pmMod ) modPreGet( pmMod, peSrc, pbScratch, cbScratch );
+
+    SymCryptFdefModElementToIntGeneric( pmMod, pData, piDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModElementSetValue(
+    _In_reads_bytes_( cbSrc )       PCBYTE                  pbSrc,
+                                    SIZE_T                  cbSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+                                    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR  scError;
+
+    scError = SymCryptFdefModElementSetValueGeneric( pbSrc, cbSrc, format, pmMod, peDst, pbScratch, cbScratch );
+
+    if( scError == SYMCRYPT_NO_ERROR )
+    {
+        SYMCRYPT_MOD_CALL( pmMod ) modSetPost( pmMod, peDst, pbScratch, cbScratch );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModElementGetValue(
+                                    PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_writes_bytes_( cbDst )     PBYTE                   pbDst,
+                                    SIZE_T                  cbDst,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    return SymCryptFdefModElementGetValue( pmMod, peSrc, pbDst, cbDst, format, pbScratch, cbScratch );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptModElementIsEqual(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc1,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc2 )
+{
+    return SymCryptFdefModElementIsEqual( pmMod, peSrc1, peSrc2 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptModElementIsZero(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc )
+{
+    return SymCryptFdefModElementIsZero( pmMod, peSrc );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModAdd(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_MOD_CALL( pmMod ) modAdd( pmMod, peSrc1, peSrc2, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModSub(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_MOD_CALL( pmMod ) modSub( pmMod, peSrc1, peSrc2, peDst, pbScratch, cbScratch );
+}
+
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModMul(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_MOD_CALL( pmMod ) modMul( pmMod, peSrc1, peSrc2, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModSquare(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_MOD_CALL( pmMod ) modSquare( pmMod, peSrc, peDst, pbScratch, cbScratch );
+}
+
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModNeg(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_MOD_CALL( pmMod ) modNeg( pmMod, peSrc, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModElementSetValueUint32(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModElementSetValueUint32Generic( value, pmMod, peDst, pbScratch, cbScratch );
+
+    SYMCRYPT_MOD_CALL( pmMod ) modSetPost( pmMod, peDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModElementSetValueNegUint32(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModElementSetValueNegUint32( value, pmMod, peDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModDivPow2(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+                                    UINT32                  exp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModDivPow2( pmMod, peSrc, exp, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModInv(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    return SYMCRYPT_MOD_CALL( pmMod ) modInv( pmMod, peSrc, peDst, flags, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModExp(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
+    _In_                            PCSYMCRYPT_INT          piExp,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptModExpGeneric( pmMod, peBase, piExp, nBitsExp, flags, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModMultiExp(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
+    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
+                                    UINT32                  nBases,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    return SymCryptModMultiExpGeneric( pmMod, peBaseArray, piExpArray, nBases, nBitsExp, flags, peDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptModSetRandom(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModSetRandomGeneric( pmMod, peDst, flags, pbScratch, cbScratch );
+
+    SYMCRYPT_MOD_CALL( pmMod ) modSetPost( pmMod, peDst, pbScratch, cbScratch );
+}
+
+PCSYMCRYPT_TRIALDIVISION_CONTEXT
+SYMCRYPT_CALL
+SymCryptCreateTrialDivisionContext( UINT32 nDigits )
+{
+    return SymCryptFdefCreateTrialDivisionContext( nDigits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntFindSmallDivisor(
+    _In_                            PCSYMCRYPT_TRIALDIVISION_CONTEXT    pContext,
+    _In_                            PCSYMCRYPT_INT                      piSrc,
+    _Out_writes_bytes_( cbScratch ) PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch )
+{
+    return SymCryptFdefIntFindSmallDivisor( pContext, piSrc, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFreeTrialDivisionContext( PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext )
+{
+    SymCryptFdefFreeTrialDivisionContext( pContext );
+}
diff --git a/libs/symcrypt/lib/aes-asm.c b/libs/symcrypt/lib/aes-asm.c
new file mode 100644
index 000000000000..e80f3bc3b847
--- /dev/null
+++ b/libs/symcrypt/lib/aes-asm.c
@@ -0,0 +1,46 @@
+//
+// aes-asm.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+
+#include "precomp.h"
+
+#if (SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM) && defined(__WINE_PE_BUILD)
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesEncryptAsm( pExpandedKey, pbSrc, pbDst );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbDecryptAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesDecryptAsm( pExpandedKey, pbSrc, pbDst );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+}
+
+#endif
diff --git a/libs/symcrypt/lib/aes-c.c b/libs/symcrypt/lib/aes-c.c
new file mode 100644
index 000000000000..ad09d2f2c71a
--- /dev/null
+++ b/libs/symcrypt/lib/aes-c.c
@@ -0,0 +1,391 @@
+//
+// aes-c.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// The fast-ish C implementation of the core AES functions
+//
+// Separate C file because at some point we want to be able to switch this out with a compact-C implementation
+// that is smaller.
+//
+
+#include "precomp.h"
+
+//
+// Static vs. dynamically generated tables.
+//
+// AES uses about 13 kB of tables; it turns out that most of these tables can be generated
+// algorithmically much faster than they can be read off the disk.
+// This implementation does not do so.
+// The reason is that generated tables live in the modifyable data segment, which means
+// that they are not shared between different instances of a DLL.
+// Static tables are shared. Especially for applications that have a very large number
+// of processes (e.g. Terminal Servers) the extra cost of generating and storing a
+// per-process copy of these tables is higher then the cost of loading it a few times
+// from disk.
+// Earlier versions of this implementation did generate the tables dynamically and ran into
+// this very problem.
+//
+// Our tables are aligned to eliminate side-channels from TLB lookups if the TLB page size
+// is big enough. For example, the SboxMatrixMult table is 1024-aligned. Each use of that
+// table consists of 4 lookups, and each lookup is within its own 1kB aligned subtable.
+// The side-channels from cache lines still remains, of course.
+//
+
+//extern BYTE SymCryptAesSbox[256];                   // Basic S-box, not used
+extern SYMCRYPT_ALIGN_AT( 256) BYTE SymCryptAesInvSbox[256];                // For final round in decryption
+extern SYMCRYPT_ALIGN_AT(1024) BYTE SymCryptAesSboxMatrixMult[4][256][4];   // Main encryption tables
+extern SYMCRYPT_ALIGN_AT(1024) BYTE SymCryptAesInvSboxMatrixMult[4][256][4];// Main decryption tables
+extern SYMCRYPT_ALIGN_AT(1024) BYTE SymCryptAesInvMatrixMult[4][256][4];    // For computing decryption round keys
+
+//
+// Throughout this implementation we use UINT32s to access byte arrays. The AES
+// algorithm almost requires this; without it the performance would be abysmal.
+// All data elements are SYMCRYPT_ALIGNed, which must be at least 4.
+//
+
+//
+// Macro to check for alignment to support platforms that need alignment fix-ups.
+//
+#define IS_UINT32_ALIGNED( __p )   ((((intptr_t)__p) & 3) == 0)
+
+//
+// Only need to enforce alignment on platforms that are not x86 or x64
+// Future improvement: should switch to using unaligned pointer accesses
+// on some platforms.
+//
+#define NEED_ALIGN (!(SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64))
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4SboxC(
+    _In_reads_(4)   PCBYTE  pIn,
+    _Out_writes_(4) PBYTE   pOut )
+//
+// Perform 4 S-box lookups.
+// This is a separate function as it can be done side-channel safe using
+// AES-NI.
+// Key expansion can actually be improved a lot more with AES-NI, but that
+// requires major code changes for which we don't have time right now.
+//
+{
+    pOut[0] = SymCryptAesSboxMatrixMult[0][pIn[0]][1];
+    pOut[1] = SymCryptAesSboxMatrixMult[0][pIn[1]][1];
+    pOut[2] = SymCryptAesSboxMatrixMult[0][pIn[2]][1];
+    pOut[3] = SymCryptAesSboxMatrixMult[0][pIn[3]][1];
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKeyC(
+    _In_reads_(16)     PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey )
+//
+// Convert an encryption round key to a decryption round key by applying the inverse
+// mixcolumn function to each 4-byte subword.
+// This is a separate function as with AES-NI there is an assembler version of this
+// function that is side-channel safe.
+//
+{
+    int i;
+    PBYTE p = pDecryptionRoundKey;
+    PCBYTE q = pEncryptionRoundKey;
+
+    for( i=0; i<4; i++ ) {
+        *(UINT32 *)p =
+            *(UINT32 *)SymCryptAesInvMatrixMult[0][q[0]] ^
+            *(UINT32 *)SymCryptAesInvMatrixMult[1][q[1]] ^
+            *(UINT32 *)SymCryptAesInvMatrixMult[2][q[2]] ^
+            *(UINT32 *)SymCryptAesInvMatrixMult[3][q[3]];
+        p += 4;
+        q += 4;
+    }
+
+}
+
+//
+// SymCryptAesEncrypt
+// NOINLINE prevents the compiler from creating additional implementations
+// that have to be FIPS selftested.
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptC(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(SYMCRYPT_AES_BLOCK_SIZE)     PCBYTE      pbPlaintext,
+    _Out_writes_(SYMCRYPT_AES_BLOCK_SIZE)   PBYTE       pbCiphertext )
+{
+    SYMCRYPT_ALIGN BYTE state[4][4] = { 0 };
+    SYMCRYPT_ALIGN UINT32 state2[4] = { 0 };
+
+    const BYTE (*keyPtr)[4][4];
+    const BYTE (*keyLimit)[4][4];
+
+#if NEED_ALIGN
+    SYMCRYPT_ALIGN BYTE   alignBuffer[SYMCRYPT_AES_BLOCK_SIZE];
+#endif
+
+#if NEED_ALIGN
+
+    //
+    // Callers who don't have their buffers aligned don't care about speed,
+    // so we do this in the simplest way.
+    //
+    if( !(IS_UINT32_ALIGNED( pbPlaintext ) & IS_UINT32_ALIGNED( pbCiphertext )) ) {
+        memcpy( alignBuffer, pbPlaintext, SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptAesEncrypt( pExpandedKey, alignBuffer, alignBuffer );
+        memcpy( pbCiphertext, alignBuffer, SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptWipeKnownSize( alignBuffer, sizeof( alignBuffer ) );
+        return;
+    }
+#endif
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    //
+    // From this point on all our data is UINT32 aligned or better on those
+    // platforms that have alignment restrictions.
+    //
+
+    keyPtr = &pExpandedKey->RoundKey[0];            // First round key
+    keyLimit = &pExpandedKey->lastEncRoundKey[0];   // Last round key
+
+    // Initial round (AddRoundKey)
+    *((UINT32 *) &state[0][0]) = *(UINT32 *) (*keyPtr)[0] ^ *(UINT32 *) &pbPlaintext[0];
+    *((UINT32 *) &state[1][0]) = *(UINT32 *) (*keyPtr)[1] ^ *(UINT32 *) &pbPlaintext[4];
+    *((UINT32 *) &state[2][0]) = *(UINT32 *) (*keyPtr)[2] ^ *(UINT32 *) &pbPlaintext[8];
+    *((UINT32 *) &state[3][0]) = *(UINT32 *) (*keyPtr)[3] ^ *(UINT32 *) &pbPlaintext[12];
+
+    keyPtr += 1;
+
+    // Main rounds
+    while (keyPtr < keyLimit)
+    {
+
+        // SubBytes/ShiftRows/MixColumns for col. 0
+        state2[0] = *((UINT32 *) &SymCryptAesSboxMatrixMult[0][ state[0][0] ]);
+        state2[3] = *((UINT32 *) &SymCryptAesSboxMatrixMult[1][ state[0][1] ]);
+        state2[2] = *((UINT32 *) &SymCryptAesSboxMatrixMult[2][ state[0][2] ]);
+        state2[1] = *((UINT32 *) &SymCryptAesSboxMatrixMult[3][ state[0][3] ]);
+
+        // SubBytes/ShiftRows/MixColumns for col. 1
+        state2[1] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[0][ state[1][0] ]);
+        state2[0] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[1][ state[1][1] ]);
+        state2[3] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[2][ state[1][2] ]);
+        state2[2] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[3][ state[1][3] ]);
+
+        // SubBytes/ShiftRows/MixColumns for col. 2
+        state2[2] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[0][ state[2][0] ]);
+        state2[1] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[1][ state[2][1] ]);
+        state2[0] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[2][ state[2][2] ]);
+        state2[3] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[3][ state[2][3] ]);
+
+        // SubBytes/ShiftRows/MixColumns for col. 3
+        state2[3] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[0][ state[3][0] ]);
+        state2[2] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[1][ state[3][1] ]);
+        state2[1] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[2][ state[3][2] ]);
+        state2[0] ^= *((UINT32 *) &SymCryptAesSboxMatrixMult[3][ state[3][3] ]);
+
+        // AddRoundKey
+        *((UINT32 *) &state[0][0]) = *(UINT32 *) (*keyPtr)[0] ^ state2[0];
+        *((UINT32 *) &state[1][0]) = *(UINT32 *) (*keyPtr)[1] ^ state2[1];
+        *((UINT32 *) &state[2][0]) = *(UINT32 *) (*keyPtr)[2] ^ state2[2];
+        *((UINT32 *) &state[3][0]) = *(UINT32 *) (*keyPtr)[3] ^ state2[3];
+
+        keyPtr += 1;
+    }
+
+    // Final round
+
+    // SubBytes/ShiftRows for col. 0
+    state2[0] = (UINT32) SymCryptAesSboxMatrixMult[0][ state[0][0] ][1];
+    state2[3] = (UINT32) SymCryptAesSboxMatrixMult[0][ state[0][1] ][1] << 8;
+    state2[2] = (UINT32) SymCryptAesSboxMatrixMult[0][ state[0][2] ][1] << 16;
+    state2[1] = (UINT32) SymCryptAesSboxMatrixMult[0][ state[0][3] ][1] << 24;
+
+    // SubBytes/ShiftRows for col. 1
+    state2[1] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[1][0] ][1];
+    state2[0] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[1][1] ][1] << 8;
+    state2[3] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[1][2] ][1] << 16;
+    state2[2] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[1][3] ][1] << 24;
+
+    // SubBytes/ShiftRows for col. 2
+    state2[2] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[2][0] ][1];
+    state2[1] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[2][1] ][1] << 8;
+    state2[0] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[2][2] ][1] << 16;
+    state2[3] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[2][3] ][1] << 24;
+
+    // SubBytes/ShiftRows for col. 3
+    state2[3] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[3][0] ][1];
+    state2[2] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[3][1] ][1] << 8;
+    state2[1] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[3][2] ][1] << 16;
+    state2[0] |= (UINT32) SymCryptAesSboxMatrixMult[0][ state[3][3] ][1] << 24;
+
+    // AddRoundKey
+    *((UINT32 *) &pbCiphertext[0 ]) = *(UINT32 *) (*keyPtr)[0] ^ state2[0];
+    *((UINT32 *) &pbCiphertext[4 ]) = *(UINT32 *) (*keyPtr)[1] ^ state2[1];
+    *((UINT32 *) &pbCiphertext[8 ]) = *(UINT32 *) (*keyPtr)[2] ^ state2[2];
+    *((UINT32 *) &pbCiphertext[12]) = *(UINT32 *) (*keyPtr)[3] ^ state2[3];
+
+    SymCryptWipeKnownSize( state, sizeof( state ) );
+    SymCryptWipeKnownSize( state2, sizeof( state2 ) );
+
+    return;
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptC(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(SYMCRYPT_AES_BLOCK_SIZE)     PCBYTE                      pbCiphertext,
+    _Out_writes_(SYMCRYPT_AES_BLOCK_SIZE)   PBYTE                       pbPlaintext )
+{
+    SYMCRYPT_ALIGN BYTE state[4][4] = { 0 };
+    SYMCRYPT_ALIGN UINT32 state2[4] = { 0 };
+
+    const BYTE (*keyPtr)[4][4];
+    const BYTE (*keyLimit)[4][4];
+
+#if NEED_ALIGN
+    SYMCRYPT_ALIGN BYTE   alignBuffer[SYMCRYPT_AES_BLOCK_SIZE];
+#endif
+
+#if NEED_ALIGN
+    //
+    // Callers who don't have their buffers aligned don't care about speed,
+    // so we do this in the simplest way.
+    //
+    if( !(IS_UINT32_ALIGNED( pbPlaintext ) & IS_UINT32_ALIGNED( pbCiphertext )) ) {
+        memcpy( alignBuffer, pbCiphertext, SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptAesDecrypt( pExpandedKey, alignBuffer, alignBuffer );
+        memcpy( pbPlaintext, alignBuffer, SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptWipeKnownSize( alignBuffer, sizeof( alignBuffer ) );
+        return;
+    }
+#endif
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    keyPtr = &pExpandedKey->lastEncRoundKey[0];     // First round key
+    keyLimit = &pExpandedKey->lastDecRoundKey[0];   // Last round key
+
+    // Initial round (AddRoundKey)
+    *((UINT32 *) &state[0][0]) = *(UINT32 *) (*keyPtr)[0] ^ *(UINT32 *) &pbCiphertext[0];
+    *((UINT32 *) &state[1][0]) = *(UINT32 *) (*keyPtr)[1] ^ *(UINT32 *) &pbCiphertext[4];
+    *((UINT32 *) &state[2][0]) = *(UINT32 *) (*keyPtr)[2] ^ *(UINT32 *) &pbCiphertext[8];
+    *((UINT32 *) &state[3][0]) = *(UINT32 *) (*keyPtr)[3] ^ *(UINT32 *) &pbCiphertext[12];
+
+    keyPtr += 1;
+
+    // Main rounds
+    while (keyPtr < keyLimit)
+    {
+
+        // SubBytes/ShiftRows/MixColumns for col. 0
+        state2[0] = *((UINT32 *) &SymCryptAesInvSboxMatrixMult[0][ state[0][0] ]);
+        state2[1] = *((UINT32 *) &SymCryptAesInvSboxMatrixMult[1][ state[0][1] ]);
+        state2[2] = *((UINT32 *) &SymCryptAesInvSboxMatrixMult[2][ state[0][2] ]);
+        state2[3] = *((UINT32 *) &SymCryptAesInvSboxMatrixMult[3][ state[0][3] ]);
+
+        // SubBytes/ShiftRows/MixColumns for col. 1
+        state2[1] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[0][ state[1][0] ]);
+        state2[2] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[1][ state[1][1] ]);
+        state2[3] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[2][ state[1][2] ]);
+        state2[0] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[3][ state[1][3] ]);
+
+        // SubBytes/ShiftRows/MixColumns for col. 2
+        state2[2] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[0][ state[2][0] ]);
+        state2[3] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[1][ state[2][1] ]);
+        state2[0] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[2][ state[2][2] ]);
+        state2[1] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[3][ state[2][3] ]);
+
+        // SubBytes/ShiftRows/MixColumns for col. 3
+        state2[3] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[0][ state[3][0] ]);
+        state2[0] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[1][ state[3][1] ]);
+        state2[1] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[2][ state[3][2] ]);
+        state2[2] ^= *((UINT32 *) &SymCryptAesInvSboxMatrixMult[3][ state[3][3] ]);
+
+        // AddRoundKey
+        *((UINT32 *) &state[0][0]) = *(UINT32 *) (*keyPtr)[0] ^ state2[0];
+        *((UINT32 *) &state[1][0]) = *(UINT32 *) (*keyPtr)[1] ^ state2[1];
+        *((UINT32 *) &state[2][0]) = *(UINT32 *) (*keyPtr)[2] ^ state2[2];
+        *((UINT32 *) &state[3][0]) = *(UINT32 *) (*keyPtr)[3] ^ state2[3];
+
+        keyPtr += 1;
+    }
+
+    // Final round
+
+    // SubBytes/ShiftRows for col. 0
+    state2[0] = (UINT32) SymCryptAesInvSbox[ state[0][0] ];
+    state2[1] = (UINT32) SymCryptAesInvSbox[ state[0][1] ] << 8;
+    state2[2] = (UINT32) SymCryptAesInvSbox[ state[0][2] ] << 16;
+    state2[3] = (UINT32) SymCryptAesInvSbox[ state[0][3] ] << 24;
+
+    // SubBytes/ShiftRows for col. 1
+    state2[1] |= (UINT32) SymCryptAesInvSbox[ state[1][0] ];
+    state2[2] |= (UINT32) SymCryptAesInvSbox[ state[1][1] ] << 8;
+    state2[3] |= (UINT32) SymCryptAesInvSbox[ state[1][2] ] << 16;
+    state2[0] |= (UINT32) SymCryptAesInvSbox[ state[1][3] ] << 24;
+
+    // SubBytes/ShiftRows for col. 2
+    state2[2] |= (UINT32) SymCryptAesInvSbox[ state[2][0] ];
+    state2[3] |= (UINT32) SymCryptAesInvSbox[ state[2][1] ] << 8;
+    state2[0] |= (UINT32) SymCryptAesInvSbox[ state[2][2] ] << 16;
+    state2[1] |= (UINT32) SymCryptAesInvSbox[ state[2][3] ] << 24;
+
+    // SubBytes/ShiftRows for col. 3
+    state2[3] |= (UINT32) SymCryptAesInvSbox[ state[3][0] ];
+    state2[0] |= (UINT32) SymCryptAesInvSbox[ state[3][1] ] << 8;
+    state2[1] |= (UINT32) SymCryptAesInvSbox[ state[3][2] ] << 16;
+    state2[2] |= (UINT32) SymCryptAesInvSbox[ state[3][3] ] << 24;
+
+    // AddRoundKey
+    *((UINT32 *) &pbPlaintext[0 ]) = *(UINT32 *) (*keyPtr)[0] ^ state2[0];
+    *((UINT32 *) &pbPlaintext[4 ]) = *(UINT32 *) (*keyPtr)[1] ^ state2[1];
+    *((UINT32 *) &pbPlaintext[8 ]) = *(UINT32 *) (*keyPtr)[2] ^ state2[2];
+    *((UINT32 *) &pbPlaintext[12]) = *(UINT32 *) (*keyPtr)[3] ^ state2[3];
+
+    SymCryptWipeKnownSize( state, sizeof( state ) );
+    SymCryptWipeKnownSize( state2, sizeof( state2 ) );
+
+    return;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesEncryptC( pExpandedKey, pbSrc, pbDst );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbDecryptC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesDecryptC( pExpandedKey, pbSrc, pbDst );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+}
diff --git a/libs/symcrypt/lib/aes-default-bc.c b/libs/symcrypt/lib/aes-default-bc.c
new file mode 100644
index 000000000000..2f38fc4aabec
--- /dev/null
+++ b/libs/symcrypt/lib/aes-default-bc.c
@@ -0,0 +1,92 @@
+//
+// aes-default-bc.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The SymCrypt API allows callers to use the generic block cipher mode functions and pass
+// a pointer to a structure that describes the block cipher.
+// This structure contains pointers to all the optimized implementations of the various modes.
+// This pulls in all the mode-specific code, which in some cases we don't want.
+//
+// We isolate the SymCryptAesBlockCipher structure into this separate C file so that it only gets
+// pulled in when the application uses this structure.
+//
+
+//
+// The virtual table for the AES block cipher.
+//
+// All pointers must point to specialized functions. The general
+// block cipher mode functions will call these pointers if they are non-NULL
+// so if they point back to an implementation that calls the generic
+// mode functions we get an infinite recursion.
+//
+// NOTE: the compile-time conditions in this file should track the actual implementations in
+// aes-default.c.
+//
+
+const SYMCRYPT_BLOCKCIPHER SymCryptAesBlockCipher_Fast = {
+    &SymCryptAesExpandKey,
+    &SymCryptAesEncrypt,
+    &SymCryptAesDecrypt,
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+    &SymCryptAesEcbEncrypt,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+    &SymCryptAesEcbDecrypt,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+    &SymCryptAesCbcEncrypt,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+    &SymCryptAesCbcDecrypt,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+    &SymCryptAesCbcMac,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+    &SymCryptAesCtrMsb64,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+    &SymCryptAesGcmEncryptPart,
+#else
+    NULL,
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+    &SymCryptAesGcmDecryptPart,
+#else
+    NULL,
+#endif
+
+    SYMCRYPT_AES_BLOCK_SIZE,
+    sizeof( SYMCRYPT_AES_EXPANDED_KEY ),
+};
+
+//
+// This indirection makes it easier to switch implementations in a binary without
+// changing the calling code.
+//
+const PCSYMCRYPT_BLOCKCIPHER SymCryptAesBlockCipher = &SymCryptAesBlockCipher_Fast;
diff --git a/libs/symcrypt/lib/aes-default.c b/libs/symcrypt/lib/aes-default.c
new file mode 100644
index 000000000000..fce247d7f74c
--- /dev/null
+++ b/libs/symcrypt/lib/aes-default.c
@@ -0,0 +1,872 @@
+//
+// aes-default.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This is the interface for the default AES implementation.
+// On each platform, this is the fastest AES implementation irrespective of code size.
+// It uses assembler, XMM, or any other trick.
+//
+
+
+#include "precomp.h"
+
+//
+// Virtual table for generic functions
+// This allows us to default to generic implementations for some modes without pulling in all the
+// dedicated functions.
+// We use this when we cannot use the optimized implementations for some reason.
+//
+const SYMCRYPT_BLOCKCIPHER SymCryptAesBlockCipherNoOpt = {
+    &SymCryptAesExpandKey,
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM
+    &SymCryptAesEncryptAsm,
+    &SymCryptAesDecryptAsm,
+#else
+    &SymCryptAesEncryptC,
+    &SymCryptAesDecryptC,
+#endif
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+
+    SYMCRYPT_AES_BLOCK_SIZE,
+    sizeof( SYMCRYPT_AES_EXPANDED_KEY ),
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4Sbox( _In_reads_(4) PCBYTE pIn, _Out_writes_(4) PBYTE pOut, BOOL UseSimd )
+{
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+    if( UseSimd )
+    {
+        SymCryptAes4SboxXmm( pIn, pOut );
+    } else {
+        SymCryptAes4SboxC( pIn, pOut );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( UseSimd )
+    {
+        SymCryptAes4SboxNeon( pIn, pOut );
+    } else {
+        SymCryptAes4SboxC( pIn, pOut );
+    }
+#else
+    UNREFERENCED_PARAMETER( UseSimd );
+    SymCryptAes4SboxC( pIn, pOut );         // never use XMM on SaveXmm arch, save/restore overhead is too large.
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKey(
+    _In_reads_(16)      PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey,
+                        BOOL    UseSimd )
+{
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+    if( UseSimd )
+    {
+        SymCryptAesCreateDecryptionRoundKeyXmm( pEncryptionRoundKey, pDecryptionRoundKey );
+    } else {
+        SymCryptAesCreateDecryptionRoundKeyC( pEncryptionRoundKey, pDecryptionRoundKey );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( UseSimd )
+    {
+        SymCryptAesCreateDecryptionRoundKeyNeon( pEncryptionRoundKey, pDecryptionRoundKey );
+    } else {
+        SymCryptAesCreateDecryptionRoundKeyC( pEncryptionRoundKey, pDecryptionRoundKey );
+    }
+#else
+    UNREFERENCED_PARAMETER( UseSimd );
+    SymCryptAesCreateDecryptionRoundKeyC( pEncryptionRoundKey, pDecryptionRoundKey );   // never use XMM on SaveXmm arch, save/restore overhead is too large.
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncrypt(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(SYMCRYPT_AES_BLOCK_SIZE)     PCBYTE                      pbSrc,
+    _Out_writes_(SYMCRYPT_AES_BLOCK_SIZE)   PBYTE                       pbDst )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesEncryptXmm( pExpandedKey, pbSrc, pbDst );
+    } else {
+        SymCryptAesEncryptAsm( pExpandedKey, pbSrc, pbDst );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesEncryptXmm( pExpandedKey, pbSrc, pbDst );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptAesEncryptAsm( pExpandedKey, pbSrc, pbDst );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptAesEncryptAsm( pExpandedKey, pbSrc, pbDst );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesEncryptNeon( pExpandedKey, pbSrc, pbDst );
+    } else {
+        SymCryptAesEncryptC( pExpandedKey, pbSrc, pbDst );
+    }
+#else
+    SymCryptAesEncryptC( pExpandedKey, pbSrc, pbDst );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecrypt(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(SYMCRYPT_AES_BLOCK_SIZE)     PCBYTE                      pbSrc,
+    _Out_writes_(SYMCRYPT_AES_BLOCK_SIZE)   PBYTE                       pbDst )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesDecryptXmm( pExpandedKey, pbSrc, pbDst );
+    } else {
+        SymCryptAesDecryptAsm( pExpandedKey, pbSrc, pbDst );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesDecryptXmm( pExpandedKey, pbSrc, pbDst );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptAesDecryptAsm( pExpandedKey, pbSrc, pbDst );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptAesDecryptAsm( pExpandedKey, pbSrc, pbDst );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesDecryptNeon( pExpandedKey, pbSrc, pbDst );
+    } else {
+        SymCryptAesDecryptC( pExpandedKey, pbSrc, pbDst );
+    }
+#else
+    SymCryptAesDecryptC( pExpandedKey, pbSrc, pbDst );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesCbcEncryptXmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptAesCbcEncryptAsm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesCbcEncryptXmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptAesCbcEncryptAsm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptAesCbcEncryptAsm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesCbcEncryptNeon( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptCbcEncrypt( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+#else
+    SymCryptCbcEncrypt( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesCbcDecryptXmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptAesCbcDecryptAsm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesCbcDecryptXmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptAesCbcDecryptAsm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptAesCbcDecryptAsm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesCbcDecryptNeon( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptCbcDecrypt( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+#else
+    SymCryptCbcDecrypt( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesEcbEncryptXmm( pExpandedKey, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptAesEcbEncryptAsm( pExpandedKey, pbSrc, pbDst, cbData );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesEcbEncryptXmm( pExpandedKey, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptAesEcbEncryptAsm( pExpandedKey, pbSrc, pbDst, cbData );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptAesEcbEncryptAsm( pExpandedKey, pbSrc, pbDst, cbData );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesEcbEncryptNeon( pExpandedKey, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptAesEcbEncryptC( pExpandedKey, pbSrc, pbDst, cbData );
+    }
+#else
+    SymCryptAesEcbEncryptC( pExpandedKey, pbSrc, pbDst, cbData );
+#endif
+}
+
+//
+// NOTE: There is no reason that SymCryptAesEcbDecrypt could not have unrolled versions similar to
+// SymCryptAesEcbEncrypt if a real use case requiring large scale Ecb decryption is found.
+// For now just decrypt 1 block at a time to reduce code size.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbDecrypt(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesDecrypt( pExpandedKey, pbSrc, pbDst );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcMac(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesCbcMacXmm( pExpandedKey, pbChainingValue, pbData, cbData );
+    } else {
+        SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptCbcMac( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbData, cbData );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesCbcMacXmm( pExpandedKey, pbChainingValue, pbData, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptCbcMac( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbData, cbData );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesCbcMacNeon( pExpandedKey, pbChainingValue, pbData, cbData );
+    } else {
+        SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptCbcMac( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbData, cbData );
+    }
+#else
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE );
+    SymCryptCbcMac( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbData, cbData );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb32(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesCtrMsb32Xmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+        SymCryptCtrMsb32( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesCtrMsb32Xmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+        SymCryptCtrMsb32( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesCtrMsb32Neon( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptCtrMsb32( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+
+#else
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+    SymCryptCtrMsb32( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb64(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        SymCryptAesCtrMsb64Xmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptAesCtrMsb64Asm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesCtrMsb64Xmm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptAesCtrMsb64Asm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_ARM
+    SymCryptAesCtrMsb64Asm( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptAesCtrMsb64Neon( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptCtrMsb64( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+    }
+
+#else
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE );        // keep Prefast happy
+    SymCryptCtrMsb64( &SymCryptAesBlockCipherNoOpt, pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptPartOnePass(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    SIZE_T      bytesToProcess;
+#if SYMCRYPT_CPU_AMD64
+    SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+#endif
+
+    //
+    // We have entered the encrypt phase, the AAD has been padded to be a multiple of block size
+    // We know that the bytes still to use in the key stream buffer and the bytes left to fill the
+    // macBlock will be the same in the context of this function
+    //
+    SYMCRYPT_ASSERT( (pState->cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == pState->bytesInMacBlock );
+
+    //
+    // We update pState->cbData once before we modify cbData.
+    // pState->cbData is not used in the rest of this function
+    //
+    SYMCRYPT_ASSERT( pState->cbData + cbData <= SYMCRYPT_GCM_MAX_DATA_SIZE );
+    pState->cbData += cbData;
+
+    if( pState->bytesInMacBlock > 0 )
+    {
+        bytesToProcess = SYMCRYPT_MIN( cbData, SYMCRYPT_GCM_BLOCK_SIZE - pState->bytesInMacBlock );
+        SymCryptXorBytes(
+            pbSrc,
+            &pState->keystreamBlock[pState->bytesInMacBlock],
+            &pState->macBlock[pState->bytesInMacBlock],
+            bytesToProcess );
+        memcpy( pbDst, &pState->macBlock[pState->bytesInMacBlock], bytesToProcess );
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        cbData -= bytesToProcess;
+        pState->bytesInMacBlock += bytesToProcess;
+
+        if( pState->bytesInMacBlock == SYMCRYPT_GCM_BLOCK_SIZE )
+        {
+            SymCryptGHashAppendData(    &pState->pKey->ghashKey,
+                                        &pState->ghashState,
+                                        &pState->macBlock[0],
+                                        SYMCRYPT_GCM_BLOCK_SIZE );
+            pState->bytesInMacBlock = 0;
+        }
+
+        //
+        // If there are bytes left in the key stream buffer, then cbData == 0 and we're done.
+        // If we used up all the bytes, then we are fine, no need to compute the next key stream block
+        //
+    }
+
+    if( cbData >= SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        bytesToProcess = cbData & SYMCRYPT_GCM_BLOCK_ROUND_MASK;
+
+        //
+        // We use a Gcm function that increments the CTR by 64 bits, rather than the 32 bits that GCM requires.
+        // As we only support 12-byte nonces, the 32-bit counter never overflows, and we can safely use
+        // the 64-bit incrementing primitive.
+        // If we ever support other nonce sizes this is going to be a big problem.
+        // You can't fake a 32-bit counter using a 64-bit counter function without side-channels that expose
+        // information about the current counter value.
+        // With other nonce sizes the actual counter value itself is not public, so we can't expose that.
+        // We can do two things:
+        // - create SymCryptAesGcmEncryptXXX32
+        // - Accept that we leak information about the counter value; after all it is not treated as a
+        //   secret when the nonce is 12 bytes.
+        //
+        SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+
+#if SYMCRYPT_CPU_AMD64
+        if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_VAES_256_CODE ) &&
+            (bytesToProcess >= GCM_YMM_MINBLOCKS * SYMCRYPT_GCM_BLOCK_SIZE) &&
+            SymCryptSaveYmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+        {
+            SymCryptAesGcmEncryptStitchedYmm_2048(
+                &pState->pKey->blockcipherKey.aes,
+                &pState->counterBlock[0],
+                &pState->pKey->ghashKey.table[0],
+                &pState->ghashState,
+                pbSrc,
+                pbDst,
+                bytesToProcess );
+
+            SymCryptRestoreYmm( &SaveData );
+        } else {
+            SymCryptAesGcmEncryptStitchedXmm(
+                &pState->pKey->blockcipherKey.aes,
+                &pState->counterBlock[0],
+                &pState->pKey->ghashKey.table[0],
+                &pState->ghashState,
+                pbSrc,
+                pbDst,
+                bytesToProcess );
+        }
+
+#elif SYMCRYPT_CPU_X86
+        SymCryptAesGcmEncryptStitchedXmm(
+            &pState->pKey->blockcipherKey.aes,
+            &pState->counterBlock[0],
+            (PSYMCRYPT_GF128_ELEMENT)&pState->pKey->ghashKey.tableSpace[pState->pKey->ghashKey.tableOffset],
+            &pState->ghashState,
+            pbSrc,
+            pbDst,
+            bytesToProcess );
+
+#elif SYMCRYPT_CPU_ARM64
+        SymCryptAesGcmEncryptStitchedNeon(
+            &pState->pKey->blockcipherKey.aes,
+            &pState->counterBlock[0],
+            &pState->pKey->ghashKey.table[0],
+            &pState->ghashState,
+            pbSrc,
+            pbDst,
+            bytesToProcess );
+
+#else
+        SymCryptAesCtrMsb32(&pState->pKey->blockcipherKey.aes,
+                            &pState->counterBlock[0],
+                            pbSrc,
+                            pbDst,
+                            cbData );
+        //
+        // We break the read-once/write once rule here by reading the pbDst data back.
+        // In this particular situation this is safe, and avoiding it is expensive as it
+        // requires an extra copy and an extra memory buffer.
+        // The first write exposes the GCM key stream, independent of the underlying data that
+        // we are processing. From an attacking point of view we can think of this as literally
+        // handing over the key stream. So encryption consists of two steps:
+        // - hand over the key stream
+        // - MAC some ciphertext
+        // In this view (which has equivalent security properties to GCM) is obviously doesn't
+        // matter that we read pbDst back.
+        //
+        SymCryptGHashAppendData(&pState->pKey->ghashKey,
+                                &pState->ghashState,
+                                pbDst,
+                                cbData );
+
+#endif
+
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        cbData -= bytesToProcess;
+    }
+
+    if( cbData > 0 )
+    {
+        SymCryptWipeKnownSize( &pState->keystreamBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+
+        SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+        SymCryptAesCtrMsb32(&pState->pKey->blockcipherKey.aes,
+                            &pState->counterBlock[0],
+                            &pState->keystreamBlock[0],
+                            &pState->keystreamBlock[0],
+                            SYMCRYPT_GCM_BLOCK_SIZE );
+
+        SymCryptXorBytes( &pState->keystreamBlock[0], pbSrc, &pState->macBlock[0], cbData );
+        memcpy( pbDst, &pState->macBlock[0], cbData );
+        pState->bytesInMacBlock = cbData;
+
+        //
+        // pState->cbData contains the data length after this call already, so it knows how many
+        // bytes are left in the keystream block
+        //
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptPartOnePass(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    SIZE_T      bytesToProcess;
+#if SYMCRYPT_CPU_AMD64
+    SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+#endif
+
+    //
+    // We have entered the decrypt phase, the AAD has been padded to be a multiple of block size
+    // We know that the bytes still to use in the key stream buffer and the bytes left to fill the
+    // macBlock will be the same in the context of this function
+    //
+    SYMCRYPT_ASSERT( (pState->cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == pState->bytesInMacBlock );
+
+    //
+    // We update pState->cbData once before we modify cbData.
+    // pState->cbData is not used in the rest of this function
+    //
+    SYMCRYPT_ASSERT( pState->cbData + cbData <= SYMCRYPT_GCM_MAX_DATA_SIZE );
+    pState->cbData += cbData;
+
+    if( pState->bytesInMacBlock > 0 )
+    {
+        bytesToProcess = SYMCRYPT_MIN( cbData, SYMCRYPT_GCM_BLOCK_SIZE - pState->bytesInMacBlock );
+        memcpy( &pState->macBlock[pState->bytesInMacBlock], pbSrc, bytesToProcess );
+        SymCryptXorBytes(
+            &pState->keystreamBlock[pState->bytesInMacBlock],
+            &pState->macBlock[pState->bytesInMacBlock],
+            pbDst,
+            bytesToProcess );
+
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        cbData -= bytesToProcess;
+        pState->bytesInMacBlock += bytesToProcess;
+
+        if( pState->bytesInMacBlock == SYMCRYPT_GCM_BLOCK_SIZE )
+        {
+            SymCryptGHashAppendData(    &pState->pKey->ghashKey,
+                                        &pState->ghashState,
+                                        &pState->macBlock[0],
+                                        SYMCRYPT_GCM_BLOCK_SIZE );
+            pState->bytesInMacBlock = 0;
+        }
+
+        //
+        // If there are bytes left in the key stream buffer, then cbData == 0 and we're done.
+        // If we used up all the bytes, then we are fine, no need to compute the next key stream block
+        //
+    }
+
+    if( cbData >= SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        bytesToProcess = cbData & SYMCRYPT_GCM_BLOCK_ROUND_MASK;
+
+        //
+        // We use a Gcm function that increments the CTR by 64 bits, rather than the 32 bits that GCM requires.
+        // As we only support 12-byte nonces, the 32-bit counter never overflows, and we can safely use
+        // the 64-bit incrementing primitive.
+        // If we ever support other nonce sizes this is going to be a big problem.
+        // You can't fake a 32-bit counter using a 64-bit counter function without side-channels that expose
+        // information about the current counter value.
+        // With other nonce sizes the actual counter value itself is not public, so we can't expose that.
+        // We can do two things:
+        // - create SymCryptAesGcmDecryptXXX32
+        // - Accept that we leak information about the counter value; after all it is not treated as a
+        //   secret when the nonce is 12 bytes.
+        //
+        SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+
+#if SYMCRYPT_CPU_AMD64
+        if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_VAES_256_CODE ) &&
+            (bytesToProcess >= GCM_YMM_MINBLOCKS * SYMCRYPT_GCM_BLOCK_SIZE) &&
+            SymCryptSaveYmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+        {
+            SymCryptAesGcmDecryptStitchedYmm_2048(
+                &pState->pKey->blockcipherKey.aes,
+                &pState->counterBlock[0],
+                &pState->pKey->ghashKey.table[0],
+                &pState->ghashState,
+                pbSrc,
+                pbDst,
+                bytesToProcess );
+
+            SymCryptRestoreYmm( &SaveData );
+        } else {
+            SymCryptAesGcmDecryptStitchedXmm(
+                &pState->pKey->blockcipherKey.aes,
+                &pState->counterBlock[0],
+                &pState->pKey->ghashKey.table[0],
+                &pState->ghashState,
+                pbSrc,
+                pbDst,
+                bytesToProcess );
+        }
+
+#elif SYMCRYPT_CPU_X86
+        SymCryptAesGcmDecryptStitchedXmm(
+            &pState->pKey->blockcipherKey.aes,
+            &pState->counterBlock[0],
+            (PSYMCRYPT_GF128_ELEMENT)&pState->pKey->ghashKey.tableSpace[pState->pKey->ghashKey.tableOffset],
+            &pState->ghashState,
+            pbSrc,
+            pbDst,
+            bytesToProcess );
+
+#elif SYMCRYPT_CPU_ARM64
+        SymCryptAesGcmDecryptStitchedNeon(
+            &pState->pKey->blockcipherKey.aes,
+            &pState->counterBlock[0],
+            &pState->pKey->ghashKey.table[0],
+            &pState->ghashState,
+            pbSrc,
+            pbDst,
+            bytesToProcess );
+
+#else
+        SymCryptGHashAppendData(&pState->pKey->ghashKey,
+                                &pState->ghashState,
+                                pbSrc,
+                                cbData );
+        //
+        // Do the actual decryption
+        // This violates the read-once rule, but it is safe for the same reasons as above
+        // in the encryption case.
+        //
+        SymCryptAesCtrMsb32(&pState->pKey->blockcipherKey.aes,
+                            &pState->counterBlock[0],
+                            pbSrc,
+                            pbDst,
+                            cbData );
+
+#endif
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        cbData -= bytesToProcess;
+    }
+
+    if( cbData > 0 )
+    {
+        SymCryptWipeKnownSize( &pState->keystreamBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+
+        SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+        SymCryptAesCtrMsb32(&pState->pKey->blockcipherKey.aes,
+                            &pState->counterBlock[0],
+                            &pState->keystreamBlock[0],
+                            &pState->keystreamBlock[0],
+                            SYMCRYPT_GCM_BLOCK_SIZE );
+
+        memcpy( &pState->macBlock[0], pbSrc, cbData );
+        SymCryptXorBytes(
+            &pState->keystreamBlock[0],
+            &pState->macBlock[0],
+            pbDst,
+            cbData );
+
+        pState->bytesInMacBlock = cbData;
+
+        //
+        // pState->cbData contains the data length after this call already, so it knows how many
+        // bytes are left in the keystream block
+        //
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_PCLMULQDQ_CODE ) )
+    {
+        SymCryptAesGcmEncryptPartOnePass( pState, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptGcmEncryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_PCLMULQDQ_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesGcmEncryptPartOnePass( pState, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptGcmEncryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES | SYMCRYPT_CPU_FEATURE_NEON_PMULL ) )
+    {
+        SymCryptAesGcmEncryptPartOnePass( pState, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptGcmEncryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+#else
+    SymCryptGcmEncryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_PCLMULQDQ_CODE ) )
+    {
+        SymCryptAesGcmDecryptPartOnePass( pState, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptGcmDecryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_PCLMULQDQ_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptAesGcmDecryptPartOnePass( pState, pbSrc, pbDst, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptGcmDecryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES | SYMCRYPT_CPU_FEATURE_NEON_PMULL ) )
+    {
+        SymCryptAesGcmDecryptPartOnePass( pState, pbSrc, pbDst, cbData );
+    } else {
+        SymCryptGcmDecryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+#else
+    SymCryptGcmDecryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+#endif
+}
diff --git a/libs/symcrypt/lib/aes-key.c b/libs/symcrypt/lib/aes-key.c
new file mode 100644
index 000000000000..e584403914a8
--- /dev/null
+++ b/libs/symcrypt/lib/aes-key.c
@@ -0,0 +1,437 @@
+//
+// aes.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// The actual encryption and decryption routines here are not nearly as fast as the
+// assembler ones. They are used on platforms that don't have assembler implementations
+// and for various testing purposes.
+//
+// This code derives from the orignal fast AES code that Niels Ferguson wrote
+// for BitLocker in Windows Vista.
+// The C code is derived from the AES that was already in the RSA32 library,
+// the assembler code was created new at that time.
+//
+
+
+#include "precomp.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Key expansion uses two functions, a 4-byte S-box lookup and one
+// to create a decryption round key from an encryption round key.
+// These are the C implementations of these functions
+//
+
+
+static BYTE g_SymCryptAesRoundConstant[11] =
+{
+    0, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
+};
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesExpandKeyInternal(
+    _Out_               PSYMCRYPT_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey,
+                        BOOLEAN                     fCreateDecryptionKeys )
+{
+    UINT32  nRounds;
+    BYTE *  p;
+    BYTE *  q;
+    UINT32  i;
+    UINT32  t;
+
+    BOOL            UseSimd = FALSE;
+    SYMCRYPT_ERROR  status = SYMCRYPT_NO_ERROR;
+
+#if SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        if( SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+        {
+            UseSimd = TRUE;
+        }
+    }
+#elif SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) )
+    {
+        UseSimd = TRUE;
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        UseSimd = TRUE;
+    }
+#endif
+
+    SYMCRYPT_SET_MAGIC( pExpandedKey );
+
+    //
+    // Separate code for each key size, this is significantly faster.
+    // We have a number of applications that do frequent key expansions.
+    //
+    switch( cbKey )
+    {
+    case 16:
+        nRounds = 10;
+        pExpandedKey->lastEncRoundKey = &pExpandedKey->RoundKey[nRounds];
+        pExpandedKey->lastDecRoundKey = &pExpandedKey->RoundKey[2*nRounds];
+
+        memcpy( &pExpandedKey->RoundKey[0], pbKey, 16 );
+
+        p = (BYTE *)&pExpandedKey->RoundKey[1];
+
+        for( i=1; i<=nRounds; i++ )
+        {
+            SymCryptAes4Sbox( &p[-4], p, UseSimd );
+            t = ROR32(SYMCRYPT_LOAD_LSBFIRST32(p), 8) ^ SYMCRYPT_LOAD_LSBFIRST32(p - 16) ^ g_SymCryptAesRoundConstant[i];
+            SYMCRYPT_STORE_LSBFIRST32( p, t );     // this is a macro that re-evaluates its arguments
+
+            *(UINT32 *)(p+4)  = *(UINT32 *) p    ^ *(UINT32 *)(p - 12);
+            *(UINT32 *)(p+8)  = *(UINT32 *)(p+4) ^ *(UINT32 *)(p -  8);
+            *(UINT32 *)(p+12) = *(UINT32 *)(p+8) ^ *(UINT32 *)(p -  4);
+
+            p += 16;
+        }
+
+        break;
+
+    case 24:
+        nRounds = 12;
+        pExpandedKey->lastEncRoundKey = &pExpandedKey->RoundKey[nRounds];
+        pExpandedKey->lastDecRoundKey = &pExpandedKey->RoundKey[2*nRounds];
+
+        memcpy( &pExpandedKey->RoundKey[0], pbKey, 24 );
+
+        p = (BYTE *)&pExpandedKey->RoundKey[0] + 24;
+
+        //
+        // We have 12 rounds, 13 round keys, and 13*16 = 208 bytes of encryption key to generate.
+        // We have 24 already, so we need 184 more.
+        // Each iteration produces 24 bytes, so we need to loop 8 times.
+        //
+        for( i=1; i<=8; i++ )
+        {
+            SymCryptAes4Sbox( &p[-4], p, UseSimd );
+            t = ROR32(SYMCRYPT_LOAD_LSBFIRST32(p), 8) ^ SYMCRYPT_LOAD_LSBFIRST32(p - 24) ^ g_SymCryptAesRoundConstant[i];
+            SYMCRYPT_STORE_LSBFIRST32( p, t );
+
+            *(UINT32 *)(p+4)  = *(UINT32 *) p     ^ *(UINT32 *)(p - 20);
+            *(UINT32 *)(p+8)  = *(UINT32 *)(p+ 4) ^ *(UINT32 *)(p - 16);
+            *(UINT32 *)(p+12) = *(UINT32 *)(p+ 8) ^ *(UINT32 *)(p - 12);
+            *(UINT32 *)(p+16) = *(UINT32 *)(p+12) ^ *(UINT32 *)(p -  8);
+            *(UINT32 *)(p+20) = *(UINT32 *)(p+16) ^ *(UINT32 *)(p -  4);
+
+            p += 24;
+        }
+
+        break;
+
+    case 32:
+        nRounds = 14;
+        pExpandedKey->lastEncRoundKey = &pExpandedKey->RoundKey[nRounds];
+        pExpandedKey->lastDecRoundKey = &pExpandedKey->RoundKey[2*nRounds];
+
+        memcpy( &pExpandedKey->RoundKey[0], pbKey, 32 );
+
+        p = (BYTE *)&pExpandedKey->RoundKey[0] + 32;
+
+        //
+        // We have 14 rounds, 15 round keys, and 15*16 = 240 bytes of encryption key to generate.
+        // We have 32 already, so we need 208 more.
+        // Each iteration produces 32 bytes, so we need to loop 6.5 times.
+        //
+        for( i=1; i<=6; i++ )
+        {
+            SymCryptAes4Sbox( &p[-4], p, UseSimd );
+            t = ROR32(SYMCRYPT_LOAD_LSBFIRST32(p), 8) ^ SYMCRYPT_LOAD_LSBFIRST32(p - 32) ^ g_SymCryptAesRoundConstant[i];
+            SYMCRYPT_STORE_LSBFIRST32( p, t );
+
+            *(UINT32 *)(p+4)  = *(UINT32 *) p       ^ *(UINT32 *)(p - 28);
+            *(UINT32 *)(p+8)  = *(UINT32 *)(p +  4) ^ *(UINT32 *)(p - 24);
+            *(UINT32 *)(p+12) = *(UINT32 *)(p +  8) ^ *(UINT32 *)(p - 20);
+
+            SymCryptAes4Sbox( &p[12], &p[16], UseSimd );
+            *(UINT32 *)(p+16) = *(UINT32 *)(p + 16) ^ *(UINT32 *)(p - 16);
+
+            *(UINT32 *)(p+20) = *(UINT32 *)(p + 16) ^ *(UINT32 *)(p - 12);
+            *(UINT32 *)(p+24) = *(UINT32 *)(p + 20) ^ *(UINT32 *)(p -  8);
+            *(UINT32 *)(p+28) = *(UINT32 *)(p + 24) ^ *(UINT32 *)(p -  4);
+
+            p += 32;
+        }
+
+        // We looped 6 times, so here is the half-loop
+
+        SymCryptAes4Sbox( &p[-4], p, UseSimd );
+        t = ROR32(SYMCRYPT_LOAD_LSBFIRST32(p), 8) ^ SYMCRYPT_LOAD_LSBFIRST32(p - 32) ^ g_SymCryptAesRoundConstant[i];
+        SYMCRYPT_STORE_LSBFIRST32( p, t );
+
+        *(UINT32 *)(p+4)  = *(UINT32 *) p       ^ *(UINT32 *)(p - 28);
+        *(UINT32 *)(p+8)  = *(UINT32 *)(p +  4) ^ *(UINT32 *)(p - 24);
+        *(UINT32 *)(p+12) = *(UINT32 *)(p +  8) ^ *(UINT32 *)(p - 20);
+
+        break;
+
+    default:
+        status = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+
+    if( fCreateDecryptionKeys )
+    {
+        p = &pExpandedKey->RoundKey[0][0][0];
+        q = (PBYTE)(pExpandedKey->lastDecRoundKey);
+
+        // The first encryption round key is the last decryption round key
+        memcpy( q, p, SYMCRYPT_AES_BLOCK_SIZE );
+        p += 16;
+        q -= 16;
+
+        while( p < (PBYTE) pExpandedKey->lastEncRoundKey )
+        {
+            SymCryptAesCreateDecryptionRoundKey( p, q, UseSimd );
+            q -= 16;
+            p += 16;
+        }
+    }
+
+cleanup:
+
+#if SYMCRYPT_CPU_X86
+    if( UseSimd )
+    {
+        SymCryptRestoreXmm( &SaveData );
+    }
+#endif
+
+    return status;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesExpandKey(
+    _Out_               PSYMCRYPT_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey )
+
+{
+    return SymCryptAesExpandKeyInternal( pExpandedKey, pbKey, cbKey, TRUE );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesExpandKeyEncryptOnly(
+    _Out_               PSYMCRYPT_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey )
+{
+    return SymCryptAesExpandKeyInternal( pExpandedKey, pbKey, cbKey, FALSE );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesKeyCopy( _In_    PCSYMCRYPT_AES_EXPANDED_KEY pSrc,
+                    _Out_   PSYMCRYPT_AES_EXPANDED_KEY  pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+
+    *pDst = *pSrc;
+    pDst->lastEncRoundKey = &pDst->RoundKey[0] + (pSrc->lastEncRoundKey - &pSrc->RoundKey[0]);
+    pDst->lastDecRoundKey = &pDst->RoundKey[0] + (pSrc->lastDecRoundKey - &pSrc->RoundKey[0]);
+
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+//
+// Self test code
+//
+
+
+const BYTE SymCryptAesNistTestVector128Ciphertext[16] = {
+    0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
+    0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a,
+};
+
+
+
+/****************************************************************
+ * OLD CODE
+ *
+ * Old code to generate the AES tables dynamically.
+ * Kept for future reference.
+ *
+
+
+//
+// Prototype; on some platforms this function is in assembler.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateRotatedTables( BYTE MatrixMult[4][256][4] );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateRotatedTables( _Inout_ BYTE MatrixMult[4][256][4] )
+{
+    int i,j,k;
+
+    //
+    // We do this byte-by-byte, which is easiest.
+    // It would be faster to use UINT32 operations,
+    // but that is endian-specific, and therefore platform-specific.
+    // Endian-agnostic UINT32-based code would be a lot more complicated.
+    // All this is extremely easy to do in assembler, which we do on those
+    // platforms that have assembler implementations.
+    //
+    for( j=1; j<4; j++ ) {
+        for( i=0; i<256; i++ ) {
+            for( k=0; k<4; k++ ) {
+                MatrixMult[j][i][k] = MatrixMult[0][i][(k-j)&3];
+            }
+        }
+    }
+}
+
+
+
+//
+// SymCryptAesInitMatrixMultiplyTable
+//
+// Initialize a matrix multiplication table.
+// Each matrix multiplication table consists of 4 tables of 256 entries of 4 bytes each.
+// The four tables are rotated copies of each other.
+// This function generates the first of those four tables from the init
+// value.
+//
+// After this call:
+//    At index i the table contains the four bytes
+//        i * init[0], i * init[1], i * init[2], i * init[3]
+//    where multiplication is in GF(2^8).
+//
+// We do not do a GF(2^8) multiplication for each entry, but rather use the
+// relationship (a xor b) * init[.] = a * init[.] xor b * init[.]
+// And only compute i*init[.] for i = 1,2,4,8,...,128. This can be done
+// using repeated multiplication by x in the finite field.
+//
+// It is safe to call this function on two separate threads for the same table.
+// All invocations will write the same data to the table, and within a tread each entry is written
+// before it is read. Doing parallel initializations of the same table can be very inefficient
+// as multiple cores will be fighting over the cache lines, but the result will be correct.
+// We use this property to initialize the tables lazily.
+//
+static
+VOID
+SYMCRYPT_CALL
+SymCryptAesInitMatrixMultiplyTable( _Out_   SYMCRYPT_ALIGN BYTE MatrixMult[256][4],
+                                    _In_    SYMCRYPT_ALIGN BYTE init[4]
+                                    )
+{
+    int i,j;
+    SYMCRYPT_ALIGN BYTE initCopy[4];
+    UINT32 initCopyAsUint32;
+
+    //
+    // We copy the init value so that we can modify it without worrying about multi-threading
+    // issues.
+    //
+    *(UINT32 *)initCopy = *(UINT32 *)init;
+
+    *(UINT32 *)MatrixMult[0] = 0;
+    for( i=1; i<256; i<<=1 )
+    {
+        initCopyAsUint32 = *(UINT32 *)initCopy;
+        for( j=0; j<i; j++ )
+        {
+            *(UINT32 *)MatrixMult[i+j] = *(UINT32 *)MatrixMult[j] ^ initCopyAsUint32;
+        }
+        for( j=0; j<4; j++ )
+        {
+            initCopy[j] = MULT_BY_X( initCopy[j] );
+        }
+    }
+}
+
+
+//
+// SymCryptAesInitialize
+//
+// Initialize the static tables for the AES implementation.
+// This function is called by the key expansion function if it finds the
+// tables not initialized.
+//
+// This leads to an interesting case where multiple threads running on multiple
+// CPUs run this initialization code at the same time.
+// This code is carefully structured to allow that. When global data is written it is
+// always with the final value, and we never read uninitialized global data.
+// Thus, even if two CPUs run this code at the same time, they will both initialize each
+// memory location to the same correct value and the end result will be correct.
+// (Performance will suffer due to the fact that cache lines will be bounced back and force
+// between the two CPUs, but that is not a significant concern as this code is used only once.)
+//
+// At the end of the initialization the flag is set to indicate that further
+// key expansion invocations do not need to re-run the initialization.
+// We use memory barriers to keep this multi-thread safe.
+//
+static
+VOID
+SYMCRYPT_CALL
+SymCryptAesInitialize(void)
+{
+    int i,j;
+    BYTE S;
+    BYTE Stimes2;
+
+    //
+    // We force alignment of these arrays as we sometimes treat them as a UINT32
+    //
+    SYMCRYPT_ALIGN BYTE InvMatrixEntry[4] = {0xe, 0x9, 0xd, 0xb};
+    SYMCRYPT_ALIGN BYTE MatrixEntry[4] = {2, 1, 1, 3};
+    SYMCRYPT_ALIGN BYTE MatrixScratch[256][4];
+
+    // Generate the forward MDS multiplication table in the scratch space
+    SymCryptAesInitMatrixMultiplyTable( MatrixScratch, MatrixEntry );
+
+    // Initialize first table of SymCryptAesInvMatrixMult
+    SymCryptAesInitMatrixMultiplyTable( SymCryptAesInvMatrixMult[0], InvMatrixEntry );
+
+    //
+    // Build the InvSbox table and the first table of SymCryptAesSboxMatrixMult and
+    // SymCryptAesInvSboxMatrixMult
+    //
+    for( i=0; i<256; i++ ) {
+        S = SymCryptAesSbox[i];
+        SymCryptAesInvSbox[S] = (BYTE) i;
+        *(UINT32 *)SymCryptAesSboxMatrixMult[0][i] = *(UINT32 *)MatrixScratch[S];
+        *(UINT32 *)SymCryptAesInvSboxMatrixMult[0][S] = *(UINT32 *)SymCryptAesInvMatrixMult[0][i];
+    }
+
+    //
+    // Now we generate the byte rotations of the tables
+    //
+    SymCryptAesCreateRotatedTables( SymCryptAesSboxMatrixMult );
+    SymCryptAesCreateRotatedTables( SymCryptAesInvSboxMatrixMult );
+    SymCryptAesCreateRotatedTables( SymCryptAesInvMatrixMult );
+
+    //
+    // This is a memory barrier. It ensures that all the memory writes we do before the barrier
+    // are globally visible to other CPUs before the memory writes we do after the fence.
+    // In this particular case, it ensures that every CPU sees the completed tables before
+    // it sees the flag as set.
+    //
+    MemoryBarrier();
+
+    //
+    // Set the flag to signal that the tables are initialized.
+    //
+    SymCryptAesTablesInitialized = TRUE;
+}
+
+
+*/
diff --git a/libs/symcrypt/lib/aes-neon.c b/libs/symcrypt/lib/aes-neon.c
new file mode 100644
index 000000000000..3c0d3fb1817f
--- /dev/null
+++ b/libs/symcrypt/lib/aes-neon.c
@@ -0,0 +1,1889 @@
+//
+// aes-neon.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// All NEON-based code for AES operations
+//
+
+#include "precomp.h"
+
+#if SYMCRYPT_CPU_ARM64
+
+#pragma clang attribute push (__attribute__((target("aes"))), apply_to=function)
+
+#define vzeroq()    vdupq_n_u64(0)
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4SboxNeon( _In_reads_(4) PCBYTE pIn, _Out_writes_(4) PBYTE pOut )
+{
+    /*
+    __m128i x;
+
+    x = _mm_set1_epi32( *(int *) pIn );
+
+    x = _mm_aeskeygenassist_si128( x, 0 );
+
+    *(unsigned *) pOut = x.m128i_u32[0];
+    */
+    __n128 x;
+
+    //
+    // There is no pure S-box lookup instruction, but the AESE instruction
+    // does a ShiftRow followed by a SubBytes.
+    // If we duplicate the input value to all 4 lanes, then the ShiftRow does nothing
+    // and the SubBytes will do the S-box lookup.
+    //
+    x = vdupq_n_u32( *(unsigned int *) pIn );
+    x = vaeseq_u8( x, vzeroq() );
+    vst1q_lane_s32( pOut, x, 0 );
+    //*(unsigned int *) pOut = x.n128_u32[0];
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKeyNeon(
+    _In_reads_(16)      PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey )
+{
+    *(__n128 *) pDecryptionRoundKey = vaesimcq_u8( *(__n128 *)pEncryptionRoundKey );
+}
+
+//
+// When doing a full round of AES encryption, make sure to give compiler opportunity to schedule dependent
+// aese/aesmc pairs to enable instruction fusion in many arm64 CPUs
+//
+#define AESE_AESMC( c, rk ) \
+{ \
+    c = vaeseq_u8( c, rk ); \
+    c = vaesmcq_u8( c ); \
+};
+
+//
+// When doing a full round of AES decryption, make sure to give compiler opportunity to schedule dependent
+// aesd/aesimc pairs to enable instruction fusion in many arm64 CPUs
+//
+#define AESD_AESIMC( c, rk ) \
+{ \
+    c = vaesdq_u8( c, rk ); \
+    c = vaesimcq_u8( c ); \
+};
+
+//
+// Using a loop with AESE_AESMC and AESD_AESIMC, the compiler can still prematurely rearrange the loop and
+// lose opportunity for scheduling adjacent pairs.
+// Instead, explicitly unroll the AES rounds with this macro.
+// Takes the name of first_round, full_round, and final_round macros, and uses them to construct block to
+// handle AES (128|192|256) for either encrypt or decrypt. For now assume only need at most 8 state
+// variables in the macros.
+// Assumes roundKey, keyPtr, and keyLimit are defined in calling context.
+//
+#define UNROLL_AES_ROUNDS_FIRST( first_round, full_round, final_round, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    /* Do 9 full rounds (AES-128|AES-192|AES-256) */ \
+    roundKey = *keyPtr++; \
+    first_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+    full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    roundKey = *keyPtr++; \
+\
+    if ( keyPtr < keyLimit ) \
+    { \
+        /* Do 2 more full rounds (AES-192|AES-256) */ \
+        full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+        full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+\
+        if ( keyPtr < keyLimit ) \
+        { \
+            /* Do 2 more full rounds (AES-256) */ \
+            full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+            roundKey = *keyPtr++; \
+            full_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+            roundKey = *keyPtr++; \
+        } \
+    } \
+\
+    /* Do final round (AES-128|AES-192|AES-256) */ \
+    final_round( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+};
+
+// Only AES_ENCRYPT_1_CHAIN needs to specify the first round differently from the full round
+#define UNROLL_AES_ROUNDS( full_round, final_round, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+    UNROLL_AES_ROUNDS_FIRST( full_round, full_round, final_round, c0, c1, c2, c3, c4, c5, c6, c7 )
+
+#define AES_ENCRYPT_ROUND_1( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESE_AESMC( c0, roundKey ) \
+};
+#define AES_ENCRYPT_FINAL_1( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaeseq_u8( c0, roundKey ); \
+    roundKey = *keyPtr; \
+    c0 = veorq_u8( c0, roundKey ); \
+};
+
+#define AES_ENCRYPT_1( pExpandedKey, c0 ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)&pExpandedKey->RoundKey[0]; \
+    keyLimit = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+\
+    UNROLL_AES_ROUNDS( \
+        AES_ENCRYPT_ROUND_1, \
+        AES_ENCRYPT_FINAL_1, \
+        c0, c1, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+// Perform AES encryption without the last round key and with a specified first round key
+//
+// For algorithms where performance is dominated by a chain of dependent AES rounds (i.e. CBC encryption, CCM, CMAC)
+// we can gain a reasonable performance uplift by computing (last round key ^ this plaintext block ^ first round key)
+// off the critical path and using this computed value in place of first round key in the first AESE instruction.
+#define AES_ENCRYPT_CHAIN_FIRST_1( c0, mergedFirstRoundKey, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESE_AESMC( c0, mergedFirstRoundKey ) \
+};
+#define AES_ENCRYPT_CHAIN_FINAL_1( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaeseq_u8( c0, roundKey ); \
+};
+
+#define AES_ENCRYPT_1_CHAIN( pExpandedKey, c0, mergedFirstRoundKey ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)&pExpandedKey->RoundKey[0]; \
+    keyLimit = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+\
+    UNROLL_AES_ROUNDS_FIRST( \
+        AES_ENCRYPT_CHAIN_FIRST_1, \
+        AES_ENCRYPT_ROUND_1, \
+        AES_ENCRYPT_CHAIN_FINAL_1, \
+        c0, mergedFirstRoundKey, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+#define AES_ENCRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESE_AESMC( c0, roundKey ) \
+    AESE_AESMC( c1, roundKey ) \
+    AESE_AESMC( c2, roundKey ) \
+    AESE_AESMC( c3, roundKey ) \
+};
+#define AES_ENCRYPT_FINAL_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaeseq_u8( c0, roundKey ); \
+    c1 = vaeseq_u8( c1, roundKey ); \
+    c2 = vaeseq_u8( c2, roundKey ); \
+    c3 = vaeseq_u8( c3, roundKey ); \
+    roundKey = *keyPtr; \
+    c0 = veorq_u8( c0, roundKey ); \
+    c1 = veorq_u8( c1, roundKey ); \
+    c2 = veorq_u8( c2, roundKey ); \
+    c3 = veorq_u8( c3, roundKey ); \
+};
+
+#define AES_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3 ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)&pExpandedKey->RoundKey[0]; \
+    keyLimit = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+\
+    UNROLL_AES_ROUNDS( \
+        AES_ENCRYPT_ROUND_4, \
+        AES_ENCRYPT_FINAL_4, \
+        c0, c1, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+#define AES_ENCRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESE_AESMC( c0, roundKey ) \
+    AESE_AESMC( c1, roundKey ) \
+    AESE_AESMC( c2, roundKey ) \
+    AESE_AESMC( c3, roundKey ) \
+    AESE_AESMC( c4, roundKey ) \
+    AESE_AESMC( c5, roundKey ) \
+    AESE_AESMC( c6, roundKey ) \
+    AESE_AESMC( c7, roundKey ) \
+};
+#define AES_ENCRYPT_FINAL_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaeseq_u8( c0, roundKey ); \
+    c1 = vaeseq_u8( c1, roundKey ); \
+    c2 = vaeseq_u8( c2, roundKey ); \
+    c3 = vaeseq_u8( c3, roundKey ); \
+    c4 = vaeseq_u8( c4, roundKey ); \
+    c5 = vaeseq_u8( c5, roundKey ); \
+    c6 = vaeseq_u8( c6, roundKey ); \
+    c7 = vaeseq_u8( c7, roundKey ); \
+    roundKey = *keyPtr; \
+    c0 = veorq_u8( c0, roundKey ); \
+    c1 = veorq_u8( c1, roundKey ); \
+    c2 = veorq_u8( c2, roundKey ); \
+    c3 = veorq_u8( c3, roundKey ); \
+    c4 = veorq_u8( c4, roundKey ); \
+    c5 = veorq_u8( c5, roundKey ); \
+    c6 = veorq_u8( c6, roundKey ); \
+    c7 = veorq_u8( c7, roundKey ); \
+};
+
+#define AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)&pExpandedKey->RoundKey[0]; \
+    keyLimit = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+\
+    UNROLL_AES_ROUNDS( \
+        AES_ENCRYPT_ROUND_8, \
+        AES_ENCRYPT_FINAL_8, \
+        c0, c1, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+#define AES_DECRYPT_ROUND_1( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESD_AESIMC( c0, roundKey ) \
+};
+#define AES_DECRYPT_FINAL_1( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaesdq_u8( c0, roundKey ); \
+    roundKey = *keyPtr; \
+    c0 = veorq_u8( c0, roundKey ); \
+};
+
+#define AES_DECRYPT_1( pExpandedKey, c0 ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+    keyLimit = (const __n128 *)pExpandedKey->lastDecRoundKey; \
+\
+    UNROLL_AES_ROUNDS( \
+        AES_DECRYPT_ROUND_1, \
+        AES_DECRYPT_FINAL_1, \
+        c0, c1, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+#define AES_DECRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESD_AESIMC( c0, roundKey ) \
+    AESD_AESIMC( c1, roundKey ) \
+    AESD_AESIMC( c2, roundKey ) \
+    AESD_AESIMC( c3, roundKey ) \
+};
+#define AES_DECRYPT_FINAL_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaesdq_u8( c0, roundKey ); \
+    c1 = vaesdq_u8( c1, roundKey ); \
+    c2 = vaesdq_u8( c2, roundKey ); \
+    c3 = vaesdq_u8( c3, roundKey ); \
+    roundKey = *keyPtr; \
+    c0 = veorq_u8( c0, roundKey ); \
+    c1 = veorq_u8( c1, roundKey ); \
+    c2 = veorq_u8( c2, roundKey ); \
+    c3 = veorq_u8( c3, roundKey ); \
+};
+
+#define AES_DECRYPT_4( pExpandedKey, c0, c1, c2, c3 ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+    keyLimit = (const __n128 *)pExpandedKey->lastDecRoundKey; \
+\
+    UNROLL_AES_ROUNDS( \
+        AES_DECRYPT_ROUND_4, \
+        AES_DECRYPT_FINAL_4, \
+        c0, c1, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+#define AES_DECRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    AESD_AESIMC( c0, roundKey ) \
+    AESD_AESIMC( c1, roundKey ) \
+    AESD_AESIMC( c2, roundKey ) \
+    AESD_AESIMC( c3, roundKey ) \
+    AESD_AESIMC( c4, roundKey ) \
+    AESD_AESIMC( c5, roundKey ) \
+    AESD_AESIMC( c6, roundKey ) \
+    AESD_AESIMC( c7, roundKey ) \
+};
+#define AES_DECRYPT_FINAL_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    c0 = vaesdq_u8( c0, roundKey ); \
+    c1 = vaesdq_u8( c1, roundKey ); \
+    c2 = vaesdq_u8( c2, roundKey ); \
+    c3 = vaesdq_u8( c3, roundKey ); \
+    c4 = vaesdq_u8( c4, roundKey ); \
+    c5 = vaesdq_u8( c5, roundKey ); \
+    c6 = vaesdq_u8( c6, roundKey ); \
+    c7 = vaesdq_u8( c7, roundKey ); \
+    roundKey = *keyPtr; \
+    c0 = veorq_u8( c0, roundKey ); \
+    c1 = veorq_u8( c1, roundKey ); \
+    c2 = veorq_u8( c2, roundKey ); \
+    c3 = veorq_u8( c3, roundKey ); \
+    c4 = veorq_u8( c4, roundKey ); \
+    c5 = veorq_u8( c5, roundKey ); \
+    c6 = veorq_u8( c6, roundKey ); \
+    c7 = veorq_u8( c7, roundKey ); \
+};
+
+#define AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+    keyLimit = (const __n128 *)pExpandedKey->lastDecRoundKey; \
+\
+    UNROLL_AES_ROUNDS( \
+        AES_DECRYPT_ROUND_8, \
+        AES_DECRYPT_FINAL_8, \
+        c0, c1, c2, c3, c4, c5, c6, c7 \
+    ) \
+};
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    __n128 c;
+
+    c = *( __n128 * ) pbSrc;
+
+    AES_ENCRYPT_1( pExpandedKey, c );
+
+    *(__n128 *) pbDst = c;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    __n128 c;
+
+    c = *( __n128 * ) pbSrc;
+
+    AES_DECRYPT_1( pExpandedKey, c );
+
+    *(__n128 *) pbDst = c;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncryptNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __n128 c = *(__n128 *)pbChainingValue;
+    __n128 rk0 = *(__n128 *) &pExpandedKey->RoundKey[0];
+    __n128 rkLast = *(__n128 *) pExpandedKey->lastEncRoundKey;
+    __n128 d, rk0AndLast;
+
+    // This algorithm is dominated by chain of dependent AES rounds, so we want to avoid EOR
+    // instructions on the critical path where possible
+    // We can compute (last round key ^ this plaintext block ^ first round key) off the critical
+    // path and use this with AES_ENCRYPT_1_CHAIN so that only AES instructions write to c in
+    // the main loop
+    rk0AndLast = veorq_u8( rk0, rkLast );
+
+    c = veorq_u8( c, rkLast );
+
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        d = veorq_u8( *(__n128 *)pbSrc, rk0AndLast);
+        AES_ENCRYPT_1_CHAIN( pExpandedKey, c, d );
+        *(__n128 *)pbDst = veorq_u8( c, rkLast );
+
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+    *(__n128 *)pbChainingValue = veorq_u8( c, rkLast );
+}
+
+// Disable warnings and VC++ runtime checks for use of uninitialized values (by design)
+#pragma warning(push)
+#pragma warning( disable: 6001 4701 )
+#pragma runtime_checks( "u", off )
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecryptNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __n128 chain;
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+    __n128 d0, d1, d2, d3, d4, d5, d6, d7;
+    const __n128 * pSrc = (const __n128 *) pbSrc;
+    __n128 * pDst = (__n128 *) pbDst;
+    SIZE_T  cData = cbData / SYMCRYPT_AES_BLOCK_SIZE;
+
+    if( cData < 1 )
+    {
+        return;
+    }
+
+    chain = *(__n128 *) pbChainingValue;
+
+    //
+    // First we do all multiples of 8 blocks
+    //
+
+    while( cData >= 8 )
+    {
+        d0 = c0 = pSrc[0];
+        d1 = c1 = pSrc[1];
+        d2 = c2 = pSrc[2];
+        d3 = c3 = pSrc[3];
+        d4 = c4 = pSrc[4];
+        d5 = c5 = pSrc[5];
+        d6 = c6 = pSrc[6];
+        d7 = c7 = pSrc[7];
+
+        AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        c0 = veorq_u8( c0, chain );
+        c1 = veorq_u8( c1, d0 );
+        c2 = veorq_u8( c2, d1 );
+        c3 = veorq_u8( c3, d2 );
+        c4 = veorq_u8( c4, d3 );
+        c5 = veorq_u8( c5, d4 );
+        c6 = veorq_u8( c6, d5 );
+        c7 = veorq_u8( c7, d6 );
+        chain = d7;
+
+        pDst[0] = c0;
+        pDst[1] = c1;
+        pDst[2] = c2;
+        pDst[3] = c3;
+        pDst[4] = c4;
+        pDst[5] = c5;
+        pDst[6] = c6;
+        pDst[7] = c7;
+
+        pSrc   += 8;
+        pDst   += 8;
+        cData  -= 8;
+    }
+
+    if( cData >= 1 )
+    {
+        //
+        // There is remaining work to be done
+        //
+        d0 = c0 = pSrc[0];
+        if( cData >= 2 )
+        {
+        d1 = c1 = pSrc[1];
+            if( cData >= 3 )
+            {
+        d2 = c2 = pSrc[2];
+                if( cData >= 4 )
+                {
+        d3 = c3 = pSrc[3];
+                    if( cData >= 5 )
+                    {
+        d4 = c4 = pSrc[4];
+                        if( cData >= 6 )
+                        {
+        d5 = c5 = pSrc[5];
+                            if( cData >= 7 )
+                            {
+        d6 = c6 = pSrc[6];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        //
+        // Decrypt 1, 4, or 8 blocks in AES-CBC mode. This might decrypt uninitialized registers,
+        // but those will not be used when we store the results.
+        //
+        if( cData > 4 )
+        {
+            AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+            c0 = veorq_u8( c0, chain );
+            c1 = veorq_u8( c1, d0 );
+            c2 = veorq_u8( c2, d1 );
+            c3 = veorq_u8( c3, d2 );
+            c4 = veorq_u8( c4, d3 );
+            c5 = veorq_u8( c5, d4 );
+            c6 = veorq_u8( c6, d5 );
+        }
+        else if( cData > 1 )
+        {
+            AES_DECRYPT_4( pExpandedKey, c0, c1, c2, c3 );
+            c0 = veorq_u8( c0, chain );
+            c1 = veorq_u8( c1, d0 );
+            c2 = veorq_u8( c2, d1 );
+            c3 = veorq_u8( c3, d2 );
+        } else
+        {
+            AES_DECRYPT_1( pExpandedKey, c0 );
+            c0 = veorq_u8( c0, chain );
+        }
+
+        chain = pSrc[ cData - 1];
+        pDst[0] = c0;
+        if( cData >= 2 )
+        {
+        pDst[1] = c1;
+            if( cData >= 3 )
+            {
+        pDst[2] = c2;
+                if( cData >= 4 )
+                {
+        pDst[3] = c3;
+                    if( cData >= 5 )
+                    {
+        pDst[4] = c4;
+                        if( cData >= 6 )
+                        {
+        pDst[5] = c5;
+                            if( cData >= 7 )
+                            {
+        pDst[6] = c6;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *(__n128 *)pbChainingValue = chain;
+
+    return;
+}
+#pragma runtime_checks( "u", restore )
+#pragma warning( pop )
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcMacNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData )
+{
+    __n128 c = *(__n128 *)pbChainingValue;
+    __n128 rk0 = *(__n128 *) &pExpandedKey->RoundKey[0];
+    __n128 rkLast = *(__n128 *) pExpandedKey->lastEncRoundKey;
+    __n128 d, rk0AndLast;
+
+    // This algorithm is dominated by chain of dependent AES rounds, so we want to avoid EOR
+    // instructions on the critical path where possible
+    // We can compute (last round key ^ this plaintext block ^ first round key) off the critical
+    // path and use this with AES_ENCRYPT_1_CHAIN so that only AES instructions write to c in
+    // the main loop
+    rk0AndLast = veorq_u8( rk0, rkLast );
+
+    c = veorq_u8( c, rkLast );
+
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        d = veorq_u8( *(__n128 *)pbData, rk0AndLast);
+        AES_ENCRYPT_1_CHAIN( pExpandedKey, c, d );
+
+        pbData += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+    *(__n128 *)pbChainingValue = veorq_u8( c, rkLast );
+}
+
+// Disable warnings and VC++ runtime checks for use of uninitialized values (by design)
+#pragma warning(push)
+#pragma warning( disable: 6001 4701 )
+#pragma runtime_checks( "u", off )
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+    const __n128 * pSrc = (const __n128 *) pbSrc;
+    __n128 * pDst = (__n128 *) pbDst;
+
+    while( cbData >= 8 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = pSrc[0];
+        c1 = pSrc[1];
+        c2 = pSrc[2];
+        c3 = pSrc[3];
+        c4 = pSrc[4];
+        c5 = pSrc[5];
+        c6 = pSrc[6];
+        c7 = pSrc[7];
+
+        AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        pDst[0] = c0;
+        pDst[1] = c1;
+        pDst[2] = c2;
+        pDst[3] = c3;
+        pDst[4] = c4;
+        pDst[5] = c5;
+        pDst[6] = c6;
+        pDst[7] = c7;
+
+        pSrc  += 8;
+        pDst  += 8;
+        cbData -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbData < 16 )
+    {
+        return;
+    }
+
+    c0 = pSrc[0];
+    if( cbData >= 32 )
+    {
+    c1 = pSrc[1];
+        if( cbData >= 48 )
+        {
+    c2 = pSrc[2];
+            if( cbData >= 64 )
+            {
+    c3 = pSrc[3];
+                if( cbData >= 80 )
+                {
+    c4 = pSrc[4];
+                    if( cbData >= 96 )
+                    {
+    c5 = pSrc[5];
+                        if( cbData >= 112 )
+                        {
+    c6 = pSrc[6];
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if( cbData >= 5 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+    }
+    else if( cbData >= 2 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        AES_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3 );
+    }
+    else
+    {
+        AES_ENCRYPT_1( pExpandedKey, c0 );
+    }
+
+    pDst[0] = c0;
+    if( cbData >= 32 )
+    {
+    pDst[1] = c1;
+        if( cbData >= 48 )
+        {
+    pDst[2] = c2;
+            if( cbData >= 64 )
+            {
+    pDst[3] = c3;
+                if( cbData >= 80 )
+                {
+    pDst[4] = c4;
+                    if( cbData >= 96 )
+                    {
+    pDst[5] = c5;
+                        if( cbData >= 112 )
+                        {
+    pDst[6] = c6;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+#pragma runtime_checks( "u", restore)
+#pragma warning( pop )
+
+#pragma warning(push)
+#pragma warning( disable:4701 ) // "Use of uninitialized variable"
+#pragma runtime_checks( "u", off )
+
+#define SYMCRYPT_AesCtrMsbXxNeon    SymCryptAesCtrMsb64Neon
+#define VADDQ_UXX                   vaddq_u64
+#define VSUBQ_UXX                   vsubq_u64
+
+#include "aes-pattern.c"
+
+#undef VSUBQ_UXX
+#undef VADDQ_UXX
+#undef SYMCRYPT_AesCtrMsbXxNeon
+
+#define SYMCRYPT_AesCtrMsbXxNeon    SymCryptAesCtrMsb32Neon
+#define VADDQ_UXX                   vaddq_u32
+#define VSUBQ_UXX                   vsubq_u32
+
+#include "aes-pattern.c"
+
+#undef VSUBQ_UXX
+#undef VADDQ_UXX
+#undef SYMCRYPT_AesCtrMsbXxNeon
+
+#pragma runtime_checks( "u", restore )
+#pragma warning(pop)
+
+
+//
+// Multiply by alpha
+//
+// <</>> indicate shifts on 128-bit values
+// <<<</>>>> indicate shifts on 32-bit values
+//
+
+// Multiply by ALPHA
+// t1 = Input <<<< 1                        words shifted left by 1
+// t2 = Input >>>> 31                       words shifted right by 31
+// t1 = t1 ^ (t2 << 32)                     t1 = S << 1
+// t2 = t2 >> 96                            t2 = highest bit of S
+// t2 = (t2 <<<< 7) + (t2 <<<<3) - (t2)     multiply polynomially by 0x87 , we can use - because we only have one bit input
+// res = t1 ^ t2
+//
+#define XTS_MUL_ALPHA_old( _in, _res ) \
+{\
+    __n128 _t1, _t2;\
+\
+    _t1 = vshlq_n_u32( _in, 1 ); \
+    _t2 = vshrq_n_u32( _in, 31); \
+    _t1 = veorq_u32( _t1, vextq_u32( vZero, _t2, 3 )); \
+    _t2 = vextq_u32( _t2, vZero, 3); \
+    _t2 = vsubq_u32( vaddq_u32( vshlq_n_u32( _t2, 7 ), vshlq_n_u32( _t2, 3 ) ), _t2 ); \
+    _res = veorq_u32( _t1, _t2 ); \
+}
+
+//
+// Another approach, use signed shift right to duplicate the bits of the leftmost byte
+// and an AND to mask the modulo reduction and the extraneous bits in the other bytes at the same time.
+// vAlphaMask = (1, 1, ..., 1, 0x87 )
+//
+#define XTS_MUL_ALPHA( _in, _res ) \
+{\
+    __n128 _t1, _t2;\
+\
+    _t1 = vshlq_n_u8( _in, 1 ); \
+    _t2 = vshrq_n_s8( _in, 7 ); \
+    _t2 = vextq_u8( _t2, _t2, 15 ); \
+    _t2 = vandq_u8( _t2, vAlphaMask ); \
+    _res = veorq_u8( _t2, _t1 ); \
+}
+
+
+// Multiply by ALPHA^2
+// t1 = Input <<<< 2
+// t2 = Input >>>> 30
+// t1 = t1 ^ (t2 << 32)
+// t2 = t2 >> 96
+// t2 = (t2 <<<< 7) ^ (t2 <<<< 2) ^ (t2 <<<< 1) ^ t2
+// res = t1 ^ t2
+#define XTS_MUL_ALPHA2( _in, _res ) \
+{\
+    __n128 _t1, _t2;\
+\
+    _t1 = vshlq_n_u32( _in, 2 ); \
+    _t2 = vshrq_n_u32( _in, 30); \
+    _t1 = veorq_u32( _t1, vextq_u32( vZero, _t2, 3 )); \
+    _t2 = vextq_u32( _t2, vZero, 3 ); \
+    _t2 = veorq_u32( veorq_u32( veorq_u32( _t2, vshlq_n_u32( _t2, 7 )), vshlq_n_u32( _t2, 2 ) ), vshlq_n_u32( _t2, 1 ) ); \
+    _res = veorq_u32( _t1, _t2 ); \
+}
+
+// Multiply by ALPHA^4
+// t1 = Input <<<< 4
+// t2 = Input >>>> 28
+// t1 = t1 ^ (t2 << 32)
+// t2 = t2 >> 96
+// t2 = (t2 <<<< 7) ^ (t2 <<<< 2) ^ (t2 <<<< 1) ^ t2
+// res = t1 ^ t2
+#define XTS_MUL_ALPHA4( _in, _res ) \
+{\
+    __n128 _t1, _t2;\
+\
+    _t1 = vshlq_n_u32( _in, 4 ); \
+    _t2 = vshrq_n_u32( _in, 28); \
+    _t1 = veorq_u32( _t1, vextq_u32( vZero, _t2, 3 )); \
+    _t2 = vextq_u32( _t2, vZero, 3 ); \
+    _t2 = veorq_u32( veorq_u32( veorq_u32( _t2, vshlq_n_u32( _t2, 7 )), vshlq_n_u32( _t2, 2 ) ), vshlq_n_u32( _t2, 1 ) ); \
+    _res = veorq_u32( _t1, _t2 ); \
+}
+
+#define XTS_MUL_ALPHA5( _in, _res ) \
+{\
+    __n128 _t1, _t2;\
+\
+    _t1 = vshlq_n_u32( _in, 5 ); \
+    _t2 = vshrq_n_u32( _in, 27); \
+    _t1 = veorq_u32( _t1, vextq_u32( vZero, _t2, 3 )); \
+    _t2 = vextq_u32( _t2, vZero, 3 ); \
+    _t2 = veorq_u32( veorq_u32( veorq_u32( _t2, vshlq_n_u32( _t2, 7 )), vshlq_n_u32( _t2, 2 ) ), vshlq_n_u32( _t2, 1 ) ); \
+    _res = veorq_u32( _t1, _t2 ); \
+}
+
+// Multiply by ALPHA^8
+// res = (Input << 8) | (Input >> 120)
+// t2 = (Input >> 120) * 0x86
+//      i.e. ((Input >> 120) <<<< 7) ^ ((Input >> 120) <<<< 2) ^ ((Input >> 120) <<<< 1)
+//           the 0x01 component is already in res where we want it
+// res = res ^ t2
+//
+// vAlphaMultiplier = (0, 0, ..., 0, 0x86 )
+
+#define XTS_MUL_ALPHA8( _in, _res ) \
+{\
+    __n128 _t2;\
+\
+    _res = vextq_u8( _in, _in, 15 ); \
+    _t2 = vmull_p8( vget_low_p8(_res), vAlphaMultiplier ); \
+    _res = veorq_u32( _res, _t2 ); \
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_(SYMCRYPT_AES_BLOCK_SIZE)PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __n128 t0, t1, t2, t3, t4, t5, t6, t7;
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+    const __n128 vZero = vmovq_n_u8(0);
+    const __n128 vAlphaMask = SYMCRYPT_SET_N128_U8(0x87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+    const __n64 vAlphaMultiplier = SYMCRYPT_SET_N64_U64(0x0000000000000086);
+
+    SIZE_T cbDataMain;  // number of bytes to handle in the main loop
+    SIZE_T cbDataTail;  // number of bytes to handle in the tail loop
+    BYTE tailBuf[2*SYMCRYPT_AES_BLOCK_SIZE];
+
+    SYMCRYPT_ASSERT(cbData >= SYMCRYPT_AES_BLOCK_SIZE);
+
+    // To simplify logic and unusual size processing, we handle all
+    // data not a multiple of 8 blocks in the tail loop
+    cbDataTail = cbData & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1);
+    // Additionally, so that ciphertext stealing logic does not rely on
+    // reading back from the destination buffer, when we have a non-zero
+    // tail, we ensure that we handle at least 1 whole block in the tail
+    //
+    // Note that our caller has ensured we have at least 1 whole block
+    // to process, this is checked in debug build
+    // This means that cbDataTail is in [1,15] at this point iff there are
+    // at least 8 whole blocks to process; so the below does not cause
+    // cbDataTail or cbDataMain to exceed cbData
+    cbDataTail += ((cbDataTail > 0) && (cbDataTail < SYMCRYPT_AES_BLOCK_SIZE)) ? (8*SYMCRYPT_AES_BLOCK_SIZE) : 0;
+    cbDataMain = cbData - cbDataTail;
+
+    SYMCRYPT_ASSERT(cbDataMain <= cbData);
+    SYMCRYPT_ASSERT(cbDataTail <= cbData);
+    SYMCRYPT_ASSERT((cbDataMain & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1)) == 0);
+
+    t0 = *(__n128 *)pbTweakBlock;
+
+    if( cbDataMain > 0 )
+    {
+        // Set up for main loop entry
+        // NOTE: We load the first 8 blocks and store the last 8 blocks out of the loop to allow
+        // greater instruction interleaving in the main loop.
+        // This appears to give about 5-8% performance uplift on little (in-order) cores and has
+        // no effect on big cores.
+        XTS_MUL_ALPHA4( t0, t4 );
+        XTS_MUL_ALPHA ( t0, t1 );
+        XTS_MUL_ALPHA ( t4, t5 );
+        XTS_MUL_ALPHA ( t1, t2 );
+        XTS_MUL_ALPHA ( t5, t6 );
+        XTS_MUL_ALPHA ( t2, t3 );
+        XTS_MUL_ALPHA ( t6, t7 );
+
+        c0 = veorq_u32( vld1q_u8( pbSrc + (0*16) ), t0 );
+        c1 = veorq_u32( vld1q_u8( pbSrc + (1*16) ), t1 );
+        c2 = veorq_u32( vld1q_u8( pbSrc + (2*16) ), t2 );
+        c3 = veorq_u32( vld1q_u8( pbSrc + (3*16) ), t3 );
+        c4 = veorq_u32( vld1q_u8( pbSrc + (4*16) ), t4 );
+        c5 = veorq_u32( vld1q_u8( pbSrc + (5*16) ), t5 );
+        c6 = veorq_u32( vld1q_u8( pbSrc + (6*16) ), t6 );
+        c7 = veorq_u32( vld1q_u8( pbSrc + (7*16) ), t7 );
+
+        for(;;)
+        {
+            pbSrc += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+
+            AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+            cbDataMain -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+            if( cbDataMain < 8 * SYMCRYPT_AES_BLOCK_SIZE )
+            {
+                break;
+            }
+
+            // Interleave the final xor, write, and compute next tweak block, and load, and first xor.
+            // This reduces register pressure and is more efficient.
+            vst1q_u8( pbDst + (0*16), veorq_u32( c0, t0 ) );
+            vst1q_u8( pbDst + (1*16), veorq_u32( c1, t1 ) );
+            vst1q_u8( pbDst + (2*16), veorq_u32( c2, t2 ) );
+            vst1q_u8( pbDst + (3*16), veorq_u32( c3, t3 ) );
+            vst1q_u8( pbDst + (4*16), veorq_u32( c4, t4 ) );
+            vst1q_u8( pbDst + (5*16), veorq_u32( c5, t5 ) );
+            vst1q_u8( pbDst + (6*16), veorq_u32( c6, t6 ) );
+            vst1q_u8( pbDst + (7*16), veorq_u32( c7, t7 ) );
+
+            XTS_MUL_ALPHA8( t0, t0 );
+            XTS_MUL_ALPHA8( t1, t1 );
+            XTS_MUL_ALPHA8( t2, t2 );
+            XTS_MUL_ALPHA8( t3, t3 );
+            XTS_MUL_ALPHA8( t4, t4 );
+            XTS_MUL_ALPHA8( t5, t5 );
+            XTS_MUL_ALPHA8( t6, t6 );
+            XTS_MUL_ALPHA8( t7, t7 );
+
+            c0 = veorq_u32( vld1q_u8( pbSrc + (0*16) ), t0 );
+            c1 = veorq_u32( vld1q_u8( pbSrc + (1*16) ), t1 );
+            c2 = veorq_u32( vld1q_u8( pbSrc + (2*16) ), t2 );
+            c3 = veorq_u32( vld1q_u8( pbSrc + (3*16) ), t3 );
+            c4 = veorq_u32( vld1q_u8( pbSrc + (4*16) ), t4 );
+            c5 = veorq_u32( vld1q_u8( pbSrc + (5*16) ), t5 );
+            c6 = veorq_u32( vld1q_u8( pbSrc + (6*16) ), t6 );
+            c7 = veorq_u32( vld1q_u8( pbSrc + (7*16) ), t7 );
+
+            pbDst += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        }
+
+        vst1q_u8( pbDst + (0*16), veorq_u32( c0, t0 ) );
+        vst1q_u8( pbDst + (1*16), veorq_u32( c1, t1 ) );
+        vst1q_u8( pbDst + (2*16), veorq_u32( c2, t2 ) );
+        vst1q_u8( pbDst + (3*16), veorq_u32( c3, t3 ) );
+        vst1q_u8( pbDst + (4*16), veorq_u32( c4, t4 ) );
+        vst1q_u8( pbDst + (5*16), veorq_u32( c5, t5 ) );
+        vst1q_u8( pbDst + (6*16), veorq_u32( c6, t6 ) );
+        vst1q_u8( pbDst + (7*16), veorq_u32( c7, t7 ) );
+
+        // We won't do another 8-block set
+        // Update only the first tweak block in case it is needed for tail
+        XTS_MUL_ALPHA8( t0, t0 );
+
+        pbDst += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail == 0 )
+    {
+        return; // <-- expected case; early return here
+    }
+
+    // Rare case, with data unit length not being multiple of 128 bytes, handle the tail one block at a time
+    while( cbDataTail >= 2*SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = veorq_u32( vld1q_u8(pbSrc), t0 );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        AES_ENCRYPT_1( pExpandedKey, c0 );
+        vst1q_u8( pbDst, veorq_u32( c0, t0 ) );
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        XTS_MUL_ALPHA( t0, t0 );
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Ciphertext stealing encryption
+        //
+        //                      +--------------+
+        //                      |              |
+        //                      |              V
+        // +-----------------+  |  +-----+-----------+
+        // |      P_m-1      |  |  | P_m |++++CP+++++|
+        // +-----------------+  |  +-----+-----------+
+        //          |           |           |
+        //       enc_m-1        |         enc_m
+        //          |           |           |
+        //          V           |           V
+        // +-----+-----------+  |  +-----------------+
+        // | C_m |++++CP+++++|--+  |      C_m-1      |
+        // +-----+-----------+     +-----------------+
+        //    |                   /
+        //    +----------------  /  --+
+        //                      /     |
+        //                      |     V
+        // +-----------------+  |  +-----+
+        // |      C_m-1      |<-+  | C_m |
+        // +-----------------+     +-----+
+
+        // Encrypt penultimate plaintext block into tailBuf
+        c0 = veorq_u32( vld1q_u8(pbSrc), t0 );
+        AES_ENCRYPT_1( pExpandedKey, c0 );
+        c0 = veorq_u32( c0, t0 );
+        vst1q_u8( &tailBuf[0], c0 );
+        vst1q_u8( &tailBuf[SYMCRYPT_AES_BLOCK_SIZE], c0 );
+
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+
+        // Copy final plaintext bytes to prefix of tailBuf - we must read before writing to support in-place encryption
+        memcpy( &tailBuf[0], pbSrc + SYMCRYPT_AES_BLOCK_SIZE, cbDataTail );
+        // Copy prefix of tailBuf[SYMCRYPT_AES_BLOCK_SIZE] to the right place in the destination buffer
+        memcpy( pbDst + SYMCRYPT_AES_BLOCK_SIZE, &tailBuf[SYMCRYPT_AES_BLOCK_SIZE], cbDataTail );
+
+        // Do final tweak update
+        XTS_MUL_ALPHA( t0, t0 );
+
+        // Load updated tailBuf into c0
+        c0 = vld1q_u8( &tailBuf[0] );
+    } else {
+        // Just load final plaintext block into c0
+        c0 = vld1q_u8( pbSrc );
+    }
+
+    // Final full block encryption
+    c0 = veorq_u32( c0, t0 );
+    AES_ENCRYPT_1( pExpandedKey, c0 );
+    vst1q_u8( pbDst, veorq_u32( c0, t0 ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_(SYMCRYPT_AES_BLOCK_SIZE)PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __n128 t0, t1, t2, t3, t4, t5, t6, t7;
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+    const __n128 vZero = vmovq_n_u8(0);
+    const __n128 vAlphaMask = SYMCRYPT_SET_N128_U8(0x87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+    const __n64 vAlphaMultiplier = SYMCRYPT_SET_N64_U64(0x0000000000000086);
+
+    SIZE_T cbDataMain;  // number of bytes to handle in the main loop
+    SIZE_T cbDataTail;  // number of bytes to handle in the tail loop
+    BYTE tailBuf[2*SYMCRYPT_AES_BLOCK_SIZE];
+
+    SYMCRYPT_ASSERT(cbData >= SYMCRYPT_AES_BLOCK_SIZE);
+
+    // To simplify logic and unusual size processing, we handle all
+    // data not a multiple of 8 blocks in the tail loop
+    cbDataTail = cbData & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1);
+    // Additionally, so that ciphertext stealing logic does not rely on
+    // reading back from the destination buffer, when we have a non-zero
+    // tail, we ensure that we handle at least 1 whole block in the tail
+    //
+    // Note that our caller has ensured we have at least 1 whole block
+    // to process, this is checked in debug build
+    // This means that cbDataTail is in [1,15] at this point iff there are
+    // at least 8 whole blocks to process; so the below does not cause
+    // cbDataTail or cbDataMain to exceed cbData
+    cbDataTail += ((cbDataTail > 0) && (cbDataTail < SYMCRYPT_AES_BLOCK_SIZE)) ? (8*SYMCRYPT_AES_BLOCK_SIZE) : 0;
+    cbDataMain = cbData - cbDataTail;
+
+    SYMCRYPT_ASSERT(cbDataMain <= cbData);
+    SYMCRYPT_ASSERT(cbDataTail <= cbData);
+    SYMCRYPT_ASSERT((cbDataMain & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1)) == 0);
+
+    t0 = *(__n128 *)pbTweakBlock;
+    t7 = t0;
+
+    if( cbDataMain > 0 )
+    {
+        // Set up for main loop entry
+        // NOTE: We load the first 8 blocks and store the last 8 blocks out of the loop to allow
+        // greater instruction interleaving in the main loop.
+        // This appears to give about 5-8% performance uplift on little (in-order) cores and has
+        // no effect on big cores.
+        XTS_MUL_ALPHA4( t0, t4 );
+        XTS_MUL_ALPHA ( t0, t1 );
+        XTS_MUL_ALPHA ( t4, t5 );
+        XTS_MUL_ALPHA ( t1, t2 );
+        XTS_MUL_ALPHA ( t5, t6 );
+        XTS_MUL_ALPHA ( t2, t3 );
+        XTS_MUL_ALPHA ( t6, t7 );
+
+        c0 = veorq_u32( vld1q_u8( pbSrc + (0*16) ), t0 );
+        c1 = veorq_u32( vld1q_u8( pbSrc + (1*16) ), t1 );
+        c2 = veorq_u32( vld1q_u8( pbSrc + (2*16) ), t2 );
+        c3 = veorq_u32( vld1q_u8( pbSrc + (3*16) ), t3 );
+        c4 = veorq_u32( vld1q_u8( pbSrc + (4*16) ), t4 );
+        c5 = veorq_u32( vld1q_u8( pbSrc + (5*16) ), t5 );
+        c6 = veorq_u32( vld1q_u8( pbSrc + (6*16) ), t6 );
+        c7 = veorq_u32( vld1q_u8( pbSrc + (7*16) ), t7 );
+
+        for(;;)
+        {
+            pbSrc += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+
+            AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+            cbDataMain -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+            if( cbDataMain < 8 * SYMCRYPT_AES_BLOCK_SIZE )
+            {
+                break;
+            }
+
+            // Interleave the final xor, write, and compute next tweak block, and load, and first xor.
+            // This reduces register pressure and is more efficient.
+            vst1q_u8( pbDst + (0*16), veorq_u32( c0, t0 ) );
+            vst1q_u8( pbDst + (1*16), veorq_u32( c1, t1 ) );
+            vst1q_u8( pbDst + (2*16), veorq_u32( c2, t2 ) );
+            vst1q_u8( pbDst + (3*16), veorq_u32( c3, t3 ) );
+            vst1q_u8( pbDst + (4*16), veorq_u32( c4, t4 ) );
+            vst1q_u8( pbDst + (5*16), veorq_u32( c5, t5 ) );
+            vst1q_u8( pbDst + (6*16), veorq_u32( c6, t6 ) );
+            vst1q_u8( pbDst + (7*16), veorq_u32( c7, t7 ) );
+
+            XTS_MUL_ALPHA8( t0, t0 );
+            XTS_MUL_ALPHA8( t1, t1 );
+            XTS_MUL_ALPHA8( t2, t2 );
+            XTS_MUL_ALPHA8( t3, t3 );
+            XTS_MUL_ALPHA8( t4, t4 );
+            XTS_MUL_ALPHA8( t5, t5 );
+            XTS_MUL_ALPHA8( t6, t6 );
+            XTS_MUL_ALPHA8( t7, t7 );
+
+            c0 = veorq_u32( vld1q_u8( pbSrc + (0*16) ), t0 );
+            c1 = veorq_u32( vld1q_u8( pbSrc + (1*16) ), t1 );
+            c2 = veorq_u32( vld1q_u8( pbSrc + (2*16) ), t2 );
+            c3 = veorq_u32( vld1q_u8( pbSrc + (3*16) ), t3 );
+            c4 = veorq_u32( vld1q_u8( pbSrc + (4*16) ), t4 );
+            c5 = veorq_u32( vld1q_u8( pbSrc + (5*16) ), t5 );
+            c6 = veorq_u32( vld1q_u8( pbSrc + (6*16) ), t6 );
+            c7 = veorq_u32( vld1q_u8( pbSrc + (7*16) ), t7 );
+
+            pbDst += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        }
+
+        vst1q_u8( pbDst + (0*16), veorq_u32( c0, t0 ) );
+        vst1q_u8( pbDst + (1*16), veorq_u32( c1, t1 ) );
+        vst1q_u8( pbDst + (2*16), veorq_u32( c2, t2 ) );
+        vst1q_u8( pbDst + (3*16), veorq_u32( c3, t3 ) );
+        vst1q_u8( pbDst + (4*16), veorq_u32( c4, t4 ) );
+        vst1q_u8( pbDst + (5*16), veorq_u32( c5, t5 ) );
+        vst1q_u8( pbDst + (6*16), veorq_u32( c6, t6 ) );
+        vst1q_u8( pbDst + (7*16), veorq_u32( c7, t7 ) );
+
+        // We won't do another 8-block set
+        // Update only the first tweak block in case it is needed for tail
+        XTS_MUL_ALPHA8( t0, t0 );
+
+        pbDst += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail == 0 )
+    {
+        return; // <-- expected case; early return here
+    }
+
+    // Rare case, with data unit length not being multiple of 128 bytes, handle the tail one block at a time
+    while( cbDataTail >= 2*SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = veorq_u32( vld1q_u8( pbSrc ), t0 );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        AES_DECRYPT_1( pExpandedKey, c0 );
+        vst1q_u8( pbDst, veorq_u32( c0, t0 ) );
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        XTS_MUL_ALPHA( t0, t0 );
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Ciphertext stealing decryption
+        //
+        //                      +--------------+
+        //                      |              |
+        //                      |              V
+        // +-----------------+  |  +-----+-----------+
+        // |      C_m-1      |  |  | C_m |++++CP+++++|
+        // +-----------------+  |  +-----+-----------+
+        //          |           |           |
+        //        dec_m         |        dec_m-1
+        //          |           |           |
+        //          V           |           V
+        // +-----+-----------+  |  +-----------------+
+        // | P_m |++++CP+++++|--+  |      P_m-1      |
+        // +-----+-----------+     +-----------------+
+        //    |                   /
+        //    +----------------  /  --+
+        //                      /     |
+        //                      |     V
+        // +-----------------+  |  +-----+
+        // |      P_m-1      |<-+  | P_m |
+        // +-----------------+     +-----+
+
+        // Do final tweak update into t1
+        // Penultimate tweak is in t0, ready for final decryption
+        XTS_MUL_ALPHA( t0, t1 );
+
+        // Decrypt penultimate ciphertext block into tailBuf
+        c0 = veorq_u32( vld1q_u8( pbSrc ), t1 );
+        AES_DECRYPT_1( pExpandedKey, c0 );
+        c0 = veorq_u32( c0, t1 );
+        vst1q_u8( &tailBuf[0], c0 );
+        vst1q_u8( &tailBuf[SYMCRYPT_AES_BLOCK_SIZE], c0 );
+
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+
+        // Copy final ciphertext bytes to prefix of tailBuf - we must read before writing to support in-place decryption
+        memcpy( &tailBuf[0], pbSrc + SYMCRYPT_AES_BLOCK_SIZE, cbDataTail );
+        // Copy prefix of tailBuf[SYMCRYPT_AES_BLOCK_SIZE] to the right place in the destination buffer
+        memcpy( pbDst + SYMCRYPT_AES_BLOCK_SIZE, &tailBuf[SYMCRYPT_AES_BLOCK_SIZE], cbDataTail );
+
+        // Load updated tailBuf into c0
+        c0 = vld1q_u8( &tailBuf[0] );
+    } else {
+        // Just load final ciphertext block into c0
+        c0 = vld1q_u8( pbSrc );
+    }
+
+    // Final full block decryption
+    c0 = veorq_u32( c0, t0 );
+    AES_DECRYPT_1( pExpandedKey, c0 );
+    vst1q_u8( pbDst, veorq_u32( c0, t0 ) );
+}
+
+#include "ghash_definitions.h"
+
+#define AES_ENCRYPT_ROUND_4_GHASH_1( c0, c1, c2, c3, r0, r0x, t0, t1, gHashPointer, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    AESE_AESMC( c0, roundKey ) \
+    AESE_AESMC( c1, roundKey ) \
+    AESE_AESMC( c2, roundKey ) \
+    AESE_AESMC( c3, roundKey ) \
+\
+    r0x = *gHashPointer; \
+    r0x = vrev64q_u8( r0x ); \
+    r0 = vextq_u8( r0x, r0x, 8 ); \
+    r0x = veorq_u8( r0, r0x ); \
+    gHashPointer++; \
+\
+    t1 = GHASH_H_POWER(gHashExpandedKeyTable, todo); \
+    t0 = vmullq_p64( r0, t1 ); \
+    t1 = vmull_high_p64( r0, t1 ); \
+\
+    resl = veorq_u8( resl, t0 ); \
+    resh = veorq_u8( resh, t1 ); \
+\
+    t1 = GHASH_Hx_POWER(gHashExpandedKeyTable, todo); \
+    t1 = vmullq_p64( r0x, t1 ); \
+\
+    resm = veorq_u8( resm, t1 ); \
+    todo--; \
+};
+
+//
+// Using a loop with AESE_AESMC and AESD_AESIMC, the compiler can still prematurely rearrange the loop and
+// lose opportunity for scheduling adjacent pairs.
+// Instead, explicitly unroll the AES rounds with this macro.
+//
+#define AES_GCM_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3, gHashPointer, gHashRounds, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)&pExpandedKey->RoundKey[0]; \
+    keyLimit = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+    __n128 t0, t1, r0, r0x; \
+    SIZE_T aesEncryptGhashLoop; \
+\
+    /* Do gHashRounds full rounds (AES-128|AES-192|AES-256) with stitched GHASH */ \
+    roundKey = *keyPtr++; \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < gHashRounds; aesEncryptGhashLoop++) \
+    { \
+        AES_ENCRYPT_ROUND_4_GHASH_1( c0, c1, c2, c3, r0, r0x, t0, t1, gHashPointer, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+        roundKey = *keyPtr++; \
+    } \
+\
+    /* Do 9-gHashRounds full rounds (AES-128|AES-192|AES-256) */ \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < (9-gHashRounds); aesEncryptGhashLoop++) \
+    { \
+        AES_ENCRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+    } \
+\
+    if ( keyPtr < keyLimit ) \
+    { \
+        /* Do 2 more full rounds (AES-192|AES-256) */ \
+        AES_ENCRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+        AES_ENCRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+\
+        if ( keyPtr < keyLimit ) \
+        { \
+            /* Do 2 more full rounds (AES-256) */ \
+            AES_ENCRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+            roundKey = *keyPtr++; \
+            AES_ENCRYPT_ROUND_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+            roundKey = *keyPtr++; \
+        } \
+    } \
+\
+    /* Do final round (AES-128|AES-192|AES-256) */ \
+    AES_ENCRYPT_FINAL_4( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+};
+
+#define AES_ENCRYPT_ROUND_8_GHASH_1( c0, c1, c2, c3, c4, c5, c6, c7, r0, r0x, t0, t1, gHashPointer, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    AESE_AESMC( c0, roundKey ) \
+    AESE_AESMC( c1, roundKey ) \
+    AESE_AESMC( c2, roundKey ) \
+    AESE_AESMC( c3, roundKey ) \
+    AESE_AESMC( c4, roundKey ) \
+    AESE_AESMC( c5, roundKey ) \
+    AESE_AESMC( c6, roundKey ) \
+    AESE_AESMC( c7, roundKey ) \
+\
+    r0x = *gHashPointer; \
+    r0x = vrev64q_u8( r0x ); \
+    r0 = vextq_u8( r0x, r0x, 8 ); \
+    r0x = veorq_u8( r0, r0x ); \
+    gHashPointer++; \
+\
+    t1 = GHASH_H_POWER(gHashExpandedKeyTable, todo); \
+    t0 = vmullq_p64( r0, t1 ); \
+    t1 = vmull_high_p64( r0, t1 ); \
+\
+    resl = veorq_u8( resl, t0 ); \
+    resh = veorq_u8( resh, t1 ); \
+\
+    t1 = GHASH_Hx_POWER(gHashExpandedKeyTable, todo); \
+    t1 = vmullq_p64( r0x, t1 ); \
+\
+    resm = veorq_u8( resm, t1 ); \
+    todo--; \
+};
+
+//
+// Using a loop with AESE_AESMC and AESD_AESIMC, the compiler can still prematurely rearrange the loop and
+// lose opportunity for scheduling adjacent pairs.
+// Instead, explicitly unroll the AES rounds with this macro.
+//
+#define AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, gHashPointer, gHashRounds, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    const __n128 *keyPtr; \
+    const __n128 *keyLimit; \
+    __n128 roundKey; \
+\
+    keyPtr = (const __n128 *)&pExpandedKey->RoundKey[0]; \
+    keyLimit = (const __n128 *)pExpandedKey->lastEncRoundKey; \
+    __n128 t0, t1, r0, r0x; \
+    SIZE_T aesEncryptGhashLoop; \
+\
+    /* Do gHashRounds full rounds (AES-128|AES-192|AES-256) with stitched GHASH */ \
+    roundKey = *keyPtr++; \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < gHashRounds; aesEncryptGhashLoop++) \
+    { \
+        AES_ENCRYPT_ROUND_8_GHASH_1( c0, c1, c2, c3, c4, c5, c6, c7, r0, r0x, t0, t1, gHashPointer, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+        roundKey = *keyPtr++; \
+    } \
+\
+    /* Do 9-gHashRounds full rounds (AES-128|AES-192|AES-256) */ \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < (9-gHashRounds); aesEncryptGhashLoop++) \
+    { \
+        AES_ENCRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+    } \
+\
+    if ( keyPtr < keyLimit ) \
+    { \
+        /* Do 2 more full rounds (AES-192|AES-256) */ \
+        AES_ENCRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+        AES_ENCRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+        roundKey = *keyPtr++; \
+\
+        if ( keyPtr < keyLimit ) \
+        { \
+            /* Do 2 more full rounds (AES-256) */ \
+            AES_ENCRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+            roundKey = *keyPtr++; \
+            AES_ENCRYPT_ROUND_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+            roundKey = *keyPtr++; \
+        } \
+    } \
+\
+    /* Do final round (AES-128|AES-192|AES-256) */ \
+    AES_ENCRYPT_FINAL_8( c0, c1, c2, c3, c4, c5, c6, c7 ) \
+};
+
+// This call is functionally identical to:
+// SymCryptAesCtrMsb64Neon( pExpandedKey,
+//                          pbChainingValue,
+//                          pbSrc,
+//                          pbDst,
+//                          cbData );
+// SymCryptGHashAppendDataPmull(    expandedKeyTable,
+//                                  pState,
+//                                  pbDstOrig,
+//                                  cbDataOrig );
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptStitchedNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __n128          chain = *(__n128 *)pbChainingValue;
+    const __n128 *  pSrc = (const __n128 *) pbSrc;
+    const __n128 *  pGhashSrc = (const __n128 *) pbDst;
+    __n128 *        pDst = (__n128 *) pbDst;
+
+    const __n128 chainIncrement1 = SYMCRYPT_SET_N128_U64( 0, 1 );
+    const __n128 chainIncrement2 = SYMCRYPT_SET_N128_U64( 0, 2 );
+    const __n128 chainIncrement8 = SYMCRYPT_SET_N128_U64( 0, 8 );
+
+    __n128 ctr0, ctr1, ctr2, ctr3, ctr4, ctr5, ctr6, ctr7;
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+    __n128 r0, r1;
+    __n128 r0x, r1x;
+
+    __n128 state;
+    __n128 a0, a1, a2;
+    const __n64 vMultiplicationConstant = SYMCRYPT_SET_N64_U64(0xc200000000000000);
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+
+    SYMCRYPT_ASSERT( (cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == 0 ); // cbData is multiple of block size
+
+    // Our chain variable is in integer format, not the MSBfirst format loaded from memory.
+    ctr0 = vrev64q_u8( chain );
+    ctr1 = vaddq_u32( ctr0, chainIncrement1 );
+    ctr2 = vaddq_u32( ctr0, chainIncrement2 );
+    ctr3 = vaddq_u32( ctr1, chainIncrement2 );
+    ctr4 = vaddq_u32( ctr2, chainIncrement2 );
+    ctr5 = vaddq_u32( ctr3, chainIncrement2 );
+    ctr6 = vaddq_u32( ctr4, chainIncrement2 );
+    ctr7 = vaddq_u32( ctr5, chainIncrement2 );
+
+    state = *(__n128 *) pState;
+
+    todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PMULL_HPOWERS );
+    CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+
+    // Do 8 blocks of CTR either for tail (if total blocks <8) or for encryption of first 8 blocks
+    c0 = vrev64q_u8( ctr0 );
+    c1 = vrev64q_u8( ctr1 );
+    c2 = vrev64q_u8( ctr2 );
+    c3 = vrev64q_u8( ctr3 );
+    c4 = vrev64q_u8( ctr4 );
+    c5 = vrev64q_u8( ctr5 );
+    c6 = vrev64q_u8( ctr6 );
+    c7 = vrev64q_u8( ctr7 );
+
+    AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+    if ( cbData >= 8 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        ctr0 = vaddq_u32( ctr0, chainIncrement8 );
+        ctr1 = vaddq_u32( ctr1, chainIncrement8 );
+        ctr2 = vaddq_u32( ctr2, chainIncrement8 );
+        ctr3 = vaddq_u32( ctr3, chainIncrement8 );
+        ctr4 = vaddq_u32( ctr4, chainIncrement8 );
+        ctr5 = vaddq_u32( ctr5, chainIncrement8 );
+        ctr6 = vaddq_u32( ctr6, chainIncrement8 );
+        ctr7 = vaddq_u32( ctr7, chainIncrement8 );
+
+        // Encrypt first 8 blocks
+        pDst[0] = veorq_u64( pSrc[0], c0 );
+        pDst[1] = veorq_u64( pSrc[1], c1 );
+        pDst[2] = veorq_u64( pSrc[2], c2 );
+        pDst[3] = veorq_u64( pSrc[3], c3 );
+        pDst[4] = veorq_u64( pSrc[4], c4 );
+        pDst[5] = veorq_u64( pSrc[5], c5 );
+        pDst[6] = veorq_u64( pSrc[6], c6 );
+        pDst[7] = veorq_u64( pSrc[7], c7 );
+
+        pDst  += 8;
+        pSrc  += 8;
+
+        while( nBlocks >= 16 )
+        {
+            // In this loop we always have 8 blocks to encrypt and we have already encrypted the previous 8 blocks ready for GHASH
+            c0 = vrev64q_u8( ctr0 );
+            c1 = vrev64q_u8( ctr1 );
+            c2 = vrev64q_u8( ctr2 );
+            c3 = vrev64q_u8( ctr3 );
+            c4 = vrev64q_u8( ctr4 );
+            c5 = vrev64q_u8( ctr5 );
+            c6 = vrev64q_u8( ctr6 );
+            c7 = vrev64q_u8( ctr7 );
+
+            ctr0 = vaddq_u32( ctr0, chainIncrement8 );
+            ctr1 = vaddq_u32( ctr1, chainIncrement8 );
+            ctr2 = vaddq_u32( ctr2, chainIncrement8 );
+            ctr3 = vaddq_u32( ctr3, chainIncrement8 );
+            ctr4 = vaddq_u32( ctr4, chainIncrement8 );
+            ctr5 = vaddq_u32( ctr5, chainIncrement8 );
+            ctr6 = vaddq_u32( ctr6, chainIncrement8 );
+            ctr7 = vaddq_u32( ctr7, chainIncrement8 );
+
+            AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pGhashSrc, 8, expandedKeyTable, todo, a0, a1, a2 );
+
+            pDst[0] = veorq_u64( pSrc[0], c0 );
+            pDst[1] = veorq_u64( pSrc[1], c1 );
+            pDst[2] = veorq_u64( pSrc[2], c2 );
+            pDst[3] = veorq_u64( pSrc[3], c3 );
+            pDst[4] = veorq_u64( pSrc[4], c4 );
+            pDst[5] = veorq_u64( pSrc[5], c5 );
+            pDst[6] = veorq_u64( pSrc[6], c6 );
+            pDst[7] = veorq_u64( pSrc[7], c7 );
+
+            pDst  += 8;
+            pSrc  += 8;
+            nBlocks -= 8;
+
+            if (todo == 0)
+            {
+                CLMUL_3_POST( a0, a1, a2 );
+                MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PMULL_HPOWERS );
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+        }
+
+        // We now have at least 8 blocks of encrypted data to GHASH and at most 7 blocks left to encrypt
+        // Do 8 blocks of GHASH in parallel with generating 0, 4, or 8 AES-CTR blocks for tail encryption
+        nBlocks -= 8;
+        if (nBlocks > 0)
+        {
+            c0 = vrev64q_u8( ctr0 );
+            c1 = vrev64q_u8( ctr1 );
+            c2 = vrev64q_u8( ctr2 );
+            c3 = vrev64q_u8( ctr3 );
+
+            if (nBlocks > 4)
+            {
+                // Do 8 rounds of AES-CTR for tail in parallel with 8 rounds of GHASH
+                c4 = vrev64q_u8( ctr4 );
+                c5 = vrev64q_u8( ctr5 );
+                c6 = vrev64q_u8( ctr6 );
+
+                AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pGhashSrc, 8, expandedKeyTable, todo, a0, a1, a2 );
+            }
+            else
+            {
+                // Do 4 rounds of AES-CTR for tail in parallel with 8 rounds of GHASH
+                AES_GCM_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3, pGhashSrc, 8, expandedKeyTable, todo, a0, a1, a2 );
+            }
+
+            if( todo == 0)
+            {
+                CLMUL_3_POST( a0, a1, a2 );
+                MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PMULL_HPOWERS );
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+        }
+        else
+        {
+            // Just do the final 8 rounds of GHASH
+            for( todo=8; todo>0; todo-- )
+            {
+                r0x = vrev64q_u8( pGhashSrc[0] );
+                r0 = vextq_u8( r0x, r0x, 8 );
+                r0x = veorq_u8( r0, r0x );
+                pGhashSrc++;
+
+                CLMUL_ACCX_3( r0, r0x, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+
+            CLMUL_3_POST( a0, a1, a2 );
+            MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+        }
+    }
+
+    if( nBlocks > 0 )
+    {
+        // Encrypt 1-7 blocks with pre-generated AES-CTR blocks and GHASH the results
+        while( nBlocks >= 2 )
+        {
+            ctr0 = vaddq_u32( ctr0, chainIncrement2 );
+
+            r0 = veorq_u64( pSrc[0], c0 );
+            r1 = veorq_u64( pSrc[1], c1 );
+
+            pDst[0] = r0;
+            pDst[1] = r1;
+
+            r0x = vrev64q_u8( r0 );
+            r1x = vrev64q_u8( r1 );
+            r0 = vextq_u8( r0x, r0x, 8 );
+            r1 = vextq_u8( r1x, r1x, 8 );
+            r0x = veorq_u8( r0, r0x );
+            r1x = veorq_u8( r1, r1x );
+
+            CLMUL_ACCX_3( r0, r0x, GHASH_H_POWER(expandedKeyTable, todo - 0), GHASH_Hx_POWER(expandedKeyTable, todo - 0), a0, a1, a2 );
+            CLMUL_ACCX_3( r1, r1x, GHASH_H_POWER(expandedKeyTable, todo - 1), GHASH_Hx_POWER(expandedKeyTable, todo - 1), a0, a1, a2 );
+
+            pDst    += 2;
+            pSrc    += 2;
+            todo    -= 2;
+            nBlocks -= 2;
+            c0 = c2;
+            c1 = c3;
+            c2 = c4;
+            c3 = c5;
+            c4 = c6;
+        }
+
+        if( nBlocks > 0 )
+        {
+            ctr0 = vaddq_u32( ctr0, chainIncrement1 );
+
+            r0 = veorq_u64( pSrc[0], c0 );
+            pDst[0] = r0;
+            r0x = vrev64q_u8( r0 );
+            r0 = vextq_u8( r0x, r0x, 8 );
+            r0x = veorq_u8( r0, r0x );
+
+            CLMUL_ACCX_3( r0, r0x, GHASH_H_POWER(expandedKeyTable, 1), GHASH_Hx_POWER(expandedKeyTable, 1), a0, a1, a2 );
+        }
+
+        CLMUL_3_POST( a0, a1, a2 );
+        MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+    }
+
+    chain = vrev64q_u8( ctr0 );
+    *(__n128 *)pbChainingValue = chain;
+    *(__n128 *)pState = state;
+}
+
+#pragma warning(push)
+#pragma warning( disable:4701 ) // "Use of uninitialized variable" -
+#pragma runtime_checks( "u", off )
+// This call is functionally identical to:
+// SymCryptGHashAppendDataPmull(expandedKeyTable,
+//                              pState,
+//                              pbSrc,
+//                              cbData );
+// SymCryptAesCtrMsb64Neon( pExpandedKey,
+//                          pbChainingValue,
+//                          pbSrc,
+//                          pbDst,
+//                          cbData );
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptStitchedNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __n128          chain = *(__n128 *)pbChainingValue;
+    const __n128 *  pSrc = (const __n128 *) pbSrc;
+    const __n128 *  pGhashSrc = (const __n128 *) pbSrc;
+    __n128 *        pDst = (__n128 *) pbDst;
+
+    const __n128 chainIncrement1 = SYMCRYPT_SET_N128_U64( 0, 1 );
+    const __n128 chainIncrement2 = SYMCRYPT_SET_N128_U64( 0, 2 );
+    const __n128 chainIncrement8 = SYMCRYPT_SET_N128_U64( 0, 8 );
+
+    __n128 ctr0, ctr1, ctr2, ctr3, ctr4, ctr5, ctr6, ctr7;
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+
+    __n128 state;
+    __n128 a0, a1, a2;
+    const __n64 vMultiplicationConstant = SYMCRYPT_SET_N64_U64(0xc200000000000000);
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+
+    SYMCRYPT_ASSERT( (cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == 0 ); // cbData is multiple of block size
+
+    // Our chain variable is in integer format, not the MSBfirst format loaded from memory.
+    ctr0 = vrev64q_u8( chain );
+    ctr1 = vaddq_u32( ctr0, chainIncrement1 );
+    ctr2 = vaddq_u32( ctr0, chainIncrement2 );
+    ctr3 = vaddq_u32( ctr1, chainIncrement2 );
+    ctr4 = vaddq_u32( ctr2, chainIncrement2 );
+    ctr5 = vaddq_u32( ctr3, chainIncrement2 );
+    ctr6 = vaddq_u32( ctr4, chainIncrement2 );
+    ctr7 = vaddq_u32( ctr5, chainIncrement2 );
+
+    state = *(__n128 *) pState;
+
+    todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PMULL_HPOWERS );
+
+    CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+
+    while( nBlocks >= 8 )
+    {
+        // In this loop we always have 8 blocks to decrypt and GHASH
+        c0 = vrev64q_u8( ctr0 );
+        c1 = vrev64q_u8( ctr1 );
+        c2 = vrev64q_u8( ctr2 );
+        c3 = vrev64q_u8( ctr3 );
+        c4 = vrev64q_u8( ctr4 );
+        c5 = vrev64q_u8( ctr5 );
+        c6 = vrev64q_u8( ctr6 );
+        c7 = vrev64q_u8( ctr7 );
+
+        ctr0 = vaddq_u32( ctr0, chainIncrement8 );
+        ctr1 = vaddq_u32( ctr1, chainIncrement8 );
+        ctr2 = vaddq_u32( ctr2, chainIncrement8 );
+        ctr3 = vaddq_u32( ctr3, chainIncrement8 );
+        ctr4 = vaddq_u32( ctr4, chainIncrement8 );
+        ctr5 = vaddq_u32( ctr5, chainIncrement8 );
+        ctr6 = vaddq_u32( ctr6, chainIncrement8 );
+        ctr7 = vaddq_u32( ctr7, chainIncrement8 );
+
+        AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pGhashSrc, 8, expandedKeyTable, todo, a0, a1, a2 );
+
+        pDst[0] = veorq_u64( pSrc[0], c0 );
+        pDst[1] = veorq_u64( pSrc[1], c1 );
+        pDst[2] = veorq_u64( pSrc[2], c2 );
+        pDst[3] = veorq_u64( pSrc[3], c3 );
+        pDst[4] = veorq_u64( pSrc[4], c4 );
+        pDst[5] = veorq_u64( pSrc[5], c5 );
+        pDst[6] = veorq_u64( pSrc[6], c6 );
+        pDst[7] = veorq_u64( pSrc[7], c7 );
+
+        pDst  += 8;
+        pSrc  += 8;
+        nBlocks -= 8;
+
+        if (todo == 0)
+        {
+            CLMUL_3_POST( a0, a1, a2 );
+            MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+            if ( nBlocks > 0 )
+            {
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PMULL_HPOWERS );
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+        }
+    }
+
+    if( nBlocks > 0 )
+    {
+        // We have 1-7 blocks to GHASH and decrypt
+        // Do the exact number of GHASH blocks we need in parallel with generating either 4 or 8 blocks of AES-CTR
+        c0 = vrev64q_u8( ctr0 );
+        c1 = vrev64q_u8( ctr1 );
+        c2 = vrev64q_u8( ctr2 );
+        c3 = vrev64q_u8( ctr3 );
+
+        if( nBlocks > 4 )
+        {
+            c4 = vrev64q_u8( ctr4 );
+            c5 = vrev64q_u8( ctr5 );
+            c6 = vrev64q_u8( ctr6 );
+
+            AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pGhashSrc, nBlocks, expandedKeyTable, todo, a0, a1, a2 );
+        } else {
+            AES_GCM_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3, pGhashSrc, nBlocks, expandedKeyTable, todo, a0, a1, a2 );
+        }
+        CLMUL_3_POST( a0, a1, a2 );
+        MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+        // Decrypt 1-7 blocks with pre-generated AES-CTR blocks
+        while( nBlocks >= 2 )
+        {
+            ctr0 = vaddq_u32( ctr0, chainIncrement2 );
+
+            pDst[0] = veorq_u64( pSrc[0], c0 );
+            pDst[1] = veorq_u64( pSrc[1], c1 );
+
+            pDst    += 2;
+            pSrc    += 2;
+            nBlocks -= 2;
+            c0 = c2;
+            c1 = c3;
+            c2 = c4;
+            c3 = c5;
+            c4 = c6;
+        }
+
+        if( nBlocks > 0 )
+        {
+            ctr0 = vaddq_u32( ctr0, chainIncrement1 );
+
+            pDst[0] = veorq_u64( pSrc[0], c0 );
+        }
+    }
+
+    chain = vrev64q_u8( ctr0 );
+    *(__n128 *)pbChainingValue = chain;
+    *(__n128 *)pState = state;
+}
+#pragma runtime_checks( "u", restore )
+#pragma warning(pop)
+#pragma clang attribute pop
+
+#endif
diff --git a/libs/symcrypt/lib/aes-pattern.c b/libs/symcrypt/lib/aes-pattern.c
new file mode 100644
index 000000000000..7fa78108d869
--- /dev/null
+++ b/libs/symcrypt/lib/aes-pattern.c
@@ -0,0 +1,348 @@
+//
+// aes-pattern.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This file contains "pattern" code for AES-related functions. It's not intended to be compiled
+// directly; rather it is included by other aes-*.c files which define the macros used here.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+#if SYMCRYPT_CPU_ARM64
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_AesCtrMsbXxNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __n128          chain = *(__n128 *)pbChainingValue;
+    const __n128 *  pSrc = (const __n128 *) pbSrc;
+    __n128 *        pDst = (__n128 *) pbDst;
+
+    const __n128 chainIncrement1 = SYMCRYPT_SET_N128_U64( 0, 1 );
+    const __n128 chainIncrement2 = SYMCRYPT_SET_N128_U64( 0, 2 );
+    const __n128 chainIncrement8 = SYMCRYPT_SET_N128_U64( 0, 8 );
+
+    __n128 ctr0, ctr1, ctr2, ctr3, ctr4, ctr5, ctr6, ctr7;
+    __n128 c0, c1, c2, c3, c4, c5, c6, c7;
+
+    cbData &= ~(SYMCRYPT_AES_BLOCK_SIZE - 1);
+
+    // Our chain variable is in integer format, not the MSBfirst format loaded from memory.
+    ctr0 = vrev64q_u8( chain );
+    ctr1 = VADDQ_UXX( ctr0, chainIncrement1 );
+    ctr2 = VADDQ_UXX( ctr0, chainIncrement2 );
+    ctr3 = VADDQ_UXX( ctr1, chainIncrement2 );
+    ctr4 = VADDQ_UXX( ctr2, chainIncrement2 );
+    ctr5 = VADDQ_UXX( ctr3, chainIncrement2 );
+    ctr6 = VADDQ_UXX( ctr4, chainIncrement2 );
+    ctr7 = VADDQ_UXX( ctr5, chainIncrement2 );
+
+/*
+    while cbData >= 5 * block
+        generate 8 blocks of key stream
+        if cbData < 8 * block
+            break;
+        process 8 blocks
+    if cbData >= 5 * block
+        process 5-7 blocks
+        done
+    if cbData >= 2 * block
+        generate 4 blocks of key stream
+        process 2-4 blocks
+        done
+    if cbData == 1 block
+        generate 1 block of key stream
+        process block
+*/
+    while( cbData >= 5 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = vrev64q_u8( ctr0 );
+        c1 = vrev64q_u8( ctr1 );
+        c2 = vrev64q_u8( ctr2 );
+        c3 = vrev64q_u8( ctr3 );
+        c4 = vrev64q_u8( ctr4 );
+        c5 = vrev64q_u8( ctr5 );
+        c6 = vrev64q_u8( ctr6 );
+        c7 = vrev64q_u8( ctr7 );
+
+        ctr0 = VADDQ_UXX( ctr0, chainIncrement8 );
+        ctr1 = VADDQ_UXX( ctr1, chainIncrement8 );
+        ctr2 = VADDQ_UXX( ctr2, chainIncrement8 );
+        ctr3 = VADDQ_UXX( ctr3, chainIncrement8 );
+        ctr4 = VADDQ_UXX( ctr4, chainIncrement8 );
+        ctr5 = VADDQ_UXX( ctr5, chainIncrement8 );
+        ctr6 = VADDQ_UXX( ctr6, chainIncrement8 );
+        ctr7 = VADDQ_UXX( ctr7, chainIncrement8 );
+
+        AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        if( cbData < 8 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            break;
+        }
+
+        pDst[0] = veorq_u64( pSrc[0], c0 );
+        pDst[1] = veorq_u64( pSrc[1], c1 );
+        pDst[2] = veorq_u64( pSrc[2], c2 );
+        pDst[3] = veorq_u64( pSrc[3], c3 );
+        pDst[4] = veorq_u64( pSrc[4], c4 );
+        pDst[5] = veorq_u64( pSrc[5], c5 );
+        pDst[6] = veorq_u64( pSrc[6], c6 );
+        pDst[7] = veorq_u64( pSrc[7], c7 );
+
+        pDst  += 8;
+        pSrc  += 8;
+        cbData -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    //
+    // At this point we have one of the two following cases:
+    // - cbData >= 5 * 16 and we have 8 blocks of key stream in c0-c7. ctr0-ctr7 is set to (c0+8)-(c7+8)
+    // - cbData < 5 * 16 and we have no blocks of key stream, and ctr0-ctr7 set to the next 8 counters to use
+    //
+
+    if( cbData >= SYMCRYPT_AES_BLOCK_SIZE ) // quick exit of function if the request was a multiple of 8 blocks
+    {
+        if( cbData >= 5 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            //
+            // We already have the key stream
+            //
+            pDst[0] = veorq_u64( pSrc[0], c0 );
+            pDst[1] = veorq_u64( pSrc[1], c1 );
+            pDst[2] = veorq_u64( pSrc[2], c2 );
+            pDst[3] = veorq_u64( pSrc[3], c3 );
+            pDst[4] = veorq_u64( pSrc[4], c4 );
+            chain = VSUBQ_UXX( ctr5, chainIncrement8 );
+
+            if( cbData >= 96 )
+            {
+                chain = VSUBQ_UXX( ctr6, chainIncrement8 );
+                pDst[5] = veorq_u64( pSrc[5], c5 );
+                if( cbData >= 112 )
+                {
+                    chain = VSUBQ_UXX( ctr7, chainIncrement8 );
+                    pDst[6] = veorq_u64( pSrc[6], c6 );
+                }
+            }
+        }
+        else if( cbData >= 2 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            // Produce 4 blocks of key stream
+
+            chain = ctr2;           // chain is only incremented by 2 for now
+
+            c0 = vrev64q_u8( ctr0 );
+            c1 = vrev64q_u8( ctr1 );
+            c2 = vrev64q_u8( ctr2 );
+            c3 = vrev64q_u8( ctr3 );
+
+            AES_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3 );
+
+            pDst[0] = veorq_u64( pSrc[0], c0 );
+            pDst[1] = veorq_u64( pSrc[1], c1 );
+            if( cbData >= 48 )
+            {
+                chain = ctr3;
+                pDst[2] = veorq_u64( pSrc[2], c2 );
+                if( cbData >= 64 )
+                {
+                    chain = ctr4;
+                    pDst[3] = veorq_u64( pSrc[3], c3 );
+                }
+            }
+        }
+        else
+        {
+            // Exactly 1 block to process
+            chain = ctr1;
+
+            c0 = vrev64q_u8( ctr0 );
+
+            AES_ENCRYPT_1( pExpandedKey, c0 );
+            pDst[0] = veorq_u64( pSrc[0], c0 );
+        }
+    }
+    else
+    {
+        chain = ctr0;
+    }
+
+    chain = vrev64q_u8( chain );
+    *(__n128 *)pbChainingValue = chain;
+}
+
+#endif // SYMCRYPT_CPU_ARM64
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_AesCtrMsbXxXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i chain = _mm_loadu_si128( (__m128i *) pbChainingValue );
+
+    __m128i BYTE_REVERSE_ORDER = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+
+    __m128i chainIncrement1 = _mm_set_epi32( 0, 0, 0, 1 );
+    __m128i chainIncrement2 = _mm_set_epi32( 0, 0, 0, 2 );
+    __m128i chainIncrement3 = _mm_set_epi32( 0, 0, 0, 3 );
+    //__m128i chainIncrement8 = _mm_set_epi32( 0, 0, 0, 8 );
+
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+
+    cbData &= ~(SYMCRYPT_AES_BLOCK_SIZE - 1);
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER );
+
+/*
+    while cbData >= 5 * block
+        generate 8 blocks of key stream
+        if cbData < 8 * block
+            break;
+        process 8 blocks
+    if cbData >= 5 * block
+        process 5-7 blocks
+        done
+    if cbData > 1 block
+        generate 4 blocks of key stream
+        process 2-4 blocks
+        done
+    if cbData == 1 block
+        generate 1 block of key stream
+        process block
+*/
+    while( cbData >= 5 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = chain;
+        c1 = MM_ADD_EPIXX( chain, chainIncrement1 );
+        c2 = MM_ADD_EPIXX( chain, chainIncrement2 );
+        c3 = MM_ADD_EPIXX( c1, chainIncrement2 );
+        c4 = MM_ADD_EPIXX( c2, chainIncrement2 );
+        c5 = MM_ADD_EPIXX( c3, chainIncrement2 );
+        c6 = MM_ADD_EPIXX( c4, chainIncrement2 );
+        c7 = MM_ADD_EPIXX( c5, chainIncrement2 );
+        chain = MM_ADD_EPIXX( c6, chainIncrement2 );
+
+        c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+        c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+        c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+        c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+        c4 = _mm_shuffle_epi8( c4, BYTE_REVERSE_ORDER );
+        c5 = _mm_shuffle_epi8( c5, BYTE_REVERSE_ORDER );
+        c6 = _mm_shuffle_epi8( c6, BYTE_REVERSE_ORDER );
+        c7 = _mm_shuffle_epi8( c7, BYTE_REVERSE_ORDER );
+
+        AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        if( cbData < 8 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            break;
+        }
+
+        _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 32), _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc + 32 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 48), _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc + 48 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 64), _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc + 64 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 80), _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc + 80 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 96), _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc + 96 ) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +112), _mm_xor_si128( c7, _mm_loadu_si128( ( __m128i * ) (pbSrc +112 ) ) ) );
+        pbDst  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbSrc  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    //
+    // At this point we have one of the two following cases:
+    // - cbData >= 5 * 16 and we have 8 blocks of key stream in c0-c7. chain is set to c7 + 1
+    // - cbData < 5 * 16 and we have no blocks of key stream, with chain the next value to use
+    //
+
+    if( cbData >= SYMCRYPT_AES_BLOCK_SIZE ) // quick exit of function if the request was a multiple of 8 blocks
+    {
+        if( cbData >= 5 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            //
+            // We already have the key stream
+            //
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0 ) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16 ) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 32), _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc + 32 ) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 48), _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc + 48 ) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 64), _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc + 64 ) ) ) );
+            chain = MM_SUB_EPIXX( chain, chainIncrement3 );
+
+            if( cbData >= 96 )
+            {
+                chain = MM_ADD_EPIXX( chain, chainIncrement1 );
+                _mm_storeu_si128( (__m128i *) (pbDst + 80), _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc + 80 ) ) ) );
+                if( cbData >= 112 )
+                {
+                    chain = MM_ADD_EPIXX( chain, chainIncrement1 );
+                    _mm_storeu_si128( (__m128i *) (pbDst + 96), _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc + 96 ) ) ) );
+                }
+            }
+        }
+        else if( cbData >= 2 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            // Produce 4 blocks of key stream
+
+            c0 = chain;
+            c1 = MM_ADD_EPIXX( chain, chainIncrement1 );
+            c2 = MM_ADD_EPIXX( chain, chainIncrement2 );
+            c3 = MM_ADD_EPIXX( c1, chainIncrement2 );
+            chain = c2;             // chain is only incremented by 2 for now
+
+            c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+            c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+            c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+            c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+
+            AES_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3 );
+
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0 ) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16 ) ) ) );
+            if( cbData >= 48 )
+            {
+                chain = MM_ADD_EPIXX( chain, chainIncrement1 );
+                _mm_storeu_si128( (__m128i *) (pbDst + 32), _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc + 32 ) ) ) );
+                if( cbData >= 64 )
+                {
+                    chain = MM_ADD_EPIXX( chain, chainIncrement1 );
+                    _mm_storeu_si128( (__m128i *) (pbDst + 48), _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc + 48 ) ) ) );
+                }
+            }
+        }
+        else
+        {
+            // Exactly 1 block to process
+            c0 = chain;
+            chain = MM_ADD_EPIXX( chain, chainIncrement1 );
+
+            c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+
+            AES_ENCRYPT_1( pExpandedKey, c0 );
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0 ) ) ) );
+        }
+    }
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER );
+    _mm_storeu_si128( (__m128i *) pbChainingValue, chain );
+}
+
+#endif // SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
diff --git a/libs/symcrypt/lib/aes-xmm.c b/libs/symcrypt/lib/aes-xmm.c
new file mode 100644
index 000000000000..c300533281f7
--- /dev/null
+++ b/libs/symcrypt/lib/aes-xmm.c
@@ -0,0 +1,1792 @@
+//
+// aes-xmm.c   code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// All XMM code for AES operations
+// Requires compiler support for ssse3, aesni and pclmulqdq
+//
+
+#include "precomp.h"
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#include "xtsaes_definitions.h"
+#include "ghash_definitions.h"
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("ssse3,aes,pclmul"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("ssse3,aes,pclmul")
+#endif
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4SboxXmm( _In_reads_(4) PCBYTE pIn, _Out_writes_(4) PBYTE pOut )
+{
+    __m128i x;
+    x = _mm_set1_epi32( *(int *) pIn );
+
+    x = _mm_aeskeygenassist_si128( x, 0 );
+
+    // Could use _mm_storeu_si32( pOut, x ) but it is missing from some headers and _mm_store_ss will be as fast
+    _mm_store_ss( (float *) pOut, _mm_castsi128_ps(x) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKeyXmm(
+    _In_reads_(16)      PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey )
+{
+    //
+    // On x86 our key structure is only 4-aligned (the best we can do) so we use unaligned load/stores.
+    // On Amd64 our round keys are aligned, but recent CPUs have fast unaligned load/store if the address is
+    // actually aligned properly.
+    //
+    _mm_storeu_si128( (__m128i *) pDecryptionRoundKey, _mm_aesimc_si128( _mm_loadu_si128( (__m128i *)pEncryptionRoundKey ) ) );
+}
+
+//
+// The latency of AES instruction has increased up to 8 cycles in Ivy Bridge,
+// and back to 7 in Haswell.
+// We use 8-parallel code to expose the maximum parallelism to the CPU.
+// On x86 it will introduce some register spilling, but the load/stores
+// should be able to hide behind the AES instruction latencies.
+// Silvermont x86 CPUs has AES-NI with latency = 8 and throughput = 5, so there
+// the CPU parallelism is low.
+// For things like BitLocker that is fine, but other uses, such as GCM & AES_CTR_DRBG
+// use odd sizes.
+// We try to do 5-8 blocks in 8-parallel code, 2-4 blocks in 4-parallel code, and
+// 1 block in 1-parallel code.
+// This is a compromise; the big cores can do 8 parallel in about the time of a 4-parallel,
+// but Silvermont cannot and would pay a big price on small requests if we only use 8-parallel.
+// Doing only 8-parallel and then 1-parallel would penalize the big cores a lot.
+//
+// We used to have 7-parallel code, but common request sizes are not multiples of 7
+// blocks so we end up doing a lot of extra work. This is especially expensive on
+// Silvermont where the extra work isn't hidden in the latencies.
+//
+
+#define AES_ENCRYPT_1( pExpandedKey, c0 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = &pExpandedKey->RoundKey[0]; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    c0 = _mm_xor_si128( c0, roundkey ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+    c0 = _mm_aesenc_si128( c0, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesenc_si128( c0, roundkey ); \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesenc_si128( c0, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesenclast_si128( c0, roundkey ); \
+};
+
+
+// Perform AES encryption without the first round key and with a specified last round key
+//
+// For algorithms where performance is dominated by a chain of dependent AES rounds (i.e. CBC encryption, CCM, CMAC)
+// we can gain a reasonable performance uplift by computing (last round key ^ next plaintext block ^ first round key)
+// off the critical path and using this computed value in place of last round key in AESENCLAST instructions.
+#define AES_ENCRYPT_1_CHAIN( pExpandedKey, cipherState, mergedLastRoundKey ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = &pExpandedKey->RoundKey[1]; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    cipherState = _mm_aesenc_si128( cipherState, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        cipherState = _mm_aesenc_si128( cipherState, roundkey ); \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        cipherState = _mm_aesenc_si128( cipherState, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    cipherState = _mm_aesenclast_si128( cipherState, mergedLastRoundKey ); \
+};
+
+#define AES_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = &pExpandedKey->RoundKey[0]; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    c0 = _mm_xor_si128( c0, roundkey ); \
+    c1 = _mm_xor_si128( c1, roundkey ); \
+    c2 = _mm_xor_si128( c2, roundkey ); \
+    c3 = _mm_xor_si128( c3, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesenc_si128( c0, roundkey ); \
+        c1 = _mm_aesenc_si128( c1, roundkey ); \
+        c2 = _mm_aesenc_si128( c2, roundkey ); \
+        c3 = _mm_aesenc_si128( c3, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesenclast_si128( c0, roundkey ); \
+    c1 = _mm_aesenclast_si128( c1, roundkey ); \
+    c2 = _mm_aesenclast_si128( c2, roundkey ); \
+    c3 = _mm_aesenclast_si128( c3, roundkey ); \
+};
+
+#define AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = &pExpandedKey->RoundKey[0]; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    c0 = _mm_xor_si128( c0, roundkey ); \
+    c1 = _mm_xor_si128( c1, roundkey ); \
+    c2 = _mm_xor_si128( c2, roundkey ); \
+    c3 = _mm_xor_si128( c3, roundkey ); \
+    c4 = _mm_xor_si128( c4, roundkey ); \
+    c5 = _mm_xor_si128( c5, roundkey ); \
+    c6 = _mm_xor_si128( c6, roundkey ); \
+    c7 = _mm_xor_si128( c7, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesenc_si128( c0, roundkey ); \
+        c1 = _mm_aesenc_si128( c1, roundkey ); \
+        c2 = _mm_aesenc_si128( c2, roundkey ); \
+        c3 = _mm_aesenc_si128( c3, roundkey ); \
+        c4 = _mm_aesenc_si128( c4, roundkey ); \
+        c5 = _mm_aesenc_si128( c5, roundkey ); \
+        c6 = _mm_aesenc_si128( c6, roundkey ); \
+        c7 = _mm_aesenc_si128( c7, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesenclast_si128( c0, roundkey ); \
+    c1 = _mm_aesenclast_si128( c1, roundkey ); \
+    c2 = _mm_aesenclast_si128( c2, roundkey ); \
+    c3 = _mm_aesenclast_si128( c3, roundkey ); \
+    c4 = _mm_aesenclast_si128( c4, roundkey ); \
+    c5 = _mm_aesenclast_si128( c5, roundkey ); \
+    c6 = _mm_aesenclast_si128( c6, roundkey ); \
+    c7 = _mm_aesenclast_si128( c7, roundkey ); \
+};
+
+#define AES_DECRYPT_1( pExpandedKey, c0 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = pExpandedKey->lastEncRoundKey; \
+    keyLimit = pExpandedKey->lastDecRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    c0 = _mm_xor_si128( c0, roundkey ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+    c0 = _mm_aesdec_si128( c0, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesdec_si128( c0, roundkey ); \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesdec_si128( c0, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesdeclast_si128( c0, roundkey ); \
+};
+
+#define AES_DECRYPT_4( pExpandedKey, c0, c1, c2, c3 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = pExpandedKey->lastEncRoundKey; \
+    keyLimit = pExpandedKey->lastDecRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    c0 = _mm_xor_si128( c0, roundkey ); \
+    c1 = _mm_xor_si128( c1, roundkey ); \
+    c2 = _mm_xor_si128( c2, roundkey ); \
+    c3 = _mm_xor_si128( c3, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesdec_si128( c0, roundkey ); \
+        c1 = _mm_aesdec_si128( c1, roundkey ); \
+        c2 = _mm_aesdec_si128( c2, roundkey ); \
+        c3 = _mm_aesdec_si128( c3, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesdeclast_si128( c0, roundkey ); \
+    c1 = _mm_aesdeclast_si128( c1, roundkey ); \
+    c2 = _mm_aesdeclast_si128( c2, roundkey ); \
+    c3 = _mm_aesdeclast_si128( c3, roundkey ); \
+};
+
+#define AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+\
+    keyPtr = pExpandedKey->lastEncRoundKey; \
+    keyLimit = pExpandedKey->lastDecRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+\
+    c0 = _mm_xor_si128( c0, roundkey ); \
+    c1 = _mm_xor_si128( c1, roundkey ); \
+    c2 = _mm_xor_si128( c2, roundkey ); \
+    c3 = _mm_xor_si128( c3, roundkey ); \
+    c4 = _mm_xor_si128( c4, roundkey ); \
+    c5 = _mm_xor_si128( c5, roundkey ); \
+    c6 = _mm_xor_si128( c6, roundkey ); \
+    c7 = _mm_xor_si128( c7, roundkey ); \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesdec_si128( c0, roundkey ); \
+        c1 = _mm_aesdec_si128( c1, roundkey ); \
+        c2 = _mm_aesdec_si128( c2, roundkey ); \
+        c3 = _mm_aesdec_si128( c3, roundkey ); \
+        c4 = _mm_aesdec_si128( c4, roundkey ); \
+        c5 = _mm_aesdec_si128( c5, roundkey ); \
+        c6 = _mm_aesdec_si128( c6, roundkey ); \
+        c7 = _mm_aesdec_si128( c7, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesdeclast_si128( c0, roundkey ); \
+    c1 = _mm_aesdeclast_si128( c1, roundkey ); \
+    c2 = _mm_aesdeclast_si128( c2, roundkey ); \
+    c3 = _mm_aesdeclast_si128( c3, roundkey ); \
+    c4 = _mm_aesdeclast_si128( c4, roundkey ); \
+    c5 = _mm_aesdeclast_si128( c5, roundkey ); \
+    c6 = _mm_aesdeclast_si128( c6, roundkey ); \
+    c7 = _mm_aesdeclast_si128( c7, roundkey ); \
+};
+
+
+//
+// The EncryptXmm code is tested through the CFB mode encryption which has no further optimizations.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    __m128i c;
+
+    c = _mm_loadu_si128( ( __m128i * ) pbSrc);
+
+    AES_ENCRYPT_1( pExpandedKey, c );
+
+    _mm_storeu_si128( (__m128i *) pbDst, c );
+}
+
+//
+// The DecryptXmm code is tested through the EcbDecrypt calls which has no further optimizations.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    __m128i c;
+
+    c = _mm_loadu_si128( ( __m128i * ) pbSrc);
+
+    AES_DECRYPT_1( pExpandedKey, c );
+
+    _mm_storeu_si128( (__m128i *) pbDst, c );
+}
+
+// Disable warnings and VC++ runtime checks for use of uninitialized values (by design)
+#pragma warning(push)
+#pragma warning( disable: 6001 4701 )
+#pragma runtime_checks( "u", off )
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+
+    while( cbData >= 8 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = _mm_loadu_si128( ( __m128i * ) (pbSrc +  0 ));
+        c1 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 16 ));
+        c2 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 32 ));
+        c3 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 48 ));
+        c4 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 64 ));
+        c5 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 80 ));
+        c6 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 96 ));
+        c7 = _mm_loadu_si128( ( __m128i * ) (pbSrc +112 ));
+
+        AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        _mm_storeu_si128( (__m128i *) (pbDst +  0 ), c0 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 16 ), c1 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 32 ), c2 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 48 ), c3 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 64 ), c4 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 80 ), c5 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 96 ), c6 );
+        _mm_storeu_si128( (__m128i *) (pbDst +112 ), c7 );
+
+        pbSrc   += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst   += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        cbData  -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbData < 16 )
+    {
+        return;
+    }
+
+    c0 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 0 ));
+    if( cbData >= 32 )
+    {
+    c1 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 16 ));
+        if( cbData >= 48 )
+        {
+    c2 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 32 ));
+            if( cbData >= 64 )
+            {
+    c3 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 48 ));
+                if( cbData >= 80 )
+                {
+    c4 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 64 ));
+                    if( cbData >= 96 )
+                    {
+    c5 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 80 ));
+                        if( cbData >= 112 )
+                        {
+    c6 = _mm_loadu_si128( ( __m128i * ) (pbSrc + 96 ));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if( cbData >= 5 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+    }
+    else if( cbData >= 2 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        AES_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3 );
+    }
+    else
+    {
+        AES_ENCRYPT_1( pExpandedKey, c0 );
+    }
+
+    _mm_storeu_si128( (__m128i *) (pbDst + 0  ), c0 );
+    if( cbData >= 32 )
+    {
+    _mm_storeu_si128( (__m128i *) (pbDst + 16 ), c1 );
+        if( cbData >= 48 )
+        {
+    _mm_storeu_si128( (__m128i *) (pbDst + 32 ), c2 );
+            if( cbData >= 64 )
+            {
+    _mm_storeu_si128( (__m128i *) (pbDst + 48 ), c3 );
+                if( cbData >= 80 )
+                {
+    _mm_storeu_si128( (__m128i *) (pbDst + 64 ), c4 );
+                    if( cbData >= 96 )
+                    {
+    _mm_storeu_si128( (__m128i *) (pbDst + 80 ), c5 );
+                        if( cbData >= 112 )
+                        {
+    _mm_storeu_si128( (__m128i *) (pbDst + 96 ), c6 );
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+#pragma runtime_checks( "u", restore )
+#pragma warning( pop )
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncryptXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i c = _mm_loadu_si128( (__m128i *) pbChainingValue );
+    __m128i rk0 = _mm_loadu_si128( (__m128i *) &pExpandedKey->RoundKey[0] );
+    __m128i rkLast = _mm_loadu_si128( (__m128i *) pExpandedKey->lastEncRoundKey );
+    __m128i d;
+
+    if (cbData < SYMCRYPT_AES_BLOCK_SIZE)
+        return;
+
+    // This algorithm is dominated by chain of dependent AES rounds, so we want to avoid XOR
+    // instructions on the critical path where possible
+    // We can compute (last round key ^ next plaintext block ^ first round key) off the critical
+    // path and use this with AES_ENCRYPT_1_CHAIN so that only AES instructions write to c in
+    // the main loop
+    d = _mm_xor_si128( _mm_loadu_si128( (__m128i *) pbSrc ), rk0 );
+    c = _mm_xor_si128( c, d );
+    pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+    cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        d = _mm_xor_si128( _mm_loadu_si128( (__m128i *) pbSrc ), rk0 );
+        AES_ENCRYPT_1_CHAIN( pExpandedKey, c, _mm_xor_si128(d, rkLast ) );
+        _mm_storeu_si128( (__m128i *) pbDst, _mm_xor_si128(c, d) );
+
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+    AES_ENCRYPT_1_CHAIN( pExpandedKey, c, rkLast );
+    _mm_storeu_si128( (__m128i *) pbDst, c );
+    _mm_storeu_si128( (__m128i *) pbChainingValue, c );
+}
+
+// Disable warnings and VC++ runtime checks for use of uninitialized values (by design)
+#pragma warning(push)
+#pragma warning( disable: 6001 4701 )
+#pragma runtime_checks( "u", off )
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecryptXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i chain;
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m128i d0, d1, d2, d3, d4, d5, d6, d7;
+
+    if( cbData < SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        return;
+    }
+
+    chain = _mm_loadu_si128( (__m128i *) pbChainingValue );
+
+    //
+    // First we do all multiples of 8 blocks
+    //
+
+    while( cbData >= 8 * SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        d0 = c0 = _mm_loadu_si128( (__m128i *) (pbSrc + 0 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d1 = c1 = _mm_loadu_si128( (__m128i *) (pbSrc + 1 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d2 = c2 = _mm_loadu_si128( (__m128i *) (pbSrc + 2 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d3 = c3 = _mm_loadu_si128( (__m128i *) (pbSrc + 3 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d4 = c4 = _mm_loadu_si128( (__m128i *) (pbSrc + 4 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d5 = c5 = _mm_loadu_si128( (__m128i *) (pbSrc + 5 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d6 = c6 = _mm_loadu_si128( (__m128i *) (pbSrc + 6 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        d7 = c7 = _mm_loadu_si128( (__m128i *) (pbSrc + 7 * SYMCRYPT_AES_BLOCK_SIZE ) );
+
+        AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        c0 = _mm_xor_si128( c0, chain );
+        c1 = _mm_xor_si128( c1, d0 );
+        c2 = _mm_xor_si128( c2, d1 );
+        c3 = _mm_xor_si128( c3, d2 );
+        c4 = _mm_xor_si128( c4, d3 );
+        c5 = _mm_xor_si128( c5, d4 );
+        c6 = _mm_xor_si128( c6, d5 );
+        c7 = _mm_xor_si128( c7, d6 );
+        chain = d7;
+
+        _mm_storeu_si128( (__m128i *) (pbDst + 0 * SYMCRYPT_AES_BLOCK_SIZE ), c0 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 1 * SYMCRYPT_AES_BLOCK_SIZE ), c1 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 2 * SYMCRYPT_AES_BLOCK_SIZE ), c2 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 3 * SYMCRYPT_AES_BLOCK_SIZE ), c3 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 4 * SYMCRYPT_AES_BLOCK_SIZE ), c4 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 5 * SYMCRYPT_AES_BLOCK_SIZE ), c5 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 6 * SYMCRYPT_AES_BLOCK_SIZE ), c6 );
+        _mm_storeu_si128( (__m128i *) (pbDst + 7 * SYMCRYPT_AES_BLOCK_SIZE ), c7 );
+
+        pbSrc  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbData >= 16 )
+    {
+        //
+        // There is remaining work to be done
+        //
+        d0 = c0 = _mm_loadu_si128( (__m128i *) (pbSrc + 0 * SYMCRYPT_AES_BLOCK_SIZE ) );
+        if( cbData >= 32 )
+        {
+        d1 = c1 = _mm_loadu_si128( (__m128i *) (pbSrc + 1 * SYMCRYPT_AES_BLOCK_SIZE ) );
+            if( cbData >= 48 )
+            {
+        d2 = c2 = _mm_loadu_si128( (__m128i *) (pbSrc + 2 * SYMCRYPT_AES_BLOCK_SIZE ) );
+                if( cbData >= 64 )
+                {
+        d3 = c3 = _mm_loadu_si128( (__m128i *) (pbSrc + 3 * SYMCRYPT_AES_BLOCK_SIZE ) );
+                    if( cbData >= 80 )
+                    {
+        d4 = c4 = _mm_loadu_si128( (__m128i *) (pbSrc + 4 * SYMCRYPT_AES_BLOCK_SIZE ) );
+                        if( cbData >= 96 )
+                        {
+        d5 = c5 = _mm_loadu_si128( (__m128i *) (pbSrc + 5 * SYMCRYPT_AES_BLOCK_SIZE ) );
+                            if( cbData >= 112 )
+                            {
+        d6 = c6 = _mm_loadu_si128( (__m128i *) (pbSrc + 6 * SYMCRYPT_AES_BLOCK_SIZE ) );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        //
+        // Decrypt 1, 4, or 8 blocks in AES-CBC mode. This might decrypt uninitialized registers,
+        // but those will not be used when we store the results.
+        //
+        if( cbData > 4 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            AES_DECRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+            c0 = _mm_xor_si128( c0, chain );
+            c1 = _mm_xor_si128( c1, d0 );
+            c2 = _mm_xor_si128( c2, d1 );
+            c3 = _mm_xor_si128( c3, d2 );
+            c4 = _mm_xor_si128( c4, d3 );
+            c5 = _mm_xor_si128( c5, d4 );
+            c6 = _mm_xor_si128( c6, d5 );
+        }
+        else if( cbData > SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            AES_DECRYPT_4( pExpandedKey, c0, c1, c2, c3 );
+            c0 = _mm_xor_si128( c0, chain );
+            c1 = _mm_xor_si128( c1, d0 );
+            c2 = _mm_xor_si128( c2, d1 );
+            c3 = _mm_xor_si128( c3, d2 );
+        } else
+        {
+            AES_DECRYPT_1( pExpandedKey, c0 );
+            c0 = _mm_xor_si128( c0, chain );
+        }
+
+        chain = _mm_loadu_si128( (__m128i *) (pbSrc + cbData - SYMCRYPT_AES_BLOCK_SIZE ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 0 * SYMCRYPT_AES_BLOCK_SIZE ), c0 );
+        if( cbData >= 32 )
+        {
+        _mm_storeu_si128( (__m128i *) (pbDst + 1 * SYMCRYPT_AES_BLOCK_SIZE ), c1 );
+            if( cbData >= 48 )
+            {
+        _mm_storeu_si128( (__m128i *) (pbDst + 2 * SYMCRYPT_AES_BLOCK_SIZE ), c2 );
+                if( cbData >= 64 )
+                {
+        _mm_storeu_si128( (__m128i *) (pbDst + 3 * SYMCRYPT_AES_BLOCK_SIZE ), c3 );
+                    if( cbData >= 80 )
+                    {
+        _mm_storeu_si128( (__m128i *) (pbDst + 4 * SYMCRYPT_AES_BLOCK_SIZE ), c4 );
+                        if( cbData >= 96 )
+                        {
+        _mm_storeu_si128( (__m128i *) (pbDst + 5 * SYMCRYPT_AES_BLOCK_SIZE ), c5 );
+                            if( cbData >= 112 )
+                            {
+        _mm_storeu_si128( (__m128i *) (pbDst + 6 * SYMCRYPT_AES_BLOCK_SIZE ), c6 );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    _mm_storeu_si128( (__m128i *) pbChainingValue, chain );
+
+    return;
+}
+#pragma runtime_checks( "u", restore )
+#pragma warning( pop )
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcMacXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData )
+{
+    __m128i c = _mm_loadu_si128( (__m128i *) pbChainingValue );
+    __m128i rk0 = _mm_loadu_si128( (__m128i *) &pExpandedKey->RoundKey[0] );
+    __m128i rkLast = _mm_loadu_si128( (__m128i *) pExpandedKey->lastEncRoundKey );
+    __m128i d, rk0AndLast;
+
+    if (cbData < SYMCRYPT_AES_BLOCK_SIZE)
+        return;
+
+    // This algorithm is dominated by chain of dependent AES rounds, so we want to avoid XOR
+    // instructions on the critical path where possible
+    // We can compute (last round key ^ next plaintext block ^ first round key) off the critical
+    // path and use this with AES_ENCRYPT_1_CHAIN so that only AES instructions write to c in
+    // the main loop
+    d = _mm_xor_si128( _mm_loadu_si128( (__m128i *) pbData ), rk0 );
+    c = _mm_xor_si128( c, d );
+    pbData += SYMCRYPT_AES_BLOCK_SIZE;
+    cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+
+    // As we don't compute ciphertext here, we only need to XOR rk0 and rkLast once
+    rk0AndLast = _mm_xor_si128( rk0, rkLast );
+
+    while( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        d = _mm_xor_si128( _mm_loadu_si128( (__m128i *) pbData ), rk0AndLast );
+        AES_ENCRYPT_1_CHAIN( pExpandedKey, c, d );
+
+        pbData += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+    AES_ENCRYPT_1_CHAIN( pExpandedKey, c, rkLast );
+    _mm_storeu_si128( (__m128i *) pbChainingValue, c );
+}
+
+
+#pragma warning(push)
+#pragma warning( disable:4701 ) // "Use of uninitialized variable"
+#pragma runtime_checks( "u", off )
+
+#define SYMCRYPT_AesCtrMsbXxXmm     SymCryptAesCtrMsb64Xmm
+#define MM_ADD_EPIXX               _mm_add_epi64
+#define MM_SUB_EPIXX               _mm_sub_epi64
+
+#include "aes-pattern.c"
+
+#undef MM_SUB_EPIXX
+#undef MM_ADD_EPIXX
+#undef SYMCRYPT_AesCtrMsbXxXmm
+
+#define SYMCRYPT_AesCtrMsbXxXmm     SymCryptAesCtrMsb32Xmm
+#define MM_ADD_EPIXX               _mm_add_epi32
+#define MM_SUB_EPIXX               _mm_sub_epi32
+
+#include "aes-pattern.c"
+
+#undef MM_SUB_EPIXX
+#undef MM_ADD_EPIXX
+#undef SYMCRYPT_AesCtrMsbXxXmm
+
+#pragma runtime_checks( "u", restore )
+#pragma warning(pop)
+
+/*
+    if( cbData >= 16 )
+    {
+        if( cbData >= 32 )
+        {
+            if( cbData >= 48 )
+            {
+                if( cbData >= 64 )
+                {
+                    if( cbData >= 80 )
+                    {
+                        if( cbData >= 96 )
+                        {
+                            if( cbData >= 112 )
+                            {
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+*/
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )       PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i t0;
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m128i roundkey, firstRoundKey, lastRoundKey;
+    __m128i XTS_ALPHA_MASK = _mm_set_epi32( 1, 1, 1, 0x87 );
+    SYMCRYPT_GF128_ELEMENT* tweakBuffer = (SYMCRYPT_GF128_ELEMENT*) pbScratch;
+
+    const BYTE (*keyPtr)[4][4];
+    const BYTE (*keyLimit)[4][4] = pExpandedKey->lastEncRoundKey;
+    UINT64 lastTweakLow, lastTweakHigh;
+    int aesEncryptXtsLoop;
+
+    SIZE_T cbDataMain;  // number of bytes to handle in the main loop
+    SIZE_T cbDataTail;  // number of bytes to handle in the tail loop
+
+    SYMCRYPT_ASSERT(cbData >= SYMCRYPT_AES_BLOCK_SIZE);
+
+    // To simplify logic and unusual size processing, we handle all
+    // data not a multiple of 8 blocks in the tail loop
+    cbDataTail = cbData & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1);
+    // Additionally, so that ciphertext stealing logic does not rely on
+    // reading back from the destination buffer, when we have a non-zero
+    // tail, we ensure that we handle at least 1 whole block in the tail
+    //
+    // Note that our caller has ensured we have at least 1 whole block
+    // to process, this is checked in debug build
+    // This means that cbDataTail is in [1,15] at this point iff there are
+    // at least 8 whole blocks to process; so the below does not cause
+    // cbDataTail or cbDataMain to exceed cbData
+    cbDataTail += ((cbDataTail > 0) && (cbDataTail < SYMCRYPT_AES_BLOCK_SIZE)) ? (8*SYMCRYPT_AES_BLOCK_SIZE) : 0;
+    cbDataMain = cbData - cbDataTail;
+
+    SYMCRYPT_ASSERT(cbDataMain <= cbData);
+    SYMCRYPT_ASSERT(cbDataTail <= cbData);
+    SYMCRYPT_ASSERT((cbDataMain & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1)) == 0);
+
+    c0 = _mm_loadu_si128( (__m128i *) pbTweakBlock );
+    XTS_MUL_ALPHA( c0, c1 );
+    XTS_MUL_ALPHA( c1, c2 );
+    XTS_MUL_ALPHA( c2, c3 );
+
+    XTS_MUL_ALPHA4( c0, c4 );
+    XTS_MUL_ALPHA ( c4, c5 );
+    XTS_MUL_ALPHA ( c5, c6 );
+    XTS_MUL_ALPHA ( c6, c7 );
+
+    tweakBuffer[0].m128i = c0;
+    tweakBuffer[1].m128i = c1;
+    tweakBuffer[2].m128i = c2;
+    tweakBuffer[3].m128i = c3;
+    tweakBuffer[4].m128i = c4;
+    tweakBuffer[5].m128i = c5;
+    tweakBuffer[6].m128i = c6;
+    tweakBuffer[7].m128i = c7;
+    lastTweakLow  = tweakBuffer[7].ull[0];
+    lastTweakHigh = tweakBuffer[7].ull[1];
+
+    firstRoundKey = _mm_loadu_si128( (__m128i *) &pExpandedKey->RoundKey[0] );
+    lastRoundKey = _mm_loadu_si128( (__m128i *) pExpandedKey->lastEncRoundKey );
+
+    while( cbDataMain > 0 )
+    {
+        // At loop entry, tweakBuffer[0-7] are tweakValues for the next 8 blocks
+        c0 = _mm_xor_si128( tweakBuffer[0].m128i, firstRoundKey );
+        c1 = _mm_xor_si128( tweakBuffer[1].m128i, firstRoundKey );
+        c2 = _mm_xor_si128( tweakBuffer[2].m128i, firstRoundKey );
+        c3 = _mm_xor_si128( tweakBuffer[3].m128i, firstRoundKey );
+        c4 = _mm_xor_si128( tweakBuffer[4].m128i, firstRoundKey );
+        c5 = _mm_xor_si128( tweakBuffer[5].m128i, firstRoundKey );
+        c6 = _mm_xor_si128( tweakBuffer[6].m128i, firstRoundKey );
+        c7 = _mm_xor_si128( tweakBuffer[7].m128i, firstRoundKey );
+
+        c0 = _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +   0) ) );
+        c1 = _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc +  16) ) );
+        c2 = _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc +  32) ) );
+        c3 = _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc +  48) ) );
+        c4 = _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc +  64) ) );
+        c5 = _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc +  80) ) );
+        c6 = _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc +  96) ) );
+        c7 = _mm_xor_si128( c7, _mm_loadu_si128( ( __m128i * ) (pbSrc + 112) ) );
+
+        keyPtr = &pExpandedKey->RoundKey[1];
+
+        // Do 8 full rounds (AES-128|AES-192|AES-256) with stitched XTS (performed in scalar registers)
+        for( aesEncryptXtsLoop = 0; aesEncryptXtsLoop < 8; aesEncryptXtsLoop++ )
+        {
+            roundkey = _mm_loadu_si128( (__m128i *) keyPtr );
+            keyPtr ++;
+            c0 = _mm_aesenc_si128( c0, roundkey );
+            c1 = _mm_aesenc_si128( c1, roundkey );
+            c2 = _mm_aesenc_si128( c2, roundkey );
+            c3 = _mm_aesenc_si128( c3, roundkey );
+            c4 = _mm_aesenc_si128( c4, roundkey );
+            c5 = _mm_aesenc_si128( c5, roundkey );
+            c6 = _mm_aesenc_si128( c6, roundkey );
+            c7 = _mm_aesenc_si128( c7, roundkey );
+
+            // Prepare tweakBuffer[8-15] with tweak^lastRoundKey
+            tweakBuffer[ 8+aesEncryptXtsLoop ].m128i = _mm_xor_si128( tweakBuffer[ aesEncryptXtsLoop ].m128i, lastRoundKey );
+            // Prepare tweakBuffer[0-7] with tweaks for next 8 blocks
+            XTS_MUL_ALPHA_Scalar( lastTweakLow, lastTweakHigh );
+            tweakBuffer[ aesEncryptXtsLoop ].ull[0] = lastTweakLow;
+            tweakBuffer[ aesEncryptXtsLoop ].ull[1] = lastTweakHigh;
+        }
+
+        do
+        {
+            roundkey = _mm_loadu_si128( (__m128i *) keyPtr );
+            keyPtr ++;
+            c0 = _mm_aesenc_si128( c0, roundkey );
+            c1 = _mm_aesenc_si128( c1, roundkey );
+            c2 = _mm_aesenc_si128( c2, roundkey );
+            c3 = _mm_aesenc_si128( c3, roundkey );
+            c4 = _mm_aesenc_si128( c4, roundkey );
+            c5 = _mm_aesenc_si128( c5, roundkey );
+            c6 = _mm_aesenc_si128( c6, roundkey );
+            c7 = _mm_aesenc_si128( c7, roundkey );
+        } while( keyPtr < keyLimit );
+
+        _mm_storeu_si128( (__m128i *) (pbDst +   0), _mm_aesenclast_si128( c0, tweakBuffer[ 8].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  16), _mm_aesenclast_si128( c1, tweakBuffer[ 9].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  32), _mm_aesenclast_si128( c2, tweakBuffer[10].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  48), _mm_aesenclast_si128( c3, tweakBuffer[11].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  64), _mm_aesenclast_si128( c4, tweakBuffer[12].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  80), _mm_aesenclast_si128( c5, tweakBuffer[13].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  96), _mm_aesenclast_si128( c6, tweakBuffer[14].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 112), _mm_aesenclast_si128( c7, tweakBuffer[15].m128i ) );
+
+        pbSrc += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        cbDataMain -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail == 0 )
+    {
+        return; // <-- expected case; early return here
+    }
+
+    // Rare case, with data unit length not being multiple of 128 bytes, handle the tail one block at a time
+    t0 = tweakBuffer[0].m128i;
+
+    while( cbDataTail >= 2*SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = _mm_xor_si128( _mm_loadu_si128( ( __m128i * ) pbSrc ), t0 );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        AES_ENCRYPT_1( pExpandedKey, c0 );
+        _mm_storeu_si128( (__m128i *) pbDst, _mm_xor_si128( c0, t0 ) );
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        XTS_MUL_ALPHA( t0, t0 );
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Ciphertext stealing encryption
+        //
+        //                      +--------------+
+        //                      |              |
+        //                      |              V
+        // +-----------------+  |  +-----+-----------+
+        // |      P_m-1      |  |  | P_m |++++CP+++++|
+        // +-----------------+  |  +-----+-----------+
+        //          |           |           |
+        //       enc_m-1        |         enc_m
+        //          |           |           |
+        //          V           |           V
+        // +-----+-----------+  |  +-----------------+
+        // | C_m |++++CP+++++|--+  |      C_m-1      |
+        // +-----+-----------+     +-----------------+
+        //    |                   /
+        //    +----------------  /  --+
+        //                      /     |
+        //                      |     V
+        // +-----------------+  |  +-----+
+        // |      C_m-1      |<-+  | C_m |
+        // +-----------------+     +-----+
+
+        // Encrypt penultimate plaintext block into tweakBuffer[0]
+        c0 = _mm_xor_si128( _mm_loadu_si128( (__m128i *) pbSrc ), t0 );
+        AES_ENCRYPT_1( pExpandedKey, c0 );
+        tweakBuffer[0].m128i = _mm_xor_si128( c0, t0 );
+
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+
+        // Copy tweakBuffer[0] to tweakBuffer[1]
+        tweakBuffer[1].m128i = tweakBuffer[0].m128i;
+        // Copy final plaintext bytes to prefix of tweakBuffer[0] - we must read before writing to support in-place encryption
+        memcpy( &tweakBuffer[0].ul[0], pbSrc + SYMCRYPT_AES_BLOCK_SIZE, cbDataTail );
+        // Copy prefix of tweakBuffer[1] to the right place in the destination buffer
+        memcpy( pbDst + SYMCRYPT_AES_BLOCK_SIZE, &tweakBuffer[1].ul[0], cbDataTail );
+
+        // Do final tweak update
+        XTS_MUL_ALPHA( t0, t0 );
+
+        // Load updated tweakBuffer[0] into c0
+        c0 = tweakBuffer[0].m128i;
+    } else {
+        // Just load final plaintext block into c0
+        c0 = _mm_loadu_si128( (__m128i*) pbSrc );
+    }
+
+    // Final full block encryption
+    c0 = _mm_xor_si128( c0, t0 );
+    AES_ENCRYPT_1( pExpandedKey, c0 );
+    _mm_storeu_si128( (__m128i *) pbDst, _mm_xor_si128( c0, t0 ) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )       PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i t0;
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m128i roundkey, firstRoundKey, lastRoundKey;
+    __m128i XTS_ALPHA_MASK = _mm_set_epi32( 1, 1, 1, 0x87 );
+    SYMCRYPT_GF128_ELEMENT* tweakBuffer = (SYMCRYPT_GF128_ELEMENT*) pbScratch;
+
+    const BYTE (*keyPtr)[4][4];
+    const BYTE (*keyLimit)[4][4] = pExpandedKey->lastDecRoundKey;
+    UINT64 lastTweakLow, lastTweakHigh;
+    int aesDecryptXtsLoop;
+
+    SIZE_T cbDataMain;  // number of bytes to handle in the main loop
+    SIZE_T cbDataTail;  // number of bytes to handle in the tail loop
+
+    SYMCRYPT_ASSERT(cbData >= SYMCRYPT_AES_BLOCK_SIZE);
+
+    // To simplify logic and unusual size processing, we handle all
+    // data not a multiple of 8 blocks in the tail loop
+    cbDataTail = cbData & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1);
+    // Additionally, so that ciphertext stealing logic does not rely on
+    // reading back from the destination buffer, when we have a non-zero
+    // tail, we ensure that we handle at least 1 whole block in the tail
+    //
+    // Note that our caller has ensured we have at least 1 whole block
+    // to process, this is checked in debug build
+    // This means that cbDataTail is in [1,15] at this point iff there are
+    // at least 8 whole blocks to process; so the below does not cause
+    // cbDataTail or cbDataMain to exceed cbData
+    cbDataTail += ((cbDataTail > 0) && (cbDataTail < SYMCRYPT_AES_BLOCK_SIZE)) ? (8*SYMCRYPT_AES_BLOCK_SIZE) : 0;
+    cbDataMain = cbData - cbDataTail;
+
+    SYMCRYPT_ASSERT(cbDataMain <= cbData);
+    SYMCRYPT_ASSERT(cbDataTail <= cbData);
+    SYMCRYPT_ASSERT((cbDataMain & ((8*SYMCRYPT_AES_BLOCK_SIZE)-1)) == 0);
+
+    c0 = _mm_loadu_si128( (__m128i *) pbTweakBlock );
+    XTS_MUL_ALPHA( c0, c1 );
+    XTS_MUL_ALPHA( c1, c2 );
+    XTS_MUL_ALPHA( c2, c3 );
+
+    XTS_MUL_ALPHA4( c0, c4 );
+    XTS_MUL_ALPHA ( c4, c5 );
+    XTS_MUL_ALPHA ( c5, c6 );
+    XTS_MUL_ALPHA ( c6, c7 );
+
+    tweakBuffer[0].m128i = c0;
+    tweakBuffer[1].m128i = c1;
+    tweakBuffer[2].m128i = c2;
+    tweakBuffer[3].m128i = c3;
+    tweakBuffer[4].m128i = c4;
+    tweakBuffer[5].m128i = c5;
+    tweakBuffer[6].m128i = c6;
+    tweakBuffer[7].m128i = c7;
+    lastTweakLow  = tweakBuffer[7].ull[0];
+    lastTweakHigh = tweakBuffer[7].ull[1];
+
+    firstRoundKey = _mm_loadu_si128( (__m128i *) pExpandedKey->lastEncRoundKey );
+    lastRoundKey = _mm_loadu_si128( (__m128i *) pExpandedKey->lastDecRoundKey );
+
+    while( cbDataMain > 0 )
+    {
+        // At loop entry, tweakBuffer[0-7] are tweakValues for the next 8 blocks
+        c0 = _mm_xor_si128( tweakBuffer[0].m128i, firstRoundKey );
+        c1 = _mm_xor_si128( tweakBuffer[1].m128i, firstRoundKey );
+        c2 = _mm_xor_si128( tweakBuffer[2].m128i, firstRoundKey );
+        c3 = _mm_xor_si128( tweakBuffer[3].m128i, firstRoundKey );
+        c4 = _mm_xor_si128( tweakBuffer[4].m128i, firstRoundKey );
+        c5 = _mm_xor_si128( tweakBuffer[5].m128i, firstRoundKey );
+        c6 = _mm_xor_si128( tweakBuffer[6].m128i, firstRoundKey );
+        c7 = _mm_xor_si128( tweakBuffer[7].m128i, firstRoundKey );
+
+        c0 = _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +   0) ) );
+        c1 = _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc +  16) ) );
+        c2 = _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc +  32) ) );
+        c3 = _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc +  48) ) );
+        c4 = _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc +  64) ) );
+        c5 = _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc +  80) ) );
+        c6 = _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc +  96) ) );
+        c7 = _mm_xor_si128( c7, _mm_loadu_si128( ( __m128i * ) (pbSrc + 112) ) );
+
+        keyPtr = pExpandedKey->lastEncRoundKey + 1;
+
+        // Do 8 full rounds (AES-128|AES-192|AES-256) with stitched XTS (performed in scalar registers)
+        for( aesDecryptXtsLoop = 0; aesDecryptXtsLoop < 8; aesDecryptXtsLoop++ )
+        {
+            roundkey = _mm_loadu_si128( (__m128i *) keyPtr );
+            keyPtr ++;
+            c0 = _mm_aesdec_si128( c0, roundkey );
+            c1 = _mm_aesdec_si128( c1, roundkey );
+            c2 = _mm_aesdec_si128( c2, roundkey );
+            c3 = _mm_aesdec_si128( c3, roundkey );
+            c4 = _mm_aesdec_si128( c4, roundkey );
+            c5 = _mm_aesdec_si128( c5, roundkey );
+            c6 = _mm_aesdec_si128( c6, roundkey );
+            c7 = _mm_aesdec_si128( c7, roundkey );
+
+            // Prepare tweakBuffer[8-15] with tweak^lastRoundKey
+            tweakBuffer[ 8+aesDecryptXtsLoop ].m128i = _mm_xor_si128( tweakBuffer[ aesDecryptXtsLoop ].m128i, lastRoundKey );
+            // Prepare tweakBuffer[0-7] with tweaks for next 8 blocks
+            XTS_MUL_ALPHA_Scalar( lastTweakLow, lastTweakHigh );
+            tweakBuffer[ aesDecryptXtsLoop ].ull[0] = lastTweakLow;
+            tweakBuffer[ aesDecryptXtsLoop ].ull[1] = lastTweakHigh;
+        }
+
+        do
+        {
+            roundkey = _mm_loadu_si128( (__m128i *) keyPtr );
+            keyPtr ++;
+            c0 = _mm_aesdec_si128( c0, roundkey );
+            c1 = _mm_aesdec_si128( c1, roundkey );
+            c2 = _mm_aesdec_si128( c2, roundkey );
+            c3 = _mm_aesdec_si128( c3, roundkey );
+            c4 = _mm_aesdec_si128( c4, roundkey );
+            c5 = _mm_aesdec_si128( c5, roundkey );
+            c6 = _mm_aesdec_si128( c6, roundkey );
+            c7 = _mm_aesdec_si128( c7, roundkey );
+        } while( keyPtr < keyLimit );
+
+        _mm_storeu_si128( (__m128i *) (pbDst +   0), _mm_aesdeclast_si128( c0, tweakBuffer[ 8].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  16), _mm_aesdeclast_si128( c1, tweakBuffer[ 9].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  32), _mm_aesdeclast_si128( c2, tweakBuffer[10].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  48), _mm_aesdeclast_si128( c3, tweakBuffer[11].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  64), _mm_aesdeclast_si128( c4, tweakBuffer[12].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  80), _mm_aesdeclast_si128( c5, tweakBuffer[13].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +  96), _mm_aesdeclast_si128( c6, tweakBuffer[14].m128i ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 112), _mm_aesdeclast_si128( c7, tweakBuffer[15].m128i ) );
+
+        pbSrc += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        cbDataMain -= 8 * SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail == 0 )
+    {
+        return; // <-- expected case; early return here
+    }
+
+    // Rare case, with data unit length not being multiple of 128 bytes, handle the tail one block at a time
+    t0 = tweakBuffer[0].m128i;
+
+    while( cbDataTail >= 2*SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        c0 = _mm_xor_si128( _mm_loadu_si128( ( __m128i * ) pbSrc ), t0 );
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        AES_DECRYPT_1( pExpandedKey, c0 );
+        _mm_storeu_si128( (__m128i *) pbDst, _mm_xor_si128( c0, t0 ) );
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        c7 = t0;
+        XTS_MUL_ALPHA( t0, t0 );
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbDataTail > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Ciphertext stealing decryption
+        //
+        //                      +--------------+
+        //                      |              |
+        //                      |              V
+        // +-----------------+  |  +-----+-----------+
+        // |      C_m-1      |  |  | C_m |++++CP+++++|
+        // +-----------------+  |  +-----+-----------+
+        //          |           |           |
+        //        dec_m         |        dec_m-1
+        //          |           |           |
+        //          V           |           V
+        // +-----+-----------+  |  +-----------------+
+        // | P_m |++++CP+++++|--+  |      P_m-1      |
+        // +-----+-----------+     +-----------------+
+        //    |                   /
+        //    +----------------  /  --+
+        //                      /     |
+        //                      |     V
+        // +-----------------+  |  +-----+
+        // |      P_m-1      |<-+  | P_m |
+        // +-----------------+     +-----+
+
+        // Do final tweak update into c1
+        // Penultimate tweak is in t0, ready for final decryption
+        XTS_MUL_ALPHA( t0, c1 );
+
+        // Decrypt penultimate ciphertext block into tweakBuffer[0]
+        c0 = _mm_xor_si128( _mm_loadu_si128( (__m128i *) pbSrc ), c1 );
+        AES_DECRYPT_1( pExpandedKey, c0 );
+        tweakBuffer[0].m128i = _mm_xor_si128( c0, c1 );
+
+        cbDataTail -= SYMCRYPT_AES_BLOCK_SIZE;
+
+        // Copy tweakBuffer[0] to tweakBuffer[1]
+        tweakBuffer[1].m128i = tweakBuffer[0].m128i;
+        // Copy final ciphertext bytes to prefix of tweakBuffer[0] - we must read before writing to support in-place decryption
+        memcpy( &tweakBuffer[0].ul[0], pbSrc + SYMCRYPT_AES_BLOCK_SIZE, cbDataTail );
+        // Copy prefix of tweakBuffer[1] to the right place in the destination buffer
+        memcpy( pbDst + SYMCRYPT_AES_BLOCK_SIZE, &tweakBuffer[1].ul[0], cbDataTail );
+
+        // Load updated tweakBuffer[0] into c0
+        c0 = tweakBuffer[0].m128i;
+    } else {
+        // Just load final ciphertext block into c0
+        c0 = _mm_loadu_si128( (__m128i*) pbSrc );
+    }
+
+    // Final full block decryption
+    c0 = _mm_xor_si128( c0, t0 );
+    AES_DECRYPT_1( pExpandedKey, c0 );
+    _mm_storeu_si128( (__m128i *) pbDst, _mm_xor_si128( c0, t0 ) );
+}
+
+#define AES_FULLROUND_4_GHASH_1( roundkey, keyPtr, c0, c1, c2, c3, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+    c0 = _mm_aesenc_si128( c0, roundkey ); \
+    c1 = _mm_aesenc_si128( c1, roundkey ); \
+    c2 = _mm_aesenc_si128( c2, roundkey ); \
+    c3 = _mm_aesenc_si128( c3, roundkey ); \
+\
+    r0 = _mm_loadu_si128( (__m128i *) gHashPointer ); \
+    r0 = _mm_shuffle_epi8( r0, byteReverseOrder ); \
+    gHashPointer += 16; \
+\
+    t1 = _mm_loadu_si128( (__m128i *) &GHASH_H_POWER(gHashExpandedKeyTable, todo) ); \
+    t0 = _mm_clmulepi64_si128( r0, t1, 0x00 ); \
+    t1 = _mm_clmulepi64_si128( r0, t1, 0x11 ); \
+\
+    resl = _mm_xor_si128( resl, t0 ); \
+    resh = _mm_xor_si128( resh, t1 ); \
+\
+    t0 = _mm_srli_si128( r0, 8 ); \
+    r0 = _mm_xor_si128( r0, t0 ); \
+    t1 = _mm_loadu_si128( (__m128i *) &GHASH_Hx_POWER(gHashExpandedKeyTable, todo) ); \
+    t1 = _mm_clmulepi64_si128( r0, t1, 0x00 ); \
+\
+    resm = _mm_xor_si128( resm, t1 ); \
+    todo --; \
+};
+
+#define AES_GCM_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3, gHashPointer, ghashRounds, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+    __m128i t0, t1; \
+    __m128i r0; \
+    SIZE_T aesEncryptGhashLoop; \
+\
+    keyPtr = &pExpandedKey->RoundKey[0]; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+    c0 = _mm_xor_si128( c0, roundkey ); \
+    c1 = _mm_xor_si128( c1, roundkey ); \
+    c2 = _mm_xor_si128( c2, roundkey ); \
+    c3 = _mm_xor_si128( c3, roundkey ); \
+\
+    /* Do ghashRounds full rounds (AES-128|AES-192|AES-256) with stitched GHASH */ \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < ghashRounds; aesEncryptGhashLoop++ ) \
+    { \
+        AES_FULLROUND_4_GHASH_1( roundkey, keyPtr, c0, c1, c2, c3, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ); \
+    } \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesenc_si128( c0, roundkey ); \
+        c1 = _mm_aesenc_si128( c1, roundkey ); \
+        c2 = _mm_aesenc_si128( c2, roundkey ); \
+        c3 = _mm_aesenc_si128( c3, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesenclast_si128( c0, roundkey ); \
+    c1 = _mm_aesenclast_si128( c1, roundkey ); \
+    c2 = _mm_aesenclast_si128( c2, roundkey ); \
+    c3 = _mm_aesenclast_si128( c3, roundkey ); \
+};
+
+#define AES_FULLROUND_8_GHASH_1( roundkey, keyPtr, c0, c1, c2, c3, c4, c5, c6, c7, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+    c0 = _mm_aesenc_si128( c0, roundkey ); \
+    c1 = _mm_aesenc_si128( c1, roundkey ); \
+    c2 = _mm_aesenc_si128( c2, roundkey ); \
+    c3 = _mm_aesenc_si128( c3, roundkey ); \
+    c4 = _mm_aesenc_si128( c4, roundkey ); \
+    c5 = _mm_aesenc_si128( c5, roundkey ); \
+    c6 = _mm_aesenc_si128( c6, roundkey ); \
+    c7 = _mm_aesenc_si128( c7, roundkey ); \
+\
+    r0 = _mm_loadu_si128( (__m128i *) gHashPointer ); \
+    r0 = _mm_shuffle_epi8( r0, byteReverseOrder ); \
+    gHashPointer += 16; \
+\
+    t1 = _mm_loadu_si128( (__m128i *) &GHASH_H_POWER(gHashExpandedKeyTable, todo) ); \
+    t0 = _mm_clmulepi64_si128( r0, t1, 0x00 ); \
+    t1 = _mm_clmulepi64_si128( r0, t1, 0x11 ); \
+\
+    resl = _mm_xor_si128( resl, t0 ); \
+    resh = _mm_xor_si128( resh, t1 ); \
+\
+    t0 = _mm_srli_si128( r0, 8 ); \
+    r0 = _mm_xor_si128( r0, t0 ); \
+    t1 = _mm_loadu_si128( (__m128i *) &GHASH_Hx_POWER(gHashExpandedKeyTable, todo) ); \
+    t1 = _mm_clmulepi64_si128( r0, t1, 0x00 ); \
+\
+    resm = _mm_xor_si128( resm, t1 ); \
+    todo --; \
+};
+
+#define AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, gHashPointer, ghashRounds, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m128i roundkey; \
+    __m128i t0, t1; \
+    __m128i r0; \
+    SIZE_T aesEncryptGhashLoop; \
+\
+    keyPtr = &pExpandedKey->RoundKey[0]; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+    keyPtr ++; \
+    c0 = _mm_xor_si128( c0, roundkey ); \
+    c1 = _mm_xor_si128( c1, roundkey ); \
+    c2 = _mm_xor_si128( c2, roundkey ); \
+    c3 = _mm_xor_si128( c3, roundkey ); \
+    c4 = _mm_xor_si128( c4, roundkey ); \
+    c5 = _mm_xor_si128( c5, roundkey ); \
+    c6 = _mm_xor_si128( c6, roundkey ); \
+    c7 = _mm_xor_si128( c7, roundkey ); \
+\
+    /* Do ghashRounds full rounds (AES-128|AES-192|AES-256) with stitched GHASH */ \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < ghashRounds; aesEncryptGhashLoop++ ) \
+    { \
+        AES_FULLROUND_8_GHASH_1( roundkey, keyPtr, c0, c1, c2, c3, c4, c5, c6, c7, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ); \
+    } \
+\
+    do \
+    { \
+        roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+        keyPtr ++; \
+        c0 = _mm_aesenc_si128( c0, roundkey ); \
+        c1 = _mm_aesenc_si128( c1, roundkey ); \
+        c2 = _mm_aesenc_si128( c2, roundkey ); \
+        c3 = _mm_aesenc_si128( c3, roundkey ); \
+        c4 = _mm_aesenc_si128( c4, roundkey ); \
+        c5 = _mm_aesenc_si128( c5, roundkey ); \
+        c6 = _mm_aesenc_si128( c6, roundkey ); \
+        c7 = _mm_aesenc_si128( c7, roundkey ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkey = _mm_loadu_si128( (__m128i *) keyPtr ); \
+\
+    c0 = _mm_aesenclast_si128( c0, roundkey ); \
+    c1 = _mm_aesenclast_si128( c1, roundkey ); \
+    c2 = _mm_aesenclast_si128( c2, roundkey ); \
+    c3 = _mm_aesenclast_si128( c3, roundkey ); \
+    c4 = _mm_aesenclast_si128( c4, roundkey ); \
+    c5 = _mm_aesenclast_si128( c5, roundkey ); \
+    c6 = _mm_aesenclast_si128( c6, roundkey ); \
+    c7 = _mm_aesenclast_si128( c7, roundkey ); \
+};
+
+// This call is functionally identical to:
+// SymCryptAesCtrMsb64Xmm( pExpandedKey,
+//                         pbChainingValue,
+//                         pbSrc,
+//                         pbDst,
+//                         cbData );
+// SymCryptGHashAppendDataPclmulqdq(   expandedKeyTable,
+//                                     pState,
+//                                     pbDst,
+//                                     cbData );
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptStitchedXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __m128i chain = _mm_loadu_si128( (__m128i *) pbChainingValue );
+
+    __m128i BYTE_REVERSE_ORDER = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+    __m128i vMultiplicationConstant = _mm_set_epi32( 0, 0, 0xc2000000, 0 );
+
+    __m128i chainIncrement1 = _mm_set_epi32( 0, 0, 0, 1 );
+    __m128i chainIncrement2 = _mm_set_epi32( 0, 0, 0, 2 );
+    __m128i chainIncrement8 = _mm_set_epi32( 0, 0, 0, 8 );
+
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m128i r0, r1;
+
+    __m128i state;
+    __m128i a0, a1, a2;
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+    PCBYTE pbGhashSrc = pbDst;
+
+    SYMCRYPT_ASSERT( (cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == 0 ); // cbData is multiple of block size
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER );
+    state = _mm_loadu_si128( (__m128i *) pState );
+
+    todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS );
+    CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+
+    // Do 8 blocks of CTR either for tail (if total blocks <8) or for encryption of first 8 blocks
+    c0 = chain;
+    c1 = _mm_add_epi32( chain, chainIncrement1 );
+    c2 = _mm_add_epi32( chain, chainIncrement2 );
+    c3 = _mm_add_epi32( c1, chainIncrement2 );
+    c4 = _mm_add_epi32( c2, chainIncrement2 );
+    c5 = _mm_add_epi32( c3, chainIncrement2 );
+    c6 = _mm_add_epi32( c4, chainIncrement2 );
+    c7 = _mm_add_epi32( c5, chainIncrement2 );
+
+    c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+    c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+    c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+    c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+    c4 = _mm_shuffle_epi8( c4, BYTE_REVERSE_ORDER );
+    c5 = _mm_shuffle_epi8( c5, BYTE_REVERSE_ORDER );
+    c6 = _mm_shuffle_epi8( c6, BYTE_REVERSE_ORDER );
+    c7 = _mm_shuffle_epi8( c7, BYTE_REVERSE_ORDER );
+
+    AES_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+    if( nBlocks >= 8 )
+    {
+        // Encrypt first 8 blocks - update chain
+        chain = _mm_add_epi32( chain, chainIncrement8 );
+
+        _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 32), _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc + 32) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 48), _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc + 48) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 64), _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc + 64) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 80), _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc + 80) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 96), _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc + 96) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +112), _mm_xor_si128( c7, _mm_loadu_si128( ( __m128i * ) (pbSrc +112) ) ) );
+
+        pbDst  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbSrc  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+
+        while( nBlocks >= 16 )
+        {
+            // In this loop we always have 8 blocks to encrypt and we have already encrypted the previous 8 blocks ready for GHASH
+            c0 = chain;
+            c1 = _mm_add_epi32( chain, chainIncrement1 );
+            c2 = _mm_add_epi32( chain, chainIncrement2 );
+            c3 = _mm_add_epi32( c1, chainIncrement2 );
+            c4 = _mm_add_epi32( c2, chainIncrement2 );
+            c5 = _mm_add_epi32( c3, chainIncrement2 );
+            c6 = _mm_add_epi32( c4, chainIncrement2 );
+            c7 = _mm_add_epi32( c5, chainIncrement2 );
+            chain = _mm_add_epi32( c6, chainIncrement2 );
+
+            c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+            c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+            c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+            c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+            c4 = _mm_shuffle_epi8( c4, BYTE_REVERSE_ORDER );
+            c5 = _mm_shuffle_epi8( c5, BYTE_REVERSE_ORDER );
+            c6 = _mm_shuffle_epi8( c6, BYTE_REVERSE_ORDER );
+            c7 = _mm_shuffle_epi8( c7, BYTE_REVERSE_ORDER );
+
+            AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pbGhashSrc, 8, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 32), _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc + 32) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 48), _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc + 48) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 64), _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc + 64) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 80), _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc + 80) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 96), _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc + 96) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst +112), _mm_xor_si128( c7, _mm_loadu_si128( ( __m128i * ) (pbSrc +112) ) ) );
+
+            pbDst  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+            pbSrc  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+            nBlocks -= 8;
+
+            if( todo == 0 )
+            {
+                CLMUL_3_POST( a0, a1, a2 );
+                MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS );
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+        }
+
+        // We now have at least 8 blocks of encrypted data to GHASH and at most 7 blocks left to encrypt
+        // Do 8 blocks of GHASH in parallel with generating 0, 4, or 8 AES-CTR blocks for tail encryption
+        nBlocks -= 8;
+        if (nBlocks > 0)
+        {
+            c0 = chain;
+            c1 = _mm_add_epi32( chain, chainIncrement1 );
+            c2 = _mm_add_epi32( chain, chainIncrement2 );
+            c3 = _mm_add_epi32( c1, chainIncrement2 );
+            c4 = _mm_add_epi32( c2, chainIncrement2 );
+
+            c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+            c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+            c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+            c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+
+            if (nBlocks > 4)
+            {
+                // Do 8 rounds of AES-CTR for tail in parallel with 8 rounds of GHASH
+                c5 = _mm_add_epi32( c4, chainIncrement1 );
+                c6 = _mm_add_epi32( c4, chainIncrement2 );
+
+                c4 = _mm_shuffle_epi8( c4, BYTE_REVERSE_ORDER );
+                c5 = _mm_shuffle_epi8( c5, BYTE_REVERSE_ORDER );
+                c6 = _mm_shuffle_epi8( c6, BYTE_REVERSE_ORDER );
+
+                AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pbGhashSrc, 8, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+            }
+            else
+            {
+                // Do 4 rounds of AES-CTR for tail in parallel with 8 rounds of GHASH
+                AES_GCM_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3, pbGhashSrc, 8, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+            }
+
+            if( todo == 0)
+            {
+                CLMUL_3_POST( a0, a1, a2 );
+                MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS );
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+        }
+        else
+        {
+            // Just do the final 8 rounds of GHASH
+            for( todo=8; todo>0; todo-- )
+            {
+                r0 = _mm_shuffle_epi8( _mm_loadu_si128( (__m128i *) (pbGhashSrc +  0) ), BYTE_REVERSE_ORDER );
+                pbGhashSrc += SYMCRYPT_AES_BLOCK_SIZE;
+
+                CLMUL_ACC_3( r0, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+
+            CLMUL_3_POST( a0, a1, a2 );
+            MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+        }
+    }
+
+    if( nBlocks > 0 )
+    {
+        // Encrypt 1-7 blocks with pre-generated AES-CTR blocks and GHASH the results
+        while( nBlocks >= 2 )
+        {
+            chain = _mm_add_epi32( chain, chainIncrement2 );
+
+            r0 = _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) );
+            r1 = _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16) ) );
+
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), r0 );
+            _mm_storeu_si128( (__m128i *) (pbDst + 16), r1 );
+
+            r0 = _mm_shuffle_epi8( r0, BYTE_REVERSE_ORDER );
+            r1 = _mm_shuffle_epi8( r1, BYTE_REVERSE_ORDER );
+
+            CLMUL_ACC_3( r0, GHASH_H_POWER(expandedKeyTable, todo - 0), GHASH_Hx_POWER(expandedKeyTable, todo - 0), a0, a1, a2 );
+            CLMUL_ACC_3( r1, GHASH_H_POWER(expandedKeyTable, todo - 1), GHASH_Hx_POWER(expandedKeyTable, todo - 1), a0, a1, a2 );
+
+            pbDst   += 2*SYMCRYPT_AES_BLOCK_SIZE;
+            pbSrc   += 2*SYMCRYPT_AES_BLOCK_SIZE;
+            todo    -= 2;
+            nBlocks -= 2;
+            c0 = c2;
+            c1 = c3;
+            c2 = c4;
+            c3 = c5;
+            c4 = c6;
+        }
+
+        if( nBlocks > 0 )
+        {
+            chain = _mm_add_epi32( chain, chainIncrement1 );
+
+            r0 = _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) );
+
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), r0 );
+
+            r0 = _mm_shuffle_epi8( r0, BYTE_REVERSE_ORDER );
+
+            CLMUL_ACC_3( r0, GHASH_H_POWER(expandedKeyTable, 1), GHASH_Hx_POWER(expandedKeyTable, 1), a0, a1, a2 );
+        }
+
+        CLMUL_3_POST( a0, a1, a2 );
+        MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+    }
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER );
+    _mm_storeu_si128( (__m128i *) pbChainingValue, chain );
+    _mm_storeu_si128( (__m128i *) pState, state );
+}
+
+#pragma warning(push)
+#pragma warning( disable:4701 )
+#pragma runtime_checks( "u", off )
+// This call is functionally identical to:
+// SymCryptGHashAppendDataPclmulqdq(   expandedKeyTable,
+//                                     pState,
+//                                     pbSrc,
+//                                     cbData );
+// SymCryptAesCtrMsb64Xmm( pExpandedKey,
+//                         pbChainingValue,
+//                         pbSrc,
+//                         pbDst,
+//                         cbData );
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptStitchedXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __m128i chain = _mm_loadu_si128( (__m128i *) pbChainingValue );
+
+    __m128i BYTE_REVERSE_ORDER = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+    __m128i vMultiplicationConstant = _mm_set_epi32( 0, 0, 0xc2000000, 0 );
+
+    __m128i chainIncrement1 = _mm_set_epi32( 0, 0, 0, 1 );
+    __m128i chainIncrement2 = _mm_set_epi32( 0, 0, 0, 2 );
+
+    __m128i c0, c1, c2, c3, c4, c5, c6, c7;
+
+    __m128i state;
+    __m128i a0, a1, a2;
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo = 0;
+    PCBYTE pbGhashSrc = pbSrc;
+
+    SYMCRYPT_ASSERT( (cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == 0 ); // cbData is multiple of block size
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER );
+    state = _mm_loadu_si128( (__m128i *) pState );
+
+    todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS );
+    CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+
+    while( nBlocks >= 8 )
+    {
+        // In this loop we always have 8 blocks to decrypt and GHASH
+        c0 = chain;
+        c1 = _mm_add_epi32( chain, chainIncrement1 );
+        c2 = _mm_add_epi32( chain, chainIncrement2 );
+        c3 = _mm_add_epi32( c1, chainIncrement2 );
+        c4 = _mm_add_epi32( c2, chainIncrement2 );
+        c5 = _mm_add_epi32( c3, chainIncrement2 );
+        c6 = _mm_add_epi32( c4, chainIncrement2 );
+        c7 = _mm_add_epi32( c5, chainIncrement2 );
+        chain = _mm_add_epi32( c6, chainIncrement2 );
+
+        c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+        c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+        c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+        c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+        c4 = _mm_shuffle_epi8( c4, BYTE_REVERSE_ORDER );
+        c5 = _mm_shuffle_epi8( c5, BYTE_REVERSE_ORDER );
+        c6 = _mm_shuffle_epi8( c6, BYTE_REVERSE_ORDER );
+        c7 = _mm_shuffle_epi8( c7, BYTE_REVERSE_ORDER );
+
+        AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pbGhashSrc, 8, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+
+        _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 32), _mm_xor_si128( c2, _mm_loadu_si128( ( __m128i * ) (pbSrc + 32) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 48), _mm_xor_si128( c3, _mm_loadu_si128( ( __m128i * ) (pbSrc + 48) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 64), _mm_xor_si128( c4, _mm_loadu_si128( ( __m128i * ) (pbSrc + 64) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 80), _mm_xor_si128( c5, _mm_loadu_si128( ( __m128i * ) (pbSrc + 80) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst + 96), _mm_xor_si128( c6, _mm_loadu_si128( ( __m128i * ) (pbSrc + 96) ) ) );
+        _mm_storeu_si128( (__m128i *) (pbDst +112), _mm_xor_si128( c7, _mm_loadu_si128( ( __m128i * ) (pbSrc +112) ) ) );
+
+        pbDst  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbSrc  += 8 * SYMCRYPT_AES_BLOCK_SIZE;
+        nBlocks -= 8;
+
+        if ( todo == 0 )
+        {
+            CLMUL_3_POST( a0, a1, a2 );
+            MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+            if ( nBlocks > 0 )
+            {
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS );
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+            }
+        }
+    }
+
+    if( nBlocks > 0 )
+    {
+        // We have 1-7 blocks to GHASH and decrypt
+        // Do the exact number of GHASH blocks we need in parallel with generating either 4 or 8 blocks of AES-CTR
+        c0 = chain;
+        c1 = _mm_add_epi32( chain, chainIncrement1 );
+        c2 = _mm_add_epi32( chain, chainIncrement2 );
+        c3 = _mm_add_epi32( c1, chainIncrement2 );
+        c4 = _mm_add_epi32( c2, chainIncrement2 );
+
+        c0 = _mm_shuffle_epi8( c0, BYTE_REVERSE_ORDER );
+        c1 = _mm_shuffle_epi8( c1, BYTE_REVERSE_ORDER );
+        c2 = _mm_shuffle_epi8( c2, BYTE_REVERSE_ORDER );
+        c3 = _mm_shuffle_epi8( c3, BYTE_REVERSE_ORDER );
+
+        if( nBlocks > 4 )
+        {
+            c5 = _mm_add_epi32( c4, chainIncrement1 );
+            c6 = _mm_add_epi32( c4, chainIncrement2 );
+
+            c4 = _mm_shuffle_epi8( c4, BYTE_REVERSE_ORDER );
+            c5 = _mm_shuffle_epi8( c5, BYTE_REVERSE_ORDER );
+            c6 = _mm_shuffle_epi8( c6, BYTE_REVERSE_ORDER );
+
+            AES_GCM_ENCRYPT_8( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pbGhashSrc, nBlocks, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+        } else {
+            AES_GCM_ENCRYPT_4( pExpandedKey, c0, c1, c2, c3, pbGhashSrc, nBlocks, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+        }
+
+        CLMUL_3_POST( a0, a1, a2 );
+        MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+
+        // Decrypt 1-7 blocks with pre-generated AES-CTR blocks
+        while( nBlocks >= 2 )
+        {
+            chain = _mm_add_epi32( chain, chainIncrement2 );
+
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) ) );
+            _mm_storeu_si128( (__m128i *) (pbDst + 16), _mm_xor_si128( c1, _mm_loadu_si128( ( __m128i * ) (pbSrc + 16) ) ) );
+
+            pbDst   += 2*SYMCRYPT_AES_BLOCK_SIZE;
+            pbSrc   += 2*SYMCRYPT_AES_BLOCK_SIZE;
+            nBlocks -= 2;
+            c0 = c2;
+            c1 = c3;
+            c2 = c4;
+            c3 = c5;
+            c4 = c6;
+        }
+
+        if( nBlocks > 0 )
+        {
+            chain = _mm_add_epi32( chain, chainIncrement1 );
+
+            _mm_storeu_si128( (__m128i *) (pbDst +  0), _mm_xor_si128( c0, _mm_loadu_si128( ( __m128i * ) (pbSrc +  0) ) ) );
+        }
+    }
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER );
+    _mm_storeu_si128( (__m128i *) pbChainingValue, chain );
+    _mm_storeu_si128((__m128i *)pState, state );
+}
+#pragma runtime_checks( "u", restore )
+#pragma warning(pop)
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif // CPU_X86 | CPU_AMD64
diff --git a/libs/symcrypt/lib/aes-ymm.c b/libs/symcrypt/lib/aes-ymm.c
new file mode 100644
index 000000000000..bb501d062678
--- /dev/null
+++ b/libs/symcrypt/lib/aes-ymm.c
@@ -0,0 +1,793 @@
+//
+// aes-ymm.c    code for AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// All YMM code for AES operations
+// Requires compiler support for aesni, pclmulqdq, avx2, vaes and vpclmulqdq
+//
+
+#include "precomp.h"
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("avx2,pclmul,vaes,vpclmulqdq"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("avx2,pclmul,vaes,vpclmulqdq")
+#endif
+
+#include "xtsaes_definitions.h"
+#include "ghash_definitions.h"
+
+#define AES_ENCRYPT_YMM_2048( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m256i roundkeys; \
+\
+    keyPtr = pExpandedKey->RoundKey; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    /* _mm256_broadcastsi128_si256 requires AVX2 */ \
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+    keyPtr ++; \
+\
+    /* _mm256_xor_si256 requires AVX2 */ \
+    c0 = _mm256_xor_si256( c0, roundkeys ); \
+    c1 = _mm256_xor_si256( c1, roundkeys ); \
+    c2 = _mm256_xor_si256( c2, roundkeys ); \
+    c3 = _mm256_xor_si256( c3, roundkeys ); \
+    c4 = _mm256_xor_si256( c4, roundkeys ); \
+    c5 = _mm256_xor_si256( c5, roundkeys ); \
+    c6 = _mm256_xor_si256( c6, roundkeys ); \
+    c7 = _mm256_xor_si256( c7, roundkeys ); \
+\
+    do \
+    { \
+        roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+        keyPtr ++; \
+        c0 = _mm256_aesenc_epi128( c0, roundkeys ); \
+        c1 = _mm256_aesenc_epi128( c1, roundkeys ); \
+        c2 = _mm256_aesenc_epi128( c2, roundkeys ); \
+        c3 = _mm256_aesenc_epi128( c3, roundkeys ); \
+        c4 = _mm256_aesenc_epi128( c4, roundkeys ); \
+        c5 = _mm256_aesenc_epi128( c5, roundkeys ); \
+        c6 = _mm256_aesenc_epi128( c6, roundkeys ); \
+        c7 = _mm256_aesenc_epi128( c7, roundkeys ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+\
+    c0 = _mm256_aesenclast_epi128( c0, roundkeys ); \
+    c1 = _mm256_aesenclast_epi128( c1, roundkeys ); \
+    c2 = _mm256_aesenclast_epi128( c2, roundkeys ); \
+    c3 = _mm256_aesenclast_epi128( c3, roundkeys ); \
+    c4 = _mm256_aesenclast_epi128( c4, roundkeys ); \
+    c5 = _mm256_aesenclast_epi128( c5, roundkeys ); \
+    c6 = _mm256_aesenclast_epi128( c6, roundkeys ); \
+    c7 = _mm256_aesenclast_epi128( c7, roundkeys ); \
+};
+
+#define AES_DECRYPT_YMM_2048( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m256i roundkeys; \
+\
+    keyPtr = pExpandedKey->lastEncRoundKey; \
+    keyLimit = pExpandedKey->lastDecRoundKey; \
+\
+    /* _mm256_broadcastsi128_si256 requires AVX2 */ \
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+    keyPtr ++; \
+\
+    /* _mm256_xor_si256 requires AVX2 */ \
+    c0 = _mm256_xor_si256( c0, roundkeys ); \
+    c1 = _mm256_xor_si256( c1, roundkeys ); \
+    c2 = _mm256_xor_si256( c2, roundkeys ); \
+    c3 = _mm256_xor_si256( c3, roundkeys ); \
+    c4 = _mm256_xor_si256( c4, roundkeys ); \
+    c5 = _mm256_xor_si256( c5, roundkeys ); \
+    c6 = _mm256_xor_si256( c6, roundkeys ); \
+    c7 = _mm256_xor_si256( c7, roundkeys ); \
+\
+    do \
+    { \
+        roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+        keyPtr ++; \
+        c0 = _mm256_aesdec_epi128( c0, roundkeys ); \
+        c1 = _mm256_aesdec_epi128( c1, roundkeys ); \
+        c2 = _mm256_aesdec_epi128( c2, roundkeys ); \
+        c3 = _mm256_aesdec_epi128( c3, roundkeys ); \
+        c4 = _mm256_aesdec_epi128( c4, roundkeys ); \
+        c5 = _mm256_aesdec_epi128( c5, roundkeys ); \
+        c6 = _mm256_aesdec_epi128( c6, roundkeys ); \
+        c7 = _mm256_aesdec_epi128( c7, roundkeys ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+\
+    c0 = _mm256_aesdeclast_epi128( c0, roundkeys ); \
+    c1 = _mm256_aesdeclast_epi128( c1, roundkeys ); \
+    c2 = _mm256_aesdeclast_epi128( c2, roundkeys ); \
+    c3 = _mm256_aesdeclast_epi128( c3, roundkeys ); \
+    c4 = _mm256_aesdeclast_epi128( c4, roundkeys ); \
+    c5 = _mm256_aesdeclast_epi128( c5, roundkeys ); \
+    c6 = _mm256_aesdeclast_epi128( c6, roundkeys ); \
+    c7 = _mm256_aesdeclast_epi128( c7, roundkeys ); \
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitYmm_2048(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i t0, t1, t2, t3, t4, t5, t6, t7;
+    __m256i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m128i XTS_ALPHA_MASK;
+    __m256i XTS_ALPHA_MULTIPLIER_Ymm;
+
+    // Load tweaks into big T
+    __m256i T0, T1, T2, T3, T4, T5, T6, T7;
+
+    SIZE_T cbDataMain;  // number of bytes to handle in the main loop
+    SIZE_T cbDataTail;  // number of bytes to handle in the tail loop
+
+    // To simplify logic and unusual size processing, we handle all
+    // data not a multiple of 16 blocks in the tail loop
+    cbDataTail = cbData & ((16*SYMCRYPT_AES_BLOCK_SIZE)-1);
+    // Additionally, so that ciphertext stealing logic does not rely on
+    // reading back from the destination buffer, when we have a non-zero
+    // tail, we ensure that we handle at least 1 whole block in the tail
+    cbDataTail += ((cbDataTail > 0) && (cbDataTail < SYMCRYPT_AES_BLOCK_SIZE)) ? (16*SYMCRYPT_AES_BLOCK_SIZE) : 0;
+    cbDataMain = cbData - cbDataTail;
+
+    SYMCRYPT_ASSERT(cbDataMain <= cbData);
+    SYMCRYPT_ASSERT(cbDataTail <= cbData);
+    SYMCRYPT_ASSERT((cbDataMain & ((16*SYMCRYPT_AES_BLOCK_SIZE)-1)) == 0);
+
+    if( cbDataMain == 0 )
+    {
+        SymCryptXtsAesEncryptDataUnitXmm( pExpandedKey, pbTweakBlock, pbScratch, pbSrc, pbDst, cbDataTail );
+        return;
+    }
+
+    t0 = _mm_loadu_si128( (__m128i *) pbTweakBlock );
+    XTS_ALPHA_MASK = _mm_set_epi32( 1, 1, 1, 0x87 );
+    XTS_ALPHA_MULTIPLIER_Ymm = _mm256_set_epi64x( 0, 0x87, 0, 0x87 );
+
+    // Do not stall.
+    XTS_MUL_ALPHA4( t0, t4 );
+    XTS_MUL_ALPHA ( t0, t1 );
+    XTS_MUL_ALPHA ( t4, t5 );
+    XTS_MUL_ALPHA ( t1, t2 );
+    XTS_MUL_ALPHA ( t5, t6 );
+    XTS_MUL_ALPHA ( t2, t3 );
+    XTS_MUL_ALPHA ( t6, t7 );
+
+    T0 = _mm256_insertf128_si256( _mm256_castsi128_si256( t0 ), t1, 1 ); // AVX
+    T1 = _mm256_insertf128_si256( _mm256_castsi128_si256( t2 ), t3, 1 );
+    T2 = _mm256_insertf128_si256( _mm256_castsi128_si256( t4 ), t5, 1 );
+    T3 = _mm256_insertf128_si256( _mm256_castsi128_si256( t6 ), t7, 1 );
+    XTS_MUL_ALPHA8_YMM(T0, T4);
+    XTS_MUL_ALPHA8_YMM(T1, T5);
+    XTS_MUL_ALPHA8_YMM(T2, T6);
+    XTS_MUL_ALPHA8_YMM(T3, T7);
+
+    for(;;)
+    {
+        c0 = _mm256_xor_si256( T0, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +                           0 ) ) );
+        c1 = _mm256_xor_si256( T1, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   2*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c2 = _mm256_xor_si256( T2, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   4*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c3 = _mm256_xor_si256( T3, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   6*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c4 = _mm256_xor_si256( T4, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   8*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c5 = _mm256_xor_si256( T5, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +  10*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c6 = _mm256_xor_si256( T6, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +  12*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c7 = _mm256_xor_si256( T7, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +  14*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+
+        pbSrc += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+
+        AES_ENCRYPT_YMM_2048( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +                          0 ), _mm256_xor_si256( c0, T0 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  2*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c1, T1 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  4*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c2, T2 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  6*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c3, T3 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  8*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c4, T4 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst + 10*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c5, T5 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst + 12*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c6, T6 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst + 14*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c7, T7 ) );
+
+        pbDst += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+
+        cbDataMain -= 16 * SYMCRYPT_AES_BLOCK_SIZE;
+        if( cbDataMain < 16 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            break;
+        }
+
+        XTS_MUL_ALPHA16_YMM(T0, T0);
+        XTS_MUL_ALPHA16_YMM(T1, T1);
+        XTS_MUL_ALPHA16_YMM(T2, T2);
+        XTS_MUL_ALPHA16_YMM(T3, T3);
+        XTS_MUL_ALPHA16_YMM(T4, T4);
+        XTS_MUL_ALPHA16_YMM(T5, T5);
+        XTS_MUL_ALPHA16_YMM(T6, T6);
+        XTS_MUL_ALPHA16_YMM(T7, T7);
+    }
+
+    // We won't do another 16-block set so we don't update the tweak blocks
+
+    if( cbDataTail > 0 )
+    {
+        //
+        // This is a rare case: the data unit length is not a multiple of 256 bytes.
+        // We do this in the Xmm implementation.
+        // Fix up the tweak block first
+        //
+        t7 = _mm256_extracti128_si256 ( T7, 1 /* Highest 128 bits */ ); // AVX2
+        _mm256_zeroupper();
+        XTS_MUL_ALPHA( t7, t0 );
+        _mm_storeu_si128( (__m128i *) pbTweakBlock, t0 );
+
+        SymCryptXtsAesEncryptDataUnitXmm( pExpandedKey, pbTweakBlock, pbScratch, pbSrc, pbDst, cbDataTail );
+    }
+    else {
+        _mm256_zeroupper();
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitYmm_2048(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    __m128i t0, t1, t2, t3, t4, t5, t6, t7;
+    __m256i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m128i XTS_ALPHA_MASK;
+    __m256i XTS_ALPHA_MULTIPLIER_Ymm;
+
+    // Load tweaks into big T
+    __m256i T0, T1, T2, T3, T4, T5, T6, T7;
+
+    SIZE_T cbDataMain;  // number of bytes to handle in the main loop
+    SIZE_T cbDataTail;  // number of bytes to handle in the tail loop
+
+    // To simplify logic and unusual size processing, we handle all
+    // data not a multiple of 16 blocks in the tail loop
+    cbDataTail = cbData & ((16*SYMCRYPT_AES_BLOCK_SIZE)-1);
+    // Additionally, so that ciphertext stealing logic does not rely on
+    // reading back from the destination buffer, when we have a non-zero
+    // tail, we ensure that we handle at least 1 whole block in the tail
+    cbDataTail += ((cbDataTail > 0) && (cbDataTail < SYMCRYPT_AES_BLOCK_SIZE)) ? (16*SYMCRYPT_AES_BLOCK_SIZE) : 0;
+    cbDataMain = cbData - cbDataTail;
+
+    SYMCRYPT_ASSERT(cbDataMain <= cbData);
+    SYMCRYPT_ASSERT(cbDataTail <= cbData);
+    SYMCRYPT_ASSERT((cbDataMain & ((16*SYMCRYPT_AES_BLOCK_SIZE)-1)) == 0);
+
+    if( cbDataMain == 0 )
+    {
+        SymCryptXtsAesDecryptDataUnitXmm( pExpandedKey, pbTweakBlock, pbScratch, pbSrc, pbDst, cbDataTail );
+        return;
+    }
+
+    t0 = _mm_loadu_si128( (__m128i *) pbTweakBlock );
+    XTS_ALPHA_MASK = _mm_set_epi32( 1, 1, 1, 0x87 );
+    XTS_ALPHA_MULTIPLIER_Ymm = _mm256_set_epi64x( 0, 0x87, 0, 0x87 );
+
+    // Do not stall.
+    XTS_MUL_ALPHA4( t0, t4 );
+    XTS_MUL_ALPHA ( t0, t1 );
+    XTS_MUL_ALPHA ( t4, t5 );
+    XTS_MUL_ALPHA ( t1, t2 );
+    XTS_MUL_ALPHA ( t5, t6 );
+    XTS_MUL_ALPHA ( t2, t3 );
+    XTS_MUL_ALPHA ( t6, t7 );
+
+    T0 = _mm256_insertf128_si256( _mm256_castsi128_si256( t0 ), t1, 1); // AVX
+    T1 = _mm256_insertf128_si256( _mm256_castsi128_si256( t2 ), t3, 1);
+    T2 = _mm256_insertf128_si256( _mm256_castsi128_si256( t4 ), t5, 1);
+    T3 = _mm256_insertf128_si256( _mm256_castsi128_si256( t6 ), t7, 1);
+    XTS_MUL_ALPHA8_YMM(T0, T4);
+    XTS_MUL_ALPHA8_YMM(T1, T5);
+    XTS_MUL_ALPHA8_YMM(T2, T6);
+    XTS_MUL_ALPHA8_YMM(T3, T7);
+
+    for(;;)
+    {
+        c0 = _mm256_xor_si256( T0, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +                           0 ) ) );
+        c1 = _mm256_xor_si256( T1, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   2*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c2 = _mm256_xor_si256( T2, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   4*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c3 = _mm256_xor_si256( T3, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   6*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c4 = _mm256_xor_si256( T4, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +   8*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c5 = _mm256_xor_si256( T5, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +  10*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c6 = _mm256_xor_si256( T6, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +  12*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+        c7 = _mm256_xor_si256( T7, _mm256_loadu_si256( ( __m256i * ) ( pbSrc +  14*SYMCRYPT_AES_BLOCK_SIZE ) ) );
+
+        pbSrc += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+
+        AES_DECRYPT_YMM_2048( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +                          0 ), _mm256_xor_si256( c0, T0 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  2*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c1, T1 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  4*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c2, T2 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  6*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c3, T3 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst +  8*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c4, T4 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst + 10*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c5, T5 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst + 12*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c6, T6 ) );
+        _mm256_storeu_si256( ( __m256i * ) ( pbDst + 14*SYMCRYPT_AES_BLOCK_SIZE ), _mm256_xor_si256( c7, T7 ) );
+
+        pbDst += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+
+        cbDataMain -= 16 * SYMCRYPT_AES_BLOCK_SIZE;
+        if( cbDataMain < 16 * SYMCRYPT_AES_BLOCK_SIZE )
+        {
+            break;
+        }
+
+        XTS_MUL_ALPHA16_YMM(T0, T0);
+        XTS_MUL_ALPHA16_YMM(T1, T1);
+        XTS_MUL_ALPHA16_YMM(T2, T2);
+        XTS_MUL_ALPHA16_YMM(T3, T3);
+        XTS_MUL_ALPHA16_YMM(T4, T4);
+        XTS_MUL_ALPHA16_YMM(T5, T5);
+        XTS_MUL_ALPHA16_YMM(T6, T6);
+        XTS_MUL_ALPHA16_YMM(T7, T7);
+    }
+
+    // We won't do another 16-block set so we don't update the tweak blocks
+
+    if( cbDataTail > 0 )
+    {
+        //
+        // This is a rare case: the data unit length is not a multiple of 256 bytes.
+        // We do this in the Xmm implementation.
+        // Fix up the tweak block first
+        //
+        t7 = _mm256_extracti128_si256 ( T7, 1 /* Highest 128 bits */ ); // AVX2
+        _mm256_zeroupper();
+        XTS_MUL_ALPHA( t7, t0 );
+        _mm_storeu_si128( (__m128i *) pbTweakBlock, t0 );
+
+        SymCryptXtsAesDecryptDataUnitXmm( pExpandedKey, pbTweakBlock, pbScratch, pbSrc, pbDst, cbDataTail );
+    }
+    else {
+        _mm256_zeroupper();
+    }
+}
+
+#define AES_FULLROUND_16_GHASH_2_Ymm( roundkeys, keyPtr, c0, c1, c2, c3, c4, c5, c6, c7, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+    keyPtr ++; \
+    c0 = _mm256_aesenc_epi128( c0, roundkeys ); \
+    c1 = _mm256_aesenc_epi128( c1, roundkeys ); \
+    c2 = _mm256_aesenc_epi128( c2, roundkeys ); \
+    c3 = _mm256_aesenc_epi128( c3, roundkeys ); \
+    c4 = _mm256_aesenc_epi128( c4, roundkeys ); \
+    c5 = _mm256_aesenc_epi128( c5, roundkeys ); \
+    c6 = _mm256_aesenc_epi128( c6, roundkeys ); \
+    c7 = _mm256_aesenc_epi128( c7, roundkeys ); \
+\
+    r0 = _mm256_loadu_si256( (__m256i *) gHashPointer ); \
+    r0 = _mm256_shuffle_epi8( r0, byteReverseOrder ); \
+    gHashPointer += 32; \
+\
+    t1 = _mm256_loadu_si256( (__m256i *) &GHASH_H_POWER(gHashExpandedKeyTable, todo) ); \
+    t0 = _mm256_clmulepi64_epi128( r0, t1, 0x00 ); \
+    t1 = _mm256_clmulepi64_epi128( r0, t1, 0x11 ); \
+\
+    resl = _mm256_xor_si256( resl, t0 ); \
+    resh = _mm256_xor_si256( resh, t1 ); \
+\
+    t0 = _mm256_srli_si256( r0, 8 ); \
+    r0 = _mm256_xor_si256( r0, t0 ); \
+    t1 = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(gHashExpandedKeyTable, todo) ); \
+    t1 = _mm256_clmulepi64_epi128( r0, t1, 0x00 ); \
+\
+    resm = _mm256_xor_si256( resm, t1 ); \
+    todo -= 2; \
+};
+
+#define AES_GCM_ENCRYPT_16_Ymm( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ) \
+{ \
+    const BYTE (*keyPtr)[4][4]; \
+    const BYTE (*keyLimit)[4][4]; \
+    __m256i roundkeys; \
+    __m256i t0, t1; \
+    __m256i r0; \
+    int aesEncryptGhashLoop; \
+\
+    keyPtr = pExpandedKey->RoundKey; \
+    keyLimit = pExpandedKey->lastEncRoundKey; \
+\
+    /* _mm256_broadcastsi128_si256 requires AVX2 */ \
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+    keyPtr ++; \
+\
+    /* _mm256_xor_si256 requires AVX2 */ \
+    c0 = _mm256_xor_si256( c0, roundkeys ); \
+    c1 = _mm256_xor_si256( c1, roundkeys ); \
+    c2 = _mm256_xor_si256( c2, roundkeys ); \
+    c3 = _mm256_xor_si256( c3, roundkeys ); \
+    c4 = _mm256_xor_si256( c4, roundkeys ); \
+    c5 = _mm256_xor_si256( c5, roundkeys ); \
+    c6 = _mm256_xor_si256( c6, roundkeys ); \
+    c7 = _mm256_xor_si256( c7, roundkeys ); \
+\
+    /* Do 8(x2) full rounds (AES-128|AES-192|AES-256) with stitched GHASH */ \
+    for( aesEncryptGhashLoop = 0; aesEncryptGhashLoop < 4; aesEncryptGhashLoop++ ) \
+    { \
+        AES_FULLROUND_16_GHASH_2_Ymm( roundkeys, keyPtr, c0, c1, c2, c3, c4, c5, c6, c7, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ); \
+        AES_FULLROUND_16_GHASH_2_Ymm( roundkeys, keyPtr, c0, c1, c2, c3, c4, c5, c6, c7, r0, t0, t1, gHashPointer, byteReverseOrder, gHashExpandedKeyTable, todo, resl, resm, resh ); \
+    } \
+\
+    do \
+    { \
+        roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+        keyPtr ++; \
+        c0 = _mm256_aesenc_epi128( c0, roundkeys ); \
+        c1 = _mm256_aesenc_epi128( c1, roundkeys ); \
+        c2 = _mm256_aesenc_epi128( c2, roundkeys ); \
+        c3 = _mm256_aesenc_epi128( c3, roundkeys ); \
+        c4 = _mm256_aesenc_epi128( c4, roundkeys ); \
+        c5 = _mm256_aesenc_epi128( c5, roundkeys ); \
+        c6 = _mm256_aesenc_epi128( c6, roundkeys ); \
+        c7 = _mm256_aesenc_epi128( c7, roundkeys ); \
+    } while( keyPtr < keyLimit ); \
+\
+    roundkeys =  _mm256_broadcastsi128_si256( *( (const __m128i *) keyPtr ) ); \
+\
+    c0 = _mm256_aesenclast_epi128( c0, roundkeys ); \
+    c1 = _mm256_aesenclast_epi128( c1, roundkeys ); \
+    c2 = _mm256_aesenclast_epi128( c2, roundkeys ); \
+    c3 = _mm256_aesenclast_epi128( c3, roundkeys ); \
+    c4 = _mm256_aesenclast_epi128( c4, roundkeys ); \
+    c5 = _mm256_aesenclast_epi128( c5, roundkeys ); \
+    c6 = _mm256_aesenclast_epi128( c6, roundkeys ); \
+    c7 = _mm256_aesenclast_epi128( c7, roundkeys ); \
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptStitchedYmm_2048(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __m128i chain = _mm_loadu_si128( (__m128i *) pbChainingValue );
+
+    __m128i BYTE_REVERSE_ORDER_xmm = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+    __m256i BYTE_REVERSE_ORDER = _mm256_set_epi64x( 0x0001020304050607, 0x08090a0b0c0d0e0f, 0x0001020304050607, 0x08090a0b0c0d0e0f );
+    __m128i vMultiplicationConstant = _mm_set_epi32( 0, 0, 0xc2000000, 0 );
+
+    __m256i chainIncrementUpper1  = _mm256_set_epi64x( 0,  1, 0,  0 );
+    __m256i chainIncrement2  = _mm256_set_epi64x( 0,  2, 0,  2 );
+    __m256i chainIncrement4  = _mm256_set_epi64x( 0,  4, 0,  4 );
+    __m256i chainIncrement16 = _mm256_set_epi64x( 0, 16, 0, 16 );
+
+    __m256i ctr0, ctr1, ctr2, ctr3, ctr4, ctr5, ctr6, ctr7;
+    __m256i c0, c1, c2, c3, c4, c5, c6, c7;
+    __m256i r0, r1, r2, r3, r4, r5, r6, r7;
+    __m256i Hi, Hix;
+
+    __m128i state;
+    __m128i a0_xmm, a1_xmm, a2_xmm;
+    __m256i a0, a1, a2;
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+    PCBYTE pbGhashSrc = pbDst;
+
+    SYMCRYPT_ASSERT( (cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == 0 ); // cbData is multiple of block size
+    SYMCRYPT_ASSERT( nBlocks >= GCM_YMM_MINBLOCKS );
+
+    todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS ) & ~(GCM_YMM_MINBLOCKS-1);
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER_xmm );
+
+    state = _mm_loadu_si128( (__m128i *) pState );
+    ctr0 = _mm256_insertf128_si256( _mm256_castsi128_si256( chain ), chain, 1); // AVX
+    ctr0 = _mm256_add_epi32( ctr0, chainIncrementUpper1 );
+    ctr1 = _mm256_add_epi32( ctr0, chainIncrement2 );
+    ctr2 = _mm256_add_epi32( ctr0, chainIncrement4 );
+    ctr3 = _mm256_add_epi32( ctr1, chainIncrement4 );
+    ctr4 = _mm256_add_epi32( ctr2, chainIncrement4 );
+    ctr5 = _mm256_add_epi32( ctr3, chainIncrement4 );
+    ctr6 = _mm256_add_epi32( ctr4, chainIncrement4 );
+    ctr7 = _mm256_add_epi32( ctr5, chainIncrement4 );
+
+    CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0_xmm, a1_xmm, a2_xmm );
+    a0 = a1 = a2 = _mm256_setzero_si256();
+
+    c0 = _mm256_shuffle_epi8( ctr0, BYTE_REVERSE_ORDER );
+    c1 = _mm256_shuffle_epi8( ctr1, BYTE_REVERSE_ORDER );
+    c2 = _mm256_shuffle_epi8( ctr2, BYTE_REVERSE_ORDER );
+    c3 = _mm256_shuffle_epi8( ctr3, BYTE_REVERSE_ORDER );
+    c4 = _mm256_shuffle_epi8( ctr4, BYTE_REVERSE_ORDER );
+    c5 = _mm256_shuffle_epi8( ctr5, BYTE_REVERSE_ORDER );
+    c6 = _mm256_shuffle_epi8( ctr6, BYTE_REVERSE_ORDER );
+    c7 = _mm256_shuffle_epi8( ctr7, BYTE_REVERSE_ORDER );
+
+    ctr0 = _mm256_add_epi32( ctr0, chainIncrement16 );
+    ctr1 = _mm256_add_epi32( ctr1, chainIncrement16 );
+    ctr2 = _mm256_add_epi32( ctr2, chainIncrement16 );
+    ctr3 = _mm256_add_epi32( ctr3, chainIncrement16 );
+    ctr4 = _mm256_add_epi32( ctr4, chainIncrement16 );
+    ctr5 = _mm256_add_epi32( ctr5, chainIncrement16 );
+    ctr6 = _mm256_add_epi32( ctr6, chainIncrement16 );
+    ctr7 = _mm256_add_epi32( ctr7, chainIncrement16 );
+
+    AES_ENCRYPT_YMM_2048( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7 );
+
+    _mm256_storeu_si256( (__m256i *) (pbDst +  0), _mm256_xor_si256( c0, _mm256_loadu_si256( ( __m256i * ) (pbSrc +  0) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst + 32), _mm256_xor_si256( c1, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 32) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst + 64), _mm256_xor_si256( c2, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 64) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst + 96), _mm256_xor_si256( c3, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 96) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst +128), _mm256_xor_si256( c4, _mm256_loadu_si256( ( __m256i * ) (pbSrc +128) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst +160), _mm256_xor_si256( c5, _mm256_loadu_si256( ( __m256i * ) (pbSrc +160) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst +192), _mm256_xor_si256( c6, _mm256_loadu_si256( ( __m256i * ) (pbSrc +192) ) ) );
+    _mm256_storeu_si256( (__m256i *) (pbDst +224), _mm256_xor_si256( c7, _mm256_loadu_si256( ( __m256i * ) (pbSrc +224) ) ) );
+
+    pbDst  += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+    pbSrc  += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+
+    while( nBlocks >= 2*GCM_YMM_MINBLOCKS )
+    {
+        c0 = _mm256_shuffle_epi8( ctr0, BYTE_REVERSE_ORDER );
+        c1 = _mm256_shuffle_epi8( ctr1, BYTE_REVERSE_ORDER );
+        c2 = _mm256_shuffle_epi8( ctr2, BYTE_REVERSE_ORDER );
+        c3 = _mm256_shuffle_epi8( ctr3, BYTE_REVERSE_ORDER );
+        c4 = _mm256_shuffle_epi8( ctr4, BYTE_REVERSE_ORDER );
+        c5 = _mm256_shuffle_epi8( ctr5, BYTE_REVERSE_ORDER );
+        c6 = _mm256_shuffle_epi8( ctr6, BYTE_REVERSE_ORDER );
+        c7 = _mm256_shuffle_epi8( ctr7, BYTE_REVERSE_ORDER );
+
+        ctr0 = _mm256_add_epi32( ctr0, chainIncrement16 );
+        ctr1 = _mm256_add_epi32( ctr1, chainIncrement16 );
+        ctr2 = _mm256_add_epi32( ctr2, chainIncrement16 );
+        ctr3 = _mm256_add_epi32( ctr3, chainIncrement16 );
+        ctr4 = _mm256_add_epi32( ctr4, chainIncrement16 );
+        ctr5 = _mm256_add_epi32( ctr5, chainIncrement16 );
+        ctr6 = _mm256_add_epi32( ctr6, chainIncrement16 );
+        ctr7 = _mm256_add_epi32( ctr7, chainIncrement16 );
+
+        AES_GCM_ENCRYPT_16_Ymm( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pbGhashSrc, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+
+        _mm256_storeu_si256( (__m256i *) (pbDst +  0), _mm256_xor_si256( c0, _mm256_loadu_si256( ( __m256i * ) (pbSrc +  0) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst + 32), _mm256_xor_si256( c1, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 32) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst + 64), _mm256_xor_si256( c2, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 64) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst + 96), _mm256_xor_si256( c3, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 96) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +128), _mm256_xor_si256( c4, _mm256_loadu_si256( ( __m256i * ) (pbSrc +128) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +160), _mm256_xor_si256( c5, _mm256_loadu_si256( ( __m256i * ) (pbSrc +160) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +192), _mm256_xor_si256( c6, _mm256_loadu_si256( ( __m256i * ) (pbSrc +192) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +224), _mm256_xor_si256( c7, _mm256_loadu_si256( ( __m256i * ) (pbSrc +224) ) ) );
+
+        pbDst  += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbSrc  += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+        nBlocks -= 16;
+
+        if ( todo == 0 )
+        {
+            a0_xmm = _mm_xor_si128( a0_xmm, _mm256_extracti128_si256 ( a0, 0 /* Lowest 128 bits */ ));
+            a1_xmm = _mm_xor_si128( a1_xmm, _mm256_extracti128_si256 ( a1, 0 /* Lowest 128 bits */ ));
+            a2_xmm = _mm_xor_si128( a2_xmm, _mm256_extracti128_si256 ( a2, 0 /* Lowest 128 bits */ ));
+
+            a0_xmm = _mm_xor_si128( a0_xmm, _mm256_extracti128_si256 ( a0, 1 /* Highest 128 bits */ ));
+            a1_xmm = _mm_xor_si128( a1_xmm, _mm256_extracti128_si256 ( a1, 1 /* Highest 128 bits */ ));
+            a2_xmm = _mm_xor_si128( a2_xmm, _mm256_extracti128_si256 ( a2, 1 /* Highest 128 bits */ ));
+            CLMUL_3_POST( a0_xmm, a1_xmm, a2_xmm );
+            MODREDUCE( vMultiplicationConstant, a0_xmm, a1_xmm, a2_xmm, state );
+
+            todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS ) & ~(GCM_YMM_MINBLOCKS-1);
+            CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0_xmm, a1_xmm, a2_xmm );
+            a0 = a1 = a2 = _mm256_setzero_si256();
+        }
+    }
+
+    r0 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc +  0) ), BYTE_REVERSE_ORDER );
+    r1 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc + 32) ), BYTE_REVERSE_ORDER );
+    r2 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc + 64) ), BYTE_REVERSE_ORDER );
+    r3 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc + 96) ), BYTE_REVERSE_ORDER );
+    r4 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc +128) ), BYTE_REVERSE_ORDER );
+    r5 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc +160) ), BYTE_REVERSE_ORDER );
+    r6 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc +192) ), BYTE_REVERSE_ORDER );
+    r7 = _mm256_shuffle_epi8( _mm256_loadu_si256( (__m256i *) (pbGhashSrc +224) ), BYTE_REVERSE_ORDER );
+
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo - 0) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo - 0) );
+    CLMUL_ACC_3_Ymm( r0, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo - 2) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo - 2) );
+    CLMUL_ACC_3_Ymm( r1, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo - 4) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo - 4) );
+    CLMUL_ACC_3_Ymm( r2, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo - 6) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo - 6) );
+    CLMUL_ACC_3_Ymm( r3, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo - 8) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo - 8) );
+    CLMUL_ACC_3_Ymm( r4, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo -10) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo -10) );
+    CLMUL_ACC_3_Ymm( r5, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo -12) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo -12) );
+    CLMUL_ACC_3_Ymm( r6, Hi, Hix, a0, a1, a2 );
+    Hi  = _mm256_loadu_si256( (__m256i *)  &GHASH_H_POWER(expandedKeyTable, todo -14) );
+    Hix = _mm256_loadu_si256( (__m256i *) &GHASH_Hx_POWER(expandedKeyTable, todo -14) );
+    CLMUL_ACC_3_Ymm( r7, Hi, Hix, a0, a1, a2 );
+
+    a0_xmm = _mm_xor_si128( a0_xmm, _mm256_extracti128_si256 ( a0, 0 /* Lowest 128 bits */ ));
+    a1_xmm = _mm_xor_si128( a1_xmm, _mm256_extracti128_si256 ( a1, 0 /* Lowest 128 bits */ ));
+    a2_xmm = _mm_xor_si128( a2_xmm, _mm256_extracti128_si256 ( a2, 0 /* Lowest 128 bits */ ));
+
+    a0_xmm = _mm_xor_si128( a0_xmm, _mm256_extracti128_si256 ( a0, 1 /* Highest 128 bits */ ));
+    a1_xmm = _mm_xor_si128( a1_xmm, _mm256_extracti128_si256 ( a1, 1 /* Highest 128 bits */ ));
+    a2_xmm = _mm_xor_si128( a2_xmm, _mm256_extracti128_si256 ( a2, 1 /* Highest 128 bits */ ));
+    CLMUL_3_POST( a0_xmm, a1_xmm, a2_xmm );
+    MODREDUCE( vMultiplicationConstant, a0_xmm, a1_xmm, a2_xmm, state );
+
+    chain = _mm256_extracti128_si256 ( ctr0, 0 /* Lowest 128 bits */ );
+    _mm256_zeroupper();
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER_xmm );
+    _mm_storeu_si128((__m128i *) pbChainingValue, chain );
+    _mm_storeu_si128((__m128i *) pState, state );
+
+    cbData &= ( GCM_YMM_MINBLOCKS*SYMCRYPT_AES_BLOCK_SIZE ) - 1;
+    SYMCRYPT_ASSERT( cbData == (nBlocks-16)*SYMCRYPT_AES_BLOCK_SIZE );
+    if ( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesGcmEncryptStitchedXmm( pExpandedKey, pbChainingValue, expandedKeyTable, pState, pbSrc, pbDst, cbData);
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptStitchedYmm_2048(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData )
+{
+    __m128i chain = _mm_loadu_si128( (__m128i *) pbChainingValue );
+
+    __m128i BYTE_REVERSE_ORDER_xmm = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+    __m256i BYTE_REVERSE_ORDER = _mm256_set_epi64x( 0x0001020304050607, 0x08090a0b0c0d0e0f, 0x0001020304050607, 0x08090a0b0c0d0e0f );
+    __m128i vMultiplicationConstant = _mm_set_epi32( 0, 0, 0xc2000000, 0 );
+
+    __m256i chainIncrementUpper1  = _mm256_set_epi64x( 0,  1, 0,  0 );
+    __m256i chainIncrement2  = _mm256_set_epi64x( 0,  2, 0,  2 );
+    __m256i chainIncrement4  = _mm256_set_epi64x( 0,  4, 0,  4 );
+    __m256i chainIncrement16 = _mm256_set_epi64x( 0, 16, 0, 16 );
+
+    __m256i ctr0, ctr1, ctr2, ctr3, ctr4, ctr5, ctr6, ctr7;
+    __m256i c0, c1, c2, c3, c4, c5, c6, c7;
+
+    __m128i state;
+    __m128i a0_xmm, a1_xmm, a2_xmm;
+    __m256i a0, a1, a2;
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+    PCBYTE pbGhashSrc = pbSrc;
+
+    SYMCRYPT_ASSERT( (cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK) == 0 ); // cbData is multiple of block size
+    SYMCRYPT_ASSERT( nBlocks >= GCM_YMM_MINBLOCKS );
+
+    todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS ) & ~(GCM_YMM_MINBLOCKS-1);
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER_xmm );
+
+    state = _mm_loadu_si128( (__m128i *) pState );
+    ctr0 = _mm256_insertf128_si256( _mm256_castsi128_si256( chain ), chain, 1); // AVX
+    ctr0 = _mm256_add_epi32( ctr0, chainIncrementUpper1 );
+    ctr1 = _mm256_add_epi32( ctr0, chainIncrement2 );
+    ctr2 = _mm256_add_epi32( ctr0, chainIncrement4 );
+    ctr3 = _mm256_add_epi32( ctr1, chainIncrement4 );
+    ctr4 = _mm256_add_epi32( ctr2, chainIncrement4 );
+    ctr5 = _mm256_add_epi32( ctr3, chainIncrement4 );
+    ctr6 = _mm256_add_epi32( ctr4, chainIncrement4 );
+    ctr7 = _mm256_add_epi32( ctr5, chainIncrement4 );
+
+    CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0_xmm, a1_xmm, a2_xmm );
+    a0 = a1 = a2 = _mm256_setzero_si256();
+
+    while( nBlocks >= GCM_YMM_MINBLOCKS )
+    {
+        c0 = _mm256_shuffle_epi8( ctr0, BYTE_REVERSE_ORDER );
+        c1 = _mm256_shuffle_epi8( ctr1, BYTE_REVERSE_ORDER );
+        c2 = _mm256_shuffle_epi8( ctr2, BYTE_REVERSE_ORDER );
+        c3 = _mm256_shuffle_epi8( ctr3, BYTE_REVERSE_ORDER );
+        c4 = _mm256_shuffle_epi8( ctr4, BYTE_REVERSE_ORDER );
+        c5 = _mm256_shuffle_epi8( ctr5, BYTE_REVERSE_ORDER );
+        c6 = _mm256_shuffle_epi8( ctr6, BYTE_REVERSE_ORDER );
+        c7 = _mm256_shuffle_epi8( ctr7, BYTE_REVERSE_ORDER );
+
+        ctr0 = _mm256_add_epi32( ctr0, chainIncrement16 );
+        ctr1 = _mm256_add_epi32( ctr1, chainIncrement16 );
+        ctr2 = _mm256_add_epi32( ctr2, chainIncrement16 );
+        ctr3 = _mm256_add_epi32( ctr3, chainIncrement16 );
+        ctr4 = _mm256_add_epi32( ctr4, chainIncrement16 );
+        ctr5 = _mm256_add_epi32( ctr5, chainIncrement16 );
+        ctr6 = _mm256_add_epi32( ctr6, chainIncrement16 );
+        ctr7 = _mm256_add_epi32( ctr7, chainIncrement16 );
+
+        AES_GCM_ENCRYPT_16_Ymm( pExpandedKey, c0, c1, c2, c3, c4, c5, c6, c7, pbGhashSrc, BYTE_REVERSE_ORDER, expandedKeyTable, todo, a0, a1, a2 );
+
+        _mm256_storeu_si256( (__m256i *) (pbDst +  0), _mm256_xor_si256( c0, _mm256_loadu_si256( ( __m256i * ) (pbSrc +  0) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst + 32), _mm256_xor_si256( c1, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 32) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst + 64), _mm256_xor_si256( c2, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 64) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst + 96), _mm256_xor_si256( c3, _mm256_loadu_si256( ( __m256i * ) (pbSrc + 96) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +128), _mm256_xor_si256( c4, _mm256_loadu_si256( ( __m256i * ) (pbSrc +128) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +160), _mm256_xor_si256( c5, _mm256_loadu_si256( ( __m256i * ) (pbSrc +160) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +192), _mm256_xor_si256( c6, _mm256_loadu_si256( ( __m256i * ) (pbSrc +192) ) ) );
+        _mm256_storeu_si256( (__m256i *) (pbDst +224), _mm256_xor_si256( c7, _mm256_loadu_si256( ( __m256i * ) (pbSrc +224) ) ) );
+
+        pbDst  += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+        pbSrc  += 16 * SYMCRYPT_AES_BLOCK_SIZE;
+        nBlocks -= 16;
+
+        if ( todo == 0 )
+        {
+            a0_xmm = _mm_xor_si128( a0_xmm, _mm256_extracti128_si256 ( a0, 0 /* Lowest 128 bits */ ));
+            a1_xmm = _mm_xor_si128( a1_xmm, _mm256_extracti128_si256 ( a1, 0 /* Lowest 128 bits */ ));
+            a2_xmm = _mm_xor_si128( a2_xmm, _mm256_extracti128_si256 ( a2, 0 /* Lowest 128 bits */ ));
+
+            a0_xmm = _mm_xor_si128( a0_xmm, _mm256_extracti128_si256 ( a0, 1 /* Highest 128 bits */ ));
+            a1_xmm = _mm_xor_si128( a1_xmm, _mm256_extracti128_si256 ( a1, 1 /* Highest 128 bits */ ));
+            a2_xmm = _mm_xor_si128( a2_xmm, _mm256_extracti128_si256 ( a2, 1 /* Highest 128 bits */ ));
+            CLMUL_3_POST( a0_xmm, a1_xmm, a2_xmm );
+            MODREDUCE( vMultiplicationConstant, a0_xmm, a1_xmm, a2_xmm, state );
+
+            if ( nBlocks > 0 )
+            {
+                todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS ) & ~(GCM_YMM_MINBLOCKS-1);
+                CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0_xmm, a1_xmm, a2_xmm );
+                a0 = a1 = a2 = _mm256_setzero_si256();
+            }
+        }
+    }
+
+    chain = _mm256_extracti128_si256 ( ctr0, 0 /* Lowest 128 bits */ );
+    _mm256_zeroupper();
+
+    chain = _mm_shuffle_epi8( chain, BYTE_REVERSE_ORDER_xmm );
+    _mm_storeu_si128((__m128i *) pbChainingValue, chain );
+    _mm_storeu_si128((__m128i *) pState, state );
+
+    cbData &= ( GCM_YMM_MINBLOCKS*SYMCRYPT_AES_BLOCK_SIZE ) - 1;
+    SYMCRYPT_ASSERT( cbData == nBlocks*SYMCRYPT_AES_BLOCK_SIZE );
+    if ( cbData >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesGcmDecryptStitchedXmm( pExpandedKey, pbChainingValue, expandedKeyTable, pState, pbSrc, pbDst, cbData);
+    }
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif // CPU_X86 | CPU_AMD64
diff --git a/libs/symcrypt/lib/aesCtrDrbg.c b/libs/symcrypt/lib/aesCtrDrbg.c
new file mode 100644
index 000000000000..457e3f7fcd90
--- /dev/null
+++ b/libs/symcrypt/lib/aesCtrDrbg.c
@@ -0,0 +1,986 @@
+//
+// aesCtrDrbg.c   code for SP 800-90 AES-CTR-DRBG implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This code is derived from the implementation already in use in CNG.
+//
+
+#include "precomp.h"
+
+#define SYMCRYPT_RNG_AES_KEY_SIZE                   (32)
+#define SYMCRYPT_RNG_AES_KEY_AND_V_SIZE             (32 + 16)
+#define SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE           (1<<16)
+#define SYMCRYPT_RNG_AES_MAX_REQUESTS_PER_RESEED    ((UINT64)1<<48)
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesBcc(
+    _In_                                    PSYMCRYPT_AES_EXPANDED_KEY  pKey,
+    _In_reads_( cbData )                   PCBYTE                      pcbData,
+    _In_                                    SIZE_T                      cbData,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbResult )
+{
+    //
+    //Length of input should always be multiple of the AES block size
+    //
+    SYMCRYPT_ASSERT(cbData % SYMCRYPT_AES_BLOCK_SIZE == 0);
+
+    SymCryptWipe( pbResult, SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptAesCbcMac( pKey, pbResult, pcbData, cbData );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesDf(
+    _In_reads_(cbData)                                 PCBYTE  pcbData,
+    _In_                                                SIZE_T  cbData,
+    _Out_writes_(SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE)   PBYTE   pbSeed )
+{
+    //maximal input length + IV + padding
+    SYMCRYPT_ALIGN BYTE         buf[SYMCRYPT_RNG_AES_MAX_SEED_SIZE + 3 * SYMCRYPT_AES_BLOCK_SIZE];
+    PBYTE                       pb;
+    SIZE_T                      lenIvS;
+
+    SYMCRYPT_ALIGN BYTE         temp[SYMCRYPT_RNG_AES_KEY_AND_V_SIZE];
+    SYMCRYPT_AES_EXPANDED_KEY   aesKey;
+    PBYTE                       pX;
+
+    SIZE_T                      i;
+
+    C_ASSERT( sizeof( temp ) % SYMCRYPT_AES_BLOCK_SIZE == 0 );
+
+    //
+    // See SP800-90 section 10.4.2
+    //
+    // Our buf contains the following data:
+    // - 16 bytes IV
+    // - 4 bytes L
+    // - 4 bytes N
+    // - up to SEEDLEN bytes input data
+    // - 1 byte 0x80
+    // - zeroes to fill to a multiple of 16
+    //
+
+    SYMCRYPT_ASSERT(    cbData >= SYMCRYPT_RNG_AES_MIN_RESEED_SIZE &&
+                        cbData <= SYMCRYPT_RNG_AES_MAX_SEED_SIZE );
+
+    //
+    // Initialize the entire buf to zero
+    //
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    //
+    // build the string S in buf[16...]
+    //
+    pb = &buf[ SYMCRYPT_AES_BLOCK_SIZE ];
+
+    //
+    // Set L; SP800-90 isn't clear, but we'll use MSB first as that is what is used elsewhere.
+    //
+    SYMCRYPT_STORE_MSBFIRST32( pb, (UINT32) cbData );
+    pb += 4;
+
+    //
+    // Set N
+    //
+    SYMCRYPT_STORE_MSBFIRST32( pb, SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE );
+    pb += 4;
+
+    //
+    // Set input_string
+    //
+
+    memcpy( pb, pcbData, cbData );
+    pb += cbData;
+
+    //
+    // set padding
+    //
+    *pb++ = 0x80;
+
+    while( (pb - buf) % SYMCRYPT_AES_BLOCK_SIZE != 0 )
+    {
+#pragma prefast( suppress: 26015, "Logic why this doesn't overflow the buf[] array is too complicated for prefast" )
+        *pb++ = 0;
+    }
+
+    lenIvS = pb - buf;      // Length of IV & S together
+
+    //
+    // Set up the inital key
+    //
+
+    for( i = 0; i < SYMCRYPT_RNG_AES_KEY_SIZE; i++ )
+    {
+        temp[i] = (BYTE) i;
+    }
+    SymCryptAesExpandKeyEncryptOnly( &aesKey, temp, SYMCRYPT_RNG_AES_KEY_SIZE );
+
+
+    //
+    // Produce the 'temp' intermediate result.
+    //
+
+    for( i=0; i< SYMCRYPT_RNG_AES_KEY_AND_V_SIZE / SYMCRYPT_AES_BLOCK_SIZE; i++ )
+    {
+        //
+        // Update the IV with the right i value.
+        // i is only 0-2, so we only have to set a single byte
+        //
+        buf[3] = (BYTE) i;
+
+        //
+        // Now we perform the BCC function, which is just CbcMac
+        // BCC(K,(IV||S))
+        SymCryptRngAesBcc( &aesKey, buf, lenIvS, &temp[ i * SYMCRYPT_AES_BLOCK_SIZE ] );
+     }
+
+    //
+    // Second phase, produce the actual output
+    //
+    SymCryptAesExpandKeyEncryptOnly( &aesKey, temp, SYMCRYPT_RNG_AES_KEY_SIZE );
+    pX = &temp[SYMCRYPT_RNG_AES_KEY_SIZE];
+
+    for( i=0; i < SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE; i += SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptAesEncrypt( &aesKey, pX, pX );
+        memcpy( &pbSeed[ i ], pX, SYMCRYPT_AES_BLOCK_SIZE );
+    }
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    SymCryptWipeKnownSize( temp, sizeof( temp ) );
+    SymCryptWipeKnownSize( &aesKey, sizeof( aesKey ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesGenerateBlocks(
+    _In_                                            PSYMCRYPT_AES_EXPANDED_KEY  pAesKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )      PBYTE                       pV,
+    _Out_writes_(cbRandom)                          PBYTE                       pbRandom,
+    _In_                                            SIZE_T                      cbRandom )
+//
+// Internal function to generate output blocks from the state.
+// cbRandom must be a multiple of the block size.
+//
+{
+    UINT64  v;
+    SIZE_T  cBlocks;
+    SIZE_T  blocksToDo;
+    SIZE_T  bytesToDo;
+
+//
+// The roll-over of the counter is hard to test, especially since our
+// NIST test vectors only cover small outputs.
+// We have an option to test the output against a simpler (older) implementation
+// to validate the proper working of the code.
+//
+#define TEST_AGAINST_OLD_CODE   0
+#if TEST_AGAINST_OLD_CODE
+    BYTE    Vcopy[16];
+    BYTE    buf[16];
+    PCBYTE  pbCheck = pbRandom;
+    SIZE_T  cbCheck = cbRandom;
+
+    memcpy( Vcopy, pV, 16 );
+#endif
+
+    //
+    // cbRandom must be a multiple of BLOCK_LEN and > 0.
+    //
+    SYMCRYPT_ASSERT( (cbRandom & (SYMCRYPT_AES_BLOCK_SIZE-1)) == 0 );
+
+    cBlocks = cbRandom / SYMCRYPT_AES_BLOCK_SIZE;
+
+    //
+    // We violate the write-once rule here by wiping the output buffer and then
+    // filling it with the CTR-mode encryption.
+    // This is safe because the caller only learns the proper output anyway.
+    //
+    SymCryptWipe( pbRandom, cbRandom );
+
+    //
+    // This loop is a little complicated because we need to pre-increment the 128-bit value V
+    // and the SymCryptAesCtrMsb64 function does a 64-bit post-increment.
+    //
+    while( cBlocks != 0 )
+    {
+        // Increment V
+        v = SYMCRYPT_LOAD_MSBFIRST64( &pV[8] ) + 1;
+        SYMCRYPT_STORE_MSBFIRST64( &pV[8], v );
+        SYMCRYPT_STORE_MSBFIRST64( &pV[0], SYMCRYPT_LOAD_MSBFIRST64( &pV[0] ) + (v == 0) );
+
+        //
+        // The SymCryptAesCtrMsb64 routine will increment the last 64 bits of the V value,
+        // but not handle the carry to the first 64 bits.
+        // We limit how many block we do so that we never cross this boundary.
+        // SymCryptAesCtrMsb64 does a post-increment, so it may increment the last 64 bits
+        // to zero as long as we don't rely on the V value afterwards.
+        // As one-in-2^64 code is not testable, we terminate the Msb64 call earlier, and
+        // much earlier on CHKed builds.
+        //
+#if SYMCRYPT_DEBUG
+#define MAX_CTRMSB64_BLOCKS (1 << 3)        // very small; overflow will be triggered by any reasonable test
+#else
+#define MAX_CTRMSB64_BLOCKS (1 << 10)         // increase when we have this well-tested
+#endif
+        //
+        // 1 + (~v & mask) is the value you can add to v so that the mask bits of the sum
+        // end up to be zero. It is in the range 1 .. mask+1
+        //
+        blocksToDo = SYMCRYPT_MIN( cBlocks, 1 + ( (~v) & (MAX_CTRMSB64_BLOCKS - 1) ) );
+
+        bytesToDo = blocksToDo * SYMCRYPT_AES_BLOCK_SIZE;
+        SYMCRYPT_ASSERT( bytesToDo <= cbRandom );
+        SymCryptAesCtrMsb64( pAesKey, &pV[0], pbRandom, pbRandom, bytesToDo );
+        pbRandom += bytesToDo;
+        cbRandom -= bytesToDo;          // only used for prefast assertions; optimized away in shipping code
+        cBlocks -= blocksToDo;
+
+        //
+        // Post-decrement the V block to compensate for the post-increment of the Msb64 function
+        //
+        v += blocksToDo - 1;
+        SYMCRYPT_ASSERT( v != 0 );
+
+        SYMCRYPT_STORE_MSBFIRST64( &pV[8], v );
+        // No need to carry to the first half of V here, it cannot happen
+    }
+
+#if TEST_AGAINST_OLD_CODE
+    //
+    // We tried to use the CtrMsb64 mode to generate the blocks, but that leads to
+    // a number of complications.
+    // The lack of carry means we end up with code paths that run once per 2^64 blocks
+    // or so, and that is very hard to test.
+    // Furthermore, CtrMsb64 uses post-increment, whereas AES-CTR_DRBG uses pre-increment.
+    // That adds sufficient extra complications and testing problems that we went back
+    // to the solution below.
+    //
+
+    while( cbCheck != 0 )
+    {
+        SYMCRYPT_ASSERT( cbCheck >= SYMCRYPT_AES_BLOCK_SIZE );     // Keep prefast happy
+        //
+        // Increment the 128-bit block V MSByte first.
+        //
+        v = SYMCRYPT_LOAD_MSBFIRST64( &Vcopy[8] ) + 1;
+        SYMCRYPT_STORE_MSBFIRST64( &Vcopy[8], v );
+        if( v == 0 )
+        {
+            //
+            // This almost never happens.
+            // Using an if() is not side-channel safe, but in this case
+            // the side channel does not reveal anything that actually hurts the
+            // security of the algorithm.
+            //
+            SYMCRYPT_STORE_MSBFIRST64( Vcopy, 1 + LOAD_MSBFIRST64( Vcopy ) );
+        }
+
+        SymCryptAesEncrypt( pAesKey, Vcopy, buf );
+        if( memcmp( buf, pbCheck, 16 ) != 0 )
+        {
+            SymCryptFatal( 'OLD?' );
+        }
+        pbCheck += SYMCRYPT_AES_BLOCK_SIZE;
+        cbCheck -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+#endif
+}
+
+FORCEINLINE
+int
+SymCryptRngAesAreBlocksIdentical(
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE ) PCBYTE  pSrc1,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE ) PCBYTE  pSrc2 )
+//
+// return 1 if the blocks are identical, 0 if they are different.
+//
+{
+    SYMCRYPT_UNALIGNED const SIZE_T * p1 = (SYMCRYPT_UNALIGNED const SIZE_T *) pSrc1;
+    SYMCRYPT_UNALIGNED const SIZE_T * p2 = (SYMCRYPT_UNALIGNED const SIZE_T *) pSrc2;
+
+    SIZE_T tmp;
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM
+
+    C_ASSERT( sizeof( SIZE_T ) == 4 );
+    tmp = (p1[0] ^ p2[0]) | (p1[1] ^ p2[1]) | (p1[2] ^ p2[2]) | (p1[3] ^ p2[3]);
+
+#elif SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+
+    C_ASSERT( sizeof( SIZE_T ) == 8 );
+    tmp = (p1[0] ^ p2[0]) | (p1[1] ^ p2[1]);
+
+#else
+
+    SIZE_T i;
+
+    C_ASSERT( 16 % sizeof( SIZE_T ) == 0 );
+
+    tmp = 0;
+    for( i=0; i < 16/sizeof( SIZE_T ); i ++ )
+    {
+        tmp |= p1[i] ^ p2[i];
+    }
+
+#endif
+
+    return tmp == 0;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesCheckBlocksNotIdentical(
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE   pbPreviousBlock,
+    _In_reads_( cbData )                        PCBYTE  pcbData,
+                                                SIZE_T  cbData )
+{
+    SIZE_T identical;
+    SIZE_T i;
+
+    SYMCRYPT_ASSERT( ((cbData & 15) == 0) && cbData > 0 );
+
+    identical = SymCryptRngAesAreBlocksIdentical( pbPreviousBlock, pcbData );
+
+    for( i = SYMCRYPT_AES_BLOCK_SIZE; i < cbData; i += SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SYMCRYPT_ASSERT( cbData >= i + SYMCRYPT_AES_BLOCK_SIZE );
+        identical |= SymCryptRngAesAreBlocksIdentical( &pcbData[i-SYMCRYPT_AES_BLOCK_SIZE], &pcbData[ i ] );
+    }
+
+    memcpy( pbPreviousBlock, &pcbData[cbData - SYMCRYPT_AES_BLOCK_SIZE], SYMCRYPT_AES_BLOCK_SIZE );
+
+    //
+    // The structure of AES-CTR-DRBG makes it impossible for two consecutive blocks of a single request
+    // to be equal. The only way this could happen is if the first block of one request is the same as
+    // the last block of the previous request. But the probability of this happening is 2^{-128}.
+    // This never happens, so the whole check is technically useless.
+    // Nevertheless, it is required by FIPS 140-2, so we have to implement it,
+    // but we don't have to handle the error usefully in any way.
+    // (Trying to handle this error sensibly is far too complicated, and adds far more danger from code
+    // bugs than it is worth. It is much better to just treat it as a fatal occurrence.)
+    //
+
+    if( identical )
+    {
+        SymCryptFatal( 'acdi' );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesUpdate(
+    _Inout_                                                 PSYMCRYPT_RNG_AES_STATE     pState,
+    _In_reads_opt_( SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE )   PCBYTE                      pbProvidedData,
+    _In_opt_                                                PSYMCRYPT_AES_EXPANDED_KEY  pAesKey)
+//
+// Implement the CTR_DRBG Update function.
+// pbProvidedData is optional, but if provided must always be exactly seedlen bits.
+// pAesKey is the already expanded key of the RngState. This is optional, and only has
+// to be provided if the caller already has it.
+//
+{
+    SYMCRYPT_AES_EXPANDED_KEY   aesKey;
+    PSYMCRYPT_AES_EXPANDED_KEY  pKey;
+    SYMCRYPT_ALIGN  BYTE        buf[SYMCRYPT_AES_BLOCK_SIZE];
+
+    if(NULL == pAesKey)
+    {
+        SymCryptAesExpandKeyEncryptOnly( &aesKey, pState->keyAndV, SYMCRYPT_RNG_AES_KEY_SIZE );
+        pKey = &aesKey;
+    }
+    else
+    {
+        pKey = pAesKey;
+    }
+
+    //
+    // Copy the V value so that we can overwrite it safely.
+    //
+
+    memcpy( buf, &pState->keyAndV[SYMCRYPT_RNG_AES_KEY_SIZE], sizeof( buf ) );
+
+    SymCryptRngAesGenerateBlocks(
+            pKey,
+            buf,                            // pV
+            pState->keyAndV,                // pbRandom
+            sizeof( pState->keyAndV) );     // cbRandom
+
+    if( pbProvidedData != NULL )
+    {
+        // XOR provided data in
+        SymCryptXorBytes( pState->keyAndV, pbProvidedData, pState->keyAndV, SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE );
+    }
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    //
+    // Only wipe the key if necessary.
+    //
+    if( pKey == &aesKey )
+    {
+        SymCryptWipeKnownSize( &aesKey, sizeof( aesKey ));
+    }
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesGenerateSmall(
+    _Inout_                             PSYMCRYPT_RNG_AES_STATE pRngState,
+    _Out_writes_( cbRandom )            PBYTE                   pbRandom,
+                                        SIZE_T                  cbRandom,
+    _In_reads_opt_( cbAdditionalInput ) PCBYTE                  pbAdditionalInput,
+                                        SIZE_T                  cbAdditionalInput )
+//
+// This is the Generate function of our SP800-90 compliant implementation.
+// It follows the method specified in SP800-90A 10.2.1.5.2
+//
+{
+    SYMCRYPT_AES_EXPANDED_KEY   aesKey;
+    SYMCRYPT_ALIGN BYTE         buf[SYMCRYPT_AES_BLOCK_SIZE];
+    SYMCRYPT_ALIGN BYTE         abSeed[SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE];
+
+    //
+    // SP 800-90 9.3.1 requires a check on the length of the request.
+    //
+    if( cbRandom > SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE )
+    {
+        return SYMCRYPT_WRONG_DATA_SIZE;
+    }
+    //
+    // The requestCounter test is useless as it can never happen. (It would require
+    // 2^48 calls to this function to trigger this error.)
+    // Unfortunately, SP800-90 section 11 requires a test of this error, so we have
+    // to implement the error.
+    //
+    if( pRngState->requestCounter > SYMCRYPT_RNG_AES_MAX_REQUESTS_PER_RESEED )
+    {
+        return SYMCRYPT_FIPS_FAILURE;
+    }
+
+    if( pbAdditionalInput != NULL )
+    {
+        // Update additional input using Derivation function
+        SymCryptRngAesDf( pbAdditionalInput, cbAdditionalInput, abSeed );
+        pbAdditionalInput = &abSeed[0];
+
+        // Update state with modified additional input
+        SymCryptRngAesUpdate( pRngState, pbAdditionalInput, NULL );
+    }
+
+    SymCryptAesExpandKeyEncryptOnly( &aesKey, pRngState->keyAndV, SYMCRYPT_RNG_AES_KEY_SIZE );
+
+    if( cbRandom >= SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SIZE_T wholeBlocks = cbRandom & ~(SYMCRYPT_AES_BLOCK_SIZE - 1);
+        SymCryptRngAesGenerateBlocks(   &aesKey,
+                                        &pRngState->keyAndV[ SYMCRYPT_RNG_AES_KEY_SIZE],
+                                        pbRandom,
+                                        wholeBlocks );
+        if( pRngState->fips140_2Check )
+        {
+            SymCryptRngAesCheckBlocksNotIdentical( pRngState->previousBlock, pbRandom, wholeBlocks );
+        }
+        pbRandom += wholeBlocks;
+        cbRandom -= wholeBlocks;
+    }
+
+    if( cbRandom > 0 )
+    {
+        SYMCRYPT_ASSERT( cbRandom < SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptRngAesGenerateBlocks(   &aesKey,
+                                        &pRngState->keyAndV[ SYMCRYPT_RNG_AES_KEY_SIZE],
+                                        buf,
+                                        sizeof( buf ) );
+        if( pRngState->fips140_2Check )
+        {
+            SymCryptRngAesCheckBlocksNotIdentical( pRngState->previousBlock, buf, sizeof( buf ) );
+        }
+
+        memcpy( pbRandom, buf, cbRandom );
+        SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    }
+
+    SymCryptRngAesUpdate( pRngState, pbAdditionalInput, &aesKey );
+
+    ++pRngState->requestCounter;
+
+    SymCryptWipeKnownSize( &aesKey, sizeof( aesKey ) );
+    SymCryptWipeKnownSize( abSeed, sizeof( abSeed ) );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+_Use_decl_annotations_
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesInstantiate(  PSYMCRYPT_RNG_AES_STATE pRngState,
+                            PCBYTE                  pcbSeedMaterial,
+                            SIZE_T                  cbSeedMaterial )
+//
+// This function creates a new SP 800-90 AES_CTR_DRBG instance.
+// Our code is structured differently from what SP 800-90 assumes.
+// At this point in time, the entropy has already been collected and it is
+// passed to this function. Thus, there is no check for failing to get
+// the entropy. If entropy collection fails, the caller of this function
+// will generate an error. (Actually, we only choose to instantiate a FIPS-compliant
+// SP 800-90 DRBG when we do have good entropy available, so there is never an
+// error that we don't have the required entropy.)
+//
+{
+    if( cbSeedMaterial < SYMCRYPT_RNG_AES_MIN_INSTANTIATE_SIZE )
+    {
+        return SYMCRYPT_EXTERNAL_FAILURE;
+    }
+
+    //
+    // Instantiation of a new state is identical to setting the state to zero
+    // and then performing a reseed with the same seed material.
+    //
+    // See SP 800-90 10.2.1.3.2 & 10.2.1.4.2
+    //
+    SymCryptWipeKnownSize( pRngState, sizeof( *pRngState ) );
+
+    SYMCRYPT_SET_MAGIC( pRngState );
+
+    return SymCryptRngAesReseed( pRngState, pcbSeedMaterial, cbSeedMaterial );
+}
+
+_Use_decl_annotations_
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesGenerate( PSYMCRYPT_RNG_AES_STATE pRngState,
+                        PBYTE                   pbRandom,
+                        SIZE_T                  cbRandom )
+//
+// For FIPS compliance purposes, this is NOT the generate function of the DRBG.
+// The generate function is SymCryptRngAesGenerateSmall.
+// This is a wrapper around the generate function that supports larger output
+// sizes, and handles any errors by making them fatal.
+//
+{
+    SYMCRYPT_ERROR scError;
+
+    SYMCRYPT_CHECK_MAGIC( pRngState );
+
+    while( cbRandom > SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE )
+    {
+
+        scError = SymCryptRngAesGenerateSmall( pRngState, pbRandom, SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE, NULL, 0 );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            SymCryptFatal( 'acdx' );
+        }
+        pbRandom += SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE;
+        cbRandom -= SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE;
+    }
+
+    if( cbRandom > 0 )
+    {
+        scError = SymCryptRngAesGenerateSmall( pRngState, pbRandom, cbRandom, NULL, 0 );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            SymCryptFatal( 'acdx' );
+        }
+    }
+}
+
+_Use_decl_annotations_
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesReseed(   PSYMCRYPT_RNG_AES_STATE pRngState,
+                        PCBYTE                  pcbSeedMaterial,
+                        SIZE_T                  cbSeedMaterial )
+{
+    SYMCRYPT_ALIGN BYTE    abSeed[SYMCRYPT_RNG_AES_INTERNAL_SEED_SIZE];
+
+    SYMCRYPT_CHECK_MAGIC( pRngState );
+
+    //
+    // For a reseed, the minimum # bits is the security strength, or the key size.
+    // We retain the same maximum as that protects our own internal buffers.
+    //
+    if (cbSeedMaterial < SYMCRYPT_RNG_AES_MIN_RESEED_SIZE ||
+        cbSeedMaterial > SYMCRYPT_RNG_AES_MAX_SEED_SIZE )
+    {
+        return SYMCRYPT_EXTERNAL_FAILURE;     // bug is external to SymCrypt (i.e. the caller)
+    }
+
+    //
+    // We do not perform the FIPS-required reseed self-test here.
+    // Rather, we have a function that external callers can use to implement that test before
+    // calling this reseed function.
+    // This allows callers that are not interested in FIPS certification to skip the test.
+    //
+
+    SymCryptRngAesDf( pcbSeedMaterial, cbSeedMaterial, abSeed );
+
+    SymCryptRngAesUpdate( pRngState, abSeed, NULL );
+
+    pRngState->requestCounter = 1;
+
+    SymCryptWipeKnownSize( abSeed, sizeof( abSeed ) );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesUninstantiate(    PSYMCRYPT_RNG_AES_STATE pRngState )
+{
+    SymCryptWipeKnownSize( pRngState, sizeof( *pRngState ) );
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Self test
+
+//
+// The test vector is from the NIST DRBG Test Vectors file
+//
+static const BYTE g_abInstantiateEntropyInputPlusNonce[] =
+{
+    // Entropy input
+
+    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+    0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+    0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
+    0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+    0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+    0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+
+    // Nonce
+    0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+    0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+
+};
+
+
+static const BYTE g_abReseedEntropy[] =
+{
+
+   0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
+   0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+   0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
+   0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+   0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
+   0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF
+};
+
+static const BYTE g_abOutput1[ 32 ] =
+{
+   0xD1,0xE9,0xC7,0x37,0xB6,0xEB,0xAE,0xD7,
+   0x65,0xA0,0xD4,0xE4,0xC6,0xEA,0xEB,0xE2,
+   0x67,0xF5,0xE9,0x19,0x36,0x80,0xFD,0xFF,
+   0xA6,0x2F,0x48,0x65,0xB3,0xF0,0x09,0xEC,
+};
+
+static const BYTE g_expectedStateAfterInstantiate[ SYMCRYPT_RNG_AES_KEY_AND_V_SIZE ] =
+{
+    //key
+    0x8C,0x10,0xB6,0x58,0x44,0x0C,0x71,0x35,
+    0x64,0x9D,0xC7,0x7B,0xE6,0xE5,0x75,0xCE,
+    0x87,0xE7,0x48,0x90,0x83,0x9B,0x89,0x59,
+    0x14,0x17,0xAF,0xAD,0x14,0xB2,0x26,0xD5,
+    //V
+    0xB4,0x03,0x6B,0x1D,0xBA,0x04,0x3A,0xE6,
+    0x55,0xAC,0xD6,0x46,0xEC,0x5A,0xD3,0x5C,
+};
+
+static const BYTE g_expectedStateAfterReseed[ SYMCRYPT_RNG_AES_KEY_AND_V_SIZE ] =
+{
+    //key
+    0x17,0x98,0xC0,0xDF,0x09,0x69,0x6A,0x46,
+    0x19,0x46,0xFE,0x6D,0x68,0x7D,0x8C,0xC8,
+    0x3F,0xEE,0xF1,0x22,0xF3,0xBB,0xC5,0xF2,
+    0x9D,0xAC,0x85,0x10,0xF3,0x4A,0xF0,0x15,
+    //V
+    0x0B,0xF3,0x34,0x4D,0xF5,0x29,0x27,0x6B,
+    0x0D,0x5B,0xBC,0x83,0x9B,0xD3,0x65,0x6A,
+};
+
+static const BYTE g_expectedStateAfterGenerate[ SYMCRYPT_RNG_AES_KEY_AND_V_SIZE ] =
+{
+    //key
+    0x28, 0xbc, 0x65, 0xa8, 0x6a, 0xb7, 0xc7, 0x4e, 0xdf, 0x4b, 0xb8, 0x72, 0x87, 0xd3, 0x4f, 0xbb,
+    0x8d, 0x6f, 0x16, 0xd7, 0xb9, 0x1b, 0x6a, 0xbb, 0xee, 0x7b, 0x88, 0x86, 0x5b, 0x0f, 0xc7, 0xbd,
+    //V
+    0xb7, 0x46, 0x11, 0xf3, 0x92, 0x95, 0xa6, 0x25, 0x7c, 0x39, 0x98, 0x4c, 0x9c, 0x09, 0x9b, 0x30,
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesTestInstantiate( PSYMCRYPT_RNG_AES_STATE pRngState )
+//
+// Test the Instantiate function on the passed instance. Leave it
+// in the initialized state for the test vector.
+//
+{
+    SYMCRYPT_ERROR scError;
+    //
+    // First test the error handling
+    //
+#pragma prefast( suppress: 26060 6309 28020, "Deliberate test of invalid parameter");
+    scError = SymCryptRngAesInstantiate( pRngState, NULL, 327 );
+    if( scError == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'aci1' );
+    }
+
+
+    scError = SymCryptRngAesInstantiate( pRngState,
+                                         g_abInstantiateEntropyInputPlusNonce,
+                                         sizeof( g_abInstantiateEntropyInputPlusNonce )
+                                         );
+
+    SymCryptInjectError( pRngState->keyAndV, SYMCRYPT_RNG_AES_KEY_AND_V_SIZE );
+
+    if ( scError != SYMCRYPT_NO_ERROR ||
+         0 != memcmp( pRngState->keyAndV,
+                        g_expectedStateAfterInstantiate,
+                        SYMCRYPT_RNG_AES_KEY_AND_V_SIZE ))
+    {
+        SymCryptFatal( 'aci2' );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesTestReseed( PSYMCRYPT_RNG_AES_STATE pRngState )
+{
+    SYMCRYPT_ERROR scError;
+
+    //
+    // Set the state to a known state
+    //
+    SYMCRYPT_SET_MAGIC( pRngState );
+    memcpy( pRngState->keyAndV, g_expectedStateAfterInstantiate, SYMCRYPT_RNG_AES_KEY_AND_V_SIZE );
+    pRngState->requestCounter = 7;
+    pRngState->fips140_2Check = FALSE;
+
+    //
+    // Test error handling
+    //
+#pragma prefast(suppress: 26060 6309 28020, "Deliberate test of invalid parameter");
+    scError = SymCryptRngAesReseed( pRngState, NULL, 597 );
+    if( scError == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'acr1' );
+    }
+
+    scError = SymCryptRngAesReseed( pRngState, g_abReseedEntropy, sizeof( g_abReseedEntropy ) );
+
+    SymCryptInjectError( pRngState->keyAndV, SYMCRYPT_RNG_AES_KEY_AND_V_SIZE );
+
+    if ( scError != SYMCRYPT_NO_ERROR ||
+         0 != memcmp( pRngState->keyAndV,
+                        g_expectedStateAfterReseed,
+                        SYMCRYPT_RNG_AES_KEY_AND_V_SIZE ) )
+    {
+        SymCryptFatal( 'acr2' );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesTestGenerate( PSYMCRYPT_RNG_AES_STATE pRngState )
+{
+    BYTE            abOutput[2*SYMCRYPT_AES_BLOCK_SIZE];
+    SYMCRYPT_ERROR  scError;
+
+    //
+    // Set the state to a known value
+    //
+    SYMCRYPT_SET_MAGIC( pRngState );
+    memcpy( pRngState->keyAndV, g_expectedStateAfterReseed, SYMCRYPT_RNG_AES_KEY_AND_V_SIZE );
+    pRngState->requestCounter = 7;
+    pRngState->fips140_2Check = FALSE;
+
+    //
+    // Test the error handling
+    // - Too many requests since last reseed
+    // - Too many bytes in request
+    //
+
+    pRngState->requestCounter = SYMCRYPT_RNG_AES_MAX_REQUESTS_PER_RESEED + 1;
+    scError = SymCryptRngAesGenerateSmall( pRngState, abOutput, sizeof( g_abOutput1 ), NULL, 0 );
+
+    if( scError == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'acg1' );
+    }
+    pRngState->requestCounter = 7;
+
+#pragma prefast( suppress: 6202 26000, "buffer size of cbOutput is purposely incorrect");
+    scError = SymCryptRngAesGenerateSmall( pRngState, abOutput, SYMCRYPT_RNG_AES_MAX_REQUEST_SIZE + 1, NULL, 0 );
+
+    if( scError == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'acg2' );
+    }
+
+    //
+    // Now test for correct output data.
+    //
+    scError = SymCryptRngAesGenerateSmall( pRngState, abOutput, sizeof( g_abOutput1 ), NULL, 0 );
+
+    SymCryptInjectError( abOutput, sizeof( abOutput ) );
+
+    if( scError != SYMCRYPT_NO_ERROR || memcmp( abOutput, g_abOutput1, sizeof( g_abOutput1 ) ) != 0 )
+    {
+        SymCryptFatal( 'acg3' );
+    }
+
+    //
+    // And test for the correct resulting state
+    //
+    SymCryptInjectError( pRngState->keyAndV, SYMCRYPT_RNG_AES_KEY_AND_V_SIZE );
+
+    if ( 0 != memcmp( pRngState->keyAndV,
+                        g_expectedStateAfterGenerate,
+                        SYMCRYPT_RNG_AES_KEY_AND_V_SIZE ) )
+    {
+        SymCryptFatal( 'acg4' );
+    }
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesTestUninstantiate( PSYMCRYPT_RNG_AES_STATE pRngState )
+{
+    const SIZE_T * p = (const SIZE_T *) pRngState;
+    SIZE_T t;
+    SIZE_T i;
+
+    C_ASSERT( sizeof( *pRngState ) % sizeof( SIZE_T ) == 0 );   // This is true on all our platforms.
+
+    SYMCRYPT_CHECK_MAGIC( pRngState );
+
+    SymCryptRngAesUninstantiate( pRngState );
+
+    t = 0;
+    for( i=0; i< sizeof( *pRngState ) / sizeof( SIZE_T ); i ++ )
+    {
+        t |= p[i];
+    }
+
+    if( t != 0 )
+    {
+        SymCryptFatal( 'acdu' );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesInstantiateSelftest(void)
+{
+    SYMCRYPT_RNG_AES_STATE rng;
+
+    SymCryptRngAesTestInstantiate( &rng );
+
+    //
+    // Uninstantiate has to be tested whenever another function is tested.
+    //
+    SymCryptRngAesTestUninstantiate( &rng );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesReseedSelftest(void)
+{
+    SYMCRYPT_RNG_AES_STATE rng;
+
+    SymCryptRngAesTestReseed( &rng );
+
+    //
+    // Uninstantiate has to be tested whenever another function is tested.
+    //
+    SymCryptRngAesTestUninstantiate( &rng );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesGenerateSelftest(void)
+{
+    SYMCRYPT_RNG_AES_STATE rng;
+
+    SymCryptRngAesTestGenerate( &rng );
+
+    //
+    // Uninstantiate has to be tested whenever another function is tested.
+    //
+    SymCryptRngAesTestUninstantiate( &rng );
+}
+
+
+///////////////////////////////////////////////////////////////////
+// AES-CTR_DRGB with FIPS 140-2 continuous self-test
+//
+
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Instantiate( PSYMCRYPT_RNG_AES_FIPS140_2_STATE   pRngState,
+                                    PCBYTE                              pcbSeedMaterial,
+                                    SIZE_T                              cbSeedMaterial )
+{
+    SYMCRYPT_ERROR scError;
+
+    scError = SymCryptRngAesInstantiate( &pRngState->rng, pcbSeedMaterial, cbSeedMaterial );
+
+    if( scError == SYMCRYPT_NO_ERROR )
+    {
+        //
+        // Generate the first block of output and store it so that we can compare future blocks.
+        //
+        SymCryptRngAesGenerate( &pRngState->rng, pRngState->rng.previousBlock, sizeof( pRngState->rng.previousBlock ) );
+        pRngState->rng.fips140_2Check = TRUE;
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Generate(    PSYMCRYPT_RNG_AES_FIPS140_2_STATE   pRngState,
+                                    PBYTE                               pbRandom,
+                                    SIZE_T                              cbRandom )
+{
+    SymCryptRngAesGenerate( &pRngState->rng, pbRandom, cbRandom );
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Reseed(  PSYMCRYPT_RNG_AES_FIPS140_2_STATE   pRngState,
+                                PCBYTE                              pcbSeedMaterial,
+                                SIZE_T                              cbSeedMaterial )
+{
+    return SymCryptRngAesReseed( &pRngState->rng, pcbSeedMaterial, cbSeedMaterial );
+}
+
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptRngAesFips140_2Uninstantiate(   PSYMCRYPT_RNG_AES_FIPS140_2_STATE   pRngState )
+{
+    SymCryptRngAesUninstantiate( &pRngState->rng );
+}
diff --git a/libs/symcrypt/lib/aescmac.c b/libs/symcrypt/lib/aescmac.c
new file mode 100644
index 000000000000..7af0e31bcd94
--- /dev/null
+++ b/libs/symcrypt/lib/aescmac.c
@@ -0,0 +1,258 @@
+//
+// aescmac.c    Implementation of the AES-CMAC block cipher mode
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+const SYMCRYPT_MAC SymCryptAesCmacAlgorithm_fast = {
+    SymCryptAesCmacExpandKey,
+    SymCryptAesCmacInit,
+    SymCryptAesCmacAppend,
+    SymCryptAesCmacResult,
+    sizeof(SYMCRYPT_AES_CMAC_EXPANDED_KEY),
+    sizeof(SYMCRYPT_AES_CMAC_STATE),
+    SYMCRYPT_AES_CMAC_RESULT_SIZE,
+    NULL,
+    0,
+};
+
+const PCSYMCRYPT_MAC SymCryptAesCmacAlgorithm = &SymCryptAesCmacAlgorithm_fast;
+
+VOID
+SYMCRYPT_CALL
+SymCryptCmacMunge(
+    _Inout_updates_bytes_(SYMCRYPT_AES_BLOCK_SIZE)  BYTE    buf[SYMCRYPT_AES_BLOCK_SIZE] )
+{
+    SIZE_T carry = 0;
+    SIZE_T tmp;
+    int i;
+
+    for( i=15; i>=0; i-- )
+    {
+        tmp = buf[i];
+        buf[i] = ((tmp << 1) | carry) & 0xff;
+        carry = tmp >> 7;
+    }
+
+    buf[15] ^= (0 - carry) & 0x87;                // This is the R_128 value from SP 800-38B 5.3
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesCmacExpandKey(
+    _Out_               PSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                          pbKey,
+                        SIZE_T                          cbKey )
+{
+    SYMCRYPT_ERROR    scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_ALIGN  BYTE            buf[SYMCRYPT_AES_BLOCK_SIZE];
+
+    scError = SymCryptAesExpandKey( &pExpandedKey->aesKey, pbKey, cbKey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    SymCryptAesEncrypt( &pExpandedKey->aesKey, buf, buf );
+
+    SymCryptCmacMunge( buf );
+    memcpy( &pExpandedKey->K1, buf, sizeof( buf ) );
+    SymCryptCmacMunge( buf );
+    memcpy( &pExpandedKey->K2, buf, sizeof( buf ) );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    SYMCRYPT_SET_MAGIC( pExpandedKey );
+
+cleanup:
+
+    return scError;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacKeyCopy(
+    _In_    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_AES_CMAC_EXPANDED_KEY  pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+    SymCryptAesKeyCopy( &pSrc->aesKey, &pDst->aesKey );
+    memcpy( pDst->K1, pSrc->K1, sizeof( pDst->K1 ) );
+    memcpy( pDst->K2, pSrc->K2, sizeof( pDst->K2 ) );
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmac(
+    _In_                                            PSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                          pbData,
+                                                    SIZE_T                          cbData,
+    _Out_writes_( SYMCRYPT_AES_CMAC_RESULT_SIZE )   PBYTE                           pbResult )
+{
+    SYMCRYPT_AES_CMAC_STATE state;
+
+    SymCryptAesCmacInit( &state, pExpandedKey );
+    SymCryptAesCmacAppend( &state, pbData, cbData );
+    SymCryptAesCmacResult( &state, pbResult );
+
+    SymCryptWipeKnownSize( &state, sizeof( state ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacStateCopy(
+    _In_        PCSYMCRYPT_AES_CMAC_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_AES_CMAC_STATE         pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+    *pDst = *pSrc;
+
+    if( pExpandedKey == NULL )
+    {
+        SYMCRYPT_CHECK_MAGIC( pSrc->pKey );
+        pDst->pKey = pSrc->pKey;
+    }
+    else
+    {
+        SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+        pDst->pKey = pExpandedKey;
+    }
+
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacInit(
+    _Out_   PSYMCRYPT_AES_CMAC_STATE        pState,
+    _In_    PCSYMCRYPT_AES_CMAC_EXPANDED_KEY pExpandedKey)
+{
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    pState->bytesInBuf = 0;
+    SymCryptWipeKnownSize( pState->chain, sizeof( pState->chain ) );
+    pState->pKey = pExpandedKey;
+
+    SYMCRYPT_SET_MAGIC( pState );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacAppend(
+    _Inout_                 PSYMCRYPT_AES_CMAC_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData )
+{
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    if( pState->bytesInBuf != 0 )
+    {
+        SIZE_T freeInBuf = SYMCRYPT_AES_BLOCK_SIZE - pState->bytesInBuf;
+        SYMCRYPT_ASSERT( freeInBuf < SYMCRYPT_AES_BLOCK_SIZE );
+
+        if( cbData <= freeInBuf )
+        {
+            // Do nothing.
+            // the data will be copied into the buf at the end of this function
+            //
+        }
+        else
+        {
+            memcpy( &pState->buf[pState->bytesInBuf], pbData, freeInBuf );
+            pbData += freeInBuf;
+            cbData -= freeInBuf;
+            SymCryptAesCbcMac( &pState->pKey->aesKey, &pState->chain[0], &pState->buf[0], SYMCRYPT_AES_BLOCK_SIZE );
+            pState->bytesInBuf = 0;
+        }
+    }
+
+    //
+    // At this point, either pState->bytesInBuf == 0, or it is !=0 but cbData is small enough that all the
+    // data will still fit in the buffer without further processing.
+    //
+
+    if( cbData > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SIZE_T bytesToDo = (cbData-1) & ~(SIZE_T)(SYMCRYPT_AES_BLOCK_SIZE - 1);
+        SymCryptAesCbcMac( &pState->pKey->aesKey, &pState->chain[0], pbData, bytesToDo );
+        pbData += bytesToDo;
+        cbData -= bytesToDo;
+    }
+
+    if( cbData > 0 )
+    {
+        memcpy( &pState->buf[pState->bytesInBuf], pbData, cbData );
+        pState->bytesInBuf += cbData;
+    }
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacResult(
+    _Inout_                                         PSYMCRYPT_AES_CMAC_STATE    pState,
+    _Out_writes_( SYMCRYPT_AES_CMAC_RESULT_SIZE )   PBYTE                       pbResult )
+{
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    if( pState->bytesInBuf < SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptWipe( &pState->buf[pState->bytesInBuf + 1], SYMCRYPT_AES_BLOCK_SIZE - pState->bytesInBuf - 1 );
+        pState->buf[pState->bytesInBuf] = 0x80;
+        SymCryptXorBytes( &pState->buf[0], &pState->pKey->K2[0], &pState->buf[0], SYMCRYPT_AES_BLOCK_SIZE );
+    }
+    else
+    {
+        SymCryptXorBytes( &pState->buf[0], &pState->pKey->K1[0], &pState->buf[0], SYMCRYPT_AES_BLOCK_SIZE );
+    }
+
+    SymCryptAesCbcMac( &pState->pKey->aesKey, &pState->chain[0], &pState->buf[0], SYMCRYPT_AES_BLOCK_SIZE );
+    memcpy( pbResult, &pState->chain[0], SYMCRYPT_AES_BLOCK_SIZE );
+
+    //
+    // Put the state back in the original starting state,
+    // and wipe any traces of the data.
+    //
+    pState->bytesInBuf = 0;
+    SymCryptWipeKnownSize( pState->chain, sizeof( pState->chain ) );
+    SymCryptWipeKnownSize( pState->buf, sizeof( pState->buf ) );
+}
+
+
+
+static const BYTE aesCmacKat[SYMCRYPT_AES_CMAC_RESULT_SIZE] = {
+    0x0a, 0x54, 0xa6, 0xa4, 0x25, 0xd4, 0x84, 0x38, 0xc3, 0xf8, 0xbb, 0xe0, 0x9b, 0xf9, 0x44, 0xcc,
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCmacSelftest(void)
+{
+    SYMCRYPT_AES_CMAC_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_AES_CMAC_RESULT_SIZE];
+
+    SymCryptAesCmacExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptAesCmac( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+    if( memcmp( res, aesCmacKat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh5' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/aeskw.c b/libs/symcrypt/lib/aeskw.c
new file mode 100644
index 000000000000..c16ec6becf58
--- /dev/null
+++ b/libs/symcrypt/lib/aeskw.c
@@ -0,0 +1,457 @@
+//
+// aeskw.c    Implementation of the AES-KW(P) block cipher modes
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// The KW and KWP modes have inherently terrible performance characteristics from how they are
+// defined. Notably, they require a serial chain of AES block operations 12x longer than an
+// equivalent AES-CBC encryption (which is already not a favored mode just because of the serial
+// nature).
+// Additionally the intermediate state of AES-KW and AES-KWP must be of a size proportional to
+// the plaintext / ciphertext, rather than fitting into some constant-sized state.
+//
+// The current strategy for intermediate state handling is to allocate an internal buffer for
+// the state. We expect that the caller does not care too much about performance if they are using
+// these modes, so the overhead of an allocation per operation should not be a problem.
+//
+// While it is possible to expose an API surface which uses the destination buffer as a scratch
+// buffer to store intermediate state, this would break the read-once/write-once rule, making the
+// API surface brittle to misuse if the caller is encrypting to memory that may be in a different
+// security domain (i.e. kernel caller encrypting a secret directly into memory which is mapped to
+// user mode).
+// If we need to expose a non-allocating version, we can introduce a lower-level API where the
+// caller provides an appropriately sized scratch buffer, but we will cross that bridge if it is
+// required.
+//
+
+#include "precomp.h"
+
+const UINT64 SymCryptAesKwDefaultICV    = 0xA6A6A6A6A6A6A6A6;
+const UINT32 SymCryptAesKwpDefaultICV   = 0xA65959A6;
+#define SYMCRYPT_AES_SEMIBLOCK_SIZE (SYMCRYPT_AES_BLOCK_SIZE / 2)
+
+const SIZE_T SymCryptAesKWMinPlaintextLen   = 16u;          // 2*SYMCRYPT_AES_SEMIBLOCK_SIZE
+const SIZE_T SymCryptAesKWMaxPlaintextLen   = (1u<<31)-8;
+const SIZE_T SymCryptAesKWMinCiphertextLen  = 24u;          // 3*SYMCRYPT_AES_SEMIBLOCK_SIZE
+const SIZE_T SymCryptAesKWMaxCiphertextLen  = (1u<<31);
+
+const SIZE_T SymCryptAesKWPMinPlaintextLen  = 1u;
+const SIZE_T SymCryptAesKWPMaxPlaintextLen  = (1u<<31)-8;
+const SIZE_T SymCryptAesKWPMinCiphertextLen = 16u;
+const SIZE_T SymCryptAesKWPMaxCiphertextLen = (1u<<31);
+
+//
+// This function corresponds to algorithm W(S) from section 6.1 of SP 800-38F
+//
+// We perform this algorithm destructively in place, reading and writing to the same location
+// multiple times
+//
+static
+VOID
+SYMCRYPT_CALL
+SymCryptAesKwxInternalWrap(
+    _In_                            PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_bytes_(cbBuf)    PBYTE                       pbBuf,
+                                    UINT32                      cbBuf )
+{
+    SYMCRYPT_ALIGN BYTE activeBlock[SYMCRYPT_AES_BLOCK_SIZE];
+    const UINT32 nSemiBlocks = cbBuf / SYMCRYPT_AES_SEMIBLOCK_SIZE; // n per SP 800-38F
+    UINT64 encryptionIdx = 1;                                       // t per SP 800-38F
+    UINT64 lowHalfTemp = 0;
+
+    SYMCRYPT_ASSERT((cbBuf & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) == 0);
+    SYMCRYPT_ASSERT(cbBuf >= SymCryptAesKWMinCiphertextLen);
+    SYMCRYPT_ASSERT(cbBuf <= SymCryptAesKWMaxCiphertextLen);
+
+    // Special case for first encryption
+    // Initialize the low half of active block with the first semi-block of input
+    memcpy( activeBlock, pbBuf, SYMCRYPT_AES_SEMIBLOCK_SIZE);
+
+    for( UINT32 outerLoopCnt = 0; outerLoopCnt < 6; outerLoopCnt++ )
+    {
+        for( UINT32 innerLoopCnt = 1; innerLoopCnt < nSemiBlocks; innerLoopCnt++ )
+        {
+            SIZE_T bufOffset = innerLoopCnt*SYMCRYPT_AES_SEMIBLOCK_SIZE;
+
+            // Initialize the high half of active block to semi-block from buf
+            memcpy( activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, pbBuf+bufOffset, SYMCRYPT_AES_SEMIBLOCK_SIZE);
+
+            // Encrypt activeBlock in place
+            SymCryptAesEncrypt( pExpandedKey, activeBlock, activeBlock );
+
+            // Store the high half of result back to semi-block from buf
+            memcpy( pbBuf+bufOffset, activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, SYMCRYPT_AES_SEMIBLOCK_SIZE );
+
+            // Use the low half of the result and the next encryptionIdx to
+            // initialize the low half of the next encryption
+            lowHalfTemp = SYMCRYPT_LOAD_LSBFIRST64( activeBlock );
+            lowHalfTemp ^= SYMCRYPT_BSWAP64( encryptionIdx );
+            SYMCRYPT_STORE_LSBFIRST64( activeBlock, lowHalfTemp );
+
+            // Update encryptionIdx
+            encryptionIdx++;
+        }
+    }
+
+    SYMCRYPT_ASSERT( (encryptionIdx-1) == (nSemiBlocks-1)*6 );
+
+    // Special case for last encryption
+    // Store the final low half of encryption as the first semi-block of output
+    SYMCRYPT_STORE_LSBFIRST64( pbBuf, lowHalfTemp );
+
+    SymCryptWipeKnownSize( activeBlock, sizeof(activeBlock) );
+}
+
+//
+// This function corresponds to algorithm W^-1(S) from section 6.1 of SP 800-38F
+//
+// We perform this algorithm destructively in place, reading and writing to the same location
+// multiple times
+//
+static
+VOID
+SYMCRYPT_CALL
+SymCryptAesKwxInternalUnwrap(
+    _In_                            PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_bytes_(cbBuf)    PBYTE                       pbBuf,
+                                    UINT32                      cbBuf )
+{
+    SYMCRYPT_ALIGN BYTE activeBlock[SYMCRYPT_AES_BLOCK_SIZE];
+    const UINT32 nSemiBlocks = cbBuf / SYMCRYPT_AES_SEMIBLOCK_SIZE; // n per SP 800-38F
+    UINT64 decryptionIdx = 6*(nSemiBlocks-1);                       // t per SP 800-38F
+    UINT64 lowHalfTemp = 0;
+
+    SYMCRYPT_ASSERT((cbBuf & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) == 0);
+    SYMCRYPT_ASSERT(cbBuf >= SymCryptAesKWMinCiphertextLen);
+    SYMCRYPT_ASSERT(cbBuf <= SymCryptAesKWMaxCiphertextLen);
+
+    // Special case for first decryption
+    // Initialize the low half temporary with the first semi-block of input
+    lowHalfTemp = SYMCRYPT_LOAD_LSBFIRST64( pbBuf );
+
+    for( UINT32 outerLoopCnt = 0; outerLoopCnt < 6; outerLoopCnt++ )
+    {
+        for( UINT32 innerLoopCnt = nSemiBlocks-1; innerLoopCnt > 0; innerLoopCnt-- )
+        {
+            SIZE_T bufOffset = innerLoopCnt*SYMCRYPT_AES_SEMIBLOCK_SIZE;
+
+            // Update low half with decryptionIdx and store to low half of active block
+            lowHalfTemp ^= SYMCRYPT_BSWAP64( decryptionIdx );
+            SYMCRYPT_STORE_LSBFIRST64( activeBlock, lowHalfTemp );
+
+            // Initialize the high half of active block to semi-block from buf
+            memcpy( activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, pbBuf+bufOffset, SYMCRYPT_AES_SEMIBLOCK_SIZE);
+
+            // Decrypt activeBlock in place
+            SymCryptAesDecrypt( pExpandedKey, activeBlock, activeBlock );
+
+            // Store the high half of result back to semi-block from buf
+            memcpy( pbBuf+bufOffset, activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, SYMCRYPT_AES_SEMIBLOCK_SIZE );
+
+            // Update decryptionIdx
+            decryptionIdx--;
+
+            // Use the low half of the result to set the low half temporary
+            lowHalfTemp = SYMCRYPT_LOAD_LSBFIRST64( activeBlock );
+        }
+    }
+
+    SYMCRYPT_ASSERT( decryptionIdx == 0 );
+
+    // Special case for last decryption
+    // Store the final low half of decryption as the first semi-block of output
+    SYMCRYPT_STORE_LSBFIRST64( pbBuf, lowHalfTemp );
+
+    SymCryptWipeKnownSize( activeBlock, sizeof(activeBlock) );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwEncrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE  pbScratch = NULL;
+    UINT32 cbScratch = 0;
+
+    if( (cbSrc < SymCryptAesKWMinPlaintextLen) ||
+        (cbSrc > SymCryptAesKWMaxPlaintextLen) ||
+        ((cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) != 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch = ((UINT32) cbSrc)+SYMCRYPT_AES_SEMIBLOCK_SIZE;
+    if( cbDst < cbScratch )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // set up input buffer as ICV1 || P
+    SYMCRYPT_STORE_LSBFIRST64( pbScratch, SymCryptAesKwDefaultICV );
+    memcpy( pbScratch+8, pbSrc, cbSrc );
+
+    // encrypt input buffer in place
+    SymCryptAesKwxInternalWrap( pExpandedKey, pbScratch, cbScratch );
+
+    // copy encrypted buffer to output
+    memcpy( pbDst, pbScratch, cbScratch );
+    *pcbResult = cbScratch;
+
+cleanup:
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwDecrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE  pbScratch = NULL;
+    UINT32 cbScratch = 0;
+
+    if( (cbSrc < SymCryptAesKWMinCiphertextLen) ||
+        (cbSrc > SymCryptAesKWMaxCiphertextLen) ||
+        ((cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) != 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch = (UINT32) cbSrc;
+    if( cbDst < cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // set up input buffer as C
+    memcpy( pbScratch, pbSrc, cbSrc );
+
+    // decrypt input buffer in place
+    SymCryptAesKwxInternalUnwrap( pExpandedKey, pbScratch, cbScratch );
+
+    // check first semi-block has the expected value
+    if( SYMCRYPT_LOAD_LSBFIRST64( pbScratch ) != SymCryptAesKwDefaultICV )
+    {
+        scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+        goto cleanup;
+    }
+
+    // copy decrypted buffer to output
+    memcpy( pbDst, pbScratch+SYMCRYPT_AES_SEMIBLOCK_SIZE, cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE );
+    *pcbResult = cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE;
+
+cleanup:
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwpEncrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE  pbScratch = NULL;
+    UINT32 cbScratch = 0;
+    UINT32 cbPad = 0;
+
+    if( (cbSrc < SymCryptAesKWPMinPlaintextLen) ||
+        (cbSrc > SymCryptAesKWPMaxPlaintextLen) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbPad = SYMCRYPT_AES_SEMIBLOCK_SIZE - ((UINT32) cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1));
+    if( cbPad == SYMCRYPT_AES_SEMIBLOCK_SIZE )
+    {
+        cbPad = 0;
+    }
+
+    cbScratch = (UINT32) cbSrc + SYMCRYPT_AES_SEMIBLOCK_SIZE + cbPad;
+    if( cbDst < cbScratch )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT( cbScratch >= 16 );
+
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // set up input buffer as ICV2 || len(P) || P || PAD
+    SYMCRYPT_STORE_LSBFIRST32( pbScratch,   SymCryptAesKwpDefaultICV );
+    SYMCRYPT_STORE_MSBFIRST32( pbScratch+4, (UINT32) cbSrc );
+    // pad by unconditionally setting the last 8 bytes to 0
+    // then overwrite some or all of the padding bytes with plaintext
+    SYMCRYPT_STORE_LSBFIRST64( pbScratch+cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE, 0u );
+    memcpy( pbScratch+8, pbSrc, cbSrc );
+
+    // encrypt input buffer in place
+    if( cbScratch == SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // special case for AES-KWP with small plaintext
+        SymCryptAesEncrypt( pExpandedKey, pbScratch, pbScratch );
+    } else {
+        SymCryptAesKwxInternalWrap( pExpandedKey, pbScratch, cbScratch );
+    }
+
+    // copy encrypted buffer to output
+    memcpy( pbDst, pbScratch, cbScratch );
+    *pcbResult = cbScratch;
+
+cleanup:
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptAesKwpDecrypt(
+    _In_                                PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSrc)                   PCBYTE                      pbSrc,
+                                        SIZE_T                      cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)  PBYTE                       pbDst,
+                                        SIZE_T                      cbDst,
+    _Out_                               SIZE_T*                     pcbResult )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE  pbScratch = NULL;
+    UINT32 cbScratch = 0;
+    UINT32 cbPlaintext = 0;
+    UINT32 cbPad = 0;
+    UINT32 mVerificationError = 0;  // Mask indicating whether the decrypted buffer is malformed
+    UINT32 mIsPlaintext = 0;        // Mask for plaintext bytes in the final semi-block
+
+    if( (cbSrc < SymCryptAesKWPMinCiphertextLen) ||
+        (cbSrc > SymCryptAesKWPMaxCiphertextLen) ||
+        ((cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) != 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch = (UINT32) cbSrc;
+    if( cbDst < cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE-7 )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // set up input buffer as C
+    memcpy( pbScratch, pbSrc, cbSrc );
+
+    // decrypt input buffer in place
+    if( cbScratch == SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // special case for AES-KWP with small ciphertext
+        SymCryptAesDecrypt( pExpandedKey, pbScratch, pbScratch );
+    } else {
+        SymCryptAesKwxInternalUnwrap( pExpandedKey, pbScratch, cbScratch );
+    }
+
+    // Check if the decrypted buffer is of an expected form
+    // check bytes [0..3] are expected ICV
+    mVerificationError |= SYMCRYPT_LOAD_LSBFIRST32( pbScratch ) ^ SymCryptAesKwpDefaultICV;
+
+    // check bytes [4..7] are a valid plaintext length (i.e. computed cbPad in range [0,7])
+    cbPlaintext = SYMCRYPT_LOAD_MSBFIRST32( pbScratch+4 );
+    cbPad = (UINT32) cbSrc - cbPlaintext - SYMCRYPT_AES_SEMIBLOCK_SIZE;
+    mVerificationError |= (cbPad & 0xfffffff8);
+
+    // check that padding is all 0s
+    for( UINT32 i = 1; i<SYMCRYPT_AES_SEMIBLOCK_SIZE; i++ )
+    {
+        mIsPlaintext = SymCryptMask32LtU31(i, SYMCRYPT_AES_SEMIBLOCK_SIZE-(cbPad&7));
+        mVerificationError |= ((UINT32) pbScratch[ cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE+i ]) & ~mIsPlaintext;
+    }
+
+    // Now if there was any verification error, we fail
+    if( mVerificationError != 0 )
+    {
+        scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+        goto cleanup;
+    }
+
+    // We are variable time w.r.t. the plaintext length on success
+    if( cbDst < cbPlaintext )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    // copy decrypted buffer to output
+    memcpy( pbDst, pbScratch+SYMCRYPT_AES_SEMIBLOCK_SIZE, cbPlaintext );
+    *pcbResult = cbPlaintext;
+
+cleanup:
+    if( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+
+}
diff --git a/libs/symcrypt/lib/amd64/aesasm.S b/libs/symcrypt/lib/amd64/aesasm.S
new file mode 100644
index 000000000000..eb33bb9f9788
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/aesasm.S
@@ -0,0 +1,1779 @@
+//
+//  aesasm.symcryptasm   Assembler code for fast AES on the amd64
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This code is derived from the AesFast implementation that
+// Niels Ferguson wrote from scratch for BitLocker during Vista.
+// That code is still in RSA32.
+//
+
+// This file has only been partially translated into symcryptasm, external function calls use the
+// generic symcryptasm registers to convert different calling conventions into using the fixed register
+// layout used in aesasm. It seems likely that changing which registers AES state will be kept in in
+// the macros could impact on performance.
+// In general we don't want to touch this code going forward; the vast majority of amd64 CPUs have aesni
+// and use the Xmm Aes codepaths.
+
+#include "symcryptasm_shared.cppasm"
+
+#define USE_BLOCK_FUNCTION 1    // Set to 1 to use block function, 0 to use block macro
+
+#if defined(SYMCRYPT_MASM)
+extern  SymCryptAesSboxMatrixMult:DWORD
+extern  SymCryptAesInvSboxMatrixMult:DWORD
+extern  SymCryptAesInvSbox:BYTE
+extern  SymCryptFatal:NEAR
+
+#elif defined(SYMCRYPT_GAS)
+
+#else
+#error Unknown target assembly
+#endif
+
+#if SYMCRYPT_DEBUG
+SET(SYMCRYPT_CODE_VERSION, ((SYMCRYPT_CODE_VERSION_API * 65536) + SYMCRYPT_CODE_VERSION_MINOR))
+SET(SYMCRYPT_MAGIC_CONSTANT, HEX(53316D76) + SYMCRYPT_CODE_VERSION) // 0x53316D76 == 'S1mv'
+
+.macro SYMCRYPT_CHECK_MAGIC  check_magic_label, ptr, struct_magic_offset, arg_1
+        mov     rax, [\ptr + \struct_magic_offset]
+        sub     rax, \ptr
+        cmp     rax, SYMCRYPT_MAGIC_CONSTANT
+        jz      \check_magic_label
+        mov     \arg_1, HEX(6D616763) // 0x6D616763 == 'magc'
+        call    SymCryptFatal
+\check_magic_label:
+.endm
+#else
+.macro SYMCRYPT_CHECK_MAGIC  check_magic_label, ptr, struct_magic_offset, arg_1
+.endm
+#endif
+
+//
+// Structure definition that mirrors the SYMCRYPT_AES_EXPANDED_KEY structure.
+//
+
+// SYMCRYPT_AES_EXPANDED_KEY struct
+//         RoundKey        dq      2*N_ROUND_KEYS_IN_AESKEY dup (?)        //
+//         lastEncRoundKey dq      ?                                       // pointer to last enc round key
+//         lastDecRoundKey dq      ?                                       // pointer to last dec round key
+//         SYMCRYPT_MAGIC_FIELD
+// SYMCRYPT_AES_EXPANDED_KEY ends
+
+SET(N_ROUND_KEYS_IN_AESKEY, 29)
+SET(lastEncRoundKeyOffset, (29*16))
+SET(lastDecRoundKeyOffset, (29*16 + 8))
+SET(magicFieldOffset, (29*16 + 8 + 8))
+
+//
+// Shorthand for the 4 tables we will use
+// We always use r11 to point to the (inv) SboxMatrixMult tables
+//
+#define SMM0  (r11 +    0)
+#define SMM1  (r11 + 1024)
+#define SMM2  (r11 + 2048)
+#define SMM3  (r11 + 3072)
+
+#define ISMM0 (r11 +    0)
+#define ISMM1 (r11 + 1024)
+#define ISMM2 (r11 + 2048)
+#define ISMM3 (r11 + 3072)
+
+.macro ENC_MIX  keyptr
+        //
+        // Perform the unkeyed mixing function for encryption
+        // plus a key addition from the key pointer
+        //
+        // input:block is in     eax, ebx, ecx, edx -  r11 points to AesSboxMatrixMult
+        // New state ends up in  eax, ebx, ecx, edx
+        // Used registers:       esi, edi, ebp, r8
+
+        //
+        // We can use the e<xx> registers for the movzx as the
+        // upper 32 bits are automatically set to 0. This saves
+        // prefix bytes
+        //
+        // We use 32-bit registers to store the state.
+        // We tried using 64-bit registers, but the extra shifts
+        // cost too much.
+        // Using 32-bit throughout makes the key xor more expensive
+        // but we avoid having to combine the 32-bit halves into
+        // 64 bit.
+        //
+
+        movzx   esi,al
+        mov     esi,[SMM0 + 4 * rsi]
+        movzx   edi,ah
+        shr     eax,16
+        mov     r8d,[SMM1 + 4 * rdi]
+        movzx   ebp,al
+        mov     ebp,[SMM2 + 4 * rbp]
+        movzx   edi,ah
+        mov     edi,[SMM3 + 4 * rdi]
+
+        movzx   eax,bl
+        xor     edi,[SMM0 + 4 * rax]
+        movzx   eax,bh
+        shr     ebx,16
+        xor     esi,[SMM1 + 4 * rax]
+        movzx   eax,bl
+        xor     r8d,[SMM2 + 4 * rax]
+        movzx   eax,bh
+        xor     ebp,[SMM3 + 4 * rax]
+
+        movzx   eax,cl
+        xor     ebp,[SMM0 + 4 * rax]
+        movzx   ebx,ch
+        shr     ecx,16
+        xor     edi,[SMM1 + 4 * rbx]
+        movzx   eax,cl
+        xor     esi,[SMM2 + 4 * rax]
+        movzx   ebx,ch
+        xor     r8d,[SMM3 + 4 * rbx]
+
+        movzx   eax,dl
+        xor     r8d,[SMM0 + 4 * rax]
+        movzx   ebx,dh
+        shr     edx,16
+        xor     ebp,[SMM1 + 4 * rbx]
+        movzx   eax,dl
+        xor     edi,[SMM2 + 4 * rax]
+        movzx   ebx,dh
+        xor     esi,[SMM3 + 4 * rbx]
+
+        mov     eax, [\keyptr]
+        mov     ebx, [\keyptr + 4]
+        xor     eax, esi
+        mov     ecx, [\keyptr + 8]
+        xor     ebx, edi
+        mov     edx, [\keyptr + 12]
+        xor     ecx, ebp
+        xor     edx, r8d
+.endm
+
+
+.macro DEC_MIX  keyptr
+        //
+        // Perform the unkeyed mixing function for decryption
+        //
+        // input:block is in      eax, ebx, ecx, edx
+        //       r11 points to AesInvSboxMatrixMult
+        // New state ends up in   esi, edi, ebp, r8d
+
+        movzx   esi,al
+        mov     esi,[ISMM0 + 4 * rsi]
+        movzx   edi,ah
+        shr     eax,16
+        mov     edi,[ISMM1 + 4 * rdi]
+        movzx   ebp,al
+        mov     ebp,[ISMM2 + 4 * rbp]
+        movzx   eax,ah
+        mov     r8d,[ISMM3 + 4 * rax]
+
+        movzx   eax,bl
+        xor     edi,[ISMM0 + 4 * rax]
+        movzx   eax,bh
+        shr     ebx,16
+        xor     ebp,[ISMM1 + 4 * rax]
+        movzx   eax,bl
+        xor     r8d,[ISMM2 + 4 * rax]
+        movzx   eax,bh
+        xor     esi,[ISMM3 + 4 * rax]
+
+        movzx   eax,cl
+        xor     ebp,[ISMM0 + 4 * rax]
+        movzx   ebx,ch
+        shr     ecx,16
+        xor     r8d,[ISMM1 + 4 * rbx]
+        movzx   eax,cl
+        xor     esi,[ISMM2 + 4 * rax]
+        movzx   ebx,ch
+        xor     edi,[ISMM3 + 4 * rbx]
+
+        movzx   eax,dl
+        xor     r8d,[ISMM0 + 4 * rax]
+        movzx   ebx,dh
+        shr     edx,16
+        xor     esi,[ISMM1 + 4 * rbx]
+        movzx   eax,dl
+        xor     edi,[ISMM2 + 4 * rax]
+        movzx   ebx,dh
+        xor     ebp,[ISMM3 + 4 * rbx]
+
+        mov     eax, [\keyptr]
+        mov     ebx, [\keyptr + 4]
+        xor     eax, esi
+        mov     ecx, [\keyptr + 8]
+        xor     ebx, edi
+        mov     edx, [\keyptr + 12]
+        xor     ecx, ebp
+        xor     edx, r8d
+.endm
+
+.macro AES_ENCRYPT_MACRO  AesEncryptMacroLoopLabel
+        //
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use (modified)
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+        // This macro is free to unroll the cipher completely, or to use a loop
+        // over r9
+        //
+
+        //
+        // xor in first round key
+        //
+        xor     eax,[r9]
+        xor     ebx,[r9+4]
+        xor     ecx,[r9+8]
+        xor     edx,[r9+12]
+
+        add     r9,32
+
+        // Do not unroll the loop at all because very few CPUs use this codepath so it's worth
+        // minimizing the binary size
+
+\AesEncryptMacroLoopLabel:
+        // Block is eax, ebx, ecx, edx
+        // r9-16 points to next round key
+
+        ENC_MIX r9-16
+
+        cmp     r9,r10
+        lea     r9,[r9+16]
+        jc      \AesEncryptMacroLoopLabel
+
+        //
+        // Now for the final round
+        // We use the fact that SboxMatrixMult[0] table is also
+        // an Sbox table if you use the second element of each entry.
+        //
+        // Result is in esi, edi, ebp, r8d
+        //
+
+        movzx   esi,al
+        movzx   esi,byte ptr[r11 + 1 + 4*rsi]
+        movzx   edi,ah
+        shr     eax,16
+        movzx   r8d,byte ptr[r11 + 1 + 4*rdi]
+        movzx   ebp,al
+        shl     r8d,8
+        movzx   ebp,byte ptr[r11 + 1 + 4*rbp]
+        shl     ebp,16
+        movzx   edi,ah
+        movzx   edi,byte ptr[r11 + 1 + 4*rdi]
+        shl     edi,24
+
+        movzx   eax,bl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        or      edi,eax
+        movzx   eax,bh
+        shr     ebx,16
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        shl     eax,8
+        or      esi,eax
+        movzx   eax,bl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        movzx   ebx,bh
+        shl     eax,16
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        or      r8d,eax
+        shl     ebx,24
+        or      ebp,ebx
+
+        movzx   eax,cl
+        movzx   ebx,ch
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        shr     ecx,16
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     ebx,8
+        or      ebp,eax
+        or      edi,ebx
+        movzx   eax,cl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        movzx   ebx,ch
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      esi,eax
+        or      r8d,ebx
+
+        movzx   eax,dl
+        movzx   ebx,dh
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        shr     edx,16
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     ebx,8
+        or      r8d,eax
+        or      ebp,ebx
+        movzx   eax,dl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        movzx   ebx,dh
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      edi,eax
+        or      esi,ebx
+
+        //
+        // xor in final round key
+        //
+
+        xor     r8d,[r10+12]
+        xor     esi,[r10]
+        xor     edi,[r10+4]
+        xor     ebp,[r10+8]
+.endm
+
+.macro AES_DECRYPT_MACRO  AesDecryptMacroLoopLabel
+        //
+        // Ciphertext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to InvSboxMatrixMult (unchanged)
+        // r12 points to InvSbox (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        //
+        // xor in first round key
+        //
+        xor     eax,[r9]
+        xor     ebx,[r9+4]
+        xor     ecx,[r9+8]
+        xor     edx,[r9+12]
+
+        add     r9,32
+
+        // Do not unroll the loop at all because very few CPUs use this codepath so it's worth
+        // minimizing the binary size
+\AesDecryptMacroLoopLabel:
+        // Block is eax, ebx, ecx, edx
+        // r9-16 points to next round key
+
+        DEC_MIX r9-16
+
+        cmp     r9,r10
+        lea     r9,[r9+16]
+        jc      \AesDecryptMacroLoopLabel
+
+        //
+        // Now for the final round
+        // Result is in esi, edi, ebp, r8d
+        //
+
+        movzx   esi,al
+        movzx   esi,byte ptr[r12 + rsi]
+        movzx   edi,ah
+        shr     eax,16
+        movzx   edi,byte ptr[r12 + rdi]
+        movzx   ebp,al
+        shl     edi,8
+        movzx   ebp,byte ptr[r12 + rbp]
+        shl     ebp,16
+        movzx   eax,ah
+        movzx   r8d,byte ptr[r12 + rax]
+        shl     r8d,24
+
+        movzx   eax,bl
+        movzx   eax,byte ptr[r12 + rax]
+        or      edi,eax
+        movzx   eax,bh
+        shr     ebx,16
+        movzx   eax,byte ptr[r12 + rax]
+        shl     eax,8
+        or      ebp,eax
+        movzx   eax,bl
+        movzx   eax,byte ptr[r12 + rax]
+        movzx   ebx,bh
+        shl     eax,16
+        movzx   ebx,byte ptr[r12 + rbx]
+        or      r8d,eax
+        shl     ebx,24
+        or      esi,ebx
+
+        movzx   eax,cl
+        movzx   ebx,ch
+        movzx   eax,byte ptr[r12 + rax]
+        shr     ecx,16
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     ebx,8
+        or      ebp,eax
+        or      r8d,ebx
+        movzx   eax,cl
+        movzx   eax,byte ptr[r12 + rax]
+        movzx   ebx,ch
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      esi,eax
+        or      edi,ebx
+
+        movzx   eax,dl
+        movzx   ebx,dh
+        movzx   eax,byte ptr[r12 + rax]
+        shr     edx,16
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     ebx,8
+        or      r8d,eax
+        or      esi,ebx
+        movzx   eax,dl
+        movzx   eax,byte ptr[r12 + rax]
+        movzx   ebx,dh
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      edi,eax
+        or      ebp,ebx
+
+        //
+        // xor in final round key
+        //
+
+        xor     esi,[r10]
+        xor     edi,[r10+4]
+        xor     ebp,[r10+8]
+        xor     r8d,[r10+12]
+.endm
+
+#if USE_BLOCK_FUNCTION
+
+        //
+        // We use a block function, the AES_ENCRYPT macro merely calls the function
+        //
+
+.macro AES_ENCRYPT  loopLabel
+        call    SymCryptAesEncryptAsmInternal
+.endm
+
+.macro AES_DECRYPT  loopLabel
+        call    SymCryptAesDecryptAsmInternal
+.endm
+
+//========================================
+//       SymCryptAesEncryptAsmInternal
+//
+//       Internal AES encryption routine with modified calling convention.
+//       This function has the exact same calling convention as the AES_ENCRYPT_MACRO
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsmInternal):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsmInternal)
+.def PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsmInternal); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsmInternal)
+
+
+.seh_endprologue
+
+
+        AES_ENCRYPT_MACRO SymCryptAesEncryptAsmInternalLoop
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+
+//========================================
+//       SymCryptAesDecryptAsmInternal
+//
+//       Internal AES encryption routine with modified calling convention.
+//       This function has the exact same calling convention as the AES_DECRYPT_MACRO
+//
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsmInternal):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsmInternal)
+.def PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsmInternal); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsmInternal)
+
+
+.seh_endprologue
+
+
+        AES_DECRYPT_MACRO SymCryptAesDecryptAsmInternalLoop
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+
+#else
+
+        //
+        // No block function, use the macro directly
+        //
+
+.macro AES_ENCRYPT  loopLabel
+        AES_ENCRYPT_MACRO \loopLabel
+.endm
+
+.macro AES_DECRYPT  loopLabel
+        AES_DECRYPT_MACRO \loopLabel
+.endm
+
+#endif
+
+//
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesEncrypt( _In_                                   PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )  PCBYTE                      pbPlaintext,
+//                    _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbCiphertext )
+//
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+#define Q13 r14
+#define D13 r14d
+#define W13 r14w
+#define B13 r14b
+#define Q14 r15
+#define D14 r15d
+#define W14 r15w
+#define B14 r15b
+PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+push Q14
+.seh_pushreg Q14
+sub rsp, 40
+.seh_stackalloc 40
+
+.seh_endprologue
+
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesEncryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy it to
+        // the place it is needed internally in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 3 argument registers are:
+        //      rcx, rdx, r8, rdi, rsi
+
+        mov     r10, [Q1 + lastEncRoundKeyOffset]
+        mov     r9, Q1
+
+        mov     [rsp + 112 /*MEMSLOT0*/], Q3
+
+        //
+        // Load the plaintext
+        //
+        mov     eax,[Q2     ]
+        mov     ebx,[Q2 +  4]
+        mov     ecx,[Q2 +  8]
+        mov     edx,[Q2 + 12]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesSboxMatrixMult)]
+
+        AES_ENCRYPT SymCryptAesEncryptAsmLoop
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+
+        // retrieve pbCiphertext using Q0 because it is always rax regardless of calling convention
+        mov     Q0,[rsp + 112 /*MEMSLOT0*/]
+        mov     [Q0     ], esi
+        mov     [Q0 +  4], edi
+        mov     [Q0 +  8], ebp
+        mov     [Q0 + 12], r8d
+
+
+add rsp, 40
+pop Q14
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+#undef Q14
+#undef D14
+#undef W14
+#undef B14
+
+
+//
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesDecrypt( _In_                                   PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )  PCBYTE                      pbCiphertext,
+//                    _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbPlaintext )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+#define Q13 r14
+#define D13 r14d
+#define W13 r14w
+#define B13 r14b
+#define Q14 r15
+#define D14 r15d
+#define W14 r15w
+#define B14 r15b
+PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+push Q14
+.seh_pushreg Q14
+sub rsp, 40
+.seh_stackalloc 40
+
+.seh_endprologue
+
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesDecryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 3 argument registers are:
+        //      rcx, rdx, r8, rdi, rsi
+
+        mov     r9,[Q1 + lastEncRoundKeyOffset]
+        mov     r10,[Q1 + lastDecRoundKeyOffset]
+
+        mov     [rsp + 112 /*MEMSLOT0*/], Q3
+
+        mov     eax,[Q2     ]
+        mov     ebx,[Q2 +  4]
+        mov     ecx,[Q2 +  8]
+        mov     edx,[Q2 + 12]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesInvSboxMatrixMult)]
+        lea     r12,[GET_SYMBOL_ADDRESS(SymCryptAesInvSbox)]
+
+        AES_DECRYPT SymCryptAesDecryptAsmLoop
+        // Ciphertext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to InvSboxMatrixMult (unchanged)
+        // r12 points to InvSbox (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+
+        // retrieve pbPlaintext using Q0 because it is always rax regardless of calling convention
+        mov     Q0,[rsp + 112 /*MEMSLOT0*/]
+        mov     [Q0     ], esi
+        mov     [Q0 +  4], edi
+        mov     [Q0 +  8], ebp
+        mov     [Q0 + 12], r8d
+
+
+add rsp, 40
+pop Q14
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+#undef Q14
+#undef D14
+#undef W14
+#undef B14
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCbcEncrypt(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+#define Q13 r14
+#define D13 r14d
+#define W13 r14w
+#define B13 r14b
+#define Q14 r15
+#define D14 r15d
+#define W14 r15w
+#define B14 r15b
+PLATFORM_NAME_MANGLE(SymCryptAesCbcEncryptAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesCbcEncryptAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptAesCbcEncryptAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesCbcEncryptAsm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+push Q14
+.seh_pushreg Q14
+sub rsp, 40
+.seh_stackalloc 40
+
+.seh_endprologue
+
+mov Q5, [rsp + 144]
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 5 argument registers are:
+        //      rcx, rdx, r8, r9, r10, rdi, rsi
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesCbcEncryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        and     Q5, NOT 15      // only deal with whole # blocks
+        jz      SymCryptAesCbcEncryptNoData
+
+        mov     [rsp + 112 /*MEMSLOT0*/], Q2    // save pbChainingValue
+        mov     rax, Q2                 // rax = pbChainingValue
+        mov     r13, Q3                 // r13 = pbSrc
+
+        mov     r15, Q5                 // r15 = cbData
+        mov     r14, Q4                 // r14 = pbDst
+
+        add     r15, Q3                 // r15 = pbSrcEnd
+
+        mov     r10,[Q1 + lastEncRoundKeyOffset]    // r10 = last enc round key
+        mov     r12,Q1                              // r12 = first round key to use
+
+        //
+        // Load the chaining state from pbChainingValue
+        //
+        mov     esi,[rax     ]
+        mov     edi,[rax +  4]
+        mov     ebp,[rax +  8]
+        mov     r8d,[rax + 12]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesSboxMatrixMult)]
+
+ALIGN(16)
+SymCryptAesCbcEncryptAsmLoop:
+        // Loop register setup
+        // r10 = last round key to use
+        // r12 = first round key to use
+        // r13 = pbSrc
+        // r14 = pbDst
+        // r15 = pbSrcEnd
+
+        // chaining state in (esi,edi,ebp,r8d)
+
+        mov     eax, [r13]
+        mov     r9, r12
+        mov     ebx, [r13+4]
+        xor     eax, esi
+        mov     ecx, [r13+8]
+        xor     ebx, edi
+        xor     ecx, ebp
+        mov     edx, [r13+12]
+        xor     edx, r8d
+
+        add     r13, 16
+
+
+        AES_ENCRYPT SymCryptAesCbcEncryptAsmInnerLoop
+        //
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        mov     [r14], esi
+        mov     [r14+4], edi
+        mov     [r14+8], ebp
+        mov     [r14+12], r8d
+
+        add     r14, 16
+
+        cmp     r13, r15
+
+        jb      SymCryptAesCbcEncryptAsmLoop
+
+
+        //
+        // Update the chaining value
+        //
+        mov     Q0,[rsp + 112 /*MEMSLOT0*/]
+        mov     [Q0], esi
+        mov     [Q0+4], edi
+        mov     [Q0+8], ebp
+        mov     [Q0+12], r8d
+
+SymCryptAesCbcEncryptNoData:
+
+
+add rsp, 40
+pop Q14
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+#undef Q14
+#undef D14
+#undef W14
+#undef B14
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCbcDecrypt(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+#define Q13 r14
+#define D13 r14d
+#define W13 r14w
+#define B13 r14b
+#define Q14 r15
+#define D14 r15d
+#define W14 r15w
+#define B14 r15b
+PLATFORM_NAME_MANGLE(SymCryptAesCbcDecryptAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesCbcDecryptAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptAesCbcDecryptAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesCbcDecryptAsm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+push Q14
+.seh_pushreg Q14
+sub rsp, 40
+.seh_stackalloc 40
+
+.seh_endprologue
+
+mov Q5, [rsp + 144]
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 5 argument registers are:
+        //      rcx, rdx, r8, r9, r10, rdi, rsi
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesCbcDecryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        and     Q5, NOT 15
+        jz      SymCryptAesCbcDecryptNoData
+
+        mov     [rsp + 112 /*MEMSLOT0*/], Q2   // save pbChainingValue
+        mov     [rsp + 120 /*MEMSLOT1*/], Q3   // save pbSrc
+
+        lea     r14, [Q5 - 16]
+        lea     r15, [Q4 + r14]         // r15 = pbDst pointed to last block
+        add     r14, Q3                 // r14 = pbSrc pointed to last block
+
+        mov     r13,[Q1 + lastEncRoundKeyOffset]
+        mov     r10,[Q1 + lastDecRoundKeyOffset]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesInvSboxMatrixMult)]
+        lea     r12,[GET_SYMBOL_ADDRESS(SymCryptAesInvSbox)]
+
+        //
+        // Load last ciphertext block & save on stack (we need to put it in the pbChaining buffer later)
+        //
+        mov     eax,[r14]
+        mov     ebx,[r14+4]
+        mov     ecx,[r14+8]
+        mov     edx,[r14+12]
+
+        mov     [rsp + 128 /*MEMSLOT2*/  ], eax
+        mov     [rsp + 128 /*MEMSLOT2*/+4], ebx
+        mov     [rsp + 136 /*MEMSLOT3*/  ], ecx
+        mov     [rsp + 136 /*MEMSLOT3*/+4], edx
+
+        jmp     SymCryptAesCbcDecryptAsmLoopEntry
+
+ALIGN(16)
+
+SymCryptAesCbcDecryptAsmLoop:
+        // Loop register setup
+        // r13 = first round key to use
+        // r14 = pbSrc
+        // r15 = pbDst
+        // [slot1] = pbSrcStart
+
+        // current ciphertext block (esi,edi,ebp,r8d)
+
+        mov     eax,[r14-16]
+        mov     ebx,[r14-12]
+        xor     esi,eax
+        mov     ecx,[r14-8]
+        xor     edi,ebx
+        mov     [r15],esi
+        mov     edx,[r14-4]
+        xor     ebp,ecx
+        mov     [r15+4],edi
+        xor     r8d,edx
+        mov     [r15+8],ebp
+        mov     [r15+12],r8d
+
+        sub     r14,16
+        sub     r15,16
+
+SymCryptAesCbcDecryptAsmLoopEntry:
+
+        mov     r9, r13
+
+        AES_DECRYPT SymCryptAesCbcDecryptAsmInnerLoop
+        //
+        // Ciphertext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to InvSboxMatrixMult (unchanged)
+        // r12 points to InvSbox (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        cmp     r14, [rsp + 120 /*MEMSLOT1*/]  // pbSrc
+        ja      SymCryptAesCbcDecryptAsmLoop
+
+        mov     rbx,[rsp + 112 /*MEMSLOT0*/]   // pbChainingValue
+        xor     esi,[rbx]
+        xor     edi,[rbx+4]
+        xor     ebp,[rbx+8]
+        xor     r8d,[rbx+12]
+
+        mov     [r15], esi
+        mov     [r15+4], edi
+        mov     [r15+8], ebp
+        mov     [r15+12], r8d
+
+        //
+        // Update the chaining value to the last ciphertext block
+        //
+        mov     rax,[rsp + 128 /*MEMSLOT2*/]
+        mov     rcx,[rsp + 136 /*MEMSLOT3*/]
+        mov     [rbx], rax
+        mov     [rbx+8], rcx
+
+SymCryptAesCbcDecryptNoData:
+
+
+add rsp, 40
+pop Q14
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+#undef Q14
+#undef D14
+#undef W14
+#undef B14
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCtrMsb64(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+#define Q13 r14
+#define D13 r14d
+#define W13 r14w
+#define B13 r14b
+#define Q14 r15
+#define D14 r15d
+#define W14 r15w
+#define B14 r15b
+PLATFORM_NAME_MANGLE(SymCryptAesCtrMsb64Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptAesCtrMsb64Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptAesCtrMsb64Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptAesCtrMsb64Asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+push Q14
+.seh_pushreg Q14
+sub rsp, 40
+.seh_stackalloc 40
+
+.seh_endprologue
+
+mov Q5, [rsp + 144]
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 5 argument registers are:
+        //      rcx, rdx, r8, r9, r10, rdi, rsi
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesCtrMsb64AsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        and     Q5, NOT 15      // only deal with whole # blocks
+        jz      SymCryptAesCtrMsb64NoData
+
+        mov     [rsp + 112 /*MEMSLOT0*/], Q2   // save pbChainingState
+        mov     rax, Q2         // rax = pbChainingValue
+        mov     r13, Q3         // r13 = pbSrc
+        mov     r14, Q5         // r14 = cbData
+        mov     r15, Q4         // r15 = pbDst
+        add     r14, Q3         // r14 = cbData + pbSrc = pbSrcEnd
+
+        mov     r10,[Q1 + lastEncRoundKeyOffset]        // r10 = last enc round key
+        mov     r12,Q1                                  // r12 = first round key to use
+
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesSboxMatrixMult)]
+
+        //
+        // Load the chaining state
+        //
+        mov     rcx, [rax + 8]
+        mov     rax, [rax    ]
+
+        //
+        // Store it in our local copy (we have no register free to keep pbChainingState in)
+        //
+        mov     [rsp + 120 /*MEMSLOT1*/], rax
+        mov     [rsp + 128 /*MEMSLOT2*/], rcx
+
+        //
+        // Move to the right registers
+        //
+        mov     rbx, rax
+        mov     rdx, rcx
+        shr     rbx, 32
+        shr     rdx, 32
+
+ALIGN(16)
+SymCryptAesCtrMsb64AsmLoop:
+        // Loop invariant
+        // Current chaining state is in (eax, ebx, ecx, edx)
+        // r10 = last round key to use
+        // r11 = SboxMatrixMult
+        // r12 = first round key to use
+        // r13 = pbSrc
+        // r14 = pbSrcEnd
+        // r15 = pbDst
+        // [slot1..slot2] = 16 bytes chaining state block
+
+        mov     r9, r12
+
+        AES_ENCRYPT SymCryptAesCtrMsb64AsmInnerLoop
+        //
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        // To improve latency, we FIRST
+        // load the chaining state, increment the counter, and write it back.
+        // leave the state in the (eax, ebx, ecx, edx) registers
+
+        mov     eax,dword ptr [rsp + 120 /*MEMSLOT1*/ + 0]
+        mov     ebx,dword ptr [rsp + 120 /*MEMSLOT1*/ + 4]
+        mov     rcx,[rsp +  128 /*MEMSLOT2*/ ]
+        bswap   rcx
+        add     rcx, 1
+        bswap   rcx
+        mov     [rsp + 128 /*MEMSLOT2*/ ], rcx
+        mov     rdx, rcx
+        shr     rdx, 32
+
+        // THEN we process the XOR of the key stream with the data
+        // This order is faster as we need to have the chaining state done
+        // before we can proceed, but there are no dependencies on the data result
+        // So we can loop back to the beginning while the data stream read/writes are
+        // still in flight.
+        //
+        // xor with the source stream
+
+        xor     esi,[r13 + 0 ]
+        xor     edi,[r13 + 4 ]
+        xor     ebp,[r13 + 8 ]
+        xor     r8d,[r13 + 12]
+
+        // store at the destination
+
+        mov     [r15 + 0], esi
+        mov     [r15 + 4], edi
+        mov     [r15 + 8], ebp
+        mov     [r15 + 12], r8d
+
+        add     r13, 16     // pbSrc += 16
+        add     r15, 16     // pbDst += 16
+
+        cmp     r13, r14
+
+        jb      SymCryptAesCtrMsb64AsmLoop
+
+        //
+        // Copy back the chaining value - we only modified the last 8 bytes, so that is all we copy
+        //
+        mov     rsi,[rsp + 112 /*MEMSLOT0*/]   // pbChainingState
+        mov     [rsi + 8], ecx
+        mov     [rsi + 12], edx
+
+        //
+        // Wipe the chaining value on stack
+        //
+        xor     rax, rax
+        mov     [rsp + 120 /*MEMSLOT1*/], rax
+        mov     [rsp + 128 /*MEMSLOT2*/], rax
+
+SymCryptAesCtrMsb64NoData:
+
+
+add rsp, 40
+pop Q14
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+#undef Q14
+#undef D14
+#undef W14
+#undef B14
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/aesasm.symcryptasm b/libs/symcrypt/lib/amd64/aesasm.symcryptasm
new file mode 100644
index 000000000000..6377a7e4cab0
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/aesasm.symcryptasm
@@ -0,0 +1,962 @@
+//
+//  aesasm.symcryptasm   Assembler code for fast AES on the amd64
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This code is derived from the AesFast implementation that
+// Niels Ferguson wrote from scratch for BitLocker during Vista.
+// That code is still in RSA32.
+//
+
+// This file has only been partially translated into symcryptasm, external function calls use the
+// generic symcryptasm registers to convert different calling conventions into using the fixed register
+// layout used in aesasm. It seems likely that changing which registers AES state will be kept in in
+// the macros could impact on performance.
+// In general we don't want to touch this code going forward; the vast majority of amd64 CPUs have aesni
+// and use the Xmm Aes codepaths.
+
+#include "symcryptasm_shared.cppasm"
+
+#define USE_BLOCK_FUNCTION 1    // Set to 1 to use block function, 0 to use block macro
+
+#if defined(SYMCRYPT_MASM)
+extern  SymCryptAesSboxMatrixMult:DWORD
+extern  SymCryptAesInvSboxMatrixMult:DWORD
+extern  SymCryptAesInvSbox:BYTE
+extern  SymCryptFatal:NEAR
+
+#elif defined(SYMCRYPT_GAS)
+
+#else
+#error Unknown target assembly
+#endif
+
+#if SYMCRYPT_DEBUG
+SET(SYMCRYPT_CODE_VERSION, ((SYMCRYPT_CODE_VERSION_API * 65536) + SYMCRYPT_CODE_VERSION_MINOR))
+SET(SYMCRYPT_MAGIC_CONSTANT, HEX(53316D76) + SYMCRYPT_CODE_VERSION) // 0x53316D76 == 'S1mv'
+
+MACRO_START(SYMCRYPT_CHECK_MAGIC, check_magic_label, ptr, struct_magic_offset, arg_1)
+        mov     rax, [ptr + struct_magic_offset]
+        sub     rax, ptr
+        cmp     rax, SYMCRYPT_MAGIC_CONSTANT
+        jz      check_magic_label
+        mov     arg_1, HEX(6D616763) // 0x6D616763 == 'magc'
+        call    SymCryptFatal
+check_magic_label:
+MACRO_END()
+#else
+MACRO_START(SYMCRYPT_CHECK_MAGIC, check_magic_label, ptr, struct_magic_offset, arg_1)
+MACRO_END()
+#endif
+
+//
+// Structure definition that mirrors the SYMCRYPT_AES_EXPANDED_KEY structure.
+//
+
+// SYMCRYPT_AES_EXPANDED_KEY struct
+//         RoundKey        dq      2*N_ROUND_KEYS_IN_AESKEY dup (?)        //
+//         lastEncRoundKey dq      ?                                       // pointer to last enc round key
+//         lastDecRoundKey dq      ?                                       // pointer to last dec round key
+//         SYMCRYPT_MAGIC_FIELD
+// SYMCRYPT_AES_EXPANDED_KEY ends
+
+SET(N_ROUND_KEYS_IN_AESKEY, 29)
+SET(lastEncRoundKeyOffset, (29*16))
+SET(lastDecRoundKeyOffset, (29*16 + 8))
+SET(magicFieldOffset, (29*16 + 8 + 8))
+
+//
+// Shorthand for the 4 tables we will use
+// We always use r11 to point to the (inv) SboxMatrixMult tables
+//
+#define SMM0  (r11 +    0)
+#define SMM1  (r11 + 1024)
+#define SMM2  (r11 + 2048)
+#define SMM3  (r11 + 3072)
+
+#define ISMM0 (r11 +    0)
+#define ISMM1 (r11 + 1024)
+#define ISMM2 (r11 + 2048)
+#define ISMM3 (r11 + 3072)
+
+MACRO_START(ENC_MIX, keyptr)
+        //
+        // Perform the unkeyed mixing function for encryption
+        // plus a key addition from the key pointer
+        //
+        // input:block is in     eax, ebx, ecx, edx -  r11 points to AesSboxMatrixMult
+        // New state ends up in  eax, ebx, ecx, edx
+        // Used registers:       esi, edi, ebp, r8
+
+        //
+        // We can use the e<xx> registers for the movzx as the
+        // upper 32 bits are automatically set to 0. This saves
+        // prefix bytes
+        //
+        // We use 32-bit registers to store the state.
+        // We tried using 64-bit registers, but the extra shifts
+        // cost too much.
+        // Using 32-bit throughout makes the key xor more expensive
+        // but we avoid having to combine the 32-bit halves into
+        // 64 bit.
+        //
+
+        movzx   esi,al
+        mov     esi,[SMM0 + 4 * rsi]
+        movzx   edi,ah
+        shr     eax,16
+        mov     r8d,[SMM1 + 4 * rdi]
+        movzx   ebp,al
+        mov     ebp,[SMM2 + 4 * rbp]
+        movzx   edi,ah
+        mov     edi,[SMM3 + 4 * rdi]
+
+        movzx   eax,bl
+        xor     edi,[SMM0 + 4 * rax]
+        movzx   eax,bh
+        shr     ebx,16
+        xor     esi,[SMM1 + 4 * rax]
+        movzx   eax,bl
+        xor     r8d,[SMM2 + 4 * rax]
+        movzx   eax,bh
+        xor     ebp,[SMM3 + 4 * rax]
+
+        movzx   eax,cl
+        xor     ebp,[SMM0 + 4 * rax]
+        movzx   ebx,ch
+        shr     ecx,16
+        xor     edi,[SMM1 + 4 * rbx]
+        movzx   eax,cl
+        xor     esi,[SMM2 + 4 * rax]
+        movzx   ebx,ch
+        xor     r8d,[SMM3 + 4 * rbx]
+
+        movzx   eax,dl
+        xor     r8d,[SMM0 + 4 * rax]
+        movzx   ebx,dh
+        shr     edx,16
+        xor     ebp,[SMM1 + 4 * rbx]
+        movzx   eax,dl
+        xor     edi,[SMM2 + 4 * rax]
+        movzx   ebx,dh
+        xor     esi,[SMM3 + 4 * rbx]
+
+        mov     eax, [keyptr]
+        mov     ebx, [keyptr + 4]
+        xor     eax, esi
+        mov     ecx, [keyptr + 8]
+        xor     ebx, edi
+        mov     edx, [keyptr + 12]
+        xor     ecx, ebp
+        xor     edx, r8d
+MACRO_END()
+
+
+MACRO_START(DEC_MIX, keyptr)
+        //
+        // Perform the unkeyed mixing function for decryption
+        //
+        // input:block is in      eax, ebx, ecx, edx
+        //       r11 points to AesInvSboxMatrixMult
+        // New state ends up in   esi, edi, ebp, r8d
+
+        movzx   esi,al
+        mov     esi,[ISMM0 + 4 * rsi]
+        movzx   edi,ah
+        shr     eax,16
+        mov     edi,[ISMM1 + 4 * rdi]
+        movzx   ebp,al
+        mov     ebp,[ISMM2 + 4 * rbp]
+        movzx   eax,ah
+        mov     r8d,[ISMM3 + 4 * rax]
+
+        movzx   eax,bl
+        xor     edi,[ISMM0 + 4 * rax]
+        movzx   eax,bh
+        shr     ebx,16
+        xor     ebp,[ISMM1 + 4 * rax]
+        movzx   eax,bl
+        xor     r8d,[ISMM2 + 4 * rax]
+        movzx   eax,bh
+        xor     esi,[ISMM3 + 4 * rax]
+
+        movzx   eax,cl
+        xor     ebp,[ISMM0 + 4 * rax]
+        movzx   ebx,ch
+        shr     ecx,16
+        xor     r8d,[ISMM1 + 4 * rbx]
+        movzx   eax,cl
+        xor     esi,[ISMM2 + 4 * rax]
+        movzx   ebx,ch
+        xor     edi,[ISMM3 + 4 * rbx]
+
+        movzx   eax,dl
+        xor     r8d,[ISMM0 + 4 * rax]
+        movzx   ebx,dh
+        shr     edx,16
+        xor     esi,[ISMM1 + 4 * rbx]
+        movzx   eax,dl
+        xor     edi,[ISMM2 + 4 * rax]
+        movzx   ebx,dh
+        xor     ebp,[ISMM3 + 4 * rbx]
+
+        mov     eax, [keyptr]
+        mov     ebx, [keyptr + 4]
+        xor     eax, esi
+        mov     ecx, [keyptr + 8]
+        xor     ebx, edi
+        mov     edx, [keyptr + 12]
+        xor     ecx, ebp
+        xor     edx, r8d
+MACRO_END()
+
+MACRO_START(AES_ENCRYPT_MACRO, AesEncryptMacroLoopLabel)
+        //
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use (modified)
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+        // This macro is free to unroll the cipher completely, or to use a loop
+        // over r9
+        //
+
+        //
+        // xor in first round key
+        //
+        xor     eax,[r9]
+        xor     ebx,[r9+4]
+        xor     ecx,[r9+8]
+        xor     edx,[r9+12]
+
+        add     r9,32
+
+        // Do not unroll the loop at all because very few CPUs use this codepath so it's worth
+        // minimizing the binary size
+
+AesEncryptMacroLoopLabel:
+        // Block is eax, ebx, ecx, edx
+        // r9-16 points to next round key
+
+        ENC_MIX r9-16
+
+        cmp     r9,r10
+        lea     r9,[r9+16]
+        jc      AesEncryptMacroLoopLabel
+
+        //
+        // Now for the final round
+        // We use the fact that SboxMatrixMult[0] table is also
+        // an Sbox table if you use the second element of each entry.
+        //
+        // Result is in esi, edi, ebp, r8d
+        //
+
+        movzx   esi,al
+        movzx   esi,byte ptr[r11 + 1 + 4*rsi]
+        movzx   edi,ah
+        shr     eax,16
+        movzx   r8d,byte ptr[r11 + 1 + 4*rdi]
+        movzx   ebp,al
+        shl     r8d,8
+        movzx   ebp,byte ptr[r11 + 1 + 4*rbp]
+        shl     ebp,16
+        movzx   edi,ah
+        movzx   edi,byte ptr[r11 + 1 + 4*rdi]
+        shl     edi,24
+
+        movzx   eax,bl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        or      edi,eax
+        movzx   eax,bh
+        shr     ebx,16
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        shl     eax,8
+        or      esi,eax
+        movzx   eax,bl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        movzx   ebx,bh
+        shl     eax,16
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        or      r8d,eax
+        shl     ebx,24
+        or      ebp,ebx
+
+        movzx   eax,cl
+        movzx   ebx,ch
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        shr     ecx,16
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     ebx,8
+        or      ebp,eax
+        or      edi,ebx
+        movzx   eax,cl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        movzx   ebx,ch
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      esi,eax
+        or      r8d,ebx
+
+        movzx   eax,dl
+        movzx   ebx,dh
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        shr     edx,16
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     ebx,8
+        or      r8d,eax
+        or      ebp,ebx
+        movzx   eax,dl
+        movzx   eax,byte ptr[r11 + 1 + 4*rax]
+        movzx   ebx,dh
+        movzx   ebx,byte ptr[r11 + 1 + 4*rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      edi,eax
+        or      esi,ebx
+
+        //
+        // xor in final round key
+        //
+
+        xor     r8d,[r10+12]
+        xor     esi,[r10]
+        xor     edi,[r10+4]
+        xor     ebp,[r10+8]
+MACRO_END()
+
+MACRO_START(AES_DECRYPT_MACRO, AesDecryptMacroLoopLabel)
+        //
+        // Ciphertext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to InvSboxMatrixMult (unchanged)
+        // r12 points to InvSbox (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        //
+        // xor in first round key
+        //
+        xor     eax,[r9]
+        xor     ebx,[r9+4]
+        xor     ecx,[r9+8]
+        xor     edx,[r9+12]
+
+        add     r9,32
+
+        // Do not unroll the loop at all because very few CPUs use this codepath so it's worth
+        // minimizing the binary size
+AesDecryptMacroLoopLabel:
+        // Block is eax, ebx, ecx, edx
+        // r9-16 points to next round key
+
+        DEC_MIX r9-16
+
+        cmp     r9,r10
+        lea     r9,[r9+16]
+        jc      AesDecryptMacroLoopLabel
+
+        //
+        // Now for the final round
+        // Result is in esi, edi, ebp, r8d
+        //
+
+        movzx   esi,al
+        movzx   esi,byte ptr[r12 + rsi]
+        movzx   edi,ah
+        shr     eax,16
+        movzx   edi,byte ptr[r12 + rdi]
+        movzx   ebp,al
+        shl     edi,8
+        movzx   ebp,byte ptr[r12 + rbp]
+        shl     ebp,16
+        movzx   eax,ah
+        movzx   r8d,byte ptr[r12 + rax]
+        shl     r8d,24
+
+        movzx   eax,bl
+        movzx   eax,byte ptr[r12 + rax]
+        or      edi,eax
+        movzx   eax,bh
+        shr     ebx,16
+        movzx   eax,byte ptr[r12 + rax]
+        shl     eax,8
+        or      ebp,eax
+        movzx   eax,bl
+        movzx   eax,byte ptr[r12 + rax]
+        movzx   ebx,bh
+        shl     eax,16
+        movzx   ebx,byte ptr[r12 + rbx]
+        or      r8d,eax
+        shl     ebx,24
+        or      esi,ebx
+
+        movzx   eax,cl
+        movzx   ebx,ch
+        movzx   eax,byte ptr[r12 + rax]
+        shr     ecx,16
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     ebx,8
+        or      ebp,eax
+        or      r8d,ebx
+        movzx   eax,cl
+        movzx   eax,byte ptr[r12 + rax]
+        movzx   ebx,ch
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      esi,eax
+        or      edi,ebx
+
+        movzx   eax,dl
+        movzx   ebx,dh
+        movzx   eax,byte ptr[r12 + rax]
+        shr     edx,16
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     ebx,8
+        or      r8d,eax
+        or      esi,ebx
+        movzx   eax,dl
+        movzx   eax,byte ptr[r12 + rax]
+        movzx   ebx,dh
+        movzx   ebx,byte ptr[r12 + rbx]
+        shl     eax,16
+        shl     ebx,24
+        or      edi,eax
+        or      ebp,ebx
+
+        //
+        // xor in final round key
+        //
+
+        xor     esi,[r10]
+        xor     edi,[r10+4]
+        xor     ebp,[r10+8]
+        xor     r8d,[r10+12]
+MACRO_END()
+
+#if USE_BLOCK_FUNCTION
+
+        //
+        // We use a block function, the AES_ENCRYPT macro merely calls the function
+        //
+
+MACRO_START(AES_ENCRYPT, loopLabel)
+        call    SymCryptAesEncryptAsmInternal
+MACRO_END()
+
+MACRO_START(AES_DECRYPT, loopLabel)
+        call    SymCryptAesDecryptAsmInternal
+MACRO_END()
+
+//========================================
+//       SymCryptAesEncryptAsmInternal
+//
+//       Internal AES encryption routine with modified calling convention.
+//       This function has the exact same calling convention as the AES_ENCRYPT_MACRO
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsmInternal), 0, 0)
+
+        AES_ENCRYPT_MACRO SymCryptAesEncryptAsmInternalLoop
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsmInternal))
+
+//========================================
+//       SymCryptAesDecryptAsmInternal
+//
+//       Internal AES encryption routine with modified calling convention.
+//       This function has the exact same calling convention as the AES_DECRYPT_MACRO
+//
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsmInternal), 0, 0)
+
+        AES_DECRYPT_MACRO SymCryptAesDecryptAsmInternalLoop
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsmInternal))
+
+#else
+
+        //
+        // No block function, use the macro directly
+        //
+
+MACRO_START(AES_ENCRYPT, loopLabel)
+        AES_ENCRYPT_MACRO loopLabel
+MACRO_END()
+
+MACRO_START(AES_DECRYPT, loopLabel)
+        AES_DECRYPT_MACRO loopLabel
+MACRO_END()
+
+#endif
+
+//
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesEncrypt( _In_                                   PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )  PCBYTE                      pbPlaintext,
+//                    _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbCiphertext )
+//
+
+NESTED_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsm), 3, 15)
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesEncryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy it to
+        // the place it is needed internally in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 3 argument registers are:
+        //      rcx, rdx, r8, rdi, rsi
+
+        mov     r10, [Q1 + lastEncRoundKeyOffset]
+        mov     r9, Q1
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q3
+
+        //
+        // Load the plaintext
+        //
+        mov     eax,[Q2     ]
+        mov     ebx,[Q2 +  4]
+        mov     ecx,[Q2 +  8]
+        mov     edx,[Q2 + 12]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesSboxMatrixMult)]
+
+        AES_ENCRYPT SymCryptAesEncryptAsmLoop
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+
+        // retrieve pbCiphertext using Q0 because it is always rax regardless of calling convention
+        mov     Q0,[rsp + GET_MEMSLOT_OFFSET(slot0)]
+        mov     [Q0     ], esi
+        mov     [Q0 +  4], edi
+        mov     [Q0 +  8], ebp
+        mov     [Q0 + 12], r8d
+
+NESTED_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesEncryptAsm))
+
+
+//
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesDecrypt( _In_                                   PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )  PCBYTE                      pbCiphertext,
+//                    _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbPlaintext )
+
+NESTED_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsm), 3, 15)
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesDecryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 3 argument registers are:
+        //      rcx, rdx, r8, rdi, rsi
+
+        mov     r9,[Q1 + lastEncRoundKeyOffset]
+        mov     r10,[Q1 + lastDecRoundKeyOffset]
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q3
+
+        mov     eax,[Q2     ]
+        mov     ebx,[Q2 +  4]
+        mov     ecx,[Q2 +  8]
+        mov     edx,[Q2 + 12]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesInvSboxMatrixMult)]
+        lea     r12,[GET_SYMBOL_ADDRESS(SymCryptAesInvSbox)]
+
+        AES_DECRYPT SymCryptAesDecryptAsmLoop
+        // Ciphertext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to InvSboxMatrixMult (unchanged)
+        // r12 points to InvSbox (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+
+        // retrieve pbPlaintext using Q0 because it is always rax regardless of calling convention
+        mov     Q0,[rsp + GET_MEMSLOT_OFFSET(slot0)]
+        mov     [Q0     ], esi
+        mov     [Q0 +  4], edi
+        mov     [Q0 +  8], ebp
+        mov     [Q0 + 12], r8d
+
+NESTED_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesDecryptAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCbcEncrypt(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData )
+
+NESTED_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesCbcEncryptAsm), 5, 15)
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 5 argument registers are:
+        //      rcx, rdx, r8, r9, r10, rdi, rsi
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesCbcEncryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        and     Q5, NOT 15      // only deal with whole # blocks
+        jz      SymCryptAesCbcEncryptNoData
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q2    // save pbChainingValue
+        mov     rax, Q2                 // rax = pbChainingValue
+        mov     r13, Q3                 // r13 = pbSrc
+
+        mov     r15, Q5                 // r15 = cbData
+        mov     r14, Q4                 // r14 = pbDst
+
+        add     r15, Q3                 // r15 = pbSrcEnd
+
+        mov     r10,[Q1 + lastEncRoundKeyOffset]    // r10 = last enc round key
+        mov     r12,Q1                              // r12 = first round key to use
+
+        //
+        // Load the chaining state from pbChainingValue
+        //
+        mov     esi,[rax     ]
+        mov     edi,[rax +  4]
+        mov     ebp,[rax +  8]
+        mov     r8d,[rax + 12]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesSboxMatrixMult)]
+
+ALIGN(16)
+SymCryptAesCbcEncryptAsmLoop:
+        // Loop register setup
+        // r10 = last round key to use
+        // r12 = first round key to use
+        // r13 = pbSrc
+        // r14 = pbDst
+        // r15 = pbSrcEnd
+
+        // chaining state in (esi,edi,ebp,r8d)
+
+        mov     eax, [r13]
+        mov     r9, r12
+        mov     ebx, [r13+4]
+        xor     eax, esi
+        mov     ecx, [r13+8]
+        xor     ebx, edi
+        xor     ecx, ebp
+        mov     edx, [r13+12]
+        xor     edx, r8d
+
+        add     r13, 16
+
+
+        AES_ENCRYPT SymCryptAesCbcEncryptAsmInnerLoop
+        //
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        mov     [r14], esi
+        mov     [r14+4], edi
+        mov     [r14+8], ebp
+        mov     [r14+12], r8d
+
+        add     r14, 16
+
+        cmp     r13, r15
+
+        jb      SymCryptAesCbcEncryptAsmLoop
+
+
+        //
+        // Update the chaining value
+        //
+        mov     Q0,[rsp + GET_MEMSLOT_OFFSET(slot0)]
+        mov     [Q0], esi
+        mov     [Q0+4], edi
+        mov     [Q0+8], ebp
+        mov     [Q0+12], r8d
+
+SymCryptAesCbcEncryptNoData:
+
+NESTED_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesCbcEncryptAsm))
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCbcDecrypt(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData )
+
+NESTED_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesCbcDecryptAsm), 5, 15)
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 5 argument registers are:
+        //      rcx, rdx, r8, r9, r10, rdi, rsi
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesCbcDecryptAsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        and     Q5, NOT 15
+        jz      SymCryptAesCbcDecryptNoData
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q2   // save pbChainingValue
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], Q3   // save pbSrc
+
+        lea     r14, [Q5 - 16]
+        lea     r15, [Q4 + r14]         // r15 = pbDst pointed to last block
+        add     r14, Q3                 // r14 = pbSrc pointed to last block
+
+        mov     r13,[Q1 + lastEncRoundKeyOffset]
+        mov     r10,[Q1 + lastDecRoundKeyOffset]
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesInvSboxMatrixMult)]
+        lea     r12,[GET_SYMBOL_ADDRESS(SymCryptAesInvSbox)]
+
+        //
+        // Load last ciphertext block & save on stack (we need to put it in the pbChaining buffer later)
+        //
+        mov     eax,[r14]
+        mov     ebx,[r14+4]
+        mov     ecx,[r14+8]
+        mov     edx,[r14+12]
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)  ], eax
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)+4], ebx
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)  ], ecx
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)+4], edx
+
+        jmp     SymCryptAesCbcDecryptAsmLoopEntry
+
+ALIGN(16)
+
+SymCryptAesCbcDecryptAsmLoop:
+        // Loop register setup
+        // r13 = first round key to use
+        // r14 = pbSrc
+        // r15 = pbDst
+        // [slot1] = pbSrcStart
+
+        // current ciphertext block (esi,edi,ebp,r8d)
+
+        mov     eax,[r14-16]
+        mov     ebx,[r14-12]
+        xor     esi,eax
+        mov     ecx,[r14-8]
+        xor     edi,ebx
+        mov     [r15],esi
+        mov     edx,[r14-4]
+        xor     ebp,ecx
+        mov     [r15+4],edi
+        xor     r8d,edx
+        mov     [r15+8],ebp
+        mov     [r15+12],r8d
+
+        sub     r14,16
+        sub     r15,16
+
+SymCryptAesCbcDecryptAsmLoopEntry:
+
+        mov     r9, r13
+
+        AES_DECRYPT SymCryptAesCbcDecryptAsmInnerLoop
+        //
+        // Ciphertext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to InvSboxMatrixMult (unchanged)
+        // r12 points to InvSbox (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        cmp     r14, [rsp + GET_MEMSLOT_OFFSET(slot1)]  // pbSrc
+        ja      SymCryptAesCbcDecryptAsmLoop
+
+        mov     rbx,[rsp + GET_MEMSLOT_OFFSET(slot0)]   // pbChainingValue
+        xor     esi,[rbx]
+        xor     edi,[rbx+4]
+        xor     ebp,[rbx+8]
+        xor     r8d,[rbx+12]
+
+        mov     [r15], esi
+        mov     [r15+4], edi
+        mov     [r15+8], ebp
+        mov     [r15+12], r8d
+
+        //
+        // Update the chaining value to the last ciphertext block
+        //
+        mov     rax,[rsp + GET_MEMSLOT_OFFSET(slot2)]
+        mov     rcx,[rsp + GET_MEMSLOT_OFFSET(slot3)]
+        mov     [rbx], rax
+        mov     [rbx+8], rcx
+
+SymCryptAesCbcDecryptNoData:
+
+NESTED_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesCbcDecryptAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCtrMsb64(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData )
+
+NESTED_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptAesCtrMsb64Asm), 5, 15)
+
+        // Here we convert from whatever calling convention we are called from externally to our
+        // AES internal calling convention.
+        // We need to be careful that we don't overwrite an argument register before we copy or use
+        // the value appropriately for use in the AES functions.
+        // There is no automatic method for checking we do this correctly - modify with care!
+        // In SystemV and MSFT x64 ABIs, the possible 5 argument registers are:
+        //      rcx, rdx, r8, r9, r10, rdi, rsi
+
+        SYMCRYPT_CHECK_MAGIC SymCryptAesCtrMsb64AsmCheckMagic, Q1, magicFieldOffset, Q1
+
+        and     Q5, NOT 15      // only deal with whole # blocks
+        jz      SymCryptAesCtrMsb64NoData
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q2   // save pbChainingState
+        mov     rax, Q2         // rax = pbChainingValue
+        mov     r13, Q3         // r13 = pbSrc
+        mov     r14, Q5         // r14 = cbData
+        mov     r15, Q4         // r15 = pbDst
+        add     r14, Q3         // r14 = cbData + pbSrc = pbSrcEnd
+
+        mov     r10,[Q1 + lastEncRoundKeyOffset]        // r10 = last enc round key
+        mov     r12,Q1                                  // r12 = first round key to use
+
+
+        lea     r11,[GET_SYMBOL_ADDRESS(SymCryptAesSboxMatrixMult)]
+
+        //
+        // Load the chaining state
+        //
+        mov     rcx, [rax + 8]
+        mov     rax, [rax    ]
+
+        //
+        // Store it in our local copy (we have no register free to keep pbChainingState in)
+        //
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], rax
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)], rcx
+
+        //
+        // Move to the right registers
+        //
+        mov     rbx, rax
+        mov     rdx, rcx
+        shr     rbx, 32
+        shr     rdx, 32
+
+ALIGN(16)
+SymCryptAesCtrMsb64AsmLoop:
+        // Loop invariant
+        // Current chaining state is in (eax, ebx, ecx, edx)
+        // r10 = last round key to use
+        // r11 = SboxMatrixMult
+        // r12 = first round key to use
+        // r13 = pbSrc
+        // r14 = pbSrcEnd
+        // r15 = pbDst
+        // [slot1..slot2] = 16 bytes chaining state block
+
+        mov     r9, r12
+
+        AES_ENCRYPT SymCryptAesCtrMsb64AsmInnerLoop
+        //
+        // Plaintext in eax, ebx, ecx, edx
+        // r9 points to first round key to use
+        // r10 is last key to use (unchanged)
+        // r11 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in esi, edi, ebp, r8d
+        //
+
+        // To improve latency, we FIRST
+        // load the chaining state, increment the counter, and write it back.
+        // leave the state in the (eax, ebx, ecx, edx) registers
+
+        mov     eax,dword ptr [rsp + GET_MEMSLOT_OFFSET(slot1) + 0]
+        mov     ebx,dword ptr [rsp + GET_MEMSLOT_OFFSET(slot1) + 4]
+        mov     rcx,[rsp +  GET_MEMSLOT_OFFSET(slot2) ]
+        bswap   rcx
+        add     rcx, 1
+        bswap   rcx
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2) ], rcx
+        mov     rdx, rcx
+        shr     rdx, 32
+
+        // THEN we process the XOR of the key stream with the data
+        // This order is faster as we need to have the chaining state done
+        // before we can proceed, but there are no dependencies on the data result
+        // So we can loop back to the beginning while the data stream read/writes are
+        // still in flight.
+        //
+        // xor with the source stream
+
+        xor     esi,[r13 + 0 ]
+        xor     edi,[r13 + 4 ]
+        xor     ebp,[r13 + 8 ]
+        xor     r8d,[r13 + 12]
+
+        // store at the destination
+
+        mov     [r15 + 0], esi
+        mov     [r15 + 4], edi
+        mov     [r15 + 8], ebp
+        mov     [r15 + 12], r8d
+
+        add     r13, 16     // pbSrc += 16
+        add     r15, 16     // pbDst += 16
+
+        cmp     r13, r14
+
+        jb      SymCryptAesCtrMsb64AsmLoop
+
+        //
+        // Copy back the chaining value - we only modified the last 8 bytes, so that is all we copy
+        //
+        mov     rsi,[rsp + GET_MEMSLOT_OFFSET(slot0)]   // pbChainingState
+        mov     [rsi + 8], ecx
+        mov     [rsi + 12], edx
+
+        //
+        // Wipe the chaining value on stack
+        //
+        xor     rax, rax
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], rax
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)], rax
+
+SymCryptAesCtrMsb64NoData:
+
+NESTED_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptAesCtrMsb64Asm))
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/fdef369_asm.S b/libs/symcrypt/lib/amd64/fdef369_asm.S
new file mode 100644
index 000000000000..59d38938cc21
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/fdef369_asm.S
@@ -0,0 +1,871 @@
+//
+//  fdef_369asm.asm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// This file contains alternative routines that are used for modular computations
+// where the modulus is 257-384 or 513-576 bits long.
+// (Currently on ARM64 it is also used for 0-192-bit moduli but not on AMD64)
+//
+// The immediate advantage is that it improves EC performance on 384, and 521-bit curves.
+//
+// Most of this code is a direct copy of the default code.
+// AMD64 digits are now 512 bits.
+// We read the 'ndigit' value. If it is 1 digit, the values are 6 64-bit words, if it is 2 the values
+// are 9 64-bit words. As we compute in groups of 3, our loop counters are one more than nDigit
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+// SymCryptFdef369RawAddAsm(
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//     _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                             UINT32      nDigits )
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm)
+
+
+.seh_endprologue
+
+
+        inc     D4
+        xor     Q0, Q0
+
+SymCryptFdef369RawAddAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        adc     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        adc     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        adc     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        lea     Q1, [Q1 + 24]
+        lea     Q2, [Q2 + 24]
+        lea     Q3, [Q3 + 24]
+        dec     D4
+        jnz     SymCryptFdef369RawAddAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+
+// UINT32
+// SYMCRYPT_CALL
+// SymCryptFdef369RawSubAsm(
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//     _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                             UINT32      nDigits )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm)
+
+
+.seh_endprologue
+
+
+        inc     D4
+        xor     Q0, Q0
+
+SymCryptFdef369RawSubAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        sbb     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        sbb     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        sbb     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        lea     Q1, [Q1 + 24]
+        lea     Q2, [Q2 + 24]
+        lea     Q3, [Q3 + 24]
+        dec     D4
+        jnz     SymCryptFdef369RawSubAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdef369MaskedCopyAsm(
+//     _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//     _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                 UINT32      nDigits,
+//                                                                 UINT32      mask )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm)
+
+
+.seh_endprologue
+
+
+        inc     D3
+        movsxd  Q4, D4
+
+SymCryptFdef369MaskedCopyAsmLoop:
+        mov     Q0, [Q1]
+        mov     Q5, [Q2]
+        xor     Q0, Q5
+        and     Q0, Q4
+        xor     Q0, Q5
+        mov     [Q2], Q0
+
+        mov     Q0, [Q1 + 8]
+        mov     Q5, [Q2 + 8]
+        xor     Q0, Q5
+        and     Q0, Q4
+        xor     Q0, Q5
+        mov     [Q2 + 8], Q0
+
+        mov     Q0, [Q1 + 16]
+        mov     Q5, [Q2 + 16]
+        xor     Q0, Q5
+        and     Q0, Q4
+        xor     Q0, Q5
+        mov     [Q2 + 16], Q0
+
+        // Move on to the next digit
+
+        add     Q1, 24
+        add     Q2, 24
+        dec     D3
+        jnz     SymCryptFdef369MaskedCopyAsmLoop
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdef369RawMulAsm(
+//     _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                     UINT32      nDigits1,
+//     _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                     UINT32      nDigits2,
+//     _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+
+.seh_endprologue
+
+mov Q2, QH
+mov Q5, [rsp + 80]
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // D2 = # words left from Src1 to process
+        // Q3 = pSrc2
+        // Q4 = nDigits2
+        // Q5 = pDst (incremented in outer loop)
+        // Q6 = inner loop pointer into pSrc2
+        // Q7 = inner loop pointer into pDst
+        // Q8 = word from Src1 to multiply with
+        // Q9 = carry
+        // D10 = inner loop counter
+
+        inc     D2
+        inc     D4
+        lea     D2, [D2 + 2*D2]     // nDigits1 * 3 = # words in Src1 to process
+
+        // Outer loop invariant established: Q1, Q3, D4, Q5
+
+        mov     Q6, Q3              // Q6 = pSrc2
+        mov     Q7, Q5              // Q7 = pDst + outer loop ctr
+        mov     Q8, [Q1]            // mulword
+        xor     Q9, Q9
+        mov     D10, D4
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+
+ALIGN(16)
+
+SymCryptFdef369RawMulAsmLoop1:
+        mov     Q0, [Q6]
+        mul     Q8
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 8]
+        mul     Q8
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 8], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 16]
+        mul     Q8
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 16], Q0
+        mov     Q9, QH
+
+        add     Q6, 24
+        add     Q7, 24
+        dec     D10
+        jnz     SymCryptFdef369RawMulAsmLoop1
+
+        mov     [Q7], QH                // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     D2
+
+ALIGN(16)
+
+SymCryptFdef369RawMulAsmLoopOuter:
+
+        add     Q1, 8                   // move to next word of pSrc1
+        add     Q5, 8                   // move Dst pointer one word over
+        mov     Q8, [Q1]
+        mov     Q6, Q3
+        mov     Q7, Q5
+        xor     Q9, Q9
+        mov     D10, D4
+
+ALIGN(16)
+
+SymCryptFdef369RawMulAsmLoop2:
+        mov     Q0, [Q6]
+        mul     Q8
+        add     Q0, [Q7]
+        adc     QH, 0
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 8]
+        mul     Q8
+        add     Q0, [Q7 + 8]
+        adc     QH, 0
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 8], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 16]
+        mul     Q8
+        add     Q0, [Q7 + 16]
+        adc     QH, 0
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 16], Q0
+        mov     Q9, QH
+
+        add     Q6, 24
+        add     Q7, 24
+        dec     D10
+        jnz     SymCryptFdef369RawMulAsmLoop2
+
+        mov     [Q7], QH           // write next word. (stays within Dst buffer)
+
+        dec     D2
+        jnz     SymCryptFdef369RawMulAsmLoopOuter
+
+
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdef369MontgomeryReduceAsm(
+//     _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//     _Inout_                         PUINT32                 pSrc,
+//     _Out_                           PUINT32                 pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]    // nDigits
+        inc     D4
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        lea     D12, [D4 + 2*D4]  // outer loop counter, in words
+
+        xor     D8, D8
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry
+        // Q8 = carry out from last word of previous loop iteration
+        // Q9 = running pointer in Src
+        // Q10 = running pointer in Mod
+        // D11 = loop counter
+        // D12 = outer loop counter (words)
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                      // fetch word of Src we want to set to zero
+        mov     Q10, Q2
+        mov     Q9, Q1
+
+        imul    Q6, Q5                        // lower word is same for signed & unsigned multiply
+
+        mov     D11, D4
+        xor     D7, D7
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmInnerloop:
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q1 = pointer to modulus value
+        // Q6 = multiplier
+        // Q7 = carry (64 bits)
+        // Q9  = running ptr to modulus
+        // Q10 = running ptr to input/scratch
+        // D11 = inner loop counter (digits)
+        // D12 = outer loop counter (words)
+
+        mov     Q0, [Q9]
+        mul     Q6
+        add     Q0, [Q10]
+        adc     QH, 0
+        add     Q0, Q7
+        adc     QH, 0
+        mov     [Q10], Q0
+        mov     Q7, QH
+
+        mov     Q0, [Q9 + 8]
+        mul     Q6
+        add     Q0, [Q10 + 8]
+        adc     QH, 0
+        add     Q0, Q7
+        adc     QH, 0
+        mov     [Q10 + 8], Q0
+        mov     Q7, QH
+
+        mov     Q0, [Q9 + 16]
+        mul     Q6
+        add     Q0, [Q10 + 16]
+        adc     QH, 0
+        add     Q0, Q7
+        adc     QH, 0
+        mov     [Q10 + 16], Q0
+        mov     Q7, QH
+
+        add     Q9, 24
+        add     Q10, 24
+        dec     D11
+        jnz     SymCryptFdef369MontgomeryReduceAsmInnerloop
+
+        add     Q7, Q8
+        mov     D8, 0
+        adc     Q8, 0
+        add     Q7, [Q10]
+        adc     Q8, 0
+        mov     [Q10], Q7
+
+        add     Q2, 8
+
+        dec     D12
+        jnz     SymCryptFdef369MontgomeryReduceAsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     D11, D4         // loop ctr
+        mov     Q10, Q2         // pSrc
+        mov     Q9, Q1          // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q8,0' resulted in 0, 1, or 2
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmSubLoop:
+        mov     Q0,[Q10]
+        sbb     Q0,[Q9]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q10 + 8]
+        sbb     Q0,[Q9 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q10 + 16]
+        sbb     Q0,[Q9 + 16]
+        mov     [Q7 + 16], Q0
+
+        lea     Q10,[Q10 + 24]
+        lea     Q9,[Q9 + 24]
+        lea     Q7,[Q7 + 24]
+
+        dec     D11
+        jnz     SymCryptFdef369MontgomeryReduceAsmSubLoop
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q8 == 0 && Cy = 1
+        sbb     Q8, 0              // mask (64 bits)
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmMaskedCopyLoop:
+        mov     Q0, [Q2]
+        mov     Q1, [Q3]
+        xor     Q0, Q1
+        and     Q0, Q8
+        xor     Q0, Q1
+        mov     [Q3], Q0
+
+        mov     Q0, [Q2 + 8]
+        mov     Q1, [Q3 + 8]
+        xor     Q0, Q1
+        and     Q0, Q8
+        xor     Q0, Q1
+        mov     [Q3 + 8], Q0
+
+        mov     Q0, [Q2 + 16]
+        mov     Q1, [Q3 + 16]
+        xor     Q0, Q1
+        and     Q0, Q8
+        xor     Q0, Q1
+        mov     [Q3 + 16], Q0
+
+        // Move on to the next digit
+
+        add     Q2, 24
+        add     Q3, 24
+        dec     D4
+        jnz     SymCryptFdef369MontgomeryReduceAsmMaskedCopyLoop
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/fdef369_asm.symcryptasm b/libs/symcrypt/lib/amd64/fdef369_asm.symcryptasm
new file mode 100644
index 000000000000..f0e40e99e7a5
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/fdef369_asm.symcryptasm
@@ -0,0 +1,451 @@
+//
+//  fdef_369asm.asm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// This file contains alternative routines that are used for modular computations
+// where the modulus is 257-384 or 513-576 bits long.
+// (Currently on ARM64 it is also used for 0-192-bit moduli but not on AMD64)
+//
+// The immediate advantage is that it improves EC performance on 384, and 521-bit curves.
+//
+// Most of this code is a direct copy of the default code.
+// AMD64 digits are now 512 bits.
+// We read the 'ndigit' value. If it is 1 digit, the values are 6 64-bit words, if it is 2 the values
+// are 9 64-bit words. As we compute in groups of 3, our loop counters are one more than nDigit
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+// SymCryptFdef369RawAddAsm(
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//     _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                             UINT32      nDigits )
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm), 4, 5)
+
+        inc     D4
+        xor     Q0, Q0
+
+SymCryptFdef369RawAddAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        adc     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        adc     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        adc     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        lea     Q1, [Q1 + 24]
+        lea     Q2, [Q2 + 24]
+        lea     Q3, [Q3 + 24]
+        dec     D4
+        jnz     SymCryptFdef369RawAddAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm))
+
+// UINT32
+// SYMCRYPT_CALL
+// SymCryptFdef369RawSubAsm(
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//     _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//     _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                             UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm), 4, 5)
+
+        inc     D4
+        xor     Q0, Q0
+
+SymCryptFdef369RawSubAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        sbb     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        sbb     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        sbb     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        lea     Q1, [Q1 + 24]
+        lea     Q2, [Q2 + 24]
+        lea     Q3, [Q3 + 24]
+        dec     D4
+        jnz     SymCryptFdef369RawSubAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdef369MaskedCopyAsm(
+//     _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//     _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                 UINT32      nDigits,
+//                                                                 UINT32      mask )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm), 4, 6)
+
+        inc     D3
+        movsxd  Q4, D4
+
+SymCryptFdef369MaskedCopyAsmLoop:
+        mov     Q0, [Q1]
+        mov     Q5, [Q2]
+        xor     Q0, Q5
+        and     Q0, Q4
+        xor     Q0, Q5
+        mov     [Q2], Q0
+
+        mov     Q0, [Q1 + 8]
+        mov     Q5, [Q2 + 8]
+        xor     Q0, Q5
+        and     Q0, Q4
+        xor     Q0, Q5
+        mov     [Q2 + 8], Q0
+
+        mov     Q0, [Q1 + 16]
+        mov     Q5, [Q2 + 16]
+        xor     Q0, Q5
+        and     Q0, Q4
+        xor     Q0, Q5
+        mov     [Q2 + 16], Q0
+
+        // Move on to the next digit
+
+        add     Q1, 24
+        add     Q2, 24
+        dec     D3
+        jnz     SymCryptFdef369MaskedCopyAsmLoop
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdef369RawMulAsm(
+//     _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                     UINT32      nDigits1,
+//     _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                     UINT32      nDigits2,
+//     _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm), 5, 11)
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // D2 = # words left from Src1 to process
+        // Q3 = pSrc2
+        // Q4 = nDigits2
+        // Q5 = pDst (incremented in outer loop)
+        // Q6 = inner loop pointer into pSrc2
+        // Q7 = inner loop pointer into pDst
+        // Q8 = word from Src1 to multiply with
+        // Q9 = carry
+        // D10 = inner loop counter
+
+        inc     D2
+        inc     D4
+        lea     D2, [D2 + 2*D2]     // nDigits1 * 3 = # words in Src1 to process
+
+        // Outer loop invariant established: Q1, Q3, D4, Q5
+
+        mov     Q6, Q3              // Q6 = pSrc2
+        mov     Q7, Q5              // Q7 = pDst + outer loop ctr
+        mov     Q8, [Q1]            // mulword
+        xor     Q9, Q9
+        mov     D10, D4
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+
+ALIGN(16)
+
+SymCryptFdef369RawMulAsmLoop1:
+        mov     Q0, [Q6]
+        mul     Q8
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 8]
+        mul     Q8
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 8], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 16]
+        mul     Q8
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 16], Q0
+        mov     Q9, QH
+
+        add     Q6, 24
+        add     Q7, 24
+        dec     D10
+        jnz     SymCryptFdef369RawMulAsmLoop1
+
+        mov     [Q7], QH                // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     D2
+
+ALIGN(16)
+
+SymCryptFdef369RawMulAsmLoopOuter:
+
+        add     Q1, 8                   // move to next word of pSrc1
+        add     Q5, 8                   // move Dst pointer one word over
+        mov     Q8, [Q1]
+        mov     Q6, Q3
+        mov     Q7, Q5
+        xor     Q9, Q9
+        mov     D10, D4
+
+ALIGN(16)
+
+SymCryptFdef369RawMulAsmLoop2:
+        mov     Q0, [Q6]
+        mul     Q8
+        add     Q0, [Q7]
+        adc     QH, 0
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 8]
+        mul     Q8
+        add     Q0, [Q7 + 8]
+        adc     QH, 0
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 8], Q0
+        mov     Q9, QH
+
+        mov     Q0, [Q6 + 16]
+        mul     Q8
+        add     Q0, [Q7 + 16]
+        adc     QH, 0
+        add     Q0, Q9
+        adc     QH, 0
+        mov     [Q7 + 16], Q0
+        mov     Q9, QH
+
+        add     Q6, 24
+        add     Q7, 24
+        dec     D10
+        jnz     SymCryptFdef369RawMulAsmLoop2
+
+        mov     [Q7], QH           // write next word. (stays within Dst buffer)
+
+        dec     D2
+        jnz     SymCryptFdef369RawMulAsmLoopOuter
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdef369MontgomeryReduceAsm(
+//     _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//     _Inout_                         PUINT32                 pSrc,
+//     _Out_                           PUINT32                 pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm), 3, 13)
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]    // nDigits
+        inc     D4
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        lea     D12, [D4 + 2*D4]  // outer loop counter, in words
+
+        xor     D8, D8
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry
+        // Q8 = carry out from last word of previous loop iteration
+        // Q9 = running pointer in Src
+        // Q10 = running pointer in Mod
+        // D11 = loop counter
+        // D12 = outer loop counter (words)
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                      // fetch word of Src we want to set to zero
+        mov     Q10, Q2
+        mov     Q9, Q1
+
+        imul    Q6, Q5                        // lower word is same for signed & unsigned multiply
+
+        mov     D11, D4
+        xor     D7, D7
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmInnerloop:
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q1 = pointer to modulus value
+        // Q6 = multiplier
+        // Q7 = carry (64 bits)
+        // Q9  = running ptr to modulus
+        // Q10 = running ptr to input/scratch
+        // D11 = inner loop counter (digits)
+        // D12 = outer loop counter (words)
+
+        mov     Q0, [Q9]
+        mul     Q6
+        add     Q0, [Q10]
+        adc     QH, 0
+        add     Q0, Q7
+        adc     QH, 0
+        mov     [Q10], Q0
+        mov     Q7, QH
+
+        mov     Q0, [Q9 + 8]
+        mul     Q6
+        add     Q0, [Q10 + 8]
+        adc     QH, 0
+        add     Q0, Q7
+        adc     QH, 0
+        mov     [Q10 + 8], Q0
+        mov     Q7, QH
+
+        mov     Q0, [Q9 + 16]
+        mul     Q6
+        add     Q0, [Q10 + 16]
+        adc     QH, 0
+        add     Q0, Q7
+        adc     QH, 0
+        mov     [Q10 + 16], Q0
+        mov     Q7, QH
+
+        add     Q9, 24
+        add     Q10, 24
+        dec     D11
+        jnz     SymCryptFdef369MontgomeryReduceAsmInnerloop
+
+        add     Q7, Q8
+        mov     D8, 0
+        adc     Q8, 0
+        add     Q7, [Q10]
+        adc     Q8, 0
+        mov     [Q10], Q7
+
+        add     Q2, 8
+
+        dec     D12
+        jnz     SymCryptFdef369MontgomeryReduceAsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     D11, D4         // loop ctr
+        mov     Q10, Q2         // pSrc
+        mov     Q9, Q1          // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q8,0' resulted in 0, 1, or 2
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmSubLoop:
+        mov     Q0,[Q10]
+        sbb     Q0,[Q9]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q10 + 8]
+        sbb     Q0,[Q9 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q10 + 16]
+        sbb     Q0,[Q9 + 16]
+        mov     [Q7 + 16], Q0
+
+        lea     Q10,[Q10 + 24]
+        lea     Q9,[Q9 + 24]
+        lea     Q7,[Q7 + 24]
+
+        dec     D11
+        jnz     SymCryptFdef369MontgomeryReduceAsmSubLoop
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q8 == 0 && Cy = 1
+        sbb     Q8, 0              // mask (64 bits)
+
+ALIGN(16)
+
+SymCryptFdef369MontgomeryReduceAsmMaskedCopyLoop:
+        mov     Q0, [Q2]
+        mov     Q1, [Q3]
+        xor     Q0, Q1
+        and     Q0, Q8
+        xor     Q0, Q1
+        mov     [Q3], Q0
+
+        mov     Q0, [Q2 + 8]
+        mov     Q1, [Q3 + 8]
+        xor     Q0, Q1
+        and     Q0, Q8
+        xor     Q0, Q1
+        mov     [Q3 + 8], Q0
+
+        mov     Q0, [Q2 + 16]
+        mov     Q1, [Q3 + 16]
+        xor     Q0, Q1
+        and     Q0, Q8
+        xor     Q0, Q1
+        mov     [Q3 + 16], Q0
+
+        // Move on to the next digit
+
+        add     Q2, 24
+        add     Q3, 24
+        dec     D4
+        jnz     SymCryptFdef369MontgomeryReduceAsmMaskedCopyLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm))
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/fdef_asm.S b/libs/symcrypt/lib/amd64/fdef_asm.S
new file mode 100644
index 000000000000..366857d42287
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/fdef_asm.S
@@ -0,0 +1,4249 @@
+//
+//  fdef_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+
+.macro MULT_SINGLEADD_128  index, src_reg, dst_reg, Q0, QH, mul_word, even_carry, odd_carry
+        // \Q0 = mul scratch
+        // \QH = mul scratch
+        // \mul_word = multiplier
+        // \src_reg = running ptr to input
+        // \dst_reg = running ptr to output/scratch
+        // \even_carry = carry for even words (64 bits)
+        // \odd_carry = carry for odd words (64 bits)
+
+        mov     \Q0, [\src_reg + 8*\index]
+        mul     \mul_word
+        mov     \odd_carry, \QH
+        add     \Q0, \even_carry
+        mov     [\dst_reg + 8*\index], \Q0
+        adc     \odd_carry, 0
+
+        mov     \Q0, [\src_reg + 8*(\index+1)]
+        mul     \mul_word
+        mov     \even_carry, \QH
+        add     \Q0, \odd_carry
+        mov     [\dst_reg + 8*(\index+1)], \Q0
+        adc     \even_carry, 0
+.endm
+
+.macro MULT_DOUBLEADD_128  index, src_reg, dst_reg, Q0, QH, mul_word, even_carry, odd_carry
+        // \Q0 = mul scratch
+        // \QH = mul scratch
+        // \mul_word = multiplier
+        // \src_reg = running ptr to input
+        // \dst_reg = running ptr to output/scratch
+        // \even_carry = carry for even words (64 bits)
+        // \odd_carry = carry for odd words (64 bits)
+
+        mov     \Q0, [\src_reg + 8*\index]
+        mul     \mul_word
+        mov     \odd_carry, \QH
+        add     \Q0, [\dst_reg + 8*\index]
+        adc     \odd_carry, 0
+        add     \Q0, \even_carry
+        mov     [\dst_reg + 8*\index], \Q0
+        adc     \odd_carry, 0
+
+        mov     \Q0, [\src_reg + 8*(\index+1)]
+        mul     \mul_word
+        mov     \even_carry, \QH
+        add     \Q0, [\dst_reg + 8*(\index+1)]
+        adc     \even_carry, 0
+        add     \Q0, \odd_carry
+        mov     [\dst_reg + 8*(\index+1)], \Q0
+        adc     \even_carry, 0
+.endm
+
+// Squaring
+
+.macro SQR_SINGLEADD_64  index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+        // \Q0 = mul scratch
+        // \QH = mul scratch
+        // \mul_word = multiplier
+        // \src_reg = running ptr to input
+        // \dst_reg = running ptr to output/scratch
+        // \src_carry = input carry
+        // \dst_carry = output carry
+
+        mov     \Q0, [\src_reg + 8*\index]
+        mul     \mul_word
+        mov     \dst_carry, \QH
+        add     \Q0, \src_carry
+        mov     [\dst_reg + 8*\index], \Q0
+        adc     \dst_carry, 0
+.endm
+
+.macro SQR_DOUBLEADD_64  index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+        // \Q0 = mul scratch
+        // \QH = mul scratch
+        // \mul_word = multiplier
+        // \src_reg = running ptr to input
+        // \dst_reg = running ptr to output/scratch
+        // \src_carry = input carry
+        // \dst_carry = output carry
+
+        mov     \Q0, [\src_reg + 8*\index]
+        mul     \mul_word
+        mov     \dst_carry, \QH
+        add     \Q0, [\dst_reg + 8*\index]
+        adc     \dst_carry, 0
+        add     \Q0, \src_carry
+        mov     [\dst_reg + 8*\index], \Q0
+        adc     \dst_carry, 0
+.endm
+
+.macro SQR_SHIFT_LEFT  index, Q0, src_reg
+    mov     \Q0, [\src_reg + 8*\index]
+    adc     \Q0, \Q0                 // Shift left and add the carry
+    mov     [\src_reg + 8*\index], \Q0
+.endm
+
+.macro SQR_DIAGONAL_PROP  index, src_reg, dst_reg, Q0, QH, carry
+    // Calculating the square
+    mov     \Q0, [\src_reg + 8*\index]     // mulword
+    mul     \Q0                     // m^2
+
+    // Adding the square to the even column
+    add     \Q0, [\dst_reg + 16*\index]
+    adc     \QH, 0
+    add     \Q0, \carry
+    adc     \QH, 0
+    mov     [\dst_reg + 16*\index], \Q0
+
+    // Propagating the sum to the next column
+    mov     \Q0, \QH
+    xor     \QH, \QH
+
+    add     \Q0, [\dst_reg + 16*\index + 8]
+    adc     \QH, 0
+    mov     [\dst_reg + 16*\index + 8], \Q0
+    mov     \carry, \QH
+.endm
+
+.macro MONTGOMERY14  Q0, QH, mul_word, pA, R0, R1, R2, R3, Cy
+    // (xx, \R1, \R2, \R3, \QH) = \mul_word * (A0..3) + (\R0, \R1, \R2, \R3)
+    // Used when it is statically known that \R0 will get set to 0, so we don't bother computing it
+    // \Cy, \Q0 = scratch
+
+    mov     \Q0, [\pA]
+    mul     \mul_word
+    add     \R0, -1  // set carry flag only when \R0 is non-zero
+    adc     \QH, 0
+    mov     \Cy, \QH
+
+    mov     \Q0, [\pA + 8]
+    mul     \mul_word
+    add     \R1, \Q0
+    adc     \QH, 0
+    add     \R1, \Cy
+    adc     \QH, 0
+    mov     \Cy, \QH
+
+    mov     \Q0, [\pA + 16]
+    mul     \mul_word
+    add     \R2, \Q0
+    adc     \QH, 0
+    add     \R2, \Cy
+    adc     \QH, 0
+    mov     \Cy, \QH
+
+    mov     \Q0, [\pA + 24]
+    mul     \mul_word
+    add     \R3, \Q0
+    adc     \QH, 0
+    add     \R3, \Cy
+    adc     \QH, 0
+.endm
+
+.macro MUL14  Q0, QH, mul_word, pA, R0, R1, R2, R3, Cy
+    // (\R0, \R1, \R2, \R3, \QH) = \mul_word * (A0..3) + (\R0, \R1, \R2, \R3)
+    // \Cy, \Q0 = scratch
+
+    mov     \Q0, [\pA]
+    mul     \mul_word
+    add     \R0, \Q0
+    adc     \QH, 0
+    mov     \Cy, \QH
+
+    mov     \Q0, [\pA + 8]
+    mul     \mul_word
+    add     \R1, \Q0
+    adc     \QH, 0
+    add     \R1, \Cy
+    adc     \QH, 0
+    mov     \Cy, \QH
+
+    mov     \Q0, [\pA + 16]
+    mul     \mul_word
+    add     \R2, \Q0
+    adc     \QH, 0
+    add     \R2, \Cy
+    adc     \QH, 0
+    mov     \Cy, \QH
+
+    mov     \Q0, [\pA + 24]
+    mul     \mul_word
+    add     \R3, \Q0
+    adc     \QH, 0
+    add     \R3, \Cy
+    adc     \QH, 0
+.endm
+
+// Macros for size-specific squaring
+.macro SQR_DOUBLEADD_64_2  index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+    SQR_DOUBLEADD_64    (\index),     \src_reg, \dst_reg, \Q0, \QH, \mul_word, \src_carry, \dst_carry
+    SQR_DOUBLEADD_64    (\index + 1), \src_reg, \dst_reg, \Q0, \QH, \mul_word, \dst_carry, \src_carry
+.endm
+
+.macro SQR_DOUBLEADD_64_4  index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+    SQR_DOUBLEADD_64_2  (\index),     \src_reg, \dst_reg, \Q0, \QH, \mul_word, \src_carry, \dst_carry
+    SQR_DOUBLEADD_64_2  (\index + 2), \src_reg, \dst_reg, \Q0, \QH, \mul_word, \src_carry, \dst_carry
+.endm
+
+.macro SQR_DOUBLEADD_64_8  index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+    SQR_DOUBLEADD_64_4  (\index),     \src_reg, \dst_reg, \Q0, \QH, \mul_word, \src_carry, \dst_carry
+    SQR_DOUBLEADD_64_4  (\index + 4), \src_reg, \dst_reg, \Q0, \QH, \mul_word, \src_carry, \dst_carry
+.endm
+
+.macro SQR_SIZE_SPECIFIC_INIT  outer_src_reg, outer_dst_reg, inner_src_reg, inner_dst_reg, mul_word
+    lea     \outer_src_reg, [\outer_src_reg + 8]  // move \outer_src_reg pointer 1 word over
+    lea     \outer_dst_reg, [\outer_dst_reg + 16] // move \outer_dst_reg pointer 2 words over
+
+    mov     \inner_src_reg, \outer_src_reg        // \inner_src_reg = \outer_src_reg
+    mov     \inner_dst_reg, \outer_dst_reg        // \inner_dst_reg = \outer_dst_reg
+
+    mov     \mul_word, [\outer_src_reg]           // Get the next mulword
+    lea     \inner_src_reg, [\inner_src_reg + 8]  // move \inner_src_reg pointer 1 word over
+.endm
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawAdd(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm)
+
+
+.seh_endprologue
+
+
+        // loop over each half digit
+        add     D4, D4
+        xor     Q0, Q0
+
+SymCryptFdefRawAddAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        adc     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        adc     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        adc     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        mov     Q0,[Q1 + 24]
+        adc     Q0,[Q2 + 24]
+        mov     [Q3 + 24], Q0
+
+        lea     Q1, [Q1 + 32]
+        lea     Q2, [Q2 + 32]
+        lea     Q3, [Q3 + 32]
+        dec     D4
+        jnz     SymCryptFdefRawAddAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSub(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm)
+
+
+.seh_endprologue
+
+
+        // loop over each half digit
+        add     D4, D4
+        xor     Q0, Q0
+
+SymCryptFdefRawSubAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        sbb     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        sbb     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        sbb     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        mov     Q0,[Q1 + 24]
+        sbb     Q0,[Q2 + 24]
+        mov     [Q3 + 24], Q0
+
+        lea     Q1,[Q1 + 32]
+        lea     Q2,[Q2 + 32]
+        lea     Q3,[Q3 + 32]
+        dec     D4
+        jnz     SymCryptFdefRawSubAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopy(
+//    _In_reads_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )      PCBYTE      pbSrc,
+//    _InOut_writes_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )  PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm)
+
+
+.seh_endprologue
+
+
+        add     D3, D3              // loop over half digits
+
+        movd    xmm0, D4            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+SymCryptFdefMaskedCopyAsmLoop:
+        movdqa  xmm2, [Q1]          // xmm2 = pSrc[i]
+        movdqa  xmm3, [Q2]          // xmm3 = pDst[i]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q2], xmm2
+
+        movdqa  xmm2, [Q1 + 16]     // xmm2 = pSrc[i + 16]
+        movdqa  xmm3, [Q2 + 16]     // xmm3 = pDst[i + 16]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q2 + 16], xmm2
+
+        // Move on to the next digit
+
+        add     Q1, 32
+        add     Q2, 32
+        dec     D3
+        jnz     SymCryptFdefMaskedCopyAsmLoop
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nDigits1 * SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                      UINT32      nDigits1,
+//    _In_reads_(nDigits2 * SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                      UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+
+.seh_endprologue
+
+mov Q2, QH
+mov Q5, [rsp + 88]
+
+        shl     Q2, 3           // nDigits1 * 8 = # words in Src1 to process
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // Q2 = # words left from Src1 to process
+        // Q3 = pSrc2
+        // Q4 = nDigits2
+        // Q5 = pDst (incremented in outer loop)
+        // Q6 = inner loop pointer into pSrc2
+        // Q7 = inner loop pointer into pDst
+        // Q8 = word from Src1 to multiply with
+        // Q9 = carry for even words (64 bits)
+        // Q10 = inner loop counter
+        // Q11 = carry for odd words (64 bits)
+
+
+        // Outer loop invariant established: Q1, Q3, Q4, Q5
+
+        mov     Q6, Q3          // Q6 = pSrc2
+        mov     Q7, Q5          // Q7 = pDst + outer loop ctr
+        mov     Q8, [Q1]        // mulword
+        xor     Q9, Q9
+        mov     Q10, Q4
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+
+ALIGN(16)
+
+SymCryptFdefRawMulAsmLoop1:
+        MULT_SINGLEADD_128 0, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_SINGLEADD_128 2, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_SINGLEADD_128 4, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_SINGLEADD_128 6, Q6, Q7, Q0, QH, Q8, Q9, Q11
+
+        lea     Q6,[Q6 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     Q10
+        jnz     SymCryptFdefRawMulAsmLoop1
+
+        mov     [Q7], Q9        // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q2
+
+ALIGN(16)
+
+SymCryptFdefRawMulAsmLoopOuter:
+
+        add     Q1, 8           // move to next word of pSrc1
+        add     Q5, 8           // move Dst pointer one word over
+        mov     Q8, [Q1]
+        mov     Q6, Q3
+        mov     Q7, Q5
+        xor     Q9, Q9
+        mov     Q10, Q4
+
+ALIGN(16)
+
+SymCryptFdefRawMulAsmLoop2:
+        MULT_DOUBLEADD_128 0, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_DOUBLEADD_128 2, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_DOUBLEADD_128 4, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_DOUBLEADD_128 6, Q6, Q7, Q0, QH, Q8, Q9, Q11
+
+        lea     Q6,[Q6 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     Q10
+        jnz     SymCryptFdefRawMulAsmLoop2
+
+        mov     [Q7], Q9        // write next word. (stays within Dst buffer)
+
+        dec     Q2
+        jnz     SymCryptFdefRawMulAsmLoopOuter
+
+
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)        PCUINT32    pSrc,
+//                                                          UINT32      nDigits,
+//   _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)    PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = outer loop pointer into pSrc
+        // Q2 = nDigits (constant)
+        // Q3 = pDst (constant)
+        // Q4 = inner loop pointer into pSrc
+        // Q5 = inner loop pointer into pDst
+        // Q6 = word from Src to multiply with
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = outer loop pointer into pDst
+        // Q10 = outer loop counter of #words left
+        // Q11 = inner loop counter of #words left
+        // Q12 = cyclic counter that specifies on which branch we jump into
+
+        ////////////////////////////////////////////////////////////////
+        // First Pass - Addition of the cross products x_i*x_j with i!=j
+        ////////////////////////////////////////////////////////////////
+        //
+        // At the beginning of each inner loop we will jump over the
+        // words that don't need processing. The decision of the jump
+        // will be based on the cyclic counter Q12.
+        //
+        // For the first pass we loop over **half** digits since having a smaller
+        // number of jumps (i.e. 4) is actually faster than having 8 jumps.
+        //
+        ////////////////////////////////////////////////////////////////
+
+        mov     [rsp + 64 /*MEMSLOT0*/], Q1   // save pSrc
+
+        mov     Q10, Q2             // nDigits
+        shl     Q10, 3              // Q10 = outer #words
+        mov     Q9, Q3              // Q9 = outer pDst
+
+        mov     Q4, Q1              // Q4 = inner pSrc
+        mov     Q5, Q3              // Q5 = inner pDst
+
+        // Initial inner loop overwrites Dst, which avoids adding the current Dst value
+
+        mov     Q6, [Q1]            // mulword
+
+        xor     Q7, Q7              // carry = 0
+        xor     Q8, Q8              // carry = 0
+
+        mov     Q11, Q10            // Q11 = inner #words
+        mov     [Q5], Q7            // Write 0 in the first word
+
+        // Skip over the first word
+        jmp     SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoopInit_Word0:
+        SQR_SINGLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoopInit_Word1:
+        SQR_SINGLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+        SQR_SINGLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        lea     Q4, [Q4 + 32]
+        lea     Q5, [Q5 + 32]
+        sub     Q11, 4
+        jnz     SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+        mov     [Q5], Q7                // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q10                     // Counter for the outer loop
+        mov     Q12, 1                  // Cyclic counter Q12 = 1
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmLoopOuter:
+
+        add     Q9, 8                   // move Dst pointer 1 word over
+
+        mov     Q4, Q1                  // Q4 = inner pSrc
+        mov     Q5, Q9                  // Q5 = inner pDst
+
+        mov     Q6, [Q1 + 8*Q12]        // Get the next mulword
+
+        inc     B12                     // Increment the cyclic counter by 1
+
+        mov     Q11, Q10                // # of words for the inner loop
+        add     Q11, 2
+        and     Q11, -4                 // Zero out the 2 lower bits
+
+        xor     Q7, Q7                  // carry = 0
+        xor     Q8, Q8                  // carry = 0
+
+        // Logic to find the correct jump
+        cmp     B12, 3
+        je      SymCryptFdefRawSquareAsmInnerLoop_Word3
+        cmp     B12, 2
+        je      SymCryptFdefRawSquareAsmInnerLoop_Word2
+        cmp     B12, 1
+        je      SymCryptFdefRawSquareAsmInnerLoop_Word1
+
+        // The following instructions are only executed when B12 == 4
+        xor     B12, B12                // Set it to 0 for the next iteration
+
+        add     Q1, 32                  // move pSrc 4 words over
+        add     Q9, 32                  // move destination 4 words over
+
+        mov     Q4, Q1                  // Q4 = inner pSrc
+        mov     Q5, Q9                  // Q5 = inner pDst
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word0:
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word1:
+        SQR_DOUBLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word2:
+        SQR_DOUBLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word3:
+        SQR_DOUBLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        lea     Q4, [Q4 + 32]
+        lea     Q5, [Q5 + 32]
+        sub     Q11, 4
+        jnz     SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+        mov     [Q5], Q7            // write next word. (stays within Dst buffer)
+
+        dec     Q10
+        cmp     Q10, 1
+        jne     SymCryptFdefRawSquareAsmLoopOuter
+
+        xor     QH, QH
+        mov     [Q9 + 40], QH       // Final word = 0
+
+
+        ////////////////////////////////////////////////////////////////
+        // Second Pass - Shifting all results 1 bit left
+        ////////////////////////////////////////////////////////////////
+
+        mov     Q10, Q2             // nDigits
+        mov     Q5, Q3              // pDst pointer
+        shl     Q10, 1              // 2*nDigits
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmSecondPass:
+        SQR_SHIFT_LEFT 0, Q0, Q5
+        SQR_SHIFT_LEFT 1, Q0, Q5
+        SQR_SHIFT_LEFT 2, Q0, Q5
+        SQR_SHIFT_LEFT 3, Q0, Q5
+
+        SQR_SHIFT_LEFT 4, Q0, Q5
+        SQR_SHIFT_LEFT 5, Q0, Q5
+        SQR_SHIFT_LEFT 6, Q0, Q5
+        SQR_SHIFT_LEFT 7, Q0, Q5
+
+        lea     Q5, [Q5 + 64]
+        dec     Q10
+        jnz     SymCryptFdefRawSquareAsmSecondPass
+
+        //////////////////////////////////////////////////////////////////////////////
+        // Third Pass - Adding the squares on the even columns and propagating the sum
+        //////////////////////////////////////////////////////////////////////////////
+
+        mov     Q1, [rsp + 64 /*MEMSLOT0*/]  // Q1 = pSrc
+
+SymCryptFdefRawSquareAsmThirdPass:
+        SQR_DIAGONAL_PROP 0, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 1, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 2, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 3, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 4, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 5, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 6, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 7, Q1, Q3, Q0, QH, Q10
+
+        add     Q1, 64              // One digit up
+        add     Q3, 128             // Two digits up
+        dec     Q2
+        jnz     SymCryptFdefRawSquareAsmThirdPass
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+//VOID
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]    // nDigits
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        mov     D13, D4         // outer loop counter
+        shl     D13, 3          // D13 is in words
+
+        xor     D9, D9
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = carry out from last word of previous loop iteration
+        // Q10 = running pointer in Src
+        // Q11 = running pointer in Mod
+        // Q12 = loop counter
+        // Q13 = loop counter
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                        // fetch word of Src we want to set to zero
+        mov     Q11, Q2
+        mov     Q10, Q1
+
+        imul    Q6, Q5                          // lower word is same for signed & unsigned multiply
+
+        mov     D12, D4
+        xor     D7, D7
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmInnerloop:
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q6 = multiplier
+        // Q1 = pointer to modulus value
+        // D13 = outer loop counter (words)
+        // D12 = inner loop counter (digits)
+        // Q10  = running ptr to modulus
+        // Q11 = running ptr to input/scratch
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+
+        MULT_DOUBLEADD_128 0, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 2, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 4, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 6, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        lea     Q10,[Q10 + 64]
+        lea     Q11,[Q11 + 64]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduceAsmInnerloop
+
+        add     Q7, Q9
+        mov     D9, 0
+        adc     Q9, 0
+        add     Q7, [Q11]
+        adc     Q9, 0
+        mov     [Q11], Q7
+
+        lea     Q2,[Q2 + 8]
+
+        dec     D13
+        jnz     SymCryptFdefMontgomeryReduceAsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     D12, D4         // loop ctr
+        mov     Q11, Q2         // pSrc
+        mov     Q10, Q1         // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q9,0' resulted in 0, 1, or 2
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmSubLoop:
+        mov     Q0,[Q11]
+        sbb     Q0,[Q10]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q11 + 8]
+        sbb     Q0,[Q10 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q11 + 16]
+        sbb     Q0,[Q10 + 16]
+        mov     [Q7 + 16], Q0
+
+        mov     Q0,[Q11 + 24]
+        sbb     Q0,[Q10 + 24]
+        mov     [Q7 + 24], Q0
+
+        mov     Q0,[Q11 + 32]
+        sbb     Q0,[Q10 + 32]
+        mov     [Q7 + 32], Q0
+
+        mov     Q0,[Q11 + 40]
+        sbb     Q0,[Q10 + 40]
+        mov     [Q7 + 40], Q0
+
+        mov     Q0,[Q11 + 48]
+        sbb     Q0,[Q10 + 48]
+        mov     [Q7 + 48], Q0
+
+        mov     Q0,[Q11 + 56]
+        sbb     Q0,[Q10 + 56]
+        mov     [Q7 + 56], Q0
+
+        lea     Q11,[Q11 + 64]
+        lea     Q10,[Q10 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduceAsmSubLoop
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q9 == 0 && Cy = 1
+        sbb     D9, 0
+
+        movd    xmm0, D9            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmMaskedCopyLoop:
+        movdqa  xmm2, [Q2]          // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3]          // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3], xmm2
+
+        movdqa  xmm2, [Q2 + 16]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 16]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 16], xmm2
+
+        movdqa  xmm2, [Q2 + 32]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 32]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 32], xmm2
+
+        movdqa  xmm2, [Q2 + 48]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 48]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 48], xmm2
+
+        // Move on to the next digit
+        lea     Q2,[Q2 + 64]
+        lea     Q3,[Q3 + 64]
+
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduceAsmMaskedCopyLoop
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+
+// --------------------------------
+// 256 and 384-bit size specific functions
+// --------------------------------
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+
+.seh_endprologue
+
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        // compute Src1 - Src2 into (Q0, Q5, Q6, Q7), Cy = inverted subtraction borrow
+
+        mov     Q0, [Q2 + 0*8]
+        sub     Q0, [Q3 + 0*8]
+        mov     Q5, [Q2 + 1*8]
+        sbb     Q5, [Q3 + 1*8]
+        mov     Q6, [Q2 + 2*8]
+        sbb     Q6, [Q3 + 2*8]
+        mov     Q7, [Q2 + 3*8]
+        sbb     Q7, [Q3 + 3*8]
+
+        // Q2, Q3: free
+        // Load Mod into (Q2, Q3, Q8, Q1)
+
+        mov     Q2, 0
+        cmovb   Q2, [Q1 + 0*8]
+        mov     Q3, 0
+        cmovb   Q3, [Q1 + 1*8]
+        mov     Q8, 0
+        cmovb   Q8, [Q1 + 2*8]
+        mov     Q1, [Q1 + 3*8]
+        cmovnb  Q1, Q2
+
+        // Add the (masked) modulus
+        add     Q0, Q2
+        adc     Q5, Q3
+        adc     Q6, Q8
+        adc     Q7, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        // compute Src1 - Src2 into (Q0, Q5, Q6, Q7, Q8, Q9), Cy = inverted subtraction borrow
+
+        mov     Q0, [Q2 + 0*8]
+        sub     Q0, [Q3 + 0*8]
+        mov     Q5, [Q2 + 1*8]
+        sbb     Q5, [Q3 + 1*8]
+        mov     Q6, [Q2 + 2*8]
+        sbb     Q6, [Q3 + 2*8]
+        mov     Q7, [Q2 + 3*8]
+        sbb     Q7, [Q3 + 3*8]
+        mov     Q8, [Q2 + 4*8]
+        sbb     Q8, [Q3 + 4*8]
+        mov     Q9, [Q2 + 5*8]
+        sbb     Q9, [Q3 + 5*8]
+
+        // Q2, Q3: free
+        // Load Mod into (Q2, Q3, Q10, Q11, Q12, Q1)
+
+        mov     Q2,  0
+        cmovb   Q2,  [Q1 + 0*8]
+        mov     Q3,  0
+        cmovb   Q3,  [Q1 + 1*8]
+        mov     Q10, 0
+        cmovb   Q10, [Q1 + 2*8]
+        mov     Q11, 0
+        cmovb   Q11, [Q1 + 3*8]
+        mov     Q12, 0
+        cmovb   Q12, [Q1 + 4*8]
+        mov     Q1,  [Q1 + 5*8]
+        cmovnb  Q1,  Q2
+
+        // Add the (masked) modulus
+        add     Q0, Q2
+        adc     Q5, Q3
+        adc     Q6, Q10
+        adc     Q7, Q11
+        adc     Q8, Q12
+        adc     Q9, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q9
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+//=================================================
+// Multiplication
+//
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefMontgomeryReduce256AsmInternal
+#endif
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+        // First we compute the product. The result will be in 8 registers
+        //       Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        mov     Q5, [Q2]
+        xor     Q8, Q8
+        xor     Q9, Q9
+        xor     Q10, Q10
+
+        mov     Q0, [Q3]
+        mul     Q5
+        mov     Q6, Q0
+        mov     Q7, QH
+
+        mov     Q0, [Q3 + 8]
+        mul     Q5
+        add     Q7, Q0
+        adc     Q8, QH
+
+        mov     Q0, [Q3 + 16]
+        mul     Q5
+        add     Q8, Q0
+        adc     Q9, QH
+
+        mov     Q0, [Q3 + 24]
+        mul     Q5
+        add     Q9, Q0
+        adc     Q10, QH
+
+        // Second row
+        mov     Q5, [Q2 + 8]
+        MUL14   Q0, QH, Q5, Q3, Q7, Q8, Q9, Q10, Q13
+        mov     Q11, QH
+
+        // third row
+        mov     Q5, [Q2 + 16]
+        MUL14   Q0, QH, Q5, Q3, Q8, Q9, Q10, Q11, Q13
+        mov     Q12, QH
+
+        // fourth row
+        mov     Q5, [Q2 + 24]
+        MUL14   Q0, QH, Q5, Q3, Q9, Q10, Q11, Q12, Q13
+        mov     Q13, QH
+
+SymCryptFdefMontgomeryReduce256AsmInternal: .global SymCryptFdefMontgomeryReduce256AsmInternal
+        // Invariant:
+        //   common prologue used
+        //   512-bit result in (Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13)
+        //   Q1 = pmMod
+        //   Q4 = pDst
+
+        mov     Q3, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        mov     Q5, Q6
+        imul    Q5, Q3             // lower word is the same for signed & unsigned multiply - Q5 = multiplicand for first row
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q6, Q7, Q8, Q9, Q6
+        mov     Q6, QH            // Save the out carries in (eventually) (Q6, Q7, Q8, Q9)
+
+        mov     Q5, Q7
+        imul    Q5, Q3
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q7, Q8, Q9, Q10, Q7
+        mov     Q7, QH            // Save the out carries in (eventually) (Q6, Q7, Q8, Q9)
+
+        mov     Q5, Q8
+        imul    Q5, Q3
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q8, Q9, Q10, Q11, Q8
+        mov     Q8, QH
+
+        mov     Q5, Q9
+        imul    Q5, Q3
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q9, Q10, Q11, Q12, Q9
+        // mov   Q9, QH
+
+        add     Q10, Q6
+        adc     Q11, Q7
+        adc     Q12, Q8
+        adc     Q13, QH
+
+        sbb     Q5, Q5        // Carry out from final addition in mask form
+
+        // reduced value in (Q10, Q11, Q12, Q13, -Q5), and it is less than 2*Modulus
+
+        mov     Q6, Q10
+        sub     Q6, [Q1]
+        mov     Q7, Q11
+        sbb     Q7, [Q1 + 8]
+        mov     Q8, Q12
+        sbb     Q8, [Q1 + 16]
+        mov     Q9, Q13
+        sbb     Q9, [Q1 + 24]
+
+        sbb     Q1, Q1        // Q1 = carry out mask
+
+        // Choose between the two
+        // addition carry = 1, then subtraction carry = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction carry = 0: pick 2nd result
+        // addition carry = 0 and subtraction carry = 1: pick first result
+
+        xor     Q1, Q5        // 0 = 2nd result, 1 = first result
+
+        cmovz   Q10, Q6
+        cmovz   Q11, Q7
+        cmovz   Q12, Q8
+        cmovz   Q13, Q9
+
+        mov     [Q4 +  0], Q10
+        mov     [Q4 +  8], Q11
+        mov     [Q4 + 16], Q12
+        mov     [Q4 + 24], Q13
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMontgomeryReduce256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+// Note we specify 4 arguments so that our prolog matches SymCryptFdefModMulMontgomery256Asm
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     Q4, Q3
+        mov     Q6,  [Q2 +  0]
+        mov     Q7,  [Q2 +  8]
+        mov     Q8,  [Q2 + 16]
+        mov     Q9,  [Q2 + 24]
+        mov     Q10, [Q2 + 32]
+        mov     Q11, [Q2 + 40]
+        mov     Q12, [Q2 + 48]
+        mov     Q13, [Q2 + 56]
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefMontgomeryReduce256AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+#if 0
+//////////////////////////////////////////////////////////////
+//
+// UNUSED ASSEMBLY FROM HERE
+// Kept in case we need it later, but removed at compile time
+// to avoid inclusion in produced binaries (the linker struggles
+// to remove unused assembly functions)
+//
+//////////////////////////////////////////////////////////////
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd256(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+
+.seh_endprologue
+
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        // compute Src1 + Src2 into (Q0, Q5, Q6, Q7) with carry out mask in Q8
+
+        mov     Q0, [Q2 + 0*8]
+        add     Q0, [Q3 + 0*8]
+        mov     Q5, [Q2 + 1*8]
+        adc     Q5, [Q3 + 1*8]
+        mov     Q6, [Q2 + 2*8]
+        adc     Q6, [Q3 + 2*8]
+        mov     Q7, [Q2 + 3*8]
+        adc     Q7, [Q3 + 3*8]
+        sbb     Q8, Q8                  // Q8 = carry out mask
+
+        // Q2, Q3: free
+        // Compute sum - Mod into (Q2, Q3, Q9, Q10) = sum - modulus, Q1 = carry out mask
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        mov     Q2,  Q0
+        sub     Q2,  [Q1 + 0*8]
+        mov     Q3,  Q5
+        sbb     Q3,  [Q1 + 1*8]
+        mov     Q9,  Q6
+        sbb     Q9,  [Q1 + 2*8]
+        mov     Q10, Q7
+        sbb     Q10, [Q1 + 3*8]
+
+        sbb     Q1, Q1                 // Q1 = carry out mask
+
+        // Choose between the two
+        // addition carry = 1, then subtraction carry = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction carry = 0: pick 2nd result
+        // addition carry = 0 and subtraction carry = 1: pick first result
+
+        xor     Q1, Q8            // 0 = 2nd result, 1 = first result
+
+        cmovz   Q0, Q2
+        cmovz   Q5, Q3
+        cmovz   Q6, Q9
+        cmovz   Q7, Q10
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomery256(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments so that our prolog matches SymCryptFdefModMulMontgomery256Asm
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        //  Result in   Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        // Q1 = pmMod
+        // Q2 = peSrc
+        // Q3 = peDst
+
+        mov     Q4, Q3
+        mov     Q5, [Q2]
+        xor     Q9, Q9
+        xor     Q10, Q10
+        xor     Q11, Q11
+        xor     Q12, Q12
+
+        // First we compute all the terms that need doubling
+
+        mov     Q0, [Q2 + 8]
+        mul     Q5
+        mov     Q7, Q0
+        mov     Q8, QH
+
+        mov     Q0, [Q2 + 16]
+        mul     Q5
+        add     Q8, Q0
+        adc     Q9, QH
+
+        mov     Q0, [Q2 + 24]
+        mul     Q5
+        add     Q9, Q0
+        adc     Q10, QH
+
+        mov     Q5, [Q2 + 8]
+        mov     Q0, [Q2 + 16]
+        mul     Q5
+        add     Q9, Q0
+        adc     QH, 0
+        mov     Q13, QH
+
+        mov     Q0, [Q2 + 24]
+        mul     Q5
+        add     Q10, Q0
+        adc     QH, 0
+        add     Q10, Q13
+        adc     Q11, QH
+
+        mov     Q5, [Q2 + 16]
+        mov     Q0, [Q2 + 24]
+        mul     Q5
+        add     Q11, Q0
+        adc     Q12, QH        // no overflow from this
+
+        // double these terms
+        xor     Q13, Q13
+
+        add     Q7, Q7
+        adc     Q8, Q8
+        adc     Q9, Q9
+        adc     Q10, Q10
+        adc     Q11, Q11
+        adc     Q12, Q12
+        adc     Q13, 0
+
+        mov     Q0, [Q2]
+        mul     Q0
+        mov     Q6, Q0
+        mov     Q5, QH
+
+        mov     Q0, [Q2 + 8]
+        mul     Q0
+
+        add     Q7, Q5
+        adc     Q8, Q0
+        adc     Q9, QH
+        sbb     Q3, Q3          // -carry
+
+        mov     Q0, [Q2 + 16]
+        mul     Q0
+
+        add     Q3, Q3
+        adc     Q10, Q0
+        adc     Q11, QH
+        sbb     Q3, Q3
+
+        mov     Q0, [Q2 + 24]
+        mul     Q0
+        add     Q3, Q3
+        adc     Q12, Q0
+        adc     Q13, QH
+
+        // See SymCryptFdefMontgomeryReduce256Asm for a discussion of this strange epilogue sequence
+        test    rsp,rsp
+        jne     SymCryptFdefMontgomeryReduce256AsmInternal       // jumps always
+
+        int     3
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+// --------------------------------
+// 512-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul512Asm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nWords)                              PUINT32     pDst )
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMul512Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMul512Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMul512Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMul512Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // Q2 = pSrc2 (constant)
+        // Q3 = # words left from Src1 to process
+        // Q4 = pDst (incremented in outer loop)
+        // Q5 = word from Src1 to multiply with
+        // Q6 = carry for even words (64 bits)
+        // Q7 = carry for odd words (64 bits)
+
+        shl     Q3, 3               // nDigits * 8 = # words in Src1 to process
+
+        mov     Q5, [Q1]            // mulword
+        xor     Q6, Q6              // carry
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+        MULT_SINGLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 64], Q6       // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+
+ALIGN(16)
+
+SymCryptFdefRawMul512AsmLoopOuter:
+
+        lea     Q1, [Q1 + 8]        // move to next word of pSrc1
+        lea     Q4, [Q4 + 8]        // move Dst pointer one word over
+
+        mov     Q5, [Q1]            // mulword
+        xor     Q6, Q6              // carry
+
+        MULT_DOUBLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 64], Q6       // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+        jnz     SymCryptFdefRawMul512AsmLoopOuter
+
+
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                       UINT32      nDigits,
+//   _Out_writes_(2*nWords)                              PUINT32     pDst )
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare512Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare512Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare512Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare512Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = outer loop pointer into pSrc
+        // Q2 = nDigits (constant)
+        // Q3 = pDst (constant)
+        // Q4 = inner loop pointer into pSrc
+        // Q5 = inner loop pointer into pDst
+        // Q6 = word from Src to multiply with
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = outer loop pointer into pDst
+        // Q10 = outer loop counter of #words left
+
+        mov     [rsp + 48 /*MEMSLOT0*/], Q1   // save pSrc
+
+        ////////////////////////////////////////////////////////////////
+        // First Pass - Addition of the cross products x_i*x_j with i!=j
+        ////////////////////////////////////////////////////////////////
+
+        mov     Q9, Q3              // Q9 = outer pDst
+
+        mov     Q4, Q1              // Q4 = inner pSrc
+        mov     Q5, Q3              // Q5 = inner pDst
+
+        // Initial inner loop overwrites Dst, which avoids adding the current Dst value
+        // 7 iterations
+        xor     Q8, Q8              // carry = 0 (for "odd" iterations set only the Q8 carry)
+        mov     Q6, [Q1]            // mulword
+        mov     [Q5], Q8            // Write 0 in the first word
+
+        SQR_SINGLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 4, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 5, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 6, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 7, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        mov     [Q5 + 8*8], Q7      // write last word, cannot overflow because Dst is at least 2 digits long
+        add     Q9, 8               // Skip over the first word
+
+        // 6 iterations
+        xor     Q7, Q7              // carry = 0 (for "even" iterations set only the Q7 carry)
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 6*8], Q7
+
+        // 5 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7  // Notice the dst_carry is Q7 since all the "double" macros have Q7 as src_carry
+        SQR_DOUBLEADD_64_4 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 5*8], Q7
+
+        // 4 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_4 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 4*8], Q7
+
+        // 3 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 3*8], Q7
+
+        // 2 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 2*8], Q7
+
+        // 1 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        mov     [Q5 + 8], Q7
+
+        xor     QH, QH
+        mov     [Q5 + 16], QH      // Final word = 0
+
+
+        ////////////////////////////////////////////////////////////////
+        // Second Pass - Shifting all results 1 bit left
+        ////////////////////////////////////////////////////////////////
+
+        xor     Q0, Q0              // carry flag = 0
+        mov     Q5, Q3              // pDst pointer
+
+        SQR_SHIFT_LEFT 0, Q0, Q5
+        SQR_SHIFT_LEFT 1, Q0, Q5
+        SQR_SHIFT_LEFT 2, Q0, Q5
+        SQR_SHIFT_LEFT 3, Q0, Q5
+
+        SQR_SHIFT_LEFT 4, Q0, Q5
+        SQR_SHIFT_LEFT 5, Q0, Q5
+        SQR_SHIFT_LEFT 6, Q0, Q5
+        SQR_SHIFT_LEFT 7, Q0, Q5
+
+        SQR_SHIFT_LEFT 8, Q0, Q5
+        SQR_SHIFT_LEFT 9, Q0, Q5
+        SQR_SHIFT_LEFT 10, Q0, Q5
+        SQR_SHIFT_LEFT 11, Q0, Q5
+
+        SQR_SHIFT_LEFT 12, Q0, Q5
+        SQR_SHIFT_LEFT 13, Q0, Q5
+        SQR_SHIFT_LEFT 14, Q0, Q5
+        SQR_SHIFT_LEFT 15, Q0, Q5
+
+        //////////////////////////////////////////////////////////////////////////////
+        // Third Pass - Adding the squares on the even columns and propagating the sum
+        //////////////////////////////////////////////////////////////////////////////
+
+        mov     Q1, [rsp + 48 /*MEMSLOT0*/]  // Q1 = pSrc
+        xor     Q7, Q7
+
+        SQR_DIAGONAL_PROP 0, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 1, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 2, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 3, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 4, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 5, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 6, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 7, Q1, Q3, Q0, QH, Q7
+
+
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+
+//VOID
+//SymCryptFdefMontgomeryReduce512Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce512Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce512Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce512Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce512Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]            // nDigits
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]              // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]              // modulus value
+
+        mov     D12, D4         // outer loop counter
+        shl     D12, 3          // D12 is in words
+
+        xor     D9, D9
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = carry out from last word of previous loop iteration
+        // Q10 = running pointer in Src
+        // Q11 = running pointer in Mod
+        // D12 = loop counter
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce512AsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                        // fetch word of Src we want to set to zero
+        mov     Q11, Q2
+        mov     Q10, Q1
+
+        imul    Q6, Q5                          // lower word is same for signed & unsigned multiply
+
+        xor     D7, D7
+
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q1 = pointer to modulus value
+        // Q6 = multiplier
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q10 = running ptr to modulus
+        // Q11 = running ptr to input/scratch
+        // D12 = outer loop counter (words)
+
+        MULT_DOUBLEADD_128 0, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 2, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 4, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 6, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        lea     Q11,[Q11 + 64]
+
+        add     Q7, Q9
+        mov     D9, 0
+        adc     Q9, 0
+        add     Q7, [Q11]
+        adc     Q9, 0
+        mov     [Q11], Q7
+
+        lea     Q2,[Q2 + 8]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduce512AsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     Q11, Q2         // pSrc
+        mov     Q10, Q1         // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q9,0' resulted in 0, 1, or 2
+        mov     Q0,[Q11]
+        sbb     Q0,[Q10]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q11 + 8]
+        sbb     Q0,[Q10 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q11 + 16]
+        sbb     Q0,[Q10 + 16]
+        mov     [Q7 + 16], Q0
+
+        mov     Q0,[Q11 + 24]
+        sbb     Q0,[Q10 + 24]
+        mov     [Q7 + 24], Q0
+
+        mov     Q0,[Q11 + 32]
+        sbb     Q0,[Q10 + 32]
+        mov     [Q7 + 32], Q0
+
+        mov     Q0,[Q11 + 40]
+        sbb     Q0,[Q10 + 40]
+        mov     [Q7 + 40], Q0
+
+        mov     Q0,[Q11 + 48]
+        sbb     Q0,[Q10 + 48]
+        mov     [Q7 + 48], Q0
+
+        mov     Q0,[Q11 + 56]
+        sbb     Q0,[Q10 + 56]
+        mov     [Q7 + 56], Q0
+
+        lea     Q11,[Q11 + 64]
+        lea     Q10,[Q10 + 64]
+        lea     Q7,[Q7 + 64]
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q9 == 0 && Cy = 1
+        sbb     D9, 0
+
+        movd    xmm0, D9            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce512AsmMaskedCopyLoop:
+        movdqa  xmm2, [Q2]          // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3]          // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3], xmm2
+
+        movdqa  xmm2, [Q2 + 16]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 16]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 16], xmm2
+
+        movdqa  xmm2, [Q2 + 32]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 32]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 32], xmm2
+
+        movdqa  xmm2, [Q2 + 48]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 48]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 48], xmm2
+
+        // Move on to the next digit
+        lea     Q2,[Q2 + 64]
+        lea     Q3,[Q3 + 64]
+
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduce512AsmMaskedCopyLoop
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+// --------------------------------
+// 1024-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul1024Asm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nWords)                              PUINT32     pDst )
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMul1024Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMul1024Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMul1024Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMul1024Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // Q2 = pSrc2 (constant)
+        // Q3 = # words left from Src1 to process
+        // Q4 = pDst (incremented in outer loop)
+        // Q5 = word from Src1 to multiply with
+        // Q6 = carry for even words (64 bits)
+        // Q7 = carry for odd words (64 bits)
+
+        shl     Q3, 3               // nDigits * 8 = # words in Src1 to process
+
+        mov     Q5, [Q1]          // mulword
+        xor     Q6, Q6              // carry
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+        MULT_SINGLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        MULT_SINGLEADD_128 8, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 10, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 12, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 14, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 128], Q6          // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+
+ALIGN(16)
+
+SymCryptFdefRawMul1024AsmLoopOuter:
+
+        lea     Q1, [Q1 + 8]        // move to next word of pSrc1
+        lea     Q4, [Q4 + 8]        // move Dst pointer one word over
+
+        mov     Q5, [Q1]            // mulword
+
+        xor     Q6, Q6              // carry
+
+        MULT_DOUBLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        MULT_DOUBLEADD_128 8, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 10, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 12, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 14, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 128], Q6    // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+        jnz     SymCryptFdefRawMul1024AsmLoopOuter
+
+
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                       UINT32      nDigits,
+//   _Out_writes_(2*nWords)                              PUINT32     pDst )
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare1024Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare1024Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare1024Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare1024Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = outer loop pointer into pSrc
+        // Q2 = nDigits (constant)
+        // Q3 = pDst (constant)
+        // Q4 = inner loop pointer into pSrc
+        // Q5 = inner loop pointer into pDst
+        // Q6 = word from Src to multiply with
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = outer loop pointer into pDst
+        // Q10 = outer loop counter of #words left
+
+        mov     [rsp + 48 /*MEMSLOT0*/], Q1   // save pSrc
+
+        ////////////////////////////////////////////////////////////////
+        // First Pass - Addition of the cross products x_i*x_j with i!=j
+        ////////////////////////////////////////////////////////////////
+
+        mov     Q9, Q3              // Q9 = outer pDst
+
+        mov     Q4, Q1              // Q4 = inner pSrc
+        mov     Q5, Q3              // Q5 = inner pDst
+
+        // Initial inner loop overwrites Dst, which avoids adding the current Dst value
+
+        // 15 iterations
+        xor     Q8, Q8              // carry = 0 (for "odd" iterations set only the Q8 carry)
+        mov     Q6, [Q1]            // mulword
+        mov     [Q5], Q8            // Write 0 in the first word
+
+        SQR_SINGLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 4, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 5, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 6, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 7, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 8, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 9, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 10, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 11, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 12, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 13, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 14, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 15, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        mov     [Q5 + 16*8], Q7     // write last word, cannot overflow because Dst is at least 2 digits long
+        add     Q9, 8               // Skip over the first word
+
+        // 14 iterations (adding the current Dst value)
+        xor     Q7, Q7            // carry = 0 (for "even" iterations set only the Q7 carry)
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 6, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 14*8], Q7
+
+        // 13 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7  // Notice the dst_carry is Q7 since all the "double" macros have Q7 as src_carry
+        SQR_DOUBLEADD_64_4 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 5, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 13*8], Q7
+
+        // 12 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_4 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 4, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 12*8], Q7
+
+        // 11 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 3, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 11*8], Q7
+
+        // 10 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 10*8], Q7
+
+        // 9 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_8 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 9*8], Q7
+
+        // 8 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_8 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 8*8], Q7
+
+        // 7 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 3, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 7*8], Q7
+
+        // 6 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 6*8], Q7
+
+        // 5 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_4 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 5*8], Q7
+
+        // 4 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_4 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 4*8], Q7
+
+        // 3 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 3*8], Q7
+
+        // 2 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 2*8], Q7
+
+        // 1 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        mov     [Q5 + 8], Q7
+
+        xor     QH, QH
+        mov     [Q5 + 16], QH       // Final word = 0
+
+
+        ////////////////////////////////////////////////////////////////
+        // Second Pass - Shifting all results 1 bit left
+        ////////////////////////////////////////////////////////////////
+
+        xor     Q0, Q0              // carry flag = 0
+        mov     Q5, Q3              // pDst pointer
+
+        SQR_SHIFT_LEFT 0, Q0, Q5
+        SQR_SHIFT_LEFT 1, Q0, Q5
+        SQR_SHIFT_LEFT 2, Q0, Q5
+        SQR_SHIFT_LEFT 3, Q0, Q5
+
+        SQR_SHIFT_LEFT 4, Q0, Q5
+        SQR_SHIFT_LEFT 5, Q0, Q5
+        SQR_SHIFT_LEFT 6, Q0, Q5
+        SQR_SHIFT_LEFT 7, Q0, Q5
+
+        SQR_SHIFT_LEFT 8, Q0, Q5
+        SQR_SHIFT_LEFT 9, Q0, Q5
+        SQR_SHIFT_LEFT 10, Q0, Q5
+        SQR_SHIFT_LEFT 11, Q0, Q5
+
+        SQR_SHIFT_LEFT 12, Q0, Q5
+        SQR_SHIFT_LEFT 13, Q0, Q5
+        SQR_SHIFT_LEFT 14, Q0, Q5
+        SQR_SHIFT_LEFT 15, Q0, Q5
+
+        SQR_SHIFT_LEFT 16, Q0, Q5
+        SQR_SHIFT_LEFT 17, Q0, Q5
+        SQR_SHIFT_LEFT 18, Q0, Q5
+        SQR_SHIFT_LEFT 19, Q0, Q5
+
+        SQR_SHIFT_LEFT 20, Q0, Q5
+        SQR_SHIFT_LEFT 21, Q0, Q5
+        SQR_SHIFT_LEFT 22, Q0, Q5
+        SQR_SHIFT_LEFT 23, Q0, Q5
+
+        SQR_SHIFT_LEFT 24, Q0, Q5
+        SQR_SHIFT_LEFT 25, Q0, Q5
+        SQR_SHIFT_LEFT 26, Q0, Q5
+        SQR_SHIFT_LEFT 27, Q0, Q5
+
+        SQR_SHIFT_LEFT 28, Q0, Q5
+        SQR_SHIFT_LEFT 29, Q0, Q5
+        SQR_SHIFT_LEFT 30, Q0, Q5
+        SQR_SHIFT_LEFT 31, Q0, Q5
+
+        //////////////////////////////////////////////////////////////////////////////
+        // Third Pass - Adding the squares on the even columns and propagating the sum
+        //////////////////////////////////////////////////////////////////////////////
+
+        mov     Q1, [rsp + 48 /*MEMSLOT0*/]  // Q1 = pSrc
+        xor     Q7, Q7
+
+        SQR_DIAGONAL_PROP 0, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 1, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 2, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 3, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 4, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 5, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 6, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 7, Q1, Q3, Q0, QH, Q7
+
+        SQR_DIAGONAL_PROP 8, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 9, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 10, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 11, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 12, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 13, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 14, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 15, Q1, Q3, Q0, QH, Q7
+
+
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+
+//VOID
+//SymCryptFdefMontgomeryReduce1024Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce1024Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce1024Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce1024Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce1024Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]    // nDigits
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        mov     D12, D4         // outer loop counter
+        shl     D12, 3          // D12 is in words
+
+        xor     D9, D9
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = carry out from last word of previous loop iteration
+        // Q10 = running pointer in Src
+        // Q11 = running pointer in Mod
+        // D12 = loop counter
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce1024AsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                      // fetch word of Src we want to set to zero
+        mov     Q11, Q2
+        mov     Q10, Q1
+
+        imul    Q6, Q5                        // lower word is same for signed & unsigned multiply
+
+        xor     D7, D7
+
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q1 = pointer to modulus value
+        // Q6 = multiplier
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q10  = running ptr to modulus
+        // Q11 = running ptr to input/scratch
+        // D12 = outer loop counter (words)
+
+        MULT_DOUBLEADD_128 0, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 2, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 4, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 6, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        MULT_DOUBLEADD_128 8, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 10, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 12, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 14, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        lea     Q11,[Q11 + 128]
+
+        add     Q7, Q9
+        mov     D9, 0
+        adc     Q9, 0
+        add     Q7, [Q11]
+        adc     Q9, 0
+        mov     [Q11], Q7
+
+        lea     Q2,[Q2 + 8]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduce1024AsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     D12, D4         // loop ctr
+        mov     Q11, Q2         // pSrc
+        mov     Q10, Q1         // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q9,0' resulted in 0, 1, or 2
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce1024AsmSubLoop:
+        mov     Q0,[Q11]
+        sbb     Q0,[Q10]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q11 + 8]
+        sbb     Q0,[Q10 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q11 + 16]
+        sbb     Q0,[Q10 + 16]
+        mov     [Q7 + 16], Q0
+
+        mov     Q0,[Q11 + 24]
+        sbb     Q0,[Q10 + 24]
+        mov     [Q7 + 24], Q0
+
+        mov     Q0,[Q11 + 32]
+        sbb     Q0,[Q10 + 32]
+        mov     [Q7 + 32], Q0
+
+        mov     Q0,[Q11 + 40]
+        sbb     Q0,[Q10 + 40]
+        mov     [Q7 + 40], Q0
+
+        mov     Q0,[Q11 + 48]
+        sbb     Q0,[Q10 + 48]
+        mov     [Q7 + 48], Q0
+
+        mov     Q0,[Q11 + 56]
+        sbb     Q0,[Q10 + 56]
+        mov     [Q7 + 56], Q0
+
+        lea     Q11,[Q11 + 64]
+        lea     Q10,[Q10 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduce1024AsmSubLoop
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q9 == 0 && Cy = 1
+        sbb     D9, 0
+
+        movd    xmm0, D9            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce1024AsmMaskedCopyLoop:
+        movdqa  xmm2, [Q2]          // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3]          // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3], xmm2
+
+        movdqa  xmm2, [Q2 + 16]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 16]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 16], xmm2
+
+        movdqa  xmm2, [Q2 + 32]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 32]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 32], xmm2
+
+        movdqa  xmm2, [Q2 + 48]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 48]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 48], xmm2
+
+        // Move on to the next digit
+        lea     Q2,[Q2 + 64]
+        lea     Q3,[Q3 + 64]
+
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduce1024AsmMaskedCopyLoop
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#endif
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/fdef_asm.symcryptasm b/libs/symcrypt/lib/amd64/fdef_asm.symcryptasm
new file mode 100644
index 000000000000..80375f3f73f2
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/fdef_asm.symcryptasm
@@ -0,0 +1,2179 @@
+//
+//  fdef_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+
+MACRO_START(MULT_SINGLEADD_128, index, src_reg, dst_reg, Q0, QH, mul_word, even_carry, odd_carry)
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // mul_word = multiplier
+        // src_reg = running ptr to input
+        // dst_reg = running ptr to output/scratch
+        // even_carry = carry for even words (64 bits)
+        // odd_carry = carry for odd words (64 bits)
+
+        mov     Q0, [src_reg + 8*index]
+        mul     mul_word
+        mov     odd_carry, QH
+        add     Q0, even_carry
+        mov     [dst_reg + 8*index], Q0
+        adc     odd_carry, 0
+
+        mov     Q0, [src_reg + 8*(index+1)]
+        mul     mul_word
+        mov     even_carry, QH
+        add     Q0, odd_carry
+        mov     [dst_reg + 8*(index+1)], Q0
+        adc     even_carry, 0
+MACRO_END()
+
+MACRO_START(MULT_DOUBLEADD_128, index, src_reg, dst_reg, Q0, QH, mul_word, even_carry, odd_carry)
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // mul_word = multiplier
+        // src_reg = running ptr to input
+        // dst_reg = running ptr to output/scratch
+        // even_carry = carry for even words (64 bits)
+        // odd_carry = carry for odd words (64 bits)
+
+        mov     Q0, [src_reg + 8*index]
+        mul     mul_word
+        mov     odd_carry, QH
+        add     Q0, [dst_reg + 8*index]
+        adc     odd_carry, 0
+        add     Q0, even_carry
+        mov     [dst_reg + 8*index], Q0
+        adc     odd_carry, 0
+
+        mov     Q0, [src_reg + 8*(index+1)]
+        mul     mul_word
+        mov     even_carry, QH
+        add     Q0, [dst_reg + 8*(index+1)]
+        adc     even_carry, 0
+        add     Q0, odd_carry
+        mov     [dst_reg + 8*(index+1)], Q0
+        adc     even_carry, 0
+MACRO_END()
+
+// Squaring
+
+MACRO_START(SQR_SINGLEADD_64, index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry)
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // mul_word = multiplier
+        // src_reg = running ptr to input
+        // dst_reg = running ptr to output/scratch
+        // src_carry = input carry
+        // dst_carry = output carry
+
+        mov     Q0, [src_reg + 8*index]
+        mul     mul_word
+        mov     dst_carry, QH
+        add     Q0, src_carry
+        mov     [dst_reg + 8*index], Q0
+        adc     dst_carry, 0
+MACRO_END()
+
+MACRO_START(SQR_DOUBLEADD_64, index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry)
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // mul_word = multiplier
+        // src_reg = running ptr to input
+        // dst_reg = running ptr to output/scratch
+        // src_carry = input carry
+        // dst_carry = output carry
+
+        mov     Q0, [src_reg + 8*index]
+        mul     mul_word
+        mov     dst_carry, QH
+        add     Q0, [dst_reg + 8*index]
+        adc     dst_carry, 0
+        add     Q0, src_carry
+        mov     [dst_reg + 8*index], Q0
+        adc     dst_carry, 0
+MACRO_END()
+
+MACRO_START(SQR_SHIFT_LEFT, index, Q0, src_reg)
+    mov     Q0, [src_reg + 8*index]
+    adc     Q0, Q0                 // Shift left and add the carry
+    mov     [src_reg + 8*index], Q0
+MACRO_END()
+
+MACRO_START(SQR_DIAGONAL_PROP, index, src_reg, dst_reg, Q0, QH, carry)
+    // Calculating the square
+    mov     Q0, [src_reg + 8*index]     // mulword
+    mul     Q0                     // m^2
+
+    // Adding the square to the even column
+    add     Q0, [dst_reg + 16*index]
+    adc     QH, 0
+    add     Q0, carry
+    adc     QH, 0
+    mov     [dst_reg + 16*index], Q0
+
+    // Propagating the sum to the next column
+    mov     Q0, QH
+    xor     QH, QH
+
+    add     Q0, [dst_reg + 16*index + 8]
+    adc     QH, 0
+    mov     [dst_reg + 16*index + 8], Q0
+    mov     carry, QH
+MACRO_END()
+
+MACRO_START(MONTGOMERY14, Q0, QH, mul_word, pA, R0, R1, R2, R3, Cy)
+    // (xx, R1, R2, R3, QH) = mul_word * (A0..3) + (R0, R1, R2, R3)
+    // Used when it is statically known that R0 will get set to 0, so we don't bother computing it
+    // Cy, Q0 = scratch
+
+    mov     Q0, [pA]
+    mul     mul_word
+    add     R0, -1  // set carry flag only when R0 is non-zero
+    adc     QH, 0
+    mov     Cy, QH
+
+    mov     Q0, [pA + 8]
+    mul     mul_word
+    add     R1, Q0
+    adc     QH, 0
+    add     R1, Cy
+    adc     QH, 0
+    mov     Cy, QH
+
+    mov     Q0, [pA + 16]
+    mul     mul_word
+    add     R2, Q0
+    adc     QH, 0
+    add     R2, Cy
+    adc     QH, 0
+    mov     Cy, QH
+
+    mov     Q0, [pA + 24]
+    mul     mul_word
+    add     R3, Q0
+    adc     QH, 0
+    add     R3, Cy
+    adc     QH, 0
+MACRO_END()
+
+MACRO_START(MUL14, Q0, QH, mul_word, pA, R0, R1, R2, R3, Cy)
+    // (R0, R1, R2, R3, QH) = mul_word * (A0..3) + (R0, R1, R2, R3)
+    // Cy, Q0 = scratch
+
+    mov     Q0, [pA]
+    mul     mul_word
+    add     R0, Q0
+    adc     QH, 0
+    mov     Cy, QH
+
+    mov     Q0, [pA + 8]
+    mul     mul_word
+    add     R1, Q0
+    adc     QH, 0
+    add     R1, Cy
+    adc     QH, 0
+    mov     Cy, QH
+
+    mov     Q0, [pA + 16]
+    mul     mul_word
+    add     R2, Q0
+    adc     QH, 0
+    add     R2, Cy
+    adc     QH, 0
+    mov     Cy, QH
+
+    mov     Q0, [pA + 24]
+    mul     mul_word
+    add     R3, Q0
+    adc     QH, 0
+    add     R3, Cy
+    adc     QH, 0
+MACRO_END()
+
+// Macros for size-specific squaring
+MACRO_START(SQR_DOUBLEADD_64_2, index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry)
+    SQR_DOUBLEADD_64    (index),     src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+    SQR_DOUBLEADD_64    (index + 1), src_reg, dst_reg, Q0, QH, mul_word, dst_carry, src_carry
+MACRO_END()
+
+MACRO_START(SQR_DOUBLEADD_64_4, index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry)
+    SQR_DOUBLEADD_64_2  (index),     src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+    SQR_DOUBLEADD_64_2  (index + 2), src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+MACRO_END()
+
+MACRO_START(SQR_DOUBLEADD_64_8, index, src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry)
+    SQR_DOUBLEADD_64_4  (index),     src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+    SQR_DOUBLEADD_64_4  (index + 4), src_reg, dst_reg, Q0, QH, mul_word, src_carry, dst_carry
+MACRO_END()
+
+MACRO_START(SQR_SIZE_SPECIFIC_INIT, outer_src_reg, outer_dst_reg, inner_src_reg, inner_dst_reg, mul_word)
+    lea     outer_src_reg, [outer_src_reg + 8]  // move outer_src_reg pointer 1 word over
+    lea     outer_dst_reg, [outer_dst_reg + 16] // move outer_dst_reg pointer 2 words over
+
+    mov     inner_src_reg, outer_src_reg        // inner_src_reg = outer_src_reg
+    mov     inner_dst_reg, outer_dst_reg        // inner_dst_reg = outer_dst_reg
+
+    mov     mul_word, [outer_src_reg]           // Get the next mulword
+    lea     inner_src_reg, [inner_src_reg + 8]  // move inner_src_reg pointer 1 word over
+MACRO_END()
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawAdd(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm), 4, 5)
+
+        // loop over each half digit
+        add     D4, D4
+        xor     Q0, Q0
+
+SymCryptFdefRawAddAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        adc     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        adc     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        adc     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        mov     Q0,[Q1 + 24]
+        adc     Q0,[Q2 + 24]
+        mov     [Q3 + 24], Q0
+
+        lea     Q1, [Q1 + 32]
+        lea     Q2, [Q2 + 32]
+        lea     Q3, [Q3 + 32]
+        dec     D4
+        jnz     SymCryptFdefRawAddAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm))
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSub(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm), 4, 5)
+
+        // loop over each half digit
+        add     D4, D4
+        xor     Q0, Q0
+
+SymCryptFdefRawSubAsmLoop:
+        // carry is in the carry flag
+        mov     Q0,[Q1]
+        sbb     Q0,[Q2]
+        mov     [Q3],Q0
+
+        mov     Q0,[Q1 + 8]
+        sbb     Q0,[Q2 + 8]
+        mov     [Q3 + 8], Q0
+
+        mov     Q0,[Q1 + 16]
+        sbb     Q0,[Q2 + 16]
+        mov     [Q3 + 16], Q0
+
+        mov     Q0,[Q1 + 24]
+        sbb     Q0,[Q2 + 24]
+        mov     [Q3 + 24], Q0
+
+        lea     Q1,[Q1 + 32]
+        lea     Q2,[Q2 + 32]
+        lea     Q3,[Q3 + 32]
+        dec     D4
+        jnz     SymCryptFdefRawSubAsmLoop
+
+        mov     Q0, 0
+        adc     Q0, Q0
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopy(
+//    _In_reads_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )      PCBYTE      pbSrc,
+//    _InOut_writes_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )  PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm), 4, 4)
+
+        add     D3, D3              // loop over half digits
+
+        movd    xmm0, D4            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+SymCryptFdefMaskedCopyAsmLoop:
+        movdqa  xmm2, [Q1]          // xmm2 = pSrc[i]
+        movdqa  xmm3, [Q2]          // xmm3 = pDst[i]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q2], xmm2
+
+        movdqa  xmm2, [Q1 + 16]     // xmm2 = pSrc[i + 16]
+        movdqa  xmm3, [Q2 + 16]     // xmm3 = pDst[i + 16]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q2 + 16], xmm2
+
+        // Move on to the next digit
+
+        add     Q1, 32
+        add     Q2, 32
+        dec     D3
+        jnz     SymCryptFdefMaskedCopyAsmLoop
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm))
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nDigits1 * SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                      UINT32      nDigits1,
+//    _In_reads_(nDigits2 * SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                      UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm), 5, 12)
+
+        shl     Q2, 3           // nDigits1 * 8 = # words in Src1 to process
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // Q2 = # words left from Src1 to process
+        // Q3 = pSrc2
+        // Q4 = nDigits2
+        // Q5 = pDst (incremented in outer loop)
+        // Q6 = inner loop pointer into pSrc2
+        // Q7 = inner loop pointer into pDst
+        // Q8 = word from Src1 to multiply with
+        // Q9 = carry for even words (64 bits)
+        // Q10 = inner loop counter
+        // Q11 = carry for odd words (64 bits)
+
+
+        // Outer loop invariant established: Q1, Q3, Q4, Q5
+
+        mov     Q6, Q3          // Q6 = pSrc2
+        mov     Q7, Q5          // Q7 = pDst + outer loop ctr
+        mov     Q8, [Q1]        // mulword
+        xor     Q9, Q9
+        mov     Q10, Q4
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+
+ALIGN(16)
+
+SymCryptFdefRawMulAsmLoop1:
+        MULT_SINGLEADD_128 0, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_SINGLEADD_128 2, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_SINGLEADD_128 4, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_SINGLEADD_128 6, Q6, Q7, Q0, QH, Q8, Q9, Q11
+
+        lea     Q6,[Q6 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     Q10
+        jnz     SymCryptFdefRawMulAsmLoop1
+
+        mov     [Q7], Q9        // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q2
+
+ALIGN(16)
+
+SymCryptFdefRawMulAsmLoopOuter:
+
+        add     Q1, 8           // move to next word of pSrc1
+        add     Q5, 8           // move Dst pointer one word over
+        mov     Q8, [Q1]
+        mov     Q6, Q3
+        mov     Q7, Q5
+        xor     Q9, Q9
+        mov     Q10, Q4
+
+ALIGN(16)
+
+SymCryptFdefRawMulAsmLoop2:
+        MULT_DOUBLEADD_128 0, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_DOUBLEADD_128 2, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_DOUBLEADD_128 4, Q6, Q7, Q0, QH, Q8, Q9, Q11
+        MULT_DOUBLEADD_128 6, Q6, Q7, Q0, QH, Q8, Q9, Q11
+
+        lea     Q6,[Q6 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     Q10
+        jnz     SymCryptFdefRawMulAsmLoop2
+
+        mov     [Q7], Q9        // write next word. (stays within Dst buffer)
+
+        dec     Q2
+        jnz     SymCryptFdefRawMulAsmLoopOuter
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)        PCUINT32    pSrc,
+//                                                          UINT32      nDigits,
+//   _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)    PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm), 3, 13)
+
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = outer loop pointer into pSrc
+        // Q2 = nDigits (constant)
+        // Q3 = pDst (constant)
+        // Q4 = inner loop pointer into pSrc
+        // Q5 = inner loop pointer into pDst
+        // Q6 = word from Src to multiply with
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = outer loop pointer into pDst
+        // Q10 = outer loop counter of #words left
+        // Q11 = inner loop counter of #words left
+        // Q12 = cyclic counter that specifies on which branch we jump into
+
+        ////////////////////////////////////////////////////////////////
+        // First Pass - Addition of the cross products x_i*x_j with i!=j
+        ////////////////////////////////////////////////////////////////
+        //
+        // At the beginning of each inner loop we will jump over the
+        // words that don't need processing. The decision of the jump
+        // will be based on the cyclic counter Q12.
+        //
+        // For the first pass we loop over **half** digits since having a smaller
+        // number of jumps (i.e. 4) is actually faster than having 8 jumps.
+        //
+        ////////////////////////////////////////////////////////////////
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q1   // save pSrc
+
+        mov     Q10, Q2             // nDigits
+        shl     Q10, 3              // Q10 = outer #words
+        mov     Q9, Q3              // Q9 = outer pDst
+
+        mov     Q4, Q1              // Q4 = inner pSrc
+        mov     Q5, Q3              // Q5 = inner pDst
+
+        // Initial inner loop overwrites Dst, which avoids adding the current Dst value
+
+        mov     Q6, [Q1]            // mulword
+
+        xor     Q7, Q7              // carry = 0
+        xor     Q8, Q8              // carry = 0
+
+        mov     Q11, Q10            // Q11 = inner #words
+        mov     [Q5], Q7            // Write 0 in the first word
+
+        // Skip over the first word
+        jmp     SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoopInit_Word0:
+        SQR_SINGLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoopInit_Word1:
+        SQR_SINGLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+        SQR_SINGLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        lea     Q4, [Q4 + 32]
+        lea     Q5, [Q5 + 32]
+        sub     Q11, 4
+        jnz     SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+        mov     [Q5], Q7                // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q10                     // Counter for the outer loop
+        mov     Q12, 1                  // Cyclic counter Q12 = 1
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmLoopOuter:
+
+        add     Q9, 8                   // move Dst pointer 1 word over
+
+        mov     Q4, Q1                  // Q4 = inner pSrc
+        mov     Q5, Q9                  // Q5 = inner pDst
+
+        mov     Q6, [Q1 + 8*Q12]        // Get the next mulword
+
+        inc     B12                     // Increment the cyclic counter by 1
+
+        mov     Q11, Q10                // # of words for the inner loop
+        add     Q11, 2
+        and     Q11, -4                 // Zero out the 2 lower bits
+
+        xor     Q7, Q7                  // carry = 0
+        xor     Q8, Q8                  // carry = 0
+
+        // Logic to find the correct jump
+        cmp     B12, 3
+        je      SymCryptFdefRawSquareAsmInnerLoop_Word3
+        cmp     B12, 2
+        je      SymCryptFdefRawSquareAsmInnerLoop_Word2
+        cmp     B12, 1
+        je      SymCryptFdefRawSquareAsmInnerLoop_Word1
+
+        // The following instructions are only executed when B12 == 4
+        xor     B12, B12                // Set it to 0 for the next iteration
+
+        add     Q1, 32                  // move pSrc 4 words over
+        add     Q9, 32                  // move destination 4 words over
+
+        mov     Q4, Q1                  // Q4 = inner pSrc
+        mov     Q5, Q9                  // Q5 = inner pDst
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word0:
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word1:
+        SQR_DOUBLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word2:
+        SQR_DOUBLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmInnerLoop_Word3:
+        SQR_DOUBLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        lea     Q4, [Q4 + 32]
+        lea     Q5, [Q5 + 32]
+        sub     Q11, 4
+        jnz     SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+        mov     [Q5], Q7            // write next word. (stays within Dst buffer)
+
+        dec     Q10
+        cmp     Q10, 1
+        jne     SymCryptFdefRawSquareAsmLoopOuter
+
+        xor     QH, QH
+        mov     [Q9 + 40], QH       // Final word = 0
+
+
+        ////////////////////////////////////////////////////////////////
+        // Second Pass - Shifting all results 1 bit left
+        ////////////////////////////////////////////////////////////////
+
+        mov     Q10, Q2             // nDigits
+        mov     Q5, Q3              // pDst pointer
+        shl     Q10, 1              // 2*nDigits
+
+ALIGN(16)
+SymCryptFdefRawSquareAsmSecondPass:
+        SQR_SHIFT_LEFT 0, Q0, Q5
+        SQR_SHIFT_LEFT 1, Q0, Q5
+        SQR_SHIFT_LEFT 2, Q0, Q5
+        SQR_SHIFT_LEFT 3, Q0, Q5
+
+        SQR_SHIFT_LEFT 4, Q0, Q5
+        SQR_SHIFT_LEFT 5, Q0, Q5
+        SQR_SHIFT_LEFT 6, Q0, Q5
+        SQR_SHIFT_LEFT 7, Q0, Q5
+
+        lea     Q5, [Q5 + 64]
+        dec     Q10
+        jnz     SymCryptFdefRawSquareAsmSecondPass
+
+        //////////////////////////////////////////////////////////////////////////////
+        // Third Pass - Adding the squares on the even columns and propagating the sum
+        //////////////////////////////////////////////////////////////////////////////
+
+        mov     Q1, [rsp + GET_MEMSLOT_OFFSET(slot0)]  // Q1 = pSrc
+
+SymCryptFdefRawSquareAsmThirdPass:
+        SQR_DIAGONAL_PROP 0, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 1, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 2, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 3, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 4, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 5, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 6, Q1, Q3, Q0, QH, Q10
+        SQR_DIAGONAL_PROP 7, Q1, Q3, Q0, QH, Q10
+
+        add     Q1, 64              // One digit up
+        add     Q3, 128             // Two digits up
+        dec     Q2
+        jnz     SymCryptFdefRawSquareAsmThirdPass
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm))
+
+//VOID
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm), 3, 14)
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]    // nDigits
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        mov     D13, D4         // outer loop counter
+        shl     D13, 3          // D13 is in words
+
+        xor     D9, D9
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = carry out from last word of previous loop iteration
+        // Q10 = running pointer in Src
+        // Q11 = running pointer in Mod
+        // Q12 = loop counter
+        // Q13 = loop counter
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                        // fetch word of Src we want to set to zero
+        mov     Q11, Q2
+        mov     Q10, Q1
+
+        imul    Q6, Q5                          // lower word is same for signed & unsigned multiply
+
+        mov     D12, D4
+        xor     D7, D7
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmInnerloop:
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q6 = multiplier
+        // Q1 = pointer to modulus value
+        // D13 = outer loop counter (words)
+        // D12 = inner loop counter (digits)
+        // Q10  = running ptr to modulus
+        // Q11 = running ptr to input/scratch
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+
+        MULT_DOUBLEADD_128 0, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 2, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 4, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 6, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        lea     Q10,[Q10 + 64]
+        lea     Q11,[Q11 + 64]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduceAsmInnerloop
+
+        add     Q7, Q9
+        mov     D9, 0
+        adc     Q9, 0
+        add     Q7, [Q11]
+        adc     Q9, 0
+        mov     [Q11], Q7
+
+        lea     Q2,[Q2 + 8]
+
+        dec     D13
+        jnz     SymCryptFdefMontgomeryReduceAsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     D12, D4         // loop ctr
+        mov     Q11, Q2         // pSrc
+        mov     Q10, Q1         // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q9,0' resulted in 0, 1, or 2
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmSubLoop:
+        mov     Q0,[Q11]
+        sbb     Q0,[Q10]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q11 + 8]
+        sbb     Q0,[Q10 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q11 + 16]
+        sbb     Q0,[Q10 + 16]
+        mov     [Q7 + 16], Q0
+
+        mov     Q0,[Q11 + 24]
+        sbb     Q0,[Q10 + 24]
+        mov     [Q7 + 24], Q0
+
+        mov     Q0,[Q11 + 32]
+        sbb     Q0,[Q10 + 32]
+        mov     [Q7 + 32], Q0
+
+        mov     Q0,[Q11 + 40]
+        sbb     Q0,[Q10 + 40]
+        mov     [Q7 + 40], Q0
+
+        mov     Q0,[Q11 + 48]
+        sbb     Q0,[Q10 + 48]
+        mov     [Q7 + 48], Q0
+
+        mov     Q0,[Q11 + 56]
+        sbb     Q0,[Q10 + 56]
+        mov     [Q7 + 56], Q0
+
+        lea     Q11,[Q11 + 64]
+        lea     Q10,[Q10 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduceAsmSubLoop
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q9 == 0 && Cy = 1
+        sbb     D9, 0
+
+        movd    xmm0, D9            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduceAsmMaskedCopyLoop:
+        movdqa  xmm2, [Q2]          // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3]          // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3], xmm2
+
+        movdqa  xmm2, [Q2 + 16]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 16]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 16], xmm2
+
+        movdqa  xmm2, [Q2 + 32]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 32]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 32], xmm2
+
+        movdqa  xmm2, [Q2 + 48]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 48]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 48], xmm2
+
+        // Move on to the next digit
+        lea     Q2,[Q2 + 64]
+        lea     Q3,[Q3 + 64]
+
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduceAsmMaskedCopyLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm))
+
+
+// --------------------------------
+// 256 and 384-bit size specific functions
+// --------------------------------
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm), 4, 9)
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        // compute Src1 - Src2 into (Q0, Q5, Q6, Q7), Cy = inverted subtraction borrow
+
+        mov     Q0, [Q2 + 0*8]
+        sub     Q0, [Q3 + 0*8]
+        mov     Q5, [Q2 + 1*8]
+        sbb     Q5, [Q3 + 1*8]
+        mov     Q6, [Q2 + 2*8]
+        sbb     Q6, [Q3 + 2*8]
+        mov     Q7, [Q2 + 3*8]
+        sbb     Q7, [Q3 + 3*8]
+
+        // Q2, Q3: free
+        // Load Mod into (Q2, Q3, Q8, Q1)
+
+        mov     Q2, 0
+        cmovb   Q2, [Q1 + 0*8]
+        mov     Q3, 0
+        cmovb   Q3, [Q1 + 1*8]
+        mov     Q8, 0
+        cmovb   Q8, [Q1 + 2*8]
+        mov     Q1, [Q1 + 3*8]
+        cmovnb  Q1, Q2
+
+        // Add the (masked) modulus
+        add     Q0, Q2
+        adc     Q5, Q3
+        adc     Q6, Q8
+        adc     Q7, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm))
+
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm), 4, 13)
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        // compute Src1 - Src2 into (Q0, Q5, Q6, Q7, Q8, Q9), Cy = inverted subtraction borrow
+
+        mov     Q0, [Q2 + 0*8]
+        sub     Q0, [Q3 + 0*8]
+        mov     Q5, [Q2 + 1*8]
+        sbb     Q5, [Q3 + 1*8]
+        mov     Q6, [Q2 + 2*8]
+        sbb     Q6, [Q3 + 2*8]
+        mov     Q7, [Q2 + 3*8]
+        sbb     Q7, [Q3 + 3*8]
+        mov     Q8, [Q2 + 4*8]
+        sbb     Q8, [Q3 + 4*8]
+        mov     Q9, [Q2 + 5*8]
+        sbb     Q9, [Q3 + 5*8]
+
+        // Q2, Q3: free
+        // Load Mod into (Q2, Q3, Q10, Q11, Q12, Q1)
+
+        mov     Q2,  0
+        cmovb   Q2,  [Q1 + 0*8]
+        mov     Q3,  0
+        cmovb   Q3,  [Q1 + 1*8]
+        mov     Q10, 0
+        cmovb   Q10, [Q1 + 2*8]
+        mov     Q11, 0
+        cmovb   Q11, [Q1 + 3*8]
+        mov     Q12, 0
+        cmovb   Q12, [Q1 + 4*8]
+        mov     Q1,  [Q1 + 5*8]
+        cmovnb  Q1,  Q2
+
+        // Add the (masked) modulus
+        add     Q0, Q2
+        adc     Q5, Q3
+        adc     Q6, Q10
+        adc     Q7, Q11
+        adc     Q8, Q12
+        adc     Q9, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q9
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm))
+
+//=================================================
+// Multiplication
+//
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefMontgomeryReduce256AsmInternal
+#endif
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm), 4, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+        // First we compute the product. The result will be in 8 registers
+        //       Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        mov     Q5, [Q2]
+        xor     Q8, Q8
+        xor     Q9, Q9
+        xor     Q10, Q10
+
+        mov     Q0, [Q3]
+        mul     Q5
+        mov     Q6, Q0
+        mov     Q7, QH
+
+        mov     Q0, [Q3 + 8]
+        mul     Q5
+        add     Q7, Q0
+        adc     Q8, QH
+
+        mov     Q0, [Q3 + 16]
+        mul     Q5
+        add     Q8, Q0
+        adc     Q9, QH
+
+        mov     Q0, [Q3 + 24]
+        mul     Q5
+        add     Q9, Q0
+        adc     Q10, QH
+
+        // Second row
+        mov     Q5, [Q2 + 8]
+        MUL14   Q0, QH, Q5, Q3, Q7, Q8, Q9, Q10, Q13
+        mov     Q11, QH
+
+        // third row
+        mov     Q5, [Q2 + 16]
+        MUL14   Q0, QH, Q5, Q3, Q8, Q9, Q10, Q11, Q13
+        mov     Q12, QH
+
+        // fourth row
+        mov     Q5, [Q2 + 24]
+        MUL14   Q0, QH, Q5, Q3, Q9, Q10, Q11, Q12, Q13
+        mov     Q13, QH
+
+ALTERNATE_ENTRY(SymCryptFdefMontgomeryReduce256AsmInternal)
+        // Invariant:
+        //   common prologue used
+        //   512-bit result in (Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13)
+        //   Q1 = pmMod
+        //   Q4 = pDst
+
+        mov     Q3, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        mov     Q5, Q6
+        imul    Q5, Q3             // lower word is the same for signed & unsigned multiply - Q5 = multiplicand for first row
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q6, Q7, Q8, Q9, Q6
+        mov     Q6, QH            // Save the out carries in (eventually) (Q6, Q7, Q8, Q9)
+
+        mov     Q5, Q7
+        imul    Q5, Q3
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q7, Q8, Q9, Q10, Q7
+        mov     Q7, QH            // Save the out carries in (eventually) (Q6, Q7, Q8, Q9)
+
+        mov     Q5, Q8
+        imul    Q5, Q3
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q8, Q9, Q10, Q11, Q8
+        mov     Q8, QH
+
+        mov     Q5, Q9
+        imul    Q5, Q3
+        MONTGOMERY14 Q0, QH, Q5, Q1, Q9, Q10, Q11, Q12, Q9
+        // mov   Q9, QH
+
+        add     Q10, Q6
+        adc     Q11, Q7
+        adc     Q12, Q8
+        adc     Q13, QH
+
+        sbb     Q5, Q5        // Carry out from final addition in mask form
+
+        // reduced value in (Q10, Q11, Q12, Q13, -Q5), and it is less than 2*Modulus
+
+        mov     Q6, Q10
+        sub     Q6, [Q1]
+        mov     Q7, Q11
+        sbb     Q7, [Q1 + 8]
+        mov     Q8, Q12
+        sbb     Q8, [Q1 + 16]
+        mov     Q9, Q13
+        sbb     Q9, [Q1 + 24]
+
+        sbb     Q1, Q1        // Q1 = carry out mask
+
+        // Choose between the two
+        // addition carry = 1, then subtraction carry = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction carry = 0: pick 2nd result
+        // addition carry = 0 and subtraction carry = 1: pick first result
+
+        xor     Q1, Q5        // 0 = 2nd result, 1 = first result
+
+        cmovz   Q10, Q6
+        cmovz   Q11, Q7
+        cmovz   Q12, Q8
+        cmovz   Q13, Q9
+
+        mov     [Q4 +  0], Q10
+        mov     [Q4 +  8], Q11
+        mov     [Q4 + 16], Q12
+        mov     [Q4 + 24], Q13
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm))
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMontgomeryReduce256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+// Note we specify 4 arguments so that our prolog matches SymCryptFdefModMulMontgomery256Asm
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce256Asm), 4, 14)
+
+        mov     Q4, Q3
+        mov     Q6,  [Q2 +  0]
+        mov     Q7,  [Q2 +  8]
+        mov     Q8,  [Q2 + 16]
+        mov     Q9,  [Q2 + 24]
+        mov     Q10, [Q2 + 32]
+        mov     Q11, [Q2 + 40]
+        mov     Q12, [Q2 + 48]
+        mov     Q13, [Q2 + 56]
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefMontgomeryReduce256AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce256Asm))
+
+#if 0
+//////////////////////////////////////////////////////////////
+//
+// UNUSED ASSEMBLY FROM HERE
+// Kept in case we need it later, but removed at compile time
+// to avoid inclusion in produced binaries (the linker struggles
+// to remove unused assembly functions)
+//
+//////////////////////////////////////////////////////////////
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd256(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm), 4, 11)
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        // compute Src1 + Src2 into (Q0, Q5, Q6, Q7) with carry out mask in Q8
+
+        mov     Q0, [Q2 + 0*8]
+        add     Q0, [Q3 + 0*8]
+        mov     Q5, [Q2 + 1*8]
+        adc     Q5, [Q3 + 1*8]
+        mov     Q6, [Q2 + 2*8]
+        adc     Q6, [Q3 + 2*8]
+        mov     Q7, [Q2 + 3*8]
+        adc     Q7, [Q3 + 3*8]
+        sbb     Q8, Q8                  // Q8 = carry out mask
+
+        // Q2, Q3: free
+        // Compute sum - Mod into (Q2, Q3, Q9, Q10) = sum - modulus, Q1 = carry out mask
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        mov     Q2,  Q0
+        sub     Q2,  [Q1 + 0*8]
+        mov     Q3,  Q5
+        sbb     Q3,  [Q1 + 1*8]
+        mov     Q9,  Q6
+        sbb     Q9,  [Q1 + 2*8]
+        mov     Q10, Q7
+        sbb     Q10, [Q1 + 3*8]
+
+        sbb     Q1, Q1                 // Q1 = carry out mask
+
+        // Choose between the two
+        // addition carry = 1, then subtraction carry = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction carry = 0: pick 2nd result
+        // addition carry = 0 and subtraction carry = 1: pick first result
+
+        xor     Q1, Q8            // 0 = 2nd result, 1 = first result
+
+        cmovz   Q0, Q2
+        cmovz   Q5, Q3
+        cmovz   Q6, Q9
+        cmovz   Q7, Q10
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomery256(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments so that our prolog matches SymCryptFdefModMulMontgomery256Asm
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm), 4, 14)
+
+        //  Result in   Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        // Q1 = pmMod
+        // Q2 = peSrc
+        // Q3 = peDst
+
+        mov     Q4, Q3
+        mov     Q5, [Q2]
+        xor     Q9, Q9
+        xor     Q10, Q10
+        xor     Q11, Q11
+        xor     Q12, Q12
+
+        // First we compute all the terms that need doubling
+
+        mov     Q0, [Q2 + 8]
+        mul     Q5
+        mov     Q7, Q0
+        mov     Q8, QH
+
+        mov     Q0, [Q2 + 16]
+        mul     Q5
+        add     Q8, Q0
+        adc     Q9, QH
+
+        mov     Q0, [Q2 + 24]
+        mul     Q5
+        add     Q9, Q0
+        adc     Q10, QH
+
+        mov     Q5, [Q2 + 8]
+        mov     Q0, [Q2 + 16]
+        mul     Q5
+        add     Q9, Q0
+        adc     QH, 0
+        mov     Q13, QH
+
+        mov     Q0, [Q2 + 24]
+        mul     Q5
+        add     Q10, Q0
+        adc     QH, 0
+        add     Q10, Q13
+        adc     Q11, QH
+
+        mov     Q5, [Q2 + 16]
+        mov     Q0, [Q2 + 24]
+        mul     Q5
+        add     Q11, Q0
+        adc     Q12, QH        // no overflow from this
+
+        // double these terms
+        xor     Q13, Q13
+
+        add     Q7, Q7
+        adc     Q8, Q8
+        adc     Q9, Q9
+        adc     Q10, Q10
+        adc     Q11, Q11
+        adc     Q12, Q12
+        adc     Q13, 0
+
+        mov     Q0, [Q2]
+        mul     Q0
+        mov     Q6, Q0
+        mov     Q5, QH
+
+        mov     Q0, [Q2 + 8]
+        mul     Q0
+
+        add     Q7, Q5
+        adc     Q8, Q0
+        adc     Q9, QH
+        sbb     Q3, Q3          // -carry
+
+        mov     Q0, [Q2 + 16]
+        mul     Q0
+
+        add     Q3, Q3
+        adc     Q10, Q0
+        adc     Q11, QH
+        sbb     Q3, Q3
+
+        mov     Q0, [Q2 + 24]
+        mul     Q0
+        add     Q3, Q3
+        adc     Q12, Q0
+        adc     Q13, QH
+
+        // See SymCryptFdefMontgomeryReduce256Asm for a discussion of this strange epilogue sequence
+        test    rsp,rsp
+        jne     SymCryptFdefMontgomeryReduce256AsmInternal       // jumps always
+
+        int     3
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm))
+
+// --------------------------------
+// 512-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul512Asm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nWords)                              PUINT32     pDst )
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawMul512Asm), 4, 8)
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // Q2 = pSrc2 (constant)
+        // Q3 = # words left from Src1 to process
+        // Q4 = pDst (incremented in outer loop)
+        // Q5 = word from Src1 to multiply with
+        // Q6 = carry for even words (64 bits)
+        // Q7 = carry for odd words (64 bits)
+
+        shl     Q3, 3               // nDigits * 8 = # words in Src1 to process
+
+        mov     Q5, [Q1]            // mulword
+        xor     Q6, Q6              // carry
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+        MULT_SINGLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 64], Q6       // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+
+ALIGN(16)
+
+SymCryptFdefRawMul512AsmLoopOuter:
+
+        lea     Q1, [Q1 + 8]        // move to next word of pSrc1
+        lea     Q4, [Q4 + 8]        // move Dst pointer one word over
+
+        mov     Q5, [Q1]            // mulword
+        xor     Q6, Q6              // carry
+
+        MULT_DOUBLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 64], Q6       // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+        jnz     SymCryptFdefRawMul512AsmLoopOuter
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawMul512Asm))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                       UINT32      nDigits,
+//   _Out_writes_(2*nWords)                              PUINT32     pDst )
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare512Asm), 3, 11)
+
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = outer loop pointer into pSrc
+        // Q2 = nDigits (constant)
+        // Q3 = pDst (constant)
+        // Q4 = inner loop pointer into pSrc
+        // Q5 = inner loop pointer into pDst
+        // Q6 = word from Src to multiply with
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = outer loop pointer into pDst
+        // Q10 = outer loop counter of #words left
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q1   // save pSrc
+
+        ////////////////////////////////////////////////////////////////
+        // First Pass - Addition of the cross products x_i*x_j with i!=j
+        ////////////////////////////////////////////////////////////////
+
+        mov     Q9, Q3              // Q9 = outer pDst
+
+        mov     Q4, Q1              // Q4 = inner pSrc
+        mov     Q5, Q3              // Q5 = inner pDst
+
+        // Initial inner loop overwrites Dst, which avoids adding the current Dst value
+        // 7 iterations
+        xor     Q8, Q8              // carry = 0 (for "odd" iterations set only the Q8 carry)
+        mov     Q6, [Q1]            // mulword
+        mov     [Q5], Q8            // Write 0 in the first word
+
+        SQR_SINGLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 4, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 5, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 6, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 7, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        mov     [Q5 + 8*8], Q7      // write last word, cannot overflow because Dst is at least 2 digits long
+        add     Q9, 8               // Skip over the first word
+
+        // 6 iterations
+        xor     Q7, Q7              // carry = 0 (for "even" iterations set only the Q7 carry)
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 6*8], Q7
+
+        // 5 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7  // Notice the dst_carry is Q7 since all the "double" macros have Q7 as src_carry
+        SQR_DOUBLEADD_64_4 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 5*8], Q7
+
+        // 4 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_4 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 4*8], Q7
+
+        // 3 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 3*8], Q7
+
+        // 2 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 2*8], Q7
+
+        // 1 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        mov     [Q5 + 8], Q7
+
+        xor     QH, QH
+        mov     [Q5 + 16], QH      // Final word = 0
+
+
+        ////////////////////////////////////////////////////////////////
+        // Second Pass - Shifting all results 1 bit left
+        ////////////////////////////////////////////////////////////////
+
+        xor     Q0, Q0              // carry flag = 0
+        mov     Q5, Q3              // pDst pointer
+
+        SQR_SHIFT_LEFT 0, Q0, Q5
+        SQR_SHIFT_LEFT 1, Q0, Q5
+        SQR_SHIFT_LEFT 2, Q0, Q5
+        SQR_SHIFT_LEFT 3, Q0, Q5
+
+        SQR_SHIFT_LEFT 4, Q0, Q5
+        SQR_SHIFT_LEFT 5, Q0, Q5
+        SQR_SHIFT_LEFT 6, Q0, Q5
+        SQR_SHIFT_LEFT 7, Q0, Q5
+
+        SQR_SHIFT_LEFT 8, Q0, Q5
+        SQR_SHIFT_LEFT 9, Q0, Q5
+        SQR_SHIFT_LEFT 10, Q0, Q5
+        SQR_SHIFT_LEFT 11, Q0, Q5
+
+        SQR_SHIFT_LEFT 12, Q0, Q5
+        SQR_SHIFT_LEFT 13, Q0, Q5
+        SQR_SHIFT_LEFT 14, Q0, Q5
+        SQR_SHIFT_LEFT 15, Q0, Q5
+
+        //////////////////////////////////////////////////////////////////////////////
+        // Third Pass - Adding the squares on the even columns and propagating the sum
+        //////////////////////////////////////////////////////////////////////////////
+
+        mov     Q1, [rsp + GET_MEMSLOT_OFFSET(slot0)]  // Q1 = pSrc
+        xor     Q7, Q7
+
+        SQR_DIAGONAL_PROP 0, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 1, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 2, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 3, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 4, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 5, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 6, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 7, Q1, Q3, Q0, QH, Q7
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare512Asm))
+
+//VOID
+//SymCryptFdefMontgomeryReduce512Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce512Asm), 3, 13)
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]            // nDigits
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]              // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]              // modulus value
+
+        mov     D12, D4         // outer loop counter
+        shl     D12, 3          // D12 is in words
+
+        xor     D9, D9
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = carry out from last word of previous loop iteration
+        // Q10 = running pointer in Src
+        // Q11 = running pointer in Mod
+        // D12 = loop counter
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce512AsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                        // fetch word of Src we want to set to zero
+        mov     Q11, Q2
+        mov     Q10, Q1
+
+        imul    Q6, Q5                          // lower word is same for signed & unsigned multiply
+
+        xor     D7, D7
+
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q1 = pointer to modulus value
+        // Q6 = multiplier
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q10 = running ptr to modulus
+        // Q11 = running ptr to input/scratch
+        // D12 = outer loop counter (words)
+
+        MULT_DOUBLEADD_128 0, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 2, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 4, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 6, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        lea     Q11,[Q11 + 64]
+
+        add     Q7, Q9
+        mov     D9, 0
+        adc     Q9, 0
+        add     Q7, [Q11]
+        adc     Q9, 0
+        mov     [Q11], Q7
+
+        lea     Q2,[Q2 + 8]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduce512AsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     Q11, Q2         // pSrc
+        mov     Q10, Q1         // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q9,0' resulted in 0, 1, or 2
+        mov     Q0,[Q11]
+        sbb     Q0,[Q10]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q11 + 8]
+        sbb     Q0,[Q10 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q11 + 16]
+        sbb     Q0,[Q10 + 16]
+        mov     [Q7 + 16], Q0
+
+        mov     Q0,[Q11 + 24]
+        sbb     Q0,[Q10 + 24]
+        mov     [Q7 + 24], Q0
+
+        mov     Q0,[Q11 + 32]
+        sbb     Q0,[Q10 + 32]
+        mov     [Q7 + 32], Q0
+
+        mov     Q0,[Q11 + 40]
+        sbb     Q0,[Q10 + 40]
+        mov     [Q7 + 40], Q0
+
+        mov     Q0,[Q11 + 48]
+        sbb     Q0,[Q10 + 48]
+        mov     [Q7 + 48], Q0
+
+        mov     Q0,[Q11 + 56]
+        sbb     Q0,[Q10 + 56]
+        mov     [Q7 + 56], Q0
+
+        lea     Q11,[Q11 + 64]
+        lea     Q10,[Q10 + 64]
+        lea     Q7,[Q7 + 64]
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q9 == 0 && Cy = 1
+        sbb     D9, 0
+
+        movd    xmm0, D9            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce512AsmMaskedCopyLoop:
+        movdqa  xmm2, [Q2]          // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3]          // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3], xmm2
+
+        movdqa  xmm2, [Q2 + 16]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 16]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 16], xmm2
+
+        movdqa  xmm2, [Q2 + 32]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 32]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 32], xmm2
+
+        movdqa  xmm2, [Q2 + 48]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 48]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 48], xmm2
+
+        // Move on to the next digit
+        lea     Q2,[Q2 + 64]
+        lea     Q3,[Q3 + 64]
+
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduce512AsmMaskedCopyLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce512Asm))
+
+// --------------------------------
+// 1024-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul1024Asm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nWords)                              PUINT32     pDst )
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawMul1024Asm), 4, 8)
+
+        // Basic structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = pSrc1  (updated in outer loop)
+        // Q2 = pSrc2 (constant)
+        // Q3 = # words left from Src1 to process
+        // Q4 = pDst (incremented in outer loop)
+        // Q5 = word from Src1 to multiply with
+        // Q6 = carry for even words (64 bits)
+        // Q7 = carry for odd words (64 bits)
+
+        shl     Q3, 3               // nDigits * 8 = # words in Src1 to process
+
+        mov     Q5, [Q1]          // mulword
+        xor     Q6, Q6              // carry
+
+        // First inner loop overwrites Dst, which avoids adding the current Dst value
+        MULT_SINGLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        MULT_SINGLEADD_128 8, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 10, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 12, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_SINGLEADD_128 14, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 128], Q6          // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+
+ALIGN(16)
+
+SymCryptFdefRawMul1024AsmLoopOuter:
+
+        lea     Q1, [Q1 + 8]        // move to next word of pSrc1
+        lea     Q4, [Q4 + 8]        // move Dst pointer one word over
+
+        mov     Q5, [Q1]            // mulword
+
+        xor     Q6, Q6              // carry
+
+        MULT_DOUBLEADD_128 0, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 2, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 4, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 6, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        MULT_DOUBLEADD_128 8, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 10, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 12, Q2, Q4, Q0, QH, Q5, Q6, Q7
+        MULT_DOUBLEADD_128 14, Q2, Q4, Q0, QH, Q5, Q6, Q7
+
+        mov     [Q4 + 128], Q6    // write last word, cannot overflow because Dst is at least 2 digits long
+
+        dec     Q3
+        jnz     SymCryptFdefRawMul1024AsmLoopOuter
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawMul1024Asm))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                       UINT32      nDigits,
+//   _Out_writes_(2*nWords)                              PUINT32     pDst )
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare1024Asm), 3, 11)
+
+        // Register assignments
+        //
+        // Q0 = tmp for mul
+        // QH = tmp for mul
+        // Q1 = outer loop pointer into pSrc
+        // Q2 = nDigits (constant)
+        // Q3 = pDst (constant)
+        // Q4 = inner loop pointer into pSrc
+        // Q5 = inner loop pointer into pDst
+        // Q6 = word from Src to multiply with
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = outer loop pointer into pDst
+        // Q10 = outer loop counter of #words left
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q1   // save pSrc
+
+        ////////////////////////////////////////////////////////////////
+        // First Pass - Addition of the cross products x_i*x_j with i!=j
+        ////////////////////////////////////////////////////////////////
+
+        mov     Q9, Q3              // Q9 = outer pDst
+
+        mov     Q4, Q1              // Q4 = inner pSrc
+        mov     Q5, Q3              // Q5 = inner pDst
+
+        // Initial inner loop overwrites Dst, which avoids adding the current Dst value
+
+        // 15 iterations
+        xor     Q8, Q8              // carry = 0 (for "odd" iterations set only the Q8 carry)
+        mov     Q6, [Q1]            // mulword
+        mov     [Q5], Q8            // Write 0 in the first word
+
+        SQR_SINGLEADD_64 1, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 3, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 4, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 5, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 6, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 7, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 8, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 9, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 10, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 11, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        SQR_SINGLEADD_64 12, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 13, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_SINGLEADD_64 14, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_SINGLEADD_64 15, Q4, Q5, Q0, QH, Q6, Q8, Q7
+
+        mov     [Q5 + 16*8], Q7     // write last word, cannot overflow because Dst is at least 2 digits long
+        add     Q9, 8               // Skip over the first word
+
+        // 14 iterations (adding the current Dst value)
+        xor     Q7, Q7            // carry = 0 (for "even" iterations set only the Q7 carry)
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 6, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 14*8], Q7
+
+        // 13 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7  // Notice the dst_carry is Q7 since all the "double" macros have Q7 as src_carry
+        SQR_DOUBLEADD_64_4 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 5, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 13*8], Q7
+
+        // 12 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_4 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 4, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 12*8], Q7
+
+        // 11 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 3, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 11*8], Q7
+
+        // 10 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_8 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 10*8], Q7
+
+        // 9 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_8 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 9*8], Q7
+
+        // 8 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_8 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 8*8], Q7
+
+        // 7 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 3, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 7*8], Q7
+
+        // 6 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        SQR_DOUBLEADD_64_4 2, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 6*8], Q7
+
+        // 5 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_4 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 5*8], Q7
+
+        // 4 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_4 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 4*8], Q7
+
+        // 3 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        SQR_DOUBLEADD_64_2 1, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 3*8], Q7
+
+        // 2 iterations
+        xor     Q7, Q7
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64_2 0, Q4, Q5, Q0, QH, Q6, Q7, Q8
+        mov     [Q5 + 2*8], Q7
+
+        // 1 iterations
+        xor     Q8, Q8
+        SQR_SIZE_SPECIFIC_INIT Q1, Q9, Q4, Q5, Q6
+        SQR_DOUBLEADD_64 0, Q4, Q5, Q0, QH, Q6, Q8, Q7
+        mov     [Q5 + 8], Q7
+
+        xor     QH, QH
+        mov     [Q5 + 16], QH       // Final word = 0
+
+
+        ////////////////////////////////////////////////////////////////
+        // Second Pass - Shifting all results 1 bit left
+        ////////////////////////////////////////////////////////////////
+
+        xor     Q0, Q0              // carry flag = 0
+        mov     Q5, Q3              // pDst pointer
+
+        SQR_SHIFT_LEFT 0, Q0, Q5
+        SQR_SHIFT_LEFT 1, Q0, Q5
+        SQR_SHIFT_LEFT 2, Q0, Q5
+        SQR_SHIFT_LEFT 3, Q0, Q5
+
+        SQR_SHIFT_LEFT 4, Q0, Q5
+        SQR_SHIFT_LEFT 5, Q0, Q5
+        SQR_SHIFT_LEFT 6, Q0, Q5
+        SQR_SHIFT_LEFT 7, Q0, Q5
+
+        SQR_SHIFT_LEFT 8, Q0, Q5
+        SQR_SHIFT_LEFT 9, Q0, Q5
+        SQR_SHIFT_LEFT 10, Q0, Q5
+        SQR_SHIFT_LEFT 11, Q0, Q5
+
+        SQR_SHIFT_LEFT 12, Q0, Q5
+        SQR_SHIFT_LEFT 13, Q0, Q5
+        SQR_SHIFT_LEFT 14, Q0, Q5
+        SQR_SHIFT_LEFT 15, Q0, Q5
+
+        SQR_SHIFT_LEFT 16, Q0, Q5
+        SQR_SHIFT_LEFT 17, Q0, Q5
+        SQR_SHIFT_LEFT 18, Q0, Q5
+        SQR_SHIFT_LEFT 19, Q0, Q5
+
+        SQR_SHIFT_LEFT 20, Q0, Q5
+        SQR_SHIFT_LEFT 21, Q0, Q5
+        SQR_SHIFT_LEFT 22, Q0, Q5
+        SQR_SHIFT_LEFT 23, Q0, Q5
+
+        SQR_SHIFT_LEFT 24, Q0, Q5
+        SQR_SHIFT_LEFT 25, Q0, Q5
+        SQR_SHIFT_LEFT 26, Q0, Q5
+        SQR_SHIFT_LEFT 27, Q0, Q5
+
+        SQR_SHIFT_LEFT 28, Q0, Q5
+        SQR_SHIFT_LEFT 29, Q0, Q5
+        SQR_SHIFT_LEFT 30, Q0, Q5
+        SQR_SHIFT_LEFT 31, Q0, Q5
+
+        //////////////////////////////////////////////////////////////////////////////
+        // Third Pass - Adding the squares on the even columns and propagating the sum
+        //////////////////////////////////////////////////////////////////////////////
+
+        mov     Q1, [rsp + GET_MEMSLOT_OFFSET(slot0)]  // Q1 = pSrc
+        xor     Q7, Q7
+
+        SQR_DIAGONAL_PROP 0, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 1, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 2, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 3, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 4, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 5, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 6, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 7, Q1, Q3, Q0, QH, Q7
+
+        SQR_DIAGONAL_PROP 8, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 9, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 10, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 11, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 12, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 13, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 14, Q1, Q3, Q0, QH, Q7
+        SQR_DIAGONAL_PROP 15, Q1, Q3, Q0, QH, Q7
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquare1024Asm))
+
+//VOID
+//SymCryptFdefMontgomeryReduce1024Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce1024Asm), 3, 13)
+
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]    // nDigits
+        mov     Q5, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        mov     D12, D4         // outer loop counter
+        shl     D12, 3          // D12 is in words
+
+        xor     D9, D9
+
+        // General register allocations
+        // Q0 = multiply result
+        // QH = multiply result
+        // Q1 = pointer to modulus value
+        // Q2 = pSrc (updated in outer loop)
+        // Q3 = pDst
+        // D4 = nDigits
+        // Q5 = pmMod->inv64
+        // Q6 = multiplier in inner loop
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q9 = carry out from last word of previous loop iteration
+        // Q10 = running pointer in Src
+        // Q11 = running pointer in Mod
+        // D12 = loop counter
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce1024AsmOuterLoop:
+
+        // start decoder with a few simple instructions, including at least one that requires
+        // a uop execution and is on the critical path
+
+        mov     Q6, [Q2]                      // fetch word of Src we want to set to zero
+        mov     Q11, Q2
+        mov     Q10, Q1
+
+        imul    Q6, Q5                        // lower word is same for signed & unsigned multiply
+
+        xor     D7, D7
+
+        // Q0 = mul scratch
+        // QH = mul scratch
+        // Q1 = pointer to modulus value
+        // Q6 = multiplier
+        // Q7 = carry for even words (64 bits)
+        // Q8 = carry for odd words (64 bits)
+        // Q10  = running ptr to modulus
+        // Q11 = running ptr to input/scratch
+        // D12 = outer loop counter (words)
+
+        MULT_DOUBLEADD_128 0, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 2, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 4, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 6, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        MULT_DOUBLEADD_128 8, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 10, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 12, Q10, Q11, Q0, QH, Q6, Q7, Q8
+        MULT_DOUBLEADD_128 14, Q10, Q11, Q0, QH, Q6, Q7, Q8
+
+        lea     Q11,[Q11 + 128]
+
+        add     Q7, Q9
+        mov     D9, 0
+        adc     Q9, 0
+        add     Q7, [Q11]
+        adc     Q9, 0
+        mov     [Q11], Q7
+
+        lea     Q2,[Q2 + 8]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduce1024AsmOuterLoop
+
+        //
+        // Most of the work is done - now all that is left is subtract the modulus if it is smaller than the result
+        //
+
+        // First we compute the pSrc result minus the modulus into the destination
+        mov     D12, D4         // loop ctr
+        mov     Q11, Q2         // pSrc
+        mov     Q10, Q1         // pMod
+        mov     Q7, Q3          // pDst
+
+        // Cy = 0 because the last 'adc Q9,0' resulted in 0, 1, or 2
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce1024AsmSubLoop:
+        mov     Q0,[Q11]
+        sbb     Q0,[Q10]
+        mov     [Q7], Q0
+
+        mov     Q0,[Q11 + 8]
+        sbb     Q0,[Q10 + 8]
+        mov     [Q7 + 8], Q0
+
+        mov     Q0,[Q11 + 16]
+        sbb     Q0,[Q10 + 16]
+        mov     [Q7 + 16], Q0
+
+        mov     Q0,[Q11 + 24]
+        sbb     Q0,[Q10 + 24]
+        mov     [Q7 + 24], Q0
+
+        mov     Q0,[Q11 + 32]
+        sbb     Q0,[Q10 + 32]
+        mov     [Q7 + 32], Q0
+
+        mov     Q0,[Q11 + 40]
+        sbb     Q0,[Q10 + 40]
+        mov     [Q7 + 40], Q0
+
+        mov     Q0,[Q11 + 48]
+        sbb     Q0,[Q10 + 48]
+        mov     [Q7 + 48], Q0
+
+        mov     Q0,[Q11 + 56]
+        sbb     Q0,[Q10 + 56]
+        mov     [Q7 + 56], Q0
+
+        lea     Q11,[Q11 + 64]
+        lea     Q10,[Q10 + 64]
+        lea     Q7,[Q7 + 64]
+
+        dec     D12
+        jnz     SymCryptFdefMontgomeryReduce1024AsmSubLoop
+
+        // Finally a masked copy from pSrc to pDst
+        // copy if: Q9 == 0 && Cy = 1
+        sbb     D9, 0
+
+        movd    xmm0, D9            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+ALIGN(16)
+
+SymCryptFdefMontgomeryReduce1024AsmMaskedCopyLoop:
+        movdqa  xmm2, [Q2]          // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3]          // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3], xmm2
+
+        movdqa  xmm2, [Q2 + 16]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 16]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 16], xmm2
+
+        movdqa  xmm2, [Q2 + 32]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 32]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 32], xmm2
+
+        movdqa  xmm2, [Q2 + 48]     // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 48]     // xmm3 = pDst[0]
+        pand    xmm2, xmm0          //
+        pand    xmm3, xmm1          //
+        por     xmm2, xmm3
+        movdqa  [Q3 + 48], xmm2
+
+        // Move on to the next digit
+        lea     Q2,[Q2 + 64]
+        lea     Q3,[Q3 + 64]
+
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduce1024AsmMaskedCopyLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduce1024Asm))
+#endif
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/fdef_mulx.S b/libs/symcrypt/lib/amd64/fdef_mulx.S
new file mode 100644
index 000000000000..9da98d5931f3
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/fdef_mulx.S
@@ -0,0 +1,5047 @@
+//
+//  fdef_mulx.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  using the bmi2 instructions mulx, adcx and adox
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+.macro ZEROREG  R
+        xor     \R,\R
+.endm
+
+.macro ZEROREG_8  R0, R1, R2, R3, R4, R5, R6, R7
+    ZEROREG \R0
+    ZEROREG \R1
+    ZEROREG \R2
+    ZEROREG \R3
+    ZEROREG \R4
+    ZEROREG \R5
+    ZEROREG \R6
+    ZEROREG \R7
+.endm
+
+.macro MULADD18  R0, R1, R2, R3, R4, R5, R6, R7, pD, pA, pB, T0, T1, QH
+    // \R0:R[7:1]:D[0] = A[7:0] * B[0] + D[0] + R[7:0]
+    // Pre: Cy = Ov = 0
+    // Post: Cy = Ov = 0
+
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mov     \QH, [\pB]
+    adox    \R0, [\pD]
+
+    mulx    \T1, \T0, [\pA + 0 * 8]
+    adcx    \R0, \T0
+    adox    \R1, \T1
+
+    mov     [\pD], \R0
+
+    mulx    \T1, \T0, [\pA + 1 * 8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1, \T0, [\pA + 2 * 8]
+    adcx    \R2, \T0
+    adox    \R3, \T1
+
+    mulx    \T1, \T0, [\pA + 3 * 8]
+    adcx    \R3, \T0
+    adox    \R4, \T1
+
+    mulx    \T1, \T0, [\pA + 4 * 8]
+    adcx    \R4, \T0
+    adox    \R5, \T1
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R5, \T0
+    adox    \R6, \T1
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R6, \T0
+    adox    \R7, \T1
+
+    mulx    \T1, \T0, [\pA + 7 * 8]
+    adcx    \R7, \T0
+
+    mov     \R0, 0
+    adox    \R0, \R0
+
+    adcx    \R0, \T1
+.endm
+
+.macro MULADD88  R0, R1, R2, R3, R4, R5, R6, R7, pD, pA, pB, T0, T1, QH
+    // pre & post: Cy = Ov = 0
+    // R[7-0]:D[7-0] = A[7:0] * B[7:0] + R[7:0] + D[7:0]
+
+    MULADD18    \R0, \R1, \R2, \R3, \R4, \R5, \R6, \R7, \pD     , \pA, \pB     , \T0, \T1, \QH
+    MULADD18    \R1, \R2, \R3, \R4, \R5, \R6, \R7, \R0, \pD +  8, \pA, \pB +  8, \T0, \T1, \QH
+    MULADD18    \R2, \R3, \R4, \R5, \R6, \R7, \R0, \R1, \pD + 16, \pA, \pB + 16, \T0, \T1, \QH
+    MULADD18    \R3, \R4, \R5, \R6, \R7, \R0, \R1, \R2, \pD + 24, \pA, \pB + 24, \T0, \T1, \QH
+    MULADD18    \R4, \R5, \R6, \R7, \R0, \R1, \R2, \R3, \pD + 32, \pA, \pB + 32, \T0, \T1, \QH
+    MULADD18    \R5, \R6, \R7, \R0, \R1, \R2, \R3, \R4, \pD + 40, \pA, \pB + 40, \T0, \T1, \QH
+    MULADD18    \R6, \R7, \R0, \R1, \R2, \R3, \R4, \R5, \pD + 48, \pA, \pB + 48, \T0, \T1, \QH
+    MULADD18    \R7, \R0, \R1, \R2, \R3, \R4, \R5, \R6, \pD + 56, \pA, \pB + 56, \T0, \T1, \QH
+.endm
+
+
+.macro HALF_SQUARE_NODIAG8  R0, R1, R2, R3, R4, R5, R6, R7, pD, pA, T0, T1, QH
+    // pre & post: Cy = Ov = 0
+    // R[7-0]:D[7-0] = D[7:0] + (A[0:7]^2 - \sum_{i=0}^7 (A[i] * 2^{64*i}) )/2
+    // This is the component of the square that needs to be doubled, and then the diagonals added
+
+    // Note that Dst[0] is not changed by this macro
+
+    mov     \QH, [\pA + 0 * 8]            // \QH = A0
+    mov     \R1, [\pD + 1 * 8]
+    mov     \R2, [\pD + 2 * 8]
+    mov     \R3, [\pD + 3 * 8]
+    mov     \R4, [\pD + 4 * 8]
+    mov     \R5, [\pD + 5 * 8]
+    mov     \R6, [\pD + 6 * 8]
+    mov     \R7, [\pD + 7 * 8]
+    xor     \R0, \R0
+
+    mulx    \T1, \T0, [\pA + 1 * 8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1, \T0, [\pA + 2 * 8]
+    adcx    \R2, \T0
+    adox    \R3, \T1
+
+    mulx    \T1, \T0, [\pA + 3 * 8]
+    adcx    \R3, \T0
+    adox    \R4, \T1
+
+    mulx    \T1, \T0, [\pA + 4 * 8]
+    adcx    \R4, \T0
+    adox    \R5, \T1
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R5, \T0
+    adox    \R6, \T1
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R6, \T0
+    adox    \R7, \T1
+
+    mulx    \T1, \T0, [\pA + 7 * 8]
+    adcx    \R7, \T0
+    mov     [\pD + 1 * 8], \R1
+
+    adox    \R0, \R0
+    adcx    \R0, \T1
+    mov     [\pD + 2 * 8], \R2
+    mov     \QH, [\pA + 1 * 8]        // \QH = A1
+
+    //=======
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pA + 2 * 8]
+    adcx    \R3, \T0
+    adox    \R4, \T1
+
+    mulx    \T1, \T0, [\pA + 3 * 8]
+    adcx    \R4, \T0
+    adox    \R5, \T1
+
+    mulx    \T1, \T0, [\pA + 4 * 8]
+    adcx    \R5, \T0
+    adox    \R6, \T1
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R6, \T0
+    adox    \R7, \T1
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R7, \T0
+    adox    \R0, \T1
+
+    mov     \QH, [\pA + 7 * 8]        // \QH = A7
+    mov     \R1, 0
+    mov     \R2, 0
+    mov     [\pD + 3 * 8], \R3
+
+    mulx    \T1, \T0, [\pA + 1 * 8]
+    adcx    \R0, \T0
+    adox    \R1, \T1                  // doesn't produce Ov as \T1 <= 0xff..fe and \R1=0
+
+    mulx    \T1, \T0, [\pA + 2 * 8]
+    adcx    \R1, \T0
+    mov     [\pD + 4 * 8], \R4
+
+    adcx    \R2, \T1
+    mov     \QH, [\pA + 2 * 8]        // \QH = A2
+
+    //======
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pA + 3 * 8]
+    adcx    \R5, \T0
+    adox    \R6, \T1
+
+    mulx    \T1, \T0, [\pA + 4 * 8]
+    adcx    \R6, \T0
+    adox    \R7, \T1
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R7, \T0
+    adox    \R0, \T1
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R0, \T0
+    adox    \R1, \T1
+
+    mov     \QH, [\pA + 4 * 8]        // \QH = A4
+    mov     \R3, 0
+    mov     \R4, 0
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1,\T0, [\pA + 6 * 8]
+    adcx    \R2, \T0
+    adox    \R3, \T1                  // doesn't produce Ov as \T1 <= 0xff..fe and \R3=0
+
+    mov     \QH, [\pA + 5 * 8]        // \QH = A5
+    mov     [\pD + 5 * 8], \R5
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R3, \T0
+    adcx    \R4, \T1
+
+    mov     \QH, [\pA + 3 * 8]        // \QH = A3
+    mov     [\pD + 6 * 8], \R6
+
+    //======
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pA + 4 * 8]
+    adcx    \R7, \T0
+    adox    \R0, \T1
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R0, \T0
+    adox    \R1, \T1
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1, \T0, [\pA + 7 * 8]
+    adcx    \R2, \T0
+    adox    \R3, \T1
+
+    mov     \QH, [\pA + 7 * 8]        // \QH = A7
+    mov     \R5, 0
+    mov     \R6, 0
+    mov     [\pD + 7 * 8], \R7
+
+    mulx    \T1, \T0, [\pA + 4 * 8]
+    adcx    \R3, \T0
+    adox    \R4, \T1
+
+    mulx    \T1, \T0, [\pA + 5 * 8]
+    adcx    \R4, \T0
+    adox    \R5, \T1                  // doesn't produce Ov as \T1 <= 0xff..fe and \R5=0
+
+    mulx    \T1, \T0, [\pA + 6 * 8]
+    adcx    \R5, \T0
+    adcx    \R6, \T1
+
+    xor     \R7, \R7
+.endm
+
+.macro MONTGOMERY18  R0, R1, R2, R3, R4, R5, R6, R7, modInv, pMod, pMont, T0, T1, QH
+    // Mont[0] = (modinv * \R0 mod 2^64)
+    // \R0:R[7:1]:<phantom> = Mont[0] * Mod[7:0] + R[7:0]
+    // Pre: -
+    // Post: -
+
+    mov     \QH, \R0
+    imul    \QH, \modInv
+
+    // Rather than add the low half of the first mulx to \R0 we can go ahead and set
+    // up the Cy flag appropriately based on \R0 directly (the addition will always
+    // result in 0 by construction), so we can have the result while imul is running
+
+    // This has a small but measurable perf improvement on SKLX (~2% improvement for
+    // 512b modmul)
+    // and it seems unlikely that it can make the performance worse
+    // My best guess as to why is that allowing this to execute a few cycles early
+    // can reduce port contention when the macro is being speculatively executed
+    or      \T0, -1          // Clear Cy and Ov
+    adcx    \R0, \T0          // Set Cy when \R0 is non-zero
+    mov     \R0, 0
+    mov     [\pMont], \QH
+
+    mulx    \T1, \T1, [\pMod + 0 * 8]
+    adox    \R1, \T1
+
+    mulx    \T1, \T0, [\pMod + 1 * 8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1, \T0, [\pMod + 2 * 8]
+    adcx    \R2, \T0
+    adox    \R3, \T1
+
+    mulx    \T1, \T0, [\pMod + 3 * 8]
+    adcx    \R3, \T0
+    adox    \R4, \T1
+
+    mulx    \T1, \T0, [\pMod + 4 * 8]
+    adcx    \R4, \T0
+    adox    \R5, \T1
+
+    mulx    \T1, \T0, [\pMod + 5 * 8]
+    adcx    \R5, \T0
+    adox    \R6, \T1
+
+    mulx    \T1, \T0, [\pMod + 6 * 8]
+    adcx    \R6, \T0
+    adox    \R7, \T1
+
+    mulx    \T1, \T0, [\pMod + 7 * 8]
+    adcx    \R7, \T0
+
+    adcx    \R0, \R0
+    adox    \R0, \T1
+.endm
+
+.macro SYMCRYPT_SQUARE_DIAG  index, src_reg, dest_reg, T0, T1, T2, T3, QH
+    mov     \QH, [\src_reg + 8 * \index]
+    mov     \T0, [\dest_reg + 16 * \index]
+    mov     \T1, [\dest_reg + 16 * \index + 8]
+    mulx    \T3, \T2, \QH
+    adcx    \T2, \T0
+    adox    \T2, \T0
+    adcx    \T3, \T1
+    adox    \T3, \T1
+    mov     [\dest_reg + 16 * \index], \T2
+    mov     [\dest_reg + 16 * \index + 8], \T3
+.endm
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawMulMulx(
+//     _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                     UINT32      nDigits1,
+//     _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                     UINT32      nDigits2,
+//     _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+mov Q5, [rsp + 104]
+
+        shl     Q4, 6
+        mov     [rsp + 72 /*MEMSLOT0*/], Q4
+        mov     [rsp + 80 /*MEMSLOT1*/], D2
+
+        // First we wipe nDigits2 of the result (size of in)
+        mov     Q6, Q5
+
+        // Wipe destination for nDigit2 blocks
+        xorps   xmm0,xmm0               // Zero register for 16-byte wipes
+        mov     Q0, Q4
+
+SymCryptFdefRawMulMulxWipeLoop:
+        movaps      [Q6],xmm0
+        movaps      [Q6+16],xmm0            // Wipe 32 bytes
+        movaps      [Q6+32],xmm0            // Wipe 32 bytes
+        movaps      [Q6+48],xmm0            // Wipe 32 bytes
+        add         Q6, 64
+        sub         Q0, 64
+        jnz         SymCryptFdefRawMulMulxWipeLoop
+
+
+SymCryptFdefRawMulxOuterLoop:
+
+        ZEROREG_8   Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13      // Leaves Cy = Ov = 0
+
+SymCryptFdefRawMulMulxInnerLoop:
+
+        // Register allocation in loops:
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q2                                    temps for multiplication
+        // Q1, Q3                                    pSrc1, pSrc2 running pointers
+        // Q4                                        inner loop counter
+        // QH                                        fixed input reg for multiplication
+        // Q5                                        Destination running pointer inner loop
+        // slot0                                     nDigits2*64
+        // slot1                                     outer loop counter
+
+        MULADD88  Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q5, Q1, Q3, Q0, Q2, QH
+
+        add     Q3, 64              // Src2 ptr
+        add     Q5, 64
+
+        sub     D4, 64                            // sets Cy = Ov = 0 because 64*nDigits2 < 2^32
+        jnz     SymCryptFdefRawMulMulxInnerLoop
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q5 + 0*8], Q6
+        mov     [Q5 + 1*8], Q7
+        mov     [Q5 + 2*8], Q8
+        mov     [Q5 + 3*8], Q9
+        mov     [Q5 + 4*8], Q10
+        mov     [Q5 + 5*8], Q11
+        mov     [Q5 + 6*8], Q12
+        mov     [Q5 + 7*8], Q13
+
+        // set up for next iteration
+        // reload 64*nDigits2
+        mov     Q4, [rsp + 72 /*MEMSLOT0*/]
+
+        // reset Q5 & increment
+        sub     Q5, Q4
+        add     Q5, 64
+        // reset Q3
+        sub     Q3, Q4
+
+        // update PSrc1
+        add     Q1, 64
+
+        // nDigits1 loop counter
+        mov     D2, [rsp + 80 /*MEMSLOT1*/]
+        sub     D2, 1                              // sets Cy = Ov = 0 because nDigits1 < 2^32 / 64
+        mov     [rsp + 80 /*MEMSLOT1*/], D2
+        jnz     SymCryptFdefRawMulxOuterLoop
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareMulx(
+//     _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+//                                                             UINT32      nDigits,
+//     _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pSrc
+        // Q2 = nDigits
+        // Q3 = pDst
+
+        // Save parameters for phase 2
+
+        mov     [rsp + 72 /*MEMSLOT0*/], Q1   // save pSrc
+        mov     [rsp + 80 /*MEMSLOT1*/], Q2   // save nDigits
+        mov     [rsp + 88 /*MEMSLOT2*/], Q3   // save pDst
+
+        shl     Q2, 6       // nDigits * 64 = # bytes in Src to process
+        mov     [rsp + 96 /*MEMSLOT3*/], Q2   // save # bytes in Src to process
+
+        // Wipe destination for nDigits blocks
+        xor     Q0, Q0
+        mov     Q5, Q3
+        mov     Q4, Q2
+
+SymCryptFdefRawSquareMulxWipeLoop:
+        // we use 8-byte writes as we will be reading this very soon in 8-byte chunks, and this way the store-load
+        // forwarding works
+        mov     [Q5     ], Q0
+        mov     [Q5 +  8], Q0
+        mov     [Q5 + 16], Q0
+        mov     [Q5 + 24], Q0
+        mov     [Q5 + 32], Q0
+        mov     [Q5 + 40], Q0
+        mov     [Q5 + 48], Q0
+        mov     [Q5 + 56], Q0
+        add     Q5, 64
+        sub     Q4, 64
+        jnz     SymCryptFdefRawSquareMulxWipeLoop
+
+        // Cy = Ov = 0 here because the last 'sub Q4,64' yielded 0
+
+SymCryptFdefRawSquareMulxOuterLoop:
+
+        HALF_SQUARE_NODIAG8 Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q3, Q1, Q0, Q4, QH
+
+        sub     Q2, 64
+        jz      SymCryptFdefRawSquareMulxPhase2     // end of phase 1
+
+        lea     Q5, [Q1 + 64]
+        lea     Q3, [Q3 + 64]
+
+SymCryptFdefRawSquareMulxInnerLoop:
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q4                                    temps for multiplication
+        // Q1                                        pSrc running pointer outer loop
+        // Q2                                        # bytes left in pSrc to process in the inner loop
+        // Q3                                        pDst running pointer inner loop
+        // QH                                        fixed input reg for multiplication
+        // Q5                                        pSrc running pointer inner loop
+
+        // slot0                                     pSrc (used for final pass)
+        // slot1                                     nDigits (used for final pass)
+        // slot2                                     pDst (used for final pass)
+        // slot3                                     # bytes to process from pSrc in this iteration
+
+        MULADD88    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q3, Q1, Q5, Q0, Q4, QH
+
+        add     Q3, 64
+        add     Q5, 64
+
+        sub     Q2, 64                  // Sets Cy = Ov = 0 because nDigits < 2^32 / bits_per_digit
+        jnz     SymCryptFdefRawSquareMulxInnerLoop
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 + 0*8], Q6
+        mov     [Q3 + 1*8], Q7
+        mov     [Q3 + 2*8], Q8
+        mov     [Q3 + 3*8], Q9
+        mov     [Q3 + 4*8], Q10
+        mov     [Q3 + 5*8], Q11
+        mov     [Q3 + 6*8], Q12
+        mov     [Q3 + 7*8], Q13
+
+        mov     Q2, [rsp + 96 /*MEMSLOT3*/]   // restore # bytes in Src to process next
+
+        add     Q1, 64                                  // Shift outer Src pointer by 1 digit
+        sub     Q3, Q2                                  // reset output ptr
+        add     Q3, 128                                 // Shift output ptr by 2 digits
+
+        sub     Q2, 64                                  // Reduce number of bytes to process by 1 digit
+        mov     [rsp + 96 /*MEMSLOT3*/], Q2
+
+        jmp     SymCryptFdefRawSquareMulxOuterLoop
+
+
+SymCryptFdefRawSquareMulxPhase2:
+        // Cy = Ov = 0 because last 'sub Q2, 64' resulted in 0
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 +  8*8], Q6
+        mov     [Q3 +  9*8], Q7
+        mov     [Q3 + 10*8], Q8
+        mov     [Q3 + 11*8], Q9
+        mov     [Q3 + 12*8], Q10
+        mov     [Q3 + 13*8], Q11
+        mov     [Q3 + 14*8], Q12
+        mov     [Q3 + 15*8], Q13
+
+        // Compute diagonals, and add double the result so far
+
+        mov     Q1, [rsp + 72 /*MEMSLOT0*/]
+        mov     Q2, [rsp + 80 /*MEMSLOT1*/]
+        mov     Q3, [rsp + 88 /*MEMSLOT2*/]
+
+        // We can't keep the carries in Cy and Ov because there is no way to do a loop counter
+        // without touching the Ov flag.
+        // So we set the Ov carry in Q0, and retain a zero in Q4
+        xor     Q0, Q0
+        xor     Q4, Q4
+
+SymCryptFdefRawSquareMulxDiagonalsLoop:
+        // Cy = carry in
+        // Q0 = carry in (1 bit)
+        // Ov = 0
+
+        // First word is different to handle the carry
+        // SYMCRYPT_SQUARE_DIAG    0, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        mov     QH, [Q1]
+        mov     Q5, [Q3]
+        mov     Q6, [Q3 + 8]
+        mulx    Q8, Q7, QH
+        adcx    Q7, Q0              // add both carries
+        adcx    Q8, Q4              // Q4 = 0 - now Cy = 0 because result of multiply <= ff..fe00..01
+
+        adcx    Q7, Q5
+        adox    Q7, Q5
+        adcx    Q8, Q6
+        adox    Q8, Q6
+        mov     [Q3], Q7
+        mov     [Q3 + 8], Q8
+
+        SYMCRYPT_SQUARE_DIAG 1, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 2, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 3, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 4, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 5, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 6, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 7, Q1, Q3, Q5, Q6, Q7, Q8, QH
+
+        // Move the Ov flag into Q0
+        mov     D0, D4
+        adox    D0, D4
+
+        // There is no way to do a loop counter without overwriting the Ov flag
+        // Even the 'dec' instruction touches it, and LAHF/SAHF doesn't load/store the Ov flag.
+        // We can't push/pop efl in a function body
+
+        lea     Q1, [Q1 + 64]
+        lea     Q3, [Q3 + 128]
+        dec     Q2
+        jnz     SymCryptFdefRawSquareMulxDiagonalsLoop
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefMontgomeryReduceMulx(
+//     _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//     _Inout_                         PUINT32                 pSrc,
+//     _Out_                           PUINT32                 pDst )
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     [rsp + 72 /*MEMSLOT0*/], Q1
+        mov     [rsp + 80 /*MEMSLOT1*/], Q2
+        mov     [rsp + 88 /*MEMSLOT2*/], Q3
+
+        mov     D0, [Q1 + SymCryptModulusNdigitsOffsetAmd64]
+        mov     [rsp + 96 /*MEMSLOT3*/], D0
+        // CntOuter = nDigits - using first half of slot3
+
+        xor     D4, D4
+        mov     [rsp + 96 /*MEMSLOT3*/ + 4], D4
+        // HighCarry = 0 - using second half of slot3
+
+SymCryptFdefMontgomeryReduceMulxOuterLoop:
+        // Q1 = pmMod
+        // Q2 = pSrc = tmp buffer that we will reduce
+        mov     Q6, [Q2 + 0 * 8]
+        mov     Q7, [Q2 + 1 * 8]
+        mov     Q8, [Q2 + 2 * 8]
+        mov     Q9, [Q2 + 3 * 8]
+        mov     Q10, [Q2 + 4 * 8]
+        mov     Q11, [Q2 + 5 * 8]
+        mov     Q12, [Q2 + 6 * 8]
+        mov     Q13, [Q2 + 7 * 8]
+
+        mov     Q3, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        // Q2 = value to reduce
+        // Q6 - Q13 = Q2[0..7]
+        // Q1 = modulus value
+        // Q3 = modinv
+
+        MONTGOMERY18    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q3, Q1, Q2 + (0 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6,  Q3, Q1, Q2 + (1 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7,  Q3, Q1, Q2 + (2 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8,  Q3, Q1, Q2 + (3 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9,  Q3, Q1, Q2 + (4 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10,  Q3, Q1, Q2 + (5 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q12, Q13, Q6, Q7, Q8, Q9, Q10, Q11,  Q3, Q1, Q2 + (6 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q13, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q1, Q2 + (7 * 8), Q0, Q5, QH
+
+        // Q6 - Q13 = carry from multiply-add
+        // Q2[0..7] = Montgomery factors
+
+        mov     Q3, Q2         // factor to multiply by
+        add     Q1, 64
+        add     Q2, 64
+
+        dec     D4
+        jz      SymCryptFdefMontgomeryReduceMulxInnerLoopDone
+
+SymCryptFdefMontgomeryReduceMulxInnerLoop:
+
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q5                                    temps for multiplication
+        // Q1                                        running pointer pMod inner loop
+        // Q2                                        running pointer pSrc inner loop
+        // Q3                                        Montgomery factors for this row
+        // D4                                        loop ctr
+        // QH                                        fixed input reg for multiplication
+
+        MULADD88    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q2, Q1, Q3, Q0, Q5, QH
+            // pre & post: Cy = Ov = 0
+            // Q13..Q6:Q2[7-0] = R[7-0]:D[7-0] = A[7:0] * B[7:0] + R[7:0] + D[7:0]
+            // QH is volatile
+
+        add     Q1, 64
+        add     Q2, 64
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduceMulxInnerLoop
+
+
+SymCryptFdefMontgomeryReduceMulxInnerLoopDone:
+
+        // We have an 8-word carry here, which we need to add to the in-memory buffer and retain a carry
+        // We also saved a 1-bit carry from the previous outer loop
+        mov     D5, [rsp + 96 /*MEMSLOT3*/ + 4]
+        // move carry into Cy flag
+        neg     D5
+
+        // We do this in separate instructions to help the instruction decoder build up a lead...
+        mov     Q0, [Q2 + 0 * 8]
+        adc     Q0, Q6
+        mov     [Q2 + 0 * 8], Q0
+
+        mov     Q5, [Q2 + 1 * 8]
+        adc     Q5, Q7
+        mov     [Q2 + 1 * 8], Q5
+
+        mov     Q0, [Q2 + 2 * 8]
+        adc     Q0, Q8
+        mov     [Q2 + 2 * 8], Q0
+
+        mov     Q5, [Q2 + 3 * 8]
+        adc     Q5, Q9
+        mov     [Q2 + 3 * 8], Q5
+
+        mov     Q0, [Q2 + 4 * 8]
+        adc     Q0, Q10
+        mov     [Q2 + 4 * 8], Q0
+
+        mov     Q5, [Q2 + 5 * 8]
+        adc     Q5, Q11
+        mov     [Q2 + 5 * 8], Q5
+
+        mov     Q0, [Q2 + 6 * 8]
+        adc     Q0, Q12
+        mov     [Q2 + 6 * 8], Q0
+
+        mov     Q5, [Q2 + 7 * 8]
+        adc     Q5, Q13
+        mov     [Q2 + 7 * 8], Q5
+
+        adc     D4, D4                // D4 = carry (D4 was previously zero)
+        mov     [rsp + 96 /*MEMSLOT3*/ + 4], D4
+
+        mov     Q2, [rsp + 80 /*MEMSLOT1*/]
+        add     Q2, 64
+        mov     [rsp + 80 /*MEMSLOT1*/], Q2
+
+        mov     Q1, [rsp + 72 /*MEMSLOT0*/]
+
+        mov     D0, [rsp + 96 /*MEMSLOT3*/]
+        dec     D0
+        mov     [rsp + 96 /*MEMSLOT3*/], D0
+
+        jnz     SymCryptFdefMontgomeryReduceMulxOuterLoop
+
+        // D4 = output carry
+
+        mov     D6, [Q1 + SymCryptModulusNdigitsOffsetAmd64]
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]                    // modulus value
+
+        mov     Q3, [rsp + 88 /*MEMSLOT2*/]
+
+        // Q2 = result buffer pointer
+        // D6 = # digits
+        // Q1 = modulus value
+        // Q3 = Dst
+
+        // copy these values for the masked copy loop
+        mov     D7, D6      // nDigits
+        mov     Q8, Q2      // result buffer
+        mov     Q9, Q3      // destination pointer
+
+        // pDst = Reduction result - Modulus
+
+SymCryptFdefMontgomeryReduceMulxSubLoop:
+        mov     Q0,[Q2 + 0 * 8]
+        sbb     Q0,[Q1 + 0 * 8]
+        mov     [Q3 + 0 * 8], Q0
+
+        mov     Q5,[Q2 + 1 * 8]
+        sbb     Q5,[Q1 + 1 * 8]
+        mov     [Q3 + 1 * 8], Q5
+
+        mov     Q0,[Q2 + 2 * 8]
+        sbb     Q0,[Q1 + 2 * 8]
+        mov     [Q3 + 2 * 8], Q0
+
+        mov     Q5,[Q2 + 3 * 8]
+        sbb     Q5,[Q1 + 3 * 8]
+        mov     [Q3 + 3 * 8], Q5
+
+        mov     Q0,[Q2 + 4 * 8]
+        sbb     Q0,[Q1 + 4 * 8]
+        mov     [Q3 + 4 * 8], Q0
+
+        mov     Q5,[Q2 + 5 * 8]
+        sbb     Q5,[Q1 + 5 * 8]
+        mov     [Q3 + 5 * 8], Q5
+
+        mov     Q0,[Q2 + 6 * 8]
+        sbb     Q0,[Q1 + 6 * 8]
+        mov     [Q3 + 6 * 8], Q0
+
+        mov     Q5,[Q2 + 7 * 8]
+        sbb     Q5,[Q1 + 7 * 8]
+        mov     [Q3 + 7 * 8], Q5
+
+        lea     Q2, [Q2 + 64]
+        lea     Q1, [Q1 + 64]
+        lea     Q3, [Q3 + 64]
+        dec     D6
+        jnz     SymCryptFdefMontgomeryReduceMulxSubLoop
+
+        // now a masked copy from the reduction buffer to the destination.
+        // copy if high carry = 0 and Cy = 1
+        sbb     D4, 0
+        // D4 = copy mask, ff...ff  if copy, 0 of no copy
+
+        movd    xmm0, D4           // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+SymCryptFdefMontgomeryReduceMulxMaskedCopyLoop:
+        movdqa  xmm2, [Q8 + 0 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 0 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 0 * 16], xmm2
+
+        movdqa  xmm2, [Q8 + 1 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 1 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 1 * 16], xmm2
+
+        movdqa  xmm2, [Q8 + 2 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 2 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 2 * 16], xmm2
+
+        movdqa  xmm2, [Q8 + 3 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 3 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 3 * 16], xmm2
+
+        // Move on to the next digit
+
+        add     Q8, 64
+        add     Q9, 64
+        dec     D7
+        jnz     SymCryptFdefMontgomeryReduceMulxMaskedCopyLoop
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+.macro MULADD_LOADSTORE18  pS, pM, pD, QH, Tc, T0, T1
+    // \Tc:D[7:0] = (M[7:0] * K) + S[7:0] + \Tc
+    //
+    // \QH = K
+    // \T0, \T1 = scratch
+    // \Tc is carry in at start and carry out at end (in [0,2^64-1])
+
+    xor     \T0, \T0 // clear flags
+
+    mulx    \T1, \T0, [\pM + 0 * 8]
+    adox    \T0, \Tc
+    adcx    \T0, [\pS + 0 * 8]
+    mov     [\pD + 0 * 8], \T0
+
+    mulx    \Tc, \T0, [\pM + 1 * 8]
+    adox    \T0, \T1
+    adcx    \T0, [\pS + 1 * 8]
+    mov     [\pD + 1 * 8], \T0
+
+    mulx    \T1, \T0, [\pM + 2 * 8]
+    adox    \T0, \Tc
+    adcx    \T0, [\pS + 2 * 8]
+    mov     [\pD + 2 * 8], \T0
+
+    mulx    \Tc, \T0, [\pM + 3 * 8]
+    adox    \T0, \T1
+    adcx    \T0, [\pS + 3 * 8]
+    mov     [\pD + 3 * 8], \T0
+
+    mulx    \T1, \T0, [\pM + 4 * 8]
+    adox    \T0, \Tc
+    adcx    \T0, [\pS + 4 * 8]
+    mov     [\pD + 4 * 8], \T0
+
+    mulx    \Tc, \T0, [\pM + 5 * 8]
+    adox    \T0, \T1
+    adcx    \T0, [\pS + 5 * 8]
+    mov     [\pD + 5 * 8], \T0
+
+    mulx    \T1, \T0, [\pM + 6 * 8]
+    adox    \T0, \Tc
+    adcx    \T0, [\pS + 6 * 8]
+    mov     [\pD + 6 * 8], \T0
+
+    mulx    \Tc, \T0, [\pM + 7 * 8]
+    adox    \T0, \T1
+    adcx    \T0, [\pS + 7 * 8]
+    mov     [\pD + 7 * 8], \T0
+
+    mov     \T1, 0
+    adox    \Tc, \T1 // cannot overflow
+    adcx    \Tc, \T1 // cannot overflow
+.endm
+
+.macro SHIFTRIGHT2  pD, index, shrVal, shrMask, shlVal, Tc, T0, T1
+    // \T0 = D[\index + 1]
+    // D[\index + 1] = \Tc:\T0 >> \shrVal (== ((\T0>>\shrVal)&\shrMask) | (\Tc<<\shlVal))
+    // \Tc = D[\index]
+    // D[\index] = \T0:\Tc >> \shrVal
+    //
+    // \shrVal in [1,64]
+    // \shrMask = (\shrVal==64) ? 0 : -1
+    // \shlVal = 64-\shrVal
+    //
+    // \T0, \T1 = scratch
+    // Value of \Tc is bits to shift in the top at start, and bits to shift
+    // into next macro invocation at end
+
+    mov     \T0, [\pD + ((\index+1)*8)]
+    shrx    \T1, \T0, \shrVal
+    shlx    \Tc, \Tc, \shlVal
+    and     \T1, \shrMask
+    or      \T1, \Tc
+    mov     [\pD + ((\index+1)*8)], \T1
+
+    mov     \Tc, [\pD + (\index*8)]
+    shrx    \T1, \Tc, \shrVal
+    shlx    \T0, \T0, \shlVal
+    and     \T1, \shrMask
+    or      \T1, \T0
+    mov     [\pD + (\index*8)], \T1
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModDivSmallPow2Mulx(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _In_range_(1, 64)               UINT32                  exp,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Mulx):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Mulx)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Mulx); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Mulx)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     Q7, [Q2]
+        mov     QH, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        imul    QH, Q7  // QH = inv64 * peSrc[0]
+                        // this is the multiple of the modulus that must be added to peSrc
+                        // s.t. the least significant **64** bits are all zero
+
+        // Form bitmask to mask out bottom exp bits from multiplication result
+        xor     D0, D0
+        mov     Q7, -1
+        sub     D0, D3
+        shrx    Q7, Q7, Q0      // mask = -1 >> (-exp % 64)
+                                //      = 0xffffffffffffffff >> (64-exp)
+                                //      = (((UINT65)1)<<exp)-1
+
+        and     QH, Q7          // Extract the least significant exp bits from QH to produce K
+                                // K is the minimal multiple of the modulus that when added to peSrc
+                                // sets the least significant **exp** bits to zero
+
+        mov     D0, [Q1 + SymCryptModulusNdigitsOffsetAmd64]            // numDigits, used directly as loop counter
+        lea     Q5, [Q1 + SymCryptModulusValueOffsetAmd64]              // pointer to modulus value
+
+        xor     Q6, Q6 // zero one word carry
+
+SymCryptFdefModDivSmallPow2MulxMulAddLoop:
+        // Set peDst = peSrc + (K * mod)
+        // Also has up to 64b of carry out the top which is stored in Q6 at the end of the loop
+        //
+        // D0   loop ctr (one iteration per digit)
+        // QH   K
+        // Q2   running pointer peSrc inner loop (we directly modify Q2 as we only need to read peSrc once)
+        // Q5   running pointer pMod inner loop
+        // Q4   running pointer peDst inner loop (we directly modify Q4 as we read/write peDst in reverse in second loop)
+        // Q6   one word carry
+        // Q7, Q8 scratch
+
+        MULADD_LOADSTORE18 Q2, Q5, Q4, QH, Q6, Q7, Q8
+
+        // Move on to the next digit
+        add     Q2, 64
+        add     Q5, 64
+        add     Q4, 64
+
+        dec     D0
+        jnz     SymCryptFdefModDivSmallPow2MulxMulAddLoop
+
+        mov     D0, [Q1 + SymCryptModulusNdigitsOffsetAmd64]            // numDigits, used directly as loop counter
+        mov     D1, 64
+        xor     Q7, Q7
+        sub     D1, D3  // shlVal (also sets ZF iff exp == 64)
+        mov     Q2, -1
+        cmovz   Q2, Q7  // shrMask = ((exp == 64) ? 0 : -1)
+
+        sub     Q4, 64  // Q4 points to the most significant digit of peDst
+
+SymCryptFdefModDivSmallPow2MulxShiftRightLoop:
+        // Set peDst = peDst >> exp
+        // We work backwards (from MSB to LSB) so we avoid having to do special handling of the one word
+        // carry out of the top of the multiply add loop
+        //
+        // D0   loop ctr (one iteration per digit)
+        // Q1   shlVal
+        // Q2   shrMask
+        // Q3   shrVal
+        // Q4   running pointer peDst inner loop (decreasing)
+        // Q6   one word carry
+        // Q7, Q8 scratch
+
+        SHIFTRIGHT2 Q4, 6, Q3, Q2, Q1, Q6, Q7, Q8
+        SHIFTRIGHT2 Q4, 4, Q3, Q2, Q1, Q6, Q7, Q8
+        SHIFTRIGHT2 Q4, 2, Q3, Q2, Q1, Q6, Q7, Q8
+        SHIFTRIGHT2 Q4, 0, Q3, Q2, Q1, Q6, Q7, Q8
+
+        sub     Q4, 64
+
+        dec     D0
+        jnz     SymCryptFdefModDivSmallPow2MulxShiftRightLoop
+
+
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+
+// --------------------------------
+// 256-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAddMulx256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx256Asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+
+.seh_endprologue
+
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64
+
+        // compute Src1 + Src2 into (Q0, Q5, Q6, Q2), Cy = addition carry
+        // Compute sum + (-Mod) into (Q7, Q8, Q3, Q1), Ov = inverted subtraction borrow
+
+        xor     Q0, Q0 // Clear the flags
+
+        mov     Q0, [Q2 + 0*8]
+        adcx    Q0, [Q3 + 0*8]
+
+        mov     Q5, [Q2 + 1*8]
+        adcx    Q5, [Q3 + 1*8]
+
+        mov     Q6, [Q2 + 2*8]
+        adcx    Q6, [Q3 + 2*8]
+
+        mov     Q2, [Q2 + 3*8]
+        adcx    Q2, [Q3 + 3*8]
+
+        mov     Q7, [Q1 + 0*8]
+        adox    Q7, Q0
+
+        mov     Q8, [Q1 + 1*8]
+        adox    Q8, Q5
+
+        mov     Q3, [Q1 + 2*8]
+        adox    Q3, Q6
+
+        mov     Q1, [Q1 + 3*8]
+        adox    Q1, Q2
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        cmovc   Q0, Q7
+        cmovc   Q5, Q8
+        cmovc   Q6, Q3
+        cmovc   Q2, Q1
+
+        cmovo   Q0, Q7
+        cmovo   Q5, Q8
+        cmovo   Q6, Q3
+        cmovo   Q2, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q2
+
+
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+
+.macro MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE  T0, T1, QH, pA, Aoff, pB, pM, K, R0, R1, R2, R3, R4, R5
+    // (\R1, \R2, \R3, \R4, \R5) = A[\Aoff] * (B0..3) + (\R1, \R2, \R3, \R4, \R5)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R0) = \K * (M0..3) + (\R0, \R1, \R2, \R3, \R4, \R5)
+    // \K = modInv * \R1
+    // Note \K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // \R5 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \T0, \T1 = scratch
+
+    mov     \QH, [\pA + \Aoff]
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pB + 0*8]
+    adox    \R1, \T0
+    adcx    \R2, \T1
+
+    mulx    \T1, \T0, [\pB + 1*8]
+    adox    \R2, \T0
+    adcx    \R3, \T1
+
+    mulx    \T1, \T0, [\pB + 2*8]
+    adox    \R3, \T0
+    adcx    \R4, \T1
+
+    mulx    \T1, \T0, [\pB + 3*8]
+    adox    \R4, \T0
+    mov     \T0, 0
+    adcx    \T1, \T0      // \T1 in range [0, 2^64-1]
+
+    adox    \R5, \T0      // \R5 in range [0, 2]
+                        // defer adding \T1 to avoid overflowing earlier than necessary (we are bound by adox/adcx throughput)
+                        // Multiplication result in \R1, \R2, \R3, \R4, \R5+\T1
+
+    xor     \T0, \T0      // clear flags
+
+    mov     \QH, \K       // now use \K as a temporary while \T1 has the value to add to \R5
+
+    mulx    \K, \T0, [\pM + 0*8]
+    adcx    \R0, \T0      // sets \R0 to 0
+    adox    \R1, \K
+
+    mulx    \K, \T0, [\pM + 1*8]
+    adcx    \R1, \T0
+    adox    \R2, \K
+
+    mulx    \K, \T0, [\pM + 2*8]
+    adcx    \R2, \T0
+    adox    \R3, \K
+
+    mulx    \K, \T0, [\pM + 3*8]
+    adcx    \R3, \T0
+    adox    \R4, \K       // This may overflow
+
+    adcx    \R4, \R0      // This may overflow
+    adox    \R5, \R0      // \R5 in range [0, 3]
+
+    adc     \R5, \T1      // This may overflow
+
+    adc     \R0, \R0
+
+    mov     \K, \R1
+    imul    \K, [\pM - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulx256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulx256AsmInternal
+#endif
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+SymCryptFdefModMulMontgomeryMulx256AsmInternal: .global SymCryptFdefModMulMontgomeryMulx256AsmInternal
+
+        //
+        // SamL: We previously did small modular multiplication by doing a full integer multiplication followed by a
+        // Montgomery reduction on the full result.
+        // I observed that this approach could have an issue with dependency chains in the Montgomery reduction step,
+        // if the multiple K we need to add to the result to zero out the bottom 64b is not computed early enough we
+        // can be bound by the latency of multiplication rather than the throughput of multiplication and carry chain
+        // handling.
+        // Additionally, I wanted the new 384-bit specific code to follow a similar pattern to the new 256-bit code, and
+        // producing a full 384bx384b->768b multiplication would mean we run out of registers and have to spill in the
+        // body of the 384-bit function.
+        //
+        // The approach I have taken is to interleave the multiplication with the Montgomery reduction, so we only have
+        // to keep n+2 words in registers for accumulators in the inner processing. Additionally, for all but the
+        // Montgomery reduction of the final least-significant word, we have a word*limb multiplication (4 or 6 mulx)
+        // between issuing the computation of K and using the result.
+        //
+
+        xor     Q11, Q11  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0        // Note: this is intentionally adc as, if we issue an add here it is
+                              // liable to contend with a multiplication uop on the critical path
+                              // (adc issues on different ports to mulx/imul so does not contend)
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        adc     Q10, Q11      // Multiplication result in Q6, Q7, Q8, Q9, Q10
+
+        // Compute K for reducing Multiplication result
+        mov     Q12, Q6
+        imul    Q12, [Q1 + SymCryptModulusInv64OffsetAmd64]
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE Q0, Q5, QH, Q2,  8, Q3, Q1, Q12, Q6, Q7, Q8, Q9, Q10, Q11
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE Q0, Q5, QH, Q2, 16, Q3, Q1, Q12, Q7, Q8, Q9, Q10, Q11, Q6
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE Q0, Q5, QH, Q2, 24, Q3, Q1, Q12, Q8, Q9, Q10, Q11, Q6, Q7
+
+        xor     Q0, Q0      // clear flags
+        mov     QH, Q12
+
+        mulx    Q5, Q0, [Q1 + 0*8]
+        adcx    Q9, Q0      // Set Cy when Q9 is non-zero
+        adox    Q10, Q5
+
+        mulx    Q5, Q0, [Q1 + 1*8]
+        adcx    Q10, Q0
+        adox    Q11, Q5
+
+        mulx    Q5, Q0, [Q1 + 2*8]
+        adcx    Q11, Q0
+        adox    Q6, Q5
+
+        mulx    Q5, Q0, [Q1 + 3*8]
+        adcx    Q6, Q0
+        adox    Q7, Q5      // This may overflow
+
+        mov     Q0, 0
+        adcx    Q7, Q0      // This may overflow
+        adox    Q8, Q0
+
+        adc     Q8, Q0
+
+        // Montgomery-reduced multiplication value in (Q10, Q11, Q6, Q7, Q8), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q0, Q2, Q3, Q5), Cy = inverted subtraction borrow
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64
+
+        mov     Q0, [Q1 + 0*8]
+        add     Q0, Q10
+        mov     Q2, [Q1 + 1*8]
+        adc     Q2, Q11
+        mov     Q3, [Q1 + 2*8]
+        adc     Q3, Q6
+        mov     Q5, [Q1 + 3*8]
+        adc     Q5, Q7
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q8, Q8  // This is non-zero iff
+                        //      the reduced value did not fit in 256-bits; or
+                        //      the subtraction by Mod did not borrow
+                        // adc sets the ZF appropriately
+
+        cmovnz  Q10, Q0
+        cmovnz  Q11, Q2
+        cmovnz  Q6, Q3
+        cmovnz  Q7, Q5
+
+        mov     [Q4 + 0*8], Q10
+        mov     [Q4 + 1*8], Q11
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulx256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulx256Asm the same
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulx256AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+// --------------------------------
+// 384-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAddMulx384(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx384Asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64
+
+        // compute Src1 + Src2 into (Q0, Q5, Q6, Q7, Q8, Q2), Cy = addition carry
+        // Compute sum + (-Mod) into (Q9, Q10, Q11, Q12, Q3, Q1), Ov = inverted subtraction borrow
+
+        xor     Q0, Q0  // clear flags
+
+        mov     Q0, [Q2 + 0*8]
+        adcx    Q0, [Q3 + 0*8]
+
+        mov     Q5, [Q2 + 1*8]
+        adcx    Q5, [Q3 + 1*8]
+
+        mov     Q6, [Q2 + 2*8]
+        adcx    Q6, [Q3 + 2*8]
+
+        mov     Q7, [Q2 + 3*8]
+        adcx    Q7, [Q3 + 3*8]
+
+        mov     Q8, [Q2 + 4*8]
+        adcx    Q8, [Q3 + 4*8]
+
+        mov     Q2, [Q2 + 5*8]
+        adcx    Q2, [Q3 + 5*8]
+
+        mov     Q9,  [Q1 + 0*8]
+        adox    Q9,  Q0
+
+        mov     Q10, [Q1 + 1*8]
+        adox    Q10, Q5
+
+        mov     Q11, [Q1 + 2*8]
+        adox    Q11, Q6
+
+        mov     Q12, [Q1 + 3*8]
+        adox    Q12, Q7
+
+        mov     Q3,  [Q1 + 4*8]
+        adox    Q3,  Q8
+
+        mov     Q1,  [Q1 + 5*8]
+        adox    Q1,  Q2
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        cmovc   Q0, Q9
+        cmovc   Q5, Q10
+        cmovc   Q6, Q11
+        cmovc   Q7, Q12
+        cmovc   Q8, Q3
+        cmovc   Q2, Q1
+
+        cmovo   Q0, Q9
+        cmovo   Q5, Q10
+        cmovo   Q6, Q11
+        cmovo   Q7, Q12
+        cmovo   Q8, Q3
+        cmovo   Q2, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q2
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+.macro MUL16_P384  T0, T1, QH, pA, Aoff, pB, R0, R1, R2, R3, R4, R5, R6, R7
+    // (\R0, \R1, \R2, \R3, \R4, \R5, \R6, \R7) = A[\Aoff] * (B0..5) + (\R0, \R1, \R2, \R3, \R4, \R5, \R6)
+    // \R6 in [0, 2] at start
+    // \R7 in [0, 1] at end
+    // \T0, \T1 = scratch
+
+    mov     \QH, [\pA + \Aoff]
+    xor     \R7, \R7      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pB + 0*8]
+    adcx    \R0, \T0
+    adox    \R1, \T1
+
+    mulx    \T1, \T0, [\pB + 1*8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1, \T0, [\pB + 2*8]
+    adcx    \R2, \T0
+    adox    \R3, \T1
+
+    mulx    \T1, \T0, [\pB + 3*8]
+    adcx    \R3, \T0
+    adox    \R4, \T1
+
+    mulx    \T1, \T0, [\pB + 4*8]
+    adcx    \R4, \T0
+    adox    \R5, \T1
+
+    mulx    \T1, \T0, [\pB + 5*8]
+    adcx    \R5, \T0
+    adox    \R6, \R7      // \R6 in [0, 3]
+
+
+    adc     \R6, \T1      // overflow possible
+    adc     \R7, \R7
+.endm
+
+.macro MONT16_P384  T0, T1, QH, pM, N4, R0, R1, R2, R3, R4, R5, R6, R7
+    // K = modInv * \R0
+    // (\R3, \N4) = \R3 - (K-\N4)
+    // (\R0, \R1, \R2, \R3, \N4) = K * (M0..2) + (\R0, \R1, \R2, \R3, \N4)
+    // (\R6, \R7) = K + (\R6, \R7)
+    //
+    // Note K is chosen using pMod's montgomery inverse s.t. \R0 is set to 0
+    // \pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // \N4 is 0 or -1 at start and end
+    // \R7 in [0, 1] at start and in [0, 2] at end
+    // \R0 is 0 at end
+    // \T0, \T1 = scratch
+    //
+    // P384 has a modulus with a form which makes it possible to perform Montgomery reduction
+    // in an interesting way. The modulus is:
+    // 2^384 - 2^128 - 2^96 + 2^32 - 1
+    // This means when we want to add a multiple of the Modulus in reduction, we can consider
+    // not performing the full multiplication, but taking shortcuts.
+    // It is hard to get much benefit from this, as doing the full multiplication of
+    // 1 digit * 6 digits takes approximately 6 cycles with mulx, adox and adcx
+    //
+    // The approach taken here notes that K*M will always add 2^384 * K to the value to reduce
+    // with a subtraction of a value < 2^256.
+    // On entry to this macro we have \N4 representing a deferred borrow (value 0 or -1) from the
+    // 4th word of the last macro (now aligned to the 3rd word of this macro), on exit we set \N4
+    // to be the borrow to defer to the next round.
+    //
+    //            ____ ____ ____ ____ ____ ____
+    //           | M5 | M4 | M3 | M2 | M1 | M0 |
+    //           | ff | ff | ff | fe | f0 | 0f | (P384 modulus if you sign extend each nibble to 32b)
+    //           |____|____|____|____|____|____|
+    //  ____ ____ ____ ____ ____ ____ ____ ____
+    // |    |    |\  /|\  /|    |    |    |    |
+    // | \R7 | \R6 | \R5 | \R4 | \R3 | \R2 | \R1 | \R0 |
+    // |____|____|/__\|/__\|____|____|____|____|
+    //                                      | x modInv (0x0000000100000001)
+    //                      ____           _v__
+    //                     |    |         |    |
+    //                   + |N4in|         |  K |
+    //                     |____|         |____|
+    //       ____           ____
+    //      |    |         |    |
+    //    + |  K |       - |  K | This row is equivalent to multiplying K by (M3..5)
+    //      |____|     ____|____| once the borrow is propagated
+    //                |    |
+    //                | \N4 |
+    //                |____|     ____ ____ ____
+    //                          |M2*K|M1*K|M0*K|
+    //                        + | lo | lo | lo |
+    //                      ____|____|____|____|
+    //                     |M2*K|M1*K|M0*K|
+    //                   + | hi | hi | hi |
+    //                     |____|____|____|
+    //
+    // It is tempting to avoid the multiplication of K by M2, and subtract 2*K from \R2 instead, but the
+    // deferred subtraction (N3) then can be in the range [0,-2], and it is no longer possible to do the
+    // subtraction using the borrow flag. It ends up being more expensive than just doing another multiplication
+    // in my testing.
+    // It is also tempting to compute K using a shift and add, rather than imul, but experimentally, this
+    // performs worse.
+    //
+
+    mov     \QH, \R0
+    imul    \QH, [\pM - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]
+
+    add     \N4, -1
+    sbb     \R3, \QH
+    sbb     \N4, \N4      // sets \N4 to 0 if no borrow, or -1 if there is a borrow
+
+    xor     \T0, \T0      // clear flags
+
+    mulx    \T1, \T0, [\pM + 0*8]
+    adcx    \R0, \T0      // sets \R0 to 0
+    adox    \R1, \T1
+
+    mulx    \T1, \T0, [\pM + 1*8]
+    adcx    \R1, \T0
+    adox    \R2, \T1
+
+    mulx    \T1, \T0, [\pM + 2*8]
+    adcx    \R2, \T0
+    adox    \R3, \T1
+
+    adcx    \R3, \R0
+    adox    \N4, \R0
+
+    adc     \N4, \R0      // A carry into \N4 can occur iff the previous subtraction borrowed
+                        // The value of \N4 is guaranteed to be 0 or -1 here
+
+    add     \R6, \QH
+    adc     \R7, \R0
+
+.endm
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulxP384AsmInternal
+#endif
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulxP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP384Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+SymCryptFdefModMulMontgomeryMulxP384AsmInternal: .global SymCryptFdefModMulMontgomeryMulxP384AsmInternal
+
+        mov     [rsp + 72 /*MEMSLOT0*/], Q4   // save pDst
+
+        // SamL: In optimizing for P384 specifically, we need to keep an extra register
+        // free for deferring a borrow (N4 in the MONT16_P384 macro). This means that it
+        // is not possible to do the INTERLEAVE approach taken in the other small Mulx
+        // multiplication variants (and described in SymCryptFdefModMulMontgomeryMulx256Asm),
+        // as there is too much register pressure.
+        // Although we reintroduce possible dependency chain issues with the generation and
+        // use of K for the Montgomery reduction, we generate fewer total micro-ops and the
+        // overall performance is a few % better at the time of writing.
+
+        xor     Q13, Q13  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0        // Note: this is intentionally adc as, if we issue an add here it is
+                              // liable to contend with a multiplication uop on the critical path
+                              // (adc issues on different ports to mulx/imul so does not contend)
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        mulx    Q11, Q0, [Q3 + 4*8]
+        adc     Q10, Q0
+
+        mulx    Q12, Q0, [Q3 + 5*8]
+        adc     Q11, Q0
+
+        adc     Q12, Q13        // Multiplication result in Q6, Q7, Q8, Q9, Q10, Q11, Q12
+        add     Q1, SymCryptModulusValueOffsetAmd64
+        xor     Q4, Q4
+
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        MUL16_P384      Q0, Q5, QH, Q2,  8, Q3, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6
+
+        MUL16_P384      Q0, Q5, QH, Q2, 16, Q3, Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7
+
+        MUL16_P384      Q0, Q5, QH, Q2, 24, Q3, Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8
+
+        MUL16_P384      Q0, Q5, QH, Q2, 32, Q3, Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9
+
+        MUL16_P384      Q0, Q5, QH, Q2, 40, Q3, Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10
+
+        xor     Q0, Q0
+
+        adox    Q7, Q4  // Q4 is 0 or -1
+                        // Adding it to Q7..Q10 sign extends it correctly; a carry represents an inverted borrow
+        adox    Q8, Q4
+        adox    Q9, Q4
+        adox    Q10, Q4
+
+        // Montgomery-reduced multiplication value in (Q12, Q13, Q6, Q7, Q8, Q9, Q10), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q2, Q3, Q5, Q0, Q1, Q11), Cy = inverted subtraction borrow
+        // (-Mod) is (0xffffffff00000001, 0x00000000ffffffff, 1, 0, 0, 0)
+
+        mov     Q2,  [Q1 + SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64 + 0*8]
+        add     Q2,  Q12
+        mov     Q3,  [Q1 + 0*8] // Modulus word 0 is equal to Negated Modulus word 1
+        adc     Q3,  Q13
+        mov     Q5,  1  // Don't need to load 1 and 0 values from memory - we statically know these values for P384
+        adc     Q5,  Q6
+        mov     Q1,  Q0 // Q0 and Q11 are already zero, set Q1 to zero too
+        adc     Q0,  Q7
+        adc     Q1,  Q8
+        adc     Q11, Q9
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q10, Q10        // This is non-zero iff
+                                //      the reduced value did not fit in 384-bits; or
+                                //      the subtraction by Mod did not borrow
+                                // adc sets the ZF appropriately
+
+        cmovnz  Q12, Q2
+        cmovnz  Q13, Q3
+        cmovnz  Q6,  Q5
+        cmovnz  Q7,  Q0
+        cmovnz  Q8,  Q1
+        cmovnz  Q9,  Q11
+
+        mov     Q4, [rsp + 72 /*MEMSLOT0*/]   // restore pDst
+
+        mov     [Q4 + 0*8], Q12
+        mov     [Q4 + 1*8], Q13
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q9
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulxP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulx384Asm the same
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP384Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulxP384AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+
+// --------------------------------
+// 1024-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nWords1)             PCUINT32    pSrc1,
+//    _In_reads_(nWords2)             PCUINT32    pSrc2,
+//                                    UINT32      nDigits,
+//    _Out_writes_(nWords1 + nWords2) PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx1024):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx1024)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx1024); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx1024)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // First we wipe nDigits of the result (size of in)
+        // Q1 = pSrc1
+        // Q2 = pSrc2
+        // Q3 = nDigits
+        // Q4 = pDst
+
+        // Wipe destination for nDigit2 blocks
+        xorps       xmm0,xmm0               // Zero register for 16-byte wipes
+
+        movaps      [Q4],xmm0
+        movaps      [Q4+16],xmm0            // Wipe 32 bytes
+        movaps      [Q4+32],xmm0            // Wipe 32 bytes
+        movaps      [Q4+48],xmm0            // Wipe 32 bytes
+
+        movaps      [Q4+64],xmm0
+        movaps      [Q4+80],xmm0            // Wipe 32 bytes
+        movaps      [Q4+96],xmm0            // Wipe 32 bytes
+        movaps      [Q4+112],xmm0           // Wipe 32 bytes
+
+        // Digit 1 from src2
+
+        ZEROREG_8   Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12      // Leaves Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q2, 64              // Src2 ptr
+        add     Q4, 64
+        xor     Q0, Q0              // sets Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q4, 64
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q4 + 0*8], Q5
+        mov     [Q4 + 1*8], Q6
+        mov     [Q4 + 2*8], Q7
+        mov     [Q4 + 3*8], Q8
+        mov     [Q4 + 4*8], Q9
+        mov     [Q4 + 5*8], Q10
+        mov     [Q4 + 6*8], Q11
+        mov     [Q4 + 7*8], Q12
+
+        // Digit 2 from src2
+
+        // set up
+
+        // Mov Q4 one digit back
+        sub     Q4, 64
+
+        // reload pSrc2
+        sub     Q2, 64
+
+        // update PSrc1
+        add     Q1, 64
+
+        ZEROREG_8   Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12      // Leaves Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q2, 64              // Src2 ptr
+        add     Q4, 64
+        xor     Q0, Q0              // sets Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q4, 64
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q4 + 0*8], Q5
+        mov     [Q4 + 1*8], Q6
+        mov     [Q4 + 2*8], Q7
+        mov     [Q4 + 3*8], Q8
+        mov     [Q4 + 4*8], Q9
+        mov     [Q4 + 5*8], Q10
+        mov     [Q4 + 6*8], Q11
+        mov     [Q4 + 7*8], Q12
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareMulx1024(
+//     _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+//                                                             UINT32      nDigits,
+//     _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx1024):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx1024)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx1024); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx1024)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Wipe 128 bytes of destination
+        xorps       xmm0,xmm0               // Zero register for 16-byte wipes
+
+        movaps      [Q3],xmm0
+        movaps      [Q3+16],xmm0
+        movaps      [Q3+32],xmm0
+        movaps      [Q3+48],xmm0
+
+        movaps      [Q3+64],xmm0
+        movaps      [Q3+80],xmm0
+        movaps      [Q3+96],xmm0
+        movaps      [Q3+112],xmm0
+
+        xor     Q0, Q0                      // Sets Cy = Ov = 0
+
+        HALF_SQUARE_NODIAG8 Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q1, Q0, Q2, QH
+
+        lea     Q4, [Q1 + 64]               // Q4 = pSrc + 64
+        lea     Q3, [Q3 + 64]               // Q3 = pDst + 64
+
+        // Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12        8-word carry
+        // Q0, Q2                                   temps for multiplication
+        // Q1                                       pSrc (constant)
+        // Q4                                       pSrc + 64 (constant)
+        // Q3                                       pDst running pointer
+        // QH                                       fixed input reg for multiplication
+
+        MULADD88    Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q3, Q1, Q4, Q0, Q2, QH
+
+        add     Q3, 64                      // Q3 = pDst + 128
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 + 0*8], Q5
+        mov     [Q3 + 1*8], Q6
+        mov     [Q3 + 2*8], Q7
+        mov     [Q3 + 3*8], Q8
+        mov     [Q3 + 4*8], Q9
+        mov     [Q3 + 5*8], Q10
+        mov     [Q3 + 6*8], Q11
+        mov     [Q3 + 7*8], Q12
+
+        // Q3 which is the destination pointer is shifted here by 2 digits
+
+        xor     Q0, Q0                        // Sets Cy = Ov = 0
+
+        HALF_SQUARE_NODIAG8 Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q4, Q0, Q2, QH
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 +  8*8], Q5
+        mov     [Q3 +  9*8], Q6
+        mov     [Q3 + 10*8], Q7
+        mov     [Q3 + 11*8], Q8
+        mov     [Q3 + 12*8], Q9
+        mov     [Q3 + 13*8], Q10
+        mov     [Q3 + 14*8], Q11
+        mov     [Q3 + 15*8], Q12
+
+        // Compute diagonals, and add double the result so far
+
+        sub     Q3, 128         // Q3 = pDst - sets Cy = Ov = 0
+
+        SYMCRYPT_SQUARE_DIAG 0, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 1, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 2, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 3, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 4, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 5, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 6, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 7, Q1, Q3, Q0, Q2, Q4, Q5, QH
+
+        SYMCRYPT_SQUARE_DIAG 8, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 9, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 10, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 11, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 12, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 13, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 14, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 15, Q1, Q3, Q0, Q2, Q4, Q5, QH
+
+
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefMontgomeryReduceMulx1024(
+//     _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//     _Inout_                         PUINT32                 pSrc,
+//     _Out_                           PUINT32                 pDst )
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx1024):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx1024)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx1024); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx1024)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        mov     [rsp + 72 /*MEMSLOT0*/], Q3
+
+        mov     D0, 2
+        mov     [rsp + 80 /*MEMSLOT1*/], D0
+        // CntOuter = nDigits - using first half of slot3
+
+        xor     D4, D4
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]                      // modulus value
+
+SymCryptFdefMontgomeryReduceMulx1024OuterLoop:
+        // Q1 = pmMod
+        // Q2 = pSrc = tmp buffer that we will reduce
+        mov     Q6, [Q2 + 0 * 8]
+        mov     Q7, [Q2 + 1 * 8]
+        mov     Q8, [Q2 + 2 * 8]
+        mov     Q9, [Q2 + 3 * 8]
+        mov     Q10, [Q2 + 4 * 8]
+        mov     Q11, [Q2 + 5 * 8]
+        mov     Q12, [Q2 + 6 * 8]
+        mov     Q13, [Q2 + 7 * 8]
+
+        mov     Q3, [Q1 - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]    // inv64
+
+        // Q2 = value to reduce
+        // Q6 - Q13 = Q2[0..7]
+        // Q1 = modulus value
+        // Q3 = modinv
+
+        MONTGOMERY18    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q3, Q1, Q2 + (0 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6,  Q3, Q1, Q2 + (1 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7,  Q3, Q1, Q2 + (2 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8,  Q3, Q1, Q2 + (3 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9,  Q3, Q1, Q2 + (4 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10,  Q3, Q1, Q2 + (5 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q12, Q13, Q6, Q7, Q8, Q9, Q10, Q11,  Q3, Q1, Q2 + (6 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q13, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q1, Q2 + (7 * 8), Q0, Q5, QH
+
+        // Q6 - Q13 = carry from multiply-add
+        // Q2[0..7] = Montgomery factors
+
+        mov     Q3, Q2         // factor to multiply by
+        add     Q1, 64
+        add     Q2, 64
+
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q5                                    temps for multiplication
+        // Q1                                        running pointer pMod inner loop
+        // Q2                                        running pointer pSrc inner loop
+        // Q3                                        Montgomery factors for this row
+        // D4                                        loop ctr
+        // QH                                        fixed input reg for multiplication
+
+        MULADD88    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q2, Q1, Q3, Q0, Q5, QH
+            // pre & post: Cy = Ov = 0
+            // Q13..Q6:Q2[7-0] = R[7-0]:D[7-0] = A[7:0] * B[7:0] + R[7:0] + D[7:0]
+            // QH is volatile
+
+        add     Q1, 64
+        add     Q2, 64
+
+        // We have an 8-word carry here, which we need to add to the in-memory buffer and retain a carry
+        // We also saved a 1-bit carry from the previous outer loop in D4
+        // move carry into Cy flag
+        neg     D4
+        mov     D4, 0
+
+        // We do this in separate instructions to help the instruction decoder build up a lead...
+        mov     Q0, [Q2 + 0 * 8]
+        adc     Q0, Q6
+        mov     [Q2 + 0 * 8], Q0
+
+        mov     Q5, [Q2 + 1 * 8]
+        adc     Q5, Q7
+        mov     [Q2 + 1 * 8], Q5
+
+        mov     Q0, [Q2 + 2 * 8]
+        adc     Q0, Q8
+        mov     [Q2 + 2 * 8], Q0
+
+        mov     Q5, [Q2 + 3 * 8]
+        adc     Q5, Q9
+        mov     [Q2 + 3 * 8], Q5
+
+        mov     Q0, [Q2 + 4 * 8]
+        adc     Q0, Q10
+        mov     [Q2 + 4 * 8], Q0
+
+        mov     Q5, [Q2 + 5 * 8]
+        adc     Q5, Q11
+        mov     [Q2 + 5 * 8], Q5
+
+        mov     Q0, [Q2 + 6 * 8]
+        adc     Q0, Q12
+        mov     [Q2 + 6 * 8], Q0
+
+        mov     Q5, [Q2 + 7 * 8]
+        adc     Q5, Q13
+        mov     [Q2 + 7 * 8], Q5
+
+        adc     D4, D4                  // D4 = carry (D4 was previously zero)
+
+        sub     Q2, 64                  // Q2 = tmp buffer that we will reduce (64B are now zeroed)
+        sub     Q1, 128                 // Q1 = modulus value
+
+        mov     D0, [rsp + 80 /*MEMSLOT1*/]
+        sub     D0, 1
+        mov     [rsp + 80 /*MEMSLOT1*/], D0
+
+        jnz     SymCryptFdefMontgomeryReduceMulx1024OuterLoop
+
+        // D4 = output carry
+
+        mov     Q3, [rsp + 72 /*MEMSLOT0*/]
+
+        // Q2 = result buffer pointer
+        // Q1 = modulus value
+        // Q3 = Dst
+
+        // pDst = Reduction result - Modulus
+
+        mov     Q0,[Q2 + 0 * 8]
+        sbb     Q0,[Q1 + 0 * 8]
+        mov     [Q3 + 0 * 8], Q0
+
+        mov     Q5,[Q2 + 1 * 8]
+        sbb     Q5,[Q1 + 1 * 8]
+        mov     [Q3 + 1 * 8], Q5
+
+        mov     Q0,[Q2 + 2 * 8]
+        sbb     Q0,[Q1 + 2 * 8]
+        mov     [Q3 + 2 * 8], Q0
+
+        mov     Q5,[Q2 + 3 * 8]
+        sbb     Q5,[Q1 + 3 * 8]
+        mov     [Q3 + 3 * 8], Q5
+
+        mov     Q0,[Q2 + 4 * 8]
+        sbb     Q0,[Q1 + 4 * 8]
+        mov     [Q3 + 4 * 8], Q0
+
+        mov     Q5,[Q2 + 5 * 8]
+        sbb     Q5,[Q1 + 5 * 8]
+        mov     [Q3 + 5 * 8], Q5
+
+        mov     Q0,[Q2 + 6 * 8]
+        sbb     Q0,[Q1 + 6 * 8]
+        mov     [Q3 + 6 * 8], Q0
+
+        mov     Q5,[Q2 + 7 * 8]
+        sbb     Q5,[Q1 + 7 * 8]
+        mov     [Q3 + 7 * 8], Q5
+
+        mov     Q0,[Q2 + 8 * 8]
+        sbb     Q0,[Q1 + 8 * 8]
+        mov     [Q3 + 8 * 8], Q0
+
+        mov     Q5,[Q2 + 9 * 8]
+        sbb     Q5,[Q1 + 9 * 8]
+        mov     [Q3 + 9 * 8], Q5
+
+        mov     Q0,[Q2 + 10 * 8]
+        sbb     Q0,[Q1 + 10 * 8]
+        mov     [Q3 + 10 * 8], Q0
+
+        mov     Q5,[Q2 + 11 * 8]
+        sbb     Q5,[Q1 + 11 * 8]
+        mov     [Q3 + 11 * 8], Q5
+
+        mov     Q0,[Q2 + 12 * 8]
+        sbb     Q0,[Q1 + 12 * 8]
+        mov     [Q3 + 12 * 8], Q0
+
+        mov     Q5,[Q2 + 13 * 8]
+        sbb     Q5,[Q1 + 13 * 8]
+        mov     [Q3 + 13 * 8], Q5
+
+        mov     Q0,[Q2 + 14 * 8]
+        sbb     Q0,[Q1 + 14 * 8]
+        mov     [Q3 + 14 * 8], Q0
+
+        mov     Q5,[Q2 + 15 * 8]
+        sbb     Q5,[Q1 + 15 * 8]
+        mov     [Q3 + 15 * 8], Q5
+
+        // now a masked copy from the reduction buffer to the destination.
+        // copy if high carry = 0 and Cy = 1
+        sbb     D4, 0
+        // D4 = copy mask, ff...ff  if copy, 0 of no copy
+
+        movd    xmm0, D4            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+
+        movdqa  xmm2, [Q2 + 0 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 0 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 0 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 1 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 1 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 1 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 2 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 2 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 2 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 3 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 3 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 3 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 4 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 4 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 4 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 5 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 5 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 5 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 6 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 6 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 6 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 7 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 7 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 7 * 16], xmm2
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+#if 0
+//////////////////////////////////////////////////////////////
+//
+// UNUSED ASSEMBLY FROM HERE
+// Kept in case we need it later, but removed at compile time
+// to avoid inclusion in produced binaries (the linker struggles
+// to remove unused assembly functions)
+//
+//////////////////////////////////////////////////////////////
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulxP256AsmInternal
+#endif
+
+.macro MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256  T0, T1, QH, pA, Aoff, pB, pM, R0, R1, R2, R3, R4, R5
+    // (\R1, \R2, \R3, \R4, \R5) = A[\Aoff] * (B0..3) + (\R1, \R2, \R3, \R4, \R5)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R0) = \R0 * (M0..3) + (\R0, \R1, \R2, \R3, \R4, \R5)
+    // \R1 = K = modInv * \R1, as modInv == 1
+    // Note K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // \R5 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \T0, \T1 = scratch
+
+    mov     \QH, [\pA + \Aoff]
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pB + 0*8]
+    adox    \R1, \T0
+    adcx    \R2, \T1
+
+    mulx    \T1, \T0, [\pB + 1*8]
+    adox    \R2, \T0
+    adcx    \R3, \T1
+
+    mulx    \T1, \T0, [\pB + 2*8]
+    adox    \R3, \T0
+    adcx    \R4, \T1
+
+    mulx    \T1, \T0, [\pB + 3*8]
+    adox    \R4, \T0
+    mov     \T0, 0
+    adcx    \T1, \T0      // \T1 in range [0, 2^64-1]
+
+    adox    \R5, \T0      // \R5 in range [0, 2]
+                        // defer adding \T1 to avoid overflowing earlier than necessary (we are bound by adox/adcx throughput)
+                        // Multiplication result in \R1, \R2, \R3, \R4, \R5+\T1
+
+    xor     \T0, \T0      // clear flags
+
+    mov     \QH, \R0      // now use \R0 as a temporary while \T1 has the value to add to \R5
+
+    adox    \R1, \R0
+
+    mulx    \R0, \T0, [\pM + 1*8]
+    adcx    \R1, \T0
+    adox    \R2, \R0
+
+    mov     \T0, 0       // For P256, 3rd digit of the modulus is 0
+    adcx    \R2, \T0
+    adox    \R3, \T0
+
+    mulx    \R0, \T0, [\pM + 3*8]
+    adcx    \R3, \T0
+    adox    \R4, \R0      // This may overflow
+
+    mov     \R0, 0
+    adcx    \R4, \R0      // This may overflow
+    adox    \R5, \R0      // \R5 in range [0, 3]
+
+    adcx    \R5, \T1      // This may overflow
+
+    adcx    \R0, \R0
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulxP256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+SymCryptFdefModMulMontgomeryMulxP256AsmInternal: .global SymCryptFdefModMulMontgomeryMulxP256AsmInternal
+
+        // See SymCryptFdefModMulMontgomeryMulx256Asm for the rationale behind this function!
+
+        xor     Q11, Q11  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        adc     Q10, Q11      // Multiplication result in Q6, Q7, Q8, Q9, Q10
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256 Q0, Q5, QH, Q2,  8, Q3, Q1, Q6, Q7, Q8, Q9, Q10, Q11
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256 Q0, Q5, QH, Q2, 16, Q3, Q1, Q7, Q8, Q9, Q10, Q11, Q6
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256 Q0, Q5, QH, Q2, 24, Q3, Q1, Q8, Q9, Q10, Q11, Q6, Q7
+
+        xor     Q0, Q0      // clear flags
+        mov     QH, Q9
+
+        adox    Q10, Q9
+
+        mulx    Q5, Q0, [Q1 + 1*8]
+        adcx    Q10, Q0
+        adox    Q11, Q5
+
+        mov     Q0, 0       // For P256, 3rd digit of the modulus is 0
+        adcx    Q11, Q0
+        adox    Q6, Q0
+
+        mulx    Q5, Q0, [Q1 + 3*8]
+        adcx    Q6, Q0
+        adox    Q7, Q5      // This may overflow
+
+        mov     Q0, 0
+        adcx    Q7, Q0      // This may overflow
+        adox    Q8, Q0
+
+        adcx    Q8, Q0
+
+        // Montgomery-reduced multiplication value in (Q10, Q11, Q6, Q7, Q8), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q0, Q2, Q3, Q5), Cy = inverted subtraction borrow
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64
+
+        mov     Q0, 1
+        add     Q0, Q10
+        mov     Q2, [Q1 + 1*8]
+        adc     Q2, Q11
+        mov     Q3, [Q1 + 2*8]
+        adc     Q3, Q6
+        mov     Q5, [Q1 + 3*8]
+        adc     Q5, Q7
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q8, Q8  // This is non-zero iff
+                        //      the reduced value did not fit in 256-bits; or
+                        //      the subtraction by Mod did not borrow
+                        // adc sets the ZF appropriately
+
+        cmovnz  Q10, Q0
+        cmovnz  Q11, Q2
+        cmovnz  Q6, Q3
+        cmovnz  Q7, Q5
+
+        mov     [Q4 + 0*8], Q10
+        mov     [Q4 + 1*8], Q11
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulxP256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulxP256Asm the same
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP256Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulxP256AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+
+.macro MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE  T0, T1, QH, pA, Aoff, pB, pM, K, R0, R1, R2, R3, R4, R5, R6, R7
+    // (\R1, \R2, \R3, \R4, \R5, \R6, \R7) = A[\Aoff] * (B0..5) + (\R1, \R2, \R3, \R4, \R5, \R6, \R7)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R6, \R7, \R0) = \K * (M0..5) + (\R0, \R1, \R2, \R3, \R4, \R5, \R6, \R7)
+    // \K = modInv * \R1
+    // Note \K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // \R7 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \T0, \T1 = scratch
+
+    mov     \QH, [\pA + \Aoff]
+    xor     \T0, \T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    \T1, \T0, [\pB + 0*8]
+    adox    \R1, \T0
+    adcx    \R2, \T1
+
+    mulx    \T1, \T0, [\pB + 1*8]
+    adox    \R2, \T0
+    adcx    \R3, \T1
+
+    mulx    \T1, \T0, [\pB + 2*8]
+    adox    \R3, \T0
+    adcx    \R4, \T1
+
+    mulx    \T1, \T0, [\pB + 3*8]
+    adox    \R4, \T0
+    adcx    \R5, \T1
+
+    mulx    \T1, \T0, [\pB + 4*8]
+    adox    \R5, \T0
+    adcx    \R6, \T1
+
+    mulx    \T1, \T0, [\pB + 5*8]
+    adox    \R6, \T0
+    mov     \T0, 0
+    adcx    \T1, \T0      // \T1 in range [0, 2^64-1]
+    adox    \R7, \T0      // \R7 in range [0, 2]
+                        // defer adding \T1 to avoid overflowing earlier than necessary (we are bound by adox/adcx throughput)
+                        // Multiplication result in \R1, \R2, \R3, \R4, \R5, \R6, \R7+\T1
+
+    xor     \T0, \T0      // clear flags
+
+    mov     \QH, \K       // now use \K as a temporary while \T1 has the value to add to \R7
+
+    mulx    \K, \T0, [\pM + 0*8]
+    adcx    \R0, \T0
+    adox    \R1, \K
+
+    mulx    \K, \T0, [\pM + 1*8]
+    adcx    \R1, \T0
+    adox    \R2, \K
+
+    mulx    \K, \T0, [\pM + 2*8]
+    adcx    \R2, \T0
+    adox    \R3, \K
+
+    mulx    \K, \T0, [\pM + 3*8]
+    adcx    \R3, \T0
+    adox    \R4, \K
+
+    mulx    \K, \T0, [\pM + 4*8]
+    adcx    \R4, \T0
+    adox    \R5, \K
+
+    mulx    \K, \T0, [\pM + 5*8]
+    adcx    \R5, \T0
+    adox    \R6, \K       // This may overflow
+
+    mov     \R0, 0
+    adcx    \R6, \R0      // This may overflow
+    adox    \R7, \R0      // \R7 in range [0,3]
+
+    adc     \R7, \T1      // This may overflow
+
+    adc     \R0, \R0
+
+    mov     \K, \R1
+    imul    \K, [\pM - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]
+.endm
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulx384AsmInternal
+#endif
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulx384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx384Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+SymCryptFdefModMulMontgomeryMulx384AsmInternal: .global SymCryptFdefModMulMontgomeryMulx384AsmInternal
+
+        mov     [rsp + 72 /*MEMSLOT0*/], Q4   // save pDst
+
+        // See SymCryptFdefModMulMontgomeryMulx256Asm for the rationale behind this function!
+
+        xor     Q13, Q13  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        mulx    Q11, Q0, [Q3 + 4*8]
+        adc     Q10, Q0
+
+        mulx    Q12, Q0, [Q3 + 5*8]
+        adc     Q11, Q0
+
+        adc     Q12, Q13        // Multiplication result in Q6, Q7, Q8, Q9, Q10, Q11, Q12
+
+        // Compute K for reducing Multiplication result
+        mov     Q4, Q6
+        imul    Q4, [Q1 + SymCryptModulusInv64OffsetAmd64]
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2,  8, Q3, Q1, Q4, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 16, Q3, Q1, Q4, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 24, Q3, Q1, Q4, Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 32, Q3, Q1, Q4, Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 40, Q3, Q1, Q4, Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9
+
+        xor     Q0, Q0          // clear flags
+        mov     QH, Q4
+
+        mulx    Q5, Q0, [Q1 + 0*8]
+        adcx    Q11, Q0         // Set Cy when Q11 is non-zero
+        adox    Q12, Q5
+
+        mulx    Q5, Q0, [Q1 + 1*8]
+        adcx    Q12, Q0
+        adox    Q13, Q5
+
+        mulx    Q5, Q0, [Q1 + 2*8]
+        adcx    Q13, Q0
+        adox    Q6, Q5
+
+        mulx    Q5, Q0, [Q1 + 3*8]
+        adcx    Q6, Q0
+        adox    Q7, Q5
+
+        mulx    Q5, Q0, [Q1 + 4*8]
+        adcx    Q7, Q0
+        adox    Q8, Q5
+
+        mulx    Q5, Q0, [Q1 + 5*8]
+        adcx    Q8, Q0
+        adox    Q9, Q5          // This may overflow
+
+        mov     Q0, 0
+        adcx    Q9, Q0          // This may overflow
+        adox    Q10, Q0
+
+        adcx    Q10, Q0
+
+        // Montgomery-reduced multiplication value in (Q12, Q13, Q6, Q7, Q8, Q9, Q10), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q0, Q2, Q3, Q5, Q11, Q1), Cy = inverted subtraction borrow
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64
+
+        mov     Q0,  [Q1 + 0*8]
+        add     Q0,  Q12
+        mov     Q2,  [Q1 + 1*8]
+        adc     Q2,  Q13
+        mov     Q3,  [Q1 + 2*8]
+        adc     Q3,  Q6
+        mov     Q5,  [Q1 + 3*8]
+        adc     Q5,  Q7
+        mov     Q11, [Q1 + 4*8]
+        adc     Q11, Q8
+        mov     Q1,  [Q1 + 5*8]
+        adc     Q1,  Q9
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q10, Q10        // This is non-zero iff
+                                //      the reduced value did not fit in 384-bits; or
+                                //      the subtraction by Mod did not borrow
+                                // adc sets the ZF appropriately
+
+        cmovnz  Q12, Q0
+        cmovnz  Q13, Q2
+        cmovnz  Q6,  Q3
+        cmovnz  Q7,  Q5
+        cmovnz  Q8,  Q11
+        cmovnz  Q9,  Q1
+
+        mov     Q4, [rsp + 72 /*MEMSLOT0*/]   // restore pDst
+
+        mov     [Q4 + 0*8], Q12
+        mov     [Q4 + 1*8], Q13
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q9
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulx384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulx384Asm the same
+#define QH rdx
+#define DH edx
+#define WH dx
+#define BH dl
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q2 r10
+#define D2 r10d
+#define W2 r10w
+#define B2 r10b
+#define Q5 r11
+#define D5 r11d
+#define W5 r11w
+#define B5 r11b
+#define Q6 rsi
+#define D6 esi
+#define W6 si
+#define B6 sil
+#define Q7 rdi
+#define D7 edi
+#define W7 di
+#define B7 dil
+#define Q8 rbp
+#define D8 ebp
+#define W8 bp
+#define B8 bpl
+#define Q9 rbx
+#define D9 ebx
+#define W9 bx
+#define B9 bl
+#define Q10 r12
+#define D10 r12d
+#define W10 r12w
+#define B10 r12b
+#define Q11 r13
+#define D11 r13d
+#define W11 r13w
+#define B11 r13b
+#define Q12 r14
+#define D12 r14d
+#define W12 r14w
+#define B12 r14b
+#define Q13 r15
+#define D13 r15d
+#define W13 r15w
+#define B13 r15b
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx384Asm)
+
+.byte 0x40; push Q6
+.seh_pushreg Q6
+push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+
+.seh_endprologue
+
+mov Q2, QH
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulx384AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+pop Q6
+ret
+.seh_endproc
+#undef QH
+#undef DH
+#undef WH
+#undef BH
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+
+#endif
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/fdef_mulx.symcryptasm b/libs/symcrypt/lib/amd64/fdef_mulx.symcryptasm
new file mode 100644
index 000000000000..90a0f2441a11
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/fdef_mulx.symcryptasm
@@ -0,0 +1,2617 @@
+//
+//  fdef_mulx.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  using the bmi2 instructions mulx, adcx and adox
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+MACRO_START(ZEROREG, R)
+        xor     R,R
+MACRO_END()
+
+MACRO_START(ZEROREG_8, R0, R1, R2, R3, R4, R5, R6, R7)
+    ZEROREG R0
+    ZEROREG R1
+    ZEROREG R2
+    ZEROREG R3
+    ZEROREG R4
+    ZEROREG R5
+    ZEROREG R6
+    ZEROREG R7
+MACRO_END()
+
+MACRO_START(MULADD18, R0, R1, R2, R3, R4, R5, R6, R7, pD, pA, pB, T0, T1, QH)
+    // R0:R[7:1]:D[0] = A[7:0] * B[0] + D[0] + R[7:0]
+    // Pre: Cy = Ov = 0
+    // Post: Cy = Ov = 0
+
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mov     QH, [pB]
+    adox    R0, [pD]
+
+    mulx    T1, T0, [pA + 0 * 8]
+    adcx    R0, T0
+    adox    R1, T1
+
+    mov     [pD], R0
+
+    mulx    T1, T0, [pA + 1 * 8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1, T0, [pA + 2 * 8]
+    adcx    R2, T0
+    adox    R3, T1
+
+    mulx    T1, T0, [pA + 3 * 8]
+    adcx    R3, T0
+    adox    R4, T1
+
+    mulx    T1, T0, [pA + 4 * 8]
+    adcx    R4, T0
+    adox    R5, T1
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R5, T0
+    adox    R6, T1
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R6, T0
+    adox    R7, T1
+
+    mulx    T1, T0, [pA + 7 * 8]
+    adcx    R7, T0
+
+    mov     R0, 0
+    adox    R0, R0
+
+    adcx    R0, T1
+MACRO_END()
+
+MACRO_START(MULADD88, R0, R1, R2, R3, R4, R5, R6, R7, pD, pA, pB, T0, T1, QH)
+    // pre & post: Cy = Ov = 0
+    // R[7-0]:D[7-0] = A[7:0] * B[7:0] + R[7:0] + D[7:0]
+
+    MULADD18    R0, R1, R2, R3, R4, R5, R6, R7, pD     , pA, pB     , T0, T1, QH
+    MULADD18    R1, R2, R3, R4, R5, R6, R7, R0, pD +  8, pA, pB +  8, T0, T1, QH
+    MULADD18    R2, R3, R4, R5, R6, R7, R0, R1, pD + 16, pA, pB + 16, T0, T1, QH
+    MULADD18    R3, R4, R5, R6, R7, R0, R1, R2, pD + 24, pA, pB + 24, T0, T1, QH
+    MULADD18    R4, R5, R6, R7, R0, R1, R2, R3, pD + 32, pA, pB + 32, T0, T1, QH
+    MULADD18    R5, R6, R7, R0, R1, R2, R3, R4, pD + 40, pA, pB + 40, T0, T1, QH
+    MULADD18    R6, R7, R0, R1, R2, R3, R4, R5, pD + 48, pA, pB + 48, T0, T1, QH
+    MULADD18    R7, R0, R1, R2, R3, R4, R5, R6, pD + 56, pA, pB + 56, T0, T1, QH
+MACRO_END()
+
+
+MACRO_START(HALF_SQUARE_NODIAG8, R0, R1, R2, R3, R4, R5, R6, R7, pD, pA, T0, T1, QH)
+    // pre & post: Cy = Ov = 0
+    // R[7-0]:D[7-0] = D[7:0] + (A[0:7]^2 - \sum_{i=0}^7 (A[i] * 2^{64*i}) )/2
+    // This is the component of the square that needs to be doubled, and then the diagonals added
+
+    // Note that Dst[0] is not changed by this macro
+
+    mov     QH, [pA + 0 * 8]            // QH = A0
+    mov     R1, [pD + 1 * 8]
+    mov     R2, [pD + 2 * 8]
+    mov     R3, [pD + 3 * 8]
+    mov     R4, [pD + 4 * 8]
+    mov     R5, [pD + 5 * 8]
+    mov     R6, [pD + 6 * 8]
+    mov     R7, [pD + 7 * 8]
+    xor     R0, R0
+
+    mulx    T1, T0, [pA + 1 * 8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1, T0, [pA + 2 * 8]
+    adcx    R2, T0
+    adox    R3, T1
+
+    mulx    T1, T0, [pA + 3 * 8]
+    adcx    R3, T0
+    adox    R4, T1
+
+    mulx    T1, T0, [pA + 4 * 8]
+    adcx    R4, T0
+    adox    R5, T1
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R5, T0
+    adox    R6, T1
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R6, T0
+    adox    R7, T1
+
+    mulx    T1, T0, [pA + 7 * 8]
+    adcx    R7, T0
+    mov     [pD + 1 * 8], R1
+
+    adox    R0, R0
+    adcx    R0, T1
+    mov     [pD + 2 * 8], R2
+    mov     QH, [pA + 1 * 8]        // QH = A1
+
+    //=======
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pA + 2 * 8]
+    adcx    R3, T0
+    adox    R4, T1
+
+    mulx    T1, T0, [pA + 3 * 8]
+    adcx    R4, T0
+    adox    R5, T1
+
+    mulx    T1, T0, [pA + 4 * 8]
+    adcx    R5, T0
+    adox    R6, T1
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R6, T0
+    adox    R7, T1
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R7, T0
+    adox    R0, T1
+
+    mov     QH, [pA + 7 * 8]        // QH = A7
+    mov     R1, 0
+    mov     R2, 0
+    mov     [pD + 3 * 8], R3
+
+    mulx    T1, T0, [pA + 1 * 8]
+    adcx    R0, T0
+    adox    R1, T1                  // doesn't produce Ov as T1 <= 0xff..fe and R1=0
+
+    mulx    T1, T0, [pA + 2 * 8]
+    adcx    R1, T0
+    mov     [pD + 4 * 8], R4
+
+    adcx    R2, T1
+    mov     QH, [pA + 2 * 8]        // QH = A2
+
+    //======
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pA + 3 * 8]
+    adcx    R5, T0
+    adox    R6, T1
+
+    mulx    T1, T0, [pA + 4 * 8]
+    adcx    R6, T0
+    adox    R7, T1
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R7, T0
+    adox    R0, T1
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R0, T0
+    adox    R1, T1
+
+    mov     QH, [pA + 4 * 8]        // QH = A4
+    mov     R3, 0
+    mov     R4, 0
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1,T0, [pA + 6 * 8]
+    adcx    R2, T0
+    adox    R3, T1                  // doesn't produce Ov as T1 <= 0xff..fe and R3=0
+
+    mov     QH, [pA + 5 * 8]        // QH = A5
+    mov     [pD + 5 * 8], R5
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R3, T0
+    adcx    R4, T1
+
+    mov     QH, [pA + 3 * 8]        // QH = A3
+    mov     [pD + 6 * 8], R6
+
+    //======
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pA + 4 * 8]
+    adcx    R7, T0
+    adox    R0, T1
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R0, T0
+    adox    R1, T1
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1, T0, [pA + 7 * 8]
+    adcx    R2, T0
+    adox    R3, T1
+
+    mov     QH, [pA + 7 * 8]        // QH = A7
+    mov     R5, 0
+    mov     R6, 0
+    mov     [pD + 7 * 8], R7
+
+    mulx    T1, T0, [pA + 4 * 8]
+    adcx    R3, T0
+    adox    R4, T1
+
+    mulx    T1, T0, [pA + 5 * 8]
+    adcx    R4, T0
+    adox    R5, T1                  // doesn't produce Ov as T1 <= 0xff..fe and R5=0
+
+    mulx    T1, T0, [pA + 6 * 8]
+    adcx    R5, T0
+    adcx    R6, T1
+
+    xor     R7, R7
+MACRO_END()
+
+MACRO_START(MONTGOMERY18, R0, R1, R2, R3, R4, R5, R6, R7, modInv, pMod, pMont, T0, T1, QH)
+    // Mont[0] = (modinv * R0 mod 2^64)
+    // R0:R[7:1]:<phantom> = Mont[0] * Mod[7:0] + R[7:0]
+    // Pre: -
+    // Post: -
+
+    mov     QH, R0
+    imul    QH, modInv
+
+    // Rather than add the low half of the first mulx to R0 we can go ahead and set
+    // up the Cy flag appropriately based on R0 directly (the addition will always
+    // result in 0 by construction), so we can have the result while imul is running
+
+    // This has a small but measurable perf improvement on SKLX (~2% improvement for
+    // 512b modmul)
+    // and it seems unlikely that it can make the performance worse
+    // My best guess as to why is that allowing this to execute a few cycles early
+    // can reduce port contention when the macro is being speculatively executed
+    or      T0, -1          // Clear Cy and Ov
+    adcx    R0, T0          // Set Cy when R0 is non-zero
+    mov     R0, 0
+    mov     [pMont], QH
+
+    mulx    T1, T1, [pMod + 0 * 8]
+    adox    R1, T1
+
+    mulx    T1, T0, [pMod + 1 * 8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1, T0, [pMod + 2 * 8]
+    adcx    R2, T0
+    adox    R3, T1
+
+    mulx    T1, T0, [pMod + 3 * 8]
+    adcx    R3, T0
+    adox    R4, T1
+
+    mulx    T1, T0, [pMod + 4 * 8]
+    adcx    R4, T0
+    adox    R5, T1
+
+    mulx    T1, T0, [pMod + 5 * 8]
+    adcx    R5, T0
+    adox    R6, T1
+
+    mulx    T1, T0, [pMod + 6 * 8]
+    adcx    R6, T0
+    adox    R7, T1
+
+    mulx    T1, T0, [pMod + 7 * 8]
+    adcx    R7, T0
+
+    adcx    R0, R0
+    adox    R0, T1
+MACRO_END()
+
+MACRO_START(SYMCRYPT_SQUARE_DIAG, index, src_reg, dest_reg, T0, T1, T2, T3, QH)
+    mov     QH, [src_reg + 8 * index]
+    mov     T0, [dest_reg + 16 * index]
+    mov     T1, [dest_reg + 16 * index + 8]
+    mulx    T3, T2, QH
+    adcx    T2, T0
+    adox    T2, T0
+    adcx    T3, T1
+    adox    T3, T1
+    mov     [dest_reg + 16 * index], T2
+    mov     [dest_reg + 16 * index + 8], T3
+MACRO_END()
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawMulMulx(
+//     _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                     UINT32      nDigits1,
+//     _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                     UINT32      nDigits2,
+//     _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx), 5, 14)
+
+        shl     Q4, 6
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q4
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], D2
+
+        // First we wipe nDigits2 of the result (size of in)
+        mov     Q6, Q5
+
+        // Wipe destination for nDigit2 blocks
+        xorps   xmm0,xmm0               // Zero register for 16-byte wipes
+        mov     Q0, Q4
+
+SymCryptFdefRawMulMulxWipeLoop:
+        movaps      [Q6],xmm0
+        movaps      [Q6+16],xmm0            // Wipe 32 bytes
+        movaps      [Q6+32],xmm0            // Wipe 32 bytes
+        movaps      [Q6+48],xmm0            // Wipe 32 bytes
+        add         Q6, 64
+        sub         Q0, 64
+        jnz         SymCryptFdefRawMulMulxWipeLoop
+
+
+SymCryptFdefRawMulxOuterLoop:
+
+        ZEROREG_8   Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13      // Leaves Cy = Ov = 0
+
+SymCryptFdefRawMulMulxInnerLoop:
+
+        // Register allocation in loops:
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q2                                    temps for multiplication
+        // Q1, Q3                                    pSrc1, pSrc2 running pointers
+        // Q4                                        inner loop counter
+        // QH                                        fixed input reg for multiplication
+        // Q5                                        Destination running pointer inner loop
+        // slot0                                     nDigits2*64
+        // slot1                                     outer loop counter
+
+        MULADD88  Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q5, Q1, Q3, Q0, Q2, QH
+
+        add     Q3, 64              // Src2 ptr
+        add     Q5, 64
+
+        sub     D4, 64                            // sets Cy = Ov = 0 because 64*nDigits2 < 2^32
+        jnz     SymCryptFdefRawMulMulxInnerLoop
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q5 + 0*8], Q6
+        mov     [Q5 + 1*8], Q7
+        mov     [Q5 + 2*8], Q8
+        mov     [Q5 + 3*8], Q9
+        mov     [Q5 + 4*8], Q10
+        mov     [Q5 + 5*8], Q11
+        mov     [Q5 + 6*8], Q12
+        mov     [Q5 + 7*8], Q13
+
+        // set up for next iteration
+        // reload 64*nDigits2
+        mov     Q4, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+
+        // reset Q5 & increment
+        sub     Q5, Q4
+        add     Q5, 64
+        // reset Q3
+        sub     Q3, Q4
+
+        // update PSrc1
+        add     Q1, 64
+
+        // nDigits1 loop counter
+        mov     D2, [rsp + GET_MEMSLOT_OFFSET(slot1)]
+        sub     D2, 1                              // sets Cy = Ov = 0 because nDigits1 < 2^32 / 64
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], D2
+        jnz     SymCryptFdefRawMulxOuterLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareMulx(
+//     _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+//                                                             UINT32      nDigits,
+//     _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx), 3, 14)
+
+        // Q1 = pSrc
+        // Q2 = nDigits
+        // Q3 = pDst
+
+        // Save parameters for phase 2
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q1   // save pSrc
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], Q2   // save nDigits
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)], Q3   // save pDst
+
+        shl     Q2, 6       // nDigits * 64 = # bytes in Src to process
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)], Q2   // save # bytes in Src to process
+
+        // Wipe destination for nDigits blocks
+        xor     Q0, Q0
+        mov     Q5, Q3
+        mov     Q4, Q2
+
+SymCryptFdefRawSquareMulxWipeLoop:
+        // we use 8-byte writes as we will be reading this very soon in 8-byte chunks, and this way the store-load
+        // forwarding works
+        mov     [Q5     ], Q0
+        mov     [Q5 +  8], Q0
+        mov     [Q5 + 16], Q0
+        mov     [Q5 + 24], Q0
+        mov     [Q5 + 32], Q0
+        mov     [Q5 + 40], Q0
+        mov     [Q5 + 48], Q0
+        mov     [Q5 + 56], Q0
+        add     Q5, 64
+        sub     Q4, 64
+        jnz     SymCryptFdefRawSquareMulxWipeLoop
+
+        // Cy = Ov = 0 here because the last 'sub Q4,64' yielded 0
+
+SymCryptFdefRawSquareMulxOuterLoop:
+
+        HALF_SQUARE_NODIAG8 Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q3, Q1, Q0, Q4, QH
+
+        sub     Q2, 64
+        jz      SymCryptFdefRawSquareMulxPhase2     // end of phase 1
+
+        lea     Q5, [Q1 + 64]
+        lea     Q3, [Q3 + 64]
+
+SymCryptFdefRawSquareMulxInnerLoop:
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q4                                    temps for multiplication
+        // Q1                                        pSrc running pointer outer loop
+        // Q2                                        # bytes left in pSrc to process in the inner loop
+        // Q3                                        pDst running pointer inner loop
+        // QH                                        fixed input reg for multiplication
+        // Q5                                        pSrc running pointer inner loop
+
+        // slot0                                     pSrc (used for final pass)
+        // slot1                                     nDigits (used for final pass)
+        // slot2                                     pDst (used for final pass)
+        // slot3                                     # bytes to process from pSrc in this iteration
+
+        MULADD88    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q3, Q1, Q5, Q0, Q4, QH
+
+        add     Q3, 64
+        add     Q5, 64
+
+        sub     Q2, 64                  // Sets Cy = Ov = 0 because nDigits < 2^32 / bits_per_digit
+        jnz     SymCryptFdefRawSquareMulxInnerLoop
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 + 0*8], Q6
+        mov     [Q3 + 1*8], Q7
+        mov     [Q3 + 2*8], Q8
+        mov     [Q3 + 3*8], Q9
+        mov     [Q3 + 4*8], Q10
+        mov     [Q3 + 5*8], Q11
+        mov     [Q3 + 6*8], Q12
+        mov     [Q3 + 7*8], Q13
+
+        mov     Q2, [rsp + GET_MEMSLOT_OFFSET(slot3)]   // restore # bytes in Src to process next
+
+        add     Q1, 64                                  // Shift outer Src pointer by 1 digit
+        sub     Q3, Q2                                  // reset output ptr
+        add     Q3, 128                                 // Shift output ptr by 2 digits
+
+        sub     Q2, 64                                  // Reduce number of bytes to process by 1 digit
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)], Q2
+
+        jmp     SymCryptFdefRawSquareMulxOuterLoop
+
+
+SymCryptFdefRawSquareMulxPhase2:
+        // Cy = Ov = 0 because last 'sub Q2, 64' resulted in 0
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 +  8*8], Q6
+        mov     [Q3 +  9*8], Q7
+        mov     [Q3 + 10*8], Q8
+        mov     [Q3 + 11*8], Q9
+        mov     [Q3 + 12*8], Q10
+        mov     [Q3 + 13*8], Q11
+        mov     [Q3 + 14*8], Q12
+        mov     [Q3 + 15*8], Q13
+
+        // Compute diagonals, and add double the result so far
+
+        mov     Q1, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+        mov     Q2, [rsp + GET_MEMSLOT_OFFSET(slot1)]
+        mov     Q3, [rsp + GET_MEMSLOT_OFFSET(slot2)]
+
+        // We can't keep the carries in Cy and Ov because there is no way to do a loop counter
+        // without touching the Ov flag.
+        // So we set the Ov carry in Q0, and retain a zero in Q4
+        xor     Q0, Q0
+        xor     Q4, Q4
+
+SymCryptFdefRawSquareMulxDiagonalsLoop:
+        // Cy = carry in
+        // Q0 = carry in (1 bit)
+        // Ov = 0
+
+        // First word is different to handle the carry
+        // SYMCRYPT_SQUARE_DIAG    0, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        mov     QH, [Q1]
+        mov     Q5, [Q3]
+        mov     Q6, [Q3 + 8]
+        mulx    Q8, Q7, QH
+        adcx    Q7, Q0              // add both carries
+        adcx    Q8, Q4              // Q4 = 0 - now Cy = 0 because result of multiply <= ff..fe00..01
+
+        adcx    Q7, Q5
+        adox    Q7, Q5
+        adcx    Q8, Q6
+        adox    Q8, Q6
+        mov     [Q3], Q7
+        mov     [Q3 + 8], Q8
+
+        SYMCRYPT_SQUARE_DIAG 1, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 2, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 3, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 4, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 5, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 6, Q1, Q3, Q5, Q6, Q7, Q8, QH
+        SYMCRYPT_SQUARE_DIAG 7, Q1, Q3, Q5, Q6, Q7, Q8, QH
+
+        // Move the Ov flag into Q0
+        mov     D0, D4
+        adox    D0, D4
+
+        // There is no way to do a loop counter without overwriting the Ov flag
+        // Even the 'dec' instruction touches it, and LAHF/SAHF doesn't load/store the Ov flag.
+        // We can't push/pop efl in a function body
+
+        lea     Q1, [Q1 + 64]
+        lea     Q3, [Q3 + 128]
+        dec     Q2
+        jnz     SymCryptFdefRawSquareMulxDiagonalsLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefMontgomeryReduceMulx(
+//     _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//     _Inout_                         PUINT32                 pSrc,
+//     _Out_                           PUINT32                 pDst )
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx), 3, 14)
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q1
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], Q2
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)], Q3
+
+        mov     D0, [Q1 + SymCryptModulusNdigitsOffsetAmd64]
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)], D0
+        // CntOuter = nDigits - using first half of slot3
+
+        xor     D4, D4
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3) + 4], D4
+        // HighCarry = 0 - using second half of slot3
+
+SymCryptFdefMontgomeryReduceMulxOuterLoop:
+        // Q1 = pmMod
+        // Q2 = pSrc = tmp buffer that we will reduce
+        mov     Q6, [Q2 + 0 * 8]
+        mov     Q7, [Q2 + 1 * 8]
+        mov     Q8, [Q2 + 2 * 8]
+        mov     Q9, [Q2 + 3 * 8]
+        mov     Q10, [Q2 + 4 * 8]
+        mov     Q11, [Q2 + 5 * 8]
+        mov     Q12, [Q2 + 6 * 8]
+        mov     Q13, [Q2 + 7 * 8]
+
+        mov     Q3, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+        mov     D4, [Q1 + SymCryptModulusNdigitsOffsetAmd64]
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]      // modulus value
+
+        // Q2 = value to reduce
+        // Q6 - Q13 = Q2[0..7]
+        // Q1 = modulus value
+        // Q3 = modinv
+
+        MONTGOMERY18    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q3, Q1, Q2 + (0 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6,  Q3, Q1, Q2 + (1 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7,  Q3, Q1, Q2 + (2 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8,  Q3, Q1, Q2 + (3 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9,  Q3, Q1, Q2 + (4 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10,  Q3, Q1, Q2 + (5 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q12, Q13, Q6, Q7, Q8, Q9, Q10, Q11,  Q3, Q1, Q2 + (6 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q13, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q1, Q2 + (7 * 8), Q0, Q5, QH
+
+        // Q6 - Q13 = carry from multiply-add
+        // Q2[0..7] = Montgomery factors
+
+        mov     Q3, Q2         // factor to multiply by
+        add     Q1, 64
+        add     Q2, 64
+
+        dec     D4
+        jz      SymCryptFdefMontgomeryReduceMulxInnerLoopDone
+
+SymCryptFdefMontgomeryReduceMulxInnerLoop:
+
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q5                                    temps for multiplication
+        // Q1                                        running pointer pMod inner loop
+        // Q2                                        running pointer pSrc inner loop
+        // Q3                                        Montgomery factors for this row
+        // D4                                        loop ctr
+        // QH                                        fixed input reg for multiplication
+
+        MULADD88    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q2, Q1, Q3, Q0, Q5, QH
+            // pre & post: Cy = Ov = 0
+            // Q13..Q6:Q2[7-0] = R[7-0]:D[7-0] = A[7:0] * B[7:0] + R[7:0] + D[7:0]
+            // QH is volatile
+
+        add     Q1, 64
+        add     Q2, 64
+        dec     D4
+        jnz     SymCryptFdefMontgomeryReduceMulxInnerLoop
+
+
+SymCryptFdefMontgomeryReduceMulxInnerLoopDone:
+
+        // We have an 8-word carry here, which we need to add to the in-memory buffer and retain a carry
+        // We also saved a 1-bit carry from the previous outer loop
+        mov     D5, [rsp + GET_MEMSLOT_OFFSET(slot3) + 4]
+        // move carry into Cy flag
+        neg     D5
+
+        // We do this in separate instructions to help the instruction decoder build up a lead...
+        mov     Q0, [Q2 + 0 * 8]
+        adc     Q0, Q6
+        mov     [Q2 + 0 * 8], Q0
+
+        mov     Q5, [Q2 + 1 * 8]
+        adc     Q5, Q7
+        mov     [Q2 + 1 * 8], Q5
+
+        mov     Q0, [Q2 + 2 * 8]
+        adc     Q0, Q8
+        mov     [Q2 + 2 * 8], Q0
+
+        mov     Q5, [Q2 + 3 * 8]
+        adc     Q5, Q9
+        mov     [Q2 + 3 * 8], Q5
+
+        mov     Q0, [Q2 + 4 * 8]
+        adc     Q0, Q10
+        mov     [Q2 + 4 * 8], Q0
+
+        mov     Q5, [Q2 + 5 * 8]
+        adc     Q5, Q11
+        mov     [Q2 + 5 * 8], Q5
+
+        mov     Q0, [Q2 + 6 * 8]
+        adc     Q0, Q12
+        mov     [Q2 + 6 * 8], Q0
+
+        mov     Q5, [Q2 + 7 * 8]
+        adc     Q5, Q13
+        mov     [Q2 + 7 * 8], Q5
+
+        adc     D4, D4                // D4 = carry (D4 was previously zero)
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3) + 4], D4
+
+        mov     Q2, [rsp + GET_MEMSLOT_OFFSET(slot1)]
+        add     Q2, 64
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], Q2
+
+        mov     Q1, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+
+        mov     D0, [rsp + GET_MEMSLOT_OFFSET(slot3)]
+        dec     D0
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)], D0
+
+        jnz     SymCryptFdefMontgomeryReduceMulxOuterLoop
+
+        // D4 = output carry
+
+        mov     D6, [Q1 + SymCryptModulusNdigitsOffsetAmd64]
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]                    // modulus value
+
+        mov     Q3, [rsp + GET_MEMSLOT_OFFSET(slot2)]
+
+        // Q2 = result buffer pointer
+        // D6 = # digits
+        // Q1 = modulus value
+        // Q3 = Dst
+
+        // copy these values for the masked copy loop
+        mov     D7, D6      // nDigits
+        mov     Q8, Q2      // result buffer
+        mov     Q9, Q3      // destination pointer
+
+        // pDst = Reduction result - Modulus
+
+SymCryptFdefMontgomeryReduceMulxSubLoop:
+        mov     Q0,[Q2 + 0 * 8]
+        sbb     Q0,[Q1 + 0 * 8]
+        mov     [Q3 + 0 * 8], Q0
+
+        mov     Q5,[Q2 + 1 * 8]
+        sbb     Q5,[Q1 + 1 * 8]
+        mov     [Q3 + 1 * 8], Q5
+
+        mov     Q0,[Q2 + 2 * 8]
+        sbb     Q0,[Q1 + 2 * 8]
+        mov     [Q3 + 2 * 8], Q0
+
+        mov     Q5,[Q2 + 3 * 8]
+        sbb     Q5,[Q1 + 3 * 8]
+        mov     [Q3 + 3 * 8], Q5
+
+        mov     Q0,[Q2 + 4 * 8]
+        sbb     Q0,[Q1 + 4 * 8]
+        mov     [Q3 + 4 * 8], Q0
+
+        mov     Q5,[Q2 + 5 * 8]
+        sbb     Q5,[Q1 + 5 * 8]
+        mov     [Q3 + 5 * 8], Q5
+
+        mov     Q0,[Q2 + 6 * 8]
+        sbb     Q0,[Q1 + 6 * 8]
+        mov     [Q3 + 6 * 8], Q0
+
+        mov     Q5,[Q2 + 7 * 8]
+        sbb     Q5,[Q1 + 7 * 8]
+        mov     [Q3 + 7 * 8], Q5
+
+        lea     Q2, [Q2 + 64]
+        lea     Q1, [Q1 + 64]
+        lea     Q3, [Q3 + 64]
+        dec     D6
+        jnz     SymCryptFdefMontgomeryReduceMulxSubLoop
+
+        // now a masked copy from the reduction buffer to the destination.
+        // copy if high carry = 0 and Cy = 1
+        sbb     D4, 0
+        // D4 = copy mask, ff...ff  if copy, 0 of no copy
+
+        movd    xmm0, D4           // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+SymCryptFdefMontgomeryReduceMulxMaskedCopyLoop:
+        movdqa  xmm2, [Q8 + 0 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 0 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 0 * 16], xmm2
+
+        movdqa  xmm2, [Q8 + 1 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 1 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 1 * 16], xmm2
+
+        movdqa  xmm2, [Q8 + 2 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 2 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 2 * 16], xmm2
+
+        movdqa  xmm2, [Q8 + 3 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q9 + 3 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q9 + 3 * 16], xmm2
+
+        // Move on to the next digit
+
+        add     Q8, 64
+        add     Q9, 64
+        dec     D7
+        jnz     SymCryptFdefMontgomeryReduceMulxMaskedCopyLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx))
+
+MACRO_START(MULADD_LOADSTORE18, pS, pM, pD, QH, Tc, T0, T1)
+    // Tc:D[7:0] = (M[7:0] * K) + S[7:0] + Tc
+    //
+    // QH = K
+    // T0, T1 = scratch
+    // Tc is carry in at start and carry out at end (in [0,2^64-1])
+
+    xor     T0, T0 // clear flags
+
+    mulx    T1, T0, [pM + 0 * 8]
+    adox    T0, Tc
+    adcx    T0, [pS + 0 * 8]
+    mov     [pD + 0 * 8], T0
+
+    mulx    Tc, T0, [pM + 1 * 8]
+    adox    T0, T1
+    adcx    T0, [pS + 1 * 8]
+    mov     [pD + 1 * 8], T0
+
+    mulx    T1, T0, [pM + 2 * 8]
+    adox    T0, Tc
+    adcx    T0, [pS + 2 * 8]
+    mov     [pD + 2 * 8], T0
+
+    mulx    Tc, T0, [pM + 3 * 8]
+    adox    T0, T1
+    adcx    T0, [pS + 3 * 8]
+    mov     [pD + 3 * 8], T0
+
+    mulx    T1, T0, [pM + 4 * 8]
+    adox    T0, Tc
+    adcx    T0, [pS + 4 * 8]
+    mov     [pD + 4 * 8], T0
+
+    mulx    Tc, T0, [pM + 5 * 8]
+    adox    T0, T1
+    adcx    T0, [pS + 5 * 8]
+    mov     [pD + 5 * 8], T0
+
+    mulx    T1, T0, [pM + 6 * 8]
+    adox    T0, Tc
+    adcx    T0, [pS + 6 * 8]
+    mov     [pD + 6 * 8], T0
+
+    mulx    Tc, T0, [pM + 7 * 8]
+    adox    T0, T1
+    adcx    T0, [pS + 7 * 8]
+    mov     [pD + 7 * 8], T0
+
+    mov     T1, 0
+    adox    Tc, T1 // cannot overflow
+    adcx    Tc, T1 // cannot overflow
+MACRO_END()
+
+MACRO_START(SHIFTRIGHT2, pD, index, shrVal, shrMask, shlVal, Tc, T0, T1)
+    // T0 = D[index + 1]
+    // D[index + 1] = Tc:T0 >> shrVal (== ((T0>>shrVal)&shrMask) | (Tc<<shlVal))
+    // Tc = D[index]
+    // D[index] = T0:Tc >> shrVal
+    //
+    // shrVal in [1,64]
+    // shrMask = (shrVal==64) ? 0 : -1
+    // shlVal = 64-shrVal
+    //
+    // T0, T1 = scratch
+    // Value of Tc is bits to shift in the top at start, and bits to shift
+    // into next macro invocation at end
+
+    mov     T0, [pD + ((index+1)*8)]
+    shrx    T1, T0, shrVal
+    shlx    Tc, Tc, shlVal
+    and     T1, shrMask
+    or      T1, Tc
+    mov     [pD + ((index+1)*8)], T1
+
+    mov     Tc, [pD + (index*8)]
+    shrx    T1, Tc, shrVal
+    shlx    T0, T0, shlVal
+    and     T1, shrMask
+    or      T1, T0
+    mov     [pD + (index*8)], T1
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModDivSmallPow2Mulx(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _In_range_(1, 64)               UINT32                  exp,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Mulx), 4, 9)
+
+        mov     Q7, [Q2]
+        mov     QH, [Q1 + SymCryptModulusInv64OffsetAmd64]      // inv64
+
+        imul    QH, Q7  // QH = inv64 * peSrc[0]
+                        // this is the multiple of the modulus that must be added to peSrc
+                        // s.t. the least significant **64** bits are all zero
+
+        // Form bitmask to mask out bottom exp bits from multiplication result
+        xor     D0, D0
+        mov     Q7, -1
+        sub     D0, D3
+        shrx    Q7, Q7, Q0      // mask = -1 >> (-exp % 64)
+                                //      = 0xffffffffffffffff >> (64-exp)
+                                //      = (((UINT65)1)<<exp)-1
+
+        and     QH, Q7          // Extract the least significant exp bits from QH to produce K
+                                // K is the minimal multiple of the modulus that when added to peSrc
+                                // sets the least significant **exp** bits to zero
+
+        mov     D0, [Q1 + SymCryptModulusNdigitsOffsetAmd64]            // numDigits, used directly as loop counter
+        lea     Q5, [Q1 + SymCryptModulusValueOffsetAmd64]              // pointer to modulus value
+
+        xor     Q6, Q6 // zero one word carry
+
+SymCryptFdefModDivSmallPow2MulxMulAddLoop:
+        // Set peDst = peSrc + (K * mod)
+        // Also has up to 64b of carry out the top which is stored in Q6 at the end of the loop
+        //
+        // D0   loop ctr (one iteration per digit)
+        // QH   K
+        // Q2   running pointer peSrc inner loop (we directly modify Q2 as we only need to read peSrc once)
+        // Q5   running pointer pMod inner loop
+        // Q4   running pointer peDst inner loop (we directly modify Q4 as we read/write peDst in reverse in second loop)
+        // Q6   one word carry
+        // Q7, Q8 scratch
+
+        MULADD_LOADSTORE18 Q2, Q5, Q4, QH, Q6, Q7, Q8
+
+        // Move on to the next digit
+        add     Q2, 64
+        add     Q5, 64
+        add     Q4, 64
+
+        dec     D0
+        jnz     SymCryptFdefModDivSmallPow2MulxMulAddLoop
+
+        mov     D0, [Q1 + SymCryptModulusNdigitsOffsetAmd64]            // numDigits, used directly as loop counter
+        mov     D1, 64
+        xor     Q7, Q7
+        sub     D1, D3  // shlVal (also sets ZF iff exp == 64)
+        mov     Q2, -1
+        cmovz   Q2, Q7  // shrMask = ((exp == 64) ? 0 : -1)
+
+        sub     Q4, 64  // Q4 points to the most significant digit of peDst
+
+SymCryptFdefModDivSmallPow2MulxShiftRightLoop:
+        // Set peDst = peDst >> exp
+        // We work backwards (from MSB to LSB) so we avoid having to do special handling of the one word
+        // carry out of the top of the multiply add loop
+        //
+        // D0   loop ctr (one iteration per digit)
+        // Q1   shlVal
+        // Q2   shrMask
+        // Q3   shrVal
+        // Q4   running pointer peDst inner loop (decreasing)
+        // Q6   one word carry
+        // Q7, Q8 scratch
+
+        SHIFTRIGHT2 Q4, 6, Q3, Q2, Q1, Q6, Q7, Q8
+        SHIFTRIGHT2 Q4, 4, Q3, Q2, Q1, Q6, Q7, Q8
+        SHIFTRIGHT2 Q4, 2, Q3, Q2, Q1, Q6, Q7, Q8
+        SHIFTRIGHT2 Q4, 0, Q3, Q2, Q1, Q6, Q7, Q8
+
+        sub     Q4, 64
+
+        dec     D0
+        jnz     SymCryptFdefModDivSmallPow2MulxShiftRightLoop
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Mulx))
+
+// --------------------------------
+// 256-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAddMulx256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx256Asm), 4, 9)
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64
+
+        // compute Src1 + Src2 into (Q0, Q5, Q6, Q2), Cy = addition carry
+        // Compute sum + (-Mod) into (Q7, Q8, Q3, Q1), Ov = inverted subtraction borrow
+
+        xor     Q0, Q0 // Clear the flags
+
+        mov     Q0, [Q2 + 0*8]
+        adcx    Q0, [Q3 + 0*8]
+
+        mov     Q5, [Q2 + 1*8]
+        adcx    Q5, [Q3 + 1*8]
+
+        mov     Q6, [Q2 + 2*8]
+        adcx    Q6, [Q3 + 2*8]
+
+        mov     Q2, [Q2 + 3*8]
+        adcx    Q2, [Q3 + 3*8]
+
+        mov     Q7, [Q1 + 0*8]
+        adox    Q7, Q0
+
+        mov     Q8, [Q1 + 1*8]
+        adox    Q8, Q5
+
+        mov     Q3, [Q1 + 2*8]
+        adox    Q3, Q6
+
+        mov     Q1, [Q1 + 3*8]
+        adox    Q1, Q2
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        cmovc   Q0, Q7
+        cmovc   Q5, Q8
+        cmovc   Q6, Q3
+        cmovc   Q2, Q1
+
+        cmovo   Q0, Q7
+        cmovo   Q5, Q8
+        cmovo   Q6, Q3
+        cmovo   Q2, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q2
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx256Asm))
+
+MACRO_START(MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE, T0, T1, QH, pA, Aoff, pB, pM, K, R0, R1, R2, R3, R4, R5)
+    // (R1, R2, R3, R4, R5) = A[Aoff] * (B0..3) + (R1, R2, R3, R4, R5)
+    // (xx, R1, R2, R3, R4, R5, R0) = K * (M0..3) + (R0, R1, R2, R3, R4, R5)
+    // K = modInv * R1
+    // Note K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // R5 is 0 or 1 at start
+    // R0 is 0 or 1 at end
+    // T0, T1 = scratch
+
+    mov     QH, [pA + Aoff]
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pB + 0*8]
+    adox    R1, T0
+    adcx    R2, T1
+
+    mulx    T1, T0, [pB + 1*8]
+    adox    R2, T0
+    adcx    R3, T1
+
+    mulx    T1, T0, [pB + 2*8]
+    adox    R3, T0
+    adcx    R4, T1
+
+    mulx    T1, T0, [pB + 3*8]
+    adox    R4, T0
+    mov     T0, 0
+    adcx    T1, T0      // T1 in range [0, 2^64-1]
+
+    adox    R5, T0      // R5 in range [0, 2]
+                        // defer adding T1 to avoid overflowing earlier than necessary (we are bound by adox/adcx throughput)
+                        // Multiplication result in R1, R2, R3, R4, R5+T1
+
+    xor     T0, T0      // clear flags
+
+    mov     QH, K       // now use K as a temporary while T1 has the value to add to R5
+
+    mulx    K, T0, [pM + 0*8]
+    adcx    R0, T0      // sets R0 to 0
+    adox    R1, K
+
+    mulx    K, T0, [pM + 1*8]
+    adcx    R1, T0
+    adox    R2, K
+
+    mulx    K, T0, [pM + 2*8]
+    adcx    R2, T0
+    adox    R3, K
+
+    mulx    K, T0, [pM + 3*8]
+    adcx    R3, T0
+    adox    R4, K       // This may overflow
+
+    adcx    R4, R0      // This may overflow
+    adox    R5, R0      // R5 in range [0, 3]
+
+    adc     R5, T1      // This may overflow
+
+    adc     R0, R0
+
+    mov     K, R1
+    imul    K, [pM - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulx256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulx256AsmInternal
+#endif
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx256Asm), 4, 13)
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+ALTERNATE_ENTRY(SymCryptFdefModMulMontgomeryMulx256AsmInternal)
+
+        //
+        // SamL: We previously did small modular multiplication by doing a full integer multiplication followed by a
+        // Montgomery reduction on the full result.
+        // I observed that this approach could have an issue with dependency chains in the Montgomery reduction step,
+        // if the multiple K we need to add to the result to zero out the bottom 64b is not computed early enough we
+        // can be bound by the latency of multiplication rather than the throughput of multiplication and carry chain
+        // handling.
+        // Additionally, I wanted the new 384-bit specific code to follow a similar pattern to the new 256-bit code, and
+        // producing a full 384bx384b->768b multiplication would mean we run out of registers and have to spill in the
+        // body of the 384-bit function.
+        //
+        // The approach I have taken is to interleave the multiplication with the Montgomery reduction, so we only have
+        // to keep n+2 words in registers for accumulators in the inner processing. Additionally, for all but the
+        // Montgomery reduction of the final least-significant word, we have a word*limb multiplication (4 or 6 mulx)
+        // between issuing the computation of K and using the result.
+        //
+
+        xor     Q11, Q11  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0        // Note: this is intentionally adc as, if we issue an add here it is
+                              // liable to contend with a multiplication uop on the critical path
+                              // (adc issues on different ports to mulx/imul so does not contend)
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        adc     Q10, Q11      // Multiplication result in Q6, Q7, Q8, Q9, Q10
+
+        // Compute K for reducing Multiplication result
+        mov     Q12, Q6
+        imul    Q12, [Q1 + SymCryptModulusInv64OffsetAmd64]
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE Q0, Q5, QH, Q2,  8, Q3, Q1, Q12, Q6, Q7, Q8, Q9, Q10, Q11
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE Q0, Q5, QH, Q2, 16, Q3, Q1, Q12, Q7, Q8, Q9, Q10, Q11, Q6
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE Q0, Q5, QH, Q2, 24, Q3, Q1, Q12, Q8, Q9, Q10, Q11, Q6, Q7
+
+        xor     Q0, Q0      // clear flags
+        mov     QH, Q12
+
+        mulx    Q5, Q0, [Q1 + 0*8]
+        adcx    Q9, Q0      // Set Cy when Q9 is non-zero
+        adox    Q10, Q5
+
+        mulx    Q5, Q0, [Q1 + 1*8]
+        adcx    Q10, Q0
+        adox    Q11, Q5
+
+        mulx    Q5, Q0, [Q1 + 2*8]
+        adcx    Q11, Q0
+        adox    Q6, Q5
+
+        mulx    Q5, Q0, [Q1 + 3*8]
+        adcx    Q6, Q0
+        adox    Q7, Q5      // This may overflow
+
+        mov     Q0, 0
+        adcx    Q7, Q0      // This may overflow
+        adox    Q8, Q0
+
+        adc     Q8, Q0
+
+        // Montgomery-reduced multiplication value in (Q10, Q11, Q6, Q7, Q8), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q0, Q2, Q3, Q5), Cy = inverted subtraction borrow
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64
+
+        mov     Q0, [Q1 + 0*8]
+        add     Q0, Q10
+        mov     Q2, [Q1 + 1*8]
+        adc     Q2, Q11
+        mov     Q3, [Q1 + 2*8]
+        adc     Q3, Q6
+        mov     Q5, [Q1 + 3*8]
+        adc     Q5, Q7
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q8, Q8  // This is non-zero iff
+                        //      the reduced value did not fit in 256-bits; or
+                        //      the subtraction by Mod did not borrow
+                        // adc sets the ZF appropriately
+
+        cmovnz  Q10, Q0
+        cmovnz  Q11, Q2
+        cmovnz  Q6, Q3
+        cmovnz  Q7, Q5
+
+        mov     [Q4 + 0*8], Q10
+        mov     [Q4 + 1*8], Q11
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx256Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulx256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulx256Asm the same
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx256Asm), 4, 13)
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulx256AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx256Asm))
+
+// --------------------------------
+// 384-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAddMulx384(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx384Asm), 4, 13)
+
+        // Q1 = pmMod
+        // Q2 = peSrc1
+        // Q3 = peSrc2
+        // Q4 = peDst
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64
+
+        // compute Src1 + Src2 into (Q0, Q5, Q6, Q7, Q8, Q2), Cy = addition carry
+        // Compute sum + (-Mod) into (Q9, Q10, Q11, Q12, Q3, Q1), Ov = inverted subtraction borrow
+
+        xor     Q0, Q0  // clear flags
+
+        mov     Q0, [Q2 + 0*8]
+        adcx    Q0, [Q3 + 0*8]
+
+        mov     Q5, [Q2 + 1*8]
+        adcx    Q5, [Q3 + 1*8]
+
+        mov     Q6, [Q2 + 2*8]
+        adcx    Q6, [Q3 + 2*8]
+
+        mov     Q7, [Q2 + 3*8]
+        adcx    Q7, [Q3 + 3*8]
+
+        mov     Q8, [Q2 + 4*8]
+        adcx    Q8, [Q3 + 4*8]
+
+        mov     Q2, [Q2 + 5*8]
+        adcx    Q2, [Q3 + 5*8]
+
+        mov     Q9,  [Q1 + 0*8]
+        adox    Q9,  Q0
+
+        mov     Q10, [Q1 + 1*8]
+        adox    Q10, Q5
+
+        mov     Q11, [Q1 + 2*8]
+        adox    Q11, Q6
+
+        mov     Q12, [Q1 + 3*8]
+        adox    Q12, Q7
+
+        mov     Q3,  [Q1 + 4*8]
+        adox    Q3,  Q8
+
+        mov     Q1,  [Q1 + 5*8]
+        adox    Q1,  Q2
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        cmovc   Q0, Q9
+        cmovc   Q5, Q10
+        cmovc   Q6, Q11
+        cmovc   Q7, Q12
+        cmovc   Q8, Q3
+        cmovc   Q2, Q1
+
+        cmovo   Q0, Q9
+        cmovo   Q5, Q10
+        cmovo   Q6, Q11
+        cmovo   Q7, Q12
+        cmovo   Q8, Q3
+        cmovo   Q2, Q1
+
+        mov     [Q4 + 0*8], Q0
+        mov     [Q4 + 1*8], Q5
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q2
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModAddMulx384Asm))
+
+MACRO_START(MUL16_P384, T0, T1, QH, pA, Aoff, pB, R0, R1, R2, R3, R4, R5, R6, R7)
+    // (R0, R1, R2, R3, R4, R5, R6, R7) = A[Aoff] * (B0..5) + (R0, R1, R2, R3, R4, R5, R6)
+    // R6 in [0, 2] at start
+    // R7 in [0, 1] at end
+    // T0, T1 = scratch
+
+    mov     QH, [pA + Aoff]
+    xor     R7, R7      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pB + 0*8]
+    adcx    R0, T0
+    adox    R1, T1
+
+    mulx    T1, T0, [pB + 1*8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1, T0, [pB + 2*8]
+    adcx    R2, T0
+    adox    R3, T1
+
+    mulx    T1, T0, [pB + 3*8]
+    adcx    R3, T0
+    adox    R4, T1
+
+    mulx    T1, T0, [pB + 4*8]
+    adcx    R4, T0
+    adox    R5, T1
+
+    mulx    T1, T0, [pB + 5*8]
+    adcx    R5, T0
+    adox    R6, R7      // R6 in [0, 3]
+
+
+    adc     R6, T1      // overflow possible
+    adc     R7, R7
+MACRO_END()
+
+MACRO_START(MONT16_P384, T0, T1, QH, pM, N4, R0, R1, R2, R3, R4, R5, R6, R7)
+    // K = modInv * R0
+    // (R3, N4) = R3 - (K-N4)
+    // (R0, R1, R2, R3, N4) = K * (M0..2) + (R0, R1, R2, R3, N4)
+    // (R6, R7) = K + (R6, R7)
+    //
+    // Note K is chosen using pMod's montgomery inverse s.t. R0 is set to 0
+    // pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // N4 is 0 or -1 at start and end
+    // R7 in [0, 1] at start and in [0, 2] at end
+    // R0 is 0 at end
+    // T0, T1 = scratch
+    //
+    // P384 has a modulus with a form which makes it possible to perform Montgomery reduction
+    // in an interesting way. The modulus is:
+    // 2^384 - 2^128 - 2^96 + 2^32 - 1
+    // This means when we want to add a multiple of the Modulus in reduction, we can consider
+    // not performing the full multiplication, but taking shortcuts.
+    // It is hard to get much benefit from this, as doing the full multiplication of
+    // 1 digit * 6 digits takes approximately 6 cycles with mulx, adox and adcx
+    //
+    // The approach taken here notes that K*M will always add 2^384 * K to the value to reduce
+    // with a subtraction of a value < 2^256.
+    // On entry to this macro we have N4 representing a deferred borrow (value 0 or -1) from the
+    // 4th word of the last macro (now aligned to the 3rd word of this macro), on exit we set N4
+    // to be the borrow to defer to the next round.
+    //
+    //            ____ ____ ____ ____ ____ ____
+    //           | M5 | M4 | M3 | M2 | M1 | M0 |
+    //           | ff | ff | ff | fe | f0 | 0f | (P384 modulus if you sign extend each nibble to 32b)
+    //           |____|____|____|____|____|____|
+    //  ____ ____ ____ ____ ____ ____ ____ ____
+    // |    |    |\  /|\  /|    |    |    |    |
+    // | R7 | R6 | R5 | R4 | R3 | R2 | R1 | R0 |
+    // |____|____|/__\|/__\|____|____|____|____|
+    //                                      | x modInv (0x0000000100000001)
+    //                      ____           _v__
+    //                     |    |         |    |
+    //                   + |N4in|         |  K |
+    //                     |____|         |____|
+    //       ____           ____
+    //      |    |         |    |
+    //    + |  K |       - |  K | This row is equivalent to multiplying K by (M3..5)
+    //      |____|     ____|____| once the borrow is propagated
+    //                |    |
+    //                | N4 |
+    //                |____|     ____ ____ ____
+    //                          |M2*K|M1*K|M0*K|
+    //                        + | lo | lo | lo |
+    //                      ____|____|____|____|
+    //                     |M2*K|M1*K|M0*K|
+    //                   + | hi | hi | hi |
+    //                     |____|____|____|
+    //
+    // It is tempting to avoid the multiplication of K by M2, and subtract 2*K from R2 instead, but the
+    // deferred subtraction (N3) then can be in the range [0,-2], and it is no longer possible to do the
+    // subtraction using the borrow flag. It ends up being more expensive than just doing another multiplication
+    // in my testing.
+    // It is also tempting to compute K using a shift and add, rather than imul, but experimentally, this
+    // performs worse.
+    //
+
+    mov     QH, R0
+    imul    QH, [pM - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]
+
+    add     N4, -1
+    sbb     R3, QH
+    sbb     N4, N4      // sets N4 to 0 if no borrow, or -1 if there is a borrow
+
+    xor     T0, T0      // clear flags
+
+    mulx    T1, T0, [pM + 0*8]
+    adcx    R0, T0      // sets R0 to 0
+    adox    R1, T1
+
+    mulx    T1, T0, [pM + 1*8]
+    adcx    R1, T0
+    adox    R2, T1
+
+    mulx    T1, T0, [pM + 2*8]
+    adcx    R2, T0
+    adox    R3, T1
+
+    adcx    R3, R0
+    adox    N4, R0
+
+    adc     N4, R0      // A carry into N4 can occur iff the previous subtraction borrowed
+                        // The value of N4 is guaranteed to be 0 or -1 here
+
+    add     R6, QH
+    adc     R7, R0
+
+MACRO_END()
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulxP384AsmInternal
+#endif
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulxP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP384Asm), 4, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+ALTERNATE_ENTRY(SymCryptFdefModMulMontgomeryMulxP384AsmInternal)
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q4   // save pDst
+
+        // SamL: In optimizing for P384 specifically, we need to keep an extra register
+        // free for deferring a borrow (N4 in the MONT16_P384 macro). This means that it
+        // is not possible to do the INTERLEAVE approach taken in the other small Mulx
+        // multiplication variants (and described in SymCryptFdefModMulMontgomeryMulx256Asm),
+        // as there is too much register pressure.
+        // Although we reintroduce possible dependency chain issues with the generation and
+        // use of K for the Montgomery reduction, we generate fewer total micro-ops and the
+        // overall performance is a few % better at the time of writing.
+
+        xor     Q13, Q13  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0        // Note: this is intentionally adc as, if we issue an add here it is
+                              // liable to contend with a multiplication uop on the critical path
+                              // (adc issues on different ports to mulx/imul so does not contend)
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        mulx    Q11, Q0, [Q3 + 4*8]
+        adc     Q10, Q0
+
+        mulx    Q12, Q0, [Q3 + 5*8]
+        adc     Q11, Q0
+
+        adc     Q12, Q13        // Multiplication result in Q6, Q7, Q8, Q9, Q10, Q11, Q12
+        add     Q1, SymCryptModulusValueOffsetAmd64
+        xor     Q4, Q4
+
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        MUL16_P384      Q0, Q5, QH, Q2,  8, Q3, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6
+
+        MUL16_P384      Q0, Q5, QH, Q2, 16, Q3, Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7
+
+        MUL16_P384      Q0, Q5, QH, Q2, 24, Q3, Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8
+
+        MUL16_P384      Q0, Q5, QH, Q2, 32, Q3, Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9
+
+        MUL16_P384      Q0, Q5, QH, Q2, 40, Q3, Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10
+        MONT16_P384     Q0, Q5, QH, Q1, Q4,     Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10
+
+        xor     Q0, Q0
+
+        adox    Q7, Q4  // Q4 is 0 or -1
+                        // Adding it to Q7..Q10 sign extends it correctly; a carry represents an inverted borrow
+        adox    Q8, Q4
+        adox    Q9, Q4
+        adox    Q10, Q4
+
+        // Montgomery-reduced multiplication value in (Q12, Q13, Q6, Q7, Q8, Q9, Q10), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q2, Q3, Q5, Q0, Q1, Q11), Cy = inverted subtraction borrow
+        // (-Mod) is (0xffffffff00000001, 0x00000000ffffffff, 1, 0, 0, 0)
+
+        mov     Q2,  [Q1 + SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64 + 0*8]
+        add     Q2,  Q12
+        mov     Q3,  [Q1 + 0*8] // Modulus word 0 is equal to Negated Modulus word 1
+        adc     Q3,  Q13
+        mov     Q5,  1  // Don't need to load 1 and 0 values from memory - we statically know these values for P384
+        adc     Q5,  Q6
+        mov     Q1,  Q0 // Q0 and Q11 are already zero, set Q1 to zero too
+        adc     Q0,  Q7
+        adc     Q1,  Q8
+        adc     Q11, Q9
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q10, Q10        // This is non-zero iff
+                                //      the reduced value did not fit in 384-bits; or
+                                //      the subtraction by Mod did not borrow
+                                // adc sets the ZF appropriately
+
+        cmovnz  Q12, Q2
+        cmovnz  Q13, Q3
+        cmovnz  Q6,  Q5
+        cmovnz  Q7,  Q0
+        cmovnz  Q8,  Q1
+        cmovnz  Q9,  Q11
+
+        mov     Q4, [rsp + GET_MEMSLOT_OFFSET(slot0)]   // restore pDst
+
+        mov     [Q4 + 0*8], Q12
+        mov     [Q4 + 1*8], Q13
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q9
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP384Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulxP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulx384Asm the same
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP384Asm), 4, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulxP384AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP384Asm))
+
+
+// --------------------------------
+// 1024-bit size specific functions
+// --------------------------------
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nWords1)             PCUINT32    pSrc1,
+//    _In_reads_(nWords2)             PCUINT32    pSrc2,
+//                                    UINT32      nDigits,
+//    _Out_writes_(nWords1 + nWords2) PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx1024), 4, 13)
+
+        // First we wipe nDigits of the result (size of in)
+        // Q1 = pSrc1
+        // Q2 = pSrc2
+        // Q3 = nDigits
+        // Q4 = pDst
+
+        // Wipe destination for nDigit2 blocks
+        xorps       xmm0,xmm0               // Zero register for 16-byte wipes
+
+        movaps      [Q4],xmm0
+        movaps      [Q4+16],xmm0            // Wipe 32 bytes
+        movaps      [Q4+32],xmm0            // Wipe 32 bytes
+        movaps      [Q4+48],xmm0            // Wipe 32 bytes
+
+        movaps      [Q4+64],xmm0
+        movaps      [Q4+80],xmm0            // Wipe 32 bytes
+        movaps      [Q4+96],xmm0            // Wipe 32 bytes
+        movaps      [Q4+112],xmm0           // Wipe 32 bytes
+
+        // Digit 1 from src2
+
+        ZEROREG_8   Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12      // Leaves Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q2, 64              // Src2 ptr
+        add     Q4, 64
+        xor     Q0, Q0              // sets Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q4, 64
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q4 + 0*8], Q5
+        mov     [Q4 + 1*8], Q6
+        mov     [Q4 + 2*8], Q7
+        mov     [Q4 + 3*8], Q8
+        mov     [Q4 + 4*8], Q9
+        mov     [Q4 + 5*8], Q10
+        mov     [Q4 + 6*8], Q11
+        mov     [Q4 + 7*8], Q12
+
+        // Digit 2 from src2
+
+        // set up
+
+        // Mov Q4 one digit back
+        sub     Q4, 64
+
+        // reload pSrc2
+        sub     Q2, 64
+
+        // update PSrc1
+        add     Q1, 64
+
+        ZEROREG_8   Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12      // Leaves Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q2, 64              // Src2 ptr
+        add     Q4, 64
+        xor     Q0, Q0              // sets Cy = Ov = 0
+
+        MULADD88  Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q4, Q1, Q2, Q0, Q3, QH
+
+        add     Q4, 64
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q4 + 0*8], Q5
+        mov     [Q4 + 1*8], Q6
+        mov     [Q4 + 2*8], Q7
+        mov     [Q4 + 3*8], Q8
+        mov     [Q4 + 4*8], Q9
+        mov     [Q4 + 5*8], Q10
+        mov     [Q4 + 6*8], Q11
+        mov     [Q4 + 7*8], Q12
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulMulx1024))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareMulx1024(
+//     _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+//                                                             UINT32      nDigits,
+//     _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx1024), 3, 13)
+
+        // Wipe 128 bytes of destination
+        xorps       xmm0,xmm0               // Zero register for 16-byte wipes
+
+        movaps      [Q3],xmm0
+        movaps      [Q3+16],xmm0
+        movaps      [Q3+32],xmm0
+        movaps      [Q3+48],xmm0
+
+        movaps      [Q3+64],xmm0
+        movaps      [Q3+80],xmm0
+        movaps      [Q3+96],xmm0
+        movaps      [Q3+112],xmm0
+
+        xor     Q0, Q0                      // Sets Cy = Ov = 0
+
+        HALF_SQUARE_NODIAG8 Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q1, Q0, Q2, QH
+
+        lea     Q4, [Q1 + 64]               // Q4 = pSrc + 64
+        lea     Q3, [Q3 + 64]               // Q3 = pDst + 64
+
+        // Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12        8-word carry
+        // Q0, Q2                                   temps for multiplication
+        // Q1                                       pSrc (constant)
+        // Q4                                       pSrc + 64 (constant)
+        // Q3                                       pDst running pointer
+        // QH                                       fixed input reg for multiplication
+
+        MULADD88    Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q3, Q1, Q4, Q0, Q2, QH
+
+        add     Q3, 64                      // Q3 = pDst + 128
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 + 0*8], Q5
+        mov     [Q3 + 1*8], Q6
+        mov     [Q3 + 2*8], Q7
+        mov     [Q3 + 3*8], Q8
+        mov     [Q3 + 4*8], Q9
+        mov     [Q3 + 5*8], Q10
+        mov     [Q3 + 6*8], Q11
+        mov     [Q3 + 7*8], Q12
+
+        // Q3 which is the destination pointer is shifted here by 2 digits
+
+        xor     Q0, Q0                        // Sets Cy = Ov = 0
+
+        HALF_SQUARE_NODIAG8 Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q4, Q0, Q2, QH
+
+        // Write the 8-word carry-out to the destination
+        mov     [Q3 +  8*8], Q5
+        mov     [Q3 +  9*8], Q6
+        mov     [Q3 + 10*8], Q7
+        mov     [Q3 + 11*8], Q8
+        mov     [Q3 + 12*8], Q9
+        mov     [Q3 + 13*8], Q10
+        mov     [Q3 + 14*8], Q11
+        mov     [Q3 + 15*8], Q12
+
+        // Compute diagonals, and add double the result so far
+
+        sub     Q3, 128         // Q3 = pDst - sets Cy = Ov = 0
+
+        SYMCRYPT_SQUARE_DIAG 0, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 1, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 2, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 3, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 4, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 5, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 6, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 7, Q1, Q3, Q0, Q2, Q4, Q5, QH
+
+        SYMCRYPT_SQUARE_DIAG 8, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 9, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 10, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 11, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 12, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 13, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 14, Q1, Q3, Q0, Q2, Q4, Q5, QH
+        SYMCRYPT_SQUARE_DIAG 15, Q1, Q3, Q0, Q2, Q4, Q5, QH
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareMulx1024))
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefMontgomeryReduceMulx1024(
+//     _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//     _Inout_                         PUINT32                 pSrc,
+//     _Out_                           PUINT32                 pDst )
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx1024), 3, 14)
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q3
+
+        mov     D0, 2
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], D0
+        // CntOuter = nDigits - using first half of slot3
+
+        xor     D4, D4
+        lea     Q1, [Q1 + SymCryptModulusValueOffsetAmd64]                      // modulus value
+
+SymCryptFdefMontgomeryReduceMulx1024OuterLoop:
+        // Q1 = pmMod
+        // Q2 = pSrc = tmp buffer that we will reduce
+        mov     Q6, [Q2 + 0 * 8]
+        mov     Q7, [Q2 + 1 * 8]
+        mov     Q8, [Q2 + 2 * 8]
+        mov     Q9, [Q2 + 3 * 8]
+        mov     Q10, [Q2 + 4 * 8]
+        mov     Q11, [Q2 + 5 * 8]
+        mov     Q12, [Q2 + 6 * 8]
+        mov     Q13, [Q2 + 7 * 8]
+
+        mov     Q3, [Q1 - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]    // inv64
+
+        // Q2 = value to reduce
+        // Q6 - Q13 = Q2[0..7]
+        // Q1 = modulus value
+        // Q3 = modinv
+
+        MONTGOMERY18    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q3, Q1, Q2 + (0 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6,  Q3, Q1, Q2 + (1 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7,  Q3, Q1, Q2 + (2 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8,  Q3, Q1, Q2 + (3 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9,  Q3, Q1, Q2 + (4 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q11, Q12, Q13, Q6, Q7, Q8, Q9, Q10,  Q3, Q1, Q2 + (5 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q12, Q13, Q6, Q7, Q8, Q9, Q10, Q11,  Q3, Q1, Q2 + (6 * 8), Q0, Q5, QH
+        MONTGOMERY18    Q13, Q6, Q7, Q8, Q9, Q10, Q11, Q12,  Q3, Q1, Q2 + (7 * 8), Q0, Q5, QH
+
+        // Q6 - Q13 = carry from multiply-add
+        // Q2[0..7] = Montgomery factors
+
+        mov     Q3, Q2         // factor to multiply by
+        add     Q1, 64
+        add     Q2, 64
+
+        // Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13        8-word carry
+        // Q0, Q5                                    temps for multiplication
+        // Q1                                        running pointer pMod inner loop
+        // Q2                                        running pointer pSrc inner loop
+        // Q3                                        Montgomery factors for this row
+        // D4                                        loop ctr
+        // QH                                        fixed input reg for multiplication
+
+        MULADD88    Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13,  Q2, Q1, Q3, Q0, Q5, QH
+            // pre & post: Cy = Ov = 0
+            // Q13..Q6:Q2[7-0] = R[7-0]:D[7-0] = A[7:0] * B[7:0] + R[7:0] + D[7:0]
+            // QH is volatile
+
+        add     Q1, 64
+        add     Q2, 64
+
+        // We have an 8-word carry here, which we need to add to the in-memory buffer and retain a carry
+        // We also saved a 1-bit carry from the previous outer loop in D4
+        // move carry into Cy flag
+        neg     D4
+        mov     D4, 0
+
+        // We do this in separate instructions to help the instruction decoder build up a lead...
+        mov     Q0, [Q2 + 0 * 8]
+        adc     Q0, Q6
+        mov     [Q2 + 0 * 8], Q0
+
+        mov     Q5, [Q2 + 1 * 8]
+        adc     Q5, Q7
+        mov     [Q2 + 1 * 8], Q5
+
+        mov     Q0, [Q2 + 2 * 8]
+        adc     Q0, Q8
+        mov     [Q2 + 2 * 8], Q0
+
+        mov     Q5, [Q2 + 3 * 8]
+        adc     Q5, Q9
+        mov     [Q2 + 3 * 8], Q5
+
+        mov     Q0, [Q2 + 4 * 8]
+        adc     Q0, Q10
+        mov     [Q2 + 4 * 8], Q0
+
+        mov     Q5, [Q2 + 5 * 8]
+        adc     Q5, Q11
+        mov     [Q2 + 5 * 8], Q5
+
+        mov     Q0, [Q2 + 6 * 8]
+        adc     Q0, Q12
+        mov     [Q2 + 6 * 8], Q0
+
+        mov     Q5, [Q2 + 7 * 8]
+        adc     Q5, Q13
+        mov     [Q2 + 7 * 8], Q5
+
+        adc     D4, D4                  // D4 = carry (D4 was previously zero)
+
+        sub     Q2, 64                  // Q2 = tmp buffer that we will reduce (64B are now zeroed)
+        sub     Q1, 128                 // Q1 = modulus value
+
+        mov     D0, [rsp + GET_MEMSLOT_OFFSET(slot1)]
+        sub     D0, 1
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], D0
+
+        jnz     SymCryptFdefMontgomeryReduceMulx1024OuterLoop
+
+        // D4 = output carry
+
+        mov     Q3, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+
+        // Q2 = result buffer pointer
+        // Q1 = modulus value
+        // Q3 = Dst
+
+        // pDst = Reduction result - Modulus
+
+        mov     Q0,[Q2 + 0 * 8]
+        sbb     Q0,[Q1 + 0 * 8]
+        mov     [Q3 + 0 * 8], Q0
+
+        mov     Q5,[Q2 + 1 * 8]
+        sbb     Q5,[Q1 + 1 * 8]
+        mov     [Q3 + 1 * 8], Q5
+
+        mov     Q0,[Q2 + 2 * 8]
+        sbb     Q0,[Q1 + 2 * 8]
+        mov     [Q3 + 2 * 8], Q0
+
+        mov     Q5,[Q2 + 3 * 8]
+        sbb     Q5,[Q1 + 3 * 8]
+        mov     [Q3 + 3 * 8], Q5
+
+        mov     Q0,[Q2 + 4 * 8]
+        sbb     Q0,[Q1 + 4 * 8]
+        mov     [Q3 + 4 * 8], Q0
+
+        mov     Q5,[Q2 + 5 * 8]
+        sbb     Q5,[Q1 + 5 * 8]
+        mov     [Q3 + 5 * 8], Q5
+
+        mov     Q0,[Q2 + 6 * 8]
+        sbb     Q0,[Q1 + 6 * 8]
+        mov     [Q3 + 6 * 8], Q0
+
+        mov     Q5,[Q2 + 7 * 8]
+        sbb     Q5,[Q1 + 7 * 8]
+        mov     [Q3 + 7 * 8], Q5
+
+        mov     Q0,[Q2 + 8 * 8]
+        sbb     Q0,[Q1 + 8 * 8]
+        mov     [Q3 + 8 * 8], Q0
+
+        mov     Q5,[Q2 + 9 * 8]
+        sbb     Q5,[Q1 + 9 * 8]
+        mov     [Q3 + 9 * 8], Q5
+
+        mov     Q0,[Q2 + 10 * 8]
+        sbb     Q0,[Q1 + 10 * 8]
+        mov     [Q3 + 10 * 8], Q0
+
+        mov     Q5,[Q2 + 11 * 8]
+        sbb     Q5,[Q1 + 11 * 8]
+        mov     [Q3 + 11 * 8], Q5
+
+        mov     Q0,[Q2 + 12 * 8]
+        sbb     Q0,[Q1 + 12 * 8]
+        mov     [Q3 + 12 * 8], Q0
+
+        mov     Q5,[Q2 + 13 * 8]
+        sbb     Q5,[Q1 + 13 * 8]
+        mov     [Q3 + 13 * 8], Q5
+
+        mov     Q0,[Q2 + 14 * 8]
+        sbb     Q0,[Q1 + 14 * 8]
+        mov     [Q3 + 14 * 8], Q0
+
+        mov     Q5,[Q2 + 15 * 8]
+        sbb     Q5,[Q1 + 15 * 8]
+        mov     [Q3 + 15 * 8], Q5
+
+        // now a masked copy from the reduction buffer to the destination.
+        // copy if high carry = 0 and Cy = 1
+        sbb     D4, 0
+        // D4 = copy mask, ff...ff  if copy, 0 of no copy
+
+        movd    xmm0, D4            // xmm0[0] = mask
+        pcmpeqd xmm1, xmm1          // xmm1 = ff...ff
+        pshufd  xmm0, xmm0, 0       // xmm0[0..3] = mask
+        pxor    xmm1, xmm0          // xmm1 = not Mask
+
+
+        movdqa  xmm2, [Q2 + 0 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 0 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 0 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 1 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 1 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 1 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 2 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 2 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 2 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 3 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 3 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 3 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 4 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 4 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 4 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 5 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 5 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 5 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 6 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 6 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 6 * 16], xmm2
+
+        movdqa  xmm2, [Q2 + 7 * 16]    // xmm2 = pSrc[0]
+        movdqa  xmm3, [Q3 + 7 * 16]    // xmm3 = pDst[0]
+        pand    xmm2, xmm0
+        pand    xmm3, xmm1
+        por     xmm2, xmm3
+        movdqa  [Q3 + 7 * 16], xmm2
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceMulx1024))
+
+#if 0
+//////////////////////////////////////////////////////////////
+//
+// UNUSED ASSEMBLY FROM HERE
+// Kept in case we need it later, but removed at compile time
+// to avoid inclusion in produced binaries (the linker struggles
+// to remove unused assembly functions)
+//
+//////////////////////////////////////////////////////////////
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulxP256AsmInternal
+#endif
+
+MACRO_START(MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256, T0, T1, QH, pA, Aoff, pB, pM, R0, R1, R2, R3, R4, R5)
+    // (R1, R2, R3, R4, R5) = A[Aoff] * (B0..3) + (R1, R2, R3, R4, R5)
+    // (xx, R1, R2, R3, R4, R5, R0) = R0 * (M0..3) + (R0, R1, R2, R3, R4, R5)
+    // R1 = K = modInv * R1, as modInv == 1
+    // Note K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // R5 is 0 or 1 at start
+    // R0 is 0 or 1 at end
+    // T0, T1 = scratch
+
+    mov     QH, [pA + Aoff]
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pB + 0*8]
+    adox    R1, T0
+    adcx    R2, T1
+
+    mulx    T1, T0, [pB + 1*8]
+    adox    R2, T0
+    adcx    R3, T1
+
+    mulx    T1, T0, [pB + 2*8]
+    adox    R3, T0
+    adcx    R4, T1
+
+    mulx    T1, T0, [pB + 3*8]
+    adox    R4, T0
+    mov     T0, 0
+    adcx    T1, T0      // T1 in range [0, 2^64-1]
+
+    adox    R5, T0      // R5 in range [0, 2]
+                        // defer adding T1 to avoid overflowing earlier than necessary (we are bound by adox/adcx throughput)
+                        // Multiplication result in R1, R2, R3, R4, R5+T1
+
+    xor     T0, T0      // clear flags
+
+    mov     QH, R0      // now use R0 as a temporary while T1 has the value to add to R5
+
+    adox    R1, R0
+
+    mulx    R0, T0, [pM + 1*8]
+    adcx    R1, T0
+    adox    R2, R0
+
+    mov     T0, 0       // For P256, 3rd digit of the modulus is 0
+    adcx    R2, T0
+    adox    R3, T0
+
+    mulx    R0, T0, [pM + 3*8]
+    adcx    R3, T0
+    adox    R4, R0      // This may overflow
+
+    mov     R0, 0
+    adcx    R4, R0      // This may overflow
+    adox    R5, R0      // R5 in range [0, 3]
+
+    adcx    R5, T1      // This may overflow
+
+    adcx    R0, R0
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulxP256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP256Asm), 4, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+ALTERNATE_ENTRY(SymCryptFdefModMulMontgomeryMulxP256AsmInternal)
+
+        // See SymCryptFdefModMulMontgomeryMulx256Asm for the rationale behind this function!
+
+        xor     Q11, Q11  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        adc     Q10, Q11      // Multiplication result in Q6, Q7, Q8, Q9, Q10
+
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256 Q0, Q5, QH, Q2,  8, Q3, Q1, Q6, Q7, Q8, Q9, Q10, Q11
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256 Q0, Q5, QH, Q2, 16, Q3, Q1, Q7, Q8, Q9, Q10, Q11, Q6
+
+        MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE_P256 Q0, Q5, QH, Q2, 24, Q3, Q1, Q8, Q9, Q10, Q11, Q6, Q7
+
+        xor     Q0, Q0      // clear flags
+        mov     QH, Q9
+
+        adox    Q10, Q9
+
+        mulx    Q5, Q0, [Q1 + 1*8]
+        adcx    Q10, Q0
+        adox    Q11, Q5
+
+        mov     Q0, 0       // For P256, 3rd digit of the modulus is 0
+        adcx    Q11, Q0
+        adox    Q6, Q0
+
+        mulx    Q5, Q0, [Q1 + 3*8]
+        adcx    Q6, Q0
+        adox    Q7, Q5      // This may overflow
+
+        mov     Q0, 0
+        adcx    Q7, Q0      // This may overflow
+        adox    Q8, Q0
+
+        adcx    Q8, Q0
+
+        // Montgomery-reduced multiplication value in (Q10, Q11, Q6, Q7, Q8), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q0, Q2, Q3, Q5), Cy = inverted subtraction borrow
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64
+
+        mov     Q0, 1
+        add     Q0, Q10
+        mov     Q2, [Q1 + 1*8]
+        adc     Q2, Q11
+        mov     Q3, [Q1 + 2*8]
+        adc     Q3, Q6
+        mov     Q5, [Q1 + 3*8]
+        adc     Q5, Q7
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q8, Q8  // This is non-zero iff
+                        //      the reduced value did not fit in 256-bits; or
+                        //      the subtraction by Mod did not borrow
+                        // adc sets the ZF appropriately
+
+        cmovnz  Q10, Q0
+        cmovnz  Q11, Q2
+        cmovnz  Q6, Q3
+        cmovnz  Q7, Q5
+
+        mov     [Q4 + 0*8], Q10
+        mov     [Q4 + 1*8], Q11
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulxP256Asm))
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulxP256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulxP256Asm the same
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP256Asm), 3, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulxP256AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulxP256Asm))
+
+
+MACRO_START(MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE, T0, T1, QH, pA, Aoff, pB, pM, K, R0, R1, R2, R3, R4, R5, R6, R7)
+    // (R1, R2, R3, R4, R5, R6, R7) = A[Aoff] * (B0..5) + (R1, R2, R3, R4, R5, R6, R7)
+    // (xx, R1, R2, R3, R4, R5, R6, R7, R0) = K * (M0..5) + (R0, R1, R2, R3, R4, R5, R6, R7)
+    // K = modInv * R1
+    // Note K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // R7 is 0 or 1 at start
+    // R0 is 0 or 1 at end
+    // T0, T1 = scratch
+
+    mov     QH, [pA + Aoff]
+    xor     T0, T0      // clear flags
+                        // makes following carry chains architecturally independent of previous ones
+
+    mulx    T1, T0, [pB + 0*8]
+    adox    R1, T0
+    adcx    R2, T1
+
+    mulx    T1, T0, [pB + 1*8]
+    adox    R2, T0
+    adcx    R3, T1
+
+    mulx    T1, T0, [pB + 2*8]
+    adox    R3, T0
+    adcx    R4, T1
+
+    mulx    T1, T0, [pB + 3*8]
+    adox    R4, T0
+    adcx    R5, T1
+
+    mulx    T1, T0, [pB + 4*8]
+    adox    R5, T0
+    adcx    R6, T1
+
+    mulx    T1, T0, [pB + 5*8]
+    adox    R6, T0
+    mov     T0, 0
+    adcx    T1, T0      // T1 in range [0, 2^64-1]
+    adox    R7, T0      // R7 in range [0, 2]
+                        // defer adding T1 to avoid overflowing earlier than necessary (we are bound by adox/adcx throughput)
+                        // Multiplication result in R1, R2, R3, R4, R5, R6, R7+T1
+
+    xor     T0, T0      // clear flags
+
+    mov     QH, K       // now use K as a temporary while T1 has the value to add to R7
+
+    mulx    K, T0, [pM + 0*8]
+    adcx    R0, T0
+    adox    R1, K
+
+    mulx    K, T0, [pM + 1*8]
+    adcx    R1, T0
+    adox    R2, K
+
+    mulx    K, T0, [pM + 2*8]
+    adcx    R2, T0
+    adox    R3, K
+
+    mulx    K, T0, [pM + 3*8]
+    adcx    R3, T0
+    adox    R4, K
+
+    mulx    K, T0, [pM + 4*8]
+    adcx    R4, T0
+    adox    R5, K
+
+    mulx    K, T0, [pM + 5*8]
+    adcx    R5, T0
+    adox    R6, K       // This may overflow
+
+    mov     R0, 0
+    adcx    R6, R0      // This may overflow
+    adox    R7, R0      // R7 in range [0,3]
+
+    adc     R7, T1      // This may overflow
+
+    adc     R0, R0
+
+    mov     K, R1
+    imul    K, [pM - SymCryptModulusValueOffsetAmd64 + SymCryptModulusInv64OffsetAmd64]
+MACRO_END()
+
+#if defined(SYMCRYPT_MASM)
+altentry SymCryptFdefModMulMontgomeryMulx384AsmInternal
+#endif
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryMulx384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify only 4 arguments as we never use arguments 5 and 6 (saves some prolog code in MSFT calling convention)
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx384Asm), 4, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc1
+        // Q3 = pSrc2
+        // Q4 = pDst
+
+ALTERNATE_ENTRY(SymCryptFdefModMulMontgomeryMulx384AsmInternal)
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q4   // save pDst
+
+        // See SymCryptFdefModMulMontgomeryMulx256Asm for the rationale behind this function!
+
+        xor     Q13, Q13  // clear flags
+        mov     QH, [Q2]
+
+        mulx    Q7, Q6,  [Q3 + 0*8]
+
+        mulx    Q8, Q0,  [Q3 + 1*8]
+        adc     Q7, Q0
+
+        mulx    Q9, Q0,  [Q3 + 2*8]
+        adc     Q8, Q0
+
+        mulx    Q10, Q0, [Q3 + 3*8]
+        adc     Q9, Q0
+
+        mulx    Q11, Q0, [Q3 + 4*8]
+        adc     Q10, Q0
+
+        mulx    Q12, Q0, [Q3 + 5*8]
+        adc     Q11, Q0
+
+        adc     Q12, Q13        // Multiplication result in Q6, Q7, Q8, Q9, Q10, Q11, Q12
+
+        // Compute K for reducing Multiplication result
+        mov     Q4, Q6
+        imul    Q4, [Q1 + SymCryptModulusInv64OffsetAmd64]
+        add     Q1, SymCryptModulusValueOffsetAmd64
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2,  8, Q3, Q1, Q4, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 16, Q3, Q1, Q4, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q6
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 24, Q3, Q1, Q4, Q8, Q9, Q10, Q11, Q12, Q13, Q6, Q7
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 32, Q3, Q1, Q4, Q9, Q10, Q11, Q12, Q13, Q6, Q7, Q8
+
+        MUL_AND_MONTGOMERY_REDUCE16_INTERLEAVE Q0, Q5, QH, Q2, 40, Q3, Q1, Q4, Q10, Q11, Q12, Q13, Q6, Q7, Q8, Q9
+
+        xor     Q0, Q0          // clear flags
+        mov     QH, Q4
+
+        mulx    Q5, Q0, [Q1 + 0*8]
+        adcx    Q11, Q0         // Set Cy when Q11 is non-zero
+        adox    Q12, Q5
+
+        mulx    Q5, Q0, [Q1 + 1*8]
+        adcx    Q12, Q0
+        adox    Q13, Q5
+
+        mulx    Q5, Q0, [Q1 + 2*8]
+        adcx    Q13, Q0
+        adox    Q6, Q5
+
+        mulx    Q5, Q0, [Q1 + 3*8]
+        adcx    Q6, Q0
+        adox    Q7, Q5
+
+        mulx    Q5, Q0, [Q1 + 4*8]
+        adcx    Q7, Q0
+        adox    Q8, Q5
+
+        mulx    Q5, Q0, [Q1 + 5*8]
+        adcx    Q8, Q0
+        adox    Q9, Q5          // This may overflow
+
+        mov     Q0, 0
+        adcx    Q9, Q0          // This may overflow
+        adox    Q10, Q0
+
+        adcx    Q10, Q0
+
+        // Montgomery-reduced multiplication value in (Q12, Q13, Q6, Q7, Q8, Q9, Q10), and it is less than 2*Modulus
+        // Compute value + (-Mod) into (Q0, Q2, Q3, Q5, Q11, Q1), Cy = inverted subtraction borrow
+
+        add     Q1, SymCryptNegDivisorSingleDigitOffsetAmd64 - SymCryptModulusValueOffsetAmd64
+
+        mov     Q0,  [Q1 + 0*8]
+        add     Q0,  Q12
+        mov     Q2,  [Q1 + 1*8]
+        adc     Q2,  Q13
+        mov     Q3,  [Q1 + 2*8]
+        adc     Q3,  Q6
+        mov     Q5,  [Q1 + 3*8]
+        adc     Q5,  Q7
+        mov     Q11, [Q1 + 4*8]
+        adc     Q11, Q8
+        mov     Q1,  [Q1 + 5*8]
+        adc     Q1,  Q9
+
+        // Choose between the two
+        // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+        // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+        // addition carry = 0 and subtraction borrow = 1: pick first result
+
+        adc     Q10, Q10        // This is non-zero iff
+                                //      the reduced value did not fit in 384-bits; or
+                                //      the subtraction by Mod did not borrow
+                                // adc sets the ZF appropriately
+
+        cmovnz  Q12, Q0
+        cmovnz  Q13, Q2
+        cmovnz  Q6,  Q3
+        cmovnz  Q7,  Q5
+        cmovnz  Q8,  Q11
+        cmovnz  Q9,  Q1
+
+        mov     Q4, [rsp + GET_MEMSLOT_OFFSET(slot0)]   // restore pDst
+
+        mov     [Q4 + 0*8], Q12
+        mov     [Q4 + 1*8], Q13
+        mov     [Q4 + 2*8], Q6
+        mov     [Q4 + 3*8], Q7
+        mov     [Q4 + 4*8], Q8
+        mov     [Q4 + 5*8], Q9
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryMulx384Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryMulx384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+//    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+//                                    SIZE_T                  cbScratch )
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryMulx384Asm the same
+MUL_FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx384Asm), 3, 14)
+
+        // Q1 = pMod
+        // Q2 = pSrc
+        // Q3 = pDst
+
+        mov     Q4, Q3
+        mov     Q3, Q2
+
+        // Normal code doesn't jump from the body of one function to the body of another function.
+        // Here we have ensured that our stack frames are identical, so it is safe.
+        // We just have to convince the other system components that this works...
+
+        // Use conditional jump so that stack unwinder doesn't think it is an epilogue
+        test    rsp,rsp
+        jne     SymCryptFdefModMulMontgomeryMulx384AsmInternal       // jumps always
+
+        int     3       // Dummy instruction because the debugger seems to have an off-by-one
+                        // error and still see the (wrong) epilogue when on the JNE instruction
+                        // Best guess: the debugger starts the stack trace *after* the current instruction
+
+MUL_FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryMulx384Asm))
+
+#endif
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/sha256xmm_asm.S b/libs/symcrypt/lib/amd64/sha256xmm_asm.S
new file mode 100644
index 000000000000..51c9f44b803a
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/sha256xmm_asm.S
@@ -0,0 +1,1042 @@
+//
+//  sha256xmm_asm.symcryptasm   Assembler code for SHA-256 hash function. Based on
+//  the intrinsics implementation SymCryptSha256AppendBlocks_xmm_4blocks() defined in
+//  sha256-xmm.c
+//
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+EXTERN(SymCryptSha256K:DWORD)
+EXTERN(BYTE_REVERSE_32:DWORD)
+EXTERN(XMM_PACKLOW:DWORD)
+EXTERN(XMM_PACKHIGH:DWORD)
+
+
+SET(SHA2_INPUT_BLOCK_BYTES_LOG2,    6)
+SET(SHA2_INPUT_BLOCK_BYTES,         64)
+SET(SHA2_ROUNDS,                    64)
+SET(SHA2_BYTES_PER_WORD,            4)
+SET(SHA2_SIMD_REG_SIZE,             16)
+SET(SHA2_SINGLE_BLOCK_THRESHOLD,    (3 * SHA2_INPUT_BLOCK_BYTES))   // Minimum number of message bytes required for using vectorized implementation
+
+
+//
+//  sha2common_asm.symcryptasm   Generic assembler routines used in SHA-2 implementations
+//
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+
+SET(SHA2_SIMD_LANES,                ((SHA2_SIMD_REG_SIZE) / (SHA2_BYTES_PER_WORD)))
+SET(SHA2_EXPANDED_MESSAGE_SIZE,     ((SHA2_ROUNDS) * (SHA2_SIMD_REG_SIZE)))
+
+
+// Local variables used by all SHA-2 implementations
+#define W               rsp
+#define LocalsBase      (W + SHA2_EXPANDED_MESSAGE_SIZE)
+#define numBlocks       (LocalsBase +  0 * 8)
+#define numBytesToWipe  (LocalsBase +  1 * 8)
+
+
+//
+// Load one message word (4- or 8-bytes depending on the size of register t1),
+// do the endianness transformation and store it in message buffer.
+//
+// ptr [in] : register pointing to the beginning of a message
+// ind [in] : message index within a block (ind = 0..15)
+// res [out]: output message word
+//
+.macro LOAD_MSG_WORD  ptr, res, ind
+
+        mov     \res, [\ptr + (\ind) * SHA2_BYTES_PER_WORD]
+        bswap   \res
+        mov     [W + (\ind) * SHA2_BYTES_PER_WORD], \res
+
+.endm
+
+
+//
+// Get the number of message blocks that could be processed in parallel using SIMD implementation
+//
+// cbMsg [in/out]   : message size in bytes on input, set to min(r1 / SHA2_INPUT_BLOCK_BYTES, SHA2_SIMD_LANES) on output
+// t                : temporary register
+//
+.macro GET_SIMD_BLOCK_COUNT  cbMsg, t
+
+        shr     \cbMsg, SHA2_INPUT_BLOCK_BYTES_LOG2
+        mov     \t, SHA2_SIMD_LANES
+        cmp     \cbMsg, \t
+        cmova   \cbMsg, \t
+
+.endm
+
+//
+// Get the number of processed message bytes
+//
+// The return value is a multiple of SHA2_INPUT_BLOCK_BYTES, depending
+// on the level of parallelism in the vectorized implementation and available
+// message bytes to process.
+//
+// cbMsg [in]       : message length in bytes
+// cbProcessed [out]: number of processed bytes
+// t                : temporary register
+//
+.macro GET_PROCESSED_BYTES  cbMsg, cbProcessed, t
+
+        mov     \cbProcessed, \cbMsg
+        mov     \t, SHA2_SIMD_LANES * SHA2_INPUT_BLOCK_BYTES
+        cmp     \cbProcessed, \t
+        cmova   \cbProcessed, \t
+        and     \cbProcessed, -SHA2_INPUT_BLOCK_BYTES
+
+.endm
+
+
+//
+// SHA-2 core round function that uses BMI2 instructions
+//
+// a, b, c, e, f, g [in]: hash function state
+// d, h [in/out]        : hash function state
+// rnd [in]             : round number, used for indexing into the Wk array
+// t1..t5               : temporary registers
+// Wk [in]              : register pointing to message/constant buffer
+// scale [in]           : granularity of elements in message buffer /constant array
+// c0_r1, c0_r2, c0_r3  : rotation constants for CSIGMA0 function
+// c1_r1, c1_r2, c1_r3  : rotation constants for CSIGMA1 function
+//
+.macro ROUND_T5_BMI2_V1  a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale, c0_r1, c0_r2, c0_r3, c1_r1, c1_r2, c1_r3
+
+    //                  SELECT          CSIGMA0         CSIGMA1         MAJ
+    //-------------------------------------------------------------------------------
+                                                         rorx \t5, \e, \c1_r1
+                                                         rorx \t4, \e, \c1_r2
+                        mov  \t1, \f
+                        andn \t2, \e, \g
+                                                         rorx \t3, \e, \c1_r3
+        add \h, [\Wk + (\rnd) * (\scale)]
+                        and  \t1, \e
+                                                         xor  \t5, \t4
+                        xor  \t1, \t2
+                                                         xor  \t5, \t3
+        add \h, \t1
+                                         rorx \t2, \a, \c0_r1
+                                                                        mov \t3, \b
+                                                                        mov \t4, \b
+                                         rorx \t1, \a, \c0_r2
+        add \h, \t5
+                                                                        or  \t3, \c
+                                                                        and \t4, \c
+                                                                        and \t3, \a
+                                                                        or  \t4, \t3
+        add \d, \h
+                                         xor  \t2, \t1
+                                         rorx \t5, \a, \c0_r3
+                                         xor  \t2, \t5
+        add \h, \t4
+        add \h, \t2
+
+.endm
+
+
+.macro ROUND_T5_BMI2_V2  a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale, c0_r1, c0_r2, c0_r3, c1_r1, c1_r2, c1_r3
+
+    //                  SELECT          CSIGMA0         CSIGMA1         MAJ
+    //-------------------------------------------------------------------------------
+                        mov  \t1, \f
+                        andn \t2, \e, \g
+                                                         rorx \t5, \e, \c1_r1
+                        and  \t1, \e
+                                                         rorx \t4, \e, \c1_r2
+                                                         rorx \t3, \e, \c1_r3
+        add \h, [\Wk + (\rnd) * (\scale)]
+                                                         xor \t5, \t4
+                        xor  \t1, \t2
+                                                         xor  \t5, \t3
+        add \h, \t1
+        add \h, \t5
+                                                                        mov \t3, \b
+                                         rorx \t1, \a, \c0_r2
+                                         rorx \t2, \a, \c0_r1
+                                                                        mov \t4, \b
+                                                                        or  \t3, \c
+                                                                        and \t4, \c
+        add \d, \h
+                                                                        and \t3, \a
+                                                                        or  \t4, \t3
+                                         xor  \t2, \t1
+                                         rorx \t5, \a, \c0_r3
+                                         xor  \t2, \t5
+        add \h, \t4
+        add \h, \t2
+
+.endm
+
+
+
+.macro ROUND_256  a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale
+
+    ROUND_T5_BMI2_V1 \a, \b, \c, \d, \e, \f, \g, \h, \rnd, \t1, \t2, \t3, \t4, \t5, \Wk, \scale, 2, 13, 22, 6, 11, 25
+
+.endm
+
+
+.macro ROUND_512  a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale
+
+    ROUND_T5_BMI2_V1 \a, \b, \c, \d, \e, \f, \g, \h, \rnd, \t1, \t2, \t3, \t4, \t5, \Wk, \scale, 28, 34, 39, 14, 18, 41
+
+.endm
+
+
+
+.macro SHA2_UPDATE_CV_HELPER  rcv, r0, r1, r2, r3, r4, r5, r6, r7
+
+        add \r0, [\rcv + 0 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 0 * SHA2_BYTES_PER_WORD], \r0
+        add \r1, [\rcv + 1 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 1 * SHA2_BYTES_PER_WORD], \r1
+        add \r2, [\rcv + 2 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 2 * SHA2_BYTES_PER_WORD], \r2
+        add \r3, [\rcv + 3 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 3 * SHA2_BYTES_PER_WORD], \r3
+        add \r4, [\rcv + 4 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 4 * SHA2_BYTES_PER_WORD], \r4
+        add \r5, [\rcv + 5 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 5 * SHA2_BYTES_PER_WORD], \r5
+        add \r6, [\rcv + 6 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 6 * SHA2_BYTES_PER_WORD], \r6
+        add \r7, [\rcv + 7 * SHA2_BYTES_PER_WORD]
+        mov [\rcv + 7 * SHA2_BYTES_PER_WORD], \r7
+
+.endm
+
+#define SHA256_UPDATE_CV(rcv)   SHA2_UPDATE_CV_HELPER rcv, D0, D1, D2, D3, D4, D5, D6, D7
+
+#define SHA512_UPDATE_CV(rcv)   SHA2_UPDATE_CV_HELPER rcv, Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7
+
+
+//
+//  Load message words, do the endianness transformation and transpose for one or more message blocks.
+//
+//  P [in]          : register pointing to the beginning of a message
+//  N [in]          : number of blocks ( N = 3 or 4 )
+//  t, v            : temporary registers
+//  ind             : index of which quarter of the block we're processing (ind = 0..3)
+//  kreverse        : YMM register containing the value used to do the endianness transform
+//  y1..y4  [out]   : YMM registers containing the transposed message words
+//  t1..t4          : temporary YMM registers
+.macro SHA512_MSG_LOAD_TRANSPOSE_YMM  P, N, t, v, ind, kreverse, y1, y2, y3, y4, t1, t2, t3, t4
+
+        // Make \t point to the third or the fourth block depending on the
+        // number of message blocks we have
+        mov \t, 2 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 32
+        mov \v, 3 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 32
+        cmp \N, 4
+        cmove \t, \v
+
+        // We have at least three message blocks
+        vmovdqu \y1, YMMWORD ptr [\P + 0 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 32]
+        vpshufb \y1, \y1, \kreverse
+        vmovdqu \y2, YMMWORD ptr [\P + 1 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 32]
+        vpshufb \y2, \y2, \kreverse
+        vmovdqu \y3, YMMWORD ptr [\P + 2 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 32]
+        vpshufb \y3, \y3, \kreverse
+
+        // If there are only three blocks then \t points to the third block and
+        // that block is read again, otherwise fourth block is loaded. Rereading
+        // a block when we don'\t have the fourth block is to avoid conditional
+        // loading, the value will not be used.
+        vmovdqu \y4, YMMWORD ptr [\P + \t]
+        vpshufb \y4, \y4, \kreverse
+
+        SHA512_MSG_TRANSPOSE_YMM \ind, \y1, \y2, \y3, \y4, \t1, \t2, \t3, \t4
+
+.endm
+
+//
+// Transpose message words from 4 blocks so that each YMM register contains message
+// words with the same index within a message block. This macro transforms four message words at a time,
+// hence needs to be called four times in order to transform 16 message words.
+//
+// The transformation is the same whether we have 4 or less blocks. If we have less than 4 blocks,
+// the corresponding high order lanes contain garbage, and will not be used in round processing.
+//
+// ind [in]         : index to which quarter the transformation will take place (ind = 0..3)
+// y1..y4 [in/out] : YMM registers each containing 4 message words from different blocks
+//                        (y1 = W[4 * ind + 0], y2 = W[4 * ind + 1], y3 = W[4 * ind + 2], y4 = W[4 * ind + 3])
+// t1..t4           : temporary YMM registers
+//
+.macro SHA512_MSG_TRANSPOSE_YMM  ind, y1, y2, y3, y4, t1, t2, t3, t4
+
+        vpunpcklqdq \t1, \y1, \y2
+        vpunpcklqdq \t2, \y3, \y4
+        vpunpckhqdq \t3, \y1, \y2
+        vpunpckhqdq \t4, \y3, \y4
+        vperm2i128  \y1, \t1, \t2, HEX(20)
+        vperm2i128  \y2, \t3, \t4, HEX(20)
+        vperm2i128  \y3, \t1, \t2, HEX(31)
+        vperm2i128  \y4, \t3, \t4, HEX(31)
+        vmovdqu     YMMWORD ptr [W + (\ind) * 128 + 0 * 32], \y1
+        vmovdqu     YMMWORD ptr [W + (\ind) * 128 + 1 * 32], \y2
+        vmovdqu     YMMWORD ptr [W + (\ind) * 128 + 2 * 32], \y3
+        vmovdqu     YMMWORD ptr [W + (\ind) * 128 + 3 * 32], \y4
+
+.endm
+
+
+
+//
+//  Load message words, do the endianness transformation and transpose
+//  the first 16 bytes of each message block.
+//
+//  P [in]          : register pointing to the beginning of a message
+//  N [in]          : number of blocks (N = 3 or 4)
+//  t1,t2           : temporary registers
+//  ind             : index corresponding to which quarter of the message blocks
+//                    the transformation will take place, 0 <= ind <= 3
+//  kreverse [in]   : XMM register containing the value for endianness transformation
+//
+//
+.macro SHA256_MSG_LOAD_TRANSPOSE_XMM  P, N, t1, t2, ind, kreverse, x0, x1, x2, x3, xt0, xt1, xt2, xt3
+
+        // Make \t2 point to the third or the fourth block depending on the
+        // number of message blocks we have
+        mov     \t2, 2 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 16
+        mov     \t1, 3 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 16
+        cmp     \N, 4
+        cmove   \t2, \t1
+
+        // Load from the first three message blocks
+        movdqu  \x0, XMMWORD ptr [\P + 0 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 16]
+        pshufb  \x0, \kreverse
+        movdqu  \x1, XMMWORD ptr [\P + 1 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 16]
+        pshufb  \x1, \kreverse
+        movdqu  \x2, XMMWORD ptr [\P + 2 * SHA2_INPUT_BLOCK_BYTES + (\ind) * 16]
+        pshufb  \x2, \kreverse
+
+        // If there are only three blocks then \t2 points to the third block and
+        // that block is read again, otherwise fourth block is loaded. Rereading
+        // a block when we don't have the fourth block is to avoid conditional
+        // loading, the value will not be used.
+        movdqu  \x3, XMMWORD ptr [\P + \t2]
+        pshufb  \x3, \kreverse
+
+        SHA256_MSG_TRANSPOSE_XMM \ind, \x0, \x1, \x2, \x3, \xt0, \xt1, \xt2, \xt3
+
+.endm
+
+//
+// Transpose message words from 4 blocks so that each XMM register contains message
+// words with the same index within a message block. This macro transforms four message words at a time,
+// hence needs to be called four times in order to transform 16 message words.
+//
+// The transformation is the same whether we have 4 or less blocks. If we have less than 4 blocks,
+// the corresponding high order lanes contain garbage, and will not be used in round processing.
+//
+// ind [in] : index to which quarter the transformation will take place (ind = 0..3)
+// x0..x3   : XMM registers containing the message words with same indices from different blocks
+//            for instance when ind=0, each xi contains the first 16 bytes of consecutive message blocks
+// t0..t3   : temporary registers
+//
+.macro SHA256_MSG_TRANSPOSE_XMM  ind, x0, x1, x2, x3, t0, t1, t2, t3
+
+        // \x0 = A3 A2 A1 A0
+        // \x1 = B3 B2 B1 B0
+        // \x2 = C3 C2 C1 C0
+        // \x3 = D3 D2 D1 D0
+        movdqa      \t0, \x0
+        punpckhdq   \t0, \x1  // \t0 = B3 A3 B2 A2
+        punpckldq   \x0, \x1  // \x0 = B1 A1 B0 A0
+        movdqa      \t1, \x2
+        punpckhdq   \t1, \x3  // \t1 = D3 C3 D2 C2
+        punpckldq   \x2, \x3  // \x2 = D1 C1 D0 C0
+
+        movdqa      \x1, \x0
+        punpckhqdq  \x1, \x2  // \x1 = D1 C1 B1 A1
+        punpcklqdq  \x0, \x2  // \x0 = D0 C0 B0 A0
+        movdqa      XMMWORD ptr [W + 64 * (\ind) + 0 * 16], \x0
+        movdqa      XMMWORD ptr [W + 64 * (\ind) + 1 * 16], \x1
+
+        movdqa      \x3, \t0
+        punpckhqdq  \x3, \t1  // \x3 = D3 C3 B3 A3
+        punpcklqdq  \t0, \t1  // \t0 = D2 C2 B2 A2
+        movdqa      XMMWORD ptr [W + 64 * (\ind) + 2 * 16], \t0
+        movdqa      XMMWORD ptr [W + 64 * (\ind) + 3 * 16], \x3
+
+.endm
+
+
+//
+// Rotate each 32-bit value in an XMM register
+//
+// x [in]   : XMM register holding four 32-bit integers
+// c [in]   : rotation count
+// res [out]: XMM register holding the rotated values
+// t1       : temporary XMM register
+//
+.macro ROR32_XMM  x, c, res, t1
+
+    movdqa  \res, \x
+    movdqa  \t1, \x
+    psrld   \res, \c
+    pslld   \t1, 32 - \c
+    pxor    \res, \t1
+
+.endm
+
+
+//
+// LSIGMA function as defined in FIPS 180-4 acting on four parallel 32-bit values in a XMM register.
+//
+// x [in]       : XMM register holding four 32-bit integers
+// c1..c3 [in]  : rotation and shift counts
+// res [out]    : output of the LSIGMA function as four 32-bit integer values
+// t1,t2        : temporary XMM registers
+//
+.macro LSIGMA_XMM  x, c1, c2, c3, res, t1, t2
+
+        ROR32_XMM   \x, \c1, \res, \t1
+        ROR32_XMM   \x, \c2, \t2, \t1
+        movdqa      \t1, \x
+        psrld       \t1, \c3
+        pxor        \res, \t2
+        pxor        \res, \t1
+
+.endm
+
+
+
+//
+// Message expansion for 4 consecutive message blocks and adds constants to round (rnd - 16)
+//
+// y0 [in/out]      : W_{rnd - 16}, on macro exit it is loaded with W_{rnd - 14} and used as y1 for the
+//                    subsequent macro invocation.
+// y1 [in]          : W_{rnd - 15}
+// y9 [in]          : W_{rnd - 7}
+// y14 [in]         : W_{rnd - 2}
+// rnd [in]         : round number, rnd = 16..24, uses the previous 16 message word state to generate the next one
+// t1 [out]         : expanded message word
+// t2..t6           : temporary XMM registers
+// Wx [in]          : pointer to the message buffer
+// k256 [in]        : pointer to the constants
+//
+.macro SHA256_MSG_EXPAND_4BLOCKS  y0, y1, y9, y14, rnd, t1, t2, t3, t4, t5, t6, Wx, k256
+
+        movd        \t1, DWORD ptr [\k256 + 4 * (\rnd - 16)]
+        pshufd      \t1, \t1, 0
+        paddd       \t1, \y0                                      // \t1 = W_{t-16} + K_{t-16}
+        movdqa      XMMWORD ptr [\Wx + (\rnd - 16) * 16], \t1      // store W_{t-16} + K_{t-16}
+
+        LSIGMA_XMM  \y14, 17, 19, 10, \t4, \t5, \t3                 // \t4 = LSIGMA1(W_{t-2})
+        LSIGMA_XMM  \y1,   7, 18,  3, \t1, \t5, \t3                 // \t1 = LSIGMA0(W_{t-15})
+        paddd       \y0, \y9                                      // \y0 = W_{t-16} + W_{t-7}
+        paddd       \t1, \y0                                      // \t1 = W_{t-16} + W_{t-7} + LSIGMA0(W_{t-15})
+        paddd       \t1, \t4                                      // \t1 = W_{t-16} + W_{t-7} + LSIGMA0(W_{t-15}) + LSIGMA1(W_{t-2})
+        movdqa      \y0, XMMWORD ptr [\Wx + (\rnd - 14) * 16]      // \y0 = W_{t-14}, load W_{t-15} for next round
+        movdqa      XMMWORD ptr [\Wx + (\rnd) * 16], \t1           // store W_t
+
+.endm
+
+
+//
+// Single block message expansion using XMM registers
+//
+// x0..x3 [in/out]  : 16 word message state
+// t1..t6           : temporary XMM registers
+// karr             : pointer to the round constants
+// ind              : index used to calculate the offsets for loading constants and storing words to
+//                    message buffer W, each increment points to next 4 round constant and message words.
+// packlow, packhigh: constants for shuffling words and clearing top/bottom halves of an XMM register
+//
+.macro SHA256_MSG_EXPAND_1BLOCK  x0, x1, x2, x3, t1, t2, t3, t4, t5, t6, karr, ind, packlow, packhigh
+
+        // Message word state before the expansion
+        // \x0 =  W3  W2  W1  W0
+        // \x1 =  W7  W6  W5  W4
+        // \x2 = W11 W10  W9  W8
+        // \x3 = W15 W14 W13 W12
+
+        // After the expansion we will have
+        // \x1 =  W7  W6  W5  W4
+        // \x2 = W11 W10  W9  W8
+        // \x3 = W15 W14 W13 W12
+        // \x0 = W19 W18 W17 W16
+
+        movdqa      \t2, \x1
+        palignr     \t2, \x0, 4                           // \t2 = W4 W3 W2 W1
+
+        // LSIGMA1(W15 W14)
+        pshufd      \t1, \x3, HEX(0fa)                    // \t1 = W15 W15 W14 W14
+        movdqa      \t5, \t1
+        movdqa      \t3, \t1
+        psrlq       \t5, 17
+        psrlq       \t3, 19
+        pxor        \t5, \t3
+        psrld       \t1, 10
+        pxor        \t5, \t1
+        pshufb      \t5, \packlow                         // \t5 = 0 0 LSIGMA1(W15 W14)
+
+        LSIGMA_XMM  \t2, 7, 18, 3, \t3, \t1, \t6            // \t3 = LSIGMA0(W4 W3 W2 W1)
+
+        paddd       \x0, \t5                              // \x0 = (W3 W2 W1 W0) + (0 0 LSIGMA1(W15 W14))
+
+        movdqa      \t4, \x3
+        palignr     \t4, \x2, 4                           // \t4 = W12 W11 W10 W9
+
+        paddd       \t4, \t3                              // \t4 = (W12 W11 W10 W9) + LSIGMA0(W4 W3 W2 W1)
+        paddd       \x0, \t4                              // \x0 = (W3 W2 W1 W0) + (0 0 LSIGMA1(W15 W14)) + (W12 W11 W10 W9) + LSIGMA0(W4 W3 W2 W1)
+
+        // LSIGMA1(W17 W16)
+        pshufd      \t1, \x0, HEX(50)                     // \t1 = W17 W17 W16 W16
+        movdqa      \t2, \t1
+        movdqa      \t3, \t1
+        psrlq       \t2, 17
+        psrlq       \t3, 19
+        pxor        \t2, \t3
+        psrld       \t1, 10
+        pxor        \t2, \t1
+        pshufb      \t2, \packhigh                        // \t2 = LSIGMA1(W17 W16) 0 0
+
+        movdqa      \t6, XMMWORD ptr [\karr + \ind * 16]   // \t6 = K19 K18 K17 K16
+        paddd       \x0, \t2                              // \x0 = W19 W18 W17 W16
+        paddd       \t6, \x0                              // \t6 = (K19 K18 K17 K16) + (W19 W18 W17 W16)
+        movdqa      XMMWORD ptr [rsp + \ind * 16], \t6
+
+.endm
+
+
+//
+// Add one set of constants to eight message words from multiple blocks in an XMM register
+//
+// rnd [in]     : round index, rnd = 0..7 (Wx and k256 are adjusted so that this macro always acts on the next 8 rounds)
+// t1, t2       : temporary XMM registers
+// Wx [in]      : pointer to the message buffer
+// k256 [in]    : pointer to the constants array
+//
+.macro SHA256_MSG_ADD_CONST  rnd, t1, t2, Wx, k256
+
+        movd    \t1, DWORD ptr [\k256 + 4 * (\rnd)]
+        pshufd  \t1, \t1, 0
+        movdqa  \t2, XMMWORD ptr [\Wx + 16 * (\rnd)]
+        paddd   \t1, \t2
+        movdqa XMMWORD ptr [\Wx + (\rnd) * 16], \t1
+
+.endm
+
+
+
+
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptSha256AppendBlocks(
+//    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE* pChain,
+//    _In_reads_(cbData)      PCBYTE                          pbData,
+//                            SIZE_T                          cbData,
+//    _Out_                   SIZE_T*                         pcbRemaining)
+
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+#define Q4 r9
+#define D4 r9d
+#define W4 r9w
+#define B4 r9b
+#define Q5 r10
+#define D5 r10d
+#define W5 r10w
+#define B5 r10b
+#define Q6 r11
+#define D6 r11d
+#define W6 r11w
+#define B6 r11b
+#define Q7 rsi
+#define D7 esi
+#define W7 si
+#define B7 sil
+#define Q8 rdi
+#define D8 edi
+#define W8 di
+#define B8 dil
+#define Q9 rbp
+#define D9 ebp
+#define W9 bp
+#define B9 bpl
+#define Q10 rbx
+#define D10 ebx
+#define W10 bx
+#define B10 bl
+#define Q11 r12
+#define D11 r12d
+#define W11 r12w
+#define B11 r12b
+#define Q12 r13
+#define D12 r13d
+#define W12 r13w
+#define B12 r13b
+#define Q13 r14
+#define D13 r14d
+#define W13 r14w
+#define B13 r14b
+#define Q14 r15
+#define D14 r15d
+#define W14 r15w
+#define B14 r15b
+PLATFORM_NAME_MANGLE(SymCryptSha256AppendBlocks_xmm_ssse3_asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptSha256AppendBlocks_xmm_ssse3_asm)
+.def PLATFORM_NAME_MANGLE(SymCryptSha256AppendBlocks_xmm_ssse3_asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptSha256AppendBlocks_xmm_ssse3_asm)
+
+.byte 0x40; push Q7
+.seh_pushreg Q7
+push Q8
+.seh_pushreg Q8
+push Q9
+.seh_pushreg Q9
+push Q10
+.seh_pushreg Q10
+push Q11
+.seh_pushreg Q11
+push Q12
+.seh_pushreg Q12
+push Q13
+.seh_pushreg Q13
+push Q14
+.seh_pushreg Q14
+sub rsp, 1208
+.seh_stackalloc 1208
+movdqa xmmword ptr [rsp + 1040], xmm6
+.seh_savexmm xmm6, 1040
+movdqa xmmword ptr [rsp + 1056], xmm7
+.seh_savexmm xmm7, 1056
+movdqa xmmword ptr [rsp + 1072], xmm8
+.seh_savexmm xmm8, 1072
+movdqa xmmword ptr [rsp + 1088], xmm9
+.seh_savexmm xmm9, 1088
+movdqa xmmword ptr [rsp + 1104], xmm10
+.seh_savexmm xmm10, 1104
+movdqa xmmword ptr [rsp + 1120], xmm11
+.seh_savexmm xmm11, 1120
+movdqa xmmword ptr [rsp + 1136], xmm12
+.seh_savexmm xmm12, 1136
+movdqa xmmword ptr [rsp + 1152], xmm13
+.seh_savexmm xmm13, 1152
+movdqa xmmword ptr [rsp + 1168], xmm14
+.seh_savexmm xmm14, 1168
+movdqa xmmword ptr [rsp + 1184], xmm15
+.seh_savexmm xmm15, 1184
+
+.seh_endprologue
+
+
+        // Q1 = pChain
+        // Q2 = pbData
+        // Q3 = cbData
+        // Q4 = pcbRemaining
+
+        mov     [rsp + 1280 /*MEMSLOT0*/], Q1
+        mov     [rsp + 1288 /*MEMSLOT1*/], Q2
+        mov     [rsp + 1296 /*MEMSLOT2*/], Q3
+        mov     [rsp + 1304 /*MEMSLOT3*/], Q4
+
+
+        // We have two implementations using different message buffer sizes. The code below checks the
+        // input message size and helps avoid wiping the larger buffer if we're not using it.
+        //
+        // If we're processing SHA2_SINGLE_BLOCK_THRESHOLD or more bytes, vectorized message expansion is
+        // used, which expands the message words for 4 blocks. Message expansion for single block processing
+        // uses a buffer of 16 message words. Both buffers start at address W (rsp).
+        //
+        // numBytesToWipe variable holds the number of bytes to wipe from the expanded message buffer
+        // before returning from this call.
+        //
+        // Q3 [in]  : cbData
+        // D8 [out] : numBytesToWipe
+        mov     D8, 16 * SHA2_BYTES_PER_WORD
+        mov     D9, SHA2_EXPANDED_MESSAGE_SIZE
+        cmp     Q3, SHA2_SINGLE_BLOCK_THRESHOLD
+        cmovae  D8, D9
+        mov     [numBytesToWipe], D8
+
+        mov     Q10, Q1
+        mov     D0, DWORD ptr [Q10 +  0]
+        mov     D1, DWORD ptr [Q10 +  4]
+        mov     D2, DWORD ptr [Q10 +  8]
+        mov     D3, DWORD ptr [Q10 + 12]
+        mov     D4, DWORD ptr [Q10 + 16]
+        mov     D5, DWORD ptr [Q10 + 20]
+        mov     D6, DWORD ptr [Q10 + 24]
+        mov     D7, DWORD ptr [Q10 + 28]
+
+        // If message size is less than SHA2_SINGLE_BLOCK_THRESHOLD then use single block message expansion,
+        // otherwise use vectorized message expansion.
+        mov     Q8, [rsp + 1296 /*MEMSLOT2*/]
+        cmp     Q8, SHA2_SINGLE_BLOCK_THRESHOLD
+        jb      single_block_entry
+
+        ALIGN(16)
+process_blocks:
+        // Calculate the number of blocks to process, Q8 = cbData
+        GET_SIMD_BLOCK_COUNT Q8, Q9     // Q8 = min(cbData / 64, 4)
+        mov     [numBlocks], Q8
+
+        //
+        // Load and transpose message words
+        //
+        mov Q9, [rsp + 1288 /*MEMSLOT1*/]
+        movdqa xmm8, XMMWORD ptr [GET_SYMBOL_ADDRESS(BYTE_REVERSE_32)]
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 0, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 1, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 2, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 3, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+
+        // Load transposed message words to registers to be used in message expansion
+        // Each register contains message words (of the same index within a block) from multiple blocks
+        movdqa xmm0, XMMWORD ptr [W + 16 *  0]
+        movdqa xmm1, XMMWORD ptr [W + 16 *  1]
+        movdqa xmm2, XMMWORD ptr [W + 16 *  9]
+        movdqa xmm3, XMMWORD ptr [W + 16 * 10]
+        movdqa xmm4, XMMWORD ptr [W + 16 * 11]
+        movdqa xmm5, XMMWORD ptr [W + 16 * 12]
+        movdqa xmm6, XMMWORD ptr [W + 16 * 13]
+        movdqa xmm7, XMMWORD ptr [W + 16 * 14]
+        movdqa xmm8, XMMWORD ptr [W + 16 * 15]
+
+        lea     Q13, [W]                                    // pointer to expanded message words
+        lea     Q14, [GET_SYMBOL_ADDRESS(SymCryptSha256K)]  // pointer to constants
+
+expand_process_first_block:
+
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm2, xmm7, (16 + 0), xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm3, xmm8, (16 + 1), xmm2, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm4, xmm9, (16 + 2), xmm3, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm5, xmm2, (16 + 3), xmm4, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm6, xmm3, (16 + 4), xmm5, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm7, xmm4, (16 + 5), xmm6, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm8, xmm5, (16 + 6), xmm7, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm9, xmm6, (16 + 7), xmm8, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7, 0, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6, 1, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5, 2, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4, 3, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3, 4, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2, 5, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1, 6, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0, 7, D8, D9, D10, D11, D12, Q13, 16
+
+        lea     Q8, [GET_SYMBOL_ADDRESS(SymCryptSha256K) + 48 * 4]
+        add     Q13, 8 * 16 // next message words
+        add     Q14, 8 * 4  // next constants
+        cmp     Q14, Q8
+        jb      expand_process_first_block
+
+        // Final 16 rounds
+final_rounds:
+        SHA256_MSG_ADD_CONST 0, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 1, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 2, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 3, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 4, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 5, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 6, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 7, xmm1, xmm2, Q13, Q14
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7, 0, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6, 1, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5, 2, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4, 3, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3, 4, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2, 5, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1, 6, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0, 7, D8, D9, D10, D11, D12, Q13, 16
+
+        lea     Q8, [GET_SYMBOL_ADDRESS(SymCryptSha256K) + 64 * 4]
+        add     Q13, 8 * 16 // next message words
+        add     Q14, 8 * 4  // next constants
+        cmp     Q14, Q8
+        jb      final_rounds
+
+        mov     Q8, [rsp + 1280 /*MEMSLOT0*/]
+        SHA256_UPDATE_CV(Q8)
+
+        // We've processed one block, update the variable.
+        // Note: We always have more than one block, no need to check the result of the decrement.
+        dec qword ptr [numBlocks]
+
+        lea     Q13, [W + 4]    // second message block words
+
+block_begin:
+
+        mov     D14, 64 / 8
+
+        ALIGN(16)
+inner_loop:
+        //                                                                      Wk  scale
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  0, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  1, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5,  2, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4,  3, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3,  4, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2,  5, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1,  6, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0,  7, D8, D9, D10, D11, D12, Q13, 16
+
+        add     Q13, 8 * 16 // advance to next 8 message words
+        sub     D14, 1
+        jnz     inner_loop
+
+        add     Q13, (4 - 64 * 16)  // advance to the beginning of message words for the next block
+
+        mov     Q8, [rsp + 1280 /*MEMSLOT0*/]
+        SHA256_UPDATE_CV(Q8)
+
+        dec     QWORD ptr [numBlocks]
+        jnz     block_begin
+
+        // Update pbData and cbData
+        mov     Q8, [rsp + 1296 /*MEMSLOT2*/]
+        GET_PROCESSED_BYTES Q8, Q9, Q10     // Q9 = bytesProcessed
+        sub     Q8, Q9
+        add     QWORD ptr [rsp + 1288 /*MEMSLOT1*/], Q9
+        mov     QWORD ptr [rsp + 1296 /*MEMSLOT2*/], Q8
+        cmp     Q8, SHA2_SINGLE_BLOCK_THRESHOLD
+        jae     process_blocks
+
+
+        ALIGN(16)
+single_block_entry:
+
+        cmp     Q8, SHA2_INPUT_BLOCK_BYTES      // Q8 = cbData
+        jb      done
+
+        // Load the constants once before the block processing loop begins
+        // These registers are not touched during block processing
+        movdqa  xmm13, XMMWORD ptr [GET_SYMBOL_ADDRESS(BYTE_REVERSE_32)]
+        movdqa  xmm14, XMMWORD ptr [GET_SYMBOL_ADDRESS(XMM_PACKLOW)]
+        movdqa  xmm15, XMMWORD ptr [GET_SYMBOL_ADDRESS(XMM_PACKHIGH)]
+
+single_block_start:
+
+        mov     Q13, [rsp + 1288 /*MEMSLOT1*/]
+        lea     Q14, [GET_SYMBOL_ADDRESS(SymCryptSha256K)]
+
+        //
+        // Load first 16 message words into xmm0..xmm3 and do the endianness transformation
+        // Store the constant added words to message buffer W
+        //
+        movdqu  xmm0, XMMWORD ptr [Q13 + 0 * 16]
+        movdqu  xmm1, XMMWORD ptr [Q13 + 1 * 16]
+        movdqu  xmm2, XMMWORD ptr [Q13 + 2 * 16]
+        movdqu  xmm3, XMMWORD ptr [Q13 + 3 * 16]
+        pshufb  xmm0, xmm13
+        pshufb  xmm1, xmm13
+        pshufb  xmm2, xmm13
+        pshufb  xmm3, xmm13
+        movdqa  xmm8,  XMMWORD ptr [Q14 + 0 * 16]
+        movdqa  xmm9,  XMMWORD ptr [Q14 + 1 * 16]
+        movdqa  xmm10, XMMWORD ptr [Q14 + 2 * 16]
+        movdqa  xmm11, XMMWORD ptr [Q14 + 3 * 16]
+        paddd   xmm8,  xmm0
+        paddd   xmm9,  xmm1
+        paddd   xmm10, xmm2
+        paddd   xmm11, xmm3
+        movdqa  XMMWORD ptr [W + 0 * 16], xmm8
+        movdqa  XMMWORD ptr [W + 1 * 16], xmm9
+        movdqa  XMMWORD ptr [W + 2 * 16], xmm10
+        movdqa  XMMWORD ptr [W + 3 * 16], xmm11
+
+inner_loop_single:
+
+        add     Q14, 16 * 4
+
+        //                                                                                    karr ind packlo packhi
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  0, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  1, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5,  2, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4,  3, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm0, xmm1, xmm2, xmm3,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 0, xmm14, xmm15
+
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3,  4, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2,  5, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1,  6, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0,  7, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm1, xmm2, xmm3, xmm0,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 1, xmm14, xmm15
+
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  8, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  9, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5, 10, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4, 11, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm2, xmm3, xmm0, xmm1,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 2, xmm14, xmm15
+
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3, 12, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2, 13, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1, 14, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0, 15, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm3, xmm0, xmm1, xmm2,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 3, xmm14, xmm15
+
+        lea     Q8, [GET_SYMBOL_ADDRESS(SymCryptSha256K) + 48 * 4]
+        cmp     Q14, Q8
+        jb      inner_loop_single
+
+        lea Q13, [W]
+        lea Q14, [W + 16 * 4]
+
+single_block_final_rounds:
+
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  0, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  1, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5,  2, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4,  3, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3,  4, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2,  5, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1,  6, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0,  7, D8, D9, D10, D11, D12, Q13, 4
+
+        add Q13, 8 * 4
+        cmp Q13, Q14
+        jb single_block_final_rounds
+
+        mov     Q8, [rsp + 1280 /*MEMSLOT0*/]
+        SHA256_UPDATE_CV(Q8)
+
+        // Update pbData and cbData
+        mov     Q8, [rsp + 1296 /*MEMSLOT2*/]
+        sub     Q8, SHA2_INPUT_BLOCK_BYTES
+        add     QWORD ptr [rsp + 1288 /*MEMSLOT1*/], SHA2_INPUT_BLOCK_BYTES
+        mov     QWORD ptr [rsp + 1296 /*MEMSLOT2*/], Q8
+        cmp     Q8, SHA2_INPUT_BLOCK_BYTES
+        jae     single_block_start
+
+done:
+
+        //mov   Q8, [rsp + 1296 /*MEMSLOT2*/]
+        mov     Q9, [rsp + 1304 /*MEMSLOT3*/]
+        mov     QWORD ptr [Q9], Q8
+
+        // Wipe expanded message words
+        xor     rax, rax
+        mov     rdi, rsp
+        mov     ecx, [numBytesToWipe]
+
+        // wipe first 64 bytes, the size of the small buffer
+        pxor    xmm0, xmm0
+        movaps  [rdi + 0 * 16], xmm0
+        movaps  [rdi + 1 * 16], xmm0
+        movaps  [rdi + 2 * 16], xmm0
+        movaps  [rdi + 3 * 16], xmm0
+        add     rdi, 4 * 16
+
+        //  if we used vectorized message expansion, wipe the large buffer
+        sub     ecx, 4 * 16 // already wiped above
+        jz      nowipe
+        rep     stosb
+
+nowipe:
+
+
+
+movdqa xmm6, xmmword ptr [rsp + 1040]
+movdqa xmm7, xmmword ptr [rsp + 1056]
+movdqa xmm8, xmmword ptr [rsp + 1072]
+movdqa xmm9, xmmword ptr [rsp + 1088]
+movdqa xmm10, xmmword ptr [rsp + 1104]
+movdqa xmm11, xmmword ptr [rsp + 1120]
+movdqa xmm12, xmmword ptr [rsp + 1136]
+movdqa xmm13, xmmword ptr [rsp + 1152]
+movdqa xmm14, xmmword ptr [rsp + 1168]
+movdqa xmm15, xmmword ptr [rsp + 1184]
+add rsp, 1208
+pop Q14
+pop Q13
+pop Q12
+pop Q11
+pop Q10
+pop Q9
+pop Q8
+pop Q7
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+#undef Q4
+#undef D4
+#undef W4
+#undef B4
+#undef Q5
+#undef D5
+#undef W5
+#undef B5
+#undef Q6
+#undef D6
+#undef W6
+#undef B6
+#undef Q7
+#undef D7
+#undef W7
+#undef B7
+#undef Q8
+#undef D8
+#undef W8
+#undef B8
+#undef Q9
+#undef D9
+#undef W9
+#undef B9
+#undef Q10
+#undef D10
+#undef W10
+#undef B10
+#undef Q11
+#undef D11
+#undef W11
+#undef B11
+#undef Q12
+#undef D12
+#undef W12
+#undef B12
+#undef Q13
+#undef D13
+#undef W13
+#undef B13
+#undef Q14
+#undef D14
+#undef W14
+#undef B14
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/sha256xmm_asm.symcryptasm b/libs/symcrypt/lib/amd64/sha256xmm_asm.symcryptasm
new file mode 100644
index 000000000000..c83aa54d1232
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/sha256xmm_asm.symcryptasm
@@ -0,0 +1,593 @@
+//
+//  sha256xmm_asm.symcryptasm   Assembler code for SHA-256 hash function. Based on
+//  the intrinsics implementation SymCryptSha256AppendBlocks_xmm_4blocks() defined in
+//  sha256-xmm.c
+//
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+EXTERN(SymCryptSha256K:DWORD)
+EXTERN(BYTE_REVERSE_32:DWORD)
+EXTERN(XMM_PACKLOW:DWORD)
+EXTERN(XMM_PACKHIGH:DWORD)
+
+
+SET(SHA2_INPUT_BLOCK_BYTES_LOG2,    6)
+SET(SHA2_INPUT_BLOCK_BYTES,         64)
+SET(SHA2_ROUNDS,                    64)
+SET(SHA2_BYTES_PER_WORD,            4)
+SET(SHA2_SIMD_REG_SIZE,             16)
+SET(SHA2_SINGLE_BLOCK_THRESHOLD,    (3 * SHA2_INPUT_BLOCK_BYTES))   // Minimum number of message bytes required for using vectorized implementation
+
+
+INCLUDE( sha2common_asm.symcryptasm )
+
+
+
+//
+//  Load message words, do the endianness transformation and transpose
+//  the first 16 bytes of each message block.
+//
+//  P [in]          : register pointing to the beginning of a message
+//  N [in]          : number of blocks (N = 3 or 4)
+//  t1,t2           : temporary registers
+//  ind             : index corresponding to which quarter of the message blocks
+//                    the transformation will take place, 0 <= ind <= 3
+//  kreverse [in]   : XMM register containing the value for endianness transformation
+//
+//
+MACRO_START(SHA256_MSG_LOAD_TRANSPOSE_XMM, P, N, t1, t2, ind, kreverse, x0, x1, x2, x3, xt0, xt1, xt2, xt3)
+
+        // Make t2 point to the third or the fourth block depending on the
+        // number of message blocks we have
+        mov     t2, 2 * SHA2_INPUT_BLOCK_BYTES + (ind) * 16
+        mov     t1, 3 * SHA2_INPUT_BLOCK_BYTES + (ind) * 16
+        cmp     N, 4
+        cmove   t2, t1
+
+        // Load from the first three message blocks
+        movdqu  x0, XMMWORD ptr [P + 0 * SHA2_INPUT_BLOCK_BYTES + (ind) * 16]
+        pshufb  x0, kreverse
+        movdqu  x1, XMMWORD ptr [P + 1 * SHA2_INPUT_BLOCK_BYTES + (ind) * 16]
+        pshufb  x1, kreverse
+        movdqu  x2, XMMWORD ptr [P + 2 * SHA2_INPUT_BLOCK_BYTES + (ind) * 16]
+        pshufb  x2, kreverse
+
+        // If there are only three blocks then t2 points to the third block and
+        // that block is read again, otherwise fourth block is loaded. Rereading
+        // a block when we don't have the fourth block is to avoid conditional
+        // loading, the value will not be used.
+        movdqu  x3, XMMWORD ptr [P + t2]
+        pshufb  x3, kreverse
+
+        SHA256_MSG_TRANSPOSE_XMM ind, x0, x1, x2, x3, xt0, xt1, xt2, xt3
+
+MACRO_END()
+
+//
+// Transpose message words from 4 blocks so that each XMM register contains message
+// words with the same index within a message block. This macro transforms four message words at a time,
+// hence needs to be called four times in order to transform 16 message words.
+//
+// The transformation is the same whether we have 4 or less blocks. If we have less than 4 blocks,
+// the corresponding high order lanes contain garbage, and will not be used in round processing.
+//
+// ind [in] : index to which quarter the transformation will take place (ind = 0..3)
+// x0..x3   : XMM registers containing the message words with same indices from different blocks
+//            for instance when ind=0, each xi contains the first 16 bytes of consecutive message blocks
+// t0..t3   : temporary registers
+//
+MACRO_START(SHA256_MSG_TRANSPOSE_XMM, ind, x0, x1, x2, x3, t0, t1, t2, t3)
+
+        // x0 = A3 A2 A1 A0
+        // x1 = B3 B2 B1 B0
+        // x2 = C3 C2 C1 C0
+        // x3 = D3 D2 D1 D0
+        movdqa      t0, x0
+        punpckhdq   t0, x1  // t0 = B3 A3 B2 A2
+        punpckldq   x0, x1  // x0 = B1 A1 B0 A0
+        movdqa      t1, x2
+        punpckhdq   t1, x3  // t1 = D3 C3 D2 C2
+        punpckldq   x2, x3  // x2 = D1 C1 D0 C0
+
+        movdqa      x1, x0
+        punpckhqdq  x1, x2  // x1 = D1 C1 B1 A1
+        punpcklqdq  x0, x2  // x0 = D0 C0 B0 A0
+        movdqa      XMMWORD ptr [W + 64 * (ind) + 0 * 16], x0
+        movdqa      XMMWORD ptr [W + 64 * (ind) + 1 * 16], x1
+
+        movdqa      x3, t0
+        punpckhqdq  x3, t1  // x3 = D3 C3 B3 A3
+        punpcklqdq  t0, t1  // t0 = D2 C2 B2 A2
+        movdqa      XMMWORD ptr [W + 64 * (ind) + 2 * 16], t0
+        movdqa      XMMWORD ptr [W + 64 * (ind) + 3 * 16], x3
+
+MACRO_END()
+
+
+//
+// Rotate each 32-bit value in an XMM register
+//
+// x [in]   : XMM register holding four 32-bit integers
+// c [in]   : rotation count
+// res [out]: XMM register holding the rotated values
+// t1       : temporary XMM register
+//
+MACRO_START(ROR32_XMM, x, c, res, t1)
+
+    movdqa  res, x
+    movdqa  t1, x
+    psrld   res, c
+    pslld   t1, 32 - c
+    pxor    res, t1
+
+MACRO_END()
+
+
+//
+// LSIGMA function as defined in FIPS 180-4 acting on four parallel 32-bit values in a XMM register.
+//
+// x [in]       : XMM register holding four 32-bit integers
+// c1..c3 [in]  : rotation and shift counts
+// res [out]    : output of the LSIGMA function as four 32-bit integer values
+// t1,t2        : temporary XMM registers
+//
+MACRO_START(LSIGMA_XMM, x, c1, c2, c3, res, t1, t2)
+
+        ROR32_XMM   x, c1, res, t1
+        ROR32_XMM   x, c2, t2, t1
+        movdqa      t1, x
+        psrld       t1, c3
+        pxor        res, t2
+        pxor        res, t1
+
+MACRO_END()
+
+
+
+//
+// Message expansion for 4 consecutive message blocks and adds constants to round (rnd - 16)
+//
+// y0 [in/out]      : W_{rnd - 16}, on macro exit it is loaded with W_{rnd - 14} and used as y1 for the
+//                    subsequent macro invocation.
+// y1 [in]          : W_{rnd - 15}
+// y9 [in]          : W_{rnd - 7}
+// y14 [in]         : W_{rnd - 2}
+// rnd [in]         : round number, rnd = 16..24, uses the previous 16 message word state to generate the next one
+// t1 [out]         : expanded message word
+// t2..t6           : temporary XMM registers
+// Wx [in]          : pointer to the message buffer
+// k256 [in]        : pointer to the constants
+//
+MACRO_START(SHA256_MSG_EXPAND_4BLOCKS, y0, y1, y9, y14, rnd, t1, t2, t3, t4, t5, t6, Wx, k256)
+
+        movd        t1, DWORD ptr [k256 + 4 * (rnd - 16)]
+        pshufd      t1, t1, 0
+        paddd       t1, y0                                      // t1 = W_{t-16} + K_{t-16}
+        movdqa      XMMWORD ptr [Wx + (rnd - 16) * 16], t1      // store W_{t-16} + K_{t-16}
+
+        LSIGMA_XMM  y14, 17, 19, 10, t4, t5, t3                 // t4 = LSIGMA1(W_{t-2})
+        LSIGMA_XMM  y1,   7, 18,  3, t1, t5, t3                 // t1 = LSIGMA0(W_{t-15})
+        paddd       y0, y9                                      // y0 = W_{t-16} + W_{t-7}
+        paddd       t1, y0                                      // t1 = W_{t-16} + W_{t-7} + LSIGMA0(W_{t-15})
+        paddd       t1, t4                                      // t1 = W_{t-16} + W_{t-7} + LSIGMA0(W_{t-15}) + LSIGMA1(W_{t-2})
+        movdqa      y0, XMMWORD ptr [Wx + (rnd - 14) * 16]      // y0 = W_{t-14}, load W_{t-15} for next round
+        movdqa      XMMWORD ptr [Wx + (rnd) * 16], t1           // store W_t
+
+MACRO_END()
+
+
+//
+// Single block message expansion using XMM registers
+//
+// x0..x3 [in/out]  : 16 word message state
+// t1..t6           : temporary XMM registers
+// karr             : pointer to the round constants
+// ind              : index used to calculate the offsets for loading constants and storing words to
+//                    message buffer W, each increment points to next 4 round constant and message words.
+// packlow, packhigh: constants for shuffling words and clearing top/bottom halves of an XMM register
+//
+MACRO_START(SHA256_MSG_EXPAND_1BLOCK, x0, x1, x2, x3, t1, t2, t3, t4, t5, t6, karr, ind, packlow, packhigh)
+
+        // Message word state before the expansion
+        // x0 =  W3  W2  W1  W0
+        // x1 =  W7  W6  W5  W4
+        // x2 = W11 W10  W9  W8
+        // x3 = W15 W14 W13 W12
+
+        // After the expansion we will have
+        // x1 =  W7  W6  W5  W4
+        // x2 = W11 W10  W9  W8
+        // x3 = W15 W14 W13 W12
+        // x0 = W19 W18 W17 W16
+
+        movdqa      t2, x1
+        palignr     t2, x0, 4                           // t2 = W4 W3 W2 W1
+
+        // LSIGMA1(W15 W14)
+        pshufd      t1, x3, HEX(0fa)                    // t1 = W15 W15 W14 W14
+        movdqa      t5, t1
+        movdqa      t3, t1
+        psrlq       t5, 17
+        psrlq       t3, 19
+        pxor        t5, t3
+        psrld       t1, 10
+        pxor        t5, t1
+        pshufb      t5, packlow                         // t5 = 0 0 LSIGMA1(W15 W14)
+
+        LSIGMA_XMM  t2, 7, 18, 3, t3, t1, t6            // t3 = LSIGMA0(W4 W3 W2 W1)
+
+        paddd       x0, t5                              // x0 = (W3 W2 W1 W0) + (0 0 LSIGMA1(W15 W14))
+
+        movdqa      t4, x3
+        palignr     t4, x2, 4                           // t4 = W12 W11 W10 W9
+
+        paddd       t4, t3                              // t4 = (W12 W11 W10 W9) + LSIGMA0(W4 W3 W2 W1)
+        paddd       x0, t4                              // x0 = (W3 W2 W1 W0) + (0 0 LSIGMA1(W15 W14)) + (W12 W11 W10 W9) + LSIGMA0(W4 W3 W2 W1)
+
+        // LSIGMA1(W17 W16)
+        pshufd      t1, x0, HEX(50)                     // t1 = W17 W17 W16 W16
+        movdqa      t2, t1
+        movdqa      t3, t1
+        psrlq       t2, 17
+        psrlq       t3, 19
+        pxor        t2, t3
+        psrld       t1, 10
+        pxor        t2, t1
+        pshufb      t2, packhigh                        // t2 = LSIGMA1(W17 W16) 0 0
+
+        movdqa      t6, XMMWORD ptr [karr + ind * 16]   // t6 = K19 K18 K17 K16
+        paddd       x0, t2                              // x0 = W19 W18 W17 W16
+        paddd       t6, x0                              // t6 = (K19 K18 K17 K16) + (W19 W18 W17 W16)
+        movdqa      XMMWORD ptr [rsp + ind * 16], t6
+
+MACRO_END()
+
+
+//
+// Add one set of constants to eight message words from multiple blocks in an XMM register
+//
+// rnd [in]     : round index, rnd = 0..7 (Wx and k256 are adjusted so that this macro always acts on the next 8 rounds)
+// t1, t2       : temporary XMM registers
+// Wx [in]      : pointer to the message buffer
+// k256 [in]    : pointer to the constants array
+//
+MACRO_START(SHA256_MSG_ADD_CONST, rnd, t1, t2, Wx, k256)
+
+        movd    t1, DWORD ptr [k256 + 4 * (rnd)]
+        pshufd  t1, t1, 0
+        movdqa  t2, XMMWORD ptr [Wx + 16 * (rnd)]
+        paddd   t1, t2
+        movdqa XMMWORD ptr [Wx + (rnd) * 16], t1
+
+MACRO_END()
+
+
+
+
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptSha256AppendBlocks(
+//    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE* pChain,
+//    _In_reads_(cbData)      PCBYTE                          pbData,
+//                            SIZE_T                          cbData,
+//    _Out_                   SIZE_T*                         pcbRemaining)
+
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptSha256AppendBlocks_xmm_ssse3_asm), 4, 15, 64*4*4+2*8, 16)
+
+        // Q1 = pChain
+        // Q2 = pbData
+        // Q3 = cbData
+        // Q4 = pcbRemaining
+
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot0)], Q1
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot1)], Q2
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot2)], Q3
+        mov     [rsp + GET_MEMSLOT_OFFSET(slot3)], Q4
+
+
+        // We have two implementations using different message buffer sizes. The code below checks the
+        // input message size and helps avoid wiping the larger buffer if we're not using it.
+        //
+        // If we're processing SHA2_SINGLE_BLOCK_THRESHOLD or more bytes, vectorized message expansion is
+        // used, which expands the message words for 4 blocks. Message expansion for single block processing
+        // uses a buffer of 16 message words. Both buffers start at address W (rsp).
+        //
+        // numBytesToWipe variable holds the number of bytes to wipe from the expanded message buffer
+        // before returning from this call.
+        //
+        // Q3 [in]  : cbData
+        // D8 [out] : numBytesToWipe
+        mov     D8, 16 * SHA2_BYTES_PER_WORD
+        mov     D9, SHA2_EXPANDED_MESSAGE_SIZE
+        cmp     Q3, SHA2_SINGLE_BLOCK_THRESHOLD
+        cmovae  D8, D9
+        mov     [numBytesToWipe], D8
+
+        mov     Q10, Q1
+        mov     D0, DWORD ptr [Q10 +  0]
+        mov     D1, DWORD ptr [Q10 +  4]
+        mov     D2, DWORD ptr [Q10 +  8]
+        mov     D3, DWORD ptr [Q10 + 12]
+        mov     D4, DWORD ptr [Q10 + 16]
+        mov     D5, DWORD ptr [Q10 + 20]
+        mov     D6, DWORD ptr [Q10 + 24]
+        mov     D7, DWORD ptr [Q10 + 28]
+
+        // If message size is less than SHA2_SINGLE_BLOCK_THRESHOLD then use single block message expansion,
+        // otherwise use vectorized message expansion.
+        mov     Q8, [rsp + GET_MEMSLOT_OFFSET(slot2)]
+        cmp     Q8, SHA2_SINGLE_BLOCK_THRESHOLD
+        jb      single_block_entry
+
+        ALIGN(16)
+process_blocks:
+        // Calculate the number of blocks to process, Q8 = cbData
+        GET_SIMD_BLOCK_COUNT Q8, Q9     // Q8 = min(cbData / 64, 4)
+        mov     [numBlocks], Q8
+
+        //
+        // Load and transpose message words
+        //
+        mov Q9, [rsp + GET_MEMSLOT_OFFSET(slot1)]
+        movdqa xmm8, XMMWORD ptr [GET_SYMBOL_ADDRESS(BYTE_REVERSE_32)]
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 0, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 1, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 2, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+        SHA256_MSG_LOAD_TRANSPOSE_XMM Q9, Q8, Q10, Q11, 3, xmm8, xmm0, xmm1, xmm2, xmm3,    xmm4, xmm5, xmm6, xmm7
+
+        // Load transposed message words to registers to be used in message expansion
+        // Each register contains message words (of the same index within a block) from multiple blocks
+        movdqa xmm0, XMMWORD ptr [W + 16 *  0]
+        movdqa xmm1, XMMWORD ptr [W + 16 *  1]
+        movdqa xmm2, XMMWORD ptr [W + 16 *  9]
+        movdqa xmm3, XMMWORD ptr [W + 16 * 10]
+        movdqa xmm4, XMMWORD ptr [W + 16 * 11]
+        movdqa xmm5, XMMWORD ptr [W + 16 * 12]
+        movdqa xmm6, XMMWORD ptr [W + 16 * 13]
+        movdqa xmm7, XMMWORD ptr [W + 16 * 14]
+        movdqa xmm8, XMMWORD ptr [W + 16 * 15]
+
+        lea     Q13, [W]                                    // pointer to expanded message words
+        lea     Q14, [GET_SYMBOL_ADDRESS(SymCryptSha256K)]  // pointer to constants
+
+expand_process_first_block:
+
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm2, xmm7, (16 + 0), xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm3, xmm8, (16 + 1), xmm2, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm4, xmm9, (16 + 2), xmm3, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm5, xmm2, (16 + 3), xmm4, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm6, xmm3, (16 + 4), xmm5, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm7, xmm4, (16 + 5), xmm6, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm0, xmm1, xmm8, xmm5, (16 + 6), xmm7, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+        SHA256_MSG_EXPAND_4BLOCKS   xmm1, xmm0, xmm9, xmm6, (16 + 7), xmm8, xmm10, xmm11, xmm12, xmm13, xmm14, Q13, Q14
+
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7, 0, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6, 1, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5, 2, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4, 3, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3, 4, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2, 5, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1, 6, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0, 7, D8, D9, D10, D11, D12, Q13, 16
+
+        lea     Q8, [GET_SYMBOL_ADDRESS(SymCryptSha256K) + 48 * 4]
+        add     Q13, 8 * 16 // next message words
+        add     Q14, 8 * 4  // next constants
+        cmp     Q14, Q8
+        jb      expand_process_first_block
+
+        // Final 16 rounds
+final_rounds:
+        SHA256_MSG_ADD_CONST 0, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 1, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 2, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 3, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 4, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 5, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 6, xmm1, xmm2, Q13, Q14
+        SHA256_MSG_ADD_CONST 7, xmm1, xmm2, Q13, Q14
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7, 0, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6, 1, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5, 2, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4, 3, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3, 4, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2, 5, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1, 6, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0, 7, D8, D9, D10, D11, D12, Q13, 16
+
+        lea     Q8, [GET_SYMBOL_ADDRESS(SymCryptSha256K) + 64 * 4]
+        add     Q13, 8 * 16 // next message words
+        add     Q14, 8 * 4  // next constants
+        cmp     Q14, Q8
+        jb      final_rounds
+
+        mov     Q8, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+        SHA256_UPDATE_CV(Q8)
+
+        // We've processed one block, update the variable.
+        // Note: We always have more than one block, no need to check the result of the decrement.
+        dec qword ptr [numBlocks]
+
+        lea     Q13, [W + 4]    // second message block words
+
+block_begin:
+
+        mov     D14, 64 / 8
+
+        ALIGN(16)
+inner_loop:
+        //                                                                      Wk  scale
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  0, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  1, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5,  2, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4,  3, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3,  4, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2,  5, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1,  6, D8, D9, D10, D11, D12, Q13, 16
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0,  7, D8, D9, D10, D11, D12, Q13, 16
+
+        add     Q13, 8 * 16 // advance to next 8 message words
+        sub     D14, 1
+        jnz     inner_loop
+
+        add     Q13, (4 - 64 * 16)  // advance to the beginning of message words for the next block
+
+        mov     Q8, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+        SHA256_UPDATE_CV(Q8)
+
+        dec     QWORD ptr [numBlocks]
+        jnz     block_begin
+
+        // Update pbData and cbData
+        mov     Q8, [rsp + GET_MEMSLOT_OFFSET(slot2)]
+        GET_PROCESSED_BYTES Q8, Q9, Q10     // Q9 = bytesProcessed
+        sub     Q8, Q9
+        add     QWORD ptr [rsp + GET_MEMSLOT_OFFSET(slot1)], Q9
+        mov     QWORD ptr [rsp + GET_MEMSLOT_OFFSET(slot2)], Q8
+        cmp     Q8, SHA2_SINGLE_BLOCK_THRESHOLD
+        jae     process_blocks
+
+
+        ALIGN(16)
+single_block_entry:
+
+        cmp     Q8, SHA2_INPUT_BLOCK_BYTES      // Q8 = cbData
+        jb      done
+
+        // Load the constants once before the block processing loop begins
+        // These registers are not touched during block processing
+        movdqa  xmm13, XMMWORD ptr [GET_SYMBOL_ADDRESS(BYTE_REVERSE_32)]
+        movdqa  xmm14, XMMWORD ptr [GET_SYMBOL_ADDRESS(XMM_PACKLOW)]
+        movdqa  xmm15, XMMWORD ptr [GET_SYMBOL_ADDRESS(XMM_PACKHIGH)]
+
+single_block_start:
+
+        mov     Q13, [rsp + GET_MEMSLOT_OFFSET(slot1)]
+        lea     Q14, [GET_SYMBOL_ADDRESS(SymCryptSha256K)]
+
+        //
+        // Load first 16 message words into xmm0..xmm3 and do the endianness transformation
+        // Store the constant added words to message buffer W
+        //
+        movdqu  xmm0, XMMWORD ptr [Q13 + 0 * 16]
+        movdqu  xmm1, XMMWORD ptr [Q13 + 1 * 16]
+        movdqu  xmm2, XMMWORD ptr [Q13 + 2 * 16]
+        movdqu  xmm3, XMMWORD ptr [Q13 + 3 * 16]
+        pshufb  xmm0, xmm13
+        pshufb  xmm1, xmm13
+        pshufb  xmm2, xmm13
+        pshufb  xmm3, xmm13
+        movdqa  xmm8,  XMMWORD ptr [Q14 + 0 * 16]
+        movdqa  xmm9,  XMMWORD ptr [Q14 + 1 * 16]
+        movdqa  xmm10, XMMWORD ptr [Q14 + 2 * 16]
+        movdqa  xmm11, XMMWORD ptr [Q14 + 3 * 16]
+        paddd   xmm8,  xmm0
+        paddd   xmm9,  xmm1
+        paddd   xmm10, xmm2
+        paddd   xmm11, xmm3
+        movdqa  XMMWORD ptr [W + 0 * 16], xmm8
+        movdqa  XMMWORD ptr [W + 1 * 16], xmm9
+        movdqa  XMMWORD ptr [W + 2 * 16], xmm10
+        movdqa  XMMWORD ptr [W + 3 * 16], xmm11
+
+inner_loop_single:
+
+        add     Q14, 16 * 4
+
+        //                                                                                    karr ind packlo packhi
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  0, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  1, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5,  2, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4,  3, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm0, xmm1, xmm2, xmm3,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 0, xmm14, xmm15
+
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3,  4, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2,  5, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1,  6, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0,  7, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm1, xmm2, xmm3, xmm0,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 1, xmm14, xmm15
+
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  8, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  9, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5, 10, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4, 11, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm2, xmm3, xmm0, xmm1,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 2, xmm14, xmm15
+
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3, 12, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2, 13, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1, 14, D8, D9, D10, D11, D12, W, 4
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0, 15, D8, D9, D10, D11, D12, W, 4
+        SHA256_MSG_EXPAND_1BLOCK xmm3, xmm0, xmm1, xmm2,  xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,  Q14, 3, xmm14, xmm15
+
+        lea     Q8, [GET_SYMBOL_ADDRESS(SymCryptSha256K) + 48 * 4]
+        cmp     Q14, Q8
+        jb      inner_loop_single
+
+        lea Q13, [W]
+        lea Q14, [W + 16 * 4]
+
+single_block_final_rounds:
+
+        ROUND_256    D0, D1, D2, D3, D4, D5, D6, D7,  0, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D7, D0, D1, D2, D3, D4, D5, D6,  1, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D6, D7, D0, D1, D2, D3, D4, D5,  2, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D5, D6, D7, D0, D1, D2, D3, D4,  3, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D4, D5, D6, D7, D0, D1, D2, D3,  4, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D3, D4, D5, D6, D7, D0, D1, D2,  5, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D2, D3, D4, D5, D6, D7, D0, D1,  6, D8, D9, D10, D11, D12, Q13, 4
+        ROUND_256    D1, D2, D3, D4, D5, D6, D7, D0,  7, D8, D9, D10, D11, D12, Q13, 4
+
+        add Q13, 8 * 4
+        cmp Q13, Q14
+        jb single_block_final_rounds
+
+        mov     Q8, [rsp + GET_MEMSLOT_OFFSET(slot0)]
+        SHA256_UPDATE_CV(Q8)
+
+        // Update pbData and cbData
+        mov     Q8, [rsp + GET_MEMSLOT_OFFSET(slot2)]
+        sub     Q8, SHA2_INPUT_BLOCK_BYTES
+        add     QWORD ptr [rsp + GET_MEMSLOT_OFFSET(slot1)], SHA2_INPUT_BLOCK_BYTES
+        mov     QWORD ptr [rsp + GET_MEMSLOT_OFFSET(slot2)], Q8
+        cmp     Q8, SHA2_INPUT_BLOCK_BYTES
+        jae     single_block_start
+
+done:
+
+        //mov   Q8, [rsp + GET_MEMSLOT_OFFSET(slot2)]
+        mov     Q9, [rsp + GET_MEMSLOT_OFFSET(slot3)]
+        mov     QWORD ptr [Q9], Q8
+
+        // Wipe expanded message words
+        xor     rax, rax
+        mov     rdi, rsp
+        mov     ecx, [numBytesToWipe]
+
+        // wipe first 64 bytes, the size of the small buffer
+        pxor    xmm0, xmm0
+        movaps  [rdi + 0 * 16], xmm0
+        movaps  [rdi + 1 * 16], xmm0
+        movaps  [rdi + 2 * 16], xmm0
+        movaps  [rdi + 3 * 16], xmm0
+        add     rdi, 4 * 16
+
+        //  if we used vectorized message expansion, wipe the large buffer
+        sub     ecx, 4 * 16 // already wiped above
+        jz      nowipe
+        rep     stosb
+
+nowipe:
+
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptSha256AppendBlocks_xmm_ssse3_asm))
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/sha2common_asm.symcryptasm b/libs/symcrypt/lib/amd64/sha2common_asm.symcryptasm
new file mode 100644
index 000000000000..db54156c9f21
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/sha2common_asm.symcryptasm
@@ -0,0 +1,264 @@
+//
+//  sha2common_asm.symcryptasm   Generic assembler routines used in SHA-2 implementations
+//
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+
+SET(SHA2_SIMD_LANES,                ((SHA2_SIMD_REG_SIZE) / (SHA2_BYTES_PER_WORD)))
+SET(SHA2_EXPANDED_MESSAGE_SIZE,     ((SHA2_ROUNDS) * (SHA2_SIMD_REG_SIZE)))
+
+
+// Local variables used by all SHA-2 implementations
+#define W               rsp
+#define LocalsBase      (W + SHA2_EXPANDED_MESSAGE_SIZE)
+#define numBlocks       (LocalsBase +  0 * 8)
+#define numBytesToWipe  (LocalsBase +  1 * 8)
+
+
+//
+// Load one message word (4- or 8-bytes depending on the size of register t1),
+// do the endianness transformation and store it in message buffer.
+//
+// ptr [in] : register pointing to the beginning of a message
+// ind [in] : message index within a block (ind = 0..15)
+// res [out]: output message word
+//
+MACRO_START(LOAD_MSG_WORD, ptr, res, ind)
+
+        mov     res, [ptr + (ind) * SHA2_BYTES_PER_WORD]
+        bswap   res
+        mov     [W + (ind) * SHA2_BYTES_PER_WORD], res
+
+MACRO_END()
+
+
+//
+// Get the number of message blocks that could be processed in parallel using SIMD implementation
+//
+// cbMsg [in/out]   : message size in bytes on input, set to min(r1 / SHA2_INPUT_BLOCK_BYTES, SHA2_SIMD_LANES) on output
+// t                : temporary register
+//
+MACRO_START(GET_SIMD_BLOCK_COUNT, cbMsg, t)
+
+        shr     cbMsg, SHA2_INPUT_BLOCK_BYTES_LOG2
+        mov     t, SHA2_SIMD_LANES
+        cmp     cbMsg, t
+        cmova   cbMsg, t
+
+MACRO_END()
+
+//
+// Get the number of processed message bytes
+//
+// The return value is a multiple of SHA2_INPUT_BLOCK_BYTES, depending
+// on the level of parallelism in the vectorized implementation and available
+// message bytes to process.
+//
+// cbMsg [in]       : message length in bytes
+// cbProcessed [out]: number of processed bytes
+// t                : temporary register
+//
+MACRO_START(GET_PROCESSED_BYTES, cbMsg, cbProcessed, t)
+
+        mov     cbProcessed, cbMsg
+        mov     t, SHA2_SIMD_LANES * SHA2_INPUT_BLOCK_BYTES
+        cmp     cbProcessed, t
+        cmova   cbProcessed, t
+        and     cbProcessed, -SHA2_INPUT_BLOCK_BYTES
+
+MACRO_END()
+
+
+//
+// SHA-2 core round function that uses BMI2 instructions
+//
+// a, b, c, e, f, g [in]: hash function state
+// d, h [in/out]        : hash function state
+// rnd [in]             : round number, used for indexing into the Wk array
+// t1..t5               : temporary registers
+// Wk [in]              : register pointing to message/constant buffer
+// scale [in]           : granularity of elements in message buffer /constant array
+// c0_r1, c0_r2, c0_r3  : rotation constants for CSIGMA0 function
+// c1_r1, c1_r2, c1_r3  : rotation constants for CSIGMA1 function
+//
+MACRO_START(ROUND_T5_BMI2_V1, a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale, c0_r1, c0_r2, c0_r3, c1_r1, c1_r2, c1_r3)
+
+    //                  SELECT          CSIGMA0         CSIGMA1         MAJ
+    //-------------------------------------------------------------------------------
+                                                         rorx t5, e, c1_r1
+                                                         rorx t4, e, c1_r2
+                        mov  t1, f
+                        andn t2, e, g
+                                                         rorx t3, e, c1_r3
+        add h, [Wk + (rnd) * (scale)]
+                        and  t1, e
+                                                         xor  t5, t4
+                        xor  t1, t2
+                                                         xor  t5, t3
+        add h, t1
+                                         rorx t2, a, c0_r1
+                                                                        mov t3, b
+                                                                        mov t4, b
+                                         rorx t1, a, c0_r2
+        add h, t5
+                                                                        or  t3, c
+                                                                        and t4, c
+                                                                        and t3, a
+                                                                        or  t4, t3
+        add d, h
+                                         xor  t2, t1
+                                         rorx t5, a, c0_r3
+                                         xor  t2, t5
+        add h, t4
+        add h, t2
+
+MACRO_END()
+
+
+MACRO_START(ROUND_T5_BMI2_V2, a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale, c0_r1, c0_r2, c0_r3, c1_r1, c1_r2, c1_r3)
+
+    //                  SELECT          CSIGMA0         CSIGMA1         MAJ
+    //-------------------------------------------------------------------------------
+                        mov  t1, f
+                        andn t2, e, g
+                                                         rorx t5, e, c1_r1
+                        and  t1, e
+                                                         rorx t4, e, c1_r2
+                                                         rorx t3, e, c1_r3
+        add h, [Wk + (rnd) * (scale)]
+                                                         xor t5, t4
+                        xor  t1, t2
+                                                         xor  t5, t3
+        add h, t1
+        add h, t5
+                                                                        mov t3, b
+                                         rorx t1, a, c0_r2
+                                         rorx t2, a, c0_r1
+                                                                        mov t4, b
+                                                                        or  t3, c
+                                                                        and t4, c
+        add d, h
+                                                                        and t3, a
+                                                                        or  t4, t3
+                                         xor  t2, t1
+                                         rorx t5, a, c0_r3
+                                         xor  t2, t5
+        add h, t4
+        add h, t2
+
+MACRO_END()
+
+
+
+MACRO_START(ROUND_256, a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale)
+
+    ROUND_T5_BMI2_V1 a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale, 2, 13, 22, 6, 11, 25
+
+MACRO_END()
+
+
+MACRO_START(ROUND_512, a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale)
+
+    ROUND_T5_BMI2_V1 a, b, c, d, e, f, g, h, rnd, t1, t2, t3, t4, t5, Wk, scale, 28, 34, 39, 14, 18, 41
+
+MACRO_END()
+
+
+
+MACRO_START(SHA2_UPDATE_CV_HELPER, rcv, r0, r1, r2, r3, r4, r5, r6, r7)
+
+        add r0, [rcv + 0 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 0 * SHA2_BYTES_PER_WORD], r0
+        add r1, [rcv + 1 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 1 * SHA2_BYTES_PER_WORD], r1
+        add r2, [rcv + 2 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 2 * SHA2_BYTES_PER_WORD], r2
+        add r3, [rcv + 3 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 3 * SHA2_BYTES_PER_WORD], r3
+        add r4, [rcv + 4 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 4 * SHA2_BYTES_PER_WORD], r4
+        add r5, [rcv + 5 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 5 * SHA2_BYTES_PER_WORD], r5
+        add r6, [rcv + 6 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 6 * SHA2_BYTES_PER_WORD], r6
+        add r7, [rcv + 7 * SHA2_BYTES_PER_WORD]
+        mov [rcv + 7 * SHA2_BYTES_PER_WORD], r7
+
+MACRO_END()
+
+#define SHA256_UPDATE_CV(rcv)   SHA2_UPDATE_CV_HELPER rcv, D0, D1, D2, D3, D4, D5, D6, D7
+
+#define SHA512_UPDATE_CV(rcv)   SHA2_UPDATE_CV_HELPER rcv, Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7
+
+
+//
+//  Load message words, do the endianness transformation and transpose for one or more message blocks.
+//
+//  P [in]          : register pointing to the beginning of a message
+//  N [in]          : number of blocks ( N = 3 or 4 )
+//  t, v            : temporary registers
+//  ind             : index of which quarter of the block we're processing (ind = 0..3)
+//  kreverse        : YMM register containing the value used to do the endianness transform
+//  y1..y4  [out]   : YMM registers containing the transposed message words
+//  t1..t4          : temporary YMM registers
+MACRO_START(SHA512_MSG_LOAD_TRANSPOSE_YMM, P, N, t, v, ind, kreverse, y1, y2, y3, y4, t1, t2, t3, t4)
+
+        // Make t point to the third or the fourth block depending on the
+        // number of message blocks we have
+        mov t, 2 * SHA2_INPUT_BLOCK_BYTES + (ind) * 32
+        mov v, 3 * SHA2_INPUT_BLOCK_BYTES + (ind) * 32
+        cmp N, 4
+        cmove t, v
+
+        // We have at least three message blocks
+        vmovdqu y1, YMMWORD ptr [P + 0 * SHA2_INPUT_BLOCK_BYTES + (ind) * 32]
+        vpshufb y1, y1, kreverse
+        vmovdqu y2, YMMWORD ptr [P + 1 * SHA2_INPUT_BLOCK_BYTES + (ind) * 32]
+        vpshufb y2, y2, kreverse
+        vmovdqu y3, YMMWORD ptr [P + 2 * SHA2_INPUT_BLOCK_BYTES + (ind) * 32]
+        vpshufb y3, y3, kreverse
+
+        // If there are only three blocks then t points to the third block and
+        // that block is read again, otherwise fourth block is loaded. Rereading
+        // a block when we don't have the fourth block is to avoid conditional
+        // loading, the value will not be used.
+        vmovdqu y4, YMMWORD ptr [P + t]
+        vpshufb y4, y4, kreverse
+
+        SHA512_MSG_TRANSPOSE_YMM ind, y1, y2, y3, y4, t1, t2, t3, t4
+
+MACRO_END()
+
+//
+// Transpose message words from 4 blocks so that each YMM register contains message
+// words with the same index within a message block. This macro transforms four message words at a time,
+// hence needs to be called four times in order to transform 16 message words.
+//
+// The transformation is the same whether we have 4 or less blocks. If we have less than 4 blocks,
+// the corresponding high order lanes contain garbage, and will not be used in round processing.
+//
+// ind [in]         : index to which quarter the transformation will take place (ind = 0..3)
+// y1..y4 [in/out] : YMM registers each containing 4 message words from different blocks
+//                        (y1 = W[4 * ind + 0], y2 = W[4 * ind + 1], y3 = W[4 * ind + 2], y4 = W[4 * ind + 3])
+// t1..t4           : temporary YMM registers
+//
+MACRO_START(SHA512_MSG_TRANSPOSE_YMM, ind, y1, y2, y3, y4, t1, t2, t3, t4)
+
+        vpunpcklqdq t1, y1, y2
+        vpunpcklqdq t2, y3, y4
+        vpunpckhqdq t3, y1, y2
+        vpunpckhqdq t4, y3, y4
+        vperm2i128  y1, t1, t2, HEX(20)
+        vperm2i128  y2, t3, t4, HEX(20)
+        vperm2i128  y3, t1, t2, HEX(31)
+        vperm2i128  y4, t3, t4, HEX(31)
+        vmovdqu     YMMWORD ptr [W + (ind) * 128 + 0 * 32], y1
+        vmovdqu     YMMWORD ptr [W + (ind) * 128 + 1 * 32], y2
+        vmovdqu     YMMWORD ptr [W + (ind) * 128 + 2 * 32], y3
+        vmovdqu     YMMWORD ptr [W + (ind) * 128 + 3 * 32], y4
+
+MACRO_END()
diff --git a/libs/symcrypt/lib/amd64/wipe.S b/libs/symcrypt/lib/amd64/wipe.S
new file mode 100644
index 000000000000..982f964ba548
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/wipe.S
@@ -0,0 +1,206 @@
+//
+//  wipe.symcryptasm   Assembler code for wiping a buffer
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptWipe( _Out_writes_bytes_( cbData )    PVOID  pbData,
+//                                              SIZE_T cbData )
+
+#define Q0 rax
+#define D0 eax
+#define W0 ax
+#define B0 al
+#define Q1 rcx
+#define D1 ecx
+#define W1 cx
+#define B1 cl
+#define Q2 rdx
+#define D2 edx
+#define W2 dx
+#define B2 dl
+#define Q3 r8
+#define D3 r8d
+#define W3 r8w
+#define B3 r8b
+PLATFORM_NAME_MANGLE(SymCryptWipeAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptWipeAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptWipeAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptWipeAsm)
+
+
+.seh_endprologue
+
+
+        // Q1 = pbData
+        // Q2 = cbData
+
+        //
+        // This function will handle any alignment of pbData and any size, but it is optimized for
+        // the case where the start and end of the buffer are 16-aligned.
+        // 16 is the natural stack alignment on AMD64, and structures can be designed to be a multiple
+        // of 16 long without adding too much slack.
+        // The cost of non-alignment is relatively low, in the order of 5 cycles or so
+        //
+
+        xorps   xmm0,xmm0               // Zero register for 16-byte wipes
+        cmp     Q2,16
+        jb      SymCryptWipeAsmSmall    // if cbData < 16, this is a rare case
+
+        test    Q1,15
+        jnz     SymCryptWipeAsmUnaligned // if data pointer is unaligned, we jump to the code that aligns the pointer
+                                        // For well-optimized callers the aligned case is the common one, and that is
+                                        // the fall-through.
+
+SymCryptWipeAsmAligned:
+        //
+        // Here Q1 is aligned, and Q2 contains the # bytes left to wipe, and Q2 >= 16
+        //
+        // Our loop wipes in 32-byte increments; we always wipe the first 16 bytes
+        // and increment the pbData pointer if cbData is 16 mod 32
+        // This avoids a conditional jump and is faster.
+        //
+        test    Q2,16
+        movaps  [Q1],xmm0               // it is safe to always wipe as cbData >= 16
+        lea     Q3,[Q1+16]
+        cmovnz  Q1,Q3                   // only increment pbData if cbData = 16 mod 32
+
+        sub     Q2,32                   // see if we have >= 32 bytes to wipe
+        jc      SymCryptWipeAsmTailOptional // if not, wipe tail, or nothing if cbData = 0 mod 16
+
+ALIGN(16)
+
+SymCryptWipeAsmLoop:
+        movaps  [Q1],xmm0
+        movaps  [Q1+16],xmm0            // Wipe 32 bytes
+        add     Q1,32
+        sub     Q2,32
+        jnc     SymCryptWipeAsmLoop
+
+SymCryptWipeAsmTailOptional:
+        // only the lower 4 bits of Q2 are valid, we have subtracted too much already.
+        // The wipe was at least 16 bytes, so we can just wipe the tail with 2 instructions
+
+        and     D2,15
+        jnz     SymCryptWipeAsmTail
+        ret
+
+SymCryptWipeAsmTail:
+        // This code appears also below at the end of the unaligned wiping routine
+        // but making the jnz jump further is slower and we only duplicate 4 instructions.
+        xor     D0,D0
+        mov     [Q1+Q2-16],Q0
+        mov     [Q1+Q2-8],Q0
+        ret
+
+ALIGN(4)
+
+SymCryptWipeAsmUnaligned:
+
+        //
+        // At this point we know that cbData(Q2) >= 16 and pbData(Q1) is unaligned.
+        // We can wipe 16 bytes and move to an aligned position
+        //
+        xor     D0,D0
+        mov     [Q1],Q0
+        mov     [Q1+8],Q0
+
+        mov     D0,D1
+        neg     D0                      // lower 4 bits of D0 = # bytes to wipe to reach alignment
+        and     D0,15
+        add     Q1,Q0
+        sub     Q2,Q0
+
+        //
+        // If Q2 > 16, go to the aligned wiping loop
+        //
+        cmp     Q2,16
+        jae     SymCryptWipeAsmAligned  // if cbData >= 16, do aligned wipes
+
+        //
+        // We have <= 16 bytes to wipe, and we know that the full wipe region was at least 16 bytes.
+        // We just wipe the last 16 bytes completely.
+        //
+        xor     D0,D0
+        mov     [Q1+Q2-16],Q0
+        mov     [Q1+Q2-8],Q0
+        ret
+
+ALIGN(8)
+
+SymCryptWipeAsmSmall:
+        // Q1 = pbData, possibly unaligned
+        // Q2 = cbData; Q2 < 16
+        //
+        // With speculative execution attacks, the cost of a jump table is prohibitive.
+        // We use a compare ladder for 5 cases:
+        //       8-15 bytes
+        //       4-7 bytes
+        //       2-3 bytes
+        //       1 byte
+        //       0 bytes
+
+        xor     D0,D0
+
+        cmp     D2, 8
+        jb      SymCryptWipeAsmSmallLessThan8
+
+        // wipe 8-15 bytes using two possibly overlapping writes
+        mov     [Q1],Q0
+        mov     [Q1+Q2-8],Q0
+        ret
+
+SymCryptWipeAsmSmallLessThan8:
+        cmp     D2, 4
+        jb      SymCryptWipeAsmSmallLessThan4
+
+        // wipe 4-7 bytes
+        mov     [Q1],D0
+        mov     [Q1+Q2-4],D0
+        ret
+
+SymCryptWipeAsmSmallLessThan4:
+        cmp     D2, 2
+        jb      SymCryptWipeAsmSmallLessThan2
+
+        // wipe 2-3 bytes
+        mov     [Q1],W0
+        mov     [Q1+Q2-2],W0
+        ret
+
+SymCryptWipeAsmSmallLessThan2:
+        or      D2,D2
+        jz      SymCryptWipeAsmSmallDone
+
+        // wipe 1 byte
+        mov     [Q1],B0
+
+SymCryptWipeAsmSmallDone:
+
+
+ret
+.seh_endproc
+#undef Q0
+#undef D0
+#undef W0
+#undef B0
+#undef Q1
+#undef D1
+#undef W1
+#undef B1
+#undef Q2
+#undef D2
+#undef W2
+#undef B2
+#undef Q3
+#undef D3
+#undef W3
+#undef B3
+
+FILE_END()
diff --git a/libs/symcrypt/lib/amd64/wipe.symcryptasm b/libs/symcrypt/lib/amd64/wipe.symcryptasm
new file mode 100644
index 000000000000..56c1e9f14f0c
--- /dev/null
+++ b/libs/symcrypt/lib/amd64/wipe.symcryptasm
@@ -0,0 +1,165 @@
+//
+//  wipe.symcryptasm   Assembler code for wiping a buffer
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptWipe( _Out_writes_bytes_( cbData )    PVOID  pbData,
+//                                              SIZE_T cbData )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptWipeAsm), 2, 4)
+
+        // Q1 = pbData
+        // Q2 = cbData
+
+        //
+        // This function will handle any alignment of pbData and any size, but it is optimized for
+        // the case where the start and end of the buffer are 16-aligned.
+        // 16 is the natural stack alignment on AMD64, and structures can be designed to be a multiple
+        // of 16 long without adding too much slack.
+        // The cost of non-alignment is relatively low, in the order of 5 cycles or so
+        //
+
+        xorps   xmm0,xmm0               // Zero register for 16-byte wipes
+        cmp     Q2,16
+        jb      SymCryptWipeAsmSmall    // if cbData < 16, this is a rare case
+
+        test    Q1,15
+        jnz     SymCryptWipeAsmUnaligned // if data pointer is unaligned, we jump to the code that aligns the pointer
+                                        // For well-optimized callers the aligned case is the common one, and that is
+                                        // the fall-through.
+
+SymCryptWipeAsmAligned:
+        //
+        // Here Q1 is aligned, and Q2 contains the # bytes left to wipe, and Q2 >= 16
+        //
+        // Our loop wipes in 32-byte increments; we always wipe the first 16 bytes
+        // and increment the pbData pointer if cbData is 16 mod 32
+        // This avoids a conditional jump and is faster.
+        //
+        test    Q2,16
+        movaps  [Q1],xmm0               // it is safe to always wipe as cbData >= 16
+        lea     Q3,[Q1+16]
+        cmovnz  Q1,Q3                   // only increment pbData if cbData = 16 mod 32
+
+        sub     Q2,32                   // see if we have >= 32 bytes to wipe
+        jc      SymCryptWipeAsmTailOptional // if not, wipe tail, or nothing if cbData = 0 mod 16
+
+ALIGN(16)
+
+SymCryptWipeAsmLoop:
+        movaps  [Q1],xmm0
+        movaps  [Q1+16],xmm0            // Wipe 32 bytes
+        add     Q1,32
+        sub     Q2,32
+        jnc     SymCryptWipeAsmLoop
+
+SymCryptWipeAsmTailOptional:
+        // only the lower 4 bits of Q2 are valid, we have subtracted too much already.
+        // The wipe was at least 16 bytes, so we can just wipe the tail with 2 instructions
+
+        and     D2,15
+        jnz     SymCryptWipeAsmTail
+        ret
+
+SymCryptWipeAsmTail:
+        // This code appears also below at the end of the unaligned wiping routine
+        // but making the jnz jump further is slower and we only duplicate 4 instructions.
+        xor     D0,D0
+        mov     [Q1+Q2-16],Q0
+        mov     [Q1+Q2-8],Q0
+        ret
+
+ALIGN(4)
+
+SymCryptWipeAsmUnaligned:
+
+        //
+        // At this point we know that cbData(Q2) >= 16 and pbData(Q1) is unaligned.
+        // We can wipe 16 bytes and move to an aligned position
+        //
+        xor     D0,D0
+        mov     [Q1],Q0
+        mov     [Q1+8],Q0
+
+        mov     D0,D1
+        neg     D0                      // lower 4 bits of D0 = # bytes to wipe to reach alignment
+        and     D0,15
+        add     Q1,Q0
+        sub     Q2,Q0
+
+        //
+        // If Q2 > 16, go to the aligned wiping loop
+        //
+        cmp     Q2,16
+        jae     SymCryptWipeAsmAligned  // if cbData >= 16, do aligned wipes
+
+        //
+        // We have <= 16 bytes to wipe, and we know that the full wipe region was at least 16 bytes.
+        // We just wipe the last 16 bytes completely.
+        //
+        xor     D0,D0
+        mov     [Q1+Q2-16],Q0
+        mov     [Q1+Q2-8],Q0
+        ret
+
+ALIGN(8)
+
+SymCryptWipeAsmSmall:
+        // Q1 = pbData, possibly unaligned
+        // Q2 = cbData; Q2 < 16
+        //
+        // With speculative execution attacks, the cost of a jump table is prohibitive.
+        // We use a compare ladder for 5 cases:
+        //       8-15 bytes
+        //       4-7 bytes
+        //       2-3 bytes
+        //       1 byte
+        //       0 bytes
+
+        xor     D0,D0
+
+        cmp     D2, 8
+        jb      SymCryptWipeAsmSmallLessThan8
+
+        // wipe 8-15 bytes using two possibly overlapping writes
+        mov     [Q1],Q0
+        mov     [Q1+Q2-8],Q0
+        ret
+
+SymCryptWipeAsmSmallLessThan8:
+        cmp     D2, 4
+        jb      SymCryptWipeAsmSmallLessThan4
+
+        // wipe 4-7 bytes
+        mov     [Q1],D0
+        mov     [Q1+Q2-4],D0
+        ret
+
+SymCryptWipeAsmSmallLessThan4:
+        cmp     D2, 2
+        jb      SymCryptWipeAsmSmallLessThan2
+
+        // wipe 2-3 bytes
+        mov     [Q1],W0
+        mov     [Q1+Q2-2],W0
+        ret
+
+SymCryptWipeAsmSmallLessThan2:
+        or      D2,D2
+        jz      SymCryptWipeAsmSmallDone
+
+        // wipe 1 byte
+        mov     [Q1],B0
+
+SymCryptWipeAsmSmallDone:
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptWipeAsm))
+
+FILE_END()
diff --git a/libs/symcrypt/lib/arm/aesasm.S b/libs/symcrypt/lib/arm/aesasm.S
new file mode 100644
index 000000000000..1a0698131f69
--- /dev/null
+++ b/libs/symcrypt/lib/arm/aesasm.S
@@ -0,0 +1,1012 @@
+//
+//  AesAsm.cppasm   Assembler code for fast AES on ARM
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This code is derived from the AMD64 version of the AesFast
+// implementation, developed by Niels Ferguson. For questions
+// about the ARM specifics, contact Aaron Giles.
+//
+
+// #include "kxarm.h"
+
+#include "symcryptasm_shared.cppasm"
+
+#if SYMCRYPT_DEBUG
+SET(SYMCRYPT_CODE_VERSION, ((SYMCRYPT_CODE_VERSION_API * 65536) + SYMCRYPT_CODE_VERSION_MINOR))
+SET(SYMCRYPT_MAGIC_CONSTANT, HEX(53316D76) + SYMCRYPT_CODE_VERSION) // 0x53316D76 == 'S1mv'
+
+.macro SYMCRYPT_CHECK_MAGIC  temp1, temp2, ptr, offset, check_magic_label
+        ldr     \temp1, [\ptr, #\offset]
+        subs    \temp1, \temp1, \ptr
+        //mov32   \temp2, =SYMCRYPT_MAGIC_CONSTANT
+        ldr     \temp2, =SYMCRYPT_MAGIC_CONSTANT
+        cmp     \temp1, \temp2
+        beq     \check_magic_label
+        //mov32   r0, 0x6d616763     // 'magc'
+        ldr     r0, =0x6d616763     // 'magc'
+        bl      SymCryptFatal
+\check_magic_label:
+.endm
+#else
+.macro SYMCRYPT_CHECK_MAGIC  temp1, temp2, ptr, offset, check_magic_label
+.endm
+#endif
+
+
+//        TTL   "Advanced Encryption Standard (AES)"
+
+//
+// typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_AES_EXPANDED_KEY {
+//     SYMCRYPT_ALIGN BYTE RoundKey[29][4][4];
+//            Round keys, first the encryption round keys in encryption order,
+//            followed by the decryption round keys in decryption order.
+//            The first decryption round key is the last encryption round key.
+//            AES-256 has 14 rounds and thus 15 round keys for encryption and 15
+//            for decryption. As they share one round key, we need room for 29.
+//     BYTE   (*lastEncRoundKey)[4][4];       Pointer to last encryption round key
+//                                            also the first round key for decryption
+//     BYTE   (*lastDecRoundKey)[4][4];       Pointer to last decryption round key.
+//
+//     SYMCRYPT_MAGIC_FIELD
+// } SYMCRYPT_AES_EXPANDED_KEY, *PSYMCRYPT_AES_EXPANDED_KEY
+//
+
+#define SYMCRYPT_AES_EXPANDED_KEY_RoundKey          (0)
+#define SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey   (29*4*4)
+#define SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey   (29*4*4+4)
+
+#if SYMCRYPT_DEBUG
+#define SYMCRYPT_AES_EXPANDED_KEY_magic             (29*4*4+4+4)
+#endif
+
+.macro ENC_MIX  keyptr
+        //
+        // Perform the unkeyed mixing function for encryption
+        // plus a key addition from the key pointer
+        //
+        // Input:
+        //   r0,r1,r2,r3 = current block
+        //   \keyptr = pointer to current key
+        //   r12 = pointer to AesSboxMatrixMult
+        //
+        // Output:
+        //   r0,r1,r2,r3 = updated block
+        //   \keyptr = updated to point to following key
+        //   r12 = unmodified
+        //
+        // Used:
+        //   r4,r5,r6,r7,lr are modified
+        //
+        // N.B. To make better use of ARM's barrel shifter, this code differs
+        //      from the AMD64 approach. The first lookups are not rotated;
+        //      instead all subsequent lookups are applied on top rotated, and
+        //      then a final rotation is performed to shift the bits into the
+        //      proper spot.
+        //
+
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r7, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r5, r0, ror #24                 //
+        ldr     r4, [r12, r4, lsl #2]           // perform lookups of each byte, leaving
+        ldr     r7, [r12, r7, lsl #2]           //   the values unrotated for now
+        ldr     r6, [r12, r6, lsl #2]           //
+        ldr     r5, [r12, r5, lsl #2]           //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     lr, [r12, lr, lsl #2]           //
+        eor     r5, r5, r0, ror #24             // exclusive-OR with previous
+        eor     r4, r4, lr, ror #24             //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        eor     r7, r7, r0, ror #24             // exclusive-OR with previous
+        eor     r6, r6, r1, ror #24             //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     lr, [r12, lr, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        eor     r6, r6, r0, ror #16             // exclusive-OR with previous
+        eor     r5, r5, r1, ror #16             //
+        eor     r4, r4, lr, ror #16             //
+        eor     r7, r7, r2, ror #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        ldr     r3, [r12, r3, lsl #2]           //
+        eor     r7, r7, r0, ror #8              // exclusive-OR with previous
+        eor     r6, r6, r1, ror #8              //
+        eor     r5, r5, r2, ror #8              //
+        eor     r4, r4, r3, ror #8              //
+
+        ldrd    r0, r1, [\keyptr, #0]            // fetch key into r0-r3
+        ldrd    r2, r3, [\keyptr, #8]            //
+        adds    \keyptr, \keyptr, #16             // increment key pointer
+        eors    r0, r0, r4                      // exclusive-OR the key and rotate into final
+        eor     r1, r1, r5, ror #8              //   position
+        eor     r2, r2, r6, ror #16             //
+        eor     r3, r3, r7, ror #24             //
+.endm
+
+.macro DEC_MIX  keyptr
+        //
+        // Perform the unkeyed mixing function for decryption
+        //
+        // Input:
+        //   r0,r1,r2,r3 = current block
+        //   \keyptr = pointer to current key
+        //   r12 = pointer to AesInvSboxMatrixMult
+        //
+        // Output:
+        //   r0,r1,r2,r3 = updated block
+        //   \keyptr = updated to point to following key
+        //   r12 = unmodified
+        //
+        // Used:
+        //   r4,r5,r6,r7,lr are modified
+        //
+        // N.B. To make better use of ARM's barrel shifter, this code differs
+        //      from the AMD64 approach. The first lookups are not rotated;
+        //      instead all subsequent lookups are applied on top rotated, and
+        //      then a final rotation is performed to shift the bits into the
+        //      proper spot.
+        //
+
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r5, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r7, r0, ror #24                 //
+        ldr     r4, [r12, r4, lsl #2]           // perform lookups of each byte, leaving
+        ldr     r5, [r12, r5, lsl #2]           //   the values unrotated for now
+        ldr     r6, [r12, r6, lsl #2]           //
+        ldr     r7, [r12, r7, lsl #2]           //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     lr, [r12, lr, lsl #2]           //
+        eor     r5, r5, r0, ror #8              // exclusive-OR with previous
+        eor     r6, r6, lr, ror #8              //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        eor     r7, r7, r0, ror #8              // exclusive-OR with previous
+        eor     r4, r4, r1, ror #8              //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     lr, [r12, lr, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        eor     r6, r6, r0, ror #16             // exclusive-OR with previous
+        eor     r7, r7, r1, ror #16             //
+        eor     r4, r4, lr, ror #16             //
+        eor     r5, r5, r2, ror #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        ldr     r3, [r12, r3, lsl #2]           //
+        eor     r7, r7, r0, ror #24             // exclusive-OR with previous
+        eor     r4, r4, r1, ror #24             //
+        eor     r5, r5, r2, ror #24             //
+        eor     r6, r6, r3, ror #24             //
+
+        ldrd    r0, r1, [\keyptr, #0]            // fetch key into r0-r3
+        ldrd    r2, r3, [\keyptr, #8]            //
+        adds    \keyptr, \keyptr, #16             // increment key pointer
+        eors    r0, r0, r4                      // exclusive-OR the key and rotate into final
+        eor     r1, r1, r5, ror #24             //   position
+        eor     r2, r2, r6, ror #16             //
+        eor     r3, r3, r7, ror #8              //
+.endm
+
+.macro AES_ENCRYPT  loopLabel
+
+        //
+        // Input:
+        //   r0,r1,r2,r3 = plaintext
+        //   r8 = pointer to first round key to use
+        //   r9 = pointer to last key to use
+        //   r12 = pointer to AesSboxMatrixMult
+        //
+        // Output:
+        //   r4,r5,r6,r7 = ciphertext
+        //   r8 = modified to point to last key
+        //   r9 = unmodified
+        //   r12 = unmodified
+        //
+        // Used:
+        //   lr is also modified
+        //
+
+        //
+        // xor in first round key
+        //
+
+        ldrd    r4, r5, [r8, #0]                // fetch key in r4-r7
+        ldrd    r6, r7, [r8, #8]                //
+        eors    r0, r0, r4                      // exclusive-OR with the plaintext
+        eors    r1, r1, r5                      //
+        eors    r2, r2, r6                      //
+        eors    r3, r3, r7                      //
+
+        add     r8, r8, #16                     // point to second key
+
+\loopLabel:
+        //
+        // Block is r0,r1,r2,r3
+        // r8 points to current round key
+        //
+
+        ENC_MIX r8                              // encrypt the block and increment key
+        cmp     r8, r9                          // are we at the end?
+        blo     \loopLabel                       // loop until it is so
+
+        //
+        // Now for the final round
+        // We use the fact that SboxMatrixMult[0] table is also
+        // an Sbox table if you use the second element of each entry.
+        //
+        // Result is in r4,r5,r6,r7
+        //
+
+        add     r12, r12, #1                    // advance by 1 to point to second element
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r7, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r5, r0, ror #24                 //
+        ldrb    r4, [r12, r4, lsl #2]           // perform lookups of each byte, leaving
+        ldrb    r7, [r12, r7, lsl #2]           //   the values unrotated for now
+        ldrb    r6, [r12, r6, lsl #2]           //
+        ldrb    r5, [r12, r5, lsl #2]           //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    lr, [r12, lr, lsl #2]           //
+        orr     r5, r5, r0, lsl #8              // merge with previous
+        orr     r4, r4, lr, lsl #8              //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    r1, [r12, r1, lsl #2]           //
+        orr     r7, r7, r0, lsl #8              // merge with previous
+        orr     r6, r6, r1, lsl #8              //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    r1, [r12, r1, lsl #2]           //
+        ldrb    lr, [r12, lr, lsl #2]           //
+        ldrb    r2, [r12, r2, lsl #2]           //
+        orr     r6, r6, r0, lsl #16             // merge with previous
+        orr     r5, r5, r1, lsl #16             //
+        orr     r4, r4, lr, lsl #16             //
+        orr     r7, r7, r2, lsl #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    r1, [r12, r1, lsl #2]           //
+        ldrb    r2, [r12, r2, lsl #2]           //
+        ldrb    r3, [r12, r3, lsl #2]           //
+        orr     r7, r7, r0, lsl #24             // merge with previous
+        orr     r6, r6, r1, lsl #24             //
+        orr     r5, r5, r2, lsl #24             //
+        orr     r4, r4, r3, lsl #24             //
+        sub     r12, r12, #1                    // put r12 back to its original value
+
+        //
+        // xor in final round key
+        //
+
+        ldrd    r0, r1, [r9, #0]                // fetch key into r0-r3
+        ldrd    r2, r3, [r9, #8]                //
+        eors    r4, r4, r0                      // exclusive-OR the key and rotate into final
+        eor     r5, r1, r5, ror #8              //   position
+        eor     r6, r2, r6, ror #16             //
+        eor     r7, r3, r7, ror #24             //
+.endm
+
+.macro AES_DECRYPT  loopLabel
+
+        //
+        // Input:
+        //   r0,r1,r2,r3 = ciphertext
+        //   r8 = pointer to first round key to use
+        //   r9 = pointer to last key to use
+        //   r10 = pointer to InvSbox
+        //   r12 = pointer to InvSboxMatrixMult
+        //
+        // Output:
+        //   r4,r5,r6,r7 = plaintext
+        //   r8 = modified to point to last key
+        //   r9 = unmodified
+        //   r10 = unmodified
+        //   r12 = unmodified
+        //
+        // Used:
+        //   lr is also modified
+        //
+
+        //
+        // xor in first round key
+        //
+        ldrd    r4, r5, [r8, #0]                // fetch key in r4-r7
+        ldrd    r6, r7, [r8, #8]                //
+        eors    r0, r0, r4                      // exclusive-OR with the plaintext
+        eors    r1, r1, r5                      //
+        eors    r2, r2, r6                      //
+        eors    r3, r3, r7                      //
+
+        add     r8, r8, #16                     // point to second key
+
+\loopLabel:
+        //
+        // Block is r0, r1, r2, r3
+        // r8 points to current round key
+        //
+
+        DEC_MIX r8                              // decrypt the block and increment key
+        cmp     r8, r9                          // are we at the end?
+        blo     \loopLabel                       // loop until it is so
+
+        //
+        // Now for the final round
+        // Result is in r4, r5, r6, r7
+        //
+
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r5, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r7, r0, ror #24                 //
+        ldrb    r4, [r10, r4]                   // perform lookups of each byte, leaving
+        ldrb    r5, [r10, r5]                   //   the values unrotated for now
+        ldrb    r6, [r10, r6]                   //
+        ldrb    r7, [r10, r7]                   //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    lr, [r10, lr]                   //
+        orr     r5, r5, r0, lsl #24             // merge with previous
+        orr     r6, r6, lr, lsl #24             //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    r1, [r10, r1]                   //
+        orr     r7, r7, r0, lsl #24             // merge with previous
+        orr     r4, r4, r1, lsl #24             //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    r1, [r10, r1]                   //
+        ldrb    lr, [r10, lr]                   //
+        ldrb    r2, [r10, r2]                   //
+        orrs    r6, r6, r0, lsl #16             // merge with previous
+        orr     r7, r7, r1, lsl #16             //
+        orr     r4, r4, lr, lsl #16             //
+        orr     r5, r5, r2, lsl #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    r1, [r10, r1]                   //
+        ldrb    r2, [r10, r2]                   //
+        ldrb    r3, [r10, r3]                   //
+        orr     r7, r7, r0, lsl #8              // merge with previous
+        orr     r4, r4, r1, lsl #8              //
+        orr     r5, r5, r2, lsl #8              //
+        orr     r6, r6, r3, lsl #8              //
+
+        //
+        // xor in final round key
+        //
+
+        ldrd    r0, r1, [r9, #0]                // fetch key into r0-r3
+        ldrd    r2, r3, [r9, #8]                //
+        eors    r4, r4, r0                      // exclusive-OR the key and rotate into final
+        eor     r5, r1, r5, ror #24             //   position
+        eor     r6, r2, r6, ror #16             //
+        eor     r7, r3, r7, ror #8              //
+
+.endm
+
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesEncrypt( _In_                                         PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )   PCBYTE                      pbPlaintext,
+//                     _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbCiphertext )
+//
+//        NESTED_ENTRY    SymCryptAesEncryptAsm
+SymCryptAesEncryptAsm:
+.globl SymCryptAesEncryptAsm
+.def SymCryptAesEncryptAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptAesEncryptAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_endprologue
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbPlaintext
+        //   r2 = pbCiphertext
+        //
+
+//        push {r2, r4-r11, lr}
+        push {r2}
+
+        //
+        // Stack layout:
+        //   [sp] = r2 = pbCipherText
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesEncryptAsm_check_magic_label
+
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r9 = last key
+        mov     r8, r0                          // r8 = first key
+        // mov32   r12, SymCryptAesSboxMatrixMult  // r12 = matrix mult table
+        ldr   r12, =SymCryptAesSboxMatrixMult  // r12 = matrix mult table
+
+        ldr     r0, [r1, #0]                    // load the plaintext
+        ldr     r2, [r1, #8]                    //
+        ldr     r3, [r1, #12]                   //
+        ldr     r1, [r1, #4]                    //
+
+        AES_ENCRYPT SymCryptAesEncryptAsm_loopLabel
+        //
+        // Plaintext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r12 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        ldr     r0, [sp]                        // recover pbCipherText
+        str     r4, [r0, #0]                    // store the encrypted data
+        str     r5, [r0, #4]                    //
+        str     r6, [r0, #8]                    //
+        str     r7, [r0, #12]                   //
+
+//        pop {r2, r4-r11, pc}             // return
+        pop {r2}
+
+//        NESTED_END      SymCryptAesEncryptAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
+.seh_endproc
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesDecrypt( _In_                                         PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )   PCBYTE                      pbCiphertext,
+//                     _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbPlaintext )
+//
+//        NESTED_ENTRY    SymCryptAesDecryptAsm
+SymCryptAesDecryptAsm:
+.globl SymCryptAesDecryptAsm
+.def SymCryptAesDecryptAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptAesDecryptAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_endprologue
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbCiphertext
+        //   r2 = pbPlaintext
+        //
+
+        // push {r2, r4-r11, lr}
+        push {r2}
+
+        //
+        // Stack layout:
+        //   [sp] = r2 = pbPlaintext
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesDecryptAsm_check_magic_label
+
+        ldr     r8, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r8 = first key
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey] // r9 = last key
+        // mov32   r10, SymCryptAesInvSbox         // r10 = inverse sbox table
+        // mov32   r12, SymCryptAesInvSboxMatrixMult // r11 = inverse matrix mult table
+        ldr   r10, =SymCryptAesInvSbox         // r10 = inverse sbox table
+        ldr   r12, =SymCryptAesInvSboxMatrixMult // r11 = inverse matrix mult table
+
+        ldr     r0, [r1, #0]                    // load the ciphertext
+        ldr     r2, [r1, #8]                    //
+        ldr     r3, [r1, #12]                   //
+        ldr     r1, [r1, #4]                    //
+
+        AES_DECRYPT SymCryptAesDecryptAsm_loopLabel
+        //
+        // Ciphertext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r10 points to InvSbox (unchanged)
+        // r12 points to InvSboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        ldr     r0, [sp]                        // recover pbPlaintext
+        str     r4, [r0, #0]                    // store the decrypted data
+        str     r5, [r0, #4]                    //
+        str     r6, [r0, #8]                    //
+        str     r7, [r0, #12]                   //
+
+        pop {r2}
+        // pop {r2, r4-r11, pc}             // return
+
+//        NESTED_END      SymCryptAesDecryptAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
+.seh_endproc
+
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesCbcEncrypt(
+//     _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbChainingValue,
+//     _In_reads_bytes_( cbData )                  PCBYTE                      pbSrc,
+//     _Out_writes_bytes_( cbData )                PBYTE                       pbDst,
+//                                                 SIZE_T                      cbData )
+
+//        NESTED_ENTRY    SymCryptAesCbcEncryptAsm
+SymCryptAesCbcEncryptAsm:
+.globl SymCryptAesCbcEncryptAsm
+.def SymCryptAesCbcEncryptAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptAesCbcEncryptAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_endprologue
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbChainingValue
+        //   r2 = pbSrc
+        //   r3 = pbDst
+        //   [sp] = cbData
+        //
+
+        // push {r0-r2, r4-r11, lr}
+        push {r0-r2}
+        sub sp, sp, #16
+
+        //
+        // Stack layout:
+        //   [sp] = pbSrc
+        //   [sp+4] = pbSrcEnd
+        //   [sp+16] = r0 = pbExpandedKey
+        //   [sp+20] = r1 = pbChainingValue
+        //   [sp+24] = r2 = pbSrc
+        //   [sp+64] = cbData
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesCbcEncryptAsm_check_magic_label
+
+        pld     [r2]                    // prefetch source data
+        ldr     r4, [sp, #64]           // r4 = cbData
+        mov     r8, r2                  // r8 = pbSrc on loop entry
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r9 = last enc round key (invariant)
+        mov     r10, r3                 // r10 = pbDst
+        // mov32   r12, SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        ldr     r12, =SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        bics    r4, r4, #15             // r4 &= ~15
+        beq     SymCryptAesCbcEncryptNoData // skip if no data
+        adds    r4, r4, r2              // r4 = pbSrc + cbData
+        // strd    r2, r4, [sp]            // save pbSrc/pbSrcEnd at [sp]
+        str     r2, [sp]
+        str     r4, [sp, #4]
+        pld     [r8, #32]               // prefetch source data
+        ldr     r4, [r1, #0]            // load chaining state from pbChainingValue
+        ldr     r5, [r1, #4]            //
+        ldr     r6, [r1, #8]            //
+        ldr     r7, [r1, #12]           //
+
+SymCryptAesCbcEncryptAsmLoop:
+        //
+        // Loop register setup
+        //   r4,r5,r6,r7 = chaining state
+        //   r8 = pbSrc
+        //   r9 = last round key to use
+        //   r10 = pbDst
+        //   r12 = SboxMatrixMult
+        //
+
+        ldr     r0, [r8]                // read next 16 bytes of plaintext
+        ldr     r1, [r8, #4]            //
+        ldr     r2, [r8, #8]            //
+        ldr     r3, [r8, #12]           //
+        pld     [r8, #64]               // prefetch source data
+        add     r8, r8, #16             // pbSrc += 16
+        str     r8, [sp]                // save it
+
+        eors    r0, r0, r4              // exclusive-OR against chaining value
+        eors    r1, r1, r5              //
+        eors    r2, r2, r6              //
+        eors    r3, r3, r7              //
+
+        ldr     r8, [sp, #16]           // r8 = first round key
+        AES_ENCRYPT SymCryptAesCbcEncryptAsm_loopLabel
+        //
+        // Plaintext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r12 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        // ldrd    r8, r0, [sp]            // fetch pbSrc/pbSrcEnd
+        ldr     r8, [sp]
+        ldr     r0, [sp, #4]
+
+        str     r4, [r10, #0]           // write ciphertext
+        str     r5, [r10, #4]           //
+        str     r6, [r10, #8]           //
+        str     r7, [r10, #12]          //
+        add     r10, r10, #16           // pbDst += 16
+
+        cmp     r8, r0                  // are we at the end of source?
+        blo     SymCryptAesCbcEncryptAsmLoop // loop until we are
+
+        ldr     r0, [sp, #20]           // r0 = pbChainingValue
+        str     r4, [r0, #0]            // update the chaining value
+        str     r5, [r0, #4]            //
+        str     r6, [r0, #8]            //
+        str     r7, [r0, #12]           //
+
+SymCryptAesCbcEncryptNoData:
+
+        add sp, sp, #16
+        pop {r0-r2}
+        // pop {r0-r2, r4-r11, pc}  // return
+
+//        NESTED_END      SymCryptAesCbcEncryptAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
+.seh_endproc
+
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesCbcDecrypt(
+//     _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbChainingValue,
+//     _In_reads_bytes_( cbData )                  PCBYTE                      pbSrc,
+//     _Out_writes_bytes_( cbData )                PBYTE                       pbDst,
+//                                                 SIZE_T                      cbData )
+
+//        NESTED_ENTRY    SymCryptAesCbcDecryptAsm
+SymCryptAesCbcDecryptAsm:
+.globl SymCryptAesCbcDecryptAsm
+.def SymCryptAesCbcDecryptAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptAesCbcDecryptAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_endprologue
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbChainingValue
+        //   r2 = pbSrc
+        //   r3 = pbDst
+        //   [sp] = cbData
+        //
+
+        // push {r0-r2, r4-r11, lr}
+        push {r0-r2}
+        sub sp, sp, #32
+
+        //
+        // Stack layout:
+        //   [sp] = pbSrc
+        //   [sp+4] = pbDst
+        //   [sp+8] = pbSrcEnd
+        //   [sp+16] = saved chaining value
+        //   [sp+32] = r0 = pbExpandedKey
+        //   [sp+36] = r1 = pbChainingValue
+        //   [sp+40] = r2 = pbSrc
+        //   [sp+80] = cbData
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesCbcDecryptAsm_check_magic_label
+
+        ldr     r4, [sp, #80]           // r4 = cbData
+        bics    r4, r4, #15             // r4 &= ~15
+        beq     SymCryptAesCbcDecryptNoData // skip if no data
+
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey] // r9 = last enc round key (invariant)
+        ldr     r8, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]
+
+        subs    r4, r4, #16
+        adds    r3, r3, r4
+        adds    r2, r2, r4
+        pld     [r2]                    // prefetch source data
+        str     r3, [sp, #4]
+        str     r2, [sp, #0]
+        str     r8, [sp, #8]
+
+        // mov32   r10, SymCryptAesInvSbox
+        // mov32   r12, SymCryptAesInvSboxMatrixMult
+        ldr   r10, =SymCryptAesInvSbox
+        ldr   r12, =SymCryptAesInvSboxMatrixMult
+
+        //
+        // Load last ciphertext block & save on stack (we need to put it in the pbChaining buffer later)
+        //
+        pld     [r2, #-32]              // prefetch source data
+        ldr     r0, [r2, #0]
+        ldr     r1, [r2, #4]
+        ldr     r3, [r2, #12]
+        ldr     r2, [r2, #8]
+
+        strd    r0, r1, [sp, #16]
+        strd    r2, r3, [sp, #24]
+
+        b       SymCryptAesCbcDecryptAsmLoopEntry
+
+SymCryptAesCbcDecryptAsmLoop:
+        // Loop register setup
+        // r13 = first round key to use
+        // r14 = pbSrc
+        // r15 = pbDst
+        // [callerP3Home] = pbSrcStart
+
+        // current ciphertext block (esi,edi,ebp,r8d)
+
+        ldr     r0, [r8, #-16]
+        ldr     r1, [r8, #-12]
+        ldr     r2, [r8, #-8]
+        ldr     r3, [r8, #-4]
+        pld     [r8, #-64]              // prefetch source data
+
+        eors    r4, r4, r0
+        eors    r5, r5, r1
+        eors    r6, r6, r2
+        eors    r7, r7, r3
+
+        str     r4, [lr, #0]
+        str     r5, [lr, #4]
+        str     r6, [lr, #8]
+        str     r7, [lr, #12]
+
+        sub     lr, lr, #16
+        sub     r8, r8, #16
+        str     r8, [sp]
+        str     lr, [sp, #4]
+
+SymCryptAesCbcDecryptAsmLoopEntry:
+
+        ldr     r8, [sp, #8]
+
+        AES_DECRYPT SymCryptAesCbcDecryptAsm_loopLabel
+
+        // ldrd    r8, lr, [sp, #0]
+        ldr     r8, [sp, #0]
+        ldr     lr, [sp, #4]
+        ldr     r0, [sp, #40]
+        cmp     r8, r0
+        bhi     SymCryptAesCbcDecryptAsmLoop
+
+        ldr     r8, [sp, #36]           // r8 = pbChainingValue
+        ldr     r0, [r8, #0]
+        ldr     r1, [r8, #4]
+        ldr     r2, [r8, #8]
+        ldr     r3, [r8, #12]
+
+        eors    r4, r4, r0
+        eors    r5, r5, r1
+        eors    r6, r6, r2
+        eors    r7, r7, r3
+
+        str     r4, [lr, #0]
+        str     r5, [lr, #4]
+        str     r6, [lr, #8]
+        str     r7, [lr, #12]
+
+        //
+        // Update the chaining value to the last ciphertext block
+        //
+        ldrd    r0, r1, [sp, #16]
+        ldrd    r2, r3, [sp, #24]
+        str     r0, [r8, #0]
+        str     r1, [r8, #4]
+        str     r2, [r8, #8]
+        str     r3, [r8, #12]
+
+SymCryptAesCbcDecryptNoData:
+
+        add sp, sp, #32
+        pop {r0-r2}
+        // pop {r0-r2, r4-r11, pc}
+
+//        NESTED_END      SymCryptAesCbcDecryptAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
+.seh_endproc
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesCtrMsb64(
+//     _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbChainingValue,
+//     _In_reads_bytes_( cbData )                  PCBYTE                      pbSrc,
+//     _Out_writes_bytes_( cbData )                PBYTE                       pbDst,
+//                                                 SIZE_T                      cbData )
+
+// NESTED_ENTRY    SymCryptAesCtrMsb64Asm
+SymCryptAesCtrMsb64Asm:
+.globl SymCryptAesCtrMsb64Asm
+.def SymCryptAesCtrMsb64Asm; .scl 2; .type 32; .endef
+.seh_proc SymCryptAesCtrMsb64Asm
+push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,lr}
+.seh_endprologue
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbChainingValue
+        //   r2 = pbSrc
+        //   r3 = pbDst
+        //   [sp] = cbData
+        //
+
+        // push {r0-r2, r4-r11, lr}
+        push {r0-r2}
+        sub sp, sp, #32
+
+        //
+        // Stack layout:
+        //   [sp] = pbDst
+        //   [sp+4] = pbSrcEnd
+        //   [sp+16] = local copy of chaining data
+        //   [sp+32] = r0 = pbExpandedKey
+        //   [sp+36] = r1 = pbChainingValue
+        //   [sp+40] = r2 = pbSrc
+        //   [sp+80] = cbData
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesCtrMsb64Asm_check_magic_label
+
+        pld     [r2]                    // prefetch source data
+        ldr     r4, [sp, #80]           // r4 = cbData
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r9 = last enc round key (invariant)
+        mov     r10, r2                 // r10 = pbSrc
+        // mov32   r12, SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        ldr   r12, =SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        bics    r4, r4, #15             // r4 &= ~15
+        beq     SymCryptAesCtrMsb64NoData // skip if no data
+        adds    r4, r4, r2              // r4 = pbSrc + cbData
+        // strd    r3, r4, [sp]            // save pbDst/pbSrcEnd at [sp]
+        str     r3, [sp]            // save pbDst at [sp]
+        str     r4, [sp, #4]            // save pbSrcEnd at [sp, #4]
+
+        pld     [r10, #32]              // prefetch source data
+        mov     r3, r1                  // load chaining state from pbChainingValue
+        ldr     r0, [r3, #0]            //
+        ldr     r1, [r3, #4]            //
+        ldr     r2, [r3, #8]            //
+        ldr     r3, [r3, #12]           //
+
+        strd    r0, r1, [sp, #16]       // save a local copy
+        strd    r2, r3, [sp, #24]       //
+
+SymCryptAesCtrMsb64AsmLoop:
+        //
+        // Loop register setup
+        //   r0,r1,r2,r3 = chaining state
+        //   r8 = pbSrc
+        //   r9 = last round key to use
+        //   r10 = pbSrc
+        //   r12 = SboxMatrixMult
+        //
+
+        ldr     r8, [sp, #32]           // r8 = first round key
+        AES_ENCRYPT loopLabel
+        //
+        // Plaintext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r12 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        ldr     r0, [r10, #0]           // load plaintext
+        ldr     r1, [r10, #4]           //
+        ldr     r2, [r10, #8]           //
+        ldr     r3, [r10, #12]          //
+        pld     [r10, #64]              // prefetch source data
+
+        // ldrd    r8, lr, [sp]            // fetch pbDst/pbSrcEnd
+        ldr     r8, [sp]
+        ldr     lr, [sp, #4]
+
+        eors    r0, r0, r4              // exclusive-OR against encrypt results
+        eors    r1, r1, r5              //
+        eors    r2, r2, r6              //
+        eors    r3, r3, r7              //
+
+        str     r0, [r8, #0]            // store to destination
+        str     r1, [r8, #4]            //
+        str     r2, [r8, #8]            //
+        str     r3, [r8, #12]           //
+
+        ldrd    r0, r1, [sp, #16]       // load chaining state
+        ldrd    r2, r3, [sp, #24]       //
+
+        add     r8, r8, #16             // pbDst += 16
+        add     r10, r10, #16           // pbSrc += 16
+        str     r8, [sp]                // save pbDst
+
+        rev     r3, r3                  // reverse the second qword
+        rev     r2, r2                  //
+        adds    r3, r3, #1              // increment the counter
+        adcs    r2, r2, #0              //
+        rev     r3, r3                  // re-reverse the second word
+        rev     r2, r2                  //
+        strd    r2, r3, [sp, #24]       // write updated state
+
+        cmp     r10, lr                 // done?
+        blo     SymCryptAesCtrMsb64AsmLoop // loop until finished
+
+        ldr     r0, [sp, #36]           // get pbChainingValue
+        movs    r1, #0                  // get 0 in r1
+        str     r2, [r0, #8]            // write back modified part of chaining state
+        str     r3, [r0, #12]           //
+        // strd    r1, r1, [sp, #16]       // wipe the stack copy
+        // strd    r1, r1, [sp, #24]       //
+        str     r1, [sp, #16]
+        str     r1, [sp, #20]
+        str     r1, [sp, #24]
+        str     r1, [sp, #28]
+
+SymCryptAesCtrMsb64NoData:
+
+        add sp, sp, #32
+        pop {r0-r2}
+        // pop {r0-r2, r4-r11, pc}  // return
+
+//        NESTED_END      SymCryptAesCtrMsb64Asm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
+.seh_endproc
diff --git a/libs/symcrypt/lib/arm/aesasm.symcryptasm b/libs/symcrypt/lib/arm/aesasm.symcryptasm
new file mode 100644
index 000000000000..37be22c1ce1c
--- /dev/null
+++ b/libs/symcrypt/lib/arm/aesasm.symcryptasm
@@ -0,0 +1,977 @@
+//
+//  AesAsm.cppasm   Assembler code for fast AES on ARM
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This code is derived from the AMD64 version of the AesFast
+// implementation, developed by Niels Ferguson. For questions
+// about the ARM specifics, contact Aaron Giles.
+//
+
+// #include "kxarm.h"
+
+#include "symcryptasm_shared.cppasm"
+
+#if SYMCRYPT_DEBUG
+SET(SYMCRYPT_CODE_VERSION, ((SYMCRYPT_CODE_VERSION_API * 65536) + SYMCRYPT_CODE_VERSION_MINOR))
+SET(SYMCRYPT_MAGIC_CONSTANT, HEX(53316D76) + SYMCRYPT_CODE_VERSION) // 0x53316D76 == 'S1mv'
+
+MACRO_START(SYMCRYPT_CHECK_MAGIC, temp1, temp2, ptr, offset, check_magic_label)
+        ldr     temp1, [ptr, #offset]
+        subs    temp1, temp1, ptr
+        //mov32   temp2, =SYMCRYPT_MAGIC_CONSTANT
+        ldr     temp2, =SYMCRYPT_MAGIC_CONSTANT
+        cmp     temp1, temp2
+        beq     check_magic_label
+        //mov32   r0, 0x6d616763     // 'magc'
+        ldr     r0, =0x6d616763     // 'magc'
+        bl      SymCryptFatal
+check_magic_label:
+MACRO_END()
+#else
+MACRO_START(SYMCRYPT_CHECK_MAGIC, temp1, temp2, ptr, offset, check_magic_label)
+MACRO_END()
+#endif
+
+
+//        TTL   "Advanced Encryption Standard (AES)"
+
+//
+// typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_AES_EXPANDED_KEY {
+//     SYMCRYPT_ALIGN BYTE RoundKey[29][4][4];
+//            Round keys, first the encryption round keys in encryption order,
+//            followed by the decryption round keys in decryption order.
+//            The first decryption round key is the last encryption round key.
+//            AES-256 has 14 rounds and thus 15 round keys for encryption and 15
+//            for decryption. As they share one round key, we need room for 29.
+//     BYTE   (*lastEncRoundKey)[4][4];       Pointer to last encryption round key
+//                                            also the first round key for decryption
+//     BYTE   (*lastDecRoundKey)[4][4];       Pointer to last decryption round key.
+//
+//     SYMCRYPT_MAGIC_FIELD
+// } SYMCRYPT_AES_EXPANDED_KEY, *PSYMCRYPT_AES_EXPANDED_KEY
+//
+
+#define SYMCRYPT_AES_EXPANDED_KEY_RoundKey          (0)
+#define SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey   (29*4*4)
+#define SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey   (29*4*4+4)
+
+#if SYMCRYPT_DEBUG
+#define SYMCRYPT_AES_EXPANDED_KEY_magic             (29*4*4+4+4)
+#endif
+
+MACRO_START(ENC_MIX, keyptr)
+        //
+        // Perform the unkeyed mixing function for encryption
+        // plus a key addition from the key pointer
+        //
+        // Input:
+        //   r0,r1,r2,r3 = current block
+        //   keyptr = pointer to current key
+        //   r12 = pointer to AesSboxMatrixMult
+        //
+        // Output:
+        //   r0,r1,r2,r3 = updated block
+        //   keyptr = updated to point to following key
+        //   r12 = unmodified
+        //
+        // Used:
+        //   r4,r5,r6,r7,lr are modified
+        //
+        // N.B. To make better use of ARM's barrel shifter, this code differs
+        //      from the AMD64 approach. The first lookups are not rotated;
+        //      instead all subsequent lookups are applied on top rotated, and
+        //      then a final rotation is performed to shift the bits into the
+        //      proper spot.
+        //
+
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r7, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r5, r0, ror #24                 //
+        ldr     r4, [r12, r4, lsl #2]           // perform lookups of each byte, leaving
+        ldr     r7, [r12, r7, lsl #2]           //   the values unrotated for now
+        ldr     r6, [r12, r6, lsl #2]           //
+        ldr     r5, [r12, r5, lsl #2]           //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     lr, [r12, lr, lsl #2]           //
+        eor     r5, r5, r0, ror #24             // exclusive-OR with previous
+        eor     r4, r4, lr, ror #24             //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        eor     r7, r7, r0, ror #24             // exclusive-OR with previous
+        eor     r6, r6, r1, ror #24             //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     lr, [r12, lr, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        eor     r6, r6, r0, ror #16             // exclusive-OR with previous
+        eor     r5, r5, r1, ror #16             //
+        eor     r4, r4, lr, ror #16             //
+        eor     r7, r7, r2, ror #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        ldr     r3, [r12, r3, lsl #2]           //
+        eor     r7, r7, r0, ror #8              // exclusive-OR with previous
+        eor     r6, r6, r1, ror #8              //
+        eor     r5, r5, r2, ror #8              //
+        eor     r4, r4, r3, ror #8              //
+
+        ldrd    r0, r1, [keyptr, #0]            // fetch key into r0-r3
+        ldrd    r2, r3, [keyptr, #8]            //
+        adds    keyptr, keyptr, #16             // increment key pointer
+        eors    r0, r0, r4                      // exclusive-OR the key and rotate into final
+        eor     r1, r1, r5, ror #8              //   position
+        eor     r2, r2, r6, ror #16             //
+        eor     r3, r3, r7, ror #24             //
+MACRO_END()
+
+MACRO_START(DEC_MIX, keyptr)
+        //
+        // Perform the unkeyed mixing function for decryption
+        //
+        // Input:
+        //   r0,r1,r2,r3 = current block
+        //   keyptr = pointer to current key
+        //   r12 = pointer to AesInvSboxMatrixMult
+        //
+        // Output:
+        //   r0,r1,r2,r3 = updated block
+        //   keyptr = updated to point to following key
+        //   r12 = unmodified
+        //
+        // Used:
+        //   r4,r5,r6,r7,lr are modified
+        //
+        // N.B. To make better use of ARM's barrel shifter, this code differs
+        //      from the AMD64 approach. The first lookups are not rotated;
+        //      instead all subsequent lookups are applied on top rotated, and
+        //      then a final rotation is performed to shift the bits into the
+        //      proper spot.
+        //
+
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r5, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r7, r0, ror #24                 //
+        ldr     r4, [r12, r4, lsl #2]           // perform lookups of each byte, leaving
+        ldr     r5, [r12, r5, lsl #2]           //   the values unrotated for now
+        ldr     r6, [r12, r6, lsl #2]           //
+        ldr     r7, [r12, r7, lsl #2]           //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     lr, [r12, lr, lsl #2]           //
+        eor     r5, r5, r0, ror #8              // exclusive-OR with previous
+        eor     r6, r6, lr, ror #8              //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        eor     r7, r7, r0, ror #8              // exclusive-OR with previous
+        eor     r4, r4, r1, ror #8              //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     lr, [r12, lr, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        eor     r6, r6, r0, ror #16             // exclusive-OR with previous
+        eor     r7, r7, r1, ror #16             //
+        eor     r4, r4, lr, ror #16             //
+        eor     r5, r5, r2, ror #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldr     r0, [r12, r0, lsl #2]           // perform lookups
+        ldr     r1, [r12, r1, lsl #2]           //
+        ldr     r2, [r12, r2, lsl #2]           //
+        ldr     r3, [r12, r3, lsl #2]           //
+        eor     r7, r7, r0, ror #24             // exclusive-OR with previous
+        eor     r4, r4, r1, ror #24             //
+        eor     r5, r5, r2, ror #24             //
+        eor     r6, r6, r3, ror #24             //
+
+        ldrd    r0, r1, [keyptr, #0]            // fetch key into r0-r3
+        ldrd    r2, r3, [keyptr, #8]            //
+        adds    keyptr, keyptr, #16             // increment key pointer
+        eors    r0, r0, r4                      // exclusive-OR the key and rotate into final
+        eor     r1, r1, r5, ror #24             //   position
+        eor     r2, r2, r6, ror #16             //
+        eor     r3, r3, r7, ror #8              //
+MACRO_END()
+
+MACRO_START(AES_ENCRYPT, loopLabel)
+
+        //
+        // Input:
+        //   r0,r1,r2,r3 = plaintext
+        //   r8 = pointer to first round key to use
+        //   r9 = pointer to last key to use
+        //   r12 = pointer to AesSboxMatrixMult
+        //
+        // Output:
+        //   r4,r5,r6,r7 = ciphertext
+        //   r8 = modified to point to last key
+        //   r9 = unmodified
+        //   r12 = unmodified
+        //
+        // Used:
+        //   lr is also modified
+        //
+
+        //
+        // xor in first round key
+        //
+
+        ldrd    r4, r5, [r8, #0]                // fetch key in r4-r7
+        ldrd    r6, r7, [r8, #8]                //
+        eors    r0, r0, r4                      // exclusive-OR with the plaintext
+        eors    r1, r1, r5                      //
+        eors    r2, r2, r6                      //
+        eors    r3, r3, r7                      //
+
+        add     r8, r8, #16                     // point to second key
+
+loopLabel:
+        //
+        // Block is r0,r1,r2,r3
+        // r8 points to current round key
+        //
+
+        ENC_MIX r8                              // encrypt the block and increment key
+        cmp     r8, r9                          // are we at the end?
+        blo     loopLabel                       // loop until it is so
+
+        //
+        // Now for the final round
+        // We use the fact that SboxMatrixMult[0] table is also
+        // an Sbox table if you use the second element of each entry.
+        //
+        // Result is in r4,r5,r6,r7
+        //
+
+        add     r12, r12, #1                    // advance by 1 to point to second element
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r7, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r5, r0, ror #24                 //
+        ldrb    r4, [r12, r4, lsl #2]           // perform lookups of each byte, leaving
+        ldrb    r7, [r12, r7, lsl #2]           //   the values unrotated for now
+        ldrb    r6, [r12, r6, lsl #2]           //
+        ldrb    r5, [r12, r5, lsl #2]           //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    lr, [r12, lr, lsl #2]           //
+        orr     r5, r5, r0, lsl #8              // merge with previous
+        orr     r4, r4, lr, lsl #8              //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    r1, [r12, r1, lsl #2]           //
+        orr     r7, r7, r0, lsl #8              // merge with previous
+        orr     r6, r6, r1, lsl #8              //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    r1, [r12, r1, lsl #2]           //
+        ldrb    lr, [r12, lr, lsl #2]           //
+        ldrb    r2, [r12, r2, lsl #2]           //
+        orr     r6, r6, r0, lsl #16             // merge with previous
+        orr     r5, r5, r1, lsl #16             //
+        orr     r4, r4, lr, lsl #16             //
+        orr     r7, r7, r2, lsl #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldrb    r0, [r12, r0, lsl #2]           // perform lookups
+        ldrb    r1, [r12, r1, lsl #2]           //
+        ldrb    r2, [r12, r2, lsl #2]           //
+        ldrb    r3, [r12, r3, lsl #2]           //
+        orr     r7, r7, r0, lsl #24             // merge with previous
+        orr     r6, r6, r1, lsl #24             //
+        orr     r5, r5, r2, lsl #24             //
+        orr     r4, r4, r3, lsl #24             //
+        sub     r12, r12, #1                    // put r12 back to its original value
+
+        //
+        // xor in final round key
+        //
+
+        ldrd    r0, r1, [r9, #0]                // fetch key into r0-r3
+        ldrd    r2, r3, [r9, #8]                //
+        eors    r4, r4, r0                      // exclusive-OR the key and rotate into final
+        eor     r5, r1, r5, ror #8              //   position
+        eor     r6, r2, r6, ror #16             //
+        eor     r7, r3, r7, ror #24             //
+MACRO_END()
+
+MACRO_START(AES_DECRYPT, loopLabel)
+
+        //
+        // Input:
+        //   r0,r1,r2,r3 = ciphertext
+        //   r8 = pointer to first round key to use
+        //   r9 = pointer to last key to use
+        //   r10 = pointer to InvSbox
+        //   r12 = pointer to InvSboxMatrixMult
+        //
+        // Output:
+        //   r4,r5,r6,r7 = plaintext
+        //   r8 = modified to point to last key
+        //   r9 = unmodified
+        //   r10 = unmodified
+        //   r12 = unmodified
+        //
+        // Used:
+        //   lr is also modified
+        //
+
+        //
+        // xor in first round key
+        //
+        ldrd    r4, r5, [r8, #0]                // fetch key in r4-r7
+        ldrd    r6, r7, [r8, #8]                //
+        eors    r0, r0, r4                      // exclusive-OR with the plaintext
+        eors    r1, r1, r5                      //
+        eors    r2, r2, r6                      //
+        eors    r3, r3, r7                      //
+
+        add     r8, r8, #16                     // point to second key
+
+loopLabel:
+        //
+        // Block is r0, r1, r2, r3
+        // r8 points to current round key
+        //
+
+        DEC_MIX r8                              // decrypt the block and increment key
+        cmp     r8, r9                          // are we at the end?
+        blo     loopLabel                       // loop until it is so
+
+        //
+        // Now for the final round
+        // Result is in r4, r5, r6, r7
+        //
+
+        uxtb    r4, r0                          // extract individual bytes from r0
+        uxtb    r5, r0, ror #8                  //
+        uxtb    r6, r0, ror #16                 //
+        uxtb    r7, r0, ror #24                 //
+        ldrb    r4, [r10, r4]                   // perform lookups of each byte, leaving
+        ldrb    r5, [r10, r5]                   //   the values unrotated for now
+        ldrb    r6, [r10, r6]                   //
+        ldrb    r7, [r10, r7]                   //
+
+        uxtb    r0, r1                          // extract individual bytes from r1
+        uxtb    lr, r1, ror #8                  //   (with 1 more register we could do 4 at a time)
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    lr, [r10, lr]                   //
+        orr     r5, r5, r0, lsl #24             // merge with previous
+        orr     r6, r6, lr, lsl #24             //
+        uxtb    r0, r1, ror #16                 // extract remaining bytes from r1
+        uxtb    r1, r1, ror #24                 //
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    r1, [r10, r1]                   //
+        orr     r7, r7, r0, lsl #24             // merge with previous
+        orr     r4, r4, r1, lsl #24             //
+
+        uxtb    r0, r2                          // extract individual bytes from r2
+        uxtb    r1, r2, ror #8                  //
+        uxtb    lr, r2, ror #16                 //
+        uxtb    r2, r2, ror #24                 //
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    r1, [r10, r1]                   //
+        ldrb    lr, [r10, lr]                   //
+        ldrb    r2, [r10, r2]                   //
+        orrs    r6, r6, r0, lsl #16             // merge with previous
+        orr     r7, r7, r1, lsl #16             //
+        orr     r4, r4, lr, lsl #16             //
+        orr     r5, r5, r2, lsl #16             //
+
+        uxtb    r0, r3                          // extract individual bytes from r3
+        uxtb    r1, r3, ror #8                  //
+        uxtb    r2, r3, ror #16                 //
+        uxtb    r3, r3, ror #24                 //
+        ldrb    r0, [r10, r0]                   // perform lookups
+        ldrb    r1, [r10, r1]                   //
+        ldrb    r2, [r10, r2]                   //
+        ldrb    r3, [r10, r3]                   //
+        orr     r7, r7, r0, lsl #8              // merge with previous
+        orr     r4, r4, r1, lsl #8              //
+        orr     r5, r5, r2, lsl #8              //
+        orr     r6, r6, r3, lsl #8              //
+
+        //
+        // xor in final round key
+        //
+
+        ldrd    r0, r1, [r9, #0]                // fetch key into r0-r3
+        ldrd    r2, r3, [r9, #8]                //
+        eors    r4, r4, r0                      // exclusive-OR the key and rotate into final
+        eor     r5, r1, r5, ror #24             //   position
+        eor     r6, r2, r6, ror #16             //
+        eor     r7, r3, r7, ror #8              //
+
+MACRO_END()
+
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesEncrypt( _In_                                         PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )   PCBYTE                      pbPlaintext,
+//                     _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbCiphertext )
+//
+//        NESTED_ENTRY    SymCryptAesEncryptAsm
+FUNCTION_START(SymCryptAesEncryptAsm, 3, 12)
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbPlaintext
+        //   r2 = pbCiphertext
+        //
+
+//        push {r2, r4-r11, lr}
+        push {r2}
+
+        //
+        // Stack layout:
+        //   [sp] = r2 = pbCipherText
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesEncryptAsm_check_magic_label
+
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r9 = last key
+        mov     r8, r0                          // r8 = first key
+        // mov32   r12, SymCryptAesSboxMatrixMult  // r12 = matrix mult table
+        ldr   r12, =SymCryptAesSboxMatrixMult  // r12 = matrix mult table
+
+        ldr     r0, [r1, #0]                    // load the plaintext
+        ldr     r2, [r1, #8]                    //
+        ldr     r3, [r1, #12]                   //
+        ldr     r1, [r1, #4]                    //
+
+        AES_ENCRYPT SymCryptAesEncryptAsm_loopLabel
+        //
+        // Plaintext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r12 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        ldr     r0, [sp]                        // recover pbCipherText
+        str     r4, [r0, #0]                    // store the encrypted data
+        str     r5, [r0, #4]                    //
+        str     r6, [r0, #8]                    //
+        str     r7, [r0, #12]                   //
+
+//        pop {r2, r4-r11, pc}             // return
+        pop {r2}
+
+//        NESTED_END      SymCryptAesEncryptAsm
+FUNCTION_END(SymCryptAesEncryptAsm)
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesDecrypt( _In_                                         PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )   PCBYTE                      pbCiphertext,
+//                     _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbPlaintext )
+//
+//        NESTED_ENTRY    SymCryptAesDecryptAsm
+FUNCTION_START(SymCryptAesDecryptAsm, 3, 12)
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbCiphertext
+        //   r2 = pbPlaintext
+        //
+
+        // push {r2, r4-r11, lr}
+        push {r2}
+
+        //
+        // Stack layout:
+        //   [sp] = r2 = pbPlaintext
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesDecryptAsm_check_magic_label
+
+        ldr     r8, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r8 = first key
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey] // r9 = last key
+        // mov32   r10, SymCryptAesInvSbox         // r10 = inverse sbox table
+        // mov32   r12, SymCryptAesInvSboxMatrixMult // r11 = inverse matrix mult table
+        ldr   r10, =SymCryptAesInvSbox         // r10 = inverse sbox table
+        ldr   r12, =SymCryptAesInvSboxMatrixMult // r11 = inverse matrix mult table
+
+        ldr     r0, [r1, #0]                    // load the ciphertext
+        ldr     r2, [r1, #8]                    //
+        ldr     r3, [r1, #12]                   //
+        ldr     r1, [r1, #4]                    //
+
+        AES_DECRYPT SymCryptAesDecryptAsm_loopLabel
+        //
+        // Ciphertext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r10 points to InvSbox (unchanged)
+        // r12 points to InvSboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        ldr     r0, [sp]                        // recover pbPlaintext
+        str     r4, [r0, #0]                    // store the decrypted data
+        str     r5, [r0, #4]                    //
+        str     r6, [r0, #8]                    //
+        str     r7, [r0, #12]                   //
+
+        pop {r2}
+        // pop {r2, r4-r11, pc}             // return
+
+//        NESTED_END      SymCryptAesDecryptAsm
+FUNCTION_END(SymCryptAesDecryptAsm)
+
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesCbcEncrypt(
+//     _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbChainingValue,
+//     _In_reads_bytes_( cbData )                  PCBYTE                      pbSrc,
+//     _Out_writes_bytes_( cbData )                PBYTE                       pbDst,
+//                                                 SIZE_T                      cbData )
+
+//        NESTED_ENTRY    SymCryptAesCbcEncryptAsm
+FUNCTION_START(SymCryptAesCbcEncryptAsm, 4, 12)
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbChainingValue
+        //   r2 = pbSrc
+        //   r3 = pbDst
+        //   [sp] = cbData
+        //
+
+        // push {r0-r2, r4-r11, lr}
+        push {r0-r2}
+        sub sp, sp, #16
+
+        //
+        // Stack layout:
+        //   [sp] = pbSrc
+        //   [sp+4] = pbSrcEnd
+        //   [sp+16] = r0 = pbExpandedKey
+        //   [sp+20] = r1 = pbChainingValue
+        //   [sp+24] = r2 = pbSrc
+        //   [sp+64] = cbData
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesCbcEncryptAsm_check_magic_label
+
+        pld     [r2]                    // prefetch source data
+        ldr     r4, [sp, #64]           // r4 = cbData
+        mov     r8, r2                  // r8 = pbSrc on loop entry
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r9 = last enc round key (invariant)
+        mov     r10, r3                 // r10 = pbDst
+        // mov32   r12, SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        ldr     r12, =SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        bics    r4, r4, #15             // r4 &= ~15
+        beq     SymCryptAesCbcEncryptNoData // skip if no data
+        adds    r4, r4, r2              // r4 = pbSrc + cbData
+        // strd    r2, r4, [sp]            // save pbSrc/pbSrcEnd at [sp]
+        str     r2, [sp]
+        str     r4, [sp, #4]
+        pld     [r8, #32]               // prefetch source data
+        ldr     r4, [r1, #0]            // load chaining state from pbChainingValue
+        ldr     r5, [r1, #4]            //
+        ldr     r6, [r1, #8]            //
+        ldr     r7, [r1, #12]           //
+
+SymCryptAesCbcEncryptAsmLoop:
+        //
+        // Loop register setup
+        //   r4,r5,r6,r7 = chaining state
+        //   r8 = pbSrc
+        //   r9 = last round key to use
+        //   r10 = pbDst
+        //   r12 = SboxMatrixMult
+        //
+
+        ldr     r0, [r8]                // read next 16 bytes of plaintext
+        ldr     r1, [r8, #4]            //
+        ldr     r2, [r8, #8]            //
+        ldr     r3, [r8, #12]           //
+        pld     [r8, #64]               // prefetch source data
+        add     r8, r8, #16             // pbSrc += 16
+        str     r8, [sp]                // save it
+
+        eors    r0, r0, r4              // exclusive-OR against chaining value
+        eors    r1, r1, r5              //
+        eors    r2, r2, r6              //
+        eors    r3, r3, r7              //
+
+        ldr     r8, [sp, #16]           // r8 = first round key
+        AES_ENCRYPT SymCryptAesCbcEncryptAsm_loopLabel
+        //
+        // Plaintext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r12 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        // ldrd    r8, r0, [sp]            // fetch pbSrc/pbSrcEnd
+        ldr     r8, [sp]
+        ldr     r0, [sp, #4]
+
+        str     r4, [r10, #0]           // write ciphertext
+        str     r5, [r10, #4]           //
+        str     r6, [r10, #8]           //
+        str     r7, [r10, #12]          //
+        add     r10, r10, #16           // pbDst += 16
+
+        cmp     r8, r0                  // are we at the end of source?
+        blo     SymCryptAesCbcEncryptAsmLoop // loop until we are
+
+        ldr     r0, [sp, #20]           // r0 = pbChainingValue
+        str     r4, [r0, #0]            // update the chaining value
+        str     r5, [r0, #4]            //
+        str     r6, [r0, #8]            //
+        str     r7, [r0, #12]           //
+
+SymCryptAesCbcEncryptNoData:
+
+        add sp, sp, #16
+        pop {r0-r2}
+        // pop {r0-r2, r4-r11, pc}  // return
+
+//        NESTED_END      SymCryptAesCbcEncryptAsm
+FUNCTION_END(SymCryptAesCbcEncryptAsm)
+
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesCbcDecrypt(
+//     _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbChainingValue,
+//     _In_reads_bytes_( cbData )                  PCBYTE                      pbSrc,
+//     _Out_writes_bytes_( cbData )                PBYTE                       pbDst,
+//                                                 SIZE_T                      cbData )
+
+//        NESTED_ENTRY    SymCryptAesCbcDecryptAsm
+FUNCTION_START(SymCryptAesCbcDecryptAsm, 4, 12)
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbChainingValue
+        //   r2 = pbSrc
+        //   r3 = pbDst
+        //   [sp] = cbData
+        //
+
+        // push {r0-r2, r4-r11, lr}
+        push {r0-r2}
+        sub sp, sp, #32
+
+        //
+        // Stack layout:
+        //   [sp] = pbSrc
+        //   [sp+4] = pbDst
+        //   [sp+8] = pbSrcEnd
+        //   [sp+16] = saved chaining value
+        //   [sp+32] = r0 = pbExpandedKey
+        //   [sp+36] = r1 = pbChainingValue
+        //   [sp+40] = r2 = pbSrc
+        //   [sp+80] = cbData
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesCbcDecryptAsm_check_magic_label
+
+        ldr     r4, [sp, #80]           // r4 = cbData
+        bics    r4, r4, #15             // r4 &= ~15
+        beq     SymCryptAesCbcDecryptNoData // skip if no data
+
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey] // r9 = last enc round key (invariant)
+        ldr     r8, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]
+
+        subs    r4, r4, #16
+        adds    r3, r3, r4
+        adds    r2, r2, r4
+        pld     [r2]                    // prefetch source data
+        str     r3, [sp, #4]
+        str     r2, [sp, #0]
+        str     r8, [sp, #8]
+
+        // mov32   r10, SymCryptAesInvSbox
+        // mov32   r12, SymCryptAesInvSboxMatrixMult
+        ldr   r10, =SymCryptAesInvSbox
+        ldr   r12, =SymCryptAesInvSboxMatrixMult
+
+        //
+        // Load last ciphertext block & save on stack (we need to put it in the pbChaining buffer later)
+        //
+        pld     [r2, #-32]              // prefetch source data
+        ldr     r0, [r2, #0]
+        ldr     r1, [r2, #4]
+        ldr     r3, [r2, #12]
+        ldr     r2, [r2, #8]
+
+        strd    r0, r1, [sp, #16]
+        strd    r2, r3, [sp, #24]
+
+        b       SymCryptAesCbcDecryptAsmLoopEntry
+
+SymCryptAesCbcDecryptAsmLoop:
+        // Loop register setup
+        // r13 = first round key to use
+        // r14 = pbSrc
+        // r15 = pbDst
+        // [callerP3Home] = pbSrcStart
+
+        // current ciphertext block (esi,edi,ebp,r8d)
+
+        ldr     r0, [r8, #-16]
+        ldr     r1, [r8, #-12]
+        ldr     r2, [r8, #-8]
+        ldr     r3, [r8, #-4]
+        pld     [r8, #-64]              // prefetch source data
+
+        eors    r4, r4, r0
+        eors    r5, r5, r1
+        eors    r6, r6, r2
+        eors    r7, r7, r3
+
+        str     r4, [lr, #0]
+        str     r5, [lr, #4]
+        str     r6, [lr, #8]
+        str     r7, [lr, #12]
+
+        sub     lr, lr, #16
+        sub     r8, r8, #16
+        str     r8, [sp]
+        str     lr, [sp, #4]
+
+SymCryptAesCbcDecryptAsmLoopEntry:
+
+        ldr     r8, [sp, #8]
+
+        AES_DECRYPT SymCryptAesCbcDecryptAsm_loopLabel
+
+        // ldrd    r8, lr, [sp, #0]
+        ldr     r8, [sp, #0]
+        ldr     lr, [sp, #4]
+        ldr     r0, [sp, #40]
+        cmp     r8, r0
+        bhi     SymCryptAesCbcDecryptAsmLoop
+
+        ldr     r8, [sp, #36]           // r8 = pbChainingValue
+        ldr     r0, [r8, #0]
+        ldr     r1, [r8, #4]
+        ldr     r2, [r8, #8]
+        ldr     r3, [r8, #12]
+
+        eors    r4, r4, r0
+        eors    r5, r5, r1
+        eors    r6, r6, r2
+        eors    r7, r7, r3
+
+        str     r4, [lr, #0]
+        str     r5, [lr, #4]
+        str     r6, [lr, #8]
+        str     r7, [lr, #12]
+
+        //
+        // Update the chaining value to the last ciphertext block
+        //
+        ldrd    r0, r1, [sp, #16]
+        ldrd    r2, r3, [sp, #24]
+        str     r0, [r8, #0]
+        str     r1, [r8, #4]
+        str     r2, [r8, #8]
+        str     r3, [r8, #12]
+
+SymCryptAesCbcDecryptNoData:
+
+        add sp, sp, #32
+        pop {r0-r2}
+        // pop {r0-r2, r4-r11, pc}
+
+//        NESTED_END      SymCryptAesCbcDecryptAsm
+FUNCTION_END(SymCryptAesCbcDecryptAsm)
+
+//
+// VOID
+// SYMCRYPT_CALL
+// SymCryptAesCtrMsb64(
+//     _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//     _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbChainingValue,
+//     _In_reads_bytes_( cbData )                  PCBYTE                      pbSrc,
+//     _Out_writes_bytes_( cbData )                PBYTE                       pbDst,
+//                                                 SIZE_T                      cbData )
+
+// NESTED_ENTRY    SymCryptAesCtrMsb64Asm
+FUNCTION_START(SymCryptAesCtrMsb64Asm, 4, 12):
+
+        //
+        // Input parameters:
+        //   r0 = pExpandedKey
+        //   r1 = pbChainingValue
+        //   r2 = pbSrc
+        //   r3 = pbDst
+        //   [sp] = cbData
+        //
+
+        // push {r0-r2, r4-r11, lr}
+        push {r0-r2}
+        sub sp, sp, #32
+
+        //
+        // Stack layout:
+        //   [sp] = pbDst
+        //   [sp+4] = pbSrcEnd
+        //   [sp+16] = local copy of chaining data
+        //   [sp+32] = r0 = pbExpandedKey
+        //   [sp+36] = r1 = pbChainingValue
+        //   [sp+40] = r2 = pbSrc
+        //   [sp+80] = cbData
+        //
+
+        SYMCRYPT_CHECK_MAGIC r4, r5, r0, SYMCRYPT_AES_EXPANDED_KEY_magic, SymCryptAesCtrMsb64Asm_check_magic_label
+
+        pld     [r2]                    // prefetch source data
+        ldr     r4, [sp, #80]           // r4 = cbData
+        ldr     r9, [r0, #SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey] // r9 = last enc round key (invariant)
+        mov     r10, r2                 // r10 = pbSrc
+        // mov32   r12, SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        ldr   r12, =SymCryptAesSboxMatrixMult // r12 = pointer to lookup table (invariant)
+        bics    r4, r4, #15             // r4 &= ~15
+        beq     SymCryptAesCtrMsb64NoData // skip if no data
+        adds    r4, r4, r2              // r4 = pbSrc + cbData
+        // strd    r3, r4, [sp]            // save pbDst/pbSrcEnd at [sp]
+        str     r3, [sp]            // save pbDst at [sp]
+        str     r4, [sp, #4]            // save pbSrcEnd at [sp, #4]
+
+        pld     [r10, #32]              // prefetch source data
+        mov     r3, r1                  // load chaining state from pbChainingValue
+        ldr     r0, [r3, #0]            //
+        ldr     r1, [r3, #4]            //
+        ldr     r2, [r3, #8]            //
+        ldr     r3, [r3, #12]           //
+
+        strd    r0, r1, [sp, #16]       // save a local copy
+        strd    r2, r3, [sp, #24]       //
+
+SymCryptAesCtrMsb64AsmLoop:
+        //
+        // Loop register setup
+        //   r0,r1,r2,r3 = chaining state
+        //   r8 = pbSrc
+        //   r9 = last round key to use
+        //   r10 = pbSrc
+        //   r12 = SboxMatrixMult
+        //
+
+        ldr     r8, [sp, #32]           // r8 = first round key
+        AES_ENCRYPT loopLabel
+        //
+        // Plaintext in r0, r1, r2, r3
+        // r8 points to first round key to use
+        // r9 is last key to use (unchanged)
+        // r12 points to SboxMatrixMult (unchanged)
+        // Ciphertext ends up in r4, r5, r6, r7
+        //
+
+        ldr     r0, [r10, #0]           // load plaintext
+        ldr     r1, [r10, #4]           //
+        ldr     r2, [r10, #8]           //
+        ldr     r3, [r10, #12]          //
+        pld     [r10, #64]              // prefetch source data
+
+        // ldrd    r8, lr, [sp]            // fetch pbDst/pbSrcEnd
+        ldr     r8, [sp]
+        ldr     lr, [sp, #4]
+
+        eors    r0, r0, r4              // exclusive-OR against encrypt results
+        eors    r1, r1, r5              //
+        eors    r2, r2, r6              //
+        eors    r3, r3, r7              //
+
+        str     r0, [r8, #0]            // store to destination
+        str     r1, [r8, #4]            //
+        str     r2, [r8, #8]            //
+        str     r3, [r8, #12]           //
+
+        ldrd    r0, r1, [sp, #16]       // load chaining state
+        ldrd    r2, r3, [sp, #24]       //
+
+        add     r8, r8, #16             // pbDst += 16
+        add     r10, r10, #16           // pbSrc += 16
+        str     r8, [sp]                // save pbDst
+
+        rev     r3, r3                  // reverse the second qword
+        rev     r2, r2                  //
+        adds    r3, r3, #1              // increment the counter
+        adcs    r2, r2, #0              //
+        rev     r3, r3                  // re-reverse the second word
+        rev     r2, r2                  //
+        strd    r2, r3, [sp, #24]       // write updated state
+
+        cmp     r10, lr                 // done?
+        blo     SymCryptAesCtrMsb64AsmLoop // loop until finished
+
+        ldr     r0, [sp, #36]           // get pbChainingValue
+        movs    r1, #0                  // get 0 in r1
+        str     r2, [r0, #8]            // write back modified part of chaining state
+        str     r3, [r0, #12]           //
+        // strd    r1, r1, [sp, #16]       // wipe the stack copy
+        // strd    r1, r1, [sp, #24]       //
+        str     r1, [sp, #16]
+        str     r1, [sp, #20]
+        str     r1, [sp, #24]
+        str     r1, [sp, #28]
+
+SymCryptAesCtrMsb64NoData:
+
+        add sp, sp, #32
+        pop {r0-r2}
+        // pop {r0-r2, r4-r11, pc}  // return
+
+//        NESTED_END      SymCryptAesCtrMsb64Asm
+FUNCTION_END(SymCryptAesCtrMsb64Asm)
diff --git a/libs/symcrypt/lib/arm/fdef_asm.S b/libs/symcrypt/lib/arm/fdef_asm.S
new file mode 100644
index 000000000000..2ade273d2245
--- /dev/null
+++ b/libs/symcrypt/lib/arm/fdef_asm.S
@@ -0,0 +1,832 @@
+//
+//  fdef_asm.cppasm   Assembler code for large integer arithmetic in the default data format for the arm architecture
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+// #include "ksarm.h"
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 32 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+// SymCryptFdefRawAdd(
+//   _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//   _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//   _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                           UINT32      nDigits )
+//
+// Initial inputs to registers:
+//       pSrc1       -> r0
+//       pSrc2       -> r1
+//       pDst        -> r2
+//       nDigits     -> r3
+
+//    LEAF_ENTRY SymCryptFdefRawAddAsm
+SymCryptFdefRawAddAsm:
+.globl SymCryptFdefRawAddAsm
+.def SymCryptFdefRawAddAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptFdefRawAddAsm
+push {r4,r5,r6,r7,r8,r9,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,lr}
+.seh_endprologue
+//    push     {r4-r9, lr}
+
+    neg     r3, r3                  // negate the digit count
+    mov     r8, #0                  // carry = r8 = 0
+    mov     r9, #0                  // r9 = 0
+
+SymCryptFdefRawAddAsmLoop:
+    rrxs    r8, r8                  // set the carry flag if bit[0] of r8 is set
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    adcs    r4, r4, r5
+    adcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    adcs    r4, r4, r5
+    adcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    adc     r8, r9, r9              // r8 = 1 if the carry flag is set
+
+    adds    r3, r3, #1              // Increment the digit count by one
+    bne     SymCryptFdefRawAddAsmLoop
+
+    mov     r0, r8                  // Set the return value equal to the carry
+
+//    pop      {r4-r9, pc}
+
+//    LEAF_END SymCryptFdefRawAddAsm
+pop {r4,r5,r6,r7,r8,r9,pc}
+.seh_endproc
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSub(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+//
+// Initial inputs to registers:
+//       pSrc1       -> r0
+//       pSrc2       -> r1
+//       pDst        -> r2
+//       nDigits     -> r3
+
+//    LEAF_ENTRY SymCryptFdefRawSubAsm
+SymCryptFdefRawSubAsm:
+.globl SymCryptFdefRawSubAsm
+.def SymCryptFdefRawSubAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptFdefRawSubAsm
+push {r4,r5,r6,r7,r8,r9,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,lr}
+.seh_endprologue
+//    push     {r4-r9, lr}
+
+    neg     r3, r3                  // negate the digit count
+    mov     r8, #0                  // borrow = r8 = 0
+    mov     r9, #0                  // r9 = 0
+
+SymCryptFdefRawSubAsmLoop:
+    subs    r8, r9, r8              // if r8>0 then the "borrow flag" is set
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    sbc     r8, r9, r9              // If borrow=1, then r8 = -1 = 0xffffffff
+
+    adds    r3, r3, #1              // Increment the digit count by one
+    bne     SymCryptFdefRawSubAsmLoop
+
+    and     r0, r8, #1             // If r8>0, set the return value to 1
+
+//    pop      {r4-r9, pc}
+
+//    LEAF_END SymCryptFdefRawSubAsm
+pop {r4,r5,r6,r7,r8,r9,pc}
+.seh_endproc
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopy(
+//    _In_reads_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )      PCBYTE      pbSrc,
+//    _InOut_writes_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )  PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+//    LEAF_ENTRY SymCryptFdefMaskedCopyAsm
+SymCryptFdefMaskedCopyAsm:
+.globl SymCryptFdefMaskedCopyAsm
+.def SymCryptFdefMaskedCopyAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptFdefMaskedCopyAsm
+push {r4,r5,r6,r7,r8,r9,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,lr}
+.seh_endprologue
+//    push     {r4-r9, lr}
+
+    neg     r2, r2                  // negate the digit count
+    mov     r9, #0                  // r9 = 0
+
+    subs    r4, r9, r3              // If (r3 > 0) clear the carry flag (i.e. borrow)
+    sbc     r3, r9, r9              // r3 = mask = 0xffffffff if the carry flag is clear
+    // orn     r9, r9, r3              // r9 = NOT(MASK) = 0 if r3 = 0xffffffff
+    orr     r9, r9, r3
+    mvn     r9, r9
+
+    mov     r8, r1                  // save the destination pointer
+
+SymCryptFdefMaskedCopyAsmLoop:
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    adds    r2, r2, #1              // Increment the digit count by one
+    bne     SymCryptFdefMaskedCopyAsmLoop
+
+    // Done, no return value
+
+//    pop      {r4-r9, pc}
+
+//    LEAF_END SymCryptFdefMaskedCopyAsm
+pop {r4,r5,r6,r7,r8,r9,pc}
+.seh_endproc
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nWords1)             PCUINT32    pSrc1,
+//                                    UINT32      nDigits1,
+//    _In_reads_(nWords2)             PCUINT32    pSrc2,
+//                                    UINT32      nDigits2,
+//    _Out_writes_(nWords1 + nWords2) PUINT32     pDst )
+//
+// Initial inputs to registers:
+//       pSrc1       -> r0
+//       nDigits1    -> r1
+//       pSrc2       -> r2
+//       nDigits2    -> r3
+//       pDst        -> In the stack
+//
+// Basic structure:
+//   for each 2 words in Src1:
+//       Dst += Src2 * (2 words of Src1)
+//
+// Register assignments
+//       r0  = pSrc1 (moving forward one word every outer loop)
+//       r1  = negated word count of pSrc1
+//       r2  = pSrc2 (moving forward one *digit* every inner loop)
+//       r3  = negated digit count of pSrc2 and pDst
+//       r4  = pDst (moving forward one *digit* every inner loop)
+//       r5  = Stored pDst (moving forward one word every outer loop)
+//       r6, r7  = Current words loaded from pSrc1
+//       r8, r9  = Current words loaded from pSrc2
+//       <r12:r11:r10> = "96-bit" sliding register to hold the result of multiplies
+//
+// Stack assignments
+#define pSrc2 0               // Stored pSrc2 in stack
+#define nDigits2 4               // Stored negated digit count of pSrc2 in stack
+
+
+//    LEAF_ENTRY SymCryptFdefRawMulAsm
+SymCryptFdefRawMulAsm:
+.globl SymCryptFdefRawMulAsm
+.def SymCryptFdefRawMulAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptFdefRawMulAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
+.seh_endprologue
+    // push         {r4-r12, lr}
+    sub     sp, sp, #8
+
+    lsl     r1, r1, #2                  // Calculate word count
+
+    ldr     r4, [sp, #(8+4*10)]         // load pDst
+
+    neg     r1, r1                      // negate nWords1
+    neg     r3, r3                      // negate nDigits2
+
+    mov     r5, r4                      // store pDst
+    str     r2, [sp, #pSrc2]            // store pSrc2
+    str     r3, [sp, #nDigits2]         // store -nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    mov     r11, #0                     // Setting r11 = 0
+    mov     r12, #0                     // and r12 = 0
+    ldmia   r0!, {r6, r7}               // Load two words from pSrc1
+
+SymCryptFdefRawMulAsmLoopInner1:
+
+    adds    r3, r3, #1                  // move one digit up
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    bne     SymCryptFdefRawMulAsmLoopInner1
+
+    stmia   r4, {r11, r12}              // Store the top two words in the destination
+
+    add     r1, r1, #2                  // move two words up
+    add     r5, r5, #8                  // move start of pDst two words up
+
+    //
+    // MAIN LOOP
+    //
+SymCryptFdefRawMulAsmLoopOuter:
+    ldr     r3, [sp, #nDigits2]         // set -nDigits2
+    ldr     r2, [sp, #pSrc2]            // set pSrc2
+    mov     r4, r5                      // set pDst
+
+    mov     r11, #0                     // Setting r11 = 0
+    mov     r12, #0                     // and r12 = 0
+    ldmia   r0!, {r6, r7}               // Load two words from pSrc1
+
+SymCryptFdefRawMulAsmLoopInner:
+
+    adds    r3, r3, #1                  // move one digit up
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    bne     SymCryptFdefRawMulAsmLoopInner
+
+    adds    r1, r1, #2                  // move two words up
+    add     r5, r5, #8                  // move start of pDst two words up
+
+    stmia   r4, {r11, r12}              // Store the top two words in the destination
+
+    bne     SymCryptFdefRawMulAsmLoopOuter
+
+    // Done, no return value
+
+    add     sp, sp, #8
+    // pop          {r4-r12, pc}
+
+//    LEAF_END SymCryptFdefRawMulAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,r12,pc}
+.seh_endproc
+
+    // Macro for the first loop of the first pass of RawSquareAsm.
+    // It takes one word from the source, multiplies it with the mulword,
+    // adds the high level word of the previous macro call, and stores it into
+    // the destination.
+    //
+    // No word is taken from the destination// thus r10 is always set to 0.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^32-1)^2 + 2^32-1 = 2^64 - 2^32
+.macro SQR_SINGLEADD_32  index
+
+        mov     r10, #0
+        ldr     r8, [r2, #4*\index]     // pSrc[i+j]
+
+        umaal   r10, r11, r6, r8        // <r11:r10> = r6 * r8 + r10 + r11
+
+        str     r10, [r4, #4*\index]    // Store to destination
+.endm
+
+    // Macro for the remaining loops of the first pass of RawSquareAsm.
+    // The only difference to the above is that it also adds the word loaded
+    // from the destination buffer.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^32-1)^2 + 2(2^32-1) = 2^64 - 1
+.macro SQR_DOUBLEADD_32  index
+
+        ldr     r8, [r2, #4*\index]     // pSrc[i+j]
+        ldr     r10, [r4, #4*\index]    // pDst[2*(i+j)]
+
+        umaal   r10, r11, r6, r8        // <r11:r10> = r6 * r8 + r10 + r11
+
+        str     r10, [r4, #4*\index]    // Store to destination
+
+.endm
+
+    // Macro for the third pass loop of RawSquareAsm.
+    // It takes one mulword from the source, squares it, and
+    // adds it to the even columns of the destination. The carries are propagated
+    // to the odd columns.
+    //
+    // Here we can have a (1-bit) carry to the next call because the maximum value for
+    // a pair of columns is (2^32-1)^2+(2^64-1)+1 = 2^65 - 2^33 + 1 < 2^65 - 1
+.macro SQR_DIAGONAL_PROP  index
+        ldr     r6, [r0, #4*\index]     // mulword
+
+        umull   r10, r11, r6, r6
+
+        ldr     r8, [r4, #8*\index]     // Load
+        ldr     r9, [r4, #8*\index + 4] // Load
+
+        // Adding the square to the even column
+        adcs    r10, r10, r8            // carry from previous and update the flags
+
+        // Propagating the sum to the next column
+        adcs    r11, r11, r9            // This can generate a carry
+
+        str     r10, [r4, #8*\index]    // Store
+        str     r11, [r4, #8*\index + 4]// Store
+.endm
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                       UINT32      nDigits,
+//   _Out_writes_(2*nWords)                              PUINT32     pDst )
+//
+// Initial inputs to registers:
+//       pSrc        -> r0
+//       nDigits     -> r1
+//       pDst        -> r2
+//
+// Register assignments
+//       r0  = pSrc
+//       r1  = negated word count of pSrc
+//       r2  = pSrc (moving forward one digit / 4 words every inner loop)
+//       r3  = negated digit count of pSrc
+//       r4  = pDst (moving forward one digit every inner loop)
+//       r5  = pDst (moving forward one word every outer loop)
+//       r6  = mulword from pSrc
+//       r7  = Stored negated digit count of pSrc
+//       r8  = Current words loaded from pSrc
+//       r9  = Cyclic counter for the jumps
+//       r10, r11 = "64-bit" sliding register to hold the result of multiplies,
+//                   r10 also receives a word from pDst
+//       r12 = Negated digit counter of pSrc (updated every 4 iterations of main loop)
+//
+// Stack assignments
+#define pDstSq 0   // Stored pDst in stack
+#define pSrc 4   // Stored pSrc in stack
+
+
+//    LEAF_ENTRY SymCryptFdefRawSquareAsm
+SymCryptFdefRawSquareAsm:
+.globl SymCryptFdefRawSquareAsm
+.def SymCryptFdefRawSquareAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptFdefRawSquareAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
+.seh_endprologue
+    // push         {r4-r12, lr}
+    sub     sp, sp, #8
+
+
+    mov     r3, r1                      // digit count into r3
+
+    lsl     r1, r1, #2                  // Calculate word count
+
+    neg     r1, r1                      // negate nWords
+    neg     r3, r3                      // negate nDigitsSq
+
+    mov     r4, r2                      // pDst
+    mov     r5, r2                      // store pDst
+
+    str     r0, [sp, #pSrc]             // store pSrc
+    str     r5, [sp, #pDstSq]           // store pDst
+    mov     r7, r3                      // store -nDigits for later
+    mov     r12, r3                     // Negated digit counter of pSrc
+
+    mov     r2, r0                      // inner loop pSrc
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    r11, r11, #0                // Clearing the carry flag and setting r11 = 0
+    ldr     r6, [r0]                    // load the first word from pSrc1
+    str     r11, [r4]                   // store 0 for the first word
+
+    b       SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+SymCryptFdefRawSquareAsmInnerLoopInit_Word0:
+    SQR_SINGLEADD_32    0
+
+SymCryptFdefRawSquareAsmInnerLoopInit_Word1:
+    SQR_SINGLEADD_32    1
+
+    SQR_SINGLEADD_32    2
+
+    SQR_SINGLEADD_32    3
+
+
+    add     r2, r2, #16
+    add     r4, r4, #16
+
+    adds    r3, r3, #1                  // move one digit up
+    bne     SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+    str     r11, [r4]                   // Store the next word into the destination
+    add     r1, r1, #2                  // move two words up (so we stop when real word count is "-1")
+    mov     r9, #1                      // Cyclic counter
+
+    //
+    // MAIN LOOP
+    //
+SymCryptFdefRawSquareAsmOuterLoop:
+
+    add     r5, r5, #4                  // move start of pDst one word up
+
+    mov     r3, r12                     // set -nDigits
+    mov     r2, r0                      // set pSrc
+    mov     r4, r5                      // set pDst
+
+    ands    r11, r11, #0                // Clearing the carry flag and setting r11 = 0
+    ldr     r6, [r0, r9, LSL #2]        // load the next word from pSrc
+
+    // Cyclic counter and jump logic
+    add     r9, r9, #1
+    cmp     r9, #1
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word1
+    cmp     r9, #2
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word2
+    cmp     r9, #3
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word3
+
+    // The following instructions are only executed when r9 == 4
+    mov     r9, #0                      // Set it to 0
+
+    add     r0, r0, #16                 // move start of pSrc 4 words up
+    add     r5, r5, #16                 // move pDst 4 words up
+
+    mov     r2, r0                      // set pSrc
+    mov     r4, r5                      // set pDst
+
+    adds    r3, r3, #1                  // add 1 digit
+    mov     r12, r3                     // set the new digit counter
+
+SymCryptFdefRawSquareAsmInnerLoop_Word0:
+    SQR_DOUBLEADD_32    0
+
+SymCryptFdefRawSquareAsmInnerLoop_Word1:
+    SQR_DOUBLEADD_32    1
+
+SymCryptFdefRawSquareAsmInnerLoop_Word2:
+    SQR_DOUBLEADD_32    2
+
+SymCryptFdefRawSquareAsmInnerLoop_Word3:
+    SQR_DOUBLEADD_32    3
+
+
+    add     r2, r2, #16
+    add     r4, r4, #16
+    adds    r3, r3, #1              // move one digit up
+    bne     SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+    str     r11, [r4]               // Store the next word into the destination
+
+    adds    r1, r1, #1              // move one word up
+    bne     SymCryptFdefRawSquareAsmOuterLoop
+
+    eor     r11, r11, r11           // Setting r11 = 0
+    str     r11, [r5, #20]          // Store 0 to destination for the top word
+
+    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Second Pass - Shifting all results 1 bit left
+    // Third Pass - Adding the squares on the even columns and propagating the sum
+    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    mov     r3, r7                  // -nDigits
+    lsl     r3, r3, #1              // Double digits
+    ldr     r4, [sp, #pDstSq]       // pDst pointer
+    ands    r1, r1, #0              // Clear the flags
+    ands    r2, r2, #0              // Clear the flags
+
+SymCryptFdefRawSquareAsmSecondPass:
+    rrxs    r2, r2                  // set the carry flag if bit[0] of r2 is set
+
+    ldmia   r4, {r8, r9}
+    adcs    r8, r8, r8              // Shift left and add the carry
+    adcs    r9, r9, r9
+    stmia   r4!, {r8, r9}
+
+    ldmia   r4, {r10, r11}
+    adcs    r10, r10, r10           // Shift left and add the carry
+    adcs    r11, r11, r11
+    stmia   r4!, {r10, r11}
+
+    adc     r2, r1, r1
+
+    adds     r3, r3, #1             // move one digit up
+    bne     SymCryptFdefRawSquareAsmSecondPass
+
+
+
+    ldr     r0, [sp, #pSrc]         // src pointer
+    ldr     r4, [sp, #pDstSq]       // pDst pointer
+    // mov r3, r7                    // Use r7 as the digit counter
+    // ands    r1, r1, #0            // Clear the flags
+    ands    r2, r2, #0              // Clear the flags
+
+SymCryptFdefRawSquareAsmThirdPass:
+    rrxs    r2, r2                  // set the carry flag if bit[0] of r2 is set
+
+    SQR_DIAGONAL_PROP 0
+    SQR_DIAGONAL_PROP 1
+    SQR_DIAGONAL_PROP 2
+    SQR_DIAGONAL_PROP 3
+
+    adc     r2, r1, r1
+
+    add     r0, r0, #16             // One digit up (not updated in SQR_DIAGONAL_PROP)
+    add     r4, r4, #32             // Two digits up (not updated in SQR_DIAGONAL_PROP)
+
+    adds    r7, r7, #1              // move one digit up
+    bne     SymCryptFdefRawSquareAsmThirdPass
+
+    // Done, no return value
+
+    add     sp, sp, #8
+    // pop          {r4-r12, pc}
+
+//    LEAF_END SymCryptFdefRawSquareAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,r12,pc}
+.seh_endproc
+
+//VOID
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Initial inputs to registers:
+//       pmMod       -> r0
+//       pSrc        -> r1
+//       pDst        -> r2
+//
+// Register assignments
+//       r0  = pMod (moving forward one *digit* every inner loop)
+//       r1  = pSrc (moving forward one *digit* every inner loop)
+//       r2  = Stored pSrc (moving forward one word every outer loop)
+//       r3  = negated digit count of pSrc and pMod
+//       r4  = negated word count of pSrc
+//       r5, r6  = m = pSrc[i]*Inv64
+//       r7  = hc = high carry variable
+//       r8, r9 = Current words loaded from pMod
+//       <r12:r11:r10> = "96-bit" sliding register to hold the result of multiplies
+
+// Stack assignments
+#define pMod 0               // Stored pMod
+#define pDst 4               // Stored pDst
+#define nDigits 8               // Stored negated digit count of pSrc
+#define inv64 12              // Inv64 of modulus
+
+//    LEAF_ENTRY SymCryptFdefMontgomeryReduceAsm
+SymCryptFdefMontgomeryReduceAsm:
+.globl SymCryptFdefMontgomeryReduceAsm
+.def SymCryptFdefMontgomeryReduceAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptFdefMontgomeryReduceAsm
+push {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
+.seh_save_regs_w {r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
+.seh_endprologue
+    // push         {r4-r12, lr}
+    sub     sp, sp, #16
+
+    str     r2, [sp, #pDst]                             // Store pDst in the stack
+    ldr     r3, [r0, #SymCryptModulusNdigitsOffsetArm]  // # of Digits
+    ldr     r5, [r0, #SymCryptModulusInv64OffsetArm]    // Inv64 of modulus
+    add     r0, r0, #SymCryptModulusValueOffsetArm      // pMod
+    str     r5, [sp, #inv64]                            // Store inv64 in the stack
+
+    lsl     r4, r3, #2                  // Multiply by 4 to get the number of words
+
+    neg     r3, r3                      // Negate the digit count
+    neg     r4, r4                      // Negate the word count
+
+    str     r0, [sp, #pMod]             // Store the pMod pointer
+    mov     r2, r1                      // Store the pSrc pointer
+    str     r3, [sp, #nDigits]          // Store the digit count for later
+
+    eor     r7, r7, r7                  // Set hc to 0
+
+    //
+    // Main loop
+    //
+SymCryptFdefMontgomeryReduceAsmOuter:
+    ldr     r3, [sp, #inv64]            // Inv64 of modulus
+
+    ldmia   r1, {r10, r12}              // Load two words from pSrc
+    ldmia   r0, {r8,r9}                 // Load two words from pMod
+    mov     r11, #0
+    mul     r5, r10, r3                 // <31:0> bits of pSrc[i]*Inv64 = m1 (first multiplier)
+    umaal   r10, r11, r5, r8            // r11 <-- High( m1*pMod[0] + pSrc[i] )
+    umaal   r12, r11, r5, r9            // Calculate pSrc[i+1] = Low( m1*pMod[1] + pSrc[i+1] + High( m1*pMod[0] + pSrc[i] ))
+    mul     r6, r12, r3                 // <31:0> bits of pSrc[i+1]*Inv64 = m2
+
+    ldr     r3, [sp, #nDigits]          // Reset the digit counter
+    mov     r11, #0                     // Set c to 0
+    mov     r12, #0                     // Set c to 0
+
+SymCryptFdefMontgomeryReduceAsmInner:
+    adds    r3, r3, #1                  // Move one digit up (none of the commands updates the carry)
+
+    ldmia   r0!, {r8, r9}               // Load two words from pMod[]
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r8            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r8            // c = <r12:r11> = m2 * pMod[j] + c
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r9            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r9            // c = <r12:r11> = m2 * pMod[j] + c
+
+    ldmia   r0!, {r8, r9}               // Load two words from pMod[]
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r8            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r8            // c = <r12:r11> = m2 * pMod[j] + c
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r9            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r9            // c = <r12:r11> = m2 * pMod[j] + c
+
+    bne     SymCryptFdefMontgomeryReduceAsmInner
+
+    mov     r8, #0                      // r8 = 0
+    mov     r9, #0                      // r9 = 0
+
+    ldmia   r1, {r5, r6}                // Load pSrc[nWords] and pSrc[nWords+1]
+
+    adds    r11, r11, r5                // c + pSrc[nWords]
+    adc     r8, r8, #0                  // Add the carry if any
+    adds    r11, r11, r7                // c + pSrc[nWords] + hc
+    adc     r8, r8, #0                  // Add the carry if any
+    str     r11, [r1], #4               // pSrc[nWords] = c
+
+    adds    r12, r12, r6                // c + pSrc[nWords+1]
+    adc     r9, r9, #0                  // Add the carry if any
+    adds    r12, r12, r8                // c + pSrc[nWords] + hc
+    adc     r7, r9, #0                  // Add the carry if any
+    str     r12, [r1]                   // pSrc[nWords+1] = c
+
+    adds    r4, r4, #2                  // Move two words up
+
+    add     r2, r2, #8                  // Move stored pSrc pointer two words up
+    ldr     r0, [sp, #pMod]             // Restore the pMod pointer
+    mov     r1, r2                      // Restore the pSrc pointer
+
+    bne     SymCryptFdefMontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+
+    // Prepare the pointers for subtract
+    mov     r0, r2                  // pSrc
+    mov     r11, r2                 // Store pSrc for later
+    ldr     r1, [sp, #pMod]         // pMod
+    ldr     r2, [sp, #pDst]         // pDst
+    ldr     r3, [sp, #nDigits]      // Reset the digit counter
+
+    mov     r10, r7                 // r10 = hc
+
+    mov     r8, #0                  // borrow = r8 = 0
+    mov     r9, #0                  // r9 = 0
+
+SymCryptFdefMontgomeryReduceRawSubAsmLoop:
+    subs    r8, r9, r8              // if r8>0 then the "borrow flag" is set
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    sbc     r8, r9, r9              // If borrow=1, then r8 = -1 = 0xffffffff
+
+    adds    r3, r3, #1              // Increment the digit count by one
+    bne     SymCryptFdefMontgomeryReduceRawSubAsmLoop
+
+    // Prepare the pointers for masked copy
+    mov     r0, r11                 // pSrc
+    ldr     r1, [sp, #pDst]         // pDst
+
+    and     r9, r8, #1              // If r8>0, set the return value to 1
+    orr     r11, r10, r9            // r11 = hc|d
+
+    ldr     r2, [sp, #nDigits]      // Restore the digit counter
+
+    mov     r9, #0                  // r9 = 0
+
+    subs    r4, r10, r11            // If (r11 > r10) clear the carry flag (i.e. borrow)
+    sbc     r3, r9, r9              // r3 = mask = 0xffffffff if the carry flag is clear
+    // orn     r9, r9, r3              // r9 = NOT(MASK) = 0 if r3 = 0xffffffff
+    orr     r9, r9, r3
+    mvn     r9, r9
+    mov     r8, r1                  // save the destination pointer
+
+SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop:
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    adds    r2, r2, #1              // Increment the digit count by one
+    bne     SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    add     sp, sp, #16
+//    pop          {r4-r12, pc}
+
+//    LEAF_END SymCryptFdefMontgomeryReduceAsm
+pop {r4,r5,r6,r7,r8,r9,r10,r11,r12,pc}
+.seh_endproc
diff --git a/libs/symcrypt/lib/arm/fdef_asm.symcryptasm b/libs/symcrypt/lib/arm/fdef_asm.symcryptasm
new file mode 100644
index 000000000000..afdb4ee4f68c
--- /dev/null
+++ b/libs/symcrypt/lib/arm/fdef_asm.symcryptasm
@@ -0,0 +1,790 @@
+//
+//  fdef_asm.cppasm   Assembler code for large integer arithmetic in the default data format for the arm architecture
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+// #include "ksarm.h"
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 32 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+// SymCryptFdefRawAdd(
+//   _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//   _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//   _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                           UINT32      nDigits )
+//
+// Initial inputs to registers:
+//       pSrc1       -> r0
+//       pSrc2       -> r1
+//       pDst        -> r2
+//       nDigits     -> r3
+
+//    LEAF_ENTRY SymCryptFdefRawAddAsm
+FUNCTION_START(SymCryptFdefRawAddAsm, 4, 10)
+//    push     {r4-r9, lr}
+
+    neg     r3, r3                  // negate the digit count
+    mov     r8, #0                  // carry = r8 = 0
+    mov     r9, #0                  // r9 = 0
+
+SymCryptFdefRawAddAsmLoop:
+    rrxs    r8, r8                  // set the carry flag if bit[0] of r8 is set
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    adcs    r4, r4, r5
+    adcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    adcs    r4, r4, r5
+    adcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    adc     r8, r9, r9              // r8 = 1 if the carry flag is set
+
+    adds    r3, r3, #1              // Increment the digit count by one
+    bne     SymCryptFdefRawAddAsmLoop
+
+    mov     r0, r8                  // Set the return value equal to the carry
+
+//    pop      {r4-r9, pc}
+
+//    LEAF_END SymCryptFdefRawAddAsm
+FUNCTION_END(SymCryptFdefRawAddAsm)
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSub(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+//
+// Initial inputs to registers:
+//       pSrc1       -> r0
+//       pSrc2       -> r1
+//       pDst        -> r2
+//       nDigits     -> r3
+
+//    LEAF_ENTRY SymCryptFdefRawSubAsm
+FUNCTION_START(SymCryptFdefRawSubAsm, 4, 10)
+//    push     {r4-r9, lr}
+
+    neg     r3, r3                  // negate the digit count
+    mov     r8, #0                  // borrow = r8 = 0
+    mov     r9, #0                  // r9 = 0
+
+SymCryptFdefRawSubAsmLoop:
+    subs    r8, r9, r8              // if r8>0 then the "borrow flag" is set
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    sbc     r8, r9, r9              // If borrow=1, then r8 = -1 = 0xffffffff
+
+    adds    r3, r3, #1              // Increment the digit count by one
+    bne     SymCryptFdefRawSubAsmLoop
+
+    and     r0, r8, #1             // If r8>0, set the return value to 1
+
+//    pop      {r4-r9, pc}
+
+//    LEAF_END SymCryptFdefRawSubAsm
+FUNCTION_END(SymCryptFdefRawSubAsm)
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopy(
+//    _In_reads_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )      PCBYTE      pbSrc,
+//    _InOut_writes_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )  PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+//    LEAF_ENTRY SymCryptFdefMaskedCopyAsm
+FUNCTION_START(SymCryptFdefMaskedCopyAsm, 4, 10)
+//    push     {r4-r9, lr}
+
+    neg     r2, r2                  // negate the digit count
+    mov     r9, #0                  // r9 = 0
+
+    subs    r4, r9, r3              // If (r3 > 0) clear the carry flag (i.e. borrow)
+    sbc     r3, r9, r9              // r3 = mask = 0xffffffff if the carry flag is clear
+    // orn     r9, r9, r3              // r9 = NOT(MASK) = 0 if r3 = 0xffffffff
+    orr     r9, r9, r3
+    mvn     r9, r9
+
+    mov     r8, r1                  // save the destination pointer
+
+SymCryptFdefMaskedCopyAsmLoop:
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    adds    r2, r2, #1              // Increment the digit count by one
+    bne     SymCryptFdefMaskedCopyAsmLoop
+
+    // Done, no return value
+
+//    pop      {r4-r9, pc}
+
+//    LEAF_END SymCryptFdefMaskedCopyAsm
+FUNCTION_END(SymCryptFdefMaskedCopyAsm)
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nWords1)             PCUINT32    pSrc1,
+//                                    UINT32      nDigits1,
+//    _In_reads_(nWords2)             PCUINT32    pSrc2,
+//                                    UINT32      nDigits2,
+//    _Out_writes_(nWords1 + nWords2) PUINT32     pDst )
+//
+// Initial inputs to registers:
+//       pSrc1       -> r0
+//       nDigits1    -> r1
+//       pSrc2       -> r2
+//       nDigits2    -> r3
+//       pDst        -> In the stack
+//
+// Basic structure:
+//   for each 2 words in Src1:
+//       Dst += Src2 * (2 words of Src1)
+//
+// Register assignments
+//       r0  = pSrc1 (moving forward one word every outer loop)
+//       r1  = negated word count of pSrc1
+//       r2  = pSrc2 (moving forward one *digit* every inner loop)
+//       r3  = negated digit count of pSrc2 and pDst
+//       r4  = pDst (moving forward one *digit* every inner loop)
+//       r5  = Stored pDst (moving forward one word every outer loop)
+//       r6, r7  = Current words loaded from pSrc1
+//       r8, r9  = Current words loaded from pSrc2
+//       <r12:r11:r10> = "96-bit" sliding register to hold the result of multiplies
+//
+// Stack assignments
+#define pSrc2 0               // Stored pSrc2 in stack
+#define nDigits2 4               // Stored negated digit count of pSrc2 in stack
+
+
+//    LEAF_ENTRY SymCryptFdefRawMulAsm
+FUNCTION_START(SymCryptFdefRawMulAsm, 4, 13)
+    // push         {r4-r12, lr}
+    sub     sp, sp, #8
+
+    lsl     r1, r1, #2                  // Calculate word count
+
+    ldr     r4, [sp, #(8+4*10)]         // load pDst
+
+    neg     r1, r1                      // negate nWords1
+    neg     r3, r3                      // negate nDigits2
+
+    mov     r5, r4                      // store pDst
+    str     r2, [sp, #pSrc2]            // store pSrc2
+    str     r3, [sp, #nDigits2]         // store -nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    mov     r11, #0                     // Setting r11 = 0
+    mov     r12, #0                     // and r12 = 0
+    ldmia   r0!, {r6, r7}               // Load two words from pSrc1
+
+SymCryptFdefRawMulAsmLoopInner1:
+
+    adds    r3, r3, #1                  // move one digit up
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    mov     r10, #0                     // Setting r10 = 0
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    bne     SymCryptFdefRawMulAsmLoopInner1
+
+    stmia   r4, {r11, r12}              // Store the top two words in the destination
+
+    add     r1, r1, #2                  // move two words up
+    add     r5, r5, #8                  // move start of pDst two words up
+
+    //
+    // MAIN LOOP
+    //
+SymCryptFdefRawMulAsmLoopOuter:
+    ldr     r3, [sp, #nDigits2]         // set -nDigits2
+    ldr     r2, [sp, #pSrc2]            // set pSrc2
+    mov     r4, r5                      // set pDst
+
+    mov     r11, #0                     // Setting r11 = 0
+    mov     r12, #0                     // and r12 = 0
+    ldmia   r0!, {r6, r7}               // Load two words from pSrc1
+
+SymCryptFdefRawMulAsmLoopInner:
+
+    adds    r3, r3, #1                  // move one digit up
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    ldmia   r2!, {r8, r9}               // Load two words from pSrc2
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r8            // <r11:r10> = r6 * r8 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r8            // <r12:r11> = r7 * r8 + r11
+
+    ldr     r10, [r4]                   // load 1 word from pDst
+    umaal   r10, r11, r6, r9            // <r11:r10> = r6 * r9 + r10 + r11
+    str     r10, [r4], #4               // Store to destination
+    umaal   r11, r12, r7, r9            // <r12:r11> = r7 * r9 + r11
+
+    bne     SymCryptFdefRawMulAsmLoopInner
+
+    adds    r1, r1, #2                  // move two words up
+    add     r5, r5, #8                  // move start of pDst two words up
+
+    stmia   r4, {r11, r12}              // Store the top two words in the destination
+
+    bne     SymCryptFdefRawMulAsmLoopOuter
+
+    // Done, no return value
+
+    add     sp, sp, #8
+    // pop          {r4-r12, pc}
+
+//    LEAF_END SymCryptFdefRawMulAsm
+FUNCTION_END(SymCryptFdefRawMulAsm)
+
+    // Macro for the first loop of the first pass of RawSquareAsm.
+    // It takes one word from the source, multiplies it with the mulword,
+    // adds the high level word of the previous macro call, and stores it into
+    // the destination.
+    //
+    // No word is taken from the destination// thus r10 is always set to 0.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^32-1)^2 + 2^32-1 = 2^64 - 2^32
+MACRO_START(SQR_SINGLEADD_32, index)
+
+        mov     r10, #0
+        ldr     r8, [r2, #4*index]     // pSrc[i+j]
+
+        umaal   r10, r11, r6, r8        // <r11:r10> = r6 * r8 + r10 + r11
+
+        str     r10, [r4, #4*index]    // Store to destination
+MACRO_END()
+
+    // Macro for the remaining loops of the first pass of RawSquareAsm.
+    // The only difference to the above is that it also adds the word loaded
+    // from the destination buffer.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^32-1)^2 + 2(2^32-1) = 2^64 - 1
+MACRO_START(SQR_DOUBLEADD_32, index)
+
+        ldr     r8, [r2, #4*index]     // pSrc[i+j]
+        ldr     r10, [r4, #4*index]    // pDst[2*(i+j)]
+
+        umaal   r10, r11, r6, r8        // <r11:r10> = r6 * r8 + r10 + r11
+
+        str     r10, [r4, #4*index]    // Store to destination
+
+MACRO_END()
+
+    // Macro for the third pass loop of RawSquareAsm.
+    // It takes one mulword from the source, squares it, and
+    // adds it to the even columns of the destination. The carries are propagated
+    // to the odd columns.
+    //
+    // Here we can have a (1-bit) carry to the next call because the maximum value for
+    // a pair of columns is (2^32-1)^2+(2^64-1)+1 = 2^65 - 2^33 + 1 < 2^65 - 1
+MACRO_START(SQR_DIAGONAL_PROP, index)
+        ldr     r6, [r0, #4*index]     // mulword
+
+        umull   r10, r11, r6, r6
+
+        ldr     r8, [r4, #8*index]     // Load
+        ldr     r9, [r4, #8*index + 4] // Load
+
+        // Adding the square to the even column
+        adcs    r10, r10, r8            // carry from previous and update the flags
+
+        // Propagating the sum to the next column
+        adcs    r11, r11, r9            // This can generate a carry
+
+        str     r10, [r4, #8*index]    // Store
+        str     r11, [r4, #8*index + 4]// Store
+MACRO_END()
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptFdefRawSquareAsm(
+//   _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                       UINT32      nDigits,
+//   _Out_writes_(2*nWords)                              PUINT32     pDst )
+//
+// Initial inputs to registers:
+//       pSrc        -> r0
+//       nDigits     -> r1
+//       pDst        -> r2
+//
+// Register assignments
+//       r0  = pSrc
+//       r1  = negated word count of pSrc
+//       r2  = pSrc (moving forward one digit / 4 words every inner loop)
+//       r3  = negated digit count of pSrc
+//       r4  = pDst (moving forward one digit every inner loop)
+//       r5  = pDst (moving forward one word every outer loop)
+//       r6  = mulword from pSrc
+//       r7  = Stored negated digit count of pSrc
+//       r8  = Current words loaded from pSrc
+//       r9  = Cyclic counter for the jumps
+//       r10, r11 = "64-bit" sliding register to hold the result of multiplies,
+//                   r10 also receives a word from pDst
+//       r12 = Negated digit counter of pSrc (updated every 4 iterations of main loop)
+//
+// Stack assignments
+#define pDstSq 0   // Stored pDst in stack
+#define pSrc 4   // Stored pSrc in stack
+
+
+//    LEAF_ENTRY SymCryptFdefRawSquareAsm
+FUNCTION_START(SymCryptFdefRawSquareAsm, 3, 13)
+    // push         {r4-r12, lr}
+    sub     sp, sp, #8
+
+
+    mov     r3, r1                      // digit count into r3
+
+    lsl     r1, r1, #2                  // Calculate word count
+
+    neg     r1, r1                      // negate nWords
+    neg     r3, r3                      // negate nDigitsSq
+
+    mov     r4, r2                      // pDst
+    mov     r5, r2                      // store pDst
+
+    str     r0, [sp, #pSrc]             // store pSrc
+    str     r5, [sp, #pDstSq]           // store pDst
+    mov     r7, r3                      // store -nDigits for later
+    mov     r12, r3                     // Negated digit counter of pSrc
+
+    mov     r2, r0                      // inner loop pSrc
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    r11, r11, #0                // Clearing the carry flag and setting r11 = 0
+    ldr     r6, [r0]                    // load the first word from pSrc1
+    str     r11, [r4]                   // store 0 for the first word
+
+    b       SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+SymCryptFdefRawSquareAsmInnerLoopInit_Word0:
+    SQR_SINGLEADD_32    0
+
+SymCryptFdefRawSquareAsmInnerLoopInit_Word1:
+    SQR_SINGLEADD_32    1
+
+    SQR_SINGLEADD_32    2
+
+    SQR_SINGLEADD_32    3
+
+
+    add     r2, r2, #16
+    add     r4, r4, #16
+
+    adds    r3, r3, #1                  // move one digit up
+    bne     SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+    str     r11, [r4]                   // Store the next word into the destination
+    add     r1, r1, #2                  // move two words up (so we stop when real word count is "-1")
+    mov     r9, #1                      // Cyclic counter
+
+    //
+    // MAIN LOOP
+    //
+SymCryptFdefRawSquareAsmOuterLoop:
+
+    add     r5, r5, #4                  // move start of pDst one word up
+
+    mov     r3, r12                     // set -nDigits
+    mov     r2, r0                      // set pSrc
+    mov     r4, r5                      // set pDst
+
+    ands    r11, r11, #0                // Clearing the carry flag and setting r11 = 0
+    ldr     r6, [r0, r9, LSL #2]        // load the next word from pSrc
+
+    // Cyclic counter and jump logic
+    add     r9, r9, #1
+    cmp     r9, #1
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word1
+    cmp     r9, #2
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word2
+    cmp     r9, #3
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word3
+
+    // The following instructions are only executed when r9 == 4
+    mov     r9, #0                      // Set it to 0
+
+    add     r0, r0, #16                 // move start of pSrc 4 words up
+    add     r5, r5, #16                 // move pDst 4 words up
+
+    mov     r2, r0                      // set pSrc
+    mov     r4, r5                      // set pDst
+
+    adds    r3, r3, #1                  // add 1 digit
+    mov     r12, r3                     // set the new digit counter
+
+SymCryptFdefRawSquareAsmInnerLoop_Word0:
+    SQR_DOUBLEADD_32    0
+
+SymCryptFdefRawSquareAsmInnerLoop_Word1:
+    SQR_DOUBLEADD_32    1
+
+SymCryptFdefRawSquareAsmInnerLoop_Word2:
+    SQR_DOUBLEADD_32    2
+
+SymCryptFdefRawSquareAsmInnerLoop_Word3:
+    SQR_DOUBLEADD_32    3
+
+
+    add     r2, r2, #16
+    add     r4, r4, #16
+    adds    r3, r3, #1              // move one digit up
+    bne     SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+    str     r11, [r4]               // Store the next word into the destination
+
+    adds    r1, r1, #1              // move one word up
+    bne     SymCryptFdefRawSquareAsmOuterLoop
+
+    eor     r11, r11, r11           // Setting r11 = 0
+    str     r11, [r5, #20]          // Store 0 to destination for the top word
+
+    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Second Pass - Shifting all results 1 bit left
+    // Third Pass - Adding the squares on the even columns and propagating the sum
+    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    mov     r3, r7                  // -nDigits
+    lsl     r3, r3, #1              // Double digits
+    ldr     r4, [sp, #pDstSq]       // pDst pointer
+    ands    r1, r1, #0              // Clear the flags
+    ands    r2, r2, #0              // Clear the flags
+
+SymCryptFdefRawSquareAsmSecondPass:
+    rrxs    r2, r2                  // set the carry flag if bit[0] of r2 is set
+
+    ldmia   r4, {r8, r9}
+    adcs    r8, r8, r8              // Shift left and add the carry
+    adcs    r9, r9, r9
+    stmia   r4!, {r8, r9}
+
+    ldmia   r4, {r10, r11}
+    adcs    r10, r10, r10           // Shift left and add the carry
+    adcs    r11, r11, r11
+    stmia   r4!, {r10, r11}
+
+    adc     r2, r1, r1
+
+    adds     r3, r3, #1             // move one digit up
+    bne     SymCryptFdefRawSquareAsmSecondPass
+
+
+
+    ldr     r0, [sp, #pSrc]         // src pointer
+    ldr     r4, [sp, #pDstSq]       // pDst pointer
+    // mov r3, r7                    // Use r7 as the digit counter
+    // ands    r1, r1, #0            // Clear the flags
+    ands    r2, r2, #0              // Clear the flags
+
+SymCryptFdefRawSquareAsmThirdPass:
+    rrxs    r2, r2                  // set the carry flag if bit[0] of r2 is set
+
+    SQR_DIAGONAL_PROP 0
+    SQR_DIAGONAL_PROP 1
+    SQR_DIAGONAL_PROP 2
+    SQR_DIAGONAL_PROP 3
+
+    adc     r2, r1, r1
+
+    add     r0, r0, #16             // One digit up (not updated in SQR_DIAGONAL_PROP)
+    add     r4, r4, #32             // Two digits up (not updated in SQR_DIAGONAL_PROP)
+
+    adds    r7, r7, #1              // move one digit up
+    bne     SymCryptFdefRawSquareAsmThirdPass
+
+    // Done, no return value
+
+    add     sp, sp, #8
+    // pop          {r4-r12, pc}
+
+//    LEAF_END SymCryptFdefRawSquareAsm
+FUNCTION_END(SymCryptFdefRawSquareAsm)
+
+//VOID
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Initial inputs to registers:
+//       pmMod       -> r0
+//       pSrc        -> r1
+//       pDst        -> r2
+//
+// Register assignments
+//       r0  = pMod (moving forward one *digit* every inner loop)
+//       r1  = pSrc (moving forward one *digit* every inner loop)
+//       r2  = Stored pSrc (moving forward one word every outer loop)
+//       r3  = negated digit count of pSrc and pMod
+//       r4  = negated word count of pSrc
+//       r5, r6  = m = pSrc[i]*Inv64
+//       r7  = hc = high carry variable
+//       r8, r9 = Current words loaded from pMod
+//       <r12:r11:r10> = "96-bit" sliding register to hold the result of multiplies
+
+// Stack assignments
+#define pMod 0               // Stored pMod
+#define pDst 4               // Stored pDst
+#define nDigits 8               // Stored negated digit count of pSrc
+#define inv64 12              // Inv64 of modulus
+
+//    LEAF_ENTRY SymCryptFdefMontgomeryReduceAsm
+FUNCTION_START(SymCryptFdefMontgomeryReduceAsm, 3, 13)
+    // push         {r4-r12, lr}
+    sub     sp, sp, #16
+
+    str     r2, [sp, #pDst]                             // Store pDst in the stack
+    ldr     r3, [r0, #SymCryptModulusNdigitsOffsetArm]  // # of Digits
+    ldr     r5, [r0, #SymCryptModulusInv64OffsetArm]    // Inv64 of modulus
+    add     r0, r0, #SymCryptModulusValueOffsetArm      // pMod
+    str     r5, [sp, #inv64]                            // Store inv64 in the stack
+
+    lsl     r4, r3, #2                  // Multiply by 4 to get the number of words
+
+    neg     r3, r3                      // Negate the digit count
+    neg     r4, r4                      // Negate the word count
+
+    str     r0, [sp, #pMod]             // Store the pMod pointer
+    mov     r2, r1                      // Store the pSrc pointer
+    str     r3, [sp, #nDigits]          // Store the digit count for later
+
+    eor     r7, r7, r7                  // Set hc to 0
+
+    //
+    // Main loop
+    //
+SymCryptFdefMontgomeryReduceAsmOuter:
+    ldr     r3, [sp, #inv64]            // Inv64 of modulus
+
+    ldmia   r1, {r10, r12}              // Load two words from pSrc
+    ldmia   r0, {r8,r9}                 // Load two words from pMod
+    mov     r11, #0
+    mul     r5, r10, r3                 // <31:0> bits of pSrc[i]*Inv64 = m1 (first multiplier)
+    umaal   r10, r11, r5, r8            // r11 <-- High( m1*pMod[0] + pSrc[i] )
+    umaal   r12, r11, r5, r9            // Calculate pSrc[i+1] = Low( m1*pMod[1] + pSrc[i+1] + High( m1*pMod[0] + pSrc[i] ))
+    mul     r6, r12, r3                 // <31:0> bits of pSrc[i+1]*Inv64 = m2
+
+    ldr     r3, [sp, #nDigits]          // Reset the digit counter
+    mov     r11, #0                     // Set c to 0
+    mov     r12, #0                     // Set c to 0
+
+SymCryptFdefMontgomeryReduceAsmInner:
+    adds    r3, r3, #1                  // Move one digit up (none of the commands updates the carry)
+
+    ldmia   r0!, {r8, r9}               // Load two words from pMod[]
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r8            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r8            // c = <r12:r11> = m2 * pMod[j] + c
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r9            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r9            // c = <r12:r11> = m2 * pMod[j] + c
+
+    ldmia   r0!, {r8, r9}               // Load two words from pMod[]
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r8            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r8            // c = <r12:r11> = m2 * pMod[j] + c
+
+    ldr     r10, [r1]                   // pSrc[j]
+    umaal   r10, r11, r5, r9            // c = <r11:r10> = m1 * pMod[j] + pSrc[j] + c
+    str     r10, [r1], #4               // pSrc[j] = (UINT32) c
+    umaal   r11, r12, r6, r9            // c = <r12:r11> = m2 * pMod[j] + c
+
+    bne     SymCryptFdefMontgomeryReduceAsmInner
+
+    mov     r8, #0                      // r8 = 0
+    mov     r9, #0                      // r9 = 0
+
+    ldmia   r1, {r5, r6}                // Load pSrc[nWords] and pSrc[nWords+1]
+
+    adds    r11, r11, r5                // c + pSrc[nWords]
+    adc     r8, r8, #0                  // Add the carry if any
+    adds    r11, r11, r7                // c + pSrc[nWords] + hc
+    adc     r8, r8, #0                  // Add the carry if any
+    str     r11, [r1], #4               // pSrc[nWords] = c
+
+    adds    r12, r12, r6                // c + pSrc[nWords+1]
+    adc     r9, r9, #0                  // Add the carry if any
+    adds    r12, r12, r8                // c + pSrc[nWords] + hc
+    adc     r7, r9, #0                  // Add the carry if any
+    str     r12, [r1]                   // pSrc[nWords+1] = c
+
+    adds    r4, r4, #2                  // Move two words up
+
+    add     r2, r2, #8                  // Move stored pSrc pointer two words up
+    ldr     r0, [sp, #pMod]             // Restore the pMod pointer
+    mov     r1, r2                      // Restore the pSrc pointer
+
+    bne     SymCryptFdefMontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+
+    // Prepare the pointers for subtract
+    mov     r0, r2                  // pSrc
+    mov     r11, r2                 // Store pSrc for later
+    ldr     r1, [sp, #pMod]         // pMod
+    ldr     r2, [sp, #pDst]         // pDst
+    ldr     r3, [sp, #nDigits]      // Reset the digit counter
+
+    mov     r10, r7                 // r10 = hc
+
+    mov     r8, #0                  // borrow = r8 = 0
+    mov     r9, #0                  // r9 = 0
+
+SymCryptFdefMontgomeryReduceRawSubAsmLoop:
+    subs    r8, r9, r8              // if r8>0 then the "borrow flag" is set
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of pSrc1
+    ldmia   r1!, {r5, r7}           // Load two words of pSrc2
+    sbcs    r4, r4, r5
+    sbcs    r6, r6, r7
+    stmia   r2!, {r4, r6}           // Store the result in the destination
+
+    sbc     r8, r9, r9              // If borrow=1, then r8 = -1 = 0xffffffff
+
+    adds    r3, r3, #1              // Increment the digit count by one
+    bne     SymCryptFdefMontgomeryReduceRawSubAsmLoop
+
+    // Prepare the pointers for masked copy
+    mov     r0, r11                 // pSrc
+    ldr     r1, [sp, #pDst]         // pDst
+
+    and     r9, r8, #1              // If r8>0, set the return value to 1
+    orr     r11, r10, r9            // r11 = hc|d
+
+    ldr     r2, [sp, #nDigits]      // Restore the digit counter
+
+    mov     r9, #0                  // r9 = 0
+
+    subs    r4, r10, r11            // If (r11 > r10) clear the carry flag (i.e. borrow)
+    sbc     r3, r9, r9              // r3 = mask = 0xffffffff if the carry flag is clear
+    // orn     r9, r9, r3              // r9 = NOT(MASK) = 0 if r3 = 0xffffffff
+    orr     r9, r9, r3
+    mvn     r9, r9
+    mov     r8, r1                  // save the destination pointer
+
+SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop:
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    ldmia   r0!, {r4, r6}           // Load two words of the source
+    ldmia   r1!, {r5, r7}           // Load two words of the destination
+    and     r4, r4, r3
+    and     r5, r5, r9
+    orr     r4, r4, r5
+    and     r6, r6, r3
+    and     r7, r7, r9
+    orr     r6, r6, r7
+    stmia   r8!, {r4, r6}           // Store the two words in the destination
+
+    adds    r2, r2, #1              // Increment the digit count by one
+    bne     SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    add     sp, sp, #16
+//    pop          {r4-r12, pc}
+
+//    LEAF_END SymCryptFdefMontgomeryReduceAsm
+FUNCTION_END(SymCryptFdefMontgomeryReduceAsm)
diff --git a/libs/symcrypt/lib/arm/wipe.S b/libs/symcrypt/lib/arm/wipe.S
new file mode 100644
index 000000000000..ee72cfde81b5
--- /dev/null
+++ b/libs/symcrypt/lib/arm/wipe.S
@@ -0,0 +1,27 @@
+//  SymCryptWipe
+//
+//  Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//  Secure wipe
+//
+
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptWipe( _Out_writes_bytes_( cbData )   PVOID  pbData,
+//                                        SIZE_T cbData )
+
+SymCryptWipeAsm:
+.globl SymCryptWipeAsm
+.def SymCryptWipeAsm; .scl 2; .type 32; .endef
+.seh_proc SymCryptWipeAsm
+push {lr}
+.seh_save_regs {lr}
+.seh_endprologue
+        // we just jump to memset.
+        // this is enough to stop the compiler optimizing the memset away.
+        mov     r2, r1
+        mov     r1, #0
+        bl       memset
+pop {pc}
+.seh_endproc
diff --git a/libs/symcrypt/lib/arm/wipe.symcryptasm b/libs/symcrypt/lib/arm/wipe.symcryptasm
new file mode 100644
index 000000000000..043f113a9c09
--- /dev/null
+++ b/libs/symcrypt/lib/arm/wipe.symcryptasm
@@ -0,0 +1,20 @@
+//  SymCryptWipe
+//
+//  Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//  Secure wipe
+//
+
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptWipe( _Out_writes_bytes_( cbData )   PVOID  pbData,
+//                                        SIZE_T cbData )
+
+FUNCTION_START(SymCryptWipeAsm, 2, 0)
+        // we just jump to memset.
+        // this is enough to stop the compiler optimizing the memset away.
+        mov     r2, r1
+        mov     r1, #0
+        bl       memset
+FUNCTION_END(SymCryptWipeAsm)
diff --git a/libs/symcrypt/lib/arm64/fdef369_asm.S b/libs/symcrypt/lib/arm64/fdef369_asm.S
new file mode 100644
index 000000000000..2a2413e7a39b
--- /dev/null
+++ b/libs/symcrypt/lib/arm64/fdef369_asm.S
@@ -0,0 +1,718 @@
+//
+//  fdef369_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// This file contains alternative routines that pretend that each digit is only 3 words.
+// This gets used if the number is 1, 2, 3, 5, 6, or 9 digits long.
+// The immediate advantage is that it improves EC performance on 192, 384, and 521-bit curves.
+//
+// Most of this code is a direct copy of the default code.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 3 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdef369RawAddAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    adds    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    adcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbz     X_3, SymCryptFdef369RawAddAsmEnd
+
+LABEL(SymCryptFdef369RawAddAsmLoop)
+    // carry is in the carry flag
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    adcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369RawAddAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdef369RawAddAsmEnd)
+    cset    X_0, cs                 // Set the return value equal to the carry
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdef369RawSubAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    subs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbz     X_3, SymCryptFdef369RawSubAsmEnd
+
+LABEL(SymCryptFdef369RawSubAsmLoop)
+    // borrow is in the carry flag (flipped)
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369RawSubAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdef369RawSubAsmEnd)
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369MaskedCopyAsm(
+//    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm)
+    .seh_endprologue
+
+    subs    xzr, xzr, X_3           // If (X_3 > 0) clear the carry flag (i.e. borrow)
+
+    ldp     X_3, X_5, [X_0]         // Load two words of the source
+    ldp     X_4, X_6, [X_1]         // Load two words of the destination
+    csel    X_3, X_3, X_4, cc       // If the carry is clear, select the source operand
+    csel    X_5, X_5, X_6, cc
+    stp     X_3, X_5, [X_1]         // Store the two words in the destination
+
+    ldr     X_3, [X_0, #16]         // Load one word of the source
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+    ldr     X_4, [X_1, #16]         // Load one word of the destination
+    csel    X_3, X_3, X_4, cc
+    str     X_3, [X_1, #16]         // Store the one word in the destination
+
+    cbz     X_2, SymCryptFdef369MaskedCopyAsmEnd
+
+LABEL(SymCryptFdef369MaskedCopyAsmLoop)
+    ldp     X_3, X_5, [X_0, #24]!   // Load two words of the source
+    ldp     X_4, X_6, [X_1, #24]!   // Load two words of the destination
+    csel    X_3, X_3, X_4, cc       // If the carry is clear, select the source operand
+    csel    X_5, X_5, X_6, cc
+    stp     X_3, X_5, [X_1]         // Store the two words in the destination
+
+    ldr     X_3, [X_0, #16]         // Load one word of the source
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+    ldr     X_4, [X_1, #16]         // Load one word of the destination
+    csel    X_3, X_3, X_4, cc
+    str     X_3, [X_1, #16]         // Store the one word in the destination
+
+    cbnz    X_2, SymCryptFdef369MaskedCopyAsmLoop
+
+LABEL(SymCryptFdef369MaskedCopyAsmEnd)
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369RawMulAsm(
+//    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                    UINT32      nDigits1,
+//    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                    UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+//
+// Basic structure:
+//   for each word in Src1:
+//       Dst += Src2 * word
+//
+// Register assignments
+//       X_0  = pSrc1 (moving forward one word every outer loop)
+//       X_1  = word count of pSrc1
+//       X_2  = pSrc2 (moving forward one *digit* every inner loop)
+//       X_3  = digit count of pSrc2 and pDst
+//       X_4  = pDst (moving forward one *digit* every inner loop)
+//       X_5  = Stored pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc1
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = Scratch registers for holding the results of multiplies
+//       X_13 = Stored pSrc2
+//       X_14 = Stored digit count of pSrc2
+//       X_15 = Scratch register for holding the results of multiplies
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm)
+    .seh_endprologue
+
+    add     X_1, X_1, X_1, LSL #1       // Calculate word count (X_1 * 3)
+
+    sub     X_2, X_2, #24               // offset pSrc2 so we can use pre-increment form of loads
+    sub     X_4, X_4, #24               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_5, X_4                    // store pDst
+    mov     X_13, X_2                   // store pSrc2
+    mov     X_14, X_3                   // store nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+
+LABEL(SymCryptFdef369RawMulAsmLoopInner1)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #24]!       // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+1]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+1]
+
+    stp     X_11, X_15, [X_4, #24]!     // Store to destination
+    ldr     X_7, [X_2, #16]             // load 1 word from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j+2]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j+2]
+
+    str     X_11, [X_4, #16]            // Store to destination
+
+    cbnz    X_3, SymCryptFdef369RawMulAsmLoopInner1
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #24]
+
+    sub     X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdef369RawMulAsmLoopOuter)
+    mov     X_3, X_14                   // set nDigits2
+    mov     X_2, X_13                   // set pSrc2
+    mov     X_4, X_5                    // set pDst
+
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the next word from pSrc1
+
+LABEL(SymCryptFdef369RawMulAsmLoopInner)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #24]!       // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #24]!      // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+1]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+1]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4]            // Store to destination
+
+    ldr     X_7, [X_2, #16]             // load 1 word from pSrc2
+    ldr     X_9, [X_4, #16]             // load 1 word from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j+2]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j+2]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+
+    str     X_9, [X_4, #16]             // Store to destination
+
+    cbnz    X_3, SymCryptFdef369RawMulAsmLoopInner
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #24]
+
+    subs    X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    bne     SymCryptFdef369RawMulAsmLoopOuter
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369MontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Register assignments
+//       X_0  = pMod (moving forward one *digit* every inner loop)
+//       X_1  = pSrc (moving forward one *digit* every inner loop)
+//       X_2  = pDst (used only in the end for subtract / result)
+//       X_3  = digit count of pSrc and pMod
+//       X_4  = word count of pSrc
+//       X_5  = Inv64 of the modulus
+//       X_6  = m = pSrc[i]*Inv64
+//       X_7  = hc = high carry variable
+//       X_8, X_9   = Current words loaded in pairs from pSrc
+//       X_10, X_11 = Current words loaded in pairs from pMod
+//       X_12, X_13 = c variable = "128-bit" register to hold the result of multiplies
+//                  It is flipped between [X_12:X_13] and [X_13:X_12] instead of doing c>>=64
+//       X_14 = Temporary intermediate result
+//       X_15 = Stored digit count of pSrc
+//       X_16 = Stored pMod pointer
+//       X_17 = Stored pSrc pointer (moving forward one word every outer loop)
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+#define X_16 x16
+#define W_16 w16
+#define X_17 x17
+#define W_17 w17
+PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm)
+    .seh_endprologue
+
+    ldr     W_3, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64      // pMod
+
+    add     X_4, X_3, X_3, LSL #1       // Calculate word count (X_3 * 3)
+
+    sub     X_0, X_0, #24               // offset pMod so we can use pre-increment form of loads
+    sub     X_1, X_1, #24               // offset pSrc so we can use pre-increment form of loads
+    sub     X_2, X_2, #24               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_15, X_3                   // Store the digit count for later
+    mov     X_16, X_0                   // Store the pMod pointer
+    mov     X_17, X_1                   // Store the pSrc pointer
+
+    mov     X_7, xzr                    // Set hc to 0
+
+    //
+    // Main loop
+    //
+LABEL(SymCryptFdef369MontgomeryReduceAsmOuter)
+    ldr     X_8, [X_1, #24]             // Load 1 word from pSrc
+    mul     X_6, X_8, X_5               // <63:0> bits of pSrc[i]*Inv64 = m
+
+    mov     X_12, xzr                   // Set c to 0
+
+LABEL(SymCryptFdef369MontgomeryReduceAsmInner)
+    ldp     X_10, X_11, [X_0, #24]!     // pMod[j]
+    ldp     X_8, X_9, [X_1, #24]!       // pSrc[j]
+
+    mul     X_14, X_6, X_10             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_8             // Adding pSrc[j]
+    umulh   X_13, X_6, X_10             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    // ***: These cannot produce extra carry as the maximum is
+    //      (2^64 - 1)*(2^64-1) + 2^64-1 + 2^64-1 = 2^128 - 1
+    str     X_12, [X_1]                 // pSrc[j] = (UINT64) c4) c
+    mov     X_12, X_13                  // c >>= 64
+
+    mul     X_14, X_6, X_11             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_9             // Adding pSrc[j]
+    umulh   X_13, X_6, X_11             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    str     X_12, [X_1, #8]             // pSrc[j] = (UINT64) c
+    mov     X_12, X_13                  // c >>= 64
+
+    ldr     X_10, [X_0, #16]            // pMod[j]
+    ldr     X_8, [X_1, #16]             // pSrc[j]
+
+    mul     X_14, X_6, X_10             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_8             // Adding pSrc[j]
+    umulh   X_13, X_6, X_10             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    str     X_12, [X_1, #16]            // pSrc[j] = (UINT64) c4) c
+    mov     X_12, X_13                  // c >>= 64
+
+    subs    X_3, X_3, #1                // Move one digit up
+    bne     SymCryptFdef369MontgomeryReduceAsmInner
+
+    ldr     X_8, [X_1, #24]             // pSrc[nWords]
+    adds    X_12, X_12, X_8             // c + pSrc[nWords]
+    adc     X_13, xzr, xzr              // Add the carry if any
+
+    adds    X_12, X_12, X_7             // c + pSrc[nWords] + hc
+    adc     X_7, X_13, xzr              // Add the carry if any and store into hc
+
+    str     X_12, [X_1, #24]            // pSrc[nWords] = c
+
+    subs    X_4, X_4, #1                // Move one word up
+
+    add     X_17, X_17, #8              // Move stored pSrc pointer one word up
+    mov     X_0, X_16                   // Restore pMod pointer
+    mov     X_1, X_17                   // Restore pSrc pointer
+
+    mov     X_3, X_15                   // Restore the digit counter
+
+    bne     SymCryptFdef369MontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+
+    mov     X_14, X_2               // Store pDst pointer
+
+    // Prepare the pointers for subtract
+    mov     X_0, X_17               // pSrc
+    mov     X_1, X_16               // pMod
+
+    mov     X_10, X_7               // X_10 = hc
+    mov     X_3, X_15               // Restore the digit counter
+    subs    X_4, X_4, X_4           // Set the carry flag (i.e. no borrow)
+
+LABEL(SymCryptFdef369MontgomeryReduceRawSubAsmLoop)
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    // borrow is in the carry flag (flipped)
+
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369MontgomeryReduceRawSubAsmLoop
+
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    orr     X_11, X_10, X_0         // X_11 = hc|d
+
+    // Prepare the pointers for masked copy
+    mov     X_0, X_17               // pSrc
+    mov     X_1, X_14               // pDst
+
+    mov     X_2, X_15               // Restore the digit counter
+    subs    X_4, X_10, X_11         // If (X_11 > X_10) clear the carry flag (i.e. borrow)
+
+LABEL(SymCryptFdef369MontgomeryReduceMaskedCopyAsmLoop)
+    sub     X_2, X_2, #1            // decrement the digit count by one
+
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of the source
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1]         // Store the two words in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of the source
+    ldr     X_5, [X_1, #16]         // Load one word of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    str     X_4, [X_1, #16]         // Store the one word in the destination
+
+    cbnz    X_2, SymCryptFdef369MontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+#undef X_17
+#undef W_17
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64/fdef369_asm.symcryptasm b/libs/symcrypt/lib/arm64/fdef369_asm.symcryptasm
new file mode 100644
index 000000000000..73d83e1faa5d
--- /dev/null
+++ b/libs/symcrypt/lib/arm64/fdef369_asm.symcryptasm
@@ -0,0 +1,465 @@
+//
+//  fdef369_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// This file contains alternative routines that pretend that each digit is only 3 words.
+// This gets used if the number is 1, 2, 3, 5, 6, or 9 digits long.
+// The immediate advantage is that it improves EC performance on 192, 384, and 521-bit curves.
+//
+// Most of this code is a direct copy of the default code.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 3 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdef369RawAddAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm), 4, 8)
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    adds    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    adcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbz     X_3, SymCryptFdef369RawAddAsmEnd
+
+LABEL(SymCryptFdef369RawAddAsmLoop)
+    // carry is in the carry flag
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    adcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369RawAddAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdef369RawAddAsmEnd)
+    cset    X_0, cs                 // Set the return value equal to the carry
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm))
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdef369RawSubAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm), 4, 8)
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    subs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbz     X_3, SymCryptFdef369RawSubAsmEnd
+
+LABEL(SymCryptFdef369RawSubAsmLoop)
+    // borrow is in the carry flag (flipped)
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369RawSubAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdef369RawSubAsmEnd)
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369MaskedCopyAsm(
+//    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm), 4, 7)
+
+    subs    xzr, xzr, X_3           // If (X_3 > 0) clear the carry flag (i.e. borrow)
+
+    ldp     X_3, X_5, [X_0]         // Load two words of the source
+    ldp     X_4, X_6, [X_1]         // Load two words of the destination
+    csel    X_3, X_3, X_4, cc       // If the carry is clear, select the source operand
+    csel    X_5, X_5, X_6, cc
+    stp     X_3, X_5, [X_1]         // Store the two words in the destination
+
+    ldr     X_3, [X_0, #16]         // Load one word of the source
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+    ldr     X_4, [X_1, #16]         // Load one word of the destination
+    csel    X_3, X_3, X_4, cc
+    str     X_3, [X_1, #16]         // Store the one word in the destination
+
+    cbz     X_2, SymCryptFdef369MaskedCopyAsmEnd
+
+LABEL(SymCryptFdef369MaskedCopyAsmLoop)
+    ldp     X_3, X_5, [X_0, #24]!   // Load two words of the source
+    ldp     X_4, X_6, [X_1, #24]!   // Load two words of the destination
+    csel    X_3, X_3, X_4, cc       // If the carry is clear, select the source operand
+    csel    X_5, X_5, X_6, cc
+    stp     X_3, X_5, [X_1]         // Store the two words in the destination
+
+    ldr     X_3, [X_0, #16]         // Load one word of the source
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+    ldr     X_4, [X_1, #16]         // Load one word of the destination
+    csel    X_3, X_3, X_4, cc
+    str     X_3, [X_1, #16]         // Store the one word in the destination
+
+    cbnz    X_2, SymCryptFdef369MaskedCopyAsmLoop
+
+LABEL(SymCryptFdef369MaskedCopyAsmEnd)
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369RawMulAsm(
+//    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                    UINT32      nDigits1,
+//    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                    UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+//
+// Basic structure:
+//   for each word in Src1:
+//       Dst += Src2 * word
+//
+// Register assignments
+//       X_0  = pSrc1 (moving forward one word every outer loop)
+//       X_1  = word count of pSrc1
+//       X_2  = pSrc2 (moving forward one *digit* every inner loop)
+//       X_3  = digit count of pSrc2 and pDst
+//       X_4  = pDst (moving forward one *digit* every inner loop)
+//       X_5  = Stored pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc1
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = Scratch registers for holding the results of multiplies
+//       X_13 = Stored pSrc2
+//       X_14 = Stored digit count of pSrc2
+//       X_15 = Scratch register for holding the results of multiplies
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm), 5, 16)
+
+    add     X_1, X_1, X_1, LSL #1       // Calculate word count (X_1 * 3)
+
+    sub     X_2, X_2, #24               // offset pSrc2 so we can use pre-increment form of loads
+    sub     X_4, X_4, #24               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_5, X_4                    // store pDst
+    mov     X_13, X_2                   // store pSrc2
+    mov     X_14, X_3                   // store nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+
+LABEL(SymCryptFdef369RawMulAsmLoopInner1)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #24]!       // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+1]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+1]
+
+    stp     X_11, X_15, [X_4, #24]!     // Store to destination
+    ldr     X_7, [X_2, #16]             // load 1 word from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j+2]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j+2]
+
+    str     X_11, [X_4, #16]            // Store to destination
+
+    cbnz    X_3, SymCryptFdef369RawMulAsmLoopInner1
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #24]
+
+    sub     X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdef369RawMulAsmLoopOuter)
+    mov     X_3, X_14                   // set nDigits2
+    mov     X_2, X_13                   // set pSrc2
+    mov     X_4, X_5                    // set pDst
+
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the next word from pSrc1
+
+LABEL(SymCryptFdef369RawMulAsmLoopInner)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #24]!       // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #24]!      // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+1]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+1]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4]            // Store to destination
+
+    ldr     X_7, [X_2, #16]             // load 1 word from pSrc2
+    ldr     X_9, [X_4, #16]             // load 1 word from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j+2]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j+2]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+
+    str     X_9, [X_4, #16]             // Store to destination
+
+    cbnz    X_3, SymCryptFdef369RawMulAsmLoopInner
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #24]
+
+    subs    X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    bne     SymCryptFdef369RawMulAsmLoopOuter
+
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm))
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369MontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Register assignments
+//       X_0  = pMod (moving forward one *digit* every inner loop)
+//       X_1  = pSrc (moving forward one *digit* every inner loop)
+//       X_2  = pDst (used only in the end for subtract / result)
+//       X_3  = digit count of pSrc and pMod
+//       X_4  = word count of pSrc
+//       X_5  = Inv64 of the modulus
+//       X_6  = m = pSrc[i]*Inv64
+//       X_7  = hc = high carry variable
+//       X_8, X_9   = Current words loaded in pairs from pSrc
+//       X_10, X_11 = Current words loaded in pairs from pMod
+//       X_12, X_13 = c variable = "128-bit" register to hold the result of multiplies
+//                  It is flipped between [X_12:X_13] and [X_13:X_12] instead of doing c>>=64
+//       X_14 = Temporary intermediate result
+//       X_15 = Stored digit count of pSrc
+//       X_16 = Stored pMod pointer
+//       X_17 = Stored pSrc pointer (moving forward one word every outer loop)
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm), 3, 18)
+
+    ldr     W_3, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64      // pMod
+
+    add     X_4, X_3, X_3, LSL #1       // Calculate word count (X_3 * 3)
+
+    sub     X_0, X_0, #24               // offset pMod so we can use pre-increment form of loads
+    sub     X_1, X_1, #24               // offset pSrc so we can use pre-increment form of loads
+    sub     X_2, X_2, #24               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_15, X_3                   // Store the digit count for later
+    mov     X_16, X_0                   // Store the pMod pointer
+    mov     X_17, X_1                   // Store the pSrc pointer
+
+    mov     X_7, xzr                    // Set hc to 0
+
+    //
+    // Main loop
+    //
+LABEL(SymCryptFdef369MontgomeryReduceAsmOuter)
+    ldr     X_8, [X_1, #24]             // Load 1 word from pSrc
+    mul     X_6, X_8, X_5               // <63:0> bits of pSrc[i]*Inv64 = m
+
+    mov     X_12, xzr                   // Set c to 0
+
+LABEL(SymCryptFdef369MontgomeryReduceAsmInner)
+    ldp     X_10, X_11, [X_0, #24]!     // pMod[j]
+    ldp     X_8, X_9, [X_1, #24]!       // pSrc[j]
+
+    mul     X_14, X_6, X_10             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_8             // Adding pSrc[j]
+    umulh   X_13, X_6, X_10             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    // ***: These cannot produce extra carry as the maximum is
+    //      (2^64 - 1)*(2^64-1) + 2^64-1 + 2^64-1 = 2^128 - 1
+    str     X_12, [X_1]                 // pSrc[j] = (UINT64) c4) c
+    mov     X_12, X_13                  // c >>= 64
+
+    mul     X_14, X_6, X_11             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_9             // Adding pSrc[j]
+    umulh   X_13, X_6, X_11             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    str     X_12, [X_1, #8]             // pSrc[j] = (UINT64) c
+    mov     X_12, X_13                  // c >>= 64
+
+    ldr     X_10, [X_0, #16]            // pMod[j]
+    ldr     X_8, [X_1, #16]             // pSrc[j]
+
+    mul     X_14, X_6, X_10             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_8             // Adding pSrc[j]
+    umulh   X_13, X_6, X_10             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    str     X_12, [X_1, #16]            // pSrc[j] = (UINT64) c4) c
+    mov     X_12, X_13                  // c >>= 64
+
+    subs    X_3, X_3, #1                // Move one digit up
+    bne     SymCryptFdef369MontgomeryReduceAsmInner
+
+    ldr     X_8, [X_1, #24]             // pSrc[nWords]
+    adds    X_12, X_12, X_8             // c + pSrc[nWords]
+    adc     X_13, xzr, xzr              // Add the carry if any
+
+    adds    X_12, X_12, X_7             // c + pSrc[nWords] + hc
+    adc     X_7, X_13, xzr              // Add the carry if any and store into hc
+
+    str     X_12, [X_1, #24]            // pSrc[nWords] = c
+
+    subs    X_4, X_4, #1                // Move one word up
+
+    add     X_17, X_17, #8              // Move stored pSrc pointer one word up
+    mov     X_0, X_16                   // Restore pMod pointer
+    mov     X_1, X_17                   // Restore pSrc pointer
+
+    mov     X_3, X_15                   // Restore the digit counter
+
+    bne     SymCryptFdef369MontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+
+    mov     X_14, X_2               // Store pDst pointer
+
+    // Prepare the pointers for subtract
+    mov     X_0, X_17               // pSrc
+    mov     X_1, X_16               // pMod
+
+    mov     X_10, X_7               // X_10 = hc
+    mov     X_3, X_15               // Restore the digit counter
+    subs    X_4, X_4, X_4           // Set the carry flag (i.e. no borrow)
+
+LABEL(SymCryptFdef369MontgomeryReduceRawSubAsmLoop)
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    // borrow is in the carry flag (flipped)
+
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369MontgomeryReduceRawSubAsmLoop
+
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    orr     X_11, X_10, X_0         // X_11 = hc|d
+
+    // Prepare the pointers for masked copy
+    mov     X_0, X_17               // pSrc
+    mov     X_1, X_14               // pDst
+
+    mov     X_2, X_15               // Restore the digit counter
+    subs    X_4, X_10, X_11         // If (X_11 > X_10) clear the carry flag (i.e. borrow)
+
+LABEL(SymCryptFdef369MontgomeryReduceMaskedCopyAsmLoop)
+    sub     X_2, X_2, #1            // decrement the digit count by one
+
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of the source
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1]         // Store the two words in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of the source
+    ldr     X_5, [X_1, #16]         // Load one word of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    str     X_4, [X_1, #16]         // Store the one word in the destination
+
+    cbnz    X_2, SymCryptFdef369MontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm))
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64/fdef_asm.S b/libs/symcrypt/lib/arm64/fdef_asm.S
new file mode 100644
index 000000000000..b9e00bf467cb
--- /dev/null
+++ b/libs/symcrypt/lib/arm64/fdef_asm.S
@@ -0,0 +1,2475 @@
+//
+//  fdef_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format for the arm64 architecture
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawAddAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    adds    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbz     X_3, SymCryptFdefRawAddAsmEnd
+
+LABEL(SymCryptFdefRawAddAsmLoop)
+    // carry is in the carry flag
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefRawAddAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdefRawAddAsmEnd)
+    cset    X_0, cs                 // Set the return value equal to the carry
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSubAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    subs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbz     X_3, SymCryptFdefRawSubAsmEnd
+
+LABEL(SymCryptFdefRawSubAsmLoop)
+    // borrow is in the carry flag (flipped)
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefRawSubAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdefRawSubAsmEnd)
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopyAsm(
+//    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm)
+    .seh_endprologue
+
+    dup     v0.4s, W_3              // broadcast the mask to v0
+
+LABEL(SymCryptFdefMaskedCopyAsmLoop)
+    ldp     q1, q3, [X_0], #32      // Load 4 words of the source
+    ldp     q2, q4, [X_1]           // Load 4 words of the destination
+    bit     v2.16b, v1.16b, v0.16b  // if the mask is 1s, overwrite the destination with source
+    bit     v4.16b, v3.16b, v0.16b  // if the mask is 1s, overwrite the destination with source
+    stp     q2, q4, [X_1], #32      // Store the two words in the destination
+
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+
+    cbnz    X_2, SymCryptFdefMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMulAsm(
+//    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                    UINT32      nDigits1,
+//    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                    UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+//
+// Basic structure:
+//   for each word in Src1:
+//       Dst += Src2 * word
+//
+// Register assignments
+//       X_0  = pSrc1 (moving forward one word every outer loop)
+//       X_1  = word count of pSrc1
+//       X_2  = pSrc2 (moving forward one *digit* every inner loop)
+//       X_3  = digit count of pSrc2 and pDst
+//       X_4  = pDst (moving forward one *digit* every inner loop)
+//       X_5  = Stored pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc1
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = Scratch registers for holding the results of multiplies
+//       X_13 = Stored pSrc2
+//       X_14 = Stored digit count of pSrc2
+//       X_15 = Scratch register for holding the results of multiplies
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm)
+    .seh_endprologue
+
+    lsl     X_1, X_1, #2                // Calculate word count
+
+    sub     X_2, X_2, #32               // offset pSrc2 so we can use pre-increment form of loads
+    sub     X_4, X_4, #32               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_5, X_4                    // store pDst
+    mov     X_13, X_2                   // store pSrc2
+    mov     X_14, X_3                   // store nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+
+LABEL(SymCryptFdefRawMulAsmLoopInner1)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #32]!       // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+1]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+1]
+
+    stp     X_11, X_15, [X_4, #32]!     // Store to destination
+    ldp     X_7, X_8, [X_2, #16]        // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j+2]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j+2]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+3]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+3]
+
+    stp     X_11, X_15, [X_4, #16]      // Store to destination
+
+    cbnz    X_3, SymCryptFdefRawMulAsmLoopInner1
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #32]
+
+    sub     X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdefRawMulAsmLoopOuter)
+    mov     X_3, X_14                   // set nDigits2
+    mov     X_2, X_13                   // set pSrc2
+    mov     X_4, X_5                    // set pDst
+
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the next word from pSrc1
+
+LABEL(SymCryptFdefRawMulAsmLoopInner)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #32]!       // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #32]!      // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+1]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+1]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4]            // Store to destination
+
+    ldp     X_7, X_8, [X_2, #16]        // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #16]       // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j+2]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+3]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j+2]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+3]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4, #16]       // Store to destination
+
+    cbnz    X_3, SymCryptFdefRawMulAsmLoopInner
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #32]
+
+    subs    X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    bne     SymCryptFdefRawMulAsmLoopOuter
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+
+    // Macro for the first loop of the first pass of RawSquareAsm.
+    // It takes one word from the source, multiplies it with the mulword,
+    // adds the high level word of the previous macro call, and stores it into
+    // the destination.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^64-1)^2 + 2^64-1 = 2^128 - 2^64
+.macro SQR_SINGLEADD_64  index, src_reg, dst_reg, mul_word, src_carry, dst_carry, scratch0, scratch1
+
+    ldr     \scratch0, [\src_reg, #8*\index]   // pSrc[i+j]
+
+    mul     \scratch1, \mul_word, \scratch0    // Bits <63:0> of pSrc[i]*pSrc[i+j]
+    adds    \scratch1, \scratch1, \src_carry   // Adding the previous word
+    umulh   \dst_carry, \mul_word, \scratch0   // Bits <127:64> of pSrc[i]*pSrc[i+j]
+    adc     \dst_carry, \dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    str     \scratch1, [\dst_reg, #8*\index]   // Store to destination
+
+.endm
+
+    // Macro for the remaining loops of the first pass of RawSquareAsm.
+    // The only difference to the above is that it also adds the word loaded
+    // from the destination buffer.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^64-1)^2 + 2(2^64-1) = 2^128 - 1
+.macro SQR_DOUBLEADD_64  index, src_reg, dst_reg, mul_word, src_carry, dst_carry, scratch0, scratch1, scratch2
+
+    ldr     \scratch0, [\src_reg, #8*\index]   // pSrc[i+j]
+    ldr     \scratch2, [\dst_reg, #8*\index]   // pDst[2*(i+j)]
+
+    mul     \scratch1, \mul_word, \scratch0    // Bits <63:0> of pSrc[i]*pSrc[i+j]
+    umulh   \dst_carry, \mul_word, \scratch0   // Bits <127:64> of pSrc[i]*pSrc[i+j]
+
+    adds    \scratch1, \scratch1, \scratch2    // Add the word from the destination
+    adc     \dst_carry, \dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    adds    \scratch1, \scratch1, \src_carry   // Adding the previous word
+    adc     \dst_carry, \dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    str     \scratch1, [\dst_reg, #8*\index]   // Store to destination
+
+.endm
+
+    // Macro for the third pass loop of RawSquareAsm.
+    // It takes one mulword from the source, squares it, and
+    // adds it to the even columns of the destination. The carries are propagated
+    // to the odd columns.
+    //
+    // Here we can have a (1-bit) carry to the next call because the maximum value for
+    // a pair of columns is (2^64-1)^2+(2^128-1)+1 = 2^129 - 2^65 + 1 < 2^129 - 1
+.macro SQR_DIAGONAL_PROP  index, src_reg, dst_reg, squarelo, squarehi, scratch0, scratch1
+
+    ldr     \squarehi, [\src_reg, #8*\index]               // mulword
+    mul     \squarelo, \squarehi, \squarehi                // Bits <63:0> of m^2
+    umulh   \squarehi, \squarehi, \squarehi                // Bits <127:64> of m^2
+
+    ldp     \scratch0, \scratch1, [\dst_reg, #16*\index]    // Load
+
+    // Adding the square to the even column
+    adcs    \squarelo, \squarelo, \scratch0                // carry from previous and update the flags
+
+    // Propagating the sum to the next column
+    adcs    \squarehi, \squarehi, \scratch1                // This can generate a carry
+
+    stp     \squarelo, \squarehi, [\dst_reg, #16*\index]    // Store
+
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawSquareAsm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst )
+//
+// Register assignments
+//       X_0  = pSrc
+//       X_1  = word count of pSrc
+//       X_2  = pSrc (moving forward one digit / 4 words every inner loop)
+//       X_3  = digit count of pSrc
+//       X_4  = pDst (moving forward one digit every inner loop)
+//       X_5  = pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = "128-bit" sliding register to hold the result of multiplies
+//       X_13 = Stored pSrc
+//       X_14 = Digit count of pSrc
+//       X_15 = Stored digit count of pSrc
+//       X_16 = Stored pDst
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+#define X_16 x16
+#define W_16 w16
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm)
+    .seh_endprologue
+
+    mov     X_3, X_1                    // digit count into X_3
+
+    lsl     X_1, X_1, #2                // Calculate word count
+
+    mov     X_4, X_2                    // pDst
+    mov     X_5, X_2                    // store pDst
+    mov     X_16, X_2                   // store pDst
+    mov     X_13, X_0                   // store pSrc
+    mov     X_2, X_0                    // inner loop pSrc
+    mov     X_14, X_3                   // store nDigits for later
+    mov     X_15, X_3                   // store nDigits for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    mov     X_12, xzr                   // set X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+    str     xzr, [X_4]                  // store 0 for the first word
+
+    b       SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoopInit_Word0)
+    SQR_SINGLEADD_64 0, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoopInit_Word1)
+    SQR_SINGLEADD_64 1, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    SQR_SINGLEADD_64 2, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    SQR_SINGLEADD_64 3, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    sub     X_3, X_3, #1                // move one digit up
+    add     X_2, X_2, #32
+    add     X_4, X_4, #32
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+    str     X_12, [X_4]                 // Store the next word into the destination
+
+    sub     X_1, X_1, #2                // move two words up (we started at the word 1)
+
+    mov     X_8, #1                     // Cyclic counter
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdefRawSquareAsmOuterLoop)
+
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    mov     X_3, X_14                   // set nDigits
+    mov     X_2, X_0                    // set pSrc
+    mov     X_4, X_5                    // set pDst
+
+    mov     X_11, xzr                   // set X_11 = 0
+    mov     X_12, xzr                   // set X_12 = 0
+    ldr     X_6, [X_0, X_8, LSL #3]     // load the next word from pSrc
+
+    // Cyclic counter and jump logic
+    add     X_8, X_8, #1
+    cmp     X_8, #1
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word1
+    cmp     X_8, #2
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word2
+    cmp     X_8, #3
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word3
+
+    // The following instructions are only executed when X_8 == 4
+    mov     X_8, xzr                // Set it to 0
+
+    add     X_0, X_0, #32           // move start of pSrc 4 words up
+    add     X_5, X_5, #32           // move pDst 4 words up
+
+    mov     X_2, X_0                // set pSrc
+    mov     X_4, X_5                // set pDst
+
+    sub     X_14, X_14, #1          // remove 1 digit
+    mov     X_3, X_14               // set the new digit counter
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word0)
+    SQR_DOUBLEADD_64 0, X_2, X_4, X_6, X_12, X_11, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word1)
+    SQR_DOUBLEADD_64 1, X_2, X_4, X_6, X_11, X_12, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word2)
+    SQR_DOUBLEADD_64 2, X_2, X_4, X_6, X_12, X_11, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word3)
+    SQR_DOUBLEADD_64 3, X_2, X_4, X_6, X_11, X_12, X_7, X_9, X_10
+
+    sub     X_3, X_3, #1                // move one digit up
+    add     X_2, X_2, #32
+    add     X_4, X_4, #32
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+    str     X_12, [X_4]                 // Store the next word into the destination
+
+    sub     X_1, X_1, #1                // move one word up
+    cbnz    X_1, SymCryptFdefRawSquareAsmOuterLoop
+
+    str     xzr, [X_5, #40]             // Store 0 to destination for the top word
+
+    ////////////////////////////////////////////////////////////////
+    // Second Pass - Shifting all results 1 bit left
+    ////////////////////////////////////////////////////////////////
+
+    mov     X_3, X_15       // nDigits
+    lsl     X_3, X_3, #1    // Double digits
+    mov     X_4, X_16       // pDst pointer
+    ands    X_7, X_7, xzr   // Clear the flags
+
+LABEL(SymCryptFdefRawSquareAsmSecondPass)
+
+    sub     X_3, X_3, #1    // move one digit up
+
+    ldp     X_7, X_8, [X_4]
+    adcs    X_7, X_7, X_7   // Shift left and add the carry
+    adcs    X_8, X_8, X_8
+    stp     X_7, X_8, [X_4], #16
+
+    ldp     X_9, X_10, [X_4]
+    adcs    X_9, X_9, X_9   // Shift left and add the carry
+    adcs    X_10, X_10, X_10
+    stp     X_9, X_10, [X_4], #16
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmSecondPass
+
+    //////////////////////////////////////////////////////////////////////////////
+    // Third Pass - Adding the squares on the even columns and propagating the sum
+    //////////////////////////////////////////////////////////////////////////////
+
+    ands    X_7, X_7, xzr   // Clear the flags
+    mov     X_0, X_13       // src pointer
+    mov     X_4, X_16       // pDst pointer
+    mov     X_3, X_15       // nDigits
+
+LABEL(SymCryptFdefRawSquareAsmThirdPass)
+    SQR_DIAGONAL_PROP 0, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 1, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 2, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 3, X_0, X_4, X_6, X_7, X_8, X_9
+
+    sub     X_3, X_3, #1        // move one digit up
+    add     X_0, X_0, #32       // One digit up (not updated in SQR_DIAGONAL_PROP)
+    add     X_4, X_4, #64       // Two digits up (not updated in SQR_DIAGONAL_PROP)
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmThirdPass
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 + Src2 into (X_4, X_5, X_6, X_7)
+    ldp     X_4, X_5, [X_1]       // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2]       // Load two words of peSrc2
+    adds    X_4, X_4, X_8
+    adcs    X_5, X_5, X_9
+
+    ldp     X_6, X_7, [X_1, #16]  // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2, #16]  // Load two words of peSrc2
+    adcs    X_6, X_6, X_8
+    adcs    X_7, X_7, X_9
+
+    cset    X_1, cs                 // If the carry is set, set X_1 to 1 (else 0)
+
+    // Compute sum - Mod into (X_2, X_8, X_9, X_0) = sum - modulus
+
+    ldp     X_2, X_8, [X_0]       // Load two words of pMod
+    subs    X_2, X_4, X_2
+    sbcs    X_8, X_5, X_8
+
+    ldp     X_9, X_0, [X_0, #16]  // Load two words of pMod
+    sbcs    X_9, X_6, X_9
+    sbcs    X_0, X_7, X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_1
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_1, X_1, XZR
+
+    csel    X_4, X_4, X_2, cc
+    csel    X_5, X_5, X_8, cc
+    stp     X_4, X_5, [X_3]
+
+    csel    X_6, X_6, X_9, cc
+    csel    X_7, X_7, X_0, cc
+    stp     X_6, X_7, [X_3, #16]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSub256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 - Src2 into (X_4, X_5, X_6, X_7)
+    ldp     X_4, X_5, [X_1]         // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2]         // Load two words of peSrc2
+    subs    X_4, X_4, X_8
+    sbcs    X_5, X_5, X_9
+
+    ldp     X_6, X_7, [X_1, #16]    // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2, #16]    // Load two words of peSrc2
+    sbcs    X_6, X_6, X_8
+    sbcs    X_7, X_7, X_9
+
+    // Load the masked modulus into (X_1, X_2, X_8, X_9)
+    ldp     X_1, X_2, [X_0]         // Load two words of pMod
+    csel    X_1, X_1, XZR, cc       // If we did not borrow, zero the modulus
+    csel    X_2, X_2, XZR, cc
+
+    ldp     X_8, X_9, [X_0, #16]    // Load two words of pMod
+    csel    X_8, X_8, XZR, cc
+    csel    X_9, X_9, XZR, cc
+
+    // Add the masked modulus
+    adds    X_4, X_4, X_1
+    adcs    X_5, X_5, X_2
+    stp     X_4, X_5, [X_3]
+
+    adcs    X_6, X_6, X_8
+    adc     X_7, X_7, X_9
+    stp     X_6, X_7, [X_3, #16]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+
+
+.macro MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE  T0, T1, Ai, pB, pM, K, Inv64, R0, R1, R2, R3, R4, R5
+    // (\R1, \R2, \R3, \R4, \R5) = \Ai * (B0..3) + (\R1, \R2, \R3, \R4, \R5)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R0) = \K * (M0..3) + (\R0, \R1, \R2, \R3, \R4, \R5)
+    // \K = \Inv64 * \R1
+    // Note \K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // \R5 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \T0, \T1 = scratch
+    // \Ai is also used as scratch when it's no longer needed
+
+    // (\R1, \R2, \R3, \R4, \R5) += \Ai * B even
+    ldr     \T0, [\pB]
+    mul     \T1, \Ai, \T0
+    adds    \R1, \R1, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R2, \R2, \T0
+
+    ldr     \T0, [\pB, #16]
+    mul     \T1, \Ai, \T0
+    adcs    \R3, \R3, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R4, \R4, \T0
+
+    adc     \R5, \R5, XZR // \R5 in range [0,2]
+
+    // (\R2, \R3, \R4, \R5) += \Ai * B odd
+    ldr     \T0, [\pB, #8]
+    mul     \T1, \Ai, \T0
+    adds    \R2, \R2, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R3, \R3, \T0
+
+    ldr     \T0, [\pB, #24]
+    mul     \T1, \Ai, \T0
+    adcs    \R4, \R4, \T1
+    umulh   \Ai, \Ai, \T0
+    adc     \Ai, \Ai, XZR // \Ai in range [0, 2^64-1]
+                        // defer adding \Ai to \R5 to avoid overflowing earlier than necessary
+
+    // (xx, \R1, \R2, \R3, \R4, \R5) = \K * M even + (\R0, \R1, \R2, \R3, \R4, \R5)
+    ldr     \T0, [\pM]
+    mul     \T1,  \K, \T0
+    adds    \R0, \R0, \T1  // Sets \R0 to 0
+    umulh   \T0,  \K, \T0
+    adcs    \R1, \R1, \T0
+
+    ldr     \T0, [\pM, #16]
+    mul     \T1,  \K, \T0
+    adcs    \R2, \R2, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \R3, \R3, \T0
+
+    adcs    \R4, \R4, XZR
+    adc     \R5, \R5, XZR // \R5 in range [0,3]
+
+    // (\R1, \R2, \R3, \R4, \R5, \R0) = \K * M odd + (\R1, \R2, \R3, \R4, \R5, \R0)
+    ldr     \T0, [\pM, #8]
+    mul     \T1,  \K, \T0
+    adds    \R1, \R1, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \R2, \R2, \T0
+
+    ldr     \T0, [\pM, #24]
+    mul     \T1,  \K, \T0
+    adcs    \R3, \R3, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \R4, \R4, \T0
+
+    mul     \K, \R1, \Inv64
+    adcs    \R5, \R5, \Ai
+    cset    \R0, cs
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm)
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc1
+    // X_2 = pSrc2
+    // X_3 = pDst
+    // X_4 = Ai
+    // X_5, X_6 = T0, T1
+    // X_7 = Inv64
+    // X_8 = K
+    // X_9, X_10, X_11, X_12, X_13, X_14 = R0..R5
+
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal): .global PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal)
+
+    ldr     X_4, [X_1]              // Load pSrc1
+
+    ldp     X_10, X_11, [X_2]       // Load pSrc2 B[0], B[1]
+    mul      X_9,  X_4, X_10        // R0 = <63:0>   of A[0]*B[0]
+    umulh   X_10,  X_4, X_10        // R1 = <127:64> of A[0]*B[0]
+
+    mul      X_5,  X_4, X_11        // T0 = <63:0>   of A[0]*B[1]
+    adds    X_10, X_10,  X_5        // Adding the previous word
+    umulh   X_11,  X_4, X_11        // R2 = <127:64> of A[0]*B[1]
+
+    ldp     X_12, X_13, [X_2, #16]  // Load pSrc2 B[2], B[3]
+    mul      X_5,  X_4, X_12        // T0 = <63:0>   of A[0]*B[2]
+    adcs    X_11, X_11,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12,  X_4, X_12        // R3 = <127:64> of A[0]*B[2]
+
+    mul      X_5,  X_4, X_13        // T0 = <63:0>   of A[0]*B[3]
+    adcs    X_12, X_12,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_13,  X_4, X_13        // R4 = <127:64> of A[0]*B[3]
+    adc     X_13, X_13,  XZR
+
+    eor     X_14, X_14, X_14
+
+    ldr     X_7, [X_0, #SymCryptModulusInv64OffsetArm64]
+    mul     X_8, X_9, X_7           // K = R0 * Inv64
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64
+
+    ldr     X_4, [X_1, #8]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_9, X_10, X_11, X_12, X_13, X_14
+
+    ldr     X_4, [X_1, #16]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_10, X_11, X_12, X_13, X_14, X_9
+
+    ldr     X_4, [X_1, #24]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_11, X_12, X_13, X_14, X_9, X_10
+
+    // (xx, R1, R2, R3, R4, R5) = K * M even + (R0, R1, R2, R3, R4, R5)
+
+    ldp      X_4,  X_5, [X_0]   // Load Mod
+    mul      X_1,  X_8, X_4
+    adds    X_12, X_12, X_1     // Sets R0 to 0
+    umulh    X_2,  X_8, X_4
+    adcs    X_13, X_13, X_2
+
+    ldp      X_6,  X_7, [X_0, #16]
+    mul      X_1,  X_8, X_6
+    adcs    X_14, X_14, X_1
+    umulh    X_2,  X_8, X_6
+    adcs     X_9,  X_9, X_2
+
+    adcs    X_10, X_10, XZR
+    adc     X_11, X_11, XZR
+
+    // (R1, R2, R3, R4, R5) = K * M odd + (R1, R2, R3, R4, R5)
+    mul      X_1,  X_8, X_5
+    adds    X_13, X_13, X_1
+    umulh    X_2,  X_8, X_5
+    adcs    X_14, X_14, X_2
+
+    mul      X_1,  X_8, X_7
+    adcs     X_9,  X_9, X_1
+    umulh    X_2,  X_8, X_7
+    adcs    X_10, X_10, X_2
+
+    adc     X_11, X_11, XZR
+
+    // Montgomery-reduced multiplication value in (X_13, X_14, X_9, X_10, X_11), and it is less than 2*Modulus
+    // Compute value - Mod into (X_4, X_5, X_6, X_7), Cy = inverted subtraction borrow
+
+    subs    X_4, X_13, X_4
+    sbcs    X_5, X_14, X_5
+    sbcs    X_6,  X_9, X_6
+    sbcs    X_7, X_10, X_7
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_11
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_11, X_11, XZR
+
+    csel    X_13, X_13, X_4, cc
+    csel    X_14, X_14, X_5, cc
+    stp     X_13, X_14, [X_3]
+
+    csel     X_9,  X_9, X_6, cc
+    csel    X_10, X_10, X_7, cc
+    stp      X_9, X_10, [X_3, #16]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomery256Asm the same
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm)
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc
+    // X_2 = pDst
+
+    mov     X_3, X_2
+    mov     X_2, X_1
+
+    // Just jump into multiply, similar to SymCryptWipeAsm jumping into memset.
+
+    b       PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal)
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 + Src2 into (X_4, X_5, X_6, X_7, X_8, X_9)
+    ldp     X_4,  X_5,  [X_1]       // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2]       // Load two words of peSrc2
+    adds    X_4,  X_4,  X_10
+    adcs    X_5,  X_5,  X_11
+
+    ldp     X_6,  X_7,  [X_1, #16]  // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #16]  // Load two words of peSrc2
+    adcs    X_6,  X_6,  X_10
+    adcs    X_7,  X_7,  X_11
+
+    ldp     X_8,  X_9,  [X_1, #32]  // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #32]  // Load two words of peSrc2
+    adcs    X_8,  X_8,  X_10
+    adcs    X_9,  X_9,  X_11
+
+    cset    X_1, cs                 // If the carry is set, set X_1 to 1 (else 0)
+
+    // Compute sum - Mod into (X_2, X_10, X_11, X_12, X_13, X_0) = sum - modulus
+
+    ldp     X_2, X_10,  [X_0]       // Load two words of pMod
+    subs    X_2,  X_4,  X_2
+    sbcs    X_10, X_5,  X_10
+
+    ldp     X_11, X_12, [X_0, #16]  // Load two words of pMod
+    sbcs    X_11, X_6,  X_11
+    sbcs    X_12, X_7,  X_12
+
+    ldp     X_13, X_0,  [X_0, #32]  // Load two words of pMod
+    sbcs    X_13, X_8,  X_13
+    sbcs    X_0,  X_9,  X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_1
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_1, X_1, XZR
+
+    csel    X_4, X_4, X_2,  cc
+    csel    X_5, X_5, X_10, cc
+    stp     X_4, X_5, [X_3]
+
+    csel    X_6, X_6, X_11, cc
+    csel    X_7, X_7, X_12, cc
+    stp     X_6, X_7, [X_3, #16]
+
+    csel    X_8, X_8, X_13, cc
+    csel    X_9, X_9, X_0,  cc
+    stp     X_8, X_9, [X_3, #32]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSub384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 - Src2 into (X_4, X_5, X_6, X_7, X_8, X_9)
+    ldp     X_4,  X_5,  [X_1]         // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2]         // Load two words of peSrc2
+    subs    X_4,  X_4,  X_10
+    sbcs    X_5,  X_5,  X_11
+
+    ldp     X_6,  X_7,  [X_1, #16]    // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #16]    // Load two words of peSrc2
+    sbcs    X_6,  X_6,  X_10
+    sbcs    X_7,  X_7,  X_11
+
+    ldp     X_8,  X_9,  [X_1, #32]    // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #32]    // Load two words of peSrc2
+    sbcs    X_8,  X_8,  X_10
+    sbcs    X_9,  X_9,  X_11
+
+    // Load the masked modulus into (X_1, X_2, X_10, X_11, X_12, X_0)
+    ldp     X_1, X_2, [X_0]         // Load two words of pMod
+    csel    X_1, X_1, XZR, cc       // If we did not borrow, zero the modulus
+    csel    X_2, X_2, XZR, cc
+
+    ldp     X_10, X_11, [X_0, #16]  // Load two words of pMod
+    csel    X_10, X_10, XZR, cc
+    csel    X_11, X_11, XZR, cc
+
+    ldp     X_12, X_0,  [X_0, #32]  // Load two words of pMod
+    csel    X_12, X_12, XZR, cc
+    csel    X_0,  X_0,  XZR, cc
+
+    // Add the masked modulus
+    adds    X_4, X_4, X_1
+    adcs    X_5, X_5, X_2
+    stp     X_4, X_5, [X_3]
+
+    adcs    X_6, X_6, X_10
+    adcs    X_7, X_7, X_11
+    stp     X_6, X_7, [X_3, #16]
+
+    adcs    X_8, X_8, X_12
+    adc     X_9, X_9, X_0
+    stp     X_8, X_9, [X_3, #32]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+
+
+.macro MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE  T0, T1, Ai, pB, K, N3, R0, R1, R2, R3, R4, R5, R6, R7
+    // (\R1, \R2, \R3, \R4, \R5, \R6, \R7) = \Ai * (B0..5) + (\R1, \R2, \R3, \R4, \R5, \R6, \R7)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R6, \R7, \R0) = \K * (M0..5) + (\R0, \R1, \R2, \R3, \R4, \R5, \R6, \R7)
+    // Note \K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \R7 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \N3 in [0, 2] at start and end
+    // \T0, \T1 = scratch
+    // \Ai is also used as scratch when it's no longer needed
+
+    // (\R1, \R2, \R3, \R4, \R5, \R6, \R7) += \Ai * B even
+    ldr     \T0, [\pB]
+    mul     \T1, \Ai, \T0
+    adds    \R1, \R1, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R2, \R2, \T0
+
+    ldr     \T0, [\pB, #16]
+    mul     \T1, \Ai, \T0
+    adcs    \R3, \R3, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R4, \R4, \T0
+
+    ldr     \T0, [\pB, #32]
+    mul     \T1, \Ai, \T0
+    adcs    \R5, \R5, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R6, \R6, \T0
+
+    adc     \R7, \R7, XZR // \R7 in range [0,2]
+
+    // (\R2, \R3, \R4, \R5, \R6, \R7) += \Ai * B odd
+    ldr     \T0, [\pB, #8]
+    mul     \T1, \Ai, \T0
+    adds    \R2, \R2, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R3, \R3, \T0
+
+    ldr     \T0, [\pB, #24]
+    mul     \T1, \Ai, \T0
+    adcs    \R4, \R4, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R5, \R5, \T0
+
+    ldr     \T0, [\pB, #40]
+    mul     \T1, \Ai, \T0
+    adcs    \R6, \R6, \T1
+    umulh   \Ai, \Ai, \T0
+    adc     \Ai, \Ai, XZR // \Ai in range [0, 2^64-1]
+                        // defer adding \Ai to \R7 to avoid overflowing earlier than necessary
+
+    // P384 has a modulus with a form which makes it possible to perform Montgomery reduction
+    // in an interesting way. The modulus is:
+    // 2^384 - 2^128 - 2^96 + 2^32 - 1
+    // This means when we want to add a multiple of the Modulus in reduction, we can actually
+    // avoid multiplication instructions entirely.
+    // On Arm64, multiplication instruction throughput is often the bottleneck, and there is no
+    // distinction in cost between addition and subtraction (unlike on AMD64).
+    // So we do the reduction directly using shifts, adds, and subtracts.
+    // On entry to this macro we have \N3 representing a deferred subtraction (in range [0,2]) from the
+    // 3rd word of the last macro (now aligned to the 2nd word of this macro), on exit we set \N3 to be
+    // the subtraction to defer to the next round.
+    //
+    //            ____ ____ ____ ____ ____ ____
+    //           | M5 | M4 | M3 | M2 | M1 | M0 |
+    //           | ff | ff | ff | fe | f0 | 0f | (P384 modulus if you sign extend each nibble to 32b)
+    //           |____|____|____|____|____|____|
+    //  ____ ____ ____ ____ ____ ____ ____ ____
+    // |    |    |\  /|\  /|\  /|    |    |    |
+    // | \R7 | \R6 | \R5 | \R4 | \R3 | \R2 | \R1 | \R0 |   R0h = \R0 >> 32
+    // |____|____|/__\|/__\|/__\|____|____|____|   R0l = \R0 mod 2^32
+    //                                      | x Inv64 (0x0000000100000001)
+    //                                     _v__
+    //                      ____          |    |
+    //                     |    |         |  \K | \K = (\R0 + (\R0 << 32)) mod 2^64
+    //                     | \N3 |         |____|   = ((R0h +32 R0l) << 32) + R0l
+    //       ____          |____|____                      ^^^ by +32 we mean addition modulo 2^32
+    //      |    |              |    |
+    //    + |  \K |            - |  \K |
+    //      |____|              |____|____
+    //                          |  \K |  \K |
+    //                        - |>>32|<<32|   \K << 32 mod 2^64 = R0l << 32
+    //                          |____|____|   \K >> 32 mod 2^64 = R0h +32 R0l
+    //                               |    |
+    //                             + |  J |   J = (\K*M0 + \R0) >> 64
+    //                           ____|____|
+    //                          |    |
+    //                        - |N3in|
+    //                          |____|
+    //
+    // The logic for how we compute J is a little involved:
+    // (\K * M0) + \R0 = (((R0h +32 R0l) << 32) + R0l) * (1<<32 - 1)                        + \R0
+    //               = (((R0h +32 R0l) << 64) + R0l<<32) - (((R0h +32 R0l) << 32) + R0l)  + R0h<<32 + R0l
+    //               =  ((R0h +32 R0l) << 64) + R0l<<32  -  ((R0h +32 R0l) << 32)         + R0h<<32
+    //               =  ((R0h +32 R0l) << 64) + (((R0h + R0l) - (R0h +32 R0l)) << 32)
+    //                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is exactly bit 33 of R0h+R0l
+    //               =  ((R0h +32 R0l) + ((R0h +33 R0l)>>32)) << 64
+    // => J = (R0h +32 R0l) + ((R0h +33 R0l)>>32)
+
+    lsl     \T0, \R0, #32     // \T0 = \R0<<32 = \K<<32
+    adds     \K, \R0, \T0      //  \K = \R0 + \T0 = \R0 + (\R0<<32) = \R0 * Inv64
+    lsr     \T1, \K, #32      // \T1 = \K>>32
+    adcs    \R1, \R1, \T1      // \R1 = \R1 + J = \R1 + \T1 + carry from setting \K
+    add     \T1, \T1, \N3      // \T1 += N3in; cannot overflow as \T1 < 2^32
+    adcs    \R2, \R2, XZR
+    csetm   \N3, cs          // \N3 -1 if carry, else 0
+
+    subs    \R1, \R1, \T0      // subtract [ \K>>32 + N3in || \K<<32 ]
+    sbcs    \R2, \R2, \T1
+    cinc    \N3, \N3, cc      // \N3 += 1 iff borrow; \N3 in [0, 1] as carry in addition => borrow here
+
+    subs    \R2, \R2, \K       // subtract \K
+    cinc    \N3, \N3, cc      // \N3 += 1 iff borrow; \N3 in [0, 2]
+
+    adds    \R6, \R6, \K       // add \K
+    adcs    \R7, \R7, \Ai
+    cset    \R0, cs
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+#define X_16 x16
+#define W_16 w16
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm)
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc1
+    // X_2 = pSrc2
+    // X_3 = pDst
+    // X_4 = Ai
+    // X_5, X_6, X_7 = T0, T1, N3
+    // X_8 = K
+    // X_9, X_10, X_11, X_12, X_13, X_14, X_15, X_16 = R0..R7
+
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal): .global PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal)
+
+    ldr     X_4, [X_1]              // Load pSrc1
+
+    ldp     X_10, X_11, [X_2]       // Load pSrc2 B[0], B[1]
+    mul      X_9,  X_4, X_10        // R0 = <63:0>   of A[0]*B[0]
+    umulh   X_10,  X_4, X_10        // R1 = <127:64> of A[0]*B[0]
+
+    mul      X_5,  X_4, X_11        // T0 = <63:0>   of A[0]*B[1]
+    adds    X_10, X_10,  X_5        // Adding the previous word
+    umulh   X_11,  X_4, X_11        // R2 = <127:64> of A[0]*B[1]
+
+    ldp     X_12, X_13, [X_2, #16]  // Load pSrc2 B[2], B[3]
+    mul      X_5,  X_4, X_12        // T0 = <63:0>   of A[0]*B[2]
+    adcs    X_11, X_11,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12,  X_4, X_12        // R3 = <127:64> of A[0]*B[2]
+
+    mul      X_5,  X_4, X_13        // T0 = <63:0>   of A[0]*B[3]
+    adcs    X_12, X_12,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_13,  X_4, X_13        // R4 = <127:64> of A[0]*B[3]
+
+    ldp     X_14, X_15, [X_2, #32]  // Load pSrc2 B[4], B[5]
+    mul      X_5,  X_4, X_14        // T0 = <63:0>   of A[0]*B[4]
+    adcs    X_13, X_13,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_14,  X_4, X_14        // R3 = <127:64> of A[0]*B[4]
+
+    mul      X_5,  X_4, X_15        // T0 = <63:0>   of A[0]*B[5]
+    adcs    X_14, X_14,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_15,  X_4, X_15        // R4 = <127:64> of A[0]*B[5]
+    adc     X_15, X_15,  XZR
+
+    eor     X_16, X_16, X_16
+    eor     X_7, X_7, X_7
+
+    ldr     X_4, [X_1, #8]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_9, X_10, X_11, X_12, X_13, X_14, X_15, X_16
+
+    ldr     X_4, [X_1, #16]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_10, X_11, X_12, X_13, X_14, X_15, X_16, X_9
+
+    ldr     X_4, [X_1, #24]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_11, X_12, X_13, X_14, X_15, X_16, X_9, X_10
+
+    ldr     X_4, [X_1, #32]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_12, X_13, X_14, X_15, X_16, X_9, X_10, X_11
+
+    ldr     X_4, [X_1, #40]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_13, X_14, X_15, X_16, X_9, X_10, X_11, X_12
+
+    // see MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE for how this reduction works
+    lsl     X_5, X_14, #32  // T0 = R0<<32 = K<<32
+    adds    X_8, X_14, X_5  //  K = R0 + T0 = R0 + (R0<<32)
+    lsr     X_6, X_8, #32   // T1 = K>>32
+    adcs    X_15, X_15, X_6 // R1 = R1 + J = R1 + T1 + carry from setting K
+    add     X_6, X_6, X_7   // T1 += N3in; cannot overflow as T1 < 2^32
+    adcs    X_16, X_16, XZR
+    csetm   X_7, cs         // N3 -1 if carry, else 0
+
+    subs    X_15, X_15, X_5 // subtract K<<32
+    sbcs    X_16, X_16, X_6
+    cinc    X_7, X_7, cc    // N3 += 1 iff borrow; N3 in [0, 1] as carry in addition => borrow here
+
+    subs    X_16, X_16, X_8 // subtract K
+    sbcs    X_9, X_9, X_7   // subtract N3 and borrow if any
+    sbcs    X_10, X_10, XZR
+    sbcs    X_11, X_11, XZR
+    sbcs    X_12, X_12, XZR
+    sbc     X_13, X_13, XZR
+
+    adds    X_12, X_12, X_8 // add K
+    adc     X_13, X_13, XZR
+
+    // Montgomery-reduced multiplication value in (X_15, X_16, X_9, X_10, X_11, X_12, X_13), and it is less than 2*Modulus
+    // Compute value - Mod into (X_4, X_5, X_6, X_7, X_14, X_0), Cy = inverted subtraction borrow
+
+    // "Load" Mod into X4, X5, X6, X0
+    mov     X_4, 0x00000000ffffffff
+    mov     X_5, 0xffffffff00000000
+    mov     X_6, 0xfffffffffffffffe
+    mov     X_0, 0xffffffffffffffff
+
+    subs     X_4, X_15,  X_4
+    sbcs     X_5, X_16,  X_5
+    sbcs     X_6,  X_9,  X_6
+    sbcs     X_7, X_10,  X_0
+    sbcs    X_14, X_11,  X_0
+    sbcs     X_0, X_12,  X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_11
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_13, X_13, XZR
+
+    csel    X_15, X_15,  X_4, cc
+    csel    X_16, X_16,  X_5, cc
+    stp     X_15, X_16, [X_3]
+
+    csel     X_9,  X_9,  X_6, cc
+    csel    X_10, X_10,  X_7, cc
+    stp      X_9, X_10, [X_3, #16]
+
+    csel    X_11, X_11, X_14, cc
+    csel    X_12, X_12,  X_0, cc
+    stp     X_11, X_12, [X_3, #32]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryP384Asm the same
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+#define X_16 x16
+#define W_16 w16
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm)
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc
+    // X_2 = pDst
+
+    mov     X_3, X_2
+    mov     X_2, X_1
+
+    // Just jump into multiply, similar to SymCryptWipeAsm jumping into memset.
+
+    b       PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal)
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Register assignments
+//       X_0  = pMod (moving forward one *digit* every inner loop)
+//       X_1  = pSrc (moving forward one *digit* every inner loop)
+//       X_2  = pDst (used only in the end for subtract / result)
+//       X_3  = byte count of pSrc and pMod (sometimes byte count - 1 digit of bytes)
+//       X_4  = word count of pSrc
+//       X_5  = Inv64 of the modulus
+//       X_6  = m = pSrc[i]*Inv64
+//       X_7  = hc = high carry variable
+//       X_8-X_19 = temporaries
+//       X_20 = c
+//       X_21 = Stored byte count of pSrc
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+#define X_14 x14
+#define W_14 w14
+#define X_15 x15
+#define W_15 w15
+#define X_16 x16
+#define W_16 w16
+#define X_17 x17
+#define W_17 w17
+#define X_18 x19
+#define W_18 w19
+#define X_19 x20
+#define W_19 w20
+#define X_20 x21
+#define W_20 w21
+#define X_21 x22
+#define W_21 w22
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm)
+    stp fp, lr, [sp, #-48]! // allocate 48 bytes of stack; store FP/LR
+    .seh_save_fplr_x 48
+    stp X_18, X_19, [sp, #16]
+    .seh_save_regp X_18, 16
+    stp X_20, X_21, [sp, #32]
+    .seh_save_regp X_20, 32
+    .seh_endprologue
+
+    ldr     W_4, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64      // pMod
+
+    lsl     X_3, X_4, #5                // Multiply by 32 to get the number of bytes
+    lsl     X_4, X_4, #2                // Multiply by 4 to get the number of words
+
+    sub     X_3, X_3, #32               // offset the byte count to make it easy to restore pointers
+
+    mov     X_7, xzr                    // Set hc to 0
+    mov     X_21, X_3                   // Store the byte count for later
+
+    //
+    // Main loop
+    //
+LABEL(SymCryptFdefMontgomeryReduceAsmOuter)
+    ldp     X_16, X_17, [X_1]           // pSrc[i+0], pSrc[i+1]
+    mul     X_6, X_16, X_5              // <63:0> bits of pSrc[i]*Inv64 = m
+
+    ldp     X_8,  X_9,  [X_0]           // pMod[0], pMod[1]
+    umulh   X_8,  X_6, X_8              // Bits <127:64> of m*pMod[0]
+    subs    xzr,  X_16, #1              // Subtract 1 from pSrc[i+0] (generate a carry iff pSrc[i] is non-zero)
+
+    mul     X_13, X_6, X_9              // Bits <63:0> of m*pMod[1]
+    umulh   X_9,  X_6, X_9              // Bits <127:64> of m*pMod[1]
+    adcs    X_8,  X_8,  X_13            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_10, X_11, [X_0, #16]      // pMod[2], pMod[3]
+    mul     X_14, X_6, X_10             // Bits <63:0> of m*pMod[2]
+    umulh   X_10, X_6, X_10             // Bits <127:64> of m*pMod[2]
+    adcs    X_9,  X_9,  X_14            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_18, X_19, [X_1, #16]      // pSrc[i+2], pSrc[i+3]
+    mul     X_15, X_6, X_11             // Bits <63:0> of m*pMod[3]
+    umulh   X_11, X_6, X_11             // Bits <127:64> of m*pMod[3]
+    adcs    X_10, X_10, X_15            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    adc     X_20, X_11, xzr             // Add the carry if any
+
+    // ( X_20, X_10, X_9, X_8, ___ ) = (( pMod[3], pMod[2], pMod[1], pMod[0] ) * m)
+    // We do not compute this --^ because (m*pMod[0] + pSrc[i]) == 0. We already accounted for the carry above.
+
+    adds    X_17, X_17, X_8             // Adding pSrc[i+1]
+    adcs    X_18, X_18, X_9             // Adding pSrc[i+2]
+    adcs    X_19, X_19, X_10            // Adding pSrc[i+3]
+
+    stp     xzr,  X_17, [X_1]           // pSrc[i+0], pSrc[i+1]
+    stp     X_18, X_19, [X_1, #16]      // pSrc[i+2], pSrc[i+3]
+
+    cbz    X_3, SymCryptFdefMontgomeryReduceAsmInnerEnd
+
+LABEL(SymCryptFdefMontgomeryReduceAsmInner)
+    // Invariant - X_20 (c) + carry flag is a value in the range [0, 2^64-1]
+    // We could always adc X_20, X_20, xzr before loop entry, but this is a wasted instruction
+    // vs. adcs with X_20 below
+
+    ldp     X_8,  X_9,  [X_0, #32]!     // pMod[j+0], pMod[j+1]
+    mul     X_12, X_6, X_8              // Bits <63:0> of m*pMod[j]
+    umulh   X_8,  X_6, X_8              // Bits <127:64> of m*pMod[j]
+    adcs    X_12, X_20, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    mul     X_13, X_6, X_9              // Bits <63:0> of m*pMod[j+1]
+    umulh   X_9,  X_6, X_9              // Bits <127:64> of m*pMod[j+1]
+    ldp     X_16, X_17, [X_1, #32]!     // pSrc[i+j+0], pSrc[i+j+1]
+    adcs    X_8,  X_8,  X_13            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_10, X_11, [X_0, #16]      // pMod[j+2], pMod[j+3]
+    mul     X_14, X_6, X_10             // Bits <63:0> of m*pMod[j+2]
+    umulh   X_10, X_6, X_10             // Bits <127:64> of m*pMod[j+2]
+    adcs    X_9,  X_9,  X_14            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_18, X_19, [X_1, #16]      // pSrc[i+j+2], pSrc[i+j+3]
+    mul     X_15, X_6, X_11             // Bits <63:0> of m*pMod[j+3]
+    umulh   X_11, X_6, X_11             // Bits <127:64> of m*pMod[j+3]
+    adcs    X_10, X_10, X_15            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    adc     X_20, X_11, xzr             // Add the carry if any
+
+    // ( X_20, X_10, X_9, X_8, X_12 ) = (( pMod[j+3], pMod[j+2], pMod[j+1], pMod[j+0] ) * m) + c
+
+    adds    X_16, X_16, X_12            // Adding pSrc[i+j+0]
+    adcs    X_17, X_17, X_8             // Adding pSrc[i+j+1]
+    adcs    X_18, X_18, X_9             // Adding pSrc[i+j+2]
+    adcs    X_19, X_19, X_10            // Adding pSrc[i+j+3]
+
+    stp     X_16, X_17, [X_1]           // pSrc[i+j+0], pSrc[i+j+1]
+    stp     X_18, X_19, [X_1, #16]      // pSrc[i+j+2], pSrc[i+j+3]
+
+    sub     X_3, X_3, #32               // Move one digit up
+    cbnz    X_3, SymCryptFdefMontgomeryReduceAsmInner
+
+LABEL(SymCryptFdefMontgomeryReduceAsmInnerEnd)
+
+    ldr     X_8, [X_1, #32]             // pSrc[nWords]
+    adcs    X_20, X_20, X_7             // c + hc
+    adc     X_7, xzr, xzr               // Add the carry if any
+
+    adds    X_20, X_20, X_8             // c + hc + pSrc[nWords]
+    adc     X_7, X_7, xzr               // Add the carry if any and store into hc
+
+    str     X_20, [X_1, #32]            // pSrc[nWords] = c
+
+    subs    X_4, X_4, #1                // Move one word up
+
+    sub     X_0, X_0, X_21              // Restore pMod pointer (subtract byte count)
+    sub     X_1, X_1, X_21              // Restore pSrc pointer (subtract byte count)
+    add     X_1, X_1, #8                // Move pSrc pointer one word up
+
+    mov     X_3, X_21                   // Restore the byte counter
+
+    bne     SymCryptFdefMontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+    add     X_3, X_3, #32           // restore the full byte count for loops using unconditional pre-indexing
+    mov     X_21, X_3               // Store the byte count for later
+
+    sub     X_0, X_0, #32           // offset pMod so we can use pre-increment form of loads
+    sub     X_1, X_1, #32           // offset pSrc so we can use pre-increment form of loads
+    sub     X_2, X_2, #32           // offset pDst so we can use pre-increment form of loads
+    mov     X_14, X_2               // Store pDst pointer
+
+    // Prepare the pointers for subtract
+    mov     X_10, X_7               // X_10 = hc
+    subs    X_4, X_4, X_4           // Set the carry flag (i.e. no borrow)
+
+LABEL(SymCryptFdefMontgomeryReduceRawSubAsmLoop)
+    sub     X_3, X_3, #32           // Decrement the byte count by 32
+    // borrow is in the carry flag (flipped)
+
+    ldp     X_4, X_6, [X_1, #32]!   // Load two words of pSrc
+    ldp     X_5, X_7, [X_0, #32]!   // Load two words of pMod
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_1, #16]    // Load two words of pSrc
+    ldp     X_5, X_7, [X_0, #16]    // Load two words of pMod
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefMontgomeryReduceRawSubAsmLoop
+
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    orr     X_11, X_10, X_0         // X_11 = hc|d
+
+    // Prepare the pointers for masked copy
+    sub     X_0, X_1, X_21          // Restore pSrc pointer (subtract byte count)
+    mov     X_1, X_14               // pDst
+
+    mov     X_2, X_21               // Restore the byte counter
+    subs    X_4, X_10, X_11         // If (X_11 > X_10) clear the carry flag (i.e. borrow)
+
+LABEL(SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop)
+    sub     X_2, X_2, #32           // decrement the byte count by 32
+
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of the source
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1]         // Store the two words in the destination
+
+    ldp     X_4, X_6, [X_0, #16]
+    ldp     X_5, X_7, [X_1, #16]
+    csel    X_4, X_4, X_5, cc
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1, #16]
+
+    cbnz    X_2, SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    ldp X_20, X_21, [sp, #32]
+    ldp X_18, X_19, [sp, #16]
+    ldp fp, lr, [sp], #48 // deallocate 48 bytes of stack; restore FP/LR
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+#undef X_17
+#undef W_17
+#undef X_18
+#undef W_18
+#undef X_19
+#undef W_19
+#undef X_20
+#undef W_20
+#undef X_21
+#undef W_21
+
+.macro MULADD_LOADSTORE18  pS, pM, pD, K, Tc, T0, T1, T2, T3, T4, T5
+    // \Tc:D[3:0] = (M[3:0] * \K) + S[3:0] + \Tc
+    //
+    // QH = \K
+    // \T0..\T5 = scratch
+    // \Tc is carry in at start and carry out at end (in [0,2^64-1])
+
+    // \Tc:T[5:2] = S[3:0] + (\K * M odd) + \Tc
+    ldp     \T2, \T3, [\pS]        // load S[0], S[1]
+    adds    \T2, \T2, \Tc
+
+    ldr     \T0, [\pM, #8]
+    mul     \T1,  \K, \T0
+    adcs    \T3, \T3, \T1
+    umulh   \T0,  \K, \T0
+    ldp     \T4, \T5, [\pS, #16]   // load S[2], S[3]
+    adcs    \T4, \T4, \T0
+
+    ldr     \T0, [\pM, #24]
+    mul     \T1,  \K, \T0
+    adcs    \T5, \T5, \T1
+    umulh   \Tc,  \K, \T0
+    adc     \Tc, \Tc, XZR
+
+    // \Tc:D[3:0] = \Tc:T[5:2] + (\K * M even)
+    ldr     \T0, [\pM]
+    mul     \T1,  \K, \T0
+    adds    \T2, \T2, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \T3, \T3, \T0
+    stp     \T2, \T3, [\pD]        // store D[0], D[1]
+
+    ldr     \T0, [\pM, #16]
+    mul     \T1,  \K, \T0
+    adcs    \T4, \T4, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \T5, \T5, \T0
+    stp     \T4, \T5, [\pD, #16]   // store D[2], D[3]
+
+    adc     \Tc, \Tc, XZR
+.endm
+
+.macro SHIFTRIGHT2  pD, index, shrVal, shrMask, shlVal, Tcin, Tcout, T0, T1
+    // \T0:\Tcout = D[\index + 1:\index]
+    // D[\index + 1] = \Tcin:\T0 >> \shrVal (== ((\T0>>\shrVal)&\shrMask) | (\Tcin<<\shlVal))
+    // D[\index] = \T0:\Tcout >> \shrVal
+    //
+    // \shrVal in [1,64]
+    // \shrMask = (\shrVal == 64) ? 0 : -1
+    // \shlVal = 64-\shrVal
+    //
+    // \T0, \T1 = scratch
+    // Value of \Tcin is bits to shift in the top at start
+    // Value of \Tcout is bits to shift into next macro invocation at end
+
+    ldp     \Tcout, \T0, [\pD, #(\index*8)]
+
+    lsr     \T1, \T0, \shrVal
+    lsl     \Tcin, \Tcin, \shlVal
+    and     \T1, \T1, \shrMask
+    orr     \T1, \T1, \Tcin
+
+    lsr     \Tcin, \Tcout, \shrVal
+    lsl     \T0, \T0, \shlVal
+    and     \Tcin, \Tcin, \shrMask
+    orr     \Tcin, \Tcin, \T0
+
+    stp     \Tcin, \T1, [\pD, #(\index*8)]
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModDivSmallPow2Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _In_range_(1, 64)               UINT32                  exp,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x13
+#define W_13 w13
+PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm)
+    .seh_endprologue
+    ldr     X_7, [X_1]
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+
+    mul     X_7, X_7, X_5   // <63:0> bits of pSrc[0]*Inv64
+                            // this is the multiple of the modulus that must be added to peSrc
+                            // s.t. the least significant **64** bits are all zero
+
+    // Form bitmask to mask out bottom exp bits from multiplication result
+    mov     X_6, #-1
+    neg     X_5, X_2
+    lsr     X_6, X_6, X_5   // mask = -1 >> (-exp % 64)
+                            //      = 0xffffffffffffffff >> (64-exp)
+                            //      = (((UINT65)1)<<exp)-1
+
+    and     X_7, X_7, X_6   // Extract the least significant exp bits from X_7 to produce K
+                            // K is the minimal multiple of the modulus that when added to peSrc
+                            // sets the least significant **exp** bits to zero
+
+    ldr     W_4, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits, used directly as loop counter
+    add     X_5, X_0, #SymCryptModulusValueOffsetArm64      // pointer to modulus value
+
+    eor     X_6, X_6, X_6   // zero one word carry
+
+LABEL(SymCryptFdefModDivSmallPow2AsmMulAddLoop)
+    // Set peDst = peSrc + (K * mod)
+    // Also has up to 64b of carry out the top which is stored in Q7 at the end of the loop
+    //
+    // W_4  loop ctr (one iteration per digit)
+    // X_7  K
+    // X_1  running pointer peSrc inner loop (we directly modify X_1 as we only need to read peSrc once)
+    // X_5  running pointer pMod inner loop
+    // X_3  running pointer peDst inner loop (we directly modify X_3 as we read/write peDst in reverse in second loop)
+    // X_6  one word carry
+    // X_8..X_13 scratch
+
+    MULADD_LOADSTORE18 X_1, X_5, X_3, X_7, X_6, X_8, X_9, X10, X_11, X_12, X_13
+
+    // Move on to the next digit
+    add     X_1, X_1, #32
+    add     X_5, X_5, #32
+    add     X_3, X_3, #32
+
+    sub     W_4, W_4, #1
+    cbnz    W_4, SymCryptFdefModDivSmallPow2AsmMulAddLoop
+
+    ldr     W_0, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits, used directly as loop counter
+    mov     X_1, #64
+    subs    X_1, X_1, X_2   // shlVal = 64-exp
+    csetm   X_4, ne         // shrMask = ((exp != 64) ? -1 : 0) = ((exp == 64) ? 0 : -1)
+
+    sub     X_3, X_3, #32  // X_3 points to the most significant digit of peDst
+
+LABEL(SymCryptFdefModDivSmallPow2AsmShiftRightLoop)
+    // Set peDst = peDst >> exp
+    // We work backwards (from MSB to LSB) so we avoid having to do special handling of the one word
+    // carry out of the top of the multiply add loop
+    //
+    // W_0  loop ctr (one iteration per digit)
+    // X_1  shlVal
+    // X_4  shrMask
+    // X_2  shrVal
+    // X_3  running pointer peDst inner loop (decreasing)
+    // X_6  one word carry even
+    // X_7  one word carry odd
+    // X_8, X_9 scratch
+
+    SHIFTRIGHT2 X_3, 2, X_2, X_4, X_1, X_6, X_7, X_8, X_9
+    SHIFTRIGHT2 X_3, 0, X_2, X_4, X_1, X_7, X_6, X_8, X_9
+
+    sub     X_3, X_3, #32
+
+    sub     W_0, W_0, #1
+    cbnz    W_0, SymCryptFdefModDivSmallPow2AsmShiftRightLoop
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64/fdef_asm.symcryptasm b/libs/symcrypt/lib/arm64/fdef_asm.symcryptasm
new file mode 100644
index 000000000000..b806f728c50f
--- /dev/null
+++ b/libs/symcrypt/lib/arm64/fdef_asm.symcryptasm
@@ -0,0 +1,1587 @@
+//
+//  fdef_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format for the arm64 architecture
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawAddAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm), 4, 8)
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    adds    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbz     X_3, SymCryptFdefRawAddAsmEnd
+
+LABEL(SymCryptFdefRawAddAsmLoop)
+    // carry is in the carry flag
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefRawAddAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdefRawAddAsmEnd)
+    cset    X_0, cs                 // Set the return value equal to the carry
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm))
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSubAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm), 4, 8)
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    subs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbz     X_3, SymCryptFdefRawSubAsmEnd
+
+LABEL(SymCryptFdefRawSubAsmLoop)
+    // borrow is in the carry flag (flipped)
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefRawSubAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdefRawSubAsmEnd)
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopyAsm(
+//    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm), 4, 4)
+
+    dup     v0.4s, W_3              // broadcast the mask to v0
+
+LABEL(SymCryptFdefMaskedCopyAsmLoop)
+    ldp     q1, q3, [X_0], #32      // Load 4 words of the source
+    ldp     q2, q4, [X_1]           // Load 4 words of the destination
+    bit     v2.16b, v1.16b, v0.16b  // if the mask is 1s, overwrite the destination with source
+    bit     v4.16b, v3.16b, v0.16b  // if the mask is 1s, overwrite the destination with source
+    stp     q2, q4, [X_1], #32      // Store the two words in the destination
+
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+
+    cbnz    X_2, SymCryptFdefMaskedCopyAsmLoop
+
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMulAsm(
+//    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                    UINT32      nDigits1,
+//    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                    UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+//
+// Basic structure:
+//   for each word in Src1:
+//       Dst += Src2 * word
+//
+// Register assignments
+//       X_0  = pSrc1 (moving forward one word every outer loop)
+//       X_1  = word count of pSrc1
+//       X_2  = pSrc2 (moving forward one *digit* every inner loop)
+//       X_3  = digit count of pSrc2 and pDst
+//       X_4  = pDst (moving forward one *digit* every inner loop)
+//       X_5  = Stored pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc1
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = Scratch registers for holding the results of multiplies
+//       X_13 = Stored pSrc2
+//       X_14 = Stored digit count of pSrc2
+//       X_15 = Scratch register for holding the results of multiplies
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm), 5, 16)
+
+    lsl     X_1, X_1, #2                // Calculate word count
+
+    sub     X_2, X_2, #32               // offset pSrc2 so we can use pre-increment form of loads
+    sub     X_4, X_4, #32               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_5, X_4                    // store pDst
+    mov     X_13, X_2                   // store pSrc2
+    mov     X_14, X_3                   // store nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+
+LABEL(SymCryptFdefRawMulAsmLoopInner1)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #32]!       // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+1]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+1]
+
+    stp     X_11, X_15, [X_4, #32]!     // Store to destination
+    ldp     X_7, X_8, [X_2, #16]        // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j+2]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j+2]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+3]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+3]
+
+    stp     X_11, X_15, [X_4, #16]      // Store to destination
+
+    cbnz    X_3, SymCryptFdefRawMulAsmLoopInner1
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #32]
+
+    sub     X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdefRawMulAsmLoopOuter)
+    mov     X_3, X_14                   // set nDigits2
+    mov     X_2, X_13                   // set pSrc2
+    mov     X_4, X_5                    // set pDst
+
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the next word from pSrc1
+
+LABEL(SymCryptFdefRawMulAsmLoopInner)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #32]!       // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #32]!      // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+1]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+1]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4]            // Store to destination
+
+    ldp     X_7, X_8, [X_2, #16]        // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #16]       // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j+2]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+3]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j+2]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+3]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4, #16]       // Store to destination
+
+    cbnz    X_3, SymCryptFdefRawMulAsmLoopInner
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #32]
+
+    subs    X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    bne     SymCryptFdefRawMulAsmLoopOuter
+
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm))
+
+    // Macro for the first loop of the first pass of RawSquareAsm.
+    // It takes one word from the source, multiplies it with the mulword,
+    // adds the high level word of the previous macro call, and stores it into
+    // the destination.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^64-1)^2 + 2^64-1 = 2^128 - 2^64
+MACRO_START(SQR_SINGLEADD_64, index, src_reg, dst_reg, mul_word, src_carry, dst_carry, scratch0, scratch1)
+
+    ldr     scratch0, [src_reg, #8*index]   // pSrc[i+j]
+
+    mul     scratch1, mul_word, scratch0    // Bits <63:0> of pSrc[i]*pSrc[i+j]
+    adds    scratch1, scratch1, src_carry   // Adding the previous word
+    umulh   dst_carry, mul_word, scratch0   // Bits <127:64> of pSrc[i]*pSrc[i+j]
+    adc     dst_carry, dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    str     scratch1, [dst_reg, #8*index]   // Store to destination
+
+MACRO_END()
+
+    // Macro for the remaining loops of the first pass of RawSquareAsm.
+    // The only difference to the above is that it also adds the word loaded
+    // from the destination buffer.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^64-1)^2 + 2(2^64-1) = 2^128 - 1
+MACRO_START(SQR_DOUBLEADD_64, index, src_reg, dst_reg, mul_word, src_carry, dst_carry, scratch0, scratch1, scratch2)
+
+    ldr     scratch0, [src_reg, #8*index]   // pSrc[i+j]
+    ldr     scratch2, [dst_reg, #8*index]   // pDst[2*(i+j)]
+
+    mul     scratch1, mul_word, scratch0    // Bits <63:0> of pSrc[i]*pSrc[i+j]
+    umulh   dst_carry, mul_word, scratch0   // Bits <127:64> of pSrc[i]*pSrc[i+j]
+
+    adds    scratch1, scratch1, scratch2    // Add the word from the destination
+    adc     dst_carry, dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    adds    scratch1, scratch1, src_carry   // Adding the previous word
+    adc     dst_carry, dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    str     scratch1, [dst_reg, #8*index]   // Store to destination
+
+MACRO_END()
+
+    // Macro for the third pass loop of RawSquareAsm.
+    // It takes one mulword from the source, squares it, and
+    // adds it to the even columns of the destination. The carries are propagated
+    // to the odd columns.
+    //
+    // Here we can have a (1-bit) carry to the next call because the maximum value for
+    // a pair of columns is (2^64-1)^2+(2^128-1)+1 = 2^129 - 2^65 + 1 < 2^129 - 1
+MACRO_START(SQR_DIAGONAL_PROP, index, src_reg, dst_reg, squarelo, squarehi, scratch0, scratch1)
+
+    ldr     squarehi, [src_reg, #8*index]               // mulword
+    mul     squarelo, squarehi, squarehi                // Bits <63:0> of m^2
+    umulh   squarehi, squarehi, squarehi                // Bits <127:64> of m^2
+
+    ldp     scratch0, scratch1, [dst_reg, #16*index]    // Load
+
+    // Adding the square to the even column
+    adcs    squarelo, squarelo, scratch0                // carry from previous and update the flags
+
+    // Propagating the sum to the next column
+    adcs    squarehi, squarehi, scratch1                // This can generate a carry
+
+    stp     squarelo, squarehi, [dst_reg, #16*index]    // Store
+
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawSquareAsm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst )
+//
+// Register assignments
+//       X_0  = pSrc
+//       X_1  = word count of pSrc
+//       X_2  = pSrc (moving forward one digit / 4 words every inner loop)
+//       X_3  = digit count of pSrc
+//       X_4  = pDst (moving forward one digit every inner loop)
+//       X_5  = pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = "128-bit" sliding register to hold the result of multiplies
+//       X_13 = Stored pSrc
+//       X_14 = Digit count of pSrc
+//       X_15 = Stored digit count of pSrc
+//       X_16 = Stored pDst
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm), 3, 17)
+
+    mov     X_3, X_1                    // digit count into X_3
+
+    lsl     X_1, X_1, #2                // Calculate word count
+
+    mov     X_4, X_2                    // pDst
+    mov     X_5, X_2                    // store pDst
+    mov     X_16, X_2                   // store pDst
+    mov     X_13, X_0                   // store pSrc
+    mov     X_2, X_0                    // inner loop pSrc
+    mov     X_14, X_3                   // store nDigits for later
+    mov     X_15, X_3                   // store nDigits for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    mov     X_12, xzr                   // set X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+    str     xzr, [X_4]                  // store 0 for the first word
+
+    b       SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoopInit_Word0)
+    SQR_SINGLEADD_64 0, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoopInit_Word1)
+    SQR_SINGLEADD_64 1, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    SQR_SINGLEADD_64 2, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    SQR_SINGLEADD_64 3, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    sub     X_3, X_3, #1                // move one digit up
+    add     X_2, X_2, #32
+    add     X_4, X_4, #32
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+    str     X_12, [X_4]                 // Store the next word into the destination
+
+    sub     X_1, X_1, #2                // move two words up (we started at the word 1)
+
+    mov     X_8, #1                     // Cyclic counter
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdefRawSquareAsmOuterLoop)
+
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    mov     X_3, X_14                   // set nDigits
+    mov     X_2, X_0                    // set pSrc
+    mov     X_4, X_5                    // set pDst
+
+    mov     X_11, xzr                   // set X_11 = 0
+    mov     X_12, xzr                   // set X_12 = 0
+    ldr     X_6, [X_0, X_8, LSL #3]     // load the next word from pSrc
+
+    // Cyclic counter and jump logic
+    add     X_8, X_8, #1
+    cmp     X_8, #1
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word1
+    cmp     X_8, #2
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word2
+    cmp     X_8, #3
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word3
+
+    // The following instructions are only executed when X_8 == 4
+    mov     X_8, xzr                // Set it to 0
+
+    add     X_0, X_0, #32           // move start of pSrc 4 words up
+    add     X_5, X_5, #32           // move pDst 4 words up
+
+    mov     X_2, X_0                // set pSrc
+    mov     X_4, X_5                // set pDst
+
+    sub     X_14, X_14, #1          // remove 1 digit
+    mov     X_3, X_14               // set the new digit counter
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word0)
+    SQR_DOUBLEADD_64 0, X_2, X_4, X_6, X_12, X_11, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word1)
+    SQR_DOUBLEADD_64 1, X_2, X_4, X_6, X_11, X_12, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word2)
+    SQR_DOUBLEADD_64 2, X_2, X_4, X_6, X_12, X_11, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word3)
+    SQR_DOUBLEADD_64 3, X_2, X_4, X_6, X_11, X_12, X_7, X_9, X_10
+
+    sub     X_3, X_3, #1                // move one digit up
+    add     X_2, X_2, #32
+    add     X_4, X_4, #32
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+    str     X_12, [X_4]                 // Store the next word into the destination
+
+    sub     X_1, X_1, #1                // move one word up
+    cbnz    X_1, SymCryptFdefRawSquareAsmOuterLoop
+
+    str     xzr, [X_5, #40]             // Store 0 to destination for the top word
+
+    ////////////////////////////////////////////////////////////////
+    // Second Pass - Shifting all results 1 bit left
+    ////////////////////////////////////////////////////////////////
+
+    mov     X_3, X_15       // nDigits
+    lsl     X_3, X_3, #1    // Double digits
+    mov     X_4, X_16       // pDst pointer
+    ands    X_7, X_7, xzr   // Clear the flags
+
+LABEL(SymCryptFdefRawSquareAsmSecondPass)
+
+    sub     X_3, X_3, #1    // move one digit up
+
+    ldp     X_7, X_8, [X_4]
+    adcs    X_7, X_7, X_7   // Shift left and add the carry
+    adcs    X_8, X_8, X_8
+    stp     X_7, X_8, [X_4], #16
+
+    ldp     X_9, X_10, [X_4]
+    adcs    X_9, X_9, X_9   // Shift left and add the carry
+    adcs    X_10, X_10, X_10
+    stp     X_9, X_10, [X_4], #16
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmSecondPass
+
+    //////////////////////////////////////////////////////////////////////////////
+    // Third Pass - Adding the squares on the even columns and propagating the sum
+    //////////////////////////////////////////////////////////////////////////////
+
+    ands    X_7, X_7, xzr   // Clear the flags
+    mov     X_0, X_13       // src pointer
+    mov     X_4, X_16       // pDst pointer
+    mov     X_3, X_15       // nDigits
+
+LABEL(SymCryptFdefRawSquareAsmThirdPass)
+    SQR_DIAGONAL_PROP 0, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 1, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 2, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 3, X_0, X_4, X_6, X_7, X_8, X_9
+
+    sub     X_3, X_3, #1        // move one digit up
+    add     X_0, X_0, #32       // One digit up (not updated in SQR_DIAGONAL_PROP)
+    add     X_4, X_4, #64       // Two digits up (not updated in SQR_DIAGONAL_PROP)
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmThirdPass
+
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm), 4, 10)
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 + Src2 into (X_4, X_5, X_6, X_7)
+    ldp     X_4, X_5, [X_1]       // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2]       // Load two words of peSrc2
+    adds    X_4, X_4, X_8
+    adcs    X_5, X_5, X_9
+
+    ldp     X_6, X_7, [X_1, #16]  // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2, #16]  // Load two words of peSrc2
+    adcs    X_6, X_6, X_8
+    adcs    X_7, X_7, X_9
+
+    cset    X_1, cs                 // If the carry is set, set X_1 to 1 (else 0)
+
+    // Compute sum - Mod into (X_2, X_8, X_9, X_0) = sum - modulus
+
+    ldp     X_2, X_8, [X_0]       // Load two words of pMod
+    subs    X_2, X_4, X_2
+    sbcs    X_8, X_5, X_8
+
+    ldp     X_9, X_0, [X_0, #16]  // Load two words of pMod
+    sbcs    X_9, X_6, X_9
+    sbcs    X_0, X_7, X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_1
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_1, X_1, XZR
+
+    csel    X_4, X_4, X_2, cc
+    csel    X_5, X_5, X_8, cc
+    stp     X_4, X_5, [X_3]
+
+    csel    X_6, X_6, X_9, cc
+    csel    X_7, X_7, X_0, cc
+    stp     X_6, X_7, [X_3, #16]
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSub256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm), 4, 10)
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 - Src2 into (X_4, X_5, X_6, X_7)
+    ldp     X_4, X_5, [X_1]         // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2]         // Load two words of peSrc2
+    subs    X_4, X_4, X_8
+    sbcs    X_5, X_5, X_9
+
+    ldp     X_6, X_7, [X_1, #16]    // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2, #16]    // Load two words of peSrc2
+    sbcs    X_6, X_6, X_8
+    sbcs    X_7, X_7, X_9
+
+    // Load the masked modulus into (X_1, X_2, X_8, X_9)
+    ldp     X_1, X_2, [X_0]         // Load two words of pMod
+    csel    X_1, X_1, XZR, cc       // If we did not borrow, zero the modulus
+    csel    X_2, X_2, XZR, cc
+
+    ldp     X_8, X_9, [X_0, #16]    // Load two words of pMod
+    csel    X_8, X_8, XZR, cc
+    csel    X_9, X_9, XZR, cc
+
+    // Add the masked modulus
+    adds    X_4, X_4, X_1
+    adcs    X_5, X_5, X_2
+    stp     X_4, X_5, [X_3]
+
+    adcs    X_6, X_6, X_8
+    adc     X_7, X_7, X_9
+    stp     X_6, X_7, [X_3, #16]
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm))
+
+
+MACRO_START(MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE, T0, T1, Ai, pB, pM, K, Inv64, R0, R1, R2, R3, R4, R5)
+    // (R1, R2, R3, R4, R5) = Ai * (B0..3) + (R1, R2, R3, R4, R5)
+    // (xx, R1, R2, R3, R4, R5, R0) = K * (M0..3) + (R0, R1, R2, R3, R4, R5)
+    // K = Inv64 * R1
+    // Note K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // R5 is 0 or 1 at start
+    // R0 is 0 or 1 at end
+    // T0, T1 = scratch
+    // Ai is also used as scratch when it's no longer needed
+
+    // (R1, R2, R3, R4, R5) += Ai * B even
+    ldr     T0, [pB]
+    mul     T1, Ai, T0
+    adds    R1, R1, T1
+    umulh   T0, Ai, T0
+    adcs    R2, R2, T0
+
+    ldr     T0, [pB, #16]
+    mul     T1, Ai, T0
+    adcs    R3, R3, T1
+    umulh   T0, Ai, T0
+    adcs    R4, R4, T0
+
+    adc     R5, R5, XZR // R5 in range [0,2]
+
+    // (R2, R3, R4, R5) += Ai * B odd
+    ldr     T0, [pB, #8]
+    mul     T1, Ai, T0
+    adds    R2, R2, T1
+    umulh   T0, Ai, T0
+    adcs    R3, R3, T0
+
+    ldr     T0, [pB, #24]
+    mul     T1, Ai, T0
+    adcs    R4, R4, T1
+    umulh   Ai, Ai, T0
+    adc     Ai, Ai, XZR // Ai in range [0, 2^64-1]
+                        // defer adding Ai to R5 to avoid overflowing earlier than necessary
+
+    // (xx, R1, R2, R3, R4, R5) = K * M even + (R0, R1, R2, R3, R4, R5)
+    ldr     T0, [pM]
+    mul     T1,  K, T0
+    adds    R0, R0, T1  // Sets R0 to 0
+    umulh   T0,  K, T0
+    adcs    R1, R1, T0
+
+    ldr     T0, [pM, #16]
+    mul     T1,  K, T0
+    adcs    R2, R2, T1
+    umulh   T0,  K, T0
+    adcs    R3, R3, T0
+
+    adcs    R4, R4, XZR
+    adc     R5, R5, XZR // R5 in range [0,3]
+
+    // (R1, R2, R3, R4, R5, R0) = K * M odd + (R1, R2, R3, R4, R5, R0)
+    ldr     T0, [pM, #8]
+    mul     T1,  K, T0
+    adds    R1, R1, T1
+    umulh   T0,  K, T0
+    adcs    R2, R2, T0
+
+    ldr     T0, [pM, #24]
+    mul     T1,  K, T0
+    adcs    R3, R3, T1
+    umulh   T0,  K, T0
+    adcs    R4, R4, T0
+
+    mul     K, R1, Inv64
+    adcs    R5, R5, Ai
+    cset    R0, cs
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm), 4, 15)
+
+    // X_0 = pMod
+    // X_1 = pSrc1
+    // X_2 = pSrc2
+    // X_3 = pDst
+    // X_4 = Ai
+    // X_5, X_6 = T0, T1
+    // X_7 = Inv64
+    // X_8 = K
+    // X_9, X_10, X_11, X_12, X_13, X_14 = R0..R5
+
+ALTERNATE_ENTRY(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal))
+
+    ldr     X_4, [X_1]              // Load pSrc1
+
+    ldp     X_10, X_11, [X_2]       // Load pSrc2 B[0], B[1]
+    mul      X_9,  X_4, X_10        // R0 = <63:0>   of A[0]*B[0]
+    umulh   X_10,  X_4, X_10        // R1 = <127:64> of A[0]*B[0]
+
+    mul      X_5,  X_4, X_11        // T0 = <63:0>   of A[0]*B[1]
+    adds    X_10, X_10,  X_5        // Adding the previous word
+    umulh   X_11,  X_4, X_11        // R2 = <127:64> of A[0]*B[1]
+
+    ldp     X_12, X_13, [X_2, #16]  // Load pSrc2 B[2], B[3]
+    mul      X_5,  X_4, X_12        // T0 = <63:0>   of A[0]*B[2]
+    adcs    X_11, X_11,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12,  X_4, X_12        // R3 = <127:64> of A[0]*B[2]
+
+    mul      X_5,  X_4, X_13        // T0 = <63:0>   of A[0]*B[3]
+    adcs    X_12, X_12,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_13,  X_4, X_13        // R4 = <127:64> of A[0]*B[3]
+    adc     X_13, X_13,  XZR
+
+    eor     X_14, X_14, X_14
+
+    ldr     X_7, [X_0, #SymCryptModulusInv64OffsetArm64]
+    mul     X_8, X_9, X_7           // K = R0 * Inv64
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64
+
+    ldr     X_4, [X_1, #8]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_9, X_10, X_11, X_12, X_13, X_14
+
+    ldr     X_4, [X_1, #16]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_10, X_11, X_12, X_13, X_14, X_9
+
+    ldr     X_4, [X_1, #24]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_11, X_12, X_13, X_14, X_9, X_10
+
+    // (xx, R1, R2, R3, R4, R5) = K * M even + (R0, R1, R2, R3, R4, R5)
+
+    ldp      X_4,  X_5, [X_0]   // Load Mod
+    mul      X_1,  X_8, X_4
+    adds    X_12, X_12, X_1     // Sets R0 to 0
+    umulh    X_2,  X_8, X_4
+    adcs    X_13, X_13, X_2
+
+    ldp      X_6,  X_7, [X_0, #16]
+    mul      X_1,  X_8, X_6
+    adcs    X_14, X_14, X_1
+    umulh    X_2,  X_8, X_6
+    adcs     X_9,  X_9, X_2
+
+    adcs    X_10, X_10, XZR
+    adc     X_11, X_11, XZR
+
+    // (R1, R2, R3, R4, R5) = K * M odd + (R1, R2, R3, R4, R5)
+    mul      X_1,  X_8, X_5
+    adds    X_13, X_13, X_1
+    umulh    X_2,  X_8, X_5
+    adcs    X_14, X_14, X_2
+
+    mul      X_1,  X_8, X_7
+    adcs     X_9,  X_9, X_1
+    umulh    X_2,  X_8, X_7
+    adcs    X_10, X_10, X_2
+
+    adc     X_11, X_11, XZR
+
+    // Montgomery-reduced multiplication value in (X_13, X_14, X_9, X_10, X_11), and it is less than 2*Modulus
+    // Compute value - Mod into (X_4, X_5, X_6, X_7), Cy = inverted subtraction borrow
+
+    subs    X_4, X_13, X_4
+    sbcs    X_5, X_14, X_5
+    sbcs    X_6,  X_9, X_6
+    sbcs    X_7, X_10, X_7
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_11
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_11, X_11, XZR
+
+    csel    X_13, X_13, X_4, cc
+    csel    X_14, X_14, X_5, cc
+    stp     X_13, X_14, [X_3]
+
+    csel     X_9,  X_9, X_6, cc
+    csel    X_10, X_10, X_7, cc
+    stp      X_9, X_10, [X_3, #16]
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomery256Asm the same
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm), 4, 15)
+
+    // X_0 = pMod
+    // X_1 = pSrc
+    // X_2 = pDst
+
+    mov     X_3, X_2
+    mov     X_2, X_1
+
+    // Just jump into multiply, similar to SymCryptWipeAsm jumping into memset.
+
+    b       PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal)
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm), 4, 14)
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 + Src2 into (X_4, X_5, X_6, X_7, X_8, X_9)
+    ldp     X_4,  X_5,  [X_1]       // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2]       // Load two words of peSrc2
+    adds    X_4,  X_4,  X_10
+    adcs    X_5,  X_5,  X_11
+
+    ldp     X_6,  X_7,  [X_1, #16]  // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #16]  // Load two words of peSrc2
+    adcs    X_6,  X_6,  X_10
+    adcs    X_7,  X_7,  X_11
+
+    ldp     X_8,  X_9,  [X_1, #32]  // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #32]  // Load two words of peSrc2
+    adcs    X_8,  X_8,  X_10
+    adcs    X_9,  X_9,  X_11
+
+    cset    X_1, cs                 // If the carry is set, set X_1 to 1 (else 0)
+
+    // Compute sum - Mod into (X_2, X_10, X_11, X_12, X_13, X_0) = sum - modulus
+
+    ldp     X_2, X_10,  [X_0]       // Load two words of pMod
+    subs    X_2,  X_4,  X_2
+    sbcs    X_10, X_5,  X_10
+
+    ldp     X_11, X_12, [X_0, #16]  // Load two words of pMod
+    sbcs    X_11, X_6,  X_11
+    sbcs    X_12, X_7,  X_12
+
+    ldp     X_13, X_0,  [X_0, #32]  // Load two words of pMod
+    sbcs    X_13, X_8,  X_13
+    sbcs    X_0,  X_9,  X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_1
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_1, X_1, XZR
+
+    csel    X_4, X_4, X_2,  cc
+    csel    X_5, X_5, X_10, cc
+    stp     X_4, X_5, [X_3]
+
+    csel    X_6, X_6, X_11, cc
+    csel    X_7, X_7, X_12, cc
+    stp     X_6, X_7, [X_3, #16]
+
+    csel    X_8, X_8, X_13, cc
+    csel    X_9, X_9, X_0,  cc
+    stp     X_8, X_9, [X_3, #32]
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSub384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm), 4, 13)
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 - Src2 into (X_4, X_5, X_6, X_7, X_8, X_9)
+    ldp     X_4,  X_5,  [X_1]         // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2]         // Load two words of peSrc2
+    subs    X_4,  X_4,  X_10
+    sbcs    X_5,  X_5,  X_11
+
+    ldp     X_6,  X_7,  [X_1, #16]    // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #16]    // Load two words of peSrc2
+    sbcs    X_6,  X_6,  X_10
+    sbcs    X_7,  X_7,  X_11
+
+    ldp     X_8,  X_9,  [X_1, #32]    // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #32]    // Load two words of peSrc2
+    sbcs    X_8,  X_8,  X_10
+    sbcs    X_9,  X_9,  X_11
+
+    // Load the masked modulus into (X_1, X_2, X_10, X_11, X_12, X_0)
+    ldp     X_1, X_2, [X_0]         // Load two words of pMod
+    csel    X_1, X_1, XZR, cc       // If we did not borrow, zero the modulus
+    csel    X_2, X_2, XZR, cc
+
+    ldp     X_10, X_11, [X_0, #16]  // Load two words of pMod
+    csel    X_10, X_10, XZR, cc
+    csel    X_11, X_11, XZR, cc
+
+    ldp     X_12, X_0,  [X_0, #32]  // Load two words of pMod
+    csel    X_12, X_12, XZR, cc
+    csel    X_0,  X_0,  XZR, cc
+
+    // Add the masked modulus
+    adds    X_4, X_4, X_1
+    adcs    X_5, X_5, X_2
+    stp     X_4, X_5, [X_3]
+
+    adcs    X_6, X_6, X_10
+    adcs    X_7, X_7, X_11
+    stp     X_6, X_7, [X_3, #16]
+
+    adcs    X_8, X_8, X_12
+    adc     X_9, X_9, X_0
+    stp     X_8, X_9, [X_3, #32]
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm))
+
+
+MACRO_START(MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE, T0, T1, Ai, pB, K, N3, R0, R1, R2, R3, R4, R5, R6, R7)
+    // (R1, R2, R3, R4, R5, R6, R7) = Ai * (B0..5) + (R1, R2, R3, R4, R5, R6, R7)
+    // (xx, R1, R2, R3, R4, R5, R6, R7, R0) = K * (M0..5) + (R0, R1, R2, R3, R4, R5, R6, R7)
+    // Note K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // R7 is 0 or 1 at start
+    // R0 is 0 or 1 at end
+    // N3 in [0, 2] at start and end
+    // T0, T1 = scratch
+    // Ai is also used as scratch when it's no longer needed
+
+    // (R1, R2, R3, R4, R5, R6, R7) += Ai * B even
+    ldr     T0, [pB]
+    mul     T1, Ai, T0
+    adds    R1, R1, T1
+    umulh   T0, Ai, T0
+    adcs    R2, R2, T0
+
+    ldr     T0, [pB, #16]
+    mul     T1, Ai, T0
+    adcs    R3, R3, T1
+    umulh   T0, Ai, T0
+    adcs    R4, R4, T0
+
+    ldr     T0, [pB, #32]
+    mul     T1, Ai, T0
+    adcs    R5, R5, T1
+    umulh   T0, Ai, T0
+    adcs    R6, R6, T0
+
+    adc     R7, R7, XZR // R7 in range [0,2]
+
+    // (R2, R3, R4, R5, R6, R7) += Ai * B odd
+    ldr     T0, [pB, #8]
+    mul     T1, Ai, T0
+    adds    R2, R2, T1
+    umulh   T0, Ai, T0
+    adcs    R3, R3, T0
+
+    ldr     T0, [pB, #24]
+    mul     T1, Ai, T0
+    adcs    R4, R4, T1
+    umulh   T0, Ai, T0
+    adcs    R5, R5, T0
+
+    ldr     T0, [pB, #40]
+    mul     T1, Ai, T0
+    adcs    R6, R6, T1
+    umulh   Ai, Ai, T0
+    adc     Ai, Ai, XZR // Ai in range [0, 2^64-1]
+                        // defer adding Ai to R7 to avoid overflowing earlier than necessary
+
+    // P384 has a modulus with a form which makes it possible to perform Montgomery reduction
+    // in an interesting way. The modulus is:
+    // 2^384 - 2^128 - 2^96 + 2^32 - 1
+    // This means when we want to add a multiple of the Modulus in reduction, we can actually
+    // avoid multiplication instructions entirely.
+    // On Arm64, multiplication instruction throughput is often the bottleneck, and there is no
+    // distinction in cost between addition and subtraction (unlike on AMD64).
+    // So we do the reduction directly using shifts, adds, and subtracts.
+    // On entry to this macro we have N3 representing a deferred subtraction (in range [0,2]) from the
+    // 3rd word of the last macro (now aligned to the 2nd word of this macro), on exit we set N3 to be
+    // the subtraction to defer to the next round.
+    //
+    //            ____ ____ ____ ____ ____ ____
+    //           | M5 | M4 | M3 | M2 | M1 | M0 |
+    //           | ff | ff | ff | fe | f0 | 0f | (P384 modulus if you sign extend each nibble to 32b)
+    //           |____|____|____|____|____|____|
+    //  ____ ____ ____ ____ ____ ____ ____ ____
+    // |    |    |\  /|\  /|\  /|    |    |    |
+    // | R7 | R6 | R5 | R4 | R3 | R2 | R1 | R0 |   R0h = R0 >> 32
+    // |____|____|/__\|/__\|/__\|____|____|____|   R0l = R0 mod 2^32
+    //                                      | x Inv64 (0x0000000100000001)
+    //                                     _v__
+    //                      ____          |    |
+    //                     |    |         |  K | K = (R0 + (R0 << 32)) mod 2^64
+    //                     | N3 |         |____|   = ((R0h +32 R0l) << 32) + R0l
+    //       ____          |____|____                      ^^^ by +32 we mean addition modulo 2^32
+    //      |    |              |    |
+    //    + |  K |            - |  K |
+    //      |____|              |____|____
+    //                          |  K |  K |
+    //                        - |>>32|<<32|   K << 32 mod 2^64 = R0l << 32
+    //                          |____|____|   K >> 32 mod 2^64 = R0h +32 R0l
+    //                               |    |
+    //                             + |  J |   J = (K*M0 + R0) >> 64
+    //                           ____|____|
+    //                          |    |
+    //                        - |N3in|
+    //                          |____|
+    //
+    // The logic for how we compute J is a little involved:
+    // (K * M0) + R0 = (((R0h +32 R0l) << 32) + R0l) * (1<<32 - 1)                        + R0
+    //               = (((R0h +32 R0l) << 64) + R0l<<32) - (((R0h +32 R0l) << 32) + R0l)  + R0h<<32 + R0l
+    //               =  ((R0h +32 R0l) << 64) + R0l<<32  -  ((R0h +32 R0l) << 32)         + R0h<<32
+    //               =  ((R0h +32 R0l) << 64) + (((R0h + R0l) - (R0h +32 R0l)) << 32)
+    //                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is exactly bit 33 of R0h+R0l
+    //               =  ((R0h +32 R0l) + ((R0h +33 R0l)>>32)) << 64
+    // => J = (R0h +32 R0l) + ((R0h +33 R0l)>>32)
+
+    lsl     T0, R0, #32     // T0 = R0<<32 = K<<32
+    adds     K, R0, T0      //  K = R0 + T0 = R0 + (R0<<32) = R0 * Inv64
+    lsr     T1, K, #32      // T1 = K>>32
+    adcs    R1, R1, T1      // R1 = R1 + J = R1 + T1 + carry from setting K
+    add     T1, T1, N3      // T1 += N3in; cannot overflow as T1 < 2^32
+    adcs    R2, R2, XZR
+    csetm   N3, cs          // N3 -1 if carry, else 0
+
+    subs    R1, R1, T0      // subtract [ K>>32 + N3in || K<<32 ]
+    sbcs    R2, R2, T1
+    cinc    N3, N3, cc      // N3 += 1 iff borrow; N3 in [0, 1] as carry in addition => borrow here
+
+    subs    R2, R2, K       // subtract K
+    cinc    N3, N3, cc      // N3 += 1 iff borrow; N3 in [0, 2]
+
+    adds    R6, R6, K       // add K
+    adcs    R7, R7, Ai
+    cset    R0, cs
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm), 4, 17)
+
+    // X_0 = pMod
+    // X_1 = pSrc1
+    // X_2 = pSrc2
+    // X_3 = pDst
+    // X_4 = Ai
+    // X_5, X_6, X_7 = T0, T1, N3
+    // X_8 = K
+    // X_9, X_10, X_11, X_12, X_13, X_14, X_15, X_16 = R0..R7
+
+ALTERNATE_ENTRY(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal))
+
+    ldr     X_4, [X_1]              // Load pSrc1
+
+    ldp     X_10, X_11, [X_2]       // Load pSrc2 B[0], B[1]
+    mul      X_9,  X_4, X_10        // R0 = <63:0>   of A[0]*B[0]
+    umulh   X_10,  X_4, X_10        // R1 = <127:64> of A[0]*B[0]
+
+    mul      X_5,  X_4, X_11        // T0 = <63:0>   of A[0]*B[1]
+    adds    X_10, X_10,  X_5        // Adding the previous word
+    umulh   X_11,  X_4, X_11        // R2 = <127:64> of A[0]*B[1]
+
+    ldp     X_12, X_13, [X_2, #16]  // Load pSrc2 B[2], B[3]
+    mul      X_5,  X_4, X_12        // T0 = <63:0>   of A[0]*B[2]
+    adcs    X_11, X_11,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12,  X_4, X_12        // R3 = <127:64> of A[0]*B[2]
+
+    mul      X_5,  X_4, X_13        // T0 = <63:0>   of A[0]*B[3]
+    adcs    X_12, X_12,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_13,  X_4, X_13        // R4 = <127:64> of A[0]*B[3]
+
+    ldp     X_14, X_15, [X_2, #32]  // Load pSrc2 B[4], B[5]
+    mul      X_5,  X_4, X_14        // T0 = <63:0>   of A[0]*B[4]
+    adcs    X_13, X_13,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_14,  X_4, X_14        // R3 = <127:64> of A[0]*B[4]
+
+    mul      X_5,  X_4, X_15        // T0 = <63:0>   of A[0]*B[5]
+    adcs    X_14, X_14,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_15,  X_4, X_15        // R4 = <127:64> of A[0]*B[5]
+    adc     X_15, X_15,  XZR
+
+    eor     X_16, X_16, X_16
+    eor     X_7, X_7, X_7
+
+    ldr     X_4, [X_1, #8]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_9, X_10, X_11, X_12, X_13, X_14, X_15, X_16
+
+    ldr     X_4, [X_1, #16]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_10, X_11, X_12, X_13, X_14, X_15, X_16, X_9
+
+    ldr     X_4, [X_1, #24]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_11, X_12, X_13, X_14, X_15, X_16, X_9, X_10
+
+    ldr     X_4, [X_1, #32]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_12, X_13, X_14, X_15, X_16, X_9, X_10, X_11
+
+    ldr     X_4, [X_1, #40]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_13, X_14, X_15, X_16, X_9, X_10, X_11, X_12
+
+    // see MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE for how this reduction works
+    lsl     X_5, X_14, #32  // T0 = R0<<32 = K<<32
+    adds    X_8, X_14, X_5  //  K = R0 + T0 = R0 + (R0<<32)
+    lsr     X_6, X_8, #32   // T1 = K>>32
+    adcs    X_15, X_15, X_6 // R1 = R1 + J = R1 + T1 + carry from setting K
+    add     X_6, X_6, X_7   // T1 += N3in; cannot overflow as T1 < 2^32
+    adcs    X_16, X_16, XZR
+    csetm   X_7, cs         // N3 -1 if carry, else 0
+
+    subs    X_15, X_15, X_5 // subtract K<<32
+    sbcs    X_16, X_16, X_6
+    cinc    X_7, X_7, cc    // N3 += 1 iff borrow; N3 in [0, 1] as carry in addition => borrow here
+
+    subs    X_16, X_16, X_8 // subtract K
+    sbcs    X_9, X_9, X_7   // subtract N3 and borrow if any
+    sbcs    X_10, X_10, XZR
+    sbcs    X_11, X_11, XZR
+    sbcs    X_12, X_12, XZR
+    sbc     X_13, X_13, XZR
+
+    adds    X_12, X_12, X_8 // add K
+    adc     X_13, X_13, XZR
+
+    // Montgomery-reduced multiplication value in (X_15, X_16, X_9, X_10, X_11, X_12, X_13), and it is less than 2*Modulus
+    // Compute value - Mod into (X_4, X_5, X_6, X_7, X_14, X_0), Cy = inverted subtraction borrow
+
+    // "Load" Mod into X4, X5, X6, X0
+    mov     X_4, 0x00000000ffffffff
+    mov     X_5, 0xffffffff00000000
+    mov     X_6, 0xfffffffffffffffe
+    mov     X_0, 0xffffffffffffffff
+
+    subs     X_4, X_15,  X_4
+    sbcs     X_5, X_16,  X_5
+    sbcs     X_6,  X_9,  X_6
+    sbcs     X_7, X_10,  X_0
+    sbcs    X_14, X_11,  X_0
+    sbcs     X_0, X_12,  X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_11
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_13, X_13, XZR
+
+    csel    X_15, X_15,  X_4, cc
+    csel    X_16, X_16,  X_5, cc
+    stp     X_15, X_16, [X_3]
+
+    csel     X_9,  X_9,  X_6, cc
+    csel    X_10, X_10,  X_7, cc
+    stp      X_9, X_10, [X_3, #16]
+
+    csel    X_11, X_11, X_14, cc
+    csel    X_12, X_12,  X_0, cc
+    stp     X_11, X_12, [X_3, #32]
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryP384Asm the same
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm), 4, 17)
+
+    // X_0 = pMod
+    // X_1 = pSrc
+    // X_2 = pDst
+
+    mov     X_3, X_2
+    mov     X_2, X_1
+
+    // Just jump into multiply, similar to SymCryptWipeAsm jumping into memset.
+
+    b       PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal)
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Register assignments
+//       X_0  = pMod (moving forward one *digit* every inner loop)
+//       X_1  = pSrc (moving forward one *digit* every inner loop)
+//       X_2  = pDst (used only in the end for subtract / result)
+//       X_3  = byte count of pSrc and pMod (sometimes byte count - 1 digit of bytes)
+//       X_4  = word count of pSrc
+//       X_5  = Inv64 of the modulus
+//       X_6  = m = pSrc[i]*Inv64
+//       X_7  = hc = high carry variable
+//       X_8-X_19 = temporaries
+//       X_20 = c
+//       X_21 = Stored byte count of pSrc
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm), 3, 22)
+
+    ldr     W_4, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64      // pMod
+
+    lsl     X_3, X_4, #5                // Multiply by 32 to get the number of bytes
+    lsl     X_4, X_4, #2                // Multiply by 4 to get the number of words
+
+    sub     X_3, X_3, #32               // offset the byte count to make it easy to restore pointers
+
+    mov     X_7, xzr                    // Set hc to 0
+    mov     X_21, X_3                   // Store the byte count for later
+
+    //
+    // Main loop
+    //
+LABEL(SymCryptFdefMontgomeryReduceAsmOuter)
+    ldp     X_16, X_17, [X_1]           // pSrc[i+0], pSrc[i+1]
+    mul     X_6, X_16, X_5              // <63:0> bits of pSrc[i]*Inv64 = m
+
+    ldp     X_8,  X_9,  [X_0]           // pMod[0], pMod[1]
+    umulh   X_8,  X_6, X_8              // Bits <127:64> of m*pMod[0]
+    subs    xzr,  X_16, #1              // Subtract 1 from pSrc[i+0] (generate a carry iff pSrc[i] is non-zero)
+
+    mul     X_13, X_6, X_9              // Bits <63:0> of m*pMod[1]
+    umulh   X_9,  X_6, X_9              // Bits <127:64> of m*pMod[1]
+    adcs    X_8,  X_8,  X_13            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_10, X_11, [X_0, #16]      // pMod[2], pMod[3]
+    mul     X_14, X_6, X_10             // Bits <63:0> of m*pMod[2]
+    umulh   X_10, X_6, X_10             // Bits <127:64> of m*pMod[2]
+    adcs    X_9,  X_9,  X_14            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_18, X_19, [X_1, #16]      // pSrc[i+2], pSrc[i+3]
+    mul     X_15, X_6, X_11             // Bits <63:0> of m*pMod[3]
+    umulh   X_11, X_6, X_11             // Bits <127:64> of m*pMod[3]
+    adcs    X_10, X_10, X_15            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    adc     X_20, X_11, xzr             // Add the carry if any
+
+    // ( X_20, X_10, X_9, X_8, ___ ) = (( pMod[3], pMod[2], pMod[1], pMod[0] ) * m)
+    // We do not compute this --^ because (m*pMod[0] + pSrc[i]) == 0. We already accounted for the carry above.
+
+    adds    X_17, X_17, X_8             // Adding pSrc[i+1]
+    adcs    X_18, X_18, X_9             // Adding pSrc[i+2]
+    adcs    X_19, X_19, X_10            // Adding pSrc[i+3]
+
+    stp     xzr,  X_17, [X_1]           // pSrc[i+0], pSrc[i+1]
+    stp     X_18, X_19, [X_1, #16]      // pSrc[i+2], pSrc[i+3]
+
+    cbz    X_3, SymCryptFdefMontgomeryReduceAsmInnerEnd
+
+LABEL(SymCryptFdefMontgomeryReduceAsmInner)
+    // Invariant - X_20 (c) + carry flag is a value in the range [0, 2^64-1]
+    // We could always adc X_20, X_20, xzr before loop entry, but this is a wasted instruction
+    // vs. adcs with X_20 below
+
+    ldp     X_8,  X_9,  [X_0, #32]!     // pMod[j+0], pMod[j+1]
+    mul     X_12, X_6, X_8              // Bits <63:0> of m*pMod[j]
+    umulh   X_8,  X_6, X_8              // Bits <127:64> of m*pMod[j]
+    adcs    X_12, X_20, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    mul     X_13, X_6, X_9              // Bits <63:0> of m*pMod[j+1]
+    umulh   X_9,  X_6, X_9              // Bits <127:64> of m*pMod[j+1]
+    ldp     X_16, X_17, [X_1, #32]!     // pSrc[i+j+0], pSrc[i+j+1]
+    adcs    X_8,  X_8,  X_13            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_10, X_11, [X_0, #16]      // pMod[j+2], pMod[j+3]
+    mul     X_14, X_6, X_10             // Bits <63:0> of m*pMod[j+2]
+    umulh   X_10, X_6, X_10             // Bits <127:64> of m*pMod[j+2]
+    adcs    X_9,  X_9,  X_14            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_18, X_19, [X_1, #16]      // pSrc[i+j+2], pSrc[i+j+3]
+    mul     X_15, X_6, X_11             // Bits <63:0> of m*pMod[j+3]
+    umulh   X_11, X_6, X_11             // Bits <127:64> of m*pMod[j+3]
+    adcs    X_10, X_10, X_15            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    adc     X_20, X_11, xzr             // Add the carry if any
+
+    // ( X_20, X_10, X_9, X_8, X_12 ) = (( pMod[j+3], pMod[j+2], pMod[j+1], pMod[j+0] ) * m) + c
+
+    adds    X_16, X_16, X_12            // Adding pSrc[i+j+0]
+    adcs    X_17, X_17, X_8             // Adding pSrc[i+j+1]
+    adcs    X_18, X_18, X_9             // Adding pSrc[i+j+2]
+    adcs    X_19, X_19, X_10            // Adding pSrc[i+j+3]
+
+    stp     X_16, X_17, [X_1]           // pSrc[i+j+0], pSrc[i+j+1]
+    stp     X_18, X_19, [X_1, #16]      // pSrc[i+j+2], pSrc[i+j+3]
+
+    sub     X_3, X_3, #32               // Move one digit up
+    cbnz    X_3, SymCryptFdefMontgomeryReduceAsmInner
+
+LABEL(SymCryptFdefMontgomeryReduceAsmInnerEnd)
+
+    ldr     X_8, [X_1, #32]             // pSrc[nWords]
+    adcs    X_20, X_20, X_7             // c + hc
+    adc     X_7, xzr, xzr               // Add the carry if any
+
+    adds    X_20, X_20, X_8             // c + hc + pSrc[nWords]
+    adc     X_7, X_7, xzr               // Add the carry if any and store into hc
+
+    str     X_20, [X_1, #32]            // pSrc[nWords] = c
+
+    subs    X_4, X_4, #1                // Move one word up
+
+    sub     X_0, X_0, X_21              // Restore pMod pointer (subtract byte count)
+    sub     X_1, X_1, X_21              // Restore pSrc pointer (subtract byte count)
+    add     X_1, X_1, #8                // Move pSrc pointer one word up
+
+    mov     X_3, X_21                   // Restore the byte counter
+
+    bne     SymCryptFdefMontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+    add     X_3, X_3, #32           // restore the full byte count for loops using unconditional pre-indexing
+    mov     X_21, X_3               // Store the byte count for later
+
+    sub     X_0, X_0, #32           // offset pMod so we can use pre-increment form of loads
+    sub     X_1, X_1, #32           // offset pSrc so we can use pre-increment form of loads
+    sub     X_2, X_2, #32           // offset pDst so we can use pre-increment form of loads
+    mov     X_14, X_2               // Store pDst pointer
+
+    // Prepare the pointers for subtract
+    mov     X_10, X_7               // X_10 = hc
+    subs    X_4, X_4, X_4           // Set the carry flag (i.e. no borrow)
+
+LABEL(SymCryptFdefMontgomeryReduceRawSubAsmLoop)
+    sub     X_3, X_3, #32           // Decrement the byte count by 32
+    // borrow is in the carry flag (flipped)
+
+    ldp     X_4, X_6, [X_1, #32]!   // Load two words of pSrc
+    ldp     X_5, X_7, [X_0, #32]!   // Load two words of pMod
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_1, #16]    // Load two words of pSrc
+    ldp     X_5, X_7, [X_0, #16]    // Load two words of pMod
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefMontgomeryReduceRawSubAsmLoop
+
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    orr     X_11, X_10, X_0         // X_11 = hc|d
+
+    // Prepare the pointers for masked copy
+    sub     X_0, X_1, X_21          // Restore pSrc pointer (subtract byte count)
+    mov     X_1, X_14               // pDst
+
+    mov     X_2, X_21               // Restore the byte counter
+    subs    X_4, X_10, X_11         // If (X_11 > X_10) clear the carry flag (i.e. borrow)
+
+LABEL(SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop)
+    sub     X_2, X_2, #32           // decrement the byte count by 32
+
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of the source
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1]         // Store the two words in the destination
+
+    ldp     X_4, X_6, [X_0, #16]
+    ldp     X_5, X_7, [X_1, #16]
+    csel    X_4, X_4, X_5, cc
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1, #16]
+
+    cbnz    X_2, SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm))
+
+MACRO_START(MULADD_LOADSTORE18, pS, pM, pD, K, Tc, T0, T1, T2, T3, T4, T5)
+    // Tc:D[3:0] = (M[3:0] * K) + S[3:0] + Tc
+    //
+    // QH = K
+    // T0..T5 = scratch
+    // Tc is carry in at start and carry out at end (in [0,2^64-1])
+
+    // Tc:T[5:2] = S[3:0] + (K * M odd) + Tc
+    ldp     T2, T3, [pS]        // load S[0], S[1]
+    adds    T2, T2, Tc
+
+    ldr     T0, [pM, #8]
+    mul     T1,  K, T0
+    adcs    T3, T3, T1
+    umulh   T0,  K, T0
+    ldp     T4, T5, [pS, #16]   // load S[2], S[3]
+    adcs    T4, T4, T0
+
+    ldr     T0, [pM, #24]
+    mul     T1,  K, T0
+    adcs    T5, T5, T1
+    umulh   Tc,  K, T0
+    adc     Tc, Tc, XZR
+
+    // Tc:D[3:0] = Tc:T[5:2] + (K * M even)
+    ldr     T0, [pM]
+    mul     T1,  K, T0
+    adds    T2, T2, T1
+    umulh   T0,  K, T0
+    adcs    T3, T3, T0
+    stp     T2, T3, [pD]        // store D[0], D[1]
+
+    ldr     T0, [pM, #16]
+    mul     T1,  K, T0
+    adcs    T4, T4, T1
+    umulh   T0,  K, T0
+    adcs    T5, T5, T0
+    stp     T4, T5, [pD, #16]   // store D[2], D[3]
+
+    adc     Tc, Tc, XZR
+MACRO_END()
+
+MACRO_START(SHIFTRIGHT2, pD, index, shrVal, shrMask, shlVal, Tcin, Tcout, T0, T1)
+    // T0:Tcout = D[index + 1:index]
+    // D[index + 1] = Tcin:T0 >> shrVal (== ((T0>>shrVal)&shrMask) | (Tcin<<shlVal))
+    // D[index] = T0:Tcout >> shrVal
+    //
+    // shrVal in [1,64]
+    // shrMask = (shrVal == 64) ? 0 : -1
+    // shlVal = 64-shrVal
+    //
+    // T0, T1 = scratch
+    // Value of Tcin is bits to shift in the top at start
+    // Value of Tcout is bits to shift into next macro invocation at end
+
+    ldp     Tcout, T0, [pD, #(index*8)]
+
+    lsr     T1, T0, shrVal
+    lsl     Tcin, Tcin, shlVal
+    and     T1, T1, shrMask
+    orr     T1, T1, Tcin
+
+    lsr     Tcin, Tcout, shrVal
+    lsl     T0, T0, shlVal
+    and     Tcin, Tcin, shrMask
+    orr     Tcin, Tcin, T0
+
+    stp     Tcin, T1, [pD, #(index*8)]
+MACRO_END()
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModDivSmallPow2Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _In_range_(1, 64)               UINT32                  exp,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm), 4, 14)
+    ldr     X_7, [X_1]
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+
+    mul     X_7, X_7, X_5   // <63:0> bits of pSrc[0]*Inv64
+                            // this is the multiple of the modulus that must be added to peSrc
+                            // s.t. the least significant **64** bits are all zero
+
+    // Form bitmask to mask out bottom exp bits from multiplication result
+    mov     X_6, #-1
+    neg     X_5, X_2
+    lsr     X_6, X_6, X_5   // mask = -1 >> (-exp % 64)
+                            //      = 0xffffffffffffffff >> (64-exp)
+                            //      = (((UINT65)1)<<exp)-1
+
+    and     X_7, X_7, X_6   // Extract the least significant exp bits from X_7 to produce K
+                            // K is the minimal multiple of the modulus that when added to peSrc
+                            // sets the least significant **exp** bits to zero
+
+    ldr     W_4, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits, used directly as loop counter
+    add     X_5, X_0, #SymCryptModulusValueOffsetArm64      // pointer to modulus value
+
+    eor     X_6, X_6, X_6   // zero one word carry
+
+LABEL(SymCryptFdefModDivSmallPow2AsmMulAddLoop)
+    // Set peDst = peSrc + (K * mod)
+    // Also has up to 64b of carry out the top which is stored in Q7 at the end of the loop
+    //
+    // W_4  loop ctr (one iteration per digit)
+    // X_7  K
+    // X_1  running pointer peSrc inner loop (we directly modify X_1 as we only need to read peSrc once)
+    // X_5  running pointer pMod inner loop
+    // X_3  running pointer peDst inner loop (we directly modify X_3 as we read/write peDst in reverse in second loop)
+    // X_6  one word carry
+    // X_8..X_13 scratch
+
+    MULADD_LOADSTORE18 X_1, X_5, X_3, X_7, X_6, X_8, X_9, X10, X_11, X_12, X_13
+
+    // Move on to the next digit
+    add     X_1, X_1, #32
+    add     X_5, X_5, #32
+    add     X_3, X_3, #32
+
+    sub     W_4, W_4, #1
+    cbnz    W_4, SymCryptFdefModDivSmallPow2AsmMulAddLoop
+
+    ldr     W_0, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits, used directly as loop counter
+    mov     X_1, #64
+    subs    X_1, X_1, X_2   // shlVal = 64-exp
+    csetm   X_4, ne         // shrMask = ((exp != 64) ? -1 : 0) = ((exp == 64) ? 0 : -1)
+
+    sub     X_3, X_3, #32  // X_3 points to the most significant digit of peDst
+
+LABEL(SymCryptFdefModDivSmallPow2AsmShiftRightLoop)
+    // Set peDst = peDst >> exp
+    // We work backwards (from MSB to LSB) so we avoid having to do special handling of the one word
+    // carry out of the top of the multiply add loop
+    //
+    // W_0  loop ctr (one iteration per digit)
+    // X_1  shlVal
+    // X_4  shrMask
+    // X_2  shrVal
+    // X_3  running pointer peDst inner loop (decreasing)
+    // X_6  one word carry even
+    // X_7  one word carry odd
+    // X_8, X_9 scratch
+
+    SHIFTRIGHT2 X_3, 2, X_2, X_4, X_1, X_6, X_7, X_8, X_9
+    SHIFTRIGHT2 X_3, 0, X_2, X_4, X_1, X_7, X_6, X_8, X_9
+
+    sub     X_3, X_3, #32
+
+    sub     W_0, W_0, #1
+    cbnz    W_0, SymCryptFdefModDivSmallPow2AsmShiftRightLoop
+
+FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm))
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64/wipe.S b/libs/symcrypt/lib/arm64/wipe.S
new file mode 100644
index 000000000000..a14c9db89ee8
--- /dev/null
+++ b/libs/symcrypt/lib/arm64/wipe.S
@@ -0,0 +1,48 @@
+//
+//  wipe.symcryptasm   Assembler code for wiping a buffer
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+    TEXTAREA()
+
+    EXTERN(PLATFORM_NAME_MANGLE(memset))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptWipe( _Out_writes_bytes_( cbData )    PVOID  pbData,
+//                                              SIZE_T cbData )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+PLATFORM_NAME_MANGLE(SymCryptWipeAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptWipeAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptWipeAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptWipeAsm)
+    .seh_endprologue
+
+// we just jump to memset.
+// this is enough to stop the compiler optimizing the memset away.
+
+    mov     X_2, X_1
+    mov     X_1, #0
+    b       PLATFORM_NAME_MANGLE(memset)
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64/wipe.symcryptasm b/libs/symcrypt/lib/arm64/wipe.symcryptasm
new file mode 100644
index 000000000000..34a2bc281b4e
--- /dev/null
+++ b/libs/symcrypt/lib/arm64/wipe.symcryptasm
@@ -0,0 +1,31 @@
+//
+//  wipe.symcryptasm   Assembler code for wiping a buffer
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+    TEXTAREA()
+
+    EXTERN(PLATFORM_NAME_MANGLE(memset))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptWipe( _Out_writes_bytes_( cbData )    PVOID  pbData,
+//                                              SIZE_T cbData )
+
+    FUNCTION_START(PLATFORM_NAME_MANGLE(SymCryptWipeAsm), 2, 3)
+
+// we just jump to memset.
+// this is enough to stop the compiler optimizing the memset away.
+
+    mov     X_2, X_1
+    mov     X_1, #0
+    b       PLATFORM_NAME_MANGLE(memset)
+
+    FUNCTION_END(PLATFORM_NAME_MANGLE(SymCryptWipeAsm))
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64ec/fdef369_asm.S b/libs/symcrypt/lib/arm64ec/fdef369_asm.S
new file mode 100644
index 000000000000..97b12bc44541
--- /dev/null
+++ b/libs/symcrypt/lib/arm64ec/fdef369_asm.S
@@ -0,0 +1,724 @@
+//
+//  fdef369_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// This file contains alternative routines that pretend that each digit is only 3 words.
+// This gets used if the number is 1, 2, 3, 5, 6, or 9 digits long.
+// The immediate advantage is that it improves EC performance on 192, 384, and 521-bit curves.
+//
+// Most of this code is a direct copy of the default code.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 3 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdef369RawAddAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawAddAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    adds    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    adcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbz     X_3, SymCryptFdef369RawAddAsmEnd
+
+LABEL(SymCryptFdef369RawAddAsmLoop)
+    // carry is in the carry flag
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    adcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369RawAddAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdef369RawAddAsmEnd)
+    cset    X_0, cs                 // Set the return value equal to the carry
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdef369RawSubAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawSubAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    subs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbz     X_3, SymCryptFdef369RawSubAsmEnd
+
+LABEL(SymCryptFdef369RawSubAsmLoop)
+    // borrow is in the carry flag (flipped)
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369RawSubAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdef369RawSubAsmEnd)
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369MaskedCopyAsm(
+//    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369MaskedCopyAsm)
+    .seh_endprologue
+
+    subs    xzr, xzr, X_3           // If (X_3 > 0) clear the carry flag (i.e. borrow)
+
+    ldp     X_3, X_5, [X_0]         // Load two words of the source
+    ldp     X_4, X_6, [X_1]         // Load two words of the destination
+    csel    X_3, X_3, X_4, cc       // If the carry is clear, select the source operand
+    csel    X_5, X_5, X_6, cc
+    stp     X_3, X_5, [X_1]         // Store the two words in the destination
+
+    ldr     X_3, [X_0, #16]         // Load one word of the source
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+    ldr     X_4, [X_1, #16]         // Load one word of the destination
+    csel    X_3, X_3, X_4, cc
+    str     X_3, [X_1, #16]         // Store the one word in the destination
+
+    cbz     X_2, SymCryptFdef369MaskedCopyAsmEnd
+
+LABEL(SymCryptFdef369MaskedCopyAsmLoop)
+    ldp     X_3, X_5, [X_0, #24]!   // Load two words of the source
+    ldp     X_4, X_6, [X_1, #24]!   // Load two words of the destination
+    csel    X_3, X_3, X_4, cc       // If the carry is clear, select the source operand
+    csel    X_5, X_5, X_6, cc
+    stp     X_3, X_5, [X_1]         // Store the two words in the destination
+
+    ldr     X_3, [X_0, #16]         // Load one word of the source
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+    ldr     X_4, [X_1, #16]         // Load one word of the destination
+    csel    X_3, X_3, X_4, cc
+    str     X_3, [X_1, #16]         // Store the one word in the destination
+
+    cbnz    X_2, SymCryptFdef369MaskedCopyAsmLoop
+
+LABEL(SymCryptFdef369MaskedCopyAsmEnd)
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369RawMulAsm(
+//    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                    UINT32      nDigits1,
+//    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                    UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+//
+// Basic structure:
+//   for each word in Src1:
+//       Dst += Src2 * word
+//
+// Register assignments
+//       X_0  = pSrc1 (moving forward one word every outer loop)
+//       X_1  = word count of pSrc1
+//       X_2  = pSrc2 (moving forward one *digit* every inner loop)
+//       X_3  = digit count of pSrc2 and pDst
+//       X_4  = pDst (moving forward one *digit* every inner loop)
+//       X_5  = Stored pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc1
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = Scratch registers for holding the results of multiplies
+//       X_13 = Stored pSrc2
+//       X_14 = Stored digit count of pSrc2
+//       X_15 = Scratch register for holding the results of multiplies
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369RawMulAsm)
+    .seh_endprologue
+
+    add     X_1, X_1, X_1, LSL #1       // Calculate word count (X_1 * 3)
+
+    sub     X_2, X_2, #24               // offset pSrc2 so we can use pre-increment form of loads
+    sub     X_4, X_4, #24               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_5, X_4                    // store pDst
+    mov     X_13, X_2                   // store pSrc2
+    mov     X_14, X_3                   // store nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+
+LABEL(SymCryptFdef369RawMulAsmLoopInner1)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #24]!       // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+1]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+1]
+
+    stp     X_11, X_15, [X_4, #24]!     // Store to destination
+    ldr     X_7, [X_2, #16]             // load 1 word from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j+2]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j+2]
+
+    str     X_11, [X_4, #16]            // Store to destination
+
+    cbnz    X_3, SymCryptFdef369RawMulAsmLoopInner1
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #24]
+
+    sub     X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdef369RawMulAsmLoopOuter)
+    mov     X_3, X_14                   // set nDigits2
+    mov     X_2, X_13                   // set pSrc2
+    mov     X_4, X_5                    // set pDst
+
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the next word from pSrc1
+
+LABEL(SymCryptFdef369RawMulAsmLoopInner)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #24]!       // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #24]!      // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+1]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+1]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4]            // Store to destination
+
+    ldr     X_7, [X_2, #16]             // load 1 word from pSrc2
+    ldr     X_9, [X_4, #16]             // load 1 word from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j+2]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j+2]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+
+    str     X_9, [X_4, #16]             // Store to destination
+
+    cbnz    X_3, SymCryptFdef369RawMulAsmLoopInner
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #24]
+
+    subs    X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    bne     SymCryptFdef369RawMulAsmLoopOuter
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdef369MontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Register assignments
+//       X_0  = pMod (moving forward one *digit* every inner loop)
+//       X_1  = pSrc (moving forward one *digit* every inner loop)
+//       X_2  = pDst (used only in the end for subtract / result)
+//       X_3  = digit count of pSrc and pMod
+//       X_4  = word count of pSrc
+//       X_5  = Inv64 of the modulus
+//       X_6  = m = pSrc[i]*Inv64
+//       X_7  = hc = high carry variable
+//       X_8, X_9   = Current words loaded in pairs from pSrc
+//       X_10, X_11 = Current words loaded in pairs from pMod
+//       X_12, X_13 = c variable = "128-bit" register to hold the result of multiplies
+//                  It is flipped between [X_12:X_13] and [X_13:X_12] instead of doing c>>=64
+//       X_14 = Temporary intermediate result
+//       X_15 = Stored digit count of pSrc
+//       X_16 = Stored pMod pointer
+//       X_17 = Stored pSrc pointer (moving forward one word every outer loop)
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+#define X_16 x19
+#define W_16 w19
+#define X_17 x20
+#define W_17 w20
+PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdef369MontgomeryReduceAsm)
+    stp fp, lr, [sp, #-32]! // allocate 32 bytes of stack; store FP/LR
+    .seh_save_fplr_x 32
+    stp X_16, X_17, [sp, #16]
+    .seh_save_regp X_16, 16
+    .seh_endprologue
+
+    ldr     W_3, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64      // pMod
+
+    add     X_4, X_3, X_3, LSL #1       // Calculate word count (X_3 * 3)
+
+    sub     X_0, X_0, #24               // offset pMod so we can use pre-increment form of loads
+    sub     X_1, X_1, #24               // offset pSrc so we can use pre-increment form of loads
+    sub     X_2, X_2, #24               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_15, X_3                   // Store the digit count for later
+    mov     X_16, X_0                   // Store the pMod pointer
+    mov     X_17, X_1                   // Store the pSrc pointer
+
+    mov     X_7, xzr                    // Set hc to 0
+
+    //
+    // Main loop
+    //
+LABEL(SymCryptFdef369MontgomeryReduceAsmOuter)
+    ldr     X_8, [X_1, #24]             // Load 1 word from pSrc
+    mul     X_6, X_8, X_5               // <63:0> bits of pSrc[i]*Inv64 = m
+
+    mov     X_12, xzr                   // Set c to 0
+
+LABEL(SymCryptFdef369MontgomeryReduceAsmInner)
+    ldp     X_10, X_11, [X_0, #24]!     // pMod[j]
+    ldp     X_8, X_9, [X_1, #24]!       // pSrc[j]
+
+    mul     X_14, X_6, X_10             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_8             // Adding pSrc[j]
+    umulh   X_13, X_6, X_10             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    // ***: These cannot produce extra carry as the maximum is
+    //      (2^64 - 1)*(2^64-1) + 2^64-1 + 2^64-1 = 2^128 - 1
+    str     X_12, [X_1]                 // pSrc[j] = (UINT64) c4) c
+    mov     X_12, X_13                  // c >>= 64
+
+    mul     X_14, X_6, X_11             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_9             // Adding pSrc[j]
+    umulh   X_13, X_6, X_11             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    str     X_12, [X_1, #8]             // pSrc[j] = (UINT64) c
+    mov     X_12, X_13                  // c >>= 64
+
+    ldr     X_10, [X_0, #16]            // pMod[j]
+    ldr     X_8, [X_1, #16]             // pSrc[j]
+
+    mul     X_14, X_6, X_10             // <63:0> of pMod[j]*m
+    adds    X_14, X_14, X_8             // Adding pSrc[j]
+    umulh   X_13, X_6, X_10             // <127:64> of pMod[j]*m
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    adds    X_12, X_12, X_14            // Add the lower bits of c
+    adc     X_13, X_13, xzr             // Add the carry if any (***)
+    str     X_12, [X_1, #16]            // pSrc[j] = (UINT64) c4) c
+    mov     X_12, X_13                  // c >>= 64
+
+    subs    X_3, X_3, #1                // Move one digit up
+    bne     SymCryptFdef369MontgomeryReduceAsmInner
+
+    ldr     X_8, [X_1, #24]             // pSrc[nWords]
+    adds    X_12, X_12, X_8             // c + pSrc[nWords]
+    adc     X_13, xzr, xzr              // Add the carry if any
+
+    adds    X_12, X_12, X_7             // c + pSrc[nWords] + hc
+    adc     X_7, X_13, xzr              // Add the carry if any and store into hc
+
+    str     X_12, [X_1, #24]            // pSrc[nWords] = c
+
+    subs    X_4, X_4, #1                // Move one word up
+
+    add     X_17, X_17, #8              // Move stored pSrc pointer one word up
+    mov     X_0, X_16                   // Restore pMod pointer
+    mov     X_1, X_17                   // Restore pSrc pointer
+
+    mov     X_3, X_15                   // Restore the digit counter
+
+    bne     SymCryptFdef369MontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+
+    mov     X_14, X_2               // Store pDst pointer
+
+    // Prepare the pointers for subtract
+    mov     X_0, X_17               // pSrc
+    mov     X_1, X_16               // pMod
+
+    mov     X_10, X_7               // X_10 = hc
+    mov     X_3, X_15               // Restore the digit counter
+    subs    X_4, X_4, X_4           // Set the carry flag (i.e. no borrow)
+
+LABEL(SymCryptFdef369MontgomeryReduceRawSubAsmLoop)
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    // borrow is in the carry flag (flipped)
+
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #24]!   // Store the result in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of pSrc1
+    ldr     X_5, [X_1, #16]         // Load one word of pSrc2
+    sbcs    X_4, X_4, X_5
+    str     X_4, [X_2, #16]         // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdef369MontgomeryReduceRawSubAsmLoop
+
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    orr     X_11, X_10, X_0         // X_11 = hc|d
+
+    // Prepare the pointers for masked copy
+    mov     X_0, X_17               // pSrc
+    mov     X_1, X_14               // pDst
+
+    mov     X_2, X_15               // Restore the digit counter
+    subs    X_4, X_10, X_11         // If (X_11 > X_10) clear the carry flag (i.e. borrow)
+
+LABEL(SymCryptFdef369MontgomeryReduceMaskedCopyAsmLoop)
+    sub     X_2, X_2, #1            // decrement the digit count by one
+
+    ldp     X_4, X_6, [X_0, #24]!   // Load two words of the source
+    ldp     X_5, X_7, [X_1, #24]!   // Load two words of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1]         // Store the two words in the destination
+
+    ldr     X_4, [X_0, #16]         // Load one word of the source
+    ldr     X_5, [X_1, #16]         // Load one word of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    str     X_4, [X_1, #16]         // Store the one word in the destination
+
+    cbnz    X_2, SymCryptFdef369MontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    ldp X_16, X_17, [sp, #16]
+    ldp fp, lr, [sp], #32 // deallocate 32 bytes of stack; restore FP/LR
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+#undef X_17
+#undef W_17
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64ec/fdef_asm.S b/libs/symcrypt/lib/arm64ec/fdef_asm.S
new file mode 100644
index 000000000000..e7b85b8475bc
--- /dev/null
+++ b/libs/symcrypt/lib/arm64ec/fdef_asm.S
@@ -0,0 +1,2496 @@
+//
+//  fdef_asm.symcryptasm   Assembler code for large integer arithmetic in the default data format for the arm64 architecture
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// A digit consists of 4 words of 64 bits each
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawAddAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawAddAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    adds    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbz     X_3, SymCryptFdefRawAddAsmEnd
+
+LABEL(SymCryptFdefRawAddAsmLoop)
+    // carry is in the carry flag
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    adcs    X_4, X_4, X_5
+    adcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefRawAddAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdefRawAddAsmEnd)
+    cset    X_0, cs                 // Set the return value equal to the carry
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSubAsm(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSubAsm)
+    .seh_endprologue
+
+    ldp     X_4, X_6, [X_0]         // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1]         // Load two words of pSrc2
+    subs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2]         // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbz     X_3, SymCryptFdefRawSubAsmEnd
+
+LABEL(SymCryptFdefRawSubAsmLoop)
+    // borrow is in the carry flag (flipped)
+    // only update pointers to srcs and destination once per loop to reduce uops and dependencies
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of pSrc1
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_0, #16]    // Load two words of pSrc1
+    sub     X_3, X_3, #1            // Decrement the digit count by one
+    ldp     X_5, X_7, [X_1, #16]    // Load two words of pSrc2
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefRawSubAsmLoop
+
+    ALIGN(4)
+LABEL(SymCryptFdefRawSubAsmEnd)
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopyAsm(
+//    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+//    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMaskedCopyAsm)
+    .seh_endprologue
+
+    dup     v0.4s, W_3              // broadcast the mask to v0
+
+LABEL(SymCryptFdefMaskedCopyAsmLoop)
+    ldp     q1, q3, [X_0], #32      // Load 4 words of the source
+    ldp     q2, q4, [X_1]           // Load 4 words of the destination
+    bit     v2.16b, v1.16b, v0.16b  // if the mask is 1s, overwrite the destination with source
+    bit     v4.16b, v3.16b, v0.16b  // if the mask is 1s, overwrite the destination with source
+    stp     q2, q4, [X_1], #32      // Store the two words in the destination
+
+    sub     X_2, X_2, #1            // Decrement the digit count by one
+
+    cbnz    X_2, SymCryptFdefMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMulAsm(
+//    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+//                                                                    UINT32      nDigits1,
+//    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+//                                                                    UINT32      nDigits2,
+//    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+//
+// Basic structure:
+//   for each word in Src1:
+//       Dst += Src2 * word
+//
+// Register assignments
+//       X_0  = pSrc1 (moving forward one word every outer loop)
+//       X_1  = word count of pSrc1
+//       X_2  = pSrc2 (moving forward one *digit* every inner loop)
+//       X_3  = digit count of pSrc2 and pDst
+//       X_4  = pDst (moving forward one *digit* every inner loop)
+//       X_5  = Stored pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc1
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = Scratch registers for holding the results of multiplies
+//       X_13 = Stored pSrc2
+//       X_14 = Stored digit count of pSrc2
+//       X_15 = Scratch register for holding the results of multiplies
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawMulAsm)
+    .seh_endprologue
+
+    lsl     X_1, X_1, #2                // Calculate word count
+
+    sub     X_2, X_2, #32               // offset pSrc2 so we can use pre-increment form of loads
+    sub     X_4, X_4, #32               // offset pDst so we can use pre-increment form of loads
+
+    mov     X_5, X_4                    // store pDst
+    mov     X_13, X_2                   // store pSrc2
+    mov     X_14, X_3                   // store nDigits2 for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+
+LABEL(SymCryptFdefRawMulAsmLoopInner1)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #32]!       // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+1]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+1]
+
+    stp     X_11, X_15, [X_4, #32]!     // Store to destination
+    ldp     X_7, X_8, [X_2, #16]        // load 2 words from pSrc2
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[0]*pSrc2[j+2]
+    adcs    X_11, X_11, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_7              // Bits <127:64> of pSrc1[0]*pSrc2[j+2]
+
+    mul     X_15, X_6, X_8              // Bits <63:0> of pSrc1[0]*pSrc2[j+3]
+    adcs    X_15, X_15, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[0]*pSrc2[j+3]
+
+    stp     X_11, X_15, [X_4, #16]      // Store to destination
+
+    cbnz    X_3, SymCryptFdefRawMulAsmLoopInner1
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #32]
+
+    sub     X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdefRawMulAsmLoopOuter)
+    mov     X_3, X_14                   // set nDigits2
+    mov     X_2, X_13                   // set pSrc2
+    mov     X_4, X_5                    // set pDst
+
+    ands    X_12, X_12, xzr             // Clearing the carry flag and setting X_12 = 0
+    ldr     X_6, [X_0]                  // load the next word from pSrc1
+
+LABEL(SymCryptFdefRawMulAsmLoopInner)
+    sub     X_3, X_3, #1                // move one digit up
+
+    ldp     X_7, X_8, [X_2, #32]!       // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #32]!      // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+1]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+1]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4]            // Store to destination
+
+    ldp     X_7, X_8, [X_2, #16]        // load 2 words from pSrc2
+    ldp     X_9, X_10, [X_4, #16]       // load 2 words from pDst
+
+    adcs    X_9, X_9, X_12              // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_11, X_6, X_7              // Bits <127:64> of pSrc1[i]*pSrc2[j+2]
+    adcs    X_10, X_11, X_10            // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12, X_6, X_8              // Bits <127:64> of pSrc1[i]*pSrc2[j+3]
+    adc     X_12, X_12, xzr             // Add the carry if any and don't update the flags
+
+    mul     X_11, X_6, X_7              // Bits <63:0> of pSrc1[i]*pSrc2[j+2]
+    adds    X_9, X_9, X_11              // add the word from the destination and update the flags (this can overflow)
+    mul     X_11, X_6, X_8              // Bits <63:0> of pSrc1[i]*pSrc2[j+3]
+    adcs    X_10, X_10, X_11            // add the word from the destination and update the flags (this can overflow)
+
+    stp     X_9, X_10, [X_4, #16]       // Store to destination
+
+    cbnz    X_3, SymCryptFdefRawMulAsmLoopInner
+
+    adc     X_12, X_12, xzr             // Store the next word into the destination (with the carry if any)
+    str     X_12, [X_4, #32]
+
+    subs    X_1, X_1, #1                // move one word up
+    add     X_0, X_0, #8                // move start of pSrc1 one word up
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    bne     SymCryptFdefRawMulAsmLoopOuter
+
+    // Done, no return value
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+
+    // Macro for the first loop of the first pass of RawSquareAsm.
+    // It takes one word from the source, multiplies it with the mulword,
+    // adds the high level word of the previous macro call, and stores it into
+    // the destination.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^64-1)^2 + 2^64-1 = 2^128 - 2^64
+.macro SQR_SINGLEADD_64  index, src_reg, dst_reg, mul_word, src_carry, dst_carry, scratch0, scratch1
+
+    ldr     \scratch0, [\src_reg, #8*\index]   // pSrc[i+j]
+
+    mul     \scratch1, \mul_word, \scratch0    // Bits <63:0> of pSrc[i]*pSrc[i+j]
+    adds    \scratch1, \scratch1, \src_carry   // Adding the previous word
+    umulh   \dst_carry, \mul_word, \scratch0   // Bits <127:64> of pSrc[i]*pSrc[i+j]
+    adc     \dst_carry, \dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    str     \scratch1, [\dst_reg, #8*\index]   // Store to destination
+
+.endm
+
+    // Macro for the remaining loops of the first pass of RawSquareAsm.
+    // The only difference to the above is that it also adds the word loaded
+    // from the destination buffer.
+    //
+    // No carry flag is propagated from the previous macro call as the maximum is
+    // (2^64-1)^2 + 2(2^64-1) = 2^128 - 1
+.macro SQR_DOUBLEADD_64  index, src_reg, dst_reg, mul_word, src_carry, dst_carry, scratch0, scratch1, scratch2
+
+    ldr     \scratch0, [\src_reg, #8*\index]   // pSrc[i+j]
+    ldr     \scratch2, [\dst_reg, #8*\index]   // pDst[2*(i+j)]
+
+    mul     \scratch1, \mul_word, \scratch0    // Bits <63:0> of pSrc[i]*pSrc[i+j]
+    umulh   \dst_carry, \mul_word, \scratch0   // Bits <127:64> of pSrc[i]*pSrc[i+j]
+
+    adds    \scratch1, \scratch1, \scratch2    // Add the word from the destination
+    adc     \dst_carry, \dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    adds    \scratch1, \scratch1, \src_carry   // Adding the previous word
+    adc     \dst_carry, \dst_carry, xzr       // Add the intermediate carry and don't update the flags
+
+    str     \scratch1, [\dst_reg, #8*\index]   // Store to destination
+
+.endm
+
+    // Macro for the third pass loop of RawSquareAsm.
+    // It takes one mulword from the source, squares it, and
+    // adds it to the even columns of the destination. The carries are propagated
+    // to the odd columns.
+    //
+    // Here we can have a (1-bit) carry to the next call because the maximum value for
+    // a pair of columns is (2^64-1)^2+(2^128-1)+1 = 2^129 - 2^65 + 1 < 2^129 - 1
+.macro SQR_DIAGONAL_PROP  index, src_reg, dst_reg, squarelo, squarehi, scratch0, scratch1
+
+    ldr     \squarehi, [\src_reg, #8*\index]               // mulword
+    mul     \squarelo, \squarehi, \squarehi                // Bits <63:0> of m^2
+    umulh   \squarehi, \squarehi, \squarehi                // Bits <127:64> of m^2
+
+    ldp     \scratch0, \scratch1, [\dst_reg, #16*\index]    // Load
+
+    // Adding the square to the even column
+    adcs    \squarelo, \squarelo, \scratch0                // carry from previous and update the flags
+
+    // Propagating the sum to the next column
+    adcs    \squarehi, \squarehi, \scratch1                // This can generate a carry
+
+    stp     \squarelo, \squarehi, [\dst_reg, #16*\index]    // Store
+
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawSquareAsm(
+//    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+//                                                        UINT32      nDigits,
+//    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst )
+//
+// Register assignments
+//       X_0  = pSrc
+//       X_1  = word count of pSrc
+//       X_2  = pSrc (moving forward one digit / 4 words every inner loop)
+//       X_3  = digit count of pSrc
+//       X_4  = pDst (moving forward one digit every inner loop)
+//       X_5  = pDst (moving forward one word every outer loop)
+//       X_6  = Current word loaded from pSrc
+//       X_7, X_8   = Current words loaded in pairs from pSrc2
+//       X_9, X_10  = Current words loaded in pairs from pDst
+//       X_11, X_12 = "128-bit" sliding register to hold the result of multiplies
+//       X_13 = Stored pSrc
+//       X_14 = Digit count of pSrc
+//       X_15 = Stored digit count of pSrc
+//       X_16 = Stored pDst
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+#define X_16 x19
+#define W_16 w19
+PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefRawSquareAsm)
+    stp fp, lr, [sp, #-32]! // allocate 32 bytes of stack; store FP/LR
+    .seh_save_fplr_x 32
+    str      X_16, [sp, #16]
+    .seh_save_reg X_16, 16
+    .seh_endprologue
+
+    mov     X_3, X_1                    // digit count into X_3
+
+    lsl     X_1, X_1, #2                // Calculate word count
+
+    mov     X_4, X_2                    // pDst
+    mov     X_5, X_2                    // store pDst
+    mov     X_16, X_2                   // store pDst
+    mov     X_13, X_0                   // store pSrc
+    mov     X_2, X_0                    // inner loop pSrc
+    mov     X_14, X_3                   // store nDigits for later
+    mov     X_15, X_3                   // store nDigits for later
+
+    //
+    // First iteration of main loop (no adding of previous values from pDst)
+    //
+    mov     X_12, xzr                   // set X_12 = 0
+    ldr     X_6, [X_0]                  // load the first word from pSrc1
+    str     xzr, [X_4]                  // store 0 for the first word
+
+    b       SymCryptFdefRawSquareAsmInnerLoopInit_Word1
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoopInit_Word0)
+    SQR_SINGLEADD_64 0, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoopInit_Word1)
+    SQR_SINGLEADD_64 1, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    SQR_SINGLEADD_64 2, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    SQR_SINGLEADD_64 3, X_2, X_4, X_6, X_12, X_12, X_7, X_8
+
+    sub     X_3, X_3, #1                // move one digit up
+    add     X_2, X_2, #32
+    add     X_4, X_4, #32
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmInnerLoopInit_Word0
+
+    str     X_12, [X_4]                 // Store the next word into the destination
+
+    sub     X_1, X_1, #2                // move two words up (we started at the word 1)
+
+    mov     X_8, #1                     // Cyclic counter
+
+    //
+    // MAIN LOOP
+    //
+LABEL(SymCryptFdefRawSquareAsmOuterLoop)
+
+    add     X_5, X_5, #8                // move start of pDst one word up
+
+    mov     X_3, X_14                   // set nDigits
+    mov     X_2, X_0                    // set pSrc
+    mov     X_4, X_5                    // set pDst
+
+    mov     X_11, xzr                   // set X_11 = 0
+    mov     X_12, xzr                   // set X_12 = 0
+    ldr     X_6, [X_0, X_8, LSL #3]     // load the next word from pSrc
+
+    // Cyclic counter and jump logic
+    add     X_8, X_8, #1
+    cmp     X_8, #1
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word1
+    cmp     X_8, #2
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word2
+    cmp     X_8, #3
+    beq     SymCryptFdefRawSquareAsmInnerLoop_Word3
+
+    // The following instructions are only executed when X_8 == 4
+    mov     X_8, xzr                // Set it to 0
+
+    add     X_0, X_0, #32           // move start of pSrc 4 words up
+    add     X_5, X_5, #32           // move pDst 4 words up
+
+    mov     X_2, X_0                // set pSrc
+    mov     X_4, X_5                // set pDst
+
+    sub     X_14, X_14, #1          // remove 1 digit
+    mov     X_3, X_14               // set the new digit counter
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word0)
+    SQR_DOUBLEADD_64 0, X_2, X_4, X_6, X_12, X_11, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word1)
+    SQR_DOUBLEADD_64 1, X_2, X_4, X_6, X_11, X_12, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word2)
+    SQR_DOUBLEADD_64 2, X_2, X_4, X_6, X_12, X_11, X_7, X_9, X_10
+
+LABEL(SymCryptFdefRawSquareAsmInnerLoop_Word3)
+    SQR_DOUBLEADD_64 3, X_2, X_4, X_6, X_11, X_12, X_7, X_9, X_10
+
+    sub     X_3, X_3, #1                // move one digit up
+    add     X_2, X_2, #32
+    add     X_4, X_4, #32
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmInnerLoop_Word0
+
+    str     X_12, [X_4]                 // Store the next word into the destination
+
+    sub     X_1, X_1, #1                // move one word up
+    cbnz    X_1, SymCryptFdefRawSquareAsmOuterLoop
+
+    str     xzr, [X_5, #40]             // Store 0 to destination for the top word
+
+    ////////////////////////////////////////////////////////////////
+    // Second Pass - Shifting all results 1 bit left
+    ////////////////////////////////////////////////////////////////
+
+    mov     X_3, X_15       // nDigits
+    lsl     X_3, X_3, #1    // Double digits
+    mov     X_4, X_16       // pDst pointer
+    ands    X_7, X_7, xzr   // Clear the flags
+
+LABEL(SymCryptFdefRawSquareAsmSecondPass)
+
+    sub     X_3, X_3, #1    // move one digit up
+
+    ldp     X_7, X_8, [X_4]
+    adcs    X_7, X_7, X_7   // Shift left and add the carry
+    adcs    X_8, X_8, X_8
+    stp     X_7, X_8, [X_4], #16
+
+    ldp     X_9, X_10, [X_4]
+    adcs    X_9, X_9, X_9   // Shift left and add the carry
+    adcs    X_10, X_10, X_10
+    stp     X_9, X_10, [X_4], #16
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmSecondPass
+
+    //////////////////////////////////////////////////////////////////////////////
+    // Third Pass - Adding the squares on the even columns and propagating the sum
+    //////////////////////////////////////////////////////////////////////////////
+
+    ands    X_7, X_7, xzr   // Clear the flags
+    mov     X_0, X_13       // src pointer
+    mov     X_4, X_16       // pDst pointer
+    mov     X_3, X_15       // nDigits
+
+LABEL(SymCryptFdefRawSquareAsmThirdPass)
+    SQR_DIAGONAL_PROP 0, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 1, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 2, X_0, X_4, X_6, X_7, X_8, X_9
+    SQR_DIAGONAL_PROP 3, X_0, X_4, X_6, X_7, X_8, X_9
+
+    sub     X_3, X_3, #1        // move one digit up
+    add     X_0, X_0, #32       // One digit up (not updated in SQR_DIAGONAL_PROP)
+    add     X_4, X_4, #64       // Two digits up (not updated in SQR_DIAGONAL_PROP)
+
+    cbnz    X_3, SymCryptFdefRawSquareAsmThirdPass
+
+    // Done, no return value
+
+    ldr      X_16, [sp, #16]
+    ldp fp, lr, [sp], #32 // deallocate 32 bytes of stack; restore FP/LR
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAdd256Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 + Src2 into (X_4, X_5, X_6, X_7)
+    ldp     X_4, X_5, [X_1]       // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2]       // Load two words of peSrc2
+    adds    X_4, X_4, X_8
+    adcs    X_5, X_5, X_9
+
+    ldp     X_6, X_7, [X_1, #16]  // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2, #16]  // Load two words of peSrc2
+    adcs    X_6, X_6, X_8
+    adcs    X_7, X_7, X_9
+
+    cset    X_1, cs                 // If the carry is set, set X_1 to 1 (else 0)
+
+    // Compute sum - Mod into (X_2, X_8, X_9, X_0) = sum - modulus
+
+    ldp     X_2, X_8, [X_0]       // Load two words of pMod
+    subs    X_2, X_4, X_2
+    sbcs    X_8, X_5, X_8
+
+    ldp     X_9, X_0, [X_0, #16]  // Load two words of pMod
+    sbcs    X_9, X_6, X_9
+    sbcs    X_0, X_7, X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_1
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_1, X_1, XZR
+
+    csel    X_4, X_4, X_2, cc
+    csel    X_5, X_5, X_8, cc
+    stp     X_4, X_5, [X_3]
+
+    csel    X_6, X_6, X_9, cc
+    csel    X_7, X_7, X_0, cc
+    stp     X_6, X_7, [X_3, #16]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSub256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSub256Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 - Src2 into (X_4, X_5, X_6, X_7)
+    ldp     X_4, X_5, [X_1]         // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2]         // Load two words of peSrc2
+    subs    X_4, X_4, X_8
+    sbcs    X_5, X_5, X_9
+
+    ldp     X_6, X_7, [X_1, #16]    // Load two words of peSrc1
+    ldp     X_8, X_9, [X_2, #16]    // Load two words of peSrc2
+    sbcs    X_6, X_6, X_8
+    sbcs    X_7, X_7, X_9
+
+    // Load the masked modulus into (X_1, X_2, X_8, X_9)
+    ldp     X_1, X_2, [X_0]         // Load two words of pMod
+    csel    X_1, X_1, XZR, cc       // If we did not borrow, zero the modulus
+    csel    X_2, X_2, XZR, cc
+
+    ldp     X_8, X_9, [X_0, #16]    // Load two words of pMod
+    csel    X_8, X_8, XZR, cc
+    csel    X_9, X_9, XZR, cc
+
+    // Add the masked modulus
+    adds    X_4, X_4, X_1
+    adcs    X_5, X_5, X_2
+    stp     X_4, X_5, [X_3]
+
+    adcs    X_6, X_6, X_8
+    adc     X_7, X_7, X_9
+    stp     X_6, X_7, [X_3, #16]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+
+
+.macro MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE  T0, T1, Ai, pB, pM, K, Inv64, R0, R1, R2, R3, R4, R5
+    // (\R1, \R2, \R3, \R4, \R5) = \Ai * (B0..3) + (\R1, \R2, \R3, \R4, \R5)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R0) = \K * (M0..3) + (\R0, \R1, \R2, \R3, \R4, \R5)
+    // \K = \Inv64 * \R1
+    // Note \K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \pM is a pointer to the bytes of the modulus (i.e. offset into SYMCRYPT_MODULUS structure)
+    // \R5 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \T0, \T1 = scratch
+    // \Ai is also used as scratch when it's no longer needed
+
+    // (\R1, \R2, \R3, \R4, \R5) += \Ai * B even
+    ldr     \T0, [\pB]
+    mul     \T1, \Ai, \T0
+    adds    \R1, \R1, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R2, \R2, \T0
+
+    ldr     \T0, [\pB, #16]
+    mul     \T1, \Ai, \T0
+    adcs    \R3, \R3, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R4, \R4, \T0
+
+    adc     \R5, \R5, XZR // \R5 in range [0,2]
+
+    // (\R2, \R3, \R4, \R5) += \Ai * B odd
+    ldr     \T0, [\pB, #8]
+    mul     \T1, \Ai, \T0
+    adds    \R2, \R2, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R3, \R3, \T0
+
+    ldr     \T0, [\pB, #24]
+    mul     \T1, \Ai, \T0
+    adcs    \R4, \R4, \T1
+    umulh   \Ai, \Ai, \T0
+    adc     \Ai, \Ai, XZR // \Ai in range [0, 2^64-1]
+                        // defer adding \Ai to \R5 to avoid overflowing earlier than necessary
+
+    // (xx, \R1, \R2, \R3, \R4, \R5) = \K * M even + (\R0, \R1, \R2, \R3, \R4, \R5)
+    ldr     \T0, [\pM]
+    mul     \T1,  \K, \T0
+    adds    \R0, \R0, \T1  // Sets \R0 to 0
+    umulh   \T0,  \K, \T0
+    adcs    \R1, \R1, \T0
+
+    ldr     \T0, [\pM, #16]
+    mul     \T1,  \K, \T0
+    adcs    \R2, \R2, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \R3, \R3, \T0
+
+    adcs    \R4, \R4, XZR
+    adc     \R5, \R5, XZR // \R5 in range [0,3]
+
+    // (\R1, \R2, \R3, \R4, \R5, \R0) = \K * M odd + (\R1, \R2, \R3, \R4, \R5, \R0)
+    ldr     \T0, [\pM, #8]
+    mul     \T1,  \K, \T0
+    adds    \R1, \R1, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \R2, \R2, \T0
+
+    ldr     \T0, [\pM, #24]
+    mul     \T1,  \K, \T0
+    adcs    \R3, \R3, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \R4, \R4, \T0
+
+    mul     \K, \R1, \Inv64
+    adcs    \R5, \R5, \Ai
+    cset    \R0, cs
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256Asm)
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc1
+    // X_2 = pSrc2
+    // X_3 = pDst
+    // X_4 = Ai
+    // X_5, X_6 = T0, T1
+    // X_7 = Inv64
+    // X_8 = K
+    // X_9, X_10, X_11, X_12, X_13, X_14 = R0..R5
+
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal): .global PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal)
+
+    ldr     X_4, [X_1]              // Load pSrc1
+
+    ldp     X_10, X_11, [X_2]       // Load pSrc2 B[0], B[1]
+    mul      X_9,  X_4, X_10        // R0 = <63:0>   of A[0]*B[0]
+    umulh   X_10,  X_4, X_10        // R1 = <127:64> of A[0]*B[0]
+
+    mul      X_5,  X_4, X_11        // T0 = <63:0>   of A[0]*B[1]
+    adds    X_10, X_10,  X_5        // Adding the previous word
+    umulh   X_11,  X_4, X_11        // R2 = <127:64> of A[0]*B[1]
+
+    ldp     X_12, X_13, [X_2, #16]  // Load pSrc2 B[2], B[3]
+    mul      X_5,  X_4, X_12        // T0 = <63:0>   of A[0]*B[2]
+    adcs    X_11, X_11,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12,  X_4, X_12        // R3 = <127:64> of A[0]*B[2]
+
+    mul      X_5,  X_4, X_13        // T0 = <63:0>   of A[0]*B[3]
+    adcs    X_12, X_12,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_13,  X_4, X_13        // R4 = <127:64> of A[0]*B[3]
+    adc     X_13, X_13,  XZR
+
+    eor     X_14, X_14, X_14
+
+    ldr     X_7, [X_0, #SymCryptModulusInv64OffsetArm64]
+    mul     X_8, X_9, X_7           // K = R0 * Inv64
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64
+
+    ldr     X_4, [X_1, #8]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_9, X_10, X_11, X_12, X_13, X_14
+
+    ldr     X_4, [X_1, #16]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_10, X_11, X_12, X_13, X_14, X_9
+
+    ldr     X_4, [X_1, #24]
+    MUL_AND_MONTGOMERY_REDUCE14_INTERLEAVE X_5, X_6, X_4, X_2, X_0, X_8, X_7, X_11, X_12, X_13, X_14, X_9, X_10
+
+    // (xx, R1, R2, R3, R4, R5) = K * M even + (R0, R1, R2, R3, R4, R5)
+
+    ldp      X_4,  X_5, [X_0]   // Load Mod
+    mul      X_1,  X_8, X_4
+    adds    X_12, X_12, X_1     // Sets R0 to 0
+    umulh    X_2,  X_8, X_4
+    adcs    X_13, X_13, X_2
+
+    ldp      X_6,  X_7, [X_0, #16]
+    mul      X_1,  X_8, X_6
+    adcs    X_14, X_14, X_1
+    umulh    X_2,  X_8, X_6
+    adcs     X_9,  X_9, X_2
+
+    adcs    X_10, X_10, XZR
+    adc     X_11, X_11, XZR
+
+    // (R1, R2, R3, R4, R5) = K * M odd + (R1, R2, R3, R4, R5)
+    mul      X_1,  X_8, X_5
+    adds    X_13, X_13, X_1
+    umulh    X_2,  X_8, X_5
+    adcs    X_14, X_14, X_2
+
+    mul      X_1,  X_8, X_7
+    adcs     X_9,  X_9, X_1
+    umulh    X_2,  X_8, X_7
+    adcs    X_10, X_10, X_2
+
+    adc     X_11, X_11, XZR
+
+    // Montgomery-reduced multiplication value in (X_13, X_14, X_9, X_10, X_11), and it is less than 2*Modulus
+    // Compute value - Mod into (X_4, X_5, X_6, X_7), Cy = inverted subtraction borrow
+
+    subs    X_4, X_13, X_4
+    sbcs    X_5, X_14, X_5
+    sbcs    X_6,  X_9, X_6
+    sbcs    X_7, X_10, X_7
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_11
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_11, X_11, XZR
+
+    csel    X_13, X_13, X_4, cc
+    csel    X_14, X_14, X_5, cc
+    stp     X_13, X_14, [X_3]
+
+    csel     X_9,  X_9, X_6, cc
+    csel    X_10, X_10, X_7, cc
+    stp      X_9, X_10, [X_3, #16]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomery256Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomery256Asm the same
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomery256Asm)
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc
+    // X_2 = pDst
+
+    mov     X_3, X_2
+    mov     X_2, X_1
+
+    // Just jump into multiply, similar to SymCryptWipeAsm jumping into memset.
+
+    b       PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomery256AsmInternal)
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModAdd384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModAdd384Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 + Src2 into (X_4, X_5, X_6, X_7, X_8, X_9)
+    ldp     X_4,  X_5,  [X_1]       // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2]       // Load two words of peSrc2
+    adds    X_4,  X_4,  X_10
+    adcs    X_5,  X_5,  X_11
+
+    ldp     X_6,  X_7,  [X_1, #16]  // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #16]  // Load two words of peSrc2
+    adcs    X_6,  X_6,  X_10
+    adcs    X_7,  X_7,  X_11
+
+    ldp     X_8,  X_9,  [X_1, #32]  // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #32]  // Load two words of peSrc2
+    adcs    X_8,  X_8,  X_10
+    adcs    X_9,  X_9,  X_11
+
+    cset    X_1, cs                 // If the carry is set, set X_1 to 1 (else 0)
+
+    // Compute sum - Mod into (X_2, X_10, X_11, X_12, X_13, X_0) = sum - modulus
+
+    ldp     X_2, X_10,  [X_0]       // Load two words of pMod
+    subs    X_2,  X_4,  X_2
+    sbcs    X_10, X_5,  X_10
+
+    ldp     X_11, X_12, [X_0, #16]  // Load two words of pMod
+    sbcs    X_11, X_6,  X_11
+    sbcs    X_12, X_7,  X_12
+
+    ldp     X_13, X_0,  [X_0, #32]  // Load two words of pMod
+    sbcs    X_13, X_8,  X_13
+    sbcs    X_0,  X_9,  X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_1
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_1, X_1, XZR
+
+    csel    X_4, X_4, X_2,  cc
+    csel    X_5, X_5, X_10, cc
+    stp     X_4, X_5, [X_3]
+
+    csel    X_6, X_6, X_11, cc
+    csel    X_7, X_7, X_12, cc
+    stp     X_6, X_7, [X_3, #16]
+
+    csel    X_8, X_8, X_13, cc
+    csel    X_9, X_9, X_0,  cc
+    stp     X_8, X_9, [X_3, #32]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSub384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSub384Asm)
+    .seh_endprologue
+
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64 // pMod
+
+    // compute Src1 - Src2 into (X_4, X_5, X_6, X_7, X_8, X_9)
+    ldp     X_4,  X_5,  [X_1]         // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2]         // Load two words of peSrc2
+    subs    X_4,  X_4,  X_10
+    sbcs    X_5,  X_5,  X_11
+
+    ldp     X_6,  X_7,  [X_1, #16]    // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #16]    // Load two words of peSrc2
+    sbcs    X_6,  X_6,  X_10
+    sbcs    X_7,  X_7,  X_11
+
+    ldp     X_8,  X_9,  [X_1, #32]    // Load two words of peSrc1
+    ldp     X_10, X_11, [X_2, #32]    // Load two words of peSrc2
+    sbcs    X_8,  X_8,  X_10
+    sbcs    X_9,  X_9,  X_11
+
+    // Load the masked modulus into (X_1, X_2, X_10, X_11, X_12, X_0)
+    ldp     X_1, X_2, [X_0]         // Load two words of pMod
+    csel    X_1, X_1, XZR, cc       // If we did not borrow, zero the modulus
+    csel    X_2, X_2, XZR, cc
+
+    ldp     X_10, X_11, [X_0, #16]  // Load two words of pMod
+    csel    X_10, X_10, XZR, cc
+    csel    X_11, X_11, XZR, cc
+
+    ldp     X_12, X_0,  [X_0, #32]  // Load two words of pMod
+    csel    X_12, X_12, XZR, cc
+    csel    X_0,  X_0,  XZR, cc
+
+    // Add the masked modulus
+    adds    X_4, X_4, X_1
+    adcs    X_5, X_5, X_2
+    stp     X_4, X_5, [X_3]
+
+    adcs    X_6, X_6, X_10
+    adcs    X_7, X_7, X_11
+    stp     X_6, X_7, [X_3, #16]
+
+    adcs    X_8, X_8, X_12
+    adc     X_9, X_9, X_0
+    stp     X_8, X_9, [X_3, #32]
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+
+
+.macro MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE  T0, T1, Ai, pB, K, N3, R0, R1, R2, R3, R4, R5, R6, R7
+    // (\R1, \R2, \R3, \R4, \R5, \R6, \R7) = \Ai * (B0..5) + (\R1, \R2, \R3, \R4, \R5, \R6, \R7)
+    // (xx, \R1, \R2, \R3, \R4, \R5, \R6, \R7, \R0) = \K * (M0..5) + (\R0, \R1, \R2, \R3, \R4, \R5, \R6, \R7)
+    // Note \K is chosen using pMod's montgomery inverse s.t. xx would get set to 0
+    // \R7 is 0 or 1 at start
+    // \R0 is 0 or 1 at end
+    // \N3 in [0, 2] at start and end
+    // \T0, \T1 = scratch
+    // \Ai is also used as scratch when it's no longer needed
+
+    // (\R1, \R2, \R3, \R4, \R5, \R6, \R7) += \Ai * B even
+    ldr     \T0, [\pB]
+    mul     \T1, \Ai, \T0
+    adds    \R1, \R1, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R2, \R2, \T0
+
+    ldr     \T0, [\pB, #16]
+    mul     \T1, \Ai, \T0
+    adcs    \R3, \R3, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R4, \R4, \T0
+
+    ldr     \T0, [\pB, #32]
+    mul     \T1, \Ai, \T0
+    adcs    \R5, \R5, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R6, \R6, \T0
+
+    adc     \R7, \R7, XZR // \R7 in range [0,2]
+
+    // (\R2, \R3, \R4, \R5, \R6, \R7) += \Ai * B odd
+    ldr     \T0, [\pB, #8]
+    mul     \T1, \Ai, \T0
+    adds    \R2, \R2, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R3, \R3, \T0
+
+    ldr     \T0, [\pB, #24]
+    mul     \T1, \Ai, \T0
+    adcs    \R4, \R4, \T1
+    umulh   \T0, \Ai, \T0
+    adcs    \R5, \R5, \T0
+
+    ldr     \T0, [\pB, #40]
+    mul     \T1, \Ai, \T0
+    adcs    \R6, \R6, \T1
+    umulh   \Ai, \Ai, \T0
+    adc     \Ai, \Ai, XZR // \Ai in range [0, 2^64-1]
+                        // defer adding \Ai to \R7 to avoid overflowing earlier than necessary
+
+    // P384 has a modulus with a form which makes it possible to perform Montgomery reduction
+    // in an interesting way. The modulus is:
+    // 2^384 - 2^128 - 2^96 + 2^32 - 1
+    // This means when we want to add a multiple of the Modulus in reduction, we can actually
+    // avoid multiplication instructions entirely.
+    // On Arm64, multiplication instruction throughput is often the bottleneck, and there is no
+    // distinction in cost between addition and subtraction (unlike on AMD64).
+    // So we do the reduction directly using shifts, adds, and subtracts.
+    // On entry to this macro we have \N3 representing a deferred subtraction (in range [0,2]) from the
+    // 3rd word of the last macro (now aligned to the 2nd word of this macro), on exit we set \N3 to be
+    // the subtraction to defer to the next round.
+    //
+    //            ____ ____ ____ ____ ____ ____
+    //           | M5 | M4 | M3 | M2 | M1 | M0 |
+    //           | ff | ff | ff | fe | f0 | 0f | (P384 modulus if you sign extend each nibble to 32b)
+    //           |____|____|____|____|____|____|
+    //  ____ ____ ____ ____ ____ ____ ____ ____
+    // |    |    |\  /|\  /|\  /|    |    |    |
+    // | \R7 | \R6 | \R5 | \R4 | \R3 | \R2 | \R1 | \R0 |   R0h = \R0 >> 32
+    // |____|____|/__\|/__\|/__\|____|____|____|   R0l = \R0 mod 2^32
+    //                                      | x Inv64 (0x0000000100000001)
+    //                                     _v__
+    //                      ____          |    |
+    //                     |    |         |  \K | \K = (\R0 + (\R0 << 32)) mod 2^64
+    //                     | \N3 |         |____|   = ((R0h +32 R0l) << 32) + R0l
+    //       ____          |____|____                      ^^^ by +32 we mean addition modulo 2^32
+    //      |    |              |    |
+    //    + |  \K |            - |  \K |
+    //      |____|              |____|____
+    //                          |  \K |  \K |
+    //                        - |>>32|<<32|   \K << 32 mod 2^64 = R0l << 32
+    //                          |____|____|   \K >> 32 mod 2^64 = R0h +32 R0l
+    //                               |    |
+    //                             + |  J |   J = (\K*M0 + \R0) >> 64
+    //                           ____|____|
+    //                          |    |
+    //                        - |N3in|
+    //                          |____|
+    //
+    // The logic for how we compute J is a little involved:
+    // (\K * M0) + \R0 = (((R0h +32 R0l) << 32) + R0l) * (1<<32 - 1)                        + \R0
+    //               = (((R0h +32 R0l) << 64) + R0l<<32) - (((R0h +32 R0l) << 32) + R0l)  + R0h<<32 + R0l
+    //               =  ((R0h +32 R0l) << 64) + R0l<<32  -  ((R0h +32 R0l) << 32)         + R0h<<32
+    //               =  ((R0h +32 R0l) << 64) + (((R0h + R0l) - (R0h +32 R0l)) << 32)
+    //                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is exactly bit 33 of R0h+R0l
+    //               =  ((R0h +32 R0l) + ((R0h +33 R0l)>>32)) << 64
+    // => J = (R0h +32 R0l) + ((R0h +33 R0l)>>32)
+
+    lsl     \T0, \R0, #32     // \T0 = \R0<<32 = \K<<32
+    adds     \K, \R0, \T0      //  \K = \R0 + \T0 = \R0 + (\R0<<32) = \R0 * Inv64
+    lsr     \T1, \K, #32      // \T1 = \K>>32
+    adcs    \R1, \R1, \T1      // \R1 = \R1 + J = \R1 + \T1 + carry from setting \K
+    add     \T1, \T1, \N3      // \T1 += N3in; cannot overflow as \T1 < 2^32
+    adcs    \R2, \R2, XZR
+    csetm   \N3, cs          // \N3 -1 if carry, else 0
+
+    subs    \R1, \R1, \T0      // subtract [ \K>>32 + N3in || \K<<32 ]
+    sbcs    \R2, \R2, \T1
+    cinc    \N3, \N3, cc      // \N3 += 1 iff borrow; \N3 in [0, 1] as carry in addition => borrow here
+
+    subs    \R2, \R2, \K       // subtract \K
+    cinc    \N3, \N3, cc      // \N3 += 1 iff borrow; \N3 in [0, 2]
+
+    adds    \R6, \R6, \K       // add \K
+    adcs    \R7, \R7, \Ai
+    cset    \R0, cs
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModMulMontgomeryP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+#define X_16 x19
+#define W_16 w19
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384Asm)
+    stp fp, lr, [sp, #-32]! // allocate 32 bytes of stack; store FP/LR
+    .seh_save_fplr_x 32
+    str      X_16, [sp, #16]
+    .seh_save_reg X_16, 16
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc1
+    // X_2 = pSrc2
+    // X_3 = pDst
+    // X_4 = Ai
+    // X_5, X_6, X_7 = T0, T1, N3
+    // X_8 = K
+    // X_9, X_10, X_11, X_12, X_13, X_14, X_15, X_16 = R0..R7
+
+PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal): .global PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal)
+
+    ldr     X_4, [X_1]              // Load pSrc1
+
+    ldp     X_10, X_11, [X_2]       // Load pSrc2 B[0], B[1]
+    mul      X_9,  X_4, X_10        // R0 = <63:0>   of A[0]*B[0]
+    umulh   X_10,  X_4, X_10        // R1 = <127:64> of A[0]*B[0]
+
+    mul      X_5,  X_4, X_11        // T0 = <63:0>   of A[0]*B[1]
+    adds    X_10, X_10,  X_5        // Adding the previous word
+    umulh   X_11,  X_4, X_11        // R2 = <127:64> of A[0]*B[1]
+
+    ldp     X_12, X_13, [X_2, #16]  // Load pSrc2 B[2], B[3]
+    mul      X_5,  X_4, X_12        // T0 = <63:0>   of A[0]*B[2]
+    adcs    X_11, X_11,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_12,  X_4, X_12        // R3 = <127:64> of A[0]*B[2]
+
+    mul      X_5,  X_4, X_13        // T0 = <63:0>   of A[0]*B[3]
+    adcs    X_12, X_12,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_13,  X_4, X_13        // R4 = <127:64> of A[0]*B[3]
+
+    ldp     X_14, X_15, [X_2, #32]  // Load pSrc2 B[4], B[5]
+    mul      X_5,  X_4, X_14        // T0 = <63:0>   of A[0]*B[4]
+    adcs    X_13, X_13,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_14,  X_4, X_14        // R3 = <127:64> of A[0]*B[4]
+
+    mul      X_5,  X_4, X_15        // T0 = <63:0>   of A[0]*B[5]
+    adcs    X_14, X_14,  X_5        // Adding the previous word (if there was a carry from the last addition it is added)
+    umulh   X_15,  X_4, X_15        // R4 = <127:64> of A[0]*B[5]
+    adc     X_15, X_15,  XZR
+
+    eor     X_16, X_16, X_16
+    eor     X_7, X_7, X_7
+
+    ldr     X_4, [X_1, #8]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_9, X_10, X_11, X_12, X_13, X_14, X_15, X_16
+
+    ldr     X_4, [X_1, #16]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_10, X_11, X_12, X_13, X_14, X_15, X_16, X_9
+
+    ldr     X_4, [X_1, #24]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_11, X_12, X_13, X_14, X_15, X_16, X_9, X_10
+
+    ldr     X_4, [X_1, #32]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_12, X_13, X_14, X_15, X_16, X_9, X_10, X_11
+
+    ldr     X_4, [X_1, #40]
+    MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE X_5, X_6, X_4, X_2, X_8, X_7, X_13, X_14, X_15, X_16, X_9, X_10, X_11, X_12
+
+    // see MUL_AND_MONTGOMERY_REDUCE16_P384_INTERLEAVE for how this reduction works
+    lsl     X_5, X_14, #32  // T0 = R0<<32 = K<<32
+    adds    X_8, X_14, X_5  //  K = R0 + T0 = R0 + (R0<<32)
+    lsr     X_6, X_8, #32   // T1 = K>>32
+    adcs    X_15, X_15, X_6 // R1 = R1 + J = R1 + T1 + carry from setting K
+    add     X_6, X_6, X_7   // T1 += N3in; cannot overflow as T1 < 2^32
+    adcs    X_16, X_16, XZR
+    csetm   X_7, cs         // N3 -1 if carry, else 0
+
+    subs    X_15, X_15, X_5 // subtract K<<32
+    sbcs    X_16, X_16, X_6
+    cinc    X_7, X_7, cc    // N3 += 1 iff borrow; N3 in [0, 1] as carry in addition => borrow here
+
+    subs    X_16, X_16, X_8 // subtract K
+    sbcs    X_9, X_9, X_7   // subtract N3 and borrow if any
+    sbcs    X_10, X_10, XZR
+    sbcs    X_11, X_11, XZR
+    sbcs    X_12, X_12, XZR
+    sbc     X_13, X_13, XZR
+
+    adds    X_12, X_12, X_8 // add K
+    adc     X_13, X_13, XZR
+
+    // Montgomery-reduced multiplication value in (X_15, X_16, X_9, X_10, X_11, X_12, X_13), and it is less than 2*Modulus
+    // Compute value - Mod into (X_4, X_5, X_6, X_7, X_14, X_0), Cy = inverted subtraction borrow
+
+    // "Load" Mod into X4, X5, X6, X0
+    mov     X_4, 0x00000000ffffffff
+    mov     X_5, 0xffffffff00000000
+    mov     X_6, 0xfffffffffffffffe
+    mov     X_0, 0xffffffffffffffff
+
+    subs     X_4, X_15,  X_4
+    sbcs     X_5, X_16,  X_5
+    sbcs     X_6,  X_9,  X_6
+    sbcs     X_7, X_10,  X_0
+    sbcs    X_14, X_11,  X_0
+    sbcs     X_0, X_12,  X_0
+
+    // Choose between the two
+    // addition carry = 1, then subtraction borrow = 1 and we pick the 2nd result.
+    // addition carry = 0 and subtraction borrow = 0: pick 2nd result
+    // addition carry = 0 and subtraction borrow = 1: pick first result
+
+    // We set borrow condition (cc) when subtracting with carry from X_11
+    // iff addition carry 0 and subtraction borrow 1
+    sbcs    X_13, X_13, XZR
+
+    csel    X_15, X_15,  X_4, cc
+    csel    X_16, X_16,  X_5, cc
+    stp     X_15, X_16, [X_3]
+
+    csel     X_9,  X_9,  X_6, cc
+    csel    X_10, X_10,  X_7, cc
+    stp      X_9, X_10, [X_3, #16]
+
+    csel    X_11, X_11, X_14, cc
+    csel    X_12, X_12,  X_0, cc
+    stp     X_11, X_12, [X_3, #32]
+
+    ldr      X_16, [sp, #16]
+    ldp fp, lr, [sp], #32 // deallocate 32 bytes of stack; restore FP/LR
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModSquareMontgomeryP384Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+//    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+// Note we specify 4 arguments to keep prolog of this and SymCryptFdefModMulMontgomeryP384Asm the same
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+#define X_16 x19
+#define W_16 w19
+PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModSquareMontgomeryP384Asm)
+    stp fp, lr, [sp, #-32]! // allocate 32 bytes of stack; store FP/LR
+    .seh_save_fplr_x 32
+    str      X_16, [sp, #16]
+    .seh_save_reg X_16, 16
+    .seh_endprologue
+
+    // X_0 = pMod
+    // X_1 = pSrc
+    // X_2 = pDst
+
+    mov     X_3, X_2
+    mov     X_2, X_1
+
+    // Just jump into multiply, similar to SymCryptWipeAsm jumping into memset.
+
+    b       PLATFORM_NAME_MANGLE(SymCryptFdefModMulMontgomeryP384AsmInternal)
+
+    ldr      X_16, [sp, #16]
+    ldp fp, lr, [sp], #32 // deallocate 32 bytes of stack; restore FP/LR
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+//
+// Register assignments
+//       X_0  = pMod (moving forward one *digit* every inner loop)
+//       X_1  = pSrc (moving forward one *digit* every inner loop)
+//       X_2  = pDst (used only in the end for subtract / result)
+//       X_3  = byte count of pSrc and pMod (sometimes byte count - 1 digit of bytes)
+//       X_4  = word count of pSrc
+//       X_5  = Inv64 of the modulus
+//       X_6  = m = pSrc[i]*Inv64
+//       X_7  = hc = high carry variable
+//       X_8-X_19 = temporaries
+//       X_20 = c
+//       X_21 = Stored byte count of pSrc
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+#define X_14 x16
+#define W_14 w16
+#define X_15 x17
+#define W_15 w17
+#define X_16 x19
+#define W_16 w19
+#define X_17 x20
+#define W_17 w20
+#define X_18 x21
+#define W_18 w21
+#define X_19 x22
+#define W_19 w22
+#define X_20 x25
+#define W_20 w25
+#define X_21 x26
+#define W_21 w26
+PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefMontgomeryReduceAsm)
+    stp fp, lr, [sp, #-64]! // allocate 64 bytes of stack; store FP/LR
+    .seh_save_fplr_x 64
+    stp X_16, X_17, [sp, #16]
+    .seh_save_regp X_16, 16
+    stp X_18, X_19, [sp, #32]
+    .seh_save_regp X_18, 32
+    stp X_20, X_21, [sp, #48]
+    .seh_save_regp X_20, 48
+    .seh_endprologue
+
+    ldr     W_4, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+    add     X_0, X_0, #SymCryptModulusValueOffsetArm64      // pMod
+
+    lsl     X_3, X_4, #5                // Multiply by 32 to get the number of bytes
+    lsl     X_4, X_4, #2                // Multiply by 4 to get the number of words
+
+    sub     X_3, X_3, #32               // offset the byte count to make it easy to restore pointers
+
+    mov     X_7, xzr                    // Set hc to 0
+    mov     X_21, X_3                   // Store the byte count for later
+
+    //
+    // Main loop
+    //
+LABEL(SymCryptFdefMontgomeryReduceAsmOuter)
+    ldp     X_16, X_17, [X_1]           // pSrc[i+0], pSrc[i+1]
+    mul     X_6, X_16, X_5              // <63:0> bits of pSrc[i]*Inv64 = m
+
+    ldp     X_8,  X_9,  [X_0]           // pMod[0], pMod[1]
+    umulh   X_8,  X_6, X_8              // Bits <127:64> of m*pMod[0]
+    subs    xzr,  X_16, #1              // Subtract 1 from pSrc[i+0] (generate a carry iff pSrc[i] is non-zero)
+
+    mul     X_13, X_6, X_9              // Bits <63:0> of m*pMod[1]
+    umulh   X_9,  X_6, X_9              // Bits <127:64> of m*pMod[1]
+    adcs    X_8,  X_8,  X_13            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_10, X_11, [X_0, #16]      // pMod[2], pMod[3]
+    mul     X_14, X_6, X_10             // Bits <63:0> of m*pMod[2]
+    umulh   X_10, X_6, X_10             // Bits <127:64> of m*pMod[2]
+    adcs    X_9,  X_9,  X_14            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_18, X_19, [X_1, #16]      // pSrc[i+2], pSrc[i+3]
+    mul     X_15, X_6, X_11             // Bits <63:0> of m*pMod[3]
+    umulh   X_11, X_6, X_11             // Bits <127:64> of m*pMod[3]
+    adcs    X_10, X_10, X_15            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    adc     X_20, X_11, xzr             // Add the carry if any
+
+    // ( X_20, X_10, X_9, X_8, ___ ) = (( pMod[3], pMod[2], pMod[1], pMod[0] ) * m)
+    // We do not compute this --^ because (m*pMod[0] + pSrc[i]) == 0. We already accounted for the carry above.
+
+    adds    X_17, X_17, X_8             // Adding pSrc[i+1]
+    adcs    X_18, X_18, X_9             // Adding pSrc[i+2]
+    adcs    X_19, X_19, X_10            // Adding pSrc[i+3]
+
+    stp     xzr,  X_17, [X_1]           // pSrc[i+0], pSrc[i+1]
+    stp     X_18, X_19, [X_1, #16]      // pSrc[i+2], pSrc[i+3]
+
+    cbz    X_3, SymCryptFdefMontgomeryReduceAsmInnerEnd
+
+LABEL(SymCryptFdefMontgomeryReduceAsmInner)
+    // Invariant - X_20 (c) + carry flag is a value in the range [0, 2^64-1]
+    // We could always adc X_20, X_20, xzr before loop entry, but this is a wasted instruction
+    // vs. adcs with X_20 below
+
+    ldp     X_8,  X_9,  [X_0, #32]!     // pMod[j+0], pMod[j+1]
+    mul     X_12, X_6, X_8              // Bits <63:0> of m*pMod[j]
+    umulh   X_8,  X_6, X_8              // Bits <127:64> of m*pMod[j]
+    adcs    X_12, X_20, X_12            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    mul     X_13, X_6, X_9              // Bits <63:0> of m*pMod[j+1]
+    umulh   X_9,  X_6, X_9              // Bits <127:64> of m*pMod[j+1]
+    ldp     X_16, X_17, [X_1, #32]!     // pSrc[i+j+0], pSrc[i+j+1]
+    adcs    X_8,  X_8,  X_13            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_10, X_11, [X_0, #16]      // pMod[j+2], pMod[j+3]
+    mul     X_14, X_6, X_10             // Bits <63:0> of m*pMod[j+2]
+    umulh   X_10, X_6, X_10             // Bits <127:64> of m*pMod[j+2]
+    adcs    X_9,  X_9,  X_14            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    ldp     X_18, X_19, [X_1, #16]      // pSrc[i+j+2], pSrc[i+j+3]
+    mul     X_15, X_6, X_11             // Bits <63:0> of m*pMod[j+3]
+    umulh   X_11, X_6, X_11             // Bits <127:64> of m*pMod[j+3]
+    adcs    X_10, X_10, X_15            // Adding the previous word (if there was a carry from the last addition it is added)
+
+    adc     X_20, X_11, xzr             // Add the carry if any
+
+    // ( X_20, X_10, X_9, X_8, X_12 ) = (( pMod[j+3], pMod[j+2], pMod[j+1], pMod[j+0] ) * m) + c
+
+    adds    X_16, X_16, X_12            // Adding pSrc[i+j+0]
+    adcs    X_17, X_17, X_8             // Adding pSrc[i+j+1]
+    adcs    X_18, X_18, X_9             // Adding pSrc[i+j+2]
+    adcs    X_19, X_19, X_10            // Adding pSrc[i+j+3]
+
+    stp     X_16, X_17, [X_1]           // pSrc[i+j+0], pSrc[i+j+1]
+    stp     X_18, X_19, [X_1, #16]      // pSrc[i+j+2], pSrc[i+j+3]
+
+    sub     X_3, X_3, #32               // Move one digit up
+    cbnz    X_3, SymCryptFdefMontgomeryReduceAsmInner
+
+LABEL(SymCryptFdefMontgomeryReduceAsmInnerEnd)
+
+    ldr     X_8, [X_1, #32]             // pSrc[nWords]
+    adcs    X_20, X_20, X_7             // c + hc
+    adc     X_7, xzr, xzr               // Add the carry if any
+
+    adds    X_20, X_20, X_8             // c + hc + pSrc[nWords]
+    adc     X_7, X_7, xzr               // Add the carry if any and store into hc
+
+    str     X_20, [X_1, #32]            // pSrc[nWords] = c
+
+    subs    X_4, X_4, #1                // Move one word up
+
+    sub     X_0, X_0, X_21              // Restore pMod pointer (subtract byte count)
+    sub     X_1, X_1, X_21              // Restore pSrc pointer (subtract byte count)
+    add     X_1, X_1, #8                // Move pSrc pointer one word up
+
+    mov     X_3, X_21                   // Restore the byte counter
+
+    bne     SymCryptFdefMontgomeryReduceAsmOuter
+
+    //
+    // Subtraction
+    //
+    add     X_3, X_3, #32           // restore the full byte count for loops using unconditional pre-indexing
+    mov     X_21, X_3               // Store the byte count for later
+
+    sub     X_0, X_0, #32           // offset pMod so we can use pre-increment form of loads
+    sub     X_1, X_1, #32           // offset pSrc so we can use pre-increment form of loads
+    sub     X_2, X_2, #32           // offset pDst so we can use pre-increment form of loads
+    mov     X_14, X_2               // Store pDst pointer
+
+    // Prepare the pointers for subtract
+    mov     X_10, X_7               // X_10 = hc
+    subs    X_4, X_4, X_4           // Set the carry flag (i.e. no borrow)
+
+LABEL(SymCryptFdefMontgomeryReduceRawSubAsmLoop)
+    sub     X_3, X_3, #32           // Decrement the byte count by 32
+    // borrow is in the carry flag (flipped)
+
+    ldp     X_4, X_6, [X_1, #32]!   // Load two words of pSrc
+    ldp     X_5, X_7, [X_0, #32]!   // Load two words of pMod
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #32]!   // Store the result in the destination
+
+    ldp     X_4, X_6, [X_1, #16]    // Load two words of pSrc
+    ldp     X_5, X_7, [X_0, #16]    // Load two words of pMod
+    sbcs    X_4, X_4, X_5
+    sbcs    X_6, X_6, X_7
+    stp     X_4, X_6, [X_2, #16]    // Store the result in the destination
+
+    cbnz    X_3, SymCryptFdefMontgomeryReduceRawSubAsmLoop
+
+    cset    X_0, cc                 // If the carry is clear (borrow), set the return value to 1
+
+    orr     X_11, X_10, X_0         // X_11 = hc|d
+
+    // Prepare the pointers for masked copy
+    sub     X_0, X_1, X_21          // Restore pSrc pointer (subtract byte count)
+    mov     X_1, X_14               // pDst
+
+    mov     X_2, X_21               // Restore the byte counter
+    subs    X_4, X_10, X_11         // If (X_11 > X_10) clear the carry flag (i.e. borrow)
+
+LABEL(SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop)
+    sub     X_2, X_2, #32           // decrement the byte count by 32
+
+    ldp     X_4, X_6, [X_0, #32]!   // Load two words of the source
+    ldp     X_5, X_7, [X_1, #32]!   // Load two words of the destination
+    csel    X_4, X_4, X_5, cc       // If the carry is clear, select the source operands
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1]         // Store the two words in the destination
+
+    ldp     X_4, X_6, [X_0, #16]
+    ldp     X_5, X_7, [X_1, #16]
+    csel    X_4, X_4, X_5, cc
+    csel    X_6, X_6, X_7, cc
+    stp     X_4, X_6, [X_1, #16]
+
+    cbnz    X_2, SymCryptFdefMontgomeryReduceMaskedCopyAsmLoop
+
+    // Done, no return value
+
+    ldp X_20, X_21, [sp, #48]
+    ldp X_18, X_19, [sp, #32]
+    ldp X_16, X_17, [sp, #16]
+    ldp fp, lr, [sp], #64 // deallocate 64 bytes of stack; restore FP/LR
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+#undef X_14
+#undef W_14
+#undef X_15
+#undef W_15
+#undef X_16
+#undef W_16
+#undef X_17
+#undef W_17
+#undef X_18
+#undef W_18
+#undef X_19
+#undef W_19
+#undef X_20
+#undef W_20
+#undef X_21
+#undef W_21
+
+.macro MULADD_LOADSTORE18  pS, pM, pD, K, Tc, T0, T1, T2, T3, T4, T5
+    // \Tc:D[3:0] = (M[3:0] * \K) + S[3:0] + \Tc
+    //
+    // QH = \K
+    // \T0..\T5 = scratch
+    // \Tc is carry in at start and carry out at end (in [0,2^64-1])
+
+    // \Tc:T[5:2] = S[3:0] + (\K * M odd) + \Tc
+    ldp     \T2, \T3, [\pS]        // load S[0], S[1]
+    adds    \T2, \T2, \Tc
+
+    ldr     \T0, [\pM, #8]
+    mul     \T1,  \K, \T0
+    adcs    \T3, \T3, \T1
+    umulh   \T0,  \K, \T0
+    ldp     \T4, \T5, [\pS, #16]   // load S[2], S[3]
+    adcs    \T4, \T4, \T0
+
+    ldr     \T0, [\pM, #24]
+    mul     \T1,  \K, \T0
+    adcs    \T5, \T5, \T1
+    umulh   \Tc,  \K, \T0
+    adc     \Tc, \Tc, XZR
+
+    // \Tc:D[3:0] = \Tc:T[5:2] + (\K * M even)
+    ldr     \T0, [\pM]
+    mul     \T1,  \K, \T0
+    adds    \T2, \T2, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \T3, \T3, \T0
+    stp     \T2, \T3, [\pD]        // store D[0], D[1]
+
+    ldr     \T0, [\pM, #16]
+    mul     \T1,  \K, \T0
+    adcs    \T4, \T4, \T1
+    umulh   \T0,  \K, \T0
+    adcs    \T5, \T5, \T0
+    stp     \T4, \T5, [\pD, #16]   // store D[2], D[3]
+
+    adc     \Tc, \Tc, XZR
+.endm
+
+.macro SHIFTRIGHT2  pD, index, shrVal, shrMask, shlVal, Tcin, Tcout, T0, T1
+    // \T0:\Tcout = D[\index + 1:\index]
+    // D[\index + 1] = \Tcin:\T0 >> \shrVal (== ((\T0>>\shrVal)&\shrMask) | (\Tcin<<\shlVal))
+    // D[\index] = \T0:\Tcout >> \shrVal
+    //
+    // \shrVal in [1,64]
+    // \shrMask = (\shrVal == 64) ? 0 : -1
+    // \shlVal = 64-\shrVal
+    //
+    // \T0, \T1 = scratch
+    // Value of \Tcin is bits to shift in the top at start
+    // Value of \Tcout is bits to shift into next macro invocation at end
+
+    ldp     \Tcout, \T0, [\pD, #(\index*8)]
+
+    lsr     \T1, \T0, \shrVal
+    lsl     \Tcin, \Tcin, \shlVal
+    and     \T1, \T1, \shrMask
+    orr     \T1, \T1, \Tcin
+
+    lsr     \Tcin, \Tcout, \shrVal
+    lsl     \T0, \T0, \shlVal
+    and     \Tcin, \Tcin, \shrMask
+    orr     \Tcin, \Tcin, \T0
+
+    stp     \Tcin, \T1, [\pD, #(\index*8)]
+.endm
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefModDivSmallPow2Asm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+//    _In_range_(1, 64)               UINT32                  exp,
+//    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+#define X_3 x3
+#define W_3 w3
+#define X_4 x4
+#define W_4 w4
+#define X_5 x5
+#define W_5 w5
+#define X_6 x6
+#define W_6 w6
+#define X_7 x7
+#define W_7 w7
+#define X_8 x8
+#define W_8 w8
+#define X_9 x9
+#define W_9 w9
+#define X_10 x10
+#define W_10 w10
+#define X_11 x11
+#define W_11 w11
+#define X_12 x12
+#define W_12 w12
+#define X_13 x15
+#define W_13 w15
+PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm):
+.globl PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm)
+.def PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptFdefModDivSmallPow2Asm)
+    .seh_endprologue
+    ldr     X_7, [X_1]
+    ldr     X_5, [X_0, #SymCryptModulusInv64OffsetArm64]    // Inv64 of modulus
+
+    mul     X_7, X_7, X_5   // <63:0> bits of pSrc[0]*Inv64
+                            // this is the multiple of the modulus that must be added to peSrc
+                            // s.t. the least significant **64** bits are all zero
+
+    // Form bitmask to mask out bottom exp bits from multiplication result
+    mov     X_6, #-1
+    neg     X_5, X_2
+    lsr     X_6, X_6, X_5   // mask = -1 >> (-exp % 64)
+                            //      = 0xffffffffffffffff >> (64-exp)
+                            //      = (((UINT65)1)<<exp)-1
+
+    and     X_7, X_7, X_6   // Extract the least significant exp bits from X_7 to produce K
+                            // K is the minimal multiple of the modulus that when added to peSrc
+                            // sets the least significant **exp** bits to zero
+
+    ldr     W_4, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits, used directly as loop counter
+    add     X_5, X_0, #SymCryptModulusValueOffsetArm64      // pointer to modulus value
+
+    eor     X_6, X_6, X_6   // zero one word carry
+
+LABEL(SymCryptFdefModDivSmallPow2AsmMulAddLoop)
+    // Set peDst = peSrc + (K * mod)
+    // Also has up to 64b of carry out the top which is stored in Q7 at the end of the loop
+    //
+    // W_4  loop ctr (one iteration per digit)
+    // X_7  K
+    // X_1  running pointer peSrc inner loop (we directly modify X_1 as we only need to read peSrc once)
+    // X_5  running pointer pMod inner loop
+    // X_3  running pointer peDst inner loop (we directly modify X_3 as we read/write peDst in reverse in second loop)
+    // X_6  one word carry
+    // X_8..X_13 scratch
+
+    MULADD_LOADSTORE18 X_1, X_5, X_3, X_7, X_6, X_8, X_9, X10, X_11, X_12, X_13
+
+    // Move on to the next digit
+    add     X_1, X_1, #32
+    add     X_5, X_5, #32
+    add     X_3, X_3, #32
+
+    sub     W_4, W_4, #1
+    cbnz    W_4, SymCryptFdefModDivSmallPow2AsmMulAddLoop
+
+    ldr     W_0, [X_0, #SymCryptModulusNdigitsOffsetArm64]  // # of Digits, used directly as loop counter
+    mov     X_1, #64
+    subs    X_1, X_1, X_2   // shlVal = 64-exp
+    csetm   X_4, ne         // shrMask = ((exp != 64) ? -1 : 0) = ((exp == 64) ? 0 : -1)
+
+    sub     X_3, X_3, #32  // X_3 points to the most significant digit of peDst
+
+LABEL(SymCryptFdefModDivSmallPow2AsmShiftRightLoop)
+    // Set peDst = peDst >> exp
+    // We work backwards (from MSB to LSB) so we avoid having to do special handling of the one word
+    // carry out of the top of the multiply add loop
+    //
+    // W_0  loop ctr (one iteration per digit)
+    // X_1  shlVal
+    // X_4  shrMask
+    // X_2  shrVal
+    // X_3  running pointer peDst inner loop (decreasing)
+    // X_6  one word carry even
+    // X_7  one word carry odd
+    // X_8, X_9 scratch
+
+    SHIFTRIGHT2 X_3, 2, X_2, X_4, X_1, X_6, X_7, X_8, X_9
+    SHIFTRIGHT2 X_3, 0, X_2, X_4, X_1, X_7, X_6, X_8, X_9
+
+    sub     X_3, X_3, #32
+
+    sub     W_0, W_0, #1
+    cbnz    W_0, SymCryptFdefModDivSmallPow2AsmShiftRightLoop
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+#undef X_3
+#undef W_3
+#undef X_4
+#undef W_4
+#undef X_5
+#undef W_5
+#undef X_6
+#undef W_6
+#undef X_7
+#undef W_7
+#undef X_8
+#undef W_8
+#undef X_9
+#undef W_9
+#undef X_10
+#undef W_10
+#undef X_11
+#undef W_11
+#undef X_12
+#undef W_12
+#undef X_13
+#undef W_13
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/arm64ec/wipe.S b/libs/symcrypt/lib/arm64ec/wipe.S
new file mode 100644
index 000000000000..a14c9db89ee8
--- /dev/null
+++ b/libs/symcrypt/lib/arm64ec/wipe.S
@@ -0,0 +1,48 @@
+//
+//  wipe.symcryptasm   Assembler code for wiping a buffer
+//  Expresses asm in a generic enough way to enable generation of MASM and GAS using the
+//  symcryptasm_processor.py script and C preprocessor
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+
+
+#include "symcryptasm_shared.cppasm"
+
+    TEXTAREA()
+
+    EXTERN(PLATFORM_NAME_MANGLE(memset))
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptWipe( _Out_writes_bytes_( cbData )    PVOID  pbData,
+//                                              SIZE_T cbData )
+
+#define X_0 x0
+#define W_0 w0
+#define X_1 x1
+#define W_1 w1
+#define X_2 x2
+#define W_2 w2
+PLATFORM_NAME_MANGLE(SymCryptWipeAsm):
+.globl PLATFORM_NAME_MANGLE(SymCryptWipeAsm)
+.def PLATFORM_NAME_MANGLE(SymCryptWipeAsm); .scl 2; .type 32; .endef
+.seh_proc PLATFORM_NAME_MANGLE(SymCryptWipeAsm)
+    .seh_endprologue
+
+// we just jump to memset.
+// this is enough to stop the compiler optimizing the memset away.
+
+    mov     X_2, X_1
+    mov     X_1, #0
+    b       PLATFORM_NAME_MANGLE(memset)
+
+    ret
+.seh_endproc
+#undef X_0
+#undef W_0
+#undef X_1
+#undef W_1
+#undef X_2
+#undef W_2
+
+    FILE_END()
diff --git a/libs/symcrypt/lib/blockciphermodes.c b/libs/symcrypt/lib/blockciphermodes.c
new file mode 100644
index 000000000000..54fe294bb990
--- /dev/null
+++ b/libs/symcrypt/lib/blockciphermodes.c
@@ -0,0 +1,470 @@
+//
+// BlockCipherModes.c   generic implementation of all block cipher modes
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcbEncrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SIZE_T i;
+    SIZE_T cbToDo = cbData & ~(pBlockCipher->blockSize - 1);
+
+    if( pBlockCipher->ecbEncryptFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pBlockCipher->ecbEncryptFunc)( pExpandedKey, pbSrc, pbDst, cbData );
+        return;
+    }
+
+    //
+    // To avoid buffer overruns we truncate the work to an integral number of blocks.
+    //
+
+    for( i=0; i<cbToDo; i+= pBlockCipher->blockSize )
+    {
+        (*pBlockCipher->encryptFunc)( pExpandedKey, pbSrc + i, pbDst + i );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcbDecrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _In_reads_( cbData )       PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SIZE_T i;
+    SIZE_T cbToDo = cbData & ~(pBlockCipher->blockSize - 1);
+
+    if( pBlockCipher->ecbDecryptFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pBlockCipher->ecbDecryptFunc)( pExpandedKey, pbSrc, pbDst, cbData );
+        return;
+    }
+
+    for( i=0; i<cbToDo; i+= pBlockCipher->blockSize )
+    {
+        (*pBlockCipher->decryptFunc)( pExpandedKey, pbSrc + i, pbDst + i );
+    }
+}
+
+
+//
+// SymCryptCbcEncrypt
+//
+// Generic CBC encryption routine for block ciphers.
+// The following restrictions must be obeyed:
+//  - blockSize <= 32 and must be a power of 2
+//  - cbData must be a multiple of the block size
+//
+VOID
+SYMCRYPT_CALL
+SymCryptCbcEncrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SYMCRYPT_ALIGN BYTE    buf[SYMCRYPT_MAX_BLOCK_SIZE];
+    SIZE_T blockSize;
+    PCBYTE pbSrcEnd;
+    PCBYTE pSrc = pbSrc;
+    PBYTE  pDst = pbDst;
+
+    if( pBlockCipher->cbcEncryptFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pBlockCipher->cbcEncryptFunc)( pExpandedKey, pbChainingValue, pSrc, pDst, cbData );
+        return;
+    }
+
+    blockSize = pBlockCipher->blockSize;
+
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+
+    //
+    // Compute the end of the data, rounding the size down to a multiple of the block size.
+    //
+    pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];
+
+    //
+    // We keep the chaining state in a local buffer to enforce the read-once write-once rule.
+    //
+    memcpy( buf, pbChainingValue, blockSize );
+    while( pSrc < pbSrcEnd )
+    {
+        SYMCRYPT_ASSERT( pSrc <= pbSrc + cbData - blockSize );  // help PreFast
+        SYMCRYPT_ASSERT( pDst <= pbDst + cbData - blockSize );  // help PreFast
+        SYMCRYPT_ASSERT( blockSize <= cbData );                 // help PreFast
+        SymCryptXorBytes( pSrc, buf, buf, blockSize );
+        (*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );
+        memcpy( pDst, buf, blockSize );
+        pSrc += blockSize;
+        pDst += blockSize;
+    }
+
+    memcpy( pbChainingValue, buf, blockSize );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ));
+}
+
+//
+// SymCryptCbcDecrypt
+//
+// Generic CBC decryption routine for block ciphers.
+// The following restrictions must be obeyed:
+//  - blockSize <= 32 and must be a power of 2
+//  - cbData must be a multiple of the block size
+//
+VOID
+SYMCRYPT_CALL
+SymCryptCbcDecrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SYMCRYPT_ALIGN BYTE buf[3 * SYMCRYPT_MAX_BLOCK_SIZE];
+    PBYTE chain = &buf[0];
+    PBYTE ciphertext = &buf[SYMCRYPT_MAX_BLOCK_SIZE];
+    PBYTE tmp = &buf[2*SYMCRYPT_MAX_BLOCK_SIZE];
+
+    SIZE_T blockSize;
+    PCBYTE pbSrcEnd;
+
+    if( pBlockCipher->cbcDecryptFunc != NULL )
+    {
+        (*pBlockCipher->cbcDecryptFunc)( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+        return;
+    }
+
+    blockSize = pBlockCipher->blockSize;
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+    //
+    // Compute the end of the data, rounding the size down to a multiple of the block size.
+    //
+    pbSrcEnd = &pbSrc[ cbData & ~(blockSize-1) ];
+
+#pragma warning(suppress: 22105)
+    memcpy( chain, pbChainingValue, blockSize );
+
+    //
+    // Loop structured to obey the read-once/write-once rule
+    //
+    while( pbSrc < pbSrcEnd )
+    {
+        SYMCRYPT_ASSERT( pbSrc <= pbSrcEnd - blockSize );   // help PreFast
+        memcpy( ciphertext, pbSrc, blockSize );
+        (*pBlockCipher->decryptFunc)( pExpandedKey, ciphertext, tmp );
+        SymCryptXorBytes( tmp, chain, pbDst, blockSize );
+        memcpy( chain, ciphertext, blockSize );
+        pbDst += blockSize;
+        pbSrc += blockSize;
+    }
+
+    memcpy( pbChainingValue, chain, blockSize );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ));
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptCbcMac(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+                                SIZE_T                  cbData )
+{
+    SYMCRYPT_ALIGN BYTE    buf[32];
+    SIZE_T blockSize;
+    PCBYTE pbSrcEnd;
+    PCBYTE p;
+
+    if( pBlockCipher->cbcMacFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pBlockCipher->cbcMacFunc)( pExpandedKey, pbChainingValue, pbSrc, cbData );
+        return;
+    }
+
+    blockSize = pBlockCipher->blockSize;
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+    //
+    // Compute the end of the data, rounding the size down to a multiple of the block size.
+    //
+    pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];
+
+    //
+    // We keep the chaining state in a local buffer to enforce the read-once write-once rule.
+    // It also improves memory locality.
+    //
+    memcpy( buf, pbChainingValue, blockSize );
+    p = pbSrc;
+    while( p < pbSrcEnd )
+    {
+        SYMCRYPT_ASSERT( p <= pbSrc + cbData - blockSize );
+        SymCryptXorBytes( p, buf, buf, blockSize );
+        (*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );
+        p += blockSize;
+    }
+
+    memcpy( pbChainingValue, buf, blockSize );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ));
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptCtrMsb32(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SYMCRYPT_ALIGN BYTE buf[2 * SYMCRYPT_MAX_BLOCK_SIZE];
+    PBYTE count = &buf[0];
+    PBYTE keystream= &buf[SYMCRYPT_MAX_BLOCK_SIZE];
+    SIZE_T blockSize;
+    PCBYTE pbSrcEnd;
+
+    blockSize = pBlockCipher->blockSize;
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+    //
+    // Compute the end of the data, rounding the size down to a multiple of the block size.
+    //
+    pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];
+
+    //
+    // We keep the chaining state in a local buffer to enforce the read-once write-once rule.
+    // It also improves memory locality.
+    //
+    #pragma warning(suppress: 22105)
+    memcpy( count, pbChainingValue, blockSize );
+    while( pbSrc < pbSrcEnd )
+    {
+        SYMCRYPT_ASSERT( pbSrc <= pbSrcEnd - blockSize );   // help PreFast
+        (*pBlockCipher->encryptFunc)( pExpandedKey, count, keystream );
+        SymCryptXorBytes( keystream, pbSrc, pbDst, blockSize );
+
+        //
+        // We only need to increment the last 32 bits of the counter value.
+        //
+        SYMCRYPT_STORE_MSBFIRST32( &count[ blockSize-4 ], 1 + SYMCRYPT_LOAD_MSBFIRST32( &count[ blockSize-4 ] ) );
+
+        pbSrc += blockSize;
+        pbDst += blockSize;
+    }
+
+    memcpy( pbChainingValue, count, blockSize );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ));
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptCtrMsb64(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SYMCRYPT_ALIGN BYTE    buf[2 * SYMCRYPT_MAX_BLOCK_SIZE];
+    PBYTE   count = &buf[0];
+    PBYTE   keystream= &buf[SYMCRYPT_MAX_BLOCK_SIZE];
+    SIZE_T blockSize;
+    PCBYTE pbSrcEnd;
+
+    if( pBlockCipher->ctrMsb64Func != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pBlockCipher->ctrMsb64Func)( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );
+        return;
+    }
+
+    blockSize = pBlockCipher->blockSize;
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+    //
+    // Compute the end of the data, rounding the size down to a multiple of the block size.
+    //
+    pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];
+
+    //
+    // We keep the chaining state in a local buffer to enforce the read-once write-once rule.
+    // It also improves memory locality.
+    //
+    #pragma warning(suppress: 22105)
+    memcpy( count, pbChainingValue, blockSize );
+    while( pbSrc < pbSrcEnd )
+    {
+        SYMCRYPT_ASSERT( pbSrc <= pbSrcEnd - blockSize );   // help PreFast
+        (*pBlockCipher->encryptFunc)( pExpandedKey, count, keystream );
+        SymCryptXorBytes( keystream, pbSrc, pbDst, blockSize );
+
+        //
+        // We only need to increment the last 64 bits of the counter value.
+        //
+        SYMCRYPT_STORE_MSBFIRST64( &count[ blockSize-8 ], 1 + SYMCRYPT_LOAD_MSBFIRST64( &count[ blockSize-8 ] ) );
+
+        pbSrc += blockSize;
+        pbDst += blockSize;
+    }
+
+    memcpy( pbChainingValue, count, blockSize );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ));
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptCfbEncrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+                                SIZE_T                  cbShift,
+    _In_                        PCVOID                  pExpandedKey,
+        _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+//
+// Encrypt a buffer using the CFB cipher mode.
+//
+// This implements the CFB mode using a 1-byte feedback shift.
+// This requires a block cipher encryption call for each byte, which is very slow.
+// Use of this cipher mode is not recommended.
+//
+// - pBlockCipher is a pointer to the block cipher description table.
+//      Suitable description tables for all ciphers in this library have been pre-defined.
+// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there
+//      is no type safety to ensure that the expanded key and the encryption function match.
+// - pbChainingValue points to the chaining value. On entry and exit it
+//      contains the last blockSize ciphertext bytes.
+// - pbSrc is the input data buffer that will be encrypted/decrypted.
+// - cbData. Number of bytes to encrypt/decrypt. This must be a multiple of the block size.
+// - pbDst is the output buffer that receives the encrypted/decrypted data. The input and output
+//      buffers may be the same or non-overlapping, but may not partially overlap.
+//
+{
+    SYMCRYPT_ALIGN BYTE buf[2*SYMCRYPT_MAX_BLOCK_SIZE];
+    PBYTE   chain = &buf[0];
+    PBYTE   tmp   = &buf[SYMCRYPT_MAX_BLOCK_SIZE];
+    SIZE_T  blockSize;
+
+    blockSize = pBlockCipher->blockSize;
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+    // Force cbShift to either be 1 or blockSize
+    if(cbShift != 1)
+    {
+        cbShift = blockSize;
+    }
+
+    memcpy( chain, pbChainingValue, blockSize );
+    while( cbData >= cbShift )
+    {
+        (*pBlockCipher->encryptFunc)( pExpandedKey, chain, tmp );
+        SymCryptXorBytes( pbSrc, tmp, tmp, cbShift );           // tmp[0..cbShift-1] ^= pbSrc[0..cbShift-1]
+        memcpy( pbDst, tmp, cbShift );
+
+        memmove( chain, chain + cbShift, blockSize - cbShift );
+        memcpy( chain + blockSize - cbShift, tmp, cbShift );
+
+        pbDst += cbShift;
+        pbSrc += cbShift;
+        cbData -= cbShift;
+    }
+
+    memcpy( pbChainingValue, chain, blockSize );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCfbDecrypt(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+                                SIZE_T                  cbShift,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData )
+{
+    SYMCRYPT_ALIGN BYTE buf[2*SYMCRYPT_MAX_BLOCK_SIZE];
+    PBYTE   chain = &buf[0];
+    PBYTE   tmp   = &buf[SYMCRYPT_MAX_BLOCK_SIZE];
+    SIZE_T  blockSize;
+
+    blockSize = pBlockCipher->blockSize;
+    SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );
+
+    // Force cbShift to either be 1 or blockSize
+    if(cbShift != 1)
+    {
+        cbShift = blockSize;
+    }
+
+    memcpy( chain, pbChainingValue, blockSize );
+    while( cbData >= cbShift )
+    {
+        (*pBlockCipher->encryptFunc)( pExpandedKey, chain, tmp );
+
+        //
+        // First we update the chain block
+        //
+
+        memmove( chain, chain + cbShift, blockSize - cbShift );
+        memcpy( chain + blockSize - cbShift, pbSrc, cbShift );
+
+        //
+        // To obey the read-once rule, we take the ciphertext from the updated chain block.
+        //
+        SymCryptXorBytes( chain + blockSize - cbShift, tmp, pbDst, cbShift );
+
+        pbDst += cbShift;
+        pbSrc += cbShift;
+        cbData -= cbShift;
+    }
+
+    memcpy( pbChainingValue, chain, blockSize );
+}
diff --git a/libs/symcrypt/lib/ccm.c b/libs/symcrypt/lib/ccm.c
new file mode 100644
index 000000000000..912f657d10e0
--- /dev/null
+++ b/libs/symcrypt/lib/ccm.c
@@ -0,0 +1,634 @@
+//
+// CCM.c    implementation of the CCM block cipher mode
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define CCM_MIN_NONCE_SIZE  (7)
+#define CCM_MAX_NONCE_SIZE  (13)
+#define CCM_MIN_TAG_SIZE    (4)
+#define CCM_MAX_TAG_SIZE    (16)
+
+#define CCM_MAX_COUNTER_SIZE    (SYMCRYPT_CCM_BLOCK_SIZE - 1 - CCM_MIN_NONCE_SIZE)
+
+#define AUTHDATA_16BIT_LIMIT    ((1<<16) - (1<<8))
+#define AUTHDATA_32BIT_LIMIT    (1ull << 32)
+
+// Compile time BOOL statically determines if we need to check cbAuthData < AUTHDATA_32BIT_LIMIT
+// Used to suppress MSVC C4127 and clang Wtautological-constant-out-of-range-compare on 32b platforms
+const BOOL fcbAuthDataLt32bitLimitStatic = SIZE_T_MAX < AUTHDATA_32BIT_LIMIT;
+
+#define CCM_BLOCK_MOD_MASK      (SYMCRYPT_CCM_BLOCK_SIZE - 1)
+#define CCM_BLOCK_ROUND_MASK    (~CCM_BLOCK_MOD_MASK)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCcmValidateParameters(
+    _In_    PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_    SIZE_T                  cbNonce,
+    _In_    SIZE_T                  cbAssociatedData,
+    _In_    UINT64                  cbData,
+    _In_    SIZE_T                  cbTag
+   )
+{
+    SIZE_T cbCounter;
+
+    UNREFERENCED_PARAMETER( cbAssociatedData );
+
+    if( pBlockCipher->blockSize != SYMCRYPT_CCM_BLOCK_SIZE )
+    {
+        return SYMCRYPT_WRONG_BLOCK_SIZE;
+    }
+
+    //
+    // Test against limits in SP800-38C appendix A
+    //
+    if( cbNonce < CCM_MIN_NONCE_SIZE || cbNonce > CCM_MAX_NONCE_SIZE )
+    {
+        return SYMCRYPT_WRONG_NONCE_SIZE;
+    }
+
+    //
+    // cbAssociatedData is limited to <2^64
+    // We don't test for this. None of our platforms has a SIZE_T that is
+    // large enough to violate this condition. And the test
+    // is of a form that the compiler cannot optimize away.
+    //
+
+    //
+    // The counter block consists of a single flag byte, the nonce, and the counter field.
+    //
+    cbCounter = SYMCRYPT_CCM_BLOCK_SIZE - cbNonce - 1;
+
+    //
+    // per SP800-38C cbData is limited to 2^{8*cbCounter}
+    // There is no way to do this test in a single comparison.
+    // We don't have to worry about side-channels in the && because
+    // cbCounter depends only on the length of the nonce, and we do not
+    // try to hide any lengths.
+    //
+    if( cbCounter < sizeof( UINT64 ) &&
+        cbData >= ((UINT64)1 << (8*cbCounter)) )
+    {
+        return SYMCRYPT_WRONG_DATA_SIZE;
+    }
+
+    if( cbTag < CCM_MIN_TAG_SIZE ||
+        cbTag > CCM_MAX_TAG_SIZE ||
+        (cbTag & 1) == 1        // valid tag lengths are [4, 6, 8, ..., 16]
+        )
+    {
+        return SYMCRYPT_WRONG_TAG_SIZE;
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncryptDecryptPart(
+    _Inout_                     PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbData )        PCBYTE              pbSrc,
+    _Out_writes_( cbData )      PBYTE               pbDst,
+                                SIZE_T              cbData )
+
+{
+    SIZE_T  cbToDo = cbData;
+    SIZE_T  bytesToProcess;
+
+    //
+    // Use any left-over key stream
+    //
+    while( (pState->bytesProcessed & CCM_BLOCK_MOD_MASK) != 0 && cbToDo > 0 )
+    {
+        *pbDst = *pbSrc ^ pState->keystreamBlock[ pState->bytesProcessed & CCM_BLOCK_MOD_MASK ];
+        pbDst++;
+        pbSrc++;
+        cbToDo--;
+        pState->bytesProcessed++;
+    }
+
+    //
+    // Bulk process the main part of the input and output
+    //
+    if( cbToDo >= SYMCRYPT_CCM_BLOCK_SIZE )
+    {
+        bytesToProcess = cbToDo & CCM_BLOCK_ROUND_MASK;
+        SYMCRYPT_ASSERT( bytesToProcess <= cbToDo );
+
+        SYMCRYPT_ASSERT( pState->pBlockCipher->blockSize == SYMCRYPT_CCM_BLOCK_SIZE );
+        SymCryptCtrMsb64(   pState->pBlockCipher,
+                            pState->pExpandedKey,
+                            &pState->counterBlock[0],
+                            pbSrc,
+                            pbDst,
+                            bytesToProcess );
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        pState->bytesProcessed += bytesToProcess;
+        cbToDo -= bytesToProcess;
+    }
+
+    if( cbToDo > 0 )
+    {
+        //
+        // Encrypt an all-zero key stream block to get the key stream.
+        //
+        SymCryptWipeKnownSize( &pState->keystreamBlock[0], SYMCRYPT_CCM_BLOCK_SIZE );
+
+        SYMCRYPT_ASSERT( pState->pBlockCipher->blockSize == SYMCRYPT_CCM_BLOCK_SIZE );
+        SymCryptCtrMsb64(   pState->pBlockCipher,
+                            pState->pExpandedKey,
+                            &pState->counterBlock[0],
+                            &pState->keystreamBlock[0],
+                            &pState->keystreamBlock[0],
+                            SYMCRYPT_CCM_BLOCK_SIZE );
+        while( cbToDo > 0 )
+        {
+            *pbDst = *pbSrc ^ pState->keystreamBlock[ pState->bytesProcessed & CCM_BLOCK_MOD_MASK ];
+            pbDst++;
+            pbSrc++;
+            cbToDo--;
+            pState->bytesProcessed++;
+        }
+    }
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmAddMacData(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbData,
+                            SIZE_T              cbData )
+{
+    SIZE_T bytesToProcess;
+    if( pState->bytesInMacBlock > 0 )
+    {
+        bytesToProcess = SYMCRYPT_MIN( cbData, SYMCRYPT_CCM_BLOCK_SIZE - pState->bytesInMacBlock );
+        SymCryptXorBytes( &pState->macBlock[pState->bytesInMacBlock], pbData, &pState->macBlock[pState->bytesInMacBlock], bytesToProcess );
+        pbData += bytesToProcess;
+        cbData -= bytesToProcess;
+        pState->bytesInMacBlock += bytesToProcess;
+
+        if( pState->bytesInMacBlock == SYMCRYPT_CCM_BLOCK_SIZE )
+        {
+            pState->pBlockCipher->encryptFunc( pState->pExpandedKey, &pState->macBlock[0], &pState->macBlock[0] );
+            pState->bytesInMacBlock = 0;
+        }
+    }
+
+    if( cbData >= SYMCRYPT_CCM_BLOCK_SIZE )
+    {
+        bytesToProcess = cbData & CCM_BLOCK_ROUND_MASK;
+        SYMCRYPT_ASSERT( pState->pBlockCipher->blockSize == SYMCRYPT_CCM_BLOCK_SIZE );
+
+        SymCryptCbcMac( pState->pBlockCipher,
+                        pState->pExpandedKey,
+                        &pState->macBlock[0],
+                        pbData,
+                        bytesToProcess );
+
+        pbData += bytesToProcess;
+        cbData -= bytesToProcess;
+    }
+
+    if( cbData > 0 )
+    {
+        SymCryptXorBytes( &pState->macBlock[0], pbData, &pState->macBlock[0], cbData );
+        pState->bytesInMacBlock = cbData;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmPadMacData( _Inout_ PSYMCRYPT_CCM_STATE  pState )
+{
+    //
+    // Pad the MAC data with zeroes until we hit the block size.
+    // The data is xorred into macBlock, so we don't have to update that.
+    // All we do is apply the block cipher if there was any data remaining in the macBlock.
+    //
+    if( pState->bytesInMacBlock > 0 )
+    {
+        pState->pBlockCipher->encryptFunc( pState->pExpandedKey, &pState->macBlock[0], &pState->macBlock[0] );
+        pState->bytesInMacBlock = 0;
+    }
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncrypt(
+    _In_                            PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                            PCVOID                  pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                  pbNonce,
+                                    SIZE_T                  cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                  pbAuthData,
+                                    SIZE_T                  cbAuthData,
+    _In_reads_( cbData )            PCBYTE                  pbSrc,
+    _Out_writes_( cbData )          PBYTE                   pbDst,
+                                    SIZE_T                  cbData,
+    _Out_writes_( cbTag )           PBYTE                   pbTag,
+                                    SIZE_T                  cbTag )
+{
+    SYMCRYPT_CCM_STATE  state;
+
+    SymCryptCcmInit(    &state,
+                        pBlockCipher,
+                        pExpandedKey,
+                        pbNonce, cbNonce,
+                        pbAuthData, cbAuthData,
+                        cbData, cbTag );
+
+    SymCryptCcmEncryptPart( &state, pbSrc, pbDst, cbData );
+
+    SymCryptCcmEncryptFinal( &state, pbTag, cbTag );
+}
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCcmDecrypt(
+    _In_                            PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                            PCVOID                  pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                  pbNonce,
+                                    SIZE_T                  cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                  pbAuthData,
+                                    SIZE_T                  cbAuthData,
+    _In_reads_( cbData )            PCBYTE                  pbSrc,
+    _Out_writes_( cbData )          PBYTE                   pbDst,
+                                    SIZE_T                  cbData,
+    _In_reads_( cbTag )             PCBYTE                  pbTag,
+                                    SIZE_T                  cbTag )
+{
+    SYMCRYPT_CCM_STATE  state;
+    SYMCRYPT_ERROR      status;
+
+    SymCryptCcmInit(    &state,
+                        pBlockCipher,
+                        pExpandedKey,
+                        pbNonce, cbNonce,
+                        pbAuthData, cbAuthData,
+                        cbData, cbTag );
+
+
+    SymCryptCcmDecryptPart( &state, pbSrc, pbDst, cbData );
+
+    status = SymCryptCcmDecryptFinal( &state, pbTag, cbTag );
+
+    //
+    // If we failed for any reason we wipe our output buffer to avoid returning
+    // decrypted but unauthenticated data.
+    //
+    if( status != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptWipe( pbDst, cbData );
+    }
+
+    return status;
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCcmInit(
+    _Out_                           PSYMCRYPT_CCM_STATE     pState,
+    _In_                            PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                            PCVOID                  pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                  pbNonce,
+                                    SIZE_T                  cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                  pbAuthData,
+                                    SIZE_T                  cbAuthData,
+                                    UINT64                  cbData,
+                                    SIZE_T                  cbTag )
+{
+    BYTE            flags;
+    BYTE            tmpBuf[ SYMCRYPT_CCM_BLOCK_SIZE ];
+    SIZE_T          cbCounter;
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+    //
+    // Validate parameters in checked builds
+    //
+    SYMCRYPT_ASSERT( SymCryptCcmValidateParameters( pBlockCipher, cbNonce, cbAuthData, cbData, cbTag ) == SYMCRYPT_NO_ERROR );
+
+
+    //
+    // compute # bytes in the counter field
+    // We limit cbNonce to 15 so that cbCounter + cbNonce = 15 will always hold
+    // This is much cheaper than full parameter validation, and it is enough to
+    // avoid any buffer overflows.
+    //
+    cbNonce &= SYMCRYPT_CCM_BLOCK_SIZE - 1;
+    cbCounter = SYMCRYPT_CCM_BLOCK_SIZE - 1 - cbNonce;
+
+    pState->pBlockCipher = pBlockCipher;
+    pState->pExpandedKey = pExpandedKey;
+    pState->cbNonce = cbNonce;
+    pState->cbData = cbData;
+    pState->cbTag = cbTag;
+    pState->cbCounter = cbCounter;
+    pState->bytesProcessed = 0;
+    pState->bytesInMacBlock = 0;
+
+    //
+    // Build the initial blocks for authentication and en/decryption
+    //
+    // Per Sp800-38c the flag byte is made up of four fields:
+    // Bits 0-2 are cbCounter - 1
+    // Bits 3-5 are (cbTag-2)/2
+    // Bit 6 is 1 if cbAuthData > 0
+    // Bit 7 is reserved and set to 0.
+    flags = (BYTE) (pState->cbCounter - 1);
+    flags |= ((cbTag-2)/2) << 3;
+    if( cbAuthData > 0 )
+    {
+        //
+        // No side-channel concerns with this if statements as we don't try to hide the
+        // data length or presence of authdata.
+        //
+        flags |= (1 << 6);
+    }
+
+
+    //
+    // The MAC starting block consists of three fields:
+    // the flag byte, the nonce, and cbData encoded into cbCounter bytes.
+    //
+    pState->macBlock[0] = flags;
+    memcpy( &pState->macBlock[1], pbNonce, cbNonce );
+    SYMCRYPT_STORE_MSBFIRST64( &tmpBuf[0], cbData );
+    memcpy( &pState->macBlock[1+cbNonce], &tmpBuf[ 8 - cbCounter ], cbCounter );
+
+    //
+    // The counter block is similar in layout, but with two changes:
+    // Bits 3-7 of the flag bytes are set to 0.
+    // The counter field is set to one (first counter value used for data encryption).
+    // Wiping the whole block first is probably faster, as the size is known and the
+    // block is aligned.
+    // We also copy the nonce from the mac block to follow the read-once rule.
+    //
+    SymCryptWipeKnownSize( &pState->counterBlock[0], SYMCRYPT_CCM_BLOCK_SIZE );
+    pState->counterBlock[0] = (BYTE)(flags & 0x7);
+    memcpy( &pState->counterBlock[1], &pState->macBlock[1], cbNonce );
+    pState->counterBlock[ SYMCRYPT_CCM_BLOCK_SIZE - 1] = 1;
+
+    //
+    // Encrypt the current MAC block; our CBC convention is to do the encryption
+    // as soon as we have enough data.
+    //
+    pBlockCipher->encryptFunc( pExpandedKey, &pState->macBlock[0], &pState->macBlock[0] );
+
+    //
+    // Next we process the associated data
+    // See the CCM specs for the details
+    //
+    if( cbAuthData <= 0 )
+    {
+        //
+        // cbAuthData == 0, nothing needs to be done.
+        //
+    } else if( cbAuthData < AUTHDATA_16BIT_LIMIT )
+    {
+        //
+        // 16-bit length encoding.
+        //
+        SYMCRYPT_STORE_MSBFIRST16( &tmpBuf[0], (UINT16) cbAuthData );
+        SymCryptCcmAddMacData( pState, &tmpBuf[0], 2 );
+    } else if( fcbAuthDataLt32bitLimitStatic || cbAuthData < AUTHDATA_32BIT_LIMIT )
+    {
+        //
+        // 32-bit length
+        //
+        tmpBuf[0] = 0xff;
+        tmpBuf[1] = 0xfe;       // Magic prefix as per SP 800-38c
+        SYMCRYPT_STORE_MSBFIRST32( &tmpBuf[2], (UINT32) cbAuthData );
+        SymCryptCcmAddMacData( pState, &tmpBuf[0], 2 + sizeof( UINT32 ) );
+    } else
+    {
+        //
+        // 64-bit length
+        //
+        tmpBuf[0] = 0xff;
+        tmpBuf[1] = 0xff;       // Magic prefix as per SP 800-38c
+        SYMCRYPT_STORE_MSBFIRST64( &tmpBuf[2], cbAuthData );
+        SymCryptCcmAddMacData( pState, &tmpBuf[0], 2 + sizeof( UINT64 ) );
+    }
+
+    SymCryptCcmAddMacData( pState, pbAuthData, cbAuthData );
+    SymCryptCcmPadMacData( pState );        // Pad MAC data with zeroes until the next block size boundary
+
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncryptPart(
+    _Inout_                   PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbData )      PCBYTE              pbSrc,
+    _Out_writes_( cbData )    PBYTE               pbDst,
+                              SIZE_T              cbData )
+{
+    UINT64 bytesProcessedAfterThisCall;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    bytesProcessedAfterThisCall = cbData + pState->bytesProcessed;
+
+    SYMCRYPT_ASSERT( bytesProcessedAfterThisCall >= cbData &&
+                     bytesProcessedAfterThisCall <= pState->cbData );
+
+    //
+    // We are violating the read-once implementation rule here. We read the data twice:
+    // once for MACing and once for encryption.
+    // In this particular situation this is safe to do.
+    // We consider the read for the MAC operation as reading the 'real' value.
+    // The encryption code reads the data, but all it does is XOR the key stream into
+    // it. (CCM encryption uses CTR mode for the encryption part.)
+    // We don't care if the attacker modifies the data before the encryption.
+    // We are revealing the key stream anyway (from the plaintext and ciphertext) and
+    // the exact byte value that we xor the key stream into is irrelevant.
+    //
+    SymCryptCcmAddMacData( pState, pbSrc, cbData );
+
+    SymCryptCcmEncryptDecryptPart( pState, pbSrc, pbDst, cbData );
+
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCcmEncryptFinal(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _Out_writes_( cbTag )   PBYTE               pbTag,
+                            SIZE_T              cbTag )
+{
+    //
+    // Check invariants in checked builds
+    //
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SYMCRYPT_ASSERT( cbTag  == pState->cbTag && pState->bytesProcessed == pState->cbData );
+
+
+    SymCryptCcmPadMacData( pState );
+
+    //
+    // Set the counter value to zero to get the counter value that encrypts the tag,
+    // and then encrypt the tag.
+    // We reset bytesProcessed so that the partial encrypt/decrypt function will do the right thing
+    //
+    SymCryptWipe( &pState->counterBlock[1 + pState->cbNonce], pState->cbCounter );
+
+    pState->bytesProcessed = 0;
+
+    SymCryptCcmEncryptDecryptPart( pState, &pState->macBlock[0], &pState->macBlock[0], SYMCRYPT_CCM_BLOCK_SIZE );
+
+    memcpy( pbTag, &pState->macBlock[0], cbTag );
+
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SYMCRYPT_ASSERT( pState->bytesInMacBlock == 0 );
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCcmDecryptPart(
+    _Inout_                 PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    UINT64 bytesProcessedAfterThisCall;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    bytesProcessedAfterThisCall = cbData + pState->bytesProcessed;
+
+    SYMCRYPT_ASSERT( bytesProcessedAfterThisCall >= cbData &&
+                     bytesProcessedAfterThisCall <= pState->cbData );
+
+
+    //
+    // We are violating the read-once/write-once implementation rule here.
+    // We write the decrypted data and then read it back for the authentication function.
+    // In this particular situation this is safe to do.
+    //
+    // Anyone who can access the memory space that contains the source and destination of this
+    // function can recover the key stream used for this (key,nonce) combination.
+    // We can think of the decryption function as merely exposing the key stream, and then the
+    // caller picking the ciphertext (and by implication the plaintext) to be authenticated.
+    // Thus the data we read during authentication is the 'real' plaintext, and the
+    // decryption function merely made the key stream available.
+    //
+    // Note that this would not safe in general, it is only safe because CTR mode decryption already
+    // reveals the key stream.
+    //
+    SymCryptCcmEncryptDecryptPart( pState, pbSrc, pbDst, cbData );
+    SymCryptCcmAddMacData( pState, pbDst, cbData );
+
+}
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCcmDecryptFinal(
+    _Inout_             PSYMCRYPT_CCM_STATE pState,
+    _In_reads_( cbTag ) PCBYTE              pbTag,
+                        SIZE_T              cbTag )
+{
+    SYMCRYPT_ERROR status;
+
+    //
+    // Check invariants in checked builds
+    //
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SYMCRYPT_ASSERT( cbTag  == pState->cbTag && pState->bytesProcessed == pState->cbData );
+
+    SymCryptCcmPadMacData( pState );
+
+    //
+    // Set the counter value to zero to get the counter value that encrypts the tag,
+    // and then encrypt the tag
+    // We reset bytesProcessed so that the partial encrypt/decrypt function will do the right thing
+    //
+    SymCryptWipe( &pState->counterBlock[1 + pState->cbNonce], pState->cbCounter );
+
+    pState->bytesProcessed = 0;
+
+    SymCryptCcmEncryptDecryptPart( pState, &pState->macBlock[0], &pState->macBlock[0], SYMCRYPT_CCM_BLOCK_SIZE );
+
+    if( !SymCryptEqual( pbTag, &pState->macBlock[0], cbTag ) )
+    {
+        status = SYMCRYPT_AUTHENTICATION_FAILURE;
+    }
+    else
+    {
+        status = SYMCRYPT_NO_ERROR;
+    }
+
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SYMCRYPT_ASSERT( pState->bytesInMacBlock == 0 );
+
+    return status;
+}
+
+
+static const BYTE SymCryptCcmSelftestResult[3 + SYMCRYPT_AES_BLOCK_SIZE ] =
+{
+    0x42, 0xd7, 0xda,
+    0x3d, 0x9e, 0x95, 0x82, 0x29, 0x3c, 0x10, 0x9c, 0xa3, 0x39, 0x31, 0x3f, 0x18, 0xf3, 0x10, 0xf6
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptCcmSelftest(void)
+{
+    BYTE    buf[ 3 + SYMCRYPT_AES_BLOCK_SIZE ];
+    SYMCRYPT_AES_EXPANDED_KEY key;
+    SYMCRYPT_ERROR err;
+
+    if( SymCryptAesExpandKey( &key, SymCryptTestKey32, 16 ) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'ccm0' );
+    }
+
+    SymCryptCcmEncrypt( SymCryptAesBlockCipher,
+                        &key,
+                        &SymCryptTestKey32[16], 12,
+                        NULL, 0,
+                        &SymCryptTestMsg3[0], buf, 3,
+                        &buf[3], SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+    if( memcmp( buf, SymCryptCcmSelftestResult, sizeof( buf ) ) != 0 )
+    {
+        SymCryptFatal( 'ccm1' );
+    }
+
+    // inject error into the ciphertext or tag
+    SymCryptInjectError( buf, sizeof( buf ) );
+
+    err = SymCryptCcmDecrypt(   SymCryptAesBlockCipher,
+                                &key,
+                                &SymCryptTestKey32[16], 12,
+                                NULL, 0,
+                                buf, buf, 3,
+                                &buf[3], SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptInjectError( buf, 3 );
+
+    if( err != SYMCRYPT_NO_ERROR || memcmp( buf, SymCryptTestMsg3, 3 ) != 0 )
+    {
+        SymCryptFatal( 'ccm2' );
+    }
+
+}
diff --git a/libs/symcrypt/lib/chacha20.c b/libs/symcrypt/lib/chacha20.c
new file mode 100644
index 000000000000..4841babea3c7
--- /dev/null
+++ b/libs/symcrypt/lib/chacha20.c
@@ -0,0 +1,267 @@
+//
+// ChaCha20.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20CryptBlocks(
+    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )  PBYTE                       pbDst,
+                            SIZE_T                      cbData );
+// Encrypt Src to Dst using whole blocks, starting at block floor(pState->offset/64).
+// # blocks processed is floor( cbData / 64 )
+// pState->offset point is updated by 64 for each block encrypted
+
+
+
+#define OFFSET_MASK     (((UINT64)1 << 38) - 1)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptChaCha20Init(
+    _Out_                   PSYMCRYPT_CHACHA20_STATE    pState,
+    _In_reads_( cbKey )     PCBYTE                      pbKey,
+    _In_                    SIZE_T                      cbKey,
+    _In_reads_( cbNonce )   PCBYTE                      pbNonce,
+                            SIZE_T                      cbNonce,
+                            UINT64                      offset )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if (cbKey != 32)
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    if (cbNonce != 12)
+    {
+        scError = SYMCRYPT_WRONG_NONCE_SIZE;
+        goto cleanup;
+    }
+
+    SymCryptLsbFirstToUint32( pbKey, &pState->key[0], 8 );
+    SymCryptLsbFirstToUint32( pbNonce, &pState->nonce[0], 3 );
+
+    SymCryptChaCha20SetOffset( pState, offset );
+
+cleanup:
+    return scError;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20SetOffset(
+    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
+                            UINT64                      offset )
+{
+    pState->offset = offset;
+    pState->keystreamBufferValid = FALSE;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Crypt(
+    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )  PBYTE                       pbDst,
+                            SIZE_T                      cbData )
+{
+    UINT32  blockOffset;
+    SIZE_T  nBytes;
+
+    blockOffset = pState->offset & 0x3f;
+
+    // If the offset is in the middle of the block, we first crypt until the end
+    // of the block
+    if( blockOffset != 0 )
+    {
+        if( !pState->keystreamBufferValid )
+        {
+            // Generate a block of key stream
+            SymCryptWipe( &pState->keystream[0], 64 );
+            SymCryptChaCha20CryptBlocks(    pState,
+                                            &pState->keystream[0],
+                                            &pState->keystream[0],
+                                            64 );
+            pState->offset -= 64;   // Don't update the offset yet
+        }
+
+        nBytes = 64 - blockOffset;  // # bytes in buffer starting at offset
+        if( cbData < nBytes )
+        {
+            // We don't use the generated block to the end. The buffer will be valid
+            // at the end as the offset won't advance beyond the block.
+            nBytes = cbData;
+            pState->keystreamBufferValid = TRUE;
+        } else {
+            // We'll use the rest of the generated block. After that the key stream
+            // buffer won't be valid as the offset will advance beyond it.
+            pState->keystreamBufferValid = FALSE;
+        }
+
+        SymCryptXorBytes( pbSrc, &pState->keystream[ blockOffset ], pbDst, nBytes );
+        pbSrc += nBytes;
+        pbDst += nBytes;
+        cbData -= nBytes;
+        pState->offset += nBytes;
+    }
+
+    // Here: pbSrc, pbDst, cbData, and pState->offset all in sync
+    // and either cbData == 0 or offset is at a block boundary
+
+    if( cbData >= 64 )
+    {
+        nBytes = cbData & ~0x3f;
+        SymCryptChaCha20CryptBlocks( pState, pbSrc, pbDst, nBytes );
+        pbSrc += nBytes;
+        pbDst += nBytes;
+        cbData -= nBytes;
+    }
+
+    if( cbData > 0 )
+    {
+        // Generate a block of key stream
+        SymCryptWipe( &pState->keystream[0], 64 );
+        SymCryptChaCha20CryptBlocks(    pState,
+                                        &pState->keystream[0],
+                                        &pState->keystream[0],
+                                        64 );
+        pState->offset -= 64;   // Don't update the offset yet
+        pState->keystreamBufferValid = TRUE;
+
+        SymCryptXorBytes( pbSrc, &pState->keystream[0], pbDst, cbData );
+        pState->offset += cbData;
+        // The following updates are correct but not needed
+        // pbSrc += cbData;
+        // pbDst += cbData;
+        // cbData -= cbData;
+    }
+}
+
+#define CHACHA_QUARTERROUND( a, b, c, d ) { \
+    a += b; d ^= a; d = ROL32( d, 16 ); \
+    c += d; b ^= c; b = ROL32( b, 12 ); \
+    a += b; d ^= a; d = ROL32( d, 8 ); \
+    c += d; b ^= c; b = ROL32( b, 7 ); \
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20CryptBlocks(
+    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )  PBYTE                       pbDst,
+                            SIZE_T                      cbData )
+{
+    UINT32 counter;
+    UINT32 s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15;
+    int i;
+
+    counter = (UINT32)(pState->offset >> 6);
+
+    while( cbData >= 64 )
+    {
+        // Initialize the state
+        s0  = 0x61707865;
+        s1  = 0x3320646e;
+        s2  = 0x79622d32;
+        s3  = 0x6b206574;
+        s4  = pState->key[0];
+        s5  = pState->key[1];
+        s6  = pState->key[2];
+        s7  = pState->key[3];
+        s8  = pState->key[4];
+        s9  = pState->key[5];
+        s10 = pState->key[6];
+        s11 = pState->key[7];
+        s12 = counter;
+        s13 = pState->nonce[0];
+        s14 = pState->nonce[1];
+        s15 = pState->nonce[2];
+
+        for( i=0; i<10; i++ )
+        {
+            CHACHA_QUARTERROUND( s0 , s4 , s8 , s12 );
+            CHACHA_QUARTERROUND( s1 , s5 , s9 , s13 );
+            CHACHA_QUARTERROUND( s2 , s6 , s10, s14 );
+            CHACHA_QUARTERROUND( s3 , s7 , s11, s15 );
+
+            CHACHA_QUARTERROUND( s0 , s5 , s10, s15 );
+            CHACHA_QUARTERROUND( s1 , s6 , s11, s12 );
+            CHACHA_QUARTERROUND( s2 , s7 , s8 , s13 );
+            CHACHA_QUARTERROUND( s3 , s4 , s9 , s14 );
+        }
+
+        s0  += 0x61707865;
+        s1  += 0x3320646e;
+        s2  += 0x79622d32;
+        s3  += 0x6b206574;
+        s4  += pState->key[0];
+        s5  += pState->key[1];
+        s6  += pState->key[2];
+        s7  += pState->key[3];
+        s8  += pState->key[4];
+        s9  += pState->key[5];
+        s10 += pState->key[6];
+        s11 += pState->key[7];
+        s12 += counter;
+        s13 += pState->nonce[0];
+        s14 += pState->nonce[1];
+        s15 += pState->nonce[2];
+
+        SYMCRYPT_STORE_LSBFIRST32( pbDst +  0, s0  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc +  0 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst +  4, s1  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc +  4 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst +  8, s2  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc +  8 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 12, s3  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 12 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 16, s4  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 16 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 20, s5  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 20 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 24, s6  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 24 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 28, s7  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 28 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 32, s8  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 32 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 36, s9  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 36 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 40, s10 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 40 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 44, s11 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 44 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 48, s12 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 48 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 52, s13 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 52 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 56, s14 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 56 ) );
+        SYMCRYPT_STORE_LSBFIRST32( pbDst + 60, s15 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 60 ) );
+
+        counter ++;
+        // If counter overflows then the caller has encrypted more than 256GB of data with a single stream, which is
+        // called out as being insecure. It is the caller's responsibility to avoid this!
+        pbSrc += 64;
+        pbDst += 64;
+        cbData -= 64;
+        pState->offset += 64;
+    }
+}
+
+static const BYTE   chacha20KatAnswer[ 3 ] = { 0xb5, 0xe0, 0x54 };
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Selftest(void)
+{
+    BYTE buf[3];
+    SYMCRYPT_CHACHA20_STATE  state;
+
+    SymCryptChaCha20Init(   &state,
+                            SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
+                            SymCryptTestMsg16, 12,
+                            0 );
+
+    SymCryptChaCha20Crypt( &state, SymCryptTestMsg3, buf, sizeof( buf ) );
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+
+    if( memcmp( buf, chacha20KatAnswer, sizeof( buf )) != 0 )
+    {
+        SymCryptFatal( 'Cha2' );
+    }
+}
diff --git a/libs/symcrypt/lib/chacha20_poly1305.c b/libs/symcrypt/lib/chacha20_poly1305.c
new file mode 100644
index 000000000000..5b46a6d209e8
--- /dev/null
+++ b/libs/symcrypt/lib/chacha20_poly1305.c
@@ -0,0 +1,257 @@
+//
+// ChaCha20_Poly1305.c
+//
+// Copyright (c) Microsoft Corporation.
+//
+
+#include "precomp.h"
+
+#define CHACHA20_POLY1305_MAX_DATA_SIZE    (((1ull << 32) - 1) * 64)
+
+// Compile time BOOL statically determines if we need to check cbData > CHACHA20_POLY1305_MAX_DATA_SIZE
+// Used to suppress MSVC C4127 and clang Wtautological-constant-out-of-range-compare on 32b platforms
+const BOOL fcbDataLteMaxDataSizeStatic = SIZE_T_MAX <= CHACHA20_POLY1305_MAX_DATA_SIZE;
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305ComputeTag(
+    _Inout_                                          PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_opt_( cbAuthData )                     PCBYTE                      pbAuthData,
+                                                     SIZE_T                      cbAuthData,
+    _In_reads_( cbData )                             PCBYTE                      pbData,
+                                                     SIZE_T                      cbData,
+    _Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE )    PBYTE                       pbTag )
+{
+    SYMCRYPT_ALIGN BYTE buf[SYMCRYPT_POLY1305_BLOCK_SIZE];
+    BYTE partialBlockSize;
+
+    SymCryptWipeKnownSize( buf, SYMCRYPT_POLY1305_BLOCK_SIZE );
+
+    // Add additional authentication data if needed.
+    if ( cbAuthData > 0 )
+    {
+        SymCryptPoly1305Append( pState, pbAuthData, cbAuthData );
+
+        // Append zeros to make a complete Poly1305 block.
+        partialBlockSize = cbAuthData % SYMCRYPT_POLY1305_BLOCK_SIZE;
+        if ( partialBlockSize > 0 )
+        {
+            SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE - partialBlockSize );
+        }
+    }
+
+    // Add ciphertext if needed.
+    if ( cbData > 0 )
+    {
+        SymCryptPoly1305Append( pState, pbData, cbData );
+
+        // Append zeros to make a complete Poly1305 block.
+        partialBlockSize = cbData % SYMCRYPT_POLY1305_BLOCK_SIZE;
+        if ( partialBlockSize > 0 )
+        {
+            SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE - partialBlockSize );
+        }
+    }
+
+    // Add length of additional authentication data and ciphertext.
+    SYMCRYPT_STORE_LSBFIRST64( &buf[0], cbAuthData );
+    SYMCRYPT_STORE_LSBFIRST64( &buf[8], cbData );
+    SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE );
+    SymCryptWipeKnownSize( buf, SYMCRYPT_POLY1305_BLOCK_SIZE );
+
+    SymCryptPoly1305Result( pState, pbTag );
+}
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305Encrypt(
+    _In_reads_( cbKey )             PCBYTE    pbKey,
+                                    SIZE_T    cbKey,
+    _In_reads_( cbNonce )           PCBYTE    pbNonce,
+                                    SIZE_T    cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE    pbAuthData,
+                                    SIZE_T    cbAuthData,
+    _In_reads_( cbData )            PCBYTE    pbSrc,
+    _Out_writes_( cbData )          PBYTE     pbDst,
+                                    SIZE_T    cbData,
+    _Out_writes_( cbTag )           PBYTE     pbTag,
+                                    SIZE_T    cbTag )
+{
+    SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_CHACHA20_STATE ChaCha20State;
+    SYMCRYPT_POLY1305_STATE Poly1305State;
+    SYMCRYPT_ALIGN BYTE     key[SYMCRYPT_POLY1305_KEY_SIZE];
+
+    if ( !fcbDataLteMaxDataSizeStatic && cbData > CHACHA20_POLY1305_MAX_DATA_SIZE )
+    {
+        status = SYMCRYPT_WRONG_DATA_SIZE;
+        goto cleanup;
+    }
+
+    if ( cbTag != SYMCRYPT_POLY1305_RESULT_SIZE )
+    {
+        status = SYMCRYPT_WRONG_TAG_SIZE;
+        goto cleanup;
+    }
+
+    status = SymCryptChaCha20Init( &ChaCha20State, pbKey, cbKey, pbNonce, cbNonce, 0 );
+    if ( status != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Generate the first 32 bytes of keystream.
+    SymCryptWipeKnownSize( key, sizeof( key ) );
+    SymCryptChaCha20Crypt( &ChaCha20State, key, key, sizeof ( key ) );
+
+    // Create the Poly1305 key using the first 32 bytes of the ChaCha20 keystream.
+    SymCryptPoly1305Init( &Poly1305State, key );
+    SymCryptWipeKnownSize( key, sizeof( key ) );
+
+    // Encrypt data if needed.
+    if ( cbData > 0 )
+    {
+        // Advance the keystream to counter 1 (offset 64) for data encryption.
+        SymCryptChaCha20SetOffset( &ChaCha20State, 64 );
+        SymCryptChaCha20Crypt( &ChaCha20State, pbSrc, pbDst, cbData );
+    }
+
+    // We read the ciphertext back, violating the general rule not to rely on I/O buffers
+    // as they can reside in a different security domain. For ChaCha20Poly1305, like GCM,
+    // this read-back of data is not a problem. An attacker with access to the buffer
+    // will get the ChaCha20 key stream plus the Poly1305 authenticator of a single value.
+    // As Poly1305 is strong even with attacker-controlled data, this is harmless.
+    SymCryptChaCha20Poly1305ComputeTag( &Poly1305State, pbAuthData, cbAuthData,
+                                        pbDst, cbData, pbTag );
+cleanup:
+
+    SymCryptWipeKnownSize( &ChaCha20State, sizeof( ChaCha20State ) );
+    SymCryptWipeKnownSize( &Poly1305State, sizeof( Poly1305State ) );
+
+    return status;
+}
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305Decrypt(
+    _In_reads_( cbKey )             PCBYTE    pbKey,
+                                    SIZE_T    cbKey,
+    _In_reads_( cbNonce )           PCBYTE    pbNonce,
+                                    SIZE_T    cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE    pbAuthData,
+                                    SIZE_T    cbAuthData,
+    _In_reads_( cbData )            PCBYTE    pbSrc,
+    _Out_writes_( cbData )          PBYTE     pbDst,
+                                    SIZE_T    cbData,
+    _In_reads_( cbTag )             PCBYTE    pbTag,
+                                    SIZE_T    cbTag )
+{
+    SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_CHACHA20_STATE ChaCha20State;
+    SYMCRYPT_POLY1305_STATE Poly1305State;
+    SYMCRYPT_ALIGN BYTE     buf[SYMCRYPT_POLY1305_RESULT_SIZE];
+    SYMCRYPT_ALIGN BYTE     key[SYMCRYPT_POLY1305_KEY_SIZE];
+
+    if ( !fcbDataLteMaxDataSizeStatic && cbData > CHACHA20_POLY1305_MAX_DATA_SIZE )
+    {
+        status = SYMCRYPT_WRONG_DATA_SIZE;
+        goto cleanup;
+    }
+
+    if ( cbTag != SYMCRYPT_POLY1305_RESULT_SIZE )
+    {
+        status = SYMCRYPT_WRONG_TAG_SIZE;
+        goto cleanup;
+    }
+
+    status = SymCryptChaCha20Init( &ChaCha20State, pbKey, cbKey, pbNonce, cbNonce, 0 );
+    if ( status != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Generate the first 32 bytes of keystream.
+    SymCryptWipeKnownSize( key, sizeof( key ) );
+    SymCryptChaCha20Crypt( &ChaCha20State, key, key, sizeof( key ) );
+
+    // Create the Poly1305 key using the first 32 bytes of the ChaCha20 keystream.
+    SymCryptPoly1305Init( &Poly1305State, key );
+    SymCryptWipeKnownSize( key, sizeof( key ) );
+
+    // We read the ciphertext back, violating the general rule not to rely on I/O buffers
+    // as they can reside in a different security domain. For ChaCha20Poly1305, like GCM,
+    // this read-back of data is not a problem. An attacker with access to the buffer
+    // will get the ChaCha20 key stream plus the Poly1305 authenticator of a single value.
+    // As Poly1305 is strong even with attacker-controlled data, this is harmless.
+    SymCryptChaCha20Poly1305ComputeTag( &Poly1305State, pbAuthData, cbAuthData,
+                                        pbSrc, cbData, buf );
+
+    // Validate tag.
+    if (!SymCryptEqual(pbTag, buf, cbTag))
+    {
+        status = SYMCRYPT_AUTHENTICATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Decrypt data if needed.
+    if ( cbData > 0)
+    {
+        // Advance the keystream to counter 1 (offset 64) for data decryption.
+        SymCryptChaCha20SetOffset( &ChaCha20State, 64 );
+        SymCryptChaCha20Crypt( &ChaCha20State, pbSrc, pbDst, cbData );
+    }
+
+cleanup:
+
+    SymCryptWipeKnownSize( &ChaCha20State, sizeof( ChaCha20State ) );
+    SymCryptWipeKnownSize( &Poly1305State, sizeof( Poly1305State ) );
+
+    return status;
+}
+
+
+static const BYTE SymCryptChaCha20Poly1305Result[3 + SYMCRYPT_POLY1305_RESULT_SIZE] =
+{
+    0x5d, 0xba, 0x7b,
+    0x80, 0x10, 0xd2, 0x05, 0x4a, 0xad, 0x53, 0x1f, 0xa2, 0xce, 0x83, 0xc1, 0x66, 0x12, 0x85, 0x21
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptChaCha20Poly1305Selftest(void)
+{
+    BYTE    buf[3 + SYMCRYPT_POLY1305_RESULT_SIZE];
+    SYMCRYPT_ERROR err;
+
+    if ( SymCryptChaCha20Poly1305Encrypt( SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
+                                          SymCryptTestMsg16, 12,
+                                          SymCryptTestMsg16, sizeof( SymCryptTestMsg16 ),
+                                          &SymCryptTestMsg3[0], buf, 3,
+                                          &buf[3], SYMCRYPT_POLY1305_RESULT_SIZE ) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'ccp0' );
+    }
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+    if ( memcmp( buf, SymCryptChaCha20Poly1305Result, sizeof( buf ) ) != 0 )
+    {
+        SymCryptFatal( 'ccp1' );
+    }
+
+    // Inject error into the ciphertext or tag.
+    SymCryptInjectError( buf, sizeof( buf ) );
+
+    err = SymCryptChaCha20Poly1305Decrypt( SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
+                                           SymCryptTestMsg16, 12,
+                                           SymCryptTestMsg16, sizeof( SymCryptTestMsg16 ),
+                                           buf, buf, 3,
+                                           &buf[3], SYMCRYPT_POLY1305_RESULT_SIZE );
+    SymCryptInjectError( buf, 3 );
+
+    if ( err != SYMCRYPT_NO_ERROR || memcmp( buf, SymCryptTestMsg3, 3 ) != 0 )
+    {
+        SymCryptFatal( 'ccp2' );
+    }
+}
diff --git a/libs/symcrypt/lib/cpuid.c b/libs/symcrypt/lib/cpuid.c
new file mode 100644
index 000000000000..2ad1b355074f
--- /dev/null
+++ b/libs/symcrypt/lib/cpuid.c
@@ -0,0 +1,419 @@
+//
+// cpuid.c   code for CPU feature detection based on CPUID
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+
+#include "precomp.h"
+
+#if (SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64) & SYMCRYPT_MS_VC
+#include <excpt.h>
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("xsave")
+#endif
+
+//
+// RDRAND availability is signaled by CPUID.1.ecx[30]
+// PCLMULQDQ availability is signaled by CPUID.1.ecx[1]
+// AES_NI availability is signaled by CPUID.1.ecx[25]
+// SSSE3 availability is signaled by CPUID.1.ecx[9]
+// SSE3 availability is signaled by CPUID.1.ecx[0]
+// SSE2 availability is signaled by CPUID.1.edx[26]
+//
+
+#define CPUID_1_ECX_RDRAND_BIT      30
+#define CPUID_1_ECX_PCLMULQDQ_BIT   1
+#define CPUID_1_ECX_AESNI_BIT       25
+#define CPUID_1_ECX_SSSE3_BIT       9
+#define CPUID_1_ECX_SSE3_BIT        0
+#define CPUID_1_EDX_SSE2_BIT        26
+#define CPUID_1_EDX_SSE_BIT         25
+#define CPUID_1_ECX_AVX_BIT         28
+#define CPUID_1_ECX_CMPXCHG16B_BIT  13
+#define CPUID_70_EBX_AVX2_BIT       5
+#define CPUID_70_EBX_RDSEED_BIT     18
+#define CPUID_70_EBX_SHANI_BIT      29
+#define CPUID_70_EBX_ADX_BIT        19
+#define CPUID_70_EBX_BMI2_BIT       8
+#define CPUID_70_EBX_AVX512F_BIT    16
+#define CPUID_70_EBX_AVX512BW_BIT   30
+#define CPUID_70_EBX_AVX512DQ_BIT   17
+#define CPUID_70_EBX_AVX512VL_BIT   31
+#define CPUID_70_ECX_VAES_BIT       9
+#define CPUID_70_ECX_VPCLMULQDQ_BIT 10
+
+
+#define CPUID_1_ECX_OSXSAVE_BIT     27
+
+typedef struct _CPUID_BIT_INFO {
+    BYTE                    leaf;
+    BYTE                    word;
+    BYTE                    bitno;
+    SYMCRYPT_CPU_FEATURES   requiredBy;
+} CPUID_BIT_INFO;
+
+#define WORD_EAX    0
+#define WORD_EBX    1
+#define WORD_ECX    2
+#define WORD_EDX    3
+
+int g_SymCryptCpuid1[4];        // We cache the results of CPUID(1) to help diagnose CPU detection errors
+
+const
+CPUID_BIT_INFO  cpuidBitInfo[] = {
+    {1, WORD_ECX, CPUID_1_ECX_RDRAND_BIT,       SYMCRYPT_CPU_FEATURE_RDRAND },
+    {1, WORD_ECX, CPUID_1_ECX_PCLMULQDQ_BIT,    SYMCRYPT_CPU_FEATURE_PCLMULQDQ },
+    {1, WORD_ECX, CPUID_1_ECX_AESNI_BIT,        SYMCRYPT_CPU_FEATURE_AESNI },
+    {1, WORD_EDX, CPUID_1_EDX_SSE_BIT,          SYMCRYPT_CPU_FEATURE_SSE2 | SYMCRYPT_CPU_FEATURE_SSSE3 },
+    {1, WORD_EDX, CPUID_1_EDX_SSE2_BIT,         SYMCRYPT_CPU_FEATURE_SSE2 | SYMCRYPT_CPU_FEATURE_SSSE3 },
+    {1, WORD_ECX, CPUID_1_ECX_SSE3_BIT,         SYMCRYPT_CPU_FEATURE_SSSE3 },
+    {1, WORD_ECX, CPUID_1_ECX_SSSE3_BIT,        SYMCRYPT_CPU_FEATURE_SSSE3 },
+    {1, WORD_ECX, CPUID_1_ECX_AVX_BIT,          SYMCRYPT_CPU_FEATURE_AVX2 },
+    {1, WORD_ECX, CPUID_1_ECX_CMPXCHG16B_BIT,   SYMCRYPT_CPU_FEATURE_CMPXCHG16B },
+    {7, WORD_EBX, CPUID_70_EBX_AVX2_BIT,        SYMCRYPT_CPU_FEATURE_AVX2 },
+    {7, WORD_EBX, CPUID_70_EBX_RDSEED_BIT,      SYMCRYPT_CPU_FEATURE_RDSEED },
+    {7, WORD_EBX, CPUID_70_EBX_SHANI_BIT,       SYMCRYPT_CPU_FEATURE_SHANI },
+    {7, WORD_EBX, CPUID_70_EBX_ADX_BIT,         SYMCRYPT_CPU_FEATURE_ADX },
+    {7, WORD_EBX, CPUID_70_EBX_BMI2_BIT,        SYMCRYPT_CPU_FEATURE_BMI2 },
+    {7, WORD_EBX, CPUID_70_EBX_AVX512F_BIT,     SYMCRYPT_CPU_FEATURE_AVX512 },
+    {7, WORD_EBX, CPUID_70_EBX_AVX512VL_BIT,    SYMCRYPT_CPU_FEATURE_AVX512 },
+    {7, WORD_EBX, CPUID_70_EBX_AVX512BW_BIT,    SYMCRYPT_CPU_FEATURE_AVX512 },
+    {7, WORD_EBX, CPUID_70_EBX_AVX512DQ_BIT,    SYMCRYPT_CPU_FEATURE_AVX512 },
+    {7, WORD_ECX, CPUID_70_ECX_VAES_BIT,        SYMCRYPT_CPU_FEATURE_VAES },
+    {7, WORD_ECX, CPUID_70_ECX_VPCLMULQDQ_BIT,  SYMCRYPT_CPU_FEATURE_VAES },
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesByCpuid( UINT32 flags )
+{
+    UINT32   result;
+    int     CPUInfo[4];
+    int     InfoType;
+    int     maxInfoType;
+    int     i;
+    BOOLEAN allowYmm, allowZmm;
+    INT64 xGetBvResult;
+
+    //
+    // Mark all features as present (the result bits indicate not-present, so set the features we know to 0).
+    //
+    result = ~ (UINT32)(
+        SYMCRYPT_CPU_FEATURE_SSE2       |
+        SYMCRYPT_CPU_FEATURE_SSSE3      |
+        SYMCRYPT_CPU_FEATURE_AESNI      |
+        SYMCRYPT_CPU_FEATURE_PCLMULQDQ  |
+        SYMCRYPT_CPU_FEATURE_AVX2       |
+        SYMCRYPT_CPU_FEATURE_SHANI      |
+        SYMCRYPT_CPU_FEATURE_BMI2       |
+        SYMCRYPT_CPU_FEATURE_ADX        |
+        SYMCRYPT_CPU_FEATURE_RDRAND     |
+        SYMCRYPT_CPU_FEATURE_RDSEED     |
+        SYMCRYPT_CPU_FEATURE_AVX512     |
+        SYMCRYPT_CPU_FEATURE_VAES       |
+        SYMCRYPT_CPU_FEATURE_CMPXCHG16B
+        );
+
+    // InfoType holds the function id of previous cpuid
+    // so we don't have to repeatedly invoke cpuid.
+    InfoType = 0;
+    SymCryptCpuidExFunc( CPUInfo, InfoType, 0 );
+    maxInfoType = CPUInfo[WORD_EAX];
+
+    for( i=0; i< sizeof( cpuidBitInfo ) / sizeof( *cpuidBitInfo ); i++ )
+    {
+        if( cpuidBitInfo[i].leaf != InfoType )
+        {
+            InfoType = cpuidBitInfo[i].leaf;
+            SymCryptCpuidExFunc( CPUInfo, InfoType, 0 );
+        }
+        if( cpuidBitInfo[i].leaf > maxInfoType || (CPUInfo[ cpuidBitInfo[i].word ] & (1UL << cpuidBitInfo[i].bitno) ) == 0 )
+        {
+            result |= cpuidBitInfo[i].requiredBy;
+        }
+    }
+
+    if( (flags & SYMCRYPT_CPUID_DETECT_FLAG_CHECK_OS_SUPPORT_FOR_YMM) != 0 )
+    {
+        //
+        // Check for OS support of the YMM registers.
+        // This detection is optional in any environment because some environments are single-threaded, and
+        // OS support is not required. (E.g. Boot library.)
+        //
+        // We use the following logic:
+        // Check that the OSXSAVE bit is 1, which means we can use XGETBV
+        // Use XGETBV and check that XCR0[2:1] = '11b' signaling that both XMM and YMM are enabled by OS
+        // Note that we only disable the AVX2 usage; AESNI & XMM registers are used independent of OS support, because
+        // all our (known) OSes have it.
+        //
+        allowYmm = FALSE;
+        allowZmm = FALSE;
+        SymCryptCpuidExFunc( CPUInfo, 1, 0 );
+
+        if( (CPUInfo[WORD_ECX] & (1 << CPUID_1_ECX_OSXSAVE_BIT)) != 0 )
+        {
+            // OSXSAVE bit is set, we can use XGETBV
+            xGetBvResult = _xgetbv( _XCR_XFEATURE_ENABLED_MASK );
+
+            // Check that bits 1 and 2 are set, corresponding to the XMM and YMM register state
+            if( (xGetBvResult & 0x6) == 0x6)
+            {
+                allowYmm = TRUE;
+
+                //
+                // For AVX-512, also check that bits 5, 6, and 7 are set, corresponding to the
+                // opmask, ZMM (0-15), and ZMM (16-31) register states
+                // This follows the recommendation in the Intel 64 and IA-32 Architectures Software
+                // Developer's Manual, Volume 1, 15.3 / 15.4.
+                //
+                // It seems plausible that on some system the OS would not support save/restore of
+                // AVX-512 state, but use of AVX-512VL instructions on Ymm or Xmm registers would be
+                // OK, however Intel explicitly suggests that we should only use AVX512-VL if the
+                // support is indicated by xgetbv, so we use the same logic as for AVX2 (our
+                // SymCrypt feature indicates both CPU support, and OS support for saving/restoring
+                // the extended state)
+                //
+                if( (xGetBvResult & 0xe0) == 0xe0)
+                {
+                    allowZmm = TRUE;
+                }
+            }
+        }
+
+        if( !allowYmm )
+        {
+            // Disallow the AVX2-dependent code because we don't have OS YMM support.
+            result |= SYMCRYPT_CPU_FEATURE_AVX2;
+        }
+
+        if( !allowZmm )
+        {
+            // Disallow any AVX512-dependent code because we don't have OS ZMM support.
+            // Note that not all AVX-512 dependent code will need to save/restore ZMM state, but we
+            // do not support AVX-512 instructions (even acting on YMM or XMM registers), unless the
+            // OS indicates support via XCR0
+            result |= SYMCRYPT_CPU_FEATURE_AVX512;
+        }
+    }
+
+
+    if( (result & SYMCRYPT_CPU_FEATURE_AESNI) == 0 )    // thus, if AES-NI is present according to CPUID
+    {
+        //
+        // In Win7 Beta we had an interesting crash bucket.
+        // It only occurred on the AsusTek A6K line of laptops which sometimes
+        // set the cpuid AES-NI bit (but not always). This leads to a crash as
+        // we start using AES instructions that don't exist on those machines.
+        //
+        // I found on-line reviews for the A6K line from december 2005 so it was launched around
+        // that time.
+        //
+        // These laptops all have AMD CPUs, so we fix it by locking out the particular AMD CPUs
+        // families that don't have AES-NI anyway.
+        //
+        // We really shouldn't need this logic, and it only slows things down.
+        // We should be able to remove it at some point in the future.
+        //
+        // At AMD's recommendation, we use the logic below.
+        // The AMD engineers reviewed this code to ensure we don't lock out future CPUs
+        // that will have AES-NI.
+        //
+        SymCryptCpuidExFunc( CPUInfo, 0, 0 );
+        if(    CPUInfo[WORD_EBX] == 'htuA'
+            && CPUInfo[WORD_ECX] == 'DMAc'
+            && CPUInfo[WORD_EDX] == 'itne' )
+        {
+            //
+            // We have an AMD cpu, check the family.
+            //
+            UINT32 baseFamily;
+            UINT32 extFamily;
+            UINT32 family;
+
+            //
+            // Extract the base family and extended family values, and combine them to the full
+            // family value.
+            //
+            SymCryptCpuidExFunc( CPUInfo, 1, 0 );
+
+            baseFamily = (CPUInfo[WORD_EAX] >> 8) & 0xf;
+
+            extFamily = (CPUInfo[WORD_EAX] >> 20) & 0xff;
+
+            if( baseFamily < 0xf )
+            {
+                family = baseFamily;
+            } else {
+                family = baseFamily + extFamily;
+            }
+
+            //
+            // AMD will not implement the AES instruction set until family 0x15
+            //
+            if( family < 0x15 )
+            {
+                result |= SYMCRYPT_CPU_FEATURE_AESNI;
+            }
+        }
+    }
+
+    SymCryptCpuidExFunc( g_SymCryptCpuid1, 1, 0 );             // Keep cache of CPUID results for diagnosis
+
+    g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) result;
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#elif SYMCRYPT_CPU_ARM
+
+#define CP15_ISAR5             15, 0,  0,  2, 5         // Instruction Set Attribute Register 5
+
+#define READ_ARM_FEATURE(_FeatureRegister, _Index) \
+        (((ULONG)_MoveFromCoprocessor(_FeatureRegister) >> ((_Index) * 4)) & 0xF)
+
+#define ISAR5_AES              1
+#define ISAR5_AES_AESE         1
+#define ISAR5_AES_PMULL        2
+
+#define ISAR5_SHA2             3
+#define ISAR5_SHA2_SHA256H     1
+
+#define ISAR5_CRC32            4
+#define ISAR5_CRC32_IMP        1
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromRegisters(void)
+{
+    UINT32 result;
+
+#if 0   // We currently do not use any neon crypto features on ARM code, so no detection needed.
+
+    //
+    // We start with a result that allows everything.
+    // This makes the code simpler when you have one CPU feature flag that disables multiple feature bits.
+    //
+    result = ~ (UINT32)(
+        SYMCRYPT_CPU_FEATURE_NEON           |
+        SYMCRYPT_CPU_FEATURE_NEON_AES       |
+        SYMCRYPT_CPU_FEATURE_NEON_PMULL     |
+        SYMCRYPT_CPU_FEATURE_NEON_SHA256
+        );
+
+    //
+    // Reading the status registers might fail, so we use a try block.
+    //
+    try {
+
+        if( READ_ARM_FEATURE(CP15_ISAR5, ISAR5_AES) < ISAR5_AES_AESE )
+        {
+            result |= SYMCRYPT_CPU_FEATURE_NEON_AES;
+        }
+
+        if( READ_ARM_FEATURE(CP15_ISAR5, ISAR5_AES) < ISAR5_AES_PMULL )
+        {
+            result |= SYMCRYPT_CPU_FEATURE_NEON_PMULL;
+        }
+
+        if( READ_ARM_FEATURE(CP15_ISAR5, ISAR5_SHA2) < ISAR5_SHA2_SHA256H )
+        {
+            result |= SYMCRYPT_CPU_FEATURE_NEON_SHA256;
+        }
+
+    } except(EXCEPTION_EXECUTE_HANDLER) {
+        //
+        // Something went wrong reading the registers; disable all the crypto extensions leaving only the standard NEON registers available.
+        //
+        result |= SYMCRYPT_CPU_FEATURE_NEON_AES | SYMCRYPT_CPU_FEATURE_NEON_PMULL | SYMCRYPT_CPU_FEATURE_NEON_SHA256;
+    }
+#endif
+    //
+    // For now we ignore the new instructions in ARM until we can get clarity on how to detect Arm32-on-Arm64.
+    //
+    result = ~(UINT32)SYMCRYPT_CPU_FEATURE_NEON;
+
+    g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) result;
+}
+
+
+#elif SYMCRYPT_CPU_ARM64 && !SYMCRYPT_PLATFORM_WINE
+
+
+#define ARM64_SYSREG(op0, op1, crn, crm, op2) \
+        ( ((op0 & 1) << 14) | \
+          ((op1 & 7) << 11) | \
+          ((crn & 15) << 7) | \
+          ((crm & 15) << 3) | \
+          ((op2 & 7) << 0) )
+
+#define ARM64_ID_AA64ISAR0_EL1  ARM64_SYSREG(3,0, 0, 6,0)   // ISA Feature Register 0
+
+#define ISAR0_AES                   1
+#define ISAR0_AES_NI                0
+#define ISAR0_AES_INSTRUCTIONS      1
+#define ISAR0_AES_PLUS_PMULL64      2
+
+#define ISAR0_SHA2                  3
+#define ISAR0_SHA2_NI               0
+#define ISAR0_SHA2_INSTRUCTIONS     1
+
+#define ISAR0_CRC32                 4
+#define ISAR0_CRC32_NI              0
+#define ISAR0_CRC32_INSTRUCTIONS    1
+
+#define READ_ARM64_FEATURE(_FeatureRegister, _Index) \
+        (((ULONG64)_ReadStatusReg(_FeatureRegister) >> ((_Index) * 4)) & 0xF)
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromRegisters(void)
+{
+    UINT32 result;
+
+    result = ~ (UINT32)(
+        SYMCRYPT_CPU_FEATURE_NEON           |
+        SYMCRYPT_CPU_FEATURE_NEON_AES       |
+        SYMCRYPT_CPU_FEATURE_NEON_PMULL     |
+        SYMCRYPT_CPU_FEATURE_NEON_SHA256
+        );
+
+#if SYMCRYPT_MS_VC
+    __try {
+
+        if( READ_ARM64_FEATURE(ARM64_ID_AA64ISAR0_EL1, ISAR0_AES) < ISAR0_AES_INSTRUCTIONS )
+        {
+            result |= SYMCRYPT_CPU_FEATURE_NEON_AES;
+        }
+
+        if( READ_ARM64_FEATURE(ARM64_ID_AA64ISAR0_EL1, ISAR0_AES) < ISAR0_AES_PLUS_PMULL64 )
+        {
+            result |= SYMCRYPT_CPU_FEATURE_NEON_PMULL;
+        }
+
+        if( READ_ARM64_FEATURE(ARM64_ID_AA64ISAR0_EL1, ISAR0_SHA2) < ISAR0_SHA2_INSTRUCTIONS )
+        {
+            result |= SYMCRYPT_CPU_FEATURE_NEON_SHA256;
+        }
+
+        g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) result;
+
+    } __except(EXCEPTION_EXECUTE_HANDLER) {
+        ; //NOTHING;
+    }
+#endif
+
+}
+
+#endif // CPU arch selection
diff --git a/libs/symcrypt/lib/cpuid_um.c b/libs/symcrypt/lib/cpuid_um.c
new file mode 100644
index 000000000000..64e7e7d4fbef
--- /dev/null
+++ b/libs/symcrypt/lib/cpuid_um.c
@@ -0,0 +1,131 @@
+//
+// cpuid_um.c   code for CPU feature detection based on OS features available in user-mode
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This file contains the feature detection code that is only compiled for user-mode.
+// The IsProcessorFeaturePresent API is only in UM, so linking any code out of
+// a source file that contains a call to it doesn't work for KM code.
+// By splitting it into a separate file, the code is ignored by KM callers because
+// they never reference anything in this file.
+//
+
+
+
+#include "precomp.h"
+
+#if SYMCRYPT_CPU_ARM64 && SYMCRYPT_PLATFORM_WINDOWS
+#undef UNREFERENCED_PARAMETER
+#include <processthreadsapi.h>
+
+// From winnt.h
+#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 30
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromIsProcessorFeaturePresent(void)
+{
+    if( IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) )
+    {
+        g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) ~(
+            SYMCRYPT_CPU_FEATURE_NEON           |
+            SYMCRYPT_CPU_FEATURE_NEON_AES       |
+            SYMCRYPT_CPU_FEATURE_NEON_PMULL     |
+            SYMCRYPT_CPU_FEATURE_NEON_SHA256
+            );
+    } else {
+        g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) ~SYMCRYPT_CPU_FEATURE_NEON;
+    }
+}
+
+#elif SYMCRYPT_CPU_ARM64 && SYMCRYPT_GNUC
+
+#if SYMCRYPT_PLATFORM_APPLE
+#include <sys/sysctl.h>
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromIsProcessorFeaturePresent(void)
+{
+    // Arm64 code relies on presence of ASIMD everywhere (it is always present with Armv8); the
+    // compiler is permitted to generate ASIMD instructions anywhere
+    // The SYMCRYPT_CPU_FEATURE_NEON is currently always present and never checked
+    SYMCRYPT_CPU_FEATURES result = ~SYMCRYPT_CPU_FEATURE_NEON;
+
+    // On macOS ARM64, we use sysctl to query CPU features
+    // All Apple Silicon Macs support AES, PMULL, and SHA2 instructions
+    uint32_t has_feature = 0;
+    size_t len = sizeof(has_feature);
+
+    // Check for AES support
+    if( sysctlbyname("hw.optional.arm.FEAT_AES", &has_feature, &len, NULL, 0) == 0 && has_feature )
+    {
+        result &= ~SYMCRYPT_CPU_FEATURE_NEON_AES;
+    }
+
+    // Check for PMULL support
+    has_feature = 0;
+    len = sizeof(has_feature);
+    if( sysctlbyname("hw.optional.arm.FEAT_PMULL", &has_feature, &len, NULL, 0) == 0 && has_feature )
+    {
+        result &= ~SYMCRYPT_CPU_FEATURE_NEON_PMULL;
+    }
+
+    // Check for SHA2 support
+    has_feature = 0;
+    len = sizeof(has_feature);
+    if( sysctlbyname("hw.optional.arm.FEAT_SHA256", &has_feature, &len, NULL, 0) == 0 && has_feature )
+    {
+        result &= ~SYMCRYPT_CPU_FEATURE_NEON_SHA256;
+    }
+
+    g_SymCryptCpuFeaturesNotPresent = result;
+}
+
+#else // Linux and other Unix platforms
+
+#include <sys/auxv.h>
+
+// #include <asm/hwcap.h>
+#define HWCAP_AES   (1 << 3)
+#define HWCAP_PMULL (1 << 4)
+#define HWCAP_SHA2  (1 << 6)
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromIsProcessorFeaturePresent(void)
+{
+    unsigned long hwcaps = getauxval( AT_HWCAP );
+
+    SYMCRYPT_CPU_FEATURES result = ~(
+        SYMCRYPT_CPU_FEATURE_NEON           |
+        SYMCRYPT_CPU_FEATURE_NEON_AES       |
+        SYMCRYPT_CPU_FEATURE_NEON_PMULL     |
+        SYMCRYPT_CPU_FEATURE_NEON_SHA256
+        );
+
+    // Arm64 code relies on presence of ASIMD everywhere (it is always present with Armv8); the
+    // compiler is permitted to generate ASIMD instructions anywhere
+    // The SYMCRYPT_CPU_FEATURE_NEON is currently always present and never checked
+
+    if( !(hwcaps & HWCAP_AES) )
+    {
+        result |= SYMCRYPT_CPU_FEATURE_NEON_AES;
+    }
+
+    if( !(hwcaps & HWCAP_PMULL) )
+    {
+        result |= SYMCRYPT_CPU_FEATURE_NEON_PMULL;
+    }
+
+    if( !(hwcaps & HWCAP_SHA2) )
+    {
+        result |= SYMCRYPT_CPU_FEATURE_NEON_SHA256;
+    }
+
+    g_SymCryptCpuFeaturesNotPresent = result;
+}
+
+#endif // SYMCRYPT_PLATFORM_APPLE
+
+#endif
diff --git a/libs/symcrypt/lib/crt.c b/libs/symcrypt/lib/crt.c
new file mode 100644
index 000000000000..85148646b7f2
--- /dev/null
+++ b/libs/symcrypt/lib/crt.c
@@ -0,0 +1,215 @@
+//
+// crt.c   Chinese Remainder Theorem Algorithms
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCrtGenerateForTwoCoprimes(
+    _In_    PCSYMCRYPT_MODULUS   pmP,
+    _In_    PCSYMCRYPT_MODULUS   pmQ,
+            UINT32               flags,
+    _Out_   PSYMCRYPT_MODELEMENT peInvQModP,
+    _Out_   PSYMCRYPT_MODELEMENT peInvPModQ,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_INT  piSrc1 = NULL;
+    PCSYMCRYPT_INT  piSrc2 = NULL;
+
+    PSYMCRYPT_INT   piInvSrc1ModSrc2 = NULL;
+    PSYMCRYPT_INT   piInvSrc2ModSrc1 = NULL;
+
+    UINT32 nDigits = 0;
+    UINT32 cbInt = 0;
+
+    BOOLEAN oddP = FALSE;
+
+    SYMCRYPT_ASSERT( pmP != NULL );
+    SYMCRYPT_ASSERT( pmQ != NULL );
+
+    nDigits = SYMCRYPT_MAX( SymCryptModulusDigitsizeOfObject( pmP ), SymCryptModulusDigitsizeOfObject( pmQ ));
+
+    // Create two temporary integers
+    cbInt = SymCryptSizeofIntFromDigits( nDigits );
+
+    SYMCRYPT_ASSERT( cbScratch >= 2*cbInt + SYMCRYPT_SCRATCH_BYTES_FOR_EXTENDED_GCD( nDigits ));
+
+    piInvSrc1ModSrc2 = SymCryptIntCreate( pbScratch, cbInt, nDigits ); pbScratch += cbInt; cbScratch -= cbInt;
+    piInvSrc2ModSrc1 = SymCryptIntCreate( pbScratch, cbInt, nDigits ); pbScratch += cbInt; cbScratch -= cbInt;
+
+    oddP = ((SymCryptIntGetValueLsbits32(SymCryptIntFromModulus( (PSYMCRYPT_MODULUS) pmP )) & 1) == 1);
+    if (oddP)
+    {
+        piSrc1 = SymCryptIntFromModulus( (PSYMCRYPT_MODULUS) pmQ );
+        piSrc2 = SymCryptIntFromModulus( (PSYMCRYPT_MODULUS) pmP );
+    }
+    else
+    {
+        piSrc1 = SymCryptIntFromModulus( (PSYMCRYPT_MODULUS) pmP );
+        piSrc2 = SymCryptIntFromModulus( (PSYMCRYPT_MODULUS) pmQ );
+    }
+
+    // IntExtendedGcd requirements:
+    //      - First argument > 0
+    //      - Second argument odd
+    if( SymCryptIntIsEqualUint32(piSrc1, 0) ||
+        ((SymCryptIntGetValueLsbits32(piSrc2) & 1) != 1) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Extended GCD
+    SymCryptIntExtendedGcd( piSrc1, piSrc2, flags, NULL, NULL, piInvSrc1ModSrc2, piInvSrc2ModSrc1, pbScratch, cbScratch );
+
+    if (oddP)
+    {
+        SymCryptIntToModElement( piInvSrc2ModSrc1, pmQ, peInvPModQ, pbScratch, cbScratch );
+        SymCryptIntToModElement( piInvSrc1ModSrc2, pmP, peInvQModP, pbScratch, cbScratch );
+    }
+    else
+    {
+        SymCryptIntToModElement( piInvSrc2ModSrc1, pmP, peInvQModP, pbScratch, cbScratch );
+        SymCryptIntToModElement( piInvSrc1ModSrc2, pmQ, peInvPModQ, pbScratch, cbScratch );
+    }
+
+cleanup:
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCrtGenerateInverses(
+                                    UINT32                  nCoprimes,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODULUS *    ppmCoprimes,
+                                    UINT32                  flags,
+    _Out_writes_( nCoprimes )       PSYMCRYPT_MODELEMENT *  ppeCrtInverses,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if (nCoprimes == 2)
+    {
+        SymCryptCrtGenerateForTwoCoprimes(
+                ppmCoprimes[0],
+                ppmCoprimes[1],
+                flags,
+                ppeCrtInverses[0],
+                ppeCrtInverses[1],
+                pbScratch,
+                cbScratch );
+    }
+    else
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCrtSolve(
+                                    UINT32                  nCoprimes,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODULUS *    ppmCoprimes,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODELEMENT * ppeCrtInverses,
+    _In_reads_( nCoprimes )         PCSYMCRYPT_MODELEMENT * ppeCrtRemainders,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_INT           piSolution,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_ASSERT( nCoprimes >= 2 );
+
+    PSYMCRYPT_INT  piTmp = NULL;
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+
+    PSYMCRYPT_INT  piDouble = NULL;
+
+    UINT32 nDigitsMax = 0;
+
+    UINT32 cbInt = 0;
+    UINT32 cbModElement = 0;
+    UINT32 cbDouble = 0;
+
+    UINT32 carry = 0;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    nDigitsMax = SYMCRYPT_MAX( SymCryptModulusDigitsizeOfObject( ppmCoprimes[0] ), SymCryptModulusDigitsizeOfObject( ppmCoprimes[1] ) );
+
+    cbInt = SymCryptSizeofIntFromDigits( nDigitsMax );
+    cbModElement = SymCryptSizeofModElementFromModulus( ppmCoprimes[0] );
+    cbDouble = SymCryptSizeofIntFromDigits( 2*nDigitsMax );
+
+    if( cbDouble == 0 )
+    {
+        // It is possible that cbDouble would not fit within the maximum integer
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT( cbScratch >= cbInt + cbModElement + cbDouble +
+                                  SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsMax ),
+                                       SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( 2*nDigitsMax ) )
+                    );
+
+    // Create temporaries
+    piTmp = SymCryptIntCreate( pbScratch, cbInt, nDigitsMax ); pbScratch += cbInt; cbScratch -= cbInt;
+
+    peTmp = SymCryptModElementCreate( pbScratch, cbModElement, ppmCoprimes[0] ); pbScratch += cbModElement; cbScratch -= cbModElement;
+
+    piDouble = SymCryptIntCreate( pbScratch, cbDouble, 2*nDigitsMax ); pbScratch += cbDouble; cbScratch -= cbDouble;
+
+    if (nCoprimes == 2)
+    {
+        //
+        // Let r0 and r1 be the two remainders modulo p and q respectively
+        // Then we calculate (q^{-1}(r0 - r1) mod p)*q + r1
+        //
+        SymCryptModElementToInt( ppmCoprimes[1], ppeCrtRemainders[1], piTmp, pbScratch, cbScratch );               // Convert r1 to Int
+        SymCryptIntToModElement( piTmp, ppmCoprimes[0], peTmp, pbScratch, cbScratch );                             // Convert it to r1 mod p
+
+        SymCryptModSub( ppmCoprimes[0], ppeCrtRemainders[0], peTmp, peTmp, pbScratch, cbScratch );                 // (r0 - r1) mod p
+        SymCryptModMul( ppmCoprimes[0], ppeCrtInverses[0], peTmp, peTmp, pbScratch, cbScratch );                   // q^{-1}*(r0 - r1) mod p
+        SymCryptModElementToInt( ppmCoprimes[0], peTmp, piTmp, pbScratch, cbScratch );                             // Convert it to integer
+
+        SymCryptIntMulMixedSize( piTmp, SymCryptIntFromModulus((PSYMCRYPT_MODULUS)ppmCoprimes[1]), piDouble, pbScratch, cbScratch );    // Multiply by q
+        scError = SymCryptIntCopyMixedSize( piDouble, piSolution );                                                 // Copy it into the solution
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        SymCryptModElementToInt( ppmCoprimes[1], ppeCrtRemainders[1], piTmp, pbScratch, cbScratch );                // Convert r1 to integer
+
+        carry = SymCryptIntAddMixedSize( piTmp, piSolution, piSolution );                                           // Add it to the solution
+
+        if (carry>0)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+    else
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/cshake_pattern.c b/libs/symcrypt/lib/cshake_pattern.c
new file mode 100644
index 000000000000..89e346ef7dd8
--- /dev/null
+++ b/libs/symcrypt/lib/cshake_pattern.c
@@ -0,0 +1,152 @@
+//
+// cshake_pattern.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+//
+// This source file implements cSHAKE128 and cSHAKE256
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+//
+// SymCryptCShake
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_Xxx(
+    _In_reads_( cbFunctionNameString )  PCBYTE  pbFunctionNameString,
+                                        SIZE_T  cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE  pbCustomizationString,
+                                        SIZE_T  cbCustomizationString,
+    _In_reads_( cbData )                PCBYTE  pbData,
+                                        SIZE_T  cbData,
+    _Out_writes_( cbResult )            PBYTE   pbResult,
+                                        SIZE_T  cbResult)
+{
+    SYMCRYPT_XXX_STATE state;
+
+    SYMCRYPT_XxxInit(&state,
+                    pbFunctionNameString, cbFunctionNameString,
+                    pbCustomizationString, cbCustomizationString);
+
+    SYMCRYPT_XxxAppend(&state, pbData, cbData);
+    SYMCRYPT_XxxExtract(&state, pbResult, cbResult, TRUE);
+}
+
+
+//
+// SymCryptCShakeInit
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxInit(
+    _Out_                               PSYMCRYPT_XXX_STATE pState,
+    _In_reads_( cbFunctionNameString )  PCBYTE              pbFunctionNameString,
+                                        SIZE_T              cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE              pbCustomizationString,
+                                        SIZE_T              cbCustomizationString)
+{
+    C_ASSERT( sizeof(SYMCRYPT_XXX_STATE) == sizeof(SYMCRYPT_SHAKEXXX_STATE) );
+
+    SYMCRYPT_SHAKEXXX_INIT( (SYMCRYPT_SHAKEXXX_STATE*)pState );
+
+    // Perform cSHAKE processing of input strings when any of the input strings is non-empty
+    if (cbFunctionNameString != 0 || cbCustomizationString != 0)
+    {
+        // cSHAKE and SHAKE have different paddings. pState->paddingValue
+        // is set to SYMCRYPT_SHAKE_PADDING_VALUE in the SHAKE initialization above.
+        // We update the padding value here because at least one of the input strings
+        // is non-empty and cSHAKE will not default to SHAKE.
+        pState->ks.paddingValue = SYMCRYPT_CSHAKE_PADDING_VALUE;
+
+        SymCryptCShakeEncodeInputStrings(&pState->ks,
+                                        pbFunctionNameString, cbFunctionNameString,
+                                        pbCustomizationString, cbCustomizationString);
+    }
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+//
+// SymCryptCShakeAppend
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxAppend(
+    _Inout_                 PSYMCRYPT_XXX_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbData,
+                            SIZE_T              cbData )
+{
+    // Fixing of the padding value
+    //
+    // SymCryptKeccakAppend will reset the state, switch to absorb mode,
+    // and append data to the empty state if the state was in squeeze mode
+    // when Append is called. This behavior is equivalent to initializing
+    // cSHAKE with empty input strings, which makes cSHAKE a SHAKE instance.
+    //
+    // cSHAKE and SHAKE have different paddings, so we have to update the
+    // padding value in case it was cSHAKE padding before.
+    if (pState->ks.squeezeMode)
+    {
+        pState->ks.paddingValue = SYMCRYPT_SHAKE_PADDING_VALUE;
+    }
+
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+//
+// SymCryptCShakeExtract
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxExtract(
+    _Inout_                 PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_(cbResult)  PBYTE               pbResult,
+                            SIZE_T              cbResult,
+                            BOOLEAN             bWipe)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, cbResult, bWipe);
+
+    if (bWipe)
+    {
+        // If the state was wiped, set the state as if cSHAKE was initialized
+        // with empty strings, which is equivalent to empty SHAKE state.
+        // We have no way to store the Function Name string and Customization
+        // string information to go back to the initial cSHAKE state.
+        pState->ks.paddingValue = SYMCRYPT_SHAKE_PADDING_VALUE;
+    }
+}
+
+//
+// SymCryptCShakeResult
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxResult(
+    _Inout_                                         PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_( SYMCRYPT_CSHAKEXXX_RESULT_SIZE )  PBYTE               pbResult)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, SYMCRYPT_CSHAKEXXX_RESULT_SIZE, TRUE);
+
+    // Revert to cSHAKE initialized with empty strings state, i.e., empty SHAKE state
+    pState->ks.paddingValue = SYMCRYPT_SHAKE_PADDING_VALUE;
+}
+
+//
+// SymCryptCShakeStateCopy
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxStateCopy(_In_ const SYMCRYPT_XXX_STATE* pSrc, _Out_ SYMCRYPT_XXX_STATE* pDst)
+{
+    SYMCRYPT_CHECK_MAGIC(pSrc);
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC(pDst);
+}
diff --git a/libs/symcrypt/lib/desx.c b/libs/symcrypt/lib/desx.c
new file mode 100644
index 000000000000..9bfbb0d23f6d
--- /dev/null
+++ b/libs/symcrypt/lib/desx.c
@@ -0,0 +1,131 @@
+//
+// DesX.c   DESX implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+
+#include "precomp.h"
+
+
+const SYMCRYPT_BLOCKCIPHER SymCryptDesxBlockCipher_default = {
+    SymCryptDesxExpandKey,  // PSYMCRYPT_BLOCKCIPHER_EXPAND_KEY    expandKeyFunc;
+    SymCryptDesxEncrypt,    // PSYMCRYPT_BLOCKCIPHER_CRYPT         encryptFunc;
+    SymCryptDesxDecrypt,    // PSYMCRYPT_BLOCKCIPHER_CRYPT         decryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_MAC_MODE      cbcMacFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    ctrMsbFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmEncryptPartFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmDecryptPartFunc;
+    8,                      // SIZE_T                              blockSize;
+    sizeof( SYMCRYPT_DESX_EXPANDED_KEY ), // SIZE_T  expandedKeySize;    // = sizeof( SYMCRYPT_XXX_EXPANDED_KEY )
+};
+
+const PCSYMCRYPT_BLOCKCIPHER SymCryptDesxBlockCipher = &SymCryptDesxBlockCipher_default;
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDesxExpandKey(   _Out_              PSYMCRYPT_DESX_EXPANDED_KEY  pExpandedKey,
+                        _In_reads_(cbKey)  PCBYTE                      pbKey,
+                                            SIZE_T                      cbKey )
+{
+    if( cbKey != 24 )
+    {
+        return SYMCRYPT_WRONG_KEY_SIZE;
+    }
+
+    SymCryptDesExpandKey( &pExpandedKey->desKey, pbKey, 8 );
+    memcpy( pExpandedKey->inputWhitening, pbKey+8, 8 );
+    memcpy( pExpandedKey->outputWhitening, pbKey+16, 8 );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesxEncrypt(
+    _In_                                        PCSYMCRYPT_DESX_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_DESX_BLOCK_SIZE )     PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_DESX_BLOCK_SIZE )    PBYTE                           pbDst )
+{
+    SYMCRYPT_ALIGN BYTE buf[8];
+
+    //
+    // We buffer the result locally to obey the read once/write once rule.
+    //
+    SymCryptXorBytes( pbSrc, pExpandedKey->inputWhitening, buf, 8 );
+    SymCryptDesEncrypt( &pExpandedKey->desKey, buf, buf );
+    SymCryptXorBytes( buf, pExpandedKey->outputWhitening, pbDst, 8 );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesxDecrypt(
+    _In_                                        PCSYMCRYPT_DESX_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( SYMCRYPT_DESX_BLOCK_SIZE )     PCBYTE                          pbSrc,
+    _Out_writes_( SYMCRYPT_DESX_BLOCK_SIZE )    PBYTE                           pbDst )
+{
+    SYMCRYPT_ALIGN BYTE buf[8];
+
+    //
+    // We buffer the result locally to obey the read once/write once rule.
+    //
+    SymCryptXorBytes( pbSrc, pExpandedKey->outputWhitening, buf, 8 );
+    SymCryptDesDecrypt( &pExpandedKey->desKey, buf, buf );
+    SymCryptXorBytes( buf, pExpandedKey->inputWhitening, pbDst, 8 );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+}
+
+
+static const BYTE desxKnownKey[24] = {
+    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+    0x01, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
+};
+
+static const BYTE desxKnownPlaintext[] = {
+    0xd9, 0xb6, 0xa1, 0x4e, 0xe6, 0x71, 0x4e, 0x17
+};
+
+static const BYTE desxKnownCiphertext[] = {
+    0x66, 0x77, 0x1f, 0x2a, 0x0c, 0x05, 0x01, 0xca
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptDesxSelftest(void)
+{
+    SYMCRYPT_DESX_EXPANDED_KEY key;
+    BYTE buf[SYMCRYPT_DESX_BLOCK_SIZE];
+
+    if( SymCryptDesxExpandKey( &key, desxKnownKey, sizeof( desxKnownKey )) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'desx' );
+    }
+
+    SymCryptDesxEncrypt( &key, desxKnownPlaintext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_DESX_BLOCK_SIZE );
+
+    if( memcmp( buf, desxKnownCiphertext, SYMCRYPT_DESX_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'desy' );
+    }
+
+    SymCryptDesxDecrypt( &key, desxKnownCiphertext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_DESX_BLOCK_SIZE );
+
+    if( memcmp( buf, desxKnownPlaintext, SYMCRYPT_DESX_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'desz' );
+    }
+
+}
diff --git a/libs/symcrypt/lib/dh.c b/libs/symcrypt/lib/dh.c
new file mode 100644
index 000000000000..9f2a3796aa79
--- /dev/null
+++ b/libs/symcrypt/lib/dh.c
@@ -0,0 +1,141 @@
+//
+// dh.c   DH functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDhSecretAgreement(
+    _In_                            PCSYMCRYPT_DLKEY        pkPrivate,
+    _In_                            PCSYMCRYPT_DLKEY        pkPublic,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+                                    UINT32                  flags,
+    _Out_writes_( cbAgreedSecret )  PBYTE                   pbAgreedSecret,
+                                    SIZE_T                  cbAgreedSecret )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+    PBYTE   pbScratchInternal = NULL;
+    SIZE_T  cbScratchInternal = 0;
+
+    PCSYMCRYPT_DLGROUP  pDlgroup = NULL;
+
+    PSYMCRYPT_MODELEMENT peRes = NULL;
+    UINT32 cbModelement = 0;
+
+    UINT32 nBitsOfExp = 0;
+
+    // Make sure that the keys may be used in DH
+    if ( ((pkPrivate->fAlgorithmInfo & SYMCRYPT_FLAG_DLKEY_DH) == 0) ||
+         ((pkPublic->fAlgorithmInfo & SYMCRYPT_FLAG_DLKEY_DH) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure we only specify the correct flags and that
+    // there is a private key
+    if ( (flags != 0) || (!pkPrivate->fHasPrivateKey) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check that the group is the same for both keys
+    if ( SymCryptDlgroupIsSame( pkPrivate->pDlgroup, pkPublic->pDlgroup ) )
+    {
+        pDlgroup = pkPrivate->pDlgroup;
+    }
+    else
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check the output buffer has the correct size
+    if (cbAgreedSecret != SymCryptDlkeySizeofPublicKey( pkPrivate ))
+    {
+        scError = SYMCRYPT_WRONG_BLOCK_SIZE;
+        goto cleanup;
+    }
+
+    // Objects and scratch space size calculation
+    cbModelement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+    cbScratch = cbModelement +
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pDlgroup->nDigitsOfP ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pDlgroup->nDigitsOfP ));
+
+    // Scratch space allocation
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Creating temporary
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = cbScratch;
+    peRes = SymCryptModElementCreate( pbScratchInternal, cbModelement, pDlgroup->pmP );
+    pbScratchInternal += cbModelement;
+    cbScratchInternal -= cbModelement;
+
+    SYMCRYPT_ASSERT( peRes != NULL);
+
+    // Fix the bits of the exponent (the private key might be either mod Q, mod 2^nBitsPriv, or mod P)
+    if (pkPrivate->fPrivateModQ)
+    {
+        nBitsOfExp = pkPrivate->nBitsPriv;
+    }
+    else
+    {
+        nBitsOfExp = pDlgroup->nBitsOfP;
+    }
+
+    // Calculate the secret
+    SymCryptModExp(
+            pDlgroup->pmP,
+            pkPublic->pePublicKey,
+            pkPrivate->piPrivateKey,
+            nBitsOfExp,
+            0,              // SC safe
+            peRes,
+            pbScratchInternal,
+            cbScratchInternal );
+
+    // Check if the result is zero
+    if ( SymCryptModElementIsZero( pDlgroup->pmP, peRes ) )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Output the result
+    scError = SymCryptModElementGetValue(
+            pDlgroup->pmP,
+            peRes,
+            pbAgreedSecret,
+            cbAgreedSecret,
+            format,
+            pbScratchInternal,
+            cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/dl_internal_groups.c b/libs/symcrypt/lib/dl_internal_groups.c
new file mode 100644
index 000000000000..3476407c5ca4
--- /dev/null
+++ b/libs/symcrypt/lib/dl_internal_groups.c
@@ -0,0 +1,922 @@
+//
+// dl_internal_groups.c   Parameters for internally supported dl groups.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Do not delete the following preprocessor directive.
+// It is used for folding the parameters.
+#if 1
+
+/***********************************
+ *                                 *
+ *      IKE GROUPS (RFC 3526)      *
+ *                                 *
+ ***********************************/
+
+static const BYTE rgbIke3526Modp2048[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbIke3526Modp3072[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+    0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+    0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+    0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+    0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+    0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+    0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+    0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+    0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+    0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+    0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+    0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+    0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+    0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+    0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+    0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+    0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbIke3526Modp4096[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+    0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+    0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+    0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+    0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+    0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+    0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+    0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+    0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+    0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+    0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+    0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+    0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+    0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+    0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+    0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+    0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+    0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+    0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+    0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+    0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+    0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+    0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+    0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+    0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+    0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+    0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+    0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+    0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+    0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+    0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+    0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+    0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbIke3526Modp6144[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+    0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+    0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+    0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+    0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+    0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+    0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+    0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+    0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+    0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+    0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+    0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+    0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+    0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+    0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+    0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+    0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+    0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+    0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+    0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+    0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+    0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+    0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+    0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+    0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+    0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+    0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+    0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+    0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+    0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+    0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+    0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+    0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+    0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+    0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+    0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+    0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+    0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+    0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+    0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+    0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+    0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+    0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+    0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+    0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+    0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+    0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+    0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+    0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+    0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+    0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+    0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+    0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+    0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+    0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+    0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+    0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+    0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+    0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+    0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+    0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+    0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+    0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+    0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+    0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const
+BYTE rgbIke3526Modp8192[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+    0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+    0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+    0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+    0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+    0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+    0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+    0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+    0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+    0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+    0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+    0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+    0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+    0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+    0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+    0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+    0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+    0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+    0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+    0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+    0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+    0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+    0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+    0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+    0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+    0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+    0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+    0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+    0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+    0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+    0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+    0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+    0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+    0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+    0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+    0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+    0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+    0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+    0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+    0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+    0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+    0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+    0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+    0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+    0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+    0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+    0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+    0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+    0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+    0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+    0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+    0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+    0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+    0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+    0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+    0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+    0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+    0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+    0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+    0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+    0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+    0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+    0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+    0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+    0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
+    0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+    0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
+    0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
+    0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+    0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
+    0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
+    0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+    0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
+    0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
+    0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+    0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
+    0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
+    0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+    0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
+    0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
+    0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+    0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
+    0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
+    0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+    0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
+    0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
+    0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+    0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
+    0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
+    0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+    0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
+    0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
+    0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+    0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
+    0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
+    0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+    0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/*******************************
+*                              *
+*     TLS GROUPS (RFC 7919)    *
+*                              *
+********************************/
+
+static const BYTE rgbTls7919ffdhe2048[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
+    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
+    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
+    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
+    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
+    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
+    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
+    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
+    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
+    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
+    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
+    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
+    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
+    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
+    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
+    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
+    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
+    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
+    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
+    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
+    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
+    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
+    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
+    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
+    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
+    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
+    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
+    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
+    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
+    0x88, 0x6B, 0x42, 0x38, 0x61, 0x28, 0x5C, 0x97,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbTls7919ffdhe3072[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
+    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
+    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
+    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
+    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
+    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
+    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
+    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
+    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
+    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
+    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
+    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
+    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
+    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
+    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
+    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
+    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
+    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
+    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
+    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
+    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
+    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
+    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
+    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
+    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
+    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
+    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
+    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
+    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
+    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
+    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
+    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
+    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
+    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
+    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
+    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
+    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
+    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
+    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
+    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
+    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
+    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
+    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
+    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
+    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
+    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0xC6, 0x2E, 0x37,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbTls7919ffdhe4096[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
+    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
+    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
+    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
+    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
+    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
+    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
+    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
+    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
+    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
+    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
+    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
+    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
+    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
+    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
+    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
+    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
+    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
+    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
+    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
+    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
+    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
+    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
+    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
+    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
+    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
+    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
+    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
+    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
+    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
+    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
+    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
+    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
+    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
+    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
+    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
+    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
+    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
+    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
+    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
+    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
+    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
+    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
+    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
+    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
+    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1,
+    0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB,
+    0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6,
+    0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18,
+    0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04,
+    0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A,
+    0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A,
+    0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32,
+    0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4,
+    0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38,
+    0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A,
+    0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C,
+    0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC,
+    0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF,
+    0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B,
+    0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1,
+    0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x65, 0x5F, 0x6A,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbTls7919ffdhe6144[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
+    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
+    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
+    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
+    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
+    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
+    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
+    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
+    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
+    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
+    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
+    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
+    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
+    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
+    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
+    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
+    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
+    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
+    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
+    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
+    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
+    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
+    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
+    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
+    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
+    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
+    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
+    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
+    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
+    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
+    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
+    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
+    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
+    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
+    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
+    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
+    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
+    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
+    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
+    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
+    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
+    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
+    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
+    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
+    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
+    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1,
+    0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB,
+    0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6,
+    0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18,
+    0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04,
+    0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A,
+    0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A,
+    0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32,
+    0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4,
+    0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38,
+    0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A,
+    0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C,
+    0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC,
+    0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF,
+    0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B,
+    0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1,
+    0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02,
+    0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A,
+    0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A,
+    0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6,
+    0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8,
+    0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C,
+    0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A,
+    0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71,
+    0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F,
+    0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77,
+    0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10,
+    0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8,
+    0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3,
+    0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E,
+    0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3,
+    0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4,
+    0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1,
+    0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92,
+    0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6,
+    0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82,
+    0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE,
+    0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C,
+    0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E,
+    0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46,
+    0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A,
+    0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17,
+    0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03,
+    0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04,
+    0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6,
+    0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69,
+    0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1,
+    0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4,
+    0xA4, 0x0E, 0x32, 0x9C, 0xD0, 0xE4, 0x0E, 0x65,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const BYTE rgbTls7919ffdhe8192[] = {
+    //P
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A,
+    0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1,
+    0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95,
+    0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
+    0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9,
+    0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8,
+    0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A,
+    0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61,
+    0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
+    0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3,
+    0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35,
+    0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77,
+    0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72,
+    0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
+    0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A,
+    0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61,
+    0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB,
+    0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68,
+    0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
+    0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19,
+    0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70,
+    0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC,
+    0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61,
+    0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
+    0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83,
+    0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73,
+    0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05,
+    0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2,
+    0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
+    0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC,
+    0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B,
+    0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38,
+    0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07,
+    0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE,
+    0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C,
+    0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70,
+    0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44,
+    0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3,
+    0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF,
+    0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E,
+    0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D,
+    0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA,
+    0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E,
+    0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF,
+    0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C,
+    0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1,
+    0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB,
+    0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6,
+    0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18,
+    0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04,
+    0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A,
+    0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A,
+    0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32,
+    0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4,
+    0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38,
+    0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A,
+    0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C,
+    0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC,
+    0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF,
+    0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B,
+    0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1,
+    0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02,
+    0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A,
+    0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A,
+    0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6,
+    0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8,
+    0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C,
+    0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A,
+    0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71,
+    0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F,
+    0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77,
+    0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10,
+    0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8,
+    0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3,
+    0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E,
+    0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3,
+    0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4,
+    0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1,
+    0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92,
+    0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6,
+    0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82,
+    0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE,
+    0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C,
+    0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E,
+    0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46,
+    0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A,
+    0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17,
+    0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03,
+    0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04,
+    0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6,
+    0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69,
+    0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1,
+    0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4,
+    0xA4, 0x0E, 0x32, 0x9C, 0xCF, 0xF4, 0x6A, 0xAA,
+    0x36, 0xAD, 0x00, 0x4C, 0xF6, 0x00, 0xC8, 0x38,
+    0x1E, 0x42, 0x5A, 0x31, 0xD9, 0x51, 0xAE, 0x64,
+    0xFD, 0xB2, 0x3F, 0xCE, 0xC9, 0x50, 0x9D, 0x43,
+    0x68, 0x7F, 0xEB, 0x69, 0xED, 0xD1, 0xCC, 0x5E,
+    0x0B, 0x8C, 0xC3, 0xBD, 0xF6, 0x4B, 0x10, 0xEF,
+    0x86, 0xB6, 0x31, 0x42, 0xA3, 0xAB, 0x88, 0x29,
+    0x55, 0x5B, 0x2F, 0x74, 0x7C, 0x93, 0x26, 0x65,
+    0xCB, 0x2C, 0x0F, 0x1C, 0xC0, 0x1B, 0xD7, 0x02,
+    0x29, 0x38, 0x88, 0x39, 0xD2, 0xAF, 0x05, 0xE4,
+    0x54, 0x50, 0x4A, 0xC7, 0x8B, 0x75, 0x82, 0x82,
+    0x28, 0x46, 0xC0, 0xBA, 0x35, 0xC3, 0x5F, 0x5C,
+    0x59, 0x16, 0x0C, 0xC0, 0x46, 0xFD, 0x82, 0x51,
+    0x54, 0x1F, 0xC6, 0x8C, 0x9C, 0x86, 0xB0, 0x22,
+    0xBB, 0x70, 0x99, 0x87, 0x6A, 0x46, 0x0E, 0x74,
+    0x51, 0xA8, 0xA9, 0x31, 0x09, 0x70, 0x3F, 0xEE,
+    0x1C, 0x21, 0x7E, 0x6C, 0x38, 0x26, 0xE5, 0x2C,
+    0x51, 0xAA, 0x69, 0x1E, 0x0E, 0x42, 0x3C, 0xFC,
+    0x99, 0xE9, 0xE3, 0x16, 0x50, 0xC1, 0x21, 0x7B,
+    0x62, 0x48, 0x16, 0xCD, 0xAD, 0x9A, 0x95, 0xF9,
+    0xD5, 0xB8, 0x01, 0x94, 0x88, 0xD9, 0xC0, 0xA0,
+    0xA1, 0xFE, 0x30, 0x75, 0xA5, 0x77, 0xE2, 0x31,
+    0x83, 0xF8, 0x1D, 0x4A, 0x3F, 0x2F, 0xA4, 0x57,
+    0x1E, 0xFC, 0x8C, 0xE0, 0xBA, 0x8A, 0x4F, 0xE8,
+    0xB6, 0x85, 0x5D, 0xFE, 0x72, 0xB0, 0xA6, 0x6E,
+    0xDE, 0xD2, 0xFB, 0xAB, 0xFB, 0xE5, 0x8A, 0x30,
+    0xFA, 0xFA, 0xBE, 0x1C, 0x5D, 0x71, 0xA8, 0x7E,
+    0x2F, 0x74, 0x1E, 0xF8, 0xC1, 0xFE, 0x86, 0xFE,
+    0xA6, 0xBB, 0xFD, 0xE5, 0x30, 0x67, 0x7F, 0x0D,
+    0x97, 0xD1, 0x1D, 0x49, 0xF7, 0xA8, 0x44, 0x3D,
+    0x08, 0x22, 0xE5, 0x06, 0xA9, 0xF4, 0x61, 0x4E,
+    0x01, 0x1E, 0x2A, 0x94, 0x83, 0x8F, 0xF8, 0x8C,
+    0xD6, 0x8C, 0x8B, 0xB7, 0xC5, 0xC6, 0x42, 0x4C,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif  // 1
+
+// Definitions
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsModp2048 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_IKE_3526,
+    .pcbPrimeP = rgbIke3526Modp2048,
+    .nBitsOfP = 2048,
+    .nMinBitsPriv = 224,    // 2s = 2 * 112
+    .nDefaultBitsPriv = 256 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsModp3072 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_IKE_3526,
+    .pcbPrimeP = rgbIke3526Modp3072,
+    .nBitsOfP = 3072,
+    .nMinBitsPriv = 256,    // 2s = 2 * 128
+    .nDefaultBitsPriv = 256 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsModp4096 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_IKE_3526,
+    .pcbPrimeP = rgbIke3526Modp4096,
+    .nBitsOfP = 4096,
+    .nMinBitsPriv = 304,    // 2s = 2 * 152
+    .nDefaultBitsPriv = 384 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsModp6144 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_IKE_3526,
+    .pcbPrimeP = rgbIke3526Modp6144,
+    .nBitsOfP = 6144,
+    .nMinBitsPriv = 352,    // 2s = 2 * 176
+    .nDefaultBitsPriv = 384 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsModp8192 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_IKE_3526,
+    .pcbPrimeP = rgbIke3526Modp8192,
+    .nBitsOfP = 8192,
+    .nMinBitsPriv = 400,    // 2s = 2 * 200
+    .nDefaultBitsPriv = 512 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsffdhe2048 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919,
+    .pcbPrimeP = rgbTls7919ffdhe2048,
+    .nBitsOfP = 2048,
+    .nMinBitsPriv = 224,    // 2s = 2 * 112
+    .nDefaultBitsPriv = 256 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsffdhe3072 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919,
+    .pcbPrimeP = rgbTls7919ffdhe3072,
+    .nBitsOfP = 3072,
+    .nMinBitsPriv = 256,    // 2s = 2 * 128
+    .nDefaultBitsPriv = 256 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsffdhe4096 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919,
+    .pcbPrimeP = rgbTls7919ffdhe4096,
+    .nBitsOfP = 4096,
+    .nMinBitsPriv = 304,    // 2s = 2 * 152
+    .nDefaultBitsPriv = 384 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsffdhe6144 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919,
+    .pcbPrimeP = rgbTls7919ffdhe6144,
+    .nBitsOfP = 6144,
+    .nMinBitsPriv = 352,    // 2s = 2 * 176
+    .nDefaultBitsPriv = 384 // rounding nMinBitsPriv up to the nearest 128
+};
+
+static const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS paramsffdhe8192 =
+{
+    .eDhSafePrimeType = SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_TLS_7919,
+    .pcbPrimeP = rgbTls7919ffdhe8192,
+    .nBitsOfP = 8192,
+    .nMinBitsPriv = 400,    // 2s = 2 * 200
+    .nDefaultBitsPriv = 512 // rounding nMinBitsPriv up to the nearest 128
+};
+
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp2048 = &paramsModp2048;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp3072 = &paramsModp3072;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp4096 = &paramsModp4096;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp6144 = &paramsModp6144;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp8192 = &paramsModp8192;
+
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe2048 = &paramsffdhe2048;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe3072 = &paramsffdhe3072;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe4096 = &paramsffdhe4096;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe6144 = &paramsffdhe6144;
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe8192 = &paramsffdhe8192;
+
+// Note, we rely on the ordering of the parameters from largest to smallest within each named set of
+// safe-prime groups as we iterate through them assuming this order in SymCryptDlgroupSetValueSafePrime
+const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptNamedSafePrimeGroups[SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT] =
+{
+    &paramsModp8192,
+    &paramsModp6144,
+    &paramsModp4096,
+    &paramsModp3072,
+    &paramsModp2048,
+    &paramsffdhe8192,
+    &paramsffdhe6144,
+    &paramsffdhe4096,
+    &paramsffdhe3072,
+    &paramsffdhe2048,
+};
diff --git a/libs/symcrypt/lib/dlgroup.c b/libs/symcrypt/lib/dlgroup.c
new file mode 100644
index 000000000000..021c0e0145e0
--- /dev/null
+++ b/libs/symcrypt/lib/dlgroup.c
@@ -0,0 +1,2016 @@
+//
+// dlgroup.c   Dlgroup functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Miller-Rabin iterations for prime generation
+#define DLGROUP_MR_ITERATIONS       (64)
+
+// Default size for Q according to FIPS 186-3
+static const struct _DSA_NBITSOFQ_CUTOFFS {
+    UINT32 nBitsOfP;
+    UINT32 nBitsOfQ;
+} g_nBitsOfQ_Cutoffs[] = {
+    { 1024, 160 },
+    { 2048, 256 },
+    { UINT32_MAX, 256 },
+};
+
+// Const label for the generation of generator G according to FIPS 186-3
+static const BYTE ggen[] = { 'g', 'g', 'e', 'n' };
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDlgroupCalculateBitsizeOfQ( UINT32 nBitsOfP )
+{
+    UINT32 i = 0;
+    while ( (i<SYMCRYPT_ARRAY_SIZE(g_nBitsOfQ_Cutoffs) - 1) &&
+            (g_nBitsOfQ_Cutoffs[i].nBitsOfP < nBitsOfP) )
+    {
+        i++;
+    };
+
+    return g_nBitsOfQ_Cutoffs[i].nBitsOfQ;
+}
+
+PSYMCRYPT_DLGROUP
+SYMCRYPT_CALL
+SymCryptDlgroupAllocate( UINT32  nBitsOfP, UINT32  nBitsOfQ )
+{
+    PVOID               p;
+    SIZE_T              cb;
+    PSYMCRYPT_DLGROUP   res = NULL;
+
+    // Invalid parameters
+    if ( (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P) ||
+         ((nBitsOfQ > 0) && (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q)) ||
+         (nBitsOfP < nBitsOfQ) )
+    {
+        goto cleanup;
+    }
+
+    cb = SymCryptSizeofDlgroupFromBitsizes( nBitsOfP, nBitsOfQ );
+
+    p = SymCryptCallbackAlloc( cb );
+
+    if ( p==NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptDlgroupCreate( p, cb, nBitsOfP, nBitsOfQ );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupFree( _Out_ PSYMCRYPT_DLGROUP pgObj )
+{
+    SYMCRYPT_CHECK_MAGIC( pgObj );
+    SymCryptDlgroupWipe( pgObj );
+    SymCryptCallbackFree( pgObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofDlgroupFromBitsizes( UINT32 nBitsOfP, UINT32 nBitsOfQ )
+{
+    UINT32 cbSeed = 0;
+
+    if (nBitsOfQ == 0)
+    {
+        nBitsOfQ = nBitsOfP-1;        // Default to the maximum possible size for Q
+    }
+
+    // Invalid parameters
+    if ( (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P) ||
+         (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q) ||
+         (nBitsOfP < nBitsOfQ) )
+    {
+        return 0;
+    }
+
+    if ( nBitsOfP == nBitsOfQ )
+    {
+        nBitsOfQ--;
+    }
+
+    // Calculate the (tight) bytesize of the seed
+    cbSeed = (nBitsOfQ+7)/8;
+
+    return  sizeof(SYMCRYPT_DLGROUP) +
+            SYMCRYPT_SIZEOF_MODULUS_FROM_BITS( nBitsOfP ) +
+            SYMCRYPT_SIZEOF_MODULUS_FROM_BITS( nBitsOfQ ) +
+            SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( nBitsOfP ) +
+            ((cbSeed + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE)*SYMCRYPT_ASYM_ALIGN_VALUE;    // Make sure that the entire structure is ASYM_ALIGNED.
+}
+
+PSYMCRYPT_DLGROUP
+SYMCRYPT_CALL
+SymCryptDlgroupCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    UINT32              nBitsOfP,
+                                    UINT32              nBitsOfQ )
+{
+    PSYMCRYPT_DLGROUP       pDlgroup = NULL;
+
+    UINT32                  cbModP;
+    UINT32                  cbModQ;
+    UINT32                  cbModElement;
+
+    SYMCRYPT_ASSERT( cbBuffer >= SymCryptSizeofDlgroupFromBitsizes( nBitsOfP, nBitsOfQ ) );
+    UNREFERENCED_PARAMETER( cbBuffer );     // only referenced in ASSERTs...
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    // Invalid parameters
+    if ( (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P) ||
+         ((nBitsOfQ > 0) && (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q)) ||
+         (nBitsOfP < nBitsOfQ) )
+    {
+        goto cleanup;
+    }
+
+    if ( nBitsOfP == nBitsOfQ )
+    {
+        nBitsOfQ--;
+    }
+
+    pDlgroup = (PSYMCRYPT_DLGROUP) pbBuffer;
+
+    SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) );
+
+    // DLGROUP parameters
+    pDlgroup->cbTotalSize = SymCryptSizeofDlgroupFromBitsizes( nBitsOfP, nBitsOfQ );
+    pDlgroup->fHasPrimeQ = FALSE;
+
+    pDlgroup->nBitsOfP = nBitsOfP;
+    pDlgroup->cbPrimeP = (nBitsOfP+7)/8;
+    pDlgroup->nDigitsOfP = SymCryptDigitsFromBits( nBitsOfP );
+    pDlgroup->nMaxBitsOfP = nBitsOfP;
+
+    pDlgroup->nBitsOfQ = nBitsOfQ;                                             // 0 value possible
+    pDlgroup->cbPrimeQ = (nBitsOfQ+7)/8;                                       // 0 value possible
+    pDlgroup->nDigitsOfQ = (nBitsOfQ>0)?SymCryptDigitsFromBits( nBitsOfQ ):0;  // 0 value possible
+    pDlgroup->nMaxBitsOfQ = (nBitsOfQ==0)?(nBitsOfP-1):nBitsOfQ;
+
+    pDlgroup->isSafePrimeGroup = FALSE;
+    pDlgroup->nMinBitsPriv = 0;
+    pDlgroup->nDefaultBitsPriv = nBitsOfQ;                                     // 0 value possible
+
+    pDlgroup->nBitsOfSeed = nBitsOfQ;                                          // 0 value possible
+    pDlgroup->cbSeed = (pDlgroup->nBitsOfSeed+7)/8;                            // 0 value possible
+
+    pDlgroup->eFipsStandard = SYMCRYPT_DLGROUP_FIPS_NONE;       // This will be set either on generate or import
+    pDlgroup->pHashAlgorithm = NULL;            // Like-wise
+    pDlgroup->dwGenCounter = 0;                 // Like-wise
+    pDlgroup->bIndexGenG = 1;                   // Default: 1
+
+    // Create SymCrypt objects
+    pbBuffer += sizeof(SYMCRYPT_DLGROUP);
+
+    cbModP = SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfP );
+    SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) + cbModP );
+    pDlgroup->pmP = SymCryptModulusCreate( pbBuffer, cbModP, pDlgroup->nDigitsOfP );
+    pbBuffer += cbModP;
+
+    //
+    // **** Always defer the creation of the Q modulus until the group generation or
+    // import of the modulus. This way it is always the fastest possible even when the caller
+    // specified nBitsOfQ = 0.
+    //
+    if (nBitsOfQ>0)
+    {
+        cbModQ = SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ );
+    }
+    else
+    {
+        cbModQ = cbModP;
+    }
+    SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) + cbModP + cbModQ );
+    pDlgroup->pbQ = pbBuffer;      // Set the aligned buffer
+    pDlgroup->pmQ = NULL;
+    pbBuffer += cbModQ;
+
+    cbModElement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+    SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) + cbModP + cbModQ + cbModElement );
+    pDlgroup->peG = SymCryptModElementCreate( pbBuffer, cbModElement, pDlgroup->pmP );
+    pbBuffer += cbModElement;
+
+    pDlgroup->pbSeed = pbBuffer;
+
+    // Setting the magic
+    SYMCRYPT_SET_MAGIC( pDlgroup );
+
+cleanup:
+    return pDlgroup;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupWipe( _Out_ PSYMCRYPT_DLGROUP pgDst )
+{
+    SymCryptWipe( (PBYTE) pgDst, pgDst->cbTotalSize );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupCopy(
+    _In_    PCSYMCRYPT_DLGROUP   pgSrc,
+    _Out_   PSYMCRYPT_DLGROUP    pgDst )
+{
+    //
+    // in-place copy is somewhat common...
+    //
+    if( pgSrc != pgDst )
+    {
+        pgDst->cbTotalSize = pgSrc->cbTotalSize;
+        pgDst->fHasPrimeQ = pgSrc->fHasPrimeQ;
+
+        pgDst->nBitsOfP = pgSrc->nBitsOfP;
+        pgDst->cbPrimeP = pgSrc->cbPrimeP;
+        pgDst->nDigitsOfP = pgSrc->nDigitsOfP;
+        pgDst->nMaxBitsOfP = pgSrc->nMaxBitsOfP;
+
+        pgDst->nBitsOfQ = pgSrc->nBitsOfQ;
+        pgDst->cbPrimeQ = pgSrc->cbPrimeQ;
+        pgDst->nDigitsOfQ = pgSrc->nDigitsOfQ;
+        pgDst->nMaxBitsOfQ = pgSrc->nMaxBitsOfQ;
+
+        pgDst->isSafePrimeGroup = pgSrc->isSafePrimeGroup;
+        pgDst->nMinBitsPriv = pgSrc->nMinBitsPriv;
+        pgDst->nDefaultBitsPriv = pgSrc->nDefaultBitsPriv;
+
+        pgDst->nBitsOfSeed = pgSrc->nBitsOfSeed;
+        pgDst->cbSeed = pgSrc->cbSeed;
+
+        pgDst->eFipsStandard = pgSrc->eFipsStandard;
+        pgDst->pHashAlgorithm = pgSrc->pHashAlgorithm;
+        pgDst->dwGenCounter = pgSrc->dwGenCounter;
+        pgDst->bIndexGenG = pgSrc->bIndexGenG;
+        pgDst->pbQ = pgSrc->pbQ;
+
+        memcpy( (PBYTE)pgDst + sizeof(SYMCRYPT_DLGROUP), (PCBYTE)pgSrc + sizeof(SYMCRYPT_DLGROUP), pgSrc->cbTotalSize - sizeof(SYMCRYPT_DLGROUP) );
+    }
+}
+
+
+// DLGROUP-specific functions
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGeneratePrimeQ_FIPS(
+    _In_    PSYMCRYPT_DLGROUP   pDlgroup,
+    _In_    PCSYMCRYPT_TRIALDIVISION_CONTEXT
+                                pTrialDivisionContext,
+    _Out_   PUINT32             pfPrimeQFound,
+    _Out_   PSYMCRYPT_INT       piQ,
+    _Out_   PSYMCRYPT_DIVISOR   pdDivTwoQ,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_HASH hashAlgorithm = pDlgroup->pHashAlgorithm;
+    UINT32 nBitsOfQ = pDlgroup->nBitsOfQ;
+    UINT32 cbPrimeQ = pDlgroup->cbPrimeQ;
+    PBYTE pbSeed = pDlgroup->pbSeed;
+    UINT32 cbSeed = pDlgroup->cbSeed;
+
+    PSYMCRYPT_INT piDivTwoQ = SymCryptIntFromDivisor(pdDivTwoQ);
+
+    SIZE_T cbHash = SymCryptHashResultSize( hashAlgorithm );
+    PBYTE pbTrHash = NULL;          // Pointer to the truncated hash value
+    PBYTE pbHashExtra = NULL;       // Needed as temp buffer for 186-2
+
+    UINT32 dwShiftBits = (8-nBitsOfQ%8)%8;              // When nBitsOfQ is a multiple of 8 -> dwShiftBits = 0;
+
+    UINT32 carry = 0;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR(SymCryptDigitsFromBits(nBitsOfQ+1)),
+                                  SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME(pDlgroup->nDigitsOfQ),
+                                       2 * cbHash )) );
+    SYMCRYPT_ASSERT( cbHash >= cbPrimeQ );
+
+    // Hash the seed according to the standard specified
+    if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
+    {
+        SYMCRYPT_ASSERT( hashAlgorithm == SymCryptSha1Algorithm );
+        SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_MAX(2*cbHash, cbSeed) );
+
+        // Hash buffers
+        pbTrHash = pbScratch;
+        pbHashExtra = pbTrHash + cbHash;
+
+        // Prepare an int for SEED + 1
+        scError = SymCryptIntSetValue( pbSeed, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piDivTwoQ );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // Add 1
+        carry = SymCryptIntAddUint32( piDivTwoQ, 1, piDivTwoQ );
+        if (carry > 0)
+        {
+            // This should never happen as the size of piDivTwoQ is at least one bit bigger than Q
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        // (SEED+1) Mod 2^nBitsOfSeed
+        SymCryptIntModPow2( piDivTwoQ, nBitsOfQ, piDivTwoQ );
+
+        // Get the value into pbTrHash (Notice the cbSeed size)
+        scError = SymCryptIntGetValue( piDivTwoQ, pbTrHash, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // Hash it into pbHashExtra
+        SymCryptHash( hashAlgorithm, pbTrHash, cbPrimeQ, pbHashExtra, cbHash );
+
+        // Hash the seed
+        SymCryptHash( hashAlgorithm, pbSeed, cbSeed, pbTrHash, cbHash );
+
+        // Xor the two
+        SymCryptXorBytes( pbTrHash, pbHashExtra, pbTrHash, cbHash );
+
+    }
+    else if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_3)
+    {
+        SYMCRYPT_ASSERT( cbScratch >= cbHash );
+        pbTrHash = pbScratch;
+        SymCryptHash( hashAlgorithm, pbSeed, cbSeed, pbTrHash, cbHash );
+    }
+    else
+    {
+        scError = SYMCRYPT_FIPS_FAILURE;
+        goto cleanup;
+    }
+
+    // Convert it to (2^{N-1} + (Hash mod 2^{N-1})) | 1
+    pbTrHash += (cbHash-cbPrimeQ);                      // Skip any leading zero bytes
+    pbTrHash[0] &= ((BYTE)0xff >> (dwShiftBits));       // Cut off top bits in the most significant byte
+    pbTrHash[0] |= ((BYTE)0x01 << (7 - dwShiftBits));   // Set the (N-1)-th bit
+    pbTrHash[cbPrimeQ-1] |= ((BYTE)0x01);               // Make the entire number odd
+
+    // Set the value
+    scError = SymCryptIntSetValue( pbTrHash, cbPrimeQ, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piQ );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Assume not a prime
+    *pfPrimeQFound = 0;
+
+    // Fast compositeness check
+    if (SymCryptIntFindSmallDivisor( pTrialDivisionContext, piQ, NULL, 0 ))
+    {
+        goto cleanup;
+    }
+
+    // IntMillerRabinPrimalityTest requirement:
+    //      piQ > 3 since nBitsOfQ is bounded by SYMCRYPT_DLGROUP_MIN_BITSIZE_Q
+    *pfPrimeQFound = SymCryptIntMillerRabinPrimalityTest(
+                        piQ,
+                        nBitsOfQ,
+                        DLGROUP_MR_ITERATIONS,
+                        SYMCRYPT_FLAG_DATA_PUBLIC,      // q and p will be public
+                        pbScratch,
+                        cbScratch );
+
+    // Set pdDivTwoQ
+    if (*pfPrimeQFound)
+    {
+        scError = SymCryptIntCopyMixedSize( piQ, piDivTwoQ );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        SymCryptIntMulPow2( piDivTwoQ, 1, piDivTwoQ );
+
+        // IntToDivisor requirement:
+        //      Q is non-zero as prime --> 2*Q != 0
+        SymCryptIntToDivisor(
+            piDivTwoQ,
+            pdDivTwoQ,
+            4*pDlgroup->nBitsOfP,              // 4*L
+            SYMCRYPT_FLAG_DATA_PUBLIC,
+            pbScratch,
+            cbScratch );
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGeneratePrimeP_FIPS(
+    _In_    PSYMCRYPT_DLGROUP   pDlgroup,
+    _In_    PSYMCRYPT_DIVISOR   pdDivTwoQ,
+    _In_    UINT32              dwMaxCounter,       // Maximum value of counter (used in validation)
+    _In_    PCSYMCRYPT_TRIALDIVISION_CONTEXT
+                                pTrialDivisionContext,
+    _Out_   PUINT32             pfPrimePFound,
+    _Out_   PSYMCRYPT_INT       piP,
+    _Out_   PUINT32             pdwCounter,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_HASH hashAlgorithm = pDlgroup->pHashAlgorithm;
+    UINT32 nBitsOfP = pDlgroup->nBitsOfP;
+    PBYTE pbSeed = pDlgroup->pbSeed;
+    UINT32 cbSeed = pDlgroup->cbSeed;
+    UINT32 nBitsOfSeed = pDlgroup->nBitsOfSeed;
+
+    SIZE_T cbHash = SymCryptHashResultSize( hashAlgorithm );
+
+    UINT32 counter = 0;
+
+    UINT32 ndDivTwoQ = SymCryptDivisorDigitsizeOfObject( pdDivTwoQ );
+    UINT32 cbIntTwoQ = SymCryptSizeofIntFromDigits( ndDivTwoQ );
+
+    PSYMCRYPT_INT piPersistent = NULL;
+    PSYMCRYPT_INT piRemainder = NULL;
+
+    PBYTE pbHashOutput = NULL;
+    PBYTE pbTempSeed = NULL;
+
+    PBYTE pbW = NULL;
+    UINT32 cbW = pDlgroup->cbPrimeP;
+
+    PBYTE pbWCurr = NULL;
+    SIZE_T cbWBytesLeft = 0;
+
+    UINT32 carry = 0;
+
+    // We will use internal scratch space at the start of pbScratch
+    // because cbHash, cbSeed and cbW are not necessarily aligned according
+    // to SYMCRYPT_ASYM_ALIGN_VALUE
+    PBYTE pbScratchInternal = 0;
+    SIZE_T cbScratchInternal = 0;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2*cbIntTwoQ + cbHash + cbSeed + cbW +
+                                  SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( pDlgroup->nDigitsOfP, ndDivTwoQ ),
+                                       SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( pDlgroup->nDigitsOfP )) );
+
+    // Create temporaries
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( pDlgroup->nDigitsOfP, ndDivTwoQ ),
+                             SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( pDlgroup->nDigitsOfP ) );
+    pbScratch += cbScratchInternal;
+
+    piPersistent = SymCryptIntCreate( pbScratch, cbIntTwoQ, ndDivTwoQ );
+    pbScratch += cbIntTwoQ;
+
+    piRemainder = SymCryptIntCreate( pbScratch, cbIntTwoQ, ndDivTwoQ );
+    pbScratch += cbIntTwoQ;
+
+    pbHashOutput = pbScratch;
+    pbScratch += cbHash;
+
+    pbTempSeed = pbScratch;
+    pbScratch += cbSeed;
+
+    pbW = pbScratch;
+
+    // Set the value for the expression "domain_parameter_seed + offset + j"
+    scError = SymCryptIntSetValue( pbSeed, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piPersistent );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // If the standard is 186-2 add 1 since the starting offset is 2
+    if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
+    {
+        carry = SymCryptIntAddUint32( piPersistent, 1, piPersistent );
+        if (carry!=0)
+        {
+            // This should never happen as piPersistent has at least one more bit than
+            // seedLen == nBitsOfQ
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        // Mod 2^seedlen
+        SymCryptIntModPow2( piPersistent, nBitsOfSeed, piPersistent );
+    }
+
+    *pfPrimePFound = 0;
+
+    for (counter = 0; counter < dwMaxCounter+1; counter++)
+    {
+        cbWBytesLeft = cbW;                         // Bytes left to write
+        pbWCurr = pbW + cbW - SYMCRYPT_MIN(cbW,cbHash);      // Position of the first hash chunk to write (if cbW < cbHash then we write only 1 chunk)
+
+        while (cbWBytesLeft > 0)
+        {
+            // Add 1 to piPersistent
+            // This can never generate a carry as piPersistent has at least one more bit than
+            // seedLen == nBitsOfQ and in the next step we always do mod 2^seedlen.
+            carry = SymCryptIntAddUint32( piPersistent, 1, piPersistent );
+            if (carry!=0)
+            {
+                scError = SYMCRYPT_FIPS_FAILURE;
+                goto cleanup;
+            }
+
+            // Mod 2^seedlen
+            SymCryptIntModPow2( piPersistent, nBitsOfSeed, piPersistent );
+
+            // Extract piPersistent into a byte array (this will always be equal to domain_parameter_seed + offset + j)
+            scError = SymCryptIntGetValue( piPersistent, pbTempSeed, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST );
+            if (scError != SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+
+            // Hash it
+            SymCryptHash( hashAlgorithm, pbTempSeed, cbSeed, pbHashOutput, cbHash );
+
+            if (cbWBytesLeft >= cbHash)
+            {
+                // Move the entire hash output to the correct location in the pbW buffer
+                memcpy(pbWCurr, pbHashOutput, cbHash );
+            }
+            else
+            {
+                // Move only the last bytes of the hash output
+                memcpy(pbWCurr, pbHashOutput + cbHash - cbWBytesLeft, cbWBytesLeft );
+            }
+
+            // Update the positions on the W buffer
+            cbWBytesLeft -= SYMCRYPT_MIN(cbHash,cbWBytesLeft);
+            pbWCurr -= SYMCRYPT_MIN(cbHash,cbWBytesLeft);
+        }
+
+        // Import the W buffer into P
+        scError = SymCryptIntSetValue( pbW, cbW, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piP );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // Zero-out the top bits of the integer
+        SymCryptIntModPow2( piP, nBitsOfP, piP );
+
+        // Set the most significant bit
+        SymCryptIntSetBits( piP, 1, nBitsOfP-1, 1);
+
+        // At this point piP = X = W + 2^{L-1}
+
+        // Calculate c = X mod 2Q
+        SymCryptIntDivMod( piP, pdDivTwoQ, NULL, piRemainder, pbScratchInternal, cbScratchInternal );
+
+        if (SymCryptIntIsEqualUint32(piRemainder, 0))
+        {
+            // Just add one to X
+            // We can never get a carry here because the remainder X mod 2Q
+            // is 0. Therefore X is even.
+            carry = SymCryptIntAddUint32( piP, 1, piP );
+            SYMCRYPT_ASSERT( carry==0 );
+        }
+        else
+        {
+            // Subtract 1 from c
+            // We can never get a borrow here because the remainder is not 0.
+            carry = SymCryptIntSubUint32( piRemainder, 1, piRemainder );
+            SYMCRYPT_ASSERT( carry==0 );
+
+            // X-(c-1)
+            // We can never get a borrow here because c is smaller
+            // or equal to X.
+            carry = SymCryptIntSubMixedSize( piP, piRemainder, piP );
+            SYMCRYPT_ASSERT( carry==0 );
+        }
+
+        // Check if smaller than 2^{L-1} by checking the L-1 bit
+        if (SymCryptIntGetBit( piP, nBitsOfP-1 ) == 0)
+        {
+            continue;
+        }
+
+        // Fast compositeness check
+        if (SymCryptIntFindSmallDivisor( pTrialDivisionContext, piP, NULL, 0 ))
+        {
+            continue;
+        }
+
+        // IntMillerRabinPrimalityTest requirement:
+        //      piP > 3 since nBitsOfP is bounded by SYMCRYPT_DLGROUP_MIN_BITSIZE_P
+        *pfPrimePFound = SymCryptIntMillerRabinPrimalityTest(
+                        piP,
+                        nBitsOfP,
+                        DLGROUP_MR_ITERATIONS,
+                        SYMCRYPT_FLAG_DATA_PUBLIC,      // q and p will be public
+                        pbScratchInternal,
+                        cbScratchInternal );
+
+        if (*pfPrimePFound)
+        {
+            *pdwCounter = counter;
+            break;
+        }
+    }
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGenerateGenG_FIPS(
+    _In_    PSYMCRYPT_DLGROUP       pDlgroup,
+    _Out_   PSYMCRYPT_MODELEMENT    peG,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_HASH hashAlgorithm = pDlgroup->pHashAlgorithm;
+    PCSYMCRYPT_MODULUS pmP = pDlgroup->pmP;
+    UINT32 nDigitsOfP = pDlgroup->nDigitsOfP;
+    UINT32 nBitsOfP = pDlgroup->nBitsOfP;
+    PCSYMCRYPT_MODULUS pmQ = pDlgroup->pmQ;
+    UINT32 nDigitsOfQ = pDlgroup->nDigitsOfQ;
+    PBYTE pbSeed = pDlgroup->pbSeed;
+    UINT32 cbSeed = pDlgroup->cbSeed;
+    BYTE bIndexGenG = pDlgroup->bIndexGenG;
+
+    SIZE_T cbHash = SymCryptHashResultSize( hashAlgorithm );
+    SYMCRYPT_ASSERT( cbHash == hashAlgorithm->resultSize );
+    SIZE_T cbState = SymCryptHashStateSize( hashAlgorithm );
+    SYMCRYPT_ASSERT( cbState == hashAlgorithm->stateSize );
+
+    UINT16 count = 0;
+    BYTE bTmp = 0;
+
+    PSYMCRYPT_INT piExp = NULL;
+    PSYMCRYPT_INT piRem = NULL;
+    PSYMCRYPT_MODELEMENT peOne = NULL;
+    PBYTE pbState = NULL;
+    PBYTE pbW = NULL;
+
+    UINT32 cbExp = SymCryptSizeofIntFromDigits( nDigitsOfP );
+    UINT32 cbRem = SymCryptSizeofIntFromDigits( nDigitsOfQ );
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmP );
+
+    UINT32 borrow = 0;
+
+    // We will use internal scratch space at the start of pbScratch
+    // because cbHash is not necessarily aligned according
+    // to SYMCRYPT_ASYM_ALIGN_VALUE
+    PBYTE pbScratchInternal = 0;
+    SIZE_T cbScratchInternal = 0;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    UNREFERENCED_PARAMETER( nDigitsOfQ );
+
+    // Create temporaries
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigitsOfP ),
+                        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( nDigitsOfP, nDigitsOfQ ),
+                             SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfP ) ));
+    SYMCRYPT_ASSERT( cbScratch >= cbScratchInternal + cbExp + cbRem );
+    SYMCRYPT_ASSERT( cbScratch >= cbScratchInternal + cbExp + cbModElement + cbHash + cbState );
+    pbScratch += cbScratchInternal;
+
+    piExp = SymCryptIntCreate( pbScratch, cbExp, nDigitsOfP );
+    pbScratch += cbExp;
+
+    piRem = SymCryptIntCreate( pbScratch, cbRem, nDigitsOfQ );
+
+    // Calculate the exponent e = (p-1)/q
+    borrow = SymCryptIntSubUint32( SymCryptIntFromModulus((PSYMCRYPT_MODULUS)pmP), 1, piExp );
+    if (borrow!=0)
+    {
+        // The only way to get a borrow here is if the imported prime P
+        // is zero and we generate a G from P and Q.
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptIntDivMod(
+            piExp,
+            SymCryptDivisorFromModulus( (PSYMCRYPT_MODULUS)pmQ ),
+            piExp,
+            piRem,
+            pbScratchInternal,
+            cbScratchInternal );
+
+    if ( !SymCryptIntIsEqualUint32(piRem, 0) )
+    {
+        // The only way to get a non-zero remainder is if Q does not divide P-1
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // To reach here we have guaranteed that P and Q are odd, with bitlength >= 32b, and Q divides P-1.
+    // It follows that piExp >= 2, as it must be even and non-zero.
+
+    peOne = SymCryptModElementCreate( pbScratch, cbModElement, pmP);
+    pbScratch += cbModElement;
+
+    pbState = pbScratch;
+    pbScratch += cbState;
+
+    pbW = pbScratch;
+
+    // Initialize the hash state
+    SymCryptHashInit( hashAlgorithm, pbState );
+
+    // Set the modelement equal to one
+    SymCryptModElementSetValueUint32( 1, pmP, peOne, pbScratchInternal, cbScratchInternal );
+
+    do
+    {
+        count += 1;
+
+        if (count == 0)
+        {
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        // Hash the seed
+        SymCryptHashAppend( hashAlgorithm, pbState, pbSeed, cbSeed );
+
+        // Hash the "ggen" string
+        SymCryptHashAppend( hashAlgorithm, pbState, ggen, sizeof(ggen) );
+
+        // Hash the index
+        SymCryptHashAppend( hashAlgorithm, pbState, &bIndexGenG, sizeof(bIndexGenG) );
+
+        // Hash the count (in MSB)
+        bTmp = (BYTE)(count >> 8);
+        SymCryptHashAppend( hashAlgorithm, pbState, &bTmp, sizeof(bTmp) );
+        bTmp = (BYTE)count;
+        SymCryptHashAppend( hashAlgorithm, pbState, &bTmp, sizeof(bTmp) );
+
+        // Result into W
+        SymCryptHashResult( hashAlgorithm, pbState, pbW, cbHash );
+
+        // Set this into G
+        scError = SymCryptModElementSetValue(
+                        pbW,
+                        cbHash,
+                        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                        pmP,
+                        peG,
+                        pbScratchInternal,
+                        cbScratchInternal );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // ModExp G in place
+        SymCryptModExp(
+            pmP,
+            peG,
+            piExp,
+            nBitsOfP,
+            SYMCRYPT_FLAG_DATA_PUBLIC,
+            peG,
+            pbScratchInternal,
+            cbScratchInternal );
+
+    } while (SymCryptModElementIsZero( pmP, peG ) || SymCryptModElementIsEqual( pmP, peG, peOne ));
+
+cleanup:
+    return scError;
+}
+
+// Scratch space requirements for the entire FIPS standards generation of P,Q,G
+UINT32
+SYMCRYPT_CALL
+SymCryptDlgroupScratchSpace_FIPS( UINT32 nBitsOfP, UINT32 nBitsOfQ, PCSYMCRYPT_HASH pHashAlgorithm )
+{
+    UINT32 nDigitsOfP = SymCryptDigitsFromBits( nBitsOfP );
+    UINT32 nDigitsOfQ = SymCryptDigitsFromBits( nBitsOfQ );
+    UINT32 ndDivTwoQ = SymCryptDigitsFromBits(nBitsOfQ + 1);
+
+    UINT32 cbPrimeP = (nBitsOfP+7)/8;   // Note: The upper bound for nBitsOfP is enforced by SymCryptDigitsFromBits
+    UINT32 cbDivTwoQ = SymCryptSizeofDivisorFromDigits(ndDivTwoQ);
+    UINT32 cbIntTwoQ = SymCryptSizeofIntFromDigits( ndDivTwoQ );
+    UINT32 cbSeed = (nBitsOfQ+7)/8;     // Note: The upper bound for nBitsOfP is enforced by SymCryptDigitsFromBits
+
+    UINT32 cbExp = SymCryptSizeofIntFromDigits( nDigitsOfP );
+    UINT32 cbRem = SymCryptSizeofIntFromDigits( nDigitsOfQ );
+    UINT32 cbModElement = SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( nBitsOfP );
+
+    UINT32 cbHash = (UINT32)SymCryptHashResultSize( pHashAlgorithm );
+    UINT32 cbState = (UINT32) SymCryptHashStateSize( pHashAlgorithm );
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow the result and is bounded by 2^28.
+    //
+    return SYMCRYPT_MAX( cbDivTwoQ + SYMCRYPT_MAX(
+                            // Generate Q
+                            SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( ndDivTwoQ ),
+                            SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( nDigitsOfQ ),
+                                 2 * cbHash)),
+                            // Generate P
+                            2*cbIntTwoQ + cbHash + cbSeed + cbPrimeP +
+                            SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( nDigitsOfP, ndDivTwoQ ),
+                                 SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( nDigitsOfP )) ),
+           SYMCRYPT_MAX(
+                // Convert P and Q to moduli
+                SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS( nDigitsOfP ),
+                // Generate GenG
+                cbExp + SYMCRYPT_MAX(cbRem, cbModElement + cbState + cbHash) +
+                SYMCRYPT_MAX(SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigitsOfP ),
+                SYMCRYPT_MAX(SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( nDigitsOfP, nDigitsOfQ ),
+                    SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfP ) )) ));
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGenerate(
+    _In_    PCSYMCRYPT_HASH         hashAlgorithm,
+    _In_    SYMCRYPT_DLGROUP_FIPS   fipsStandard,
+    _Inout_ PSYMCRYPT_DLGROUP       pDlgroup )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    PBYTE pbScratchInternal = NULL;
+    SIZE_T cbScratchInternal = 0;
+
+    UINT32 fPrimeQFound = 0;
+    UINT32 fPrimePFound = 0;
+
+    // A divisor equal to 2*Q will be needed for the generation of P
+    PSYMCRYPT_DIVISOR pdDivTwoQ = NULL;
+    UINT32 cbDivTwoQ = 0;
+    UINT32 ndDivTwoQ = 0;
+
+    UINT32 nBitsOfP = 0;
+    UINT32 nDigitsOfP = 0;
+    UINT32 nBitsOfQ = 0;
+    UINT32 nDigitsOfQ = 0;
+
+    PCSYMCRYPT_TRIALDIVISION_CONTEXT pTrialDivisionContext = NULL;
+
+    if (fipsStandard == SYMCRYPT_DLGROUP_FIPS_NONE)
+    {
+        fipsStandard = SYMCRYPT_DLGROUP_FIPS_LATEST;
+    }
+
+    // Numbered comments refer to the steps in the FIPS standard
+    // 1. Check that L,N is in the list of acceptable pairs
+    //      => Skipped as SymCrypt supports more sizes
+
+    // 2. Check that seedlen >= N
+    //      => Skipped as we always have seedlen == N (see below)
+
+
+    // Make sure that a hash algorithm is passed (if needed)
+    // and set the FIPS standard
+    if (fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
+    {
+        if (hashAlgorithm != NULL)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        pDlgroup->eFipsStandard = fipsStandard;
+        hashAlgorithm = SymCryptSha1Algorithm;
+    }
+    else
+    {
+        if (hashAlgorithm == NULL)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        pDlgroup->eFipsStandard = fipsStandard;
+    }
+
+    // If during allocation the caller didn't know the size of Q
+    // and set it to 0, pick the default bitsize here
+    // and fix all the zero parameters.
+    if (pDlgroup->nBitsOfQ == 0)
+    {
+        pDlgroup->nBitsOfQ = SymCryptDlgroupCalculateBitsizeOfQ(pDlgroup->nBitsOfP);
+
+        if (pDlgroup->nBitsOfQ > pDlgroup->nMaxBitsOfQ)
+        {
+            scError = SYMCRYPT_FIPS_FAILURE;        // This hits when nMaxBitsOfQ = (nBitsOfP-1) <= 160
+            goto cleanup;
+        }
+
+        pDlgroup->cbPrimeQ = (pDlgroup->nBitsOfQ + 7)/8;
+        pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits( pDlgroup->nBitsOfQ );
+        pDlgroup->nDefaultBitsPriv = pDlgroup->nBitsOfQ;
+        pDlgroup->nBitsOfSeed = pDlgroup->nBitsOfQ;
+        pDlgroup->cbSeed = (pDlgroup->nBitsOfSeed+7)/8;
+    }
+
+    // Helper variables
+    nBitsOfP = pDlgroup->nBitsOfP;
+    nDigitsOfP = pDlgroup->nDigitsOfP;
+    nBitsOfQ = pDlgroup->nBitsOfQ;
+    nDigitsOfQ = pDlgroup->nDigitsOfQ;
+
+    // Create the modulus Q
+    pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( nDigitsOfQ ), nDigitsOfQ );
+
+    // Conditions on the hash function output size
+    // The second condition is needed for generation of G in SymCrypt
+    // since it allows even very small sizes of P.
+    if ( (8*((UINT32)SymCryptHashResultSize( hashAlgorithm )) < nBitsOfQ) ||
+         (8*((UINT32)SymCryptHashResultSize( hashAlgorithm )) > nBitsOfP) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Set the group's hash algorithm
+    pDlgroup->pHashAlgorithm = hashAlgorithm;
+
+    // Calculate sizes for the 2*Q divisor
+    ndDivTwoQ = SymCryptDigitsFromBits(nBitsOfQ + 1);
+    cbDivTwoQ = SymCryptSizeofDivisorFromDigits(ndDivTwoQ);
+
+    // Scratch space
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - SymCryptDlgroupScratchSpace_FIPS is bounded by 2^28.
+    //
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = SymCryptDlgroupScratchSpace_FIPS( nBitsOfP, nBitsOfQ, hashAlgorithm );
+    pbScratch = SymCryptCallbackAlloc(cbScratch);
+    if (pbScratch==NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Create a divisor 2*Q (needed for the generation of P)
+    pdDivTwoQ = SymCryptDivisorCreate( pbScratch, cbDivTwoQ, ndDivTwoQ );
+    pbScratchInternal = pbScratch + cbDivTwoQ;
+    cbScratchInternal = cbScratch - cbDivTwoQ;
+
+    // Create a trial division context for both P and Q
+    pTrialDivisionContext = SymCryptCreateTrialDivisionContext( pDlgroup->nDigitsOfP );
+    if (pTrialDivisionContext == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    do
+    {
+        do
+        {
+            // Fill the seed buffer in the DLGroup with seedlen bits
+            scError = SymCryptCallbackRandom( pDlgroup->pbSeed, pDlgroup->cbSeed );
+            if (scError != SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+
+            // Zero-out the top bits if needed
+            if ((pDlgroup->nBitsOfSeed)%8 != 0)
+            {
+                pDlgroup->pbSeed[0] &= ((BYTE)0xff >> (8 - (pDlgroup->nBitsOfSeed)%8));
+            }
+
+            scError = SymCryptDlgroupGeneratePrimeQ_FIPS(
+                            pDlgroup,
+                            pTrialDivisionContext,
+                            &fPrimeQFound,
+                            SymCryptIntFromModulus(pDlgroup->pmQ),
+                            pdDivTwoQ,
+                            pbScratchInternal,
+                            cbScratchInternal );
+            if (scError != SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+        }
+        while (fPrimeQFound == 0);
+
+        scError = SymCryptDlgroupGeneratePrimeP_FIPS(
+                        pDlgroup,
+                        pdDivTwoQ,
+                        4*nBitsOfP - 1,
+                        pTrialDivisionContext,
+                        &fPrimePFound,
+                        SymCryptIntFromModulus(pDlgroup->pmP),
+                        &(pDlgroup->dwGenCounter),
+                        pbScratchInternal,
+                        cbScratchInternal );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+    while (fPrimePFound == 0);
+
+    // Specify that we have a Q
+    pDlgroup->fHasPrimeQ = TRUE;
+
+    // Convert both of P and Q to moduli
+    // IntToModulus requirement:
+    //      Both P,Q > 0 since they are primes
+    SymCryptIntToModulus(
+        SymCryptIntFromModulus( pDlgroup->pmP ),
+        pDlgroup->pmP,
+        1000*nBitsOfP,        // Average operations
+        SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+        pbScratch,
+        cbScratch );
+
+    SymCryptIntToModulus(
+        SymCryptIntFromModulus( pDlgroup->pmQ ),
+        pDlgroup->pmQ,
+        1000*nBitsOfP,        // Average operations
+        SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+        pbScratch,
+        cbScratch );
+
+    // Generate G
+    scError = SymCryptDlgroupGenerateGenG_FIPS( pDlgroup, pDlgroup->peG, pbScratch, cbScratch );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    if (pTrialDivisionContext!=NULL)
+    {
+        SymCryptFreeTrialDivisionContext( pTrialDivisionContext );
+    }
+
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupSetValueSafePrime(
+            SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE   dhSafePrimeType,
+    _Inout_ PSYMCRYPT_DLGROUP                   pDlgroup )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+
+    PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS safePrimeParams = NULL;
+
+    UINT32 i;
+    UINT32 nBitsOfQ;
+
+    // Given we know nBitsOfP = nBitsOfQ+1 for all safe-prime groups, this specifies a tight bound when selecting a group
+    UINT32 nMaxBitsOfP = SYMCRYPT_MIN(pDlgroup->nMaxBitsOfP, pDlgroup->nMaxBitsOfQ+1);
+    UINT32 nMaxDigitsOfP;
+
+    if ( dhSafePrimeType == SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_NONE )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Iterate through all named safe-prime groups until we find one which fits the requested parameters
+    // We can definitely do something smarter here, but we have only 10 values to check so do the dumb thing for now
+    // Relies on the fact the SymCryptNamedSafePrimeGroups is ordered from largest to smallest
+    for ( i=0; i<SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT; i++ )
+    {
+        if ( SymCryptNamedSafePrimeGroups[i]->eDhSafePrimeType == dhSafePrimeType &&
+             SymCryptNamedSafePrimeGroups[i]->nBitsOfP <= nMaxBitsOfP )
+        {
+            safePrimeParams = SymCryptNamedSafePrimeGroups[i];
+            break;
+        }
+    }
+
+    if (safePrimeParams == NULL)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    nMaxDigitsOfP = SymCryptDigitsFromBits(safePrimeParams->nBitsOfP);
+
+    // Scratch space
+    //
+    // From symcrypt_internal.h we have:
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(nMaxDigitsOfP),
+        SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nMaxDigitsOfP) );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch==NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Set fields marking the Dlgroup as being a named safe-prime group
+    pDlgroup->isSafePrimeGroup  = TRUE;
+    pDlgroup->eFipsStandard     = SYMCRYPT_DLGROUP_FIPS_NONE;
+    pDlgroup->nMinBitsPriv      = safePrimeParams->nMinBitsPriv;
+    pDlgroup->nDefaultBitsPriv  = safePrimeParams->nDefaultBitsPriv;
+
+    // Ensure that fields which don't apply to named safe-prime groups are cleared
+    pDlgroup->pHashAlgorithm = NULL;
+    pDlgroup->dwGenCounter = 0;
+
+    pDlgroup->nBitsOfSeed = 0;
+    pDlgroup->pbSeed = NULL;
+    pDlgroup->cbSeed = 0;
+
+    // Set the bitsize and bytesize of P
+    pDlgroup->nBitsOfP = safePrimeParams->nBitsOfP;
+    pDlgroup->cbPrimeP = (safePrimeParams->nBitsOfP + 7)/ 8;
+    pDlgroup->nDigitsOfP = SymCryptDigitsFromBits(safePrimeParams->nBitsOfP);
+
+    // Set the bitsize and bytesize of Q
+    nBitsOfQ = pDlgroup->nBitsOfP - 1;
+    pDlgroup->nBitsOfQ = nBitsOfQ;
+    pDlgroup->cbPrimeQ = (nBitsOfQ + 7)/8;
+    pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits(nBitsOfQ);
+    pDlgroup->fHasPrimeQ = TRUE;
+
+    //
+    // Prime P
+    //
+
+    // Recreate the modulus P
+    // (this will set nDigits in the modulus object appropriately, which is necessary for use of SymCryptIntShr1 below)
+    pDlgroup->pmP = SymCryptModulusCreate( (PBYTE) pDlgroup->pmP, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfP ), pDlgroup->nDigitsOfP );
+
+    scError = SymCryptIntSetValue( safePrimeParams->pcbPrimeP, pDlgroup->cbPrimeP, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, SymCryptIntFromModulus(pDlgroup->pmP) );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // IntToModulus requirement:
+    //      nBitsOfP >= SYMCRYPT_DLGROUP_MIN_BITSIZE_P --> P > 0
+    SymCryptIntToModulus(
+        SymCryptIntFromModulus( pDlgroup->pmP ),
+        pDlgroup->pmP,
+        1000*pDlgroup->nBitsOfP,        // Average operations
+        SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+        pbScratch,
+        cbScratch );
+
+    //
+    // Prime Q
+    //
+
+    // Create the modulus Q
+    pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ ), pDlgroup->nDigitsOfQ );
+
+    // Q = floor( P / 2 )
+    SymCryptIntShr1( 0, SymCryptIntFromModulus(pDlgroup->pmP), SymCryptIntFromModulus(pDlgroup->pmQ) );
+
+    // IntToModulus requirement:
+    //      nBitsOfQ >= SYMCRYPT_DLGROUP_MIN_BITSIZE_Q --> Q > 0
+    SymCryptIntToModulus(
+        SymCryptIntFromModulus( pDlgroup->pmQ ),
+        pDlgroup->pmQ,
+        1000*nBitsOfQ,        // Average operations
+        SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+        pbScratch,
+        cbScratch );
+
+    //
+    // Generator G
+    //
+
+    // G to 2
+    SymCryptModElementSetValueUint32( 2, pDlgroup->pmP, pDlgroup->peG, pbScratch, cbScratch );
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptDlgroupIsSame(
+    _In_    PCSYMCRYPT_DLGROUP  pDlgroup1,
+    _In_    PCSYMCRYPT_DLGROUP  pDlgroup2 )
+{
+    BOOLEAN fIsSameGroup = FALSE;
+
+    if ( pDlgroup1 == pDlgroup2 )
+    {
+        fIsSameGroup = TRUE;
+        goto cleanup;
+    }
+
+    if ( (pDlgroup1->nBitsOfP != pDlgroup2->nBitsOfP) ||
+         (pDlgroup1->nDigitsOfP != pDlgroup2->nDigitsOfP) ||
+         !SymCryptIntIsEqual ( SymCryptIntFromModulus(pDlgroup1->pmP), SymCryptIntFromModulus(pDlgroup2->pmP) ) ||
+         !SymCryptModElementIsEqual ( pDlgroup1->pmP, pDlgroup1->peG, pDlgroup2->peG ))
+    {
+        goto cleanup;
+    }
+
+    fIsSameGroup = TRUE;
+
+cleanup:
+    return fIsSameGroup;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlgroupGetSizes(
+    _In_    PCSYMCRYPT_DLGROUP      pDlgroup,
+    _Out_   SIZE_T*                 pcbPrimeP,
+    _Out_   SIZE_T*                 pcbPrimeQ,
+    _Out_   SIZE_T*                 pcbGenG,
+    _Out_   SIZE_T*                 pcbSeed )
+{
+    if (pcbPrimeP!=NULL)
+    {
+        *pcbPrimeP = pDlgroup->cbPrimeP;
+    }
+
+    if (pcbPrimeQ!=NULL)
+    {
+        *pcbPrimeQ = pDlgroup->cbPrimeQ;     // This returns 0 if the group does not have a prime Q
+    }
+
+    if (pcbGenG!=NULL)
+    {
+        *pcbGenG = pDlgroup->cbPrimeP;
+    }
+
+    if (pcbSeed!=NULL)
+    {
+        *pcbSeed = pDlgroup->cbSeed;        // This returns 0 if the group does not have a prime Q
+    }
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupAutoCompleteNamedSafePrimeGroup(
+    _Inout_                         PSYMCRYPT_DLGROUP   pDlgroup,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE  pbScratchInternal;
+    SIZE_T cbScratchInternal;
+
+    PSYMCRYPT_INT piTemp = NULL;
+    UINT32 cbTemp;
+    UINT32 i;
+    UINT32 nBitsOfQ;
+    PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS safePrimeParams = NULL;
+
+    // Check whether bottom 64b of P all 1 - as first cheap check
+    if ( SymCryptIntGetValueLsbits64( SymCryptIntFromModulus(pDlgroup->pmP) ) != ((UINT64) -1) )
+    {
+        goto cleanup; // Not a named safe-prime group
+    }
+
+    cbTemp = SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfP );
+    SYMCRYPT_ASSERT( cbScratch >= cbTemp );
+
+    // Create an integer piTemp
+    piTemp = SymCryptIntCreate( pbScratch, cbTemp, pDlgroup->nDigitsOfP );
+    pbScratchInternal = pbScratch + cbTemp;
+    cbScratchInternal = cbScratch - cbTemp;
+
+    // Set piTemp to the generator G (this will fail if the number cannot fit in the object)
+    SymCryptModElementToInt( pDlgroup->pmP, pDlgroup->peG, piTemp, pbScratchInternal, cbScratchInternal );
+
+    // Generator must be 2 mod P
+    if ( !SymCryptIntIsEqualUint32( piTemp, 2 ) )
+    {
+        goto cleanup; // Not a named safe-prime group
+    }
+
+    // Iterate through all named safe-prime groups and check whether any of them have matching Prime P
+    // We can definitely do something smarter here, but we have only 10 values to check so do the dumb thing for now
+    for ( i=0; i<SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT; i++ )
+    {
+        if ( SymCryptNamedSafePrimeGroups[i]->nBitsOfP == pDlgroup->nBitsOfP )
+        {
+            // Set piTemp to the named safe-prime group's P (this will fail if the number cannot fit in the object)
+            SymCryptIntSetValue( SymCryptNamedSafePrimeGroups[i]->pcbPrimeP, pDlgroup->cbPrimeP, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piTemp );
+
+            if ( SymCryptIntIsEqual( piTemp, SymCryptIntFromModulus(pDlgroup->pmP) ) )
+            {
+                safePrimeParams = SymCryptNamedSafePrimeGroups[i];
+                break;
+            }
+        }
+    }
+
+    // If we found a match in the previous loop, auto-populate appropriate fields in pDlGroup
+    if (safePrimeParams != NULL)
+    {
+        if ( pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2 ||
+             pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_3 )
+        {
+            // Inappropriate use of named safe-prime groups
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        // Set fields marking the Dlgroup as being a named safe-prime group
+        pDlgroup->isSafePrimeGroup  = TRUE;
+        pDlgroup->eFipsStandard     = SYMCRYPT_DLGROUP_FIPS_NONE;
+        pDlgroup->nMinBitsPriv      = safePrimeParams->nMinBitsPriv;
+        pDlgroup->nDefaultBitsPriv  = safePrimeParams->nDefaultBitsPriv;
+
+        // Ensure that fields which don't apply to named safe-prime groups are cleared
+        pDlgroup->pHashAlgorithm = NULL;
+        pDlgroup->dwGenCounter = 0;
+
+        pDlgroup->nBitsOfSeed = 0;
+        pDlgroup->pbSeed = NULL;
+        pDlgroup->cbSeed = 0;
+
+        // Set the bitsize and bytesize of Q
+        nBitsOfQ = pDlgroup->nBitsOfP - 1;
+        pDlgroup->nBitsOfQ = nBitsOfQ;
+        pDlgroup->cbPrimeQ = (nBitsOfQ + 7)/8;
+        pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits(nBitsOfQ);
+
+        // Create the modulus Q
+        pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ ), pDlgroup->nDigitsOfQ );
+
+        // piTemp still has the value of P, and Q = floor( P / 2 )
+        SymCryptIntShr1( 0, piTemp, piTemp );
+
+        // Set the prime Q
+        scError = SymCryptIntCopyMixedSize( piTemp, SymCryptIntFromModulus(pDlgroup->pmQ) );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // IntToModulus requirement:
+        //      nBitsOfQ >= SYMCRYPT_DLGROUP_MIN_BITSIZE_Q --> Q > 0
+        SymCryptIntToModulus(
+            SymCryptIntFromModulus( pDlgroup->pmQ ),
+            pDlgroup->pmQ,
+            1000*nBitsOfQ,        // Average operations
+            SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+            pbScratch,
+            cbScratch );
+
+        pDlgroup->fHasPrimeQ = TRUE;
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupSetValue(
+    _In_reads_bytes_( cbPrimeP )    PCBYTE                  pbPrimeP,
+                                    SIZE_T                  cbPrimeP,
+    _In_reads_bytes_( cbPrimeQ )    PCBYTE                  pbPrimeQ,
+                                    SIZE_T                  cbPrimeQ,
+    _In_reads_bytes_( cbGenG )      PCBYTE                  pbGenG,
+                                    SIZE_T                  cbGenG,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+    _In_opt_                        PCSYMCRYPT_HASH         pHashAlgorithm,
+    _In_reads_bytes_( cbSeed )      PCBYTE                  pbSeed,
+                                    SIZE_T                  cbSeed,
+                                    UINT32                  genCounter,
+                                    SYMCRYPT_DLGROUP_FIPS   fipsStandard,
+    _Inout_                         PSYMCRYPT_DLGROUP       pDlgroup )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    SIZE_T cbScratchVerify = 0;
+
+    PSYMCRYPT_INT piTemp = NULL;
+
+    UINT32 nBitsOfP = 0;
+    UINT32 nBitsOfQ = 0;
+
+    UINT32 nMaxDigitsOfP = SymCryptDigitsFromBits(pDlgroup->nMaxBitsOfP);
+    UINT32 nMaxDigitsOfQ = SymCryptDigitsFromBits(pDlgroup->nMaxBitsOfQ);
+
+    PCSYMCRYPT_TRIALDIVISION_CONTEXT pTrialDivisionContext = NULL;
+
+    // Make sure that the inputs make sense
+    if ( (pbPrimeP==NULL) || (cbPrimeP==0) ||       // Prime P is needed
+         ((pbGenG==NULL)&&(cbGenG>0)) ||
+         ((pbPrimeQ==NULL)&&(cbPrimeQ>0)) ||
+         ((pbGenG==NULL)&&(pbPrimeQ==NULL)) ||      // We can't have both Q and G missing
+         ((pbSeed==NULL)&&(cbSeed>0)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // FIPS 186-4 verification is needed
+    if (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE)
+    {
+        // Make sure we have what we need
+        if ((pbPrimeQ == NULL)||
+            (cbPrimeQ == 0) ||
+            (pbSeed == NULL) ||
+            (cbSeed == 0) ||
+            ((fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2) && (pHashAlgorithm != NULL)) ||
+            ((fipsStandard != SYMCRYPT_DLGROUP_FIPS_186_2) && (pHashAlgorithm == NULL)) )
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+            goto cleanup;
+        }
+    }
+
+    // Set the hashAlgorithm
+    if ( (fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2) ||
+         ((pHashAlgorithm==NULL) && (pbGenG == NULL)) )
+    {
+        // This hits either when:
+        //  - The FIPS standard is 186-2
+        //  - When we don't specify an algorithm or generator G (thus we need a hash algorithm to generate it
+        //    ourselves)
+        pDlgroup->pHashAlgorithm = SymCryptSha1Algorithm;
+    }
+    else
+    {
+        pDlgroup->pHashAlgorithm = pHashAlgorithm;
+    }
+
+    if ( (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE) || (pbGenG == NULL))
+    {
+        // The following is the scratch space for generation / verification
+        // Notice that we take the maximum size possible so it can get relatively big.
+        // Also, we will need some additional space for the computed parameters:
+        //      computedP, computedQ, and computedG.
+        cbScratchVerify = SymCryptDlgroupScratchSpace_FIPS( pDlgroup->nMaxBitsOfP, pDlgroup->nMaxBitsOfQ, pDlgroup->pHashAlgorithm ) +
+                          SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nMaxDigitsOfP),
+                          SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nMaxDigitsOfQ),
+                               2*SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS(nMaxDigitsOfP)));
+    }
+
+    // Scratch space
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - SymCryptDlgroupScratchSpace_FIPS is bounded by 2^28.
+    //
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nMaxDigitsOfP) +
+                    SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nMaxDigitsOfQ),
+                    SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(nMaxDigitsOfP) ),
+                     cbScratchVerify );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch==NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    //
+    // Prime P
+    //
+
+    // Set the prime P (this will fail if the number cannot fit in the object)
+    scError = SymCryptIntSetValue( pbPrimeP, cbPrimeP, numFormat, SymCryptIntFromModulus(pDlgroup->pmP) );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Check the bitsize of value
+    nBitsOfP = SymCryptIntBitsizeOfValue(SymCryptIntFromModulus(pDlgroup->pmP));
+    if ( nBitsOfP > pDlgroup->nMaxBitsOfP)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P)
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    // FIPS 186-4 verification is needed
+    // Check genCounter is not too big
+    if (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE &&
+        genCounter > 4*nBitsOfP-1 )
+    {
+        scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+        goto cleanup;
+    }
+
+    if( (SymCryptIntGetValueLsbits32( SymCryptIntFromModulus( pDlgroup->pmP ) ) & 1) == 0 )
+    {
+        // P is even, when it should be a prime of at least 32 bits
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Set the bitsize and bytesize of the value
+    pDlgroup->nBitsOfP = nBitsOfP;
+    pDlgroup->cbPrimeP = (nBitsOfP + 7)/8;
+
+    // IntToModulus requirement:
+    //      nBitsOfP >= SYMCRYPT_DLGROUP_MIN_BITSIZE_P --> P > 0
+    SymCryptIntToModulus(
+        SymCryptIntFromModulus( pDlgroup->pmP ),
+        pDlgroup->pmP,
+        1000*nBitsOfP,        // Average operations
+        SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+        pbScratch,
+        cbScratch );
+
+    //
+    // Prime Q
+    //
+
+    // Wiping of previous (optional) parameters related to Q
+    if (pDlgroup->pmQ != NULL)
+    {
+        SymCryptModulusWipe( pDlgroup->pmQ );
+    }
+    if (pDlgroup->cbSeed != 0)
+    {
+        SymCryptWipe( pDlgroup->pbSeed, pDlgroup->cbSeed);
+    }
+
+    if (pbPrimeQ != NULL)
+    {
+        // Create an integer piTemp
+        piTemp = SymCryptIntCreate( pbScratch, cbScratch, nMaxDigitsOfQ );
+
+        // Set the prime Q (this will fail if the number cannot fit in the object)
+        scError = SymCryptIntSetValue( pbPrimeQ, cbPrimeQ, numFormat, piTemp );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // Check the bitsize of value
+        nBitsOfQ = SymCryptIntBitsizeOfValue(piTemp);
+        if ( nBitsOfQ > pDlgroup->nMaxBitsOfQ)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        if (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q)
+        {
+            scError = SYMCRYPT_WRONG_KEY_SIZE;
+            goto cleanup;
+        }
+
+        if( (SymCryptIntGetValueLsbits32( piTemp ) & 1) == 0 )
+        {
+            // Some of our modinv algorithms require odd inputs, and Q should be odd as it
+            // claims to be a prime.
+            // (Q can't be 2 as it must be at least 32 bits long.)
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        // Set the bitsize and bytesize of the value
+        pDlgroup->nBitsOfQ = nBitsOfQ;
+        pDlgroup->cbPrimeQ = (nBitsOfQ + 7)/8;
+        pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits(nBitsOfQ);
+        pDlgroup->nDefaultBitsPriv = nBitsOfQ;
+        pDlgroup->nBitsOfSeed = nBitsOfQ;
+        pDlgroup->cbSeed = (nBitsOfQ+7)/8;
+
+        // Create the modulus Q
+        pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ ), pDlgroup->nDigitsOfQ );
+
+        // Set the prime Q
+        scError = SymCryptIntCopyMixedSize( piTemp, SymCryptIntFromModulus(pDlgroup->pmQ) );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // piTemp is not needed any more so we are free to re-use the scratch space
+
+        // IntToModulus requirement:
+        //      nBitsOfQ >= SYMCRYPT_DLGROUP_MIN_BITSIZE_Q --> Q > 0
+        SymCryptIntToModulus(
+            SymCryptIntFromModulus( pDlgroup->pmQ ),
+            pDlgroup->pmQ,
+            1000*nBitsOfP,        // Average operations
+            SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+            pbScratch,
+            cbScratch );
+
+        pDlgroup->fHasPrimeQ = TRUE;
+    }
+    else
+    {
+        // Clear all info about Q
+        pDlgroup->cbPrimeQ = 0;
+        pDlgroup->nBitsOfQ = 0;
+        pDlgroup->nDigitsOfQ = 0;
+
+        pDlgroup->nDefaultBitsPriv = 0;
+        pDlgroup->nBitsOfSeed = 0;
+        pDlgroup->cbSeed = 0;
+
+        pDlgroup->pmQ = NULL;
+        pDlgroup->fHasPrimeQ = FALSE;
+    }
+
+    pDlgroup->isSafePrimeGroup = FALSE;
+    pDlgroup->nMinBitsPriv = 0;
+
+    //
+    // Provided Generator G
+    //
+    if (pbGenG != NULL)
+    {
+        // Set the generator G (this will fail if the number cannot fit in the object)
+        scError = SymCryptModElementSetValue( pbGenG, cbGenG, numFormat, pDlgroup->pmP, pDlgroup->peG, pbScratch, cbScratch );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        scError = SymCryptDlgroupAutoCompleteNamedSafePrimeGroup( pDlgroup, pbScratch, cbScratch );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // Successfully detected, validated and autocompleted named safe-prime group
+        if (pDlgroup->isSafePrimeGroup)
+        {
+            goto cleanup;
+        }
+    }
+
+    //
+    //  Verification data (this has to be done before possibly generating G)
+    //
+
+    // Set the FIPS standard
+    pDlgroup->eFipsStandard = fipsStandard;
+
+    // Set the seed
+    if (pbSeed != NULL)
+    {
+        if (cbSeed != pDlgroup->cbSeed)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+        memcpy( pDlgroup->pbSeed, pbSeed, cbSeed );
+    }
+
+    // Set the genCounter
+    pDlgroup->dwGenCounter = genCounter;
+
+    //
+    // Generator G
+    //
+
+    if (pbGenG == NULL)
+    {
+        // Let's generate G here since none was given
+
+        // // We need Q (check at the beginning)
+        // if (pbPrimeQ==NULL)
+        // {
+            // scError = SYMCRYPT_INVALID_ARGUMENT;
+            // goto cleanup;
+        // }
+
+        // If no seed was given let's generate our own
+        if (pbSeed==NULL)
+        {
+            SymCryptCallbackRandom(pDlgroup->pbSeed, pDlgroup->cbSeed);
+        }
+
+        scError = SymCryptDlgroupGenerateGenG_FIPS(
+                        pDlgroup,
+                        pDlgroup->peG,
+                        pbScratch,
+                        cbScratch );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+
+    // Verification
+    if (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE)
+    {
+        // Verification
+        PBYTE pbScratchInternal = pbScratch;
+        SIZE_T cbScratchInternal = cbScratch;
+
+        UINT32 ndDivTwoQ = 0;
+        UINT32 cbDivTwoQ = 0;
+        PSYMCRYPT_DIVISOR pdDivTwoQ = NULL;
+
+        UINT32 cbComputed = 0;
+        PSYMCRYPT_INT piComputed = NULL;
+        UINT32 fPrimeComputed = 0;
+        UINT32 dwComputedCounter = 0;
+
+        PSYMCRYPT_MODELEMENT peComputed = NULL;
+        PSYMCRYPT_MODELEMENT peOne = NULL;
+
+        // Step 3: Acceptable pairs of L,N => skipped
+
+        // Step 6: nBitsOfSeed < nBitsOfQ => skipped
+
+        // Create the divisor object
+        ndDivTwoQ = SymCryptDigitsFromBits(pDlgroup->nBitsOfQ + 1);
+        cbDivTwoQ = SymCryptSizeofDivisorFromDigits( ndDivTwoQ );
+        pdDivTwoQ = SymCryptDivisorCreate( pbScratchInternal, cbDivTwoQ, ndDivTwoQ );
+        pbScratchInternal += cbDivTwoQ;
+        cbScratchInternal -= cbDivTwoQ;
+
+        // Create the temporary integer of size Q
+        cbComputed = SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfQ );
+        piComputed = SymCryptIntCreate( pbScratchInternal, cbComputed, pDlgroup->nDigitsOfQ );
+        pbScratchInternal += cbComputed;
+        cbScratchInternal -= cbComputed;
+
+        // Create a trial division context for both P and Q
+        pTrialDivisionContext = SymCryptCreateTrialDivisionContext( pDlgroup->nDigitsOfP );
+        if (pTrialDivisionContext == NULL)
+        {
+            scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+            goto cleanup;
+        }
+
+        // Steps 8,9: Check if computed_q is prime and equal to q
+        scError = SymCryptDlgroupGeneratePrimeQ_FIPS(
+                            pDlgroup,
+                            pTrialDivisionContext,
+                            &fPrimeComputed,
+                            piComputed,
+                            pdDivTwoQ,
+                            pbScratchInternal,
+                            cbScratchInternal );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;  // Overwrite any possible error
+            goto cleanup;
+        }
+
+        if ((!fPrimeComputed)||(!SymCryptIntIsEqual( piComputed, SymCryptIntFromModulus(pDlgroup->pmQ))))
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+            goto cleanup;
+        }
+
+        // Create the temporary integer of size P
+        pbScratchInternal -= cbComputed;
+        cbScratchInternal += cbComputed;
+        cbComputed = SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfP );
+        piComputed = SymCryptIntCreate( pbScratchInternal, cbComputed, pDlgroup->nDigitsOfP );
+        pbScratchInternal += cbComputed;
+        cbScratchInternal -= cbComputed;
+
+        // Steps 10-14: Check if computed_p is prime and equal to p
+        scError = SymCryptDlgroupGeneratePrimeP_FIPS(
+                        pDlgroup,
+                        pdDivTwoQ,
+                        pDlgroup->dwGenCounter,     // Go up to this
+                        pTrialDivisionContext,
+                        &fPrimeComputed,
+                        piComputed,
+                        &dwComputedCounter,
+                        pbScratchInternal,
+                        cbScratchInternal );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;  // Overwrite any possible error
+            goto cleanup;
+        }
+
+        if ((!fPrimeComputed)||(dwComputedCounter!=pDlgroup->dwGenCounter)||(!SymCryptIntIsEqual( piComputed, SymCryptIntFromModulus(pDlgroup->pmP))))
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+            goto cleanup;
+        }
+
+        // Validation of G
+
+        // Create the temporary modelement mod P
+        pbScratchInternal -= cbComputed;
+        cbScratchInternal += cbComputed;
+        cbComputed = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+        peOne = SymCryptModElementCreate( pbScratchInternal, cbComputed, pDlgroup->pmP );
+        pbScratchInternal += cbComputed;
+        cbScratchInternal -= cbComputed;
+        peComputed = SymCryptModElementCreate( pbScratchInternal, cbComputed, pDlgroup->pmP );
+        pbScratchInternal += cbComputed;
+        cbScratchInternal -= cbComputed;
+
+        // Step 2: Verify that 2<= G <= p-1
+        SymCryptModElementSetValueUint32( 1, pDlgroup->pmP, peOne, pbScratchInternal, cbScratchInternal );     // Set the temporary to 1
+
+        if ((SymCryptModElementIsZero(pDlgroup->pmP, pDlgroup->peG)) || (SymCryptModElementIsEqual(pDlgroup->pmP, pDlgroup->peG, peOne)))
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+            goto cleanup;
+        }
+
+        // Step 3: Verify that G^Q == 1
+        SymCryptModExp(
+            pDlgroup->pmP,
+            pDlgroup->peG,
+            SymCryptIntFromModulus(pDlgroup->pmQ),
+            nBitsOfQ,
+            SYMCRYPT_FLAG_DATA_PUBLIC,
+            peComputed,
+            pbScratchInternal,
+            cbScratchInternal );
+
+        if (!SymCryptModElementIsEqual(pDlgroup->pmP, peComputed, peOne))
+        {
+            scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+            goto cleanup;
+        }
+
+    }
+
+cleanup:
+    if (pTrialDivisionContext!=NULL)
+    {
+        SymCryptFreeTrialDivisionContext( pTrialDivisionContext );
+    }
+
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlgroupGetValue(
+    _In_                            PCSYMCRYPT_DLGROUP      pDlgroup,
+    _Out_writes_bytes_( cbPrimeP )  PBYTE                   pbPrimeP,
+                                    SIZE_T                  cbPrimeP,
+    _Out_writes_bytes_( cbPrimeQ )  PBYTE                   pbPrimeQ,
+                                    SIZE_T                  cbPrimeQ,
+    _Out_writes_bytes_( cbGenG )    PBYTE                   pbGenG,
+                                    SIZE_T                  cbGenG,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+    _Out_                           PCSYMCRYPT_HASH *       ppHashAlgorithm,
+    _Out_writes_bytes_( cbSeed )    PBYTE                   pbSeed,
+                                    SIZE_T                  cbSeed,
+    _Out_                           PUINT32                 pGenCounter )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+
+    if ( ((pbPrimeP==NULL)&&(cbPrimeP>0)) ||
+         ((pbPrimeQ==NULL)&&(cbPrimeQ>0)) ||
+         ((pbGenG==NULL)&&(cbGenG>0)) ||
+         ((pbSeed==NULL)&&(cbSeed>0)) ||
+         ((pbSeed!=NULL)&&(cbSeed!=pDlgroup->cbSeed)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ((pbPrimeQ!=NULL) && (!pDlgroup->fHasPrimeQ))
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    if (pbPrimeP!=NULL)
+    {
+        scError = SymCryptIntGetValue(
+                        SymCryptIntFromModulus(pDlgroup->pmP),
+                        pbPrimeP,
+                        cbPrimeP,
+                        numFormat );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    if (pbPrimeQ!=NULL)
+    {
+        scError = SymCryptIntGetValue(
+                        SymCryptIntFromModulus(pDlgroup->pmQ),
+                        pbPrimeQ,
+                        cbPrimeQ,
+                        numFormat );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    if (pbGenG!=NULL)
+    {
+        // Scratch space is needed
+        cbScratch = SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pDlgroup->nDigitsOfP);
+        pbScratch = SymCryptCallbackAlloc( cbScratch );
+        if (pbScratch==NULL)
+        {
+            scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+            goto cleanup;
+        }
+
+        scError = SymCryptModElementGetValue(
+                        pDlgroup->pmP,
+                        pDlgroup->peG,
+                        pbGenG,
+                        cbGenG,
+                        numFormat,
+                        pbScratch,
+                        cbScratch );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    if (ppHashAlgorithm!=NULL)
+    {
+        if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
+        {
+            *ppHashAlgorithm = NULL;
+        }
+        else
+        {
+            *ppHashAlgorithm = pDlgroup->pHashAlgorithm;
+        }
+    }
+
+    if (pbSeed!=NULL && pDlgroup->pbSeed!=NULL)
+    {
+        memcpy( pbSeed, pDlgroup->pbSeed, pDlgroup->cbSeed);
+    }
+
+    if (pGenCounter!=NULL)
+    {
+        *pGenCounter = pDlgroup->dwGenCounter;
+    }
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
diff --git a/libs/symcrypt/lib/dlkey.c b/libs/symcrypt/lib/dlkey.c
new file mode 100644
index 000000000000..7df34d6125df
--- /dev/null
+++ b/libs/symcrypt/lib/dlkey.c
@@ -0,0 +1,921 @@
+//
+// dlkey.c   Dlkey functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+PSYMCRYPT_DLKEY
+SYMCRYPT_CALL
+SymCryptDlkeyAllocate( _In_ PCSYMCRYPT_DLGROUP pDlgroup )
+{
+    PVOID               p;
+    SIZE_T              cb;
+    PSYMCRYPT_DLKEY     res = NULL;
+
+    cb = SymCryptSizeofDlkeyFromDlgroup( pDlgroup );
+
+    p = SymCryptCallbackAlloc( cb );
+
+    if ( p==NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptDlkeyCreate( p, cb, pDlgroup );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlkeyFree( _Out_ PSYMCRYPT_DLKEY pkObj )
+{
+    SYMCRYPT_CHECK_MAGIC( pkObj );
+    SymCryptDlkeyWipe( pkObj );
+    SymCryptCallbackFree( pkObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofDlkeyFromDlgroup( _In_ PCSYMCRYPT_DLGROUP pDlgroup )
+{
+    // Always allocate memory for large private keys
+    return sizeof(SYMCRYPT_DLKEY) + SymCryptSizeofModElementFromModulus( pDlgroup->pmP ) + SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfP );
+}
+
+PSYMCRYPT_DLKEY
+SYMCRYPT_CALL
+SymCryptDlkeyCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+    _In_                            PCSYMCRYPT_DLGROUP  pDlgroup )
+{
+    PSYMCRYPT_DLKEY pkRes = NULL;
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+
+    SYMCRYPT_ASSERT( cbBuffer >= SymCryptSizeofDlkeyFromDlgroup( pDlgroup ) );
+    SYMCRYPT_ASSERT( cbBuffer >= sizeof(SYMCRYPT_DLKEY) + cbModElement );
+    UNREFERENCED_PARAMETER( cbBuffer );     // only referenced in above ASSERTs...
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    pkRes = (PSYMCRYPT_DLKEY) pbBuffer;
+
+    // DLKEY parameters
+    pkRes->fAlgorithmInfo = 0;
+    pkRes->pDlgroup = pDlgroup;
+    pkRes->fHasPrivateKey = FALSE;
+    pkRes->fPrivateModQ = FALSE;            // This will be properly set during generate or setvalue
+    pkRes->nBitsPriv = pDlgroup->nDefaultBitsPriv;
+
+    // Create SymCrypt objects
+    pbBuffer += sizeof(SYMCRYPT_DLKEY);
+
+    pkRes->pePublicKey = SymCryptModElementCreate( pbBuffer, cbModElement, pDlgroup->pmP );
+    if (pkRes->pePublicKey == NULL)
+    {
+        goto cleanup;
+    }
+    pbBuffer += cbModElement;
+
+    //
+    // **** Always defer the creation of the private key until the key generation or
+    // set value.
+    //
+    // In place of the pbPrivate pointer store the pointer to the allocated buffer.
+    //
+    pkRes->pbPrivate = pbBuffer;
+    pkRes->piPrivateKey = NULL;
+
+    // Setting the magic
+    SYMCRYPT_SET_MAGIC( pkRes );
+
+cleanup:
+    return pkRes;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlkeyWipe( _Out_ PSYMCRYPT_DLKEY pkDst )
+{
+    SymCryptWipe( (PBYTE) pkDst, SymCryptSizeofDlkeyFromDlgroup(pkDst->pDlgroup) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptDlkeyCopy(
+    _In_    PCSYMCRYPT_DLKEY   pkSrc,
+    _Out_   PSYMCRYPT_DLKEY    pkDst )
+{
+    PCSYMCRYPT_DLGROUP pDlgroup = pkSrc->pDlgroup;
+
+    //
+    // in-place copy is somewhat common...
+    //
+    if( pkSrc != pkDst )
+    {
+        pkDst->fAlgorithmInfo = pkSrc->fAlgorithmInfo;
+        pkDst->fHasPrivateKey = pkSrc->fHasPrivateKey;
+        pkDst->fPrivateModQ = pkSrc->fPrivateModQ;
+        pkDst->nBitsPriv = pkSrc->nBitsPriv;
+
+        // Copy the public key
+        SymCryptModElementCopy( pDlgroup->pmP, pkSrc->pePublicKey, pkDst->pePublicKey );
+
+        // Copy the private key
+        SymCryptIntCopy( pkSrc->piPrivateKey, pkDst->piPrivateKey );
+    }
+}
+
+
+// DLKEY specific functions
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeySetPrivateKeyLength( _Inout_ PSYMCRYPT_DLKEY pkDlkey, UINT32 nBitsPriv, UINT32 flags )
+{
+    if( nBitsPriv > pkDlkey->pDlgroup->nBitsOfQ ||
+        nBitsPriv < pkDlkey->pDlgroup->nMinBitsPriv ||
+        flags != 0 )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    pkDlkey->nBitsPriv = nBitsPriv;
+    return SYMCRYPT_NO_ERROR;
+}
+
+PCSYMCRYPT_DLGROUP
+SYMCRYPT_CALL
+SymCryptDlkeyGetGroup( _In_ PCSYMCRYPT_DLKEY pkDlkey )
+{
+    return pkDlkey->pDlgroup;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDlkeySizeofPublicKey( _In_ PCSYMCRYPT_DLKEY pkDlkey )
+{
+    return pkDlkey->pDlgroup->cbPrimeP;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptDlkeySizeofPrivateKey( _In_ PCSYMCRYPT_DLKEY pkDlkey )
+{
+    PCSYMCRYPT_DLGROUP pDlgroup = pkDlkey->pDlgroup;
+
+    if (pkDlkey->fPrivateModQ)
+    {
+        if (pDlgroup->fHasPrimeQ)
+        {
+            if (pkDlkey->nBitsPriv != pDlgroup->nBitsOfQ)
+            {
+                return (pkDlkey->nBitsPriv + 7) / 8;
+            }
+            else
+            {
+                return pDlgroup->cbPrimeQ;
+            }
+        }
+        else
+        {
+            return pDlgroup->cbPrimeP;  // Somehow the group has no prime Q but the key was set with prime Q, return the safe option
+        }
+    }
+    else
+    {
+        return pDlgroup->cbPrimeP;
+    }
+}
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptDlkeyHasPrivateKey( _In_ PCSYMCRYPT_DLKEY pkDlkey )
+{
+    return pkDlkey->fHasPrivateKey;
+}
+
+#define SYMCRYPT_FLAG_DLKEY_PUBLIC_KEY_ORDER_VALIDATION (0x1)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyPerformPublicKeyValidation(
+    _In_                            PCSYMCRYPT_DLKEY        pkDlkey,
+    _In_                            UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    PCSYMCRYPT_DLGROUP pDlgroup = pkDlkey->pDlgroup;
+
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+    PSYMCRYPT_MODELEMENT peTmpPublicKeyExpQ = NULL;
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+
+    SYMCRYPT_ASSERT( cbScratch >= (2 * cbModElement) +
+                                    SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(pDlgroup->nDigitsOfP) );
+
+    // Check if Public key is 0
+    if ( SymCryptModElementIsZero( pDlgroup->pmP, pkDlkey->pePublicKey ) )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    peTmp = SymCryptModElementCreate( pbScratch, cbModElement, pDlgroup->pmP);
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+
+    // Check if Public key is P-1
+    SymCryptModElementSetValueNegUint32( 1, pDlgroup->pmP, peTmp, pbScratch, cbScratch );
+    if ( SymCryptModElementIsEqual( pDlgroup->pmP, pkDlkey->pePublicKey, peTmp ) )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // Check if Public key is 1 (do this check second as we may reuse 1 element in next check)
+    SymCryptModElementSetValueUint32( 1, pDlgroup->pmP, peTmp, pbScratch, cbScratch );
+    if ( SymCryptModElementIsEqual( pDlgroup->pmP, pkDlkey->pePublicKey, peTmp ) )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // Perform validation that Public key is in a subgroup of order Q.
+    if ( (flags & SYMCRYPT_FLAG_DLKEY_PUBLIC_KEY_ORDER_VALIDATION) != 0 )
+    {
+        peTmpPublicKeyExpQ = SymCryptModElementCreate( pbScratch, cbModElement, pDlgroup->pmP);
+        pbScratch += cbModElement;
+        cbScratch -= cbModElement;
+
+        // Ensure that Q is specified in the Dlgroup
+        if ( !pDlgroup->fHasPrimeQ )
+        {
+            return SYMCRYPT_INVALID_ARGUMENT;
+        }
+
+        // Calculate peTmpPublicKeyExpQ = (Public key)^Q
+        SymCryptModExp(
+            pDlgroup->pmP,
+            pkDlkey->pePublicKey,
+            SymCryptIntFromModulus( pDlgroup->pmQ ),
+            pDlgroup->nBitsOfQ,
+            SYMCRYPT_FLAG_DATA_PUBLIC, // No need for side-channel safety for public key validation
+            peTmpPublicKeyExpQ,
+            pbScratch,
+            cbScratch );
+
+        // Ensure (Public key)^Q == 1 mod P
+        if ( !SymCryptModElementIsEqual( pDlgroup->pmP, peTmpPublicKeyExpQ, peTmp ) )
+        {
+            return SYMCRYPT_INVALID_ARGUMENT;
+        }
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+#define DLKEY_GEN_RANDOM_GENERIC_LIMIT   (1000)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyGenerate(
+    _In_    UINT32                  flags,
+    _Inout_ PSYMCRYPT_DLKEY         pkDlkey )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    PBYTE pbScratchInternal = NULL;
+    SIZE_T cbScratchInternal = 0;
+
+    PCSYMCRYPT_DLGROUP pDlgroup = pkDlkey->pDlgroup;
+
+    PSYMCRYPT_MODELEMENT pePrivateKey = NULL;
+    UINT32 cbPrivateKey = 0;
+
+    PSYMCRYPT_MODULUS pmPriv = NULL;
+    UINT32 nDigitsPriv = 0;
+    UINT32 nBitsPriv = 0;
+    UINT32 fFlagsForModSetRandom = 0;
+
+    BOOLEAN useModSetRandom = TRUE;
+    UINT32 nBytesPriv = 0;
+    UINT32 dwShiftBits;
+    BYTE   privMask;
+    UINT32 cntr;
+
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_DLKEY_DSA | SYMCRYPT_FLAG_DLKEY_DH;
+    // Make sure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_DLKEY_GEN_MODP | SYMCRYPT_FLAG_KEY_NO_FIPS | algorithmFlags;
+
+    if ( ( ( flags & ~allowedFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Extra sanity checks when running with FIPS
+    // Either Dlgroup is named SafePrime group and key is for DH,
+    // or Dlgroup is not named SafePrime group and key is for DSA
+    if ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+         ( (pDlgroup->isSafePrimeGroup && (flags & SYMCRYPT_FLAG_DLKEY_DSA)) ||
+           (!(pDlgroup->isSafePrimeGroup) && (flags & SYMCRYPT_FLAG_DLKEY_DH)) ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pkDlkey->fPrivateModQ = (((flags & SYMCRYPT_FLAG_DLKEY_GEN_MODP)==0) && (pDlgroup->fHasPrimeQ));
+
+    if (pkDlkey->fPrivateModQ)
+    {
+        pmPriv = pDlgroup->pmQ;
+        nDigitsPriv = pDlgroup->nDigitsOfQ;
+        nBitsPriv = pDlgroup->nBitsOfQ;
+        fFlagsForModSetRandom = SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE | SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE; // 1 to Q-1
+
+        if ( pDlgroup->isSafePrimeGroup && (pkDlkey->nBitsPriv != pDlgroup->nBitsOfQ) )
+        {
+            useModSetRandom = FALSE;
+            SYMCRYPT_ASSERT( pkDlkey->nBitsPriv < pDlgroup->nBitsOfQ );     // 2^nBitsPriv < Q
+
+            nBitsPriv = pkDlkey->nBitsPriv;                                 // 1 to (2^nBitsPriv)-1
+            nBytesPriv = (pkDlkey->nBitsPriv + 7) / 8;
+        }
+    }
+    else
+    {
+        // We perform Private key range validation by construction
+        // The Private key is constructed in the range [1,min(2^nBitsPriv,Q)-1] precisely when pkDlkey->fPrivateModQ
+        if ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        pmPriv = pDlgroup->pmP;
+        nDigitsPriv = pDlgroup->nDigitsOfP;
+        nBitsPriv = pDlgroup->nBitsOfP;
+        fFlagsForModSetRandom = SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE;                                          // 1 to P-2
+    }
+
+    cbPrivateKey = SymCryptSizeofModElementFromModulus( pmPriv );
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = SYMCRYPT_MAX( cbPrivateKey + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nDigitsPriv),
+                    (2 * cbModElement) + SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(pDlgroup->nDigitsOfP));
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Create the private key integer
+    pkDlkey->piPrivateKey = SymCryptIntCreate( pkDlkey->pbPrivate, SymCryptSizeofIntFromDigits(nDigitsPriv), nDigitsPriv );
+
+    if (useModSetRandom)
+    {
+        // Create the private key modelement
+        pePrivateKey = SymCryptModElementCreate( pbScratch, cbPrivateKey, pmPriv );
+        pbScratchInternal = pbScratch + cbPrivateKey;
+        cbScratchInternal = cbScratch - cbPrivateKey;
+
+        // Set a modelement from 1 to q-1 (or 1 to p-2)
+        SymCryptModSetRandom(
+            pmPriv,
+            pePrivateKey,
+            fFlagsForModSetRandom,
+            pbScratchInternal,
+            cbScratchInternal );
+
+        // Set the private key
+        SymCryptModElementToInt(
+            pmPriv,
+            pePrivateKey,
+            pkDlkey->piPrivateKey,
+            pbScratchInternal,
+            cbScratchInternal );
+    }
+    else
+    {
+        // Set private key from 1 to (2^nBitsPriv)-1
+        // Wipe any bytes we won't fill with random
+        SymCryptWipe( pbScratch + nBytesPriv, (nDigitsPriv * SYMCRYPT_FDEF_DIGIT_SIZE) - nBytesPriv );
+
+        dwShiftBits = (0u-nBitsPriv) & 7;
+        privMask = (BYTE)(0xff >> dwShiftBits);
+
+        for(cntr=0; cntr<DLKEY_GEN_RANDOM_GENERIC_LIMIT; cntr++)
+        {
+            // Try random values until we get one we like
+            SymCryptCallbackRandom( pbScratch, nBytesPriv );
+
+            pbScratch[nBytesPriv-1] &= privMask;
+
+            // If non-zero we have a value in range [1, (2^nBitsPriv)-1]
+            if( !SymCryptFdefRawIsEqualUint32( (PCUINT32)pbScratch, nDigitsPriv, 0 ) )
+            {
+                break;
+            }
+        }
+
+        if (cntr >= DLKEY_GEN_RANDOM_GENERIC_LIMIT)
+        {
+            SymCryptFatal( 'rndl' );
+        }
+
+        scError = SymCryptIntSetValue( pbScratch, nBytesPriv, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, pkDlkey->piPrivateKey );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    // Calculate the public key
+    SymCryptModExp(
+        pDlgroup->pmP,
+        pDlgroup->peG,
+        pkDlkey->piPrivateKey,
+        nBitsPriv,
+        0,      // Side-channel safe
+        pkDlkey->pePublicKey,
+        pbScratch, // We can overwrite pePrivateKey now
+        cbScratch );
+
+    // Perform range validation on generated Public key.
+    if ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 )
+    {
+        // Perform Public key validation.
+        // Always perform range validation, and validation that Public key is in subgroup of order Q
+        scError = SymCryptDlkeyPerformPublicKeyValidation(
+            pkDlkey,
+            SYMCRYPT_FLAG_DLKEY_PUBLIC_KEY_ORDER_VALIDATION,
+            pbScratch,
+            cbScratch );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    // Set the fHasPrivateKey flag
+    pkDlkey->fHasPrivateKey = TRUE;
+
+    pkDlkey->fAlgorithmInfo = flags; // We want to track all of the flags in the Dlkey
+
+    if ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 )
+    {
+        if( ( flags & SYMCRYPT_FLAG_DLKEY_DSA ) != 0 )
+        {
+            // Ensure DSA algorithm selftest is run before first use of DSA algorithm
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptDsaSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_DSA );
+
+            // Run PCT eagerly as the key can only be used for DSA - there is no value in deferring
+            SYMCRYPT_RUN_KEY_GEN_PCT(
+                SymCryptDsaPct,
+                pkDlkey,
+                SYMCRYPT_PCT_DSA );
+        }
+
+        if( ( flags & SYMCRYPT_FLAG_DLKEY_DH ) != 0 )
+        {
+            // Ensure we have run the algorithm selftest at least once.
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptDhSecretAgreementSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_DH );
+
+            // Run PCT eagerly as the key can only be used for DH
+
+            // DH PCT per SP80056a-rev3 5.6.2.1.4 b)
+            // Recompute the public key from the private key
+            // Option a) appears to be explicitly overruled by 140-3 IG
+
+            // Calculate the public key from the private key in scratch
+            pbScratchInternal = pbScratch;
+            cbScratchInternal = cbScratch;
+
+            peTmp = SymCryptModElementCreate( pbScratchInternal, cbModElement, pDlgroup->pmP );
+            pbScratchInternal += cbModElement;
+            cbScratchInternal -= cbModElement;
+
+            SymCryptModExp(
+                    pDlgroup->pmP,
+                    pDlgroup->peG,
+                    pkDlkey->piPrivateKey,
+                    nBitsPriv,  // This is either bits of P, Q, or some caller-defined value i.e. public values
+                    0,          // Side-channel safe
+                    peTmp,
+                    pbScratchInternal,
+                    cbScratchInternal );
+
+            SYMCRYPT_FIPS_ASSERT( SymCryptModElementIsEqual(pDlgroup->pmP, peTmp, pkDlkey->pePublicKey) );
+        }
+    }
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeySetValue(
+    _In_reads_bytes_( cbPrivateKey )    PCBYTE                  pbPrivateKey,
+                                        SIZE_T                  cbPrivateKey,
+    _In_reads_bytes_( cbPublicKey )     PCBYTE                  pbPublicKey,
+                                        SIZE_T                  cbPublicKey,
+                                        SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                        UINT32                  flags,
+    _Inout_                             PSYMCRYPT_DLKEY         pkDlkey )
+{
+    SYMCRYPT_ERROR      scError = SYMCRYPT_NO_ERROR;
+    PBYTE               pbScratch = NULL;
+    UINT32              cbScratch = 0;
+    PBYTE               pbScratchInternal = NULL;
+    UINT32              cbScratchInternal = 0;
+
+    PCSYMCRYPT_DLGROUP pDlgroup = pkDlkey->pDlgroup;
+
+    UINT32 nDigitsPriv = 0;
+    UINT32 nBitsPriv = 0;
+
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
+    UINT32 fValidatePublicKeyOrder = SYMCRYPT_FLAG_DLKEY_PUBLIC_KEY_ORDER_VALIDATION;
+
+    if ( ((pbPrivateKey==NULL) && (cbPrivateKey!=0)) ||
+         ((pbPublicKey==NULL) && (cbPublicKey!=0)) ||
+         ((pbPrivateKey==NULL) && (pbPublicKey==NULL)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_DLKEY_DSA | SYMCRYPT_FLAG_DLKEY_DH;
+    // Make sure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION | algorithmFlags;
+
+    if ( ( ( flags & ~allowedFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Extra sanity checks when running with FIPS
+    // Either Dlgroup is named SafePrime group and key is for DH,
+    // or Dlgroup is not named SafePrime group and key is for DSA
+    if ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+         ( (pDlgroup->isSafePrimeGroup && (flags & SYMCRYPT_FLAG_DLKEY_DSA)) ||
+           (!(pDlgroup->isSafePrimeGroup) && (flags & SYMCRYPT_FLAG_DLKEY_DH)) ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check that minimal validation flag only specified with no fips
+    if ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+         ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) != 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) != 0 )
+    {
+        fValidatePublicKeyOrder = 0;
+    }
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = SYMCRYPT_MAX( cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(pDlgroup->nDigitsOfP),
+                     (2 * cbModElement) + SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(pDlgroup->nDigitsOfP) );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    if ( pbPrivateKey != NULL )
+    {
+        //
+        // Check the size of the imported private key to detect if it is mod P or mod Q
+        // If the group does not have a Q assume that the imported key is modulo P as
+        // it wouldn't help us assume otherwise (the bitsize of the private key should be kept
+        // secret from SC attacks).
+        // If the private key has had some non-default value set for nBitsPriv then the caller
+        // has explicitly opted in to more stringent range checking.
+        //
+        pkDlkey->fPrivateModQ = ( (pDlgroup->fHasPrimeQ) &&
+            ((cbPrivateKey < pDlgroup->cbPrimeQ) ||
+            ((cbPrivateKey == pDlgroup->cbPrimeQ) && (pDlgroup->cbPrimeQ < pDlgroup->cbPrimeP)) ||
+            (pkDlkey->nBitsPriv != pDlgroup->nDefaultBitsPriv)) );
+
+        if ( pkDlkey->fPrivateModQ )
+        {
+            nDigitsPriv = pDlgroup->nDigitsOfQ;
+            nBitsPriv = pDlgroup->nBitsOfQ;
+
+            if ( pDlgroup->isSafePrimeGroup )
+            {
+                nBitsPriv = pkDlkey->nBitsPriv;
+            }
+        }
+        else
+        {
+            nDigitsPriv = pDlgroup->nDigitsOfP;
+            nBitsPriv = pDlgroup->nBitsOfP;
+        }
+
+        pkDlkey->piPrivateKey = SymCryptIntCreate( pkDlkey->pbPrivate, SymCryptSizeofIntFromDigits(nDigitsPriv), nDigitsPriv );
+
+        scError = SymCryptIntSetValue(
+                        pbPrivateKey,
+                        cbPrivateKey,
+                        numFormat,
+                        pkDlkey->piPrivateKey );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Perform range validation on imported Private key.
+        // Check if Private key is 0 - perform unconditionally as it is cheap
+        // and it never makes sense for private key to be 0 intentionally
+        if ( SymCryptIntIsEqualUint32( pkDlkey->piPrivateKey, 0 ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        // Continue range validation on imported Private key.
+        if ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+        {
+            // Ensure that Q is specified in the Dlgroup
+            if ( !pDlgroup->fHasPrimeQ )
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+
+            // If nBitsPriv is specified, check if Private key is greater than or equal to 2^nBitsPriv
+            // Otherwise, check if Private key is greater than or equal to Q
+            if ( ( ( (nBitsPriv <  pDlgroup->nBitsOfQ) &&
+                    SymCryptIntBitsizeOfValue( pkDlkey->piPrivateKey ) > nBitsPriv ) ) ||
+                   ( (nBitsPriv >= pDlgroup->nBitsOfQ) &&
+                    !SymCryptIntIsLessThan( pkDlkey->piPrivateKey, SymCryptIntFromModulus( pDlgroup->pmQ ) ) ) )
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+        }
+
+        pkDlkey->fHasPrivateKey = TRUE;
+    }
+
+    if ( pbPublicKey != NULL )
+    {
+        scError = SymCryptModElementSetValue(
+                        pbPublicKey,
+                        cbPublicKey,
+                        numFormat,
+                        pDlgroup->pmP,
+                        pkDlkey->pePublicKey,
+                        pbScratch,
+                        cbScratch );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Perform range validation on imported Public key.
+        if ( (flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION) == 0 )
+        {
+            // Perform Public key validation.
+            // Always perform range validation
+            // May also perform validation that Public key is in subgroup of order Q, depending on flags
+            scError = SymCryptDlkeyPerformPublicKeyValidation(
+                pkDlkey,
+                fValidatePublicKeyOrder,
+                pbScratch,
+                cbScratch );
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    // Calculating the public key if no key was provided
+    // or if needed for keypair regeneration validation
+    if ( (pbPublicKey==NULL) ||
+         ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+          (pbPrivateKey!=NULL) && (pbPublicKey!=NULL) ) )
+    {
+        // Calculate the public key from the private key
+        pbScratchInternal = pbScratch;
+        cbScratchInternal = cbScratch;
+
+        // By default calculate the public key directly where it will be persisted
+        peTmp = pkDlkey->pePublicKey;
+
+        if ( pbPublicKey != NULL )
+        {
+            // If doing regeneration validation calculate the public key in scratch
+            peTmp = SymCryptModElementCreate( pbScratchInternal, cbModElement, pDlgroup->pmP);
+            pbScratchInternal += cbModElement;
+            cbScratchInternal -= cbModElement;
+        }
+
+        SymCryptModExp(
+                pDlgroup->pmP,
+                pDlgroup->peG,
+                pkDlkey->piPrivateKey,
+                nBitsPriv,  // This is either bits of P, Q, or some caller-defined value i.e. public values
+                0,          // Side-channel safe
+                peTmp,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        if ( pbPublicKey != NULL )
+        {
+            if ( !SymCryptModElementIsEqual(pDlgroup->pmP, peTmp, pkDlkey->pePublicKey) )
+            {
+                scError = SYMCRYPT_AUTHENTICATION_FAILURE;
+                goto cleanup;
+            }
+        }
+        else if ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) == 0 )
+        {
+            // Perform Public key validation on generated public key.
+            // Always perform range validation
+            // May also perform validation that Public key is in subgroup of order Q, depending on flags
+            scError = SymCryptDlkeyPerformPublicKeyValidation(
+                pkDlkey,
+                fValidatePublicKeyOrder,
+                pbScratch,
+                cbScratch );
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    pkDlkey->fAlgorithmInfo = flags; // We want to track all of the flags in the Dlkey
+
+    if ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 )
+    {
+        if( ( flags & SYMCRYPT_FLAG_DLKEY_DSA ) != 0 )
+        {
+            // Ensure DSA algorithm selftest is run before first use of DSA algorithm
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptDsaSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_DSA );
+
+            // PCT does not need to be run on import - mark it as done
+            pkDlkey->fAlgorithmInfo |= SYMCRYPT_PCT_DSA;
+        }
+
+        if( ( flags & SYMCRYPT_FLAG_DLKEY_DH ) != 0 )
+        {
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptDhSecretAgreementSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_DH );
+        }
+    }
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyGetValue(
+    _In_    PCSYMCRYPT_DLKEY        pkDlkey,
+    _Out_writes_bytes_( cbPrivateKey )
+            PBYTE                   pbPrivateKey,
+            SIZE_T                  cbPrivateKey,
+    _Out_writes_bytes_( cbPublicKey )
+            PBYTE                   pbPublicKey,
+            SIZE_T                  cbPublicKey,
+            SYMCRYPT_NUMBER_FORMAT  numFormat,
+            UINT32                  flags )
+{
+    SYMCRYPT_ERROR      scError = SYMCRYPT_NO_ERROR;
+    PBYTE               pbScratch = NULL;
+    UINT32              cbScratch = 0;
+
+    PCSYMCRYPT_DLGROUP pDlgroup = pkDlkey->pDlgroup;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    if ( ((pbPrivateKey==NULL) && (cbPrivateKey!=0)) ||
+         ((pbPublicKey==NULL) && (cbPublicKey!=0)) ||
+         ((pbPrivateKey==NULL) && (pbPublicKey==NULL)) ||
+         ((pbPrivateKey!=NULL) && !pkDlkey->fHasPrivateKey) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (pbPrivateKey != NULL)
+    {
+        scError = SymCryptIntGetValue(
+                        pkDlkey->piPrivateKey,
+                        pbPrivateKey,
+                        cbPrivateKey,
+                        numFormat );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    if (pbPublicKey != NULL)
+    {
+        cbScratch = SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(pDlgroup->nDigitsOfP);
+        pbScratch = SymCryptCallbackAlloc( cbScratch );
+        if (pbScratch == NULL)
+        {
+            scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+            goto cleanup;
+        }
+
+        scError = SymCryptModElementGetValue(
+                        pDlgroup->pmP,
+                        pkDlkey->pePublicKey,
+                        pbPublicKey,
+                        cbPublicKey,
+                        numFormat,
+                        pbScratch,
+                        cbScratch );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDlkeyExtendKeyUsage(
+    _Inout_ PSYMCRYPT_DLKEY pkDlkey,
+            UINT32          flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_DLKEY_DSA | SYMCRYPT_FLAG_DLKEY_DH;
+
+    if ( ( ( flags & ~algorithmFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pkDlkey->fAlgorithmInfo |= flags;
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/dsa.c b/libs/symcrypt/lib/dsa.c
new file mode 100644
index 000000000000..068d413c8a44
--- /dev/null
+++ b/libs/symcrypt/lib/dsa.c
@@ -0,0 +1,695 @@
+//
+// dsa.c   DSA functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Truncating function according to the FIPS 186-4 standard
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaTruncateHash(
+    _In_                            PCSYMCRYPT_DLGROUP      pDlgroup,
+    _In_reads_bytes_( cbHashValue ) PCBYTE                  pbHashValue,
+                                    SIZE_T                  cbHashValue,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peMsghash,
+    _Out_                           PSYMCRYPT_INT           piIntLarge,
+    _Out_                           PSYMCRYPT_INT           piIntQ,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Get the value of msghash into piIntLarge
+    scError = SymCryptIntSetValue( pbHashValue, cbHashValue, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piIntLarge );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Truncate the rightmost bits if the value exceeds the size of the modulus Q
+    if (SymCryptIntBitsizeOfValue(piIntLarge) > pDlgroup->nBitsOfQ)
+    {
+        SymCryptIntDivPow2( piIntLarge, SymCryptIntBitsizeOfValue(piIntLarge) - pDlgroup->nBitsOfQ, piIntLarge );
+    }
+
+    scError = SymCryptIntCopyMixedSize( piIntLarge, piIntQ );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        // This should never fail here as we truncated the IntLarge
+        goto cleanup;
+    }
+
+    // Now we can call IntToModElement as they have the same digit size
+    SymCryptIntToModElement( piIntQ, pDlgroup->pmQ, peMsghash, pbScratch, cbScratch );        // msghash mod Q
+
+cleanup:
+    return scError;
+}
+
+#define SYMCRYPT_MAX_DSA_SIGNATURE_COUNT (100)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaSignEx(
+    _In_                                PCSYMCRYPT_DLKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_opt_                            PCSYMCRYPT_INT          piK,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+    PBYTE   pbScratchInternal = NULL;
+    SIZE_T  cbScratchInternal = 0;
+
+    SIZE_T  cbScratchInputK = 0;        // Extra scratch space needed if the caller specified a K
+
+    PCSYMCRYPT_DLGROUP pDlgroup = pKey->pDlgroup;
+    UINT32 nDigitsOfP = pDlgroup->nDigitsOfP;
+    UINT32 nDigitsOfQ = pDlgroup->nDigitsOfQ;
+
+    UINT32 ndIntLarge = 0;
+
+    UINT32 cbIntLarge = 0;
+    UINT32 cbIntQ = 0;
+    UINT32 cbIntP = 0;
+
+    UINT32 cbModelementP = 0;
+    UINT32 cbModelementQ = 0;
+
+    // Helper Integers
+    PSYMCRYPT_INT piIntLarge = NULL;    // Safe size for all caller specified sizes
+    PSYMCRYPT_INT piIntP = NULL;        // Same number of digits as P
+    PSYMCRYPT_INT piIntQ = NULL;        // Same number of digits as Q
+
+    // Elements modulo P
+    PSYMCRYPT_MODELEMENT peRmodP = NULL;
+
+    // Elements modulo Q
+    PSYMCRYPT_MODELEMENT peMsghash = NULL;
+    PSYMCRYPT_MODELEMENT peRmodQ = NULL;
+    PSYMCRYPT_MODELEMENT peK = NULL;
+    PSYMCRYPT_MODELEMENT peS = NULL;
+
+    UINT32 signatureCount = 0;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key may be used in DSA
+    if ( ((pKey->fAlgorithmInfo & SYMCRYPT_FLAG_DLKEY_DSA) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the group and the key have all the
+    // information for dsa, i.e. prime q and private key
+    // modulo q, and we are not using a named DH safe-prime
+    // group
+    if ((!pDlgroup->fHasPrimeQ) ||
+        (!pKey->fHasPrivateKey) ||
+        (!pKey->fPrivateModQ) ||
+        (pDlgroup->isSafePrimeGroup))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Calculate the digit size for the HashValue
+    ndIntLarge = SymCryptDigitsFromBits( (UINT32)cbHashValue * 8 );
+
+    // Calculate the sizes of temp objects
+    cbIntLarge = SymCryptSizeofIntFromDigits(ndIntLarge);
+    cbIntQ = SymCryptSizeofIntFromDigits(nDigitsOfQ);
+    cbIntP = SymCryptSizeofIntFromDigits(nDigitsOfP);
+
+    cbModelementP = SymCryptSizeofModElementFromModulus( pDlgroup-> pmP );
+    cbModelementQ = SymCryptSizeofModElementFromModulus( pDlgroup-> pmQ );
+
+    // Allocate scratch space
+    cbScratchInputK = (piK==NULL)?0:SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD(SymCryptIntDigitsizeOfObject(piK),nDigitsOfQ);
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = cbIntLarge + cbIntQ + cbIntP + cbModelementP + 4*cbModelementQ +
+                SYMCRYPT_MAX( cbScratchInputK,
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfQ ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfP ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigitsOfP ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( nDigitsOfQ ) ))));
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch==NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Create the objects
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = cbScratch;
+
+    piIntLarge = SymCryptIntCreate(pbScratchInternal, cbIntLarge, ndIntLarge); pbScratchInternal += cbIntLarge; cbScratchInternal -= cbIntLarge;
+    piIntQ = SymCryptIntCreate(pbScratchInternal, cbIntQ, nDigitsOfQ); pbScratchInternal += cbIntQ; cbScratchInternal -= cbIntQ;
+    piIntP = SymCryptIntCreate(pbScratchInternal, cbIntP, nDigitsOfP); pbScratchInternal += cbIntP; cbScratchInternal -= cbIntP;
+
+    peRmodP = SymCryptModElementCreate(pbScratchInternal, cbModelementP, pDlgroup->pmP); pbScratchInternal += cbModelementP; cbScratchInternal -= cbModelementP;
+
+    peMsghash = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+    peRmodQ = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+    peK = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+    peS = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+
+    // Get the message into a modelement
+    scError = SymCryptDsaTruncateHash(
+                    pDlgroup,
+                    pbHashValue,
+                    cbHashValue,
+                    flags,
+                    peMsghash,
+                    piIntLarge,
+                    piIntQ,
+                    pbScratchInternal,
+                    cbScratchInternal );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    //
+    // Main loop: Stop when both R and S are not zero (unless a specific k is provided)
+    //
+    while( TRUE )
+    {
+        if (piK==NULL)
+        {
+            SymCryptModSetRandom(
+                pDlgroup->pmQ,
+                peK,
+                SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE|SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE,
+                pbScratchInternal,
+                cbScratchInternal );
+
+            SymCryptModElementToInt(
+                pDlgroup->pmQ,
+                peK,
+                piIntQ,
+                pbScratchInternal,
+                cbScratchInternal );
+        }
+        else
+        {
+            SymCryptIntDivMod(
+                piK,
+                SymCryptDivisorFromModulus( pDlgroup->pmQ ),
+                NULL,
+                piIntQ,
+                pbScratchInternal,
+                cbScratchInternal );
+
+            SymCryptIntToModElement(
+                piIntQ,
+                pDlgroup->pmQ,
+                peK,
+                pbScratchInternal,
+                cbScratchInternal );
+
+            // Make sure that the K passed in is not zero
+            if (SymCryptModElementIsZero(pDlgroup->pmQ, peK))
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+        }
+
+        // Here piIntQ and peK hold the random exponent K
+
+        // G^K mod P
+        SymCryptModExp(
+                pDlgroup->pmP,
+                pDlgroup->peG,
+                piIntQ,
+                pDlgroup->nBitsOfQ,
+                0,      // Side-channel safe
+                peRmodP,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // Convert to integer
+        SymCryptModElementToInt(
+                pDlgroup->pmP,
+                peRmodP,
+                piIntP,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // Convert to mod Q
+        SymCryptIntDivMod(
+                piIntP,
+                SymCryptDivisorFromModulus( pDlgroup->pmQ ),
+                NULL,
+                piIntQ,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // Convert to modelement
+        SymCryptIntToModElement(
+                piIntQ,
+                pDlgroup->pmQ,
+                peRmodQ,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // Invert k mod q
+        scError = SymCryptModInv(
+                pDlgroup->pmQ,
+                peK,
+                peK,    // In place
+                0,
+                pbScratchInternal,
+                cbScratchInternal );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Get the private key X to modelement
+        // *** We are sure here that the digit
+        // size of it is nDigitsOfQ
+        SymCryptIntToModElement(
+                pKey->piPrivateKey,
+                pDlgroup->pmQ,
+                peS,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // X*R
+        SymCryptModMul(
+                pDlgroup->pmQ,
+                peS,
+                peRmodQ,
+                peS,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // H(m)+X*R
+        SymCryptModAdd(
+                pDlgroup->pmQ,
+                peS,
+                peMsghash,
+                peS,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        // S = k^{-1}*(H(m)+X*R)
+        SymCryptModMul(
+                pDlgroup->pmQ,
+                peK,
+                peS,
+                peS,
+                pbScratchInternal,
+                cbScratchInternal );
+
+        if ( !( SymCryptModElementIsZero( pDlgroup->pmQ, peRmodQ ) |
+                SymCryptModElementIsZero( pDlgroup->pmQ, peS ) ) )
+        {
+            break;
+        }
+
+        if (piK != NULL)
+        {
+            // piK resulted in 0 signature
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        signatureCount++;
+        if ( signatureCount >= SYMCRYPT_MAX_DSA_SIGNATURE_COUNT )
+        {
+            // We have not generated a non-zero signature after SYMCRYPT_MAX_DSA_SIGNATURE_COUNT attempts;
+            // Something is wrong with the group setup
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+    // Output R
+    scError = SymCryptModElementGetValue(
+                pDlgroup->pmQ,
+                peRmodQ,
+                pbSignature,
+                cbSignature / 2,
+                format,
+                pbScratchInternal,
+                cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Output S
+    scError = SymCryptModElementGetValue(
+                pDlgroup->pmQ,
+                peS,
+                pbSignature + cbSignature / 2,
+                cbSignature / 2,
+                format,
+                pbScratchInternal,
+                cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        SymCryptWipe( pbSignature, cbSignature );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaSign(
+    _In_                                PCSYMCRYPT_DLKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature )
+{
+    return SymCryptDsaSignEx( pKey, pbHashValue, cbHashValue, NULL, format, flags, pbSignature, cbSignature );
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaVerify(
+    _In_                                PCSYMCRYPT_DLKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                  pbSignature,
+                                        SIZE_T                  cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BOOLEAN fValidSignature = FALSE;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+    PBYTE   pbScratchInternal = NULL;
+    SIZE_T  cbScratchInternal = 0;
+
+    PCSYMCRYPT_DLGROUP pDlgroup = pKey->pDlgroup;
+    UINT32 nDigitsOfP = pDlgroup->nDigitsOfP;
+    UINT32 nDigitsOfQ = pDlgroup->nDigitsOfQ;
+
+    UINT32 ndIntLarge = 0;
+
+    UINT32 cbIntLarge = 0;
+    UINT32 cbIntQ = 0;
+    UINT32 cbIntP = 0;
+
+    UINT32 cbModelementP = 0;
+    UINT32 cbModelementQ = 0;
+
+    PSYMCRYPT_MODELEMENT peBases[2] = { NULL, NULL }; // Array with pointers to base points
+
+    // Helper Integers
+    PSYMCRYPT_INT piIntLarge = NULL;            // Safe size for all caller specified sizes
+    PSYMCRYPT_INT piIntP = NULL;                // Same number of digits as P
+    PSYMCRYPT_INT piIntQ[2] = { NULL, NULL };   // Same number of digits as Q
+
+    // Elements modulo P
+    PSYMCRYPT_MODELEMENT peResP = NULL;
+
+    // Elements modulo Q
+    PSYMCRYPT_MODELEMENT peR = NULL;
+    PSYMCRYPT_MODELEMENT peS = NULL;
+    PSYMCRYPT_MODELEMENT peT = NULL;    // Temp
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key may be used in DSA
+    if ( ((pKey->fAlgorithmInfo & SYMCRYPT_FLAG_DLKEY_DSA) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the group has a prime q, and we are not using a named DH safe-prime group
+    if (!pDlgroup->fHasPrimeQ || pDlgroup->isSafePrimeGroup)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Calculate the digit sizes
+    ndIntLarge = SymCryptDigitsFromBits( (UINT32)cbHashValue * 8 );
+    ndIntLarge = SYMCRYPT_MAX( ndIntLarge, SymCryptDigitsFromBits( (UINT32)cbSignature * 4 ) );  // pbSignature contains (R,S)
+
+    // Calculate the sizes of temp objects
+    cbIntLarge = SymCryptSizeofIntFromDigits(ndIntLarge);
+    cbIntQ = SymCryptSizeofIntFromDigits(nDigitsOfQ);
+    cbIntP = SymCryptSizeofIntFromDigits(nDigitsOfP);
+
+    cbModelementP = SymCryptSizeofModElementFromModulus( pDlgroup-> pmP );
+    cbModelementQ = SymCryptSizeofModElementFromModulus( pDlgroup-> pmQ );
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = cbIntLarge + cbIntP + 2*cbIntQ + cbModelementP + 3*cbModelementQ +
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD(nDigitsOfP,nDigitsOfQ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfQ ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfP ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODMULTIEXP( SymCryptModulusDigitsizeOfObject(pDlgroup->pmP), 2, pDlgroup->nBitsOfQ ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( nDigitsOfQ ) ))));
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch==NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Create the objects
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = cbScratch;
+
+    piIntLarge = SymCryptIntCreate(pbScratchInternal, cbIntLarge, ndIntLarge); pbScratchInternal += cbIntLarge; cbScratchInternal -= cbIntLarge;
+    piIntP = SymCryptIntCreate(pbScratchInternal, cbIntP, nDigitsOfP); pbScratchInternal += cbIntP; cbScratchInternal -= cbIntP;
+    piIntQ[0] = SymCryptIntCreate(pbScratchInternal, cbIntQ, nDigitsOfQ); pbScratchInternal += cbIntQ; cbScratchInternal -= cbIntQ;
+    piIntQ[1] = SymCryptIntCreate(pbScratchInternal, cbIntQ, nDigitsOfQ); pbScratchInternal += cbIntQ; cbScratchInternal -= cbIntQ;
+
+    peResP = SymCryptModElementCreate(pbScratchInternal, cbModelementP, pDlgroup->pmP); pbScratchInternal += cbModelementP; cbScratchInternal -= cbModelementP;
+
+    peR = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+    peS = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+    peT = SymCryptModElementCreate(pbScratchInternal, cbModelementQ, pDlgroup->pmQ); pbScratchInternal += cbModelementQ; cbScratchInternal -= cbModelementQ;
+
+    // Get R
+    scError = SymCryptIntSetValue( pbSignature, cbSignature / 2, format, piIntLarge );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Check if R is less than Q
+    if ( !SymCryptIntIsLessThan( piIntLarge, SymCryptIntFromModulus( pDlgroup->pmQ ) ) )
+    {
+        goto cleanup;
+    }
+
+    // R mod Q (use piIntQ[0] as temp space)
+    scError = SymCryptIntCopyMixedSize( piIntLarge, piIntQ[0] );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        // This should never fail here as we verified that IntLarge is less than Q
+        goto cleanup;
+    }
+    SymCryptIntToModElement( piIntQ[0], pDlgroup->pmQ, peR, pbScratchInternal, cbScratchInternal );
+
+    // Check if R is zero
+    if (SymCryptModElementIsZero( pDlgroup->pmQ, peR ))
+    {
+        goto cleanup;
+    }
+
+    // Get S
+    scError = SymCryptIntSetValue( pbSignature + cbSignature / 2, cbSignature / 2, format, piIntLarge );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Check if S is less than Q
+    if ( !SymCryptIntIsLessThan( piIntLarge, SymCryptIntFromModulus( pDlgroup->pmQ ) ) )
+    {
+        goto cleanup;
+    }
+
+    // S mod Q (use piIntQ[0] as temp space)
+    scError = SymCryptIntCopyMixedSize( piIntLarge, piIntQ[0] );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        // This should never fail here as we verified that IntLarge is less than Q
+        goto cleanup;
+    }
+    SymCryptIntToModElement( piIntQ[0], pDlgroup->pmQ, peS, pbScratchInternal, cbScratchInternal );
+
+    // Check if S is zero
+    if (SymCryptModElementIsZero( pDlgroup->pmQ, peS ))
+    {
+        goto cleanup;
+    }
+
+    // Calculate 1/S mod Q
+    // S is part of the signature and therefore not a secret.
+    // We mark it public to avoid the use of random blinding, which would require a source of randomness
+    // just to verify a DSA signature.
+    scError = SymCryptModInv( pDlgroup->pmQ, peS, peS, SYMCRYPT_FLAG_DATA_PUBLIC, pbScratchInternal, cbScratchInternal );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Get the message into a modelement
+    scError = SymCryptDsaTruncateHash(
+                    pDlgroup,
+                    pbHashValue,
+                    cbHashValue,
+                    flags,
+                    peT,
+                    piIntLarge,
+                    piIntQ[0],
+                    pbScratchInternal,
+                    cbScratchInternal );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Calculate U1 = Hash(M)/S modQ
+    SymCryptModMul(
+            pDlgroup->pmQ,
+            peT,
+            peS,
+            peT,
+            pbScratchInternal,
+            cbScratchInternal );
+
+    // Convert U1 to integer
+    SymCryptModElementToInt(
+                pDlgroup->pmQ,
+                peT,
+                piIntQ[0],
+                pbScratchInternal,
+                cbScratchInternal );
+
+    // Calculate U2 = R/S modQ
+    SymCryptModMul(
+            pDlgroup->pmQ,
+            peR,
+            peS,
+            peT,
+            pbScratchInternal,
+            cbScratchInternal );
+
+    // Convert U2 to integer
+    SymCryptModElementToInt(
+                pDlgroup->pmQ,
+                peT,
+                piIntQ[1],
+                pbScratchInternal,
+                cbScratchInternal );
+
+    // Arrange the pointers for v = G^U1 * Y^U2
+    peBases[0] = pDlgroup->peG;
+    peBases[1] = pKey->pePublicKey;
+
+    // v = G^U1 * Y^U2
+    scError = SymCryptModMultiExp(
+                pDlgroup->pmP,
+                peBases,
+                piIntQ,
+                2,
+                pDlgroup->nBitsOfQ,
+                SYMCRYPT_FLAG_DATA_PUBLIC,
+                peResP,
+                pbScratchInternal,
+                cbScratchInternal );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Convert V to a modelement modulo Q
+    SymCryptModElementToInt(
+                pDlgroup->pmP,
+                peResP,
+                piIntP,
+                pbScratchInternal,
+                cbScratchInternal );
+    SymCryptIntDivMod(
+                piIntP,
+                SymCryptDivisorFromModulus( pDlgroup->pmQ ),
+                NULL,
+                piIntQ[0],
+                pbScratchInternal,
+                cbScratchInternal );
+    SymCryptIntToModElement(
+                piIntQ[0],
+                pDlgroup->pmQ,
+                peT,
+                pbScratchInternal,
+                cbScratchInternal );
+
+    // Comparison V = R
+    if (SymCryptModElementIsEqual( pDlgroup->pmQ, peT, peR ))
+    {
+        fValidSignature = TRUE;
+    }
+
+
+cleanup:
+
+    if (!fValidSignature)
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+    }
+
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ec_dh.c b/libs/symcrypt/lib/ec_dh.c
new file mode 100644
index 000000000000..e7b840786452
--- /dev/null
+++ b/libs/symcrypt/lib/ec_dh.c
@@ -0,0 +1,157 @@
+//
+// ec_dh.c   ECDH function
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDhSecretAgreement(
+    _In_                            PCSYMCRYPT_ECKEY        pkPrivate,
+    _In_                            PCSYMCRYPT_ECKEY        pkPublic,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+                                    UINT32                  flags,
+    _Out_writes_( cbAgreedSecret )  PBYTE                   pbAgreedSecret,
+                                    SIZE_T                  cbAgreedSecret )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+    SIZE_T  cbScratchInternal = 0;
+    PBYTE   pCurr = NULL;
+
+    PCSYMCRYPT_ECURVE   pCurve = NULL;
+
+    PSYMCRYPT_ECPOINT   poQ = NULL;
+    PBYTE               pbX = NULL;
+
+    UINT32              cbQ = 0;
+    UINT32              cbX = 0;
+
+    // Make sure that the keys may be used in ECDH
+    if ( ((pkPrivate->fAlgorithmInfo & SYMCRYPT_FLAG_ECKEY_ECDH) == 0) ||
+         ((pkPublic->fAlgorithmInfo & SYMCRYPT_FLAG_ECKEY_ECDH) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure we only specify the correct flags and that
+    // there is a private key
+    if ( (flags != 0) ||
+         (!pkPrivate->hasPrivateKey) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check that the curve is the same for both keys
+    if ( SymCryptEcurveIsSame( pkPrivate->pCurve, pkPublic->pCurve ) )
+    {
+        pCurve = pkPrivate->pCurve;
+    }
+    else
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Objects and scratch space size calculation
+    cbQ = SymCryptSizeofEcpointFromCurve( pCurve );
+    cbX  = SymCryptEcurveSizeofFieldElement( pCurve );
+
+    // Check the output buffer has the correct size
+    if (cbAgreedSecret != cbX)
+    {
+        scError = SYMCRYPT_WRONG_BLOCK_SIZE;
+        goto cleanup;
+    }
+
+    cbScratchInternal = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS(pCurve),
+                        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( pCurve ),
+                             SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) ));
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = cbScratchInternal + cbQ + cbX;
+
+    // Scratch space allocation
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Creating temporaries
+    pCurr = pbScratch + cbScratchInternal;
+    poQ = SymCryptEcpointCreate( pCurr, cbQ, pCurve );
+    pCurr += cbQ;
+    pbX = pCurr;
+
+    SYMCRYPT_ASSERT( poQ != NULL);
+
+    // Make sure that the public key is not the zero point
+    // No need to check that the point is on the curve; that check is done when the
+    // public key is created.
+    if (SymCryptEcpointIsZero(pCurve, pkPublic->poPublicKey, pbScratch, cbScratchInternal))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Calculate the secret
+    // Always do low order clearing by multiplying by the cofactor.
+    //  Note:   the internal format of piPrivateKey is "DivH", so we
+    //          get the correct result.
+    scError = SymCryptEcpointScalarMul(
+                pCurve,
+                pkPrivate->piPrivateKey,
+                pkPublic->poPublicKey,
+                SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL,
+                poQ,
+                pbScratch,
+                cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Check if the result is the identity point
+    if ( SymCryptEcpointIsZero(
+                pCurve,
+                poQ,
+                pbScratch,
+                cbScratchInternal ) )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Get the x from poQ
+    scError = SymCryptEcpointGetValue( pCurve, poQ, format, SYMCRYPT_ECPOINT_FORMAT_X, pbX, cbX, 0, pbScratch, cbScratchInternal);
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Store it in the destination
+    memcpy( pbAgreedSecret, pbX, cbX);
+
+cleanup:
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ec_dispatch.c b/libs/symcrypt/lib/ec_dispatch.c
new file mode 100644
index 000000000000..02229ac5efcf
--- /dev/null
+++ b/libs/symcrypt/lib/ec_dispatch.c
@@ -0,0 +1,300 @@
+//
+// ec_dispatch.c   Dispatch file for elliptic curve crypto functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Table with all the pointers to SYMCRYPT_ECURVE_FUNCTIONS
+const SYMCRYPT_ECURVE_FUNCTIONS SymCryptEcurveDispatchTable[] =
+{
+    // NULL Type
+    {
+        NULL,       // SymCryptEcpointSetZeroNotImplemented,
+        NULL,       // SymCryptEcpointSetDistinguishedPointNotImplemented,
+        NULL,       // SymCryptEcpointSetRandomNotImplemented,
+        NULL,       // SymCryptEcpointIsEqualNotImplemented,
+        NULL,       // SymCryptEcpointIsZeroNotImplemented,
+        NULL,       // SymCryptEcpointOnCurveNotImplemented,
+        NULL,       // SymCryptEcpointAddNotImplemented,
+        NULL,       // SymCryptEcpointAddDiffNonZeroNotImplemented,
+        NULL,       // SymCryptEcpointDoubleNotImplemented,
+        NULL,       // SymCryptEcpointNegateNotImplemented,
+        NULL,       // SymCryptEcpointScalarMulNotImplemented,
+        NULL,       // SymCryptEcpointMultiScalarMulNotImplemented,
+        NULL,       // SymCryptEcurveFillScratchSpacesNotImplemented,
+    },
+    // Short Weierstrass
+    {
+        SymCryptShortWeierstrassSetZero,
+        SymCryptShortWeierstrassSetDistinguished,
+        SymCryptEcpointGenericSetRandom,
+        SymCryptShortWeierstrassIsEqual,
+        SymCryptShortWeierstrassIsZero,
+        SymCryptShortWeierstrassOnCurve,
+        SymCryptShortWeierstrassAdd,
+        SymCryptShortWeierstrassAddDiffNonZero,
+        SymCryptShortWeierstrassDouble,
+        SymCryptShortWeierstrassNegate,
+        SymCryptEcpointScalarMulFixedWindow,
+        SymCryptEcpointMultiScalarMulWnafWithInterleaving,
+        SymCryptShortWeierstrassFillScratchSpaces,
+    },
+    // Twisted Edwards
+    {
+        SymCryptTwistedEdwardsSetZero,
+        SymCryptTwistedEdwardsSetDistinguished,
+        SymCryptEcpointGenericSetRandom,
+        SymCryptTwistedEdwardsIsEqual,
+        SymCryptTwistedEdwardsIsZero,
+        SymCryptTwistedEdwardsOnCurve,
+        SymCryptTwistedEdwardsAdd,
+        SymCryptTwistedEdwardsAddDiffNonZero,
+        SymCryptTwistedEdwardsDouble,
+        SymCryptTwistedEdwardsNegate,
+        SymCryptEcpointScalarMulFixedWindow,
+        SymCryptEcpointMultiScalarMulWnafWithInterleaving,
+        SymCryptTwistedEdwardsFillScratchSpaces,
+    },
+    // Montgomery
+    {
+        NULL,       // SymCryptEcpointSetZeroNotImplemented,
+        SymCryptMontgomerySetDistinguished,
+        SymCryptEcpointGenericSetRandom,
+        SymCryptMontgomeryIsEqual,
+        SymCryptMontgomeryIsZero,
+        NULL,       // SymCryptEcpointOnCurveNotImplemented,
+        NULL,       // SymCryptEcpointAddNotImplemented,
+        NULL,       // SymCryptEcpointAddDiffNonZeroNotImplemented,
+        NULL,       // SymCryptEcpointDoubleNotImplemented,
+        NULL,       // SymCryptEcpointNegateNotImplemented,
+        SymCryptMontgomeryPointScalarMul,
+        NULL,       // SymCryptEcpointMultiScalarMulNotImplemented,
+        SymCryptMontgomeryFillScratchSpaces,
+    },
+    // Short Weierstrass with A==-3
+    {
+        SymCryptShortWeierstrassSetZero,
+        SymCryptShortWeierstrassSetDistinguished,
+        SymCryptEcpointGenericSetRandom,
+        SymCryptShortWeierstrassIsEqual,
+        SymCryptShortWeierstrassIsZero,
+        SymCryptShortWeierstrassOnCurve,
+        SymCryptShortWeierstrassAdd,
+        SymCryptShortWeierstrassAddDiffNonZero,
+        SymCryptShortWeierstrassDoubleSpecializedAm3,
+        SymCryptShortWeierstrassNegate,
+        SymCryptEcpointScalarMulFixedWindow,
+        SymCryptEcpointMultiScalarMulWnafWithInterleaving,
+        SymCryptShortWeierstrassFillScratchSpaces,
+    },
+    // Slack to make dispatch table size a power of 2
+    {NULL,},
+    {NULL,},
+    {NULL,},
+};
+
+#define SYMCRYPT_ECURVE_DISPATCH_TABLE_SIZE (sizeof( SymCryptEcurveDispatchTable ))
+
+// Ensure the table size is a power of 2
+C_ASSERT( (SYMCRYPT_ECURVE_DISPATCH_TABLE_SIZE & (SYMCRYPT_ECURVE_DISPATCH_TABLE_SIZE - 1)) == 0 );
+
+// For now the ECurve type encodes the index into this dispatch table, so we just mask by the size of the table
+//
+// We could instead encode the absolute offset into the table in the type field (similar to the Modulus dispatch table),
+// and this mask would be multiplied by SYMCRYPT_ECURVE_FUNCTIONS_SIZE
+#define SYMCRYPT_ECURVE_DISPATCH_TABLE_MASK ((SYMCRYPT_ECURVE_DISPATCH_TABLE_SIZE / SYMCRYPT_ECURVE_FUNCTIONS_SIZE)-1)
+
+// We mask to constrain the unpredictable behaviour in the case of memory corruption; we do not want to interpret some data
+// beyond the end of the dispatch table as function pointers
+#define SYMCRYPT_ECURVE_CALL(v) (SymCryptEcurveDispatchTable[SYMCRYPT_FORCE_READ32(&(v)->type) & SYMCRYPT_ECURVE_DISPATCH_TABLE_MASK]).
+
+// We read the curve's internal type with a 32b read so it must be 4 bytes large
+C_ASSERT(sizeof(((PCSYMCRYPT_ECURVE)0)->type) == 4);
+
+// Main functions
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointSetZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) setZeroFunc( pCurve, poDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointSetDistinguishedPoint(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) setDistinguishedFunc( pCurve, poDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointSetRandom(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _Out_   PSYMCRYPT_INT           piScalar,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) setRandomFunc( pCurve, piScalar, poDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+UINT32
+SYMCRYPT_CALL
+SymCryptEcpointIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    return SYMCRYPT_ECURVE_CALL( pCurve ) isEqualFunc( pCurve, poSrc1, poSrc2, flags, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+UINT32
+SYMCRYPT_CALL
+SymCryptEcpointIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    return SYMCRYPT_ECURVE_CALL( pCurve ) isZeroFunc( pCurve, poSrc, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+UINT32
+SYMCRYPT_CALL
+SymCryptEcpointOnCurve(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    return SYMCRYPT_ECURVE_CALL( pCurve ) onCurveFunc( pCurve, poSrc, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointAdd(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) addFunc( pCurve, poSrc1, poSrc2, poDst, flags, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointAddDiffNonZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) addDiffFunc( pCurve, poSrc1, poSrc2, poDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointDouble(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) doubleFunc( pCurve, poSrc, poDst, flags, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointNegate(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) negateFunc( pCurve, poSrc, mask, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointScalarMul(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_INT      piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT  poSrc,
+            UINT32              flags,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    return SYMCRYPT_ECURVE_CALL( pCurve ) scalarMulFunc( pCurve, piScalar, poSrc, flags, poDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointMultiScalarMul(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _In_    PCSYMCRYPT_INT *        piSrcScalarArray,
+    _In_    PCSYMCRYPT_ECPOINT *    poSrcEcpointArray,
+            UINT32                  nPoints,
+            UINT32                  flags,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch )
+{
+    return SYMCRYPT_ECURVE_CALL( pCurve ) multiScalarMulFunc( pCurve, piSrcScalarArray, poSrcEcpointArray, nPoints, flags, poDst, pbScratch, cbScratch );
+}
+
+SYMCRYPT_DISABLE_CFG
+VOID
+SYMCRYPT_CALL
+SymCryptEcurveFillScratchSpaces(
+    _Inout_    PSYMCRYPT_ECURVE pCurve )
+{
+    SYMCRYPT_ECURVE_CALL( pCurve ) fillScratchSpacesFunc( pCurve );
+}
diff --git a/libs/symcrypt/lib/ec_dsa.c b/libs/symcrypt/lib/ec_dsa.c
new file mode 100644
index 000000000000..b9da0063e272
--- /dev/null
+++ b/libs/symcrypt/lib/ec_dsa.c
@@ -0,0 +1,694 @@
+//
+// ec_dsa.c   ECDSA functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+/*
+    Sections 7.2.7 and 7.2.8 of the 29 August 2000
+    IEEE Standard Specifications for Public-Key Cryptography,
+    IEEE Std 1363-2000, list DSA versions of the elliptic
+    curve signature and verification primitives.
+    This file has draft interfaces,
+
+    7.2.7  ECSP_DSA (pages 35-36)
+
+       Inputs:
+          E -- An elliptic curve.
+          G (generator) -- A point on E of prime order r.
+          r -- See G.
+          s -- A secret exponent, 1 <= s < r (Private key)
+          msghash -- Hash of the message being signed.
+
+        Outputs:
+           c, d --  Two integers in the interval [1, r-1]
+
+        Algorithm:
+           1) Generate random exponent k, 1 <= k < r,
+              to be kept from adversary.
+              Compute KG = k*G in E.
+              Note KG <> (point at infinity).
+           2) Convert x(KG) (an element of GF(q))
+              to an integer FE2IP(x(KG)).
+              Let c = FE2IP(x(KG)) (mod r).
+           3) Compute d = (msghash + s*c)/k (mod r).
+           4) If c == 0 or d == 0, return to 1).
+           5) Output c and d as integers.
+
+    7.2.8  ECVP_DSA
+
+        Inputs:
+            E, G, r, msghash -- Same as in ECSP_DSA.
+            W -- The signer's public key.  Equal to
+                s*G where s was passed to ECSP_DSA.
+            c, d  -- A signature to be checked.
+
+        Output:
+            TRUE if signature OK, else FALSE.
+
+        Algorithm:
+            1) If c or d is not in [1, r-1], return FALSE.
+            2) Compute h1 = msghash/d (mod r)
+               and h2 = c/d (mod r).
+            3) Compute P = h1*G + h2*W.
+               If P == (point at infinity), return FALSE.
+            4) If c == FE2IP(x(P)) mod r, return TRUE.
+               Otherwise return FALSE.
+
+FE2IP is a P1363 function that casts a field element to an
+integer (MSB_FIRST).  See Section 5.5.5 of P1363.
+*/
+
+//
+// Truncating function according to the standard or
+// the original CNG implementation:
+//
+// Initially both implementations truncate the last **bytes**
+// of the hash that are over the group byte length. Then if
+// the bit length of the hash is still bigger than the bit
+// length of the group order, ...
+//
+// 1. According to the X9.62 standard, we do an appropriate right shift to the entire hash.
+//      An example of this is a 160-bit hash, but a 113-bit subgroup order.  For this case:
+//      a. We would truncate cbHash to (113 + 7) / 8 = 15 bytes.
+//      b. Since 15*8 = 120 > 113 we need to right-shift by 7 bits.
+// 2. According to the original CNG implementation, we mask an appropriate number of the
+//    topmost bits of the hash.
+//      In the same example as before we would zero out the top 7 bits.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaTruncateHash(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_reads_bytes_( cbHashValue ) PCBYTE                  pbHashValue,
+                                    SIZE_T                  cbHashValue,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peMsghash,
+    _Out_                           PSYMCRYPT_INT           piTmp,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 uiBitsizeOfTmp = 0;
+    UINT32 uiBitsizeOfGroup = 0;
+
+    // Make sure that only the correct flags are set
+    if ( (flags & ~SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Get the bitsize of the group order
+    uiBitsizeOfGroup = SymCryptEcurveBitsizeofGroupOrder( pCurve );
+
+    // Truncate the last bytes of the hash
+    if (cbHashValue*8 > uiBitsizeOfGroup)
+    {
+        cbHashValue = (uiBitsizeOfGroup + 7)/8;
+    }
+
+    // Get the value of msghash
+    scError = SymCryptIntSetValue( pbHashValue, cbHashValue, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piTmp );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Get the bit size of the hash
+    uiBitsizeOfTmp = (UINT32)cbHashValue * 8;
+
+    // If SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION is set, we don't do hash truncation.
+    // The caller can do their own truncation before calling into Symcrypt.
+    if ( ( flags & SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION ) == 0)
+    {
+        // ******** Standard truncation **************
+        // Shift right if needed
+        if ( uiBitsizeOfTmp > uiBitsizeOfGroup )
+        {
+            SymCryptIntDivPow2( piTmp, uiBitsizeOfTmp - uiBitsizeOfGroup, piTmp );
+        }
+    }
+
+    SymCryptIntToModElement( piTmp, pCurve->GOrd, peMsghash, pbScratch, cbScratch );        // msghash mod r
+
+cleanup:
+    return scError;
+}
+
+#define SYMCRYPT_MAX_ECDSA_SIGNATURE_COUNT (100)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaSignEx(
+    _In_                                PCSYMCRYPT_ECKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_opt_                            PCSYMCRYPT_INT          piK,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+    SIZE_T  cbScratchInternal = 0;
+    PBYTE   pCurr = NULL;
+
+    PCSYMCRYPT_ECURVE       pCurve = pKey->pCurve;
+
+    PSYMCRYPT_INT           piTmp = NULL;
+    PSYMCRYPT_INT           piMul = NULL;
+    PSYMCRYPT_ECPOINT       poKG = NULL;
+
+    PSYMCRYPT_MODELEMENT    peMsghash = NULL;
+    PSYMCRYPT_MODELEMENT    peSigC = NULL;
+    PSYMCRYPT_MODELEMENT    peSigD = NULL;
+    PSYMCRYPT_MODELEMENT    peTmp = NULL;
+
+    PBYTE                   pbX = NULL;
+
+    UINT32  nDigitsInt = 0;
+    UINT32  nDigitsMul = 0;
+
+    UINT32  cbInt = 0;
+    UINT32  cbMul = 0;
+    UINT32  cbKG = 0;
+    UINT32  cbRs = 0;
+    UINT32  cbX  = 0;
+
+    UINT32 signatureCount = 0;
+    UINT32 allowedFlags = SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION | SYMCRYPT_FLAG_DATA_PUBLIC;
+    UINT32 publicFlag = flags & SYMCRYPT_FLAG_DATA_PUBLIC;
+    UINT32 truncationFlag = flags & SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION;
+
+    // Make sure that the key may be used in ECDSA
+    if ( ((pKey->fAlgorithmInfo & SYMCRYPT_FLAG_ECKEY_ECDSA) == 0)  )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure only allowed flags are specified and
+    // there is a private key
+    if ( ((flags & ~(allowedFlags)) != 0) ||
+         (!pKey->hasPrivateKey) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Calculating the digits for the temporary integers
+    nDigitsInt = pCurve->GOrdDigits;
+
+    nDigitsMul = SymCryptEcurveDigitsofScalarMultiplier(pCurve);
+
+    // Objects and scratch space size calculation
+    cbInt = SymCryptSizeofIntFromDigits( nDigitsInt );
+    cbMul = SymCryptSizeofIntFromDigits( nDigitsMul );
+    cbKG = SymCryptSizeofEcpointFromCurve( pCurve );
+    cbRs = SymCryptSizeofModElementFromModulus( pCurve->GOrd );
+    cbX  = SymCryptEcurveSizeofFieldElement( pCurve );
+
+    cbScratchInternal = SYMCRYPT_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS( pCurve );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->GOrdDigits ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( pCurve->GOrdDigits ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) );
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = cbScratchInternal + cbInt + cbMul + cbKG + 4*cbRs + cbX;
+
+    // Scratch space allocation
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Creating temporaries
+    pCurr = pbScratch + cbScratchInternal;
+    piTmp = SymCryptIntCreate( pCurr, cbInt, nDigitsInt );
+    pCurr += cbInt;
+    piMul = SymCryptIntCreate( pCurr, cbMul, nDigitsMul );
+    pCurr += cbMul;
+    poKG = SymCryptEcpointCreate( pCurr, cbKG, pCurve );
+    pCurr += cbKG;
+    peMsghash = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    peSigC = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    peSigD = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    peTmp = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    pbX = pCurr;
+
+    SYMCRYPT_ASSERT( piTmp != NULL);
+    SYMCRYPT_ASSERT( piMul != NULL);
+    SYMCRYPT_ASSERT( poKG != NULL);
+    SYMCRYPT_ASSERT( peMsghash != NULL);
+    SYMCRYPT_ASSERT( peSigC != NULL);
+    SYMCRYPT_ASSERT( peSigD != NULL);
+    SYMCRYPT_ASSERT( peTmp != NULL);
+
+    // Truncate the message according to the flags
+    scError = SymCryptEcDsaTruncateHash(
+                    pCurve,
+                    pbHashValue,
+                    cbHashValue,
+                    truncationFlag,
+                    peMsghash,
+                    piTmp,
+                    pbScratch,
+                    cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    //
+    // Main loop: Stop when both c and d are not zero (unless a specific k is provided)
+    //
+    while( TRUE )
+    {
+        if ( piK == NULL )
+        {
+            SymCryptEcpointSetRandom( pCurve, piMul, poKG, pbScratch, cbScratchInternal );          // Generate k and k*G
+            SymCryptIntToModElement( piMul, pCurve->GOrd, peTmp, pbScratch, cbScratchInternal );
+        }
+        else
+        {
+            // Ensure that piK is in the range [1, GOrd-1]
+            if( SymCryptIntIsEqualUint32( piK, 0 ) ||
+                !SymCryptIntIsLessThan( piK, SymCryptIntFromModulus( pCurve->GOrd ) ) )
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+
+            SymCryptIntCopy( piK, piMul );
+            SymCryptIntToModElement( piMul, pCurve->GOrd, peTmp, pbScratch, cbScratchInternal );
+
+            scError = SymCryptEcpointScalarMul( pCurve, piMul, NULL, 0, poKG, pbScratch, cbScratchInternal );  // Generate k*G
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+
+        scError = SymCryptModInv( pCurve->GOrd, peTmp, peTmp, publicFlag, pbScratch, cbScratchInternal );              // Invert k
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Get the x coordinates from KG
+        scError = SymCryptEcpointGetValue(
+                        pCurve,
+                        poKG,
+                        SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                        SYMCRYPT_ECPOINT_FORMAT_X,
+                        pbX,
+                        cbX,
+                        publicFlag,
+                        pbScratch,
+                        cbScratchInternal );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Store c = x(KG) as an integer
+        scError = SymCryptModElementSetValue( pbX, cbX, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pCurve->GOrd, peSigC, pbScratch, cbScratch );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Move the private key into peSigD
+        SymCryptIntToModElement( pKey->piPrivateKey, pCurve->GOrd, peSigD, pbScratch, cbScratchInternal );
+
+        // Multiply the private key by h since its internal format is "DivH"
+        for (UINT32 i=0; i<pCurve->coFactorPower; i++)
+        {
+            SymCryptModAdd( pCurve->GOrd, peSigD, peSigD, peSigD, pbScratch, cbScratchInternal );
+        }
+
+        SymCryptModMul( pCurve->GOrd, peSigC, peSigD, peSigD, pbScratch, cbScratchInternal );                   // s * c
+        SymCryptModAdd( pCurve->GOrd, peMsghash, peSigD, peSigD, pbScratch, cbScratchInternal );                // msghash + s*c
+        SymCryptModMul( pCurve->GOrd, peSigD, peTmp, peSigD, pbScratch, cbScratchInternal );                    // ( msghash + s*c ) / k
+
+        if ( !( SymCryptModElementIsZero( pCurve->GOrd, peSigC ) |
+                SymCryptModElementIsZero( pCurve->GOrd, peSigD ) ) )
+        {
+            break;
+        }
+
+        if (piK != NULL)
+        {
+            // piK resulted in 0 signature
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        signatureCount++;
+        if ( signatureCount >= SYMCRYPT_MAX_ECDSA_SIGNATURE_COUNT )
+        {
+            // We have not generated a non-zero signature after SYMCRYPT_MAX_ECDSA_SIGNATURE_COUNT attempts;
+            // Something is wrong with the group setup
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+    // Output c
+    scError = SymCryptModElementGetValue( pCurve->GOrd, peSigC, pbSignature, cbSignature / 2, format, pbScratch, cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Output d
+    scError = SymCryptModElementGetValue( pCurve->GOrd, peSigD, pbSignature + cbSignature / 2, cbSignature / 2, format, pbScratch, cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        SymCryptWipe( pbSignature, cbSignature );
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaSign(
+    _In_                                PCSYMCRYPT_ECKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE                   pbSignature,
+                                        SIZE_T                  cbSignature )
+{
+    // Make sure that only the correct flags are set
+    if ( (flags & ~SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION) != 0 )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // We must have a private key to perform PCT or signature
+    if( !pKey->hasPrivateKey || !(pKey->fAlgorithmInfo & SYMCRYPT_FLAG_ECKEY_ECDSA) )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // If the key was generated in SymCrypt and has not yet had a PCT performed - perform PCT before first use
+    SYMCRYPT_RUN_KEY_GEN_PCT(
+        SymCryptEcDsaPct,
+        pKey,
+        SYMCRYPT_PCT_ECDSA );
+
+    return SymCryptEcDsaSignEx( pKey, pbHashValue, cbHashValue, NULL, format, flags, pbSignature, cbSignature );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaVerify(
+    _In_                                PCSYMCRYPT_ECKEY        pKey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                  pbHashValue,
+                                        SIZE_T                  cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                  pbSignature,
+                                        SIZE_T                  cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT  format,
+                                        UINT32                  flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+    SIZE_T  cbScratchInternal = 0;
+    PBYTE   pCurr = NULL;
+    BOOLEAN fValidSignature = FALSE;
+
+    PCSYMCRYPT_ECURVE       pCurve = pKey->pCurve;
+
+    PSYMCRYPT_INT           piTmp = NULL;
+    PSYMCRYPT_INT           piMul1 = NULL;
+    PSYMCRYPT_INT           piMul2 = NULL;
+    PSYMCRYPT_ECPOINT       poQ1 = NULL;
+    PSYMCRYPT_ECPOINT       poQ2 = NULL;
+
+    PSYMCRYPT_MODELEMENT    peMsghash = NULL;
+    PSYMCRYPT_MODELEMENT    peSigC = NULL;
+    PSYMCRYPT_MODELEMENT    peSigD = NULL;
+    PSYMCRYPT_MODELEMENT    peTmp = NULL;
+
+    PBYTE                   pbX = NULL;
+    PCSYMCRYPT_ECPOINT      poTable[2] = { 0 };
+    PCSYMCRYPT_INT          piTable[2] = { 0 };
+
+    UINT32  nDigitsInt = 0;
+    UINT32  nDigitsMul = 0;
+
+    UINT32  cbInt = 0;
+    UINT32  cbMul = 0;
+    UINT32  cbKG = 0;
+    UINT32  cbRs = 0;
+    UINT32  cbX  = 0;
+
+    // Make sure that the key may be used in ECDSA
+    if ( ((pKey->fAlgorithmInfo & SYMCRYPT_FLAG_ECKEY_ECDSA) == 0)  )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that only the correct flags are set
+    if ( (flags & ~SYMCRYPT_FLAG_ECDSA_NO_TRUNCATION) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Calculating the digits for the temporary integer
+    nDigitsInt = SYMCRYPT_MAX( pCurve->FModDigits, pCurve->GOrdDigits );
+    nDigitsInt = SYMCRYPT_MAX( nDigitsInt, SymCryptDigitsFromBits( (UINT32)cbSignature * 4 ) );  // pbSignature contains (c,d)
+
+    nDigitsMul = SymCryptEcurveDigitsofScalarMultiplier(pCurve);
+
+    // Objects and scratch space size calculation
+    cbInt = SymCryptSizeofIntFromDigits( nDigitsInt );
+    cbMul = SymCryptSizeofIntFromDigits( nDigitsMul );
+    cbKG = SymCryptSizeofEcpointFromCurve( pCurve );
+    cbRs = SymCryptSizeofModElementFromModulus( pCurve->GOrd );
+    cbX  = SymCryptEcurveSizeofFieldElement( pCurve );
+
+    cbScratchInternal = SYMCRYPT_SCRATCH_BYTES_FOR_MULTI_SCALAR_ECURVE_OPERATIONS( pCurve, 2 );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->GOrdDigits ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( pCurve->GOrdDigits ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) );
+    cbScratchInternal = SYMCRYPT_MAX( cbScratchInternal, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = cbScratchInternal + cbInt + 2*cbMul + 2*cbKG + 4*cbRs + cbX;
+
+    // Scratch space allocation
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Creating temporaries
+    pCurr = pbScratch + cbScratchInternal;
+    piTmp = SymCryptIntCreate( pCurr, cbInt, nDigitsInt );
+    pCurr += cbInt;
+    piMul1 = SymCryptIntCreate( pCurr, cbMul, nDigitsMul );
+    pCurr += cbMul;
+    piMul2 = SymCryptIntCreate( pCurr, cbMul, nDigitsMul );
+    pCurr += cbMul;
+    poQ1 = SymCryptEcpointCreate( pCurr, cbKG, pCurve );
+    pCurr += cbKG;
+    poQ2 = SymCryptEcpointCreate( pCurr, cbKG, pCurve );
+    pCurr += cbKG;
+    peMsghash = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    peSigC = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    peSigD = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    peTmp = SymCryptModElementCreate( pCurr, cbRs, pCurve->GOrd );
+    pCurr += cbRs;
+    pbX = pCurr;
+
+    SYMCRYPT_ASSERT( piTmp != NULL);
+    SYMCRYPT_ASSERT( piMul1 != NULL);
+    SYMCRYPT_ASSERT( piMul2 != NULL);
+    SYMCRYPT_ASSERT( poQ1 != NULL);
+    SYMCRYPT_ASSERT( poQ2 != NULL);
+    SYMCRYPT_ASSERT( peMsghash != NULL);
+    SYMCRYPT_ASSERT( peSigC != NULL);
+    SYMCRYPT_ASSERT( peSigD != NULL);
+    SYMCRYPT_ASSERT( peTmp != NULL);
+
+    // Get c
+    scError = SymCryptIntSetValue( pbSignature, cbSignature / 2, format, piTmp );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Check if c is less than r
+    if ( !SymCryptIntIsLessThan( piTmp, SymCryptIntFromModulus( pCurve->GOrd ) ) )
+    {
+        goto cleanup;
+    }
+
+    // c mod r
+    SymCryptIntToModElement( piTmp, pCurve->GOrd, peSigC, pbScratch, cbScratchInternal );
+
+    // Check if c is zero
+    if (SymCryptModElementIsZero( pCurve->GOrd, peSigC ))
+    {
+        goto cleanup;
+    }
+
+    // Get d
+    scError = SymCryptIntSetValue( pbSignature + cbSignature / 2, cbSignature / 2, format, piTmp );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Check if d is less than r
+    if ( !SymCryptIntIsLessThan( piTmp, SymCryptIntFromModulus( pCurve->GOrd ) ) )
+    {
+        goto cleanup;
+    }
+
+    // d mod r
+    SymCryptIntToModElement( piTmp, pCurve->GOrd, peSigD, pbScratch, cbScratchInternal );
+
+    // Check if d is zero
+    if (SymCryptModElementIsZero( pCurve->GOrd, peSigD ))
+    {
+        goto cleanup;
+    }
+
+    // Calculate 1/d mod r
+    // The D value is not secret; it is part of the signature.
+    // We mark it public to avoid the use of random blinding, which would require a source of randomness
+    // just to verify an ECDSA signature.
+    scError = SymCryptModInv( pCurve->GOrd, peSigD, peSigD, SYMCRYPT_FLAG_DATA_PUBLIC, pbScratch, cbScratchInternal );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Truncate the message according to the flags
+    scError = SymCryptEcDsaTruncateHash(
+                    pCurve,
+                    pbHashValue,
+                    cbHashValue,
+                    flags,
+                    peMsghash,
+                    piTmp,
+                    pbScratch,
+                    cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptModMul( pCurve->GOrd, peMsghash, peSigD, peMsghash, pbScratch, cbScratchInternal );     // msghash / d = h1
+    SymCryptModMul( pCurve->GOrd, peSigC, peSigD, peTmp, pbScratch, cbScratchInternal );            // c / d = h2
+
+    SymCryptModElementToInt( pCurve->GOrd, peMsghash, piMul1, pbScratch, cbScratchInternal );
+    SymCryptModElementToInt( pCurve->GOrd, peTmp, piMul2, pbScratch, cbScratchInternal );
+
+    // h1*G + h2*W
+    piTable[0] = piMul1;
+    piTable[1] = piMul2;
+
+    poTable[0] = NULL;  // The first base point is the generator G of the group
+    poTable[1] = pKey->poPublicKey;
+
+    scError = SymCryptEcpointMultiScalarMul( pCurve, piTable, poTable, 2, SYMCRYPT_FLAG_DATA_PUBLIC, poQ1, pbScratch, cbScratchInternal );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Check for point at infinity
+    if ( SymCryptEcpointIsZero( pCurve, poQ1, pbScratch, cbScratchInternal ) )
+    {
+        goto cleanup;
+    }
+
+    // Get the x from poQ1
+    scError = SymCryptEcpointGetValue( pCurve, poQ1, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, SYMCRYPT_ECPOINT_FORMAT_X, pbX, cbX, SYMCRYPT_FLAG_DATA_PUBLIC, pbScratch, cbScratchInternal);
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Store it in a big enough INT
+    scError = SymCryptIntSetValue( pbX, cbX, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piTmp );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptIntToModElement( piTmp, pCurve->GOrd, peTmp, pbScratch, cbScratchInternal );    // x mod r
+
+    // Comparison c = x
+    if (SymCryptModElementIsEqual( pCurve->GOrd, peSigC, peTmp ))
+    {
+        fValidSignature = TRUE;
+    }
+
+cleanup:
+
+    if (!fValidSignature)
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+    }
+
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ec_internal_curve_params.c b/libs/symcrypt/lib/ec_internal_curve_params.c
new file mode 100644
index 000000000000..4c0301f1c686
--- /dev/null
+++ b/libs/symcrypt/lib/ec_internal_curve_params.c
@@ -0,0 +1,597 @@
+//
+// ec_internal_curve_params.c   Parameters for internally supported curves.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Do not delete the following preprocessor directive.
+// It is used for folding the parameters.
+#if 1
+
+/***********************************
+ *                                 *
+ *           NIST CURVES           *
+ *                                 *
+ ***********************************/
+
+static const BYTE rgbNistP192[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x18, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x18, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+    //B
+    0x64, 0x21, 0x05, 0x19, 0xE5, 0x9C, 0x80, 0xE7,
+    0x0F, 0xA7, 0xE9, 0xAB, 0x72, 0x24, 0x30, 0x49,
+    0xFE, 0xB8, 0xDE, 0xEC, 0xC1, 0x46, 0xB9, 0xB1,
+    //x
+    0x18, 0x8D, 0xA8, 0x0E, 0xB0, 0x30, 0x90, 0xF6,
+    0x7C, 0xBF, 0x20, 0xEB, 0x43, 0xA1, 0x88, 0x00,
+    0xf4, 0xFF, 0x0A, 0xFD, 0x82, 0xFF, 0x10, 0x12,
+    //y
+    0x07, 0x19, 0x2B, 0x95, 0xFF, 0xC8, 0xDA, 0x78,
+    0x63, 0x10, 0x11, 0xED, 0x6B, 0x24, 0xCD, 0xD5,
+    0x73, 0xF9, 0x77, 0xA1, 0x1E, 0x79, 0x48, 0x11,
+    //q
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xDE, 0xF8, 0x36,
+    0x14, 0x6B, 0xC9, 0xB1, 0xB4, 0xD2, 0x28, 0x31,
+    //h
+    0x01
+};
+
+static const BYTE rgbNistP224[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x1C, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x1C, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFE,
+    //B
+    0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB,
+    0xF5, 0x41, 0x32, 0x56, 0x50, 0x44, 0xB0, 0xB7,
+    0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43,
+    0x23, 0x55, 0xFF, 0xB4,
+    //x
+    0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F,
+    0x32, 0x13, 0x90, 0xB9, 0x4A, 0x03, 0xC1, 0xD3,
+    0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xD6,
+    0x11, 0x5C, 0x1D, 0x21,
+    //y
+    0xBD, 0x37, 0x63, 0x88, 0xB5, 0xF7, 0x23, 0xFB,
+    0x4C, 0x22, 0xDF, 0xE6, 0xCD, 0x43, 0x75, 0xA0,
+    0x5A, 0x07, 0x47, 0x64, 0x44, 0xD5, 0x81, 0x99,
+    0x85, 0x00, 0x7E, 0x34,
+    //q
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x16, 0xA2,
+    0xE0, 0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 0x45,
+    0x5C, 0x5C, 0x2A, 0x3D,
+    //h
+    0x01
+};
+
+static const BYTE rgbNistP256[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x20, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x20, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+    //B
+    0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7,
+    0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC,
+    0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6,
+    0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B,
+    //x
+    0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47,
+    0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2,
+    0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0,
+    0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
+    //y
+    0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B,
+    0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16,
+    0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE,
+    0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5,
+    //q
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+    0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51,
+    //h
+    0x01
+};
+
+static const BYTE rgbNistP384[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x30, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x30, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC,
+    //B
+    0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4,
+    0x98, 0x8E, 0x05, 0x6B, 0xE3, 0xF8, 0x2D, 0x19,
+    0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12,
+    0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A,
+    0xC6, 0x56, 0x39, 0x8D, 0x8A, 0x2E, 0xD1, 0x9D,
+    0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF,
+    //x
+    0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37,
+    0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74,
+    0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98,
+    0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38,
+    0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C,
+    0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7,
+    //y
+    0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F,
+    0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29,
+    0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C,
+    0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0,
+    0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D,
+    0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F,
+    //q
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF,
+    0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A,
+    0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73,
+    //h
+    0x01
+};
+
+static const BYTE rgbNistP521[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x42, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x42, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF,
+    //A
+    0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFC,
+    //B
+    0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C,
+    0x9A, 0x1F, 0x92, 0x9A, 0x21, 0xA0, 0xB6, 0x85,
+    0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3,
+    0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1,
+    0x09, 0xE1, 0x56, 0x19, 0x39, 0x51, 0xEC, 0x7E,
+    0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1,
+    0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C,
+    0x34, 0xF1, 0xEF, 0x45, 0x1F, 0xD4, 0x6B, 0x50,
+    0x3F, 0x00,
+    //x
+    0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04,
+    0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, 0x95,
+    0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F,
+    0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D,
+    0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, 0xE7,
+    0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF,
+    0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A,
+    0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2, 0xE5,
+    0xBD, 0x66,
+    //y
+    0x01, 0x18, 0x39, 0x29, 0x6A, 0x78, 0x9A, 0x3B,
+    0xC0, 0x04, 0x5C, 0x8A, 0x5F, 0xB4, 0x2C, 0x7D,
+    0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B,
+    0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E,
+    0x66, 0x2C, 0x97, 0xEE, 0x72, 0x99, 0x5E, 0xF4,
+    0x26, 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD,
+    0x07, 0x61, 0x35, 0x3C, 0x70, 0x86, 0xA2, 0x72,
+    0xC2, 0x40, 0x88, 0xBE, 0x94, 0x76, 0x9F, 0xD1,
+    0x66, 0x50,
+    //q
+    0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F,
+    0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09,
+    0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C,
+    0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38,
+    0x64, 0x09,
+    //h
+    0x01
+};
+
+/*****************************************
+*                                        *
+*         TWISTED EDWARDS CURVES         *
+*                                        *
+******************************************/
+static const BYTE rgbNumsP256t1[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x02, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x20, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x20, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x43,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x42,
+    //d
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xEE,
+    //x
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
+    //y
+    0x7D, 0x0A, 0xB4, 0x1E, 0x2A, 0x12, 0x76, 0xDB,
+    0xA3, 0xD3, 0x30, 0xB3, 0x9F, 0xA0, 0x46, 0xBF,
+    0xBE, 0x2A, 0x6D, 0x63, 0x82, 0x4D, 0x30, 0x3F,
+    0x70, 0x7F, 0x6F, 0xB5, 0x33, 0x1C, 0xAD, 0xBA,
+    //q
+    0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xBE, 0x6A, 0xA5, 0x5A, 0xD0, 0xA6, 0xBC, 0x64,
+    0xE5, 0xB8, 0x4E, 0x6F, 0x11, 0x22, 0xB4, 0xAD,
+    //h
+    0x04
+};
+
+static const BYTE rgbNumsP384t1[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x02, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x30, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x30, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xC3,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xC2,
+    //d
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x8A,
+    //x
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+    //y
+    0x74, 0x9C, 0xDA, 0xBA, 0x13, 0x6C, 0xE9, 0xB6,
+    0x5B, 0xD4, 0x47, 0x17, 0x94, 0xAA, 0x61, 0x9D,
+    0xAA, 0x5C, 0x7B, 0x4C, 0x93, 0x0B, 0xFF, 0x8E,
+    0xBD, 0x79, 0x8A, 0x8A, 0xE7, 0x53, 0xC6, 0xD7,
+    0x2F, 0x00, 0x38, 0x60, 0xFE, 0xBA, 0xBA, 0xD5,
+    0x34, 0xA4, 0xAC, 0xF5, 0xFA, 0x7F, 0x5B, 0xEE,
+    //q
+    0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xEC, 0xD7, 0xD1, 0x1E, 0xD5, 0xA2, 0x59, 0xA2,
+    0x5A, 0x13, 0xA0, 0x45, 0x8E, 0x39, 0xF4, 0xE4,
+    0x51, 0xD6, 0xD7, 0x1F, 0x70, 0x42, 0x6E, 0x25,
+    //h
+    0x04
+};
+
+static const BYTE rgbNumsP512t1[] = {
+    //dwVersion
+    0x01, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x02, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x40, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x40, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xC7,
+    //A
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xC6,
+    //d
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xBA, 0xA8,
+    //x
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+    //y
+    0x7D, 0x67, 0xE8, 0x41, 0xDC, 0x4C, 0x46, 0x7B,
+    0x60, 0x50, 0x91, 0xD8, 0x08, 0x69, 0x21, 0x2F,
+    0x9C, 0xEB, 0x12, 0x4B, 0xF7, 0x26, 0x97, 0x3F,
+    0x9F, 0xF0, 0x48, 0x77, 0x9E, 0x1D, 0x61, 0x4E,
+    0x62, 0xAE, 0x2E, 0xCE, 0x50, 0x57, 0xB5, 0xDA,
+    0xD9, 0x6B, 0x7A, 0x89, 0x7C, 0x1D, 0x72, 0x79,
+    0x92, 0x61, 0x13, 0x46, 0x38, 0x75, 0x0F, 0x4F,
+    0x0C, 0xB9, 0x10, 0x27, 0x54, 0x3B, 0x1C, 0x5E,
+    //q
+    0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xA7, 0xE5, 0x08, 0x09, 0xEF, 0xDA, 0xBB, 0xB9,
+    0xA6, 0x24, 0x78, 0x4F, 0x44, 0x95, 0x45, 0xF0,
+    0xDC, 0xEA, 0x5F, 0xF0, 0xCB, 0x80, 0x0F, 0x89,
+    0x4E, 0x78, 0xD1, 0xCB, 0x0B, 0x5F, 0x01, 0x89,
+    //h
+    0x04
+};
+
+
+/************************************
+*                                   *
+*         MONTGOMERY CURVES         *
+*                                   *
+*************************************/
+
+static const BYTE rgbCurve25519[] = {
+    //dwVersion
+    0x02, 0x00, 0x00, 0x00,
+    //dwCurveType
+    0x03, 0x00, 0x00, 0x00,
+    //dwCurveGenerationAlgId
+    0x00, 0x00, 0x00, 0x00,
+    //cbFieldLength
+    0x20, 0x00, 0x00, 0x00,
+    //cbSubgroupOrder
+    0x20, 0x00, 0x00, 0x00,
+    //cbCofactor
+    0x01, 0x00, 0x00, 0x00,
+    //cbSeed
+    0x00, 0x00, 0x00, 0x00,
+    //p
+    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED,
+    //A
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x6D, 0x06,
+    //B
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    //x
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+    //y
+    0x20, 0xAE, 0x19, 0xA1, 0xB8, 0xA0, 0x86, 0xB4,
+    0xE0, 0x1E, 0xDD, 0x2C, 0x77, 0x48, 0xD1, 0x4C,
+    0x92, 0x3D, 0x4D, 0x7E, 0x6D, 0x7C, 0x61, 0xB2,
+    0x29, 0xE9, 0xC5, 0xA2, 0x7E, 0xCE, 0xD3, 0xD9,
+    //q
+    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7, 0x9C, 0xD6,
+    0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED,
+    //h
+    0x08,
+
+    // Version 2 parameters
+    // PrivateKeyDefaultFormat
+    0x03, 0x00, 0x00, 0x00,
+    // HighBitRestrictionNumOfBits
+    0x02, 0x00, 0x00, 0x00,
+    // HighBitRestrictionPosition
+    0xFE, 0x00, 0x00, 0x00,
+    // HighBitRestrictionValue
+    0x01, 0x00, 0x00, 0x00,
+};
+
+#endif  // 1
+
+// Version 2 parameter extension
+static const SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION paramsV2ExtensionShortWeierstrass =
+{
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL,
+    0,
+    0,
+    0,
+};
+
+static const SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION paramsV2ExtensionTwistedEdwards =
+{
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH,
+    0,
+    0,
+    0,
+};
+
+static const SYMCRYPT_ECURVE_PARAMS_V2_EXTENSION paramsV2ExtensionMontgomery =
+{
+    SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH_TIMESH,
+    0,
+    0,
+    0,
+};
+
+// Definitions
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNistP192 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNistP192;
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNistP224 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNistP224;
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNistP256 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNistP256;
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNistP384 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNistP384;
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNistP521 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNistP521;
+
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNumsP256t1 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNumsP256t1;
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNumsP384t1 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNumsP384t1;
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsNumsP512t1 = (PCSYMCRYPT_ECURVE_PARAMS) rgbNumsP512t1;
+
+const PCSYMCRYPT_ECURVE_PARAMS SymCryptEcurveParamsCurve25519 = (PCSYMCRYPT_ECURVE_PARAMS) rgbCurve25519;
+
+const PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION SymCryptEcurveParamsV2ExtensionShortWeierstrass = &paramsV2ExtensionShortWeierstrass;
+const PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION SymCryptEcurveParamsV2ExtensionTwistedEdwards = &paramsV2ExtensionTwistedEdwards;
+const PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION SymCryptEcurveParamsV2ExtensionMontgomery = &paramsV2ExtensionMontgomery;
diff --git a/libs/symcrypt/lib/ec_internal_curves.c b/libs/symcrypt/lib/ec_internal_curves.c
new file mode 100644
index 000000000000..d0107f431c7a
--- /dev/null
+++ b/libs/symcrypt/lib/ec_internal_curves.c
@@ -0,0 +1,79 @@
+//
+// ec_internal_curves.c   Internally allocated elliptic curves.
+//
+// These curves are lazy-initialized. Currently only used
+// for composite algorithms to avoid per-key allocation overhead.
+//
+
+#include "precomp.h"
+
+static PCSYMCRYPT_ECURVE rgpCachedCurves[SYMCRYPT_CACHED_ECURVE_ID_COUNT] = { 0 };
+
+static
+PCSYMCRYPT_ECURVE_PARAMS
+SYMCRYPT_CALL
+SymCryptGetCachedEcurveParams(
+    SYMCRYPT_CACHED_ECURVE_ID   curveId )
+{
+    switch (curveId)
+    {
+        case SYMCRYPT_CACHED_ECURVE_ID_NIST_P256:
+            return SymCryptEcurveParamsNistP256;
+        case SYMCRYPT_CACHED_ECURVE_ID_NIST_P384:
+            return SymCryptEcurveParamsNistP384;
+        case SYMCRYPT_CACHED_ECURVE_ID_CURVE_25519:
+            return SymCryptEcurveParamsCurve25519;
+        default:
+            return NULL;
+    }
+}
+
+PCSYMCRYPT_ECURVE
+SYMCRYPT_CALL
+SymCryptGetCachedEcurve(
+    SYMCRYPT_CACHED_ECURVE_ID   curveId )
+{
+    PCSYMCRYPT_ECURVE pCachedCurve = NULL;
+    PSYMCRYPT_ECURVE pNewCurve = NULL;
+    PSYMCRYPT_ECURVE pCurrCurve = NULL;
+    PCSYMCRYPT_ECURVE_PARAMS pParams = NULL;
+
+    if ( curveId < 0 || curveId >= SYMCRYPT_CACHED_ECURVE_ID_COUNT )
+    {
+        return NULL;
+    }
+
+    pCachedCurve = (PCSYMCRYPT_ECURVE) SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE( &rgpCachedCurves[curveId] );
+    if ( pCachedCurve != NULL )
+    {
+        return pCachedCurve;
+    }
+
+    pParams = SymCryptGetCachedEcurveParams( curveId );
+    if ( pParams == NULL )
+    {
+        return NULL;
+    }
+
+    pNewCurve = SymCryptEcurveAllocate( pParams, 0 );
+    if ( pNewCurve == NULL )
+    {
+        return NULL;
+    }
+
+    pCurrCurve = SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE(
+                    &rgpCachedCurves[curveId],
+                    pNewCurve,
+                    NULL);
+
+    // Means the original curve was already filled
+    // and that our new curve was not used. So we
+    // free the new curve and return the existing one.
+    if ( pCurrCurve != NULL )
+    {
+        SymCryptEcurveFree( pNewCurve );
+        return pCurrCurve;
+    }
+
+    return pNewCurve;
+}
diff --git a/libs/symcrypt/lib/ec_montgomery.c b/libs/symcrypt/lib/ec_montgomery.c
new file mode 100644
index 000000000000..ced3fcdaafc2
--- /dev/null
+++ b/libs/symcrypt/lib/ec_montgomery.c
@@ -0,0 +1,443 @@
+//
+// ec_montgomery.c   Montgomery Implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptMontgomeryFillScratchSpaces(_In_ PSYMCRYPT_ECURVE pCurve)
+{
+    UINT32 nDigits = SymCryptDigitsFromBits( pCurve->FModBitsize );
+    UINT32 nBytes = SymCryptSizeofModElementFromModulus( pCurve->FMod );
+    UINT32 nCommon = SYMCRYPT_MAX( SymCryptSizeofIntFromDigits( nDigits ), SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ), SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( nDigits ) ) );
+    UINT32 cbModElement = pCurve->cbModElement;
+    UINT32 nDigitsFieldLength = pCurve->FModDigits;
+
+    //
+    // All the scratch space computations are upper bounded by the SizeofXXX bound (2^19) and
+    // the SCRATCH_BYTES_FOR_XXX bound (2^24) (see symcrypt_internal.h).
+    //
+    // One caveat is SymCryptSizeofEcpointFromCurve and SymCryptSizeofEcpointEx which calculate the
+    // size of EcPoint with 4 coordinates (each one a modelement of max size 2^17). Thus upper
+    // bounded by 2^20.
+    //
+
+    pCurve->cbScratchCommon = nCommon;
+    pCurve->cbScratchScalar =
+        SymCryptSizeofIntFromDigits(nDigits) +
+        6 * nBytes +
+        nCommon;
+
+    pCurve->cbScratchScalarMulti = 0;
+    pCurve->cbScratchGetSetValue =
+        SymCryptSizeofEcpointEx( cbModElement, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH ) +
+        2 * cbModElement +
+        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsFieldLength ),
+             SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( nDigitsFieldLength ) );
+
+    pCurve->cbScratchGetSetValue = SYMCRYPT_MAX( pCurve->cbScratchGetSetValue, SymCryptSizeofIntFromDigits( nDigits ) );
+
+    pCurve->cbScratchEckey =
+        SYMCRYPT_MAX( cbModElement + SymCryptSizeofIntFromDigits(SymCryptEcurveDigitsofScalarMultiplier(pCurve)),
+            SymCryptSizeofEcpointFromCurve( pCurve ) ) +
+        SYMCRYPT_MAX( pCurve->cbScratchScalar, pCurve->cbScratchGetSetValue );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMontgomerySetDistinguished(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_MONTGOMERY_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptEcpointCopy( pCurve, pCurve->G, poDst );
+}
+
+//
+//  Verify poSrc1(X1, Z1) = poSrc2(X2, Z2)
+//  To avoid ModInv for 1/Z, we do
+//     X1 * Z2 = X2 * Z1
+//
+//  This function currently ignores the flags parameter as there is no distinction between equal and
+//  negative equal case in Single Projective Coordinates used in Montgomery curves. We accept the flags
+//  to maintain the same API as for other curves.
+//
+UINT32
+SYMCRYPT_CALL
+SymCryptMontgomeryIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch)
+{
+    PSYMCRYPT_MODELEMENT  peTemp[2];
+    PSYMCRYPT_MODELEMENT  peSrc1X, peSrc1Z;
+    PSYMCRYPT_MODELEMENT  peSrc2X, peSrc2Z;
+    PSYMCRYPT_MODULUS     pmMod = pCurve->FMod;
+    SIZE_T nBytes;
+
+    SYMCRYPT_ASSERT( (flags & ~(SYMCRYPT_FLAG_ECPOINT_EQUAL|SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL)) == 0 );
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_MONTGOMERY_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nBytes );
+
+    for (UINT32 i = 0; i < 2; ++i)
+    {
+        peTemp[i] = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+        pbScratch += nBytes;
+        cbScratch -= nBytes;
+    }
+
+    peSrc1X = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc1 );
+    peSrc1Z = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc1 );
+
+    peSrc2X = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc2 );
+    peSrc2Z = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc2 );
+
+    // peTemp[0] = X1 * Z2
+    SymCryptModMul( pmMod, peSrc1X, peSrc2Z, peTemp[0], pbScratch, cbScratch );
+
+    // peTemp[1] = X2 * Z1
+    SymCryptModMul( pmMod, peSrc2X, peSrc1Z, peTemp[1], pbScratch, cbScratch );
+
+    return SymCryptModElementIsEqual( pmMod, peTemp[0], peTemp[1] );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMontgomeryIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peZ = NULL;    // Pointer to Z
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_MONTGOMERY_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    // Getting pointer to Z of the source point
+    peZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1,  pCurve, poSrc );
+
+    return SymCryptModElementIsZero( FMod, peZ );
+}
+
+VOID
+SymCryptMontgomeryDoubleAndAdd(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peX1,
+    _In_opt_                        PCSYMCRYPT_MODELEMENT   peZ1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peA24,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peX2,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peZ2,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peX3,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peZ3,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peTemp1,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peTemp2,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch)
+/*
+We use the notation of ladd-1987-m-3, this is a generic Montgomery ladder implementation.
+This is similar to RFC7748 for TLS use of curve25519, however, unlike in the RFC, we support the case when Z1 != 1.
+
+When it is statically known that Z1 == 1 the caller can set peZ1 to NULL to skip one redundant modular multiplication.
+   Note that this will be revealed through timing, so peZ1 can only be set to NULL it is not secret that Z1 == 1.
+   Z1 == 1 is statically known for points which have just been imported into SymCrypt (and for the distinguished point of the
+   curve), and this knowledge is tracked in an ecPoint's normalized flag.
+
+The (X,Z) values represent an x-coordinate (X/Z) but it avoids the modular division.
+
+The value a24 is such that 4*a24 = a+2 where a is one of the Montgomery curve parameters.
+Thus, a24 = (a+2)/4. For curve25519, A = 486662, so a24 = 121666 (=0x01db42)
+
+Algorithm (ladd-1987-m-3), with all operations expanded
+   A  = X2 + Z2
+   AA = A^2
+   B  = X2 - Z2
+   BB = B^2
+   E  = AA - BB
+   C  = X3 + Z3
+   D  = X3 - Z3
+   DA = D * A
+   CB = C * B
+   X5 = (DA + CB)^2
+        DApCB = DA + CB
+        X5 = DApCB^2
+   if peZ1 != NULL:
+        X5 = Z1 * X5
+   Z5 = X1 * (DA - CB)^2
+        DAmCB = DA - CB
+        DAmCB2 = DAmCB ^ 2
+        Z5 = X1 * DAmCB2
+   X4 = AA * BB
+   Z4 = E * (BB + a24 * E)
+        A24E = A24 * E
+        BAE = BB + A24 * E
+        Z4 = E * BAE
+
+If we write a = (X2,Z2) and b = (X3,Z3), and a-b = (X1,Z1), then this algorithm computes
+(2*a) and (a+b) into (X4, Z4) and (X5,Z5) respectively.
+The Montgomery ladder uses this as follows:
+- Store xP and (x+1)P
+- To process a 0 bit in the scalar, apply the DoubleAndAdd to (xP,(x+1)P) to get (2xP, (2x+1)P)
+- To process a 1 bit in the scalar, apply the DoubleAndAdd to ((x+1)P, xP) to get ((2x+2)P, (2x+1)P)
+This updates the state to either (2xP, (2x+1)P) or to ((2x+1)P, (2x+2)P) and corresponds to updating
+x to either 2x or 2x+1.
+
+The starting value is (0,P), represented as ((1,0),(P_x,P_z)
+The algorithm above, when applied to (1, 0, X, Z) produces:
+    A = 1, AA = 1, B = 1, BB = 1, E = 0,
+    C = X+Z, D = X-Z, DA = X-Z, CB = X+Z,
+    X5 = 4(X^2)Z, Z5 = 4X(Z^2)
+    X4 = 1, Z4 = 0
+for an output of (1, 0, 4(X^2)Z, 4X(Z^2))
+But (4(X^2)Z, 4X(Z^2)) is just another representation of (X,Z) as only the quotient of the two numbers is significant.
+So even if an exponent starts with a bunch of 0 bits, the DoubleAndAdd-based function computes the right result in constant time.
+
+*/
+{
+    // Temp1 =          A = X2 + Z2
+    SymCryptModAdd( pmMod, peX2, peZ2, peTemp1, pbScratch, cbScratch );
+
+    // Z2 =             B = X2 - Z2
+    SymCryptModSub( pmMod, peX2, peZ2, peZ2, pbScratch, cbScratch );
+
+    // Temp2 =          C = X3 + Z3
+    SymCryptModAdd( pmMod, peX3, peZ3, peTemp2, pbScratch, cbScratch );
+
+    // Z3 =             D = X3 - Z3
+    SymCryptModSub( pmMod, peX3, peZ3, peZ3, pbScratch, cbScratch );
+
+    // X3 =             CB = C * B      = Temp2 * Z2
+    SymCryptModMul( pmMod, peTemp2, peZ2, peX3, pbScratch, cbScratch );
+
+    // Z3 =             DA = D * A      = Z3 * Temp1
+    SymCryptModMul( pmMod, peZ3, peTemp1, peZ3, pbScratch, cbScratch );
+
+    // From this point on, the outputs (X5,Z5) depend only on (X3,Z3) and (X1,Z1)
+    // and the outputs (X4,Z4) only on (Temp1,Z2) and A24
+    // We'll do the (X4,Z4) first
+
+    // X2 =             AA = A * A      = Temp1 * Temp1
+    SymCryptModSquare( pmMod, peTemp1, peX2, pbScratch, cbScratch );
+
+    // Temp1 =          BB = B * B      = Z2 * Z2
+    SymCryptModSquare( pmMod, peZ2, peTemp1, pbScratch, cbScratch );
+
+    // Temp2 =          E = AA - BB     = X2 - Temp1
+    SymCryptModSub( pmMod, peX2, peTemp1, peTemp2, pbScratch, cbScratch );
+
+    // X2 =             X4 = AA * BB    = X2 * Temp1
+    SymCryptModMul( pmMod, peX2, peTemp1, peX2, pbScratch, cbScratch );
+
+    // Z2 =             A24E = A24 * E    = A24 * Temp2
+    SymCryptModMul( pmMod, peA24, peTemp2, peZ2, pbScratch, cbScratch );
+
+    // Z2 =             BAE = (BB + a24 * E) = BB + A24E = Temp1 + Z2
+    SymCryptModAdd( pmMod, peTemp1, peZ2, peZ2, pbScratch, cbScratch );
+
+    // Z2 =             Z4 = E * BAE        = Temp2 + Z2
+    SymCryptModMul( pmMod, peTemp2, peZ2, peZ2, pbScratch, cbScratch );
+
+    // Now we compute (X5, Z5)
+
+    // Temp1 =          DApCB = DA + CB     = Z3 + X3
+    SymCryptModAdd( pmMod, peZ3, peX3, peTemp1, pbScratch, cbScratch );
+
+    // Z3 =             DAmCB = DA - CB     = Z3 - X3
+    SymCryptModSub( pmMod, peZ3, peX3, peZ3, pbScratch, cbScratch );
+
+    // X3 =             DApCB^2 = Temp1 ^ 2 ( = X5 when (peZ1 == NULL) => Z1 == 1)
+    SymCryptModSquare( pmMod, peTemp1, peX3, pbScratch, cbScratch );
+
+    if (peZ1 != NULL) // source point is not normalized
+    {
+    // X3 =             X5 = Z1 * DApCB^2   = Z1 * X3
+    SymCryptModMul( pmMod, peZ1, peX3, peX3, pbScratch, cbScratch );
+    }
+
+    // Z3 =             DAmCB2 = DAmCB ^ 2  = Z3 ^ 2
+    SymCryptModSquare( pmMod, peZ3, peZ3, pbScratch, cbScratch );
+
+    // Z3 =             Z5 = X1 * DAmCB2        = X1 * Z3
+    SymCryptModMul( pmMod, peX1, peZ3, peZ3, pbScratch, cbScratch );
+}
+
+//
+// Montgomery point multiplication only works on X-coordinates.
+// We ignore the Y-coordinates.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMontgomeryPointScalarMul(
+    _In_    PCSYMCRYPT_ECURVE      pCurve,
+    _In_    PCSYMCRYPT_INT         piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT     poSrc,
+            UINT32                 flags,
+    _Out_   PSYMCRYPT_ECPOINT      poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE                  pbScratch,
+            SIZE_T                 cbScratch)
+{
+    SYMCRYPT_ERROR        scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_MODULUS     pmMod;
+    PSYMCRYPT_MODELEMENT  peX1, peZ1, peA24, peX2, peZ2, peX3, peZ3, peTemp1, peTemp2, peResult;
+    UINT32                i, nBytes, nDigits, cond, newcond, nCommon;
+    PBYTE                 pBegin;
+    SIZE_T                cbAllScratch;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_MONTGOMERY_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( (poSrc == NULL || SymCryptEcurveIsSame(pCurve, poSrc->pCurve)) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    // Make sure we only specify the correct flags
+    if ((flags & ~SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (poSrc == NULL)
+    {
+        poSrc = pCurve->G;
+    }
+
+    //
+    // Set up structure for X2, Z2, X3, Z3, Temp1, and Temp2, and the scratch space.
+    //
+    pmMod = pCurve->FMod;
+
+    nDigits = SymCryptDigitsFromBits( pCurve->FModBitsize );
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+    nCommon = SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nDigits), SYMCRYPT_MAX(SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nDigits), SYMCRYPT_SCRATCH_BYTES_FOR_MODINV(nDigits)));
+
+    SYMCRYPT_ASSERT( cbScratch >= 6 * nBytes + nCommon );
+
+    cbAllScratch = cbScratch;
+    pBegin = pbScratch;
+
+    //
+    // Create mod elements
+    //
+    peX2 = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+
+    peZ2 = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+
+    peX3 = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+
+    peZ3 = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+
+    peTemp1 = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+
+    peTemp2 = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+
+    cbScratch = nCommon;
+
+    //
+    // Set up values
+    //
+
+    peA24 = pCurve->A;
+
+    // X1 = X, Z1 = Z
+    peX1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc);
+    peZ1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc);
+
+    // X2 = 1, Z2 = 0, X3 = X, Z3 = Z
+    SymCryptModElementSetValueUint32( 1, pmMod, peX2, pbScratch, cbScratch );
+    SymCryptModElementSetValueUint32( 0, pmMod, peZ2, pbScratch, cbScratch );
+    SymCryptModElementCopy( pmMod, peX1, peX3 );
+    SymCryptModElementCopy( pmMod, peZ1, peZ3 );
+
+    if ( poSrc->normalized )
+    {
+        // Set peZ1 to NULL to avoid redundant multiplications in SymCryptMontgomeryDoubleAndAdd
+        peZ1 = NULL;
+    }
+
+    //
+    //  Montgomery ladder scalar multiplication
+    //
+
+    i = (pCurve->GOrdBitsize + pCurve->coFactorPower);
+    cond = 0;
+    while ( i != 0 )
+    {
+        // If cond = 0, we have (X2, Z2, X3, Z3)
+        // if cond = 1, we have (X3, Z3, X2, Z2)
+        i--;
+        newcond = SymCryptIntGetBit( piScalar, i );
+        cond ^= newcond;
+
+        SymCryptModElementConditionalSwap( pmMod, peX2, peX3, cond);
+        SymCryptModElementConditionalSwap( pmMod, peZ2, peZ3, cond);
+
+        cond = newcond;
+
+        SymCryptMontgomeryDoubleAndAdd( pmMod, peX1, peZ1, peA24, peX2, peZ2, peX3, peZ3, peTemp1, peTemp2, pbScratch, cbScratch );
+    }
+
+    // Now put them back in the normal order
+    SymCryptModElementConditionalSwap( pmMod, peX2, peX3, cond);
+    SymCryptModElementConditionalSwap( pmMod, peZ2, peZ3, cond);
+
+    // Multiply by the cofactor (if needed) by continuing the doubling
+    if ((flags & SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL) != 0)
+    {
+        i = pCurve->coFactorPower;
+        while (i!=0)
+        {
+            i--;
+            // We only use the doubling output here, so we definitely don't need to provide Z1
+            // We could refactor to have a separate SymCryptMontgomeryDouble function but for Curve25519 this loop is ~1% of runtime
+            SymCryptMontgomeryDoubleAndAdd( pmMod, peX1, NULL, peA24, peX2, peZ2, peX3, peZ3, peTemp1, peTemp2, pbScratch, cbScratch );
+        }
+    }
+
+    // Set X coordinate
+    peResult = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst);
+    SymCryptModElementCopy( pCurve->FMod, peX2, peResult );
+
+    // Set Z coordinate
+    peResult = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst);
+    SymCryptModElementCopy( pCurve->FMod, peZ2, peResult );
+
+    poDst->normalized = FALSE;
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ec_mul.c b/libs/symcrypt/lib/ec_mul.c
new file mode 100644
index 000000000000..615edffa2013
--- /dev/null
+++ b/libs/symcrypt/lib/ec_mul.c
@@ -0,0 +1,571 @@
+//
+// ec_mul.c   Generic multiplication algorithms for elliptic curves
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+//
+// Most of the following algorithms were presented in the paper
+// "Selecting Elliptic Curves for Cryptography: An Efficiency and
+//  Security Analysis" by Bos, Costello, Longa, and Naehrig
+//
+
+//
+// The following is an adaptation of algorithm 4: "Precomputation
+// scheme for Weierstrass curves"
+//
+// Input:   Point P and number of precomputed points nPoints (=2^(w-2))
+//
+// Output:  P[i] = (2*i+1)P for 0<=i<2^(w-2)
+//
+// Remarks:
+//          1. We store each point in an array of 4*2^(w-2) = 2^w modelements where
+//             each point is represented with X,Y,Z Jacobian coordinates and the W=-Y
+//             negated Y coordinate (so that we can get the negative of a point easily)
+//          2. The source point P is already in the 0'th position of the array.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptPrecomputation(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+            UINT32                  nPoints,
+    _In_reads_( SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS )
+            PSYMCRYPT_ECPOINT *     poPIs,
+    _Out_   PSYMCRYPT_ECPOINT       poQ,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poQ->pCurve) );
+    // Calculation for Q = 2*P
+    SymCryptEcpointDouble( pCurve, poPIs[0], poQ, 0, pbScratch, cbScratch );
+
+    for (UINT32 i=1; i<nPoints; i++)
+    {
+        // Calculation for (2i+1)*P = i*Q + P
+        SymCryptEcpointAddDiffNonZero( pCurve, poQ, poPIs[i-1], poPIs[i], pbScratch, cbScratch );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptOfflinePrecomputation(
+    _In_    PSYMCRYPT_ECURVE pCurve,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE         pbScratch,
+            SIZE_T        cbScratch )
+{
+    PSYMCRYPT_ECPOINT poQ = NULL;
+
+    UINT32 cbEcpoint = SymCryptSizeofEcpointFromCurve( pCurve );
+
+    SYMCRYPT_ASSERT( cbScratch >= cbEcpoint + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) );
+
+    poQ = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+    SYMCRYPT_ASSERT( poQ != NULL );
+    pbScratch += cbEcpoint;
+    cbScratch -= cbEcpoint;
+
+    SymCryptPrecomputation(
+                pCurve,
+                pCurve->info.sw.nPrecompPoints,
+                pCurve->info.sw.poPrecompPoints,
+                poQ,
+                pbScratch,
+                cbScratch );
+}
+
+// Mask which is 0xffffffff only when _index == _target
+#define DELTA_MASK( _index, _target)    SYMCRYPT_MASK32_ZERO( (_index) ^ (_target) )
+
+//
+// The following is an adaptation of algorithm 1: "Variable-base scalar multiplication
+// using the fixed-window method"
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointScalarMulFixedWindow(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _In_    PCSYMCRYPT_INT          piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT      poSrc,
+            UINT32                  flags,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+
+    UINT32  i, j;
+
+    UINT32  w = pCurve->info.sw.window;
+    UINT32  nPrecompPoints = pCurve->info.sw.nPrecompPoints;
+	// dcl - assuming that nRecodedDigits has some reasonably small range - please document
+	// so that we can know usage of this variable will not cause problems
+	// Also, documentation of inputs, notes, etc at the function definition would be quite helpful
+    UINT32  nRecodedDigits = ((pCurve->GOrdBitsize + w - 2) / (w-1)) + 1;
+
+    // Masks
+    UINT32  fZero = 0;
+    UINT32  fEven = 0;
+    UINT32  indexMask = 0;
+
+    BOOLEAN bPrecompOffline = FALSE;
+
+    // ====================================================
+    // Temporaries
+    PSYMCRYPT_MODELEMENT    peT = NULL;
+    PSYMCRYPT_ECPOINT       poPIs[SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS] = { 0 };
+    PSYMCRYPT_ECPOINT       poQ = NULL;
+    PSYMCRYPT_ECPOINT       poTmp = NULL;
+    PSYMCRYPT_INT           piRem = NULL;
+    PSYMCRYPT_INT           piTmp = NULL;
+    PUINT32                 absofKIs = NULL;
+    PUINT32                 sigofKIs = NULL;
+    // ===================================================
+
+    PSYMCRYPT_MODELEMENT    peQX = NULL;
+    PSYMCRYPT_MODELEMENT    peQY = NULL;
+    PSYMCRYPT_MODELEMENT    peQZ = NULL;
+
+    SIZE_T cbEcpoint = SymCryptSizeofEcpointFromCurve( pCurve );
+    SIZE_T cbScalar = SymCryptSizeofIntFromDigits( pCurve->GOrdDigits );
+
+    // Make sure we only specify the correct flags
+    if ((flags & ~SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    // Check if poSrc is NULL and if yes set it to G
+    if (poSrc == NULL)
+    {
+        poSrc = pCurve->G;
+        bPrecompOffline = TRUE;
+    }
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) ||
+                     SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS(pCurve, 1) );
+
+    SYMCRYPT_ASSERT( cbScratch >=
+                        pCurve->cbModElement +
+                        (nPrecompPoints+2)*cbEcpoint +
+                        2*cbScalar +
+                        ((2*nRecodedDigits*sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE )*SYMCRYPT_ASYM_ALIGN_VALUE +
+                        SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    // Creating temporary modelement
+    peT = SymCryptModElementCreate( pbScratch, pCurve->cbModElement, FMod );
+    SYMCRYPT_ASSERT( peT != NULL );
+    pbScratch += pCurve->cbModElement;
+
+    // Creating temporary precomputed points (if needed)
+    SYMCRYPT_ASSERT( nPrecompPoints <= SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS );
+    for (i=0; i<nPrecompPoints; i++)
+    {
+        if (bPrecompOffline)
+        {
+            poPIs[i] = pCurve->info.sw.poPrecompPoints[i];
+        }
+        else
+        {
+            poPIs[i] = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+            SYMCRYPT_ASSERT( poPIs[i] != NULL );
+            pbScratch += cbEcpoint;
+        }
+    }
+
+    // Creating temporary points
+    poQ = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+    SYMCRYPT_ASSERT( poQ != NULL );
+    pbScratch += cbEcpoint;
+
+    poTmp = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+    SYMCRYPT_ASSERT( poTmp != NULL );
+    pbScratch += cbEcpoint;
+
+    // Creating temporary scalar for the remainder
+    piRem = SymCryptIntCreate( pbScratch, cbScalar, pCurve->GOrdDigits );
+    SYMCRYPT_ASSERT( piRem != NULL);
+    pbScratch += cbScalar;
+
+    piTmp = SymCryptIntCreate( pbScratch, cbScalar, pCurve->GOrdDigits );
+    SYMCRYPT_ASSERT( piTmp != NULL);
+    pbScratch += cbScalar;
+
+    // Fixing pointers to recoded digits (be careful that the remaining space is SYMCRYPT_ASYM_ALIGNed)
+    absofKIs = (PUINT32) pbScratch;
+    pbScratch += nRecodedDigits * sizeof(UINT32);
+    sigofKIs = (PUINT32) pbScratch;
+    pbScratch += nRecodedDigits * sizeof(UINT32);
+    pbScratch = (PBYTE) ( ((SIZE_T)pbScratch + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1) );
+
+    // Fixing remaining scratch space size
+    cbScratch -= ( pCurve->cbModElement + (nPrecompPoints+2)*cbEcpoint + 2*cbScalar );
+    cbScratch -= (((2*nRecodedDigits*sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE )*SYMCRYPT_ASYM_ALIGN_VALUE);
+
+    //
+    // Main algorithm
+    //
+
+    // It is the caller's responsibility to ensure that the provided piScalar <= GOrd, double check this in debug mode
+    SYMCRYPT_ASSERT( !SymCryptIntIsLessThan( SymCryptIntFromModulus( pCurve->GOrd ), piScalar ) );
+
+    // Store k into an int
+    SymCryptIntCopy( piScalar, piRem );
+
+    // Check if k is 0
+    fZero = SymCryptIntIsEqualUint32( piRem, 0 );
+
+    // Or if the src point is zero
+    fZero |= SymCryptEcpointIsZero( pCurve, poSrc, pbScratch, cbScratch );
+
+    // Check if k is even and convert it to r-k if true
+    fEven = SYMCRYPT_MASK32_ZERO(SymCryptIntGetBit( piRem, 0 ));
+    SymCryptIntSubSameSize( SymCryptIntFromModulus(pCurve->GOrd), piRem, piTmp);
+    SymCryptIntMaskedCopy( piTmp, piRem, fEven );
+
+    // Recoding stage
+    SymCryptFixedWindowRecoding( w, piRem, piTmp, absofKIs, sigofKIs, nRecodedDigits );
+
+    // Precomputation stage
+    if (!bPrecompOffline)
+    {
+        // Copy the first point in the start of the poPIs array
+        SymCryptEcpointCopy( pCurve, poSrc, poPIs[0] );
+
+        SymCryptPrecomputation( pCurve, nPrecompPoints, poPIs, poQ, pbScratch, cbScratch );
+    }
+
+
+    // Get the pointers to Q
+    peQX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poQ );
+    peQY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poQ );
+    peQZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poQ );
+
+    // Q = P[ (|k_t|-1)/2 ] in memory access side-channel safe way
+    // That is, we touch all the precomputed points. The access pattern of KIs is fixed.
+    for (j=0; j<nPrecompPoints; j++)
+    {
+        indexMask = DELTA_MASK( j, absofKIs[nRecodedDigits-1] );
+        SymCryptEcpointMaskedCopy( pCurve, poPIs[j], poQ, indexMask);
+    }
+
+    for (i=nRecodedDigits - 2; i>0; i--)
+    {
+        // Q = 2^(w-1) * Q
+        for (j=0; j<w-1; j++)
+        {
+            SymCryptEcpointDouble( pCurve, poQ, poQ, 0, pbScratch, cbScratch );
+        }
+
+        // Copy the required precomputed point into poTmp (touch all points)
+        for (j=0; j<nPrecompPoints; j++)
+        {
+            indexMask = DELTA_MASK( j, absofKIs[i] );
+            SymCryptEcpointMaskedCopy( pCurve, poPIs[j], poTmp, indexMask);
+        }
+
+        // Negate if needed
+        SymCryptEcpointNegate( pCurve, poTmp, sigofKIs[i], pbScratch, cbScratch );
+
+        // Do the addition Q + s_i P[k_i]
+        SymCryptEcpointAddDiffNonZero( pCurve, poQ, poTmp, poQ, pbScratch, cbScratch );
+    }
+
+    // Q = 2^(w-1) * Q
+    for (j=0; j<w-1; j++)
+    {
+        SymCryptEcpointDouble( pCurve, poQ, poQ, 0, pbScratch, cbScratch );
+    }
+
+    // Copy the point s_0 P[k_0] into poTmp
+    for (j=0; j<nPrecompPoints; j++)
+    {
+        indexMask = DELTA_MASK( j, absofKIs[0] );
+        SymCryptEcpointMaskedCopy( pCurve, poPIs[j], poTmp, indexMask);
+    }
+
+    // Negate if needed
+    SymCryptEcpointNegate( pCurve, poTmp, sigofKIs[0], pbScratch, cbScratch );
+
+    // Complete addition routine
+    SymCryptEcpointAdd( pCurve, poQ, poTmp, poQ, 0, pbScratch, cbScratch );
+
+    // If even invert
+    SymCryptEcpointNegate( pCurve, poQ, fEven, pbScratch, cbScratch );
+
+    // Multiply by the cofactor (if needed) by continuing the doubling
+    if ((pCurve->coFactorPower!=0) && ((flags & SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL) != 0))
+    {
+        for (j=0; j<pCurve->coFactorPower; j++)
+        {
+            SymCryptEcpointDouble( pCurve, poQ, poQ, 0, pbScratch, cbScratch );
+        }
+    }
+
+    // If the resultant point is zero, ensure it will be set to the canonical zero point
+    fZero |= SymCryptEcpointIsZero( pCurve, poQ, pbScratch, cbScratch );
+
+    // Set the zero point
+    SymCryptEcpointSetZero( pCurve, poTmp, pbScratch, cbScratch );
+    SymCryptEcpointMaskedCopy( pCurve, poTmp, poQ, fZero );
+
+    // Output the result (normalized flag == FALSE)
+    SymCryptEcpointCopy( pCurve, poQ, poDst );
+
+    scError = SYMCRYPT_NO_ERROR;
+
+exit:
+
+    return scError;
+}
+
+//
+// The following is an adaptation of algorithm 9: "Double-scalar multiplication using the
+// width-w NAF with interleaving"
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointMultiScalarMulWnafWithInterleaving(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_reads_( nPoints )           PCSYMCRYPT_INT *        piSrcScalarArray,
+    _In_reads_( nPoints )           PCSYMCRYPT_ECPOINT *    poSrcEcpointArray,
+    _In_                            UINT32                  nPoints,
+    _In_                            UINT32                  flags,
+    _Out_                           PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+
+    UINT32  i, j;
+
+    UINT32  w = pCurve->info.sw.window;
+    UINT32  nPrecompPoints = pCurve->info.sw.nPrecompPoints;            // One table for each base
+    UINT32  nRecodedDigits = pCurve->GOrdBitsize + 1;                   // Notice the difference with the fixed window
+
+    // Masks
+    UINT32  fZero[SYMCRYPT_ECURVE_MULTI_SCALAR_MUL_MAX_NPOINTS] = { 0 };
+    UINT32  fZeroTot = 0xffffffff;
+
+    BOOLEAN bPrecompOffline = FALSE;
+
+    // ====================================================
+    // Temporaries
+    PSYMCRYPT_ECPOINT       poPIs[SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS] = { 0 };
+    PSYMCRYPT_ECPOINT       poQ = NULL;
+    PSYMCRYPT_ECPOINT       poTmp = NULL;
+    PSYMCRYPT_INT           piRem = NULL;
+    PSYMCRYPT_INT           piTmp = NULL;
+
+    PUINT32                 absofKIs = NULL;
+    PUINT32                 sigofKIs = NULL;
+    // ===================================================
+
+    SIZE_T cbEcpoint = SymCryptSizeofEcpointFromCurve( pCurve );
+    SIZE_T cbScalar = SymCryptSizeofIntFromDigits( pCurve->GOrdDigits );
+
+    PBYTE pbScratchEnd = pbScratch + cbScratch;
+    UNREFERENCED_PARAMETER( pbScratchEnd ); // Used in asserts
+
+    // Make sure we only specify the correct flags
+    if ((flags & ~(SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL)) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    // Check the maximum number of points
+    if (nPoints > SYMCRYPT_ECURVE_MULTI_SCALAR_MUL_MAX_NPOINTS)
+    {
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto exit;
+    }
+
+    // Check if the first point is NULL
+    if (poSrcEcpointArray[0] == NULL)
+    {
+        poSrcEcpointArray[0] = pCurve->G;
+        bPrecompOffline = TRUE;
+    }
+
+    // Make sure that the non side-channel flag is specified
+    if ((flags & SYMCRYPT_FLAG_DATA_PUBLIC) == 0 )
+    {
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto exit;
+    }
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) ||
+                     SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS(pCurve, nPoints) );
+
+    // Creating temporary precomputed points (if needed for the first point)
+    for (i=0; i<nPoints*nPrecompPoints; i++)
+    {
+        if ((i<nPrecompPoints) && bPrecompOffline)
+        {
+            poPIs[i] = pCurve->info.sw.poPrecompPoints[i];
+        }
+        else
+        {
+            SYMCRYPT_ASSERT( pbScratch + cbEcpoint <= pbScratchEnd );
+            poPIs[i] = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+            SYMCRYPT_ASSERT( poPIs[i] != NULL );
+            pbScratch += cbEcpoint;
+        }
+    }
+
+    SYMCRYPT_ASSERT( pbScratch + 2*cbEcpoint + 2*cbScalar + 2*nPoints*nRecodedDigits*sizeof(UINT32) <= pbScratchEnd );
+    // Creating temporary points
+    poQ = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+    SYMCRYPT_ASSERT( poQ != NULL );
+    pbScratch += cbEcpoint;
+
+    poTmp = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+    SYMCRYPT_ASSERT( poTmp != NULL );
+    pbScratch += cbEcpoint;
+
+    // Creating temporary scalar for the remainder
+    piRem = SymCryptIntCreate( pbScratch, cbScalar, pCurve->GOrdDigits );
+    SYMCRYPT_ASSERT( piRem != NULL);
+    pbScratch += cbScalar;
+
+    piTmp = SymCryptIntCreate( pbScratch, cbScalar, pCurve->GOrdDigits );
+    SYMCRYPT_ASSERT( piTmp != NULL);
+    pbScratch += cbScalar;
+
+    // Fixing pointers to recoded digits (be careful that the remaining space is SYMCRYPT_ASYM_ALIGNed)
+    absofKIs = (PUINT32) pbScratch;
+    pbScratch += nPoints * nRecodedDigits * sizeof(UINT32);
+    sigofKIs = (PUINT32) pbScratch;
+    pbScratch += nPoints * nRecodedDigits * sizeof(UINT32);
+    pbScratch = (PBYTE) ( ((SIZE_T)pbScratch + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1) );
+
+    // Fixing remaining scratch space size
+	// dcl - my guess is that the values here are small enough that there should not be a problem, but
+	// would be better if that were documented.
+    cbScratch -= ( (nPoints*nPrecompPoints+2)*cbEcpoint + 2*cbScalar );
+    cbScratch -= (((2*nPoints*nRecodedDigits*sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE )*SYMCRYPT_ASYM_ALIGN_VALUE);
+
+    //
+    // Main algorithm
+    //
+    for (j = 0; j<nPoints; j++)
+    {
+        SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrcEcpointArray[j]->pCurve) );
+
+        // Check if k is 0 or if the src point is zero
+        fZero[j] = ( SymCryptIntIsEqualUint32( piSrcScalarArray[j], 0 ) | SymCryptEcpointIsZero( pCurve, poSrcEcpointArray[j], pbScratch, cbScratch ) );
+        fZeroTot &= fZero[j];
+
+        // Skip the recoding stage (and all remaining steps) if this point will give result zero
+        if (!fZero[j])
+        {
+            SymCryptIntCopy( piSrcScalarArray[j], piRem );
+
+            // Recoding stage
+            SymCryptWidthNafRecoding( w, piRem, &absofKIs[j*nRecodedDigits], &sigofKIs[j*nRecodedDigits], nRecodedDigits );
+
+            // Precomputation stage
+            if ((j>0) || !bPrecompOffline)
+            {
+                // Copy the first point in the start of the poPIs array
+                SymCryptEcpointCopy( pCurve, poSrcEcpointArray[j], poPIs[j*nPrecompPoints] );
+
+                SymCryptPrecomputation( pCurve, nPrecompPoints, &poPIs[j*nPrecompPoints], poQ, pbScratch, cbScratch );
+            }
+        }
+    }
+
+    // Set poQ to zero point
+    SymCryptEcpointSetZero( pCurve, poQ, pbScratch, cbScratch );
+
+    if (!fZeroTot)
+    {
+        // Main loop
+        for (INT32 i = nRecodedDigits-1; i>-1; i--)
+        {
+            SymCryptEcpointDouble( pCurve, poQ, poQ, 0, pbScratch, cbScratch );
+
+            for (j = 0; j<nPoints; j++)
+            {
+                if (!fZero[j] && sigofKIs[j*nRecodedDigits + i] != 0)
+                {
+                    SymCryptEcpointCopy( pCurve, poPIs[j*nPrecompPoints + absofKIs[j*nRecodedDigits + i]/2], poTmp );
+
+                    if (sigofKIs[j*nRecodedDigits + i] == 0xffffffff)
+                    {
+                        SymCryptEcpointNegate( pCurve, poTmp, 0xffffffff, pbScratch, cbScratch );
+                    }
+
+                    SymCryptEcpointAdd( pCurve, poQ, poTmp, poQ, SYMCRYPT_FLAG_DATA_PUBLIC, pbScratch, cbScratch );
+                }
+            }
+        }
+    }
+
+    // Multiply by the cofactor (if needed) by continuing the doubling
+    if ((pCurve->coFactorPower!=0) && ((flags & SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL) != 0))
+    {
+        for (j=0; j<pCurve->coFactorPower; j++)
+        {
+            SymCryptEcpointDouble( pCurve, poQ, poQ, 0, pbScratch, cbScratch );
+        }
+    }
+
+    // If the resultant point is zero, ensure it will be set to the canonical zero point
+    if ( SymCryptEcpointIsZero( pCurve, poQ, pbScratch, cbScratch ) )
+    {
+        // Set poQ to zero point
+        SymCryptEcpointSetZero( pCurve, poQ, pbScratch, cbScratch );
+    }
+
+    // Copy the result to the destination (normalized flag == FALSE)
+    SymCryptEcpointCopy( pCurve, poQ, poDst );
+
+    scError = SYMCRYPT_NO_ERROR;
+
+exit:
+    return scError;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointGenericSetRandom(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _Out_   PSYMCRYPT_INT           piScalar,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch )
+{
+    PSYMCRYPT_MODELEMENT peScalar = NULL;
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_SCALAR_ECURVE_OPERATIONS(pCurve, 1) );
+    SYMCRYPT_ASSERT( cbScratch >= pCurve->cbModElement );
+
+    peScalar = SymCryptModElementCreate( pbScratch, pCurve->cbModElement, pCurve->GOrd );
+    SYMCRYPT_ASSERT( peScalar != NULL );
+
+    // Setting a random mod element in the [1, SubgroupOrder-1] set
+    SymCryptModSetRandom( pCurve->GOrd, peScalar, (SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE|SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE), pbScratch + pCurve->cbModElement, cbScratch - pCurve->cbModElement );
+
+    // Setting the integer
+    SymCryptModElementToInt( pCurve->GOrd, peScalar, piScalar, pbScratch + pCurve->cbModElement, cbScratch - pCurve->cbModElement );
+
+    // Do the multiplication (pass over the entire scratch space as it is not needed anymore)
+    // !! Explicitly not checking the error return here as the only error is from specifying invalid flags !!
+    SymCryptEcpointScalarMul( pCurve, piScalar, NULL, 0, poDst, pbScratch, cbScratch );
+}
diff --git a/libs/symcrypt/lib/ec_short_weierstrass.c b/libs/symcrypt/lib/ec_short_weierstrass.c
new file mode 100644
index 000000000000..ca8399a5fd75
--- /dev/null
+++ b/libs/symcrypt/lib/ec_short_weierstrass.c
@@ -0,0 +1,935 @@
+//
+// ec_short_weierstrass.c   ECPOINT functions for short Weierstrass curves.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+//
+// Scratch space requirements for each ECPOINT function.
+//
+// A function's requirements in scratch space consist of requirements for its own arithmetic
+// operations and temporaries ("self" scratch space) and scratch space requirements for other
+// ECPOINT functions it might call ("callee" scratch space).
+//
+// If the outer function does not need the temporaries after calling the inner ECPOINT
+// function, then the total scratch space can be the maximum of both. Otherwise the scratch
+// space of the outer function should be the concatenation of the "self" scratch space and
+// the "callee" scratch space.
+//
+// The following table shows the scratch space requirements of each function with appropriate
+// abbreviations. The calling sequence implies a directed graph that starting from the "leaves"
+// (functions that do no call others) allows to calculate the total scratch space requirements.
+//
+//  #N Function                     Calls Function  Self Temporaries            Self Scratch
+//   1 SetZero                      -               0                           COM_MOD(FMod)
+//   2 SetDistinguishedPoint        -               0                           0
+//   3 IsEqual                      -               4 ModEl                     COM_MOD(FMod)
+//   4 IsZero                       -               1 ModEl                     COM_MOD(FMod)
+//   5 OnCurve                      -               2 ModEl                     COM_MOD(FMod)
+//   6 Double                       1,4,9           2 Ecp                       0
+//   7 Add                          1,3,4,8,9       2 Ecp                       0
+//   8 AddDiffNonZero               -               8 ModEl                     COM_MOD(FMod)
+//   9 Double                       -               6 ModEl                     COM_MOD(FMod)
+//
+//  10 SetRandom                    11              0                           COM_MOD(GOrd)
+//  11 ScalarMul                    4,5,7           1ModEl + (n+2)Ecp + 2Int    COM_MOD(GOrd)
+//
+// Since only 4 functions call others and to keep things simple, we will have 2
+// types of scratch space: "ECURVE_COMMON" and "ECURVE_SCALAR"
+//
+// ---- All functions except 10 and 11 will use the "ECURVE_COMMON" scratch space. The size of it
+//      depends only on parameters of the curve. Schematically it will be:
+//          |----------COMMON------------------------------------------------------------------|
+//          |------8 ModEl + 2 Ecpoint----||------COM_MOD(FMod)--------------------------------|
+//
+// ---- The SetRandom and ScalarMul have requirements that depend on temporaries for the pre-computation.
+//      Also they depend on the "self" temporaries after calling the inner functions.
+//      Therefore, these will require the "ECURVE_SCALAR" scratch space which
+//      consists of two parts: The self space for the above two functions and the
+//      common scratch space. These parts SHOULD NOT overlap. Schematically:
+//
+//          |--------------SCALAR---------------------------------------------------|
+//          |----1ModEl + (n+2)Ecp + 2Int--------||---max(COMMON, COM_MOD(GOrd)----|
+
+// The scratch space sizes are all calculated by the following function.
+// *** Notice that almost all the curve parameters (exception is the distinguished point)
+//     must have been initialized before calling this function.
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassFillScratchSpaces( _In_ PSYMCRYPT_ECURVE pCurve )
+{
+    UINT32 nDigits = SymCryptDigitsFromBits( pCurve->FModBitsize );
+
+    //
+    // All the scratch space computations are upper bounded by the SizeofXXX bound (2^19) and
+    // the SCRATCH_BYTES_FOR_XXX bound (2^24) (see symcrypt_internal.h).
+    //
+    // One caveat is SymCryptSizeofEcpointFromCurve and SymCryptSizeofEcpointEx which calculate
+    // the size of EcPoint with 4 coordinates (each one a modelement of max size 2^17). Thus upper
+    // bounded by 2^20.
+    //
+    // Another is the precomp points computation where the nPrecompPoints are up to
+    // 2^SYMCRYPT_ECURVE_SW_DEF_WINDOW = 2^6 and the nRecodedDigits are equal to the
+    // GOrd bitsize < 2^20.
+    //
+    // Thus cbScratchScalarMulti is upper bounded by 2^6*2^20 + 2*2^20*2^4 ~ 2^26.
+    //
+
+    // Common
+    pCurve->cbScratchCommon =
+            8 * pCurve->cbModElement +
+            2 * SymCryptSizeofEcpointFromCurve( pCurve ) +
+            SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits );
+
+    // Scalar (Overhead)
+    pCurve->cbScratchScalar =
+            pCurve->cbModElement +
+            2 * SymCryptSizeofEcpointFromCurve( pCurve ) +
+            2 * SymCryptSizeofIntFromDigits( pCurve->GOrdDigits ) +
+            SYMCRYPT_MAX( pCurve->cbScratchCommon, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->GOrdDigits ));
+
+    // Scalar dependent on precomp points (be careful to align the UINT32 arrays properly)
+    pCurve->cbScratchScalarMulti =
+            pCurve->info.sw.nPrecompPoints * SymCryptSizeofEcpointFromCurve( pCurve ) +
+            ((2*pCurve->info.sw.nRecodedDigits * sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1 )/SYMCRYPT_ASYM_ALIGN_VALUE) * SYMCRYPT_ASYM_ALIGN_VALUE;
+
+    // GetSetValue
+    pCurve->cbScratchGetSetValue =
+            SymCryptSizeofEcpointEx( pCurve->cbModElement, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH ) +
+            2 * pCurve->cbModElement +
+            SYMCRYPT_MAX(    SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ),
+                    SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( pCurve->FModDigits ) );
+
+    pCurve->cbScratchGetSetValue = SYMCRYPT_MAX( pCurve->cbScratchGetSetValue, SymCryptSizeofIntFromDigits( nDigits ) );
+
+    // Eckey
+    pCurve->cbScratchEckey =
+            SYMCRYPT_MAX( pCurve->cbModElement + SymCryptSizeofIntFromDigits(SymCryptEcurveDigitsofScalarMultiplier(pCurve)),
+                SymCryptSizeofEcpointFromCurve( pCurve ) ) +
+            SYMCRYPT_MAX( pCurve->cbScratchScalar + pCurve->cbScratchScalarMulti, pCurve->cbScratchGetSetValue );
+}
+
+//
+// The following function sets the point to (1:1:0) in Jacobian coordinates.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassSetZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) );
+
+    // Getting handle to X
+    peTmp = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+
+    // Setting the right value (always 1)
+    SymCryptModElementSetValueUint32( 1, FMod, peTmp, pbScratch, cbScratch );
+
+    // Getting handle to Y
+    peTmp = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+
+    // Setting the right value (always 1)
+    SymCryptModElementSetValueUint32( 1, FMod, peTmp, pbScratch, cbScratch );
+
+    // Getting handle to Z
+    peTmp = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+
+    // Setting the right value (always 0)
+    SymCryptModElementSetValueUint32( 0, pCurve->FMod, peTmp, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassSetDistinguished(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptEcpointCopy( pCurve, pCurve->G, poDst );
+}
+
+//
+// The following function checks if
+//      - X1*Z2^2 = X2*Z1^2 and Y1*Z2^3 = Y2*Z1^3 (Equal case)
+//      - X1*Z2^2 = X2*Z1^2 and Y1*Z2^3 = -Y2*Z1^3 (Negative case)
+//
+// Remark: The case where Z1 = Z2 = 0 is covered above (the zero point
+//         is equal to its negative).
+//
+UINT32
+SYMCRYPT_CALL
+SymCryptShortWeierstrassIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+
+    PSYMCRYPT_MODELEMENT peX1 = NULL;    // Pointer to X1
+    PSYMCRYPT_MODELEMENT peY1 = NULL;    // Pointer to Y1
+    PSYMCRYPT_MODELEMENT peZ1 = NULL;    // Pointer to Z1
+    PSYMCRYPT_MODELEMENT peX2 = NULL;    // Pointer to X2
+    PSYMCRYPT_MODELEMENT peY2 = NULL;    // Pointer to Y2
+    PSYMCRYPT_MODELEMENT peZ2 = NULL;    // Pointer to Z2
+
+    UINT32  dResX = 0;
+    UINT32  dResY = 0;
+    UINT32  dResYN = 0;
+
+    PSYMCRYPT_MODELEMENT peT[4] = { 0 };  // Temporaries
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) );
+    SYMCRYPT_ASSERT( (flags & ~(SYMCRYPT_FLAG_ECPOINT_EQUAL|SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL)) == 0 );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + 4 * pCurve->cbModElement );
+
+    // Creating temporaries
+    for (UINT32 i=0; i<4; i++)
+    {
+        peT[i] = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+
+        SYMCRYPT_ASSERT( peT[i] != NULL);
+
+        pbScratch += pCurve->cbModElement;
+    }
+
+    // Fixing remaining scratch space size
+    cbScratch -= 4 * pCurve->cbModElement;
+
+    // Getting pointers to x and y of the source point
+    peX1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc1 );
+    peY1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc1 );
+    peZ1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc1 );
+    peX2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc2 );
+    peY2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc2 );
+    peZ2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc2 );
+
+    // Setting the default flag if flags == 0
+    flags |= ( SYMCRYPT_MASK32_ZERO( flags ) & SYMCRYPT_FLAG_ECPOINT_EQUAL );
+
+    // Calculation
+    SymCryptModSquare( FMod, peZ1, peT[0], pbScratch, cbScratch );            // T0 := Z1 * Z1 = Z1^2
+    SymCryptModSquare( FMod, peZ2, peT[1], pbScratch, cbScratch );            // T1 := Z2 * Z2 = Z2^2
+    SymCryptModMul( FMod, peX1, peT[1], peT[2], pbScratch, cbScratch );       // T2 := X1 * T1 = X1*Z2^2
+    SymCryptModMul( FMod, peX2, peT[0], peT[3], pbScratch, cbScratch );       // T3 := X2 * T0 = X2*Z1^2
+
+    dResX = SymCryptModElementIsEqual( FMod, peT[2], peT[3] );
+
+    SymCryptModMul( FMod, peZ1, peT[0], peT[0], pbScratch, cbScratch );       // T0 := Z1 * T0 = Z1^3
+    SymCryptModMul( FMod, peZ2, peT[1], peT[1], pbScratch, cbScratch );       // T1 := Z2 * T1 = Z2^3
+    SymCryptModMul( FMod, peY1, peT[1], peT[2], pbScratch, cbScratch );       // T2 := Y1 * T1 = Y1*Z2^3
+    SymCryptModMul( FMod, peY2, peT[0], peT[3], pbScratch, cbScratch );       // T3 := Y2 * T0 = Y2*Z1^3
+
+    dResY = SymCryptModElementIsEqual( FMod, peT[2], peT[3] );
+
+    SymCryptModNeg( FMod, peT[3], peT[3], pbScratch, cbScratch );             // T3 := -T3 = -Y2*Z1^3
+
+    dResYN = SymCryptModElementIsEqual( FMod, peT[2], peT[3] );
+
+    return (SYMCRYPT_MASK32_NONZERO(flags & SYMCRYPT_FLAG_ECPOINT_EQUAL) & dResX & dResY) |
+           (SYMCRYPT_MASK32_NONZERO(flags & SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL) & dResX & dResYN);
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptShortWeierstrassIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peZ = NULL;    // Pointer to Z
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    // Getting pointer to Z of the source point
+    peZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    // Setting temporary to 0
+    return SymCryptModElementIsZero( FMod, peZ );
+}
+
+//
+// The following function verifies if the point (X:Y:Z) in Jacobian
+// coordinates satisfies the equation Y^2 = X^3 + aXZ^4+bZ^6 .
+//
+UINT32
+SYMCRYPT_CALL
+SymCryptShortWeierstrassOnCurve(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+
+    PSYMCRYPT_MODELEMENT peX = NULL;    // Pointer to X
+    PSYMCRYPT_MODELEMENT peY = NULL;    // Pointer to Y
+    PSYMCRYPT_MODELEMENT peZ = NULL;    // Pointer to Z
+
+    PSYMCRYPT_MODELEMENT peT[2] = { 0 }; // Temporaries
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + 2 * pCurve->cbModElement );
+
+    // Creating temporaries
+    for (UINT32 i=0; i<2; i++)
+    {
+        peT[i] = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+
+        SYMCRYPT_ASSERT( peT[i] != NULL);
+
+        pbScratch += pCurve->cbModElement;
+    }
+
+    // Fixing remaining scratch space size
+    cbScratch -= 2*pCurve->cbModElement;
+
+    // Getting pointers to coordinates of the source point
+    peX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+    peY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+    peZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    // Calculation
+    SymCryptModSquare( FMod, peZ, peT[0], pbScratch, cbScratch );               // T1 := Z  * Z  = Z^2
+    SymCryptModSquare( FMod, peT[0], peT[1], pbScratch, cbScratch );            // T2 := T1 * T1 = Z^4
+    SymCryptModMul( FMod, peT[0], peT[1], peT[0], pbScratch, cbScratch );       // T1 := T1 * T2 = Z^6
+
+    SymCryptModMul( FMod, peT[0], pCurve->B, peT[0], pbScratch, cbScratch );    // T1 := T1 * b  = bZ^6
+
+    SymCryptModMul( FMod, peT[1], peX, peT[1], pbScratch, cbScratch );          // T2 := T2 * X  = XZ^4
+    SymCryptModMul( FMod, peT[1], pCurve->A, peT[1], pbScratch, cbScratch );    // T2 := T2 * a  = aXZ^4
+
+    SymCryptModAdd( FMod, peT[0], peT[1], peT[1], pbScratch, cbScratch );       // T2 := T1 + T2 = aXZ^4 + bZ^6
+
+    SymCryptModSquare( FMod, peX, peT[0], pbScratch, cbScratch );               // T1 := X  * X  = X^2
+    SymCryptModMul( FMod, peT[0], peX, peT[0], pbScratch, cbScratch );          // T1 := T1 * X  = X^3
+    SymCryptModAdd( FMod, peT[0], peT[1], peT[1], pbScratch, cbScratch );       // T2 := T1 + T2 = X^3 + aXZ^4 + bZ^6
+
+    SymCryptModSquare( FMod, peY, peT[0], pbScratch, cbScratch );               // T1 := Y  * Y  = Y^2
+
+    return SymCryptModElementIsEqual( FMod, peT[0], peT[1] );
+}
+
+//
+// based on dbl-2007-bl formula
+// but tweaked by saml to
+//   a) remove overeager conversions from modular multiplication to modular squaring which introduce
+//      more addition/subtraction. With current implementations (based on montgomery reduction),
+//      the cost of [a square and an add/sub] is greater than the cost of [a multiplication]
+//   b) share intermediate results of producing 8YYYY. [add/sub] is ~10% of cost of mul, so reducing
+//      count of these operation has a real impact
+//
+//      2Y = 2*Y1
+//      2YY = 2Y*Y1
+//      4YY = 2*2YY
+//      8YYYY = 2YY*4YY
+//      S = X1*4YY
+//      XX = X1^2
+//      ZZ = Z1^2
+//      ZZZZ = ZZ^2
+//      M = 3*XX+a*ZZZZ
+//      T = M^2-2*S
+//      X3 = T
+//      Y3 = M*(S-T)-8YYYY
+//      Z3 = Z1*2Y
+//
+// Total cost:
+//      6 Mul (1 by a)
+//      4 Sqr
+//      2 Add
+//      4 Sub
+//      3 Dbl
+//
+// Special Case:
+//      If the source point is equal to the identity
+//      point of the curve (i.e. Z1 = 0 in Jacobian
+//      coordinates) then the resulting point has
+//      Z3 = Z1*2Y1 = 0. Thus, this formula is
+//      complete (it works for all points).
+//
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassDouble(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peT[3] = { 0 };    // Temporaries
+
+    PCSYMCRYPT_MODELEMENT peX1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+    PCSYMCRYPT_MODELEMENT peY1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+    PCSYMCRYPT_MODELEMENT peZ1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    PSYMCRYPT_MODELEMENT  peX3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT  peY3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT  peZ3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+
+    SYMCRYPT_ASSERT( pCurve->type == SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + 3 * pCurve->cbModElement );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Creating temporaries
+    for (UINT32 i=0; i<3; i++)
+    {
+        peT[i] = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+
+        SYMCRYPT_ASSERT( peT[i] != NULL);
+
+        pbScratch += pCurve->cbModElement;
+    }
+
+    // Fixing remaining scratch space size
+    cbScratch -= 3*pCurve->cbModElement;
+
+    // Calculate the points
+    SymCryptModAdd( FMod, peY1, peY1, peT[0], pbScratch, cbScratch );           /* T0 := Y1 + Y1 = 2Y */
+    SymCryptModSquare( FMod, peZ1, peT[1], pbScratch, cbScratch );              /* T1 := Z1 * Z1 = ZZ */
+    SymCryptModMul( FMod, peT[0], peZ1, peZ3, pbScratch, cbScratch );           /* Z3 := 2Y * Z1 = 2YZ */
+
+    SymCryptModMul( FMod, peY1, peT[0], peY3, pbScratch, cbScratch );           /* Y3 := 2Y * Y1 = 2YY */
+    SymCryptModAdd( FMod, peY3, peY3, peT[0], pbScratch, cbScratch );           /* T0 := 2YY + 2YY = 4YY */
+    SymCryptModMul( FMod, peT[0], peY3, peY3, pbScratch, cbScratch );           /* Y3 := 2YY * 4YY = 8YYYY */
+
+    SymCryptModMul( FMod, peT[0], peX1, peT[0], pbScratch, cbScratch );         /* T0 := X1 * 4YY = 4XYY = S */
+
+    SymCryptModSquare( FMod, peT[1], peT[1], pbScratch, cbScratch );            /* T1 := T1 * T1 = ZZZZ */
+    SymCryptModSquare( FMod, peX1, peT[2], pbScratch, cbScratch );              /* T2 := X1 * X1 = XX */
+    SymCryptModMul( FMod, peT[1], pCurve->A, peT[1], pbScratch, cbScratch );    /* T1 := T1 * a  = a*ZZZZ */
+    SymCryptModAdd( FMod, peT[2], peT[1], peT[1], pbScratch, cbScratch );       /* T1 := T2 + T1 = XX + a*ZZZZ */
+    SymCryptModAdd( FMod, peT[2], peT[2], peT[2], pbScratch, cbScratch );       /* T2 := T2 + T2 = 2*XX */
+    SymCryptModAdd( FMod, peT[0], peT[0], peX3, pbScratch, cbScratch );         /* X3 := 2*S */
+    SymCryptModAdd( FMod, peT[2], peT[1], peT[1], pbScratch, cbScratch );       /* T1 := T2 + T1 = 3*XX + a*ZZZZ = M */
+
+    SymCryptModSquare( FMod, peT[1], peT[2], pbScratch, cbScratch );            /* T2 := M^2 */
+    SymCryptModSub( FMod, peT[2], peX3, peX3, pbScratch, cbScratch );           /* X3 := M^2 - 2*S = T */
+
+    SymCryptModSub( FMod, peT[0], peX3, peT[0], pbScratch, cbScratch );         /* T0 := S - T */
+    SymCryptModMul( FMod, peT[1], peT[0], peT[0], pbScratch, cbScratch );       /* T0 := M * (S - T) */
+    SymCryptModSub( FMod, peT[0], peY3, peY3, pbScratch, cbScratch );           /* Y3 := M * (S - T) - 8*YYYY */
+}
+
+
+//
+// based on dbl-2007-bl / dbl-2001-b formulae
+// but tweaked by saml to
+//   a) remove overeager conversions from modular multiplication to modular squaring which introduce
+//      more addition/subtraction. With current implementations (based on montgomery reduction),
+//      the cost of [a square and an add/sub] is greater than the cost of [a multiplication]
+//   b) share intermediate results of producing 8YYYY. [add/sub] is ~10% of cost of mul, so reducing
+//      count of these operation has a real impact
+//   c) make use of knowledge that curve has a == -3, so M can be calculated more efficiently
+//
+//      2Y = 2*Y1
+//      2YY = 2Y*Y1
+//      4YY = 2*2YY
+//      8YYYY = 2YY*4YY
+//      ZZ = Z1^2
+//      S = X1*4YY
+//      M = 3*(X1+ZZ)*(X1-ZZ) = 3*(XX - ZZZZ)
+//      T = M^2-2*S
+//      X3 = T
+//      Y3 = M*(S-T)-8YYYY
+//      Z3 = 2Y*Z1
+//
+// Total cost:
+//      6 Mul
+//      2 Sqr
+//      2 Add
+//      4 Sub
+//      4 Dbl
+//
+// Special Case:
+//      If the source point is equal to the identity
+//      point of the curve (i.e. Z1 = 0 in Jacobian
+//      coordinates) then the resulting point has
+//      Z3 = Z1*2Y1 = 0. Thus, this formula is
+//      complete (it works for all points).
+//
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassDoubleSpecializedAm3(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peT[3] = { 0 };    // Temporaries
+
+    PCSYMCRYPT_MODELEMENT peX1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+    PCSYMCRYPT_MODELEMENT peY1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+    PCSYMCRYPT_MODELEMENT peZ1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    PSYMCRYPT_MODELEMENT  peX3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT  peY3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT  peZ3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+
+    SYMCRYPT_ASSERT( pCurve->type == SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS_AM3 );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + 3 * pCurve->cbModElement );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Creating temporaries
+    for (UINT32 i=0; i<3; i++)
+    {
+        peT[i] = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+
+        SYMCRYPT_ASSERT( peT[i] != NULL);
+
+        pbScratch += pCurve->cbModElement;
+    }
+
+    // Fixing remaining scratch space size
+    cbScratch -= 3*pCurve->cbModElement;
+
+    // Calculate the points
+    SymCryptModAdd( FMod, peY1, peY1, peT[0], pbScratch, cbScratch );       /* T0 := Y1 + Y1 = 2Y */
+    SymCryptModSquare( FMod, peZ1, peT[1], pbScratch, cbScratch );          /* T1 := Z1 * Z1 = ZZ */
+    SymCryptModMul( FMod, peY1, peT[0], peY3, pbScratch, cbScratch );       /* Y3 := 2Y * Y1 = 2YY */
+
+    SymCryptModMul( FMod, peT[0], peZ1, peZ3, pbScratch, cbScratch );       /* Z3 := 2Y * Z1 = 2YZ */
+
+    SymCryptModAdd( FMod, peY3, peY3, peT[0], pbScratch, cbScratch );       /* T0 := 2YY + 2YY = 4YY */
+    SymCryptModAdd( FMod, peX1, peT[1], peT[2], pbScratch, cbScratch );     /* T2 := X1 + ZZ */
+    SymCryptModMul( FMod, peT[0], peY3, peY3, pbScratch, cbScratch );       /* Y3 := 2YY * 4YY = 8YYYY */
+
+    SymCryptModSub( FMod, peX1, peT[1], peT[1], pbScratch, cbScratch );     /* T1 := X1 - ZZ */
+    SymCryptModMul( FMod, peT[0], peX1, peT[0], pbScratch, cbScratch );     /* T0 := X1 * 4YY = 4XYY = S */
+
+    SymCryptModMul( FMod, peT[2], peT[1], peT[2], pbScratch, cbScratch );   /* T2 := (X1 + ZZ)*(X1 - ZZ) = XX - ZZZZ */
+    SymCryptModAdd( FMod, peT[2], peT[2], peT[1], pbScratch, cbScratch );   /* T1 := 2*(XX - ZZZZ) */
+    SymCryptModAdd( FMod, peT[0], peT[0], peX3, pbScratch, cbScratch );     /* X3 := 2*S */
+    SymCryptModAdd( FMod, peT[1], peT[2], peT[1], pbScratch, cbScratch );   /* T1 := 3*(XX - ZZZZ) = M */
+
+    SymCryptModSquare( FMod, peT[1], peT[2], pbScratch, cbScratch );        /* T2 := M^2 */
+    SymCryptModSub( FMod, peT[2], peX3, peX3, pbScratch, cbScratch );       /* X3 := M^2 - 2*S = T */
+
+    SymCryptModSub( FMod, peT[0], peX3, peT[0], pbScratch, cbScratch );     /* T0 := S - T */
+    SymCryptModMul( FMod, peT[1], peT[0], peT[0], pbScratch, cbScratch );   /* T0 := M * (S - T) */
+    SymCryptModSub( FMod, peT[0], peY3, peY3, pbScratch, cbScratch );       /* Y3 := M * (S - T) - 8*YYYY */
+}
+
+//
+// based on add-2007-bl formula
+// but tweaked by saml to
+//      remove overeager conversions from modular multiplication to modular squaring which introduce
+//      more addition/subtraction.
+//
+//      Z1Z1 = Z1^2
+//      Z2Z2 = Z2^2
+//      U1 = X1*Z2Z2
+//      U2 = X2*Z1Z1
+//      S1 = Y1*Z2*Z2Z2
+//      S2 = Y2*Z1*Z1Z1
+//      H = U2-U1
+//      2H = 2*H
+//      I = (2H)^2
+//      J = H*I
+//      r = 2*(S2-S1)
+//      V = U1*I
+//      X3 = r^2-J-2*V
+//      Y3 = r*(V-X3)-2*S1*J
+//      Z3 = (Z1*Z2)*2H
+//
+// Total cost:
+//     12 Mul
+//      4 Sqr
+//      0 Add
+//      7 Sub
+//      3 Dbl
+//
+// Special Case:
+//      If the two source points are opposite (X1 / Z1^2 == X2 / Z2^2),
+//      then H = U2-U1 = 0. Thus Z3 = 0 and the result is correct.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassAddDiffNonZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+
+    PCSYMCRYPT_MODELEMENT peX1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc1 );
+    PCSYMCRYPT_MODELEMENT peY1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc1 );
+    PCSYMCRYPT_MODELEMENT peZ1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc1 );
+
+    PCSYMCRYPT_MODELEMENT peX2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc2 );
+    PCSYMCRYPT_MODELEMENT peY2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc2 );
+    PCSYMCRYPT_MODELEMENT peZ2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc2 );
+
+    PSYMCRYPT_MODELEMENT  peX3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT  peY3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT  peZ3 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+
+    PSYMCRYPT_MODELEMENT peT[7] = { 0 };  // Temporaries
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + 7 * pCurve->cbModElement );
+
+    // Creating temporaries
+    for (UINT32 i=0; i<7; i++)
+    {
+        peT[i] = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+
+        SYMCRYPT_ASSERT( peT[i] != NULL);
+
+        pbScratch += pCurve->cbModElement;
+    }
+
+    // Fixing remaining scratch space size
+    cbScratch -= 7*pCurve->cbModElement;
+
+    // Calculation
+
+    SymCryptModSquare( FMod, peZ1, peT[0], pbScratch, cbScratch );          /* T0 := Z1 * Z1 = Z1Z1 */
+    SymCryptModMul( FMod, peZ1, peT[0], peT[1], pbScratch, cbScratch );     /* T1 := Z1*Z1Z1 */
+
+    SymCryptModSquare( FMod, peZ2, peT[6], pbScratch, cbScratch );          /* T6 := Z2 * Z2 = Z2Z2 */
+    SymCryptModMul( FMod, peX1, peT[6], peT[2], pbScratch, cbScratch );     /* T2 := X1 * T6 = X1*Z2Z2 = U1 */
+    SymCryptModMul( FMod, peX2, peT[0], peT[3], pbScratch, cbScratch );     /* T3 := X2 * Z1Z1 = U2 */
+    SymCryptModSub( FMod, peT[3], peT[2], peT[5], pbScratch, cbScratch );   /* T5 := T3 - T2 = U2 - U1 = H */
+    SymCryptModAdd( FMod, peT[5], peT[5], peT[3], pbScratch, cbScratch );   /* T3 := T5 + T5 = 2H */
+
+    SymCryptModMul( FMod, peZ1, peZ2, peT[4], pbScratch, cbScratch );       /* T4 := Z1 * Z2 */
+
+    SymCryptModMul( FMod, peZ2, peT[6], peT[6], pbScratch, cbScratch );     /* T6 := Z2 * T6 = Z2*Z2Z2 */
+    SymCryptModMul( FMod, peT[4], peT[3], peZ3, pbScratch, cbScratch );     /* Z3 := T4 * T3 = Z1*Z2*2H */
+
+    SymCryptModMul( FMod, peY1, peT[6], peT[6], pbScratch, cbScratch );     /* T6 := Y1 * T6 = Y1*Z2*Z2Z2 = S1 */
+    SymCryptModMul( FMod, peY2, peT[1], peT[4], pbScratch, cbScratch );     /* T4 := Y2*Z1*Z1Z1 = S2 */
+    SymCryptModSub( FMod, peT[4], peT[6], peT[4], pbScratch, cbScratch );   /* T4 := T4 - T6 = S2-S1 */
+    SymCryptModAdd( FMod, peT[4], peT[4], peT[4], pbScratch, cbScratch );   /* T4 := T4 + T4 = 2*(S2-S1) = r */
+
+    SymCryptModSquare( FMod, peT[3], peT[3], pbScratch, cbScratch );        /* T3 := T3 * T3 = (2*H)^2 = I */
+    SymCryptModMul( FMod, peT[3], peT[5], peT[5], pbScratch, cbScratch );   /* T5 := T3 * T5 = H*I = J */
+    SymCryptModMul( FMod, peT[2], peT[3], peT[3], pbScratch, cbScratch );   /* T3 := T2 * T3 = U1*I = V */
+
+    SymCryptModSquare( FMod, peT[4], peT[2], pbScratch, cbScratch );        /* T2 := T4 * T4 = r^2 */
+    SymCryptModSub( FMod, peT[2], peT[5], peT[2], pbScratch, cbScratch );   /* T2 := T2 - T5 = r^2 - J */
+    SymCryptModSub( FMod, peT[2], peT[3], peT[2], pbScratch, cbScratch );   /* T2 := T2 - T3 = r^2 - J - V */
+    SymCryptModSub( FMod, peT[2], peT[3], peX3, pbScratch, cbScratch );     /* T2 := T2 - T3 = r^2 - J - 2*V = X3 */
+
+    SymCryptModSub( FMod, peT[3], peX3, peT[3], pbScratch, cbScratch );     /* T3 := T3 - T2 = V - X3 */
+    SymCryptModMul( FMod, peT[3], peT[4], peT[3], pbScratch, cbScratch );   /* T3 := T3 * T4 = r*(V-X3) */
+    SymCryptModMul( FMod, peT[6], peT[5], peT[6], pbScratch, cbScratch );   /* T6 := T6 * T5 = S1*J */
+    SymCryptModAdd( FMod, peT[6], peT[6], peT[6], pbScratch, cbScratch );   /* T6 := T6 + T6 = 2*S1*J */
+    SymCryptModSub( FMod, peT[3], peT[6], peY3, pbScratch, cbScratch );     /* Y3 := T6 - T3 = r*(V-X3) - 2*S1*J */
+}
+
+//
+// The following function is a complete **SIDE-CHANNEL-UNSAFE**
+// addition of points that detects as fast as possible the special cases
+// and merges the two previous calls.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassAddSideChannelUnsafe(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+
+    PCSYMCRYPT_MODELEMENT peX1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc1 );
+    PCSYMCRYPT_MODELEMENT peY1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc1 );
+    PCSYMCRYPT_MODELEMENT peZ1 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc1 );
+
+    PCSYMCRYPT_MODELEMENT peX2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc2 );
+    PCSYMCRYPT_MODELEMENT peY2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc2 );
+    PCSYMCRYPT_MODELEMENT peZ2 = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc2 );
+
+    PSYMCRYPT_MODELEMENT peT[8] = { 0 };  // Temporaries
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + 8 * pCurve->cbModElement );
+
+    // Check if one of the points is zero
+    if (SymCryptModElementIsZero( FMod, peZ1 ))
+    {
+        SymCryptEcpointCopy( pCurve, poSrc2, poDst);
+        return;
+    }
+
+    if (SymCryptModElementIsZero( FMod, peZ2 ))
+    {
+        SymCryptEcpointCopy( pCurve, poSrc1, poDst);
+        return;
+    }
+
+    // Creating temporaries
+    for (UINT32 i=0; i<8; i++)
+    {
+        peT[i] = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+
+        SYMCRYPT_ASSERT( peT[i] != NULL);
+
+        pbScratch += pCurve->cbModElement;
+    }
+
+    // Fixing remaining scratch space size
+    cbScratch -= 8*pCurve->cbModElement;
+
+    // Calculation
+
+    SymCryptModSquare( FMod, peZ1, peT[0], pbScratch, cbScratch );          /* T0 := Z1 * Z1 = Z1Z1 */
+    SymCryptModMul( FMod, peZ1, peT[0], peT[1], pbScratch, cbScratch );     /* T1 := Z1*Z1Z1 */
+
+    SymCryptModSquare( FMod, peZ2, peT[6], pbScratch, cbScratch );          /* T6 := Z2 * Z2 = Z2Z2 */
+    SymCryptModMul( FMod, peX1, peT[6], peT[2], pbScratch, cbScratch );     /* T2 := X1 * T6 = X1*Z2Z2 = U1 */
+    SymCryptModMul( FMod, peX2, peT[0], peT[3], pbScratch, cbScratch );     /* T3 := X2 * Z1Z1 = U2 */
+    SymCryptModSub( FMod, peT[3], peT[2], peT[5], pbScratch, cbScratch );   /* T5 := T3 - T2 = U2 - U1 = H */
+
+    SymCryptModMul( FMod, peY2, peT[1], peT[7], pbScratch, cbScratch );     /* T7 := Y2 * T1 = Y2*Z1*Z1Z1 = S2 */
+    SymCryptModMul( FMod, peZ2, peT[6], peT[1], pbScratch, cbScratch );     /* T1 := Z2 * T6 = Z2*Z2Z2 */
+    SymCryptModMul( FMod, peY1, peT[1], peT[1], pbScratch, cbScratch );     /* T1 := Y1 * T1 = Y1*Z2*Z2Z2 = S1 */
+    SymCryptModSub( FMod, peT[7], peT[1], peT[7], pbScratch, cbScratch );   /* T7 := T7 - T1 = S2-S1 */
+
+    if (SymCryptModElementIsZero( FMod, peT[5] ) & SymCryptModElementIsZero( FMod, peT[7] ))
+    {
+        // Points are equal - run double on poSrc1
+
+        SymCryptModElementCopy( FMod, peT[0], peT[4] );                             /* Move Z1Z1 for later */
+
+        SymCryptModSquare( FMod, peX1, peT[0], pbScratch, cbScratch );              /* T0 := X1 * X1 = XX */
+        SymCryptModSquare( FMod, peY1, peT[3], pbScratch, cbScratch );              /* T3 := Y1 * Y1 = YY */
+        SymCryptModSquare( FMod, peT[3], peT[5], pbScratch, cbScratch );            /* T5 := T3 * T3 = YYYY */
+
+        SymCryptModAdd( FMod, peX1, peT[3], peT[1], pbScratch, cbScratch );         /* T1 := X1 + T3 = X + YY */
+        SymCryptModSquare( FMod, peT[1], peT[1], pbScratch, cbScratch );            /* T1 := T1 * T1 = (X + YY)^2 */
+        SymCryptModSub( FMod, peT[1], peT[0], peT[1], pbScratch, cbScratch );       /* T1 := T1 - T0 = (X + YY)^2 - XX */
+        SymCryptModSub( FMod, peT[1], peT[5], peT[1], pbScratch, cbScratch );       /* T1 := T1 - T5 = (X + YY)^2 - XX - YYYY */
+        SymCryptModAdd( FMod, peT[1], peT[1], peT[1], pbScratch, cbScratch );       /* T1 := T1 + T1 = 2*((X + YY)^2 - XX - YYYY) = S */
+
+        //SymCryptModSquare( FMod, peZ1, peT[4], pbScratch, cbScratch );              /* T4 := Z1 * Z1 = ZZ */
+
+        SymCryptModSquare( FMod, peT[4], peT[2], pbScratch, cbScratch );            /* T2 := T4 * T4 = ZZ^2 */
+        SymCryptModMul( FMod, peT[2], pCurve->A, peT[2], pbScratch, cbScratch );    /* T2 := T2 * a  = a*ZZ^2 */
+        SymCryptModAdd( FMod, peT[2], peT[0], peT[2], pbScratch, cbScratch );       /* T2 := T2 + T0 = XX + a*ZZ^2 */
+        SymCryptModAdd( FMod, peT[0], peT[0], peT[0], pbScratch, cbScratch );       /* T0 := T0 + T0 = 2*XX */
+        SymCryptModAdd( FMod, peT[2], peT[0], peT[2], pbScratch, cbScratch );       /* T2 := T2 + T0 = 3*XX + a*ZZ^2 = M */
+
+        SymCryptModSquare( FMod, peT[2], peT[0], pbScratch, cbScratch );            /* T0 := T2 * T2 = M^2 */
+        SymCryptModSub( FMod, peT[0], peT[1], peT[0], pbScratch, cbScratch );       /* T0 := T0 - T1 = M^2 - S */
+        SymCryptModSub( FMod, peT[0], peT[1], peT[0], pbScratch, cbScratch );       /* T0 := T0 - T1 = M^2 - 2*S = T = X3 */
+
+        SymCryptModSub( FMod, peT[1], peT[0], peT[1], pbScratch, cbScratch );       /* T1 := T1 - T0 = S - T */
+        SymCryptModMul( FMod, peT[2], peT[1], peT[1], pbScratch, cbScratch );       /* T1 := T2 * T1 = M * (S - T) */
+        SymCryptModAdd( FMod, peT[5], peT[5], peT[5], pbScratch, cbScratch );       /* T5 := T5 + T5 = 2*YYYY */
+        SymCryptModAdd( FMod, peT[5], peT[5], peT[5], pbScratch, cbScratch );       /* T5 := T5 + T5 = 4*YYYY */
+        SymCryptModAdd( FMod, peT[5], peT[5], peT[5], pbScratch, cbScratch );       /* T5 := T5 + T5 = 8*YYYY */
+        SymCryptModSub( FMod, peT[1], peT[5], peT[1], pbScratch, cbScratch );       /* T1 := T1 - T5 = M * (S - T) - 8*YYYY = Y3 */
+
+        SymCryptModAdd( FMod, peY1, peZ1, peT[2], pbScratch, cbScratch );           /* T2 := Y1 + Z1 */
+        SymCryptModSquare( FMod, peT[2], peT[2], pbScratch, cbScratch );            /* T2 := T2 * T2 = (Y + Z )^2 */
+        SymCryptModSub( FMod, peT[2], peT[3], peT[2], pbScratch, cbScratch );       /* T2 := T2 - T3 = (Y + Z )^2 - YY */
+        SymCryptModSub( FMod, peT[2], peT[4], peT[2], pbScratch, cbScratch );       /* T2 := T2 - T4 = (Y + Z )^2 - YY - ZZ = Z3 */
+
+        // Setting the result
+        SymCryptModElementCopy( FMod, peT[0], SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst ) );
+        SymCryptModElementCopy( FMod, peT[1], SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst ) );
+        SymCryptModElementCopy( FMod, peT[2], SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst ) );
+    }
+    else
+    {
+        // Continue the addition
+
+        SymCryptModAdd( FMod, peZ1, peZ2, peT[4], pbScratch, cbScratch );       /* T4 := Z1 + Z2 */
+        SymCryptModSquare( FMod, peT[4], peT[4], pbScratch, cbScratch );        /* T4 := T4 * T4 = (Z1 + Z2)^2 */
+        SymCryptModSub( FMod, peT[4], peT[0], peT[4], pbScratch, cbScratch );   /* T4 := T4 - Z1Z1 = (Z1 + Z2)^2 - Z1Z1 */
+        SymCryptModSub( FMod, peT[4], peT[6], peT[4], pbScratch, cbScratch );   /* T4 := T4 - T6 = (Z1 + Z2)^2 - Z1Z1 - Z2Z2 */
+        SymCryptModMul( FMod, peT[4], peT[5], peT[4], pbScratch, cbScratch );   /* T4 := T4 * T5 = ((Z1 + Z2)^2 - Z1Z1 - Z2Z2)*H = Z3 */
+
+        SymCryptModAdd( FMod, peT[7], peT[7], peT[7], pbScratch, cbScratch );   /* T7 := T7 + T7 = 2*(S2-S1) = r */
+
+        SymCryptModAdd( FMod, peT[5], peT[5], peT[3], pbScratch, cbScratch );   /* T3 := T5 + T5 = 2*H */
+        SymCryptModSquare( FMod, peT[3], peT[3], pbScratch, cbScratch );        /* T3 := T3 * T3 = (2*H)^2 = I */
+        SymCryptModMul( FMod, peT[3], peT[5], peT[5], pbScratch, cbScratch );   /* T5 := T3 * T5 = H*I = J */
+        SymCryptModMul( FMod, peT[2], peT[3], peT[3], pbScratch, cbScratch );   /* T3 := T2 * T3 = U1*I = V */
+
+        SymCryptModSquare( FMod, peT[7], peT[2], pbScratch, cbScratch );        /* T2 := T7 * T7 = r^2 */
+        SymCryptModSub( FMod, peT[2], peT[5], peT[2], pbScratch, cbScratch );   /* T2 := T2 - T5 = r^2 - J */
+        SymCryptModSub( FMod, peT[2], peT[3], peT[2], pbScratch, cbScratch );   /* T2 := T2 - T3 = r^2 - J - V */
+        SymCryptModSub( FMod, peT[2], peT[3], peT[2], pbScratch, cbScratch );   /* T2 := T2 - T3 = r^2 - J - 2*V = X3 */
+
+        SymCryptModSub( FMod, peT[3], peT[2], peT[3], pbScratch, cbScratch );   /* T3 := T3 - T2 = V - X3 */
+        SymCryptModMul( FMod, peT[3], peT[7], peT[3], pbScratch, cbScratch );   /* T3 := T3 * T7 = r*(V-X3) */
+        SymCryptModMul( FMod, peT[1], peT[5], peT[6], pbScratch, cbScratch );   /* T6 := T1 * T5 = S1*J */
+        SymCryptModAdd( FMod, peT[6], peT[6], peT[6], pbScratch, cbScratch );   /* T6 := T6 + T6 = 2*S1*J */
+        SymCryptModSub( FMod, peT[3], peT[6], peT[3], pbScratch, cbScratch );   /* T3 := T6 - T3 = r*(V-X3) - 2*S1*J = Y3 */
+
+        // Setting the result
+        SymCryptModElementCopy( FMod, peT[2], SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst ) );
+        SymCryptModElementCopy( FMod, peT[3], SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst ) );
+        SymCryptModElementCopy( FMod, peT[4], SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst ) );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassAdd(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    UINT32 dSrc1Zero = 0;
+    UINT32 dSrc2Zero = 0;
+    UINT32 dSrcEqual = 0;
+
+    // Temporary points
+    PSYMCRYPT_ECPOINT   poQ0 = NULL;
+    PSYMCRYPT_ECPOINT   poQ1 = NULL;
+
+    SIZE_T cbEcpoint = SymCryptSizeofEcpointFromCurve( pCurve );
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );     // We will need the entire scratch space
+
+    SYMCRYPT_ASSERT( cbScratch > 2*cbEcpoint );
+
+    if ((flags & SYMCRYPT_FLAG_DATA_PUBLIC) != 0)
+    {
+        SymCryptShortWeierstrassAddSideChannelUnsafe( pCurve, poSrc1, poSrc2, poDst, pbScratch, cbScratch );
+    }
+    else
+    {
+        // Creating temporary points
+        poQ0 = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+        SYMCRYPT_ASSERT( poQ0 != NULL);
+        pbScratch += cbEcpoint;
+
+        poQ1 = SymCryptEcpointCreate( pbScratch, cbEcpoint, pCurve );
+        SYMCRYPT_ASSERT( poQ1 != NULL);
+        pbScratch += cbEcpoint;
+
+        // Fixing remaining scratch space size
+        cbScratch -= 2*cbEcpoint;
+
+        // Calculate the masks
+        dSrc1Zero = SymCryptShortWeierstrassIsZero( pCurve, poSrc1, pbScratch, cbScratch );
+        dSrc2Zero = SymCryptShortWeierstrassIsZero( pCurve, poSrc2, pbScratch, cbScratch );
+        dSrcEqual = SymCryptShortWeierstrassIsEqual( pCurve, poSrc1, poSrc2, SYMCRYPT_FLAG_ECPOINT_EQUAL, pbScratch, cbScratch );
+
+        // Side-channel safe computations
+        SymCryptShortWeierstrassAddDiffNonZero( pCurve, poSrc1, poSrc2, poQ0, pbScratch, cbScratch );   // This covers the cases where Src1 != Src2 or Src1 = -Src2
+
+        SymCryptEcpointDouble( pCurve, poSrc1, poQ1, 0, pbScratch, cbScratch );     // Dispatch to Double function; enables type assertion on SymCryptShortWeierstrassDouble to be specific
+        SymCryptEcpointMaskedCopy( pCurve, poQ1, poQ0, dSrcEqual );                                     // (Masked) copy if the points are equal
+
+        SymCryptEcpointMaskedCopy( pCurve, poSrc1, poQ0, dSrc2Zero );                                   // (Masked) copy if Src2 = 0
+        SymCryptEcpointMaskedCopy( pCurve, poSrc2, poQ0, dSrc1Zero );                                   // (Masked) copy if Src1 = 0
+
+        SymCryptEcpointCopy( pCurve, poQ0, poDst );                                 // Copy the final result to destination
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassNegate(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc);
+
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_SHORT_WEIERSTRASS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + pCurve->cbModElement);
+
+    peTmp = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+    SYMCRYPT_ASSERT( peTmp != NULL);
+
+    pbScratch += pCurve->cbModElement;
+    cbScratch -= pCurve->cbModElement;
+
+    SymCryptModNeg( FMod, peY, peTmp, pbScratch, cbScratch );
+    SymCryptModElementMaskedCopy( FMod, peTmp, peY, mask );
+}
diff --git a/libs/symcrypt/lib/ec_twisted_edwards.c b/libs/symcrypt/lib/ec_twisted_edwards.c
new file mode 100644
index 000000000000..9d4371c40d08
--- /dev/null
+++ b/libs/symcrypt/lib/ec_twisted_edwards.c
@@ -0,0 +1,575 @@
+//
+// ec_twisted_edwards.c   Twisted Edwards Curve Implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsFillScratchSpaces( _In_ PSYMCRYPT_ECURVE pCurve )
+{
+    UINT32 nDigits = SymCryptDigitsFromBits( pCurve->FModBitsize );
+    UINT32 cbModElement = pCurve->cbModElement;
+    UINT32 nDigitsFieldLength = pCurve->FModDigits;
+
+    //
+    // All the scratch space computations are upper bounded by the SizeofXXX bound (2^19) and
+    // the SCRATCH_BYTES_FOR_XXX bound (2^24) (see symcrypt_internal.h).
+    //
+    // One caveat is SymCryptSizeofEcpointFromCurve and SymCryptSizeofEcpointEx which calculate
+    // the size of EcPoint with 4 coordinates (each one a modelement of max size 2^17). Thus upper
+    // bounded by 2^20.
+    //
+    // Another is the precomp points computation where the nPrecompPoints are up to
+    // 2^SYMCRYPT_ECURVE_SW_DEF_WINDOW = 2^6 and the nRecodedDigits are equal to the
+    // GOrd bitsize < 2^20.
+    //
+    // Thus cbScratchScalarMulti is upper bounded by 2^6*2^20 + 2*2^20*2^4 ~ 2^26.
+    //
+
+    pCurve->cbScratchCommon = 8 * cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits );
+
+    pCurve->cbScratchScalar =
+            (pCurve->cbModElement) +
+            2 * SymCryptSizeofEcpointFromCurve( pCurve ) +
+            2 * SymCryptSizeofIntFromDigits( pCurve->GOrdDigits ) +
+            SYMCRYPT_MAX( pCurve->cbScratchCommon, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->GOrdDigits ));
+
+    pCurve->cbScratchScalarMulti =
+            pCurve->info.sw.nPrecompPoints * SymCryptSizeofEcpointFromCurve( pCurve ) +
+            ((2*pCurve->info.sw.nRecodedDigits * sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1 )/SYMCRYPT_ASYM_ALIGN_VALUE) * SYMCRYPT_ASYM_ALIGN_VALUE;
+
+    pCurve->cbScratchGetSetValue =
+        SymCryptSizeofEcpointEx(cbModElement, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH) +
+        2 * cbModElement +
+        SYMCRYPT_MAX(SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nDigitsFieldLength),
+        SYMCRYPT_SCRATCH_BYTES_FOR_MODINV(nDigitsFieldLength));
+
+    pCurve->cbScratchGetSetValue = SYMCRYPT_MAX( pCurve->cbScratchGetSetValue, SymCryptSizeofIntFromDigits( nDigits ) );
+
+    pCurve->cbScratchEckey =
+        SYMCRYPT_MAX( pCurve->cbModElement + SymCryptSizeofIntFromDigits(SymCryptEcurveDigitsofScalarMultiplier(pCurve)),
+                SymCryptSizeofEcpointFromCurve( pCurve ) ) +
+        SYMCRYPT_MAX( pCurve->cbScratchScalar + pCurve->cbScratchScalarMulti, pCurve->cbScratchGetSetValue );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsSetDistinguished(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptEcpointCopy( pCurve, pCurve->G, poDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch)
+{
+    PSYMCRYPT_MODULUS     pmMod = pCurve->FMod;
+    UINT32 dResX = 0, dResY = 0;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    PSYMCRYPT_MODELEMENT peSrcX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+    PSYMCRYPT_MODELEMENT peSrcY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+    PSYMCRYPT_MODELEMENT peSrcZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    dResX = SymCryptModElementIsZero( pmMod, peSrcX );
+    dResY = SymCryptModElementIsEqual( pmMod, peSrcY, peSrcZ );
+
+    return ( dResX & dResY );
+}
+
+//
+// Verify that
+//   a * x^2 + y^2 = 1 + d * x^2 * y^2
+//   x = X/Z, y = Y/Z,
+// To avoid mod inv calculation which is expensive,
+// we verify Z^2(aX^2 + Y^2) = Z^4 + d * X^2 * Y^2
+//
+UINT32
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsOnCurve(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch)
+{
+    PSYMCRYPT_MODELEMENT  peTemp[4];
+    PSYMCRYPT_MODULUS     pmMod = pCurve->FMod;
+    SIZE_T nBytes;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+
+    SYMCRYPT_ASSERT( cbScratch >= 4*nBytes );
+
+    for (UINT32 i = 0; i < 4; ++i)
+    {
+       peTemp[i] = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+       pbScratch += nBytes;
+       cbScratch -= nBytes;
+    }
+
+    PSYMCRYPT_MODELEMENT peSrcX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+    PSYMCRYPT_MODELEMENT peSrcY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+    PSYMCRYPT_MODELEMENT peSrcZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    // peTemp[0] = X^2
+    SymCryptModSquare( pmMod, peSrcX, peTemp[0], pbScratch, cbScratch);
+
+    // peTemp[1] = Y^2
+    SymCryptModSquare( pmMod, peSrcY, peTemp[1], pbScratch, cbScratch);
+
+    // peTemp[2] = Z^2
+    SymCryptModSquare( pmMod, peSrcZ, peTemp[2], pbScratch, cbScratch);
+
+    // peTemp[3] = a * X^2
+    SymCryptModMul( pmMod, pCurve->A, peTemp[0], peTemp[3], pbScratch, cbScratch );
+
+    // peTemp[3] = a * X^2 + Y^2
+    SymCryptModAdd( pmMod, peTemp[3], peTemp[1], peTemp[3], pbScratch, cbScratch );
+
+    // peTemp[3] = Z^2 (a * X^2 + Y^2)
+    SymCryptModMul( pmMod, peTemp[3], peTemp[2], peTemp[3], pbScratch, cbScratch );
+
+    // peTemp[1] = X^2 * Y^2
+    SymCryptModMul( pmMod, peTemp[0], peTemp[1], peTemp[1], pbScratch, cbScratch );
+
+    // peTemp[1] = d * X^2 *Y^2
+    SymCryptModMul( pmMod, pCurve->B, peTemp[1], peTemp[1], pbScratch, cbScratch );
+
+    // peTemp[2] = Z^4
+    SymCryptModMul( pmMod, peTemp[2], peTemp[2], peTemp[2], pbScratch, cbScratch );
+
+    // peTemp[1] = Z^4 + d * X^2 * Y^2
+    SymCryptModAdd( pmMod, peTemp[2], peTemp[1], peTemp[1], pbScratch, cbScratch );
+
+    return SymCryptModElementIsEqual( pmMod, peTemp[1], peTemp[3] );
+}
+
+//
+//  Point doubling: dbl-2008-hwcd, 5Mul + 4Square + 2Add + 5Sub
+//
+//  poDst (X, Y, Z, T) = 2 * poSrc(X, Y, Z, T)
+//  1.  A = X1 ^ 2
+//  2.  B = Y1 ^ 2
+//  3.  C = 2 * Z1 ^ 2
+//  4.  D = a * A
+//  5.  E = (X1 + Y1) ^ 2 - A - B
+//  6.  G = D + B
+//  7.  F = G - C
+//  8.  H = D - B
+//  9.  X3 = E * F
+//  10. Y3 = G * H
+//  11. T3 = E * H
+//  12. Z3 = F * G
+//
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsDouble(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch)
+{
+    PSYMCRYPT_MODELEMENT  peTemp[8];
+    PSYMCRYPT_MODULUS     pmMod = pCurve->FMod;
+    SIZE_T nBytes;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+
+    SYMCRYPT_ASSERT( cbScratch >= 8*nBytes );
+
+    for (UINT32 i = 0; i < 8; ++i)
+    {
+       peTemp[i] = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+       pbScratch += nBytes;
+       cbScratch -= nBytes;
+    }
+
+    PSYMCRYPT_MODELEMENT peSrcX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+    PSYMCRYPT_MODELEMENT peSrcY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+    PSYMCRYPT_MODELEMENT peSrcZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+
+    PSYMCRYPT_MODELEMENT peDstX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstT = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3, pCurve, poDst );
+
+    PSYMCRYPT_MODELEMENT peA = peTemp[0];
+    PSYMCRYPT_MODELEMENT peB = peTemp[1];
+    PSYMCRYPT_MODELEMENT peC = peTemp[2];
+    PSYMCRYPT_MODELEMENT peD = peTemp[3];
+    PSYMCRYPT_MODELEMENT peE = peTemp[4];
+    PSYMCRYPT_MODELEMENT peF = peTemp[5];
+    PSYMCRYPT_MODELEMENT peG = peTemp[6];
+    PSYMCRYPT_MODELEMENT peH = peTemp[7];
+
+
+    // A = X1^2
+    SymCryptModSquare( pmMod, peSrcX, peA, pbScratch, cbScratch );
+
+    // B = Y1^2
+    SymCryptModSquare( pmMod, peSrcY, peB, pbScratch, cbScratch );
+
+    // C1 = Z1^2
+    SymCryptModSquare( pmMod, peSrcZ, peC, pbScratch, cbScratch );
+
+    // C = C1 + C1 = Z1^2 + Z1^2 = 2 * Z1^2
+    SymCryptModAdd( pmMod, peC, peC, peC, pbScratch, cbScratch );
+
+    // D = a * A
+    SymCryptModMul( pmMod, pCurve->A, peA, peD, pbScratch, cbScratch );
+
+    // E1 = X1 + Y1
+    SymCryptModAdd( pmMod, peSrcX, peSrcY, peE, pbScratch, cbScratch );
+
+    // E2 = E1^2 = (X1 + Y1)^2
+    SymCryptModSquare( pmMod, peE, peE, pbScratch, cbScratch );
+
+    // E3 = E2 - A = (X1 + Y1)^2 - A
+    SymCryptModSub( pmMod, peE, peA, peE, pbScratch, cbScratch );
+
+    // E = E3 - B = (X1 + Y1)^2 - A - B
+    SymCryptModSub( pmMod, peE, peB, peE, pbScratch, cbScratch );
+
+    // G = D + B
+    SymCryptModAdd( pmMod, peD, peB, peG, pbScratch, cbScratch );
+
+    // F = G - C
+    SymCryptModSub( pmMod, peG, peC, peF, pbScratch, cbScratch );
+
+    // H = D - B
+    SymCryptModSub( pmMod, peD, peB, peH, pbScratch, cbScratch );
+
+    // X3 = E * F
+    SymCryptModMul( pmMod, peE, peF, peDstX, pbScratch, cbScratch );
+
+    // Y3 = G * H
+    SymCryptModMul( pmMod, peG, peH, peDstY, pbScratch, cbScratch );
+
+    // T3 = E * H
+    SymCryptModMul( pmMod, peE, peH, peDstT, pbScratch, cbScratch );
+
+    // Z3 = F * G
+    SymCryptModMul( pmMod, peF, peG, peDstZ, pbScratch, cbScratch );
+}
+
+
+//
+//  Point addition: add-2008-hwcd  11Mul + 3add + 4sub
+//
+//  poDst(X, Y, Z, T) = poSrc(X, Y, Z, T) + poSrc2(X, Y, Z, T)
+//  1.  A = X1 * X2
+//  2.  B = Y1 * Y2
+//  3.  C = d * T1 * T2
+//  4.  D = Z1 * Z2
+//  5.  E = (X1 + Y1) * (X2 + Y2) - A - B
+//  6.  F = D - C
+//  7.  G = D + C
+//  8.  H = B - a * A
+//  9.  X3 = E * F
+//  10. Y3 = G * H
+//  11. T3 = E * H
+//  12. Z3 = F * G
+//
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsAdd(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PSYMCRYPT_MODELEMENT  peTemp[8];
+    PSYMCRYPT_MODULUS     pmMod = pCurve->FMod;
+    SIZE_T nBytes;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+
+    SYMCRYPT_ASSERT( cbScratch >= 8*nBytes );
+
+    for (UINT32 i = 0; i < 8; ++i)
+    {
+       peTemp[i] = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+       pbScratch += nBytes;
+       cbScratch -= nBytes;
+    }
+
+    PSYMCRYPT_MODELEMENT peSrc1X = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc1 );
+    PSYMCRYPT_MODELEMENT peSrc1Y = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc1 );
+    PSYMCRYPT_MODELEMENT peSrc1Z = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc1 );
+    PSYMCRYPT_MODELEMENT peSrc1T = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3, pCurve, poSrc1 );
+
+    PSYMCRYPT_MODELEMENT peSrc2X = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc2 );
+    PSYMCRYPT_MODELEMENT peSrc2Y = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc2 );
+    PSYMCRYPT_MODELEMENT peSrc2Z = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc2 );
+    PSYMCRYPT_MODELEMENT peSrc2T = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3, pCurve, poSrc2 );
+
+    PSYMCRYPT_MODELEMENT peDstX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstT = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3, pCurve, poDst );
+
+    PSYMCRYPT_MODELEMENT peA = peTemp[0];
+    PSYMCRYPT_MODELEMENT peB = peTemp[1];
+    PSYMCRYPT_MODELEMENT peC = peTemp[2];
+    PSYMCRYPT_MODELEMENT peD = peTemp[3];
+    PSYMCRYPT_MODELEMENT peE = peTemp[4];
+    PSYMCRYPT_MODELEMENT peF = peTemp[5];
+    PSYMCRYPT_MODELEMENT peG = peTemp[6];
+    PSYMCRYPT_MODELEMENT peH = peTemp[7];
+
+    // A = X1 * X2
+    SymCryptModMul( pmMod, peSrc1X, peSrc2X, peA, pbScratch, cbScratch );
+
+    // B = Y1 * Y2
+    SymCryptModMul( pmMod, peSrc1Y, peSrc2Y, peB, pbScratch, cbScratch );
+
+    // C1 = T1 * T2
+    SymCryptModMul( pmMod, peSrc1T, peSrc2T, peC, pbScratch, cbScratch );
+
+    // C = d * C1 = d * T1 * T2
+    SymCryptModMul( pmMod, pCurve->B, peC, peC, pbScratch, cbScratch );
+
+    // D = Z1 * Z2
+    SymCryptModMul( pmMod, peSrc1Z, peSrc2Z, peD, pbScratch, cbScratch );
+
+    // E1 = X1 + Y1
+    SymCryptModAdd( pmMod, peSrc1X, peSrc1Y, peE, pbScratch, cbScratch );
+
+    // E2 = X2 + Y2
+    SymCryptModAdd( pmMod, peSrc2X, peSrc2Y, peF, pbScratch, cbScratch );
+
+    // E = E * F
+    SymCryptModMul( pmMod, peE, peF, peE, pbScratch, cbScratch );
+
+    // E = E - A
+    SymCryptModSub( pmMod, peE, peA, peE, pbScratch, cbScratch );
+
+    // E = E - B
+    SymCryptModSub( pmMod, peE, peB, peE, pbScratch, cbScratch );
+
+    // F = D - C
+    SymCryptModSub( pmMod, peD, peC, peF, pbScratch, cbScratch );
+
+    // G = D + C
+    SymCryptModAdd( pmMod, peD, peC, peG, pbScratch, cbScratch );
+
+    // H = a * A
+    SymCryptModMul( pmMod, pCurve->A, peA, peH, pbScratch, cbScratch );
+
+    // H = B - a * A
+    SymCryptModSub( pmMod, peB, peH, peH, pbScratch, cbScratch );
+
+    // X3 = E * F
+    SymCryptModMul( pmMod, peE, peF, peDstX, pbScratch, cbScratch );
+
+    // Y3 = G * H
+    SymCryptModMul( pmMod, peG, peH, peDstY, pbScratch, cbScratch );
+
+    // T3 = E * H
+    SymCryptModMul( pmMod, peE, peH, peDstT, pbScratch, cbScratch );
+
+    // Y3 = F * G
+    SymCryptModMul( pmMod, peF, peG, peDstZ, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsAddDiffNonZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_(cbScratch)
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    SymCryptTwistedEdwardsAdd( pCurve, poSrc1, poSrc2, poDst, 0, pbScratch, cbScratch );
+}
+
+//
+//  Verify poSrc1(X1, Y1, Z1, T1) = poSrc2(X2, Y2, Z2, T2)
+//  To avoid ModInv for 1/Z, we do
+//     X1 * Z2 = X2 * Z1, and
+//     Y1 * Z2 = Y2 * Z1
+//
+//  This function also do poSrc1 = -1 * poSrc check as flags indicates
+//
+UINT32
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_(cbScratch)
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch)
+{
+    PSYMCRYPT_MODELEMENT  peTemp[2];
+    PSYMCRYPT_MODELEMENT  peSrc1X, peSrc1Y, peSrc1Z;
+    PSYMCRYPT_MODELEMENT  peSrc2X, peSrc2Y, peSrc2Z;
+    PSYMCRYPT_MODULUS     pmMod = pCurve->FMod;
+    SIZE_T nBytes;
+    UINT32  dResX = 0;
+    UINT32  dResXN = 0;
+    UINT32  dResY = 0;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc1->pCurve) && SymCryptEcurveIsSame(pCurve, poSrc2->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+
+    SYMCRYPT_ASSERT( cbScratch >= 2*nBytes );
+
+    for (UINT32 i = 0; i < 2; ++i)
+    {
+        peTemp[i] = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+        pbScratch += nBytes;
+        cbScratch -= nBytes;
+    }
+
+    peSrc1X = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc1 );
+    peSrc1Y = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc1 );
+    peSrc1Z = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc1 );
+
+    peSrc2X = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc2 );
+    peSrc2Y = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc2 );
+    peSrc2Z = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc2 );
+
+    // Setting the default flag if flags == 0
+    flags |= (SYMCRYPT_MASK32_ZERO(flags) & SYMCRYPT_FLAG_ECPOINT_EQUAL);
+
+    // peTemp[0] = X1 * Z2
+    SymCryptModMul( pmMod, peSrc1X, peSrc2Z, peTemp[0], pbScratch, cbScratch );
+
+    // peTemp[1] = X2 * Z1
+    SymCryptModMul( pmMod, peSrc2X, peSrc1Z, peTemp[1], pbScratch, cbScratch );
+
+    dResX = SymCryptModElementIsEqual( pmMod, peTemp[0], peTemp[1] );
+
+    // Neg peTemp[1]
+    SymCryptModNeg(pmMod, peTemp[1], peTemp[1], pbScratch, cbScratch);
+    dResXN = SymCryptModElementIsEqual(pmMod, peTemp[0], peTemp[1]);
+
+    // peTemp[0] = Y1 * Z2
+    SymCryptModMul( pmMod, peSrc1Y, peSrc2Z, peTemp[0], pbScratch, cbScratch );
+
+    // peTemp[1] = Y2 * Z1
+    SymCryptModMul( pmMod, peSrc2Y, peSrc1Z, peTemp[1], pbScratch, cbScratch );
+
+    dResY = SymCryptModElementIsEqual( pmMod, peTemp[0], peTemp[1] );
+
+    return (SYMCRYPT_MASK32_NONZERO( flags & SYMCRYPT_FLAG_ECPOINT_EQUAL ) & dResX & dResY ) |
+           (SYMCRYPT_MASK32_NONZERO( flags & SYMCRYPT_FLAG_ECPOINT_NEG_EQUAL ) & dResXN & dResY );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsSetZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_(cbScratch)
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch)
+{
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_COMMON_ECURVE_OPERATIONS( pCurve ) );
+
+    PSYMCRYPT_MODULUS  pmMod = pCurve->FMod;
+
+    PSYMCRYPT_MODELEMENT peDstX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstZ = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+    PSYMCRYPT_MODELEMENT peDstT = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3, pCurve, poDst );
+
+    SymCryptModElementSetValueUint32( 0, pmMod, peDstX, pbScratch, cbScratch );
+    SymCryptModElementSetValueUint32( 1, pmMod, peDstY, pbScratch, cbScratch );
+    SymCryptModElementSetValueUint32( 1, pmMod, peDstZ, pbScratch, cbScratch );
+    SymCryptModElementSetValueUint32( 0, pmMod, peDstT, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsNegate(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch )
+{
+    PCSYMCRYPT_MODULUS FMod = pCurve->FMod;
+    PSYMCRYPT_MODELEMENT peX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0,pCurve, poSrc);
+    PSYMCRYPT_MODELEMENT peT = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3,pCurve, poSrc);
+
+    PSYMCRYPT_MODELEMENT peTmp = NULL;
+
+    SYMCRYPT_ASSERT( SYMCRYPT_CURVE_IS_TWISTED_EDWARDS_TYPE(pCurve) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ) + pCurve->cbModElement );
+
+    peTmp = SymCryptModElementCreate(
+                pbScratch,
+                pCurve->cbModElement,
+                FMod );
+    SYMCRYPT_ASSERT( peTmp != NULL);
+
+    pbScratch += pCurve->cbModElement;
+    cbScratch -= pCurve->cbModElement;
+
+    SymCryptModNeg( FMod, peX, peTmp, pbScratch, cbScratch );
+    SymCryptModElementMaskedCopy( FMod, peTmp, peX, mask );
+
+    SymCryptModNeg( FMod, peT, peTmp, pbScratch, cbScratch );
+    SymCryptModElementMaskedCopy( FMod, peTmp, peT, mask );
+}
diff --git a/libs/symcrypt/lib/eckey.c b/libs/symcrypt/lib/eckey.c
new file mode 100644
index 000000000000..0e05518614e1
--- /dev/null
+++ b/libs/symcrypt/lib/eckey.c
@@ -0,0 +1,996 @@
+//
+// eckey.c   Functions for the ECKEY object
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+PSYMCRYPT_ECKEY
+SYMCRYPT_CALL
+SymCryptEckeyAllocate( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    PVOID               p;
+    SIZE_T              cb;
+    PSYMCRYPT_ECKEY     res = NULL;
+
+    cb = SymCryptSizeofEckeyFromCurve( pCurve );
+
+    p = SymCryptCallbackAlloc( cb );
+
+    if ( p==NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptEckeyCreate( p, cb, pCurve );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEckeyFree( _Out_ PSYMCRYPT_ECKEY pkObj )
+{
+    SYMCRYPT_CHECK_MAGIC( pkObj );
+    SymCryptEckeyWipe( pkObj );
+    SymCryptCallbackFree( pkObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofEckeyFromCurve( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - SymCryptSizeofEcpointFromCurve outputs the size of up to 4 modelements + some overhead
+    // Thus the following calculation does not overflow the result.
+    //
+    return sizeof(SYMCRYPT_ECKEY) + SymCryptSizeofEcpointFromCurve( pCurve ) + SymCryptSizeofIntFromDigits(SymCryptEcurveDigitsofScalarMultiplier(pCurve));
+}
+
+PSYMCRYPT_ECKEY
+SYMCRYPT_CALL
+SymCryptEckeyCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    PCSYMCRYPT_ECURVE   pCurve )
+{
+    PSYMCRYPT_ECKEY         pkObj = NULL;
+    UINT32 privateKeyDigits = SymCryptEcurveDigitsofScalarMultiplier(pCurve);
+
+    SIZE_T cbPublicKey = SymCryptSizeofEcpointFromCurve( pCurve );
+    SIZE_T cbPrivateKey = SymCryptSizeofIntFromDigits( privateKeyDigits );
+
+    UNREFERENCED_PARAMETER( cbBuffer );     // only referenced in ASSERTs...
+
+    SYMCRYPT_ASSERT( pCurve != NULL );
+    SYMCRYPT_ASSERT( cbBuffer >= SymCryptSizeofEckeyFromCurve( pCurve ) );
+
+    SYMCRYPT_ASSERT( cbBuffer >= sizeof(SYMCRYPT_ECKEY) +
+                            cbPublicKey +
+                            cbPrivateKey );
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    pkObj = (PSYMCRYPT_ECKEY) pbBuffer;
+
+    pkObj->fAlgorithmInfo = 0;
+    pkObj->hasPrivateKey = FALSE;
+    pkObj->pCurve = pCurve;
+
+    pkObj->poPublicKey = SymCryptEcpointCreate(
+                        pbBuffer + sizeof(SYMCRYPT_ECKEY),
+                        cbPublicKey,
+                        pCurve );
+    SYMCRYPT_ASSERT( pkObj->poPublicKey != NULL );
+
+    pkObj->piPrivateKey = SymCryptIntCreate(
+                        pbBuffer + sizeof(SYMCRYPT_ECKEY) + cbPublicKey,
+                        cbPrivateKey,
+                        privateKeyDigits );
+    SYMCRYPT_ASSERT( pkObj->piPrivateKey );
+
+    // Setting the magic
+    SYMCRYPT_SET_MAGIC( pkObj );
+
+    return pkObj;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEckeyWipePrivateState(
+    _Inout_ PSYMCRYPT_ECKEY pkEckey )
+{
+    SymCryptIntSetValueUint32( 0, pkEckey->piPrivateKey );
+    pkEckey->hasPrivateKey = FALSE;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEckeyWipe( _Out_ PSYMCRYPT_ECKEY pkDst )
+{
+    // Wipe the whole structure in one go.
+    SymCryptWipe( pkDst, SymCryptSizeofEckeyFromCurve( pkDst->pCurve ) );
+}
+
+VOID
+SymCryptEckeyCopy(
+    _In_    PCSYMCRYPT_ECKEY  pkSrc,
+    _Out_   PSYMCRYPT_ECKEY   pkDst )
+{
+    //
+    // in-place copy is somewhat common...
+    //
+    if( pkSrc != pkDst )
+    {
+        // Copy the fAlgorithmInfo flags
+        pkDst->fAlgorithmInfo = pkSrc->fAlgorithmInfo;
+
+        // Copy the hasPrivateKey flag
+        pkDst->hasPrivateKey = pkSrc->hasPrivateKey;
+
+        // Copy the public key
+        SymCryptEcpointCopy( pkSrc->pCurve, pkSrc->poPublicKey, pkDst->poPublicKey );
+
+        // Copy the private key
+        SymCryptIntCopy( pkSrc->piPrivateKey, pkDst->piPrivateKey );
+    }
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEckeySizeofPublicKey(
+    _In_ PCSYMCRYPT_ECKEY           pkEckey,
+    _In_ SYMCRYPT_ECPOINT_FORMAT    ecPointFormat )
+{
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - SymCryptEcpointFormatNumberofElements returns up to 4 elements.
+    //
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    return SymCryptEcpointFormatNumberofElements[ecPointFormat] * SymCryptEcurveSizeofFieldElement( pkEckey->pCurve );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEckeySizeofPrivateKey( _In_ PCSYMCRYPT_ECKEY pkEckey )
+{
+    return SymCryptEcurveSizeofScalarMultiplier( pkEckey->pCurve );
+}
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEckeyHasPrivateKey( _In_ PCSYMCRYPT_ECKEY pkEckey )
+{
+    return pkEckey->hasPrivateKey;
+}
+
+#define SYMCRYPT_FLAG_ECKEY_PUBLIC_KEY_ORDER_VALIDATION (0x1)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyPerformPublicKeyValidation(
+    _In_                            PCSYMCRYPT_ECKEY        pEckey,
+    _In_                            UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_ECURVE   pCurve = pEckey->pCurve;
+
+    PSYMCRYPT_ECPOINT   poNPub = NULL;
+    UINT32              cbNPub = SymCryptSizeofEcpointFromCurve( pCurve );
+
+    // This is an excessive amount of space to require, but all callers can currently provide it, and it's easy to phrase
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_ECKEY_ECURVE_OPERATIONS( pCurve ) );
+
+    SYMCRYPT_ASSERT( cbScratch >= cbNPub );
+
+    // Check if Public key is O
+    if ( SymCryptEcpointIsZero( pCurve, pEckey->poPublicKey, pbScratch, cbScratch ) )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // Public key is represented by Modelements of the underlying finite field for the curve
+    // If we have reached this point we have either:
+    // Constructed the Public key to have coordinates in the field (Generate case), or
+    // Verified the Public key has coordinates in the field (SetValue case)
+
+    // Check that Public key is on the curve
+    // Skip check for Montgomery curves as we do not have an EcpointOnCurve function for them
+    if ( !SYMCRYPT_CURVE_IS_MONTGOMERY_TYPE(pCurve) &&
+        !SymCryptEcpointOnCurve( pCurve, pEckey->poPublicKey, pbScratch, cbScratch ) )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // Perform validation that Public key is in a subgroup of order GOrd.
+    if ( (flags & SYMCRYPT_FLAG_ECKEY_PUBLIC_KEY_ORDER_VALIDATION) != 0 )
+    {
+        if ( SymCryptIntIsEqualUint32( pCurve->H, 1 ) )
+        {
+            // If cofactor is 1 then to validate that Public key has order GOrd
+            // it is sufficient to validate Public key is on the curve
+            // We just performed this check - so we are done.
+        }
+        else
+        {
+            // Ensure GOrd*(Public key) == O
+            poNPub = SymCryptEcpointCreate( pbScratch, cbNPub, pCurve );
+            pbScratch += cbNPub;
+            cbScratch -= cbNPub;
+
+            SYMCRYPT_ASSERT( poNPub != NULL );
+
+            // Do the multiplication
+            scError = SymCryptEcpointScalarMul(
+                pCurve,
+                SymCryptIntFromModulus( pCurve->GOrd ),
+                pEckey->poPublicKey,
+                0, // Do not multiply by cofactor!
+                poNPub,
+                pbScratch,
+                cbScratch );
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                return scError;
+            }
+
+            if ( !SymCryptEcpointIsZero( pCurve, poNPub, pbScratch, cbScratch ) )
+            {
+                return SYMCRYPT_INVALID_ARGUMENT;
+            }
+        }
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeySetValue(
+    _In_reads_bytes_( cbPrivateKey )
+            PCBYTE                  pbPrivateKey,
+            SIZE_T                  cbPrivateKey,
+    _In_reads_bytes_( cbPublicKey )
+            PCBYTE                  pbPublicKey,
+            SIZE_T                  cbPublicKey,
+            SYMCRYPT_NUMBER_FORMAT  numFormat,
+            SYMCRYPT_ECPOINT_FORMAT ecPointFormat,
+            UINT32                  flags,
+    _Inout_ PSYMCRYPT_ECKEY         pEckey )
+{
+    SYMCRYPT_ERROR      scError = SYMCRYPT_NO_ERROR;
+    PBYTE               pbScratch = NULL;
+    UINT32              cbScratch = 0;
+    PBYTE               pbScratchInternal = NULL;
+    UINT32              cbScratchInternal = 0;
+
+    PCSYMCRYPT_ECURVE   pCurve = pEckey->pCurve;
+
+    PSYMCRYPT_ECPOINT   poTmp = NULL;
+    UINT32              cbTmp = 0;
+
+    PSYMCRYPT_INT           piTmpInteger = NULL;
+    UINT32                  cbTmpInteger = 0;
+    PSYMCRYPT_MODELEMENT    peTmpModElement = NULL;
+    UINT32                  cbTmpModElement = pCurve->cbModElement;
+
+    UINT32 privateKeyDigits = SymCryptEcurveDigitsofScalarMultiplier(pCurve);
+
+    UINT32 fValidatePublicKeyOrder = SYMCRYPT_FLAG_ECKEY_PUBLIC_KEY_ORDER_VALIDATION;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_ECKEY_ECDSA | SYMCRYPT_FLAG_ECKEY_ECDH;
+    // Make sure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION | algorithmFlags;
+
+    if ( ( ( flags & ~allowedFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check that minimal validation flag only specified with no fips
+    if ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+         ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) != 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) != 0 )
+    {
+        fValidatePublicKeyOrder = 0;
+    }
+
+    if ( ( ( cbPrivateKey == 0 ) && ( cbPublicKey == 0 ) ) ||
+         ( ( cbPrivateKey != 0 ) && ( cbPrivateKey != SymCryptEcurveSizeofScalarMultiplier( pEckey->pCurve ) ) ) ||
+         ( ( cbPublicKey != 0 )  && ( cbPublicKey != SymCryptEckeySizeofPublicKey( pEckey, ecPointFormat ) ) ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Allocate scratch space
+    cbScratch = SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_ECKEY_ECURVE_OPERATIONS( pCurve );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    if ( pbPrivateKey != NULL )
+    {
+        //
+        // Private key calculations
+        //
+
+        pbScratchInternal = pbScratch;
+        cbScratchInternal = cbScratch;
+
+        // Allocate the integer
+        cbTmpInteger = SymCryptSizeofIntFromDigits( privateKeyDigits );
+        piTmpInteger = SymCryptIntCreate( pbScratchInternal, cbTmpInteger, privateKeyDigits );
+        SYMCRYPT_ASSERT( piTmpInteger != NULL );
+
+        pbScratchInternal += cbTmpInteger;
+        cbScratchInternal -= cbTmpInteger;
+
+        // Allocate the modelement
+        peTmpModElement = SymCryptModElementCreate( pbScratchInternal, cbTmpModElement, pCurve->GOrd );
+        SYMCRYPT_ASSERT( peTmpModElement != NULL );
+
+        pbScratchInternal += cbTmpModElement;
+        cbScratchInternal -= cbTmpModElement;
+
+        // Get the "raw" private key
+        scError = SymCryptIntSetValue( pbPrivateKey, cbPrivateKey, numFormat, piTmpInteger );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // Validation steps
+        if ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) == 0 )
+        {
+            // Perform range validation on imported Private key if it is in canonical format
+            if ( pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL )
+            {
+                // Check if Private key is greater than or equal to GOrd
+                if ( !SymCryptIntIsLessThan( piTmpInteger, SymCryptIntFromModulus( pCurve->GOrd ) ) )
+                {
+                    scError = SYMCRYPT_INVALID_ARGUMENT;
+                    goto cleanup;
+                }
+            }
+
+            // "TimesH" formats
+            // IntGetBits requirements:
+            //      We know that coFactorPower is up to SYMCRYPT_ECURVE_MAX_COFACTOR_POWER. Thus
+            //      less than 32 and less than the digits size in bits.
+            if ( (pCurve->coFactorPower>0) &&
+                (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH_TIMESH) &&
+                (SymCryptIntGetBits( piTmpInteger, 0, pCurve->coFactorPower) != 0) )
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+
+
+            // High bit restrictions
+            // IntGetBits requirements:
+            //      Satisfied by asserting that
+            //      HighBitRestrictionPosition + HighBitRestrictionNumOfBits <= GOrdBitsize + coFactorPower
+            //      during EcurveAllocate.
+            if ( (pCurve->HighBitRestrictionNumOfBits>0) &&
+                (SymCryptIntGetBits(
+                    piTmpInteger,
+                    pCurve->HighBitRestrictionPosition,
+                    pCurve->HighBitRestrictionNumOfBits) != pCurve->HighBitRestrictionValue) )
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+        }
+
+        // Convert the private key to "DivH" format
+        if (pCurve->coFactorPower>0)
+        {
+            // "TimesH" format: Divide the input private key with the cofactor
+            // by shifting right the appropriate number of bits
+            if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH_TIMESH)
+            {
+                SymCryptIntDivPow2( piTmpInteger, pCurve->coFactorPower, piTmpInteger );
+            }
+
+            // "Canonical" format: Divide by h modulo GOrd
+            if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL)
+            {
+                SymCryptIntToModElement( piTmpInteger, pCurve->GOrd, peTmpModElement, pbScratchInternal, cbScratchInternal );
+                SymCryptModDivPow2( pCurve->GOrd, peTmpModElement, pCurve->coFactorPower, peTmpModElement, pbScratchInternal, cbScratchInternal );
+                SymCryptModElementToInt( pCurve->GOrd, peTmpModElement, piTmpInteger, pbScratchInternal, cbScratchInternal );
+            }
+        }
+
+        // Divide the input private key since it could be larger than subgroup order
+        SymCryptIntDivMod(
+            piTmpInteger,
+            SymCryptDivisorFromModulus(pCurve->GOrd),
+            NULL,
+            piTmpInteger,
+            pbScratchInternal,
+            cbScratchInternal );
+
+        // Check if Private key is 0 after dividing it by the subgroup order
+        // Other part of range validation - perform unconditionally as it is cheap
+        // and it never makes sense for private key to be 0 intentionally
+        if (SymCryptIntIsEqualUint32( piTmpInteger, 0 ))
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        // Copy into the ECKEY
+        SymCryptIntCopy( piTmpInteger, pEckey->piPrivateKey );
+
+        pEckey->hasPrivateKey = TRUE;
+    }
+
+    if ( pbPublicKey != NULL )
+    {
+        scError = SymCryptEcpointSetValue(
+                            pCurve,
+                            pbPublicKey,
+                            cbPublicKey,
+                            numFormat,
+                            ecPointFormat,
+                            pEckey->poPublicKey,
+                            SYMCRYPT_FLAG_DATA_PUBLIC,
+                            pbScratch,
+                            cbScratch );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Perform Public key validation on imported Public key.
+        if ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) == 0 )
+        {
+            scError = SymCryptEckeyPerformPublicKeyValidation(
+                pEckey,
+                fValidatePublicKeyOrder,
+                pbScratch,
+                cbScratch );
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    // Calculating the public key if no key was provided
+    // or if needed for keypair regeneration validation
+    if ( (pbPublicKey==NULL) ||
+         ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+          (pbPrivateKey!=NULL) && (pbPublicKey!=NULL) ) )
+    {
+        // Calculate the public key from the private key
+        pbScratchInternal = pbScratch;
+        cbScratchInternal = cbScratch;
+
+        // By default calculate the Public key directly where it will be persisted
+        poTmp = pEckey->poPublicKey;
+
+        if ( pbPublicKey != NULL )
+        {
+            // If doing regeneration validation calculate the Public key in scratch
+            cbTmp = SymCryptSizeofEcpointFromCurve( pCurve );
+            poTmp = SymCryptEcpointCreate( pbScratchInternal, cbTmp, pCurve );
+            pbScratchInternal += cbTmp;
+            cbScratchInternal -= cbTmp;
+        }
+
+        SYMCRYPT_ASSERT( poTmp != NULL );
+
+        // Always multiply by the cofactor since the internal format is "DIVH"
+        scError = SymCryptEcpointScalarMul(
+            pCurve,
+            pEckey->piPrivateKey,
+            NULL,
+            SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL,
+            poTmp,
+            pbScratchInternal,
+            cbScratchInternal );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        if ( pbPublicKey != NULL )
+        {
+            if ( !SymCryptEcpointIsEqual( pCurve, poTmp, pEckey->poPublicKey, 0, pbScratchInternal, cbScratchInternal ) )
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+        }
+        else if ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) == 0 )
+        {
+            // Perform Public key validation on generated Public key.
+            scError = SymCryptEckeyPerformPublicKeyValidation(
+                pEckey,
+                fValidatePublicKeyOrder,
+                pbScratch,
+                cbScratch );
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    pEckey->fAlgorithmInfo = flags; // We want to track all of the flags in the Eckey
+
+    if ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        if ( ( flags & SYMCRYPT_FLAG_ECKEY_ECDSA ) != 0 )
+        {
+            // Ensure ECDSA algorithm selftest is run before first use of ECDSA algorithm
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptEcDsaSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_ECDSA );
+
+            // PCT does not need to be run on import - mark it as done
+            pEckey->fAlgorithmInfo |= SYMCRYPT_PCT_ECDSA;
+        }
+
+        if ( ( flags & SYMCRYPT_FLAG_ECKEY_ECDH ) != 0 )
+        {
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptEcDhSecretAgreementSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_ECDH );
+        }
+    }
+
+cleanup:
+
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyGetValue(
+    _In_    PCSYMCRYPT_ECKEY        pEckey,
+    _Out_writes_bytes_( cbPrivateKey )
+            PBYTE                   pbPrivateKey,
+            SIZE_T                  cbPrivateKey,
+    _Out_writes_bytes_( cbPublicKey )
+            PBYTE                   pbPublicKey,
+            SIZE_T                  cbPublicKey,
+            SYMCRYPT_NUMBER_FORMAT  numFormat,
+            SYMCRYPT_ECPOINT_FORMAT ecPointFormat,
+            UINT32                  flags )
+{
+    SYMCRYPT_ERROR      scError = SYMCRYPT_NO_ERROR;
+    PBYTE               pbScratch = NULL;
+    UINT32              cbScratch = 0;
+    PBYTE               pbScratchInternal = NULL;
+    UINT32              cbScratchInternal = 0;
+
+    PCSYMCRYPT_ECURVE   pCurve = pEckey->pCurve;
+
+    PSYMCRYPT_INT           piTmpInteger = NULL;
+    UINT32                  cbTmpInteger = 0;
+    PSYMCRYPT_MODELEMENT    peTmpModElement = NULL;
+    UINT32                  cbTmpModElement = pCurve->cbModElement;
+
+    UINT32 privateKeyDigits = SymCryptEcurveDigitsofScalarMultiplier(pCurve);
+
+    SYMCRYPT_ASSERT( (cbPrivateKey==0) || (cbPrivateKey == SymCryptEcurveSizeofScalarMultiplier( pEckey->pCurve )) );
+    SYMCRYPT_ASSERT( (cbPublicKey==0) || (cbPublicKey == SymCryptEckeySizeofPublicKey( pEckey, ecPointFormat)) );
+
+    // Make sure we only specify the correct flags
+    if (flags != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Allocate scratch space
+    cbScratch = SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_ECKEY_ECURVE_OPERATIONS( pCurve );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = cbScratch;
+
+    // Allocate the integer
+    cbTmpInteger = SymCryptSizeofIntFromDigits( privateKeyDigits );
+    piTmpInteger = SymCryptIntCreate( pbScratchInternal, cbTmpInteger, privateKeyDigits );
+    SYMCRYPT_ASSERT( piTmpInteger != NULL );
+
+    pbScratchInternal += cbTmpInteger;
+    cbScratchInternal -= cbTmpInteger;
+
+    // Allocate the modelement
+    peTmpModElement = SymCryptModElementCreate( pbScratchInternal, cbTmpModElement, pCurve->GOrd );
+    SYMCRYPT_ASSERT( peTmpModElement != NULL );
+
+    pbScratchInternal += cbTmpModElement;
+    cbScratchInternal -= cbTmpModElement;
+
+    if ((cbPrivateKey == 0) && (cbPublicKey == 0))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbPrivateKey != 0)
+    {
+        if (!pEckey->hasPrivateKey)
+        {
+            scError = SYMCRYPT_INVALID_BLOB;
+            goto cleanup;
+        }
+
+        // If this keypair may be used in ECDSA, and does not have the no FIPS flag, run the PCT if
+        // it has not already been run
+        if ( ((pEckey->fAlgorithmInfo & SYMCRYPT_FLAG_ECKEY_ECDSA) != 0) &&
+             ((pEckey->fAlgorithmInfo & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0) )
+        {
+            SYMCRYPT_RUN_KEY_GEN_PCT(
+                SymCryptEcDsaPct,
+                pEckey,
+                SYMCRYPT_PCT_ECDSA );
+        }
+
+        // Copy the key into the temporary integer
+        SymCryptIntCopy( pEckey->piPrivateKey, piTmpInteger );
+
+        // Convert the "DivH" format into the external format
+        if (pCurve->coFactorPower>0)
+        {
+            // For the "Canonical" format: Multiply the integer by h
+            // and then take the result modulo GOrd
+            if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL)
+            {
+                SymCryptIntMulPow2( piTmpInteger, pCurve->coFactorPower, piTmpInteger );
+                SymCryptIntDivMod(
+                    piTmpInteger,
+                    SymCryptDivisorFromModulus(pCurve->GOrd),
+                    NULL,
+                    piTmpInteger,
+                    pbScratchInternal,
+                    cbScratchInternal );
+            }
+
+            // For the "TimesH" format: Multiply the integer by h again by shifting
+            if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH_TIMESH)
+            {
+                SymCryptIntMulPow2( piTmpInteger, pCurve->coFactorPower, piTmpInteger );
+            }
+        }
+
+        scError = SymCryptIntGetValue( piTmpInteger, pbPrivateKey, cbPrivateKey, numFormat );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    if (cbPublicKey != 0)
+    {
+        scError = SymCryptEcpointGetValue(
+                            pCurve,
+                            pEckey->poPublicKey,
+                            numFormat,
+                            ecPointFormat,
+                            pbPublicKey,
+                            cbPublicKey,
+                            SYMCRYPT_FLAG_DATA_PUBLIC,
+                            pbScratch,
+                            cbScratch );
+    }
+
+cleanup:
+
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
+
+#define SYMCRYPT_ECPOINT_SET_RANDOM_MAX_TRIES   (1000)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeySetRandom(
+    _In_    UINT32                  flags,
+    _Inout_ PSYMCRYPT_ECKEY         pEckey )
+{
+    SYMCRYPT_ERROR      scError = SYMCRYPT_NO_ERROR;
+    PBYTE               pbScratch = NULL;
+    UINT32              cbScratch = 0;
+    PBYTE               pbScratchInternal = NULL;
+    UINT32              cbScratchInternal = 0;
+
+    PCSYMCRYPT_ECURVE   pCurve = pEckey->pCurve;
+
+    PSYMCRYPT_ECPOINT   poTmp = NULL;
+    UINT32              cbTmp = 0;
+
+    INT32 cntr = SYMCRYPT_ECPOINT_SET_RANDOM_MAX_TRIES;
+
+    PSYMCRYPT_MODELEMENT peScalar = NULL;
+    PSYMCRYPT_INT        piScalar = NULL;
+    UINT32               cbScalar = 0;
+
+    UINT32 highBitRestrictionPosition = pCurve->HighBitRestrictionPosition;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_ECKEY_ECDSA | SYMCRYPT_FLAG_ECKEY_ECDH;
+    // Make sure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS | algorithmFlags;
+
+    if ( ( ( flags & ~allowedFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    cbScratch = SYMCRYPT_INTERNAL_SCRATCH_BYTES_FOR_ECKEY_ECURVE_OPERATIONS( pCurve );
+    pbScratch = SymCryptCallbackAlloc( cbScratch );
+    if ( pbScratch == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Allocating temporaries
+    pbScratchInternal = pbScratch;
+    cbScratchInternal = cbScratch;
+
+    peScalar = SymCryptModElementCreate( pbScratchInternal, pCurve->cbModElement, pCurve->GOrd );
+    SYMCRYPT_ASSERT( peScalar != NULL );
+
+    pbScratchInternal += pCurve->cbModElement;
+    cbScratchInternal -= pCurve->cbModElement;
+
+    cbScalar = SymCryptSizeofIntFromDigits( SymCryptEcurveDigitsofScalarMultiplier(pCurve) );
+    piScalar = SymCryptIntCreate( pbScratchInternal, cbScalar, SymCryptEcurveDigitsofScalarMultiplier(pCurve) );
+
+    pbScratchInternal += cbScalar;
+    cbScratchInternal -= cbScalar;
+
+    // Shift the high bit position if the format is "TIMESH"
+    //  Note:   Do not actually multiply the integer as we will check if it is
+    //          less than the group order
+    if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_DIVH_TIMESH)
+    {
+        highBitRestrictionPosition -= pCurve->coFactorPower;
+    }
+
+    // Main loop
+    do
+    {
+        // We perform Private key range validation by construction
+        // Setting a random mod element in the [1, SubgroupOrder-1] set
+        // This will be the "DivH" format of the private key. This means
+        // that PublicKey = h * PrivateKey * G
+        SymCryptModSetRandom(
+            pCurve->GOrd,
+            peScalar,
+            (SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE|SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE),
+            pbScratchInternal,
+            cbScratchInternal );
+
+        // Converting to "canonical" format
+        if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL)
+        {
+            for (UINT32 i=0; i<pCurve->coFactorPower; i++)
+            {
+                SymCryptModAdd( pCurve->GOrd, peScalar, peScalar, peScalar, pbScratchInternal, cbScratchInternal );
+            }
+        }
+
+        // Set the temporary scalar to verify the format
+        SymCryptModElementToInt( pCurve->GOrd, peScalar, piScalar, pbScratchInternal, cbScratchInternal );
+
+        if (pCurve->HighBitRestrictionNumOfBits > 0)
+        {
+            // Set the desired bits
+            SymCryptIntSetBits(
+                    piScalar,
+                    pCurve->HighBitRestrictionValue,
+                    highBitRestrictionPosition,
+                    pCurve->HighBitRestrictionNumOfBits );
+
+            // Make sure we didn't exceed the group order
+            if ( SymCryptIntIsLessThan(
+                    piScalar,
+                    SymCryptIntFromModulus( pCurve->GOrd )) )
+            {
+                break;
+            }
+        }
+        else
+        {
+            // No high bit restriction was specified
+            break;
+        }
+
+        cntr--;
+    }
+    while (cntr>0);
+
+    if (cntr <= 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Here piScalar has a private key that satisfies the restriction(s)
+    // Move it to the modelement
+    SymCryptIntToModElement( piScalar, pCurve->GOrd, peScalar, pbScratchInternal, cbScratchInternal );
+
+    // Convert the private key back to "DIVH" format
+    if (pCurve->PrivateKeyDefaultFormat == SYMCRYPT_ECKEY_PRIVATE_FORMAT_CANONICAL)
+    {
+        SymCryptModDivPow2( pCurve->GOrd, peScalar, pCurve->coFactorPower, peScalar, pbScratchInternal, cbScratchInternal );
+    }
+
+    // Set the private key
+    SymCryptModElementToInt( pCurve->GOrd, peScalar, pEckey->piPrivateKey, pbScratchInternal, cbScratchInternal );
+
+    // Do the multiplication (pass over the entire scratch space as it is not needed anymore)
+    scError = SymCryptEcpointScalarMul(
+        pCurve,
+        pEckey->piPrivateKey,
+        NULL,
+        SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL,
+        pEckey->poPublicKey,
+        pbScratch,
+        cbScratch );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Perform range and public key order validation on generated Public key.
+    if ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 )
+    {
+        // Perform Public key validation.
+        // Always perform range validation and validation that Public key is in subgroup of order GOrd
+        scError = SymCryptEckeyPerformPublicKeyValidation(
+            pEckey,
+            SYMCRYPT_FLAG_ECKEY_PUBLIC_KEY_ORDER_VALIDATION,
+            pbScratch,
+            cbScratch );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    pEckey->hasPrivateKey = TRUE;
+
+    pEckey->fAlgorithmInfo = flags; // We want to track all of the flags in the Eckey
+
+    if ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 )
+    {
+        if( ( flags & SYMCRYPT_FLAG_ECKEY_ECDSA ) != 0 )
+        {
+            // Ensure ECDSA algorithm selftest is run before first use of ECDSA algorithm
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptEcDsaSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_ECDSA );
+        }
+
+        if( ( flags & SYMCRYPT_FLAG_ECKEY_ECDH ) != 0 )
+        {
+            // Ensure we have run the algorithm selftest at least once.
+            SYMCRYPT_RUN_SELFTEST_ONCE(
+                SymCryptEcDhSecretAgreementSelftest,
+                SYMCRYPT_SELFTEST_ALGORITHM_ECDH );
+
+            // Run PCT eagerly so it only needs to be defined here
+            // The important case for performance is ECDH key generation
+
+            // ECDH PCT per SP80056a-rev3 5.6.2.1.4 b)
+            // Recompute the public key from the private key
+            // Option a) appears to be explicitly overruled by 140-3 IG
+            pbScratchInternal = pbScratch;
+            cbScratchInternal = cbScratch;
+
+            cbTmp = SymCryptSizeofEcpointFromCurve( pCurve );
+            poTmp = SymCryptEcpointCreate( pbScratchInternal, cbTmp, pCurve );
+            pbScratchInternal += cbTmp;
+            cbScratchInternal -= cbTmp;
+
+            SYMCRYPT_ASSERT( poTmp != NULL );
+
+            // Always multiply by the cofactor since the internal format is "DIVH"
+            scError = SymCryptEcpointScalarMul(
+                pCurve,
+                pEckey->piPrivateKey,
+                NULL,
+                SYMCRYPT_FLAG_ECC_LL_COFACTOR_MUL,
+                poTmp,
+                pbScratchInternal,
+                cbScratchInternal );
+            if ( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+
+            SYMCRYPT_FIPS_ASSERT( SymCryptEcpointIsEqual( pCurve, poTmp, pEckey->poPublicKey, 0, pbScratchInternal, cbScratchInternal ) );
+        }
+    }
+
+cleanup:
+
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyExtendKeyUsage(
+    _Inout_ PSYMCRYPT_ECKEY pEckey,
+            UINT32          flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_ECKEY_ECDSA | SYMCRYPT_FLAG_ECKEY_ECDH;
+
+    if ( ( ( flags & ~algorithmFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pEckey->fAlgorithmInfo |= flags;
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ecpoint.c b/libs/symcrypt/lib/ecpoint.c
new file mode 100644
index 000000000000..d2f36fa57e5a
--- /dev/null
+++ b/libs/symcrypt/lib/ecpoint.c
@@ -0,0 +1,785 @@
+//
+// ecpoint.c   Ecpoint functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Table with the number of field elements for each point format
+const UINT32 SymCryptEcpointFormatNumberofElements[] = {
+    0,
+    1,      // SYMCRYPT_ECPOINT_FORMAT_X
+    2,      // SYMCRYPT_ECPOINT_FORMAT_XY
+};
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofEcpointEx(
+    UINT32 cbModElement,
+    UINT32 numOfCoordinates )
+{
+    SYMCRYPT_ASSERT(numOfCoordinates > 0);
+    SYMCRYPT_ASSERT(numOfCoordinates <= SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH);
+
+    // Callers should never specify numOfCoordinates equal to 0 or greater than
+    // SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH
+    // Return 0 to indicate failure if a caller does specify invalid numOfCoordinates
+    if( (numOfCoordinates == 0) || (numOfCoordinates > SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH) )
+    {
+        return 0;
+    }
+
+    // Since the maximum number of coordinates is 4 this result is bounded
+    // by 4*2^17 + overhead ~ 2^20
+    return sizeof(SYMCRYPT_ECPOINT) + numOfCoordinates * cbModElement;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofEcpointFromCurve( PCSYMCRYPT_ECURVE pCurve )
+{
+    // Same bound as SymCryptSizeofEcpointEx
+    return SymCryptSizeofEcpointEx( pCurve->cbModElement, SYMCRYPT_INTERNAL_NUMOF_COORDINATES(pCurve->eCoordinates) );
+}
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointAllocate( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    PVOID               p = NULL;
+    SIZE_T              cb;
+    PSYMCRYPT_ECPOINT   res = NULL;
+
+    cb = SymCryptSizeofEcpointFromCurve( pCurve );
+
+    if ( cb != 0 )
+    {
+        p = SymCryptCallbackAlloc( cb );
+    }
+
+    if ( p==NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptEcpointCreate( p, cb, pCurve );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointFree(
+     _In_ PCSYMCRYPT_ECURVE pCurve,
+     _Out_ PSYMCRYPT_ECPOINT poDst )
+{
+    SYMCRYPT_CHECK_MAGIC( poDst );
+    SymCryptEcpointWipe( pCurve, poDst );
+    SymCryptCallbackFree( poDst );
+}
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointCreateEx(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    PCSYMCRYPT_ECURVE   pCurve,
+                                    UINT32              numOfCoordinates )
+{
+    PSYMCRYPT_ECPOINT       poPoint = NULL;
+
+    PSYMCRYPT_MODELEMENT    pmTmp = NULL;
+    UINT32                  cbModElement = pCurve->cbModElement;
+
+    PBYTE pbBufferEnd = pbBuffer + cbBuffer;
+    UNREFERENCED_PARAMETER( pbBufferEnd );     // only referenced in an ASSERT...
+
+    SYMCRYPT_ASSERT( pCurve->FMod != 0 );
+    SYMCRYPT_ASSERT( pCurve->cbModElement != 0 );
+    SYMCRYPT_ASSERT( cbBuffer >= SymCryptSizeofEcpointEx( pCurve->cbModElement, numOfCoordinates ) );
+    if ( cbBuffer == 0 || numOfCoordinates == 0 )
+    {
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    poPoint = (PSYMCRYPT_ECPOINT) pbBuffer;
+
+    pbBuffer += sizeof(SYMCRYPT_ECPOINT);
+
+    // Setting the point coordinates
+    for (UINT32 i=0; i<numOfCoordinates; i++)
+    {
+        SYMCRYPT_ASSERT( pbBuffer + cbModElement <= pbBufferEnd );
+        pmTmp = SymCryptModElementCreate( pbBuffer, cbModElement, pCurve->FMod );
+        if ( pmTmp == NULL )
+        {
+            poPoint = NULL;
+            goto cleanup;
+        }
+        pbBuffer += cbModElement;
+    }
+
+    // Setting the normalized flag
+    poPoint->normalized = FALSE;
+
+    // Setting the curve
+    poPoint->pCurve     = pCurve;
+
+    // Setting the magic
+    SYMCRYPT_SET_MAGIC( poPoint );
+
+cleanup:
+    return poPoint;
+}
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+    _In_                            PCSYMCRYPT_ECURVE   pCurve )
+{
+
+    SYMCRYPT_ASSERT( pCurve->eCoordinates != 0 );
+
+    return SymCryptEcpointCreateEx( pbBuffer, cbBuffer, pCurve, SYMCRYPT_INTERNAL_NUMOF_COORDINATES(pCurve->eCoordinates) );
+}
+
+PSYMCRYPT_ECPOINT
+SYMCRYPT_CALL
+SymCryptEcpointRetrieveHandle( _In_  PBYTE   pbBuffer )
+{
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    return (PSYMCRYPT_ECPOINT) pbBuffer;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointWipe( _In_ PCSYMCRYPT_ECURVE pCurve, _Out_ PSYMCRYPT_ECPOINT poDst )
+{
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    // Wipe the whole structure in one go.
+    SymCryptWipe( poDst, SymCryptSizeofEcpointFromCurve( pCurve ) );
+}
+
+VOID
+SymCryptEcpointCopy(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst )
+{
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    if( poSrc != poDst )
+    {
+        // Unconditionally set the normalization state of destination to source
+        poDst->normalized = poSrc->normalized;
+
+        memcpy(poDst + 1, poSrc + 1, SYMCRYPT_INTERNAL_NUMOF_COORDINATES(pCurve->eCoordinates) * pCurve->FModDigits * SYMCRYPT_FDEF_DIGIT_SIZE);
+    }
+}
+
+VOID
+SymCryptEcpointMaskedCopy(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              mask )
+{
+    SYMCRYPT_ASSERT( (mask == 0) || (mask == 0xffffffff) );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+
+    // Unconditionally combine the normalization state of source and destination to avoid potential for
+    // leak of mask. Normalized is a non-secret value and is permitted to be leaked by side-channels
+    poDst->normalized &= poSrc->normalized;
+
+	// dcl - this looks like the equivalent of memcpy
+	// should be proven that arguments cannot be the result of an integer overflow
+    SymCryptFdefMaskedCopy((PCBYTE)poSrc + sizeof(SYMCRYPT_ECPOINT), (PBYTE)poDst + sizeof(SYMCRYPT_ECPOINT), SYMCRYPT_INTERNAL_NUMOF_COORDINATES(pCurve->eCoordinates) * pCurve->FModDigits, mask );
+}
+
+//
+// SymCryptEcpointTransform: Internal function to transform an ECPOINT
+// from one coordinate representation to another. One point has the default
+// format of the curve. The other point has a format large enough for the external
+// SYMCRYPT_ECPOINT_FORMAT.
+//
+// When the boolean setValue is set to TRUE, the source point is the one with
+// the external format eformat, and the destination point has the default
+// format of the curve. If setValue = FALSE the roles are reversed.
+// This function is only called by the Get / Set Value functions.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointTransform(
+    _In_    PCSYMCRYPT_ECURVE               pCurve,
+    _In_    PCSYMCRYPT_ECPOINT              poSrc,
+    _Out_   PSYMCRYPT_ECPOINT               poDst,
+            SYMCRYPT_ECPOINT_FORMAT         eformat,
+            BOOLEAN                         setValue,
+            UINT32                          flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE                           pbScratch,
+            SIZE_T                          cbScratch )
+{
+    SYMCRYPT_ERROR          scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_MODELEMENT    peSrc = NULL;
+    PSYMCRYPT_MODELEMENT    peDst = NULL;
+    PSYMCRYPT_MODELEMENT    peX = NULL;
+    PSYMCRYPT_MODELEMENT    peY = NULL;
+
+    SYMCRYPT_ECPOINT_COORDINATES    coFrom = SYMCRYPT_ECPOINT_COORDINATES_INVALID;
+    SYMCRYPT_ECPOINT_COORDINATES    coTo = SYMCRYPT_ECPOINT_COORDINATES_INVALID;
+
+    PSYMCRYPT_MODELEMENT peT[2] = { 0 };    // Temporaries
+
+    SYMCRYPT_ASSERT( (flags & ~SYMCRYPT_FLAG_DATA_PUBLIC) == 0 );
+    SYMCRYPT_ASSERT( SymCryptEcurveIsSame(pCurve, poSrc->pCurve) && SymCryptEcurveIsSame(pCurve, poDst->pCurve) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_MAX(  SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pCurve->FModDigits ),
+                                        SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( pCurve->FModDigits )) +
+                                  2 * pCurve->cbModElement );
+
+    // Get the assumed representation from the external format
+    switch (eformat)
+    {
+    case (SYMCRYPT_ECPOINT_FORMAT_X):
+        coFrom = SYMCRYPT_ECPOINT_COORDINATES_SINGLE;
+        break;
+    case (SYMCRYPT_ECPOINT_FORMAT_XY):
+        coFrom = SYMCRYPT_ECPOINT_COORDINATES_AFFINE;
+        break;
+    default:
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Find out whether we are setting or getting the value of the ECPOINT
+    if (setValue)
+    {
+        coTo = pCurve->eCoordinates;
+    }
+    else
+    {
+        coTo = coFrom;
+        coFrom = pCurve->eCoordinates;
+    }
+
+    // Take all the possible supported transformations:
+    //      - From SYMCRYPT_ECPOINT_COORDINATES_SINGLE to
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE (identity transformation)
+    //          * SYMCRYPT_ECPOINT_COORDINATES_AFFINE (** Set all zeros to the Y coordinate **)
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE
+    //      - From SYMCRYPT_ECPOINT_COORDINATES_AFFINE to
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE (** Ignore Y coordinate **)
+    //          * SYMCRYPT_ECPOINT_COORDINATES_AFFINE (identity transformation)
+    //          * SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN
+    //          * SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE (** Ignore Y coordinate **)
+    //      - From SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN to
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_AFFINE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN (identity transformation)
+    //      - From SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE to
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_AFFINE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE (identity transformation)
+    //      - From SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE
+    //          * SYMCRYPT_ECPOINT_COORDINATES_AFFINE (** Set all zeros to the Y coordinate **)
+    //          * SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE (identity transformation)
+
+	// dcl - this appears that it might be a candidate for refactoring. Lots of code that looks
+	// duplicated across sections. Maybe some number of small functions would make it less fragile?
+    if ( coFrom == coTo )
+    {
+        SymCryptEcpointCopy( pCurve, poSrc, poDst );    // All the identity transformations.
+    }
+    else if (coFrom == SYMCRYPT_ECPOINT_COORDINATES_SINGLE)
+    {
+        if (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE)
+        {
+            // Copy X
+            peX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peX != NULL );
+
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            SymCryptModElementCopy( pCurve->FMod, peX, peDst );
+
+            // Set Y to 0
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            SymCryptModElementSetValueUint32( 0, pCurve->FMod, peDst, pbScratch, cbScratch );
+        }
+        else if (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE)
+        {
+            // Copy X
+            peX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peX != NULL );
+
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            SymCryptModElementCopy( pCurve->FMod, peX, peDst );
+
+            // Set Y to 1
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            SymCryptModElementSetValueUint32( 1, pCurve->FMod, peDst, pbScratch, cbScratch );
+
+            // Setting the normalized flag
+            poDst->normalized = TRUE;
+        }
+        else
+        {
+            scError = SYMCRYPT_NOT_IMPLEMENTED;
+            goto cleanup;
+        }
+    }
+    else if (coFrom == SYMCRYPT_ECPOINT_COORDINATES_AFFINE)
+    {
+        if ( (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE) ||
+             (coTo == SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN) ||
+             (coTo == SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE) ||
+             (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE)
+           )
+        {
+            // Copy X
+            peX = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peX != NULL );
+
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            SymCryptModElementCopy( pCurve->FMod, peX, peDst );
+
+            if ( (coTo == SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN) ||
+                 (coTo == SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE) )
+            {
+                // Copy Y
+                peY = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+                SYMCRYPT_ASSERT( peY != NULL );
+
+                peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+                SYMCRYPT_ASSERT( peDst != NULL );
+
+                SymCryptModElementCopy( pCurve->FMod, peY, peDst );
+
+                // Set Z to 1
+                peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poDst );
+                SYMCRYPT_ASSERT( peDst != NULL );
+
+                SymCryptModElementSetValueUint32( 1, pCurve->FMod, peDst, pbScratch, cbScratch );
+
+                if (coTo == SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE)
+                {
+                    // T = x * y * z
+                    peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 3, pCurve, poDst );
+                    SYMCRYPT_ASSERT( peDst != NULL );
+
+                    SymCryptModMul( pCurve->FMod, peX, peY, peDst, pbScratch, cbScratch );
+                }
+
+                // Setting the normalized flag
+                poDst->normalized = TRUE;
+            }
+            else if (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE)
+            {
+                // Set Y to 1 (Ignore the second coordinate of the source point)
+                peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+                SYMCRYPT_ASSERT( peDst != NULL );
+
+                SymCryptModElementSetValueUint32( 1, pCurve->FMod, peDst, pbScratch, cbScratch );
+
+                // Setting the normalized flag
+                poDst->normalized = TRUE;
+            }
+        }
+        else
+        {
+            scError = SYMCRYPT_NOT_IMPLEMENTED;
+            goto cleanup;
+        }
+    }
+    else if (coFrom == SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN)
+    {
+        if ( (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE) ||
+             (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE) )
+        {
+            // Creating temporaries
+            for (UINT32 i=0; i<2; i++)
+            {
+                peT[i] = SymCryptModElementCreate( pbScratch, pCurve->cbModElement, pCurve->FMod );
+                SYMCRYPT_ASSERT( peT[i] != NULL);
+
+                pbScratch += pCurve->cbModElement;
+            }
+
+            cbScratch -= 2*pCurve->cbModElement;
+
+            // Get the Z coordinate of the source point
+            peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peSrc != NULL );
+
+            // Check if Z is equal to 0 (i.e. the point is the point at infinity)
+            if (SymCryptModElementIsZero(pCurve->FMod, peSrc))
+            {
+                scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+                goto cleanup;
+            }
+
+            // Calculation
+            // T0 := 1  / Z
+            scError = SymCryptModInv( pCurve->FMod, peSrc, peT[0], flags, pbScratch, cbScratch );
+            if( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+
+            SymCryptModMul( pCurve->FMod, peT[0], peT[0], peT[1], pbScratch, cbScratch );           // T1 := T0 * T0 = 1/Z^2
+
+            // Get the X coordinates
+            peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peSrc != NULL );
+
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            // Set the new X
+            SymCryptModMul( pCurve->FMod, peSrc, peT[1], peDst, pbScratch, cbScratch );       // X2 := X * T1 = X/Z^2
+
+            if (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE)
+            {
+                SymCryptModMul( pCurve->FMod, peT[0], peT[1], peT[1], pbScratch, cbScratch );     // T1 := T0 * T1 = 1/Z^3
+
+                // Get the Y coordinates
+                peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+                SYMCRYPT_ASSERT( peSrc != NULL );
+
+                peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+                SYMCRYPT_ASSERT( peDst != NULL );
+
+                // Set the new Y
+                SymCryptModMul( pCurve->FMod, peSrc, peT[1], peDst, pbScratch, cbScratch );       // Y2 := Y * T1 = Y/Z^3
+            }
+        }
+        else
+        {
+            scError = SYMCRYPT_NOT_IMPLEMENTED;
+            goto cleanup;
+        }
+    }
+    else if ( coFrom == SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE )
+    {
+
+        if ( (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE) ||
+             (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE) )
+        {
+            // Creating temporary
+            peT[0] = SymCryptModElementCreate( pbScratch, pCurve->cbModElement, pCurve->FMod );
+            SYMCRYPT_ASSERT( peT[0] != NULL);
+            pbScratch += pCurve->cbModElement;
+            cbScratch -= 2*pCurve->cbModElement;
+
+            // Get the Z coordinate of the source point
+            peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 2, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peSrc != NULL );
+
+            // Check if Z is equal to 0 (i.e. the point is the point at infinity)
+            if (SymCryptModElementIsZero(pCurve->FMod, peSrc))
+            {
+                scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+                goto cleanup;
+            }
+
+            // peT[0] = 1 / Z
+            scError = SymCryptModInv( pCurve->FMod, peSrc, peT[0], flags, pbScratch, cbScratch );
+            if( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+
+            // Get the X coordinates
+            peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peSrc != NULL );
+
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            // x = X * (1 / Z)
+            SymCryptModMul( pCurve->FMod, peSrc, peT[0], peDst, pbScratch, cbScratch );
+
+            if (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE)
+            {
+                // Get the Y coordinates
+                peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+                SYMCRYPT_ASSERT( peSrc != NULL );
+
+                peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+                SYMCRYPT_ASSERT( peDst != NULL );
+
+                // y = Y * (1 / Z)
+                SymCryptModMul( pCurve->FMod, peSrc, peT[0], peDst, pbScratch, cbScratch );
+            }
+        }
+        else
+        {
+            scError = SYMCRYPT_NOT_IMPLEMENTED;
+            goto cleanup;
+        }
+    }
+    else if (coFrom == SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE)
+    {
+        if ( (coTo == SYMCRYPT_ECPOINT_COORDINATES_SINGLE) ||
+             (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE) )
+        {
+            // Creating temporary
+            peT[0] = SymCryptModElementCreate( pbScratch, pCurve->cbModElement, pCurve->FMod );
+            SYMCRYPT_ASSERT( peT[0] != NULL);
+
+            pbScratch += pCurve->cbModElement;
+            cbScratch -= pCurve->cbModElement;
+
+            // Get the Y coordinate of the source point
+            peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peSrc != NULL );
+
+            // Check if Y is equal to 0 (i.e. the point is the point at infinity)
+            if (SymCryptModElementIsZero(pCurve->FMod, peSrc))
+            {
+                scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+                goto cleanup;
+            }
+
+            // Calculation
+            scError = SymCryptModInv( pCurve->FMod, peSrc, peT[0], flags, pbScratch, cbScratch );              // T0 := 1 / Y
+            if( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+
+            // Get the X coordinates
+            peSrc = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poSrc );
+            SYMCRYPT_ASSERT( peSrc != NULL );
+
+            peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 0, pCurve, poDst );
+            SYMCRYPT_ASSERT( peDst != NULL );
+
+            // Set the new X
+            SymCryptModMul( pCurve->FMod, peSrc, peT[0], peDst, pbScratch, cbScratch );       // X2 := X * T0 = X/Y
+
+            if (coTo == SYMCRYPT_ECPOINT_COORDINATES_AFFINE)
+            {
+                // Set Y to 0
+                peDst = SYMCRYPT_INTERNAL_ECPOINT_COORDINATE( 1, pCurve, poDst );
+                SYMCRYPT_ASSERT( peDst != NULL );
+
+                SymCryptModElementSetValueUint32( 0, pCurve->FMod, peDst, pbScratch, cbScratch );
+            }
+        }
+    }
+    else
+    {
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointSetValue(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_reads_bytes_(cbSrc)         PCBYTE                  pbSrc,
+                                    SIZE_T                  cbSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  nformat,
+                                    SYMCRYPT_ECPOINT_FORMAT eformat,
+    _Out_                           PSYMCRYPT_ECPOINT       poDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR          scError = SYMCRYPT_NOT_IMPLEMENTED;
+    PSYMCRYPT_MODELEMENT    peTmp = NULL;       // Temporary MODELEMENT handle
+    PSYMCRYPT_ECPOINT       poLarge = NULL;     // ECPOINT with the largest format available
+    UINT32                  cbLarge = 0;
+    PSYMCRYPT_INT           piTemp = NULL;
+    UINT32                  cbTemp = 0;
+    UINT32                  publicKeyDigits = SymCryptEcurveDigitsofFieldElement( pCurve );
+
+    SYMCRYPT_ASSERT( (flags & ~SYMCRYPT_FLAG_DATA_PUBLIC) == 0 );
+
+    SYMCRYPT_ASSERT( pCurve->FMod != 0 );
+    SYMCRYPT_ASSERT( pCurve->eCoordinates != 0 );
+    SYMCRYPT_ASSERT( pCurve->cbModElement != 0 );
+
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) );
+
+    // Check that the buffer is of correct size
+    if ( cbSrc != SymCryptEcpointFormatNumberofElements[ eformat ] * SymCryptEcurveSizeofFieldElement( pCurve ) )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+    cbSrc = cbSrc / SymCryptEcpointFormatNumberofElements[ eformat ];
+
+    cbTemp = SymCryptSizeofIntFromDigits( publicKeyDigits );
+    SYMCRYPT_ASSERT( cbScratch > cbTemp );
+
+    piTemp = SymCryptIntCreate( pbScratch, cbTemp, publicKeyDigits );
+
+    // Validate the coordinate of the input public key is less than the field modulus
+    for ( UINT32 i = 0; i < SymCryptEcpointFormatNumberofElements[eformat]; i++ )
+    {
+        scError = SymCryptIntSetValue( pbSrc + i * cbSrc, cbSrc, nformat, piTemp );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        if ( !SymCryptIntIsLessThan( piTemp, SymCryptIntFromModulus( pCurve->FMod ) ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+    // Create the large point
+    cbLarge = SymCryptSizeofEcpointEx( pCurve->cbModElement, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH );
+    SYMCRYPT_ASSERT( cbScratch > cbLarge );
+    poLarge = SymCryptEcpointCreateEx( pbScratch, cbLarge, pCurve, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH );
+    if ( poLarge == NULL )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Setting the point coordinates into the big point
+    for (UINT32 i=0; i<SymCryptEcpointFormatNumberofElements[eformat]; i++)
+    {
+        peTmp = (PSYMCRYPT_MODELEMENT)((PBYTE)poLarge + SYMCRYPT_INTERNAL_ECPOINT_COORDINATE_OFFSET( pCurve, i ));
+        if ( peTmp == NULL )
+        {
+            scError = SYMCRYPT_INVALID_BLOB;
+            goto cleanup;
+        }
+
+        scError = SymCryptModElementSetValue(
+                            pbSrc,
+                            cbSrc,
+                            nformat,
+                            pCurve->FMod,
+                            peTmp,
+                            pbScratch + cbLarge,
+                            cbScratch - cbLarge );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+        pbSrc += cbSrc;
+    }
+
+    // Transform the big point into the destination point
+    scError = SymCryptEcpointTransform( pCurve, poLarge, poDst, eformat, TRUE, flags, pbScratch + cbLarge, cbScratch - cbLarge);
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointGetValue(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_                            PCSYMCRYPT_ECPOINT      poSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  nformat,
+                                    SYMCRYPT_ECPOINT_FORMAT eformat,
+    _Out_writes_bytes_(cbDst)       PBYTE                   pbDst,
+                                    SIZE_T                  cbDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR          scError = SYMCRYPT_NOT_IMPLEMENTED;
+    PSYMCRYPT_MODELEMENT    peTmp = NULL;       // Temporary MODELEMENT handle
+    PSYMCRYPT_ECPOINT       poLarge = NULL;     // ECPOINT with the largest format available
+    UINT32                  cbLarge = 0;
+    SIZE_T                  cbDstElem;
+
+    SYMCRYPT_ASSERT( (flags & ~SYMCRYPT_FLAG_DATA_PUBLIC) == 0 );
+    SYMCRYPT_ASSERT( pCurve->FMod != 0 );
+    SYMCRYPT_ASSERT( pCurve->eCoordinates != 0 );
+    SYMCRYPT_ASSERT( pCurve->cbModElement != 0 );
+
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_GETSET_VALUE_ECURVE_OPERATIONS( pCurve ) );
+
+    // Check that the buffer is of correct size
+    if ( cbDst != SymCryptEcpointFormatNumberofElements[ eformat ] * SymCryptEcurveSizeofFieldElement( pCurve ) )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+    SYMCRYPT_ASSERT( SymCryptEcpointFormatNumberofElements[ eformat ] > 0 );
+    cbDstElem = cbDst / SymCryptEcpointFormatNumberofElements[ eformat ];
+
+    // Create the big point
+    cbLarge = SymCryptSizeofEcpointEx( pCurve->cbModElement, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH );
+    SYMCRYPT_ASSERT( cbScratch > cbLarge );
+    poLarge = SymCryptEcpointCreateEx( pbScratch, cbLarge, pCurve, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH );
+    if ( poLarge == NULL )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Transform the source point into the big point if needed
+    scError = SymCryptEcpointTransform( pCurve, poSrc, poLarge, eformat, FALSE, flags, pbScratch + cbLarge, cbScratch - cbLarge);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Getting the point coordinates into the destination buffer
+    for (UINT32 i=0; i<SymCryptEcpointFormatNumberofElements[eformat]; i++)
+    {
+        SYMCRYPT_ASSERT( cbDst >= cbDstElem );
+        peTmp = (PSYMCRYPT_MODELEMENT)( (PBYTE)poLarge + SYMCRYPT_INTERNAL_ECPOINT_COORDINATE_OFFSET( pCurve, i ) );
+        if ( peTmp == NULL )
+        {
+            scError = SYMCRYPT_INVALID_BLOB;
+            goto cleanup;
+        }
+
+        scError = SymCryptModElementGetValue(
+                            pCurve->FMod,
+                            peTmp,
+                            pbDst,
+                            cbDstElem,
+                            nformat,
+                            pbScratch + cbLarge,
+                            cbScratch - cbLarge );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+        pbDst += cbDstElem;
+        cbDst -= cbDstElem;
+    }
+
+cleanup:
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ecurve.c b/libs/symcrypt/lib/ecurve.c
new file mode 100644
index 000000000000..7e5cc37f3134
--- /dev/null
+++ b/libs/symcrypt/lib/ecurve.c
@@ -0,0 +1,771 @@
+//
+// ecurve.c   Ecurve functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+// Approximate number of consecutive operations with the modulus and the
+// (sub)group order of the curve. These numbers can trigger special optimizations
+// on the underlying code, e.g. use of Montgomery multiplication or not.
+#define SYMCRYPT_INTERNAL_ECURVE_MODULUS_NUMOF_OPERATIONS( _bitsize )      ( 100 * (_bitsize) )
+#define SYMCRYPT_INTERNAL_ECURVE_GROUP_ORDER_NUMOF_OPERATIONS              ( 1 )
+
+// We limit the max size of the elliptic curve to avoid denial-of-service attacks when
+// an attacker sends a curve specification.
+// Elliptic curve operations are O(n^3) in the curve size. Theoretically SymCrypt supports
+// values up to 2^20 bits at the moment, so that is 2^12 times more than a typical curve size
+// of 256 bits. Operations are then 2^36 times slower, and a single operation could take months.
+// Our largest curve is 521 bits, and we won't see curves > 1024 bits for a while yet.
+#define SYMCRYPT_INTERNAL_MAX_ECURVE_SIZE   (1024)
+
+// Private struct which records the sizes of various different parts of the elliptic curve
+// structure.
+typedef struct _SYMCRYPT_ECURVE_SIZES {
+    UINT32                          nDigitsFieldLength;
+    UINT32                          nDigitsSubgroupOrder;
+    UINT32                          nDigitsCoFactor;
+    UINT32                          cbAlloc; // Length of the whole curve buffer
+    UINT32                          cbModulus;
+    UINT32                          cbModElement;
+    UINT32                          cbEcpoint;
+    UINT32                          cbSubgroupOrder;
+    UINT32                          cbCoFactor;
+    UINT32                          cbScratch;
+    SYMCRYPT_ECPOINT_COORDINATES    eCoordinates;
+} SYMCRYPT_ECURVE_SIZES, *PSYMCRYPT_ECURVE_SIZES;
+typedef const SYMCRYPT_ECURVE_SIZES * PCSYMCRYPT_ECURVE_SIZES;
+
+// Helper function which validates curve parameters and computes various buffer sizes.
+static
+BOOLEAN
+SymCryptEcurveValidateAndComputeSizes(
+    _In_    PCSYMCRYPT_ECURVE_PARAMS    pParams,
+    _Out_   PSYMCRYPT_ECURVE_SIZES      pSizes )
+{
+    BOOLEAN fSuccess = FALSE;
+
+    // Check that the parameters are well formatted
+    SYMCRYPT_ASSERT( pParams != NULL );
+    SYMCRYPT_ASSERT( (pParams->version == 1) || (pParams->version == 2) );
+    SYMCRYPT_ASSERT( pParams->cbFieldLength != 0 );
+    SYMCRYPT_ASSERT( pParams->cbSubgroupOrder != 0 );
+    SYMCRYPT_ASSERT( pParams->cbCofactor != 0 );
+    SYMCRYPT_ASSERT( (pParams->type == SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS) ||
+                     (pParams->type == SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS) ||
+                     (pParams->type == SYMCRYPT_ECURVE_TYPE_MONTGOMERY) );
+
+    // Reject inputs that are wildly big to avoid denial-of-service attacks.
+    if ( pParams->cbFieldLength > SYMCRYPT_INTERNAL_MAX_ECURVE_SIZE/8 ||
+         pParams->cbSubgroupOrder > SYMCRYPT_INTERNAL_MAX_ECURVE_SIZE / 8 + 1 ||     // subgroup can be > field prime
+         pParams->cbCofactor > 2 ||                                                  // We support co-factor = 256
+         pParams->cbSeed > 256 )
+    {
+        goto cleanup;
+    }
+
+    // Getting the # of digits of the various parameters
+    pSizes->nDigitsFieldLength = SymCryptDigitsFromBits( pParams->cbFieldLength * 8 );
+    pSizes->nDigitsSubgroupOrder = SymCryptDigitsFromBits( pParams->cbSubgroupOrder * 8 );
+    pSizes->nDigitsCoFactor = SymCryptDigitsFromBits( pParams->cbCofactor * 8 );
+
+    // -----------------------------------------------
+    // Getting the byte sizes of different objects
+    // -----------------------------------------------
+    pSizes->cbModulus = SymCryptSizeofModulusFromDigits( pSizes->nDigitsFieldLength );
+    pSizes->cbSubgroupOrder = SymCryptSizeofModulusFromDigits( pSizes->nDigitsSubgroupOrder );
+    pSizes->cbCoFactor =  SymCryptSizeofIntFromDigits( pSizes->nDigitsCoFactor );
+
+    pSizes->cbModElement = SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pParams->cbFieldLength * 8 );
+
+    // EcPoint: The curve is not initialized yet, we call the helper function.
+    // It depends on the default format of each curve type
+    switch (pParams->type)
+    {
+    case (SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS):
+        pSizes->eCoordinates = SYMCRYPT_ECPOINT_COORDINATES_JACOBIAN;
+        break;
+    case (SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS):
+        pSizes->eCoordinates = SYMCRYPT_ECPOINT_COORDINATES_EXTENDED_PROJECTIVE;
+        break;
+    case (SYMCRYPT_ECURVE_TYPE_MONTGOMERY):
+        pSizes->eCoordinates = SYMCRYPT_ECPOINT_COORDINATES_SINGLE_PROJECTIVE;
+        break;
+    default:
+        goto cleanup;
+    }
+
+    pSizes->cbEcpoint = SymCryptSizeofEcpointEx( pSizes->cbModElement, SYMCRYPT_INTERNAL_NUMOF_COORDINATES( pSizes->eCoordinates ) );
+    // -----------------------------------------------
+
+    // Compute memory needed for the curve
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    // Thus the following calculation does not overflow cbAlloc.
+    //
+    pSizes->cbAlloc =   sizeof( SYMCRYPT_ECURVE ) +
+                        pSizes->cbModulus +
+                        2 * pSizes->cbModElement +
+                        pSizes->cbSubgroupOrder +
+                        pSizes->cbCoFactor;
+
+    if ( (pParams->type == SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS) ||
+         (pParams->type == SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS) )
+    {
+        // If the curve's type is short Weierstrass allocate space for 2^(w-2) ECPOINTs
+        // at the end of the curve's structure, where w is the width of the window.
+        //
+        // Note: The window width is fixed now. In later versions we can pass it in as a parameter.
+        // SYMCRYPT_ASSERT( (1 << (SYMCRYPT_ECURVE_SW_DEF_WINDOW-2)) <= SYMCRYPT_ECURVE_SW_MAX_NPRECOMP_POINTS );
+        pSizes->cbAlloc += (1 << (SYMCRYPT_ECURVE_SW_DEF_WINDOW-2))*pSizes->cbEcpoint;
+    }
+    else
+    {
+        // Otherwise just allocate space for just the distinguished point
+        pSizes->cbAlloc += pSizes->cbEcpoint;
+    }
+
+    // Compute memory needed for internal scratch space
+    // EcpointSetValue and SymCryptOfflinePrecomputation
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - SymCryptSizeofEcpointEx is bounded by 2^20
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    pSizes->cbScratch = SymCryptSizeofEcpointEx( pSizes->cbModElement, SYMCRYPT_ECPOINT_FORMAT_MAX_LENGTH ) +
+                        8 * pSizes->cbModElement +
+                        SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pSizes->nDigitsFieldLength ),
+                                      SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( pSizes->nDigitsFieldLength ) );
+    // IntToModulus( FMod and GOrd )
+    pSizes->cbScratch = SYMCRYPT_MAX( pSizes->cbScratch,
+                            SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS( SYMCRYPT_MAX(pSizes->nDigitsFieldLength, pSizes->nDigitsSubgroupOrder) ) );
+    // ModElementSetValue( FMod )
+    pSizes->cbScratch = SYMCRYPT_MAX( pSizes->cbScratch,
+                            SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pSizes->nDigitsFieldLength ) );
+
+    fSuccess = TRUE;
+
+cleanup:
+    return fSuccess;
+}
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEcurveBufferSizesFromParams(
+    _In_    PCSYMCRYPT_ECURVE_PARAMS        pParams,
+    _Out_   SIZE_T *                        pcbCurve,
+    _Out_   SIZE_T *                        pcbScratch )
+{
+    BOOLEAN fSuccess = FALSE;
+    SYMCRYPT_ECURVE_SIZES sizes;
+
+    if ( !SymCryptEcurveValidateAndComputeSizes( pParams, &sizes ))
+    {
+        goto cleanup;
+    }
+
+    *pcbCurve = sizes.cbAlloc;
+    *pcbScratch = sizes.cbScratch;
+
+    fSuccess = TRUE;
+
+cleanup:
+    return fSuccess;
+}
+
+// Internal function which actually computes and writes curve into the given buffer.
+//
+// This is called internally by both SymCryptEcurveCreate() and SymCryptEcurveAllocate().
+static
+PSYMCRYPT_ECURVE
+SymCryptEcurveInitialize(
+    _In_                                    PCSYMCRYPT_ECURVE_PARAMS    pParams,
+    _In_                                    UINT32                      flags,
+    _In_                                    PCSYMCRYPT_ECURVE_SIZES     pSizes,
+    _Out_writes_bytes_( pSizes->cbAlloc )   PBYTE                       pbCurve,
+    _Out_writes_bytes_( pSizes->cbScratch)  PBYTE                       pbScratch )
+{
+    BOOLEAN         fSuccess = FALSE;
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_ECURVE pCurve = (PSYMCRYPT_ECURVE)pbCurve;
+    PBYTE            pDst = NULL;   // Destination pointer
+    PBYTE            pSrc = NULL;   // Source pointer
+
+    PBYTE            pSrcGenerator = NULL;  // We have to set the generator point
+                                            // only after we have fully initialized the curve
+
+    PSYMCRYPT_INT   pTempInt = 0;
+
+    PSYMCRYPT_MODELEMENT  peTemp = NULL;
+
+    PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION   pcParamsV2Ext = NULL;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // -----------------------------------------------
+    // Populating the fields of the curve object
+    // -----------------------------------------------
+
+    // Version of curve structure
+    pCurve->version = SYMCRYPT_INTERNAL_ECURVE_VERSION_LATEST;
+
+    // Type of curve
+    pCurve->type = (int) pParams->type;
+
+    // Curve point format
+    pCurve->eCoordinates = pSizes->eCoordinates;
+
+    // Number of digits of the field modulus
+    pCurve->FModDigits = pSizes->nDigitsFieldLength;
+
+    // Number of digits of the group order
+    pCurve->GOrdDigits = pSizes->nDigitsSubgroupOrder;
+
+    // Byte size of field elements
+    pCurve->FModBytesize = (UINT32)pParams->cbFieldLength;
+
+    // Byte size of group elements
+    SYMCRYPT_ASSERT( pParams->cbSubgroupOrder < UINT32_MAX );
+    pCurve->GOrdBytesize = (UINT32)pParams->cbSubgroupOrder;
+
+    // Byte size of mod elements
+    pCurve->cbModElement = pSizes->cbModElement;
+
+    // Total bytesize of the curve (used to free the curve object)
+    pCurve->cbAlloc = pSizes->cbAlloc;
+
+    // Set destination and source pointers
+    pDst = ((PBYTE) pCurve) + sizeof( SYMCRYPT_ECURVE );
+    pSrc = ((PBYTE) pParams) + sizeof( SYMCRYPT_ECURVE_PARAMS );
+
+    // Field Modulus
+    pCurve->FMod = SymCryptModulusCreate( pDst, pSizes->cbModulus, pSizes->nDigitsFieldLength );
+    if ( pCurve->FMod == NULL )
+    {
+        goto cleanup;
+    }
+
+    pTempInt = SymCryptIntFromModulus( pCurve->FMod );
+    if ( pTempInt == NULL)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptIntSetValue( pSrc, pParams->cbFieldLength, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pTempInt );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Field Modulus Bitsize
+    pCurve->FModBitsize = SymCryptIntBitsizeOfValue( pTempInt );
+    if (pCurve->FModBitsize < SYMCRYPT_ECURVE_MIN_BITSIZE_FMOD)
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    if( (SymCryptIntGetValueLsbits32( pTempInt ) & 1) == 0 )
+    {
+        // 'Prime' must be odd to avoid errors in conversion to modulus
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // IntToModulus requirement:
+    //      FModBitsize >= SYMCRYPT_ECURVE_MIN_BITSIZE_FMOD --> pTempInt > 0
+    SymCryptIntToModulus(
+                    pTempInt,
+                    pCurve->FMod,
+                    SYMCRYPT_INTERNAL_ECURVE_MODULUS_NUMOF_OPERATIONS( 8 * pParams->cbFieldLength ),
+                    SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+                    pbScratch,
+                    pSizes->cbScratch );
+
+    pDst += pSizes->cbModulus;
+    pSrc += pParams->cbFieldLength;
+
+    // A constant
+    pCurve->A = SymCryptModElementCreate( pDst, pSizes->cbModElement, pCurve->FMod );
+    if ( pCurve->A == NULL )
+    {
+        goto cleanup;
+    }
+    scError = SymCryptModElementSetValue(
+                    pSrc,
+                    pParams->cbFieldLength,
+                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                    pCurve->FMod,
+                    pCurve->A,
+                    pbScratch,
+                    pSizes->cbScratch );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+    pDst += pSizes->cbModElement;
+    pSrc += pParams->cbFieldLength;
+
+    // B constant
+    pCurve->B = SymCryptModElementCreate( pDst, pSizes->cbModElement, pCurve->FMod );
+    if ( pCurve->B == NULL )
+    {
+        goto cleanup;
+    }
+
+    // Detect Short-Weierstrass curves with A == -3 (NIST prime curves are all of this form)
+    // Use B's ModElement space for check
+    if( pParams->type == SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS )
+    {
+        SymCryptModElementSetValueNegUint32(
+            3,
+            pCurve->FMod,
+            pCurve->B,
+            pbScratch,
+            pSizes->cbScratch );
+        if ( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+        if( SymCryptModElementIsEqual( pCurve->FMod, pCurve->A, pCurve->B ) )
+        {
+            pCurve->type = SYMCRYPT_INTERNAL_ECURVE_TYPE_SHORT_WEIERSTRASS_AM3;
+        }
+    }
+
+    // Set B to the correct value
+    scError = SymCryptModElementSetValue(
+                    pSrc,
+                    pParams->cbFieldLength,
+                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                    pCurve->FMod,
+                    pCurve->B,
+                    pbScratch,
+                    pSizes->cbScratch );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+    pDst += pSizes->cbModElement;
+    pSrc += pParams->cbFieldLength;
+
+    // Skip over the distinguished point until we fix all the parameters and scratch space sizes
+    pSrcGenerator = pSrc;
+    pSrc += pParams->cbFieldLength * 2;
+
+    // Subgroup Order
+    pCurve->GOrd = SymCryptModulusCreate( pDst, pSizes->cbSubgroupOrder, pSizes->nDigitsSubgroupOrder );
+    if ( pCurve->GOrd == NULL )
+    {
+        goto cleanup;
+    }
+
+    pTempInt = SymCryptIntFromModulus( pCurve->GOrd );
+    if ( pTempInt == NULL)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptIntSetValue( pSrc, pParams->cbSubgroupOrder, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, pTempInt );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Subgroup Order Bitsize
+    pCurve->GOrdBitsize = SymCryptIntBitsizeOfValue( pTempInt );
+    if (pCurve->GOrdBitsize < SYMCRYPT_ECURVE_MIN_BITSIZE_GORD)
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    if( (SymCryptIntGetValueLsbits32( pTempInt ) & 1) == 0 )
+    {
+        // 'Prime' must be odd to avoid errors in conversion to modulus
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // IntToModulus requirement:
+    //      GOrdBitsize >= SYMCRYPT_ECURVE_MIN_BITSIZE_GORD --> pTempInt > 0
+    SymCryptIntToModulus(
+            pTempInt,
+            pCurve->GOrd,
+            SYMCRYPT_INTERNAL_ECURVE_GROUP_ORDER_NUMOF_OPERATIONS,
+            SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+            pbScratch,
+            pSizes->cbScratch );
+
+    pDst += pSizes->cbSubgroupOrder;
+    pSrc += pParams->cbSubgroupOrder;
+
+    // Cofactor
+    pCurve->H = SymCryptIntCreate( pDst, pSizes->cbCoFactor, pSizes->nDigitsCoFactor );
+    if ( pCurve->H == NULL )
+    {
+        goto cleanup;
+    }
+    scError = SymCryptIntSetValue(
+                    pSrc,
+                    pParams->cbCofactor,
+                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                    pCurve->H );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+
+    // Make sure that the cofactor is not zero or too big
+    pCurve->coFactorPower = SymCryptIntBitsizeOfValue( pCurve->H ) - 1;
+    if (pCurve->coFactorPower == (UINT32)-1 || pCurve->coFactorPower > SYMCRYPT_ECURVE_MAX_COFACTOR_POWER)
+    {
+        goto cleanup;
+    }
+
+    // Validate that the cofactor is a power of two
+    if (!SymCryptIntIsEqualUint32( pCurve->H, 1<<(pCurve->coFactorPower) ))
+    {
+        goto cleanup;
+    }
+
+    pDst += pSizes->cbCoFactor;
+    pSrc += pParams->cbCofactor;
+
+    // Calculate scratch spaces' sizes
+    if (pParams->type == SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS)
+    {
+        pCurve->info.sw.window = SYMCRYPT_ECURVE_SW_DEF_WINDOW;
+        pCurve->info.sw.nPrecompPoints = (1 << (SYMCRYPT_ECURVE_SW_DEF_WINDOW-2));
+        pCurve->info.sw.nRecodedDigits = pCurve->GOrdBitsize + 1;               // This is the maximum - used by the wNAF Interleaving method
+    }
+    else if ( pParams->type == SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS )
+    {
+        pCurve->info.sw.window = SYMCRYPT_ECURVE_SW_DEF_WINDOW;
+        pCurve->info.sw.nPrecompPoints = (1 << (SYMCRYPT_ECURVE_SW_DEF_WINDOW-2));
+        pCurve->info.sw.nRecodedDigits = pCurve->GOrdBitsize + 1;               // This is the maximum - used by the wNAF Interleaving method
+    }
+
+    SymCryptEcurveFillScratchSpaces(pCurve);
+
+    // Now set the distinguished point
+    pCurve->G = SymCryptEcpointCreate( pDst, pSizes->cbEcpoint, pCurve );
+    if ( pCurve->G == NULL )
+    {
+        goto cleanup;
+    }
+    scError = SymCryptEcpointSetValue(
+                    pCurve,
+                    pSrcGenerator,
+                    pParams->cbFieldLength * 2,
+                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                    SYMCRYPT_ECPOINT_FORMAT_XY,
+                    pCurve->G,
+                    SYMCRYPT_FLAG_DATA_PUBLIC,
+                    pbScratch,
+                    pSizes->cbScratch );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+    pDst += pSizes->cbEcpoint;
+
+    // Fill the precomputed table
+    if ( (pParams->type == SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS) ||
+         (pParams->type == SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS) )
+    {
+        // The first point of the table is the generator
+        pCurve->info.sw.poPrecompPoints[0] = pCurve->G;
+
+        for (UINT32 i=1; i<pCurve->info.sw.nPrecompPoints; i++)
+        {
+            pCurve->info.sw.poPrecompPoints[i] = SymCryptEcpointCreate( pDst, pSizes->cbEcpoint, pCurve );
+            if ( pCurve->info.sw.poPrecompPoints[i] == NULL )
+            {
+                goto cleanup;
+            }
+            pDst += pSizes->cbEcpoint;
+        }
+
+        SymCryptOfflinePrecomputation( pCurve, pbScratch, pSizes->cbScratch );
+    }
+
+    // For Montgomery curve, we calculate A = (A + 2) / 4
+    if (pParams->type == SYMCRYPT_ECURVE_TYPE_MONTGOMERY)
+    {
+        peTemp = SymCryptModElementCreate( pbScratch, pSizes->cbModElement, pCurve->FMod );
+
+        // SetValueUint32 requirements:
+        //  FMod > 2 since it has more than SYMCRYPT_ECURVE_MIN_BITSIZE_FMOD bits
+        SymCryptModElementSetValueUint32( 2, pCurve->FMod, peTemp, pbScratch + pSizes->cbModElement, pSizes->cbScratch - pSizes->cbModElement );
+        SymCryptModAdd (pCurve->FMod, pCurve->A, peTemp, pCurve->A, pbScratch + pSizes->cbModElement, pSizes->cbScratch - pSizes->cbModElement );   // A = A + 2;
+        SymCryptModDivPow2( pCurve->FMod, pCurve->A, 2, pCurve->A, pbScratch + pSizes->cbModElement, pSizes->cbScratch - pSizes->cbModElement );    // A = (A + 2) / 4
+    }
+
+    // Set the default curve policy for parameters of version 2
+    if (pParams->version == 2)
+    {
+        // Skip over the seed (if any)
+        pSrc += pParams->cbSeed;
+
+        // Copy the extension info (it can be unaligned)
+        pcParamsV2Ext = (PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION) pSrc;
+    }
+    else
+    {
+        // Set the defaults for version 1
+        if (pParams->type == SYMCRYPT_ECURVE_TYPE_SHORT_WEIERSTRASS)
+        {
+            pcParamsV2Ext = SymCryptEcurveParamsV2ExtensionShortWeierstrass;
+        }
+        else if ( pParams->type == SYMCRYPT_ECURVE_TYPE_TWISTED_EDWARDS )
+        {
+            pcParamsV2Ext = SymCryptEcurveParamsV2ExtensionTwistedEdwards;
+        }
+        else if ( pParams->type == SYMCRYPT_ECURVE_TYPE_MONTGOMERY )
+        {
+            pcParamsV2Ext = SymCryptEcurveParamsV2ExtensionMontgomery;
+        }
+    }
+
+    pCurve->PrivateKeyDefaultFormat = pcParamsV2Ext->PrivateKeyDefaultFormat;
+    pCurve->HighBitRestrictionNumOfBits = pcParamsV2Ext->HighBitRestrictionNumOfBits;
+    pCurve->HighBitRestrictionPosition = pcParamsV2Ext->HighBitRestrictionPosition;
+    pCurve->HighBitRestrictionValue = pcParamsV2Ext->HighBitRestrictionValue;
+
+    // Make sure that the HighBitRestrictions make sense
+    // (see SymCryptIntGet/SetBits)
+    if ( (pCurve->HighBitRestrictionNumOfBits>32) ||
+         ((pCurve->HighBitRestrictionNumOfBits>0) &&
+          (pCurve->HighBitRestrictionPosition + pCurve->HighBitRestrictionNumOfBits > pCurve->GOrdBitsize + pCurve->coFactorPower)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Setting the magic
+    SYMCRYPT_SET_MAGIC( pCurve );
+
+    fSuccess = TRUE;
+
+cleanup:
+    if (!fSuccess)
+    {
+        SymCryptWipe( pbCurve, pSizes->cbAlloc );
+        pCurve = NULL;
+    }
+
+    return pCurve;
+}
+
+PSYMCRYPT_ECURVE
+SYMCRYPT_CALL
+SymCryptEcurveCreate(
+    _In_                                PSYMCRYPT_ECURVE_PARAMS pParams,
+    _In_                                UINT32                  flags,
+    _Out_writes_bytes_( cbCurve )       PBYTE                   pbCurve,
+                                        SIZE_T                  cbCurve,
+    _Out_writes_bytes_( cbScratch )     PBYTE                   pbScratch,
+                                        SIZE_T                  cbScratch)
+{
+    SYMCRYPT_ECURVE_SIZES sizes;
+
+    PSYMCRYPT_ECURVE pCurve = NULL;
+
+    if ( !SymCryptEcurveValidateAndComputeSizes(pParams, &sizes) )
+    {
+        goto cleanup;
+    }
+
+    if ( cbCurve < sizes.cbAlloc )
+    {
+        goto cleanup;
+    }
+
+    if ( cbScratch < sizes.cbScratch )
+    {
+        goto cleanup;
+    }
+
+    pCurve = SymCryptEcurveInitialize( pParams, flags, &sizes, pbCurve, pbScratch );
+
+cleanup:
+    return pCurve;
+}
+
+PSYMCRYPT_ECURVE
+SYMCRYPT_CALL
+SymCryptEcurveAllocate(
+    _In_    PCSYMCRYPT_ECURVE_PARAMS    pParams,
+    _In_    UINT32                      flags )
+{
+    SYMCRYPT_ECURVE_SIZES sizes;
+
+    PBYTE pbCurve = NULL;
+    PBYTE pbScratch = NULL;
+
+    PSYMCRYPT_ECURVE pCurve = NULL;
+
+    if ( !SymCryptEcurveValidateAndComputeSizes(pParams, &sizes) )
+    {
+        goto cleanup;
+    }
+
+    pbCurve = SymCryptCallbackAlloc( sizes.cbAlloc );
+    if ( pbCurve == NULL )
+    {
+        goto cleanup;
+    }
+
+    pbScratch = SymCryptCallbackAlloc( sizes.cbScratch );
+    if ( pbScratch == NULL )
+    {
+        goto cleanup;
+    }
+
+    pCurve = SymCryptEcurveInitialize( pParams, flags, &sizes, pbCurve, pbScratch );
+    if ( pCurve != NULL )
+    {
+        pbCurve = NULL;
+    }
+
+cleanup:
+    if ( pbScratch != NULL )
+    {
+        SymCryptWipe( pbScratch, sizes.cbScratch );
+        SymCryptCallbackFree( pbScratch );
+    }
+
+    if ( pbCurve != NULL )
+    {
+        SymCryptCallbackFree( pbCurve );
+    }
+
+    return pCurve;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcurveFree( _Out_ PSYMCRYPT_ECURVE pCurve )
+{
+    SYMCRYPT_CHECK_MAGIC( pCurve );
+
+    SymCryptWipe( (PBYTE) pCurve, pCurve->cbAlloc );
+
+    SymCryptCallbackFree( pCurve );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveBitsizeofFieldModulus( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->FModBitsize;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveBitsizeofGroupOrder( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->GOrdBitsize;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveDigitsofFieldElement( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->FModDigits;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveSizeofFieldElement( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->FModBytesize;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveSizeofScalarMultiplier( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->GOrdBytesize;
+}
+
+PCSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptEcurveGroupOrder( _In_    PCSYMCRYPT_ECURVE   pCurve )
+{
+    return pCurve->GOrd;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveDigitsofScalarMultiplier( _In_    PCSYMCRYPT_ECURVE   pCurve )
+{
+    return SymCryptDigitsFromBits( pCurve->GOrdBitsize + pCurve->coFactorPower );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurvePrivateKeyDefaultFormat( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->PrivateKeyDefaultFormat;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveHighBitRestrictionNumOfBits( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->HighBitRestrictionNumOfBits;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveHighBitRestrictionPosition( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->HighBitRestrictionPosition;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptEcurveHighBitRestrictionValue( _In_ PCSYMCRYPT_ECURVE pCurve )
+{
+    return pCurve->HighBitRestrictionValue;
+}
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEcurveIsSame(
+    _In_    PCSYMCRYPT_ECURVE  pCurve1,
+    _In_    PCSYMCRYPT_ECURVE  pCurve2)
+{
+    BOOLEAN fIsSameCurve = FALSE;
+
+    if ( pCurve1 == pCurve2 )
+    {
+        fIsSameCurve = TRUE;
+        goto cleanup;
+    }
+
+    if ( (pCurve1->type != pCurve2->type) ||
+         !SymCryptIntIsEqual (
+              SymCryptIntFromModulus( pCurve1->FMod ),
+              SymCryptIntFromModulus( pCurve2->FMod ) ) ||
+         !SymCryptModElementIsEqual ( pCurve1->FMod, pCurve1->A, pCurve2->A ) ||
+         !SymCryptModElementIsEqual ( pCurve1->FMod, pCurve1->B, pCurve2->B ) )
+    {
+        goto cleanup;
+    }
+
+    fIsSameCurve = TRUE;
+
+cleanup:
+    return fIsSameCurve;
+}
diff --git a/libs/symcrypt/lib/env_windowsUserModeWin8_1.c b/libs/symcrypt/lib/env_windowsUserModeWin8_1.c
new file mode 100644
index 000000000000..ae66963b2648
--- /dev/null
+++ b/libs/symcrypt/lib/env_windowsUserModeWin8_1.c
@@ -0,0 +1,187 @@
+//
+// env_windowsUserMode.c
+// Platform-specific code for windows user mode.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//#include "precomp.h"
+
+#pragma warning(push)
+#pragma warning(disable: 5103) // Arm64's wdm.h included below currently generate a lot of 5103 warnings
+#include <windows.h>
+#pragma warning(pop)
+#include "symcrypt.h"
+#include "sc_lib.h"
+
+SYMCRYPT_CPU_FEATURES SYMCRYPT_CALL SymCryptCpuFeaturesNeverPresentEnvWindowsUsermodeWin8_1nLater()
+{
+    return 0;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptInitEnvWindowsUsermodeWin8_1nLater( UINT32 version )
+{
+    if( g_SymCryptFlags & SYMCRYPT_FLAG_LIB_INITIALIZED )
+    {
+        return;
+    }
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+    //
+    // First we detect what the CPU has
+    //
+    SymCryptDetectCpuFeaturesByCpuid( SYMCRYPT_CPUID_DETECT_FLAG_CHECK_OS_SUPPORT_FOR_YMM );
+
+    //
+    // We also need to be sure that the OS supports the extended registers.
+    //
+    {
+        ULONGLONG FeatureMask = GetEnabledXStateFeatures();
+
+        if( !(FeatureMask & XSTATE_MASK_AVX) )
+        {
+            g_SymCryptCpuFeaturesNotPresent |= SYMCRYPT_CPU_FEATURE_AVX2;
+        }
+
+        if( !(FeatureMask & XSTATE_MASK_AVX512) )
+        {
+            g_SymCryptCpuFeaturesNotPresent |= SYMCRYPT_CPU_FEATURE_AVX512;
+        }
+    }
+
+    //
+    // Our SaveXmm function never fails because it doesn't have to do anything in User mode.
+    //
+    g_SymCryptCpuFeaturesNotPresent &= ~SYMCRYPT_CPU_FEATURE_SAVEXMM_NOFAIL;
+
+#elif SYMCRYPT_CPU_ARM
+
+    g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) ~SYMCRYPT_CPU_FEATURE_NEON;
+
+#elif SYMCRYPT_CPU_ARM64
+
+    SymCryptDetectCpuFeaturesFromIsProcessorFeaturePresent();
+
+#endif
+
+    SymCryptInitEnvCommon( version );
+}
+
+_Analysis_noreturn_
+VOID
+SYMCRYPT_CALL
+SymCryptFatalEnvWindowsUsermodeWin8_1nLater( UINT32 fatalCode )
+{
+    UINT32 fatalCodeVar;
+
+    SymCryptFatalIntercept( fatalCode );
+
+    //
+    // Put the fatal code in a location where it shows up in the dump
+    //
+    SYMCRYPT_FORCE_WRITE32( &fatalCodeVar, fatalCode );
+
+    //
+    // Our first preference is to fastfail,
+    // the second to create an AV, which triggers a Watson report so that we get to
+    // see what is going wrong.
+    //
+    __fastfail( FAST_FAIL_CRYPTO_LIBRARY );
+
+    //
+    // Next we write to the NULL pointer, this causes an AV
+    //
+    SYMCRYPT_FORCE_WRITE32( (volatile UINT32 *)NULL, fatalCode );
+
+    //
+    // If that fails, we terminate the process. (This function call also ensures that this environment is actually
+    // used in user mode and not some other environment.)
+    // (During testing we had the TerminateProcess as the first option, but that makes debugging very hard as
+    // it leaves no traces of what went wrong.)
+    //
+    TerminateProcess( GetCurrentProcess(), fatalCode );
+
+    SymCryptFatalHang( fatalCode );
+}
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSaveXmmEnvWindowsUsermodeWin8_1nLater( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea )
+{
+    //
+    // In usermode there is no need to save XMM registers.
+    // The compiler should inline this function and optimize it away.
+    //
+
+    UNREFERENCED_PARAMETER( pSaveArea );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRestoreXmmEnvWindowsUsermodeWin8_1nLater( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea )
+{
+    //
+    // In usermode there is no need to save XMM registers.
+    // The compiler should inline this function and optimize it away.
+    //
+
+    UNREFERENCED_PARAMETER( pSaveArea );
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSaveYmmEnvWindowsUsermodeWin8_1nLater( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea )
+{
+    //
+    // In usermode there is no need to save XMM registers.
+    // The compiler should inline this function and optimize it away.
+    //
+
+    UNREFERENCED_PARAMETER( pSaveArea );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRestoreYmmEnvWindowsUsermodeWin8_1nLater( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveArea )
+{
+    //
+    // In usermode there is no need to save XMM registers.
+    // The compiler should inline this function and optimize it away.
+    //
+
+    UNREFERENCED_PARAMETER( pSaveArea );
+}
+
+#endif
+
+VOID
+SYMCRYPT_CALL
+SymCryptTestInjectErrorEnvWindowsUsermodeWin8_1nLater( PBYTE pbBuf, SIZE_T cbBuf )
+{
+    //
+    // This feature is only used during testing. In production it is always
+    // an empty function that the compiler can optimize away.
+    //
+    UNREFERENCED_PARAMETER( pbBuf );
+    UNREFERENCED_PARAMETER( cbBuf );
+}
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86
+
+VOID
+SYMCRYPT_CALL
+SymCryptCpuidExFuncEnvWindowsUsermodeWin8_1nLater( int cpuInfo[4], int function_id, int subfunction_id )
+{
+    __cpuidex( cpuInfo, function_id, subfunction_id );
+}
+
+#endif
diff --git a/libs/symcrypt/lib/equal.c b/libs/symcrypt/lib/equal.c
new file mode 100644
index 000000000000..eec4c804fb1e
--- /dev/null
+++ b/libs/symcrypt/lib/equal.c
@@ -0,0 +1,48 @@
+//
+// equal.c  Memory comparison routine that is safe against side channels.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptEqual(  _In_reads_( cbBytes )  PCBYTE pbSrc1,
+                _In_reads_( cbBytes )  PCBYTE pbSrc2,
+                                       SIZE_T cbBytes )
+{
+    UINT32 neq = 0;
+    BYTE b;
+    volatile BYTE * p1 = (volatile BYTE *) pbSrc1;
+    volatile BYTE * p2 = (volatile BYTE *) pbSrc2;
+
+    //
+    // We use forced-access memory reads to ensure that the compiler doesn't get
+    // smart and implement an early-out solution.
+    //
+
+    while( cbBytes >= 4 )
+    {
+        neq |= SYMCRYPT_FORCE_READ32( (volatile UINT32 *) p1 ) ^ SYMCRYPT_FORCE_READ32( (volatile UINT32 *) p2 );
+        p1 += 4;
+        p2 += 4;
+        cbBytes -= 4;
+    }
+
+    // We have to deal with the remaining bytes using a separate accumulator to work around an issue in the ARM64 compiler.
+    if( cbBytes > 0 )
+    {
+        b = 0;
+        while( cbBytes > 0 )
+        {
+            b |= SYMCRYPT_FORCE_READ8( p1 ) ^ SYMCRYPT_FORCE_READ8( p2 );
+            p1++;
+            p2++;
+            cbBytes--;
+        }
+        neq |= b;
+    }
+
+    return neq == 0;
+}
diff --git a/libs/symcrypt/lib/fdef369_mod.c b/libs/symcrypt/lib/fdef369_mod.c
new file mode 100644
index 000000000000..a5ac0b864a83
--- /dev/null
+++ b/libs/symcrypt/lib/fdef369_mod.c
@@ -0,0 +1,235 @@
+//
+// fdef_int.c   INT functions for default number format
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#if (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64) && defined(__WINE_PE_BUILD)
+
+#if !(SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64)
+#error code in this file is specific to normal digits being 4 64-bit words, and here 3 64-bit words
+#endif
+
+//
+// Some of the functions in this file need to know how large the values are in # UINT32s
+// On ARM64, normal digits are 256 bits and 369 digits are 192 bits, so the answer is 6 * # digits
+// On AMD64, normal digits are 512 bits, and this code is only used for 384 and 576 bits.
+//  So if nDigits = 1 then nUint32 = 12, and if nDigits=2 then nUint32 = 18.
+//
+#if SYMCRYPT_CPU_AMD64
+#define SYMCRYPT_FDEF369_DIGITS_TO_NUINT32( nD )        (((nD) + 1) * 6)
+#elif SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_FDEF369_DIGITS_TO_NUINT32( nD )        ((nD) * 6)
+#else
+#error ??
+#endif
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModAddGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 c;
+    UINT32 d;
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( cbScratch >= nDigits*SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    //
+    // Doing add/cmp/sub might be faster or not.
+    // Masked add is hard because the mask operations destroy the carry flag.
+    //
+	// dcl - cleanup?
+//    c = SymCryptFdefRawAdd( &pSrc1->uint32[0], &pSrc2->uint32[0], &pDst->uint32[0], nDigits);
+//    d = SymCryptFdefRawSub( &pDst->uint32[0], &pMod->Divisor.Int.uint32[0], &pDst->uint32[0], nDigits );
+//    e = SymCryptFdefRawMaskedAdd( &pDst->uint32[0], &pMod->Divisor.Int.uint32[0], 0 - (c^d), nDigits );
+
+    c = SymCryptFdef369RawAddAsm( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], &peDst->d.uint32[0], nDigits );
+    d = SymCryptFdef369RawSubAsm( &peDst->d.uint32[0], SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), (PUINT32) pbScratch, nDigits );
+    SymCryptFdef369MaskedCopyAsm( pbScratch, (PBYTE) &peDst->d.uint32[0], nDigits, (c^d) - 1 );
+
+    // We can't have a carry in the first addition, and no carry in the subtraction.
+    SYMCRYPT_ASSERT( !( c == 1 && d == 0 ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModSubGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 c;
+    UINT32 d;
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( cbScratch >= nDigits*SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    c = SymCryptFdef369RawSubAsm( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], &peDst->d.uint32[0], nDigits );
+    d = SymCryptFdef369RawAddAsm( &peDst->d.uint32[0], SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), (PUINT32) pbScratch, nDigits );
+    SymCryptFdef369MaskedCopyAsm( pbScratch, (PBYTE) &peDst->d.uint32[0], nDigits, 0 - c );
+
+    SYMCRYPT_ASSERT( !(c == 1 && d == 0) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModulusInitMontgomery(
+    _Inout_                         PSYMCRYPT_MODULUS       pmMod,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModulusInitMontgomeryInternal( pmMod, SYMCRYPT_FDEF369_DIGITS_TO_NUINT32( pmMod->nDigits ), pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModMulMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdef369RawMul( &peSrc1->d.uint32[0], nDigits, &peSrc2->d.uint32[0], nDigits, pTmp );
+    SymCryptFdef369MontgomeryReduce( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModSquareMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdef369ModMulMontgomery( pmMod, peSrc, peSrc, peDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModSetPostMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    // Montgomery representation for X is R*X mod M where R = 2^<nDigits * bits-per-digit>
+    // Montgomery reduction performs an implicit division by R
+    // This function converts to the internal representation by multiplying by R^2 mod M and then performing a Montgomery reduction
+    UINT32 nDigits = pmMod->nDigits;
+
+	// dcl - release mode check?
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptFdef369RawMul( &peObj->d.uint32[0], nDigits, pmMod->tm.montgomery.Rsqr, nDigits, (PUINT32) pbScratch );
+    SymCryptFdef369MontgomeryReduce( pmMod, (PUINT32) pbScratch, &peObj->d.uint32[0] );
+}
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdef369ModPreGetMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 nUint32 = SYMCRYPT_FDEF369_DIGITS_TO_NUINT32( nDigits );
+
+	// dcl - release mode check?
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    memcpy( pTmp, &peObj->d.uint32[0], nUint32 * sizeof( UINT32 ) );
+    SymCryptWipe( pTmp + nUint32, nUint32 * sizeof( UINT32 ) );
+    SymCryptFdef369MontgomeryReduce( pmMod, pTmp, pTmp );
+
+    // This gives the right result, but it isn't the size that is expected.
+    // Wipe the extra bytes
+    SymCryptWipe( pTmp + nUint32, nDigits * SYMCRYPT_FDEF_DIGIT_SIZE - nUint32 * sizeof( UINT32 ) );
+
+    return pTmp;
+}
+
+VOID
+SymCryptFdef369MontgomeryReduce(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst )
+{
+    SymCryptFdef369MontgomeryReduceAsm( pmMod, pSrc, pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369RawMul(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+{
+    SymCryptFdef369RawMulAsm( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdef369ModInvMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 nBytes = SYMCRYPT_FDEF369_DIGITS_TO_NUINT32( nDigits ) * sizeof( UINT32 );
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pTmp );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nBytes );
+
+    //
+    // We have R*X; we first apply the montgomery reduction twice to get X/R, and then invert that
+    // using the generic inversion to get R/X.
+    //
+    memcpy( pTmp, &peSrc->d.uint32[0], nBytes );
+
+    SymCryptWipe( (PBYTE)pTmp + nBytes, nBytes );
+    SymCryptFdef369MontgomeryReduce( pmMod, pTmp, pTmp );
+
+    SymCryptWipe( (PBYTE)pTmp + nBytes, nBytes );
+    SymCryptFdef369MontgomeryReduce( pmMod, pTmp, &peDst->d.uint32[0] );
+
+    scError = SymCryptFdefModInvGeneric( pmMod, peDst, peDst, flags, pbScratch, cbScratch );
+
+    return scError;
+}
+
+
+#endif // CPU_AMD64
diff --git a/libs/symcrypt/lib/fdef_general.c b/libs/symcrypt/lib/fdef_general.c
new file mode 100644
index 000000000000..1bc2509aaa23
--- /dev/null
+++ b/libs/symcrypt/lib/fdef_general.c
@@ -0,0 +1,1552 @@
+//
+// fdef_general.c   General functions of the default format.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+//
+
+#include "precomp.h"
+
+#include "smallPrimes32.h"      // For SymCryptTestTrialdivisionMaxSmallPrime
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+
+#define SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES   (16)        // Measured on amd64
+#define SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES       (2)         // Measured on amd64
+#define SYMCRYPT_RABINMILLER_DIGIT_CYCLES               (43000)     // Measured on amd64
+
+#elif SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM
+
+#define SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES   (18)        // Measured on x86
+#define SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES       (16)        // Measured on x86
+#define SYMCRYPT_RABINMILLER_DIGIT_CYCLES               (25300)     // Measured on x86
+
+#else
+
+#define SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES   (18)        // Measured on x86
+#define SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES       (16)        // Measured on x86
+#define SYMCRYPT_RABINMILLER_DIGIT_CYCLES               (25300)     // Measured on x86
+
+#endif
+
+
+#define SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME          (1<<22)     // Some large limit to bound memory usage
+C_ASSERT( SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME <= UINT32_MAX );
+C_ASSERT( SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME == ((UINT32) SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME) );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMaskedCopyC(
+    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+                                                                UINT32      nDigits,
+                                                                UINT32      mask )
+	/*
+	This function is dangerous, and would create a buffer overflow if nDigits > nDigits for pbDst
+	It also appears that it is never called. Consider removing it if it is not needed.
+	*/
+{
+    UINT64 m64 = (UINT64)0 - (mask & 1);
+    PUINT64 pSrc = (PUINT64) pbSrc; // should be a const pointer to match pSrc
+    PUINT64 pDst = (PUINT64) pbDst;
+    SIZE_T i;
+
+	// This allows 0xffffffff and 0, is that what you wanted?
+	// If so, ( mask == 0xffffffff || mask == 0 )
+	// would be more readable. It is also odd that 1 is not valid, but it results in exactly the
+	// same code flow as ~0.
+    SYMCRYPT_ASSERT( (mask + 1) < 2 );      // Check that mask is valid
+
+	// This - nDigits * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 )
+	// seems to occur often. Consider a macro with a name that explains what you are doing
+	// A comment on the macro which explains why this multiplication is never a problem would be
+	// helpful - I'm fairly sure it is not a problem.
+    for( i=0; i< nDigits * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 ); i += 2 )
+    {
+        pDst[i  ] = (pSrc[i  ] & m64) | (pDst[i  ] & ~m64 );
+        pDst[i+1] = (pSrc[i+1] & m64) | (pDst[i+1] & ~m64 );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMaskedCopy(
+    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+                                                                UINT32      nDigits,
+                                                                UINT32      mask )
+{
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbSrc );
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbDst );
+    SymCryptFdefMaskedCopyAsm( pbSrc, pbDst, nDigits, mask );
+#else
+    SymCryptFdefMaskedCopyC( pbSrc, pbDst, nDigits, mask );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefConditionalSwapC(
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbSrc1,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbSrc2,
+                                                                UINT32      nDigits,
+                                                                UINT32      cond )
+{
+	/*
+	Some documentation as to what the cond argument means would be helpful.
+	*/
+    UINT64  m64 = (UINT64)0 - (cond & 1);
+    PUINT64 pSrc1 = (PUINT64) pbSrc1;
+    PUINT64 pSrc2 = (PUINT64) pbSrc2;
+    UINT64  tmp1 = 0;
+    UINT64  tmp2 = 0;
+    SIZE_T  i;
+
+	// Unlike the previous function, this only allows 0 and 1 why?
+    SYMCRYPT_ASSERT( cond < 2 );      // Check that the condition is valid
+
+    for( i=0; i< nDigits * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 ); i += 2 )
+    {
+        tmp1 = (pSrc1[i  ] ^ pSrc2[i  ]) & m64;
+        tmp2 = (pSrc1[i+1] ^ pSrc2[i+1]) & m64;
+
+        pSrc1[i  ] ^= tmp1;    pSrc2[i  ] ^= tmp1;
+        pSrc1[i+1] ^= tmp2;    pSrc2[i+1] ^= tmp2;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefConditionalSwap(
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbSrc1,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbSrc2,
+                                                                UINT32      nDigits,
+                                                                UINT32      cond )
+{
+    SymCryptFdefConditionalSwapC( pbSrc1, pbSrc2, nDigits, cond );
+}
+
+
+UINT32
+SymCryptFdefDigitsFromBits( UINT32 nBits )
+{
+    UINT32  res;
+
+    if( nBits == 0 )
+    {
+        res = 1;
+    }
+    else
+    {
+        SYMCRYPT_ASSERT( nBits <= SYMCRYPT_INT_MAX_BITS );
+
+        // Callers with integers larger than SYMCRYPT_INT_MAX_BITS should not occur in real use cases
+        // To avoid overflow issues, return the 0 digits to indicate an error which can be handled by
+        // callers, or flow through into object allocation which will in turn recognize the invalid
+        // digit count.
+        if( nBits > SYMCRYPT_INT_MAX_BITS )
+        {
+            res = 0;
+        } else {
+            res = SYMCRYPT_FDEF_DIGITS_FROM_BITS( nBits );
+        }
+    }
+
+    return res;
+}
+
+// Let's limit max bits to the number of bits we actually test
+C_ASSERT( SYMCRYPT_INT_MAX_BITS < (1 << 30) );      // Larger values can cause overflows and sign confusion
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntAllocate( UINT32 nDigits )
+{
+    PVOID               p = NULL;
+    UINT32              cb;
+    PSYMCRYPT_INT       res = NULL;
+
+    //
+    // The nDigits requirements are enforced by SymCryptFdefSizeofIntFromDigits. Thus
+    // the result does not overflow and is upper bounded by 2^18.
+    //
+    cb = SymCryptFdefSizeofIntFromDigits( nDigits );
+
+    if( cb != 0 )
+    {
+        p = SymCryptCallbackAlloc( cb );
+    }
+
+    if( p == NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptIntCreate( p, cb, nDigits );
+
+cleanup:
+    return res;
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofIntFromDigits( UINT32 nDigits )
+{
+    SYMCRYPT_ASSERT( nDigits != 0 );
+    SYMCRYPT_ASSERT( nDigits <= SYMCRYPT_FDEF_UPB_DIGITS );
+
+    // Ensure we do not overflow the following calculation when provided with invalid inputs
+    if( nDigits == 0 || nDigits > SYMCRYPT_FDEF_UPB_DIGITS )
+    {
+        return 0;
+    }
+
+    // Note: ti stands for 'Type-Int' and it helps catch type errors when type-casting macros are used.
+    return SYMCRYPT_FIELD_OFFSET( SYMCRYPT_INT, ti ) + nDigits * SYMCRYPT_FDEF_DIGIT_SIZE;
+}
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits )
+{
+    PSYMCRYPT_INT pInt = NULL;
+    UINT32 cb = SymCryptFdefSizeofIntFromDigits( nDigits );
+
+    SYMCRYPT_ASSERT( cb >= sizeof(SYMCRYPT_INT) );
+    SYMCRYPT_ASSERT( cbBuffer >= cb );
+    if( (cb == 0) || (cbBuffer < cb) )
+    {
+        goto cleanup; // return NULL
+    }
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+    pInt = (PSYMCRYPT_INT) pbBuffer;
+
+    pInt->type = 'gI' << 16;
+    pInt->nDigits = nDigits;
+
+    //
+    // The nDigits requirements are enforced by SymCryptFdefSizeofIntFromDigits. Thus
+    // the result does not overflow and is upper bounded by 2^18.
+    //
+    pInt->cbSize = cb;
+
+    SYMCRYPT_SET_MAGIC( pInt );
+
+cleanup:
+    return pInt;
+}
+
+
+VOID
+SymCryptFdefIntCopyFixup(
+    _In_    PCSYMCRYPT_INT pSrc,
+    _Out_   PSYMCRYPT_INT  pDst )
+{
+    UNREFERENCED_PARAMETER( pSrc );
+    UNREFERENCED_PARAMETER( pDst ); // not used in FRE builds...
+
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+VOID
+SymCryptFdefIntCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+
+    //
+    // in-place copy is somewhat common, and addresses are always public, so we can test for a no-op copy.
+    //
+    if( piSrc != piDst )
+    {
+        // This is normally considered a banned, unsafe function. A note about why it is safe in this use
+        // would be good.
+        memcpy( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_FDEF_INT_PUINT32( piSrc ), SYMCRYPT_OBJ_NBYTES( piDst ));
+    }
+}
+
+VOID
+SymCryptFdefIntMaskedCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          mask )
+	/*
+	Function notes would be helpful - what is mask, what does it do?
+	*/
+{
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+
+    SymCryptFdefMaskedCopy( (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc ), (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piDst ), piSrc->nDigits, mask );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntConditionalCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          cond )
+{
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+
+    SymCryptFdefMaskedCopy( (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc ), (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piDst ), piSrc->nDigits, SYMCRYPT_MASK32_NONZERO( cond ) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntConditionalSwap(
+    _Inout_ PSYMCRYPT_INT   piSrc1,
+    _Inout_ PSYMCRYPT_INT   piSrc2,
+            UINT32          cond )
+{
+    SYMCRYPT_CHECK_MAGIC( piSrc1 );
+    SYMCRYPT_CHECK_MAGIC( piSrc2 );
+
+    SYMCRYPT_ASSERT( piSrc1->nDigits == piSrc2->nDigits );
+
+    SymCryptFdefConditionalSwap( (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), piSrc1->nDigits, cond );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntBitsizeOfObject( _In_ PCSYMCRYPT_INT  piSrc )
+{
+    // This does not overflow since the nDigits field is
+    // bounded by SYMCRYPT_FDEF_UPB_DIGITS.
+    return SYMCRYPT_FDEF_DIGIT_BITS * piSrc->nDigits;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefNumberofDigitsFromInt( _In_ PCSYMCRYPT_INT piSrc )
+{
+    return piSrc->nDigits;
+}
+
+SYMCRYPT_ERROR
+SymCryptFdefIntCopyMixedSize(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    UINT32  n;
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    // in-place copy is somewhat common, and addresses are always public, so we can test for a no-op copy.
+    if( piSrc == piDst )
+    {
+        goto cleanup;
+    }
+
+    //
+    // Copy the digits that are available in both
+    //
+    n = SYMCRYPT_MIN( piSrc->nDigits, piDst->nDigits );
+    memcpy( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_FDEF_INT_PUINT32( piSrc ), n * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    if( piDst->nDigits > n )
+    {
+        SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piDst )[n * SYMCRYPT_FDEF_DIGIT_NUINT32], (piDst->nDigits - n) * SYMCRYPT_FDEF_DIGIT_SIZE );
+    }
+
+    if( piSrc->nDigits > n )
+    {
+        // Check that the rest of the source is zero
+        PUINT64 p = (PUINT64) &SYMCRYPT_FDEF_INT_PUINT32( piSrc )[n * SYMCRYPT_FDEF_DIGIT_NUINT32];
+        UINT64 v = 0;
+        UINT32 i = (piSrc->nDigits - n) * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 );
+        while( i > 0 )
+        {
+            v |= *p++;
+            i--;
+        }
+
+        //
+        // If the Src doesn't fit, we are allowed to publish that fact, so we can use an IF.
+        //
+        if( v != 0 )
+        {
+            scError = SYMCRYPT_BUFFER_TOO_SMALL;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    return scError;
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefBitsizeOfUint32( UINT32 v )
+{
+    UINT32 res;
+    UINT32 mask;
+    UINT32 vUpper;
+    UINT32 vBit1;
+
+    // This is tricky to do side-channel safe using only defined behaviour of the C language.
+
+	// This is very difficult to make any sense of. A comment containing the C code that one would normally
+	// write to do the same thing would be helpful. I will need to come back to this.
+	// Also, there is no test coverage of this function. There should be a unit test to show that it does the same thing
+	// as the code one would normally write.
+
+    vUpper = v & 0xffff0000;
+    mask = (UINT32) ( (0 -(UINT64)(vUpper)) >> 32 );      // mask = 0 or 0xffffffff
+    res = mask & 16;                                      // Why do we want the 9th bit? Also, 0x10 would be better here
+    v = ((v & 0xffff) & ~mask) | ((vUpper >> 16) & mask);
+
+    vUpper = v & 0xff00;
+    mask = (0 - vUpper) >> 16;                           // mask = 0 or 0xffff
+    res |= mask & 8;
+    v = ((v & 0xff) & ~mask) | ((v >> 8) & mask);
+
+    vUpper = v & 0xf0;
+    mask = (0 - vUpper) >> 16;
+    res |= mask & 4;
+    v = ((v & 0xf) & ~mask) | ((v >> 4) & mask );
+
+    vUpper = v & 0xc;
+    mask = (0 - vUpper) >> 16;
+    res |= mask & 2;
+    v = ((v & 0x3) & ~mask) | ((v >> 2) & mask);
+
+    //
+    // Only 2 bits left.
+    //
+    vBit1 = (v >> 1) & 1;
+    res |= vBit1;
+
+    //
+    // Now we have the bit number of the MSbit set in res.
+    // We need to increase this by one if v was nonzero, so that we
+    // get 0 for v==0, and the # bits needed for v > 0
+    //
+    res += (v | vBit1) & 1;
+
+    return res;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntBitsizeOfValue( _In_ PCSYMCRYPT_INT piSrc )
+{
+    UINT32  nUint32 = SYMCRYPT_OBJ_NUINT32( piSrc );
+
+    UINT32  res = 0;
+    UINT32  msNonzeroWord = 0;      // most significant nonzero digit
+    UINT32  searchingMask = SYMCRYPT_MASK32_SET; // Set if still searching, 0 otherwise
+    UINT32  d;
+    UINT32  dIsNonzeroMask;
+    UINT32  foundMask;
+
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+
+	// This while loop reveals the value of nUint32, is that OK?
+	// If so, document why
+    while( nUint32 > 0 )
+    {
+        //
+        // Invariant:
+        // If no nonzero digit has been found, res = 0 and updateMask = -1.
+        // If a nonzero digit has been found:
+        //  msNonzeroDigit = most significant nonzero digit in Src
+        //  res = index where most-significant nonzero digit was found
+        //  updateMask = 0
+        //
+
+        nUint32--;
+        d = SYMCRYPT_FDEF_INT_PUINT32( piSrc )[nUint32];
+
+        dIsNonzeroMask = SYMCRYPT_MASK32_NONZERO( d );
+        foundMask = dIsNonzeroMask & searchingMask;
+        res |= nUint32 & foundMask;
+        msNonzeroWord |= d & foundMask;
+        searchingMask &= ~foundMask;
+    }
+
+    //
+    // If all words are zero, then res == 0 and msNonzeroDigit == 0.
+    //
+    res = res * 8 * sizeof( UINT32 ) + SymCryptFdefBitsizeOfUint32( msNonzeroWord );
+
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSetValueUint32(
+            UINT32          u32Src,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    SymCryptWipe( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_OBJ_NBYTES( piDst ) );
+    SYMCRYPT_FDEF_INT_PUINT32( piDst )[0] = u32Src;
+}
+
+C_ASSERT( SYMCRYPT_FDEF_DIGIT_SIZE >= 8 );      // Code below fails if this doesn't hold
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSetValueUint64(
+            UINT64          u64Src,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    SymCryptWipe( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_OBJ_NBYTES( piDst ) );
+    SYMCRYPT_FDEF_INT_PUINT32( piDst )[0] = (UINT32) u64Src;
+    SYMCRYPT_FDEF_INT_PUINT32( piDst )[1] = (UINT32)(u64Src >> 32);
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefRawSetValue(
+    _In_reads_bytes_(cbSrc)                             PCBYTE                  pbSrc,
+                                                        SIZE_T                  cbSrc,
+                                                        SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_writes_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32                 pDst,
+                                                        UINT32                  nDigits )
+{
+    SYMCRYPT_ERROR scError;
+    UINT32  b;
+    INT32   step;
+    UINT32  w;
+    UINT32  windex;
+    UINT32  i;
+    UINT32  nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    //
+    // This is a very simple and slow generic implementation;
+    // We'll create optimized versions for specific CPU platforms
+    // (e.g. use of memcpy)
+    //
+
+    // I assume the number format is public?
+    switch( format )
+    {
+    case SYMCRYPT_NUMBER_FORMAT_LSB_FIRST:
+        step = 1;
+        break;
+    case SYMCRYPT_NUMBER_FORMAT_MSB_FIRST:
+        step = -1;
+        pbSrc += cbSrc; // avoid tripping pointer overflow sanitizer with cbSrc == 0
+        pbSrc--;
+        break;
+    default:
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    for( windex = 0; windex < nWords; windex++ )
+    {
+        w = 0;
+        for( i=0; i<4; i++ )
+        {
+            // read the next byte into b
+            if( cbSrc > 0 )
+            {
+                b = *pbSrc;
+                cbSrc -= 1;
+                pbSrc += step;
+                w |= b << 8*i;
+            }
+        }
+        pDst[windex] = w;
+    }
+
+    // Inspect any remaining input bytes
+    b = 0;
+    while( cbSrc > 0 )
+    {
+        b |= *pbSrc;
+        pbSrc += step;
+        cbSrc -= 1;
+    }
+
+    if( b > 0 )
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefIntSetValue(
+    _In_reads_bytes_(cbSrc)     PCBYTE                  pbSrc,
+                                SIZE_T                  cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_                       PSYMCRYPT_INT           piDst )
+{
+    SYMCRYPT_ERROR scError;
+
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    scError = SymCryptFdefRawSetValue( pbSrc, cbSrc, format, SYMCRYPT_FDEF_INT_PUINT32( piDst ), piDst->nDigits );
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefRawGetValue(
+    _In_reads_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)   PCUINT32                pSrc,
+                                                        UINT32                  nDigits,
+    _Out_writes_bytes_(cbDst)                           PBYTE                   pbDst,
+                                                        SIZE_T                  cbDst,
+                                                        SYMCRYPT_NUMBER_FORMAT  format )
+{
+    SYMCRYPT_ERROR scError;
+    UINT32  b;
+    INT32   step;
+    UINT32  w;
+    UINT32  windex;
+    UINT32  i;
+    UINT32  nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    //
+    // This is a very simple and slow generic implementation;
+    // We'll create optimized versions for specific CPU platforms
+    // (e.g. use of memcpy)
+    //
+
+    switch( format )
+    {
+    case SYMCRYPT_NUMBER_FORMAT_LSB_FIRST:
+        step = 1;
+        break;
+    case SYMCRYPT_NUMBER_FORMAT_MSB_FIRST:
+        step = -1;
+        pbDst += cbDst; // avoid tripping pointer overflow sanitizer with cbSrc == 0
+        pbDst--;
+        break;
+    default:
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    for( windex = 0; windex < nWords; windex++ )
+    {
+        w = pSrc[windex];
+        for( i=0; i<4; i++ )
+        {
+            b = w & 0xff;
+            w >>= 8;
+
+            // write the next byte
+            if( cbDst > 0 )
+            {
+                *pbDst = (BYTE)b;
+                cbDst -= 1;
+                pbDst += step;
+            } else {
+                if( b != 0 )
+                {
+                    scError = SYMCRYPT_BUFFER_TOO_SMALL;
+                    goto cleanup;
+                }
+            }
+        }
+    }
+
+    // Zero any remaining output bytes
+    while( cbDst > 0 )
+    {
+        *pbDst = 0;
+        pbDst += step;
+        cbDst -= 1;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefIntGetValue(
+    _In_                        PCSYMCRYPT_INT          piSrc,
+    _Out_writes_bytes_(cbDst)   PBYTE                   pbDst,
+                                SIZE_T                  cbDst,
+                                SYMCRYPT_NUMBER_FORMAT  format )
+{
+    SYMCRYPT_ERROR scError;
+
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+
+    scError = SymCryptFdefRawGetValue( &SYMCRYPT_FDEF_INT_PUINT32( piSrc )[0], piSrc->nDigits, pbDst, cbDst, format );
+
+    return scError;
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntGetValueLsbits32( _In_  PCSYMCRYPT_INT piSrc )
+{
+    // nDigits cannot be zero, so we don't have to test
+    return SYMCRYPT_FDEF_INT_PUINT32( piSrc )[0];
+}
+
+UINT64
+SYMCRYPT_CALL
+SymCryptFdefIntGetValueLsbits64( _In_  PCSYMCRYPT_INT piSrc )
+{
+    // nDigits cannot be zero, so we don't have to test
+    PCUINT32 p = SYMCRYPT_FDEF_INT_PUINT32( piSrc );
+    return ((UINT64)(p[1]) << 32) | p[0];
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsEqualUint32(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PCUINT32        pSrc1,
+                                                    UINT32          nDigits,
+    _In_                                            UINT32          u32Src2 )
+{
+    UINT32 d;
+    UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    d = pSrc1[0] ^ u32Src2;
+    for( UINT32 i=1; i<nWords; i++)
+    {
+        d |= pSrc1[i];
+    }
+
+    return SYMCRYPT_MASK32_ZERO( d );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntIsEqualUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    UINT32          u32Src2 )
+{
+    return SymCryptFdefRawIsEqualUint32( &SYMCRYPT_FDEF_INT_PUINT32( piSrc1 )[0], piSrc1->nDigits, u32Src2 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntIsEqual(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 )
+{
+    UINT32 d;
+    UINT32  n1 = SYMCRYPT_OBJ_NUINT32( piSrc1 );
+    UINT32  n2 = SYMCRYPT_OBJ_NUINT32( piSrc2 );
+    UINT32  i;
+    UINT32  n;
+    PCUINT32 pSrc1 = SYMCRYPT_FDEF_INT_PUINT32( piSrc1 );
+    PCUINT32 pSrc2 = SYMCRYPT_FDEF_INT_PUINT32( piSrc2 );
+
+    n = SYMCRYPT_MIN( n1, n2 );
+    d = 0;
+    for( i=0; i < n ; i++ )
+    {
+        d |= pSrc1[i] ^ pSrc2[i];
+    }
+
+    // i == n1 or i == n2, so at most one of the 2 loops below is ever run
+
+    while( i < n1 )
+    {
+        d |= pSrc1[i];
+        i++;
+    }
+
+    while( i < n2 )
+    {
+        d |= pSrc2[i];
+        i++;
+    }
+
+    return SYMCRYPT_MASK32_ZERO( d );
+}
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorAllocate( UINT32 nDigits )
+{
+    PVOID               p = NULL;
+    UINT32              cb;
+    PSYMCRYPT_DIVISOR   res = NULL;
+
+    //
+    // The nDigits requirements are enforced by SymCryptFdefSizeofDivisorFromDigits. Thus
+    // the result does not overflow and is upper bounded by 2^19.
+    //
+    cb = SymCryptFdefSizeofDivisorFromDigits( nDigits );
+
+    if( cb != 0 )
+    {
+        p = SymCryptCallbackAlloc( cb );
+    }
+
+    if( p == NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptFdefDivisorCreate( p, cb, nDigits );
+
+cleanup:
+    return res;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofDivisorFromDigits( UINT32 nDigits )
+{
+    SYMCRYPT_ASSERT( nDigits != 0 );
+    SYMCRYPT_ASSERT( nDigits <= SYMCRYPT_FDEF_UPB_DIGITS );
+
+    // Ensure we do not overflow the following calculation when provided with invalid inputs
+    if( nDigits == 0 || nDigits > SYMCRYPT_FDEF_UPB_DIGITS )
+    {
+        return 0;
+    }
+
+    return SYMCRYPT_FIELD_OFFSET( SYMCRYPT_DIVISOR, Int ) + SymCryptFdefSizeofIntFromDigits( nDigits );
+}
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits )
+{
+    PSYMCRYPT_DIVISOR  pdDiv = NULL;
+    UINT32 cb = SymCryptSizeofDivisorFromDigits( nDigits );
+
+    SYMCRYPT_ASSERT( cb >= sizeof(SYMCRYPT_DIVISOR) );
+    SYMCRYPT_ASSERT( cbBuffer >= cb );
+    if( (cb == 0) || (cbBuffer < cb) )
+    {
+        goto cleanup; // return NULL
+    }
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+    pdDiv = (PSYMCRYPT_DIVISOR) pbBuffer;
+
+    pdDiv->type = 'gD' << 16;
+    pdDiv->nDigits = nDigits;
+
+    //
+    // The nDigits requirements are enforced by SymCryptFdefSizeofDivisorFromDigits. Thus
+    // the result does not overflow and is upper bounded by 2^19.
+    //
+    pdDiv->cbSize = cb;
+
+    SYMCRYPT_SET_MAGIC( pdDiv );
+
+    SymCryptIntCreate( (PBYTE)&pdDiv->Int, cbBuffer - SYMCRYPT_FIELD_OFFSET( SYMCRYPT_DIVISOR, Int ), nDigits );
+
+cleanup:
+    return pdDiv;
+}
+
+VOID
+SymCryptFdefDivisorCopyFixup(
+    _In_    PCSYMCRYPT_DIVISOR pdSrc,
+    _Out_   PSYMCRYPT_DIVISOR  pdDst )
+{
+    UNREFERENCED_PARAMETER( pdSrc );
+    UNREFERENCED_PARAMETER( pdDst );
+
+    SymCryptFdefIntCopyFixup( &pdSrc->Int, &pdDst->Int );
+
+    SYMCRYPT_SET_MAGIC( pdDst );
+}
+
+VOID
+SymCryptFdefDivisorCopy(
+    _In_    PCSYMCRYPT_DIVISOR  pdSrc,
+    _Out_   PSYMCRYPT_DIVISOR   pdDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pdSrc );
+    SYMCRYPT_CHECK_MAGIC( pdDst );
+
+    SYMCRYPT_ASSERT( pdSrc->nDigits == pdDst->nDigits );
+
+    // in-place copy is somewhat common, and addresses are always public, so we can test for a no-op copy.
+    if( pdSrc != pdDst )
+    {
+        memcpy( pdDst, pdSrc, pdDst->cbSize );
+
+        SymCryptFdefDivisorCopyFixup( pdSrc, pdDst );
+    }
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefClaimScratch( PBYTE pbScratch, SIZE_T cbScratch, SIZE_T cbMin )
+{
+#if SYMCRYPT_DEBUG
+    SYMCRYPT_ASSERT( cbScratch >= cbMin );
+    SymCryptWipe( pbScratch, cbMin );
+#else
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+    UNREFERENCED_PARAMETER( cbMin );
+#endif
+}
+
+UINT32
+SymCryptTestTrialdivisionMaxSmallPrime(
+    _In_                            PCSYMCRYPT_TRIALDIVISION_CONTEXT    pContext )
+{
+    return pContext->maxTrialPrime;
+}
+
+UINT64
+SymCryptInverseMod2e64( UINT64 m )
+{
+    // Compute the inv64 value such that inv64 * m = 1 mod 2^64 for odd m.
+    // If m is even, there exists no inverse, this function will return a
+    // useless value in constant time.
+    //
+    // We use Newton's method to search for a zero of f(x) := x^-1 - m, working modulo 2^64
+    // We get the iteration formula
+    //  x_{i+1} = x_i - f(x_i)/f'(x_i)
+    //          = x_i - (x_i^-1 - m)/(-x_i^-2)
+    //          = x_i + x_i^2(1/x_i - m)
+    //          = x_i + x_i - (x_i^2 * m)
+    //          = x_i (2 - x_i*m)
+    //
+    // Let x_i = d + 2^n * e where d = inv64 = m^-1 mod 2^64, and 2^n * e is the error term that is zero in the n least
+    // significant bits. We have
+    //  x_{i+1} = (d + 2^n * e) (2 - (d + 2^n * e) * m)
+    //          = (d + 2^n * e) (2 - d*m - 2^n * e * m)
+    //          = (d + 2^n * e) (2 - 1 - 2^n * e * m)
+    //          = (d + 2^n * e) (1 - 2^n * e * m)
+    //          = d - (2^n * e * (d*m)) + (2^n * e) - (2^{2n} * e^2 * m)
+    //          = d - (2^{2n} * e^2 * m)
+    // In other words, the error has been squared and multiplied by m. In our case, working modulo 2^64, the number of correct bits
+    // on the least significant side is doubled.
+    //
+    // To get a 4-bit correct estimate for m^-1 given odd m, we consider the least significant 4 bits of m and inv:
+    // m   = ... m_3 m_2 m_1 m_0
+    // inv = ... i_3 i_2 i_1 i_0
+    // We want to directly compute i_[3..0] s.t. (m*inv) & 0xf == 1
+    // working through some simple simultaneous equations it is easily shown that:
+    // i_0 = m_0 = 1
+    // i_1 = m_1
+    // i_2 = m_2
+    // i_3 = m_1 ^ m_2 ^ m_3
+    // Once we have 4 correct bits, we can double that multiple times using Newton's method.
+    //
+    // We use 32-bit operations for most of the iterations for speed on 32-bit platforms.
+    //
+    UINT32 inv32;
+    UINT64 inv64;
+    UINT32 m32;
+
+    m32 = (UINT32)m;
+
+    inv32 = m32 ^ (((m32 - 1) * 0x6) & 0x8); // sets inv32 bits [3..0]
+    SYMCRYPT_ASSERT( ((m&1) == 0) || (((inv32 * m32) & 0xf) == 1) );
+
+    inv32 = inv32 * (2 - inv32 * m32 );
+    SYMCRYPT_ASSERT( ((m&1) == 0) || (((inv32 * m32) & 0xff) == 1) );
+
+    inv32 = inv32 * (2 - inv32 * m32 );
+    SYMCRYPT_ASSERT( ((m&1) == 0) || (((inv32 * m32) & 0xffff) == 1) );
+
+    inv32 = inv32 * (2 - inv32 * m32 );
+    SYMCRYPT_ASSERT( ((m&1) == 0) || ((inv32 * m32) == 1) );
+
+    inv64 = inv32;
+    inv64 = inv64 * (2 - inv64 * m );
+    SYMCRYPT_ASSERT( ((m&1) == 0) || ((inv64 * m) == 1) );
+
+    return inv64;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefInitTrialdivisionPrime(
+            UINT32                          prime,
+    _Out_   PSYMCRYPT_TRIALDIVISION_PRIME   pPrime )
+{
+    // Compute the inverse of the prime mod 2^64
+    pPrime->invMod2e64 = SymCryptInverseMod2e64( prime );
+    pPrime->compareLimit = ((UINT64) -1) / prime;
+}
+
+FORCEINLINE
+UINT32
+SymCryptIsMultipleOfSmallPrime( UINT64 value, PCSYMCRYPT_TRIALDIVISION_PRIME pPrime )
+{
+    return (value * pPrime->invMod2e64) <= pPrime->compareLimit;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefInitTrialDivisionGroup( PSYMCRYPT_TRIALDIVISION_GROUP pGroup, UINT32 nPrimes, UINT32 primeProd )
+{
+    UINT32 f;
+    UINT32 r;
+    UINT32 i;
+
+    pGroup->nPrimes = nPrimes;
+
+    // These % operations are expensive; maybe we can optimize this further.
+    // In assembler we can do the UINT64 % UINT32 -> UINT32
+    // hopefully the compiler is smart enough...
+
+    f = (UINT32) (((UINT64)1 << 32) % primeProd);
+    pGroup->factor[0] = f;
+    r = f;
+    for( i=1; i<9; i++ )
+    {
+        r = (UINT32) (SYMCRYPT_MUL32x32TO64( r, f ) % primeProd);
+        pGroup->factor[i] = r;
+    }
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptGenerateSmallPrimes( UINT32 maxPrime, PUINT32 * ppList )
+{
+    // returns a list of small primes, excluding 2, 3, 5, and 17.
+    UINT32 nPrimes = 0;
+    PUINT32 pList = NULL;
+
+    // pSieve[i] corresponds to 2*i+1
+    // value X is in location X/2
+    UINT32 nSieve;
+    PBYTE pSieve;
+
+    UINT32 pi;
+    UINT32 p;
+    UINT32 si;
+    UINT32 i;
+
+    maxPrime = SYMCRYPT_MAX( maxPrime, 32 );         // simplify error handling by always producing primes at least up to 32
+    maxPrime = SYMCRYPT_MIN( maxPrime, 1 << 24 );    // Limit prime list to something sane (sieve = 8 MB, list = 4 MB or so).
+
+    // highest index is (maxPrime - 1)/2 which encodes maxPrime if odd, or maxPrime-1 if even
+    nSieve = (maxPrime - 1) / 2 + 1;
+
+    pSieve = SymCryptCallbackAlloc( nSieve );
+    if( pSieve == NULL )
+    {
+        goto cleanup;
+    }
+
+    SymCryptWipe( pSieve, nSieve );
+
+
+    pi = 1; // index of first prime 3
+    p = 2*pi + 1;  // prime value
+    for(;;)
+    {
+        si = 2*(pi*pi + pi);    // index of p^2
+        if( si > nSieve )
+        {
+            break;  // We're done sieving
+        }
+        while( si < nSieve )
+        {
+            pSieve[si] = 1;
+            si += p;
+        }
+        // Search for the next prime
+        do {
+            pi += 1;
+        } while( pSieve[pi] != 0 );
+        p = 2*pi + 1;
+    }
+
+    // Eliminate 3, 5, and 17
+    pSieve[1] = 1;
+    pSieve[2] = 1;
+    pSieve[8] = 1;
+
+    for( i=1; i<nSieve; i++ )
+    {
+        nPrimes += 1 - pSieve[i];
+    }
+
+	// dcl - I suspect that this is not a problem, but please document
+	// why this multiplication cannot overflow. I assume there is a practical limit on nPrimes, but unsure
+	// what that would be.
+    pList = SymCryptCallbackAlloc( nPrimes * sizeof( UINT32 ) );
+    if( pList == NULL )
+    {
+        goto cleanup;
+    }
+
+    pi = 0;
+    for( i=1; i<nSieve; i++ )
+    {
+        if( pSieve[i] == 0 )
+        {
+            pList[pi++] = 2*i+1;
+        }
+    }
+
+    SYMCRYPT_ASSERT( pi == nPrimes );
+
+cleanup:
+    if( pSieve != NULL )
+    {
+        SymCryptWipe( pSieve, nSieve );
+        SymCryptCallbackFree( pSieve );
+    }
+
+    *ppList = pList;
+    return nPrimes;
+}
+
+
+PCSYMCRYPT_TRIALDIVISION_CONTEXT
+SYMCRYPT_CALL
+SymCryptFdefCreateTrialDivisionContext( UINT32 nDigits )
+{
+    PSYMCRYPT_TRIALDIVISION_CONTEXT pRes = NULL;
+    PBYTE pAlloc;
+    UINT32 nBytes;
+    UINT32 iPrime;
+    UINT32 iGroup;
+    UINT32 nPrimes;
+    UINT32 nGroups;
+    UINT32 M;
+    UINT32 iGroupSpec;
+    UINT32 i;
+    UINT32 j;
+    UINT64 cRabinMillerCost;
+    UINT64 cPerPrimeCost;
+    UINT64 tmp64;
+    UINT32 maxPrime;
+    UINT32 minPrime;
+    UINT32 nSmallPrimes = 0;
+    UINT32 n;
+    UINT32 nP;
+    UINT32 nG;
+    PUINT32 pSmallPrimeList = NULL;
+
+    // First we estimate the largest prime we will do trial division with
+    // Inputs:
+    // - cycles/digit of reduction per group of primes
+    // - cycles/prime of divide test
+    // - cycles per digit^3 for a Rabin-Miller test
+    // We optimize in this model, which is pretty accurate for large inputs but underestimates the RM cost
+    // for smaller sizes.
+
+    // Compute the Rabin-Miller cost estimate. We reduce it by 20% because our cost model does not take
+    // into account some of the trial-division cost such as memory footprint, cache pressure,
+    // setup cost, etc. Reducing the Rabin-Miller cost leads us to do fewer trial divisions to approximately
+    // balance the hidden costs.
+
+    if( nDigits <= 1000 )
+    {
+        // nDigits is small enough to not have any overflows in this computation
+        if( nDigits == 0 )
+        {
+            goto cleanup; // return NULL
+        }
+
+        cRabinMillerCost = (UINT64) nDigits * nDigits * nDigits * (SYMCRYPT_RABINMILLER_DIGIT_CYCLES * 8 / 10);
+        i = 0;
+        minPrime = 0;
+        for(;;)
+        {
+            nPrimes = g_SymCryptSmallPrimeGroupsSpec[i].nPrimes;
+            maxPrime = g_SymCryptSmallPrimeGroupsSpec[i].maxPrime;
+            nGroups = g_SymCryptSmallPrimeGroupsSpec[i].nGroups;
+            cPerPrimeCost = (UINT64) nDigits * SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES / nPrimes + SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES;
+
+            // If the last group isn't worth it, we shouldn't go to even fewer primes
+            if( nGroups == 0 || maxPrime * cPerPrimeCost >=  cRabinMillerCost)
+            {
+                break;
+            }
+            i++;
+            minPrime = maxPrime;
+        }
+
+        // Now we know how many primes are in the last groups, let's find out how large the largest prime should be
+        tmp64 = cRabinMillerCost / cPerPrimeCost;
+        tmp64 = SYMCRYPT_MIN( tmp64, SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME );
+        maxPrime = (UINT32) tmp64;
+        maxPrime = SYMCRYPT_MAX( maxPrime, minPrime );       // Make sure we don't fall into the previous group size that we don't want
+    }
+    else
+    {
+        maxPrime = SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME;
+    }
+
+    nSmallPrimes = SymCryptGenerateSmallPrimes( maxPrime, &pSmallPrimeList );
+
+    // Find out how many groups we'll have, and how many actual primes we'll use
+    n = nSmallPrimes;
+    nG = 0;
+    nP = 0;
+    i = 0;
+    for(;;)
+    {
+        nPrimes = g_SymCryptSmallPrimeGroupsSpec[i].nPrimes;
+        nGroups = g_SymCryptSmallPrimeGroupsSpec[i].nGroups;
+
+        if( n < nPrimes * nGroups || nGroups == 0 )
+        {
+            // At the right nPrimes, compute exactly how many groups to add
+            n = n / nPrimes;
+            nG += n;
+            nP += n * nPrimes;
+            n = 0;      // No primes left
+            break;
+        }
+
+        // Use up all the groups of this size...
+        nG += nGroups;
+        nP += nPrimes * nGroups;
+        n -= nPrimes * nGroups;
+        i++;
+    }
+
+	// dcl - Potential integer overflow
+	// Need to document sizes, and limits of nG, nP, and confirm
+	// an overflow is not possible, also recall that size_t varies in size, but nBytes is 32-bit
+    nBytes = sizeof( SYMCRYPT_TRIALDIVISION_CONTEXT )
+            + (nG + 1) * sizeof( SYMCRYPT_TRIALDIVISION_GROUP )     // + 1 for 0 sentinel
+            + (nP + 1) * sizeof( SYMCRYPT_TRIALDIVISION_PRIME )     // + 1 for 0 sentinel
+            + (nP + 1) * sizeof( UINT32 );                          // + 1 for 0 sentinel
+
+    pAlloc = SymCryptCallbackAlloc( nBytes );
+    if( pAlloc == NULL )
+    {
+        goto cleanup;
+    }
+
+    pRes = (PSYMCRYPT_TRIALDIVISION_CONTEXT) pAlloc;
+    pAlloc += sizeof( *pRes );
+
+    pRes->nBytesAlloc = nBytes;
+
+    pRes->pGroupList = (PSYMCRYPT_TRIALDIVISION_GROUP)pAlloc;
+    pAlloc += (nG + 1) * sizeof( SYMCRYPT_TRIALDIVISION_GROUP );
+
+    pRes->pPrimeList = (PSYMCRYPT_TRIALDIVISION_PRIME) pAlloc;
+    pAlloc += (nP + 1) * sizeof( SYMCRYPT_TRIALDIVISION_PRIME );
+
+    pRes->pPrimes = (PUINT32) pAlloc;
+    pAlloc += (nP + 1) * sizeof( UINT32 );
+
+    SYMCRYPT_ASSERT( nBytes == (SIZE_T)(pAlloc - (PBYTE)pRes) );
+
+    // Initialize the primes 3, 5, and 17
+    SymCryptFdefInitTrialdivisionPrime(  3, &pRes->Primes3_5_17[0] );
+    SymCryptFdefInitTrialdivisionPrime(  5, &pRes->Primes3_5_17[1] );
+    SymCryptFdefInitTrialdivisionPrime( 17, &pRes->Primes3_5_17[2] );
+
+    memcpy( pRes->pPrimes, pSmallPrimeList, nP * sizeof( UINT32 ) );
+    pRes->pPrimes[nP] = 0;
+    pRes->maxTrialPrime = pRes->pPrimes[nP-1];
+
+    /*
+    *** Old code to decrypt the nibble encoding. Keep in case we want it back later...
+    // Generate the other primes from the difference table.
+    // We initialize the prime structures, and a list of the primes that is used to compute the group specs
+
+    pNibs = &g_SymCryptSmallPrimeDifferenceNibbles[0];
+
+    smallPrime = 3;
+    nPrimes = 0;
+    while( smallPrime < SYMCRYPT_MAX_SMALL_PRIME )
+    {
+        b = *pNibs++;
+        nib = b & 0xf;
+
+        if( nib == 0 )
+        {
+            smallPrime += 30;
+            // No check for termination here as we wouldn't encode a 0 if there wasn't another prime.
+        } else {
+            smallPrime += 2*nib;
+            pRes->pPrimes[nPrimes] = smallPrime;
+            SymCryptFdefInitTrialdivisionPrime( smallPrime, &pRes->pPrimeList[nPrimes] );
+            nPrimes++;
+            if( smallPrime >= SYMCRYPT_MAX_SMALL_PRIME )
+            {
+                break;
+            }
+        }
+        nib = b >> 4;
+        if( nib == 0 )
+        {
+            smallPrime += 30;
+        } else {
+            smallPrime += 2*nib;
+            pRes->pPrimes[nPrimes] = smallPrime;
+            SymCryptFdefInitTrialdivisionPrime( smallPrime, &pRes->pPrimeList[nPrimes] );
+            nPrimes++;
+        }
+    }
+    SYMCRYPT_ASSERT( smallPrime == SYMCRYPT_MAX_SMALL_PRIME && nPrimes == SYMCRYPT_N_SMALL_PRIMES_ENCODED );
+    */
+
+    for( iPrime = 0; iPrime < nP; iPrime++ )
+    {
+        SymCryptFdefInitTrialdivisionPrime( pRes->pPrimes[iPrime], &pRes->pPrimeList[iPrime] );
+    }
+
+    // Add the trailing 0s
+    pRes->pPrimeList[nP].invMod2e64 = 0;
+    pRes->pPrimeList[nP].compareLimit = 0;
+
+    // Make sure we have the 32-bit tables, not the 64-bit ones.
+	// dcl - warning suppression is not portable. Also, if it is a compile time constant, shouldn't it be a compile assert?
+#pragma warning( suppress: 4127 )       // conditional expression is constant
+    SYMCRYPT_ASSERT( SYMCRYPT_MAX_SMALL_PRIME_GROUP_PRODUCT <= (UINT32)-1 );
+
+    iGroup = 0;
+    iPrime = 0;
+    iGroupSpec = 0;
+    nPrimes = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nPrimes;
+    nGroups = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nGroups;
+    while( iPrime < nP )
+    {
+        if( nGroups == 0 )
+        {
+            iGroupSpec +=1 ;
+            nPrimes = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nPrimes;
+            nGroups = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nGroups;
+            if( nGroups == 0 )
+            {
+                nGroups = nG - iGroup;
+            }
+        }
+
+        SYMCRYPT_ASSERT( iPrime + nPrimes <= nP );
+        M = pRes->pPrimes[iPrime++];
+        for( j=1; j<nPrimes; j++ )
+        {
+            SYMCRYPT_ASSERT( M <= SYMCRYPT_MAX_SMALL_PRIME_GROUP_PRODUCT / pRes->pPrimes[iPrime] );
+            M *= pRes->pPrimes[iPrime++];
+        }
+        SymCryptFdefInitTrialDivisionGroup( &pRes->pGroupList[iGroup], nPrimes, M );
+        iGroup++;
+
+        nGroups--;
+    }
+
+    SYMCRYPT_ASSERT( iPrime == nP && iGroup == nG );
+
+    // Add the trailing sentinel group
+    pRes->pGroupList[iGroup].nPrimes = 0;
+
+cleanup:
+    if( pSmallPrimeList != NULL )
+    {
+        SymCryptWipe( pSmallPrimeList, nSmallPrimes * sizeof( UINT32 ) );
+        SymCryptCallbackFree( pSmallPrimeList );
+        pSmallPrimeList = NULL;
+    }
+    return pRes;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefFreeTrialDivisionContext( PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext )
+{
+    // No security reason to wipe it, but our test code verifies that we wipe everything...
+    // Perf cost is minor
+    SymCryptWipe( (PBYTE) pContext, pContext->nBytesAlloc );
+    SymCryptCallbackFree( (PSYMCRYPT_TRIALDIVISION_CONTEXT) pContext );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntFindSmallDivisor(
+    _In_                            PCSYMCRYPT_TRIALDIVISION_CONTEXT    pContext,
+    _In_                            PCSYMCRYPT_INT                      piSrc,
+    _Out_writes_bytes_( cbScratch ) PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch )
+{
+    PCUINT32    pSrc = SYMCRYPT_FDEF_INT_PUINT32( piSrc );
+    PCUINT32    p;
+    UINT32      nDigits = piSrc->nDigits;
+    UINT32      nUint32 = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+    UINT64      Acc;
+    PCSYMCRYPT_TRIALDIVISION_GROUP pGroup;
+    PCSYMCRYPT_TRIALDIVISION_PRIME pPrime;
+    UINT32      nPrimes;
+    UINT32      res;
+
+    // Check for 2. Not really needed for prime generation, but it makes the function easier to test/document/describe.
+    if( (*pSrc & 1) == 0 )
+    {
+        res = 2;
+        goto cleanup;
+    }
+
+    // Check the factors 3, 5, 17. These are special as they divide 2^32 - 1
+    // (We could also do 257 and 65537 but that doesn't seem worth the added complexity.)
+    Acc = 0;
+    p = pSrc;
+    do {
+#if SYMCRYPT_FDEF_DIGIT_SIZE == 16
+        Acc = Acc + p[0] + p[1] + p[2] + p[3];
+        p += 4;
+#elif (SYMCRYPT_FDEF_DIGIT_SIZE % 32) == 0
+        Acc = Acc + p[0] + p[1] + p[2] + p[3] + p[4] + p[5] + p[6] + p[7];
+        p += 8;
+#else
+		// dcl - ideally, #error would have a descriptive message so it is easily found in code if encountered, same below
+#error ??
+#endif
+    } while( p < pSrc + nUint32 );
+
+    if( SymCryptIsMultipleOfSmallPrime( Acc, &pContext->Primes3_5_17[0] ) )
+    {
+        res = 3;
+        goto cleanup;
+    }
+
+    if( SymCryptIsMultipleOfSmallPrime( Acc, &pContext->Primes3_5_17[1] ) )
+    {
+        res = 5;
+        goto cleanup;
+    }
+
+    if( SymCryptIsMultipleOfSmallPrime( Acc, &pContext->Primes3_5_17[2] ) )
+    {
+        res = 17;
+        goto cleanup;
+    }
+
+    pGroup = pContext->pGroupList;
+    pPrime = pContext->pPrimeList;
+    while( (nPrimes = pGroup->nPrimes) != 0 )
+    {
+        // Reduce Src modulo the group product to a 64-bit value
+        Acc = 0;
+        p = pSrc + nUint32;
+
+#if SYMCRYPT_FDEF_DIGIT_SIZE == 16
+        if( (nUint32 & 4) != 0 )
+        {
+            // nUInt32 is 4 mod 8, process the top 4 words only
+            p -= 4;
+            Acc =
+                p[0] +
+                SYMCRYPT_MUL32x32TO64( p[1],  pGroup->factor[0] ) +
+                SYMCRYPT_MUL32x32TO64( p[2],  pGroup->factor[1] ) +
+                SYMCRYPT_MUL32x32TO64( p[3],  pGroup->factor[2] );
+        } else {
+            // Process 8 words to start
+            p -= 8;
+            Acc =
+                p[0] +
+                SYMCRYPT_MUL32x32TO64( p[1],  pGroup->factor[0] ) +
+                SYMCRYPT_MUL32x32TO64( p[2],  pGroup->factor[1] ) +
+                SYMCRYPT_MUL32x32TO64( p[3],  pGroup->factor[2] ) +
+                SYMCRYPT_MUL32x32TO64( p[4],  pGroup->factor[3] ) +
+                SYMCRYPT_MUL32x32TO64( p[5],  pGroup->factor[4] ) +
+                SYMCRYPT_MUL32x32TO64( p[6],  pGroup->factor[5] ) +
+                SYMCRYPT_MUL32x32TO64( p[7],  pGroup->factor[6] );
+        }
+#elif (SYMCRYPT_FDEF_DIGIT_SIZE % 32) == 0
+
+        p -= 8;
+        Acc =
+            p[0] +
+            SYMCRYPT_MUL32x32TO64( p[1],  pGroup->factor[0] ) +
+            SYMCRYPT_MUL32x32TO64( p[2],  pGroup->factor[1] ) +
+            SYMCRYPT_MUL32x32TO64( p[3],  pGroup->factor[2] ) +
+            SYMCRYPT_MUL32x32TO64( p[4],  pGroup->factor[3] ) +
+            SYMCRYPT_MUL32x32TO64( p[5],  pGroup->factor[4] ) +
+            SYMCRYPT_MUL32x32TO64( p[6],  pGroup->factor[5] ) +
+            SYMCRYPT_MUL32x32TO64( p[7],  pGroup->factor[6] );
+
+#else
+#error ??
+#endif
+        while( p > pSrc )
+        {
+            p -= 8;
+            Acc =
+                p[0] +
+                SYMCRYPT_MUL32x32TO64( p[1],  pGroup->factor[0] ) +
+                SYMCRYPT_MUL32x32TO64( p[2],  pGroup->factor[1] ) +
+                SYMCRYPT_MUL32x32TO64( p[3],  pGroup->factor[2] ) +
+                SYMCRYPT_MUL32x32TO64( p[4],  pGroup->factor[3] ) +
+                SYMCRYPT_MUL32x32TO64( p[5],  pGroup->factor[4] ) +
+                SYMCRYPT_MUL32x32TO64( p[6],  pGroup->factor[5] ) +
+                SYMCRYPT_MUL32x32TO64( p[7],  pGroup->factor[6] ) +
+                SYMCRYPT_MUL32x32TO64( (UINT32) Acc       , pGroup->factor[7] ) +
+                SYMCRYPT_MUL32x32TO64( (UINT32)(Acc >> 32), pGroup->factor[8] );
+        }
+
+        // Now we check whether we have a multiple of one of the primes
+        while( nPrimes > 0 )
+        {
+            if( SymCryptIsMultipleOfSmallPrime( Acc, pPrime ) )
+            {
+                res = pContext->pPrimes[ (pPrime - pContext->pPrimeList) ];    // pointer subtraction auto-divides by size...
+                goto cleanup;
+            }
+            pPrime++;
+            nPrimes--;
+        }
+
+        pGroup++;
+    }
+
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    // Did not find a small factor, return zero
+    res = 0;
+
+cleanup:
+    return res;
+}
+
+#ifndef __WINE_PE_BUILD
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMaskedCopyAsm(
+    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+                                                                UINT32      nDigits,
+                                                                UINT32      mask )
+{
+    SymCryptFdefMaskedCopyC( pbSrc, pbDst, nDigits, mask );
+}
+
+#endif
diff --git a/libs/symcrypt/lib/fdef_int.c b/libs/symcrypt/lib/fdef_int.c
new file mode 100644
index 000000000000..0a82df5ee51f
--- /dev/null
+++ b/libs/symcrypt/lib/fdef_int.c
@@ -0,0 +1,1321 @@
+//
+// fdef_int.c   INT functions for default number format
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// Default big-number format:
+// INT objects are stored in two parts:
+//  a SYMCRYPT_FDEF_INT structure
+//  an array of UINT32; the # elements in the array is a multiple of SYMCRYPT_FDEF_DIGIT_SIZE/4.
+//
+// The pointer passed points to the start of the UINT32 array, just after the SYMCRYPT_FDEF_INT structure.
+//
+// The generic implementation accesses the digits as an array of UINT32, but on 64-bit CPUs
+// the code can also view it as an array of UINT64.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAddC(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+
+    t = 0;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        t = t + pSrc1[i] + pSrc2[i];
+        pDst[i] = (UINT32) t;
+        t >>= 32;
+    }
+
+    return (UINT32) t;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAdd(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
+    return SymCryptFdefRawAddAsm( pSrc1, pSrc2, pDst, nDigits );
+#else
+    return SymCryptFdefRawAddC( pSrc1, pSrc2, pDst, nDigits );
+#endif
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAddUint32(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+                                                            UINT32      Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+
+    t = Src2;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        t = t + Src1[i];
+        Dst[i] = (UINT32) t;
+        t >>= 32;
+    }
+
+    return (UINT32) t;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntAddUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SYMCRYPT_CHECK_MAGIC( piSrc1 );
+    SYMCRYPT_CHECK_MAGIC( piDst );
+
+    SYMCRYPT_ASSERT( piSrc1->nDigits == piDst->nDigits );
+
+    return SymCryptFdefRawAddUint32( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), u32Src2, SYMCRYPT_FDEF_INT_PUINT32( piDst ), piDst->nDigits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntAddSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    SYMCRYPT_ASSERT( piSrc1->nDigits == piSrc2->nDigits && piSrc2->nDigits == piDst->nDigits );
+
+    return SymCryptFdefRawAdd(  SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ),
+                                SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ),
+                                SYMCRYPT_FDEF_INT_PUINT32( piDst ),
+                                piDst->nDigits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntAddMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    UINT32      nS1 = piSrc1->nDigits;
+    UINT32      nS2 = piSrc2->nDigits;
+    UINT32      nD  = piDst->nDigits;
+    UINT32      c;
+    UINT32      nW;
+
+    SYMCRYPT_ASSERT( nD >= nS1 && nD >= nS2 );
+
+    if( nS1 < nS2 )
+    {
+        c = SymCryptFdefRawAdd( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), SYMCRYPT_FDEF_INT_PUINT32( piDst ), nS1 );
+        c = SymCryptFdefRawAddUint32( &SYMCRYPT_FDEF_INT_PUINT32( piSrc2 )[nS1 * SYMCRYPT_FDEF_DIGIT_NUINT32], c, &SYMCRYPT_FDEF_INT_PUINT32( piDst )[nS1 * SYMCRYPT_FDEF_DIGIT_NUINT32], nS2 - nS1 );
+        nW = nS2;
+    } else {
+        // nS2 < nS1
+        c = SymCryptFdefRawAdd( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), SYMCRYPT_FDEF_INT_PUINT32( piDst ), nS2 );
+        c = SymCryptFdefRawAddUint32( &SYMCRYPT_FDEF_INT_PUINT32( piSrc1 )[nS2 * SYMCRYPT_FDEF_DIGIT_NUINT32], c, &SYMCRYPT_FDEF_INT_PUINT32( piDst )[nS2 * SYMCRYPT_FDEF_DIGIT_NUINT32], nS1 - nS2 );
+        nW = nS1;
+    }
+
+    if( nW < nD )
+    {
+        SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piDst )[nW * SYMCRYPT_FDEF_DIGIT_NUINT32], (nD - nW) * SYMCRYPT_FDEF_DIGIT_SIZE );
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[nW * SYMCRYPT_FDEF_DIGIT_NUINT32] = c;
+        c = 0;
+    }
+
+    return c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSubC(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+    UINT32 c;
+
+    c = 0;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        // c == 1 for carry, 0 for no carry
+        t = (UINT64) pSrc1[i] - pSrc2[i] - c;
+        pDst[i] = (UINT32) t;
+        c = (UINT32)(t >> 32) & 1;
+    }
+
+    return c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSub(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
+    return SymCryptFdefRawSubAsm( pSrc1, pSrc2, pDst, nDigits );
+#else
+    return SymCryptFdefRawSubC( pSrc1, pSrc2, pDst, nDigits );
+#endif
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSubUint32(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+                                                            UINT32      Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+    UINT32 c;
+
+    c = Src2;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        t = (UINT64)pSrc1[i] - c;
+        pDst[i] = (UINT32) t;
+        c = (UINT32)(t >> 32) & 1;
+    }
+
+    return c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawNeg(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+                                                            UINT32      carryIn,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+    UINT32 c;
+
+    c = carryIn;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        t = (UINT64)0 - pSrc1[i] - c;
+        pDst[i] = (UINT32) t;
+        c = (UINT32)(t >> 32) & 1;
+    }
+
+    return c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntSubUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SYMCRYPT_ASSERT( piSrc1->nDigits == piDst->nDigits );
+
+    return SymCryptFdefRawSubUint32( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), u32Src2, SYMCRYPT_FDEF_INT_PUINT32( piDst ), piDst->nDigits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntSubSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    SYMCRYPT_ASSERT( piSrc1->nDigits == piSrc2->nDigits && piSrc1->nDigits == piDst->nDigits );
+
+    return SymCryptFdefRawSub( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), SYMCRYPT_FDEF_INT_PUINT32( piDst ), piDst->nDigits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntSubMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst )
+{
+    UINT32      nS1 = piSrc1->nDigits;
+    UINT32      nS2 = piSrc2->nDigits;
+    UINT32      nD  = piDst->nDigits;
+    UINT32      c;
+    UINT32      n;
+
+    SYMCRYPT_ASSERT( nD >= nS1 && nD >= nS2 );
+
+    if( nS1 < nS2 )
+    {
+        c = SymCryptFdefRawSub( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), SYMCRYPT_FDEF_INT_PUINT32( piDst ), nS1 );
+        c = SymCryptFdefRawNeg( &SYMCRYPT_FDEF_INT_PUINT32( piSrc2 )[nS1 * SYMCRYPT_FDEF_DIGIT_NUINT32], c, &SYMCRYPT_FDEF_INT_PUINT32( piDst )[nS1 * SYMCRYPT_FDEF_DIGIT_NUINT32], nS2 - nS1 );
+        n = nS2 * SYMCRYPT_FDEF_DIGIT_NUINT32;
+    } else {
+        // nS2 < nS1
+        c = SymCryptFdefRawSub( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), SYMCRYPT_FDEF_INT_PUINT32( piDst ), nS2 );
+        c = SymCryptFdefRawSubUint32( &SYMCRYPT_FDEF_INT_PUINT32( piSrc1 )[nS2 * SYMCRYPT_FDEF_DIGIT_NUINT32], c, &SYMCRYPT_FDEF_INT_PUINT32( piDst )[nS2 * SYMCRYPT_FDEF_DIGIT_NUINT32], nS1 - nS2 );
+        n = nS1 * SYMCRYPT_FDEF_DIGIT_NUINT32;
+    }
+
+    //
+    // Set the rest of the result to 0s or 1s
+    //
+    while( n < nD * SYMCRYPT_FDEF_DIGIT_NUINT32 )
+    {
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[n++] = 0 - c;
+    }
+
+    return c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsLessThanC(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+    UINT32 c;
+
+    // We just do a subtraction without writing and return the carry
+    c = 0;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        // c == 1 for carry, 0 for no carry
+        t = (UINT64) pSrc1[i] - pSrc2[i] - c;
+        c = (UINT32)(t >> 32) & 1;
+    }
+
+    // All booleans are returned as masks
+    return 0 - c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsLessThan(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+                                                            UINT32      nDigits )
+{
+#if 0 & SYMCRYPT_CPU_AMD64
+//    return SymCryptFdefRawIsLessThanAsm( pSrc1, pSrc2, nDigits );
+#else
+    return SymCryptFdefRawIsLessThanC( pSrc1, pSrc2, nDigits );
+#endif
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsZeroC(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT32 c;
+
+    c = 0;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        c |= pSrc1[i];
+    }
+
+    // All booleans are returned as masks
+    return SYMCRYPT_MASK32_ZERO( c );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsZero(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+                                                            UINT32      nDigits )
+{
+#if 0 & SYMCRYPT_CPU_AMD64
+//    return SymCryptFdefRawIsZeroAsm( pSrc1, nDigits );
+#else
+    return SymCryptFdefRawIsZeroC( pSrc1, nDigits );
+#endif
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntIsLessThan(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 )
+{
+    UINT32  nD1 = piSrc1->nDigits;
+    UINT32  nD2 = piSrc2->nDigits;
+
+    UINT32 res;
+
+    if( nD1 == nD2 )
+    {
+        res = SymCryptFdefRawIsLessThan( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), nD1 );
+    } else if( nD1 < nD2 ) {
+        res =  SymCryptFdefRawIsLessThan( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), nD1 );
+        res |= ~SymCryptFdefRawIsZero( &SYMCRYPT_FDEF_INT_PUINT32( piSrc2 )[ nD1 * SYMCRYPT_FDEF_DIGIT_NUINT32 ], nD2 - nD1 );
+    } else {
+        res =  SymCryptFdefRawIsLessThan( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), nD2 );
+        res &= SymCryptFdefRawIsZero( &SYMCRYPT_FDEF_INT_PUINT32( piSrc1 )[ nD2 * SYMCRYPT_FDEF_DIGIT_NUINT32 ], nD1 - nD2 );
+    }
+
+    return res;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntNeg(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    UINT32 nDigits = piDst->nDigits;
+    SYMCRYPT_ASSERT( piSrc->nDigits == nDigits );
+
+    SymCryptFdefRawNeg( SYMCRYPT_FDEF_INT_PUINT32( piSrc ), 0, SYMCRYPT_FDEF_INT_PUINT32( piDst ), nDigits );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntMulPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          Exp,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+
+    SIZE_T  shiftWords = Exp / (8 * sizeof( UINT32 ) );
+    SIZE_T  shiftBits  = Exp % (8 * sizeof( UINT32 ) );
+
+    UINT32  nWords = piDst->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    if( shiftWords >= nWords )
+    {
+        SymCryptWipe( SYMCRYPT_FDEF_INT_PUINT32( piDst ), nWords * sizeof( UINT32 ) );
+        goto cleanup;
+    }
+
+    SIZE_T i = nWords;
+    while( i > shiftWords )
+    {
+        i--;
+        UINT64 t = (UINT64)SYMCRYPT_FDEF_INT_PUINT32( piSrc )[i - shiftWords] << 32;
+        if( i > shiftWords )
+        {
+            t |= SYMCRYPT_FDEF_INT_PUINT32( piSrc )[i - shiftWords - 1];
+        }
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[i] = (UINT32)(t >> (32 - shiftBits));
+    }
+
+    while( i > 0 )
+    {
+        i--;
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[i] = 0;
+    }
+
+cleanup:
+    ;
+}
+
+// In shift-based operations which we have no assembly for, and we'd like to use 32-bit words
+// on 32-bit architectures and 64-bit words on 64-bit architectures. So we use NATIVE_UINT &
+// friends.
+
+// Note that accessing the FDEF uint32 array as an array of NATIVE_UINTs relies on
+// the little-endianness of the target if NATIVE_UINT is larger than 32 bits.
+// AMD64 is little endian and ARM64 code is always expected to execute in little
+// endian mode, but this is not true in general for an arbitrary 64 bit platform.
+//
+// If we need to support a 64 bit big endian platform, we need to either
+// restrict its NATIVE_UINT to 32 bits, or introduce load and store macros.
+#define SYMCRYPT_FDEF_INT_PNATIVE_UINT(p) ((NATIVE_UINT*) SYMCRYPT_FDEF_INT_PUINT32( p ))
+// Ensure that sizeof(NATIVE_UINT) > 4 only when compiling for known little endian target
+C_ASSERT( (NATIVE_BYTES <= 4) || SYMCRYPT_CPU_AMD64 || SYMCRYPT_CPU_ARM64 );
+
+#define SYMCRYPT_FDEF_DIGIT_NNATIVE_UINT  ((NATIVE_UINT)(SYMCRYPT_FDEF_DIGIT_SIZE / NATIVE_BYTES))
+
+// Ensure that digit is divisible by native word size!
+C_ASSERT(SYMCRYPT_FDEF_DIGIT_NNATIVE_UINT * NATIVE_BYTES == SYMCRYPT_FDEF_DIGIT_SIZE);
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntDivPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SIZE_T  shiftWords = exp / NATIVE_BITS;
+    SIZE_T  shiftRightBits  = exp % NATIVE_BITS;
+    SIZE_T  shiftLeftBits   = (NATIVE_BITS-1) - shiftRightBits;
+    NATIVE_UINT lowWord, highWord, highPart;
+    SIZE_T i = 0;
+
+    NATIVE_UINT nWords = piDst->nDigits * SYMCRYPT_FDEF_DIGIT_NNATIVE_UINT;
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+
+    shiftWords = SYMCRYPT_MIN(shiftWords, nWords);
+    if( shiftWords < nWords )
+    {
+        lowWord = SYMCRYPT_FDEF_INT_PNATIVE_UINT(piSrc)[shiftWords];
+        while( i+shiftWords+1 < nWords )
+        {
+            highWord = SYMCRYPT_FDEF_INT_PNATIVE_UINT(piSrc)[i+shiftWords+1];
+
+            // We always shift highWord left by 1 to keep variable shiftLeftBits in range [0,NATIVE_BITS-1]
+            highPart = (highWord << shiftLeftBits)<<1;
+
+            SYMCRYPT_FDEF_INT_PNATIVE_UINT(piDst)[i] = (lowWord >> shiftRightBits) | highPart;
+
+            lowWord = highWord;
+            i++;
+        }
+        SYMCRYPT_FDEF_INT_PNATIVE_UINT(piDst)[i] = (lowWord >> shiftRightBits);
+        i++;
+    }
+
+    SYMCRYPT_ASSERT(i + shiftWords == nWords);
+
+    SymCryptWipe( &SYMCRYPT_FDEF_INT_PNATIVE_UINT( piDst )[nWords-shiftWords], shiftWords * NATIVE_BYTES );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntShr1(
+            UINT32          highestBit,
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    UINT32  nWords = piDst->nDigits * SYMCRYPT_FDEF_DIGIT_NNATIVE_UINT;
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+    SYMCRYPT_ASSERT( highestBit < 2 );
+
+    SIZE_T i = 0;
+    NATIVE_UINT lowWord = SYMCRYPT_FDEF_INT_PNATIVE_UINT(piSrc)[0];
+    NATIVE_UINT highWord = 0;
+    while( i+1 < nWords )
+    {
+        highWord = SYMCRYPT_FDEF_INT_PNATIVE_UINT(piSrc)[i+1];
+
+        SYMCRYPT_FDEF_INT_PNATIVE_UINT(piDst)[i] = (lowWord >> 1) | (highWord << (NATIVE_BITS - 1));
+
+        lowWord = highWord;
+        i++;
+    }
+
+    SYMCRYPT_FDEF_INT_PNATIVE_UINT(piDst)[i] = (lowWord >> 1) | ((NATIVE_UINT)highestBit) << (NATIVE_BITS - 1);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntModPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst )
+{
+    SIZE_T  expWords = exp / 32;        // index of word with the partial mask
+    SIZE_T  expBits  = exp % 32;        // # bits to leave in that word
+
+    UINT32  nWords = piDst->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
+
+    if( piSrc != piDst )
+    {
+        memcpy( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_FDEF_INT_PUINT32( piSrc ), nWords * sizeof( UINT32 ) );
+    }
+
+    if( expWords >= nWords )
+    {
+        // exp is so large that Dst = Src is sufficient.
+        goto cleanup;
+    }
+
+    for( SIZE_T i=expWords + 1; i < nWords; i++ )
+    {
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[i] = 0;
+    }
+
+    if( expBits != 0 )
+    {
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[expWords] &= ((UINT32) -1) >> (32 - expBits );
+    } else {
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[expWords] = 0;
+    }
+
+cleanup:
+    ;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntGetBit(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit )
+{
+    SYMCRYPT_ASSERT( iBit < piSrc->nDigits * SYMCRYPT_FDEF_DIGIT_BITS );
+
+    return (((SYMCRYPT_FDEF_INT_PUINT32( piSrc)[iBit / 32]) >> (iBit % 32)) & 1);
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntGetBits(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit,
+            UINT32          nBits )
+{
+    UINT32 mainMask = 0;
+    UINT32 result = 0;
+
+    SYMCRYPT_ASSERT( (nBits > 0) &&
+                     (nBits < 33) &&
+                     (iBit < piSrc->nDigits * SYMCRYPT_FDEF_DIGIT_BITS) &&
+                     (iBit + nBits <= piSrc->nDigits * SYMCRYPT_FDEF_DIGIT_BITS) );
+
+    mainMask = (UINT32)(-1) >> (32-nBits);
+
+    // Get the lower word first (it exists since iBit is smaller than the max bit)
+    result = SYMCRYPT_FDEF_INT_PUINT32(piSrc)[iBit/32];
+
+    // Shift to the right accordingly
+    result >>= (iBit%32);
+
+    // Get the upper word (if we need it)
+    // Note: the iBit and nBits values are public
+    if ((iBit%32!=0) && ( iBit/32 + 1 < piSrc->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 ))
+    {
+        result |= ( SYMCRYPT_FDEF_INT_PUINT32(piSrc)[iBit/32+1] << (32 - iBit%32) );
+    }
+
+    // Mask out the top bits
+    result &= mainMask;
+
+    return result;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSetBits(
+    _In_    PSYMCRYPT_INT   piDst,
+            UINT32          value,
+            UINT32          iBit,
+            UINT32          nBits )
+{
+    UINT32 mainMask = 0;
+
+    UINT32 alignedVal = 0;
+    UINT32 alignedMask = 0;
+
+    SYMCRYPT_ASSERT( (nBits > 0) &&
+                     (nBits < 33) &&
+                     (iBit < piDst->nDigits * SYMCRYPT_FDEF_DIGIT_BITS) &&
+                     (iBit + nBits <= piDst->nDigits * SYMCRYPT_FDEF_DIGIT_BITS) );
+
+    // Zero out the not needed bits of the value
+    mainMask = (UINT32)(-1) >> (32-nBits);
+    value &= mainMask;
+
+    //
+    // Lower word
+    //
+
+    // Create the needed mask
+    alignedMask = mainMask << (iBit%32);
+
+    // Align the value
+    alignedVal = value << (iBit%32);
+
+    // Set the lower word first (it exists since iBit is smaller than the max bit)
+    SYMCRYPT_FDEF_INT_PUINT32(piDst)[iBit/32] = (SYMCRYPT_FDEF_INT_PUINT32(piDst)[iBit/32] & ~alignedMask) | alignedVal;
+
+    //
+    // Upper word
+    //
+
+    if ((iBit%32!=0) && ( iBit/32 + 1 < piDst->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 ))
+    {
+        // Create the needed mask
+        alignedMask = mainMask >> (32 - iBit%32);
+
+        // Align the value
+        alignedVal = value >> (32 - iBit%32);
+
+        // Set the upper word
+        SYMCRYPT_FDEF_INT_PUINT32(piDst)[iBit/32 + 1] = (SYMCRYPT_FDEF_INT_PUINT32(piDst)[iBit/32 + 1] & ~alignedMask) | alignedVal;
+    }
+
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntMulUint32(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+                                    UINT32          Src2,
+    _Out_                           PSYMCRYPT_INT   piDst )
+{
+    UINT32  nWords = piDst->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    SYMCRYPT_ASSERT( piSrc1->nDigits == piDst->nDigits );
+
+    UINT64 c = 0;
+    for( UINT32 i=0; i<nWords; i++ )
+    {
+        c += SYMCRYPT_MUL32x32TO64( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 )[i], Src2 );
+        SYMCRYPT_FDEF_INT_PUINT32( piDst )[i] = (UINT32) c;
+        c >>= 32;
+    }
+
+    return (UINT32) c;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntMulSameSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    SymCryptFdefIntMulMixedSize( piSrc1, piSrc2, piDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSquare(
+    _In_                            PCSYMCRYPT_INT  piSrc,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    UINT32      nS = piSrc->nDigits;
+    UINT32      nD = piDst->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MUL( piDst->nDigits ) );
+
+    SYMCRYPT_ASSERT( 2*nS <= nD );
+
+    SymCryptFdefRawSquare( SYMCRYPT_FDEF_INT_PUINT32( piSrc ), nS, SYMCRYPT_FDEF_INT_PUINT32( piDst ) );
+
+    if( 2*nS < nD )
+    {
+        SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piDst )[2 * nS * SYMCRYPT_FDEF_DIGIT_NUINT32], (nD - 2*nS) * SYMCRYPT_FDEF_DIGIT_SIZE );
+    }
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMulC(
+    _In_reads_(nDigits1 * SYMCRYPT_FDEF_DIGIT_NUINT32)              PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2 * SYMCRYPT_FDEF_DIGIT_NUINT32)              PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+{
+    UINT32 nWords1 = nDigits1 * SYMCRYPT_FDEF_DIGIT_NUINT32;
+    UINT32 nWords2 = nDigits2 * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    // Set Dst to zero
+    SymCryptWipe( pDst, (nDigits1+nDigits2) * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    for( UINT32 i = 0; i < nWords1; i++ )
+    {
+        UINT32 m = pSrc1[i];
+        UINT64 c = 0;
+        for( UINT32 j = 0; j < nWords2; j++ )
+        {
+            // Invariant: c < 2^32
+            c += SYMCRYPT_MUL32x32TO64( pSrc2[j], m );
+            c += pDst[i+j];
+            // There is no overflow on C because the max value is
+            // (2^32 - 1) * (2^32 - 1) + 2^32 - 1 + 2^32 - 1 = 2^64 - 1.
+            pDst[i+j] = (UINT32) c;
+            c >>= 32;
+        }
+        pDst[i + nWords2] = (UINT32) c;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMul(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_MULX ) )
+    {
+        SymCryptFdefRawMulMulx( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+    } else {
+        SymCryptFdefRawMulAsm( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+    }
+#elif SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
+    SymCryptFdefRawMulAsm( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+#else
+    SymCryptFdefRawMulC( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquareC(
+    _In_reads_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)   PCUINT32    pSrc,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst )
+{
+    UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+
+    UINT32 m = 0;
+    UINT64 c = 0;
+
+    // Set Dst to zero
+    SymCryptWipe( pDst, (2*nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    // First Pass - Addition of the cross products x_i*x_j with i!=j
+    for( UINT32 i = 0; i < nWords; i++ )
+    {
+        m = pSrc[i];
+        c = 0;
+        for( UINT32 j = i+1; j < nWords; j++ )
+        {
+            // Invariant: c < 2^32
+            c += SYMCRYPT_MUL32x32TO64( pSrc[j], m );
+            c += pDst[i+j];
+            // There is no overflow on C because the max value is
+            // (2^32 - 1) * (2^32 - 1) + 2^32 - 1 + 2^32 - 1 = 2^64 - 1.
+            pDst[i+j] = (UINT32) c;
+            c >>= 32;
+        }
+        pDst[i + nWords] = (UINT32) c;
+    }
+
+    // Second Pass - Shifting all results 1 bit left
+    c = 0;
+    for( UINT32 i = 1; i < 2*nWords; i++ )
+    {
+        c |= (((UINT64)pDst[i])<<1);
+        pDst[i] = (UINT32)c;
+        c >>= 32;
+    }
+
+    // Third Pass - Adding the squares on the even columns and propagating the sum
+    c = 0;
+    for( UINT32 i = 0; i < nWords; i++ )
+    {
+        //
+        // Even column
+        //
+        m = pSrc[i];
+        c += SYMCRYPT_MUL32x32TO64( m, m );
+        c += pDst[2*i];
+        // There is no overflow on C because the max value is
+        // (2^32 - 1) * (2^32 - 1) + 2^32 - 1 + 2^32 - 1 = 2^64 - 1
+
+        pDst[2*i] = (UINT32) c;
+        c >>= 32;
+
+        //
+        // Odd column
+        //
+        c += pDst[2*i+1];
+        // There is no overflow on C because the max value is
+        // 2^32 - 1 + 2^32 - 1 = 2^33 - 2
+
+        pDst[2*i+1] = (UINT32) c;
+        c >>= 32;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquare(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+                                                            UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_MULX ) )
+    {
+        SymCryptFdefRawSquareMulx( pSrc, nDigits, pDst );
+    } else {
+        SymCryptFdefRawSquareAsm( pSrc, nDigits, pDst );
+    }
+#elif SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
+    SymCryptFdefRawSquareAsm( pSrc, nDigits, pDst );
+#elif SYMCRYPT_CPU_X86
+    SymCryptFdefRawMulAsm( pSrc, nDigits, pSrc, nDigits, pDst );
+#else
+    SymCryptFdefRawSquareC( pSrc, nDigits, pDst );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntMulMixedSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    UINT32      nS1 = piSrc1->nDigits;
+    UINT32      nS2 = piSrc2->nDigits;
+    UINT32      nD  = piDst ->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_MUL( piDst->nDigits ) );
+
+    SYMCRYPT_ASSERT( nS1 + nS2 <= nD );
+
+    SymCryptFdefRawMul( SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), nS1, SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), nS2, SYMCRYPT_FDEF_INT_PUINT32( piDst ) );
+
+    if( nS1 + nS2 < nD )
+    {
+        SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piDst )[(nS1 + nS2) * SYMCRYPT_FDEF_DIGIT_NUINT32], (nD - (nS1 + nS2)) * SYMCRYPT_FDEF_DIGIT_SIZE );
+    }
+}
+
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntFromDivisor( _In_ PSYMCRYPT_DIVISOR pdSrc )
+{
+    return &pdSrc->Int;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntToDivisor(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_DIVISOR   pdDst,
+                                    UINT32              totalOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    UINT32      W;
+    UINT32      nBits;
+    UINT32      nWords;
+    UINT32      bitToTest;
+    UINT64      P;
+
+    UNREFERENCED_PARAMETER( totalOperations );
+    UNREFERENCED_PARAMETER( flags );
+
+    SYMCRYPT_CHECK_MAGIC( piSrc );
+    SYMCRYPT_CHECK_MAGIC( pdDst );
+
+    SYMCRYPT_ASSERT( piSrc->nDigits == pdDst->nDigits );
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( piSrc->nDigits ) );
+
+    //
+    // Copy the Int.
+    //
+    SymCryptFdefIntCopy( piSrc, &pdDst->Int );
+
+    //
+    // For an N-bit divisor M, and D-bit divisor digit size,
+    // the value W is defined as
+    //     floor( (2^{N+D} - 1) / M } - 2^D
+    // which is the largest W such that (W * M + 2^D * M )< 2^{N+D}
+    // To compute W we use a binary search.
+    // This can be optimized, but this is the simplest side-channel safe solution.
+    // We can compute the upper bits of W * M + 2^D * M in a simple loop.
+    //
+    // For now we only compute a 32-bit W for a 32-bit digit divisor size.
+    //
+
+    nBits = SymCryptIntBitsizeOfValue( &pdDst->Int );
+
+    SYMCRYPT_ASSERT( nBits != 0 );
+    if( nBits == 0 )
+    {
+        // Can't create a divisor from a Int whose value is 0
+
+        // We really should not have any callers which get here (it is a requirement that Src != 0)
+        // We assert in CHKed builds
+        // In release set the divisor to 1 instead
+        SymCryptIntSetValueUint32( 1, &pdDst->Int );
+    }
+
+    pdDst->nBits = nBits;
+
+    nWords = (nBits + 31)/32;
+    bitToTest = (UINT32)1 << 31;
+    W = 0;
+    while( bitToTest > 0 )
+    {
+        W |= bitToTest;
+        // Do the multiplication
+        P = 0;
+        for( UINT32 i=0; i<nWords; i++ )
+        {
+            // Invariant:
+            // P <= 2^{2D} - 2 which ensures the mul-add doesn't generate an overflow
+            // P  = floor( (W + 2^32)*M[0..i-1] / 2^{32*i} )
+            P += SYMCRYPT_MUL32x32TO64( W, SYMCRYPT_FDEF_INT_PUINT32( &pdDst->Int )[i] );
+            P >>= 32;
+            P += SYMCRYPT_FDEF_INT_PUINT32( &pdDst->Int )[i];
+        }
+        // We are interested in bit N+D, and P[0] is bit nWords*D, this shift brings the relevant bit to position 0
+        P >>= ((nBits+31) % 32) + 1;
+        // If the bit is 1, W*M is too large and we reset the corresponding bit in W.
+        W ^= bitToTest & (0 - ((UINT32)P & 1));
+        bitToTest >>= 1;
+    }
+    pdDst->td.fdef.W = W;
+
+    SYMCRYPT_SET_MAGIC( pdDst );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawMultSubUint32(
+    _Inout_updates_( nUint32 + 1 )  PUINT32     pAcc,
+    _In_reads_( nUint32 )           PCUINT32    pSrc1,
+                                    UINT32      Src2,
+                                    UINT32      nUint32 )
+{
+    //
+    // pAcc -= pSrc1 * Src2
+    // BEWARE: this is only used by the DivMod routine, and works in Words rather than Digits
+    // making optimizations hard.
+    //
+
+    UINT32 i;
+    UINT64 tmul;
+    UINT64 tsub;
+    UINT32 c;
+
+    tmul = 0;
+    c = 0;
+    for( i=0; i<nUint32; i++ )
+    {
+        tmul += SYMCRYPT_MUL32x32TO64( pSrc1[i], Src2 );
+        tsub  = (UINT64)pAcc[i] - (UINT32) tmul - c;
+        pAcc[i] = (UINT32) tsub;
+        c = (tsub >> 32) & 1;
+        tmul >>= 32;
+    }
+
+    // Writing the last word is strictly speaking not necessary, but a really good check that things are going right.
+    // We can remove the write, but still need the computation of c so it gains very little.
+
+    tsub = (UINT64) pAcc[i] - (UINT32) tmul - c;
+    pAcc[i] = (UINT32) tsub;
+    c = (tsub >> 32) & 1;
+
+    return c;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawMaskedAddSubdigit(
+    _Inout_updates_( nUint32 )  PUINT32     pAcc,
+    _In_reads_( nUint32 )       PCUINT32    pSrc,
+                                UINT32      mask,
+                                UINT32      nUint32 )
+{
+    UINT32 i;
+    UINT64 t;
+
+    t = 0;
+    for( i=0; i<nUint32; i++ )
+    {
+        t = t + pAcc[i] + (mask & pSrc[i]);
+        pAcc[i] = (UINT32) t;
+        t >>= 32;
+    }
+
+    return (UINT32) t;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawMaskedAdd(
+    _Inout_updates_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )  PUINT32     pAcc,
+    _In_reads_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )       PCUINT32    pSrc,
+                                                            UINT32      mask,
+                                                            UINT32      nDigits )
+{
+    return SymCryptFdefRawMaskedAddSubdigit( pAcc, pSrc, mask, nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawMaskedSub(
+    _Inout_updates_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )  PUINT32     pAcc,
+    _In_reads_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )       PCUINT32    pSrc,
+                                                            UINT32      mask,
+                                                            UINT32      nDigits )
+{
+    UINT32 i;
+    UINT64 t;
+    UINT32 c;
+
+    c = 0;
+    for( i=0; i<nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        t = (UINT64) pAcc[i] - (mask & pSrc[i]) - c;
+        pAcc[i] = (UINT32) t;
+        c = (UINT32)(t >>= 32) & 1;
+    }
+
+    return c;
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawDivMod(
+    _In_reads_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)           PCUINT32                pNum,
+                                                                UINT32                  nDigits,
+    _In_                                                        PCSYMCRYPT_DIVISOR      pdDivisor,
+    _Out_writes_opt_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32                 pQuotient,
+    _Out_writes_opt_(SYMCRYPT_OBJ_NUINT32(pdDivisor))           PUINT32                 pRemainder,
+    _Out_writes_bytes_( cbScratch )                             PBYTE                   pbScratch,
+                                                                SIZE_T                  cbScratch )
+{
+    UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+    UINT32 activeDivWords = (pdDivisor->nBits + 8 * sizeof(UINT32) - 1) / (8 * sizeof( UINT32 ) );
+    UINT32 remainderWords = SYMCRYPT_OBJ_NUINT32( pdDivisor );
+
+    UINT32 cbScratchNeeded = (nWords+4) * sizeof( UINT32 );
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32 Qest;
+    UINT32 Q;
+    UINT32 c;
+    UINT32 d;
+    UINT32 shift;
+    UINT32 X0, X1;
+    UINT32 W;
+    UINT64 T;
+    UINT32 nQ;
+
+    SYMCRYPT_ASSERT( cbScratch >= cbScratchNeeded );
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbScratch );
+
+    if( nWords < activeDivWords )
+    {
+        //
+        // input is smaller in size than the significant size of the divisor, no division to do.
+        // Note that both values in the if() statement are public, so this does not create a side channel.
+        //
+
+        // Set quotient to zero, and the remainder to the input value
+        if( pQuotient != NULL )
+        {
+            SymCryptWipe( pQuotient, nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+        }
+
+        if( pRemainder != NULL )
+        {
+            SYMCRYPT_ASSERT( remainderWords >= nWords );
+            memcpy( pRemainder, pNum, nWords * sizeof( UINT32 ) );
+            SymCryptWipe( &pRemainder[nWords], (remainderWords - nWords) * sizeof( UINT32 ) );        // clear the rest of the remainder words
+        }
+
+        SymCryptFdefClaimScratch( pbScratch, cbScratch, cbScratchNeeded );
+        goto cleanup;
+    }
+
+    //
+    // We have two zero words in front and two zero words behind the tmp value to allow unrestricted accesses.
+    // We keep the explicit offset of 2 rather than adjust the pTmp pointer to avoid negative indexes which appear
+    // to be buffer overflows, and cause trouble with unsigned computations of negative index values that overflow
+    // to 2^32 - 1 on a 64-bit CPU.
+    //
+    pTmp[0] = pTmp[1] = 0;
+    memcpy( &pTmp[2], pNum, nWords * sizeof( UINT32 ) );
+    pTmp[nWords + 2] = pTmp[nWords + 3] = 0;
+    shift = (0 - pdDivisor->nBits) & 31;   // # bits we have to shift top words to the left to align with the W value
+
+    // We generate the quotient words one at a time, starting at the most significant position
+    // The top (divWords - 1) words are always zero
+
+    if( pQuotient != NULL )
+    {
+        SymCryptWipe( &pQuotient[nWords - activeDivWords + 1], (activeDivWords - 1) * sizeof( UINT32 ) );
+    }
+
+    nQ = nWords - activeDivWords + 1;
+
+    // There is always at least one word of Q to be computed, so we can use a do-while loop which
+    // also avoids the UINT32 underflow.
+    do
+    {
+        nQ--;
+        X0 = ( ((UINT64) pTmp[nQ + activeDivWords + 2] << 32) + pTmp[nQ + activeDivWords + 1] ) >> (32 - shift);
+        X1 = ( ((UINT64) pTmp[nQ + activeDivWords + 1] << 32) + pTmp[nQ + activeDivWords + 0] ) >> (32 - shift);
+
+        W = (UINT32) pdDivisor->td.fdef.W;
+        T = SYMCRYPT_MUL32x32TO64( W, X0 ) + (((UINT64)X0) << 32) + X1 + ((W>>1) & ((UINT32)0 - (X1 >> 31)));
+        Qest = (UINT32)(T >> 32);
+        // At this point the estimator is correct or one too small, add one but don't overflow
+        Qest += 1;
+        Qest += SYMCRYPT_MASK32_ZERO( Qest );
+
+        c = SymCryptFdefRawMultSubUint32( &pTmp[nQ+2], SYMCRYPT_FDEF_INT_PUINT32( &pdDivisor->Int ), Qest, activeDivWords );
+        Q = Qest - c;
+        d = SymCryptFdefRawMaskedAddSubdigit( &pTmp[nQ+2], SYMCRYPT_FDEF_INT_PUINT32( &pdDivisor->Int ), (0-c), activeDivWords );
+        SYMCRYPT_ASSERT( c == d );
+        SYMCRYPT_ASSERT( pTmp[nQ + activeDivWords+2] == (0 - c) );
+
+        if( pQuotient != NULL )
+        {
+            pQuotient[nQ] = Q;
+        }
+    } while( nQ > 0 );
+
+    if( pRemainder != NULL )
+    {
+        memcpy( pRemainder, pTmp+2, activeDivWords * sizeof( UINT32 ) );
+        SymCryptWipe( &pRemainder[activeDivWords], (remainderWords - activeDivWords) * sizeof( UINT32 ) );
+    }
+
+cleanup:
+    return;         // label needs a statement to follow it...
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntDivMod(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _In_                            PCSYMCRYPT_DIVISOR  pdDivisor,
+    _Out_opt_                       PSYMCRYPT_INT       piQuotient,
+    _Out_opt_                       PSYMCRYPT_INT       piRemainder,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    UINT32  nDigits = SYMCRYPT_OBJ_NDIGITS( piSrc );
+
+    SYMCRYPT_ASSERT( piQuotient  == NULL || piQuotient->nDigits >= piSrc->nDigits );
+    SYMCRYPT_ASSERT( piRemainder == NULL || piRemainder->nDigits >= pdDivisor->nDigits );
+
+    SymCryptFdefRawDivMod(
+        SYMCRYPT_FDEF_INT_PUINT32( piSrc ),
+        nDigits,
+        pdDivisor,
+        piQuotient  == NULL ? NULL : SYMCRYPT_FDEF_INT_PUINT32( piQuotient ),
+        piRemainder == NULL ? NULL : SYMCRYPT_FDEF_INT_PUINT32( piRemainder ),
+        pbScratch,
+        cbScratch
+        );
+
+    if ((piQuotient != NULL) && (piQuotient->nDigits > piSrc->nDigits))
+    {
+        SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piQuotient )[piSrc->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32], (piQuotient->nDigits - piSrc->nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE );
+    }
+
+    if ((piRemainder != NULL) && (piRemainder->nDigits > pdDivisor->nDigits))
+    {
+        SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piRemainder )[pdDivisor->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32], (piRemainder->nDigits - pdDivisor->nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE );
+    }
+}
+
+#ifndef __WINE_PE_BUILD
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAddAsm(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+                                                            UINT32      nDigits )
+{
+    return SymCryptFdefRawAddC( Src1, Src2, Dst, nDigits );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSubAsm(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits )
+{
+    return SymCryptFdefRawSubC( pSrc1, pSrc2, pDst, nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMulAsm(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+{
+    SymCryptFdefRawMulC( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquareAsm(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst )
+{
+    SymCryptFdefRawSquareC( pSrc, nDigits, pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquareMulx(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+                                                            UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst )
+{
+    SymCryptFdefRawSquareC( pSrc, nDigits, pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMulMulx(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst )
+{
+    SymCryptFdefRawMulC( pSrc1, nDigits1, pSrc2, nDigits2, pDst );
+}
+
+#endif
diff --git a/libs/symcrypt/lib/fdef_mod.c b/libs/symcrypt/lib/fdef_mod.c
new file mode 100644
index 000000000000..27e8f8a69c3b
--- /dev/null
+++ b/libs/symcrypt/lib/fdef_mod.c
@@ -0,0 +1,1733 @@
+//
+// fdef_int.c   INT functions for default number format
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptFdefModulusAllocate( UINT32 nDigits )
+{
+    PVOID               p = NULL;
+    UINT32              cb;
+    PSYMCRYPT_MODULUS   res = NULL;
+
+    //
+    // The nDigits requirements are enforced by SymCryptFdefSizeofModulusFromDigits. Thus
+    // the result does not overflow and is upper bounded by 2^19.
+    //
+    cb = SymCryptFdefSizeofModulusFromDigits( nDigits );
+
+    if( cb != 0 )
+    {
+        p = SymCryptCallbackAlloc( cb );
+    }
+
+    if( p == NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptFdefModulusCreate( p, cb, nDigits );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusFree( _Out_ PSYMCRYPT_MODULUS pmObj )
+{
+    SymCryptModulusWipe( pmObj );
+    SymCryptCallbackFree( pmObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofModulusFromDigits( UINT32 nDigits )
+{
+    SYMCRYPT_ASSERT( nDigits != 0 );
+    SYMCRYPT_ASSERT( nDigits <= SYMCRYPT_FDEF_UPB_DIGITS );
+
+    // Ensure we do not overflow the following calculation when provided with invalid inputs
+    if( nDigits == 0 || nDigits > SYMCRYPT_FDEF_UPB_DIGITS )
+    {
+        return 0;
+    }
+
+    // Room for the Modulus structure, the Divisor, the negated divisor, and the R^2 Montgomery factor
+    //
+    return SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor ) + SymCryptFdefSizeofDivisorFromDigits( nDigits ) + (2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE);
+}
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptFdefModulusCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits )
+{
+    PSYMCRYPT_MODULUS pmMod = NULL;
+    UINT32 cb = SymCryptFdefSizeofModulusFromDigits( nDigits );
+
+    const UINT32 offset = SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MODULUS, Divisor );
+
+    SYMCRYPT_ASSERT( cb >= sizeof(SYMCRYPT_MODULUS) );
+    SYMCRYPT_ASSERT( cbBuffer >= cb );
+    if( (cb == 0) || (cbBuffer < cb) )
+    {
+        goto cleanup; // return NULL
+    }
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+    pmMod = (PSYMCRYPT_MODULUS) pbBuffer;
+
+    pmMod->type = 'gM' << 16;
+    pmMod->nDigits = nDigits;
+
+    //
+    // The nDigits requirements are enforced by SymCryptFdefSizeofModulusFromDigits. Thus
+    // the result does not overflow and is upper bounded by 2^19.
+    //
+    pmMod->cbSize = cb;
+    pmMod->flags = 0;
+
+    // The following is bounded by 2^17
+    pmMod->cbModElement = nDigits * SYMCRYPT_FDEF_DIGIT_SIZE;
+
+    SymCryptFdefDivisorCreate( pbBuffer + offset, cbBuffer - offset, nDigits );
+
+    // We don't have a modulus value yet, so we don't create/initialize any implementation-specific things.
+
+    SYMCRYPT_SET_MAGIC( pmMod );
+
+cleanup:
+    return pmMod;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitGeneric(
+    _Inout_                         PSYMCRYPT_MODULUS       pmMod,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UNREFERENCED_PARAMETER( pmMod );
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+}
+
+
+VOID
+SymCryptFdefModulusCopy(
+    _In_    PCSYMCRYPT_MODULUS  pmSrc,
+    _Out_   PSYMCRYPT_MODULUS   pmDst )
+{
+    SYMCRYPT_ASSERT( pmSrc->nDigits == pmDst->nDigits );
+
+    if( pmSrc != pmDst )
+    {
+        memcpy( pmDst, pmSrc, pmDst->cbSize );
+
+        SymCryptFdefDivisorCopyFixup( &pmSrc->Divisor, &pmDst->Divisor );
+
+        // Copy the type-specific fields
+        SYMCRYPT_MOD_CALL( pmSrc ) modulusCopyFixup( pmSrc, pmDst );
+
+        SYMCRYPT_SET_MAGIC( pmDst );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusCopyFixupGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmSrc,
+    _Out_                           PSYMCRYPT_MODULUS       pmDst )
+{
+    // Only have to handle the type-specific fields, which we don't have any of.
+    UNREFERENCED_PARAMETER( pmSrc );
+    UNREFERENCED_PARAMETER( pmDst );
+}
+
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptFdefModElementAllocate( _In_ PCSYMCRYPT_MODULUS pmMod )
+{
+    PVOID                   p;
+    UINT32                  cb;
+    PSYMCRYPT_MODELEMENT    res = NULL;
+
+    //
+    // The nDigits requirements are enforced by the modulus object. Thus
+    // the result does not overflow and is upper bounded by 2^17.
+    //
+    cb = SymCryptFdefSizeofModElementFromModulus( pmMod );
+
+    p = SymCryptCallbackAlloc( cb );
+
+    if( p == NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptFdefModElementCreate( p, cb, pmMod );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementFree(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peObj )
+{
+    SymCryptFdefModElementWipe( pmMod, peObj );
+    SymCryptCallbackFree( peObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofModElementFromModulus( PCSYMCRYPT_MODULUS pmMod )
+{
+    // Upper bounded by 2^17 since the modulus is up to SYMCRYPT_INT_MAXBITS = 2^20 bits.
+    return pmMod->cbModElement;
+}
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptFdefModElementCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    PCSYMCRYPT_MODULUS  pmMod )
+{
+    PSYMCRYPT_MODELEMENT pDst = (PSYMCRYPT_MODELEMENT) pbBuffer;
+
+    UNREFERENCED_PARAMETER( pmMod );
+    UNREFERENCED_PARAMETER( cbBuffer );
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+    SYMCRYPT_ASSERT( cbBuffer >= SymCryptFdefSizeofModElementFromModulus( pmMod ) );
+    SYMCRYPT_ASSERT( cbBuffer >= pmMod->nDigits*SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    //
+    // We have various optimizations where we use only part of the last digit
+    // Simple and fast solution: always wipe the last digit
+    //
+#if (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64)
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptWipeKnownSize( pbBuffer + (nDigits-1) * SYMCRYPT_FDEF_DIGIT_SIZE, SYMCRYPT_FDEF_DIGIT_SIZE );
+#endif
+
+    // There is nothing to initialize...
+
+    return pDst;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementWipe(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst )
+{
+    SymCryptWipe( peDst, pmMod->cbModElement );
+}
+
+VOID
+SymCryptFdefModElementCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst )
+{
+    if( peSrc != peDst )
+    {
+        memcpy( peDst, peSrc, pmMod->cbModElement );
+    }
+}
+
+VOID
+SymCryptFdefModElementMaskedCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst,
+            UINT32                  mask )
+{
+    SymCryptFdefMaskedCopy( (PCBYTE) peSrc, (PBYTE) peDst, pmMod->nDigits, mask );
+}
+
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc )
+{
+    return &pmSrc->Divisor;
+}
+
+VOID
+SymCryptFdefModElementConditionalSwap(
+    _In_       PCSYMCRYPT_MODULUS    pmMod,
+    _Inout_    PSYMCRYPT_MODELEMENT  peData1,
+    _Inout_    PSYMCRYPT_MODELEMENT  peData2,
+    _In_       UINT32                cond )
+{
+    SymCryptFdefConditionalSwap( (PBYTE) &peData1->d.uint32[0], (PBYTE) &peData2->d.uint32[0], pmMod->nDigits, cond );
+}
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc )
+{
+
+    return SymCryptFdefIntFromDivisor( &pmSrc->Divisor );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefDecideModulusType( PCSYMCRYPT_INT piSrc, UINT32 nDigits, UINT32 averageOperations, UINT32 flags )
+{
+    UINT32 res = 0;
+    BOOLEAN disableMontgomery = 0;
+    BYTE tempBuf[64];
+    PCSYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY pEntry;
+
+    UINT32 nBitsizeOfValue = SymCryptIntBitsizeOfValue( piSrc );
+    UINT32 modulusFeatures = 0;
+
+    if( !disableMontgomery &&
+        ( flags & (SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC)) != 0 &&
+        (SymCryptIntGetValueLsbits32( piSrc ) & 1) == 1 &&
+        averageOperations >= 10 )
+    {
+        modulusFeatures |= SYMCRYPT_MODULUS_FEATURE_MONTGOMERY;
+
+        // Specific modulus value detection
+        if( (flags & SYMCRYPT_FLAG_DATA_PUBLIC) != 0 )
+        {
+            // Detect if modulus value is the P384 field modulus (convert piSrc to big endian and do comparison with known value of P384 modulus)
+            if( nBitsizeOfValue == 384 &&
+                SymCryptFdefRawGetValue(SYMCRYPT_FDEF_INT_PUINT32(piSrc), SYMCRYPT_FDEF_DIGITS_FROM_BITS(384), tempBuf, 64, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST) == SYMCRYPT_NO_ERROR )
+            {
+                // First 16 bytes are guaranteed to be zero because nBitsizeOfValue is 384
+                if( memcmp(tempBuf+16, ((PBYTE)SymCryptEcurveParamsNistP384) + sizeof(SYMCRYPT_ECURVE_PARAMS), 48) == 0 )
+                {
+                    modulusFeatures |= SYMCRYPT_MODULUS_FEATURE_NISTP384;
+                }
+            }
+
+            // Detect if modulus value is the P256 field modulus (not currently used)
+            // if( nBitsizeOfValue == 256 &&
+            //     SymCryptFdefRawGetValue(SYMCRYPT_FDEF_INT_PUINT32(piSrc), SYMCRYPT_FDEF_DIGITS_FROM_BITS(256), tempBuf, 64, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST) == SYMCRYPT_NO_ERROR )
+            // {
+            //     // First 32 bytes are guaranteed to be zero because nBitsizeOfValue is 256
+            //     if( memcmp(tempBuf+32, ((PBYTE)SymCryptEcurveParamsNistP256) + sizeof(SYMCRYPT_ECURVE_PARAMS), 32) == 0 )
+            //     {
+            //         modulusFeatures |= SYMCRYPT_MODULUS_FEATURE_NISTP256;
+            //     }
+            // }
+        }
+    }
+
+    pEntry = SymCryptModulusTypeSelections;
+
+    for(;;)
+    {
+        if( SYMCRYPT_CPU_FEATURES_PRESENT( pEntry->cpuFeatures ) &&
+            (pEntry->maxBits == 0 || (nDigits <= SymCryptDigitsFromBits( pEntry->maxBits ) && nBitsizeOfValue <= pEntry->maxBits )) &&
+            (pEntry->modulusFeatures & ~modulusFeatures) == 0
+            )
+        {
+            res = pEntry->type;
+            break;
+        }
+        pEntry++;
+    }
+
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UNREFERENCED_PARAMETER( pmMod );
+    UNREFERENCED_PARAMETER( peObj );
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+}
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdefModPreGetGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UNREFERENCED_PARAMETER( pmMod );
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    return &peObj->d.uint32[0];
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntToModulus(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_MODULUS   pmDst,
+                                    UINT32              averageOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    pmDst->flags = flags;
+    SymCryptIntToDivisor( piSrc, &pmDst->Divisor, averageOperations, flags & SYMCRYPT_FLAG_DATA_PUBLIC, pbScratch, cbScratch );
+
+    pmDst->type = SymCryptFdefDecideModulusType( piSrc, pmDst->nDigits, averageOperations, flags );
+
+    // Set inv64 - note the value is only valid if the modulus is odd, but the computation
+    // is constant time regardless of the parity, so we can safely compute it in all cases
+    pmDst->inv64 = 0 - SymCryptInverseMod2e64( SymCryptIntGetValueLsbits64(piSrc) );
+
+    SYMCRYPT_MOD_CALL( pmDst ) modulusInit( pmDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntToModElement(
+    _In_                            PCSYMCRYPT_INT          piSrc,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefRawDivMod(
+        SYMCRYPT_FDEF_INT_PUINT32( piSrc ),
+        piSrc->nDigits,
+        &pmMod->Divisor,
+        NULL,                   // throw away the quotient
+        &peDst->d.uint32[0],
+        pbScratch,
+        cbScratch );
+
+    SYMCRYPT_MOD_CALL( pmMod ) modSetPost( pmMod, peDst, pbScratch, cbScratch );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementToIntGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_bytes_( pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )
+                                    PCUINT32                pSrc,
+    _Out_                           PSYMCRYPT_INT           piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    memcpy( SYMCRYPT_FDEF_INT_PUINT32( piDst ), pSrc, pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piDst )[pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32], (piDst->nDigits - pmMod->nDigits) * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pmMod->nDigits ) );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModElementSetValueGeneric(
+    _In_reads_bytes_( cbSrc )       PCBYTE                  pbSrc,
+                                    SIZE_T                  cbSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError;
+    UINT32  nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    SYMCRYPT_ASSERT( cbSrc <= nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    scError = SymCryptFdefRawSetValue( pbSrc, cbSrc, format, &peDst->d.uint32[0], nDigits );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptFdefRawDivMod(
+        &peDst->d.uint32[0],
+        nDigits,
+        &pmMod->Divisor,
+        NULL,
+        &peDst->d.uint32[0],
+        pbScratch,
+        cbScratch );
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModElementGetValue(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_writes_bytes_( cbDst )     PBYTE                   pbDst,
+                                    SIZE_T                  cbDst,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError;
+    PCUINT32 pUint32;
+    UINT32  nDigits = pmMod->nDigits;
+
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    SYMCRYPT_ASSERT( cbDst <= nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    pUint32 = SYMCRYPT_MOD_CALL( pmMod ) modPreGet( pmMod, peSrc, pbScratch, cbScratch );
+
+    scError = SymCryptFdefRawGetValue( pUint32, nDigits, pbDst, cbDst, format );
+
+    return scError;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefModElementIsEqual(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc1,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc2 )
+{
+    UINT32 d;
+    UINT32 i;
+
+    d = 0;
+    for( i=0; i < pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 ; i++ )
+    {
+        d |= peSrc1->d.uint32[i] ^ peSrc2->d.uint32[i];
+    }
+
+    return SYMCRYPT_MASK32_ZERO( d );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefModElementIsZero(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc )
+{
+    UINT32 d;
+    UINT32 i;
+
+    d = 0;
+    for( i=0; i < pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 ; i++ )
+    {
+        d |= peSrc->d.uint32[i];        // Check that all bits are zero
+    }
+
+    return SYMCRYPT_MASK32_ZERO( d );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModAddGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 c;
+    UINT32 d;
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( cbScratch >= nDigits*SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    //
+    // Doing add/cmp/sub might be faster or not.
+    // Masked add is hard because the mask operations destroy the carry flag.
+    //
+
+	// dcl - cleanup?
+
+//    c = SymCryptFdefRawAdd( &pSrc1->uint32[0], &pSrc2->uint32[0], &pDst->uint32[0], nDigits);
+//    d = SymCryptFdefRawSub( &pDst->uint32[0], &pMod->Divisor.Int.uint32[0], &pDst->uint32[0], nDigits );
+//    e = SymCryptFdefRawMaskedAdd( &pDst->uint32[0], &pMod->Divisor.Int.uint32[0], 0 - (c^d), nDigits );
+
+    c = SymCryptFdefRawAdd( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], &peDst->d.uint32[0], nDigits );
+    d = SymCryptFdefRawSub( &peDst->d.uint32[0], SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), (PUINT32) pbScratch, nDigits );
+    SymCryptFdefMaskedCopy( pbScratch, (PBYTE) &peDst->d.uint32[0], nDigits, (c^d) - 1 );
+
+    // We can't have a carry in the first addition, and no carry in the subtraction.
+    SYMCRYPT_ASSERT( !( c == 1 && d == 0 ) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSubGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 c;
+    UINT32 d;
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( cbScratch >= nDigits*SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    c = SymCryptFdefRawSub( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], &peDst->d.uint32[0], nDigits );
+    d = SymCryptFdefRawAdd( &peDst->d.uint32[0], SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), (PUINT32) pbScratch, nDigits );
+    SymCryptFdefMaskedCopy( pbScratch, (PBYTE) &peDst->d.uint32[0], nDigits, 0 - c );
+
+    SYMCRYPT_ASSERT( !(c == 1 && d == 0) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModNegGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 isZero;
+    UINT32 i;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    //
+    // We have to be careful to handle the value 0 properly as it does NOT map to Modulus - Value.
+    //
+    isZero = SymCryptFdefRawIsEqualUint32( &peSrc->d.uint32[0], nDigits , 0 );
+    SymCryptFdefRawSub( SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), &peSrc->d.uint32[0], &peDst->d.uint32[0], nDigits );
+
+    // Now we set the result to zero if the input was zero
+    for( i=0; i< nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+    {
+        peDst->d.uint32[i] &= ~isZero;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementSetValueUint32Generic(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    if( pmMod->Divisor.nBits <= 32 && value >= SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int )[0] )
+    {
+        // The value is >=  the modulus; this is not supported
+
+        // For now do a possibly non-sidechannel safe, but mathematically correct modulo operation
+        value %= SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int )[0];
+    }
+
+    peDst->d.uint32[0] = value;
+
+    SymCryptWipe( &peDst->d.uint32[1], nDigits * SYMCRYPT_FDEF_DIGIT_SIZE - sizeof( UINT32 ) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementSetValueNegUint32(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    if( pmMod->Divisor.nBits <= 32 && value >= SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int )[0] )
+    {
+        // The value is >=  the modulus; this is not supported.
+
+        // For now do a possibly non-sidechannel safe, but mathematically correct modulo operation
+        value %= SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int )[0];
+    }
+
+    if( value == 0 )
+    {
+        SymCryptWipe( &peDst->d.uint32[0], nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+    } else {
+        SymCryptFdefRawSubUint32( SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), value, &peDst->d.uint32[0], nDigits );
+    }
+
+    //
+    // Possible future optimization: we can optimize the value==0 and value==1 cases on a per-type basis
+    //
+    SYMCRYPT_MOD_CALL( pmMod ) modSetPost( pmMod, peDst, pbScratch, cbScratch );
+}
+
+// In the worst case there is a 1 in 8 chance of successfully generating a value
+// This is when the modulus is 4 (nBits of modulus is 3), and 0, 1, and -1 are disallowed.
+// In this case, having 1000 retries, there is a ~ 2^-193 chance of failure unless SymCryptCallbackRandom
+// is completely broken. This passes the bar of being reasonable to Fatal.
+#define FDEF_MOD_SET_RANDOM_GENERIC_LIMIT   (1000)
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetRandomGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 offset;
+    UINT32 ulimit;
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32 nUsedBytes;
+    UINT32 mask;
+    UINT32 c;
+    UINT32 cntr;
+    PUINT32 pDst = &peDst->d.uint32[0];
+    PCUINT32 pMod = SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int );
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    if( (flags & SYMCRYPT_FLAG_MODRANDOM_ALLOW_ZERO) != 0 )
+    {
+        // SYMCRYPT_FLAG_MODRANDOM_ALLOW_ZERO => SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE
+        offset = 0;
+    } else if( (flags & SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE) != 0 )
+    {
+        offset = 1;
+    } else
+    {
+        offset = 2;
+    }
+
+    if( (flags & SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE ) )
+    {
+        ulimit = 0;
+    } else {
+        ulimit = 1;
+    }
+
+    //
+    // Special case for small divisors:
+    //  When the divisor is 1, 2, or 3 we always allow returning -1
+    //  We may also allow returning 1 or 0 depending on the flags specified
+    if ( pmMod->Divisor.nBits < 3 )
+    {
+        // At a minimum, allow -1
+        offset = SYMCRYPT_MIN(offset, pMod[0] - 1);
+        ulimit = 0;
+    }
+
+    // Set pTmp to pMod-(offset+ulimit)
+    SYMCRYPT_ASSERT( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE <= cbScratch );
+    c = SymCryptFdefRawSubUint32( pMod, offset + ulimit, pTmp, nDigits );
+    SYMCRYPT_ASSERT( c == 0 );
+
+    nUsedBytes = (pmMod->Divisor.nBits + 7)/8;
+    mask = 0x100 >> ( (8-pmMod->Divisor.nBits) & 7);
+    mask -= 1;
+
+    // Wipe any bytes we won't fill with random
+    SymCryptWipe( (PBYTE)pDst + nUsedBytes, (nDigits * SYMCRYPT_FDEF_DIGIT_SIZE) - nUsedBytes );
+
+    for(cntr=0; cntr<FDEF_MOD_SET_RANDOM_GENERIC_LIMIT; cntr++)
+    {
+        // Try random values until we get one we like
+        SymCryptCallbackRandom( (PBYTE)pDst, nUsedBytes );
+        ((PBYTE)pDst)[nUsedBytes-1] &= (BYTE) mask;
+
+        // Compare value to pMod-(offset+ulimit)
+        if( SymCryptFdefRawIsLessThan( pDst, pTmp, nDigits ) )
+        {
+            // The value is within required range [0, Divisor-offset-ulimit)
+            break;
+        }
+    }
+
+    // Wipe all the digits in pTmp
+    SymCryptWipe( pTmp, nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    if (cntr >= FDEF_MOD_SET_RANDOM_GENERIC_LIMIT)
+    {
+        SymCryptFatal( 'rndc');
+    }
+
+    // Add the offset which allows us to avoid 0 and/or 1 if required.
+    // Now result is in range [offset, Divisor-ulimit)
+    c = SymCryptFdefRawAddUint32( pDst, offset, pDst, nDigits );
+    SYMCRYPT_ASSERT( c == 0 );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2Generic(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst)
+{
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 mask;
+    UINT64 t;
+    UINT64 u;
+    UINT32 i;
+    PCUINT32 pMod = SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int );
+
+    // mod must be odd
+    SYMCRYPT_ASSERT( (pMod[0] & 1) != 0 );
+    SYMCRYPT_ASSERT( (exp >= 1) && (exp <= NATIVE_BITS) );
+
+    do
+    {
+        mask = (UINT32)0 - (peSrc->d.uint32[0] & 1);
+
+        t = (UINT64) peSrc->d.uint32[0] + (pMod[0] & mask);
+        u = (UINT32) t;
+        t >>= 32;
+
+        for( i = 1; i < nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32; i++ )
+        {
+            t += pMod[i] & mask;
+            t += peSrc->d.uint32[i];
+
+            u |= t << 32;
+
+            peDst->d.uint32[i-1] = (UINT32)(u >> 1);
+            t >>= 32;
+            u >>= 32;
+        }
+        u |= t << 32;
+        peDst->d.uint32[i-1] = (UINT32)( u >> 1 );
+
+        exp -= 1;
+
+        // First iteration reads from peSrc and writes to peDst
+        // subsequent iterations must read from and write to peDst
+        peSrc = peDst;
+    } while (exp > 0);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst )
+{
+
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_MULX ) )
+    {
+        SymCryptFdefModDivSmallPow2Mulx( pmMod, peSrc, exp, peDst );
+    }
+    else
+    {
+        // Currently SymCryptAsm does not support AMD64 functions with shl/shr/shrd
+        // by a variable count, as this needs special handling of the rcx (cl) register
+        // For now we just fallback to the generic implementation on machines without MULX
+        SymCryptFdefModDivSmallPow2Generic( pmMod, peSrc, exp, peDst );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    SymCryptFdefModDivSmallPow2Asm( pmMod, peSrc, exp, peDst );
+#else
+    SymCryptFdefModDivSmallPow2Generic( pmMod, peSrc, exp, peDst );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivPow2(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+                                    UINT32                  exp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 shiftAmount;
+
+    UNREFERENCED_PARAMETER(pbScratch);
+    UNREFERENCED_PARAMETER(cbScratch);
+
+    // mod must be odd
+    SYMCRYPT_ASSERT( (SYMCRYPT_FDEF_INT_PUINT32(&pmMod->Divisor.Int)[0] & 1) != 0 );
+
+    if( exp == 0 )
+    {
+        // If exp is 0 we just need to copy peSrc to peDst
+        SymCryptFdefModElementCopy( pmMod, peSrc, peDst );
+        return;
+    }
+
+    do
+    {
+        shiftAmount = SYMCRYPT_MIN(NATIVE_BITS, exp);
+        SymCryptFdefModDivSmallPow2( pmMod, peSrc, shiftAmount, peDst );
+        exp -= shiftAmount;
+
+        // First iteration reads from peSrc and writes to peDst
+        // subsequent iterations must read from and write to peDst
+        peSrc = peDst;
+    } while( exp > 0 );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32  scratchOffset = 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) >= scratchOffset + SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( 2 * nDigits, nDigits ) );
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbScratch );
+
+    // Tmp space is enough for the product plus the DivMod scratch
+
+    SymCryptFdefRawMul( &peSrc1->d.uint32[0], nDigits, &peSrc2->d.uint32[0], nDigits, pTmp );
+
+    SymCryptFdefRawDivMod( pTmp, 2*nDigits, &pmMod->Divisor, NULL, &peDst->d.uint32[0], pbScratch + scratchOffset, cbScratch - scratchOffset );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32  scratchOffset = 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE;
+
+    SymCryptFdefClaimScratch( pbScratch, cbScratch, SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+    SYMCRYPT_ASSERT( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) >= scratchOffset + SYMCRYPT_FDEF_SCRATCH_BYTES_FOR_INT_DIVMOD( 2 * nDigits, nDigits ) );
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbScratch );
+
+    // Tmp space is enough for the product plus the DivMod scratch
+
+    SymCryptFdefRawSquare( &peSrc->d.uint32[0], nDigits, pTmp );
+
+    SymCryptFdefRawDivMod( pTmp, 2*nDigits, &pmMod->Divisor, NULL, &peDst->d.uint32[0], pbScratch + scratchOffset, cbScratch - scratchOffset );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModInvGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 nBytes;
+    UINT32 c;
+    UINT32 leastSignificantUint32;
+    UINT32 trailingZeros;
+
+    //
+    // This function is called on Montgomery moduli so we can't directly call specifically optimized modular operations from here.
+    //
+    // For now we use dispatch functions with pmMod to perform potentially optimized modular operations.
+    // This approach makes sense when on average the cost of dispatch is less than the benefit using an optimized operation.
+    // The alternative is to make specialized ModInv routines for different types of moduli, but we do not yet do this to
+    // reduce code duplication / code size.
+    //
+
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_MODINV( nDigits ) );
+
+    if( (pmMod->flags & (SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME )) != (SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME ) )
+    {
+        // Inversion over non-public or non-prime moduli currently not supported.
+        // Our blinding below only works for prime moduli.
+        // As the modulus cannot be blinded, it requires a fully side-channel safe algorithm which is much more complicated and
+        // slower.
+        // When this is necessary, we will add a second ModInv implementation for those cases.
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    //
+    // Algorithm:
+    // R = random nonzero value mod Mod
+    // X := Src * R (mod Mod)
+    // A = X
+    // B = Mod
+    // Va = 1
+    // Vb = 0
+    // invariant: A = Va*X (mod Mod), B = Vb*X (mod Mod),
+    //
+    // if( A == 0 ): error
+    //
+    // verify (A | B) is odd
+    // if B even: swap (A,B), swap( Va, Vb)
+    //
+    //  repeat:
+    //      while( A even ):
+    //          A /= 2; Va /= 2 (mod Mod)
+    //      if( A == 1 ): break1
+    //      (A, Va, B, Vb) = (B-A, Vb - Va, A, Va)
+    //      if( A == 0 ): error (not co-prime)
+
+    nBytes = SymCryptSizeofModElementFromModulus( pmMod );
+
+    SYMCRYPT_ASSERT( cbScratch >= 4*nBytes );
+    PSYMCRYPT_MODELEMENT peR = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+    PSYMCRYPT_MODELEMENT peX = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+    PSYMCRYPT_MODELEMENT peVa = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+    PSYMCRYPT_MODELEMENT peVb = SymCryptModElementCreate( pbScratch, nBytes, pmMod );
+    pbScratch += nBytes;
+    cbScratch -= 4*nBytes;
+
+    PSYMCRYPT_MODELEMENT peVtmpPtr;
+
+    nBytes = SymCryptSizeofIntFromDigits( nDigits );
+    SYMCRYPT_ASSERT( cbScratch >= 3 * nBytes );
+    PSYMCRYPT_INT piA = SymCryptIntCreate( pbScratch, nBytes, nDigits );
+    pbScratch += nBytes;
+    PSYMCRYPT_INT piB = SymCryptIntCreate( pbScratch, nBytes, nDigits );
+    pbScratch += nBytes;
+    PSYMCRYPT_INT piT = SymCryptIntCreate( pbScratch, nBytes, nDigits );
+    pbScratch += nBytes;
+    cbScratch -= 3*nBytes;
+
+    PSYMCRYPT_INT piTmpPtr;
+
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigits ) );
+
+    // If the data is not public, multiply by a random blinding factor; otherwise copy the value
+    if( (flags & SYMCRYPT_FLAG_DATA_PUBLIC) == 0 )
+    {
+        SymCryptModSetRandom( pmMod, peR, SYMCRYPT_FLAG_MODRANDOM_ALLOW_ONE | SYMCRYPT_FLAG_MODRANDOM_ALLOW_MINUSONE, pbScratch, cbScratch );   //R = random
+        SymCryptModMul( pmMod, peR, peSrc, peX, pbScratch, cbScratch );     // X = R * Src
+    } else
+    {
+        SymCryptModElementCopy( pmMod, peSrc, peX );
+    }
+
+    // Set up piA and piB
+    SymCryptFdefModElementToIntGeneric( pmMod, &peX->d.uint32[0], piA, pbScratch, cbScratch );   // A = X
+    SymCryptIntCopy( SymCryptIntFromModulus( (PSYMCRYPT_MODULUS) pmMod ), piB );          // B = Mod
+
+    // Reject if A = 0, B = 0, or A and B both even
+    if( SymCryptIntIsEqualUint32( piA, 0 ) |
+        SymCryptIntIsEqualUint32( piB, 0 ) |
+        (((SymCryptIntGetValueLsbits32( piA ) | SymCryptIntGetValueLsbits32( piB )) & 1) ^ 1) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if( SymCryptIntIsEqualUint32( piB, 2 ) )
+    {
+        // Mod = 2 is a valid input. Luckily, modular inversion is easy.
+        // The rest of the code assumes that Mod is odd. Other even values are not prime.
+        SymCryptModElementCopy( pmMod, peSrc, peDst);
+        goto cleanup;
+    }
+
+    SymCryptFdefModElementSetValueUint32Generic( 1, pmMod, peVa, pbScratch, cbScratch );               // Va = 1
+    SymCryptFdefModElementSetValueUint32Generic( 0, pmMod, peVb, pbScratch, cbScratch );               // Vb = 0
+
+    for(;;)
+    {
+        // invariant: A = Va*X (mod Mod), B = Vb*X (mod Mod), A != 0, B > 1.
+        // Remove factors of 2 from A. This loop terminates because A != 0
+        leastSignificantUint32 = SymCryptIntGetValueLsbits32(piA);
+        while( (leastSignificantUint32 & 1) == 0 )
+        {
+            trailingZeros = SymCryptCountTrailingZeros32( leastSignificantUint32 );
+            SymCryptIntDivPow2( piA, trailingZeros, piA );
+            SymCryptFdefModDivSmallPow2( pmMod, peVa, trailingZeros, peVa );
+            leastSignificantUint32 = SymCryptIntGetValueLsbits32(piA);
+        }
+
+        if( SymCryptIntIsEqualUint32( piA, 1 ) )
+        {
+            // A = 1 = Va * X (mod Mod), so Va is the inverse of X
+            break;
+        }
+
+        c = SymCryptIntSubSameSize( piB, piA, piT );
+
+        // If A != 1 and A=B, then A is the GCD of the original inputs, and there is no inverse
+        if( SymCryptIntIsEqualUint32( piT, 0 ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        if( c == 0 )
+        {
+            // B > A, we set B to B-A and swap (B,A)
+            // that way we continue our halving on B-A
+
+            SymCryptIntCopy( piT, piB );
+            SymCryptModSub( pmMod, peVb, peVa, peVb, pbScratch, cbScratch );
+
+            piTmpPtr  = piB;  piB  = piA;  piA  = piTmpPtr;
+            peVtmpPtr = peVb; peVb = peVa; peVa = peVtmpPtr;
+        } else {
+            // B < A, Set A to A-B and continue halving A
+            SymCryptIntNeg( piT, piA );
+            SymCryptModSub( pmMod, peVa, peVb, peVa, pbScratch, cbScratch );
+        }
+    }
+
+    // 1 = A = Va * X (mod Mod), so Va is the inverse of X
+    // Check computation that we can test in the debugger
+    SymCryptModMul( pmMod, peVa, peX, peVb, pbScratch, cbScratch );
+
+    // Actual answer
+
+    // If the data is not public, multiply by the random blinding factor; otherwise copy the value
+    if( (flags & SYMCRYPT_FLAG_DATA_PUBLIC) == 0 )
+    {
+        SymCryptModMul( pmMod, peVa, peR, peDst, pbScratch, cbScratch );
+    } else
+    {
+        SymCryptModElementCopy( pmMod, peVa, peDst );
+    }
+
+cleanup:
+    return scError;
+}
+
+
+//=============================
+// Montgomery representation
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitMontgomeryInternal(
+    _Inout_                         PSYMCRYPT_MODULUS       pmMod,
+                                    UINT32                  nUint32Used,           // R = 2^{32 * this parameter}
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    // Scratch space is big enough for an nDigit+1 byte value + sufficient divmod scratch
+    PUINT32 pR2;
+    UINT32  cbR2;
+    UINT32 nDigits;
+
+    PUINT32 modR2;
+    PUINT32 negDivisor;
+
+    nDigits = pmMod->nDigits;
+    modR2 = (PUINT32)((PBYTE)&pmMod->Divisor + SymCryptFdefSizeofDivisorFromDigits( nDigits ));
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbScratch );
+
+    pmMod->tm.montgomery.Rsqr = modR2;
+    negDivisor = (PUINT32)((PBYTE)modR2 + (nDigits * SYMCRYPT_FDEF_DIGIT_SIZE));
+
+    // We pre-compute R^2 mod M
+
+    pR2 = (PUINT32) pbScratch;
+    cbR2 = (2*nDigits + 1) * SYMCRYPT_FDEF_DIGIT_SIZE;
+    SYMCRYPT_ASSERT( cbScratch >= cbR2 );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nUint32Used * sizeof(UINT32) );
+
+    // Set it to R^2
+    SymCryptWipe( pR2, cbR2 );
+    pR2[ 2 * nUint32Used ] = 1;
+    SymCryptFdefRawDivMod( pR2, 2*nDigits + 1, &pmMod->Divisor, NULL, modR2, pbScratch + cbR2, cbScratch - cbR2 );
+
+    SymCryptFdefRawNeg( SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int ), 0, negDivisor, nDigits );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitMontgomery(
+    _Inout_                         PSYMCRYPT_MODULUS       pmMod,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModulusInitMontgomeryInternal( pmMod, pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32, pbScratch, cbScratch );
+}
+
+VOID
+SymCryptFdefMontgomeryReduceC(
+    _In_                                                                PCSYMCRYPT_MODULUS  pmMod,
+    _Inout_updates_( 2 * pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 ) PUINT32             pSrc,
+    _Out_writes_( pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 )        PUINT32             pDst )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
+    PCUINT32 pMod = SYMCRYPT_FDEF_INT_PUINT32( &pmMod->Divisor.Int );
+
+    UINT32 hc = 0;
+    for( UINT32 i=0; i<nWords; i++ )
+    {
+        UINT32 m = (UINT32)pmMod->inv64 * pSrc[0];
+        UINT64 c = 0;
+        for( UINT32 j = 0; j < nWords; j++ )
+        {
+            // Invariant: c < 2^32
+            c += SYMCRYPT_MUL32x32TO64( pMod[j], m );
+            c += pSrc[j];
+            // There is no overflow on C because the max value is
+            // (2^32 - 1) * (2^32 - 1) + 2^32 - 1 + 2^32 - 1 = 2^64 - 1.
+            pSrc[j] = (UINT32) c;
+            c >>= 32;
+        }
+        c = c + pSrc[nWords] + hc;
+        pSrc[nWords] = (UINT32) c;
+        hc = c >> 32;
+        pSrc++;
+    }
+    SYMCRYPT_ASSERT( hc < 2 );
+
+    UINT32 d = SymCryptFdefRawSub( pSrc, pMod, pDst, nDigits );
+
+    SYMCRYPT_ASSERT( hc <= d );     // if hc = 1, then d = 1 is mandatory
+
+    SymCryptFdefMaskedCopy( (PCBYTE) pSrc, (PBYTE) pDst, nDigits, hc - (hc | d) );  // copy only if hc=0, d=1
+}
+
+VOID
+SymCryptFdefMontgomeryReduce(
+    _In_                                                                PCSYMCRYPT_MODULUS  pmMod,
+    _Inout_updates_( 2 * pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 ) PUINT32             pSrc,
+    _Out_writes_( pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32 )        PUINT32             pDst )
+{
+#if SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_MULX ) )
+    {
+        SymCryptFdefMontgomeryReduceMulx( pmMod, pSrc, pDst );
+    } else {
+        SymCryptFdefMontgomeryReduceAsm( pmMod, pSrc, pDst );
+    }
+#elif SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
+    SymCryptFdefMontgomeryReduceAsm( pmMod, pSrc, pDst );
+#else
+    SymCryptFdefMontgomeryReduceC( pmMod, pSrc, pDst );
+#endif
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    // Montgomery representation for X is R*X mod M where R = 2^<nDigits * bits-per-digit>
+    // Montgomery reduction performs an implicit division by R
+    // This function converts to the internal representation by multiplying by R^2 mod M and then performing a Montgomery reduction
+    UINT32 nDigits = pmMod->nDigits;
+
+	// dcl - this should not incur significant cost, consider checking always
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptFdefRawMul( &peObj->d.uint32[0], nDigits, pmMod->tm.montgomery.Rsqr, nDigits, (PUINT32) pbScratch );
+    SymCryptFdefMontgomeryReduce( pmMod, (PUINT32) pbScratch, &peObj->d.uint32[0] );
+}
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdefModPreGetMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32 nDigits = pmMod->nDigits;
+
+	// dcl - this should not incur significant cost, consider checking always
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    memcpy( pTmp, &peObj->d.uint32[0], nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+    SymCryptWipe( pTmp + nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32, nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+    SymCryptFdefMontgomeryReduce( pmMod, pTmp, pTmp );
+
+    return pTmp;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusCopyFixupMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmSrc,
+    _Out_                           PSYMCRYPT_MODULUS       pmDst )
+{
+    // We only have to fix up the Montgomery-specific stuff here
+	// dcl - not sure I understand why you pass pmSrc here
+    UNREFERENCED_PARAMETER( pmSrc );
+    pmDst->tm.montgomery.Rsqr = (PUINT32)((PBYTE)&pmDst->Divisor + SymCryptFdefSizeofDivisorFromDigits( pmDst->nDigits ));
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+	// dcl - missing assert?
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefRawMul( &peSrc1->d.uint32[0], nDigits, &peSrc2->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduce( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+#if SYMCRYPT_CPU_AMD64 && defined(__WINE_PE_BUILD)
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryMulx(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefRawMulMulx( &peSrc1->d.uint32[0], nDigits, &peSrc2->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduceMulx( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryMulx1024(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefRawMulMulx1024( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduceMulx1024( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+#endif
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefRawSquare( &peSrc->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduce( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+
+#if SYMCRYPT_CPU_AMD64 && defined(__WINE_PE_BUILD)
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryMulx(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefRawSquareMulx( &peSrc->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduceMulx( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryMulx1024(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+
+    SymCryptFdefRawSquareMulx1024( &peSrc->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduceMulx1024( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+#endif
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModInvMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 nDigits = pmMod->nDigits;
+    UINT32 nBytes = nDigits * SYMCRYPT_FDEF_DIGIT_SIZE;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pTmp );
+
+    //
+    // We have R*X; we first apply the montgomery reduction twice to get X/R, and then invert that
+    // using the generic inversion to get R/X.
+    //
+	SYMCRYPT_ASSERT( cbScratch >= 2 * nBytes );
+    memcpy( pTmp, &peSrc->d.uint32[0], nBytes );
+
+    SymCryptWipe( (PBYTE)pTmp + nBytes, nBytes );
+    SymCryptFdefMontgomeryReduce( pmMod, pTmp, pTmp );
+
+    SymCryptWipe( (PBYTE)pTmp + nBytes, nBytes );
+    SymCryptFdefMontgomeryReduce( pmMod, pTmp, &peDst->d.uint32[0] );
+
+    scError = SymCryptFdefModInvGeneric( pmMod, peDst, peDst, flags, pbScratch, cbScratch );
+
+    return scError;
+}
+
+#if SYMCRYPT_CPU_AMD64 && defined(__WINE_PE_BUILD)
+
+//=====================================
+// 256-bit Montgomery modulus code
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModInvMontgomery256(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 nBytes = 32;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pTmp );
+
+    //
+    // We have R*X; we first apply the montgomery reduction twice to get X/R, and then invert that
+    // using the generic inversion to get R/X.
+    //
+    SYMCRYPT_ASSERT( cbScratch >= 2 * nBytes );
+    memcpy( pTmp, &peSrc->d.uint32[0], nBytes );
+
+    SymCryptWipe( (PBYTE)pTmp + nBytes, nBytes );
+    SymCryptFdefMontgomeryReduce256Asm( pmMod, pTmp, pTmp );
+
+    SymCryptWipe( (PBYTE)pTmp + nBytes, nBytes );
+    SymCryptFdefMontgomeryReduce256Asm( pmMod, pTmp, &peDst->d.uint32[0] );
+
+    scError = SymCryptFdefModInvGeneric( pmMod, peDst, peDst, flags, pbScratch, cbScratch );
+
+    return scError;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostMontgomeryMulx256(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    // Montgomery representation for X is R*X mod M where R = 2^<nDigits * bits-per-digit>
+    // Montgomery reduction performs an implicit division by R
+    // This function converts to the internal representation by multiplying by R^2 mod M and then performing a Montgomery reduction
+    UINT32 nDigits = pmMod->nDigits;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+    UNREFERENCED_PARAMETER( nDigits );
+
+    SymCryptFdefModMulMontgomeryMulx256Asm( pmMod, (PSYMCRYPT_MODELEMENT) pmMod->tm.montgomery.Rsqr, peObj, peObj );
+}
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdefModPreGetMontgomery256(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    PUINT32 pTmp = (PUINT32) pbScratch;
+    UINT32 nDigits = 1;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    memcpy( pTmp, &peObj->d.uint32[0], nDigits * SYMCRYPT_FDEF_DIGIT_SIZE );
+    SymCryptFdefMontgomeryReduce256Asm( pmMod, pTmp, pTmp );
+
+    // This gives the right result, but relies on peObj having zeroed upper half
+    // on AMD64 when digits are 512 bits. This should be true - check in a CHKed build.
+    for( UINT32 i=8; i<16; ++i )
+    {
+        SYMCRYPT_ASSERT( pTmp[i] == 0 );
+    }
+
+    // Wipe the extra bytes
+    // SymCryptWipeKnownSize( pTmp + (SYMCRYPT_FDEF_DIGIT_NUINT32 / 2), 32 );
+
+    return pTmp;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitMontgomery256(
+    _Inout_                         PSYMCRYPT_MODULUS       pmMod,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SymCryptFdefModulusInitMontgomeryInternal( pmMod, 8, pbScratch, cbScratch );
+}
+
+//=====================================
+// 384-bit Montgomery modulus code
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostMontgomeryMulxP384(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    // Montgomery representation for X is R*X mod M where R = 2^<nDigits * bits-per-digit>
+    // Montgomery reduction performs an implicit division by R
+    // This function converts to the internal representation by multiplying by R^2 mod M and then performing a Montgomery reduction
+    UINT32 nDigits = pmMod->nDigits;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( pbScratch );
+    UNREFERENCED_PARAMETER( cbScratch );
+    UNREFERENCED_PARAMETER( nDigits );
+
+    SymCryptFdefModMulMontgomeryMulxP384Asm( pmMod, (PSYMCRYPT_MODELEMENT) pmMod->tm.montgomery.Rsqr, peObj, peObj );
+}
+
+#if 0
+//=====================================
+// 512-bit Montgomery modulus code
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomery512(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptFdefRawMul512Asm( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduce512Asm( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomery512(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptFdefRawSquare512Asm( &peSrc->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduce512Asm( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+//=====================================
+// 1024-bit Montgomery modulus code
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomery1024(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptFdefRawMul1024Asm( &peSrc1->d.uint32[0], &peSrc2->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduce1024Asm( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomery1024(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 nDigits = pmMod->nDigits;
+    PUINT32 pTmp = (PUINT32) pbScratch;
+
+    SYMCRYPT_ASSERT( cbScratch >= nDigits * 2 * SYMCRYPT_FDEF_DIGIT_SIZE );
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    SymCryptFdefRawSquare1024Asm( &peSrc->d.uint32[0], nDigits, pTmp );
+    SymCryptFdefMontgomeryReduce1024Asm( pmMod, pTmp, &peDst->d.uint32[0] );
+}
+#endif
+
+#endif
+
+#ifndef __WINE_PE_BUILD
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduceAsm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst )
+{
+    SymCryptFdefMontgomeryReduceC( pmMod, pSrc, pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2Mulx(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst )
+{
+    SymCryptFdefModDivSmallPow2Generic( pmMod, peSrc, exp, peDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduceMulx(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst )
+{
+    SymCryptFdefMontgomeryReduceC( pmMod, pSrc, pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2Asm(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst )
+{
+    SymCryptFdefModDivSmallPow2Generic( pmMod, peSrc, exp, peDst );
+}
+
+#endif
diff --git a/libs/symcrypt/lib/gcm.c b/libs/symcrypt/lib/gcm.c
new file mode 100644
index 000000000000..a3a66ddea2a2
--- /dev/null
+++ b/libs/symcrypt/lib/gcm.c
@@ -0,0 +1,902 @@
+//
+// gcm.c    Implementation of the GCM block cipher mode
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define GCM_MIN_NONCE_SIZE          (1)
+#define GCM_MIN_TAG_SIZE            (12)
+#define GCM_MAX_TAG_SIZE            (16)
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmValidateParameters(
+    _In_    PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_    SIZE_T                  cbNonce,
+    _In_    UINT64                  cbAssociatedData,
+    _In_    UINT64                  cbData,
+    _In_    SIZE_T                  cbTag )
+{
+    if( pBlockCipher->blockSize != SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        return SYMCRYPT_WRONG_BLOCK_SIZE;
+    }
+
+    //
+    // SP800-38D specifies that the nonce must be at least one bit, but we operate on bytes,
+    // so the minimum is one byte.
+    //
+    if( cbNonce < GCM_MIN_NONCE_SIZE )
+    {
+        return SYMCRYPT_WRONG_NONCE_SIZE;
+    }
+
+    //
+    // cbAssociatedData is limited to <2^61 bytes
+    //
+    if( (cbAssociatedData >> 61) > 0 )
+    {
+        return SYMCRYPT_WRONG_DATA_SIZE;
+    }
+
+    //
+    // per SP800-38D cbData is limited to 2^36 - 32 bytes
+    //
+    if( cbData > SYMCRYPT_GCM_MAX_DATA_SIZE )
+    {
+        return SYMCRYPT_WRONG_DATA_SIZE;
+    }
+
+    if( cbTag < GCM_MIN_TAG_SIZE || cbTag > GCM_MAX_TAG_SIZE )
+    {
+        return SYMCRYPT_WRONG_TAG_SIZE;
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmAddMacData(
+    _Inout_                     PSYMCRYPT_GCM_STATE  pState,
+    _In_reads_opt_( cbData )    PCBYTE               pbData,
+                                SIZE_T               cbData )
+{
+    SIZE_T bytesToProcess;
+    if( pState->bytesInMacBlock > 0 )
+    {
+        bytesToProcess = SYMCRYPT_MIN( cbData, SYMCRYPT_GCM_BLOCK_SIZE - pState->bytesInMacBlock );
+        memcpy( &pState->macBlock[pState->bytesInMacBlock], pbData, bytesToProcess );
+        pbData += bytesToProcess;
+        cbData -= bytesToProcess;
+        pState->bytesInMacBlock += bytesToProcess;
+
+        if( pState->bytesInMacBlock == SYMCRYPT_GCM_BLOCK_SIZE )
+        {
+            SymCryptGHashAppendData(    &pState->pKey->ghashKey,
+                                        &pState->ghashState,
+                                        &pState->macBlock[0],
+                                        SYMCRYPT_GCM_BLOCK_SIZE );
+            pState->bytesInMacBlock = 0;
+        }
+    }
+
+    if( cbData >= SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        bytesToProcess = cbData & SYMCRYPT_GCM_BLOCK_ROUND_MASK;
+
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, pbData, bytesToProcess );
+
+        pbData += bytesToProcess;
+        cbData -= bytesToProcess;
+    }
+
+    if( cbData > 0 )
+    {
+        memcpy( &pState->macBlock[0], pbData, cbData );
+        pState->bytesInMacBlock = cbData;
+    }
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmPadMacData( _Inout_ PSYMCRYPT_GCM_STATE  pState )
+{
+    SIZE_T nBytes;
+    //
+    // Pad the MAC data with zeroes until we hit the block size.
+    //
+    nBytes = pState->bytesInMacBlock;
+    if( nBytes > 0 )
+    {
+        SymCryptWipe( &pState->macBlock[nBytes], SYMCRYPT_GCM_BLOCK_SIZE - nBytes );
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &pState->macBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+        pState->bytesInMacBlock = 0;
+    }
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptDecryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    SIZE_T      bytesToProcess;
+    SIZE_T      bytesUsedInKeyStreamBuffer;
+
+    bytesUsedInKeyStreamBuffer = (SIZE_T) (pState->cbData & SYMCRYPT_GCM_BLOCK_MOD_MASK);
+
+    //
+    // We update pState->cbData once before we modify cbData.
+    // pState->cbData is not used in the rest of this function
+    //
+    SYMCRYPT_ASSERT( pState->cbData + cbData <= SYMCRYPT_GCM_MAX_DATA_SIZE );
+    pState->cbData += cbData;
+
+    if( bytesUsedInKeyStreamBuffer != 0 )
+    {
+        bytesToProcess = SYMCRYPT_MIN( cbData, SYMCRYPT_GCM_BLOCK_SIZE - bytesUsedInKeyStreamBuffer );
+        SymCryptXorBytes( pbSrc, &pState->keystreamBlock[bytesUsedInKeyStreamBuffer], pbDst, bytesToProcess );
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        cbData -= bytesToProcess;
+
+        //
+        // If there are bytes left in the key stream buffer, then cbData == 0 and we're done.
+        // If we used up all the bytes, then we are fine, no need to compute the next key stream block
+        //
+    }
+
+    if( cbData >= SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        bytesToProcess = cbData & SYMCRYPT_GCM_BLOCK_ROUND_MASK;
+
+        SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+        SymCryptCtrMsb32(   pState->pKey->pBlockCipher,
+                            &pState->pKey->blockcipherKey,
+                            &pState->counterBlock[0],
+                            pbSrc,
+                            pbDst,
+                            bytesToProcess );
+
+        pbSrc += bytesToProcess;
+        pbDst += bytesToProcess;
+        cbData -= bytesToProcess;
+    }
+
+    if( cbData > 0 )
+    {
+        SymCryptWipeKnownSize( &pState->keystreamBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+
+        SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+        SymCryptCtrMsb32(   pState->pKey->pBlockCipher,
+                            &pState->pKey->blockcipherKey,
+                            &pState->counterBlock[0],
+                            &pState->keystreamBlock[0],
+                            &pState->keystreamBlock[0],
+                            SYMCRYPT_GCM_BLOCK_SIZE );
+
+        SymCryptXorBytes( &pState->keystreamBlock[0], pbSrc, pbDst, cbData );
+
+        //
+        // pState->cbData contains the data length after this call already, so it knows how many
+        // bytes are left in the keystream block
+        //
+    }
+
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmResetCounterBlock(
+    _Inout_ PSYMCRYPT_GCM_STATE pState )
+{
+    // Computing the tag for GCM requires invoking the GCTR function with the pre-counter
+    // block which was computed when the nonce was set. Historically, we only supported 12-byte
+    // nonces, so we could trivially reset the counter block by just setting the last 4 bytes to
+    // (DWORD) 1. With support for larger IVs, the pre-counter block is computed from a GHash of
+    // the nonce, and we don't store the value. Adding a field in the GCM struct to store the value
+    // would be ABI-breaking, so instead we can recompute the value by decrementing the last 32 bits
+    // of the counter block by the number of blocks that have been processed (since the counter is
+    // incremented once per block), plus one for the initial increment.
+    UINT32 preCounter32 = SYMCRYPT_LOAD_MSBFIRST32(&pState->counterBlock[12]) -
+        (UINT32) ((pState->cbData + SYMCRYPT_GCM_BLOCK_SIZE - 1) / SYMCRYPT_GCM_BLOCK_SIZE) - 1;
+
+    SYMCRYPT_STORE_MSBFIRST32(&pState->counterBlock[12], preCounter32);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmComputeTag(
+    _Inout_                                 PSYMCRYPT_GCM_STATE pState,
+    _Out_writes_( SYMCRYPT_GCM_BLOCK_SIZE ) PBYTE               pbTag )
+{
+    SYMCRYPT_ALIGN BYTE  buf[2 * SYMCRYPT_GCM_BLOCK_SIZE];
+
+    SYMCRYPT_STORE_MSBFIRST64( &buf[16], pState->cbAuthData * 8 );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[24], pState->cbData * 8 );
+
+    if( pState->bytesInMacBlock > 0 )
+    {
+        //
+        // Pad the MAC data with zeroes until we hit the block size
+        //
+        SymCryptWipeKnownSize( &buf[0], SYMCRYPT_GCM_BLOCK_SIZE );
+        memcpy( buf, &pState->macBlock[0], pState->bytesInMacBlock );
+
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &buf[0], 2 * SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+    else
+    {
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &buf[16], SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+
+    SymCryptGcmResetCounterBlock(pState);
+
+    //
+    // Convert the GHash state to an array of bytes
+    //
+    SYMCRYPT_STORE_MSBFIRST64( &buf[0], pState->ghashState.ull[1] );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[8], pState->ghashState.ull[0] );
+
+    SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+    SymCryptCtrMsb32(   pState->pKey->pBlockCipher,
+                        &pState->pKey->blockcipherKey,
+                        &pState->counterBlock[0],
+                        buf,
+                        pbTag,
+                        SYMCRYPT_GCM_BLOCK_SIZE );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+}
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmExpandKey(
+    _Out_               PSYMCRYPT_GCM_EXPANDED_KEY  pExpandedKey,
+    _In_                PCSYMCRYPT_BLOCKCIPHER      pBlockCipher,
+    _In_reads_( cbKey ) PCBYTE                      pbKey,
+                        SIZE_T                      cbKey )
+{
+    SYMCRYPT_ALIGN BYTE    H[SYMCRYPT_GCM_BLOCK_SIZE];
+    SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;
+
+    if( cbKey > SYMCRYPT_GCM_MAX_KEY_SIZE )
+    {
+        status = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    //
+    // Perform the Block cipher key expansion first
+    //
+    pExpandedKey->pBlockCipher = pBlockCipher;
+    status = pBlockCipher->expandKeyFunc( &pExpandedKey->blockcipherKey, pbKey, cbKey );
+
+    if( status != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    //
+    // We keep a copy of the key to make it easy to
+    // implement the SymCryptGcmKeyCopy function
+    //
+    pExpandedKey->cbKey = cbKey;
+    memcpy( &pExpandedKey->abKey[0], pbKey, cbKey );
+
+    //
+    // Compute H and the GHASH expanded key
+    //
+    SymCryptWipeKnownSize( H, sizeof( H ) );
+    pBlockCipher->encryptFunc( &pExpandedKey->blockcipherKey, H, H );
+
+
+    SymCryptGHashExpandKey( &pExpandedKey->ghashKey, H );
+
+
+    SYMCRYPT_SET_MAGIC( pExpandedKey );
+
+    SymCryptWipeKnownSize( H, sizeof( H ) );
+
+cleanup:
+
+    return status;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmKeyCopy( _In_ PCSYMCRYPT_GCM_EXPANDED_KEY pSrc, _Out_ PSYMCRYPT_GCM_EXPANDED_KEY pDst )
+{
+    SYMCRYPT_ERROR  status;
+
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+
+    status = SymCryptGcmExpandKey( pDst, pSrc->pBlockCipher, &pSrc->abKey[0], pSrc->cbKey );
+    SYMCRYPT_ASSERT( status == SYMCRYPT_NO_ERROR );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmSetNonce(
+    _Out_                   PSYMCRYPT_GCM_STATE         pState,
+    _In_reads_( cbNonce )   PCBYTE                      pbNonce,
+                            SIZE_T                      cbNonce )
+{
+    SYMCRYPT_ASSERT( cbNonce >= GCM_MIN_NONCE_SIZE );
+
+    // Handle the nonce depending on its size, as specified in NIST SP800-38D
+    if( cbNonce == 12 )
+    {
+        // If len(nonce) = 96 bits (12 bytes), pre-counter block = nonce || (DWORD) 1
+        memcpy( &pState->counterBlock[0], pbNonce, cbNonce );
+        SymCryptWipeKnownSize( &pState->counterBlock[12], 4 );
+        pState->counterBlock[15] = 1;
+    }
+    else
+    {
+        // If len(nonce) != 96 bits (12 bytes),
+        // pre-counter block = GHASH(nonce padded to a multiple of 128 bits || (QWORD) len(nonce))
+        BYTE buf[SYMCRYPT_GF128_BLOCK_SIZE];
+        SIZE_T cbNonceRemainder = cbNonce & (SYMCRYPT_GF128_BLOCK_SIZE - 1);
+
+        // Process all full blocks of the nonce, i.e. all nonce bytes up to a multiple of
+        // SYMCRYPT_GF128_BLOCK_SIZE. SymCryptGHashAppendData ignores additional data that are
+        // not a multiple of the block size. We will handle any such remaining data below.
+        // (This also works if the nonce is less than the block size.)
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, pbNonce, cbNonce );
+
+        // If the nonce length is not a multiple of SYMCRYPT_GF128_BLOCK_SIZE, we need to pad any
+        // remaining data to a multiple of the block size.
+        if(cbNonceRemainder > 0)
+        {
+            SymCryptWipeKnownSize( buf, sizeof(buf) );
+            memcpy(buf, pbNonce + cbNonce - cbNonceRemainder, cbNonceRemainder);
+            SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, buf, sizeof(buf) );
+        }
+
+        // Now we append the length of the nonce in bits. We take the length as a 64-bit integer,
+        // but it too must be padded to 128 bits for use in GHASH.
+        SymCryptWipeKnownSize( buf, 8 );
+        SYMCRYPT_STORE_MSBFIRST64( &buf[8], cbNonce * 8 );
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, buf, sizeof(buf) );
+
+        SymCryptGHashResult( &pState->ghashState, pState->counterBlock );
+        SymCryptWipeKnownSize( &pState->ghashState, sizeof( pState->ghashState ) );
+    }
+
+    // Increment the last 32 bits of the counter. We'll recalculate the pre-counter block later
+    // when computing the tag.
+    SYMCRYPT_STORE_MSBFIRST32(
+        &pState->counterBlock[12],
+        1 + SYMCRYPT_LOAD_MSBFIRST32( &pState->counterBlock[12] ) );
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmInit(
+    _Out_                   PSYMCRYPT_GCM_STATE         pState,
+    _In_                    PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbNonce )   PCBYTE                      pbNonce,
+                            SIZE_T                      cbNonce )
+{
+    UNREFERENCED_PARAMETER( cbNonce );          // It is used in an ASSERT, but only in CHKed builds.
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    pState->pKey = pExpandedKey;
+    pState->cbData = 0;
+    pState->cbAuthData = 0;
+    pState->bytesInMacBlock = 0;
+    SymCryptWipeKnownSize( &pState->ghashState, sizeof( pState->ghashState ) );
+
+    SymCryptGcmSetNonce(pState, pbNonce, cbNonce);
+
+    SYMCRYPT_SET_MAGIC( pState );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmStateCopy(
+    _In_        PCSYMCRYPT_GCM_STATE            pSrc,
+    _In_opt_    PCSYMCRYPT_GCM_EXPANDED_KEY     pExpandedKeyCopy,
+    _Out_       PSYMCRYPT_GCM_STATE             pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+
+    *pDst = *pSrc;
+    if( pExpandedKeyCopy != NULL )
+    {
+        pDst->pKey = pExpandedKeyCopy;
+    }
+
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmAuthPart(
+    _Inout_                     PSYMCRYPT_GCM_STATE pState,
+    _In_reads_opt_( cbData )    PCBYTE              pbAuthData,
+                                SIZE_T              cbData )
+{
+    SYMCRYPT_CHECK_MAGIC( pState );
+    SYMCRYPT_ASSERT( pState->cbData == 0 );
+
+    SymCryptGcmAddMacData( pState, pbAuthData, cbData );
+    pState->cbAuthData += cbData;
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    if( pState->cbData == 0 )
+    {
+        //
+        // This is the first actual encryption data, pad the Auth data with zeroes if needed.
+        //
+        SymCryptGcmPadMacData( pState );
+    }
+
+    if ( pState->pKey->pBlockCipher->gcmEncryptPartFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pState->pKey->pBlockCipher->gcmEncryptPartFunc) ( pState, pbSrc, pbDst, cbData );
+        SYMCRYPT_ASSERT( pState->bytesInMacBlock <= 15 );
+    }
+    else
+    {
+        SymCryptGcmEncryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptPartTwoPass(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    //
+    // Do the actual encryption
+    //
+    SymCryptGcmEncryptDecryptPart( pState, pbSrc, pbDst, cbData );
+
+    //
+    // We break the read-once/write once rule here by reading the pbDst data back.
+    // In this particular situation this is safe, and avoiding it is expensive as it
+    // requires an extra copy and an extra memory buffer.
+    // The first write exposes the GCM key stream, independent of the underlying data that
+    // we are processing. From an attacking point of view we can think of this as literally
+    // handing over the key stream. So encryption consists of two steps:
+    // - hand over the key stream
+    // - MAC some ciphertext
+    // In this view (which has equivalent security properties to GCM) is obviously doesn't
+    // matter that we read pbDst back.
+    //
+
+    SymCryptGcmAddMacData( pState, pbDst, cbData );
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmDecryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    if( pState->cbData == 0 )
+    {
+        //
+        // This is the first actual encryption data, pad the Auth data with zeroes if needed.
+        //
+        SymCryptGcmPadMacData( pState );
+    }
+
+    if ( pState->pKey->pBlockCipher->gcmDecryptPartFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pState->pKey->pBlockCipher->gcmDecryptPartFunc) ( pState, pbSrc, pbDst, cbData );
+        SYMCRYPT_ASSERT( pState->bytesInMacBlock <= 15 );
+    }
+    else
+    {
+        SymCryptGcmDecryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmDecryptPartTwoPass(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData )
+{
+    SymCryptGcmAddMacData( pState, pbSrc, cbData );
+
+    //
+    // Do the actual decryption
+    // This violates the read-once rule, but it is safe for the same reasons as above
+    // in the encryption case.
+    //
+
+    SymCryptGcmEncryptDecryptPart( pState, pbSrc, pbDst, cbData );
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptFinal(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _Out_writes_( cbTag )   PBYTE               pbTag,
+                            SIZE_T              cbTag )
+{
+    SYMCRYPT_ALIGN BYTE    buf[SYMCRYPT_GCM_BLOCK_SIZE];
+
+    SYMCRYPT_ASSERT( cbTag >= GCM_MIN_TAG_SIZE && cbTag <= GCM_MAX_TAG_SIZE );
+
+    SymCryptGcmComputeTag( pState, &buf[0] );
+    memcpy( pbTag, buf, cbTag );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SYMCRYPT_ASSERT( pState->bytesInMacBlock == 0 );
+}
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmDecryptFinal(
+    _Inout_             PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbTag ) PCBYTE              pbTag,
+                        SIZE_T              cbTag )
+{
+    SYMCRYPT_ALIGN BYTE    buf[SYMCRYPT_GCM_BLOCK_SIZE];
+    SYMCRYPT_ERROR status;
+
+    SYMCRYPT_ASSERT( cbTag >= GCM_MIN_TAG_SIZE && cbTag <= GCM_MAX_TAG_SIZE );
+
+    SymCryptGcmComputeTag( pState, &buf[0] );
+
+    if( !SymCryptEqual( pbTag, buf, cbTag ) )
+    {
+        status = SYMCRYPT_AUTHENTICATION_FAILURE;
+    }
+    else
+    {
+        status = SYMCRYPT_NO_ERROR;
+    }
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SYMCRYPT_ASSERT( pState->bytesInMacBlock == 0 );
+
+    return status;
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncrypt(
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                      pbNonce,
+                                    SIZE_T                      cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _Out_writes_( cbTag )           PBYTE                       pbTag,
+                                    SIZE_T                      cbTag )
+{
+    SYMCRYPT_ALIGN BYTE buf[2 * SYMCRYPT_GCM_BLOCK_SIZE];
+    SYMCRYPT_GCM_STATE  state;
+    PSYMCRYPT_GCM_STATE pState = &state;
+
+    // SymCryptGcmInit( &state, pExpandedKey, pbNonce, cbNonce );
+    UNREFERENCED_PARAMETER( cbNonce );          // It is used in an ASSERT, but only in CHKed builds.
+
+    SYMCRYPT_ASSERT( cbNonce >= GCM_MIN_NONCE_SIZE );
+    SYMCRYPT_ASSERT( cbTag >= GCM_MIN_TAG_SIZE && cbTag <= GCM_MAX_TAG_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    pState->pKey = pExpandedKey;
+    pState->cbData = 0;
+    pState->cbAuthData = 0;
+    pState->bytesInMacBlock = 0;
+    SymCryptWipeKnownSize( &pState->ghashState, sizeof( pState->ghashState ) );
+
+    SymCryptGcmSetNonce( pState, pbNonce, cbNonce );
+
+    // SymCryptGcmAuthPart( &state, pbAuthData, cbAuthData );
+    pState->cbAuthData += cbAuthData;
+    if( cbAuthData >= SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        SIZE_T bytesToDo = cbAuthData & SYMCRYPT_GCM_BLOCK_ROUND_MASK;
+
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, pbAuthData, bytesToDo );
+
+        pbAuthData += bytesToDo;
+        cbAuthData -= bytesToDo;
+    }
+
+    if( cbAuthData > 0 )
+    {
+        //
+        // Pad the MAC data with zeroes until we hit the block size.
+        //
+        SymCryptWipeKnownSize( &pState->macBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+        memcpy( &pState->macBlock[0], pbAuthData, cbAuthData );
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &pState->macBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+
+    // SymCryptGcmEncryptPart( &state, pbSrc, pbDst, cbData );
+    if ( pState->pKey->pBlockCipher->gcmEncryptPartFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pState->pKey->pBlockCipher->gcmEncryptPartFunc) ( pState, pbSrc, pbDst, cbData );
+    }
+    else
+    {
+        SymCryptGcmEncryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+    // SymCryptGcmEncryptFinal( &state, pbTag, cbTag );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[16], pState->cbAuthData * 8 );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[24], pState->cbData * 8 );
+
+    if( pState->bytesInMacBlock > 0 )
+    {
+        //
+        // Pad the MAC data with zeroes until we hit the block size
+        //
+        SymCryptWipeKnownSize( &buf[0], SYMCRYPT_GCM_BLOCK_SIZE );
+        memcpy( buf, &pState->macBlock[0], pState->bytesInMacBlock );
+
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &buf[0], 2 * SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+    else
+    {
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &buf[16], SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+
+    // Reset the counter block prior to computing the tag
+    SymCryptGcmResetCounterBlock( pState );
+
+    //
+    // Convert the GHash state to an array of bytes
+    //
+    SYMCRYPT_STORE_MSBFIRST64( &buf[0], pState->ghashState.ull[1] );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[8], pState->ghashState.ull[0] );
+
+    SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+    SymCryptCtrMsb32(   pState->pKey->pBlockCipher,
+                        &pState->pKey->blockcipherKey,
+                        &pState->counterBlock[0],
+                        buf,
+                        buf,
+                        SYMCRYPT_GCM_BLOCK_SIZE );
+
+    memcpy( pbTag, buf, cbTag );
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+}
+
+
+SYMCRYPT_NOINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptGcmDecrypt(
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbNonce )           PCBYTE                      pbNonce,
+                                    SIZE_T                      cbNonce,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _In_reads_( cbTag )             PCBYTE                      pbTag,
+                                    SIZE_T                      cbTag )
+{
+    SYMCRYPT_ERROR status;
+    SYMCRYPT_ALIGN BYTE buf[2 * SYMCRYPT_GCM_BLOCK_SIZE];
+    SYMCRYPT_GCM_STATE  state;
+    PSYMCRYPT_GCM_STATE pState = &state;
+
+    // SymCryptGcmInit( &state, pExpandedKey, pbNonce, cbNonce );
+    UNREFERENCED_PARAMETER( cbNonce );          // It is used in an ASSERT, but only in CHKed builds.
+
+    SYMCRYPT_ASSERT( cbNonce >= GCM_MIN_NONCE_SIZE );
+    SYMCRYPT_ASSERT( cbTag >= GCM_MIN_TAG_SIZE && cbTag <= GCM_MAX_TAG_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    pState->pKey = pExpandedKey;
+    pState->cbData = 0;
+    pState->cbAuthData = 0;
+    pState->bytesInMacBlock = 0;
+    SymCryptWipeKnownSize( &pState->ghashState, sizeof( pState->ghashState ) );
+
+    SymCryptGcmSetNonce( pState, pbNonce, cbNonce );
+
+    // SymCryptGcmAuthPart( &state, pbAuthData, cbAuthData );
+    pState->cbAuthData += cbAuthData;
+    if( cbAuthData >= SYMCRYPT_GCM_BLOCK_SIZE )
+    {
+        SIZE_T bytesToDo = cbAuthData & SYMCRYPT_GCM_BLOCK_ROUND_MASK;
+
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, pbAuthData, bytesToDo );
+
+        pbAuthData += bytesToDo;
+        cbAuthData -= bytesToDo;
+    }
+
+    if( cbAuthData > 0 )
+    {
+        //
+        // Pad the MAC data with zeroes until we hit the block size.
+        //
+        SymCryptWipeKnownSize( &pState->macBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+        memcpy( &pState->macBlock[0], pbAuthData, cbAuthData );
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &pState->macBlock[0], SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+
+    // SymCryptGcmDecryptPart( &state, pbSrc, pbDst, cbData );
+    if ( pState->pKey->pBlockCipher->gcmDecryptPartFunc != NULL )
+    {
+        //
+        // Use optimized implementation if available
+        //
+        (*pState->pKey->pBlockCipher->gcmDecryptPartFunc) ( pState, pbSrc, pbDst, cbData );
+    }
+    else
+    {
+        SymCryptGcmDecryptPartTwoPass( pState, pbSrc, pbDst, cbData );
+    }
+
+    //status = SymCryptGcmDecryptFinal( &state, pbTag, cbTag );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[16], pState->cbAuthData * 8 );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[24], pState->cbData * 8 );
+
+    if( pState->bytesInMacBlock > 0 )
+    {
+        //
+        // Pad the MAC data with zeroes until we hit the block size
+        //
+        SymCryptWipeKnownSize( &buf[0], SYMCRYPT_GCM_BLOCK_SIZE );
+        memcpy( buf, &pState->macBlock[0], pState->bytesInMacBlock );
+
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &buf[0], 2 * SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+    else
+    {
+        SymCryptGHashAppendData( &pState->pKey->ghashKey, &pState->ghashState, &buf[16], SYMCRYPT_GCM_BLOCK_SIZE );
+    }
+
+    SymCryptGcmResetCounterBlock( pState );
+
+    //
+    // Convert the GHash state to an array of bytes
+    //
+    SYMCRYPT_STORE_MSBFIRST64( &buf[0], pState->ghashState.ull[1] );
+    SYMCRYPT_STORE_MSBFIRST64( &buf[8], pState->ghashState.ull[0] );
+
+    SYMCRYPT_ASSERT( pState->pKey->pBlockCipher->blockSize == SYMCRYPT_GCM_BLOCK_SIZE );
+    SymCryptCtrMsb32(   pState->pKey->pBlockCipher,
+                        &pState->pKey->blockcipherKey,
+                        &pState->counterBlock[0],
+                        buf,
+                        buf,
+                        SYMCRYPT_GCM_BLOCK_SIZE );
+
+    if( !SymCryptEqual( pbTag, buf, cbTag ) )
+    {
+        status = SYMCRYPT_AUTHENTICATION_FAILURE;
+    }
+    else
+    {
+        status = SYMCRYPT_NO_ERROR;
+    }
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+
+    if( status != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptWipe( pbDst, cbData );
+    }
+
+    return status;
+}
+
+
+static const BYTE SymCryptGcmSelftestResult[3 + SYMCRYPT_AES_BLOCK_SIZE ] =
+{
+    0xa5, 0x4c, 0x60,
+    0x80, 0xb0, 0x48, 0x6d, 0x03, 0x9f, 0xea, 0xc3, 0x3c, 0x28, 0x96, 0x3f, 0x99, 0x8a, 0x77, 0x43,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmSelftest(void)
+{
+    BYTE    buf[ 3 + SYMCRYPT_AES_BLOCK_SIZE ];
+    SYMCRYPT_GCM_EXPANDED_KEY key;
+    SYMCRYPT_ERROR err;
+
+    if( SymCryptGcmExpandKey( &key, SymCryptAesBlockCipher, SymCryptTestKey32, 16 ) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'gcm0' );
+    }
+
+    SymCryptGcmEncrypt( &key,
+                        &SymCryptTestKey32[16], 12,
+                        NULL, 0,
+                        &SymCryptTestMsg3[0], buf, 3,
+                        &buf[3], SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+    if( memcmp( buf, SymCryptGcmSelftestResult, sizeof( buf ) ) != 0 )
+    {
+        SymCryptFatal( 'gcm1' );
+    }
+
+    // inject error into the ciphertext or tag
+    SymCryptInjectError( buf, sizeof( buf ) );
+
+    err = SymCryptGcmDecrypt(   &key,
+                                &SymCryptTestKey32[16], 12,
+                                NULL, 0,
+                                buf, buf, 3,
+                                &buf[3], SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptInjectError( buf, 3 );
+
+    if( err != SYMCRYPT_NO_ERROR || memcmp( buf, SymCryptTestMsg3, 3 ) != 0 )
+    {
+        SymCryptFatal( 'gcm2' );
+    }
+
+}
diff --git a/libs/symcrypt/lib/gen_int.c b/libs/symcrypt/lib/gen_int.c
new file mode 100644
index 000000000000..5f5983358a08
--- /dev/null
+++ b/libs/symcrypt/lib/gen_int.c
@@ -0,0 +1,368 @@
+//
+// gen_int.c   Generic integer algorithms (not tied to low-level implementations)
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+
+UINT64
+SYMCRYPT_CALL
+SymCryptUint64Gcd( UINT64 a, UINT64 b, UINT32 flags )
+{
+    UINT64 swap;
+    UINT64 tmp;
+    UINT64 a2;
+    UINT64 b2;
+    UINT32 i;
+
+/*
+    Algorithm outline:
+
+    if( b even )
+        swap (a,b)
+
+ loop:
+    { invariant: b is odd }
+    if( a even )
+        a = a/2
+    else
+        if a < b
+            swap (a,b)
+        a = (a - b) / 2
+
+    We ignore the data_public flag as we currently always use a side-channel safe implementation
+
+    to compute (a < b) on 64-bit values is hard if we want to avoid
+*/
+    SYMCRYPT_ASSERT( (flags & SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN) != 0 && ((a | b) & 1) != 0 );
+    UNREFERENCED_PARAMETER( flags );
+
+    // First we make sure that b is odd
+    // If b even: swap (a,b)
+    swap = ~(0 - (b & 1));
+    tmp = (a ^ b) & swap;
+    a ^= tmp;
+    b ^= tmp;
+
+    // Each loop iteration reduces len(a) + len(b) by at least 1, so looping 127 times is enough.
+    // For inputs (2^63, 2^63 + 1) we get 63 iterations to reduce a to 1, and then another 63 to get
+    // the other value to 1, plus one more to make it 0.
+    for( i=0; i < 127; i++ )
+    {
+        // Compute the result of the 'else' part of the if( a even ) into (a2, b2)
+        // First we evaluate (a < b), which is a bit tricky without access to the carry flag.
+        // a < b =      (b>>63) if ((a^b) >> 63) == 1
+        //              (a - b) >> 63 otherwise
+        tmp = a ^ b;
+        tmp = (tmp & b) | (~tmp & (a-b));
+        swap = 0 - (tmp >> 63);
+
+        // Now swap if a < b into (a2, b2)
+        tmp = (a ^ b) & swap;
+        a2 = a ^ tmp;
+        b2 = b ^ tmp;
+
+        //
+        a2 = (a2 - b2) / 2;
+
+        // Compute the (a is odd) condition
+        tmp = 0 - (a & 1);
+
+        // Assemble the final result
+        a = (tmp & a2) | (~tmp & a/2);
+        b = (tmp & b2) | (~tmp & b);
+    }
+
+    SYMCRYPT_ASSERT( a == 0 );
+    return b;
+}
+
+
+/*
+Extended GCD notes.
+
+A side-channel safe implementation cannot effectively use Euclid's algorithm.
+The quotient is typically very small, but it can be very large. An SCS implementation
+would require the quotient to always be treated as a full-sized number, which would kill performance.
+Instead we use the binary algorithm which is easier to adapt to side-channel safety.
+
+Basic algorithm for inputs S1 and S2:
+    Eliminate the joint factors of two. These are added later to the result
+    For now we assume that both S1 and S2 are non-zero and S2 is odd.
+
+Invariant:
+    A = A1 * S1 (mod S2)
+    B = B1 * S1 (mod S2)
+    B is odd
+
+Initial values:
+    A = S1; A1 = 1;
+    B = S2; B1 = 0;
+
+Main loop:
+
+    t = len(A) + len(B) - 1     // Careful of overflows, use a SIZE_T
+
+    repeat t times:
+        1. if A odd and A < B:
+                Swap (A, A1) with (B, B1)
+        2. if A odd:
+                A -= B;
+                A1 -= B1 (mod S2);
+        3. A /= 2;
+           A1 /= 2 (mod S2);
+
+Proof of the invariant:
+    It is easy to see that initially the invariant holds (S2 is odd).
+
+    Assume the invariant holds at the start of the loop's iteration.
+    Step 1 of the main loop preserves the invariant since the first 2
+    equations of the invariant are the same for A's and B's and
+    the swapping happens only if A is odd. Therefore, B is odd
+    after step 1.
+    Step 2 essentially subtracts the second equation of the invariant
+    from the first (modulo S2). This preserves the invariant since step
+    1 ensured that A >= B (when A odd), so the operation A = A-B holds
+    modulo S2.
+    Step 3 essentially multiplies the first equation of the invariant
+    with the inverse of 2 modulo S2. Since S2 is odd we know that the
+    inverse exists. Also the operation A = A/2 is correct modulo S2
+    because steps 1 and 2 ensured that A is even at this point.
+    (To see this, consider 2*a = x (mod S2) => a = x*2^{-1} (mod S2)
+     where a is an integer and 2^{-1} is the inverse of 2 modulo S2)
+
+Termination/Results:
+    Each iteration reduces len(A) + len(B) by at least one until A=0.
+    When A=0 the loop does nothing except churn by dividing A and A1
+    by 2 every time.
+    After len(A)+len(B)-1 iterations, A must be zero. At that point
+    we have
+
+        B = GCD
+        B1 * S1 = GCD (mod S2)
+
+    The LCM is calculated as S1*S2 / GCD.
+
+    InvS1ModS2 is defined as the smallest value X such that
+    X*S1 = GCD (mod S2), but B1 might not be the smallest solution.
+    Let P2 = S2/GCD.
+    Any two solutions to X*S1 = GCD (mod S2) has (X1-X2)*S1 mod S2 = 0,
+    so X1-X2 is a multiple of P2. Therefore we need to reduce B1 modulo P2
+    to get the smallest solution for InvS1ModS2.
+
+    ** Notice that if B1 is a multiple of S2 (or 0), which means that GCD is equal to S2,
+    then the above result is 0. In that case InvS1ModS2 is undefined.
+
+    Similarly, InvS2ModS1 is defined as the smallest value Y such that
+    Y*S2 = GCD (mod S1). We have that for some integer q:
+
+        q*S2 = B1*S1 - GCD       =>      (-q mod S1) * S2 = GCD (mod S1)
+
+    As above, if B1 is 0, then InvS2ModS1 is undefined. Therefore we ignore this case.
+    For the defined case, B1>=1 and S1>=GCD which implies that q >= 0. This
+    allows us to divide (B1*S1 - GCD) by S2.
+    Therefore InvS2ModS1 can be computed as -((B1*S1 - GCD)/S2) mod S1.
+
+For simplicity, our generic implementation works with all values the same size.
+This can be less efficient if one input is much larger than the other, for
+example for RSA key generation when one input is 1000+ bits and the other 17 bits.
+However, that is not a high-performance path. If it is, a dedicated GCD with one
+input a UINT32 or UINT64 would be the solution to a much faster extended GCD.
+*/
+VOID
+SYMCRYPT_CALL
+SymCryptIntExtendedGcd(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+                                    UINT32          flags,
+    _Out_opt_                       PSYMCRYPT_INT   piGcd,
+    _Out_opt_                       PSYMCRYPT_INT   piLcm,
+    _Out_opt_                       PSYMCRYPT_INT   piInvSrc1ModSrc2,
+    _Out_opt_                       PSYMCRYPT_INT   piInvSrc2ModSrc1,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch )
+{
+    UINT32 nDigits  = SYMCRYPT_MAX( SymCryptIntDigitsizeOfObject( piSrc1 ), SymCryptIntDigitsizeOfObject( piSrc2 ));
+    PSYMCRYPT_INT       piA;        // size nDigits
+    PSYMCRYPT_INT       piB;        // size nDigits, NOT ALLOCATED (part of the pdGcd divisor)
+    PSYMCRYPT_INT       piTmp;      // size nDigits
+    PSYMCRYPT_INT       piA1;       // size nDigits
+    PSYMCRYPT_INT       piB1;       // size nDigits
+    PSYMCRYPT_INT       piTmpDbl;   // size 2*nDigits
+    PSYMCRYPT_DIVISOR   pdGcd;      // size nDigits
+    PSYMCRYPT_DIVISOR   pdTmp;      // size nDigits
+    UINT32              cbInt;
+    UINT32              cbWideInt;
+    UINT32              cbDivisor;
+    SIZE_T              cbFnScratch;
+    UINT32              t;
+    UINT32              c;
+    UINT32              d;
+
+    UNREFERENCED_PARAMETER( flags );    // Currently not used to improve performance.
+
+    // Compute how much scratch space we need for the functions we call
+    cbFnScratch = SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( 2 * nDigits, nDigits );
+    cbFnScratch = SYMCRYPT_MAX( cbFnScratch, SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( 2*nDigits ) );
+    cbFnScratch = SYMCRYPT_MAX( cbFnScratch, SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( nDigits ) );
+
+    cbInt = SymCryptSizeofIntFromDigits( nDigits );
+    cbWideInt = SymCryptSizeofIntFromDigits( 2*nDigits );
+    cbDivisor = SymCryptSizeofDivisorFromDigits( nDigits );
+
+    SYMCRYPT_ASSERT( cbWideInt != 0 );
+    SYMCRYPT_ASSERT( cbScratch >=   4 * cbInt +
+                                    1 * cbWideInt +
+                                    2 * cbDivisor +
+                                    cbFnScratch );
+
+    piA = SymCryptIntCreate( pbScratch, cbInt, nDigits );
+    pbScratch += cbInt; cbScratch -= cbInt;
+    // piB is stored inside the pdGcd object created later
+    piTmp = SymCryptIntCreate( pbScratch, cbInt, nDigits );
+    pbScratch += cbInt; cbScratch -= cbInt;
+    piA1 = SymCryptIntCreate( pbScratch, cbInt, nDigits );
+    pbScratch += cbInt; cbScratch -= cbInt;
+    piB1 = SymCryptIntCreate( pbScratch, cbInt, nDigits );
+    pbScratch += cbInt; cbScratch -= cbInt;
+
+    piTmpDbl = SymCryptIntCreate( pbScratch, cbWideInt, 2 * nDigits );
+    pbScratch += cbWideInt; cbScratch -= cbWideInt;
+
+    pdGcd = SymCryptDivisorCreate( pbScratch, cbDivisor, nDigits );
+    pbScratch += cbDivisor; cbScratch -= cbDivisor;
+    piB = SymCryptIntFromDivisor( pdGcd );
+
+    pdTmp = SymCryptDivisorCreate( pbScratch, cbDivisor, nDigits );
+    pbScratch += cbDivisor; cbScratch -= cbDivisor;
+
+    SymCryptIntCopyMixedSize( piSrc1, piA );    // Ignore the error return value here as we know
+    SymCryptIntCopyMixedSize( piSrc2, piB );    // that the destination integers are large enough.
+
+    SymCryptIntSetValueUint32( 1, piA1 );
+    SymCryptIntSetValueUint32( 0, piB1 );
+
+    // Currently not supported: Src1 to be 0 or Src2 to be even
+    SYMCRYPT_ASSERT( !SymCryptIntIsEqualUint32( piA, 0 ) );
+    SYMCRYPT_ASSERT( (SymCryptIntGetValueLsbits32( piB ) & 1) != 0 );
+    if ( SymCryptIntIsEqualUint32( piA, 0 ) ||
+        ((SymCryptIntGetValueLsbits32( piB ) & 1) == 0) )
+    {
+        goto cleanup;
+    }
+
+    // Currently not supported: piInvSrc2ModSrc1 != NULL and max( Src1.nDigits, Src2.nDigits ) * 2 > SymCryptDigitsFromBits(SYMCRYPT_INT_MAX_BITS)
+    if( (piInvSrc2ModSrc1 != NULL) && (piTmpDbl == NULL) )
+    {
+        goto cleanup;
+    }
+
+    t = SymCryptIntBitsizeOfObject( piSrc1 ) + SymCryptIntBitsizeOfObject( piSrc2 ) - 1;
+    while( t > 0 )
+    {
+        t--;
+
+        //if A odd and A < B:
+        //    Swap (A, A1) with (B, B1)
+        c = 1 & (SymCryptIntGetValueLsbits32( piA ) & SymCryptIntSubSameSize( piA, piB, piTmp ) );
+        SymCryptIntConditionalSwap( piA, piB, c );
+        SymCryptIntConditionalSwap( piA1, piB1, c );
+
+        //if A odd:
+        //    A -= B;     A1 -= B1 (mod S2);
+        c = 1 & SymCryptIntGetValueLsbits32( piA );
+        SymCryptIntSubSameSize( piA, piB, piTmp );          // Never a carry due to the previous conditional swap
+        SymCryptIntConditionalCopy( piTmp, piA, c );
+
+        d = SymCryptIntSubSameSize( piA1, piB1, piTmp );
+        SymCryptIntConditionalCopy( piTmp, piA1, c );
+        SymCryptIntAddMixedSize( piA1, piSrc2, piTmp );
+        SymCryptIntConditionalCopy( piTmp, piA1, c & d );
+
+        // A /= 2;      A1 /= 2 (mod S2);
+        SYMCRYPT_ASSERT( (SymCryptIntGetValueLsbits32( piA ) & 1) == 0 );
+        SymCryptIntShr1( 0, piA, piA );
+        c = SymCryptIntGetValueLsbits32( piA1 ) & 1;
+        d = SymCryptIntAddMixedSize( piA1, piSrc2, piTmp );
+        SymCryptIntConditionalCopy( piTmp, piA1, c );
+        SymCryptIntShr1( c & d, piA1, piA1 );
+
+    }
+
+    // B = GCD, B1 * S1 = GCD (mod S2)
+    // A = 0, A1 is scratch
+    //
+    // Algorithm from here:
+    // GCD as divisor
+    // LCM = S1 * S2 / GCD.
+    // P2 = S2 / GCD, as divisor (only for InvS1ModS2)
+    // InvS1ModS2 = B1 mod P2
+    // InvS2ModS1 = -((B1*S1 - GCD) div S2) mod S1
+
+    if( piGcd != NULL )
+    {
+        SymCryptIntCopyMixedSize( piB, piGcd );
+    }
+
+    if( piLcm == NULL && piInvSrc1ModSrc2 == NULL && piInvSrc2ModSrc1 == NULL )
+    {
+        // Only GCD needed; don't do the other work
+        goto cleanup;
+    }
+
+    SymCryptIntCopyMixedSize( piB, SymCryptIntFromDivisor( pdGcd ) );                // copy into INT of the right size
+
+    // IntToDivisor requirement:
+    //      Gcd !=0
+    SymCryptIntToDivisor( SymCryptIntFromDivisor( pdGcd ), pdGcd, 3, 0, pbScratch, cbScratch );
+
+    if( piLcm != NULL )
+    {
+        // LCM = S1 * S2 / GCD
+        SymCryptIntMulMixedSize( piSrc1, piSrc2, piLcm, pbScratch, cbScratch );
+        SymCryptIntDivMod( piLcm, pdGcd, piLcm, NULL, pbScratch, cbScratch );
+    }
+
+    if( piInvSrc1ModSrc2 != NULL )
+    {
+        // Future optimization: if GCD == 1 then we can just copy B1.
+        SymCryptIntDivMod( piSrc2, pdGcd, SymCryptIntFromDivisor( pdTmp ), NULL, pbScratch, cbScratch );
+
+        // IntToDivisor requirement:
+        //      Src2 / pdGcd > 0
+        SymCryptIntToDivisor( SymCryptIntFromDivisor( pdTmp ), pdTmp, 1, 0, pbScratch, cbScratch );
+        SymCryptIntDivMod( piB1, pdTmp, NULL, piInvSrc1ModSrc2, pbScratch, cbScratch );
+    }
+
+    if( piInvSrc2ModSrc1 != NULL )
+    {
+        //  InvS2ModS1 = - ( (B1*S1 - GCD)/S2 ) mod S1
+
+        // S2 as divisor
+        SymCryptIntCopyMixedSize( piSrc2, SymCryptIntFromDivisor( pdTmp ) );
+
+        // IntToDivisor requirement:
+        //      Src2 is odd --> Src2 != 0
+        SymCryptIntToDivisor( SymCryptIntFromDivisor( pdTmp ), pdTmp, 1, 0, pbScratch, cbScratch );
+
+        SymCryptIntMulMixedSize( piB1, piSrc1, piTmpDbl, pbScratch, cbScratch );
+        SymCryptIntSubMixedSize( piTmpDbl, piB, piTmpDbl );     // Never a borrow if B1 >= 1
+        SymCryptIntDivMod( piTmpDbl, pdTmp, piTmpDbl, NULL, pbScratch, cbScratch );
+
+        // and reduce modulo S1
+        SymCryptIntCopyMixedSize( piSrc1, SymCryptIntFromDivisor( pdTmp ) );
+
+        // IntToDivisor requirement:
+        //      Src1 > 0
+        SymCryptIntToDivisor( SymCryptIntFromDivisor( pdTmp ), pdTmp, 1, 0, pbScratch, cbScratch );
+        SymCryptIntDivMod( piTmpDbl, pdTmp, NULL, piInvSrc2ModSrc1, pbScratch, cbScratch );
+
+        // Negative modulo S1
+        SymCryptIntSubMixedSize( SymCryptIntFromDivisor( pdTmp ), piInvSrc2ModSrc1, piInvSrc2ModSrc1 );   // Never a borrow as piInvSrc2ModSrc1 < S1
+    }
+
+cleanup:
+    return; // Need a statement after a label...
+}
diff --git a/libs/symcrypt/lib/ghash.c b/libs/symcrypt/lib/ghash.c
new file mode 100644
index 000000000000..32533a74dfb7
--- /dev/null
+++ b/libs/symcrypt/lib/ghash.c
@@ -0,0 +1,951 @@
+//
+// GHASH.c
+//
+// Implementation of the NIST SP800-38D GHASH function which is the
+// core authentication function for the GCM and GMAC modes.
+//
+// This implementation was done by Niels Ferguson for the RSA32.lib library in 2008,
+// and adapted to the SymCrypt library in 2009.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+#include "ghash_definitions.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// Platform-independent code
+//
+
+//
+// GHashExpandKeyC
+// Generic GHash key expansion routine, works on all platforms.
+// This function computes a table of H, Hx, Hx^2, Hx^3, ..., Hx^127
+//
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyC(
+    _Out_writes_( SYMCRYPT_GF128_FIELD_SIZE )   PSYMCRYPT_GF128_ELEMENT expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )    PCBYTE                  pH )
+{
+    UINT64 H0, H1, t;
+    UINT32 i;
+
+    //
+    // (H1, H0) form a 128-bit integer, H1 is the upper part, H0 the lower part.
+    // Convert pH[] to (H1, H0) using MSByte first convention.
+    //
+    H1 = SYMCRYPT_LOAD_MSBFIRST64( &pH[0] );
+    H0 = SYMCRYPT_LOAD_MSBFIRST64( &pH[8] );
+
+    for( i=0; i<SYMCRYPT_GF128_FIELD_SIZE; i++ )
+    {
+        expandedKey[i].ull[0] = H0;
+        expandedKey[i].ull[1] = H1;
+        //
+        // Multiply (H1,H0) by x in the GF(2^128) field using the field encoding from SP800-38D
+        //
+        t =  UINT64_NEG(H0 & 1) & ((UINT64)GF128_FIELD_R_BYTE << (8 * ( sizeof( UINT64 ) - 1 )) ) ;
+        H0 = (H0 >> 1) | (H1 << 63);
+        H1 = (H1 >> 1) ^ t;
+    }
+}
+
+
+//
+// GHashAppendDataC
+// Generic GHash routine, works on all platforms.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataC(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE )  PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                  PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                     PCBYTE                      pbData,
+                                             SIZE_T                      cbData )
+{
+    UINT64 R0, R1;
+    UINT64 mask;
+    SYMCRYPT_ALIGN UINT32 state32[4];
+    UINT32 t;
+    int i,j;
+    while( cbData >= SYMCRYPT_GF128_BLOCK_SIZE )
+    {
+        R0 = R1 = 0;
+
+        //
+        // We have two nested loops so that we can do most of our operations
+        // on 32-bit words. 64-bit rotates/shifts can be really slow on a 32-bit CPU.
+        // On AMD64 we use the XMM version which is much faster.
+        //
+        state32[0] = (UINT32)pState->ull[0];
+        state32[1] = (UINT32)(pState->ull[0] >> 32);
+        state32[2] = (UINT32)pState->ull[1];
+        state32[3] = (UINT32)(pState->ull[1] >> 32);
+        for( i=0; i<4; i++ )
+        {
+            t = SYMCRYPT_LOAD_MSBFIRST32( &pbData[4*i] ) ^ state32[3-i];
+            for( j=31; j>=0; j-- )
+            {
+                mask = (UINT64)( -(INT64)(t & 1 ));
+                R0 ^= expandedKeyTable[32*i+j].ull[0] & mask;
+                R1 ^= expandedKeyTable[32*i+j].ull[1] & mask;
+                t >>= 1;
+            }
+        }
+        pState->ull[0] = R0;
+        pState->ull[1] = R1;
+        pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+        cbData -= SYMCRYPT_GF128_BLOCK_SIZE;
+    }
+
+    SymCryptWipeKnownSize( state32, sizeof( state32 ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashResult(
+    _In_                                        PCSYMCRYPT_GF128_ELEMENT    pState,
+    _Out_writes_( SYMCRYPT_GF128_BLOCK_SIZE )   PBYTE                       pbResult )
+{
+    SYMCRYPT_STORE_MSBFIRST64( pbResult    , pState->ull[1] );
+    SYMCRYPT_STORE_MSBFIRST64( pbResult + 8, pState->ull[0] );
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// XMM code
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyXmm(
+    _Out_writes_( SYMCRYPT_GF128_FIELD_SIZE )   PSYMCRYPT_GF128_ELEMENT expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )    PCBYTE                  pH )
+{
+    //
+    // We use the same layout for XMM code as we did for C code, so we can use the same key
+    // expansion code.
+    // Improvement: we can add an expansion routine that uses the XMM registers for speed.
+    //
+
+    SymCryptGHashExpandKeyC( expandedKey, pH );
+}
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("sse2"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("sse2")
+#endif
+
+//
+// The XMM-based GHash append data function, only on AMD64 & X86
+//
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataXmm(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbData,
+                                            SIZE_T                      cbData )
+{
+    __m128i R;
+    __m128i cmpValue;
+    __m128i mask;
+    __m128i T;
+    __m128i tmp;
+
+    PCSYMCRYPT_GF128_ELEMENT   p;
+    PCSYMCRYPT_GF128_ELEMENT   pLimit;
+    UINT32 t;
+    int i;
+
+    cmpValue = _mm_setzero_si128();             // cmpValue = 0
+
+    while( cbData >= SYMCRYPT_GF128_BLOCK_SIZE )
+    {
+        R = _mm_setzero_si128();
+
+        //
+        // The amd64 compiler can't optimize array indices in a loop where
+        // you use _mm intrinsics,
+        // so we do all the pointer arithmetic for the compiler.
+        //
+        p = &expandedKeyTable[0];
+        pLimit = &expandedKeyTable[32];
+
+        for( i=0; i<4; i++ )
+        {
+            //
+            // Set up our XMM register with 4 identical 32-bit integers so that
+            // we can generate the mask from the individual bits of the 32-bit value.
+            // Note the use of tmp; if we assign directly to the fields of T the
+            // compiler no longer caches T in an XMM register, which is bad.
+            //
+            // There are XMM instructions where we can do the duplication in the XMM
+            // registers, but they require SSE3 support, and this code only requires
+            // SSE2. As the inner loop consumes most of the time, it isn't worth
+            // using the SSE3 instructions.
+            //
+            // Note that accessing the state as an array of UINT32s depends on the
+            // endianness of the CPU, but this is XMM code that only runs on
+            // little endian machines.
+            //
+            t = SYMCRYPT_LOAD_MSBFIRST32( &pbData[4*i] ) ^ pState->ul[3-i];
+            tmp = _mm_set_epi32(t, t, t, t);
+
+            T = tmp;
+            while( p < pLimit )
+            {
+                //
+                // p and plimit are always at indexes that are multiples of 4 from
+                // the start of the array.
+                // We need to explain to prefast that this means that p <= pLimit - 4
+                //
+                SYMCRYPT_ASSERT( p <= pLimit - 4 );
+
+                mask = _mm_cmpgt_epi32( cmpValue, T );
+                T = _mm_add_epi32( T, T );
+                mask = _mm_and_si128( mask, p[0].m128i );
+                R = _mm_xor_si128( R, mask );
+
+                mask = _mm_cmpgt_epi32( cmpValue, T );
+                T = _mm_add_epi32( T, T );
+                mask = _mm_and_si128( mask, p[1].m128i );
+                R = _mm_xor_si128( R, mask );
+
+                mask = _mm_cmpgt_epi32( cmpValue, T );
+                T = _mm_add_epi32( T, T );
+                mask = _mm_and_si128( mask, p[2].m128i );
+                R = _mm_xor_si128( R, mask );
+
+                mask = _mm_cmpgt_epi32( cmpValue, T );
+                T = _mm_add_epi32( T, T );
+                mask = _mm_and_si128( mask, p[3].m128i );
+                R = _mm_xor_si128( R, mask );
+
+                p += 4;
+            }
+            pLimit += 32;
+        }
+
+        pState->m128i = R;
+        pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+        cbData -= SYMCRYPT_GF128_BLOCK_SIZE;
+    }
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif
+
+#if SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+//
+// The NEON-based GHash append data function, only on ARM & ARM64
+//
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataNeon(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE )     PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                     PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData )
+{
+    // Room for improvement: replace non-crypto NEON code below, based on a bit by bit lookup with
+    // pmull on 8b elements - 8x(8bx8b) -> 8x(16b) pmull is NEON instruction since Armv7
+    //
+    // When properly unrolled:
+    // 1 (64bx64b -> 128b) pmull instruction and 1 eor instruction can be replaced by
+    // 8 (8x(8bx8b) -> 8x(16b)) pmull instructions and 8 eor instructions
+    // so each 128b of data could be processed by less than 64 instructions (using karatsuba)
+    // rather than ~512 instructions (bit by bit)
+    //
+    // Not a priority, expect that AES-GCM performance will be dominated by AES on these platforms
+
+    __n128 R;
+    __n128 cmpValue;
+    __n128 mask;
+    __n128 T;
+
+    PCSYMCRYPT_GF128_ELEMENT   p;
+    PCSYMCRYPT_GF128_ELEMENT   pLimit;
+    UINT32 t;
+    int i;
+
+    cmpValue = vdupq_n_u32(0);             // cmpValue = 0
+
+    while( cbData >= SYMCRYPT_GF128_BLOCK_SIZE )
+    {
+        R = cmpValue;
+
+        //
+        // Do all the pointer arithmetic for the compiler.
+        //
+        p = &expandedKeyTable[0];
+        pLimit = &expandedKeyTable[32];
+
+        for( i=0; i<4; i++ )
+        {
+            //
+            // Set up our XMM register with 4 identical 32-bit integers so that
+            // we can generate the mask from the individual bits of the 32-bit value.
+            // Note the use of tmp; if we assign directly to the fields of T the
+            // compiler no longer caches T in an XMM register, which is bad.
+            //
+            // Note that accessing the state as an array of UINT32s depends on the
+            // endianness of the CPU, but Arm code is always expected to execute in
+            // little endian mode.
+            //
+            t = SYMCRYPT_LOAD_MSBFIRST32( &pbData[4*i] ) ^ pState->ul[3-i];
+            T = vdupq_n_u32( t );
+
+            while( p < pLimit )
+            {
+                //
+                // p and plimit are always at indexes that are multiples of 4 from
+                // the start of the array.
+                // We need to explain to prefast that this means that p <= pLimit - 4
+                //
+                SYMCRYPT_ASSERT( p <= pLimit - 4 );
+
+                mask = vcgtq_s32( cmpValue, T );
+                T = vaddq_u32( T, T );
+                mask = vandq_u32( mask, p[0].n128 );
+                R = veorq_u32( R, mask );
+
+                mask = vcgtq_s32( cmpValue, T );
+                T = vaddq_u32( T, T );
+                mask = vandq_u32( mask, p[1].n128 );
+                R = veorq_u32( R, mask );
+
+                mask = vcgtq_s32( cmpValue, T );
+                T = vaddq_u32( T, T );
+                mask = vandq_u32( mask, p[2].n128 );
+                R = veorq_u32( R, mask );
+
+                mask = vcgtq_s32( cmpValue, T );
+                T = vaddq_u32( T, T );
+                mask = vandq_u32( mask, p[3].n128 );
+                R = veorq_u32( R, mask );
+
+                p += 4;
+            }
+            pLimit += 32;
+        }
+
+        pState->n128 = R;
+        pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+        cbData -= SYMCRYPT_GF128_BLOCK_SIZE;
+    }
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Pclmulqdq implementation
+//
+
+/*
+GHASH GF(2^128) multiplication using PCLMULQDQ
+
+The GF(2^128) field used in GHASH is GF(2)[x]/p(x) where p(x) is the primitive polynomial
+    x^128 + x^7 + x^2 + x + 1
+
+Notation: We use the standard mathematical notation '+' for the addition in the field,
+which corresponds to a xor of the bits.
+
+Multiplication:
+Given two field elements A and B (represented as 128-bit values),
+we first compute the polynomial product
+    (C,D) := A * B
+where C and D are also 128-bit values.
+
+The PCLMULQDQ instruction performs a 64 x 64 -> 128 bit carryless multiplication.
+To multiply 128-bit values we write A = (A1, A0) and B = (B1, B0) in two 64-bit halves.
+
+The schoolbook multiplication is computed by
+    (C, D) = (A1 * B1)x^128 + (A1 * B0 + A0 * B1)x^64 + (A0 * B0)
+This require four PCLMULQDQ instructions. The middle 128-bit result has to be shifted
+left and right, and each half added to the upper and lower 128-bit result to get (C,D).
+
+Alternatively, the middle 128-bit intermediate result be computed using Karatsuba:
+    (A1*B0 + A0*B1) = (A1 + A0) * (B1 + B0) + (A1*B1) + (A0*B0)
+This requires only one PCLMULQDQ instruction to multiply (A1 + A0) by (B1 + B0)
+as the other two products are already computed.
+Whether this is faster depends on the relative speed of shift/xor verses PCLMULQDQ.
+
+Both multiplication algorithms produce three 128-bit intermediate results (R1, Rmid, R0),
+with the full result defined by R1 x^128 + Rmid x^64 + R0.
+If we do Multiply-Accumulate then we can accumulate the three 128-bit intermediate results
+directly. As there are no carries, there is no overflow, and the combining of the three
+intermediate results into a 256-bit result can be shared amongst all multiplications.
+
+
+Modulo reduction:
+We use << and >> to denote shifts on 128-bit values.
+The modulo reduction can now be done as follows:
+given a 256-bit value (C,D) representing C x^128 + D we compute
+    (T1,T0) := C + C*x + C * x^2 + C * x^7
+    R := D + T0 + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
+
+(T1,T0) is just the value C x^128 reduced one step modulo p(x).The value T1 is at most 7 bits,
+so in the next step the reduction, which computes the result R, is easy. The
+expression T1 + (T1 << 1) + (T1 << 2) + (T1 << 7) is just T1 * x^128 reduced modulo p(x).
+
+Let's first get rid of the polynomial arithmetic and write this completely using shifts on
+128-bit values.
+
+T0 := C + (C << 1) + (C << 2) + (C << 7)
+T1 := (C >> 127) + (C >> 126) + (C >> 121)
+R := D + T0 + T1  + (T1 << 1) + (T1 << 2) + (T1 << 7)
+
+We can optimize this by rewriting the equations
+
+T2 := T1 + C
+    = C + (C>>127) + (C>>126) + (C>>121)
+R   = D + T0 + T1  + (T1 << 1) + (T1 << 2) + (T1 << 7)
+    = D + C + (C << 1) + (C << 2) + (C << 7) + T1  + (T1 << 1) + (T1 << 2) + (T1 << 7)
+    = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
+
+Thus
+T2  = C + (C>>127) + (C>>126) + (C>>121)
+R   = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
+
+Gets the right result and uses only 6 shifts.
+
+The SSE instruction set does not implement bit-shifts of 128-bit values. Instead, we will
+use bit-shifts of the 32-bit subvalues, and byte shifts (shifts by a multiple of 8 bits)
+on the full 128-bit values.
+We use the <<<< and >>>> operators to denote shifts on 32-bit subwords.
+
+We can now do the modulo reduction by
+
+t1 := (C >> 127) = (C >>>> 31) >> 96
+t2 := (C >> 126) = (C >>>> 30) >> 96
+t3 := (C >> 121) = (C >>>> 25) >> 96
+T2 = C + t1 + t2 + t3
+
+left-shifts in the computation of R are a bit more involved as we have to move bits from
+one subword to the next
+
+u1 := (T2 << 1) = (T2 <<<< 1) + ((T2 >>>> 31) << 32)
+u2 := (T2 << 2) = (T2 <<<< 2) + ((T2 >>>> 30) << 32)
+u3 := (T2 << 7) = (T2 <<<< 7) + ((T2 >>>> 25) << 32)
+R = D + T2 + u1 + u2 + u3
+
+We can eliminate some common subexpressions. For any k we have
+(T2 >>>> k) = ((C + r) >>>> k)
+where r is a 7-bit value. If k>7 then this is equal to (C >>>> k). This means that
+the value (T2 >>>> 31) is equal to (C >>>> 31) so we don't have to compute it again.
+
+So we can rewrite our formulas as
+t4 := (C >>>> 31)
+t5 := (C >>>> 30)
+t6 := (C >>>> 25)
+ts = t4 + t5 + t6
+T2 = C + (ts >> 96)
+
+Note that ts = (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
+which is equal to (T2 >>>> 31) + (T2 >>>> 30) + (T2 >>>> 25)
+
+R = D + T2 + u1 + u2 + u3
+  = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
+
+All together, we can do the modulo reduction using the following formulas
+
+ts := (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
+T2 := C + (ts >> 96)
+R = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
+
+Using a total of 16 operations. (6 subword shifts, 2 byte shifts, and 8 additions)
+
+Reversed bit order:
+There is one more complication. GHASH uses the bits in the reverse order from normal representation.
+The bits b_0, b_1, ..., b_127 represent the polynomial b_0 + b_1 * x + ... + b_127 * x^127.
+This means that the most significant bit in each byte is actually the least significant bit in the
+polynomial.
+
+SSE CPUs use the LSBFirst convention. This means that the bits b_0, b_1, ..., b_127 of the polynomial
+end up at positions 7, 6, 5, ..., 1, 0, 15, 14, ..., 9, 8, 23, 22, ... of our XMM register.
+This is obviously not a useful representation to do arithmetic in.
+The first step is to BSWAP the value so that the bits appear in pure reverse order.
+That is at least algebraically useful.
+
+To compute the multiplication we use the fact that GF(2)[x] multiplication has no carries and
+thus no preference for bit order. After the BSWAP we don't have the values A and B, but rather
+rev(A) and rev(B) where rev() is a function that reverses the bit order. We can now compute
+
+  rev(A) * rev(B) = rev( A*B ) >> 1
+
+where the shift operator is on the 256-bit product.
+
+The modulo reduction remains the same, except that we change all the shifts to be the other direction.
+
+This gives us finally the outline of our multiplication:
+
+- Apply BSWAP to all values loaded from memory.
+    A := BSWAP( Abytes )
+    B := BSWAP( Bbytes )
+- Compute the 256-bit product, possibly using Karatsuba.
+    (P1, P0) := A * B   // 128x128 carryless multiplication
+- Shift the result left one bit.
+    (Q1, Q0) := (P1, P0) << 1
+    which is computed as
+        Q0 = (P0 <<<< 1) + (P0 >>>> 31) << 32
+        Q1 = (P1 <<<< 1) + (P1 >>>> 31) << 32 + (P0 >>>> 31) >> 96
+- Perform the modulo reduction, with reversed bit order
+    ts := (Q0 <<<< 31) + (Q0 <<<< 30) + (Q0 <<<< 25)
+    T2 := Q0 + (ts << 96)
+    R = Q1 + T2 + (T2 >>>> 1) + (T2 >>>> 2) + (T2 >>>> 7) + (ts >> 32)
+
+Future work:
+It might be possible to construct a faster solution by merging the leftshift of (P1,P0)
+with the modulo reduction.
+
+*/
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("ssse3,pclmul"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("ssse3,pclmul")
+#endif
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyPclmulqdq(
+    _Out_writes_( SYMCRYPT_GF128_FIELD_SIZE )   PSYMCRYPT_GF128_ELEMENT expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )     PCBYTE                  pH )
+{
+    int i;
+    __m128i H, Hx, H2, H2x;
+    __m128i t0, t1, t2, t3, t4, t5;
+    __m128i Hi_even, Hix_even, Hi_odd, Hix_odd;
+    __m128i BYTE_REVERSE_ORDER = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+    __m128i vMultiplicationConstant = _mm_set_epi32( 0, 0, 0xc2000000, 0 );
+
+    //
+    // Our expanded key consists of a list of N=SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS
+    // powers of H. The first entry is H^N, the next H^(N-1), then H^(N-2), ...
+    //
+    // For each power we store two 128-bit values. The first is H^i (Hi) and the second
+    // contains the two halves of H^i xorred with each other in the lower 64 bits (Hix).
+    //
+    // We keep all of the Hi entries together in the first half of the expanded key
+    // table, and all of the Hix entries together in the second half of the table.
+    //
+    // This ordering allow for efficient vectorization with arbitrary vector width, as
+    // many multiplication constants can be loaded into wider vectors with the correct
+    // alignment. Not maintaining different layouts for different vector lengths does
+    // leave a small amount of performance on the table, but experimentally it seems to
+    // <1% difference, and using a single layout reduces complexity significantly.
+    //
+    C_ASSERT( 2*SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS <= SYMCRYPT_GF128_FIELD_SIZE );
+
+    H = _mm_loadu_si128((__m128i *) pH );
+    H = _mm_shuffle_epi8( H, BYTE_REVERSE_ORDER );
+    Hx = _mm_xor_si128( H, _mm_srli_si128( H, 8 ) );
+
+    _mm_store_si128( &GHASH_H_POWER(expandedKey, 1), H );
+    _mm_store_si128( &GHASH_Hx_POWER(expandedKey, 1), Hx );
+
+    CLMUL_X_3( H, Hx, H, Hx, t0, t1, t2 );
+    CLMUL_3_POST( t0, t1, t2 );
+    MODREDUCE( vMultiplicationConstant, t0, t1, t2, H2 );
+    H2x = _mm_xor_si128( H2, _mm_srli_si128( H2, 8 ) );
+    _mm_store_si128( &GHASH_H_POWER(expandedKey, 2), H2 );
+    _mm_store_si128( &GHASH_Hx_POWER(expandedKey, 2), H2x );
+
+    Hi_even = H2;
+    Hix_even = H2x;
+
+    for( i=2; i<SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS; i+=2 )
+    {
+        CLMUL_X_3( H, Hx, Hi_even, Hix_even, t0, t1, t2 );
+        CLMUL_3_POST( t0, t1, t2 );
+        CLMUL_X_3( H2, H2x, Hi_even, Hix_even, t3, t4, t5 );
+        CLMUL_3_POST( t3, t4, t5 );
+        MODREDUCE( vMultiplicationConstant, t0, t1, t2, Hi_odd );
+        MODREDUCE( vMultiplicationConstant, t3, t4, t5, Hi_even );
+        Hix_odd  = _mm_xor_si128( Hi_odd, _mm_srli_si128( Hi_odd, 8 ) );
+        Hix_even = _mm_xor_si128( Hi_even, _mm_srli_si128( Hi_even, 8 ) );
+
+        _mm_store_si128( &GHASH_H_POWER(expandedKey, i + 1), Hi_odd );
+        _mm_store_si128( &GHASH_H_POWER(expandedKey, i + 2), Hi_even );
+        _mm_store_si128( &GHASH_Hx_POWER(expandedKey, i + 1), Hix_odd );
+        _mm_store_si128( &GHASH_Hx_POWER(expandedKey, i + 2), Hix_even );
+    }
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataPclmulqdq(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbData,
+                                            SIZE_T                      cbData )
+{
+    __m128i state;
+    __m128i data;
+    __m128i a0, a1, a2;
+    __m128i Hi, Hix;
+    SIZE_T i;
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+
+    //
+    // To do a BSWAP we need an __m128i value with the bytes
+    //
+
+    __m128i BYTE_REVERSE_ORDER = _mm_set_epi8(
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+    __m128i vMultiplicationConstant = _mm_set_epi32( 0, 0, 0xc2000000, 0 );
+
+    state = _mm_loadu_si128( (__m128i *) pState );
+
+    while( nBlocks > 0 )
+    {
+        //
+        // We process the data in blocks of up to SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS blocks
+        //
+        todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS );
+
+        //
+        // The first block is xorred with the state before multiplying it with a power of H
+        //
+        data = _mm_loadu_si128( (__m128i *) pbData );
+        data = _mm_shuffle_epi8( data, BYTE_REVERSE_ORDER );
+        pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+
+        state = _mm_xor_si128( state, data );
+        CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+
+        //
+        // Then we just do an improduct
+        //
+        for( i=1; i<todo; i++ )
+        {
+            data = _mm_loadu_si128( (__m128i *) pbData );
+            data = _mm_shuffle_epi8( data, BYTE_REVERSE_ORDER );
+            pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+
+            Hi  = _mm_load_si128( &GHASH_H_POWER(expandedKeyTable, todo - i) );
+            Hix = _mm_load_si128( &GHASH_Hx_POWER(expandedKeyTable, todo - i) );
+            CLMUL_ACC_3( data, Hi, Hix, a0, a1, a2 );
+        }
+
+        CLMUL_3_POST( a0, a1, a2 );
+        MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+        nBlocks -= todo;
+    }
+
+    _mm_storeu_si128((__m128i *)pState, state );
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif  // CPU_X86 || CPU_AMD64
+
+#if SYMCRYPT_CPU_ARM64
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("aes"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("aes")
+#endif
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyPmull(
+    _Out_writes_( SYMCRYPT_GF128_FIELD_SIZE )   PSYMCRYPT_GF128_ELEMENT expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )    PCBYTE                  pH )
+{
+    int i;
+    __n128 H, Hx, H2, H2x;
+    __n128 t0, t1, t2, t3, t4, t5;
+    __n128 Hi_even, Hix_even, Hi_odd, Hix_odd;
+    const __n64 vMultiplicationConstant = SYMCRYPT_SET_N64_U64(0xc200000000000000);
+    //
+    // Our expanded key consists of a list of N=SYMCRYPT_GHASH_PMULL_HPOWERS
+    // powers of H. The first entry is H^N, the next H^(N-1), then H^(N-2), ...
+    //
+    // For each power we store two 128-bit values. The first is H^i (Hi) and the second
+    // contains the two halves of H^i xorred with each other in the lower 64 bits (Hix).
+    //
+    // We keep all of the Hi entries together in the first half of the expanded key
+    // table, and all of the Hix entries together in the second half of the table.
+    //
+    // This ordering allow for efficient vectorization with arbitrary vector width, as
+    // many multiplication constants can be loaded into wider vectors with the correct
+    // alignment. Not maintaining different layouts for different vector lengths does
+    // leave a small amount of performance on the table, but experimentally it seems to
+    // <1% difference, and using a single layout reduces complexity significantly.
+    //
+    C_ASSERT( 2*SYMCRYPT_GHASH_PMULL_HPOWERS <= SYMCRYPT_GF128_FIELD_SIZE );
+
+    H = *(__n128 *) pH;
+    Hx = vrev64q_u8( H );
+    H = vextq_u8( Hx, Hx, 8 );
+    Hx = veorq_u8( H, Hx );
+
+    GHASH_H_POWER(expandedKey, 1) = H;
+    GHASH_Hx_POWER(expandedKey, 1) = Hx;
+
+    CLMUL_X_3( H, Hx, H, Hx, t0, t1, t2 );
+    CLMUL_3_POST( t0, t1, t2 );
+    MODREDUCE( vMultiplicationConstant, t0, t1, t2, H2 );
+    H2x = veorq_u8( H2, vextq_u8( H2, H2, 8 ) );
+    GHASH_H_POWER(expandedKey, 2) = H2;
+    GHASH_Hx_POWER(expandedKey, 2) = H2x;
+
+    Hi_even = H2;
+    Hix_even = H2x;
+
+    for( i=2; i<SYMCRYPT_GHASH_PMULL_HPOWERS; i+=2 )
+    {
+        CLMUL_X_3( H, Hx, Hi_even, Hix_even, t0, t1, t2 );
+        CLMUL_3_POST( t0, t1, t2 );
+        CLMUL_X_3( H2, H2x, Hi_even, Hix_even, t3, t4, t5 );
+        CLMUL_3_POST( t3, t4, t5 );
+        MODREDUCE( vMultiplicationConstant, t0, t1, t2, Hi_odd );
+        MODREDUCE( vMultiplicationConstant, t3, t4, t5, Hi_even );
+        Hix_odd = veorq_u8( Hi_odd, vextq_u8( Hi_odd, Hi_odd, 8 ) );
+        Hix_even = veorq_u8( Hi_even, vextq_u8( Hi_even, Hi_even, 8 ) );
+
+        GHASH_H_POWER(expandedKey, i + 1) = Hi_odd;
+        GHASH_H_POWER(expandedKey, i + 2) = Hi_even;
+        GHASH_Hx_POWER(expandedKey, i + 1) = Hix_odd;
+        GHASH_Hx_POWER(expandedKey, i + 2) = Hix_even;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataPmull(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbData,
+                                            SIZE_T                      cbData )
+{
+    __n128 state;
+    __n128 data, datax;
+    __n128 a0, a1, a2;
+    __n128 Hi, Hix;
+    const __n64 vMultiplicationConstant = SYMCRYPT_SET_N64_U64(0xc200000000000000);
+    SIZE_T i;
+    SIZE_T nBlocks = cbData / SYMCRYPT_GF128_BLOCK_SIZE;
+    SIZE_T todo;
+
+    state = *(__n128 *) pState;
+
+    while( nBlocks > 0 )
+    {
+        //
+        // We process the data in blocks of up to SYMCRYPT_GHASH_PMULL_HPOWERS blocks
+        //
+        todo = SYMCRYPT_MIN( nBlocks, SYMCRYPT_GHASH_PMULL_HPOWERS );
+
+        //
+        // The first block is xorred with the state before multiplying it with a power of H
+        //
+        data = *(__n128 *)pbData;
+        REVERSE_BYTES( data, data );
+        pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+
+        state = veorq_u8( state, data );
+        CLMUL_3( state, GHASH_H_POWER(expandedKeyTable, todo), GHASH_Hx_POWER(expandedKeyTable, todo), a0, a1, a2 );
+
+        //
+        // Then we just do an improduct
+        //
+        for( i=1; i<todo; i++ )
+        {
+            // we can avoid an EXT here by precomputing datax for CLMUL_ACCX_3
+            datax = vrev64q_u8( *(__n128 *)pbData );
+            data = vextq_u8( datax, datax, 8 );
+            datax = veorq_u8( data, datax );
+            pbData += SYMCRYPT_GF128_BLOCK_SIZE;
+
+            Hi  = GHASH_H_POWER(expandedKeyTable, todo - i);
+            Hix = GHASH_Hx_POWER(expandedKeyTable, todo - i);
+            CLMUL_ACCX_3( data, datax, Hi, Hix, a0, a1, a2 );
+        }
+
+        CLMUL_3_POST( a0, a1, a2 );
+        MODREDUCE( vMultiplicationConstant, a0, a1, a2, state );
+        nBlocks -= todo;
+    }
+
+    *(__n128 *) pState = state;
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif  // CPU_ARM64
+
+
+
+//////////////////////////////////////////////////////////////
+// Stuff around the core algorithm implementation functions
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKey(
+    _Out_                                       PSYMCRYPT_GHASH_EXPANDED_KEY    expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )     PCBYTE                          pH )
+{
+#if  SYMCRYPT_CPU_X86
+    PSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    //
+    // Initialize offset into table space for 16-alignment.
+    //
+    expandedKey->tableOffset = (0 -((UINT_PTR) &expandedKey->tableSpace[0])) % sizeof(SYMCRYPT_GF128_ELEMENT);
+
+    pExpandedKeyTable = (PSYMCRYPT_GF128_ELEMENT)&expandedKey->tableSpace[expandedKey->tableOffset];
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_PCLMULQDQ_CODE ) )
+    {
+        //
+        // We can only use the PCLMULQDQ data representation if the SaveXmm never fails.
+        // This is one of the CPU features required.
+        // We check anyway...
+        //
+        if( SymCryptSaveXmm( &SaveData ) != SYMCRYPT_NO_ERROR )
+        {
+            SymCryptFatal( 'pclm' );
+        }
+        SymCryptGHashExpandKeyPclmulqdq( pExpandedKeyTable, pH );
+        SymCryptRestoreXmm( &SaveData );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) && SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptGHashExpandKeyXmm( pExpandedKeyTable, pH );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptGHashExpandKeyC( pExpandedKeyTable, pH );
+    }
+
+#elif SYMCRYPT_CPU_AMD64
+    PSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+    pExpandedKeyTable = &expandedKey->table[0];
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_PCLMULQDQ_CODE ) )
+    {
+        SymCryptGHashExpandKeyPclmulqdq( pExpandedKeyTable, pH );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) )
+    {
+        SymCryptGHashExpandKeyXmm( pExpandedKeyTable, pH );
+    } else {
+        SymCryptGHashExpandKeyC( pExpandedKeyTable, pH );
+    }
+
+#elif SYMCRYPT_CPU_ARM64
+    PSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+    pExpandedKeyTable = &expandedKey->table[0];
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_PMULL ) )
+    {
+        SymCryptGHashExpandKeyPmull( pExpandedKeyTable, pH );
+    } else {
+        SymCryptGHashExpandKeyC( pExpandedKeyTable, pH );
+    }
+
+#else
+    SymCryptGHashExpandKeyC( &expandedKey->table[0], pH );      // Default expansion (does not need alignment)
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendData(
+    _In_                              PCSYMCRYPT_GHASH_EXPANDED_KEY   expandedKey,
+    _Inout_                           PSYMCRYPT_GF128_ELEMENT         pState,
+    _In_reads_( cbData )              PCBYTE                          pbData,
+                                      SIZE_T                          cbData )
+{
+#if SYMCRYPT_CPU_X86
+    PCSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    pExpandedKeyTable = (PSYMCRYPT_GF128_ELEMENT)&expandedKey->tableSpace[expandedKey->tableOffset];
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_PCLMULQDQ_CODE ) )
+    {
+        if( SymCryptSaveXmm( &SaveData ) != SYMCRYPT_NO_ERROR )
+        {
+            SymCryptFatal( 'pclm' );
+        }
+        SymCryptGHashAppendDataPclmulqdq( pExpandedKeyTable, pState, pbData, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) && SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptGHashAppendDataXmm( pExpandedKeyTable, pState, pbData, cbData );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptGHashAppendDataC( pExpandedKeyTable, pState, pbData, cbData );
+    }
+
+#elif SYMCRYPT_CPU_AMD64
+    PCSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+
+    pExpandedKeyTable = &expandedKey->table[0];
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_PCLMULQDQ_CODE ) )
+    {
+        SymCryptGHashAppendDataPclmulqdq( pExpandedKeyTable, pState, pbData, cbData );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) )
+    {
+        SymCryptGHashAppendDataXmm( pExpandedKeyTable, pState, pbData, cbData );
+    } else {
+        SymCryptGHashAppendDataC( pExpandedKeyTable, pState, pbData, cbData );
+    }
+#elif SYMCRYPT_CPU_ARM
+    PCSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+
+    pExpandedKeyTable = &expandedKey->table[0];
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON ) )
+    {
+        SymCryptGHashAppendDataNeon( pExpandedKeyTable, pState, pbData, cbData );
+    } else {
+        SymCryptGHashAppendDataC( pExpandedKeyTable, pState, pbData, cbData );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    PCSYMCRYPT_GF128_ELEMENT pExpandedKeyTable;
+
+    pExpandedKeyTable = &expandedKey->table[0];
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_PMULL ) )
+    {
+        SymCryptGHashAppendDataPmull( pExpandedKeyTable, pState, pbData, cbData );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON ) )
+    {
+        SymCryptGHashAppendDataNeon( pExpandedKeyTable, pState, pbData, cbData );
+    } else {
+        SymCryptGHashAppendDataC( pExpandedKeyTable, pState, pbData, cbData );
+    }
+#else
+    SymCryptGHashAppendDataC( &expandedKey->table[0], pState, pbData, cbData );
+#endif
+}
diff --git a/libs/symcrypt/lib/ghash_definitions.h b/libs/symcrypt/lib/ghash_definitions.h
new file mode 100644
index 000000000000..351ad82f9d25
--- /dev/null
+++ b/libs/symcrypt/lib/ghash_definitions.h
@@ -0,0 +1,472 @@
+//
+// ghash_definitions.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Constants & globals
+//
+
+#define GF128_FIELD_R_BYTE   (0xe1)
+#define UINT64_NEG(x) ((UINT64)-(INT64)(x))
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Pclmulqdq implementation
+//
+
+/*
+GHASH GF(2^128) multiplication using PCLMULQDQ
+
+The GF(2^128) field used in GHASH is GF(2)[x]/p(x) where p(x) is the primitive polynomial
+    x^128 + x^7 + x^2 + x + 1
+
+Notation: We use the standard mathematical notation '+' for the addition in the field,
+which corresponds to a xor of the bits.
+
+Multiplication:
+Given two field elements A and B (represented as 128-bit values),
+we first compute the polynomial product
+    (C,D) := A * B
+where C and D are also 128-bit values.
+
+The PCLMULQDQ instruction performs a 64 x 64 -> 128 bit carryless multiplication.
+To multiply 128-bit values we write A = (A1, A0) and B = (B1, B0) in two 64-bit halves.
+
+The schoolbook multiplication is computed by
+    (C, D) = (A1 * B1)x^128 + (A1 * B0 + A0 * B1)x^64 + (A0 * B0)
+This require four PCLMULQDQ instructions. The middle 128-bit result has to be shifted
+left and right, and each half added to the upper and lower 128-bit result to get (C,D).
+
+Alternatively, the middle 128-bit intermediate result be computed using Karatsuba:
+    (A1*B0 + A0*B1) = (A1 + A0) * (B1 + B0) + (A1*B1) + (A0*B0)
+This requires only one PCLMULQDQ instruction to multiply (A1 + A0) by (B1 + B0)
+as the other two products are already computed.
+Whether this is faster depends on the relative speed of shift/xor verses PCLMULQDQ.
+
+Both multiplication algorithms produce three 128-bit intermediate results (R1, Rmid, R0),
+with the full result defined by R1 x^128 + Rmid x^64 + R0.
+If we do Multiply-Accumulate then we can accumulate the three 128-bit intermediate results
+directly. As there are no carries, there is no overflow, and the combining of the three
+intermediate results into a 256-bit result can be shared amongst all multiplications.
+
+
+Modulo reduction:
+We use << and >> to denote shifts on 128-bit values.
+The modulo reduction can now be done as follows:
+given a 256-bit value (C,D) representing C x^128 + D we compute
+    (T1,T0) := C + C*x + C * x^2 + C * x^7
+    R := D + T0 + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
+
+(T1,T0) is just the value C x^128 reduced one step modulo p(x).The value T1 is at most 7 bits,
+so in the next step the reduction, which computes the result R, is easy. The
+expression T1 + (T1 << 1) + (T1 << 2) + (T1 << 7) is just T1 * x^128 reduced modulo p(x).
+
+Let's first get rid of the polynomial arithmetic and write this completely using shifts on
+128-bit values.
+
+T0 := C + (C << 1) + (C << 2) + (C << 7)
+T1 := (C >> 127) + (C >> 126) + (C >> 121)
+R := D + T0 + T1  + (T1 << 1) + (T1 << 2) + (T1 << 7)
+
+We can optimize this by rewriting the equations
+
+T2 := T1 + C
+    = C + (C>>127) + (C>>126) + (C>>121)
+R   = D + T0 + T1  + (T1 << 1) + (T1 << 2) + (T1 << 7)
+    = D + C + (C << 1) + (C << 2) + (C << 7) + T1  + (T1 << 1) + (T1 << 2) + (T1 << 7)
+    = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
+
+Thus
+T2  = C + (C>>127) + (C>>126) + (C>>121)
+R   = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
+
+Gets the right result and uses only 6 shifts.
+
+The SSE instruction set does not implement bit-shifts of 128-bit values. Instead, we will
+use bit-shifts of the 32-bit subvalues, and byte shifts (shifts by a multiple of 8 bits)
+on the full 128-bit values.
+We use the <<<< and >>>> operators to denote shifts on 32-bit subwords.
+
+We can now do the modulo reduction by
+
+t1 := (C >> 127) = (C >>>> 31) >> 96
+t2 := (C >> 126) = (C >>>> 30) >> 96
+t3 := (C >> 121) = (C >>>> 25) >> 96
+T2 = C + t1 + t2 + t3
+
+left-shifts in the computation of R are a bit more involved as we have to move bits from
+one subword to the next
+
+u1 := (T2 << 1) = (T2 <<<< 1) + ((T2 >>>> 31) << 32)
+u2 := (T2 << 2) = (T2 <<<< 2) + ((T2 >>>> 30) << 32)
+u3 := (T2 << 7) = (T2 <<<< 7) + ((T2 >>>> 25) << 32)
+R = D + T2 + u1 + u2 + u3
+
+We can eliminate some common subexpressions. For any k we have
+(T2 >>>> k) = ((C + r) >>>> k)
+where r is a 7-bit value. If k>7 then this is equal to (C >>>> k). This means that
+the value (T2 >>>> 31) is equal to (C >>>> 31) so we don't have to compute it again.
+
+So we can rewrite our formulas as
+t4 := (C >>>> 31)
+t5 := (C >>>> 30)
+t6 := (C >>>> 25)
+ts = t4 + t5 + t6
+T2 = C + (ts >> 96)
+
+Note that ts = (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
+which is equal to (T2 >>>> 31) + (T2 >>>> 30) + (T2 >>>> 25)
+
+R = D + T2 + u1 + u2 + u3
+  = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
+
+All together, we can do the modulo reduction using the following formulas
+
+ts := (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
+T2 := C + (ts >> 96)
+R = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
+
+Using a total of 16 operations. (6 subword shifts, 2 byte shifts, and 8 additions)
+
+Reversed bit order:
+There is one more complication. GHASH uses the bits in the reverse order from normal representation.
+The bits b_0, b_1, ..., b_127 represent the polynomial b_0 + b_1 * x + ... + b_127 * x^127.
+This means that the most significant bit in each byte is actually the least significant bit in the
+polynomial.
+
+SSE CPUs use the LSBFirst convention. This means that the bits b_0, b_1, ..., b_127 of the polynomial
+end up at positions 7, 6, 5, ..., 1, 0, 15, 14, ..., 9, 8, 23, 22, ... of our XMM register.
+This is obviously not a useful representation to do arithmetic in.
+The first step is to BSWAP the value so that the bits appear in pure reverse order.
+That is at least algebraically useful.
+
+To compute the multiplication we use the fact that GF(2)[x] multiplication has no carries and
+thus no preference for bit order. After the BSWAP we don't have the values A and B, but rather
+rev(A) and rev(B) where rev() is a function that reverses the bit order. We can now compute
+
+  rev(A) * rev(B) = rev( A*B ) >> 1
+
+where the shift operator is on the 256-bit product.
+
+The modulo reduction remains the same, except that we change all the shifts to be the other direction.
+
+This gives us finally the outline of our multiplication:
+
+- Apply BSWAP to all values loaded from memory.
+    A := BSWAP( Abytes )
+    B := BSWAP( Bbytes )
+- Compute the 256-bit product, possibly using Karatsuba.
+    (P1, P0) := A * B   // 128x128 carryless multiplication
+- Shift the result left one bit.
+    (Q1, Q0) := (P1, P0) << 1
+    which is computed as
+        Q0 = (P0 <<<< 1) + (P0 >>>> 31) << 32
+        Q1 = (P1 <<<< 1) + (P1 >>>> 31) << 32 + (P0 >>>> 31) >> 96
+- Perform the modulo reduction, with reversed bit order
+    ts := (Q0 <<<< 31) + (Q0 <<<< 30) + (Q0 <<<< 25)
+    T2 := Q0 + (ts << 96)
+    R = Q1 + T2 + (T2 >>>> 1) + (T2 >>>> 2) + (T2 >>>> 7) + (ts >> 32)
+
+Future work:
+It might be possible to construct a faster solution by merging the leftshift of (P1,P0)
+with the modulo reduction.
+
+*/
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#define SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS    32
+
+#define GHASH_H_POWER( ghashTable, ind )  ( (ghashTable)[  SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS - (ind)].m128i )
+#define GHASH_Hx_POWER( ghashTable, ind ) ( (ghashTable)[2*SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS - (ind)].m128i )
+
+//
+// We define a few macros
+//
+
+//
+// CLMUL_4 multiplies two operands into three intermediate results using 4 pclmulqdq instructions
+//
+#define CLMUL_4( opA, opB, resl, resm, resh ) \
+{ \
+    resl = _mm_clmulepi64_si128( opA, opB, 0x00 ); \
+    resm = _mm_xor_si128( _mm_clmulepi64_si128( opA, opB, 0x01 ), _mm_clmulepi64_si128( opA, opB, 0x10 ) ); \
+    resh = _mm_clmulepi64_si128( opA, opB, 0x11 ); \
+};
+
+//
+// CLMUL_3 multiplies two operands into three intermediate results using 3 pclmulqdq instructions.
+// The second operand has a pre-computed difference of the two halves.
+// This uses Karatsuba, but we delay xorring the high and low piece into the middle piece.
+//
+#define CLMUL_3( opA, opB, opBx, resl, resm, resh ) \
+{ \
+    __m128i _tmpA; \
+    resl = _mm_clmulepi64_si128( opA, opB, 0x00 ); \
+    resh = _mm_clmulepi64_si128( opA, opB, 0x11 ); \
+    _tmpA = _mm_xor_si128( opA, _mm_srli_si128( opA, 8 ) ); \
+    resm = _mm_clmulepi64_si128( _tmpA, opBx, 0x00 ); \
+};
+//
+// CLMUL_X_3 is as CLMUL_3 only it takes precomputed differences of both multiplicands.
+//
+#define CLMUL_X_3( opA, opAx, opB, opBx, resl, resm, resh ) \
+{ \
+    resl = _mm_clmulepi64_si128( opA, opB, 0x00 ); \
+    resh = _mm_clmulepi64_si128( opA, opB, 0x11 ); \
+    resm = _mm_clmulepi64_si128( opAx, opBx, 0x00 ); \
+};
+
+//
+// Post-process the CLMUL_3 result to be compatible with the CLMUL_4
+//
+#define CLMUL_3_POST( resl, resm, resh ) \
+    resm = _mm_xor_si128( resm, _mm_xor_si128( resl, resh ) );
+
+//
+// Multiply-accumulate using CLMUL_4
+//
+#define CLMUL_ACC_4( opA, opB, resl, resm, resh ) \
+{\
+    __m128i _tmpl, _tmpm, _tmph;\
+    CLMUL_4( opA, opB, _tmpl, _tmpm, _tmph );\
+    resl = _mm_xor_si128( resl, _tmpl ); \
+    resm = _mm_xor_si128( resm, _tmpm ); \
+    resh = _mm_xor_si128( resh, _tmph ); \
+};
+
+//
+// Multiply-accumulate using CLMUL_3
+//
+#define CLMUL_ACC_3( opA, opB, opBx, resl, resm, resh ) \
+{\
+    __m128i _tmpl, _tmpm, _tmph;\
+    CLMUL_3( opA, opB, opBx, _tmpl, _tmpm, _tmph );\
+    resl = _mm_xor_si128( resl, _tmpl ); \
+    resm = _mm_xor_si128( resm, _tmpm ); \
+    resh = _mm_xor_si128( resh, _tmph ); \
+};
+#define CLMUL_ACC_3_Ymm( opA, opB, opBx, resl, resm, resh ) \
+{\
+    __m256i _tmpl, _tmpm, _tmph;\
+    __m256i _tmpA; \
+    _tmpl = _mm256_clmulepi64_epi128( opA, opB, 0x00 ); \
+    _tmph = _mm256_clmulepi64_epi128( opA, opB, 0x11 ); \
+    _tmpA = _mm256_xor_si256( opA, _mm256_srli_si256( opA, 8 ) ); \
+    _tmpm = _mm256_clmulepi64_epi128( _tmpA, opBx, 0x00 ); \
+    resl = _mm256_xor_si256( resl, _tmpl ); \
+    resm = _mm256_xor_si256( resm, _tmpm ); \
+    resh = _mm256_xor_si256( resh, _tmph ); \
+};
+
+
+//
+// Convert the 3 intermediate results to a 256-bit result,
+// and do the modulo reduction.
+#define MODREDUCE( vMultiplicationConstant, rl, rm, rh, res ) \
+{\
+    __m128i _T0, _T1; \
+\
+    /* multiply rl by constant which is (rev(0x87) << 1) - we'll eor the lost high bit in manually */ \
+    _T0 = _mm_clmulepi64_si128( rl, vMultiplicationConstant, 0x00 ); \
+\
+    /* we want the high 64b of rl to align with the low 64b of rm, because we haven't merged rm into rl and rh */ \
+    /* we want the low 64b of rl to align with the high 64b of rm, because we lost the high bit in the previous pmull */ \
+    rl = _mm_shuffle_epi32( rl, _MM_SHUFFLE( 1, 0, 3, 2 ) ); \
+\
+    rm = _mm_xor_si128( rm, _T0 ); \
+    rm = _mm_xor_si128( rm, rl ); \
+\
+    /* almost same again to fold rm into rh, but bit 63 needs no more multiplication and the result ultimately needs shifting left by 1 */ \
+    /* pre-shift bottom of rm left by 1 and accumulate the result when the other parts are aligned */ \
+    _T0 = _mm_clmulepi64_si128( _mm_slli_epi64( rm, 1 ), vMultiplicationConstant, 0x00 ); \
+\
+    rm = _mm_shuffle_epi32( rm, _MM_SHUFFLE( 1, 0, 3, 2 ) ); \
+    res = _mm_xor_si128( rh, rm ); \
+\
+    /* rotate res left by 1 and accumulate the aligned parts */ \
+    _T1 = _mm_slli_epi32( res, 1 ); \
+    res = _mm_srli_epi32( res, 31 ); \
+\
+    _T0 = _mm_xor_si128( _T0, _T1 ); \
+    res = _mm_shuffle_epi32( res, _MM_SHUFFLE( 2, 1, 0, 3 ) ); \
+\
+    res = _mm_xor_si128( res, _T0 ); \
+};
+
+//
+// See the large comment above on how this is done.
+// When we want to do MODREDUCE in parallel with other work, making use of pclmuldq to reduce
+// total instruction count (and register pressure) is beneficial. When testing on Haswell,
+// using the newer approach is beneficial. Keeping the old approach around in case we have significant
+// regression on older platforms.
+//
+#define MODREDUCE_OLD( rl, rm, rh, res ) \
+{\
+    __m128i _T0, _T1, _T2, _Q0, _Q1; \
+    rl = _mm_xor_si128( rl, _mm_slli_si128( rm, 8 ) ); \
+    rh = _mm_xor_si128( rh, _mm_srli_si128( rm, 8 ) ); \
+\
+    _Q0 = _mm_slli_epi32( rl, 1 ); \
+    _Q1 = _mm_slli_epi32( rh, 1 ); \
+\
+    _T0 = _mm_srli_epi32( rl, 31 ); \
+    _T1 = _mm_srli_epi32( rh, 31 ); \
+\
+    _T1 = _mm_alignr_epi8( _T1, _T0, 12 ); \
+    _T0 = _mm_slli_si128( _T0, 4 ); \
+\
+    _Q0 = _mm_xor_si128( _Q0, _T0 ); \
+    _Q1 = _mm_xor_si128( _Q1, _T1 ); \
+\
+    _T0 = _mm_slli_epi32( _Q0, 31 ); \
+    _T1 = _mm_slli_epi32( _Q0, 30 ); \
+    _T2 = _mm_slli_epi32( _Q0, 25 ); \
+    _T0 = _mm_xor_si128( _T0, _T1 ); \
+    _T0 = _mm_xor_si128( _T0, _T2 ); \
+\
+    _T1 = _mm_slli_si128( _T0, 12 ); \
+\
+    _T2 = _mm_xor_si128( _Q0, _T1 ); \
+\
+    res = _mm_xor_si128( _Q1, _T2 ); \
+    _T1 = _mm_srli_si128( _T0, 4 ); \
+    res = _mm_xor_si128(  res, _T1 ); \
+\
+    _T0 = _mm_srli_epi32( _T2, 1 ); \
+    _T1 = _mm_srli_epi32( _T2, 2 ); \
+    _T2 = _mm_srli_epi32( _T2, 7 ); \
+\
+    _T1 = _mm_xor_si128( _T0, _T1 ); \
+    res = _mm_xor_si128( res, _T2 ); \
+    res = _mm_xor_si128( res, _T1 ); \
+};
+
+#endif  // CPU_X86 || CPU_AMD64
+
+#if SYMCRYPT_CPU_ARM64
+
+#define SYMCRYPT_GHASH_PMULL_HPOWERS    32
+
+#define GHASH_H_POWER( ghashTable, ind )  ( (ghashTable)[  SYMCRYPT_GHASH_PMULL_HPOWERS - (ind)].n128 )
+#define GHASH_Hx_POWER( ghashTable, ind ) ( (ghashTable)[2*SYMCRYPT_GHASH_PMULL_HPOWERS - (ind)].n128 )
+
+#if SYMCRYPT_MS_VC
+#ifndef vshl_n_u64
+#define vshl_n_u64(src1, src2) neon_shlis64(src1, src2)
+#endif
+#endif
+//
+// CLMUL_4 multiplies two operands into three intermediate results using 4 pmull instructions
+//
+#define CLMUL_4( opA, opB, resl, resm, resh ) \
+{ \
+    __n128 _tmp; \
+    resl = vmullq_p64( opA, opB ); \
+    _tmp = vextq_u8( opA, opA, 8 ); \
+    resm = veorq_u8( vmullq_p64( opB, _tmp ), vmull_high_p64( opB, _tmp ) );\
+    resh = vmull_high_p64( opA, opB ); \
+};
+
+//
+// CLMUL_3 multiplies two operands into three intermediate results using 3 pmull instructions.
+// The second operand has a pre-computed difference of the two halves.
+// This uses Karatsuba, but we delay xorring the high and low piece into the middle piece.
+//
+#define CLMUL_3( opA, opB, opBx, resl, resm, resh ) \
+{ \
+    __n128 _tmpA; \
+    resl = vmullq_p64( opA, opB ); \
+    resh = vmull_high_p64( opA, opB ); \
+    _tmpA = veorq_u8( opA, vextq_u8( opA, opA, 8 ) ); \
+    resm = vmullq_p64( _tmpA, opBx ); \
+};
+//
+// CLMUL_X_3 is as CLMUL_3 only it takes precomputed differences of both multiplicands
+//
+#define CLMUL_X_3( opA, opAx, opB, opBx, resl, resm, resh ) \
+{ \
+    resl = vmullq_p64( opA, opB ); \
+    resh = vmull_high_p64( opA, opB ); \
+    resm = vmullq_p64( opAx, opBx ); \
+};
+
+//
+// Post-process the CLMUL_3 result to be compatible with the CLMUL_4
+//
+#define CLMUL_3_POST( resl, resm, resh ) \
+    resm = veorq_u8( resm, veorq_u8( resl, resh ) );
+
+//
+// Multiply-accumulate using CLMUL_4
+//
+#define CLMUL_ACC_4( opA, opB, resl, resm, resh ) \
+{\
+    __n128 _tmpl, _tmpm, _tmph;\
+    CLMUL_4( opA, opB, _tmpl, _tmpm, _tmph );\
+    resl = veorq_u8( resl, _tmpl ); \
+    resm = veorq_u8( resm, _tmpm ); \
+    resh = veorq_u8( resh, _tmph ); \
+};
+
+//
+// Multiply-accumulate two operands into 3 accumulators.
+// Takes the multiplicands and the pre-computed differences of the two halves of both multiplicands.
+//
+#define CLMUL_ACCX_3( opA, opAx, opB, opBx, resl, resm, resh ) \
+{\
+    __n128 _tmpl, _tmpm, _tmph;\
+    CLMUL_X_3( opA, opAx, opB, opBx, _tmpl, _tmpm, _tmph ); \
+    resl = veorq_u8( resl, _tmpl ); \
+    resm = veorq_u8( resm, _tmpm ); \
+    resh = veorq_u8( resh, _tmph ); \
+};
+
+
+//
+// Convert the 3 intermediate results to a 256-bit result,
+// and do the modulo reduction.
+// See the large comment above on how this is done.
+//
+#define MODREDUCE( vMultiplicationConstant, rl, rm, rh, res ) \
+{\
+    __n128 _T0, _T1; \
+\
+    /* multiply rl by constant which is (rev(0x87) << 1) - we'll eor the lost high bit in manually */ \
+    _T0 = vmull_p64( vget_low_p64(rl), vMultiplicationConstant ); \
+\
+    /* we want the high 64b of rl to align with the low 64b of rm, because we haven't merged rm into rl and rh */ \
+    /* we want the low 64b of rl to align with the high 64b of rm, because we lost the high bit in the previous pmull */ \
+    rl = vextq_u8( rl, rl, 8 ); \
+\
+    rm = veorq_u8( rm, _T0 ); \
+    rm = veorq_u8( rm, rl ); \
+\
+    /* almost same again to fold rm into rh, but bit 63 needs no more multiplication and the result ultimately needs shifting left by 1 */ \
+    /* pre-shift bottom of rm left by 1 and accumulate the result when the other parts are aligned */ \
+    _T0 = vmull_p64( vshl_n_u64(vget_low_p64(rm), 1), vMultiplicationConstant ); \
+\
+    rm = vextq_u8( rm, rm, 8 ); \
+    res = veorq_u8( rh, rm ); \
+\
+    /* rotate res left by 1 and accumulate the aligned parts */ \
+    _T1 = vshlq_n_u32( res, 1 ); \
+    res = vshrq_n_u32( res, 31 ); \
+\
+    _T0 = veorq_u8( _T0, _T1 ); \
+    res = vextq_u8( res, res, 12 ); \
+\
+    res = veorq_u8( res, _T0 ); \
+};
+
+#define REVERSE_BYTES( _in, _out )\
+{\
+    __n128 _t;\
+    _t = vrev64q_u8( _in ); \
+    _out = vextq_u8( _t, _t, 8 ); \
+}
+
+#endif  // CPU_ARM64
diff --git a/libs/symcrypt/lib/hash.c b/libs/symcrypt/lib/hash.c
new file mode 100644
index 000000000000..a0420c35964d
--- /dev/null
+++ b/libs/symcrypt/lib/hash.c
@@ -0,0 +1,216 @@
+//
+// hash.c generic code used in many hash implementations.
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashAppendInternal(
+    _In_                        PCSYMCRYPT_HASH             pHash,
+    _Inout_                     PSYMCRYPT_COMMON_HASH_STATE pState,
+    _In_reads_bytes_( cbData )  PCBYTE                      pbData,
+                                SIZE_T                      cbData )
+{
+    UINT32  bytesInBuffer;
+    UINT32  freeInBuffer;
+    SIZE_T  tmp;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    pState->dataLengthL += cbData;
+    if( pState->dataLengthL < cbData ) {
+        pState->dataLengthH ++;                         // This is almost-unreachable code as it requires 2^64 bytes to be hashed.
+    }
+
+    bytesInBuffer = pState->bytesInBuffer;
+
+    //
+    // If previous data in buffer, buffer new input and transform if possible.
+    //
+    if( bytesInBuffer > 0 )
+    {
+        SYMCRYPT_ASSERT( pHash->inputBlockSize > bytesInBuffer );
+
+        freeInBuffer = pHash->inputBlockSize - bytesInBuffer;
+        if( cbData < freeInBuffer )
+        {
+            //
+            // All the data will fit in the buffer.
+            // We don't do anything here.
+            // As cbData < inputBlockSize the bulk data processing is skipped,
+            // and the data will be copied to the buffer at the end
+            // of this code.
+        } else {
+            //
+            // Enough data to fill the whole buffer & process it
+            //
+            memcpy(&pState->buffer[bytesInBuffer], pbData, freeInBuffer);
+            pbData += freeInBuffer;
+            cbData -= freeInBuffer;
+            (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, &pState->buffer[0], pHash->inputBlockSize, &tmp );
+
+            bytesInBuffer = 0;
+        }
+    }
+
+    //
+    // Internal buffer is empty; process all remaining whole blocks in the input
+    //
+    if( cbData >= pHash->inputBlockSize )
+    {
+        (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, pbData, cbData, &tmp );
+        SYMCRYPT_ASSERT( tmp < pHash->inputBlockSize );
+        pbData += cbData - tmp;
+        cbData = tmp;
+    }
+
+    SYMCRYPT_ASSERT( cbData < pHash->inputBlockSize );
+
+    //
+    // buffer remaining input if necessary.
+    //
+    if( cbData > 0 )
+    {
+        memcpy( &pState->buffer[bytesInBuffer], pbData, cbData );
+        bytesInBuffer += (UINT32) cbData;
+    }
+
+    pState->bytesInBuffer = bytesInBuffer;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashCommonPaddingMd4Style(
+    _In_                        PCSYMCRYPT_HASH             pHash,
+    _Inout_                     PSYMCRYPT_COMMON_HASH_STATE pState )
+{
+    SIZE_T tmp;
+    SIZE_T bytesInBuffer = pState->bytesInBuffer;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+    SYMCRYPT_ASSERT( pHash->inputBlockSize == 64 );
+    SYMCRYPT_ASSERT( bytesInBuffer == (pState->dataLengthL & 0x3f) );
+
+    //
+    // The buffer is never completely full, so we can always put the first
+    // padding byte in.
+    //
+    pState->buffer[bytesInBuffer++] = 0x80;
+
+    if( bytesInBuffer > 64-8 ) {
+        //
+        // No room for the rest of the padding. Pad with zeroes & process block
+        // bytesInBuffer is at most 64, so we do not have an integer underflow
+        //
+        SymCryptWipe( &pState->buffer[bytesInBuffer], 64-bytesInBuffer );
+        (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, pState->buffer, 64, &tmp );
+        SYMCRYPT_ASSERT( tmp == 0 );
+        bytesInBuffer = 0;
+    }
+
+    //
+    // Set rest of padding
+    // At this point bytesInBuffer <= 64-8, so we don't have an underflow
+    // We wipe to the end of the buffer as it is 16-aligned,
+    // and it is faster to wipe to an aligned point
+    //
+    SymCryptWipe( &pState->buffer[bytesInBuffer], 64-bytesInBuffer );
+    SYMCRYPT_STORE_LSBFIRST64( &pState->buffer[64-8], pState->dataLengthL * 8 );
+
+    //
+    // Process the final block
+    //
+    (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, pState->buffer, 64, &tmp );
+}
+
+
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHashResultSize( _In_ PCSYMCRYPT_HASH pHash )
+{
+    return pHash->resultSize;
+}
+
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHashInputBlockSize( _In_ PCSYMCRYPT_HASH pHash )
+{
+    return pHash->inputBlockSize;
+}
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHashStateSize( _In_ PCSYMCRYPT_HASH pHash )
+{
+    return pHash->stateSize;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHash(
+    _In_                                                         PCSYMCRYPT_HASH pHash,
+    _In_reads_( cbData )                                         PCBYTE          pbData,
+                                                                 SIZE_T          cbData,
+    _Out_writes_( SYMCRYPT_MIN( cbResult, pHash->resultSize ) )  PBYTE           pbResult,
+                                                                 SIZE_T          cbResult )
+{
+    SYMCRYPT_HASH_STATE hash;
+
+    _Analysis_assume_( pHash->stateSize <= sizeof( hash ) );
+    SymCryptHashInit( pHash, &hash );
+    SymCryptHashAppend( pHash, &hash, pbData, cbData );
+    SymCryptHashResult( pHash, &hash, pbResult, cbResult );
+    SymCryptWipe( &hash, pHash->stateSize );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashInit(
+    _In_                                        PCSYMCRYPT_HASH pHash,
+    _Out_writes_bytes_( pHash->stateSize )      PVOID           pState )
+{
+    (*pHash->initFunc)( pState );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashAppend(
+    _In_                                        PCSYMCRYPT_HASH pHash,
+    _Inout_updates_bytes_( pHash->stateSize )   PVOID           pState,
+    _In_reads_( cbData )                        PCBYTE          pbData,
+                                                SIZE_T          cbData )
+{
+    (*pHash->appendFunc)( pState, pbData, cbData );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashResult(
+    _In_                                                         PCSYMCRYPT_HASH pHash,
+    _Inout_updates_bytes_( pHash->stateSize )                    PVOID           pState,
+    _Out_writes_( SYMCRYPT_MIN( cbResult, pHash->resultSize ) )  PBYTE           pbResult,
+                                                                 SIZE_T          cbResult )
+{
+    BYTE    buf[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+
+    _Analysis_assume_( pHash->resultSize <= SYMCRYPT_HASH_MAX_RESULT_SIZE );
+
+    (*pHash->resultFunc)( pState, buf );
+    memcpy( pbResult, buf, SYMCRYPT_MIN( cbResult, pHash->resultSize ));
+    SymCryptWipe( buf, pHash->resultSize );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashStateCopy(
+    _In_                                PCSYMCRYPT_HASH pHash,
+    _In_reads_( pHash->stateSize )      PCVOID          pSrc,
+    _Out_writes_( pHash->stateSize )    PVOID           pDst)
+{
+    (*pHash->stateCopyFunc)( pSrc, pDst );
+}
diff --git a/libs/symcrypt/lib/hash_buffer_pattern.c b/libs/symcrypt/lib/hash_buffer_pattern.c
new file mode 100644
index 000000000000..c0d5dd19c92f
--- /dev/null
+++ b/libs/symcrypt/lib/hash_buffer_pattern.c
@@ -0,0 +1,75 @@
+//
+// hash_buffer_pattern.c
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+/*
+SymCryptXxxAppend( _Inout_               SYMCRYPT_Xxx_STATE * state,
+                   _In_reads_bytes_( cbData ) PCBYTE               pbData,
+                                         SIZE_T               cbData )
+{
+    <Set up a SIZE_T variable 'bytesInBuffer' that contains the # bytes in the buffer>
+*/
+
+    //
+    // Truncate bytesInBuffer so that we never have an integer overflow.
+    //
+    bytesInBuffer &= SYMCRYPT_XXX_INPUT_BLOCK_SIZE - 1;
+
+    //
+    // If previous data in buffer, buffer new input and transform if possible.
+    //
+    if (bytesInBuffer > 0)
+    {
+        SIZE_T freeInBuffer = SYMCRYPT_XXX_INPUT_BLOCK_SIZE - bytesInBuffer;
+        if( cbData < freeInBuffer )
+        {
+            //
+            // All the data will fit in the buffer.
+            // We don't do anything here.
+            // As cbData < INPUT_BLOCK_SIZE the bulk data processing is skipped,
+            // and the data will be copied to the buffer at the end
+            // of this code.
+        } else {
+            //
+            // Enough data to fill the whole buffer & process it
+            //
+            memcpy(&state->buffer[bytesInBuffer], pbData, freeInBuffer);
+            pbData += freeInBuffer;
+            cbData -= freeInBuffer;
+            SYMCRYPT_XxxAppendBlocks( &state->chain, state->buffer, SYMCRYPT_XXX_INPUT_BLOCK_SIZE );
+
+            //
+            // Set bytesInBuffer to zero to ensure that the trailing data in the
+            // buffer will be copied to the right location of the buffer below.
+            //
+            bytesInBuffer = 0;
+        }
+    }
+
+    //
+    // Internal buffer is empty; process all remaining whole blocks in the input
+    //
+    if( cbData >= SYMCRYPT_XXX_INPUT_BLOCK_SIZE )
+    {
+        SIZE_T cbDataRoundedDown = cbData & ~(SIZE_T)(SYMCRYPT_XXX_INPUT_BLOCK_SIZE - 1);
+        SYMCRYPT_XxxAppendBlocks( &state->chain, pbData, cbDataRoundedDown );
+        pbData += cbDataRoundedDown;
+        cbData -= cbDataRoundedDown;
+    }
+
+    //
+    // buffer remaining input if necessary.
+    //
+    if( cbData > 0 )
+    {
+        memcpy( &state->buffer[bytesInBuffer], pbData, cbData );
+    }
+
+/*
+}
+*/
diff --git a/libs/symcrypt/lib/hash_pattern.c b/libs/symcrypt/lib/hash_pattern.c
new file mode 100644
index 000000000000..57920c7cb717
--- /dev/null
+++ b/libs/symcrypt/lib/hash_pattern.c
@@ -0,0 +1,39 @@
+//
+// hash_pattern.c
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+//
+// This is a file that is #included to define the
+// all-in-one hash function.
+//
+
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_Xxx(
+    _In_reads_( cbData )                                    PCBYTE  pbData,
+                                                            SIZE_T  cbData,
+    _Out_writes_( CONCAT3( SYMCRYPT_, ALG, _RESULT_SIZE ) ) PBYTE   pbResult )
+{
+    SYMCRYPT_XXX_STATE state;
+
+    SYMCRYPT_XxxInit( &state );
+    SYMCRYPT_XxxAppend( &state, pbData, cbData );
+    SYMCRYPT_XxxResult( & state, pbResult );
+}
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxStateCopy( _In_ const SYMCRYPT_XXX_STATE * pSrc, _Out_ SYMCRYPT_XXX_STATE * pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC( pDst );
+}
diff --git a/libs/symcrypt/lib/hkdf.c b/libs/symcrypt/lib/hkdf.c
new file mode 100644
index 000000000000..7a188f9b674c
--- /dev/null
+++ b/libs/symcrypt/lib/hkdf.c
@@ -0,0 +1,229 @@
+//
+// hkdf.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement the HKDF
+// function for the TLS protocol 1.3. It is used in
+// the protocol's key derivation function.
+//
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfExpandKey(
+    _Out_                   PSYMCRYPT_HKDF_EXPANDED_KEY     pExpandedKey,
+    _In_                    PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbIkm)       PCBYTE                          pbIkm,
+                            SIZE_T                          cbIkm,
+    _In_reads_opt_(cbSalt)  PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_ALIGN BYTE rbPrk[SYMCRYPT_MAC_MAX_RESULT_SIZE] = { 0 };
+
+    SYMCRYPT_ASSERT( macAlgorithm->expandedKeySize <= sizeof( pExpandedKey->macKey ) );
+
+    scError = SymCryptHkdfExtractPrk( macAlgorithm, pbIkm, cbIkm, pbSalt, cbSalt, rbPrk, macAlgorithm->resultSize );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptHkdfPrkExpandKey( pExpandedKey, macAlgorithm, rbPrk, macAlgorithm->resultSize );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(&rbPrk[0], sizeof(rbPrk));
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfExtractPrk(
+    _In_                    PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbIkm)       PCBYTE                          pbIkm,
+                            SIZE_T                          cbIkm,
+    _In_reads_opt_(cbSalt)  PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt,
+    _Out_writes_(cbPrk)     PBYTE                           pbPrk,
+                            SIZE_T                          cbPrk )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_MAC_STATE state;
+    SYMCRYPT_MAC_EXPANDED_KEY key;
+
+    // Ensure that pbPrk is the correct size
+    if (cbPrk != macAlgorithm->resultSize)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Calculation of PRK = HMAC-Hash(salt, IKM)
+    scError = macAlgorithm->expandKeyFunc( &key, pbSalt, cbSalt );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    macAlgorithm->initFunc( &state, &key );
+    macAlgorithm->appendFunc( &state, pbIkm, cbIkm );
+    macAlgorithm->resultFunc( &state, pbPrk );
+
+cleanup:
+    SymCryptWipeKnownSize(&key, sizeof(key));
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfPrkExpandKey(
+    _Out_                   PSYMCRYPT_HKDF_EXPANDED_KEY     pExpandedKey,
+    _In_                    PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbPrk)       PCBYTE                          pbPrk,
+                            SIZE_T                          cbPrk )
+{
+    SYMCRYPT_ASSERT( macAlgorithm->expandedKeySize <= sizeof( pExpandedKey->macKey ) );
+
+    pExpandedKey->macAlg = macAlgorithm;
+    return macAlgorithm->expandKeyFunc( &pExpandedKey->macKey, pbPrk, cbPrk );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdfDerive(
+    _In_                    PCSYMCRYPT_HKDF_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbInfo)  PCBYTE                          pbInfo,
+                            SIZE_T                          cbInfo,
+    _Out_writes_(cbResult)  PBYTE                           pbResult,
+                            SIZE_T                          cbResult)
+{
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_MAC_STATE state;
+
+    PCSYMCRYPT_MAC pMacAlgorithm = pExpandedKey->macAlg;
+
+    SYMCRYPT_ALIGN BYTE    rbPartialResult[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+                   BYTE *  pbCurr = pbResult;
+
+    SIZE_T  cbMacResultSize = pMacAlgorithm->resultSize;
+
+    BYTE cntr = 0x01;
+
+    // Check that cbResult <= 255*HashLen
+    if (cbResult > 0xff * cbMacResultSize)
+    {
+        scError = SYMCRYPT_WRONG_DATA_SIZE;
+        goto cleanup;
+    }
+
+    // In the first iteration T(0) is the empty string
+    // Calculate T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
+    pMacAlgorithm->initFunc( &state, pExpandedKey );
+    pMacAlgorithm->appendFunc( &state, pbInfo, cbInfo );
+    pMacAlgorithm->appendFunc( &state, &cntr, sizeof(cntr) );
+    pMacAlgorithm->resultFunc( &state, rbPartialResult );
+
+    // Store the result in the output buffer
+    memcpy(pbCurr, rbPartialResult, SYMCRYPT_MIN(cbResult, cbMacResultSize));
+    if (cbResult <= cbMacResultSize)
+    {
+        goto cleanup;
+    }
+
+    // Update counters
+    cntr++;
+    pbCurr += cbMacResultSize;
+    cbResult -= cbMacResultSize;
+
+    while( cbResult > 0 )
+    {
+        // Calculate T(i) = HMAC-Hash(PRK, T(i-1) | info | 0xi)
+        pMacAlgorithm->initFunc( &state, pExpandedKey );
+        pMacAlgorithm->appendFunc( &state, rbPartialResult, cbMacResultSize );
+        pMacAlgorithm->appendFunc( &state, pbInfo, cbInfo );
+        pMacAlgorithm->appendFunc( &state, &cntr, sizeof(cntr) );
+        pMacAlgorithm->resultFunc( &state, rbPartialResult );
+
+        // Store the result in the output buffer
+        memcpy(pbCurr, rbPartialResult, SYMCRYPT_MIN(cbResult, cbMacResultSize));
+        if (cbResult <= cbMacResultSize)
+        {
+            goto cleanup;
+        }
+
+        // Update counters
+        cntr++;
+        pbCurr += cbMacResultSize;
+        cbResult -= cbMacResultSize;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(&rbPartialResult[0], sizeof(rbPartialResult));
+
+    return scError;
+}
+
+//
+// The full HKDF
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHkdf(
+                            PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbIkm)       PCBYTE          pbIkm,
+                            SIZE_T          cbIkm,
+    _In_reads_opt_(cbSalt)  PCBYTE          pbSalt,
+                            SIZE_T          cbSalt,
+    _In_reads_opt_(cbInfo)  PCBYTE          pbInfo,
+                            SIZE_T          cbInfo,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_HKDF_EXPANDED_KEY key;
+
+    // Create the expanded key
+    scError = SymCryptHkdfExpandKey(
+        &key,
+        macAlgorithm,
+        pbIkm,
+        cbIkm,
+        pbSalt,
+        cbSalt );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Derive the key
+    scError = SymCryptHkdfDerive(
+        &key,
+        pbInfo,
+        cbInfo,
+        pbResult,
+        cbResult );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(&key, sizeof(key));
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/hmac.c b/libs/symcrypt/lib/hmac.c
new file mode 100644
index 000000000000..766fd9eb95e3
--- /dev/null
+++ b/libs/symcrypt/lib/hmac.c
@@ -0,0 +1,195 @@
+//
+// hmac.c
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacStateCopy(
+    _In_        PCSYMCRYPT_HMAC_STATE           pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_EXPANDED_KEY    pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_STATE            pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+
+    PCSYMCRYPT_HASH pHash = pSrc->pKey->pHash;
+
+    SymCryptHashStateCopy( pHash, &pSrc->hash, &pDst->hash );
+
+    if( pExpandedKey != NULL )
+    {
+        SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+        pDst->pKey = pExpandedKey;
+    }
+    else
+    {
+        SYMCRYPT_CHECK_MAGIC( pSrc->pKey );
+        pDst->pKey = pSrc->pKey;
+    }
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacKeyCopy(
+    _In_    PCSYMCRYPT_HMAC_EXPANDED_KEY    pSrc,
+    _Out_   PSYMCRYPT_HMAC_EXPANDED_KEY     pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+
+    // Copy innerState and outerState
+    SymCryptHashStateCopy(pSrc->pHash, &pSrc->innerState, &pDst->innerState );
+    SymCryptHashStateCopy(pSrc->pHash, &pSrc->outerState, &pDst->outerState );
+
+    pDst->pHash = pSrc->pHash;
+
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacExpandKey(
+    _In_                    PCSYMCRYPT_HASH             pHash,
+    _Out_                   PSYMCRYPT_HMAC_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                      pbKey,
+                            SIZE_T                      cbKey )
+{
+    // This buffer has to be large enough to hold one input block
+    // and the result of the hash function.
+    // Using SHA3-224 input block size to satisfy those requirements.
+    SYMCRYPT_ALIGN BYTE iblock[ SYMCRYPT_SHA3_224_INPUT_BLOCK_SIZE ];
+
+    SYMCRYPT_ASSERT( sizeof(iblock) >= pHash->inputBlockSize );
+    SYMCRYPT_ASSERT( sizeof(iblock) >= pHash->resultSize );
+
+    // XorByteIntoBuffer function updates the buffer in multiples of 8-bytes
+    SYMCRYPT_ASSERT( pHash->inputBlockSize % 8 == 0);
+
+    memset( iblock, 0, sizeof( iblock ) );
+
+    if( cbKey <= pHash->inputBlockSize )
+    {
+        if( cbKey > 0 )
+        {
+            memcpy( iblock, pbKey, cbKey );
+        }
+    }
+    else
+    {
+        SymCryptHash( pHash, pbKey, cbKey, iblock, pHash->resultSize );
+    }
+
+    XorByteIntoBuffer( iblock, pHash->inputBlockSize / 8, HMAC_IPAD_BYTE );
+
+    //
+    // Initialize the inner and outer states in the expanded key
+    //
+    SymCryptHashInit( pHash, &pExpandedKey->innerState );
+    SymCryptHashInit( pHash, &pExpandedKey->outerState );
+
+    // Update the inner state in the expanded key
+    SymCryptHashAppend( pHash, &pExpandedKey->innerState, iblock, pHash->inputBlockSize );
+
+    XorByteIntoBuffer( iblock, pHash->inputBlockSize / 8, HMAC_IPAD_BYTE ^ HMAC_OPAD_BYTE );
+
+    // Update the outer state in the expanded key
+    SymCryptHashAppend( pHash, &pExpandedKey->outerState, iblock, pHash->inputBlockSize );
+
+    SymCryptWipeKnownSize( iblock, sizeof( iblock ) );
+
+    // Save the hash function in the expanded key, it will be used in other
+    // generic HMAC function calls.
+    pExpandedKey->pHash = pHash;
+
+    SYMCRYPT_SET_MAGIC(pExpandedKey);
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptHmacInit(
+    _Out_   PSYMCRYPT_HMAC_STATE            pState,
+    _In_    PCSYMCRYPT_HMAC_EXPANDED_KEY    pExpandedKey )
+{
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    SymCryptHashStateCopy( pExpandedKey->pHash, &pExpandedKey->innerState, &pState->hash );
+
+    pState->pKey = pExpandedKey;
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacAppend(
+    _Inout_                 PSYMCRYPT_HMAC_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData )
+{
+    SymCryptHashAppend( pState->pKey->pHash, &pState->hash, pbData, cbData );
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptHmacResult(
+    _Inout_                                         PSYMCRYPT_HMAC_STATE    pState,
+    _Out_writes_( pState->pKey->pHash->resultSize ) PBYTE                   pbResult )
+{
+    BYTE    innerRes[64];
+
+    PCSYMCRYPT_HASH pHash = pState->pKey->pHash;
+
+    SYMCRYPT_ASSERT(sizeof(innerRes) >= pHash->resultSize);
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    //
+    // We have to buffer the inner hash result. We can't put it directly in the
+    // hash state data buffer as the Result() function wipes that buffer before returning.
+    //
+    SymCryptHashResult( pHash, &pState->hash, innerRes, pHash->resultSize );
+
+    SYMCRYPT_CHECK_MAGIC( pState->pKey )
+
+    SymCryptHashStateCopy( pHash, &pState->pKey->outerState, &pState->hash );
+
+    SymCryptHashAppend( pHash, &pState->hash, innerRes, pHash->resultSize );
+
+    SymCryptHashResult( pHash, &pState->hash, pbResult, pHash->resultSize );
+
+    //
+    // The SymCryptHashResult already wipes the hash state.
+    // We only need to wipe our own buffer.
+    //
+    // We also set the key pointer to NULL. This is not for security;
+    // it creates a clear error when callers forget to call the Init routine
+    // when re-using a state. Rather than the wrong result, they will get
+    // a NULL pointer exception, and they will fix their code.
+    //
+
+    SymCryptWipeKnownSize( innerRes, sizeof( innerRes ) );
+    pState->pKey = NULL;
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptHmac(
+    _In_                                            PCSYMCRYPT_HMAC_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( cbData )                            PCBYTE                          pbData,
+                                                    SIZE_T                          cbData,
+    _Out_writes_( pExpandedKey->pHash->resultSize ) PBYTE                           pbResult )
+{
+    SYMCRYPT_HMAC_STATE state;
+
+    SymCryptHmacInit( &state, pExpandedKey );
+    SymCryptHmacAppend( &state, pbData, cbData );
+    SymCryptHmacResult( &state, pbResult );
+}
diff --git a/libs/symcrypt/lib/hmac_pattern.c b/libs/symcrypt/lib/hmac_pattern.c
new file mode 100644
index 000000000000..1d4ccf7c7b58
--- /dev/null
+++ b/libs/symcrypt/lib/hmac_pattern.c
@@ -0,0 +1,197 @@
+//
+// hmac_pattern.c
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxxStateCopy(
+    _In_        PCSYMCRYPT_HMAC_XXX_STATE           pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_XXX_EXPANDED_KEY    pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_XXX_STATE            pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+
+    SYMCRYPT_XxxStateCopy( &pSrc->hash, &pDst->hash );
+
+    if( pExpandedKey != NULL )
+    {
+        SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+        pDst->pKey = pExpandedKey;
+    }
+    else
+    {
+        SYMCRYPT_CHECK_MAGIC( pSrc->pKey );
+        pDst->pKey = pSrc->pKey;
+    }
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxxKeyCopy( _In_ PCSYMCRYPT_HMAC_XXX_EXPANDED_KEY pSrc, _Out_ PSYMCRYPT_HMAC_XXX_EXPANDED_KEY pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxxExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_XXX_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                          pbKey,
+                            SIZE_T                          cbKey )
+{
+    SYMCRYPT_XXX_STATE  hashState;
+    SYMCRYPT_ALIGN BYTE iblock[ SYMCRYPT_XXX_INPUT_BLOCK_SIZE ];  // One input block for the hash function
+    SIZE_T tmp;
+
+    SYMCRYPT_SET_MAGIC( pExpandedKey );
+
+    //
+    // Initialize our hash state and our input block
+    // We wipe the whole block & then copy the key into it. This is often faster
+    // as the compiler can optimize the wipe because it knows the size at compile time.
+    //
+    SYMCRYPT_XxxInit( &hashState );
+    memset( iblock, 0, sizeof( iblock ) );
+
+    if( cbKey <= sizeof( iblock ) )
+    {
+        if( cbKey > 0 )
+        {
+            memcpy( iblock, pbKey, cbKey );
+        }
+    } else {
+        //
+        // We can use the existing MD5 state to hash the long key.
+        // The state is re-initialized by the SymCryptMd5Result() function.
+        //
+        SYMCRYPT_XxxAppend( &hashState, pbKey, cbKey );
+        SYMCRYPT_XxxResult( &hashState, iblock );
+    }
+
+    XorByteIntoBuffer( iblock, sizeof( iblock )/8, HMAC_IPAD_BYTE );
+
+    //
+    // Copy the initial chaining state to both states in the expanded key
+    //
+    pExpandedKey->innerState = hashState.chain;
+    pExpandedKey->outerState = hashState.chain;
+
+    //
+    // Update the state in the expanded key directly
+    //
+    SYMCRYPT_XxxAppendBlocks( &pExpandedKey->innerState, iblock, sizeof( iblock ), &tmp );
+
+    XorByteIntoBuffer( iblock, sizeof( iblock )/8, HMAC_IPAD_BYTE ^ HMAC_OPAD_BYTE );
+
+    SYMCRYPT_XxxAppendBlocks( &pExpandedKey->outerState, iblock, sizeof( iblock ), &tmp );
+
+    SymCryptWipeKnownSize( iblock, sizeof( iblock ) );
+    SymCryptWipeKnownSize( &hashState, sizeof( hashState ) );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxxInit(
+    _Out_   PSYMCRYPT_HMAC_XXX_STATE            pState,
+    _In_    PCSYMCRYPT_HMAC_XXX_EXPANDED_KEY    pExpandedKey)
+{
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+    //
+    // We don't call SymCryptXxxInit on the hash sub-state;
+    // instead we directly initialize its fields.
+    //
+    SYMCRYPT_SET_MAGIC( &pState->hash );
+    pState->hash.chain = pExpandedKey->innerState;
+    SET_DATALENGTH( pState->hash, SYMCRYPT_XXX_INPUT_BLOCK_SIZE );
+    pState->hash.bytesInBuffer = 0;
+    pState->pKey = pExpandedKey;
+}
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxxAppend(
+    _Inout_                 PSYMCRYPT_HMAC_XXX_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData )
+{
+    SYMCRYPT_XxxAppend( &pState->hash, pbData, cbData );
+}
+
+C_ASSERT( SYMCRYPT_XXX_RESULT_SIZE == SYMCRYPT_HMAC_XXX_RESULT_SIZE );
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxxResult(
+    _Inout_                                         PSYMCRYPT_HMAC_XXX_STATE    pState,
+    _Out_writes_( SYMCRYPT_HMAC_XXX_RESULT_SIZE )   PBYTE                       pbResult )
+{
+    BYTE    innerRes[SYMCRYPT_XXX_RESULT_SIZE];
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    //
+    // We have to buffer the inner hash result. We can't put it directly in the
+    // hash state data buffer as the Result() function wipes that buffer before returning.
+    //
+
+    SYMCRYPT_XxxResult( &pState->hash, innerRes );
+
+    SYMCRYPT_CHECK_MAGIC( pState->pKey )
+
+    pState->hash.chain = pState->pKey->outerState;
+
+    //
+    // We put the data directly in the buffer, rather than call the Append function.
+    //
+    memcpy( &pState->hash.buffer, innerRes, sizeof( innerRes ) );
+    SET_DATALENGTH(  pState->hash, SYMCRYPT_XXX_INPUT_BLOCK_SIZE + SYMCRYPT_XXX_RESULT_SIZE );
+    pState->hash.bytesInBuffer = SYMCRYPT_XXX_RESULT_SIZE;
+
+    SYMCRYPT_XxxResult( &pState->hash, pbResult );
+
+    //
+    // The SymCryptXxxResult already wipes the hash state.
+    // We only need to wipe our own buffer.
+    //
+    // We also set the key pointer to NULL. This is not for security;
+    // it creates a clear error when callers forget to call the Init routine
+    // when re-using a state. Rather than the wrong result, they will get
+    // a NULL pointer exception, and they will fix their code.
+    //
+
+    SymCryptWipe( innerRes, sizeof( innerRes ) );
+    pState->pKey = NULL;
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_HmacXxx(
+    _In_                                            PCSYMCRYPT_HMAC_XXX_EXPANDED_KEY    pExpandedKey,
+    _In_reads_( cbData )                           PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_HMAC_XXX_RESULT_SIZE )   PBYTE                               pbResult )
+{
+    SYMCRYPT_HMAC_XXX_STATE state;
+
+    SYMCRYPT_HmacXxxInit( &state, pExpandedKey );
+    SYMCRYPT_HmacXxxAppend( &state, pbData, cbData );
+    SYMCRYPT_HmacXxxResult( &state, pbResult );
+
+}
diff --git a/libs/symcrypt/lib/hmacmd5.c b/libs/symcrypt/lib/hmacmd5.c
new file mode 100644
index 000000000000..03f5fe01aea0
--- /dev/null
+++ b/libs/symcrypt/lib/hmacmd5.c
@@ -0,0 +1,56 @@
+//
+// HmacMd5.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define ALG MD5
+#define Alg Md5
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacMd5Algorithm_default = {
+    SymCryptHmacMd5ExpandKey,
+    SymCryptHmacMd5Init,
+    SymCryptHmacMd5Append,
+    SymCryptHmacMd5Result,
+    sizeof(SYMCRYPT_HMAC_MD5_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_MD5_STATE),
+    SYMCRYPT_HMAC_MD5_RESULT_SIZE,
+    &SymCryptMd5Algorithm,
+    0,
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacMd5Algorithm = &SymCryptHmacMd5Algorithm_default;
+
+static const BYTE hmacMd5Kat[16] = {
+    0x77, 0x33, 0x69, 0x79, 0x9e, 0x54, 0xeb, 0x49, 0xff, 0x21, 0xe6, 0xf9, 0x63, 0xe5, 0xbb, 0x49,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacMd5Selftest(void)
+{
+    SYMCRYPT_HMAC_MD5_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_MD5_RESULT_SIZE];
+
+    SymCryptHmacMd5ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacMd5( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacMd5Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hmd5');
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha1.c b/libs/symcrypt/lib/hmacsha1.c
new file mode 100644
index 000000000000..e8a8de0c4af0
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha1.c
@@ -0,0 +1,65 @@
+//
+// HmacSha1.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// This implementation of HMAC uses extensive knowledge of the internal workings of the
+// SHA1 implementation and uses internal routines.
+// This reduces the overhead per HMAC computation by up to 20%, which is significant
+// enough to take on the added complexity.
+//
+
+#define ALG SHA1
+#define Alg Sha1
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha1Algorithm_default = {
+    SymCryptHmacSha1ExpandKey,
+    SymCryptHmacSha1Init,
+    SymCryptHmacSha1Append,
+    SymCryptHmacSha1Result,
+    sizeof(SYMCRYPT_HMAC_SHA1_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA1_STATE),
+    SYMCRYPT_HMAC_SHA1_RESULT_SIZE,
+    &SymCryptSha1Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA1_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha1Algorithm = &SymCryptHmacSha1Algorithm_default;
+
+static const BYTE hmacSha1Kat[20] = {
+    0x2a, 0x29, 0x85, 0x40, 0x23, 0xba, 0x2e, 0xf1,
+    0x49, 0x0f, 0x8c, 0xd8, 0x97, 0xa8, 0xcc, 0x6b,
+    0x55, 0x7b, 0x2a, 0x12,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha1Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA1_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA1_RESULT_SIZE];
+
+    SymCryptHmacSha1ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha1( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha1Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hSh1' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha224.c b/libs/symcrypt/lib/hmacsha224.c
new file mode 100644
index 000000000000..fcfc8d85cdfc
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha224.c
@@ -0,0 +1,62 @@
+//
+// HmacSha224.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define SymCryptSha224AppendBlocks  SymCryptSha256AppendBlocks
+
+#define ALG SHA224
+#define Alg Sha224
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha224Algorithm_default = {
+    SymCryptHmacSha224ExpandKey,
+    SymCryptHmacSha224Init,
+    SymCryptHmacSha224Append,
+    SymCryptHmacSha224Result,
+    sizeof(SYMCRYPT_HMAC_SHA224_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA224_STATE),
+    SYMCRYPT_HMAC_SHA224_RESULT_SIZE,
+    &SymCryptSha224Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA224_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha224Algorithm = &SymCryptHmacSha224Algorithm_default;
+
+static const BYTE hmacSha224Kat[28] = {
+    0x3e, 0x1c, 0x48, 0x2f, 0x66, 0x49, 0x67, 0xa9,
+    0xad, 0x4f, 0x76, 0x52, 0x36, 0xf8, 0x5a, 0x1f,
+    0x63, 0x5b, 0x34, 0xe9, 0x35, 0x71, 0x62, 0x35,
+    0xa2, 0x9e, 0x61, 0xb1
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha224Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA224_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA224_RESULT_SIZE];
+
+    SymCryptHmacSha224ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha224( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha224Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh4' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha256.c b/libs/symcrypt/lib/hmacsha256.c
new file mode 100644
index 000000000000..c2641ef39aa8
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha256.c
@@ -0,0 +1,60 @@
+//
+// HmacSha256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define ALG SHA256
+#define Alg Sha256
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha256Algorithm_default = {
+    SymCryptHmacSha256ExpandKey,
+    SymCryptHmacSha256Init,
+    SymCryptHmacSha256Append,
+    SymCryptHmacSha256Result,
+    sizeof(SYMCRYPT_HMAC_SHA256_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA256_STATE),
+    SYMCRYPT_HMAC_SHA256_RESULT_SIZE,
+    &SymCryptSha256Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA256_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha256Algorithm = &SymCryptHmacSha256Algorithm_default;
+
+static const BYTE hmacSha256Kat[32] = {
+    0xd6, 0x01, 0xcc, 0x17, 0x75, 0x59, 0xb0, 0x24,
+    0x84, 0x59, 0x78, 0x7f, 0x7e, 0x80, 0x4e, 0xd7,
+    0xf2, 0x76, 0x89, 0xb5, 0x99, 0x5c, 0x59, 0xb6,
+    0x61, 0x80, 0x2d, 0x96, 0x82, 0xfd, 0xf8, 0xd2,
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha256Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA256_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA256_RESULT_SIZE];
+
+    SymCryptHmacSha256ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha256( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha256Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh2' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha384.c b/libs/symcrypt/lib/hmacsha384.c
new file mode 100644
index 000000000000..36042092edfc
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha384.c
@@ -0,0 +1,59 @@
+//
+// HmacSha512.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define SymCryptSha384AppendBlocks  SymCryptSha512AppendBlocks
+
+#define ALG SHA384
+#define Alg Sha384
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len; state.dataLengthH = 0;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha384Algorithm_default = {
+    SymCryptHmacSha384ExpandKey,
+    SymCryptHmacSha384Init,
+    SymCryptHmacSha384Append,
+    SymCryptHmacSha384Result,
+    sizeof(SYMCRYPT_HMAC_SHA384_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA384_STATE),
+    SYMCRYPT_HMAC_SHA384_RESULT_SIZE,
+    &SymCryptSha384Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA384_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha384Algorithm = &SymCryptHmacSha384Algorithm_default;
+
+static const BYTE hmacSha384Kat[48] = {
+    0x67, 0xdb, 0x9d, 0x4d, 0x66, 0xed, 0xf2, 0xe7, 0x2b, 0x88, 0xb8, 0x50, 0x55, 0x68, 0xa0, 0x00,
+    0xa9, 0x83, 0x2b, 0xa3, 0x5e, 0x4f, 0xde, 0xcf, 0xe5, 0x38, 0x9a, 0x5d, 0x92, 0x79, 0x81, 0x53,
+    0x6d, 0xdb, 0x94, 0xc0, 0xf6, 0xc0, 0xbd, 0x94, 0xc4, 0x18, 0x96, 0x4b, 0xbe, 0x4b, 0x6c, 0xf2,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha384Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA384_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA384_RESULT_SIZE];
+
+    SymCryptHmacSha384ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha384( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+    if( memcmp( res, hmacSha384Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh3' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha3_224.c b/libs/symcrypt/lib/hmacsha3_224.c
new file mode 100644
index 000000000000..8f3b7626e436
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha3_224.c
@@ -0,0 +1,122 @@
+//
+// HmacSha3_224.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_224ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey)
+{
+    return SymCryptHmacExpandKey(SymCryptSha3_224Algorithm, &pExpandedKey->generic, pbKey, cbKey);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY  pDst)
+{
+    SymCryptHmacKeyCopy(&pSrc->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE )  PBYTE                                   pbResult )
+{
+    SymCryptHmac(&pExpandedKey->generic, pbData, cbData, pbResult);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_224_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_224_STATE         pDst )
+{
+    SymCryptHmacStateCopy(&pSrc->generic, pExpandedKey == NULL ? NULL : &pExpandedKey->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_224_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY pExpandedKey)
+{
+    SymCryptHmacInit(&pState->generic, &pExpandedKey->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_224_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData )
+{
+    SymCryptHmacAppend(&pState->generic, pbData, cbData);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_224_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE )  PBYTE                           pbResult )
+{
+    SymCryptHmacResult(&pState->generic, pbResult);
+}
+
+
+const SYMCRYPT_MAC SymCryptHmacSha3_224Algorithm_default = {
+    SymCryptHmacSha3_224ExpandKey,
+    SymCryptHmacSha3_224Init,
+    SymCryptHmacSha3_224Append,
+    SymCryptHmacSha3_224Result,
+    sizeof(SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA3_224_STATE),
+    SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE,
+    &SymCryptSha3_224Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY, generic.outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha3_224Algorithm = &SymCryptHmacSha3_224Algorithm_default;
+
+
+static const BYTE hmacSha3_224Kat[28] =
+{
+    0x10, 0x90, 0xac, 0xa1, 0xd5, 0xad, 0xc4, 0x12,
+    0xf5, 0xe7, 0xb4, 0xdf, 0xd2, 0x87, 0x09, 0xdd,
+    0x24, 0x82, 0xc0, 0x4a, 0x5e, 0x9a, 0x3b, 0xf0,
+    0xc3, 0x35, 0x7e, 0x12
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_224Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA3_224_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA3_224_RESULT_SIZE];
+
+    SymCryptHmacSha3_224ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha3_224( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha3_224Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh3' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha3_256.c b/libs/symcrypt/lib/hmacsha3_256.c
new file mode 100644
index 000000000000..8f145d398bfa
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha3_256.c
@@ -0,0 +1,122 @@
+//
+// HmacSha3_256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_256ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey)
+{
+    return SymCryptHmacExpandKey(SymCryptSha3_256Algorithm, &pExpandedKey->generic, pbKey, cbKey);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY  pDst)
+{
+    SymCryptHmacKeyCopy(&pSrc->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE )  PBYTE                                   pbResult )
+{
+    SymCryptHmac(&pExpandedKey->generic, pbData, cbData, pbResult);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_256_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_256_STATE         pDst )
+{
+    SymCryptHmacStateCopy(&pSrc->generic, pExpandedKey == NULL ? NULL : &pExpandedKey->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_256_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY pExpandedKey)
+{
+    SymCryptHmacInit(&pState->generic, &pExpandedKey->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_256_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData )
+{
+    SymCryptHmacAppend(&pState->generic, pbData, cbData);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_256_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE )  PBYTE                           pbResult )
+{
+    SymCryptHmacResult(&pState->generic, pbResult);
+}
+
+
+const SYMCRYPT_MAC SymCryptHmacSha3_256Algorithm_default = {
+    SymCryptHmacSha3_256ExpandKey,
+    SymCryptHmacSha3_256Init,
+    SymCryptHmacSha3_256Append,
+    SymCryptHmacSha3_256Result,
+    sizeof(SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA3_256_STATE),
+    SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE,
+    &SymCryptSha3_256Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY, generic.outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha3_256Algorithm = &SymCryptHmacSha3_256Algorithm_default;
+
+
+static const BYTE hmacSha3_256Kat[32] =
+{
+    0x18, 0xe8, 0x2e, 0xa4, 0x5a, 0x94, 0x07, 0xcc,
+    0xb7, 0x87, 0x29, 0x16, 0x80, 0x99, 0xd6, 0xc6,
+    0x73, 0x1b, 0x56, 0x2e, 0x0d, 0x16, 0x67, 0x5a,
+    0x1f, 0xe2, 0xe3, 0xd6, 0x81, 0x56, 0x52, 0x77
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_256Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA3_256_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA3_256_RESULT_SIZE];
+
+    SymCryptHmacSha3_256ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha3_256( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha3_256Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh3' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha3_384.c b/libs/symcrypt/lib/hmacsha3_384.c
new file mode 100644
index 000000000000..14eef9d09d76
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha3_384.c
@@ -0,0 +1,124 @@
+//
+// HmacSha3_256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_384ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey)
+{
+    return SymCryptHmacExpandKey(SymCryptSha3_384Algorithm, &pExpandedKey->generic, pbKey, cbKey);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY  pDst)
+{
+    SymCryptHmacKeyCopy(&pSrc->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE )  PBYTE                                   pbResult )
+{
+    SymCryptHmac(&pExpandedKey->generic, pbData, cbData, pbResult);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_384_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_384_STATE         pDst )
+{
+    SymCryptHmacStateCopy(&pSrc->generic, pExpandedKey == NULL ? NULL : &pExpandedKey->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_384_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY pExpandedKey)
+{
+    SymCryptHmacInit(&pState->generic, &pExpandedKey->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_384_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData )
+{
+    SymCryptHmacAppend(&pState->generic, pbData, cbData);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_384_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE )  PBYTE                           pbResult )
+{
+    SymCryptHmacResult(&pState->generic, pbResult);
+}
+
+
+const SYMCRYPT_MAC SymCryptHmacSha3_384Algorithm_default = {
+    SymCryptHmacSha3_384ExpandKey,
+    SymCryptHmacSha3_384Init,
+    SymCryptHmacSha3_384Append,
+    SymCryptHmacSha3_384Result,
+    sizeof(SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA3_384_STATE),
+    SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE,
+    &SymCryptSha3_384Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY, generic.outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha3_384Algorithm = &SymCryptHmacSha3_384Algorithm_default;
+
+
+static const BYTE hmacSha3_384Kat[48] =
+{
+    0x8f, 0xc4, 0x8a, 0x84, 0xb5, 0xa7, 0xa1, 0x36,
+    0x3c, 0x3b, 0x4b, 0x21, 0x3c, 0xfb, 0x65, 0x36,
+    0xa6, 0x2b, 0xa3, 0x4c, 0x12, 0x33, 0xa1, 0x27,
+    0xbc, 0xfc, 0xb2, 0xd7, 0xae, 0xaf, 0x30, 0x6b,
+    0xc9, 0xe6, 0x90, 0xfd, 0xf1, 0xfa, 0x12, 0x61,
+    0xa4, 0x7e, 0xb2, 0x27, 0x1a, 0xeb, 0xf1, 0x34
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_384Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA3_384_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA3_384_RESULT_SIZE];
+
+    SymCryptHmacSha3_384ExpandKey( &xKey, SymCryptTestKey32, 24 );
+    SymCryptHmacSha3_384( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha3_384Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh3' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha3_512.c b/libs/symcrypt/lib/hmacsha3_512.c
new file mode 100644
index 000000000000..5a576e6ae247
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha3_512.c
@@ -0,0 +1,126 @@
+//
+// HmacSha3_256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHmacSha3_512ExpandKey(
+    _Out_                   PSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY    pExpandedKey,
+    _In_reads_opt_(cbKey)   PCBYTE                                  pbKey,
+                            SIZE_T                                  cbKey)
+{
+    return SymCryptHmacExpandKey(SymCryptSha3_512Algorithm, &pExpandedKey->generic, pbKey, cbKey);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512KeyCopy(
+    _In_    PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY  pDst)
+{
+    SymCryptHmacKeyCopy(&pSrc->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512(
+    _In_                                                PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY   pExpandedKey,
+    _In_reads_( cbData )                                PCBYTE                                  pbData,
+                                                        SIZE_T                                  cbData,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE )  PBYTE                                   pbResult )
+{
+    SymCryptHmac(&pExpandedKey->generic, pbData, cbData, pbResult);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512StateCopy(
+    _In_        PCSYMCRYPT_HMAC_SHA3_512_STATE        pSrc,
+    _In_opt_    PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY pExpandedKey,
+    _Out_       PSYMCRYPT_HMAC_SHA3_512_STATE         pDst )
+{
+    SymCryptHmacStateCopy(&pSrc->generic, pExpandedKey == NULL ? NULL : &pExpandedKey->generic, &pDst->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Init(
+    _Out_   PSYMCRYPT_HMAC_SHA3_512_STATE         pState,
+    _In_    PCSYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY pExpandedKey)
+{
+    SymCryptHmacInit(&pState->generic, &pExpandedKey->generic);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Append(
+    _Inout_                 PSYMCRYPT_HMAC_SHA3_512_STATE   pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData )
+{
+    SymCryptHmacAppend(&pState->generic, pbData, cbData);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Result(
+    _Inout_                                             PSYMCRYPT_HMAC_SHA3_512_STATE   pState,
+    _Out_writes_( SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE )  PBYTE                           pbResult )
+{
+    SymCryptHmacResult(&pState->generic, pbResult);
+}
+
+
+const SYMCRYPT_MAC SymCryptHmacSha3_512Algorithm_default = {
+    SymCryptHmacSha3_512ExpandKey,
+    SymCryptHmacSha3_512Init,
+    SymCryptHmacSha3_512Append,
+    SymCryptHmacSha3_512Result,
+    sizeof(SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA3_512_STATE),
+    SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE,
+    &SymCryptSha3_512Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY, generic.outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha3_512Algorithm = &SymCryptHmacSha3_512Algorithm_default;
+
+
+static const BYTE hmacSha3_512Kat[64] =
+{
+    0x83, 0x3b, 0x31, 0xe7, 0x77, 0xd6, 0xb3, 0x3d,
+    0x75, 0x23, 0xa5, 0x79, 0xcc, 0x3b, 0xeb, 0x27,
+    0x6f, 0xd6, 0x52, 0x57, 0x54, 0xc4, 0xc5, 0x4b,
+    0x2d, 0x5a, 0x34, 0x7d, 0x36, 0x24, 0x07, 0x91,
+    0x7a, 0x3c, 0x62, 0x6e, 0x7e, 0xdb, 0x8e, 0x49,
+    0x3b, 0x42, 0xc8, 0xe5, 0xa6, 0x96, 0xd5, 0xe6,
+    0x6b, 0xa7, 0xad, 0x20, 0x00, 0xeb, 0x6c, 0xff,
+    0x76, 0xcb, 0x1e, 0xc0, 0x30, 0x13, 0x0e, 0x81
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha3_512Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA3_512_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA3_512_RESULT_SIZE];
+
+    SymCryptHmacSha3_512ExpandKey( &xKey, SymCryptTestKey32, 32 );
+    SymCryptHmacSha3_512( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha3_512Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh3' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha512.c b/libs/symcrypt/lib/hmacsha512.c
new file mode 100644
index 000000000000..c91dfab7e1a3
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha512.c
@@ -0,0 +1,59 @@
+//
+// HmacSha512.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define ALG SHA512
+#define Alg Sha512
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len; state.dataLengthH = 0;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha512Algorithm_default = {
+    SymCryptHmacSha512ExpandKey,
+    SymCryptHmacSha512Init,
+    SymCryptHmacSha512Append,
+    SymCryptHmacSha512Result,
+    sizeof(SYMCRYPT_HMAC_SHA512_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA512_STATE),
+    SYMCRYPT_HMAC_SHA512_RESULT_SIZE,
+    &SymCryptSha512Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA512_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha512Algorithm = &SymCryptHmacSha512Algorithm_default;
+
+static const BYTE hmacSha512Kat[64] = {
+    0x07, 0x64, 0xa6, 0x58, 0xeb, 0x3e, 0x2f, 0xb0, 0x2c, 0x06, 0x72, 0x93, 0xcd, 0xaa, 0x3c, 0x05,
+    0x28, 0x73, 0x15, 0xf2, 0xd3, 0xb4, 0x5a, 0x28, 0x10, 0x20, 0x1e, 0x26, 0xc3, 0x89, 0x35, 0x48,
+    0xe9, 0xea, 0xca, 0x72, 0xf0, 0x2e, 0x04, 0x19, 0x20, 0x31, 0x71, 0x68, 0xb5, 0x7a, 0x86, 0x40,
+    0x29, 0x1b, 0x3b, 0xb7, 0xaa, 0x4a, 0x5f, 0xaf, 0x80, 0x26, 0xb4, 0xad, 0x23, 0x5a, 0xc4, 0x25,
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA512_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA512_RESULT_SIZE];
+
+    SymCryptHmacSha512ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha512( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+    if( memcmp( res, hmacSha512Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh5' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha512_224.c b/libs/symcrypt/lib/hmacsha512_224.c
new file mode 100644
index 000000000000..a36556485dcd
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha512_224.c
@@ -0,0 +1,62 @@
+//
+// HmacSha512_224.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define SymCryptSha512_224AppendBlocks  SymCryptSha512AppendBlocks
+
+#define ALG SHA512_224
+#define Alg Sha512_224
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len; state.dataLengthH = 0;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha512_224Algorithm_default = {
+    SymCryptHmacSha512_224ExpandKey,
+    SymCryptHmacSha512_224Init,
+    SymCryptHmacSha512_224Append,
+    SymCryptHmacSha512_224Result,
+    sizeof(SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA512_224_STATE),
+    SYMCRYPT_HMAC_SHA512_224_RESULT_SIZE,
+    &SymCryptSha512_224Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha512_224Algorithm = &SymCryptHmacSha512_224Algorithm_default;
+
+static const BYTE hmacSha512_224Kat[28] = {
+    0x62, 0xc9, 0x59, 0xc7, 0x5b, 0x3c, 0xb2, 0xaf,
+    0x95, 0xf5, 0x59, 0x73, 0x2c, 0x46, 0x1d, 0x72,
+    0x06, 0x9e, 0xf9, 0x52, 0x9a, 0x8d, 0x84, 0x1a,
+    0x73, 0x97, 0xa6, 0x9c
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_224Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA512_224_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA512_224_RESULT_SIZE];
+
+    SymCryptHmacSha512_224ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha512_224( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha512_224Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh4' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/hmacsha512_256.c b/libs/symcrypt/lib/hmacsha512_256.c
new file mode 100644
index 000000000000..a7a49413cda3
--- /dev/null
+++ b/libs/symcrypt/lib/hmacsha512_256.c
@@ -0,0 +1,62 @@
+//
+// HmacSha512_256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define SymCryptSha512_256AppendBlocks  SymCryptSha512AppendBlocks
+
+#define ALG SHA512_256
+#define Alg Sha512_256
+#define SET_DATALENGTH( state, len )    {state.dataLengthL = len; state.dataLengthH = 0;}
+#include "hmac_pattern.c"
+#undef SET_DATALENGTH
+#undef Alg
+#undef ALG
+
+const SYMCRYPT_MAC SymCryptHmacSha512_256Algorithm_default = {
+    SymCryptHmacSha512_256ExpandKey,
+    SymCryptHmacSha512_256Init,
+    SymCryptHmacSha512_256Append,
+    SymCryptHmacSha512_256Result,
+    sizeof(SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY),
+    sizeof(SYMCRYPT_HMAC_SHA512_256_STATE),
+    SYMCRYPT_HMAC_SHA512_256_RESULT_SIZE,
+    &SymCryptSha512_256Algorithm,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY, outerState ),
+};
+
+const PCSYMCRYPT_MAC SymCryptHmacSha512_256Algorithm = &SymCryptHmacSha512_256Algorithm_default;
+
+static const BYTE hmacSha512_256Kat[32] = {
+    0x79, 0x44, 0xb9, 0x97, 0xc0, 0xaa, 0xf7, 0x11,
+    0xdd, 0xb3, 0x78, 0x60, 0x68, 0xdb, 0x2b, 0xa1,
+    0x40, 0x80, 0x4f, 0xdc, 0xb7, 0x02, 0x7b, 0x6a,
+    0xe9, 0x9f, 0x5a, 0x38, 0xc8, 0x28, 0x67, 0x4c
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHmacSha512_256Selftest(void)
+{
+    SYMCRYPT_HMAC_SHA512_256_EXPANDED_KEY xKey;
+    BYTE res[SYMCRYPT_HMAC_SHA512_256_RESULT_SIZE];
+
+    SymCryptHmacSha512_256ExpandKey( &xKey, SymCryptTestKey32, 16 );
+    SymCryptHmacSha512_256( &xKey, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, hmacSha512_256Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'hsh4' );
+    }
+
+    //
+    // Normally we would wipe the expanded key structure here,
+    // but as this is a selftest with known data this is not needed.
+    //
+}
diff --git a/libs/symcrypt/lib/i386/aesasm.S b/libs/symcrypt/lib/i386/aesasm.S
new file mode 100644
index 000000000000..ac70baa6dc49
--- /dev/null
+++ b/libs/symcrypt/lib/i386/aesasm.S
@@ -0,0 +1,1053 @@
+//
+//  AesAsm.cppasm   Assembler code for fast AES on the x86
+//  Requires C preprocessor to correctly include C_asm_shared.inc and symcrypt_magic.inc
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This code is derived from the AesFast implementation that
+// Niels Ferguson wrote from scratch for BitLocker during Vista.
+// That code is still in RSA32.
+//
+// Manually converted to GNU as format for Wine
+//
+
+#include "symcryptasm_shared.cppasm"
+
+//
+// Structure definition that mirrors the SYMCRYPT_AES_EXPANDED_KEY structure.
+//
+
+#define N_ROUND_KEYS_IN_AESKEY 29
+
+        .set SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey, 16*N_ROUND_KEYS_IN_AESKEY
+        .set SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey, 16*N_ROUND_KEYS_IN_AESKEY + 4
+
+
+//=====================================================================
+//       AesEncrypt & AesDecrypt Macros
+//
+
+// Shorthand for the 4 tables we will use
+
+
+#define SMM0     _SymCryptAesSboxMatrixMult
+#define SMM1     _SymCryptAesSboxMatrixMult + 0x0400
+#define SMM2     _SymCryptAesSboxMatrixMult + 0x0800
+#define SMM3     _SymCryptAesSboxMatrixMult + 0x0c00
+
+#define ISMM0     _SymCryptAesInvSboxMatrixMult
+#define ISMM1     _SymCryptAesInvSboxMatrixMult + 0x0400
+#define ISMM2     _SymCryptAesInvSboxMatrixMult + 0x0800
+#define ISMM3     _SymCryptAesInvSboxMatrixMult + 0x0c00
+
+
+        .macro ENC_MIX   load_into_esi
+        //
+        // Perform the unkeyed mixing function for encryption
+        // load_into_esi is the value loaded into esi by the end
+        //
+        // input block is in      eax, ebx, edx, ebp
+        // New state ends up in   ecx, edi, eax, ebp
+
+        push    ebp
+
+        movzx   ecx,al
+        mov     ecx,[SMM0 + 4 * ecx]
+        movzx   esi,ah
+        shr     eax,16
+        mov     ebp,[SMM1 + 4 * esi]
+        movzx   edi,ah
+        mov     edi,[SMM3 + 4 * edi]
+        movzx   eax,al
+        mov     eax,[SMM2 + 4 * eax]
+
+        movzx   esi,bl
+        xor     edi,[SMM0 + 4 * esi]
+        movzx   esi,bh
+        shr     ebx,16
+        xor     ecx,[SMM1 + 4 * esi]
+        movzx   esi,bl
+        xor     ebp,[SMM2 + 4 * esi]
+        movzx   ebx,bh
+        xor     eax,[SMM3 + 4 * ebx]
+
+        pop     ebx
+
+        movzx   esi,dl
+        xor     eax,[SMM0 + 4 * esi]
+        movzx   esi,dh
+        xor     edi,[SMM1 + 4 * esi]
+        shr     edx,16
+        movzx   esi,dl
+        xor     ecx,[SMM2 + 4 * esi]
+        movzx   edx,dh
+        xor     ebp,[SMM3 + 4 * edx]
+
+        mov     esi,[\load_into_esi]
+
+        movzx   edx,bl
+        xor     ebp,[SMM0 + 4 * edx]
+        movzx   edx,bh
+        xor     eax,[SMM1 + 4 * edx]
+        shr     ebx,16
+        movzx   edx,bl
+        xor     edi,[SMM2 + 4 * edx]
+        movzx   ebx,bh
+        xor     ecx,[SMM3 + 4 * ebx]
+
+        .endm
+
+
+        .macro DEC_MIX   load_into_esi
+        //
+        // Perform the unkeyed mixing function for decryption
+        // load_into_esi is the value loaded into esi by the end
+        //
+        // input block is in      eax, ebx, edx, ebp
+        // New state ends up in   ecx, edi, eax, ebp
+
+        push    ebp
+
+        movzx   ecx,al
+        mov     ecx,[ISMM0 + 4 * ecx]
+        movzx   esi,ah
+        shr     eax,16
+        mov     edi,[ISMM1 + 4 * esi]
+        movzx   ebp,ah
+        mov     ebp,[ISMM3 + 4 * ebp]
+        movzx   eax,al
+        mov     eax,[ISMM2 + 4 * eax]
+
+        movzx   esi,bl
+        xor     edi,[ISMM0 + 4 * esi]
+        movzx   esi,bh
+        shr     ebx,16
+        xor     eax,[ISMM1 + 4 * esi]
+        movzx   esi,bl
+        xor     ebp,[ISMM2 + 4 * esi]
+        movzx   ebx,bh
+        xor     ecx,[ISMM3 + 4 * ebx]
+
+        pop     ebx
+
+        movzx   esi,dl
+        xor     eax,[ISMM0 + 4 * esi]
+        movzx   esi,dh
+        xor     ebp,[ISMM1 + 4 * esi]
+        shr     edx,16
+        movzx   esi,dl
+        xor     ecx,[ISMM2 + 4 * esi]
+        movzx   edx,dh
+        xor     edi,[ISMM3 + 4 * edx]
+
+        mov     esi,[\load_into_esi]
+
+        movzx   edx,bl
+        xor     ebp,[ISMM0 + 4 * edx]
+        movzx   edx,bh
+        xor     ecx,[ISMM1 + 4 * edx]
+        shr     ebx,16
+        movzx   edx,bl
+        xor     edi,[ISMM2 + 4 * edx]
+        movzx   ebx,bh
+        xor     eax,[ISMM3 + 4 * ebx]
+
+        .endm
+
+
+        .macro ADD_KEY   keyptr
+        //
+        // Input is block in     ecx, edi, eax, ebp
+        // keyptr points to the round key
+        // Output is in          eax, ebx, edx, ebp
+        //
+
+        mov     edx, [\keyptr+8]
+        xor     edx, eax
+        mov     eax, [\keyptr]
+        xor     eax, ecx
+        xor     ebp, [\keyptr+12]
+        mov     ebx, [\keyptr+4]
+        xor     ebx, edi
+
+        .endm
+
+
+
+        .macro AES_ENC   load_into_esi_offset, key_limit, key_ptr
+        //
+        // Plaintext in eax,ebx,edx,ebp
+        // ecx points to first round key to use
+        // [key_limit] is last key to use
+        // [key_ptr] is temp storage area on stack
+        //
+        // Ciphertext ends up in ecx,edi,eax,ebp
+        //
+        // Loads [esp+load_into_esi_offset] into esi, except of offset = -1
+        //
+
+        xor     eax,[ecx]
+        xor     ebx,[ecx+4]
+        xor     edx,[ecx+8]
+        xor     ebp,[ecx+12]
+
+        lea     ecx,[ecx+16]
+        mov     [\key_ptr],ecx
+
+        .balign   16
+1:
+        // Block is in eax,ebx,edx,ebp
+        // ecx points to next round key
+
+        ENC_MIX \key_ptr
+        ADD_KEY esi
+
+        add     esi,16
+        cmp     esi,[\key_limit]
+
+        mov     [\key_ptr],esi
+
+        jc      1b
+
+        push    ebp
+
+        movzx   ecx,al
+        movzx   ecx,byte ptr SMM0[1 + 4 * ecx]
+        movzx   esi,ah
+        shr     eax,16
+        movzx   ebp,byte ptr SMM0[1 + 4 * esi]
+        movzx   edi,ah
+        shl     ebp,8
+        movzx   edi,byte ptr SMM0[1 + 4 * edi]
+        movzx   eax,al
+        shl     edi,24
+        movzx   eax,byte ptr SMM0[1 + 4 * eax]
+        shl     eax,16
+
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        or      edi,esi
+        movzx   esi,bh
+        shr     ebx,16
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        shl     esi,8
+        or      ecx,esi
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   ebx,bh
+        shl     esi,16
+        movzx   ebx,byte ptr SMM0[1 + 4 * ebx]
+        or      ebp,esi
+        shl     ebx,24
+        or      eax,ebx
+
+        movzx   esi,dl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   ebx,dh
+        shr     edx,16
+        movzx   ebx,byte ptr SMM0[1 + 4 * ebx]
+        or      eax,esi
+        shl     ebx,8
+        movzx   esi,dl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        or      edi,ebx
+        pop     ebx
+        movzx   edx,dh
+        movzx   edx,byte ptr SMM0[1 + 4 * edx]
+        shl     esi,16
+        or      ecx,esi
+        shl     edx,24
+        or      ebp,edx
+
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   edx,bh
+        shr     ebx,16
+        movzx   edx,byte ptr SMM0[1 + 4 * edx]
+        or      ebp,esi
+        shl     edx,8
+        or      eax,edx
+        mov     edx,[\key_ptr]
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   ebx,bh
+        movzx   ebx,byte ptr SMM0[1 + 4 * ebx]
+        shl     esi,16
+        shl     ebx,24
+        or      edi,esi
+
+        .ifgt      \load_into_esi_offset
+        mov     esi,[esp+\load_into_esi_offset]
+        .endif
+
+        or      ecx,ebx
+
+
+        xor     ecx,[edx]
+        xor     edi,[edx+4]
+        xor     eax,[edx+8]
+        xor     ebp,[edx+12]
+
+        .endm
+
+        .macro AES_DEC   load_into_esi_offset, key_limit, key_ptr
+        //
+        //
+        // Ciphertext in eax,ebx,edx,ebp
+        // ecx points to first round key to use
+        // [key_limit] is last key to use
+        // [key_ptr] is temp storage area on stack
+        //
+        // Plaintext ends up in eax,ebc,edx,ebp
+        //
+
+        xor     eax,[ecx]
+        xor     ebx,[ecx+4]
+        xor     edx,[ecx+8]
+        xor     ebp,[ecx+12]
+
+        lea     ecx,[ecx+16]
+        mov     [\key_ptr],ecx
+
+        .balign   16
+1:
+        // Block is in eax,ebx,edx,ebp
+        // ecx points to next round key
+
+        DEC_MIX \key_ptr
+        ADD_KEY esi
+
+        add     esi,16
+        cmp     esi,[\key_limit]
+
+        mov     [\key_ptr],esi
+
+        jc      1b
+
+
+        push    ebp
+
+        movzx   ecx,al
+        movzx   ecx,byte ptr _SymCryptAesInvSbox[ecx]
+        movzx   edi,ah
+        shr     eax,16
+        movzx   edi,byte ptr _SymCryptAesInvSbox[edi]
+        movzx   esi,ah
+        shl     edi,8
+        movzx   ebp,byte ptr _SymCryptAesInvSbox[esi]
+        movzx   eax,al
+        shl     ebp,24
+        movzx   eax,byte ptr _SymCryptAesInvSbox[eax]
+        shl     eax,16
+
+        movzx   esi,bl
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        or      edi,esi
+        movzx   esi,bh
+        shr     ebx,16
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        shl     esi,8
+        or      eax,esi
+        movzx   esi,bl
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        movzx   ebx,bh
+        shl     esi,16
+        movzx   ebx,byte ptr _SymCryptAesInvSbox[ebx]
+        or      ebp,esi
+        shl     ebx,24
+        or      ecx,ebx
+
+        movzx   esi,dl
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        movzx   ebx,dh
+        shr     edx,16
+        movzx   ebx,byte ptr _SymCryptAesInvSbox[ebx]
+        or      eax,esi
+        shl     ebx,8
+        movzx   esi,dl
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        or      ebp,ebx
+        pop     ebx
+        movzx   edx,dh
+        movzx   edx,byte ptr _SymCryptAesInvSbox[edx]
+        shl     esi,16
+        or      ecx,esi
+        shl     edx,24
+        or      edi,edx
+
+        movzx   esi,bl
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        movzx   edx,bh
+        shr     ebx,16
+        movzx   edx,byte ptr _SymCryptAesInvSbox[edx]
+        or      ebp,esi
+        shl     edx,8
+        or      ecx,edx
+        mov     edx,[\key_ptr]
+        movzx   esi,bl
+        movzx   esi,byte ptr _SymCryptAesInvSbox[esi]
+        movzx   ebx,bh
+        movzx   ebx,byte ptr _SymCryptAesInvSbox[ebx]
+        shl     esi,16
+        shl     ebx,24
+        or      edi,esi
+
+        .ifge      \load_into_esi_offset
+        mov     esi,[esp+\load_into_esi_offset]
+        .endif
+
+        or      eax,ebx
+
+        xor     ecx,[edx]
+        xor     edi,[edx+4]
+        xor     eax,[edx+8]
+        xor     ebp,[edx+12]
+
+        .endm
+
+        .globl @SymCryptAesEncryptAsm@12
+        .def @SymCryptAesEncryptAsm@12
+        .scl 2
+        .type 32
+        .endef
+@SymCryptAesEncryptAsm@12:
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesEncrypt( _In_                                   PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//                    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )  PCBYTE                      pbPlaintext,
+//                    _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbCiphertext );
+
+        // stack frame
+        .set AesEncryptFrame_key_pointer,   0
+        .set AesEncryptFrame_lastRoundKey,  4
+        .set AesEncryptFrame_SaveEdi,       8
+        .set AesEncryptFrame_SaveEsi,       12
+        .set AesEncryptFrame_SaveEbp,       16
+        .set AesEncryptFrame_SaveEbx,       20
+        .set AesEncryptFrame_ReturnAddress, 24
+        .set AesEncryptFrame_Ciphertext,    28
+
+        // ecx = AesKey
+        // edx = Plaintext
+        // [esp+4] = Ciphertext
+
+        //
+        // 2-byte NOP for hot patching
+        // This is what our current compiler does for every function, so we will follow
+        // that.
+        //
+        mov     edi,edi
+
+        //
+        // Set up our stack frame
+        //
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+        sub     esp, 8
+
+        // Load the plaintext block into eax,ebx,edx,ebp
+
+        mov     eax,[edx]
+        mov     ebx,[edx+4]
+        mov     ebp,[edx+12]
+        mov     edx,[edx+8]     // Plaintext ptr no longer needed
+
+        // Get the address of the last round key
+        mov     esi,[ecx+SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]
+        mov     [esp+AesEncryptFrame_lastRoundKey],esi
+
+        AES_ENC AesEncryptFrame_Ciphertext, esp + AesEncryptFrame_lastRoundKey, esp + AesEncryptFrame_key_pointer
+//       call    @SymCryptAesEncryptAsmInternal@16
+
+        //mov     esi,[esp+AesEncryptFrame_Ciphertext]
+        mov     [esi],ecx
+        mov     [esi+4],edi
+        mov     [esi+8],eax
+        mov     [esi+12],ebp
+
+        // wipe the stack location we used for temporary pushes
+
+        mov     [esp-8],esp
+
+        add     esp,8
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     4
+
+
+        .globl @SymCryptAesDecryptAsm@12
+        .def @SymCryptAesDecryptAsm@12
+        .scl 2
+        .type 32
+        .endef
+@SymCryptAesDecryptAsm@12:
+//VOID
+//AesDecrypt( _In_                            PCADD_KEY   AesKey,
+//            _In_reads_bytes_(AES_BLOCK_SIZE)     PCBYTE      Ciphertext,
+//            _Out_writes_bytes_(AES_BLOCK_SIZE)    PBYTE       Plaintext
+//            )
+
+        // stack frame
+        .set AesDecryptFrame_key_pointer,   0
+        .set AesDecryptFrame_lastRoundKey,  4
+        .set AesDecryptFrame_SaveEdi,       8
+        .set AesDecryptFrame_SaveEsi,       12
+        .set AesDecryptFrame_SaveEbp,       16
+        .set AesDecryptFrame_SaveEbx,       20
+        .set AesDecryptFrame_ReturnAddress, 24
+        .set AesDecryptFrame_Plaintext,     28
+
+        // ecx = AesKey
+        // edx = Ciphertext
+        // [esp+4] = Plaintext
+
+        //
+        // 2-byte NOP for hot patching
+        // This is what our current compiler does for every function, so we will follow
+        // that.
+        //
+        mov     edi,edi
+
+        //
+        // Set up our stack frame
+        //
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+        sub     esp, 8
+
+        // Load the ciphertext block into eax,ebx,edx,ebp
+
+        mov     eax,[edx]
+        mov     ebx,[edx+4]
+        mov     ebp,[edx+12]
+        mov     edx,[edx+8]     // Ciphertext ptr no longer needed
+
+
+        // Get the address of the last round key
+        mov     esi,[ecx+SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey]       // esi = lastDecRoundKey
+        mov     ecx,[ecx+SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]       // ecx = lastEncRoundKey = first Dec round key
+        mov     [esp+AesDecryptFrame_lastRoundKey],esi
+
+        AES_DEC AesDecryptFrame_Plaintext, esp + AesDecryptFrame_lastRoundKey, esp + AesDecryptFrame_key_pointer
+
+
+        mov     [esi],ecx
+        mov     [esi+4],edi
+        mov     [esi+8],eax
+        mov     [esi+12],ebp
+
+        mov     [esp-8],esp
+
+        add     esp,8
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     4
+
+
+
+
+        .globl @SymCryptAesCbcEncryptAsm@20
+        .def @SymCryptAesCbcEncryptAsm@20
+        .scl 2
+        .type 32
+        .endef
+@SymCryptAesCbcEncryptAsm@20:
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCbcEncrypt(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData );
+//
+
+        // stack frame
+        .set AesCbcEncryptFrame_key_pointer,     0
+        .set AesCbcEncryptFrame_lastRoundKey,    4
+        .set AesCbcEncryptFrame_pbEndDst,        8
+        .set AesCbcEncryptFrame_firstRoundKey,   12
+        .set AesCbcEncryptFrame_pbChainingValue, 16
+        .set AesCbcEncryptFrame_SaveEdi,         20
+        .set AesCbcEncryptFrame_SaveEsi,         24
+        .set AesCbcEncryptFrame_SaveEbp,         28
+        .set AesCbcEncryptFrame_SaveEbx,         32
+        .set AesCbcEncryptFrame_ReturnAddress,   36
+        .set AesCbcEncryptFrame_pbSrc,           40
+        .set AesCbcEncryptFrame_pbDst,           44
+        .set AesCbcEncryptFrame_cbData,          48
+
+        // ecx = pExpandedKey
+        // edx = pbChainingValue
+        // [esp+4...] = pbSrc, pbDst, cbData
+
+        //
+        // 2-byte NOP for hot patching
+        // This is what our current compiler does for every function, so we will follow
+        // that.
+        //
+        mov     edi,edi
+
+        //
+        // Set up our stack frame
+        //
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             // pbChainingValue
+        push    ecx             // pbExpandedKey points to the first round key
+        sub     esp, 12
+
+        mov     eax,[esp + AesCbcEncryptFrame_cbData]
+        and     eax, NOT 15
+        jz      AesCbcEncryptDoNothing
+
+        // Get & store the address of the last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]
+        mov     [esp+AesCbcEncryptFrame_lastRoundKey],ebx
+
+
+        mov     esi,[esp + AesCbcEncryptFrame_pbDst]
+        add     eax,esi
+        mov     [esp + AesCbcEncryptFrame_pbEndDst], eax
+
+        //
+        // Convert pbSrc to an offset from pbDst to reduce the # updates
+        // in a loop
+        //
+        //mov    ecx,[esp + AesCbcEncryptFrame_pbSrc]
+        //sub    ecx, esi
+        //mov    [esp + AesCbcEncryptFrame_pbSrc], ecx
+
+        // esi = pbDst
+
+        // Load state from chaining value
+        // State is in ecx,edi,eax,ebp
+        mov     ecx,[edx]
+        mov     edi,[edx + 4]
+        mov     eax,[edx + 8]
+        mov     ebp,[edx + 12]
+
+
+AesCbcEncryptLoop:
+        // Invariant:
+        //  State in (ecx, edi, eax, ebp)
+        //  esi = pbDst for next block
+
+        //
+        // Change esi to point to current pbSrc pointer. pbSrc is here an offset from pbDst.
+        //
+        mov     esi,[esp + AesCbcEncryptFrame_pbSrc]
+
+        //
+        // Xor next plaintext block & move state to (eax, ebx, edx, ebp)
+        // We keep the reads sequentially to help the HW prefetch logic in the CPU
+        //
+        xor     ecx, [esi]
+
+        mov     ebx, [esi + 4]
+        xor     ebx, edi
+
+        mov     edx, [esi + 8]
+        xor     edx, eax
+
+        mov     eax, ecx
+
+        xor     ebp, [esi + 12]
+
+        add     esi, 16
+        mov     [esp + AesCbcEncryptFrame_pbSrc], esi
+
+        mov     ecx,[esp + AesCbcEncryptFrame_firstRoundKey]
+
+
+        AES_ENC AesCbcEncryptFrame_pbDst, esp + AesCbcEncryptFrame_lastRoundKey, esp + AesCbcEncryptFrame_key_pointer
+
+        //call   @SymCryptAesEncryptAsmInternal@16
+        //mov    esi,[esp + AesCbcEncryptFrame_pbDst]
+
+        mov     [esi], ecx
+        mov     [esi + 4], edi
+        mov     [esi + 8], eax
+        mov     [esi + 12], ebp
+
+        add     esi,16
+        cmp     esi,[esp + AesCbcEncryptFrame_pbEndDst]
+        mov     [esp + AesCbcEncryptFrame_pbDst], esi
+        jb      AesCbcEncryptLoop
+
+        mov     edx,[esp + AesCbcEncryptFrame_pbChainingValue]
+        mov     [edx], ecx
+        mov     [edx + 4], edi
+        mov     [edx + 8], eax
+        mov     [edx + 12], ebp
+
+        //
+        // Wipe the one stack location that the internal encrypt routine uses
+        // for temporary data storage
+        //
+        mov     [esp - 8], esp
+
+AesCbcEncryptDoNothing:
+
+        add     esp, 20
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     12
+
+
+
+
+        .globl @SymCryptAesCbcDecryptAsm@20
+        .def @SymCryptAesCbcDecryptAsm@20
+        .scl 2
+        .type 32
+        .endef
+@SymCryptAesCbcDecryptAsm@20:
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCbcDecrypt(
+//    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+//    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+//    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+//                                            SIZE_T                      cbData );
+//
+
+        // stack frame
+        .set AesCbcDecryptFrame_key_pointer,     0
+        .set AesCbcDecryptFrame_lastRoundKey,    4
+        .set AesCbcDecryptFrame_newChainValue,   8     // New chaining value after the call
+        .set AesCbcDecryptFrame_pbCurrentDst,    24
+        .set AesCbcDecryptFrame_firstRoundKey,   28
+        .set AesCbcDecryptFrame_pbChainingValue, 32
+        .set AesCbcDecryptFrame_SaveEdi,         36
+        .set AesCbcDecryptFrame_SaveEsi,         40
+        .set AesCbcDecryptFrame_SaveEbp,         44
+        .set AesCbcDecryptFrame_SaveEbx,         48
+        .set AesCbcDecryptFrame_ReturnAddress,   52
+        .set AesCbcDecryptFrame_pbSrc,           56
+        .set AesCbcDecryptFrame_pbDst,           60
+        .set AesCbcDecryptFrame_cbData,          64
+
+        // ecx = pExpandedKey
+        // edx = pbChainingValue
+        // [esp+4...] = pbSrc, pbDst, cbData
+
+        //
+        // 2-byte NOP for hot patching
+        // This is what our current compiler does for every function, so we will follow
+        // that.
+        //
+        mov     edi,edi
+
+        //
+        // Set up our stack frame
+        //
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             // pbChainingValue
+        sub     esp, 32
+
+        mov     eax,[esp + AesCbcDecryptFrame_cbData]
+        and     eax, NOT 15
+        jz      AesCbcDecryptDoNothing
+
+        // Get & store the address of the first & last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY_lastDecRoundKey]
+        mov     [esp+AesCbcDecryptFrame_lastRoundKey],ebx
+
+        sub     eax, 16
+
+        mov     ecx,[ecx + SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]           // = first dec round key
+        mov     [esp + AesCbcDecryptFrame_firstRoundKey], ecx
+
+        mov     edi,[esp + AesCbcDecryptFrame_pbDst]
+        add     edi, eax
+        mov     [esp + AesCbcDecryptFrame_pbCurrentDst], edi    // points to last block in buffer
+
+        mov     esi,[esp + AesCbcDecryptFrame_pbSrc]
+        add     esi, eax
+        mov     [esp + AesCbcDecryptFrame_pbSrc], esi
+
+        //
+        // Load last block & store it for the chaining output
+        //
+        mov     eax,[esi]
+        mov     [esp + AesCbcDecryptFrame_newChainValue], eax
+        mov     ebx,[esi + 4]
+        mov     [esp + AesCbcDecryptFrame_newChainValue + 4], ebx
+        mov     edx,[esi + 8]
+        mov     [esp + AesCbcDecryptFrame_newChainValue + 8], edx
+        mov     ebp,[esi + 12]
+        mov     [esp + AesCbcDecryptFrame_newChainValue + 12], ebp
+
+        jmp     AesCbcDecryptLoopEnter
+
+AesCbcDecryptLoop:
+        // Invariant:
+        //  State in (ecx, edi, eax, ebp)
+        //  State is just after decrypt, but before feed-forward xor
+        //  esi = pbCurrentDst for just decrypted state
+        //  pbSrc = corresponding position in Src buffer
+
+        mov     ebx,[esp + AesCbcDecryptFrame_pbSrc]
+
+        mov     edx,[ebx - 16 + 8]
+        xor     eax, edx
+        mov     [esi + 16 + 8], eax
+
+        mov     eax,[ebx - 16 + 0]
+        xor     ecx, eax
+        mov     [esi + 16 + 0], ecx
+
+        lea     ecx, [ebx - 16]         // decrement + move ptr to allow register shuffle
+
+        mov     ebx,[ebx - 16 + 4]
+        xor     edi, ebx
+        mov     [esi + 16 + 4], edi
+
+        mov     edi,[ecx + 12]
+        xor     ebp, edi
+        mov     [esi + 16 + 12], ebp
+        mov     ebp, edi
+
+        mov     [esp + AesCbcDecryptFrame_pbSrc], ecx
+
+        mov     ecx,[esp + AesCbcDecryptFrame_firstRoundKey]
+
+AesCbcDecryptLoopEnter:
+
+        AES_DEC AesCbcDecryptFrame_pbCurrentDst, esp + AesCbcDecryptFrame_lastRoundKey, esp + AesCbcDecryptFrame_key_pointer
+        //call   @SymCryptAesDecryptAsmInternal@16
+        //mov    esi,[esp + AesCbcDecryptFrame_pbCurrentDst]
+
+        cmp     esi,[esp + AesCbcDecryptFrame_pbDst]
+        lea     esi,[esi-16]
+        mov     [esp + AesCbcDecryptFrame_pbCurrentDst], esi
+
+        ja      AesCbcDecryptLoop
+
+        //
+        // Xor with chaining value and store last block (first in output buffer)
+        //
+
+        mov     ebx,[esp + AesCbcDecryptFrame_pbChainingValue]
+        xor     ecx,[ebx]
+        mov     [esi + 16], ecx
+        xor     edi,[ebx + 4]
+        mov     [esi + 16 + 4], edi
+        xor     eax,[ebx + 8]
+        mov     [esi + 16 + 8], eax
+        xor     ebp,[ebx + 12]
+        mov     [esi + 16+12], ebp
+
+        //
+        // Set the new chaining value
+        //
+
+        mov     eax,[esp + AesCbcDecryptFrame_newChainValue + 0]
+        mov     [ebx + 0], eax
+        mov     ecx,[esp + AesCbcDecryptFrame_newChainValue + 4]
+        mov     [ebx + 4], ecx
+        mov     eax,[esp + AesCbcDecryptFrame_newChainValue + 8]
+        mov     [ebx + 8], eax
+        mov     ecx,[esp + AesCbcDecryptFrame_newChainValue + 12]
+        mov     [ebx + 12], ecx
+
+
+        // Wipe the one stack location that the internal encrypt routine uses
+        // for temporary data storage
+        //
+        mov     [esp - 8], esp
+
+AesCbcDecryptDoNothing:
+
+        add     esp, 36
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     12
+
+
+
+        .globl @SymCryptAesCtrMsb64Asm@20
+        .def @SymCryptAesCtrMsb64Asm@20
+        .scl 2
+        .type 32
+        .endef
+@SymCryptAesCtrMsb64Asm@20:
+//VOID
+//SYMCRYPT_CALL
+//SymCryptAesCtrMsb64(
+//        _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+//        _Inout_updates_bytes_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+//        _In_reads_bytes_( cbData )                       PCBYTE                      pbSrc,
+//        _Out_writes_bytes_( cbData )                      PBYTE                       pbDst,
+//                                                    SIZE_T                      cbData )
+
+        // stack frame
+        .set AesCtrMsb64Frame_key_pointer,        0
+        .set AesCtrMsb64Frame_lastRoundKey,       4
+        .set AesCtrMsb64Frame_chainValue,         8
+        .set AesCtrMsb64Frame_pbEndDst,           24
+        .set AesCtrMsb64Frame_firstRoundKey,      28
+        .set AesCtrMsb64Frame_pbChainingValue,    32
+        .set AesCtrMsb64Frame_SaveEdi,            36
+        .set AesCtrMsb64Frame_SaveEsi,            40
+        .set AesCtrMsb64Frame_SaveEbp,            44
+        .set AesCtrMsb64Frame_SaveEbx,            48
+        .set AesCtrMsb64Frame_ReturnAddress,      52
+        .set AesCtrMsb64Frame_pbSrc,              56
+        .set AesCtrMsb64Frame_pbDst,              60
+        .set AesCtrMsb64Frame_cbData,             64
+
+        // ecx = pExpandedKey
+        // edx = pbChainingValue
+        // [esp+4...] = pbSrc, pbDst, cbData
+
+        //
+        // 2-byte NOP for hot patching
+        // This is what our current compiler does for every function, so we will follow
+        // that.
+        //
+        mov     edi,edi
+
+        //
+        // Set up our stack frame
+        //
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             // pbChainingValue
+        push    ecx             // pbExpandedKey points to the first round key
+        sub     esp, 28
+
+        mov     eax,[esp + AesCtrMsb64Frame_cbData]
+        and     eax, NOT 15
+        jz      AesCtrMsb64DoNothing
+
+        // Get & store the address of the last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY_lastEncRoundKey]
+        mov     [esp+AesCtrMsb64Frame_lastRoundKey],ebx
+
+
+        mov     esi,[esp + AesCtrMsb64Frame_pbDst]
+        add     eax,esi
+        mov     [esp + AesCtrMsb64Frame_pbEndDst], eax
+
+        // esi = pbDst
+
+        // Load state from chaining value & copy into local stack
+        // State is in ecx,edi,eax,ebp
+        mov     eax,[edx]
+        mov     [esp + AesCtrMsb64Frame_chainValue], eax
+        mov     ebx,[edx + 4]
+        mov     [esp + AesCtrMsb64Frame_chainValue + 4], ebx
+        mov     ebp,[edx + 12]
+        mov     edx,[edx + 8]
+
+
+AesCtrMsb64Loop:
+        // Invariant:
+        //  counter State in (eax, ebx, edx, ebp)
+        //  first 8 bytes of counter state in [esp+chainValue]
+
+        //
+        // Write the updated counter value to the local copy
+        //
+        mov     [esp + AesCtrMsb64Frame_chainValue + 8], edx
+        mov     ecx,[esp + AesCtrMsb64Frame_firstRoundKey]
+        mov     [esp + AesCtrMsb64Frame_chainValue + 12], ebp
+
+        AES_ENC AesCtrMsb64Frame_pbSrc, esp + AesCtrMsb64Frame_lastRoundKey, esp + AesCtrMsb64Frame_key_pointer
+        //       result is in (ecx, edi, eax, ebp)
+        //       esi = pbSrc
+
+        mov     ebx,[esp + AesCtrMsb64Frame_pbDst]
+
+        //
+        // Read the Src block, xor it with the state, and write it to Dst
+        //
+        xor     ecx,[esi]
+        mov     [ebx],ecx
+        xor     edi,[esi+4]
+        mov     [ebx+4],edi
+        xor     eax,[esi+8]
+        mov     [ebx+8],eax
+        xor     ebp,[esi+12]
+        mov     [ebx+12],ebp
+
+        add     esi, 16
+        mov     [esp + AesCtrMsb64Frame_pbSrc], esi
+
+        lea     ecx,[ebx + 16]                  // increment and move pointer to allow reg shuffle
+
+        //
+        // Load the current chain value, and do the increment
+        //
+        mov     ebp,[esp + AesCtrMsb64Frame_chainValue+12]
+        bswap   ebp
+        mov     edx,[esp + AesCtrMsb64Frame_chainValue+8]
+        bswap   edx
+        add     ebp,1
+        mov     eax,[esp + AesCtrMsb64Frame_chainValue]
+        adc     edx, 0
+        mov     ebx,[esp + AesCtrMsb64Frame_chainValue+4]
+        bswap   ebp
+        bswap   edx
+
+
+        cmp     ecx,[esp + AesCtrMsb64Frame_pbEndDst]
+        mov     [esp + AesCtrMsb64Frame_pbDst], ecx
+
+        jb      AesCtrMsb64Loop
+
+        //
+        // Write the updated chaining value; we only need to update 8 bytes
+        //
+        mov     ebx,[esp + AesCtrMsb64Frame_pbChainingValue]
+        mov     [ebx + 8], edx
+        mov     [ebx + 12], ebp
+
+        //
+        // Wipe our local copy of the chaining value
+        //
+        xor     eax,eax
+        mov     [esp + AesCtrMsb64Frame_chainValue], eax
+        mov     [esp + AesCtrMsb64Frame_chainValue + 4], eax
+        mov     [esp + AesCtrMsb64Frame_chainValue + 8], eax
+        mov     [esp + AesCtrMsb64Frame_chainValue + 12], eax
+
+        //
+        // Wipe the one stack location that the internal encrypt routine uses
+        // for temporary data storage
+        //
+        mov     [esp - 8], esp
+
+AesCtrMsb64DoNothing:
+
+        add     esp, 36
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     12
diff --git a/libs/symcrypt/lib/i386/aesasm.cppasm b/libs/symcrypt/lib/i386/aesasm.cppasm
new file mode 100644
index 000000000000..6e087ebcc11e
--- /dev/null
+++ b/libs/symcrypt/lib/i386/aesasm.cppasm
@@ -0,0 +1,1829 @@
+;
+;  AesAsm.cppasm   Assembler code for fast AES on the x86
+;  Requires C preprocessor to correctly include C_asm_shared.inc and symcrypt_magic.inc
+;
+; Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+;
+; This code is derived from the AesFast implementation that
+; Niels Ferguson wrote from scratch for BitLocker during Vista.
+; That code is still in RSA32.
+;
+
+
+
+        TITLE   "Advanced Encryption Standard (AES)"
+        .586P
+        .XMM
+
+;
+; FPO documentation:
+; The .FPO provides debugging information.
+; This stuff not well documented,
+; but here is the information I've gathered about the arguments to .FPO
+;
+; In order:
+; cdwLocals: Size of local variables, in DWords
+; cdwParams: Size of parameters, in DWords. Given that this is all about
+;            stack stuff, I'm assuming this is only about parameters passed
+;            on the stack.
+; cbProlog : Number of bytes in the prolog code. We have interleaved the
+;            prolog code with work for better performance. Most uses of
+;            .FPO seem to set this value to 0 anyway, which is what we
+;            will do.
+; cbRegs   : # registers saved in the prolog.
+; fUseBP   : 0 if EBP is not used as base pointer, 1 if EBP is used as base pointer
+; cbFrame  : Type of frame.
+;            0 = FPO frame (no frame pointer)
+;            1 = Trap frame (result of a CPU trap event)
+;            2 = TSS frame
+;
+; Having looked at various occurrences of .FPO in the Windows code it
+; seems to be used fairly sloppy, with lots of arguments left 0 even when
+; they probably shouldn't be according to the spec.
+;
+
+
+
+_TEXT   SEGMENT PARA PUBLIC USE32 'CODE'
+        ASSUME  CS:_TEXT, DS:FLAT, SS:FLAT
+
+#include "C_asm_shared.inc"
+#include "symcrypt_magic.inc"
+
+;
+; Structure definition that mirrors the SYMCRYPT_AES_EXPANDED_KEY structure.
+;
+
+N_ROUND_KEYS_IN_AESKEY    EQU     29
+
+SYMCRYPT_AES_EXPANDED_KEY struct
+        RoundKey        db      16*N_ROUND_KEYS_IN_AESKEY dup (?)
+        lastEncRoundKey dd      ?
+        lastDecRoundKey dd      ?
+
+        SYMCRYPT_MAGIC_FIELD
+
+SYMCRYPT_AES_EXPANDED_KEY ends
+
+
+
+        PUBLIC  @SymCryptAesEncryptAsm@12
+;        PUBLIC  @SymCryptAesEncryptXmm@12
+
+        PUBLIC  @SymCryptAesDecryptAsm@12
+;        PUBLIC  @SymCryptAesDecryptXmm@12
+
+        PUBLIC  @SymCryptAesCbcEncryptAsm@20
+;        PUBLIC  @SymCryptAesCbcEncryptXmm@20
+
+        PUBLIC  @SymCryptAesCbcDecryptAsm@20
+        ;PUBLIC  @SymCryptAesCbcDecryptXmm@20
+
+        ;PUBLIC @SymCryptAesCbcMacAsm@16
+        PUBLIC @SymCryptAesCtrMsb64Asm@20
+
+        ;PUBLIC  @SymCryptAes4SboxXmm@8
+        ;PUBLIC  @SymCryptAesCreateDecryptionRoundKeyXmm@8
+
+;        PUBLIC  @SymCryptAesEncryptAsmInternal@16       ; Not needed, but makes debugging easier
+;        PUBLIC  @SymCryptAesDecryptAsmInternal@16       ; Not needed, but makes debugging easier
+
+        EXTRN   _SymCryptAesSboxMatrixMult:DWORD
+        EXTRN   _SymCryptAesInvSboxMatrixMult:DWORD
+        EXTRN   _SymCryptAesInvSbox:BYTE
+
+
+BEFORE_PROC     MACRO
+        ;
+        ; Our current x86 compiler inserts 5 0xcc bytes before every function
+        ; and starts every function with a 2-byte NOP.
+        ; This supports hot-patching.
+        ;
+        DB      5 dup (0cch)
+                ENDM
+
+
+;=====================================================================
+;       AesEncrypt & AesDecrypt Macros
+;
+
+; Shorthand for the 4 tables we will use
+
+
+SMM0    EQU     _SymCryptAesSboxMatrixMult
+SMM1    EQU     _SymCryptAesSboxMatrixMult + 0400h
+SMM2    EQU     _SymCryptAesSboxMatrixMult + 0800h
+SMM3    EQU     _SymCryptAesSboxMatrixMult + 0c00h
+
+
+ISMM0    EQU     _SymCryptAesInvSboxMatrixMult
+ISMM1    EQU     _SymCryptAesInvSboxMatrixMult + 0400h
+ISMM2    EQU     _SymCryptAesInvSboxMatrixMult + 0800h
+ISMM3    EQU     _SymCryptAesInvSboxMatrixMult + 0c00h
+
+
+ENC_MIX MACRO   load_into_esi
+        ;
+        ; Perform the unkeyed mixing function for encryption
+        ; load_into_esi is the value loaded into esi by the end
+        ;
+        ; input block is in      eax, ebx, edx, ebp
+        ; New state ends up in   ecx, edi, eax, ebp
+
+        push    ebp
+
+        movzx   ecx,al
+        mov     ecx,[SMM0 + 4 * ecx]
+        movzx   esi,ah
+        shr     eax,16
+        mov     ebp,[SMM1 + 4 * esi]
+        movzx   edi,ah
+        mov     edi,[SMM3 + 4 * edi]
+        movzx   eax,al
+        mov     eax,[SMM2 + 4 * eax]
+
+        movzx   esi,bl
+        xor     edi,[SMM0 + 4 * esi]
+        movzx   esi,bh
+        shr     ebx,16
+        xor     ecx,[SMM1 + 4 * esi]
+        movzx   esi,bl
+        xor     ebp,[SMM2 + 4 * esi]
+        movzx   ebx,bh
+        xor     eax,[SMM3 + 4 * ebx]
+
+        pop     ebx
+
+        movzx   esi,dl
+        xor     eax,[SMM0 + 4 * esi]
+        movzx   esi,dh
+        xor     edi,[SMM1 + 4 * esi]
+        shr     edx,16
+        movzx   esi,dl
+        xor     ecx,[SMM2 + 4 * esi]
+        movzx   edx,dh
+        xor     ebp,[SMM3 + 4 * edx]
+
+        mov     esi,[load_into_esi]
+
+        movzx   edx,bl
+        xor     ebp,[SMM0 + 4 * edx]
+        movzx   edx,bh
+        xor     eax,[SMM1 + 4 * edx]
+        shr     ebx,16
+        movzx   edx,bl
+        xor     edi,[SMM2 + 4 * edx]
+        movzx   ebx,bh
+        xor     ecx,[SMM3 + 4 * ebx]
+
+        ENDM
+
+
+DEC_MIX MACRO   load_into_esi
+        ;
+        ; Perform the unkeyed mixing function for decryption
+        ; load_into_esi is the value loaded into esi by the end
+        ;
+        ; input block is in      eax, ebx, edx, ebp
+        ; New state ends up in   ecx, edi, eax, ebp
+
+        push    ebp
+
+        movzx   ecx,al
+        mov     ecx,[ISMM0 + 4 * ecx]
+        movzx   esi,ah
+        shr     eax,16
+        mov     edi,[ISMM1 + 4 * esi]
+        movzx   ebp,ah
+        mov     ebp,[ISMM3 + 4 * ebp]
+        movzx   eax,al
+        mov     eax,[ISMM2 + 4 * eax]
+
+        movzx   esi,bl
+        xor     edi,[ISMM0 + 4 * esi]
+        movzx   esi,bh
+        shr     ebx,16
+        xor     eax,[ISMM1 + 4 * esi]
+        movzx   esi,bl
+        xor     ebp,[ISMM2 + 4 * esi]
+        movzx   ebx,bh
+        xor     ecx,[ISMM3 + 4 * ebx]
+
+        pop     ebx
+
+        movzx   esi,dl
+        xor     eax,[ISMM0 + 4 * esi]
+        movzx   esi,dh
+        xor     ebp,[ISMM1 + 4 * esi]
+        shr     edx,16
+        movzx   esi,dl
+        xor     ecx,[ISMM2 + 4 * esi]
+        movzx   edx,dh
+        xor     edi,[ISMM3 + 4 * edx]
+
+        mov     esi,[load_into_esi]
+
+        movzx   edx,bl
+        xor     ebp,[ISMM0 + 4 * edx]
+        movzx   edx,bh
+        xor     ecx,[ISMM1 + 4 * edx]
+        shr     ebx,16
+        movzx   edx,bl
+        xor     edi,[ISMM2 + 4 * edx]
+        movzx   ebx,bh
+        xor     eax,[ISMM3 + 4 * ebx]
+
+        ENDM
+
+
+ADD_KEY MACRO   keyptr
+        ;
+        ; Input is block in     ecx, edi, eax, ebp
+        ; keyptr points to the round key
+        ; Output is in          eax, ebx, edx, ebp
+        ;
+
+        mov     edx, [keyptr+8]
+        xor     edx, eax
+        mov     eax, [keyptr]
+        xor     eax, ecx
+        xor     ebp, [keyptr+12]
+        mov     ebx, [keyptr+4]
+        xor     ebx, edi
+
+        ENDM
+
+
+
+AES_ENC MACRO   load_into_esi_offset
+        ;
+        ; Plaintext in eax,ebx,edx,ebp
+        ; ecx points to first round key to use
+        ; [key_limit] is last key to use
+        ; [key_ptr] is temp storage area on stack
+        ;
+        ; Ciphertext ends up in ecx,edi,eax,ebp
+        ;
+        ; Loads [esp+load_into_esi_offset] into esi, except of offset = -1
+        ;
+
+        xor     eax,[ecx]
+        xor     ebx,[ecx+4]
+        xor     edx,[ecx+8]
+        xor     ebp,[ecx+12]
+
+        lea     ecx,[ecx+16]
+        mov     [key_ptr],ecx
+
+        align   16
+@@:
+        ; Block is in eax,ebx,edx,ebp
+        ; ecx points to next round key
+
+        ENC_MIX key_ptr
+        ADD_KEY esi
+
+        add     esi,16
+        cmp     esi,[key_limit]
+
+        mov     [key_ptr],esi
+
+        jc      @B
+
+        push    ebp
+
+        movzx   ecx,al
+        movzx   ecx,byte ptr SMM0[1 + 4 * ecx]
+        movzx   esi,ah
+        shr     eax,16
+        movzx   ebp,byte ptr SMM0[1 + 4 * esi]
+        movzx   edi,ah
+        shl     ebp,8
+        movzx   edi,byte ptr SMM0[1 + 4 * edi]
+        movzx   eax,al
+        shl     edi,24
+        movzx   eax,byte ptr SMM0[1 + 4 * eax]
+        shl     eax,16
+
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        or      edi,esi
+        movzx   esi,bh
+        shr     ebx,16
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        shl     esi,8
+        or      ecx,esi
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   ebx,bh
+        shl     esi,16
+        movzx   ebx,byte ptr SMM0[1 + 4 * ebx]
+        or      ebp,esi
+        shl     ebx,24
+        or      eax,ebx
+
+        movzx   esi,dl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   ebx,dh
+        shr     edx,16
+        movzx   ebx,byte ptr SMM0[1 + 4 * ebx]
+        or      eax,esi
+        shl     ebx,8
+        movzx   esi,dl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        or      edi,ebx
+        pop     ebx
+        movzx   edx,dh
+        movzx   edx,byte ptr SMM0[1 + 4 * edx]
+        shl     esi,16
+        or      ecx,esi
+        shl     edx,24
+        or      ebp,edx
+
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   edx,bh
+        shr     ebx,16
+        movzx   edx,byte ptr SMM0[1 + 4 * edx]
+        or      ebp,esi
+        shl     edx,8
+        or      eax,edx
+        mov     edx,[key_ptr]
+        movzx   esi,bl
+        movzx   esi,byte ptr SMM0[1 + 4 * esi]
+        movzx   ebx,bh
+        movzx   ebx,byte ptr SMM0[1 + 4 * ebx]
+        shl     esi,16
+        shl     ebx,24
+        or      edi,esi
+
+        IF      load_into_esi_offset NE -1
+        mov     esi,[esp+load_into_esi_offset]
+        ENDIF
+
+        or      ecx,ebx
+
+
+        xor     ecx,[edx]
+        xor     edi,[edx+4]
+        xor     eax,[edx+8]
+        xor     ebp,[edx+12]
+
+        ENDM
+
+AES_DEC MACRO   load_into_esi_offset
+        ;
+        ;
+        ; Ciphertext in eax,ebx,edx,ebp
+        ; ecx points to first round key to use
+        ; [key_limit] is last key to use
+        ; [key_ptr] is temp storage area on stack
+        ;
+        ; Plaintext ends up in eax,ebc,edx,ebp
+        ;
+
+        xor     eax,[ecx]
+        xor     ebx,[ecx+4]
+        xor     edx,[ecx+8]
+        xor     ebp,[ecx+12]
+
+        lea     ecx,[ecx+16]
+        mov     [key_ptr],ecx
+
+        align   16
+@@:
+        ; Block is in eax,ebx,edx,ebp
+        ; ecx points to next round key
+
+        DEC_MIX key_ptr
+        ADD_KEY esi
+
+        add     esi,16
+        cmp     esi,[key_limit]
+
+        mov     [key_ptr],esi
+
+        jc      @B
+
+
+        push    ebp
+
+        movzx   ecx,al
+        movzx   ecx,_SymCryptAesInvSbox[ecx]
+        movzx   edi,ah
+        shr     eax,16
+        movzx   edi,_SymCryptAesInvSbox[edi]
+        movzx   esi,ah
+        shl     edi,8
+        movzx   ebp,_SymCryptAesInvSbox[esi]
+        movzx   eax,al
+        shl     ebp,24
+        movzx   eax,_SymCryptAesInvSbox[eax]
+        shl     eax,16
+
+        movzx   esi,bl
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        or      edi,esi
+        movzx   esi,bh
+        shr     ebx,16
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        shl     esi,8
+        or      eax,esi
+        movzx   esi,bl
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        movzx   ebx,bh
+        shl     esi,16
+        movzx   ebx,_SymCryptAesInvSbox[ebx]
+        or      ebp,esi
+        shl     ebx,24
+        or      ecx,ebx
+
+        movzx   esi,dl
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        movzx   ebx,dh
+        shr     edx,16
+        movzx   ebx,_SymCryptAesInvSbox[ebx]
+        or      eax,esi
+        shl     ebx,8
+        movzx   esi,dl
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        or      ebp,ebx
+        pop     ebx
+        movzx   edx,dh
+        movzx   edx,_SymCryptAesInvSbox[edx]
+        shl     esi,16
+        or      ecx,esi
+        shl     edx,24
+        or      edi,edx
+
+        movzx   esi,bl
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        movzx   edx,bh
+        shr     ebx,16
+        movzx   edx,_SymCryptAesInvSbox[edx]
+        or      ebp,esi
+        shl     edx,8
+        or      ecx,edx
+        mov     edx,[key_ptr]
+        movzx   esi,bl
+        movzx   esi,_SymCryptAesInvSbox[esi]
+        movzx   ebx,bh
+        movzx   ebx,_SymCryptAesInvSbox[ebx]
+        shl     esi,16
+        shl     ebx,24
+        or      edi,esi
+
+        IF      load_into_esi_offset NE -1
+        mov     esi,[esp+load_into_esi_offset]
+        ENDIF
+
+        or      eax,ebx
+
+        xor     ecx,[edx]
+        xor     edi,[edx+4]
+        xor     eax,[edx+8]
+        xor     ebp,[edx+12]
+
+        ENDM
+
+
+        if      0       ; We use the macro throughout, no need for the internal Enc/Dec routines
+
+;=====================================================================
+;       Internal AesEncrypt & AesDecrypt
+;
+
+;
+; SymCryptAesEncryptAsmInternal
+;
+; Internal AES encryption routine with modified calling convention.
+; This is a bare-bones AES encryption routine which can only be called from assembler code
+; as the calling convention is modified.
+;
+; Input: plaintext in eax,ebx,edx,ebp
+;        ecx points to first round key to use
+;       [esp+8] points to last round key to use (key_limit)
+;       [esp+4] is scratch area on stack
+;
+; Output: ciphertext in ecx,edi,eax,ebp
+;         The stack is unchanged.
+;
+; To call this function you push two values on the stack: the key limit and a scratch space value.
+; Note that key_limit remains the same for most applications that need multiple AES encryptions.
+; For example, a CBC implementation can leave key_limit and the scratch space on the stack throughout the main loop.
+;
+; This function uses one stack variable for temporary storage. This is located just above the return address;
+; callers should wipe this area before returning as it contains keying material.
+;
+        BEFORE_PROC
+
+        align   16              ; We align this function for speed reasons
+
+@SymCryptAesEncryptAsmInternal@16   PROC
+        .FPO(0,2,0,0,0,0)
+
+key_limit       equ     esp+8
+key_ptr         equ     esp+4
+        ;
+        ; Plaintext in eax,ebx,edx,ebp
+        ; ecx points to first round key to use
+        ; [key_limit] is last key to use
+        ; [key_ptr] is temp storage area on stack
+        ;
+        ; Ciphertext ends up in ecx,edi,eax,ebp
+        ;
+        AES_ENC -1
+
+        ret
+
+@SymCryptAesEncryptAsmInternal@16   ENDP
+
+
+;
+; SymCryptAesDecryptAsmInternal
+;
+; Internal AES decryption routine with modified calling convention.
+; This is a bare-bones AES decryption routine which can only be called from assembler code
+; as the calling convention is modified.
+;
+; Input: ciphertext in eax,ebx,edx,ebp
+;        ecx points to first round key to use
+;       [esp+8] points to last round key to use (key_limit)
+;       [esp+4] is scratch area on stack
+;
+; Output: plaintext in ecx,edi,eax,ebp
+;         The stack is unchanged.
+;
+; To call this function you push two values on the stack: the key limit and a scratch space value.
+; Note that key_limit remains the same for most applications that need multiple AES encryptions.
+; For example, a CBC implementation can leave key_limit and the scratch space on the stack throughout the main loop.
+;
+; This function uses one stack variable for temporary storage. This is located just above the return address;
+; callers should wipe this area before returning as it contains keying material.
+;
+        BEFORE_PROC
+
+        align   16
+
+@SymCryptAesDecryptAsmInternal@16   PROC
+        .FPO(0,2,0,0,0,0)
+
+key_limit       equ     esp+8
+key_ptr         equ     esp+4
+        ;
+        ; Ciphertext in eax,ebx,edx,ebp
+        ; ecx points to first round key to use
+        ; [key_limit] is last key to use
+        ; [key_ptr] is temp storage area on stack
+        ; [tmp_buffer] is 16-byte temp buffer
+        ;
+        ; Plaintext ends up in eax,ebc,edx,ebp
+        ;
+
+        AES_DEC -1
+
+        ret
+@SymCryptAesDecryptAsmInternal@16   ENDP
+
+        endif
+
+
+        BEFORE_PROC
+
+@SymCryptAesEncryptAsm@12 PROC
+;VOID
+;SYMCRYPT_CALL
+;SymCryptAesEncrypt( _In_                                   PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+;                    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_LEN )  PCBYTE                      pbPlaintext,
+;                    _Out_writes_bytes_( SYMCRYPT_AES_BLOCK_LEN ) PBYTE                       pbCiphertext );
+;
+;
+; The .FPO provides debugging information.
+; This stuff not well documented,
+; but here is the information I've gathered about the arguments to .FPO
+;
+; In order:
+; cdwLocals: Size of local variables, in DWords
+; cdwParams: Size of parameters, in DWords. Given that this is all about
+;            stack stuff, I'm assuming this is only about parameters passed
+;            on the stack.
+; cbProlog : Number of bytes in the prolog code. We have interleaved the
+;            prolog code with work for better performance. Most uses of
+;            .FPO seem to set this value to 0 anyway, which is what we
+;            will do.
+; cbRegs   : # registers saved in the prolog. 4 in our case
+; fUseBP   : 0 if EBP is not used as base pointer, 1 if EBP is used as base pointer
+; cbFrame  : Type of frame.
+;            0 = FPO frame (no frame pointer)
+;            1 = Trap frame (result of a CPU trap event)
+;            2 = TSS frame
+;
+; Having looked at various occurrences of .FPO in the Windows code it
+; seems to be used fairly sloppy, with lots of arguments left 0 even when
+; they probably shouldn't be according to the spec.
+;
+        .FPO(6,1,0,4,0,0)
+
+AesEncryptFrame struct  4, NONUNIQUE
+
+key_pointer     dd      ?
+lastRoundKey    dd      ?
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+Ciphertext      dd      ?
+
+AesEncryptFrame ends
+
+        ; ecx = AesKey
+        ; edx = Plaintext
+        ; [esp+4] = Ciphertext
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+
+        ;
+        ; Set up our stack frame
+        ;
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+        sub     esp, 8
+
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        ; Load the plaintext block into eax,ebx,edx,ebp
+
+        mov     eax,[edx]
+        mov     ebx,[edx+4]
+        mov     ebp,[edx+12]
+        mov     edx,[edx+8]     ; Plaintext ptr no longer needed
+
+        ; Get the address of the last round key
+        mov     esi,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+        mov     [esp+AesEncryptFrame.lastRoundKey],esi
+
+key_limit       equ     esp + AesEncryptFrame.lastRoundKey
+key_ptr         equ     esp + AesEncryptFrame.key_pointer
+        AES_ENC AesEncryptFrame.Ciphertext
+;       call    @SymCryptAesEncryptAsmInternal@16
+
+        ;mov     esi,[esp+AesEncryptFrame.Ciphertext]
+        mov     [esi],ecx
+        mov     [esi+4],edi
+        mov     [esi+8],eax
+        mov     [esi+12],ebp
+
+        ; wipe the stack location we used for temporary pushes
+
+        mov     [esp-8],esp
+
+        add     esp,8
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     4
+
+
+@SymCryptAesEncryptAsm@12 ENDP
+
+
+
+        BEFORE_PROC
+
+@SymCryptAesDecryptAsm@12 PROC
+;VOID
+;AesDecrypt( _In_                            PCADD_KEY   AesKey,
+;            _In_reads_bytes_(AES_BLOCK_SIZE)     PCBYTE      Ciphertext,
+;            _Out_writes_bytes_(AES_BLOCK_SIZE)    PBYTE       Plaintext
+;            )
+
+        .FPO(6,1,0,4,0,0)
+
+AesDecryptFrame struct  4, NONUNIQUE
+
+key_pointer     dd      ?
+lastRoundKey    dd      ?
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+Plaintext       dd      ?
+
+AesDecryptFrame ends
+
+        ; ecx = AesKey
+        ; edx = Ciphertext
+        ; [esp+4] = Plaintext
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+
+        ;
+        ; Set up our stack frame
+        ;
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+        sub     esp, 8
+
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        ; Load the ciphertext block into eax,ebx,edx,ebp
+
+        mov     eax,[edx]
+        mov     ebx,[edx+4]
+        mov     ebp,[edx+12]
+        mov     edx,[edx+8]     ; Ciphertext ptr no longer needed
+
+
+        ; Get the address of the last round key
+        mov     esi,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastDecRoundKey]       ; esi = lastDecRoundKey
+        mov     ecx,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]       ; ecx = lastEncRoundKey = first Dec round key
+        mov     [esp+AesDecryptFrame.lastRoundKey],esi
+
+key_limit       equ     esp + AesDecryptFrame.lastRoundKey
+key_ptr         equ     esp + AesDecryptFrame.key_pointer
+        AES_DEC AesDecryptFrame.Plaintext
+
+
+        mov     [esi],ecx
+        mov     [esi+4],edi
+        mov     [esi+8],eax
+        mov     [esi+12],ebp
+
+        mov     [esp-8],esp
+
+        add     esp,8
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     4
+
+
+@SymCryptAesDecryptAsm@12 ENDP
+
+
+
+
+        BEFORE_PROC
+
+@SymCryptAesCbcEncryptAsm@20 PROC
+;VOID
+;SYMCRYPT_CALL
+;SymCryptAesCbcEncrypt(
+;    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+;    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+;    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+;    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+;                                            SIZE_T                      cbData );
+;
+        .FPO(9,3,0,4,0,0)
+
+AesCbcEncryptFrame struct  4, NONUNIQUE
+
+key_pointer     dd      ?
+lastRoundKey    dd      ?
+pbEndDst        dd      ?
+firstRoundKey   dd      ?
+pbChainingValue dd      ?
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pbSrc           dd      ?
+pbDst           dd      ?
+cbData          dd      ?
+
+AesCbcEncryptFrame ends
+
+        ; ecx = pExpandedKey
+        ; edx = pbChainingValue
+        ; [esp+4...] = pbSrc, pbDst, cbData
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+
+        ;
+        ; Set up our stack frame
+        ;
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             ; pbChainingValue
+        push    ecx             ; pbExpandedKey points to the first round key
+        sub     esp, 12
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        mov     eax,[esp + AesCbcEncryptFrame.cbData]
+        and     eax, NOT 15
+        jz      AesCbcEncryptDoNothing
+
+        ; Get & store the address of the last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+        mov     [esp+AesCbcEncryptFrame.lastRoundKey],ebx
+
+
+        mov     esi,[esp + AesCbcEncryptFrame.pbDst]
+        add     eax,esi
+        mov     [esp + AesCbcEncryptFrame.pbEndDst], eax
+
+        ;
+        ; Convert pbSrc to an offset from pbDst to reduce the # updates
+        ; in a loop
+        ;
+        ;mov    ecx,[esp + AesCbcEncryptFrame.pbSrc]
+        ;sub    ecx, esi
+        ;mov    [esp + AesCbcEncryptFrame.pbSrc], ecx
+
+        ; esi = pbDst
+
+        ; Load state from chaining value
+        ; State is in ecx,edi,eax,ebp
+        mov     ecx,[edx]
+        mov     edi,[edx + 4]
+        mov     eax,[edx + 8]
+        mov     ebp,[edx + 12]
+
+
+AesCbcEncryptLoop:
+        ; Invariant:
+        ;  State in (ecx, edi, eax, ebp)
+        ;  esi = pbDst for next block
+
+        ;
+        ; Change esi to point to current pbSrc pointer. pbSrc is here an offset from pbDst.
+        ;
+        mov     esi,[esp + AesCbcEncryptFrame.pbSrc]
+
+        ;
+        ; Xor next plaintext block & move state to (eax, ebx, edx, ebp)
+        ; We keep the reads sequentially to help the HW prefetch logic in the CPU
+        ;
+        xor     ecx, [esi]
+
+        mov     ebx, [esi + 4]
+        xor     ebx, edi
+
+        mov     edx, [esi + 8]
+        xor     edx, eax
+
+        mov     eax, ecx
+
+        xor     ebp, [esi + 12]
+
+        add     esi, 16
+        mov     [esp + AesCbcEncryptFrame.pbSrc], esi
+
+        mov     ecx,[esp + AesCbcEncryptFrame.firstRoundKey]
+
+
+key_limit       equ     esp + AesCbcEncryptFrame.lastRoundKey
+key_ptr         equ     esp + AesCbcEncryptFrame.key_pointer
+        AES_ENC AesCbcEncryptFrame.pbDst        ; argument is address that is loaded into esi
+
+        ;call   @SymCryptAesEncryptAsmInternal@16
+        ;mov    esi,[esp + AesCbcEncryptFrame.pbDst]
+
+        mov     [esi], ecx
+        mov     [esi + 4], edi
+        mov     [esi + 8], eax
+        mov     [esi + 12], ebp
+
+        add     esi,16
+        cmp     esi,[esp + AesCbcEncryptFrame.pbEndDst]
+        mov     [esp + AesCbcEncryptFrame.pbDst], esi
+        jb      AesCbcEncryptLoop
+
+        mov     edx,[esp + AesCbcEncryptFrame.pbChainingValue]
+        mov     [edx], ecx
+        mov     [edx + 4], edi
+        mov     [edx + 8], eax
+        mov     [edx + 12], ebp
+
+        ;
+        ; Wipe the one stack location that the internal encrypt routine uses
+        ; for temporary data storage
+        ;
+        mov     [esp - 8], esp
+
+AesCbcEncryptDoNothing:
+
+        add     esp, 20
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     12
+
+
+@SymCryptAesCbcEncryptAsm@20 ENDP
+
+
+
+
+        BEFORE_PROC
+
+@SymCryptAesCbcDecryptAsm@20 PROC
+;VOID
+;SYMCRYPT_CALL
+;SymCryptAesCbcDecrypt(
+;    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+;    _In_reads_bytes_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+;    _In_reads_bytes_( cbData )                   PCBYTE                      pbSrc,
+;    _Out_writes_bytes_( cbData )                  PBYTE                       pbDst,
+;                                            SIZE_T                      cbData );
+;
+        .FPO(13,3,0,4,0,0)
+
+AesCbcDecryptFrame struct  4, NONUNIQUE
+
+key_pointer     dd      ?
+lastRoundKey    dd      ?
+newChainValue   dd      4 dup (?)       ; New chaining value after the call
+pbCurrentDst    dd      ?
+firstRoundKey   dd      ?
+pbChainingValue dd      ?
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pbSrc           dd      ?
+pbDst           dd      ?
+cbData          dd      ?
+
+AesCbcDecryptFrame ends
+
+        ; ecx = pExpandedKey
+        ; edx = pbChainingValue
+        ; [esp+4...] = pbSrc, pbDst, cbData
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+
+        ;
+        ; Set up our stack frame
+        ;
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             ; pbChainingValue
+        sub     esp, 32
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        mov     eax,[esp + AesCbcDecryptFrame.cbData]
+        and     eax, NOT 15
+        jz      AesCbcDecryptDoNothing
+
+        ; Get & store the address of the first & last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastDecRoundKey]
+        mov     [esp+AesCbcDecryptFrame.lastRoundKey],ebx
+
+        sub     eax, 16
+
+        mov     ecx,[ecx + SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]           ; = first dec round key
+        mov     [esp + AesCbcDecryptFrame.firstRoundKey], ecx
+
+        mov     edi,[esp + AesCbcDecryptFrame.pbDst]
+        add     edi, eax
+        mov     [esp + AesCbcDecryptFrame.pbCurrentDst], edi    ; points to last block in buffer
+
+        mov     esi,[esp + AesCbcDecryptFrame.pbSrc]
+        add     esi, eax
+        mov     [esp + AesCbcDecryptFrame.pbSrc], esi
+
+        ;
+        ; Load last block & store it for the chaining output
+        ;
+        mov     eax,[esi]
+        mov     [esp + AesCbcDecryptFrame.newChainValue], eax
+        mov     ebx,[esi + 4]
+        mov     [esp + AesCbcDecryptFrame.newChainValue + 4], ebx
+        mov     edx,[esi + 8]
+        mov     [esp + AesCbcDecryptFrame.newChainValue + 8], edx
+        mov     ebp,[esi + 12]
+        mov     [esp + AesCbcDecryptFrame.newChainValue + 12], ebp
+
+        jmp     AesCbcDecryptLoopEnter
+
+AesCbcDecryptLoop:
+        ; Invariant:
+        ;  State in (ecx, edi, eax, ebp)
+        ;  State is just after decrypt, but before feed-forward xor
+        ;  esi = pbCurrentDst for just decrypted state
+        ;  pbSrc = corresponding position in Src buffer
+
+        mov     ebx,[esp + AesCbcDecryptFrame.pbSrc]
+
+        mov     edx,[ebx - 16 + 8]
+        xor     eax, edx
+        mov     [esi + 16 + 8], eax
+
+        mov     eax,[ebx - 16 + 0]
+        xor     ecx, eax
+        mov     [esi + 16 + 0], ecx
+
+        lea     ecx, [ebx - 16]         ; decrement + move ptr to allow register shuffle
+
+        mov     ebx,[ebx - 16 + 4]
+        xor     edi, ebx
+        mov     [esi + 16 + 4], edi
+
+        mov     edi,[ecx + 12]
+        xor     ebp, edi
+        mov     [esi + 16 + 12], ebp
+        mov     ebp, edi
+
+        mov     [esp + AesCbcDecryptFrame.pbSrc], ecx
+
+        mov     ecx,[esp + AesCbcDecryptFrame.firstRoundKey]
+
+AesCbcDecryptLoopEnter:
+
+key_limit       equ     esp + AesCbcDecryptFrame.lastRoundKey
+key_ptr         equ     esp + AesCbcDecryptFrame.key_pointer
+
+        AES_DEC AesCbcDecryptFrame.pbCurrentDst
+        ;call   @SymCryptAesDecryptAsmInternal@16
+        ;mov    esi,[esp + AesCbcDecryptFrame.pbCurrentDst]
+
+        cmp     esi,[esp + AesCbcDecryptFrame.pbDst]
+        lea     esi,[esi-16]
+        mov     [esp + AesCbcDecryptFrame.pbCurrentDst], esi
+
+        ja      AesCbcDecryptLoop
+
+        ;
+        ; Xor with chaining value and store last block (first in output buffer)
+        ;
+
+        mov     ebx,[esp + AesCbcDecryptFrame.pbChainingValue]
+        xor     ecx,[ebx]
+        mov     [esi + 16], ecx
+        xor     edi,[ebx + 4]
+        mov     [esi + 16 + 4], edi
+        xor     eax,[ebx + 8]
+        mov     [esi + 16 + 8], eax
+        xor     ebp,[ebx + 12]
+        mov     [esi + 16+12], ebp
+
+        ;
+        ; Set the new chaining value
+        ;
+
+        mov     eax,[esp + AesCbcDecryptFrame.newChainValue + 0]
+        mov     [ebx + 0], eax
+        mov     ecx,[esp + AesCbcDecryptFrame.newChainValue + 4]
+        mov     [ebx + 4], ecx
+        mov     eax,[esp + AesCbcDecryptFrame.newChainValue + 8]
+        mov     [ebx + 8], eax
+        mov     ecx,[esp + AesCbcDecryptFrame.newChainValue + 12]
+        mov     [ebx + 12], ecx
+
+
+        ; Wipe the one stack location that the internal encrypt routine uses
+        ; for temporary data storage
+        ;
+        mov     [esp - 8], esp
+
+AesCbcDecryptDoNothing:
+
+        add     esp, 36
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     12
+
+
+@SymCryptAesCbcDecryptAsm@20 ENDP
+
+
+        BEFORE_PROC
+
+@SymCryptAesCtrMsb64Asm@20 PROC
+;VOID
+;SYMCRYPT_CALL
+;SymCryptAesCtrMsb64(
+;        _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+;        _Inout_updates_bytes_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+;        _In_reads_bytes_( cbData )                       PCBYTE                      pbSrc,
+;        _Out_writes_bytes_( cbData )                      PBYTE                       pbDst,
+;                                                    SIZE_T                      cbData )
+
+        .FPO(13,3,0,4,0,0)
+
+AesCtrMsb64Frame struct  4, NONUNIQUE
+
+key_pointer     dd      ?
+lastRoundKey    dd      ?
+chainValue      dd      4 dup (?)
+pbEndDst        dd      ?
+firstRoundKey   dd      ?
+pbChainingValue dd      ?
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pbSrc           dd      ?
+pbDst           dd      ?
+cbData          dd      ?
+
+AesCtrMsb64Frame ends
+
+        ; ecx = pExpandedKey
+        ; edx = pbChainingValue
+        ; [esp+4...] = pbSrc, pbDst, cbData
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+
+        ;
+        ; Set up our stack frame
+        ;
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             ; pbChainingValue
+        push    ecx             ; pbExpandedKey points to the first round key
+        sub     esp, 28
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        mov     eax,[esp + AesCtrMsb64Frame.cbData]
+        and     eax, NOT 15
+        jz      AesCtrMsb64DoNothing
+
+        ; Get & store the address of the last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+        mov     [esp+AesCtrMsb64Frame.lastRoundKey],ebx
+
+
+        mov     esi,[esp + AesCtrMsb64Frame.pbDst]
+        add     eax,esi
+        mov     [esp + AesCtrMsb64Frame.pbEndDst], eax
+
+        ; esi = pbDst
+
+        ; Load state from chaining value & copy into local stack
+        ; State is in ecx,edi,eax,ebp
+        mov     eax,[edx]
+        mov     [esp + AesCtrMsb64Frame.chainValue], eax
+        mov     ebx,[edx + 4]
+        mov     [esp + AesCtrMsb64Frame.chainValue + 4], ebx
+        mov     ebp,[edx + 12]
+        mov     edx,[edx + 8]
+
+
+AesCtrMsb64Loop:
+        ; Invariant:
+        ;  counter State in (eax, ebx, edx, ebp)
+        ;  first 8 bytes of counter state in [esp+chainValue]
+
+        ;
+        ; Write the updated counter value to the local copy
+        ;
+        mov     [esp + AesCtrMsb64Frame.chainValue + 8], edx
+        mov     ecx,[esp + AesCtrMsb64Frame.firstRoundKey]
+        mov     [esp + AesCtrMsb64Frame.chainValue + 12], ebp
+
+key_limit       equ     esp + AesCtrMsb64Frame.lastRoundKey
+key_ptr         equ     esp + AesCtrMsb64Frame.key_pointer
+
+        AES_ENC AesCtrMsb64Frame.pbSrc
+        ;       result is in (ecx, edi, eax, ebp)
+        ;       esi = pbSrc
+
+        mov     ebx,[esp + AesCtrMsb64Frame.pbDst]
+
+        ;
+        ; Read the Src block, xor it with the state, and write it to Dst
+        ;
+        xor     ecx,[esi]
+        mov     [ebx],ecx
+        xor     edi,[esi+4]
+        mov     [ebx+4],edi
+        xor     eax,[esi+8]
+        mov     [ebx+8],eax
+        xor     ebp,[esi+12]
+        mov     [ebx+12],ebp
+
+        add     esi, 16
+        mov     [esp + AesCtrMsb64Frame.pbSrc], esi
+
+        lea     ecx,[ebx + 16]                  ; increment and move pointer to allow reg shuffle
+
+        ;
+        ; Load the current chain value, and do the increment
+        ;
+        mov     ebp,[esp + AesCtrMsb64Frame.chainValue+12]
+        bswap   ebp
+        mov     edx,[esp + AesCtrMsb64Frame.chainValue+8]
+        bswap   edx
+        add     ebp,1
+        mov     eax,[esp + AesCtrMsb64Frame.chainValue]
+        adc     edx, 0
+        mov     ebx,[esp + AesCtrMsb64Frame.chainValue+4]
+        bswap   ebp
+        bswap   edx
+
+
+        cmp     ecx,[esp + AesCtrMsb64Frame.pbEndDst]
+        mov     [esp + AesCtrMsb64Frame.pbDst], ecx
+
+        jb      AesCtrMsb64Loop
+
+        ;
+        ; Write the updated chaining value; we only need to update 8 bytes
+        ;
+        mov     ebx,[esp + AesCtrMsb64Frame.pbChainingValue]
+        mov     [ebx + 8], edx
+        mov     [ebx + 12], ebp
+
+        ;
+        ; Wipe our local copy of the chaining value
+        ;
+        xor     eax,eax
+        mov     [esp + AesCtrMsb64Frame.chainValue], eax
+        mov     [esp + AesCtrMsb64Frame.chainValue + 4], eax
+        mov     [esp + AesCtrMsb64Frame.chainValue + 8], eax
+        mov     [esp + AesCtrMsb64Frame.chainValue + 12], eax
+
+        ;
+        ; Wipe the one stack location that the internal encrypt routine uses
+        ; for temporary data storage
+        ;
+        mov     [esp - 8], esp
+
+AesCtrMsb64DoNothing:
+
+        add     esp, 36
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     12
+
+
+@SymCryptAesCtrMsb64Asm@20 ENDP
+
+
+        if 0    ; disabled while we concentrate on reaching RSA32 parity. The code below works though.
+
+
+        BEFORE_PROC
+
+@SymCryptAesCbcMacAsm@16 PROC
+;VOID
+;SYMCRYPT_CALL
+;SymCryptAesCbcMac(
+;        _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+;        _Inout_updates_bytes_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+;        _In_reads_bytes_( cbData )                       PCBYTE                      pbData,
+;                                                    SIZE_T                      cbData )
+;
+        .FPO(9,2,0,4,0,0)
+
+AesCbcMacFrame struct  4, NONUNIQUE
+
+key_pointer     dd      ?
+lastRoundKey    dd      ?
+pbEnd           dd      ?
+firstRoundKey   dd      ?
+pbChainingValue dd      ?
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pbData          dd      ?
+cbData          dd      ?
+
+AesCbcMacFrame ends
+
+        ; ecx = pExpandedKey
+        ; edx = pbChainingValue
+        ; [esp+4...] = pbSrc, pbDst, cbData
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+
+        ;
+        ; Set up our stack frame
+        ;
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        push    edx             ; pbChainingValue
+        push    ecx             ; pbExpandedKey points to the first round key
+        sub     esp, 12
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        mov     eax,[esp + AesCbcMacFrame.cbData]
+        and     eax, NOT 15
+        jz      AesCbcMacDoNothing
+
+        ; Get & store the address of the last round key
+        mov     ebx,[ecx+SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+        mov     [esp+AesCbcMacFrame.lastRoundKey],ebx
+
+
+        mov     esi,[esp + AesCbcMacFrame.pbData]
+        add     eax,esi
+        mov     [esp + AesCbcMacFrame.pbEnd], eax
+
+        ; esi = pbData
+
+        ; Load state from chaining value
+        ; State is in ecx,edi,eax,ebp
+        mov     ecx,[edx]
+        mov     edi,[edx + 4]
+        mov     eax,[edx + 8]
+        mov     ebp,[edx + 12]
+
+
+AesCbcMacLoop:
+        ; Invariant:
+        ;  State in (ecx, edi, eax, ebp)
+        ;  esi = pbData
+
+        ;
+        ; Xor next plaintext block & move state to (eax, ebx, edx, ebp)
+        ; We keep the reads sequentially to help the HW prefetch logic in the CPU
+        ;
+        xor     ecx, [esi]
+
+        mov     ebx, [esi + 4]
+        xor     ebx, edi
+
+        mov     edx, [esi + 8]
+        xor     edx, eax
+
+        mov     eax, ecx
+
+        xor     ebp, [esi + 12]
+
+        mov     ecx,[esp + AesCbcMacFrame.firstRoundKey]
+
+
+key_limit       equ     esp + AesCbcMacFrame.lastRoundKey
+key_ptr         equ     esp + AesCbcMacFrame.key_pointer
+        AES_ENC AesCbcMacFrame.pbData           ; argument is address that is loaded into esi
+
+        ;call   @SymCryptAesEncryptAsmInternal@16
+        ;mov    esi,[esp + AesCbcMacFrame.pbData]
+
+        add     esi, 16
+        cmp     esi,[esp + AesCbcMacFrame.pbEnd]
+        mov     [esp + AesCbcMacFrame.pbData], esi
+        jb      AesCbcMacLoop
+
+        mov     edx,[esp + AesCbcMacFrame.pbChainingValue]
+        mov     [edx], ecx
+        mov     [edx + 4], edi
+        mov     [edx + 8], eax
+        mov     [edx + 12], ebp
+
+        ;
+        ; Wipe the one stack location that the internal encrypt routine uses
+        ; for temporary data storage
+        ;
+        mov     [esp - 8], esp
+
+AesCbcMacDoNothing:
+
+        add     esp, 20
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+        ret     8
+
+
+@SymCryptAesCbcMacAsm@16 ENDP
+
+        endif
+
+
+
+;===========================================================================
+;       AES-NI code
+
+
+if 0    ; No longer used; replaced with intrinsic C code that can be inlined.
+
+        BEFORE_PROC
+
+@SymCryptAes4SboxXmm@8       PROC
+;
+;VOID
+;Aes4SboxAsm( _In_reads_bytes_(4) PCBYTE pIn, _Out_writes_bytes_(4) PBYTE pOut );
+;
+        .FPO(0,0,0,0,0,0)
+        ;
+        ;ecx points to source
+        ;edx points to destination
+        ;
+        ;We only use volatile registers so we do not have to save any registers.
+        ;
+
+        mov     eax,[ecx]       ; Use a register to avoid alignment issues
+        movd    xmm0, eax
+
+        movsldup        xmm0, xmm0      ; copy [31:0] to [63:32]
+        aeskeygenassist xmm0, xmm0, 0
+
+        movd    eax, xmm0
+        mov     [edx], eax
+
+        ret
+
+@SymCryptAes4SboxXmm@8       ENDP
+
+
+        BEFORE_PROC
+
+@SymCryptAesCreateDecryptionRoundKeyXmm@8       PROC
+;
+;VOID
+;AesCreateDecryptionRoundKeyAsm( _In_reads_bytes_(16) PCBYTE pEncryptionRoundKey,
+;                                _Out_writes_bytes_(16) PBYTE pDecryptionRoundKey );
+;
+        .FPO(0,0,0,0,0,0)
+        ;ecx points to source
+        ;edx points to destination
+
+        movups  xmm0,[ecx]
+        aesimc  xmm0, xmm0
+        movups  [edx], xmm0
+        ret
+
+@SymCryptAesCreateDecryptionRoundKeyXmm@8       ENDP
+
+endif
+
+
+
+AES_ENCRYPT_XMM MACRO
+        ; xmm0 contains the plaintext
+        ; ecx points to first round key to use
+        ; eax is last key to use (unchanged)
+        ; Ciphertext ends up in xmm0, xmm1 used
+        ;
+
+        ;
+        ; xor in first round key; round keys are 16-aligned on amd64
+        ;
+        movups  xmm1, [ecx]
+        pxor    xmm0, xmm1
+        movups  xmm1, [ecx+16]
+        aesenc  xmm0, xmm1
+
+        add     ecx, 32
+
+@@:
+        ; r9 points to next round key
+        movups  xmm1, [ecx]
+        aesenc  xmm0, xmm1
+
+        movups  xmm1, [ecx + 16]
+        aesenc  xmm0, xmm1
+
+        add     ecx, 32
+        cmp     ecx, eax
+        jc      @B
+
+        ;
+        ; Now for the final round
+        ;
+        movups          xmm1, [eax]
+        aesenclast      xmm0, xmm1
+
+        ENDM
+
+
+AES_DECRYPT_XMM MACRO
+        ; xmm0 contains the plaintext
+        ; ecx points to first round key to use
+        ; eax is last key to use (unchanged)
+        ; Ciphertext ends up in xmm0, xmm1 used
+        ;
+
+        ;
+        ; xor in first round key; round keys are 16-aligned on amd64
+        ;
+        movups  xmm1, [ecx]
+        pxor    xmm0, xmm1
+        movups  xmm1, [ecx+16]
+        aesdec  xmm0, xmm1
+
+        add     ecx, 32
+
+@@:
+        ; r9 points to next round key
+        movups  xmm1, [ecx]
+        aesdec  xmm0, xmm1
+
+        movups  xmm1, [ecx + 16]
+        aesdec  xmm0, xmm1
+
+        add     ecx, 32
+        cmp     ecx, eax
+        jc      @B
+
+        ;
+        ; Now for the final round
+        ;
+        movups          xmm1, [eax]
+        aesdeclast      xmm0, xmm1
+
+        ENDM
+
+
+
+        BEFORE_PROC
+
+if 0
+@SymCryptAesEncryptXmm@12       PROC
+        ;
+        ; rcx = expanded key
+        ; rdx = pbSrc
+        ; [esp+4] = pbDst
+
+        .FPO( 0, 1, 0, 0, 0, 0)
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        movups  xmm0,[edx]
+        mov     eax, [ecx + SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+
+
+        AES_ENCRYPT_XMM
+        ; xmm0 contains the plaintext
+        ; ecx points to first round key to use
+        ; eax is last key to use (unchanged)
+        ; Ciphertext ends up in xmm0
+
+        mov     ecx,[esp + 4]
+
+        movups  [ecx],xmm0
+
+        ret     4
+
+@SymCryptAesEncryptXmm@12       ENDP
+
+endif
+if 0
+        BEFORE_PROC
+
+@SymCryptAesDecryptXmm@12       PROC
+        ;
+        ; rcx = expanded key
+        ; rdx = pbSrc
+        ; [esp+4] = pbDst
+
+        .FPO( 0, 1, 0, 0, 0, 0)
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        movups  xmm0,[edx]
+        mov     eax, [ecx + SYMCRYPT_AES_EXPANDED_KEY.lastDecRoundKey]
+        mov     ecx, [ecx + SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+
+
+        AES_DECRYPT_XMM
+        ; xmm0 contains the plaintext
+        ; ecx points to first round key to use
+        ; eax is last key to use (unchanged)
+        ; Ciphertext ends up in xmm0
+
+        mov     ecx,[esp + 4]
+
+        movups  [ecx],xmm0
+
+        ret     4
+
+@SymCryptAesDecryptXmm@12       ENDP
+
+endif
+
+
+if 0
+        BEFORE_PROC
+
+@SymCryptAesCbcEncryptXmm@20 PROC
+        ; ecx = pExpandedKey
+        ; edx = pbChainingValue
+        ; [esp+4...] = pbSrc, pbDst, cbData
+
+        .FPO    (4, 3, 0, 4, 0, 0 )     ; locals, params, 0, regs saved, 0, 0
+
+SymCryptAesCbcEncryptXmmFrame struct  4, NONUNIQUE
+
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pbSrc           dd      ?
+pbDst           dd      ?
+cbData          dd      ?
+
+SymCryptAesCbcEncryptXmmFrame ends
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        mov     ebp,[esp + SymCryptAesCbcEncryptXmmFrame.cbData]
+
+        and     ebp, NOT 15
+        jz      SymCryptAesCbcEncryptXmmNoData
+
+        mov     esi,[esp +  SymCryptAesCbcEncryptXmmFrame.pbSrc]
+        mov     edi,[esp +  SymCryptAesCbcEncryptXmmFrame.pbDst]
+
+        add     ebp, esi                        ; ebp = pbSrcEnd
+
+        movups  xmm0,[edx]
+        mov     eax, [ecx + SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+        mov     ebx, ecx
+
+
+SymCryptAesCbcEncryptXmmLoop:
+        movups  xmm1, [esi]
+        pxor    xmm0, xmm1
+        add     esi, 16
+
+        mov     ecx, ebx
+
+        AES_ENCRYPT_XMM
+
+        movups  [edi], xmm0
+        add     edi, 16
+        cmp     esi, ebp
+        jc      SymCryptAesCbcEncryptXmmLoop
+
+        movups  [edx], xmm0
+
+SymCryptAesCbcEncryptXmmNoData:
+
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+
+        ret     12
+
+
+@SymCryptAesCbcEncryptXmm@20 ENDP
+
+endif
+
+
+if 0    ; replaced with C/intrinsic code
+
+        BEFORE_PROC
+
+@SymCryptAesCbcDecryptXmm@20 PROC
+        ; ecx = pExpandedKey
+        ; edx = pbChainingValue
+        ; [esp+4...] = pbSrc, pbDst, cbData
+
+        .FPO    (4, 3, 0, 4, 0, 0 )     ; locals, params, 0, regs saved, 0, 0
+
+SymCryptAesCbcDecryptXmmFrame struct  4, NONUNIQUE
+
+SaveEdi         dd      ?
+SaveEsi         dd      ?
+SaveEbp         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pbSrc           dd      ?
+pbDst           dd      ?
+cbData          dd      ?
+
+SymCryptAesCbcDecryptXmmFrame ends
+
+        ;
+        ; 2-byte NOP for hot patching
+        ; This is what our current compiler does for every function, so we will follow
+        ; that.
+        ;
+        mov     edi,edi
+        push    ebx
+        push    ebp
+        push    esi
+        push    edi
+
+        SYMCRYPT_CHECK_MAGIC    ecx, SYMCRYPT_AES_EXPANDED_KEY
+
+        mov     ebp,[esp + SymCryptAesCbcDecryptXmmFrame.cbData]
+
+        and     ebp, NOT 15
+        jz      SymCryptAesCbcDecryptXmmNoData
+
+        mov     esi,[esp + SymCryptAesCbcDecryptXmmFrame.pbSrc]
+        mov     edi,[esp + SymCryptAesCbcDecryptXmmFrame.pbDst]
+
+        add     ebp, esi                        ; ebp = pbSrcEnd
+
+        movups  xmm3,[edx]
+        mov     eax, [ecx + SYMCRYPT_AES_EXPANDED_KEY.lastDecRoundKey]
+        mov     ebx, [ecx + SYMCRYPT_AES_EXPANDED_KEY.lastEncRoundKey]
+
+
+SymCryptAesCbcDecryptXmmLoop:
+        movups  xmm0, [esi]
+        add     esi, 16
+
+        movaps  xmm2, xmm0
+
+        mov     ecx, ebx
+
+        AES_DECRYPT_XMM
+
+        pxor    xmm0, xmm3
+
+        movaps  xmm3, xmm2
+
+        movups  [edi], xmm0
+        add     edi, 16
+        cmp     esi, ebp
+        jc      SymCryptAesCbcDecryptXmmLoop
+
+        movups  [edx], xmm2
+
+SymCryptAesCbcDecryptXmmNoData:
+
+        pop     edi
+        pop     esi
+        pop     ebp
+        pop     ebx
+
+        ret     12
+
+
+@SymCryptAesCbcDecryptXmm@20 ENDP
+
+endif;
+
+_TEXT   ENDS
+
+END
diff --git a/libs/symcrypt/lib/i386/fdef_asm.S b/libs/symcrypt/lib/i386/fdef_asm.S
new file mode 100644
index 000000000000..956e7abb9467
--- /dev/null
+++ b/libs/symcrypt/lib/i386/fdef_asm.S
@@ -0,0 +1,633 @@
+//
+//  fdef_asm.cppasm     Assembler code for fast arithmetic
+//  Requires C preprocessor to correctly include C_asm_shared.inc and symcrypt_magic.in
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// Manually converted to GNU as format for Wine
+//
+
+#include "symcryptasm_shared.cppasm"
+
+        .globl @SymCryptFdefRawAddAsm@16
+        .def @SymCryptFdefRawAddAsm@16
+        .scl 2
+        .type 32
+        .endef
+@SymCryptFdefRawAddAsm@16:
+
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawAdd(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+        // stack frame
+        .set SymCryptFdefRawAddAsmFrame_SaveEdi,       0
+        .set SymCryptFdefRawAddAsmFrame_SaveEbx,       4
+        .set SymCryptFdefRawAddAsmFrame_ReturnAddress, 8
+        .set SymCryptFdefRawAddAsmFrame_pDst,          12
+        .set SymCryptFdefRawAddAsmFrame_nDigits,       16
+
+        // ecx = pSrc1
+        // edx = pSrc2
+
+        mov     edi,edi
+        push    ebx
+        push    edi
+
+        mov     edi, [esp + SymCryptFdefRawAddAsmFrame_pDst]
+        mov     ebx, [esp + SymCryptFdefRawAddAsmFrame_nDigits]
+
+        xor     eax,eax                     // Cy = 0
+
+.LSymCryptFdefRawAddAsmLoop:
+        mov     eax, [ecx]
+        adc     eax, [edx]
+        mov     [edi], eax
+
+        mov     eax, [ecx + 4]
+        adc     eax, [edx + 4]
+        mov     [edi + 4], eax
+
+        mov     eax, [ecx + 8]
+        adc     eax, [edx + 8]
+        mov     [edi + 8], eax
+
+        mov     eax, [ecx + 12]
+        adc     eax, [edx + 12]
+        mov     [edi + 12], eax
+
+        lea     ecx, [ecx + 16]
+        lea     edx, [edx + 16]
+        lea     edi, [edi + 16]
+
+        dec     ebx
+        jnz     .LSymCryptFdefRawAddAsmLoop
+
+        mov     eax, 0
+        adc     eax, eax
+
+        pop     edi
+        pop     ebx
+        ret     8
+
+
+        .globl @SymCryptFdefRawSubAsm@16
+        .def @SymCryptFdefRawSubAsm@16
+        .scl 2
+        .type 32
+        .endef
+@SymCryptFdefRawSubAsm@16:
+//UINT32
+//SYMCRYPT_CALL
+//SymCryptFdefRawSub(
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+//    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+//    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+//                                                            UINT32      nDigits )
+
+        // stack frame
+        .set SymCryptFdefRawSubAsmFrame_SaveEdi,       0
+        .set SymCryptFdefRawSubAsmFrame_SaveEbx,       4
+        .set SymCryptFdefRawSubAsmFrame_ReturnAddress, 8
+        .set SymCryptFdefRawSubAsmFrame_pDst,          12
+        .set SymCryptFdefRawSubAsmFrame_nDigits,       16
+
+        // ecx = pSrc1
+        // edx = pSrc2
+
+        mov     edi,edi
+        push    ebx
+        push    edi
+
+        mov     edi, [esp + SymCryptFdefRawSubAsmFrame_pDst]
+        mov     ebx, [esp + SymCryptFdefRawSubAsmFrame_nDigits]
+
+        xor     eax,eax                     // Cy = 0
+
+.LSymCryptFdefRawSubAsmLoop:
+        mov     eax, [ecx]
+        sbb     eax, [edx]
+        mov     [edi], eax
+
+        mov     eax, [ecx + 4]
+        sbb     eax, [edx + 4]
+        mov     [edi + 4], eax
+
+        mov     eax, [ecx + 8]
+        sbb     eax, [edx + 8]
+        mov     [edi + 8], eax
+
+        mov     eax, [ecx + 12]
+        sbb     eax, [edx + 12]
+        mov     [edi + 12], eax
+
+        lea     ecx, [ecx + 16]
+        lea     edx, [edx + 16]
+        lea     edi, [edi + 16]
+
+        dec     ebx
+        jnz     .LSymCryptFdefRawSubAsmLoop
+
+        mov     eax, 0
+        adc     eax, eax
+
+        pop     edi
+        pop     ebx
+        ret     8
+
+        .globl @SymCryptFdefMaskedCopyAsm@16
+        .def @SymCryptFdefMaskedCopyAsm@16
+        .scl 2
+        .type 32
+        .endef
+@SymCryptFdefMaskedCopyAsm@16:
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefMaskedCopy(
+//    _In_reads_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )      PCBYTE      pbSrc,
+//    _InOut_writes_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )  PBYTE       pbDst,
+//                                                                UINT32      nDigits,
+//                                                                UINT32      mask )
+
+        // stack frame
+        .set SymCryptFdefMaskedCopyAsmFrame_SaveEdi,       0
+        .set SymCryptFdefMaskedCopyAsmFrame_SaveEsi,       4
+        .set SymCryptFdefMaskedCopyAsmFrame_SaveEbx,       8
+        .set SymCryptFdefMaskedCopyAsmFrame_Returnaddress, 12
+        .set SymCryptFdefMaskedCopyAsmFrame_nDigits,       16
+        .set SymCryptFdefMaskedCopyAsmFrame_vMask,         20
+
+        mov     edi,edi
+        push    ebx
+        push    esi
+        push    edi
+
+        mov     esi, [esp + SymCryptFdefMaskedCopyAsmFrame_vMask]
+        mov     edi, [esp + SymCryptFdefMaskedCopyAsmFrame_nDigits]
+
+.LSymCryptFdefMaskedCopyAsmLoop:
+        mov     eax, [ecx]
+        mov     ebx, [edx]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx], eax
+
+        mov     eax, [ecx + 4]
+        mov     ebx, [edx + 4]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx + 4], eax
+
+        mov     eax, [ecx + 8]
+        mov     ebx, [edx + 8]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx + 8], eax
+
+        mov     eax, [ecx + 12]
+        mov     ebx, [edx + 12]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx + 12], eax
+
+        add     ecx, 16
+        add     edx, 16
+        sub     edi, 1
+        jnz     .LSymCryptFdefMaskedCopyAsmLoop
+
+        // No return value
+        pop     edi
+        pop     esi
+        pop     ebx
+        ret     8
+
+
+        .globl @SymCryptFdefRawMulAsm@20
+        .def @SymCryptFdefRawMulAsm@20
+        .scl 2
+        .type 32
+        .endef
+@SymCryptFdefRawMulAsm@20:
+
+//VOID
+//SYMCRYPT_CALL
+//SymCryptFdefRawMul(
+//    _In_reads_(nWords1)             PCUINT32    pSrc1,
+//                                    UINT32      nDigits1,
+//    _In_reads_(nWords2)             PCUINT32    pSrc2,
+//                                    UINT32      nDigits2,
+//    _Out_writes_(nWords1 + nWords2) PUINT32     pDst )
+
+        // stack frame
+        .set SymCryptFdefRawMulAsmFrame_nWords1,       0
+        .set SymCryptFdefRawMulAsmFrame_pSrc1,         4
+        .set SymCryptFdefRawMulAsmFrame_SaveEbp,       8
+        .set SymCryptFdefRawMulAsmFrame_SaveEsi,       12
+        .set SymCryptFdefRawMulAsmFrame_SaveEdi,       16
+        .set SymCryptFdefRawMulAsmFrame_SaveEbx,       20
+        .set SymCryptFdefRawMulAsmFrame_ReturnAddress, 24
+        .set SymCryptFdefRawMulAsmFrame_pSrc2,         28
+        .set SymCryptFdefRawMulAsmFrame_nDigits2,      32
+        .set SymCryptFdefRawMulAsmFrame_pDst,          36
+
+        // Basic code structure:
+        //   for each word in Src1:
+        //       Dst += Src2 * word
+        // Register assignments
+        //
+        //   eax = tmp/lower half of mult
+        //   ebx = multiplicant
+        //   ecx = loop counter, initialized to nDigits2
+        //   edx = upper half of mult
+        //   esi = running pointer into Src2
+        //   edi = dst
+        //   ebp = carry
+        //
+        //   esp + pSrc1     running pointer into Src1
+        //   esp +
+
+
+        mov     edi,edi
+        push    ebx
+        push    esi
+        push    edi
+        push    ebp
+        sub     esp, SymCryptFdefRawMulAsmFrame_SaveEbp
+
+        // ecx = pSrc1
+        // edx = nDigits1
+
+        mov     esi,[esp + SymCryptFdefRawMulAsmFrame_pSrc2]
+
+        mov     ebx, [ecx]                                          // ebx = pSrc1[0]
+        add     ecx, 4
+        mov     [esp + SymCryptFdefRawMulAsmFrame_pSrc1], ecx
+
+        xor     ebp, ebp                                            // carry = 0
+
+        mov     edi,[esp + SymCryptFdefRawMulAsmFrame_pDst]
+
+        shl     edx, 2                                              // convert nDigits1 to nWords1
+        sub     edx, 1                                              // the first word is outside the outer loop, decremented here
+        mov     [esp + SymCryptFdefRawMulAsmFrame_nWords1], edx
+
+        mov     ecx,[esp + SymCryptFdefRawMulAsmFrame_nDigits2]
+
+.LSymCryptFdefRawMulAsmLoop1:
+        mov     eax, [esi]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 4]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 4], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 8]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 8], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 12]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 12], eax
+        mov     ebp, edx
+
+        add     esi, 16
+        add     edi, 16
+        sub     ecx,1
+        jnz     .LSymCryptFdefRawMulAsmLoop1
+
+        mov     [edi], edx                  // write last word; no overflow because Dst is at least 2 digits long
+
+
+.LSymCryptFdefRawMulAsmLoopOuter:
+
+        mov     esi, [esp + SymCryptFdefRawMulAsmFrame_pSrc2]
+
+        // Fetch next Src1 word & increment pointer
+        mov     eax, [esp + SymCryptFdefRawMulAsmFrame_pSrc1]
+        mov     ebx, [eax]
+        add     eax, 4
+        mov     [esp + SymCryptFdefRawMulAsmFrame_pSrc1], eax
+
+        xor     ebp, ebp
+
+        // increment pDst & set in edi
+        mov     edi, [esp + SymCryptFdefRawMulAsmFrame_pDst]
+        add     edi, 4
+        mov     [esp + SymCryptFdefRawMulAsmFrame_pDst], edi
+
+        mov     ecx, [esp + SymCryptFdefRawMulAsmFrame_nDigits2]
+
+.LSymCryptFdefRawMulAsmLoop2:
+        mov     eax, [esi]
+        mul     ebx
+        add     eax, [edi]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 4]
+        mul     ebx
+        add     eax, [edi + 4]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 4], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 8]
+        mul     ebx
+        add     eax, [edi + 8]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 8], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 12]
+        mul     ebx
+        add     eax, [edi + 12]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 12], eax
+        mov     ebp, edx
+
+        add     esi, 16
+        add     edi, 16
+        sub     ecx,1
+        jnz     .LSymCryptFdefRawMulAsmLoop2
+
+        mov     [edi], edx
+
+        mov     eax,[esp + SymCryptFdefRawMulAsmFrame_nWords1]
+        sub     eax, 1
+        mov     [esp + SymCryptFdefRawMulAsmFrame_nWords1], eax
+
+        jnz     .LSymCryptFdefRawMulAsmLoopOuter
+
+
+        add     esp, SymCryptFdefRawMulAsmFrame_SaveEbp
+        pop     ebp
+        pop     edi
+        pop     esi
+        pop     ebx
+        ret     12
+
+
+        .globl @SymCryptFdefMontgomeryReduceAsm@12
+        .def @SymCryptFdefMontgomeryReduceAsm@12
+        .scl 2
+        .type 32
+        .endef
+@SymCryptFdefMontgomeryReduceAsm@12:
+//VOID
+//SymCryptFdefMontgomeryReduceAsm(
+//    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+//    _Inout_                         PUINT32                 pSrc,
+//    _Out_                           PUINT32                 pDst )
+
+        // stack frame
+        .set SymCryptFdefMontgomeryReduceAsmFrame_HighCarry,     0
+        .set SymCryptFdefMontgomeryReduceAsmFrame_pSrc,          4
+        .set SymCryptFdefMontgomeryReduceAsmFrame_pModValue,     8
+        .set SymCryptFdefMontgomeryReduceAsmFrame_nWords,        12
+        .set SymCryptFdefMontgomeryReduceAsmFrame_SaveEbp,       16
+        .set SymCryptFdefMontgomeryReduceAsmFrame_SaveEsi,       20
+        .set SymCryptFdefMontgomeryReduceAsmFrame_SaveEdi,       24
+        .set SymCryptFdefMontgomeryReduceAsmFrame_SaveEbx,       28
+        .set SymCryptFdefMontgomeryReduceAsmFrame_ReturnAddress, 32
+        .set SymCryptFdefMontgomeryReduceAsmFrame_pDst,          36
+
+        mov     edi,edi
+        push    ebx
+        push    esi
+        push    edi
+        push    ebp
+
+        sub     esp, SymCryptFdefMontgomeryReduceAsmFrame_SaveEbp
+
+        // ecx = pmMod
+        // edx = pSrc
+
+        mov     eax, [ecx + SymCryptModulusNdigitsOffsetX86]
+        shl     eax, 2
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_nWords], eax
+
+        lea     esi, [ecx + SymCryptModulusValueOffsetX86]
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_pModValue], esi
+
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_pSrc], edx
+        mov     edi, edx
+
+        xor     eax,eax
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_HighCarry], eax     // set high-carry to zero
+
+.LSymCryptFdefMontgomeryReduceOuterLoop:
+        // eax = <undef>
+        // ebx = <undef>
+        // ecx = <undef>
+        // edx = <undef>
+        // esi = start of mod value
+        // edi = pSrc + 4 * loop iteration count
+        // ebp = <undef>
+
+        // compute multiplier for this outer loop iteration.
+        mov     ebx, [esi - SymCryptModulusValueOffsetX86 + SymCryptModulusInv64OffsetX86 ]
+        imul    ebx, [edi]              // word we want to zero out, ebx = multiplier for this inner loop
+
+        mov     ecx, [esi - SymCryptModulusValueOffsetX86 + SymCryptModulusNdigitsOffsetX86]        // ecx = nDigits
+        xor     ebp, ebp        // set carry = 0
+
+.LSymCryptFdefMontgomeryReduceInnerLoop:
+        // eax = mul scratch
+        // ebx = multiplier
+        // ecx = digit counter
+        // edx = mul scratch
+        // esi = running pointer to mod value
+        // edi = running pointer to input/scratch
+        // ebp = carry (32 bits)
+
+        mov     eax, [esi]
+        mul     ebx
+        add     eax, [edi]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 4]
+        mul     ebx
+        add     eax, [edi + 4]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 4], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 8]
+        mul     ebx
+        add     eax, [edi + 8]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 8], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 12]
+        mul     ebx
+        add     eax, [edi + 12]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 12], eax
+        mov     ebp, edx
+
+        add     esi, 16
+        add     edi, 16
+        sub     ecx,1
+        jnz     .LSymCryptFdefMontgomeryReduceInnerLoop
+
+        // Process the last high-word result with the high carry
+        xor     eax, eax
+        add     ebp,[esp + SymCryptFdefMontgomeryReduceAsmFrame_HighCarry]
+        adc     eax, eax            // eax was 0, so this is equal to adc eax, 0
+        add     ebp,[edi]
+        adc     eax, 0
+        mov     [edi], ebp
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_HighCarry], eax
+
+        mov     esi, [esp + SymCryptFdefMontgomeryReduceAsmFrame_pModValue]
+
+        mov     edi, [esp + SymCryptFdefMontgomeryReduceAsmFrame_pSrc]
+        add     edi, 4
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_pSrc], edi
+
+        mov     eax, [esp + SymCryptFdefMontgomeryReduceAsmFrame_nWords]
+        sub     eax, 1
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame_nWords], eax
+
+        jnz     .LSymCryptFdefMontgomeryReduceOuterLoop
+
+        // At this point:
+        // esi = pModValue
+        // edi = pSrc
+        // Additional 1-bit carry word for the pSrc value is in the HighCarry stack variable.
+        // Cy = 0
+
+
+        mov     ecx, [esi - SymCryptModulusValueOffsetX86 + SymCryptModulusNdigitsOffsetX86]    // loop counter
+        mov     edx, [esp + SymCryptFdefMontgomeryReduceAsmFrame_pDst]
+
+        // ecx = nDigits
+
+        // Save some values for the copy loop
+        mov     ebx, ecx            // nDigits
+        mov     ebp, edx            // pDst
+
+        // Carry = 0 from previous sub eax,1 of the outer loop counter
+.LSymCryptFdefMontgomeryReduceAsmSubLoop:
+        // edi = running pointer into pSrc result
+        // esi = running ptr into modulus value
+        // edx = running ptr into pDst
+
+        mov     eax,[edi]
+        sbb     eax,[esi]
+        mov     [edx], eax
+
+        mov     eax,[edi + 4]
+        sbb     eax,[esi + 4]
+        mov     [edx + 4], eax
+
+        mov     eax,[edi + 8]
+        sbb     eax,[esi + 8]
+        mov     [edx + 8], eax
+
+        mov     eax,[edi + 12]
+        sbb     eax,[esi + 12]
+        mov     [edx + 12], eax
+
+        lea     edi, [edi + 16]
+        lea     esi, [esi + 16]
+        lea     edx, [edx + 16]
+        dec     ecx
+        jnz     .LSymCryptFdefMontgomeryReduceAsmSubLoop
+
+        mov     ecx, [esp + SymCryptFdefMontgomeryReduceAsmFrame_HighCarry]
+        sbb     ecx, 0
+        // eax = mask for masked copy
+
+
+        mov     edi, [esp + SymCryptFdefMontgomeryReduceAsmFrame_pSrc]
+
+        // ecx = mask
+.LSymCryptFdefMontgomeryReduceAsmMaskedCopyLoop:
+        // ebx = loop counter
+        // ebp = pDst
+        // edi = pSrc
+        // ecx = mask
+
+        mov     eax, [edi]
+        mov     edx, [ebp]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp], eax
+
+        mov     eax, [edi + 4]
+        mov     edx, [ebp + 4]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp + 4], eax
+
+        mov     eax, [edi + 8]
+        mov     edx, [ebp + 8]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp + 8], eax
+
+        mov     eax, [edi + 12]
+        mov     edx, [ebp + 12]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp + 12], eax
+
+        add     edi, 16
+        add     ebp, 16
+        sub     ebx, 1
+        jnz     .LSymCryptFdefMontgomeryReduceAsmMaskedCopyLoop
+
+        // Done, no return value
+
+        add     esp, SymCryptFdefMontgomeryReduceAsmFrame_SaveEbp
+        pop     ebp
+        pop     edi
+        pop     esi
+        pop     ebx
+
+        ret     4
diff --git a/libs/symcrypt/lib/i386/fdef_asm.cppasm b/libs/symcrypt/lib/i386/fdef_asm.cppasm
new file mode 100644
index 000000000000..7be5af8e2a7b
--- /dev/null
+++ b/libs/symcrypt/lib/i386/fdef_asm.cppasm
@@ -0,0 +1,704 @@
+;
+;  fdef_asm.cppasm     Assembler code for fast arithmetic
+;  Requires C preprocessor to correctly include C_asm_shared.inc and symcrypt_magic.in
+;
+; Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+;
+
+        TITLE   "Fdef_asm"
+        .686P
+;        .XMM           // Do not use XMM in x86 code; there are environments where it is not available
+
+;
+; FPO documentation:
+; The .FPO provides debugging information.
+; This stuff not well documented,
+; but here is the information I've gathered about the arguments to .FPO
+;
+; In order:
+; cdwLocals: Size of local variables, in DWords
+; cdwParams: Size of parameters, in DWords. Given that this is all about
+;            stack stuff, I'm assuming this is only about parameters passed
+;            on the stack.
+; cbProlog : Number of bytes in the prolog code. We have interleaved the
+;            prolog code with work for better performance. Most uses of
+;            .FPO seem to set this value to 0 anyway, which is what we
+;            will do.
+; cbRegs   : # registers saved in the prolog.
+; fUseBP   : 0 if EBP is not used as base pointer, 1 if EBP is used as base pointer
+; cbFrame  : Type of frame.
+;            0 = FPO frame (no frame pointer)
+;            1 = Trap frame (result of a CPU trap event)
+;            2 = TSS frame
+;
+; Having looked at various occurrences of .FPO in the Windows code it
+; seems to be used fairly sloppy, with lots of arguments left 0 even when
+; they probably shouldn't be according to the spec.
+;
+
+
+
+_TEXT   SEGMENT PARA PUBLIC USE32 'CODE'
+        ASSUME  CS:_TEXT, DS:FLAT, SS:FLAT
+
+#include "C_asm_shared.inc"
+#include "symcrypt_magic.inc"
+
+        PUBLIC  @SymCryptFdefRawAddAsm@16
+        PUBLIC  @SymCryptFdefRawSubAsm@16
+        PUBLIC  @SymCryptFdefMaskedCopyAsm@16
+        PUBLIC  @SymCryptFdefRawMulAsm@20
+        PUBLIC  @SymCryptFdefMontgomeryReduceAsm@12
+
+
+BEFORE_PROC     MACRO
+        ;
+        ; Our current x86 compiler inserts 5 0xcc bytes before every function
+        ; and starts every function with a 2-byte NOP.
+        ; This supports hot-patching.
+        ;
+        DB      5 dup (0cch)
+                ENDM
+
+
+
+
+
+        BEFORE_PROC
+@SymCryptFdefRawAddAsm@16 PROC
+;UINT32
+;SYMCRYPT_CALL
+;SymCryptFdefRawAdd(
+;    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+;    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+;    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+;                                                            UINT32      nDigits )
+
+    .FPO(2,2,0,2,0,0)
+
+SymCryptFdefRawAddAsmFrame struct  4, NONUNIQUE
+
+SaveEdi         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pDst            dd      ?
+nDigits         dd      ?
+
+SymCryptFdefRawAddAsmFrame ends
+
+        ; ecx = pSrc1
+        ; edx = pSrc2
+
+        mov     edi,edi
+        push    ebx
+        push    edi
+
+        mov     edi, [esp + SymCryptFdefRawAddAsmFrame.pDst]
+        mov     ebx, [esp + SymCryptFdefRawAddAsmFrame.nDigits]
+
+        xor     eax,eax                     ; Cy = 0
+
+SymCryptFdefRawAddAsmLoop:
+        mov     eax, [ecx]
+        adc     eax, [edx]
+        mov     [edi], eax
+
+        mov     eax, [ecx + 4]
+        adc     eax, [edx + 4]
+        mov     [edi + 4], eax
+
+        mov     eax, [ecx + 8]
+        adc     eax, [edx + 8]
+        mov     [edi + 8], eax
+
+        mov     eax, [ecx + 12]
+        adc     eax, [edx + 12]
+        mov     [edi + 12], eax
+
+        lea     ecx, [ecx + 16]
+        lea     edx, [edx + 16]
+        lea     edi, [edi + 16]
+
+        dec     ebx
+        jnz     SymCryptFdefRawAddAsmLoop
+
+        mov     eax, 0
+        adc     eax, eax
+
+        pop     edi
+        pop     ebx
+        ret     8
+
+@SymCryptFdefRawAddAsm@16 ENDP
+
+
+        BEFORE_PROC
+@SymCryptFdefRawSubAsm@16 PROC
+;UINT32
+;SYMCRYPT_CALL
+;SymCryptFdefRawSub(
+;    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+;    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+;    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+;                                                            UINT32      nDigits )
+
+    .FPO(2,2,0,2,0,0)
+
+SymCryptFdefRawSubAsmFrame struct  4, NONUNIQUE
+
+SaveEdi         dd      ?
+SaveEbx         dd      ?
+ReturnAddress   dd      ?
+pDst            dd      ?
+nDigits         dd      ?
+
+SymCryptFdefRawSubAsmFrame ends
+
+        ; ecx = pSrc1
+        ; edx = pSrc2
+
+        mov     edi,edi
+        push    ebx
+        push    edi
+
+        mov     edi, [esp + SymCryptFdefRawSubAsmFrame.pDst]
+        mov     ebx, [esp + SymCryptFdefRawSubAsmFrame.nDigits]
+
+        xor     eax,eax                     ; Cy = 0
+
+SymCryptFdefRawSubAsmLoop:
+        mov     eax, [ecx]
+        sbb     eax, [edx]
+        mov     [edi], eax
+
+        mov     eax, [ecx + 4]
+        sbb     eax, [edx + 4]
+        mov     [edi + 4], eax
+
+        mov     eax, [ecx + 8]
+        sbb     eax, [edx + 8]
+        mov     [edi + 8], eax
+
+        mov     eax, [ecx + 12]
+        sbb     eax, [edx + 12]
+        mov     [edi + 12], eax
+
+        lea     ecx, [ecx + 16]
+        lea     edx, [edx + 16]
+        lea     edi, [edi + 16]
+
+        dec     ebx
+        jnz     SymCryptFdefRawSubAsmLoop
+
+        mov     eax, 0
+        adc     eax, eax
+
+        pop     edi
+        pop     ebx
+        ret     8
+
+@SymCryptFdefRawSubAsm@16 ENDP
+
+
+        BEFORE_PROC
+@SymCryptFdefMaskedCopyAsm@16 PROC
+;VOID
+;SYMCRYPT_CALL
+;SymCryptFdefMaskedCopy(
+;    _In_reads_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )      PCBYTE      pbSrc,
+;    _InOut_writes_bytes_( nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )  PBYTE       pbDst,
+;                                                                UINT32      nDigits,
+;                                                                UINT32      mask )
+
+    .FPO(3,2,0,3,0,0)
+
+SymCryptFdefMaskedCopyAsmFrame struct  4, NONUNIQUE
+    SaveEdi         dd      ?
+    SaveEsi         dd      ?
+    SaveEbx         dd      ?
+    Returnaddress   dd      ?
+    nDigits         dd      ?
+    vMask           dd      ?
+SymCryptFdefMaskedCopyAsmFrame ends
+
+        mov     edi,edi
+        push    ebx
+        push    esi
+        push    edi
+
+        mov     esi, [esp + SymCryptFdefMaskedCopyAsmFrame.vMask]
+        mov     edi, [esp + SymCryptFdefMaskedCopyAsmFrame.nDigits]
+
+SymCryptFdefMaskedCopyAsmLoop:
+        mov     eax, [ecx]
+        mov     ebx, [edx]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx], eax
+
+        mov     eax, [ecx + 4]
+        mov     ebx, [edx + 4]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx + 4], eax
+
+        mov     eax, [ecx + 8]
+        mov     ebx, [edx + 8]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx + 8], eax
+
+        mov     eax, [ecx + 12]
+        mov     ebx, [edx + 12]
+        xor     eax, ebx
+        and     eax, esi
+        xor     eax, ebx
+        mov     [edx + 12], eax
+
+        add     ecx, 16
+        add     edx, 16
+        sub     edi, 1
+        jnz     SymCryptFdefMaskedCopyAsmLoop
+
+        ; No return value
+        pop     edi
+        pop     esi
+        pop     ebx
+        ret     8
+
+@SymCryptFdefMaskedCopyAsm@16   ENDP
+
+
+
+        BEFORE_PROC
+@SymCryptFdefRawMulAsm@20 PROC
+
+        .FPO( 6, 3, 0, 4, 0, 0)
+
+;VOID
+;SYMCRYPT_CALL
+;SymCryptFdefRawMul(
+;    _In_reads_(nWords1)             PCUINT32    pSrc1,
+;                                    UINT32      nDigits1,
+;    _In_reads_(nWords2)             PCUINT32    pSrc2,
+;                                    UINT32      nDigits2,
+;    _Out_writes_(nWords1 + nWords2) PUINT32     pDst )
+SymCryptFdefRawMulAsmFrame struct  4, NONUNIQUE
+
+        nWords1         dd  ?
+        pSrc1           dd  ?
+        SaveEbp         dd  ?
+        SaveEsi         dd  ?
+        SaveEdi         dd  ?
+        SaveEbx         dd  ?
+        ReturnAddress   dd  ?
+        pSrc2           dd  ?
+        nDigits2        dd  ?
+        pDst            dd  ?
+SymCryptFdefRawMulAsmFrame        ends
+
+        ; Basic code structure:
+        ;   for each word in Src1:
+        ;       Dst += Src2 * word
+        ; Register assignments
+        ;
+        ;   eax = tmp/lower half of mult
+        ;   ebx = multiplicant
+        ;   ecx = loop counter, initialized to nDigits2
+        ;   edx = upper half of mult
+        ;   esi = running pointer into Src2
+        ;   edi = dst
+        ;   ebp = carry
+        ;
+        ;   esp + pSrc1     running pointer into Src1
+        ;   esp +
+
+
+        mov     edi,edi
+        push    ebx
+        push    esi
+        push    edi
+        push    ebp
+        sub     esp, SymCryptFdefRawMulAsmFrame.SaveEbp
+
+        ; ecx = pSrc1
+        ; edx = nDigits1
+
+        mov     esi,[esp + SymCryptFdefRawMulAsmFrame.pSrc2]
+
+        mov     ebx, [ecx]                                          ; ebx = pSrc1[0]
+        add     ecx, 4
+        mov     [esp + SymCryptFdefRawMulAsmFrame.pSrc1], ecx
+
+        xor     ebp, ebp                                            ; carry = 0
+
+        mov     edi,[esp + SymCryptFdefRawMulAsmFrame.pDst]
+
+        shl     edx, 2                                              ; convert nDigits1 to nWords1
+        sub     edx, 1                                              ; the first word is outside the outer loop, decremented here
+        mov     [esp + SymCryptFdefRawMulAsmFrame.nWords1], edx
+
+        mov     ecx,[esp + SymCryptFdefRawMulAsmFrame.nDigits2]
+
+SymCryptFdefRawMulAsmLoop1:
+        mov     eax, [esi]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 4]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 4], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 8]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 8], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 12]
+        mul     ebx
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 12], eax
+        mov     ebp, edx
+
+        add     esi, 16
+        add     edi, 16
+        sub     ecx,1
+        jnz     SymCryptFdefRawMulAsmLoop1
+
+        mov     [edi], edx                  ; write last word; no overflow because Dst is at least 2 digits long
+
+
+SymCryptFdefRawMulAsmLoopOuter:
+
+        mov     esi, [esp + SymCryptFdefRawMulAsmFrame.pSrc2]
+
+        ; Fetch next Src1 word & increment pointer
+        mov     eax, [esp + SymCryptFdefRawMulAsmFrame.pSrc1]
+        mov     ebx, [eax]
+        add     eax, 4
+        mov     [esp + SymCryptFdefRawMulAsmFrame.pSrc1], eax
+
+        xor     ebp, ebp
+
+        ; increment pDst & set in edi
+        mov     edi, [esp + SymCryptFdefRawMulAsmFrame.pDst]
+        add     edi, 4
+        mov     [esp + SymCryptFdefRawMulAsmFrame.pDst], edi
+
+        mov     ecx, [esp + SymCryptFdefRawMulAsmFrame.nDigits2]
+
+SymCryptFdefRawMulAsmLoop2:
+        mov     eax, [esi]
+        mul     ebx
+        add     eax, [edi]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 4]
+        mul     ebx
+        add     eax, [edi + 4]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 4], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 8]
+        mul     ebx
+        add     eax, [edi + 8]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 8], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 12]
+        mul     ebx
+        add     eax, [edi + 12]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 12], eax
+        mov     ebp, edx
+
+        add     esi, 16
+        add     edi, 16
+        sub     ecx,1
+        jnz     SymCryptFdefRawMulAsmLoop2
+
+        mov     [edi], edx
+
+        mov     eax,[esp + SymCryptFdefRawMulAsmFrame.nWords1]
+        sub     eax, 1
+        mov     [esp + SymCryptFdefRawMulAsmFrame.nWords1], eax
+
+        jnz     SymCryptFdefRawMulAsmLoopOuter
+
+
+        add     esp, SymCryptFdefRawMulAsmFrame.SaveEbp
+        pop     ebp
+        pop     edi
+        pop     esi
+        pop     ebx
+        ret     12
+
+@SymCryptFdefRawMulAsm@20 ENDP
+
+
+        BEFORE_PROC
+@SymCryptFdefMontgomeryReduceAsm@12 PROC
+
+        .FPO( 8, 1, 0, 4, 0, 0)
+
+;VOID
+;SymCryptFdefMontgomeryReduceAsm(
+;    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+;    _Inout_                         PUINT32                 pSrc,
+;    _Out_                           PUINT32                 pDst )
+
+SymCryptFdefMontgomeryReduceAsmFrame struct 4, NONUNIQUE
+
+        HighCarry       dd  ?
+        pSrc            dd  ?
+        pModValue       dd  ?
+        nWords          dd  ?
+        SaveEbp         dd  ?       ; # words still to process in outer loop
+        SaveEsi         dd  ?
+        SaveEdi         dd  ?
+        SaveEbx         dd  ?
+        ReturnAddress   dd  ?
+        pDst            dd  ?
+SymCryptFdefMontgomeryReduceAsmFrame        ends
+
+        mov     edi,edi
+        push    ebx
+        push    esi
+        push    edi
+        push    ebp
+
+        sub     esp, SymCryptFdefMontgomeryReduceAsmFrame.SaveEbp
+
+        ; ecx = pmMod
+        ; edx = pSrc
+
+        mov     eax, [ecx + SymCryptModulusNdigitsOffsetX86]
+        shl     eax, 2
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.nWords], eax
+
+        lea     esi, [ecx + SymCryptModulusValueOffsetX86]
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.pModValue], esi
+
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.pSrc], edx
+        mov     edi, edx
+
+        xor     eax,eax
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.HighCarry], eax     ; set high-carry to zero
+
+SymCryptFdefMontgomeryReduceOuterLoop:
+        ; eax = <undef>
+        ; ebx = <undef>
+        ; ecx = <undef>
+        ; edx = <undef>
+        ; esi = start of mod value
+        ; edi = pSrc + 4 * loop iteration count
+        ; ebp = <undef>
+
+        ; compute multiplier for this outer loop iteration.
+        mov     ebx, [esi - SymCryptModulusValueOffsetX86 + SymCryptModulusInv64OffsetX86 ]
+        imul    ebx, [edi]              ; word we want to zero out, ebx = multiplier for this inner loop
+
+        mov     ecx, [esi - SymCryptModulusValueOffsetX86 + SymCryptModulusNdigitsOffsetX86]        ; ecx = nDigits
+        xor     ebp, ebp        ; set carry = 0
+
+SymCryptFdefMontgomeryReduceInnerLoop:
+        ; eax = mul scratch
+        ; ebx = multiplier
+        ; ecx = digit counter
+        ; edx = mul scratch
+        ; esi = running pointer to mod value
+        ; edi = running pointer to input/scratch
+        ; ebp = carry (32 bits)
+
+        mov     eax, [esi]
+        mul     ebx
+        add     eax, [edi]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 4]
+        mul     ebx
+        add     eax, [edi + 4]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 4], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 8]
+        mul     ebx
+        add     eax, [edi + 8]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 8], eax
+        mov     ebp, edx
+
+        mov     eax, [esi + 12]
+        mul     ebx
+        add     eax, [edi + 12]
+        adc     edx, 0
+        add     eax, ebp
+        adc     edx, 0
+        mov     [edi + 12], eax
+        mov     ebp, edx
+
+        add     esi, 16
+        add     edi, 16
+        sub     ecx,1
+        jnz     SymCryptFdefMontgomeryReduceInnerLoop
+
+        ; Process the last high-word result with the high carry
+        xor     eax, eax
+        add     ebp,[esp + SymCryptFdefMontgomeryReduceAsmFrame.HighCarry]
+        adc     eax, eax            ; eax was 0, so this is equal to adc eax, 0
+        add     ebp,[edi]
+        adc     eax, 0
+        mov     [edi], ebp
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.HighCarry], eax
+
+        mov     esi, [esp + SymCryptFdefMontgomeryReduceAsmFrame.pModValue]
+
+        mov     edi, [esp + SymCryptFdefMontgomeryReduceAsmFrame.pSrc]
+        add     edi, 4
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.pSrc], edi
+
+        mov     eax, [esp + SymCryptFdefMontgomeryReduceAsmFrame.nWords]
+        sub     eax, 1
+        mov     [esp + SymCryptFdefMontgomeryReduceAsmFrame.nWords], eax
+
+        jnz     SymCryptFdefMontgomeryReduceOuterLoop
+
+        ; At this point:
+        ; esi = pModValue
+        ; edi = pSrc
+        ; Additional 1-bit carry word for the pSrc value is in the HighCarry stack variable.
+        ; Cy = 0
+
+
+        mov     ecx, [esi - SymCryptModulusValueOffsetX86 + SymCryptModulusNdigitsOffsetX86]    ; loop counter
+        mov     edx, [esp + SymCryptFdefMontgomeryReduceAsmFrame.pDst];
+
+        ; ecx = nDigits
+
+        ; Save some values for the copy loop
+        mov     ebx, ecx            ; nDigits
+        mov     ebp, edx            ; pDst
+
+        ; Carry = 0 from previous sub eax,1 of the outer loop counter
+SymCryptFdefMontgomeryReduceAsmSubLoop:
+        ; edi = running pointer into pSrc result
+        ; esi = running ptr into modulus value
+        ; edx = running ptr into pDst
+
+        mov     eax,[edi]
+        sbb     eax,[esi]
+        mov     [edx], eax
+
+        mov     eax,[edi + 4]
+        sbb     eax,[esi + 4]
+        mov     [edx + 4], eax
+
+        mov     eax,[edi + 8]
+        sbb     eax,[esi + 8]
+        mov     [edx + 8], eax
+
+        mov     eax,[edi + 12]
+        sbb     eax,[esi + 12]
+        mov     [edx + 12], eax
+
+        lea     edi, [edi + 16]
+        lea     esi, [esi + 16]
+        lea     edx, [edx + 16]
+        dec     ecx
+        jnz     SymCryptFdefMontgomeryReduceAsmSubLoop
+
+        mov     ecx, [esp + SymCryptFdefMontgomeryReduceAsmFrame.HighCarry]
+        sbb     ecx, 0
+        ; eax = mask for masked copy
+
+
+        mov     edi, [esp + SymCryptFdefMontgomeryReduceAsmFrame.pSrc]
+
+        ; ecx = mask
+SymCryptFdefMontgomeryReduceAsmMaskedCopyLoop:
+        ; ebx = loop counter
+        ; ebp = pDst
+        ; edi = pSrc
+        ; ecx = mask
+
+        mov     eax, [edi]
+        mov     edx, [ebp]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp], eax
+
+        mov     eax, [edi + 4]
+        mov     edx, [ebp + 4]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp + 4], eax
+
+        mov     eax, [edi + 8]
+        mov     edx, [ebp + 8]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp + 8], eax
+
+        mov     eax, [edi + 12]
+        mov     edx, [ebp + 12]
+        xor     eax, edx
+        and     eax, ecx
+        xor     eax, edx
+        mov     [ebp + 12], eax
+
+        add     edi, 16
+        add     ebp, 16
+        sub     ebx, 1
+        jnz     SymCryptFdefMontgomeryReduceAsmMaskedCopyLoop
+
+        ; Done, no return value
+
+        add     esp, SymCryptFdefMontgomeryReduceAsmFrame.SaveEbp
+        pop     ebp
+        pop     edi
+        pop     esi
+        pop     ebx
+
+        ret     4
+
+@SymCryptFdefMontgomeryReduceAsm@12 ENDP
+
+_TEXT   ENDS
+
+END
diff --git a/libs/symcrypt/lib/i386/wipe.S b/libs/symcrypt/lib/i386/wipe.S
new file mode 100644
index 000000000000..d6b4515c32ba
--- /dev/null
+++ b/libs/symcrypt/lib/i386/wipe.S
@@ -0,0 +1,132 @@
+//
+// Wipe.asm
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// Manually converted to GNU as format for Wine
+//
+
+#include "symcryptasm_shared.cppasm"
+
+// VOID
+// SYMCRYPT_CALL
+// SymCryptWipe( _Out_writes_bytes_( cbData )   PVOID  pbData,
+//                                        SIZE_T cbData )
+
+        .globl @SymCryptWipeAsm@8
+        .def @SymCryptWipeAsm@8
+        .scl 2
+        .type 32
+        .endef
+@SymCryptWipeAsm@8:
+
+        // ecx = pbData
+        // edx = cbData
+
+        //
+        // This function will handle any alignment of pbData and any size, but it is optimized for
+        // the case where pbData is 4-aligned, and cbData is a multiple of 4.
+        //
+
+        xor     eax,eax
+        cmp     edx,8
+        jb      .LSymCryptWipeAsmSmall     // if cbData < 8, this is a rare case
+
+        test    ecx,7
+        jnz     .LSymCryptWipeAsmUnaligned // if data pointer is unaligned, we jump to the code that aligns the pointer
+                                           // For well-optimized code the aligned case is the common one, and that is
+                                           // the fall-through.
+
+.LSymCryptWipeAsmAligned:
+        //
+        // Here ecx is aligned, and edx contains the # bytes left to wipe, and edx >= 8
+        //
+        // Our loop wipes in 8-byte increments;
+        //
+        sub     edx,8
+
+        .balign   16
+.LSymCryptWipeAsmLoop:
+        mov     [ecx],eax
+        mov     [ecx+4],eax           // Wipe 8 bytes
+        add     ecx,8
+        sub     edx,8
+        ja      .LSymCryptWipeAsmLoop   // Loop if we have >8 byte to wipe. <= 8 bytes is handled by the code below
+
+        //
+        // The end of the buffer to wipe is at ecx + edx + 8 as
+        // the number of bytes we wiped is 8 less than the number we subtracted from edx.
+        //
+        // We know we had 8 or more bytes to wipe, so we just wipe the last 8 bytes.
+        // This avoids a lot of conditional jumping etc.
+        //
+        mov     [ecx+edx+8-8],eax
+        mov     [ecx+edx+8-4],eax
+
+.LSymCryptWipeAsmDone:
+        ret
+
+.LSymCryptWipeAsmUnaligned:
+        //
+        // At this point we know that cbData(rdx) >= 8 and pbData(rcx) is unaligned.
+        // We wipe 4 bytes and set our pointer & length to the remaining aligned buffer
+        //
+        mov     [ecx],eax
+        mov     [ecx+4],eax
+        sub     eax,ecx
+        and     eax,7
+        add     ecx,eax
+        sub     edx,eax
+        xor     eax,eax
+
+        //
+        // edx = cbData could be less than 4 now. If it is, go directly to the
+        // tail wiping.
+        //
+        cmp     edx,8
+        jae     .LSymCryptWipeAsmAligned  // if cbData >= 8, do aligned wipes
+
+.LSymCryptWipeAsmSmall:
+        jmp     dword ptr [.LSymCryptWipeAsmSmallTable + 4*edx]
+
+.LSymCryptWipeAsmT4:
+        mov     [ecx],eax
+.LSymCryptWipeAsmT0:
+        ret
+
+.LSymCryptWipeAsmT1:
+        mov     [ecx],al
+        ret
+
+.LSymCryptWipeAsmT3:
+        mov     [ecx+2],al
+.LSymCryptWipeAsmT2:
+        mov     [ecx],ax
+        ret
+
+.LSymCryptWipeAsmT5:
+        mov     [ecx],eax
+        mov     [ecx+1],eax
+        ret
+
+.LSymCryptWipeAsmT6:
+        mov     [ecx],eax
+        mov     [ecx+2],eax
+        ret
+
+.LSymCryptWipeAsmT7:
+        mov     [ecx],eax
+        mov     [ecx+3],eax
+        ret
+
+
+        .balign   4
+.LSymCryptWipeAsmSmallTable:
+        .long      .LSymCryptWipeAsmT0
+        .long      .LSymCryptWipeAsmT1
+        .long      .LSymCryptWipeAsmT2
+        .long      .LSymCryptWipeAsmT3
+        .long      .LSymCryptWipeAsmT4
+        .long      .LSymCryptWipeAsmT5
+        .long      .LSymCryptWipeAsmT6
+        .long      .LSymCryptWipeAsmT7
diff --git a/libs/symcrypt/lib/i386/wipe.asm b/libs/symcrypt/lib/i386/wipe.asm
new file mode 100644
index 000000000000..178149189b19
--- /dev/null
+++ b/libs/symcrypt/lib/i386/wipe.asm
@@ -0,0 +1,171 @@
+;
+; Wipe.asm
+;
+; Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+;
+
+        TITLE   wipe.asm
+
+
+        .686
+        .xmm
+
+_TEXT   SEGMENT PARA PUBLIC USE32 'CODE'
+        ASSUME  CS:_TEXT, DS:FLAT, SS:FLAT
+
+        PUBLIC  @SymCryptWipeAsm@8
+
+;VOID
+;SYMCRYPT_CALL
+;SymCryptWipe( _Out_writes_bytes_( cbData )   PVOID  pbData,
+;                                       SIZE_T cbData )
+
+@SymCryptWipeAsm@8    PROC
+
+        ;
+        ; The .FPO provides debugging information for stack frames that do not use
+        ; ebp as a base pointer.
+        ; This stuff not well documented,
+        ; but here is the information I've gathered about the arguments to .FPO
+        ;
+        ; In order:
+        ; cdwLocals: Size of local variables, in DWords
+        ; cdwParams: Size of parameters, in DWords. Given that this is all about
+        ;            stack stuff, I'm assuming this is only about parameters passed
+        ;            on the stack.
+        ; cbProlog : Number of bytes in the prolog code. We sometimes interleaved the
+        ;            prolog code with work for better performance. Most uses of
+        ;            .FPO seem to set this value to 0.
+        ;            The debugger seems to work if the prolog defined by this value
+        ;            contains all the stack adjustments.
+        ; cbRegs   : # registers saved in the prolog. 4 in our case
+        ; fUseBP   : 0 if EBP is not used as base pointer, 1 if EBP is used as base pointer
+        ; cbFrame  : Type of frame.
+        ;            0 = FPO frame (no frame pointer)
+        ;            1 = Trap frame (result of a CPU trap event)
+        ;            2 = TSS frame
+        ;
+        ; Having looked at various occurrences of .FPO in the Windows code it
+        ; seems to be used fairly sloppy, with lots of arguments left 0 even when
+        ; they probably shouldn't be according to the spec.
+        ;
+        .FPO(0,0,0,0,0,0)       ; 3 byte prolog (covers esp adjustment only)
+
+        ; ecx = pbData
+        ; edx = cbData
+
+        ;
+        ; This function will handle any alignment of pbData and any size, but it is optimized for
+        ; the case where pbData is 4-aligned, and cbData is a multiple of 4.
+        ;
+
+        xor     eax,eax
+        cmp     edx,8
+        jb      SymCryptWipeAsmSmall     ; if cbData < 8, this is a rare case
+
+        test    ecx,7
+        jnz     SymCryptWipeAsmUnAligned; if data pointer is unaligned, we jump to the code that aligns the pointer
+                                        ; For well-optimized code the aligned case is the common one, and that is
+                                        ; the fall-through.
+
+SymCryptWipeAsmAligned:
+        ;
+        ; Here ecx is aligned, and edx contains the # bytes left to wipe, and edx >= 8
+        ;
+        ; Our loop wipes in 8-byte increments;
+        ;
+        sub     edx,8
+
+        align   16
+SymCryptWipeAsmLoop:
+        mov     [ecx],eax
+        mov     [ecx+4],eax           ; Wipe 8 bytes
+        add     ecx,8
+        sub     edx,8
+        ja      SymCryptWipeAsmLoop   ; Loop if we have >8 byte to wipe. <= 8 bytes is handled by the code below
+
+        ;
+        ; The end of the buffer to wipe is at ecx + edx + 8 as
+        ; the number of bytes we wiped is 8 less than the number we subtracted from edx.
+        ;
+        ; We know we had 8 or more bytes to wipe, so we just wipe the last 8 bytes.
+        ; This avoids a lot of conditional jumping etc.
+        ;
+        mov     [ecx+edx+8-8],eax
+        mov     [ecx+edx+8-4],eax
+
+SymCryptWipeAsmDone:
+        ret
+
+SymCryptWipeAsmUnaligned:
+        ;
+        ; At this point we know that cbData(rdx) >= 8 and pbData(rcx) is unaligned.
+        ; We wipe 4 bytes and set our pointer & length to the remaining aligned buffer
+        ;
+        mov     [ecx],eax
+        mov     [ecx+4],eax
+        sub     eax,ecx
+        and     eax,7
+        add     ecx,eax
+        sub     edx,eax
+        xor     eax,eax
+
+        ;
+        ; edx = cbData could be less than 4 now. If it is, go directly to the
+        ; tail wiping.
+        ;
+        cmp     edx,8
+        jae     SymCryptWipeAsmAligned  ; if cbData >= 8, do aligned wipes
+
+SymCryptWipeAsmSmall:
+        jmp     dword ptr [SymCryptWipeAsmSmallTable + 4*edx]
+
+SymCryptWipeAsmT4:
+        mov     [ecx],eax
+SymCryptWipeAsmT0:
+        ret
+
+SymCryptWipeAsmT1:
+        mov     [ecx],al
+        ret
+
+SymCryptWipeAsmT3:
+        mov     [ecx+2],al
+SymCryptWipeAsmT2:
+        mov     [ecx],ax
+        ret
+
+SymCryptWipeAsmT5:
+        mov     [ecx],eax
+        mov     [ecx+1],eax
+        ret
+
+SymCryptWipeAsmT6:
+        mov     [ecx],eax
+        mov     [ecx+2],eax
+        ret
+
+SymCryptWipeAsmT7:
+        mov     [ecx],eax
+        mov     [ecx+3],eax
+        ret
+
+
+        align   4
+SymCryptWipeAsmSmallTable   label   dword
+        dd      SymCryptWipeAsmT0
+        dd      SymCryptWipeAsmT1
+        dd      SymCryptWipeAsmT2
+        dd      SymCryptWipeAsmT3
+        dd      SymCryptWipeAsmT4
+        dd      SymCryptWipeAsmT5
+        dd      SymCryptWipeAsmT6
+        dd      SymCryptWipeAsmT7
+
+
+
+@SymCryptWipeAsm@8    ENDP
+
+_TEXT           ENDS
+
+END
diff --git a/libs/symcrypt/lib/kmac.c b/libs/symcrypt/lib/kmac.c
new file mode 100644
index 000000000000..ae82af143c48
--- /dev/null
+++ b/libs/symcrypt/lib/kmac.c
@@ -0,0 +1,123 @@
+//
+// kmac.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+
+//
+//  KMAC128
+//
+#define Alg Kmac128
+#define ALG KMAC128
+#define SYMCRYPT_CSHAKEXXX_INIT             SymCryptCShake128Init
+#define SYMCRYPT_CSHAKEXXX_STATE            SYMCRYPT_CSHAKE128_STATE
+#define SYMCRYPT_KMACXXX_RESULT_SIZE        SYMCRYPT_KMAC128_RESULT_SIZE
+#include "kmac_pattern.c"
+#undef SYMCRYPT_KMACXXX_RESULT_SIZE
+#undef SYMCRYPT_CSHAKEXXX_STATE
+#undef SYMCRYPT_CSHAKEXXX_INIT
+#undef ALG
+#undef Alg
+
+// MAC interface
+const SYMCRYPT_MAC SymCryptKmac128Algorithm_Default = {
+    SymCryptKmac128ExpandKey,
+    SymCryptKmac128Init,
+    SymCryptKmac128Append,
+    SymCryptKmac128Result,
+    sizeof(SYMCRYPT_KMAC128_EXPANDED_KEY),
+    sizeof(SYMCRYPT_KMAC128_STATE),
+    SYMCRYPT_KMAC128_RESULT_SIZE,
+    NULL,   // ppHashAlgorithm
+    0,      // outerChainingStateOffset
+};
+
+const PCSYMCRYPT_MAC SymCryptKmac128Algorithm = &SymCryptKmac128Algorithm_Default;
+
+static const BYTE kmac128KATAnswer[SYMCRYPT_KMAC128_RESULT_SIZE] = {
+    0xea, 0xe9, 0xde, 0xd3, 0xee, 0x2f, 0x34, 0x8a,
+    0xd6, 0xd2, 0xcb, 0x70, 0x4b, 0xba, 0xd4, 0x47,
+    0x15, 0x32, 0x46, 0x82, 0x8e, 0x41, 0x3a, 0xf5,
+    0xf5, 0x62, 0x96, 0x1a, 0xf7, 0x67, 0x48, 0xc1
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac128Selftest(void)
+{
+    BYTE result[SYMCRYPT_KMAC128_RESULT_SIZE];
+    static const unsigned char Sstr[] = { 'S' };
+    SYMCRYPT_KMAC128_EXPANDED_KEY expandedKey;
+
+    SymCryptKmac128ExpandKeyEx(&expandedKey, SymCryptTestKey32, 16, Sstr, sizeof(Sstr));
+
+    SymCryptKmac128(&expandedKey, SymCryptTestMsg16, sizeof(SymCryptTestMsg16), result);
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, kmac128KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('kmac');
+    }
+}
+
+
+//
+//  KMAC256
+//
+#define Alg Kmac256
+#define ALG KMAC256
+#define SYMCRYPT_CSHAKEXXX_INIT             SymCryptCShake256Init
+#define SYMCRYPT_CSHAKEXXX_STATE            SYMCRYPT_CSHAKE256_STATE
+#define SYMCRYPT_KMACXXX_RESULT_SIZE        SYMCRYPT_KMAC256_RESULT_SIZE
+#include "kmac_pattern.c"
+#undef SYMCRYPT_KMACXXX_RESULT_SIZE
+#undef SYMCRYPT_CSHAKEXXX_STATE
+#undef SYMCRYPT_CSHAKEXXX_INIT
+#undef ALG
+#undef Alg
+
+// MAC interface
+const SYMCRYPT_MAC SymCryptKmac256Algorithm_Default = {
+    SymCryptKmac256ExpandKey,
+    SymCryptKmac256Init,
+    SymCryptKmac256Append,
+    SymCryptKmac256Result,
+    sizeof(SYMCRYPT_KMAC256_EXPANDED_KEY),
+    sizeof(SYMCRYPT_KMAC256_STATE),
+    SYMCRYPT_KMAC256_RESULT_SIZE,
+    NULL,   // ppHashAlgorithm
+    0,      // outerChainingStateOffset
+};
+
+const PCSYMCRYPT_MAC SymCryptKmac256Algorithm = &SymCryptKmac256Algorithm_Default;
+
+static const BYTE kmac256KATAnswer[SYMCRYPT_KMAC256_RESULT_SIZE] = {
+    0xa9, 0x1d, 0x09, 0x00, 0x71, 0x0c, 0x63, 0xc5, 0x0f, 0xb6, 0x4d, 0xfa, 0xd8, 0x75, 0x4d, 0x78,
+    0x2d, 0xc0, 0x82, 0x4b, 0x87, 0x97, 0xda, 0xf2, 0x36, 0xde, 0xe9, 0x35, 0x69, 0x2e, 0x50, 0x81,
+    0x0a, 0xea, 0x3b, 0x05, 0xaf, 0x1b, 0x82, 0x3b, 0xc8, 0xa1, 0x9e, 0xe9, 0x9c, 0x5f, 0xd5, 0x5a,
+    0x20, 0x92, 0x89, 0x46, 0xa4, 0xe4, 0x1a, 0xdd, 0x3d, 0xb6, 0x47, 0x4d, 0xf2, 0xa5, 0xfc, 0x73
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptKmac256Selftest(void)
+{
+    BYTE result[SYMCRYPT_KMAC256_RESULT_SIZE];
+    static const unsigned char Sstr[] = { 'S' };
+    SYMCRYPT_KMAC256_EXPANDED_KEY expandedKey;
+
+    SymCryptKmac256ExpandKeyEx(&expandedKey, SymCryptTestKey32, 32, Sstr, sizeof(Sstr));
+
+    SymCryptKmac256(&expandedKey, SymCryptTestMsg16, sizeof(SymCryptTestMsg16), result);
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, kmac256KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('kmac');
+    }
+}
diff --git a/libs/symcrypt/lib/kmac_pattern.c b/libs/symcrypt/lib/kmac_pattern.c
new file mode 100644
index 000000000000..1b4aa0604bc1
--- /dev/null
+++ b/libs/symcrypt/lib/kmac_pattern.c
@@ -0,0 +1,218 @@
+//
+// kmac_pattern.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+//
+// This source file implements KMAC128 and KMAC256
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+//
+// SymCryptKmac
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_Xxx(
+        _In_                                                PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbInput )                         PCBYTE  pbInput,
+                                                            SIZE_T  cbInput,
+        _Out_writes_bytes_( SYMCRYPT_KMACXXX_RESULT_SIZE )  PBYTE   pbResult)
+{
+    SYMCRYPT_XXX_STATE state;
+
+    SYMCRYPT_XxxInit(&state, pExpandedKey);
+    SYMCRYPT_XxxAppend(&state, pbInput, cbInput);
+    SYMCRYPT_XxxResult(&state, pbResult);
+}
+
+//
+// SymCryptKmacEx
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxEx(
+        _In_                            PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbInput )     PCBYTE  pbInput,
+                                        SIZE_T  cbInput,
+        _Out_writes_bytes_( cbResult )  PBYTE   pbResult,
+                                        SIZE_T  cbResult)
+{
+    SYMCRYPT_XXX_STATE state;
+
+    SYMCRYPT_XxxInit(&state, pExpandedKey);
+    SYMCRYPT_XxxAppend(&state, pbInput, cbInput);
+    SYMCRYPT_XxxResultEx(&state, pbResult, cbResult);
+}
+
+
+//
+// SymCryptKmacExpandKey
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SYMCRYPT_XxxExpandKey(
+    _Out_                       PSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+    _In_reads_bytes_(cbKey)     PCBYTE  pbKey,
+                                SIZE_T  cbKey)
+{
+    return SYMCRYPT_XxxExpandKeyEx(pExpandedKey, pbKey, cbKey, NULL, 0);
+}
+
+//
+// SymCryptKmacExpandKeyEx
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SYMCRYPT_XxxExpandKeyEx(
+        _Out_                                       PSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey,
+        _In_reads_bytes_( cbKey )                   PCBYTE  pbKey,
+                                                    SIZE_T  cbKey,
+        _In_reads_bytes_( cbCustomizationString )   PCBYTE  pbCustomizationString,
+                                                    SIZE_T  cbCustomizationString)
+{
+    static const BYTE nameString[] = { 0x4b, 0x4d, 0x41, 0x43 };  // "KMAC"
+
+    C_ASSERT( sizeof(SYMCRYPT_XXX_EXPANDED_KEY) == sizeof(SYMCRYPT_CSHAKEXXX_STATE) );
+
+    SYMCRYPT_CSHAKEXXX_INIT( (SYMCRYPT_CSHAKEXXX_STATE*)pExpandedKey, nameString, sizeof(nameString), pbCustomizationString, cbCustomizationString);
+
+    SYMCRYPT_KECCAK_STATE* pks = &pExpandedKey->ks;
+
+    // byte_pad( encode_string( K ) )
+    SymCryptKeccakAppendEncodeTimes8(pks, pks->inputBlockSize / 8, TRUE);
+    SymCryptKeccakAppendEncodedString(pks, pbKey, cbKey);
+
+    if (pks->stateIndex != 0)
+    {
+        SymCryptKeccakZeroAppendBlock(pks);
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+//
+// SymCryptKmacInit
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxInit(
+    _Out_   PSYMCRYPT_XXX_STATE         pState,
+    _In_    PCSYMCRYPT_XXX_EXPANDED_KEY pExpandedKey)
+{
+    C_ASSERT(sizeof(*pState) == sizeof(*pExpandedKey));
+
+    SYMCRYPT_CHECK_MAGIC(pExpandedKey);
+    memcpy(pState, pExpandedKey, sizeof(*pState));
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+//
+// SymCryptKmacAppend
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxAppend(
+    _Inout_                 PSYMCRYPT_XXX_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbData,
+                            SIZE_T              cbData )
+{
+    SYMCRYPT_ASSERT(!pState->ks.squeezeMode);
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+//
+// SymCryptKmacExtract
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxExtract(
+    _Inout_                     PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_( cbOutput )    PBYTE               pbOutput,
+                                SIZE_T              cbOutput,
+                                BOOLEAN             bWipe)
+{
+    // This function uses KMAC in XOF mode.
+    //
+    // If this is the first time Extract is being called, append right_encode(0)
+    // to indicate that we're in XOF mode. This padding will be applied only once
+    // as SymCryptKeccakExtract will transition the state to squeeze mode.
+    if (!pState->ks.squeezeMode)
+    {
+        SymCryptKeccakAppendEncodeTimes8(&pState->ks, 0, FALSE);
+    }
+
+    SymCryptKeccakExtract(&pState->ks, pbOutput, cbOutput, bWipe);
+}
+
+//
+// SymCryptKmacResult
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxResult(
+    _Inout_                                         PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_( SYMCRYPT_KMACXXX_RESULT_SIZE )    PBYTE               pbOutput)
+{
+    SYMCRYPT_XxxResultEx(pState, pbOutput, SYMCRYPT_KMACXXX_RESULT_SIZE);
+}
+
+
+//
+// SymCryptKmacResultEx
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxResultEx(
+    _Inout_                     PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_( cbOutput )    PBYTE               pbOutput,
+                                SIZE_T              cbOutput)
+{
+    // Result and ResultEx functions are used to extract data only once.
+    // KMAC requires the output length to be encoded and appended to the
+    // end of the input before the state switches to squeeze mode.
+    //
+    // If Result or ResultEx is called after an Extract call with bWipe=FALSE,
+    // this means KMAC was used in XOF mode and length padding has already been
+    // applied. In this case, Result and ResultEx functions extract data one last
+    // time in XOF mode and wipe the state afterwards.
+
+    if (!pState->ks.squeezeMode)
+    {
+        // Append right_encode(L)
+        SymCryptKeccakAppendEncodeTimes8(&pState->ks, cbOutput, FALSE);
+    }
+
+    SymCryptKeccakExtract(&pState->ks, pbOutput, cbOutput, TRUE);
+}
+
+//
+// SymCryptKmacKeyCopy
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxKeyCopy(_In_ PCSYMCRYPT_XXX_EXPANDED_KEY pSrc, _Out_ PSYMCRYPT_XXX_EXPANDED_KEY pDst)
+{
+    SYMCRYPT_CHECK_MAGIC(pSrc);
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC(pDst);
+}
+
+//
+// SymCryptKmacStateCopy
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxStateCopy(_In_ const SYMCRYPT_XXX_STATE* pSrc, _Out_ SYMCRYPT_XXX_STATE* pDst)
+{
+    SYMCRYPT_CHECK_MAGIC(pSrc);
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC(pDst);
+}
diff --git a/libs/symcrypt/lib/libmain.c b/libs/symcrypt/lib/libmain.c
new file mode 100644
index 000000000000..03ea5254268a
--- /dev/null
+++ b/libs/symcrypt/lib/libmain.c
@@ -0,0 +1,539 @@
+//
+// libmain.c
+// General routines for the SymCrypt library
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#include "C_asm_shared.inc"
+
+#include "buildInfo.h"
+
+// The following global g_SymCryptFlags has to be at least 32
+// bits because the iOS environment has interlocked function
+// support for variables of size at least 32 bits.
+// The relevant function is OSAtomicOr32Barrier.
+UINT32 g_SymCryptFlags = 0;
+
+SYMCRYPT_CPU_FEATURES g_SymCryptCpuFeaturesNotPresent = (SYMCRYPT_CPU_FEATURES) ~0;
+SYMCRYPT_CPU_FEATURES g_SymCryptCpuFeaturesPresentCheck = 0;
+
+#if SYMCRYPT_DEBUG
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptLibraryWasNotInitialized(void)
+{
+    SymCryptFatal( 'init' );    // Function name helps figure out what the problem is.
+}
+
+#endif
+
+const CHAR * const SymCryptBuildString =
+        "v" SYMCRYPT_BUILD_INFO_VERSION
+        "_" SYMCRYPT_BUILD_INFO_BRANCH
+        "_" SYMCRYPT_BUILD_INFO_COMMIT
+        "_" SYMCRYPT_BUILD_INFO_TIMESTAMP;
+
+VOID
+SYMCRYPT_CALL
+SymCryptInitEnvCommon( UINT32 version )
+// Returns TRUE if the initialization steps have to be performed.
+{
+    UINT32 tmp;
+
+    const CHAR * p;
+
+    // Assertion that verifies that the calling application was compiled with
+    // the same version header files as the library.
+    if( version != SYMCRYPT_API_VERSION )
+    {
+        SymCryptFatal( 'apiv' );
+    }
+
+    //
+    // Use an interlocked to set the flag in case we add other flags
+    // that are modified by different threads.
+    //
+    SYMCRYPT_ATOMIC_OR32_PRE_RELAXED( &g_SymCryptFlags, SYMCRYPT_FLAG_LIB_INITIALIZED );
+
+    //
+    // Do a forced write of our code version. This ensures that the code
+    // version is part of the binary, so we can look at a binary and figure
+    // out which version of SymCrypt it was linked with.
+    //
+    SYMCRYPT_FORCE_WRITE32( &tmp, SYMCRYPT_API_VERSION );
+
+    //
+    // Force the build string to be in memory, because otherwise the
+    // compiler might get smart and remove it.
+    // This ensures we can always track back to the SymCrypt source code from
+    // any binary that links this library
+    //
+    for( p = SymCryptBuildString; *p!=0; p++ )
+    {
+        SYMCRYPT_FORCE_WRITE8( (PBYTE) &tmp, *p );
+    }
+
+    //
+    // Make an inverted copy of the CPU detection results.
+    // This helps us diagnose corruption of our flags
+    // Force-write otherwise the compiler optimizes it away
+    //
+    SYMCRYPT_FORCE_WRITE32( &g_SymCryptCpuFeaturesPresentCheck, ~g_SymCryptCpuFeaturesNotPresent );
+
+    //
+    // Test that the C and assembler code agree on the various structure member offsets.
+    // This gets optimized away in FRE builds as all the values are compile-time computable.
+    //
+#define SYMCRYPT_CHECK_ASM_OFFSET( a, b ) if( (a) != (b) ) {SymCryptFatal( b );}
+    SYMCRYPT_CHECK_ASM_OFFSETS;
+#undef SYMCRYPT_CHECK_ASM_OFFSET
+}
+
+_Analysis_noreturn_
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptFatalHang( UINT32 fatalCode )
+//
+// This function is used by the environment-specific fatal code
+// as a last resort when none of the other fatal methods work.
+//
+{
+    UINT32   fcode;
+
+    //
+    // Put the fatal code in a location we can find
+    //
+    SYMCRYPT_FORCE_WRITE32( &fcode, fatalCode );
+
+fatalInfiniteLoop:
+    goto fatalInfiniteLoop;
+}
+
+#ifdef __WINE_PE_BUILD
+
+VOID
+SYMCRYPT_CALL
+SymCryptWipeAsm( _Out_writes_bytes_( cbData ) PVOID pbData, SIZE_T cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptWipe( _Out_writes_bytes_( cbData ) PVOID pbData, SIZE_T cbData )
+{
+    SymCryptWipeAsm( pbData, cbData );
+}
+
+#else
+//
+// Generic but slow wipe routine.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptWipe( _Out_writes_bytes_( cbData ) PVOID pbData, SIZE_T cbData )
+{
+    volatile BYTE * p = (volatile BYTE *) pbData;
+    SIZE_T i;
+
+    for( i=0; i<cbData; i++ ){
+        p[i] = 0;
+    }
+
+}
+#endif
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM
+VOID
+SYMCRYPT_CALL
+SymCryptXorBytes(
+    _In_reads_( cbBytes )   PCBYTE  pbSrc1,
+    _In_reads_( cbBytes )   PCBYTE  pbSrc2,
+    _Out_writes_( cbBytes ) PBYTE   pbResult,
+                            SIZE_T  cbBytes )
+{
+    SIZE_T i;
+
+    if( cbBytes == 16 )
+    {
+        PCUINT32 s1 = (PCUINT32) pbSrc1;
+        PCUINT32 s2 = (PCUINT32) pbSrc2;
+        PUINT32 d = (PUINT32) pbResult;
+
+        d[0] = s1[0] ^ s2[0];
+        d[1] = s1[1] ^ s2[1];
+        d[2] = s1[2] ^ s2[2];
+        d[3] = s1[3] ^ s2[3];
+    }
+    else
+    {
+        i = 0;
+        while( i + 3 < cbBytes )
+        {
+            *(UINT32 *)&pbResult[i] = *(UINT32 *)&pbSrc1[i] ^ *(UINT32 *)&pbSrc2[i];
+            i += 4;
+        }
+
+        while( i < cbBytes )
+        {
+            pbResult[i] = pbSrc1[i] ^ pbSrc2[i];
+            i++;
+        }
+    }
+}
+
+#elif SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+
+VOID
+SYMCRYPT_CALL
+SymCryptXorBytes(
+    _In_reads_( cbBytes )   PCBYTE  pbSrc1,
+    _In_reads_( cbBytes )   PCBYTE  pbSrc2,
+    _Out_writes_( cbBytes ) PBYTE   pbResult,
+                            SIZE_T  cbBytes )
+{
+    if( cbBytes == 16 )
+    {
+        PCUINT64 s1 = (PCUINT64) pbSrc1;
+        PCUINT64 s2 = (PCUINT64) pbSrc2;
+        PUINT64  d = (PUINT64) pbResult;
+
+        d[0] = s1[0] ^ s2[0];
+        d[1] = s1[1] ^ s2[1];
+    }
+    else
+    {
+        while( cbBytes >= 8 )
+        {
+            *(UINT64 *)pbResult = *(UINT64 *)pbSrc1 ^ *(UINT64 *)pbSrc2;
+            pbSrc1 += 8;
+            pbSrc2 += 8;
+            pbResult += 8;
+            cbBytes -= 8;
+        }
+
+        while( cbBytes > 0 )
+        {
+            *pbResult = *pbSrc1 ^ *pbSrc2;
+            pbResult++;
+            pbSrc1++;
+            pbSrc2++;
+            cbBytes--;
+        }
+    }
+}
+
+
+#else
+//
+// Generic code
+//
+VOID
+SYMCRYPT_CALL
+SymCryptXorBytes(
+    _In_reads_( cbBytes )   PCBYTE  pbSrc1,
+    _In_reads_( cbBytes )   PCBYTE  pbSrc2,
+    _Out_writes_( cbBytes ) PBYTE   pbResult,
+                            SIZE_T  cbBytes )
+{
+    SIZE_T i;
+
+    for( i=0; i<cbBytes; i++ )
+    {
+        pbResult[i] = pbSrc1[i] ^ pbSrc2[i];
+    }
+}
+#endif
+
+
+//
+// Generic LSB/MSBfirst load/store code for variable-sized buffers.
+// These implementations are inefficient and not side-channel safe.
+// This is sufficient for the current usage (typically to allow
+// callers to read/write RSA public exponents from/to variable-sized
+// buffers).
+// Consider upgrading them in future.
+//
+
+UINT32
+SymCryptUint32Bitsize( UINT32 value )
+//
+// Some CPUs/compilers have intrinsics for this,
+// but this is portable and works everywhere.
+//
+{
+    UINT32 res;
+
+    res = 0;
+    while( value != 0 )
+    {
+        res += 1;
+        value >>= 1;
+    }
+
+    return res;
+}
+
+UINT32
+SymCryptUint64Bitsize( UINT64 value )
+{
+    UINT32 res;
+    UINT32 upper;
+
+    upper = (UINT32)(value >> 32);
+
+    if( upper == 0 )
+    {
+        res = SymCryptUint32Bitsize( (UINT32) value );
+    } else {
+        res = 32 + SymCryptUint32Bitsize( upper );
+    }
+
+    return res;
+}
+
+UINT32
+SymCryptUint32Bytesize( UINT32 value )
+{
+    if( value == 0 )
+    {
+        return 0;
+    }
+    if( value < 0x100 )
+    {
+        return 1;
+    }
+    if( value < 0x10000 )
+    {
+        return 2;
+    }
+    if( value < 0x1000000 )
+    {
+        return 3;
+    }
+    return 4;
+}
+
+UINT32
+SymCryptUint64Bytesize( UINT64 value )
+{
+    UINT32 res;
+    UINT32 upper;
+
+    upper = (UINT32)(value >> 32);
+
+    if( upper == 0 )
+    {
+        res = SymCryptUint32Bytesize( (UINT32) value );
+    } else {
+        res = 4 + SymCryptUint32Bytesize( upper );
+    }
+
+    return res;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadLsbFirstUint32(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT32 pDst )
+{
+    UINT64 v64;
+    UINT32 v32;
+    SYMCRYPT_ERROR scError;
+
+    scError = SymCryptLoadLsbFirstUint64( pbSrc, cbSrc, &v64 );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    v32 = (UINT32) v64;
+    if( v32 != v64 )
+    {
+        scError = SYMCRYPT_VALUE_TOO_LARGE;
+        goto cleanup;
+    }
+
+    *pDst = v32;
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadLsbFirstUint64(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT64 pDst )
+{
+    UINT64 v;
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    v = 0;
+    pbSrc += cbSrc;
+    while( cbSrc > 8 )
+    {
+        if( *--pbSrc != 0 )
+        {
+            scError = SYMCRYPT_VALUE_TOO_LARGE;
+            goto cleanup;
+        }
+        cbSrc--;
+    }
+
+    while( cbSrc > 0 )
+    {
+        v = (v << 8) | *--pbSrc;
+        cbSrc--;
+    }
+
+    *pDst = v;
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadMsbFirstUint32(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT32 pDst )
+{
+    UINT64 v64;
+    UINT32 v32;
+    SYMCRYPT_ERROR scError;
+
+    scError = SymCryptLoadMsbFirstUint64( pbSrc, cbSrc, &v64 );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    v32 = (UINT32) v64;
+    if( v32 != v64 )
+    {
+        scError = SYMCRYPT_VALUE_TOO_LARGE;
+        goto cleanup;
+    }
+
+    *pDst = v32;
+
+cleanup:
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLoadMsbFirstUint64(
+    _In_reads_( cbSrc ) PCBYTE  pbSrc,
+                        SIZE_T  cbSrc,
+    _Out_               PUINT64 pDst )
+{
+    UINT64 v;
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    v = 0;
+    while( cbSrc > 8 )
+    {
+        if( *pbSrc++ != 0 )
+        {
+            scError = SYMCRYPT_VALUE_TOO_LARGE;
+            goto cleanup;
+        }
+        cbSrc--;
+    }
+
+    while( cbSrc > 0 )
+    {
+        v = (v << 8) | *pbSrc++;
+        cbSrc--;
+    }
+
+    *pDst = v;
+
+cleanup:
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreLsbFirstUint32(
+                            UINT32  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst )
+{
+    return SymCryptStoreLsbFirstUint64( src, pbDst, cbDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreLsbFirstUint64(
+                            UINT64  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    while( cbDst > 0 )
+    {
+        *pbDst++ = (BYTE) src;
+        src >>= 8;
+        cbDst--;
+    }
+
+    if( src != 0 )
+    {
+        scError = SYMCRYPT_VALUE_TOO_LARGE;
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreMsbFirstUint32(
+                            UINT32  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst )
+{
+    return SymCryptStoreMsbFirstUint64( src, pbDst, cbDst );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptStoreMsbFirstUint64(
+                            UINT64  src,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    pbDst += cbDst;
+    while( cbDst > 0 )
+    {
+        *--pbDst = (BYTE) src;
+        src >>= 8;
+        cbDst--;
+    }
+
+    if( src != 0 )
+    {
+        scError = SYMCRYPT_VALUE_TOO_LARGE;
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/lms.c b/libs/symcrypt/lib/lms.c
new file mode 100644
index 000000000000..50879e89f9e4
--- /dev/null
+++ b/libs/symcrypt/lib/lms.c
@@ -0,0 +1,1162 @@
+//
+// lms.c LMS implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+static const PCSYMCRYPT_HASH* LmsHashObjects[] = {
+    &SymCryptSha256Algorithm,       //  0
+    &SymCryptShake256HashAlgorithm, //  1
+};
+
+typedef struct _SYMCRYPT_LMS_PARAMETER_PREDEFINED
+{
+    SYMCRYPT_LMS_ALGID  lmsAlgId;
+
+    // output length
+    UINT8               cbHashOutput;
+
+    // total tree height
+    UINT8               nTreeHeight;
+
+    // hash function index
+    UINT8               nHashIdx;
+
+} SYMCRYPT_LMS_PARAMETER_PREDEFINED, * PSYMCRYPT_LMS_PARAMETER_PREDEFINED;
+
+typedef const SYMCRYPT_LMS_PARAMETER_PREDEFINED* PCSYMCRYPT_LMS_PARAMETER_PREDEFINED;
+
+
+static const SYMCRYPT_LMS_PARAMETER_PREDEFINED LmsParametersPredefined[] = {
+
+    //  algId                             m   h    HIdx
+    {   SYMCRYPT_LMS_SHA256_M32_H5,       32, 5 ,    0    },
+    {   SYMCRYPT_LMS_SHA256_M32_H10,      32, 10,    0    },
+    {   SYMCRYPT_LMS_SHA256_M32_H15,      32, 15,    0    },
+    {   SYMCRYPT_LMS_SHA256_M32_H20,      32, 20,    0    },
+    {   SYMCRYPT_LMS_SHA256_M32_H25,      32, 25,    0    },
+    {   SYMCRYPT_LMS_SHAKE_M32_H5,        32, 5 ,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M32_H10,       32, 10,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M32_H15,       32, 15,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M32_H20,       32, 20,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M32_H25,       32, 25,    1    },
+    {   SYMCRYPT_LMS_SHA256_M24_H5,       24, 5 ,    0    },
+    {   SYMCRYPT_LMS_SHA256_M24_H10,      24, 10,    0    },
+    {   SYMCRYPT_LMS_SHA256_M24_H15,      24, 15,    0    },
+    {   SYMCRYPT_LMS_SHA256_M24_H20,      24, 20,    0    },
+    {   SYMCRYPT_LMS_SHA256_M24_H25,      24, 25,    0    },
+    {   SYMCRYPT_LMS_SHAKE_M24_H5,        24, 5 ,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M24_H10,       24, 10,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M24_H15,       24, 15,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M24_H20,       24, 20,    1    },
+    {   SYMCRYPT_LMS_SHAKE_M24_H25,       24, 25,    1    },
+};
+
+typedef struct _SYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED
+{
+    SYMCRYPT_LMS_OTS_ALGID  lmsOtsAlgId;
+
+    // output length
+    UINT8                   cbHashOutput;
+
+    // Winternitz width
+    UINT8                   nWidth;
+
+    // hash function index
+    UINT8                   nHashIdx;
+
+} SYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED, * PSYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED;
+typedef const SYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED* PCSYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED;
+
+static const SYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED LmsOtsParametersPredefined[] = {
+
+    //  algId                             n   w  HIdx
+    {   SYMCRYPT_LMS_OTS_SHA256_N32_W1,   32, 1,  0   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N32_W2,   32, 2,  0   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N32_W4,   32, 4,  0   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N32_W8,   32, 8,  0   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N32_W1,    32, 1,  1   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N32_W2,    32, 2,  1   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N32_W4,    32, 4,  1   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N32_W8,    32, 8,  1   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N24_W1,   24, 1,  0   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N24_W2,   24, 2,  0   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N24_W4,   24, 4,  0   },
+    {   SYMCRYPT_LMS_OTS_SHA256_N24_W8,   24, 8,  0   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N24_W1,    24, 1,  1   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N24_W2,    24, 2,  1   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N24_W4,    24, 4,  1   },
+    {   SYMCRYPT_LMS_OTS_SHAKE_N24_W8,    24, 8,  1   },
+};
+static const BYTE SYMCRYPT_LMS_D_PBLC[] = { 0x80, 0x80 };
+static const BYTE SYMCRYPT_LMS_D_MESG[] = { 0x81, 0x81 };
+static const BYTE SYMCRYPT_LMS_D_LEAF[] = { 0x82, 0x82 };
+static const BYTE SYMCRYPT_LMS_D_INTR[] = { 0x83, 0x83 };
+
+static
+VOID
+LmsHashMessage(
+    _In_                                                    PCSYMCRYPT_HASH pHash,
+    _In_reads_bytes_(SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE) PCBYTE          pbId,
+    _In_reads_bytes_(sizeof(UINT32))                        PCBYTE          pbLeafNumber,
+    _In_reads_bytes_(cbRandomizer)                          PCBYTE          pbRandomizer,
+                                                            SIZE_T          cbRandomizer,
+    _In_reads_bytes_(cbMessage)                             PCBYTE          pbMessage,
+                                                            SIZE_T          cbMessage,
+    _Out_writes_bytes_(cbOut)                               PBYTE           pbOut,
+                                                            SIZE_T          cbOut)
+{
+    SYMCRYPT_HASH_STATE state   = { 0 };
+
+    SymCryptHashInit(pHash, &state);
+    SymCryptHashAppend(pHash, &state, pbId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    SymCryptHashAppend(pHash, &state, pbLeafNumber, sizeof(UINT32));
+    SymCryptHashAppend(pHash, &state, SYMCRYPT_LMS_D_MESG, sizeof(SYMCRYPT_LMS_D_MESG));
+    SymCryptHashAppend(pHash, &state, pbRandomizer, cbRandomizer);
+    SymCryptHashAppend(pHash, &state, pbMessage, cbMessage);
+    SymCryptHashResult(pHash, &state, pbOut, cbOut);
+}
+
+static
+VOID
+SYMCRYPT_CALL
+LmsOtskeyComputePrivate(
+    _In_    PCSYMCRYPT_LMS_KEY  pKey,
+    _In_    UINT32              nLeafNumber,
+    _In_    UINT32              nPIdx,
+    _Out_writes_bytes_(pKey->params.cbHashOutput)
+            PBYTE               pbOtsPrivateKey)
+{
+    UINT32              cbHashOutput                = pKey->params.cbHashOutput;
+    PCSYMCRYPT_HASH     pHash                       = pKey->params.pLmsHashFunction;
+    SYMCRYPT_HASH_STATE state                       = { 0 };
+    BYTE                abTemp[sizeof(UINT32) + 3]  = { 0 }; // sizeof(UINT32) for nLeafNumber, 2 bytes of nPIdx and 1 byte of 0xff
+
+    SYMCRYPT_ASSERT(nLeafNumber <= (((UINT32)1 << pKey->params.nTreeHeight) - 1));
+
+    SYMCRYPT_STORE_MSBFIRST32(abTemp, nLeafNumber);
+    SYMCRYPT_STORE_MSBFIRST16(abTemp + sizeof(UINT32), (UINT16)nPIdx);
+    abTemp[sizeof(UINT32) + 2] = 0xff;
+
+    SymCryptHashInit(pHash, &state);
+    SymCryptHashAppend(pHash, &state, pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    SymCryptHashAppend(pHash, &state, abTemp, sizeof(abTemp));
+    SymCryptHashAppend(pHash, &state, pKey->abSeed, cbHashOutput);
+    SymCryptHashResult(pHash, &state, pbOtsPrivateKey, cbHashOutput);
+}
+
+static
+VOID
+SYMCRYPT_CALL
+LmskeyWipe(
+    _Inout_ PSYMCRYPT_LMS_KEY pKey)
+{
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    SymCryptWipeKnownSize(pKey->abSeed, sizeof(pKey->abSeed));
+    SymCryptWipeKnownSize(pKey->abPublicRoot, sizeof(pKey->abPublicRoot));
+    SymCryptWipeKnownSize(pKey->abId, sizeof(pKey->abId));
+    pKey->nNextUnusedLeaf = 0;
+    pKey->keyType = SYMCRYPT_LMSKEY_TYPE_NONE;
+}
+
+static
+UINT16
+LmsOtsCalculateChecksum(
+    _In_reads_bytes_(cbString)  PCBYTE pbString,
+                                UINT32 cbString,
+                                UINT32 nWidth,
+                                UINT32 nLeftShift)
+{
+    UINT32 sum = 0;
+    UINT32 max = (1 << nWidth) - 1;
+    SYMCRYPT_ASSERT(SYMCRYPT_IS_VALID_WINTERNITZ_WIDTH(nWidth));
+
+    for (UINT32 i = 0; i < (cbString * 8 / nWidth); i = i + 1)
+    {
+        sum = sum + max - SymCryptHbsGetDigit(nWidth, pbString, cbString, i);
+    }
+    return (UINT16)(sum << nLeftShift);
+}
+
+static
+SIZE_T
+SYMCRYPT_CALL
+LmsOtsSizeofSignatureFromParams(
+    _In_  PCSYMCRYPT_LMS_PARAMS pParams)
+{
+    UINT32 n    = pParams->cbHashOutput;
+    UINT32 p    = pParams->nByteStringCount;
+    SIZE_T size = 0;
+
+    size += sizeof(UINT32); // type
+    size += n;              // randomizer
+    size += p * n;          // y[0..p-1]
+
+    return size;
+}
+
+static
+VOID
+SYMCRYPT_CALL
+LmsOtskeySign(
+    _In_                                        PSYMCRYPT_LMS_KEY   pKey,
+                                                UINT64              nLeafNumber,
+    _In_reads_bytes_(cbMessage)                 PCBYTE              pbMessage,
+                                                SIZE_T              cbMessage,
+    _In_reads_bytes_(pKey->params.cbHashOutput) PCBYTE              pbRandomizer,
+    _Out_writes_bytes_(cbSignature)             PBYTE               pbSignature,
+                                                SIZE_T              cbSignature)
+{
+    PCSYMCRYPT_HASH     pHash                                                   = pKey->params.pLmsHashFunction;
+    SYMCRYPT_HASH_STATE state                                                   = { 0 };
+    UINT32              nIndex                                                  = 0;
+    UINT32              cbHashOutput                                            = pKey->params.cbHashOutput;
+    UINT32              nWinternitzChainWidth                                   = pKey->params.nWinternitzChainWidth;
+    SIZE_T              cbRemainingBytes                                        = cbSignature;
+    UINT16              nChecksum                                                   = 0;
+    BYTE                en32LeafNumber[sizeof(UINT32)]                          = {0};
+    BYTE                en16Index[sizeof(UINT16)]                               = { 0 };
+    BYTE                abOtsPrivateKey[SYMCRYPT_LMS_MAX_N]                     = { 0 };
+    BYTE                abLmsHashedMessage[SYMCRYPT_LMS_MAX_N + sizeof(nChecksum)]  = { 0 };
+    PBYTE               pbDest                                                  = pbSignature;
+
+    SYMCRYPT_ASSERT(cbSignature == LmsOtsSizeofSignatureFromParams(&pKey->params));
+
+    SYMCRYPT_STORE_MSBFIRST32(pbDest, pKey->params.lmsOtsAlgID);
+    pbDest += sizeof(UINT32);
+    cbRemainingBytes -= sizeof(UINT32);
+
+    memcpy(pbDest, pbRandomizer, cbHashOutput);
+    pbDest += cbHashOutput;
+    cbRemainingBytes -= cbHashOutput;
+
+    SYMCRYPT_STORE_MSBFIRST32(en32LeafNumber, (UINT32)nLeafNumber);
+    LmsHashMessage(pHash, pKey->abId, en32LeafNumber, pbRandomizer, cbHashOutput, pbMessage, cbMessage, abLmsHashedMessage, cbHashOutput);
+
+    nChecksum = LmsOtsCalculateChecksum(abLmsHashedMessage, cbHashOutput, nWinternitzChainWidth, pKey->params.nChecksumLShiftBits);
+    SYMCRYPT_STORE_MSBFIRST16((UINT16*)&abLmsHashedMessage[cbHashOutput], nChecksum);
+
+    SymCryptHashInit(pHash, &state);
+    for (nIndex = 0; nIndex < pKey->params.nByteStringCount; nIndex++)
+    {
+        BYTE coeff = (BYTE)SymCryptHbsGetDigit(nWinternitzChainWidth, abLmsHashedMessage, cbHashOutput + sizeof(nChecksum), nIndex);
+        LmsOtskeyComputePrivate(pKey, (UINT32)nLeafNumber, nIndex, abOtsPrivateKey);
+
+        SYMCRYPT_STORE_MSBFIRST16(en16Index, (UINT16)nIndex);
+
+        for (BYTE j = 0; j < coeff; j++)
+        {
+            SymCryptHashAppend(pHash, &state, pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+            SymCryptHashAppend(pHash, &state, en32LeafNumber, sizeof(UINT32));
+            SymCryptHashAppend(pHash, &state, en16Index, sizeof(UINT16));
+            SymCryptHashAppend(pHash, &state, &j, 1);
+            SymCryptHashAppend(pHash, &state, abOtsPrivateKey, cbHashOutput);
+            SymCryptHashResult(pHash, &state, abOtsPrivateKey, cbHashOutput);
+        }
+        memcpy(pbDest, abOtsPrivateKey, cbHashOutput);
+        pbDest += cbHashOutput;
+        cbRemainingBytes -= cbHashOutput;
+    }
+    SYMCRYPT_ASSERT(cbRemainingBytes == 0);
+
+    return;
+}
+
+static
+VOID
+SYMCRYPT_CALL
+LmsOtskeyComputePublic(
+    _In_                                            PCSYMCRYPT_LMS_KEY  pKey,
+                                                    UINT32              nNodeIdx,
+    _Out_writes_bytes_(pKey->params.cbHashOutput)   PBYTE               pbK)
+{
+    UINT32              cbHashOutput                    = pKey->params.cbHashOutput;
+    UINT32              maxJ                            = (1 << pKey->params.nWinternitzChainWidth) - 1;
+    PCSYMCRYPT_HASH     pHash                           = pKey->params.pLmsHashFunction;
+    SYMCRYPT_HASH_STATE statePriv                       = { 0 };
+    SYMCRYPT_HASH_STATE statePub                        = { 0 };
+    BYTE                en32LeafNumber[sizeof(UINT32)]  = { 0 };
+    BYTE                en16Index[sizeof(UINT16)]       = { 0 };
+    BYTE                abNode[SYMCRYPT_LMS_MAX_N]      = { 0 };
+
+    SYMCRYPT_STORE_MSBFIRST32(en32LeafNumber, nNodeIdx);
+
+    SymCryptHashInit(pHash, &statePub);
+    SymCryptHashAppend(pHash, &statePub, pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    SymCryptHashAppend(pHash, &statePub, en32LeafNumber, sizeof(UINT32));
+    SymCryptHashAppend(pHash, &statePub, SYMCRYPT_LMS_D_PBLC, sizeof(SYMCRYPT_LMS_D_PBLC));
+
+    SymCryptHashInit(pHash, &statePriv);
+    for (UINT32 i = 0; i < pKey->params.nByteStringCount; i++)
+    {
+        LmsOtskeyComputePrivate(pKey, nNodeIdx, i, abNode);
+        SYMCRYPT_STORE_MSBFIRST16(en16Index, (UINT16)i);
+
+        for (BYTE j = 0; j < maxJ; j++)
+        {
+            SymCryptHashAppend(pHash, &statePriv, pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+            SymCryptHashAppend(pHash, &statePriv, en32LeafNumber, sizeof(UINT32));
+            SymCryptHashAppend(pHash, &statePriv, en16Index, sizeof(UINT16));
+            SymCryptHashAppend(pHash, &statePriv, &j, 1);
+            SymCryptHashAppend(pHash, &statePriv, abNode, cbHashOutput);
+            SymCryptHashResult(pHash, &statePriv, abNode, cbHashOutput);
+        }
+        SymCryptHashAppend(pHash, &statePub, abNode, cbHashOutput);
+    }
+    SymCryptHashResult(pHash, &statePub, pbK, cbHashOutput);
+}
+
+static
+VOID
+SYMCRYPT_CALL
+LmsComputeNodeValue(
+    _In_                                            PCSYMCRYPT_LMS_KEY  pKey,
+                                                    UINT32              nIndex,
+    _Out_writes_bytes_(pKey->params.cbHashOutput)   PBYTE               pbNodeValue,
+                                                    SIZE_T              cbNodeValue)
+{
+    UNREFERENCED_PARAMETER(cbNodeValue);
+
+    UINT32              cbHashOutput                    = pKey->params.cbHashOutput;
+    UINT32              nInternalNodes                  = (UINT32)1 << pKey->params.nTreeHeight;
+    PCSYMCRYPT_HASH     pHash                           = pKey->params.pLmsHashFunction;
+    SYMCRYPT_HASH_STATE state                           = { 0 };
+    BYTE                abTemp[SYMCRYPT_LMS_MAX_N]      = { 0 };
+    BYTE                en32Index[sizeof(UINT32)]       = { 0 };
+    BYTE                abOtsPubKey[SYMCRYPT_LMS_MAX_N] = { 0 };
+
+    SYMCRYPT_ASSERT(nIndex > 0);
+    SYMCRYPT_ASSERT(cbNodeValue == cbHashOutput);
+
+    SYMCRYPT_STORE_MSBFIRST32(en32Index, nIndex);
+
+    SymCryptHashInit(pHash, &state);
+    SymCryptHashAppend(pHash, &state, pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    SymCryptHashAppend(pHash, &state, en32Index, sizeof(UINT32));
+    if (nIndex >= nInternalNodes)
+    {
+        LmsOtskeyComputePublic(pKey, nIndex - nInternalNodes, abOtsPubKey);
+
+        SymCryptHashAppend(pHash, &state, SYMCRYPT_LMS_D_LEAF, sizeof(SYMCRYPT_LMS_D_LEAF));
+        SymCryptHashAppend(pHash, &state, abOtsPubKey, cbHashOutput);
+    }
+    else
+    {
+        SymCryptHashAppend(pHash, &state, SYMCRYPT_LMS_D_INTR, sizeof(SYMCRYPT_LMS_D_INTR));
+
+        LmsComputeNodeValue(pKey, 2 * nIndex, abTemp, cbHashOutput);
+        SymCryptHashAppend(pHash, &state, abTemp, cbHashOutput);
+        SymCryptWipeKnownSize(abTemp, SYMCRYPT_LMS_MAX_N);
+
+        LmsComputeNodeValue(pKey, 2 * nIndex + 1, abTemp, cbHashOutput);
+        SymCryptHashAppend(pHash, &state, abTemp, cbHashOutput);
+    }
+    SymCryptHashResult(pHash, &state, pbNodeValue, cbHashOutput);
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsParamsFromAlgId(
+            SYMCRYPT_LMS_ALGID      lmsAlgID,
+            SYMCRYPT_LMS_OTS_ALGID  lmsOtsAlgID,
+    _Out_   PSYMCRYPT_LMS_PARAMS    pParams)
+{
+    SYMCRYPT_ERROR                          scError                 = SYMCRYPT_NO_ERROR;
+    SIZE_T                                  uLmsParametersCount     = 0;
+    SIZE_T                                  uLmsOtsParametersCount  = 0;
+    UINT32                                  u                       = 0;
+    UINT32                                  v                       = 0;
+    PCSYMCRYPT_LMS_PARAMETER_PREDEFINED     pLmsParameters          = NULL;
+    PCSYMCRYPT_LMS_OTS_PARAMETER_PREDEFINED pLmsOtsParameters       = NULL;
+    BOOL bFound = FALSE;
+
+    SymCryptWipeKnownSize(pParams, sizeof(*pParams));
+    pLmsOtsParameters = LmsOtsParametersPredefined;
+    uLmsOtsParametersCount = SYMCRYPT_ARRAY_SIZE(LmsOtsParametersPredefined);
+    pLmsParameters = LmsParametersPredefined;
+    uLmsParametersCount = SYMCRYPT_ARRAY_SIZE(LmsParametersPredefined);
+
+    for (UINT32 i = 0; i < uLmsParametersCount; i++)
+    {
+        if (pLmsParameters[i].lmsAlgId == lmsAlgID)
+        {
+            pParams->lmsAlgID = lmsAlgID;
+            pParams->nTreeHeight = pLmsParameters[i].nTreeHeight;
+            SYMCRYPT_ASSERT(pParams->nTreeHeight <= SYMCRYPT_LMS_MAX_CUSTOM_TREE_HEIGHT);
+
+            pParams->cbHashOutput = pLmsParameters[i].cbHashOutput;
+
+            SYMCRYPT_ASSERT(pLmsParameters[i].nHashIdx < SYMCRYPT_ARRAY_SIZE(LmsHashObjects));
+            pParams->pLmsHashFunction = *LmsHashObjects[pLmsParameters[i].nHashIdx];
+            bFound = TRUE;
+            break;
+        }
+    }
+    if (!bFound)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    bFound = FALSE;
+    for (UINT32 i = 0; i < uLmsOtsParametersCount; i++)
+    {
+        if (pLmsOtsParameters[i].lmsOtsAlgId == lmsOtsAlgID)
+        {
+            SYMCRYPT_ASSERT(pLmsOtsParameters[i].nHashIdx < SYMCRYPT_ARRAY_SIZE(LmsHashObjects));
+
+            if (pParams->pLmsHashFunction != *LmsHashObjects[pLmsOtsParameters[i].nHashIdx] ||
+                pParams->cbHashOutput != pLmsOtsParameters[i].cbHashOutput)
+            {
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+            pParams->lmsOtsAlgID = lmsOtsAlgID;
+            pParams->nWinternitzChainWidth = pLmsOtsParameters[i].nWidth;
+            SymCryptHbsGetWinternitzLengths(
+                pParams->cbHashOutput,
+                pParams->nWinternitzChainWidth,
+                &u,
+                &v);
+            SYMCRYPT_ASSERT((v * pParams->nWinternitzChainWidth) <= SYMCRYPT_LMS_CHECKSUM_SIZE);
+            pParams->nChecksumLShiftBits = SYMCRYPT_LMS_CHECKSUM_SIZE - (v * pParams->nWinternitzChainWidth);
+            pParams->nByteStringCount = u + v;
+            bFound = TRUE;
+            break;
+        }
+    }
+    if (!bFound)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptLmsSizeofSignatureFromParams(
+    _In_ PCSYMCRYPT_LMS_PARAMS pParams)
+{
+    SIZE_T size = 0;
+
+    size += sizeof(UINT32); // q
+    size += LmsOtsSizeofSignatureFromParams(pParams); // LMS-OTS signature
+    size += sizeof(UINT32); // type
+    size += pParams->nTreeHeight * pParams->cbHashOutput; // path[0..h-1]
+    return size;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsSetParams(
+    _Out_   PSYMCRYPT_LMS_PARAMS    pParams,
+            UINT32                  lmsAlgID,
+            UINT32                  lmsOtsAlgID,
+    _In_    PCSYMCRYPT_HASH         pLmsHashFunction,
+            UINT32                  cbHashOutput,
+            UINT32                  nTreeHeight,
+            UINT32                  nWinternitzChainWidth)
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+    UINT32          u       = 0;
+    UINT32          v       = 0;
+
+    SymCryptWipeKnownSize(pParams, sizeof(*pParams));
+
+    // nTreeHeight must be positive and maximum SYMCRYPT_LMS_MAX_CUSTOM_TREE_HEIGHT
+    if (nTreeHeight == 0 || nTreeHeight > SYMCRYPT_LMS_MAX_CUSTOM_TREE_HEIGHT)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Output cbHashOutput cannot be larger than the hash output size or SYMCRYPT_LMS_MAX_N
+    if (cbHashOutput == 0 || cbHashOutput > pLmsHashFunction->resultSize || cbHashOutput > SYMCRYPT_LMS_MAX_N)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Winternitz parameter must be one of 1, 2, 4, or 8
+    if (!SYMCRYPT_IS_VALID_WINTERNITZ_WIDTH(nWinternitzChainWidth))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pParams->lmsAlgID = lmsAlgID;
+    pParams->lmsOtsAlgID = lmsOtsAlgID;
+    pParams->pLmsHashFunction = pLmsHashFunction;
+    pParams->nTreeHeight = nTreeHeight;
+    pParams->cbHashOutput = cbHashOutput;
+    pParams->nWinternitzChainWidth = nWinternitzChainWidth;
+    SymCryptHbsGetWinternitzLengths(
+        pParams->cbHashOutput,
+        pParams->nWinternitzChainWidth,
+        &u,
+        &v);
+    pParams->nChecksumLShiftBits = SYMCRYPT_LMS_CHECKSUM_SIZE - (v * pParams->nWinternitzChainWidth);
+    pParams->nByteStringCount = u + v;
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsSizeofKeyBlobFromParams(
+    _In_    PCSYMCRYPT_LMS_PARAMS   pParams,
+            SYMCRYPT_LMSKEY_TYPE    keyType,
+    _Out_   SIZE_T*                 pcbKey)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    switch (keyType)
+    {
+        case SYMCRYPT_LMSKEY_TYPE_PUBLIC:
+            *pcbKey = SYMCRYPT_LMS_PUB_KEY_SIZE(pParams->cbHashOutput);
+            break;
+
+        case SYMCRYPT_LMSKEY_TYPE_PRIVATE:
+            *pcbKey = SYMCRYPT_LMS_PRIV_KEY_SIZE(pParams->cbHashOutput);
+            break;
+
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            break;
+    }
+
+    return scError;
+}
+
+PSYMCRYPT_LMS_KEY
+SYMCRYPT_CALL
+SymCryptLmskeyAllocate(
+    _In_    PCSYMCRYPT_LMS_PARAMS   pParams,
+            UINT32                  flags)
+{
+    PSYMCRYPT_LMS_KEY pKey   = NULL;
+    SIZE_T            cbSize = sizeof(SYMCRYPT_LMS_KEY);
+
+    if (flags != 0)
+    {
+        goto cleanup;
+    }
+
+    pKey = SymCryptCallbackAlloc(cbSize);
+    if (pKey == NULL)
+    {
+        goto cleanup;
+    }
+
+    SymCryptWipe(pKey, cbSize);
+    pKey->cbSize = cbSize;
+
+    memcpy(&pKey->params, pParams, sizeof(*pParams));
+    SYMCRYPT_SET_MAGIC(pKey);
+
+cleanup:
+    return pKey;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptLmskeyFree(
+    _Inout_ PSYMCRYPT_LMS_KEY   pKey)
+{
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    SymCryptWipeKnownSize(pKey, sizeof(*pKey));
+    SymCryptCallbackFree(pKey);
+}
+
+static
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeyVerifyRoot(
+    _In_    PCSYMCRYPT_LMS_KEY pKey)
+{
+    BYTE            abPublicRoot[SYMCRYPT_LMS_MAX_N]  = { 0 };
+    SYMCRYPT_ERROR  scError                           = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    // key to be verified has to be a private key
+    if (pKey->keyType != SYMCRYPT_LMSKEY_TYPE_PRIVATE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // compute the public root from the private key, root node has index 1
+    LmsComputeNodeValue(
+        pKey,
+        1,
+        abPublicRoot,
+        pKey->params.cbHashOutput);
+
+    if (!SymCryptEqual(abPublicRoot, pKey->abPublicRoot, pKey->params.cbHashOutput))
+    {
+        scError = SYMCRYPT_HBS_PUBLIC_ROOT_MISMATCH;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeyGenerate(
+    _Inout_ PSYMCRYPT_LMS_KEY   pKey,
+            UINT32              flags)
+{
+    SYMCRYPT_ERROR   scError  = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if (flags != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pKey->nNextUnusedLeaf = 0;
+    // Set the LMS key identifier I
+    scError = SymCryptCallbackRandom(pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Set the private key Seed value
+    scError = SymCryptCallbackRandom(pKey->abSeed, pKey->params.cbHashOutput);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // compute the public root from the private key
+    LmsComputeNodeValue(
+        pKey,
+        1,
+        pKey->abPublicRoot,
+        pKey->params.cbHashOutput);
+
+    pKey->keyType = SYMCRYPT_LMSKEY_TYPE_PRIVATE;
+
+cleanup:
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        LmskeyWipe(pKey);
+    }
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeyGetValue(
+    _In_                        PCSYMCRYPT_LMS_KEY      pKey,
+                                SYMCRYPT_LMSKEY_TYPE    keyType,
+                                UINT32                  flags,
+    _Out_writes_bytes_(cbBlob)  PBYTE                   pbBlob,
+                                SIZE_T                  cbBlob)
+{
+    SYMCRYPT_ERROR   scError        = SYMCRYPT_NO_ERROR;
+    SIZE_T           cbHashOutput   = pKey->params.cbHashOutput;
+    SIZE_T           cbKey          = 0;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if (flags != 0 ||
+        (keyType != SYMCRYPT_LMSKEY_TYPE_PRIVATE &&
+        keyType != SYMCRYPT_LMSKEY_TYPE_PUBLIC))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ((keyType == SYMCRYPT_LMSKEY_TYPE_PRIVATE) && (pKey->keyType == SYMCRYPT_LMSKEY_TYPE_PUBLIC))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptLmsSizeofKeyBlobFromParams(&pKey->params, keyType, &cbKey);
+    if (cbBlob != cbKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+    SYMCRYPT_STORE_MSBFIRST32(pbBlob, (UINT32)pKey->params.lmsAlgID);
+    pbBlob += sizeof(UINT32);
+
+    SYMCRYPT_STORE_MSBFIRST32(pbBlob, (UINT32)pKey->params.lmsOtsAlgID);
+    pbBlob += sizeof(UINT32);
+
+    memcpy(pbBlob, pKey->abId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    pbBlob += SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE;
+
+    memcpy(pbBlob, pKey->abPublicRoot, cbHashOutput);
+    pbBlob += cbHashOutput;
+
+    if (keyType == SYMCRYPT_LMSKEY_TYPE_PRIVATE)
+    {
+
+        SYMCRYPT_ASSERT((pKey->nNextUnusedLeaf & 0xFFFFFFFF00000000) == 0);
+
+        SYMCRYPT_STORE_MSBFIRST32(pbBlob, (UINT32)pKey->nNextUnusedLeaf);
+        pbBlob += sizeof(UINT32);
+
+        memcpy(pbBlob, pKey->abSeed, cbHashOutput);
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmskeySetValue(
+    _In_reads_bytes_(cbBlob)    PCBYTE                  pbBlob,
+                                SIZE_T                  cbBlob,
+                                SYMCRYPT_LMSKEY_TYPE    keyType,
+                                UINT32                  flags,
+    _Inout_                     PSYMCRYPT_LMS_KEY       pKey)
+{
+    SYMCRYPT_ERROR  scError     = SYMCRYPT_NO_ERROR;
+    UINT32          lmsAlgID    = 0;
+    UINT32          lmsOtsAlgID = 0;
+    SIZE_T          cbKey       = 0;
+
+    SYMCRYPT_ASSERT(keyType == SYMCRYPT_LMSKEY_TYPE_PUBLIC || keyType == SYMCRYPT_LMSKEY_TYPE_PRIVATE);
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if (flags & (~SYMCRYPT_FLAG_LMSKEY_VERIFY_ROOT))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Public key validation can only be performed for private keys
+    if ((flags & SYMCRYPT_FLAG_LMSKEY_VERIFY_ROOT) != 0 &&
+        keyType != SYMCRYPT_LMSKEY_TYPE_PRIVATE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptLmsSizeofKeyBlobFromParams(&pKey->params, keyType, &cbKey);
+    if (cbBlob != cbKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    lmsAlgID = SYMCRYPT_LOAD_MSBFIRST32(pbBlob);
+    pbBlob += sizeof(UINT32);
+
+    lmsOtsAlgID = SYMCRYPT_LOAD_MSBFIRST32(pbBlob);
+    pbBlob += sizeof(UINT32);
+
+    // check if the lmsAlgID and lmsOtsAlgID matches the ones in the key
+    if (lmsAlgID != pKey->params.lmsAlgID || lmsOtsAlgID != pKey->params.lmsOtsAlgID)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+    SymCryptWipeKnownSize(pKey->abPublicRoot, sizeof(pKey->abPublicRoot));
+    SymCryptWipeKnownSize(pKey->abId, sizeof(pKey->abId));
+
+    pKey->keyType = keyType;
+
+    memcpy(pKey->abId, pbBlob, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    pbBlob += SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE;
+
+    memcpy(pKey->abPublicRoot, pbBlob, pKey->params.cbHashOutput);
+    pbBlob += pKey->params.cbHashOutput;
+
+    if (keyType == SYMCRYPT_LMSKEY_TYPE_PRIVATE)
+    {
+        // Wipe private key material
+        pKey->nNextUnusedLeaf = 0;
+        SymCryptWipeKnownSize(pKey->abSeed, sizeof(pKey->abSeed));
+
+        pKey->nNextUnusedLeaf = SYMCRYPT_LOAD_MSBFIRST32(pbBlob);
+        pbBlob += sizeof(UINT32);
+
+        memcpy(pKey->abSeed, pbBlob,pKey->params.cbHashOutput);
+
+        if (flags & SYMCRYPT_FLAG_LMSKEY_VERIFY_ROOT)
+        {
+            scError = SymCryptLmskeyVerifyRoot(pKey);
+            if (scError != SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+cleanup:
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        LmskeyWipe(pKey);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsSign(
+    _Inout_                         PSYMCRYPT_LMS_KEY   pKey,
+    _In_reads_bytes_(cbMessage)     PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _Out_writes_bytes_(cbSignature) PBYTE               pbSignature,
+                                    SIZE_T              cbSignature)
+{
+    SYMCRYPT_ERROR  scError                             = SYMCRYPT_NO_ERROR;
+    UINT32          nLeafNumber                         = (UINT32)pKey->nNextUnusedLeaf;
+    UINT32          cbHashOutput                        = pKey->params.cbHashOutput;
+    UINT32          nTreeHeight                         = pKey->params.nTreeHeight;
+    SIZE_T          cbRemainingBytes                    = cbSignature;
+    UINT32          nLeavesCount                        = ((UINT32)1 << nTreeHeight);
+    UINT32          nNodeIndex                          = 0;
+    UINT32          nTemp                               = 0;
+    SIZE_T          cbOtsSignature                      = LmsOtsSizeofSignatureFromParams(&pKey->params);
+    BYTE            abLMSRandomizer[SYMCRYPT_LMS_MAX_N] = { 0 };
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if (flags != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (pKey->keyType != SYMCRYPT_LMSKEY_TYPE_PRIVATE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbSignature != SymCryptLmsSizeofSignatureFromParams(&pKey->params))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptCallbackRandom(abLMSRandomizer, cbHashOutput);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    nLeafNumber = (UINT32)SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(&pKey->nNextUnusedLeaf, 1) - 1;
+    if (nLeafNumber >= (nLeavesCount))
+    {
+        scError = SYMCRYPT_HBS_NO_OTS_KEYS_LEFT;
+        pKey->nNextUnusedLeaf = nLeavesCount;
+        goto cleanup;
+    }
+    SYMCRYPT_STORE_MSBFIRST32(pbSignature, nLeafNumber);
+    pbSignature += sizeof(UINT32);
+    cbRemainingBytes -= sizeof(UINT32);
+
+    LmsOtskeySign(
+        pKey,
+        nLeafNumber,
+        pbMessage,
+        cbMessage,
+        abLMSRandomizer,
+        pbSignature,
+        cbOtsSignature);
+    pbSignature += cbOtsSignature;
+    cbRemainingBytes -= cbOtsSignature;
+
+    SYMCRYPT_STORE_MSBFIRST32(pbSignature, pKey->params.lmsAlgID);
+    pbSignature += sizeof(UINT32);
+    cbRemainingBytes -= sizeof(UINT32);
+
+    nNodeIndex = nLeavesCount + nLeafNumber;
+	// write the path into the signature
+    for (UINT32 nIndex = 0; nIndex < nTreeHeight; nIndex++)
+    {
+        nTemp = (nNodeIndex >> nIndex) ^ 1;
+        LmsComputeNodeValue(
+            pKey,
+            nTemp,
+            pbSignature,
+            cbHashOutput);
+        pbSignature += cbHashOutput;
+        cbRemainingBytes -= cbHashOutput;
+    }
+    SYMCRYPT_ASSERT(cbRemainingBytes == 0);
+
+cleanup:
+    return scError;
+}
+
+static
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+LmsComputeOtsPubKeyCandidate(
+                                                            UINT32                  nLeafNumber,
+    _In_reads_bytes_(cbMessage)                             PCBYTE                  pbMessage,
+                                                            SIZE_T                  cbMessage,
+    _In_reads_bytes_(cbOtsSignature)                        PCBYTE                  pbOtsSignature,
+                                                            SIZE_T                  cbOtsSignature,
+    _In_reads_bytes_(SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE) PCBYTE                  pbId,
+    _In_                                                    PCSYMCRYPT_LMS_PARAMS   pSigParams,
+    _Out_writes_bytes_(pSigParams->cbHashOutput)            PBYTE                   pbOtsPubKeyCandidate)
+{
+    SYMCRYPT_ERROR          scError                                 = SYMCRYPT_NO_ERROR;
+    UINT32                  cbHashOutput                            = pSigParams->cbHashOutput;
+    UINT32                  nWinternitzChainWidth                   = pSigParams->nWinternitzChainWidth;
+    UINT32                  nByteStringCount                        = pSigParams->nByteStringCount;
+    UINT32                  nSigType                                = 0;
+    UINT32                  nMaxJ                                   = (1 << nWinternitzChainWidth) - 1;
+    UINT16                  nCksm                                   = 0;
+    PCSYMCRYPT_HASH         pHash                                   = pSigParams->pLmsHashFunction;
+    SYMCRYPT_HASH_STATE     state                                   = { 0 };
+    SYMCRYPT_HASH_STATE     stateKc                                  = { 0 };
+    BYTE                    en32LeafNumber[sizeof(UINT32)]          = { 0 };
+    BYTE                    en16Index[sizeof(UINT16)]                            = { 0 };
+    BYTE                    abLmsHashedMsg[SYMCRYPT_LMS_MAX_N + sizeof(nCksm)]  = { 0 };
+    BYTE                    abTmpRes[SYMCRYPT_LMS_MAX_N]            = { 0 };
+    PCBYTE                  pbRandomizer                            = NULL;
+
+    if (cbOtsSignature != LmsOtsSizeofSignatureFromParams(pSigParams))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    nSigType = SYMCRYPT_LOAD_MSBFIRST32(pbOtsSignature);
+    pbOtsSignature += sizeof(UINT32);
+    if (nSigType != pSigParams->lmsOtsAlgID)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pbRandomizer = pbOtsSignature;
+    pbOtsSignature += cbHashOutput;
+
+    SYMCRYPT_STORE_MSBFIRST32(en32LeafNumber, nLeafNumber);
+
+    LmsHashMessage(pHash, pbId, en32LeafNumber, pbRandomizer, cbHashOutput, pbMessage, cbMessage, abLmsHashedMsg, cbHashOutput);
+    nCksm = LmsOtsCalculateChecksum(abLmsHashedMsg, cbHashOutput, nWinternitzChainWidth, pSigParams->nChecksumLShiftBits);
+    SYMCRYPT_STORE_MSBFIRST16((UINT16*)&abLmsHashedMsg[cbHashOutput], (UINT16)nCksm);
+
+    SymCryptHashInit(pHash, &stateKc);
+    SymCryptHashAppend(pHash, &stateKc, pbId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    SymCryptHashAppend(pHash, &stateKc, en32LeafNumber, sizeof(UINT32));
+    SymCryptHashAppend(pHash, &stateKc, SYMCRYPT_LMS_D_PBLC, sizeof(SYMCRYPT_LMS_D_PBLC));
+
+    SymCryptHashInit(pHash, &state);
+    for (UINT32 i = 0; i < nByteStringCount; i++)
+    {
+        BYTE a = (BYTE)SymCryptHbsGetDigit(nWinternitzChainWidth, abLmsHashedMsg, cbHashOutput + sizeof(nCksm), i);
+        PCBYTE tmp = pbOtsSignature + (i * cbHashOutput);
+
+        SYMCRYPT_STORE_MSBFIRST16(en16Index, (UINT16)i);
+
+        for (BYTE j = a; j < nMaxJ; j++)
+        {
+            SymCryptHashAppend(pHash, &state, pbId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+            SymCryptHashAppend(pHash, &state, en32LeafNumber, sizeof(UINT32));
+            SymCryptHashAppend(pHash, &state, en16Index, sizeof(UINT16));
+            SymCryptHashAppend(pHash, &state, &j, 1);
+            SymCryptHashAppend(pHash, &state, tmp, cbHashOutput);
+            SymCryptHashResult(pHash, &state, abTmpRes, cbHashOutput);
+            tmp = abTmpRes;
+        }
+        SymCryptHashAppend(pHash, &stateKc, tmp, cbHashOutput);
+    }
+    SymCryptHashResult(pHash, &stateKc, pbOtsPubKeyCandidate, cbHashOutput);
+
+cleanup:
+    return scError;
+}
+
+static
+VOID
+SYMCRYPT_CALL
+LmsComputeRootCandidate(
+                                                                    UINT32                  nLeafNumber,
+    _In_                                                            PCSYMCRYPT_LMS_PARAMS   pParams,
+    _In_reads_bytes_(SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE)         PCBYTE                  pbId,
+    _In_reads_bytes_(pParams->nTreeHeight * pParams->cbHashOutput)  PCBYTE                  pbPath,
+    _In_reads_bytes_(pParams->cbHashOutput)                         PCBYTE                  pbPubKeyCandidate,
+    _Out_writes_bytes_(pParams->cbHashOutput)                       PBYTE                   pbRootCandidate
+)
+{
+    PCSYMCRYPT_HASH     pHash                       = pParams->pLmsHashFunction;
+    SYMCRYPT_HASH_STATE state                       = { 0 };
+    UINT32              cbHashOutput                = pParams->cbHashOutput;
+    UINT32              nIndex                      = 0;
+    UINT32              nNodeNum                    = (1 << pParams->nTreeHeight) + nLeafNumber;
+    PBYTE               pbTemp                      = pbRootCandidate;
+    BYTE                en32NodeNum[sizeof(UINT32)] = { 0 };
+
+    SYMCRYPT_STORE_MSBFIRST32(en32NodeNum, nNodeNum);
+    SymCryptHashInit(pHash, &state);
+    SymCryptHashAppend(pHash, &state, pbId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+    SymCryptHashAppend(pHash, &state, en32NodeNum, sizeof(UINT32));
+    SymCryptHashAppend(pHash, &state, SYMCRYPT_LMS_D_LEAF, sizeof(SYMCRYPT_LMS_D_LEAF));
+    SymCryptHashAppend(pHash, &state, pbPubKeyCandidate, cbHashOutput);
+    SymCryptHashResult(pHash, &state, pbTemp, cbHashOutput);
+
+    for (nIndex = 0; nIndex  < pParams->nTreeHeight; nIndex ++)
+    {
+        SYMCRYPT_STORE_MSBFIRST32(en32NodeNum, nNodeNum / 2);
+        SymCryptHashAppend(pHash, &state, pbId, SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE);
+        SymCryptHashAppend(pHash, &state, en32NodeNum, sizeof(UINT32));
+        SymCryptHashAppend(pHash, &state, SYMCRYPT_LMS_D_INTR, sizeof(SYMCRYPT_LMS_D_INTR));
+        if (nNodeNum % 2)
+        {
+            SymCryptHashAppend(pHash, &state, pbPath + (cbHashOutput * nIndex), cbHashOutput);
+            SymCryptHashAppend(pHash, &state, pbTemp, cbHashOutput);
+        }
+        else
+        {
+            SymCryptHashAppend(pHash, &state, pbTemp, cbHashOutput);
+            SymCryptHashAppend(pHash, &state, pbPath + (cbHashOutput * nIndex), cbHashOutput);
+        }
+        SymCryptHashResult(pHash, &state, pbTemp, cbHashOutput);
+        nNodeNum /= 2;
+    }
+    SYMCRYPT_ASSERT(nNodeNum <= 1);
+
+    memcpy(pbRootCandidate, pbTemp, cbHashOutput);
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsVerifyInternal(
+    _In_                            PCSYMCRYPT_LMS_KEY  pKey,
+    _In_reads_bytes_(cbMessage)     PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_(cbSignature)   PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature)
+{
+    SYMCRYPT_ASSERT(pKey != NULL);
+    SYMCRYPT_ASSERT(pKey->keyType != SYMCRYPT_LMSKEY_TYPE_NONE);
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    SYMCRYPT_ERROR          scError                                     = SYMCRYPT_NO_ERROR;
+    UINT32                  cbHashOutput                                = pKey->params.cbHashOutput;
+    PCSYMCRYPT_LMS_PARAMS   pLmsKeyParams                               = &pKey->params;
+    PCBYTE                  pbLocSignature                              = pbSignature;
+    BYTE                    abRootCandidate[SYMCRYPT_LMS_MAX_N]         = { 0 };
+    BYTE                    abOtsPubKeyCandidate[SYMCRYPT_LMS_MAX_N]    = { 0 };
+
+    if (flags != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbSignature != SymCryptLmsSizeofSignatureFromParams(&pKey->params))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    UINT32 nLeafNumber = SYMCRYPT_LOAD_MSBFIRST32(pbLocSignature);
+    pbLocSignature += sizeof(UINT32);
+    if (nLeafNumber >= ((UINT32)1 << pKey->params.nTreeHeight))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    UINT32 nOtsSigtype = SYMCRYPT_LOAD_MSBFIRST32(pbLocSignature);
+    pbLocSignature += sizeof(UINT32);
+
+    if (nOtsSigtype != pLmsKeyParams->lmsOtsAlgID)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pbLocSignature += cbHashOutput * (pKey->params.nByteStringCount + 1); // +1 is for the randomizer
+    UINT32 nSigType = SYMCRYPT_LOAD_MSBFIRST32(pbLocSignature);
+    pbLocSignature += sizeof(UINT32);
+
+    if (nSigType != pLmsKeyParams->lmsAlgID)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = LmsComputeOtsPubKeyCandidate(
+        nLeafNumber,
+        pbMessage,
+        cbMessage,
+        pbSignature + sizeof(UINT32), //the +sizeof(UINT32) is to skip the leaf number and reach the LMS-OTS signature
+        LmsOtsSizeofSignatureFromParams(&pKey->params),
+        pKey->abId,
+        pLmsKeyParams,
+        abOtsPubKeyCandidate);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    LmsComputeRootCandidate(
+        nLeafNumber,
+        pLmsKeyParams,
+        pKey->abId,
+        pbLocSignature,
+        abOtsPubKeyCandidate,
+        abRootCandidate);
+    if (!SymCryptEqual(abRootCandidate, pKey->abPublicRoot, cbHashOutput))
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsVerify(
+    _In_                            PCSYMCRYPT_LMS_KEY  pKey,
+    _In_reads_bytes_(cbMessage)     PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_(cbSignature)   PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_RUN_SELFTEST_ONCE(
+        SymCryptLmsSelftest,
+        SYMCRYPT_SELFTEST_ALGORITHM_LMS);
+
+    scError = SymCryptLmsVerifyInternal(
+        pKey,
+        pbMessage,
+        cbMessage,
+        flags,
+        pbSignature,
+        cbSignature);
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/marvin32.c b/libs/symcrypt/lib/marvin32.c
new file mode 100644
index 000000000000..9e92633bebc8
--- /dev/null
+++ b/libs/symcrypt/lib/marvin32.c
@@ -0,0 +1,331 @@
+//
+// Marvin32.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement the Marvin32 checksum function
+//
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+//
+// Default initial seed, first 8 bytes of SHA256( "Marvin32" );
+//
+static const SYMCRYPT_MARVIN32_EXPANDED_SEED SymCryptMarvin32DefaultSeedStruct = {
+    {0xcd0893b7, 0xd53cd9ce},
+#if defined( SYMCRYPT_MAGIC_ENABLED )
+    SYMCRYPT_MAGIC_VALUE( &SymCryptMarvin32DefaultSeedStruct ),
+#endif
+    };
+
+PCSYMCRYPT_MARVIN32_EXPANDED_SEED const SymCryptMarvin32DefaultSeed = &SymCryptMarvin32DefaultSeedStruct;
+
+//
+// Round rotation amounts. This array is optimized away by the compiler
+// as we inline all our rotations.
+//
+static const int rotate[4] = {
+    20, 9, 27, 19,
+};
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMarvin32ExpandSeed(
+    _Out_               PSYMCRYPT_MARVIN32_EXPANDED_SEED    pExpandedSeed,
+    _In_reads_(cbSeed)  PCBYTE                              pbSeed,
+                        SIZE_T                              cbSeed )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( cbSeed != SYMCRYPT_MARVIN32_SEED_SIZE )
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+    pExpandedSeed->s[0] = SYMCRYPT_LOAD_LSBFIRST32( pbSeed );
+    pExpandedSeed->s[1] = SYMCRYPT_LOAD_LSBFIRST32( pbSeed + 4 );
+
+    SYMCRYPT_SET_MAGIC( pExpandedSeed );
+
+cleanup:
+    return scError;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32SeedCopy(   _In_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pSrc,
+                            _Out_   PSYMCRYPT_MARVIN32_EXPANDED_SEED    pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32StateCopy(
+    _In_        PCSYMCRYPT_MARVIN32_STATE           pSrc,
+    _In_opt_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed,
+    _Out_       PSYMCRYPT_MARVIN32_STATE            pDst )
+{
+    SYMCRYPT_CHECK_MAGIC( pSrc );
+    *pDst = *pSrc;
+
+    if( pExpandedSeed == NULL )
+    {
+        SYMCRYPT_CHECK_MAGIC( pSrc->pSeed );
+        pDst->pSeed = pSrc->pSeed;
+    }
+    else
+    {
+        SYMCRYPT_CHECK_MAGIC( pExpandedSeed );
+        pDst->pSeed = pExpandedSeed;
+    }
+
+    SYMCRYPT_SET_MAGIC( pDst );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Init(   _Out_   PSYMCRYPT_MARVIN32_STATE            pState,
+                        _In_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed)
+{
+    pState->chain = *pExpandedSeed;
+    pState->dataLength = 0;
+    pState->pSeed = pExpandedSeed;
+
+    *(UINT32 *) &pState->buffer[4] = 0; // wipe the last 4 bytes of the buffer.
+
+    SYMCRYPT_SET_MAGIC( pState );
+}
+
+
+//
+// SymCryptMarvin32Append
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Append(     _Inout_                 PSYMCRYPT_MARVIN32_STATE    state,
+                            _In_reads_( cbData )    PCBYTE                      pbData,
+                                                    SIZE_T                      cbData )
+{
+    UINT32 bytesInBuffer = state->dataLength;
+
+    SYMCRYPT_CHECK_MAGIC( state );
+
+    state->dataLength += (UINT32) cbData;    // We only keep track of the last 2 bits...
+
+#define ALG MARVIN32
+#define Alg Marvin32
+#include "hash_buffer_pattern.c"
+#undef ALG
+#undef Alg
+
+}
+
+
+//
+// SymCryptMarvin32Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Result(
+     _Inout_                                        PSYMCRYPT_MARVIN32_STATE    pState,
+     _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE )  PBYTE                       pbResult )
+{
+    SIZE_T bytesInBuffer = ( pState->dataLength) & 0x3;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    //
+    // Wipe four bytes in the buffer.
+    // Doing this first ensures that this write is aligned when the input was of
+    // length 0 mod 4.
+    // The buffer is 8 bytes long, so we never overwrite anything else.
+    //
+    *(UINT32 *) &pState->buffer[bytesInBuffer] = 0;
+
+    //
+    // The buffer is never completely full, so we can always put the first
+    // padding byte in.
+    //
+    pState->buffer[bytesInBuffer++] = 0x80;
+
+    //
+    // Process the final block
+    //
+    SymCryptMarvin32AppendBlocks( &pState->chain, pState->buffer, 8 );
+
+    SYMCRYPT_STORE_LSBFIRST32( pbResult    , pState->chain.s[0] );
+    SYMCRYPT_STORE_LSBFIRST32( pbResult + 4, pState->chain.s[1] );
+
+    //
+    // Wipe only those things that we need to wipe.
+    //
+
+    *(UINT32 *) &pState->buffer[0] = 0;
+    pState->dataLength = 0;
+
+    pState->chain = *pState->pSeed;
+}
+
+#define BLOCK( a, b ) \
+{\
+    b ^= a; a = ROL32( a, rotate[0] );\
+    a += b; b = ROL32( b, rotate[1] );\
+    b ^= a; a = ROL32( a, rotate[2] );\
+    a += b; b = ROL32( b, rotate[3] );\
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32AppendBlocks(
+    _Inout_                 PSYMCRYPT_MARVIN32_CHAINING_STATE   pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData )
+{
+    UINT32 s0 = pChain->s[0];
+    UINT32 s1 = pChain->s[1];
+
+    SIZE_T bytesInFirstBlock = cbData & 0xc;        // 0, 4, 8, or 12
+
+    SYMCRYPT_ASSERT( (cbData & 3) == 0 );
+
+
+    pbData += bytesInFirstBlock;
+    cbData -= bytesInFirstBlock;
+
+    switch( bytesInFirstBlock )
+    {
+    case 0: // This handles the cbData == 0 case too
+        while( cbData > 0 )
+        {
+            pbData += 16;
+            cbData -= 16;
+
+            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData - 16 );
+            BLOCK( s0, s1 );
+    case 12:
+            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData - 12 );
+            BLOCK( s0, s1 );
+    case 8:
+            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData -  8 );
+            BLOCK( s0, s1 );
+    case 4:
+            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData -  4 );
+            BLOCK( s0, s1 );
+        }
+    }
+
+    pChain->s[0] = s0;
+    pChain->s[1] = s1;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32(
+    _In_                                            PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed,
+    _In_reads_( cbData )                            PCBYTE                              pbData,
+                                                    SIZE_T                              cbData,
+    _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE )   PBYTE                               pbResult )
+//
+// To reduce the per-computation overhead, we have a dedicated code here instead of the whole Init/Append/Result stuff.
+//
+{
+    UINT32 tmp;
+
+    UINT32 s0 = pExpandedSeed->s[0];
+    UINT32 s1 = pExpandedSeed->s[1];
+
+    while( cbData > 7 )
+    {
+        s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData );
+        BLOCK( s0, s1 );
+        s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData + 4 );
+        BLOCK( s0, s1 );
+        pbData += 8;
+        cbData -= 8;
+    }
+
+    /*
+    switch( cbData )
+    {
+    case 3:
+        buf[2] = pbData[2];
+    case 2:
+        *(UINT16 *) &buf[0] = *(UINT16 *) pbData;
+        break;
+    case 1:
+        buf[0] = pbData[0];
+    case 0:
+        ;
+    }
+
+    buf[ cbData ] = 0x80;
+
+    s0 += LOAD_LSBFIRST32( buf );
+    */
+
+
+    switch( cbData )
+    {
+    default:
+    case 4: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
+    case 0: tmp = 0x80; break;
+
+    case 5: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
+    case 1: tmp = 0x8000 | pbData[0]; break;
+
+    case 6: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
+    case 2: tmp = 0x800000 | SYMCRYPT_LOAD_LSBFIRST16( pbData ); break;
+
+    case 7: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
+    case 3: tmp = SYMCRYPT_LOAD_LSBFIRST16( pbData ) | (pbData[2] << 16) | 0x80000000; break;
+    }
+    s0 += tmp;
+
+
+    BLOCK( s0, s1 );
+    BLOCK( s0, s1 );
+
+    SYMCRYPT_STORE_LSBFIRST32( pbResult    , s0 );
+    SYMCRYPT_STORE_LSBFIRST32( pbResult + 4, s1 );
+}
+
+
+
+//
+// Simple test vector
+//
+
+static const BYTE   marvin32KATAnswer[ 8 ] = {
+    0xbf, 0x69, 0x27, 0x49, 0x39, 0x43, 0xc7, 0x22,
+} ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32Selftest(void)
+{
+    BYTE res[SYMCRYPT_MARVIN32_RESULT_SIZE];
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+    if( memcmp( res, marvin32KATAnswer, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'marv' );
+    }
+}
diff --git a/libs/symcrypt/lib/md2.c b/libs/symcrypt/lib/md2.c
new file mode 100644
index 000000000000..b754df333ce5
--- /dev/null
+++ b/libs/symcrypt/lib/md2.c
@@ -0,0 +1,307 @@
+//
+// Md2.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+// This module contains the routines to implement MD2 from RFC 1319
+//
+// This is a new implementation, NOT based on the existing one in RSA32.lib,
+// which is the one from RSA data security.
+//
+// The implementation had to be refreshed anyway to conform to our coding
+// guidelines for cryptographic functions.
+// Re-implementing the function along the lines of our SHA-family implementations
+// was easy, and it removes a file with RSA copyright from our system.
+//
+// The only data copied for this implementation is the S table from the
+// RFC.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+const SYMCRYPT_HASH SymCryptMd2Algorithm_default = {
+    &SymCryptMd2Init,
+    &SymCryptMd2Append,
+    &SymCryptMd2Result,
+    &SymCryptMd2AppendBlocks,
+    &SymCryptMd2StateCopy,
+    sizeof( SYMCRYPT_MD2_STATE ),
+    SYMCRYPT_MD2_RESULT_SIZE,
+    SYMCRYPT_MD2_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MD2_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_MD2_STATE, chain ),
+};
+
+const PCSYMCRYPT_HASH SymCryptMd2Algorithm = &SymCryptMd2Algorithm_default;
+
+//
+// These entries are called S[i] in RFC1319
+//
+const BYTE SymCryptMd2STable[256] = {
+    41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
+    19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
+    76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
+    138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
+    245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
+    148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
+    39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
+    181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
+    150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
+    112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
+    96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
+    85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
+    234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
+    129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
+    8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
+    203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
+    166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
+    31, 26, 219, 153, 141, 51, 159, 17, 131, 20
+};
+
+
+//
+// SymCryptMd2
+//
+#define ALG MD2
+#define Alg Md2
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+//
+// SymCryptMd2Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Init( _Out_ PSYMCRYPT_MD2_STATE  pState )
+{
+    //
+    // We use the secure wipe as the init routine is also used to re-initialize
+    // (and wipe) the state after a hash computation.
+    // In that case the compiler might conclude that this wipe can be optimized
+    // away, and that would leak data.
+    //
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SYMCRYPT_SET_MAGIC( pState );
+}
+
+
+//
+// SymCryptMd2Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Append( _Inout_              PSYMCRYPT_MD2_STATE    pState,
+                   _In_reads_( cbData ) PCBYTE                 pbData,
+                                        SIZE_T                 cbData )
+{
+    SymCryptHashAppendInternal( SymCryptMd2Algorithm, (PSYMCRYPT_COMMON_HASH_STATE)pState, pbData, cbData );
+}
+
+
+//
+// SymCryptMd2Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Result(  _Inout_                                 PSYMCRYPT_MD2_STATE state,
+                   _Out_writes_( SYMCRYPT_MD2_RESULT_SIZE ) PBYTE               pbResult )
+{
+    //
+    // The buffer is never completely full, so it is easy to compute the actual padding.
+    //
+    SIZE_T tmp;
+    SIZE_T paddingBytes = 16 - state->bytesInBuffer;
+
+
+    SYMCRYPT_CHECK_MAGIC( state );
+
+    memset( &state->buffer[state->bytesInBuffer], (BYTE)paddingBytes, paddingBytes );
+
+    SymCryptMd2AppendBlocks( &state->chain, state->buffer, SYMCRYPT_MD2_INPUT_BLOCK_SIZE, &tmp );
+
+    //
+    // Append the checksum
+    //
+    SymCryptMd2AppendBlocks( &state->chain, state->chain.C, SYMCRYPT_MD2_INPUT_BLOCK_SIZE, &tmp );
+
+    memcpy( pbResult, &state->chain.X[0], SYMCRYPT_MD2_RESULT_SIZE );
+
+    //
+    // Wipe & re-initialize
+    //
+    // (Our init code wipes the buffer too, so we don't have to.)
+    //
+    SymCryptMd2Init( state );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2AppendBlocks(
+    _Inout_                 PSYMCRYPT_MD2_CHAINING_STATE    pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining )
+{
+    //
+    // For variable names see RFC 1319.
+    //
+    unsigned int t;
+    int j,k;
+
+    while( cbData >= SYMCRYPT_MD2_INPUT_BLOCK_SIZE )
+    {
+        BYTE L;
+        //
+        // read the data once into our structure
+        //
+        memcpy( &pChain->X[16], pbData, SYMCRYPT_MD2_INPUT_BLOCK_SIZE );
+
+        //
+        // Update the checksum block.
+        // The L value at the end of the previous block is in the last byte of the checksum
+        //
+        L = pChain->C[15];
+
+        for( j=0; j<16; j++ )
+        {
+            pChain->C[j] = L = pChain->C[j] ^  SymCryptMd2STable[ L ^ pChain->X[16+j] ];
+        }
+
+        //
+        // Now we compute the actual hash
+        //
+        SymCryptXorBytes( &pChain->X[0], &pChain->X[16], &pChain->X[32], 16 );
+
+        t = 0;
+        for( j=0; j<18; j++ )
+        {
+            for( k=0; k<48; k++ )
+            {
+                t = pChain->X[k] ^ SymCryptMd2STable[t];
+                pChain->X[k] = (BYTE) t;
+            }
+            t = (t + j)& 0xff;
+        }
+
+    pbData += SYMCRYPT_MD2_INPUT_BLOCK_SIZE;
+    cbData -= SYMCRYPT_MD2_INPUT_BLOCK_SIZE;
+    }
+
+    *pcbRemaining = cbData;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2StateExport(
+    _In_                                                    PCSYMCRYPT_MD2_STATE    pState,
+    _Out_writes_bytes_( SYMCRYPT_MD2_STATE_EXPORT_SIZE )    PBYTE                   pbBlob )
+{
+    SYMCRYPT_ALIGN SYMCRYPT_MD2_STATE_EXPORT_BLOB    blob;           // local copy to have proper alignment.
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_MD2_STATE_EXPORT_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) ); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_MD2_STATE_EXPORT_SIZE;
+    blob.header.type = SymCryptBlobTypeMd2State;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+    memcpy( &blob.C[0], &pState->chain.C[0], 16 );
+    memcpy( &blob.X[0], &pState->chain.X[0], 16 );
+    blob.bytesInBuffer = (UINT32) pState->bytesInBuffer;
+    memcpy( &blob.buffer[0], &pState->buffer[0], blob.bytesInBuffer );
+
+    SYMCRYPT_ASSERT( (PCBYTE) &blob + sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ) == (PCBYTE) &blob.trailer );
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), &blob.trailer.checksum[0] );
+
+    memcpy( pbBlob, &blob, sizeof( blob ) );
+
+//cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) );
+    return;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMd2StateImport(
+    _Out_                                               PSYMCRYPT_MD2_STATE     pState,
+    _In_reads_bytes_( SYMCRYPT_MD2_STATE_EXPORT_SIZE)   PCBYTE                  pbBlob )
+{
+    SYMCRYPT_ERROR                  scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_ALIGN SYMCRYPT_MD2_STATE_EXPORT_BLOB  blob;                       // local copy to have proper alignment.
+    BYTE                            checksum[8];
+
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_MD2_STATE_EXPORT_SIZE );
+    memcpy( &blob, pbBlob, sizeof( blob ) );
+
+    if( blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_MD2_STATE_EXPORT_SIZE ||
+        blob.header.type != SymCryptBlobTypeMd2State )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), checksum );
+    if( memcmp( checksum, &blob.trailer.checksum[0], 8 ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    memcpy( &pState->chain.C[0], &blob.C[0], 16 );
+    memcpy( &pState->chain.X[0], &blob.X[0], 16 );
+    memcpy( &pState->buffer[0], &blob.buffer[0], 16 );
+    pState->bytesInBuffer = blob.bytesInBuffer;
+
+    pState->dataLengthL = blob.bytesInBuffer;
+    pState->dataLengthH = 1;
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof(blob) );
+    return scError;
+}
+
+
+
+
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE   md2KATAnswer[ 16 ] = {
+    0xda, 0x85, 0x3b, 0x0d, 0x3f, 0x88, 0xd9, 0x9b,
+    0x30, 0x28, 0x3a, 0x69, 0xe6, 0xde, 0xd6, 0xbb,
+} ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd2Selftest(void)
+{
+    BYTE result[SYMCRYPT_MD2_RESULT_SIZE];
+
+    SymCryptMd2( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, md2KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'MD2t' );
+    }
+}
diff --git a/libs/symcrypt/lib/md4.c b/libs/symcrypt/lib/md4.c
new file mode 100644
index 000000000000..759e317c3a8c
--- /dev/null
+++ b/libs/symcrypt/lib/md4.c
@@ -0,0 +1,425 @@
+//
+// Md4.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement MD4 from RFC 1320
+//
+//
+// This is a new implementation, NOT based on the existing ones in RSA32.lib.
+// There are 2 versions in RSA32.lib, one from RSA data security and one from
+// Scott Fields.
+//
+// MD4 and MD5 are extremely similar. Having already done a new MD5 implementation it
+// was very little work to copy the code & turn it into an MD4 implementation.
+// In fact, it was easier than reviewing & modifying the old code to bring it up to
+// the current implementation guidelines.
+//
+// This also ensures that this file is not a derived work from RSA data security
+// code which simplifies the copyright situation.
+//
+// We dropped the assembler implementation. MD4 is so weak that it should be removed
+// from use, not sped up.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+const SYMCRYPT_HASH SymCryptMd4Algorithm_default = {
+    &SymCryptMd4Init,
+    &SymCryptMd4Append,
+    &SymCryptMd4Result,
+    &SymCryptMd4AppendBlocks,
+    &SymCryptMd4StateCopy,
+    sizeof( SYMCRYPT_MD4_STATE ),
+    SYMCRYPT_MD4_RESULT_SIZE,
+    SYMCRYPT_MD4_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MD4_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_MD4_STATE, chain ),
+};
+
+const PCSYMCRYPT_HASH SymCryptMd4Algorithm = &SymCryptMd4Algorithm_default;
+
+//
+// The round constants used by MD4
+//
+//
+static const UINT32 md4Const[3] = {
+    0x00000000UL,
+    0x5A827999UL,
+    0x6ED9EBA1UL,
+    };
+
+//
+// Round rotation amounts. This array is optimized away by the compiler
+// as we inline all our rotations.
+//
+static const int md4Rotate[48] = {
+    3, 7, 11, 19,
+    3, 7, 11, 19,
+    3, 7, 11, 19,
+    3, 7, 11, 19,
+
+    3, 5, 9, 13,
+    3, 5, 9, 13,
+    3, 5, 9, 13,
+    3, 5, 9, 13,
+
+    3, 9, 11, 15,
+    3, 9, 11, 15,
+    3, 9, 11, 15,
+    3, 9, 11, 15,
+
+};
+
+//
+// Message word index table. This array is optimized away by the compiler
+// as we inline all our accesses.
+//
+static const int md4MsgIndex[48] = {
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+     0,  4,  8, 12,  1,  5,  9, 13,  2,  6, 10, 14,  3,  7, 11, 15,
+     0,  8,  4, 12,  2, 10,  6, 14,  1,  9,  5, 13,  3, 11,  7, 15,
+};
+
+//
+// Initial state
+//
+static const UINT32 md4InitialState[4] = {
+    0x67452301UL,
+    0xefcdab89UL,
+    0x98badcfeUL,
+    0x10325476UL,
+};
+
+
+//
+// SymCryptMd4
+//
+#define ALG MD4
+#define Alg Md4
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+
+//
+// SymCryptmd4Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Init( _Out_ PSYMCRYPT_MD4_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthL = 0;
+    pState->dataLengthH = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &md4InitialState[0], sizeof( md4InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+//
+// SymCryptMd4Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Append( _Inout_              PSYMCRYPT_MD4_STATE    pState,
+                   _In_reads_( cbData ) PCBYTE                 pbData,
+                                        SIZE_T                 cbData )
+{
+    SymCryptHashAppendInternal( SymCryptMd4Algorithm, (PSYMCRYPT_COMMON_HASH_STATE)pState, pbData, cbData );
+}
+
+
+//
+// SymCryptmd4Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Result(
+    _Inout_                                 PSYMCRYPT_MD4_STATE pState,
+   _Out_writes_( SYMCRYPT_MD4_RESULT_SIZE ) PBYTE               pbResult )
+{
+    SymCryptHashCommonPaddingMd4Style( SymCryptMd4Algorithm, (PSYMCRYPT_COMMON_HASH_STATE)pState );
+
+    //
+    // Write the output in the correct byte order
+    //
+    SymCryptUint32ToLsbFirst( &pState->chain.H[0], pbResult, 4 );
+
+    //
+    // Wipe & re-initialize
+    // We have to wipe the whole state as the initialization might be optimized away.
+    //
+    SymCryptWipeKnownSize( pState, sizeof( *pState ));
+    SymCryptMd4Init( pState );
+    }
+
+
+//
+// For documentation on these function see rfc-1320
+//
+//#define F( x, y, z )     (((x) & (y)) | ((~(x)) & (z)))
+//#define G( x, y, z )    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+#define F( x, y, z )    ((((z) ^ (y)) & (x)) ^ (z))
+#define G( x, y, z )    ((((x) | (y)) & (z) ) | ((x) & (y)))
+#define H( x, y, z )    ((x) ^ (y) ^ (z) )
+
+//
+// The values a-d are stored in an array called ad.
+// We have unrolled the code completely. This makes both the indices into
+// the ad array constant, and it makes the message addressing constant.
+//
+// We copy the message into our own buffer to obey the read-once rule.
+// Memory is sometimes aliased so that multiple threads or processes can access
+// the same memory at the same time. With MD4 there is a danger that some other
+// process could modify the memory while the computation is ongoing and introduce
+// changes in the computation not envisioned by the designers or cryptanalists.
+// At this level in the library we cannot guarantee that this is not the case,
+// and we can't trust the higher layers to respect a don't-change-it-while-computing-md4
+// restriction. (In practice, such restrictions are lost through the many
+// layers in the stack.)
+//
+
+//
+// r is the round number
+// ad[(r+0)%4] = a;
+// ad[(r+1)%4] = d;
+// ad[(r+2)%4] = c;
+// ad[(r+3)%4] = b;
+//
+// When r increments the register re-naming is automatically correct.
+//
+
+//
+// CROUND is the core round function
+//
+#define CROUND( r, Func ) { \
+    ad[r%4] = ROL32( ad[r%4] + Func(ad[(r+3)%4], ad[(r+2)%4], ad[(r+1)%4]) + Wt + md4Const[r/16], md4Rotate[r] ); \
+}
+
+//
+// IROUND is the initial round that loads the message and copies it into our buffer.
+//
+#define IROUND( r, Func ) { \
+    Wt = SYMCRYPT_LOAD_LSBFIRST32( &pbData[ 4*md4MsgIndex[r] ] ); \
+    W[r] = Wt; \
+    CROUND( r, Func ); \
+}
+
+//
+// FROUND are the subsequent rounds.
+//
+#define FROUND( r, Func ) { \
+    Wt = W[md4MsgIndex[r]];\
+    CROUND( r, Func ); \
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4AppendBlocks(
+    _Inout_                 PSYMCRYPT_MD4_CHAINING_STATE    pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining )
+{
+
+    SYMCRYPT_ALIGN UINT32 W[16];
+    SYMCRYPT_ALIGN UINT32 ad[4];
+    UINT32 Wt;
+
+    ad[0] = pChain->H[0];
+    ad[1] = pChain->H[3];
+    ad[2] = pChain->H[2];
+    ad[3] = pChain->H[1];
+
+    while( cbData >= 64 )
+    {
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND(  0, F );
+        IROUND(  1, F );
+        IROUND(  2, F );
+        IROUND(  3, F );
+        IROUND(  4, F );
+        IROUND(  5, F );
+        IROUND(  6, F );
+        IROUND(  7, F );
+        IROUND(  8, F );
+        IROUND(  9, F );
+        IROUND( 10, F );
+        IROUND( 11, F );
+        IROUND( 12, F );
+        IROUND( 13, F );
+        IROUND( 14, F );
+        IROUND( 15, F );
+
+        FROUND( 16, G );
+        FROUND( 17, G );
+        FROUND( 18, G );
+        FROUND( 19, G );
+        FROUND( 20, G );
+        FROUND( 21, G );
+        FROUND( 22, G );
+        FROUND( 23, G );
+        FROUND( 24, G );
+        FROUND( 25, G );
+        FROUND( 26, G );
+        FROUND( 27, G );
+        FROUND( 28, G );
+        FROUND( 29, G );
+        FROUND( 30, G );
+        FROUND( 31, G );
+
+        FROUND( 32, H );
+        FROUND( 33, H );
+        FROUND( 34, H );
+        FROUND( 35, H );
+        FROUND( 36, H );
+        FROUND( 37, H );
+        FROUND( 38, H );
+        FROUND( 39, H );
+        FROUND( 40, H );
+        FROUND( 41, H );
+        FROUND( 42, H );
+        FROUND( 43, H );
+        FROUND( 44, H );
+        FROUND( 45, H );
+        FROUND( 46, H );
+        FROUND( 47, H );
+
+        pChain->H[0] = ad[0] = ad[0] + pChain->H[0];
+        pChain->H[3] = ad[1] = ad[1] + pChain->H[3];
+        pChain->H[2] = ad[2] = ad[2] + pChain->H[2];
+        pChain->H[1] = ad[3] = ad[3] + pChain->H[1];
+
+        pbData += 64;
+        cbData -= 64;
+    }
+
+    *pcbRemaining = cbData;
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( ad, sizeof( ad ) );
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SYMCRYPT_FORCE_WRITE32( &Wt, 0 );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4StateExport(
+    _In_                                                    PCSYMCRYPT_MD4_STATE    pState,
+    _Out_writes_bytes_( SYMCRYPT_MD4_STATE_EXPORT_SIZE )    PBYTE                   pbBlob )
+{
+    SYMCRYPT_ALIGN SYMCRYPT_MD4_STATE_EXPORT_BLOB    blob;           // local copy to have proper alignment.
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_MD4_STATE_EXPORT_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) ); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_MD4_STATE_EXPORT_SIZE;
+    blob.header.type = SymCryptBlobTypeMd4State;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+
+    SymCryptUint32ToLsbFirst( &pState->chain.H[0], &blob.chain[0], 4 );
+    blob.dataLength = pState->dataLengthL;
+    memcpy( &blob.buffer[0], &pState->buffer[0], blob.dataLength & 0x3f );
+
+    SYMCRYPT_ASSERT( (PCBYTE) &blob + sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ) == (PCBYTE) &blob.trailer );
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), &blob.trailer.checksum[0] );
+
+    memcpy( pbBlob, &blob, sizeof( blob ) );
+
+//cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) );
+    return;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMd4StateImport(
+    _Out_                                               PSYMCRYPT_MD4_STATE     pState,
+    _In_reads_bytes_( SYMCRYPT_MD4_STATE_EXPORT_SIZE)   PCBYTE                  pbBlob )
+{
+    SYMCRYPT_ERROR                  scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_ALIGN SYMCRYPT_MD4_STATE_EXPORT_BLOB  blob;                       // local copy to have proper alignment.
+    BYTE                            checksum[8];
+
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_MD4_STATE_EXPORT_SIZE );
+    memcpy( &blob, pbBlob, sizeof( blob ) );
+
+    if( blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_MD4_STATE_EXPORT_SIZE ||
+        blob.header.type != SymCryptBlobTypeMd4State )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), checksum );
+    if( memcmp( checksum, &blob.trailer.checksum[0], 8 ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptLsbFirstToUint32( &blob.chain[0], &pState->chain.H[0], 4 );
+    pState->dataLengthL = blob.dataLength;
+    pState->dataLengthH = 0;
+    pState->bytesInBuffer = blob.dataLength & 0x3f;
+    memcpy( &pState->buffer[0], &blob.buffer[0], pState->bytesInBuffer );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof(blob) );
+    return scError;
+}
+
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE   md4KATAnswer[ 16 ] = {
+    0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52,
+    0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d,
+} ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd4Selftest(void)
+{
+    BYTE result[SYMCRYPT_MD4_RESULT_SIZE];
+
+    SymCryptMd4( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, md4KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'MD4t' );
+    }
+}
diff --git a/libs/symcrypt/lib/md5.c b/libs/symcrypt/lib/md5.c
new file mode 100644
index 000000000000..c5de50f26462
--- /dev/null
+++ b/libs/symcrypt/lib/md5.c
@@ -0,0 +1,503 @@
+//
+// Md5.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement MD5 from RFC 1321
+//
+//
+// This is a new implementation, NOT based on the existing one in RSA32.lib,
+// which is the one from RSA data security. RFC-1321 also contains code that
+// at a glance looks very similar to the RSA32.lib code.
+//
+// The implementation had to be refreshed anyway to conform to our coding
+// guidelines for cryptographic functions.
+// Re-implementing the function along the lines of our SHA-family implementations
+// was easy, and it removes one file with RSA copyright from our system.
+//
+// The only data copied for this implementation is the round constant values
+// which were copied from the RFC.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+const SYMCRYPT_HASH SymCryptMd5Algorithm_default = {
+    &SymCryptMd5Init,
+    &SymCryptMd5Append,
+    &SymCryptMd5Result,
+    &SymCryptMd5AppendBlocks,
+    &SymCryptMd5StateCopy,
+    sizeof( SYMCRYPT_MD5_STATE ),
+    SYMCRYPT_MD5_RESULT_SIZE,
+    SYMCRYPT_MD5_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_MD5_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_MD5_STATE, chain ),
+};
+
+const PCSYMCRYPT_HASH SymCryptMd5Algorithm = &SymCryptMd5Algorithm_default;
+
+//
+// The round constants used by MD5
+//
+// These are called T[i] in RFC1321 although T[i] uses the range [1..64] and we use [0..63]
+// This array should be optimized away by the compiler as all values are inlined.
+//
+static const UINT32 md5Const[64] = {
+    0xd76aa478UL,
+    0xe8c7b756UL,
+    0x242070dbUL,
+    0xc1bdceeeUL,
+    0xf57c0fafUL,
+    0x4787c62aUL,
+    0xa8304613UL,
+    0xfd469501UL,
+    0x698098d8UL,
+    0x8b44f7afUL,
+    0xffff5bb1UL,
+    0x895cd7beUL,
+    0x6b901122UL,
+    0xfd987193UL,
+    0xa679438eUL,
+    0x49b40821UL,
+    0xf61e2562UL,
+    0xc040b340UL,
+    0x265e5a51UL,
+    0xe9b6c7aaUL,
+    0xd62f105dUL,
+    0x02441453UL,
+    0xd8a1e681UL,
+    0xe7d3fbc8UL,
+    0x21e1cde6UL,
+    0xc33707d6UL,
+    0xf4d50d87UL,
+    0x455a14edUL,
+    0xa9e3e905UL,
+    0xfcefa3f8UL,
+    0x676f02d9UL,
+    0x8d2a4c8aUL,
+    0xfffa3942UL,
+    0x8771f681UL,
+    0x6d9d6122UL,
+    0xfde5380cUL,
+    0xa4beea44UL,
+    0x4bdecfa9UL,
+    0xf6bb4b60UL,
+    0xbebfbc70UL,
+    0x289b7ec6UL,
+    0xeaa127faUL,
+    0xd4ef3085UL,
+    0x04881d05UL,
+    0xd9d4d039UL,
+    0xe6db99e5UL,
+    0x1fa27cf8UL,
+    0xc4ac5665UL,
+    0xf4292244UL,
+    0x432aff97UL,
+    0xab9423a7UL,
+    0xfc93a039UL,
+    0x655b59c3UL,
+    0x8f0ccc92UL,
+    0xffeff47dUL,
+    0x85845dd1UL,
+    0x6fa87e4fUL,
+    0xfe2ce6e0UL,
+    0xa3014314UL,
+    0x4e0811a1UL,
+    0xf7537e82UL,
+    0xbd3af235UL,
+    0x2ad7d2bbUL,
+    0xeb86d391UL,
+};
+
+//
+// Round rotation amounts. This array is optimized away by the compiler
+// as we inline all our rotations.
+//
+static const int md5Rotate[64] = {
+    7, 12, 17, 22,
+    7, 12, 17, 22,
+    7, 12, 17, 22,
+    7, 12, 17, 22,
+
+    5, 9, 14, 20,
+    5, 9, 14, 20,
+    5, 9, 14, 20,
+    5, 9, 14, 20,
+
+    4, 11, 16, 23,
+    4, 11, 16, 23,
+    4, 11, 16, 23,
+    4, 11, 16, 23,
+
+    6, 10, 15, 21,
+    6, 10, 15, 21,
+    6, 10, 15, 21,
+    6, 10, 15, 21,
+};
+
+//
+// Message word index table. This array is optimized away by the compiler
+// as we inline all our accesses.
+//
+static const int md5MsgIndex[64] = {
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+     1,  6, 11,  0,  5, 10, 15,  4,  9, 14,  3,  8, 13,  2,  7, 12,
+     5,  8, 11, 14,  1,  4,  7, 10, 13,  0,  3,  6,  9, 12, 15,  2,
+     0,  7, 14,  5, 12,  3, 10,  1,  8, 15,  6, 13,  4, 11,  2,  9,
+};
+
+//
+// Initial state
+//
+static const UINT32 md5InitialState[4] = {
+    0x67452301UL,
+    0xefcdab89UL,
+    0x98badcfeUL,
+    0x10325476UL,
+};
+
+//
+// SymCryptMd5
+//
+#define ALG MD5
+#define Alg Md5
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+
+
+//
+// SymCryptMd5Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Init( _Out_ PSYMCRYPT_MD5_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthL = 0;
+    pState->dataLengthH = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &md5InitialState[0], sizeof( md5InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+//
+// SymCryptMd5Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Append(
+    _Inout_             PSYMCRYPT_MD5_STATE pState,
+   _In_reads_( cbData ) PCBYTE              pbData,
+                        SIZE_T              cbData )
+{
+    SymCryptHashAppendInternal( SymCryptMd5Algorithm, (PSYMCRYPT_COMMON_HASH_STATE)pState, pbData, cbData );
+}
+
+//
+// SymCryptMd5Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Result(
+    _Inout_                                 PSYMCRYPT_MD5_STATE pState,
+   _Out_writes_( SYMCRYPT_MD5_RESULT_SIZE ) PBYTE               pbResult )
+{
+    SymCryptHashCommonPaddingMd4Style( SymCryptMd5Algorithm, (PSYMCRYPT_COMMON_HASH_STATE) pState );
+
+    //
+    // Write the output in the correct byte order
+    //
+    SymCryptUint32ToLsbFirst( &pState->chain.H[0], pbResult, 4 );
+
+    //
+    // Wipe & re-initialize
+    // We have to wipe the whole state because the Init call
+    // might be optimized away by a smart compiler.
+    // And we need to wipe old data.
+    //
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SymCryptMd5Init( pState );
+}
+
+
+//
+// For documentation on these function see rfc-1321
+//
+//#define F( x, y, z )     (((x) & (y)) ^ ((~(x)) & (z)))
+#define F( x, y, z )  ((((z) ^ (y)) & (x)) ^ (z))
+#define G( x, y, z )  F( (z), (x), (y) )
+#define H( x, y, z ) ((x) ^ (y) ^ (z) )
+#define I( x, y, z ) ((y) ^ ((x) | ~(z)))
+
+//
+// The values a-d are stored in an array called ad.
+// We have unrolled the code completely. This makes both the indices into
+// the ad array constant, and it makes the message addressing constant.
+//
+// We copy the message into our own buffer to obey the read-once rule.
+// Memory is sometimes aliased so that multiple threads or processes can access
+// the same memory at the same time. With MD5 there is a danger that some other
+// process could modify the memory while the computation is ongoing and introduce
+// changes in the computation not envisioned by the designers or cryptanalysts.
+// At this level in the library we cannot guarantee that this is not the case,
+// and we can't trust the higher layers to respect a don't-change-it-while-computing-md5
+// restriction. (In practice, such restrictions are lost through the many
+// layers in the stack.)
+//
+//
+// Initial round macro
+//
+// r is the round number
+// ad[(r+0)%4] = a;
+// ad[(r+1)%4] = d;
+// ad[(r+2)%4] = c;
+// ad[(r+3)%4] = b;
+//
+// When r increments the register re-naming is automatically correct.
+//
+#define CROUND( r, Func ) { \
+    ad[r%4] = ad[(r+3)%4] + ROL32( ad[r%4] + Func(ad[(r+3)%4], ad[(r+2)%4], ad[(r+1)%4]) + Wt + md5Const[r], md5Rotate[r] ); \
+}
+
+#define IROUND( r, Func ) { \
+    Wt = SYMCRYPT_LOAD_LSBFIRST32( &pbData[ 4*md5MsgIndex[r] ] ); \
+    W[r] = Wt; \
+    CROUND( r, Func ); \
+}
+
+//
+// Subsequent rounds.
+// This is the same as the IROUND except that it uses the copied message.
+//
+#define FROUND( r, Func ) { \
+    Wt = W[md5MsgIndex[r]];\
+    CROUND( r, Func ); \
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5AppendBlocks(
+    _Inout_                 SYMCRYPT_MD5_CHAINING_STATE   * pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining )
+{
+
+    UINT32 W[16];
+    UINT32 ad[4];
+    UINT32 Wt;
+
+    ad[0] = pChain->H[0];
+    ad[1] = pChain->H[3];
+    ad[2] = pChain->H[2];
+    ad[3] = pChain->H[1];
+
+    while( cbData >= 64 )
+    {
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND(  0, F );
+        IROUND(  1, F );
+        IROUND(  2, F );
+        IROUND(  3, F );
+        IROUND(  4, F );
+        IROUND(  5, F );
+        IROUND(  6, F );
+        IROUND(  7, F );
+        IROUND(  8, F );
+        IROUND(  9, F );
+        IROUND( 10, F );
+        IROUND( 11, F );
+        IROUND( 12, F );
+        IROUND( 13, F );
+        IROUND( 14, F );
+        IROUND( 15, F );
+
+        FROUND( 16, G );
+        FROUND( 17, G );
+        FROUND( 18, G );
+        FROUND( 19, G );
+        FROUND( 20, G );
+        FROUND( 21, G );
+        FROUND( 22, G );
+        FROUND( 23, G );
+        FROUND( 24, G );
+        FROUND( 25, G );
+        FROUND( 26, G );
+        FROUND( 27, G );
+        FROUND( 28, G );
+        FROUND( 29, G );
+        FROUND( 30, G );
+        FROUND( 31, G );
+
+        FROUND( 32, H );
+        FROUND( 33, H );
+        FROUND( 34, H );
+        FROUND( 35, H );
+        FROUND( 36, H );
+        FROUND( 37, H );
+        FROUND( 38, H );
+        FROUND( 39, H );
+        FROUND( 40, H );
+        FROUND( 41, H );
+        FROUND( 42, H );
+        FROUND( 43, H );
+        FROUND( 44, H );
+        FROUND( 45, H );
+        FROUND( 46, H );
+        FROUND( 47, H );
+
+        FROUND( 48, I );
+        FROUND( 49, I );
+        FROUND( 50, I );
+        FROUND( 51, I );
+        FROUND( 52, I );
+        FROUND( 53, I );
+        FROUND( 54, I );
+        FROUND( 55, I );
+        FROUND( 56, I );
+        FROUND( 57, I );
+        FROUND( 58, I );
+        FROUND( 59, I );
+        FROUND( 60, I );
+        FROUND( 61, I );
+        FROUND( 62, I );
+        FROUND( 63, I );
+
+        pChain->H[0] = ad[0] = ad[0] + pChain->H[0];
+        pChain->H[3] = ad[1] = ad[1] + pChain->H[3];
+        pChain->H[2] = ad[2] = ad[2] + pChain->H[2];
+        pChain->H[1] = ad[3] = ad[3] + pChain->H[1];
+
+        pbData += 64;
+        cbData -= 64;
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( ad, sizeof( ad ) );
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SymCryptWipeKnownSize( &Wt, sizeof( Wt ) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5StateExport(
+    _In_                                                    PCSYMCRYPT_MD5_STATE    pState,
+    _Out_writes_bytes_( SYMCRYPT_MD5_STATE_EXPORT_SIZE )    PBYTE                   pbBlob )
+{
+    SYMCRYPT_MD5_STATE_EXPORT_BLOB    blob;           // local copy to have proper alignment.
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_MD5_STATE_EXPORT_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) ); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_MD5_STATE_EXPORT_SIZE;
+    blob.header.type = SymCryptBlobTypeMd5State;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+
+    SymCryptUint32ToLsbFirst( &pState->chain.H[0], &blob.chain[0], 4 );
+    blob.dataLength = pState->dataLengthL;
+    memcpy( &blob.buffer[0], &pState->buffer[0], blob.dataLength & 0x3f );
+
+    SYMCRYPT_ASSERT( (PCBYTE) &blob + sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ) == (PCBYTE) &blob.trailer );
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), &blob.trailer.checksum[0] );
+
+    memcpy( pbBlob, &blob, sizeof( blob ) );
+
+//cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) );
+    return;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMd5StateImport(
+    _Out_                                               PSYMCRYPT_MD5_STATE     pState,
+    _In_reads_bytes_( SYMCRYPT_MD5_STATE_EXPORT_SIZE)   PCBYTE                  pbBlob )
+{
+    SYMCRYPT_ERROR                  scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_MD5_STATE_EXPORT_BLOB  blob;                       // local copy to have proper alignment.
+    BYTE                            checksum[8];
+
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_MD5_STATE_EXPORT_SIZE );
+    memcpy( &blob, pbBlob, sizeof( blob ) );
+
+    if( blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_MD5_STATE_EXPORT_SIZE ||
+        blob.header.type != SymCryptBlobTypeMd5State )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), checksum );
+    if( memcmp( checksum, &blob.trailer.checksum[0], 8 ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptLsbFirstToUint32( &blob.chain[0], &pState->chain.H[0], 4 );
+    pState->dataLengthL = blob.dataLength;
+    pState->dataLengthH = 0;
+    pState->bytesInBuffer = blob.dataLength & 0x3f;
+    memcpy( &pState->buffer[0], &blob.buffer[0], pState->bytesInBuffer );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof(blob) );
+    return scError;
+}
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE   md5KATAnswer[ 16 ] = {
+    0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
+    0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72,
+} ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMd5Selftest(void)
+{
+    BYTE result[SYMCRYPT_MD5_RESULT_SIZE];
+
+    SymCryptMd5( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, md5KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'MD5t' );
+    }
+}
diff --git a/libs/symcrypt/lib/mldsa.c b/libs/symcrypt/lib/mldsa.c
new file mode 100644
index 000000000000..2dca32703685
--- /dev/null
+++ b/libs/symcrypt/lib/mldsa.c
@@ -0,0 +1,1096 @@
+//
+// mldsa.c   ML-DSA related functionality
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+_Use_decl_annotations_
+PSYMCRYPT_MLDSAKEY
+SYMCRYPT_CALL
+SymCryptMlDsakeyAllocate(
+    SYMCRYPT_MLDSA_PARAMS params )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PSYMCRYPT_MLDSAKEY pkMlDsakey = NULL;
+    PSYMCRYPT_MLDSA_INTERNAL_PARAMS pInternalParams = NULL;
+    PBYTE pbKey = NULL;
+    UINT32 cbKey = 0;
+
+    scError = SymCryptMlDsaGetInternalParamsFromParams( params, &pInternalParams );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    cbKey = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_KEY( pInternalParams->nRows, pInternalParams->nCols );
+    pbKey = SymCryptCallbackAlloc( cbKey );
+    if( pbKey == NULL )
+    {
+        goto cleanup;
+    }
+
+    pkMlDsakey = SymCryptMlDsakeyInitialize( pInternalParams, pbKey, cbKey );
+    if( pkMlDsakey == NULL )
+    {
+        goto cleanup;
+    }
+
+    // On success, memory is owned by pkMlDsakey
+    pbKey = NULL;
+
+cleanup:
+    if( pbKey != NULL )
+    {
+        SymCryptCallbackFree( pbKey );
+    }
+
+    return pkMlDsakey;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsakeyFree(
+    PSYMCRYPT_MLDSAKEY pkMlDsakey )
+{
+    SYMCRYPT_CHECK_MAGIC( pkMlDsakey );
+
+    SymCryptWipe( pkMlDsakey, pkMlDsakey->cbTotalSize );
+    SymCryptCallbackFree( pkMlDsakey );
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaKeyGenerateEx(
+    PSYMCRYPT_MLDSAKEY  pkMlDsakey,
+    PCBYTE              pbRootSeed,
+    SIZE_T              cbRootSeed,
+    UINT32              flags )
+{
+    UNREFERENCED_PARAMETER( flags );
+
+    SYMCRYPT_ASSERT( cbRootSeed == SYMCRYPT_MLDSA_ROOT_SEED_SIZE );
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams = pkMlDsakey->pParams;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+
+    BYTE privateVectorSeed[SYMCRYPT_MLDSA_PRIVATE_VECTOR_SEED_SIZE];
+
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize(
+        pParams,
+        1, // row vectors
+        0, // column vectors
+        1, // poly elements
+        pParams->cbEncodedPublicKey ); // scratch space
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    memcpy( pkMlDsakey->rootSeed, pbRootSeed, cbRootSeed );
+
+    {
+        PSYMCRYPT_SHAKE256_STATE pShakeState = &(pTemps->shake256State);
+        SymCryptShake256Init( pShakeState );
+        SymCryptShake256Append( pShakeState, pkMlDsakey->rootSeed, cbRootSeed );
+        SymCryptShake256Append( pShakeState, (PCBYTE) &pParams->nRows, sizeof(BYTE) );
+        SymCryptShake256Append( pShakeState, (PCBYTE) &pParams->nCols, sizeof(BYTE) );
+
+        SymCryptShake256Extract( pShakeState, pkMlDsakey->publicSeed, sizeof(pkMlDsakey->publicSeed), FALSE);
+        SymCryptShake256Extract( pShakeState, privateVectorSeed, sizeof(privateVectorSeed), FALSE );
+        SymCryptShake256Extract( pShakeState, pkMlDsakey->privateSigningSeed, sizeof(pkMlDsakey->privateSigningSeed), FALSE); // Wiped when pTemps is freed
+    }
+
+    SymCryptMlDsaExpandA( pkMlDsakey->publicSeed, sizeof(pkMlDsakey->publicSeed), pkMlDsakey->pmA );
+
+    SymCryptMlDsaExpandS(
+        pkMlDsakey->pParams,
+        privateVectorSeed,
+        sizeof(privateVectorSeed),
+        pkMlDsakey->pvs1,
+        pkMlDsakey->pvs2 );
+
+    // Convert s1 and s2 to NTT form
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvs1 );
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvs2 );
+
+    SymCryptMlDsakeyComputeT(
+        pkMlDsakey->pmA,
+        pkMlDsakey->pvs1,
+        pkMlDsakey->pvs2,
+        pkMlDsakey->pvt0,
+        pkMlDsakey->pvt1,
+        pTemps->pvRowVectors[0],
+        pTemps->pePolyElements[0] );
+
+    // Convert t0 and t1 to NTT form
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvt0 );
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvt1 );
+
+    scError = SymCryptMlDsaPkEncode( pkMlDsakey, pTemps->pbScratch, pParams->cbEncodedPublicKey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    SymCryptShake256(
+        pTemps->pbScratch,
+        pParams->cbEncodedPublicKey,
+        pkMlDsakey->publicKeyHash,
+        sizeof(pkMlDsakey->publicKeyHash) );
+
+    pkMlDsakey->hasRootSeed = TRUE;
+    pkMlDsakey->hasPrivateKey = TRUE;
+
+cleanup:
+    if( pTemps != NULL )
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    SymCryptWipeKnownSize( privateVectorSeed, sizeof(privateVectorSeed) );
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsakeyGenerate(
+    PSYMCRYPT_MLDSAKEY  pkMlDsakey,
+    UINT32              flags)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BYTE random[SYMCRYPT_MLDSA_ROOT_SEED_SIZE];
+    PBYTE  pbPctSignature = NULL;
+    SIZE_T cbPctSignature = 0;
+
+    // Ensure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+
+    if ( ( flags & ~allowedFlags ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptCallbackRandom( random, sizeof(random) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsakeySetValue( random, sizeof(random), SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED, flags, pkMlDsakey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // SymCryptMlDsakeySetValue ensures the self-test is run before
+    // first operational use of MlDsa
+
+    if( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // PCT on key generation, sign/verify the empty message with the generated key
+
+        cbPctSignature = pkMlDsakey->pParams->cbEncodedSignature;
+
+        pbPctSignature = SymCryptCallbackAlloc( cbPctSignature );
+        if( pbPctSignature == NULL )
+        {
+            scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+            goto cleanup;
+        }
+
+        scError = SymCryptMlDsaSign( pkMlDsakey,
+            NULL, 0,
+            NULL, 0,
+            0,
+            pbPctSignature, cbPctSignature );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        scError = SymCryptMlDsaVerify( pkMlDsakey,
+            NULL, 0,
+            NULL, 0,
+            pbPctSignature, cbPctSignature,
+            0 );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        // could track having run the PCT with a flag in pkMlDsakey->fAlgorithmInfo,
+        // but currently no need to do that given we don't ever defer the PCT
+    }
+
+cleanup:
+    if( pbPctSignature != NULL )
+    {
+        // Wiping is not required for security, but has low relative cost
+        // and better to be on the safe side for FIPS
+        SymCryptWipe( pbPctSignature, cbPctSignature );
+        SymCryptCallbackFree( pbPctSignature );
+    }
+
+    SymCryptWipeKnownSize( random, sizeof(random) );
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsakeySetValue(
+    PCBYTE                      pbSrc,
+    SIZE_T                      cbSrc,
+    SYMCRYPT_MLDSAKEY_FORMAT    mlDsakeyFormat,
+    UINT32                      flags,
+    PSYMCRYPT_MLDSAKEY          pkMlDsakey )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // Ensure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+
+    if ( ( flags & ~allowedFlags ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // Ensure ML-DSA algorithm selftest is run before first use of ML-DSA algorithms;
+        // notably _before_ first full KeyGen
+        SYMCRYPT_RUN_SELFTEST_ONCE(
+            SymCryptMlDsaSelftest,
+            SYMCRYPT_SELFTEST_ALGORITHM_MLDSA);
+    }
+
+    switch( mlDsakeyFormat )
+    {
+        case SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED:
+            if( cbSrc != SYMCRYPT_MLDSA_ROOT_SEED_SIZE )
+            {
+                scError = SYMCRYPT_WRONG_KEY_SIZE;
+                goto cleanup;
+            }
+
+            scError = SymCryptMlDsaKeyGenerateEx( pkMlDsakey, pbSrc, cbSrc, flags );
+            break;
+        case SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_KEY:
+            scError = SymCryptMlDsaSkDecode( pbSrc, cbSrc, flags, pkMlDsakey );
+            break;
+        case SYMCRYPT_MLDSAKEY_FORMAT_PUBLIC_KEY:
+            scError = SymCryptMlDsaPkDecode( pbSrc, cbSrc, flags, pkMlDsakey );
+            break;
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            break;
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsakeyGetValue(
+    PCSYMCRYPT_MLDSAKEY         pkMlDsakey,
+    PBYTE                       pbDst,
+    SIZE_T                      cbDst,
+    SYMCRYPT_MLDSAKEY_FORMAT    mlDsakeyFormat,
+    UINT32                      flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( flags != 0 ) // No flags currently supported
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    switch( mlDsakeyFormat )
+    {
+        case SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_KEY:
+            scError = SymCryptMlDsaSkEncode(
+                pkMlDsakey,
+                pbDst,
+                cbDst );
+            break;
+        case SYMCRYPT_MLDSAKEY_FORMAT_PUBLIC_KEY:
+            scError = SymCryptMlDsaPkEncode(
+                pkMlDsakey,
+                pbDst,
+                cbDst );
+            break;
+        case SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED:
+            if( cbDst < SYMCRYPT_MLDSA_ROOT_SEED_SIZE )
+            {
+                scError = SYMCRYPT_BUFFER_TOO_SMALL;
+                goto cleanup;
+            }
+
+            if( !pkMlDsakey->hasRootSeed )
+            {
+                scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+                goto cleanup;
+            }
+
+            memcpy( pbDst, pkMlDsakey->rootSeed, SYMCRYPT_MLDSA_ROOT_SEED_SIZE );
+
+            break;
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            break;
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSignEx(
+    PCSYMCRYPT_MLDSAKEY     pkMlDsakey,
+    PCBYTE                  pbInput,
+    SIZE_T                  cbInput,
+    PCBYTE                  pbContext,
+    SIZE_T                  cbContext,
+    PCBYTE                  pbHashOid,
+    SIZE_T                  cbHashOid,
+    PCBYTE                  pbRandom,
+    SIZE_T                  cbRandom,
+    UINT32                  flags,
+    PBYTE                   pbSignature,
+    SIZE_T                  cbSignature )
+{
+    SYMCRYPT_ASSERT( pkMlDsakey->hasPrivateKey == TRUE );
+    SYMCRYPT_ASSERT( cbContext <= SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH );
+    SYMCRYPT_ASSERT( cbRandom == SYMCRYPT_MLDSA_SIGNING_RANDOM_SIZE );
+    SYMCRYPT_ASSERT( pbHashOid != NULL || cbHashOid == 0 );
+    SYMCRYPT_ASSERT( pbContext != NULL || cbContext == 0 );
+    SYMCRYPT_ASSERT( cbSignature == pkMlDsakey->pParams->cbEncodedSignature );
+    SYMCRYPT_ASSERT( (flags & ~SYMCRYPT_FLAG_MLDSA_EXTERNALMU) == 0 );
+    SYMCRYPT_ASSERT( ((flags & SYMCRYPT_FLAG_MLDSA_EXTERNALMU) == 0) || (pbContext == NULL && pbHashOid == NULL) );
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams = pkMlDsakey->pParams;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+
+    const UINT32 beta = (UINT32) pParams->nChallengeNonZeroCoeffs * pParams->privateKeyRange;
+
+    BOOL bExternalMu = (flags & SYMCRYPT_FLAG_MLDSA_EXTERNALMU) != 0;
+    UINT8 modeId = (pbHashOid == NULL) ? 0 : 1; // 0 for ML-DSA, 1 for HashML-DSA
+    UINT8 cbContextByte = (UINT8) cbContext;
+    BYTE messageRepresentative[SYMCRYPT_SHAKE256_RESULT_SIZE];
+    BYTE privateRandom[SYMCRYPT_SHAKE256_RESULT_SIZE];
+    BYTE commitmentHash[64]; // Largest possible size for commitment hash
+
+    const UINT32 cbw1Encoded = pParams->nRows * pParams->w1EncodeCoefficientBitLength *
+        ( SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8 );
+
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize(
+        pParams,
+        2,              // row vectors - W, W1, cs2, ct0, r0, hint (not all needed simultaneously)
+        3,              // column vectors - mask, cs1, response
+        1,              // poly element - challenge
+        cbw1Encoded );  // scratch space - w1 encoded
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    PSYMCRYPT_SHAKE256_STATE pShakeState = &(pTemps->shake256State);
+    SymCryptShake256Init( pShakeState );
+
+    if ( bExternalMu )
+    {
+        // Caller passes the externally-computed message representative mu
+        SYMCRYPT_ASSERT( cbInput == SYMCRYPT_SHAKE256_RESULT_SIZE );
+        memcpy( messageRepresentative, pbInput, SYMCRYPT_SHAKE256_RESULT_SIZE );
+    }
+    else
+    {
+        // Line 6: calculate message representative mu
+        // = SHAKE256( public key hash || modeId || cbContextByte || context || OID? || message/hash, 64 )
+        // The OID is only included in the HashML-DSA mode
+        SymCryptShake256Append( pShakeState, pkMlDsakey->publicKeyHash, sizeof(pkMlDsakey->publicKeyHash) );
+        SymCryptShake256Append( pShakeState, &modeId, sizeof( modeId ) );
+        SymCryptShake256Append( pShakeState, &cbContextByte, sizeof( cbContextByte ) );
+
+        // These appends are no-ops if the length is zero
+        SymCryptShake256Append( pShakeState, pbContext, cbContext );
+        SymCryptShake256Append( pShakeState, pbHashOid, cbHashOid );
+
+        SymCryptShake256Append( pShakeState, pbInput, cbInput );
+        SymCryptShake256Result( pShakeState, messageRepresentative );
+    }
+
+    // Line 7: Calculate private random seed rho prime prime
+    // = SHAKE256( private signing seed K || pbRandom || message representative mu, 64 )
+    SymCryptShake256Append( pShakeState, pkMlDsakey->privateSigningSeed, sizeof(pkMlDsakey->privateSigningSeed) );
+    SymCryptShake256Append( pShakeState, pbRandom, cbRandom );
+    SymCryptShake256Append( pShakeState, messageRepresentative, sizeof(messageRepresentative) );
+    SymCryptShake256Result( pShakeState, privateRandom );
+
+    PSYMCRYPT_MLDSA_VECTOR pvW = pTemps->pvRowVectors[0];
+    PSYMCRYPT_MLDSA_VECTOR pvHint = NULL;
+
+    PSYMCRYPT_MLDSA_VECTOR pvMask = pTemps->pvColVectors[0];
+    PSYMCRYPT_MLDSA_VECTOR pvResponse = pTemps->pvColVectors[1];
+    PSYMCRYPT_MLDSA_VECTOR pvcs1 = pTemps->pvColVectors[2];
+
+    PBYTE pbW1Encoded = pTemps->pbScratch;
+
+    UINT16 k = 0;
+    while( TRUE )
+    {
+        SymCryptMlDsaExpandMask(
+            pParams,
+            pShakeState,
+            privateRandom,
+            sizeof(privateRandom),
+            k,
+            pvMask );
+
+        // Increment k early so we can continue to the next loop iteration when validity checks fail
+        // It's okay to leak how many iterations this loop takes because the SHAKE inputs and
+        // outputs are still unpredictable; this does not leak information about the private key
+        k += (UINT16) pParams->nCols;
+
+        SymCryptMlDsaMatrixVectorMontMul( pkMlDsakey->pmA, pvMask, pvW, pTemps->pePolyElements[0] );
+
+        for(UINT8 i = 0; i < pvW->nElems; ++i)
+        {
+            SymCryptMlDsaPolyElementMulR( SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT(i, pvW) );
+        }
+
+        SymCryptMlDsaVectorINTT( pvW );
+
+        {
+            // Scope for pvW1
+            PSYMCRYPT_MLDSA_VECTOR pvW1 = pTemps->pvRowVectors[1];
+            SymCryptMlDsaVectorHighBits( pParams, pvW, pvW1 );
+            SymCryptMlDsaVectorEncode( pvW1, pParams->w1EncodeCoefficientBitLength, 0, pbW1Encoded );
+        }
+
+        // Calculate commitment hash
+        SymCryptShake256Append( pShakeState, messageRepresentative, sizeof(messageRepresentative) );
+        SymCryptShake256Append( pShakeState, pbW1Encoded, cbw1Encoded );
+        SymCryptShake256Extract( pShakeState, commitmentHash, pParams->cbCommitmentHash, TRUE );
+
+        // Calculate challenge
+        // Reusing poly element 0 for challenge (previously temp space for multiplication)
+        PSYMCRYPT_MLDSA_POLYELEMENT peC = pTemps->pePolyElements[0];
+        SymCryptMlDsaSampleInBall( pParams, commitmentHash, pParams->cbCommitmentHash, peC );
+
+        SymCryptMlDsaPolyElementNTT( peC );
+        SymCryptMlDsaPolyElementMulR( peC );
+
+        {
+            // Scope for cs2 - reusing row vector 1, previously W1
+            PSYMCRYPT_MLDSA_VECTOR pvcs2 = pTemps->pvRowVectors[1];
+            SymCryptMlDsaVectorPolyElementMontMul( pkMlDsakey->pvs1, peC, pvcs1 );
+            SymCryptMlDsaVectorPolyElementMontMul( pkMlDsakey->pvs2, peC, pvcs2 );
+
+            SymCryptMlDsaVectorINTT( pvcs1 );
+            SymCryptMlDsaVectorINTT( pvcs2 );
+
+            SymCryptMlDsaVectorINTT( pvMask );
+            SymCryptMlDsaVectorAdd( pvMask, pvcs1, pvResponse );
+
+            // (w - cs2) is an input to both LowBits (for r0) and MakeHint
+            SymCryptMlDsaVectorSub( pvW, pvcs2, pvW );
+        }
+
+        {
+            // Scope for r0 - reusing row vector 1, previously cs2
+            PSYMCRYPT_MLDSA_VECTOR pvr0 = pTemps->pvRowVectors[1];
+            SymCryptMlDsaVectorLowBits( pParams, pvW, pvr0 );
+
+            UINT32 zInfinityNorm = SymCryptMlDsaVectorInfinityNorm( pvResponse );
+            UINT32 r0InfinityNorm = SymCryptMlDsaVectorInfinityNorm( pvr0 );
+
+            if( (zInfinityNorm >= (1 << pParams->maskCoefficientRangeLog2) - beta) ||
+                (r0InfinityNorm >= pParams->commitmentRoundingRange - beta) )
+            {
+                continue;
+            }
+        }
+
+        {
+            // Scope for ct0 - reusing row vector 1, previously r0
+            PSYMCRYPT_MLDSA_VECTOR pvct0 = pTemps->pvRowVectors[1];
+
+            SymCryptMlDsaVectorPolyElementMontMul( pkMlDsakey->pvt0, peC, pvct0 );
+            SymCryptMlDsaVectorINTT( pvct0 );
+
+            UINT32 ct0InfinityNorm = SymCryptMlDsaVectorInfinityNorm( pvct0 );
+            if( ct0InfinityNorm >= pParams->commitmentRoundingRange )
+            {
+                continue;
+            }
+
+            // MakeHint vectors
+            // w - cs2 = pvW
+            // w - cs2 + ct0 = pvct0 + pvW
+            SymCryptMlDsaVectorAdd( pvct0, pvW, pvct0 );
+
+            // Write hint in-place over ct0
+            UINT32 nHintBitsSet = 0;
+            SymCryptMlDsaMakeHint( pParams, pvW, pvct0, pvct0, &nHintBitsSet );
+
+            if( nHintBitsSet > pParams->nHintNonZeroCoeffs )
+            {
+                continue;
+            }
+        }
+
+        // Row vector 1, previously ct0, now contains the hint
+        pvHint = pTemps->pvRowVectors[1];
+
+        break;
+    }
+
+    SYMCRYPT_ASSERT( pvHint != NULL );
+
+    SymCryptMlDsaSigEncode(
+        pParams,
+        commitmentHash,
+        pParams->cbCommitmentHash,
+        pvResponse,
+        pvHint,
+        pbSignature,
+        cbSignature );
+
+cleanup:
+    if( pTemps != NULL )
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSign(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PCBYTE              pbMessage,
+    SIZE_T              cbMessage,
+    PCBYTE              pbContext,
+    SIZE_T              cbContext,
+    UINT32              flags,
+    PBYTE               pbSignature,
+    SIZE_T              cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( (flags != 0) || // No flags currently supported
+        (cbContext > SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH) ||
+        (pkMlDsakey->hasPrivateKey == FALSE) ||
+        (cbSignature != pkMlDsakey->pParams->cbEncodedSignature) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    BYTE random[SYMCRYPT_MLDSA_SIGNING_RANDOM_SIZE];
+    scError = SymCryptCallbackRandom( random, sizeof(random) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsaSignEx(
+        pkMlDsakey,
+        pbMessage,
+        cbMessage,
+        pbContext,
+        cbContext,
+        NULL, // pbHashOid
+        0, // cbHashOid
+        random,
+        sizeof(random),
+        flags,
+        pbSignature,
+        cbSignature );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize( random, sizeof(random) );
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptExternalMuMlDsaSign(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PCBYTE              pbMu,
+    SIZE_T              cbMu,
+    UINT32              flags,
+    PBYTE               pbSignature,
+    SIZE_T              cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( (flags != 0) || // No flags currently supported
+        (pkMlDsakey->hasPrivateKey == FALSE) ||
+        (cbMu != SYMCRYPT_SHAKE256_RESULT_SIZE) ||
+        (cbSignature != pkMlDsakey->pParams->cbEncodedSignature) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    BYTE random[SYMCRYPT_MLDSA_SIGNING_RANDOM_SIZE];
+    scError = SymCryptCallbackRandom( random, sizeof(random) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsaSignEx(
+        pkMlDsakey,
+        pbMu,
+        cbMu,
+        NULL, // pbContext
+        0, // cbContext
+        NULL, // pbHashOid
+        0, // cbHashOid
+        random,
+        sizeof(random),
+        SYMCRYPT_FLAG_MLDSA_EXTERNALMU,
+        pbSignature,
+        cbSignature );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize( random, sizeof(random) );
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHashMlDsaSign(
+    PCSYMCRYPT_MLDSAKEY     pkMlDsakey,
+    SYMCRYPT_PQDSA_HASH_ID  hashAlg,
+    PCBYTE                  pbHash,
+    SIZE_T                  cbHash,
+    PCBYTE                  pbContext,
+    SIZE_T                  cbContext,
+    UINT32                  flags,
+    PBYTE                   pbSignature,
+    SIZE_T                  cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_OID pHashOid = NULL;
+
+    if( (flags != 0) || // No flags currently supported
+        (cbContext > SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH) ||
+        (pkMlDsakey->hasPrivateKey == FALSE) ||
+        (cbSignature != pkMlDsakey->pParams->cbEncodedSignature) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    BYTE random[SYMCRYPT_MLDSA_SIGNING_RANDOM_SIZE];
+    scError = SymCryptCallbackRandom( random, sizeof(random) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptHashMlDsaValidateHashAlgAndGetOid(
+        pkMlDsakey->pParams,
+        hashAlg,
+        cbHash,
+        &pHashOid );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsaSignEx(
+        pkMlDsakey,
+        pbHash,
+        cbHash,
+        pbContext,
+        cbContext,
+        pHashOid->pbOID,
+        pHashOid->cbOID,
+        random,
+        sizeof(random),
+        flags,
+        pbSignature,
+        cbSignature );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+
+cleanup:
+    SymCryptWipeKnownSize( random, sizeof(random) );
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaVerifyEx(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PCBYTE              pbInput,
+    SIZE_T              cbInput,
+    PCBYTE              pbContext,
+    SIZE_T              cbContext,
+    PCBYTE              pbHashOid,
+    SIZE_T              cbHashOid,
+    PCBYTE              pbSignature,
+    SIZE_T              cbSignature,
+    UINT32              flags )
+{
+    UNREFERENCED_PARAMETER( flags );
+
+    SYMCRYPT_ASSERT( cbContext <= SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH );
+    SYMCRYPT_ASSERT( pbHashOid != NULL || cbHashOid == 0 );
+    SYMCRYPT_ASSERT( pbContext != NULL || cbContext == 0 );
+    SYMCRYPT_ASSERT( cbSignature == pkMlDsakey->pParams->cbEncodedSignature );
+    SYMCRYPT_ASSERT( (flags & ~SYMCRYPT_FLAG_MLDSA_EXTERNALMU) == 0 );
+    SYMCRYPT_ASSERT( ((flags & SYMCRYPT_FLAG_MLDSA_EXTERNALMU) == 0) || (pbContext == NULL && pbHashOid == NULL) );
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams = pkMlDsakey->pParams;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+
+    const UINT32 beta = (UINT32) pParams->nChallengeNonZeroCoeffs * pParams->privateKeyRange;
+
+    BOOL bExternalMu = (flags & SYMCRYPT_FLAG_MLDSA_EXTERNALMU) != 0;
+    UINT8 modeId = (pbHashOid == NULL) ? 0 : 1; // 0 for ML-DSA, 1 for HashML-DSA
+    UINT8 cbContextByte = (UINT8) cbContext;
+    BYTE messageRepresentative[SYMCRYPT_SHAKE256_RESULT_SIZE];
+    BYTE commitmentHash[64]; // Largest possible size for commitment hash
+    BYTE recalculatedCommitmentHash[64];
+    UINT32 responseInfinityNorm;
+
+    const UINT32 cbw1Encoded = pParams->nRows * pParams->w1EncodeCoefficientBitLength *
+        ( SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8 );
+
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize(
+        pParams,
+        4, // row vectors - hint, A*NTT(z), T1*(2^d), commitment
+        1, // column vectors - response
+        2, // poly elements - challenge, temp space for multiplication
+        cbw1Encoded ); // scratch space - w1 encoded
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Row vectors
+    PSYMCRYPT_MLDSA_VECTOR pvHint = pTemps->pvRowVectors[0];
+    PSYMCRYPT_MLDSA_VECTOR pvATimesNTTz = pTemps->pvRowVectors[1];
+    PSYMCRYPT_MLDSA_VECTOR pvT1Times2D = pTemps->pvRowVectors[2];
+    PSYMCRYPT_MLDSA_VECTOR pvCommitment = pTemps->pvRowVectors[3];
+
+    // Column vectors
+    PSYMCRYPT_MLDSA_VECTOR pvResponse = pTemps->pvColVectors[0];
+
+    // Poly elements
+    PSYMCRYPT_MLDSA_POLYELEMENT peC = pTemps->pePolyElements[0];
+    PSYMCRYPT_MLDSA_POLYELEMENT peTmp = pTemps->pePolyElements[1];
+
+    PBYTE pbw1Encoded = pTemps->pbScratch;
+
+    scError = SymCryptMlDsaSigDecode(
+        pParams,
+        pbSignature,
+        cbSignature,
+        commitmentHash,
+        pParams->cbCommitmentHash,
+        pvResponse,
+        pvHint );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    responseInfinityNorm = SymCryptMlDsaVectorInfinityNorm( pvResponse );
+
+    // For the signature to be valid, the response infinity norm must be <= (gamma_1 - beta)
+    // gamma_1 = (1 << maskCoefficientRangeLog2)
+    if( responseInfinityNorm >= (1 << pParams->maskCoefficientRangeLog2) - beta )
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+    SymCryptMlDsaSampleInBall(
+        pParams,
+        commitmentHash,
+        pParams->cbCommitmentHash,
+        peC );
+
+    SymCryptMlDsaVectorNTT( pvResponse );
+
+    for(UINT8 i = 0; i < pvResponse->nElems; ++i)
+    {
+        SymCryptMlDsaPolyElementMulR( SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT(i, pvResponse) );
+    }
+
+    SymCryptMlDsaMatrixVectorMontMul(
+        pkMlDsakey->pmA,
+        pvResponse,
+        pvATimesNTTz,
+        peTmp );
+
+    // TODO osgvsowi/55435592 - Consider precomputing t1 * 2^d
+    const UINT32 pow2DTimesR = 4214781;
+    for(UINT8 i = 0; i < pkMlDsakey->pvt1->nElems; ++i)
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peSrc = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pkMlDsakey->pvt1 );
+        PSYMCRYPT_MLDSA_POLYELEMENT peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvT1Times2D );
+        for(UINT32 j = 0; j < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; ++j)
+        {
+            peDst->coeffs[j] = SymCryptMlDsaMontMul( peSrc->coeffs[j], pow2DTimesR );
+        }
+    }
+
+    SymCryptMlDsaPolyElementNTT( peC );
+    SymCryptMlDsaPolyElementMulR( peC );
+
+    SymCryptMlDsaVectorPolyElementMontMul(
+        pvT1Times2D,
+        peC,
+        pvT1Times2D );
+
+    SymCryptMlDsaVectorSub(
+        pvATimesNTTz,
+        pvT1Times2D,
+        pvCommitment );
+
+    SymCryptMlDsaVectorINTT( pvCommitment );
+
+    SymCryptMlDsaUseHint(
+        pParams,
+        pvHint,
+        pvCommitment );
+
+    SymCryptMlDsaVectorEncode(
+        pvCommitment,
+        pParams->w1EncodeCoefficientBitLength,
+        0,
+        pbw1Encoded );
+
+    PSYMCRYPT_SHAKE256_STATE pShakeState = &(pTemps->shake256State);
+    SymCryptShake256Init( pShakeState );
+
+    if ( bExternalMu )
+    {
+        // Caller passes the externally-computed message representative mu
+        SYMCRYPT_ASSERT( cbInput == SYMCRYPT_SHAKE256_RESULT_SIZE );
+        memcpy( messageRepresentative, pbInput, SYMCRYPT_SHAKE256_RESULT_SIZE );
+    }
+    else
+    {
+        // Line 7: calculate message representative mu
+        // = SHAKE256( public key hash || modeId || cbContextByte || context || OID? || message/hash, 64 )
+        // The OID is only included in the HashML-DSA mode
+        SymCryptShake256Append( pShakeState, pkMlDsakey->publicKeyHash, sizeof(pkMlDsakey->publicKeyHash) );
+        SymCryptShake256Append( pShakeState, &modeId, sizeof( modeId ) );
+        SymCryptShake256Append( pShakeState, &cbContextByte, sizeof( cbContextByte ) );
+
+        SymCryptShake256Append( pShakeState, pbContext, cbContext );
+        SymCryptShake256Append( pShakeState, pbHashOid, cbHashOid );
+
+        SymCryptShake256Append( pShakeState, pbInput, cbInput );
+        SymCryptShake256Result( pShakeState, messageRepresentative );
+    }
+
+    SymCryptShake256Append( pShakeState, messageRepresentative, sizeof(messageRepresentative) );
+    SymCryptShake256Append( pShakeState, pbw1Encoded, cbw1Encoded );
+    SymCryptShake256Extract( pShakeState, recalculatedCommitmentHash, pParams->cbCommitmentHash, TRUE );
+
+    if( !SymCryptEqual( recalculatedCommitmentHash, commitmentHash, pParams->cbCommitmentHash ) )
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+cleanup:
+    if( pTemps != NULL )
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaVerify(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PCBYTE              pbMessage,
+    SIZE_T              cbMessage,
+    PCBYTE              pbContext,
+    SIZE_T              cbContext,
+    PCBYTE              pbSignature,
+    SIZE_T              cbSignature,
+    UINT32              flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( (flags != 0) || // No flags currently supported
+        (cbContext > SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH) ||
+        (cbSignature != pkMlDsakey->pParams->cbEncodedSignature) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsaVerifyEx(
+        pkMlDsakey,
+        pbMessage,
+        cbMessage,
+        pbContext,
+        cbContext,
+        NULL, // pbHashOid
+        0, // cbHashOid
+        pbSignature,
+        cbSignature,
+        flags );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptExternalMuMlDsaVerify(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PCBYTE              pbMu,
+    SIZE_T              cbMu,
+    PCBYTE              pbSignature,
+    SIZE_T              cbSignature,
+    UINT32              flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( (flags != 0) || // No flags currently supported
+        (cbMu != SYMCRYPT_SHAKE256_RESULT_SIZE) ||
+        (cbSignature != pkMlDsakey->pParams->cbEncodedSignature) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsaVerifyEx(
+        pkMlDsakey,
+        pbMu,
+        cbMu,
+        NULL, // pbContext
+        0, // cbContext
+        NULL, // pbHashOid
+        0, // cbHashOid
+        pbSignature,
+        cbSignature,
+        SYMCRYPT_FLAG_MLDSA_EXTERNALMU );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHashMlDsaVerify(
+    PCSYMCRYPT_MLDSAKEY     pkMlDsakey,
+    SYMCRYPT_PQDSA_HASH_ID  hashAlg,
+    PCBYTE                  pbHash,
+    SIZE_T                  cbHash,
+    PCBYTE                  pbContext,
+    SIZE_T                  cbContext,
+    PCBYTE                  pbSignature,
+    SIZE_T                  cbSignature,
+    UINT32                  flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_OID pHashOid = NULL;
+
+    if( (flags != 0) || // No flags currently supported
+        (cbContext > SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH) ||
+        (cbSignature != pkMlDsakey->pParams->cbEncodedSignature) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptHashMlDsaValidateHashAlgAndGetOid(
+        pkMlDsakey->pParams,
+        hashAlg,
+        cbHash,
+        &pHashOid );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlDsaVerifyEx(
+        pkMlDsakey,
+        pbHash,
+        cbHash,
+        pbContext,
+        cbContext,
+        pHashOid->pbOID,
+        pHashOid->cbOID,
+        pbSignature,
+        cbSignature,
+        flags );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/mldsa_primitives.c b/libs/symcrypt/lib/mldsa_primitives.c
new file mode 100644
index 000000000000..a6fe610e1474
--- /dev/null
+++ b/libs/symcrypt/lib/mldsa_primitives.c
@@ -0,0 +1,2410 @@
+//
+// mldsa_primitives.c   ML-DSA low-level primitive implementations
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// Q^-1 mod 2^32 - used in Montgomery reduction
+//
+#define SYMCRYPT_MLDSA_Q_INV (58728449)
+
+//
+// Inverse NTT fixup times R = (256^-1 << 32) mod Q
+//
+#define SYMCRYPT_MLDSA_INTT_FIXUP_TIMES_R (16382)
+
+//
+// R^2 mod Q - used for multiplying a factor of R into a polynomial in NTT form via
+// Montgomery multiplication
+//
+#define SYMCRYPT_MLDSA_RSQR (2365951)
+
+//
+// Size of the expanded public seed used in SymCryptMlDsaRejNttPoly
+// Defined in FIPS 204 to be 272 bits (256 bit public seed rho || 8 bit index s || 8 bit index r)
+//
+#define SYMCRYPT_MLDSA_REJNTTPOLY_SEED_SIZE (34)
+
+//
+// Size of the expanded private seed used in SymCryptMlDsaRejBoundedPoly
+// Defined in FIPS 204 to be 528 bits (512 bit private vector seed rho' || 16 bit index)
+//
+#define SYMCRYPT_MLDSA_REJBOUNDEDPOLY_SEED_SIZE (66)
+
+//
+// Number of low-order bits dropped by Power2Round. Defined as d in FIPS 204
+//
+#define SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS (13)
+
+//
+// Zeta tables.
+// For ML-DSA, zeta = 1753, which is a 512th root of unity modulo Q
+//
+// In ML-DSA we use powers of zeta to convert to and from NTT form
+// and to perform multiplication between polynomials in NTT form
+//
+
+// This table is a lookup for (Zeta^(BitRev(index)) * R) mod Q
+// Used in NTT and Inverse NTT
+// i.e. element 1 is Zeta^(BitRev(1)) * (2^32) mod Q == (1753^128)*(2^32) mod 8380417 == 25847
+//
+// MLDSA_ZETA_BITREV_TIMES_R = [ (pow(1753, bitRev(i), 8380417) << 32) % 8380417 for i in range(256) ]
+//
+const UINT32 MLDSA_ZETA_BITREV_TIMES_R[SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS] = {
+    4193792, 25847, 5771523, 7861508, 237124, 7602457, 7504169, 466468,
+    1826347, 2353451, 8021166, 6288512, 3119733, 5495562, 3111497, 2680103,
+    2725464, 1024112, 7300517, 3585928, 7830929, 7260833, 2619752, 6271868,
+    6262231, 4520680, 6980856, 5102745, 1757237, 8360995, 4010497, 280005,
+    2706023, 95776, 3077325, 3530437, 6718724, 4788269, 5842901, 3915439,
+    4519302, 5336701, 3574422, 5512770, 3539968, 8079950, 2348700, 7841118,
+    6681150, 6736599, 3505694, 4558682, 3507263, 6239768, 6779997, 3699596,
+    811944, 531354, 954230, 3881043, 3900724, 5823537, 2071892, 5582638,
+    4450022, 6851714, 4702672, 5339162, 6927966, 3475950, 2176455, 6795196,
+    7122806, 1939314, 4296819, 7380215, 5190273, 5223087, 4747489, 126922,
+    3412210, 7396998, 2147896, 2715295, 5412772, 4686924, 7969390, 5903370,
+    7709315, 7151892, 8357436, 7072248, 7998430, 1349076, 1852771, 6949987,
+    5037034, 264944, 508951, 3097992, 44288, 7280319, 904516, 3958618,
+    4656075, 8371839, 1653064, 5130689, 2389356, 8169440, 759969, 7063561,
+    189548, 4827145, 3159746, 6529015, 5971092, 8202977, 1315589, 1341330,
+    1285669, 6795489, 7567685, 6940675, 5361315, 4499357, 4751448, 3839961,
+    2091667, 3407706, 2316500, 3817976, 5037939, 2244091, 5933984, 4817955,
+    266997, 2434439, 7144689, 3513181, 4860065, 4621053, 7183191, 5187039,
+    900702, 1859098, 909542, 819034, 495491, 6767243, 8337157, 7857917,
+    7725090, 5257975, 2031748, 3207046, 4823422, 7855319, 7611795, 4784579,
+    342297, 286988, 5942594, 4108315, 3437287, 5038140, 1735879, 203044,
+    2842341, 2691481, 5790267, 1265009, 4055324, 1247620, 2486353, 1595974,
+    4613401, 1250494, 2635921, 4832145, 5386378, 1869119, 1903435, 7329447,
+    7047359, 1237275, 5062207, 6950192, 7929317, 1312455, 3306115, 6417775,
+    7100756, 1917081, 5834105, 7005614, 1500165, 777191, 2235880, 3406031,
+    7838005, 5548557, 6709241, 6533464, 5796124, 4656147, 594136, 4603424,
+    6366809, 2432395, 2454455, 8215696, 1957272, 3369112, 185531, 7173032,
+    5196991, 162844, 1616392, 3014001, 810149, 1652634, 4686184, 6581310,
+    5341501, 3523897, 3866901, 269760, 2213111, 7404533, 1717735, 472078,
+    7953734, 1723600, 6577327, 1910376, 6712985, 7276084, 8119771, 4546524,
+    5441381, 6144432, 7959518, 6094090, 183443, 7403526, 1612842, 4834730,
+    7826001, 3919660, 8332111, 7018208, 3937738, 1400424, 7534263, 1976782
+};
+
+const UINT32 MLDSA_NEGATIVE_ZETA_BITREV_TIMES_R[SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS] = {
+    4186625, 8354570, 2608894, 518909, 8143293, 777960, 876248, 7913949,
+    6554070, 6026966, 359251, 2091905, 5260684, 2884855, 5268920, 5700314,
+    5654953, 7356305, 1079900, 4794489, 549488, 1119584, 5760665, 2108549,
+    2118186, 3859737, 1399561, 3277672, 6623180, 19422, 4369920, 8100412,
+    5674394, 8284641, 5303092, 4849980, 1661693, 3592148, 2537516, 4464978,
+    3861115, 3043716, 4805995, 2867647, 4840449, 300467, 6031717, 539299,
+    1699267, 1643818, 4874723, 3821735, 4873154, 2140649, 1600420, 4680821,
+    7568473, 7849063, 7426187, 4499374, 4479693, 2556880, 6308525, 2797779,
+    3930395, 1528703, 3677745, 3041255, 1452451, 4904467, 6203962, 1585221,
+    1257611, 6441103, 4083598, 1000202, 3190144, 3157330, 3632928, 8253495,
+    4968207, 983419, 6232521, 5665122, 2967645, 3693493, 411027, 2477047,
+    671102, 1228525, 22981, 1308169, 381987, 7031341, 6527646, 1430430,
+    3343383, 8115473, 7871466, 5282425, 8336129, 1100098, 7475901, 4421799,
+    3724342, 8578, 6727353, 3249728, 5991061, 210977, 7620448, 1316856,
+    8190869, 3553272, 5220671, 1851402, 2409325, 177440, 7064828, 7039087,
+    7094748, 1584928, 812732, 1439742, 3019102, 3881060, 3628969, 4540456,
+    6288750, 4972711, 6063917, 4562441, 3342478, 6136326, 2446433, 3562462,
+    8113420, 5945978, 1235728, 4867236, 3520352, 3759364, 1197226, 3193378,
+    7479715, 6521319, 7470875, 7561383, 7884926, 1613174, 43260, 522500,
+    655327, 3122442, 6348669, 5173371, 3556995, 525098, 768622, 3595838,
+    8038120, 8093429, 2437823, 4272102, 4943130, 3342277, 6644538, 8177373,
+    5538076, 5688936, 2590150, 7115408, 4325093, 7132797, 5894064, 6784443,
+    3767016, 7129923, 5744496, 3548272, 2994039, 6511298, 6476982, 1050970,
+    1333058, 7143142, 3318210, 1430225, 451100, 7067962, 5074302, 1962642,
+    1279661, 6463336, 2546312, 1374803, 6880252, 7603226, 6144537, 4974386,
+    542412, 2831860, 1671176, 1846953, 2584293, 3724270, 7786281, 3776993,
+    2013608, 5948022, 5925962, 164721, 6423145, 5011305, 8194886, 1207385,
+    3183426, 8217573, 6764025, 5366416, 7570268, 6727783, 3694233, 1799107,
+    3038916, 4856520, 4513516, 8110657, 6167306, 975884, 6662682, 7908339,
+    426683, 6656817, 1803090, 6470041, 1667432, 1104333, 260646, 3833893,
+    2939036, 2235985, 420899, 2286327, 8196974, 976891, 6767575, 3545687,
+    554416, 4460757, 48306, 1362209, 4442679, 6979993, 846154, 6403635
+};
+
+const SYMCRYPT_MLDSA_INTERNAL_PARAMS SymCryptMlDsaInternalParams44 =
+{
+    .params = SYMCRYPT_MLDSA_PARAMS_MLDSA44,
+    .cbPolyElement = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT,
+    .cbRowVector = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(4),
+    .cbColVector = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(4),
+    .cbMatrix = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX(4, 4),
+    .nRows = 4,
+    .nCols = 4,
+    .privateKeyRange = 2,
+    .encodedCoefficientBitLength = 3,
+    .nChallengeNonZeroCoeffs = 39,
+    .nHintNonZeroCoeffs = 80,
+    .maskCoefficientRangeLog2 = 17,
+    .commitmentModulus = 44,
+    .decomposeR1Factor = 11275,
+    .commitmentRoundingRange = 95232,
+    .w1EncodeCoefficientBitLength = 6, // [0, 43]
+    .cbCommitmentHash = 32,
+    .cbEncodedPrivateKey = 2560,
+    .cbEncodedPublicKey = 1312,
+    .cbEncodedSignature = SYMCRYPT_MLDSA_SIGNATURE_SIZE_MLDSA44
+};
+
+const SYMCRYPT_MLDSA_INTERNAL_PARAMS SymCryptMlDsaInternalParams65 =
+{
+    .params = SYMCRYPT_MLDSA_PARAMS_MLDSA65,
+    .cbPolyElement = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT,
+    .cbRowVector = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(6),
+    .cbColVector = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(5),
+    .cbMatrix = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX(6, 5),
+    .nRows = 6,
+    .nCols = 5,
+    .privateKeyRange = 4,
+    .encodedCoefficientBitLength = 4,
+    .nChallengeNonZeroCoeffs = 49,
+    .nHintNonZeroCoeffs = 55,
+    .maskCoefficientRangeLog2 = 19,
+    .commitmentModulus = 16,
+    .decomposeR1Factor = 4100,
+    .commitmentRoundingRange = 261888,
+    .w1EncodeCoefficientBitLength = 4, // [0, 15]
+    .cbCommitmentHash = 48,
+    .cbEncodedPrivateKey = 4032,
+    .cbEncodedPublicKey = 1952,
+    .cbEncodedSignature = SYMCRYPT_MLDSA_SIGNATURE_SIZE_MLDSA65
+};
+
+const SYMCRYPT_MLDSA_INTERNAL_PARAMS SymCryptMlDsaInternalParams87 =
+{
+    .params = SYMCRYPT_MLDSA_PARAMS_MLDSA87,
+    .cbPolyElement = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT,
+    .cbRowVector = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(8),
+    .cbColVector = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(7),
+    .cbMatrix = SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX(8, 7),
+    .nRows = 8,
+    .nCols = 7,
+    .privateKeyRange = 2,
+    .encodedCoefficientBitLength = 3,
+    .nChallengeNonZeroCoeffs = 60,
+    .nHintNonZeroCoeffs = 75,
+    .maskCoefficientRangeLog2 = 19,
+    .commitmentModulus = 16,
+    .decomposeR1Factor = 4100,
+    .commitmentRoundingRange = 261888,
+    .w1EncodeCoefficientBitLength = 4, // [0, 15]
+    .cbCommitmentHash = 64,
+    .cbEncodedPrivateKey = 4896,
+    .cbEncodedPublicKey = 2592,
+    .cbEncodedSignature = SYMCRYPT_MLDSA_SIGNATURE_SIZE_MLDSA87
+};
+
+typedef struct _SYMCRYPT_HASH_OID_MAPPING
+{
+    SYMCRYPT_PQDSA_HASH_ID  hashId;
+    const PCSYMCRYPT_HASH   pHashAlgorithm;
+    PCSYMCRYPT_OID          pOid;
+    BOOLEAN                 fIsXof;
+} SYMCRYPT_HASH_OID_MAPPING, *PSYMCRYPT_HASH_OID_MAPPING;
+
+//
+// Mapping of hash OIDs to SymCrypt hash algorithms. Currently this only contains the "short" hash
+// OIDs, and only for those algorithms that are approved for use in ML-DSA. In the future, we might
+// want to make this functionality more generic, but that requires more thought about the design.
+// Ideally, the SYMCRYPT_HASH structures could contain pointers to their corresponding OIDs, but
+// those structures are exposed externally, so extending them would be a breaking change.
+//
+const SYMCRYPT_HASH_OID_MAPPING g_hashOidMap[] =
+{
+    { SYMCRYPT_PQDSA_HASH_ID_SHA256,        &SymCryptSha256Algorithm_default,       &SymCryptSha256OidList[1],      FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHA384,        &SymCryptSha384Algorithm_default,       &SymCryptSha384OidList[1],      FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHA512,        &SymCryptSha512Algorithm_default,       &SymCryptSha512OidList[1],      FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHA512_256,    &SymCryptSha512_256Algorithm_default,   &SymCryptSha512_256OidList[1],  FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHA3_256,      &SymCryptSha3_256Algorithm_default,     &SymCryptSha3_256OidList[1],    FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHA3_384,      &SymCryptSha3_384Algorithm_default,     &SymCryptSha3_384OidList[1],    FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHA3_512,      &SymCryptSha3_512Algorithm_default,     &SymCryptSha3_512OidList[1],    FALSE },
+    { SYMCRYPT_PQDSA_HASH_ID_SHAKE128,      &SymCryptShake128HashAlgorithm_default, &SymCryptShake128OidList[1],    TRUE  },
+    { SYMCRYPT_PQDSA_HASH_ID_SHAKE256,      &SymCryptShake256HashAlgorithm_default, &SymCryptShake256OidList[1],    TRUE  }
+};
+
+//
+// The table above relies on the OID lists having (at least) two entries, where the second one
+// is the 11-byte encoding of the OID. If this ever changes, the table needs to be updated.
+//
+C_ASSERT( SYMCRYPT_SHA256_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHA384_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHA512_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHA512_256_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHA3_256_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHA3_384_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHA3_512_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHAKE128_OID_COUNT == 2 );
+C_ASSERT( SYMCRYPT_SHAKE256_OID_COUNT == 2 );
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaGetInternalParamsFromParams(
+    SYMCRYPT_MLDSA_PARAMS               params,
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS*   pInternalParams )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    switch( params )
+    {
+        case SYMCRYPT_MLDSA_PARAMS_MLDSA44:
+            *pInternalParams = &SymCryptMlDsaInternalParams44;
+            break;
+        case SYMCRYPT_MLDSA_PARAMS_MLDSA65:
+            *pInternalParams = &SymCryptMlDsaInternalParams65;
+            break;
+        case SYMCRYPT_MLDSA_PARAMS_MLDSA87:
+            *pInternalParams = &SymCryptMlDsaInternalParams87;
+            break;
+        case SYMCRYPT_MLDSA_PARAMS_NULL:
+            scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+            break;
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            break;
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSizeofKeyFormatFromParams(
+    SYMCRYPT_MLDSA_PARAMS       params,
+    SYMCRYPT_MLDSAKEY_FORMAT    mlDsakeyFormat,
+    SIZE_T*                     pcbKeyFormat )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pInternalParams = NULL;
+
+    scError = SymCryptMlDsaGetInternalParamsFromParams( params, &pInternalParams );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    switch( mlDsakeyFormat )
+    {
+        case SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED:
+            *pcbKeyFormat = SYMCRYPT_MLDSA_ROOT_SEED_SIZE;
+            break;
+        case SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_KEY:
+            *pcbKeyFormat = pInternalParams->cbEncodedPrivateKey;
+            break;
+        case SYMCRYPT_MLDSAKEY_FORMAT_PUBLIC_KEY:
+            *pcbKeyFormat = pInternalParams->cbEncodedPublicKey;
+            break;
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            break;
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSizeofSignatureFromParams(
+    SYMCRYPT_MLDSA_PARAMS       params,
+    SIZE_T*                     pcbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pInternalParams = NULL;
+
+    scError = SymCryptMlDsaGetInternalParamsFromParams( params, &pInternalParams );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    *pcbSignature = pInternalParams->cbEncodedSignature;
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+PSYMCRYPT_MLDSAKEY
+SYMCRYPT_CALL
+SymCryptMlDsakeyInitialize(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pInternalParams,
+    PBYTE                            pbKey,
+    UINT32                           cbKey )
+{
+    PSYMCRYPT_MLDSAKEY pkMlDsakey = (PSYMCRYPT_MLDSAKEY) pbKey;
+    SYMCRYPT_ASSERT( pkMlDsakey != NULL );
+
+    UINT8 nRows = pInternalParams->nRows;
+    UINT8 nCols = pInternalParams->nCols;
+
+    SYMCRYPT_ASSERT( cbKey == SYMCRYPT_INTERNAL_MLDSA_SIZEOF_KEY(nRows, nCols) );
+
+    UINT32 cbMatrix = pInternalParams->cbMatrix; // A matrix
+    UINT32 cbRowVector = pInternalParams->cbRowVector; // s2, t vectors
+    UINT32 cbColVector = pInternalParams->cbColVector; // s1 vector
+
+    SymCryptWipe( pbKey, cbKey );
+
+    pkMlDsakey->pParams = pInternalParams;
+    pkMlDsakey->cbTotalSize = cbKey;
+
+    PBYTE pbCurrent = pbKey + sizeof(SYMCRYPT_MLDSAKEY);
+
+    // Public components
+    pkMlDsakey->pmA = SymCryptMlDsaMatrixCreate( pbCurrent, cbMatrix, nRows, nCols );
+    pbCurrent += cbMatrix;
+
+    pkMlDsakey->pvt1 = SymCryptMlDsaVectorCreate( pbCurrent, cbRowVector, nRows );
+    pbCurrent += cbRowVector;
+
+    // Private components
+    pkMlDsakey->pvs1 = SymCryptMlDsaVectorCreate( pbCurrent, cbColVector, nCols );
+    pbCurrent += cbColVector;
+
+    pkMlDsakey->pvs2 = SymCryptMlDsaVectorCreate( pbCurrent, cbRowVector, nRows );
+    pbCurrent += cbRowVector;
+
+    pkMlDsakey->pvt0 = SymCryptMlDsaVectorCreate( pbCurrent, cbRowVector, nRows );
+    pbCurrent += cbRowVector;
+
+    SYMCRYPT_ASSERT( pbCurrent == pbKey + cbKey );
+
+    SYMCRYPT_SET_MAGIC( pkMlDsakey );
+
+    return pkMlDsakey;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsakeyComputeT(
+    PCSYMCRYPT_MLDSA_MATRIX     pmA,
+    PCSYMCRYPT_MLDSA_VECTOR     pvs1,
+    PCSYMCRYPT_MLDSA_VECTOR     pvs2,
+    PSYMCRYPT_MLDSA_VECTOR      pvt0,
+    PSYMCRYPT_MLDSA_VECTOR      pvt1,
+    PSYMCRYPT_MLDSA_VECTOR      pvTmp,
+    PSYMCRYPT_MLDSA_POLYELEMENT peTmp )
+{
+    // T = InvNTT(NTT(A)*NTT(s1) + NTT(s2))
+    // pvTmp := NTT(A)*NTT(s1)
+    SymCryptMlDsaMatrixVectorMontMul(
+        pmA,
+        pvs1,
+        pvTmp,
+        peTmp );
+
+    // TODO: should probably do multiplication by directly in the matrix multiplication function
+    for(UINT8 i = 0; i < pvTmp->nElems; ++i)
+    {
+        SymCryptMlDsaPolyElementMulR(SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT(i, pvTmp));
+    }
+
+    // pvTmp := pvTmp + NTT(s2)
+    SymCryptMlDsaVectorAdd( pvTmp, pvs2, pvTmp );
+
+    // T = pvTmp := InvNTT(NTT(A)*NTT(s1) + NTT(s2))
+    SymCryptMlDsaVectorINTT( pvTmp );
+
+    SymCryptMlDsaVectorPower2Round( pvTmp, pvt1, pvt0 );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaMontReduce( UINT64 a )
+{
+    UINT32 t = ((UINT32) a) * SYMCRYPT_MLDSA_Q_INV;
+    UINT32 m = (((UINT64) t) * SYMCRYPT_MLDSA_Q) >> SYMCRYPT_MLDSA_R_LOG2;
+
+    UINT64 res = (a >> SYMCRYPT_MLDSA_R_LOG2) - m;
+    UINT32 additionMask = SYMCRYPT_MASK32_LT( res, 0 );
+
+    res = res + (SYMCRYPT_MLDSA_Q & additionMask);
+    SYMCRYPT_ASSERT( res < SYMCRYPT_MLDSA_Q );
+
+    return (UINT32) res;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaMontMul( UINT32 a, UINT32 b )
+{
+    SYMCRYPT_ASSERT( a < SYMCRYPT_MLDSA_Q );
+    SYMCRYPT_ASSERT( b < SYMCRYPT_MLDSA_Q );
+
+    return SymCryptMlDsaMontReduce((UINT64) a * b);
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaModAdd( UINT32 a, UINT32 b )
+{
+    SYMCRYPT_ASSERT( a < SYMCRYPT_MLDSA_Q );
+    SYMCRYPT_ASSERT( b < SYMCRYPT_MLDSA_Q );
+
+    UINT32 res = a + b;
+    UINT32 subtractionMask = SYMCRYPT_MASK32_LT( SYMCRYPT_MLDSA_Q - 1, res );
+
+    // If res >= Q, subtract Q
+    res = res - (SYMCRYPT_MLDSA_Q & subtractionMask);
+
+    return res;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaModSub( UINT32 a, UINT32 b )
+{
+    SYMCRYPT_ASSERT( a < SYMCRYPT_MLDSA_Q );
+    SYMCRYPT_ASSERT( b < SYMCRYPT_MLDSA_Q );
+
+    UINT32 additionMask = SYMCRYPT_MASK32_LT( a, b );
+
+    // If a < b, result is negative, so we add Q
+    return (INT32) a - (INT32) b + (SYMCRYPT_MLDSA_Q & additionMask);
+}
+
+_Use_decl_annotations_
+PSYMCRYPT_MLDSA_POLYELEMENT
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementCreate(
+    PBYTE   pbBuffer,
+    SIZE_T  cbBuffer )
+{
+    UNREFERENCED_PARAMETER( cbBuffer );
+    SYMCRYPT_ASSERT( cbBuffer == SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT );
+
+    PSYMCRYPT_MLDSA_POLYELEMENT peElement = (PSYMCRYPT_MLDSA_POLYELEMENT) pbBuffer;
+    SYMCRYPT_ASSERT( peElement != NULL );
+
+    return peElement;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementSetZero(
+    PSYMCRYPT_MLDSA_POLYELEMENT peDst )
+{
+    for( UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; ++i )
+    {
+        peDst->coeffs[i] = 0;
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementNTT(
+    PSYMCRYPT_MLDSA_POLYELEMENT peSrc )
+{
+    C_ASSERT( (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS & 1) == 0);
+
+    UINT32 k = 0;
+
+    for(UINT32 len = SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 2; len >= 1; len /= 2)
+    {
+        for(UINT32 start = 0; start < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; start += 2 * len)
+        {
+            k++;
+            UINT32 twiddleFactor = MLDSA_ZETA_BITREV_TIMES_R[k];
+
+            for(UINT32 j = start; j < start + len; j++)
+            {
+                //
+                // Typically for Montgomery multiplication, both operands have a factor of R.
+                // After multiplying, the product has a factor for R^2, and a reduction is then
+                // performed which divides out a factor of R, resulting in the product again having
+                // a factor of R^1, mod Q. In this case, the twiddleFactor is pre-multiplied by R,
+                // but the coefficients are not expected to have a factor of R; thus, after
+                // reduction, the result does not have a factor of R either.
+                //
+                UINT32 t = SymCryptMlDsaMontMul(twiddleFactor, peSrc->coeffs[j + len]);
+                peSrc->coeffs[j + len] = SymCryptMlDsaModSub(peSrc->coeffs[j], t);
+                peSrc->coeffs[j] = SymCryptMlDsaModAdd(peSrc->coeffs[j], t);
+            }
+        }
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementINTT(
+    PSYMCRYPT_MLDSA_POLYELEMENT peSrc )
+{
+    C_ASSERT( (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS & 1) == 0);
+
+    UINT32 k = SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS;
+
+    for(UINT32 len = 1; len < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; len *= 2)
+    {
+        for(UINT32 start = 0; start < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; start += 2 * len)
+        {
+            k--;
+            UINT32 twiddleFactor = MLDSA_NEGATIVE_ZETA_BITREV_TIMES_R[k];
+
+            for(UINT32 j = start; j < start + len; j++)
+            {
+                //
+                // As above, our twiddleFactor is pre-multiplied by R, but the coefficients are not,
+                // so after the reduction, the result does not have a factor of R.
+                //
+                UINT32 t = peSrc->coeffs[j];
+                peSrc->coeffs[j] = SymCryptMlDsaModAdd(t, peSrc->coeffs[j + len]);
+                peSrc->coeffs[j + len] = SymCryptMlDsaModSub(t, peSrc->coeffs[j + len]);
+                peSrc->coeffs[j + len] = SymCryptMlDsaMontMul(twiddleFactor, peSrc->coeffs[j + len]);
+            }
+        }
+    }
+
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        peSrc->coeffs[i] = SymCryptMlDsaMontMul(SYMCRYPT_MLDSA_INTT_FIXUP_TIMES_R, peSrc->coeffs[i]);
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementMulR(
+    PSYMCRYPT_MLDSA_POLYELEMENT peSrc )
+{
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        peSrc->coeffs[i] = SymCryptMlDsaMontMul(SYMCRYPT_MLDSA_RSQR, peSrc->coeffs[i]);
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementMontMul(
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc1,
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    PSYMCRYPT_MLDSA_POLYELEMENT     peDst )
+{
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        peDst->coeffs[i] = SymCryptMlDsaMontMul(peSrc1->coeffs[i], peSrc2->coeffs[i]);
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementAdd(
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc1,
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    PSYMCRYPT_MLDSA_POLYELEMENT     peDst )
+{
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        peDst->coeffs[i] = SymCryptMlDsaModAdd(peSrc1->coeffs[i], peSrc2->coeffs[i]);
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementSub(
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc1,
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    PSYMCRYPT_MLDSA_POLYELEMENT     peDst )
+{
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        peDst->coeffs[i] = SymCryptMlDsaModSub(peSrc1->coeffs[i], peSrc2->coeffs[i]);
+    }
+}
+
+_Use_decl_annotations_
+PSYMCRYPT_MLDSA_VECTOR
+SYMCRYPT_CALL
+SymCryptMlDsaVectorCreate(
+    PBYTE   pbBuffer,
+    UINT32  cbBuffer,
+    UINT8   nElems )
+{
+    SYMCRYPT_ASSERT( nElems > 0);
+    SYMCRYPT_ASSERT( nElems <= SYMCRYPT_MLDSA_VECTOR_MAX_LENGTH );
+    SYMCRYPT_ASSERT( cbBuffer == SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR(nElems) );
+
+    PSYMCRYPT_MLDSA_VECTOR pvVector = (PSYMCRYPT_MLDSA_VECTOR) pbBuffer;
+    SYMCRYPT_ASSERT( pvVector != NULL );
+
+    pvVector->nElems = nElems;
+    pvVector->cbTotalSize = cbBuffer;
+
+    PBYTE pbCurrent = pbBuffer + sizeof(SYMCRYPT_MLDSA_VECTOR);
+    for( UINT32 i = 0; i < nElems; ++i )
+    {
+        SymCryptMlDsaPolyElementCreate( pbCurrent, SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT );
+        pbCurrent += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT;
+    }
+
+    return pvVector;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorCopy(
+    PCSYMCRYPT_MLDSA_VECTOR pvSrc,
+    PSYMCRYPT_MLDSA_VECTOR  pvDst )
+{
+    SYMCRYPT_ASSERT( pvSrc->nElems == pvDst->nElems );
+
+    memcpy( pvDst, pvSrc, pvSrc->cbTotalSize );
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorSetZero(
+    PSYMCRYPT_MLDSA_VECTOR pvDst )
+{
+    for( UINT32 i = 0; i < pvDst->nElems; ++i )
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+        SymCryptMlDsaPolyElementSetZero( peDst );
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorAdd(
+    PCSYMCRYPT_MLDSA_VECTOR pvSrc1,
+    PCSYMCRYPT_MLDSA_VECTOR pvSrc2,
+    PSYMCRYPT_MLDSA_VECTOR  pvDst )
+{
+    SYMCRYPT_ASSERT( pvSrc1->nElems == pvSrc2->nElems );
+    SYMCRYPT_ASSERT( pvSrc1->nElems == pvDst->nElems );
+
+    PCSYMCRYPT_MLDSA_POLYELEMENT peSrc1, peSrc2;
+    PSYMCRYPT_MLDSA_POLYELEMENT peDst;
+
+    for( UINT32 i = 0; i < pvSrc1->nElems; ++i )
+    {
+        peSrc1 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc1 );
+        peSrc2 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc2 );
+        peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+
+        SymCryptMlDsaPolyElementAdd( peSrc1, peSrc2, peDst );
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorSub(
+    PCSYMCRYPT_MLDSA_VECTOR pvSrc1,
+    PCSYMCRYPT_MLDSA_VECTOR pvSrc2,
+    PSYMCRYPT_MLDSA_VECTOR  pvDst )
+{
+    SYMCRYPT_ASSERT( pvSrc1->nElems == pvSrc2->nElems );
+    SYMCRYPT_ASSERT( pvSrc1->nElems == pvDst->nElems );
+
+    PCSYMCRYPT_MLDSA_POLYELEMENT peSrc1, peSrc2;
+    PSYMCRYPT_MLDSA_POLYELEMENT peDst;
+
+    for( UINT32 i = 0; i < pvSrc1->nElems; ++i )
+    {
+        peSrc1 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc1 );
+        peSrc2 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc2 );
+        peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+
+        SymCryptMlDsaPolyElementSub( peSrc1, peSrc2, peDst );
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorPolyElementMontMul(
+    PCSYMCRYPT_MLDSA_VECTOR         pvSrc1,
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    PSYMCRYPT_MLDSA_VECTOR          pvDst )
+{
+    SYMCRYPT_ASSERT( pvSrc1->nElems == pvDst->nElems );
+
+    for( UINT32 i = 0; i < pvSrc1->nElems; ++i )
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peSrc1 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc1 );
+        PSYMCRYPT_MLDSA_POLYELEMENT peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+
+        SymCryptMlDsaPolyElementMontMul( peSrc1, peSrc2, peDst );
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorNTT(
+    PSYMCRYPT_MLDSA_VECTOR pvSrc )
+{
+    for( UINT32 i = 0; i < pvSrc->nElems; ++i )
+    {
+        SymCryptMlDsaPolyElementNTT( SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc ) );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorINTT(
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR pvSrc )
+{
+    for( UINT32 i = 0; i < pvSrc->nElems; ++i )
+    {
+        SymCryptMlDsaPolyElementINTT( SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc ) );
+    }
+}
+
+_Use_decl_annotations_
+PSYMCRYPT_MLDSA_MATRIX
+SYMCRYPT_CALL
+SymCryptMlDsaMatrixCreate(
+    PBYTE   pbBuffer,
+    UINT32  cbBuffer,
+    UINT8   nRows,
+    UINT8   nCols )
+{
+    SYMCRYPT_ASSERT( nRows > 0);
+    SYMCRYPT_ASSERT( nCols > 0);
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLDSA_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( nCols <= SYMCRYPT_MLDSA_MATRIX_MAX_NCOLS );
+    SYMCRYPT_ASSERT( cbBuffer == SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX(nRows, nCols) );
+
+    PSYMCRYPT_MLDSA_MATRIX pMatrix = (PSYMCRYPT_MLDSA_MATRIX) pbBuffer;
+    SYMCRYPT_ASSERT( pMatrix != NULL );
+
+    pMatrix->nRows = nRows;
+    pMatrix->nCols = nCols;
+    pMatrix->cbTotalSize = cbBuffer;
+
+    PBYTE pbCurrent = pbBuffer + sizeof(SYMCRYPT_MLDSA_MATRIX);
+    for(UINT32 i = 0; i < (UINT32) nRows * nCols; ++i)
+    {
+        SymCryptMlDsaPolyElementCreate( pbCurrent, SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT );
+        pbCurrent += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT;
+    }
+
+    return pMatrix;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaMatrixVectorMontMul(
+    PCSYMCRYPT_MLDSA_MATRIX     pmSrc1,
+    PCSYMCRYPT_MLDSA_VECTOR     pvSrc2,
+    PSYMCRYPT_MLDSA_VECTOR      pvDst,
+    PSYMCRYPT_MLDSA_POLYELEMENT peTmp)
+{
+    SYMCRYPT_ASSERT( pmSrc1->nCols == pvSrc2->nElems );
+    SYMCRYPT_ASSERT( pmSrc1->nRows == pvDst->nElems );
+
+    PCSYMCRYPT_MLDSA_POLYELEMENT peSrc1, peSrc2;
+    PSYMCRYPT_MLDSA_POLYELEMENT peDst;
+
+    SymCryptMlDsaVectorSetZero( pvDst );
+
+    _Analysis_assume_( pmSrc1->nRows > 0 );
+    _Analysis_assume_( pmSrc1->nCols > 0 );
+
+    for( UINT32 i = 0; i < pmSrc1->nRows; ++i )
+    {
+        // peDst = pvDst[i]
+        peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+
+        for( UINT32 j = 0; j < pmSrc1->nCols; ++j )
+        {
+            peSrc1 = SYMCRYPT_INTERNAL_MLDSA_MATRIX_ELEMENT( i, j, pmSrc1 );
+            peSrc2 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( j, pvSrc2 );
+
+            SymCryptMlDsaPolyElementMontMul( peSrc1, peSrc2, peTmp );
+            SymCryptMlDsaPolyElementAdd( peDst, peTmp, peDst );
+        }
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaRejNttPoly(
+    PCBYTE                      pbRejNttPolySeed,
+    SIZE_T                      cbRejNttPolySeed,
+    PSYMCRYPT_MLDSA_POLYELEMENT peDst )
+{
+    SYMCRYPT_ASSERT( cbRejNttPolySeed == SYMCRYPT_MLDSA_REJNTTPOLY_SEED_SIZE );
+
+    SYMCRYPT_SHAKE128_STATE shakeState;
+    SymCryptShake128Init( &shakeState );
+    SymCryptShake128Append( &shakeState, pbRejNttPolySeed, cbRejNttPolySeed );
+
+    UINT32 coeff = 0;
+    BYTE shakeBytes[4]; // We only use 3 bytes, but using 4 allows converting to UINT32 more easily
+
+    SymCryptWipeKnownSize( shakeBytes, sizeof(shakeBytes) );
+
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        // CoeffFromThreeBytes from FIPS 204
+        do
+        {
+            SymCryptShake128Extract( &shakeState, shakeBytes, 3, FALSE );
+            shakeBytes[2] &= 0x7F; // if b2 > 127, b2 -= 128
+            coeff = SYMCRYPT_LOAD_LSBFIRST32( shakeBytes );
+        } while (coeff >= SYMCRYPT_MLDSA_Q);
+
+        peDst->coeffs[i] = coeff;
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaExpandA(
+    PCBYTE                  pbPublicSeed,
+    SIZE_T                  cbPublicSeed,
+    PSYMCRYPT_MLDSA_MATRIX  pmA )
+{
+    SYMCRYPT_ASSERT( cbPublicSeed == SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE );
+    C_ASSERT( SYMCRYPT_MLDSA_REJNTTPOLY_SEED_SIZE == SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE + 2 );
+
+    // The expanded seed is the public seed concatenated with one byte each for the column and row
+    // indices of the matrix element being expanded.
+    BYTE rejNttSeed[SYMCRYPT_MLDSA_REJNTTPOLY_SEED_SIZE];
+    memcpy( rejNttSeed, pbPublicSeed, cbPublicSeed );
+
+    for( UINT8 i = 0; i < pmA->nRows; ++i )
+    {
+        for( UINT8 j = 0; j < pmA->nCols; ++j )
+        {
+            rejNttSeed[SYMCRYPT_MLDSA_REJNTTPOLY_SEED_SIZE - 2] = j;
+            rejNttSeed[SYMCRYPT_MLDSA_REJNTTPOLY_SEED_SIZE - 1] = i;
+
+            #pragma prefast( suppress: 6385, "False warning - reading invalid data from rejNttSeed" );
+            SymCryptMlDsaRejNttPoly( rejNttSeed, sizeof(rejNttSeed), SYMCRYPT_INTERNAL_MLDSA_MATRIX_ELEMENT( i, j, pmA ) );
+
+        }
+    }
+}
+
+_Use_decl_annotations_
+FORCEINLINE
+INT8
+SYMCRYPT_CALL
+SymCryptMlDsaCoeffFromHalfByte(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    UINT8                               halfByte )
+{
+    SYMCRYPT_ASSERT( halfByte <= 15 );
+    SYMCRYPT_ASSERT( pParams->privateKeyRange == 2 || pParams->privateKeyRange == 4 );
+
+    if( pParams->privateKeyRange == 2 && halfByte < 15)
+    {
+        UINT8 halfByteDiv5 = (UINT8) ( ( halfByte * 13 ) >> 6 );
+        UINT8 halfByteMod5 = halfByte - (5 * halfByteDiv5);
+        return 2 - halfByteMod5;
+    }
+    else if( pParams->privateKeyRange == 4 && halfByte < 9 )
+    {
+        return 4 - halfByte;
+    }
+
+    return INT8_MIN;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaRejBoundedPoly(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCBYTE                              pbRejBoundedPolySeed,
+    SIZE_T                              cbRejBoundedPolySeed,
+    PSYMCRYPT_MLDSA_POLYELEMENT         peDst )
+{
+    SYMCRYPT_ASSERT( cbRejBoundedPolySeed == SYMCRYPT_MLDSA_REJBOUNDEDPOLY_SEED_SIZE );
+
+    SYMCRYPT_SHAKE256_STATE shakeState;
+    SymCryptShake256Init( &shakeState );
+    SymCryptShake256Append( &shakeState, pbRejBoundedPolySeed, cbRejBoundedPolySeed );
+
+    BYTE shakeByte;
+    UINT32 i = 0;
+    INT8 z0, z1;
+
+    do
+    {
+        // Note on sidechannel safety: the rejection sampling here can leak which bytes of the SHAKE
+        // output are used and which are rejected. However, bytes themselves are not leaked. This
+        // may allow the attacker to more quickly eliminate incorrect seed values when doing an
+        // exhaustive search, but given the size of the seed this should still not make the
+        // exhaustive search computationally feasible.
+        SymCryptShake256Extract( &shakeState, &shakeByte, sizeof(shakeByte), FALSE );
+        z0 = SymCryptMlDsaCoeffFromHalfByte( pParams, shakeByte & 0x0F );
+        z1 = SymCryptMlDsaCoeffFromHalfByte( pParams, shakeByte >> 4 );
+
+        SYMCRYPT_ASSERT( z0 == INT8_MIN || (( z0 + pParams->privateKeyRange >= 0 ) && ( z0 + pParams->privateKeyRange <= 2 * pParams->privateKeyRange )) );
+        SYMCRYPT_ASSERT( z1 == INT8_MIN || (( z1 + pParams->privateKeyRange >= 0 ) && ( z1 + pParams->privateKeyRange <= 2 * pParams->privateKeyRange )) );
+
+        if(z0 != INT8_MIN)
+        {
+            peDst->coeffs[i] = SymCryptMlDsaSignedCoefficientModQ( z0 );
+            i++;
+        }
+
+        if(z1 != INT8_MIN && i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS)
+        {
+            peDst->coeffs[i] = SymCryptMlDsaSignedCoefficientModQ( z1 );
+            i++;
+        }
+
+    } while( i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS );
+
+    SymCryptWipeKnownSize( &shakeState, sizeof(shakeState) );
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaExpandS(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCBYTE                              pbPrivateVectorSeed,
+    SIZE_T                              cbPrivateVectorSeed,
+    PSYMCRYPT_MLDSA_VECTOR              pvs1,
+    PSYMCRYPT_MLDSA_VECTOR              pvs2 )
+{
+    SYMCRYPT_ASSERT( cbPrivateVectorSeed == SYMCRYPT_MLDSA_PRIVATE_VECTOR_SEED_SIZE );
+    C_ASSERT( SYMCRYPT_MLDSA_REJBOUNDEDPOLY_SEED_SIZE == SYMCRYPT_MLDSA_PRIVATE_VECTOR_SEED_SIZE + 2 );
+
+    UINT32 nRows = pParams->nRows;
+    UINT32 nCols = pParams->nCols;
+
+    // The expanded seed is the private vector seed concatenated with the (two-byte) row/column
+    // index of the vector element being expanded.
+    BYTE rejBoundedPolySeed[SYMCRYPT_MLDSA_REJBOUNDEDPOLY_SEED_SIZE];
+    memcpy( rejBoundedPolySeed, pbPrivateVectorSeed, cbPrivateVectorSeed );
+
+    for(UINT16 i = 0; i < nCols; ++i)
+    {
+        SYMCRYPT_STORE_LSBFIRST16( rejBoundedPolySeed + SYMCRYPT_MLDSA_REJBOUNDEDPOLY_SEED_SIZE - sizeof(UINT16), i );
+        SymCryptMlDsaRejBoundedPoly( pParams, rejBoundedPolySeed, sizeof(rejBoundedPolySeed),
+            SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvs1 ) );
+    }
+
+    for(UINT16 i = 0; i < nRows; ++i)
+    {
+        SYMCRYPT_STORE_LSBFIRST16( rejBoundedPolySeed + SYMCRYPT_MLDSA_REJBOUNDEDPOLY_SEED_SIZE - sizeof(UINT16), (UINT16) nCols + i );
+        #pragma prefast( suppress: 6385, "False warning - reading invalid data from rejBoundedPolySeed" ); // Doesn't trigger in previous loop for some reason
+        SymCryptMlDsaRejBoundedPoly( pParams, rejBoundedPolySeed, sizeof(rejBoundedPolySeed),
+            SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvs2 ) );
+    }
+
+    SymCryptWipeKnownSize( rejBoundedPolySeed, sizeof(rejBoundedPolySeed) );
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaSampleInBall(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCBYTE                              pbCommitmentHash,
+    SIZE_T                              cbCommitmentHash,
+    PSYMCRYPT_MLDSA_POLYELEMENT         peChallenge )
+{
+    SymCryptMlDsaPolyElementSetZero( peChallenge );
+
+    SYMCRYPT_SHAKE256_STATE shakeState;
+    SymCryptShake256Init( &shakeState );
+    SymCryptShake256Append( &shakeState, pbCommitmentHash, cbCommitmentHash );
+
+    // The first 8 bytes are used as as powers of negative one when sampling the challenge
+    // polynomial: c[j] = -1^(H(rho)[i + tau - 256])
+    BYTE temp[8];
+    SYMCRYPT_ASSERT( pParams->nChallengeNonZeroCoeffs <= 8 * sizeof(temp) );
+    SymCryptShake256Extract( &shakeState, temp, sizeof(temp), FALSE );
+
+    UINT64 powersOfNegativeOne = SYMCRYPT_LOAD_LSBFIRST64( temp );
+
+    for(UINT32 i = SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS - pParams->nChallengeNonZeroCoeffs;
+        i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS;
+        ++i)
+    {
+        BYTE j = 0;
+        do
+        {
+            SymCryptShake256Extract( &shakeState, &j, sizeof(j), FALSE );
+        } while( j > i );
+
+        peChallenge->coeffs[i] = peChallenge->coeffs[j];
+
+        UINT32 negativeMask = SYMCRYPT_MASK32_NONZERO( powersOfNegativeOne & 1 );
+        powersOfNegativeOne >>= 1;
+
+        // Set the coefficient modulo Q
+        peChallenge->coeffs[j] = ((SYMCRYPT_MLDSA_Q - 1) & negativeMask) | (1 & ~negativeMask);
+    }
+
+    SymCryptWipeKnownSize( &shakeState, sizeof(shakeState) );
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaExpandMask(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PSYMCRYPT_SHAKE256_STATE            pShakeState,
+    PCBYTE                              pbPrivateRandom,
+    SIZE_T                              cbPrivateRandom,
+    UINT16                              counter,
+    PSYMCRYPT_MLDSA_VECTOR              pvMask )
+{
+    SYMCRYPT_ASSERT( pParams->nCols == pvMask->nElems );
+    SYMCRYPT_ASSERT( cbPrivateRandom == SYMCRYPT_SHAKE256_RESULT_SIZE );
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BYTE seedSuffix[2];
+
+    UINT32 cbShakeOutput = (pParams->maskCoefficientRangeLog2 + 1) * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    BYTE shakeOutput[20 * 32]; // Maximum size of the SHAKE output
+    SYMCRYPT_ASSERT(  cbShakeOutput <= sizeof(shakeOutput) );
+
+    for(UINT16 i = 0; i < pvMask->nElems; ++i)
+    {
+        SYMCRYPT_STORE_LSBFIRST16( seedSuffix, counter + i );
+        SymCryptShake256Append( pShakeState, pbPrivateRandom, cbPrivateRandom );
+        SymCryptShake256Append( pShakeState, (PBYTE) &seedSuffix, sizeof(seedSuffix) );
+        SymCryptShake256Extract( pShakeState, shakeOutput, cbShakeOutput, TRUE );
+
+        scError = SymCryptMlDsaPolyElementDecode(
+            shakeOutput,
+            pParams->maskCoefficientRangeLog2 + 1,
+            1 << pParams->maskCoefficientRangeLog2,
+            SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvMask ) );
+        SYMCRYPT_ASSERT( scError == SYMCRYPT_NO_ERROR );
+    }
+
+    SymCryptMlDsaVectorNTT( pvMask );
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaMakeHint(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PSYMCRYPT_MLDSA_VECTOR              pvWMinusCs2,
+    PSYMCRYPT_MLDSA_VECTOR              pvWMinusCs2PlusCt0,
+    PSYMCRYPT_MLDSA_VECTOR              pvDst,
+    UINT32*                             nBitsSet )
+{
+    SYMCRYPT_ASSERT( pvWMinusCs2->nElems == pvWMinusCs2PlusCt0->nElems );
+    SYMCRYPT_ASSERT( pvWMinusCs2->nElems == pvDst->nElems );
+
+    *nBitsSet = 0;
+
+    SymCryptMlDsaVectorHighBits( pParams, pvWMinusCs2, pvWMinusCs2 );
+    SymCryptMlDsaVectorHighBits( pParams, pvWMinusCs2PlusCt0, pvWMinusCs2PlusCt0 );
+
+    for( UINT32 i = 0; i < pvDst->nElems; ++i )
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peDst = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+        PSYMCRYPT_MLDSA_POLYELEMENT peVec0 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvWMinusCs2 );
+        PSYMCRYPT_MLDSA_POLYELEMENT peVec1 = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvWMinusCs2PlusCt0 );
+
+        for( UINT32 j = 0; j < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; ++j )
+        {
+            peDst->coeffs[j] = 1 & ~SYMCRYPT_MASK32_EQ(peVec0->coeffs[j], peVec1->coeffs[j]);
+            *nBitsSet += peDst->coeffs[j];
+        }
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaUseHint(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCSYMCRYPT_MLDSA_VECTOR             pvHint,
+    PSYMCRYPT_MLDSA_VECTOR              pvCommitment )
+{
+    SYMCRYPT_ASSERT( pvHint->nElems == pvCommitment->nElems );
+
+    UINT32 r1, r0;
+    UINT32 hintIsZeroMask;
+    UINT32 tmpMask;
+    UINT32 r0PrimeGtZeroMask;
+    UINT32 positiveOffsetCoeff;
+    UINT32 negativeOffsetCoeff;
+
+    for(UINT32 i = 0; i < pvHint->nElems; ++i)
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peHint = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvHint );
+        PSYMCRYPT_MLDSA_POLYELEMENT peCommitment = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvCommitment );
+
+        for(UINT32 j = 0; j < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; ++j)
+        {
+            SYMCRYPT_ASSERT( peHint->coeffs[j] == 0 || peHint->coeffs[j] == 1 );
+
+            SymCryptMlDsaDecompose( pParams, peCommitment->coeffs[j], &r1, &r0 );
+
+            //
+            // FIPS-204 UseHint lines 3-5
+            // r1 in range [0, commitmentModulus)
+            // r0 in range [-commitmentRoundingRange, commitmentRoundingRange], encoded
+            // as an unsigned integer modulo Q
+            //
+            // Let r0' := r0 mod+- 2*gamma_2
+            // (This is just r0 in FIPS 204, which uses signed integers.)
+            //
+            // r0 - 1 < commitmentRoundingRange => r0' > 0
+            // r0 - 1 >= commitmentRoundingRange => r0' <= 0
+            //
+            // There are three cases to consider:
+            // 1. If the hint is zero, the coefficient is set to r1.
+            // 2. Else if r0' > 0, the coefficient is (r1 + 1) mod commitmentModulus.
+            // 3. Else (r0' <= 0), the coefficient is (r1 - 1) mod commitmentModulus.
+            //
+            // The hint is public so we don't have to implement this in a sidechannel-safe manner,
+            // but avoiding branches will improve performance.
+            //
+
+            // Set up masks to determine which case we fall into
+            hintIsZeroMask = peHint->coeffs[j] - 1; // 0 if hint is 1, 0xFFFFFFFF if hint is 0
+            r0PrimeGtZeroMask = SYMCRYPT_MASK32_LT( r0 - 1, pParams->commitmentRoundingRange );
+
+            // Case 2: r0' > 0, so the coefficient is (r1 + 1) mod commitmentModulus,
+            // i.e. (r1 + 1) if r1 != commitmentModulus - 1, else 0
+            tmpMask = ~SYMCRYPT_MASK32_EQ( r1, (UINT32) (pParams->commitmentModulus - 1) );
+            positiveOffsetCoeff = tmpMask & (r1 + 1);
+
+            // Case 3: r0' <= 0, so the coefficient is (r1 - 1) mod commitmentModulus,
+            // i.e. (r1 - 1) if r1 != 0,  else commitmentModulus - 1
+            tmpMask = SYMCRYPT_MASK32_EQ( r1, 0 );
+            negativeOffsetCoeff = ( tmpMask & (pParams->commitmentModulus - 1) ) | ( ~tmpMask & ( r1 - 1 ) );
+
+            // Mask out each possible coefficient based on which case we fall into
+            r1 &= hintIsZeroMask;
+            positiveOffsetCoeff &= ~hintIsZeroMask & r0PrimeGtZeroMask;
+            negativeOffsetCoeff &= ~hintIsZeroMask & ~r0PrimeGtZeroMask;
+
+            // Sanity check: no combination of masked values should be set simultaneously
+            SYMCRYPT_ASSERT( (r1 & positiveOffsetCoeff) == 0 );
+            SYMCRYPT_ASSERT( (r1 & negativeOffsetCoeff) == 0 );
+            SYMCRYPT_ASSERT( (positiveOffsetCoeff & negativeOffsetCoeff) == 0 );
+
+            // Finally, we can pick the correct coefficient using our masks
+            peCommitment->coeffs[j] = r1 | positiveOffsetCoeff | negativeOffsetCoeff;
+        }
+    }
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaPkEncode(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PBYTE               pbDst,
+    SIZE_T              cbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams = pkMlDsakey->pParams;
+    UINT32 cbEncodedKey = pParams->cbEncodedPublicKey;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+
+    if( cbDst != cbEncodedKey )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize( pParams, 1, 0, 0, 0 );
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    {
+        PSYMCRYPT_MLDSA_VECTOR pvT1InvNTT = pTemps->pvRowVectors[0];
+
+        PBYTE pbCurr = pbDst;
+        memcpy( pbCurr, pkMlDsakey->publicSeed, SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE );
+
+        pbCurr += SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE;
+
+        SymCryptMlDsaVectorCopy( pkMlDsakey->pvt1, pvT1InvNTT );
+        SymCryptMlDsaVectorINTT( pvT1InvNTT );
+
+        // Pack each coefficient of T1 into 10 bits. Coefficients are rounded by Power2Round so they're
+        // guaranteed to be at most 10 bits long.
+        SYMCRYPT_ASSERT( cbDst - SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE == SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pvT1InvNTT, 10u ) );
+        SymCryptMlDsaVectorEncode( pvT1InvNTT, 10, 0, pbCurr );
+    }
+
+cleanup:
+    if( pTemps != NULL)
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaPkDecode(
+    PCBYTE              pbSrc,
+    SIZE_T              cbSrc,
+    UINT32              flags,
+    PSYMCRYPT_MLDSAKEY  pkMlDsakey )
+{
+    UNREFERENCED_PARAMETER( flags );
+
+    // Size of one encoded polynomial from t1: 256 coefficients * 10 bits per coefficient / 8 bits per byte
+    const UINT32 cbEncodedPoly = SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS * 10 / 8;
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCBYTE pbCurr = pbSrc;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+    PSYMCRYPT_SHAKE256_STATE pShakeState = NULL;
+
+    if( cbSrc != pkMlDsakey->pParams->cbEncodedPublicKey )
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    // Allocate space for an encoded polynomial so we can copy the input, decode it, and append it
+    // to our SHAKE state. We copy it to a local buffer so we don't violate the read-once rule when
+    // appending to the SHAKE state.
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize( pkMlDsakey->pParams, 0, 0, 0, cbEncodedPoly );
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Reset the private key state in case this key object is being reused
+    if( pkMlDsakey->hasRootSeed )
+    {
+        SymCryptWipeKnownSize( pkMlDsakey->rootSeed, SYMCRYPT_MLDSA_ROOT_SEED_SIZE );
+        pkMlDsakey->hasRootSeed = FALSE;
+    }
+
+    if( pkMlDsakey->hasPrivateKey )
+    {
+        SymCryptWipeKnownSize( pkMlDsakey->privateSigningSeed, SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE );
+        SymCryptMlDsaVectorSetZero( pkMlDsakey->pvs1 );
+        SymCryptMlDsaVectorSetZero( pkMlDsakey->pvs2 );
+        SymCryptMlDsaVectorSetZero( pkMlDsakey->pvt0 );
+        pkMlDsakey->hasPrivateKey = FALSE;
+    }
+
+    memcpy( pkMlDsakey->publicSeed, pbCurr, SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE;
+
+    pShakeState = &(pTemps->shake256State);
+
+    SymCryptShake256Init( pShakeState );
+    SymCryptShake256Append( pShakeState, pkMlDsakey->publicSeed, SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE );
+
+    for( UINT32 i = 0; i < pkMlDsakey->pvt1->nElems; ++i )
+    {
+        memcpy( pTemps->pbScratch, pbCurr, cbEncodedPoly );
+
+        PSYMCRYPT_MLDSA_POLYELEMENT peElement = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pkMlDsakey->pvt1 );
+
+        scError = SymCryptMlDsaPolyElementDecode(
+            pTemps->pbScratch,
+            10,
+            0,
+            peElement );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        SymCryptShake256Append( pShakeState, pTemps->pbScratch, cbEncodedPoly );
+
+        pbCurr += cbEncodedPoly;
+    }
+
+    SYMCRYPT_ASSERT( pbCurr == pbSrc + cbSrc );
+
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvt1 );
+
+    SymCryptMlDsaExpandA(
+        pkMlDsakey->publicSeed,
+        SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE,
+        pkMlDsakey->pmA );
+
+    SymCryptShake256Result( pShakeState, pkMlDsakey->publicKeyHash );
+
+cleanup:
+    if( pTemps != NULL )
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSkEncode(
+    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    PBYTE               pbDst,
+    SIZE_T              cbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams = pkMlDsakey->pParams;
+    UINT32 cbEncodedKey = pParams->cbEncodedPrivateKey;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+    PBYTE pbCurr = pbDst;
+
+    if( !pkMlDsakey->hasPrivateKey )
+    {
+        scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+        goto cleanup;
+    }
+
+    if( cbDst != cbEncodedKey )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize( pParams, 1, 1, 0, 0 );
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    memcpy( pbCurr, pkMlDsakey->publicSeed, SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE;
+
+    memcpy( pbCurr, pkMlDsakey->privateSigningSeed, SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE;
+
+    memcpy( pbCurr, pkMlDsakey->publicKeyHash, SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE;
+
+    {
+        // Inverse NTT and encode s1
+        PSYMCRYPT_MLDSA_VECTOR pvs1InvNTT = pTemps->pvColVectors[0];
+        SymCryptMlDsaVectorCopy( pkMlDsakey->pvs1, pvs1InvNTT );
+        SymCryptMlDsaVectorINTT( pvs1InvNTT );
+
+        SymCryptMlDsaVectorEncode(
+            pvs1InvNTT,
+            pParams->encodedCoefficientBitLength,
+            pParams->privateKeyRange,
+            pbCurr );
+        pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pvs1InvNTT, pParams->encodedCoefficientBitLength );
+    }
+
+    {
+        // Inverse NTT and encode s2
+        PSYMCRYPT_MLDSA_VECTOR pvs2InvNTT = pTemps->pvRowVectors[0];
+        SymCryptMlDsaVectorCopy( pkMlDsakey->pvs2, pvs2InvNTT );
+        SymCryptMlDsaVectorINTT( pvs2InvNTT );
+
+        SymCryptMlDsaVectorEncode(
+            pvs2InvNTT,
+            pParams->encodedCoefficientBitLength,
+            pParams->privateKeyRange,
+            pbCurr );
+        pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pvs2InvNTT, pParams->encodedCoefficientBitLength );
+    }
+
+    {
+        // Inverse NTT and encode t0
+        // Can re-use the previous temporary row vector as it's no longer needed
+        PSYMCRYPT_MLDSA_VECTOR pvt0InvNTT = pTemps->pvRowVectors[0];
+        SymCryptMlDsaVectorCopy( pkMlDsakey->pvt0, pvt0InvNTT );
+        SymCryptMlDsaVectorINTT( pvt0InvNTT );
+
+        SymCryptMlDsaVectorEncode(
+            pvt0InvNTT,
+            SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS,
+            1 << (SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS - 1),
+            pbCurr );
+        pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pvt0InvNTT, SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS );
+    }
+
+    SYMCRYPT_ASSERT( pbCurr == pbDst + cbDst );
+
+cleanup:
+    if( pTemps != NULL)
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSkDecode(
+    PCBYTE              pbSrc,
+    SIZE_T              cbSrc,
+    UINT32              flags,
+    PSYMCRYPT_MLDSAKEY  pkMlDsakey )
+{
+    UNREFERENCED_PARAMETER( flags );
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams = pkMlDsakey->pParams;
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemps = NULL;
+    PCBYTE pbCurr = pbSrc;
+    BYTE pubKeyHashTmp[SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE];
+
+    if( cbSrc != pkMlDsakey->pParams->cbEncodedPrivateKey )
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    pTemps = SymCryptMlDsaTemporariesAllocateAndInitialize(
+        pParams,
+        2, // row vectors - t, t0
+        0, // column vectors
+        1, // poly elements - temporary space
+        pkMlDsakey->pParams->cbEncodedPublicKey ); // scratch space
+    if( pTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // We use temporaries to recalculate t, t0, t1 and the public key hash from the import values
+    // for A (derived from the public seed), s1 and s2. This is not strictly necessary for callers
+    // who just want to import a private key and use it for signing, but it allows the private
+    // key to also be used for verification, and more importantly, provides extra robustness by
+    // ensuring that the derived values are consistent with the encoded values. If the perf of
+    // importing a key becomes a concern, we can move this recalculation to the first time the key
+    // is used for verification.
+    PSYMCRYPT_MLDSA_VECTOR pvtTmp = pTemps->pvRowVectors[0];
+    PSYMCRYPT_MLDSA_VECTOR pvt0Tmp = pTemps->pvRowVectors[1];
+    PSYMCRYPT_MLDSA_POLYELEMENT peTmp = pTemps->pePolyElements[0];
+    PBYTE pbEncodedPubKeyTmp = pTemps->pbScratch;
+
+    // Reset the key state in case this key is being reused
+    pkMlDsakey->hasRootSeed = FALSE;
+    pkMlDsakey->hasPrivateKey = FALSE;
+    SymCryptWipeKnownSize( pkMlDsakey->rootSeed, SYMCRYPT_MLDSA_ROOT_SEED_SIZE );
+
+    memcpy( pkMlDsakey->publicSeed, pbCurr, SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE;
+
+    memcpy( pkMlDsakey->privateSigningSeed, pbCurr, SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE;
+
+    memcpy( pubKeyHashTmp, pbCurr, SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE );
+    pbCurr += SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE;
+
+    // Expand A matrix
+    SymCryptMlDsaExpandA( pkMlDsakey->publicSeed, SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE, pkMlDsakey->pmA );
+
+    scError = SymCryptMlDsaVectorDecode(
+        pbCurr,
+        pParams->encodedCoefficientBitLength,
+        pParams->privateKeyRange,
+        pkMlDsakey->pvs1 );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+    pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pkMlDsakey->pvs1, pParams->encodedCoefficientBitLength );
+
+
+    scError = SymCryptMlDsaVectorDecode(
+        pbCurr,
+        pParams->encodedCoefficientBitLength,
+        pParams->privateKeyRange,
+        pkMlDsakey->pvs2 );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+    pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pkMlDsakey->pvs2, pParams->encodedCoefficientBitLength );
+
+    scError = SymCryptMlDsaVectorDecode(
+        pbCurr,
+        SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS,
+        1 << (SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS - 1),
+        pvt0Tmp );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+    pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pkMlDsakey->pvt0, SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS );
+
+    SYMCRYPT_ASSERT( pbCurr == pbSrc + cbSrc );
+
+    // Convert s1 and s2 to NTT form
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvs1 );
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvs2 );
+
+    SymCryptMlDsakeyComputeT(
+        pkMlDsakey->pmA,
+        pkMlDsakey->pvs1,
+        pkMlDsakey->pvs2,
+        pkMlDsakey->pvt0,
+        pkMlDsakey->pvt1,
+        pvtTmp,
+        peTmp );
+
+    // If the recalculated t0 doesn't match, the imported key is invalid.
+    // Note: SymCryptMlDsakeyComputeT sets t0 and t1 in NTT form.
+    if( memcmp( pkMlDsakey->pvt0, pvt0Tmp, pvt0Tmp->cbTotalSize ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Convert t0 and t1 to NTT form
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvt0 );
+    SymCryptMlDsaVectorNTT( pkMlDsakey->pvt1 );
+
+    scError = SymCryptMlDsaPkEncode( pkMlDsakey, pbEncodedPubKeyTmp, pParams->cbEncodedPublicKey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Recalculate the public key hash and compare it to the imported value. If they don't match,
+    // the imported key is invalid.
+    SymCryptShake256(
+        pbEncodedPubKeyTmp,
+        pParams->cbEncodedPublicKey,
+        pkMlDsakey->publicKeyHash,
+        SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE );
+
+    if( memcmp( pkMlDsakey->publicKeyHash, pubKeyHashTmp, SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    pkMlDsakey->hasPrivateKey = TRUE;
+
+cleanup:
+    if( pTemps != NULL )
+    {
+        SymCryptMlDsaTemporariesFree( pTemps );
+    }
+
+    // Wipe private state on error as defense-in-depth
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptWipeKnownSize( pkMlDsakey->privateSigningSeed, SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE );
+        SymCryptMlDsaVectorSetZero( pkMlDsakey->pvs1 );
+        SymCryptMlDsaVectorSetZero( pkMlDsakey->pvs2 );
+        SymCryptMlDsaVectorSetZero( pkMlDsakey->pvt0 );
+    }
+
+    return scError;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaSigEncode(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PBYTE                               pbCommitmentHash,
+    SIZE_T                              cbCommitmentHash,
+    PCSYMCRYPT_MLDSA_VECTOR             pvResponse,
+    PCSYMCRYPT_MLDSA_VECTOR             pvHint,
+    PBYTE                               pbDst,
+    SIZE_T                              cbDst )
+{
+    SYMCRYPT_ASSERT( cbDst == pParams->cbEncodedSignature );
+    UNREFERENCED_PARAMETER( cbDst );
+
+    const SIZE_T cbEncodedHint = pParams->nHintNonZeroCoeffs + pvHint->nElems;
+
+    PBYTE pbCurr = pbDst;
+
+    memcpy( pbCurr, pbCommitmentHash, cbCommitmentHash );
+    pbCurr += cbCommitmentHash;
+
+    SymCryptMlDsaVectorEncode(
+        pvResponse,
+        pParams->maskCoefficientRangeLog2 + 1,
+        1 << pParams->maskCoefficientRangeLog2,
+        pbCurr );
+
+    pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pvResponse, pParams->maskCoefficientRangeLog2 + 1 );
+
+    SymCryptMlDsaHintBitPack( pParams, pvHint, pbCurr );
+    pbCurr += cbEncodedHint;
+
+    SYMCRYPT_ASSERT( pbCurr == pbDst + cbDst );
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSigDecode(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCBYTE                              pbSig,
+    SIZE_T                              cbSig,
+    PBYTE                               pbCommitmentHash,
+    SIZE_T                              cbCommitmentHash,
+    PSYMCRYPT_MLDSA_VECTOR              pvResponse,
+    PSYMCRYPT_MLDSA_VECTOR              pvHint )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if( cbSig != pParams->cbEncodedSignature )
+    {
+        scError = SYMCRYPT_WRONG_DATA_SIZE;
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT( cbCommitmentHash == pParams->cbCommitmentHash );
+
+    PCBYTE pbCurr = pbSig;
+
+    memcpy( pbCommitmentHash, pbSig, cbCommitmentHash );
+    pbCurr += cbCommitmentHash;
+
+    SymCryptMlDsaVectorDecode(
+        pbCurr,
+        pParams->maskCoefficientRangeLog2 + 1,
+        1 << pParams->maskCoefficientRangeLog2,
+        pvResponse );
+    pbCurr += SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( pvResponse, pParams->maskCoefficientRangeLog2 + 1 );
+
+    SYMCRYPT_ASSERT( cbSig - (pbCurr - pbSig) == (SIZE_T) pParams->nHintNonZeroCoeffs + pvHint->nElems );
+
+    scError = SymCryptMlDsaHintBitUnpack( pParams, pbCurr, pvHint );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaHintBitPack(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    PBYTE                               pbDst )
+{
+    SymCryptWipe( pbDst, pParams->nHintNonZeroCoeffs );
+
+    UINT32 index = 0;
+    for( UINT32 i = 0; i < pvSrc->nElems; ++i )
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peElement = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc );
+        for( UINT32 j = 0; j < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; ++j )
+        {
+            // Side channel safety: the hint is public (part of the signature) so it's okay to
+            // leak information here
+            if( peElement->coeffs[j] != 0 )
+            {
+                // Each byte in the hint is the index of a non-zero coefficient
+                pbDst[index] = (BYTE) j;
+                index++;
+            }
+        }
+
+        // The number of non-zero coefficients in polynomials 0..i is stored in the
+        // (nHintNonZeroCoeffs + i)th byte. This allows us to determine which indices correspond
+        // to which polynomials during decoding while still only using one byte per index.
+        SYMCRYPT_ASSERT( index <= pParams->nHintNonZeroCoeffs );
+        pbDst[pParams->nHintNonZeroCoeffs + i] = (BYTE) index;
+    }
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaHintBitUnpack(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCBYTE                              pbSrc,
+    PSYMCRYPT_MLDSA_VECTOR              pvDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 index = 0;
+    UINT32 maxIndex = 0;
+    UINT32 first = 0;
+
+    for( UINT32 i = 0; i < pvDst->nElems; ++i )
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peElement = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+
+        // Ensure pvDst is zeroed out before unpacking
+        SymCryptMlDsaPolyElementSetZero( peElement );
+
+        maxIndex = pbSrc[pParams->nHintNonZeroCoeffs + i];
+        if( ( maxIndex < index) ||
+            ( maxIndex > pParams->nHintNonZeroCoeffs) )
+        {
+            // Invalid input
+            scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+            goto cleanup;
+        }
+
+        first = index;
+        while( index < maxIndex )
+        {
+            if( index > first && pbSrc[index - 1] >= pbSrc[index])
+            {
+                // Invalid input
+                scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+                goto cleanup;
+            }
+
+            peElement->coeffs[pbSrc[index]] = 1;
+            index++;
+        }
+    }
+
+    for(UINT32 leftover = index; leftover < pParams->nHintNonZeroCoeffs; ++leftover)
+    {
+        if( pbSrc[leftover] != 0 )
+        {
+            // Invalid input
+            scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHashMlDsaValidateHashAlgAndGetOid(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    SYMCRYPT_PQDSA_HASH_ID              hashAlg,
+    SIZE_T                              cbHash,
+    PCSYMCRYPT_OID*                     ppOid )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCSYMCRYPT_OID pHashOid = NULL;
+    BOOLEAN fFound = FALSE;
+    BOOLEAN fIsXof = FALSE;
+    SIZE_T cbHashExpected = 0;
+
+    for( UINT32 i = 0; i < SYMCRYPT_ARRAY_SIZE(g_hashOidMap); ++i )
+    {
+        if( g_hashOidMap[i].hashId == hashAlg )
+        {
+            fFound = TRUE;
+            pHashOid = g_hashOidMap[i].pOid;
+            fIsXof = g_hashOidMap[i].fIsXof;
+            cbHashExpected = g_hashOidMap[i].pHashAlgorithm->resultSize;
+            break;
+        }
+    }
+
+    if( !fFound )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT( pHashOid->cbOID == SYMCRYPT_MLDSA_SUPPORTED_HASH_OID_SIZE );
+
+    // For traditional hash algorithms (non-XOFs), the hash length must exactly match the expected
+    // value. For XOFs, the output length is arbitrary, and any length is acceptable as long as it
+    // meets the minimum collision strength specified by the parameter set (cbCommitmentHash)
+    if( (!fIsXof && cbHash != cbHashExpected ) ||
+        ( cbHash < pParams->cbCommitmentHash ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    *ppOid = pHashOid;
+
+cleanup:
+    return scError;
+}
+
+FORCEINLINE
+INT32
+SYMCRYPT_CALL
+SymCryptMlDsaModPlusMinus( UINT32 r, UINT32 modulus )
+{
+    SYMCRYPT_ASSERT( r < modulus );
+
+    // In most cases this function is used with even moduli, e.g. with Power2Round.
+    // However, it's okay if the modulus is odd. FIPS 204 specifies that the output is in the range
+    // (-ceil(modulus/2), floor(modulus/2) ]
+    // = ( -((modulus + 1) // 2), modulus // 2 ]
+    // = [ -modulus // 2, modulus // 2 ]
+    const INT32 halfModulus = modulus >> 1;
+
+    // Mask for conditional subtraction: 0 if r <= (modulus/2), 0xFFFFFFFF otherwise
+    UINT32 subtractionMask = SYMCRYPT_MASK32_LT( halfModulus, r );
+
+    INT32 r0 = (INT32) r - (modulus & subtractionMask);
+    SYMCRYPT_ASSERT( r0 > -halfModulus && r0 <= halfModulus);
+
+    return r0;
+}
+
+_Use_decl_annotations_
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementInfinityNorm( PCSYMCRYPT_MLDSA_POLYELEMENT peSrc )
+{
+    UINT32 norm = 0;
+    UINT32 mask;
+    INT32 curr;
+    for( UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        // Convert the coefficient to signed form
+        curr = SymCryptMlDsaModPlusMinus( peSrc->coeffs[i], SYMCRYPT_MLDSA_Q );
+
+        // If the coefficient is less than 0, negate it
+        mask = SYMCRYPT_MASK32_LT( curr, 0 );
+        curr = (curr & ~mask) | ((curr * -1) & mask);
+
+        // If the coefficient is greater than the current norm, update the norm
+        mask = SYMCRYPT_MASK32_LT( norm, curr );
+        norm = (norm & ~mask) | (curr & mask);
+    }
+
+    return norm;
+}
+
+_Use_decl_annotations_
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaVectorInfinityNorm( PCSYMCRYPT_MLDSA_VECTOR pvSrc )
+{
+    UINT32 norm = 0;
+    UINT32 curr;
+    INT32 mask;
+    for( UINT32 i = 0; i < pvSrc->nElems; i++ )
+    {
+        curr = SymCryptMlDsaPolyElementInfinityNorm( SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT(i, pvSrc) );
+        mask = SYMCRYPT_MASK32_LT( norm, curr );
+        norm = (norm & ~mask) | (curr & mask);
+    }
+
+    return norm;
+}
+
+_Use_decl_annotations_
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaDecompose(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams,
+    UINT32                           r,
+    UINT32                           *puR1,
+    UINT32                           *puR0 )
+{
+    SYMCRYPT_ASSERT( r < SYMCRYPT_MLDSA_Q );
+    SYMCRYPT_ASSERT( puR1 != NULL || puR0 != NULL );
+
+    UINT32 r1 = 0;
+    UINT32 mask = 0;
+    INT32 r0 = 0;
+
+    // Some tricks for calculating this are borrowed from the reference implementation
+    // https://github.com/pq-crystals/dilithium/blob/master/ref/rounding.c
+    //
+    // The multiplication constants for calculating r1 are in the PCSYMCRYPT_MLDSA_INTERNAL_PARAMS
+    // structure. They are calculated as follows.
+    //
+    // To keep intermediate values in the 32-bit range, instead of using r directly, we calculate
+    // ceil( r/128 ). We likewise divide the commitment rounding range by 128.
+    //
+    // For ML-DSA 44:
+    //     2*commmitmentRoundingRange = 2 * 95,232 = 190,464
+    //     190464 // 128 = 1488
+    //     1 / 1488 ~= floor(2^24 // 1488) * 2^24 = 11,275 // 2^24
+    // For ML-DSA 65 and 87:
+    //     2*commmitmentRoundingRange = 2*261888 = 523776
+    //     523776 // 128 = 4092
+    //     1 / 4092 ~= floor(2^22 // 4092) * 2^22 = 1025 // 2^22 = 4100 // 2^24
+    //
+
+    UINT32 rdiv128 = (r + 127) >> 7;
+    r1 = ( rdiv128 * pParams->decomposeR1Factor + (1 << 23)) >> 24;
+
+    // Handle corner case: if r1 is outside of the expected range, set it to 0
+    mask = SYMCRYPT_MASK32_LT( r1, pParams->commitmentModulus );
+    r1 &= mask;
+
+    r0 = r - ( r1 * 2 * pParams->commitmentRoundingRange );
+
+    // Handle corner case for r0
+    r0 -= ((((SYMCRYPT_MLDSA_Q - 1) >> 1) - r0) >> 31) & SYMCRYPT_MLDSA_Q;
+    r0 = SymCryptMlDsaSignedCoefficientModQ( r0 );
+
+    if( puR1 != NULL )
+    {
+        *puR1 = r1;
+    }
+
+    if( puR0 != NULL )
+    {
+        *puR0 = r0;
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorHighBits(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    PSYMCRYPT_MLDSA_VECTOR              pvDst )
+{
+    SYMCRYPT_ASSERT( pvSrc->nElems == pvDst->nElems );
+
+    for(UINT32 i = 0; i < pvSrc->nElems; i++)
+    {
+        for(UINT32 j = 0; j < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; j++)
+        {
+            SymCryptMlDsaDecompose(
+                pParams,
+                SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc )->coeffs[j],
+                &(SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst )->coeffs[j]),
+                NULL );
+        }
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorLowBits(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    PSYMCRYPT_MLDSA_VECTOR              pvDst )
+{
+    SYMCRYPT_ASSERT( pvSrc->nElems == pvDst->nElems );
+
+    for(UINT32 i = 0; i < pvSrc->nElems; i++)
+    {
+        for(UINT32 j = 0; j < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; j++)
+        {
+            SymCryptMlDsaDecompose(
+                pParams,
+                SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc )->coeffs[j],
+                NULL,
+                &(SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst )->coeffs[j]) );
+        }
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPower2Round(
+    UINT32  r,
+    UINT32* puR1,
+    UINT32* puR0 )
+{
+    SYMCRYPT_ASSERT( r < SYMCRYPT_MLDSA_Q );
+
+    UINT32 rPrime = r & ( (1 << SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS) - 1 ); // r mod 2^d
+    INT32 r0 = SymCryptMlDsaModPlusMinus( rPrime, 1 << SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS );
+
+    UINT32 r1 = (r - r0) >> SYMCRYPT_POWER2ROUND_LOW_ORDER_BITS;
+
+    *puR1 = r1;
+    *puR0 = SymCryptMlDsaSignedCoefficientModQ( r0 ); //, 4096 );
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementPower2Round(
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc,
+    PSYMCRYPT_MLDSA_POLYELEMENT     peDst1,
+    PSYMCRYPT_MLDSA_POLYELEMENT     peDst0 )
+{
+    UINT32 r1, r0;
+
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        SymCryptMlDsaPower2Round( peSrc->coeffs[i], &r1, &r0 );
+        peDst1->coeffs[i] = r1;
+        peDst0->coeffs[i] = r0;
+    }
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorPower2Round(
+    PCSYMCRYPT_MLDSA_VECTOR pvSrc,
+    PSYMCRYPT_MLDSA_VECTOR  pvDst1,
+    PSYMCRYPT_MLDSA_VECTOR  pvDst0 )
+{
+    SYMCRYPT_ASSERT( pvSrc->nElems == pvDst1->nElems );
+    SYMCRYPT_ASSERT( pvSrc->nElems == pvDst0->nElems );
+
+    for(UINT32 i = 0; i < pvSrc->nElems; i++)
+    {
+        SymCryptMlDsaPolyElementPower2Round(
+            SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc ),
+            SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst1 ),
+            SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst0 ) );
+    }
+}
+
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaSignedCoefficientModQ( INT32 coefficient )
+{
+    SYMCRYPT_ASSERT(coefficient > -1 * SYMCRYPT_MLDSA_Q && coefficient < SYMCRYPT_MLDSA_Q);
+
+    UINT32 result;
+    UINT32 negativeMask = SYMCRYPT_MASK32_LT( coefficient, 0 );
+
+    result = coefficient + (SYMCRYPT_MLDSA_Q & negativeMask);
+    SYMCRYPT_ASSERT( result < SYMCRYPT_MLDSA_Q );
+
+    return result;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementEncode(
+    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc,
+    UINT32                          nBitsPerCoefficient,
+    UINT32                          signedCoefficientBound,
+    PBYTE                           pbDst )
+{
+    SYMCRYPT_ASSERT( nBitsPerCoefficient >  0  );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient <= 20 ); // Maximum number of bits per coefficient across all encodings
+
+    INT32 coefficient;
+    UINT32 nBitsInCoefficient;
+    UINT32 bitsToEncode;
+    UINT32 nBitsToEncode;
+    UINT32 cbDstWritten = 0;
+    UINT32 accumulator = 0;
+    UINT32 nBitsInAccumulator = 0;
+
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        nBitsInCoefficient = nBitsPerCoefficient;
+        coefficient = peSrc->coeffs[i];
+
+        SYMCRYPT_ASSERT( coefficient < SYMCRYPT_MLDSA_Q );
+
+        // If the coefficient is greater than the signedCoefficientBound, that means it is
+        // a negative value modulo Q, so we need to subtract Q to get the original value.
+        coefficient -= ( SYMCRYPT_MLDSA_Q & SYMCRYPT_MASK32_LT( signedCoefficientBound, coefficient ) );
+
+        // The coefficient is now in the range [-signedCoefficientBound, signedCoefficientBound],
+        // we need to map it to the range [0, 2*signedCoefficientBound] for encoding.
+        coefficient = SYMCRYPT_INTERNAL_MLDSA_SHORT_COEFFICIENT_ENCODE_DECODE( coefficient, signedCoefficientBound );
+
+        // Some coefficients are always positive and so do not need any special encoding. In this
+        // case, we revert to the original value from the source polynomial.
+        coefficient = ( peSrc->coeffs[i] & SYMCRYPT_MASK32_ZERO( signedCoefficientBound ) ) |
+            ( coefficient & SYMCRYPT_MASK32_NONZERO( signedCoefficientBound ) );
+
+        SYMCRYPT_ASSERT( coefficient >= 0 );
+        SYMCRYPT_ASSERT( signedCoefficientBound == 0 || (UINT32) coefficient <= signedCoefficientBound * 2 );
+        SYMCRYPT_ASSERT( (UINT32) coefficient < (1ul << nBitsPerCoefficient) );
+
+        // encode the coefficient
+        // simple loop to add bits to accumulator and write accumulator to output
+        do
+        {
+            nBitsToEncode = SYMCRYPT_MIN( nBitsInCoefficient, 32 - nBitsInAccumulator );
+
+            bitsToEncode = coefficient & ( ( 1UL << nBitsToEncode ) - 1 );
+            coefficient >>= nBitsToEncode;
+            nBitsInCoefficient -= nBitsToEncode;
+
+            accumulator |= ( bitsToEncode << nBitsInAccumulator );
+            nBitsInAccumulator += nBitsToEncode;
+            if(nBitsInAccumulator == 32)
+            {
+                SYMCRYPT_STORE_LSBFIRST32( pbDst + cbDstWritten, accumulator );
+                cbDstWritten += 4;
+                accumulator = 0;
+                nBitsInAccumulator = 0;
+            }
+        } while( nBitsInCoefficient > 0 );
+    }
+
+    SYMCRYPT_ASSERT(nBitsInAccumulator == 0);
+    SYMCRYPT_ASSERT(cbDstWritten == (nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8)));
+}
+
+_Use_decl_annotations_
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementDecode(
+    PCBYTE                      pbSrc,
+    UINT32                      nBitsPerCoefficient,
+    UINT32                      signedCoefficientBound,
+    PSYMCRYPT_MLDSA_POLYELEMENT peDst )
+{
+    SYMCRYPT_ASSERT( nBitsPerCoefficient >  0  );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient <= 20 ); // Maximum number of bits per coefficient across all encodings
+
+    INT32 coefficient;
+    UINT32 nBitsInCoefficient;
+    UINT32 bitsToDecode;
+    UINT32 nBitsToDecode;
+    UINT32 cbSrcRead = 0;
+    UINT32 accumulator = 0;
+    UINT32 nBitsInAccumulator = 0;
+
+    for(UINT32 i = 0; i < SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        coefficient = 0;
+        nBitsInCoefficient = 0;
+
+        // first gather and decode bits from pbSrc
+        do
+        {
+            if(nBitsInAccumulator == 0)
+            {
+                accumulator = SYMCRYPT_LOAD_LSBFIRST32( pbSrc+cbSrcRead );
+                cbSrcRead += 4;
+                nBitsInAccumulator = 32;
+            }
+
+            nBitsToDecode = SYMCRYPT_MIN(nBitsPerCoefficient-nBitsInCoefficient, nBitsInAccumulator);
+            SYMCRYPT_ASSERT(nBitsToDecode <= nBitsInAccumulator);
+
+            bitsToDecode = accumulator & ((1UL<<nBitsToDecode)-1);
+            accumulator >>= nBitsToDecode;
+            nBitsInAccumulator -= nBitsToDecode;
+
+            coefficient |= (bitsToDecode << nBitsInCoefficient);
+            nBitsInCoefficient += nBitsToDecode;
+        } while( nBitsPerCoefficient > nBitsInCoefficient );
+        SYMCRYPT_ASSERT( nBitsInCoefficient == nBitsPerCoefficient );
+
+        // Coefficient should always be positive at this point since it's encoded in <= 20 bits
+        SYMCRYPT_ASSERT( coefficient >= 0 );
+
+        if( ( signedCoefficientBound != 0 ) && ( (UINT32) coefficient > 2 * signedCoefficientBound ) )
+        {
+            // Most of the encoded components of ML-DSA keys and signatures cannot be outside the
+            // valid range, because the number of bits in their encodings do not permit invalid
+            // values. However, the private key components s1 and s2 have encodings that do allow
+            // for invalid values (because their valid ranges are [-2, 2] or [-4, 4]). If any
+            // coefficient is outside this range, the key is invalid and we return an error.
+            // We do not need to do this check in constant time because we treat the validity of an
+            // imported key as public information.
+            return SYMCRYPT_INVALID_BLOB;
+        }
+
+        // If this coefficient is intended to be signed, we need to decode it into its original
+        // signed form and then map it modulo Q.
+        // Side-channel safety: signedCoefficientBound just indicates which component we are
+        // decoding, so it's public information
+        if( signedCoefficientBound != 0 )
+        {
+            coefficient = SYMCRYPT_INTERNAL_MLDSA_SHORT_COEFFICIENT_ENCODE_DECODE( coefficient, signedCoefficientBound );
+            coefficient = SymCryptMlDsaSignedCoefficientModQ( coefficient );
+        }
+
+        peDst->coeffs[i] = coefficient;
+    }
+
+    SYMCRYPT_ASSERT(nBitsInAccumulator == 0);
+    SYMCRYPT_ASSERT(cbSrcRead == (nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8)));
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorEncode(
+    PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    UINT32                              nBitsPerCoefficient,
+    UINT32                              signedCoefficientBound,
+    PBYTE                               pbDst )
+{
+    PBYTE pbCurr = pbDst;
+    const SIZE_T cbEncodedPoly = nBitsPerCoefficient * ( SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8 );
+
+    for( UINT32 i = 0; i < pvSrc->nElems; ++i )
+    {
+        PCSYMCRYPT_MLDSA_POLYELEMENT peElement = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvSrc );
+
+        SymCryptMlDsaPolyElementEncode(
+            peElement,
+            nBitsPerCoefficient,
+            signedCoefficientBound,
+            pbCurr );
+
+        pbCurr += cbEncodedPoly;
+    }
+
+    SYMCRYPT_ASSERT( pbCurr == pbDst + ( pvSrc->nElems * cbEncodedPoly ) );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaVectorDecode(
+    _In_reads_bytes_( pvDst->nElems * nBitsPerCoefficient * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) )
+            PCBYTE                              pbSrc,
+            UINT32                              nBitsPerCoefficient,
+            UINT32                              signedCoefficientBound,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR              pvDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCBYTE pbCurr = pbSrc;
+    const SIZE_T cbEncodedPoly = nBitsPerCoefficient * ( SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8 );
+
+    for( UINT32 i = 0; i < pvDst->nElems; ++i )
+    {
+        PSYMCRYPT_MLDSA_POLYELEMENT peElement = SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( i, pvDst );
+
+        scError = SymCryptMlDsaPolyElementDecode(
+            pbCurr,
+            nBitsPerCoefficient,
+            signedCoefficientBound,
+            peElement );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            // Side-channel safety: the validity of an imported key is public information.
+            // See comment in SymCryptMlDsaPolyElementDecode.
+            goto cleanup;
+        }
+
+        pbCurr += cbEncodedPoly;
+    }
+
+    SYMCRYPT_ASSERT( pbCurr == pbSrc + ( pvDst->nElems * cbEncodedPoly ) );
+
+cleanup:
+    return scError;
+}
+
+_Use_decl_annotations_
+PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES
+SYMCRYPT_CALL
+SymCryptMlDsaTemporariesAllocateAndInitialize(
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    UINT32                              nRowVectors,
+    UINT32                              nColVectors,
+    UINT32                              nPolyElements,
+    UINT32                              cbScratch )
+{
+    // Round scratch space to nearest multiple of 8 for alignment
+    cbScratch = (cbScratch + 7) & ~7;
+
+    UINT32 cbTotalSize = sizeof( SYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES) +
+        ( nRowVectors * sizeof( PSYMCRYPT_MLDSA_VECTOR ) ) +        // Row vector pointers
+        ( nColVectors * sizeof( PSYMCRYPT_MLDSA_VECTOR ) ) +        // Col vector pointers
+        ( nPolyElements * sizeof( PSYMCRYPT_MLDSA_POLYELEMENT ) ) + // Poly element pointers
+        ( nRowVectors * pParams->cbRowVector ) +                    // Row vector buffer
+        ( nColVectors * pParams->cbColVector ) +                    // Col vector buffer
+        ( nPolyElements * pParams->cbPolyElement ) +                // Poly element buffer
+        ( cbScratch );                                              // Scratch buffer
+
+    PBYTE pbBuffer = SymCryptCallbackAlloc( cbTotalSize );
+    if( pbBuffer == NULL )
+    {
+        return NULL;
+    }
+
+    SymCryptWipe( pbBuffer, cbTotalSize );
+
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemporaries =
+        (PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES) pbBuffer;
+
+    pTemporaries->cbTotalSize = cbTotalSize;
+    pTemporaries->nRowVectors = nRowVectors;
+    pTemporaries->nColVectors = nColVectors;
+    pTemporaries->nPolyElements = nPolyElements;
+    pTemporaries->cbScratch = cbScratch;
+
+    PBYTE pbCurrent = pbBuffer + sizeof( SYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES );
+
+    if( nRowVectors > 0 )
+    {
+        pTemporaries->pvRowVectors = (PSYMCRYPT_MLDSA_VECTOR *) pbCurrent;
+        pbCurrent += nRowVectors * sizeof( PSYMCRYPT_MLDSA_VECTOR );
+    }
+
+    if( nColVectors > 0 )
+    {
+        pTemporaries->pvColVectors = (PSYMCRYPT_MLDSA_VECTOR *) pbCurrent;
+        pbCurrent += nColVectors * sizeof( PSYMCRYPT_MLDSA_VECTOR );
+    }
+
+    if( nPolyElements > 0 )
+    {
+        pTemporaries->pePolyElements = (PSYMCRYPT_MLDSA_POLYELEMENT *) pbCurrent;
+        pbCurrent += nPolyElements * sizeof( PSYMCRYPT_MLDSA_POLYELEMENT );
+    }
+
+    for(UINT32 i = 0; i < nRowVectors; i++)
+    {
+        pTemporaries->pvRowVectors[i] = SymCryptMlDsaVectorCreate( pbCurrent, pParams->cbRowVector, pParams->nRows );
+        pbCurrent += pParams->cbRowVector;
+    }
+
+    for(UINT32 i = 0; i < nColVectors; i++)
+    {
+        pTemporaries->pvColVectors[i] = SymCryptMlDsaVectorCreate( pbCurrent, pParams->cbColVector, pParams->nCols );
+        pbCurrent += pParams->cbColVector;
+    }
+
+    for(UINT32 i = 0; i < nPolyElements; i++)
+    {
+        pTemporaries->pePolyElements[i] = SymCryptMlDsaPolyElementCreate( pbCurrent, pParams->cbPolyElement );
+        pbCurrent += pParams->cbPolyElement;
+    }
+
+    if( cbScratch > 0 )
+    {
+        pTemporaries->pbScratch = pbCurrent;
+        pbCurrent += cbScratch;
+    }
+
+    SYMCRYPT_ASSERT( pbCurrent == pbBuffer + cbTotalSize );
+
+    SYMCRYPT_SET_MAGIC( pTemporaries );
+
+    return pTemporaries;
+}
+
+_Use_decl_annotations_
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaTemporariesFree(
+    PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemporaries )
+{
+    SYMCRYPT_CHECK_MAGIC( pTemporaries );
+
+    SymCryptWipe( pTemporaries, pTemporaries->cbTotalSize );
+    SymCryptCallbackFree( pTemporaries );
+}
diff --git a/libs/symcrypt/lib/mlkem.c b/libs/symcrypt/lib/mlkem.c
new file mode 100644
index 000000000000..975775587ce2
--- /dev/null
+++ b/libs/symcrypt/lib/mlkem.c
@@ -0,0 +1,1164 @@
+//
+// mlkem.c   ML-KEM related functionality
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define NROWS_MLKEM512   (2)
+#define NROWS_MLKEM768   (3)
+#define NROWS_MLKEM1024  (4)
+
+const SYMCRYPT_MLKEM_INTERNAL_PARAMS SymCryptMlKemInternalParamsMlKem512 =
+{
+    .params         = SYMCRYPT_MLKEM_PARAMS_MLKEM512,
+    .cbPolyElement  = SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT,
+    .nRows          = NROWS_MLKEM512,
+    .cbVector       = sizeof(SYMCRYPT_MLKEM_VECTOR) + (NROWS_MLKEM512 * SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT),
+    .cbMatrix       = sizeof(SYMCRYPT_MLKEM_MATRIX) + (NROWS_MLKEM512 *
+                                                       NROWS_MLKEM512 *
+                                                       SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT),
+    .nEta1          = 3,
+    .nEta2          = 2,
+    .nBitsOfU       = 10,
+    .nBitsOfV       = 4,
+};
+
+const SYMCRYPT_MLKEM_INTERNAL_PARAMS SymCryptMlKemInternalParamsMlKem768 =
+{
+    .params         = SYMCRYPT_MLKEM_PARAMS_MLKEM768,
+    .cbPolyElement  = SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT,
+    .nRows          = NROWS_MLKEM768,
+    .cbVector       = sizeof(SYMCRYPT_MLKEM_VECTOR) + (NROWS_MLKEM768 * SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT),
+    .cbMatrix       = sizeof(SYMCRYPT_MLKEM_MATRIX) + (NROWS_MLKEM768 *
+                                                       NROWS_MLKEM768 *
+                                                       SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT),
+    .nEta1          = 2,
+    .nEta2          = 2,
+    .nBitsOfU       = 10,
+    .nBitsOfV       = 4,
+};
+
+const SYMCRYPT_MLKEM_INTERNAL_PARAMS SymCryptMlKemInternalParamsMlKem1024 =
+{
+    .params         = SYMCRYPT_MLKEM_PARAMS_MLKEM1024,
+    .cbPolyElement  = SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT,
+    .nRows          = NROWS_MLKEM1024,
+    .cbVector       = sizeof(SYMCRYPT_MLKEM_VECTOR) + (NROWS_MLKEM1024 * SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT),
+    .cbMatrix       = sizeof(SYMCRYPT_MLKEM_MATRIX) + (NROWS_MLKEM1024 *
+                                                       NROWS_MLKEM1024 *
+                                                       SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT),
+    .nEta1          = 2,
+    .nEta2          = 2,
+    .nBitsOfU       = 11,
+    .nBitsOfV       = 5,
+};
+
+static
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeyGetInternalParamsFromParams(
+            SYMCRYPT_MLKEM_PARAMS           params,
+    _Out_   PSYMCRYPT_MLKEM_INTERNAL_PARAMS pInternalParams )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    switch( params )
+    {
+        case SYMCRYPT_MLKEM_PARAMS_MLKEM512:
+            *pInternalParams = SymCryptMlKemInternalParamsMlKem512;
+            break;
+        case SYMCRYPT_MLKEM_PARAMS_MLKEM768:
+            *pInternalParams = SymCryptMlKemInternalParamsMlKem768;
+            break;
+        case SYMCRYPT_MLKEM_PARAMS_MLKEM1024:
+            *pInternalParams = SymCryptMlKemInternalParamsMlKem1024;
+            break;
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+static
+PSYMCRYPT_MLKEMKEY
+SYMCRYPT_CALL
+SymCryptMlKemkeyInitialize(
+    _In_                        PCSYMCRYPT_MLKEM_INTERNAL_PARAMS    pInternalParams,
+    _Out_writes_bytes_(cbKey)   PBYTE                               pbKey,
+                                UINT32                              cbKey )
+{
+    PSYMCRYPT_MLKEMKEY pRes = NULL;
+    PSYMCRYPT_MLKEMKEY pKey = (PSYMCRYPT_MLKEMKEY)pbKey;
+    PBYTE pbCurr = pbKey + sizeof(SYMCRYPT_MLKEMKEY);
+
+    SymCryptWipeKnownSize( pbKey, cbKey );
+
+    pKey->fAlgorithmInfo = 0;
+    pKey->params = *pInternalParams;
+    pKey->cbTotalSize = cbKey;
+    pKey->hasPrivateSeed = FALSE;
+    pKey->hasPrivateKey = FALSE;
+
+    pKey->pmAtranspose = SymCryptMlKemMatrixCreate( pbCurr, pInternalParams->cbMatrix, pInternalParams->nRows );
+    if( pKey->pmAtranspose == NULL )
+    {
+        goto cleanup;
+    }
+    pbCurr += pInternalParams->cbMatrix;
+
+    pKey->pvt = SymCryptMlKemVectorCreate( pbCurr, pInternalParams->cbVector, pInternalParams->nRows );
+    if( pKey->pvt == NULL )
+    {
+        goto cleanup;
+    }
+    pbCurr += pInternalParams->cbVector;
+
+    pKey->pvs = SymCryptMlKemVectorCreate( pbCurr, pInternalParams->cbVector, pInternalParams->nRows );
+    if( pKey->pvs == NULL )
+    {
+        goto cleanup;
+    }
+    pbCurr += pInternalParams->cbVector;
+
+    SYMCRYPT_ASSERT( pbCurr == (pbKey + cbKey) );
+
+    SYMCRYPT_SET_MAGIC( pKey );
+
+    pRes = pKey;
+
+cleanup:
+    return pRes;
+}
+
+PSYMCRYPT_MLKEMKEY
+SYMCRYPT_CALL
+SymCryptMlKemkeyAllocate(
+    SYMCRYPT_MLKEM_PARAMS   params )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE  pbKey = NULL;
+    UINT32 cbKey;
+    SYMCRYPT_MLKEM_INTERNAL_PARAMS internalParams;
+
+    PSYMCRYPT_MLKEMKEY pKey = NULL;
+
+    scError = SymCryptMlKemkeyGetInternalParamsFromParams(params, &internalParams);
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    cbKey = sizeof(SYMCRYPT_MLKEMKEY) + internalParams.cbMatrix + (2*internalParams.cbVector);
+
+    pbKey = SymCryptCallbackAlloc( cbKey );
+    if ( pbKey == NULL )
+    {
+        goto cleanup;
+    }
+
+    pKey = SymCryptMlKemkeyInitialize( &internalParams, pbKey, cbKey );
+    if ( pKey == NULL )
+    {
+        goto cleanup;
+    }
+
+    pbKey = NULL;
+
+cleanup:
+    if ( pbKey != NULL )
+    {
+        SymCryptCallbackFree( pbKey );
+    }
+
+    return pKey;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyFree(
+    _Inout_ PSYMCRYPT_MLKEMKEY  pkMlKemkey )
+{
+    SYMCRYPT_CHECK_MAGIC( pkMlKemkey );
+
+    SymCryptWipe( (PBYTE) pkMlKemkey, pkMlKemkey->cbTotalSize );
+
+    SymCryptCallbackFree( pkMlKemkey );
+}
+
+#define SYMCRYPT_MLKEM_SIZEOF_ENCODED_UNCOMPRESSED_VECTOR(_nRows)   (384UL * _nRows)
+
+// s and t are encoded uncompressed vectors
+// public seed, H(encapsulation key) and z are each 32 bytes
+#define SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY(_nRows)  ((2*SYMCRYPT_MLKEM_SIZEOF_ENCODED_UNCOMPRESSED_VECTOR(_nRows)) + (3*32))
+// t is encoded uncompressed vector
+// public seed is 32 bytes
+#define SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY(_nRows)  (SYMCRYPT_MLKEM_SIZEOF_ENCODED_UNCOMPRESSED_VECTOR(_nRows) + 32)
+
+C_ASSERT( SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY(NROWS_MLKEM512) == SYMCRYPT_MLKEM_DECAPSULATION_KEY_SIZE_MLKEM512 );
+C_ASSERT( SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY(NROWS_MLKEM768) == SYMCRYPT_MLKEM_DECAPSULATION_KEY_SIZE_MLKEM768 );
+C_ASSERT( SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY(NROWS_MLKEM1024) == SYMCRYPT_MLKEM_DECAPSULATION_KEY_SIZE_MLKEM1024 );
+
+C_ASSERT( SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY(NROWS_MLKEM512) == SYMCRYPT_MLKEM_ENCAPSULATION_KEY_SIZE_MLKEM512 );
+C_ASSERT( SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY(NROWS_MLKEM768) == SYMCRYPT_MLKEM_ENCAPSULATION_KEY_SIZE_MLKEM768 );
+C_ASSERT( SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY(NROWS_MLKEM1024) == SYMCRYPT_MLKEM_ENCAPSULATION_KEY_SIZE_MLKEM1024 );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemSizeofKeyFormatFromParams(
+            SYMCRYPT_MLKEM_PARAMS       params,
+            SYMCRYPT_MLKEMKEY_FORMAT    mlKemkeyFormat,
+    _Out_   SIZE_T*                     pcbKeyFormat )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_MLKEM_INTERNAL_PARAMS internalParams;
+
+    if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_NULL )
+    {
+        scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+        goto cleanup;
+    }
+
+    scError = SymCryptMlKemkeyGetInternalParamsFromParams(params, &internalParams);
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    switch( mlKemkeyFormat )
+    {
+        case SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED:
+            *pcbKeyFormat = SYMCRYPT_MLKEM_PRIVATE_SEED_SIZE;
+            break;
+
+        case SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY:
+            *pcbKeyFormat = SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY(internalParams.nRows);
+            break;
+
+        case SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY:
+            *pcbKeyFormat = SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY(internalParams.nRows);
+            break;
+
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemSizeofCiphertextFromParams(
+            SYMCRYPT_MLKEM_PARAMS       params,
+    _Out_   SIZE_T*                     pcbCiphertext )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_MLKEM_INTERNAL_PARAMS internalParams;
+    SIZE_T cbU, cbV;
+
+    scError = SymCryptMlKemkeyGetInternalParamsFromParams(params, &internalParams);
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // u vector encoded with nBitsOfU * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits per polynomial
+    cbU = ((SIZE_T)internalParams.nRows) * internalParams.nBitsOfU * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    // v polynomial encoded with nBitsOfV * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits
+    cbV = ((SIZE_T)internalParams.nBitsOfV) * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    *pcbCiphertext = cbU + cbV;
+
+    SYMCRYPT_ASSERT( (internalParams.params != SYMCRYPT_MLKEM_PARAMS_MLKEM512)  || ((cbU + cbV) == SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_MLKEM512)  );
+    SYMCRYPT_ASSERT( (internalParams.params != SYMCRYPT_MLKEM_PARAMS_MLKEM768)  || ((cbU + cbV) == SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_MLKEM768)  );
+    SYMCRYPT_ASSERT( (internalParams.params != SYMCRYPT_MLKEM_PARAMS_MLKEM1024) || ((cbU + cbV) == SYMCRYPT_MLKEM_CIPHERTEXT_SIZE_MLKEM1024) );
+
+cleanup:
+    return scError;
+}
+
+static
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyExpandPublicMatrixFromPublicSeed(
+    _Inout_ PSYMCRYPT_MLKEMKEY                                  pkMlKemkey,
+    _Inout_ PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES    pCompTemps )
+{
+    UINT32 i, j;
+    BYTE coordinates[2];
+
+    PSYMCRYPT_SHAKE128_STATE pShakeStateBase = &pCompTemps->hashState0.shake128State;
+    PSYMCRYPT_SHAKE128_STATE pShakeStateWork = &pCompTemps->hashState1.shake128State;
+    const UINT32 nRows = pkMlKemkey->params.nRows;
+
+    SymCryptShake128Init( pShakeStateBase );
+    SymCryptShake128Append( pShakeStateBase, pkMlKemkey->publicSeed, sizeof(pkMlKemkey->publicSeed) );
+
+    for( i=0; i<nRows; i++ )
+    {
+        coordinates[1] = (BYTE)i;
+        for( j=0; j<nRows; j++ )
+        {
+            coordinates[0] = (BYTE)j;
+            SymCryptShake128StateCopy( pShakeStateBase, pShakeStateWork );
+            SymCryptShake128Append( pShakeStateWork, coordinates, sizeof(coordinates) );
+
+            SymCryptMlKemPolyElementSampleNTTFromShake128( pShakeStateWork, pkMlKemkey->pmAtranspose->apPolyElements[(i*nRows)+j] );
+        }
+    }
+
+    // no need to wipe; everything computed here is always public
+}
+
+static
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyComputeEncapsulationKeyHash(
+    _Inout_ PSYMCRYPT_MLKEMKEY                                  pkMlKemkey,
+    _Inout_ PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES    pCompTemps,
+            SIZE_T                                              cbEncodedVector )
+{
+    PSYMCRYPT_SHA3_256_STATE pState = &pCompTemps->hashState0.sha3_256State;
+
+    SymCryptSha3_256Init( pState );
+    SymCryptSha3_256Append( pState, pkMlKemkey->encodedT, cbEncodedVector );
+    SymCryptSha3_256Append( pState, pkMlKemkey->publicSeed, sizeof(pkMlKemkey->publicSeed) );
+    SymCryptSha3_256Result( pState, pkMlKemkey->encapsKeyHash );
+}
+
+static
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyExpandFromPrivateSeed(
+    _Inout_ PSYMCRYPT_MLKEMKEY                                  pkMlKemkey,
+    _Inout_ PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES    pCompTemps )
+{
+    BYTE privateSeedHash[SYMCRYPT_SHA3_512_RESULT_SIZE];
+    BYTE CBDSampleBuffer[3*64 + 1];
+    PSYMCRYPT_MLKEM_VECTOR pvTmp;
+    PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp;
+    PSYMCRYPT_SHAKE256_STATE pShakeStateBase = &pCompTemps->hashState0.shake256State;
+    PSYMCRYPT_SHAKE256_STATE pShakeStateWork = &pCompTemps->hashState1.shake256State;
+    UINT32 i;
+    const UINT32 nRows = pkMlKemkey->params.nRows;
+    const UINT32 nEta1 = pkMlKemkey->params.nEta1;
+    const SIZE_T cbEncodedVector = SYMCRYPT_MLKEM_SIZEOF_ENCODED_UNCOMPRESSED_VECTOR(nRows);
+    const UINT32 cbPolyElement = pkMlKemkey->params.cbPolyElement;
+    const UINT32 cbVector = pkMlKemkey->params.cbVector;
+
+    SYMCRYPT_ASSERT( pkMlKemkey->hasPrivateSeed );
+    SYMCRYPT_ASSERT( (nEta1 == 2) || (nEta1 == 3) );
+    SYMCRYPT_ASSERT( cbEncodedVector <= sizeof(pkMlKemkey->encodedT) );
+
+    pvTmp = SymCryptMlKemVectorCreate( pCompTemps->abVectorBuffer0, cbVector, nRows );
+    SYMCRYPT_ASSERT( pvTmp != NULL );
+    paTmp = SymCryptMlKemPolyElementAccumulatorCreate( pCompTemps->abPolyElementAccumulatorBuffer, 2*cbPolyElement );
+    SYMCRYPT_ASSERT( paTmp != NULL );
+
+    // (rho || sigma) = G(d || k)
+    // use CBDSampleBuffer to concatenate the private seed and encoding of nRows
+    memcpy( CBDSampleBuffer, pkMlKemkey->privateSeed, sizeof(pkMlKemkey->privateSeed) );
+    CBDSampleBuffer[sizeof(pkMlKemkey->privateSeed)] = (BYTE) nRows;
+    SymCryptSha3_512( CBDSampleBuffer, sizeof(pkMlKemkey->privateSeed)+1, privateSeedHash );
+
+    // copy public seed
+    memcpy( pkMlKemkey->publicSeed, privateSeedHash, sizeof(pkMlKemkey->publicSeed) );
+
+    // generate A from public seed
+    SymCryptMlKemkeyExpandPublicMatrixFromPublicSeed( pkMlKemkey, pCompTemps );
+
+    // Initialize pShakeStateBase with sigma
+    SymCryptShake256Init( pShakeStateBase );
+    SymCryptShake256Append( pShakeStateBase, privateSeedHash+sizeof(pkMlKemkey->publicSeed), 32 );
+
+    // Expand s in place
+    for( i=0; i<nRows; i++ )
+    {
+        CBDSampleBuffer[0] = (BYTE) i;
+        SymCryptShake256StateCopy( pShakeStateBase, pShakeStateWork );
+        SymCryptShake256Append( pShakeStateWork, CBDSampleBuffer, 1 );
+
+        SymCryptShake256Extract( pShakeStateWork, CBDSampleBuffer, 64ul*nEta1, FALSE );
+
+        SymCryptMlKemPolyElementSampleCBDFromBytes( CBDSampleBuffer, nEta1, SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT(i, pkMlKemkey->pvs) );
+    }
+    // Expand e in t, ready for multiply-add
+    for( i=0; i<nRows; i++ )
+    {
+        CBDSampleBuffer[0] = (BYTE) (nRows+i);
+        SymCryptShake256StateCopy( pShakeStateBase, pShakeStateWork );
+        SymCryptShake256Append( pShakeStateWork, CBDSampleBuffer, 1 );
+
+        SymCryptShake256Extract( pShakeStateWork, CBDSampleBuffer, 64ul*nEta1, FALSE );
+
+        SymCryptMlKemPolyElementSampleCBDFromBytes( CBDSampleBuffer, nEta1, SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT(i, pkMlKemkey->pvt) );
+    }
+
+    // Perform NTT on s and e
+    SymCryptMlKemVectorNTT( pkMlKemkey->pvs );
+    SymCryptMlKemVectorNTT( pkMlKemkey->pvt );
+
+    // pvTmp = s .* R
+    SymCryptMlKemVectorMulR( pkMlKemkey->pvs, pvTmp );
+
+    // t = ((A o (s .* R)) ./ R) + e = A o s + e
+    SymCryptMlKemMatrixVectorMontMulAndAdd( pkMlKemkey->pmAtranspose, pvTmp, pkMlKemkey->pvt, paTmp );
+
+    // transpose A
+    SymCryptMlKemMatrixTranspose( pkMlKemkey->pmAtranspose );
+
+    // precompute byte-encoding of public vector t
+    SymCryptMlKemVectorCompressAndEncode( pkMlKemkey->pvt, 12, pkMlKemkey->encodedT, cbEncodedVector );
+
+    // precompute hash of encapsulation key blob
+    SymCryptMlKemkeyComputeEncapsulationKeyHash( pkMlKemkey, pCompTemps, cbEncodedVector );
+
+    // Cleanup!
+    SymCryptWipeKnownSize( privateSeedHash, sizeof(privateSeedHash) );
+    SymCryptWipeKnownSize( CBDSampleBuffer, sizeof(CBDSampleBuffer) );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeySetValue(
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_MLKEMKEY_FORMAT    mlKemkeyFormat,
+                                UINT32                      flags,
+    _Inout_                     PSYMCRYPT_MLKEMKEY          pkMlKemkey )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PCBYTE pbCurr = pbSrc;
+    PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES pCompTemps = NULL;
+    const UINT32 nRows = pkMlKemkey->params.nRows;
+    const SIZE_T cbEncodedVector = SYMCRYPT_MLKEM_SIZEOF_ENCODED_UNCOMPRESSED_VECTOR( nRows );
+
+    // Ensure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION;
+
+    if ( ( flags & ~allowedFlags ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check that minimal validation flag only specified with no fips
+    if ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+         ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) != 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_NULL )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // Ensure ML-KEM algorithm selftest is run before first use of ML-KEM algorithms;
+        // notably _before_ first full KeyGen
+        SYMCRYPT_RUN_SELFTEST_ONCE(
+            SymCryptMlKemSelftest,
+            SYMCRYPT_SELFTEST_ALGORITHM_MLKEM);
+    }
+
+    pCompTemps = SymCryptCallbackAlloc( sizeof(SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES) );
+    if( pCompTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED )
+    {
+        if( cbSrc != SYMCRYPT_MLKEM_PRIVATE_SEED_SIZE )
+        {
+            scError = SYMCRYPT_WRONG_KEY_SIZE;
+            goto cleanup;
+        }
+
+        pkMlKemkey->hasPrivateSeed = TRUE;
+        memcpy( pkMlKemkey->privateSeed, pbCurr, sizeof(pkMlKemkey->privateSeed) );
+        pbCurr += sizeof(pkMlKemkey->privateSeed);
+
+        pkMlKemkey->hasPrivateKey = TRUE;
+        memcpy( pkMlKemkey->privateRandom, pbCurr, sizeof(pkMlKemkey->privateRandom) );
+        pbCurr += sizeof(pkMlKemkey->privateRandom);
+
+        SymCryptMlKemkeyExpandFromPrivateSeed( pkMlKemkey, pCompTemps );
+    }
+    else if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY )
+    {
+        if( cbSrc != SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY( nRows ) )
+        {
+            scError = SYMCRYPT_WRONG_KEY_SIZE;
+            goto cleanup;
+        }
+
+        // decode s
+        scError = SymCryptMlKemVectorDecodeAndDecompress( pbCurr, cbEncodedVector, 12, pkMlKemkey->pvs );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+        pbCurr += cbEncodedVector;
+
+        // copy t and decode t
+        memcpy( pkMlKemkey->encodedT, pbCurr, cbEncodedVector );
+        pbCurr += cbEncodedVector;
+        scError = SymCryptMlKemVectorDecodeAndDecompress( pkMlKemkey->encodedT, cbEncodedVector, 12, pkMlKemkey->pvt );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // copy public seed and expand public matrix
+        memcpy( pkMlKemkey->publicSeed, pbCurr, sizeof(pkMlKemkey->publicSeed) );
+        pbCurr += sizeof(pkMlKemkey->publicSeed);
+        SymCryptMlKemkeyExpandPublicMatrixFromPublicSeed( pkMlKemkey, pCompTemps );
+
+        // transpose A
+        SymCryptMlKemMatrixTranspose( pkMlKemkey->pmAtranspose );
+
+        // compute hash of encapsulation key blob
+        SymCryptMlKemkeyComputeEncapsulationKeyHash( pkMlKemkey, pCompTemps, cbEncodedVector );
+
+        // check hash of encapsulation key matches hash in the provided blob
+        if( !SymCryptEqual( pbCurr, pkMlKemkey->encapsKeyHash, sizeof(pkMlKemkey->encapsKeyHash) ) )
+        {
+            scError = SYMCRYPT_INVALID_BLOB;
+            goto cleanup;
+        }
+        pbCurr += sizeof(pkMlKemkey->encapsKeyHash);
+
+        // copy private random
+        memcpy( pkMlKemkey->privateRandom, pbCurr, sizeof(pkMlKemkey->privateRandom) );
+        pbCurr += sizeof(pkMlKemkey->privateRandom);
+
+        pkMlKemkey->hasPrivateSeed = FALSE;
+        pkMlKemkey->hasPrivateKey  = TRUE;
+    }
+    else if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY )
+    {
+        if( cbSrc != SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY( nRows ) )
+        {
+            scError = SYMCRYPT_WRONG_KEY_SIZE;
+            goto cleanup;
+        }
+
+        // copy t and decode t
+        memcpy( pkMlKemkey->encodedT, pbCurr, cbEncodedVector );
+        pbCurr += cbEncodedVector;
+        scError = SymCryptMlKemVectorDecodeAndDecompress( pkMlKemkey->encodedT, cbEncodedVector, 12, pkMlKemkey->pvt );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // copy public seed and expand public matrix
+        memcpy( pkMlKemkey->publicSeed, pbCurr, sizeof(pkMlKemkey->publicSeed) );
+        pbCurr += sizeof(pkMlKemkey->publicSeed);
+        SymCryptMlKemkeyExpandPublicMatrixFromPublicSeed( pkMlKemkey, pCompTemps );
+
+        // transpose A
+        SymCryptMlKemMatrixTranspose( pkMlKemkey->pmAtranspose );
+
+        // precompute hash of encapsulation key blob
+        SymCryptMlKemkeyComputeEncapsulationKeyHash( pkMlKemkey, pCompTemps, cbEncodedVector );
+
+        pkMlKemkey->hasPrivateSeed = FALSE;
+        pkMlKemkey->hasPrivateKey  = FALSE;
+    }
+    else
+    {
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT( pbCurr == pbSrc + cbSrc );
+
+cleanup:
+    if( pCompTemps != NULL )
+    {
+        SymCryptWipe( pCompTemps, sizeof(*pCompTemps) );
+        SymCryptCallbackFree( pCompTemps );
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeyGetValue(
+    _In_                        PCSYMCRYPT_MLKEMKEY         pkMlKemkey,
+    _Out_writes_bytes_( cbDst ) PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+                                SYMCRYPT_MLKEMKEY_FORMAT    mlKemkeyFormat,
+                                UINT32                      flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE pbCurr = pbDst;
+    const UINT32 nRows = pkMlKemkey->params.nRows;
+    const SIZE_T cbEncodedVector = SYMCRYPT_MLKEM_SIZEOF_ENCODED_UNCOMPRESSED_VECTOR( nRows );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_NULL )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED )
+    {
+        if( cbDst != SYMCRYPT_MLKEM_PRIVATE_SEED_SIZE )
+        {
+            scError = SYMCRYPT_WRONG_KEY_SIZE;
+            goto cleanup;
+        }
+
+        if( !pkMlKemkey->hasPrivateSeed )
+        {
+            scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+            goto cleanup;
+        }
+
+        memcpy( pbCurr, pkMlKemkey->privateSeed, sizeof(pkMlKemkey->privateSeed) );
+        pbCurr += sizeof(pkMlKemkey->privateSeed);
+
+        memcpy( pbCurr, pkMlKemkey->privateRandom, sizeof(pkMlKemkey->privateRandom) );
+        pbCurr += sizeof(pkMlKemkey->privateRandom);
+    }
+    else if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_DECAPSULATION_KEY )
+    {
+        if( cbDst != SYMCRYPT_MLKEM_SIZEOF_FORMAT_DECAPSULATION_KEY( nRows ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        if( !pkMlKemkey->hasPrivateKey )
+        {
+            scError = SYMCRYPT_INCOMPATIBLE_FORMAT;
+            goto cleanup;
+        }
+
+        // We don't precompute byte-encoding of private key as exporting decapsulation key is not a critical path operation
+        // All other fields are kept in memory
+        SymCryptMlKemVectorCompressAndEncode( pkMlKemkey->pvs, 12, pbCurr, cbEncodedVector );
+        pbCurr += cbEncodedVector;
+
+        memcpy( pbCurr, pkMlKemkey->encodedT, cbEncodedVector );
+        pbCurr += cbEncodedVector;
+
+        memcpy( pbCurr, pkMlKemkey->publicSeed, sizeof(pkMlKemkey->publicSeed) );
+        pbCurr += sizeof(pkMlKemkey->publicSeed);
+
+        memcpy( pbCurr, pkMlKemkey->encapsKeyHash, sizeof(pkMlKemkey->encapsKeyHash) );
+        pbCurr += sizeof(pkMlKemkey->encapsKeyHash);
+
+        memcpy( pbCurr, pkMlKemkey->privateRandom, sizeof(pkMlKemkey->privateRandom) );
+        pbCurr += sizeof(pkMlKemkey->privateRandom);
+    }
+    else if( mlKemkeyFormat == SYMCRYPT_MLKEMKEY_FORMAT_ENCAPSULATION_KEY )
+    {
+        if( cbDst != SYMCRYPT_MLKEM_SIZEOF_FORMAT_ENCAPSULATION_KEY( nRows ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        memcpy( pbCurr, pkMlKemkey->encodedT, cbEncodedVector );
+        pbCurr += cbEncodedVector;
+
+        memcpy( pbCurr, pkMlKemkey->publicSeed, sizeof(pkMlKemkey->publicSeed) );
+        pbCurr += sizeof(pkMlKemkey->publicSeed);
+    }
+    else
+    {
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    SYMCRYPT_ASSERT( pbCurr == pbDst + cbDst );
+
+cleanup:
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemkeyGenerate(
+    _Inout_                     PSYMCRYPT_MLKEMKEY  pkMlKemkey,
+                                UINT32              flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BYTE privateSeed[SYMCRYPT_MLKEM_PRIVATE_SEED_SIZE];
+    PBYTE  pbPctCipherText = NULL;
+    SIZE_T cbPctCipherText = 0;
+
+    // Ensure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS;
+
+    if ( ( flags & ~allowedFlags ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptCallbackRandom( privateSeed, sizeof(privateSeed) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlKemkeySetValue( privateSeed, sizeof(privateSeed), SYMCRYPT_MLKEMKEY_FORMAT_PRIVATE_SEED, flags, pkMlKemkey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // SymCryptMlKemkeySetValue ensures the self-test is run before
+    // first operational use of MlKem
+
+    if( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // PCT on key generation, encaps/decaps and check that both parties get the same shared secret with the generated key
+        SIZE_T cbU, cbV;
+        const UINT32 nRows = pkMlKemkey->params.nRows;
+        const UINT32 nBitsOfU = pkMlKemkey->params.nBitsOfU;
+        const UINT32 nBitsOfV = pkMlKemkey->params.nBitsOfV;
+
+        // u vector encoded with nBitsOfU * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits per polynomial
+        cbU = nRows * nBitsOfU * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+        // v polynomial encoded with nBitsOfV * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits
+        cbV = nBitsOfV * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+        cbPctCipherText = cbU + cbV;
+
+        pbPctCipherText = SymCryptCallbackAlloc( cbPctCipherText );
+        if( pbPctCipherText == NULL )
+        {
+            scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+            goto cleanup;
+        }
+
+        C_ASSERT( SYMCRYPT_MLKEM_PRIVATE_SEED_SIZE >= 2*SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET );
+
+        // reuse bytes 0..31 of privateSeed buffer for encapsulation shared secret
+        scError = SymCryptMlKemEncapsulate(
+            pkMlKemkey,
+            &privateSeed[0], SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET,
+            pbPctCipherText, cbPctCipherText );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        // reuse second 32..63 bytes of privateSeed buffer for encapsulation shared secret
+        scError = SymCryptMlKemDecapsulate(
+            pkMlKemkey,
+            pbPctCipherText, cbPctCipherText,
+            &privateSeed[SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET], SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        if( !SymCryptEqual( &privateSeed[0], &privateSeed[SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET], SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET ) )
+        {
+            // Do not fatal on PCT failure here, as it is expected with very low probability that
+            // with correct keygen and encaps/decaps, the agreed secrets do not match
+            scError = SYMCRYPT_FIPS_FAILURE;
+            goto cleanup;
+        }
+
+        // could track having run the PCT with a flag in pkMlKemkey->fAlgorithmInfo,
+        // but currently no need to do that given we don't ever defer the PCT
+    }
+
+cleanup:
+    if( pbPctCipherText != NULL )
+    {
+        // Wiping is not required for security, but has low relative cost
+        // and better to be on the safe side for FIPS
+        SymCryptWipe( pbPctCipherText, cbPctCipherText );
+        SymCryptCallbackFree( pbPctCipherText );
+    }
+
+    SymCryptWipeKnownSize( privateSeed, sizeof(privateSeed) );
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemEncapsulateInternal(
+    _In_    PCSYMCRYPT_MLKEMKEY                                 pkMlKemkey,
+    _Out_writes_bytes_( cbAgreedSecret )
+            PBYTE                                               pbAgreedSecret,
+            SIZE_T                                              cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )
+            PBYTE                                               pbCiphertext,
+            SIZE_T                                              cbCiphertext,
+    _In_reads_bytes_( SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM )
+            PCBYTE                                              pbRandom,
+    _Inout_ PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES    pCompTemps )
+{
+    BYTE CBDSampleBuffer[3*64 + 1];
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PSYMCRYPT_MLKEM_VECTOR pvrInner;
+    PSYMCRYPT_MLKEM_VECTOR pvTmp;
+    PSYMCRYPT_MLKEM_POLYELEMENT peTmp0, peTmp1;
+    PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp;
+    PSYMCRYPT_SHA3_512_STATE pHashState = &pCompTemps->hashState0.sha3_512State;
+    PSYMCRYPT_SHAKE256_STATE pShakeBaseState = &pCompTemps->hashState0.shake256State;
+    PSYMCRYPT_SHAKE256_STATE pShakeWorkState = &pCompTemps->hashState1.shake256State;
+    SIZE_T cbU, cbV;
+    UINT32 i;
+    const UINT32 nRows = pkMlKemkey->params.nRows;
+    const UINT32 nBitsOfU = pkMlKemkey->params.nBitsOfU;
+    const UINT32 nBitsOfV = pkMlKemkey->params.nBitsOfV;
+    const UINT32 nEta1 = pkMlKemkey->params.nEta1;
+    const UINT32 nEta2 = pkMlKemkey->params.nEta2;
+    const UINT32 cbPolyElement = pkMlKemkey->params.cbPolyElement;
+    const UINT32 cbVector = pkMlKemkey->params.cbVector;
+
+    // u vector encoded with nBitsOfU * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits per polynomial
+    cbU = nRows * nBitsOfU * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    // v polynomial encoded with nBitsOfV * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits
+    cbV = nBitsOfV * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+
+    if( (cbAgreedSecret != SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET) ||
+        (cbCiphertext != cbU + cbV) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pvrInner = SymCryptMlKemVectorCreate( pCompTemps->abVectorBuffer0, cbVector, nRows );
+    SYMCRYPT_ASSERT( pvrInner != NULL );
+    pvTmp = SymCryptMlKemVectorCreate( pCompTemps->abVectorBuffer1, cbVector, nRows );
+    SYMCRYPT_ASSERT( pvTmp != NULL );
+    peTmp0 = SymCryptMlKemPolyElementCreate( pCompTemps->abPolyElementBuffer0, cbPolyElement );
+    SYMCRYPT_ASSERT( peTmp0 != NULL );
+    peTmp1 = SymCryptMlKemPolyElementCreate( pCompTemps->abPolyElementBuffer1, cbPolyElement );
+    SYMCRYPT_ASSERT( peTmp1 != NULL );
+    paTmp = SymCryptMlKemPolyElementAccumulatorCreate( pCompTemps->abPolyElementAccumulatorBuffer, 2*cbPolyElement );
+    SYMCRYPT_ASSERT( paTmp != NULL );
+
+    // CBDSampleBuffer = (K || rOuter) = SHA3-512(pbRandom || encapsKeyHash)
+    SymCryptSha3_512Init( pHashState );
+    SymCryptSha3_512Append( pHashState, pbRandom, SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM );
+    SymCryptSha3_512Append( pHashState, pkMlKemkey->encapsKeyHash, sizeof(pkMlKemkey->encapsKeyHash) );
+    SymCryptSha3_512Result( pHashState, CBDSampleBuffer );
+
+    // Write K to pbAgreedSecret
+    memcpy( pbAgreedSecret, CBDSampleBuffer, SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET );
+
+    // Initialize pShakeStateBase with rOuter
+    SymCryptShake256Init( pShakeBaseState );
+    SymCryptShake256Append( pShakeBaseState, CBDSampleBuffer+cbAgreedSecret, 32 );
+
+    // Expand rInner vector
+    for( i=0; i<nRows; i++ )
+    {
+        CBDSampleBuffer[0] = (BYTE) i;
+        SymCryptShake256StateCopy( pShakeBaseState, pShakeWorkState );
+        SymCryptShake256Append( pShakeWorkState, CBDSampleBuffer, 1 );
+
+        SymCryptShake256Extract( pShakeWorkState, CBDSampleBuffer, 64ul*nEta1, FALSE );
+
+        SymCryptMlKemPolyElementSampleCBDFromBytes( CBDSampleBuffer, nEta1, SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT(i, pvrInner) );
+    }
+
+    // Perform NTT on rInner
+    SymCryptMlKemVectorNTT( pvrInner );
+
+    // Set pvTmp to 0
+    SymCryptMlKemVectorSetZero( pvTmp );
+
+    // pvTmp = (Atranspose o rInner) ./ R
+    SymCryptMlKemMatrixVectorMontMulAndAdd( pkMlKemkey->pmAtranspose, pvrInner, pvTmp, paTmp );
+
+    // pvTmp = INTT(Atranspose o rInner)
+    SymCryptMlKemVectorINTTAndMulR( pvTmp );
+
+    // Expand e1 and add it to pvTmp - do addition PolyElement-wise to reduce memory usage
+    for( i=0; i<nRows; i++ )
+    {
+        CBDSampleBuffer[0] = (BYTE) (nRows+i);
+        SymCryptShake256StateCopy( pShakeBaseState, pShakeWorkState );
+        SymCryptShake256Append( pShakeWorkState, CBDSampleBuffer, 1 );
+
+        SymCryptShake256Extract( pShakeWorkState, CBDSampleBuffer, 64ul*nEta2, FALSE );
+
+        SymCryptMlKemPolyElementSampleCBDFromBytes( CBDSampleBuffer, nEta2, peTmp0 );
+
+        SymCryptMlKemPolyElementAdd( SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT(i, pvTmp), peTmp0, SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT(i, pvTmp) );
+    }
+
+    // pvTmp = u = INTT(Atranspose o rInner) + e1
+    // Compress and encode u into prefix of ciphertext
+    SymCryptMlKemVectorCompressAndEncode( pvTmp, nBitsOfU, pbCiphertext, cbU );
+
+    // peTmp0 = (t o r) ./ R
+    SymCryptMlKemVectorMontDotProduct( pkMlKemkey->pvt, pvrInner, peTmp0, paTmp );
+
+    // peTmp0 = INTT(t o r)
+    SymCryptMlKemPolyElementINTTAndMulR( peTmp0 );
+
+    // Expand e2 polynomial in peTmp1
+    CBDSampleBuffer[0] = (BYTE) (2*nRows);
+    SymCryptShake256StateCopy( pShakeBaseState, pShakeWorkState );
+    SymCryptShake256Append( pShakeWorkState, CBDSampleBuffer, 1 );
+
+    SymCryptShake256Extract( pShakeWorkState, CBDSampleBuffer, 64ul*nEta2, FALSE );
+
+    SymCryptMlKemPolyElementSampleCBDFromBytes( CBDSampleBuffer, nEta2, peTmp1 );
+
+    // peTmp = INTT(t o r) + e2
+    SymCryptMlKemPolyElementAdd( peTmp0, peTmp1, peTmp0 );
+
+    // peTmp1 = mu
+    SymCryptMlKemPolyElementDecodeAndDecompress( pbRandom, 1, peTmp1 );
+
+    // peTmp0 = v = INTT(t o r) + e2 + mu
+    SymCryptMlKemPolyElementAdd( peTmp0, peTmp1, peTmp0 );
+
+    // Compress and encode v into remainder of ciphertext
+    SymCryptMlKemPolyElementCompressAndEncode( peTmp0, nBitsOfV, pbCiphertext+cbU );
+
+cleanup:
+    SymCryptWipeKnownSize( CBDSampleBuffer, sizeof(CBDSampleBuffer) );
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemEncapsulateEx(
+    _In_                                    PCSYMCRYPT_MLKEMKEY pkMlKemkey,
+    _In_reads_bytes_( cbRandom )            PCBYTE              pbRandom,
+                                            SIZE_T              cbRandom,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE               pbAgreedSecret,
+                                            SIZE_T              cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )      PBYTE               pbCiphertext,
+                                            SIZE_T              cbCiphertext )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES pCompTemps = NULL;
+
+    if( cbRandom != SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pCompTemps = SymCryptCallbackAlloc( sizeof(SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES) );
+    if( pCompTemps == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    scError = SymCryptMlKemEncapsulateInternal(
+        pkMlKemkey,
+        pbAgreedSecret, cbAgreedSecret,
+        pbCiphertext, cbCiphertext,
+        pbRandom,
+        pCompTemps );
+
+cleanup:
+    if( pCompTemps != NULL )
+    {
+        SymCryptWipe( pCompTemps, sizeof(*pCompTemps) );
+        SymCryptCallbackFree( pCompTemps );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemEncapsulate(
+    _In_                                    PCSYMCRYPT_MLKEMKEY pkMlKemkey,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE               pbAgreedSecret,
+                                            SIZE_T              cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )      PBYTE               pbCiphertext,
+                                            SIZE_T              cbCiphertext )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BYTE pbm[SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM];
+
+    scError = SymCryptCallbackRandom( pbm, sizeof(pbm) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptMlKemEncapsulateEx(
+        pkMlKemkey,
+        pbm, sizeof(pbm),
+        pbAgreedSecret, cbAgreedSecret,
+        pbCiphertext, cbCiphertext );
+
+cleanup:
+    SymCryptWipeKnownSize( pbm, sizeof(pbm) );
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemDecapsulate(
+    _In_                                    PCSYMCRYPT_MLKEMKEY pkMlKemkey,
+    _In_reads_bytes_( cbCiphertext )        PCBYTE              pbCiphertext,
+                                            SIZE_T              cbCiphertext,
+    _Out_writes_bytes_( cbAgreedSecret )    PBYTE               pbAgreedSecret,
+                                            SIZE_T              cbAgreedSecret )
+{
+    PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES pCompTemps = NULL;
+    BYTE pbDecryptedRandom[SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM];
+    BYTE pbDecapsulatedSecret[SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET];
+    BYTE pbImplicitRejectionSecret[SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET];
+    PBYTE pbReadCiphertext, pbReencapsulatedCiphertext;
+    BOOLEAN successfulReencrypt;
+
+    PBYTE pbCurr;
+    PBYTE pbAlloc = NULL;
+    const SIZE_T cbAlloc = sizeof(SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES) + (2*cbCiphertext);
+
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SIZE_T cbU, cbV, cbCopy;
+    PSYMCRYPT_MLKEM_VECTOR pvu;
+    PSYMCRYPT_MLKEM_POLYELEMENT peTmp0, peTmp1;
+    PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp;
+    PSYMCRYPT_SHAKE256_STATE pShakeState;
+    const UINT32 nRows = pkMlKemkey->params.nRows;
+    const UINT32 nBitsOfU = pkMlKemkey->params.nBitsOfU;
+    const UINT32 nBitsOfV = pkMlKemkey->params.nBitsOfV;
+    const UINT32 cbPolyElement = pkMlKemkey->params.cbPolyElement;
+    const UINT32 cbVector = pkMlKemkey->params.cbVector;
+
+    // u vector encoded with nBitsOfU * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits per polynomial
+    cbU = nRows * nBitsOfU * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    // v polynomial encoded with nBitsOfV * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS bits
+    cbV = nBitsOfV * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+
+    if( (cbAgreedSecret != SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET) ||
+        (cbCiphertext != cbU + cbV) ||
+        !pkMlKemkey->hasPrivateKey )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pbAlloc = SymCryptCallbackAlloc( cbAlloc );
+    if( pbAlloc == NULL )
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+    pbCurr = pbAlloc;
+
+    pCompTemps = (PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES) pbCurr;
+    pbCurr += sizeof(SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES);
+
+    pbReadCiphertext = pbCurr;
+    pbCurr += cbCiphertext;
+
+    pbReencapsulatedCiphertext = pbCurr;
+    pbCurr += cbCiphertext;
+
+    SYMCRYPT_ASSERT( pbCurr == (pbAlloc + cbAlloc) );
+
+    // Read the input ciphertext once to local pbReadCiphertext to ensure our view of ciphertext consistent
+    memcpy( pbReadCiphertext, pbCiphertext, cbCiphertext );
+
+    pvu = SymCryptMlKemVectorCreate( pCompTemps->abVectorBuffer0, cbVector, nRows );
+    SYMCRYPT_ASSERT( pvu != NULL );
+    peTmp0 = SymCryptMlKemPolyElementCreate( pCompTemps->abPolyElementBuffer0, cbPolyElement );
+    SYMCRYPT_ASSERT( peTmp0 != NULL );
+    peTmp1 = SymCryptMlKemPolyElementCreate( pCompTemps->abPolyElementBuffer1, cbPolyElement );
+    SYMCRYPT_ASSERT( peTmp1 != NULL );
+    paTmp = SymCryptMlKemPolyElementAccumulatorCreate( pCompTemps->abPolyElementAccumulatorBuffer, 2*cbPolyElement );
+    SYMCRYPT_ASSERT( paTmp != NULL );
+
+    // Decode and decompress u
+    scError = SymCryptMlKemVectorDecodeAndDecompress( pbReadCiphertext, cbU, nBitsOfU, pvu );
+    SYMCRYPT_ASSERT( scError == SYMCRYPT_NO_ERROR );
+
+    // Perform NTT on u
+    SymCryptMlKemVectorNTT( pvu );
+
+    // peTmp0 = (s o NTT(u)) ./ R
+    SymCryptMlKemVectorMontDotProduct( pkMlKemkey->pvs, pvu, peTmp0, paTmp );
+
+    // peTmp0 = INTT(s o NTT(u))
+    SymCryptMlKemPolyElementINTTAndMulR( peTmp0 );
+
+    // Decode and decompress v
+    scError = SymCryptMlKemPolyElementDecodeAndDecompress( pbReadCiphertext+cbU, nBitsOfV, peTmp1 );
+    SYMCRYPT_ASSERT( scError == SYMCRYPT_NO_ERROR );
+
+    // peTmp0 = w = v - INTT(s o NTT(u))
+    SymCryptMlKemPolyElementSub( peTmp1, peTmp0, peTmp0 );
+
+    // pbDecryptedRandom = m' = Encoding of w
+    SymCryptMlKemPolyElementCompressAndEncode( peTmp0, 1, pbDecryptedRandom );
+
+    // Compute:
+    //  pbDecapsulatedSecret = K' = Decapsulated secret (without implicit rejection)
+    //  pbReencapsulatedCiphertext = c' = Ciphertext from re-encapsulating decrypted random value
+    scError = SymCryptMlKemEncapsulateInternal(
+        pkMlKemkey,
+        pbDecapsulatedSecret, sizeof(pbDecapsulatedSecret),
+        pbReencapsulatedCiphertext, cbCiphertext,
+        pbDecryptedRandom,
+        pCompTemps );
+    SYMCRYPT_ASSERT( scError == SYMCRYPT_NO_ERROR );
+
+    // Compute the secret we will return if using implicit rejection
+    // pbImplicitRejectionSecret = K_bar = SHAKE256( z || c )
+    pShakeState = &pCompTemps->hashState0.shake256State;
+    SymCryptShake256Init( pShakeState );
+    SymCryptShake256Append( pShakeState, pkMlKemkey->privateRandom, sizeof(pkMlKemkey->privateRandom) );
+    SymCryptShake256Append( pShakeState, pbReadCiphertext, cbCiphertext );
+    SymCryptShake256Extract( pShakeState, pbImplicitRejectionSecret, sizeof(pbImplicitRejectionSecret), FALSE );
+
+    // Constant time test if re-encryption successful
+    successfulReencrypt = SymCryptEqual( pbReencapsulatedCiphertext, pbReadCiphertext, cbCiphertext );
+
+    // If not successful, perform side-channel-safe copy of Implicit Rejection secret over Decapsulated secret
+    cbCopy = (((SIZE_T)successfulReencrypt)-1) & SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET;
+    SymCryptScsCopy( pbImplicitRejectionSecret, cbCopy, pbDecapsulatedSecret, SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET );
+
+    // Write agreed secret (with implicit rejection) to pbAgreedSecret
+    memcpy( pbAgreedSecret, pbDecapsulatedSecret, SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET );
+
+cleanup:
+    if( pbAlloc != NULL )
+    {
+        SymCryptWipe( pbAlloc, cbAlloc );
+        SymCryptCallbackFree( pbAlloc );
+    }
+
+    SymCryptWipeKnownSize( pbDecryptedRandom, sizeof(pbDecryptedRandom) );
+    SymCryptWipeKnownSize( pbDecapsulatedSecret, sizeof(pbDecapsulatedSecret) );
+    SymCryptWipeKnownSize( pbImplicitRejectionSecret, sizeof(pbImplicitRejectionSecret) );
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/mlkem_primitives.c b/libs/symcrypt/lib/mlkem_primitives.c
new file mode 100644
index 000000000000..ab8a6e86c82d
--- /dev/null
+++ b/libs/symcrypt/lib/mlkem_primitives.c
@@ -0,0 +1,1442 @@
+//
+// mlkem_primitives.c   ML-KEM related functionality
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// Current approach is to represent polynomial ring elements as a 512-byte buffer (256 UINT16s).
+//
+
+// Coefficients are added and subtracted when polynomials are in the NTT domain and in the lattice domain.
+//
+// Coefficients are only multiplied in the NTT/INTT operations, and in MulAdd which only operates on
+// polynomials in NTT form.
+// We choose to perform modular multiplication exclusively using Montgomery multiplication, that is, we choose
+// a Montgomery divisor R, and modular multiplication always divides by R, as this make reduction logic easy
+// and quick.
+// i.e. MontMul(a,b) -> ((a*b) / R) mod Q
+//
+// For powers of Zeta used in as multiplication twiddle factors in NTT/INTT and base polynomial multiplication,
+// we pre-multiply the constants by R s.t.
+//  MontMul(x, twiddleForZetaToTheK) -> x*(Zeta^K) mod Q.
+//
+// Most other modular multiplication can be done with a fixup deferred until the INTT. The one exception is in key
+// generation, where A o s + e = t, we need to pre-multiply s'
+
+// R = 2^16
+const UINT32 SYMCRYPT_MLKEM_Rlog2 = 16;
+const UINT32 SYMCRYPT_MLKEM_Rmask = 0xffff;
+
+// NegQInvModR = -Q^(-1) mod R
+const UINT32 SYMCRYPT_MLKEM_NegQInvModR = 3327;
+
+// Rsqr = R^2 = (1<<32) mod Q
+const UINT32 SYMCRYPT_MLKEM_Rsqr = 1353;
+// RsqrTimesNegQInvModR = R^2 = ((1<<32) mod Q) * -Q^(-1) mod R
+const UINT32 SYMCRYPT_MLKEM_RsqrTimesNegQInvModR = 44983;
+
+//
+// Zeta tables.
+// Zeta = 17, which is a primitive 256-th root of unity modulo Q
+//
+// In ML-KEM we use powers of zeta to convert to and from NTT form
+// and to perform multiplication between polynomials in NTT form
+//
+
+// This table is a lookup for (Zeta^(BitRev(index)) * R) mod Q
+// Used in NTT and INTT
+// i.e. element 1 is Zeta^(BitRev(1)) * (2^16) mod Q == (17^64)*(2^16) mod 3329 == 2571
+//
+// MlKemZetaBitRevTimesR = [ (pow(17, bitRev(i), 3329) << 16) % 3329 for i in range(128) ]
+const UINT16 MlKemZetaBitRevTimesR[128] =
+{
+    2285, 2571, 2970, 1812, 1493, 1422,  287,  202,
+    3158,  622, 1577,  182,  962, 2127, 1855, 1468,
+     573, 2004,  264,  383, 2500, 1458, 1727, 3199,
+    2648, 1017,  732,  608, 1787,  411, 3124, 1758,
+    1223,  652, 2777, 1015, 2036, 1491, 3047, 1785,
+     516, 3321, 3009, 2663, 1711, 2167,  126, 1469,
+    2476, 3239, 3058,  830,  107, 1908, 3082, 2378,
+    2931,  961, 1821, 2604,  448, 2264,  677, 2054,
+    2226,  430,  555,  843, 2078,  871, 1550,  105,
+     422,  587,  177, 3094, 3038, 2869, 1574, 1653,
+    3083,  778, 1159, 3182, 2552, 1483, 2727, 1119,
+    1739,  644, 2457,  349,  418,  329, 3173, 3254,
+     817, 1097,  603,  610, 1322, 2044, 1864,  384,
+    2114, 3193, 1218, 1994, 2455,  220, 2142, 1670,
+    2144, 1799, 2051,  794, 1819, 2475, 2459,  478,
+    3221, 3021,  996,  991,  958, 1869, 1522, 1628,
+};
+
+// This table is a lookup for ((Zeta^(BitRev(index)) * R) mod Q) * -Q^(-1) mod R
+// Used in NTT and INTT
+//
+// MlKemZetaBitRevTimesRTimesNegQInvModR = [ (((pow(17, bitRev(i), Q) << 16) % Q) * 3327) & 0xffff for i in range(128) ]
+const UINT16 MlKemZetaBitRevTimesRTimesNegQInvModR[128] =
+{
+       19, 34037, 50790, 64748, 52011, 12402, 37345, 16694,
+    20906, 37778,  3799, 15690, 54846, 64177, 11201, 34372,
+     5827, 48172, 26360, 29057, 59964,  1102, 44097, 26241,
+    28072, 41223, 10532, 56736, 47109, 56677, 38860, 16162,
+     5689,  6516, 64039, 34569, 23564, 45357, 44825, 40455,
+    12796, 38919, 49471, 12441, 56401,   649, 25986, 37699,
+    45652, 28249, 15886,  8898, 28309, 56460, 30198, 47286,
+    52109, 51519, 29155, 12756, 48704, 61224, 24155, 17914,
+      334, 54354, 11477, 52149, 32226, 14233, 45042, 21655,
+    27738, 52405, 64591,  4586, 14882, 42443, 59354, 60043,
+    33525, 32502, 54905, 35218, 36360, 18741, 28761, 52897,
+    18485, 45436, 47975, 47011, 14430, 46007,  5275, 12618,
+    31183, 45239, 40101, 63390,  7382, 50180, 41144, 32384,
+    20926,  6279, 54590, 14902, 41321, 11044, 48546, 51066,
+    55200, 21497,  7933, 20198, 22501, 42325, 54629, 17442,
+    33899, 23859, 36892, 20257, 41538, 57779, 17422, 42404,
+};
+
+// This table is a lookup for ((Zeta^(2*BitRev(index) + 1) * R) mod Q)
+// Used in multiplication of 2 NTT-form polynomials
+//
+// zetaTwoTimesBitRevPlus1TimesR =  [ (pow(17, 2*bitRev(i)+1, 3329) << 16) % 3329 for i in range(128) ]
+const UINT16 zetaTwoTimesBitRevPlus1TimesR[128] =
+{
+    2226, 1103,  430, 2899,  555, 2774,  843, 2486,
+    2078, 1251,  871, 2458, 1550, 1779,  105, 3224,
+     422, 2907,  587, 2742,  177, 3152, 3094,  235,
+    3038,  291, 2869,  460, 1574, 1755, 1653, 1676,
+    3083,  246,  778, 2551, 1159, 2170, 3182,  147,
+    2552,  777, 1483, 1846, 2727,  602, 1119, 2210,
+    1739, 1590,  644, 2685, 2457,  872,  349, 2980,
+     418, 2911,  329, 3000, 3173,  156, 3254,   75,
+     817, 2512, 1097, 2232,  603, 2726,  610, 2719,
+    1322, 2007, 2044, 1285, 1864, 1465,  384, 2945,
+    2114, 1215, 3193,  136, 1218, 2111, 1994, 1335,
+    2455,  874,  220, 3109, 2142, 1187, 1670, 1659,
+    2144, 1185, 1799, 1530, 2051, 1278,  794, 2535,
+    1819, 1510, 2475,  854, 2459,  870,  478, 2851,
+    3221,  108, 3021,  308,  996, 2333,  991, 2338,
+     958, 2371, 1869, 1460, 1522, 1807, 1628, 1701,
+};
+
+PSYMCRYPT_MLKEM_POLYELEMENT
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer )
+{
+    PSYMCRYPT_MLKEM_POLYELEMENT pDst = (PSYMCRYPT_MLKEM_POLYELEMENT) pbBuffer;
+
+    UNREFERENCED_PARAMETER( cbBuffer );
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+    SYMCRYPT_ASSERT( cbBuffer == SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT );
+
+    return pDst;
+}
+
+PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementAccumulatorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer )
+{
+    PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR pDst = (PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR) pbBuffer;
+
+    UNREFERENCED_PARAMETER( cbBuffer );
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+    SYMCRYPT_ASSERT( cbBuffer == SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT_ACCUMULATOR );
+
+    return pDst;
+}
+
+PSYMCRYPT_MLKEM_VECTOR
+SYMCRYPT_CALL
+SymCryptMlKemVectorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer,
+                                    UINT32  nRows )
+{
+    PSYMCRYPT_MLKEM_VECTOR pDst = NULL;
+    PSYMCRYPT_MLKEM_VECTOR pVector = (PSYMCRYPT_MLKEM_VECTOR)pbBuffer;
+    PSYMCRYPT_MLKEM_POLYELEMENT peTmp = NULL;
+    UINT32 i;
+    PBYTE pbTmp = pbBuffer + sizeof(SYMCRYPT_MLKEM_VECTOR);
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    SYMCRYPT_ASSERT( nRows > 0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+
+    pVector->nRows = nRows;
+    pVector->cbTotalSize = cbBuffer;
+
+    for( i=0; i<nRows; i++ )
+    {
+        peTmp = SymCryptMlKemPolyElementCreate( pbTmp, SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT );
+        if( peTmp == NULL )
+        {
+            goto cleanup;
+        }
+
+        pbTmp += SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT;
+    }
+
+    SYMCRYPT_ASSERT( pbTmp == (pbBuffer + cbBuffer) );
+
+    pDst = pVector;
+
+cleanup:
+    return pDst;
+}
+
+PSYMCRYPT_MLKEM_MATRIX
+SYMCRYPT_CALL
+SymCryptMlKemMatrixCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer,
+                                    UINT32  nRows )
+{
+    PSYMCRYPT_MLKEM_MATRIX pDst = NULL;
+    PSYMCRYPT_MLKEM_MATRIX pMatrix = (PSYMCRYPT_MLKEM_MATRIX)pbBuffer;
+    UINT32 i;
+    PBYTE pbTmp = pbBuffer + sizeof(SYMCRYPT_MLKEM_MATRIX);
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
+
+    SYMCRYPT_ASSERT( nRows > 0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+
+    pMatrix->nRows = nRows;
+    pMatrix->cbTotalSize = cbBuffer;
+
+    for( i=0; i<(nRows*nRows); i++ )
+    {
+        pMatrix->apPolyElements[i] = SymCryptMlKemPolyElementCreate( pbTmp, SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT );
+        if( pMatrix->apPolyElements[i] == NULL )
+        {
+            goto cleanup;
+        }
+
+        pbTmp += SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT;
+    }
+
+    SYMCRYPT_ASSERT( pbTmp == (pbBuffer + cbBuffer) );
+
+    pDst = pMatrix;
+
+cleanup:
+    return pDst;
+}
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("sse2"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("sse2")
+#endif
+
+#define VEC128_TYPE_UINT16 __m128i
+
+#define VEC128_LOAD_UINT16( addr )       _mm_loadu_si128( (__m128i*) (addr) )
+#define VEC64_LOAD_UINT16( addr )        _mm_loadu_si64( (PBYTE) (addr) )
+#define VEC32_LOAD_UINT16( addr )        _mm_cvtsi32_si128( SYMCRYPT_LOAD_LSBFIRST32( addr ) )
+
+#define VEC128_STORE_UINT16( addr, vec ) _mm_storeu_si128( (__m128i*) (addr), (vec) )
+#define VEC64_STORE_UINT16( addr, vec )  _mm_storeu_si64( (PBYTE) (addr), (vec) )
+#define VEC32_STORE_UINT16( addr, vec )  SYMCRYPT_STORE_LSBFIRST32( (addr), _mm_cvtsi128_si32( vec ) )
+
+#define VEC128_SET_UINT16( value )       _mm_set1_epi16( (value) )
+
+#define VEC128_MOD_SUB_UINT16( res, a, b, Q, zero, tmp1 ) \
+    /* res = a - b */ \
+    res = _mm_sub_epi16( a, b ); \
+    /* tmp1 = (a - b) < 0 ? -1 : 0 */ \
+    tmp1 = _mm_cmpgt_epi16( zero, res ); \
+    /* tmp1 = (a - b) < 0 ? Q : 0 */ \
+    tmp1 = _mm_and_si128( tmp1, Q ); \
+    /* res = (a - b) mod Q */ \
+    res = _mm_add_epi16( res, tmp1 );
+
+#define VEC128_MOD_ADD_UINT16( res, a, b, Q, tmp1 ) \
+    /* res = a + b */ \
+    res = _mm_add_epi16( a, b ); \
+    /* tmp1 = (a + b) < Q ? -1 : 0 */ \
+    tmp1 = _mm_cmpgt_epi16( Q, res ); \
+    /* tmp1 = (a + b) < Q ? 0 : Q */ \
+    tmp1 = _mm_andnot_si128( tmp1, Q ); \
+    /* res = (a + b) mod Q */ \
+    res = _mm_sub_epi16( res, tmp1 );
+
+#define VEC128_MONTGOMERY_MUL_UINT16( res, a, b, bTimesNegQInvModR, Q, zero, one, tmp1, tmp2 ) \
+    /* tmp1 = a *low  bTimesNegQInvModR */ \
+    tmp1 = _mm_mullo_epi16( a, bTimesNegQInvModR ); \
+    /* res  = a *high b */ \
+    res = _mm_mulhi_epu16( a, b ); \
+    /* tmp2 = (tmp1 == 0) ? -1 : 0 */ \
+    tmp2 = _mm_cmpeq_epi16( tmp1, zero ); \
+    /* tmp1 = (a *low bTimesNegQInvModR) *high Q */ \
+    tmp1 = _mm_mulhi_epu16( tmp1, Q ); \
+    /* res = a *high b + 1 */ \
+    res = _mm_add_epi16( res, one ); \
+    /* res  = a *high b (+ 1 if a != 0) */ \
+    res = _mm_add_epi16( res, tmp2 ); \
+    /* res  = a *high b + inv*Q (+ 1 if a != 0) */ \
+    res = _mm_add_epi16( res, tmp1 ); \
+    /* res  = (a*b + inv*Q >> 16) mod Q */ \
+    VEC128_MOD_SUB_UINT16( res, res, Q, Q, zero, tmp1 );
+
+#elif SYMCRYPT_CPU_ARM64
+
+#define VEC128_TYPE_UINT16 uint16x8_t
+
+#define VEC128_LOAD_UINT16( addr )       vld1q_u16( addr )
+#define VEC64_LOAD_UINT16( addr )        vld1q_dup_u64( addr )
+#define VEC32_LOAD_UINT16( addr )        vld1q_dup_u32( addr )
+
+#define VEC128_STORE_UINT16( addr, vec ) vst1q_u16( (addr), (vec) )
+#define VEC64_STORE_UINT16( addr, vec )  vst1_u16( (uint16_t*) (addr), vget_low_u16(vec) )
+#define VEC32_STORE_UINT16( addr, vec )  vst1_lane_u32( (PBYTE) (addr), vget_low_u32(vec), 0 )
+
+#define VEC128_SET_UINT16( value )       vdupq_n_u16( (value) )
+
+#define VEC128_MOD_SUB_UINT16( res, a, b, Q, zero, tmp1 ) \
+    /* res = a - b */ \
+    res = vsubq_u16( a, b ); \
+    /* tmp1 = (a - b) < 0 ? -1 : 0 */ \
+    tmp1 = vcltzq_s16( res ); \
+    /* tmp1 = (a - b) < 0 ? Q : 0 */ \
+    tmp1 = vandq_u16( tmp1, Q ); \
+    /* res = (a - b) mod Q */ \
+    res = vaddq_u16( res, tmp1 );
+
+#define VEC128_MOD_ADD_UINT16( res, a, b, Q, tmp1 ) \
+    /* res = a + b */ \
+    res = vaddq_u16( a, b ); \
+    /* tmp1 = (a + b) >= Q ? -1 : 0 */ \
+    tmp1 = vcgeq_u16( res, Q ); \
+    /* tmp1 = (a + b) >= Q ? Q : 0 */ \
+    tmp1 = vandq_u16( tmp1, Q ); \
+    /* res = (a + b) mod Q */ \
+    res = vsubq_u16( res, tmp1 );
+
+#define VEC128_MONTGOMERY_MUL_UINT16( res, a, b, bTimesNegQInvModR, Q, zero, one, tmp1, tmp2 ) \
+    /* tmp1 = a *low  bTimesNegQInvModR */ \
+    tmp1 = vmulq_u16( a, bTimesNegQInvModR ); \
+    /* tmp2 = a*b [0-3]*/ \
+    tmp2  = vmull_u16( vget_low_u16(a), vget_low_u16(b) ); \
+    /* res  = a*b [4-7]*/ \
+    res = vmull_high_u16( a, b ); \
+    /* tmp2 = a*b + inv*Q [0-3]*/ \
+    tmp2  = vmlal_u16( tmp2, vget_low_u16(tmp1), vget_low_u16(Q) ); \
+    /* res  = a*b + inv*Q [4-7]*/ \
+    res = vmlal_high_u16( res, tmp1, Q ); \
+    /* res  = a*b + inv*Q >> 16 */ \
+    res  = vuzp2q_u16( tmp2, res ); \
+    /* res  = (a*b + inv*Q >> 16) mod Q */ \
+    VEC128_MOD_SUB_UINT16( res, res, Q, Q, zero, tmp1 );
+
+#endif
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementNTTLayerVec128(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc,
+            UINT32                       k,
+            UINT32                       len )
+{
+    UINT32 start, j;
+    VEC128_TYPE_UINT16 vc0, vc1, vTmp0, vTmp1, vc1Twiddle, vTwiddleFactor, vTwiddleFactorMont, vQ, vZero, vOne;
+
+    SYMCRYPT_ASSERT( len >= 2 );
+
+    vQ = VEC128_SET_UINT16( SYMCRYPT_MLKEM_Q );
+    vZero = VEC128_SET_UINT16( 0 );
+    vOne = VEC128_SET_UINT16( 1 );
+
+    for( start=0; start<256; start+=(2*len) )
+    {
+        vTwiddleFactor     = VEC128_SET_UINT16( MlKemZetaBitRevTimesR[k] );
+        vTwiddleFactorMont = VEC128_SET_UINT16( MlKemZetaBitRevTimesRTimesNegQInvModR[k] );
+        k++;
+        for( j=0; j<len; j+=8 )
+        {
+            if( len >= 8 )
+            {
+                vc0 = VEC128_LOAD_UINT16( &(peSrc->coeffs[start+j]    ) );
+                vc1 = VEC128_LOAD_UINT16( &(peSrc->coeffs[start+j+len]) );
+            }
+            else if ( len == 4 )
+            {
+                vc0 = VEC64_LOAD_UINT16( &(peSrc->coeffs[start+j]    ) );
+                vc1 = VEC64_LOAD_UINT16( &(peSrc->coeffs[start+j+len]) );
+            }
+            else /*if ( len == 2 )*/
+            {
+                vc0 = VEC32_LOAD_UINT16( &(peSrc->coeffs[start+j]    ) );
+                vc1 = VEC32_LOAD_UINT16( &(peSrc->coeffs[start+j+len]) );
+            }
+
+            // c1TimesTwiddle = twiddleFactor * c1 mod Q;
+            VEC128_MONTGOMERY_MUL_UINT16( vc1Twiddle, vc1, vTwiddleFactor, vTwiddleFactorMont, vQ, vZero, vOne, vTmp0, vTmp1 );
+            // c1 = c0 - c1TimesTwiddle mod Q
+            VEC128_MOD_SUB_UINT16( vc1, vc0, vc1Twiddle, vQ, vZero, vTmp0 );
+            // c0 = c0 + c1TimesTwiddle mod Q
+            VEC128_MOD_ADD_UINT16( vc0, vc0, vc1Twiddle, vQ, vTmp1 );
+
+            if( len >= 8 )
+            {
+                VEC128_STORE_UINT16( &(peSrc->coeffs[start+j]    ), vc0 );
+                VEC128_STORE_UINT16( &(peSrc->coeffs[start+j+len]), vc1 );
+            }
+            else if ( len == 4 )
+            {
+                VEC64_STORE_UINT16( &(peSrc->coeffs[start+j]    ), vc0 );
+                VEC64_STORE_UINT16( &(peSrc->coeffs[start+j+len]), vc1 );
+            }
+            else /*if ( len == 2 )*/
+            {
+                VEC32_STORE_UINT16( &(peSrc->coeffs[start+j]    ), vc0 );
+                VEC32_STORE_UINT16( &(peSrc->coeffs[start+j+len]), vc1 );
+            }
+        }
+    }
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementINTTLayerVec128(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc,
+            UINT32                       k,
+            UINT32                       len )
+{
+    UINT32 start, j;
+    VEC128_TYPE_UINT16 vc0, vc1, vTmp0, vTmp1, vTmp2, vTwiddleFactor, vTwiddleFactorMont, vQ, vZero, vOne;
+
+    SYMCRYPT_ASSERT( len >= 2 );
+
+    vQ = VEC128_SET_UINT16( SYMCRYPT_MLKEM_Q );
+    vZero = VEC128_SET_UINT16( 0 );
+    vOne = VEC128_SET_UINT16( 1 );
+
+    for( start=0; start<256; start+=(2*len) )
+    {
+        vTwiddleFactor     = VEC128_SET_UINT16( MlKemZetaBitRevTimesR[k] );
+        vTwiddleFactorMont = VEC128_SET_UINT16( MlKemZetaBitRevTimesRTimesNegQInvModR[k] );
+        k--;
+        for( j=0; j<len; j+=8 )
+        {
+            if( len >= 8 )
+            {
+                vc0 = VEC128_LOAD_UINT16( &(peSrc->coeffs[start+j]    ) );
+                vc1 = VEC128_LOAD_UINT16( &(peSrc->coeffs[start+j+len]) );
+            }
+            else if ( len == 4 )
+            {
+                vc0 = VEC64_LOAD_UINT16( &(peSrc->coeffs[start+j]    ) );
+                vc1 = VEC64_LOAD_UINT16( &(peSrc->coeffs[start+j+len]) );
+            }
+            else /*if ( len == 2 )*/
+            {
+                vc0 = VEC32_LOAD_UINT16( &(peSrc->coeffs[start+j]    ) );
+                vc1 = VEC32_LOAD_UINT16( &(peSrc->coeffs[start+j+len]) );
+            }
+
+            // tmp = c0 + c1 mod Q
+            VEC128_MOD_ADD_UINT16( vTmp2, vc0, vc1, vQ, vTmp0 );
+            // c1 = c1 - c0 mod Q
+            VEC128_MOD_SUB_UINT16( vc1, vc1, vc0, vQ, vZero, vTmp1 );
+            // c1 = twiddleFactor * c1;
+            VEC128_MONTGOMERY_MUL_UINT16( vc1, vc1, vTwiddleFactor, vTwiddleFactorMont, vQ, vZero, vOne, vTmp0, vTmp1 );
+
+            if( len >= 8 )
+            {
+                VEC128_STORE_UINT16( &(peSrc->coeffs[start+j]    ), vTmp2 );
+                VEC128_STORE_UINT16( &(peSrc->coeffs[start+j+len]), vc1 );
+            }
+            else if ( len == 4 )
+            {
+                VEC64_STORE_UINT16( &(peSrc->coeffs[start+j]    ), vTmp2 );
+                VEC64_STORE_UINT16( &(peSrc->coeffs[start+j+len]), vc1 );
+            }
+            else /*if ( len == 2 )*/
+            {
+                VEC32_STORE_UINT16( &(peSrc->coeffs[start+j]    ), vTmp2 );
+                VEC32_STORE_UINT16( &(peSrc->coeffs[start+j+len]), vc1 );
+            }
+        }
+    }
+}
+
+#endif
+
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlKemModAdd(
+    UINT32 a,
+    UINT32 b )
+{
+    UINT32 res;
+
+    SYMCRYPT_ASSERT( a < SYMCRYPT_MLKEM_Q );
+    SYMCRYPT_ASSERT( b < SYMCRYPT_MLKEM_Q );
+
+    res = a + b - SYMCRYPT_MLKEM_Q;
+    SYMCRYPT_ASSERT( ((res >> 16) == 0) || ((res >> 16) == 0xffff) );
+    res = res + (SYMCRYPT_MLKEM_Q & (res >> 16));
+    SYMCRYPT_ASSERT( res < SYMCRYPT_MLKEM_Q );
+
+    return res;
+}
+
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlKemModSub(
+    UINT32 a,
+    UINT32 b )
+{
+    UINT32 res;
+
+    SYMCRYPT_ASSERT( a < 2*SYMCRYPT_MLKEM_Q );
+    SYMCRYPT_ASSERT( b <= SYMCRYPT_MLKEM_Q );
+
+    res = a - b;
+    SYMCRYPT_ASSERT( ((res >> 16) == 0) || ((res >> 16) == 0xffff) );
+    res = res + (SYMCRYPT_MLKEM_Q & (res >> 16));
+    SYMCRYPT_ASSERT( res < SYMCRYPT_MLKEM_Q );
+
+    return res;
+}
+
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlKemMontMul(
+    UINT32 a,
+    UINT32 b,
+    UINT32 bMont )
+{
+    UINT32 res, inv;
+
+    SYMCRYPT_ASSERT( a < SYMCRYPT_MLKEM_Q );
+    SYMCRYPT_ASSERT( b < SYMCRYPT_MLKEM_Q );
+    SYMCRYPT_ASSERT( bMont <= SYMCRYPT_MLKEM_Rmask );
+    SYMCRYPT_ASSERT( bMont == ((b * SYMCRYPT_MLKEM_NegQInvModR) & SYMCRYPT_MLKEM_Rmask) );
+
+    res = a * b;
+    inv = (a * bMont) & SYMCRYPT_MLKEM_Rmask;
+    res += inv * SYMCRYPT_MLKEM_Q;
+    SYMCRYPT_ASSERT( (res & SYMCRYPT_MLKEM_Rmask) == 0 );
+    res = res >> SYMCRYPT_MLKEM_Rlog2;
+
+    return SymCryptMlKemModSub( res, SYMCRYPT_MLKEM_Q );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementNTTLayerC(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc,
+            UINT32                       k,
+            UINT32                       len )
+{
+    UINT32 start, j;
+    UINT32 twiddleFactor, twiddleFactorMont, c0, c1, c1TimesTwiddle;
+
+    for( start=0; start<256; start+=(2*len) )
+    {
+        twiddleFactor = MlKemZetaBitRevTimesR[k];
+        twiddleFactorMont = MlKemZetaBitRevTimesRTimesNegQInvModR[k];
+        k++;
+        for( j=0; j<len; j++ )
+        {
+            c0 = peSrc->coeffs[start+j];
+            SYMCRYPT_ASSERT( c0 < SYMCRYPT_MLKEM_Q );
+            c1 = peSrc->coeffs[start+j+len];
+            SYMCRYPT_ASSERT( c1 < SYMCRYPT_MLKEM_Q );
+
+            c1TimesTwiddle = SymCryptMlKemMontMul( c1, twiddleFactor, twiddleFactorMont );
+            c1 = SymCryptMlKemModSub( c0, c1TimesTwiddle );
+            c0 = SymCryptMlKemModAdd( c0, c1TimesTwiddle );
+
+            peSrc->coeffs[start+j]      = (UINT16) c0;
+            peSrc->coeffs[start+j+len]  = (UINT16) c1;
+        }
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementINTTLayerC(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc,
+            UINT32                       k,
+            UINT32                       len )
+{
+    UINT32 start, j;
+    UINT32 twiddleFactor, twiddleFactorMont, c0, c1, tmp;
+
+    for( start=0; start<256; start+=(2*len) )
+    {
+        twiddleFactor = MlKemZetaBitRevTimesR[k];
+        twiddleFactorMont = MlKemZetaBitRevTimesRTimesNegQInvModR[k];
+        k--;
+        for( j=0; j<len; j++ )
+        {
+            c0 = peSrc->coeffs[start+j];
+            SYMCRYPT_ASSERT( c0 < SYMCRYPT_MLKEM_Q );
+            c1 = peSrc->coeffs[start+j+len];
+            SYMCRYPT_ASSERT( c1 < SYMCRYPT_MLKEM_Q );
+
+            tmp = SymCryptMlKemModAdd( c0, c1 );
+            c1 = SymCryptMlKemModSub( c1, c0 );
+            c1 = SymCryptMlKemMontMul( c1, twiddleFactor, twiddleFactorMont );
+
+            peSrc->coeffs[start+j]      = (UINT16) tmp;
+            peSrc->coeffs[start+j+len]  = (UINT16) c1;
+        }
+    }
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementNTTLayer(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc,
+            UINT32                       k,
+            UINT32                       len )
+{
+#if SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) && SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptMlKemPolyElementNTTLayerVec128( peSrc, k, len );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptMlKemPolyElementNTTLayerC( peSrc, k, len );
+    }
+#elif SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) )
+    {
+        SymCryptMlKemPolyElementNTTLayerVec128( peSrc, k, len );
+    } else {
+        SymCryptMlKemPolyElementNTTLayerC( peSrc, k, len );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON ) )
+    {
+        SymCryptMlKemPolyElementNTTLayerVec128( peSrc, k, len );
+    } else {
+        SymCryptMlKemPolyElementNTTLayerC( peSrc, k, len );
+    }
+#else
+    SymCryptMlKemPolyElementNTTLayerC( peSrc, k, len );
+#endif
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementINTTLayer(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc,
+            UINT32                       k,
+            UINT32                       len )
+{
+#if SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) && SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptMlKemPolyElementINTTLayerVec128( peSrc, k, len );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptMlKemPolyElementINTTLayerC( peSrc, k, len );
+    }
+#elif SYMCRYPT_CPU_AMD64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSE2 ) )
+    {
+        SymCryptMlKemPolyElementINTTLayerVec128( peSrc, k, len );
+    } else {
+        SymCryptMlKemPolyElementINTTLayerC( peSrc, k, len );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON ) )
+    {
+        SymCryptMlKemPolyElementINTTLayerVec128( peSrc, k, len );
+    } else {
+        SymCryptMlKemPolyElementINTTLayerC( peSrc, k, len );
+    }
+#else
+    SymCryptMlKemPolyElementINTTLayerC( peSrc, k, len );
+#endif
+}
+
+#define SYMCRYPT_MLKEM_MaxCoeff                   (SYMCRYPT_MLKEM_Q - 1)
+#define SYMCRYPT_MLKEM_MaxCoeffProduct            (SYMCRYPT_MLKEM_MaxCoeff*SYMCRYPT_MLKEM_MaxCoeff)
+
+// max([ ((i*j) + ((((i*j)*NegQInvModR) & Rmask)*Q)) >> Rlog2 for i in range(Q) for j in range(Q) ])
+#define SYMCRYPT_MLKEM_MaxFirstStepReduction      (3494)
+// max([ ( pow(17, (2*i)+1, Q) << Rlog2 ) % Q for i in range(128) ])
+#define SYMCRYPT_MLKEM_MaxZetaTwoTimesPlus1TimesR (3254)
+#define SYMCRYPT_MLKEM_MaxA1B1ZetaPow             (SYMCRYPT_MLKEM_MaxFirstStepReduction*SYMCRYPT_MLKEM_MaxZetaTwoTimesPlus1TimesR)
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementMulAndAccumulate(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT            peSrc1,
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT            peSrc2,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paDst )
+{
+    UINT32 i;
+    UINT32 a0, a1, b0, b1, c0, c1;
+    UINT32 a0b0, a1b1, a0b1, a1b0, a1b1zetapow, inv;
+
+    for( i=0; i<(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 2); i++ )
+    {
+        a0 = peSrc1->coeffs[(2*i)  ];
+        SYMCRYPT_ASSERT( a0 < SYMCRYPT_MLKEM_Q );
+        a1 = peSrc1->coeffs[(2*i)+1];
+        SYMCRYPT_ASSERT( a1 < SYMCRYPT_MLKEM_Q );
+
+        b0 = peSrc2->coeffs[(2*i)  ];
+        SYMCRYPT_ASSERT( b0 < SYMCRYPT_MLKEM_Q );
+        b1 = peSrc2->coeffs[(2*i)+1];
+        SYMCRYPT_ASSERT( b1 < SYMCRYPT_MLKEM_Q );
+
+        c0 = paDst->coeffs[(2*i)  ];
+        SYMCRYPT_ASSERT( c0 <= 3*(SYMCRYPT_MLKEM_MaxCoeffProduct + SYMCRYPT_MLKEM_MaxA1B1ZetaPow) );
+        c1 = paDst->coeffs[(2*i)+1];
+        SYMCRYPT_ASSERT( c1 <= 3*(SYMCRYPT_MLKEM_MaxCoeffProduct + SYMCRYPT_MLKEM_MaxA1B1ZetaPow) );
+
+        // multiplication results in range [0, MaxCoeffProduct = 3328*3328]
+        a0b0 = a0 * b0;
+        a1b1 = a1 * b1;
+        a0b1 = a0 * b1;
+        a1b0 = a1 * b0;
+
+        // we need a1*b1*zetaTwoTimesBitRevPlus1TimesR[i]
+        // eagerly reduce a1*b1 with montgomery reduction
+        // a1b1 = red(a1*b1) -> range [0, MaxFirstStepReduction = 3494]
+        //   (3494 is maximum result of first step of montgomery reduction of x*y for x,y in [0, 3328])
+        // we do not need to do final reduction yet
+        inv = (a1b1 * SYMCRYPT_MLKEM_NegQInvModR) & SYMCRYPT_MLKEM_Rmask;
+        a1b1 = (a1b1 + (inv * SYMCRYPT_MLKEM_Q)) >> SYMCRYPT_MLKEM_Rlog2; // in range [0, MaxFirstStepReduction]
+        SYMCRYPT_ASSERT( a1b1 <= SYMCRYPT_MLKEM_MaxFirstStepReduction );
+
+        // now multiply a1b1 by power of zeta
+        a1b1zetapow = a1b1 * zetaTwoTimesBitRevPlus1TimesR[i];
+        // MaxZetaTwoTimesPlus1TimesR = 3254
+        // MaxA1B1ZetaPow = MaxFirstStepReduction*MaxZetaTwoTimesPlus1TimesR = 3494*3254
+        SYMCRYPT_ASSERT( a1b1zetapow <= SYMCRYPT_MLKEM_MaxA1B1ZetaPow );
+
+        // sum pairs of products
+        a0b0 += a1b1zetapow;    // a0*b0 + red(a1*b1)*zetapower in range [0, MaxCoeffProduct + MaxA1B1ZetaPow]
+        SYMCRYPT_ASSERT( a0b0 <= SYMCRYPT_MLKEM_MaxCoeffProduct + SYMCRYPT_MLKEM_MaxA1B1ZetaPow );
+        a0b1 += a1b0;           // a0*b1 + a1*b0                in range [0, 2*MaxCoeffProduct]
+        SYMCRYPT_ASSERT( a0b1 <= 2*SYMCRYPT_MLKEM_MaxCoeffProduct );
+
+        // We sum at most 4 pairs of products into an accumulator in ML-KEM
+        C_ASSERT( SYMCRYPT_MLKEM_MATRIX_MAX_NROWS <= 4 );
+        c0 += a0b0; // in range [0,4*MaxCoeffProduct + 4*MaxA1B1ZetaPow]
+        SYMCRYPT_ASSERT( c0 < (4*SYMCRYPT_MLKEM_MaxCoeffProduct) + (4*SYMCRYPT_MLKEM_MaxA1B1ZetaPow) );
+        c1 += a0b1; // in range [0,5*MaxCoeffProduct + 3*MaxA1B1ZetaPow]
+        SYMCRYPT_ASSERT( c1 < (5*SYMCRYPT_MLKEM_MaxCoeffProduct) + (3*SYMCRYPT_MLKEM_MaxA1B1ZetaPow) );
+
+        paDst->coeffs[(2*i)  ] = c0;
+        paDst->coeffs[(2*i)+1] = c1;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemMontgomeryReduceAndAddPolyElementAccumulatorToPolyElement(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paSrc,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT             peDst )
+{
+    UINT32 i;
+    UINT32 a, c, inv;
+
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        a = paSrc->coeffs[i];
+        SYMCRYPT_ASSERT( a <= 4*(SYMCRYPT_MLKEM_MaxCoeffProduct + SYMCRYPT_MLKEM_MaxA1B1ZetaPow) );
+        paSrc->coeffs[i] = 0;
+
+        c = peDst->coeffs[i];
+        SYMCRYPT_ASSERT( c < SYMCRYPT_MLKEM_Q );
+
+        // montgomery reduce sum of products
+        inv = (a * SYMCRYPT_MLKEM_NegQInvModR) & SYMCRYPT_MLKEM_Rmask;
+        a = (a + (inv * SYMCRYPT_MLKEM_Q)) >> SYMCRYPT_MLKEM_Rlog2; // in range [0, 4711]
+        SYMCRYPT_ASSERT( a <= 4711 );
+
+        // add destination
+        c += a;
+        SYMCRYPT_ASSERT( c <= 8039 );
+
+        // subtraction and conditional additions for constant time range reduction
+        c -= 2*SYMCRYPT_MLKEM_Q;           // in range [-2Q, 1381]
+        SYMCRYPT_ASSERT( (c >= ((UINT32)(-2*SYMCRYPT_MLKEM_Q))) || (c < 1381) );
+        c += SYMCRYPT_MLKEM_Q & (c >> 16); // in range [-Q, Q-1]
+        SYMCRYPT_ASSERT( (c >= ((UINT32)-SYMCRYPT_MLKEM_Q)) || (c < SYMCRYPT_MLKEM_Q) );
+        c += SYMCRYPT_MLKEM_Q & (c >> 16); // in range [0, Q-1]
+        SYMCRYPT_ASSERT( c < SYMCRYPT_MLKEM_Q );
+
+        peDst->coeffs[i] = (UINT16) c;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementMulR(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT     peDst )
+{
+    UINT32 i;
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        peDst->coeffs[i] = (UINT16) SymCryptMlKemMontMul(
+            peSrc->coeffs[i], SYMCRYPT_MLKEM_Rsqr, SYMCRYPT_MLKEM_RsqrTimesNegQInvModR );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementAdd(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc1,
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc2,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT  peDst )
+{
+    UINT32 i;
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        peDst->coeffs[i] = (UINT16) SymCryptMlKemModAdd( peSrc1->coeffs[i], peSrc2->coeffs[i] );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementSub(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc1,
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc2,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT  peDst )
+{
+    UINT32 i;
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        peDst->coeffs[i] = (UINT16) SymCryptMlKemModSub( peSrc1->coeffs[i], peSrc2->coeffs[i] );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementNTT(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc )
+{
+    SymCryptMlKemPolyElementNTTLayer( peSrc,  1, 128 );
+    SymCryptMlKemPolyElementNTTLayer( peSrc,  2,  64 );
+    SymCryptMlKemPolyElementNTTLayer( peSrc,  4,  32 );
+    SymCryptMlKemPolyElementNTTLayer( peSrc,  8,  16 );
+    SymCryptMlKemPolyElementNTTLayer( peSrc, 16,   8 );
+    SymCryptMlKemPolyElementNTTLayer( peSrc, 32,   4 );
+    SymCryptMlKemPolyElementNTTLayer( peSrc, 64,   2 );
+}
+
+// INTTFixupTimesRsqr = R^2 * 3303 = (3303<<32) mod Q
+// 3303 constant is fixup from FIPS 203
+// Multiplied by R^2 to additionally multiply coefficients by R after montgomery reduction
+const UINT32 SYMCRYPT_MLKEM_INTTFixupTimesRsqr = 1441;
+const UINT32 SYMCRYPT_MLKEM_INTTFixupTimesRsqrTimesNegQInvModR = 10079;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementINTTAndMulR(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT  peSrc )
+{
+    UINT32 i;
+
+    SymCryptMlKemPolyElementINTTLayer( peSrc, 127,   2 );
+    SymCryptMlKemPolyElementINTTLayer( peSrc,  63,   4 );
+    SymCryptMlKemPolyElementINTTLayer( peSrc,  31,   8 );
+    SymCryptMlKemPolyElementINTTLayer( peSrc,  15,  16 );
+    SymCryptMlKemPolyElementINTTLayer( peSrc,   7,  32 );
+    SymCryptMlKemPolyElementINTTLayer( peSrc,   3,  64 );
+    SymCryptMlKemPolyElementINTTLayer( peSrc,   1, 128 );
+
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++)
+    {
+        peSrc->coeffs[i] = (UINT16) SymCryptMlKemMontMul(
+            peSrc->coeffs[i], SYMCRYPT_MLKEM_INTTFixupTimesRsqr, SYMCRYPT_MLKEM_INTTFixupTimesRsqrTimesNegQInvModR );
+    }
+}
+
+// ((1<<33) / SYMCRYPT_MLKEM_Q) rounded to nearest integer
+//
+// 1<<33 is the smallest power of 2 s.t. the constant has sufficient precision to round
+// all inputs correctly in compression for all nBitsPerCoefficient < 12. A smaller
+// constant could be used for smaller nBitsPerCoefficient for a small performance gain
+//
+const UINT32 SYMCRYPT_MLKEM_COMPRESS_MULCONSTANT = 0x275f6f;
+const UINT32 SYMCRYPT_MLKEM_COMPRESS_SHIFTCONSTANT = 33;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementCompressAndEncode(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc,
+            UINT32                          nBitsPerCoefficient,
+    _Out_writes_bytes_(nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8))
+            PBYTE                           pbDst )
+{
+    UINT32 i;
+    UINT64 multiplication;
+    UINT32 coefficient;
+    UINT32 nBitsInCoefficient;
+    UINT32 bitsToEncode;
+    UINT32 nBitsToEncode;
+    UINT32 cbDstWritten = 0;
+    UINT32 accumulator = 0;
+    UINT32 nBitsInAccumulator = 0;
+
+    SYMCRYPT_ASSERT( nBitsPerCoefficient >  0  );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient <= 12 );
+
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        nBitsInCoefficient = nBitsPerCoefficient;
+        coefficient = peSrc->coeffs[i]; // in range [0, Q-1]
+        SYMCRYPT_ASSERT( coefficient < SYMCRYPT_MLKEM_Q );
+
+        // first compress the coefficient
+        // when nBitsPerCoefficient < 12 we compress per Compress_d in FIPS 203;
+        if(nBitsPerCoefficient < 12)
+        {
+            // Multiply by 2^(nBitsPerCoefficient+1) / Q by multiplying by constant and shifting right
+            multiplication = SYMCRYPT_MUL32x32TO64(coefficient, SYMCRYPT_MLKEM_COMPRESS_MULCONSTANT);
+            coefficient = (UINT32) (multiplication >> (SYMCRYPT_MLKEM_COMPRESS_SHIFTCONSTANT-(nBitsPerCoefficient+1)));
+
+            // add "half" to round to nearest integer
+            coefficient++;
+
+            // final divide by two to get multiplication by 2^nBitsPerCoefficient / Q
+            coefficient >>= 1;                              // in range [0, 2^nBitsPerCoefficient]
+            SYMCRYPT_ASSERT(coefficient <= (1UL<<nBitsPerCoefficient));
+
+            // modular reduction by masking
+            coefficient &= (1UL<<nBitsPerCoefficient)-1;    // in range [0, 2^nBitsPerCoefficient - 1]
+            SYMCRYPT_ASSERT(coefficient <  (1UL<<nBitsPerCoefficient));
+        }
+
+        // encode the coefficient
+        // simple loop to add bits to accumulator and write accumulator to output
+        do
+        {
+            nBitsToEncode = SYMCRYPT_MIN(nBitsInCoefficient, 32-nBitsInAccumulator);
+
+            bitsToEncode = coefficient & ((1UL<<nBitsToEncode)-1);
+            coefficient >>= nBitsToEncode;
+            nBitsInCoefficient -= nBitsToEncode;
+
+            accumulator |= (bitsToEncode << nBitsInAccumulator);
+            nBitsInAccumulator += nBitsToEncode;
+            if(nBitsInAccumulator == 32)
+            {
+                SYMCRYPT_STORE_LSBFIRST32( pbDst+cbDstWritten, accumulator );
+                cbDstWritten += 4;
+                accumulator = 0;
+                nBitsInAccumulator = 0;
+            }
+        } while( nBitsInCoefficient > 0 );
+    }
+
+    SYMCRYPT_ASSERT(nBitsInAccumulator == 0);
+    SYMCRYPT_ASSERT(cbDstWritten == (nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8)));
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementDecodeAndDecompress(
+    _In_reads_bytes_(nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8))
+            PCBYTE                      pbSrc,
+            UINT32                      nBitsPerCoefficient,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT peDst )
+{
+    UINT32 i;
+    UINT32 coefficient;
+    UINT32 nBitsInCoefficient;
+    UINT32 bitsToDecode;
+    UINT32 nBitsToDecode;
+    UINT32 cbSrcRead = 0;
+    UINT32 accumulator = 0;
+    UINT32 nBitsInAccumulator = 0;
+
+    SYMCRYPT_ASSERT( nBitsPerCoefficient >  0  );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient <= 12 );
+
+    for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i++ )
+    {
+        coefficient = 0;
+        nBitsInCoefficient = 0;
+
+        // first gather and decode bits from pbSrc
+        do
+        {
+            if(nBitsInAccumulator == 0)
+            {
+                accumulator = SYMCRYPT_LOAD_LSBFIRST32( pbSrc+cbSrcRead );
+                cbSrcRead += 4;
+                nBitsInAccumulator = 32;
+            }
+
+            nBitsToDecode = SYMCRYPT_MIN(nBitsPerCoefficient-nBitsInCoefficient, nBitsInAccumulator);
+            SYMCRYPT_ASSERT(nBitsToDecode <= nBitsInAccumulator);
+
+            bitsToDecode = accumulator & ((1UL<<nBitsToDecode)-1);
+            accumulator >>= nBitsToDecode;
+            nBitsInAccumulator -= nBitsToDecode;
+
+            coefficient |= (bitsToDecode << nBitsInCoefficient);
+            nBitsInCoefficient += nBitsToDecode;
+        } while( nBitsPerCoefficient > nBitsInCoefficient );
+        SYMCRYPT_ASSERT(nBitsInCoefficient == nBitsPerCoefficient);
+
+        // decompress the coefficient
+        // when nBitsPerCoefficient < 12 we decompress per Decompress_d in FIPS 203
+        // otherwise we perform input validation per 203 6.2 Input validation 2 (Modulus check)
+        if(nBitsPerCoefficient < 12)
+        {
+            // Multiply by Q / 2^(nBitsPerCoefficient-1) by multiplying by constant and shifting right
+            coefficient *= SYMCRYPT_MLKEM_Q;
+            coefficient >>= (nBitsPerCoefficient-1);
+
+            // add "half" to round to nearest integer
+            coefficient++;
+
+            // final divide by two to get multiplication by Q / 2^nBitsPerCoefficient
+            coefficient >>= 1;  // in range [0, Q]
+
+            // modular reduction by conditional subtraction
+            coefficient = SymCryptMlKemModSub( coefficient, SYMCRYPT_MLKEM_Q );
+            SYMCRYPT_ASSERT( coefficient < SYMCRYPT_MLKEM_Q );
+        }
+        else if( coefficient >= SYMCRYPT_MLKEM_Q )
+        {
+            // input validation failure - this can happen with a malformed or corrupt encapsulation
+            // or decapsulation key; we do not need to be constant time because we treat the
+            // validity of an imported key as public information.
+            return SYMCRYPT_INVALID_BLOB;
+        }
+
+        peDst->coeffs[i] = (UINT16) coefficient;
+    }
+
+    SYMCRYPT_ASSERT(nBitsInAccumulator == 0);
+    SYMCRYPT_ASSERT(cbSrcRead == (nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8)));
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementSampleNTTFromShake128(
+    _Inout_ PSYMCRYPT_SHAKE128_STATE    pState,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT  peDst )
+{
+    UINT32 i=0;
+    BYTE shakeOutputBuf[3*8]; // Keccak likes extracting multiples of 8-bytes
+    UINT32 currBufIndex = sizeof(shakeOutputBuf);
+    UINT16 sample0, sample1;
+
+    while( i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS )
+    {
+        SYMCRYPT_ASSERT(currBufIndex <= sizeof(shakeOutputBuf));
+        if( currBufIndex == sizeof(shakeOutputBuf) )
+        {
+            SymCryptShake128Extract(pState, shakeOutputBuf, sizeof(shakeOutputBuf), FALSE);
+            currBufIndex = 0;
+        }
+
+        sample0 = SYMCRYPT_LOAD_LSBFIRST16( shakeOutputBuf+currBufIndex ) & 0xfff;
+        sample1 = SYMCRYPT_LOAD_LSBFIRST16( shakeOutputBuf+currBufIndex+1 ) >> 4;
+        currBufIndex += 3;
+
+        peDst->coeffs[i] = sample0;
+        i += sample0 < SYMCRYPT_MLKEM_Q;
+
+        if( i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS )
+        {
+            peDst->coeffs[i] = sample1;
+            i += sample1 < SYMCRYPT_MLKEM_Q;
+        }
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementSampleCBDFromBytes(
+    _In_reads_bytes_(eta*2*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) + 1)
+                    PCBYTE                      pbSrc,
+    _In_range_(2,3) UINT32                      eta,
+    _Out_           PSYMCRYPT_MLKEM_POLYELEMENT peDst )
+{
+    UINT32 i, j;
+    UINT32 sampleBits;
+    UINT32 coefficient;
+
+    SYMCRYPT_ASSERT((eta == 2) || (eta == 3));
+    if( eta == 3 )
+    {
+        for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i+=4 )
+        {
+            // unconditionally load 4 bytes into sampleBits, but only treat the load
+            // as being 3 bytes (24-bits -> 4 coefficients) for eta==3 to align to
+            // byte boundaries. Source buffer must be 1 byte larger than shake output
+            sampleBits = SYMCRYPT_LOAD_LSBFIRST32( pbSrc );
+            pbSrc += 3;
+
+            // sum bit samples - each consecutive slice of eta bits is summed together
+            sampleBits = (sampleBits&0x249249) + ((sampleBits>>1)&0x249249) + ((sampleBits>>2)&0x249249);
+
+            for( j=0; j<4; j++ )
+            {
+                // each coefficient is formed by taking the difference of two consecutive slices of eta bits
+                // the first eta bits are positive, the second eta bits are negative
+                coefficient = sampleBits & 0x3f;
+                sampleBits >>= 6;
+                coefficient = (coefficient&3) - (coefficient>>3);
+                SYMCRYPT_ASSERT((coefficient >= ((UINT32)-3)) || (coefficient <= 3));
+
+                coefficient = coefficient + (SYMCRYPT_MLKEM_Q & (coefficient >> 16));     // in range [0, Q-1]
+                SYMCRYPT_ASSERT( coefficient < SYMCRYPT_MLKEM_Q );
+
+                peDst->coeffs[i+j] = (UINT16) coefficient;
+            }
+        }
+    }
+    else
+    {
+        for( i=0; i<SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS; i+=8 )
+        {
+            // unconditionally load 4 bytes (32-bits -> 8 coefficients) into sampleBits
+            sampleBits = SYMCRYPT_LOAD_LSBFIRST32( pbSrc );
+            pbSrc += 4;
+
+            // sum bit samples - each consecutive slice of eta bits is summed together
+            sampleBits = (sampleBits&0x55555555) + ((sampleBits>>1)&0x55555555);
+
+            for( j=0; j<8; j++ )
+            {
+                // each coefficient is formed by taking the difference of two consecutive slices of eta bits
+                // the first eta bits are positive, the second eta bits are negative
+                coefficient = sampleBits & 0xf;
+                sampleBits >>= 4;
+                coefficient = (coefficient&3) - (coefficient>>2);
+                SYMCRYPT_ASSERT((coefficient >= ((UINT32)-2)) || (coefficient <= 2));
+
+                coefficient = coefficient + (SYMCRYPT_MLKEM_Q & (coefficient >> 16));     // in range [0, Q-1]
+                SYMCRYPT_ASSERT( coefficient < SYMCRYPT_MLKEM_Q );
+
+                peDst->coeffs[i+j] = (UINT16) coefficient;
+            }
+        }
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemMatrixTranspose(
+    _Inout_ PSYMCRYPT_MLKEM_MATRIX  pmSrc )
+{
+    UINT32 i, j;
+    PSYMCRYPT_MLKEM_POLYELEMENT swap;
+    const UINT32 nRows = pmSrc->nRows;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+
+    for( i=0; i<nRows; i++ )
+    {
+        for( j=i+1; j<nRows; j++ )
+        {
+            swap = pmSrc->apPolyElements[(i*nRows) + j];
+            pmSrc->apPolyElements[(i*nRows) + j] = pmSrc->apPolyElements[(j*nRows) + i];
+            pmSrc->apPolyElements[(j*nRows) + i] = swap;
+        }
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemMatrixVectorMontMulAndAdd(
+    _In_    PCSYMCRYPT_MLKEM_MATRIX                 pmSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR                 pvSrc2,
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR                  pvDst,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp )
+{
+    UINT32 i, j;
+    const UINT32 nRows = pmSrc1->nRows;
+    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc1, peSrc2;
+    PSYMCRYPT_MLKEM_POLYELEMENT  peDst;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( pvSrc2->nRows == nRows );
+    SYMCRYPT_ASSERT( pvDst->nRows == nRows );
+
+    // Zero paTmp
+    SymCryptWipeKnownSize( paTmp, SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT_ACCUMULATOR );
+
+    for( i=0; i<nRows; i++ )
+    {
+        for( j=0; j<nRows; j++ )
+        {
+            peSrc1 = pmSrc1->apPolyElements[(i*nRows) + j];
+            peSrc2 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( j, pvSrc2 );
+            SymCryptMlKemPolyElementMulAndAccumulate( peSrc1, peSrc2, paTmp );
+        }
+
+        // write accumulator to dest and zero accumulator
+        peDst  = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvDst );
+        SymCryptMlKemMontgomeryReduceAndAddPolyElementAccumulatorToPolyElement( paTmp, peDst );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorMontDotProduct(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR                 pvSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR                 pvSrc2,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT             peDst,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc1->nRows;
+    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc1, peSrc2;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( pvSrc2->nRows == nRows );
+
+    // Zero paTmp and peDst
+    SymCryptWipeKnownSize( paTmp, SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT_ACCUMULATOR );
+    SymCryptWipeKnownSize( peDst, SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT );
+
+    for( i=0; i<nRows; i++ )
+    {
+        peSrc1 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc1 );
+        peSrc2 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc2 );
+        SymCryptMlKemPolyElementMulAndAccumulate( peSrc1, peSrc2, paTmp );
+    }
+
+    // write accumulator to dest and zero accumulator
+    SymCryptMlKemMontgomeryReduceAndAddPolyElementAccumulatorToPolyElement( paTmp, peDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorSetZero(
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR  pvSrc )
+{
+    const UINT32 nRows = pvSrc->nRows;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+
+    SymCryptWipe( (PBYTE) SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( 0, pvSrc ), nRows*SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorMulR(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc,
+    _Out_   PSYMCRYPT_MLKEM_VECTOR  pvDst )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc->nRows;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( pvDst->nRows == nRows );
+
+    for( i=0; i<nRows; i++ )
+    {
+        SymCryptMlKemPolyElementMulR(
+            SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc ),
+            SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvDst ) );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorAdd(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc2,
+    _Out_   PSYMCRYPT_MLKEM_VECTOR  pvDst )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc1->nRows;
+    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc1, peSrc2;
+    PSYMCRYPT_MLKEM_POLYELEMENT  peDst;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( pvSrc2->nRows == nRows );
+    SYMCRYPT_ASSERT( pvDst->nRows == nRows );
+
+    for( i=0; i<nRows; i++ )
+    {
+        peSrc1 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc1 );
+        peSrc2 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc2 );
+        peDst  = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvDst );
+        SymCryptMlKemPolyElementAdd( peSrc1, peSrc2, peDst );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorSub(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc2,
+    _Out_   PSYMCRYPT_MLKEM_VECTOR  pvDst )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc1->nRows;
+    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc1, peSrc2;
+    PSYMCRYPT_MLKEM_POLYELEMENT  peDst;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( pvSrc2->nRows == nRows );
+    SYMCRYPT_ASSERT( pvDst->nRows == nRows );
+
+    for( i=0; i<nRows; i++ )
+    {
+        peSrc1 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc1 );
+        peSrc2 = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc2 );
+        peDst  = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvDst );
+        SymCryptMlKemPolyElementSub( peSrc1, peSrc2, peDst );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorNTT(
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR  pvSrc )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc->nRows;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+
+    for( i=0; i<nRows; i++ )
+    {
+        SymCryptMlKemPolyElementNTT( SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc ) );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorINTTAndMulR(
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR  pvSrc )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc->nRows;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+
+    for( i=0; i<nRows; i++ )
+    {
+        SymCryptMlKemPolyElementINTTAndMulR( SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc ) );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorCompressAndEncode(
+    _In_                        PCSYMCRYPT_MLKEM_VECTOR pvSrc,
+                                UINT32                  nBitsPerCoefficient,
+    _Out_writes_bytes_(cbDst)   PBYTE                   pbDst,
+                                SIZE_T                  cbDst )
+{
+    UINT32 i;
+    const UINT32 nRows = pvSrc->nRows;
+    PCSYMCRYPT_MLKEM_POLYELEMENT peSrc;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient >  0  );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient <= 12 );
+    SYMCRYPT_ASSERT( cbDst == nRows*nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) );
+
+    UNREFERENCED_PARAMETER( cbDst );
+
+    for( i=0; i<nRows; i++ )
+    {
+        peSrc  = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvSrc );
+        SymCryptMlKemPolyElementCompressAndEncode( peSrc, nBitsPerCoefficient, pbDst );
+        pbDst += nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    }
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemVectorDecodeAndDecompress(
+    _In_reads_bytes_(cbSrc) PCBYTE                  pbSrc,
+                            SIZE_T                  cbSrc,
+                            UINT32                  nBitsPerCoefficient,
+    _Out_                   PSYMCRYPT_MLKEM_VECTOR  pvDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 i;
+    const UINT32 nRows = pvDst->nRows;
+    PSYMCRYPT_MLKEM_POLYELEMENT peDst;
+
+    SYMCRYPT_ASSERT( nRows >  0 );
+    SYMCRYPT_ASSERT( nRows <= SYMCRYPT_MLKEM_MATRIX_MAX_NROWS );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient >  0  );
+    SYMCRYPT_ASSERT( nBitsPerCoefficient <= 12 );
+    SYMCRYPT_ASSERT( cbSrc == nRows*nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) );
+
+    UNREFERENCED_PARAMETER( cbSrc );
+
+    for( i=0; i<nRows; i++ )
+    {
+        peDst  = SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( i, pvDst );
+        scError = SymCryptMlKemPolyElementDecodeAndDecompress( pbSrc, nBitsPerCoefficient, peDst );
+        if( scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+        pbSrc += nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8);
+    }
+
+cleanup:
+    return scError;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyWipePrivateState(
+    _Inout_ PSYMCRYPT_MLKEMKEY  pkMlKemkey )
+{
+    SymCryptMlKemVectorSetZero( pkMlKemkey->pvs );
+    SymCryptWipeKnownSize( pkMlKemkey->privateRandom, sizeof(pkMlKemkey->privateRandom) );
+    SymCryptWipeKnownSize( pkMlKemkey->privateSeed, sizeof(pkMlKemkey->privateSeed) );
+    pkMlKemkey->hasPrivateKey = FALSE;
+    pkMlKemkey->hasPrivateSeed = FALSE;
+}
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+#endif
diff --git a/libs/symcrypt/lib/modexp.c b/libs/symcrypt/lib/modexp.c
new file mode 100644
index 000000000000..674200cd6409
--- /dev/null
+++ b/libs/symcrypt/lib/modexp.c
@@ -0,0 +1,510 @@
+//
+// modexp.c   Modular exponentiation functions
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The windowed modular exponentiation algorithm works by generating a
+// side-channel table of all the powers of the base from 0 up to 2^W - 1
+// where W is the window size:
+//      scsPrecomp = { 1, base, base^2, ..., base^(2^W-1) }
+//
+// TODO: To mitigate power analysis attacks when multiplying by 1 (which might
+//       contain a lot of zeros in non-Montgomery moduli), future work is to
+//       get rid of the 1 in the table. The leak is limited since now we always
+//       have Montgomery moduli.
+//
+// Then it slices the exponent into chunks of W bits and goes through
+// each chunk of the exponent starting from the most significant
+// chunk. For each chunk c_i it squares a temporary modelement
+// W times and then multiplies it by scsPrecomp[c_i]. The starting
+// value of the temporary modelement is scsPrecomp[c_0] i.e. the one
+// corresponding to the most significant chunk.
+//
+// Denote by M and SQ the multiplications and squarings and by B = nBitsExp
+// number of bits of the exponent. Then the algorithm does
+//      (2^W - 2)*M + (B-1)/W*(W*SQ + M) =
+//      (2^W + (B-1)/W -2) multiplications and (B-1) squarings
+//
+// It is beneficial to change the window size from W to W+1 when
+//      2^(W+1) + (B-1)/(W+1) < 2^W + (B-1)/W =>
+//      B > 2^W*W(W+1)+1
+// A simple table that calculates the optimal values for the window size
+// is shown below.
+//
+// The minimum value of W is W=4 as 2^W should be a multiple
+// of the groupsize of the scsTable, which is 4 by default.
+
+#define MIN_WINDOW_SIZE     (4)
+
+static const UINT32 cutoffs[] =
+{
+    // 5,      // W should be 2 for 5 < B <= 25
+    // 25,     // W should be 3 for 25 < B <= 97
+    // 97,     // W should be 4 for 97 < B <= 321
+    321,    // W should be 5 for 321 < B <= 961
+    // 961,    // W should be 6 for 961 < B
+};
+
+static const UINT32 nCuttoffs = sizeof(cutoffs) / sizeof(cutoffs[0]);
+
+VOID
+SYMCRYPT_CALL
+SymCryptModExpWindowed(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
+    _In_                            PCSYMCRYPT_INT          piExp,
+                                    UINT32                  nBitsExp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 W = 0;
+    UINT32 nTableElements = 0;
+
+    SYMCRYPT_SCSTABLE scsPrecomp = { 0 };
+    UINT32 cbScsPrecomp = 0;
+
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
+
+    PSYMCRYPT_MODELEMENT peT1 = NULL;
+    PSYMCRYPT_MODELEMENT peT2 = NULL;
+
+    UINT32 nIterations = 0;
+    UINT32 iBit = 0;
+    UINT32 nBits = 0;
+    UINT32 index = 0;
+
+    // Truncate the nBitsExp if above the object size
+    nBitsExp = SYMCRYPT_MIN( nBitsExp, SymCryptIntBitsizeOfObject(piExp) );
+
+    // Calculate the window size
+    W = MIN_WINDOW_SIZE;
+    while ((W-MIN_WINDOW_SIZE < nCuttoffs) && (cutoffs[W-MIN_WINDOW_SIZE]<nBitsExp))
+    {
+        W++;
+    }
+    nTableElements = (1<<W);
+
+    // Initialize the table of temporary modelements
+    cbScsPrecomp = SymCryptScsTableInit( &scsPrecomp, nTableElements, cbModElement );
+
+    SYMCRYPT_ASSERT( cbScratch >= cbScsPrecomp + 2*cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pmMod->nDigits ) );
+
+    SymCryptScsTableSetBuffer( &scsPrecomp, pbScratch, cbScsPrecomp );
+    pbScratch += cbScsPrecomp;
+    cbScratch -= cbScsPrecomp;
+
+    // Create the temporary modelement
+    peT1 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+    SYMCRYPT_ASSERT( peT1 != NULL );
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+    peT2 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+    SYMCRYPT_ASSERT( peT2 != NULL );
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+
+    // Fill the first element with 1 (**note: this will cause 0^0 = 1)
+    // and the second with peBase
+    SYMCRYPT_ASSERT( nTableElements >= 2 );
+
+    SymCryptModElementSetValueUint32( 1, pmMod, peT1, pbScratch, cbScratch );
+    SymCryptScsTableStore( &scsPrecomp, 0, (PBYTE)peT1, cbModElement );
+
+    SymCryptModElementCopy( pmMod, peBase, peT1 );
+    SymCryptScsTableStore( &scsPrecomp, 1, (PBYTE)peT1, cbModElement );
+
+    // Fill the table with the powers of peBase
+    for (UINT32 i=2; i<nTableElements; i++)
+    {
+        // TODO: Future improvement, use squarings for this table.
+        SymCryptModMul( pmMod, peT1, peBase, peT1, pbScratch, cbScratch );
+        SymCryptScsTableStore( &scsPrecomp, i, (PBYTE)peT1, cbModElement );
+    }
+
+    // Find the number of iterations (minus one) and the starting position bit
+    SYMCRYPT_ASSERT( nBitsExp != 0 );
+    nIterations = (nBitsExp - 1) / W;
+    iBit = nIterations * W;
+
+    // Do the first chunk (it might be smaller than W bits)
+    nBits = nBitsExp - iBit;
+    index = SymCryptIntGetBits( piExp, iBit, nBits );
+    SymCryptScsTableLoad( &scsPrecomp, index, (PBYTE)peT1, cbModElement );
+
+    // Work in batches of W bits in the exponent
+    for (UINT32 i=0; i<nIterations; i++)
+    {
+        // Square W times
+        for (UINT32 j=0; j<W; j++)
+        {
+            SymCryptModSquare( pmMod, peT1, peT1, pbScratch, cbScratch );
+        }
+
+        iBit -= W;
+        index = SymCryptIntGetBits( piExp, iBit, W );
+        SymCryptScsTableLoad( &scsPrecomp, index, (PBYTE)peT2, cbModElement );
+
+        SymCryptModMul( pmMod, peT1, peT2, peT1, pbScratch, cbScratch );
+    }
+
+    SYMCRYPT_ASSERT( iBit == 0 );
+
+    SymCryptModElementCopy( pmMod, peT1, peDst );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModExpSquareAndMultiply32(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
+    _In_                            PCSYMCRYPT_INT          piExp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
+
+    PSYMCRYPT_MODELEMENT peT1 = NULL;
+    PSYMCRYPT_MODELEMENT peT2 = NULL;
+
+    // The bits of the exponent when this function is called are
+    // always less than 32.
+    UINT32 exp = SymCryptIntGetValueLsbits32( piExp );
+
+    SYMCRYPT_ASSERT( cbScratch >= 2*cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pmMod->nDigits ) );
+
+    // Create the temporary modelements
+    peT1 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+    SYMCRYPT_ASSERT( peT1 != NULL );
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+    peT2 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+    SYMCRYPT_ASSERT( peT2 != NULL );
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+
+    if (exp == 0)
+    {
+        SymCryptModElementSetValueUint32( 1, pmMod, peDst, pbScratch, cbScratch );
+    }
+    else
+    {
+        SymCryptModElementSetValueUint32( 1, pmMod, peT1, pbScratch, cbScratch );
+        SymCryptModElementCopy( pmMod, peBase, peT2 );
+
+        while (exp>1)
+        {
+            if (exp%2 == 1)
+            {
+                SymCryptModMul( pmMod, peT1, peT2, peT1, pbScratch, cbScratch );
+            }
+
+            SymCryptModSquare( pmMod, peT2, peT2, pbScratch, cbScratch );
+            exp /= 2;
+        }
+
+        SymCryptModMul( pmMod, peT1, peT2, peDst, pbScratch, cbScratch );
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptModExpGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
+    _In_                            PCSYMCRYPT_INT          piExp,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    if ( ((flags & SYMCRYPT_FLAG_DATA_PUBLIC)!=0) && (nBitsExp <= sizeof(UINT32)*8) )
+    {
+        SymCryptModExpSquareAndMultiply32( pmMod, peBase, piExp, peDst, pbScratch, cbScratch );
+    }
+    else
+    {
+        SymCryptModExpWindowed( pmMod, peBase, piExp, nBitsExp, peDst, pbScratch, cbScratch );  // This is the default
+    }
+}
+
+//
+// MultiExponentiation
+//
+
+// SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP: The maximum number of precomputed powers of the
+// base point allowed for the multi-exponentiation operation.
+//  It should be equal to 2^(SYMCRYPT_FDEF_MAX_WINDOW_MODEXP-1)
+#define SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP       (1<<(SYMCRYPT_FDEF_MAX_WINDOW_MODEXP-1))
+
+// SYMCRYPT_MODMULTIEXP_WINDOW_SIZE: Fixed window size for the WnafWithInterleaving
+// implementation. It is found to give the faster running times for sizes
+// 512 - 2048 bits.
+#define SYMCRYPT_MODMULTIEXP_WINDOW_SIZE        (5)
+
+C_ASSERT( (1 << (SYMCRYPT_MODMULTIEXP_WINDOW_SIZE-1)) <= SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP );
+
+//
+// The following function fills the table with odd powers
+// of the base point B.
+//
+// The first value must be filled by the caller.
+VOID
+SYMCRYPT_CALL
+SymCryptModExpPrecomputation(
+    _In_    PCSYMCRYPT_MODULUS      pmP,
+            UINT32                  nPrecomputedPowers,
+    _In_reads_( SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP )
+            PSYMCRYPT_MODELEMENT *  pePIs,
+            PSYMCRYPT_MODELEMENT    peTemp,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch
+)
+{
+    SYMCRYPT_ASSERT(nPrecomputedPowers>=2);
+
+    // Calculate B^2
+    SymCryptModSquare( pmP, pePIs[0], peTemp, pbScratch, cbScratch );
+
+    for (UINT32 i=1; i<nPrecomputedPowers; i++)
+    {
+        // B[i] = B^2*B[i-1]
+        SymCryptModMul( pmP, peTemp, pePIs[i-1], pePIs[i], pbScratch, cbScratch );
+    }
+}
+
+//
+// The following is a similar algorithm to SymCryptEcpointMultiScalarMulWnafWithInterleaving.
+// It is a NON SIDE-CHANNEL SAFE algorithm.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptModMultiExpWnafWithInterleaving(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
+    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
+                                    UINT32                  nBases,
+                                    UINT32                  nBitsExp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    UINT32  i, j;
+
+    UINT32  w = 0;
+    UINT32  nPrecompPoints = 0;
+    UINT32  nRecodedDigits = 0;
+
+    // Masks
+    UINT32  fOne[SYMCRYPT_MODMULTIEXP_MAX_NBASES] = { 0 };
+    UINT32  fOneTot = 0xffffffff;       // Final result 1
+
+    UINT32  fZeroExp = 0;               // Zero exponent
+    UINT32  fZeroTot = 0;               // Final result 0
+
+    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
+
+    // ====================================================
+    // Temporaries
+    PSYMCRYPT_MODELEMENT    pePIs[SYMCRYPT_MODMULTIEXP_MAX_NBASES*SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP] = { 0 };
+    PSYMCRYPT_MODELEMENT    peTemp = NULL;
+    PSYMCRYPT_MODELEMENT    peOne = NULL;
+
+    PUINT32                 absofKIs = NULL;
+    // ===================================================
+
+    // Calculate the window size
+    w = SYMCRYPT_MODMULTIEXP_WINDOW_SIZE;
+    nPrecompPoints = (1 << (w-1));          // We only store odd powers of the base point
+
+    // Number of recoded digits
+    nRecodedDigits = nBitsExp;
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - nBases, nPrecompPoints, and nRecodedDigits are bounded by SYMCRYPT_MODMULTIEXP_MAX_NBASES,
+    //        SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP, and SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP, respectively.
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    SYMCRYPT_ASSERT( SYMCRYPT_MODMULTIEXP_MAX_NBASES >= nBases );
+    SYMCRYPT_ASSERT( SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP >= nPrecompPoints );
+
+    // Creating temporary precomputed modelements
+    for (i=0; i<nBases*nPrecompPoints; i++)
+    {
+        SYMCRYPT_ASSERT( cbScratch >= cbModElement );
+        pePIs[i] = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+        SYMCRYPT_ASSERT( pePIs[i] != NULL );
+        pbScratch += cbModElement;
+        cbScratch -= cbModElement;
+    }
+
+    SYMCRYPT_ASSERT( cbScratch >=
+            2*cbModElement +
+            ((nBases*nRecodedDigits*sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE)*SYMCRYPT_ASYM_ALIGN_VALUE +
+            SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( SymCryptModulusDigitsizeOfObject( pmMod ) ) );
+
+    // Creating temporary points
+    peTemp = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+    SYMCRYPT_ASSERT( peTemp != NULL );
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+
+    peOne = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+    SYMCRYPT_ASSERT( peOne != NULL );
+    pbScratch += cbModElement;
+    cbScratch -= cbModElement;
+
+    // Fixing pointers to recoded digits (be careful that the remaining space is SYMCRYPT_ASYM_ALIGNed)
+    absofKIs = (PUINT32) pbScratch;
+    pbScratch += nBases * nRecodedDigits * sizeof(UINT32);
+    cbScratch -= nBases * nRecodedDigits * sizeof(UINT32);
+
+    // Update cbScratch first using pbScratch, as the amount of scratch skipped for alignment depends upon the alignment of pbScratch
+    cbScratch -=        ( ((SIZE_T)pbScratch + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1) ) - (SIZE_T)pbScratch;
+    pbScratch = (PBYTE) ( ((SIZE_T)pbScratch + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1) );
+
+
+    //
+    // Main algorithm
+    //
+
+    // Set peOne to 1
+    SymCryptModElementSetValueUint32( 1, pmMod, peOne, pbScratch, cbScratch );
+
+    // Zero-out all recoded digits
+    SymCryptWipe( (PBYTE)absofKIs, nBases*nRecodedDigits*sizeof(UINT32) );
+
+    for (j = 0; j<nBases; j++)
+    {
+        // Check if the exponent is zero
+        fZeroExp = SymCryptIntIsEqualUint32( piExpArray[j], 0 );
+
+        // Check if the result is 0 (i.e. 0^e with e!=0)
+        if( !fZeroExp && SymCryptModElementIsZero(pmMod, peBaseArray[j]) )
+        {
+            fZeroTot = 0xffffffff;
+            break;
+        }
+
+        // Check if the exponent is 0 or if the base point is 1
+        fOne[j] = ( fZeroExp | SymCryptModElementIsEqual( pmMod, peBaseArray[j], peOne ) );
+        fOneTot &= fOne[j];
+
+        // Skip the recoding stage (and all remaining steps) if this point will give result 1
+        if (!fOne[j])
+        {
+            // Recoding stage
+            SymCryptPositiveWidthNafRecoding( w, piExpArray[j], nBitsExp, &absofKIs[j*nRecodedDigits], nRecodedDigits );
+
+            // Copy the base in the start of the pePIs array
+            SymCryptModElementCopy( pmMod, peBaseArray[j], pePIs[j*nPrecompPoints] );
+
+            // Precomputation stage
+            SymCryptModExpPrecomputation( pmMod, nPrecompPoints, &pePIs[j*nPrecompPoints], peTemp, pbScratch, cbScratch );
+        }
+    }
+
+    if (fZeroTot)
+    {
+        SymCryptModElementSetValueUint32( 0, pmMod, peDst, pbScratch, cbScratch );
+    }
+    else
+    {
+        SymCryptModElementSetValueUint32( 1, pmMod, peTemp, pbScratch, cbScratch );
+
+        if (!fOneTot)
+        {
+            // Main loop
+            for (INT32 i = nRecodedDigits-1; i>-1; i--)
+            {
+                SymCryptModSquare( pmMod, peTemp, peTemp, pbScratch, cbScratch );
+
+                for (j = 0; j<nBases; j++)
+                {
+                    if (absofKIs[j*nRecodedDigits + i] != 0)
+                    {
+                        SymCryptModMul( pmMod, peTemp, pePIs[j*nPrecompPoints + absofKIs[j*nRecodedDigits + i]/2], peTemp, pbScratch, cbScratch );
+                    }
+                }
+            }
+        }
+
+        // Copy the result into the destination
+        SymCryptModElementCopy( pmMod, peTemp, peDst );
+    }
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModMultiExpGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
+    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
+                                    UINT32                  nBases,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if ( (nBases > SYMCRYPT_MODMULTIEXP_MAX_NBASES) ||
+         (nBitsExp > SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ((flags & SYMCRYPT_FLAG_DATA_PUBLIC)!=0)
+    {
+        SymCryptModMultiExpWnafWithInterleaving( pmMod, peBaseArray, piExpArray, nBases, nBitsExp, peDst, pbScratch, cbScratch );
+    }
+    else
+    {
+        UINT32 cbModElement = 0;
+        PSYMCRYPT_MODELEMENT peTemp = NULL;
+        PSYMCRYPT_MODELEMENT peAcc = NULL;
+
+        // Use two temporary modelements to store the results
+        // *** Make sure that the scratch space is enough i.e. the scratch space of ModMultiExp is
+        //  at least 2 modelements bigger than the scratch space of ModExp
+        cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
+
+        SYMCRYPT_ASSERT( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(SymCryptModulusDigitsizeOfObject(pmMod)) + 2*cbModElement <=
+                         SYMCRYPT_SCRATCH_BYTES_FOR_MODMULTIEXP( SymCryptModulusDigitsizeOfObject(pmMod), nBases, nBitsExp ) );
+        SYMCRYPT_ASSERT( cbScratch >= 2*cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(SymCryptModulusDigitsizeOfObject(pmMod)) );
+
+        peTemp = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+        pbScratch += cbModElement; cbScratch -= cbModElement;
+
+        peAcc = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
+        pbScratch += cbModElement; cbScratch -= cbModElement;
+
+        // Set peAcc to 1
+        SymCryptModElementSetValueUint32( 1, pmMod, peAcc, pbScratch, cbScratch );
+
+        for (UINT32 i=0; i<nBases; i++)
+        {
+            SymCryptModExpWindowed( pmMod, peBaseArray[i], piExpArray[i], nBitsExp, peTemp, pbScratch, cbScratch );
+
+            SymCryptModMul( pmMod, peAcc, peTemp, peAcc, pbScratch, cbScratch );
+        }
+
+        // Copy the result into the destination
+        SymCryptModElementCopy( pmMod, peAcc, peDst );
+    }
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/paddingPkcs7.c b/libs/symcrypt/lib/paddingPkcs7.c
new file mode 100644
index 000000000000..5d3a466b2538
--- /dev/null
+++ b/libs/symcrypt/lib/paddingPkcs7.c
@@ -0,0 +1,167 @@
+//
+// paddingPkcs7.c   Add/Remove PKCS7 padding
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptPaddingPkcs7Add(
+                                            SIZE_T  cbBlockSize,
+    _In_reads_(cbSrc)                       PCBYTE  pbSrc,
+                                            SIZE_T  cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)      PBYTE   pbDst,
+                                            SIZE_T  cbDst,
+                                            SIZE_T* pcbResult)
+{
+    SIZE_T          cbPadVal;               // PadVal is the number of bytes to pad.
+    SIZE_T          cbDataLastBlock;        // dwDataLastBlock is the number of bytes of data at the final block.
+    SIZE_T          cbResult = 0;           // This variable must always have a valid value when we finish the function.
+
+    SYMCRYPT_ASSERT(cbBlockSize < 256);                         // cbBlockSize must be < 256
+    SYMCRYPT_ASSERT((cbBlockSize & (cbBlockSize - 1)) == 0);    // cbBlockSize must be a power of 2
+
+    //
+    // Compute the padding parameters.
+    //
+
+    cbDataLastBlock = (cbSrc & (cbBlockSize - 1));
+
+    cbResult = (cbSrc - cbDataLastBlock + cbBlockSize);
+
+    SYMCRYPT_ASSERT(cbDst >= cbResult);     // cbDst >= cbSrc - cbSrc % cbBlockSize + cbBlockSize
+
+    if (cbResult > cbDst)
+    {
+        goto cleanup;
+    }
+
+    cbPadVal = (cbBlockSize - cbDataLastBlock);
+
+    //
+    //  perform the padding
+    //
+
+    // cbSrc must be greater than zero. memcpy(pbDst, NULL, 0) is not defined!
+    if (pbDst != pbSrc && cbSrc > 0)
+    {
+        memcpy(pbDst, pbSrc, cbSrc);
+    }
+
+    memset(pbDst + cbSrc, (int)cbPadVal, cbPadVal);
+
+cleanup:
+    *pcbResult = cbResult;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPaddingPkcs7Remove(
+                                            SIZE_T  cbBlockSize,
+    _In_reads_(cbSrc)                       PCBYTE  pbSrc,
+                                            SIZE_T  cbSrc,
+    _Out_writes_to_(cbDst, *pcbResult)      PBYTE   pbDst,
+                                            SIZE_T  cbDst,
+                                            SIZE_T* pcbResult)
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    UINT32  mPaddingError       = 0;    // Indicates whether there is an error in padding or not.
+    UINT32  mBufferSizeError    = 0;    // Indicates whether pbDst is large enough to contain the entire message.
+    UINT32  mask                = 0;    // Mask for message bytes at the final block.
+    UINT32  cbPadVal;                   // PadVal is the number of padded bytes.
+    UINT32  cbSrc32;
+    UINT32  cbDst32;
+    UINT32  cbMsg32;
+
+    SIZE_T  cbBulk = 0;
+    SIZE_T  cbResult;           // This variable must always have a valid value when we finish the function.
+
+
+    SYMCRYPT_ASSERT(cbBlockSize < 256);                         // cbBlockSize must be < 256
+    SYMCRYPT_ASSERT((cbBlockSize & (cbBlockSize - 1)) == 0);    // cbBlockSize must be a power of 2
+    SYMCRYPT_ASSERT((cbSrc & (cbBlockSize - 1)) == 0);          // cbSrc is a multiple of cbBlockSize
+    SYMCRYPT_ASSERT(cbSrc > 0);                                 // cbSrc is greaten than zero
+
+    cbPadVal = (UINT32)pbSrc[cbSrc - 1];
+
+    // check the Padding to make sure it is valid.
+    mPaddingError |= SymCryptMask32IsZeroU31(cbPadVal) | SymCryptMask32LtU31((UINT32)cbBlockSize, cbPadVal);
+
+    // If cbPadVal is greater than cbSrc, SYMCRYPT_INVALID_ARGUMENT will be returned
+    // and cbResult will not be the right value.
+    cbResult = cbSrc - cbPadVal;
+
+    //
+    // Bulk processing
+    //
+
+    cbDst = SYMCRYPT_MIN(cbDst, cbSrc);
+
+    cbBulk = cbSrc - cbBlockSize;
+
+    // cbSrc, cbDst, and blockSize are not secrets.
+    // This condition can be checked in a non-side channel safe way.
+    if (cbDst < cbBulk)
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    if (pbDst != pbSrc)
+    {
+        memcpy(pbDst, pbSrc, cbBulk);
+    }
+
+    // Updating parameters
+    pbSrc += cbBulk; cbSrc -= cbBulk;
+    pbDst += cbBulk; cbDst -= cbBulk;
+
+    cbSrc32 = (UINT32)cbSrc;
+    cbDst32 = (UINT32)cbDst;
+
+    //
+    // Validating padding
+    //
+    // If cbPadVal is greater than cbBlockSize,
+    // we have to limit cbPadVal to be at most equal to cbBlockSize.
+    cbPadVal = 1 + ((cbPadVal - 1) & (cbBlockSize - 1));
+    cbMsg32 = (UINT32)(cbBlockSize - cbPadVal);
+
+    //check Dst buffer length to make sure it is possible copy the whole message (not including the padding).
+    mBufferSizeError |= SymCryptMask32LtU31(cbDst32, cbMsg32);
+
+    //
+    // Final Block processing
+    //
+
+    // Updating only the bytes of the message and leaving the other bytes in pbDst unchanged.
+    // Validating the value of the padded bytes.
+
+    for (UINT32 i = 0; i < cbBlockSize; ++i)  // cbDst <= cbSrc == cbBlockSize
+    {
+        mask = SymCryptMask32LtU31(i, cbMsg32);
+
+        mPaddingError |= (SymCryptMask32IsNonzeroU31((UINT32)pbSrc[i] ^ cbPadVal) & ~mask);
+
+        if (i < cbDst)
+        {
+            pbDst[i] ^= (pbDst[i] ^ pbSrc[i]) & mask;
+        }
+    }
+
+cleanup:
+
+    *pcbResult = cbResult;
+
+    // Update scError with the two error masks.
+    // SYMCRYPT_INVALID_ARGUMENT gets precedence over SYMCRYPT_BUFFER_TOO_SMALL
+    scError ^= mBufferSizeError & (scError ^ SYMCRYPT_BUFFER_TOO_SMALL);
+    scError ^= mPaddingError & (scError ^ SYMCRYPT_INVALID_ARGUMENT);
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/pbkdf2.c b/libs/symcrypt/lib/pbkdf2.c
new file mode 100644
index 000000000000..3c1d06e43298
--- /dev/null
+++ b/libs/symcrypt/lib/pbkdf2.c
@@ -0,0 +1,126 @@
+//
+// pbkdf2.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement the pbkdf2 function
+//
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPbkdf2Derive(
+    _In_                    PCSYMCRYPT_PBKDF2_EXPANDED_KEY  pExpandedKey,
+    _In_reads_opt_(cbSalt)  PCBYTE                          pbSalt,
+                            SIZE_T                          cbSalt,
+                            UINT64                          iterationCnt,
+    _Out_writes_(cbResult)  PBYTE                           pbResult,
+                            SIZE_T                          cbResult)
+{
+    SYMCRYPT_MAC_STATE  macState;
+    UINT32 iBlock;
+    SIZE_T bytes;
+    SIZE_T blockSize = pExpandedKey->macAlg->resultSize;
+    UINT64 iterations;
+    SYMCRYPT_ALIGN BYTE  rbBlockResult[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+    SYMCRYPT_ALIGN BYTE  rbWorkBuffer[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+
+    SYMCRYPT_ASSERT(
+        blockSize <= SYMCRYPT_MAC_MAX_RESULT_SIZE &&
+        cbResult > 0 );
+
+    if (iterationCnt == 0)
+    {
+        return SYMCRYPT_WRONG_ITERATION_COUNT;
+    }
+
+    iBlock = 0;
+    while( cbResult > 0 )
+    {
+        iBlock += 1;
+        SYMCRYPT_STORE_MSBFIRST32( &rbBlockResult[0], iBlock );      // use result buf as temp
+
+        pExpandedKey->macAlg->initFunc  ( &macState, &pExpandedKey->macKey);
+        pExpandedKey->macAlg->appendFunc( &macState, pbSalt, cbSalt);
+        pExpandedKey->macAlg->appendFunc( &macState, &rbBlockResult[0], 4 );         // block count encoded in 4 bytes
+        pExpandedKey->macAlg->resultFunc( &macState, rbWorkBuffer);
+
+#pragma warning(suppress: 22105)
+        memcpy( rbBlockResult, rbWorkBuffer, blockSize );
+        for( iterations = 1; iterations < iterationCnt; iterations++ )
+        {
+            pExpandedKey->macAlg->initFunc  ( &macState, &pExpandedKey->macKey );
+            pExpandedKey->macAlg->appendFunc( &macState, rbWorkBuffer, blockSize );
+            pExpandedKey->macAlg->resultFunc( &macState, rbWorkBuffer );
+            SymCryptXorBytes( &rbWorkBuffer[0], &rbBlockResult[0], &rbBlockResult[0], blockSize );
+        }
+
+        bytes = SYMCRYPT_MIN( cbResult, blockSize );
+        memcpy( pbResult, rbBlockResult, bytes );
+        pbResult += bytes;
+        cbResult -= bytes;
+    }
+
+    SymCryptWipeKnownSize( &rbWorkBuffer[0], sizeof( rbWorkBuffer ) );
+    SymCryptWipeKnownSize( &rbBlockResult[0], sizeof( rbBlockResult ) );
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPbkdf2ExpandKey(
+    _Out_               PSYMCRYPT_PBKDF2_EXPANDED_KEY   pExpandedKey,
+    _In_                PCSYMCRYPT_MAC                  macAlgorithm,
+    _In_reads_(cbKey)   PCBYTE                          pbKey,
+                        SIZE_T                          cbKey )
+{
+    SYMCRYPT_ASSERT( macAlgorithm->expandedKeySize <= sizeof( pExpandedKey->macKey ) );
+
+    pExpandedKey->macAlg = macAlgorithm;
+    return macAlgorithm->expandKeyFunc(&pExpandedKey->macKey, pbKey, cbKey );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptPbkdf2(
+                            PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbKey)       PCBYTE          pbKey,
+                            SIZE_T          cbKey,
+    _In_reads_opt_(cbSalt)  PCBYTE          pbSalt,
+                            SIZE_T          cbSalt,
+                            UINT64          iterationCnt,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult)
+{
+    SYMCRYPT_PBKDF2_EXPANDED_KEY key;
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    scError = SymCryptPbkdf2ExpandKey( &key, macAlgorithm, pbKey, cbKey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptPbkdf2Derive( &key, pbSalt, cbSalt, iterationCnt, pbResult, cbResult );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    SymCryptWipeKnownSize( &key, sizeof( key ) );
+
+    return scError;
+
+}
+
+//
+// Self tests are in pbkdf_*.c files
+// to avoid pulling in SHA-1 when only PBKDF-SHA256 is used and
+// similar scenarios.
+//
diff --git a/libs/symcrypt/lib/pbkdf2_hmacsha1.c b/libs/symcrypt/lib/pbkdf2_hmacsha1.c
new file mode 100644
index 000000000000..cf675a8b85ea
--- /dev/null
+++ b/libs/symcrypt/lib/pbkdf2_hmacsha1.c
@@ -0,0 +1,41 @@
+//
+// pbkdf2_hmacsha1.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The PBKDF SHA-1 test
+// This is in a separate module to avoid pulling in SHA-1 whenever we use PBKDF
+//
+
+static const UINT64 pbkdf2_IterationCnt = 5;
+
+static const BYTE    pbkdf2_sha1Answer[] =
+{
+    0xef, 0xa9, 0xbf, 0xea, 0xa3, 0x4d, 0x70, 0x64,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptPbkdf2_HmacSha1SelfTest(void)
+{
+    BYTE    res[sizeof(pbkdf2_sha1Answer)];
+
+    SymCryptPbkdf2(
+        SymCryptHmacSha1Algorithm,
+        &SymCryptTestKey32[0], 8,
+        &SymCryptTestKey32[16], 16,
+        pbkdf2_IterationCnt,
+        res,
+        sizeof(res));
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if (memcmp(res, pbkdf2_sha1Answer, sizeof(res)) !=0)
+    {
+        SymCryptFatal('Pbk2');
+    }
+}
diff --git a/libs/symcrypt/lib/pbkdf2_hmacsha256.c b/libs/symcrypt/lib/pbkdf2_hmacsha256.c
new file mode 100644
index 000000000000..ebef54bb81d9
--- /dev/null
+++ b/libs/symcrypt/lib/pbkdf2_hmacsha256.c
@@ -0,0 +1,41 @@
+//
+// pbkdf2_hmacsha256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The PBKDF SHA-256 test
+// This is in a separate module to avoid pulling in SHA-256 whenever we use PBKDF
+//
+
+static const UINT64 pbkdf2_IterationCnt = 5;
+
+static const BYTE    pbkdf2_sha256Answer[] =
+{
+    0x05, 0x98, 0x1e, 0x89, 0x48, 0xd2, 0x84, 0x61,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptPbkdf2_HmacSha256SelfTest(void)
+{
+    BYTE    res[sizeof(pbkdf2_sha256Answer)];
+
+    SymCryptPbkdf2(
+        SymCryptHmacSha256Algorithm,
+        &SymCryptTestKey32[0], 8,
+        &SymCryptTestKey32[16], 16,
+        pbkdf2_IterationCnt,
+        res,
+        sizeof(res));
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if (memcmp(res, pbkdf2_sha256Answer, sizeof(res)) !=0)
+    {
+        SymCryptFatal('Pbk2');
+    }
+}
diff --git a/libs/symcrypt/lib/poly1305.c b/libs/symcrypt/lib/poly1305.c
new file mode 100644
index 000000000000..286f023d6b83
--- /dev/null
+++ b/libs/symcrypt/lib/poly1305.c
@@ -0,0 +1,468 @@
+//
+// Poly1305.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305ProcessBlocks(
+    _Inout_                 PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305(
+    _In_reads_( SYMCRYPT_POLY1305_KEY_SIZE )        PCBYTE  pbKey,
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE )   PBYTE   pbResult )
+{
+    SYMCRYPT_POLY1305_STATE state;
+
+    SymCryptPoly1305Init( &state, pbKey );
+    SymCryptPoly1305Append( &state, pbData, cbData );
+    SymCryptPoly1305Result( &state, pbResult );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Init(
+    _Out_                                       PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_( SYMCRYPT_POLY1305_KEY_SIZE )    PCBYTE                      pbKey )
+{
+    pState->r[0] = SYMCRYPT_LOAD_LSBFIRST32( pbKey +  0 ) & 0x0fffffff;
+    pState->r[1] = SYMCRYPT_LOAD_LSBFIRST32( pbKey +  4 ) & 0x0ffffffc;
+    pState->r[2] = SYMCRYPT_LOAD_LSBFIRST32( pbKey +  8 ) & 0x0ffffffc;
+    pState->r[3] = SYMCRYPT_LOAD_LSBFIRST32( pbKey + 12 ) & 0x0ffffffc;
+
+    pState->s[0] = SYMCRYPT_LOAD_LSBFIRST32( pbKey + 16 );
+    pState->s[1] = SYMCRYPT_LOAD_LSBFIRST32( pbKey + 20 );
+    pState->s[2] = SYMCRYPT_LOAD_LSBFIRST32( pbKey + 24 );
+    pState->s[3] = SYMCRYPT_LOAD_LSBFIRST32( pbKey + 28 );
+
+    // Set accumulator to zero
+    SymCryptWipeKnownSize( &pState->a[0], sizeof( pState->a ) );
+
+    pState->bytesInBuffer = 0;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Append(
+    _Inout_                 PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData )
+{
+    SIZE_T nBytes;
+    SIZE_T bytesInBuffer;
+
+    bytesInBuffer = pState->bytesInBuffer;
+    if( bytesInBuffer != 0 )
+    {
+        // We have a partial block in the buffer, keep filling the block
+
+        SYMCRYPT_ASSERT( bytesInBuffer < 16 );
+        nBytes = 16 - bytesInBuffer;
+        if( nBytes > cbData )
+        {
+            nBytes = cbData;
+        }
+
+        memcpy( &pState->buf[bytesInBuffer], pbData, nBytes );
+        pbData += nBytes;
+        cbData -= nBytes;
+        bytesInBuffer += nBytes;
+
+        if( bytesInBuffer == 16 )
+        {
+            // Buffer is full, process it and empty the buffer
+            SymCryptPoly1305ProcessBlocks( pState, pState->buf, bytesInBuffer );
+            bytesInBuffer = 0;
+        }
+        pState->bytesInBuffer = bytesInBuffer;
+    }
+
+    if( cbData >= 16 )
+    {
+        // There are whole blocks to process
+        SymCryptPoly1305ProcessBlocks( pState, pbData, cbData & ~0xf );
+        pbData += cbData;
+        cbData &= 0xf;
+        pbData -= cbData;
+    }
+
+    if( cbData > 0 )
+    {
+        // Copy remaining data to buffer
+        SYMCRYPT_ASSERT( cbData < 16 );
+        memcpy( &pState->buf[0], pbData, cbData );
+        pState->bytesInBuffer = cbData;
+    }
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Result(
+    _Inout_                                         PSYMCRYPT_POLY1305_STATE    pState,
+    _Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE )   PBYTE                       pbResult )
+{
+    SIZE_T bytesInBuffer;
+    UINT64 t;
+    UINT32 a4, a3, a2, a1, a0;
+    UINT32 maskOld, maskNew;
+
+    bytesInBuffer = pState->bytesInBuffer;
+    if( bytesInBuffer > 0 )
+    {
+        // Add trailing '1' byte and pad with zeroes
+        // Wipe function deals with 0-length wipes properly
+        pState->buf[bytesInBuffer++] = 1;
+        SymCryptWipe( &pState->buf[bytesInBuffer], 16 - bytesInBuffer );
+
+        // Now we have to process the block, but the block function adds a trailing
+        // 1 byte to each 16-byte block. We compensate for that by decrementing
+        // the highest word of the accumulator first; the 1 byte added by the block
+        // processing function has the effect of incrementing the highest accumulator
+        // word so those two operations cancel each other out.
+        pState->a[4] -= 1;
+        SymCryptPoly1305ProcessBlocks( pState, pState->buf, 16 );
+    }
+
+    // We have to fully reduce the accumulator first
+    // We have a[4]<6 at this point
+    a0 = pState->a[0];
+    a1 = pState->a[1];
+    a2 = pState->a[2];
+    a3 = pState->a[3];
+    a4 = pState->a[4];
+
+    SYMCRYPT_ASSERT( a4 < 6 );
+    // Because a4 < 6, we have to subtract either 0*P or 1*P
+    // we subtract P and them mux-choose between the new and old value
+    // Subtracting P is the same as subtracting 2^130 and adding 5
+    t = 5;
+
+    t += a0;
+    a0 = (UINT32) t;
+    t >>= 32;
+
+    t += a1;
+    a1 = (UINT32) t;
+    t >>= 32;
+
+    t += a2;
+    a2 = (UINT32) t;
+    t >>= 32;
+
+    t += a3;
+    a3 = (UINT32) t;
+    t >>= 32;
+
+    t += a4;
+    t -= 4;
+    a4 = (UINT32) t;
+    t >>= 32;
+
+    // If this subtraction produced a carry, then t = 0xffffffff, otherwise it is 0
+    maskOld = (UINT32) t;       // ffffffff if the old value is correct, 0 otherwise
+    maskNew = ~maskOld;         // ffffffff if the new value is correct, 0 otherwise
+
+    a0 = (maskNew & a0) | (maskOld & pState->a[0]);
+    a1 = (maskNew & a1) | (maskOld & pState->a[1]);
+    a2 = (maskNew & a2) | (maskOld & pState->a[2]);
+    a3 = (maskNew & a3) | (maskOld & pState->a[3]);
+ // a4 = (maskNew & a4) | (maskOld & pState->a[4]); // We don't need a4...
+
+    // Now we add S and return the data
+    t  = a0;
+    t += pState->s[0];
+    SYMCRYPT_STORE_LSBFIRST32( pbResult +  0, (UINT32) t );
+    t >>= 32;
+
+    t += a1;
+    t += pState->s[1];
+    SYMCRYPT_STORE_LSBFIRST32( pbResult +  4, (UINT32) t );
+    t >>= 32;
+
+    t += a2;
+    t += pState->s[2];
+    SYMCRYPT_STORE_LSBFIRST32( pbResult +  8, (UINT32) t );
+    t >>= 32;
+
+    t += a3;
+    t += pState->s[3];
+    SYMCRYPT_STORE_LSBFIRST32( pbResult + 12, (UINT32) t );
+
+    SymCryptWipeKnownSize( (PBYTE) pState, sizeof( *pState ) );
+}
+
+
+/*
+The heart of Poly1305 is a modular multiplication.
+The modulus P := 2^130 - 5
+
+One multiplicant is R which is part of the key. R is restricted to a subset of all possible
+values ("clamped") to make the computation faster.
+The other multiplicant is the accumulator A. The overall operation is
+
+    A += <value derived from the data>
+    A = (A*R) mod P
+
+We write all values base 2^32:
+b := 2^32
+A = a4 b^4 + a3 b^3 + a2 b^2 + a1 b + a1
+R =          r3 b^3 + r2 b^2 + r1 b + r0
+
+Fully reduced we would have a4 <= 3 but we don't store A in fully-reduced form. Instead
+we maintain a4 < L with L:=8.
+
+The restrictions on R are:
+    r3, r2, r1, r0 < 2^28
+    r3, r2, r1 are multiples of 4
+
+The core algorithm looks like this (explanations below)
+
+
+                  a4   a3  a2   a1   a0
+                       r3  r2   r1   r0 *
+---------------------------------------
+               a4r0 a3r0 a2r0 a1r0 a0r0
+          a4r1 a3r1 a2r1 a1r1 a0r1
+     a4r2 a3r2 a2r2 a1r1 a0r2
+a4r3 a3r3 a2r3 a1r3 a0r3                 +
+----------------------------------------
+  S7   S6   S5   S4   S3   S2   S1   S0
+
+  S7   S6   S5 T4+U   T3   S2   S1   S0
+
+                      T3   S2   S1   S0
+                      S7   S6   S5   T4
+                    S7/4 S6/4 S5/4 T4/4 +
+                    -------------------
+                  U   V3   V2   V1   V0
+
+At the top you see A and R with the 5*4 digit products arranged in columns.
+The S values are the sums of the product columns without any carries.
+Because the r values are <2^28 and a4 < L we have
+
+    S0 <= 1*(2^32-1)(2^28-1)
+    S1 <= 2*(2^32-1)(2^28-1)
+    S2 <= 3*(2^32-1)(2^28-1)
+    S3 <= 4*(2^32-1)(2^28-1)
+    S4 <= 3*(2^32-1)(2^28-1) + (L-1)*(2^28-1)
+    S5 <= 2*(2^32-1)(2^28-1) + (L-1)*(2^28-1), multiple of 4
+    S6 <= 1*(2^32-1)(2^28-1) + (L-1)*(2^28-1), multiple of 4
+    S7 <=                      (L-1)*(2^28-1), multiple of 4
+
+The next line defines T4, U, and T3 by
+T3 := S3 mod b          the lower word of S3
+T := S4 + floor(S3/b)   add the upper word of S3 to S4
+U := T mod 4
+T4 := T - U             Split T into a small value U and a bigger T4 that is a multiple of 4
+
+note that the digits (S7,S6, S5, S4, S3, S2, S1, S0) and (S7, S6, S5, T4+U, T3, S2, S1, S0)
+encode the same number, namely the result of the multiplication.
+
+We have bounds
+    floor(S3/b) <= 2^2 * (2^32-1) * (2^28-1) / 2^32  <  2^30
+    T  < 3*(2^32-1)(2^28-1) + (L-1)*(2^28-1) + 2^30
+    U  < 4
+    T4 < 3*(2^32-1)(2^28-1) + (L-1)*(2^28-1) + 2^30, multiple of 4
+
+Now we are ready to perform the modulo reduction. Because P = 2^130 - 5 we have for any value X
+    X*2^130 mod P = 5*X mod P
+because 2^130 = 5 mod P
+Or, if X is a multiple of 4 then
+    X*2^128 = (X + X/4) mod P
+(this is just the previous equation divided by 4)
+We apply that to S7, S6, S5, and T4 and add them (column wise) to (T3, S2, S1, S0) to get
+
+V0 := S0 + T4 + T4/4
+V1 := S1 + S5 + S5/4
+V2 := S2 + S6 + S6/4
+V3 := T3 + S7 + S7/4
+
+and note that (U, V3, V2, V1, V0) is equal to the result of the multiplication modulo P
+We can derive some bounds on these values
+
+ We assume L <= 8 (will get strict bound later)
+
+    V0 < 1*(2^32-1)(2^28-1) + 3*(2^32-1)(2^28-1) + (L-1)*(2^28-1) + 2^30 + (3*(2^32-1)(2^28-1) + (L-1)*(2^28-1) + 2^30)/4
+       = 4*1*(2^32-1)(2^28-1) + (L-1)*(2^28-1) + 2^30 + (3*(2^32-1)(2^28-1) + (L-1)*(2^28-1) + 2^30)/4
+       < 2^2 * 2^32 * 2^28 + 2^31 + 2^30 + (2^4 * 2^32 * 2^28 + 2^31 + 2^30)/4
+       = 2^62 + 2^31 + 2^30 + 2^60 + 2^29 + 2^30
+       < 2^63
+
+    V1 < 2*2^60 + 2*2^60 + 2^31 + (2*2^60 + 2^31)/4 < 2^63
+
+    V2 < 3*2^60 + 2^60 + 2^31 + (2^60 + 2^31) < 2^63
+
+    V3 < 2^32 + 2^31 + 2^29 < 2*2^32
+
+    U < 4
+
+So all the V values fit in 64 bits. A final carry propagation pass cleans this up to an array of 32-bit values which become
+the new accumulator value. (During carry propagation the 32-bit carry from the lower digit can be added to the higher digit
+because the V values are less than 2^63.)
+
+V3 < 2*2^32 and after adding at most 2^32 from a carry it is < 3*2^32 so the carry from V3 to U is at most 2.
+Thus the highest digit of the accumulator can be at most 3 + 2 = 5. This ensures a5<L for L>=6. We assumed L<=8 before, so
+L=6 works and satisfies the earlier assumption.
+
+To clarify the logic: IF a4<8 at the start of the multiplication THEN a4<6 after this function. Between multiplications we add
+a value < 2^129 which could result in adding 2 to a4, but as a4<6 before the addition the a4<8 before the multiplication
+is still satisfied.
+*/
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305ProcessBlocks(
+    _Inout_                 PSYMCRYPT_POLY1305_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData )
+// This is the portable C implementation, based on 32-bit operations.
+// If necessary, we'll add assembler code for this function later.
+{
+    UINT32 a0, a1, a2, a3, a4;
+    UINT32 r0, r1, r2, r3;
+    UINT64 t64;
+    UINT32 T3;
+    UINT32 V0, V1, V2;
+    UINT32 cy;
+    UINT32 U;
+    UINT32 t32;
+
+    r0 = pState->r[0];
+    r1 = pState->r[1];
+    r2 = pState->r[2];
+    r3 = pState->r[3];
+
+    a0 = pState->a[0];
+    a1 = pState->a[1];
+    a2 = pState->a[2];
+    a3 = pState->a[3];
+    a4 = pState->a[4];
+
+    // Here we have a4 < 6, but we sometimes decrement a4 to compensate for the
+    // 2^128 this function always adds. So we test a4 + 1 < 7
+    SYMCRYPT_ASSERT( a4 + 1 < 7 );
+
+    while( cbData >= 16 )
+    {
+        // Acc += data[0..15] + 2^128
+        t64 = (UINT64) a0 + SYMCRYPT_LOAD_LSBFIRST32( pbData +  0 );
+        a0 = (UINT32) t64;
+        t64 >>= 32;
+
+        t64 += (UINT64) a1 + SYMCRYPT_LOAD_LSBFIRST32( pbData +  4 );
+        a1 = (UINT32) t64;
+        t64 >>= 32;
+
+        t64 += (UINT64) a2 + SYMCRYPT_LOAD_LSBFIRST32( pbData +  8 );
+        a2 = (UINT32) t64;
+        t64 >>= 32;
+
+        t64 += (UINT64) a3 + SYMCRYPT_LOAD_LSBFIRST32( pbData + 12 );
+        a3 = (UINT32) t64;
+        t64 >>= 32;
+
+        a4 = (UINT32) t64 + a4 + 1;   // +1 is the padding '1' which we always apply
+        SYMCRYPT_ASSERT( a4 < 8 );
+
+        pbData += 16;
+        cbData -=16;
+
+        // Compute S3
+        t64 =  SYMCRYPT_MUL32x32TO64( a3, r0 )
+             + SYMCRYPT_MUL32x32TO64( a2, r1 )
+             + SYMCRYPT_MUL32x32TO64( a1, r2 )
+             + SYMCRYPT_MUL32x32TO64( a0, r3 );
+
+        SYMCRYPT_ASSERT( t64 < (1ULL << 62) );
+
+        T3 = (UINT32) t64;
+        t64 >>= 32;
+
+        // Compute T = S4 + floor(S3/2^32). We have the floor part in t64 already
+        // now add S4 to it
+        t64 +=   a4*r0                              // this fits in 32 bits as r0 < 2^28 and a4 < 8
+                + SYMCRYPT_MUL32x32TO64( a3, r1 )
+                + SYMCRYPT_MUL32x32TO64( a2, r2 )
+                + SYMCRYPT_MUL32x32TO64( a1, r3 );
+
+        U = (UINT32) t64 & 3;
+        t64 &= ~3;                  // t64 = T4 here
+
+        // Compute S0 + T4 + T4/4, and V0
+        t64 += (t64 >> 2) + SYMCRYPT_MUL32x32TO64( a0, r0 );
+        V0 = (UINT32)t64;
+        cy = (UINT32)(t64 >> 32);     // the carry from S0 to S1
+
+        // Compute S5
+        t64 = a4 * r1 + SYMCRYPT_MUL32x32TO64( a3, r2 ) + SYMCRYPT_MUL32x32TO64( a2, r3 );
+        t64 += t64 >> 2;    // = S5 + S5/4
+
+        t64 += SYMCRYPT_MUL32x32TO64( a1, r0 ) + SYMCRYPT_MUL32x32TO64( a0, r1 );
+        // t64 = S1 + S5 + S5/4
+
+        t64 += cy;
+        V1 = (UINT32) t64;
+        cy = (UINT32)(t64 >> 32);     // the carry from S1 to S2
+
+        // Compute S6
+        t64 = a4 * r2 + SYMCRYPT_MUL32x32TO64( a3, r3 );
+        t64 += t64 >> 2;    // S6 + S6/4
+
+        // now add S2
+        t64 += SYMCRYPT_MUL32x32TO64( a2, r0 ) + SYMCRYPT_MUL32x32TO64( a1, r1 ) + SYMCRYPT_MUL32x32TO64( a0, r2 );
+        t64 += cy;
+        V2 = (UINT32) t64;
+        cy = (UINT32)(t64 >> 32);
+
+        // Finally T3 + S7 + S7/4
+        t32 = a4 * r3;      // =S7, a 32-bit value
+        t32 += t32/4;
+        t64 = (UINT64) T3 + t32;
+        t64 += cy;
+
+        a0 = V0;
+        a1 = V1;
+        a2 = V2;
+        a3 = (UINT32) t64;
+        a4 = U + (UINT32)(t64 >> 32);
+
+        SYMCRYPT_ASSERT( a4 < 6 );
+    }
+
+    pState->a[0] = a0;
+    pState->a[1] = a1;
+    pState->a[2] = a2;
+    pState->a[3] = a3;
+    pState->a[4] = a4;
+}
+
+
+static const BYTE poly1305Kat[16] = {
+    0xef, 0x9e, 0x73, 0x2a, 0x7f, 0x2d, 0xf1, 0x85, 0xa7, 0x11, 0x80, 0xae, 0x58, 0x3a, 0x0f, 0x93,
+};
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptPoly1305Selftest(void)
+{
+    BYTE res[16];
+
+    SymCryptPoly1305( SymCryptTestKey32, SymCryptTestMsg16, 16, res );
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if( memcmp( res, poly1305Kat, sizeof( res ) ) != 0 )
+    {
+        SymCryptFatal( 'p135');
+    }
+}
diff --git a/libs/symcrypt/lib/precomp.h b/libs/symcrypt/lib/precomp.h
new file mode 100644
index 000000000000..305fa1ac4b15
--- /dev/null
+++ b/libs/symcrypt/lib/precomp.h
@@ -0,0 +1,45 @@
+//
+// SymCrypt library pre-compiled header file
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#ifdef __cplusplus
+#error C++
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnt.h>
+
+#include "symcrypt.h"
+#include "sc_lib.h"
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+#include <wmmintrin.h>
+#include <immintrin.h>
+
+    #if SYMCRYPT_GNUC
+        #include <x86intrin.h>  // required for definition of _rdseed64_step for GCC 8 and earlier
+        #include <xsaveintrin.h>
+        #define _XCR_XFEATURE_ENABLED_MASK 0
+    #endif
+#endif
+
+#ifndef __WINE_PE_BUILD
+#define SymCryptSha256AppendBlocks_xmm_ssse3_asm SymCryptSha256AppendBlocks_ul1
+#define SymCryptXtsAesEncryptInternalAsm SymCryptXtsAesEncryptInternalC
+#define SymCryptXtsAesDecryptInternalAsm SymCryptXtsAesDecryptInternalC
+#define SymCryptAesEncryptAsm SymCryptAesEncryptC
+#define SymCryptAesDecryptAsm SymCryptAesDecryptC
+#define SymCryptAesEcbEncryptAsm SymCryptAesEcbEncryptC
+#define SymCryptAesCbcEncryptAsm(key, chain, src, dst, data) \
+    SymCryptCbcEncrypt(&SymCryptAesBlockCipherNoOpt, key, chain, src, dst, data)
+#define SymCryptAesCbcDecryptAsm(key, chain, src, dst, data) \
+    SymCryptCbcDecrypt(&SymCryptAesBlockCipherNoOpt, key, chain, src, dst, data)
+#define SymCryptAesCtrMsb64Asm(key, chain, src, dst, data) \
+    SymCryptCtrMsb64(&SymCryptAesBlockCipherNoOpt, key, chain, src, dst, data)
+#endif
diff --git a/libs/symcrypt/lib/primes.c b/libs/symcrypt/lib/primes.c
new file mode 100644
index 000000000000..6c9fcbee96f9
--- /dev/null
+++ b/libs/symcrypt/lib/primes.c
@@ -0,0 +1,306 @@
+//
+// primes.c
+// Primality tests and prime number generation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+UINT32
+SYMCRYPT_CALL
+SymCryptIntMillerRabinPrimalityTest(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+                                    UINT32              nBitsSrc,
+                                    UINT32              nIterations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    BOOLEAN     innerLoop = TRUE;
+    UINT32      borrow = 0;
+
+    UINT32      nDigitsSrc = 0;
+
+    UINT32                  R = 1;
+    PSYMCRYPT_INT           piD = NULL;
+    UINT32                  cbD = 0;
+    PSYMCRYPT_MODULUS       pmModulus = NULL;
+    UINT32                  cbModulus = 0;
+    PSYMCRYPT_MODELEMENT    peX = NULL;
+    UINT32                  cbX = 0;
+
+    PSYMCRYPT_MODELEMENT    peOne = NULL;
+    PSYMCRYPT_MODELEMENT    peMinOne = NULL;
+
+    nDigitsSrc = SymCryptIntDigitsizeOfObject( piSrc );
+    cbD = SymCryptSizeofIntFromDigits( nDigitsSrc );
+    cbModulus = SymCryptSizeofModulusFromDigits( nDigitsSrc );
+
+    SYMCRYPT_ASSERT( nBitsSrc >= SymCryptIntBitsizeOfValue( piSrc ) );
+
+    SYMCRYPT_ASSERT( cbScratch >= cbModulus + SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(nDigitsSrc) );
+
+    // Allocate the modulus
+    pmModulus = SymCryptModulusCreate( pbScratch, cbModulus, nDigitsSrc );
+    SYMCRYPT_ASSERT( pmModulus != NULL );
+    pbScratch += cbModulus;
+    cbScratch -= cbModulus;
+
+    // Set the modulus
+    SymCryptIntToModulus(
+            piSrc,
+            pmModulus,
+            nBitsSrc,       // Average number of expected operations
+            SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC,
+            pbScratch,
+            cbScratch );
+
+    // Modelement size
+    cbX = SymCryptSizeofModElementFromModulus( pmModulus );
+
+    SYMCRYPT_ASSERT( cbScratch >=   3*cbX + cbD +
+                                    SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsSrc ) );
+
+    peX = SymCryptModElementCreate( pbScratch, cbX, pmModulus );
+    SYMCRYPT_ASSERT( peX != NULL );
+    pbScratch += cbX;
+    cbScratch -= cbX;
+
+    peOne = SymCryptModElementCreate( pbScratch, cbX, pmModulus );
+    SYMCRYPT_ASSERT( peOne != NULL );
+    pbScratch += cbX;
+    cbScratch -= cbX;
+
+    peMinOne = SymCryptModElementCreate( pbScratch, cbX, pmModulus );
+    SYMCRYPT_ASSERT( peMinOne != NULL );
+    pbScratch += cbX;
+    cbScratch -= cbX;
+
+    // Allocate D
+    piD = SymCryptIntCreate( pbScratch, cbD, nDigitsSrc );
+    SYMCRYPT_ASSERT( piD != NULL );
+    pbScratch += cbD;
+    cbScratch -= cbD;
+
+    // Calculate (piSrc - 1)
+    // Note: We should never get a borrow here because the requirement
+    // is that Src > 3.
+    SymCryptIntCopy( piSrc, piD );
+    borrow = SymCryptIntSubUint32( piD, 1, piD );
+    SYMCRYPT_ASSERT( borrow==0 );
+
+    SYMCRYPT_ASSERT( SymCryptIntGetBit( piD, 0 ) == 0 );
+
+    // Check the 3 mod 4 requirement when side-channel safe
+    SYMCRYPT_ASSERT(
+            ((flags & SYMCRYPT_FLAG_DATA_PUBLIC) != 0) ||
+            (SymCryptIntGetBit( piD, 1 )!=0) );
+    UNREFERENCED_PARAMETER( flags );
+
+    // Calculate R and D such that Src - 1 = D*2^R
+    //      Notice that the loop executes only if
+    //      the SYMCRYPT_FLAG_DATA_PUBLIC is
+    //      specified (and Src != 3 mod 4)
+    R = 1;
+    while( SymCryptIntGetBit( piD, R )==0 )
+    {
+        R++;
+    }
+    SymCryptIntDivPow2( piD, R, piD );
+
+    // Set peOne and peMinOne
+    SymCryptModElementSetValueUint32( 1, pmModulus, peOne, pbScratch, cbScratch );
+    SymCryptModElementSetValueNegUint32( 1, pmModulus, peMinOne, pbScratch, cbScratch );
+
+    for (UINT32 i=0; i<nIterations; i++)
+    {
+        // Pick a random X in [2, piSrc-2]
+        // Therefore the flags parameter is 0 (default: not allowed 0, 1, -1 when modulus > 3)
+        SymCryptModSetRandom( pmModulus, peX, 0, pbScratch, cbScratch );
+
+        // X^D mod piSrc
+        // Notice that nBitsSrc is public in the call of SymCryptModExp
+        SymCryptModExp( pmModulus, peX, piD, nBitsSrc, 0, peX, pbScratch, cbScratch );
+
+        // Check for 1 or -1
+        if ( SymCryptModElementIsEqual( pmModulus, peX, peOne ) |
+             SymCryptModElementIsEqual( pmModulus, peX, peMinOne ) )
+        {
+            continue;
+        }
+
+        // repeat R-1 times
+        //      Notice that the inner loop executes only if
+        //      the SYMCRYPT_FLAG_DATA_PUBLIC is
+        //      specified (and Src != 3 mod 4)
+        innerLoop = TRUE;
+        for (UINT32 j=0; (j<R-1)&&(innerLoop); j++)
+        {
+            // Square X
+            SymCryptModSquare( pmModulus, peX, peX, pbScratch, cbScratch );
+
+            // Check if it is 1
+            if (SymCryptModElementIsEqual( pmModulus, peX, peOne ))
+            {
+                return 0x0;
+            }
+
+            // Check if it is -1
+            if (SymCryptModElementIsEqual( pmModulus, peX, peMinOne ))
+            {
+                innerLoop = FALSE;
+                break;
+            }
+        }
+
+        if (innerLoop)
+        {
+            return 0x0;
+        }
+    }
+
+    return  0xffffffff;     // Prime
+}
+
+#define SYMCRYPT_PRIME_GENERATION_MR_ITERATIONS (64)
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptIntGenerateRandomPrime(
+    _In_                            PCSYMCRYPT_INT      piLow,
+    _In_                            PCSYMCRYPT_INT      piHigh,
+    _In_reads_opt_( nPubExp )       PCUINT64            pu64PubExp,
+                                    UINT32              nPubExp,
+                                    UINT32              nTries,
+                                    UINT32              flags,
+    _Inout_                         PSYMCRYPT_INT       piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_EXTERNAL_FAILURE;
+    PSYMCRYPT_DIVISOR pdPubExp[ SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS ];
+    PSYMCRYPT_INT piTmp;
+
+    UINT32 cnt = 0;
+    UINT32 e;
+    BOOLEAN reject;
+    SIZE_T cbObj;
+
+    UINT32 nBits = SymCryptIntBitsizeOfObject(piDst);
+    UINT32 nBytes = (nBits + 7)/8;
+
+    UINT32 nBitsHigh = SymCryptIntBitsizeOfValue( piHigh );
+
+    PCSYMCRYPT_TRIALDIVISION_CONTEXT pTrialDivisionContext = SymCryptCreateTrialDivisionContext( SymCryptIntDigitsizeOfObject( piHigh ) );
+
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_INT_PRIME_GEN( SymCryptIntDigitsizeOfObject( piDst ) ) );
+    SYMCRYPT_ASSERT( nPubExp <= SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS );
+    SYMCRYPT_ASSERT( SymCryptDigitsFromBits( 64 ) == 1 );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Allocate divisor objects for each public exponent & initialize them
+    cbObj = SymCryptSizeofDivisorFromDigits( 1 );
+    for( e = 0; e < nPubExp; e++ )
+    {
+        SYMCRYPT_ASSERT( cbScratch >= cbObj );
+        if( pu64PubExp[e] == 0 )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto exit;
+        }
+
+        pdPubExp[e] = SymCryptDivisorCreate( pbScratch, cbObj, 1 );
+        pbScratch += cbObj;
+        cbScratch -= cbObj;
+
+        SymCryptIntSetValueUint64( pu64PubExp[e], SymCryptIntFromDivisor( pdPubExp[e] ) );
+        SymCryptIntToDivisor( SymCryptIntFromDivisor( pdPubExp[e] ), pdPubExp[e], 1000, SYMCRYPT_FLAG_DATA_PUBLIC, pbScratch, cbScratch );
+    }
+
+    cbObj = SymCryptSizeofIntFromDigits( 1 );
+    SYMCRYPT_ASSERT( cbScratch >= cbObj + nBytes );
+    piTmp = SymCryptIntCreate( pbScratch, cbObj, 1 );
+    pbScratch += cbObj;
+    cbScratch -= cbObj;
+
+    do
+    {
+        cnt++;
+
+        scError = SymCryptCallbackRandom( pbScratch, nBytes );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto exit;
+        }
+
+        scError = SymCryptIntSetValue( pbScratch, nBytes, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piDst );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto exit;
+        }
+
+        // Set the integer to 3 mod 4
+        SymCryptIntSetBits( piDst, 3, 0, 2 );
+
+        // Zero out the top bits above the upper limit
+        SymCryptIntModPow2( piDst, nBitsHigh, piDst );
+
+        // Check if it is in the correct range
+        if ( (SymCryptIntIsLessThan( piDst, piLow )) ||
+             (!SymCryptIntIsLessThan( piDst, piHigh )) )
+        {
+            continue;
+        }
+
+        // Fast compositeness check
+        if( SymCryptIntFindSmallDivisor( pTrialDivisionContext, piDst, NULL, 0 ) != 0 )
+        {
+            // We found a small divisor; it is not a prime
+            continue;
+        }
+
+        // Check for compatibility with public exponents (if provided)
+        reject = FALSE;
+        for( e = 0; e < nPubExp; e++ )
+        {
+            SymCryptIntDivMod( piDst, pdPubExp[e], NULL, piTmp, pbScratch, cbScratch );
+
+            // Check that e has a modular inverse mod P-1
+            // If e and P-1 are coprime, or GCD( P-1, e ) == 1, then e^-1 exists
+            // We have (P mod e) in piTmp.
+            // If piTmp == 0 then P is divisible by e, and will fail primality test - we don't care about the result of the GCD
+            // Otherwise, GCD( (P mod e)-1, e ) == GCD( P-1 mod e, e ) == GCD( P-1, e )
+            //
+            // Note that if P-1 is a multiple of e then (P mod e)-1 == 0, and GCD( 0, e ) == e
+            if( SymCryptUint64Gcd( pu64PubExp[e], SymCryptIntGetValueLsbits64( piTmp ) - 1, SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN ) != 1 )
+            {
+                // We can't continue the big loop from here :-(
+                reject = TRUE;
+                break;
+            }
+        }
+        if( reject )
+        {
+            continue;
+        }
+
+        // Primality check
+        if (SymCryptIntMillerRabinPrimalityTest( piDst, nBitsHigh, SYMCRYPT_PRIME_GENERATION_MR_ITERATIONS, 0, pbScratch, cbScratch ))
+        {
+            scError = SYMCRYPT_NO_ERROR;
+            break;
+        }
+    }
+    while (cnt<nTries);
+
+    if (cnt>=nTries)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+exit:
+    SymCryptFreeTrialDivisionContext( pTrialDivisionContext );
+    return scError;
+}
diff --git a/libs/symcrypt/lib/rc2.c b/libs/symcrypt/lib/rc2.c
new file mode 100644
index 000000000000..d17807fd0dcd
--- /dev/null
+++ b/libs/symcrypt/lib/rc2.c
@@ -0,0 +1,438 @@
+//
+// Rc2.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement RC2 from RFC 2268
+//
+// This is a new implementation, based on the RFC specification
+// and NOT based on the existing one in RSA32.lib,	which is the one from RSA data security.
+//
+
+
+#include "precomp.h"
+
+const SYMCRYPT_BLOCKCIPHER SymCryptRc2BlockCipher_default = {
+    SymCryptRc2ExpandKey,   // PSYMCRYPT_BLOCKCIPHER_EXPAND_KEY    expandKeyFunc;
+    SymCryptRc2Encrypt,     // PSYMCRYPT_BLOCKCIPHER_CRYPT         encryptFunc;
+    SymCryptRc2Decrypt,     // PSYMCRYPT_BLOCKCIPHER_CRYPT         decryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_ECB     ecbDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcEncryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    cbcDecryptFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_MAC_MODE      cbcMacFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_CRYPT_MODE    ctrMsbFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmEncryptPartFunc;
+    NULL,                   // PSYMCRYPT_BLOCKCIPHER_AEADPART_MODE gcmDecryptPartFunc;
+    8,                      // SIZE_T                              blockSize;
+    sizeof( SYMCRYPT_RC2_EXPANDED_KEY ), // SIZE_T  expandedKeySize;    // = sizeof( SYMCRYPT_XXX_EXPANDED_KEY )
+};
+
+const PCSYMCRYPT_BLOCKCIPHER SymCryptRc2BlockCipher = &SymCryptRc2BlockCipher_default;
+
+/*
+ *
+ *  constants
+ *
+ */
+static  const BYTE    PITABLE[] =
+{
+    0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
+    0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
+    0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
+    0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
+    0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
+    0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
+    0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
+    0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
+    0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
+    0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
+    0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
+    0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
+    0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
+    0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
+    0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
+    0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad
+};
+
+/*
+ *
+ *  macros
+ *
+ */
+
+
+/*
+ * These are the original macros we derived directly from the RFC.
+ * To improve the perf we changed to using R0, R1, R2, R3 variables rather
+ * than an array.
+ */
+
+/*
+#define MIX(R, K, i, j, S)    {\
+            R[i] = R[i] + K[j] + (R[(i-1)&3] & R[(i-2)&3]) + ((~R[(i-1)&3]) & R[(i-3)&3]);\
+            j = j + 1;\
+            R[i] = ROL16(R[i], S);\
+    }
+
+#define MIXROUND(R, K, j) {\
+            MIX(R, K, 0, j, 1);\
+            MIX(R, K, 1, j, 2);\
+            MIX(R, K, 2, j, 3);\
+            MIX(R, K, 3, j, 5);\
+    }
+
+#define MASH(R, K, i) \
+            R[i] = R[i] + K[R[(i-1)&3]&63];
+
+#define MASHROUND(R, K) {\
+            MASH(R, K, 0);\
+            MASH(R, K, 1);\
+            MASH(R, K, 2);\
+            MASH(R, K, 3);\
+    }
+
+//
+//  decrypt macros
+//
+
+
+#define RMIX(R, K, i, j, S)   {\
+            R[i] = ROR16( R[i], S );\
+            R[i] = R[i] - K[j] - (R[(i-1)&3] & R[(i-2)&3]) - ((~R[(i-1)&3]) & R[(i-3)&3]);\
+            j = j - 1;\
+    }
+
+#define RMIXROUND(R, K, j)   {\
+            RMIX(R, K, 3, j, 5);\
+            RMIX(R, K, 2, j, 3);\
+            RMIX(R, K, 1, j, 2);\
+            RMIX(R, K, 0, j, 1);\
+    }
+
+#define RMASH(R, K, i) \
+            R[i] = R[i] - K[R[(i-1)&3] & 63];
+
+#define RMASHROUND(R, K)    {\
+            RMASH(R, K, 3);\
+            RMASH(R, K, 2);\
+            RMASH(R, K, 1);\
+            RMASH(R, K, 0);\
+    }
+*/
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRc2ExpandKeyEx(
+    _Out_               PSYMCRYPT_RC2_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)  PCBYTE                      pbKey,
+                        SIZE_T                      cbKey,
+                        UINT32                      effectiveKeySizeInBits )
+{
+    SYMCRYPT_ALIGN BYTE   L[128];
+    UINT32  T;
+    UINT32  T1;
+    UINT32  T8;
+    UINT32  TM;
+    int     i;
+
+    SYMCRYPT_SET_MAGIC( pExpandedKey );
+
+    //
+    // According to RFC 2268 any key size in 1..128 is allowed.
+    //
+    // The effective key size cannot be 0 as the RFC specs would lead to a buffer overflow
+    // in the key expansion.
+    //
+    // If the effective key size <= 8 then T8=1 and the key expansion backward recursion
+    // drops into a fixed point because L[i+1] xor L[i+T8] is zero.
+    // Therefore, we require an effective key size of at least 9.
+    //
+    if( cbKey < 1 || cbKey > 128 || effectiveKeySizeInBits < 9 || effectiveKeySizeInBits > 8*128 )
+    {
+        return SYMCRYPT_WRONG_KEY_SIZE;
+    }
+
+    T = (UINT32)cbKey;              // 1 <= T1 <= 128
+
+    T1 = effectiveKeySizeInBits;    // 9 <= T1 <= 1024
+    T8 = (T1+7)/8;                  //  2 <= T8 <= 128
+
+    TM = 255 & ((1 << (8 + (UINT32)T1 - 8*T8))-1);
+
+    //
+    // To be endian-agnostic our expanded key is stored as an array of UINT16s. We do the key
+    // expansion in a local buffer and copy the values into the expanded key using the proper conversion.
+    //
+    memcpy(L, pbKey, T);
+
+    for(i = T; i <= 127; i++)
+    {
+        L[i] = PITABLE[(L[i-1]+L[i-T]) & 0xff];
+        //
+        // If the key size T=1 then we lose one bit of key space in the key expansion because
+        // L[i-1] == L[i-T] which makes the index to PITABLE even. So L[1..128] depend only on
+        // 7 bits.
+        //
+    }
+
+    L[128-T8] = PITABLE[L[128-T8] & TM];
+
+    for( i = 127-T8; i >=0; i--)
+    {
+        L[i] = PITABLE[L[i+1] ^ L[i+T8]];
+    }
+
+    //
+    // Now we copy the result into the UINT16 array in our expanded key.
+    // This is a memcpy for little-endian platforms, but this code works on all CPUs.
+    //
+    for( i=0; i<64; i++ )
+    {
+        pExpandedKey->K[i] = SYMCRYPT_LOAD_LSBFIRST16( &L[2*i] );
+    }
+
+    SymCryptWipeKnownSize( L, sizeof( L ) );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRc2ExpandKey(
+    _Out_               PSYMCRYPT_RC2_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                      pbKey,
+                        SIZE_T                      cbKey )
+{
+    return SymCryptRc2ExpandKeyEx( pExpandedKey, pbKey, cbKey, (UINT32) (8*cbKey) );
+}
+
+
+#define MIXROUND( n ) {\
+            R0 = R0 + K[4*n] + (R3 & R2) + (~R3 & R1); \
+            R0 = ROL16( R0, 1 ); \
+            R1 = R1 + K[4*n+1] + (R0 & R3) + (~R0 & R2 ); \
+            R1 = ROL16( R1, 2 ); \
+            R2 = R2 + K[4*n+2] + (R1 & R0) + (~R1 & R3); \
+            R2 = ROL16( R2, 3 ); \
+            R3 = R3 + K[4*n+3] + (R2 & R1) + (~R2 & R0 ); \
+            R3 = ROL16( R3, 5 ); \
+    }
+
+#define MASHROUND() { \
+        R0 = R0 + K[R3 & 63]; \
+        R1 = R1 + K[R0 & 63]; \
+        R2 = R2 + K[R1 & 63]; \
+        R3 = R3 + K[R2 & 63]; \
+    }
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc2Encrypt(
+    _In_                                    PCSYMCRYPT_RC2_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_RC2_BLOCK_SIZE )  PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_RC2_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    UINT16 R0, R1, R2, R3;
+    PCUINT16 K;
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    //
+    //  1. Initialize words R[0], ..., R[3] to contain the 64-bit plaintext value.
+    //
+    R0 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[0] );
+    R1 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[2] );
+    R2 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[4] );
+    R3 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[6] );
+
+    //
+    //  2. Expand the key, so that words K[0], ..., K[63] become defined.
+    //  (In our case the key was previously expanded, so we just grab the pointer to it.)
+    //
+    K = pExpandedKey->K;
+
+    //
+    //  3. Initialize j to zero.
+    //
+
+    //
+    //  4. Perform five mixing rounds.
+    //
+    MIXROUND(0);
+    MIXROUND(1);
+    MIXROUND(2);
+    MIXROUND(3);
+    MIXROUND(4);
+
+    //
+    //  5. Perform one mashing round.
+    //
+    MASHROUND();
+
+    //
+    //  6. Perform six mixing rounds.
+    //
+    MIXROUND(5);
+    MIXROUND(6);
+    MIXROUND(7);
+    MIXROUND(8);
+    MIXROUND(9);
+    MIXROUND(10);
+
+    //
+    //  7. Perform one mashing round.
+    //
+    MASHROUND();
+
+    //
+    //  8. Perform five mixing rounds.
+    //
+    MIXROUND(11);
+    MIXROUND(12);
+    MIXROUND(13);
+    MIXROUND(14);
+    MIXROUND(15);
+
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[0], R0 );
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[2], R1 );
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[4], R2 );
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[6], R3 );
+
+}
+
+
+#define RMIXROUND( n ) {\
+            R3 = ROR16( R3, 5 ); \
+            R3 = R3 - K[4*n+3] - (R2 & R1) - (~R2 & R0 ); \
+            R2 = ROR16( R2, 3 ); \
+            R2 = R2 - K[4*n+2] - (R1 & R0) - (~R1 & R3); \
+            R1 = ROR16( R1, 2 ); \
+            R1 = R1 - K[4*n+1] - (R0 & R3) - (~R0 & R2 ); \
+            R0 = ROR16( R0, 1 ); \
+            R0 = R0 - K[4*n  ] - (R3 & R2) - (~R3 & R1); \
+        }
+
+#define RMASHROUND() { \
+        R3 = R3 - K[R2 & 63]; \
+        R2 = R2 - K[R1 & 63]; \
+        R1 = R1 - K[R0 & 63]; \
+        R0 = R0 - K[R3 & 63]; \
+    }
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc2Decrypt(
+    _In_                                    PCSYMCRYPT_RC2_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_RC2_BLOCK_SIZE )  PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_RC2_BLOCK_SIZE ) PBYTE                       pbDst )
+{
+    UINT16 R0, R1, R2, R3;
+    PCUINT16 K;
+
+    SYMCRYPT_CHECK_MAGIC( pExpandedKey );
+
+    //
+    //  1. Initialize words R[0], ..., R[3] to contain the 64-bit plaintext value.
+    //
+    R0 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[0] );
+    R1 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[2] );
+    R2 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[4] );
+    R3 = SYMCRYPT_LOAD_LSBFIRST16( &pbSrc[6] );
+
+    //
+    //  2. Expand the key, so that words K[0], ..., K[63] become defined.
+    //  (In our case the key was previously expanded, so we just grab the pointer to it.)
+    //
+    K = pExpandedKey->K;
+
+    //
+    //  3. Initialize j to 63.
+    //
+
+    //
+    //  4. Perform five r-mixing rounds.
+    //
+    RMIXROUND(15);
+    RMIXROUND(14);
+    RMIXROUND(13);
+    RMIXROUND(12);
+    RMIXROUND(11);
+
+    //
+    //  5. Perform one r-mashing round.
+    //
+    RMASHROUND();
+
+    //
+    //  6. Perform six r-mixing rounds.
+    //
+    RMIXROUND(10);
+    RMIXROUND(9);
+    RMIXROUND(8);
+    RMIXROUND(7);
+    RMIXROUND(6);
+    RMIXROUND(5);
+
+    //
+    //  7. Perform one r-mashing round.
+    //
+    RMASHROUND();
+
+    //
+    //  8. Perform five r-mixing rounds.
+    //
+    RMIXROUND(4);
+    RMIXROUND(3);
+    RMIXROUND(2);
+    RMIXROUND(1);
+    RMIXROUND(0);
+
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[0], R0 );
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[2], R1 );
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[4], R2 );
+    SYMCRYPT_STORE_LSBFIRST16( &pbDst[6], R3 );
+
+}
+
+static const BYTE testPlaintext[8] = { 'P', 'l', 'a', 'i', 'n', 't', 'x', 't', };
+static const BYTE testCiphertext[8] = {
+    0x89, 0xe8, 0x5d, 0x1a, 0x98, 0xcd, 0xe5, 0x52,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc2Selftest(void)
+{
+    SYMCRYPT_RC2_EXPANDED_KEY key;
+    BYTE buf[SYMCRYPT_RC2_BLOCK_SIZE];
+
+    if( SymCryptRc2ExpandKeyEx( &key, SymCryptTestKey32, 16, 87) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'rc21' );
+    }
+
+    SymCryptRc2Encrypt( &key, testPlaintext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_RC2_BLOCK_SIZE );
+
+    if( memcmp( buf, testCiphertext, SYMCRYPT_RC2_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'rc22' );
+    }
+
+    SymCryptRc2Decrypt( &key, testCiphertext, buf );
+
+    SymCryptInjectError( buf, SYMCRYPT_RC2_BLOCK_SIZE );
+
+    if( memcmp( buf, testPlaintext, SYMCRYPT_RC2_BLOCK_SIZE ) != 0 )
+    {
+        SymCryptFatal( 'rc23' );
+    }
+
+}
diff --git a/libs/symcrypt/lib/rc4.c b/libs/symcrypt/lib/rc4.c
new file mode 100644
index 000000000000..914771c5aa54
--- /dev/null
+++ b/libs/symcrypt/lib/rc4.c
@@ -0,0 +1,156 @@
+//
+// Rc4.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This is a new implementation, NOT based on the existing ones in RSA32.lib.
+// The algorithm specification is taken from "ARCFOUR Algorithm" internet
+// draft dated July 1999, and from memory.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRc4Init(
+    _Out_                   PSYMCRYPT_RC4_STATE pState,
+    _In_reads_( cbKey )     PCBYTE              pbKey,
+    _In_                    SIZE_T              cbKey )
+{
+    SIZE_T i;
+    SIZE_T j;
+    BYTE keyBuf[256];
+    SIZE_T keyIdx;
+
+    SYMCRYPT_RC4_S_TYPE T;
+
+    if( cbKey > 256 || cbKey == 0 )
+    {
+        return SYMCRYPT_WRONG_KEY_SIZE;
+    }
+
+    //
+    // Make a copy of the key to obey the read-once rule.
+    //  This is a case where it looks safe to break the read-once
+    //  rule, but it isn't. RC4 with very long keys (e.g. 256 bytes)
+    //  is actually very vulnerable against related-key attacks.
+    //  One obvious precaution is to limit the length of the RC4 key,
+    //  which one of the layers above us might do.
+    //  Allowing the key bytes to change as we read them negates
+    //  this countermeasure.
+    //
+    memcpy( keyBuf, pbKey, cbKey );
+
+    for( i=0; i<256; i++ )
+    {
+        pState->S[i] = (SYMCRYPT_RC4_S_TYPE) i;
+    }
+
+    j = 0;
+    keyIdx = 0;
+    for( i=0; i<256; i++ )
+    {
+
+        T = pState->S[i];
+        j = (j + T + keyBuf[keyIdx]) & 0xff;
+        pState->S[i] = pState->S[j];
+        pState->S[j] = T;
+        keyIdx++;
+        if( keyIdx == cbKey )
+        {
+            keyIdx = 0;
+        }
+    }
+
+    //
+    // We store the i value already incremented for the next byte.
+    // This seems to allow better instruction sequencing interleaving in the actual en/decrypt loop
+    //
+    pState->i = 1;
+    pState->j = 0;
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+    SymCryptWipe( keyBuf, cbKey );
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc4Crypt(
+    _Inout_                 PSYMCRYPT_RC4_STATE pState,
+    _In_reads_( cbData )   PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+    _In_                    SIZE_T              cbData )
+{
+    SIZE_T i;
+    SIZE_T j;
+    SYMCRYPT_RC4_S_TYPE Ti;
+    SYMCRYPT_RC4_S_TYPE Tj;
+    PCBYTE pbSrcEnd = pbSrc + cbData;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    i = pState->i;
+    j = pState->j;
+
+    //
+    // I tried to unroll this loop 4x and use a single 32-bit operation to XOR the key
+    // stream with the data. This actually makes the code slower by 1 c/B on a Core 2.
+    // I suspect that that is because the instruction decoders are the bottleneck, and
+    // a small loop can be run out of the uop queue which bypasses the instruction decoders.
+    // A larger loop has to be decoded every time, and that slows things down.
+    // The theoretical gain of unrolling the loop is less than 1 c/B,
+    // and as Core 2 and derived CPUs are the most commonly used CPUs by our customers,
+    // it is not worthwhile to persue this further.
+    //
+    //  - Niels Ferguson (niels) 2010-10-11
+    //
+
+    while( pbSrc < pbSrcEnd )
+    {
+        //
+        // Our i value is already incremented
+        //
+        Ti = pState->S[i];
+        j = (j + Ti ) & 0xff;
+        Tj = pState->S[j];
+        pState->S[i] = Tj;
+        pState->S[j] = Ti;
+        *pbDst = (BYTE) (*pbSrc ^ pState->S[(Ti + Tj) & 0xff]);
+
+        i = (i + 1) & 0xff;
+
+        pbSrc++;
+        pbDst++;
+    }
+
+    pState->i = (BYTE) i;
+    pState->j = (BYTE) j;
+}
+
+
+static const BYTE   rc4KatAnswer[ 3 ] = { 0x71, 0x46, 0x92 };
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRc4Selftest(void)
+{
+    BYTE buf[3];
+    SYMCRYPT_RC4_STATE  state;
+
+    SymCryptRc4Init( &state, SymCryptTestKey32, sizeof( SymCryptTestKey32 ) );
+
+    SymCryptRc4Crypt( &state, SymCryptTestMsg3, buf, sizeof( buf ) );
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+
+    if( memcmp( buf, rc4KatAnswer, sizeof( buf )) != 0 )
+    {
+        SymCryptFatal( 'rc4 ' );
+    }
+
+}
diff --git a/libs/symcrypt/lib/rdrand.c b/libs/symcrypt/lib/rdrand.c
new file mode 100644
index 000000000000..79e8d951e3ae
--- /dev/null
+++ b/libs/symcrypt/lib/rdrand.c
@@ -0,0 +1,172 @@
+//
+// rdrand.c  Support for RdRand instruction
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#if (SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64)  // only available on x86 and amd64 architectures
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("rdrnd"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("rdrnd")
+#endif
+
+#if SYMCRYPT_MS_VC && _MSC_VER < 1610
+#error MSVC version lacks support for RDRAND intrinsics. Compile for the generic environment instead.
+#endif
+
+//
+// TODO: the _rdrand_u*() versions of the intrinsics can be removed once the new compiler
+// with the _rdrand*_step() intrinsics is used in all branches
+
+#if SYMCRYPT_MS_VC && _MSC_VER < 1700				// 1700 = Dev11,
+
+//
+// This is the code that uses the old intrinsics in the compiler version 16.1
+//
+
+unsigned int _rdrand_u32(void);
+unsigned __int64 _rdrand_u64(void);
+
+
+#if SYMCRYPT_CPU_X86
+#define SymCryptRdrandSizet(p) ( *(p)=(SIZE_T)_rdrand_u32(), SYMCRYPT_NO_ERROR )
+#else
+#define SymCryptRdrandSizet(p) ( *(p)=(SIZE_T)_rdrand_u64(), SYMCRYPT_NO_ERROR )
+#endif
+
+#else	// _MSC_VER
+
+//
+// Code for the new Dev11 intrinsics
+//
+
+#if SYMCRYPT_CPU_X86
+#define _rdrandxx_step(_p) _rdrand32_step( (unsigned int *) (_p) )
+#else
+#define _rdrandxx_step(_p) _rdrand64_step( (UINT64  *) (_p) )
+#endif
+
+FORCEINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdrandSizet( SIZE_T * p )
+{
+	int i;
+
+    //
+    // In Win8/WinBlue we iterated 1000 times.
+    // But we got a crash bucket where we fail because of
+    // not getting any random data.
+    // I contacted the Intel people; according to them they cannot make the
+    // RDRAND instruction fail more than a dozen times in a row under any tested
+    // circumstance. They have no idea how it could fail 1000 times in a row.
+    // As a failure of this code leads to a bugcheck (it fails a security promise, and
+    // is therefore treated as a critical security bug) I have increased the
+    // iteration count to 1000000.
+    // This will not affect any machine that didn't bugcheck before, but it hopefully
+    // will remove some of the current bugchecks.
+    //
+    // Niels Ferguson (niels) 2014-04-09.
+    //
+	for( i=0; i<1000000; i++ )
+	{
+		if( _rdrandxx_step( p ) != 0 )
+		{
+			return SYMCRYPT_NO_ERROR;
+		}
+	}
+	return SYMCRYPT_HARDWARE_FAILURE;
+}
+
+#endif
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdrandStatus(void)
+{
+    //
+    // Check that the library is initialized; otherwise the CPUID info
+    // is all zeroes. (This check only happens in CHKed builds.)
+    //
+    SymCryptCheckLibraryInitialized();
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_RDRAND ) )
+    {
+        return SYMCRYPT_NO_ERROR;
+    }
+    else
+    {
+        return SYMCRYPT_NOT_IMPLEMENTED;
+    }
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdrandGetBytes(
+    _Out_writes_( cbBuffer )                    PBYTE   pbBuffer,
+                                                SIZE_T  cbBuffer,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE   pbResult )
+{
+    SIZE_T * pBuf;
+    SIZE_T nBuf;
+    SIZE_T i;
+	SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    //
+    // Take care of the obvious errors that can happen
+    //
+    if( SymCryptRdrandStatus() != SYMCRYPT_NO_ERROR ||
+        (cbBuffer & 0xf) != 0
+      )
+    {
+        SymCryptFatal( 'rdrn' );
+    }
+
+    pBuf = (SIZE_T *) pbBuffer;
+    nBuf = cbBuffer / sizeof( SIZE_T );
+
+    for( i=0; i<nBuf; i++ )
+    {
+		scError = SymCryptRdrandSizet( &pBuf[i] );
+		if( scError != SYMCRYPT_NO_ERROR )
+		{
+			goto cleanup;
+		}
+    }
+
+    SymCryptSha512( pbBuffer, cbBuffer, pbResult );
+
+cleanup:
+    SymCryptWipe( pbBuffer, cbBuffer );
+
+	return scError;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRdrandGet(
+    _Out_writes_( cbBuffer )                    PBYTE   pbBuffer,
+                                                SIZE_T  cbBuffer,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE   pbResult )
+{
+	if( SymCryptRdrandGetBytes( pbBuffer, cbBuffer, pbResult ) != SYMCRYPT_NO_ERROR )
+	{
+		SymCryptFatal( 'rdrx' );
+	}
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif // SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
diff --git a/libs/symcrypt/lib/rdseed.c b/libs/symcrypt/lib/rdseed.c
new file mode 100644
index 000000000000..5e1f94f7809a
--- /dev/null
+++ b/libs/symcrypt/lib/rdseed.c
@@ -0,0 +1,135 @@
+//
+// rdseed.c  Support for RdSeed instruction
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#if (SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64) // only available on x86 and amd64 architectures.
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("rdseed"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("rdseed")
+#endif
+
+#if SYMCRYPT_MS_VC && _MSC_VER < 1610
+#error MSVC version lacks support for RDSEED intrinsics. Compile for the generic environment instead.
+#endif
+
+//
+// Create a definition that works on SIZE_Ts.
+//
+
+#if SYMCRYPT_CPU_X86
+#define _rdseedxx_step(_p) _rdseed32_step( (unsigned int *) (_p) )
+#else
+#define _rdseedxx_step(_p) _rdseed64_step( (UINT64  *) (_p) )
+#endif
+
+FORCEINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdseedSizet( SIZE_T * p )
+{
+	int i;
+
+    //
+    // There is no way to report errors, and customers rely on the RNG to work properly.
+    // Therefore, higher layers will fatal if this function fails.
+    // This is why we have a very high retry count; the alternative is to fatal.
+    //
+    //
+	for( i=0; i<10000000; i++ )
+	{
+		if( _rdseedxx_step( p ) != 0 )
+		{
+			return SYMCRYPT_NO_ERROR;
+		}
+	}
+	return SYMCRYPT_HARDWARE_FAILURE;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdseedStatus(void)
+{
+    //
+    // Check that the library is initialized; otherwise the CPUID info
+    // is all zeroes. (This check only happens in CHKed builds.)
+    //
+    SymCryptCheckLibraryInitialized();
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_RDSEED ) )
+    {
+        return SYMCRYPT_NO_ERROR;
+    }
+    else
+    {
+        return SYMCRYPT_NOT_IMPLEMENTED;
+    }
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRdseedGetBytes(
+    _Out_writes_( cbResult )                    PBYTE   pbResult,
+                                                SIZE_T  cbResult )
+{
+    SIZE_T * pBuf;
+    SIZE_T nBuf;
+    SIZE_T i;
+	SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    //
+    // Take care of the obvious errors that can happen
+    //
+    if( SymCryptRdseedStatus() != SYMCRYPT_NO_ERROR ||
+        (cbResult & 0xf) != 0
+      )
+    {
+        SymCryptFatal( 'rdsd' );
+    }
+
+    pBuf = (SIZE_T *) pbResult;
+    nBuf = cbResult / sizeof( SIZE_T );
+
+    for( i=0; i<nBuf; i++ )
+    {
+		scError = SymCryptRdseedSizet( &pBuf[i] );
+		if( scError != SYMCRYPT_NO_ERROR )
+		{
+			goto cleanup;
+		}
+    }
+
+cleanup:
+
+	return scError;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptRdseedGet(
+    _Out_writes_( cbResult )                    PBYTE   pbResult,
+                                                SIZE_T  cbResult )
+{
+	if( SymCryptRdseedGetBytes( pbResult, cbResult ) != SYMCRYPT_NO_ERROR )
+	{
+		SymCryptFatal( 'rdsx' );
+	}
+}
+
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif // SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
diff --git a/libs/symcrypt/lib/recoding.c b/libs/symcrypt/lib/recoding.c
new file mode 100644
index 000000000000..4341c5c8fffb
--- /dev/null
+++ b/libs/symcrypt/lib/recoding.c
@@ -0,0 +1,209 @@
+//
+// recoding.c   Algorithms for recoding the factors / exponents in various implementations
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+//
+
+#include "precomp.h"
+
+//
+// The following is an adaptation of algorithm 6: "Protected
+// odd-only recoding algorithm for the fixed-window representation"
+// from the paper
+//      "Selecting Elliptic Curves for Cryptography: An Efficiency and
+//       Security Analysis" by Bos, Costello, Longa, and Naehrig
+//
+// Input:   odd integer k \in [1,GOrd], window width w>=2, and
+//          t = ceil( GOrdBitsize / w-1 )
+//
+// Output:  (k_t, ... , k_0) where k_i \in {+-1, +-3, ..., +-(2^(w-1) -1)}
+//
+// Algorithm:
+//          for i=0 to (t-1) do
+//              k_i = (k mod 2^w) - 2^(w-1)
+//              k = (k-k_i)/2^(w-1)
+//          k_t = k mod 2^(w-1)
+//          return (k_t, ..., k_0)
+//
+// Remarks:
+//          1. An invariant of the main loop is that (k > 0 and k odd). This means
+//             that all k_i's are odd and that k_t > 0.
+//          2. We will store the values of k_i's as absolute values and signs in
+//             absofKIs and sigofKIs arrays, resp. The sigofKIs[i] is 0xffffffff if
+//             k_i < 0, otherwise it is 0.
+//          3. In the multiplication algorithm we always access the precomputed point
+//             P[(|k_i|-1)/2]. Therefore here we just shift the |k_i| value left by
+//             one bit before storing it in absofKIs table.
+//          4. Caller should check k in range [1,GOrd] to ensure use of recoding will
+//             give correct results. This algorithm always recodes the t * (w-1) least
+//             significant bits of the provided k, interpreted as an unsigned integer.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptFixedWindowRecoding(
+            UINT32          W,
+    _Inout_ PSYMCRYPT_INT   piK,
+    _Inout_ PSYMCRYPT_INT   piTmp,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         absofKIs,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         sigofKIs,
+            UINT32          nRecodedDigits )
+{
+    UINT32 T1 = 0;
+    UINT32 T2 = 0;
+    UINT32 mask = ~(0xffffffff << W);       // Window mask = 2^w - 1 (e.g. 0x0000003f for w = 6)
+    UINT32 smask = 0x1 << (W-1);            // Sign mask   = 2^(w-1) (e.g. 0x00000020 for w = 6)
+
+    SYMCRYPT_ASSERT( W < 32 );
+
+    for (UINT32 i=0; i < nRecodedDigits - 1; i++)
+    {
+        T1 = SymCryptIntGetValueLsbits32( piK ) & mask;         // T1 = k mod 2^W
+
+        // At this point if the w-th bit of T1 is 1 then we know that T1 > 2^(w-1)
+        // (Since k = odd is a loop invariant).
+        //
+        // In this case, (case A), T1 & ~smask is equal to (k mod 2^w) - 2^(w-1) = k_i = |k_i|.
+        //
+        // Otherwise, (case B), we know that T1 < 2^(w-1). Therefore 2^(w-1) - T1 = |k_i|.
+
+        sigofKIs[i] = SYMCRYPT_MASK32_ZERO( T1 & smask );       // If the sign of k_i is - this mask is set to 0xffffffff. (Case B)
+
+        T2 = T1 & ~smask;                                       // |k_i| in case A
+        T1 = smask - T1;                                        // |k_i| in case B
+
+        absofKIs[i] = ((T1 & sigofKIs[i]) | (T2 & ~sigofKIs[i])) >> 1; // Setting (masked) the absolute value of k_i in absofKIs (divided by 2)
+
+        SymCryptIntSubUint32( piK, T2, piTmp );                 // This gives k - k_i in case (A)
+        SymCryptIntAddUint32( piK, T1, piK );                   // This gives k - k_i in case (B)
+
+        SymCryptIntMaskedCopy( piTmp, piK, ~sigofKIs[i] );      // Copy the result to piK in case (B)
+
+        SymCryptIntDivPow2( piK, W-1, piK );                    // k := k / 2^(w-1)
+    }
+
+    // The last sign is positive given k < GOrd => k_t < 2^w
+    sigofKIs[nRecodedDigits - 1] = 0;
+    // Belts and braces, select only the bottom w-1 bits (ensure all absofKIs represent odd values in range [1,2^(w-1)-1])
+    absofKIs[nRecodedDigits - 1] = (SymCryptIntGetValueLsbits32( piK ) & mask & ~smask) >> 1;
+}
+
+//
+// The following is an algorithm for computing the width-w NAF of a positive integer.
+//
+// Input:   integer k \in [1,GOrd), window width w>=2, and nRecodedDigits = GOrdBitsize + 1
+//
+// Output:  (k_(nRecodedDigits-1), ... , k_0) where k_i \in {0, +-1, +-3, ..., +-(2^(w-1) -1)}
+//
+// Algorithm:
+//          for i = 0 to (nRecodedDigits-1)
+//              if (k is odd)
+//                  k_i = (k mods 2^w)
+//                  k = k - k_i
+//              else
+//                  k_i = 0
+//              k = k/2
+//          return (k_(nRecodedDigits-1), ..., k_0)
+//
+// Note: k mods 2^w is the integer u with (u == k mod 2^w) and (-2^(w-1) <= u < 2^(w-1) ).
+//
+// Remarks:
+//          1. The above algorithm and the implementation are NOT SIDE-CHANNEL SAFE.
+//             Therefore, it should only be used when the SYMCRYPT_FLAG_DATA_PUBLIC is
+//             specified.
+//          2. The multiplication algorithm uses |k_i|/2 as indexes. Therefore we will shift left
+//             the absolute value of k_i by 1 bit and store only |k_i|/2.
+//          3. Since now the k_i's can be zero we will store the following in sigofKIs:
+//                  sigofKIs[i] = 0x00000001    if k_i > 0
+//                  sigofKIs[i] = 0x00000000    if k_i = 0
+//                  sigofKIs[i] = 0xffffffff    if k_i < 0
+//
+VOID
+SYMCRYPT_CALL
+SymCryptWidthNafRecoding(
+            UINT32          W,
+    _Inout_ PSYMCRYPT_INT   piK,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         absofKIs,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         sigofKIs,
+            UINT32          nRecodedDigits )
+{
+    UINT32 T1 = 0;
+    UINT32 mask = ~(0xffffffff << W);       // Window mask = 2^w - 1 (e.g. 0x0000003f for w = 6)
+    UINT32 modulus = mask + 1;              // 2^w
+    UINT32 smask = 0x1 << (W-1);            // Sign mask   = 2^(w-1) (e.g. 0x00000020 for w = 6)
+
+    SYMCRYPT_ASSERT( W < 32 );
+
+    for (UINT32 i=0; i < nRecodedDigits; i++)
+    {
+        T1 = SymCryptIntGetValueLsbits32( piK ) & mask;         // T1 = k mod 2^W
+
+        if (T1 & 0x1)
+        {
+            if (T1 > smask)
+            {
+                sigofKIs[i] = 0xffffffff;
+                absofKIs[i] = modulus - T1;                         // 2^W - T1 = |T1 - 2^W|
+                SymCryptIntAddUint32( piK, absofKIs[i], piK );      // k-k_i
+            }
+            else
+            {
+                // Here (k mod 2^W) is already in the specified range
+                sigofKIs[i] = 0x00000001;
+                absofKIs[i] = T1;
+                SymCryptIntSubUint32( piK, absofKIs[i], piK );      // k-k_i
+            }
+        }
+        else
+        {
+            absofKIs[i] = 0;
+            sigofKIs[i] = 0;
+        }
+
+        SymCryptIntDivPow2( piK, 1, piK );                      // k := k / 2
+    }
+}
+
+//
+// The following is an algorithm similar to the above
+// but the output is only non-negative (odd) digits.
+//
+// Requirements:
+//      nRecodedDigits == nBitsExp
+//
+VOID
+SYMCRYPT_CALL
+SymCryptPositiveWidthNafRecoding(
+            UINT32          W,
+    _In_    PCSYMCRYPT_INT  piK,
+            UINT32          nBitsExp,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         absofKIs,
+            UINT32          nRecodedDigits )
+{
+    UINT32 T1 = 0;
+    UINT32 cntrZ = W;       // Counter that specifies when we filled the last non-zero NAF digit
+
+    SYMCRYPT_ASSERT( nRecodedDigits <= SymCryptIntBitsizeOfObject( piK ) );
+
+    for (UINT32 i=0; i < nRecodedDigits; i++)
+    {
+        T1 = SymCryptIntGetBits( piK, i, SYMCRYPT_MIN(W, nBitsExp-i) );   // Get a batch of W bits (but don't go over nBitsExp)
+
+        if ((cntrZ>=W) && ((T1 & 0x01) > 0))    // Only store odd digits
+        {
+            absofKIs[i] = T1;
+            cntrZ = 0;
+        }
+        else
+        {
+            absofKIs[i] = 0;
+        }
+
+        cntrZ++;        // Prepare the counter for the next iteration
+    }
+}
diff --git a/libs/symcrypt/lib/rsa_enc.c b/libs/symcrypt/lib/rsa_enc.c
new file mode 100644
index 000000000000..a76e663d9234
--- /dev/null
+++ b/libs/symcrypt/lib/rsa_enc.c
@@ -0,0 +1,1531 @@
+//
+// rsa_enc.c   RSA related algorithms
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// Helper functions for RSA raw encrypt/decrypt (they do NOT allocate scratch space)
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsaCoreEncScratchSpace( _In_ PCSYMCRYPT_RSAKEY pkRsakey)
+{
+	// Bounded by 2^19 + 2^24 ~ 2^24 (see symcrypt_internal.h)
+    return SymCryptSizeofModElementFromModulus( pkRsakey->pmModulus ) +
+           SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus ),
+                SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pkRsakey->nDigitsOfModulus ) );
+}
+
+SYMCRYPT_ERROR
+SymCryptRsaCoreVerifyInput(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                SIZE_T                      cbDst,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch)
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+    PSYMCRYPT_INT           piTmpInteger = NULL;
+    UINT32                  cbTmpInteger = 0;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    if ( cbSrc > SymCryptRsakeySizeofModulus(pkRsakey) ||
+         cbDst < SymCryptRsakeySizeofModulus(pkRsakey) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // It is an error of value(pbSrc) >= modulus
+    // We already know that cbSrc <= sizeof( modulus ) so we only have to run this check
+    // if cbSrc == sizeof( modulus )
+    // No side channel issues here: we are only comparing the input to the public part of the key.
+    if (cbSrc == SymCryptRsakeySizeofModulus(pkRsakey))
+    {
+        cbTmpInteger = SymCryptSizeofIntFromDigits( pkRsakey->nDigitsOfModulus );
+        SYMCRYPT_ASSERT( cbScratch >= cbTmpInteger );
+        piTmpInteger = SymCryptIntCreate( pbScratch, cbTmpInteger, pkRsakey->nDigitsOfModulus );
+
+        scError = SymCryptIntSetValue( pbSrc, cbSrc, numFormat, piTmpInteger );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+           goto cleanup;
+        }
+
+        if (!SymCryptIntIsLessThan(piTmpInteger, SymCryptIntFromModulus(pkRsakey->pmModulus)))
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+   return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaCoreEnc(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_MODELEMENT peRes = NULL;
+    UINT32 cbModElement = 0;
+
+    PBYTE   pbFnScratch = NULL;
+    SIZE_T  cbFnScratch = 0;
+
+    BYTE    abExpIntBuffer[ SYMCRYPT_SIZEOF_INT_FROM_BITS( 64 ) + SYMCRYPT_ASYM_ALIGN_VALUE];
+    PSYMCRYPT_INT piExp = NULL;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    scError = SymCryptRsaCoreVerifyInput(pkRsakey, pbSrc, cbSrc, numFormat, cbDst, pbScratch, cbScratch);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    cbModElement = SymCryptSizeofModElementFromModulus( pkRsakey->pmModulus );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= cbModElement +
+                                  SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus ),
+                                       SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pkRsakey->nDigitsOfModulus ) ));
+
+    pbFnScratch = pbScratch;
+    cbFnScratch = cbScratch;
+
+    peRes = SymCryptModElementCreate( pbScratch, cbModElement, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peRes != NULL );
+    pbFnScratch += cbModElement;
+    cbFnScratch -= cbModElement;
+
+    // Set the original value
+    scError = SymCryptModElementSetValue( pbSrc, cbSrc, numFormat, pkRsakey->pmModulus, peRes, pbFnScratch, cbFnScratch );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Convert the public exponent to an Int
+    // Future: we can optimize the ModExp to take an UINT64
+    piExp = SymCryptIntCreate( SYMCRYPT_ASYM_ALIGN_UP(abExpIntBuffer), sizeof( abExpIntBuffer) - SYMCRYPT_ASYM_ALIGN_VALUE, 1 );
+    if( piExp == NULL )
+    {
+        scError = SYMCRYPT_HARDWARE_FAILURE;
+        goto cleanup;
+    }
+    SymCryptIntSetValueUint64( pkRsakey->au64PubExp[0], piExp );
+
+    // Modular Exponentiation
+    SymCryptModExp(
+            pkRsakey->pmModulus,
+            peRes,
+            piExp,
+            SymCryptIntBitsizeOfValue( piExp ),   // This is a public value
+            SYMCRYPT_FLAG_DATA_PUBLIC,
+            peRes,
+            pbFnScratch,
+            cbFnScratch );
+
+    // Output the value
+    scError = SymCryptModElementGetValue( pkRsakey->pmModulus, peRes, pbDst, cbDst, numFormat, pbFnScratch, cbFnScratch );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    if( piExp != NULL )
+    {
+        SymCryptIntWipe( piExp );
+    }
+
+    return scError;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsaCoreDecCrtScratchSpace( _In_ PCSYMCRYPT_RSAKEY pkRsakey)
+{
+    UINT32 cbModElementTotal = 0;
+    UINT32 nPrimes = pkRsakey->nPrimes;
+
+    SYMCRYPT_ASSERT( nPrimes <= SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES );
+    // clamp nPrimes to SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES for scratch memory allocation purposes
+    // SymCryptRsaCoreDecCrt will fail with invalid argument if there are too many primes later
+    nPrimes = SYMCRYPT_MIN( nPrimes, SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES );
+
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        cbModElementTotal += SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pkRsakey->nBitsOfPrimes[i]);
+    }
+
+    // Bounded by 5*2^19 + 2^24 ~ 2^24 (see symcrypt_internal.h)
+    return 3*SymCryptSizeofIntFromDigits( pkRsakey->nDigitsOfModulus ) +
+           SymCryptSizeofIntFromDigits( pkRsakey->nMaxDigitsOfPrimes ) +
+           cbModElementTotal +
+           SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pkRsakey->nBitsOfModulus) +
+           SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus ),
+           SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pkRsakey->nDigitsOfModulus ),
+           SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( pkRsakey->nDigitsOfModulus, pkRsakey->nMaxDigitsOfPrimes ),
+                SYMCRYPT_SCRATCH_BYTES_FOR_CRT_SOLUTION( pkRsakey->nMaxDigitsOfPrimes ) )));
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsaCoreDecScratchSpace( _In_ PCSYMCRYPT_RSAKEY pkRsakey)
+{
+	// Bounded by 2^19 + 2^24 ~ 2^24 (see symcrypt_internal.h)
+    return SymCryptSizeofModElementFromModulus( pkRsakey->pmModulus ) +
+           SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus ),
+                SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pkRsakey->nDigitsOfModulus ) );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaCoreDecCrt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_INT piCiphertext = NULL;
+    PSYMCRYPT_INT piPlaintext = NULL;
+    UINT32 cbInt = 0;
+
+    PSYMCRYPT_INT piTmp = NULL;
+    UINT32 cbTmp = 0;
+
+    PSYMCRYPT_MODELEMENT peCrtElements[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES] = { 0 };
+    UINT32 cbModElements[SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES] = { 0 };
+    UINT32 cbModElementTotal = 0;
+
+    // Used to verify decryption
+    PSYMCRYPT_INT piVerify = NULL;  // Size equal to cbInt
+    PSYMCRYPT_MODELEMENT peVerify = NULL;
+    UINT32 cbModElementVerify = 0;
+
+    PBYTE   pbFnScratch = NULL;
+    SIZE_T  cbFnScratch = 0;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key has a private key
+    if (!pkRsakey->hasPrivateKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaCoreVerifyInput(pkRsakey, pbSrc, cbSrc, numFormat, cbDst, pbScratch, cbScratch);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Verify that the number of primes does not cause a stack overflow
+    if (pkRsakey->nPrimes > SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbInt = SymCryptSizeofIntFromDigits( pkRsakey->nDigitsOfModulus );
+    cbTmp = SymCryptSizeofIntFromDigits( pkRsakey->nMaxDigitsOfPrimes );
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        cbModElements[i] = SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pkRsakey->nBitsOfPrimes[i]);
+        cbModElementTotal += cbModElements[i];
+    }
+
+    cbModElementVerify = SymCryptSizeofModElementFromModulus( pkRsakey->pmModulus );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - nPrimes is at most SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES = 2
+    // Thus the following calculation does not overflow cbScratch.
+    //
+    SYMCRYPT_ASSERT( cbScratch >=
+                3*cbInt + cbTmp + cbModElementTotal + cbModElementVerify +
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pkRsakey->nDigitsOfModulus ),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( pkRsakey->nDigitsOfModulus, pkRsakey->nMaxDigitsOfPrimes ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_CRT_SOLUTION( pkRsakey->nMaxDigitsOfPrimes ) ))) );
+
+    pbFnScratch = pbScratch;
+    cbFnScratch = cbScratch;
+
+    piPlaintext = SymCryptIntCreate( pbFnScratch, cbFnScratch, pkRsakey->nDigitsOfModulus );
+    SYMCRYPT_ASSERT( piPlaintext != NULL );
+    pbFnScratch += cbInt;
+    cbFnScratch -= cbInt;
+
+    piCiphertext = SymCryptIntCreate( pbFnScratch, cbFnScratch, pkRsakey->nDigitsOfModulus );
+    SYMCRYPT_ASSERT( piCiphertext != NULL );
+    pbFnScratch += cbInt;
+    cbFnScratch -= cbInt;
+
+    piTmp = SymCryptIntCreate( pbFnScratch, cbFnScratch, pkRsakey->nMaxDigitsOfPrimes );
+    SYMCRYPT_ASSERT( piTmp != NULL );
+    pbFnScratch += cbTmp;
+    cbFnScratch -= cbTmp;
+
+    SYMCRYPT_ASSERT( pkRsakey->nPrimes <= SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES );
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        peCrtElements[i] = SymCryptModElementCreate( pbFnScratch, cbFnScratch, pkRsakey->pmPrimes[i] );
+        SYMCRYPT_ASSERT( peCrtElements[i] != NULL );
+        pbFnScratch += cbModElements[i];
+        cbFnScratch -= cbModElements[i];
+    }
+
+    piVerify = SymCryptIntCreate( pbFnScratch, cbFnScratch, pkRsakey->nDigitsOfModulus );
+    SYMCRYPT_ASSERT( piVerify != NULL );
+    pbFnScratch += cbInt;
+    cbFnScratch -= cbInt;
+
+    peVerify = SymCryptModElementCreate( pbFnScratch, cbFnScratch, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peVerify != NULL );
+    pbFnScratch += cbModElementVerify;
+    cbFnScratch -= cbModElementVerify;
+
+    // Set the ciphertext
+    scError = SymCryptIntSetValue( pbSrc, cbSrc, numFormat, piCiphertext );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Modular exponentiations
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        // c mod the prime
+        // Note: For two equally sized primes we can use straight the faster SymCryptIntToModElement function
+        // but for now this is the general case.
+        SymCryptIntDivMod(
+                piCiphertext,
+                SymCryptDivisorFromModulus(pkRsakey->pmPrimes[i]),
+                NULL,
+                piTmp,
+                pbFnScratch,
+                cbFnScratch );
+
+        SymCryptIntToModElement( piTmp, pkRsakey->pmPrimes[i], peCrtElements[i], pbFnScratch, cbFnScratch );
+
+        // Modular Exponentiation
+        SymCryptModExp(
+                pkRsakey->pmPrimes[i],
+                peCrtElements[i],
+                pkRsakey->piCrtPrivExps[i],    // For now only the first exponent is allowed
+                pkRsakey->nBitsOfPrimes[i],    // This is a public value
+                0,                             // Side-channel safe modexp
+                peCrtElements[i],
+                pbFnScratch,
+                cbFnScratch );
+    }
+
+    // Solve the crt equations
+    scError = SymCryptCrtSolve(
+            pkRsakey->nPrimes,
+            (PCSYMCRYPT_MODULUS *) pkRsakey->pmPrimes,
+            (PSYMCRYPT_MODELEMENT *) pkRsakey->peCrtInverses,
+            peCrtElements,
+            0,
+            piPlaintext,
+            pbFnScratch,
+            cbFnScratch );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    /*
+        A hardware error during RSA decryption can leak the
+        prime factors.  For example, suppose the message
+        is M and you try to sign it with
+        M^d for some decryption exponent d.
+        Using the CRT, you compute M^d mod p correctly but
+        M^d mod q incorrectly.  Your supposed M^d (mod p*q) is
+        then raised to an encryption exponent e
+        by the verifier, detects an invalid signature.
+        The verifier can also find p via a GCD and factor the modulus.
+
+        To avoid this problem, re-encrypt the supposed M^d
+        and verify our own signature.
+   */
+
+    // Don't call the full encryption function just the modular exponentiation
+
+    SymCryptIntToModElement( piPlaintext, pkRsakey->pmModulus, peVerify, pbFnScratch, cbFnScratch );
+
+    SymCryptIntSetValueUint64( pkRsakey->au64PubExp[0], piTmp );
+
+    // Modular Exponentiation (Not side-channel safe)
+    SymCryptModExp(
+            pkRsakey->pmModulus,
+            peVerify,
+            piTmp,
+            SymCryptIntBitsizeOfValue( piTmp ),
+            SYMCRYPT_FLAG_DATA_PUBLIC,          // Exponent is public
+            peVerify,
+            pbFnScratch,
+            cbFnScratch );
+
+    SymCryptModElementToInt( pkRsakey->pmModulus, peVerify, piVerify, pbFnScratch, cbFnScratch );
+
+    if (!SymCryptIntIsEqual( piCiphertext, piVerify ))
+    {
+        scError = SYMCRYPT_HARDWARE_FAILURE;
+        goto cleanup;
+    }
+
+    // Output the result
+    scError = SymCryptIntGetValue( piPlaintext, pbDst, cbDst, numFormat );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaCoreDec(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PSYMCRYPT_MODELEMENT peRes = NULL;
+    UINT32 cbModElement = 0;
+
+    PBYTE   pbFnScratch = NULL;
+    SIZE_T  cbFnScratch = 0;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key has a private key
+    if ((cbSrc>SymCryptRsakeySizeofModulus(pkRsakey)) ||
+        (!pkRsakey->hasPrivateKey) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbModElement = SymCryptSizeofModElementFromModulus( pkRsakey->pmModulus );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= cbModElement +
+                     SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus ),
+                          SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( pkRsakey->nDigitsOfModulus ) ) );
+
+    pbFnScratch = pbScratch;
+    cbFnScratch = cbScratch;
+
+    peRes = SymCryptModElementCreate( pbScratch, cbModElement, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peRes != NULL );
+    pbFnScratch += cbModElement;
+    cbFnScratch -= cbModElement;
+
+    // Set the ciphertext
+    scError = SymCryptModElementSetValue( pbSrc, cbSrc, numFormat, pkRsakey->pmModulus, peRes, pbFnScratch, cbFnScratch );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Modular Exponentiation
+    SymCryptModExp(
+            pkRsakey->pmModulus,
+            peRes,
+            pkRsakey->piPrivExps[0],    // For now only the first exponent is allowed
+            pkRsakey->nBitsOfModulus,   // This is a public value
+            0,                          // Side-channel safe modexp
+            peRes,
+            pbFnScratch,
+            cbFnScratch );
+
+    // Output the value
+    scError = SymCryptModElementGetValue( pkRsakey->pmModulus, peRes, pbDst, cbDst, numFormat, pbFnScratch, cbFnScratch );
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+
+//
+// Encryption / decryption functions
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaRawEncrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    UINT32  cbScratch = 0;
+
+    // Make sure that the key may be used in Encrypt/Decrypt
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_ENCRYPT) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch = SymCryptRsaCoreEncScratchSpace( pkRsakey );
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaCoreEnc( pkRsakey, pbSrc, cbSrc, numFormat, flags, pbDst, cbDst, pbScratch, cbScratch );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaRawDecrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      numFormat,
+                                UINT32                      flags,
+    _Out_writes_( cbDst )       PBYTE                       pbDst,
+                                SIZE_T                      cbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    UINT32  cbScratch = 0;
+
+    // Make sure that the key may be used in Encrypt/Decrypt
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_ENCRYPT) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the key has a private key
+    if (!pkRsakey->hasPrivateKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+#define SYMCRYPT_CRT_DECRYPTION     (1)     // Set this to 0 to test the non-crt decryption
+
+    // Scratch space
+#if (SYMCRYPT_CRT_DECRYPTION)
+    cbScratch = SymCryptRsaCoreDecCrtScratchSpace( pkRsakey );
+#else
+    cbScratch = SymCryptRsaCoreDecScratchSpace( pkRsakey );
+#endif
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    scError = SymCryptRsaCoreDecCrt( pkRsakey, pbSrc, cbSrc, numFormat, flags, pbDst, cbDst, pbScratch, cbScratch );
+#else
+    scError = SymCryptRsaCoreDec( pkRsakey, pbSrc, cbSrc, numFormat, flags, pbDst, cbDst, pbScratch, cbScratch );
+#endif
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Encrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                UINT32                      flags,
+                                SYMCRYPT_NUMBER_FORMAT      nfDst,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    cbScratch = cbTmp + SymCryptRsaCoreEncScratchSpace( pkRsakey );
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key may be used in Encrypt/Decrypt
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_ENCRYPT) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    *pcbDst = cbTmp;
+
+    // Check if only *pcbDst is needed
+    if (pbDst == NULL)
+    {
+        scError = SYMCRYPT_NO_ERROR;
+        goto cleanup;
+    }
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    scError = SymCryptRsaPkcs1ApplyEncryptionPadding(
+                    pbSrc,
+                    cbSrc,
+                    pbTmp,
+                    cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaCoreEnc(
+                    pkRsakey,
+                    pbTmp,
+                    cbTmp,
+                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,   // Always MSB first for RSA OAEP
+                    flags,
+                    pbDst,
+                    cbDst,
+                    pbScratch,
+                    cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if (nfDst == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+// Ensure SymCryptRoundUpPow2Sizet below will not fail
+C_ASSERT((UINT32) ((SYMCRYPT_RSAKEY_MAX_BITSIZE_MODULUS + 7) / 8) <= ((SIZE_T_MAX / 2) + 1));
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Decrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      nfSrc,
+                                UINT32                      flags,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbModulus = SymCryptRsakeySizeofModulus(pkRsakey);
+    SIZE_T  cbTmp = SymCryptRoundUpPow2Sizet( cbModulus );      // tmp buffer needs to be a power of 2
+
+    // Make sure that the key may be used in Encrypt/Decrypt
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_ENCRYPT) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the key has a private key
+    if (!pkRsakey->hasPrivateKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    cbScratch = cbTmp + SymCryptRsaCoreDecCrtScratchSpace( pkRsakey );
+#else
+    cbScratch = cbTmp + SymCryptRsaCoreDecScratchSpace( pkRsakey );
+#endif
+
+    UNREFERENCED_PARAMETER( flags );
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    if (nfSrc == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    scError = SymCryptRsaCoreDecCrt(
+                pkRsakey,
+                pbSrc,
+                cbSrc,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbTmp,
+                cbModulus,
+                pbScratch,
+                cbScratch - cbTmp );
+#else
+        scError = SymCryptRsaCoreDec(
+                pkRsakey,
+                pbSrc,
+                cbSrc,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbTmp,
+                cbModulus,
+                pbScratch,
+                cbScratch - cbTmp );
+#endif
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaPkcs1RemoveEncryptionPadding(
+                pbTmp,
+                cbModulus,
+                cbTmp,
+                pbDst,
+                cbDst,
+                pcbDst );
+    // The error that is returned from the encryption padding is confidential data
+    // due to Bleichenbacher-style attacks.
+    // Make sure we don't create a side-channel leak for it.
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepEncrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_( cbLabel ) PCBYTE                      pbLabel,
+                                SIZE_T                      cbLabel,
+                                UINT32                      flags,
+                                SYMCRYPT_NUMBER_FORMAT      nfDst,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key may be used in Encrypt/Decrypt
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_ENCRYPT) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    *pcbDst = cbTmp;
+
+    // Check if only *pcbDst is needed
+    if (pbDst == NULL)
+    {
+        scError = SYMCRYPT_NO_ERROR;
+        goto cleanup;
+    }
+
+    // The SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP macro does not
+    // overflow cbScratch since cbTmp < 2^17.
+    cbScratch = cbTmp + SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP( hashAlgorithm, cbTmp ), SymCryptRsaCoreEncScratchSpace( pkRsakey ) );
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    scError = SymCryptRsaOaepApplyEncryptionPadding(
+                    pbSrc,
+                    cbSrc,
+                    hashAlgorithm,
+                    pbLabel,
+                    cbLabel,
+                    NULL,               // Seed
+                    0,                  // cbSeed
+                    pbTmp,
+                    cbTmp,
+                    pbScratch,
+                    cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaCoreEnc(
+                    pkRsakey,
+                    pbTmp,
+                    cbTmp,
+                    SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,   // Always MSB first for RSA OAEP
+                    flags,
+                    pbDst,
+                    cbDst,
+                    pbScratch,
+                    cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if (nfDst == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepDecrypt(
+    _In_                        PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT      nfSrc,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_( cbLabel ) PCBYTE                      pbLabel,
+                                SIZE_T                      cbLabel,
+                                UINT32                      flags,
+    _Out_writes_opt_( cbDst )   PBYTE                       pbDst,
+                                SIZE_T                      cbDst,
+    _Out_                       SIZE_T                      *pcbDst )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+    SIZE_T  cbDstResult = 0;        // We always return a value into *pcbDst
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Make sure that the key may be used in Encrypt/Decrypt
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_ENCRYPT) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbSrc > cbTmp)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the key has a private key
+    if (!pkRsakey->hasPrivateKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // The SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP macro does not
+    // overflow cbScratch since cbTmp < 2^17.
+#if (SYMCRYPT_CRT_DECRYPTION)
+    cbScratch = cbTmp + SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP( hashAlgorithm, cbSrc ), SymCryptRsaCoreDecCrtScratchSpace( pkRsakey ) );
+#else
+    cbScratch = cbTmp + SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_RSA_OAEP( hashAlgorithm, cbSrc ), SymCryptRsaCoreDecScratchSpace( pkRsakey ) );
+#endif
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    if (nfSrc == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    scError = SymCryptRsaCoreDecCrt(
+                pkRsakey,
+                pbSrc,
+                cbSrc,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbTmp,
+                cbTmp,
+                pbScratch,
+                cbScratch - cbTmp );
+#else
+    scError = SymCryptRsaCoreDec(
+                pkRsakey,
+                pbSrc,
+                cbSrc,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbTmp,
+                cbTmp,
+                pbScratch,
+                cbScratch - cbTmp );
+#endif
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaOaepRemoveEncryptionPadding(
+                    pbTmp,
+                    cbTmp,
+                    hashAlgorithm,
+                    pbLabel,
+                    cbLabel,
+                    flags,
+                    pbDst,
+                    cbDst,
+                    &cbDstResult,
+                    pbScratch,
+                    cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    *pcbDst = cbDstResult;
+
+    return scError;
+}
+
+//
+// Signing / Verification functions
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Sign(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_                                PCSYMCRYPT_OID              pHashOIDs,
+    _In_                                SIZE_T                      nOIDCount,
+                                        UINT32                      flags,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _Out_writes_opt_( cbSignature )     PBYTE                       pbSignature,
+                                        SIZE_T                      cbSignature,
+    _Out_                               SIZE_T                      *pcbSignature )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    PCBYTE pbOID = NULL;
+    SIZE_T cbOID = 0;
+
+    UNREFERENCED_PARAMETER(nOIDCount);
+
+    pbOID = pHashOIDs ? pHashOIDs->pbOID : NULL;
+    cbOID = pHashOIDs ? pHashOIDs->cbOID : 0;
+
+    // Make sure that the key may be used in Sign/Verify
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_SIGN) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the key has a private key
+    if (!pkRsakey->hasPrivateKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    *pcbSignature = cbTmp;
+
+    // Check if only *pcbSignature is needed
+    if (pbSignature == NULL)
+    {
+        scError = SYMCRYPT_NO_ERROR;
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    cbScratch = cbTmp + SymCryptRsaCoreDecCrtScratchSpace( pkRsakey );
+#else
+    cbScratch = cbTmp + SymCryptRsaCoreDecScratchSpace( pkRsakey );
+#endif
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    scError = SymCryptRsaPkcs1ApplySignaturePadding(
+                        pbHashValue,
+                        cbHashValue,
+                        pbOID,
+                        cbOID,
+                        flags,
+                        pbTmp,
+                        cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    scError = SymCryptRsaCoreDecCrt(
+                pkRsakey,
+                pbTmp,
+                cbTmp,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbSignature,
+                cbSignature,
+                pbScratch,
+                cbScratch - cbTmp );
+#else
+    scError = SymCryptRsaCoreDec(
+                pkRsakey,
+                pbTmp,
+                cbTmp,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbSignature,
+                cbSignature,
+                pbScratch,
+                cbScratch - cbTmp );
+#endif
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if (nfSignature == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1Verify(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                      pbSignature,
+                                        SIZE_T                      cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _In_reads_opt_( nOIDCount )         PCSYMCRYPT_OID              pHashOIDs,
+    _In_                                SIZE_T                      nOIDCount,
+                                        UINT32                      flags )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    // Make sure that the key may be used in Sign/Verify
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_SIGN) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbSignature > cbTmp)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (nfSignature == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    // The SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PKCS1 macro does not
+    // overflow cbScratch since cbTmp < 2^17.
+    cbScratch = cbTmp +
+                SYMCRYPT_MAX( SymCryptRsaCoreEncScratchSpace( pkRsakey ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PKCS1( cbTmp ) );
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    scError = SymCryptRsaCoreEnc(
+                pkRsakey,
+                pbSignature,
+                cbSignature,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbTmp,
+                cbTmp,
+                pbScratch,
+                cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaPkcs1VerifySignaturePadding(
+                    pbHashValue,
+                    cbHashValue,
+                    pHashOIDs,
+                    nOIDCount,
+                    pbTmp,
+                    cbTmp,
+                    flags,
+                    pbScratch,
+                    cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssSign(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_                                PCSYMCRYPT_HASH             hashAlgorithm,
+                                        SIZE_T                      cbSalt,
+                                        UINT32                      flags,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _Out_writes_opt_( cbSignature )     PBYTE                       pbSignature,
+                                        SIZE_T                      cbSignature,
+    _Out_                               SIZE_T                      *pcbSignature )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    // Make sure that the key may be used in Sign/Verify
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_SIGN) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ( (cbHashValue > cbTmp) ||
+         (cbSalt > cbTmp) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Make sure that the key has a private key
+    if (!pkRsakey->hasPrivateKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    *pcbSignature = cbTmp;
+
+    // Check if only *pcbSignature is needed
+    if (pbSignature == NULL)
+    {
+        scError = SYMCRYPT_NO_ERROR;
+        goto cleanup;
+    }
+
+    // The SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS macro does not
+    // overflow cbScratch since cbTmp < 2^17.
+#if (SYMCRYPT_CRT_DECRYPTION)
+    cbScratch = cbTmp +
+                SYMCRYPT_MAX( SymCryptRsaCoreDecCrtScratchSpace( pkRsakey ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS( hashAlgorithm, cbHashValue, cbTmp ) );
+#else
+    cbScratch = cbTmp +
+                SYMCRYPT_MAX( SymCryptRsaCoreDecScratchSpace( pkRsakey ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS( hashAlgorithm, cbHashValue, cbTmp ) );
+#endif
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    scError = SymCryptRsaPssApplySignaturePadding(
+                        pbHashValue,
+                        cbHashValue,
+                        hashAlgorithm,
+                        NULL,       // For now only random salt supported
+                        cbSalt,
+                        pkRsakey->nBitsOfModulus,
+                        flags,
+                        pbTmp,
+                        cbTmp,
+                        pbScratch,
+                        cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+#if (SYMCRYPT_CRT_DECRYPTION)
+    scError = SymCryptRsaCoreDecCrt(
+                pkRsakey,
+                pbTmp,
+                cbTmp,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbSignature,
+                cbSignature,
+                pbScratch,
+                cbScratch - cbTmp );
+#else
+    scError = SymCryptRsaCoreDec(
+                pkRsakey,
+                pbTmp,
+                cbTmp,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbSignature,
+                cbSignature,
+                pbScratch,
+                cbScratch - cbTmp );
+#endif
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if (nfSignature == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssVerify(
+    _In_                                PCSYMCRYPT_RSAKEY           pkRsakey,
+    _In_reads_bytes_( cbHashValue )     PCBYTE                      pbHashValue,
+                                        SIZE_T                      cbHashValue,
+    _In_reads_bytes_( cbSignature )     PCBYTE                      pbSignature,
+                                        SIZE_T                      cbSignature,
+                                        SYMCRYPT_NUMBER_FORMAT      nfSignature,
+    _In_                                PCSYMCRYPT_HASH             hashAlgorithm,
+                                        SIZE_T                      cbSalt,
+                                        UINT32                      flags )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PBYTE   pbScratch = NULL;
+    SIZE_T  cbScratch = 0;
+
+    PBYTE   pbTmp = NULL;
+    SIZE_T  cbTmp = SymCryptRsakeySizeofModulus(pkRsakey);
+
+    // Make sure that the key may be used in Sign/Verify
+    if ( (pkRsakey->fAlgorithmInfo & SYMCRYPT_FLAG_RSAKEY_SIGN) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ( (cbHashValue > cbTmp) ||
+         (cbSalt > cbTmp) ||
+         (cbSignature > cbTmp) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (nfSignature == SYMCRYPT_NUMBER_FORMAT_LSB_FIRST)
+    {
+        // To implement this revert the buffer properly
+        scError = SYMCRYPT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+    // The SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS macro does not
+    // overflow cbScratch since cbTmp < 2^17.
+    cbScratch = cbTmp +
+                SYMCRYPT_MAX( SymCryptRsaCoreEncScratchSpace( pkRsakey ),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_RSA_PSS( hashAlgorithm, cbHashValue, cbTmp ) );
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbTmp = pbScratch + cbScratch - cbTmp;
+
+    scError = SymCryptRsaCoreEnc(
+                pkRsakey,
+                pbSignature,
+                cbSignature,
+                SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
+                flags,
+                pbTmp,
+                cbTmp,
+                pbScratch,
+                cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptRsaPssVerifySignaturePadding(
+                pbHashValue,
+                cbHashValue,
+                hashAlgorithm,
+                cbSalt,
+                pbTmp,
+                cbTmp,
+                pkRsakey->nBitsOfModulus,
+                flags,
+                pbScratch,
+                cbScratch - cbTmp );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/rsa_padding.c b/libs/symcrypt/lib/rsa_padding.c
new file mode 100644
index 000000000000..d2f384b61c31
--- /dev/null
+++ b/libs/symcrypt/lib/rsa_padding.c
@@ -0,0 +1,1218 @@
+//
+// rsa_padding.c   RSA padding algorithms
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define ASN1_SEQUENCE_BYTE      (0x30)
+#define ASN1_OCTET_STRING_BYTE  (0x04)
+
+#define PKCS_BLOCKTYPE_1        (0x01)      // This is not used, added here for completeness
+#define PKCS_BLOCKTYPE_2        (0x02)
+
+//
+// Note: we could optimize these OID lists by using the same byte sequence for
+// the long and short versions.
+//
+const SYMCRYPT_OID SymCryptMd5OidList[] =
+{
+    {12, (BYTE *)"\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00"},
+    {10, (BYTE *)"\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05"},
+};
+
+const SYMCRYPT_OID SymCryptSha1OidList[] =
+{
+    {9, (BYTE *)"\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00"},
+    {7, (BYTE *)"\x06\x05\x2b\x0e\x03\x02\x1a"}
+};
+
+const SYMCRYPT_OID SymCryptSha224OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04"}
+};
+
+const SYMCRYPT_OID SymCryptSha256OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01"}
+};
+
+const SYMCRYPT_OID SymCryptSha384OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02"}
+};
+
+const SYMCRYPT_OID SymCryptSha512OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03"}
+};
+
+const SYMCRYPT_OID SymCryptSha512_224OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05"}
+};
+
+const SYMCRYPT_OID SymCryptSha512_256OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06"}
+};
+
+const SYMCRYPT_OID SymCryptSha3_224OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07"}
+};
+
+const SYMCRYPT_OID SymCryptSha3_256OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08"}
+};
+
+const SYMCRYPT_OID SymCryptSha3_384OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09"}
+};
+
+const SYMCRYPT_OID SymCryptSha3_512OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0a\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0a"}
+};
+
+const SYMCRYPT_OID SymCryptShake128OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0b\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0b"}
+};
+
+const SYMCRYPT_OID SymCryptShake256OidList[] =
+{
+    {13, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0c\x05\x00"},
+    {11, (BYTE *)"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0c"}
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsaPaddingMaskGeneration(
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_                        PVOID                       pHashState,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+    _Out_writes_bytes_( cbDst ) PBYTE                       pbDst,
+                                SIZE_T                      cbDst )
+{
+    SIZE_T              cIterations = 0;
+
+    BYTE                rgbHash[SYMCRYPT_HASH_MAX_RESULT_SIZE] = { 0 };
+    BYTE                rgbCount[sizeof(UINT32)] = { 0 };
+    PBYTE               pbCount = NULL;
+    SIZE_T              cbMaskRemaining = cbDst;
+    PBYTE               pbMaskIndex = pbDst;
+
+    BOOLEAN             fAvoidDWORDReverse = FALSE;
+
+    SIZE_T              cbHashAlg = SymCryptHashResultSize( hashAlgorithm );
+
+    cIterations = (cbDst + (cbHashAlg - 1)) / cbHashAlg;
+    if (cIterations < 256)
+    {
+        fAvoidDWORDReverse = TRUE;
+    }
+
+    for (UINT32 i = 0; i < cIterations; i++)
+    {
+        SymCryptHashInit( hashAlgorithm, pHashState );
+
+        // hash the seed
+        SymCryptHashAppend( hashAlgorithm, pHashState, pbSrc, cbSrc );
+
+        // Reverse the count bytes
+        pbCount = (BYTE*)&i;
+        if (fAvoidDWORDReverse)
+        {
+            rgbCount[3] = pbCount[0];
+        }
+        else
+        {
+            for (UINT32 j = 0; j < sizeof(UINT32); j++)
+            {
+                rgbCount[j] = pbCount[sizeof(UINT32) - j - 1];
+            }
+        }
+
+        // hash the count
+        SymCryptHashAppend( hashAlgorithm, pHashState, rgbCount, sizeof(UINT32) );
+
+        // copy the bytes from this hash into the mask buffer
+        if (cbMaskRemaining >= cbHashAlg)
+        {
+            SymCryptHashResult( hashAlgorithm, pHashState, pbMaskIndex, cbHashAlg );
+
+            cbMaskRemaining -= cbHashAlg;
+            pbMaskIndex += cbHashAlg;
+        }
+        else
+        {
+            SymCryptHashResult( hashAlgorithm, pHashState, rgbHash, cbHashAlg );
+
+            memcpy( pbMaskIndex, rgbHash, cbMaskRemaining);
+            break;
+        }
+    }
+}
+
+//
+// PKCS1 Encryption Format:
+//      0x00 || 0x02 || PS || 0x00 || M
+//
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1ApplyEncryptionPadding(
+    _In_reads_bytes_( cbPlaintext )     PCBYTE      pbPlaintext,
+                                        SIZE_T      cbPlaintext,
+    _Out_writes_bytes_( cbPkcs1Format ) PBYTE       pbPkcs1Format,
+                                        SIZE_T      cbPkcs1Format )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // Format: 00 02 <PS> 00 <M>
+    // <PS> 8 or more padding bytes, random, all nonzero
+    // <M> message, length between 0 and cbPKCS1Format - 11.
+    // See RFC 3447 for more details.
+
+    SIZE_T cbPS;
+    SIZE_T i;
+
+    // ensure output buffer is big enough (padding has 11 bytes overhead)
+    if( cbPkcs1Format < (cbPlaintext + 11) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbPS = cbPkcs1Format - (cbPlaintext + 3);
+
+    pbPkcs1Format[0] = 0x00;
+    pbPkcs1Format[1] = PKCS_BLOCKTYPE_2;
+
+    scError = SymCryptCallbackRandom( &pbPkcs1Format[2], cbPS );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    // Make sure that none of the bytes in PS is zero (as per specs)
+    for( i = 0; i < cbPS; i++ )
+    {
+        while( pbPkcs1Format[2 + i] == 0x00 )
+        {
+            scError = SymCryptCallbackRandom( &pbPkcs1Format[2+i], 1 );
+            if( scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    pbPkcs1Format[2 + cbPS] = 0x00;
+
+    memcpy(pbPkcs1Format + 3 + cbPS, pbPlaintext, cbPlaintext);
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1RemoveEncryptionPadding(
+    _Inout_updates_bytes_( cbPkcs1Buffer )  PBYTE       pbPkcs1Format,
+                                            SIZE_T      cbPkcs1Format,
+                                            SIZE_T      cbPkcs1Buffer,
+    _Out_writes_bytes_opt_( cbPlaintext )   PBYTE       pbPlaintext,
+                                            SIZE_T      cbPlaintext,
+    _Out_                                   SIZE_T     *pcbPlaintext )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 mPaddingError = 0;
+    UINT32 mBufferSizeError = 0;
+
+    UINT32 cbPlaintextResult = 0;
+    UINT32 i;
+    UINT32 mByteIsZero;
+    UINT32 mLengthFound;
+    UINT32 iFirstZero;
+    UINT32 cbPlaintextTruncated;
+
+    SYMCRYPT_ASSERT( cbPkcs1Buffer >= cbPkcs1Format );
+    SYMCRYPT_ASSERT( cbPkcs1Buffer >= 32 );                         // Requirements for SymcryptScsRotateBuffer
+    SYMCRYPT_ASSERT( (cbPkcs1Buffer & (cbPkcs1Buffer - 1)) == 0 );  // must be a power of 2
+    SYMCRYPT_ASSERT( cbPkcs1Buffer <= (1 << 30 ));                  // Ensure we can use 31-bit masking operations
+
+    // Format: 00 02 <PS> 00 <M>
+    // <PS> 8 or more padding bytes, random, all nonzero
+    // <M> message, length between 0 and cbPKCS1Format - 11.
+    // See RFC 3447 for more details.
+    // We do not reveal the buffer contents through side-channels to avoid Bleichenbacher-style attacks
+    // This includes the plaintext length, which is determined by the location of the 00 byte
+
+    if ( cbPkcs1Format < 11 )
+    {
+        // cbPKCS1Format is public, so the if() is safe. 11 is the total overhead
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+    // this also implies that cbPkcs1Buffer >= 16
+
+    // Check the leading bytes
+    mPaddingError |= SymCryptMask32IsNonzeroU31( pbPkcs1Format[0] ); // First byte must be = 0
+    mPaddingError |= SymCryptMask32NeqU31( pbPkcs1Format[1], PKCS_BLOCKTYPE_2 ); // Second byte must be = 2
+
+    iFirstZero = 0;
+    mLengthFound = 0;
+    for (i = 2; i < cbPkcs1Format; i++)
+    {
+        mByteIsZero = SymCryptMask32IsZeroU31( pbPkcs1Format[i] );
+
+        // remember the index of the first zero byte
+        iFirstZero |= i & mByteIsZero & ~mLengthFound;
+        mLengthFound |= mByteIsZero;
+    }
+    mPaddingError |= ~mLengthFound;
+
+    // At this point:
+    // - iFirstZero points to the first zero byte, or is 0 if there is no zero byte
+    // - mPaddingError is set if no zero byte was found
+
+    // It is an error if the first zero is at index < 10 as <PS> needs to be at least 8 bytes
+    mPaddingError |= SymCryptMask32LtU31( iFirstZero, 10 );
+
+    // Compute the # bytes of the message; 0 if there was a padding error
+    cbPlaintextResult = ~mPaddingError & ((UINT32)(cbPkcs1Format - iFirstZero - 1));
+
+    // We're done if the caller didn't want the actual message, but only the size.
+    // We do that before checking the size of the plaintext buffer so that callers who
+    // only want the size do not get an error.
+    if( pbPlaintext == NULL )
+    {
+        // Condition is public.
+        goto cleanup;
+    }
+
+    // Checking that the output buffer is large enough is a bit tricky as we have a SIZE_T as
+    // buffer size, but we like to work on 31-bit integers as they have better mask algorithm perf.
+    // We can truncate the SIZE_T and check for equality, which is side-channel safe.
+    cbPlaintextTruncated = ((UINT32) cbPlaintext) & 0x7fffffff;                 // Truncate to 31 bits
+    if( cbPlaintextTruncated == cbPlaintext )
+    {
+        // Condition is public as we write the whole plaintext buffer anyway.
+        mBufferSizeError = SymCryptMask32LtU31( cbPlaintextTruncated, cbPlaintextResult );
+    }
+
+    // The message starts at iFirstZero + 1, which is a variable location so we can't just memcpy it without
+    // revealing information through side channels.
+    // Instead we rotate the buffer left (side-channel safe) so that the message appears at the front.
+    // Rotation constant is such that the message appears at the start.
+    SymCryptScsRotateBuffer( pbPkcs1Format, cbPkcs1Buffer, (iFirstZero + 1) & (cbPkcs1Buffer - 1) );
+
+    // The ScsCopy function can copy the data to the destination buffer, but the input buffer must be
+    // as long as the output buffer. We can't just use cbPlaintext as the output buffer size, as it is
+    // unbounded. But we can limit it to cbPkcs1Format as that is the public key size and is public.
+    SymCryptScsCopy( pbPkcs1Format, cbPlaintextResult, pbPlaintext, SYMCRYPT_MIN( cbPlaintext, cbPkcs1Format ) );
+
+cleanup:
+    // Update scError with the two error masks. Padding error given highest priority.
+    scError ^= mBufferSizeError & (scError ^ SYMCRYPT_BUFFER_TOO_SMALL);
+    scError ^= mPaddingError & (scError ^ SYMCRYPT_INVALID_ARGUMENT);
+
+    *pcbPlaintext = cbPlaintextResult;
+    return scError;
+}
+
+//
+// OAEP Encryption Format:
+//                             +----------+---------+-------+
+//                        DB = |  lHash   |    PS   |   M   |
+//                             +----------+---------+-------+
+//                                            |
+//                  +----------+              V
+//                  |   seed   |--> MGF ---> xor
+//                  +----------+              |
+//                        |                   |
+//               +--+     V                   |
+//               |00|    xor <----- MGF <-----|
+//               +--+     |                   |
+//                 |      |                   |
+//                 V      V                   V
+//               +--+----------+----------------------------+
+//         EM =  |00|maskedSeed|          maskedDB          |
+//               +--+----------+----------------------------+
+//
+// PS = zero or more bytes 0x00 || 0x01
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepApplyEncryptionPadding(
+    _In_reads_bytes_( cbPlaintext )     PCBYTE          pbPlaintext,
+                                        SIZE_T          cbPlaintext,
+    _In_                                PCSYMCRYPT_HASH hashAlgorithm,
+    _In_reads_bytes_( cbLabel )         PCBYTE          pbLabel,
+                                        SIZE_T          cbLabel,
+    _In_reads_bytes_opt_( cbSeed )      PCBYTE          pbSeed,
+                                        SIZE_T          cbSeed,
+    _Out_writes_bytes_( cbOaepFormat )  PBYTE           pbOaepFormat,
+                                        SIZE_T          cbOaepFormat,
+    _Out_writes_bytes_( cbScratch )     PBYTE           pbScratch,
+                                        SIZE_T          cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PVOID   pHashState;
+
+    PBYTE   pbSeedInternal;
+    PBYTE   pbSeedMask;
+    PBYTE   pbDB;
+    PBYTE   pbDBMask;
+
+    SIZE_T  cbDB;
+    SIZE_T  cbPS;
+
+    SIZE_T  cbHash = SymCryptHashResultSize( hashAlgorithm );
+    SIZE_T  cbHashState = SymCryptHashStateSize( hashAlgorithm );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    // OAEP overhead is 2 + 2 * size of hash result
+    if(  cbOaepFormat < (cbPlaintext + (cbHash * 2) + 2) ||
+         ((pbSeed!=NULL) && (cbSeed>cbHash)) ||
+         ((pbSeed==NULL) && (cbSeed!=0)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbPS = cbOaepFormat - (cbPlaintext + (cbHash * 2) + 2);
+    cbDB = cbOaepFormat - (cbHash + 1);
+
+    SYMCRYPT_ASSERT( cbScratch >= cbHashState + (cbHash * 2) + (cbDB * 2) );
+
+    pHashState = (PVOID) pbScratch;
+    pbSeedInternal = pbScratch + cbHashState;
+    pbSeedMask = pbSeedInternal + cbHash;
+    pbDB = pbSeedMask + cbHash;
+    pbDBMask = pbDB + cbDB;
+
+    // hash the label
+    SymCryptHash( hashAlgorithm, pbLabel, cbLabel, pbDB, cbHash );
+
+    SymCryptWipe(pbDB + cbHash, cbPS);
+    pbDB[cbHash + cbPS] = 0x01;
+
+	// dcl - are we quite sure that none of these numbers are under attacker control?
+    memcpy(pbDB + cbHash + cbPS + 1, pbPlaintext, cbPlaintext);
+
+    if (NULL == pbSeed)
+    {
+        // generate the random seed (same length as the hash result)
+        scError = SymCryptCallbackRandom( pbSeedInternal, cbHash );
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+    else
+    {
+        SymCryptWipe( pbSeedInternal, cbHash );
+        memcpy(pbSeedInternal, pbSeed, cbSeed);
+    }
+
+    // MGF(seed)
+    SymCryptRsaPaddingMaskGeneration(
+                            hashAlgorithm,
+                            pHashState,
+                            pbSeedInternal,
+                            cbHash,
+                            pbDBMask,
+                            cbDB);
+
+    // set the most significant byte to 0x00
+    pbOaepFormat[0] = 0x00;
+
+    // XOR the DB and the mask MGF(seed)
+    for (UINT32 i = 0; i < cbDB; i++)
+    {
+        pbOaepFormat[cbHash + 1 + i] = pbDB[i] ^ pbDBMask[i];
+    }
+
+    // MGF(masked DB)
+    SymCryptRsaPaddingMaskGeneration(
+                            hashAlgorithm,
+                            pHashState,
+                            pbOaepFormat + cbHash + 1,
+                            cbDB,
+                            pbSeedMask,
+                            cbHash);
+
+    // XOR the seed and the seed mask MGF(masked DB)
+    for (UINT32 i = 0; i < cbHash; i++)
+    {
+        pbOaepFormat[1 + i] = pbSeedInternal[i] ^ pbSeedMask[i];
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaOaepRemoveEncryptionPadding(
+    _In_reads_bytes_( cbOAEPFormat )
+                                PCBYTE                      pbOAEPFormat,
+                                SIZE_T                      cbOAEPFormat,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_( cbLabel ) PCBYTE                      pbLabel,
+                                SIZE_T                      cbLabel,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPlaintext )
+                                PBYTE                       pbPlaintext,
+                                SIZE_T                      cbPlaintext,
+    _Out_                       SIZE_T                      *pcbPlaintext,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    PVOID   pHashState;
+
+    PBYTE   pbSeedMask;
+    PBYTE   pbSeed;
+    PBYTE   pbDBMask;
+    PBYTE   pbDB;
+    PBYTE   pbLabelHash;
+    UINT32  mPaddingError;
+
+    SIZE_T  cbDB;
+
+    SIZE_T  cnt = 0;
+
+    SIZE_T  cbHashAlg = SymCryptHashResultSize( hashAlgorithm );
+    SIZE_T  cbHashState = SymCryptHashStateSize( hashAlgorithm );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    if (flags != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // check if the most significant byte is set to 0x00
+    mPaddingError = SymCryptMask32IsNonzeroU31( pbOAEPFormat[0] );
+
+    // Padding overhead is 2 hash values plus 2 bytes
+    if( cbOAEPFormat < (2*cbHashAlg + 2) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbDB = cbOAEPFormat - (cbHashAlg + 1);
+
+    SYMCRYPT_ASSERT( cbScratch >= cbHashState + (cbHashAlg * 3) + (cbDB * 2) );
+
+    pHashState = (PVOID) pbScratch;
+    pbSeedMask = pbScratch + cbHashState;
+    pbSeed = pbSeedMask + cbHashAlg;
+    pbDBMask = pbSeed + cbHashAlg;
+    pbDB = pbDBMask + cbDB;
+    pbLabelHash = pbDB + cbDB;
+
+    // MGF(masked DB)
+    SymCryptRsaPaddingMaskGeneration(
+                            hashAlgorithm,
+                            pHashState,
+                            pbOAEPFormat + cbHashAlg + 1,
+                            cbDB,
+                            pbSeedMask,
+                            cbHashAlg);
+
+    // XOR the masked seed and the seed mask MGF(masked DB)
+    for (UINT32 i = 0; i < cbHashAlg; i++)
+    {
+        pbSeed[i] = pbOAEPFormat[1 + i] ^ pbSeedMask[i];
+    }
+
+    // MGF(seed)
+    SymCryptRsaPaddingMaskGeneration(
+                            hashAlgorithm,
+                            pHashState,
+                            pbSeed,
+                            cbHashAlg,
+                            pbDBMask,
+                            cbDB);
+
+    // XOR the masked DB and the mask MGF(seed)
+    for (UINT32 i = 0; i < cbDB; i++)
+    {
+        pbDB[i] = pbOAEPFormat[cbHashAlg + 1 + i] ^ pbDBMask[i];
+    }
+
+    // hash the label
+    SymCryptHash( hashAlgorithm, pbLabel, cbLabel, pbLabelHash, cbHashAlg );
+
+    // check the label hash
+    mPaddingError |= SymCryptMask32IsZeroU31( SymCryptEqual( pbLabelHash, pbDB, cbHashAlg ) );
+
+    //
+    // At this point we have verified the leading 0 byte and the label hash, with any
+    // errors in mPaddingError. We could continue to make the entire padding removal
+    // side-channel safe like we do in the PKCS1 padding case, but that is not necessary.
+    // The side-channel only leaks data if the attacker can trigger two different behaviours
+    // and derive information from the difference.
+    // This is relatively easy to do with something like a match on 1 or 2 bytes because the
+    // chance of satisfying the check on a random input is still useful. But here we have
+    // matched 33 bytes (assuming a 32-byte hash) and the Bleichenbacher style attacks don't
+    // work beyond this point. Basically, these attacks produce ciphertexts without knowing
+    // the corresponding plaintext, and the chance of the label hash matching is something
+    // like 2^{-256}. So these ciphertexts will always fail right here, and there is no
+    // difference of behaviour that leaks data to the attacker.
+    // Thus, we can switch back to normal processing of the errors here.
+    //
+
+    if( mPaddingError != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // check the PS
+    for (cnt = cbHashAlg; cnt < cbDB; cnt++)
+    {
+        if (pbDB[cnt] == 0x01)
+        {
+            cnt++;
+            break;
+        }
+        else if (pbDB[cnt] != 0x00)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+    if (pbDB[cnt - 1] != 0x01)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // the rest is data
+    *pcbPlaintext = cbDB - cnt;
+
+    if(NULL == pbPlaintext)
+    {
+        scError = SYMCRYPT_NO_ERROR;
+        goto cleanup;
+    }
+
+    if (cbPlaintext < *pcbPlaintext)
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    memcpy(pbPlaintext, pbDB + cnt, *pcbPlaintext);
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+
+    return scError;
+}
+
+//
+// PKCS1 Signature Format:
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1ApplySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_reads_bytes_( cbHashOid )
+                                PCBYTE                      pbHashOid,
+                                SIZE_T                      cbHashOid,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPKCS1Format )
+                                PBYTE                       pbPKCS1Format,
+                                SIZE_T                      cbPKCS1Format )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SIZE_T cbEncoding;
+    SIZE_T cbPadding;
+    SIZE_T cbOidOffset;
+
+    BOOLEAN fInsertASN1 = TRUE;
+
+    if ((flags & ~SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Simple check to avoid funky behavior if cbHash is close to SIZE_MAX
+    if (cbHash >= cbPKCS1Format)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    fInsertASN1 = ((flags & SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1) == 0);
+
+    if (fInsertASN1)
+    {
+        if ( (pbHashOid!=NULL) && (cbHashOid>0) )
+        {
+            // determine the length of the ASN1 Encoding
+            // 2 sequence bytes, 1 id byte and 3 length bytes
+            cbEncoding = 6 + cbHashOid + cbHash;
+        }
+        else
+        {
+            if (cbHashOid > 0)
+            {
+                // The caller has passed a NULL hash and a non 0 size for it.
+                // We can't guess the intent, hence we fail
+                scError = SYMCRYPT_INVALID_ARGUMENT;
+                goto cleanup;
+            }
+
+            // special case for MD5 hash without OID
+            cbEncoding = 2 + cbHash;
+        }
+
+        // we don't support encodings longer than 128 bytes,
+        // with this check we know that the length of the OID as
+        // well as the length of the hash value will each fit in
+        // one byte
+        if (cbEncoding > 0x80)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+    else
+    {
+        cbEncoding = cbHash;
+    }
+
+    // In a few scenarios (involving small RSA keys), the new large SHA
+    // hashes are too big to be signed by the specified key.
+    // There must be at least 8 bytes of 0xff.
+    if (3 + 8 + cbEncoding > cbPKCS1Format)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbPadding = cbPKCS1Format - 3 - cbEncoding;
+
+
+    // insert the block type and delimiters
+    pbPKCS1Format[0] = 0x00;
+    pbPKCS1Format[1] = 0x01;
+    pbPKCS1Format[2 + cbPadding] = 0x00;
+
+    // insert the type 1 padding
+    memset(pbPKCS1Format + 2, 0xff, cbPadding);
+
+    if (fInsertASN1)
+    {
+        cbOidOffset = 1;
+        if ( (pbHashOid!=NULL) && (cbHashOid>0) )
+        {
+            // insert the algorithm encoding
+            pbPKCS1Format[2 + cbPadding + 1] = ASN1_SEQUENCE_BYTE;
+            pbPKCS1Format[2 + cbPadding + 2] = (BYTE)cbEncoding - 2;
+
+            // insert the sequence string byte, length of the hash and the hash value
+            pbPKCS1Format[2 + cbPadding + 3] = ASN1_SEQUENCE_BYTE;
+            pbPKCS1Format[2 + cbPadding + 4] = (BYTE)cbHashOid;
+            cbOidOffset += 4;
+            memcpy(pbPKCS1Format + 2 + cbPadding + cbOidOffset, pbHashOid, cbHashOid);
+        }
+
+        // insert the octet string byte, length of the hash and the hash value
+        pbPKCS1Format[2 + cbPadding + cbOidOffset + cbHashOid] = ASN1_OCTET_STRING_BYTE;
+        pbPKCS1Format[2 + cbPadding + cbOidOffset + cbHashOid + 1] = (BYTE)cbHash;
+        memcpy(pbPKCS1Format + 2 + cbPadding + cbOidOffset + cbHashOid + 2, pbHash, cbHash);
+    }
+    else
+    {
+        memcpy(pbPKCS1Format + 3 + cbPadding, pbHash, cbHash);
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+
+    return scError;
+}
+
+//
+// Check if a PKCS1 padding is valid with regard to a hash oid
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1CheckSignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_reads_bytes_( cbHashOid )
+                                PCBYTE                      pbHashOid,
+                                SIZE_T                      cbHashOid,
+    _In_reads_bytes_( cbPKCS1Format )
+                                PCBYTE                      pbPKCS1Format,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPKCS1Format )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbPKCS1Format)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SymCryptWipe(pbScratch, cbPKCS1Format);
+
+    scError = SymCryptRsaPkcs1ApplySignaturePadding(
+                      pbHash,
+                      cbHash,
+                      pbHashOid,
+                      cbHashOid,
+                      flags,
+                      pbScratch,
+                      cbPKCS1Format );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if ( SymCryptEqual(pbScratch, pbPKCS1Format, cbPKCS1Format) )
+    {
+        scError = SYMCRYPT_NO_ERROR;
+    }
+    else
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPkcs1VerifySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_reads_( nOIDCount )     PCSYMCRYPT_OID              pHashOIDs,
+    _In_                        SIZE_T                      nOIDCount,
+    _In_reads_bytes_( cbPKCS1Format )
+                                PCBYTE                      pbPKCS1Format,
+                                SIZE_T                      cbPKCS1Format,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 i = 0;
+
+    UNREFERENCED_PARAMETER( cbScratch );
+    SYMCRYPT_ASSERT( cbScratch >= cbPKCS1Format );
+
+    if ((flags & ~SYMCRYPT_FLAG_RSA_PKCS1_OPTIONAL_HASH_OID) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    //
+    // Verify padding and the hash value
+    //
+    if (pHashOIDs)
+    {
+        for (i = 0; i < nOIDCount; i++)
+        {
+            scError = SymCryptRsaPkcs1CheckSignaturePadding(
+                              pbHash,
+                              cbHash,
+                              pHashOIDs[i].pbOID,
+                              pHashOIDs[i].cbOID,
+                              pbPKCS1Format,
+                              0,
+                              pbScratch,
+                              cbPKCS1Format );
+            if (scError == SYMCRYPT_NO_ERROR)
+            {
+                break;
+            }
+        }
+    }
+
+    if  ((pHashOIDs == NULL ) ||
+         (scError != SYMCRYPT_NO_ERROR &&
+          flags & SYMCRYPT_FLAG_RSA_PKCS1_OPTIONAL_HASH_OID))
+    {
+        // if no OID is passed in, or
+        // OID is passed in but failed verification, but OID is optional
+        scError = SymCryptRsaPkcs1CheckSignaturePadding(
+                          pbHash,
+                          cbHash,
+                          NULL,
+                          0,
+                          pbPKCS1Format,
+                          SYMCRYPT_FLAG_RSA_PKCS1_NO_ASN1,
+                          pbScratch,
+                          cbPKCS1Format );
+    }
+
+cleanup:
+
+    return scError;
+}
+
+//
+// PSS Signature Format:
+//                        +--------+----------+----------+
+//                   M' = |Padding1|  Hash M  |   salt   |
+//                        +--------+----------+----------+
+//                                       |
+//             +--------+----------+     V
+//       DB =  |Padding2|   salt   |   Hash
+//             +--------+----------+     |
+//                       |               |
+//                       V               |    +--+
+//                      xor <--- MGF <---|    |bc|
+//                       |               |    +--+
+//                       |               |      |
+//                       V               V      V
+//             +-------------------+----------+--+
+//       EM =  |    maskedDB       |     H    |bc|
+//             +-------------------+----------+--+
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssApplySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_reads_bytes_opt_( cbSalt )
+                                PCBYTE                      pbSalt,
+    _In_range_(0, cbPSSFormat)  SIZE_T                      cbSalt,
+                                UINT32                      nBitsOfModulus,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbPSSFormat )
+                                PBYTE                       pbPSSFormat,
+                                SIZE_T                      cbPSSFormat,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PVOID   pHashState;
+
+    PBYTE pbMPrime;
+    PBYTE pbDB;
+    PBYTE pbDBMask;
+
+    SIZE_T cbDB;
+    SIZE_T cbMPrime;
+    SIZE_T cbPadding2;
+
+    SIZE_T dwZeroBits  = 0; // Number of bits of the leftmost bit to be zeroed
+
+    SIZE_T cbHashAlg = SymCryptHashResultSize( hashAlgorithm );
+    SIZE_T cbHashState = SymCryptHashStateSize( hashAlgorithm );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    if ((cbPSSFormat == 0) || (pbPSSFormat == NULL))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Corner case of RFC 3447 for PSS:
+    //  If nBitsOfModulus == 1 mod 8, then emBits = nBitsOfModulus - 1 == 0 mod 8
+    //  Thus the size of the input buffer in bytes is emLen = ceil(emBits /8),
+    //  one smaller than the size of the modulus. Fix this here by setting the
+    //  leftmost byte of the output equal to 0.
+    if (nBitsOfModulus%8 == 1)
+    {
+        pbPSSFormat[0] = 0;
+        pbPSSFormat++;
+        cbPSSFormat--;
+    }
+
+    if ((flags!=0) ||
+        (cbPSSFormat < (cbHashAlg + cbSalt + 2)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbDB = cbPSSFormat - (cbHashAlg + 1);
+    cbPadding2 = cbDB - cbSalt - 1;
+    cbMPrime = 8 + cbHash + cbSalt;
+
+    SYMCRYPT_ASSERT( cbScratch >= cbHashState + cbMPrime + (cbDB * 2) );
+
+    pHashState = (PVOID) pbScratch;
+    pbMPrime = pbScratch + cbHashState;
+    pbDB = pbMPrime + cbMPrime;
+    pbDBMask = pbDB + cbDB;
+
+    // set up the M Prime
+    SymCryptWipe(pbMPrime, 8);
+    memcpy(pbMPrime + 8, pbHash, cbHash);
+
+    if (NULL == pbSalt)
+    {
+        // generate the random salt
+        scError = SymCryptCallbackRandom(
+                            pbMPrime + 8 + cbHash,
+                            cbSalt);
+        if (scError != SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+    else
+    {
+        // copy the salt passed
+        memcpy(pbMPrime + 8 + cbHash, pbSalt, cbSalt);
+    }
+
+    // hash the MPrime
+    SymCryptHash( hashAlgorithm, pbMPrime, cbMPrime, pbPSSFormat + cbDB, cbHashAlg );
+
+    // copy the same salt into the DB
+    SymCryptWipe(pbDB, cbPadding2);
+    pbDB[cbPadding2] = 0x01;
+    memcpy(pbDB + cbPadding2 + 1, pbMPrime + 8 + cbHash, cbSalt);
+
+    // MGF(Hash of MPrime)
+    SymCryptRsaPaddingMaskGeneration(
+                            hashAlgorithm,
+                            pHashState,
+                            pbPSSFormat + cbDB,
+                            cbHashAlg,
+                            pbDBMask,
+                            cbDB);
+
+    // XOR the DB and the mask MGF(seed)
+    for (UINT32 i = 0; i < cbDB; i++)
+    {
+        pbPSSFormat[i] = pbDB[i] ^ pbDBMask[i];
+    }
+
+    // calculate the number of bits to be zeroed
+    dwZeroBits = 8*cbPSSFormat + 1 - nBitsOfModulus;
+
+    // mask off dwZeroBits worth of the encoded message
+    pbPSSFormat[0] &= (BYTE)(0xff >> dwZeroBits);
+
+    // set the least significant byte of pbPSSFormat to bc
+    pbPSSFormat[cbPSSFormat - 1] = 0xbc;
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaPssVerifySignaturePadding(
+    _In_reads_bytes_( cbHash )  PCBYTE                      pbHash,
+                                SIZE_T                      cbHash,
+    _In_                        PCSYMCRYPT_HASH             hashAlgorithm,
+    _In_range_(0, cbPSSFormat)  SIZE_T                      cbSalt,
+    _In_reads_bytes_( cbPSSFormat )
+                                PCBYTE                      pbPSSFormat,
+                                SIZE_T                      cbPSSFormat,
+                                UINT32                      nBitsOfModulus,
+                                UINT32                      flags,
+    _Out_writes_bytes_( cbScratch )
+                                PBYTE                       pbScratch,
+                                SIZE_T                      cbScratch )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    PVOID   pHashState;
+
+    PBYTE pbDBMask;
+    PBYTE pbMPrime;
+    PBYTE pbMPrimeHash;
+    PCBYTE pbHashOfMPrimeIndex;
+
+    SIZE_T cbDB;
+    SIZE_T cbMPrime;
+    SIZE_T cbPadding2;
+    SIZE_T cbSaltObserved;
+
+    SIZE_T dwZeroBits  = 0; // Number of bits of the leftmost bit to be zeroed
+
+    SIZE_T cbHashAlg = SymCryptHashResultSize( hashAlgorithm );
+    SIZE_T cbHashState = SymCryptHashStateSize( hashAlgorithm );
+
+    UNREFERENCED_PARAMETER( cbScratch );
+
+    if (((flags & ~SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT) != 0) ||
+        (cbPSSFormat == 0) ||
+        (pbPSSFormat == NULL))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Corner case of RFC 3447 for PSS:
+    //  If nBitsOfModulus == 1 mod 8, then emBits = nBitsOfModulus - 1 == 0 mod 8
+    //  Thus the size of the input buffer in bytes is emLen = ceil(emBits /8),
+    //  one smaller than the size of the modulus. Fix this here by checking that the
+    //  leftmost byte of the input equals 0.
+    if (nBitsOfModulus%8 == 1)
+    {
+        if (pbPSSFormat[0] != 0)
+        {
+            scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+            goto cleanup;
+        }
+        pbPSSFormat++;
+        cbPSSFormat--;
+    }
+
+    // calculate the number of bits to be zeroed
+    dwZeroBits = 8*cbPSSFormat + 1 - nBitsOfModulus;
+
+    // check the most significant dwZeroBits bits to ensure they're zero and
+    // check the least significant byte
+    if( (cbPSSFormat < (cbHashAlg + cbSalt + 2)) ||
+        (pbPSSFormat[0] & (BYTE)(0xff << (8 - dwZeroBits))) != 0 ||
+        pbPSSFormat[cbPSSFormat - 1] != 0xbc
+        )
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+    cbDB = cbPSSFormat - (cbHashAlg + 1);
+
+    pHashState = (PVOID) pbScratch;
+    pbDBMask = pbScratch + cbHashState;
+
+    // index to hash of M Prime
+    pbHashOfMPrimeIndex = pbPSSFormat + (cbPSSFormat - (cbHashAlg + 1));
+
+    // MGF(masked DB)
+    SymCryptRsaPaddingMaskGeneration(
+            hashAlgorithm,
+            pHashState,
+            pbHashOfMPrimeIndex,
+            cbHashAlg,
+            pbDBMask,
+            cbDB);
+
+    // XOR the DB and the DB mask and store the result in pbDBMask (not needed after this)
+    for (UINT32 i = 0; i < cbDB; i++)
+    {
+        pbDBMask[i] = pbPSSFormat[i] ^ pbDBMask[i];
+    }
+
+    // mask off the first dwZeroBits
+    pbDBMask[0] &= (BYTE)(0xff >> dwZeroBits);
+
+    // find the length of the all-zeroes padding2 in pbDBMask
+    // padding2 must be terminated by a 0x01 byte
+    for (cbPadding2 = 0; cbPadding2 < (cbDB - cbSalt); cbPadding2++)
+    {
+        if (pbDBMask[cbPadding2] == 0x01)
+        {
+            // we have reached the end of padding2
+            break;
+        }
+
+        if (pbDBMask[cbPadding2] != 0x00)
+        {
+            // non-zero byte in what should be padding2
+            scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+            goto cleanup;
+        }
+    }
+
+    // Here we have either:
+    // cbPadding2 == cbDB - cbSalt, which means the padding is too long
+    // or
+    // cbPadding2 <= cbDB - cbSalt - 1, and we have broken out of the loop when we found the 0x01 byte
+    if( cbPadding2 == cbDB - cbSalt )
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+    cbSaltObserved = cbDB - cbPadding2 - 1;
+    // cbSalt <= cbDB - cbPadding2 - 1 = cbSaltObserved
+    // so cbSaltObserved is acceptable value for signature verification
+    // with SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT
+
+    if( ((flags & SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT) == 0) &&
+        cbSaltObserved != cbSalt )
+    {
+        // When SYMCRYPT_FLAG_RSA_PSS_VERIFY_WITH_MINIMUM_SALT not specified,
+        // we require salt length observed to exactly match the caller provided salt length
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbMPrime = pbDBMask + cbDB;
+    cbMPrime = 8 + cbHash + cbSaltObserved;
+    pbMPrimeHash = pbMPrime + cbMPrime;
+
+    SYMCRYPT_ASSERT( cbScratch >= cbHashState + cbDB + cbMPrime + cbHashAlg );
+
+    // create the M Prime
+    SymCryptWipe(pbMPrime, 8);
+    memcpy(pbMPrime + 8, pbHash, cbHash);
+    memcpy(pbMPrime + 8 + cbHash,
+           pbDBMask + (cbDB - cbSaltObserved),
+           cbSaltObserved);
+
+    // hash the M Prime
+    SymCryptHash( hashAlgorithm, pbMPrime, cbMPrime, pbMPrimeHash, cbHashAlg );
+
+    if ( !SymCryptEqual(pbPSSFormat + cbDB, pbMPrimeHash, cbHashAlg) )
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+    scError = SYMCRYPT_NO_ERROR;
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/rsakey.c b/libs/symcrypt/lib/rsakey.c
new file mode 100644
index 000000000000..32b802ba7e60
--- /dev/null
+++ b/libs/symcrypt/lib/rsakey.c
@@ -0,0 +1,1631 @@
+//
+// rsakey.c   RSA keys' related algorithms
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+#define RSA_DEFAULT_PUBLIC_EXPONENT        (65537)
+
+PSYMCRYPT_RSAKEY
+SYMCRYPT_CALL
+SymCryptRsakeyAllocate(
+    _In_    PCSYMCRYPT_RSA_PARAMS   pParams,
+    _In_    UINT32                  flags )
+{
+    PVOID               p;
+    SIZE_T              cb;
+    PSYMCRYPT_RSAKEY    res = NULL;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    SYMCRYPT_ASSERT( pParams != NULL );
+
+    cb = SymCryptSizeofRsakeyFromParams( pParams );
+
+    p = SymCryptCallbackAlloc( cb );
+
+    if ( p==NULL )
+    {
+        goto cleanup;
+    }
+
+    res = SymCryptRsakeyCreate( p, cb, pParams );
+
+cleanup:
+    return res;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsakeyFree( _Out_ PSYMCRYPT_RSAKEY pkObj )
+{
+    SYMCRYPT_CHECK_MAGIC( pkObj );
+    SymCryptRsakeyWipe( pkObj );
+    SymCryptCallbackFree( pkObj );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofRsakeyFromParams( _In_ PCSYMCRYPT_RSA_PARAMS pParams )
+{
+    UINT32 nModulusDigits;
+    UINT32 res;
+
+    SYMCRYPT_ASSERT( pParams != NULL );
+
+    nModulusDigits = SymCryptDigitsFromBits( pParams->nBitsOfModulus );
+
+    //
+    // From symcrypt_internal.h we have:
+    //      - sizeof results are upper bounded by 2^19
+    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
+    //      - nPrimes and nPubExps are bounded by SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES = 2 and
+    //        SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS = 1
+    // Thus the following calculation does not overflow the result.
+    //
+    res =  sizeof(SYMCRYPT_RSAKEY) +
+           SymCryptSizeofModulusFromDigits( nModulusDigits ) +                                              // For Modulus
+           pParams->nPrimes * SymCryptSizeofModulusFromDigits( nModulusDigits ) +                           // For Primes
+           pParams->nPrimes * SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pParams->nBitsOfModulus ) +             // For CrtInverses
+           pParams->nPubExp * SymCryptSizeofIntFromDigits( nModulusDigits ) +                               // For PrivExps
+           pParams->nPubExp * pParams->nPrimes * SymCryptSizeofIntFromDigits( nModulusDigits );             // For CrtPrivExps
+
+    // Consistency check with the static macro (optimized away in production)
+    SYMCRYPT_ASSERT( res <= SYMCRYPT_SIZEOF_RSAKEY_FROM_PARAMS( pParams->nBitsOfModulus, pParams->nPrimes, pParams->nPubExp ) );
+
+    return res;
+}
+
+PSYMCRYPT_RSAKEY
+SYMCRYPT_CALL
+SymCryptRsakeyCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE                   pbBuffer,
+                                    SIZE_T                  cbBuffer,
+    _In_                            PCSYMCRYPT_RSA_PARAMS   pParams )
+{
+    PSYMCRYPT_RSAKEY pkObj = NULL;
+
+    PBYTE pbCurr = pbBuffer;
+    SIZE_T cbNeeded;
+    SIZE_T itemSize;
+
+    SYMCRYPT_ASSERT( pParams != NULL );
+
+    cbNeeded = SymCryptSizeofRsakeyFromParams( pParams );
+
+    SYMCRYPT_ASSERT_ASYM_ALIGNED( pbCurr );
+
+    if (( cbBuffer < cbNeeded ) ||
+        ( pParams->nBitsOfModulus < SYMCRYPT_RSAKEY_MIN_BITSIZE_MODULUS ) ||
+        ( pParams->nBitsOfModulus > SYMCRYPT_RSAKEY_MAX_BITSIZE_MODULUS ) ||
+        ( pParams->nPubExp < 1 ) ||
+        ( pParams->nPubExp > SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS ) ||
+        ( pParams->nPrimes == 1 ) ||
+        ( pParams->nPrimes > SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES ) )
+    {
+        goto cleanup;
+    }
+    SYMCRYPT_ASSERT( cbBuffer >= sizeof( SYMCRYPT_RSAKEY ) );
+
+    pkObj = (PSYMCRYPT_RSAKEY) pbCurr;
+
+    // Set all the parameters to 0
+    SymCryptWipe( pbBuffer, cbBuffer );
+
+    // Main parameters of the RSAKEY
+    // Everything is 0 until created
+
+    pkObj->cbTotalSize = (UINT32) cbNeeded;
+    // The result should always be within 4 GB, but we check to avoid security bugs
+    SYMCRYPT_ASSERT( pkObj->cbTotalSize == cbNeeded );
+
+    pkObj->hasPrivateKey = FALSE;
+
+    pkObj->nSetBitsOfModulus = pParams->nBitsOfModulus;
+    pkObj->nDigitsOfModulus = SymCryptDigitsFromBits( pkObj->nSetBitsOfModulus );   // The modulus object has always this number of digits
+
+    pkObj->nPrimes = pParams->nPrimes;
+    pkObj->nPubExp = pParams->nPubExp;
+
+    pbCurr += sizeof( SYMCRYPT_RSAKEY );
+
+    // Modulus
+    itemSize = SymCryptSizeofModulusFromDigits( pkObj->nDigitsOfModulus );
+    SYMCRYPT_ASSERT( cbBuffer >= sizeof( SYMCRYPT_RSAKEY ) + itemSize
+                                 + (pkObj->nPrimes*SymCryptSizeofModulusFromDigits( pkObj->nDigitsOfModulus ))
+                                 + (pkObj->nPrimes*SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pParams->nBitsOfModulus ))
+                                 + (pkObj->nPubExp*SymCryptSizeofIntFromDigits( pkObj->nDigitsOfModulus ))
+                                 + (pkObj->nPubExp*pkObj->nPrimes*SymCryptSizeofIntFromDigits( pkObj->nDigitsOfModulus )) );
+    pkObj->pmModulus = SymCryptModulusCreate(
+                        pbCurr,
+                        itemSize,
+                        pkObj->nDigitsOfModulus );
+    SYMCRYPT_ASSERT( pkObj->pmModulus != NULL );
+    pbCurr += itemSize;
+
+    // For the remaining objects
+    // defer creation until SymCryptRsakeyGenerate or
+    // SymCryptRsakeySetValue
+
+    // Primes
+    for (UINT32 i=0; i<pkObj->nPrimes; i++)
+    {
+        pkObj->pbPrimes[i] = pbCurr;
+        pbCurr += SymCryptSizeofModulusFromDigits( pkObj->nDigitsOfModulus );
+    }
+
+    // CRT Inverses of primes
+    for (UINT32 i=0; i<pkObj->nPrimes; i++)
+    {
+        pkObj->pbCrtInverses[i] = pbCurr;
+        pbCurr += SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pParams->nBitsOfModulus );
+    }
+
+    // Private exponents
+    for (UINT32 i=0; i<pkObj->nPubExp; i++)
+    {
+        pkObj->pbPrivExps[i] = pbCurr;
+        pbCurr += SymCryptSizeofIntFromDigits( pkObj->nDigitsOfModulus );
+    }
+
+    // Private exponents modulo each prime (minus 1)
+    for (UINT32 i=0; i<pkObj->nPubExp*pkObj->nPrimes; i++)
+    {
+        pkObj->pbCrtPrivExps[i] = pbCurr;
+        pbCurr += SymCryptSizeofIntFromDigits( pkObj->nDigitsOfModulus );
+    }
+
+    // Setting the magic
+    SYMCRYPT_SET_MAGIC( pkObj );
+
+cleanup:
+    return pkObj;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsakeyWipe( _Out_ PSYMCRYPT_RSAKEY pkDst )
+{
+    // Wipe the whole structure in one go.
+    SymCryptWipe( pkDst, pkDst->cbTotalSize );
+}
+
+#if 0
+VOID
+SYMCRYPT_CALL
+SymCryptRsakeyCopy(
+    _In_    PCSYMCRYPT_RSAKEY  pkSrc,
+    _Out_   PSYMCRYPT_RSAKEY   pkDst )
+{
+    SymCryptFatal( 'rsac' );
+    // This function doesn't work correctly because subobjects might
+    // not have been created yet.
+    // Future: fix this
+
+    //
+    // in-place copy is somewhat common...
+    //
+    if( pkSrc != pkDst )
+    {
+        pkDst->fAlgorithmInfo = pkSrc->fAlgorithmInfo;
+        pkDst->cbTotalSize = pkSrc->cbTotalSize;
+        pkDst->hasPrivateKey = pkSrc->hasPrivateKey;
+        pkDst->nSetBitsOfModulus = pkSrc->nSetBitsOfModulus;
+
+        pkDst->nBitsOfModulus = pkSrc->nBitsOfModulus;
+        pkDst->nDigitsOfModulus = pkSrc->nDigitsOfModulus;
+
+        pkDst->nPubExp = pkSrc->nPubExp;
+        for (UINT32 i=0; i<SYMCRYPT_RSAKEY_MAX_NUMOF_PUBEXPS; i++)
+        {
+            pkDst->au64PubExp[i] = pkSrc->au64PubExp[i];
+        }
+
+        pkDst->nPrimes = pkSrc->nPrimes;
+        for (UINT32 i=0; i<SYMCRYPT_RSAKEY_MAX_NUMOF_PRIMES; i++)
+        {
+            pkDst->nBitsOfPrimes[i] = pkSrc->nBitsOfPrimes[i];
+            pkDst->nDigitsOfPrimes[i] = pkSrc->nDigitsOfPrimes[i];
+        }
+
+        // Copy the objects
+        SymCryptModulusCopy( pkSrc->pmModulus, pkDst->pmModulus );
+
+        for (UINT32 i=0; i< pkSrc->nPrimes; i++)
+        {
+            SymCryptModulusCopy( pkSrc->pmPrimes[i], pkDst->pmPrimes[i] );
+            SymCryptModElementCopy( pkSrc->pmPrimes[i], pkSrc->peCrtInverses[i], pkDst->peCrtInverses[i] );
+        }
+
+        for (UINT32 i=0; i< pkSrc->nPubExp; i++)
+        {
+            SymCryptIntCopy( pkSrc->piPrivExps[i], pkDst->piPrivExps[i] );
+        }
+
+        for (UINT32 i=0; i< pkSrc->nPubExp*pkSrc->nPrimes; i++)
+        {
+            SymCryptIntCopy( pkSrc->piCrtPrivExps[i], pkDst->piCrtPrivExps[i] );
+        }
+    }
+}
+#endif
+
+BOOLEAN
+SYMCRYPT_CALL
+SymCryptRsakeyHasPrivateKey( _In_ PCSYMCRYPT_RSAKEY pkRsakey )
+{
+    return pkRsakey->hasPrivateKey;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeySizeofModulus( _In_ PCSYMCRYPT_RSAKEY pkRsakey )
+{
+    return (pkRsakey->nBitsOfModulus + 7)/8;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeyModulusBits( _In_ PCSYMCRYPT_RSAKEY pkRsakey )
+{
+    return pkRsakey->nBitsOfModulus;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeySizeofPublicExponent(
+    _In_    PCSYMCRYPT_RSAKEY pRsakey,
+            UINT32            index )
+{
+    SYMCRYPT_ASSERT( index == 0 );
+    UNREFERENCED_PARAMETER( index );
+    return SymCryptUint64Bytesize( pRsakey->au64PubExp[0] );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeySizeofPrime(
+    _In_    PCSYMCRYPT_RSAKEY pkRsakey,
+            UINT32            index )
+{
+    return (pkRsakey->nBitsOfPrimes[index] + 7)/8;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeyGetNumberOfPublicExponents( _In_ PCSYMCRYPT_RSAKEY pkRsakey )
+{
+    return pkRsakey->nPubExp;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptRsakeyGetNumberOfPrimes( _In_ PCSYMCRYPT_RSAKEY pkRsakey )
+{
+    return pkRsakey->nPrimes;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptRsakeyCreateAllObjects( _Inout_ PSYMCRYPT_RSAKEY  pkRsakey )
+{
+    // Primes
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        pkRsakey->pmPrimes[i] = SymCryptModulusCreate(
+                                pkRsakey->pbPrimes[i],
+                                SymCryptSizeofModulusFromDigits( pkRsakey->nDigitsOfPrimes[i] ),
+                                pkRsakey->nDigitsOfPrimes[i] );
+        SYMCRYPT_ASSERT( pkRsakey->pmPrimes[i] != NULL );
+    }
+
+    // CRT Inverses of primes
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        pkRsakey->peCrtInverses[i] = SymCryptModElementCreate(
+                                pkRsakey->pbCrtInverses[i],
+                                SymCryptSizeofModElementFromModulus( pkRsakey->pmPrimes[i] ),
+                                pkRsakey->pmPrimes[i] );
+        SYMCRYPT_ASSERT( pkRsakey->peCrtInverses[i] != NULL );
+    }
+
+    // Private exponents
+    for( UINT32 i=0; i<pkRsakey->nPubExp; i++ )
+    {
+        pkRsakey->piPrivExps[i] = SymCryptIntCreate(
+                                    pkRsakey->pbPrivExps[i],
+                                    SymCryptSizeofIntFromDigits( pkRsakey->nDigitsOfModulus ),
+                                    pkRsakey->nDigitsOfModulus );
+        SYMCRYPT_ASSERT( pkRsakey->piPrivExps[i] != NULL );
+    }
+
+    // Private exponents modulo each prime (minus 1)
+    for (UINT32 i=0; i<pkRsakey->nPubExp*pkRsakey->nPrimes; i++)
+    {
+        pkRsakey->piCrtPrivExps[i] = SymCryptIntCreate(
+                                pkRsakey->pbCrtPrivExps[i],
+                                SymCryptSizeofIntFromDigits( pkRsakey->nDigitsOfPrimes[i] ),
+                                pkRsakey->nDigitsOfPrimes[i] );
+        SYMCRYPT_ASSERT( pkRsakey->piCrtPrivExps[i] != NULL );
+    }
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyCalculatePrivateFields(
+    _Inout_ PSYMCRYPT_RSAKEY  pkRsakey,
+    _Out_   PSYMCRYPT_DIVISOR pdTmp,    // Temporary of nMaxDigitsOfPrimes
+    _Out_   PSYMCRYPT_INT     piPhi,    // Temporary of nDigitsOfModulus
+    _Out_   PSYMCRYPT_INT     piAcc,    // Temporary of nMaxDigitsOfPrimes + nDigitsOfModulus
+    _Out_writes_bytes_( cbScratch )
+            PBYTE             pbScratch,
+            SIZE_T            cbScratch,
+            UINT32            flags
+)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BYTE tmpGcdBuf[ SYMCRYPT_SIZEOF_INT_FROM_BITS( 64 ) + SYMCRYPT_ASYM_ALIGN_VALUE];
+    PSYMCRYPT_INT piTmpGcd;
+
+    // Use pdTmp as int scratch
+    PSYMCRYPT_INT piScr = SymCryptIntFromDivisor(pdTmp);
+
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION;
+
+    if ( ( flags & ~allowedFlags ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // We need a 1-digit tmp value to store the GCD in.
+    // Simpler to put it on the stack than to add full scratch size computation support to this function
+    piTmpGcd = SymCryptIntCreate( SYMCRYPT_ASYM_ALIGN_UP( tmpGcdBuf ), sizeof( tmpGcdBuf ) - SYMCRYPT_ASYM_ALIGN_VALUE, SymCryptDigitsFromBits( 64 ) );
+
+    // Run the CRT generation
+    scError = SymCryptCrtGenerateInverses( pkRsakey->nPrimes, pkRsakey->pmPrimes, 0, pkRsakey->peCrtInverses, pbScratch, cbScratch);
+    if (scError!=SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Calculate Phi
+    SymCryptIntSetValueUint32( 1, piPhi );
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        // piScr can have the different number of digits than each prime
+        scError = SymCryptIntCopyMixedSize( SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ), piScr );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+             goto cleanup;
+        }
+        SymCryptIntSubUint32( piScr, 1, piScr );         // p-1
+        SymCryptIntMulMixedSize( piScr, piPhi, piAcc, pbScratch, cbScratch );
+        scError = SymCryptIntCopyMixedSize( piAcc, piPhi );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    // Calculate the private exponents
+    for (UINT32 i=0; i<pkRsakey->nPubExp; i++)
+    {
+        // IntExtendedGcd requirements:
+        //      - First argument > 0: piPhi as the product of p-1's
+        //      - Second argument: odd, verified below
+        // We also reject public exponent 1, as that is obviously unsafe.
+        if( pkRsakey->au64PubExp[i] == 1 || (pkRsakey->au64PubExp[i] & 1) != 1)
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        // Calculate D
+        SymCryptIntSetValueUint64( pkRsakey->au64PubExp[i], piScr );
+
+        // Calculate D
+        SymCryptIntExtendedGcd(
+            piPhi,
+            piScr,
+            SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN,
+            piTmpGcd,   // Gcd
+            NULL,   // Lcm
+            NULL,   // InvSrc1ModSrc2
+            pkRsakey->piPrivExps[i],
+            pbScratch,
+            cbScratch);
+
+        if( !SymCryptIntIsEqualUint32( piTmpGcd, 1 ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+    //Calculate the private exponents modulo each prime minus 1
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        scError = SymCryptIntCopyMixedSize( SymCryptIntFromModulus(pkRsakey->pmPrimes[i]), SymCryptIntFromDivisor(pdTmp) );
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // IntToDivisor requirement:
+        //      Each prime has at least SYMCRYPT_RSAKEY_MIN_BITSIZE_PRIME bits --> P-1 > 0
+        SymCryptIntSubUint32( SymCryptIntFromDivisor(pdTmp), 1, SymCryptIntFromDivisor(pdTmp) );
+        SymCryptIntToDivisor(
+            SymCryptIntFromDivisor(pdTmp),
+            pdTmp,
+            pkRsakey->nPubExp,
+            0,
+            pbScratch,
+            cbScratch );
+
+        for (UINT32 j=0; j<pkRsakey->nPubExp; j++)
+        {
+            SymCryptIntDivMod(
+                pkRsakey->piPrivExps[j],
+                pdTmp,
+                NULL,
+                piPhi,      // Set it to Phi as each private exponent might have different size
+                pbScratch,
+                cbScratch );
+
+            scError = SymCryptIntCopyMixedSize( piPhi, pkRsakey->piCrtPrivExps[ j*pkRsakey->nPrimes + i ]);
+            if (scError!=SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    // Check that the product of the primes is in fact the modulus
+    if( (flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION) == 0 )
+    {
+        if( pkRsakey->nPrimes != 2 )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        SymCryptIntMulMixedSize(
+            SymCryptIntFromModulus(pkRsakey->pmPrimes[0]),
+            SymCryptIntFromModulus(pkRsakey->pmPrimes[1]),
+            piAcc,
+            pbScratch, cbScratch );
+
+        if( !SymCryptIntIsEqual( piAcc, SymCryptIntFromModulus( pkRsakey->pmModulus ) ) )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyGenerate(
+    _Inout_                     PSYMCRYPT_RSAKEY    pkRsakey,
+    _In_reads_opt_( nPubExp )   PCUINT64            pu64PubExp,
+                                UINT32              nPubExp,
+    _In_                        UINT32              flags )
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    // 3 sizes of temporary elements:
+    //  - ndPrimes = number of digit size of each prime (we choose it to be the same for all primes)
+    //  - ndMod = pkRsakey->nDigitsOfModulus
+    //  - ndLarge = ndPrimes + ndMod
+
+    UINT32 ndPrimes = 0;
+
+    UINT32 cbPrimes = 0;
+    PSYMCRYPT_INT piLow = NULL;
+    PSYMCRYPT_INT piHigh = NULL;
+
+    UINT32 cbDivisor = 0;
+    PSYMCRYPT_DIVISOR pdTmp = NULL;
+
+    UINT32 ndMod = pkRsakey->nDigitsOfModulus;
+    UINT32 cbMod = 0;
+    PSYMCRYPT_INT piPhi = NULL;
+
+    UINT32 ndLarge = 0;
+    UINT32 cbLarge = 0;
+    PSYMCRYPT_INT piAcc = NULL;
+
+    PBYTE           pbScratch = NULL;
+    UINT32          cbScratch = 0;
+    PBYTE           pbFnScratch = NULL;
+    UINT32          cbFnScratch = 0;
+
+    UINT32 maxTries = 0;                    // For the prime generation (and the modulus operations ?)
+    UINT32 primeBits = 0;
+
+    const UINT64 defaultExponent = RSA_DEFAULT_PUBLIC_EXPONENT;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_RSAKEY_SIGN | SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+    // Ensure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS | algorithmFlags;
+
+    if ( ( ( flags & ~allowedFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // SymCryptRsaSignVerifyPct requires the generated key to be at least 496 bits to avoid fatal
+    // Require caller to specify NO_FIPS for up to 1024 bits as running FIPS tests on too-small keys
+    // does not make it FIPS certifiable and gives the wrong impression to callers
+    if ( ( (flags & SYMCRYPT_FLAG_KEY_NO_FIPS) == 0 ) &&
+         ( pkRsakey->nSetBitsOfModulus < SYMCRYPT_RSAKEY_FIPS_MIN_BITSIZE_MODULUS ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Handle the default exponent case
+    if( pu64PubExp == NULL && nPubExp == 0 )
+    {
+        pu64PubExp = &defaultExponent;
+        nPubExp = 1;
+    }
+
+    // Make sure we have:
+    // - exactly 2 primes
+    // - the right number of public exponents
+    // - exactly 1 public exponent
+    if (pkRsakey->nPrimes != 2 || nPubExp != pkRsakey->nPubExp || nPubExp != 1 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Copy the public exponent into the key
+    pkRsakey->au64PubExp[0] = pu64PubExp[0];
+
+    // Before doing anything calculate all the needed sizes
+    // The size limits were checked in SymCryptRsakeyCreate which is the only way to create an Rsakey object.
+    pkRsakey->nBitsOfModulus = pkRsakey->nSetBitsOfModulus;             // This will be the exact bit size of our modulus
+
+    pkRsakey->nBitsOfPrimes[0] = (pkRsakey->nBitsOfModulus + 1)/2;
+    pkRsakey->nBitsOfPrimes[1] = pkRsakey->nBitsOfModulus/2;            // The second prime is one bit smaller for odd-length moduli
+
+    pkRsakey->nDigitsOfPrimes[0] = SymCryptDigitsFromBits(pkRsakey->nBitsOfPrimes[0]);
+    pkRsakey->nDigitsOfPrimes[1] = SymCryptDigitsFromBits(pkRsakey->nBitsOfPrimes[1]);
+
+    pkRsakey->nMaxDigitsOfPrimes = SYMCRYPT_MAX(pkRsakey->nDigitsOfPrimes[0], pkRsakey->nDigitsOfPrimes[1]);
+
+    ndPrimes = pkRsakey->nMaxDigitsOfPrimes;
+    ndLarge = ndPrimes + ndMod;
+
+    primeBits = SYMCRYPT_MAX(pkRsakey->nBitsOfPrimes[0],pkRsakey->nBitsOfPrimes[1]);
+    maxTries = 100 * primeBits;
+
+    // Create all the SymCryptObjects
+    SymCryptRsakeyCreateAllObjects( pkRsakey );
+
+    // Allocate the temp integers and the scratch space
+    // All sizes are limited by the modulus sizes verified in SymCryptRsakeyCreate
+    cbPrimes = SymCryptSizeofIntFromDigits( ndPrimes );
+    cbMod = SymCryptSizeofIntFromDigits( ndMod );
+    cbLarge = SymCryptSizeofIntFromDigits( ndLarge );
+    cbDivisor = SymCryptSizeofDivisorFromDigits( ndPrimes );
+
+    cbScratch = 2*cbPrimes + cbMod + cbLarge + cbDivisor +
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_PRIME_GEN(ndPrimes),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(ndMod),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL(ndMod),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_CRT_GENERATION(ndPrimes),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_EXTENDED_GCD(ndMod),
+                SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR(ndPrimes),
+                     SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( ndMod, ndPrimes )
+                    ))))));
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    pbFnScratch = pbScratch;
+    cbFnScratch = cbScratch;
+
+    // Create temporaries
+	// dcl - this would be easier to review with one statement per line
+    piLow = SymCryptIntCreate( pbFnScratch, cbPrimes, ndPrimes ); pbFnScratch += cbPrimes; cbFnScratch -= cbPrimes;
+    piHigh = SymCryptIntCreate( pbFnScratch, cbPrimes, ndPrimes ); pbFnScratch += cbPrimes; cbFnScratch -= cbPrimes;
+
+    piPhi = SymCryptIntCreate( pbFnScratch, cbMod, ndMod ); pbFnScratch += cbMod; cbFnScratch -= cbMod;
+
+    piAcc = SymCryptIntCreate( pbFnScratch, cbLarge, ndLarge ); pbFnScratch += cbLarge; cbFnScratch -= cbLarge;
+
+    pdTmp = SymCryptDivisorCreate( pbFnScratch, cbDivisor, ndPrimes ); pbFnScratch += cbDivisor; cbFnScratch -= cbDivisor;
+
+    // ***Prime generation limits***
+    //
+    // If nBitsOfModulus is even (main case)
+    //  Low limit   = 2^{primeBits-1} + 2^{primeBits - 2}
+    //  High limit  = 2^primeBits - 1
+    //
+    // If nBitsOfModulus is odd we use different
+    // limits for the two primes (until we have an integer sqrt function)
+    //
+    // For the first
+    //      Low limit   = 2^{primeBits-1} + 2^{primeBits - 2}
+    //      High limit  = 2^primeBits - 1
+    // For the second
+    //      Low limit   = 2^{primeBits-2} + 2^{primeBits - 3}
+    //      High limit  = 2^{primeBits-1} - 1
+    //
+    // Notice that nBitsOfModulus is a public value.
+    //
+    // *** TODO: This works only for 2 primes to give modulus
+    //           of exactly nBitsOfModulus bits.
+
+    SymCryptIntSetValueUint32( 3, piLow );
+    SymCryptIntMulPow2( piLow, primeBits - 2, piLow );
+
+    SymCryptIntSetValueUint32( 1, piHigh );
+    SymCryptIntMulPow2( piHigh, primeBits, piHigh );
+    SymCryptIntSubUint32( piHigh, 1, piHigh );
+
+    // Generate primes and at the same time accumulate their product into piPhi
+    SymCryptIntSetValueUint32( 1, piPhi );
+    for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+    {
+        if ( ((pkRsakey->nBitsOfModulus % 2)==1) && (i>0) )
+        {
+            SymCryptIntDivPow2( piLow, 1, piLow );
+            SymCryptIntDivPow2( piHigh, 1, piHigh );
+        }
+
+        // IntGenerateRandomPrime requirement:
+        //      piLow > 3 since nBitsOfModulus is bounded by
+        //      SYMCRYPT_RSAKEY_MIN_BITSIZE_MODULUS.
+        scError = SymCryptIntGenerateRandomPrime(
+                    piLow,
+                    piHigh,
+                    pu64PubExp,
+                    nPubExp,
+                    maxTries,
+                    0,
+                    SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ),
+                    pbFnScratch,
+                    cbFnScratch);
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+
+        // IntToModulus requirement:
+        //      piLow > 0 --> pkRsakey->pmPrimes[i] > 0
+        SymCryptIntToModulus(
+                SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ),
+                pkRsakey->pmPrimes[i],
+                pkRsakey->nBitsOfModulus,       // Average number of operations
+                SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+                pbFnScratch,
+                cbFnScratch );
+
+        SymCryptIntMulMixedSize( SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ), piPhi, piAcc, pbFnScratch, cbFnScratch );   // P_i * Product
+        scError = SymCryptIntCopyMixedSize( piAcc, piPhi );     // Move the result to piPhi
+        if (scError!=SYMCRYPT_NO_ERROR)
+        {
+            goto cleanup;
+        }
+    }
+
+    // IntToModulus requirement:
+    //      piPhi product of non-zero primes --> piPhi > 0
+    SymCryptIntCopy( piPhi, SymCryptIntFromModulus( pkRsakey->pmModulus ) );
+    SymCryptIntToModulus(
+                SymCryptIntFromModulus( pkRsakey->pmModulus ),
+                pkRsakey->pmModulus,
+                pkRsakey->nBitsOfModulus,       // Average number of operations
+                SYMCRYPT_FLAG_DATA_PUBLIC,
+                pbFnScratch,
+                cbFnScratch );
+
+    if ( SymCryptIntBitsizeOfValue( piPhi ) != pkRsakey->nBitsOfModulus)
+    {
+        scError = SYMCRYPT_EXTERNAL_FAILURE;    // This should never happen (make it assert)
+        goto cleanup;
+    }
+
+    // Calculate the rest of the fields
+    scError = SymCryptRsakeyCalculatePrivateFields( pkRsakey, pdTmp, piPhi, piAcc, pbFnScratch, cbFnScratch, 0 );
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    pkRsakey->hasPrivateKey = TRUE;
+
+    pkRsakey->fAlgorithmInfo = flags; // We want to track all of the flags in the Rsakey
+
+    if ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // Ensure RSA algorithm selftest is run before first use of RSA algorithm
+        // Per FIPS 140-3 IG, this selftest cannot be a PCT
+        SYMCRYPT_RUN_SELFTEST_ONCE(
+            SymCryptRsaSelftest,
+            SYMCRYPT_SELFTEST_ALGORITHM_RSA);
+
+        // Run SignVerify PCT on generated keypair
+        // Our current understanding is that this PCT is sufficient for both RSA_SIGN and RSA_ENCRYPT
+
+        // Unconditionally set the sign flag to enable SignVerify PCT on encrypt-only keypair
+        pkRsakey->fAlgorithmInfo |= SYMCRYPT_FLAG_RSAKEY_SIGN;
+
+        SYMCRYPT_RUN_KEY_GEN_PCT(
+            SymCryptRsaSignVerifyPct,
+            pkRsakey,
+            SYMCRYPT_PCT_RSA_SIGN );
+
+        // Unset the sign flag before returning encrypt-only keypair
+        if ( ( flags & SYMCRYPT_FLAG_RSAKEY_SIGN ) == 0 )
+        {
+            pkRsakey->fAlgorithmInfo ^= SYMCRYPT_FLAG_RSAKEY_SIGN;
+        }
+    }
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+// The maximum number of iterations we use in probabilistic prime recovery method
+// If n, e, d are valid then successful prime recover for each iteration should
+// occur with probability ~1/2; with 100 iterations we fail for a valid private
+// exponent with probability ~2^-100
+#define SYMCRYPT_MAX_PRIME_RECOVERY_ITERATIONS  (100)
+
+#define SYMCRYPT_SCRATCH_BYTES_FOR_PRIME_RECOVERY( _ndMod, _ndPubExp, _nBitsMod ) \
+    SymCryptSizeofIntFromDigits( _ndMod ) + /* Space for piPrivExp*/ \
+    SymCryptSizeofIntFromDigits( _ndPubExp ) + /* Space for piPubExp*/ \
+    SymCryptSizeofIntFromDigits( _ndMod + _ndPubExp ) + /* Space for piExpProd*/ \
+    (4*SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( _nBitsMod )) + /* Space for peTmpY, peTmpX, peOne, peNegOne */\
+    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_MUL( _ndMod + _ndPubExp ), /* Space for SymCryptIntMulMixedSize */ \
+    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( _ndMod ), /* Space for other SymCryptMod* */ \
+    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( _ndMod ), /* Space for SymCryptModExp */ \
+    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_EXTENDED_GCD( _ndMod ), /* Space for SymCryptIntExtendedGcd */ \
+    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS( _ndMod ), /* Space for SymCryptIntToModulus */ \
+            SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( _ndMod, _ndMod ) /* Space for SymCryptIntDivMod */ \
+        )))))
+
+static
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyCalculatePrimesFromPrivateExponent(
+    _Inout_ PSYMCRYPT_RSAKEY        pkRsakey, // must already have modulus and public exponent set
+    _In_reads_bytes_( cbPrivateExponent )
+            PCBYTE                  pbPrivateExponent,
+            SIZE_T                  cbPrivateExponent,
+            SYMCRYPT_NUMBER_FORMAT  numFormat,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE                   pbScratch,
+            UINT32                  cbScratch )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // 3 digit sizes of temporary integers:
+    //  - ndMod = pkRsakey->nDigitsOfModulus
+    //  - ndPubExp = digits for a UINT64 public exponent
+    //  - ndExpProd = ndMod + ndPubExp
+
+    UINT32 ndMod = pkRsakey->nDigitsOfModulus;
+    UINT32 cbMod = SymCryptSizeofIntFromDigits( ndMod );
+
+    UINT32 ndPubExp = SymCryptDigitsFromBits( 64 );
+    UINT32 cbPubExp = SymCryptSizeofIntFromDigits( ndPubExp );
+
+    UINT32 nBitsExpProd = 0; // we compute this later before use
+    UINT32 ndExpProd = ndMod + ndPubExp;
+    UINT32 cbExpProd = SymCryptSizeofIntFromDigits( ndExpProd );
+
+    UINT32 cbModElement = SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( pkRsakey->nBitsOfModulus );
+
+    PSYMCRYPT_INT piPrivExp = NULL;
+    PSYMCRYPT_INT piPubExp = NULL;
+    PSYMCRYPT_INT piExpProd = NULL;
+    PSYMCRYPT_MODELEMENT peTmpY = NULL;
+    PSYMCRYPT_MODELEMENT peTmpX = NULL;
+    PSYMCRYPT_MODELEMENT peTmpPtr = NULL;
+    PSYMCRYPT_MODELEMENT peOne = NULL;
+    PSYMCRYPT_MODELEMENT peNegOne = NULL;
+
+    PBYTE   pbFnScratch = pbScratch;
+    UINT32  cbFnScratch = cbScratch;
+
+    UINT64  low64ExpProd = 0;
+    UINT32  trailingZeros = 0;
+
+    BOOL bFoundNonTrivialRoot = FALSE;
+
+    //
+    // Recover primes from private exponent using probabilistic prime-factor recovery method
+    // See SP800-56B rev2 Appendix C.1 and Boneh 1999
+    //
+    SYMCRYPT_ASSERT( pkRsakey->nPrimes == 2 );
+    SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_SCRATCH_BYTES_FOR_PRIME_RECOVERY(ndMod, ndPubExp, pkRsakey->nBitsOfModulus) );
+
+    piPrivExp = SymCryptIntCreate( pbFnScratch, cbMod, ndMod );
+    SYMCRYPT_ASSERT( piPrivExp != NULL );
+    pbFnScratch += cbMod;
+    cbFnScratch -= cbMod;
+    piPubExp = SymCryptIntCreate( pbFnScratch, cbPubExp, ndPubExp );
+    SYMCRYPT_ASSERT( piPubExp != NULL );
+    pbFnScratch += cbPubExp;
+    cbFnScratch -= cbPubExp;
+    piExpProd = SymCryptIntCreate( pbFnScratch, cbExpProd, ndExpProd );
+    SYMCRYPT_ASSERT( piExpProd != NULL );
+    pbFnScratch += cbExpProd;
+    cbFnScratch -= cbExpProd;
+
+    peTmpY = SymCryptModElementCreate( pbFnScratch, cbModElement, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peTmpY != NULL );
+    pbFnScratch += cbModElement;
+    cbFnScratch -= cbModElement;
+    peTmpX = SymCryptModElementCreate( pbFnScratch, cbModElement, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peTmpX != NULL );
+    pbFnScratch += cbModElement;
+    cbFnScratch -= cbModElement;
+    peOne = SymCryptModElementCreate( pbFnScratch, cbModElement, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peOne != NULL );
+    pbFnScratch += cbModElement;
+    cbFnScratch -= cbModElement;
+    peNegOne = SymCryptModElementCreate( pbFnScratch, cbModElement, pkRsakey->pmModulus );
+    SYMCRYPT_ASSERT( peNegOne != NULL );
+    pbFnScratch += cbModElement;
+    cbFnScratch -= cbModElement;
+
+    // Ensure that modulus is odd - this is required for later SymCryptIntExtendedGcd
+    if( (SymCryptIntGetValueLsbits32(SymCryptIntFromModulus( pkRsakey->pmModulus ))& 1)==0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Import private exponent
+    scError = SymCryptIntSetValue(pbPrivateExponent, cbPrivateExponent, numFormat, piPrivExp);
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        // The integer cannot fit the private exponent (SYMCRYPT_BUFFER_TOO_SMALL),
+        // only if the caller providing a private exponent larger than the public modulus
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Basic range check
+    if( !SymCryptIntIsLessThan(piPrivExp, SymCryptIntFromModulus(pkRsakey->pmModulus)) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Given range check, we can guarantee to compute
+    // Private exponent (d) * Public exponent (e)
+    // In piExpProd without overflow
+    SymCryptIntSetValueUint64( pkRsakey->au64PubExp[0], piPubExp );
+
+    // compute upper bound on product bit count based on public data (nBitsOfModulus (public) >= nBitsOfPrivateExponent (private))
+    nBitsExpProd = pkRsakey->nBitsOfModulus + SymCryptIntBitsizeOfValue( piPubExp );
+
+    SymCryptIntMulMixedSize( piPrivExp, piPubExp, piExpProd, pbFnScratch, cbFnScratch );
+
+    // Ensure d*e is odd
+    low64ExpProd = SymCryptIntGetValueLsbits64( piExpProd );
+
+    if( (low64ExpProd & 1) == 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Compute how many trailing zeros in m = d*e - 1
+    //
+    // We are variable time w.r.t. the number of trailing (up to 64) zeroes. An attacker using
+    // sidechannels to determine the number of trailing zeroes of m can glean information about
+    // the private exponent proportionate to the number of trailing zeroes. As we bound this to
+    // 64, at most an attacker can theoretically determine 64-bits of an expected 2048-bits of
+    // private exponent - and they can only do this for 1 in 2^64 keys.
+    //
+    // It would be possible to mask the number of trailing zeroes from sidechannels by always
+    // squaring by 64 times in the inner loop below and using masked operations to select out
+    // any found non-trivial root. We can consider doing this as a hardening measure if this API
+    // does see a lot of usage, but the expectation is that this method will almost never be
+    // used and it is just not enough of a leak for an attacker to even try to measuring.
+    trailingZeros = SymCryptCountTrailingZeros64( low64ExpProd-1 );
+
+    // If there are 64 trailing zeroes then we abort because the prime factor recovery method
+    // could theoretically leak more than 64-bits of the private exponent. The likelihood of any
+    // key which has this many trailing zeroes _ever_ having being generated by a legitimate key
+    // generation process is extremely small given the cost of RSA key generation. This much more
+    // likely indicates faulty inputs or a hardware fault rather than a legitimate keypair we
+    // should try to import.
+    if( trailingZeros == 64 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptIntDivPow2( piExpProd, trailingZeros, piExpProd );  // r = m >> t
+    SymCryptModElementSetValueUint32( 1, pkRsakey->pmModulus, peOne, pbFnScratch, cbFnScratch );
+    SymCryptModElementSetValueNegUint32( 1, pkRsakey->pmModulus, peNegOne, pbFnScratch, cbFnScratch );
+
+    for( UINT32 i=0; i<SYMCRYPT_MAX_PRIME_RECOVERY_ITERATIONS; i++ )
+    {
+        // y is random value g in [2,n-2]
+        SymCryptModSetRandom( pkRsakey->pmModulus, peTmpY, 0, pbFnScratch, cbFnScratch );
+
+        // ModExp y = g^r in place
+        // could make this a bit faster and leakier using trailingZeros to reduce nBitsExp, but
+        // not normally a big performance win
+        SymCryptModExp(
+            pkRsakey->pmModulus,
+            peTmpY,
+            piExpProd,
+            nBitsExpProd-1,
+            0,
+            peTmpY,
+            pbFnScratch, cbFnScratch );
+
+        // if y == 1 or y == -1, start over (we found a trivial root of 1)
+        if( SymCryptModElementIsEqual( pkRsakey->pmModulus, peTmpY, peOne ) ||
+            SymCryptModElementIsEqual( pkRsakey->pmModulus, peTmpY, peNegOne ) )
+        {
+            continue;
+        }
+
+        for( UINT32 j=1; j<=trailingZeros; j++ )
+        {
+            // x = y^2
+            SymCryptModSquare( pkRsakey->pmModulus, peTmpY, peTmpX, pbFnScratch, cbFnScratch );
+
+            // if x == 1 then y is a non-trivial root of 1 (it is not -1 or 1)
+            if( SymCryptModElementIsEqual( pkRsakey->pmModulus, peTmpX, peOne) )
+            {
+                bFoundNonTrivialRoot = TRUE;
+                break;
+            }
+
+            // if x == -1, start over
+            if( SymCryptModElementIsEqual( pkRsakey->pmModulus, peTmpX, peNegOne) )
+            {
+                break; // just break out of inner loop; continues outer loop
+            }
+
+            // swap x and y
+            peTmpPtr = peTmpY;
+            peTmpY = peTmpX;
+            peTmpX = peTmpPtr;
+        }
+        if( bFoundNonTrivialRoot )
+        {
+            break;
+        }
+    }
+
+    if( !bFoundNonTrivialRoot )
+    {
+        // we failed to find a non-trivial root of 1, so we cannot recover prime factors
+        // it is almost certain that this means that the inputs were wrong
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // piPrivExp = y
+    SymCryptModElementToInt( pkRsakey->pmModulus, peTmpY, piPrivExp, pbFnScratch, cbFnScratch );
+    // piPrivExp = y-1 (we know this cannot borrow as y^2 is 1, so y != 0)
+    SymCryptIntSubUint32( piPrivExp, 1, piPrivExp );
+
+    // piPrivExp = p0 = GCD(y-1, n)
+    SymCryptIntExtendedGcd(
+        piPrivExp,
+        SymCryptIntFromModulus( pkRsakey->pmModulus ),
+        SYMCRYPT_FLAG_GCD_INPUTS_NOT_BOTH_EVEN,
+        piPrivExp,
+        NULL,
+        NULL,
+        NULL,
+        pbFnScratch, cbFnScratch );
+
+    // compute the sizes of the primes
+    pkRsakey->nBitsOfPrimes[0] = SymCryptIntBitsizeOfValue(piPrivExp);
+    pkRsakey->nBitsOfPrimes[1] = pkRsakey->nBitsOfModulus - pkRsakey->nBitsOfPrimes[0];
+    for( UINT32 i=0; i<2; i++ )
+    {
+        pkRsakey->nDigitsOfPrimes[i] = SymCryptDigitsFromBits(pkRsakey->nBitsOfPrimes[i]);
+        if( pkRsakey->nBitsOfPrimes[i] < SYMCRYPT_RSAKEY_MIN_BITSIZE_PRIME )
+        {
+            scError = SYMCRYPT_WRONG_KEY_SIZE;
+            goto cleanup;
+        }
+    }
+    pkRsakey->nMaxDigitsOfPrimes = SYMCRYPT_MAX(pkRsakey->nDigitsOfPrimes[0], pkRsakey->nDigitsOfPrimes[1]);
+
+    // Create all the objects
+    SymCryptRsakeyCreateAllObjects(pkRsakey);
+
+    scError = SymCryptIntCopyMixedSize( piPrivExp, SymCryptIntFromModulus( pkRsakey->pmPrimes[0] ) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        // only fails if we computed the wrong bit-size for the primes above
+        scError = SYMCRYPT_HARDWARE_FAILURE;
+        goto cleanup;
+    }
+
+    SymCryptIntToModulus(   SymCryptIntFromModulus( pkRsakey->pmPrimes[0] ),
+                            pkRsakey->pmPrimes[0],
+                            pkRsakey->nBitsOfModulus,   // Average number of operations
+                            SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+                            pbFnScratch, cbFnScratch );
+
+    SymCryptIntDivMod( SymCryptIntFromModulus( pkRsakey->pmModulus ),
+                       SymCryptDivisorFromModulus( pkRsakey->pmPrimes[0] ),
+                       piExpProd, // n / p0 - use piExpProd as Quotient.nDigits must be >= Src.nDigits
+                       piPrivExp, // n % p0 - use piPrivExp as Remainder.nDigits must be >= Divisor.nDigits
+                       pbFnScratch, cbFnScratch );
+
+    // Check remainder from dividing n by p0 is 0
+    if( !SymCryptIntIsEqualUint32( piPrivExp, 0 ) )
+    {
+        // Should always be true as p0 is GCD(y-1, n) so is definitionally a divisor of n
+        // Failure here indicates something wrong in our math, or hardware failure
+        scError = SYMCRYPT_HARDWARE_FAILURE;
+        goto cleanup;
+    }
+
+    scError = SymCryptIntCopyMixedSize( piExpProd, SymCryptIntFromModulus( pkRsakey->pmPrimes[1] ) );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        // only fails if we computed the wrong bit-size for the primes above
+        scError = SYMCRYPT_HARDWARE_FAILURE;
+        goto cleanup;
+    }
+
+    SymCryptIntToModulus(   SymCryptIntFromModulus( pkRsakey->pmPrimes[1] ),
+                            pkRsakey->pmPrimes[1],
+                            pkRsakey->nBitsOfModulus,   // Average number of operations
+                            SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+                            pbFnScratch, cbFnScratch );
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeySetValueInternal(
+    _In_reads_bytes_( cbModulus )               PCBYTE                  pbModulus,
+                                                SIZE_T                  cbModulus,
+    _In_reads_( nPubExp )                       PCUINT64                pu64PubExp,
+                                                UINT32                  nPubExp,
+    _In_reads_bytes_opt_( cbPrivateExponent )   PCBYTE                  pbPrivateExponent,
+                                                SIZE_T                  cbPrivateExponent,
+    _In_reads_opt_( nPrimes )                   PCBYTE *                ppPrimes,
+    _In_reads_opt_( nPrimes )                   SIZE_T *                pcbPrimes,
+                                                UINT32                  nPrimes,
+                                                SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                                UINT32                  flags,
+    _Inout_                                     PSYMCRYPT_RSAKEY        pkRsakey )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // 3 digit sizes of temporary integers:
+    //  - ndPrimes = max digitsize of prime buffers
+    //  - ndMod = pkRsakey->nDigitsOfModulus
+    //  - ndLarge = ndPrimes + ndMod
+
+    UINT32 cbDivisor = 0;
+    PSYMCRYPT_DIVISOR pdTmp = NULL;
+
+    UINT32 ndMod = 0;
+    UINT32 cbMod = 0;
+    PSYMCRYPT_INT piPhi = NULL;
+
+    UINT32 cbLarge = 0;
+    PSYMCRYPT_INT piAcc = NULL;
+
+    PBYTE           pbScratch = NULL;
+    UINT32          cbScratch = 0;
+    PBYTE           pbFnScratch = NULL;
+    UINT32          cbFnScratch = 0;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_RSAKEY_SIGN | SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+    // Ensure only allowed flags are specified
+    UINT32 allowedFlags = SYMCRYPT_FLAG_KEY_NO_FIPS | SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION | algorithmFlags;
+
+    if ( ( ( flags & ~allowedFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check that minimal validation flag only specified with no fips
+    if ( ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 ) &&
+         ( ( flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION ) != 0 ) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Internal requirement that private key is either specified by primes or by private exponent, not both
+    // This is not exposed to external API surface - if we were to dynamically check, the SYMCRYPT_ERROR
+    // should indicate internal logic error - for now just assert
+    SYMCRYPT_ASSERT( (nPrimes==0) || (pbPrivateExponent==NULL) );
+
+    // Check if the arguments are correct
+    if ( (pbModulus==NULL) || (cbModulus==0) ||         // Modulus is needed
+         (nPubExp != 1) || (pu64PubExp==NULL) ||        // Exactly 1 public exponent is needed
+         ((nPrimes != 2) && (nPrimes != 0)) ||
+         ((nPrimes == 2) && ((ppPrimes==NULL) || (pcbPrimes==NULL) ||
+                             (ppPrimes[0]==NULL) || (ppPrimes[1]==NULL) ||
+                             (pcbPrimes[0]==0) || (pcbPrimes[1]==0))) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    ndMod = pkRsakey->nDigitsOfModulus;
+
+    // Calculate scratch spaces
+	// No integer overflows as all numbers are limited by ndMod which is checked during Create
+    if ( (pbPrivateExponent != NULL) || (nPrimes > 0) )
+    {
+        if( pkRsakey->nPrimes != 2 )
+        {
+            // The key was not allocated with space for private key material
+            // so we cannot set it with private key material
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        cbMod = SymCryptSizeofIntFromDigits( ndMod );
+        cbLarge = SymCryptSizeofIntFromDigits( 2 * ndMod ); // 2*ndMod is still < SymCryptDigitsFromBits(SYMCRYPT_INT_MAX_BITS)
+        cbDivisor = SymCryptSizeofDivisorFromDigits( ndMod );
+
+        cbScratch = cbMod + cbLarge + cbDivisor +
+                    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(ndMod),
+                    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_CRT_GENERATION(ndMod),
+                    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_EXTENDED_GCD(ndMod),
+                    SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR(ndMod),
+                         SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( ndMod, ndMod )
+                        ))));
+
+        if( pbPrivateExponent != NULL )
+        {
+            // We use at least as much scratch space when importing by private exponent, but probably more
+            SYMCRYPT_ASSERT( SymCryptDigitsFromBits( 64 ) == 1 );
+
+            cbScratch = SYMCRYPT_MAX( cbScratch,
+                SYMCRYPT_SCRATCH_BYTES_FOR_PRIME_RECOVERY(ndMod, 1, pkRsakey->nSetBitsOfModulus) );
+        }
+    }
+    else
+    {
+        cbScratch = SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(ndMod);
+    }
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc( cbScratch );
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    // Modulus
+    scError = SymCryptIntSetValue( pbModulus, cbModulus, numFormat, SymCryptIntFromModulus( pkRsakey->pmModulus ) );
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Compute actual modulus size, and check that it isn't bigger than the created size
+    pkRsakey->nBitsOfModulus = SymCryptIntBitsizeOfValue(SymCryptIntFromModulus(pkRsakey->pmModulus));
+    if (pkRsakey->nBitsOfModulus > pkRsakey->nSetBitsOfModulus)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (pkRsakey->nBitsOfModulus < SYMCRYPT_RSAKEY_MIN_BITSIZE_MODULUS)
+    {
+        scError = SYMCRYPT_WRONG_KEY_SIZE;
+        goto cleanup;
+    }
+
+    // IntToModulus requirement:
+    //      nBitsOfModulus >= SYMCRYPT_RSAKEY_MIN_BITSIZE_MODULUS --> pmModulus > 0
+    SymCryptIntToModulus(
+            SymCryptIntFromModulus( pkRsakey->pmModulus ),
+            pkRsakey->pmModulus,
+            pkRsakey->nBitsOfModulus,
+            SYMCRYPT_FLAG_DATA_PUBLIC,
+            pbScratch,
+            cbScratch );
+
+    // Public exponents
+    pkRsakey->nPubExp = nPubExp;
+    for (UINT32 i = 0; i<pkRsakey->nPubExp; i++)
+    {
+        pkRsakey->au64PubExp[i] = pu64PubExp[i];
+    }
+
+    //  Private key import either by private exponent or primes
+    if ( (pbPrivateExponent != NULL) || (nPrimes > 0) )
+    {
+        if (pbPrivateExponent != NULL)
+        {
+            // Private exponent
+            scError = SymCryptRsakeyCalculatePrimesFromPrivateExponent(
+                pkRsakey,
+                pbPrivateExponent, cbPrivateExponent,
+                numFormat,
+                pbScratch, cbScratch );
+            if (scError != SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+
+            pbFnScratch = pbScratch;
+            cbFnScratch = cbScratch;
+
+            // Create temporary piPhi
+            piPhi = SymCryptIntCreate( pbFnScratch, cbMod, ndMod ); pbFnScratch += cbMod; cbFnScratch -= cbMod;
+        }
+        else //if (nPrimes > 0)
+        {
+            // Primes
+            pbFnScratch = pbScratch;
+            cbFnScratch = cbScratch;
+
+            // Create temporary piPhi
+            piPhi = SymCryptIntCreate( pbFnScratch, cbMod, ndMod ); pbFnScratch += cbMod; cbFnScratch -= cbMod;
+
+            // First fix the tight number of digits of each prime
+            pkRsakey->nMaxDigitsOfPrimes = 0;
+            for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+            {
+#pragma warning(suppress: 26007) // "Incorrect Annotation" - cannot phrase array of pointers to arrays in SAL
+                scError = SymCryptIntSetValue( ppPrimes[i], pcbPrimes[i], numFormat, piPhi );
+                if (scError != SYMCRYPT_NO_ERROR )
+                {
+                    goto cleanup;
+                }
+
+                pkRsakey->nBitsOfPrimes[i] = SymCryptIntBitsizeOfValue(piPhi);
+                pkRsakey->nDigitsOfPrimes[i] = SymCryptDigitsFromBits(pkRsakey->nBitsOfPrimes[i]);
+
+                pkRsakey->nMaxDigitsOfPrimes = SYMCRYPT_MAX(pkRsakey->nMaxDigitsOfPrimes, pkRsakey->nDigitsOfPrimes[i]);
+
+                if (pkRsakey->nBitsOfPrimes[i] < SYMCRYPT_RSAKEY_MIN_BITSIZE_PRIME)
+                {
+                    scError = SYMCRYPT_WRONG_KEY_SIZE;
+                    goto cleanup;
+                }
+            }
+
+            // Create all the objects
+            SymCryptRsakeyCreateAllObjects(pkRsakey);
+
+            // Set the values
+            for (UINT32 i=0; i<pkRsakey->nPrimes; i++)
+            {
+#pragma warning(suppress: 26007) // "Incorrect Annotation" - cannot phrase array of pointers to arrays in SAL
+                scError = SymCryptIntSetValue( ppPrimes[i], pcbPrimes[i], numFormat, SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ) );
+                if (scError != SYMCRYPT_NO_ERROR )
+                {
+                    goto cleanup;
+                }
+
+                // Check that this prime is odd (should we check for primality?)
+                if ((SymCryptIntGetValueLsbits32(SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ))& 1)==0)
+                {
+                    scError = SYMCRYPT_INVALID_ARGUMENT;
+                    goto cleanup;
+                }
+
+                // IntToModulus requirement:
+                //      nBitsOfPrimes >= SYMCRYPT_RSAKEY_MIN_BITSIZE_PRIME --> pmPrimes[i] > 0
+                SymCryptIntToModulus(
+                        SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ),
+                        pkRsakey->pmPrimes[i],
+                        pkRsakey->nBitsOfModulus,   // Average number of operations
+                        SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
+                        pbFnScratch,
+                        cbFnScratch );
+            }
+        }
+
+        // Create remaining temporaries
+        piAcc = SymCryptIntCreate( pbFnScratch, cbLarge, 2 * ndMod ); pbFnScratch += cbLarge; cbFnScratch -= cbLarge;
+        pdTmp = SymCryptDivisorCreate( pbFnScratch, cbDivisor, ndMod ); pbFnScratch += cbDivisor; cbFnScratch -= cbDivisor;
+
+        // Calculate the rest of the fields
+        scError = SymCryptRsakeyCalculatePrivateFields( pkRsakey, pdTmp, piPhi, piAcc, pbFnScratch, cbFnScratch, flags & SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION );
+        if (scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+
+        // Everything is set here
+        pkRsakey->hasPrivateKey = TRUE;
+    }
+
+    pkRsakey->fAlgorithmInfo = flags; // We want to track all of the flags in the Rsakey
+
+    if ( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // Ensure RSA algorithm selftest is run before first use of RSA algorithm
+        SYMCRYPT_RUN_SELFTEST_ONCE(
+            SymCryptRsaSelftest,
+            SYMCRYPT_SELFTEST_ALGORITHM_RSA);
+
+        // PCT does not need to be run on import - mark it as done
+        pkRsakey->fAlgorithmInfo |= SYMCRYPT_PCT_RSA_SIGN;
+    }
+
+cleanup:
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeySetValue(
+    _In_reads_bytes_( cbModulus )   PCBYTE                  pbModulus,
+                                    SIZE_T                  cbModulus,
+    _In_reads_( nPubExp )           PCUINT64                pu64PubExp,
+                                    UINT32                  nPubExp,
+    _In_reads_opt_( nPrimes )       PCBYTE *                ppPrimes,
+    _In_reads_opt_( nPrimes )       SIZE_T *                pcbPrimes,
+                                    UINT32                  nPrimes,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                    UINT32                  flags,
+    _Inout_                         PSYMCRYPT_RSAKEY        pkRsakey )
+{
+    return SymCryptRsakeySetValueInternal(
+        pbModulus, cbModulus,
+        pu64PubExp, nPubExp,
+        NULL, 0,
+        ppPrimes, pcbPrimes, nPrimes,
+        numFormat,
+        flags,
+        pkRsakey );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeySetValueFromPrivateExponent(
+    _In_reads_bytes_( cbModulus )           PCBYTE                  pbModulus,
+                                            SIZE_T                  cbModulus,
+                                            UINT64                  u64PubExp,
+    _In_reads_bytes_( cbPrivateExponent )   PCBYTE                  pbPrivateExponent,
+                                            SIZE_T                  cbPrivateExponent,
+                                            SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                            UINT32                  flags,
+    _Inout_                                 PSYMCRYPT_RSAKEY        pkRsakey )
+{
+    return SymCryptRsakeySetValueInternal(
+        pbModulus, cbModulus,
+        &u64PubExp, 1,
+        pbPrivateExponent, cbPrivateExponent,
+        NULL, NULL, 0,
+        numFormat,
+        flags,
+        pkRsakey );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyGetValue(
+    _In_                            PCSYMCRYPT_RSAKEY       pkRsakey,
+    _Out_writes_bytes_( cbModulus ) PBYTE                   pbModulus,
+                                    SIZE_T                  cbModulus,
+    _Out_writes_opt_( nPubExp )     PUINT64                 pu64PubExp,
+                                    UINT32                  nPubExp,
+    _Out_writes_opt_( nPrimes )     PBYTE *                 ppPrimes,
+    _In_reads_opt_( nPrimes )       SIZE_T *                pcbPrimes,
+                                    UINT32                  nPrimes,
+                                    SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                    UINT32                  flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Check if private key needed but not there
+    if ((nPrimes!=0) && (pkRsakey->hasPrivateKey == FALSE))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Modulus
+    if (pbModulus!=NULL)
+    {
+        // We'll get an error if cbModulus is 0 or too small
+        scError = SymCryptIntGetValue( SymCryptIntFromModulus( pkRsakey->pmModulus ), pbModulus, cbModulus, numFormat );
+        if (scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    // Public exponents
+    if( pu64PubExp != NULL )
+    {
+        if( nPubExp != 1 )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+        pu64PubExp[0] = pkRsakey->au64PubExp[0];
+    }
+
+    // Primes i.e. private key
+    if( nPrimes != 0 )
+    {
+        if( nPrimes != 2 || ppPrimes == NULL || pcbPrimes == NULL )
+        {
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            goto cleanup;
+        }
+
+        for (UINT32 i=0; i<nPrimes; i++)
+        {
+            if (ppPrimes[i]!=NULL)
+            {
+                scError = SymCryptIntGetValue( SymCryptIntFromModulus( pkRsakey->pmPrimes[i] ), ppPrimes[i], pcbPrimes[i], numFormat );
+                if (scError != SYMCRYPT_NO_ERROR )
+                {
+                    goto cleanup;
+                }
+            }
+        }
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyGetCrtValue(
+    _In_                                        PCSYMCRYPT_RSAKEY       pkRsakey,
+    _Out_writes_opt_(nCrtExponents)             PBYTE *                 ppCrtExponents,
+    _In_reads_(nCrtExponents)                   SIZE_T *                pcbCrtExponents,
+                                                UINT32                  nCrtExponents,
+    _Out_writes_bytes_opt_(cbCrtCoefficient)    PBYTE                   pbCrtCoefficient,
+                                                SIZE_T                  cbCrtCoefficient,
+    _Out_writes_bytes_opt_(cbPrivateExponent)   PBYTE                   pbPrivateExponent,
+                                                SIZE_T                  cbPrivateExponent,
+                                                SYMCRYPT_NUMBER_FORMAT  numFormat,
+                                                UINT32                  flags)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+
+    UNREFERENCED_PARAMETER( flags );
+
+    // Check if the arguments are correct
+    if ( (ppCrtExponents==NULL) && (nCrtExponents!=0) ||
+         (nCrtExponents != 0 && nCrtExponents != 2 ))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Crt value can only be available we have private key.
+    if (pkRsakey->hasPrivateKey == FALSE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Crt exponents
+    for (UINT32 i=0; i<nCrtExponents; i++)
+    {
+        if (ppCrtExponents[i]!=NULL)
+        {
+            scError = SymCryptIntGetValue( pkRsakey->piCrtPrivExps[i], ppCrtExponents[i], pcbCrtExponents[i], numFormat );
+            if (scError != SYMCRYPT_NO_ERROR )
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+    if (pbCrtCoefficient!=NULL)
+    {
+        cbScratch = SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pkRsakey->nDigitsOfModulus );
+        pbScratch = SymCryptCallbackAlloc( cbScratch );
+
+        if (pbScratch==NULL)
+        {
+            scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+            goto cleanup;
+        }
+
+        scError = SymCryptModElementGetValue(
+                      pkRsakey->pmPrimes[0],
+                      pkRsakey->peCrtInverses[0],
+                      pbCrtCoefficient,
+                      cbCrtCoefficient,
+                      numFormat,
+                      pbScratch,
+                      cbScratch);
+        if (scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+    if (pbPrivateExponent!=NULL)
+    {
+        scError = SymCryptIntGetValue( pkRsakey->piPrivExps[0], pbPrivateExponent, cbPrivateExponent, numFormat );
+        if (scError != SYMCRYPT_NO_ERROR )
+        {
+            goto cleanup;
+        }
+    }
+
+cleanup:
+
+    if (pbScratch!=NULL)
+    {
+        SymCryptWipe(pbScratch,cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsakeyExtendKeyUsage(
+    _Inout_ PSYMCRYPT_RSAKEY    pkRsakey,
+            UINT32              flags )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    // Ensure caller has specified what algorithm(s) the key will be used with
+    UINT32 algorithmFlags = SYMCRYPT_FLAG_RSAKEY_SIGN | SYMCRYPT_FLAG_RSAKEY_ENCRYPT;
+
+    if ( ( ( flags & ~algorithmFlags ) != 0 ) ||
+         ( ( flags & algorithmFlags ) == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pkRsakey->fAlgorithmInfo |= flags;
+
+cleanup:
+    return scError;
+}
diff --git a/libs/symcrypt/lib/sc_lib.h b/libs/symcrypt/lib/sc_lib.h
new file mode 100644
index 000000000000..9d0537b57711
--- /dev/null
+++ b/libs/symcrypt/lib/sc_lib.h
@@ -0,0 +1,5134 @@
+//
+// sc_lib.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// Internal definitions for the symcrypt library.
+// This include file is used only for the files inside the library, not by
+// the code that calls the library.
+//
+
+
+#if SYMCRYPT_MS_VC
+#define SYMCRYPT_DISABLE_CFG    __declspec(guard(nocf))
+#else
+#define SYMCRYPT_DISABLE_CFG
+#endif
+
+//
+// Global flags
+//
+
+#define SYMCRYPT_FLAG_LIB_INITIALIZED   0x00000001
+
+extern UINT32 g_SymCryptFlags;
+
+//==============================================================================================
+//  Common environment functions
+//==============================================================================================
+
+VOID
+SYMCRYPT_CALL
+SymCryptInitEnvCommon( UINT32 version );
+
+_Analysis_noreturn_
+VOID
+SYMCRYPT_CALL
+SymCryptFatalHang( UINT32 fatalcode );
+
+#include <symcrypt_low_level.h>
+
+// Types
+
+typedef int                 BOOL;
+
+#if !defined(TRUE)
+#define TRUE  (1)
+#endif
+
+#if !defined(FALSE)
+#define FALSE (0)
+#endif
+
+#if !defined(UNREFERENCED_PARAMETER)
+#define UNREFERENCED_PARAMETER(x)   ((void)x)
+#endif
+
+#if !defined(FAST_FAIL_CRYPTO_LIBRARY)
+#define FAST_FAIL_CRYPTO_LIBRARY    22
+#endif
+
+//
+// We want to write some of our code to use the native register size provided by the platform we are using to enable
+// generic code to compile into reasonable performant versions on 32b and 64b platforms. Below definitions give us
+// this flexibility without relying on compiler specifics.
+//
+// WARNING: Some use of NATIVE_UINT also relies on the little-endianness of the 64b platform; our generic code normally
+// uses UINT32, and at the time of writing mixing UINT32 and NATIVE_UINT will not work on a big-endian 64b platform!
+//
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+typedef INT64               NATIVE_INT;
+typedef UINT64              NATIVE_UINT;
+#define NATIVE_BITS         (64)
+#define NATIVE_BYTES        (8)
+#define NATIVE_BYTES_LOG2   (3)
+#else
+typedef INT32               NATIVE_INT;
+typedef UINT32              NATIVE_UINT;
+#define NATIVE_BITS         (32)
+#define NATIVE_BYTES        (4)
+#define NATIVE_BYTES_LOG2   (2)
+#endif
+
+
+//
+// Our Wipe code uses FORCE_WRITE* which are implemented using
+// WriteNoFence* functions. Unfortunately, they declare their parameter
+// to be interlocked, and the compiler complains when we also access the variable
+// using non-interlocked code.
+// This warning is nonsensical in our situation, so we disable it.
+// The second warning is about accessing a local variable via an interlocked ptr.
+//
+#pragma prefast( disable:28112 )
+#pragma prefast( disable:28113 )
+#pragma warning( disable: 4702 )        // unreachable code. The compilers are not equally smart, and some complain
+                                        // about 'function must return a value' and some about 'unreachable code'
+#pragma warning( disable: 4296 )        // expression is always false - this warning is forced to be an error by a
+                                        // pragma in the SDK warning.h, but we don't consider it useful
+
+
+//
+// These macros allow a bunch of generic code to be written.
+// For example, the Hash append function is written once generically
+// using these macros.
+//
+
+#define CONCAT_I2( a, b )       a##b
+#define CONCAT_I3( a, b, c )    a##b##c
+
+
+#define CONCAT2( a, b )         CONCAT_I2( a, b )
+#define CONCAT3( a, b, c )      CONCAT_I3( a, b, c )
+//#define CONCAT4( a, b, c, d)    a##b##c##d
+
+
+
+#define SYMCRYPT_XXX_STATE              CONCAT3( SYMCRYPT_, ALG, _STATE )
+#define PSYMCRYPT_XXX_STATE             CONCAT3( PSYMCRYPT_, ALG, _STATE )
+#define PCSYMCRYPT_XXX_STATE            CONCAT3( PCSYMCRYPT_, ALG, _STATE )
+
+#define SYMCRYPT_Xxx                    CONCAT2( SymCrypt, Alg )
+
+#define SYMCRYPT_XxxStateCopy           CONCAT3( SymCrypt, Alg, StateCopy )
+#define SYMCRYPT_XxxInit                CONCAT3( SymCrypt, Alg, Init )
+#define SYMCRYPT_XxxAppend              CONCAT3( SymCrypt, Alg, Append )
+#define SYMCRYPT_XxxResult              CONCAT3( SymCrypt, Alg, Result )
+#define SYMCRYPT_XxxAppendBlocks        CONCAT3( SymCrypt, Alg, AppendBlocks )
+#define SYMCRYPT_XxxStateImport         CONCAT3( SymCrypt, Alg, StateImport)
+#define SYMCRYPT_XxxStateExport         CONCAT3( SymCrypt, Alg, StateExport)
+
+// for XOFs and KMAC
+#define SYMCRYPT_XXX_EXPANDED_KEY       CONCAT3( SYMCRYPT_, ALG, _EXPANDED_KEY )
+#define PSYMCRYPT_XXX_EXPANDED_KEY      CONCAT3( PSYMCRYPT_, ALG, _EXPANDED_KEY )
+#define PCSYMCRYPT_XXX_EXPANDED_KEY     CONCAT3( PCSYMCRYPT_, ALG, _EXPANDED_KEY )
+#define SYMCRYPT_XxxEx                  CONCAT3( SymCrypt, Alg, Ex)
+#define SYMCRYPT_XxxDefault             CONCAT3( SymCrypt, Alg, Default )
+#define SYMCRYPT_XxxExpandKey           CONCAT3( SymCrypt, Alg, ExpandKey )
+#define SYMCRYPT_XxxExpandKeyEx         CONCAT3( SymCrypt, Alg, ExpandKeyEx )
+#define SYMCRYPT_XxxExtract             CONCAT3( SymCrypt, Alg, Extract )
+#define SYMCRYPT_XxxResultEx            CONCAT3( SymCrypt, Alg, ResultEx )
+#define SYMCRYPT_XxxKeyCopy             CONCAT3( SymCrypt, Alg, KeyCopy )
+
+#define SYMCRYPT_HmacXxx                CONCAT2( SymCryptHmac, Alg )
+#define SYMCRYPT_HmacXxxStateCopy       CONCAT3( SymCryptHmac, Alg, StateCopy )
+#define SYMCRYPT_HmacXxxKeyCopy         CONCAT3( SymCryptHmac, Alg, KeyCopy )
+#define SYMCRYPT_HmacXxxExpandKey       CONCAT3( SymCryptHmac, Alg, ExpandKey )
+#define SYMCRYPT_HmacXxxInit            CONCAT3( SymCryptHmac, Alg, Init )
+#define SYMCRYPT_HmacXxxAppend          CONCAT3( SymCryptHmac, Alg, Append )
+#define SYMCRYPT_HmacXxxResult          CONCAT3( SymCryptHmac, Alg, Result )
+
+
+#define SYMCRYPT_XXX_INPUT_BLOCK_SIZE   CONCAT3( SYMCRYPT_, ALG, _INPUT_BLOCK_SIZE )
+#define SYMCRYPT_XXX_RESULT_SIZE        CONCAT3( SYMCRYPT_, ALG, _RESULT_SIZE )
+
+#define SYMCRYPT_HMAC_XXX_INPUT_BLOCK_SIZE  SYMCRYPT_XXX_INPUT_BLOCK_SIZE
+#define SYMCRYPT_HMAC_XXX_RESULT_SIZE       SYMCRYPT_XXX_RESULT_SIZE
+
+#define PSYMCRYPT_HMAC_XXX_EXPANDED_KEY     CONCAT3( PSYMCRYPT_HMAC_, ALG, _EXPANDED_KEY )
+#define PCSYMCRYPT_HMAC_XXX_EXPANDED_KEY    CONCAT3( PCSYMCRYPT_HMAC_, ALG, _EXPANDED_KEY )
+#define SYMCRYPT_HMAC_XXX_STATE             CONCAT3( SYMCRYPT_HMAC_, ALG, _STATE )
+#define PSYMCRYPT_HMAC_XXX_STATE            CONCAT3( PSYMCRYPT_HMAC_, ALG, _STATE )
+#define PCSYMCRYPT_HMAC_XXX_STATE            CONCAT3( PCSYMCRYPT_HMAC_, ALG, _STATE )
+
+
+//==============================================================================================
+//  PLATFORM SPECIFICS
+//==============================================================================================
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+//
+// The XMM save/restore functions need to be passed a buffer in which they can store their data.
+// We have two different places where we use this, in kernel mode and in user mode (while testing)
+// We can't declare a union of the two structs as we can't include the kernel-mode headers in this file
+// when compiled for a user-mode app.
+// Instead we define a structure with reserved space, and have each environment check the size and
+// cast the pointer.
+//
+// We always use the KeSaveExtendedProcessorState call, and not the KeSaveFloatingPointState as it
+// allows us to save only the XMM registers and not touch the X87/MMX registers which should
+// save time.
+//
+#if SYMCRYPT_CPU_X86
+
+//
+// The XSTATE_SAVE structure consists of a union between
+//  struct:
+//      - INT64             8
+//      - INT32             4
+//      - Pointer           4
+//      - Pointer           4
+//      - Pointer           4
+//      - Pointer           4
+//      - BYTE              1 + 3 padding
+//                          32 total
+// - XSTATE_CONTEXT
+//      - UINT64            8
+//      - UINT32            4
+//      - UINT32            4
+//      - Pointer + UINT32  8
+//      - Pointer + UINT32  8
+//                          32 total
+//
+// Experimentally: need 4 more bytes, don't know why yet.
+// Should have a look with the debugger when I have time.
+//
+
+#define SYMCRYPT_XSTATE_SAVE_SIZE    (32)
+
+#elif SYMCRYPT_CPU_AMD64
+
+//
+// The XSTATE_SAVE structure consists of
+// - pointer            8
+// - pointer            8
+// - BYTE               1 + 7 padding
+// - XSTATE_CONTEXT
+//      - UINT64        8
+//      - UINT32        4
+//      - UINT32        4
+//      - Pointer       8
+//      - Pointer       8
+//
+#define SYMCRYPT_XSTATE_SAVE_SIZE    (56)
+
+#endif
+
+typedef
+SYMCRYPT_ALIGN
+struct _SYMCRYPT_EXTENDED_SAVE_DATA {
+    SYMCRYPT_ALIGN  BYTE    data[SYMCRYPT_XSTATE_SAVE_SIZE];
+                    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_EXTENDED_SAVE_DATA, *PSYMCRYPT_EXTENDED_SAVE_DATA;
+
+
+//
+// Two functions to save/restore the XMM registers.
+// These must ALWAYS be called in pairs, even if the SaveXmm function returned an error.
+// XMM registers cannot be used if the save function returned an error.
+// If the SYMCRYPT_CPU_FEATURE_SAVEXMM_NOFAIL feature is present, then the
+// SymCryptSaveXmm function will never return an error.
+//
+
+//
+// Functions to save/restore the XMM or YMM registers.
+// If the Save*mm function is called and succeeds, then the corresponding
+// Restore*mm function MUST be called later on the same thread.
+// The extended registers cannot be called if the Save function returns an error.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSaveXmm( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptRestoreXmm( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveData );
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSaveYmm( _Out_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptRestoreYmm( _Inout_ PSYMCRYPT_EXTENDED_SAVE_DATA pSaveData );
+#endif
+
+
+//==============================================================================================
+//  Library declarations
+//==============================================================================================
+
+//
+// Function to check that the library has been initialized
+//
+#if SYMCRYPT_DEBUG
+
+VOID
+SYMCRYPT_CALL
+SymCryptLibraryWasNotInitialized(void);
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCheckLibraryInitialized(void)
+{
+    if( !(g_SymCryptFlags & SYMCRYPT_FLAG_LIB_INITIALIZED)  )
+    {
+        SymCryptLibraryWasNotInitialized();
+    }
+}
+#else
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptCheckLibraryInitialized(void)
+{
+}
+#endif
+
+#define HMAC_IPAD_BYTE   0x36
+#define HMAC_OPAD_BYTE   0x5c
+
+// SYMCRYPT_CPU_FEATURES
+#define SYMCRYPT_CPU_FEATURES_FOR_PCLMULQDQ_CODE  (SYMCRYPT_CPU_FEATURE_PCLMULQDQ | SYMCRYPT_CPU_FEATURE_SSSE3 | SYMCRYPT_CPU_FEATURE_SAVEXMM_NOFAIL )
+
+#define SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE (SYMCRYPT_CPU_FEATURE_SSSE3 | SYMCRYPT_CPU_FEATURE_AESNI)
+#define SYMCRYPT_CPU_FEATURES_FOR_AESNI_PCLMULQDQ_CODE (SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE | SYMCRYPT_CPU_FEATURES_FOR_PCLMULQDQ_CODE)
+#define SYMCRYPT_CPU_FEATURES_FOR_VAES_256_CODE (SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE | SYMCRYPT_CPU_FEATURE_AVX2 | SYMCRYPT_CPU_FEATURE_VAES)
+#define SYMCRYPT_CPU_FEATURES_FOR_VAES_512_CODE (SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE | SYMCRYPT_CPU_FEATURE_AVX512 | SYMCRYPT_CPU_FEATURE_VAES)
+
+#define SYMCRYPT_CPU_FEATURES_FOR_SHANI_CODE (SYMCRYPT_CPU_FEATURE_SSSE3 | SYMCRYPT_CPU_FEATURE_SHANI)
+
+#define SYMCRYPT_CPU_FEATURES_FOR_MULX (SYMCRYPT_CPU_FEATURE_BMI2 | SYMCRYPT_CPU_FEATURE_ADX | SYMCRYPT_CPU_FEATURE_SSE2 )
+
+//
+// ROTATE OPERATIONS
+//
+//
+// If this lib is ever ported to a platform that doesn't have the _rotx functions
+// the macros can be replaced by portable definitions just like the ROL16/ROR16
+//
+
+#define ROL16( x, n ) ((UINT16)( ( ((x) << (n)) | ((x) >> (16-(n))) ) ))
+#define ROR16( x, n ) ((UINT16)( ( ((x) >> (n)) | ((x) << (16-(n))) ) ))
+
+#if SYMCRYPT_MS_VC
+    #define ROL32( x, n ) _rotl( (x), (n) )
+    #define ROR32( x, n ) _rotr( (x), (n) )
+    #define ROL64( x, n ) _rotl64( (x), (n) )
+    #define ROR64( x, n ) _rotr64( (x), (n) )
+#elif SYMCRYPT_GNUC
+    #define ROL32( x, n ) ((UINT32)( ( ((x) << (n)) | ((x) >> (32-(n))) ) ))
+    #define ROR32( x, n ) ((UINT32)( ( ((x) >> (n)) | ((x) << (32-(n))) ) ))
+    #define ROL64( x, n ) ((UINT64)( ( ((x) << (n)) | ((x) >> (64-(n))) ) ))
+    #define ROR64( x, n ) ((UINT64)( ( ((x) >> (n)) | ((x) << (64-(n))) ) ))
+#else
+    #error Unknown compiler
+#endif
+
+
+#define SYMCRYPT_ARRAY_SIZE(_x)     (sizeof(_x)/sizeof(_x[0]))
+
+enum{
+    STATE_NEXT = 0,         // starting state = 0, set by structure wipe.
+    STATE_DATA_START,
+    STATE_DATA_END,
+    STATE_RESULT2,          // 2nd phase of result computation (1st phase is at STATE_NEXT when the result operation is found)
+    STATE_RESULT_DONE,      // 3rd phase of result computation
+};
+
+
+
+//==========================================================================
+// Inline implementations ...
+//==========================================================================
+
+//
+// These are a bunch of functions to convert between an array of
+// 32 or 64-bit integers to an array of bytes in LSBfirst or MSBfirst convention.
+// Not all variations have been implemented yet. We add them as they are
+// needed.
+//
+
+//
+// These implementations are optimized for inlining, especially when the
+// size of the data to be converted is a compile-time constant.
+//
+
+//
+// SymCryptUint32ToMsbFirst & SymCryptMsbFirstToUint32.
+// This is used by the SHA family
+//
+#if SYMCRYPT_CPU_AMD64
+
+//
+// On AMD64 we can do 2 UINT32s at once by doing a ROL(x,32) and a BSWAP.
+//
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint32ToMsbFirst( _In_reads_(cuData)     PCUINT32 puData,
+                          _Out_writes_(4*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData )
+{
+    while( cuData >= 2 )
+    {
+        SYMCRYPT_STORE_MSBFIRST64( pbResult, ROL64( *(UINT64*)puData, 32 ));
+        pbResult += 8;
+        puData += 2;
+        cuData -= 2;
+    }
+
+    if( cuData != 0 )
+    {
+        SYMCRYPT_STORE_MSBFIRST32( pbResult, *puData );
+    }
+}
+
+#else // not _AMD64_
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint32ToMsbFirst( _In_reads_(cuData)     PCUINT32 puData,
+                          _Out_writes_(4*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData )
+{
+    while( cuData != 0 )
+    {
+        SYMCRYPT_STORE_MSBFIRST32( pbResult, *puData );
+        puData++;
+        pbResult += 4;
+        cuData--;
+    }
+}
+#endif // platform switch for SymCryptUint32ToMsbFirst
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMsbFirstToUint32( _In_reads_(4*cuResult) PCBYTE  pbData,
+                          _Out_writes_(cuResult) PUINT32 puResult,
+                                                 SIZE_T  cuResult )
+{
+    while( cuResult != 0 )
+    {
+        *puResult = SYMCRYPT_LOAD_MSBFIRST32( pbData );
+        puResult++;
+        pbData += 4;
+        cuResult--;
+    }
+}
+
+
+//
+// SymCryptUint32ToLsbFirst & SymCryptLsbFirstToUint32
+// These are used by the MD4 and MD5 hash functions
+//
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+
+//
+// On AMD64, X86, and ARM this is just a memcpy
+//
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint32ToLsbFirst( _In_reads_(cuData)     PCUINT32 puData,
+                          _Out_writes_(4*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData )
+
+{
+    memcpy( pbResult, puData, 4*cuData );
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptLsbFirstToUint32( _In_reads_(4*cuResult) PCBYTE  pbData,
+                          _Out_writes_(cuResult) PUINT32 puResult,
+                                                 SIZE_T  cuResult )
+{
+    memcpy( puResult, pbData, 4*cuResult );
+}
+
+#else // not (AMD64_ or X86_ or ARM or ARM64)
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint32ToLsbFirst( _In_reads_(cuData)     PCUINT32 puData,
+                          _Out_writes_(4*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData )
+{
+    while( cuData != 0 )
+    {
+        SYMCRYPT_STORE_LSBFIRST32( pbResult, *puData );
+        puData++;
+        pbResult += 4;
+        cuData--;
+    }
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptLsbFirstToUint32( _In_reads_(4*cuResult) PCBYTE  pbData,
+                          _Out_writes_(cuResult) PUINT32 puResult,
+                                                 SIZE_T  cuResult )
+{
+    while( cuResult != 0 )
+    {
+        *puResult = SYMCRYPT_LOAD_LSBFIRST32( pbData );
+        pbData += 4;
+        puResult++;
+        cuResult--;
+    }
+}
+
+#endif // Platform switch for SymCryptUint32ToLsbFirst
+
+
+//
+// SymCryptUint64ToLsbFirst & SymCryptLsbFirstToUint64
+// These are used by Keccak.
+//
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+
+//
+// On AMD64, X86, and ARM this is just a memcpy
+//
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint64ToLsbFirst( _In_reads_(cuData)     PCUINT64 puData,
+                          _Out_writes_(8*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData )
+
+{
+    memcpy( pbResult, puData, 8*cuData );
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptLsbFirstToUint64( _In_reads_(8*cuResult) PCBYTE  pbData,
+                          _Out_writes_(cuResult) PUINT64 puResult,
+                                                 SIZE_T  cuResult )
+{
+    memcpy( puResult, pbData, 8*cuResult );
+}
+
+#else // not (AMD64_ or X86_ or ARM or ARM64)
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint64ToLsbFirst( _In_reads_(cuData)     PCUINT64 puData,
+                          _Out_writes_(8*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData )
+{
+    while( cuData != 0 )
+    {
+        SYMCRYPT_STORE_LSBFIRST64( pbResult, *puData );
+        puData++;
+        pbResult += 8;
+        cuData--;
+    }
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptLsbFirstToUint64( _In_reads_(8*cuResult) PCBYTE  pbData,
+                          _Out_writes_(cuResult) PUINT64 puResult,
+                                                 SIZE_T  cuResult )
+{
+    while( cuResult != 0 )
+    {
+        *puResult = SYMCRYPT_LOAD_LSBFIRST64( pbData );
+        pbData += 8;
+        puResult++;
+        cuResult--;
+    }
+}
+
+#endif // Platform switch for SymCryptUint64ToLsbFirst & SymCryptLsbFirstToUint64
+
+
+//
+// SymCryptUint64ToMsbFirst & SymCryptMsbFirstToUint64
+//
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptUint64ToMsbFirst( _In_reads_(cuData)     PCUINT64    puData,
+                          _Out_writes_(8*cuData) PBYTE       pbResult,
+                                                 SIZE_T      cuData )
+{
+    while( cuData != 0 )
+    {
+        SYMCRYPT_STORE_MSBFIRST64( pbResult, *puData );
+        pbResult += 8;
+        puData ++;
+        cuData --;
+    }
+}
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMsbFirstToUint64( _In_reads_(8*cuResult) PCBYTE      pbData,
+                          _Out_writes_(cuResult) PUINT64  puResult,
+                                                 SIZE_T      cuResult )
+{
+    while( cuResult != 0 )
+    {
+        *puResult = SYMCRYPT_LOAD_MSBFIRST64( pbData );
+        puResult++;
+        pbData += 8;
+        cuResult--;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+//  Internal function prototypes
+//
+
+//
+// SymCryptSha1AppendBlocks
+//
+// Updates the chaining state of the hash function with one or more blocks of data.
+// Each block is 64 bytes long, the natural size of a SHA256 input block.
+//
+// cbData must be a multiple of 64.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha1AppendBlocks(
+    _Inout_                 SYMCRYPT_SHA1_CHAINING_STATE  * pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining );
+
+//
+// SymCryptSha256AppendBlocks
+//
+// Updates the chaining state of the hash function with one or more blocks of data.
+// Each block is 64 bytes long, the natural size of a SHA256 input block.
+//
+// cbData must be a multiple of 64.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE    * pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Intrinsics implementation processing 4 message blocks in parallel using XMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_xmm_4blocks(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE    * pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Assembly implementation processing 4 message blocks in parallel using XMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_xmm_ssse3_asm(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE    * pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Intrinsics implementation processing 8 message blocks in parallel using YMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_ymm_8blocks(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE    * pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Assembly implementation processing 8 message blocks in parallel using YMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_ymm_avx2_asm(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE    * pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+
+//
+// SymCryptSha512AppendBlocks
+//
+// Updates the chaining state of the hash function with one or more blocks of data.
+// Each block is 128 bytes long, the natural size of a SHA512 input block.
+//
+// cbData must be a multiple of 128.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE    * pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_xmm(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Intrinsics implementation using YMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ymm_1block(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Intrinsics implementation processing 2 message blocks in parallel using YMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ymm_2blocks(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Intrinsics implementation processing 4 message blocks in parallel using YMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ymm_4blocks(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Assembly implementation processing 4 message blocks in parallel using YMM registers
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ymm_avx2_asm(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+// Assembly implementation processing 4 message blocks in parallel using YMM registers with AVX512 instruction set
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ymm_avx512vl_asm(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining );
+
+
+
+
+//
+// SymCryptMd5AppendBlocks
+//
+// Updates the chaining state of the hash function with one or more blocks of data.
+// Each block is 64 bytes long, the natural size of a MD5 input block.
+//
+// cbData must be a multiple of 64.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd5AppendBlocks(
+    _Inout_                 SYMCRYPT_MD5_CHAINING_STATE   * pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining );
+
+
+//
+// SymCryptMd4AppendBlocks
+//
+// Updates the chaining state of the hash function with one or more blocks of data.
+// Each block is 64 bytes long, the natural size of a MD5 input block.
+//
+// cbData must be a multiple of 64.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd4AppendBlocks(
+    _Inout_                 SYMCRYPT_MD4_CHAINING_STATE   * pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining );
+
+
+//
+// SymCryptMd2AppendBlock
+//
+// Update the C and X state based on the message block in the buffer.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMd2AppendBlocks(
+    _Inout_                 SYMCRYPT_MD2_CHAINING_STATE   * pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining );
+
+
+//
+// SymCryptUint32ToMsbFirst
+//
+// Convert an array of UINT32s to 4-byte values stored MSB first (big-endian) conversion.
+// Note that the count is the number of UINT32s to convert, not the number
+// of bytes. This is somewhat unusual, but it avoids any confusion about
+// converting an odd number of bytes.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptUint32ToMsbFirst( _In_reads_(cuData)     PCUINT32 puData,
+                          _Out_writes_(4*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData );
+
+//
+// SymCryptUint32ToLsbFirst
+//
+// Convert an array of UINT32s to 4-byte values stored LSB first (little-endian) conversion.
+// Note that the count is the number of UINT32s to convert, not the number
+// of bytes. This is somewhat unusual, but it avoids any confusion about
+// converting an odd number of bytes.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptUint32ToLsbFirst( _In_reads_(cuData)     PCUINT32 puData,
+                          _Out_writes_(4*cuData) PBYTE    pbResult,
+                                                 SIZE_T   cuData );
+
+//
+// SymCryptMsbFirstToUint32
+//
+// Convert an array of 4-byte values stored MSB first to an array of UINT32s
+// (big-endian) conversion.
+// Note that the count is the number of UINT32s to convert, not the number
+// of bytes. This is somewhat unusual, but it avoids any confusion about
+// converting an odd number of bytes.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMsbFirstToUint32( _In_reads_(4*cuResult) PCBYTE   pbData,
+                          _Out_writes_(cuResult) PUINT32  puResult,
+                                                 SIZE_T   cuResult );
+
+//
+// SymCryptLsbFirstToUint32
+//
+// Convert an array of 4-byte values stored LSB first to an array of UINT32s
+// (little-endian) conversion.
+// Note that the count is the number of UINT32s to convert, not the number
+// of bytes. This is somewhat unusual, but it avoids any confusion about
+// converting an odd number of bytes.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptLsbFirstToUint32( _In_reads_(4*cuResult) PCBYTE  pbData,
+                          _Out_writes_(cuResult) PUINT32 puResult,
+                                                 SIZE_T  cuResult );
+
+//
+// SymCryptUint64ToMsbFirst
+//
+// Convert an array of UINT64s to an array of bytes using the MSB first
+// (big-endian) conversion.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptUint64ToMsbFirst( _In_reads_(cuData)     PCUINT64    puData,
+                          _Out_writes_(8*cuData) PBYTE       pbResult,
+                                                 SIZE_T      cuData );
+
+//
+// SymCryptMsbFirstToUint64
+//
+// Convert an array of 4-byte values stored MSB first to an array of UINT64s
+// (big-endian) conversion.
+// Note that the count is the number of UINT64s to convert, not the number
+// of bytes. This is somewhat unusual, but it avoids any confusion about
+// converting an odd number of bytes.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptMsbFirstToUint64( _In_reads_(8*cuResult) PCBYTE      pbData,
+                          _Out_writes_(cuResult) PUINT64      puResult,
+                                                 SIZE_T      cuResult );
+
+
+
+//============================================================================
+// HMAC macros and inline functions.
+//
+#define REPEAT_BYTE_TO_UINT32( x ) (((UINT32)x << 24) | ((UINT32)x << 16) | ((UINT32)x << 8) | x)
+#define REPEAT_BYTE_TO_UINT64( x ) ( ((UINT64)REPEAT_BYTE_TO_UINT32(x) << 32) | REPEAT_BYTE_TO_UINT32(x) )
+
+//
+// The XorByteIntoBuffer function is a platform-optimized function to xor a byte
+// repeatedly into a buffer.
+// Note that the buffer length must be a multiple of 8.
+//
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+XorByteIntoBuffer( _Inout_updates_( 8*cqBuf ) PBYTE pbBuf, SIZE_T cqBuf, BYTE v )
+{
+    SIZE_T i;
+    const UINT64 v64 = REPEAT_BYTE_TO_UINT64( v );
+
+    for( i=0; i<cqBuf; i++ )
+    {
+        ((UINT64 *)pbBuf)[i] ^= v64;
+    }
+}
+#else
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+XorByteIntoBuffer( _Inout_updates_( 8*cqBuf ) PBYTE pbBuf, SIZE_T cqBuf, BYTE v )
+{
+    SIZE_T i;
+
+    for( i=0; i<8*cqBuf; i++ )
+    {
+        pbBuf[i] ^= v;
+    }
+}
+#endif
+
+//
+// GHASH
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKey(
+    _Out_                                       PSYMCRYPT_GHASH_EXPANDED_KEY    expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )     PCBYTE                          pH );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyC(
+    _Out_writes_( SYMCRYPT_GF128_FIELD_SIZE )   PSYMCRYPT_GF128_ELEMENT expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )     PCBYTE                  pH );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyX86(
+    _Out_                                   PSYMCRYPT_GHASH_EXPANDED_KEY    expandedKey,
+   _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )  PCBYTE                          pH );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashExpandKeyAmd64(
+    _Out_writes_( SYMCRYPT_GF128_FIELD_SIZE )   PSYMCRYPT_GF128_ELEMENT expandedKey,
+    _In_reads_( SYMCRYPT_GF128_BLOCK_SIZE )     PCBYTE                  pH );
+
+//
+// For all GHashAppendData functions, data will be appended in multiples of SYMCRYPT_GF128_BLOCK_SIZE.
+// If the data is not a multiple of SYMCRYPT_GF128_BLOCK_SIZE, any remaining data will be ignored.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendData(
+    _In_                    PCSYMCRYPT_GHASH_EXPANDED_KEY   expandedKey,
+    _Inout_                 PSYMCRYPT_GF128_ELEMENT         pState,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataC(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE )     PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                     PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataXmm(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbData,
+                                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataNeon(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE )     PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                     PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashAppendDataPclmulqdq(
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbData,
+                                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGHashResult(
+    _In_                                        PCSYMCRYPT_GF128_ELEMENT    pState,
+    _Out_writes_( SYMCRYPT_GF128_BLOCK_SIZE )   PBYTE                       pbResult );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMarvin32AppendBlocks(
+    _Inout_                 PSYMCRYPT_MARVIN32_CHAINING_STATE   pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData );
+
+
+
+
+extern const BYTE SymCryptTestMsg3[3];
+extern const BYTE SymCryptTestMsg16[16];
+extern const BYTE SymCryptTestKey32[32];
+
+VOID
+SYMCRYPT_CALL
+SymCryptInjectError( PBYTE pbData, SIZE_T cbData );
+
+
+#define SYMCRYPT_CPUID_DETECT_FLAG_CHECK_OS_SUPPORT_FOR_YMM  1      // enable checking of OSXSAVE bit & XGETBV logic
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesByCpuid( UINT32 flags );
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromRegisters(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromRegistersNoTry(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptDetectCpuFeaturesFromIsProcessorFeaturePresent(void);
+
+VOID
+SYMCRYPT_CALL
+SymCryptCpuidExFunc( int cpuInfo[4], int function_id, int subfunction_id );
+
+////////////////////////////////////////////////////////////////////////////
+// Export blob formats
+////////////////////////////////////////////////////////////////////////
+
+//==========================================================
+// BLOBS
+//
+// SYMCRYPT_BLOB_HEADER
+// Generic header for all exported blobs from SymCrypt
+//
+
+typedef enum _SYMCRYPT_BLOB_TYPE {
+    SymCryptBlobTypeUnknown     = 0,
+    SymCryptBlobTypeHashState   = 0x100,
+    SymCryptBlobTypeMd2State    = SymCryptBlobTypeHashState + 1,       // explicit constants as these have to remain the same forever.
+    SymCryptBlobTypeMd4State    = SymCryptBlobTypeHashState + 2,
+    SymCryptBlobTypeMd5State    = SymCryptBlobTypeHashState + 3,
+    SymCryptBlobTypeSha1State   = SymCryptBlobTypeHashState + 4,
+    SymCryptBlobTypeSha256State = SymCryptBlobTypeHashState + 5,
+    SymCryptBlobTypeSha384State = SymCryptBlobTypeHashState + 6,
+    SymCryptBlobTypeSha512State = SymCryptBlobTypeHashState + 7,
+    SymCryptBlobTypeSha3_256State = SymCryptBlobTypeHashState + 8,
+    SymCryptBlobTypeSha3_384State = SymCryptBlobTypeHashState + 9,
+    SymCryptBlobTypeSha3_512State = SymCryptBlobTypeHashState + 10,
+    SymCryptBlobTypeSha224State = SymCryptBlobTypeHashState + 11,
+    SymCryptBlobTypeSha512_224State = SymCryptBlobTypeHashState + 12,
+    SymCryptBlobTypeSha512_256State = SymCryptBlobTypeHashState + 13,
+    SymCryptBlobTypeSha3_224State = SymCryptBlobTypeHashState + 14,
+} SYMCRYPT_BLOB_TYPE;
+
+#define SYMCRYPT_BLOB_MAGIC ('cmys')
+
+//
+// We define all export structures with pack=1 so that there are no padding bytes.
+//
+#pragma pack(push, 1)
+
+typedef struct _SYMCRYPT_BLOB_HEADER {
+    UINT32              magic;              // 'cmys'
+    UINT32              size;               // total size of blob
+    UINT32              type;               // SYMCRYPT_BLOB_TYPE: type of blob
+} SYMCRYPT_BLOB_HEADER, *PSYMCRYPT_BLOB_HEADER;
+
+typedef struct _SYMCRYPT_BLOB_TRAILER {
+    BYTE                checksum[8];        // contains the Marvin32 checksum of the rest of the blob
+} SYMCRYPT_BLOB_TRAILER, *PSYMCRYPT_BLOB_TRAILER;
+
+typedef struct _SYMCRYPT_MD2_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    C[16];
+    BYTE                    X[16];
+    UINT32                  bytesInBuffer;
+    BYTE                    buffer[16];
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_MD2_STATE_EXPORT_BLOB;
+
+C_ASSERT( sizeof( SYMCRYPT_MD2_STATE_EXPORT_BLOB ) == SYMCRYPT_MD2_STATE_EXPORT_SIZE );
+
+
+typedef struct _SYMCRYPT_MD4_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    chain[16];          // In the same format used for the final hash value of MD4
+    UINT64                  dataLength;
+    BYTE                    buffer[64];
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_MD4_STATE_EXPORT_BLOB;
+
+C_ASSERT( sizeof( SYMCRYPT_MD4_STATE_EXPORT_BLOB ) == SYMCRYPT_MD4_STATE_EXPORT_SIZE );
+
+
+typedef struct _SYMCRYPT_MD5_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    chain[16];          // In the same format used for the final hash value of MD5
+    UINT64                  dataLength;
+    BYTE                    buffer[64];
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_MD5_STATE_EXPORT_BLOB;
+
+C_ASSERT( sizeof( SYMCRYPT_MD5_STATE_EXPORT_BLOB ) == SYMCRYPT_MD5_STATE_EXPORT_SIZE );
+
+
+typedef struct _SYMCRYPT_SHA1_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    chain[20];          // in the same format used for the final hash value of SHA-1
+    UINT64                  dataLength;
+    BYTE                    buffer[64];
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_SHA1_STATE_EXPORT_BLOB;
+
+C_ASSERT( sizeof( SYMCRYPT_SHA1_STATE_EXPORT_BLOB ) == SYMCRYPT_SHA1_STATE_EXPORT_SIZE );
+
+
+typedef struct _SYMCRYPT_SHA256_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    chain[32];          // in the same format used for the final hash value of SHA-256
+    UINT64                  dataLength;
+    BYTE                    buffer[64];
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_SHA256_STATE_EXPORT_BLOB;
+
+C_ASSERT( sizeof( SYMCRYPT_SHA256_STATE_EXPORT_BLOB ) == SYMCRYPT_SHA256_STATE_EXPORT_SIZE );
+
+
+typedef struct _SYMCRYPT_SHA512_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    chain[64];          // in the same format used for the final hash value of SHA-512
+    UINT64                  dataLengthL;        // low 64 bits of data length
+    UINT64                  dataLengthH;        // high 64 bits of data length
+    BYTE                    buffer[128];
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_SHA512_STATE_EXPORT_BLOB;
+
+C_ASSERT( sizeof( SYMCRYPT_SHA512_STATE_EXPORT_BLOB ) == SYMCRYPT_SHA512_STATE_EXPORT_SIZE );
+
+// Refer to SYMCRYPT_KECCAK_STATE documentation for the explanation of each struct member
+typedef struct _SYMCRYPT_KECCAK_STATE_EXPORT_BLOB {
+    SYMCRYPT_BLOB_HEADER    header;
+    BYTE                    state[200];
+    UINT32                  stateIndex;
+    UINT8                   paddingValue;
+    BOOLEAN                 squeezeMode;
+    BYTE                    rfu[8];             // rfu = Reserved for Future Use.
+    SYMCRYPT_BLOB_TRAILER   trailer;
+} SYMCRYPT_KECCAK_STATE_EXPORT_BLOB;
+
+typedef SYMCRYPT_KECCAK_STATE_EXPORT_BLOB SYMCRYPT_SHA3_224_STATE_EXPORT_BLOB;
+typedef SYMCRYPT_KECCAK_STATE_EXPORT_BLOB SYMCRYPT_SHA3_256_STATE_EXPORT_BLOB;
+typedef SYMCRYPT_KECCAK_STATE_EXPORT_BLOB SYMCRYPT_SHA3_384_STATE_EXPORT_BLOB;
+typedef SYMCRYPT_KECCAK_STATE_EXPORT_BLOB SYMCRYPT_SHA3_512_STATE_EXPORT_BLOB;
+
+C_ASSERT(sizeof(SYMCRYPT_SHA3_224_STATE_EXPORT_BLOB) == SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE);
+C_ASSERT(sizeof(SYMCRYPT_SHA3_256_STATE_EXPORT_BLOB) == SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE);
+C_ASSERT(sizeof(SYMCRYPT_SHA3_384_STATE_EXPORT_BLOB) == SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE);
+C_ASSERT(sizeof(SYMCRYPT_SHA3_512_STATE_EXPORT_BLOB) == SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE);
+
+#pragma pack(pop)
+
+/////////////////////////////////////////////
+// AES internal functions
+
+extern const SYMCRYPT_BLOCKCIPHER SymCryptAesBlockCipherNoOpt;
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4Sbox(
+    _In_reads_(4)   PCBYTE  pIn,
+    _Out_writes_(4) PBYTE   pOut,
+                    BOOL    UseSimd );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4SboxC(
+    _In_reads_(4)   PCBYTE  pIn,
+    _Out_writes_(4) PBYTE   pOut );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4SboxXmm(
+    _In_reads_(4)   PCBYTE  pIn,
+    _Out_writes_(4) PBYTE   pOut );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAes4SboxNeon(
+    _In_reads_(4)   PCBYTE  pIn,
+    _Out_writes_(4) PBYTE   pOut );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKey(
+    _In_reads_(16)      PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey,
+                        BOOL    UseSimd );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKeyC(
+    _In_reads_(16)     PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKeyXmm(
+    _In_reads_(16)     PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCreateDecryptionRoundKeyNeon(
+    _In_reads_(16)     PCBYTE  pEncryptionRoundKey,
+    _Out_writes_(16)    PBYTE   pDecryptionRoundKey );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptC(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptAsm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEncryptNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptC(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptAsm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesDecryptNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                      pbSrc,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE ) PBYTE                       pbDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbEncryptNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesEcbDecryptC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncryptAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncryptXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcEncryptNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecryptAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecryptXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcDecryptNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcMacXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCbcMacNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbData,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb64Asm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb64Xmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb64Neon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb32Xmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb32Neon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+// pbScratch must currently be 16B aligned
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )       PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+// pbScratch must currently be 16B aligned
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitXmm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )       PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitZmm_2048(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitZmm_2048(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitYmm_2048(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitYmm_2048(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _Out_writes_( SYMCRYPT_AES_BLOCK_SIZE*16 )  PBYTE                       pbScratch,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitNeon(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsEncryptDataUnit(
+    _In_                                        PCSYMCRYPT_BLOCKCIPHER      pBlockCipher,
+    _In_                                        PCVOID                      pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsDecryptDataUnit(
+    _In_                                        PCSYMCRYPT_BLOCKCIPHER      pBlockCipher,
+    _In_                                        PCVOID                      pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptStitchedXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptStitchedXmm(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData );
+
+#define GCM_YMM_MINBLOCKS 16
+
+// Caller must check cbData >= GCM_YMM_MINBLOCKS * SYMCRYPT_GCM_BLOCK_SIZE
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptStitchedYmm_2048(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData );
+
+// Caller must check cbData >= GCM_YMM_MINBLOCKS * SYMCRYPT_GCM_BLOCK_SIZE
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptStitchedYmm_2048(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptStitchedNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptStitchedNeon(
+    _In_                                    PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PBYTE                       pbChainingValue,
+    _In_reads_( SYMCRYPT_GF128_FIELD_SIZE ) PCSYMCRYPT_GF128_ELEMENT    expandedKeyTable,
+    _Inout_                                 PSYMCRYPT_GF128_ELEMENT     pState,
+    _In_reads_( cbData )                    PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                  PBYTE                       pbDst,
+                                            SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmEncryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesGcmDecryptPart(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmEncryptPartTwoPass(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptGcmDecryptPartTwoPass(
+    _Inout_                 PSYMCRYPT_GCM_STATE pState,
+    _In_reads_( cbData )    PCBYTE              pbSrc,
+    _Out_writes_( cbData )  PBYTE               pbDst,
+                            SIZE_T              cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptCtrMsb32(
+    _In_                        PCSYMCRYPT_BLOCKCIPHER  pBlockCipher,
+    _In_                        PCVOID                  pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )
+                                PBYTE                   pbChainingValue,
+    _In_reads_( cbData )        PCBYTE                  pbSrc,
+    _Out_writes_( cbData )      PBYTE                   pbDst,
+                                SIZE_T                  cbData );
+//
+// SymCryptCtrMsb32 implements the CTR cipher mode with a 32-bit increment function.
+// It is not intended to be used as-is, rather it is a building block for modes like GCM.
+// See the description of SymCryptCtrMsb64 in symcrypt.h for more details.
+//
+// For now, this function is only intended for use with GCM, which specifies the use a
+// 32-bit increment function. It's only used in cases where we can't use one of the optimized
+// implementations (i.e. on ARM32 or x86[-64] without AESNI). Therefore, unlike the 64-bit version,
+// there are no optimized implementations of the CTR function to call. If we ever need this
+// functionality for other block cipher modes, this function will need to be updated and we'll
+// need to add an additional pointer to SYMCRYPT_BLOCKCIPHER for the optimized CTR function.
+
+VOID
+SYMCRYPT_CALL
+SymCryptAesCtrMsb32(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbChainingValue,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData );
+
+// SymCryptAesCtrMsb32 is a dispatch function for the optimized AES CTR implementations that use
+//a 32-bit counter function (currently only relevant to GCM).
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptParallelHashProcess_serial(
+    _In_                                                            PCSYMCRYPT_PARALLEL_HASH            pParHash,
+    _Inout_updates_bytes_( nStates * pParHash->pHash->stateSize )   PVOID                               pStates,
+                                                                    SIZE_T                              nStates,
+    _Inout_updates_( nOperations )                                  PSYMCRYPT_PARALLEL_HASH_OPERATION   pOperations,
+                                                                    SIZE_T                              nOperations,
+    _Out_writes_( cbScratch )                                       PBYTE                               pbScratch,
+                                                                    SIZE_T                              cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptParallelHashProcess(
+    _In_                                                            PCSYMCRYPT_PARALLEL_HASH            pParHash,
+    _Inout_updates_bytes_( nStates * pParHash->pHash->stateSize )   PVOID                               pStates,
+                                                                    SIZE_T                              nStates,
+    _Inout_updates_( nOperations )                                  PSYMCRYPT_PARALLEL_HASH_OPERATION   pOperations,
+                                                                    SIZE_T                              nOperations,
+    _Out_writes_( cbScratch )                                       PBYTE                               pbScratch,
+                                                                    SIZE_T                              cbScratch,
+                                                                    UINT32                              maxParallel );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashAppendInternal(
+    _In_                        PCSYMCRYPT_HASH             pHash,
+    _Inout_                     PSYMCRYPT_COMMON_HASH_STATE pState,
+    _In_reads_bytes_( cbData )  PCBYTE                      pbData,
+                                SIZE_T                      cbData );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHashCommonPaddingMd4Style(
+    _In_                        PCSYMCRYPT_HASH             pHash,
+    _Inout_                     PSYMCRYPT_COMMON_HASH_STATE pState );
+
+
+extern const PCSYMCRYPT_PARALLEL_HASH SymCryptParallelSha256Algorithm;
+extern const PCSYMCRYPT_PARALLEL_HASH SymCryptParallelSha384Algorithm;
+extern const PCSYMCRYPT_PARALLEL_HASH SymCryptParallelSha512Algorithm;
+
+#define PAR_SCRATCH_ELEMENTS_256    (4+8+64)    // # scratch elements our parallel SHA256 implementations need
+#define PAR_SCRATCH_ELEMENTS_512    (4+8+80)    // # scratch elements our parallel SHA512 implementations need
+
+// pScratch must be 32B aligned, as it is used as an array of __m256i
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha256AppendBlocks_ymm(
+    _Inout_updates_( 8 )                                PSYMCRYPT_SHA256_CHAINING_STATE   * pChain,
+    _Inout_updates_( 8 )                                PCBYTE                            * ppByte,
+                                                        SIZE_T                              nBytes,
+    _Out_writes_( PAR_SCRATCH_ELEMENTS_256 * 32 )       PBYTE                               pScratch );
+
+// pScratch must be 32B aligned, as it is used as an array of __m256i
+VOID
+SYMCRYPT_CALL
+SymCryptParallelSha512AppendBlocks_ymm(
+    _Inout_updates_( 4 )                                PSYMCRYPT_SHA512_CHAINING_STATE   * pChain,
+    _Inout_updates_( 4 )                                PCBYTE                            * ppByte,
+                                                        SIZE_T                              nBytes,
+    _Out_writes_( PAR_SCRATCH_ELEMENTS_512 * 32 )       PBYTE                               pScratch );
+
+extern const SYMCRYPT_HASH SymCryptMd2Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptMd4Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptMd5Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha1Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha224Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha256Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha384Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha512Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha512_224Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha512_256Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha3_224Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha3_256Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha3_384Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptSha3_512Algorithm_default;
+extern const SYMCRYPT_HASH SymCryptShake128HashAlgorithm_default;
+extern const SYMCRYPT_HASH SymCryptShake256HashAlgorithm_default;
+
+
+
+// Paddings used by various SHA-3 derived algorithms
+#define SYMCRYPT_SHA3_PADDING_VALUE     0x06    // 01 10* padding
+#define SYMCRYPT_SHAKE_PADDING_VALUE    0x1f    // 11 11 10* padding
+#define SYMCRYPT_CSHAKE_PADDING_VALUE   0x04    // 00 10* padding (used when N or S are non-empty strings)
+
+//
+// Functions operating on the Keccak state
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakPermute(_Inout_updates_(25) UINT64* pState);
+// Keccak-f[1600] permutation
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakInit(_Out_ PSYMCRYPT_KECCAK_STATE pState, UINT32 inputBlockSize, UINT8 padding);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakReset(_Out_ PSYMCRYPT_KECCAK_STATE pState);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakZeroAppendBlock(_Inout_ PSYMCRYPT_KECCAK_STATE pState);
+// Zero pads the current block by invoking the permutation and setting
+// pState->stateIndex to 0.
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppend(
+    _Inout_                 PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_(cbData)      PCBYTE                  pbData,
+                            SIZE_T                  cbData);
+// Generic append function.
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakExtract(
+    _Inout_                 PSYMCRYPT_KECCAK_STATE  pState,
+    _Out_writes_(cbResult)  PBYTE                   pbResult,
+                            SIZE_T                  cbResult,
+                            BOOLEAN                 bWipe);
+// Generic extract function, no restriction on cbResult.
+// bWipe denotes whether to wipe the Keccak state and initialize it
+// for a new computation.
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakStateExport(
+                                                            SYMCRYPT_BLOB_TYPE      type,
+    _In_                                                    PCSYMCRYPT_KECCAK_STATE pState,
+    _Out_writes_bytes_(SYMCRYPT_KECCAK_STATE_EXPORT_SIZE)   PBYTE                   pbBlob);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptKeccakStateImport(
+                                                        SYMCRYPT_BLOB_TYPE      type,
+    _Out_                                               PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_bytes_(SYMCRYPT_KECCAK_STATE_EXPORT_SIZE) PCBYTE                  pbBlob);
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendEncodeTimes8(
+    _Inout_ SYMCRYPT_KECCAK_STATE *pState,
+            UINT64  uValue,
+            BOOLEAN bLeftEncode);
+// Appends the left-encoding of uValue * 8 to the state
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendEncodedString(
+    _Inout_                 PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_(cbString)    PCBYTE                  pbString,
+                            SIZE_T                  cbString);
+// Appends 'left_encode(cbString * 8) || pbString' to the state
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShakeEncodeInputStrings(
+    _Inout_                             PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_( cbFunctionNameString )  PCBYTE                  pbFunctionNameString,
+                                        SIZE_T                  cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE                  pbCustomizationString,
+                                        SIZE_T                  cbCustomizationString);
+// Process CShake input strings
+// Appends byte_pad( encode_string( pbFunctionNameString ) || encode_string( pbCustomizationString ), pState->inputBlockSize )
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFatalIntercept( UINT32 fatalCode );
+
+extern const BYTE SymCryptSha256KATAnswer[32];
+extern const BYTE SymCryptSha384KATAnswer[48];
+extern const BYTE SymCryptSha512KATAnswer[64];
+
+//
+// Arithmetic
+//
+
+#define SYMCRYPT_ASSERT_ASYM_ALIGNED( _p )           SYMCRYPT_ASSERT( ((SIZE_T)(_p) & (SYMCRYPT_ASYM_ALIGN_VALUE - 1)) == 0 );
+
+
+#define SYMCRYPT_FDEF_DIGIT_NUINT32             ((UINT32)(SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT32 ) ))
+
+#define SYMCRYPT_OBJ_NDIGITS( _p )              ((_p)->nDigits)
+#define SYMCRYPT_OBJ_NBYTES( _p )               ((_p)->nDigits * SYMCRYPT_FDEF_DIGIT_SIZE)
+#define SYMCRYPT_OBJ_NUINT32( _p )              ((_p)->nDigits * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT32 ))
+
+#if SYMCRYPT_MS_VC
+#define SYMCRYPT_MUL32x32TO64( _a, _b )         UInt32x32To64( (_a), (_b) )
+#elif SYMCRYPT_GNUC
+#define SYMCRYPT_MUL32x32TO64( _a, _b )         ( (UINT64)(_a)*(UINT64)(_b) )
+#else
+    #error Unknown compiler
+#endif
+typedef VOID (SYMCRYPT_CALL * SYMCRYPT_MOD_BINARY_OP_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * SYMCRYPT_MOD_UNARY_OP_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+typedef SYMCRYPT_ERROR (SYMCRYPT_CALL * SYMCRYPT_MOD_UNARY_OP_FLAG_STATUS_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * SYMCRYPT_MOD_SET_POST_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+typedef PCUINT32 (SYMCRYPT_CALL * SYMCRYPT_MOD_PRE_GET_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * SYMCRYPT_MOD_COPY_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+typedef VOID (SYMCRYPT_CALL * SYMCRYPT_MODULUS_COPYFIXUP_FN)(
+    _In_                            PCSYMCRYPT_MODULUS      pmSrc,
+    _Out_                           PSYMCRYPT_MODULUS       pmDst );
+
+typedef VOID (SYMCRYPT_CALL * SYMCRYPT_MODULUS_INIT_FN)(
+    _Inout_                         PSYMCRYPT_MODULUS       pmObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+//
+// In the future we might want to implement a 'prepare divisor' for people who want to do one or more modular divisions.
+// In EC projective coordinates you have a value stored as (X,Z) with X/Z being the actual value that needs to be exported.
+// In Montgomery format, this is stored as (RX, RZ), and just doing RX * (1/RZ) gets you the value to be exported.
+// There seem to be many tricks here to get some more speed; maybe we just need to define export functions for each
+// point format and allow the Modulus to contain special optimizations.
+//
+// The SetPost function is the post-processing function of any SetValue operation. The SetValue operation will store the
+// modElement in the normal integer format into the ModElement. The SetPost function post-processes it into the proper
+// representation for that modulus.
+//
+// The PreGet function is the pre-processing function to any GetValue operation. It returns a pointer to the proper value
+// stored in standard integer format. This pointer can either be into the ModElement itself, or into the scratch space.
+//
+
+typedef struct _SYMCRYPT_MODULAR_FUNCTIONS {
+    SYMCRYPT_MOD_BINARY_OP_FN               modAdd;
+    SYMCRYPT_MOD_BINARY_OP_FN               modSub;
+    SYMCRYPT_MOD_UNARY_OP_FN                modNeg;
+    SYMCRYPT_MOD_BINARY_OP_FN               modMul;
+    SYMCRYPT_MOD_UNARY_OP_FN                modSquare;
+    SYMCRYPT_MOD_UNARY_OP_FLAG_STATUS_FN    modInv;
+    SYMCRYPT_MOD_SET_POST_FN                modSetPost;
+    SYMCRYPT_MOD_PRE_GET_FN                 modPreGet;
+    SYMCRYPT_MODULUS_COPYFIXUP_FN           modulusCopyFixup;   // non-generic fixup after memcpy
+    SYMCRYPT_MODULUS_INIT_FN                modulusInit;
+    PVOID                                   slack[6];
+} SYMCRYPT_MODULAR_FUNCTIONS;
+
+#define SYMCRYPT_MODULAR_FUNCTIONS_SIZE    (sizeof( SYMCRYPT_MODULAR_FUNCTIONS ) )
+
+extern const SYMCRYPT_MODULAR_FUNCTIONS g_SymCryptModFns[];
+extern const UINT32 g_SymCryptModFnsMask;
+
+//
+// Table entry that contains the information about an implementation.
+// Allows generic code to make the decision.
+// First entry in the table that is allowed is chosen, last entry always matches everything
+//
+
+#define SYMCRYPT_MODULUS_FEATURE_MONTGOMERY         1       // Modulus is suitable for Montgomery processing
+// #define SYMCRYPT_MODULUS_FEATURE_PSEUDO_MERSENNE    2       // Modulus is suitable for Pseudo-Mersenne processing
+// #define SYMCRYPT_MODULUS_FEATURE_NISTP256           4       // Modulus is the NIST P256 curve prime
+#define SYMCRYPT_MODULUS_FEATURE_NISTP384           8       // Modulus is the NIST P384 curve prime
+
+typedef struct _SYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY
+{
+    UINT32                  type;               // Type value of this solution
+    SYMCRYPT_CPU_FEATURES   cpuFeatures;        // Required CPU features
+    UINT32                  maxBits;            // Max # bits that the actual value of the modulus is, 0 = no limit
+    UINT32                  modulusFeatures;    // Required features of the modulus
+} SYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY, *PSYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY;
+typedef const SYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY* PCSYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY;
+
+extern const SYMCRYPT_MODULUS_TYPE_SELECTION_ENTRY SymCryptModulusTypeSelections[];       // Array can be any size...
+
+
+// Check that the size is a power of 2
+C_ASSERT( (SYMCRYPT_MODULAR_FUNCTIONS_SIZE & (SYMCRYPT_MODULAR_FUNCTIONS_SIZE-1)) == 0 );
+
+// The macro that we use to call modular functions
+#define SYMCRYPT_MOD_CALL(v) ((SYMCRYPT_MODULAR_FUNCTIONS *)(( SYMCRYPT_FORCE_READ32( &(v)->type) & g_SymCryptModFnsMask) + (PBYTE)(&g_SymCryptModFns) ))->
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_GENERIC {\
+    &SymCryptFdefModAddGeneric,\
+    &SymCryptFdefModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdefModMulGeneric,\
+    &SymCryptFdefModSquareGeneric,\
+    &SymCryptFdefModInvGeneric,\
+    &SymCryptFdefModSetPostGeneric,\
+    &SymCryptFdefModPreGetGeneric,\
+    &SymCryptFdefModulusCopyFixupGeneric,\
+    &SymCryptFdefModulusInitGeneric,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY {\
+    &SymCryptFdefModAddGeneric,\
+    &SymCryptFdefModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdefModMulMontgomery,\
+    &SymCryptFdefModSquareMontgomery,\
+    &SymCryptFdefModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomery,\
+    &SymCryptFdefModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_ARM64256 {\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModAdd256Asm,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModSub256Asm,\
+    &SymCryptFdefModNegGeneric,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModMulMontgomery256Asm, \
+    (SYMCRYPT_MOD_UNARY_OP_FN) &SymCryptFdefModSquareMontgomery256Asm, \
+    &SymCryptFdefModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomery,\
+    &SymCryptFdefModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_ARM64P384 {\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModAdd384Asm,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModSub384Asm,\
+    &SymCryptFdefModNegGeneric,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModMulMontgomeryP384Asm, \
+    (SYMCRYPT_MOD_UNARY_OP_FN) &SymCryptFdefModSquareMontgomeryP384Asm, \
+    &SymCryptFdef369ModInvMontgomery,\
+    &SymCryptFdef369ModSetPostMontgomery,\
+    &SymCryptFdef369ModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdef369ModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX256 {\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModAddMulx256Asm,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModSub256Asm,\
+    &SymCryptFdefModNegGeneric,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModMulMontgomeryMulx256Asm,\
+    (SYMCRYPT_MOD_UNARY_OP_FN) &SymCryptFdefModSquareMontgomeryMulx256Asm,\
+    &SymCryptFdefModInvMontgomery256,\
+    &SymCryptFdefModSetPostMontgomeryMulx256,\
+    &SymCryptFdefModPreGetMontgomery256,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery256,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULXP256 {\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModAddMulx256Asm,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModSub256Asm,\
+    &SymCryptFdefModNegGeneric,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModMulMontgomeryMulxP256Asm,\
+    (SYMCRYPT_MOD_UNARY_OP_FN) &SymCryptFdefModSquareMontgomeryMulxP256Asm,\
+    &SymCryptFdefModInvMontgomery256,\
+    &SymCryptFdefModSetPostMontgomeryMulx256,\
+    &SymCryptFdefModPreGetMontgomery256,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery256,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX384 {\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModAddMulx384Asm,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModSub384Asm,\
+    &SymCryptFdefModNegGeneric,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModMulMontgomeryMulx384Asm,\
+    (SYMCRYPT_MOD_UNARY_OP_FN) &SymCryptFdefModSquareMontgomeryMulx384Asm,\
+    &SymCryptFdef369ModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomeryMulx384,\
+    &SymCryptFdef369ModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdef369ModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULXP384 {\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModAddMulx384Asm,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModSub384Asm,\
+    &SymCryptFdefModNegGeneric,\
+    (SYMCRYPT_MOD_BINARY_OP_FN) &SymCryptFdefModMulMontgomeryMulxP384Asm,\
+    (SYMCRYPT_MOD_UNARY_OP_FN) &SymCryptFdefModSquareMontgomeryMulxP384Asm,\
+    &SymCryptFdef369ModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomeryMulxP384,\
+    &SymCryptFdef369ModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdef369ModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF369_MONTGOMERY {\
+    &SymCryptFdef369ModAddGeneric,\
+    &SymCryptFdef369ModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdef369ModMulMontgomery,\
+    &SymCryptFdef369ModSquareMontgomery,\
+    &SymCryptFdef369ModInvMontgomery,\
+    &SymCryptFdef369ModSetPostMontgomery,\
+    &SymCryptFdef369ModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdef369ModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX {\
+    &SymCryptFdefModAddGeneric,\
+    &SymCryptFdefModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdefModMulMontgomeryMulx,\
+    &SymCryptFdefModSquareMontgomeryMulx,\
+    &SymCryptFdefModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomery,\
+    &SymCryptFdefModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY512 {\
+    &SymCryptFdefModAddGeneric,\
+    &SymCryptFdefModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdefModMulMontgomery512,\
+    &SymCryptFdefModSquareMontgomery512,\
+    &SymCryptFdefModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomery,\
+    &SymCryptFdefModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY1024 {\
+    &SymCryptFdefModAddGeneric,\
+    &SymCryptFdefModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdefModMulMontgomery1024,\
+    &SymCryptFdefModSquareMontgomery1024,\
+    &SymCryptFdefModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomery,\
+    &SymCryptFdefModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery,\
+}
+
+#define SYMCRYPT_MOD_FUNCTIONS_FDEF_MONTGOMERY_MULX1024 {\
+    &SymCryptFdefModAddGeneric,\
+    &SymCryptFdefModSubGeneric,\
+    &SymCryptFdefModNegGeneric,\
+    &SymCryptFdefModMulMontgomeryMulx1024,\
+    &SymCryptFdefModSquareMontgomeryMulx1024,\
+    &SymCryptFdefModInvMontgomery,\
+    &SymCryptFdefModSetPostMontgomery,\
+    &SymCryptFdefModPreGetMontgomery,\
+    &SymCryptFdefModulusCopyFixupMontgomery,\
+    &SymCryptFdefModulusInitMontgomery,\
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMaskedCopy(
+    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+                                                                UINT32      nDigits,
+                                                                UINT32      mask );
+//
+// Copies Src to Dst under mask.
+// Requirements:
+//  - mask == 0 or mask == 0xffffffff
+//  - cbData must be a multiple of the size of a digit, or a multiple of the size of a ModElement.
+//  - pbSrc and pbDst must be SYMCRYPT_ALIGNed
+// if mask == 0 this function does nothing.
+// if mask == 0xffffffff this function is a memcpy from Src to Dst.
+// This function is side-channel safe; the value of mask is not revealed
+// through the memory access patterns.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefConditionalSwap(
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbSrc1,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbSrc2,
+                                                                UINT32      nDigits,
+                                                                UINT32      cond );
+
+//
+// Swaps the bytes of Src1 with the bytes of Src2 under a condition.
+// Requirements:
+//  - cond = 0 or cond = 1 .
+//  - cbData must be a multiple of the size of a digit, or a multiple of the size of a ModElement.
+//  - pbSrc1 and pbSrc2 must be SYMCRYPT_ALIGNed
+// if cond == 0 this function does nothing.
+// if cond == 1 this function swaps the bytes of Src1 with the bytes of Src2.
+// This function is side-channel safe; the value of cond is not revealed
+// through the memory access patterns.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefClaimScratch( PBYTE pbScratch, SIZE_T cbScratch, SIZE_T cbMin );
+
+UINT32
+SymCryptFdefDigitsFromBits( UINT32 nBits );
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntAllocate( UINT32 nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofIntFromDigits( UINT32 nDigits );
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits );
+
+VOID
+SymCryptFdefIntCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+VOID
+SymCryptFdefIntMaskedCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          mask );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntConditionalCopy(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Inout_ PSYMCRYPT_INT   piDst,
+            UINT32          cond );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntConditionalSwap(
+    _Inout_ PSYMCRYPT_INT   piSrc1,
+    _Inout_ PSYMCRYPT_INT   piSrc2,
+            UINT32          cond );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntBitsizeOfObject( _In_ PCSYMCRYPT_INT  piSrc );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefNumberofDigitsFromInt( _In_ PCSYMCRYPT_INT piSrc );
+
+SYMCRYPT_ERROR
+SymCryptFdefIntCopyMixedSize(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntBitsizeOfValue( _In_ PCSYMCRYPT_INT piSrc );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSetValueUint32(
+            UINT32          u32Src,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSetValueUint64(
+            UINT64          u64Src,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefIntSetValue(
+    _In_reads_bytes_(cbSrc)     PCBYTE                  pbSrc,
+                                SIZE_T                  cbSrc,
+                                SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_                       PSYMCRYPT_INT           piDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefIntGetValue(
+    _In_                        PCSYMCRYPT_INT          piSrc,
+    _Out_writes_bytes_(cbDst)   PBYTE                   pbDst,
+                                SIZE_T                  cbDst,
+                                SYMCRYPT_NUMBER_FORMAT  format );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntGetValueLsbits32( _In_  PCSYMCRYPT_INT piSrc );
+
+UINT64
+SYMCRYPT_CALL
+SymCryptFdefIntGetValueLsbits64( _In_  PCSYMCRYPT_INT piSrc );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntAddUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntAddSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntAddMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntSubUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+            UINT32          u32Src2,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntSubSameSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntSubMixedSize(
+    _In_    PCSYMCRYPT_INT piSrc1,
+    _In_    PCSYMCRYPT_INT piSrc2,
+    _Out_   PSYMCRYPT_INT  piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntNeg(
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntMulPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          Exp,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntDivPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntShr1(
+            UINT32          highestBit,
+    _In_    PCSYMCRYPT_INT  piSrc,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntModPow2(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            SIZE_T          exp,
+    _Out_   PSYMCRYPT_INT   piDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntGetBit(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntGetBits(
+    _In_    PCSYMCRYPT_INT  piSrc,
+            UINT32          iBit,
+            UINT32          nBits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSetBits(
+    _In_    PSYMCRYPT_INT   piDst,
+            UINT32          value,
+            UINT32          iBit,
+            UINT32          nBits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntIsEqualUint32(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    UINT32          u32Src2 );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntIsEqual(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntIsLessThan(
+    _In_    PCSYMCRYPT_INT  piSrc1,
+    _In_    PCSYMCRYPT_INT  piSrc2 );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntMulUint32(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+                                    UINT32          Src2,
+    _Out_                           PSYMCRYPT_INT   piDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntMulSameSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntSquare(
+    _In_                            PCSYMCRYPT_INT  piSrc,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntMulMixedSize(
+    _In_                            PCSYMCRYPT_INT  piSrc1,
+    _In_                            PCSYMCRYPT_INT  piSrc2,
+    _Out_                           PSYMCRYPT_INT   piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE           pbScratch,
+                                    SIZE_T          cbScratch );
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorAllocate( UINT32 nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofDivisorFromDigits( UINT32 nDigits );
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits );
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorRetrieveHandle( _In_ PBYTE pbBuffer );
+
+VOID
+SymCryptFdefDivisorCopy(
+    _In_    PCSYMCRYPT_DIVISOR  pdSrc,
+    _Out_   PSYMCRYPT_DIVISOR   pdDst );
+
+VOID
+SymCryptFdefDivisorCopyFixup(
+    _In_    PCSYMCRYPT_DIVISOR  pSrc,
+    _Out_   PSYMCRYPT_DIVISOR   pDst );
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntFromDivisor( _In_ PSYMCRYPT_DIVISOR pdSrc );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntToDivisor(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_DIVISOR   pdDst,
+                                    UINT32              totalOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntDivMod(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _In_                            PCSYMCRYPT_DIVISOR  pdDivisor,
+    _Out_opt_                       PSYMCRYPT_INT       piQuotient,
+    _Out_opt_                       PSYMCRYPT_INT       piRemainder,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawDivMod(
+    _In_reads_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)           PCUINT32            pNum,
+                                                                UINT32              nDigits,
+    _In_                                                        PCSYMCRYPT_DIVISOR  pdDivisor,
+    _Out_writes_opt_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32             pQuotient,
+    _Out_writes_opt_(SYMCRYPT_OBJ_NUINT32(pdDivisor))           PUINT32             pRemainder,
+    _Out_writes_bytes_( cbScratch )                             PBYTE               pbScratch,
+                                                                SIZE_T              cbScratch );
+
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptFdefModulusAllocate( UINT32 nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusFree( _Out_ PSYMCRYPT_MODULUS pmObj );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofModulusFromDigits( UINT32 nDigits );
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptFdefModulusCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    SIZE_T  cbBuffer,
+                                    UINT32  nDigits );
+
+PSYMCRYPT_MODULUS
+SYMCRYPT_CALL
+SymCryptFdefModulusRetrieveHandle( _In_ PBYTE pbBuffer );
+
+
+VOID
+SymCryptFdefModulusCopy(
+    _In_    PCSYMCRYPT_MODULUS  pmSrc,
+    _Out_   PSYMCRYPT_MODULUS   pmDst );
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptFdefModElementAllocate( _In_ PCSYMCRYPT_MODULUS pmMod );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementFree(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peObj );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefSizeofModElementFromModulus( PCSYMCRYPT_MODULUS pmMod );
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptFdefModElementCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE               pbBuffer,
+                                    SIZE_T              cbBuffer,
+                                    PCSYMCRYPT_MODULUS   pmMod );
+
+PSYMCRYPT_MODELEMENT
+SYMCRYPT_CALL
+SymCryptFdefModElementRetrieveHandle( _In_ PBYTE pbBuffer );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementWipe(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SymCryptFdefModElementCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SymCryptFdefModElementMaskedCopy(
+    _In_    PCSYMCRYPT_MODULUS      pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_   PSYMCRYPT_MODELEMENT    peDst,
+            UINT32                  mask );
+
+PSYMCRYPT_DIVISOR
+SYMCRYPT_CALL
+SymCryptFdefDivisorFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc );
+
+VOID
+SymCryptFdefModElementConditionalSwap(
+    _In_       PCSYMCRYPT_MODULUS    pmMod,
+    _Inout_    PSYMCRYPT_MODELEMENT  peData1,
+    _Inout_    PSYMCRYPT_MODELEMENT  peData2,
+    _In_       UINT32                cond );
+
+PSYMCRYPT_INT
+SYMCRYPT_CALL
+SymCryptFdefIntFromModulus( _In_ PSYMCRYPT_MODULUS pmSrc );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntToModulus(
+    _In_                            PCSYMCRYPT_INT      piSrc,
+    _Out_                           PSYMCRYPT_MODULUS   pmDst,
+                                    UINT32              averageOperations,
+                                    UINT32              flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefIntToModElement(
+    _In_                            PCSYMCRYPT_INT          piSrc,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementToIntGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_bytes_( pmMod->nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )
+                                    PCUINT32                pSrc,
+    _Out_                           PSYMCRYPT_INT           piDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefRawSetValue(
+    _In_reads_bytes_(cbSrc)                             PCBYTE                  pbSrc,
+                                                        SIZE_T                  cbSrc,
+                                                        SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_writes_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32                 pDst,
+                                                        UINT32                  nDigits );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModElementSetValueGeneric(
+    _In_reads_bytes_( cbSrc )       PCBYTE                  pbSrc,
+                                    SIZE_T                  cbSrc,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementSetValueUint32Generic(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModElementSetValueNegUint32(
+                                    UINT32                  value,
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefRawGetValue(
+    _In_reads_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32)   PCUINT32                pSrc,
+                                                        UINT32                  nDigits,
+    _Out_writes_bytes_(cbDst)                           PBYTE                   pbDst,
+                                                        SIZE_T                  cbDst,
+                                                        SYMCRYPT_NUMBER_FORMAT  format );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModElementGetValue(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_writes_bytes_( cbDst )     PBYTE                   pbDst,
+                                    SIZE_T                  cbDst,
+                                    SYMCRYPT_NUMBER_FORMAT  format,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefModElementIsEqual(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc1,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc2 );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefModElementIsZero(
+    _In_    PCSYMCRYPT_MODULUS     pmMod,
+    _In_    PCSYMCRYPT_MODELEMENT  peSrc );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModAddGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModAddMulx256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModAddMulx384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModAdd256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModAdd384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModAddGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSubGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModSubGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSub256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSub384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModNegGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostMontgomeryMulx256(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetPostMontgomeryMulxP384(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModSetPostMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PSYMCRYPT_MODELEMENT    peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdefModPreGetGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdefModPreGetMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdefModPreGetMontgomery256(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+PCUINT32
+SYMCRYPT_CALL
+SymCryptFdef369ModPreGetMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusCopyFixupGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmSrc,
+    _Out_                           PSYMCRYPT_MODULUS       pmDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusCopyFixupMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pmSrc,
+    _Out_                           PSYMCRYPT_MODULUS       pmDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitGeneric(
+    _Inout_                         PSYMCRYPT_MODULUS       pmObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitMontgomeryInternal(
+    _Inout_                         PSYMCRYPT_MODULUS       pmObj,
+                                    UINT32                  nUint32Used,            // R = 2^{32 * this parameter}
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitMontgomery(
+    _Inout_                         PSYMCRYPT_MODULUS       pmObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModulusInitMontgomery256(
+    _Inout_                         PSYMCRYPT_MODULUS       pmObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModulusInitMontgomery(
+    _Inout_                         PSYMCRYPT_MODULUS       pmObj,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAdd(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSub(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits );
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSubUint32(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+                                                            UINT32      Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryMulx256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryMulxP384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomery256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryP384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModMulMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryMulx(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModMulMontgomeryMulx1024(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryMulx256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryMulxP384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomery256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryP384Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc1,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc2,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369ModSquareMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryMulx(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSquareMontgomeryMulx1024(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMul(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMulMulx(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMulMulx1024(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquare(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+                                                            UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquareMulx(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+                                                            UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquareMulx1024(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)         PCUINT32    pSrc,
+                                                            UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369RawMul(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsEqualUint32(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PCUINT32        pSrc1,
+                                                    UINT32          nDigits,
+    _In_                                            UINT32          u32Src2 );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawNeg(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+                                                            UINT32      carryIn,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawMaskedAdd(
+    _Inout_updates_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )  PUINT32     pAcc,
+    _In_reads_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )       PCUINT32    pSrc,
+                                                            UINT32      mask,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawMaskedSub(
+    _Inout_updates_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )  PUINT32     pAcc,
+    _In_reads_( nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32 )       PCUINT32    pSrc,
+                                                            UINT32      mask,
+                                                            UINT32      nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivPow2(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peSrc,
+                                    UINT32                  exp,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2Asm(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModDivSmallPow2Mulx(
+    _In_                        PCSYMCRYPT_MODULUS      pmMod,
+    _In_                        PCSYMCRYPT_MODELEMENT   peSrc,
+    _In_range_(1, NATIVE_BITS)  UINT32                  exp,
+    _Out_                       PSYMCRYPT_MODELEMENT    peDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModInvGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModInvMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdefModInvMontgomery256(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptFdef369ModInvMontgomery(
+    _In_                            PCSYMCRYPT_MODULUS      pMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   pSrc,
+    _Out_                           PSYMCRYPT_MODELEMENT    pDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptModExpGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
+    _In_                            PCSYMCRYPT_INT          piExp,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptModMultiExpGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
+    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
+                                    UINT32                  nBases,
+                                    UINT32                  nBitsExp,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefModSetRandomGeneric(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAddUint32(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+                                                            UINT32      Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawAddAsm(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdef369RawAddAsm(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    Src2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     Dst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawSubAsm(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdef369RawSubAsm(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+    _Out_writes_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE ) PUINT32     pDst,
+                                                            UINT32      nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefRawIsLessThan(
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc1,
+    _In_reads_bytes_(nDigits * SYMCRYPT_FDEF_DIGIT_SIZE )   PCUINT32    pSrc2,
+                                                            UINT32      nDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMaskedCopyAsm(
+    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+                                                                UINT32      nDigits,
+                                                                UINT32      mask );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369MaskedCopyAsm(
+    _In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )        PCBYTE      pbSrc,
+    _Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE )   PBYTE       pbDst,
+                                                                UINT32      nDigits,
+                                                                UINT32      mask );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMulAsm(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquareAsm(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369RawMulAsm(
+    _In_reads_(nDigits1*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc1,
+                                                                    UINT32      nDigits1,
+    _In_reads_(nDigits2*SYMCRYPT_FDEF_DIGIT_NUINT32)                PCUINT32    pSrc2,
+                                                                    UINT32      nDigits2,
+    _Out_writes_((nDigits1+nDigits2)*SYMCRYPT_FDEF_DIGIT_NUINT32)   PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMul512Asm(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquare512Asm(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawMul1024Asm(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc1,
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc2,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefRawSquare1024Asm(
+    _In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32)     PCUINT32    pSrc,
+                                                        UINT32      nDigits,
+    _Out_writes_(2*nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32     pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduceAsm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduce256Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduce512Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduce1024Asm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369MontgomeryReduce(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdef369MontgomeryReduceAsm(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduceMulx(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefMontgomeryReduceMulx1024(
+    _In_                            PCSYMCRYPT_MODULUS      pmMod,
+    _Inout_                         PUINT32                 pSrc,
+    _Out_                           PUINT32                 pDst );
+
+
+//=====================================================
+// Current state of FIPS tests for asymmetric keys
+//=====================================================
+
+// --------------------------------------------------------------------
+// Key type |       |
+//     &    | Alg   | Description
+// Operation|       |
+// --------------------------------------------------------------------
+// Dlkey    | DH    | Requires use of named safe-prime group (otherwise we cannot perform private
+// Generate |       | key range check, or public key order validation).
+//          |       |
+//          |       | From SP800-56Ar3:
+//          |       | Check private key is in the range [1, min(2^nBitsPriv, q)-1]
+//          |       |   nBitsPriv is specified either using a default value or using
+//          |       |   SymCryptDlkeySetPrivateKeyLength, such that 2s <= nBitsPriv <= nBitsOfQ.
+//          |       |   (s is the maximum security strength for a named safe-prime group as
+//          |       |   specified in SP800 - 56arev3)
+//          |       | Check public key is in the range [2, p-2]
+//          |       | Check that (Public key)^q == 1 mod p
+//          |       |
+//          |       | FIPS 140-3 does not require a further PCT before first use of the key.
+//          |-----------------------------------------------------------
+//          | DSA   | Requires use of a Dlgroup which has q, but is not a named safe-prime group.
+//          |       |
+//          |       | FIPS 186-4 and SP800-89 do not require DSA keypair owners to perform
+//          |       | validation of keypairs they generate.
+//          |       |
+//          |       | FIPS 140-3 requires that a module generating a Dlkey keypair for use in DSA
+//          |       | must perform a PCT on the keypair before first operational use in DSA.
+//          |       | As the Dlgroups supported by FIPS are distinct for DH and DSA, we can perform
+//          |       | this PCT on key generation without fear of adverse performance.
+// --------------------------------------------------------------------
+// Dlkey    | DH    | Requires use of named safe-prime group (otherwise we cannot perform private
+// SetValue |       | key range check, or public key order validation).
+//          |       |
+//          |       | From SP800-56Ar3:
+//          |       | If importing a private key:
+//          |       |   Check private key is in the range [1, min(2^nBitsPriv, q)-1]
+//          |       |     nBitsPriv is specified either using a default value or using
+//          |       |     SymCryptDlkeySetPrivateKeyLength, such that 2s <= nBitsPriv <= nBitsOfQ.
+//          |       |     (s is the maximum security strength for a named safe-prime group as
+//          |       |     specified in SP800-56Arev3)
+//          |       |
+//          |       | If importing a public key:
+//          |       |   Check public key is in the range [2, p-2]
+//          |       |   Check that (Public key)^q == 1 mod p
+//          |       |
+//          |       | If importing both a private and public key, as above and also:
+//          |       |   Use the imported Private key to generate a Public key, and check the
+//          |       |   generated Public key is equal to the imported Public key.
+//          |-----------------------------------------------------------
+//          | DSA   | Requires use of a Dlgroup which is not a named safe-prime group.
+//          |       |
+//          |       | FIPS 184-4 refers to SP800-89:
+//          |       | If importing a public key:
+//          |       |   Check public key is in the range [2, p-2]
+//          |       |   Check that (Public key)^q == 1 mod p
+//          |       | If importing a private and public key:
+//          |       |   Use the imported Private key to generate a Public key, and check the
+//          |       |   generated Public key is equal to the imported Public key.
+// --------------------------------------------------------------------
+// Eckey    | ECDH  | Requires use of a NIST prime Elliptic Curve (P224, P256, P384, or P521)
+// SetRandom|       |
+//          |       | From SP800-56Ar3:
+//          |       | Check private key is in range [1, GOrd-1]
+//          |       | Check public key is nonzero, has coordinates in the underlying field, and is a
+//          |       | point on the curve
+//          |       | Check that GOrd*(Public key) == O
+//          |       |
+//          |       | FIPS 140-3 does not require a further PCT before first use of the key
+//          |----------------------------------------------------------
+//          | ECDSA | Requires use of a NIST prime Elliptic Curve (P224, P256, P384, or P521)
+//          |       |
+//          |       | FIPS 186-4 and SP800-89 do not require ECDSA keypair owners to perform
+//          |       | validation of keypairs they generate.
+//          |       |
+//          |       | FIPS 140-3 requires that a module generating an Eckey keypair for use in ECDSA
+//          |       | must perform a PCT on the keypair before first operational use in ECDSA.
+//          |       | As the Elliptic curves used in ECDH and ECDSA are the same, an Eckey may be
+//          |       | used for both ECDH and ECDSA. We defer the ECDSA PCT from the EckeySetRandom
+//          |       | call to the first use of EcDsaSign, or the first export of the keypair.
+// --------------------------------------------------------------------
+// Eckey    | ECDH  | Requires use of a NIST prime Elliptic Curve (P224, P256, P384, or P521)
+// SetValue |       |
+//          |       | From SP800-56Ar3:
+//          |       | If importing a private key:
+//          |       |   Check private key is in range [1, GOrd-1]
+//          |       |
+//          |       | If importing a public key:
+//          |       |   Check public key is nonzero, has coordinates in the underlying field, and is
+//          |       |   a point on the curve
+//          |       |   Check that GOrd*(Public key) == O
+//          |       |
+//          |       | If importing a private and public key:
+//          |       |   Use the imported Private key to generate a Public key, and check the
+//          |       |   generated Public key is equal to the imported Public key.
+//          |----------------------------------------------------------
+//          | ECDSA | Requires use of a NIST prime Elliptic Curve (P224, P256, P384, or P521)
+//          |       |
+//          |       | FIPS 184-4 refers to SP800-89:
+//          |       | If importing a public key:
+//          |       |   SP800-89 refers to ANS X9.62. Assume same tests required as SP800-56Ar3:
+//          |       |   Check public key is nonzero, has coordinates in the underlying field, and is
+//          |       |   a point on the curve
+//          |       |   Check that GOrd*(Public key) == O
+//          |       |
+//          |       | If importing a private and public key:
+//          |       |   Use the imported Private key to generate a Public key, and check the
+//          |       |   generated Public key is equal to the imported Public key.
+// --------------------------------------------------------------------
+// Rsakey   | RSA   | From FIPS 186-4 (SIGN) and SP800-56Br2 (ENCRYPT for key transport):
+// Generate |ENCRYPT| Ensure p and q are in open range (2 ^ ((nBits - 1) / 2), 2 ^ (nBits / 2))
+//          | and   | Ensure |p-q| > 2^((nBits/2)-100)
+//          | RSA   | Ensure e is coprime with (p-1) and (q-1)
+//          | SIGN  | Ensure d is in range [2 ^ (nBits/2) + 1, LCM(p-1,q-1) - 1]
+//          |       | Ensure that d*e == 1 mod LCM(p-1,q-1)
+//          |       |
+//          |       | FIPS 140-3 requires that a module generating an Rsakey keypair for use in an
+//          |       | RSA algorithm must perform a PCT on the keypair before first operational use.
+//          |       |
+//          |       | For ENCRYPT, SP800-56Br2 specifies the PCT to perform as part of key
+//          |       | generation is:
+//          |       |   Check (m^e)^d == m mod n for some m in range [2, n-2]
+//          |       |
+//          |       | For SIGN, FIPS 186-4 refers to SP800-89, which does not clearly specify a
+//          |       | PCT, but does specify that for an owner to have assurance of Private Key
+//          |       | Possession they can sign a message with the private key and validate it with
+//          |       | the public key to check they correspond to each other. Notably, this
+//          |       | internally will verify (m^d)^e == m mod n for some m (along with testing
+//          |       | additional padding logic)
+//          |       |
+//          |       | FIPS 140-2 explicitly says that only one PCT is required if a keypair may be
+//          |       | used in either algorithm, with the module able to choose the PCT.
+//          |       | FIPS 140-3 does not say anything specific about only requiring one PCT, but
+//          |       | given that mathematically (m^e)^d == (m^ed) == (m^d)^e mod n, our
+//          |       | current understanding is that the SIGN PCT works in lieu of the ENCRYPT PCT
+//          |       |
+//          |       | NOTE: FIPS 140-3 explicitly says that an RSA PCT cannot be used in lieu of an
+//          |       | RSA algorithm selftest (CAST)
+// --------------------------------------------------------------------
+// Rsakey   | RSA   | If importing a keypair (primes and modulus):
+// SetValue |ENCRYPT| SP800-56Br2 specifies:
+//          |       | Check (m^e)^d mod n == m for some m in range [2, n-2]
+//          |       | Check n == p*q
+//          |       | Check p and q are in open range (2 ^ ((nBits - 1) / 2), 2 ^ (nBits / 2))
+//          |       | Check |p-q| > 2^((nBits/2)-100)
+//          |       | Check e is coprime with (p-1) and (q-1)
+//          |       | Check p and q are probably prime
+//          |       | Check d is in range [2 ^ (nBits/2) + 1, LCM(p-1,q-1) - 1]
+//          |       | Check that d*e == 1 mod LCM(p-1,q-1)
+//          |       |
+//          |       | If importing a public key (only modulus):
+//          |       | SP800-56Br2, refers to SP800-89 which details the following Partial Public Key
+//          |       | Validation:
+//          |       | Check n is odd
+//          |       | Check n is not a prime or a power of a prime
+//          |       | Check n has no factors smaller than 752
+//          |----------------------------------------------------------
+//          | RSA   | FIPS 186-4 refers only to SP800-89 which has weaker tests for a keypair than
+//          | SIGN  | SP800-56Br2 (i.e. success at SP800-56Br2 tests implies success in SP800-89)
+//          |       | The current strategy will be to always perform the stronger tests.
+// --------------------------------------------------------------------
+
+// Macro for executing a Cryptographic Algorithm Self-Test (CAST) and setting the corresponding
+// flag. These selftests must be run once per algorithm before the algorithm is used. For algorithms
+// like hashing and symmetric encryption which have a low performance cost, we run the CASTs when
+// the module is loaded. For asymmetric algorithms, we defer the CASTs until the first use of the
+// algorithm; hence we need flags to keep track of which CASTs have been run.
+#define SYMCRYPT_RUN_SELFTEST_ONCE(AlgorithmSelftestFunction, AlgorithmSelftestFlag) \
+if( ( g_SymCryptFipsSelftestsPerformed & AlgorithmSelftestFlag ) == 0 ) \
+{ \
+    AlgorithmSelftestFunction( ); \
+    SYMCRYPT_ATOMIC_OR32_PRE_RELAXED( &g_SymCryptFipsSelftestsPerformed, AlgorithmSelftestFlag ); \
+}
+
+// Macros for executing a pairwise consistency test on a key and setting the per-key selftest flag.
+// Typically PCTs must be run for each key before the key is first used or exported, but the
+// specific requirements vary between algorithms.
+//
+// Note that a PCT is not considered a CAST and thus does not satisfy the aforementioned requirement
+// for algorithm selftests.
+#define SYMCRYPT_RUN_KEY_GEN_PCT(KeySelftestFunction, Key, KeySelftestFlag) \
+if( ( Key->fAlgorithmInfo & (KeySelftestFlag | SYMCRYPT_FLAG_KEY_NO_FIPS) ) == 0 ) \
+{ \
+    /* PCT should never fail on key generation - FIPS assert that it does not */ \
+    SYMCRYPT_FIPS_ASSERT( KeySelftestFunction( Key ) == SYMCRYPT_NO_ERROR ); \
+    SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(&Key->fAlgorithmInfo, KeySelftestFlag); \
+}
+
+// Macro to check flag used in fAlgorithmInfo is non-zero and a power of 2
+#define CHECK_ALGORITHM_INFO_FLAG_POW2( flag ) \
+    C_ASSERT( (flag != 0) && ((flag & (flag-1)) == 0) );
+
+// Macro to check flags used together in fAlgorithmInfo are distinct
+#define CHECK_ALGORITHM_INFO_FLAGS_DISTINCT( flag0, flag1, flag2, flag3, flag4 ) \
+    C_ASSERT( (flag0 < flag1) && (flag1 < flag2) && (flag2 < flag3) && (flag3 < flag4) );
+
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_PCT_DSA);
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_PCT_ECDSA);
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_PCT_RSA_SIGN);
+
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_KEY_NO_FIPS);
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION);
+
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_DLKEY_DSA);
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_DLKEY_DH);
+
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_ECKEY_ECDSA);
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_ECKEY_ECDH);
+
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_RSAKEY_SIGN);
+CHECK_ALGORITHM_INFO_FLAG_POW2(SYMCRYPT_FLAG_RSAKEY_ENCRYPT);
+
+CHECK_ALGORITHM_INFO_FLAGS_DISTINCT(SYMCRYPT_PCT_DSA, SYMCRYPT_FLAG_KEY_NO_FIPS, SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION, SYMCRYPT_FLAG_DLKEY_DSA, SYMCRYPT_FLAG_DLKEY_DH);
+CHECK_ALGORITHM_INFO_FLAGS_DISTINCT(SYMCRYPT_PCT_ECDSA, SYMCRYPT_FLAG_KEY_NO_FIPS, SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION, SYMCRYPT_FLAG_ECKEY_ECDSA, SYMCRYPT_FLAG_ECKEY_ECDH);
+CHECK_ALGORITHM_INFO_FLAGS_DISTINCT(SYMCRYPT_PCT_RSA_SIGN, SYMCRYPT_FLAG_KEY_NO_FIPS, SYMCRYPT_FLAG_KEY_MINIMAL_VALIDATION, SYMCRYPT_FLAG_RSAKEY_SIGN, SYMCRYPT_FLAG_RSAKEY_ENCRYPT);
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptRsaSignVerifyPct( PCSYMCRYPT_RSAKEY pkRsakey );
+//
+// FIPS pairwise consistency test for RSA sign/verify.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptDsaPct( PCSYMCRYPT_DLKEY pkDlkey );
+//
+// FIPS pairwise consistency test for DSA sign/verify.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcDsaPct( PCSYMCRYPT_ECKEY pkEckey );
+//
+// FIPS pairwise consistency test for ECDSA sign/verify.
+//
+
+typedef struct _SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS {
+    SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE eDhSafePrimeType;
+
+    PCBYTE  pcbPrimeP;
+
+    UINT32  nBitsOfP;           // nBitsOfQ == nBitsOfP-1
+    UINT32  nMinBitsPriv;       // nMinBitsPriv == 2s
+                                // s is the maximum security strength supported by the group based on SP800-56Arev3
+    UINT32  nDefaultBitsPriv;   // nBitsOfQ >= nDefaultBitsPriv >= nMinBitsPriv
+                                // nDefaultBitsPriv will be the default value of nBitsPriv for a Dlkey in this Dlgroup
+                                // nBitsPriv is the maximum length of the private key
+} SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS;
+typedef const SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS * PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS;
+//
+// SYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS is used to specify all the parameters needed for creation
+// of a Dlgroup based on a safe-prime group (i.e. p = 2q+1, and g = 2).
+// Currently this is used exclusively internally, and the interface for explicitly specifying use of
+// safe-prime group in SymCrypt is to use
+
+// Internally supported Safe Prime groups
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp2048;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp3072;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp4096;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp6144;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsModp8192;
+
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe2048;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe3072;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe4096;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe6144;
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptDlgroupDhSafePrimeParamsffdhe8192;
+
+#define SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT (10)
+
+// Note, we rely on the ordering of the parameters from smallest to largest within each named set of
+// safe-prime groups as we iterate through them assuming this order in SymCryptDlgroupSetValueSafePrime
+extern const PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS SymCryptNamedSafePrimeGroups[SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT];
+
+//
+// Definitions for ECurve dispatch functions
+//
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_SET_ZERO_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_SET_DISTINGUISHED_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_SET_RANDOM_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_INT       piScalar,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef UINT32 (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_ISEQUAL_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+typedef UINT32 (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_ONCURVE_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef UINT32 (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_ISZERO_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_ADD_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_ADD_DIFF_NONZERO_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_DOUBLE_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_NEGATE_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef SYMCRYPT_ERROR (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_SCALAR_MUL_FUNC) (
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _In_    PCSYMCRYPT_INT          piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT      poSrc,
+            UINT32                  flags,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+typedef SYMCRYPT_ERROR (SYMCRYPT_CALL * PSYMCRYPT_ECPOINT_MULTI_SCALAR_MUL_FUNC) (
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_reads_( nPoints )           PCSYMCRYPT_INT *        piSrcScalarArray,
+    _In_reads_( nPoints )           PCSYMCRYPT_ECPOINT *    poSrcEcpointArray,
+                                    UINT32                  nPoints,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch  );
+
+typedef VOID (SYMCRYPT_CALL * PSYMCRYPT_ECURVE_FILL_SCRATCH_SPACES_FUNC) (
+    _Inout_    PSYMCRYPT_ECURVE pCurve );
+
+
+typedef struct _SYMCRYPT_ECURVE_FUNCTIONS
+{
+    PSYMCRYPT_ECPOINT_SET_ZERO_FUNC             setZeroFunc;
+    PSYMCRYPT_ECPOINT_SET_DISTINGUISHED_FUNC    setDistinguishedFunc;
+    PSYMCRYPT_ECPOINT_SET_RANDOM_FUNC           setRandomFunc;
+    PSYMCRYPT_ECPOINT_ISEQUAL_FUNC              isEqualFunc;
+    PSYMCRYPT_ECPOINT_ISZERO_FUNC               isZeroFunc;
+    PSYMCRYPT_ECPOINT_ONCURVE_FUNC              onCurveFunc;
+    PSYMCRYPT_ECPOINT_ADD_FUNC                  addFunc;
+    PSYMCRYPT_ECPOINT_ADD_DIFF_NONZERO_FUNC     addDiffFunc;
+    PSYMCRYPT_ECPOINT_DOUBLE_FUNC               doubleFunc;
+    PSYMCRYPT_ECPOINT_NEGATE_FUNC               negateFunc;
+    PSYMCRYPT_ECPOINT_SCALAR_MUL_FUNC           scalarMulFunc;
+    PSYMCRYPT_ECPOINT_MULTI_SCALAR_MUL_FUNC     multiScalarMulFunc;
+    PSYMCRYPT_ECURVE_FILL_SCRATCH_SPACES_FUNC   fillScratchSpacesFunc;
+    PVOID                                       slack[3];
+} SYMCRYPT_ECURVE_FUNCTIONS, *PSYMCRYPT_ECURVE_FUNCTIONS;
+typedef const SYMCRYPT_ECURVE_FUNCTIONS  *PCSYMCRYPT_ECURVE_FUNCTIONS;
+
+#define SYMCRYPT_ECURVE_FUNCTIONS_SIZE    (sizeof( SYMCRYPT_ECURVE_FUNCTIONS ) )
+
+// Check that the size is a power of 2
+C_ASSERT( (SYMCRYPT_ECURVE_FUNCTIONS_SIZE & (SYMCRYPT_ECURVE_FUNCTIONS_SIZE-1)) == 0 );
+
+//
+// Functions for the each type of curve
+//
+
+//--------------------------------------------------------
+//--------- Short Weierstrass ----------------------------
+//--------------------------------------------------------
+
+extern const PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION SymCryptEcurveParamsV2ExtensionShortWeierstrass;
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassFillScratchSpaces( _In_ PSYMCRYPT_ECURVE pCurve );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassSetZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassSetDistinguished(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptShortWeierstrassIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptShortWeierstrassIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptShortWeierstrassOnCurve(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassAdd(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassAddDiffNonZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassDouble(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassNegate(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptShortWeierstrassDoubleSpecializedAm3(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+//--------------------------------------------------------
+//--------- Twisted Edwards ------------------------------
+//--------------------------------------------------------
+
+extern const PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION SymCryptEcurveParamsV2ExtensionTwistedEdwards;
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsFillScratchSpaces( _In_ PSYMCRYPT_ECURVE pCurve );
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsSetDistinguished(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsAdd(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsAddDiffNonZero(
+     _In_   PCSYMCRYPT_ECURVE   pCurve,
+     _In_   PCSYMCRYPT_ECPOINT  poSrc1,
+     _In_   PCSYMCRYPT_ECPOINT  poSrc2,
+     _Out_  PSYMCRYPT_ECPOINT   poDst,
+     _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsDouble(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+UINT32
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+UINT32
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsOnCurve(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+UINT32
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsSetZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+VOID
+SYMCRYPT_CALL
+SymCryptTwistedEdwardsNegate(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Inout_ PSYMCRYPT_ECPOINT   poSrc,
+            UINT32              mask,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+//--------------------------------------------------------
+//--------- Montgomery -----------------------------------
+//--------------------------------------------------------
+
+extern const PCSYMCRYPT_ECURVE_PARAMS_V2_EXTENSION SymCryptEcurveParamsV2ExtensionMontgomery;
+
+VOID
+SYMCRYPT_CALL
+SymCryptMontgomeryFillScratchSpaces( _In_ PSYMCRYPT_ECURVE pCurve );
+
+VOID
+SYMCRYPT_CALL
+SymCryptMontgomerySetDistinguished(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _Out_   PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMontgomeryIsEqual(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc1,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc2,
+            UINT32              flags,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch);
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMontgomeryIsZero(
+    _In_    PCSYMCRYPT_ECURVE   pCurve,
+    _In_    PCSYMCRYPT_ECPOINT  poSrc,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMontgomeryPointScalarMul(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _In_    PCSYMCRYPT_INT          piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT      poSrc,
+            UINT32                  flags,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+//--------------------------------------------------------
+//--------- Generic multiplication-related functions -----
+//--------------------------------------------------------
+
+VOID
+SYMCRYPT_CALL
+SymCryptOfflinePrecomputation(
+    _In_ PSYMCRYPT_ECURVE pCurve,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE         pbScratch,
+            SIZE_T        cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointScalarMulFixedWindow(
+    _In_    PCSYMCRYPT_ECURVE       pCurve,
+    _In_    PCSYMCRYPT_INT          piScalar,
+    _In_opt_
+            PCSYMCRYPT_ECPOINT      poSrc,
+            UINT32                  flags,
+    _Out_   PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE               pbScratch,
+            SIZE_T              cbScratch );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEcpointMultiScalarMulWnafWithInterleaving(
+    _In_                            PCSYMCRYPT_ECURVE       pCurve,
+    _In_reads_( nPoints )           PCSYMCRYPT_INT *        piSrcScalarArray,
+    _In_reads_( nPoints )           PCSYMCRYPT_ECPOINT *    poSrcEcpointArray,
+                                    UINT32                  nPoints,
+                                    UINT32                  flags,
+    _Out_                           PSYMCRYPT_ECPOINT       poDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
+                                    SIZE_T                  cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcpointGenericSetRandom(
+    _In_                            PCSYMCRYPT_ECURVE   pCurve,
+    _Out_                           PSYMCRYPT_INT       piScalar,
+    _Out_                           PSYMCRYPT_ECPOINT   poDst,
+    _Out_writes_bytes_( cbScratch ) PBYTE               pbScratch,
+                                    SIZE_T              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptEcurveFillScratchSpaces(
+    _Inout_    PSYMCRYPT_ECURVE   pCurve);
+//--------------------------------------------------------
+//--------------------------------------------------------
+
+// Table with the number of field elements for each point format (in ecpoint.c)
+extern const UINT32 SymCryptEcpointFormatNumberofElements[4];
+
+UINT32
+SYMCRYPT_CALL
+SymCryptSizeofEcpointEx(
+    UINT32 cbModElement,
+    UINT32 numOfCoordinates );
+
+
+PCSYMCRYPT_TRIALDIVISION_CONTEXT
+SYMCRYPT_CALL
+SymCryptFdefCreateTrialDivisionContext( UINT32 nDigits );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptFdefIntFindSmallDivisor(
+    _In_                            PCSYMCRYPT_TRIALDIVISION_CONTEXT    pContext,
+    _In_                            PCSYMCRYPT_INT                      piSrc,
+    _Out_writes_bytes_( cbScratch ) PBYTE                               pbScratch,
+                                    SIZE_T                              cbScratch );
+
+VOID
+SYMCRYPT_CALL
+SymCryptFdefFreeTrialDivisionContext( PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext );
+
+UINT64
+SymCryptInverseMod2e64( UINT64 m );
+
+
+//--------------------------------------------------------
+//--------------------------------------------------------
+
+// Helper function for wiping the Ec key's private state (e.g. for use in layers such as composites)
+VOID
+SYMCRYPT_CALL
+SymCryptEckeyWipePrivateState(
+    _Inout_ PSYMCRYPT_ECKEY pkEckey );
+
+// Recoding algorithms
+VOID
+SYMCRYPT_CALL
+SymCryptFixedWindowRecoding(
+            UINT32          W,
+    _Inout_ PSYMCRYPT_INT   piK,
+    _Inout_ PSYMCRYPT_INT   piTmp,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         absofKIs,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         sigofKIs,
+            UINT32          nRecodedDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptWidthNafRecoding(
+            UINT32          W,
+    _Inout_ PSYMCRYPT_INT   piK,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         absofKIs,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         sigofKIs,
+            UINT32          nRecodedDigits );
+
+VOID
+SYMCRYPT_CALL
+SymCryptPositiveWidthNafRecoding(
+            UINT32          W,
+    _In_    PCSYMCRYPT_INT  piK,
+            UINT32          nBitsExp,
+    _Out_writes_( nRecodedDigits )
+            PUINT32         absofKIs,
+            UINT32          nRecodedDigits );
+
+// M-LWE: Module Learning-With-Errors (ML-KEM, ML-DSA)
+//
+// ML-KEM (also known as Kyber) and ML-DSA (also known as Dilithium) are Post-Quantum algorithms
+// based on the Learning-With-Errors problem over Module Lattices (or the hardness of the M-LWE
+// problem).
+//
+// A Module is a Vector Space over a Ring. That is, elements of the vector spaces are elements in
+// the underlying ring.
+// We refer to Module as MLWE in the below types to avoid naming confusion with Module as in
+// "FIPS module". Though technically components acting on MLWE types could be used outside of the
+// MLWE problem, these types are SymCrypt-internal, and are only currently intended for use in
+// these MLWE-based algorithms.
+//
+// In ML-KEM and ML-DSA, Polynomial Rings are used. That is, a ring defined over polynomials.
+// For both schemes, the polynomial ring is defined modulo the polynomial (X^256 + 1). This means
+// there is a representative of each polynomial ring element with 256 coefficients
+// (c_255*X^255 + c_254*X^254 + ... + c_0). The coefficients themselves are modulo a small prime
+// in both schemes. For ML-KEM the small prime is 3329 (12-bits), and for ML-DSA the small prime
+// is 8380417 (23-bits).
+// Additionally, for both schemes there is a Number Theoretic Transform (NTT) which maps polynomial
+// ring elements to a corresponding ring for efficient multiplication.
+// The in-memory representation of a polynomial ring element uses the same struct regardless of
+// whether it is in standard form, or the NTT form. For brevity we tend to refer to polynomial
+// ring elements as PolyElements.
+//
+#define SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS (256)
+
+// MLWE internal function definitions are in their own headers
+#include "sc_lib_mlkem.h"
+#include "sc_lib_mldsa.h"
+
+//
+// Common Composite Definitions
+//
+
+typedef enum {
+    SYMCRYPT_CACHED_ECURVE_ID_NIST_P256 = 0,
+    SYMCRYPT_CACHED_ECURVE_ID_NIST_P384,
+    SYMCRYPT_CACHED_ECURVE_ID_CURVE_25519,
+    SYMCRYPT_CACHED_ECURVE_ID_COUNT
+} SYMCRYPT_CACHED_ECURVE_ID, *PSYMCRYPT_CACHED_ECURVE_ID;
+
+PCSYMCRYPT_ECURVE
+SYMCRYPT_CALL
+SymCryptGetCachedEcurve(
+    SYMCRYPT_CACHED_ECURVE_ID   curveId );
+
+#define SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PUBLIC_KEY_P256        (65)
+#define SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PUBLIC_KEY_P384        (97)
+#define SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PUBLIC_KEY_CURVE_25519 (32)
+
+#define SYMCRYPT_COMPOSITE_SIZEOF_MAX_ENCODED_EC_PUBLIC_KEY SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PUBLIC_KEY_P384
+
+#define SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PRIVATE_KEY_P256           (51)
+#define SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PRIVATE_KEY_P384           (64)
+#define SYMCRYPT_COMPOSITE_SIZEOF_ENCODED_EC_PRIVATE_KEY_CURVE_25519    (32)
+
+UINT32
+SYMCRYPT_CALL
+SymCryptCompositeGetSizeOfEncodedEcSk(
+    SYMCRYPT_CACHED_ECURVE_ID   curveId );
+
+UINT32
+SYMCRYPT_CALL
+SymCryptCompositeGetSizeOfEncodedEcPk(
+    SYMCRYPT_CACHED_ECURVE_ID   curveId );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyGetValueCompositeEncodingSk(
+    _In_                        PCSYMCRYPT_ECKEY            pEckey,
+                                SYMCRYPT_CACHED_ECURVE_ID   curveId,
+    _Out_writes_bytes_( cbDst ) PBYTE                       pbDst,
+                                SIZE_T                      cbDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeyGetValueCompositeEncodingPk(
+    _In_                        PCSYMCRYPT_ECKEY            pEckey,
+                                SYMCRYPT_CACHED_ECURVE_ID   curveId,
+    _Out_writes_bytes_( cbDst ) PBYTE                       pbDst,
+                                SIZE_T                      cbDst );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeySetValueCompositeEncodingPk(
+    _In_                        SYMCRYPT_CACHED_ECURVE_ID   curveId,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                UINT32                      flags,
+    _Inout_                     PSYMCRYPT_ECKEY             pEckey );
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptEckeySetValueCompositeEncodingSk(
+                                SYMCRYPT_CACHED_ECURVE_ID   curveId,
+    _In_reads_bytes_( cbSrc )   PCBYTE                      pbSrc,
+                                SIZE_T                      cbSrc,
+                                UINT32                      flags,
+    _Inout_                     PSYMCRYPT_ECKEY             pEckey );
+
+//
+// Composite ML-KEM definitions
+//
+
+typedef struct _SYMCRYPT_COMPOSITE_MLKEM_INTERNAL_PARAMS {
+    SYMCRYPT_COMPOSITE_MLKEM_PARAMS     params;
+    SYMCRYPT_CACHED_ECURVE_ID           ecurveId;
+    SYMCRYPT_MLKEM_PARAMS               mlKemParams;
+    PCBYTE                              pbLabel;
+    SIZE_T                              cbLabel;
+    SIZE_T                              cbCiphertext;
+    SYMCRYPT_NUMBER_FORMAT              numFormat;
+    SYMCRYPT_ECPOINT_FORMAT             ecPointFormat;
+    SIZE_T                              cbExpandedSeed;
+    SIZE_T                              cbEncodedPrivateKey;
+    SIZE_T                              cbEncodedPublicKey;
+} SYMCRYPT_COMPOSITE_MLKEM_INTERNAL_PARAMS, *PSYMCRYPT_COMPOSITE_MLKEM_INTERNAL_PARAMS;
+typedef const SYMCRYPT_COMPOSITE_MLKEM_INTERNAL_PARAMS *PCSYMCRYPT_COMPOSITE_MLKEM_INTERNAL_PARAMS;
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_COMPOSITE_MLKEMKEY {
+    PCSYMCRYPT_COMPOSITE_MLKEM_INTERNAL_PARAMS  pParams;    // pointer to internal params for Composite ML-KEM being used
+    PSYMCRYPT_MLKEMKEY                          pkMlKemkey;
+    PSYMCRYPT_ECKEY                             pkEcKey;    // all composite keys with the same elliptic curve type
+                                                            // share the same lazily allocated curve object. This
+                                                            // avoids the overhead of setting up a new curve object per key.
+
+    BOOLEAN                                     hasPrivateSeed;
+    BYTE                                        privateSeed[SYMCRYPT_COMPOSITE_MLKEM_IRTF_PRIVATE_SEED_SIZE];
+
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_COMPOSITE_MLKEMKEY, *PSYMCRYPT_COMPOSITE_MLKEMKEY;
+typedef const SYMCRYPT_COMPOSITE_MLKEMKEY *PCSYMCRYPT_COMPOSITE_MLKEMKEY;
+
+// Rejection sampling for generating an EC scalar from an IRTF Composite ML-KEM seed
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptCompositeMlKemGetRandomScalarForEcKeyEx(
+                                    SYMCRYPT_CACHED_ECURVE_ID   ecurveId,
+                                    SYMCRYPT_NUMBER_FORMAT      numFormat,
+    _In_reads_bytes_( cbSeed )      PCBYTE                      pbSeed,
+                                    SIZE_T                      cbSeed,
+    _Out_writes_bytes_( cbScalar )  PBYTE                       pbScalar,
+                                    SIZE_T                      cbScalar );
+
+//
+// XMSS
+//
+
+//
+// ADRS structure definitions as specified in RFC 8391
+//
+typedef enum _XMSS_ADRS_TYPE
+{
+    XMSS_ADRS_TYPE_OTS          = 0,
+    XMSS_ADRS_TYPE_LTREE        = 1,
+    XMSS_ADRS_TYPE_HASH_TREE    = 2,
+} XMSS_ADRS_TYPE;
+
+typedef struct _XMSS_OTS_ADDRESS
+{
+    BYTE  en32Leaf[4];
+    BYTE  en32Chain[4];
+    BYTE  en32Hash[4];
+} XMSS_OTS_ADDRESS, *PXMSS_OTS_ADDRESS;
+
+typedef struct _XMSS_LTREE_ADDRESS
+{
+    BYTE  en32Leaf[4];
+    BYTE  en32Height[4];
+    BYTE  en32Index[4];
+} XMSS_LTREE_ADDRESS, * PXMSS_LTREE_ADDRESS;
+
+typedef struct _XMSS_HASHTREE_ADDRESS
+{
+    BYTE  padding[4];
+    BYTE  en32Height[4];
+    BYTE  en32Index[4];
+} XMSS_HASHTREE_ADDRESS, * PXMSS_HASHTREE_ADDRESS;
+
+typedef struct _XMSS_ADRS
+{
+    BYTE  en32Layer[4];
+    BYTE  en64Tree[8];
+    BYTE  en32Type[4];
+
+    union {
+        XMSS_OTS_ADDRESS        ots;
+        XMSS_LTREE_ADDRESS      ltree;
+        XMSS_HASHTREE_ADDRESS   hashtree;
+    } u;
+
+    BYTE  en32KeyAndMask[4];
+
+} XMSS_ADRS, *PXMSS_ADRS;
+
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_XMSS_KEY
+{
+    UINT32  version;
+
+    SYMCRYPT_XMSS_PARAMS params;
+
+    SYMCRYPT_XMSSKEY_TYPE keyType;
+
+    // Public key
+    BYTE    Root[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE    Seed[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+
+    SYMCRYPT_MAGIC_FIELD
+
+    // Private key
+    SYMCRYPT_ALIGN_AT(16) UINT64  Idx;  // Aligning on 16-bytes to suppress clang warning
+                                        // when atomic increment is performed on it.
+    BYTE    SkXmss[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE    SkPrf[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+
+} SYMCRYPT_XMSS_KEY;
+
+typedef SYMCRYPT_XMSS_KEY* PSYMCRYPT_XMSS_KEY;
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssComputePublicRoot(
+    _In_                            PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_reads_bytes_( cbSeed )      PCBYTE                  pbSeed,
+                                    SIZE_T                  cbSeed,
+    _In_reads_bytes_( cbSkXmss )    PCBYTE                  pbSkXmss,
+                                    SIZE_T                  cbSkXmss,
+    _Out_writes_bytes_( cbRoot )    PBYTE                   pbRoot,
+                                    SIZE_T                  cbRoot );
+//
+//  Compute public root value from SEED and SK_XMSS
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeyVerifyRoot(
+    _In_    PCSYMCRYPT_XMSS_KEY pKey );
+//
+// Verifies that the public root matches the private key by recomputing it
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssVerifyInternal(
+    _Inout_                         PSYMCRYPT_XMSS_KEY  pKey,
+    _In_reads_bytes_( cbMessage )   PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_( cbSignature ) PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature );
+//
+// The function that actually does the signature verification. This one doesn't
+// run the self-tests so that it can be called from the self-test function.
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHbsGetWinternitzLengths(
+            UINT32  n,      // data size in bytes
+            UINT32  w,      // digit length in bits (Winternitz coefficient)
+    _Out_   PUINT32 puLen1, // number of w-bit digits in n
+    _Out_   PUINT32 puLen2  // number of w-bit digits to store the checksum len1 * (2^w - 1)
+    );
+
+typedef struct _SYMCRYPT_TREEHASH_NODE
+{
+    UINT32  index;
+    UINT32  height;
+    BYTE    value[SYMCRYPT_ANYSIZE_ARRAY];
+} SYMCRYPT_TREEHASH_NODE, * PSYMCRYPT_TREEHASH_NODE;
+
+#define SYMCRYPT_SIZEOF_TREEHASH_NODE(cbValue) (sizeof(SYMCRYPT_TREEHASH_NODE) - 1 + (cbValue))
+
+#define SYMCRYPT_TREEHASH_NODE_GET(aNodes, cbValue, i) ((PSYMCRYPT_TREEHASH_NODE)((PBYTE)(aNodes) + (i) * SYMCRYPT_SIZEOF_TREEHASH_NODE(cbValue)))
+
+
+typedef struct _SYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT
+{
+    PCSYMCRYPT_XMSS_PARAMS  pParams;
+    PCBYTE                  pbSeed;
+    XMSS_ADRS               adrs;
+
+} SYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT, * PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT;
+
+
+typedef
+VOID
+(SYMCRYPT_CALL *PSYMCRYPT_INCREMENTAL_TREEHASH_FUNC)(
+    _In_    PSYMCRYPT_TREEHASH_NODE pNodeLeft,
+    _In_    PSYMCRYPT_TREEHASH_NODE pNodeRight,
+    _Out_   PSYMCRYPT_TREEHASH_NODE pNodeOut,
+    _Inout_ PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT pContext );
+
+
+typedef struct _SYMCRYPT_INCREMENTAL_TREEHASH
+{
+    UINT32 cbNode;      // node size; height + hash result
+    UINT32 nSize;       // current size of the stack
+    UINT32 nCapacity;   // maximum items
+    UINT32 nLastLeafIndex;
+    PSYMCRYPT_INCREMENTAL_TREEHASH_FUNC funcCompressNodes;
+    PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT pContext;
+
+    SYMCRYPT_TREEHASH_NODE arrNodes[SYMCRYPT_ANYSIZE_ARRAY];
+
+} SYMCRYPT_INCREMENTAL_TREEHASH, *PSYMCRYPT_INCREMENTAL_TREEHASH;
+
+
+PSYMCRYPT_INCREMENTAL_TREEHASH
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashInit(
+    UINT32  nLeaves,
+    PBYTE   pbBuffer,
+    SIZE_T  cbBuffer,
+    UINT32  cbHashResult,
+    PSYMCRYPT_INCREMENTAL_TREEHASH_FUNC funcCompressNodes,
+    PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT pContext);
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashGetNode(
+    _In_ PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash,
+         SIZE_T                         index );
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashAllocNode(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash,
+            UINT32                          nLeafIndex );
+
+VOID
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashGetTopNodes(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash,
+    _Out_   PSYMCRYPT_TREEHASH_NODE         *ppNodeLeft,
+    _Out_   PSYMCRYPT_TREEHASH_NODE         *ppNodeRight );
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashProcessCommon(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash,
+            BOOLEAN                         fFinal );
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashProcess(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash);
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashFinalize(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash);
+
+UINT32
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashStackDepth(
+    UINT32 nLeaves);
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHbsSizeofScratchBytesForIncrementalTreehash(
+    UINT32  cbNode,
+    UINT32  nLeaves);
+
+UINT32
+SYMCRYPT_CALL
+SymCryptHbsGetDigit(
+            UINT32 width,
+    _In_    PCBYTE pbBuffer,
+            SIZE_T cbBuffer,
+            UINT32 index);
+
+//
+// LMS
+//
+#define SYMCRYPT_IS_VALID_WINTERNITZ_WIDTH(w) ( ((w) == 1) || ((w) == 2) || ((w) == 4) || ((w) == 8) )
+#define SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE   16
+#define SYMCRYPT_LMS_MAX_N                      32
+#define SYMCRYPT_LMS_MAX_P                      265
+#define SYMCRYPT_LMS_MAX_H                      25
+#define SYMCRYPT_LMS_MAX_CUSTOM_TREE_HEIGHT     31
+#define SYMCRYPT_LMS_CHECKSUM_SIZE              16
+
+// LmsAlgId || LmsOtsAlgId ||  I  || RootNode
+#define SYMCRYPT_LMS_PUB_KEY_SIZE(cbHashOutput) (8 + SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE + cbHashOutput)
+
+// LmsAlgId || LmsOtsAlgId ||  I  || RootNode || NextUnusedLeaf || Seed
+#define SYMCRYPT_LMS_PRIV_KEY_SIZE(cbHashOutput) (SYMCRYPT_LMS_PUB_KEY_SIZE(cbHashOutput) + sizeof(UINT32) + cbHashOutput)
+
+//==========================================================================
+//   LMS internal structures
+//==========================================================================
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_LMS_KEY{
+    SIZE_T cbSize;
+    SYMCRYPT_LMS_PARAMS     params;
+
+    // Leaf number of the next LM-OTS private key that has not yet been used
+    UINT64                  nNextUnusedLeaf;
+
+    // The key type, can be: SYMCRYPT_LMSKEY_TYPE_PUBLIC, or SYMCRYPT_LMSKEY_TYPE_PRIVATE
+    UINT32                  keyType;
+
+    // Key identifier
+    BYTE                    abId[SYMCRYPT_LMS_KEY_PAIR_IDENTIFIER_SIZE];
+
+    // Public key root
+    BYTE                    abPublicRoot[SYMCRYPT_LMS_MAX_N];
+
+    // Private key seed
+    BYTE                    abSeed[SYMCRYPT_LMS_MAX_N];
+
+    SYMCRYPT_MAGIC_FIELD
+} SYMCRYPT_LMS_KEY;
+typedef         SYMCRYPT_LMS_KEY* PSYMCRYPT_LMS_KEY;
+typedef const   SYMCRYPT_LMS_KEY* PCSYMCRYPT_LMS_KEY;
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptLmsVerifyInternal(
+    _In_                            PCSYMCRYPT_LMS_KEY  pKey,
+    _In_reads_bytes_(cbMessage)     PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_(cbSignature)   PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature);
+//
+// This function carries out the actual LMS verification process. It's essential to prevent an infinite
+// recursive call in SymCryptLmsVerifySelftest.
+//
+
+
+// Atomics.
+//
+// We define all our SymCrypt atomics below. Different compilers/environments have different
+// intrinsics to handle atomics in different environments.
+//
+// The SymCrypt atomics take the form SYMCRYPT_ATOMIC_<Operation><Bitsize>_<Return>_<Ordering>
+//
+// <Operation> is the atomic operation (i.e. LOAD, OR, XOR, AND, ADD, INC, etc.)
+// <Bitsize> indicates the bitsize of the values that the atomic operation operates on. Pointers to
+// values which atomics operate on must be aligned to the size of the value.
+// <Return> takes the value PRE or POST, indicating whether the return value of the atomic is the
+// value of the destination before (PRE) or after (POST) the operation was performed. Not used when
+// operation is LOAD!
+// <Ordering> specifies the memory ordering of the atomic operation in relation to other loads/stores
+// and can take one of the following values:
+//   RELAXED corresponds to relaxed memory ordering in C++11
+//   SEQ_CST corresponds to sequentially consistent memory ordering in C++11
+//   ACQUIRE corresponds to acquire memory ordering in C++11
+//   RELEASE corresponds to release memory ordering in C++11
+//
+
+#if SYMCRYPT_PLATFORM_WINDOWS
+#include <intrin.h>
+
+#if SYMCRYPT_CPU_ARM64
+// 64b loads are naturally atomic on Arm64
+#define SYMCRYPT_ATOMIC_LOAD64_RELAXED(_dest)           SYMCRYPT_FORCE_READ64(_dest)
+#define SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(_dest, _val)   _InterlockedOr_nf( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD32_PRE_RELAXED(_dest, _val)  _InterlockedExchangeAdd_nf( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(_dest, _val) _InterlockedAdd64_nf( (volatile LONG64 *)(_dest), (LONG64)(_val) )
+
+#define SYMCRYPT_ATOMIC_ADD32_POST_SEQ_CST(_dest, _val) _InterlockedAdd( (volatile LONG *)(_dest), (LONG)(_val) )
+
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          ((PVOID)__ldar64( (volatile UINT64 *)(_dest) ))
+#define SYMCRYPT_ATOMIC_STOREPTR_RELEASE(_dest, _val)   __stlr64( (volatile UINT64 *)(_dest), (UINT64)(_val) )
+
+// For ARM/ARM64, MSVC does not have a dedicated acquire-release CAS intrinsic.
+#define SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE( _dest, _exchange, _comp ) \
+    _InterlockedCompareExchangePointer( (volatile PVOID *)(_dest), (PVOID)(_exchange), (PVOID)(_comp) )
+
+#elif SYMCRYPT_CPU_ARM
+#define SYMCRYPT_ATOMIC_LOAD64_RELAXED(_dest)           _InterlockedOr64_nf( (volatile LONG64 *)(_dest), 0 )
+#define SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(_dest, _val)   _InterlockedOr_nf( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD32_PRE_RELAXED(_dest, _val)  _InterlockedExchangeAdd_nf( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(_dest, _val) _InterlockedAdd64_nf( (volatile LONG64 *)(_dest), (LONG64)(_val) )
+
+#define SYMCRYPT_ATOMIC_ADD32_POST_SEQ_CST(_dest, _val) _InterlockedAdd( (volatile LONG *)(_dest), (LONG)(_val) )
+
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          ((PVOID)_InterlockedOr_acq( (volatile LONG *)(_dest), 0 ))
+#define SYMCRYPT_ATOMIC_STOREPTR_RELEASE(_dest, _val)   _InterlockedExchangePointer_rel( (volatile PVOID *)(_dest), (PVOID)(_val) )
+
+#define SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE( _dest, _exchange, _comp ) \
+    _InterlockedCompareExchangePointer( (volatile PVOID *)(_dest), (PVOID)(_exchange), (PVOID)(_comp) )
+
+#elif SYMCRYPT_CPU_AMD64
+// For MSVC on AMD64, there are no _nf atomic intrinsics
+// 64b loads are naturally atomic on AMD64
+#define SYMCRYPT_ATOMIC_LOAD64_RELAXED(_dest)           SYMCRYPT_FORCE_READ64(_dest)
+#define SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(_dest, _val)   _InterlockedOr( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD32_PRE_RELAXED(_dest, _val)  _InterlockedExchangeAdd( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(_dest, _val) (_InterlockedExchangeAdd64( (volatile LONG64 *)(_dest), (LONG64)(_val) ) + (LONG64)(_val))
+
+#define SYMCRYPT_ATOMIC_ADD32_POST_SEQ_CST(_dest, _val) (_InterlockedExchangeAdd( (volatile LONG *)(_dest), (LONG)(_val) ) + (LONG)(_val))
+
+// Volatile load / store are sufficient for acquire-release semantics on AMD64
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          ((PVOID)SYMCRYPT_FORCE_READ64(_dest))
+#define SYMCRYPT_ATOMIC_STOREPTR_RELEASE(_dest, _val)   SYMCRYPT_FORCE_WRITE64(_dest, ((UINT64)(_val)))
+
+#define SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE( _dest, _exchange, _comp ) \
+    _InterlockedCompareExchangePointer( (volatile PVOID *)(_dest), (PVOID)(_exchange), (PVOID)(_comp) )
+
+#elif SYMCRYPT_CPU_X86
+// For MSVC on x86, there is no 64b atomic load intrinsic - use expected to fail CAS, attempting to set from 0 to 0
+#define SYMCRYPT_ATOMIC_LOAD64_RELAXED(_dest)           _InterlockedCompareExchange64( (volatile LONG64 *)(_dest), 0, 0 )
+// For MSVC on x86, there are no _nf atomic intrinsics
+#define SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(_dest, _val)   _InterlockedOr( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD32_PRE_RELAXED(_dest, _val)  _InterlockedExchangeAdd( (volatile LONG *)(_dest), (LONG)(_val) )
+// For MSVC on x86, there is no 64b atomic add intrinsic
+// We could use InterlockedAdd64 function from windows.h if we are using MSVC for Windows, but
+// to remove dependency we just define our own inline function using _InterlockedCompareExchange64
+FORCEINLINE
+LONG64
+SymCryptInlineInterlockedAdd64( volatile LONG64* destination, LONG64 value )
+{
+    LONG64 preValue;
+    do {
+        preValue = *destination;
+    } while (_InterlockedCompareExchange64(destination, preValue + value, preValue) != preValue);
+
+    return preValue + value;
+}
+#define SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(_dest, _val) SymCryptInlineInterlockedAdd64( (volatile LONG64 *)(_dest), (LONG64)(_val) )
+
+#define SYMCRYPT_ATOMIC_ADD32_POST_SEQ_CST(_dest, _val) (_InterlockedExchangeAdd( (volatile LONG *)(_dest), (LONG)(_val) ) + (LONG)(_val))
+
+// Volatile load / store are sufficient for acquire-release semantics on x86
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          ((PVOID)SYMCRYPT_FORCE_READ32(_dest))
+#define SYMCRYPT_ATOMIC_STOREPTR_RELEASE(_dest, _val)   SYMCRYPT_FORCE_WRITE32(_dest, ((UINT32)(_val)))
+
+#define SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE( _dest, _exchange, _comp ) \
+    _InterlockedCompareExchangePointer( (volatile PVOID *)(_dest), (PVOID)(_exchange), (PVOID)(_comp) )
+
+#else
+
+// Fallback intended to generically work across all supported platforms for cases where
+// we do not make decisions based on CPU architecture, such as no ASM builds. For the most
+// part the same as x86 except in cases where the underlying definition relies on pointer size.
+
+#define SYMCRYPT_ATOMIC_LOAD64_RELAXED(_dest)           _InterlockedCompareExchange64( (volatile LONG64 *)(_dest), 0, 0 )
+#define SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(_dest, _val)   _InterlockedOr( (volatile LONG *)(_dest), (LONG)(_val) )
+#define SYMCRYPT_ATOMIC_ADD32_PRE_RELAXED(_dest, _val)  _InterlockedExchangeAdd( (volatile LONG *)(_dest), (LONG)(_val) )
+
+FORCEINLINE
+LONG64
+SymCryptInlineInterlockedAdd64( volatile LONG64* destination, LONG64 value )
+{
+    LONG64 preValue;
+    do {
+        preValue = *destination;
+    } while (_InterlockedCompareExchange64(destination, preValue + value, preValue) != preValue);
+
+    return preValue + value;
+}
+#define SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(_dest, _val) SymCryptInlineInterlockedAdd64( (volatile LONG64 *)(_dest), (LONG64)(_val) )
+
+#define SYMCRYPT_ATOMIC_ADD32_POST_SEQ_CST(_dest, _val) (_InterlockedExchangeAdd( (volatile LONG *)(_dest), (LONG)(_val) ) + (LONG)(_val))
+
+#if defined(_WIN64)
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          ((PVOID)_InterlockedOr64( (volatile LONG64 *)(_dest), 0 ))
+#else
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          ((PVOID)_InterlockedOr( (volatile LONG *)(_dest), 0 ))
+#endif
+
+#define SYMCRYPT_ATOMIC_STOREPTR_RELEASE(_dest, _val)   _InterlockedExchangePointer( (volatile PVOID *)(_dest), (PVOID)(_val) )
+
+#define SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE( _dest, _exchange, _comp ) \
+    _InterlockedCompareExchangePointer( (volatile PVOID *)(_dest), (PVOID)(_exchange), (PVOID)(_comp) )
+
+#endif
+
+#elif SYMCRYPT_GNUC
+#define SYMCRYPT_ATOMIC_LOAD64_RELAXED(_dest)           __atomic_load_n( (volatile uint64_t *)(_dest), __ATOMIC_RELAXED )
+#define SYMCRYPT_ATOMIC_OR32_PRE_RELAXED(_dest, _val)   __atomic_fetch_or( (volatile uint32_t *)(_dest), (uint32_t)(_val), __ATOMIC_RELAXED )
+#define SYMCRYPT_ATOMIC_ADD32_PRE_RELAXED(_dest, _val)  __atomic_fetch_add( (volatile uint32_t *)(_dest), (uint32_t)(_val), __ATOMIC_RELAXED )
+#define SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(_dest, _val) __atomic_add_fetch( (volatile uint64_t *)(_dest), (uint64_t)(_val), __ATOMIC_RELAXED )
+
+#define SYMCRYPT_ATOMIC_ADD32_POST_SEQ_CST(_dest, _val) __atomic_add_fetch( (volatile uint32_t *)(_dest), (uint32_t)(_val), __ATOMIC_ACQ_REL )
+
+#define SYMCRYPT_ATOMIC_LOADPTR_ACQUIRE(_dest)          __atomic_load_n( (volatile void* *)(_dest), __ATOMIC_ACQUIRE )
+#define SYMCRYPT_ATOMIC_STOREPTR_RELEASE(_dest, _val)   __atomic_store_n( (volatile void* *)(_dest), (void*)(_val), __ATOMIC_RELEASE )
+
+FORCEINLINE
+void*
+SymCryptAtomicCasPtrAcqRel(
+    void** dest,
+    void* desired,
+    void* expected)
+{
+    __atomic_compare_exchange_n(
+        dest,               // ptr
+        &expected,
+        desired,
+        FALSE,              // weak (set to FALSE => strong)
+        __ATOMIC_RELEASE,   // success_memorder
+        __ATOMIC_ACQUIRE ); // failure_memorder
+    return expected;
+}
+
+#define SYMCRYPT_ATOMIC_CAS_PTR_ACQUIRE_RELEASE( _dest, _exchange, _comp ) \
+    SymCryptAtomicCasPtrAcqRel( (volatile void **)(_dest), (void *)(_exchange), (void *)(_comp) )
+
+#endif
+
+// Inline CAS-128 functions
+
+// BOOLEAN
+// SymCryptAtomicCas128Relaxed(
+//     _Inout_updates_(2)  PUINT64     destination,
+//     _Inout_updates_(2)  PUINT64     expectedValue,
+//     _In_reads_(2)       PCUINT64    desiredValue);
+// Performs Compare-and-Swap on a 128b memory location.
+// Atomically reads destination, compares with expectedValue, and:
+//   if they are equal, writes desiredValue to destination, and return TRUE
+//   if they are not equal, writes the value read from destination to expectedValue, and returns FALSE
+//
+// Remarks:
+// On success, the value of expectedValue is not guaranteed.
+// Only destination is guaranteed to be read and written atomically, expectedValue should be a buffer
+// which is only owned by the calling thread.
+// destination must be aligned to 16 bytes
+//
+
+#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
+
+#if SYMCRYPT_PLATFORM_WINDOWS
+
+#if SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_MSVC_CAS128_NF _InterlockedCompareExchange128_nf
+#elif SYMCRYPT_CPU_AMD64
+#define SYMCRYPT_MSVC_CAS128_NF _InterlockedCompareExchange128
+#endif
+
+FORCEINLINE
+BOOLEAN
+SymCryptAtomicCas128Relaxed(
+    _Inout_updates_(2)  PUINT64     destination,
+    _Inout_updates_(2)  PUINT64     expectedValue,
+    _In_reads_(2)       PCUINT64    desiredValue)
+{
+    return SYMCRYPT_MSVC_CAS128_NF(
+        (volatile LONG64 *)destination,
+        (LONG64)desiredValue[1],
+        (LONG64)desiredValue[0],
+        (LONG64 *) expectedValue );
+}
+
+#elif SYMCRYPT_GNUC
+
+FORCEINLINE
+BOOLEAN
+SymCryptAtomicCas128Relaxed(
+    _Inout_updates_(2)  PUINT64     destination,
+    _Inout_updates_(2)  PUINT64     expectedValue,
+    _In_reads_(2)       PCUINT64    desiredValue)
+{
+#if SYMCRYPT_CPU_AMD64
+    // To avoid dynamically linking libatomic in OpenEnclave, use inline assembly for cmpxchg16b
+    // on AMD64. We always need to perform CPU feature detection before we hit this function.
+    BOOLEAN result;
+    __asm__ __volatile__
+    (
+        "lock cmpxchg16b %1\n\t"
+        "sete %0"
+        : "=r" (result)
+        , "+m" (*destination)
+        , "+d" (expectedValue[1])
+        , "+a" (expectedValue[0])
+        : "c"  (desiredValue[1])
+        , "b"  (desiredValue[0])
+        : "cc"
+    );
+    return result;
+#elif SYMCRYPT_CPU_ARM64
+    // clang inlines this but GCC dynamically links to libatomic
+    // For now, just let the compiler decide, and for ARM64 modules, always allow linking to libatomic
+    // We may want to break out into inline asm for LDXP/STXP implementation (v8.0) vs. CASP
+    // implementation (v8.1) in future
+    return __atomic_compare_exchange(
+        (__int128 *)destination,    // ptr
+        (__int128 *)expectedValue,  // expected
+        (__int128 *)desiredValue,   // desired
+        FALSE,                      // weak (set to FALSE => strong)
+        __ATOMIC_RELAXED,           // success_memorder
+        __ATOMIC_RELAXED);          // failure_memorder
+#endif
+}
+
+#endif
+
+#endif
+
+FORCEINLINE
+UINT32
+SymCryptCountTrailingZeros32( UINT32 value )
+{
+    unsigned long index = 0;
+    if( value == 0 )
+    {
+        return 32;
+    }
+
+#if SYMCRYPT_PLATFORM_WINDOWS && (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM)
+    _BitScanForward(&index, value);
+#elif SYMCRYPT_GNUC
+    index = __builtin_ctz(value);
+#else
+    while( (value & 1) == 0 )
+    {
+        index++;
+        value >>= 1;
+    }
+#endif
+
+    return (UINT32)index;
+}
+
+FORCEINLINE
+UINT32
+SymCryptCountTrailingZeros64( UINT64 value )
+{
+    unsigned long index = 0;
+    if( value == 0 )
+    {
+        return 64;
+    }
+
+#if SYMCRYPT_PLATFORM_WINDOWS && (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64)
+    _BitScanForward64(&index, value);
+#elif SYMCRYPT_PLATFORM_WINDOWS && (SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM)
+    if( ((UINT32)value) == 0 )
+    {
+        _BitScanForward(&index, (UINT32)(value>>32));
+        index += 32;
+    } else {
+        _BitScanForward(&index, (UINT32)value);
+    }
+
+#elif SYMCRYPT_GNUC
+    index = __builtin_ctzll(value);
+#else
+    while( (value & 1) == 0 )
+    {
+        index++;
+        value >>= 1;
+    }
+#endif
+
+    return (UINT32)index;
+}
+
+FORCEINLINE
+UINT32
+SymCryptCountLeadingZeros32( UINT32 value )
+{
+    unsigned long zeros = 0;
+
+    if(value == 0)
+    {
+        return 32;
+    }
+
+#if SYMCRYPT_PLATFORM_WINDOWS && (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM)
+    _BitScanReverse(&zeros, value);
+    zeros = 31 - zeros;
+#elif SYMCRYPT_GNUC
+    zeros = __builtin_clz(value);
+#else
+    while( (value & 0x80000000) == 0 )
+    {
+        zeros++;
+        value <<= 1;
+    }
+#endif
+
+    return (UINT32)zeros;
+}
+
+FORCEINLINE
+UINT32
+SymCryptCountLeadingZeros64( UINT64 value )
+{
+    unsigned long zeros = 0;
+
+    if(value == 0)
+    {
+        return 64;
+    }
+
+#if SYMCRYPT_PLATFORM_WINDOWS && (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64)
+    _BitScanReverse64(&zeros, value);
+    zeros = 63 - zeros;
+#elif SYMCRYPT_PLATFORM_WINDOWS && (SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM)
+    if( (value >> 32) == 0 )
+    {
+        _BitScanReverse(&zeros, (UINT32)value);
+        zeros = 63 - zeros;
+    } else {
+        _BitScanReverse(&zeros, (UINT32)(value >> 32));
+        zeros = 31 - zeros;
+    }
+#elif SYMCRYPT_GNUC
+    zeros = __builtin_clzll(value);
+#else
+    while( (value & 0x8000000000000000) == 0 )
+    {
+        zeros++;
+        value <<= 1;
+    }
+#endif
+
+    return (UINT32)zeros;
+}
diff --git a/libs/symcrypt/lib/sc_lib_mldsa.h b/libs/symcrypt/lib/sc_lib_mldsa.h
new file mode 100644
index 000000000000..5d8f558df526
--- /dev/null
+++ b/libs/symcrypt/lib/sc_lib_mldsa.h
@@ -0,0 +1,1086 @@
+//
+// sc_lib_mldsa.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// Internal ML-DSA definitions for the symcrypt library.
+// Always intended to be included as part of sc_lib.h
+//
+
+//
+// Modulus for ML-DSA
+//
+#define SYMCRYPT_MLDSA_Q (8380417)
+
+//
+// Montgomery multiplier for ML-DSA, log 2 (i.e. R = 2^32)
+//
+#define SYMCRYPT_MLDSA_R_LOG2 (32)
+
+//
+// Size of the root seed xi used in key generation
+//
+#define SYMCRYPT_MLDSA_ROOT_SEED_SIZE (32)
+
+//
+// Size of the public seed rho
+//
+#define SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE (32)
+
+//
+// Size of public key hash (tr) = SHAKE256 result size = 64 bytes
+//
+#define SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE SYMCRYPT_SHAKE256_RESULT_SIZE
+
+//
+// Size of private signing seed K
+//
+#define SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE (32)
+
+//
+// Size of the private vector seed rho prime
+//
+#define SYMCRYPT_MLDSA_PRIVATE_VECTOR_SEED_SIZE (64)
+
+//
+// Size of random value used in signing (rnd in FIPS 204)
+//
+#define SYMCRYPT_MLDSA_SIGNING_RANDOM_SIZE (32)
+
+//
+// Length of hash algorithm OIDs in bytes. Currently all supported hash algorithms have 11-byte
+// OIDs, but this is not guaranteed to be the case as more algorithms are added in the future.
+// If the OID length becomes variable, functions which use this value will need to be changed.
+//
+#define SYMCRYPT_MLDSA_SUPPORTED_HASH_OID_SIZE (11)
+
+//
+// Flag for Sign and Verify with External Mu
+//
+#define SYMCRYPT_FLAG_MLDSA_EXTERNALMU (0x1)
+
+typedef struct _SYMCRYPT_MLDSA_POLYELEMENT {
+    // PolyElements just store the coefficients without any header.
+    UINT32      coeffs[SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS];
+} SYMCRYPT_MLDSA_POLYELEMENT, * PSYMCRYPT_MLDSA_POLYELEMENT;
+typedef const SYMCRYPT_MLDSA_POLYELEMENT* PCSYMCRYPT_MLDSA_POLYELEMENT;
+
+// Maximum number of rows and columns in A matrix for ML-DSA
+#define SYMCRYPT_MLDSA_VECTOR_MAX_LENGTH (8)
+#define SYMCRYPT_MLDSA_MATRIX_MAX_NROWS  (8)
+#define SYMCRYPT_MLDSA_MATRIX_MAX_NCOLS  (7)
+
+typedef _Struct_size_bytes_( cbTotalSize ) struct _SYMCRYPT_MLDSA_VECTOR {
+    _Field_range_( 1, SYMCRYPT_MLDSA_VECTOR_MAX_LENGTH )
+    UINT8       nElems;         // Number of PolyElements in the vector
+    UINT32      cbTotalSize;    // Total size of the Vector
+
+    // Followed by:
+    // nElems PolyElements
+} SYMCRYPT_MLDSA_VECTOR, * PSYMCRYPT_MLDSA_VECTOR;
+typedef const SYMCRYPT_MLDSA_VECTOR* PCSYMCRYPT_MLDSA_VECTOR;
+
+typedef _Struct_size_bytes_( cbTotalSize ) struct _SYMCRYPT_MLDSA_MATRIX {
+    _Field_range_( 1, SYMCRYPT_MLDSA_MATRIX_MAX_NROWS )
+    UINT8       nRows;          // k in FIPS-204
+    _Field_range_( 1, SYMCRYPT_MLDSA_MATRIX_MAX_NCOLS )
+    UINT8       nCols;          // l in FIPS-204
+    UINT32      cbTotalSize;    // Total size of the Matrix
+
+    // Followed by:
+    // nRows*nCols PolyElements in row-major order
+} SYMCRYPT_MLDSA_MATRIX, * PSYMCRYPT_MLDSA_MATRIX;
+typedef const SYMCRYPT_MLDSA_MATRIX* PCSYMCRYPT_MLDSA_MATRIX;
+
+typedef struct _SYMCRYPT_MLDSA_INTERNAL_PARAMS {
+    UINT32 params;                      // parameter set of ML-DSA being used - takes a value from SYMCRYPT_MLDSA_PARAMS
+
+    UINT32 cbPolyElement;               // size in bytes of one polynomial ring element
+    UINT32 cbRowVector;                 // size in bytes of one row vector (k elements)
+    UINT32 cbColVector;                 // size in bytes of one column vector (l elements)
+    UINT32 cbMatrix;                    // size in bytes of one matrix
+
+    UINT8 nRows;                        // Number of rows in the A matrix (k in FIPS-204)
+    UINT8 nCols;                        // Number of columns in the A matrix (l in FIPS-204)
+
+    UINT8 privateKeyRange;              // Coefficient range of s1, s2 private key vectors (eta in FIPS-204)
+    UINT8 encodedCoefficientBitLength;  // Bit length of encoded private key coefficients
+
+    UINT8 nChallengeNonZeroCoeffs;        // Number of non-zero coefficients in the challenge polynomial (tau in FIPS-204)
+    UINT8 nHintNonZeroCoeffs;             // Max number of non-zero coefficients in the hint polynomial (omega in FIPS-204)
+    UINT8 maskCoefficientRangeLog2;       // Coefficient range of mask polynomial y (log_2(gamma_1) in FIPS-204)
+    UINT8 commitmentModulus;              // Modulus for commitment values in UseHint and MakeHint (q-1)/(2*gamma_2)
+    UINT32 decomposeR1Factor;             // Multiplication factor for R1 in SymCryptMlDsaDecompose - see function comments
+    UINT32 commitmentRoundingRange;       // Rounding range for commitment value (gamma_2 in FIPS-204)
+    UINT32 w1EncodeCoefficientBitLength;  // Bit length of coefficients for w1 encoding (q - 1) / ((2 * gamma_2) - 1))
+
+    UINT32 cbCommitmentHash;            // Size of the commitment hash (lambda / 4 in FIPS 204)
+    UINT32 cbEncodedPrivateKey;         // Size of the encoded private key
+    UINT32 cbEncodedPublicKey;          // Size of the encoded public key
+    UINT32 cbEncodedSignature;          // Size of the encoded signature
+} SYMCRYPT_MLDSA_INTERNAL_PARAMS, * PSYMCRYPT_MLDSA_INTERNAL_PARAMS;
+typedef const SYMCRYPT_MLDSA_INTERNAL_PARAMS* PCSYMCRYPT_MLDSA_INTERNAL_PARAMS;
+
+typedef _Struct_size_bytes_( cbTotalSize ) struct _SYMCRYPT_MLDSAKEY {
+    UINT32                  fAlgorithmInfo; // Tracks which algorithms the key can be used in (not currently used)
+                                            // Also tracks which per-key selftests have been performed on this key
+                                            // A bitwise OR of SYMCRYPT_FLAG_KEY_*, SYMCRYPT_FLAG_MLDSAKEY_*, and
+                                            // SYMCRYPT_SELFTEST_KEY_* values
+
+    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pParams;
+
+    UINT32 cbTotalSize;             // Total in-memory size of the ML-DSA key (this header and the following structs)
+
+    BOOLEAN hasRootSeed;            // True if the key has the seed used in key generation (xi)
+    BOOLEAN hasPrivateKey;          // True if the key has private vectors s1, s2, t0
+
+    // Seeds
+    _When_( hasRootSeed, _Field_size_bytes_(SYMCRYPT_MLDSA_ROOT_SEED_SIZE) )
+    _When_( !hasRootSeed, _Field_size_bytes_part_(SYMCRYPT_MLDSA_ROOT_SEED_SIZE, 0) )
+    BYTE rootSeed[SYMCRYPT_MLDSA_ROOT_SEED_SIZE];       // Root seed used in key generation (xi) - only available for keys generated by SymCrypt, or imported from a seed
+
+    _When_( hasPrivateKey, _Field_size_bytes_(SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE) )
+    _When_( !hasPrivateKey, _Field_size_bytes_part_(SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE, 0) )
+    BYTE privateSigningSeed[SYMCRYPT_MLDSA_PRIVATE_SIGNING_SEED_SIZE];  // Private seed used in signing (K)
+
+    BYTE publicSeed[SYMCRYPT_MLDSA_PUBLIC_SEED_SIZE];   // Public seed from which A can be derived (rho)
+    BYTE publicKeyHash[SYMCRYPT_MLDSA_PUBLIC_KEY_HASH_SIZE];  // SHAKE-256 hash of the public key
+
+    //
+    // ML-DSA matrix/vector components: A * s1 + s2 = t
+    //
+    // t is separated into two components, t0 and t1, using Power2Round. t0 is private and is used
+    // during signing; t1 is public and is used during verification. All components are stored in
+    // NTT form so that we do not need to convert them during signing or verification.
+    //
+
+    // Public components - always valid
+    PSYMCRYPT_MLDSA_MATRIX pmA;    // Public matrix A - size nRows x nCols
+    PSYMCRYPT_MLDSA_VECTOR pvt1;   // Public component of t vector from Power2Round (row vector)
+
+    // Private components - only valid when hasPrivateKey is TRUE
+    PSYMCRYPT_MLDSA_VECTOR pvs1;   // Private vector s1 (column vector)
+    PSYMCRYPT_MLDSA_VECTOR pvs2;   // Private vector s2 (row vector)
+    PSYMCRYPT_MLDSA_VECTOR pvt0;   // Private component of t vector from Power2Round (row vector)
+
+    SYMCRYPT_MAGIC_FIELD
+    // Followed by:
+    // A
+    // t1
+    // s1
+    // s2
+    // t0
+} SYMCRYPT_MLDSAKEY, * PSYMCRYPT_MLDSAKEY;
+typedef const SYMCRYPT_MLDSAKEY* PCSYMCRYPT_MLDSAKEY;
+
+typedef _Struct_size_bytes_(cbTotalSize) struct _SYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES
+{
+    UINT32                          cbTotalSize;     // Total in-memory size of this structure
+    UINT32                          nRowVectors;     // Number of row vectors
+    UINT32                          nColVectors;     // Number of column vectors
+    UINT32                          nPolyElements;   // Number of PolyElements
+    UINT32                          cbScratch;       // Size of scratch buffer
+
+
+    SYMCRYPT_SHAKE256_STATE         shake256State;
+
+    _Field_size_( nRowVectors )
+    PSYMCRYPT_MLDSA_VECTOR*         pvRowVectors;   // Array of pointers to row vectors
+    _Field_size_( nColVectors )
+    PSYMCRYPT_MLDSA_VECTOR*         pvColVectors;   // Array of pointers to column vectors
+    _Field_size_( nPolyElements)
+    PSYMCRYPT_MLDSA_POLYELEMENT*    pePolyElements; // Array of pointers to PolyElements
+
+    _Field_size_bytes_( cbScratch )
+    PBYTE                           pbScratch;
+
+    SYMCRYPT_MAGIC_FIELD
+    // Followed by:
+    // pvRowVectors[0..nRowVectors-1]
+    // pvColVectors[0..nColVectors-1]
+    // pePolyElements[0..nPolyElements-1]
+    // nRowVectors * SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR( nRows ) buffer for row vectors
+    // nColVectors * SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR( nCols ) buffer for column vectors
+    // nPoly * SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT buffer for PolyElements
+    // cbScratch bytes of scratch space
+} SYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES, * PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES;
+
+#define SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT                ( sizeof( SYMCRYPT_MLDSA_POLYELEMENT ) )
+#define SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR( _nElems )          ( sizeof( SYMCRYPT_MLDSA_VECTOR ) + ( _nElems * sizeof( SYMCRYPT_MLDSA_POLYELEMENT ) ) )
+#define SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX( _nRows, _nCols )   ( sizeof( SYMCRYPT_MLDSA_MATRIX ) + ( _nRows * _nCols * sizeof( SYMCRYPT_MLDSA_POLYELEMENT ) ) )
+#define SYMCRYPT_INTERNAL_MLDSA_SIZEOF_KEY( _nRows, _nCols )      ( sizeof( SYMCRYPT_MLDSAKEY) + \
+                                                                    SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX( _nRows, _nCols ) + \
+                                                                    SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR( _nCols ) + \
+                                                                    (SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR( _nRows ) * 3u) )
+
+#define SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT_OFFSET( _row )     ( sizeof( SYMCRYPT_MLDSA_VECTOR ) + (_row * SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT) )
+#define SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT( _row, _pVector )  ((PSYMCRYPT_MLDSA_POLYELEMENT) ( ((PBYTE) (_pVector)) + SYMCRYPT_INTERNAL_MLDSA_VECTOR_ELEMENT_OFFSET( _row ) ))
+#define SYMCRYPT_INTERNAL_MLDSA_MATRIX_ELEMENT_OFFSET( _row, _col, _pMatrix )  ( sizeof( SYMCRYPT_MLDSA_MATRIX ) + ((_row * (_pMatrix)->nCols + _col) * SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT) )
+#define SYMCRYPT_INTERNAL_MLDSA_MATRIX_ELEMENT( _row, _col, _pMatrix) ((PSYMCRYPT_MLDSA_POLYELEMENT) ( ((PBYTE) (_pMatrix)) + SYMCRYPT_INTERNAL_MLDSA_MATRIX_ELEMENT_OFFSET( _row, _col, _pMatrix ) ))
+
+#define SYMCRYPT_INTERNAL_MLDSA_SIZEOF_ENCODED_VECTOR( _pVector, _nBitsPerCoeff ) ( ((_pVector)->nElems * SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS * (_nBitsPerCoeff) ) / 8u )
+
+// For packing signed coefficients into the minimum possible number of bits for encoding, ML-DSA
+// converts them to (signed upper bound - x) for each coefficient x. For example, when encoding
+// s1 and s2 which have coefficients in the range [-eta, eta] with ML-DSA-65 (eta = 4), 1 is encoded
+// as (4 - 1) = 3, 0 is encoded as (4 - 0) = 4, -1 is encoded as (4 - (-1)) = 5, etc. Conveniently,
+// this also works in reverse to decode the coefficients.
+#define SYMCRYPT_INTERNAL_MLDSA_SHORT_COEFFICIENT_ENCODE_DECODE( _val, _bound)   ( _bound - _val )
+
+//////////////////////////////////////////////////////////////////////////
+// Internal implementations of public APIs
+//////////////////////////////////////////////////////////////////////////
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaKeyGenerateEx(
+    _Inout_                     PSYMCRYPT_MLDSAKEY  pkMlDsakey,
+    _In_reads_( cbRootSeed )    PCBYTE              pbRootSeed,
+                                SIZE_T              cbRootSeed,
+                                UINT32              flags );
+//
+// Implements SymCryptMlDsakeyGenerate. Takes a seed from the caller so that keys can be generated
+// deterministically for testing.
+//
+// Parameters:
+// - (pbRootSeed, cbRootSeed): The seed used to generate the key (xi in FIPS 204)
+//
+// See SymCryptMlDsakeyGenerate for additional documentation.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSignEx(
+    _In_                                                PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _In_reads_( cbInput )                               PCBYTE              pbInput,
+                                                        SIZE_T              cbInput,
+    _In_reads_opt_( cbContext )                         PCBYTE              pbContext,
+    _In_range_( 0, SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH )  SIZE_T              cbContext,
+    _In_reads_opt_( cbHashOid )                         PCBYTE              pbHashOid,
+                                                        SIZE_T              cbHashOid,
+    _In_reads_( cbRandom )                              PCBYTE              pbRandom,
+                                                        SIZE_T              cbRandom,
+                                                        UINT32              flags,
+    _Out_writes_( cbSignature )                         PBYTE               pbSignature,
+                                                        SIZE_T              cbSignature );
+//
+// Implements SymCryptMlDsaSign, SymCryptExternalMuMlDsaSign, and SymCryptHashMlDsaSign.
+// Takes the random value from the caller so that signing can be done deterministically for testing.
+//
+// Parameters:
+// - (pbInput, cbInput): The message to be signed. For SymCryptMlDsaSign, this is the full message.
+//   For SymCryptHashMlDsaSign, this is the hash of the message.
+// - (pbContext, cbContext): An optional context string which will be prepended to the message.
+// - (pbHashOid, cbHashOid): The DER-encoded OID of the hash algorithm used to hash the message,
+//   when using SymCryptHashMlDsaSign. Must be NULL for SymCryptMlDsaSign.
+// - (pbRandom, cbRandom): The random value used in the signing process (rnd in FIPS 204).
+// - flags: 0 or SYMCRYPT_FLAG_MLDSA_EXTERNALMU.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaVerifyEx(
+    _In_                                                PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _In_reads_( cbInput )                               PCBYTE              pbInput,
+                                                        SIZE_T              cbInput,
+    _In_reads_opt_( cbContext )                         PCBYTE              pbContext,
+    _In_range_( 0, SYMCRYPT_MLDSA_CONTEXT_MAX_LENGTH )  SIZE_T              cbContext,
+    _In_reads_opt_( cbHashOid )                         PCBYTE              pbHashOid,
+                                                        SIZE_T              cbHashOid,
+    _In_reads_( cbSignature )                           PCBYTE              pbSignature,
+                                                        SIZE_T              cbSignature,
+                                                        UINT32              flags );
+//
+// Implements SymCryptMlDsaVerify, SymCryptExternalMuMlDsaVerify, and SymCryptHashMlDsaVerify.
+//
+// Parameters:
+// - (pbInput, cbInput): The message to be verified. For SymCryptMlDsaVerify, this is the full
+//   message. For SymCryptHashMlDsaVerify, this is the hash of the message.
+// - (pbContext, cbContext): An optional context string which will be prepended to the message.
+// - (pbHashOid, cbHashOid): The DER-encoded OID of the hash algorithm used to hash the message,
+//   when using SymCryptHashMlDsaVerify. Must be NULL for SymCryptMlDsaVerify.
+// - (pbSignature, cbSignature): The signature to be verified.
+// - flags: 0 or SYMCRYPT_FLAG_MLDSA_EXTERNALMU.
+//
+
+_Success_( TRUE )
+PSYMCRYPT_MLDSAKEY
+SYMCRYPT_CALL
+SymCryptMlDsakeyInitialize(
+    _In_                        PCSYMCRYPT_MLDSA_INTERNAL_PARAMS pInternalParams,
+    _Out_writes_bytes_(cbKey)   PBYTE                            pbKey,
+                                UINT32                           cbKey );
+//
+// Initializes a SYMCRYPT_MLDSAKEY structure in the given buffer. The buffer size (cbKey) must
+// be exactly equal to the size of the key structure, which can be calculated using
+// SYMCRYPT_INTERNAL_MLDSA_SIZEOF_KEY.
+//
+// Parameters:
+// - pInternalParams: Parameter set to use for the key.
+// - (pbKey, cbKey): Buffer for the key structure.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsakeyComputeT(
+    _In_    PCSYMCRYPT_MLDSA_MATRIX     pmA,
+    _In_    PCSYMCRYPT_MLDSA_VECTOR     pvs1,
+    _In_    PCSYMCRYPT_MLDSA_VECTOR     pvs2,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR      pvt0,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR      pvt1,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR      pvTmp,
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT peTmp );
+//
+// Helper function for computing the t vector in ML-DSA: A * s1 + s2 = t. Used by key generation
+// and private key import. All inputs must be in NTT form. The outputs t0 and t1 are NOT returned
+// in NTT form; it is the caller's responsibility to convert them when appropriate.
+//
+// Parameters:
+// - pmA: Public matrix A
+// - pvs1: Private vector s1.
+// - pvs2: Private vector s2.
+// - pvt0: Private component of t vector from Power2Round.
+// - pvt1: Public component of t vector from Power2Round.
+// - pvTmp: Temporary vector for intermediate computations.
+// - peTmp: Temporary PolyElement for intermediate computations.
+//
+
+//////////////////////////////////////////////////////////////////////////
+// Montgomery reduction and multiplication
+//////////////////////////////////////////////////////////////////////////
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaMontReduce( UINT64 a );
+//
+// Montgomery reduction
+// res = a * R^-1 mod Q.
+//
+// Note that this divides out a factor of R.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaMontMul( UINT32 a, UINT32 b );
+//
+// Montgomery multiplication
+// res = (a * b) / R mod Q
+//
+// Equivalent to SymCryptMlDsaMontReduce( (UINT64) a * b )
+// As above, this divides out a factor of R, which can be compensated for in either input,
+// or taken into account in the output.
+//
+
+//////////////////////////////////////////////////////////////////////////
+// 32-bit modular arithmetic
+//////////////////////////////////////////////////////////////////////////
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaModAdd( UINT32 a, UINT32 b );
+//
+// res := a + b mod Q
+//
+// Requirements: a < Q, b < Q
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaModSub( UINT32 a, UINT32 b );
+//
+// res := a - b mod Q
+//
+// Requirements: a < Q, b < Q
+//
+
+//////////////////////////////////////////////////////////////////////////
+// Polynomial operations
+//////////////////////////////////////////////////////////////////////////
+
+_Success_( TRUE )
+PSYMCRYPT_MLDSA_POLYELEMENT
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementCreate(
+    _Inout_updates_( cbBuffer ) PBYTE   pbBuffer,
+                                SIZE_T  cbBuffer );
+//
+// Initializes a SYMCRYPT_MLDSA_POLYELEMENT in the given buffer.
+// cbBuffer must be equal to SYMCRYPT_INTERNAL_MLDSA_SIZEOF_POLYELEMENT.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementSetZero(
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT     peDst );
+//
+// Sets all coefficients to zero
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementNTT(
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT     peSrc );
+//
+// ML-DSA Polynomial Ring Element NTT:
+//      peSrc = NTT(peSrc) per FIPS 204
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementINTT(
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT     peSrc );
+//
+// ML-DSA Polynomial Ring Element inverse NTT:
+//      peSrc = InverseNTT(peSrc) per FIPS 204
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementMulR(
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT     peSrc );
+//
+// ML-DSA Polynomial multiplication by the Montgomery multiplier R:
+//    peSrc = (peSrc * R) mod Q
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementMontMul(
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc1,
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    _Out_   PSYMCRYPT_MLDSA_POLYELEMENT     peDst );
+//
+// ML-DSA Polynomial Montgomery multiplication:
+//     peDst = (peSrc1 * peSrc2) ./ R
+// where:
+// * is polynomial multiplication given sources in NTT form
+// ./ is coefficient-wise division and R is the Montgomery multiplier
+//
+// Requirements:
+//  - peSrc1 and peSrc2 must be PolyElements in ML-DSA NTT form
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementAdd(
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc1,
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    _Out_   PSYMCRYPT_MLDSA_POLYELEMENT     peDst );
+//
+// ML-DSA Polynomial Ring Element addition
+//      peDst = peSrc1 + peSrc2
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementSub(
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc1,
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    _Out_   PSYMCRYPT_MLDSA_POLYELEMENT     peDst );
+//
+// ML-DSA Polynomial Ring Element subtraction
+//      peDst = peSrc1 - peSrc2
+//
+
+//////////////////////////////////////////////////////////////////////////
+// Vector operations
+//////////////////////////////////////////////////////////////////////////
+
+_Success_( TRUE )
+PSYMCRYPT_MLDSA_VECTOR
+SYMCRYPT_CALL
+SymCryptMlDsaVectorCreate(
+    _Out_writes_( cbBuffer )    PBYTE   pbBuffer,
+                                UINT32  cbBuffer,
+                                UINT8   nElems );
+//
+// Initializes a vector of nElems PolyElements in the given buffer.
+// cbBuffer must be equal to SYMCRYPT_INTERNAL_MLDSA_SIZEOF_VECTOR( nElems ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorCopy(
+    _In_      PCSYMCRYPT_MLDSA_VECTOR         pvSrc,
+    _Inout_   PSYMCRYPT_MLDSA_VECTOR          pvDst );
+//
+// pvDst = pvSrc. Vectors must be the same size.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorSetZero(
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvDst );
+//
+// Sets all elements of the vector to zero.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorAdd(
+    _In_    PCSYMCRYPT_MLDSA_VECTOR         pvSrc1,
+    _In_    PCSYMCRYPT_MLDSA_VECTOR         pvSrc2,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvDst );
+//
+//  pvDst = pvSrc1 + pvSrc2
+//
+// Requirements: pvSrc1, pvSrc2, and pvDst must all have the same number of elements.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorSub(
+    _In_    PCSYMCRYPT_MLDSA_VECTOR         pvSrc1,
+    _In_    PCSYMCRYPT_MLDSA_VECTOR         pvSrc2,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvDst );
+//
+//  pvDst = pvSrc1 - pvSrc2
+//
+// Requirements: pvSrc1, pvSrc2, and pvDst must all have the same number of elements.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorPolyElementMontMul(
+    _In_    PCSYMCRYPT_MLDSA_VECTOR         pvSrc1,
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc2,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvDst );
+//
+// ML-DSA Vector-PolyElement Montgomery Multiplication:
+//    pvDst[i] = (pvSrc1[i] * peSrc2) ./ R
+//
+// where:
+// * is polynomial multiplication given sources in NTT form
+// ./ is coefficient-wise division and R is Montgomery multiplier
+//
+// Requirements:
+//  - peSrc2, and all elements of pvSrc1 must be in ML-DSA NTT form
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorNTT(
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvSrc );
+//
+// ML-DSA Vector NTT:
+//     pvSrc[i] = NTT(pvSrc[i]) for each element in pvSrc
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorINTT(
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvSrc );
+//
+// ML-DSA Vector inverse NTT:
+//     pvSrc[i] = INTT(pvSrc[i]) for each element in pvSrc
+//
+
+
+//////////////////////////////////////////////////////////////////////////
+// Matrix operations
+//////////////////////////////////////////////////////////////////////////
+
+PSYMCRYPT_MLDSA_MATRIX
+SYMCRYPT_CALL
+SymCryptMlDsaMatrixCreate(
+    _Out_writes_( cbBuffer )    PBYTE       pbBuffer,
+                                UINT32      cbBuffer,
+                                UINT8       nRows,
+                                UINT8       nCols );
+//
+// Initializes a matrix of nRows * nCols PolyElements in the given buffer.
+// cbBuffer must be equal to SYMCRYPT_INTERNAL_MLDSA_SIZEOF_MATRIX( nRows, nCols ).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaMatrixVectorMontMul(
+    _In_    PCSYMCRYPT_MLDSA_MATRIX         pmSrc1,
+    _In_    PCSYMCRYPT_MLDSA_VECTOR         pvSrc2,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR          pvDst,
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT     peTmp );
+//
+// ML-DSA Matrix-Vector Montgomery Multiplication:
+//     pvDst = (pmSrc1 * pvSrc2) ./ R
+//
+// where:
+// * is matrix-vector multiplication of polynomials in NTT form
+// ./ is coefficient-wise division and R is Montgomery multiplier
+//
+
+
+//////////////////////////////////////////////////////////////////////////
+// Sampling and rejection
+//////////////////////////////////////////////////////////////////////////
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaRejNttPoly(
+    _In_reads_( cbRejNttPolySeed )  PCBYTE                      pbRejNttPolySeed,
+                                    SIZE_T                      cbRejNttPolySeed,
+    _Inout_                         PSYMCRYPT_MLDSA_POLYELEMENT peDst );
+//
+// RejNTTPoly from FIPS 204
+// Used by SymCryptMlDsaExpandA to generate a polynomials in the public matrix A from the expanded
+// public seed. The output polynomial is in NTT form with coefficients modulo Q.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaExpandA(
+    _In_reads_( cbPublicSeed )  PCBYTE                      pbPublicSeed,
+                                SIZE_T                      cbPublicSeed,
+    _Inout_                     PSYMCRYPT_MLDSA_MATRIX      pmA );
+//
+// ExpandA from FIPS 204
+// Expands the public seed into the public matrix A.
+// \hat{A}[i, j] = RejNttPoly(seed || j || i) for each index (i, j) in A
+//
+
+FORCEINLINE
+INT8
+SYMCRYPT_CALL
+SymCryptMlDsaCoeffFromHalfByte(
+    _In_                 PCSYMCRYPT_MLDSA_INTERNAL_PARAMS   pParams,
+    _In_range_( 0, 15 )  UINT8                              halfByte );
+//
+// CoeffFromHalfByte from FIPS 204
+// Converts a nibble (range [0, 15]) to a coefficient in the range [-eta, eta]
+// If the nibble is outside of the valid private key range ([0, 14] for eta = 2, [0, 8] for eta = 4),
+// returns INT8_MIN.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaRejBoundedPoly(
+    _In_                                PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_reads_( cbRejBoundedPolySeed )  PCBYTE                              pbRejBoundedPolySeed,
+                                        SIZE_T                              cbRejBoundedPolySeed,
+    _Inout_                             PSYMCRYPT_MLDSA_POLYELEMENT         peDst );
+//
+// RejBoundedPoly from FIPS 204
+// Used by SymCryptMlDsaExpandS to generate polynomials in the private vectors s1 and s2 from the
+// expanded private vector seed. Coefficients in the output polynomial are modulo Q.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaExpandS(
+    _In_                                PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_reads_( cbPrivateVectorSeed )   PCBYTE                              pbPrivateVectorSeed,
+                                        SIZE_T                              cbPrivateVectorSeed,
+    _Inout_                             PSYMCRYPT_MLDSA_VECTOR              pvs1,
+    _Inout_                             PSYMCRYPT_MLDSA_VECTOR              pvs2 );
+//
+// ExpandS from FIPS 204
+// s1 = RejBoundedPoly(seed || i) for each index i in s1 (column vector)
+// s2 = RejBoundedPoly(seed || i) for each index i in s2 (row vector)
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaSampleInBall(
+    _In_                            PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_reads_( cbCommitmentHash )  PCBYTE                              pbCommitmentHash,
+                                    SIZE_T                              cbCommitmentHash,
+    _Inout_                         PSYMCRYPT_MLDSA_POLYELEMENT         peChallenge );
+//
+// SampleInBall from FIPS 204
+// Samples a polynomial c in R_q with coefficients in {-1, 0, 1} and Hamming weight tau.
+// As with all polynomials, coefficients are represented as unsigned integers modulo Q.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaExpandMask(
+    _In_                            PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _Inout_                         PSYMCRYPT_SHAKE256_STATE            pShakeState,
+    _In_reads_( cbPrivateRandom )   PCBYTE                              pbPrivateRandom,
+                                    SIZE_T                              cbPrivateRandom,
+    _In_                            UINT16                              counter,
+    _Inout_                         PSYMCRYPT_MLDSA_VECTOR              pvMask );
+//
+// ExpandMask from FIPS 204
+// Samples a polynomial vector y in R^l such that each polynomial y[r] has coefficients between
+// (-gamma_1 + 1, gamma_1) modulo Q, where gamma_1 == 2^(maskCoefficientRangeLog2) . The output
+// vector is returned in NTT form.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaMakeHint(
+    _In_        PCSYMCRYPT_MLDSA_INTERNAL_PARAMS                pParams,
+    _Inout_     PSYMCRYPT_MLDSA_VECTOR                          pvWMinusCs2,
+    _Inout_     PSYMCRYPT_MLDSA_VECTOR                          pvWMinusCs2PlusCt0,
+    _Inout_     PSYMCRYPT_MLDSA_VECTOR                          pvDst,
+    _Out_       UINT32*                                         nBitsSet );
+//
+// MakeHint from FIPS 204
+// Computes the hint vector. Each coefficient of the polynomials in the vector is a single bit
+// indicating whether adding ct0 to (w - cs2) alters the high bits of the corresponding coefficient.
+// We define our inputs differently from FIPS 204 to reduce computations:
+//
+// In FIPS 204, MakeHint is defined as:
+// [[r1 != v1]] where r1 = HighBits(r), v1 = HighBits(r + z)
+//
+// ML-DSA.Sign_internal calls MakeHint with inputs:
+// z = -ct0, r = w - cs2 + ct0
+//
+// We can simplify this to:
+// r1 = HighBits(w - cs2 + ct0), v1 = HighBits(w - cs2)
+//
+// Note that this function modifies the inputs in place for efficiency.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaUseHint(
+    _In_        PCSYMCRYPT_MLDSA_INTERNAL_PARAMS                pParams,
+    _In_        PCSYMCRYPT_MLDSA_VECTOR                         pvHint,
+    _Inout_     PSYMCRYPT_MLDSA_VECTOR                          pvCommitment );
+//
+// UseHint from FIPS 204
+// Uses the hint vector to recalculate the original commitment vector from the approximated
+// commitment vector by setting the high bits of the coefficients that were dropped in the
+// approximation. On input, pvCommitment is the approximated commitment vector. On output, it is
+// the recalculated original commitment vector.
+//
+// TODO osgvsowi/55435592 Consider decoding the hint just-in-time to avoid allocating an
+// entire vector for it
+//
+
+//////////////////////////////////////////////////////////////////////////
+// Encoding/decoding
+//////////////////////////////////////////////////////////////////////////
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementEncode(
+    _In_    PCSYMCRYPT_MLDSA_POLYELEMENT    peSrc,
+            UINT32                          nBitsPerCoefficient,
+            UINT32                          signedCoefficientBound,
+    _Out_writes_( nBitsPerCoefficient * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) )
+            PBYTE                           pbDst );
+//
+// Encode a polynomial with coefficients in the range [0, 2^nBitsPerCoefficient] into a tightly
+// packed byte array.
+//
+// Signed coefficients are encoded as described in the comment for
+// SYMCRYPT_INTERNAL_MLDSA_SHORT_COEFFICIENT_ENCODE_DECODE. For these coefficients, the
+// signedCoefficientBound parameter indicates the upper bound of the coefficients when they are
+// positive, and is used to convert them from their internal representation modulo Q to the
+// encoded representation.
+//
+// For polynomials whose coefficients are always positive and do not need any special encoding
+// (e.g. t1), signedCoefficientBound must be 0.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementDecode(
+    _In_reads_bytes_( nBitsPerCoefficient * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) )
+            PCBYTE                          pbSrc,
+            UINT32                          nBitsPerCoefficient,
+            UINT32                          signedCoefficientBound,
+    _Inout_ PSYMCRYPT_MLDSA_POLYELEMENT     peDst );
+//
+// From a byte array that was previously encoded as described in SymCryptMlDsaPolyElementEncode,
+// decode a polynomial with coefficients in the range [0, 2^nBitsPerCoefficient].
+//
+// See comments on SymCryptMlDsaPolyElementEncode for information about how coefficients are
+// encoded and decoded.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorEncode(
+    _In_    PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+            UINT32                              nBitsPerCoefficient,
+            UINT32                              signedCoefficientBound,
+    _Out_writes_( pvSrc->nElems * nBitsPerCoefficient * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) )
+            PBYTE                               pbDst );
+//
+// Encodes a vector of polynomials into a tightly packed byte array.
+// pbDst := SymCryptMlDsaPolyElementEncode(i) for each polynomial i in pvSrc
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaVectorDecode(
+    _In_reads_bytes_( pvDst->nElems * nBitsPerCoefficient * (SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) )
+            PCBYTE                              pbSrc,
+            UINT32                              nBitsPerCoefficient,
+            UINT32                              signedCoefficientBound,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR              pvDst );
+//
+// Decodes a vector of encoded polynomials from a byte array.
+// pvDst[i] := SymCryptMlDsaPolyElementDecode(i) for each encoded polynomial i in pbSrc
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaPkEncode(
+    _In_                    PCSYMCRYPT_MLDSAKEY pkMlDsakey,
+    _Out_writes_( cbDst )   PBYTE               pbDst,
+                            SIZE_T              cbDst );
+//
+// pkEncode(key) = rho || SimpleBitPack(t1)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaPkDecode(
+    _In_reads_( cbSrc ) PCBYTE                  pbSrc,
+                        SIZE_T                  cbSrc,
+                        UINT32                  flags,
+    _Inout_             PSYMCRYPT_MLDSAKEY      pkMlDsakey );
+//
+// Decodes a public key from a byte array. The encoded public key only contains rho and t1.
+// We recalculate the A matrix from rho.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSkEncode(
+    _In_                    PCSYMCRYPT_MLDSAKEY pKey,
+    _Out_writes_( cbDst )   PBYTE               pbDst,
+                            SIZE_T              cbDst );
+//
+// skEncode(key) = rho || K || H(pkEncode(key)) || BitPack(s1) || BitPack(s2) || BitPack(t0)
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSkDecode(
+    _In_reads_( cbSrc ) PCBYTE                  pbSrc,
+                        SIZE_T                  cbSrc,
+                        UINT32                  flags,
+    _Inout_             PSYMCRYPT_MLDSAKEY      pKey );
+//
+// Decodes a private key from a byte array. The encoded private key contains rho, K, s1, s2 and t0.
+// We recalculate the A matrix from rho, t1 by recalculating A * s1 + s2 = t. This function also
+// validates that the recalculated public key hash and t0 match the encoded values. If they do
+// not, it returns SYMCRYPT_INVALID_BLOB.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaSigEncode(
+    _In_                            PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_reads_( cbCommitmentHash )  PBYTE                               pbCommitmentHash,
+                                    SIZE_T                              cbCommitmentHash,
+    _In_                            PCSYMCRYPT_MLDSA_VECTOR             pvResponse,
+    _In_                            PCSYMCRYPT_MLDSA_VECTOR             pvHint,
+    _Out_writes_( cbDst )           PBYTE                               pbDst,
+                                    SIZE_T                              cbDst );
+//
+// SigEncode from FIPS 204
+// Encodes a signature into a tightly packed byte array.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaSigDecode(
+    _In_                            PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_reads_( cbSig )             PCBYTE                              pbSig,
+                                    SIZE_T                              cbSig,
+    _Out_writes_( cbCommitmentHash) PBYTE                               pbCommitmentHash,
+                                    SIZE_T                              cbCommitmentHash,
+    _Inout_                         PSYMCRYPT_MLDSA_VECTOR              pvResponse,
+    _Inout_                         PSYMCRYPT_MLDSA_VECTOR              pvHint );
+//
+// SigDecode from FIPS 204
+// Decodes a signature from a tightly packed byte array, producing the commitment hash, response
+// vector, and hint vector.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaHintBitPack(
+    _In_    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_    PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    _Out_writes_bytes_( pParams->nHintNonZeroCoeffs + pvSrc->nElems )
+            PBYTE                               pbDst );
+//
+// HintBitPack from FIPS 204
+// Packs the hint vector into a byte array. The first nHintNonZeroCoeffs bytes are the indices
+// of non-zero coefficients in the vector, and the last nElems bytes contain the number of
+// non-zero coefficients in polynomials 0..i of the vector.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaHintBitUnpack(
+    _In_    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_reads_bytes_( pParams->nHintNonZeroCoeffs + pvDst->nElems )
+            PCBYTE                              pbSrc,
+    _Inout_ PSYMCRYPT_MLDSA_VECTOR              pvDst );
+//
+// HintBitUnpack from FIPS 204
+// Unpacks the hint vector from a byte array where each byte indicates the index of a non-zero
+// coefficient in the corresponding polynomial. See comment on SymCryptMlDsaHintBitPack for more
+// details about encoding.
+//
+
+//////////////////////////////////////////////////////////////////////////
+// Auxiliary functions
+//////////////////////////////////////////////////////////////////////////
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlDsaGetInternalParamsFromParams(
+            SYMCRYPT_MLDSA_PARAMS               params,
+    _Out_   PCSYMCRYPT_MLDSA_INTERNAL_PARAMS*   pInternalParams );
+//
+// Get the internal parameter structure corresponding to the given parameter set enum.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptHashMlDsaValidateHashAlgAndGetOid(
+    _In_    PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+            SYMCRYPT_PQDSA_HASH_ID              hashAlg,
+            SIZE_T                              cbHash,
+    _Out_   PCSYMCRYPT_OID*                     ppOid );
+//
+// Validates that the given hash algorithm meets the required collision strength for the ML-DSA
+// parameter set, as defined in FIPS 204. Also validates that cbHash matches the expected length
+// for the hash algorithm, or for XOFs, is >= the required collision strength.
+// See comments on the definition of SymCryptHashMlDsaSign
+//
+
+FORCEINLINE
+INT32
+SYMCRYPT_CALL
+SymCryptMlDsaModPlusMinus( UINT32 r, UINT32 modulus );
+//
+// Helper function which implements the mod+- operation from FIPS 204.
+// In FIPS 204, r0 := r mod+- 2^d where mod+- returns the unique element in  (-(2^d/2), 2^d/2]
+// which is congruent to r modulo 2^d. Importantly, this means that r0 may be negative.
+// To use consistent data structures throughout the our implementation and simplify modular
+// arithmetic, we do not use negative numbers. Instead, we always represent negative values as
+// UINT32s modulo Q.
+//
+// Requirements: r < modulus
+//
+
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementInfinityNorm( _In_ PCSYMCRYPT_MLDSA_POLYELEMENT peSrc );
+//
+// Returns the infinity norm of the given polynomial element as defined in FIPS 204.
+// The infinity norm is the maximum absolute value of w mod+- Q for each coefficient w in the
+// polynomial.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaVectorInfinityNorm( _In_ PCSYMCRYPT_MLDSA_VECTOR pvSrc );
+//
+// Returns the infinity norm of the given vector as defined in FIPS 204.
+// = max(InfinityNorm(pvSrc[i])) for each polynomial in pvSrc
+//
+
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaDecompose(
+    _In_                                PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_range_(0, SYMCRYPT_MLDSA_Q - 1) UINT32                              r,
+    _Out_opt_                           UINT32                              *puR1,
+    _Out_opt_                           UINT32                              *puR0 );
+//
+// Decompose from FIPS 204
+// Decomposes r into (r1, r0) such that r1*2*gamma_2 + r0 is congruent to r modulo q
+// See note above in SymCryptMlDsaModPlusMinus for important information about the
+// representation of r0.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorHighBits(
+    _In_                                PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_                                PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    _Inout_                             PSYMCRYPT_MLDSA_VECTOR              pvDst );
+//
+// HighBits from FIPS 204
+// For each coefficent r of each polynomial in pvSrc, the corresponding coefficient in pvDst is
+// set to *puR1 from Decompose(r).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorLowBits(
+    _In_                                PCSYMCRYPT_MLDSA_INTERNAL_PARAMS    pParams,
+    _In_                                PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    _Inout_                             PSYMCRYPT_MLDSA_VECTOR              pvDst );
+//
+// LowBits from FIPS 204
+// For each coefficent r of each polynomial in pvSrc, the corresponding coefficient in pvDst is
+// set to *puR0 from Decompose(r).
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPower2Round(
+    _In_range_(0, SYMCRYPT_MLDSA_Q - 1) UINT32                              r,
+    _Out_                               UINT32                              *puR1,
+    _Out_                               UINT32                              *puR0 );
+//
+// Power2Round from FIPS 204
+// Decomposes r into (r1, r0) such that r1*2^d + r0 is congruent to r modulo q
+// See note above in SymCryptMlDsaModPlusMinus for important information about the
+// representation of r0.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaPolyElementPower2Round(
+    _In_                                PCSYMCRYPT_MLDSA_POLYELEMENT        peSrc,
+    _Inout_                             PSYMCRYPT_MLDSA_POLYELEMENT         peDst1,
+    _Inout_                             PSYMCRYPT_MLDSA_POLYELEMENT         peDst0 );
+//
+// (peDst1[i], peDst0[i]) = Power2Round(peSrc[i]) for each coefficient in peSrc
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaVectorPower2Round(
+    _In_                                PCSYMCRYPT_MLDSA_VECTOR             pvSrc,
+    _Inout_                             PSYMCRYPT_MLDSA_VECTOR              pvDst1,
+    _Inout_                             PSYMCRYPT_MLDSA_VECTOR              pvDst0 );
+//
+// (pvDst1[i], pvDst0[i]) = Power2Round(pvSrc[i]) for each polynomial in pvSrc
+//
+
+FORCEINLINE
+UINT32
+SYMCRYPT_CALL
+SymCryptMlDsaSignedCoefficientModQ( INT32 coefficient );
+//
+// Maps a signed short coefficient to a residue modulo Q.
+//
+
+_Success_( return != NULL )
+PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES
+SYMCRYPT_CALL
+SymCryptMlDsaTemporariesAllocateAndInitialize(
+    _In_            PCSYMCRYPT_MLDSA_INTERNAL_PARAMS                pParams,
+                    UINT32                                          nRowVectors,
+                    UINT32                                          nColVectors,
+                    UINT32                                          nPolyElements,
+                    UINT32                                          cbScratch );
+//
+// Allocates and initializes a SYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES structure and
+// returns a pointer to the caller. Returns NULL if allocation fails.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlDsaTemporariesFree(
+    _In_ _Post_invalid_ PSYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES pTemporaries );
+//
+// Wipes and frees a SYMCRYPT_MLDSA_INTERNAL_COMPUTATION_TEMPORARIES structure previously allocated
+// by SymCryptMlDsaTemporariesAllocateAndInitialize.
+//
diff --git a/libs/symcrypt/lib/sc_lib_mlkem.h b/libs/symcrypt/lib/sc_lib_mlkem.h
new file mode 100644
index 000000000000..15c2ff91ce52
--- /dev/null
+++ b/libs/symcrypt/lib/sc_lib_mlkem.h
@@ -0,0 +1,468 @@
+//
+// sc_lib_mlkem.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// Internal ML-KEM definitions for the symcrypt library.
+// Always intended to be included as part of sc_lib.h
+//
+
+//=====================================================
+//  ML-KEM internal high level types
+//
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEM_POLYELEMENT {
+    // PolyElements just store the coefficients without any header.
+    UINT16    coeffs[SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS];
+} SYMCRYPT_MLKEM_POLYELEMENT;
+typedef       SYMCRYPT_MLKEM_POLYELEMENT * PSYMCRYPT_MLKEM_POLYELEMENT;
+typedef const SYMCRYPT_MLKEM_POLYELEMENT * PCSYMCRYPT_MLKEM_POLYELEMENT;
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR {
+    // PolyElement Accumulators just store the coefficients without any header.
+    UINT32    coeffs[SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS];
+} SYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR;
+typedef SYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR * PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR;
+
+// Currently maximum size of MLKEM matrices is baked in, they are always square and up to 4x4.
+#define SYMCRYPT_MLKEM_MATRIX_MAX_NROWS (4)
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEM_VECTOR {
+    _Field_range_( 1, SYMCRYPT_MLKEM_MATRIX_MAX_NROWS )
+        UINT32                      nRows;
+        UINT32                      cbTotalSize;    // Total size of the Vector
+
+    // Followed by:
+    // nRows PolyElements
+} SYMCRYPT_MLKEM_VECTOR, *PSYMCRYPT_MLKEM_VECTOR;
+typedef const SYMCRYPT_MLKEM_VECTOR * PCSYMCRYPT_MLKEM_VECTOR;
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEM_MATRIX {
+    _Field_range_( 1, SYMCRYPT_MLKEM_MATRIX_MAX_NROWS )
+        UINT32                      nRows;
+        UINT32                      cbTotalSize;    // Total size of the Matrix
+
+    // Array of pointers to PolyElements in row-major order
+        PSYMCRYPT_MLKEM_POLYELEMENT apPolyElements[SYMCRYPT_MLKEM_MATRIX_MAX_NROWS * SYMCRYPT_MLKEM_MATRIX_MAX_NROWS];
+    // Note: the extra indirection is intentional to make transposing the matrix cheap,
+    // given that in the MLKEM context the underlying PolyElements are relatively large
+    // so we don't want to move them around
+
+    // Followed by:
+    // nRows*nRows PolyElements
+} SYMCRYPT_MLKEM_MATRIX, *PSYMCRYPT_MLKEM_MATRIX;
+typedef const SYMCRYPT_MLKEM_MATRIX * PCSYMCRYPT_MLKEM_MATRIX;
+
+//
+// MLKEMKEY type
+//
+
+#define SYMCRYPT_MLKEMKEY_MAX_SIZEOF_ENCODED_T (1536)
+
+typedef SYMCRYPT_ALIGN_STRUCT _SYMCRYPT_MLKEM_INTERNAL_PARAMS {
+    UINT32  params;         // parameter set of ML-KEM being used, takes a value from SYMCRYPT_MLKEM_PARAMS
+
+    UINT32  cbPolyElement;  // size of one polynomial ring element
+    UINT32  cbVector;       // size of one vector
+    UINT32  cbMatrix;       // size of one matrix
+
+    UINT8   nRows;          // corresponds to k from FIPS 203; the number of rows and columns in the matrix A,
+                            // and the number of rows in column vectors s and t
+    UINT8   nEta1;          // corresponds to eta_1 from FIPS 203; number of coinflips used in generating s and e
+                            // in keypair generation, and r in encapsulation
+    UINT8   nEta2;          // corresponds to eta_2 from FIPS 203; number of coinflips used in generating e_1 and
+                            // e_2 in encapsulation
+    UINT8   nBitsOfU;       // corresponds to d_u from FIPS 203; number of bits that the coefficients of the polynomial
+                            // ring elements of u are compressed to in encapsulation for encoding into ciphertext
+    UINT8   nBitsOfV;       // corresponds to d_v from FIPS 203; number of bits that the coefficients of the polynomial
+                            // ring element v is compressed to in encapsulation for encoding into ciphertext
+} SYMCRYPT_MLKEM_INTERNAL_PARAMS, *PSYMCRYPT_MLKEM_INTERNAL_PARAMS;
+typedef const SYMCRYPT_MLKEM_INTERNAL_PARAMS * PCSYMCRYPT_MLKEM_INTERNAL_PARAMS;
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEMKEY {
+    UINT32                  fAlgorithmInfo; // Tracks which algorithms the key can be used in
+                                            // Also tracks which per-key selftests have been performed on this key
+                                            // A bitwise OR of SYMCRYPT_FLAG_KEY_*, SYMCRYPT_FLAG_MLKEMKEY_*, and
+                                            // SYMCRYPT_SELFTEST_KEY_* values
+
+    SYMCRYPT_MLKEM_INTERNAL_PARAMS params;
+
+    UINT32                  cbTotalSize;    // Total in-memory size of the ML-KEM key (this header and the following structs)
+
+    BOOLEAN                 hasPrivateSeed; // Set to true if key has the private seed (d)
+    BOOLEAN                 hasPrivateKey;  // Set to true if key has the private key (s and z)
+
+    // seeds
+    BYTE                    privateSeed[32];    // private seed (d) from which entire private PKE key can be derived
+    BYTE                    privateRandom[32];  // private random (z) used in implicit rejection
+
+    BYTE                    publicSeed[32];     // public seed (rho) from which A can be derived
+
+    // A o s + e = t
+    PSYMCRYPT_MLKEM_MATRIX  pmAtranspose;   // public matrix in NTT form (derived from publicSeed)
+    PSYMCRYPT_MLKEM_VECTOR  pvt;            // public vector in NTT form
+
+    PSYMCRYPT_MLKEM_VECTOR  pvs;            // private vector in NTT form
+
+    // misc fields
+    BYTE                    encodedT[SYMCRYPT_MLKEMKEY_MAX_SIZEOF_ENCODED_T]; // byte-encoding of public vector
+                                                                              // may only use a prefix of this buffer
+    BYTE                    encapsKeyHash[32];  // Precomputed value of hash of ML-KEM's byte-encoding of encapsulation key
+
+    SYMCRYPT_MAGIC_FIELD
+    // Followed by:
+    // Atranspose
+    // t
+    // s
+} SYMCRYPT_MLKEMKEY;
+
+//=====================================================
+//  ML-KEM primitives
+//
+
+#define SYMCRYPT_MLKEM_Q                        (3329)
+
+#define SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT              ( sizeof(SYMCRYPT_MLKEM_POLYELEMENT) )
+#define SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT_ACCUMULATOR  ( sizeof(SYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR) )
+#define SYMCRYPT_INTERNAL_MLKEM_MAXIMUM_VECTOR_SIZE                 ( sizeof(SYMCRYPT_MLKEM_VECTOR) + (SYMCRYPT_MLKEM_MATRIX_MAX_NROWS * SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT) )
+#define SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT_OFFSET( _row )       ( sizeof(SYMCRYPT_MLKEM_VECTOR) + ((_row) * SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT) )
+#define SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT( _row, _pVector )    (PSYMCRYPT_MLKEM_POLYELEMENT)( (PBYTE)(_pVector) + SYMCRYPT_INTERNAL_MLKEM_VECTOR_ELEMENT_OFFSET(_row) )
+
+#define SYMCRYPT_MLKEM_SIZEOF_MAX_CIPHERTEXT    (1568UL)
+#define SYMCRYPT_MLKEM_SIZEOF_AGREED_SECRET     (32UL)
+#define SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM     (32UL)
+
+typedef SYMCRYPT_ASYM_ALIGN_STRUCT _SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES {
+    BYTE abVectorBuffer0[SYMCRYPT_INTERNAL_MLKEM_MAXIMUM_VECTOR_SIZE];
+    BYTE abVectorBuffer1[SYMCRYPT_INTERNAL_MLKEM_MAXIMUM_VECTOR_SIZE];
+    BYTE abPolyElementBuffer0[SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT];
+    BYTE abPolyElementBuffer1[SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT];
+    BYTE abPolyElementAccumulatorBuffer[SYMCRYPT_INTERNAL_MLKEM_SIZEOF_POLYRINGELEMENT_ACCUMULATOR];
+    union {
+        SYMCRYPT_SHAKE128_STATE shake128State;
+        SYMCRYPT_SHAKE256_STATE shake256State;
+        SYMCRYPT_SHA3_256_STATE sha3_256State;
+        SYMCRYPT_SHA3_512_STATE sha3_512State;
+    } hashState0;
+    union {
+        SYMCRYPT_SHAKE128_STATE shake128State;
+        SYMCRYPT_SHAKE256_STATE shake256State;
+        SYMCRYPT_SHA3_256_STATE sha3_256State;
+        SYMCRYPT_SHA3_512_STATE sha3_512State;
+    } hashState1;
+} SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES;
+typedef SYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES * PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES;
+
+// exposed here for KAT testing
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemEncapsulateInternal(
+    _In_    PCSYMCRYPT_MLKEMKEY                                 pkMlKemkey,
+    _Out_writes_bytes_( cbAgreedSecret )
+            PBYTE                                               pbAgreedSecret,
+            SIZE_T                                              cbAgreedSecret,
+    _Out_writes_bytes_( cbCiphertext )
+            PBYTE                                               pbCiphertext,
+            SIZE_T                                              cbCiphertext,
+    _In_reads_bytes_( SYMCRYPT_MLKEM_SIZEOF_ENCAPS_RANDOM )
+            PCBYTE                                              pbRandom,
+    _Inout_ PSYMCRYPT_MLKEM_INTERNAL_COMPUTATION_TEMPORARIES    pCompTemps );
+
+PSYMCRYPT_MLKEM_POLYELEMENT
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer );
+
+PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementAccumulatorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer );
+
+PSYMCRYPT_MLKEM_VECTOR
+SYMCRYPT_CALL
+SymCryptMlKemVectorCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer,
+                                    UINT32  nRows );
+
+PSYMCRYPT_MLKEM_MATRIX
+SYMCRYPT_CALL
+SymCryptMlKemMatrixCreate(
+    _Out_writes_bytes_( cbBuffer )  PBYTE   pbBuffer,
+                                    UINT32  cbBuffer,
+                                    UINT32  nRows );
+
+//
+// ML-KEM operations acting on individual polynomial ring elements (PolyElements)
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementMulAndAccumulate(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT            peSrc1,
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT            peSrc2,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paDst );
+//
+// ML-KEM Polynomial Ring Element multiply and add:
+//      paDst = paDst + (peSrc1 o peSrc2)
+// where:
+//  o is polynomial multiplication given sources in NTT form
+//
+// Requirements:
+//  - peSrc1 and peSrc2 must be PolyElements in ML-KEM's NTT form
+//  - paDst must be in NTT form
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemMontgomeryReduceAndAddPolyElementAccumulatorToPolyElement(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paSrc,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT             peDst );
+//
+// Montgomery reduce and add a Polynomial Ring Element Accumulator to a Polynomial Ring
+// Element, and wipe the accumulator:
+//      peDst = peDst + (paSrc ./ R)
+//      paSrc = 0
+// where:
+//  ./ is coefficient-wise division and R is Montgomery multiplier
+//
+//  - One of the following conditions must be true:
+//      - paSrc to be pre-multiplied coefficient-wise by R for addition with a canonical
+//      representation of peDst
+//      - peDst must be coefficient-wise multiplied by the same constant factor as the
+//      resulting of (paSrc ./ R) for the addition to make sense
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementMulR(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT     peDst );
+//
+// ML-KEM Polynomial Ring Element multiply each coefficient by Montgomery multiplier R
+//      peDst = peSrc .* R
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementAdd(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc1,
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc2,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT     peDst );
+//
+// ML-KEM Polynomial Ring Element addition
+//      peDst = peSrc1 + peSrc2
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementSub(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc1,
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc2,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT     peDst );
+//
+// ML-KEM Polynomial Ring Element subtract:
+//      peDst = peSrc1 - peSrc2
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementNTT(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT peSrc );
+//
+// ML-KEM Polynomial Ring Element NTT:
+//      peSrc = NTT(peSrc) per FIPS 203
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementINTTAndMulR(
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT peSrc );
+//
+// ML-KEM Polynomial Ring Element INTT:
+//      peSrc = NTTinverse(peSrc) .* R
+// where .* is coefficient-wise multiplication and R is Montgomery multiplier
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementCompressAndEncode(
+    _In_    PCSYMCRYPT_MLKEM_POLYELEMENT    peSrc,
+            UINT32                          nBitsPerCoefficient,
+    _Out_writes_bytes_(nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8))
+            PBYTE                           pbDst );
+//
+// ML-KEM Polynomial Ring Element Compress and Encode.
+//
+// Each coefficient in the ring element is Compressed to nBitsPerCoefficient using
+// rounding logic specified in FIPS 203, and the coefficients are encoded
+// (packed together densely as 256 contiguous bitfields) into the pbDst buffer.
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementDecodeAndDecompress(
+    _In_reads_bytes_(nBitsPerCoefficient*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8))
+            PCBYTE                      pbSrc,
+            UINT32                      nBitsPerCoefficient,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT peDst );
+//
+// ML-KEM Polynomial Ring Element Decode and Decompress.
+//
+// The pbSrc buffer is interpreted as an encoded ring element, with each coefficient
+// being represented by nBitsPerCoefficient. The resulting ring element is written to
+// peDst.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementSampleNTTFromShake128(
+    _Inout_ PSYMCRYPT_SHAKE128_STATE    pState,
+    _Out_   PSYMCRYPT_MLKEM_POLYELEMENT peDst );
+//
+// Generates an ML-KEM Polynomial Ring Element in NTT form by extracting bytes from
+// pre-instantiated SHAKE128 state.
+//
+// NOTE: we pass the SHAKE state to this function because we do not know up front
+// how many bytes need to be extracted.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemPolyElementSampleCBDFromBytes(
+    _In_reads_bytes_(eta*2*(SYMCRYPT_MLWE_POLYNOMIAL_COEFFICIENTS / 8) + 1)
+                    PCBYTE                      pbSrc,
+    _In_range_(2,3) UINT32                      eta,
+    _Out_           PSYMCRYPT_MLKEM_POLYELEMENT peDst );
+//
+// Generates an ML-KEM Polynomial Ring Element in centered binomial distribution
+// from input byte array.
+// Each coefficient is generated using 2*eta bits.
+//
+
+
+//
+// ML-KEM operations acting on Linear Algebra objects
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemMatrixTranspose(
+    _Inout_ PSYMCRYPT_MLKEM_MATRIX  pmSrc );
+//
+// pmSrc = transpose(pmSrc)
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemMatrixVectorMontMulAndAdd(
+    _In_    PCSYMCRYPT_MLKEM_MATRIX                 pmSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR                 pvSrc2,
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR                  pvDst,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp );
+//
+// pvDst = ((pmSrc1 o pvSrc2) ./ R) + pvDst
+//
+// Remarks:
+//  - paTmp is used internally for temporary storage, it is wiped before and after use
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorMontDotProduct(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR                 pvSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR                 pvSrc2,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT             peDst,
+    _Inout_ PSYMCRYPT_MLKEM_POLYELEMENT_ACCUMULATOR paTmp );
+//
+// peDst = (pvSrc1 o pvSrc2) ./ R
+//
+// Remarks:
+//  - paTmp is used internally for temporary storage, it is wiped before and after use
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorSetZero(
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR  pvSrc );
+//
+// pvSrc = 0
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorMulR(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc,
+    _Out_   PSYMCRYPT_MLKEM_VECTOR  pvDst );
+//
+// pvDst = pvSrc .* R
+//
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorAdd(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc2,
+    _Out_   PSYMCRYPT_MLKEM_VECTOR  pvDst );
+//
+// pvDst = pvSrc1 + pvSrc2
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorSub(
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc1,
+    _In_    PCSYMCRYPT_MLKEM_VECTOR pvSrc2,
+    _Out_   PSYMCRYPT_MLKEM_VECTOR  pvDst );
+//
+// pvDst = pvSrc1 - pvSrc2
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorNTT(
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR  pvSrc );
+//
+// pvSrc = NTT(peSrc) per FIPS 203
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorINTTAndMulR(
+    _Inout_ PSYMCRYPT_MLKEM_VECTOR  pvSrc );
+//
+// pvSrc = NTTinverse(pvSrc) .* R
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemVectorCompressAndEncode(
+    _In_                        PCSYMCRYPT_MLKEM_VECTOR pvSrc,
+                                UINT32                  nBitsPerCoefficient,
+    _Out_writes_bytes_(cbDst)   PBYTE                   pbDst,
+                                SIZE_T                  cbDst );
+//
+// See ML-KEM Polynomial Ring Element Compress and Encode
+//
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptMlKemVectorDecodeAndDecompress(
+    _In_reads_bytes_(cbSrc) PCBYTE                  pbSrc,
+                            SIZE_T                  cbSrc,
+                            UINT32                  nBitsPerCoefficient,
+    _Out_                   PSYMCRYPT_MLKEM_VECTOR  pvDst );
+//
+// See ML-KEM Polynomial Ring Element Decode and Decompress
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptMlKemkeyWipePrivateState(
+    _Inout_ PSYMCRYPT_MLKEMKEY  pkMlKemkey );
+//
+// Wipes the ML-KEM key's private state.
+//
diff --git a/libs/symcrypt/lib/scsTools.c b/libs/symcrypt/lib/scsTools.c
new file mode 100644
index 000000000000..3787d36adf68
--- /dev/null
+++ b/libs/symcrypt/lib/scsTools.c
@@ -0,0 +1,367 @@
+//
+// scsTools.c   Support tools for writing side-channel safe code
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// This code needs to process data in words, and we'd like to use 32-bit words on 32-bit
+// architectures and 64-bit words on 64-bit architectures. So we use NATIVE_UINT & friends.
+//
+
+// Buffer limits for SymCryptScsRotateBuffer
+#define MIN_BUFFER_SIZE (32)
+
+//
+// Masking functions
+// Masking functions can be more efficient if the inputs are restricted to values that can
+// be represented in the signed data types.
+// This is why we have some functions that take 31-bit inputs.
+//
+
+// 31-bit inputs
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32IsNonzeroU31( UINT32 v )
+{
+    SYMCRYPT_ASSERT( v < (1UL<<31) );
+    return (-(INT32) v) >> 31;
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32IsZeroU31( UINT32 v )
+{
+    return ~SymCryptMask32IsNonzeroU31( v );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32NeqU31( UINT32 a, UINT32 b )
+{
+    SYMCRYPT_ASSERT( a < (1UL<<31) );
+    SYMCRYPT_ASSERT( b < (1UL<<31) );
+
+    return SymCryptMask32IsNonzeroU31( a ^ b );
+}
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32LtU31( UINT32 a, UINT32 b )
+{
+    SYMCRYPT_ASSERT( a < (1UL<<31) );
+    SYMCRYPT_ASSERT( b < (1UL<<31) );
+
+    // Casting to INT32 is defined as a and b are < 2^31
+    return ((INT32) a - (INT32) b) >> 31;
+}
+
+
+// 32-bit inputs
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMask32EqU32( UINT32 a, UINT32 b )
+{
+    return ~(UINT32) ( (-(INT64)(a^b)) >> 32);
+}
+
+
+// Other helper functions
+SIZE_T
+SYMCRYPT_CALL
+SymCryptRoundUpPow2Sizet( SIZE_T v )
+{
+    SIZE_T res;
+
+    SYMCRYPT_ASSERT( v <= (SIZE_T_MAX / 2) + 1);
+    // If v is very large, then the result res might overflow.
+    // As SIZE_T is an unsigned type, the overflow is defined to
+    // be modulo 2^n for some n, and therefore we'll get res==0
+    // which will terminate the loop.
+
+    res = 1;
+    while( res < v )
+    {
+        res += res;
+
+        // Catch any overflows; should never happen but break to avoid infinite loop
+        if( res == 0 )
+        {
+            break;
+        }
+    }
+
+    return res;
+}
+
+
+//
+// Copy data
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsCopy(
+    _In_reads_( cbDst )     PCBYTE  pbSrc,
+                            SIZE_T  cbSrc,
+    _Out_writes_( cbDst )   PBYTE   pbDst,
+                            SIZE_T  cbDst )
+// Copy cbSrc bytes of pbSrc into pbDst without revealing cbSrc
+// through side channels.
+// - pbSrc/cbSrc: buffer to copy data from
+// - pbDst/cbDst: buffer that receives the data
+// Equivalent to:
+//      n = min( cbSrc, cbDst )
+//      pbDst[ 0.. n-1 ] = pbSrc[ 0 .. n - 1 ]
+// cbSrc is protected from side-channels; cbDst is public.
+// Note that pbSrc must be cbDst bytes long, not cbSrc bytes.
+{
+    UINT32 i;
+
+    SYMCRYPT_ASSERT( cbSrc <= (1UL << 31) && cbDst <= (1UL << 31) );
+
+    // Loop over the destination buffer and update each byte with the source data (if appropriate)
+    // We round-robin loop over the source buffer
+    for( i = 0; i < cbDst; i++ )
+    {
+        pbDst[ i ] ^= (pbSrc[ i ] ^ pbDst[ i ]) & SymCryptMask32LtU31( i, (UINT32) cbSrc );
+    }
+}
+
+
+//
+// Buffer rotation
+// To recover a message from an encoding with variable data position we have to do a copy from a
+// variable memory location. But our memory access pattern cannot depend on the secret location.
+// This code rotates a given buffer by a variable # bytes without revealing the shift amount.
+//
+// For efficiency we do this using NATIVE_UINT values so that we get the best performance on each platform.
+//
+// The first step is to rotate the array between 0 and NATIVE_BYTES-1 bytes to get the proper word alignment.
+// After that we only have to rotate the words.
+// We do this using a sequence of swaps.
+// Notation:
+//  W[i]    array of words, 0 <= i < n where n is the # words, a power of 2.
+//  s       Rotation amount (to the left). The value in W[s] at the start should appear in W[0] at the end.
+//
+// We use masked swaps as they seem to be more efficient then masked multiplexers.
+// We can split this problem down recursively
+//
+// Function Rotate( W, n, s)
+//  - Rotate W[0..n/2-1] by s mod n/2
+//  - Rotate W[n/2..n-1] by s mod n/2
+// for i in 0..n/2-1:
+//      swap W[i] and W[i+n/2] if (i+s) % n >= n/2
+//
+// After the two half-sized rotates, each word is in the right position modulo n/2, so all that needs to be
+// done in possibly swap (W[i],W[i+n/2]) pairs.
+// Let W' be the array after the half-sized rotates. We have
+// W'[i] = W[ (i + s) % (n/2) ] for i in 0..n/2
+// In the final array W'' we should have W''[i] = W[ (i+s)%n ]
+// So W''[i] = W'[i] when (i+s) % n = (i+s) % n/2 which is equivalent to (i+s)%n < n/2.
+//
+// We turn this into a non-recursive algorithm.
+// First we do rotations on 2 words,
+// then the fixups to make it 4-word rotations,
+// then on to 8-words, etc.
+// At each level we compute the masks for the swaps once, and re-use them for each copy
+// As a further optimization, we merge the 1st and 2nd pass into one to reduce the # read/writes
+//
+// We avoid using / and % throughout to avoid any time-dependent instructions.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptScsRotateBuffer(
+    _Inout_updates_( cbBuffer ) PBYTE   pbBuffer,
+                                SIZE_T  cbBuffer,
+                                SIZE_T  lshift )
+{
+    NATIVE_UINT * pBuf;
+    UINT32 n;
+    UINT32 a;
+    UINT32 b;
+    UINT32 i;
+    UINT32 j;
+    UINT32 blockSize;
+    UINT32 blockSizeLog;
+    UINT32 blockSizeLimit;
+
+    NATIVE_UINT V;
+    NATIVE_UINT T;
+    NATIVE_UINT A;
+    NATIVE_UINT B;
+    NATIVE_UINT C;
+    NATIVE_UINT D;
+    NATIVE_UINT M;
+    NATIVE_UINT M0;
+    NATIVE_UINT M1;
+
+    NATIVE_UINT Mask[ 16 ];      // Size must be a power of 2
+
+    SYMCRYPT_ASSERT( (cbBuffer & (cbBuffer - 1)) == 0 && cbBuffer >= MIN_BUFFER_SIZE );
+    SYMCRYPT_ASSERT( lshift < cbBuffer );
+
+    pBuf = (NATIVE_UINT *) pbBuffer;
+    n = (UINT32)cbBuffer / NATIVE_BYTES;
+
+    // First a rotate left by lshift % NATIVE_BYTES
+    // This is more complex because shifting by NATIVE_BITS is not a defined operation, and behavior is different
+    // on different CPUs.
+
+    // Compute the shift amounts & mask
+    // M = 0 if lshift % NATIVE_BYTES == 0, -1 otherwise
+    a = 8 * (lshift & (NATIVE_BYTES-1));            // Core shift
+    M = (-(NATIVE_INT)a) >> (NATIVE_BITS - 1);      // mask
+    b = (NATIVE_BITS - a) & (UINT32) M;         // complementary shift, or 0 if it would be equal to NATIVE_BITS
+
+    i = n;
+    V = pBuf[0];
+    do{
+        // Loop invariant: i > 0 && v = pBuf[i] from before any changes;
+        i--;
+        T = pBuf[i];
+        pBuf[i] = T >> a | ((V << b) & M);
+        V = T;
+    } while( i > 0 );
+
+    // Now that the rotation is word-aligned, we can start our word rotation
+    lshift >>= NATIVE_BYTES_LOG2;           // convert to # words to rotate.
+
+    // We know we have at least 4 words, so we start with a pass do do 4-word rotations
+    SYMCRYPT_ASSERT( n >= 4 );
+
+    M = -(NATIVE_INT)(lshift & 1);
+    M0 = -(NATIVE_INT)( ((lshift + 0) >> 1) & 1 );      // s + 0 mod 4 >= 2
+    M1 = -(NATIVE_INT)( ((lshift + 1) >> 1) & 1 );      // s + 1 mod 4 >= 2
+
+    for( i=0; i<n; i+=4 )
+    {
+        A = pBuf[i];
+        B = pBuf[i+1];
+        C = pBuf[i+2];
+        D = pBuf[i+3];
+
+        T = (A ^ B) & M;
+        A ^= T;
+        B ^= T;
+
+        T = (C ^ D) & M;
+        C ^= T;
+        D ^= T;
+
+        T = (A ^ C) & M0;
+        A ^= T;
+        C ^= T;
+
+        T = (B ^ D) & M1;
+        B ^= T;
+        D ^= T;
+
+        pBuf[i  ] = A;
+        pBuf[i+1] = B;
+        pBuf[i+2] = C;
+        pBuf[i+3] = D;
+    }
+
+    // Do the swaps using the mask array
+    blockSize = 4;  // size of rotated blocks
+    blockSizeLog = 2;
+
+    //
+    // Using the mask array is beneficial as long as the array is used twice or more
+    // Each swap loop processes 2 * blockSize of data, so the block size should never
+    // be larger than n/4
+    blockSizeLimit = SYMCRYPT_MIN( SYMCRYPT_ARRAY_SIZE( Mask ), n/4 );
+    while( blockSize <= blockSizeLimit )
+    {
+        // Compute the masks for this level
+        for( i=0; i<blockSize; i++ )
+        {
+            Mask[i] =-(NATIVE_INT)( ((i + lshift) >> blockSizeLog) & 1);
+        }
+
+        // Now swap the elements of pairs of blocks according to the masks
+        for( i=0; i < n; i += 2 * blockSize )
+        {
+            for( j=0; j < blockSize; j++ )
+            {
+                A = pBuf[ i + j ];
+                B = pBuf[ i + j + blockSize ];
+                T = (A ^ B) & Mask[j];
+                A ^= T;
+                B ^= T;
+                pBuf[ i + j ] = A;
+                pBuf[ i + j + blockSize ] = B;
+            }
+        }
+        blockSize *= 2;
+        blockSizeLog += 1;
+    }
+
+    // Do the rest without using a mask array, either because we are only
+    // going to use each mask value once, or because we don't have a large-enough
+    // array
+    while( blockSize < n  )
+    {
+        // Now swap the elements of pairs of blocks according to the masks
+        for( i=0; i < n; i += 2 * blockSize )
+        {
+            for( j=0; j < blockSize; j++ )
+            {
+                M = -(NATIVE_INT)( ((j + lshift) >> blockSizeLog) & 1);
+                A = pBuf[ i + j ];
+                B = pBuf[ i + j + blockSize ];
+                T = (A ^ B) & M;
+                A ^= T;
+                B ^= T;
+                pBuf[ i + j ] = A;
+                pBuf[ i + j + blockSize ] = B;
+            }
+        }
+        blockSize *= 2;
+        blockSizeLog += 1;
+    }
+
+}
+
+
+//
+// Map values in a side-channel safe way, typically used for mapping error codes.
+//
+// (pcMap, nMap) point to an array of nMap entries of type SYMCRYPT_UINT32_MAP;
+// each entry specifies a single mapping. If u32Input matches the
+// 'from' field, the return value will be the 'to' field value.
+// If u32Input is not equal to any 'from' field values, the return value is u32Default.
+// Both u32Input and the return value are treated as secrets w.r.t. side channels.
+//
+// If multiple map entries have the same 'from' field value, then the return value
+// is one of the several 'to' field values; which one is not defined.
+//
+// This function is particularly useful when mapping error codes in situations where
+// the actual error cannot be revealed through side channels.
+//
+
+UINT32
+SYMCRYPT_CALL
+SymCryptMapUint32(
+                        UINT32                  u32Input,
+                        UINT32                  u32Default,
+    _In_reads_(nMap)    PCSYMCRYPT_UINT32_MAP   pcMap,
+                        SIZE_T                  nMap)
+{
+    UINT32 mask;
+    UINT32 u32Output    = u32Default;
+
+    for (SIZE_T i = 0; i < nMap; ++i)
+    {
+        mask = SymCryptMask32EqU32(u32Input, pcMap[i].from);
+        u32Output ^= (u32Output ^ pcMap[i].to) & mask;
+    }
+
+    return u32Output;
+}
diff --git a/libs/symcrypt/lib/selftest.c b/libs/symcrypt/lib/selftest.c
new file mode 100644
index 000000000000..4921b343a590
--- /dev/null
+++ b/libs/symcrypt/lib/selftest.c
@@ -0,0 +1,17 @@
+//
+// selftest.c
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+const BYTE SymCryptTestMsg3 [ 3] = { 'a', 'b', 'c' };
+
+const BYTE SymCryptTestKey32[32] = {
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+};
+
+const BYTE SymCryptTestMsg16[16] = {
+    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+};
diff --git a/libs/symcrypt/lib/session.c b/libs/symcrypt/lib/session.c
new file mode 100644
index 000000000000..71f59549d9b7
--- /dev/null
+++ b/libs/symcrypt/lib/session.c
@@ -0,0 +1,377 @@
+//
+// session.c   code for Session API implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionSenderInit(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT32              senderId,
+            UINT32              flags )
+{
+    // Make sure we only specify the correct flags
+    if (flags != 0)
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    pSession->replayState.messageNumber = 0;
+    pSession->senderId                  = senderId;
+    pSession->flags                     = SYMCRYPT_FLAG_SESSION_ENCRYPT;
+    pSession->pMutex                    = NULL;
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionReceiverInit(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT32              senderId,
+            UINT32              flags )
+{
+    PVOID pMutex = NULL;
+
+    // Make sure we only specify the correct flags
+    if (flags != 0)
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+#if SYMCRYPT_CPU_AMD64
+    if ( !SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_CMPXCHG16B ) )
+    {
+        pMutex = SymCryptCallbackAllocateMutexFastInproc();
+        if( pMutex == NULL )
+        {
+            return SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        }
+    }
+#elif SYMCRYPT_CPU_ARM64 // Arm64 always has support for CAS128 - so never need a lock
+#else // 32b and generic platforms will always need to use a lock
+    pMutex = SymCryptCallbackAllocateMutexFastInproc();
+    if( pMutex == NULL )
+    {
+        return SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+    }
+#endif
+    pSession->pMutex = pMutex;
+
+    // This represents that the message numbers 1-64 inclusive have not yet been successfully use in decryption
+    pSession->replayState.replayMask    = 0;
+    pSession->replayState.messageNumber = 64;
+
+    pSession->senderId      = senderId;
+    pSession->flags         = 0;
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSessionDestroy(_Inout_ PSYMCRYPT_SESSION   pSession )
+{
+    if ( pSession->pMutex != NULL )
+    {
+        SymCryptCallbackFreeMutexFastInproc(pSession->pMutex);
+    }
+    SymCryptWipeKnownSize(pSession, sizeof(*pSession));
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionGcmEncrypt(
+    _Inout_                         PSYMCRYPT_SESSION           pSession,
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _Out_writes_( cbTag )           PBYTE                       pbTag,
+                                    SIZE_T                      cbTag,
+    _Out_opt_                       PUINT64                     pu64MessageNumber )
+{
+    BYTE    nonce[12];
+    UINT64  messageNumber;
+
+    if ( (pSession->flags & SYMCRYPT_FLAG_SESSION_ENCRYPT) != SYMCRYPT_FLAG_SESSION_ENCRYPT )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    messageNumber = SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(&pSession->replayState.messageNumber, 1);
+
+    // We do not allow messageNumber to go above some maximum value (currently 2^64 - 2^32)
+    if ( messageNumber > SYMCRYPT_SESSION_MAX_MESSAGE_NUMBER )
+    {
+        // Decrement the session messageNumber on the error path so that this session will continue
+        // to only generate errors
+        SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(&pSession->replayState.messageNumber, -1ll);
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    SYMCRYPT_STORE_MSBFIRST32(&nonce[0], pSession->senderId);
+    SYMCRYPT_STORE_MSBFIRST64(&nonce[4], messageNumber);
+
+    SymCryptGcmEncrypt(
+        pExpandedKey,
+        nonce,
+        sizeof(nonce),
+        pbAuthData,
+        cbAuthData,
+        pbSrc,
+        pbDst,
+        cbData,
+        pbTag,
+        cbTag);
+
+    if( pu64MessageNumber != NULL )
+    {
+        *pu64MessageNumber = messageNumber;
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+// Convenience function used in SymCryptSessionDecryptUpdateState*
+//
+// Given an observedState check whether messageNumber represents a replay
+// If it does, return SYMCRYPT_SESSION_REPLAY_FAILURE
+// Otherwise, set desiredState to the observedState updated to represent messageNumber has been seen
+// and return SYMCRYPT_NO_ERROR
+FORCEINLINE
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionDecryptComputeDesiredReplayState(
+    _In_    PCSYMCRYPT_SESSION_REPLAY_STATE observedState,
+    _Out_   PSYMCRYPT_SESSION_REPLAY_STATE  desiredState,
+            UINT64                          messageNumber )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT64 messageMask;
+    UINT64 shiftAmount;
+    UINT64 shiftedMask;
+
+    if ( messageNumber > observedState->messageNumber )
+    {
+        // The observed message number is behind messageNumber that we want to mark successful
+        // Shift replayMask appropriately to preserve previously seen message numbers
+        shiftedMask = 0;
+        shiftAmount = messageNumber - observedState->messageNumber;
+        if( shiftAmount < 64 )
+        {
+            shiftedMask = observedState->replayMask << shiftAmount;
+        }
+        // Mark messageNumber as seen in the replayMask
+        desiredState->replayMask = shiftedMask | 1;
+        desiredState->messageNumber = messageNumber;
+    }
+    else if ( messageNumber <= observedState->messageNumber - 64 )
+    {
+        // The observed message number is too far ahead of messageNumber
+        // We cannot hope to succeed
+        scError = SYMCRYPT_SESSION_REPLAY_FAILURE;
+        goto cleanup;
+    }
+    else
+    {
+        // The observed message number is ahead of or equal to messageNumber
+        // Check if messageNumber has already been used
+        messageMask = 1ull << (observedState->messageNumber - messageNumber); // shiftAmount is in [0, 63]
+        if ((messageMask & observedState->replayMask) == messageMask)
+        {
+            scError = SYMCRYPT_SESSION_REPLAY_FAILURE;
+            goto cleanup;
+        }
+        // This is first time we have seen messageNumber - set the replayMask bit appropriately
+        desiredState->replayMask = observedState->replayMask | messageMask;
+        desiredState->messageNumber = observedState->messageNumber;
+    }
+
+cleanup:
+    return scError;
+}
+
+#if SYMCRYPT_USE_CAS128
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionDecryptUpdateStateCAS128(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT64              messageNumber )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_SESSION_REPLAY_STATE expectedState;
+    SYMCRYPT_SESSION_REPLAY_STATE desiredState;
+
+    // Non-atomic read of pSession's replayState. We can use this initial value as a good guess of
+    // the expected state, but we cannot fail based on it (as replayMask and messageNumber may have
+    // been read from different writes to the replayState)
+    expectedState = pSession->replayState;
+
+    // Compute desiredState based on non-atomic read
+    // If it looks like this may be a replay, ensure we fail first CAS so we recompute desiredState
+    // from an atomic read in the loop below
+    if ( SymCryptSessionDecryptComputeDesiredReplayState(&expectedState, &desiredState, messageNumber) != SYMCRYPT_NO_ERROR )
+    {
+        // pSession->replayState.messageNumber can never take the value 0 as it starts at 64 and is
+        // monotonic increasing
+        expectedState.messageNumber = 0;
+    }
+
+    while( scError == SYMCRYPT_NO_ERROR )
+    {
+        if ( SymCryptAtomicCas128Relaxed((PUINT64)&pSession->replayState, (PUINT64)&expectedState, (PUINT64)&desiredState) )
+        {
+            // We succeeded in updating pSession->replayState and are done
+            break;
+        }
+
+        // Compute new desiredState based on atomic read from CAS failure
+        // We may now correctly fall out of loop if a replay is detected
+        scError = SymCryptSessionDecryptComputeDesiredReplayState(&expectedState, &desiredState, messageNumber);
+    }
+
+    return scError;
+}
+
+#endif
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionDecryptUpdateStateLock(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT64              messageNumber )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_SESSION_REPLAY_STATE desiredState;
+
+    if ( pSession->pMutex == NULL )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    // Check whether we are definitely too late to proceed before attempting to acquire mutex
+    // Do not need atomic read of full replayState here, but do need atomic 64b read of
+    // pSession->replayState.messageNumber
+    if ( messageNumber <= (UINT64) SYMCRYPT_ATOMIC_LOAD64_RELAXED(&pSession->replayState.messageNumber) - 64 )
+    {
+        return SYMCRYPT_SESSION_REPLAY_FAILURE;
+    }
+
+    SymCryptCallbackAcquireMutexFastInproc(pSession->pMutex);
+    //////
+    // !!! Do not return until we have called SymCryptCallbackReleaseMutexFastInproc !!!
+    //////
+
+    scError = SymCryptSessionDecryptComputeDesiredReplayState(&pSession->replayState, &desiredState, messageNumber);
+    if ( scError == SYMCRYPT_NO_ERROR )
+    {
+        pSession->replayState = desiredState;
+    }
+
+    SymCryptCallbackReleaseMutexFastInproc(pSession->pMutex);
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionDecryptUpdateState(
+    _Inout_ PSYMCRYPT_SESSION   pSession,
+            UINT64              messageNumber )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+#if SYMCRYPT_CPU_AMD64
+    if ( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_CMPXCHG16B ) )
+    {
+        scError = SymCryptSessionDecryptUpdateStateCAS128( pSession, messageNumber );
+    }
+    else
+    {
+        scError = SymCryptSessionDecryptUpdateStateLock( pSession, messageNumber );
+    }
+#elif SYMCRYPT_CPU_ARM64 // Arm64 always has support for CAS128 (possibly via LDXP + STXP)
+    scError = SymCryptSessionDecryptUpdateStateCAS128( pSession, messageNumber );
+#else // 32b and generic platforms will always need to use a lock
+    scError = SymCryptSessionDecryptUpdateStateLock( pSession, messageNumber );
+#endif
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSessionGcmDecrypt(
+    _Inout_                         PSYMCRYPT_SESSION           pSession,
+                                    UINT64                      messageNumber,
+    _In_                            PCSYMCRYPT_GCM_EXPANDED_KEY pExpandedKey,
+    _In_reads_opt_( cbAuthData )    PCBYTE                      pbAuthData,
+                                    SIZE_T                      cbAuthData,
+    _In_reads_( cbData )            PCBYTE                      pbSrc,
+    _Out_writes_( cbData )          PBYTE                       pbDst,
+                                    SIZE_T                      cbData,
+    _In_reads_( cbTag )             PCBYTE                      pbTag,
+                                    SIZE_T                      cbTag )
+{
+    BYTE    nonce[12];
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if ( (pSession->flags & SYMCRYPT_FLAG_SESSION_ENCRYPT) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check for messageNumbers which are too high or not valid
+    if ( (messageNumber > SYMCRYPT_SESSION_MAX_MESSAGE_NUMBER) || (messageNumber == 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Check whether we are definitely too late to proceed before attempting to acquire mutex
+    // Do not need atomic read of full replayState here, but do need atomic 64b read of
+    // pSession->replayState.messageNumber
+    if ( messageNumber <= (UINT64) SYMCRYPT_ATOMIC_LOAD64_RELAXED(&pSession->replayState.messageNumber) - 64 )
+    {
+        scError = SYMCRYPT_SESSION_REPLAY_FAILURE;
+        goto cleanup;
+    }
+
+    SYMCRYPT_STORE_MSBFIRST32(&nonce[0], pSession->senderId);
+    SYMCRYPT_STORE_MSBFIRST64(&nonce[4], messageNumber);
+
+    scError = SymCryptGcmDecrypt(
+        pExpandedKey,
+        nonce,
+        sizeof(nonce),
+        pbAuthData,
+        cbAuthData,
+        pbSrc,
+        pbDst,
+        cbData,
+        pbTag,
+        cbTag);
+
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup; // wipes pbDst twice, but we don't care about performance in the error case
+    }
+
+    scError = SymCryptSessionDecryptUpdateState(pSession, messageNumber);
+
+cleanup:
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptWipe( pbDst, cbData );
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/sha1.c b/libs/symcrypt/lib/sha1.c
new file mode 100644
index 000000000000..90711a9bfb30
--- /dev/null
+++ b/libs/symcrypt/lib/sha1.c
@@ -0,0 +1,472 @@
+//
+// Sha1.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+// This revised implementation is based on the older one in RSA32LIB by
+// Scott Field and Dan Shumow. It is not based on any 3rd party code.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+const SYMCRYPT_HASH SymCryptSha1Algorithm_default = {
+    &SymCryptSha1Init,
+    &SymCryptSha1Append,
+    &SymCryptSha1Result,
+    &SymCryptSha1AppendBlocks,
+    &SymCryptSha1StateCopy,
+    sizeof( SYMCRYPT_SHA1_STATE ),
+    SYMCRYPT_SHA1_RESULT_SIZE,
+    SYMCRYPT_SHA1_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA1_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA1_STATE, chain ),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha1Algorithm = &SymCryptSha1Algorithm_default;
+
+
+//
+// The round constants used by SHA-1
+//
+static const UINT32 Sha1K[4] = {
+    0x5a827999UL, 0x6ed9eba1UL, 0x8f1bbcdcUL, 0xca62c1d6UL,
+};
+
+//
+// Initial state
+//
+static const UINT32 sha1InitialState[5] = {
+    0x67452301UL,
+    0xefcdab89UL,
+    0x98badcfeUL,
+    0x10325476UL,
+    0xc3d2e1f0UL,
+};
+
+//
+// SymCryptSha1
+//
+#define ALG     SHA1
+#define Alg     Sha1
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+
+
+//
+// SymCryptSha1Init
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Init( _Out_ PSYMCRYPT_SHA1_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthL = 0;
+    pState->dataLengthH = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &sha1InitialState[0], sizeof( sha1InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+//
+// SymCryptSha1Append
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Append(
+    _Inout_                 PSYMCRYPT_SHA1_STATE    pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData )
+{
+    SymCryptHashAppendInternal( SymCryptSha1Algorithm, (PSYMCRYPT_COMMON_HASH_STATE)pState, pbData, cbData );
+}
+
+
+//
+// SymCryptSha1Result
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Result(
+    _Inout_                                     PSYMCRYPT_SHA1_STATE    pState,
+    _Out_writes_( SYMCRYPT_SHA1_RESULT_SIZE )   PBYTE                   pbResult )
+{
+    UINT32 bytesInBuffer;
+    SIZE_T tmp;
+
+    //
+    // SHA-1 uses almost the MD4 padding, except that the length in the padding is stored
+    // MSBFirst, rather than LSBFirst.
+    // As SHA-256 has a dedicated (fast) padding anyway, there is no gain to create a
+    // common padding routine for SHA-1 as it wouldn't be shared by anyone right now.
+    //
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    bytesInBuffer = (UINT32)(pState->bytesInBuffer);
+
+    //
+    // The buffer is never completely full, so we can always put the first
+    // padding byte in.
+    //
+    pState->buffer[bytesInBuffer++] = 0x80;
+
+    if( bytesInBuffer > 64-8 ) {
+        //
+        // No room for the rest of the padding. Pad with zeroes & process block
+        // bytesInBuffer is at most 64, so we do not have an integer underflow
+        //
+        memset( &pState->buffer[bytesInBuffer], 0, 64-bytesInBuffer );
+        SymCryptSha1AppendBlocks( &pState->chain, pState->buffer, 64, &tmp );
+        bytesInBuffer = 0;
+    }
+
+    //
+    // Set rest of padding
+    // At this point bytesInBuffer <= 64-8, so we don't have an underflow
+    // We wipe to the end of the buffer as it is 16-aligned,
+    // and it is faster to wipe to an aligned point
+    //
+    memset( &pState->buffer[bytesInBuffer], 0, 64-bytesInBuffer );
+    SYMCRYPT_STORE_MSBFIRST64( &pState->buffer[64-8], pState->dataLengthL * 8 );
+
+    //
+    // Process the final block
+    //
+    SymCryptSha1AppendBlocks( &pState->chain, pState->buffer, 64, &tmp );
+
+    //
+    // Write the output in the correct byte order
+    //
+    SymCryptUint32ToMsbFirst( &pState->chain.H[0], pbResult, 5 );
+
+    //
+    // Wipe & re-initialize
+    // We have to wipe the whole state because the Init call
+    // might be optimized away by a smart compiler.
+    //
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+    SymCryptSha1Init( pState );
+}
+
+
+//
+// For documentation on these function see FIPS 180-2
+//
+// CH, MAJ and PARITY are the functions Ch, Maj, and Parity from the standard.
+//
+//#define CH( x, y, z )     (((x) & (y)) ^ ((~(x)) & (z)))
+//#define MAJ( x, y, z )    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define MAJ( x, y, z )  ((((x) | (y)) & (z) ) | ((x) & (y)))
+#define CH( x, y, z )  ((((z) ^ (y)) & (x)) ^ (z))
+
+#define PARITY( x, y, z ) ((x) ^ (y) ^ (z) )
+
+
+//
+// The values a-e are stored in an array called ae.
+// We have unrolled the code completely. This makes both the indices into
+// the ae array constant, and it makes the message addressing constant.
+//
+
+//
+// Initial round macro
+//
+// r is the round number
+// ae[(r+0)%5] = e;
+// ae[(r+1)%5] = d;
+// ae[(r+2)%5] = c;
+// ae[(r+3)%5] = b;
+// ae[(r+4)%5] = a;
+// After that incrementing the round number will automatically map a->b, b->c, etc.
+//
+
+//
+// The core round routine (excluding the message schedule)
+//
+// In more readable form this macro does the following:
+//      e = ROL(a,5) + F(b,c,d) + e + K[r/20] + W[round]
+//      b = ROL( b, 30 )
+//
+
+#define CROUND( a, b, c, d, e, r, F ) {\
+    W[r%16] = Wt; \
+    e += ROL32( a, 5 ) + F(b, c, d) + Sha1K[r/20] + Wt;\
+    b = ROR32( b, 2 );\
+}
+
+#define IROUND( a, b, c, d, e, r, F ) { \
+    Wt = SYMCRYPT_LOAD_MSBFIRST32( &pbData[ 4*r ] ); \
+    CROUND( a, b, c, d, e, r, F ); \
+}
+
+//
+// Subsequent rounds.
+// This is the same as the IROUND except that it adds the message schedule,
+// and takes the message word from the intermediate
+//
+#define FROUND( a, b, c, d, e, r, F ) { \
+    Wt = ROL32( W[(r+13)%16] ^ W[(r+8)%16] ^ W[(r+2)%16] ^ W[r%16], 1 );\
+    CROUND( a, b, c, d, e, r, F ); \
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1AppendBlocks(
+    _Inout_                 SYMCRYPT_SHA1_CHAINING_STATE *  pChain,
+    _In_reads_( cbData )    PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T                        * pcbRemaining )
+{
+
+    SYMCRYPT_ALIGN UINT32 W[16];
+    UINT32 A, B, C, D, E;
+    UINT32 Wt;
+
+    A = pChain->H[0];
+    B = pChain->H[1];
+    C = pChain->H[2];
+    D = pChain->H[3];
+    E = pChain->H[4];
+
+    while( cbData >= 64 )
+    {
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND(  A, B, C, D, E,  0, CH );
+        IROUND(  E, A, B, C, D,  1, CH );
+        IROUND(  D, E, A, B, C,  2, CH );
+        IROUND(  C, D, E, A, B,  3, CH );
+        IROUND(  B, C, D, E, A,  4, CH );
+        IROUND(  A, B, C, D, E,  5, CH );
+        IROUND(  E, A, B, C, D,  6, CH );
+        IROUND(  D, E, A, B, C,  7, CH );
+        IROUND(  C, D, E, A, B,  8, CH );
+        IROUND(  B, C, D, E, A,  9, CH );
+        IROUND(  A, B, C, D, E, 10, CH );
+        IROUND(  E, A, B, C, D, 11, CH );
+        IROUND(  D, E, A, B, C, 12, CH );
+        IROUND(  C, D, E, A, B, 13, CH );
+        IROUND(  B, C, D, E, A, 14, CH );
+        IROUND(  A, B, C, D, E, 15, CH );
+
+        //
+        // Full rounds (including msg expansion) from here on
+        //
+        FROUND(  E, A, B, C, D, 16, CH );
+        FROUND(  D, E, A, B, C, 17, CH );
+        FROUND(  C, D, E, A, B, 18, CH );
+        FROUND(  B, C, D, E, A, 19, CH );
+
+
+        FROUND(  A, B, C, D, E, 20, PARITY );
+        FROUND(  E, A, B, C, D, 21, PARITY );
+        FROUND(  D, E, A, B, C, 22, PARITY );
+        FROUND(  C, D, E, A, B, 23, PARITY );
+        FROUND(  B, C, D, E, A, 24, PARITY );
+        FROUND(  A, B, C, D, E, 25, PARITY );
+        FROUND(  E, A, B, C, D, 26, PARITY );
+        FROUND(  D, E, A, B, C, 27, PARITY );
+        FROUND(  C, D, E, A, B, 28, PARITY );
+        FROUND(  B, C, D, E, A, 29, PARITY );
+        FROUND(  A, B, C, D, E, 30, PARITY );
+        FROUND(  E, A, B, C, D, 31, PARITY );
+        FROUND(  D, E, A, B, C, 32, PARITY );
+        FROUND(  C, D, E, A, B, 33, PARITY );
+        FROUND(  B, C, D, E, A, 34, PARITY );
+        FROUND(  A, B, C, D, E, 35, PARITY );
+        FROUND(  E, A, B, C, D, 36, PARITY );
+        FROUND(  D, E, A, B, C, 37, PARITY );
+        FROUND(  C, D, E, A, B, 38, PARITY );
+        FROUND(  B, C, D, E, A, 39, PARITY );
+
+
+        FROUND(  A, B, C, D, E, 40, MAJ );
+        FROUND(  E, A, B, C, D, 41, MAJ );
+        FROUND(  D, E, A, B, C, 42, MAJ );
+        FROUND(  C, D, E, A, B, 43, MAJ );
+        FROUND(  B, C, D, E, A, 44, MAJ );
+        FROUND(  A, B, C, D, E, 45, MAJ );
+        FROUND(  E, A, B, C, D, 46, MAJ );
+        FROUND(  D, E, A, B, C, 47, MAJ );
+        FROUND(  C, D, E, A, B, 48, MAJ );
+        FROUND(  B, C, D, E, A, 49, MAJ );
+        FROUND(  A, B, C, D, E, 50, MAJ );
+        FROUND(  E, A, B, C, D, 51, MAJ );
+        FROUND(  D, E, A, B, C, 52, MAJ );
+        FROUND(  C, D, E, A, B, 53, MAJ );
+        FROUND(  B, C, D, E, A, 54, MAJ );
+        FROUND(  A, B, C, D, E, 55, MAJ );
+        FROUND(  E, A, B, C, D, 56, MAJ );
+        FROUND(  D, E, A, B, C, 57, MAJ );
+        FROUND(  C, D, E, A, B, 58, MAJ );
+        FROUND(  B, C, D, E, A, 59, MAJ );
+
+        FROUND(  A, B, C, D, E, 60, PARITY );
+        FROUND(  E, A, B, C, D, 61, PARITY );
+        FROUND(  D, E, A, B, C, 62, PARITY );
+        FROUND(  C, D, E, A, B, 63, PARITY );
+        FROUND(  B, C, D, E, A, 64, PARITY );
+        FROUND(  A, B, C, D, E, 65, PARITY );
+        FROUND(  E, A, B, C, D, 66, PARITY );
+        FROUND(  D, E, A, B, C, 67, PARITY );
+        FROUND(  C, D, E, A, B, 68, PARITY );
+        FROUND(  B, C, D, E, A, 69, PARITY );
+        FROUND(  A, B, C, D, E, 70, PARITY );
+        FROUND(  E, A, B, C, D, 71, PARITY );
+        FROUND(  D, E, A, B, C, 72, PARITY );
+        FROUND(  C, D, E, A, B, 73, PARITY );
+        FROUND(  B, C, D, E, A, 74, PARITY );
+        FROUND(  A, B, C, D, E, 75, PARITY );
+        FROUND(  E, A, B, C, D, 76, PARITY );
+        FROUND(  D, E, A, B, C, 77, PARITY );
+        FROUND(  C, D, E, A, B, 78, PARITY );
+        FROUND(  B, C, D, E, A, 79, PARITY );
+
+
+        pChain->H[0] = A = A + pChain->H[0];
+        pChain->H[1] = B = B + pChain->H[1];
+        pChain->H[2] = C = C + pChain->H[2];
+        pChain->H[3] = D = D + pChain->H[3];
+        pChain->H[4] = E = E + pChain->H[4];
+
+        pbData += 64;
+        cbData -= 64;
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SYMCRYPT_FORCE_WRITE32( &A, 0 );
+    SYMCRYPT_FORCE_WRITE32( &B, 0 );
+    SYMCRYPT_FORCE_WRITE32( &C, 0 );
+    SYMCRYPT_FORCE_WRITE32( &D, 0 );
+    SYMCRYPT_FORCE_WRITE32( &E, 0 );
+    SYMCRYPT_FORCE_WRITE32( &Wt, 0 );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1StateExport(
+    _In_                                                    PCSYMCRYPT_SHA1_STATE   pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA1_STATE_EXPORT_SIZE )   PBYTE                   pbBlob )
+{
+    SYMCRYPT_ALIGN SYMCRYPT_SHA1_STATE_EXPORT_BLOB    blob;           // local copy to have proper alignment.
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_SHA1_STATE_EXPORT_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) ); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_SHA1_STATE_EXPORT_SIZE;
+    blob.header.type = SymCryptBlobTypeSha1State;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+
+    SymCryptUint32ToMsbFirst( &pState->chain.H[0], &blob.chain[0], 5 );
+    blob.dataLength = pState->dataLengthL;
+    memcpy( &blob.buffer[0], &pState->buffer[0], blob.dataLength & 0x3f );
+
+    SYMCRYPT_ASSERT( (PCBYTE) &blob + sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ) == (PCBYTE) &blob.trailer );
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), &blob.trailer.checksum[0] );
+
+    memcpy( pbBlob, &blob, sizeof( blob ) );
+
+//cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) );
+    return;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha1StateImport(
+    _Out_                                               PSYMCRYPT_SHA1_STATE    pState,
+    _In_reads_bytes_( SYMCRYPT_SHA1_STATE_EXPORT_SIZE)  PCBYTE                  pbBlob )
+{
+    SYMCRYPT_ERROR                  scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_ALIGN SYMCRYPT_SHA1_STATE_EXPORT_BLOB blob;                       // local copy to have proper alignment.
+    BYTE                            checksum[8];
+
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_SHA1_STATE_EXPORT_SIZE );
+    memcpy( &blob, pbBlob, sizeof( blob ) );
+
+    if( blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_SHA1_STATE_EXPORT_SIZE ||
+        blob.header.type != SymCryptBlobTypeSha1State )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), checksum );
+    if( memcmp( checksum, &blob.trailer.checksum[0], 8 ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMsbFirstToUint32( &blob.chain[0], &pState->chain.H[0], 5 );
+    pState->dataLengthL = blob.dataLength;
+    pState->dataLengthH = 0;
+    pState->bytesInBuffer = blob.dataLength & 0x3f;
+    memcpy( &pState->buffer[0], &blob.buffer[0], pState->bytesInBuffer );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof(blob) );
+    return scError;
+}
+
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE sha1KATAnswer[ 20 ] = {
+    0xa9, 0x99, 0x3e, 0x36,
+    0x47, 0x06, 0x81, 0x6a,
+    0xba, 0x3e, 0x25, 0x71,
+    0x78, 0x50, 0xc2, 0x6c,
+    0x9c, 0xd0, 0xd8, 0x9d
+    } ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha1Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA1_RESULT_SIZE];
+
+    SymCryptSha1( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, sha1KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SHA1' );
+    }
+}
diff --git a/libs/symcrypt/lib/sha256-xmm.c b/libs/symcrypt/lib/sha256-xmm.c
new file mode 100644
index 000000000000..c181c4824a26
--- /dev/null
+++ b/libs/symcrypt/lib/sha256-xmm.c
@@ -0,0 +1,354 @@
+#include "precomp.h"
+
+#if  SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("ssse3"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("ssse3")
+#endif
+
+extern SYMCRYPT_ALIGN_AT(256) const  UINT32 SymCryptSha256K[64];
+
+
+// Endianness transformation for 4 32-bit values in an XMM register
+const SYMCRYPT_ALIGN_AT(16) UINT32 BYTE_REVERSE_32[4] = {
+    0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f,
+};
+
+// Shuffle 32-bit words in an XMM register: W3 W2 W1 W0 -> 0 0 W2 W0
+// Used by the SSSE3 assembly implementation
+const SYMCRYPT_ALIGN_AT(16) UINT32 XMM_PACKLOW[4] = {
+    0x03020100, 0x0b0a0908, 0x80808080, 0x80808080,
+};
+
+// Shuffle 32-bit words in an XMM register: W3 W2 W1 W0 -> W2 W0 0 0
+// Used by the SSSE3 assembly implementation
+const SYMCRYPT_ALIGN_AT(16) UINT32 XMM_PACKHIGH[4] = {
+    0x80808080, 0x80808080, 0x03020100, 0x0b0a0908,
+};
+
+
+#if SYMCRYPT_MS_VC && !defined(__clang__)
+#define RORX_U32  _rorx_u32
+#define RORX_U64  _rorx_u64
+#else
+// TODO: implement _rorx functions for clang
+#define RORX_U32  ROR32
+#define RORX_U64  ROR64
+#endif // SYMCRYPT_MS_VC
+
+
+//
+// For documentation on these function see FIPS 180-2
+//
+// MAJ and CH are the functions Maj and Ch from the standard.
+// CSIGMA0 and CSIGMA1 are the capital sigma functions.
+// LSIGMA0 and LSIGMA1 are the lowercase sigma functions.
+//
+// The canonical definitions of the MAJ and CH functions are:
+//#define MAJ( x, y, z )    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+//#define CH( x, y, z )  (((x) & (y)) ^ ((~(x)) & (z)))
+// We use optimized versions defined below
+//
+#define MAJ( x, y, z )  ((((z) | (y)) & (x) ) | ((z) & (y)))
+#define CH( x, y, z )  ((((z) ^ (y)) & (x)) ^ (z))
+
+#define LSIGMA0( x )    (ROR32((x),  7) ^ ROR32((x), 18) ^ ((x)>> 3))
+#define LSIGMA1( x )    (ROR32((x), 17) ^ ROR32((x), 19) ^ ((x)>>10))
+
+#define CSIGMA0(x) (RORX_U32(x, 2) ^ RORX_U32(x, 13) ^ RORX_U32(x, 22))
+#define CSIGMA1(x) (RORX_U32(x, 6) ^ RORX_U32(x, 11) ^ RORX_U32(x, 25))
+
+
+#define LSIGMA0XMM( x ) \
+    _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( \
+        _mm_slli_epi32(x,25)  , _mm_srli_epi32(x,  7) ),\
+        _mm_slli_epi32(x,14) ), _mm_srli_epi32(x, 18) ),\
+        _mm_srli_epi32(x, 3) )
+#define LSIGMA1XMM( x ) \
+    _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( \
+        _mm_slli_epi32(x,15)  , _mm_srli_epi32(x, 17) ),\
+        _mm_slli_epi32(x,13) ), _mm_srli_epi32(x, 19) ),\
+        _mm_srli_epi32(x,10) )
+
+
+
+// Initial loading of message words and endianness transformation.
+// bl : The number of blocks to load,  1 <= bl <= 4.
+//
+// When bl < 4, the high order lanes of the XMM registers corresponding to the missing blocks are unused.
+//
+#define SHA256_MSG_LOAD_4BLOCKS(bl) { \
+        for(SIZE_T i = 0; i < bl; i++) \
+        { \
+            Wx.xmm[i +  0] = _mm_shuffle_epi8(_mm_loadu_si128((__m128i*) &pbData[i * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE +  0]), kBYTE_REVERSE_32); \
+            Wx.xmm[i +  4] = _mm_shuffle_epi8(_mm_loadu_si128((__m128i*) &pbData[i * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE + 16]), kBYTE_REVERSE_32); \
+            Wx.xmm[i +  8] = _mm_shuffle_epi8(_mm_loadu_si128((__m128i*) &pbData[i * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE + 32]), kBYTE_REVERSE_32); \
+            Wx.xmm[i + 12] = _mm_shuffle_epi8(_mm_loadu_si128((__m128i*) &pbData[i * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE + 48]), kBYTE_REVERSE_32); \
+        } \
+}
+
+// Shuffles the initially loaded message words from multiple blocks
+// so that each XMM register contains message words with the same index
+// within a block (e.g. Wx.xmm[0] contains the first words of each block).
+//
+// We have to use this macro four times to transform the message blocks of 64-bytes.
+// ind=0 processes the first quarter (16-bytes), ind=1 does the second quarter and so on.
+//
+#define SHA256_MSG_TRANSPOSE_QUARTER_4BLOCKS(ind)  { \
+        __m128i t1, t2, t3, t4; \
+        t1 = _mm_unpacklo_epi32(Wx.xmm[4 * (ind) + 0], Wx.xmm[4 * (ind) + 1]); \
+        t2 = _mm_unpacklo_epi32(Wx.xmm[4 * (ind) + 2], Wx.xmm[4 * (ind) + 3]); \
+        t3 = _mm_unpackhi_epi32(Wx.xmm[4 * (ind) + 0], Wx.xmm[4 * (ind) + 1]); \
+        t4 = _mm_unpackhi_epi32(Wx.xmm[4 * (ind) + 2], Wx.xmm[4 * (ind) + 3]); \
+        Wx.xmm[4 * (ind) + 0] = _mm_unpacklo_epi64(t1, t2); \
+        Wx.xmm[4 * (ind) + 1] = _mm_unpackhi_epi64(t1, t2); \
+        Wx.xmm[4 * (ind) + 2] = _mm_unpacklo_epi64(t3, t4); \
+        Wx.xmm[4 * (ind) + 3] = _mm_unpackhi_epi64(t3, t4); \
+}
+
+#define SHA256_MSG_TRANSPOSE_4BLOCKS() { \
+        SHA256_MSG_TRANSPOSE_QUARTER_4BLOCKS(0); \
+        SHA256_MSG_TRANSPOSE_QUARTER_4BLOCKS(1); \
+        SHA256_MSG_TRANSPOSE_QUARTER_4BLOCKS(2); \
+        SHA256_MSG_TRANSPOSE_QUARTER_4BLOCKS(3); \
+}
+
+// One round message schedule, updates the rth message word. ( 16 <= r < 64 )
+// Also adds the constants for round (r-16).
+#define SHA256_MSG_EXPAND_4BLOCKS_1ROUND(r) { \
+        Wx.xmm[r] = _mm_add_epi32(_mm_add_epi32(_mm_add_epi32(Wx.xmm[r - 16], Wx.xmm[r - 7]), \
+                                LSIGMA0XMM(Wx.xmm[r - 15])), LSIGMA1XMM(Wx.xmm[r - 2])); \
+        Wx.xmm[r - 16] = _mm_add_epi32(Wx.xmm[r - 16], _mm_set1_epi32(SymCryptSha256K[r - 16])); \
+}
+
+// Four rounds of message schedule. Generates message words for rounds r, r+1, r+2, r+3.
+#define SHA256_MSG_EXPAND_4BLOCKS_4ROUNDS(r) { \
+        SHA256_MSG_EXPAND_4BLOCKS_1ROUND((r) + 0); SHA256_MSG_EXPAND_4BLOCKS_1ROUND((r) + 1); \
+        SHA256_MSG_EXPAND_4BLOCKS_1ROUND((r) + 2); SHA256_MSG_EXPAND_4BLOCKS_1ROUND((r) + 3); \
+}
+// Sixteen rounds of message schedule. Generates message words for rounds r, ..., r+15.
+#define SHA256_MSG_EXPAND_4BLOCKS_16ROUNDS(r) { \
+        SHA256_MSG_EXPAND_4BLOCKS_4ROUNDS((r) + 0); SHA256_MSG_EXPAND_4BLOCKS_4ROUNDS((r) + 4); \
+        SHA256_MSG_EXPAND_4BLOCKS_4ROUNDS((r) + 8); SHA256_MSG_EXPAND_4BLOCKS_4ROUNDS((r) + 12); \
+}
+
+// Core round function using message words from Wx array.
+// Wx contains -interleaved- expanded message words from b blocks.
+// i.e. Message words for round r for each block, followed by the message words for the (r+1)^th block.
+//
+// r16 : round number mod 16
+// rb  : base round number so that (rb+r16) gives the actual round number
+// b   : message block index, b = 0..3
+#define CROUND_4BLOCKS(r16, rb, b) {   \
+    Wt = Wx.ul4[(rb)+(r16)][b]; \
+    ah[ r16   &7] += CSIGMA1(ah[(r16+3)&7]) + CH(ah[(r16+3)&7], ah[(r16+2)&7], ah[(r16+1)&7]) + Wt;\
+    ah[(r16+4)&7] += ah[r16 &7];\
+    ah[ r16   &7] += CSIGMA0(ah[(r16+7)&7]) + MAJ(ah[(r16+7)&7], ah[(r16+6)&7], ah[(r16+5)&7]);\
+}
+
+//
+// Core round function
+//
+// r16 : round number mod 16
+// r   : round number, r = 0..63
+//
+#define CROUND( r16, r ) {;\
+    ah[ r16   &7] += CSIGMA1(ah[(r16+3)&7]) + CH(ah[(r16+3)&7], ah[(r16+2)&7], ah[(r16+1)&7]) + SymCryptSha256K[r] + Wt;\
+    ah[(r16+4)&7] += ah[r16 &7];\
+    ah[ r16   &7] += CSIGMA0(ah[(r16+7)&7]) + MAJ(ah[(r16+7)&7], ah[(r16+6)&7], ah[(r16+5)&7]);\
+}
+
+//
+// Initial round that reads the message.
+// r is the round number 0..15
+//
+#define IROUND( r ) {\
+    Wt = SYMCRYPT_LOAD_MSBFIRST32( &pbData[ 4*r ] );\
+    Wx.ul[r] = Wt; \
+    CROUND(r,r);\
+}
+
+//
+// Subsequent rounds.
+// r16 is the round number mod 16. rb is the round number minus r16.
+//
+#define FROUND(r16, rb) {                                    \
+    Wt = LSIGMA1( Wx.ul[(r16-2) & 15] ) +   Wx.ul[(r16-7) & 15] +    \
+         LSIGMA0( Wx.ul[(r16-15) & 15]) +   Wx.ul[r16 & 15];         \
+    Wx.ul[r16] = Wt; \
+    CROUND( r16, r16+rb ); \
+}
+
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_xmm_4blocks(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE* pChain,
+    _In_reads_(cbData)      PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T*                         pcbRemaining)
+{
+
+    SYMCRYPT_ALIGN union { UINT32 ul[16]; UINT32 ul4[64][4]; __m128i xmm[64]; } Wx;
+    SYMCRYPT_ALIGN UINT32 ah[8];
+    UINT32 Wt;
+    SIZE_T uWipeSize = (cbData >= (3 * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE)) ? (64 * 4 * sizeof(UINT32)) : (16 * sizeof(UINT32));
+
+    const __m128i kBYTE_REVERSE_32 = _mm_load_si128((const __m128i*)BYTE_REVERSE_32);
+
+    while (cbData >= (3 * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE))
+    {
+        // If we have 4 or more blocks then process 4, else process whatever is left.
+        SIZE_T numBlocks = (cbData >= 4 * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE) ? 4 : (cbData / SYMCRYPT_SHA256_INPUT_BLOCK_SIZE);
+
+        SHA256_MSG_LOAD_4BLOCKS(numBlocks);
+        SHA256_MSG_TRANSPOSE_4BLOCKS();
+
+        for (int j = 16; j < 64; j += 16)
+        {
+            SHA256_MSG_EXPAND_4BLOCKS_16ROUNDS(j);
+        }
+
+        // Constants up to r=48 were added during message expansion. Add the remaining ones here.
+        for (int i = 48; i < 64; i++)
+        {
+            Wx.xmm[i] = _mm_add_epi32(Wx.xmm[i], _mm_set1_epi32(SymCryptSha256K[i]));
+        }
+
+        for (SIZE_T bl = 0; bl < numBlocks; bl++)
+        {
+            ah[7] = pChain->H[0];
+            ah[6] = pChain->H[1];
+            ah[5] = pChain->H[2];
+            ah[4] = pChain->H[3];
+            ah[3] = pChain->H[4];
+            ah[2] = pChain->H[5];
+            ah[1] = pChain->H[6];
+            ah[0] = pChain->H[7];
+
+            for (int iterCount = 0; iterCount < (64/8); iterCount++)
+            {
+                const int roundBase = iterCount*8;
+                CROUND_4BLOCKS( 0, roundBase, bl);
+                CROUND_4BLOCKS( 1, roundBase, bl);
+                CROUND_4BLOCKS( 2, roundBase, bl);
+                CROUND_4BLOCKS( 3, roundBase, bl);
+                CROUND_4BLOCKS( 4, roundBase, bl);
+                CROUND_4BLOCKS( 5, roundBase, bl);
+                CROUND_4BLOCKS( 6, roundBase, bl);
+                CROUND_4BLOCKS( 7, roundBase, bl);
+                //CROUND_4BLOCKS( 8, roundBase, bl);
+                //CROUND_4BLOCKS( 9, roundBase, bl);
+                //CROUND_4BLOCKS(10, roundBase, bl);
+                //CROUND_4BLOCKS(11, roundBase, bl);
+                //CROUND_4BLOCKS(12, roundBase, bl);
+                //CROUND_4BLOCKS(13, roundBase, bl);
+                //CROUND_4BLOCKS(14, roundBase, bl);
+                //CROUND_4BLOCKS(15, roundBase, bl);
+            }
+
+            pChain->H[0] = ah[7] + pChain->H[0];
+            pChain->H[1] = ah[6] + pChain->H[1];
+            pChain->H[2] = ah[5] + pChain->H[2];
+            pChain->H[3] = ah[4] + pChain->H[3];
+            pChain->H[4] = ah[3] + pChain->H[4];
+            pChain->H[5] = ah[2] + pChain->H[5];
+            pChain->H[6] = ah[1] + pChain->H[6];
+            pChain->H[7] = ah[0] + pChain->H[7];
+        }
+
+        pbData += (numBlocks * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE);
+        cbData -= (numBlocks * SYMCRYPT_SHA256_INPUT_BLOCK_SIZE);
+    }
+
+
+    while (cbData >= SYMCRYPT_SHA256_INPUT_BLOCK_SIZE)
+    {
+        ah[7] = pChain->H[0];
+        ah[6] = pChain->H[1];
+        ah[5] = pChain->H[2];
+        ah[4] = pChain->H[3];
+        ah[3] = pChain->H[4];
+        ah[2] = pChain->H[5];
+        ah[1] = pChain->H[6];
+        ah[0] = pChain->H[7];
+
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND(0);
+        IROUND(1);
+        IROUND(2);
+        IROUND(3);
+        IROUND(4);
+        IROUND(5);
+        IROUND(6);
+        IROUND(7);
+        IROUND(8);
+        IROUND(9);
+        IROUND(10);
+        IROUND(11);
+        IROUND(12);
+        IROUND(13);
+        IROUND(14);
+        IROUND(15);
+
+
+        //
+        // rounds 16 to 64.
+        //
+        for (int iterCount = 1; iterCount < (64/16); iterCount++)
+        {
+            const int roundBase = iterCount*16;
+            FROUND(0, roundBase);
+            FROUND(1, roundBase);
+            FROUND(2, roundBase);
+            FROUND(3, roundBase);
+            FROUND(4, roundBase);
+            FROUND(5, roundBase);
+            FROUND(6, roundBase);
+            FROUND(7, roundBase);
+            FROUND(8, roundBase);
+            FROUND(9, roundBase);
+            FROUND(10, roundBase);
+            FROUND(11, roundBase);
+            FROUND(12, roundBase);
+            FROUND(13, roundBase);
+            FROUND(14, roundBase);
+            FROUND(15, roundBase);
+        }
+
+        pChain->H[0] = ah[7] + pChain->H[0];
+        pChain->H[1] = ah[6] + pChain->H[1];
+        pChain->H[2] = ah[5] + pChain->H[2];
+        pChain->H[3] = ah[4] + pChain->H[3];
+        pChain->H[4] = ah[3] + pChain->H[4];
+        pChain->H[5] = ah[2] + pChain->H[5];
+        pChain->H[6] = ah[1] + pChain->H[6];
+        pChain->H[7] = ah[0] + pChain->H[7];
+
+        pbData += SYMCRYPT_SHA256_INPUT_BLOCK_SIZE;
+        cbData -= SYMCRYPT_SHA256_INPUT_BLOCK_SIZE;
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipe(&Wx, uWipeSize);
+    SymCryptWipeKnownSize(ah, sizeof(ah));
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif // SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
diff --git a/libs/symcrypt/lib/sha256.c b/libs/symcrypt/lib/sha256.c
new file mode 100644
index 000000000000..975ead6d18ca
--- /dev/null
+++ b/libs/symcrypt/lib/sha256.c
@@ -0,0 +1,1884 @@
+//
+// Sha256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement SHA2-256 from FIPS 180-2
+//
+// This revised implementation is based on the older one in RSA32LIB by Scott Field from 2001
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+const SYMCRYPT_HASH SymCryptSha224Algorithm_default = {
+    &SymCryptSha224Init,
+    &SymCryptSha224Append,
+    &SymCryptSha224Result,
+    &SymCryptSha256AppendBlocks,
+    &SymCryptSha224StateCopy,
+    sizeof( SYMCRYPT_SHA224_STATE ),
+    SYMCRYPT_SHA224_RESULT_SIZE,
+    SYMCRYPT_SHA224_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA224_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA224_STATE, chain ),
+};
+
+const SYMCRYPT_HASH SymCryptSha256Algorithm_default = {
+    &SymCryptSha256Init,
+    &SymCryptSha256Append,
+    &SymCryptSha256Result,
+    &SymCryptSha256AppendBlocks,
+    &SymCryptSha256StateCopy,
+    sizeof( SYMCRYPT_SHA256_STATE ),
+    SYMCRYPT_SHA256_RESULT_SIZE,
+    SYMCRYPT_SHA256_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA256_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA256_STATE, chain ),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha224Algorithm = &SymCryptSha224Algorithm_default;
+const PCSYMCRYPT_HASH SymCryptSha256Algorithm = &SymCryptSha256Algorithm_default;
+
+//
+// SHA-256 uses 64 magic constants of 32 bits each. These are
+// referred to as K^{256}_i for i=0...63 by FIPS 180-2.
+// This array is also used by the parallel SHA256 implementation
+// For performance we align to 256 bytes, which gives optimal cache alignment.
+//
+SYMCRYPT_ALIGN_AT( 256 ) const  UINT32 SymCryptSha256K[64] = {
+    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+    0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+    0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+    0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+    0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+    0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+    0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+    0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+    0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+    0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+    0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+    0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+    0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+//
+// Initial state
+//
+static const UINT32 sha224InitialState[8] = {
+    0xc1059ed8UL,
+    0x367cd507UL,
+    0x3070dd17UL,
+    0xf70e5939UL,
+    0xffc00b31UL,
+    0x68581511UL,
+    0x64f98fa7UL,
+    0xbefa4fa4UL,
+};
+
+static const UINT32 sha256InitialState[8] = {
+    0x6a09e667UL,
+    0xbb67ae85UL,
+    0x3c6ef372UL,
+    0xa54ff53aUL,
+    0x510e527fUL,
+    0x9b05688cUL,
+    0x1f83d9abUL,
+    0x5be0cd19UL,
+};
+
+//
+// SymCryptSha224
+//
+#define ALG SHA224
+#define Alg Sha224
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+//
+// SymCryptSha256
+//
+#define ALG SHA256
+#define Alg Sha256
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+
+//
+// SymCryptSha256Init
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Init( _Out_ PSYMCRYPT_SHA256_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthL = 0;
+    //pState->dataLengthH = 0;      // not used
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &sha256InitialState[0], sizeof( sha256InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+//
+// SymCryptSha224Init
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Init( _Out_ PSYMCRYPT_SHA224_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthL = 0;
+    //pState->dataLengthH = 0;      // not used
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &sha224InitialState[0], sizeof( sha224InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+//
+// SymCryptSha256Append
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Append(
+    _Inout_                 PSYMCRYPT_SHA256_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData )
+{
+    UINT32  bytesInBuffer;
+    UINT32  freeInBuffer;
+    SIZE_T  tmp;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    pState->dataLengthL += cbData;      // dataLengthH is not used...
+
+    bytesInBuffer = pState->bytesInBuffer;
+
+    //
+    // If previous data in buffer, buffer new input and transform if possible.
+    //
+    if( bytesInBuffer > 0 )
+    {
+        SYMCRYPT_ASSERT( SYMCRYPT_SHA256_INPUT_BLOCK_SIZE > bytesInBuffer );
+
+        freeInBuffer = SYMCRYPT_SHA256_INPUT_BLOCK_SIZE - bytesInBuffer;
+        if( cbData < freeInBuffer )
+        {
+            //
+            // All the data will fit in the buffer.
+            // We don't do anything here.
+            // As cbData < inputBlockSize the bulk data processing is skipped,
+            // and the data will be copied to the buffer at the end
+            // of this code.
+        } else {
+            //
+            // Enough data to fill the whole buffer & process it
+            //
+            memcpy(&pState->buffer[bytesInBuffer], pbData, freeInBuffer);
+            pbData += freeInBuffer;
+            cbData -= freeInBuffer;
+            SymCryptSha256AppendBlocks( &pState->chain, &pState->buffer[0], SYMCRYPT_SHA256_INPUT_BLOCK_SIZE, &tmp );
+
+            bytesInBuffer = 0;
+        }
+    }
+
+    //
+    // Internal buffer is empty; process all remaining whole blocks in the input
+    //
+    if( cbData >= SYMCRYPT_SHA256_INPUT_BLOCK_SIZE )
+    {
+        SymCryptSha256AppendBlocks( &pState->chain, pbData, cbData, &tmp );
+        SYMCRYPT_ASSERT( tmp < SYMCRYPT_SHA256_INPUT_BLOCK_SIZE );
+        pbData += cbData - tmp;
+        cbData = tmp;
+    }
+
+    SYMCRYPT_ASSERT( cbData < SYMCRYPT_SHA256_INPUT_BLOCK_SIZE );
+
+    //
+    // buffer remaining input if necessary.
+    //
+    if( cbData > 0 )
+    {
+        memcpy( &pState->buffer[bytesInBuffer], pbData, cbData );
+        bytesInBuffer += (UINT32) cbData;
+    }
+
+    pState->bytesInBuffer = bytesInBuffer;
+}
+
+
+//
+// SymCryptSha224Append
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Append(
+    _Inout_                 PSYMCRYPT_SHA224_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData )
+{
+    SymCryptSha256Append( (PSYMCRYPT_SHA256_STATE)pState, pbData, cbData );
+}
+
+
+//
+// SymCryptSha256Result
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Result(
+    _Inout_                                     PSYMCRYPT_SHA256_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA256_RESULT_SIZE ) PBYTE                   pbResult )
+{
+    //
+    // We don't use the common padding code as that is slower, and SHA-256 is very frequently used in
+    // performance-sensitive areas.
+    //
+    UINT32 bytesInBuffer;
+    SIZE_T tmp;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    bytesInBuffer = pState->bytesInBuffer;
+
+    //
+    // The buffer is never completely full, so we can always put the first
+    // padding byte in.
+    //
+    pState->buffer[bytesInBuffer++] = 0x80;
+
+    if( bytesInBuffer > 64-8 ) {
+        //
+        // No room for the rest of the padding. Pad with zeroes & process block
+        // bytesInBuffer is at most 64, so we do not have an integer underflow
+        //
+        SymCryptWipe( &pState->buffer[bytesInBuffer], 64-bytesInBuffer );
+        SymCryptSha256AppendBlocks( &pState->chain, pState->buffer, 64, &tmp );
+        bytesInBuffer = 0;
+    }
+
+    //
+    // Set rest of padding
+    // At this point bytesInBuffer <= 64-8, so we don't have an underflow
+    // We wipe to the end of the buffer as it is 16-aligned,
+    // and it is faster to wipe to an aligned point
+    //
+    SymCryptWipe( &pState->buffer[bytesInBuffer], 64-bytesInBuffer );
+    SYMCRYPT_STORE_MSBFIRST64( &pState->buffer[64-8], pState->dataLengthL * 8 );
+
+    //
+    // Process the final block
+    //
+    SymCryptSha256AppendBlocks( &pState->chain, pState->buffer, 64, &tmp );
+
+    //
+    // Write the output in the correct byte order
+    //
+    SymCryptUint32ToMsbFirst( &pState->chain.H[0], pbResult, 8 );
+
+    //
+    // Wipe & re-initialize
+    // We have to wipe the whole state because the Init call
+    // might be optimized away by a smart compiler.
+    //
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+
+    memcpy( &pState->chain.H[0], &sha256InitialState[0], sizeof( sha256InitialState ) );
+    SYMCRYPT_SET_MAGIC( pState );
+}
+
+
+//
+// SymCryptSha224Result
+//
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Result(
+    _Inout_                                     PSYMCRYPT_SHA224_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA224_RESULT_SIZE ) PBYTE                   pbResult )
+{
+    SYMCRYPT_ALIGN BYTE sha256Result[SYMCRYPT_SHA256_RESULT_SIZE];      // Buffer for SHA-256 output
+
+    //
+    // The SHA-3224 result is the first 28 bytes of the SHA-256 result of our state
+    //
+    SymCryptSha256Result( (PSYMCRYPT_SHA256_STATE)pState, sha256Result );
+    memcpy( pbResult, sha256Result, SYMCRYPT_SHA224_RESULT_SIZE );
+
+    //
+    // The buffer was already wiped by the SymCryptSha256Result function, we
+    // just have to re-initialize for SHA-224
+    //
+    SymCryptSha224Init( pState );
+
+    SymCryptWipeKnownSize( sha256Result, sizeof( sha256Result ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256StateExportCore(
+    _In_                                                    PCSYMCRYPT_SHA256_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE ) PBYTE                   pbBlob,
+    _In_                                                    UINT32                  type )
+{
+    SYMCRYPT_ALIGN SYMCRYPT_SHA256_STATE_EXPORT_BLOB blob;           // local copy to have proper alignment.
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_SHA256_STATE_EXPORT_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) ); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_SHA256_STATE_EXPORT_SIZE;
+    blob.header.type = type;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+
+    SymCryptUint32ToMsbFirst( &pState->chain.H[0], &blob.chain[0], 8 );
+    blob.dataLength = pState->dataLengthL;
+    memcpy( &blob.buffer[0], &pState->buffer[0], blob.dataLength & 0x3f );
+
+    SYMCRYPT_ASSERT( (PCBYTE) &blob + sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ) == (PCBYTE) &blob.trailer );
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), &blob.trailer.checksum[0] );
+
+    memcpy( pbBlob, &blob, sizeof( blob ) );
+
+//cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) );
+    return;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256StateExport(
+    _In_                                                    PCSYMCRYPT_SHA256_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE ) PBYTE                   pbBlob)
+{
+    SymCryptSha256StateExportCore( pState, pbBlob, SymCryptBlobTypeSha256State );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224StateExport(
+    _In_                                                    PCSYMCRYPT_SHA224_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE ) PBYTE                   pbBlob)
+{
+    SymCryptSha256StateExportCore( (PSYMCRYPT_SHA256_STATE)pState, pbBlob, SymCryptBlobTypeSha224State );
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha256StateImportCore(
+    _Out_                                                   PSYMCRYPT_SHA256_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob,
+    _In_                                                    UINT32                  type )
+{
+    SYMCRYPT_ERROR                      scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_ALIGN SYMCRYPT_SHA256_STATE_EXPORT_BLOB   blob;                       // local copy to have proper alignment.
+    BYTE                                checksum[8];
+
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_SHA256_STATE_EXPORT_SIZE );
+    memcpy( &blob, pbBlob, sizeof( blob ) );
+
+    if( blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_SHA256_STATE_EXPORT_SIZE ||
+        blob.header.type != type )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), checksum );
+    if( memcmp( checksum, &blob.trailer.checksum[0], 8 ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMsbFirstToUint32( &blob.chain[0], &pState->chain.H[0], 8 );
+    pState->dataLengthL = blob.dataLength;
+    pState->bytesInBuffer = blob.dataLength & 0x3f;
+    memcpy( &pState->buffer[0], &blob.buffer[0], pState->bytesInBuffer );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof(blob) );
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha256StateImport(
+    _Out_                                                   PSYMCRYPT_SHA256_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA256_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob )
+{
+    return SymCryptSha256StateImportCore( pState, pbBlob, SymCryptBlobTypeSha256State );
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha224StateImport(
+    _Out_                                                   PSYMCRYPT_SHA224_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA224_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob )
+{
+    return SymCryptSha256StateImportCore( (PSYMCRYPT_SHA256_STATE)pState, pbBlob, SymCryptBlobTypeSha224State );
+}
+
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+const BYTE SymCryptSha256KATAnswer[ 32 ] = {
+    0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+    0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+    0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+    0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,
+    } ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA256_RESULT_SIZE];
+
+    SymCryptSha256( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, SymCryptSha256KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SH25' );
+    }
+}
+
+//
+// Simple test vector for FIPS module testing
+//
+
+const BYTE SymCryptSha224KATAnswer[ 28 ] = {
+    0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8, 0x22,
+    0x86, 0x42, 0xa4, 0x77, 0xbd, 0xa2, 0x55, 0xb3,
+    0x2a, 0xad, 0xbc, 0xe4, 0xbd, 0xa0, 0xb3, 0xf7,
+    0xe3, 0x6c, 0x9d, 0xa7,
+    } ;
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha224Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA224_RESULT_SIZE];
+
+    SymCryptSha224( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, SymCryptSha224KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SH22' );
+    }
+}
+
+
+
+//
+// Below are multiple implementations of the SymCryptSha256AppendBlocks function,
+// with a compile-time switch about which one to use.
+// We keep the multiple implementations here for future reference;
+// as CPU architectures evolve we might want to switch to one of the
+// other implementations.
+// All implementations here have been tested, but some lack production hardening.
+//
+
+//
+// Enable frame pointer omission to free up an extra register on X86.
+//
+#if SYMCRYPT_CPU_X86 && SYMCRYPT_MS_VC && !defined(__clang__)
+#pragma optimize( "y", on )
+#endif
+
+//
+// For documentation on these function see FIPS 180-2
+//
+// MAJ and CH are the functions Maj and Ch from the standard.
+// CSIGMA0 and CSIGMA1 are the capital sigma functions.
+// LSIGMA0 and LSIGMA1 are the lowercase sigma functions.
+//
+// The canonical definitions of the MAJ and CH functions are:
+//#define MAJ( x, y, z )    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+//#define CH( x, y, z )  (((x) & (y)) ^ ((~(x)) & (z)))
+// We use optimized versions defined below
+//
+#define MAJ( x, y, z )  ((((z) | (y)) & (x) ) | ((z) & (y)))
+#define CH( x, y, z )  ((((z) ^ (y)) & (x)) ^ (z))
+
+//
+// The four Sigma functions
+//
+
+//
+// We have two versions of the rotate-and-xor functions.
+// one is just a macro that does the rotations and xors.
+// This works well on ARM
+// For Intel/AMD we have one where we use the rotated value
+// from one intermediate result to derive the next rotated
+// value from. This removes one register copy from the
+// code stream.
+//
+// In practice, our compiler doesn't take advantage of the
+// reduction in the # operations required, and inserts a
+// bunch of extra register copies anyway.
+// It actually hurts on AMD64.
+//
+// This should be re-tuned for every release to get the best overall
+// SHA-256 performance.
+// At the moment we get an improvement from 19.76 c/B to 19.40 c/B on a Core 2 core.
+// We should probably tune this to the Atom CPU.
+//
+#if SYMCRYPT_CPU_X86
+#define USE_CSIGMA0_MULTIROT 1
+#define USE_CSIGMA1_MULTIROT 0
+#define USE_LSIGMA0_MULTIROT 0
+#define USE_LSIGMA1_MULTIROT 0
+
+#else
+//
+// On ARM we have no reason to believe this helps at all.
+// on AMD64 it slows our code down.
+//
+#define USE_CSIGMA0_MULTIROT 0
+#define USE_CSIGMA1_MULTIROT 0
+#define USE_LSIGMA0_MULTIROT 0
+#define USE_LSIGMA1_MULTIROT 0
+#endif
+
+#if USE_CSIGMA0_MULTIROT
+FORCEINLINE
+UINT32
+CSIGMA0( UINT32 x )
+{
+    UINT32 res;
+    x = ROR32( x, 2 );
+    res = x;
+    x = ROR32( x, 11 );
+    res ^= x;
+    x = ROR32( x, 9 );
+    res ^= x;
+    return res;
+}
+#else
+#define CSIGMA0( x )    (ROR32((x),  2) ^ ROR32((x), 13) ^ ROR32((x), 22))
+#endif
+
+#if USE_CSIGMA1_MULTIROT
+FORCEINLINE
+UINT32
+CSIGMA1( UINT32 x )
+{
+    UINT32 res;
+    x = ROR32( x, 6 );
+    res = x;
+    x = ROR32( x, 5 );
+    res ^= x;
+    x = ROR32( x, 14 );
+    res ^= x;
+    return res;
+}
+#else
+#define CSIGMA1( x )    (ROR32((x),  6) ^ ROR32((x), 11) ^ ROR32((x), 25))
+#endif
+
+#if USE_LSIGMA0_MULTIROT
+FORCEINLINE
+UINT32
+LSIGMA0( UINT32 x )
+{
+    UINT32 res;
+    res = x >> 3;
+    x = ROR32( x, 7 );
+    res ^= x;
+    x = ROR32( x, 11 );
+    res ^= x;
+    return res;
+}
+#else
+#define LSIGMA0( x )    (ROR32((x),  7) ^ ROR32((x), 18) ^ ((x)>> 3))
+#endif
+
+#if USE_LSIGMA1_MULTIROT
+FORCEINLINE
+UINT32
+LSIGMA1( UINT32 x )
+{
+    UINT32 res;
+    res = x >> 10;
+    x = ROR32( x, 17 );
+    res ^= x;
+    x = ROR32( x, 2 );
+    res ^= x;
+    return res;
+}
+#else
+#define LSIGMA1( x )    (ROR32((x), 17) ^ ROR32((x), 19) ^ ((x)>>10))
+#endif
+
+
+//
+// The values a-h are stored in an array called ah.
+// We have unrolled the loop 16 times. This makes both the indices into
+// the ah array constant, and it makes the message addressing constant.
+// This provides a significant speed improvement, at the cost of making
+// the main loop about 4 kB in code.
+//
+// The earlier implementation had the loop unrolled 8 times, and is
+// around 10 cycles/byte slower. If loading the code from disk takes
+// 100 cycles/byte, then we break even once you have hashed 20 kB.
+// This is a worthwhile tradeoff as all code is codesigned with SHA-256.
+//
+
+//
+// Core round macro
+//
+// r16 is the round number mod 16, r is the round number.
+// r16 is a separate macro argument because it is always a compile-time constant
+// which allows much better optimizations of the memory accesses.
+//
+// ah[ r16   &7] = h
+// ah[(r16+1)&7] = g;
+// ah[(r16+2)&7] = f;
+// ah[(r16+3)&7] = e;
+// ah[(r16+4)&7] = d;
+// ah[(r16+5)&7] = c;
+// ah[(r16+6)&7] = b;
+// ah[(r16+7)&7] = a;
+//
+// After that incrementing the round number will automatically map a->b, b->c, etc.
+//
+// The core round, after the message word has been computed for this round and put in Wt.
+// r16 is the round number modulo 16. (Static after loop unrolling)
+// r is the round number (dynamic, which is why we don't use (r&0xf) for r16)
+// In more readable form this macro does the following:
+//      h += CSIGMA( e ) + CH( e, f, g ) + K[round] + W[round];
+//      d += h;
+//      h += CSIGMA( a ) + MAJ( a, b, c );
+//
+#define CROUND( r16, r ) {;\
+    ah[ r16   &7] += CSIGMA1(ah[(r16+3)&7]) + CH(ah[(r16+3)&7], ah[(r16+2)&7], ah[(r16+1)&7]) + SymCryptSha256K[r] + Wt;\
+    ah[(r16+4)&7] += ah[r16 &7];\
+    ah[ r16   &7] += CSIGMA0(ah[(r16+7)&7]) + MAJ(ah[(r16+7)&7], ah[(r16+6)&7], ah[(r16+5)&7]);\
+}
+
+//
+// Initial round that reads the message.
+// r is the round number 0..15
+//
+#define IROUND( r ) {\
+    Wt = SYMCRYPT_LOAD_MSBFIRST32( &pbData[ 4*r ] );\
+    W[r] = Wt; \
+    CROUND(r,r);\
+    }
+
+//
+// Subsequent rounds.
+// r16 is the round number mod 16. rb is the round number minus r16.
+//
+#define FROUND(r16, rb) {                                      \
+    Wt = LSIGMA1( W[(r16-2) & 15] ) +   W[(r16-7) & 15] +    \
+         LSIGMA0( W[(r16-15) & 15]) +   W[r16 & 15];       \
+    W[r16] = Wt; \
+    CROUND( r16, r16+rb ); \
+}
+
+//
+// UINT32 implementation 1
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_ul1(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN UINT32 W[16];
+    SYMCRYPT_ALIGN UINT32 ah[8];
+    int round;
+    UINT32 Wt;
+
+    while( cbData >= 64 )
+    {
+        ah[7] = pChain->H[0];
+        ah[6] = pChain->H[1];
+        ah[5] = pChain->H[2];
+        ah[4] = pChain->H[3];
+        ah[3] = pChain->H[4];
+        ah[2] = pChain->H[5];
+        ah[1] = pChain->H[6];
+        ah[0] = pChain->H[7];
+
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND(  0 );
+        IROUND(  1 );
+        IROUND(  2 );
+        IROUND(  3 );
+        IROUND(  4 );
+        IROUND(  5 );
+        IROUND(  6 );
+        IROUND(  7 );
+        IROUND(  8 );
+        IROUND(  9 );
+        IROUND( 10 );
+        IROUND( 11 );
+        IROUND( 12 );
+        IROUND( 13 );
+        IROUND( 14 );
+        IROUND( 15 );
+
+
+        //
+        // rounds 16 to 64.
+        //
+        for( round=16; round<64; round += 16 )
+        {
+            FROUND(  0, round );
+            FROUND(  1, round );
+            FROUND(  2, round );
+            FROUND(  3, round );
+            FROUND(  4, round );
+            FROUND(  5, round );
+            FROUND(  6, round );
+            FROUND(  7, round );
+            FROUND(  8, round );
+            FROUND(  9, round );
+            FROUND( 10, round );
+            FROUND( 11, round );
+            FROUND( 12, round );
+            FROUND( 13, round );
+            FROUND( 14, round );
+            FROUND( 15, round );
+        }
+
+        pChain->H[0] = ah[7] + pChain->H[0];
+        pChain->H[1] = ah[6] + pChain->H[1];
+        pChain->H[2] = ah[5] + pChain->H[2];
+        pChain->H[3] = ah[4] + pChain->H[3];
+        pChain->H[4] = ah[3] + pChain->H[4];
+        pChain->H[5] = ah[2] + pChain->H[5];
+        pChain->H[6] = ah[1] + pChain->H[6];
+        pChain->H[7] = ah[0] + pChain->H[7];
+
+        pbData += 64;
+        cbData -= 64;
+
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( ah, sizeof( ah ) );
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SYMCRYPT_FORCE_WRITE32( &Wt, 0 );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_ul2(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    //
+    // Different arrangement of the code, currently 25 c/B vs 20 c/b for the version above.
+    // On Atom: 50 c/B vs 41 c/B for the one above.
+    //
+    SYMCRYPT_ALIGN UINT32 buf[4 + 8 + 64];    // chaining state concatenated with the expanded input block
+    UINT32 * W = &buf[4 + 8];
+    UINT32 * ha = &buf[4]; // initial state words, in order h, g, ..., b, a
+    UINT32 A, B, C, D, T;
+    int r;
+
+    ha[7] = pChain->H[0]; buf[3] = ha[7];
+    ha[6] = pChain->H[1]; buf[2] = ha[6];
+    ha[5] = pChain->H[2]; buf[1] = ha[5];
+    ha[4] = pChain->H[3]; buf[0] = ha[4];
+    ha[3] = pChain->H[4];
+    ha[2] = pChain->H[5];
+    ha[1] = pChain->H[6];
+    ha[0] = pChain->H[7];
+
+    while( cbData >= 64 )
+    {
+        //
+        // Capture the input into W[0..15]
+        //
+        for( r=0; r<16; r++ )
+        {
+            W[r] = SYMCRYPT_LOAD_MSBFIRST32( &pbData[ 4*r ] );
+        }
+
+        //
+        // Expand the message
+        //
+        A = W[15];
+        B = W[14];
+        D = W[0];
+        for( r=16; r<64; r+= 2 )
+        {
+            // Loop invariant: A=W[r-1], B = W[r-2], D = W[r-16]
+
+            //
+            // Macro for one word of message expansion.
+            // Invariant:
+            // on entry: a = W[r-1], b = W[r-2], d = W[r-16]
+            // on exit:  W[r] computed, a = W[r-1], b = W[r], c = W[r-15]
+            //
+            #define EXPAND( a, b, c, d, r ) \
+                        c = W[r-15]; \
+                        b =  d + LSIGMA1( b ) + W[r-7] + LSIGMA0( c ); \
+                        W[r] = b; \
+
+            EXPAND( A, B, C, D, r );
+            EXPAND( B, A, D, C, (r+1));
+
+            #undef EXPAND
+        }
+
+        A = ha[7];
+        B = ha[6];
+        C = ha[5];
+        D = ha[4];
+
+        for( r=0; r<64; r += 4 )
+        {
+            //
+            // Loop invariant:
+            // A, B, C, and D are the a,b,c,d values of the current state.
+            // W[r] is the next expanded message word to be processed.
+            // W[r-8 .. r-5] contain the current state words h, g, f, e.
+            //
+
+            //
+            // Macro to compute one round
+            //
+            #define DO_ROUND( a, b, c, d, t, r ) \
+                t = W[r] + CSIGMA1( W[r-5] ) + W[r-8] + CH( W[r-5], W[r-6], W[r-7] ) + SymCryptSha256K[r]; \
+                W[r-4] = t + d; \
+                d = t + CSIGMA0( a ) + MAJ( c, b, a );
+
+            DO_ROUND( A, B, C, D, T, r );
+            DO_ROUND( D, A, B, C, T, (r+1) );
+            DO_ROUND( C, D, A, B, T, (r+2) );
+            DO_ROUND( B, C, D, A, T, (r+3) );
+            #undef DO_ROUND
+        }
+
+        buf[3] = ha[7] = buf[3] + A;
+        buf[2] = ha[6] = buf[2] + B;
+        buf[1] = ha[5] = buf[1] + C;
+        buf[0] = ha[4] = buf[0] + D;
+        ha[3] += W[r-5];
+        ha[2] += W[r-6];
+        ha[1] += W[r-7];
+        ha[0] += W[r-8];
+
+        pbData += 64;
+        cbData -= 64;
+    }
+
+    pChain->H[0] = ha[7];
+    pChain->H[1] = ha[6];
+    pChain->H[2] = ha[5];
+    pChain->H[3] = ha[4];
+    pChain->H[4] = ha[3];
+    pChain->H[5] = ha[2];
+    pChain->H[6] = ha[1];
+    pChain->H[7] = ha[0];
+
+    *pcbRemaining = cbData;
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    SYMCRYPT_FORCE_WRITE32( &A, 0 );
+    SYMCRYPT_FORCE_WRITE32( &B, 0 );
+    SYMCRYPT_FORCE_WRITE32( &D, 0 );
+    SYMCRYPT_FORCE_WRITE32( &T, 0 );
+}
+
+#undef CROUND
+#undef IROUND
+#undef FROUND
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+//
+// Don't omit frame pointer for XMM code; it isn't register-starved as much
+//
+#if SYMCRYPT_CPU_X86 && SYMCRYPT_MS_VC && !defined(__clang__)
+#pragma optimize( "y", off )
+#endif
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("ssse3,sha"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("ssse3,sha")
+#endif
+
+//
+// Code that uses the XMM registers.
+// This code is currently unused. It was written in case it would provide better performance, but
+// it did not. We are retaining it in case it might be useful in a future CPU generation.
+//
+#if 0
+
+#define MAJXMM( x, y, z ) _mm_or_si128( _mm_and_si128( _mm_or_si128( z, y ), x ), _mm_and_si128( z, y ))
+#define CHXMM( x, y, z )  _mm_xor_si128( _mm_and_si128( _mm_xor_si128( z, y ), x ), z )
+
+#define CSIGMA0XMM( x ) \
+    _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( \
+        _mm_slli_epi32(x,30)  , _mm_srli_epi32(x,  2) ),\
+        _mm_slli_epi32(x,19) ), _mm_srli_epi32(x, 13) ),\
+        _mm_slli_epi32(x,10) ), _mm_srli_epi32(x, 22) )
+#define CSIGMA1XMM( x ) \
+    _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( \
+        _mm_slli_epi32(x,26)  , _mm_srli_epi32(x,  6) ),\
+        _mm_slli_epi32(x,21) ), _mm_srli_epi32(x, 11) ),\
+        _mm_slli_epi32(x,7) ), _mm_srli_epi32(x, 25) )
+#define LSIGMA0XMM( x ) \
+    _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( \
+        _mm_slli_epi32(x,25)  , _mm_srli_epi32(x,  7) ),\
+        _mm_slli_epi32(x,14) ), _mm_srli_epi32(x, 18) ),\
+        _mm_srli_epi32(x, 3) )
+#define LSIGMA1XMM( x ) \
+    _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( \
+        _mm_slli_epi32(x,15)  , _mm_srli_epi32(x, 17) ),\
+        _mm_slli_epi32(x,13) ), _mm_srli_epi32(x, 19) ),\
+        _mm_srli_epi32(x,10) )
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_xmm1(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    //
+    // Implementation that has one value in each XMM register.
+    // This is significantly slower than the _ul1 implementation
+    // but can be extended to compute 4 hash blocks in parallel.
+    //
+    SYMCRYPT_ALIGN __m128i buf[4 + 8 + 64];    // chaining state concatenated with the expanded input block
+    __m128i * W = &buf[4 + 8];
+    __m128i * ha = &buf[4]; // initial state words, in order h, g, ..., b, a
+    __m128i A, B, C, D, T;
+    int r;
+
+    //
+    // For 1-input only; set the input buffer to zero so that we have known values in every byte
+    //
+    //SymCryptWipeKnownSize( buf, sizeof( buf ) );
+
+    //
+    // Copy the chaining state into the start of the buffer, order = h,g,f,e,d,c,b,a
+    //
+    ha[7] = _mm_insert_epi32(ha[7], pChain->H[0], 0);
+    ha[6] = _mm_insert_epi32(ha[6], pChain->H[1], 0);
+    ha[5] = _mm_insert_epi32(ha[5], pChain->H[2], 0);
+    ha[4] = _mm_insert_epi32(ha[4], pChain->H[3], 0);
+    ha[3] = _mm_insert_epi32(ha[3], pChain->H[4], 0);
+    ha[2] = _mm_insert_epi32(ha[2], pChain->H[5], 0);
+    ha[1] = _mm_insert_epi32(ha[1], pChain->H[6], 0);
+    ha[0] = _mm_insert_epi32(ha[0], pChain->H[7], 0);
+
+    buf[0] = ha[4];
+    buf[1] = ha[5];
+    buf[2] = ha[6];
+    buf[3] = ha[7];
+
+    while( cbData >= 64 )
+    {
+
+        //
+        // Capture the input into W[0..15]
+        //
+        for( r=0; r<16; r++ )
+        {
+            W[r] = _mm_insert_epi32(W[r], SYMCRYPT_LOAD_MSBFIRST32( &pbData[ 4*r ] ), 0);
+        }
+
+        //
+        // Expand the message
+        //
+        A = W[15];
+        B = W[14];
+        D = W[0];
+        for( r=16; r<64; r+= 2 )
+        {
+            // Loop invariant: A=W[r-1], B = W[r-2], D = W[r-16]
+
+            //
+            // Macro for one word of message expansion.
+            // Invariant:
+            // on entry: a = W[r-1], b = W[r-2], d = W[r-16]
+            // on exit:  W[r] computed, a = W[r-1], b = W[r], c = W[r-15]
+            //
+            #define EXPAND( a, b, c, d, r ) \
+                        c = W[r-15]; \
+                        b = _mm_add_epi32( _mm_add_epi32( _mm_add_epi32( d, LSIGMA1XMM( b ) ), W[r-7] ), LSIGMA0XMM( c ) ); \
+                        W[r] = b; \
+
+            EXPAND( A, B, C, D, r );
+            EXPAND( B, A, D, C, (r+1));
+
+            #undef EXPAND
+        }
+
+        A = ha[7];
+        B = ha[6];
+        C = ha[5];
+        D = ha[4];
+
+        for( r=0; r<64; r += 4 )
+        {
+            //
+            // Loop invariant:
+            // A, B, C, and D are the a,b,c,d values of the current state.
+            // W[r] is the next expanded message word to be processed.
+            // W[r-8 .. r-5] contain the current state words h, g, f, e.
+            //
+
+            //
+            // Macro to compute one round
+            //
+            #define DO_ROUND( a, b, c, d, t, r ) \
+                t = W[r]; \
+                t = _mm_add_epi32( t, CSIGMA1XMM( W[r-5] ) ); \
+                t = _mm_add_epi32( t, W[r-8] ); \
+                t = _mm_add_epi32( t, CHXMM( W[r-5], W[r-6], W[r-7] ) ); \
+                t = _mm_add_epi32( t, _mm_cvtsi32_si128( SymCryptSha256K[r] ) ); \
+                W[r-4] = _mm_add_epi32( t, d ); \
+                d = _mm_add_epi32( t, CSIGMA0XMM( a ) ); \
+                d = _mm_add_epi32( d, MAJXMM( c, b, a ) );
+
+            DO_ROUND( A, B, C, D, T, r );
+            DO_ROUND( D, A, B, C, T, (r+1) );
+            DO_ROUND( C, D, A, B, T, (r+2) );
+            DO_ROUND( B, C, D, A, T, (r+3) );
+            #undef DO_ROUND
+        }
+
+        buf[3] = ha[7] = _mm_add_epi32( buf[3], A );
+        buf[2] = ha[6] = _mm_add_epi32( buf[2], B );
+        buf[1] = ha[5] = _mm_add_epi32( buf[1], C );
+        buf[0] = ha[4] = _mm_add_epi32( buf[0], D );
+        ha[3] = _mm_add_epi32( ha[3], W[r-5] );
+        ha[2] = _mm_add_epi32( ha[2], W[r-6] );
+        ha[1] = _mm_add_epi32( ha[1], W[r-7] );
+        ha[0] = _mm_add_epi32( ha[0], W[r-8] );
+
+        pbData += 64;
+        cbData -= 64;
+    }
+
+    //
+    // Copy the chaining state back into the hash structure
+    //
+    pChain->H[0] = _mm_extract_epi32(ha[7], 0);
+    pChain->H[1] = _mm_extract_epi32(ha[6], 0);
+    pChain->H[2] = _mm_extract_epi32(ha[5], 0);
+    pChain->H[3] = _mm_extract_epi32(ha[4], 0);
+    pChain->H[4] = _mm_extract_epi32(ha[3], 0);
+    pChain->H[5] = _mm_extract_epi32(ha[2], 0);
+    pChain->H[6] = _mm_extract_epi32(ha[1], 0);
+    pChain->H[7] = _mm_extract_epi32(ha[0], 0);
+
+    *pcbRemaining = cbData;
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    SymCryptWipeKnownSize( &A, sizeof( A ) );
+    SymCryptWipeKnownSize( &B, sizeof( B ) );
+    SymCryptWipeKnownSize( &C, sizeof( C ) );
+    SymCryptWipeKnownSize( &D, sizeof( D ) );
+    SymCryptWipeKnownSize( &T, sizeof( T ) );
+}
+
+
+//
+// XMM implementation 2
+// We use the XMM registers to compute part of the message schedule.
+// The load, BSWAP, and part of the message schedule recursion are done in XMM registers.
+// The rest of the work is done using integers.
+//
+// Core2: 0.1 c/B slower than the _ul1
+// Atom: 1.0 c/B slower than _ul1   (42.34 vs 41.39 c/B)
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_xmm2(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN union { UINT32 ul[16]; __m128i xmm[4]; } W;
+    SYMCRYPT_ALIGN UINT32 ah[8];
+    int round;
+    UINT32 Wt;
+    const __m128i BYTE_REVERSE_32 = _mm_set_epi8( 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3 );
+
+    ah[7] = pChain->H[0];
+    ah[6] = pChain->H[1];
+    ah[5] = pChain->H[2];
+    ah[4] = pChain->H[3];
+    ah[3] = pChain->H[4];
+    ah[2] = pChain->H[5];
+    ah[1] = pChain->H[6];
+    ah[0] = pChain->H[7];
+
+#define CROUND( r16, r ) {;\
+    ah[ r16   &7] += CSIGMA1(ah[(r16+3)&7]) + CH(ah[(r16+3)&7], ah[(r16+2)&7], ah[(r16+1)&7]) + SymCryptSha256K[r] + Wt;\
+    ah[(r16+4)&7] += ah[r16 &7];\
+    ah[ r16   &7] += CSIGMA0(ah[(r16+7)&7]) + MAJ(ah[(r16+7)&7], ah[(r16+6)&7], ah[(r16+5)&7]);\
+}
+
+
+//
+// Initial round that reads the message.
+// r is the round number 0..15
+//
+//    Wt = LOAD_MSBFIRST32( &pbData[ 4*r ] );\
+//    W.ul[r] = Wt; \
+
+#define IROUND( r ) {\
+    Wt = W.ul[r];\
+    CROUND(r,r);\
+    }
+
+//
+// Subsequent rounds.
+// r16 is the round number mod 16. rb is the round number minus r16.
+//
+#define FROUND(r16, rb) { \
+    Wt = W.ul[r16];\
+    CROUND( r16, r16+rb ); \
+}
+
+
+    while( cbData >= 64 )
+    {
+        //
+        // The code is faster if we directly access the W.ul array, rather than the W.xmm alias.
+        // I think the compiler gets more confused if you use the W.xmm values.
+        // We retain them in the union to ensure alignment
+        //
+        _mm_store_si128( (__m128i *)&W.ul[ 0], _mm_shuffle_epi8( _mm_loadu_si128( (__m128i *)&pbData[   0 ] ), BYTE_REVERSE_32 ));
+        _mm_store_si128( (__m128i *)&W.ul[ 4], _mm_shuffle_epi8( _mm_loadu_si128( (__m128i *)&pbData[  16 ] ), BYTE_REVERSE_32 ));
+        _mm_store_si128( (__m128i *)&W.ul[ 8], _mm_shuffle_epi8( _mm_loadu_si128( (__m128i *)&pbData[  32 ] ), BYTE_REVERSE_32 ));
+        _mm_store_si128( (__m128i *)&W.ul[12], _mm_shuffle_epi8( _mm_loadu_si128( (__m128i *)&pbData[  48 ] ), BYTE_REVERSE_32 ));
+
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND(  0 );
+        IROUND(  1 );
+        IROUND(  2 );
+        IROUND(  3 );
+        IROUND(  4 );
+        IROUND(  5 );
+        IROUND(  6 );
+        IROUND(  7 );
+        IROUND(  8 );
+        IROUND(  9 );
+        IROUND( 10 );
+        IROUND( 11 );
+        IROUND( 12 );
+        IROUND( 13 );
+        IROUND( 14 );
+        IROUND( 15 );
+
+
+        //
+        // rounds 16 to 64.
+        //
+        for( round=16; round<64; round += 16 )
+        {
+            __m128i Tmp;
+
+            Tmp = _mm_add_epi32( _mm_add_epi32(
+                    LSIGMA0XMM(_mm_loadu_si128( (__m128i *)&W.ul[1] )),
+                    _mm_load_si128( (__m128i *)&W.ul[0] ) ),
+                    _mm_loadu_si128( (__m128i *)&W.ul[9] ) );
+
+            //
+            // The final part of the message schedule can be done in XMM registers, but it isn't worth it.
+            // The rotates in XMM take two shifts and an OR/XOR, vs one instruction in integer registers.
+            // As the sigma1( W_{t-2} ) recursion component can only be computed 2 at a time
+            // (because the result of the first two are the inputs to the second two)
+            // you lose more than you gain by using XMM registers.
+            //
+            //Tmp = _mm_add_epi32( Tmp, LSIGMA1XMM( _mm_srli_si128( _mm_load_si128( (__m128i *)&W.ul[12] ), 8 ) ) );
+            //Tmp = _mm_add_epi32( Tmp, LSIGMA1XMM( _mm_slli_si128( Tmp, 8 ) ) );
+            //_mm_store_si128( (__m128i *)&W.ul[0], Tmp );
+            //
+
+            _mm_store_si128( (__m128i *)&W.ul[0], Tmp );
+            W.ul[0] += LSIGMA1( W.ul[14] );
+            W.ul[1] += LSIGMA1( W.ul[15] );
+            W.ul[2] += LSIGMA1( W.ul[0] );
+            W.ul[3] += LSIGMA1( W.ul[1] );
+
+            FROUND(  0, round );
+            FROUND(  1, round );
+            FROUND(  2, round );
+            FROUND(  3, round );
+
+            Tmp = _mm_add_epi32( _mm_add_epi32(
+                    LSIGMA0XMM(_mm_loadu_si128( (__m128i *)&W.ul[5] )),
+                    _mm_load_si128( (__m128i *)&W.ul[4] ) ),
+                    _mm_alignr_epi8( _mm_load_si128( (__m128i *)&W.ul[0] ), _mm_load_si128( (__m128i *)&W.ul[12] ), 4) );
+
+            _mm_store_si128( (__m128i *)&W.ul[4], Tmp );
+
+            W.ul[4] += LSIGMA1( W.ul[2] );
+            W.ul[5] += LSIGMA1( W.ul[3] );
+            W.ul[6] += LSIGMA1( W.ul[4] );
+            W.ul[7] += LSIGMA1( W.ul[5] );
+
+            FROUND(  4, round );
+            FROUND(  5, round );
+            FROUND(  6, round );
+            FROUND(  7, round );
+
+            Tmp = _mm_add_epi32( _mm_add_epi32(
+                    LSIGMA0XMM(_mm_loadu_si128( (__m128i *)&W.ul[9] )),
+                    _mm_load_si128( (__m128i *)&W.ul[8] ) ),
+                    _mm_loadu_si128( (__m128i *)&W.ul[1] ) );
+
+            _mm_store_si128( (__m128i *)&W.ul[8], Tmp );
+            W.ul[ 8] += LSIGMA1( W.ul[6] );
+            W.ul[ 9] += LSIGMA1( W.ul[7] );
+            W.ul[10] += LSIGMA1( W.ul[8] );
+            W.ul[11] += LSIGMA1( W.ul[9] );
+
+            FROUND(  8, round );
+            FROUND(  9, round );
+            FROUND( 10, round );
+            FROUND( 11, round );
+
+
+            Tmp = _mm_add_epi32( _mm_add_epi32(
+                    LSIGMA0XMM( _mm_alignr_epi8( _mm_load_si128( (__m128i *)&W.ul[0] ), _mm_load_si128( (__m128i *)&W.ul[12] ), 4) ),
+                    _mm_load_si128( (__m128i *)&W.ul[12] ) ),
+                    _mm_loadu_si128( (__m128i *)&W.ul[5] ) );
+
+            _mm_store_si128( (__m128i *)&W.ul[12], Tmp );
+            W.ul[12] += LSIGMA1( W.ul[10] );
+            W.ul[13] += LSIGMA1( W.ul[11] );
+            W.ul[14] += LSIGMA1( W.ul[12] );
+            W.ul[15] += LSIGMA1( W.ul[13] );
+
+            FROUND( 12, round );
+            FROUND( 13, round );
+            FROUND( 14, round );
+            FROUND( 15, round );
+        }
+
+        pChain->H[0] = ah[7] = ah[7] + pChain->H[0];
+        pChain->H[1] = ah[6] = ah[6] + pChain->H[1];
+        pChain->H[2] = ah[5] = ah[5] + pChain->H[2];
+        pChain->H[3] = ah[4] = ah[4] + pChain->H[3];
+        pChain->H[4] = ah[3] = ah[3] + pChain->H[4];
+        pChain->H[5] = ah[2] = ah[2] + pChain->H[5];
+        pChain->H[6] = ah[1] = ah[1] + pChain->H[6];
+        pChain->H[7] = ah[0] = ah[0] + pChain->H[7];
+
+        pbData += 64;
+        cbData -= 64;
+
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( ah, sizeof( ah ) );
+    SymCryptWipeKnownSize( &W, sizeof( W ) );
+    SYMCRYPT_FORCE_WRITE32( &Wt, 0 );
+
+#undef IROUND
+#undef FROUND
+#undef CROUND
+}
+
+#endif
+
+//
+// SHA-NI Implementation
+//
+
+#if SYMCRYPT_MS_VC && !defined(__clang__)
+// Intrinsic definitions included here
+// until the header is updated.
+// *******************************
+// *******************************
+// *******************************
+extern __m128i _mm_sha256rnds2_epu32(__m128i, __m128i, __m128i);
+extern __m128i _mm_sha256msg1_epu32(__m128i, __m128i);
+extern __m128i _mm_sha256msg2_epu32(__m128i, __m128i);
+// *******************************
+// *******************************
+// *******************************
+#endif
+
+// For the SHA-NI implementation we will utilize 128-bit XMM registers. Each
+// XMM state will be denoted as (R_3, R_2, R_1, R_0), where each R_i
+// is a 32-bit word and R_i refers to bits [32*i : (32*i + 31)] of the
+// 128-bit XMM state.
+//
+// The following macro updates the state variables A,B,C,...,H of the SHA algorithms
+// for 4 rounds using:
+//  - The current round number t with 0<=t<= 63 and t a multiple of 4.
+//  - A current message XMM state _MSG which consists of 4 32-bit words
+//      ( W_(t+3), W_(t+2), W_(t+1), W_(t+0) ).
+//  - Two XMM states _ABEF and _CDGH which contain the variables
+//      ( A, B, E, F ) and ( C, D, G, H ) respectively.
+
+#define SHANI_UPDATE_STATE( _round, _MSG, _ABEF, _CDGH ) \
+    _MSG = _mm_add_epi32( _MSG, *(__m128i *)&SymCryptSha256K[_round] );     /* Add the K_t constants to the W_t's */    \
+    _CDGH = _mm_sha256rnds2_epu32( _CDGH, _ABEF, _MSG );                    /* 2 rounds using SHA-NI */                 \
+    _MSG = _mm_shuffle_epi32( _MSG, 0x0e );                                 /* Move words 2 & 3 to positions 0 & 1 */   \
+    _ABEF = _mm_sha256rnds2_epu32( _ABEF, _CDGH, _MSG );                    /* 2 rounds using SHA-NI */
+
+// For the SHA message schedule (i.e. to create words W_16 to W_63) we use 4 XMM states / accumulators.
+// Each accumulator holds 4 words.
+//
+// The final result for each word will be of the form W_t = X_t + Y_t, where
+//          X_t = W_(t-16) + \sigma_0(W_(t-15)) and
+//          Y_t = W_(t- 7) + \sigma_1(W_(t- 2))
+//
+//          The X_t's are calculated by the _mm_sha256msg1_epu32 intrinsic.
+//          The \sigma_1(W_(t-2)) part of the Y_t's by the _mm_sha256msg2_epu32 intrinsic.
+//
+// Remarks:
+//      - Calculation of the first four X_t's (i.e. 16<=t<=19) can start from round 4 (since 19-15 = 4).
+//      - Calculation of the first four Y_t's can start from round 12 (since 19-7=12 and W_(19-7) is calculated
+//        in the intrinsic call).
+//      - Due to the W_(t-7) term, producing the Y_t's need special shifting via the _mm_alignr_epi8 intrinsic and
+//        adding the correct accumulator into another variable MTEMP.
+//
+// For rounds 16 - 51 we execute the following macro in a loop. For all the other rounds we
+// use specific code.
+//
+// The loop invariant to be satisfied at the beginning of iteration i (corresponding to rounds
+// (16+4*i) to (19+4*i) ) is the following:
+//      _MSG_0 = ( W_(19 + 4*i), W_(18 + 4*i), W_(17 + 4*i), W_(16 + 4*i) )
+//      _MSG_1 = ( X_(23 + 4*i), X_(22 + 4*i), X_(21 + 4*i), X_(20 + 4*i) )
+//      _MSG_2 = ( X_(27 + 4*i), X_(26 + 4*i), X_(25 + 4*i), X_(24 + 4*i) )
+//      _MSG_3 = ( W_(15 + 4*i), W_(14 + 4*i), W_(13 + 4*i), W_(12 + 4*i) )
+//
+#define SHANI_MESSAGE_SCHEDULE( _MSG_0, _MSG_1, _MSG_2, _MSG_3, _MTEMP ) \
+    _MTEMP = _mm_alignr_epi8( _MSG_0, _MSG_3, 4);       /* _MTEMP := ( W_(16 + 4*i), W_(15 + 4*i), W_(14 + 4*i), W_(13 + 4*i) ) */          \
+    _MSG_1 = _mm_add_epi32( _MSG_1, _MTEMP);            /* _MSG_1 := _MSG_1 + ( W_(16 + 4*i), W_(15 + 4*i), W_(14 + 4*i), W_(13 + 4*i) ) */ \
+    _MSG_1 = _mm_sha256msg2_epu32( _MSG_1, _MSG_0 );    /* _MSG_1 := ( W_(23 + 4*i), W_(22 + 4*i), W_(21 + 4*i), W_(20 + 4*i) ) */          \
+    _MSG_3 = _mm_sha256msg1_epu32( _MSG_3, _MSG_0 );    /* _MSG_3 := ( X_(31+4*i), X_(30+4*i), X_(29+4*i), X_(28+4*i) ) */
+//
+// After each iteration the subsequent call rotates the accumulators so that the loop
+// invariant is preserved (please verify!):
+//          -- MSG_0 <---- MSG_1 <--- MSG_2 <--- MSG_3 <--
+//          |                                            |
+//          ----------------------------------------------
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_shani(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    const __m128i BYTE_REVERSE_32 = _mm_set_epi8( 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3 );
+
+    // Our chain state is in order A, B, ..., H.
+    // First load our chaining state
+    __m128i DCBA = _mm_loadu_si128( (__m128i *)&(pChain->H[0]) );   // (D, C, B, A)
+    __m128i HGFE = _mm_loadu_si128( (__m128i *)&(pChain->H[4]) );   // (H, G, F, E)
+    __m128i FEBA = _mm_unpacklo_epi64( DCBA, HGFE );                // (F, E, B, A)
+    __m128i HGDC = _mm_unpackhi_epi64( DCBA, HGFE );                // (H, G, D, C)
+    __m128i ABEF = _mm_shuffle_epi32( FEBA, 0x1b );                 // (A, B, E, F)
+    __m128i CDGH = _mm_shuffle_epi32( HGDC, 0x1b );                 // (C, D, G, H)
+
+    while( cbData >= 64 )
+    {
+        // Save the current state for the feed-forward later
+        __m128i ABEF_start = ABEF;
+        __m128i CDGH_start = CDGH;
+
+        // Current message and temporary state
+        __m128i MSG;
+
+        // Accumulators
+        __m128i MSG_0;
+        __m128i MSG_1;
+        __m128i MSG_2;
+        __m128i MSG_3;
+
+        // Rounds 0-3
+        MSG = _mm_loadu_si128( (__m128i *)pbData );         // Reversed word - ( M_3, M_2, M_1, M_0 )
+        pbData += 16;
+        MSG = _mm_shuffle_epi8( MSG, BYTE_REVERSE_32 );     // Reverse each word
+        MSG_0 = MSG;                                        // MSG_0 := ( W_3 = M3, W_2 = M_2, W_1 = M_1, W_0 = M_0 )
+
+        SHANI_UPDATE_STATE( 0, MSG, ABEF, CDGH );
+
+        // Rounds 4-7
+        MSG = _mm_loadu_si128( (__m128i *)pbData );         // Reversed word - ( M_7, M_6, M_5, M_4 )
+        pbData += 16;
+        MSG = _mm_shuffle_epi8( MSG, BYTE_REVERSE_32 );     // Reverse each word
+        MSG_1 = MSG;                                        // MSG_1 := ( W_7 = M_7, W_6 = M_6, W_5 = M_5, W_4 = M_4 )
+
+        SHANI_UPDATE_STATE( 4, MSG, ABEF, CDGH );
+
+        MSG_0 = _mm_sha256msg1_epu32( MSG_0, MSG_1 );       // MSG_0 := ( X_19, X_18, X_17, X_16 ) =
+                                                            // ( W_3 + \sigma_0(W_4), ..., W_0 + \sigma_0(W_1) )
+
+        // Rounds 8-11
+        MSG = _mm_loadu_si128( (__m128i *)pbData );         // Reversed word - ( M_11, M_10, M_9, M_8 )
+        pbData += 16;
+        MSG = _mm_shuffle_epi8( MSG, BYTE_REVERSE_32 );     // Reverse each word
+        MSG_2 = MSG;                                        // MSG_2 := ( W_11 = M_11, W_10 = M_10, W_9 = M_9, W_8 = M_8 )
+
+        SHANI_UPDATE_STATE( 8, MSG, ABEF, CDGH );
+
+        MSG_1 = _mm_sha256msg1_epu32( MSG_1, MSG_2 );       // MSG_1 := ( X_23, X_22, X_21, X_20 )
+
+        // Rounds 12-15
+        MSG = _mm_loadu_si128( (__m128i *)pbData );         // Reversed word - ( M_15, M_14, M_13, M_12 )
+        pbData += 16;
+        MSG = _mm_shuffle_epi8( MSG, BYTE_REVERSE_32 );     // Reverse each word
+        MSG_3 = MSG;                                        // MSG_3 := ( W_15 = M_15, W_14 = M_14, W_13 = M_13, W_12 = M_12 )
+
+        SHANI_UPDATE_STATE( 12, MSG, ABEF, CDGH );
+
+        MSG = _mm_alignr_epi8( MSG_3, MSG_2, 4);            // MSG := ( W_12, W_11, W_10, W_9 )
+        MSG_0 = _mm_add_epi32( MSG_0, MSG);                 // MSG_0 := MSG_0 + ( W_12, W_11, W_10, W_9 )
+        MSG_0 = _mm_sha256msg2_epu32( MSG_0, MSG_3 );       // MSG_0 := ( W_19, W_18, W_17, W_16 ) =
+                                                            // ( X_19 + W_12 + \sigma_1(W_17)], ..., X_16 + W_9 + \sigma_1(W_14)] )
+
+        MSG_2 = _mm_sha256msg1_epu32( MSG_2, MSG_3 );       // MSG_2 := ( X_27, X_26, X_25, X_24 )
+
+
+        // Rounds 16 - 19
+        MSG = MSG_0;
+        SHANI_UPDATE_STATE( 16, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_0, MSG_1, MSG_2, MSG_3, MSG );
+
+        // Rounds 20 - 23
+        MSG = MSG_1;
+        SHANI_UPDATE_STATE( 20, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_1, MSG_2, MSG_3, MSG_0, MSG );
+
+        // Rounds 24 - 27
+        MSG = MSG_2;
+        SHANI_UPDATE_STATE( 24, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_2, MSG_3, MSG_0, MSG_1, MSG );
+
+        // Rounds 28 - 31
+        MSG = MSG_3;
+        SHANI_UPDATE_STATE( 28, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_3, MSG_0, MSG_1, MSG_2, MSG );
+
+        // Rounds 32 - 35
+        MSG = MSG_0;
+        SHANI_UPDATE_STATE( 32, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_0, MSG_1, MSG_2, MSG_3, MSG );
+
+        // Rounds 36 - 39
+        MSG = MSG_1;
+        SHANI_UPDATE_STATE( 36, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_1, MSG_2, MSG_3, MSG_0, MSG );
+
+        // Rounds 40 - 43
+        MSG = MSG_2;
+        SHANI_UPDATE_STATE( 40, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_2, MSG_3, MSG_0, MSG_1, MSG );
+
+        // Rounds 44 - 47
+        MSG = MSG_3;
+        SHANI_UPDATE_STATE( 44, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_3, MSG_0, MSG_1, MSG_2, MSG );
+
+        // Rounds 48 - 51
+        MSG = MSG_0;
+        SHANI_UPDATE_STATE( 48, MSG, ABEF, CDGH );
+        SHANI_MESSAGE_SCHEDULE( MSG_0, MSG_1, MSG_2, MSG_3, MSG );
+
+        // Rounds 52 - 55
+        MSG = MSG_1;                                                    // ( W_55, W_54, W_53, W_52 )
+        SHANI_UPDATE_STATE( 52, MSG, ABEF, CDGH );
+
+        MSG = _mm_alignr_epi8( MSG_1, MSG_0, 4);                        // MSG := ( W_52, W_51, W_50, W_49 )
+        MSG_2 = _mm_add_epi32( MSG_2, MSG);                             // MSG_2 := MSG_2 + ( W_52, W_51, W_50, W_49 )
+        MSG_2 = _mm_sha256msg2_epu32( MSG_2, MSG_1 );                   // Calculate ( W_59, W_58, W_57, W_56 )
+
+        // Rounds 56 - 59
+        MSG = MSG_2;                                                    // ( W_59, W_58, W_57, W_56 )
+        SHANI_UPDATE_STATE( 56, MSG, ABEF, CDGH );
+
+        MSG = _mm_alignr_epi8( MSG_2, MSG_1, 4);                        // MSG := ( W_56, W_55, W_54, W_53 )
+        MSG_3 = _mm_add_epi32( MSG_3, MSG);                             // MSG_3 := MSG_3 + ( W_56, W_55, W_54, W_53 )
+        MSG_3 = _mm_sha256msg2_epu32( MSG_3, MSG_2 );                   // Calculate ( W_63, W_62, W_61, W_60 )
+
+        // Rounds 60 - 63
+        SHANI_UPDATE_STATE( 60, MSG_3, ABEF, CDGH );
+
+        // Add the feed-forward
+        ABEF = _mm_add_epi32( ABEF, ABEF_start );
+        CDGH = _mm_add_epi32( CDGH, CDGH_start );
+
+        cbData -= 64;
+    }
+
+    // Unpack the state registers and store them in the state
+    FEBA = _mm_shuffle_epi32( ABEF, 0x1b );
+    HGDC = _mm_shuffle_epi32( CDGH, 0x1b );
+    DCBA = _mm_unpacklo_epi64( FEBA, HGDC );                        // (D, C, B, A)
+    HGFE = _mm_unpackhi_epi64( FEBA, HGDC );                        // (H, G, F, E)
+    _mm_storeu_si128 ( (__m128i *)&(pChain->H[0]), DCBA);            // (D, C, B, A)
+    _mm_storeu_si128 ( (__m128i *)&(pChain->H[4]), HGFE);            // (H, G, F, E)
+
+    *pcbRemaining = cbData;
+}
+
+#undef SHANI_UPDATE_STATE
+#undef SHANI_MESSAGE_SCHEDULE
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif  // SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#if SYMCRYPT_CPU_ARM64
+/*
+ARM64 has special SHA-256 instructions
+
+SHA256H and SHA256H2 implement 4 rounds of SHA-256. The inputs are two registers containing the 256-bit state,
+and one register containing 128 bits of expanded message plus the round constants.
+These instructions perform the same computation, but SHA256H returns the first half of the 256-bit result,
+and SHA256H2 returns the second half of the 256-bit result.
+
+SHA256H( ABCDE, FGHIJ, W )
+Where the least significant word of the ABCDE vector is A. The W vector contains W_i + K_i for the four rounds being computed.
+
+SHA256SU0 is the message schedule update function.
+It takes 2 inputs and produces 1 output.
+We describe the vectors for i=0,1,2,3
+Inputs:  [W_{t-16+i}], [W_{t-12+i}]
+Output: [Sigma0(W_{t-15+i}) + W_{t-16+i}]
+
+SHA256SU1 is the second message schedule update function
+Takes 3 inputs and produces 1 output
+Input 1: Output of SHA256SU0: [Sigma0(W_{t-15+i}) + W_{t-16+i}]
+Input 2:
+Input 3: [W_{t-4+i}]
+
+*/
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("sha2"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("sha2")
+#endif
+
+#define vldq(_p)     (*(__n128 *)(_p))
+#define vstq(_p, _v) (*(__n128 *)(_p) = (_v) )
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks_instr(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    //
+    // Armv8 has 32 Neon registers. We can use a lot of variables.
+    // 16 for the constants, 4 for the message, 2 for the current state, 2 for the starting state,
+    // total = 24 which leaves enough for some temp values
+    //
+    __n128      ABCD, ABCDstart;
+    __n128      EFGH, EFGHstart;
+    __n128      W0, W1, W2, W3;
+    __n128      K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13, K14, K15;
+
+    __n128      Wr;
+    __n128      t;
+
+    ABCD = ABCDstart = vldq( &pChain->H[0] );
+    EFGH = EFGHstart = vldq( &pChain->H[4] );
+
+    K0  = vldq( &SymCryptSha256K[ 4 *  0 ] );
+    K1  = vldq( &SymCryptSha256K[ 4 *  1 ] );
+    K2  = vldq( &SymCryptSha256K[ 4 *  2 ] );
+    K3  = vldq( &SymCryptSha256K[ 4 *  3 ] );
+    K4  = vldq( &SymCryptSha256K[ 4 *  4 ] );
+    K5  = vldq( &SymCryptSha256K[ 4 *  5 ] );
+    K6  = vldq( &SymCryptSha256K[ 4 *  6 ] );
+    K7  = vldq( &SymCryptSha256K[ 4 *  7 ] );
+    K8  = vldq( &SymCryptSha256K[ 4 *  8 ] );
+    K9  = vldq( &SymCryptSha256K[ 4 *  9 ] );
+    K10 = vldq( &SymCryptSha256K[ 4 * 10 ] );
+    K11 = vldq( &SymCryptSha256K[ 4 * 11 ] );
+    K12 = vldq( &SymCryptSha256K[ 4 * 12 ] );
+    K13 = vldq( &SymCryptSha256K[ 4 * 13 ] );
+    K14 = vldq( &SymCryptSha256K[ 4 * 14 ] );
+    K15 = vldq( &SymCryptSha256K[ 4 * 15 ] );
+
+    while( cbData >= 64 )
+    {
+        W0 = vrev32q_u8( vldq( &pbData[ 0] ) );
+        W1 = vrev32q_u8( vldq( &pbData[16] ) );
+        W2 = vrev32q_u8( vldq( &pbData[32] ) );
+        W3 = vrev32q_u8( vldq( &pbData[48] ) );
+
+        //
+        // The sha256h/sha256h2 instructions overwrite one of the two state input registers.
+        // This implies we have to have a copy made of one of the input states.
+        //
+#define ROUNDOP {\
+    t = ABCD;\
+    ABCD = vsha256hq_u32 ( ABCD, EFGH, Wr );\
+    EFGH = vsha256h2q_u32( EFGH, t, Wr );\
+    }
+
+        Wr = vaddq_u32( W0, K0 );
+        ROUNDOP;
+        Wr = vaddq_u32( W1, K1 );
+        ROUNDOP;
+        Wr = vaddq_u32( W2, K2 );
+        ROUNDOP;
+        Wr = vaddq_u32( W3, K3 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W0, W1 );
+        W0 = vsha256su1q_u32( t, W2, W3 );
+        Wr = vaddq_u32( W0, K4 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W1, W2 );
+        W1 = vsha256su1q_u32( t, W3, W0 );
+        Wr = vaddq_u32( W1, K5 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W2, W3 );
+        W2 = vsha256su1q_u32( t, W0, W1 );
+        Wr = vaddq_u32( W2, K6 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W3, W0 );
+        W3 = vsha256su1q_u32( t, W1, W2 );
+        Wr = vaddq_u32( W3, K7 );
+        ROUNDOP;
+
+
+        t = vsha256su0q_u32( W0, W1 );
+        W0 = vsha256su1q_u32( t, W2, W3 );
+        Wr = vaddq_u32( W0, K8 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W1, W2 );
+        W1 = vsha256su1q_u32( t, W3, W0 );
+        Wr = vaddq_u32( W1, K9 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W2, W3 );
+        W2 = vsha256su1q_u32( t, W0, W1 );
+        Wr = vaddq_u32( W2, K10 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W3, W0 );
+        W3 = vsha256su1q_u32( t, W1, W2 );
+        Wr = vaddq_u32( W3, K11 );
+        ROUNDOP;
+
+
+        t = vsha256su0q_u32( W0, W1 );
+        W0 = vsha256su1q_u32( t, W2, W3 );
+        Wr = vaddq_u32( W0, K12 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W1, W2 );
+        W1 = vsha256su1q_u32( t, W3, W0 );
+        Wr = vaddq_u32( W1, K13 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W2, W3 );
+        W2 = vsha256su1q_u32( t, W0, W1 );
+        Wr = vaddq_u32( W2, K14 );
+        ROUNDOP;
+
+        t = vsha256su0q_u32( W3, W0 );
+        W3 = vsha256su1q_u32( t, W1, W2 );
+        Wr = vaddq_u32( W3, K15 );
+        ROUNDOP;
+
+        ABCDstart = ABCD = vaddq_u32( ABCDstart, ABCD );
+        EFGHstart = EFGH = vaddq_u32( EFGHstart, EFGH );
+
+        pbData += 64;
+        cbData -= 64;
+#undef ROUNDOP
+
+    }
+
+    *pcbRemaining = cbData;
+    vstq( &pChain->H[0], ABCD );
+    vstq( &pChain->H[4], EFGH );
+
+    //
+    // All our local variables should be in registers, so no way to wipe them.
+    //
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif
+
+
+
+//
+// Easy switch between different implementations
+//
+//FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha256AppendBlocks(
+    _Inout_                 SYMCRYPT_SHA256_CHAINING_STATE* pChain,
+    _In_reads_(cbData)      PCBYTE                          pbData,
+                            SIZE_T                          cbData,
+    _Out_                   SIZE_T*                         pcbRemaining)
+{
+#if SYMCRYPT_CPU_AMD64
+
+    SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+
+    if (SYMCRYPT_CPU_FEATURES_PRESENT(SYMCRYPT_CPU_FEATURES_FOR_SHANI_CODE) &&
+        SymCryptSaveXmm(&SaveData) == SYMCRYPT_NO_ERROR)
+    {
+        SymCryptSha256AppendBlocks_shani(pChain, pbData, cbData, pcbRemaining);
+
+        SymCryptRestoreXmm(&SaveData);
+    }
+    // Temporarily disabling use of Ymm in SHA2
+    // else if (SYMCRYPT_CPU_FEATURES_PRESENT(SYMCRYPT_CPU_FEATURE_AVX2 | SYMCRYPT_CPU_FEATURE_BMI2) &&
+    //         SymCryptSaveYmm(&SaveData) == SYMCRYPT_NO_ERROR)
+    // {
+    //     //SymCryptSha256AppendBlocks_ul1(pChain, pbData, cbData, pcbRemaining);
+    //     //SymCryptSha256AppendBlocks_ymm_8blocks(pChain, pbData, cbData, pcbRemaining);
+    //     SymCryptSha256AppendBlocks_ymm_avx2_asm(pChain, pbData, cbData, pcbRemaining);
+
+    //     SymCryptRestoreYmm(&SaveData);
+    // }
+    else if (SYMCRYPT_CPU_FEATURES_PRESENT(SYMCRYPT_CPU_FEATURE_SSSE3 | SYMCRYPT_CPU_FEATURE_BMI2) &&
+            SymCryptSaveXmm(&SaveData) == SYMCRYPT_NO_ERROR)
+    {
+        //SymCryptSha256AppendBlocks_xmm_4blocks(pChain, pbData, cbData, pcbRemaining);
+        SymCryptSha256AppendBlocks_xmm_ssse3_asm(pChain, pbData, cbData, pcbRemaining);
+
+        SymCryptRestoreXmm(&SaveData);
+    }
+    else
+    {
+       SymCryptSha256AppendBlocks_ul1( pChain, pbData, cbData, pcbRemaining );
+       //SymCryptSha256AppendBlocks_ul2(pChain, pbData, cbData, pcbRemaining);
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_SHANI_CODE | SYMCRYPT_CPU_FEATURE_SAVEXMM_NOFAIL ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptSha256AppendBlocks_shani( pChain, pbData, cbData, pcbRemaining );
+        SymCryptRestoreXmm( &SaveData );
+    }
+    else if (SYMCRYPT_CPU_FEATURES_PRESENT(SYMCRYPT_CPU_FEATURE_SSSE3 | SYMCRYPT_CPU_FEATURE_BMI2)
+            && SymCryptSaveXmm(&SaveData) == SYMCRYPT_NO_ERROR)
+    {
+        SymCryptSha256AppendBlocks_xmm_4blocks(pChain, pbData, cbData, pcbRemaining);
+        SymCryptRestoreXmm(&SaveData);
+    }
+    else {
+        SymCryptSha256AppendBlocks_ul1( pChain, pbData, cbData, pcbRemaining );
+    }
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_SHA256 ) )
+    {
+        SymCryptSha256AppendBlocks_instr( pChain, pbData, cbData, pcbRemaining );
+    } else {
+        SymCryptSha256AppendBlocks_ul1( pChain, pbData, cbData, pcbRemaining );
+    }
+#else
+    SymCryptSha256AppendBlocks_ul1( pChain, pbData, cbData, pcbRemaining );
+#endif
+
+    //SymCryptSha256AppendBlocks_ul2( pChain, pbData, cbData, pcbRemaining );
+    //SymCryptSha256AppendBlocks_xmm1( pChain, pbData, cbData, pcbRemaining );  !!! Needs Save/restore logic
+    //SymCryptSha256AppendBlocks_xmm2( pChain, pbData, cbData, pcbRemaining );
+}
diff --git a/libs/symcrypt/lib/sha3.c b/libs/symcrypt/lib/sha3.c
new file mode 100644
index 000000000000..fbbd0fe28dd2
--- /dev/null
+++ b/libs/symcrypt/lib/sha3.c
@@ -0,0 +1,619 @@
+//
+// Sha3.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+//
+// Keccak state
+//
+// Keccak-f[1600] state consists of 25 64-bit words. We represent this state as a single
+// dimensional array of 25 elements (Wi being the i^th element of the array for i=0..24)
+// with the following mapping to two dimensional coordinates. Note that in FIPS 202 Figure 2,
+// the element W0 at (x,y)=(0,0) is depicted in the middle of the 5x5 array. We set W0
+// to be the first element so that the rate part of the permutation maps to the beginning
+// of the state.
+//
+//       x=0  x=1  x=2  x=3  x=4
+//       -----------------------
+// y=0    W0   W1   W2   W3   W4
+// y=1    W5   W6   W7   W8   W9
+// y=2   W10  W11  W12  W13  W14
+// y=3   W15  W16  W17  W18  W19
+// y=4   W20  W21  W22  W23  W24
+
+
+
+// Rotation constants for Keccak Rho transformation
+static const UINT8 KeccakRhoK[25] = {
+     0,  1, 62, 28, 27,     // y = 0
+    36, 44,  6, 55, 20,     // y = 1
+     3, 10, 43, 25, 39,     // y = 2
+    41, 45, 15, 21,  8,     // y = 3
+    18,  2, 61, 56, 14,     // y = 4
+};
+
+// Keccak round constants
+static UINT64 KeccakIotaK[24] = {
+    0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
+    0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
+    0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
+    0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
+    0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL,
+    0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL
+};
+
+// XOR sum of column c of the state
+#define KECCAK_COLUMN_SUM(state, c) \
+    (state[0 + (c)] ^ state[5 + (c)] ^ state[10 + (c)] ^ state[15 + (c)] ^ state[20 + (c)])
+
+// XOR w to all the lanes in column c of the state
+//
+// Note: The expression to be XORed is copied to a temporary variable to avoid reevaluation
+#define KECCAK_COLUMN_UPDATE(state, c, w) { \
+    UINT64 t = (w); \
+    state[ 0 + (c)] ^= t; \
+    state[ 5 + (c)] ^= t; \
+    state[10 + (c)] ^= t; \
+    state[15 + (c)] ^= t; \
+    state[20 + (c)] ^= t; \
+}
+
+// Apply Theta transformation to the state
+#define KECCAK_THETA(state) { \
+    UINT64 colSum[5]; \
+    colSum[0] = KECCAK_COLUMN_SUM(state, 0); \
+    colSum[1] = KECCAK_COLUMN_SUM(state, 1); \
+    colSum[2] = KECCAK_COLUMN_SUM(state, 2); \
+    colSum[3] = KECCAK_COLUMN_SUM(state, 3); \
+    colSum[4] = KECCAK_COLUMN_SUM(state, 4); \
+    KECCAK_COLUMN_UPDATE(state, 0, colSum[4] ^ ROL64(colSum[1], 1)); \
+    KECCAK_COLUMN_UPDATE(state, 1, colSum[0] ^ ROL64(colSum[2], 1)); \
+    KECCAK_COLUMN_UPDATE(state, 2, colSum[1] ^ ROL64(colSum[3], 1)); \
+    KECCAK_COLUMN_UPDATE(state, 3, colSum[2] ^ ROL64(colSum[4], 1)); \
+    KECCAK_COLUMN_UPDATE(state, 4, colSum[3] ^ ROL64(colSum[0], 1)); \
+}
+
+// Apply Rho transformation to row r of the state
+#define KECCAK_RHO_ROW(state, r) { \
+    state[5 * (r) + 0] = ROL64(state[5 * (r) + 0], KeccakRhoK[5 * (r) + 0]); \
+    state[5 * (r) + 1] = ROL64(state[5 * (r) + 1], KeccakRhoK[5 * (r) + 1]); \
+    state[5 * (r) + 2] = ROL64(state[5 * (r) + 2], KeccakRhoK[5 * (r) + 2]); \
+    state[5 * (r) + 3] = ROL64(state[5 * (r) + 3], KeccakRhoK[5 * (r) + 3]); \
+    state[5 * (r) + 4] = ROL64(state[5 * (r) + 4], KeccakRhoK[5 * (r) + 4]); \
+}
+
+// Apply Rho transformation to row 0 of the state
+//
+// The first row contains a rotation by 0 on the first lane that uses a shift
+// by 64 which we want to avoid. Rho operation below omits the rotation on the first lane.
+#define KECCAK_RHO_ROW0(state) { \
+    state[1] = ROL64(state[1], KeccakRhoK[1]); \
+    state[2] = ROL64(state[2], KeccakRhoK[2]); \
+    state[3] = ROL64(state[3], KeccakRhoK[3]); \
+    state[4] = ROL64(state[4], KeccakRhoK[4]); \
+}
+
+// Apply Rho transformation to the state
+#define KECCAK_RHO(state) { \
+    KECCAK_RHO_ROW0(state); \
+    KECCAK_RHO_ROW(state, 1); \
+    KECCAK_RHO_ROW(state, 2); \
+    KECCAK_RHO_ROW(state, 3); \
+    KECCAK_RHO_ROW(state, 4); \
+}
+
+// Apply Pi transformation to the state
+#define KECCAK_PI(state) { \
+    UINT64 t  = state[ 1]; state[ 1] = state[ 6]; state[ 6] = state[ 9]; state[ 9] = state[22]; state[22] = state[14]; \
+    state[14] = state[20]; state[20] = state[ 2]; state[ 2] = state[12]; state[12] = state[13]; state[13] = state[19]; \
+    state[19] = state[23]; state[23] = state[15]; state[15] = state[ 4]; state[ 4] = state[24]; state[24] = state[21]; \
+    state[21] = state[ 8]; state[ 8] = state[16]; state[16] = state[ 5]; state[ 5] = state[ 3]; state[ 3] = state[18]; \
+    state[18] = state[17]; state[17] = state[11]; state[11] = state[ 7]; state[ 7] = state[10]; state[10] = t; \
+}
+
+// Apply Chi transformation on row r of state
+#define KECCAK_CHI_ROW(state, r) { \
+    UINT64 t1 = state[5 * (r) + 0] ^ (~state[5 * (r) + 1] & state[5 * (r) + 2]); \
+    UINT64 t2 = state[5 * (r) + 1] ^ (~state[5 * (r) + 2] & state[5 * (r) + 3]); \
+    state[5 * (r) + 2] = state[5 * (r) + 2] ^ (~state[5 * (r) + 3] & state[5 * (r) + 4]); \
+    state[5 * (r) + 3] = state[5 * (r) + 3] ^ (~state[5 * (r) + 4] & state[5 * (r) + 0]); \
+    state[5 * (r) + 4] = state[5 * (r) + 4] ^ (~state[5 * (r) + 0] & state[5 * (r) + 1]); \
+    state[5 * (r) + 0] = t1; \
+    state[5 * (r) + 1] = t2; \
+}
+
+// Apply Chi transformation to state
+#define KECCAK_CHI(state) { \
+    KECCAK_CHI_ROW(state, 0); \
+    KECCAK_CHI_ROW(state, 1); \
+    KECCAK_CHI_ROW(state, 2); \
+    KECCAK_CHI_ROW(state, 3); \
+    KECCAK_CHI_ROW(state, 4); \
+}
+
+// Add round constant to state
+#define KECCAK_IOTA(state, rnd) state[0] ^= KeccakIotaK[rnd]
+
+// Perform one round of Keccak permutation on state
+#define KECCAK_PERM_ROUND(state, rnd) { \
+    KECCAK_THETA(state); \
+    KECCAK_RHO(state); \
+    KECCAK_PI(state); \
+    KECCAK_CHI(state); \
+    KECCAK_IOTA(state, rnd); \
+}
+
+
+//
+// SymCryptKeccakPermute
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakPermute(_Inout_updates_(25) UINT64* pState)
+{
+    for (int r = 0; r < 24; r++)
+    {
+        KECCAK_PERM_ROUND(pState, r);
+    }
+}
+
+
+//
+// SymCryptKeccakInit
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakInit(_Out_ PSYMCRYPT_KECCAK_STATE pState, UINT32 inputBlockSize, UINT8 paddingValue)
+{
+    pState->inputBlockSize = inputBlockSize;
+    pState->paddingValue = paddingValue;
+
+    // Initialize the Keccak permutation state and set mutable state variables
+    // to their default values.
+    SymCryptKeccakReset(pState);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakReset(_Out_ PSYMCRYPT_KECCAK_STATE pState)
+{
+    //
+    // Wipe & re-initialize
+    //
+    // Wipe the Keccak permutation state and set the mutable state variables to their
+    // default values. Non-mutable state variables retain their values. State becomes
+    // re-initialized after this call.
+    SymCryptWipeKnownSize(pState->state, sizeof(pState->state));
+    pState->stateIndex = 0;
+    pState->squeezeMode = FALSE;
+}
+
+//
+// SymCryptKeccakAppendByte
+//
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendByte(_Inout_ PSYMCRYPT_KECCAK_STATE  pState, BYTE val)
+{
+    SYMCRYPT_ASSERT(!pState->squeezeMode);
+    SYMCRYPT_ASSERT(pState->stateIndex < pState->inputBlockSize);
+
+    pState->state[pState->stateIndex / sizeof(UINT64)] ^= ((UINT64)val << (8 * (pState->stateIndex % 8)));
+    pState->stateIndex++;
+}
+
+//
+// SymCryptKeccakAppendBytes
+//
+FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendBytes(_Inout_ PSYMCRYPT_KECCAK_STATE  pState, PCBYTE pbBuffer, SIZE_T cbBuffer)
+{
+    SYMCRYPT_ASSERT(!pState->squeezeMode);
+    SYMCRYPT_ASSERT((pState->stateIndex + cbBuffer) <= pState->inputBlockSize);
+
+    for (SIZE_T i = 0; i < cbBuffer; i++)
+    {
+        pState->state[(pState->stateIndex + i) / sizeof(UINT64)] ^= ((UINT64)pbBuffer[i] << (8 * ((pState->stateIndex + i) % 8)));
+    }
+
+    pState->stateIndex += (UINT32)cbBuffer;
+}
+
+
+//
+// SymCryptKeccakAppendLanes
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendLanes(
+    _Inout_                                 PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_(uLaneCount * sizeof(UINT64)) PCBYTE                  pbData,
+                                            SIZE_T                  uLaneCount)
+{
+    SYMCRYPT_ASSERT(!pState->squeezeMode);
+    SYMCRYPT_ASSERT((pState->inputBlockSize & 0x7) == 0);
+    SYMCRYPT_ASSERT((pState->stateIndex & 0x7) == 0);
+    SYMCRYPT_ASSERT(pState->stateIndex != pState->inputBlockSize);
+
+    // Locate the lane in the state for next append.
+    // Currently, pState->stateIndex/sizeof(UINT64) of the lanes are used.
+    UINT32 uLaneIndex = pState->stateIndex / sizeof(UINT64);
+
+    for (SIZE_T i = 0; i < uLaneCount; i++)
+    {
+        pState->state[uLaneIndex] ^= SYMCRYPT_LOAD_LSBFIRST64(pbData + i * sizeof(UINT64));
+        pState->stateIndex += sizeof(UINT64);
+        uLaneIndex++;
+
+        if (pState->stateIndex == pState->inputBlockSize)
+        {
+            SymCryptKeccakPermute(pState->state);
+            pState->stateIndex = 0;
+            uLaneIndex = 0;
+        }
+    }
+}
+
+//
+// SymCryptKeccakZeroAppendBlock
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakZeroAppendBlock(_Inout_ PSYMCRYPT_KECCAK_STATE  pState)
+{
+    SYMCRYPT_ASSERT(!pState->squeezeMode);
+    SymCryptKeccakPermute(pState->state);
+    pState->stateIndex = 0;
+}
+
+//
+// SymCryptKeccakAppend
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppend(
+    _Inout_                 PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_(cbData)      PCBYTE                  pbData,
+                            SIZE_T                  cbData)
+{
+    SYMCRYPT_ASSERT(pState->inputBlockSize % 8 == 0);
+
+    // If we were in squeeze mode (Append is called after an Extract without wiping),
+    // switch to absorb mode to start a new hash computation.
+    if (pState->squeezeMode)
+    {
+        SymCryptKeccakReset(pState);
+    }
+
+    SYMCRYPT_ASSERT(pState->stateIndex < pState->inputBlockSize);
+
+    // Make pState->stateIndex a multiple of 8.
+    // Message block boundary will not be crossed, check
+    // if permutation is needed after this part.
+    while (cbData > 0 && (pState->stateIndex & 0x7))
+    {
+        SymCryptKeccakAppendByte(pState, *pbData);
+        pbData++;
+        cbData--;
+    }
+
+    // Permute if input message block is filled
+    if (pState->stateIndex == pState->inputBlockSize)
+    {
+        SymCryptKeccakPermute(pState->state);
+        pState->stateIndex = 0;
+    }
+
+    // Append full lanes
+    SIZE_T uFullLanes = cbData / sizeof(UINT64);
+    if (uFullLanes > 0)
+    {
+        SymCryptKeccakAppendLanes(pState, pbData, uFullLanes);
+        pbData += uFullLanes * sizeof(UINT64);
+        cbData -= uFullLanes * sizeof(UINT64);
+    }
+
+    SYMCRYPT_ASSERT(cbData < sizeof(UINT64));
+    SymCryptKeccakAppendBytes(pState, pbData, cbData);
+
+    SYMCRYPT_ASSERT(pState->stateIndex != pState->inputBlockSize);
+}
+
+//
+// SymCryptKeccakApplyPadding
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakApplyPadding(_Inout_ PSYMCRYPT_KECCAK_STATE pState)
+{
+    SYMCRYPT_ASSERT(!pState->squeezeMode);
+
+    // Locate the lane and byte position for the padding byte
+    UINT32 uLanePos = pState->stateIndex / sizeof(UINT64);
+    UINT32 uBytePos = pState->stateIndex % sizeof(UINT64);
+    pState->state[uLanePos] ^= ((UINT64)pState->paddingValue << (8 * uBytePos));
+
+    // Pad the final 1 bit to the msb of the last lane in the rate portion of the state
+    pState->state[pState->inputBlockSize / sizeof(UINT64) - 1] ^= (1ULL << 63);
+
+    // Process the padded block and switch to squeeze mode
+    SymCryptKeccakPermute(pState->state);
+    pState->stateIndex = 0;
+    pState->squeezeMode = TRUE;
+}
+
+//
+// SymCryptKeccakExtractByte
+//
+FORCEINLINE
+BYTE
+SYMCRYPT_CALL
+SymCryptKeccakExtractByte(_Inout_ PSYMCRYPT_KECCAK_STATE  pState)
+{
+    SYMCRYPT_ASSERT(pState->squeezeMode);
+    SYMCRYPT_ASSERT(pState->stateIndex < pState->inputBlockSize);
+
+    BYTE ret = (BYTE)((pState->state[pState->stateIndex / sizeof(UINT64)] >> (8 * (pState->stateIndex % 8))) & 0xff);
+    pState->stateIndex++;
+    return ret;
+}
+
+//
+// SymCryptKeccakExtractLanes
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakExtractLanes(
+    _Inout_                                     PSYMCRYPT_KECCAK_STATE  pState,
+    _Out_writes_(uLaneCount * sizeof(UINT64))   PBYTE                   pbResult,
+                                                SIZE_T                  uLaneCount)
+{
+    SYMCRYPT_ASSERT(pState->squeezeMode);
+    SYMCRYPT_ASSERT((pState->inputBlockSize & 0x7) == 0);
+    SYMCRYPT_ASSERT((pState->stateIndex & 0x7) == 0);
+
+    // Locate the lane in the state for next extraction
+    UINT32 uLaneIndex = pState->stateIndex / sizeof(UINT64);
+
+    for (SIZE_T i = 0; i < uLaneCount; i++)
+    {
+        SYMCRYPT_ASSERT(pState->stateIndex <= pState->inputBlockSize);
+
+        if (pState->stateIndex == pState->inputBlockSize)
+        {
+            SymCryptKeccakPermute(pState->state);
+            pState->stateIndex = 0;
+            uLaneIndex = 0;
+        }
+
+        SYMCRYPT_STORE_LSBFIRST64(pbResult + i * sizeof(UINT64), pState->state[uLaneIndex]);
+        pState->stateIndex += sizeof(UINT64);
+        uLaneIndex++;
+    }
+}
+
+//
+// SymCryptKeccakExtract
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakExtract(
+    _Inout_                 PSYMCRYPT_KECCAK_STATE  pState,
+    _Out_writes_(cbResult)  PBYTE                   pbResult,
+                            SIZE_T                  cbResult,
+                            BOOLEAN                 bWipe)
+{
+    // Apply padding and switch to squeeze mode if this is the first call to Extract
+    if (!pState->squeezeMode)
+    {
+        SymCryptKeccakApplyPadding(pState);
+    }
+
+    // Do the permutation if there are no bytes available in the state
+    if ( (cbResult > 0) && (pState->stateIndex == pState->inputBlockSize) )
+    {
+        SymCryptKeccakPermute(pState->state);
+        pState->stateIndex= 0;
+    }
+
+    // Make stateIndex a multiple of 8 so that the extraction can be performed in lanes.
+    // We don't call the permutation as soon as the stateIndex reaches inputBlockSize,
+    // cbResult must also be non-zero for that. This condition is checked
+    // in ExtractLanes or in the 'remaining bytes' block that follows it.
+    while (cbResult > 0 && (pState->stateIndex & 0x7))
+    {
+        *pbResult = SymCryptKeccakExtractByte(pState);
+        pbResult++;
+        cbResult--;
+    }
+
+    SYMCRYPT_ASSERT((cbResult == 0) || ((pState->stateIndex & 0x7) == 0));
+
+    // Extract full lanes
+    SIZE_T uFullLanes = cbResult / sizeof(UINT64);
+    if (uFullLanes > 0)
+    {
+        SymCryptKeccakExtractLanes(pState, pbResult, uFullLanes);
+        pbResult += uFullLanes * sizeof(UINT64);
+        cbResult -= uFullLanes * sizeof(UINT64);
+    }
+
+    // Extract the remaining bytes
+    SYMCRYPT_ASSERT(cbResult < sizeof(UINT64));
+    while (cbResult > 0)
+    {
+        if (pState->stateIndex == pState->inputBlockSize)
+        {
+            SymCryptKeccakPermute(pState->state);
+            pState->stateIndex = 0;
+        }
+
+        *pbResult = SymCryptKeccakExtractByte(pState);
+        pbResult++;
+        cbResult--;
+    }
+
+    if (bWipe)
+    {
+        // Wipe the Keccak state and make it ready for a new hash computation
+        SymCryptKeccakReset(pState);
+    }
+}
+
+//
+// SymCryptKeccakStateExport
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakStateExport(
+                                                            SYMCRYPT_BLOB_TYPE      type,
+    _In_                                                    PCSYMCRYPT_KECCAK_STATE pState,
+    _Out_writes_bytes_(SYMCRYPT_KECCAK_STATE_EXPORT_SIZE)   PBYTE                   pbBlob)
+{
+
+    SYMCRYPT_ALIGN SYMCRYPT_KECCAK_STATE_EXPORT_BLOB    blob;           // local copy to have proper alignment.
+    C_ASSERT(sizeof(blob) == SYMCRYPT_KECCAK_STATE_EXPORT_SIZE);
+
+    SymCryptWipeKnownSize(&blob, sizeof(blob)); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_KECCAK_STATE_EXPORT_SIZE;
+    blob.header.type = type;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+
+    SymCryptUint64ToLsbFirst(&pState->state[0], &blob.state[0], 25);
+    blob.stateIndex = pState->stateIndex;
+    blob.paddingValue = pState->paddingValue;
+    blob.squeezeMode = pState->squeezeMode;
+
+    SYMCRYPT_ASSERT((PCBYTE)&blob + sizeof(blob) - sizeof(SYMCRYPT_BLOB_TRAILER) == (PCBYTE)&blob.trailer);
+    SymCryptMarvin32(SymCryptMarvin32DefaultSeed, (PCBYTE)&blob, sizeof(blob) - sizeof(SYMCRYPT_BLOB_TRAILER), &blob.trailer.checksum[0]);
+
+    memcpy(pbBlob, &blob, sizeof(blob));
+
+    SymCryptWipeKnownSize(&blob, sizeof(blob));
+    return;
+}
+
+
+//
+// SymCryptKeccakStateImport
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptKeccakStateImport(
+                                                        SYMCRYPT_BLOB_TYPE      type,
+    _Out_                                               PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_bytes_(SYMCRYPT_KECCAK_STATE_EXPORT_SIZE) PCBYTE                  pbBlob)
+{
+    SYMCRYPT_ERROR                  scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_ALIGN SYMCRYPT_KECCAK_STATE_EXPORT_BLOB blob;                       // local copy to have proper alignment.
+    BYTE checksum[8];
+
+    C_ASSERT(sizeof(blob) == SYMCRYPT_KECCAK_STATE_EXPORT_SIZE);
+    memcpy(&blob, pbBlob, sizeof(blob));
+
+    if (blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_KECCAK_STATE_EXPORT_SIZE ||
+        blob.header.type != (UINT32)type)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32(SymCryptMarvin32DefaultSeed, (PCBYTE)&blob, sizeof(blob) - sizeof(SYMCRYPT_BLOB_TRAILER), checksum);
+    if (memcmp(checksum, &blob.trailer.checksum[0], 8) != 0)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptLsbFirstToUint64(&blob.state[0], &pState->state[0], 25);
+    pState->stateIndex = blob.stateIndex;
+    pState->paddingValue = blob.paddingValue;
+    pState->squeezeMode = blob.squeezeMode;
+
+    //
+    // Set state fields based on the blob type and do validation
+    //
+
+    // default values indicate error
+    pState->inputBlockSize = 0;
+    pState->paddingValue = 0;
+
+    switch (blob.header.type)
+    {
+        case SymCryptBlobTypeSha3_224State:
+            pState->inputBlockSize = SYMCRYPT_SHA3_224_INPUT_BLOCK_SIZE;
+            if (blob.paddingValue == SYMCRYPT_SHA3_PADDING_VALUE)
+            {
+                pState->paddingValue = blob.paddingValue;
+            }
+            break;
+        case SymCryptBlobTypeSha3_256State:
+            pState->inputBlockSize = SYMCRYPT_SHA3_256_INPUT_BLOCK_SIZE;
+            if (blob.paddingValue == SYMCRYPT_SHA3_PADDING_VALUE)
+            {
+                pState->paddingValue = blob.paddingValue;
+            }
+            break;
+
+        case SymCryptBlobTypeSha3_384State:
+            pState->inputBlockSize = SYMCRYPT_SHA3_384_INPUT_BLOCK_SIZE;
+            if (blob.paddingValue == SYMCRYPT_SHA3_PADDING_VALUE)
+            {
+                pState->paddingValue = blob.paddingValue;
+            }
+            break;
+
+        case SymCryptBlobTypeSha3_512State:
+            pState->inputBlockSize = SYMCRYPT_SHA3_512_INPUT_BLOCK_SIZE;
+            if (blob.paddingValue == SYMCRYPT_SHA3_PADDING_VALUE)
+            {
+                pState->paddingValue = blob.paddingValue;
+            }
+            break;
+        default:
+            scError = SYMCRYPT_INVALID_BLOB;
+            goto cleanup;
+    }
+
+    if (pState->inputBlockSize == 0 || pState->paddingValue == 0)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    if (pState->stateIndex > pState->inputBlockSize)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Allow stateIndex = inputBlockSize only in squeeze mode
+    if ((pState->stateIndex == pState->inputBlockSize) && !pState->squeezeMode)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(&blob, sizeof(blob));
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/sha3_224.c b/libs/symcrypt/lib/sha3_224.c
new file mode 100644
index 000000000000..69dec7545096
--- /dev/null
+++ b/libs/symcrypt/lib/sha3_224.c
@@ -0,0 +1,141 @@
+//
+// Sha3_224.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+const SYMCRYPT_HASH SymCryptSha3_224Algorithm_default = {
+    &SymCryptSha3_224Init,
+    &SymCryptSha3_224Append,
+    &SymCryptSha3_224Result,
+    NULL,                           // AppendBlocks function is not implemented for SHA-3
+    &SymCryptSha3_224StateCopy,
+    sizeof(SYMCRYPT_SHA3_224_STATE),
+    SYMCRYPT_SHA3_224_RESULT_SIZE,
+    SYMCRYPT_SHA3_224_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SHA3_224_STATE, ks.state),
+    SYMCRYPT_FIELD_SIZE(SYMCRYPT_SHA3_224_STATE, ks.state),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha3_224Algorithm = &SymCryptSha3_224Algorithm_default;
+
+
+//
+// SymCryptSha3_224
+//
+#define ALG     SHA3_224
+#define Alg     Sha3_224
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+//
+// SymCryptSha3_224Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Init(_Out_ PSYMCRYPT_SHA3_224_STATE pState)
+{
+    SymCryptKeccakInit(&pState->ks,
+                        SYMCRYPT_SHA3_224_INPUT_BLOCK_SIZE,
+                        SYMCRYPT_SHA3_PADDING_VALUE);
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+
+//
+// SymCryptSha3_224Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Append(
+    _Inout_             PSYMCRYPT_SHA3_224_STATE    pState,
+    _In_reads_(cbData)  PCBYTE                      pbData,
+                        SIZE_T                      cbData)
+{
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+
+//
+// SymCryptSha3_224Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Result(
+    _Inout_                                     PSYMCRYPT_SHA3_224_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_224_RESULT_SIZE) PBYTE                       pbResult)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, SYMCRYPT_SHA3_224_RESULT_SIZE, TRUE);
+}
+
+
+//
+// SymCryptSha3_224StateExport
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_224_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE) PBYTE                       pbBlob)
+{
+    SYMCRYPT_CHECK_MAGIC(pState);
+    SymCryptKeccakStateExport(SymCryptBlobTypeSha3_224State, &pState->ks, pbBlob);
+}
+
+
+//
+// SymCryptSha3_224StateImport
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_224StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_224_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob)
+{
+    SYMCRYPT_ERROR scError = SymCryptKeccakStateImport(SymCryptBlobTypeSha3_224State, &pState->ks, pbBlob);
+
+    if (scError == SYMCRYPT_NO_ERROR)
+    {
+        SYMCRYPT_SET_MAGIC(pState);
+    }
+
+    return scError;
+}
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE sha3_224KATAnswer[28] = {
+    0xe6, 0x42, 0x82, 0x4c, 0x3f, 0x8c, 0xf2, 0x4a,
+    0xd0, 0x92, 0x34, 0xee, 0x7d, 0x3c, 0x76, 0x6f,
+    0xc9, 0xa3, 0xa5, 0x16, 0x8d, 0x0c, 0x94, 0xad,
+    0x73, 0xb4, 0x6f, 0xdf,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_224Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA3_224_RESULT_SIZE];
+
+    SymCryptSha3_224(SymCryptTestMsg3, sizeof(SymCryptTestMsg3), result);
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, sha3_224KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('SHA3');
+    }
+}
diff --git a/libs/symcrypt/lib/sha3_256.c b/libs/symcrypt/lib/sha3_256.c
new file mode 100644
index 000000000000..aabe11b71a3e
--- /dev/null
+++ b/libs/symcrypt/lib/sha3_256.c
@@ -0,0 +1,141 @@
+//
+// Sha3_256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+const SYMCRYPT_HASH SymCryptSha3_256Algorithm_default = {
+    &SymCryptSha3_256Init,
+    &SymCryptSha3_256Append,
+    &SymCryptSha3_256Result,
+    NULL,                           // AppendBlocks function is not implemented for SHA-3
+    &SymCryptSha3_256StateCopy,
+    sizeof(SYMCRYPT_SHA3_256_STATE),
+    SYMCRYPT_SHA3_256_RESULT_SIZE,
+    SYMCRYPT_SHA3_256_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SHA3_256_STATE, ks.state),
+    SYMCRYPT_FIELD_SIZE(SYMCRYPT_SHA3_256_STATE, ks.state),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha3_256Algorithm = &SymCryptSha3_256Algorithm_default;
+
+
+//
+// SymCryptSha3_256
+//
+#define ALG     SHA3_256
+#define Alg     Sha3_256
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+//
+// SymCryptSha3_256Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Init(_Out_ PSYMCRYPT_SHA3_256_STATE pState)
+{
+    SymCryptKeccakInit(&pState->ks,
+                        SYMCRYPT_SHA3_256_INPUT_BLOCK_SIZE,
+                        SYMCRYPT_SHA3_PADDING_VALUE);
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+
+//
+// SymCryptSha3_256Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Append(
+    _Inout_             PSYMCRYPT_SHA3_256_STATE    pState,
+    _In_reads_(cbData)  PCBYTE                      pbData,
+                        SIZE_T                      cbData)
+{
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+
+//
+// SymCryptSha3_256Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Result(
+    _Inout_                                     PSYMCRYPT_SHA3_256_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_256_RESULT_SIZE) PBYTE                       pbResult)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, SYMCRYPT_SHA3_256_RESULT_SIZE, TRUE);
+}
+
+
+//
+// SymCryptSha3_256StateExport
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_256_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE) PBYTE                       pbBlob)
+{
+    SYMCRYPT_CHECK_MAGIC(pState);
+    SymCryptKeccakStateExport(SymCryptBlobTypeSha3_256State, &pState->ks, pbBlob);
+}
+
+
+//
+// SymCryptSha3_256StateImport
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_256StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_256_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob)
+{
+    SYMCRYPT_ERROR scError = SymCryptKeccakStateImport(SymCryptBlobTypeSha3_256State, &pState->ks, pbBlob);
+
+    if (scError == SYMCRYPT_NO_ERROR)
+    {
+        SYMCRYPT_SET_MAGIC(pState);
+    }
+
+    return scError;
+}
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE sha3_256KATAnswer[32] = {
+    0x3a, 0x98, 0x5d, 0xa7, 0x4f, 0xe2, 0x25, 0xb2,
+    0x04, 0x5c, 0x17, 0x2d, 0x6b, 0xd3, 0x90, 0xbd,
+    0x85, 0x5f, 0x08, 0x6e, 0x3e, 0x9d, 0x52, 0x5b,
+    0x46, 0xbf, 0xe2, 0x45, 0x11, 0x43, 0x15, 0x32
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_256Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA3_256_RESULT_SIZE];
+
+    SymCryptSha3_256(SymCryptTestMsg3, sizeof(SymCryptTestMsg3), result);
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, sha3_256KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('SHA3');
+    }
+}
diff --git a/libs/symcrypt/lib/sha3_384.c b/libs/symcrypt/lib/sha3_384.c
new file mode 100644
index 000000000000..0f557661ac22
--- /dev/null
+++ b/libs/symcrypt/lib/sha3_384.c
@@ -0,0 +1,143 @@
+//
+// Sha3_384.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+const SYMCRYPT_HASH SymCryptSha3_384Algorithm_default = {
+    &SymCryptSha3_384Init,
+    &SymCryptSha3_384Append,
+    &SymCryptSha3_384Result,
+    NULL,                           // AppendBlocks function is not implemented for SHA-3
+    &SymCryptSha3_384StateCopy,
+    sizeof(SYMCRYPT_SHA3_384_STATE),
+    SYMCRYPT_SHA3_384_RESULT_SIZE,
+    SYMCRYPT_SHA3_384_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SHA3_384_STATE, ks.state),
+    SYMCRYPT_FIELD_SIZE(SYMCRYPT_SHA3_384_STATE, ks.state),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha3_384Algorithm = &SymCryptSha3_384Algorithm_default;
+
+
+//
+// SymCryptSha3_384
+//
+#define ALG     SHA3_384
+#define Alg     Sha3_384
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+//
+// SymCryptSha3_384Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Init(_Out_ PSYMCRYPT_SHA3_384_STATE pState)
+{
+    SymCryptKeccakInit(&pState->ks,
+                        SYMCRYPT_SHA3_384_INPUT_BLOCK_SIZE,
+                        SYMCRYPT_SHA3_PADDING_VALUE);
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+
+//
+// SymCryptSha3_384Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Append(
+    _Inout_             PSYMCRYPT_SHA3_384_STATE    pState,
+    _In_reads_(cbData)  PCBYTE                      pbData,
+                        SIZE_T                      cbData)
+{
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+
+//
+// SymCryptSha3_384Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Result(
+    _Inout_                                     PSYMCRYPT_SHA3_384_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_384_RESULT_SIZE) PBYTE                       pbResult)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, SYMCRYPT_SHA3_384_RESULT_SIZE, TRUE);
+}
+
+
+//
+// SymCryptSha3_384StateExport
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_384_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE) PBYTE                       pbBlob)
+{
+    SYMCRYPT_CHECK_MAGIC(pState);
+    SymCryptKeccakStateExport(SymCryptBlobTypeSha3_384State, &pState->ks, pbBlob);
+}
+
+
+//
+// SymCryptSha3_384StateImport
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_384StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_384_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob)
+{
+    SYMCRYPT_ERROR scError = SymCryptKeccakStateImport(SymCryptBlobTypeSha3_384State, &pState->ks, pbBlob);
+
+    if (scError == SYMCRYPT_NO_ERROR)
+    {
+        SYMCRYPT_SET_MAGIC(pState);
+    }
+
+    return scError;
+}
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE sha3_384KATAnswer[48] = {
+    0xec, 0x01, 0x49, 0x82, 0x88, 0x51, 0x6f, 0xc9,
+    0x26, 0x45, 0x9f, 0x58, 0xe2, 0xc6, 0xad, 0x8d,
+    0xf9, 0xb4, 0x73, 0xcb, 0x0f, 0xc0, 0x8c, 0x25,
+    0x96, 0xda, 0x7c, 0xf0, 0xe4, 0x9b, 0xe4, 0xb2,
+    0x98, 0xd8, 0x8c, 0xea, 0x92, 0x7a, 0xc7, 0xf5,
+    0x39, 0xf1, 0xed, 0xf2, 0x28, 0x37, 0x6d, 0x25
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_384Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA3_384_RESULT_SIZE];
+
+    SymCryptSha3_384(SymCryptTestMsg3, sizeof(SymCryptTestMsg3), result);
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, sha3_384KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('SHA3');
+    }
+}
diff --git a/libs/symcrypt/lib/sha3_512.c b/libs/symcrypt/lib/sha3_512.c
new file mode 100644
index 000000000000..71eccc71a648
--- /dev/null
+++ b/libs/symcrypt/lib/sha3_512.c
@@ -0,0 +1,144 @@
+//
+// Sha3_512.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+const SYMCRYPT_HASH SymCryptSha3_512Algorithm_default = {
+    &SymCryptSha3_512Init,
+    &SymCryptSha3_512Append,
+    &SymCryptSha3_512Result,
+    NULL,                           // AppendBlocks function is not implemented for SHA-3
+    &SymCryptSha3_512StateCopy,
+    sizeof(SYMCRYPT_SHA3_512_STATE),
+    SYMCRYPT_SHA3_512_RESULT_SIZE,
+    SYMCRYPT_SHA3_512_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SHA3_512_STATE, ks.state),
+    SYMCRYPT_FIELD_SIZE(SYMCRYPT_SHA3_512_STATE, ks.state),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha3_512Algorithm = &SymCryptSha3_512Algorithm_default;
+
+
+//
+// SymCryptSha3_512
+//
+#define ALG     SHA3_512
+#define Alg     Sha3_512
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+//
+// SymCryptSha3_512Init
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Init(_Out_ PSYMCRYPT_SHA3_512_STATE pState)
+{
+    SymCryptKeccakInit(&pState->ks,
+                        SYMCRYPT_SHA3_512_INPUT_BLOCK_SIZE,
+                        SYMCRYPT_SHA3_PADDING_VALUE);
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+
+//
+// SymCryptSha3_512Append
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Append(
+    _Inout_             PSYMCRYPT_SHA3_512_STATE    pState,
+    _In_reads_(cbData)  PCBYTE                      pbData,
+                        SIZE_T                      cbData)
+{
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+
+//
+// SymCryptSha3_512Result
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Result(
+    _Inout_                                     PSYMCRYPT_SHA3_512_STATE    pState,
+    _Out_writes_(SYMCRYPT_SHA3_512_RESULT_SIZE) PBYTE                       pbResult)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, SYMCRYPT_SHA3_512_RESULT_SIZE, TRUE);
+}
+
+
+//
+// SymCryptSha3_512StateExport
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512StateExport(
+    _In_                                                    PCSYMCRYPT_SHA3_512_STATE   pState,
+    _Out_writes_bytes_(SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE) PBYTE                       pbBlob)
+{
+    SYMCRYPT_CHECK_MAGIC(pState);
+    SymCryptKeccakStateExport(SymCryptBlobTypeSha3_512State, &pState->ks, pbBlob);
+}
+
+//
+// SymCryptSha3_512StateExport
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha3_512StateImport(
+    _Out_                                                   PSYMCRYPT_SHA3_512_STATE    pState,
+    _In_reads_bytes_(SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE)   PCBYTE                      pbBlob)
+{
+    SYMCRYPT_ERROR scError = SymCryptKeccakStateImport(SymCryptBlobTypeSha3_512State, &pState->ks, pbBlob);
+
+    if (scError == SYMCRYPT_NO_ERROR)
+    {
+        SYMCRYPT_SET_MAGIC(pState);
+    }
+
+    return scError;
+}
+
+
+//
+// Simple test vector for FIPS module testing
+//
+
+static const BYTE sha3_512KATAnswer[64] = {
+    0xb7, 0x51, 0x85, 0x0b, 0x1a, 0x57, 0x16, 0x8a,
+    0x56, 0x93, 0xcd, 0x92, 0x4b, 0x6b, 0x09, 0x6e,
+    0x08, 0xf6, 0x21, 0x82, 0x74, 0x44, 0xf7, 0x0d,
+    0x88, 0x4f, 0x5d, 0x02, 0x40, 0xd2, 0x71, 0x2e,
+    0x10, 0xe1, 0x16, 0xe9, 0x19, 0x2a, 0xf3, 0xc9,
+    0x1a, 0x7e, 0xc5, 0x76, 0x47, 0xe3, 0x93, 0x40,
+    0x57, 0x34, 0x0b, 0x4c, 0xf4, 0x08, 0xd5, 0xa5,
+    0x65, 0x92, 0xf8, 0x27, 0x4e, 0xec, 0x53, 0xf0
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha3_512Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA3_512_RESULT_SIZE];
+
+    SymCryptSha3_512(SymCryptTestMsg3, sizeof(SymCryptTestMsg3), result);
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, sha3_512KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('SHA3');
+    }
+}
diff --git a/libs/symcrypt/lib/sha512.c b/libs/symcrypt/lib/sha512.c
new file mode 100644
index 000000000000..0b763c546771
--- /dev/null
+++ b/libs/symcrypt/lib/sha512.c
@@ -0,0 +1,1715 @@
+//
+// Sha512.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement SHA2-512 from FIPS 180-2
+//
+
+
+#include "precomp.h"
+
+//
+// SHA-512 uses 80 magic constants of 64 bits each. These are
+// referred to as K^{512}_i for i=0...79 by FIPS 180-2.
+// We use a static array as that does not pollute the linker name space
+// For performance we align to the cache line size of 64 bytes
+// We have one extra value at the end to allow an XMM read from each element
+// of the array.
+//
+SYMCRYPT_ALIGN_AT( 64 ) const  UINT64 SymCryptSha512K[81] = {
+    0x428a2f98d728ae22UL, 0x7137449123ef65cdUL,
+    0xb5c0fbcfec4d3b2fUL, 0xe9b5dba58189dbbcUL,
+    0x3956c25bf348b538UL, 0x59f111f1b605d019UL,
+    0x923f82a4af194f9bUL, 0xab1c5ed5da6d8118UL,
+    0xd807aa98a3030242UL, 0x12835b0145706fbeUL,
+    0x243185be4ee4b28cUL, 0x550c7dc3d5ffb4e2UL,
+    0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL,
+    0x9bdc06a725c71235UL, 0xc19bf174cf692694UL,
+    0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL,
+    0x0fc19dc68b8cd5b5UL, 0x240ca1cc77ac9c65UL,
+    0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL,
+    0x5cb0a9dcbd41fbd4UL, 0x76f988da831153b5UL,
+    0x983e5152ee66dfabUL, 0xa831c66d2db43210UL,
+    0xb00327c898fb213fUL, 0xbf597fc7beef0ee4UL,
+    0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL,
+    0x06ca6351e003826fUL, 0x142929670a0e6e70UL,
+    0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL,
+    0x4d2c6dfc5ac42aedUL, 0x53380d139d95b3dfUL,
+    0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL,
+    0x81c2c92e47edaee6UL, 0x92722c851482353bUL,
+    0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL,
+    0xc24b8b70d0f89791UL, 0xc76c51a30654be30UL,
+    0xd192e819d6ef5218UL, 0xd69906245565a910UL,
+    0xf40e35855771202aUL, 0x106aa07032bbd1b8UL,
+    0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL,
+    0x2748774cdf8eeb99UL, 0x34b0bcb5e19b48a8UL,
+    0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL,
+    0x5b9cca4f7763e373UL, 0x682e6ff3d6b2b8a3UL,
+    0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL,
+    0x84c87814a1f0ab72UL, 0x8cc702081a6439ecUL,
+    0x90befffa23631e28UL, 0xa4506cebde82bde9UL,
+    0xbef9a3f7b2c67915UL, 0xc67178f2e372532bUL,
+    0xca273eceea26619cUL, 0xd186b8c721c0c207UL,
+    0xeada7dd6cde0eb1eUL, 0xf57d4f7fee6ed178UL,
+    0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL,
+    0x113f9804bef90daeUL, 0x1b710b35131c471bUL,
+    0x28db77f523047d84UL, 0x32caab7b40c72493UL,
+    0x3c9ebe0a15c9bebcUL, 0x431d67c49c100d4cUL,
+    0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL,
+    0x5fcb6fab3ad6faecUL, 0x6c44198c4a475817UL,
+};
+
+//
+// Initial states
+//
+const UINT64 SymCryptSha512InitialState[8] = {
+    0x6a09e667f3bcc908UL,
+    0xbb67ae8584caa73bUL,
+    0x3c6ef372fe94f82bUL,
+    0xa54ff53a5f1d36f1UL,
+    0x510e527fade682d1UL,
+    0x9b05688c2b3e6c1fUL,
+    0x1f83d9abfb41bd6bUL,
+    0x5be0cd19137e2179UL,
+};
+
+const UINT64 SymCryptSha384InitialState[8] = {
+    0xcbbb9d5dc1059ed8UL,
+    0x629a292a367cd507UL,
+    0x9159015a3070dd17UL,
+    0x152fecd8f70e5939UL,
+    0x67332667ffc00b31UL,
+    0x8eb44a8768581511UL,
+    0xdb0c2e0d64f98fa7UL,
+    0x47b5481dbefa4fa4UL,
+};
+
+const UINT64 SymCryptSha512_224InitialState[8] = {
+    0x8c3d37c819544da2UL,
+    0x73e1996689dcd4d6UL,
+    0x1dfab7ae32ff9c82UL,
+    0x679dd514582f9fcfUL,
+    0x0f6d2b697bd44da8UL,
+    0x77e36f7304c48942UL,
+    0x3f9d85a86a1d36c8UL,
+    0x1112e6ad91d692a1UL,
+};
+
+const UINT64 SymCryptSha512_256InitialState[8] = {
+    0x22312194fc2bf72cUL,
+    0x9f555fa3c84c64c2UL,
+    0x2393b86b6f53b151UL,
+    0x963877195940eabdUL,
+    0x96283ee2a88effe3UL,
+    0xbe5e1e2553863992UL,
+    0x2b0199fc2c85b8aaUL,
+    0x0eb72ddc81c52ca2UL,
+};
+
+
+//
+// Todo: this structure pulls in the SHA284 code anytime someone uses
+// SHA-512; should be split into a separate file.
+//
+const SYMCRYPT_HASH SymCryptSha384Algorithm_default = {
+    &SymCryptSha384Init,
+    &SymCryptSha384Append,
+    &SymCryptSha384Result,
+    &SymCryptSha512AppendBlocks,
+    &SymCryptSha384StateCopy,
+    sizeof( SYMCRYPT_SHA384_STATE ),
+    SYMCRYPT_SHA384_RESULT_SIZE,
+    SYMCRYPT_SHA384_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA384_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA384_STATE, chain ),
+};
+
+const SYMCRYPT_HASH SymCryptSha512Algorithm_default = {
+    &SymCryptSha512Init,
+    &SymCryptSha512Append,
+    &SymCryptSha512Result,
+    &SymCryptSha512AppendBlocks,
+    &SymCryptSha512StateCopy,
+    sizeof( SYMCRYPT_SHA512_STATE ),
+    SYMCRYPT_SHA512_RESULT_SIZE,
+    SYMCRYPT_SHA512_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA512_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA512_STATE, chain ),
+};
+
+const SYMCRYPT_HASH SymCryptSha512_224Algorithm_default = {
+    &SymCryptSha512_224Init,
+    &SymCryptSha512_224Append,
+    &SymCryptSha512_224Result,
+    &SymCryptSha512AppendBlocks,
+    &SymCryptSha512_224StateCopy,
+    sizeof( SYMCRYPT_SHA512_224_STATE ),
+    SYMCRYPT_SHA512_224_RESULT_SIZE,
+    SYMCRYPT_SHA512_224_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA512_224_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA512_224_STATE, chain ),
+};
+
+const SYMCRYPT_HASH SymCryptSha512_256Algorithm_default = {
+    &SymCryptSha512_256Init,
+    &SymCryptSha512_256Append,
+    &SymCryptSha512_256Result,
+    &SymCryptSha512AppendBlocks,
+    &SymCryptSha512_256StateCopy,
+    sizeof( SYMCRYPT_SHA512_256_STATE ),
+    SYMCRYPT_SHA512_256_RESULT_SIZE,
+    SYMCRYPT_SHA512_256_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET( SYMCRYPT_SHA512_256_STATE, chain ),
+    SYMCRYPT_FIELD_SIZE( SYMCRYPT_SHA512_256_STATE, chain ),
+};
+
+const PCSYMCRYPT_HASH SymCryptSha384Algorithm = &SymCryptSha384Algorithm_default;
+const PCSYMCRYPT_HASH SymCryptSha512Algorithm = &SymCryptSha512Algorithm_default;
+const PCSYMCRYPT_HASH SymCryptSha512_224Algorithm = &SymCryptSha512_224Algorithm_default;
+const PCSYMCRYPT_HASH SymCryptSha512_256Algorithm = &SymCryptSha512_256Algorithm_default;
+
+//
+// SymCryptSha384
+//
+#define ALG SHA384
+#define Alg Sha384
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+//
+// SymCryptSha512
+//
+#define ALG SHA512
+#define Alg Sha512
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+//
+// SymCryptSha512/224
+//
+#define ALG SHA512_224
+#define Alg Sha512_224
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+//
+// SymCryptSha512/256
+//
+#define ALG SHA512_256
+#define Alg Sha512_256
+#include "hash_pattern.c"
+#undef ALG
+#undef Alg
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Init( _Out_ PSYMCRYPT_SHA512_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthH = 0;
+    pState->dataLengthL = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &SymCryptSha512InitialState[0], sizeof( SymCryptSha512InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Init( _Out_ PSYMCRYPT_SHA384_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthH = 0;
+    pState->dataLengthL = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &SymCryptSha384InitialState[0], sizeof( SymCryptSha384InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Init( _Out_ PSYMCRYPT_SHA512_224_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthH = 0;
+    pState->dataLengthL = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &SymCryptSha512_224InitialState[0], sizeof( SymCryptSha512_224InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Init( _Out_ PSYMCRYPT_SHA512_256_STATE pState )
+{
+    SYMCRYPT_SET_MAGIC( pState );
+
+    pState->dataLengthH = 0;
+    pState->dataLengthL = 0;
+    pState->bytesInBuffer = 0;
+
+    memcpy( &pState->chain.H[0], &SymCryptSha512_256InitialState[0], sizeof( SymCryptSha512_256InitialState ) );
+
+    //
+    // There is no need to initialize the buffer part of the state as that will be
+    // filled before it is used.
+    //
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Append(
+    _Inout_                 PSYMCRYPT_SHA512_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData )
+{
+    UINT32 bytesInBuffer;
+    UINT32 freeInBuffer;
+    SIZE_T tmp;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    pState->dataLengthL += cbData;
+    if( pState->dataLengthL < cbData ) {
+        pState->dataLengthH++;
+    }
+
+    bytesInBuffer = pState->bytesInBuffer;
+
+    //
+    // If previous data in buffer, buffer new input and transform if possible.
+    //
+    if( bytesInBuffer > 0 )
+    {
+        SYMCRYPT_ASSERT( SYMCRYPT_SHA512_INPUT_BLOCK_SIZE > bytesInBuffer );
+
+        freeInBuffer = SYMCRYPT_SHA512_INPUT_BLOCK_SIZE - bytesInBuffer;
+        if( cbData < freeInBuffer )
+        {
+            //
+            // All the data will fit in the buffer.
+            // We don't do anything here.
+            // As cbData < inputBlockSize the bulk data processing is skipped,
+            // and the data will be copied to the buffer at the end
+            // of this code.
+        } else {
+            //
+            // Enough data to fill the whole buffer & process it
+            //
+            memcpy(&pState->buffer[bytesInBuffer], pbData, freeInBuffer);
+            pbData += freeInBuffer;
+            cbData -= freeInBuffer;
+            SymCryptSha512AppendBlocks( &pState->chain, &pState->buffer[0], SYMCRYPT_SHA512_INPUT_BLOCK_SIZE, &tmp );
+
+            bytesInBuffer = 0;
+        }
+    }
+
+    //
+    // Internal buffer is empty; process all remaining whole blocks in the input
+    //
+    if( cbData >= SYMCRYPT_SHA512_INPUT_BLOCK_SIZE )
+    {
+        SymCryptSha512AppendBlocks( &pState->chain, pbData, cbData, &tmp );
+        SYMCRYPT_ASSERT( tmp < SYMCRYPT_SHA512_INPUT_BLOCK_SIZE );
+        pbData += cbData - tmp;
+        cbData = tmp;
+    }
+
+    SYMCRYPT_ASSERT( cbData < SYMCRYPT_SHA512_INPUT_BLOCK_SIZE );
+
+    //
+    // buffer remaining input if necessary.
+    //
+    if( cbData > 0 )
+    {
+        memcpy( &pState->buffer[bytesInBuffer], pbData, cbData );
+        bytesInBuffer += (UINT32) cbData;
+    }
+
+    pState->bytesInBuffer = bytesInBuffer;
+
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Append(
+    _Inout_                 PSYMCRYPT_SHA384_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                  pbData,
+                            SIZE_T                  cbData )
+{
+
+    SymCryptSha512Append( (PSYMCRYPT_SHA512_STATE)pState, pbData, cbData );
+
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Append(
+    _Inout_                 PSYMCRYPT_SHA512_224_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData )
+{
+    SymCryptSha512Append( (PSYMCRYPT_SHA512_STATE)pState, pbData, cbData );
+}
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Append(
+    _Inout_                 PSYMCRYPT_SHA512_256_STATE  pState,
+    _In_reads_( cbData )    PCBYTE                      pbData,
+                            SIZE_T                      cbData )
+{
+    SymCryptSha512Append( (PSYMCRYPT_SHA512_STATE)pState, pbData, cbData );
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Result(
+    _Inout_                                     PSYMCRYPT_SHA512_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA512_RESULT_SIZE ) PBYTE                   pbResult )
+{
+    UINT32 bytesInBuffer;
+    SIZE_T tmp;
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    bytesInBuffer = pState->bytesInBuffer;
+
+    //
+    // The buffer is never completely full, so we can always put the first
+    // padding byte in.
+    //
+    pState->buffer[bytesInBuffer++] = 0x80;
+
+    if( bytesInBuffer > 128-16 ) {
+        //
+        // No room for the rest of the padding. Pad with zeroes & process block
+        // bytesInBuffer is at most 128, so we do not have an integer underflow
+        //
+        SymCryptWipe( &pState->buffer[bytesInBuffer], 128-bytesInBuffer );
+        SymCryptSha512AppendBlocks( &pState->chain, pState->buffer, 128, &tmp );
+        bytesInBuffer = 0;
+    }
+
+    //
+    // Set rest of padding
+    // We wipe to the end of the buffer as it is 16-aligned,
+    // and it is faster to wipe to an aligned point
+    //
+    SymCryptWipe( &pState->buffer[bytesInBuffer], 128-bytesInBuffer );
+    SYMCRYPT_STORE_MSBFIRST64( &pState->buffer[128-16], (pState->dataLengthH << 3) + (pState->dataLengthL >> 61)  );
+    SYMCRYPT_STORE_MSBFIRST64( &pState->buffer[128- 8], (pState->dataLengthL << 3) );
+
+    SymCryptSha512AppendBlocks( &pState->chain, pState->buffer, 128, &tmp );
+
+    SymCryptUint64ToMsbFirst( &pState->chain.H[0], pbResult, 8 );
+
+    //
+    // We have to wipe the whole state because the Init call
+    // might be optimized away by a smart compiler.
+    //
+    SymCryptWipeKnownSize( pState, sizeof( *pState ) );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+    memcpy( &pState->chain.H[0], &SymCryptSha512InitialState[0], sizeof( SymCryptSha512InitialState ) );
+    }
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Result(
+    _Inout_                                     PSYMCRYPT_SHA384_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA384_RESULT_SIZE ) PBYTE                   pbResult )
+{
+    //
+    // For simplicity we re-use SymCryptSha512Result. This is slightly slower,
+    // but SHA-384 isn't used that much.
+    //
+    SYMCRYPT_ALIGN BYTE sha512Result[SYMCRYPT_SHA512_RESULT_SIZE];      // Buffer for SHA-512 output
+
+    //
+    // The SHA-384 result is the first 48 bytes of the SHA-512 result of our state
+    //
+    SymCryptSha512Result( (PSYMCRYPT_SHA512_STATE)pState, sha512Result );
+    memcpy( pbResult, sha512Result, SYMCRYPT_SHA384_RESULT_SIZE );
+
+    //
+    // The buffer was already wiped by the SymCryptSha512Result function, we
+    // just have to re-initialize for SHA-384
+    //
+    SymCryptSha384Init( pState );
+
+    SymCryptWipeKnownSize( sha512Result, sizeof( sha512Result ) );
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Result(
+    _Inout_                                         PSYMCRYPT_SHA512_224_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA512_224_RESULT_SIZE ) PBYTE                       pbResult )
+{
+    SYMCRYPT_ALIGN BYTE sha512Result[SYMCRYPT_SHA512_RESULT_SIZE];      // Buffer for SHA-512 output
+
+    //
+    // The SHA-512/224 result is the first 28 bytes of the SHA-512 result of our state
+    //
+    SymCryptSha512Result( (PSYMCRYPT_SHA512_STATE)pState, sha512Result );
+    memcpy( pbResult, sha512Result, SYMCRYPT_SHA512_224_RESULT_SIZE );
+
+    //
+    // The buffer was already wiped by the SymCryptSha512Result function, we
+    // just have to re-initialize for SHA-512/224
+    //
+    SymCryptSha512_224Init( pState );
+
+    SymCryptWipeKnownSize( sha512Result, sizeof( sha512Result ) );
+}
+
+
+SYMCRYPT_NOINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Result(
+    _Inout_                                         PSYMCRYPT_SHA512_256_STATE  pState,
+    _Out_writes_( SYMCRYPT_SHA512_256_RESULT_SIZE ) PBYTE                       pbResult )
+{
+    SYMCRYPT_ALIGN BYTE sha512Result[SYMCRYPT_SHA512_RESULT_SIZE];      // Buffer for SHA-512 output
+
+    //
+    // The SHA-512/256 result is the first 32 bytes of the SHA-512 result of our state
+    //
+    SymCryptSha512Result( (PSYMCRYPT_SHA512_STATE)pState, sha512Result );
+    memcpy( pbResult, sha512Result, SYMCRYPT_SHA512_256_RESULT_SIZE );
+
+    //
+    // The buffer was already wiped by the SymCryptSha512Result function, we
+    // just have to re-initialize for SHA-512/256
+    //
+    SymCryptSha512_256Init( pState );
+
+    SymCryptWipeKnownSize( sha512Result, sizeof( sha512Result ) );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512StateExportCore(
+    _In_                                                    PCSYMCRYPT_SHA512_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_STATE_EXPORT_SIZE ) PBYTE                   pbBlob,
+    _In_                                                    UINT32                  type )
+{
+    SYMCRYPT_ALIGN SYMCRYPT_SHA512_STATE_EXPORT_BLOB blob;           // local copy to have proper alignment.
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_SHA512_STATE_EXPORT_SIZE );
+
+    SYMCRYPT_CHECK_MAGIC( pState );
+
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) ); // wipe to avoid any data leakage
+
+    blob.header.magic = SYMCRYPT_BLOB_MAGIC;
+    blob.header.size = SYMCRYPT_SHA512_STATE_EXPORT_SIZE;
+    blob.header.type = type;
+
+    //
+    // Copy the relevant data. Buffer will be 0-padded.
+    //
+
+    SymCryptUint64ToMsbFirst( &pState->chain.H[0], &blob.chain[0], 8 );
+    blob.dataLengthL = pState->dataLengthL;
+    blob.dataLengthH = pState->dataLengthH;
+    memcpy( &blob.buffer[0], &pState->buffer[0], blob.dataLengthL & 0x7f );
+
+    SYMCRYPT_ASSERT( (PCBYTE) &blob + sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ) == (PCBYTE) &blob.trailer );
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), &blob.trailer.checksum[0] );
+
+    memcpy( pbBlob, &blob, sizeof( blob ) );
+
+//cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof( blob ) );
+    return;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512StateExport(
+    _In_                                                    PCSYMCRYPT_SHA512_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_STATE_EXPORT_SIZE ) PBYTE                   pbBlob )
+{
+    SymCryptSha512StateExportCore( pState, pbBlob, SymCryptBlobTypeSha512State );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384StateExport(
+    _In_                                                    PCSYMCRYPT_SHA384_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA384_STATE_EXPORT_SIZE ) PBYTE                   pbBlob )
+{
+    SymCryptSha512StateExportCore( (PCSYMCRYPT_SHA512_STATE)pState, pbBlob, SymCryptBlobTypeSha384State );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224StateExport(
+    _In_                                                        PCSYMCRYPT_SHA512_224_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_224_STATE_EXPORT_SIZE ) PBYTE                       pbBlob )
+{
+    SymCryptSha512StateExportCore( (PCSYMCRYPT_SHA512_STATE)pState, pbBlob, SymCryptBlobTypeSha512_224State );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256StateExport(
+    _In_                                                        PCSYMCRYPT_SHA512_256_STATE pState,
+    _Out_writes_bytes_( SYMCRYPT_SHA512_256_STATE_EXPORT_SIZE ) PBYTE                       pbBlob )
+{
+    SymCryptSha512StateExportCore( (PCSYMCRYPT_SHA512_STATE)pState, pbBlob, SymCryptBlobTypeSha512_256State );
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512StateImportCore(
+    _Out_                                                   PSYMCRYPT_SHA512_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob,
+    _In_                                                    UINT32                  type )
+{
+    SYMCRYPT_ERROR                                      scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_ALIGN SYMCRYPT_SHA512_STATE_EXPORT_BLOB    blob;                       // local copy to have proper alignment.
+    BYTE                                                checksum[8];
+
+    C_ASSERT( sizeof( blob ) == SYMCRYPT_SHA512_STATE_EXPORT_SIZE );
+    memcpy( &blob, pbBlob, sizeof( blob ) );
+
+    if( blob.header.magic != SYMCRYPT_BLOB_MAGIC ||
+        blob.header.size != SYMCRYPT_SHA512_STATE_EXPORT_SIZE ||
+        blob.header.type != type )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, (PCBYTE) &blob, sizeof( blob ) - sizeof( SYMCRYPT_BLOB_TRAILER ), checksum );
+    if( memcmp( checksum, &blob.trailer.checksum[0], 8 ) != 0 )
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    SymCryptMsbFirstToUint64( &blob.chain[0], &pState->chain.H[0], 8 );
+    pState->dataLengthL = blob.dataLengthL;
+    pState->dataLengthH = blob.dataLengthH;
+    pState->bytesInBuffer = blob.dataLengthL & 0x7f;
+    memcpy( &pState->buffer[0], &blob.buffer[0], pState->bytesInBuffer );
+
+    SYMCRYPT_SET_MAGIC( pState );
+
+cleanup:
+    SymCryptWipeKnownSize( &blob, sizeof(blob) );
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512StateImport(
+    _Out_                                                   PSYMCRYPT_SHA512_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob )
+{
+    return SymCryptSha512StateImportCore( pState, pbBlob, SymCryptBlobTypeSha512State );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha384StateImport(
+    _Out_                                                   PSYMCRYPT_SHA384_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA384_STATE_EXPORT_SIZE)    PCBYTE                  pbBlob )
+{
+    return SymCryptSha512StateImportCore( (PSYMCRYPT_SHA512_STATE)pState, pbBlob, SymCryptBlobTypeSha384State );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512_224StateImport(
+    _Out_                                                       PSYMCRYPT_SHA512_224_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_224_STATE_EXPORT_SIZE)    PCBYTE                      pbBlob )
+{
+    return SymCryptSha512StateImportCore( (PSYMCRYPT_SHA512_STATE)pState, pbBlob, SymCryptBlobTypeSha512_224State );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSha512_256StateImport(
+    _Out_                                                       PSYMCRYPT_SHA512_256_STATE  pState,
+    _In_reads_bytes_( SYMCRYPT_SHA512_256_STATE_EXPORT_SIZE)    PCBYTE                      pbBlob )
+{
+    return SymCryptSha512StateImportCore( (PSYMCRYPT_SHA512_STATE)pState, pbBlob, SymCryptBlobTypeSha512_256State );
+}
+
+
+//
+// A simple test case intended for module testing for
+// FIPS compliance.
+// This is the one-block example message from FIPS 180-2 appendix C
+//
+
+const BYTE SymCryptSha512KATAnswer[64] =
+{
+    0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+    0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+    0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+    0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+    0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+    0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+    0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+    0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA512_RESULT_SIZE];
+
+    SymCryptSha512( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, SymCryptSha512KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SH51' );
+    }
+}
+
+//
+// A simple test case intended for module testing for
+// FIPS compliance.
+// This is the one-block example message from FIPS 180-2 appendix D
+//
+
+const BYTE SymCryptSha384KATAnswer[ 48 ] =
+{
+    0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
+    0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
+    0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+    0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
+    0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+    0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha384Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA384_RESULT_SIZE];
+
+    SymCryptSha384( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, SymCryptSha384KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SH38' );
+    }
+}
+
+//
+// Simple test vector for FIPS module testing
+//
+
+const BYTE SymCryptSha512_224KATAnswer[ 28 ] =
+{
+    0x46, 0x34, 0x27, 0x0f, 0x70, 0x7b, 0x6a, 0x54,
+    0xda, 0xae, 0x75, 0x30, 0x46, 0x08, 0x42, 0xe2,
+    0x0e, 0x37, 0xed, 0x26, 0x5c, 0xee, 0xe9, 0xa4,
+    0x3e, 0x89, 0x24, 0xaa,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_224Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA512_224_RESULT_SIZE];
+
+    SymCryptSha512_224( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, SymCryptSha512_224KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SH51' );
+    }
+}
+
+//
+// Simple test vector for FIPS module testing
+//
+
+const BYTE SymCryptSha512_256KATAnswer[ 32 ] =
+{
+    0x53, 0x04, 0x8e, 0x26, 0x81, 0x94, 0x1e, 0xf9,
+    0x9b, 0x2e, 0x29, 0xb7, 0x6b, 0x4c, 0x7d, 0xab,
+    0xe4, 0xc2, 0xd0, 0xc6, 0x34, 0xfc, 0x6d, 0x46,
+    0xe0, 0xe2, 0xf1, 0x31, 0x07, 0xe7, 0xaf, 0x23,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512_256Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHA512_256_RESULT_SIZE];
+
+    SymCryptSha512_256( SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), result );
+
+    SymCryptInjectError( result, sizeof( result ) );
+
+    if( memcmp( result, SymCryptSha512_256KATAnswer, sizeof( result ) ) != 0 ) {
+        SymCryptFatal( 'SH51' );
+    }
+}
+
+//
+// We keep multiple implementations in this file.
+// This allows us to switch different platforms to different implementations, whichever
+// is faster. Even if we don't use a particular implementation in one release,
+// we keep it around in case it becomes the preferred one for a new CPU release.
+// (Performance can change a lot with changes in micro-architecture.)
+//
+
+//===================================================================================
+// Implementation of compression function using UINT64s
+//
+
+//
+// For documentation on these function see FIPS 180-2
+//
+// MAJ and CH are the functions Maj and Ch from the standard.
+// CSIGMA0 and CSIGMA1 are the capital sigma functions.
+// LSIGMA0 and LSIGMA1 are the lowercase sigma functions.
+//
+// The canonical definitions of the MAJ and CH functions are:
+//#define MAJ( x, y, z )    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+//#define CH( x, y, z )  (((x) & (y)) ^ ((~(x)) & (z)))
+// We use optimized versions defined below
+//
+#define MAJ( x, y, z )  ((((z) | (y)) & (x) ) | ((z) & (y)))
+#define CH( x, y, z )  ((((z) ^ (y)) & (x)) ^ (z))
+
+//
+// The four Sigma functions
+//
+
+//#define CSIGMA0( x )    (ROR64((x), 28) ^ ROR64((x), 34) ^ ROR64((x), 39))
+//#define CSIGMA1( x )    (ROR64((x), 14) ^ ROR64((x), 18) ^ ROR64((x), 41))
+//#define LSIGMA0( x )    (ROR64((x),  1) ^ ROR64((x),  8) ^ ((x)>> 7))
+//#define LSIGMA1( x )    (ROR64((x), 19) ^ ROR64((x), 61) ^ ((x)>> 6))
+
+#define CSIGMA0( x )	(ROR64((ROR64((x), 6) ^ ROR64((x), 11) ^ (x)), 28))
+#define CSIGMA1( x )	(ROR64((ROR64((x), 4) ^ ROR64((x), 27) ^ (x)), 14))
+#define LSIGMA0( x )    (ROR64((x) ^ ROR64((x),  7),  1) ^ ((x)>> 7))
+#define LSIGMA1( x )    (ROR64((x) ^ ROR64((x), 42), 19) ^ ((x)>> 6))
+
+
+
+//
+// The values a-h were stored in an array called ah.
+// We have unrolled the loop 16 times. This makes both the indices into
+// the ah array constant, and it makes the message addressing constant.
+// This provides a significant speed improvement, at the cost of making
+// the main loop about 4 kB in code.
+//
+// Initial round; r16 is the round number mod 16
+// ah[ r16   &7] = h
+// ah[(r16+1)&7] = g;
+// ah[(r16+2)&7] = f;
+// ah[(r16+3)&7] = e;
+// ah[(r16+4)&7] = d;
+// ah[(r16+5)&7] = c;
+// ah[(r16+6)&7] = b;
+// ah[(r16+7)&7] = a;
+//
+// Unfortunately, the compiler seems to choke on this, allocating an extra variable for
+// each of the array indices, with duplicate stores to both locations.
+//
+
+//
+// The core round, after the message word has been computed for this round and put in Wt.
+// r16 is the round number modulo 16. (Static after loop unrolling)
+// r is the round number
+#define CROUND( a, b, c, d, e, f, g, h, r, r16 ) {;\
+    W[r16] = Wt; \
+    h += CSIGMA1(e) + CH(e, f, g) + SymCryptSha512K[r] + Wt;\
+    d += h;\
+    h += CSIGMA0(a) + MAJ(a, b, c);\
+}
+
+//
+// Initial round that reads the message.
+// r is the round number 0..15
+//
+#define IROUND( a, b, c, d, e, f, g, h, r ) {\
+    Wt = SYMCRYPT_LOAD_MSBFIRST64( &pbData[ 8*r ] );\
+    CROUND( a, b, c, d, e, f, g, h, r, r);\
+    }
+//
+// Subsequent rounds.
+// r is the round number, r16 is the round number mod 16.
+// These are separate as typically r is run-time and r16 is compile time constant.
+//
+#define FROUND( a, b, c, d, e, f, g, h, r, r16 ) {                                      \
+    Wt = LSIGMA1( W[(r16-2) & 15] ) +   W[(r16-7) & 15] +    \
+         LSIGMA0( W[(r16-15) & 15]) +   W[r16 & 15];       \
+    CROUND( a, b, c, d, e, f, g, h, r, r16 ); \
+    }
+
+//
+// This is the core routine that does the actual hard work
+// This is based on the older one in RSA32LIB by Scott Field from 2001
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ull(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN UINT64 W[16];
+    UINT64 A, B, C, D, E, F, G, H;
+    int round;
+    UINT64 Wt;
+
+
+    while( cbData >= 128 )
+    {
+        A = pChain->H[0];
+        B = pChain->H[1];
+        C = pChain->H[2];
+        D = pChain->H[3];
+        E = pChain->H[4];
+        F = pChain->H[5];
+        G = pChain->H[6];
+        H = pChain->H[7];
+
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND( A, B, C, D, E, F, G, H,  0 );
+        IROUND( H, A, B, C, D, E, F, G,  1 );
+        IROUND( G, H, A, B, C, D, E, F,  2 );
+        IROUND( F, G, H, A, B, C, D, E,  3 );
+        IROUND( E, F, G, H, A, B, C, D,  4 );
+        IROUND( D, E, F, G, H, A, B, C,  5 );
+        IROUND( C, D, E, F, G, H, A, B,  6 );
+        IROUND( B, C, D, E, F, G, H, A,  7 );
+        IROUND( A, B, C, D, E, F, G, H,  8 );
+        IROUND( H, A, B, C, D, E, F, G,  9 );
+        IROUND( G, H, A, B, C, D, E, F, 10 );
+        IROUND( F, G, H, A, B, C, D, E, 11 );
+        IROUND( E, F, G, H, A, B, C, D, 12 );
+        IROUND( D, E, F, G, H, A, B, C, 13 );
+        IROUND( C, D, E, F, G, H, A, B, 14 );
+        IROUND( B, C, D, E, F, G, H, A, 15 );
+
+        for( round=16; round<80; round += 16 )
+        {
+            FROUND( A, B, C, D, E, F, G, H, round +  0,  0 );
+            FROUND( H, A, B, C, D, E, F, G, round +  1,  1 );
+            FROUND( G, H, A, B, C, D, E, F, round +  2,  2 );
+            FROUND( F, G, H, A, B, C, D, E, round +  3,  3 );
+            FROUND( E, F, G, H, A, B, C, D, round +  4,  4 );
+            FROUND( D, E, F, G, H, A, B, C, round +  5,  5 );
+            FROUND( C, D, E, F, G, H, A, B, round +  6,  6 );
+            FROUND( B, C, D, E, F, G, H, A, round +  7,  7 );
+            FROUND( A, B, C, D, E, F, G, H, round +  8,  8 );
+            FROUND( H, A, B, C, D, E, F, G, round +  9,  9 );
+            FROUND( G, H, A, B, C, D, E, F, round + 10, 10 );
+            FROUND( F, G, H, A, B, C, D, E, round + 11, 11 );
+            FROUND( E, F, G, H, A, B, C, D, round + 12, 12 );
+            FROUND( D, E, F, G, H, A, B, C, round + 13, 13 );
+            FROUND( C, D, E, F, G, H, A, B, round + 14, 14 );
+            FROUND( B, C, D, E, F, G, H, A, round + 15, 15 );
+        }
+
+        pChain->H[0] = A + pChain->H[0];
+        pChain->H[1] = B + pChain->H[1];
+        pChain->H[2] = C + pChain->H[2];
+        pChain->H[3] = D + pChain->H[3];
+        pChain->H[4] = E + pChain->H[4];
+        pChain->H[5] = F + pChain->H[5];
+        pChain->H[6] = G + pChain->H[6];
+        pChain->H[7] = H + pChain->H[7];
+
+        pbData += 128;
+        cbData -= 128;
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SYMCRYPT_FORCE_WRITE64( &A, 0 );
+    SYMCRYPT_FORCE_WRITE64( &B, 0 );
+    SYMCRYPT_FORCE_WRITE64( &C, 0 );
+    SYMCRYPT_FORCE_WRITE64( &D, 0 );
+    SYMCRYPT_FORCE_WRITE64( &E, 0 );
+    SYMCRYPT_FORCE_WRITE64( &F, 0 );
+    SYMCRYPT_FORCE_WRITE64( &G, 0 );
+    SYMCRYPT_FORCE_WRITE64( &H, 0 );
+    SYMCRYPT_FORCE_WRITE64( &Wt, 0 );
+}
+
+//
+// UINT64 based implementation that
+// first computes the expanded message, and then the
+// actual hash computation.
+// It tries to use fewer registers; this is probably a good approach for CPUs with only 8
+// 64-bit registers; which is what you would use on x86 XMM, but we have XMM code below.
+// This uses more memory, but might allow better register re-use and thereby
+// reduce the number of load/stores.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ull2(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN UINT64 buf[4 + 8 + 80];    // 4 words original chaining state, chaining state, and expanded input block
+    UINT64 * W = &buf[4 + 8];
+    UINT64 * ha = &buf[4]; // initial state words, in order h, g, ..., b, a
+    UINT64 A, B, C, D, T;
+    int r;
+
+    ha[7] = pChain->H[0]; buf[3] = ha[7];
+    ha[6] = pChain->H[1]; buf[2] = ha[6];
+    ha[5] = pChain->H[2]; buf[1] = ha[5];
+    ha[4] = pChain->H[3]; buf[0] = ha[4];
+    ha[3] = pChain->H[4];
+    ha[2] = pChain->H[5];
+    ha[1] = pChain->H[6];
+    ha[0] = pChain->H[7];
+
+    while( cbData >= 128 )
+    {
+
+        //
+        // Capture the input into W[0..15]
+        //
+        for( r=0; r<16; r+= 2 )
+        {
+            W[r  ] = SYMCRYPT_LOAD_MSBFIRST64( &pbData[ 8* r    ] );
+            W[r+1] = SYMCRYPT_LOAD_MSBFIRST64( &pbData[ 8*(r+1) ] );
+        }
+
+        //
+        // Expand the message
+        //
+        A = W[15];
+        B = W[14];
+        D = W[0];
+        for( r=16; r<80; r+= 2 )
+        {
+            // Loop invariant: A=W[r-1], B = W[r-2], D = W[r-16]
+
+            //
+            // Macro for one word of message expansion.
+            // Invariant:
+            // on entry: a = W[r-1], b = W[r-2], d = W[r-16]
+            // on exit:  W[r] computed, a = W[r-1], b = W[r], c = W[r-15]
+            //
+            #define EXPAND( a, b, c, d, r ) \
+                        c = W[r-15]; \
+                        b = d + LSIGMA1( b ) + W[r-7] + LSIGMA0( c ); \
+                        W[r] = b; \
+
+            EXPAND( A, B, C, D, r );
+            EXPAND( B, A, D, C, (r+1));
+
+            #undef EXPAND
+        }
+
+        A = ha[7];
+        B = ha[6];
+        C = ha[5];
+        D = ha[4];
+
+        for( r=0; r<80; r += 4 )
+        {
+            //
+            // Loop invariant:
+            // A, B, C, and D are the a,b,c,d values of the current state.
+            // W[r] is the next expanded message word to be processed.
+            // W[r-8 .. r-5] contain the current state words h, g, f, e.
+            //
+
+            //
+            // Macro to compute one round
+            //
+            #define DO_ROUND( a, b, c, d, t, r ) \
+                t = W[r] + CSIGMA1( W[r-5] ) + W[r-8] + CH( W[r-5], W[r-6], W[r-7] ) + SymCryptSha512K[r]; \
+                W[r-4] = t + d; \
+                d = t + CSIGMA0( a ) + MAJ( c, b, a );
+
+            DO_ROUND( A, B, C, D, T, r );
+            DO_ROUND( D, A, B, C, T, (r+1) );
+            DO_ROUND( C, D, A, B, T, (r+2) );
+            DO_ROUND( B, C, D, A, T, (r+3) );
+            #undef DO_ROUND
+        }
+
+        buf[3] = ha[7] = buf[3] + A;
+        buf[2] = ha[6] = buf[2] + B;
+        buf[1] = ha[5] = buf[1] + C;
+        buf[0] = ha[4] = buf[0] + D;
+        ha[3] += W[r-5];
+        ha[2] += W[r-6];
+        ha[1] += W[r-7];
+        ha[0] += W[r-8];
+
+        pbData += 128;
+        cbData -= 128;
+    }
+
+    pChain->H[0] = ha[7];
+    pChain->H[1] = ha[6];
+    pChain->H[2] = ha[5];
+    pChain->H[3] = ha[4];
+    pChain->H[4] = ha[3];
+    pChain->H[5] = ha[2];
+    pChain->H[6] = ha[1];
+    pChain->H[7] = ha[0];
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    SYMCRYPT_FORCE_WRITE64( &A, 0 );
+    SYMCRYPT_FORCE_WRITE64( &B, 0 );
+    SYMCRYPT_FORCE_WRITE64( &C, 0 );
+    SYMCRYPT_FORCE_WRITE64( &D, 0 );
+    SYMCRYPT_FORCE_WRITE64( &T, 0 );
+
+}
+
+//
+// UINT64 based implementation that
+// first computes the expanded message, and then the
+// actual hash computation.
+// This one uses more registers than the previous one.
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_ull3(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN UINT64 W[80];
+    SYMCRYPT_ALIGN UINT64 ha[8];
+    UINT64 A, B, C, D, E, F, G, H;
+    int r;
+
+    ha[7] = pChain->H[0];
+    ha[6] = pChain->H[1];
+    ha[5] = pChain->H[2];
+    ha[4] = pChain->H[3];
+    ha[3] = pChain->H[4];
+    ha[2] = pChain->H[5];
+    ha[1] = pChain->H[6];
+    ha[0] = pChain->H[7];
+
+    while( cbData >= 128 )
+    {
+
+        //
+        // Capture the input into W[0..15]
+        //
+        for( r=0; r<16; r+= 2 )
+        {
+            W[r  ] = SYMCRYPT_LOAD_MSBFIRST64( &pbData[ 8* r    ] );
+            W[r+1] = SYMCRYPT_LOAD_MSBFIRST64( &pbData[ 8*(r+1) ] );
+        }
+
+        //
+        // Expand the message
+        //
+        A = W[15];
+        B = W[14];
+        D = W[0];
+        for( r=16; r<80; r+= 2 )
+        {
+            // Loop invariant: A=W[r-1], B = W[r-2], D = W[r-16]
+
+            //
+            // Macro for one word of message expansion.
+            // Invariant:
+            // on entry: a = W[r-1], b = W[r-2], d = W[r-16]
+            // on exit:  W[r] computed, a = W[r-1], b = W[r], c = W[r-15]
+            //
+            #define EXPAND( a, b, c, d, r ) \
+                        c = W[r-15]; \
+                        b = d + LSIGMA1( b ) + W[r-7] + LSIGMA0( c ); \
+                        W[r] = b; \
+
+            EXPAND( A, B, C, D, r );
+            EXPAND( B, A, D, C, (r+1));
+
+            #undef EXPAND
+        }
+
+        A = ha[7];
+        B = ha[6];
+        C = ha[5];
+        D = ha[4];
+        E = ha[3];
+        F = ha[2];
+        G = ha[1];
+        H = ha[0];
+
+        for( r=0; r<80; r += 8 )
+        {
+            //
+            // Loop invariant:
+            // A, B, C, and D, E, F, G, H, are the values of the current state.
+            // W[r] is the next expanded message word to be processed.
+            //
+
+            //
+            // Macro to compute one round
+            //
+            #define DO_ROUND( a, b, c, d, e, f, g, h, r ) \
+                h += W[r] + CSIGMA1( e ) + CH( e, f, g ) + SymCryptSha512K[r]; \
+                d += h; \
+                h += CSIGMA0( a ) + MAJ( c, b, a );
+
+            DO_ROUND( A, B, C, D, E, F, G, H, (r  ) );
+            DO_ROUND( H, A, B, C, D, E, F, G, (r+1) );
+            DO_ROUND( G, H, A, B, C, D, E, F, (r+2) );
+            DO_ROUND( F, G, H, A, B, C, D, E, (r+3) );
+            DO_ROUND( E, F, G, H, A, B, C, D, (r+4) );
+            DO_ROUND( D, E, F, G, H, A, B, C, (r+5) );
+            DO_ROUND( C, D, E, F, G, H, A, B, (r+6) );
+            DO_ROUND( B, C, D, E, F, G, H, A, (r+7) );
+            #undef DO_ROUND
+        }
+
+        ha[7] += A;
+        ha[6] += B;
+        ha[5] += C;
+        ha[4] += D;
+        ha[3] += E;
+        ha[2] += F;
+        ha[1] += G;
+        ha[0] += H;
+
+        pbData += 128;
+        cbData -= 128;
+    }
+
+    pChain->H[0] = ha[7];
+    pChain->H[1] = ha[6];
+    pChain->H[2] = ha[5];
+    pChain->H[3] = ha[4];
+    pChain->H[4] = ha[3];
+    pChain->H[5] = ha[2];
+    pChain->H[6] = ha[1];
+    pChain->H[7] = ha[0];
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SymCryptWipeKnownSize( ha, sizeof( ha ) );
+    SYMCRYPT_FORCE_WRITE64( &A, 0 );
+    SYMCRYPT_FORCE_WRITE64( &B, 0 );
+    SYMCRYPT_FORCE_WRITE64( &C, 0 );
+    SYMCRYPT_FORCE_WRITE64( &D, 0 );
+    SYMCRYPT_FORCE_WRITE64( &E, 0 );
+    SYMCRYPT_FORCE_WRITE64( &F, 0 );
+    SYMCRYPT_FORCE_WRITE64( &G, 0 );
+    SYMCRYPT_FORCE_WRITE64( &H, 0 );
+}
+
+#undef MAJ
+#undef CH
+#undef CSIGMA0
+#undef CSIGMA1
+#undef LSIGMA0
+#undef LSIGMA1
+#undef CROUND
+#undef IROUND
+#undef FROUND
+
+//======================================================================================
+// Implementation using Xmm registers
+//
+#if SYMCRYPT_CPU_X86 // only on X86; AMD64 is faster when using UINT64s
+
+#ifdef __clang__
+#pragma clang attribute push (__attribute__((target("ssse3"))), apply_to=function)
+#else
+#pragma GCC push_options
+#pragma GCC target("ssse3")
+#endif
+
+#if SYMCRYPT_MS_VC
+#ifndef _mm_storeu_si64
+    // Workaround missing intrinsic on some versions of MSVC
+    #define _mm_storeu_si64(p, a) (_mm_storel_epi64((__m128i*)(p), (a)))
+#endif
+#endif
+
+#define XMMADD( _a, _b ) _mm_add_epi64((_a), (_b))
+#define XMMAND( _a, _b ) _mm_and_si128((_a), (_b))
+#define XMMOR(  _a, _b ) _mm_or_si128((_a), (_b))
+#define XMMROR( _a, _n ) _mm_xor_si128( _mm_slli_epi64( (_a), 64-(_n)), _mm_srli_epi64( (_a), (_n)) )
+#define XMMSHR( _a, _n ) _mm_srli_epi64((_a), (_n))
+#define XMMXOR( _a, _b ) _mm_xor_si128((_a), (_b))
+#define XMMSTORE_UINT64( _a, _addr ) _mm_storeu_si64((_addr), (_a))
+
+#define XMMMAJ( x, y, z )  XMMOR( XMMAND( XMMOR( (z), (y)), (x)), XMMAND( (z), (y) ) )
+#define XMMCH(  x, y, z )  XMMXOR( XMMAND( XMMXOR( (z), (y) ), (x)), (z))
+#define XMMCSIGMA0( x )    XMMXOR( XMMXOR( XMMROR((x), 28), XMMROR((x), 34)), XMMROR((x), 39))
+#define XMMCSIGMA1( x )    XMMXOR( XMMXOR( XMMROR((x), 14), XMMROR((x), 18)), XMMROR((x), 41))
+#define XMMLSIGMA0( x )    XMMXOR( XMMXOR( XMMROR((x),  1), XMMROR((x),  8)), XMMSHR((x), 7))
+#define XMMLSIGMA1( x )    XMMXOR( XMMXOR( XMMROR((x), 19), XMMROR((x), 61)), XMMSHR((x), 6))
+
+//
+// Core round takes two arguments: r16 = round number modulo 16, r = round number - r16.
+// On entry, Wt must be equal to the sum of the round constant and the expanded message word for this round.
+// Only the lower word of each Xmm register is used.
+//
+#define XMMCROUND( r16, r ) {;\
+    ah[r16 & 7] = XMMADD( XMMADD( XMMADD( ah[r16 & 7], XMMCSIGMA1(ah[(r16+3)&7]) ), XMMCH(ah[(r16+3)&7], ah[(r16+2)&7], ah[(r16+1)&7]) ), Wt );\
+    ah[(r16+4)&7] = XMMADD( ah[(r16+4)&7], ah[r16 &7] );\
+    ah[r16 & 7] = XMMADD( XMMADD( ah[r16 & 7], XMMCSIGMA0(ah[(r16+7)&7])), XMMMAJ(ah[(r16+7)&7], ah[(r16+6)&7], ah[(r16+5)&7]) );\
+}
+
+#pragma warning( disable: 4127 )   // conditional expression is constant
+
+//
+// Initial round; reads data and performs a round.
+// Data is read in 128-bit chunks every other round.
+//
+#define XMMIROUND( r ) {\
+    if( (r&1) == 0 ) \
+    { \
+        Wt = _mm_loadu_si128( (__m128i *)&pbData[ 8*r ] ); \
+        Wt = _mm_shuffle_epi8( Wt, BYTE_REVERSE_64 ); \
+        W[r/2] = Wt; \
+        Wt = XMMADD( Wt, _mm_load_si128( (__m128i *)&SymCryptSha512K[r] ) ); \
+        Ws = _mm_srli_si128( Wt, 8 ); \
+    } else {\
+        Wt = Ws;\
+    }\
+    XMMCROUND( r, r );\
+}
+
+//
+// Working version of XMMIROUND:
+//    Wt = XMMFROM_MSBF( &pbData[ 8*r ] );\
+//    W[r] = Wt;\
+//    Wt = XMMADD( XMMFROM_UINT64(SymCryptSha512K[r]), Wt );\
+//    XMMCROUND(r,r);\
+
+#define XMMFROUND(r16, rb) { \
+    if( (r16 & 1) == 0 ) \
+    {\
+        Wt = XMMADD( XMMADD( XMMADD(    XMMLSIGMA1( W[((r16 -  2)&15)/2] ), \
+                                        _mm_alignr_epi8( W[((r16 - 6)&15)/2], W[((r16 - 7)&15)/2], 8 ) ), \
+                                        XMMLSIGMA0( _mm_alignr_epi8( W[((r16 - 14)&15)/2], W[((r16 - 15)&15)/2], 8 ) ) ), \
+                                        W[((r16 - 16)&15)/2] ); \
+        W[r16/2] = Wt;\
+        Ws = _mm_load_si128( (__m128i *)&SymCryptSha512K[r16 + rb] );\
+        Wt = XMMADD( Ws , Wt );\
+        Ws = _mm_srli_si128( Wt, 8 );\
+    } else {\
+        Wt = Ws;\
+    }\
+    XMMCROUND( r16, r16+rb ); \
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_xmm(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN __m128i W[8];   // message expansion buffer, 8 elements each storing 2 consecutive UINT64s
+    SYMCRYPT_ALIGN __m128i ah[8];
+    SYMCRYPT_ALIGN __m128i feedf[8];
+    int round;
+    __m128i Wt, Ws;
+    const __m128i BYTE_REVERSE_64 = _mm_set_epi8( 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 );
+
+    Wt = _mm_loadu_si128( (__m128i *) &pChain->H[0] );
+    feedf[7] = ah[7] = Wt;
+    feedf[6] = ah[6] = _mm_srli_si128( Wt, 8 );
+    Wt = _mm_loadu_si128( (__m128i *) &pChain->H[2] );
+    feedf[5] = ah[5] = Wt;
+    feedf[4] = ah[4] = _mm_srli_si128( Wt, 8 );
+    Wt = _mm_loadu_si128( (__m128i *) &pChain->H[4] );
+    feedf[3] = ah[3] = Wt;
+    feedf[2] = ah[2] = _mm_srli_si128( Wt, 8 );
+    Wt = _mm_loadu_si128( (__m128i *) &pChain->H[6] );
+    feedf[1] = ah[1] = Wt;
+    feedf[0] = ah[0] = _mm_srli_si128( Wt, 8 );
+
+    while( cbData >= 128 )
+    {
+        //
+        // initial rounds 1 to 16
+        //
+
+        XMMIROUND(  0 );
+        XMMIROUND(  1 );
+        XMMIROUND(  2 );
+        XMMIROUND(  3 );
+        XMMIROUND(  4 );
+        XMMIROUND(  5 );
+        XMMIROUND(  6 );
+        XMMIROUND(  7 );
+        XMMIROUND(  8 );
+        XMMIROUND(  9 );
+        XMMIROUND( 10 );
+        XMMIROUND( 11 );
+        XMMIROUND( 12 );
+        XMMIROUND( 13 );
+        XMMIROUND( 14 );
+        XMMIROUND( 15 );
+
+        for( round=16; round<80; round += 16 )
+        {
+            XMMFROUND(  0, round );
+            XMMFROUND(  1, round );
+            XMMFROUND(  2, round );
+            XMMFROUND(  3, round );
+            XMMFROUND(  4, round );
+            XMMFROUND(  5, round );
+            XMMFROUND(  6, round );
+            XMMFROUND(  7, round );
+            XMMFROUND(  8, round );
+            XMMFROUND(  9, round );
+            XMMFROUND( 10, round );
+            XMMFROUND( 11, round );
+            XMMFROUND( 12, round );
+            XMMFROUND( 13, round );
+            XMMFROUND( 14, round );
+            XMMFROUND( 15, round );
+        }
+
+        feedf[0] = ah[0] = XMMADD( ah[0], feedf[0] );
+        feedf[1] = ah[1] = XMMADD( ah[1], feedf[1] );
+        feedf[2] = ah[2] = XMMADD( ah[2], feedf[2] );
+        feedf[3] = ah[3] = XMMADD( ah[3], feedf[3] );
+        feedf[4] = ah[4] = XMMADD( ah[4], feedf[4] );
+        feedf[5] = ah[5] = XMMADD( ah[5], feedf[5] );
+        feedf[6] = ah[6] = XMMADD( ah[6], feedf[6] );
+        feedf[7] = ah[7] = XMMADD( ah[7], feedf[7] );
+
+        pbData += 128;
+        cbData -= 128;
+
+    }
+
+    XMMSTORE_UINT64( ah[7], &(pChain->H[0]) );
+    XMMSTORE_UINT64( ah[6], &(pChain->H[1]) );
+    XMMSTORE_UINT64( ah[5], &(pChain->H[2]) );
+    XMMSTORE_UINT64( ah[4], &(pChain->H[3]) );
+    XMMSTORE_UINT64( ah[3], &(pChain->H[4]) );
+    XMMSTORE_UINT64( ah[2], &(pChain->H[5]) );
+    XMMSTORE_UINT64( ah[1], &(pChain->H[6]) );
+    XMMSTORE_UINT64( ah[0], &(pChain->H[7]) );
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( ah, sizeof( ah ) );
+    SymCryptWipeKnownSize( feedf, sizeof( feedf ) );
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SymCryptWipeKnownSize( &Wt, sizeof( Wt ));
+    SymCryptWipeKnownSize( &Ws, sizeof( Ws ));
+}
+
+#ifdef __clang__
+#pragma clang attribute pop
+#else
+#pragma GCC pop_options
+#endif
+
+#endif
+
+
+
+//======================================================================================
+// Implementation using NEON registers
+//
+#if SYMCRYPT_CPU_ARM
+
+
+#define ROR( _a, _n ) vorr_u64( vshl_n_u64( _a, 64 - _n ), vshr_n_u64( _a, _n ) )
+#define ADD( x, y ) vadd_u64( (x), (y) )
+
+#define MAJ( x, y, z )  vorr_u64( vand_u64( vorr_u64( (z), (y)), (x)), vand_u64( (z), (y) ) )
+#define CH(  x, y, z )  veor_u64( vand_u64( veor_u64( (z), (y) ), (x)), (z))
+#define CSIGMA0( x )    veor_u64( veor_u64( ROR((x), 28), ROR((x), 34)), ROR((x), 39))
+#define CSIGMA1( x )    veor_u64( veor_u64( ROR((x), 14), ROR((x), 18)), ROR((x), 41))
+#define LSIGMA0( x )    veor_u64( veor_u64( ROR((x),  1), ROR((x),  8)), vshr_n_u64((x), 7))
+#define LSIGMA1( x )    veor_u64( veor_u64( ROR((x), 19), ROR((x), 61)), vshr_n_u64((x), 6))
+
+//
+// r = round number, r16 = r mod 16 (often a compile-time constant when r is not)
+//
+#define CROUND( a, b, c, d, e, f, g, h, r, r16 ) {\
+    W[r16] = Wt; \
+    h = ADD( h, ADD( ADD( ADD( CSIGMA1(e), CH(e, f, g)), *(__n64 *)&SymCryptSha512K[r]),  Wt ));\
+    d = ADD( d, h );\
+    h = ADD( h, ADD( CSIGMA0(a), MAJ(a, b, c)));\
+}
+
+//
+// Initial round that reads the message.
+// r is the round number 0..15
+//
+#define IROUND( a, b, c, d, e, f, g, h, r ) {\
+    Wt = vmov_n_u64( SYMCRYPT_LOAD_MSBFIRST64( &pbData[ 8*r ] ) );\
+    CROUND( a, b, c, d, e, f, g, h, r, r);\
+    }
+//
+// Subsequent rounds.
+// r is the round number, r16 is the round number mod 16.
+// These are separate as typically r is run-time and r16 is compile time constant.
+//
+#define FROUND( a, b, c, d, e, f, g, h, r, r16 ) {                                      \
+    Wt = ADD( ADD( LSIGMA1( W[(r16-2) & 15] ), LSIGMA0( W[(r16-15) & 15])) , ADD( W[(r16-7) & 15], W[r16 & 15])); \
+    CROUND( a, b, c, d, e, f, g, h, r, r16 ); \
+    }
+
+//
+// This is the core routine that does the actual hard work
+// This is based on the older one in RSA32LIB by Scott Field from 2001
+//
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks_neon(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE  *   pChain,
+    _In_reads_(cbData)      PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+    SYMCRYPT_ALIGN __n64 W[16];
+    __n64 A, B, C, D, E, F, G, H;
+    int round;
+    __n64 Wt;
+    __n64 * pH = (__n64 *) &pChain->H[0];
+
+    A = pH[0];
+    B = pH[1];
+    C = pH[2];
+    D = pH[3];
+    E = pH[4];
+    F = pH[5];
+    G = pH[6];
+    H = pH[7];
+
+    while( cbData >= 128 )
+    {
+        //
+        // initial rounds 1 to 16
+        //
+
+        IROUND( A, B, C, D, E, F, G, H,  0 );
+        IROUND( H, A, B, C, D, E, F, G,  1 );
+        IROUND( G, H, A, B, C, D, E, F,  2 );
+        IROUND( F, G, H, A, B, C, D, E,  3 );
+        IROUND( E, F, G, H, A, B, C, D,  4 );
+        IROUND( D, E, F, G, H, A, B, C,  5 );
+        IROUND( C, D, E, F, G, H, A, B,  6 );
+        IROUND( B, C, D, E, F, G, H, A,  7 );
+        IROUND( A, B, C, D, E, F, G, H,  8 );
+        IROUND( H, A, B, C, D, E, F, G,  9 );
+        IROUND( G, H, A, B, C, D, E, F, 10 );
+        IROUND( F, G, H, A, B, C, D, E, 11 );
+        IROUND( E, F, G, H, A, B, C, D, 12 );
+        IROUND( D, E, F, G, H, A, B, C, 13 );
+        IROUND( C, D, E, F, G, H, A, B, 14 );
+        IROUND( B, C, D, E, F, G, H, A, 15 );
+
+        for( round=16; round<80; round += 16 )
+        {
+            FROUND( A, B, C, D, E, F, G, H, round +  0,  0 );
+            FROUND( H, A, B, C, D, E, F, G, round +  1,  1 );
+            FROUND( G, H, A, B, C, D, E, F, round +  2,  2 );
+            FROUND( F, G, H, A, B, C, D, E, round +  3,  3 );
+            FROUND( E, F, G, H, A, B, C, D, round +  4,  4 );
+            FROUND( D, E, F, G, H, A, B, C, round +  5,  5 );
+            FROUND( C, D, E, F, G, H, A, B, round +  6,  6 );
+            FROUND( B, C, D, E, F, G, H, A, round +  7,  7 );
+            FROUND( A, B, C, D, E, F, G, H, round +  8,  8 );
+            FROUND( H, A, B, C, D, E, F, G, round +  9,  9 );
+            FROUND( G, H, A, B, C, D, E, F, round + 10, 10 );
+            FROUND( F, G, H, A, B, C, D, E, round + 11, 11 );
+            FROUND( E, F, G, H, A, B, C, D, round + 12, 12 );
+            FROUND( D, E, F, G, H, A, B, C, round + 13, 13 );
+            FROUND( C, D, E, F, G, H, A, B, round + 14, 14 );
+            FROUND( B, C, D, E, F, G, H, A, round + 15, 15 );
+        }
+
+        pH[0] = A = ADD( A, pH[0] );
+        pH[1] = B = ADD( B, pH[1] );
+        pH[2] = C = ADD( C, pH[2] );
+        pH[3] = D = ADD( D, pH[3] );
+        pH[4] = E = ADD( E, pH[4] );
+        pH[5] = F = ADD( F, pH[5] );
+        pH[6] = G = ADD( G, pH[6] );
+        pH[7] = H = ADD( H, pH[7] );
+
+        pbData += 128;
+        cbData -= 128;
+    }
+
+    *pcbRemaining = cbData;
+
+    //
+    // Wipe the variables;
+    //
+    SymCryptWipeKnownSize( W, sizeof( W ) );
+    SymCryptWipeKnownSize( &A, sizeof( A ) );
+    SymCryptWipeKnownSize( &B, sizeof( B ) );
+    SymCryptWipeKnownSize( &C, sizeof( C ) );
+    SymCryptWipeKnownSize( &D, sizeof( D ) );
+    SymCryptWipeKnownSize( &E, sizeof( E ) );
+    SymCryptWipeKnownSize( &F, sizeof( F ) );
+    SymCryptWipeKnownSize( &G, sizeof( G ) );
+    SymCryptWipeKnownSize( &H, sizeof( H ) );
+    SymCryptWipeKnownSize( &Wt, sizeof( Wt ) );
+}
+
+#endif
+
+//======================================================================================
+//
+// Switch between different implementations of compression function
+//
+//FORCEINLINE
+VOID
+SYMCRYPT_CALL
+SymCryptSha512AppendBlocks(
+    _Inout_                 SYMCRYPT_SHA512_CHAINING_STATE *    pChain,
+    _In_reads_( cbData )    PCBYTE                              pbData,
+                            SIZE_T                              cbData,
+    _Out_                   SIZE_T                            * pcbRemaining )
+{
+#if SYMCRYPT_CPU_AMD64
+
+    // Temporarily disabling use of Ymm in SHA2
+    // SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+
+    // if (SYMCRYPT_CPU_FEATURES_PRESENT(SYMCRYPT_CPU_FEATURE_AVX512 | SYMCRYPT_CPU_FEATURE_BMI2) &&
+    //     SymCryptSaveYmm(&SaveData) == SYMCRYPT_NO_ERROR)
+    // {
+    //     SymCryptSha512AppendBlocks_ymm_avx512vl_asm(pChain, pbData, cbData, pcbRemaining);
+
+    //     SymCryptRestoreYmm(&SaveData);
+    // }
+    // else if (SYMCRYPT_CPU_FEATURES_PRESENT(SYMCRYPT_CPU_FEATURE_AVX2 | SYMCRYPT_CPU_FEATURE_BMI2) &&
+    //     SymCryptSaveYmm(&SaveData) == SYMCRYPT_NO_ERROR)
+    // {
+    //     //SymCryptSha512AppendBlocks_ymm_1block(pChain, pbData, cbData, pcbRemaining);
+    //     //SymCryptSha512AppendBlocks_ymm_2blocks(pChain, pbData, cbData, pcbRemaining);
+    //     //SymCryptSha512AppendBlocks_ymm_4blocks(pChain, pbData, cbData, pcbRemaining);
+    //     SymCryptSha512AppendBlocks_ymm_avx2_asm(pChain, pbData, cbData, pcbRemaining);
+
+    //     SymCryptRestoreYmm(&SaveData);
+    // }
+    // else
+    {
+        SymCryptSha512AppendBlocks_ull( pChain, pbData, cbData, pcbRemaining );
+        //SymCryptSha512AppendBlocks_ull2( pChain, pbData, cbData, pcbRemaining );
+        //SymCryptSha512AppendBlocks_ull3( pChain, pbData, cbData, pcbRemaining );
+    }
+
+
+#elif SYMCRYPT_CPU_ARM
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON ) )
+    {
+        SymCryptSha512AppendBlocks_neon( pChain, pbData, cbData, pcbRemaining );      // Tegra T3: 48 c/B
+    } else {
+        SymCryptSha512AppendBlocks_ull( pChain, pbData, cbData, pcbRemaining );       // Tegra T3: 65.34 c/B
+        //SymCryptSha512AppendBlocks_ull2( pChain, pbData, cbData, pcbRemaining );      // Tegra T3: 77.4 c/B
+        //SymCryptSha512AppendBlocks_ull3( pChain, pbData, cbData, pcbRemaining );      // Tegra T3: 71.6 c/B
+    }
+
+#elif SYMCRYPT_CPU_X86
+
+    SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_SSSE3 ) && SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptSha512AppendBlocks_xmm( pChain, pbData, cbData, pcbRemaining );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptSha512AppendBlocks_ull( pChain, pbData, cbData, pcbRemaining );       // core2: 36.40 c/B
+        //SymCryptSha512AppendBlocks_ull2( pChain, pbData, cbData, pcbRemaining );      // core2: 49.09 c/B
+        //SymCryptSha512AppendBlocks_ull3( pChain, pbData, cbData, pcbRemaining );      // core2: 38.29 c/B
+    }
+
+#else
+
+    SymCryptSha512AppendBlocks_ull( pChain, pbData, cbData, pcbRemaining );       // need tuning...
+
+#endif
+}
diff --git a/libs/symcrypt/lib/shake.c b/libs/symcrypt/lib/shake.c
new file mode 100644
index 000000000000..1850bc7be049
--- /dev/null
+++ b/libs/symcrypt/lib/shake.c
@@ -0,0 +1,337 @@
+//
+// Shake.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+
+//
+//  SHAKE128
+//
+#define Alg Shake128
+#define ALG SHAKE128
+#define SYMCRYPT_SHAKEXXX_INPUT_BLOCK_SIZE      SYMCRYPT_SHAKE128_INPUT_BLOCK_SIZE
+#define SYMCRYPT_SHAKEXXX_RESULT_SIZE           SYMCRYPT_SHAKE128_RESULT_SIZE
+#include "shake_pattern.c"
+#undef SYMCRYPT_SHAKEXXX_RESULT_SIZE
+#undef SYMCRYPT_SHAKEXXX_INPUT_BLOCK_SIZE
+#undef ALG
+#undef Alg
+
+const SYMCRYPT_HASH SymCryptShake128HashAlgorithm_default = {
+    &SymCryptShake128Init,
+    &SymCryptShake128Append,
+    &SymCryptShake128Result,
+    NULL,                           // AppendBlocks function is not implemented for SHA-3
+    &SymCryptShake128StateCopy,
+    sizeof(SYMCRYPT_SHAKE128_STATE),
+    SYMCRYPT_SHAKE128_RESULT_SIZE,
+    SYMCRYPT_SHAKE128_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SHAKE128_STATE, ks.state),
+    SYMCRYPT_FIELD_SIZE(SYMCRYPT_SHAKE128_STATE, ks.state),
+};
+
+const PCSYMCRYPT_HASH SymCryptShake128HashAlgorithm = &SymCryptShake128HashAlgorithm_default;
+
+static const BYTE shake128KATAnswer[SYMCRYPT_SHAKE128_RESULT_SIZE] = {
+    0x58, 0x81, 0x09, 0x2d, 0xd8, 0x18, 0xbf, 0x5c,
+    0xf8, 0xa3, 0xdd, 0xb7, 0x93, 0xfb, 0xcb, 0xa7,
+    0x40, 0x97, 0xd5, 0xc5, 0x26, 0xa6, 0xd3, 0x5f,
+    0x97, 0xb8, 0x33, 0x51, 0x94, 0x0f, 0x2c ,0xc8
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake128Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHAKE128_RESULT_SIZE];
+
+    SymCryptShake128(SymCryptTestMsg3, sizeof(SymCryptTestMsg3), result, sizeof(result));
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, shake128KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('shk1');
+    }
+}
+
+
+//
+//  SHAKE256
+//
+#define Alg Shake256
+#define ALG SHAKE256
+#define SYMCRYPT_SHAKEXXX_INPUT_BLOCK_SIZE      SYMCRYPT_SHAKE256_INPUT_BLOCK_SIZE
+#define SYMCRYPT_SHAKEXXX_RESULT_SIZE           SYMCRYPT_SHAKE256_RESULT_SIZE
+#include "shake_pattern.c"
+#undef SYMCRYPT_SHAKEXXX_RESULT_SIZE
+#undef SYMCRYPT_SHAKEXXX_INPUT_BLOCK_SIZE
+#undef ALG
+#undef Alg
+
+const SYMCRYPT_HASH SymCryptShake256HashAlgorithm_default = {
+    &SymCryptShake256Init,
+    &SymCryptShake256Append,
+    &SymCryptShake256Result,
+    NULL,                           // AppendBlocks function is not implemented for SHA-3
+    &SymCryptShake256StateCopy,
+    sizeof(SYMCRYPT_SHAKE256_STATE),
+    SYMCRYPT_SHAKE256_RESULT_SIZE,
+    SYMCRYPT_SHAKE256_INPUT_BLOCK_SIZE,
+    SYMCRYPT_FIELD_OFFSET(SYMCRYPT_SHAKE256_STATE, ks.state),
+    SYMCRYPT_FIELD_SIZE(SYMCRYPT_SHAKE256_STATE, ks.state),
+};
+
+const PCSYMCRYPT_HASH SymCryptShake256HashAlgorithm = &SymCryptShake256HashAlgorithm_default;
+
+static const BYTE shake256KATAnswer[SYMCRYPT_SHAKE256_RESULT_SIZE] = {
+    0x48, 0x33, 0x66, 0x60, 0x13, 0x60, 0xa8, 0x77, 0x1c, 0x68, 0x63, 0x08, 0x0c, 0xc4, 0x11, 0x4d,
+    0x8d, 0xb4, 0x45, 0x30, 0xf8, 0xf1, 0xe1, 0xee, 0x4f, 0x94, 0xea, 0x37, 0xe7, 0x8b, 0x57, 0x39,
+    0xd5, 0xa1, 0x5b, 0xef, 0x18, 0x6a, 0x53, 0x86, 0xc7, 0x57, 0x44, 0xc0, 0x52, 0x7e, 0x1f, 0xaa,
+    0x9f, 0x87, 0x26, 0xe4, 0x62, 0xa1, 0x2a, 0x4f, 0xeb, 0x06, 0xbd, 0x88, 0x01, 0xe7, 0x51, 0xe4
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptShake256Selftest(void)
+{
+    BYTE result[SYMCRYPT_SHAKE256_RESULT_SIZE];
+
+    SymCryptShake256(SymCryptTestMsg3, sizeof(SymCryptTestMsg3), result, sizeof(result));
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, shake256KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('shk2');
+    }
+}
+
+
+//
+//  CSHAKE128
+//
+#define Alg CShake128
+#define ALG CSHAKE128
+#define SYMCRYPT_SHAKEXXX_INIT                  SymCryptShake128Init
+#define SYMCRYPT_SHAKEXXX_STATE                 SYMCRYPT_SHAKE128_STATE
+#define SYMCRYPT_CSHAKEXXX_INPUT_BLOCK_SIZE     SYMCRYPT_CSHAKE128_INPUT_BLOCK_SIZE
+#define SYMCRYPT_CSHAKEXXX_RESULT_SIZE          SYMCRYPT_CSHAKE128_RESULT_SIZE
+#include "cshake_pattern.c"
+#undef SYMCRYPT_CSHAKEXXX_RESULT_SIZE
+#undef SYMCRYPT_CSHAKEXXX_INPUT_BLOCK_SIZE
+#undef SYMCRYPT_SHAKEXXX_STATE
+#undef SYMCRYPT_SHAKEXXX_INIT
+#undef ALG
+#undef Alg
+
+
+static const BYTE cshake128KATAnswer[SYMCRYPT_CSHAKE128_RESULT_SIZE] = {
+    0x14, 0xe5, 0xdf, 0xf3, 0xae, 0xfd, 0xfe, 0x8e,
+    0xa6, 0xae, 0xed, 0xfd, 0x99, 0xe6, 0x84, 0x74,
+    0xbc, 0x61, 0xb9, 0xd6, 0x17, 0x4e, 0x9f, 0x4a,
+    0xe3, 0xbd, 0x87, 0xdf, 0x0e, 0xf2, 0x16, 0xdb,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake128Selftest(void)
+{
+    BYTE result[SYMCRYPT_CSHAKE128_RESULT_SIZE];
+    static const unsigned char Nstr[] = { 'N' };
+    static const unsigned char Sstr[] = { 'S' };
+
+    SymCryptCShake128(  Nstr, sizeof(Nstr),
+                        Sstr, sizeof(Sstr),
+                        SymCryptTestMsg3, sizeof(SymCryptTestMsg3),
+                        result, sizeof(result));
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, cshake128KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('cshk');
+    }
+}
+
+
+//
+//  CSHAKE256
+//
+#define Alg CShake256
+#define ALG CSHAKE256
+#define SYMCRYPT_SHAKEXXX_INIT                  SymCryptShake256Init
+#define SYMCRYPT_SHAKEXXX_STATE                 SYMCRYPT_SHAKE256_STATE
+#define SYMCRYPT_CSHAKEXXX_INPUT_BLOCK_SIZE     SYMCRYPT_CSHAKE256_INPUT_BLOCK_SIZE
+#define SYMCRYPT_CSHAKEXXX_RESULT_SIZE          SYMCRYPT_CSHAKE256_RESULT_SIZE
+#include "cshake_pattern.c"
+#undef SYMCRYPT_CSHAKEXXX_RESULT_SIZE
+#undef SYMCRYPT_CSHAKEXXX_INPUT_BLOCK_SIZE
+#undef SYMCRYPT_SHAKEXXX_STATE
+#undef SYMCRYPT_SHAKEXXX_INIT
+#undef ALG
+#undef Alg
+
+
+static const BYTE cshake256KATAnswer[SYMCRYPT_CSHAKE256_RESULT_SIZE] = {
+    0x4d, 0xe8, 0x71, 0x6c, 0x4a, 0x16, 0x7e, 0x28, 0x2c, 0x18, 0xc5, 0x1e, 0xed, 0xa6, 0x00, 0xb8,
+    0x91, 0x92, 0x4f, 0xea, 0x2e, 0x20, 0x7f, 0x71, 0x2c, 0xfd, 0xe2, 0x95, 0xfd, 0x1c, 0x67, 0x32,
+    0x31, 0x49, 0x98, 0x23, 0xc0, 0x5e, 0x6a, 0xe3, 0x89, 0xad, 0x4d, 0xa2, 0x32, 0x9c, 0xc9, 0x2e,
+    0x0f, 0xd6, 0x90, 0xb9, 0xee, 0x91, 0x0e, 0x86, 0xf7, 0x1d, 0x03, 0x88, 0xb5, 0x95, 0x61, 0x95
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptCShake256Selftest(void)
+{
+    BYTE result[SYMCRYPT_CSHAKE256_RESULT_SIZE];
+    static const unsigned char Nstr[] = { 'N' };
+    static const unsigned char Sstr[] = { 'S' };
+
+    SymCryptCShake256(Nstr, sizeof(Nstr),
+        Sstr, sizeof(Sstr),
+        SymCryptTestMsg3, sizeof(SymCryptTestMsg3),
+        result, sizeof(result));
+
+    SymCryptInjectError(result, sizeof(result));
+
+    if (memcmp(result, cshake256KATAnswer, sizeof(result)) != 0)
+    {
+        SymCryptFatal('cshk');
+    }
+}
+
+//
+// CShake helper functions
+//
+
+//
+// SymCryptCShakeEncodeInputStrings
+//
+VOID
+SYMCRYPT_CALL
+SymCryptCShakeEncodeInputStrings(
+    _Inout_                             PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_( cbFunctionNameString )  PCBYTE                  pbFunctionNameString,
+                                        SIZE_T                  cbFunctionNameString,
+    _In_reads_( cbCustomizationString ) PCBYTE                  pbCustomizationString,
+                                        SIZE_T                  cbCustomizationString)
+{
+    SYMCRYPT_ASSERT((cbFunctionNameString > 0) || (cbCustomizationString > 0));
+
+    // left_encode( inputBlockSize ) for byte_pad function
+    //
+    // SymCryptKeccakEncodeTimes8 function encodes 8 times the value passed to
+    // it. Here, we want the actual value of pState->inputBlockSize to be encoded,
+    // hence the division by 8.
+    SymCryptKeccakAppendEncodeTimes8(pState, pState->inputBlockSize / 8, TRUE);
+
+    SymCryptKeccakAppendEncodedString(pState, pbFunctionNameString, cbFunctionNameString);
+    SymCryptKeccakAppendEncodedString(pState, pbCustomizationString, cbCustomizationString);
+
+    // Appending of Customization String may have already called the permutation
+    // if the appended data is aligned to input block size, in which case the zero
+    // padding has been done.
+    if (pState->stateIndex != 0)
+    {
+        SymCryptKeccakZeroAppendBlock(pState);
+    }
+}
+
+//
+// SymCryptKeccakEncodeTimes8
+//
+SIZE_T
+SYMCRYPT_CALL
+SymCryptKeccakEncodeTimes8(
+                            UINT64  uInput,
+    _Out_writes_(cbOutput)  PBYTE   pbOutput,
+                            SIZE_T  cbOutput,
+                            BOOLEAN bLeftEncode)
+{
+    BYTE encoding[1 + sizeof(UINT64)];
+    SIZE_T ret = 0;
+
+    // longest encoding is 1 byte for length + 9 bytes for uInput * 8
+    SYMCRYPT_ASSERT(cbOutput >= (1 + sizeof(encoding)));
+    UNREFERENCED_PARAMETER(cbOutput);
+
+    //
+    // encoding[0] .. encoding[8] will contain (uInput * 8) in big endian form
+    encoding[0] = (BYTE)(uInput >> 61);
+    SYMCRYPT_STORE_MSBFIRST64(&encoding[1], uInput * 8);
+
+    SIZE_T length = 1;                              // number of bytes required to encode uInput
+    PCBYTE pbMsb = &encoding[sizeof(encoding) - 1]; // pointer to the most significant byte
+
+    // Locate the most significant non-zero byte
+    for (int i = 0; i < sizeof(encoding); i++)
+    {
+        // Do not early terminate on the most significant byte
+        if (encoding[i] != 0 && length == 1)
+        {
+            length = sizeof(encoding) - i;
+            pbMsb = &encoding[i];
+        }
+    }
+
+    ret = 1 + length;
+
+    if (bLeftEncode)
+    {
+        // length for left_encode
+        *pbOutput++ = (BYTE)length;
+    }
+
+    memcpy(pbOutput, pbMsb, length);
+
+    if(!bLeftEncode)
+    {
+        // length for right_encode
+        pbOutput[length] = (BYTE)length;
+    }
+
+    return ret; // total number of bytes written to pbOutput
+}
+
+//
+// SymCryptKeccakAppendEncodeTimes8
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendEncodeTimes8(
+    _Inout_ SYMCRYPT_KECCAK_STATE *pState,
+            UINT64  uValue,
+            BOOLEAN bLeftEncode)
+
+{
+    BYTE encoding[1 + (1 + sizeof(UINT64))];
+    SIZE_T ret;
+
+    ret = SymCryptKeccakEncodeTimes8(uValue, encoding, sizeof(encoding), bLeftEncode);
+
+    SymCryptKeccakAppend(pState, encoding, ret);
+}
+
+
+//
+// SymCryptKeccakAppendEncodedString
+//
+VOID
+SYMCRYPT_CALL
+SymCryptKeccakAppendEncodedString(
+    _Inout_                 PSYMCRYPT_KECCAK_STATE  pState,
+    _In_reads_(cbString)    PCBYTE                  pbString,
+                            SIZE_T                  cbString)
+{
+    SymCryptKeccakAppendEncodeTimes8(pState, cbString, TRUE);
+    SymCryptKeccakAppend(pState, pbString, cbString);
+}
diff --git a/libs/symcrypt/lib/shake_pattern.c b/libs/symcrypt/lib/shake_pattern.c
new file mode 100644
index 000000000000..59551a391f8b
--- /dev/null
+++ b/libs/symcrypt/lib/shake_pattern.c
@@ -0,0 +1,111 @@
+//
+// shake_pattern.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+//
+// This source file implements SHAKE128 and SHAKE256
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+//
+// SymCryptShake
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxDefault(
+    _In_reads_( cbData )                            PCBYTE  pbData,
+                                                    SIZE_T  cbData,
+    _Out_writes_( SYMCRYPT_SHAKEXXX_RESULT_SIZE )   PBYTE   pbResult)
+{
+    SYMCRYPT_Xxx(pbData, cbData, pbResult, SYMCRYPT_SHAKEXXX_RESULT_SIZE);
+}
+
+//
+// SymCryptShakeEx
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_Xxx(
+    _In_reads_( cbData )        PCBYTE  pbData,
+                                SIZE_T  cbData,
+    _Out_writes_( cbResult )    PBYTE   pbResult,
+                                SIZE_T  cbResult)
+{
+    SYMCRYPT_XXX_STATE state;
+
+    SYMCRYPT_XxxInit(&state);
+    SYMCRYPT_XxxAppend(&state, pbData, cbData);
+    SYMCRYPT_XxxExtract(&state, pbResult, cbResult, TRUE);
+}
+
+//
+// SymCryptShakeStateCopy
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxStateCopy(_In_ const SYMCRYPT_XXX_STATE* pSrc, _Out_ SYMCRYPT_XXX_STATE* pDst)
+{
+    SYMCRYPT_CHECK_MAGIC(pSrc);
+    *pDst = *pSrc;
+    SYMCRYPT_SET_MAGIC(pDst);
+}
+
+//
+// SymCryptShakeInit
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxInit(_Out_ PSYMCRYPT_XXX_STATE pState)
+{
+    SymCryptKeccakInit(&pState->ks,
+                        SYMCRYPT_SHAKEXXX_INPUT_BLOCK_SIZE,
+                        SYMCRYPT_SHAKE_PADDING_VALUE);
+
+    SYMCRYPT_SET_MAGIC(pState);
+}
+
+//
+// SymCryptShakeAppend
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxAppend(
+    _Inout_             PSYMCRYPT_XXX_STATE pState,
+    _In_reads_(cbData)  PCBYTE              pbData,
+                        SIZE_T              cbData)
+{
+    SymCryptKeccakAppend(&pState->ks, pbData, cbData);
+}
+
+//
+// SymCryptShakeExtract
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxExtract(
+    _Inout_                 PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_(cbResult)  PBYTE               pbResult,
+                            SIZE_T              cbResult,
+                            BOOLEAN             bWipe)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, cbResult, bWipe);
+}
+
+//
+// SymCryptShakeResult
+//
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XxxResult(
+    _Inout_                                     PSYMCRYPT_XXX_STATE pState,
+    _Out_writes_(SYMCRYPT_SHAKEXXX_RESULT_SIZE) PBYTE               pbResult)
+{
+    SymCryptKeccakExtract(&pState->ks, pbResult, SYMCRYPT_SHAKEXXX_RESULT_SIZE, TRUE);
+}
diff --git a/libs/symcrypt/lib/smallPrimes32.h b/libs/symcrypt/lib/smallPrimes32.h
new file mode 100644
index 000000000000..8afad4c93804
--- /dev/null
+++ b/libs/symcrypt/lib/smallPrimes32.h
@@ -0,0 +1,29 @@
+//
+// Parameters for trial division mechanism
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+// GENERATED FILE, DO NOT EDIT.
+//
+
+
+//
+// The primes are put into groups of consecutive primes (skipping 2, 3, 5, and 17).
+// Each group has a product less than SYMCRYPT_MAX_SMALL_PRIME_GROUP_PRODUCT which is
+// chosen to avoid overflows in the modular reduction computation.
+//
+
+typedef struct _SYMCRYPT_SMALL_PRIME_GROUPS_SPEC {
+    UINT16 nGroups;    // # groups of this size
+    UINT8  nPrimes;    // # primes in the group
+    UINT32 maxPrime;   // largest prime in the last group
+} SYMCRYPT_SMALL_PRIME_GROUPS_SPEC;
+
+#define SYMCRYPT_MAX_SMALL_PRIME_GROUP_PRODUCT    (0x1c71c71cU)
+
+const SYMCRYPT_SMALL_PRIME_GROUPS_SPEC g_SymCryptSmallPrimeGroupsSpec[] = {
+    {     1,  7, 31 },
+    {     1,  5, 53 },
+    {     5,  4, 151 },
+    {    34,  3, 787 },
+    {  1156,  2, 21841 },
+    {     0,  1, 0xffffffff },
+};
diff --git a/libs/symcrypt/lib/sp800_108.c b/libs/symcrypt/lib/sp800_108.c
new file mode 100644
index 000000000000..56690caa1a4b
--- /dev/null
+++ b/libs/symcrypt/lib/sp800_108.c
@@ -0,0 +1,143 @@
+//
+// sp800_108.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement the SP800-108 CTR KDF function
+//
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSp800_108Derive(
+    _In_                        PCSYMCRYPT_SP800_108_EXPANDED_KEY   pExpandedKey,
+    _In_reads_opt_(cbLabel)     PCBYTE                              pbLabel,
+                                SIZE_T                              cbLabel,
+    _In_reads_opt_(cbContext)   PCBYTE                              pbContext,
+                                SIZE_T                              cbContext,
+    _Out_writes_(cbResult)      PBYTE                               pbResult,
+                                SIZE_T                              cbResult)
+{
+    SYMCRYPT_MAC_STATE  macState;
+    UINT32 iBlock;
+    SIZE_T bytes;
+    SIZE_T blockSize = pExpandedKey->macAlg->resultSize;
+    SIZE_T bytesRemaining = cbResult;
+    BYTE  buf[4];
+    SYMCRYPT_ALIGN BYTE  rbBlockResult[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+
+    SYMCRYPT_ASSERT(
+        blockSize <= SYMCRYPT_MAC_MAX_RESULT_SIZE &&
+        bytesRemaining > 0 );
+
+    if( cbResult > UINT32_MAX/8 )
+    {
+        // SP800-108 requires the output size in bits to be encoded in a 32-bit value.
+        // cbResults that are too large are impossible.
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    iBlock = 0;
+    while( bytesRemaining > 0 )
+    {
+        iBlock += 1;
+        pExpandedKey->macAlg->initFunc  ( &macState, &pExpandedKey->macKey);
+
+        //
+        // We append the pieces into the MAC function. This is inefficient but works always.
+        // If we need more speed for large outputs, we could use a fixed-size stack buffer to build the
+        // concatenation & do a single append. This reduces the # calls in the loop, but adds one memcpy to
+        // the parameters. For small output sizes this is probably a wash.
+        //
+
+        SYMCRYPT_STORE_MSBFIRST32( &buf[0], iBlock );
+        pExpandedKey->macAlg->appendFunc( &macState, &buf[0], 4 );          // block count encoded in 4 bytes
+
+        if( cbLabel != (SIZE_T) -1 )
+        {
+            //
+            // cbLabel == -1 signals a generic input in the Context field.
+            //
+            pExpandedKey->macAlg->appendFunc( &macState, pbLabel, cbLabel );    // label
+
+            buf[0] = 0;
+            pExpandedKey->macAlg->appendFunc( &macState, &buf[0], 1 );          // zero byte
+        }
+
+        pExpandedKey->macAlg->appendFunc( &macState, pbContext, cbContext); // Context
+
+        SYMCRYPT_STORE_MSBFIRST32( &buf[0], 8 * (UINT32)cbResult );
+        pExpandedKey->macAlg->appendFunc( &macState, &buf[0], 4 );         // output length, in bits
+
+        pExpandedKey->macAlg->resultFunc( &macState, rbBlockResult );
+
+        bytes = SYMCRYPT_MIN( bytesRemaining, blockSize );
+        memcpy( pbResult, rbBlockResult, bytes );
+        pbResult += bytes;
+        bytesRemaining -= bytes;
+    }
+
+    SymCryptWipeKnownSize( rbBlockResult, sizeof( rbBlockResult ) );
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSp800_108ExpandKey(
+    _Out_               PSYMCRYPT_SP800_108_EXPANDED_KEY    pExpandedKey,
+    _In_                PCSYMCRYPT_MAC                      macAlgorithm,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey )
+{
+    SYMCRYPT_ASSERT( macAlgorithm->expandedKeySize <= sizeof( pExpandedKey->macKey ) );
+
+    pExpandedKey->macAlg = macAlgorithm;
+    return macAlgorithm->expandKeyFunc(&pExpandedKey->macKey, pbKey, cbKey );
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSp800_108(
+                                PCSYMCRYPT_MAC  macAlgorithm,
+    _In_reads_(cbKey)           PCBYTE          pbKey,
+                                SIZE_T          cbKey,
+    _In_reads_opt_(cbLabel)     PCBYTE          pbLabel,
+                                SIZE_T          cbLabel,
+    _In_reads_opt_(cbContext)   PCBYTE          pbContext,
+                                SIZE_T          cbContext,
+    _Out_writes_(cbResult)      PBYTE           pbResult,
+                                SIZE_T          cbResult)
+{
+    SYMCRYPT_SP800_108_EXPANDED_KEY key;
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    scError = SymCryptSp800_108ExpandKey( &key, macAlgorithm, pbKey, cbKey );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptSp800_108Derive( &key, pbLabel, cbLabel, pbContext, cbContext, pbResult, cbResult );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    SymCryptWipeKnownSize( &key, sizeof( key ) );
+
+    return scError;
+
+}
+
+
+//
+// Self tests are in sp800_108_*.c files
+// to avoid pulling in SHA-1 when only SP800-108-SHA256 is used and
+// similar scenarios.
+//
diff --git a/libs/symcrypt/lib/sp800_108_hmacsha1.c b/libs/symcrypt/lib/sp800_108_hmacsha1.c
new file mode 100644
index 000000000000..691a6b2a73b4
--- /dev/null
+++ b/libs/symcrypt/lib/sp800_108_hmacsha1.c
@@ -0,0 +1,39 @@
+//
+// sp800_108_hmacsha1.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The SP800-108 SHA-1 test
+// This is in a separate module to avoid pulling in SHA-1 whenever we use SP800_108
+//
+
+static const BYTE    sp800_108_sha1Answer[] =
+{
+    0xcf, 0x4b, 0xfe, 0x4f, 0x85, 0xa1, 0x0b, 0xad,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha1SelfTest(void)
+{
+    BYTE    res[sizeof(sp800_108_sha1Answer)];
+
+    SymCryptSp800_108(
+        SymCryptHmacSha1Algorithm,
+        &SymCryptTestKey32[0], 8,       // key
+        (PCBYTE)"Label", 5,             // label
+        &SymCryptTestKey32[16], 16,     // context
+        res,
+        sizeof(res));
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if (memcmp(res, sp800_108_sha1Answer, sizeof(res)) !=0)
+    {
+        SymCryptFatal('8108');
+    }
+}
diff --git a/libs/symcrypt/lib/sp800_108_hmacsha256.c b/libs/symcrypt/lib/sp800_108_hmacsha256.c
new file mode 100644
index 000000000000..69ecc59669ca
--- /dev/null
+++ b/libs/symcrypt/lib/sp800_108_hmacsha256.c
@@ -0,0 +1,39 @@
+//
+// sp800_108_hmacsha256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The SP800-108 SHA-256 test
+// This is in a separate module to avoid pulling in SHA-256 whenever we use SP800_108
+//
+
+static const BYTE    sp800_108_sha256Answer[] =
+{
+    0x00, 0x26, 0x4b, 0xbb, 0x14, 0x97, 0x40, 0x54,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha256SelfTest(void)
+{
+    BYTE    res[sizeof(sp800_108_sha256Answer)];
+
+    SymCryptSp800_108(
+        SymCryptHmacSha256Algorithm,
+        &SymCryptTestKey32[0], 8,       // key
+        (PCBYTE)"Label", 5,             // label
+        &SymCryptTestKey32[16], 16,     // context
+        res,
+        sizeof(res));
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if (memcmp(res, sp800_108_sha256Answer, sizeof(res)) !=0)
+    {
+        SymCryptFatal('8108');
+    }
+}
diff --git a/libs/symcrypt/lib/sp800_108_hmacsha512.c b/libs/symcrypt/lib/sp800_108_hmacsha512.c
new file mode 100644
index 000000000000..bbb9fa0fdae5
--- /dev/null
+++ b/libs/symcrypt/lib/sp800_108_hmacsha512.c
@@ -0,0 +1,66 @@
+//
+// sp800_108_hmacsha512.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// The SP800-108 SHA-384 / SHA-512 self tests
+// This is in a separate module to avoid pulling in SHA-512 whenever we use SP800_108
+//
+
+static const BYTE    sp800_108_sha384Answer[] =
+{
+    0xc7, 0x10, 0x27, 0x87, 0xd8, 0x96, 0xbc, 0x89,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha384SelfTest(void)
+{
+    BYTE    res[sizeof(sp800_108_sha384Answer)];
+
+    SymCryptSp800_108(
+        SymCryptHmacSha384Algorithm,
+        &SymCryptTestKey32[0], 8,       // key
+        (PCBYTE)"Label", 5,             // label
+        &SymCryptTestKey32[16], 16,     // context
+        res,
+        sizeof(res));
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if (memcmp(res, sp800_108_sha384Answer, sizeof(res)) !=0)
+    {
+        SymCryptFatal('8108');
+    }
+}
+
+static const BYTE    sp800_108_sha512Answer[] =
+{
+    0xdb, 0x3a, 0x18, 0xd9, 0x6c, 0x4a, 0xd4, 0x1e,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSp800_108_HmacSha512SelfTest(void)
+{
+    BYTE    res[sizeof(sp800_108_sha512Answer)];
+
+    SymCryptSp800_108(
+        SymCryptHmacSha512Algorithm,
+        &SymCryptTestKey32[0], 8,       // key
+        (PCBYTE)"Label", 5,             // label
+        &SymCryptTestKey32[16], 16,     // context
+        res,
+        sizeof(res));
+
+    SymCryptInjectError( res, sizeof( res ) );
+
+    if (memcmp(res, sp800_108_sha512Answer, sizeof(res)) !=0)
+    {
+        SymCryptFatal('8108');
+    }
+}
diff --git a/libs/symcrypt/lib/srtp_kdf.c b/libs/symcrypt/lib/srtp_kdf.c
new file mode 100644
index 000000000000..34c7f4837d7f
--- /dev/null
+++ b/libs/symcrypt/lib/srtp_kdf.c
@@ -0,0 +1,175 @@
+//
+// srtp_kdf.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module implements SRTP-KDF specified in RFC 3711 Section 4.3.1.
+//
+
+#include "precomp.h"
+
+
+#define SYMCRYPT_SRTP_KDF_SALT_SIZE (112 / 8)
+
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSrtpKdfExpandKey(
+    _Out_               PSYMCRYPT_SRTPKDF_EXPANDED_KEY      pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey)
+{
+    return SymCryptAesExpandKeyEncryptOnly(&pExpandedKey->aesExpandedKey, pbKey, cbKey);
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSrtpKdfDerive(
+    _In_                    PCSYMCRYPT_SRTPKDF_EXPANDED_KEY pExpandedKey,
+    _In_reads_(cbSalt)      PCBYTE  pbSalt,
+                            SIZE_T  cbSalt,
+                            UINT32  uKeyDerivationRate,
+                            UINT64  uIndex,
+                            UINT32  uIndexWidth,
+                            BYTE    label,
+    _Out_writes_(cbOutput)  PBYTE   pbOutput,
+                            SIZE_T  cbOutput)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    BYTE X[16] = { 0 };
+
+    //
+    // uIndexWidth must be one of 0, 32 or 48. RFC 3711 defines SRTP indices to be
+    // 48-bits. SRTCP indices were first specified as 32-bit values and then updated to
+    // 48-bits by Errata ID 3712. uIndexWidth parameter allows specifying the width of
+    // the uIndex parameter for both SRTP and SRTCP indices. The test vectors use
+    // 32-bit SRTCP index values.
+    //
+    // The default value of 0 is equivalent to setting uIndexWidth to 48.
+    if (uIndexWidth == 0)
+    {
+        uIndexWidth = 48;
+    }
+    else if (uIndexWidth != 32 && uIndexWidth != 48)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbSalt != SYMCRYPT_SRTP_KDF_SALT_SIZE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // uKeyDerivationRate must be zero or 2^i for 0 <= i <= 24.
+    // This is verified by checking both it is not greater than 2^24 and it is either zero or a power of two.
+    if( (uKeyDerivationRate > (1 << 24))  || ((uKeyDerivationRate & (uKeyDerivationRate - 1)) != 0) )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Initialize X to Salt || 0
+    memcpy(X, pbSalt, cbSalt);
+
+    // (uIndex DIV uKeyDerivationRate) operation can be performed with a right shift as
+    // uKeyDerivationRate is either zero or a power of 2. When uKeyDerivationRate is zero,
+    // DIV operation should evaluate to zero, which can be performed by shifting uIndex by 48 bits,
+    // i.e., maximum value it may have.
+    UINT32 kdrShift = 48;
+    if (uKeyDerivationRate)
+    {
+        for (UINT32 i = 0; i <= 24; i++)
+        {
+            if (uKeyDerivationRate == (1UL << i))
+            {
+                kdrShift = i;
+                break;
+            }
+        }
+    }
+
+    UINT64 r = uIndex >> kdrShift;
+
+    UINT64 key_id = ((UINT64)label << uIndexWidth) | r;
+
+    // XOR key_id into salt
+    //
+    // X = S0 ... |S6 ... S13| 0 0
+    //            |  key_id  |
+    //
+    PBYTE pbXorPos = &X[SYMCRYPT_SRTP_KDF_SALT_SIZE - sizeof(key_id)];
+    UINT64 uSaltLsb = SYMCRYPT_LOAD_MSBFIRST64(pbXorPos);
+    SYMCRYPT_STORE_MSBFIRST64(pbXorPos, uSaltLsb ^ key_id);
+
+    //
+    // We break the read-once/write once rule here by writing to the pbOutput buffer twice.
+    // The first write wipes the buffer so that we get the raw keystream bytes from AES-CTR encryption.
+    // The second write to pbOutput occurs with the SymCryptAesCtrMsb64() call that produces the keystream bytes.
+    //
+    // Modification of pbOutput between the two calls does not leak any information, it just results in flipping of the
+    // corresponding bits of the correct output.
+    SymCryptWipe(pbOutput, cbOutput);
+    SymCryptAesCtrMsb64(&pExpandedKey->aesExpandedKey, X, pbOutput, pbOutput, cbOutput & ~0xf);
+
+    // SymCryptAesCtrMsb64 only processes full blocks. If cbOutput is not a multiple of 16 we generate the last block of
+    // keystream to local buffer and copy the necessary number of bytes to output.
+    if (cbOutput & 0xf)
+    {
+        BYTE lastBlockBytes[16] = { 0 };
+
+        SymCryptAesCtrMsb64(&pExpandedKey->aesExpandedKey, X, lastBlockBytes, lastBlockBytes, 16);
+
+        memcpy(pbOutput + 16 * (cbOutput / 16), lastBlockBytes, cbOutput & 0xf);
+
+        SymCryptWipeKnownSize(lastBlockBytes, sizeof(lastBlockBytes));
+    }
+
+cleanup:
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSrtpKdf(
+    _In_reads_(cbKey)           PCBYTE  pbKey,
+                                SIZE_T  cbKey,
+    _In_reads_(cbSalt)          PCBYTE  pbSalt,
+                                SIZE_T  cbSalt,
+                                UINT32  uKeyDerivationRate,
+                                UINT64  uIndex,
+                                UINT32  uIndexWidth,
+                                BYTE    label,
+    _Out_writes_(cbOutput)      PBYTE   pbOutput,
+                                SIZE_T  cbOutput)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_SRTPKDF_EXPANDED_KEY expandedKey;
+
+    scError = SymCryptSrtpKdfExpandKey(&expandedKey, pbKey, cbKey);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptSrtpKdfDerive(&expandedKey,
+                                    pbSalt, cbSalt,
+                                    uKeyDerivationRate,
+                                    uIndex, uIndexWidth,
+                                    label,
+                                    pbOutput, cbOutput);
+
+cleanup:
+
+    SymCryptWipeKnownSize(&expandedKey, sizeof(expandedKey));
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ssh_kdf.c b/libs/symcrypt/lib/ssh_kdf.c
new file mode 100644
index 000000000000..b1d961107d00
--- /dev/null
+++ b/libs/symcrypt/lib/ssh_kdf.c
@@ -0,0 +1,122 @@
+//
+// ssh_kdf.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module implements SSH-KDF specified in RFC 4253 Section 7.2.
+//
+
+#include "precomp.h"
+
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSshKdfExpandKey(
+    _Out_               PSYMCRYPT_SSHKDF_EXPANDED_KEY   pExpandedKey,
+    _In_                PCSYMCRYPT_HASH                 pHashFunc,
+    _In_reads_(cbKey)   PCBYTE                          pbKey,
+                        SIZE_T                          cbKey)
+{
+    pExpandedKey->pHashFunc = pHashFunc;
+
+    SymCryptHashInit(pHashFunc, &pExpandedKey->hashState);
+    SymCryptHashAppend(pHashFunc, &pExpandedKey->hashState, pbKey, cbKey);
+
+    return SYMCRYPT_NO_ERROR;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSshKdfDerive(
+    _In_                        PCSYMCRYPT_SSHKDF_EXPANDED_KEY  pExpandedKey,
+    _In_reads_(cbHashValue)     PCBYTE                          pbHashValue,
+                                SIZE_T                          cbHashValue,
+                                BYTE                            label,
+    _In_reads_(cbSessionId)     PCBYTE                          pbSessionId,
+                                SIZE_T                          cbSessionId,
+    _Inout_updates_(cbOutput)   PBYTE                           pbOutput,
+                                SIZE_T                          cbOutput)
+{
+    SYMCRYPT_ERROR      scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_HASH_STATE hashState;
+
+    PCBYTE          pcbOutputSave = pbOutput;
+    PCSYMCRYPT_HASH pHashFunc = pExpandedKey->pHashFunc;
+    SIZE_T          cbHashResultSize = SymCryptHashResultSize(pHashFunc);
+
+
+    while (cbOutput > 0)
+    {
+        SIZE_T cbGeneratedOutput = pbOutput - pcbOutputSave;
+
+        SymCryptHashStateCopy(pHashFunc, &pExpandedKey->hashState, &hashState);
+        SymCryptHashAppend(pHashFunc, &hashState, pbHashValue, cbHashValue);                // hashState has (K || H)
+
+        // label and session ID are appended only in the first iteration
+        if (cbGeneratedOutput == 0)
+        {
+            SymCryptHashAppend(pHashFunc, &hashState, &label, 1);
+            SymCryptHashAppend(pHashFunc, &hashState, pbSessionId, cbSessionId);
+        }
+        else
+        {
+            // We break the read-once write-once rule here by appending data to a
+            // hash computation from pbOutput that was written by SymCryptHashResult()
+            // below.
+            // Modification of data in pbOutput buffer after it's written and before
+            // used again will have uncontrolled disturbances in the hash output and cannot
+            // be used to gain knowledge about the secret key.
+            SymCryptHashAppend(pHashFunc, &hashState, pcbOutputSave, cbGeneratedOutput);    // hashState has (K || H || K1 .. Ki)
+        }
+
+        SymCryptHashResult(pHashFunc, &hashState, pbOutput, cbOutput);
+
+        SIZE_T bytesCopied = SYMCRYPT_MIN(cbOutput, cbHashResultSize);
+
+        pbOutput += bytesCopied;
+        cbOutput -= bytesCopied;
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSshKdf(
+    _In_                    PCSYMCRYPT_HASH     pHashFunc,
+    _In_reads_(cbKey)       PCBYTE              pbKey,
+                            SIZE_T              cbKey,
+    _In_reads_(cbHashValue) PCBYTE              pbHashValue,
+                            SIZE_T              cbHashValue,
+                            BYTE                label,
+    _In_reads_(cbSessionId) PCBYTE              pbSessionId,
+                            SIZE_T              cbSessionId,
+    _Out_writes_(cbOutput)  PBYTE               pbOutput,
+                            SIZE_T              cbOutput)
+{
+    SYMCRYPT_SSHKDF_EXPANDED_KEY expandedKey;
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    scError = SymCryptSshKdfExpandKey(&expandedKey, pHashFunc, pbKey, cbKey);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptSshKdfDerive(&expandedKey,
+                                    pbHashValue, cbHashValue,
+                                    label,
+                                    pbSessionId, cbSessionId,
+                                    pbOutput, cbOutput);
+
+ cleanup:
+
+    SymCryptWipeKnownSize(&expandedKey, sizeof(expandedKey));
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/ssh_kdf_sha256.c b/libs/symcrypt/lib/ssh_kdf_sha256.c
new file mode 100644
index 000000000000..f324dda3ae2c
--- /dev/null
+++ b/libs/symcrypt/lib/ssh_kdf_sha256.c
@@ -0,0 +1,65 @@
+//
+// ssh_kdf_sha256.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+
+
+static const BYTE pbKey[] =
+{
+    0x00, 0x00, 0x00, 0x81, 0x00, 0x87, 0x5c, 0x55, 0x1c, 0xef, 0x52, 0x6a, 0x4a, 0x8b, 0xe1, 0xa7,
+    0xdf, 0x27, 0xe9, 0xed, 0x35, 0x4b, 0xac, 0x9a, 0xfb, 0x71, 0xf5, 0x3d, 0xba, 0xe9, 0x05, 0x67,
+    0x9d, 0x14, 0xf9, 0xfa, 0xf2, 0x46, 0x9c, 0x53, 0x45, 0x7c, 0xf8, 0x0a, 0x36, 0x6b, 0xe2, 0x78,
+    0x96, 0x5b, 0xa6, 0x25, 0x52, 0x76, 0xca, 0x2d, 0x9f, 0x4a, 0x97, 0xd2, 0x71, 0xf7, 0x1e, 0x50,
+    0xd8, 0xa9, 0xec, 0x46, 0x25, 0x3a, 0x6a, 0x90, 0x6a, 0xc2, 0xc5, 0xe4, 0xf4, 0x8b, 0x27, 0xa6,
+    0x3c, 0xe0, 0x8d, 0x80, 0x39, 0x0a, 0x49, 0x2a, 0xa4, 0x3b, 0xad, 0x9d, 0x88, 0x2c, 0xca, 0xc2,
+    0x3d, 0xac, 0x88, 0xbc, 0xad, 0xa4, 0xb4, 0xd4, 0x26, 0xa3, 0x62, 0x08, 0x3d, 0xab, 0x65, 0x69,
+    0xc5, 0x4c, 0x22, 0x4d, 0xd2, 0xd8, 0x76, 0x43, 0xaa, 0x22, 0x76, 0x93, 0xe1, 0x41, 0xad, 0x16,
+    0x30, 0xce, 0x13, 0x14, 0x4e
+};
+
+static const BYTE pbHash[] =
+{
+    0x0e, 0x68, 0x3f, 0xc8, 0xa9, 0xed, 0x7c, 0x2f, 0xf0, 0x2d, 0xef, 0x23, 0xb2, 0x74, 0x5e, 0xbc,
+    0x99, 0xb2, 0x67, 0xda, 0xa8, 0x6a, 0x4a, 0xa7, 0x69, 0x72, 0x39, 0x08, 0x82, 0x53, 0xf6, 0x42
+};
+
+static const BYTE pbSessionId[] =
+{
+    0x0e, 0x68, 0x3f, 0xc8, 0xa9, 0xed, 0x7c, 0x2f, 0xf0, 0x2d, 0xef, 0x23, 0xb2, 0x74, 0x5e, 0xbc,
+    0x99, 0xb2, 0x67, 0xda, 0xa8, 0x6a, 0x4a, 0xa7, 0x69, 0x72, 0x39, 0x08, 0x82, 0x53, 0xf6, 0x42
+};
+
+static const BYTE label = SYMCRYPT_SSHKDF_ENCRYPTION_KEY_CLIENT_TO_SERVER;
+
+static const BYTE pbResult[] =
+{
+    0x4a, 0x63, 0x14, 0xd2, 0xf7, 0x51, 0x1b, 0xf8, 0x8f, 0xad, 0x39, 0xfb, 0x68, 0x92, 0xf3, 0xf2, 0x18, 0xca, 0xfd, 0x53, 0x0e, 0x72, 0xfe, 0x43
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSshKdfSha256SelfTest(void)
+{
+    SYMCRYPT_SSHKDF_EXPANDED_KEY expandedKey;
+    SYMCRYPT_ALIGN BYTE rbResult[sizeof(pbResult)];
+
+    SymCryptSshKdfExpandKey(&expandedKey, SymCryptSha256Algorithm, pbKey, sizeof(pbKey));
+
+    SymCryptSshKdfDerive(&expandedKey,
+                        pbHash, sizeof(pbHash),
+                        label,
+                        pbSessionId, sizeof(pbSessionId),
+                        rbResult, sizeof(rbResult)
+                        );
+
+    SymCryptInjectError(rbResult, sizeof(rbResult));
+
+    if (memcmp(rbResult, pbResult, sizeof(pbResult)) != 0)
+    {
+        SymCryptFatal('sshk');
+    }
+}
diff --git a/libs/symcrypt/lib/ssh_kdf_sha512.c b/libs/symcrypt/lib/ssh_kdf_sha512.c
new file mode 100644
index 000000000000..909403753c16
--- /dev/null
+++ b/libs/symcrypt/lib/ssh_kdf_sha512.c
@@ -0,0 +1,70 @@
+//
+// ssh_kdf_sha512.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+
+
+static const BYTE pbKey[] =
+{
+    0x00, 0x00, 0x00, 0x80, 0x57, 0x53, 0x08, 0xca, 0x39, 0x57, 0x98, 0xbb, 0x21, 0xec, 0x54, 0x38,
+    0xc4, 0x6a, 0x88, 0xff, 0xa3, 0xf7, 0xf7, 0x67, 0x1c, 0x06, 0xf9, 0x24, 0xab, 0xf7, 0xc3, 0xcf,
+    0xb4, 0x6c, 0x78, 0xc0, 0x25, 0x59, 0x6e, 0x4a, 0xba, 0x50, 0xc3, 0x27, 0x10, 0x89, 0x18, 0x4a,
+    0x44, 0x7a, 0x57, 0x1a, 0xbb, 0x7f, 0x4a, 0x1b, 0x1c, 0x41, 0xf5, 0xd5, 0xca, 0x80, 0x62, 0x94,
+    0x0d, 0x43, 0x69, 0x77, 0x85, 0x89, 0xfd, 0xe8, 0x1a, 0x71, 0xb2, 0x22, 0x8f, 0x01, 0x8c, 0x4c,
+    0x83, 0x6c, 0xf3, 0x89, 0xf8, 0x54, 0xf8, 0x6d, 0xe7, 0x1a, 0x68, 0xb1, 0x69, 0x3f, 0xe8, 0xff,
+    0xa1, 0xc5, 0x9c, 0xe7, 0xe9, 0xf9, 0x22, 0x3d, 0xeb, 0xad, 0xa2, 0x56, 0x6d, 0x2b, 0x0e, 0x56,
+    0x78, 0xa4, 0x8b, 0xfb, 0x53, 0x0e, 0x7b, 0xee, 0x42, 0xbd, 0x2a, 0xc7, 0x30, 0x4a, 0x0a, 0x5a,
+    0xe3, 0x39, 0xa2, 0xcd
+};
+
+static const BYTE pbHash[] =
+{
+    0xa4, 0x12, 0x5a, 0xa9, 0x89, 0x80, 0x92, 0xca, 0x50, 0xc3, 0xc1, 0x63, 0x1c, 0x03, 0xdc, 0xbc,
+    0x9d, 0xf9, 0x5c, 0xeb, 0xb4, 0x09, 0x88, 0x1e, 0x58, 0x01, 0x08, 0xb6, 0xcc, 0x47, 0x04, 0xb7,
+    0x6c, 0xc7, 0x7b, 0x87, 0x95, 0xfd, 0x59, 0x40, 0x56, 0x1e, 0x32, 0x24, 0xcc, 0x75, 0x84, 0x85,
+    0x18, 0x99, 0x2b, 0xd8, 0xd9, 0xb7, 0x0f, 0xe0, 0xfc, 0x97, 0x7a, 0x47, 0x60, 0x63, 0xc8, 0xbf
+};
+
+static const BYTE pbSessionId[] =
+{
+    0xa4, 0x12, 0x5a, 0xa9, 0x89, 0x80, 0x92, 0xca, 0x50, 0xc3, 0xc1, 0x63, 0x1c, 0x03, 0xdc, 0xbc,
+    0x9d, 0xf9, 0x5c, 0xeb, 0xb4, 0x09, 0x88, 0x1e, 0x58, 0x01, 0x08, 0xb6, 0xcc, 0x47, 0x04, 0xb7,
+    0x6c, 0xc7, 0x7b, 0x87, 0x95, 0xfd, 0x59, 0x40, 0x56, 0x1e, 0x32, 0x24, 0xcc, 0x75, 0x84, 0x85,
+    0x18, 0x99, 0x2b, 0xd8, 0xd9, 0xb7, 0x0f, 0xe0, 0xfc, 0x97, 0x7a, 0x47, 0x60, 0x63, 0xc8, 0xbf
+};
+
+static const BYTE label = SYMCRYPT_SSHKDF_ENCRYPTION_KEY_CLIENT_TO_SERVER;
+
+static const BYTE pbResult[] =
+{
+    0x7e, 0x4a, 0x72, 0x1f, 0xb7, 0x37, 0x9e, 0xbb, 0x42, 0x33, 0x06, 0x46, 0x4d, 0x57, 0xdb, 0x46,
+    0xaf, 0xa3, 0xcc, 0xa1, 0x0a, 0x1d, 0x7f, 0xeb
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptSshKdfSha512SelfTest(void)
+{
+    SYMCRYPT_SSHKDF_EXPANDED_KEY expandedKey;
+    SYMCRYPT_ALIGN BYTE rbResult[sizeof(pbResult)];
+
+    SymCryptSshKdfExpandKey(&expandedKey, SymCryptSha512Algorithm, pbKey, sizeof(pbKey));
+
+    SymCryptSshKdfDerive(&expandedKey,
+                    pbHash, sizeof(pbHash),
+                    label,
+                    pbSessionId, sizeof(pbSessionId),
+                    rbResult, sizeof(rbResult)
+                    );
+
+    SymCryptInjectError(rbResult, sizeof(rbResult));
+
+    if (memcmp(rbResult, pbResult, sizeof(pbResult)) != 0)
+    {
+        SymCryptFatal('sshk');
+    }
+}
diff --git a/libs/symcrypt/lib/sskdf.c b/libs/symcrypt/lib/sskdf.c
new file mode 100644
index 000000000000..65aebaf24dd6
--- /dev/null
+++ b/libs/symcrypt/lib/sskdf.c
@@ -0,0 +1,266 @@
+//
+// sskdf.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module implements Single-Step KDF as specified in SP800-56C section 4.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+#define SYMCRYPT_SSKDF_KMAC_128_DEFAULT_SALT_SIZE (164)
+#define SYMCRYPT_SSKDF_KMAC_256_DEFAULT_SALT_SIZE (132)
+#define SYMCRYPT_SSKDF_DEFAULT_SALT_MAX SYMCRYPT_SSKDF_KMAC_128_DEFAULT_SALT_SIZE
+
+
+static const BYTE   pbKmacCustomizationString[3] = { 'K', 'D', 'F' };
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfMacExpandSalt(
+    _Out_                   PSYMCRYPT_SSKDF_MAC_EXPANDED_SALT   pExpandedSalt,
+    _In_                    PCSYMCRYPT_MAC                      macAlgorithm,
+    _In_reads_opt_(cbSalt)  PCBYTE                              pbSalt,
+                            SIZE_T                              cbSalt)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    const BYTE pbSaltDefault[SYMCRYPT_SSKDF_DEFAULT_SALT_MAX] = { 0 };
+
+    SYMCRYPT_ASSERT( macAlgorithm->expandedKeySize <= sizeof( pExpandedSalt->macKey ) );
+
+    pExpandedSalt->macAlg = macAlgorithm;
+
+    if ( pbSalt == NULL )
+    {
+        if ( macAlgorithm == SymCryptKmac128Algorithm )
+        {
+            cbSalt = SYMCRYPT_SSKDF_KMAC_128_DEFAULT_SALT_SIZE;
+        }
+        else if ( macAlgorithm == SymCryptKmac256Algorithm )
+        {
+            cbSalt = SYMCRYPT_SSKDF_KMAC_256_DEFAULT_SALT_SIZE;
+        }
+        else
+        {
+            cbSalt = SymCryptHashInputBlockSize( *(macAlgorithm->ppHashAlgorithm) );
+        }
+
+        pbSalt = pbSaltDefault;
+    }
+
+    if ( macAlgorithm == SymCryptKmac128Algorithm )
+    {
+        scError = SymCryptKmac128ExpandKeyEx(
+            &pExpandedSalt->macKey.kmac128Key,
+            pbSalt,
+            cbSalt,
+            pbKmacCustomizationString,
+            sizeof( pbKmacCustomizationString ) );
+    }
+    else if ( macAlgorithm == SymCryptKmac256Algorithm )
+    {
+        scError = SymCryptKmac256ExpandKeyEx(
+            &pExpandedSalt->macKey.kmac256Key,
+            pbSalt,
+            cbSalt,
+            pbKmacCustomizationString,
+            sizeof( pbKmacCustomizationString ) );
+    }
+    else
+    {
+        scError = macAlgorithm->expandKeyFunc( &pExpandedSalt->macKey, pbSalt, cbSalt );
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfMacDerive(
+    _In_                    PCSYMCRYPT_SSKDF_MAC_EXPANDED_SALT  pExpandedSalt,
+                            SIZE_T                              cbMacOutputSize,
+    _In_reads_(cbSecret)    PCBYTE                              pbSecret,
+                            SIZE_T                              cbSecret,
+    _In_reads_opt_(cbInfo)  PCBYTE                              pbInfo,
+                            SIZE_T                              cbInfo,
+    _Out_writes_(cbResult)  PBYTE                               pbResult,
+                            SIZE_T                              cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_MAC_STATE state;
+    PCSYMCRYPT_MAC pMacAlgorithm = pExpandedSalt->macAlg;
+    PSYMCRYPT_MAC_RESULT_EX resultFuncEx = NULL;
+
+    SYMCRYPT_ALIGN BYTE rbPartialResult[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+    PBYTE pbCurr = pbResult;
+
+    SIZE_T cbMacResultSize = pMacAlgorithm->resultSize;
+    SIZE_T cbBlock;
+    UINT32 cntr = 1;
+    BYTE ctrBuf[4];
+
+    if ( cbMacOutputSize > 64 )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if ( pMacAlgorithm == SymCryptKmac128Algorithm )
+    {
+        cbMacResultSize = cbMacOutputSize;
+        resultFuncEx = SymCryptKmac128ResultEx;
+    }
+    else if ( pMacAlgorithm == SymCryptKmac256Algorithm )
+    {
+        cbMacResultSize = cbMacOutputSize;
+        resultFuncEx = SymCryptKmac256ResultEx;
+    }
+    else if ( cbMacOutputSize > 0 && cbMacOutputSize != pMacAlgorithm->resultSize )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    while ( cbResult > 0 )
+    {
+        SYMCRYPT_STORE_MSBFIRST32( ctrBuf, cntr );
+
+        // Calculate K(i) = H(counter || Z || FixedInfo)
+        pMacAlgorithm->initFunc( &state, &pExpandedSalt->macKey );
+        pMacAlgorithm->appendFunc( &state, ctrBuf, sizeof( ctrBuf ) );
+        pMacAlgorithm->appendFunc( &state, pbSecret, cbSecret );
+        pMacAlgorithm->appendFunc( &state, pbInfo, cbInfo );
+
+        cbBlock = SYMCRYPT_MIN( cbResult, cbMacResultSize );
+
+        if ( resultFuncEx != NULL )
+        {
+            if ( cbMacOutputSize > 0 )
+            {
+                resultFuncEx( &state, rbPartialResult, cbMacOutputSize );
+            }
+            else
+            {
+                // If the output size is not specified, calculate the full result
+                resultFuncEx( &state, pbResult, cbResult );
+                break;
+            }
+        }
+        else
+        {
+            pMacAlgorithm->resultFunc( &state, rbPartialResult );
+        }
+
+        // Store the result in the output buffer
+        memcpy( pbCurr, rbPartialResult, cbBlock );
+
+        // Update counters
+        cntr++;
+        pbCurr += cbBlock;
+        cbResult -= cbBlock;
+    }
+
+cleanup:
+
+    SymCryptWipeKnownSize( &rbPartialResult[0], sizeof( rbPartialResult ) );
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfMac(
+    _In_                    PCSYMCRYPT_MAC  macAlgorithm,
+                            SIZE_T          cbMacOutputSize,
+    _In_reads_(cbSecret)    PCBYTE          pbSecret,
+                            SIZE_T          cbSecret,
+    _In_reads_opt_(cbSalt)  PCBYTE          pbSalt,
+                            SIZE_T          cbSalt,
+    _In_reads_opt_(cbInfo)  PCBYTE          pbInfo,
+                            SIZE_T          cbInfo,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_SSKDF_MAC_EXPANDED_SALT expandedSalt;
+
+    scError = SymCryptSskdfMacExpandSalt( &expandedSalt, macAlgorithm, pbSalt, cbSalt );
+
+    if ( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptSskdfMacDerive(
+        &expandedSalt,
+        cbMacOutputSize,
+        pbSecret,
+        cbSecret,
+        pbInfo,
+        cbInfo,
+        pbResult,
+        cbResult );
+
+cleanup:
+
+    SymCryptWipeKnownSize( &expandedSalt, sizeof( expandedSalt ) );
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptSskdfHash(
+    _In_                    PCSYMCRYPT_HASH hashAlgorithm,
+                            SIZE_T          cbHashOutputSize,
+    _In_reads_(cbSecret)    PCBYTE          pbSecret,
+                            SIZE_T          cbSecret,
+    _In_reads_opt_(cbInfo)  PCBYTE          pbInfo,
+                            SIZE_T          cbInfo,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult)
+{
+    SYMCRYPT_HASH_STATE state;
+
+    PBYTE pbCurr = pbResult;
+
+    SIZE_T cbHashResultSize = hashAlgorithm->resultSize;
+    SIZE_T cbPartialResult;
+    UINT32 cntr = 1;
+    BYTE ctrBuf[4];
+
+    if ( cbHashOutputSize > 64 ||
+         cbHashOutputSize > 0 && cbHashOutputSize != hashAlgorithm->resultSize )
+    {
+        return SYMCRYPT_INVALID_ARGUMENT;
+    }
+
+    while ( cbResult > 0 )
+    {
+        SYMCRYPT_STORE_MSBFIRST32( ctrBuf, cntr );
+
+        cbPartialResult = SYMCRYPT_MIN( cbResult, cbHashResultSize );
+
+        // Calculate K(i) = H(counter || Z || FixedInfo)
+        SymCryptHashInit( hashAlgorithm, &state );
+        SymCryptHashAppend( hashAlgorithm, &state, ctrBuf, sizeof( ctrBuf ) );
+        SymCryptHashAppend( hashAlgorithm, &state, pbSecret, cbSecret );
+        SymCryptHashAppend( hashAlgorithm, &state, pbInfo, cbInfo );
+        SymCryptHashResult( hashAlgorithm, &state, pbCurr, cbPartialResult );
+
+        // Update counters
+        cntr++;
+        pbCurr += cbPartialResult;
+        cbResult -= cbPartialResult;
+    }
+
+    return SYMCRYPT_NO_ERROR;
+}
diff --git a/libs/symcrypt/lib/symcryptasm_shared.cppasm b/libs/symcrypt/lib/symcryptasm_shared.cppasm
new file mode 100644
index 000000000000..7eea09322916
--- /dev/null
+++ b/libs/symcrypt/lib/symcryptasm_shared.cppasm
@@ -0,0 +1,97 @@
+//
+//  symcryptasm_shared.cppasm   Shared definitions used by the C preprocessor step in symcryptasm
+//  processing. See scripts/symcryptasm_processor.py for more details.
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#ifdef SYMCRYPT_PLATFORM_WINE
+# define SYMCRYPT_GAS
+# if defined __aarch64__ || defined __arm64ec__
+#  define SYMCRYPT_CPU_ARM64 1
+# elif defined __arm__
+#  define SYMCRYPT_CPU_ARM 1
+# elif defined __x86_64__
+#  define SYMCRYPT_CPU_AMD64 1
+# elif defined __i386__
+#  define SYMCRYPT_CPU_X86 1
+# endif
+#endif
+
+#include "C_asm_shared.inc"
+
+// Platform-specific name mangling
+// ARM64EC needs to use a modified function name as the code can co-exist with equivalent ARM64 code.
+// macOS (Mach-O) needs to prefix all C symbols with an underscore.
+#if defined __arm64ec__ && defined SYMCRYPT_GAS
+    /* from compiler-rt assembly.h */
+    #define GLUE2_(a, b) a##b
+    #define GLUE2(a, b) GLUE2_(a, b)
+    #define QUOTE(a) #a
+    #define STR(a) QUOTE(a)
+    #define HASH #
+    #define PLATFORM_NAME_MANGLE(name) STR(GLUE2(HASH, name))
+#elif defined _M_ARM64EC
+    // ARM64EC name mangling for Windows
+    #define ARM64EC_NAME_DECORATION_SYMBOL() #
+    #define ARM64EC_NAME_DECORATE(FuncName) ARM64EC_NAME_DECORATION_SYMBOL() ## FuncName
+    #define PLATFORM_NAME_MANGLE(FuncName) |ARM64EC_NAME_DECORATE(FuncName)|
+#else
+    #if defined(__APPLE__)
+        // macOS/Mach-O requires underscore prefix for C symbols
+        #define PLATFORM_NAME_MANGLE(FuncName) _ ## FuncName
+    #else
+        // No prefix for other platforms
+        #define PLATFORM_NAME_MANGLE(FuncName) FuncName
+    #endif
+#endif
+
+#if defined(SYMCRYPT_MASM)
+
+    #if defined(SYMCRYPT_CPU_AMD64)
+        include ksamd64.inc
+    #elif defined(SYMCRYPT_CPU_ARM64)
+        #include "ksarm64.h"
+    #endif
+
+    #define FILE_END()                      END
+    #define ALIGN(__alignment)              align __alignment
+    #define HEX(__constant)                 __constant##h
+    #define TEXTAREA()                      TEXTAREA
+    #define EXTERN(__label)                 EXTERN __label
+    #define LABEL(__labelname)              __labelname
+    #define GET_SYMBOL_ADDRESS(__symbol)    __symbol
+
+#elif defined(SYMCRYPT_GAS)
+
+    #if defined(SYMCRYPT_CPU_AMD64)
+        .intel_syntax noprefix
+    #elif defined(SYMCRYPT_CPU_X86)
+        .intel_syntax noprefix
+	.def @feat.00; .scl 3; .type 0; .endef
+        .globl @feat.00
+        .set @feat.00, 1  /* safeseh */
+    #endif
+
+    #define FILE_END()
+    #if defined(SYMCRYPT_CPU_AMD64)
+        #define ALIGN(__alignment)          .balign __alignment
+    #else
+        #define ALIGN(__alignment)
+    #endif
+    #define HEX(__constant)                 0x##__constant
+    #define TEXTAREA()
+    #define EXTERN(__label)                 .extern __label
+    #define LABEL(__labelname)              __labelname:
+
+    #if defined(__APPLE__)
+        #define GET_SYMBOL_ADDRESS(__symbol)    _ ## __symbol
+    #else
+        #define GET_SYMBOL_ADDRESS(__symbol)    __symbol+rip
+    #endif
+
+#else
+
+    #error Unknown target assembly
+
+#endif
diff --git a/libs/symcrypt/lib/tlsCbcVerify.c b/libs/symcrypt/lib/tlsCbcVerify.c
new file mode 100644
index 000000000000..26a1a173a077
--- /dev/null
+++ b/libs/symcrypt/lib/tlsCbcVerify.c
@@ -0,0 +1,458 @@
+//
+// tlsCbcVerify.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// This code needs to process data in words, and we'd like to use 32-bit words on 32-bit
+// architectures and 64-bit words on 64-bit architectures. So we use NATIVE_UINT & friends.
+//
+// We don't want to use 64-bit words on 32-bit architectures because the 64-bit shift/rotate
+// code might not be constant-time, and it puts further register pressure on the x86 that can only
+// use 6 registers in C code.
+//
+
+#if NATIVE_BYTES == 8
+#define NATIVE_01   (0x0101010101010101)
+#elif NATIVE_BYTES == 4
+#define NATIVE_01   (0x01010101)
+#else
+#error Unexpected NATIVE_BYTES value
+#endif
+
+//
+// MASK32 macros return UINT32 values based on conditions of the inputs
+//
+
+// MASK32_LT returns a UINT32 that is -1 if _a < _b, 0 otherwise.
+#define MASK32_LT( _a, _b )     ((UINT32)( ( (INT32)((_a)-(_b)) ) >> 31 ) )
+
+// MASK32_EQ returns a UINT32 that is -1 if _a == _b, 0 otherwise.
+#define MASK32_EQ( _a, _b )     (~(UINT32)(-(INT32)((_a) ^ (_b)) >> 31))
+
+
+//
+// Native Byte mask generation is done in inlined functions, as that makes them much more readable
+// These are mask values that are computed per byte in the word.
+//
+
+// Relevant bits to look at when determining whether an index is in the word
+// Difference of index & word start must be in 0..NATIVE_BYTES - 1
+// This mask defines the relevant bits we look at.
+// We avoid using the highest bits as we use fact that the result after the mask
+// is positive. This works as all our positions are < 2^16.
+#define MASKNB_INWORD_RELEVANTBITS (~(NATIVE_BYTES - 1) & 0x0fffffff)
+
+#define MASKNB_BROADCAST( _b )     ((NATIVE_UINT)(_b) * NATIVE_01)
+
+FORCEINLINE
+NATIVE_UINT
+SymCryptNMaskGe( UINT32 wordStart, UINT32 boundary )
+// Return a word starting at byte wordStart from an array with a[i] = 0xff if i>=boundary, 0 otherwise
+{
+    INT32 diff32;
+    NATIVE_INT anySet;
+    UINT32 shift;
+
+    // Mask that is -1 if boundary < wordStart + 8
+    anySet = ((NATIVE_INT) boundary - (NATIVE_INT) wordStart - NATIVE_BYTES) >> (NATIVE_BITS - 1);
+
+    // Compute the index of boundary into the word, possibly negative
+    diff32 = (INT32)boundary - (INT32)wordStart;
+    // Compute the necessary shift when the result will be partially set
+    shift = 8 * (diff32 & (NATIVE_BYTES - 1));
+
+    // Mask the shift to 0 if the word is to be all set as boundary < wordStart
+    shift &= (INT32)~diff32 >> 31;
+
+    return (NATIVE_UINT) anySet << shift;
+}
+
+FORCEINLINE
+NATIVE_UINT
+SymCryptNMaskEq( UINT32 wordStart, UINT32 boundary )
+// Return a word starting at byte wordStart from an array with a[i] = (i == boundary) ? 0xff : 0
+{
+    INT32 diff32;
+    NATIVE_UINT inWord;
+
+    // 32-bit signed difference
+    diff32 = (INT32)boundary - (INT32)wordStart;
+
+    // inWord = (-1) if boundary is within the word, 0 otherwise
+    // Cast to NATIVE_UINT is free on AMD64, as is subsequent cast to NATIVE_INT
+    // A direct cast from INT32 to NATIVE_INT requires a sign extension instruction, so this is faster.
+    inWord = ~ ((-(NATIVE_INT)(NATIVE_UINT)(diff32  & MASKNB_INWORD_RELEVANTBITS)) >> (NATIVE_BITS -1));
+
+    return inWord & ((NATIVE_UINT)0xff << 8 * (diff32 & (NATIVE_BYTES - 1)) );
+}
+
+FORCEINLINE
+NATIVE_UINT
+SymCryptNMaskEq80( UINT32 wordStart, UINT32 boundary )
+// Same as SymcryptNMaskEq except the 0xff is replaced with 0x80.
+{
+    INT32 diff32;
+    NATIVE_UINT inWord;
+
+    // 32-bit signed difference
+    diff32 = (INT32)boundary - (INT32)wordStart;
+
+    // inWord = (-1) if boundary is within the word, 0 otherwise
+    inWord = ~ ((-(NATIVE_INT)(NATIVE_UINT)(diff32  & MASKNB_INWORD_RELEVANTBITS)) >> (NATIVE_BITS -1));
+
+    return inWord & ((NATIVE_UINT)0x80 << 8 * (diff32 & (NATIVE_BYTES - 1)) );
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsCbcHmacVerifyCore(
+    _In_                                        PCSYMCRYPT_HASH             pHash,
+    _Inout_                                     PSYMCRYPT_COMMON_HASH_STATE pState,
+    _In_reads_bytes_(cbData)                    PCBYTE                      pbData,
+                                                SIZE_T                      cbData,
+    _Inout_updates_( pHash->inputBlockSize / 2) PBYTE                       pbMacValue,
+    _Inout_updates_( pHash->resultSize )        PBYTE                       pbHashResult,
+    _Out_                                       PUINT32                     pu32PaddingError )
+//
+// The core of the constant-time TLS record validation.
+// This appends the data part of the record to the hash state, returns the intermediate hash value,
+// and extracts the MAC value out of the record and returns it.
+//
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    UINT32 cbPad;
+    UINT32 maxPadLength;
+    UINT32 u32;
+    UINT32 i;
+    UINT32 iPaddingStart;       // using 'i' for index
+    UINT32 iMacStart;
+    NATIVE_UINT mInData;
+    NATIVE_UINT mInMac;
+    NATIVE_UINT mInPadding;
+    NATIVE_UINT m;
+    NATIVE_UINT nPaddingError = 0;     // nonzero if a padding byte value is wrong.
+    UINT32 next;
+    UINT32 cbHashPrefix;
+    UINT32 cbExtendedData;
+    UINT32 totalBytesHashed;
+    UINT32 hashPaddingFinal;
+    UINT32 resultHashBlockIndex;
+    UINT32 lastHashBlockIndex;
+    NATIVE_UINT w;
+    NATIVE_UINT data;
+    UINT32 backOffset;
+    NATIVE_UINT * bufferLocation;
+    NATIVE_UINT padBytes;
+    UINT32 m32ResultBlock;
+    NATIVE_UINT mResultBlock;
+    SIZE_T tmp;
+    const UINT32 cbMacValue = pHash->inputBlockSize / 2;
+
+    SYMCRYPT_ASSERT( cbMacValue == SymCryptRoundUpPow2Sizet(pHash->resultSize) );
+
+    // Process all the data up to the part where the MAC value might appear
+    // The if() is safe as both cbData and u32 are public values.
+    u32 = pHash->resultSize + 256;
+    if( cbData > u32 )
+    {
+        (*pHash->appendFunc)(pState, pbData, cbData-u32 );
+        pbData += cbData - u32;
+        cbData = u32;
+    }
+
+    // Check that we have enough data for a valid record.
+    // We need one MAC value plus one padding_length byte
+    if (cbData < pHash->resultSize + 1)
+    {
+        scError = SYMCRYPT_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    // We OR our results into the result buffers, so we must init them to zero
+    SymCryptWipe( pbMacValue, cbMacValue );
+    SymCryptWipe( pbHashResult, pHash->resultSize );
+
+    // Pick up the padding_length. Note that this is the value we have to keep secret from
+    // side-channel attacks.
+    cbPad = pbData[cbData - 1];
+
+    // We reduce cbData so that the padding_length byte is no longer under consideration.
+    cbData -= 1;
+
+    // Bound the padding length to cbData - mac_length
+    // This doesn't reveal data as we treat all cbPad values the same, but it makes our
+    // further computations easier
+    maxPadLength = (UINT32)cbData - pHash->resultSize;          // We checked this is >= 0
+    u32 = MASK32_LT( maxPadLength, cbPad );             // mask: maxPadLength < cbPad
+    cbPad = cbPad + ((maxPadLength - cbPad) & u32);
+    nPaddingError |= u32;           // mark as padding error
+
+    // From here on out we maintain indices into a conceptual extended buffer with length cbExtendedData,
+    // and index 0 at the start of the hash computation.
+    // This aligns us with the hash input block, and simplifies hash padding computations and word
+    // accesses.
+    // However, we must always subtract cbHashPrefix from indices before using them to access bytes
+    // in pbData, as the HashPrefix was already hashed into the MAC state.
+
+    cbHashPrefix = (UINT32)pState->dataLengthL;     // # bytes already hashed into the MAC state
+    cbExtendedData = (UINT32)cbData + cbHashPrefix; // total # bytes that the conceptual extended buffer has
+    next = cbHashPrefix;                            // next index we will consider for processing (start of pbData)
+
+    totalBytesHashed = cbExtendedData - pHash->resultSize - cbPad;
+    SYMCRYPT_STORE_MSBFIRST32( &hashPaddingFinal, totalBytesHashed * 8 );      // Length padding for result hash block
+
+    // We need to figure out what the index is of the last hash input block in the computation
+    // (including any phantom blocks after the actual hash is done) and the index of the result
+    // hash block of the actual hash computation.
+    // We've limited the max input for simplicity (everything fits in 32 bits)
+    // We also avoid 64-bit operations as their implementation on 32-bit architectures might not
+    // always be constant-time.
+    // This computation works for SHA-1, SHA-256, and SHA-384 which is all we care about
+    // We avoid using % as the runtime isn't constant and our inputs are secret.
+
+    // First the actual # bytes in the real and phantom computation
+    resultHashBlockIndex = totalBytesHashed + 1 + pHash->inputBlockSize / 8;    // 1 byte 0x80 + length padding in last block
+    lastHashBlockIndex = resultHashBlockIndex + cbPad;                          // The furthest any hash could go
+
+    // round up to a whole # blocks
+    resultHashBlockIndex = (resultHashBlockIndex + pHash->inputBlockSize - 1) & ~(pHash->inputBlockSize - 1);
+    lastHashBlockIndex   = (lastHashBlockIndex   + pHash->inputBlockSize - 1) & ~(pHash->inputBlockSize - 1);
+
+    // Compute the indices where the MAC and padding start
+    iPaddingStart = cbExtendedData - cbPad;
+    iMacStart = iPaddingStart - pHash->resultSize;
+
+    SYMCRYPT_ASSERT( iMacStart < cbExtendedData );    // Fail if the last computation underflowed.
+
+    // Align our handling to the native word size so that we can safely use native words
+    if( (next & (NATIVE_BYTES - 1)) != 0 )
+    {
+        backOffset = next & (NATIVE_BYTES - 1);
+
+        // Process a partial word
+        SYMCRYPT_ASSERT( ( (next ^ pState->bytesInBuffer) & (NATIVE_BYTES - 1) ) == 0 );
+
+        // Read a word; as the MAC value is > 8 bytes this won't overflow the buffer
+        w = *(NATIVE_UINT *) &pbData[0];
+        m = SymCryptNMaskGe( next, iMacStart );
+        mInData = ~m;
+        mInMac = m;
+
+        data = w & mInData;
+        data |= SymCryptNMaskEq80( next, iMacStart );    // add 0x80 byte @ iMacStart
+
+        // Now we put the data into the hash buffer
+        bufferLocation = (NATIVE_UINT *)&pState->buffer[ pState->bytesInBuffer - backOffset ];
+        *bufferLocation = (*bufferLocation & (((NATIVE_UINT)1 << 8*backOffset) - 1))  | (data << 8*backOffset);
+        pState->bytesInBuffer += NATIVE_BYTES - backOffset;
+
+        // And the MAC data in the mac buffer
+        *(NATIVE_UINT *)&pbMacValue[(next - backOffset) & (cbMacValue - 1)] |= (w & mInMac) << 8*backOffset;
+
+        if( pState->bytesInBuffer == pHash->inputBlockSize )
+        {
+            // Block is full. This can't be the result block as we didn't have room for the padding yet.
+            (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, &pState->buffer[0], pHash->inputBlockSize, &tmp );
+            pState->bytesInBuffer = 0;
+        }
+
+        next += NATIVE_BYTES - backOffset;
+    }
+
+    padBytes = MASKNB_BROADCAST( cbPad );
+
+    // Now we can loop over the data in whole words
+    while( next <= cbExtendedData - NATIVE_BYTES )
+    {
+        w = *(NATIVE_UINT *) &pbData[next - cbHashPrefix];
+
+        m = SymCryptNMaskGe( next, iMacStart );
+        mInMac = m;
+        mInData = ~m;
+
+        m = SymCryptNMaskGe( next, iPaddingStart );
+        mInPadding = m;
+        mInMac &= ~m;
+
+        data = w & mInData;
+        data |= SymCryptNMaskEq80( next, iMacStart );    // add 0x80 byte @ iMacStart
+
+        *(NATIVE_UINT *)(&pState->buffer[ pState->bytesInBuffer ]) = data;
+        pState->bytesInBuffer += NATIVE_BYTES;
+
+        if (pState->bytesInBuffer == pHash->inputBlockSize)
+        {
+            // Insert the length component of the hash padding (only in result block)
+            m32ResultBlock = MASK32_EQ( next, resultHashBlockIndex - NATIVE_BYTES );
+            *(UINT32*) &pState->buffer[ pHash->inputBlockSize - 4 ] |= hashPaddingFinal & m32ResultBlock;
+
+            (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, &pState->buffer[0], pHash->inputBlockSize, &tmp );
+            SYMCRYPT_ASSERT( tmp == 0 );
+
+            mResultBlock = (NATIVE_UINT)(NATIVE_INT)(INT32) m32ResultBlock;       // Convert 32-bit mask to native mask
+
+            // Masked copy of result to result buffer
+            // We do whole words, and then an optional UINT32 to handle the 20-byte SHA-1 result on AMD64.
+            // The for() and if() are side-channel safe as the resultSize and NATIVE_BYTES values are public.
+            for ( i = 0; i < pHash->resultSize / NATIVE_BYTES; i++)
+            {
+                ((NATIVE_UINT *)pbHashResult)[i] |= ((NATIVE_UINT *)((PBYTE)pState + pHash->chainOffset))[i] & mResultBlock;
+            }
+            if( (pHash->resultSize & (NATIVE_BYTES - 1)) != 0 )
+            {
+                *(UINT32 *) (&pbHashResult[ pHash->resultSize - 4 ]) |= *(UINT32 *) ((PBYTE) pState + pHash->chainOffset + pHash->resultSize - 4) & (UINT32) mResultBlock;
+            }
+            pState->bytesInBuffer = 0;
+        }
+
+        *(NATIVE_UINT *)&pbMacValue[next & (cbMacValue - 1)] |= w & mInMac;
+
+        nPaddingError |= (w ^ padBytes) & mInPadding;
+
+        next += NATIVE_BYTES;
+    }
+
+    if( next < cbExtendedData )
+    {
+        // Process the remaining bytes. This can't be data so we only do the MAC and padding...
+        // The main difference is that we read the last full word in pbData and then align it
+        // as if we read the next word starting at pbData[next - cbHashPrefix]
+        w = *(NATIVE_UINT *) &pbData[ cbData - NATIVE_BYTES ];      // last word
+        w >>= 8 * (next - cbExtendedData + NATIVE_BYTES );          // Shift to right location
+        padBytes >>= 8 * (next - cbExtendedData + NATIVE_BYTES );   // Zero padBytes that are never read
+
+        m = SymCryptNMaskGe( next, iPaddingStart );
+        mInPadding = m;
+        mInMac = ~m;
+
+        *(NATIVE_UINT *)&pbMacValue[next & (cbMacValue - 1)] |= w & mInMac;
+
+        nPaddingError |= (w ^ padBytes) & mInPadding;
+        next = cbExtendedData;
+    }
+
+    // At this point we still have to potentially do one more hash block.
+    // The data is all copied into the hash input buffer, as is the 0x80 padding byte.
+
+    if (next < lastHashBlockIndex)
+    {
+        // there is still one more hash block to compute. This could either be the actual last block of the hash
+        // computation, or a phantom block for side-channel hiding.
+        // This IF depends only on the cbData, the # bytes hashed before this final pbData buffer, and the hash algorithm
+        // properties.
+        // We never need to compute more than 1 additional hash block as we are at least pHash->resultSize bytes beyond the
+        // actual data.
+        SymCryptWipe( &pState->buffer[ pState->bytesInBuffer], pHash->inputBlockSize - pState->bytesInBuffer );
+
+        // Just put in the padding, no need to mask this
+        *(UINT32*) &pState->buffer[ pHash->inputBlockSize - 4 ] = hashPaddingFinal;
+
+        (*pHash->appendBlockFunc)( (PBYTE)pState + pHash->chainOffset, &pState->buffer[0], pHash->inputBlockSize, &tmp );
+        SYMCRYPT_ASSERT( tmp == 0 );
+
+        // Masked copy of the result
+        mResultBlock = (NATIVE_UINT)(NATIVE_INT)(INT32) MASK32_EQ( lastHashBlockIndex, resultHashBlockIndex );
+
+        // Masked copy of result to result buffer
+        // We do whole words, and then an optional UINT32 to handle the 20-byte SHA-1 result on AMD64.
+        for ( i = 0; i < pHash->resultSize / NATIVE_BYTES; i++)
+        {
+            ((NATIVE_UINT *)pbHashResult)[i] |= ((NATIVE_UINT *)((PBYTE)pState + pHash->chainOffset))[i] & mResultBlock;
+        }
+        if( (pHash->resultSize & (NATIVE_BYTES - 1)) != 0 )
+        {
+            *(UINT32 *) (&pbHashResult[ pHash->resultSize - 4 ]) |= *(UINT32 *) ((PBYTE) pState + pHash->chainOffset + pHash->resultSize - 4) & (UINT32) mResultBlock;
+        }
+        pState->bytesInBuffer = 0;
+    }
+
+    // Now we have the hash result, and the Mac value buffer is filled with a rotated copy of the Mac value.
+    // We have to un-rotate the Mac value.
+
+    // Check that we have the right hash result
+    //for( SIZE_T t=0; t < cbMacValue; t++ )
+    //{
+    //    SYMCRYPT_ASSERT( pbMacValue[ (iMacStart + t) & (cbMacValue - 1 ) ] == (t >= pHash->resultSize ? 0 : pbData[iMacStart - cbHashPrefix + t] ));
+    //}
+
+    SymCryptScsRotateBuffer(  pbMacValue, cbMacValue, iMacStart & (cbMacValue - 1) );
+
+    //for( SIZE_T t=0; t < cbMacValue; t++ )
+    //{
+    //    SYMCRYPT_ASSERT( pbMacValue[ t ] == (t >= pHash->resultSize ? 0 : pbData[iMacStart - cbHashPrefix + t] ));
+    //}
+
+cleanup:
+
+    nPaddingError |= nPaddingError >> (NATIVE_BITS/2);      // Map possibly 64 bits down to 32
+
+    *pu32PaddingError = (UINT32) nPaddingError;
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsCbcHmacVerify(
+    _In_                PCSYMCRYPT_MAC  pMacAlgorithm,
+    _In_                PVOID           pExpandedKey,
+    _Inout_             PVOID           pState,
+    _In_reads_(cbData)  PCBYTE          pbData,
+                        SIZE_T          cbData)
+{
+    BYTE abMacValue[64];
+    BYTE abHashResult[48];
+    UINT32 u32PaddingError;
+    PSYMCRYPT_COMMON_HASH_STATE pHashState = (PSYMCRYPT_COMMON_HASH_STATE) pState;
+    PCSYMCRYPT_HASH pHashAlgorithm = *(pMacAlgorithm->ppHashAlgorithm);
+    UINT32 i;
+
+    SYMCRYPT_ASSERT(pMacAlgorithm == SymCryptHmacSha1Algorithm ||
+                    pMacAlgorithm == SymCryptHmacSha256Algorithm ||
+                    pMacAlgorithm == SymCryptHmacSha384Algorithm );
+    SYMCRYPT_ASSERT(((*(pMacAlgorithm->ppHashAlgorithm))->inputBlockSize)/2 <= 64);
+    SYMCRYPT_ASSERT((*(pMacAlgorithm->ppHashAlgorithm))->resultSize <= 48);
+
+    SymCryptTlsCbcHmacVerifyCore(
+        pHashAlgorithm,
+        pHashState,
+        pbData,
+        cbData,
+        abMacValue,
+        abHashResult,
+        &u32PaddingError );
+
+    // We have the hash value, convert it to a MAC value
+    // First we set up the chaining value
+    memcpy( ((PBYTE)pHashState + pHashAlgorithm->chainOffset),
+            (PBYTE)pExpandedKey + pMacAlgorithm->outerChainingStateOffset,
+            pHashAlgorithm->chainSize );
+    // Then copy the data & set the length
+    // The hash result wasn't BSWAPPED yet...
+    if( pMacAlgorithm->resultSize <= 32 )
+    {
+        SymCryptUint32ToMsbFirst( (UINT32 *) abHashResult, pHashState->buffer, pHashAlgorithm->resultSize / 4 );
+    } else {
+        SymCryptUint64ToMsbFirst( (UINT64 *) abHashResult, pHashState->buffer, pHashAlgorithm->resultSize / 8 );
+    }
+    pHashState->bytesInBuffer = pHashAlgorithm->resultSize;
+    pHashState->dataLengthL = pHashAlgorithm->resultSize + pHashAlgorithm->inputBlockSize;
+
+    (*pHashAlgorithm->resultFunc)( pHashState, abHashResult );
+
+    // Verify in 32-bit chunks to support SHA-1 without further problems
+    for( i=0; i<pHashAlgorithm->resultSize / 4; i++ )
+    {
+        u32PaddingError |= *(PUINT32)&abHashResult[4*i] ^ *(PUINT32)&abMacValue[4*i];
+    }
+
+    // We may reveal the final error-or-no-error as that will be visible anyway
+    return u32PaddingError == 0 ? SYMCRYPT_NO_ERROR : SYMCRYPT_AUTHENTICATION_FAILURE;
+}
diff --git a/libs/symcrypt/lib/tlsprf.c b/libs/symcrypt/lib/tlsprf.c
new file mode 100644
index 000000000000..4a3167ec65e6
--- /dev/null
+++ b/libs/symcrypt/lib/tlsprf.c
@@ -0,0 +1,569 @@
+//
+// tlsprf.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// This module contains the routines to implement the two PRF
+// functions for the TLS protocols 1.1 and 1.2. These are used in
+// the protocol's key derivation function.
+//
+//
+
+#include "precomp.h"
+
+//
+// TLS PRF Constants
+//
+#define SYMCRYPT_TLS_MAX_LABEL_AND_SEED_SIZE     (SYMCRYPT_TLS_MAX_LABEL_SIZE + SYMCRYPT_TLS_MAX_SEED_SIZE)
+
+// This **MUST** be a common multiple of MD5
+// output size and SHA1 output size.
+#define SYMCRYPT_TLS_1_1_CHUNK_SIZE              80
+
+//
+// SymCryptTlsPrf1_1ExpandKey is the key expansion function for versions 1.0
+// and 1.1 of the TLS protocol. It takes as inputs a pointer to the expanded TLSPRF1.1
+// key, and the key material in pbKey. Regarding the treatment of the key
+// material (the "secret"), the following is defined in RFCs 2246 and 4346:
+//
+//      TLS's PRF is created by splitting the secret into two halves and
+//      using one half to generate data with P_MD5 and the other half to
+//      generate data with P_SHA - 1, then exclusive - or'ing the outputs of
+//      these two expansion functions together.
+//
+//      S1 and S2 are the two halves of the secret and each is the same
+//      length. S1 is taken from the first half of the secret, S2 from the
+//      second half. Their length is created by rounding up the length of the
+//      overall secret divided by two; thus, if the original secret is an odd
+//      number of bytes long, the last byte of S1 will be the same as the
+//      first byte of S2.
+//
+//          L_S = length in bytes of secret;
+//          L_S1 = L_S2 = ceil(L_S / 2);
+//
+//      The secret is partitioned into two halves (with the possibility of
+//      one shared byte) as described above, S1 taking the first L_S1 bytes
+//      and S2 the last L_S2 bytes.
+//
+//  Note:   In pre-RS1 Windows if the length of the key material of each half
+//          exceeded HMAC_K_PADSIZE = 64, we truncated the key. This does not comply
+//          with RFC 2014 (HMAC). However, as of April 2016 several cipher suites
+//          used keys (pre-master secret) longer than 128 bytes. To achieve interop
+//          with servers complying to the RFC we use the entire key for the HMAC calculation.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1ExpandKey(
+    _Out_               PSYMCRYPT_TLSPRF1_1_EXPANDED_KEY    pExpandedKey,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey)
+{
+    SYMCRYPT_ERROR  scError = SYMCRYPT_NO_ERROR;
+
+    SIZE_T cbKeySize;
+    SIZE_T cbHalfSecret;
+    SIZE_T cbOdd;
+
+    // Calculating the two halves
+    cbHalfSecret = cbKey / 2;
+    cbOdd = cbKey % 2;
+    cbKeySize = cbHalfSecret + cbOdd;
+
+    //
+    //     The bytes of the key material are split as following:
+    //     cbOdd == 0   =>   cbKeySize == cbHalfSecret
+    //
+    //          ********************************************
+    //          <----cbHalfSecret----><----cbHalfSecret---->
+    //          <----cbKeySize-------><----cbKeySize------->
+    //
+    //
+    //     cbOdd == 1   =>   cbKeySize == cbHalfSecret + 1
+    //
+    //          **********************$**********************
+    //          <----cbHalfSecret----> <----cbHalfSecret---->
+    //          <----cbKeySize-------->
+    //                                <----cbKeySize-------->
+    //
+    //      Note that the middle byte of the key input might be
+    //      read twice (when the key length is odd). This violates
+    //      the standard rule that input data should only be read
+    //      once. In this case, we do this for the following reasons:
+    //      -   Avoiding the dual-read is difficult; we'd have to buffer
+    //          an arbitrary-size input, and SymCrypt avoids memory
+    //          allocations for symmetric algorithms.
+    //      -   The dual-reading of inputs is a problem when the
+    //          memory is double-mapped to a different (less trusted)
+    //          security context. (E.g. a kernel-mode operation on
+    //          memory that is also mapped into a user address space.)
+    //          This PRF is used by TLS in LSA where that situation
+    //          does not occur.
+    //      -   This is used for TLS 1.0 and TLS 1.1, both of which
+    //          are on the deprecation path.
+    //      -   In the dual-read attack, the input is typically provided
+    //          by the attacker, and then changed whilst the code is
+    //          accessing it. But if the attacker is providing the input,
+    //          she could just as well have provided an even-length key
+    //          input that provides full freedom for choosing both HMAC
+    //          keys; there is simply no reason to try and perform the
+    //          dual-read attack.
+    //      -   Even if the dual-read problem were to occur, it does not
+    //          seem to help an attacker in any way.
+
+    // MD5 Key Expansion
+    scError = SymCryptHmacMd5ExpandKey(&pExpandedKey->macMd5Key, pbKey, cbKeySize);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // SHA1 Key Expansion
+    scError = SymCryptHmacSha1ExpandKey(&pExpandedKey->macSha1Key, pbKey + cbHalfSecret, cbKeySize);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        SymCryptWipeKnownSize(&pExpandedKey->macMd5Key, sizeof(pExpandedKey->macMd5Key));
+
+        goto cleanup;
+    }
+
+cleanup:
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2ExpandKey(
+    _Out_               PSYMCRYPT_TLSPRF1_2_EXPANDED_KEY    pExpandedKey,
+    _In_                PCSYMCRYPT_MAC                      macAlgorithm,
+    _In_reads_(cbKey)   PCBYTE                              pbKey,
+                        SIZE_T                              cbKey )
+{
+    SYMCRYPT_ASSERT( macAlgorithm->expandedKeySize <= sizeof( pExpandedKey->macKey ) );
+
+    pExpandedKey->macAlg = macAlgorithm;
+    return macAlgorithm->expandKeyFunc( &pExpandedKey->macKey, pbKey, cbKey );
+}
+
+//
+// SymCryptTlsPrfMac uses the expanded key and hashes the concatenated
+// inputs pbAi and pbSeed. It is used by all the TLS versions per
+// RFCs 2246, 4346, and 5246.
+// Remark:
+//      - cbSeed can be 0 and pbSeed NULL.
+//      - pbResult should be of size at least pMacAlgorithm->resultSize
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptTlsPrfMac(
+    _In_                    PCSYMCRYPT_MAC              pMacAlgorithm,
+    _In_                    PCSYMCRYPT_MAC_EXPANDED_KEY pMacExpandedKey,
+    _In_reads_(cbAi)        PCBYTE                      pbAi,
+    _In_                    SIZE_T                      cbAi,
+    _In_reads_opt_(cbSeed)  PCBYTE                      pbSeed,
+    _In_                    SIZE_T                      cbSeed,
+    _Out_                   PBYTE                       pbResult)
+{
+    SYMCRYPT_MAC_STATE macState;
+
+    pMacAlgorithm->initFunc( &macState, pMacExpandedKey );
+    pMacAlgorithm->appendFunc(&macState, pbAi, cbAi);
+
+    if (cbSeed > 0)
+    {
+        pMacAlgorithm->appendFunc( &macState, pbSeed, cbSeed );
+    }
+
+    pMacAlgorithm->resultFunc( &macState, pbResult );
+
+    // No need to wipe the state. The resultFunc wipes it.
+}
+
+//
+// SymCryptTlsPrfPHash is defined in RFCs 2246, 4346,
+// and 5246 as follows:
+//
+//      First, we define a data expansion function, P_hash(secret, data)
+//      which uses a single hash function to expand a secret and seed into
+//      an arbitrary quantity of output:
+//
+//          P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+//                                 HMAC_hash(secret, A(2) + seed) +
+//                                 HMAC_hash(secret, A(3) + seed) + ...
+//
+//      Where + indicates concatenation.
+//      A() is defined as:
+//          A(0) = seed
+//          A(i) = HMAC_hash(secret, A(i-1))
+//
+
+VOID
+SYMCRYPT_CALL
+SymCryptTlsPrfPHash(
+    _In_                        PCSYMCRYPT_MAC              pMacAlgorithm,
+    _In_                        PCSYMCRYPT_MAC_EXPANDED_KEY pMacExpandedKey,
+    _In_reads_(cbSeed)          PCBYTE                      pbSeed,
+    _In_                        SIZE_T                      cbSeed,
+    _In_reads_opt_(cbAiIn)      PCBYTE                      pbAiIn,         // Buffer for the previous Ai (used in 1.1)
+    _In_                        SIZE_T                      cbAiIn,
+    _Out_writes_(cbResult)      PBYTE                       pbResult,
+                                SIZE_T                      cbResult,
+    _Out_writes_opt_(cbAiOut)   PBYTE                       pbAiOut,        // Buffer for the next Ai (only with AiIn)
+                                SIZE_T                      cbAiOut)
+{
+    SYMCRYPT_ALIGN BYTE    rbAi[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+    SYMCRYPT_ALIGN BYTE    rbPartialResult[SYMCRYPT_MAC_MAX_RESULT_SIZE];
+                   BYTE *  pbTmp = pbResult;
+
+    SIZE_T  cbMacResultSize = pMacAlgorithm->resultSize;
+    SIZE_T  cbBytesToWrite = cbResult;
+
+    if (cbAiIn == 0)
+    {
+        // Build A(1)
+        SymCryptTlsPrfMac(
+            pMacAlgorithm,
+            pMacExpandedKey,
+            pbSeed,         // This is A(0)
+            cbSeed,
+            NULL,           // No "seed" part for A(i)'s
+            0,
+            rbAi);
+    }
+    else
+    {
+        // Get the previous Ai
+        memcpy(rbAi, pbAiIn, SYMCRYPT_MIN(SYMCRYPT_MAC_MAX_RESULT_SIZE, cbAiIn));
+    }
+
+    while (cbBytesToWrite > 0)
+    {
+        // Build HMAC( secret, A(i) + seed)
+        SymCryptTlsPrfMac(
+            pMacAlgorithm,
+            pMacExpandedKey,
+            rbAi,          // this is A(i)
+            cbMacResultSize,
+            pbSeed,             // the "seed" part
+            cbSeed,
+            rbPartialResult);
+
+        // Store it in the output buffer
+        memcpy(pbTmp, rbPartialResult, SYMCRYPT_MIN(cbBytesToWrite, cbMacResultSize));
+
+        // Build A(i+1)
+        SymCryptTlsPrfMac(
+            pMacAlgorithm,
+            pMacExpandedKey,
+            rbAi,         // This is A(i)
+            cbMacResultSize,
+            NULL,              // No "seed" part for A(i)'s
+            0,
+            rbAi);
+
+        if (cbBytesToWrite <= cbMacResultSize)
+        {
+            break;
+        }
+
+        pbTmp += cbMacResultSize;
+        cbBytesToWrite -= cbMacResultSize;
+    }
+
+    // Store the next A(i) if needed
+    if (cbAiOut > 0)
+    {
+        memcpy(pbAiOut, rbAi, SYMCRYPT_MIN(cbAiOut,cbMacResultSize));
+    }
+
+    SymCryptWipeKnownSize(rbAi, sizeof(rbAi));
+    SymCryptWipeKnownSize(rbPartialResult, sizeof(rbPartialResult));
+}
+
+
+//
+// The following PRF is defined in RFC 2246 and 4346:
+//
+//      The PRF is then defined as the result of mixing the two pseudorandom
+//      streams by exclusive - or'ing them together.
+//
+//          PRF(secret, label, seed) =  P_MD5(S1, label + seed) XOR
+//                                      P_SHA-1(S2, label + seed);
+//
+// Remark: We will do the do the two P_hash computations in parallel
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1Derive(
+    _In_                    PCSYMCRYPT_TLSPRF1_1_EXPANDED_KEY   pExpandedKey,
+    _In_reads_opt_(cbLabel) PCBYTE                              pbLabel,
+    _In_                    SIZE_T                              cbLabel,
+    _In_reads_(cbSeed)      PCBYTE                              pbSeed,
+    _In_                    SIZE_T                              cbSeed,
+    _Out_writes_(cbResult)  PBYTE                               pbResult,
+                            SIZE_T                              cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_ALIGN BYTE    rbLabelAndSeed[SYMCRYPT_TLS_MAX_LABEL_AND_SEED_SIZE];
+                   SIZE_T  cbLabelAndSeed = 0;
+
+    SYMCRYPT_ALIGN BYTE    rbAiMd5[SYMCRYPT_HMAC_MD5_RESULT_SIZE];
+    SYMCRYPT_ALIGN BYTE    rbPartialResultMd5[SYMCRYPT_TLS_1_1_CHUNK_SIZE];
+
+    SYMCRYPT_ALIGN BYTE    rbAiSha1[SYMCRYPT_HMAC_SHA1_RESULT_SIZE];
+    SYMCRYPT_ALIGN BYTE    rbPartialResultSha1[SYMCRYPT_TLS_1_1_CHUNK_SIZE];
+
+                   BYTE *  pbTmp = pbResult;
+                   SIZE_T  cbBytesToWrite = cbResult;
+
+    // Size checks
+    if ((cbLabel > SYMCRYPT_TLS_MAX_LABEL_SIZE) || (cbSeed > SYMCRYPT_TLS_MAX_SEED_SIZE))
+    {
+        scError = SYMCRYPT_WRONG_DATA_SIZE;
+        goto cleanup;
+    }
+
+    // Concatenating the label and the seed
+    pbTmp = rbLabelAndSeed;
+    if( cbLabel > 0 )
+    {
+        memcpy(pbTmp, pbLabel, cbLabel);
+        pbTmp += cbLabel;
+    }
+    memcpy(pbTmp, pbSeed, cbSeed);
+    cbLabelAndSeed = cbLabel + cbSeed;
+
+    // Build A(1)'s
+    SymCryptTlsPrfMac(
+        SymCryptHmacMd5Algorithm,
+        (PCSYMCRYPT_MAC_EXPANDED_KEY)&pExpandedKey->macMd5Key,
+        rbLabelAndSeed,         // This is A(0)
+        cbLabelAndSeed,
+        NULL,                   // No "seed" part for A(i)'s
+        0,
+        rbAiMd5);
+
+    SymCryptTlsPrfMac(
+        SymCryptHmacSha1Algorithm,
+        (PCSYMCRYPT_MAC_EXPANDED_KEY)&pExpandedKey->macSha1Key,
+        rbLabelAndSeed,         // This is A(0)
+        cbLabelAndSeed,
+        NULL,                   // No "seed" part for A(i)'s
+        0,
+        rbAiSha1);
+
+    // Calculate the output
+    pbTmp = pbResult;
+    while (cbBytesToWrite > 0)
+    {
+        // Calculate the two P_Hashes up to SYMCRYPT_TLS_1_1_CHUNK_SIZE bytes
+
+        // P_MD5
+        SymCryptTlsPrfPHash(
+            SymCryptHmacMd5Algorithm,
+            (PCSYMCRYPT_MAC_EXPANDED_KEY)&pExpandedKey->macMd5Key,
+            rbLabelAndSeed,
+            cbLabelAndSeed,
+            rbAiMd5,
+            SYMCRYPT_HMAC_MD5_RESULT_SIZE,
+            rbPartialResultMd5,
+            SYMCRYPT_MIN(cbBytesToWrite, SYMCRYPT_TLS_1_1_CHUNK_SIZE),
+            rbAiMd5,
+            SYMCRYPT_HMAC_MD5_RESULT_SIZE);
+
+        // P_SHA1
+        SymCryptTlsPrfPHash(
+            SymCryptHmacSha1Algorithm,
+            (PCSYMCRYPT_MAC_EXPANDED_KEY)&pExpandedKey->macSha1Key,
+            rbLabelAndSeed,
+            cbLabelAndSeed,
+            rbAiSha1,
+            SYMCRYPT_HMAC_SHA1_RESULT_SIZE,
+            rbPartialResultSha1,
+            SYMCRYPT_MIN(cbBytesToWrite, SYMCRYPT_TLS_1_1_CHUNK_SIZE),
+            rbAiSha1,
+            SYMCRYPT_HMAC_SHA1_RESULT_SIZE);
+
+        // XOR the two into the output
+        SymCryptXorBytes(
+            rbPartialResultMd5,
+            rbPartialResultSha1,
+            pbTmp,
+            SYMCRYPT_MIN(cbBytesToWrite, SYMCRYPT_TLS_1_1_CHUNK_SIZE));
+
+        if (cbBytesToWrite <= SYMCRYPT_TLS_1_1_CHUNK_SIZE)
+        {
+            break;
+        }
+
+        cbBytesToWrite -= SYMCRYPT_TLS_1_1_CHUNK_SIZE;
+        pbTmp += SYMCRYPT_TLS_1_1_CHUNK_SIZE;
+
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(rbLabelAndSeed, sizeof(rbLabelAndSeed));
+    SymCryptWipeKnownSize(rbAiMd5, sizeof(rbAiMd5));
+    SymCryptWipeKnownSize(rbPartialResultMd5, sizeof(rbPartialResultMd5));
+    SymCryptWipeKnownSize(rbAiSha1, sizeof(rbAiSha1));
+    SymCryptWipeKnownSize(rbPartialResultSha1, sizeof(rbPartialResultSha1));
+
+    return scError;
+}
+
+//
+// The following PRF is defined in RFC 5246:
+//
+//      TLS's PRF is created by applying P_hash to the secret as:
+//
+//          PRF(secret, label, seed) = P_<hash>(secret, label + seed)
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2Derive(
+    _In_                    PCSYMCRYPT_TLSPRF1_2_EXPANDED_KEY   pExpandedKey,
+    _In_reads_opt_(cbLabel) PCBYTE                              pbLabel,
+    _In_                    SIZE_T                              cbLabel,
+    _In_reads_(cbSeed)      PCBYTE                              pbSeed,
+    _In_                    SIZE_T                              cbSeed,
+    _Out_writes_(cbResult)  PBYTE                               pbResult,
+                            SIZE_T                              cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_ALIGN BYTE    rbLabelAndSeed[SYMCRYPT_TLS_MAX_LABEL_AND_SEED_SIZE];
+                   BYTE *  pbTmp;
+
+    // Size checks
+    if ((cbLabel > SYMCRYPT_TLS_MAX_LABEL_SIZE) || (cbSeed > SYMCRYPT_TLS_MAX_SEED_SIZE))
+    {
+        scError = SYMCRYPT_WRONG_DATA_SIZE;
+        goto cleanup;
+    }
+
+    // Concatenating the label and the seed
+    pbTmp = rbLabelAndSeed;
+    if( cbLabel > 0 )
+    {
+        memcpy(pbTmp, pbLabel, cbLabel);
+        pbTmp += cbLabel;
+    }
+    memcpy(pbTmp, pbSeed, cbSeed);
+
+    //
+    // According to RFC 2104 (HMAC),  hash the secret if its length
+    // exceeds the basic compression block length. This is taken
+    // care by the specific HMAC inside SymCryptTlsPrfPHash.
+    //
+    SymCryptTlsPrfPHash(
+        pExpandedKey->macAlg,
+        &pExpandedKey->macKey,
+        rbLabelAndSeed,
+        cbLabel + cbSeed,
+        NULL,
+        0,
+        pbResult,
+        cbResult,
+        NULL,
+        0);
+
+cleanup:
+    SymCryptWipeKnownSize(rbLabelAndSeed, sizeof(rbLabelAndSeed));
+
+    return scError;
+}
+
+//
+// The full TLS 1.0/1.1 Key Derivation Function
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_1(
+    _In_reads_(cbKey)       PCBYTE   pbKey,
+    _In_                    SIZE_T   cbKey,
+    _In_reads_opt_(cbLabel) PCBYTE   pbLabel,
+    _In_                    SIZE_T   cbLabel,
+    _In_reads_(cbSeed)      PCBYTE   pbSeed,
+    _In_                    SIZE_T   cbSeed,
+    _Out_writes_(cbResult)  PBYTE    pbResult,
+                            SIZE_T   cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_TLSPRF1_1_EXPANDED_KEY key;
+
+    // Create the expanded key
+    scError = SymCryptTlsPrf1_1ExpandKey(&key, pbKey, cbKey);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Derive the key
+    scError = SymCryptTlsPrf1_1Derive(
+        &key,
+        pbLabel,
+        cbLabel,
+        pbSeed,
+        cbSeed,
+        pbResult,
+        cbResult);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(&key, sizeof(key));
+
+    return scError;
+}
+
+
+//
+// The full TLS 1.2 Key Derivation Function
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptTlsPrf1_2(
+    _In_                    PCSYMCRYPT_MAC  pMacAlgorithm,
+    _In_reads_(cbKey)       PCBYTE          pbKey,
+    _In_                    SIZE_T          cbKey,
+    _In_reads_opt_(cbLabel) PCBYTE          pbLabel,
+    _In_                    SIZE_T          cbLabel,
+    _In_reads_(cbSeed)      PCBYTE          pbSeed,
+    _In_                    SIZE_T          cbSeed,
+    _Out_writes_(cbResult)  PBYTE           pbResult,
+                            SIZE_T          cbResult)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SYMCRYPT_TLSPRF1_2_EXPANDED_KEY key;
+
+    // Create the expanded key
+    scError = SymCryptTlsPrf1_2ExpandKey(&key, pMacAlgorithm, pbKey, cbKey);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Derive the key
+    scError = SymCryptTlsPrf1_2Derive(
+        &key,
+        pbLabel,
+        cbLabel,
+        pbSeed,
+        cbSeed,
+        pbResult,
+        cbResult);
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+cleanup:
+    SymCryptWipeKnownSize(&key, sizeof(key));
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/xmss.c b/libs/symcrypt/lib/xmss.c
new file mode 100644
index 000000000000..1bd9b116c969
--- /dev/null
+++ b/libs/symcrypt/lib/xmss.c
@@ -0,0 +1,2129 @@
+//
+// xmss.c XMSS and XMSS^MT implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+//
+// See the symcrypt.h file for documentation on what the various functions do.
+//
+
+
+// Maximum size of the domain separator prefix used in PRFs
+#define SYMCRYPT_XMSS_MAX_PREFIX_SIZE       SYMCRYPT_HASH_MAX_RESULT_SIZE
+
+// PRF domain separators
+#define SYMCRYPT_XMSS_F             0x00
+#define SYMCRYPT_XMSS_H             0x01
+#define SYMCRYPT_XMSS_H_MSG         0x02
+#define SYMCRYPT_XMSS_PRF           0x03
+#define SYMCRYPT_XMSS_PRF_KEYGEN    0x04
+
+
+static const PCSYMCRYPT_HASH* XmssHashArray[] = {
+    &SymCryptSha256Algorithm,       //  0
+    &SymCryptSha512Algorithm,       //  1
+    &SymCryptShake128HashAlgorithm, //  2
+    &SymCryptShake256HashAlgorithm, //  3
+};
+
+
+typedef enum _SYMCRYPT_XMSS_WOTSP_ALGID
+{
+    //                                                  Hash Fn.    RFC-8391    SP800-208
+    SYMCRYPT_XMSS_WOTSP_SHA2_256        = 0x00000001,   // SHA-256      X           X
+    SYMCRYPT_XMSS_WOTSP_SHA2_512        = 0x00000002,   // SHA-512      X
+    SYMCRYPT_XMSS_WOTSP_SHAKE_256       = 0x00000003,   // SHAKE128     X
+    SYMCRYPT_XMSS_WOTSP_SHAKE_512       = 0x00000004,   // SHAKE256     X
+    SYMCRYPT_XMSS_WOTSP_SHA2_192        = 0x00000005,   // SHA-256                  X
+    SYMCRYPT_XMSS_WOTSP_SHAKE256_256    = 0x00000006,   // SHAKE256                 X
+    SYMCRYPT_XMSS_WOTSP_SHAKE256_192    = 0x00000007,   // SHAKE256                 X
+
+} SYMCRYPT_XMSS_WOTSP_ALGID, *PSYMCRYPT_XMSS_WOTSP_ALGID;
+
+
+typedef struct _SYMCRYPT_XMSS_WOTSP_PARAMS
+{
+    SYMCRYPT_XMSS_WOTSP_ALGID   wotspId;
+    UINT8                       hashIndex;
+    UINT8                       n;
+    UINT8                       w;
+    UINT8                       cbPrefix;
+
+} SYMCRYPT_XMSS_WOTSP_PARAMS, *PSYMCRYPT_XMSS_WOTSP_PARAMS;
+
+
+static const SYMCRYPT_XMSS_WOTSP_PARAMS XmssWotspParams[] =
+{
+    //  wotspId                             hashIndex   n       w   cbPrefix
+    {   SYMCRYPT_XMSS_WOTSP_SHA2_256,       0,          32,     4,  32  },  // SHA-256
+    {   SYMCRYPT_XMSS_WOTSP_SHA2_512,       1,          64,     4,  64  },  // SHA-512
+    {   SYMCRYPT_XMSS_WOTSP_SHAKE_256,      2,          32,     4,  32  },  // SHAKE128
+    {   SYMCRYPT_XMSS_WOTSP_SHAKE_512,      3,          64,     4,  64  },  // SHAKE256
+    {   SYMCRYPT_XMSS_WOTSP_SHA2_192,       0,          24,     4,  4   },  // SHA-256
+    {   SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   3,          32,     4,  32  },  // SHAKE256
+    {   SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   3,          24,     4,  4   },  // SHAKE256
+};
+
+typedef struct _SYMCRYPT_XMSS_PARAMETER_PREDEFINED
+{
+    UINT32                      idAlg;
+
+    SYMCRYPT_XMSS_WOTSP_ALGID   idWotsp;
+
+    // total tree height (each level has height h/d)
+    UINT8                       h;
+
+    // number of layers (for single tree, d=1)
+    UINT8                       d;
+
+} SYMCRYPT_XMSS_PARAMETER_PREDEFINED;
+
+typedef SYMCRYPT_XMSS_PARAMETER_PREDEFINED* PSYMCRYPT_XMSS_PARAMETER_PREDEFINED;
+typedef const SYMCRYPT_XMSS_PARAMETER_PREDEFINED* PCSYMCRYPT_XMSS_PARAMETER_PREDEFINED;
+
+
+static const SYMCRYPT_XMSS_PARAMETER_PREDEFINED XmssParametersPredefined[] = {
+
+    //  algId                               wotspId/wotspIndex                  h   d
+    {   SYMCRYPT_XMSS_SHA2_10_256,          SYMCRYPT_XMSS_WOTSP_SHA2_256,       10, 1   },
+    {   SYMCRYPT_XMSS_SHA2_16_256,          SYMCRYPT_XMSS_WOTSP_SHA2_256,       16, 1   },
+    {   SYMCRYPT_XMSS_SHA2_20_256,          SYMCRYPT_XMSS_WOTSP_SHA2_256,       20, 1   },
+    {   SYMCRYPT_XMSS_SHA2_10_512,          SYMCRYPT_XMSS_WOTSP_SHA2_512,       10, 1   },
+    {   SYMCRYPT_XMSS_SHA2_16_512,          SYMCRYPT_XMSS_WOTSP_SHA2_512,       16, 1   },
+    {   SYMCRYPT_XMSS_SHA2_20_512,          SYMCRYPT_XMSS_WOTSP_SHA2_512,       20, 1   },
+    {   SYMCRYPT_XMSS_SHAKE_10_256,         SYMCRYPT_XMSS_WOTSP_SHAKE_256,      10, 1   },
+    {   SYMCRYPT_XMSS_SHAKE_16_256,         SYMCRYPT_XMSS_WOTSP_SHAKE_256,      16, 1   },
+    {   SYMCRYPT_XMSS_SHAKE_20_256,         SYMCRYPT_XMSS_WOTSP_SHAKE_256,      20, 1   },
+    {   SYMCRYPT_XMSS_SHAKE_10_512,         SYMCRYPT_XMSS_WOTSP_SHAKE_512,      10, 1   },
+    {   SYMCRYPT_XMSS_SHAKE_16_512,         SYMCRYPT_XMSS_WOTSP_SHAKE_512,      16, 1   },
+    {   SYMCRYPT_XMSS_SHAKE_20_512,         SYMCRYPT_XMSS_WOTSP_SHAKE_512,      20, 1   },
+    {   SYMCRYPT_XMSS_SHA2_10_192,          SYMCRYPT_XMSS_WOTSP_SHA2_192,       10, 1   },
+    {   SYMCRYPT_XMSS_SHA2_16_192,          SYMCRYPT_XMSS_WOTSP_SHA2_192,       16, 1   },
+    {   SYMCRYPT_XMSS_SHA2_20_192,          SYMCRYPT_XMSS_WOTSP_SHA2_192,       20, 1   },
+    {   SYMCRYPT_XMSS_SHAKE256_10_256,      SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   10, 1   },
+    {   SYMCRYPT_XMSS_SHAKE256_16_256,      SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   16, 1   },
+    {   SYMCRYPT_XMSS_SHAKE256_20_256,      SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   20, 1   },
+    {   SYMCRYPT_XMSS_SHAKE256_10_192,      SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   10, 1   },
+    {   SYMCRYPT_XMSS_SHAKE256_16_192,      SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   16, 1   },
+    {   SYMCRYPT_XMSS_SHAKE256_20_192,      SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   20, 1   },
+};
+
+
+static const SYMCRYPT_XMSS_PARAMETER_PREDEFINED XmssMtParametersPredefined[] = {
+
+    //  algId                               wotspId/wotspIndex                  h   d
+    {   SYMCRYPT_XMSSMT_SHA2_20_2_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       20, 2   },
+    {   SYMCRYPT_XMSSMT_SHA2_20_4_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       20, 4   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_2_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       40, 2   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_4_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       40, 4   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_8_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       40, 8   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_3_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       60, 3   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_6_256,      SYMCRYPT_XMSS_WOTSP_SHA2_256,       60, 6   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_12_256,     SYMCRYPT_XMSS_WOTSP_SHA2_256,       60, 12  },
+
+    {   SYMCRYPT_XMSSMT_SHA2_20_2_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       20, 2   },
+    {   SYMCRYPT_XMSSMT_SHA2_20_4_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       20, 4   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_2_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       40, 2   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_4_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       40, 4   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_8_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       40, 8   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_3_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       60, 3   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_6_512,      SYMCRYPT_XMSS_WOTSP_SHA2_512,       60, 6   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_12_512,     SYMCRYPT_XMSS_WOTSP_SHA2_512,       60, 12  },
+
+    {   SYMCRYPT_XMSSMT_SHAKE_20_2_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      20, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE_20_4_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      20, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE_40_2_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      40, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE_40_4_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      40, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE_40_8_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      40, 8   },
+    {   SYMCRYPT_XMSSMT_SHAKE_60_3_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      60, 3   },
+    {   SYMCRYPT_XMSSMT_SHAKE_60_6_256,     SYMCRYPT_XMSS_WOTSP_SHAKE_256,      60, 6   },
+    {   SYMCRYPT_XMSSMT_SHAKE_60_12_256,    SYMCRYPT_XMSS_WOTSP_SHAKE_256,      60, 12  },
+
+    {   SYMCRYPT_XMSSMT_SHAKE_20_2_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      20, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE_20_4_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      20, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE_40_2_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      40, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE_40_4_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      40, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE_40_8_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      40, 8   },
+    {   SYMCRYPT_XMSSMT_SHAKE_60_3_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      60, 3   },
+    {   SYMCRYPT_XMSSMT_SHAKE_60_6_512,     SYMCRYPT_XMSS_WOTSP_SHAKE_512,      60, 6   },
+    {   SYMCRYPT_XMSSMT_SHAKE_60_12_512,    SYMCRYPT_XMSS_WOTSP_SHAKE_512,      60, 12  },
+
+    {   SYMCRYPT_XMSSMT_SHA2_20_2_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       20, 2   },
+    {   SYMCRYPT_XMSSMT_SHA2_20_4_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       20, 4   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_2_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       40, 2   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_4_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       40, 4   },
+    {   SYMCRYPT_XMSSMT_SHA2_40_8_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       40, 8   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_3_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       60, 3   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_6_192,      SYMCRYPT_XMSS_WOTSP_SHA2_192,       60, 6   },
+    {   SYMCRYPT_XMSSMT_SHA2_60_12_192,     SYMCRYPT_XMSS_WOTSP_SHA2_192,       60, 12  },
+
+    {   SYMCRYPT_XMSSMT_SHAKE256_20_2_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   20, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_20_4_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   20, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_40_2_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   40, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_40_4_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   40, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_40_8_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   40, 8   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_60_3_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   60, 3   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_60_6_256,  SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   60, 6   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_60_12_256, SYMCRYPT_XMSS_WOTSP_SHAKE256_256,   60, 12  },
+
+    {   SYMCRYPT_XMSSMT_SHAKE256_20_2_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   20, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_20_4_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   20, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_40_2_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   40, 2   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_40_4_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   40, 4   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_40_8_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   40, 8   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_60_3_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   60, 3   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_60_6_192,  SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   60, 6   },
+    {   SYMCRYPT_XMSSMT_SHAKE256_60_12_192, SYMCRYPT_XMSS_WOTSP_SHAKE256_192,   60, 12  },
+};
+
+//
+// Compute the number of chains for an n-byte input and its checksum
+// for Winternitz parameter w (i.e., using w-bit blocks) in an OTS scheme
+//
+VOID
+SYMCRYPT_CALL
+SymCryptHbsGetWinternitzLengths(
+            UINT32  n,
+            UINT32  w,
+    _Out_   PUINT32 puLen1,
+    _Out_   PUINT32 puLen2
+    )
+{
+    UINT32  len1;
+    UINT32  len2;
+    UINT32  maxChecksum;
+    UINT32  msb;
+
+    SYMCRYPT_ASSERT(n > 0);
+    SYMCRYPT_ASSERT(w >= 1 && w <= 8);
+
+    // number of w-bit digits in an n-byte input
+    len1 = (8 * n + (w - 1)) / w;
+
+    // maximum value the checksum can take (each w-bit digit can have value at most 2^w-1)
+    maxChecksum = len1 * ((1 << w) - 1);
+
+    msb = 31 - SymCryptCountLeadingZeros32(maxChecksum);
+
+    // msb + 1 bits are required to store the maxChecksum,
+    // calculate the number of w-bit blocks to represent that
+    len2 = (msb + 1 + (w - 1)) / w;
+
+    *puLen1 = len1;
+    *puLen2 = len2;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssGetWotspParams(
+            SYMCRYPT_XMSS_WOTSP_ALGID   id,
+    _Out_   PSYMCRYPT_XMSS_PARAMS       pParams )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    for (UINT32 i = 0; i < SYMCRYPT_ARRAY_SIZE(XmssWotspParams); i++)
+    {
+        if (XmssWotspParams[i].wotspId == id)
+        {
+            SYMCRYPT_ASSERT(XmssWotspParams[i].hashIndex < SYMCRYPT_ARRAY_SIZE(XmssHashArray));
+            pParams->hash = *XmssHashArray[XmssWotspParams[i].hashIndex];
+            pParams->cbHashOutput = XmssWotspParams[i].n;
+            pParams->nWinternitzWidth = XmssWotspParams[i].w;
+            pParams->cbPrefix = XmssWotspParams[i].cbPrefix;
+            goto cleanup;
+        }
+    }
+
+    scError = SYMCRYPT_INVALID_ARGUMENT;
+
+cleanup:
+
+    return scError;
+}
+
+//
+// Derive XMSS parameters that can be computed from others
+//
+// SYMCRYPT_XMSS_PARAMS structure must be initialized with either predefined
+// or user defined parameters before this function is called.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptXmssDeriveParams(
+    _Inout_ PSYMCRYPT_XMSS_PARAMS pParams )
+{
+    SymCryptHbsGetWinternitzLengths(
+        pParams->cbHashOutput,
+        pParams->nWinternitzWidth,
+        &pParams->len1,
+        &pParams->len2);
+
+    pParams->len = pParams->len1 + pParams->len2;
+
+    UINT32 nChecksumBits = pParams->len2 * pParams->nWinternitzWidth;
+    SYMCRYPT_ASSERT(nChecksumBits <= 32);
+    pParams->nLeftShift32 = (UINT8)(32 - nChecksumBits);
+
+    if (pParams->nLayers == 1)
+    {
+        // single trees have a 32-bit Idx value
+        pParams->cbIdx = 4;
+    }
+    else
+    {
+        // number of bytes to store h-bits for Idx
+        pParams->cbIdx = (pParams->nTotalTreeHeight + 7) / 8;
+    }
+
+    pParams->nLayerHeight = pParams->nTotalTreeHeight / pParams->nLayers;
+}
+
+
+//
+//  Fill a SYMCRYPT_XMSS_PARAMS structure from either an XMSS algorithm ID or
+//  XMSS^MT algorithm ID from predefined parameter sets.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssParamsFromAlgIdCommon(
+            UINT32                  id,
+            BOOLEAN                 isMultiTree,
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_INVALID_ARGUMENT;
+    PCSYMCRYPT_XMSS_PARAMETER_PREDEFINED pParameters = NULL;
+    SIZE_T uParameterCount;
+
+    SymCryptWipeKnownSize(pParams, sizeof(*pParams));
+
+    if (isMultiTree)
+    {
+        pParameters = XmssMtParametersPredefined;
+        uParameterCount = SYMCRYPT_ARRAY_SIZE(XmssMtParametersPredefined);
+    }
+    else
+    {
+        pParameters = XmssParametersPredefined;
+        uParameterCount = SYMCRYPT_ARRAY_SIZE(XmssParametersPredefined);
+    }
+
+    for (UINT32 i = 0; i < uParameterCount; i++)
+    {
+        if (pParameters[i].idAlg == id)
+        {
+            scError = SymCryptXmssGetWotspParams(pParameters[i].idWotsp, pParams);
+
+            if (scError == SYMCRYPT_NO_ERROR)
+            {
+                SYMCRYPT_ASSERT(pParams->cbHashOutput <= SYMCRYPT_HASH_MAX_RESULT_SIZE);
+
+                pParams->id = id;
+                pParams->nTotalTreeHeight = pParameters[i].h;
+                pParams->nLayers = pParameters[i].d;
+                SymCryptXmssDeriveParams(pParams);
+            }
+
+            break;
+        }
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssParamsFromAlgId(
+            SYMCRYPT_XMSS_ALGID     id,
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams )
+{
+    return SymCryptXmssParamsFromAlgIdCommon(id, FALSE, pParams);
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssMtParamsFromAlgId(
+            SYMCRYPT_XMSSMT_ALGID   id,
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams)
+{
+    return SymCryptXmssParamsFromAlgIdCommon(id, TRUE, pParams);
+}
+
+
+//
+//  Set custom XMSS/XMSS^MT parameters
+//
+//  This function can be used to initialize SYMCRYPT_XMSS_PARAMS with
+//  custom parameters that are not defined by the standards.
+//
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssSetParams(
+    _Out_   PSYMCRYPT_XMSS_PARAMS   pParams,
+            UINT32                  id,
+    _In_    PCSYMCRYPT_HASH         pHash,              // hash algorithm
+            UINT32                  cbHashOutput,       // hash output size
+            UINT32                  nWinternitzWidth,   // Winternitz parameter
+            UINT32                  nTotalTreeHeight,   // total tree height
+            UINT32                  nLayers,            // number of layers
+            UINT32                  cbPrefix            // domain separator prefix length
+    )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    if (pParams == NULL)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptWipeKnownSize(pParams, sizeof(*pParams));
+
+    if (pHash == NULL)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Output size n can at most be equal to the hash output size
+    if (cbHashOutput == 0 ||
+        cbHashOutput > pHash->resultSize ||
+        cbHashOutput > SYMCRYPT_HASH_MAX_RESULT_SIZE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Winternitz parameter must be one of 1, 2, 4, or 8
+    if (nWinternitzWidth == 0 ||
+        nWinternitzWidth > 8 ||
+        (nWinternitzWidth & (nWinternitzWidth - 1)) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // nTotalTreeHeight and nLayers must both be positive and
+    // nLayers must divide nTotalTreeHeight
+    if (nTotalTreeHeight == 0 ||
+        nLayers == 0 ||
+        (nTotalTreeHeight % nLayers) != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Layer height (tree height of one layer) can be at most 31
+    if ((nTotalTreeHeight / nLayers) > 31)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Total tree height can be at most 63
+    if (nTotalTreeHeight > 63)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    if (cbPrefix == 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    pParams->id = id;
+    pParams->hash = pHash;
+    pParams->cbHashOutput = cbHashOutput;
+    pParams->nWinternitzWidth = nWinternitzWidth;
+    pParams->nTotalTreeHeight = nTotalTreeHeight;
+    pParams->nLayers = nLayers;
+    SymCryptXmssDeriveParams(pParams);
+
+    pParams->cbPrefix = cbPrefix;
+
+cleanup:
+
+    return scError;
+}
+
+
+//
+//  Updates the type field in ADRS structure and clears the
+//  subsequent fields.
+//
+//  Does not modify the first two fields (Layer and Tree) of
+//  the ADRS structure.
+//
+VOID
+SYMCRYPT_CALL
+SymCryptXmssSetAdrsType(
+    _Out_   PXMSS_ADRS  adrs,
+            UINT32      type )
+{
+    SYMCRYPT_STORE_MSBFIRST32(adrs->en32Type, type);
+    SymCryptWipeKnownSize(&adrs->u, sizeof(adrs->u));
+    SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 0);
+}
+
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptXmssSizeofSignatureFromParams(
+    _In_ PCSYMCRYPT_XMSS_PARAMS pParams )
+{
+    SYMCRYPT_ASSERT(pParams->nLayers != 0);
+    SYMCRYPT_ASSERT((pParams->nTotalTreeHeight % pParams->nLayers) == 0);
+    SYMCRYPT_ASSERT(pParams->nLayerHeight > 0);
+
+    SIZE_T size = 0;
+    size += pParams->cbIdx;         // idx
+    size += pParams->cbHashOutput;  // randomness
+
+    // WOTSP signature + authentication path for each layer
+    size += pParams->nLayers * ( pParams->cbHashOutput * (pParams->len + pParams->nLayerHeight) );
+
+    return size;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssSizeofKeyBlobFromParams(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+            SYMCRYPT_XMSSKEY_TYPE   keyType,
+    _Out_   SIZE_T*                 pcbKey )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SIZE_T cbPublicKey = 0;
+    SIZE_T cbPrivateKey = 0;
+
+    // Public Key
+    cbPublicKey += sizeof(UINT32);             // Alg ID
+    cbPublicKey += 2 * pParams->cbHashOutput;  // Root and Seed
+
+    // Private Key (on top of the public key)
+    cbPrivateKey = cbPublicKey;
+    cbPrivateKey += sizeof(UINT64);             // Idx
+    cbPrivateKey += 2 * pParams->cbHashOutput;  // SK_XMSS and SK_PRF
+
+    switch (keyType)
+    {
+        case SYMCRYPT_XMSSKEY_TYPE_PUBLIC:
+            *pcbKey = cbPublicKey;
+            break;
+
+        case SYMCRYPT_XMSSKEY_TYPE_PRIVATE:
+            *pcbKey = cbPrivateKey;
+            break;
+
+        default:
+            scError = SYMCRYPT_INVALID_ARGUMENT;
+            break;
+    }
+
+    return scError;
+}
+
+PSYMCRYPT_XMSS_KEY
+SYMCRYPT_CALL
+SymCryptXmsskeyAllocate(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+            UINT32                  flags )
+{
+    PSYMCRYPT_XMSS_KEY pKey = NULL;
+
+    // No flags allowed
+    if (flags != 0)
+    {
+        goto cleanup;
+    }
+
+    SIZE_T cbSize = sizeof(SYMCRYPT_XMSS_KEY);
+
+    pKey = SymCryptCallbackAlloc(cbSize);
+
+    if (pKey == NULL)
+    {
+        goto cleanup;
+    }
+
+    SymCryptWipe(pKey, cbSize);
+    pKey->version = 1;
+    pKey->keyType = SYMCRYPT_XMSSKEY_TYPE_NONE;
+    pKey->params = *pParams;
+
+    SYMCRYPT_SET_MAGIC(pKey);
+
+ cleanup:
+
+    return pKey;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmsskeyFree(
+    _Inout_ PSYMCRYPT_XMSS_KEY pKey )
+{
+    SYMCRYPT_CHECK_MAGIC(pKey);
+    SymCryptWipeKnownSize(pKey, sizeof(*pKey));
+    SymCryptCallbackFree(pKey);
+}
+
+
+PSYMCRYPT_INCREMENTAL_TREEHASH
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashInit(
+    UINT32  nLeaves,
+    PBYTE   pbBuffer,
+    SIZE_T  cbBuffer,
+    UINT32  cbHashResult,
+    PSYMCRYPT_INCREMENTAL_TREEHASH_FUNC funcCompressNodes,
+    PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT pContext )
+{
+    UNREFERENCED_PARAMETER(cbBuffer);
+
+    SYMCRYPT_ASSERT(cbBuffer >= SymCryptHbsSizeofScratchBytesForIncrementalTreehash(cbHashResult, nLeaves));
+
+    PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash = (PSYMCRYPT_INCREMENTAL_TREEHASH)pbBuffer;
+
+    pIncHash->cbNode = 2 * sizeof(UINT32) + cbHashResult;
+    pIncHash->nSize = 0;
+    pIncHash->nCapacity = SymCryptHbsIncrementalTreehashStackDepth(nLeaves);
+    pIncHash->nLastLeafIndex = 0;
+    pIncHash->funcCompressNodes = funcCompressNodes;
+    pIncHash->pContext = pContext;
+
+    return pIncHash;
+}
+
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashGetNode(
+    _In_ PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash,
+         SIZE_T                         index )
+{
+    PBYTE pNode = (PBYTE)pIncHash->arrNodes;
+
+    pNode += index * pIncHash->cbNode;
+
+    return (PSYMCRYPT_TREEHASH_NODE)pNode;
+}
+
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashAllocNode(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash,
+            UINT32                          nLeafIndex )
+{
+    SYMCRYPT_ASSERT(pIncHash->nSize < pIncHash->nCapacity);
+
+    PSYMCRYPT_TREEHASH_NODE pNode = SymCryptHbsIncrementalTreehashGetNode(pIncHash, pIncHash->nSize);
+
+    pNode->height = 0;
+    pNode->index = nLeafIndex;
+
+    pIncHash->nSize++;
+
+    return pNode;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashGetTopNodes(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash,
+    _Out_   PSYMCRYPT_TREEHASH_NODE         *ppNodeLeft,
+    _Out_   PSYMCRYPT_TREEHASH_NODE         *ppNodeRight )
+{
+    *ppNodeRight = (pIncHash->nSize < 1) ? NULL : SymCryptHbsIncrementalTreehashGetNode(pIncHash, pIncHash->nSize - 1);
+
+    *ppNodeLeft = (pIncHash->nSize < 2) ? NULL : SymCryptHbsIncrementalTreehashGetNode(pIncHash, pIncHash->nSize - 2);
+}
+
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashProcessCommon(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash,
+            BOOLEAN                         fFinal )
+{
+    PSYMCRYPT_TREEHASH_NODE pNodeLeft = NULL;
+    PSYMCRYPT_TREEHASH_NODE pNodeRight = NULL;
+
+    SYMCRYPT_ASSERT(pIncHash->nSize > 0);
+
+    SymCryptHbsIncrementalTreehashGetTopNodes(pIncHash, &pNodeLeft, &pNodeRight);
+
+    while ( pNodeLeft &&
+            (fFinal || (pNodeLeft->height == pNodeRight->height)) )
+    {
+        pIncHash->funcCompressNodes(
+            pNodeLeft,
+            pNodeRight,
+            pNodeLeft,
+            pIncHash->pContext);
+
+        pIncHash->nSize--;
+
+        SymCryptHbsIncrementalTreehashGetTopNodes(pIncHash, &pNodeLeft, &pNodeRight);
+    }
+
+    return pNodeRight;
+}
+
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashProcess(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash)
+{
+    return SymCryptHbsIncrementalTreehashProcessCommon(pIncHash, FALSE);
+}
+
+
+PSYMCRYPT_TREEHASH_NODE
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashFinalize(
+    _Inout_ PSYMCRYPT_INCREMENTAL_TREEHASH  pIncHash )
+{
+    return SymCryptHbsIncrementalTreehashProcessCommon(pIncHash, TRUE);
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptHbsIncrementalTreehashStackDepth(
+    UINT32 nLeaves)
+{
+    UINT32 h;
+
+    // Minimum height binary tree that contains nLeaves many leaves is h+1
+    h = 31 - SymCryptCountLeadingZeros32(nLeaves);
+
+    // Tree root computation will require a stack of depth equal to tree height plus 1
+    return (h + 2);
+}
+
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptHbsSizeofScratchBytesForIncrementalTreehash(
+    UINT32  cbNode,
+    UINT32  nLeaves)
+{
+    SIZE_T nodeSize = cbNode + 2 * sizeof(UINT32);
+    SIZE_T result = (sizeof(SYMCRYPT_INCREMENTAL_TREEHASH) - sizeof(SYMCRYPT_TREEHASH_NODE));
+
+    result += nodeSize * SymCryptHbsIncrementalTreehashStackDepth(nLeaves);
+    return result;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssPrfInit(
+    _In_    PCSYMCRYPT_HASH         hash,
+            BYTE                    PrfType,
+            SIZE_T                  prefixLength,
+    _Out_   PSYMCRYPT_HASH_STATE    state )
+{
+    BYTE prefix[SYMCRYPT_XMSS_MAX_PREFIX_SIZE];
+
+    SYMCRYPT_ASSERT(prefixLength <= SYMCRYPT_XMSS_MAX_PREFIX_SIZE);
+
+    SymCryptWipe(prefix, prefixLength);
+    prefix[prefixLength - 1] = PrfType;
+
+    SymCryptHashInit(hash, state);
+    SymCryptHashAppend(hash, state, prefix, prefixLength);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssPrfKey(
+    _In_                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_reads_bytes_( cbKey )   PCBYTE                  pbKey,
+                                SIZE_T                  cbKey,
+    _Out_                       SYMCRYPT_HASH_STATE     *pState )
+{
+    SymCryptXmssPrfInit(pParams->hash, SYMCRYPT_XMSS_PRF, pParams->cbPrefix, pState);
+    SymCryptHashAppend(pParams->hash, pState, pbKey, cbKey);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssPrf(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+                                                BYTE                    PrfType,
+    _In_reads_bytes_( cbKey )                   PCBYTE                  pbKey,
+                                                SIZE_T                  cbKey,
+    _In_reads_bytes_( cbMsg )                   PCBYTE                  pbMsg,
+                                                SIZE_T                  cbMsg,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbOutput )
+{
+    SYMCRYPT_HASH_STATE state;
+
+    SymCryptXmssPrfInit(pParams->hash, PrfType, pParams->cbPrefix, &state);
+    SymCryptHashAppend(pParams->hash, &state, pbKey, cbKey);
+    SymCryptHashAppend(pParams->hash, &state, pbMsg, cbMsg);
+    SymCryptHashResult(pParams->hash, &state, pbOutput, pParams->cbHashOutput);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssRandHash(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_                                     XMSS_ADRS              *adrs,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSeed,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbLeft,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbRight,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbOutput )
+{
+    BYTE key[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE bitmask[2 * SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    SYMCRYPT_HASH_STATE stateKeyed;
+    SYMCRYPT_HASH_STATE stateMask;
+
+    SYMCRYPT_ASSERT(pParams->cbHashOutput <= SYMCRYPT_HASH_MAX_RESULT_SIZE);
+
+    SymCryptXmssPrfKey(pParams, pbSeed, pParams->cbHashOutput, &stateKeyed);
+
+    SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 1);
+    SymCryptHashStateCopy(pParams->hash, &stateKeyed, &stateMask);
+    SymCryptHashAppend(pParams->hash, &stateMask, (PCBYTE)adrs, sizeof(*adrs));
+    SymCryptHashResult(pParams->hash, &stateMask, &bitmask[0], pParams->cbHashOutput);
+
+    SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 2);
+    SymCryptHashStateCopy(pParams->hash, &stateKeyed, &stateMask);
+    SymCryptHashAppend(pParams->hash, &stateMask, (PCBYTE)adrs, sizeof(*adrs));
+    SymCryptHashResult(pParams->hash, &stateMask, &bitmask[pParams->cbHashOutput], pParams->cbHashOutput);
+
+    SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 0);
+    SymCryptHashAppend(pParams->hash, &stateKeyed, (PCBYTE)adrs, sizeof(*adrs));
+    SymCryptHashResult(pParams->hash, &stateKeyed, key, pParams->cbHashOutput);
+
+    SymCryptXorBytes(&bitmask[0], pbLeft, &bitmask[0], pParams->cbHashOutput);
+    SymCryptXorBytes(&bitmask[pParams->cbHashOutput], pbRight, &bitmask[pParams->cbHashOutput], pParams->cbHashOutput);
+
+    SymCryptXmssPrf(pParams, SYMCRYPT_XMSS_H, key, pParams->cbHashOutput, bitmask, 2 * pParams->cbHashOutput, pbOutput);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssTreeNodeCompress(
+    _In_    PSYMCRYPT_TREEHASH_NODE                     pNodeLeft,
+    _In_    PSYMCRYPT_TREEHASH_NODE                     pNodeRight,
+    _Out_   PSYMCRYPT_TREEHASH_NODE                     pNodeOut,
+    _Inout_ PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT pCtxIncHash )
+{
+    SYMCRYPT_STORE_MSBFIRST32(pCtxIncHash->adrs.u.hashtree.en32Height, pNodeLeft->height);
+    SYMCRYPT_STORE_MSBFIRST32(pCtxIncHash->adrs.u.hashtree.en32Index, pNodeLeft->index / 2);
+
+    SymCryptXmssRandHash(
+        pCtxIncHash->pParams,
+        &pCtxIncHash->adrs,
+        pCtxIncHash->pbSeed,
+        pNodeLeft->value,
+        pNodeRight->value,
+        pNodeOut->value);
+
+    pNodeOut->index = pNodeLeft->index / 2;
+    pNodeOut->height = pNodeLeft->height + 1;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssLtreeNodeCompress(
+    _In_    PSYMCRYPT_TREEHASH_NODE                     pNodeLeft,
+    _In_    PSYMCRYPT_TREEHASH_NODE                     pNodeRight,
+    _Out_   PSYMCRYPT_TREEHASH_NODE                     pNodeOut,
+    _Inout_ PSYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT pCtxIncHash )
+{
+    SYMCRYPT_STORE_MSBFIRST32(pCtxIncHash->adrs.u.ltree.en32Height, pNodeLeft->height);
+    SYMCRYPT_STORE_MSBFIRST32(pCtxIncHash->adrs.u.ltree.en32Index, pNodeLeft->index / 2);
+
+    SymCryptXmssRandHash(
+        pCtxIncHash->pParams,
+        &pCtxIncHash->adrs,
+        pCtxIncHash->pbSeed,
+        pNodeLeft->value,
+        pNodeRight->value,
+        pNodeOut->value);
+
+    pNodeOut->index = pNodeLeft->index / 2;
+    pNodeOut->height = pNodeLeft->height + 1;
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssCreateWotspSecret(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSkXmss,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSeed,
+    _Inout_                                     XMSS_ADRS               *adrs,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbOutput )
+{
+    SYMCRYPT_HASH_STATE state;
+
+    SymCryptXmssPrfInit(pParams->hash, SYMCRYPT_XMSS_PRF_KEYGEN, pParams->cbPrefix, &state);
+    SymCryptHashAppend(pParams->hash, &state, pbSkXmss, pParams->cbHashOutput);
+    SymCryptHashAppend(pParams->hash, &state, pbSeed, pParams->cbHashOutput);
+    SymCryptHashAppend(pParams->hash, &state, (PCBYTE)adrs, sizeof(*adrs));
+    SymCryptHashResult(pParams->hash, &state, pbOutput, pParams->cbHashOutput);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssChain(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbInput,
+                                                UINT32                  startIndex,
+                                                UINT32                  steps,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSeed,
+    _Inout_                                     XMSS_ADRS              *adrs,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbOutput )
+{
+    BYTE tmp[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE key[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE bm[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    SYMCRYPT_HASH_STATE stateKey;
+    SYMCRYPT_HASH_STATE stateMask;
+
+    memcpy(tmp, pbInput, pParams->cbHashOutput);
+
+    for (UINT32 i = startIndex; i < startIndex + steps; i++)
+    {
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Hash, i);
+
+        SymCryptXmssPrfKey(pParams, pbSeed, pParams->cbHashOutput, &stateKey);
+        SymCryptHashStateCopy(pParams->hash, &stateKey, &stateMask);
+
+        SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 0);
+        SymCryptHashAppend(pParams->hash, &stateKey, (PCBYTE)adrs, sizeof(*adrs));
+        SymCryptHashResult(pParams->hash, &stateKey, key, pParams->cbHashOutput);
+
+        SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 1);
+        SymCryptHashAppend(pParams->hash, &stateMask, (PCBYTE)adrs, sizeof(*adrs));
+        SymCryptHashResult(pParams->hash, &stateMask, bm, pParams->cbHashOutput);
+
+        SymCryptXorBytes(tmp, bm, tmp, pParams->cbHashOutput);
+
+        SymCryptXmssPrf(pParams, SYMCRYPT_XMSS_F, key, pParams->cbHashOutput, tmp, pParams->cbHashOutput, tmp);
+    }
+
+    // reset used ADRS fields
+    SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Hash, 0);
+    SYMCRYPT_STORE_MSBFIRST32(adrs->en32KeyAndMask, 0);
+
+    memcpy(pbOutput, tmp, pParams->cbHashOutput);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssCreateWotspPublickey(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_                                     XMSS_ADRS               *adrs,
+                                                UINT32                  uLeaf,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSkXmss,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSeed,
+    _Out_writes_bytes_opt_( cbScratch )         PBYTE                   pbScratch,
+                                                SIZE_T                  cbScratch,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbOutput )
+{
+    PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash = NULL;
+    PSYMCRYPT_TREEHASH_NODE pNode = NULL;
+    SYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT ctxIncHash;
+
+    SYMCRYPT_ASSERT(cbScratch >= SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, pParams->len));
+
+    SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_LTREE);
+    SYMCRYPT_STORE_MSBFIRST32(adrs->u.ltree.en32Leaf, uLeaf);
+
+    ctxIncHash.adrs = *adrs;
+    ctxIncHash.pParams = pParams;
+    ctxIncHash.pbSeed = pbSeed;
+
+    pIncHash = SymCryptHbsIncrementalTreehashInit(
+                    pParams->len,
+                    pbScratch,
+                    cbScratch,
+                    pParams->cbHashOutput,
+                    SymCryptXmssLtreeNodeCompress,
+                    &ctxIncHash);
+
+    for (UINT32 i = 0; i < pParams->len; i++)
+    {
+        pNode = SymCryptHbsIncrementalTreehashAllocNode(pIncHash, i);
+
+        SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_OTS);
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Leaf, uLeaf);
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Chain, i);
+
+        SymCryptXmssCreateWotspSecret(
+            pParams,
+            pbSkXmss,
+            pbSeed,
+            adrs,
+            pNode->value);
+
+        SymCryptXmssChain(
+            pParams,
+            pNode->value,
+            0,
+            (1 << pParams->nWinternitzWidth) - 1,
+            pbSeed,
+            adrs,
+            pNode->value);
+
+        SymCryptHbsIncrementalTreehashProcess(pIncHash);
+
+    }
+
+    pNode = SymCryptHbsIncrementalTreehashFinalize(pIncHash);
+
+    memcpy(pbOutput, pNode->value, pParams->cbHashOutput);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssComputeSubtreeRoot(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_                                        XMSS_ADRS               *adrs,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSkXmss,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbSeed,
+                                                UINT32                  uLeaf,
+                                                UINT32                  uHeight,
+    _Out_writes_bytes_opt_( cbScratch )         PBYTE                   pbScratch,
+                                                SIZE_T                  cbScratch,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbRoot )
+{
+    UNREFERENCED_PARAMETER(cbScratch);
+
+    PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash = NULL;
+    PSYMCRYPT_TREEHASH_NODE pNode = NULL;
+    SYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT ctxIncHash;
+
+    SYMCRYPT_ASSERT((uLeaf & ((1UL << uHeight) - 1)) == 0); // uLeaf must be a multiple of 2^uHeight
+    SYMCRYPT_ASSERT(pParams->nLayerHeight < 32); // Ensure nLeaves fits in 32 bits
+
+    SIZE_T cbScratchTree = SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, 1ULL << pParams->nLayerHeight);
+    SIZE_T cbScratchLtree = SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, pParams->len);
+
+    SYMCRYPT_ASSERT(cbScratch >= (cbScratchTree + cbScratchLtree));
+
+    PBYTE pbScratchTree = pbScratch;
+    PBYTE pbScratchLtree = pbScratch + cbScratchTree;
+
+    SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_HASH_TREE);
+
+    ctxIncHash.adrs = *adrs;
+    ctxIncHash.pParams = pParams;
+    ctxIncHash.pbSeed = pbSeed;
+
+    pIncHash = SymCryptHbsIncrementalTreehashInit(
+        1ULL << uHeight,
+        pbScratchTree,
+        cbScratchTree,
+        pParams->cbHashOutput,
+        SymCryptXmssTreeNodeCompress,
+        &ctxIncHash);
+
+    for (UINT32 nLeafIndex = uLeaf; nLeafIndex < uLeaf + (1UL << uHeight); nLeafIndex++)
+    {
+        pNode = SymCryptHbsIncrementalTreehashAllocNode(pIncHash, nLeafIndex);
+
+        SymCryptXmssCreateWotspPublickey(pParams,
+                                    adrs,
+                                    nLeafIndex,
+                                    pbSkXmss,
+                                    pbSeed,
+                                    pbScratchLtree,
+                                    cbScratchLtree,
+                                    pNode->value );
+
+        SymCryptHbsIncrementalTreehashProcess(pIncHash);
+    }
+
+    pNode = SymCryptHbsIncrementalTreehashFinalize(pIncHash);
+
+    memcpy(pbRoot, pNode->value, pParams->cbHashOutput);
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssComputePublicRoot(
+    _In_                            PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_reads_bytes_( cbSeed )      PCBYTE                  pbSeed,
+                                    SIZE_T                  cbSeed,
+    _In_reads_bytes_( cbSkXmss )    PCBYTE                  pbSkXmss,
+                                    SIZE_T                  cbSkXmss,
+    _Out_writes_bytes_( cbRoot )    PBYTE                   pbRoot,
+                                    SIZE_T                  cbRoot )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    XMSS_ADRS adrs;
+
+    SYMCRYPT_ASSERT(pParams->nLayerHeight < 32); // Ensure nLeaves fits in 32 bits
+
+    if (pbRoot == NULL || cbRoot != pParams->cbHashOutput ||
+        pbSeed == NULL || cbSeed != pParams->cbHashOutput ||
+        pbSkXmss == NULL || cbSkXmss != pParams->cbHashOutput)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch += SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, 1ULL << pParams->nLayerHeight);
+    cbScratch += SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, pParams->len);
+
+    SYMCRYPT_ASSERT(cbScratch > 0);
+    pbScratch = SymCryptCallbackAlloc(cbScratch);
+
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    SymCryptWipeKnownSize(&adrs, sizeof(XMSS_ADRS));
+    SYMCRYPT_STORE_MSBFIRST32(adrs.en32Layer, pParams->nLayers - 1);
+
+    SymCryptXmssComputeSubtreeRoot(
+        pParams,
+        &adrs,
+        pbSkXmss,
+        pbSeed,
+        0,
+        pParams->nLayerHeight,
+        pbScratch,
+        cbScratch,
+        pbRoot );
+
+cleanup:
+
+    if (pbScratch != NULL)
+    {
+        SymCryptWipe(pbScratch, cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeyVerifyRoot(
+    _In_    PCSYMCRYPT_XMSS_KEY pKey)
+{
+    BYTE Root[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    // key to be verified has to be a private key
+    if (pKey->keyType != SYMCRYPT_XMSSKEY_TYPE_PRIVATE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptWipeKnownSize(Root, sizeof(Root));
+
+    scError = SymCryptXmssComputePublicRoot(
+        &pKey->params,
+        pKey->Seed,
+        pKey->params.cbHashOutput,
+        pKey->SkXmss,
+        pKey->params.cbHashOutput,
+        Root,
+        pKey->params.cbHashOutput);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    if (!SymCryptEqual(Root, pKey->Root, pKey->params.cbHashOutput))
+    {
+        scError = SYMCRYPT_HBS_PUBLIC_ROOT_MISMATCH;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeyGenerate(
+    _Inout_ PSYMCRYPT_XMSS_KEY  pKey,
+            UINT32              flags)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if (flags != 0)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Wipe key material
+    SymCryptWipeKnownSize(pKey->Root, sizeof(pKey->Root));
+    SymCryptWipeKnownSize(pKey->Seed, sizeof(pKey->Seed));
+    SymCryptWipeKnownSize(pKey->SkPrf, sizeof(pKey->SkPrf));
+    SymCryptWipeKnownSize(pKey->SkXmss, sizeof(pKey->SkXmss));
+    pKey->Idx = 0;
+
+    scError = SymCryptCallbackRandom(pKey->SkPrf, pKey->params.cbHashOutput);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptCallbackRandom(pKey->SkXmss, pKey->params.cbHashOutput);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    scError = SymCryptCallbackRandom(pKey->Seed, pKey->params.cbHashOutput);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    // Compute public root from the private key
+    scError = SymCryptXmssComputePublicRoot(
+        &pKey->params,
+        pKey->Seed,
+        pKey->params.cbHashOutput,
+        pKey->SkXmss,
+        pKey->params.cbHashOutput,
+        pKey->Root,
+        pKey->params.cbHashOutput);
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        goto cleanup;
+    }
+
+    pKey->keyType = SYMCRYPT_XMSSKEY_TYPE_PRIVATE;
+
+cleanup:
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        SymCryptWipeKnownSize(pKey->SkPrf, sizeof(pKey->SkPrf));
+        SymCryptWipeKnownSize(pKey->SkXmss, sizeof(pKey->SkXmss));
+        pKey->keyType = SYMCRYPT_XMSSKEY_TYPE_NONE;
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeySetValue(
+    _In_reads_bytes_( cbInput ) PCBYTE                  pbInput,
+                                SIZE_T                  cbInput,
+                                SYMCRYPT_XMSSKEY_TYPE   keyType,
+                                UINT32                  flags,
+    _Inout_                     PSYMCRYPT_XMSS_KEY      pKey )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    UINT32 uAlgId;
+    SIZE_T cbKey;
+
+    SYMCRYPT_ASSERT(keyType == SYMCRYPT_XMSSKEY_TYPE_PUBLIC || keyType == SYMCRYPT_XMSSKEY_TYPE_PRIVATE);
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if ((flags & (~SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT)) != 0 ||
+        (keyType != SYMCRYPT_XMSSKEY_TYPE_PUBLIC && keyType != SYMCRYPT_XMSSKEY_TYPE_PRIVATE))
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Public root validation can only be performed for private keys
+    if ((flags & SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT) != 0 &&
+        keyType != SYMCRYPT_XMSSKEY_TYPE_PRIVATE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptXmssSizeofKeyBlobFromParams(&pKey->params, keyType, &cbKey);
+
+    if (cbInput != cbKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    uAlgId = SYMCRYPT_LOAD_MSBFIRST32(pbInput);
+    pbInput += sizeof(UINT32);
+
+    if (uAlgId != pKey->params.id)
+    {
+        scError = SYMCRYPT_INVALID_BLOB;
+        goto cleanup;
+    }
+
+    // Wipe private key material
+    pKey->Idx = 0;
+    SymCryptWipeKnownSize(pKey->SkPrf, sizeof(pKey->SkPrf));
+    SymCryptWipeKnownSize(pKey->SkXmss, sizeof(pKey->SkXmss));
+
+    pKey->keyType = keyType;
+
+    memcpy(pKey->Root, pbInput, pKey->params.cbHashOutput);
+    pbInput += pKey->params.cbHashOutput;
+
+    memcpy(pKey->Seed, pbInput, pKey->params.cbHashOutput);
+    pbInput += pKey->params.cbHashOutput;
+
+    if (keyType == SYMCRYPT_XMSSKEY_TYPE_PRIVATE)
+    {
+        pKey->Idx = SYMCRYPT_LOAD_MSBFIRST64(pbInput);
+        pbInput += sizeof(UINT64);
+
+        memcpy(pKey->SkXmss, pbInput, pKey->params.cbHashOutput);
+        pbInput += pKey->params.cbHashOutput;
+
+        memcpy(pKey->SkPrf, pbInput, pKey->params.cbHashOutput);
+        pbInput += pKey->params.cbHashOutput;
+
+        if ((flags & SYMCRYPT_FLAG_XMSSKEY_VERIFY_ROOT) != 0)
+        {
+            // pKey has been initialized by now
+            scError = SymCryptXmsskeyVerifyRoot(pKey);
+
+            if (scError != SYMCRYPT_NO_ERROR)
+            {
+                goto cleanup;
+            }
+        }
+    }
+
+cleanup:
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        SymCryptWipeKnownSize(pKey->SkPrf, sizeof(pKey->SkPrf));
+        SymCryptWipeKnownSize(pKey->SkXmss, sizeof(pKey->SkXmss));
+        pKey->keyType = SYMCRYPT_XMSSKEY_TYPE_NONE;
+    }
+
+    return scError;
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmsskeyGetValue(
+    _In_                            PCSYMCRYPT_XMSS_KEY     pKey,
+                                    SYMCRYPT_XMSSKEY_TYPE   keyType,
+                                    UINT32                  flags,
+    _Out_writes_bytes_( cbOutput )  PBYTE                   pbOutput,
+                                    SIZE_T                  cbOutput)
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    SIZE_T cbKey;
+
+    SYMCRYPT_ASSERT(keyType == SYMCRYPT_XMSSKEY_TYPE_PUBLIC || keyType == SYMCRYPT_XMSSKEY_TYPE_PRIVATE);
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    if (flags != 0 ||
+        (keyType != SYMCRYPT_XMSSKEY_TYPE_PUBLIC && keyType != SYMCRYPT_XMSSKEY_TYPE_PRIVATE) ||
+        pKey->keyType == SYMCRYPT_XMSSKEY_TYPE_NONE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    // Cannot export private key from a public key object
+    if (keyType == SYMCRYPT_XMSSKEY_TYPE_PRIVATE && pKey->keyType != SYMCRYPT_XMSSKEY_TYPE_PRIVATE)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    SymCryptXmssSizeofKeyBlobFromParams(&pKey->params, keyType, &cbKey);
+
+    if (cbOutput != cbKey)
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    //
+    // Public Key
+    //
+
+    // Alg Id
+    SYMCRYPT_STORE_MSBFIRST32(pbOutput, pKey->params.id);
+    pbOutput += sizeof(UINT32);
+
+    // Root
+    memcpy(pbOutput, pKey->Root, pKey->params.cbHashOutput);
+    pbOutput += pKey->params.cbHashOutput;
+
+    // Seed
+    memcpy(pbOutput, pKey->Seed, pKey->params.cbHashOutput);
+    pbOutput += pKey->params.cbHashOutput;
+
+    if (keyType == SYMCRYPT_XMSSKEY_TYPE_PRIVATE)
+    {
+        //
+        // Private Key
+        //
+
+        // Idx
+        SYMCRYPT_STORE_MSBFIRST64(pbOutput, pKey->Idx);
+        pbOutput += sizeof(pKey->Idx);
+
+        // SK_XMSS
+        memcpy(pbOutput, pKey->SkXmss, pKey->params.cbHashOutput);
+        pbOutput += pKey->params.cbHashOutput;
+
+        // SK_PRF
+        memcpy(pbOutput, pKey->SkPrf, pKey->params.cbHashOutput);
+        pbOutput += pKey->params.cbHashOutput;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+
+UINT32
+SYMCRYPT_CALL
+SymCryptHbsGetDigit(
+            UINT32 width,
+    _In_    PCBYTE pbBuffer,
+            SIZE_T cbBuffer,
+            UINT32 index )
+{
+    UNREFERENCED_PARAMETER(cbBuffer);
+
+    SYMCRYPT_ASSERT(width == 1 || width == 2 || width == 4 || width == 8);
+    SYMCRYPT_ASSERT(index < ((cbBuffer * 8) / width));
+
+    UINT32 digitsPerByte = 8 / width;
+
+    BYTE value = pbBuffer[index / digitsPerByte];
+
+    value >>= width * (digitsPerByte - 1 - (index % digitsPerByte));
+
+    value &= (1 << width) - 1;
+
+    return value;
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssTreeRootFromAuthenticationPath(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_ XMSS_ADRS              *adrs,
+            UINT32                  uLeaf,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbStartingNode,
+    _In_reads_bytes_( pParams->cbHashOutput * pParams->nLayerHeight )
+            PCBYTE                  pbAuthNodes,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbSeed,
+    _Out_writes_bytes_( pParams->cbHashOutput )
+            PBYTE                   pbOutput )
+{
+    BYTE node[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE tmp[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    UINT32 uNodeIndex = uLeaf;
+
+    memcpy(node, pbStartingNode, pParams->cbHashOutput);
+
+    SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_HASH_TREE);
+    SYMCRYPT_STORE_MSBFIRST32(adrs->u.hashtree.en32Index, uNodeIndex);
+
+    for (UINT32 i = 0; i < pParams->nLayerHeight; i++)
+    {
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.hashtree.en32Height, i);
+
+        if ( ((uLeaf >> i) & 1) == 0 )
+        {
+            uNodeIndex = uNodeIndex / 2;
+            SYMCRYPT_STORE_MSBFIRST32(adrs->u.hashtree.en32Index, uNodeIndex);
+            SymCryptXmssRandHash(pParams, adrs, pbSeed, node, &pbAuthNodes[pParams->cbHashOutput * i], tmp);
+        }
+        else
+        {
+            uNodeIndex = (uNodeIndex - 1) / 2;
+            SYMCRYPT_STORE_MSBFIRST32(adrs->u.hashtree.en32Index, uNodeIndex);
+            SymCryptXmssRandHash(pParams, adrs, pbSeed, &pbAuthNodes[pParams->cbHashOutput * i], node, tmp);
+        }
+
+        memcpy(node, tmp, pParams->cbHashOutput);
+    }
+
+    memcpy(pbOutput, node, pParams->cbHashOutput);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssRandomizedHash(
+    _In_                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+                                                UINT64                  Idx,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbRandomizer,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbRoot,
+    _In_reads_bytes_( pParams->cbHashOutput )   PCBYTE                  pbMsg,
+                                                SIZE_T                  cbMsg,
+    _Out_writes_bytes_( pParams->cbHashOutput ) PBYTE                   pbOutput )
+{
+    SYMCRYPT_HASH_STATE state;
+    BYTE idxBuf[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+
+    SymCryptWipe(idxBuf, pParams->cbHashOutput);
+    SYMCRYPT_STORE_MSBFIRST64(&idxBuf[pParams->cbHashOutput - sizeof(Idx)], Idx);
+
+    SymCryptXmssPrfInit(pParams->hash, SYMCRYPT_XMSS_H_MSG, pParams->cbPrefix, &state);
+    SymCryptHashAppend(pParams->hash, &state, pbRandomizer, pParams->cbHashOutput);
+    SymCryptHashAppend(pParams->hash, &state, pbRoot, pParams->cbHashOutput);
+    SymCryptHashAppend(pParams->hash, &state, idxBuf, pParams->cbHashOutput);
+    SymCryptHashAppend(pParams->hash, &state, pbMsg, cbMsg);
+    SymCryptHashResult(pParams->hash, &state, pbOutput, pParams->cbHashOutput);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssWotspPublickeyFromSignature(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_ XMSS_ADRS              *adrs,
+            UINT32                  idx,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbMsg,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbSeed,
+    _In_reads_bytes_( pParams->cbHashOutput * pParams->len )
+            PCBYTE  pbSignature,
+    _Out_writes_bytes_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch,
+   _Out_writes_bytes_( pParams->cbHashOutput )
+            PBYTE                   pbOutput )
+{
+
+    UINT32 digit;
+    UINT32 checksum = 0;
+    BYTE en32Checksum[4];
+    const UINT32 maxChainIndex = (1 << pParams->nWinternitzWidth) - 1;
+    PSYMCRYPT_INCREMENTAL_TREEHASH pIncHash = NULL;
+    PSYMCRYPT_TREEHASH_NODE pNode = NULL;
+    SYMCRYPT_XMSS_INCREMENTAL_TREEHASH_CONTEXT ctxIncHash;
+
+    SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_LTREE);
+    SYMCRYPT_STORE_MSBFIRST32(adrs->u.ltree.en32Leaf, idx);
+
+    ctxIncHash.adrs = *adrs;
+    ctxIncHash.pParams = pParams;
+    ctxIncHash.pbSeed = pbSeed;
+
+    pIncHash = SymCryptHbsIncrementalTreehashInit(
+                    pParams->len,
+                    pbScratch,
+                    cbScratch,
+                    pParams->cbHashOutput,
+                    SymCryptXmssLtreeNodeCompress,
+                    &ctxIncHash);
+
+    for (UINT32 i = 0; i < pParams->len; i++)
+    {
+        if (i < pParams->len1)
+        {
+            digit = SymCryptHbsGetDigit(pParams->nWinternitzWidth, pbMsg, pParams->cbHashOutput, i);
+
+            checksum += maxChainIndex - digit;
+        }
+        else
+        {
+            if (i == pParams->len1)
+            {
+                checksum <<= pParams->nLeftShift32;
+                SYMCRYPT_STORE_MSBFIRST32(en32Checksum, checksum);
+            }
+
+            digit = SymCryptHbsGetDigit(pParams->nWinternitzWidth, en32Checksum, sizeof(en32Checksum), i - pParams->len1);
+        }
+
+        pNode = SymCryptHbsIncrementalTreehashAllocNode(pIncHash, i);
+
+        SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_OTS);
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Leaf, idx);
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Chain, i);
+
+        SymCryptXmssChain(
+            pParams,
+            &pbSignature[pParams->cbHashOutput * i],
+            digit,
+            maxChainIndex - digit,
+            pbSeed,
+            adrs,
+            pNode->value);
+
+        SymCryptHbsIncrementalTreehashProcess(pIncHash);
+    }
+
+    pNode = SymCryptHbsIncrementalTreehashFinalize(pIncHash);
+
+    memcpy(pbOutput, pNode->value, pParams->cbHashOutput);
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssTreeRootFromSignature(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_ XMSS_ADRS               *adrs,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbSeed,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbHash,
+            UINT32                  uLeaf,
+    _In_reads_bytes_( pParams->cbHashOutput * pParams->len )
+            PCBYTE                  pbWotspSig,
+    _In_reads_bytes_( pParams->cbHashOutput* pParams->nLayerHeight )
+            PCBYTE                  pbAuthNodes,
+    _Out_writes_bytes_( pParams->cbHashOutput )
+            PBYTE                   pbOutput,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch )
+{
+    BYTE WotspPublickey[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+
+    SymCryptXmssWotspPublickeyFromSignature(
+        pParams,
+        adrs,
+        uLeaf,
+        pbHash,
+        pbSeed,
+        pbWotspSig,
+        pbScratch,
+        cbScratch,
+        WotspPublickey);
+
+    SymCryptXmssTreeRootFromAuthenticationPath(
+        pParams,
+        adrs,
+        uLeaf,
+        WotspPublickey,
+        pbAuthNodes,
+        pbSeed,
+        pbOutput);
+}
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptXmssSizeofWotspSignature(_In_ PCSYMCRYPT_XMSS_PARAMS pParams)
+{
+    // WOTSP signature size is len = len1 + len2 many hash outputs
+    return pParams->cbHashOutput * pParams->len;
+}
+
+SIZE_T
+SYMCRYPT_CALL
+SymCryptXmssSizeofAuthNodes(
+    _In_ PCSYMCRYPT_XMSS_PARAMS pParams)
+{
+    // size of authentication nodes for single tree
+    return pParams->cbHashOutput * pParams->nLayerHeight;
+}
+
+UINT64
+SYMCRYPT_CALL
+SymCryptXmssSignatureGetIdx(
+    _In_ PCSYMCRYPT_XMSS_PARAMS pParams,
+    _In_ PCBYTE                 pbSig )
+{
+    UINT64 Idx = 0;
+
+    for (UINT8 i = 0; i < pParams->cbIdx; i++)
+    {
+        Idx <<= 8;
+        Idx |= (UINT64)pbSig[i];
+    }
+
+    return Idx;
+}
+
+PBYTE
+SYMCRYPT_CALL
+SymCryptXmssSignatureGetRandomness(
+    _In_ PCSYMCRYPT_XMSS_PARAMS pParams,
+    _In_ PCBYTE                 pbSig )
+{
+    PBYTE pb = (PBYTE)pbSig;
+
+    // randomness comes after idx
+    pb += pParams->cbIdx;
+
+    return pb;
+}
+
+PBYTE
+SYMCRYPT_CALL
+SymCryptXmssSignatureGetWotspSig(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_    PCBYTE                  pbSig,
+            UINT32                  uLayer )
+{
+    PBYTE pb = SymCryptXmssSignatureGetRandomness(pParams, pbSig);
+
+    // skip randomness
+    pb += pParams->cbHashOutput;
+
+    // each layer contains WOTSP signature and AuthNodes
+    pb += uLayer * (SymCryptXmssSizeofWotspSignature(pParams) + SymCryptXmssSizeofAuthNodes(pParams));
+
+    return pb;
+}
+
+PBYTE
+SYMCRYPT_CALL
+SymCryptXmssSignatureGetAuthNodes(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _In_    PCBYTE                  pbSig,
+            UINT32                  uLayer )
+{
+    PBYTE pb = SymCryptXmssSignatureGetWotspSig(pParams, pbSig, uLayer);
+
+    // AuthNodes follow WOTSP signature
+    pb += SymCryptXmssSizeofWotspSignature(pParams);
+
+    return pb;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssVerifyInternal(
+    _Inout_                         PSYMCRYPT_XMSS_KEY  pKey,
+    _In_reads_bytes_( cbMessage )   PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_( cbSignature ) PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    PCSYMCRYPT_XMSS_PARAMS pParams = &pKey->params;
+    BYTE RandomizedHash[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE ComputedRoot[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    XMSS_ADRS adrs;
+    UINT32 uLayer;
+    UINT64 uTree;
+    UINT32 uLeaf;
+    const UINT64 LeafMask = (1ULL << pParams->nLayerHeight) - 1;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    SYMCRYPT_ASSERT(pParams->nLayerHeight < 32); // Ensure nLeaves fits in 32 bits
+
+    if (flags != 0 ||
+        pbSignature == NULL ||
+        cbSignature != SymCryptXmssSizeofSignatureFromParams(pParams) ||
+        pKey->keyType == SYMCRYPT_XMSSKEY_TYPE_NONE )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch += SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, pParams->len);
+    cbScratch += SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, 1ULL << pParams->nLayerHeight);
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc(cbScratch);
+
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    PBYTE pbRandomness = SymCryptXmssSignatureGetRandomness(pParams, pbSignature);
+    UINT64 Idx = SymCryptXmssSignatureGetIdx(pParams, pbSignature);
+
+    SymCryptXmssRandomizedHash(
+        pParams,
+        Idx,
+        pbRandomness,
+        pKey->Root,
+        pbMessage,
+        cbMessage,
+        RandomizedHash);
+
+    SymCryptWipeKnownSize(&adrs, sizeof(XMSS_ADRS));
+
+    for (uLayer = 0; uLayer < pParams->nLayers; uLayer++)
+    {
+        uTree = Idx >> pParams->nLayerHeight;
+        uLeaf = (UINT32)(Idx & LeafMask);
+
+        SYMCRYPT_STORE_MSBFIRST32(adrs.en32Layer, uLayer);
+        SYMCRYPT_STORE_MSBFIRST64(adrs.en64Tree, uTree);
+        SymCryptXmssTreeRootFromSignature(
+            pParams,
+            &adrs,
+            pKey->Seed,
+            uLayer == 0 ? RandomizedHash : ComputedRoot,
+            uLeaf,
+            SymCryptXmssSignatureGetWotspSig(pParams, pbSignature, uLayer),
+            SymCryptXmssSignatureGetAuthNodes(pParams, pbSignature, uLayer),
+            ComputedRoot,
+            pbScratch,
+            cbScratch);
+
+        Idx >>= pParams->nLayerHeight;
+    }
+
+    if (!SymCryptEqual(ComputedRoot, pKey->Root, pParams->cbHashOutput))
+    {
+        scError = SYMCRYPT_SIGNATURE_VERIFICATION_FAILURE;
+        goto cleanup;
+    }
+
+cleanup:
+
+    if (pbScratch)
+    {
+        SymCryptWipe(pbScratch, cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssVerify(
+    _Inout_                         PSYMCRYPT_XMSS_KEY  pKey,
+    _In_reads_bytes_( cbMessage )   PCBYTE              pbMessage,
+                                    SIZE_T              cbMessage,
+                                    UINT32              flags,
+    _In_reads_bytes_( cbSignature ) PCBYTE              pbSignature,
+                                    SIZE_T              cbSignature )
+{
+    SYMCRYPT_RUN_SELFTEST_ONCE(
+        SymCryptXmssSelftest,
+        SYMCRYPT_SELFTEST_ALGORITHM_XMSS);
+
+    return SymCryptXmssVerifyInternal(
+        pKey,
+        pbMessage,
+        cbMessage,
+        flags,
+        pbSignature,
+        cbSignature);
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssWotspSign(
+    _In_                                                        PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_                                                     XMSS_ADRS*              adrs,
+    _In_reads_bytes_( pParams->cbHashOutput )                   PCBYTE                  pbInput,
+                                                                UINT32                  uLeaf,
+    _In_reads_bytes_( pParams->cbHashOutput )                   PCBYTE                  pbSeed,
+    _In_reads_bytes_( pParams->cbHashOutput )                   PCBYTE                  pbSkXmss,
+    _Out_writes_bytes_( pParams->cbHashOutput * pParams->len )  PBYTE                   pbOutput )
+{
+    BYTE node[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    UINT32 nChecksum = 0;
+    BYTE en32Checksum[sizeof(UINT32)];
+    UINT32 digit;
+    const UINT32 maxChainIndex = (1UL << pParams->nWinternitzWidth) - 1;
+
+
+    SymCryptXmssSetAdrsType(adrs, XMSS_ADRS_TYPE_OTS);
+    SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Leaf, uLeaf);
+
+    for (UINT32 i = 0; i < pParams->len; i++)
+    {
+        SYMCRYPT_STORE_MSBFIRST32(adrs->u.ots.en32Chain, i);
+        SymCryptXmssCreateWotspSecret(
+            pParams,
+            pbSkXmss,
+            pbSeed,
+            adrs,
+            node);
+
+        if (i < pParams->len1)
+        {
+            digit = SymCryptHbsGetDigit(pParams->nWinternitzWidth, pbInput, pParams->cbHashOutput, i);
+
+            nChecksum += maxChainIndex - digit;
+        }
+        else
+        {
+            if (i == pParams->len1)
+            {
+                nChecksum <<= pParams->nLeftShift32;
+                SYMCRYPT_STORE_MSBFIRST32(en32Checksum, nChecksum);
+            }
+
+            digit = SymCryptHbsGetDigit(pParams->nWinternitzWidth, en32Checksum, sizeof(en32Checksum), i - pParams->len1);
+        }
+
+        SymCryptXmssChain(
+            pParams,
+            node,
+            0,
+            digit,
+            pbSeed,
+            adrs,
+            &pbOutput[i * pParams->cbHashOutput]);
+    }
+
+    SymCryptWipeKnownSize(node, sizeof(node));
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXmssTreeSignHash(
+    _In_    PCSYMCRYPT_XMSS_PARAMS  pParams,
+    _Inout_ XMSS_ADRS               *adrs,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbSkXmss,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbSeed,
+    _In_reads_bytes_( pParams->cbHashOutput )
+            PCBYTE                  pbHash,
+            UINT32                  Idx,
+    _Out_writes_bytes_( pParams->cbHashOutput * pParams->len )
+            PBYTE                   pbWotspSig,
+    _Out_writes_bytes_( pParams->cbHashOutput * pParams->nLayerHeight )
+            PBYTE                   pbAuthNodes,
+    _Out_writes_bytes_opt_( pParams->cbHashOutput )
+            PBYTE                   pbRoot,
+    _Out_writes_bytes_opt_( cbScratch )
+            PBYTE                   pbScratch,
+            SIZE_T                  cbScratch )
+{
+    BYTE WotspPublicKey[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+
+    SymCryptXmssWotspSign(
+        pParams,
+        adrs,
+        pbHash,
+        Idx,
+        pbSeed,
+        pbSkXmss,
+        pbWotspSig );
+
+    // Generate authentication path
+    for (UINT32 h = 0; h < pParams->nLayerHeight; h++)
+    {
+        UINT32 uLeaf = ((Idx >> h) ^ 1UL) << h;
+        SymCryptXmssComputeSubtreeRoot(
+            pParams,
+            adrs,
+            pbSkXmss,
+            pbSeed,
+            uLeaf,
+            h,
+            pbScratch,
+            cbScratch,
+            &pbAuthNodes[h * pParams->cbHashOutput]);
+    }
+
+    //
+    // Calculate tree root if requested by the caller
+    //
+    // This is used to return the tree root to be signed with the upper
+    // layer in XMSS^MT.
+    if (pbRoot)
+    {
+        SymCryptXmssCreateWotspPublickey(
+            pParams,
+            adrs,
+            Idx,
+            pbSkXmss,
+            pbSeed,
+            pbScratch,
+            cbScratch,
+            WotspPublicKey);
+
+        SymCryptXmssTreeRootFromAuthenticationPath(
+            pParams,
+            adrs,
+            Idx,
+            WotspPublicKey,
+            pbAuthNodes,
+            pbSeed,
+            pbRoot);
+    }
+}
+
+//
+// Compute randomness for randomized hashing
+//
+VOID
+SYMCRYPT_CALL
+SymCryptXmssComputeRandomness(
+    _In_                                            PCSYMCRYPT_XMSS_KEY pKey,
+                                                    UINT64              Idx,
+    _Out_writes_bytes_( pKey->params.cbHashOutput ) PBYTE               pbRandomness )
+{
+    BYTE IdxBuffer[32];
+
+    SymCryptWipeKnownSize(IdxBuffer, sizeof(IdxBuffer));
+    SYMCRYPT_STORE_MSBFIRST64(IdxBuffer + sizeof(IdxBuffer) - sizeof(Idx), Idx);
+
+    SymCryptXmssPrf(
+        &pKey->params,
+        SYMCRYPT_XMSS_PRF,
+        pKey->SkPrf,
+        pKey->params.cbHashOutput,
+        IdxBuffer,
+        sizeof(IdxBuffer),
+        pbRandomness);
+}
+
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXmssSign(
+    _Inout_                             PSYMCRYPT_XMSS_KEY  pKey,
+    _In_reads_bytes_( cbMessage )       PCBYTE              pbMessage,
+                                        SIZE_T              cbMessage,
+                                        UINT32              flags,
+    _Out_writes_bytes_( cbSignature )   PBYTE               pbSignature,
+                                        SIZE_T              cbSignature )
+{
+    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
+    PBYTE pbScratch = NULL;
+    SIZE_T cbScratch = 0;
+    PSYMCRYPT_XMSS_PARAMS pParams = &pKey->params;
+    UINT64 Idx;
+    BYTE en64Idx[sizeof(UINT64)];
+    BYTE Randomness[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE RandomizedHash[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    BYTE TreeRoot[SYMCRYPT_HASH_MAX_RESULT_SIZE];
+    XMSS_ADRS adrs;
+    UINT32 uLayer;
+    UINT64 uTree;
+    UINT32 uLeaf;
+    const UINT64 LeafMask = (1ULL << pParams->nLayerHeight) - 1;
+
+    SYMCRYPT_CHECK_MAGIC(pKey);
+
+    SYMCRYPT_ASSERT(pParams->nLayerHeight < 32); // Ensure nLeaves fits in 32 bits
+
+    if (flags != 0 ||
+        pbSignature == NULL ||
+        cbSignature != SymCryptXmssSizeofSignatureFromParams(pParams) ||
+        pKey->keyType != SYMCRYPT_XMSSKEY_TYPE_PRIVATE )
+    {
+        scError = SYMCRYPT_INVALID_ARGUMENT;
+        goto cleanup;
+    }
+
+    cbScratch += SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, pParams->len);        // Ltree hashing
+    cbScratch += SymCryptHbsSizeofScratchBytesForIncrementalTreehash(pParams->cbHashOutput, 1ULL << pParams->nLayerHeight);  // Merkle-tree hashing
+
+    pbScratch = (PBYTE)SymCryptCallbackAlloc(cbScratch);
+
+    if (pbScratch == NULL)
+    {
+        scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
+        goto cleanup;
+    }
+
+    Idx = SYMCRYPT_ATOMIC_ADD64_POST_RELAXED(&pKey->Idx, 1) - 1;
+    if (Idx >= (1ULL << pParams->nTotalTreeHeight))
+    {
+        // Set Idx to first unusable value
+        pKey->Idx = (1ULL << pParams->nTotalTreeHeight);
+
+        scError = SYMCRYPT_HBS_NO_OTS_KEYS_LEFT;
+        goto cleanup;
+    }
+
+    SYMCRYPT_STORE_MSBFIRST64(en64Idx, Idx);
+    memcpy(pbSignature, &en64Idx[sizeof(en64Idx) - pParams->cbIdx], pParams->cbIdx);
+
+    SymCryptXmssComputeRandomness(pKey, Idx, Randomness);
+    memcpy(SymCryptXmssSignatureGetRandomness(pParams, pbSignature), Randomness, pKey->params.cbHashOutput);
+
+    SymCryptXmssRandomizedHash(&pKey->params, Idx, Randomness, pKey->Root, pbMessage, cbMessage, RandomizedHash);
+
+    SymCryptWipeKnownSize(&adrs, sizeof(XMSS_ADRS));
+
+    for (uLayer = 0; uLayer < pParams->nLayers; uLayer++)
+    {
+        uTree = Idx >> pParams->nLayerHeight;
+        uLeaf = (UINT32)(Idx & LeafMask);
+
+        SYMCRYPT_STORE_MSBFIRST32(adrs.en32Layer, uLayer);
+        SYMCRYPT_STORE_MSBFIRST64(adrs.en64Tree, uTree);
+
+        SymCryptXmssTreeSignHash(
+            &pKey->params,
+            &adrs,
+            pKey->SkXmss,
+            pKey->Seed,
+            uLayer == 0 ? RandomizedHash : TreeRoot,
+            uLeaf,
+            SymCryptXmssSignatureGetWotspSig(pParams, pbSignature, uLayer),
+            SymCryptXmssSignatureGetAuthNodes(pParams, pbSignature, uLayer),
+            uLayer == (UINT32)(pParams->nLayers - 1) ? NULL : TreeRoot, // No need to compute the root for the top layer tree
+            pbScratch,
+            cbScratch);
+
+        Idx >>= pParams->nLayerHeight;
+    }
+
+    if (scError != SYMCRYPT_NO_ERROR)
+    {
+        SymCryptWipe(pbSignature, cbSignature);
+        goto cleanup;
+    }
+
+cleanup:
+
+    if (pbScratch)
+    {
+        SymCryptWipe(pbScratch, cbScratch);
+        SymCryptCallbackFree(pbScratch);
+    }
+
+    return scError;
+}
diff --git a/libs/symcrypt/lib/xtsaes.c b/libs/symcrypt/lib/xtsaes.c
new file mode 100644
index 000000000000..55afe374cc03
--- /dev/null
+++ b/libs/symcrypt/lib/xtsaes.c
@@ -0,0 +1,727 @@
+//
+// xtsaes.c   code for XTS-AES implementation
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#include "precomp.h"
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXtsAesExpandKey(
+    _Out_               PSYMCRYPT_XTS_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_( cbKey ) PCBYTE                          pbKey,
+                        SIZE_T                          cbKey )
+{
+    SYMCRYPT_ERROR  scError;
+    SIZE_T          halfKeySize = cbKey / 2;
+
+    scError = SymCryptAesExpandKey( &pExpandedKey->key1, pbKey, halfKeySize );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+    //
+    // Pass the 'rest' of the key to the second one. This catches errors such as
+    // an attempt to pass a 33 byte key.
+    // halfKeySize = 16, which is valid, but this expansion gets a 17-byte key which will fail.
+    // Key2 is only used for tweak encryption, so we can use the EncryptOnly key expansion.
+    //
+    scError = SymCryptAesExpandKeyEncryptOnly( &pExpandedKey->key2, pbKey + halfKeySize, cbKey - halfKeySize );
+    if( scError != SYMCRYPT_NO_ERROR )
+    {
+        goto cleanup;
+    }
+
+cleanup:
+
+    return scError;
+}
+
+SYMCRYPT_ERROR
+SYMCRYPT_CALL
+SymCryptXtsAesExpandKeyEx(
+    _Out_               PSYMCRYPT_XTS_AES_EXPANDED_KEY  pExpandedKey,
+    _In_reads_( cbKey ) PCBYTE                          pbKey,
+                        SIZE_T                          cbKey,
+                        UINT32                          flags )
+{
+    if( ( flags & SYMCRYPT_FLAG_KEY_NO_FIPS ) == 0 )
+    {
+        // FIPS IG C.I enforces that the two AES keys internally used in XTS-AES are non-equal
+        if( cbKey > 64 )
+        {
+            return SYMCRYPT_WRONG_KEY_SIZE;
+        }
+        if( SymCryptEqual( pbKey, pbKey+(cbKey/2), (cbKey/2) ) )
+        {
+            return SYMCRYPT_FIPS_FAILURE;
+        }
+    }
+
+    return SymCryptXtsAesExpandKey( pExpandedKey, pbKey, cbKey );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesKeyCopy(
+    _In_    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pSrc,
+    _Out_   PSYMCRYPT_XTS_AES_EXPANDED_KEY  pDst )
+{
+    SymCryptAesKeyCopy( &pSrc->key1, &pDst->key1 );
+    SymCryptAesKeyCopy( &pSrc->key2, &pDst->key2 );
+}
+
+#define N_PARALLEL_TWEAKS   16
+
+#define SYMCRYPT_XTS_AES_LOCALSCRATCH_DEFN \
+    SYMCRYPT_ALIGN BYTE localScratch[N_PARALLEL_TWEAKS * SYMCRYPT_AES_BLOCK_SIZE];
+
+#define SYMCRYPT_AesEcbEncryptXxx SymCryptAesEcbEncryptC
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesEncryptInternalC
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesEncryptDataUnitC( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesDecryptInternalC
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesDecryptDataUnitC( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#undef SYMCRYPT_AesEcbEncryptXxx
+
+
+#if (SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM) && defined(__WINE_PE_BUILD)
+#define SYMCRYPT_AesEcbEncryptXxx SymCryptAesEcbEncryptAsm
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesEncryptInternalAsm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesEncryptDataUnitAsm( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesDecryptInternalAsm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesDecryptDataUnitAsm( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#undef SYMCRYPT_AesEcbEncryptXxx
+#endif
+
+#if SYMCRYPT_CPU_ARM64
+#define SYMCRYPT_AesEcbEncryptXxx SymCryptAesEcbEncryptNeon
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesEncryptInternalNeon
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesEncryptDataUnitNeon( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesDecryptInternalNeon
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesDecryptDataUnitNeon( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#undef SYMCRYPT_AesEcbEncryptXxx
+#endif
+
+#undef SYMCRYPT_XTS_AES_LOCALSCRATCH_DEFN
+
+
+#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
+
+#define SYMCRYPT_XTS_AES_LOCALSCRATCH_DEFN \
+    /* Defining localScratch as a buffer of __m128is ensures there is required 16B alignment on x86 */ \
+    __m128i localScratch[ N_PARALLEL_TWEAKS + 16 ];
+#define SYMCRYPT_AesEcbEncryptXxx SymCryptAesEcbEncryptXmm
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesEncryptInternalXmm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesEncryptDataUnitXmm( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesDecryptInternalXmm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesDecryptDataUnitXmm( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesEncryptInternalYmm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesEncryptDataUnitYmm_2048( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesDecryptInternalYmm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesDecryptDataUnitYmm_2048( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#if 0 //do not compile Zmm code for now
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesEncryptInternalZmm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesEncryptDataUnitZmm_2048( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#define SYMCRYPT_XtsAesXxx SymCryptXtsAesDecryptInternalZmm
+#define SYMCRYPT_XTSAESDATAUNIT_INVOKE \
+    SymCryptXtsAesDecryptDataUnitZmm_2048( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+#include "xtsaes_pattern.c"
+#undef SYMCRYPT_XtsAesXxx
+#undef SYMCRYPT_XTSAESDATAUNIT_INVOKE
+
+#endif
+
+#undef SYMCRYPT_XTS_AES_LOCALSCRATCH_DEFN
+#undef SYMCRYPT_AesEcbEncryptXxx
+
+#endif
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptInternal(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData,
+                                            BOOLEAN                         bOverflow )
+{
+#if SYMCRYPT_CPU_AMD64
+    SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+    /* if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_VAES_512_CODE ) ) {
+        SymCryptXtsAesEncryptInternalZmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    } else */
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_VAES_256_CODE ) &&
+        SymCryptSaveYmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptXtsAesEncryptInternalYmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+        SymCryptRestoreYmm( &SaveData );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) ) {
+        SymCryptXtsAesEncryptInternalXmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    } else {
+        SymCryptXtsAesEncryptInternalAsm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptXtsAesEncryptInternalXmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptXtsAesEncryptInternalAsm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptXtsAesEncryptInternalAsm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptXtsAesEncryptInternalNeon( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    } else {
+        SymCryptXtsAesEncryptInternalC( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    }
+#else
+    SymCryptXtsAesEncryptInternalC( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncrypt(
+    _In_                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                            SIZE_T                          cbDataUnit,
+                            UINT64                          tweak,
+    _In_reads_( cbData )    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )  PBYTE                           pbDst,
+                            SIZE_T                          cbData )
+{
+    SYMCRYPT_ALIGN BYTE fullTweak[SYMCRYPT_AES_BLOCK_SIZE];
+
+    SYMCRYPT_ASSERT( cbData % cbDataUnit == 0 );
+
+    if( cbDataUnit < SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Invalid data unit size
+        // Return early to avoid repeated checks deeper in the code
+        return;
+    }
+
+    SYMCRYPT_STORE_LSBFIRST64(&fullTweak[0], tweak);
+    SYMCRYPT_STORE_LSBFIRST64(&fullTweak[8], 0);
+
+    SymCryptXtsAesEncryptInternal( pExpandedKey, cbDataUnit, &fullTweak[0], pbSrc, pbDst, cbData, FALSE );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptWith128bTweak(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData )
+{
+    if( cbDataUnit < SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Invalid data unit size
+        // Return early to avoid repeated checks deeper in the code
+        return;
+    }
+
+    SymCryptXtsAesEncryptInternal( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, TRUE );
+}
+
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptInternal(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData,
+                                            BOOLEAN                         bOverflow )
+{
+#if SYMCRYPT_CPU_AMD64
+    SYMCRYPT_EXTENDED_SAVE_DATA SaveData;
+    /* if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_VAES_512_CODE ) ) {
+        SymCryptXtsAesDecryptInternalZmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    } else */
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_VAES_256_CODE ) &&
+        SymCryptSaveYmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptXtsAesDecryptInternalYmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+        SymCryptRestoreYmm( &SaveData );
+    } else if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) ) {
+        SymCryptXtsAesDecryptInternalXmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    } else {
+        SymCryptXtsAesDecryptInternalAsm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    }
+#elif SYMCRYPT_CPU_X86
+    SYMCRYPT_EXTENDED_SAVE_DATA  SaveData;
+
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURES_FOR_AESNI_CODE ) &&
+        SymCryptSaveXmm( &SaveData ) == SYMCRYPT_NO_ERROR )
+    {
+        SymCryptXtsAesDecryptInternalXmm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+        SymCryptRestoreXmm( &SaveData );
+    } else {
+        SymCryptXtsAesDecryptInternalAsm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    }
+#elif SYMCRYPT_CPU_ARM
+    SymCryptXtsAesDecryptInternalAsm( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+#elif SYMCRYPT_CPU_ARM64
+    if( SYMCRYPT_CPU_FEATURES_PRESENT( SYMCRYPT_CPU_FEATURE_NEON_AES ) )
+    {
+        SymCryptXtsAesDecryptInternalNeon( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    } else {
+        SymCryptXtsAesDecryptInternalC( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+    }
+#else
+    SymCryptXtsAesDecryptInternalC( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, bOverflow );
+#endif
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecrypt(
+    _In_                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                            SIZE_T                          cbDataUnit,
+                            UINT64                          tweak,
+    _In_reads_( cbData )    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )  PBYTE                           pbDst,
+                            SIZE_T                          cbData )
+{
+    SYMCRYPT_ALIGN BYTE fullTweak[SYMCRYPT_AES_BLOCK_SIZE];
+
+    SYMCRYPT_ASSERT( cbData % cbDataUnit == 0 );
+
+    if( cbDataUnit < SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Invalid data unit size
+        // Return early to avoid repeated checks deeper in the code
+        return;
+    }
+
+    SYMCRYPT_STORE_LSBFIRST64(&fullTweak[0], tweak);
+    SYMCRYPT_STORE_LSBFIRST64(&fullTweak[8], 0);
+
+    SymCryptXtsAesDecryptInternal( pExpandedKey, cbDataUnit, &fullTweak[0], pbSrc, pbDst, cbData, FALSE );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptWith128bTweak(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData )
+{
+    if( cbDataUnit < SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Invalid data unit size
+        // Return early to avoid repeated checks deeper in the code
+        return;
+    }
+
+    SymCryptXtsAesDecryptInternal( pExpandedKey, cbDataUnit, pbTweak, pbSrc, pbDst, cbData, TRUE );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsUpdateTweak(
+    _Inout_updates_(SYMCRYPT_AES_BLOCK_SIZE)    PBYTE   buf )
+{
+/*
+    UINT32 b0 = LOAD_LSBFIRST32( buf      );
+    UINT32 b1 = LOAD_LSBFIRST32( buf +  4 );
+    UINT32 b2 = LOAD_LSBFIRST32( buf +  8 );
+    UINT32 b3 = LOAD_LSBFIRST32( buf + 12 );
+    UINT32 msbit = b3 >> 31;
+
+    //
+    // The STORE_* macros re-evaluate their arguments sometimes, so we
+    // keep all computations in local variables.
+    //
+    UINT32 r0 = (b0 << 1) ^ (135 * msbit);
+    UINT32 r1 = (b1 << 1) | (b0 >> 31);
+    UINT32 r2 = (b2 << 1) | (b1 >> 31);
+    UINT32 r3 = (b3 << 1) | (b2 >> 31);
+
+    STORE_LSBFIRST32( buf     , r0 );
+    STORE_LSBFIRST32( buf +  4, r1 );
+    STORE_LSBFIRST32( buf +  8, r2 );
+    STORE_LSBFIRST32( buf + 12, r3 );
+*/
+    UINT64 b0 = SYMCRYPT_LOAD_LSBFIRST64( buf     );
+    UINT64 b1 = SYMCRYPT_LOAD_LSBFIRST64( buf + 8 );
+
+    /*
+    UINT32 msbit = (UINT32)(b1 >> 63);
+    //UINT32 feedback = 135 * msbit;
+    UINT32 feedback = (msbit << 7) + (msbit << 3) - msbit;
+    */
+    UINT32 feedback = (((INT64)b1) >> 63) & 135;
+
+    UINT64 r0 = (b0 << 1) ^ feedback;
+    UINT64 r1 = (b1 << 1) | (b0 >> 63);
+
+    SYMCRYPT_STORE_LSBFIRST64( buf    , r0 );
+    SYMCRYPT_STORE_LSBFIRST64( buf + 8, r1 );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsEncryptDataUnit(
+    _In_                                        PCSYMCRYPT_BLOCKCIPHER      pBlockCipher,
+    _In_                                        PCVOID                      pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    BYTE  buf[2*SYMCRYPT_AES_BLOCK_SIZE];
+
+    while( cbData >= 2*SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptXorBytes( pbTweakBlock, pbSrc, buf, SYMCRYPT_AES_BLOCK_SIZE );
+        (*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );
+        SymCryptXorBytes( pbTweakBlock, buf, pbDst, SYMCRYPT_AES_BLOCK_SIZE );
+
+        SYMCRYPT_ASSERT( pBlockCipher->blockSize == SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptXtsUpdateTweak( pbTweakBlock );
+
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbData > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Ciphertext stealing encryption
+        //
+        //                      +--------------+
+        //                      |              |
+        //                      |              V
+        // +-----------------+  |  +-----+-----------+
+        // |      P_m-1      |  |  | P_m |++++CP+++++|
+        // +-----------------+  |  +-----+-----------+
+        //          |           |           |
+        //       enc_m-1        |         enc_m
+        //          |           |           |
+        //          V           |           V
+        // +-----+-----------+  |  +-----------------+
+        // | C_m |++++CP+++++|--+  |      C_m-1      |
+        // +-----+-----------+     +-----------------+
+        //    |                   /
+        //    +----------------  /  --+
+        //                      /     |
+        //                      |     V
+        // +-----------------+  |  +-----+
+        // |      C_m-1      |<-+  | C_m |
+        // +-----------------+     +-----+
+
+        // Encrypt penultimate plaintext block into buf
+        SymCryptXorBytes( pbTweakBlock, pbSrc, buf, SYMCRYPT_AES_BLOCK_SIZE );
+        (*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );
+        SymCryptXorBytes( pbTweakBlock, buf, buf, SYMCRYPT_AES_BLOCK_SIZE );
+
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+
+        // Copy buf to buf[SYMCRYPT_AES_BLOCK_SIZE]
+        memcpy( &buf[SYMCRYPT_AES_BLOCK_SIZE], buf, SYMCRYPT_AES_BLOCK_SIZE );
+        // Copy final plaintext bytes to prefix of buf - we must read before writing to support in-place encryption
+        memcpy( buf, pbSrc + SYMCRYPT_AES_BLOCK_SIZE, cbData );
+        // Copy prefix of buf[SYMCRYPT_AES_BLOCK_SIZE] to the right place in the destination buffer
+        memcpy( pbDst + SYMCRYPT_AES_BLOCK_SIZE, &buf[SYMCRYPT_AES_BLOCK_SIZE], cbData );
+
+        // Do final tweak update
+        SymCryptXtsUpdateTweak( pbTweakBlock );
+
+        // Set pbSrc correctly to share code with non-ciphertext stealing case
+        pbSrc = &buf[0];
+    }
+
+    // Final full block encryption
+    SymCryptXorBytes( pbTweakBlock, pbSrc, buf, SYMCRYPT_AES_BLOCK_SIZE );
+    (*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );
+    SymCryptXorBytes( pbTweakBlock, buf, pbDst, SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptWipeKnownSize( buf, sizeof(buf) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsDecryptDataUnit(
+    _In_                                        PCSYMCRYPT_BLOCKCIPHER      pBlockCipher,
+    _In_                                        PCVOID                      pExpandedKey,
+    _Inout_updates_( pBlockCipher->blockSize )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    BYTE buf[2*SYMCRYPT_AES_BLOCK_SIZE];
+    BYTE tweakBuf[SYMCRYPT_AES_BLOCK_SIZE];
+
+    while( cbData >= 2*SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        SymCryptXorBytes( pbTweakBlock, pbSrc, buf, SYMCRYPT_AES_BLOCK_SIZE );
+        (*pBlockCipher->decryptFunc)( pExpandedKey, buf, buf );
+        SymCryptXorBytes( pbTweakBlock, buf, pbDst, SYMCRYPT_AES_BLOCK_SIZE );
+
+        SYMCRYPT_ASSERT( pBlockCipher->blockSize == SYMCRYPT_AES_BLOCK_SIZE );
+        SymCryptXtsUpdateTweak( pbTweakBlock );
+
+        pbSrc += SYMCRYPT_AES_BLOCK_SIZE;
+        pbDst += SYMCRYPT_AES_BLOCK_SIZE;
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+    }
+
+    if( cbData > SYMCRYPT_AES_BLOCK_SIZE )
+    {
+        // Ciphertext stealing decryption
+        //
+        //                      +--------------+
+        //                      |              |
+        //                      |              V
+        // +-----------------+  |  +-----+-----------+
+        // |      C_m-1      |  |  | C_m |++++CP+++++|
+        // +-----------------+  |  +-----+-----------+
+        //          |           |           |
+        //        dec_m         |        dec_m-1
+        //          |           |           |
+        //          V           |           V
+        // +-----+-----------+  |  +-----------------+
+        // | P_m |++++CP+++++|--+  |      P_m-1      |
+        // +-----+-----------+     +-----------------+
+        //    |                   /
+        //    +----------------  /  --+
+        //                      /     |
+        //                      |     V
+        // +-----------------+  |  +-----+
+        // |      P_m-1      |<-+  | P_m |
+        // +-----------------+     +-----+
+
+        // Save penultimate value of tweak to tweakBuf
+        memcpy( tweakBuf, pbTweakBlock, SYMCRYPT_AES_BLOCK_SIZE );
+
+        // Do final tweak update
+        SymCryptXtsUpdateTweak( pbTweakBlock );
+
+        // Decrypt penultimate ciphertext block into buf
+        SymCryptXorBytes( pbTweakBlock, pbSrc, buf, SYMCRYPT_AES_BLOCK_SIZE );
+        (*pBlockCipher->decryptFunc)( pExpandedKey, buf, buf );
+        SymCryptXorBytes( pbTweakBlock, buf, buf, SYMCRYPT_AES_BLOCK_SIZE );
+
+        cbData -= SYMCRYPT_AES_BLOCK_SIZE;
+
+        // Copy buf to buf[SYMCRYPT_AES_BLOCK_SIZE]
+        memcpy( &buf[SYMCRYPT_AES_BLOCK_SIZE], buf, SYMCRYPT_AES_BLOCK_SIZE );
+        // Copy final ciphertext bytes to prefix of buf - we must read before writing to support in-place decryption
+        memcpy( buf, pbSrc + SYMCRYPT_AES_BLOCK_SIZE, cbData );
+        // Copy prefix of buf[SYMCRYPT_AES_BLOCK_SIZE] to the right place in the destination buffer
+        memcpy( pbDst + SYMCRYPT_AES_BLOCK_SIZE, &buf[SYMCRYPT_AES_BLOCK_SIZE], cbData );
+
+        // Set pbSrc and pbTweakBlock correctly to share code with non-ciphertext stealing case
+        pbSrc = &buf[0];
+        pbTweakBlock = &tweakBuf[0];
+    }
+
+    // Final full block decryption
+    SymCryptXorBytes( pbTweakBlock, pbSrc, buf, SYMCRYPT_AES_BLOCK_SIZE );
+    (*pBlockCipher->decryptFunc)( pExpandedKey, buf, buf );
+    SymCryptXorBytes( pbTweakBlock, buf, pbDst, SYMCRYPT_AES_BLOCK_SIZE );
+
+    SymCryptWipeKnownSize( buf, sizeof(buf) );
+    SymCryptWipeKnownSize( tweakBuf, sizeof(tweakBuf) );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+    SymCryptXtsEncryptDataUnit(
+            &SymCryptAesBlockCipherNoOpt,
+            pExpandedKey,
+            pbTweakBlock,
+            pbSrc,
+            pbDst,
+            cbData );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitAsm(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+    SymCryptXtsDecryptDataUnit(
+            &SymCryptAesBlockCipherNoOpt,
+            pExpandedKey,
+            pbTweakBlock,
+            pbSrc,
+            pbDst,
+            cbData );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesEncryptDataUnitC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    // No special optimizations...
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+    SymCryptXtsEncryptDataUnit(
+        &SymCryptAesBlockCipherNoOpt,
+        pExpandedKey,
+        pbTweakBlock,
+        pbSrc,
+        pbDst,
+        cbData );
+}
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesDecryptDataUnitC(
+    _In_                                        PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
+    _Inout_updates_( SYMCRYPT_AES_BLOCK_SIZE )  PBYTE                       pbTweakBlock,
+    _In_reads_( cbData )                        PCBYTE                      pbSrc,
+    _Out_writes_( cbData )                      PBYTE                       pbDst,
+                                                SIZE_T                      cbData )
+{
+    SYMCRYPT_ASSERT( SymCryptAesBlockCipherNoOpt.blockSize == SYMCRYPT_AES_BLOCK_SIZE ); // keep Prefast happy
+    SymCryptXtsDecryptDataUnit(
+        &SymCryptAesBlockCipherNoOpt,
+        pExpandedKey,
+        pbTweakBlock,
+        pbSrc,
+        pbDst,
+        cbData );
+
+}
+
+static const BYTE SymCryptXtsAesCiphertext[32] = {
+    0xef, 0xe5, 0x8b, 0x1a, 0x0b, 0xaf, 0xc1, 0x08,
+    0xe9, 0xb7, 0x74, 0x1c, 0xcb, 0xdc, 0xf8, 0x53,
+    0x4f, 0x90, 0x55, 0x32, 0x53, 0xf6, 0x18, 0xd2,
+    0x34, 0xd5, 0xf2, 0x29, 0xf6, 0x4f, 0xd3, 0x8c,
+};
+
+VOID
+SYMCRYPT_CALL
+SymCryptXtsAesSelftest(void)
+{
+    SYMCRYPT_XTS_AES_EXPANDED_KEY key;
+    BYTE buf[32];
+    BYTE plaintext[sizeof( buf )];
+
+    SymCryptWipeKnownSize( buf, sizeof( buf ) );
+    buf[0] = 1;
+
+    if( SymCryptXtsAesExpandKeyEx( &key, buf, sizeof( buf ), 0 ) != SYMCRYPT_NO_ERROR )
+    {
+        SymCryptFatal( 'xtsa' );
+    }
+
+    SymCryptXtsAesEncrypt( &key, sizeof( buf ), 0, buf, buf, sizeof( buf ) );
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+    if( memcmp( buf, SymCryptXtsAesCiphertext, sizeof( buf ) ) != 0 )
+    {
+        SymCryptFatal( 'xtsa' );
+    }
+
+    SymCryptXtsAesDecrypt( &key, sizeof( buf ), 0, buf, buf, sizeof( buf ) );
+
+    SymCryptInjectError( buf, sizeof( buf ) );
+
+    SymCryptWipeKnownSize( plaintext, sizeof( plaintext ) );
+    plaintext[0] = 1;
+    if( memcmp( buf, plaintext, sizeof( buf ) ) != 0 )
+    {
+        SymCryptFatal( 'xtsa' );
+    }
+}
diff --git a/libs/symcrypt/lib/xtsaes_definitions.h b/libs/symcrypt/lib/xtsaes_definitions.h
new file mode 100644
index 000000000000..0a316b65a1b6
--- /dev/null
+++ b/libs/symcrypt/lib/xtsaes_definitions.h
@@ -0,0 +1,176 @@
+//
+// xtsaes_definitions.h
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+//
+// Multiply by alpha
+//
+// <</>> indicate shifts on 128-bit values
+// <<<</>>>> indicate shifts on 32-bit values (a word)
+//
+
+// Multiply by ALPHA
+// Since there's no instruction to shift the 128 bit register left by one, the following shifts do the trick.
+// All shifts are zero extended
+// t1 = _in <<<< 1                          words shifted left by 1, this is almost a _in << 1 but there are
+//                                          gaps at first bit of each word, the following two shifts fixes that.
+// t2 = _in >>>> 31                         words shifted right by 31
+// t1 = t1 ^ (t2 << 32)                     t1 = _in << 1, note ^ could be |
+// Do the special case for first byte of _in where last carry means xor with 135 for first byte.
+// t2 = t2 >> 96                            t2 = _in >> 127, i.e., last bit of _in is placed in first bit
+// t2 = (t2 <<<< 7) + (t2 <<<<3) - (t2)     t2 = 135 if last bit of t2 is set
+// res = t1 ^ t2
+#define XTS_MUL_ALPHA_old( _in, _res ) \
+{\
+    __m128i _t1, _t2;\
+\
+    _t1 = _mm_slli_epi32( _in, 1 ); \
+    _t2 = _mm_srli_epi32( _in, 31); \
+    _t1 = _mm_xor_si128( _t1, _mm_slli_si128( _t2, 4 )); \
+    _t2 = _mm_srli_si128( _t2, 12 ); \
+    _t2 = _mm_sub_epi32( _mm_add_epi32( _mm_slli_epi32( _t2, 7 ), _mm_slli_epi32( _t2, 3 ) ), _t2 ); \
+    _res = _mm_xor_si128( _t1, _t2 ); \
+}
+
+// An improved approach; use arithmetic shift-right to duplicate the carry-out, PSHUFD to re-arrange, and an AND to
+// implement both the polynomial and mask the other words down to 1 bit again.
+
+// __m128i XTS_ALPHA_MASK = _mm_set_epi32( 1, 1, 1, 0x87 );
+#define XTS_MUL_ALPHA( _in, _res ) \
+{\
+    __m128i _t1, _t2;\
+\
+    _t1 = _mm_slli_epi32( _in, 1 ); \
+    _t2 = _mm_srai_epi32( _in, 31); \
+    _t2 = _mm_shuffle_epi32( _t2, _MM_SHUFFLE( 2, 1, 0, 3 ) ); \
+    _t2 = _mm_and_si128( _t2, XTS_ALPHA_MASK ); \
+    _res = _mm_xor_si128( _t1, _t2 ); \
+}
+
+// Like XTS_MUL_ALPHA_old but operate on __m512i for _in and _res.
+// TODO: do this with VSHUFPS.
+#define XTS_MUL_ALPHA_ZMM_old( _in, _res ) \
+{\
+    __m512i _t1, _t2;\
+\
+    _t1 = _mm512_slli_epi32( _in, 1 ); \
+    _t2 = _mm512_srli_epi32( _in, 31); \
+    _t1 = _mm512_xor_si512( _t1, _mm512_bslli_epi128( _t2, 4 )); \
+    _t2 = _mm512_bsrli_epi128( _t2, 12 ); \
+    _t2 = _mm512_sub_epi32( _mm512_add_epi32( _mm512_slli_epi32( _t2, 7 ), _mm512_slli_epi32( _t2, 3 ) ), _t2 ); \
+    _res = _mm512_xor_si512( _t1, _t2 ); \
+}
+
+// Multiply by ALPHA^2
+// t1 = Input <<<< 2
+// t2 = Input >>>> 30
+// t1 = t1 ^ (t2 << 32)
+// t2 = t2 >> 96
+// t2 = (t2 <<<< 7) ^ (t2 <<<< 2) ^ (t2 <<<< 1) ^ t2
+// res = t1 ^ t2
+#define XTS_MUL_ALPHA2( _in, _res ) \
+{\
+    __m128i _t1, _t2;\
+\
+    _t1 = _mm_slli_epi32( _in, 2 ); \
+    _t2 = _mm_srli_epi32( _in, 30); \
+    _t1 = _mm_xor_si128( _t1, _mm_slli_si128( _t2, 4 )); \
+    _t2 = _mm_srli_si128( _t2, 12 ); \
+    _t2 = _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_slli_epi32( _t2, 7 ), _mm_slli_epi32( _t2, 2 ) ), _mm_slli_epi32( _t2, 1 )), _t2 ); \
+    _res = _mm_xor_si128( _t1, _t2 ); \
+}
+
+// Multiply by ALPHA^4
+// t1 = Input <<<< 4
+// t2 = Input >>>> 28
+// t1 = t1 ^ (t2 << 32)
+// t2 = t2 >> 96
+// t2 = (t2 <<<< 7) ^ (t2 <<<< 2) ^ (t2 <<<< 1) ^ t2
+// res = t1 ^ t2
+#define XTS_MUL_ALPHA4( _in, _res ) \
+{\
+    __m128i _t1, _t2;\
+\
+    _t1 = _mm_slli_epi32( _in, 4 ); \
+    _t2 = _mm_srli_epi32( _in, 28); \
+    _t1 = _mm_xor_si128( _t1, _mm_slli_si128( _t2, 4 )); \
+    _t2 = _mm_srli_si128( _t2, 12 ); \
+    _t2 = _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_slli_epi32( _t2, 7 ), _mm_slli_epi32( _t2, 2 ) ), _mm_slli_epi32( _t2, 1 )), _t2 ); \
+    _res = _mm_xor_si128( _t1, _t2 ); \
+}
+
+#define XTS_MUL_ALPHA5( _in, _res ) \
+{\
+    __m128i _t1, _t2;\
+\
+    _t1 = _mm_slli_epi32( _in, 5 ); \
+    _t2 = _mm_srli_epi32( _in, 27); \
+    _t1 = _mm_xor_si128( _t1, _mm_slli_si128( _t2, 4 )); \
+    _t2 = _mm_srli_si128( _t2, 12 ); \
+    _t2 = _mm_xor_si128( _mm_xor_si128( _mm_xor_si128( _mm_slli_epi32( _t2, 7 ), _mm_slli_epi32( _t2, 2 ) ), _mm_slli_epi32( _t2, 1 )), _t2 ); \
+    _res = _mm_xor_si128( _t1, _t2 ); \
+}
+
+
+// Multiply by ALPHA^8
+// t2 = Input >> 120
+// t2 = (t2 <<<< 7) ^ (t2 <<<< 2) ^ (t2 <<<< 1) ^ t2
+// res = (Input << 8) ^ t2
+//
+// Only currently used with VPCLMULQDQ (in Ymm / Zmm versions) as support for non-vectorized PCLMULQDQ is not always supported with AESNI,
+// and is sometimes slower than shift+xor
+
+// __m256i XTS_ALPHA_MULTIPLIER_Ymm = _mm256_set_epi64x( 0, 0x87, 0, 0x87);
+#define XTS_MUL_ALPHA8_YMM( _in, _res ) \
+{\
+    __m256i _t2;\
+\
+    _t2 = _mm256_srli_si256( _in, 15 ); /* AVX2 */ \
+    _res = _mm256_slli_si256( _in, 1 ); \
+    _t2 = _mm256_clmulepi64_epi128( _t2, XTS_ALPHA_MULTIPLIER_Ymm, 0x00 ); \
+    _res = _mm256_xor_si256( _res, _t2 ); \
+}
+
+#define XTS_MUL_ALPHA16_YMM( _in, _res ) \
+{\
+    __m256i _t2;\
+\
+    _t2 = _mm256_srli_si256( _in, 14 ); /* AVX2 */ \
+    _res = _mm256_slli_si256( _in, 2 ); \
+    _t2 = _mm256_clmulepi64_epi128( _t2, XTS_ALPHA_MULTIPLIER_Ymm, 0x00 ); \
+    _res = _mm256_xor_si256( _res, _t2 ); \
+}
+
+// __m512i XTS_ALPHA_MULTIPLIER_Zmm = _mm512_set_epi64( 0, 0x87, 0, 0x87, 0, 0x87, 0, 0x87 );
+#define XTS_MUL_ALPHA8_ZMM( _in, _res ) \
+{\
+    __m512i _t2; \
+\
+    _t2 = _mm512_bsrli_epi128( _in, 15 ); \
+    _res = _mm512_bslli_epi128( _in, 1 ); \
+    _t2 = _mm512_clmulepi64_epi128( _t2, XTS_ALPHA_MULTIPLIER_Zmm, 0x00 ); \
+    _res = _mm512_xor_si512( _res, _t2 ); \
+}
+
+#define XTS_MUL_ALPHA16_ZMM( _in, _res ) \
+{\
+    __m512i _t2; \
+\
+    _t2 = _mm512_bsrli_epi128( _in, 14 ); \
+    _res = _mm512_bslli_epi128( _in, 2 ); \
+    _t2 = _mm512_clmulepi64_epi128( _t2, XTS_ALPHA_MULTIPLIER_Zmm, 0x00 ); \
+    _res = _mm512_xor_si512( _res, _t2 ); \
+}
+
+// Currently only use UINT64 for x86 and amd64 - this does regress perf on x86
+// but we don't expect a lot of XTS in x86. If the regression causes any real problems
+// we can consider introducing another variant. Not doing this now to avoid code bloat
+#define XTS_MUL_ALPHA_Scalar( _inout_low_u64, _inout_high_u64 ) \
+{ \
+    UINT64 tmp = (UINT64) ((INT64)_inout_high_u64 >> 63); \
+    \
+    _inout_high_u64 = (_inout_high_u64 << 1) ^ (_inout_low_u64 >> 63); \
+    _inout_low_u64 = (_inout_low_u64 << 1) ^ (tmp & 0x87); \
+}
diff --git a/libs/symcrypt/lib/xtsaes_pattern.c b/libs/symcrypt/lib/xtsaes_pattern.c
new file mode 100644
index 000000000000..f6199cd5d55e
--- /dev/null
+++ b/libs/symcrypt/lib/xtsaes_pattern.c
@@ -0,0 +1,90 @@
+//
+// xtsaes_pattern.c
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
+//
+
+#if 0
+#pragma makedep header
+#endif
+
+VOID
+SYMCRYPT_CALL
+SYMCRYPT_XtsAesXxx(
+    _In_                                    PCSYMCRYPT_XTS_AES_EXPANDED_KEY pExpandedKey,
+                                            SIZE_T                          cbDataUnit,
+    _In_reads_( SYMCRYPT_AES_BLOCK_SIZE )   PCBYTE                          pbTweak,
+    _In_reads_( cbData )                    PCBYTE                          pbSrc,
+    _Out_writes_( cbData )                  PBYTE                           pbDst,
+                                            SIZE_T                          cbData,
+                                            BOOLEAN                         bOverflow )
+{
+    SYMCRYPT_XTS_AES_LOCALSCRATCH_DEFN;
+    // SYMCRYPT_ALIGN BYTE localScratch[N_PARALLEL_TWEAKS * SYMCRYPT_AES_BLOCK_SIZE];
+    // or
+    // /* Defining localScratch as a buffer of __m128is ensures there is required 16B alignment on x86 */
+    // __m128i localScratch[ N_PARALLEL_TWEAKS + 16 ];
+    // Note that the extra 16 __m128i space is used for internal scratch space for SymCryptXtsAesEncryptDataUnitXmm
+    // This allows modified tweak generation to be performed in scalar registers in parallel with AES in Xmm register
+    // which reduces register pressure and increases throughput
+    PBYTE   tweakBuf    = (PBYTE) &localScratch[0];
+    SIZE_T  i, tweakBytes;
+    UINT64  tweakLow64  = SYMCRYPT_LOAD_LSBFIRST64(pbTweak);
+    UINT64  tweakHigh64 = SYMCRYPT_LOAD_LSBFIRST64(pbTweak+8);
+    UINT64  previousTweakLow64;
+
+    SYMCRYPT_ASSERT( cbData % cbDataUnit == 0 );
+
+    while( cbData >= cbDataUnit )
+    {
+        //
+        // We encrypt the tweaks of many data units in parallel for best performance.
+        // In the first loop we build the tweaks and decrement cbData.
+        // In the second loop we use up all the tweaks, and update the pointers.
+        // Both loops are executed the same number of times.
+        //
+        tweakBytes = 0;
+        previousTweakLow64 = tweakLow64;
+
+        do // do-while because we know we are going to go through at least once.
+        {
+            SYMCRYPT_STORE_LSBFIRST64(&tweakBuf[tweakBytes    ], tweakLow64);
+            SYMCRYPT_STORE_LSBFIRST64(&tweakBuf[tweakBytes + 8], tweakHigh64);
+            tweakLow64++;
+            cbData -= cbDataUnit;
+            tweakBytes += SYMCRYPT_AES_BLOCK_SIZE;
+        } while( cbData >= cbDataUnit && tweakBytes < SYMCRYPT_AES_BLOCK_SIZE * N_PARALLEL_TWEAKS );
+
+        if( bOverflow && previousTweakLow64 > tweakLow64 )
+        {
+            // Very rare fix-up of tweaks if tweakLow64 overflowed, and should have incremented tweakHigh64
+            // bOverflow=FALSE allows backwards compatibility with old API which wrapped around at 64-bits
+            SYMCRYPT_ASSERT( tweakLow64 < N_PARALLEL_TWEAKS );
+
+            // Increment tweakHigh64 and store new value in high half of the previous tweakLow64 tweaks
+            tweakHigh64++;
+            for( i=0; i<tweakLow64; i++)
+            {
+                SYMCRYPT_STORE_LSBFIRST64(&tweakBuf[tweakBytes - (16*i) - 8], tweakHigh64);
+            }
+        }
+
+        SYMCRYPT_AesEcbEncryptXxx( &pExpandedKey->key2, &tweakBuf[0], &tweakBuf[0], tweakBytes );
+
+        i = 0;
+        while( i < tweakBytes )
+        {
+            SYMCRYPT_XTSAESDATAUNIT_INVOKE;
+            // SymCryptXtsAesXxcryptDataUnitXxx( &pExpandedKey->key1, &tweakBuf[i], pbSrc, pbDst, cbDataUnit );
+            // or
+            // SymCryptXtsAesXxcryptDataUnitXxx( &pExpandedKey->key1, &tweakBuf[i], (PBYTE) &localScratch[N_PARALLEL_TWEAKS], pbSrc, pbDst, cbDataUnit );
+            // Note that the scratch space being provided to the DataUnit function is an offset into the localScratch buffer
+
+            pbSrc += cbDataUnit;
+            pbDst += cbDataUnit;
+            i += SYMCRYPT_AES_BLOCK_SIZE;
+        }
+    }
+
+    SymCryptWipeKnownSize( localScratch, sizeof( localScratch ) );
+}
diff --git a/libs/symcrypt/make_symcrypt b/libs/symcrypt/make_symcrypt
new file mode 100755
index 000000000000..bd0c984fe80a
--- /dev/null
+++ b/libs/symcrypt/make_symcrypt
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# Script to import symcrypt
+#
+# Run from the top of the Wine source dir
+#
+
+tag=v103.11.0
+
+set -e
+
+cd libs/symcrypt
+
+# update files
+if test "$1" = "-u"
+then
+    test -d SymCrypt/.git || git clone https://github.com/microsoft/SymCrypt.git
+
+    (cd SymCrypt
+     git checkout $tag
+     python3 scripts/version.py -b
+     sed -i 's!#define SYMCRYPT_BUILD_INFO_TIMESTAMP .*!#define SYMCRYPT_BUILD_INFO_TIMESTAMP ""!' inc/buildInfo.h)
+
+    for i in $(git ls-files inc lib)
+    do
+        test -f SymCrypt/$i || continue
+        sed -e 's!\r!!' -e 's!  *$!!' SymCrypt/$i >$i
+    done
+fi
+
+for i in \
+	aesasm \
+	fdef369_asm \
+	fdef_asm \
+	fdef_mulx \
+	sha256xmm_asm \
+	wipe
+do
+    if test -f lib/amd64/$i.symcryptasm
+    then
+        python3 scripts/symcryptasm_processor.py gas-pe amd64 msft lib/amd64/$i.symcryptasm lib/amd64/$i.S
+    fi
+    if test -f lib/arm64/$i.symcryptasm
+    then
+        python3 scripts/symcryptasm_processor.py gas-pe arm64 aapcs64 lib/arm64/$i.symcryptasm lib/arm64/$i.S
+        python3 scripts/symcryptasm_processor.py gas-pe arm64 arm64ec lib/arm64/$i.symcryptasm lib/arm64ec/$i.S
+    fi
+    if test -f lib/arm/$i.symcryptasm
+    then
+        python3 scripts/symcryptasm_processor.py gas-pe arm aapcs32 lib/arm/$i.symcryptasm lib/arm/$i.S
+    fi
+done
+
+exit 0
diff --git a/libs/symcrypt/scripts/symcryptasm_processor.py b/libs/symcrypt/scripts/symcryptasm_processor.py
new file mode 100755
index 000000000000..e92664c4f7d9
--- /dev/null
+++ b/libs/symcrypt/scripts/symcryptasm_processor.py
@@ -0,0 +1,1358 @@
+#!/usr/bin/env python3
+"""
+This script enables processing of symcryptasm files so that they can be assembled in a variety of
+environments without requiring forking or duplication of source files - symcryptasm files phrase
+assembly in an assembler and environment agnostic way.
+
+The current target assemblers are:
+    MASM, GAS, and armasm64 (Arm64 assembler which ships with MSVC)
+The current target environments are:
+    amd64 Windows (using the Microsoft x64 calling convention),
+    amd64 Linux (using the SystemV amd64 calling convention),
+    arm64 Windows (using the aapcs64 calling convention),
+    arm64 Windows (using the arm64ec calling convention), and
+    arm64 Linux (using the aapcs64 calling convention)
+
+
+The plan is to rephrase all remaining .asm in SymCrypt as symcryptasm, extending support as
+appropriate to enable this effort.
+
+Normally the processing of symcryptasm files takes place in 2 passes. The first pass is performed by
+this symcryptasm_processor.py script, which does the more stateful processing, outputting a .cppasm
+file. If the processed symcryptasm file includes other files via the INCLUDE directive, the contents
+of the included files are merged at their point of inclusion to generate a single expanded symcryptasm
+file which is saved with a .symcryptasmexp extension to the output folder. For symcryptasm files which
+do not include other files, there's no corresponding .symcryptasmexp file as it would be identical to
+the source file.
+
+The .cppasm files are further processed by the C preprocessor to do more simple stateless text
+substitutions, outputting a .asm file which can be assembled by the target assembler for the target
+environment.
+The exception is when using the armasm64 assembler, which uses the C preprocessor before assembling
+its inputs already; so the output of this script is directly assembled by armasm64.
+
+We have set up the intermediate generated files to be created in the output directories in both
+razzle and CMake builds.
+
+### symcryptasm syntax ###
+
+Different calling conventions pass arguments to functions in different registers, have differing
+numbers of volatile and non-volatile registers, and use the stack in different ways.
+
+We define our own register naming scheme which abstracts away the differences between calling
+conventions. The generalities of the naming scheme will be similar across target architectures, but
+refer to the Architecture specifics below for details. For the following general information we use
+the notation R<n> to denote registers in the symcryptasm register naming scheme.
+
+
+A leaf function (a function which does not call another function) begins with an invocation of the
+FUNCTION_START macro which currently takes 3 mandatory and 2 optional arguments:
+1) The function name
+    This must be the name that matches the corresponding declaration of the function
+2) The number of arguments (arg_count) that the function takes
+    These arguments will be accessible in some contiguous region of the symcrypt registers at the
+    start of the function
+        On amd64 this contiguous region is R1..R<arg_count>
+        On arm64 this contiguous region is R0..R<arg_count-1>
+    Note: arg_count need not correspond to the exact number of argument in the function declaration
+    if the assembly does not use some tail of the arguments
+3) The number of registers (reg_count) that the function uses
+    These registers will be accessible as R0..R<reg_count-1>
+4) Stack allocation size (stack_alloc_size) for local variables (amd64 only)
+    This parameter is optional, default = 0 if not provided.
+    Allows reserving space for local variables on the stack. This value will be rounded to nearest
+    multiple of 8 and the stack pointer will be aligned on 16B boundary. The allocated space does
+    not include the space needed to save non-volatile registers or shadow space and for function use only.
+    If the function is not nested, rsp will point to the beginning of the buffer after the prologue.
+    Otherwise, rsp will point to shadow space, which is right below the allocated buffer on the stack.
+    In this case, the function has to access the local buffer by using an offset equal to the size of the
+    shadow space (32B).
+5) The number of Xmm registers (xmm_reg_count) that the function uses (amd64 only)
+    This parameter is optional and can take on values [0,16], default = 0 if not provided.
+    If a non-zero value is specified, used registers are assumed to be Xmm0..Xmm<xmm_reg_count-1>
+    When xmm_reg_count > 6, used Xmm registers starting from Xmm6 will be saved on the
+    stack on amd64 Windows environment.
+
+A leaf function ends with the FUNCTION_END macro, which also takes the function name
+    (a FUNCTION_END macro's function name must match the preceding FUNCTION_START's name)
+
+At the function start a prologue is generated which arranges the arguments appropriately in
+registers, and saves non-volatile registers that have been requested to be used.
+At the function end an epilogue is generated which restores the non-volatile registers and returns.
+
+
+A nested function (a function which does call another function) is specified similarly, only using
+NESTED_FUNCTION_START and NESTED_FUNCTION_END macros. A nested function currently updates and aligns
+the stack pointer in the function prologue, and avoids use of the redzone in the SystemV ABI.
+Nested functions are not currently supported for Arm64.
+
+
+A macro begins with an invocation of the MACRO_START macro which takes the Macro name, and variable
+number of macros argument names. It ends with MACRO_END.
+
+### Architecture specifics ###
+
+### amd64 ###
+We allow up to 15 registers to be addressed, with the names:
+Q0-Q14 (64-bit registers), D0-D14 (32-bit registers), W0-W14 (16-bit registers), and B0-B14 (8-bit
+registers)
+Xmm0-Xmm5 registers may be used directly in assembly too, as in both amd64 calling conventions we
+currently support, these registers are volatile so do not need any special handling. If the number
+of used Xmm registers specified in FUNCTION_START macro is greater than 6, those registers among
+Xmm6..Xmm15 are saved/restored on amd64 Windows. There is no save/restore for Xmm16-Xmm31 as of now.
+
+On function entry we insert a prologue which ensures:
+Q0 is the result register (the return value of the function, and the low half of a multiplication)
+Q1-Q6 are the first 6 arguments passed to the function
+
+Additionally, there is a special case for functions using mul or mulx instructions, as these
+instructions make rdx a special register. Functions using these instructions may address Q0-Q13,
+and QH. As rdx is used to pass arguments, its value is moved to another register in the function
+prologue. The MUL_FUNCTION_START and MUL_FUNCTION_END macros are used in this case.
+    We currently do not support nested mul functions, as we have none of them.
+
+Stack layout for amd64 is as follows. Xmm registers are volatile and not saved on Linux.
+
+            Memory          Exists if
+    |-------------------|
+    |                   |
+    |   Shadow space    |
+    |                   |
+    |-------------------|
+    |   Return address  |
+    |-------------------|
+    |   Non-volatile    |
+    |   general purpose |   reg_count > volatile_registers
+    |   registers       |
+    |-------------------|
+    |   Non-volatile    |
+    |   Xmm registers   |   xmm_reg_count > 6 and Windows
+    |  (Windows only)   |
+    |-------------------|---> 16B aligned
+    |                   |
+    |   Local variables |   stack_alloc_size > 0
+    |                   |
+    |-------------------|---> 16B aligned
+    |                   |
+    |   Shadow space    |   nested
+    | (nested function) |
+    |-------------------|---> 16B aligned
+
+
+
+### arm64 ###
+We allow up to 23 registers to be addressed, with the names:
+X_0-X_22 (64-bit registers) and W_0-W_22 (32-bit registers)
+v0-v7 ASIMD registers may by used directly in assembly too, as in both arm64 calling conventions we
+currently support, these registers are volatile so do not need any special handling
+
+X_0 is always the result register and the first argument passed to the function.
+X_1-X_7 are the arguments 2-8 passed to the function
+
+
+### arm (32) ###
+We allow the registers r0-r12 to be addressed as r13-r15 are special registers that we cannot use as general purpose registers.
+As r12 is volatile in a leaf function, it should be used in preference to r4, to avoid spilling/restoring a register.
+
+"""
+
+import re
+import types
+import logging
+import os
+
+class Register:
+    """A class to represent registers"""
+
+    def __init__(self, name64, name32, name16=None, name8=None):
+        self.name64 = name64
+        self.name32 = name32
+        self.name16 = name16
+        self.name8  = name8
+
+# amd64 registers
+AMD64_RAX = Register("rax",  "eax",   "ax",   "al")
+AMD64_RBX = Register("rbx",  "ebx",   "bx",   "bl")
+AMD64_RCX = Register("rcx",  "ecx",   "cx",   "cl")
+AMD64_RDX = Register("rdx",  "edx",   "dx",   "dl")
+AMD64_RSI = Register("rsi",  "esi",   "si",  "sil")
+AMD64_RDI = Register("rdi",  "edi",   "di",  "dil")
+AMD64_RSP = Register("rsp",  "esp",   "sp",  "spl")
+AMD64_RBP = Register("rbp",  "ebp",   "bp",  "bpl")
+AMD64_R8  = Register( "r8",  "r8d",  "r8w",  "r8b")
+AMD64_R9  = Register( "r9",  "r9d",  "r9w",  "r9b")
+AMD64_R10 = Register("r10", "r10d", "r10w", "r10b")
+AMD64_R11 = Register("r11", "r11d", "r11w", "r11b")
+AMD64_R12 = Register("r12", "r12d", "r12w", "r12b")
+AMD64_R13 = Register("r13", "r13d", "r13w", "r13b")
+AMD64_R14 = Register("r14", "r14d", "r14w", "r14b")
+AMD64_R15 = Register("r15", "r15d", "r15w", "r15b")
+
+# arm64 registers
+ARM64_R0  = Register( "x0",  "w0")
+ARM64_R1  = Register( "x1",  "w1")
+ARM64_R2  = Register( "x2",  "w2")
+ARM64_R3  = Register( "x3",  "w3")
+ARM64_R4  = Register( "x4",  "w4")
+ARM64_R5  = Register( "x5",  "w5")
+ARM64_R6  = Register( "x6",  "w6")
+ARM64_R7  = Register( "x7",  "w7")
+ARM64_R8  = Register( "x8",  "w8")
+ARM64_R9  = Register( "x9",  "w9")
+ARM64_R10 = Register("x10", "w10")
+ARM64_R11 = Register("x11", "w11")
+ARM64_R12 = Register("x12", "w12")
+ARM64_R13 = Register("x13", "w13")
+ARM64_R14 = Register("x14", "w14")
+ARM64_R15 = Register("x15", "w15")
+ARM64_R16 = Register("x16", "w16")
+ARM64_R17 = Register("x17", "w17")
+ARM64_R18 = Register("x18", "w18")
+ARM64_R19 = Register("x19", "w19")
+ARM64_R20 = Register("x20", "w20")
+ARM64_R21 = Register("x21", "w21")
+ARM64_R22 = Register("x22", "w22")
+ARM64_R23 = Register("x23", "w23")
+ARM64_R24 = Register("x24", "w24")
+ARM64_R25 = Register("x25", "w25")
+ARM64_R26 = Register("x26", "w26")
+ARM64_R27 = Register("x27", "w27")
+ARM64_R28 = Register("x28", "w28")
+ARM64_R29 = Register("x29", "w29") # Frame Pointer
+ARM64_R30 = Register("x30", "w30") # Link Register
+
+# arm32 registers
+ARM32_R0  = Register(None,  "r0")
+ARM32_R1  = Register(None,  "r1")
+ARM32_R2  = Register(None,  "r2")
+ARM32_R3  = Register(None,  "r3")
+ARM32_R4  = Register(None,  "r4")
+ARM32_R5  = Register(None,  "r5")
+ARM32_R6  = Register(None,  "r6")
+ARM32_R7  = Register(None,  "r7")
+ARM32_R8  = Register(None,  "r8")
+ARM32_R9  = Register(None,  "r9")
+ARM32_R10 = Register(None, "r10")
+ARM32_R11 = Register(None, "r11")
+ARM32_R12 = Register(None, "r12")
+ARM32_R13 = Register(None, "r13")
+ARM32_R14 = Register(None, "r14")
+ARM32_R15 = Register(None, "r15")
+
+# Assembler constants
+ASSEMBLER_MASM      = "masm"
+ASSEMBLER_ARMASM64  = "armasm64"
+ASSEMBLER_GAS       = "gas"
+ASSEMBLER_GAS_MACHO = "gas-macho"
+ASSEMBLER_GAS_PE    = "gas-pe"
+
+class CallingConvention:
+    """A class to represent calling conventions"""
+
+    def __init__(self, name, architecture, mapping, max_arguments, argument_registers, volatile_registers, gen_prologue_fn, gen_epilogue_fn, gen_get_memslot_offset_fn):
+        self.name = name
+        self.architecture = architecture
+        self.mapping = mapping
+        self.max_arguments = max_arguments
+        self.argument_registers = argument_registers
+        self.volatile_registers = volatile_registers
+        self.gen_prologue_fn = types.MethodType(gen_prologue_fn, self)
+        self.gen_epilogue_fn = types.MethodType(gen_epilogue_fn, self)
+        self.gen_get_memslot_offset_fn = types.MethodType(gen_get_memslot_offset_fn, self)
+
+
+def get_mul_mapping_from_normal_mapping(mapping, argument_registers):
+    """Gets the register mapping used in functions requiring special rdx handling.
+
+    In amd64, when using mul and mulx, rdx is a special register.
+    rdx is also used for passing arguments in both Msft and System V calling conventions.
+    In asm functions that use mul or mulx, we will explicitly move the argument passed in
+    rdx to a different volatile register in the function prologue, and in the function body
+    we refer to rdx using (Q|D|W|B)H.
+    """
+    rdx_index = None
+    return_mapping = { 'H': AMD64_RDX }
+    for (index, register) in mapping.items():
+        if register == AMD64_RDX:
+            rdx_index = index
+            break
+    for (index, register) in mapping.items():
+        # preserve argument registers
+        if (index <= argument_registers) and (index != rdx_index):
+            return_mapping[index] = register
+        # replace rdx with the first non-argument register
+        if index == argument_registers+1:
+            return_mapping[rdx_index] = register
+        # shuffle all later registers down to fill the gap
+        if index > argument_registers+1:
+            return_mapping[index-1] = register
+    return return_mapping
+
+# Microsoft x64 calling convention
+MAPPING_AMD64_MSFT = {
+    0: AMD64_RAX, # Result register / volatile
+    1: AMD64_RCX, # Argument 1 / volatile
+    2: AMD64_RDX, # Argument 2 / volatile
+    3: AMD64_R8,  # Argument 3 / volatile
+    4: AMD64_R9,  # Argument 4 / volatile
+    5: AMD64_R10, # volatile
+    6: AMD64_R11, # volatile
+    7: AMD64_RSI, # All registers from rsi are non-volatile and need to be saved/restored in epi/prologue
+    8: AMD64_RDI,
+    9: AMD64_RBP,
+    10:AMD64_RBX,
+    11:AMD64_R12,
+    12:AMD64_R13,
+    13:AMD64_R14,
+    14:AMD64_R15,
+    # currently not mapping rsp
+}
+
+
+def calc_amd64_stack_allocation_sizes(self, reg_count, stack_alloc_size, xmm_reg_count, nested):
+    """ Calculate the sizes of different regions on the stack for adjusting the stack pointer
+        in the function prologue/epilogue accordingly.
+
+        Given the number of general purpose and Xmm registers used by a function and the amount of
+        local buffer it requires, this function calculates and returns as a tuple:
+            1 - reg_save_size : space required to save general purpose registers
+            2 - xmm_save_size : space required to save Xmm registers if any, including 16B alignment
+                                padding if necessary
+            3 - stack_alloc_aligned_size : space required to store local variables based on the requested
+                                buffer size in stack_alloc_size, rounded to multiple of 8 and 16B aligned
+            4 - shadow_space_allocation_size : space required for shadow store/home location if the function
+                                is nested, including 16B alignment padding if necessary
+    """
+
+    # Keep track of stack alignment during each step
+    # We assume rsp % 16 is either 0 or 8 all the time
+    # Initially we have rsp % 16 = 8 because return address is pushed on 16B aligned stack
+    aligned_on_16B = False
+
+    # Calculate the space needed to save general purpose registers on the stack
+    saved_reg_gp = 0 if reg_count <= self.volatile_registers else (reg_count - self.volatile_registers)
+    reg_save_size = 8 * saved_reg_gp
+    if saved_reg_gp % 2:
+        aligned_on_16B = True
+
+    # Calculate the space needed to save Xmm registers on the stack
+    saved_reg_xmm = 0 if xmm_reg_count <= 6 else (xmm_reg_count - 6)
+    xmm_save_size = 16 * saved_reg_xmm
+    if xmm_save_size > 0 and not aligned_on_16B:
+        xmm_save_size += 8
+        aligned_on_16B = True
+
+    # Calculate space needed for local buffer
+    # Round the requested buffer size to multiple of 8 and align it on 16B boundary
+    stack_alloc_aligned_size = 0
+    if stack_alloc_size > 0:
+        stack_alloc_qwords = (stack_alloc_size + 7) // 8
+        stack_alloc_adjusted_size = 8 * stack_alloc_qwords
+        stack_alloc_aligned_size = stack_alloc_adjusted_size
+        if aligned_on_16B ^ ((stack_alloc_aligned_size % 16) == 0):
+            stack_alloc_aligned_size += 8
+            aligned_on_16B = True
+
+    # If we are a nested function, we need to align the stack to 16B, and allocate space for up to 4
+    # memory slots not in the redzone. We can use the same logic as on the MSFT x64 side to allocate
+    # our own space for 32B of local variables (whereas on the MSFT side, we use this for allocating
+    # space for a function we are about to call)
+    shadow_space_allocation_size = 32 + 8 * (not aligned_on_16B) if nested else 0
+
+    return reg_save_size, xmm_save_size, stack_alloc_aligned_size, shadow_space_allocation_size
+
+
+
+def gen_prologue_amd64_msft(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, mul_fixup="", nested=False):
+
+    prologue = "\n"
+
+    # Calculate the sizes of the buffers needed for saving registers, local variable buffer and shadow space.
+    # Each of the sections other than general purpose registers are aligned on 16B boundary and some of them
+    # may include an 8B padding in their size.
+    reg_save_size, xmm_save_size, stack_alloc_aligned_size, shadow_space_allocation_size = calc_amd64_stack_allocation_sizes(
+        self, reg_count, stack_alloc_size, xmm_reg_count, nested)
+
+    # Save general purpose registers
+    if reg_count > self.volatile_registers:
+        prologue += ".byte 0x40; push Q%s\n.seh_pushreg Q%s\n" % (self.volatile_registers, self.volatile_registers)
+        for i in range(self.volatile_registers+1, reg_count):
+            prologue += "push Q%s\n.seh_pushreg Q%s\n" % (i, i)
+
+    # Allocate space on the stack
+    stack_total_size = xmm_save_size + stack_alloc_aligned_size + shadow_space_allocation_size
+    if stack_total_size > 0:
+        prologue += "sub rsp, %d\n.seh_stackalloc %d\n" % (stack_total_size, stack_total_size)
+
+    # Save Xmm registers
+    if xmm_save_size > 0:
+        for i in range(6, xmm_reg_count):
+            prologue += "movdqa xmmword ptr [rsp + %d], xmm%d\n.seh_savexmm xmm%d, %d\n" % (shadow_space_allocation_size + stack_alloc_aligned_size + 16 * (i - 6), i, i, shadow_space_allocation_size + stack_alloc_aligned_size + 16 * (i - 6))
+
+    prologue += "\n.seh_endprologue\n\n"
+
+    prologue += mul_fixup
+
+    # put additional arguments into Q5-Q6 (we do not support more than 6 arguments for now)
+    # stack_offset to get the 5th argument is:
+    # 32B of shadow space + 8B return address + (8*#pushed registers in prologue) + (16*#saved xmm registers in prologue) + local buffer size + shadow_space_allocation_size
+    stack_offset = 32 + 8 + reg_save_size + xmm_save_size + stack_alloc_aligned_size + shadow_space_allocation_size
+    for i in range(self.argument_registers+1, arg_count+1):
+        prologue += "mov Q%s, [rsp + %d]\n" % (i, stack_offset)
+        stack_offset += 8
+    return prologue
+
+def gen_prologue_amd64_msft_mul(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_prologue_amd64_msft(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, mul_fixup = "mov Q2, QH\n", nested = False)
+
+def gen_prologue_amd64_msft_nested(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_prologue_amd64_msft(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, mul_fixup = "", nested = True)
+
+def gen_epilogue_amd64_msft(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested = False):
+
+    epilogue = "\n"
+
+    reg_save_size, xmm_save_size, stack_alloc_aligned_size, shadow_space_allocation_size = calc_amd64_stack_allocation_sizes(
+        self, reg_count, stack_alloc_size, xmm_reg_count, nested)
+
+    # Restore non-volatile Xmm registers
+    if xmm_save_size > 0:
+        for i in range(6, xmm_reg_count):
+            epilogue += "movdqa xmm%d, xmmword ptr [rsp + %d]\n" % (i, shadow_space_allocation_size + stack_alloc_aligned_size + 16 * (i - 6))
+
+    # Restore stack pointer
+    stack_total_size = xmm_save_size + stack_alloc_aligned_size + shadow_space_allocation_size
+    if stack_total_size > 0:
+        epilogue += "add rsp, %d\n" % stack_total_size
+
+    #epilogue += "BEGIN_EPILOGUE\n"
+
+    if reg_count > self.volatile_registers:
+        for i in reversed(range(self.volatile_registers, reg_count)):
+            epilogue += "pop Q%s\n" % i
+
+    epilogue += "ret\n"
+    return epilogue
+
+def gen_epilogue_amd64_msft_nested(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_epilogue_amd64_msft(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested = True)
+
+def gen_get_memslot_offset_amd64_msft(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested = False):
+    # only support 4 memory slots for now (in shadow space)
+    if(slot >= 4):
+        logging.error("symcryptasm currently only support 4 memory slots! (requested slot%d)" % slot)
+        exit(1)
+
+    # 8B for return address + (8*#pushed registers in prologue) + (16*#saved XMM registers) + local buffer size + shadow space
+    reg_save_size, xmm_save_size, stack_alloc_aligned_size, shadow_space_allocation_size = calc_amd64_stack_allocation_sizes(
+        self, reg_count, stack_alloc_size, xmm_reg_count, nested)
+    stack_offset = 8 + reg_save_size + xmm_save_size + stack_alloc_aligned_size + shadow_space_allocation_size
+    return "%d /*MEMSLOT%d*/" % (stack_offset+(8*slot), slot)
+
+def gen_get_memslot_offset_amd64_msft_nested(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_get_memslot_offset_amd64_msft(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested = True)
+
+CALLING_CONVENTION_AMD64_MSFT = CallingConvention(
+    "msft_x64", "amd64", MAPPING_AMD64_MSFT, 6, 4, 7,
+    gen_prologue_amd64_msft, gen_epilogue_amd64_msft, gen_get_memslot_offset_amd64_msft)
+CALLING_CONVENTION_AMD64_MSFT_MUL = CallingConvention(
+    "msft_x64", "amd64", get_mul_mapping_from_normal_mapping(MAPPING_AMD64_MSFT, 4), 6, 4, 6,
+    gen_prologue_amd64_msft_mul, gen_epilogue_amd64_msft, gen_get_memslot_offset_amd64_msft)
+CALLING_CONVENTION_AMD64_MSFT_NESTED = CallingConvention(
+    "msft_x64", "amd64", MAPPING_AMD64_MSFT, 6, 4, 7,
+    gen_prologue_amd64_msft_nested, gen_epilogue_amd64_msft_nested, gen_get_memslot_offset_amd64_msft_nested)
+
+# AMD64 System V calling convention
+MAPPING_AMD64_SYSTEMV = {
+    0: AMD64_RAX, # Result register / volatile
+    1: AMD64_RDI, # Argument 1 / volatile
+    2: AMD64_RSI, # Argument 2 / volatile
+    3: AMD64_RDX, # Argument 3 / volatile
+    4: AMD64_RCX, # Argument 4 / volatile
+    5: AMD64_R8,  # Argument 5 / volatile
+    6: AMD64_R9,  # Argument 6 / volatile
+    7: AMD64_R10, # volatile
+    8: AMD64_R11, # volatile
+    9: AMD64_RBX, # All registers from rbx are non-volatile and need to be saved/restored in epi/prologue
+    10:AMD64_RBP,
+    11:AMD64_R12,
+    12:AMD64_R13,
+    13:AMD64_R14,
+    14:AMD64_R15
+    # currently not mapping rsp
+}
+
+def gen_prologue_amd64_systemv(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, mul_fixup = "", nested = False):
+
+    # Calculate the sizes required for each section
+    # We need to call with xmm_reg_count=0 to avoid allocation/alignment for saving Xmm registers since they're
+    # volatile for this calling convention.
+    reg_save_size, xmm_save_size, stack_alloc_aligned_size, shadow_space_allocation_size = calc_amd64_stack_allocation_sizes(
+        self, reg_count, stack_alloc_size, 0, nested)
+
+    # push volatile registers onto the stack
+    prologue = "\n"
+    if reg_count > self.volatile_registers:
+        for i in range(self.volatile_registers, reg_count):
+            prologue += "push Q%s\n" % i
+
+    # update stack pointer if local buffer size is nonzero or shadow space exists
+    if stack_alloc_aligned_size + shadow_space_allocation_size > 0:
+        prologue += "sub rsp, %s // allocate local buffer, memslot space and align stack\n\n" % str(stack_alloc_aligned_size + shadow_space_allocation_size)
+
+    prologue += mul_fixup
+
+    # do not support more than 6 arguments for now
+    # # put additional arguments into Q7-Qn
+    # # stack_offset to get the 7th argument is:
+    # # 8B for return address
+    # stack_offset = 8
+    # for i in range(self.argument_registers+1, arg_count+1):
+    #     prologue += "mov Q%s, [rsp + %d]\n" % (i, stack_offset)
+    #     stack_offset += 8
+
+    return prologue
+
+def gen_prologue_amd64_systemv_mul(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_prologue_amd64_systemv(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, mul_fixup = "mov Q3, QH\n", nested = False)
+
+def gen_prologue_amd64_systemv_nested(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_prologue_amd64_systemv(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, mul_fixup = "", nested = True)
+
+def gen_epilogue_amd64_systemv(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested = False):
+
+    epilogue = ""
+
+    # Calculate the sizes required for each section
+    # We need to call with xmm_reg_count=0 to avoid allocation/alignment for saving Xmm registers since they're
+    # volatile for this calling convention.
+    reg_save_size, xmm_save_size, stack_alloc_aligned_size, shadow_space_allocation_size = calc_amd64_stack_allocation_sizes(
+        self, reg_count, stack_alloc_size, 0, nested)
+
+    # update stack pointer if local buffer size is nonzero or shadow space exists
+    if stack_alloc_aligned_size + shadow_space_allocation_size > 0:
+        epilogue += "add rsp, %s // deallocate local buffer, memslot space and align stack\n\n" % str(stack_alloc_aligned_size + shadow_space_allocation_size)
+
+    if reg_count > self.volatile_registers:
+        for i in reversed(range(self.volatile_registers, reg_count)):
+            epilogue += "pop Q%s\n" % i
+    epilogue += "ret\n"
+    return epilogue
+
+def gen_epilogue_amd64_systemv_nested(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_epilogue_amd64_systemv(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested=True)
+
+def gen_get_memslot_offset_amd64_systemv(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested = False):
+    # only support 4 memory slots for now
+    if(slot >= 4):
+        logging.error("symcryptasm currently only support 4 memory slots! (requested slot%d)" % slot)
+        exit(1)
+    # For leaf functions, use the top of the redzone below the stack pointer
+    offset = -8 * (slot+1)
+    if nested:
+        # For nested functions, use the 32B of memslot space above the stack pointer created in the prologue
+        offset = 8*slot
+    return "%d /*MEMSLOT%d*/" % (offset, slot)
+
+def gen_get_memslot_offset_amd64_systemv_nested(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    return gen_get_memslot_offset_amd64_systemv(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested=True)
+
+CALLING_CONVENTION_AMD64_SYSTEMV = CallingConvention(
+    "amd64_systemv", "amd64", MAPPING_AMD64_SYSTEMV, 6, 6, 9,
+    gen_prologue_amd64_systemv, gen_epilogue_amd64_systemv, gen_get_memslot_offset_amd64_systemv)
+CALLING_CONVENTION_AMD64_SYSTEMV_MUL = CallingConvention(
+    "amd64_systemv", "amd64", get_mul_mapping_from_normal_mapping(MAPPING_AMD64_SYSTEMV, 6), 6, 6, 8,
+    gen_prologue_amd64_systemv_mul, gen_epilogue_amd64_systemv, gen_get_memslot_offset_amd64_systemv)
+CALLING_CONVENTION_AMD64_SYSTEMV_NESTED = CallingConvention(
+    "amd64_systemv", "amd64", MAPPING_AMD64_SYSTEMV, 6, 6, 9,
+    gen_prologue_amd64_systemv_nested, gen_epilogue_amd64_systemv_nested, gen_get_memslot_offset_amd64_systemv_nested)
+
+
+# ARM64 calling conventions
+MAPPING_ARM64_AAPCS64 = {
+    0: ARM64_R0,  # Argument 1 / Result register / volatile
+    1: ARM64_R1,  # Argument 2 / volatile
+    2: ARM64_R2,  # Argument 3 / volatile
+    3: ARM64_R3,  # Argument 4 / volatile
+    4: ARM64_R4,  # Argument 5 / volatile
+    5: ARM64_R5,  # Argument 6 / volatile
+    6: ARM64_R6,  # Argument 7 / volatile
+    7: ARM64_R7,  # Argument 8 / volatile
+    8: ARM64_R8,  # Indirect result location / volatile
+    9: ARM64_R9,  # volatile
+    10:ARM64_R10, # volatile
+    11:ARM64_R11, # volatile
+    12:ARM64_R12, # volatile
+    13:ARM64_R13, # volatile
+    14:ARM64_R14, # volatile
+    15:ARM64_R15, # volatile
+    # R16 and R17 are intra-procedure-call temporary registers which may be used by the linker
+    # We cannot use these registers for local scratch if we call out to arbitrary procedures, but
+    # currently we only have leaf functions in Arm64 symcryptasm.
+    16:ARM64_R16, # IP0 / volatile
+    17:ARM64_R17, # IP1 / volatile
+    # R18 is a platform register which has a special meaning in kernel mode - we do not use it
+    18:ARM64_R19, # non-volatile
+    19:ARM64_R20, # non-volatile
+    20:ARM64_R21, # non-volatile
+    21:ARM64_R22, # non-volatile
+    22:ARM64_R23, # non-volatile
+    # We could map more registers (R24-R28) but we can only support 23 registers for ARM64EC, and we
+    # don't use this many registers in any symcryptasm yet
+}
+
+MAPPING_ARM64_ARM64ECMSFT = {
+    0: ARM64_R0,  # Argument 1 / Result register / volatile
+    1: ARM64_R1,  # Argument 2 / volatile
+    2: ARM64_R2,  # Argument 3 / volatile
+    3: ARM64_R3,  # Argument 4 / volatile
+    4: ARM64_R4,  # Argument 5 / volatile
+    5: ARM64_R5,  # Argument 6 / volatile
+    6: ARM64_R6,  # Argument 7 / volatile
+    7: ARM64_R7,  # Argument 8 / volatile
+    8: ARM64_R8,  # Indirect result location / volatile
+    9: ARM64_R9,  # volatile
+    10:ARM64_R10, # volatile
+    11:ARM64_R11, # volatile
+    12:ARM64_R12, # volatile
+    # R13 and R14 are reserved in ARM64EC
+    13:ARM64_R15, # volatile
+    14:ARM64_R16, # volatile
+    15:ARM64_R17, # volatile
+    16:ARM64_R19, # non-volatile
+    17:ARM64_R20, # non-volatile
+    18:ARM64_R21, # non-volatile
+    19:ARM64_R22, # non-volatile
+    # R23 and R24 are reserved in ARM64EC
+    20:ARM64_R25, # non-volatile
+    21:ARM64_R26, # non-volatile
+    22:ARM64_R27, # non-volatile
+    # R28 is reserved in ARM64EC
+}
+
+# ARM32 calling convention
+# A subroutine must preserve the contents of the registers r4-r8, r10, r11 and SP (and r9 in PCS variants that designate r9 as v6).
+MAPPING_ARM32_AAPCS32 = {
+    0: ARM32_R0,  # Argument 1 / Result register / volatile
+    1: ARM32_R1,  # Argument 2 / Result register / volatile
+    2: ARM32_R2,  # Argument 3 / volatile
+    3: ARM32_R3,  # Argument 4 / volatile
+    4: ARM32_R4,  # non-volatile
+    5: ARM32_R5,  # non-volatile
+    6: ARM32_R6,  # non-volatile
+    7: ARM32_R7,  # non-volatile
+    8: ARM32_R8,  # non-volatile
+    9: ARM32_R9,  # reserved for something
+    10:ARM32_R10, # non-volatile
+    11:ARM32_R11, # FP non-volatile
+    12:ARM32_R12, # volatile for leaf functions
+    13:ARM32_R13, # SP
+    14:ARM32_R14, # LR
+    15:ARM32_R15, # PC
+}
+
+def gen_prologue_aapcs32(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    assert(not stack_alloc_size and not xmm_reg_count)
+    prologue = ""
+    # Always spill at least 1 register (LR).
+    # LR needs to be saved for nested functions but for now we'll store it always
+    # since we don't differentiate between nested and leaf functions for arm yet.
+    registers_to_spill = []
+    logging.info(f"prologue {reg_count} > {self.volatile_registers}")
+    if reg_count > self.volatile_registers:
+        for i in range(self.volatile_registers, reg_count):
+            registers_to_spill.append('r%s' % i)
+        # Stack pointer is word 4B aligned
+        # required_stack_space = 4 * len(registers_to_spill)
+    registers_to_spill.append('lr')
+    prologue += "push {" + ",".join(registers_to_spill) + "}\n"
+    if reg_count > 7:
+        prologue += ".seh_save_regs_w {" + ",".join(registers_to_spill) + "}\n"
+    else:
+        prologue += ".seh_save_regs {" + ",".join(registers_to_spill) + "}\n"
+    prologue += ".seh_endprologue\n"
+    return prologue
+
+def gen_epilogue_aapcs32(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    assert(not stack_alloc_size and not xmm_reg_count)
+    epilogue = ""
+
+    registers_to_spill = []
+    logging.info(f"epilogue {reg_count} > {self.volatile_registers}")
+    if reg_count > self.volatile_registers:
+        for i in range(self.volatile_registers, reg_count):
+            registers_to_spill.append('r%s' % i)
+        # Stack pointer is word 4B aligned
+        # required_stack_space = 4 * len(registers_to_spill)
+    registers_to_spill.append('pc')
+    epilogue += "pop {" + ",".join(registers_to_spill) + "}\n"
+    return epilogue
+
+def gen_prologue_arm64_armasm64(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    prologue = ""
+
+    if reg_count > self.volatile_registers:
+        # Calculate required stack space
+        # If we allocate stack space we must spill fp and lr, so we always spill at least 2 registers
+        registers_to_spill = 2 + reg_count - self.volatile_registers
+        # Stack pointer remain 16B aligned, so round up to the nearest multiple of 16B
+        required_stack_space = 16 * ((registers_to_spill + 1) // 2)
+        prologue += "    PROLOG_SAVE_REG_PAIR fp, lr, #-%d! // allocate %d bytes of stack; store FP/LR\n" % (required_stack_space, required_stack_space)
+
+        stack_offset = 16
+        for i in range(self.volatile_registers, reg_count-1, 2):
+            prologue += "    PROLOG_SAVE_REG_PAIR X_%d, X_%d, #%d\n" % (i, i+1, stack_offset)
+            stack_offset += 16
+        if registers_to_spill % 2 == 1:
+            prologue += "    PROLOG_SAVE_REG      X_%d, #%d\n" % (reg_count-1, stack_offset)
+
+    return prologue
+
+def gen_epilogue_arm64_armasm64(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    epilogue = ""
+
+    if reg_count > self.volatile_registers:
+        # Calculate required stack space
+        # If we allocate stack space we must spill fp and lr, so we always spill at least 2 registers
+        registers_to_spill = 2 + reg_count - self.volatile_registers
+        # Stack pointer remain 16B aligned, so round up to the nearest multiple of 16B
+        required_stack_space = 16 * ((registers_to_spill + 1) // 2)
+
+        stack_offset = required_stack_space-16
+        if registers_to_spill % 2 == 1:
+            epilogue += "    EPILOG_RESTORE_REG      X_%d, #%d\n" % (reg_count-1, stack_offset)
+            stack_offset -= 16
+        for i in reversed(range(self.volatile_registers, reg_count-1, 2)):
+            epilogue += "    EPILOG_RESTORE_REG_PAIR X_%d, X_%d, #%d\n" % (i, i+1, stack_offset)
+            stack_offset -= 16
+        epilogue += "    EPILOG_RESTORE_REG_PAIR fp, lr, #%d! // deallocate %d bytes of stack; restore FP/LR\n" % (required_stack_space, required_stack_space)
+        epilogue += "    EPILOG_RETURN\n"
+    else:
+        epilogue += "    ret\n"
+
+    return epilogue
+
+def gen_prologue_arm64_gas(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    prologue = ""
+
+    if reg_count > self.volatile_registers:
+        # Calculate required stack space
+        # If we allocate stack space we must spill fp and lr, so we always spill at least 2 registers
+        registers_to_spill = 2 + reg_count - self.volatile_registers
+        # Stack pointer remain 16B aligned, so round up to the nearest multiple of 16B
+        required_stack_space = 16 * ((registers_to_spill + 1) // 2)
+        prologue += "    stp fp, lr, [sp, #-%d]! // allocate %d bytes of stack; store FP/LR\n" % (required_stack_space, required_stack_space)
+
+        stack_offset = 16
+        for i in range(self.volatile_registers, reg_count-1, 2):
+            prologue += "    stp X_%d, X_%d, [sp, #%d]\n" % (i, i+1, stack_offset)
+            stack_offset += 16
+        if registers_to_spill % 2 == 1:
+            prologue += "    str      X_%d, [sp, #%d]\n" % (reg_count-1, stack_offset)
+
+    return prologue
+
+def gen_prologue_arm64_gas_pe(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    prologue = ""
+
+    if reg_count > self.volatile_registers:
+        # Calculate required stack space
+        # If we allocate stack space we must spill fp and lr, so we always spill at least 2 registers
+        registers_to_spill = 2 + reg_count - self.volatile_registers
+        # Stack pointer remain 16B aligned, so round up to the nearest multiple of 16B
+        required_stack_space = 16 * ((registers_to_spill + 1) // 2)
+        prologue += "    stp fp, lr, [sp, #-%d]! // allocate %d bytes of stack; store FP/LR\n" % (required_stack_space, required_stack_space)
+        prologue += "    .seh_save_fplr_x %d\n" % required_stack_space
+
+        stack_offset = 16
+        for i in range(self.volatile_registers, reg_count-1, 2):
+            prologue += "    stp X_%d, X_%d, [sp, #%d]\n" % (i, i+1, stack_offset)
+            prologue += "    .seh_save_regp X_%d, %d\n" % (i, stack_offset)
+            stack_offset += 16
+        if registers_to_spill % 2 == 1:
+            prologue += "    str      X_%d, [sp, #%d]\n" % (reg_count-1, stack_offset)
+            prologue += "    .seh_save_reg X_%d, %d\n" % (reg_count-1, stack_offset)
+
+    prologue += "    .seh_endprologue\n"
+    return prologue
+
+def gen_epilogue_arm64_gas(self, arg_count, reg_count, stack_alloc_size, xmm_reg_count):
+    epilogue = ""
+
+    if reg_count > self.volatile_registers:
+        # Calculate required stack space
+        # If we allocate stack space we must spill fp and lr, so we always spill at least 2 registers
+        registers_to_spill = 2 + reg_count - self.volatile_registers
+        # Stack pointer remain 16B aligned, so round up to the nearest multiple of 16B
+        required_stack_space = 16 * ((registers_to_spill + 1) // 2)
+
+        stack_offset = required_stack_space-16
+        if registers_to_spill % 2 == 1:
+            epilogue += "    ldr      X_%d, [sp, #%d]\n" % (reg_count-1, stack_offset)
+            stack_offset -= 16
+        for i in reversed(range(self.volatile_registers, reg_count-1, 2)):
+            epilogue += "    ldp X_%d, X_%d, [sp, #%d]\n" % (i, i+1, stack_offset)
+            stack_offset -= 16
+        epilogue += "    ldp fp, lr, [sp], #%d // deallocate %d bytes of stack; restore FP/LR\n" % (required_stack_space, required_stack_space)
+    epilogue += "    ret\n"
+
+    return epilogue
+
+def gen_get_memslot_offset_arm64(self, slot, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested=False):
+    logging.error("symcryptasm currently does not support memory slots for arm64!")
+    exit(1)
+
+CALLING_CONVENTION_ARM64_AAPCS64_ARMASM64 = CallingConvention(
+    "arm64_aapcs64", "arm64", MAPPING_ARM64_AAPCS64, 8, 8, 18,
+    gen_prologue_arm64_armasm64, gen_epilogue_arm64_armasm64, gen_get_memslot_offset_arm64)
+
+CALLING_CONVENTION_ARM64_AAPCS64_GAS = CallingConvention(
+    "arm64_aapcs64", "arm64", MAPPING_ARM64_AAPCS64, 8, 8, 18,
+    gen_prologue_arm64_gas, gen_epilogue_arm64_gas, gen_get_memslot_offset_arm64)
+
+CALLING_CONVENTION_ARM64_AAPCS64_GAS_PE = CallingConvention(
+    "arm64_aapcs64", "arm64", MAPPING_ARM64_AAPCS64, 8, 8, 18,
+    gen_prologue_arm64_gas_pe, gen_epilogue_arm64_gas, gen_get_memslot_offset_arm64)
+
+CALLING_CONVENTION_ARM64EC_GAS_PE = CallingConvention(
+    "arm64ec_msft", "arm64", MAPPING_ARM64_ARM64ECMSFT, 8, 8, 16,
+    gen_prologue_arm64_gas_pe, gen_epilogue_arm64_gas, gen_get_memslot_offset_arm64)
+
+CALLING_CONVENTION_ARM64EC_MSFT = CallingConvention(
+    "arm64ec_msft", "arm64", MAPPING_ARM64_ARM64ECMSFT, 8, 8, 16,
+    gen_prologue_arm64_armasm64, gen_epilogue_arm64_armasm64, gen_get_memslot_offset_arm64)
+
+CALLING_CONVENTION_ARM32_AAPCS32 = CallingConvention(
+    "arm32_aapcs32", "arm32", MAPPING_ARM32_AAPCS32, 4, 4, 4,
+    gen_prologue_aapcs32, gen_epilogue_aapcs32, gen_get_memslot_offset_arm64)
+
+def gen_function_defines(architecture, mapping, arg_count, reg_count, start=True):
+    defines = ""
+    if architecture == "amd64":
+        prefix64 = "Q"
+        prefix32 = "D"
+        prefix16 = "W"
+        prefix8  = "B"
+    elif architecture == "arm64":
+        prefix64 = "X_"
+        prefix32 = "W_"
+    elif architecture == "arm32":
+        return defines
+    else:
+        logging.error("Unhandled architecture (%s) in gen_function_defines" % architecture)
+        exit(1)
+
+    for (index, reg) in mapping.items():
+        if (index != 'H') and (index >= max(arg_count+1, reg_count)):
+            continue
+        if start:
+            if (reg.name64 is not None):
+                defines += "#define %s%s %s\n" % (prefix64, index, reg.name64)
+            if (reg.name32 is not None):
+                defines += "#define %s%s %s\n" % (prefix32, index, reg.name32)
+            if (reg.name16 is not None):
+                defines += "#define %s%s %s\n" % (prefix16, index, reg.name16)
+            if (reg.name8 is not None):
+                defines += "#define %s%s %s\n" % (prefix8,  index, reg.name8)
+        else:
+            if (reg.name64 is not None):
+                defines += "#undef %s%s\n" % (prefix64, index)
+            if (reg.name32 is not None):
+                defines += "#undef %s%s\n" % (prefix32, index)
+            if (reg.name16 is not None):
+                defines += "#undef %s%s\n" % (prefix16, index)
+            if (reg.name8 is not None):
+                defines += "#undef %s%s\n" % (prefix8,  index)
+    return defines
+
+def gen_function_start_defines(architecture, mapping, arg_count, reg_count):
+    return gen_function_defines(architecture, mapping, arg_count, reg_count, start=True)
+
+def gen_function_end_defines(architecture, mapping, arg_count, reg_count):
+    return gen_function_defines(architecture, mapping, arg_count, reg_count, start=False)
+
+def replace_identifier(arg, newarg, line):
+    """Replaces all instances of identifier arg with newarg in string line."""
+    argmatch = r"(^|[^a-zA-Z0-9_])" + arg + r"(?![a-zA-Z0-9_])"
+    line = re.sub(argmatch, r"\1" + newarg, line)
+    return line
+
+MASM_FRAMELESS_FUNCTION_ENTRY   = "LEAF_ENTRY %s"
+MASM_FRAMELESS_FUNCTION_END     = "LEAF_END %s"
+MASM_FRAME_FUNCTION_ENTRY       = "NESTED_ENTRY %s"
+MASM_FRAME_FUNCTION_END         = "NESTED_END %s"
+
+# MASM function macros takes the text area as an argument
+MASM_FUNCTION_TEMPLATE      = "%s, _TEXT\n"
+# ARMASM64 function macros must be correctly indented
+ARMASM64_FUNCTION_TEMPLATE  = "    %s\n"
+
+GAS_FUNCTION_ENTRY_ELF    = "%s: .global %s\n.type %s, %%function\n// .func %s\n"
+GAS_FUNCTION_ENTRY_MACHO  = "%s: .global %s\n// .func %s\n"
+GAS_FUNCTION_END      = "// .endfunc // %s"
+GAS_FUNCTION_ENTRY_PE  = "%s:\n.globl %s\n.def %s; .scl 2; .type 32; .endef\n.seh_proc %s\n"
+GAS_FUNCTION_END_PE    = ".seh_endproc\n"
+
+def generate_prologue(assembler, calling_convention, function_name, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested):
+    function_entry = None
+    if assembler in [ASSEMBLER_MASM, ASSEMBLER_ARMASM64]:
+        # need to identify and mark up frame functions in masm and armasm64
+        # for masm we also consider Xmm register and local buffer use (stack_alloc_size)
+        if assembler == ASSEMBLER_MASM and (nested or (reg_count > calling_convention.volatile_registers) or (xmm_reg_count > 6) or (stack_alloc_size > 0)):
+            function_entry = MASM_FRAME_FUNCTION_ENTRY % (function_name)
+        elif nested or (reg_count > calling_convention.volatile_registers):
+            function_entry = MASM_FRAME_FUNCTION_ENTRY % (function_name)
+        else:
+            function_entry = MASM_FRAMELESS_FUNCTION_ENTRY % (function_name)
+
+        if assembler == ASSEMBLER_MASM:
+            function_entry = MASM_FUNCTION_TEMPLATE % function_entry
+        elif assembler == ASSEMBLER_ARMASM64:
+            function_entry = ARMASM64_FUNCTION_TEMPLATE % function_entry
+    elif assembler == ASSEMBLER_GAS_PE:
+        function_entry = GAS_FUNCTION_ENTRY_PE % (function_name, function_name, function_name, function_name)
+    elif assembler == ASSEMBLER_GAS_MACHO:
+        # macOS uses Mach-O format which doesn't support .type directive
+        function_entry = GAS_FUNCTION_ENTRY_MACHO % (function_name, function_name, function_name)
+    elif assembler == ASSEMBLER_GAS:
+        function_entry = GAS_FUNCTION_ENTRY_ELF % (function_name, function_name, function_name, function_name)
+    else:
+        logging.error("Unhandled assembler (%s) in generate_prologue" % assembler)
+        exit(1)
+
+    prologue = gen_function_start_defines(calling_convention.architecture, calling_convention.mapping, arg_count, reg_count)
+    prologue += "%s" % (function_entry)
+    prologue += calling_convention.gen_prologue_fn(arg_count, reg_count, stack_alloc_size, xmm_reg_count)
+
+    return prologue
+
+def generate_epilogue(assembler, calling_convention, function_name, arg_count, reg_count, stack_alloc_size, xmm_reg_count, nested):
+    function_end = None
+    if assembler in [ASSEMBLER_MASM, ASSEMBLER_ARMASM64]:
+        # need to identify and mark up frame functions in masm
+        # for masm we also consider Xmm register and local buffer use (stack_alloc_size)
+        if assembler == ASSEMBLER_MASM and (nested or (reg_count > calling_convention.volatile_registers) or (xmm_reg_count > 6) or (stack_alloc_size > 0)):
+            function_end = MASM_FRAME_FUNCTION_END % (function_name)
+        elif nested or (reg_count > calling_convention.volatile_registers):
+            function_end = MASM_FRAME_FUNCTION_END % (function_name)
+        else:
+            function_end = MASM_FRAMELESS_FUNCTION_END % (function_name)
+
+        if assembler == ASSEMBLER_MASM:
+            function_end = MASM_FUNCTION_TEMPLATE % function_end
+        elif assembler == ASSEMBLER_ARMASM64:
+            function_end = ARMASM64_FUNCTION_TEMPLATE % function_end
+    elif assembler == ASSEMBLER_GAS_PE:
+            function_end = GAS_FUNCTION_END_PE
+    elif assembler in [ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO]:
+            function_end = GAS_FUNCTION_END % function_name
+    else:
+        logging.error("Unhandled assembler (%s) in generate_epilogue" % assembler)
+        exit(1)
+
+    epilogue = calling_convention.gen_epilogue_fn(arg_count, reg_count, stack_alloc_size, xmm_reg_count)
+    epilogue += "%s" % (function_end)
+    epilogue += gen_function_end_defines(calling_convention.architecture, calling_convention.mapping, arg_count, reg_count)
+
+    return epilogue
+
+MASM_MACRO_START    = "%s MACRO %s\n"
+MASM_MACRO_END      = "ENDM\n"
+ARMASM64_MACRO_START= "    MACRO\n    %s %s"
+ARMASM64_MACRO_END  = "    MEND\n"
+GAS_MACRO_START     = ".macro %s %s\n"
+GAS_MACRO_END       = ".endm\n"
+MASM_ALTERNATE_ENTRY= "ALTERNATE_ENTRY %s\n"
+GAS_ALTERNATE_ENTRY = "%s: .global %s\n"
+ARMASM64_ALTERNATE_ENTRY= "    ALTERNATE_ENTRY %s\n"
+
+FUNCTION_START_PATTERN  = re.compile(r"\s*(NESTED_)?(MUL_)?FUNCTION_START\s*\(\s*([a-zA-Z0-9_\(\)]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*(,\s*[0-9\*\+\-]+)?\s*(,\s*[0-9]+)?\s*\)")
+FUNCTION_END_PATTERN    = re.compile(r"\s*(NESTED_)?(MUL_)?FUNCTION_END\s*\(\s*([a-zA-Z0-9_\(\)]+)\s*\)")
+GET_MEMSLOT_PATTERN     = re.compile(r"GET_MEMSLOT_OFFSET\s*\(\s*slot([0-9]+)\s*\)")
+ALTERNATE_ENTRY_PATTERN = re.compile(r"\s*ALTERNATE_ENTRY\s*\(\s*([a-zA-Z0-9_\(\)]+)\s*\)")
+MACRO_START_PATTERN     = re.compile(r"\s*MACRO_START\s*\(\s*([A-Z_0-9]+)\s*,([^\)]+)\)")
+MACRO_END_PATTERN       = re.compile(r"\s*MACRO_END\s*\(\s*\)")
+INCLUDE_PATTERN         = re.compile(r"\s*INCLUDE\s*\(\s*([^\s]+)\s*\)")
+
+class ProcessingStateMachine:
+    """A class to hold the state when processing a file and handle files line by line"""
+
+    def __init__(self, assembler, normal_calling_convention, mul_calling_convention, nested_calling_convention):
+        self.assembler = assembler
+        self.normal_calling_convention = normal_calling_convention
+        self.mul_calling_convention = mul_calling_convention
+        self.nested_calling_convention = nested_calling_convention
+
+        self.function_start_match = None
+        self.function_start_line = 0
+        self.is_nested_function = None
+        self.is_mul_function = None
+        self.calling_convention = None
+        self.function_name = None
+        self.arg_count = None
+        self.reg_count = None
+        self.stack_alloc_size = None
+        self.xmm_reg_count = None
+
+        self.macro_start_match = None
+        self.macro_name = None
+        self.macro_args = None
+
+    def process_line(self, line, line_num):
+        if self.function_start_match == None and self.macro_start_match == None:
+            return self.process_normal_line(line, line_num)
+        elif self.function_start_match != None:
+            return self.process_function_line(line, line_num)
+        elif self.macro_start_match != None:
+            return self.process_macro_line(line, line_num)
+        else:
+            logging.error("Whoops, something is broken with the state machine (failed at line %d)" % line_num)
+            exit(1)
+
+    def process_normal_line(self, line, line_num):
+        # Not currently in a function or macro
+        match = FUNCTION_START_PATTERN.match(line)
+        if (match):
+            return self.process_start_function(match, line, line_num)
+
+        match = MACRO_START_PATTERN.match(line)
+        if (match):
+            return self.process_start_macro(match, line, line_num)
+
+        # Not starting a function or a macro
+        return line
+
+    def process_start_function(self, match, line, line_num):
+        # Entering a new function
+        self.function_start_match = match
+        self.function_start_line = line_num
+        self.is_nested_function = (match.group(1) == "NESTED_")
+        self.is_mul_function = (match.group(2) == "MUL_")
+        self.function_name = match.group(3)
+        self.arg_count = int(match.group(4))
+        self.reg_count = int(match.group(5))
+        # last two parameters are optional and their corresponding capturing groups will be None if not supplied
+        self.stack_alloc_size = 0 if not match.group(6) else eval(match.group(6).strip(", \'\""))
+        self.xmm_reg_count = 0 if not match.group(7) else int(match.group(7).strip(", "))
+
+        if self.is_nested_function and self.nested_calling_convention is None:
+            logging.error(
+                "symcryptasm nested functions are not currently supported with assembler (%s) and architecture (%s)!\n\t"
+                "%s (line %d)"
+                % (self.assembler, self.normal_calling_convention.architecture, line, line_num))
+            exit(1)
+        if self.is_mul_function and self.mul_calling_convention is None:
+            logging.error(
+                "symcryptasm mul functions are not supported with assembler (%s) and architecture (%s)!\n\t"
+                "%s (line %d)"
+                % (self.assembler, self.normal_calling_convention.architecture, line, line_num))
+            exit(1)
+        if self.is_nested_function and self.is_mul_function:
+            logging.error(
+                "Too many prefixes for symcryptasm function - currently only 1 of prefix, MUL_ or NESTED_, is supported!\n\t"
+                "%s (line %d)"
+                % (line, line_num))
+            exit(1)
+        if self.arg_count > self.normal_calling_convention.max_arguments:
+            logging.error(
+                "Too many (%d) arguments for symcryptasm function - only %d arguments are supported by calling convention (%s)\n\t"
+                "%s (line %d)"
+                % (self.arg_count, self.normal_calling_convention.max_arguments, self.normal_calling_convention.name, match.group(0), line_num))
+            exit(1)
+        if self.reg_count > len(self.normal_calling_convention.mapping):
+            logging.error(
+                "Too many (%d) registers required for symcryptasm function - only %d registers are mapped by calling convention (%s)\n\t"
+                "%s (line %d)"
+                % (self.reg_count, len(self.normal_calling_convention.mapping), self.normal_calling_convention.name, match.group(0), line_num))
+            exit(1)
+        if self.is_mul_function and self.reg_count > len(self.mul_calling_convention.mapping)-1:
+            logging.error(
+                "Too many (%d) registers required for symcryptasm mul function - only %d registers are mapped by calling convention (%s)\n\t"
+                "%s (line %d)"
+                % (self.reg_count, len(self.mul_calling_convention.mapping)-1, self.mul_calling_convention.name, match.group(0), line_num))
+            exit(1)
+        if not 0 <= self.xmm_reg_count <= 16:
+            logging.error(
+                "Invalid number of used XMM registers (%d) specified for calling convention (%s) - must be in range [0,16]\n\t"
+                "%s (line %d)"
+                % (self.xmm_reg_count, self.mul_calling_convention.name, match.group(6), line_num))
+            exit(1)
+
+
+        logging.info("%d: function start %s, %d, %d" % (line_num, self.function_name, self.arg_count, self.reg_count))
+
+        if self.is_nested_function:
+            self.calling_convention = self.nested_calling_convention
+        elif self.is_mul_function:
+            self.calling_convention = self.mul_calling_convention
+        else:
+            self.calling_convention = self.normal_calling_convention
+
+        return generate_prologue(self.assembler,
+                                 self.calling_convention,
+                                 self.function_name,
+                                 self.arg_count,
+                                 self.reg_count,
+                                 self.stack_alloc_size,
+                                 self.xmm_reg_count,
+                                 self.is_nested_function
+                                 )
+
+    def process_start_macro(self, match, line, line_num):
+        self.macro_start_match = match
+        self.macro_name = match.group(1)
+        self.macro_args = [ x.strip() for x in match.group(2).split(",") ]
+
+        logging.info("%d: macro start %s, %s" % (line_num, self.macro_name, self.macro_args))
+
+        if self.assembler == ASSEMBLER_MASM:
+            return MASM_MACRO_START % (self.macro_name, match.group(2))
+        elif self.assembler in [ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO, ASSEMBLER_GAS_PE]:
+            return GAS_MACRO_START % (self.macro_name, match.group(2))
+        elif self.assembler == ASSEMBLER_ARMASM64:
+            # In armasm64 we need to escape all macro arguments with $
+            prefixed_args = ", $".join(self.macro_args)
+            if prefixed_args:
+                prefixed_args = "$" + prefixed_args
+            return ARMASM64_MACRO_START % (self.macro_name, prefixed_args)
+        else:
+            logging.error("Unhandled assembler (%s) in process_start_macro" % self.assembler)
+            exit(1)
+
+    def process_function_line(self, line, line_num):
+        # Currently in a function
+        match = ALTERNATE_ENTRY_PATTERN.match(line)
+        if (match):
+            if self.assembler == ASSEMBLER_MASM:
+                return MASM_ALTERNATE_ENTRY % match.group(1)
+            elif self.assembler in [ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO, ASSEMBLER_GAS_PE]:
+                    return GAS_ALTERNATE_ENTRY % (match.group(1), match.group(1))
+            elif self.assembler == ASSEMBLER_ARMASM64:
+                return ARMASM64_ALTERNATE_ENTRY % match.group(1)
+            else:
+                logging.error("Unhandled assembler (%s) in process_function_line" % self.assembler)
+                exit(1)
+
+        match = FUNCTION_END_PATTERN.match(line)
+        if (match):
+            # Check the end function has same prefix as previous start function
+            if  (self.is_nested_function ^ (match.group(1) == "NESTED_")) or \
+                (self.is_mul_function ^ (match.group(2) == "MUL_")):
+                logging.error("Function start and end do not have same MUL_ or NESTED_ prefix!\n\tStart: %s (line %d)\n\tEnd:   %s (line %d)" \
+                    % (self.function_start_match.group(0), self.function_start_line, match.group(0), line_num))
+                exit(1)
+            # Check the end function pattern has the same label as the previous start function pattern
+            if self.function_name != match.groups()[-1]:
+                logging.error("Function start label does not match Function end label!\n\tStart: %s (line %d)\n\tEnd:   %s (line %d)" \
+                    % (self.function_name, self.function_start_line, match.groups()[-1], line_num))
+                exit(1)
+
+            epilogue = generate_epilogue(self.assembler, self.calling_convention, self.function_name, self.arg_count, self.reg_count, self.stack_alloc_size, self.xmm_reg_count, self.is_nested_function)
+
+            logging.info("%d: function end %s" % (line_num, self.function_name))
+
+            self.function_start_match = None
+            self.function_start_line = 0
+            self.is_nested_function = None
+            self.is_mul_function = None
+            self.calling_convention = None
+            self.function_name = None
+            self.arg_count = None
+            self.reg_count = None
+            self.stack_alloc_size = None
+            self.xmm_reg_count = None
+
+            return epilogue
+
+        # replace any GET_MEMSLOT_OFFSET macros in line
+        match = GET_MEMSLOT_PATTERN.search(line)
+        while(match):
+            slot = int(match.group(1))
+            replacement = self.calling_convention.gen_get_memslot_offset_fn(slot, self.arg_count, self.reg_count, self.stack_alloc_size, self.xmm_reg_count)
+            line = GET_MEMSLOT_PATTERN.sub(replacement, line)
+            match = GET_MEMSLOT_PATTERN.search(line)
+
+            logging.info("%d: memslot macro %d" % (line_num, slot))
+
+        # Not modifying the line any further
+        return line
+
+    def process_macro_line(self, line, line_num):
+        # Currently in a macro
+        match = MACRO_END_PATTERN.match(line)
+        if (match):
+            logging.info("%d: macro end %s" % (line_num, self.macro_name))
+
+            self.macro_start_match = None
+            self.macro_name = None
+            self.macro_args = None
+
+            if self.assembler == ASSEMBLER_MASM:
+                return MASM_MACRO_END
+            elif self.assembler in [ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO, ASSEMBLER_GAS_PE]:
+                return GAS_MACRO_END
+            elif self.assembler == ASSEMBLER_ARMASM64:
+                return ARMASM64_MACRO_END
+            else:
+                logging.error("Unhandled assembler (%s) in process_macro_line" % self.assembler)
+                exit(1)
+
+        arg_prefix = ""
+        if self.assembler == ASSEMBLER_ARMASM64:
+            # In armasm64 macros we need to escape all of the macro arguments with a $ in the macro body
+            arg_prefix = "$"
+        elif self.assembler in [ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO, ASSEMBLER_GAS_PE]:
+            # In GAS macros we need to escape all of the macro arguments with a backslash in the macro body
+            arg_prefix = r"\\"
+
+        if arg_prefix:
+            for arg in self.macro_args:
+                line = replace_identifier(arg, arg_prefix + arg, line)
+
+        # Not modifying the line any further
+        return line
+
+
+def expand_files(filename, line_num_parent, line_parent):
+    ''' Read the contents of filename and insert include files where there's an INCLUDE directive'''
+    if line_parent:
+        logging.info(
+        "expand file %s\n\t"
+        "%s (line %d)"
+        % (filename, line_parent, line_num_parent))
+    base_dir = os.path.dirname(filename)
+    expanded_lines = []
+    has_includes = False
+    try:
+        infile = open(filename)
+        for line_num, line in enumerate(infile.readlines()):
+            match = INCLUDE_PATTERN.match(line)
+            if (match):
+                include_file = match.group(1)
+                full_path = base_dir + "/" + include_file
+                logging.info("%d: including file %s" % (line_num + 1, include_file))
+                logging.info("%d: full path %s" % (line_num + 1, full_path))
+                include_lines, _ = expand_files(full_path, line_num + 1, line)
+                expanded_lines.extend(include_lines)
+                has_includes = True
+            else:
+                expanded_lines.append(line)
+        infile.close()
+    except IOError:
+        logging.error(
+            "cannot open include file %s\n\t"
+            "%s (line %d)"
+            % (filename, line_parent, line_num_parent))
+        exit(1)
+
+    return expanded_lines, has_includes
+
+def gen_file_header(assembler, architecture, calling_convention):
+    """ Generate header to be inserted at the beginning of each symcryptasm file"""
+    header = ""
+
+    if assembler == ASSEMBLER_MASM:
+        header += "// begin masm header\n"
+        header += "option casemap:none\n"
+        header += "// end masm header\n\n"
+
+    return header
+
+def process_file(assembler, architecture, calling_convention, infilename, outfilename):
+    normal_calling_convention = None
+
+    if assembler in [ASSEMBLER_MASM, ASSEMBLER_GAS_PE]:
+        if architecture == "amd64" and calling_convention == "msft":
+            normal_calling_convention = CALLING_CONVENTION_AMD64_MSFT
+            mul_calling_convention = CALLING_CONVENTION_AMD64_MSFT_MUL
+            nested_calling_convention = CALLING_CONVENTION_AMD64_MSFT_NESTED
+        elif architecture == "arm64" and calling_convention == "aapcs64":
+            normal_calling_convention = CALLING_CONVENTION_ARM64_AAPCS64_GAS_PE
+            mul_calling_convention = None
+            nested_calling_convention = None
+        elif architecture == "arm64" and calling_convention == "arm64ec":
+            normal_calling_convention = CALLING_CONVENTION_ARM64EC_GAS_PE
+            mul_calling_convention = None
+            nested_calling_convention = None
+        elif architecture == "arm" and calling_convention == "aapcs32":
+            normal_calling_convention = CALLING_CONVENTION_ARM32_AAPCS32
+            mul_calling_convention = None
+            nested_calling_convention = None
+    elif assembler in [ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO]:
+        if architecture == "amd64" and calling_convention == "systemv":
+            normal_calling_convention = CALLING_CONVENTION_AMD64_SYSTEMV
+            mul_calling_convention = CALLING_CONVENTION_AMD64_SYSTEMV_MUL
+            nested_calling_convention = CALLING_CONVENTION_AMD64_SYSTEMV_NESTED
+        elif architecture == "arm64" and calling_convention == "aapcs64":
+            normal_calling_convention = CALLING_CONVENTION_ARM64_AAPCS64_GAS
+            mul_calling_convention = None
+            nested_calling_convention = None
+        elif architecture == "arm" and calling_convention == "aapcs32":
+            normal_calling_convention = CALLING_CONVENTION_ARM32_AAPCS32
+            mul_calling_convention = None
+            nested_calling_convention = None
+    elif assembler == ASSEMBLER_ARMASM64:
+        if architecture == "arm64" and calling_convention == "aapcs64":
+            normal_calling_convention = CALLING_CONVENTION_ARM64_AAPCS64_ARMASM64
+            mul_calling_convention = None
+            nested_calling_convention = None
+        elif architecture == "arm64" and calling_convention == "arm64ec":
+            normal_calling_convention = CALLING_CONVENTION_ARM64EC_MSFT
+            mul_calling_convention = None
+            nested_calling_convention = None
+    else:
+        logging.error("Unhandled assembler (%s) in process_file" % assembler)
+        exit(1)
+
+    if normal_calling_convention is None:
+        logging.error("Unhandled combination (%s + %s + %s) in process_file"
+            % (assembler, architecture, calling_convention))
+        exit(1)
+
+    file_processing_state = ProcessingStateMachine(
+        assembler, normal_calling_convention, mul_calling_convention, nested_calling_convention)
+
+    #
+    # expand included files
+    #
+    expanded_lines = []
+
+    # suppress header insertion
+    header = ""
+    # insert assembler specific header per symcryptasm file
+    #header = gen_file_header(assembler, architecture, calling_convention)
+    #expanded_lines.extend(header)
+
+    # expand_files() is called recursively when a .symcryptasm file contains an INCLUDE directive,
+    # except for the first call here where we're starting to process the input source file
+    # as if it was included by some other file.
+    expanded_file, infile_has_includes = expand_files(infilename, 0, "")
+    expanded_lines.extend(expanded_file)
+
+    # if header was nonempty or there were any INCLUDE directives, output the expanded source file for debugging
+    if header or infile_has_includes:
+        expanded_filename = os.path.dirname(outfilename) + "/" + os.path.basename(infilename) + "exp"
+        with open(expanded_filename, "w") as outfile:
+            outfile.writelines(expanded_lines)
+
+    # iterate through file line by line in one pass
+    with open(outfilename, "w") as outfile:
+        for line_num, line in enumerate(expanded_lines):
+            processed_line = file_processing_state.process_line(line, line_num)
+            # logging.info("processed line: %s" % processed_line)
+            outfile.write(processed_line)
+
+if __name__ == "__main__":
+    import argparse
+
+    # logging.basicConfig(level=logging.INFO)
+    parser = argparse.ArgumentParser(description="Preprocess symcryptasm into files that will be further processed with C preprocessor to generate MASM or GAS")
+    parser.add_argument('assembler', type=str, help='Assembler that we want to preprocess for', choices=[ASSEMBLER_MASM, ASSEMBLER_ARMASM64, ASSEMBLER_GAS, ASSEMBLER_GAS_MACHO, ASSEMBLER_GAS_PE])
+    parser.add_argument('architecture', type=str, help='Architecture that we want to preprocess for', choices=['amd64', 'arm64', 'arm'])
+    parser.add_argument('calling_convention', type=str, help='Calling convention that we want to preprocess for', choices=['msft', 'systemv', 'aapcs64', 'arm64ec', 'aapcs32'])
+    parser.add_argument('inputfile', type=str, help='Path to input file')
+    parser.add_argument('outputfile', type=str, help='Path to output file')
+
+    args = parser.parse_args()
+    process_file(args.assembler, args.architecture, args.calling_convention, args.inputfile, args.outputfile)
+    logging.info("Done")
diff --git a/libs/tomcrypt/LICENSE b/libs/tomcrypt/LICENSE
deleted file mode 100644
index 07f8582bfdc4..000000000000
--- a/libs/tomcrypt/LICENSE
+++ /dev/null
@@ -1,29 +0,0 @@
-LibTomCrypt is licensed under DUAL licensing terms.
-
-Choose and use the license of your needs.
-
-[LICENSE #1]
-
-LibTomCrypt is public domain.  As should all quality software be.
-
-Tom St Denis
-
-[/LICENSE #1]
-
-[LICENSE #2]
-
-            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
-                    Version 2, December 2004
-
- Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
-
- Everyone is permitted to copy and distribute verbatim or modified
- copies of this license document, and changing it is allowed as long
- as the name is changed.
-
-            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. You just DO WHAT THE FUCK YOU WANT TO.
-
-[/LICENSE #2]
diff --git a/libs/tomcrypt/Makefile.in b/libs/tomcrypt/Makefile.in
deleted file mode 100644
index cf77b9f3358d..000000000000
--- a/libs/tomcrypt/Makefile.in
+++ /dev/null
@@ -1,303 +0,0 @@
-STATICLIB = libtomcrypt.a
-EXTRAINCL = $(TOMCRYPT_PE_CFLAGS)
-EXTRADEFS = -D_ACRTIMP= -DLTC_NO_TEST -DMP_32BIT=
-
-SOURCES = \
-	mpi.c \
-	src/ciphers/aes/aes.c \
-	src/ciphers/aes/aes_tab.c \
-	src/ciphers/blowfish.c \
-	src/ciphers/des.c \
-	src/ciphers/rc2.c \
-	src/encauth/ccm/ccm_add_aad.c \
-	src/encauth/ccm/ccm_add_nonce.c \
-	src/encauth/ccm/ccm_done.c \
-	src/encauth/ccm/ccm_init.c \
-	src/encauth/ccm/ccm_memory.c \
-	src/encauth/ccm/ccm_process.c \
-	src/encauth/ccm/ccm_reset.c \
-	src/encauth/eax/eax_addheader.c \
-	src/encauth/eax/eax_decrypt.c \
-	src/encauth/eax/eax_decrypt_verify_memory.c \
-	src/encauth/eax/eax_done.c \
-	src/encauth/eax/eax_encrypt.c \
-	src/encauth/eax/eax_encrypt_authenticate_memory.c \
-	src/encauth/eax/eax_init.c \
-	src/encauth/gcm/gcm_add_aad.c \
-	src/encauth/gcm/gcm_add_iv.c \
-	src/encauth/gcm/gcm_done.c \
-	src/encauth/gcm/gcm_gf_mult.c \
-	src/encauth/gcm/gcm_init.c \
-	src/encauth/gcm/gcm_memory.c \
-	src/encauth/gcm/gcm_mult_h.c \
-	src/encauth/gcm/gcm_process.c \
-	src/encauth/gcm/gcm_reset.c \
-	src/encauth/ocb/ocb_decrypt.c \
-	src/encauth/ocb/ocb_decrypt_verify_memory.c \
-	src/encauth/ocb/ocb_done_decrypt.c \
-	src/encauth/ocb/ocb_done_encrypt.c \
-	src/encauth/ocb/ocb_encrypt.c \
-	src/encauth/ocb/ocb_encrypt_authenticate_memory.c \
-	src/encauth/ocb/ocb_init.c \
-	src/encauth/ocb/ocb_ntz.c \
-	src/encauth/ocb/ocb_shift_xor.c \
-	src/encauth/ocb/s_ocb_done.c \
-	src/encauth/ocb3/ocb3_add_aad.c \
-	src/encauth/ocb3/ocb3_decrypt.c \
-	src/encauth/ocb3/ocb3_decrypt_last.c \
-	src/encauth/ocb3/ocb3_decrypt_verify_memory.c \
-	src/encauth/ocb3/ocb3_done.c \
-	src/encauth/ocb3/ocb3_encrypt.c \
-	src/encauth/ocb3/ocb3_encrypt_authenticate_memory.c \
-	src/encauth/ocb3/ocb3_encrypt_last.c \
-	src/encauth/ocb3/ocb3_init.c \
-	src/encauth/ocb3/ocb3_int_ntz.c \
-	src/encauth/ocb3/ocb3_int_xor_blocks.c \
-	src/hashes/md2.c \
-	src/hashes/md4.c \
-	src/hashes/md5.c \
-	src/hashes/rmd128.c \
-	src/hashes/rmd160.c \
-	src/hashes/rmd256.c \
-	src/hashes/rmd320.c \
-	src/hashes/sha1.c \
-	src/hashes/sha2/sha224.c \
-	src/hashes/sha2/sha256.c \
-	src/hashes/sha2/sha384.c \
-	src/hashes/sha2/sha512.c \
-	src/hashes/sha2/sha512_224.c \
-	src/hashes/sha2/sha512_256.c \
-	src/hashes/sha3.c \
-	src/mac/hmac/hmac_done.c \
-	src/mac/hmac/hmac_init.c \
-	src/mac/hmac/hmac_memory.c \
-	src/mac/hmac/hmac_memory_multi.c \
-	src/mac/hmac/hmac_process.c \
-	src/mac/omac/omac_done.c \
-	src/mac/omac/omac_init.c \
-	src/mac/omac/omac_memory.c \
-	src/mac/omac/omac_memory_multi.c \
-	src/mac/omac/omac_process.c \
-	src/mac/pmac/pmac_done.c \
-	src/mac/pmac/pmac_init.c \
-	src/mac/pmac/pmac_memory.c \
-	src/mac/pmac/pmac_memory_multi.c \
-	src/mac/pmac/pmac_ntz.c \
-	src/mac/pmac/pmac_process.c \
-	src/mac/pmac/pmac_shift_xor.c \
-	src/mac/xcbc/xcbc_done.c \
-	src/mac/xcbc/xcbc_init.c \
-	src/mac/xcbc/xcbc_memory.c \
-	src/mac/xcbc/xcbc_memory_multi.c \
-	src/mac/xcbc/xcbc_process.c \
-	src/math/fp/ltc_ecc_fp_mulmod.c \
-	src/math/ltm_desc.c \
-	src/math/multi.c \
-	src/math/radix_to_bin.c \
-	src/math/rand_bn.c \
-	src/math/rand_prime.c \
-	src/math/tfm_desc.c \
-	src/misc/adler32.c \
-	src/misc/base64/base64_decode.c \
-	src/misc/base64/base64_encode.c \
-	src/misc/burn_stack.c \
-	src/misc/crc32.c \
-	src/misc/crypt/crypt_cipher_descriptor.c \
-	src/misc/crypt/crypt_cipher_is_valid.c \
-	src/misc/crypt/crypt_find_cipher.c \
-	src/misc/crypt/crypt_find_cipher_any.c \
-	src/misc/crypt/crypt_find_cipher_id.c \
-	src/misc/crypt/crypt_find_hash.c \
-	src/misc/crypt/crypt_find_hash_any.c \
-	src/misc/crypt/crypt_find_hash_id.c \
-	src/misc/crypt/crypt_find_hash_oid.c \
-	src/misc/crypt/crypt_find_prng.c \
-	src/misc/crypt/crypt_hash_descriptor.c \
-	src/misc/crypt/crypt_hash_is_valid.c \
-	src/misc/crypt/crypt_inits.c \
-	src/misc/crypt/crypt_ltc_mp_descriptor.c \
-	src/misc/crypt/crypt_prng_descriptor.c \
-	src/misc/crypt/crypt_prng_is_valid.c \
-	src/misc/crypt/crypt_prng_rng_descriptor.c \
-	src/misc/crypt/crypt_register_cipher.c \
-	src/misc/crypt/crypt_register_hash.c \
-	src/misc/crypt/crypt_register_prng.c \
-	src/misc/crypt/crypt_unregister_cipher.c \
-	src/misc/crypt/crypt_unregister_hash.c \
-	src/misc/crypt/crypt_unregister_prng.c \
-	src/misc/mem_neq.c \
-	src/misc/pk_get_oid.c \
-	src/misc/pkcs5/pkcs_5_1.c \
-	src/misc/pkcs5/pkcs_5_2.c \
-	src/misc/zeromem.c \
-	src/modes/cbc/cbc_decrypt.c \
-	src/modes/cbc/cbc_done.c \
-	src/modes/cbc/cbc_encrypt.c \
-	src/modes/cbc/cbc_getiv.c \
-	src/modes/cbc/cbc_setiv.c \
-	src/modes/cbc/cbc_start.c \
-	src/modes/cfb/cfb_decrypt.c \
-	src/modes/cfb/cfb_done.c \
-	src/modes/cfb/cfb_encrypt.c \
-	src/modes/cfb/cfb_getiv.c \
-	src/modes/cfb/cfb_setiv.c \
-	src/modes/cfb/cfb_start.c \
-	src/modes/ctr/ctr_decrypt.c \
-	src/modes/ctr/ctr_done.c \
-	src/modes/ctr/ctr_encrypt.c \
-	src/modes/ctr/ctr_getiv.c \
-	src/modes/ctr/ctr_setiv.c \
-	src/modes/ctr/ctr_start.c \
-	src/modes/ecb/ecb_decrypt.c \
-	src/modes/ecb/ecb_done.c \
-	src/modes/ecb/ecb_encrypt.c \
-	src/modes/ecb/ecb_start.c \
-	src/modes/f8/f8_decrypt.c \
-	src/modes/f8/f8_done.c \
-	src/modes/f8/f8_encrypt.c \
-	src/modes/f8/f8_getiv.c \
-	src/modes/f8/f8_setiv.c \
-	src/modes/f8/f8_start.c \
-	src/modes/f8/f8_test_mode.c \
-	src/modes/lrw/lrw_decrypt.c \
-	src/modes/lrw/lrw_done.c \
-	src/modes/lrw/lrw_encrypt.c \
-	src/modes/lrw/lrw_getiv.c \
-	src/modes/lrw/lrw_process.c \
-	src/modes/lrw/lrw_setiv.c \
-	src/modes/lrw/lrw_start.c \
-	src/modes/ofb/ofb_decrypt.c \
-	src/modes/ofb/ofb_done.c \
-	src/modes/ofb/ofb_encrypt.c \
-	src/modes/ofb/ofb_getiv.c \
-	src/modes/ofb/ofb_setiv.c \
-	src/modes/ofb/ofb_start.c \
-	src/modes/xts/xts_decrypt.c \
-	src/modes/xts/xts_done.c \
-	src/modes/xts/xts_encrypt.c \
-	src/modes/xts/xts_init.c \
-	src/modes/xts/xts_mult_x.c \
-	src/pk/asn1/der/bit/der_decode_bit_string.c \
-	src/pk/asn1/der/bit/der_decode_raw_bit_string.c \
-	src/pk/asn1/der/bit/der_encode_bit_string.c \
-	src/pk/asn1/der/bit/der_encode_raw_bit_string.c \
-	src/pk/asn1/der/bit/der_length_bit_string.c \
-	src/pk/asn1/der/boolean/der_decode_boolean.c \
-	src/pk/asn1/der/boolean/der_encode_boolean.c \
-	src/pk/asn1/der/boolean/der_length_boolean.c \
-	src/pk/asn1/der/choice/der_decode_choice.c \
-	src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c \
-	src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c \
-	src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c \
-	src/pk/asn1/der/ia5/der_decode_ia5_string.c \
-	src/pk/asn1/der/ia5/der_encode_ia5_string.c \
-	src/pk/asn1/der/ia5/der_length_ia5_string.c \
-	src/pk/asn1/der/integer/der_decode_integer.c \
-	src/pk/asn1/der/integer/der_encode_integer.c \
-	src/pk/asn1/der/integer/der_length_integer.c \
-	src/pk/asn1/der/object_identifier/der_decode_object_identifier.c \
-	src/pk/asn1/der/object_identifier/der_encode_object_identifier.c \
-	src/pk/asn1/der/object_identifier/der_length_object_identifier.c \
-	src/pk/asn1/der/octet/der_decode_octet_string.c \
-	src/pk/asn1/der/octet/der_encode_octet_string.c \
-	src/pk/asn1/der/octet/der_length_octet_string.c \
-	src/pk/asn1/der/printable_string/der_decode_printable_string.c \
-	src/pk/asn1/der/printable_string/der_encode_printable_string.c \
-	src/pk/asn1/der/printable_string/der_length_printable_string.c \
-	src/pk/asn1/der/sequence/der_decode_sequence_ex.c \
-	src/pk/asn1/der/sequence/der_decode_sequence_flexi.c \
-	src/pk/asn1/der/sequence/der_decode_sequence_multi.c \
-	src/pk/asn1/der/sequence/der_decode_subject_public_key_info.c \
-	src/pk/asn1/der/sequence/der_encode_sequence_ex.c \
-	src/pk/asn1/der/sequence/der_encode_sequence_multi.c \
-	src/pk/asn1/der/sequence/der_encode_subject_public_key_info.c \
-	src/pk/asn1/der/sequence/der_length_sequence.c \
-	src/pk/asn1/der/sequence/der_sequence_free.c \
-	src/pk/asn1/der/sequence/der_sequence_shrink.c \
-	src/pk/asn1/der/set/der_encode_set.c \
-	src/pk/asn1/der/set/der_encode_setof.c \
-	src/pk/asn1/der/short_integer/der_decode_short_integer.c \
-	src/pk/asn1/der/short_integer/der_encode_short_integer.c \
-	src/pk/asn1/der/short_integer/der_length_short_integer.c \
-	src/pk/asn1/der/teletex_string/der_decode_teletex_string.c \
-	src/pk/asn1/der/teletex_string/der_length_teletex_string.c \
-	src/pk/asn1/der/utctime/der_decode_utctime.c \
-	src/pk/asn1/der/utctime/der_encode_utctime.c \
-	src/pk/asn1/der/utctime/der_length_utctime.c \
-	src/pk/asn1/der/utf8/der_decode_utf8_string.c \
-	src/pk/asn1/der/utf8/der_encode_utf8_string.c \
-	src/pk/asn1/der/utf8/der_length_utf8_string.c \
-	src/pk/dh/dh.c \
-	src/pk/dh/dh_check_pubkey.c \
-	src/pk/dh/dh_export.c \
-	src/pk/dh/dh_export_key.c \
-	src/pk/dh/dh_free.c \
-	src/pk/dh/dh_generate_key.c \
-	src/pk/dh/dh_import.c \
-	src/pk/dh/dh_set.c \
-	src/pk/dh/dh_set_pg_dhparam.c \
-	src/pk/dh/dh_shared_secret.c \
-	src/pk/dsa/dsa_decrypt_key.c \
-	src/pk/dsa/dsa_encrypt_key.c \
-	src/pk/dsa/dsa_export.c \
-	src/pk/dsa/dsa_free.c \
-	src/pk/dsa/dsa_generate_key.c \
-	src/pk/dsa/dsa_generate_pqg.c \
-	src/pk/dsa/dsa_import.c \
-	src/pk/dsa/dsa_make_key.c \
-	src/pk/dsa/dsa_set.c \
-	src/pk/dsa/dsa_set_pqg_dsaparam.c \
-	src/pk/dsa/dsa_shared_secret.c \
-	src/pk/dsa/dsa_sign_hash.c \
-	src/pk/dsa/dsa_verify_hash.c \
-	src/pk/dsa/dsa_verify_key.c \
-	src/pk/ecc/ecc.c \
-	src/pk/ecc/ecc_ansi_x963_export.c \
-	src/pk/ecc/ecc_ansi_x963_import.c \
-	src/pk/ecc/ecc_decrypt_key.c \
-	src/pk/ecc/ecc_encrypt_key.c \
-	src/pk/ecc/ecc_export.c \
-	src/pk/ecc/ecc_free.c \
-	src/pk/ecc/ecc_get_size.c \
-	src/pk/ecc/ecc_import.c \
-	src/pk/ecc/ecc_make_key.c \
-	src/pk/ecc/ecc_shared_secret.c \
-	src/pk/ecc/ecc_sign_hash.c \
-	src/pk/ecc/ecc_sizes.c \
-	src/pk/ecc/ecc_verify_hash.c \
-	src/pk/ecc/ltc_ecc_is_valid_idx.c \
-	src/pk/ecc/ltc_ecc_map.c \
-	src/pk/ecc/ltc_ecc_mul2add.c \
-	src/pk/ecc/ltc_ecc_mulmod.c \
-	src/pk/ecc/ltc_ecc_mulmod_timing.c \
-	src/pk/ecc/ltc_ecc_points.c \
-	src/pk/ecc/ltc_ecc_projective_add_point.c \
-	src/pk/ecc/ltc_ecc_projective_dbl_point.c \
-	src/pk/pkcs1/pkcs_1_i2osp.c \
-	src/pk/pkcs1/pkcs_1_mgf1.c \
-	src/pk/pkcs1/pkcs_1_oaep_decode.c \
-	src/pk/pkcs1/pkcs_1_oaep_encode.c \
-	src/pk/pkcs1/pkcs_1_os2ip.c \
-	src/pk/pkcs1/pkcs_1_pss_decode.c \
-	src/pk/pkcs1/pkcs_1_pss_encode.c \
-	src/pk/pkcs1/pkcs_1_v1_5_decode.c \
-	src/pk/pkcs1/pkcs_1_v1_5_encode.c \
-	src/pk/rsa/rsa_decrypt_key.c \
-	src/pk/rsa/rsa_encrypt_key.c \
-	src/pk/rsa/rsa_export.c \
-	src/pk/rsa/rsa_exptmod.c \
-	src/pk/rsa/rsa_free.c \
-	src/pk/rsa/rsa_get_size.c \
-	src/pk/rsa/rsa_import.c \
-	src/pk/rsa/rsa_import_pkcs8.c \
-	src/pk/rsa/rsa_import_x509.c \
-	src/pk/rsa/rsa_make_key.c \
-	src/pk/rsa/rsa_set.c \
-	src/pk/rsa/rsa_sign_hash.c \
-	src/pk/rsa/rsa_sign_saltlen_get.c \
-	src/pk/rsa/rsa_verify_hash.c \
-	src/prngs/rc4.c \
-	src/prngs/rng_get_bytes.c \
-	src/prngs/rng_make_prng.c \
-	src/prngs/sprng.c \
-	src/stream/rc4/rc4_stream.c
diff --git a/libs/tomcrypt/mpi.c b/libs/tomcrypt/mpi.c
deleted file mode 100644
index c110a78e9873..000000000000
--- a/libs/tomcrypt/mpi.c
+++ /dev/null
@@ -1,8586 +0,0 @@
-/* LibTomMath, multiple-precision integer library -- Tom St Denis
- *
- * LibTomMath is a library that provides multiple-precision
- * integer arithmetic as well as number theoretic functionality.
- *
- * The library was designed directly after the MPI library by
- * Michael Fromberger but has been written from scratch with
- * additional optimizations in place.
- *
- * SPDX-License-Identifier: Unlicense
- */
-
-#include <stdarg.h>
-#include "tommath_private.h"
-
-/* Start: bn_fast_mp_invmod.c */
-
-/* computes the modular inverse via binary extended euclidean algorithm,
- * that is c = 1/a mod b
- *
- * Based on slow invmod except this is optimized for the case where b is
- * odd as per HAC Note 14.64 on pp. 610
- */
-int fast_mp_invmod(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   mp_int  x, y, u, v, B, D;
-   int     res, neg;
-
-   /* 2. [modified] b must be odd   */
-   if (mp_iseven(b) == MP_YES) {
-      return MP_VAL;
-   }
-
-   /* init all our temps */
-   if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) {
-      return res;
-   }
-
-   /* x == modulus, y == value to invert */
-   if ((res = mp_copy(b, &x)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* we need y = |a| */
-   if ((res = mp_mod(a, b, &y)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* if one of x,y is zero return an error! */
-   if ((mp_iszero(&x) == MP_YES) || (mp_iszero(&y) == MP_YES)) {
-      res = MP_VAL;
-      goto LBL_ERR;
-   }
-
-   /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
-   if ((res = mp_copy(&x, &u)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_copy(&y, &v)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_set(&D, 1uL);
-
-top:
-   /* 4.  while u is even do */
-   while (mp_iseven(&u) == MP_YES) {
-      /* 4.1 u = u/2 */
-      if ((res = mp_div_2(&u, &u)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      /* 4.2 if B is odd then */
-      if (mp_isodd(&B) == MP_YES) {
-         if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) {
-            goto LBL_ERR;
-         }
-      }
-      /* B = B/2 */
-      if ((res = mp_div_2(&B, &B)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* 5.  while v is even do */
-   while (mp_iseven(&v) == MP_YES) {
-      /* 5.1 v = v/2 */
-      if ((res = mp_div_2(&v, &v)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      /* 5.2 if D is odd then */
-      if (mp_isodd(&D) == MP_YES) {
-         /* D = (D-x)/2 */
-         if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) {
-            goto LBL_ERR;
-         }
-      }
-      /* D = D/2 */
-      if ((res = mp_div_2(&D, &D)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* 6.  if u >= v then */
-   if (mp_cmp(&u, &v) != MP_LT) {
-      /* u = u - v, B = B - D */
-      if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   } else {
-      /* v - v - u, D = D - B */
-      if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* if not zero goto step 4 */
-   if (mp_iszero(&u) == MP_NO) {
-      goto top;
-   }
-
-   /* now a = C, b = D, gcd == g*v */
-
-   /* if v != 1 then there is no inverse */
-   if (mp_cmp_d(&v, 1uL) != MP_EQ) {
-      res = MP_VAL;
-      goto LBL_ERR;
-   }
-
-   /* b is now the inverse */
-   neg = a->sign;
-   while (D.sign == MP_NEG) {
-      if ((res = mp_add(&D, b, &D)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* too big */
-   while (mp_cmp_mag(&D, b) != MP_LT) {
-      if ((res = mp_sub(&D, b, &D)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   mp_exch(&D, c);
-   c->sign = neg;
-   res = MP_OKAY;
-
-LBL_ERR:
-   mp_clear_multi(&x, &y, &u, &v, &B, &D, NULL);
-   return res;
-}
-
-/* End: bn_fast_mp_invmod.c */
-
-/* Start: bn_fast_mp_montgomery_reduce.c */
-
-/* computes xR**-1 == x (mod N) via Montgomery Reduction
- *
- * This is an optimized implementation of montgomery_reduce
- * which uses the comba method to quickly calculate the columns of the
- * reduction.
- *
- * Based on Algorithm 14.32 on pp.601 of HAC.
-*/
-int fast_mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho)
-{
-   int     ix, res, olduse;
-   mp_word W[MP_WARRAY];
-
-   if (x->used > (int)MP_WARRAY) {
-      return MP_VAL;
-   }
-
-   /* get old used count */
-   olduse = x->used;
-
-   /* grow a as required */
-   if (x->alloc < (n->used + 1)) {
-      if ((res = mp_grow(x, n->used + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* first we have to get the digits of the input into
-    * an array of double precision words W[...]
-    */
-   {
-      mp_word *_W;
-      mp_digit *tmpx;
-
-      /* alias for the W[] array */
-      _W   = W;
-
-      /* alias for the digits of  x*/
-      tmpx = x->dp;
-
-      /* copy the digits of a into W[0..a->used-1] */
-      for (ix = 0; ix < x->used; ix++) {
-         *_W++ = *tmpx++;
-      }
-
-      /* zero the high words of W[a->used..m->used*2] */
-      for (; ix < ((n->used * 2) + 1); ix++) {
-         *_W++ = 0;
-      }
-   }
-
-   /* now we proceed to zero successive digits
-    * from the least significant upwards
-    */
-   for (ix = 0; ix < n->used; ix++) {
-      /* mu = ai * m' mod b
-       *
-       * We avoid a double precision multiplication (which isn't required)
-       * by casting the value down to a mp_digit.  Note this requires
-       * that W[ix-1] have  the carry cleared (see after the inner loop)
-       */
-      mp_digit mu;
-      mu = ((W[ix] & MP_MASK) * rho) & MP_MASK;
-
-      /* a = a + mu * m * b**i
-       *
-       * This is computed in place and on the fly.  The multiplication
-       * by b**i is handled by offseting which columns the results
-       * are added to.
-       *
-       * Note the comba method normally doesn't handle carries in the
-       * inner loop In this case we fix the carry from the previous
-       * column since the Montgomery reduction requires digits of the
-       * result (so far) [see above] to work.  This is
-       * handled by fixing up one carry after the inner loop.  The
-       * carry fixups are done in order so after these loops the
-       * first m->used words of W[] have the carries fixed
-       */
-      {
-         int iy;
-         mp_digit *tmpn;
-         mp_word *_W;
-
-         /* alias for the digits of the modulus */
-         tmpn = n->dp;
-
-         /* Alias for the columns set by an offset of ix */
-         _W = W + ix;
-
-         /* inner loop */
-         for (iy = 0; iy < n->used; iy++) {
-            *_W++ += (mp_word)mu * (mp_word)*tmpn++;
-         }
-      }
-
-      /* now fix carry for next digit, W[ix+1] */
-      W[ix + 1] += W[ix] >> (mp_word)DIGIT_BIT;
-   }
-
-   /* now we have to propagate the carries and
-    * shift the words downward [all those least
-    * significant digits we zeroed].
-    */
-   {
-      mp_digit *tmpx;
-      mp_word *_W, *_W1;
-
-      /* nox fix rest of carries */
-
-      /* alias for current word */
-      _W1 = W + ix;
-
-      /* alias for next word, where the carry goes */
-      _W = W + ++ix;
-
-      for (; ix <= ((n->used * 2) + 1); ix++) {
-         *_W++ += *_W1++ >> (mp_word)DIGIT_BIT;
-      }
-
-      /* copy out, A = A/b**n
-       *
-       * The result is A/b**n but instead of converting from an
-       * array of mp_word to mp_digit than calling mp_rshd
-       * we just copy them in the right order
-       */
-
-      /* alias for destination word */
-      tmpx = x->dp;
-
-      /* alias for shifted double precision result */
-      _W = W + n->used;
-
-      for (ix = 0; ix < (n->used + 1); ix++) {
-         *tmpx++ = *_W++ & (mp_word)MP_MASK;
-      }
-
-      /* zero oldused digits, if the input a was larger than
-       * m->used+1 we'll have to clear the digits
-       */
-      for (; ix < olduse; ix++) {
-         *tmpx++ = 0;
-      }
-   }
-
-   /* set the max used and clamp */
-   x->used = n->used + 1;
-   mp_clamp(x);
-
-   /* if A >= m then A = A - m */
-   if (mp_cmp_mag(x, n) != MP_LT) {
-      return s_mp_sub(x, n, x);
-   }
-   return MP_OKAY;
-}
-
-/* End: bn_fast_mp_montgomery_reduce.c */
-
-/* Start: bn_fast_s_mp_mul_digs.c */
-
-/* Fast (comba) multiplier
- *
- * This is the fast column-array [comba] multiplier.  It is
- * designed to compute the columns of the product first
- * then handle the carries afterwards.  This has the effect
- * of making the nested loops that compute the columns very
- * simple and schedulable on super-scalar processors.
- *
- * This has been modified to produce a variable number of
- * digits of output so if say only a half-product is required
- * you don't have to compute the upper half (a feature
- * required for fast Barrett reduction).
- *
- * Based on Algorithm 14.12 on pp.595 of HAC.
- *
- */
-int fast_s_mp_mul_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs)
-{
-   int     olduse, res, pa, ix, iz;
-   mp_digit W[MP_WARRAY];
-   mp_word  _W;
-
-   /* grow the destination as required */
-   if (c->alloc < digs) {
-      if ((res = mp_grow(c, digs)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* number of output digits to produce */
-   pa = MIN(digs, a->used + b->used);
-
-   /* clear the carry */
-   _W = 0;
-   for (ix = 0; ix < pa; ix++) {
-      int      tx, ty;
-      int      iy;
-      mp_digit *tmpx, *tmpy;
-
-      /* get offsets into the two bignums */
-      ty = MIN(b->used-1, ix);
-      tx = ix - ty;
-
-      /* setup temp aliases */
-      tmpx = a->dp + tx;
-      tmpy = b->dp + ty;
-
-      /* this is the number of times the loop will iterrate, essentially
-         while (tx++ < a->used && ty-- >= 0) { ... }
-       */
-      iy = MIN(a->used-tx, ty+1);
-
-      /* execute loop */
-      for (iz = 0; iz < iy; ++iz) {
-         _W += (mp_word)*tmpx++ * (mp_word)*tmpy--;
-
-      }
-
-      /* store term */
-      W[ix] = (mp_digit)_W & MP_MASK;
-
-      /* make next carry */
-      _W = _W >> (mp_word)DIGIT_BIT;
-   }
-
-   /* setup dest */
-   olduse  = c->used;
-   c->used = pa;
-
-   {
-      mp_digit *tmpc;
-      tmpc = c->dp;
-      for (ix = 0; ix < pa; ix++) {
-         /* now extract the previous digit [below the carry] */
-         *tmpc++ = W[ix];
-      }
-
-      /* clear unused digits [that existed in the old copy of c] */
-      for (; ix < olduse; ix++) {
-         *tmpc++ = 0;
-      }
-   }
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_fast_s_mp_mul_digs.c */
-
-/* Start: bn_fast_s_mp_mul_high_digs.c */
-
-/* this is a modified version of fast_s_mul_digs that only produces
- * output digits *above* digs.  See the comments for fast_s_mul_digs
- * to see how it works.
- *
- * This is used in the Barrett reduction since for one of the multiplications
- * only the higher digits were needed.  This essentially halves the work.
- *
- * Based on Algorithm 14.12 on pp.595 of HAC.
- */
-int fast_s_mp_mul_high_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs)
-{
-   int     olduse, res, pa, ix, iz;
-   mp_digit W[MP_WARRAY];
-   mp_word  _W;
-
-   /* grow the destination as required */
-   pa = a->used + b->used;
-   if (c->alloc < pa) {
-      if ((res = mp_grow(c, pa)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* number of output digits to produce */
-   pa = a->used + b->used;
-   _W = 0;
-   for (ix = digs; ix < pa; ix++) {
-      int      tx, ty, iy;
-      mp_digit *tmpx, *tmpy;
-
-      /* get offsets into the two bignums */
-      ty = MIN(b->used-1, ix);
-      tx = ix - ty;
-
-      /* setup temp aliases */
-      tmpx = a->dp + tx;
-      tmpy = b->dp + ty;
-
-      /* this is the number of times the loop will iterrate, essentially its
-         while (tx++ < a->used && ty-- >= 0) { ... }
-       */
-      iy = MIN(a->used-tx, ty+1);
-
-      /* execute loop */
-      for (iz = 0; iz < iy; iz++) {
-         _W += (mp_word)*tmpx++ * (mp_word)*tmpy--;
-      }
-
-      /* store term */
-      W[ix] = (mp_digit)_W & MP_MASK;
-
-      /* make next carry */
-      _W = _W >> (mp_word)DIGIT_BIT;
-   }
-
-   /* setup dest */
-   olduse  = c->used;
-   c->used = pa;
-
-   {
-      mp_digit *tmpc;
-
-      tmpc = c->dp + digs;
-      for (ix = digs; ix < pa; ix++) {
-         /* now extract the previous digit [below the carry] */
-         *tmpc++ = W[ix];
-      }
-
-      /* clear unused digits [that existed in the old copy of c] */
-      for (; ix < olduse; ix++) {
-         *tmpc++ = 0;
-      }
-   }
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_fast_s_mp_mul_high_digs.c */
-
-/* Start: bn_fast_s_mp_sqr.c */
-
-/* the jist of squaring...
- * you do like mult except the offset of the tmpx [one that
- * starts closer to zero] can't equal the offset of tmpy.
- * So basically you set up iy like before then you min it with
- * (ty-tx) so that it never happens.  You double all those
- * you add in the inner loop
-
-After that loop you do the squares and add them in.
-*/
-
-int fast_s_mp_sqr(const mp_int *a, mp_int *b)
-{
-   int       olduse, res, pa, ix, iz;
-   mp_digit   W[MP_WARRAY], *tmpx;
-   mp_word   W1;
-
-   /* grow the destination as required */
-   pa = a->used + a->used;
-   if (b->alloc < pa) {
-      if ((res = mp_grow(b, pa)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* number of output digits to produce */
-   W1 = 0;
-   for (ix = 0; ix < pa; ix++) {
-      int      tx, ty, iy;
-      mp_word  _W;
-      mp_digit *tmpy;
-
-      /* clear counter */
-      _W = 0;
-
-      /* get offsets into the two bignums */
-      ty = MIN(a->used-1, ix);
-      tx = ix - ty;
-
-      /* setup temp aliases */
-      tmpx = a->dp + tx;
-      tmpy = a->dp + ty;
-
-      /* this is the number of times the loop will iterrate, essentially
-         while (tx++ < a->used && ty-- >= 0) { ... }
-       */
-      iy = MIN(a->used-tx, ty+1);
-
-      /* now for squaring tx can never equal ty
-       * we halve the distance since they approach at a rate of 2x
-       * and we have to round because odd cases need to be executed
-       */
-      iy = MIN(iy, ((ty-tx)+1)>>1);
-
-      /* execute loop */
-      for (iz = 0; iz < iy; iz++) {
-         _W += (mp_word)*tmpx++ * (mp_word)*tmpy--;
-      }
-
-      /* double the inner product and add carry */
-      _W = _W + _W + W1;
-
-      /* even columns have the square term in them */
-      if (((unsigned)ix & 1u) == 0u) {
-         _W += (mp_word)a->dp[ix>>1] * (mp_word)a->dp[ix>>1];
-      }
-
-      /* store it */
-      W[ix] = _W & MP_MASK;
-
-      /* make next carry */
-      W1 = _W >> (mp_word)DIGIT_BIT;
-   }
-
-   /* setup dest */
-   olduse  = b->used;
-   b->used = a->used+a->used;
-
-   {
-      mp_digit *tmpb;
-      tmpb = b->dp;
-      for (ix = 0; ix < pa; ix++) {
-         *tmpb++ = W[ix] & MP_MASK;
-      }
-
-      /* clear unused digits [that existed in the old copy of c] */
-      for (; ix < olduse; ix++) {
-         *tmpb++ = 0;
-      }
-   }
-   mp_clamp(b);
-   return MP_OKAY;
-}
-
-/* End: bn_fast_s_mp_sqr.c */
-
-/* Start: bn_mp_2expt.c */
-
-/* computes a = 2**b
- *
- * Simple algorithm which zeroes the int, grows it then just sets one bit
- * as required.
- */
-int mp_2expt(mp_int *a, int b)
-{
-   int     res;
-
-   /* zero a as per default */
-   mp_zero(a);
-
-   /* grow a to accomodate the single bit */
-   if ((res = mp_grow(a, (b / DIGIT_BIT) + 1)) != MP_OKAY) {
-      return res;
-   }
-
-   /* set the used count of where the bit will go */
-   a->used = (b / DIGIT_BIT) + 1;
-
-   /* put the single bit in its place */
-   a->dp[b / DIGIT_BIT] = (mp_digit)1 << (mp_digit)(b % DIGIT_BIT);
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_2expt.c */
-
-/* Start: bn_mp_abs.c */
-
-/* b = |a|
- *
- * Simple function copies the input and fixes the sign to positive
- */
-int mp_abs(const mp_int *a, mp_int *b)
-{
-   int     res;
-
-   /* copy a to b */
-   if (a != b) {
-      if ((res = mp_copy(a, b)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* force the sign of b to positive */
-   b->sign = MP_ZPOS;
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_abs.c */
-
-/* Start: bn_mp_add.c */
-
-/* high level addition (handles signs) */
-int mp_add(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     sa, sb, res;
-
-   /* get sign of both inputs */
-   sa = a->sign;
-   sb = b->sign;
-
-   /* handle two cases, not four */
-   if (sa == sb) {
-      /* both positive or both negative */
-      /* add their magnitudes, copy the sign */
-      c->sign = sa;
-      res = s_mp_add(a, b, c);
-   } else {
-      /* one positive, the other negative */
-      /* subtract the one with the greater magnitude from */
-      /* the one of the lesser magnitude.  The result gets */
-      /* the sign of the one with the greater magnitude. */
-      if (mp_cmp_mag(a, b) == MP_LT) {
-         c->sign = sb;
-         res = s_mp_sub(b, a, c);
-      } else {
-         c->sign = sa;
-         res = s_mp_sub(a, b, c);
-      }
-   }
-   return res;
-}
-
-/* End: bn_mp_add.c */
-
-/* Start: bn_mp_add_d.c */
-
-/* single digit addition */
-int mp_add_d(const mp_int *a, mp_digit b, mp_int *c)
-{
-   int     res, ix, oldused;
-   mp_digit *tmpa, *tmpc, mu;
-
-   /* grow c as required */
-   if (c->alloc < (a->used + 1)) {
-      if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* if a is negative and |a| >= b, call c = |a| - b */
-   if ((a->sign == MP_NEG) && ((a->used > 1) || (a->dp[0] >= b))) {
-      mp_int a_ = *a;
-      /* temporarily fix sign of a */
-      a_.sign = MP_ZPOS;
-
-      /* c = |a| - b */
-      res = mp_sub_d(&a_, b, c);
-
-      /* fix sign  */
-      c->sign = MP_NEG;
-
-      /* clamp */
-      mp_clamp(c);
-
-      return res;
-   }
-
-   /* old number of used digits in c */
-   oldused = c->used;
-
-   /* source alias */
-   tmpa    = a->dp;
-
-   /* destination alias */
-   tmpc    = c->dp;
-
-   /* if a is positive */
-   if (a->sign == MP_ZPOS) {
-      /* add digit, after this we're propagating
-       * the carry.
-       */
-      *tmpc   = *tmpa++ + b;
-      mu      = *tmpc >> DIGIT_BIT;
-      *tmpc++ &= MP_MASK;
-
-      /* now handle rest of the digits */
-      for (ix = 1; ix < a->used; ix++) {
-         *tmpc   = *tmpa++ + mu;
-         mu      = *tmpc >> DIGIT_BIT;
-         *tmpc++ &= MP_MASK;
-      }
-      /* set final carry */
-      ix++;
-      *tmpc++  = mu;
-
-      /* setup size */
-      c->used = a->used + 1;
-   } else {
-      /* a was negative and |a| < b */
-      c->used  = 1;
-
-      /* the result is a single digit */
-      if (a->used == 1) {
-         *tmpc++  =  b - a->dp[0];
-      } else {
-         *tmpc++  =  b;
-      }
-
-      /* setup count so the clearing of oldused
-       * can fall through correctly
-       */
-      ix       = 1;
-   }
-
-   /* sign always positive */
-   c->sign = MP_ZPOS;
-
-   /* now zero to oldused */
-   while (ix++ < oldused) {
-      *tmpc++ = 0;
-   }
-   mp_clamp(c);
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_add_d.c */
-
-/* Start: bn_mp_addmod.c */
-
-/* d = a + b (mod c) */
-int mp_addmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d)
-{
-   int     res;
-   mp_int  t;
-
-   if ((res = mp_init(&t)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_add(a, b, &t)) != MP_OKAY) {
-      mp_clear(&t);
-      return res;
-   }
-   res = mp_mod(&t, c, d);
-   mp_clear(&t);
-   return res;
-}
-
-/* End: bn_mp_addmod.c */
-
-/* Start: bn_mp_and.c */
-
-/* AND two ints together */
-int mp_and(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     res, ix, px;
-   mp_int  t;
-   const mp_int *x;
-
-   if (a->used > b->used) {
-      if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-         return res;
-      }
-      px = b->used;
-      x = b;
-   } else {
-      if ((res = mp_init_copy(&t, b)) != MP_OKAY) {
-         return res;
-      }
-      px = a->used;
-      x = a;
-   }
-
-   for (ix = 0; ix < px; ix++) {
-      t.dp[ix] &= x->dp[ix];
-   }
-
-   /* zero digits above the last from the smallest mp_int */
-   for (; ix < t.used; ix++) {
-      t.dp[ix] = 0;
-   }
-
-   mp_clamp(&t);
-   mp_exch(c, &t);
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_and.c */
-
-/* Start: bn_mp_clamp.c */
-
-/* trim unused digits
- *
- * This is used to ensure that leading zero digits are
- * trimed and the leading "used" digit will be non-zero
- * Typically very fast.  Also fixes the sign if there
- * are no more leading digits
- */
-void mp_clamp(mp_int *a)
-{
-   /* decrease used while the most significant digit is
-    * zero.
-    */
-   while ((a->used > 0) && (a->dp[a->used - 1] == 0u)) {
-      --(a->used);
-   }
-
-   /* reset the sign flag if used == 0 */
-   if (a->used == 0) {
-      a->sign = MP_ZPOS;
-   }
-}
-
-/* End: bn_mp_clamp.c */
-
-/* Start: bn_mp_clear.c */
-
-/* clear one (frees)  */
-void mp_clear(mp_int *a)
-{
-   int i;
-
-   /* only do anything if a hasn't been freed previously */
-   if (a->dp != NULL) {
-      /* first zero the digits */
-      for (i = 0; i < a->used; i++) {
-         a->dp[i] = 0;
-      }
-
-      /* free ram */
-      XFREE(a->dp);
-
-      /* reset members to make debugging easier */
-      a->dp    = NULL;
-      a->alloc = a->used = 0;
-      a->sign  = MP_ZPOS;
-   }
-}
-
-/* End: bn_mp_clear.c */
-
-/* Start: bn_mp_clear_multi.c */
-
-void mp_clear_multi(mp_int *mp, ...)
-{
-   mp_int *next_mp = mp;
-   va_list args;
-   va_start(args, mp);
-   while (next_mp != NULL) {
-      mp_clear(next_mp);
-      next_mp = va_arg(args, mp_int *);
-   }
-   va_end(args);
-}
-
-/* End: bn_mp_clear_multi.c */
-
-/* Start: bn_mp_cmp.c */
-
-/* compare two ints (signed)*/
-int mp_cmp(const mp_int *a, const mp_int *b)
-{
-   /* compare based on sign */
-   if (a->sign != b->sign) {
-      if (a->sign == MP_NEG) {
-         return MP_LT;
-      } else {
-         return MP_GT;
-      }
-   }
-
-   /* compare digits */
-   if (a->sign == MP_NEG) {
-      /* if negative compare opposite direction */
-      return mp_cmp_mag(b, a);
-   } else {
-      return mp_cmp_mag(a, b);
-   }
-}
-
-/* End: bn_mp_cmp.c */
-
-/* Start: bn_mp_cmp_d.c */
-
-/* compare a digit */
-int mp_cmp_d(const mp_int *a, mp_digit b)
-{
-   /* compare based on sign */
-   if (a->sign == MP_NEG) {
-      return MP_LT;
-   }
-
-   /* compare based on magnitude */
-   if (a->used > 1) {
-      return MP_GT;
-   }
-
-   /* compare the only digit of a to b */
-   if (a->dp[0] > b) {
-      return MP_GT;
-   } else if (a->dp[0] < b) {
-      return MP_LT;
-   } else {
-      return MP_EQ;
-   }
-}
-
-/* End: bn_mp_cmp_d.c */
-
-/* Start: bn_mp_cmp_mag.c */
-
-/* compare maginitude of two ints (unsigned) */
-int mp_cmp_mag(const mp_int *a, const mp_int *b)
-{
-   int     n;
-   mp_digit *tmpa, *tmpb;
-
-   /* compare based on # of non-zero digits */
-   if (a->used > b->used) {
-      return MP_GT;
-   }
-
-   if (a->used < b->used) {
-      return MP_LT;
-   }
-
-   /* alias for a */
-   tmpa = a->dp + (a->used - 1);
-
-   /* alias for b */
-   tmpb = b->dp + (a->used - 1);
-
-   /* compare based on digits  */
-   for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
-      if (*tmpa > *tmpb) {
-         return MP_GT;
-      }
-
-      if (*tmpa < *tmpb) {
-         return MP_LT;
-      }
-   }
-   return MP_EQ;
-}
-
-/* End: bn_mp_cmp_mag.c */
-
-/* Start: bn_mp_cnt_lsb.c */
-
-static const int lnz[16] = {
-   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
-};
-
-/* Counts the number of lsbs which are zero before the first zero bit */
-int mp_cnt_lsb(const mp_int *a)
-{
-   int x;
-   mp_digit q, qq;
-
-   /* easy out */
-   if (mp_iszero(a) == MP_YES) {
-      return 0;
-   }
-
-   /* scan lower digits until non-zero */
-   for (x = 0; (x < a->used) && (a->dp[x] == 0u); x++) {}
-   q = a->dp[x];
-   x *= DIGIT_BIT;
-
-   /* now scan this digit until a 1 is found */
-   if ((q & 1u) == 0u) {
-      do {
-         qq  = q & 15u;
-         x  += lnz[qq];
-         q >>= 4;
-      } while (qq == 0u);
-   }
-   return x;
-}
-
-/* End: bn_mp_cnt_lsb.c */
-
-/* Start: bn_mp_complement.c */
-
-/* b = ~a */
-int mp_complement(const mp_int *a, mp_int *b)
-{
-   int res = mp_neg(a, b);
-   return (res == MP_OKAY) ? mp_sub_d(b, 1uL, b) : res;
-}
-
-/* End: bn_mp_complement.c */
-
-/* Start: bn_mp_copy.c */
-
-/* copy, b = a */
-int mp_copy(const mp_int *a, mp_int *b)
-{
-   int     res, n;
-
-   /* if dst == src do nothing */
-   if (a == b) {
-      return MP_OKAY;
-   }
-
-   /* grow dest */
-   if (b->alloc < a->used) {
-      if ((res = mp_grow(b, a->used)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* zero b and copy the parameters over */
-   {
-      mp_digit *tmpa, *tmpb;
-
-      /* pointer aliases */
-
-      /* source */
-      tmpa = a->dp;
-
-      /* destination */
-      tmpb = b->dp;
-
-      /* copy all the digits */
-      for (n = 0; n < a->used; n++) {
-         *tmpb++ = *tmpa++;
-      }
-
-      /* clear high digits */
-      for (; n < b->used; n++) {
-         *tmpb++ = 0;
-      }
-   }
-
-   /* copy used count and sign */
-   b->used = a->used;
-   b->sign = a->sign;
-   return MP_OKAY;
-}
-
-/* End: bn_mp_copy.c */
-
-/* Start: bn_mp_count_bits.c */
-
-/* returns the number of bits in an int */
-int mp_count_bits(const mp_int *a)
-{
-   int     r;
-   mp_digit q;
-
-   /* shortcut */
-   if (a->used == 0) {
-      return 0;
-   }
-
-   /* get number of digits and add that */
-   r = (a->used - 1) * DIGIT_BIT;
-
-   /* take the last digit and count the bits in it */
-   q = a->dp[a->used - 1];
-   while (q > (mp_digit)0) {
-      ++r;
-      q >>= (mp_digit)1;
-   }
-   return r;
-}
-
-/* End: bn_mp_count_bits.c */
-
-/* Start: bn_mp_div.c */
-
-/* integer signed division.
- * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
- * HAC pp.598 Algorithm 14.20
- *
- * Note that the description in HAC is horribly
- * incomplete.  For example, it doesn't consider
- * the case where digits are removed from 'x' in
- * the inner loop.  It also doesn't consider the
- * case that y has fewer than three digits, etc..
- *
- * The overall algorithm is as described as
- * 14.20 from HAC but fixed to treat these cases.
-*/
-int mp_div(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d)
-{
-   mp_int  q, x, y, t1, t2;
-   int     res, n, t, i, norm, neg;
-
-   /* is divisor zero ? */
-   if (mp_iszero(b) == MP_YES) {
-      return MP_VAL;
-   }
-
-   /* if a < b then q=0, r = a */
-   if (mp_cmp_mag(a, b) == MP_LT) {
-      if (d != NULL) {
-         res = mp_copy(a, d);
-      } else {
-         res = MP_OKAY;
-      }
-      if (c != NULL) {
-         mp_zero(c);
-      }
-      return res;
-   }
-
-   if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) {
-      return res;
-   }
-   q.used = a->used + 2;
-
-   if ((res = mp_init(&t1)) != MP_OKAY) {
-      goto LBL_Q;
-   }
-
-   if ((res = mp_init(&t2)) != MP_OKAY) {
-      goto LBL_T1;
-   }
-
-   if ((res = mp_init_copy(&x, a)) != MP_OKAY) {
-      goto LBL_T2;
-   }
-
-   if ((res = mp_init_copy(&y, b)) != MP_OKAY) {
-      goto LBL_X;
-   }
-
-   /* fix the sign */
-   neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
-   x.sign = y.sign = MP_ZPOS;
-
-   /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
-   norm = mp_count_bits(&y) % DIGIT_BIT;
-   if (norm < (DIGIT_BIT - 1)) {
-      norm = (DIGIT_BIT - 1) - norm;
-      if ((res = mp_mul_2d(&x, norm, &x)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-      if ((res = mp_mul_2d(&y, norm, &y)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-   } else {
-      norm = 0;
-   }
-
-   /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
-   n = x.used - 1;
-   t = y.used - 1;
-
-   /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
-   if ((res = mp_lshd(&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
-      goto LBL_Y;
-   }
-
-   while (mp_cmp(&x, &y) != MP_LT) {
-      ++(q.dp[n - t]);
-      if ((res = mp_sub(&x, &y, &x)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-   }
-
-   /* reset y by shifting it back down */
-   mp_rshd(&y, n - t);
-
-   /* step 3. for i from n down to (t + 1) */
-   for (i = n; i >= (t + 1); i--) {
-      if (i > x.used) {
-         continue;
-      }
-
-      /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
-       * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
-      if (x.dp[i] == y.dp[t]) {
-         q.dp[(i - t) - 1] = ((mp_digit)1 << (mp_digit)DIGIT_BIT) - (mp_digit)1;
-      } else {
-         mp_word tmp;
-         tmp = (mp_word)x.dp[i] << (mp_word)DIGIT_BIT;
-         tmp |= (mp_word)x.dp[i - 1];
-         tmp /= (mp_word)y.dp[t];
-         if (tmp > (mp_word)MP_MASK) {
-            tmp = MP_MASK;
-         }
-         q.dp[(i - t) - 1] = (mp_digit)(tmp & (mp_word)MP_MASK);
-      }
-
-      /* while (q{i-t-1} * (yt * b + y{t-1})) >
-               xi * b**2 + xi-1 * b + xi-2
-
-         do q{i-t-1} -= 1;
-      */
-      q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1uL) & (mp_digit)MP_MASK;
-      do {
-         q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & (mp_digit)MP_MASK;
-
-         /* find left hand */
-         mp_zero(&t1);
-         t1.dp[0] = ((t - 1) < 0) ? 0u : y.dp[t - 1];
-         t1.dp[1] = y.dp[t];
-         t1.used = 2;
-         if ((res = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) {
-            goto LBL_Y;
-         }
-
-         /* find right hand */
-         t2.dp[0] = ((i - 2) < 0) ? 0u : x.dp[i - 2];
-         t2.dp[1] = ((i - 1) < 0) ? 0u : x.dp[i - 1];
-         t2.dp[2] = x.dp[i];
-         t2.used = 3;
-      } while (mp_cmp_mag(&t1, &t2) == MP_GT);
-
-      /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
-      if ((res = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-
-      if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-
-      if ((res = mp_sub(&x, &t1, &x)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-
-      /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
-      if (x.sign == MP_NEG) {
-         if ((res = mp_copy(&y, &t1)) != MP_OKAY) {
-            goto LBL_Y;
-         }
-         if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) {
-            goto LBL_Y;
-         }
-         if ((res = mp_add(&x, &t1, &x)) != MP_OKAY) {
-            goto LBL_Y;
-         }
-
-         q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & MP_MASK;
-      }
-   }
-
-   /* now q is the quotient and x is the remainder
-    * [which we have to normalize]
-    */
-
-   /* get sign before writing to c */
-   x.sign = (x.used == 0) ? MP_ZPOS : a->sign;
-
-   if (c != NULL) {
-      mp_clamp(&q);
-      mp_exch(&q, c);
-      c->sign = neg;
-   }
-
-   if (d != NULL) {
-      if ((res = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) {
-         goto LBL_Y;
-      }
-      mp_exch(&x, d);
-   }
-
-   res = MP_OKAY;
-
-LBL_Y:
-   mp_clear(&y);
-LBL_X:
-   mp_clear(&x);
-LBL_T2:
-   mp_clear(&t2);
-LBL_T1:
-   mp_clear(&t1);
-LBL_Q:
-   mp_clear(&q);
-   return res;
-}
-
-/* End: bn_mp_div.c */
-
-/* Start: bn_mp_div_2.c */
-
-/* b = a/2 */
-int mp_div_2(const mp_int *a, mp_int *b)
-{
-   int     x, res, oldused;
-
-   /* copy */
-   if (b->alloc < a->used) {
-      if ((res = mp_grow(b, a->used)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   oldused = b->used;
-   b->used = a->used;
-   {
-      mp_digit r, rr, *tmpa, *tmpb;
-
-      /* source alias */
-      tmpa = a->dp + b->used - 1;
-
-      /* dest alias */
-      tmpb = b->dp + b->used - 1;
-
-      /* carry */
-      r = 0;
-      for (x = b->used - 1; x >= 0; x--) {
-         /* get the carry for the next iteration */
-         rr = *tmpa & 1u;
-
-         /* shift the current digit, add in carry and store */
-         *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
-
-         /* forward carry to next iteration */
-         r = rr;
-      }
-
-      /* zero excess digits */
-      tmpb = b->dp + b->used;
-      for (x = b->used; x < oldused; x++) {
-         *tmpb++ = 0;
-      }
-   }
-   b->sign = a->sign;
-   mp_clamp(b);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_div_2.c */
-
-/* Start: bn_mp_div_2d.c */
-
-/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
-int mp_div_2d(const mp_int *a, int b, mp_int *c, mp_int *d)
-{
-   mp_digit D, r, rr;
-   int     x, res;
-
-   /* if the shift count is <= 0 then we do no work */
-   if (b <= 0) {
-      res = mp_copy(a, c);
-      if (d != NULL) {
-         mp_zero(d);
-      }
-      return res;
-   }
-
-   /* copy */
-   if ((res = mp_copy(a, c)) != MP_OKAY) {
-      return res;
-   }
-   /* 'a' should not be used after here - it might be the same as d */
-
-   /* get the remainder */
-   if (d != NULL) {
-      if ((res = mp_mod_2d(a, b, d)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* shift by as many digits in the bit count */
-   if (b >= DIGIT_BIT) {
-      mp_rshd(c, b / DIGIT_BIT);
-   }
-
-   /* shift any bit count < DIGIT_BIT */
-   D = (mp_digit)(b % DIGIT_BIT);
-   if (D != 0u) {
-      mp_digit *tmpc, mask, shift;
-
-      /* mask */
-      mask = ((mp_digit)1 << D) - 1uL;
-
-      /* shift for lsb */
-      shift = (mp_digit)DIGIT_BIT - D;
-
-      /* alias */
-      tmpc = c->dp + (c->used - 1);
-
-      /* carry */
-      r = 0;
-      for (x = c->used - 1; x >= 0; x--) {
-         /* get the lower  bits of this word in a temp */
-         rr = *tmpc & mask;
-
-         /* shift the current word and mix in the carry bits from the previous word */
-         *tmpc = (*tmpc >> D) | (r << shift);
-         --tmpc;
-
-         /* set the carry to the carry bits of the current word found above */
-         r = rr;
-      }
-   }
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_div_2d.c */
-
-/* Start: bn_mp_div_3.c */
-
-/* divide by three (based on routine from MPI and the GMP manual) */
-int mp_div_3(const mp_int *a, mp_int *c, mp_digit *d)
-{
-   mp_int   q;
-   mp_word  w, t;
-   mp_digit b;
-   int      res, ix;
-
-   /* b = 2**DIGIT_BIT / 3 */
-   b = ((mp_word)1 << (mp_word)DIGIT_BIT) / (mp_word)3;
-
-   if ((res = mp_init_size(&q, a->used)) != MP_OKAY) {
-      return res;
-   }
-
-   q.used = a->used;
-   q.sign = a->sign;
-   w = 0;
-   for (ix = a->used - 1; ix >= 0; ix--) {
-      w = (w << (mp_word)DIGIT_BIT) | (mp_word)a->dp[ix];
-
-      if (w >= 3u) {
-         /* multiply w by [1/3] */
-         t = (w * (mp_word)b) >> (mp_word)DIGIT_BIT;
-
-         /* now subtract 3 * [w/3] from w, to get the remainder */
-         w -= t+t+t;
-
-         /* fixup the remainder as required since
-          * the optimization is not exact.
-          */
-         while (w >= 3u) {
-            t += 1u;
-            w -= 3u;
-         }
-      } else {
-         t = 0;
-      }
-      q.dp[ix] = (mp_digit)t;
-   }
-
-   /* [optional] store the remainder */
-   if (d != NULL) {
-      *d = (mp_digit)w;
-   }
-
-   /* [optional] store the quotient */
-   if (c != NULL) {
-      mp_clamp(&q);
-      mp_exch(&q, c);
-   }
-   mp_clear(&q);
-
-   return res;
-}
-
-/* End: bn_mp_div_3.c */
-
-/* Start: bn_mp_div_d.c */
-
-static int s_is_power_of_two(mp_digit b, int *p)
-{
-   int x;
-
-   /* fast return if no power of two */
-   if ((b == 0u) || ((b & (b-1u)) != 0u)) {
-      return 0;
-   }
-
-   for (x = 0; x < DIGIT_BIT; x++) {
-      if (b == ((mp_digit)1<<(mp_digit)x)) {
-         *p = x;
-         return 1;
-      }
-   }
-   return 0;
-}
-
-/* single digit division (based on routine from MPI) */
-int mp_div_d(const mp_int *a, mp_digit b, mp_int *c, mp_digit *d)
-{
-   mp_int  q;
-   mp_word w;
-   mp_digit t;
-   int     res, ix;
-
-   /* cannot divide by zero */
-   if (b == 0u) {
-      return MP_VAL;
-   }
-
-   /* quick outs */
-   if ((b == 1u) || (mp_iszero(a) == MP_YES)) {
-      if (d != NULL) {
-         *d = 0;
-      }
-      if (c != NULL) {
-         return mp_copy(a, c);
-      }
-      return MP_OKAY;
-   }
-
-   /* power of two ? */
-   if (s_is_power_of_two(b, &ix) == 1) {
-      if (d != NULL) {
-         *d = a->dp[0] & (((mp_digit)1<<(mp_digit)ix) - 1uL);
-      }
-      if (c != NULL) {
-         return mp_div_2d(a, ix, c, NULL);
-      }
-      return MP_OKAY;
-   }
-
-   /* three? */
-   if (b == 3u) {
-      return mp_div_3(a, c, d);
-   }
-
-   /* no easy answer [c'est la vie].  Just division */
-   if ((res = mp_init_size(&q, a->used)) != MP_OKAY) {
-      return res;
-   }
-
-   q.used = a->used;
-   q.sign = a->sign;
-   w = 0;
-   for (ix = a->used - 1; ix >= 0; ix--) {
-      w = (w << (mp_word)DIGIT_BIT) | (mp_word)a->dp[ix];
-
-      if (w >= b) {
-         t = (mp_digit)(w / b);
-         w -= (mp_word)t * (mp_word)b;
-      } else {
-         t = 0;
-      }
-      q.dp[ix] = t;
-   }
-
-   if (d != NULL) {
-      *d = (mp_digit)w;
-   }
-
-   if (c != NULL) {
-      mp_clamp(&q);
-      mp_exch(&q, c);
-   }
-   mp_clear(&q);
-
-   return res;
-}
-
-/* End: bn_mp_div_d.c */
-
-/* Start: bn_mp_dr_is_modulus.c */
-
-/* determines if a number is a valid DR modulus */
-int mp_dr_is_modulus(const mp_int *a)
-{
-   int ix;
-
-   /* must be at least two digits */
-   if (a->used < 2) {
-      return 0;
-   }
-
-   /* must be of the form b**k - a [a <= b] so all
-    * but the first digit must be equal to -1 (mod b).
-    */
-   for (ix = 1; ix < a->used; ix++) {
-      if (a->dp[ix] != MP_MASK) {
-         return 0;
-      }
-   }
-   return 1;
-}
-
-/* End: bn_mp_dr_is_modulus.c */
-
-/* Start: bn_mp_dr_reduce.c */
-
-/* reduce "x" in place modulo "n" using the Diminished Radix algorithm.
- *
- * Based on algorithm from the paper
- *
- * "Generating Efficient Primes for Discrete Log Cryptosystems"
- *                 Chae Hoon Lim, Pil Joong Lee,
- *          POSTECH Information Research Laboratories
- *
- * The modulus must be of a special format [see manual]
- *
- * Has been modified to use algorithm 7.10 from the LTM book instead
- *
- * Input x must be in the range 0 <= x <= (n-1)**2
- */
-int mp_dr_reduce(mp_int *x, const mp_int *n, mp_digit k)
-{
-   int      err, i, m;
-   mp_word  r;
-   mp_digit mu, *tmpx1, *tmpx2;
-
-   /* m = digits in modulus */
-   m = n->used;
-
-   /* ensure that "x" has at least 2m digits */
-   if (x->alloc < (m + m)) {
-      if ((err = mp_grow(x, m + m)) != MP_OKAY) {
-         return err;
-      }
-   }
-
-   /* top of loop, this is where the code resumes if
-    * another reduction pass is required.
-    */
-top:
-   /* aliases for digits */
-   /* alias for lower half of x */
-   tmpx1 = x->dp;
-
-   /* alias for upper half of x, or x/B**m */
-   tmpx2 = x->dp + m;
-
-   /* set carry to zero */
-   mu = 0;
-
-   /* compute (x mod B**m) + k * [x/B**m] inline and inplace */
-   for (i = 0; i < m; i++) {
-      r         = ((mp_word)*tmpx2++ * (mp_word)k) + *tmpx1 + mu;
-      *tmpx1++  = (mp_digit)(r & MP_MASK);
-      mu        = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
-   }
-
-   /* set final carry */
-   *tmpx1++ = mu;
-
-   /* zero words above m */
-   for (i = m + 1; i < x->used; i++) {
-      *tmpx1++ = 0;
-   }
-
-   /* clamp, sub and return */
-   mp_clamp(x);
-
-   /* if x >= n then subtract and reduce again
-    * Each successive "recursion" makes the input smaller and smaller.
-    */
-   if (mp_cmp_mag(x, n) != MP_LT) {
-      if ((err = s_mp_sub(x, n, x)) != MP_OKAY) {
-         return err;
-      }
-      goto top;
-   }
-   return MP_OKAY;
-}
-
-/* End: bn_mp_dr_reduce.c */
-
-/* Start: bn_mp_dr_setup.c */
-#include "tommath_private.h"
-
-/* determines the setup value */
-void mp_dr_setup(const mp_int *a, mp_digit *d)
-{
-   /* the casts are required if DIGIT_BIT is one less than
-    * the number of bits in a mp_digit [e.g. DIGIT_BIT==31]
-    */
-   *d = (mp_digit)(((mp_word)1 << (mp_word)DIGIT_BIT) - (mp_word)a->dp[0]);
-}
-
-/* End: bn_mp_dr_setup.c */
-
-/* Start: bn_mp_exch.c */
-
-/* swap the elements of two integers, for cases where you can't simply swap the
- * mp_int pointers around
- */
-void mp_exch(mp_int *a, mp_int *b)
-{
-   mp_int  t;
-
-   t  = *a;
-   *a = *b;
-   *b = t;
-}
-
-/* End: bn_mp_exch.c */
-
-/* Start: bn_mp_export.c */
-
-/* based on gmp's mpz_export.
- * see http://gmplib.org/manual/Integer-Import-and-Export.html
- */
-int mp_export(void *rop, size_t *countp, int order, size_t size,
-              int endian, size_t nails, const mp_int *op)
-{
-   int result;
-   size_t odd_nails, nail_bytes, i, j, bits, count;
-   unsigned char odd_nail_mask;
-
-   mp_int t;
-
-   if ((result = mp_init_copy(&t, op)) != MP_OKAY) {
-      return result;
-   }
-
-   if (endian == 0) {
-      union {
-         unsigned int i;
-         char c[4];
-      } lint;
-      lint.i = 0x01020304;
-
-      endian = (lint.c[0] == '\x04') ? -1 : 1;
-   }
-
-   odd_nails = (nails % 8u);
-   odd_nail_mask = 0xff;
-   for (i = 0; i < odd_nails; ++i) {
-      odd_nail_mask ^= (unsigned char)(1u << (7u - i));
-   }
-   nail_bytes = nails / 8u;
-
-   bits = (size_t)mp_count_bits(&t);
-   count = (bits / ((size * 8u) - nails)) + (((bits % ((size * 8u) - nails)) != 0u) ? 1u : 0u);
-
-   for (i = 0; i < count; ++i) {
-      for (j = 0; j < size; ++j) {
-         unsigned char *byte = (unsigned char *)rop +
-                               (((order == -1) ? i : ((count - 1u) - i)) * size) +
-                               ((endian == -1) ? j : ((size - 1u) - j));
-
-         if (j >= (size - nail_bytes)) {
-            *byte = 0;
-            continue;
-         }
-
-         *byte = (unsigned char)((j == ((size - nail_bytes) - 1u)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFFuL));
-
-         if ((result = mp_div_2d(&t, (j == ((size - nail_bytes) - 1u)) ? (int)(8u - odd_nails) : 8, &t, NULL)) != MP_OKAY) {
-            mp_clear(&t);
-            return result;
-         }
-      }
-   }
-
-   mp_clear(&t);
-
-   if (countp != NULL) {
-      *countp = count;
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_export.c */
-
-/* Start: bn_mp_expt_d.c */
-
-/* wrapper function for mp_expt_d_ex() */
-int mp_expt_d(const mp_int *a, mp_digit b, mp_int *c)
-{
-   return mp_expt_d_ex(a, b, c, 0);
-}
-
-/* End: bn_mp_expt_d.c */
-
-/* Start: bn_mp_expt_d_ex.c */
-
-/* calculate c = a**b  using a square-multiply algorithm */
-int mp_expt_d_ex(const mp_int *a, mp_digit b, mp_int *c, int fast)
-{
-   int     res;
-   unsigned int x;
-
-   mp_int  g;
-
-   if ((res = mp_init_copy(&g, a)) != MP_OKAY) {
-      return res;
-   }
-
-   /* set initial result */
-   mp_set(c, 1uL);
-
-   if (fast != 0) {
-      while (b > 0u) {
-         /* if the bit is set multiply */
-         if ((b & 1u) != 0u) {
-            if ((res = mp_mul(c, &g, c)) != MP_OKAY) {
-               mp_clear(&g);
-               return res;
-            }
-         }
-
-         /* square */
-         if (b > 1u) {
-            if ((res = mp_sqr(&g, &g)) != MP_OKAY) {
-               mp_clear(&g);
-               return res;
-            }
-         }
-
-         /* shift to next bit */
-         b >>= 1;
-      }
-   } else {
-      for (x = 0; x < (unsigned)DIGIT_BIT; x++) {
-         /* square */
-         if ((res = mp_sqr(c, c)) != MP_OKAY) {
-            mp_clear(&g);
-            return res;
-         }
-
-         /* if the bit is set multiply */
-         if ((b & ((mp_digit)1 << (DIGIT_BIT - 1))) != 0u) {
-            if ((res = mp_mul(c, &g, c)) != MP_OKAY) {
-               mp_clear(&g);
-               return res;
-            }
-         }
-
-         /* shift to next bit */
-         b <<= 1;
-      }
-   } /* if ... else */
-
-   mp_clear(&g);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_expt_d_ex.c */
-
-/* Start: bn_mp_exptmod.c */
-
-/* this is a shell function that calls either the normal or Montgomery
- * exptmod functions.  Originally the call to the montgomery code was
- * embedded in the normal function but that wasted alot of stack space
- * for nothing (since 99% of the time the Montgomery code would be called)
- */
-int mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y)
-{
-   int dr;
-
-   /* modulus P must be positive */
-   if (P->sign == MP_NEG) {
-      return MP_VAL;
-   }
-
-   /* if exponent X is negative we have to recurse */
-   if (X->sign == MP_NEG) {
-      mp_int tmpG, tmpX;
-      int err;
-
-      /* first compute 1/G mod P */
-      if ((err = mp_init(&tmpG)) != MP_OKAY) {
-         return err;
-      }
-      if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
-         mp_clear(&tmpG);
-         return err;
-      }
-
-      /* now get |X| */
-      if ((err = mp_init(&tmpX)) != MP_OKAY) {
-         mp_clear(&tmpG);
-         return err;
-      }
-      if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
-         mp_clear_multi(&tmpG, &tmpX, NULL);
-         return err;
-      }
-
-      /* and now compute (1/G)**|X| instead of G**X [X < 0] */
-      err = mp_exptmod(&tmpG, &tmpX, P, Y);
-      mp_clear_multi(&tmpG, &tmpX, NULL);
-      return err;
-   }
-
-   /* modified diminished radix reduction */
-   if (mp_reduce_is_2k_l(P) == MP_YES) {
-      return s_mp_exptmod(G, X, P, Y, 1);
-   }
-
-   /* is it a DR modulus? */
-   dr = mp_dr_is_modulus(P);
-
-   /* if not, is it a unrestricted DR modulus? */
-   if (dr == 0) {
-      dr = mp_reduce_is_2k(P) << 1;
-   }
-
-   /* if the modulus is odd or dr != 0 use the montgomery method */
-   if ((mp_isodd(P) == MP_YES) || (dr !=  0)) {
-      return mp_exptmod_fast(G, X, P, Y, dr);
-   } else {
-      /* otherwise use the generic Barrett reduction technique */
-      return s_mp_exptmod(G, X, P, Y, 0);
-   }
-}
-
-/* End: bn_mp_exptmod.c */
-
-/* Start: bn_mp_exptmod_fast.c */
-
-/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
- *
- * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
- * The value of k changes based on the size of the exponent.
- *
- * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
- */
-
-#ifdef MP_LOW_MEM
-#   define TAB_SIZE 32
-#else
-#   define TAB_SIZE 256
-#endif
-
-int mp_exptmod_fast(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode)
-{
-   mp_int  M[TAB_SIZE], res;
-   mp_digit buf, mp;
-   int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
-
-   /* use a pointer to the reduction algorithm.  This allows us to use
-    * one of many reduction algorithms without modding the guts of
-    * the code with if statements everywhere.
-    */
-   int (*redux)(mp_int *x, const mp_int *n, mp_digit rho);
-
-   /* find window size */
-   x = mp_count_bits(X);
-   if (x <= 7) {
-      winsize = 2;
-   } else if (x <= 36) {
-      winsize = 3;
-   } else if (x <= 140) {
-      winsize = 4;
-   } else if (x <= 450) {
-      winsize = 5;
-   } else if (x <= 1303) {
-      winsize = 6;
-   } else if (x <= 3529) {
-      winsize = 7;
-   } else {
-      winsize = 8;
-   }
-
-#ifdef MP_LOW_MEM
-   if (winsize > 5) {
-      winsize = 5;
-   }
-#endif
-
-   /* init M array */
-   /* init first cell */
-   if ((err = mp_init_size(&M[1], P->alloc)) != MP_OKAY) {
-      return err;
-   }
-
-   /* now init the second half of the array */
-   for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
-      if ((err = mp_init_size(&M[x], P->alloc)) != MP_OKAY) {
-         for (y = 1<<(winsize-1); y < x; y++) {
-            mp_clear(&M[y]);
-         }
-         mp_clear(&M[1]);
-         return err;
-      }
-   }
-
-   /* determine and setup reduction code */
-   if (redmode == 0) {
-      /* now setup montgomery  */
-      if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) {
-         goto LBL_M;
-      }
-
-      /* automatically pick the comba one if available (saves quite a few calls/ifs) */
-      if ((((P->used * 2) + 1) < (int)MP_WARRAY) &&
-          (P->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) {
-         redux = fast_mp_montgomery_reduce;
-      } else
-      {
-         /* use slower baseline Montgomery method */
-         redux = mp_montgomery_reduce;
-      }
-   } else if (redmode == 1) {
-      /* setup DR reduction for moduli of the form B**k - b */
-      mp_dr_setup(P, &mp);
-      redux = mp_dr_reduce;
-   } else {
-      /* setup DR reduction for moduli of the form 2**k - b */
-      if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
-         goto LBL_M;
-      }
-      redux = mp_reduce_2k;
-   }
-
-   /* setup result */
-   if ((err = mp_init_size(&res, P->alloc)) != MP_OKAY) {
-      goto LBL_M;
-   }
-
-   /* create M table
-    *
-
-    *
-    * The first half of the table is not computed though accept for M[0] and M[1]
-    */
-
-   if (redmode == 0) {
-      /* now we need R mod m */
-      if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) {
-         goto LBL_RES;
-      }
-
-      /* now set M[1] to G * R mod m */
-      if ((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) {
-         goto LBL_RES;
-      }
-   } else {
-      mp_set(&res, 1uL);
-      if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
-         goto LBL_RES;
-      }
-   }
-
-   /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
-   if ((err = mp_copy(&M[1], &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) {
-      goto LBL_RES;
-   }
-
-   for (x = 0; x < (winsize - 1); x++) {
-      if ((err = mp_sqr(&M[(size_t)1 << (winsize - 1)], &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) {
-         goto LBL_RES;
-      }
-      if ((err = redux(&M[(size_t)1 << (winsize - 1)], P, mp)) != MP_OKAY) {
-         goto LBL_RES;
-      }
-   }
-
-   /* create upper table */
-   for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
-      if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
-         goto LBL_RES;
-      }
-      if ((err = redux(&M[x], P, mp)) != MP_OKAY) {
-         goto LBL_RES;
-      }
-   }
-
-   /* set initial mode and bit cnt */
-   mode   = 0;
-   bitcnt = 1;
-   buf    = 0;
-   digidx = X->used - 1;
-   bitcpy = 0;
-   bitbuf = 0;
-
-   for (;;) {
-      /* grab next digit as required */
-      if (--bitcnt == 0) {
-         /* if digidx == -1 we are out of digits so break */
-         if (digidx == -1) {
-            break;
-         }
-         /* read next digit and reset bitcnt */
-         buf    = X->dp[digidx--];
-         bitcnt = (int)DIGIT_BIT;
-      }
-
-      /* grab the next msb from the exponent */
-      y     = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
-      buf <<= (mp_digit)1;
-
-      /* if the bit is zero and mode == 0 then we ignore it
-       * These represent the leading zero bits before the first 1 bit
-       * in the exponent.  Technically this opt is not required but it
-       * does lower the # of trivial squaring/reductions used
-       */
-      if ((mode == 0) && (y == 0)) {
-         continue;
-      }
-
-      /* if the bit is zero and mode == 1 then we square */
-      if ((mode == 1) && (y == 0)) {
-         if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         if ((err = redux(&res, P, mp)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         continue;
-      }
-
-      /* else we add it to the window */
-      bitbuf |= (y << (winsize - ++bitcpy));
-      mode    = 2;
-
-      if (bitcpy == winsize) {
-         /* ok window is filled so square as required and multiply  */
-         /* square first */
-         for (x = 0; x < winsize; x++) {
-            if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-            if ((err = redux(&res, P, mp)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-         }
-
-         /* then multiply */
-         if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         if ((err = redux(&res, P, mp)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-
-         /* empty window and reset */
-         bitcpy = 0;
-         bitbuf = 0;
-         mode   = 1;
-      }
-   }
-
-   /* if bits remain then square/multiply */
-   if ((mode == 2) && (bitcpy > 0)) {
-      /* square then multiply if the bit is set */
-      for (x = 0; x < bitcpy; x++) {
-         if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         if ((err = redux(&res, P, mp)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-
-         /* get next bit of the window */
-         bitbuf <<= 1;
-         if ((bitbuf & (1 << winsize)) != 0) {
-            /* then multiply */
-            if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-            if ((err = redux(&res, P, mp)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-         }
-      }
-   }
-
-   if (redmode == 0) {
-      /* fixup result if Montgomery reduction is used
-       * recall that any value in a Montgomery system is
-       * actually multiplied by R mod n.  So we have
-       * to reduce one more time to cancel out the factor
-       * of R.
-       */
-      if ((err = redux(&res, P, mp)) != MP_OKAY) {
-         goto LBL_RES;
-      }
-   }
-
-   /* swap res with Y */
-   mp_exch(&res, Y);
-   err = MP_OKAY;
-LBL_RES:
-   mp_clear(&res);
-LBL_M:
-   mp_clear(&M[1]);
-   for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
-      mp_clear(&M[x]);
-   }
-   return err;
-}
-
-/* End: bn_mp_exptmod_fast.c */
-
-/* Start: bn_mp_exteuclid.c */
-
-/* Extended euclidean algorithm of (a, b) produces
-   a*u1 + b*u2 = u3
- */
-int mp_exteuclid(const mp_int *a, const mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3)
-{
-   mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp;
-   int err;
-
-   if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) {
-      return err;
-   }
-
-   /* initialize, (u1,u2,u3) = (1,0,a) */
-   mp_set(&u1, 1uL);
-   if ((err = mp_copy(a, &u3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* initialize, (v1,v2,v3) = (0,1,b) */
-   mp_set(&v2, 1uL);
-   if ((err = mp_copy(b, &v3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* loop while v3 != 0 */
-   while (mp_iszero(&v3) == MP_NO) {
-      /* q = u3/v3 */
-      if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */
-      if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      /* (u1,u2,u3) = (v1,v2,v3) */
-      if ((err = mp_copy(&v1, &u1)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_copy(&v2, &u2)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_copy(&v3, &u3)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      /* (v1,v2,v3) = (t1,t2,t3) */
-      if ((err = mp_copy(&t1, &v1)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_copy(&t2, &v2)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_copy(&t3, &v3)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* make sure U3 >= 0 */
-   if (u3.sign == MP_NEG) {
-      if ((err = mp_neg(&u1, &u1)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_neg(&u2, &u2)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((err = mp_neg(&u3, &u3)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* copy result out */
-   if (U1 != NULL) {
-      mp_exch(U1, &u1);
-   }
-   if (U2 != NULL) {
-      mp_exch(U2, &u2);
-   }
-   if (U3 != NULL) {
-      mp_exch(U3, &u3);
-   }
-
-   err = MP_OKAY;
-LBL_ERR:
-   mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL);
-   return err;
-}
-
-/* End: bn_mp_exteuclid.c */
-
-/* Start: bn_mp_gcd.c */
-
-/* Greatest Common Divisor using the binary method */
-int mp_gcd(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   mp_int  u, v;
-   int     k, u_lsb, v_lsb, res;
-
-   /* either zero than gcd is the largest */
-   if (mp_iszero(a) == MP_YES) {
-      return mp_abs(b, c);
-   }
-   if (mp_iszero(b) == MP_YES) {
-      return mp_abs(a, c);
-   }
-
-   /* get copies of a and b we can modify */
-   if ((res = mp_init_copy(&u, a)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_init_copy(&v, b)) != MP_OKAY) {
-      goto LBL_U;
-   }
-
-   /* must be positive for the remainder of the algorithm */
-   u.sign = v.sign = MP_ZPOS;
-
-   /* B1.  Find the common power of two for u and v */
-   u_lsb = mp_cnt_lsb(&u);
-   v_lsb = mp_cnt_lsb(&v);
-   k     = MIN(u_lsb, v_lsb);
-
-   if (k > 0) {
-      /* divide the power of two out */
-      if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) {
-         goto LBL_V;
-      }
-
-      if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) {
-         goto LBL_V;
-      }
-   }
-
-   /* divide any remaining factors of two out */
-   if (u_lsb != k) {
-      if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) {
-         goto LBL_V;
-      }
-   }
-
-   if (v_lsb != k) {
-      if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) {
-         goto LBL_V;
-      }
-   }
-
-   while (mp_iszero(&v) == MP_NO) {
-      /* make sure v is the largest */
-      if (mp_cmp_mag(&u, &v) == MP_GT) {
-         /* swap u and v to make sure v is >= u */
-         mp_exch(&u, &v);
-      }
-
-      /* subtract smallest from largest */
-      if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) {
-         goto LBL_V;
-      }
-
-      /* Divide out all factors of two */
-      if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) {
-         goto LBL_V;
-      }
-   }
-
-   /* multiply by 2**k which we divided out at the beginning */
-   if ((res = mp_mul_2d(&u, k, c)) != MP_OKAY) {
-      goto LBL_V;
-   }
-   c->sign = MP_ZPOS;
-   res = MP_OKAY;
-LBL_V:
-   mp_clear(&u);
-LBL_U:
-   mp_clear(&v);
-   return res;
-}
-
-/* End: bn_mp_gcd.c */
-
-/* Start: bn_mp_get_bit.c */
-
-/* Checks the bit at position b and returns MP_YES
-   if the bit is 1, MP_NO if it is 0 and MP_VAL
-   in case of error */
-int mp_get_bit(const mp_int *a, int b)
-{
-   int limb;
-   mp_digit bit, isset;
-
-   if (b < 0) {
-      return MP_VAL;
-   }
-
-   limb = b / DIGIT_BIT;
-
-   /*
-    * Zero is a special value with the member "used" set to zero.
-    * Needs to be tested before the check for the upper boundary
-    * otherwise (limb >= a->used) would be true for a = 0
-    */
-
-   if (mp_iszero(a) != MP_NO) {
-      return MP_NO;
-   }
-
-   if (limb >= a->used) {
-      return MP_VAL;
-   }
-
-   bit = (mp_digit)(1) << (b % DIGIT_BIT);
-
-   isset = a->dp[limb] & bit;
-   return (isset != 0u) ? MP_YES : MP_NO;
-}
-
-/* End: bn_mp_get_bit.c */
-
-/* Start: bn_mp_get_double.c */
-
-double mp_get_double(const mp_int *a)
-{
-   int i;
-   double d = 0.0, fac = 1.0;
-   for (i = 0; i < DIGIT_BIT; ++i) {
-      fac *= 2.0;
-   }
-   for (i = USED(a); i --> 0;) {
-      d = (d * fac) + (double)DIGIT(a, i);
-   }
-   return (mp_isneg(a) != MP_NO) ? -d : d;
-}
-
-/* End: bn_mp_get_double.c */
-
-/* Start: bn_mp_get_int.c */
-
-/* get the lower 32-bits of an mp_int */
-unsigned long mp_get_int(const mp_int *a)
-{
-   int i;
-   mp_min_u32 res;
-
-   if (a->used == 0) {
-      return 0;
-   }
-
-   /* get number of digits of the lsb we have to read */
-   i = MIN(a->used, ((((int)sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1;
-
-   /* get most significant digit of result */
-   res = DIGIT(a, i);
-
-   while (--i >= 0) {
-      res = (res << DIGIT_BIT) | DIGIT(a, i);
-   }
-
-   /* force result to 32-bits always so it is consistent on non 32-bit platforms */
-   return res & 0xFFFFFFFFUL;
-}
-
-/* End: bn_mp_get_int.c */
-
-/* Start: bn_mp_get_long.c */
-
-/* get the lower unsigned long of an mp_int, platform dependent */
-unsigned long mp_get_long(const mp_int *a)
-{
-   int i;
-   unsigned long res;
-
-   if (a->used == 0) {
-      return 0;
-   }
-
-   /* get number of digits of the lsb we have to read */
-   i = MIN(a->used, ((((int)sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1;
-
-   /* get most significant digit of result */
-   res = DIGIT(a, i);
-
-#if (ULONG_MAX != 0xffffffffuL) || (DIGIT_BIT < 32)
-   while (--i >= 0) {
-      res = (res << DIGIT_BIT) | DIGIT(a, i);
-   }
-#endif
-   return res;
-}
-
-/* End: bn_mp_get_long.c */
-
-/* Start: bn_mp_get_long_long.c */
-
-/* get the lower unsigned long long of an mp_int, platform dependent */
-unsigned long long mp_get_long_long(const mp_int *a)
-{
-   int i;
-   unsigned long long res;
-
-   if (a->used == 0) {
-      return 0;
-   }
-
-   /* get number of digits of the lsb we have to read */
-   i = MIN(a->used, ((((int)sizeof(unsigned long long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1;
-
-   /* get most significant digit of result */
-   res = DIGIT(a, i);
-
-#if DIGIT_BIT < 64
-   while (--i >= 0) {
-      res = (res << DIGIT_BIT) | DIGIT(a, i);
-   }
-#endif
-   return res;
-}
-
-/* End: bn_mp_get_long_long.c */
-
-/* Start: bn_mp_grow.c */
-
-/* grow as required */
-int mp_grow(mp_int *a, int size)
-{
-   int     i;
-   mp_digit *tmp;
-
-   /* if the alloc size is smaller alloc more ram */
-   if (a->alloc < size) {
-      /* ensure there are always at least MP_PREC digits extra on top */
-      size += (MP_PREC * 2) - (size % MP_PREC);
-
-      /* reallocate the array a->dp
-       *
-       * We store the return in a temporary variable
-       * in case the operation failed we don't want
-       * to overwrite the dp member of a.
-       */
-      tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * (size_t)size);
-      if (tmp == NULL) {
-         /* reallocation failed but "a" is still valid [can be freed] */
-         return MP_MEM;
-      }
-
-      /* reallocation succeeded so set a->dp */
-      a->dp = tmp;
-
-      /* zero excess digits */
-      i        = a->alloc;
-      a->alloc = size;
-      for (; i < a->alloc; i++) {
-         a->dp[i] = 0;
-      }
-   }
-   return MP_OKAY;
-}
-
-/* End: bn_mp_grow.c */
-
-/* Start: bn_mp_import.c */
-
-/* based on gmp's mpz_import.
- * see http://gmplib.org/manual/Integer-Import-and-Export.html
- */
-int mp_import(mp_int *rop, size_t count, int order, size_t size,
-              int endian, size_t nails, const void *op)
-{
-   int result;
-   size_t odd_nails, nail_bytes, i, j;
-   unsigned char odd_nail_mask;
-
-   mp_zero(rop);
-
-   if (endian == 0) {
-      union {
-         unsigned int i;
-         char c[4];
-      } lint;
-      lint.i = 0x01020304;
-
-      endian = (lint.c[0] == '\x04') ? -1 : 1;
-   }
-
-   odd_nails = (nails % 8u);
-   odd_nail_mask = 0xff;
-   for (i = 0; i < odd_nails; ++i) {
-      odd_nail_mask ^= (unsigned char)(1u << (7u - i));
-   }
-   nail_bytes = nails / 8u;
-
-   for (i = 0; i < count; ++i) {
-      for (j = 0; j < (size - nail_bytes); ++j) {
-         unsigned char byte = *((unsigned char *)op +
-                                (((order == 1) ? i : ((count - 1u) - i)) * size) +
-                                ((endian == 1) ? (j + nail_bytes) : (((size - 1u) - j) - nail_bytes)));
-
-         if ((result = mp_mul_2d(rop, (j == 0u) ? (int)(8u - odd_nails) : 8, rop)) != MP_OKAY) {
-            return result;
-         }
-
-         rop->dp[0] |= (j == 0u) ? (mp_digit)(byte & odd_nail_mask) : (mp_digit)byte;
-         rop->used  += 1;
-      }
-   }
-
-   mp_clamp(rop);
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_import.c */
-
-/* Start: bn_mp_init.c */
-
-/* init a new mp_int */
-int mp_init(mp_int *a)
-{
-   int i;
-
-   /* allocate memory required and clear it */
-   a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * (size_t)MP_PREC);
-   if (a->dp == NULL) {
-      return MP_MEM;
-   }
-
-   /* set the digits to zero */
-   for (i = 0; i < MP_PREC; i++) {
-      a->dp[i] = 0;
-   }
-
-   /* set the used to zero, allocated digits to the default precision
-    * and sign to positive */
-   a->used  = 0;
-   a->alloc = MP_PREC;
-   a->sign  = MP_ZPOS;
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_init.c */
-
-/* Start: bn_mp_init_copy.c */
-
-/* creates "a" then copies b into it */
-int mp_init_copy(mp_int *a, const mp_int *b)
-{
-   int     res;
-
-   if ((res = mp_init_size(a, b->used)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_copy(b, a)) != MP_OKAY) {
-      mp_clear(a);
-   }
-
-   return res;
-}
-
-/* End: bn_mp_init_copy.c */
-
-/* Start: bn_mp_init_multi.c */
-
-int mp_init_multi(mp_int *mp, ...)
-{
-   mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
-   int n = 0;                 /* Number of ok inits */
-   mp_int *cur_arg = mp;
-   va_list args;
-
-   va_start(args, mp);        /* init args to next argument from caller */
-   while (cur_arg != NULL) {
-      if (mp_init(cur_arg) != MP_OKAY) {
-         /* Oops - error! Back-track and mp_clear what we already
-            succeeded in init-ing, then return error.
-         */
-         va_list clean_args;
-
-         /* now start cleaning up */
-         cur_arg = mp;
-         va_start(clean_args, mp);
-         while (n-- != 0) {
-            mp_clear(cur_arg);
-            cur_arg = va_arg(clean_args, mp_int *);
-         }
-         va_end(clean_args);
-         res = MP_MEM;
-         break;
-      }
-      n++;
-      cur_arg = va_arg(args, mp_int *);
-   }
-   va_end(args);
-   return res;                /* Assumed ok, if error flagged above. */
-}
-
-/* End: bn_mp_init_multi.c */
-
-/* Start: bn_mp_init_set.c */
-
-/* initialize and set a digit */
-int mp_init_set(mp_int *a, mp_digit b)
-{
-   int err;
-   if ((err = mp_init(a)) != MP_OKAY) {
-      return err;
-   }
-   mp_set(a, b);
-   return err;
-}
-
-/* End: bn_mp_init_set.c */
-
-/* Start: bn_mp_init_set_int.c */
-
-/* initialize and set a digit */
-int mp_init_set_int(mp_int *a, unsigned long b)
-{
-   int err;
-   if ((err = mp_init(a)) != MP_OKAY) {
-      return err;
-   }
-   return mp_set_int(a, b);
-}
-
-/* End: bn_mp_init_set_int.c */
-
-/* Start: bn_mp_init_size.c */
-
-/* init an mp_init for a given size */
-int mp_init_size(mp_int *a, int size)
-{
-   int x;
-
-   /* pad size so there are always extra digits */
-   size += (MP_PREC * 2) - (size % MP_PREC);
-
-   /* alloc mem */
-   a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * (size_t)size);
-   if (a->dp == NULL) {
-      return MP_MEM;
-   }
-
-   /* set the members */
-   a->used  = 0;
-   a->alloc = size;
-   a->sign  = MP_ZPOS;
-
-   /* zero the digits */
-   for (x = 0; x < size; x++) {
-      a->dp[x] = 0;
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_init_size.c */
-
-/* Start: bn_mp_invmod.c */
-
-/* hac 14.61, pp608 */
-int mp_invmod(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   /* b cannot be negative and has to be >1 */
-   if ((b->sign == MP_NEG) || (mp_cmp_d(b, 1uL) != MP_GT)) {
-      return MP_VAL;
-   }
-
-   /* if the modulus is odd we can use a faster routine instead */
-   if ((mp_isodd(b) == MP_YES)) {
-      return fast_mp_invmod(a, b, c);
-   }
-
-   return mp_invmod_slow(a, b, c);
-}
-
-/* End: bn_mp_invmod.c */
-
-/* Start: bn_mp_invmod_slow.c */
-
-/* hac 14.61, pp608 */
-int mp_invmod_slow(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   mp_int  x, y, u, v, A, B, C, D;
-   int     res;
-
-   /* b cannot be negative */
-   if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) {
-      return MP_VAL;
-   }
-
-   /* init temps */
-   if ((res = mp_init_multi(&x, &y, &u, &v,
-                            &A, &B, &C, &D, NULL)) != MP_OKAY) {
-      return res;
-   }
-
-   /* x = a, y = b */
-   if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_copy(b, &y)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* 2. [modified] if x,y are both even then return an error! */
-   if ((mp_iseven(&x) == MP_YES) && (mp_iseven(&y) == MP_YES)) {
-      res = MP_VAL;
-      goto LBL_ERR;
-   }
-
-   /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
-   if ((res = mp_copy(&x, &u)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_copy(&y, &v)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_set(&A, 1uL);
-   mp_set(&D, 1uL);
-
-top:
-   /* 4.  while u is even do */
-   while (mp_iseven(&u) == MP_YES) {
-      /* 4.1 u = u/2 */
-      if ((res = mp_div_2(&u, &u)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      /* 4.2 if A or B is odd then */
-      if ((mp_isodd(&A) == MP_YES) || (mp_isodd(&B) == MP_YES)) {
-         /* A = (A+y)/2, B = (B-x)/2 */
-         if ((res = mp_add(&A, &y, &A)) != MP_OKAY) {
-            goto LBL_ERR;
-         }
-         if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) {
-            goto LBL_ERR;
-         }
-      }
-      /* A = A/2, B = B/2 */
-      if ((res = mp_div_2(&A, &A)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((res = mp_div_2(&B, &B)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* 5.  while v is even do */
-   while (mp_iseven(&v) == MP_YES) {
-      /* 5.1 v = v/2 */
-      if ((res = mp_div_2(&v, &v)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      /* 5.2 if C or D is odd then */
-      if ((mp_isodd(&C) == MP_YES) || (mp_isodd(&D) == MP_YES)) {
-         /* C = (C+y)/2, D = (D-x)/2 */
-         if ((res = mp_add(&C, &y, &C)) != MP_OKAY) {
-            goto LBL_ERR;
-         }
-         if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) {
-            goto LBL_ERR;
-         }
-      }
-      /* C = C/2, D = D/2 */
-      if ((res = mp_div_2(&C, &C)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if ((res = mp_div_2(&D, &D)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* 6.  if u >= v then */
-   if (mp_cmp(&u, &v) != MP_LT) {
-      /* u = u - v, A = A - C, B = B - D */
-      if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      if ((res = mp_sub(&A, &C, &A)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   } else {
-      /* v - v - u, C = C - A, D = D - B */
-      if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      if ((res = mp_sub(&C, &A, &C)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* if not zero goto step 4 */
-   if (mp_iszero(&u) == MP_NO)
-      goto top;
-
-   /* now a = C, b = D, gcd == g*v */
-
-   /* if v != 1 then there is no inverse */
-   if (mp_cmp_d(&v, 1uL) != MP_EQ) {
-      res = MP_VAL;
-      goto LBL_ERR;
-   }
-
-   /* if its too low */
-   while (mp_cmp_d(&C, 0uL) == MP_LT) {
-      if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* too big */
-   while (mp_cmp_mag(&C, b) != MP_LT) {
-      if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* C is now the inverse */
-   mp_exch(&C, c);
-   res = MP_OKAY;
-LBL_ERR:
-   mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL);
-   return res;
-}
-
-/* End: bn_mp_invmod_slow.c */
-
-/* Start: bn_mp_is_square.c */
-
-/* Check if remainders are possible squares - fast exclude non-squares */
-static const char rem_128[128] = {
-   0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
-   1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1
-};
-
-static const char rem_105[105] = {
-   0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-   0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
-   0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1,
-   1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
-   0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
-   1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1,
-   1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1
-};
-
-/* Store non-zero to ret if arg is square, and zero if not */
-int mp_is_square(const mp_int *arg, int *ret)
-{
-   int           res;
-   mp_digit      c;
-   mp_int        t;
-   unsigned long r;
-
-   /* Default to Non-square :) */
-   *ret = MP_NO;
-
-   if (arg->sign == MP_NEG) {
-      return MP_VAL;
-   }
-
-   /* digits used?  (TSD) */
-   if (arg->used == 0) {
-      return MP_OKAY;
-   }
-
-   /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */
-   if (rem_128[127u & DIGIT(arg, 0)] == (char)1) {
-      return MP_OKAY;
-   }
-
-   /* Next check mod 105 (3*5*7) */
-   if ((res = mp_mod_d(arg, 105uL, &c)) != MP_OKAY) {
-      return res;
-   }
-   if (rem_105[c] == (char)1) {
-      return MP_OKAY;
-   }
-
-
-   if ((res = mp_init_set_int(&t, 11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) {
-      return res;
-   }
-   if ((res = mp_mod(arg, &t, &t)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   r = mp_get_int(&t);
-   /* Check for other prime modules, note it's not an ERROR but we must
-    * free "t" so the easiest way is to goto LBL_ERR.  We know that res
-    * is already equal to MP_OKAY from the mp_mod call
-    */
-   if (((1uL<<(r%11uL)) & 0x5C4uL) != 0uL)         goto LBL_ERR;
-   if (((1uL<<(r%13uL)) & 0x9E4uL) != 0uL)         goto LBL_ERR;
-   if (((1uL<<(r%17uL)) & 0x5CE8uL) != 0uL)        goto LBL_ERR;
-   if (((1uL<<(r%19uL)) & 0x4F50CuL) != 0uL)       goto LBL_ERR;
-   if (((1uL<<(r%23uL)) & 0x7ACCA0uL) != 0uL)      goto LBL_ERR;
-   if (((1uL<<(r%29uL)) & 0xC2EDD0CuL) != 0uL)     goto LBL_ERR;
-   if (((1uL<<(r%31uL)) & 0x6DE2B848uL) != 0uL)    goto LBL_ERR;
-
-   /* Final check - is sqr(sqrt(arg)) == arg ? */
-   if ((res = mp_sqrt(arg, &t)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sqr(&t, &t)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   *ret = (mp_cmp_mag(&t, arg) == MP_EQ) ? MP_YES : MP_NO;
-LBL_ERR:
-   mp_clear(&t);
-   return res;
-}
-
-/* End: bn_mp_is_square.c */
-
-/* Start: bn_mp_jacobi.c */
-
-/* computes the jacobi c = (a | n) (or Legendre if n is prime)
- * Kept for legacy reasons, please use mp_kronecker() instead
- */
-int mp_jacobi(const mp_int *a, const mp_int *n, int *c)
-{
-   /* if a < 0 return MP_VAL */
-   if (mp_isneg(a) == MP_YES) {
-      return MP_VAL;
-   }
-
-   /* if n <= 0 return MP_VAL */
-   if (mp_cmp_d(n, 0uL) != MP_GT) {
-      return MP_VAL;
-   }
-
-   return mp_kronecker(a, n, c);
-}
-
-/* End: bn_mp_jacobi.c */
-
-/* Start: bn_mp_karatsuba_mul.c */
-
-/* c = |a| * |b| using Karatsuba Multiplication using
- * three half size multiplications
- *
- * Let B represent the radix [e.g. 2**DIGIT_BIT] and
- * let n represent half of the number of digits in
- * the min(a,b)
- *
- * a = a1 * B**n + a0
- * b = b1 * B**n + b0
- *
- * Then, a * b =>
-   a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0
- *
- * Note that a1b1 and a0b0 are used twice and only need to be
- * computed once.  So in total three half size (half # of
- * digit) multiplications are performed, a0b0, a1b1 and
- * (a1+b1)(a0+b0)
- *
- * Note that a multiplication of half the digits requires
- * 1/4th the number of single precision multiplications so in
- * total after one call 25% of the single precision multiplications
- * are saved.  Note also that the call to mp_mul can end up back
- * in this function if the a0, a1, b0, or b1 are above the threshold.
- * This is known as divide-and-conquer and leads to the famous
- * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than
- * the standard O(N**2) that the baseline/comba methods use.
- * Generally though the overhead of this method doesn't pay off
- * until a certain size (N ~ 80) is reached.
- */
-int mp_karatsuba_mul(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   mp_int  x0, x1, y0, y1, t1, x0y0, x1y1;
-   int     B, err;
-
-   /* default the return code to an error */
-   err = MP_MEM;
-
-   /* min # of digits */
-   B = MIN(a->used, b->used);
-
-   /* now divide in two */
-   B = B >> 1;
-
-   /* init copy all the temps */
-   if (mp_init_size(&x0, B) != MP_OKAY)
-      goto LBL_ERR;
-   if (mp_init_size(&x1, a->used - B) != MP_OKAY)
-      goto X0;
-   if (mp_init_size(&y0, B) != MP_OKAY)
-      goto X1;
-   if (mp_init_size(&y1, b->used - B) != MP_OKAY)
-      goto Y0;
-
-   /* init temps */
-   if (mp_init_size(&t1, B * 2) != MP_OKAY)
-      goto Y1;
-   if (mp_init_size(&x0y0, B * 2) != MP_OKAY)
-      goto T1;
-   if (mp_init_size(&x1y1, B * 2) != MP_OKAY)
-      goto X0Y0;
-
-   /* now shift the digits */
-   x0.used = y0.used = B;
-   x1.used = a->used - B;
-   y1.used = b->used - B;
-
-   {
-      int x;
-      mp_digit *tmpa, *tmpb, *tmpx, *tmpy;
-
-      /* we copy the digits directly instead of using higher level functions
-       * since we also need to shift the digits
-       */
-      tmpa = a->dp;
-      tmpb = b->dp;
-
-      tmpx = x0.dp;
-      tmpy = y0.dp;
-      for (x = 0; x < B; x++) {
-         *tmpx++ = *tmpa++;
-         *tmpy++ = *tmpb++;
-      }
-
-      tmpx = x1.dp;
-      for (x = B; x < a->used; x++) {
-         *tmpx++ = *tmpa++;
-      }
-
-      tmpy = y1.dp;
-      for (x = B; x < b->used; x++) {
-         *tmpy++ = *tmpb++;
-      }
-   }
-
-   /* only need to clamp the lower words since by definition the
-    * upper words x1/y1 must have a known number of digits
-    */
-   mp_clamp(&x0);
-   mp_clamp(&y0);
-
-   /* now calc the products x0y0 and x1y1 */
-   /* after this x0 is no longer required, free temp [x0==t2]! */
-   if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY)
-      goto X1Y1;          /* x0y0 = x0*y0 */
-   if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY)
-      goto X1Y1;          /* x1y1 = x1*y1 */
-
-   /* now calc x1+x0 and y1+y0 */
-   if (s_mp_add(&x1, &x0, &t1) != MP_OKAY)
-      goto X1Y1;          /* t1 = x1 - x0 */
-   if (s_mp_add(&y1, &y0, &x0) != MP_OKAY)
-      goto X1Y1;          /* t2 = y1 - y0 */
-   if (mp_mul(&t1, &x0, &t1) != MP_OKAY)
-      goto X1Y1;          /* t1 = (x1 + x0) * (y1 + y0) */
-
-   /* add x0y0 */
-   if (mp_add(&x0y0, &x1y1, &x0) != MP_OKAY)
-      goto X1Y1;          /* t2 = x0y0 + x1y1 */
-   if (s_mp_sub(&t1, &x0, &t1) != MP_OKAY)
-      goto X1Y1;          /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */
-
-   /* shift by B */
-   if (mp_lshd(&t1, B) != MP_OKAY)
-      goto X1Y1;          /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<<B */
-   if (mp_lshd(&x1y1, B * 2) != MP_OKAY)
-      goto X1Y1;          /* x1y1 = x1y1 << 2*B */
-
-   if (mp_add(&x0y0, &t1, &t1) != MP_OKAY)
-      goto X1Y1;          /* t1 = x0y0 + t1 */
-   if (mp_add(&t1, &x1y1, c) != MP_OKAY)
-      goto X1Y1;          /* t1 = x0y0 + t1 + x1y1 */
-
-   /* Algorithm succeeded set the return code to MP_OKAY */
-   err = MP_OKAY;
-
-X1Y1:
-   mp_clear(&x1y1);
-X0Y0:
-   mp_clear(&x0y0);
-T1:
-   mp_clear(&t1);
-Y1:
-   mp_clear(&y1);
-Y0:
-   mp_clear(&y0);
-X1:
-   mp_clear(&x1);
-X0:
-   mp_clear(&x0);
-LBL_ERR:
-   return err;
-}
-
-/* End: bn_mp_karatsuba_mul.c */
-
-/* Start: bn_mp_karatsuba_sqr.c */
-
-/* Karatsuba squaring, computes b = a*a using three
- * half size squarings
- *
- * See comments of karatsuba_mul for details.  It
- * is essentially the same algorithm but merely
- * tuned to perform recursive squarings.
- */
-int mp_karatsuba_sqr(const mp_int *a, mp_int *b)
-{
-   mp_int  x0, x1, t1, t2, x0x0, x1x1;
-   int     B, err;
-
-   err = MP_MEM;
-
-   /* min # of digits */
-   B = a->used;
-
-   /* now divide in two */
-   B = B >> 1;
-
-   /* init copy all the temps */
-   if (mp_init_size(&x0, B) != MP_OKAY)
-      goto LBL_ERR;
-   if (mp_init_size(&x1, a->used - B) != MP_OKAY)
-      goto X0;
-
-   /* init temps */
-   if (mp_init_size(&t1, a->used * 2) != MP_OKAY)
-      goto X1;
-   if (mp_init_size(&t2, a->used * 2) != MP_OKAY)
-      goto T1;
-   if (mp_init_size(&x0x0, B * 2) != MP_OKAY)
-      goto T2;
-   if (mp_init_size(&x1x1, (a->used - B) * 2) != MP_OKAY)
-      goto X0X0;
-
-   {
-      int x;
-      mp_digit *dst, *src;
-
-      src = a->dp;
-
-      /* now shift the digits */
-      dst = x0.dp;
-      for (x = 0; x < B; x++) {
-         *dst++ = *src++;
-      }
-
-      dst = x1.dp;
-      for (x = B; x < a->used; x++) {
-         *dst++ = *src++;
-      }
-   }
-
-   x0.used = B;
-   x1.used = a->used - B;
-
-   mp_clamp(&x0);
-
-   /* now calc the products x0*x0 and x1*x1 */
-   if (mp_sqr(&x0, &x0x0) != MP_OKAY)
-      goto X1X1;           /* x0x0 = x0*x0 */
-   if (mp_sqr(&x1, &x1x1) != MP_OKAY)
-      goto X1X1;           /* x1x1 = x1*x1 */
-
-   /* now calc (x1+x0)**2 */
-   if (s_mp_add(&x1, &x0, &t1) != MP_OKAY)
-      goto X1X1;           /* t1 = x1 - x0 */
-   if (mp_sqr(&t1, &t1) != MP_OKAY)
-      goto X1X1;           /* t1 = (x1 - x0) * (x1 - x0) */
-
-   /* add x0y0 */
-   if (s_mp_add(&x0x0, &x1x1, &t2) != MP_OKAY)
-      goto X1X1;           /* t2 = x0x0 + x1x1 */
-   if (s_mp_sub(&t1, &t2, &t1) != MP_OKAY)
-      goto X1X1;           /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */
-
-   /* shift by B */
-   if (mp_lshd(&t1, B) != MP_OKAY)
-      goto X1X1;           /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<<B */
-   if (mp_lshd(&x1x1, B * 2) != MP_OKAY)
-      goto X1X1;           /* x1x1 = x1x1 << 2*B */
-
-   if (mp_add(&x0x0, &t1, &t1) != MP_OKAY)
-      goto X1X1;           /* t1 = x0x0 + t1 */
-   if (mp_add(&t1, &x1x1, b) != MP_OKAY)
-      goto X1X1;           /* t1 = x0x0 + t1 + x1x1 */
-
-   err = MP_OKAY;
-
-X1X1:
-   mp_clear(&x1x1);
-X0X0:
-   mp_clear(&x0x0);
-T2:
-   mp_clear(&t2);
-T1:
-   mp_clear(&t1);
-X1:
-   mp_clear(&x1);
-X0:
-   mp_clear(&x0);
-LBL_ERR:
-   return err;
-}
-
-/* End: bn_mp_karatsuba_sqr.c */
-
-/* Start: bn_mp_kronecker.c */
-
-/*
-   Kronecker symbol (a|p)
-   Straightforward implementation of algorithm 1.4.10 in
-   Henri Cohen: "A Course in Computational Algebraic Number Theory"
-
-   @book{cohen2013course,
-     title={A course in computational algebraic number theory},
-     author={Cohen, Henri},
-     volume={138},
-     year={2013},
-     publisher={Springer Science \& Business Media}
-    }
- */
-int mp_kronecker(const mp_int *a, const mp_int *p, int *c)
-{
-   mp_int a1, p1, r;
-
-   int e = MP_OKAY;
-   int v, k;
-
-   static const int table[8] = {0, 1, 0, -1, 0, -1, 0, 1};
-
-   if (mp_iszero(p) != MP_NO) {
-      if ((a->used == 1) && (a->dp[0] == 1u)) {
-         *c = 1;
-         return e;
-      } else {
-         *c = 0;
-         return e;
-      }
-   }
-
-   if ((mp_iseven(a) != MP_NO) && (mp_iseven(p) != MP_NO)) {
-      *c = 0;
-      return e;
-   }
-
-   if ((e = mp_init_copy(&a1, a)) != MP_OKAY) {
-      return e;
-   }
-   if ((e = mp_init_copy(&p1, p)) != MP_OKAY) {
-      goto LBL_KRON_0;
-   }
-
-   v = mp_cnt_lsb(&p1);
-   if ((e = mp_div_2d(&p1, v, &p1, NULL)) != MP_OKAY) {
-      goto LBL_KRON_1;
-   }
-
-   if ((v & 0x1) == 0) {
-      k = 1;
-   } else {
-      k = table[a->dp[0] & 7u];
-   }
-
-   if (p1.sign == MP_NEG) {
-      p1.sign = MP_ZPOS;
-      if (a1.sign == MP_NEG) {
-         k = -k;
-      }
-   }
-
-   if ((e = mp_init(&r)) != MP_OKAY) {
-      goto LBL_KRON_1;
-   }
-
-   for (;;) {
-      if (mp_iszero(&a1) != MP_NO) {
-         if (mp_cmp_d(&p1, 1uL) == MP_EQ) {
-            *c = k;
-            goto LBL_KRON;
-         } else {
-            *c = 0;
-            goto LBL_KRON;
-         }
-      }
-
-      v = mp_cnt_lsb(&a1);
-      if ((e = mp_div_2d(&a1, v, &a1, NULL)) != MP_OKAY) {
-         goto LBL_KRON;
-      }
-
-      if ((v & 0x1) == 1) {
-         k = k * table[p1.dp[0] & 7u];
-      }
-
-      if (a1.sign == MP_NEG) {
-         /*
-          * Compute k = (-1)^((a1)*(p1-1)/4) * k
-          * a1.dp[0] + 1 cannot overflow because the MSB
-          * of the type mp_digit is not set by definition
-          */
-         if (((a1.dp[0] + 1u) & p1.dp[0] & 2u) != 0u) {
-            k = -k;
-         }
-      } else {
-         /* compute k = (-1)^((a1-1)*(p1-1)/4) * k */
-         if ((a1.dp[0] & p1.dp[0] & 2u) != 0u) {
-            k = -k;
-         }
-      }
-
-      if ((e = mp_copy(&a1, &r)) != MP_OKAY) {
-         goto LBL_KRON;
-      }
-      r.sign = MP_ZPOS;
-      if ((e = mp_mod(&p1, &r, &a1)) != MP_OKAY) {
-         goto LBL_KRON;
-      }
-      if ((e = mp_copy(&r, &p1)) != MP_OKAY) {
-         goto LBL_KRON;
-      }
-   }
-
-LBL_KRON:
-   mp_clear(&r);
-LBL_KRON_1:
-   mp_clear(&p1);
-LBL_KRON_0:
-   mp_clear(&a1);
-
-   return e;
-}
-
-/* End: bn_mp_kronecker.c */
-
-/* Start: bn_mp_lcm.c */
-
-/* computes least common multiple as |a*b|/(a, b) */
-int mp_lcm(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     res;
-   mp_int  t1, t2;
-
-
-   if ((res = mp_init_multi(&t1, &t2, NULL)) != MP_OKAY) {
-      return res;
-   }
-
-   /* t1 = get the GCD of the two inputs */
-   if ((res = mp_gcd(a, b, &t1)) != MP_OKAY) {
-      goto LBL_T;
-   }
-
-   /* divide the smallest by the GCD */
-   if (mp_cmp_mag(a, b) == MP_LT) {
-      /* store quotient in t2 such that t2 * b is the LCM */
-      if ((res = mp_div(a, &t1, &t2, NULL)) != MP_OKAY) {
-         goto LBL_T;
-      }
-      res = mp_mul(b, &t2, c);
-   } else {
-      /* store quotient in t2 such that t2 * a is the LCM */
-      if ((res = mp_div(b, &t1, &t2, NULL)) != MP_OKAY) {
-         goto LBL_T;
-      }
-      res = mp_mul(a, &t2, c);
-   }
-
-   /* fix the sign to positive */
-   c->sign = MP_ZPOS;
-
-LBL_T:
-   mp_clear_multi(&t1, &t2, NULL);
-   return res;
-}
-
-/* End: bn_mp_lcm.c */
-
-/* Start: bn_mp_lshd.c */
-
-/* shift left a certain amount of digits */
-int mp_lshd(mp_int *a, int b)
-{
-   int     x, res;
-
-   /* if its less than zero return */
-   if (b <= 0) {
-      return MP_OKAY;
-   }
-   /* no need to shift 0 around */
-   if (mp_iszero(a) == MP_YES) {
-      return MP_OKAY;
-   }
-
-   /* grow to fit the new digits */
-   if (a->alloc < (a->used + b)) {
-      if ((res = mp_grow(a, a->used + b)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   {
-      mp_digit *top, *bottom;
-
-      /* increment the used by the shift amount then copy upwards */
-      a->used += b;
-
-      /* top */
-      top = a->dp + a->used - 1;
-
-      /* base */
-      bottom = (a->dp + a->used - 1) - b;
-
-      /* much like mp_rshd this is implemented using a sliding window
-       * except the window goes the otherway around.  Copying from
-       * the bottom to the top.  see bn_mp_rshd.c for more info.
-       */
-      for (x = a->used - 1; x >= b; x--) {
-         *top-- = *bottom--;
-      }
-
-      /* zero the lower digits */
-      top = a->dp;
-      for (x = 0; x < b; x++) {
-         *top++ = 0;
-      }
-   }
-   return MP_OKAY;
-}
-
-/* End: bn_mp_lshd.c */
-
-/* Start: bn_mp_mod.c */
-
-/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */
-int mp_mod(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   mp_int  t;
-   int     res;
-
-   if ((res = mp_init_size(&t, b->used)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_div(a, b, NULL, &t)) != MP_OKAY) {
-      mp_clear(&t);
-      return res;
-   }
-
-   if ((mp_iszero(&t) != MP_NO) || (t.sign == b->sign)) {
-      res = MP_OKAY;
-      mp_exch(&t, c);
-   } else {
-      res = mp_add(b, &t, c);
-   }
-
-   mp_clear(&t);
-   return res;
-}
-
-/* End: bn_mp_mod.c */
-
-/* Start: bn_mp_mod_2d.c */
-
-/* calc a value mod 2**b */
-int mp_mod_2d(const mp_int *a, int b, mp_int *c)
-{
-   int     x, res;
-
-   /* if b is <= 0 then zero the int */
-   if (b <= 0) {
-      mp_zero(c);
-      return MP_OKAY;
-   }
-
-   /* if the modulus is larger than the value than return */
-   if (b >= (a->used * DIGIT_BIT)) {
-      res = mp_copy(a, c);
-      return res;
-   }
-
-   /* copy */
-   if ((res = mp_copy(a, c)) != MP_OKAY) {
-      return res;
-   }
-
-   /* zero digits above the last digit of the modulus */
-   for (x = (b / DIGIT_BIT) + (((b % DIGIT_BIT) == 0) ? 0 : 1); x < c->used; x++) {
-      c->dp[x] = 0;
-   }
-   /* clear the digit that is not completely outside/inside the modulus */
-   c->dp[b / DIGIT_BIT] &=
-      ((mp_digit)1 << (mp_digit)(b % DIGIT_BIT)) - (mp_digit)1;
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_mod_2d.c */
-
-/* Start: bn_mp_mod_d.c */
-
-int mp_mod_d(const mp_int *a, mp_digit b, mp_digit *c)
-{
-   return mp_div_d(a, b, NULL, c);
-}
-
-/* End: bn_mp_mod_d.c */
-
-/* Start: bn_mp_montgomery_calc_normalization.c */
-
-/*
- * shifts with subtractions when the result is greater than b.
- *
- * The method is slightly modified to shift B unconditionally upto just under
- * the leading bit of b.  This saves alot of multiple precision shifting.
- */
-int mp_montgomery_calc_normalization(mp_int *a, const mp_int *b)
-{
-   int     x, bits, res;
-
-   /* how many bits of last digit does b use */
-   bits = mp_count_bits(b) % DIGIT_BIT;
-
-   if (b->used > 1) {
-      if ((res = mp_2expt(a, ((b->used - 1) * DIGIT_BIT) + bits - 1)) != MP_OKAY) {
-         return res;
-      }
-   } else {
-      mp_set(a, 1uL);
-      bits = 1;
-   }
-
-
-   /* now compute C = A * B mod b */
-   for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
-      if ((res = mp_mul_2(a, a)) != MP_OKAY) {
-         return res;
-      }
-      if (mp_cmp_mag(a, b) != MP_LT) {
-         if ((res = s_mp_sub(a, b, a)) != MP_OKAY) {
-            return res;
-         }
-      }
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_montgomery_calc_normalization.c */
-
-/* Start: bn_mp_montgomery_reduce.c */
-
-/* computes xR**-1 == x (mod N) via Montgomery Reduction */
-int mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho)
-{
-   int     ix, res, digs;
-   mp_digit mu;
-
-   /* can the fast reduction [comba] method be used?
-    *
-    * Note that unlike in mul you're safely allowed *less*
-    * than the available columns [255 per default] since carries
-    * are fixed up in the inner loop.
-    */
-   digs = (n->used * 2) + 1;
-   if ((digs < (int)MP_WARRAY) &&
-       (x->used <= (int)MP_WARRAY) &&
-       (n->used <
-        (int)(1u << (((size_t)CHAR_BIT * sizeof(mp_word)) - (2u * (size_t)DIGIT_BIT))))) {
-      return fast_mp_montgomery_reduce(x, n, rho);
-   }
-
-   /* grow the input as required */
-   if (x->alloc < digs) {
-      if ((res = mp_grow(x, digs)) != MP_OKAY) {
-         return res;
-      }
-   }
-   x->used = digs;
-
-   for (ix = 0; ix < n->used; ix++) {
-      /* mu = ai * rho mod b
-       *
-       * The value of rho must be precalculated via
-       * montgomery_setup() such that
-       * it equals -1/n0 mod b this allows the
-       * following inner loop to reduce the
-       * input one digit at a time
-       */
-      mu = (mp_digit)(((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK);
-
-      /* a = a + mu * m * b**i */
-      {
-         int iy;
-         mp_digit *tmpn, *tmpx, u;
-         mp_word r;
-
-         /* alias for digits of the modulus */
-         tmpn = n->dp;
-
-         /* alias for the digits of x [the input] */
-         tmpx = x->dp + ix;
-
-         /* set the carry to zero */
-         u = 0;
-
-         /* Multiply and add in place */
-         for (iy = 0; iy < n->used; iy++) {
-            /* compute product and sum */
-            r       = ((mp_word)mu * (mp_word)*tmpn++) +
-                      (mp_word)u + (mp_word)*tmpx;
-
-            /* get carry */
-            u       = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-
-            /* fix digit */
-            *tmpx++ = (mp_digit)(r & (mp_word)MP_MASK);
-         }
-         /* At this point the ix'th digit of x should be zero */
-
-
-         /* propagate carries upwards as required*/
-         while (u != 0u) {
-            *tmpx   += u;
-            u        = *tmpx >> DIGIT_BIT;
-            *tmpx++ &= MP_MASK;
-         }
-      }
-   }
-
-   /* at this point the n.used'th least
-    * significant digits of x are all zero
-    * which means we can shift x to the
-    * right by n.used digits and the
-    * residue is unchanged.
-    */
-
-   /* x = x/b**n.used */
-   mp_clamp(x);
-   mp_rshd(x, n->used);
-
-   /* if x >= n then x = x - n */
-   if (mp_cmp_mag(x, n) != MP_LT) {
-      return s_mp_sub(x, n, x);
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_montgomery_reduce.c */
-
-/* Start: bn_mp_montgomery_setup.c */
-
-/* setups the montgomery reduction stuff */
-int mp_montgomery_setup(const mp_int *n, mp_digit *rho)
-{
-   mp_digit x, b;
-
-   /* fast inversion mod 2**k
-    *
-    * Based on the fact that
-    *
-    * XA = 1 (mod 2**n)  =>  (X(2-XA)) A = 1 (mod 2**2n)
-    *                    =>  2*X*A - X*X*A*A = 1
-    *                    =>  2*(1) - (1)     = 1
-    */
-   b = n->dp[0];
-
-   if ((b & 1u) == 0u) {
-      return MP_VAL;
-   }
-
-   x = (((b + 2u) & 4u) << 1) + b; /* here x*a==1 mod 2**4 */
-   x *= 2u - (b * x);              /* here x*a==1 mod 2**8 */
-#if !defined(MP_8BIT)
-   x *= 2u - (b * x);              /* here x*a==1 mod 2**16 */
-#endif
-#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
-   x *= 2u - (b * x);              /* here x*a==1 mod 2**32 */
-#endif
-#ifdef MP_64BIT
-   x *= 2u - (b * x);              /* here x*a==1 mod 2**64 */
-#endif
-
-   /* rho = -1/m mod b */
-   *rho = (mp_digit)(((mp_word)1 << (mp_word)DIGIT_BIT) - x) & MP_MASK;
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_montgomery_setup.c */
-
-/* Start: bn_mp_mul.c */
-
-/* high level multiplication (handles sign) */
-int mp_mul(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     res, neg;
-   neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
-
-   /* use Toom-Cook? */
-   if (MIN(a->used, b->used) >= TOOM_MUL_CUTOFF) {
-      res = mp_toom_mul(a, b, c);
-   } else
-      /* use Karatsuba? */
-      if (MIN(a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
-         res = mp_karatsuba_mul(a, b, c);
-      } else
-      {
-         /* can we use the fast multiplier?
-          *
-          * The fast multiplier can be used if the output will
-          * have less than MP_WARRAY digits and the number of
-          * digits won't affect carry propagation
-          */
-         int     digs = a->used + b->used + 1;
-
-         if ((digs < (int)MP_WARRAY) &&
-             (MIN(a->used, b->used) <=
-              (int)(1u << (((size_t)CHAR_BIT * sizeof(mp_word)) - (2u * (size_t)DIGIT_BIT))))) {
-            res = fast_s_mp_mul_digs(a, b, c, digs);
-         } else
-         {
-            res = s_mp_mul(a, b, c); /* uses s_mp_mul_digs */
-         }
-      }
-   c->sign = (c->used > 0) ? neg : MP_ZPOS;
-   return res;
-}
-
-/* End: bn_mp_mul.c */
-
-/* Start: bn_mp_mul_2.c */
-
-/* b = a*2 */
-int mp_mul_2(const mp_int *a, mp_int *b)
-{
-   int     x, res, oldused;
-
-   /* grow to accomodate result */
-   if (b->alloc < (a->used + 1)) {
-      if ((res = mp_grow(b, a->used + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   oldused = b->used;
-   b->used = a->used;
-
-   {
-      mp_digit r, rr, *tmpa, *tmpb;
-
-      /* alias for source */
-      tmpa = a->dp;
-
-      /* alias for dest */
-      tmpb = b->dp;
-
-      /* carry */
-      r = 0;
-      for (x = 0; x < a->used; x++) {
-
-         /* get what will be the *next* carry bit from the
-          * MSB of the current digit
-          */
-         rr = *tmpa >> (mp_digit)(DIGIT_BIT - 1);
-
-         /* now shift up this digit, add in the carry [from the previous] */
-         *tmpb++ = ((*tmpa++ << 1uL) | r) & MP_MASK;
-
-         /* copy the carry that would be from the source
-          * digit into the next iteration
-          */
-         r = rr;
-      }
-
-      /* new leading digit? */
-      if (r != 0u) {
-         /* add a MSB which is always 1 at this point */
-         *tmpb = 1;
-         ++(b->used);
-      }
-
-      /* now zero any excess digits on the destination
-       * that we didn't write to
-       */
-      tmpb = b->dp + b->used;
-      for (x = b->used; x < oldused; x++) {
-         *tmpb++ = 0;
-      }
-   }
-   b->sign = a->sign;
-   return MP_OKAY;
-}
-
-/* End: bn_mp_mul_2.c */
-
-/* Start: bn_mp_mul_2d.c */
-
-/* shift left by a certain bit count */
-int mp_mul_2d(const mp_int *a, int b, mp_int *c)
-{
-   mp_digit d;
-   int      res;
-
-   /* copy */
-   if (a != c) {
-      if ((res = mp_copy(a, c)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   if (c->alloc < (c->used + (b / DIGIT_BIT) + 1)) {
-      if ((res = mp_grow(c, c->used + (b / DIGIT_BIT) + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* shift by as many digits in the bit count */
-   if (b >= DIGIT_BIT) {
-      if ((res = mp_lshd(c, b / DIGIT_BIT)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* shift any bit count < DIGIT_BIT */
-   d = (mp_digit)(b % DIGIT_BIT);
-   if (d != 0u) {
-      mp_digit *tmpc, shift, mask, r, rr;
-      int x;
-
-      /* bitmask for carries */
-      mask = ((mp_digit)1 << d) - (mp_digit)1;
-
-      /* shift for msbs */
-      shift = (mp_digit)DIGIT_BIT - d;
-
-      /* alias */
-      tmpc = c->dp;
-
-      /* carry */
-      r    = 0;
-      for (x = 0; x < c->used; x++) {
-         /* get the higher bits of the current word */
-         rr = (*tmpc >> shift) & mask;
-
-         /* shift the current word and OR in the carry */
-         *tmpc = ((*tmpc << d) | r) & MP_MASK;
-         ++tmpc;
-
-         /* set the carry to the carry bits of the current word */
-         r = rr;
-      }
-
-      /* set final carry */
-      if (r != 0u) {
-         c->dp[(c->used)++] = r;
-      }
-   }
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_mul_2d.c */
-
-/* Start: bn_mp_mul_d.c */
-
-/* multiply by a digit */
-int mp_mul_d(const mp_int *a, mp_digit b, mp_int *c)
-{
-   mp_digit u, *tmpa, *tmpc;
-   mp_word  r;
-   int      ix, res, olduse;
-
-   /* make sure c is big enough to hold a*b */
-   if (c->alloc < (a->used + 1)) {
-      if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* get the original destinations used count */
-   olduse = c->used;
-
-   /* set the sign */
-   c->sign = a->sign;
-
-   /* alias for a->dp [source] */
-   tmpa = a->dp;
-
-   /* alias for c->dp [dest] */
-   tmpc = c->dp;
-
-   /* zero carry */
-   u = 0;
-
-   /* compute columns */
-   for (ix = 0; ix < a->used; ix++) {
-      /* compute product and carry sum for this term */
-      r       = (mp_word)u + ((mp_word)*tmpa++ * (mp_word)b);
-
-      /* mask off higher bits to get a single digit */
-      *tmpc++ = (mp_digit)(r & (mp_word)MP_MASK);
-
-      /* send carry into next iteration */
-      u       = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-   }
-
-   /* store final carry [if any] and increment ix offset  */
-   *tmpc++ = u;
-   ++ix;
-
-   /* now zero digits above the top */
-   while (ix++ < olduse) {
-      *tmpc++ = 0;
-   }
-
-   /* set used count */
-   c->used = a->used + 1;
-   mp_clamp(c);
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_mul_d.c */
-
-/* Start: bn_mp_mulmod.c */
-
-/* d = a * b (mod c) */
-int mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d)
-{
-   int     res;
-   mp_int  t;
-
-   if ((res = mp_init_size(&t, c->used)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_mul(a, b, &t)) != MP_OKAY) {
-      mp_clear(&t);
-      return res;
-   }
-   res = mp_mod(&t, c, d);
-   mp_clear(&t);
-   return res;
-}
-
-/* End: bn_mp_mulmod.c */
-
-/* Start: bn_mp_n_root.c */
-
-/* wrapper function for mp_n_root_ex()
- * computes c = (a)**(1/b) such that (c)**b <= a and (c+1)**b > a
- */
-int mp_n_root(const mp_int *a, mp_digit b, mp_int *c)
-{
-   return mp_n_root_ex(a, b, c, 0);
-}
-
-/* End: bn_mp_n_root.c */
-
-/* Start: bn_mp_n_root_ex.c */
-
-/* find the n'th root of an integer
- *
- * Result found such that (c)**b <= a and (c+1)**b > a
- *
- * This algorithm uses Newton's approximation
- * x[i+1] = x[i] - f(x[i])/f'(x[i])
- * which will find the root in log(N) time where
- * each step involves a fair bit.  This is not meant to
- * find huge roots [square and cube, etc].
- */
-int mp_n_root_ex(const mp_int *a, mp_digit b, mp_int *c, int fast)
-{
-   mp_int  t1, t2, t3, a_;
-   int     res;
-
-   /* input must be positive if b is even */
-   if (((b & 1u) == 0u) && (a->sign == MP_NEG)) {
-      return MP_VAL;
-   }
-
-   if ((res = mp_init(&t1)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_init(&t2)) != MP_OKAY) {
-      goto LBL_T1;
-   }
-
-   if ((res = mp_init(&t3)) != MP_OKAY) {
-      goto LBL_T2;
-   }
-
-   /* if a is negative fudge the sign but keep track */
-   a_ = *a;
-   a_.sign = MP_ZPOS;
-
-   /* t2 = 2 */
-   mp_set(&t2, 2uL);
-
-   do {
-      /* t1 = t2 */
-      if ((res = mp_copy(&t2, &t1)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */
-
-      /* t3 = t1**(b-1) */
-      if ((res = mp_expt_d_ex(&t1, b - 1u, &t3, fast)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      /* numerator */
-      /* t2 = t1**b */
-      if ((res = mp_mul(&t3, &t1, &t2)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      /* t2 = t1**b - a */
-      if ((res = mp_sub(&t2, &a_, &t2)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      /* denominator */
-      /* t3 = t1**(b-1) * b  */
-      if ((res = mp_mul_d(&t3, b, &t3)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      /* t3 = (t1**b - a)/(b * t1**(b-1)) */
-      if ((res = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      if ((res = mp_sub(&t1, &t3, &t2)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-   }  while (mp_cmp(&t1, &t2) != MP_EQ);
-
-   /* result can be off by a few so check */
-   for (;;) {
-      if ((res = mp_expt_d_ex(&t1, b, &t2, fast)) != MP_OKAY) {
-         goto LBL_T3;
-      }
-
-      if (mp_cmp(&t2, &a_) == MP_GT) {
-         if ((res = mp_sub_d(&t1, 1uL, &t1)) != MP_OKAY) {
-            goto LBL_T3;
-         }
-      } else {
-         break;
-      }
-   }
-
-   /* set the result */
-   mp_exch(&t1, c);
-
-   /* set the sign of the result */
-   c->sign = a->sign;
-
-   res = MP_OKAY;
-
-LBL_T3:
-   mp_clear(&t3);
-LBL_T2:
-   mp_clear(&t2);
-LBL_T1:
-   mp_clear(&t1);
-   return res;
-}
-
-/* End: bn_mp_n_root_ex.c */
-
-/* Start: bn_mp_neg.c */
-
-/* b = -a */
-int mp_neg(const mp_int *a, mp_int *b)
-{
-   int     res;
-   if (a != b) {
-      if ((res = mp_copy(a, b)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   if (mp_iszero(b) != MP_YES) {
-      b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS;
-   } else {
-      b->sign = MP_ZPOS;
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_neg.c */
-
-/* Start: bn_mp_or.c */
-
-/* OR two ints together */
-int mp_or(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     res, ix, px;
-   mp_int  t;
-   const mp_int *x;
-
-   if (a->used > b->used) {
-      if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-         return res;
-      }
-      px = b->used;
-      x = b;
-   } else {
-      if ((res = mp_init_copy(&t, b)) != MP_OKAY) {
-         return res;
-      }
-      px = a->used;
-      x = a;
-   }
-
-   for (ix = 0; ix < px; ix++) {
-      t.dp[ix] |= x->dp[ix];
-   }
-   mp_clamp(&t);
-   mp_exch(c, &t);
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_or.c */
-
-/* Start: bn_mp_prime_fermat.c */
-
-/* performs one Fermat test.
- *
- * If "a" were prime then b**a == b (mod a) since the order of
- * the multiplicative sub-group would be phi(a) = a-1.  That means
- * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a).
- *
- * Sets result to 1 if the congruence holds, or zero otherwise.
- */
-int mp_prime_fermat(const mp_int *a, const mp_int *b, int *result)
-{
-   mp_int  t;
-   int     err;
-
-   /* default to composite  */
-   *result = MP_NO;
-
-   /* ensure b > 1 */
-   if (mp_cmp_d(b, 1uL) != MP_GT) {
-      return MP_VAL;
-   }
-
-   /* init t */
-   if ((err = mp_init(&t)) != MP_OKAY) {
-      return err;
-   }
-
-   /* compute t = b**a mod a */
-   if ((err = mp_exptmod(b, a, a, &t)) != MP_OKAY) {
-      goto LBL_T;
-   }
-
-   /* is it equal to b? */
-   if (mp_cmp(&t, b) == MP_EQ) {
-      *result = MP_YES;
-   }
-
-   err = MP_OKAY;
-LBL_T:
-   mp_clear(&t);
-   return err;
-}
-
-/* End: bn_mp_prime_fermat.c */
-
-/* Start: bn_mp_prime_frobenius_underwood.c */
-
-/*
- *  See file bn_mp_prime_is_prime.c or the documentation in doc/bn.tex for the details
- */
-#ifndef LTM_USE_FIPS_ONLY
-
-#ifdef MP_8BIT
-/*
- * floor of positive solution of
- * (2^16)-1 = (a+4)*(2*a+5)
- * TODO: Both values are smaller than N^(1/4), would have to use a bigint
- *       for a instead but any a biger than about 120 are already so rare that
- *       it is possible to ignore them and still get enough pseudoprimes.
- *       But it is still a restriction of the set of available pseudoprimes
- *       which makes this implementation less secure if used stand-alone.
- */
-#define LTM_FROBENIUS_UNDERWOOD_A 177
-#else
-#define LTM_FROBENIUS_UNDERWOOD_A 32764
-#endif
-int mp_prime_frobenius_underwood(const mp_int *N, int *result)
-{
-   mp_int T1z, T2z, Np1z, sz, tz;
-
-   int a, ap2, length, i, j, isset;
-   int e;
-
-   *result = MP_NO;
-
-   if ((e = mp_init_multi(&T1z, &T2z, &Np1z, &sz, &tz, NULL)) != MP_OKAY) {
-      return e;
-   }
-
-   for (a = 0; a < LTM_FROBENIUS_UNDERWOOD_A; a++) {
-      /* TODO: That's ugly! No, really, it is! */
-      if ((a==2) || (a==4) || (a==7) || (a==8) || (a==10) ||
-          (a==14) || (a==18) || (a==23) || (a==26) || (a==28)) {
-         continue;
-      }
-      /* (32764^2 - 4) < 2^31, no bigint for >MP_8BIT needed) */
-      if ((e = mp_set_long(&T1z, (unsigned long)a)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-
-      if ((e = mp_sqr(&T1z, &T1z)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-
-      if ((e = mp_sub_d(&T1z, 4uL, &T1z)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-
-      if ((e = mp_kronecker(&T1z, N, &j)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-
-      if (j == -1) {
-         break;
-      }
-
-      if (j == 0) {
-         /* composite */
-         goto LBL_FU_ERR;
-      }
-   }
-   /* Tell it a composite and set return value accordingly */
-   if (a >= LTM_FROBENIUS_UNDERWOOD_A) {
-      e = MP_ITER;
-      goto LBL_FU_ERR;
-   }
-   /* Composite if N and (a+4)*(2*a+5) are not coprime */
-   if ((e = mp_set_long(&T1z, (unsigned long)((a+4)*((2*a)+5)))) != MP_OKAY) {
-      goto LBL_FU_ERR;
-   }
-
-   if ((e = mp_gcd(N, &T1z, &T1z)) != MP_OKAY) {
-      goto LBL_FU_ERR;
-   }
-
-   if (!((T1z.used == 1) && (T1z.dp[0] == 1u))) {
-      goto LBL_FU_ERR;
-   }
-
-   ap2 = a + 2;
-   if ((e = mp_add_d(N, 1uL, &Np1z)) != MP_OKAY) {
-      goto LBL_FU_ERR;
-   }
-
-   mp_set(&sz, 1uL);
-   mp_set(&tz, 2uL);
-   length = mp_count_bits(&Np1z);
-
-   for (i = length - 2; i >= 0; i--) {
-      /*
-       * temp = (sz*(a*sz+2*tz))%N;
-       * tz   = ((tz-sz)*(tz+sz))%N;
-       * sz   = temp;
-       */
-      if ((e = mp_mul_2(&tz, &T2z)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-
-      /* a = 0 at about 50% of the cases (non-square and odd input) */
-      if (a != 0) {
-         if ((e = mp_mul_d(&sz, (mp_digit)a, &T1z)) != MP_OKAY) {
-            goto LBL_FU_ERR;
-         }
-         if ((e = mp_add(&T1z, &T2z, &T2z)) != MP_OKAY) {
-            goto LBL_FU_ERR;
-         }
-      }
-
-      if ((e = mp_mul(&T2z, &sz, &T1z)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-      if ((e = mp_sub(&tz, &sz, &T2z)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-      if ((e = mp_add(&sz, &tz, &sz)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-      if ((e = mp_mul(&sz, &T2z, &tz)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-      if ((e = mp_mod(&tz, N, &tz)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-      if ((e = mp_mod(&T1z, N, &sz)) != MP_OKAY) {
-         goto LBL_FU_ERR;
-      }
-      if ((isset = mp_get_bit(&Np1z, i)) == MP_VAL) {
-         e = isset;
-         goto LBL_FU_ERR;
-      }
-      if (isset == MP_YES) {
-         /*
-          *  temp = (a+2) * sz + tz
-          *  tz   = 2 * tz - sz
-          *  sz   = temp
-          */
-         if (a == 0) {
-            if ((e = mp_mul_2(&sz, &T1z)) != MP_OKAY) {
-               goto LBL_FU_ERR;
-            }
-         } else {
-            if ((e = mp_mul_d(&sz, (mp_digit)ap2, &T1z)) != MP_OKAY) {
-               goto LBL_FU_ERR;
-            }
-         }
-         if ((e = mp_add(&T1z, &tz, &T1z)) != MP_OKAY) {
-            goto LBL_FU_ERR;
-         }
-         if ((e = mp_mul_2(&tz, &T2z)) != MP_OKAY) {
-            goto LBL_FU_ERR;
-         }
-         if ((e = mp_sub(&T2z, &sz, &tz)) != MP_OKAY) {
-            goto LBL_FU_ERR;
-         }
-         mp_exch(&sz, &T1z);
-      }
-   }
-
-   if ((e = mp_set_long(&T1z, (unsigned long)((2 * a) + 5))) != MP_OKAY) {
-      goto LBL_FU_ERR;
-   }
-   if ((e = mp_mod(&T1z, N, &T1z)) != MP_OKAY) {
-      goto LBL_FU_ERR;
-   }
-   if ((mp_iszero(&sz) != MP_NO) && (mp_cmp(&tz, &T1z) == MP_EQ)) {
-      *result = MP_YES;
-      goto LBL_FU_ERR;
-   }
-
-LBL_FU_ERR:
-   mp_clear_multi(&tz, &sz, &Np1z, &T2z, &T1z, NULL);
-   return e;
-}
-
-#endif
-
-/* End: bn_mp_prime_frobenius_underwood.c */
-
-/* Start: bn_mp_prime_is_divisible.c */
-
-/* determines if an integers is divisible by one
- * of the first PRIME_SIZE primes or not
- *
- * sets result to 0 if not, 1 if yes
- */
-int mp_prime_is_divisible(const mp_int *a, int *result)
-{
-   int     err, ix;
-   mp_digit res;
-
-   /* default to not */
-   *result = MP_NO;
-
-   for (ix = 0; ix < PRIME_SIZE; ix++) {
-      /* what is a mod LBL_prime_tab[ix] */
-      if ((err = mp_mod_d(a, ltm_prime_tab[ix], &res)) != MP_OKAY) {
-         return err;
-      }
-
-      /* is the residue zero? */
-      if (res == 0u) {
-         *result = MP_YES;
-         return MP_OKAY;
-      }
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_prime_is_divisible.c */
-
-/* Start: bn_mp_prime_is_prime.c */
-
-/* portable integer log of two with small footprint */
-static unsigned int s_floor_ilog2(int value)
-{
-   unsigned int r = 0;
-   while ((value >>= 1) != 0) {
-      r++;
-   }
-   return r;
-}
-
-
-int mp_prime_is_prime(const mp_int *a, int t, int *result)
-{
-   mp_int  b;
-   int     ix, err, res, p_max = 0, size_a, len;
-   unsigned int fips_rand, mask;
-
-   /* default to no */
-   *result = MP_NO;
-
-   /* valid value of t? */
-   if (t > PRIME_SIZE) {
-      return MP_VAL;
-   }
-
-   /* Some shortcuts */
-   /* N > 3 */
-   if (a->used == 1) {
-      if ((a->dp[0] == 0u) || (a->dp[0] == 1u)) {
-         *result = 0;
-         return MP_OKAY;
-      }
-      if (a->dp[0] == 2u) {
-         *result = 1;
-         return MP_OKAY;
-      }
-   }
-
-   /* N must be odd */
-   if (mp_iseven(a) == MP_YES) {
-      return MP_OKAY;
-   }
-   /* N is not a perfect square: floor(sqrt(N))^2 != N */
-   if ((err = mp_is_square(a, &res)) != MP_OKAY) {
-      return err;
-   }
-   if (res != 0) {
-      return MP_OKAY;
-   }
-
-   /* is the input equal to one of the primes in the table? */
-   for (ix = 0; ix < PRIME_SIZE; ix++) {
-      if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) {
-         *result = MP_YES;
-         return MP_OKAY;
-      }
-   }
-#ifdef MP_8BIT
-   /* The search in the loop above was exhaustive in this case */
-   if ((a->used == 1) && (PRIME_SIZE >= 31)) {
-      return MP_OKAY;
-   }
-#endif
-
-   /* first perform trial division */
-   if ((err = mp_prime_is_divisible(a, &res)) != MP_OKAY) {
-      return err;
-   }
-
-   /* return if it was trivially divisible */
-   if (res == MP_YES) {
-      return MP_OKAY;
-   }
-
-   /*
-       Run the Miller-Rabin test with base 2 for the BPSW test.
-    */
-   if ((err = mp_init_set(&b, 2uL)) != MP_OKAY) {
-      return err;
-   }
-
-   if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
-      goto LBL_B;
-   }
-   if (res == MP_NO) {
-      goto LBL_B;
-   }
-   /*
-      Rumours have it that Mathematica does a second M-R test with base 3.
-      Other rumours have it that their strong L-S test is slightly different.
-      It does not hurt, though, beside a bit of extra runtime.
-   */
-   b.dp[0]++;
-   if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
-      goto LBL_B;
-   }
-   if (res == MP_NO) {
-      goto LBL_B;
-   }
-
-   /*
-    * Both, the Frobenius-Underwood test and the the Lucas-Selfridge test are quite
-    * slow so if speed is an issue, define LTM_USE_FIPS_ONLY to use M-R tests with
-    * bases 2, 3 and t random bases.
-    */
-#ifndef LTM_USE_FIPS_ONLY
-   if (t >= 0) {
-      /*
-       * Use a Frobenius-Underwood test instead of the Lucas-Selfridge test for
-       * MP_8BIT (It is unknown if the Lucas-Selfridge test works with 16-bit
-       * integers but the necesssary analysis is on the todo-list).
-       */
-#if defined (MP_8BIT) || defined (LTM_USE_FROBENIUS_TEST)
-      err = mp_prime_frobenius_underwood(a, &res);
-      if ((err != MP_OKAY) && (err != MP_ITER)) {
-         goto LBL_B;
-      }
-      if (res == MP_NO) {
-         goto LBL_B;
-      }
-#else
-      if ((err = mp_prime_strong_lucas_selfridge(a, &res)) != MP_OKAY) {
-         goto LBL_B;
-      }
-      if (res == MP_NO) {
-         goto LBL_B;
-      }
-#endif
-   }
-#endif
-
-   /* run at least one Miller-Rabin test with a random base */
-   if (t == 0) {
-      t = 1;
-   }
-
-   /*
-      abs(t) extra rounds of M-R to extend the range of primes it can find if t < 0.
-      Only recommended if the input range is known to be < 3317044064679887385961981
-
-      It uses the bases for a deterministic M-R test if input < 3317044064679887385961981
-      The caller has to check the size.
-
-      Not for cryptographic use because with known bases strong M-R pseudoprimes can
-      be constructed. Use at least one M-R test with a random base (t >= 1).
-
-      The 1119 bit large number
-
-      80383745745363949125707961434194210813883768828755814583748891752229742737653\
-      33652186502336163960045457915042023603208766569966760987284043965408232928738\
-      79185086916685732826776177102938969773947016708230428687109997439976544144845\
-      34115587245063340927902227529622941498423068816854043264575340183297861112989\
-      60644845216191652872597534901
-
-      has been constructed by F. Arnault (F. Arnault, "Rabin-Miller primality test:
-      composite numbers which pass it.",  Mathematics of Computation, 1995, 64. Jg.,
-      Nr. 209, S. 355-361), is a semiprime with the two factors
-
-      40095821663949960541830645208454685300518816604113250877450620473800321707011\
-      96242716223191597219733582163165085358166969145233813917169287527980445796800\
-      452592031836601
-
-      20047910831974980270915322604227342650259408302056625438725310236900160853505\
-      98121358111595798609866791081582542679083484572616906958584643763990222898400\
-      226296015918301
-
-      and it is a strong pseudoprime to all forty-six prime M-R bases up to 200
-
-      It does not fail the strong Bailley-PSP test as implemented here, it is just
-      given as an example, if not the reason to use the BPSW-test instead of M-R-tests
-      with a sequence of primes 2...n.
-
-   */
-   if (t < 0) {
-      t = -t;
-      /*
-          Sorenson, Jonathan; Webster, Jonathan (2015).
-           "Strong Pseudoprimes to Twelve Prime Bases".
-       */
-      /* 0x437ae92817f9fc85b7e5 = 318665857834031151167461 */
-      if ((err =   mp_read_radix(&b, "437ae92817f9fc85b7e5", 16)) != MP_OKAY) {
-         goto LBL_B;
-      }
-
-      if (mp_cmp(a, &b) == MP_LT) {
-         p_max = 12;
-      } else {
-         /* 0x2be6951adc5b22410a5fd = 3317044064679887385961981 */
-         if ((err = mp_read_radix(&b, "2be6951adc5b22410a5fd", 16)) != MP_OKAY) {
-            goto LBL_B;
-         }
-
-         if (mp_cmp(a, &b) == MP_LT) {
-            p_max = 13;
-         } else {
-            err = MP_VAL;
-            goto LBL_B;
-         }
-      }
-
-      /* for compatibility with the current API (well, compatible within a sign's width) */
-      if (p_max < t) {
-         p_max = t;
-      }
-
-      if (p_max > PRIME_SIZE) {
-         err = MP_VAL;
-         goto LBL_B;
-      }
-      /* we did bases 2 and 3  already, skip them */
-      for (ix = 2; ix < p_max; ix++) {
-         mp_set(&b, ltm_prime_tab[ix]);
-         if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
-            goto LBL_B;
-         }
-         if (res == MP_NO) {
-            goto LBL_B;
-         }
-      }
-   }
-   /*
-       Do "t" M-R tests with random bases between 3 and "a".
-       See Fips 186.4 p. 126ff
-   */
-   else if (t > 0) {
-      /*
-       * The mp_digit's have a defined bit-size but the size of the
-       * array a.dp is a simple 'int' and this library can not assume full
-       * compliance to the current C-standard (ISO/IEC 9899:2011) because
-       * it gets used for small embeded processors, too. Some of those MCUs
-       * have compilers that one cannot call standard compliant by any means.
-       * Hence the ugly type-fiddling in the following code.
-       */
-      size_a = mp_count_bits(a);
-      mask = (1u << s_floor_ilog2(size_a)) - 1u;
-      /*
-         Assuming the General Rieman hypothesis (never thought to write that in a
-         comment) the upper bound can be lowered to  2*(log a)^2.
-         E. Bach, "Explicit bounds for primality testing and related problems,"
-         Math. Comp. 55 (1990), 355-380.
-
-            size_a = (size_a/10) * 7;
-            len = 2 * (size_a * size_a);
-
-         E.g.: a number of size 2^2048 would be reduced to the upper limit
-
-            floor(2048/10)*7 = 1428
-            2 * 1428^2       = 4078368
-
-         (would have been ~4030331.9962 with floats and natural log instead)
-         That number is smaller than 2^28, the default bit-size of mp_digit.
-      */
-
-      /*
-        How many tests, you might ask? Dana Jacobsen of Math::Prime::Util fame
-        does exactly 1. In words: one. Look at the end of _GMP_is_prime() in
-        Math-Prime-Util-GMP-0.50/primality.c if you do not believe it.
-
-        The function mp_rand() goes to some length to use a cryptographically
-        good PRNG. That also means that the chance to always get the same base
-        in the loop is non-zero, although very low.
-        If the BPSW test and/or the addtional Frobenious test have been
-        performed instead of just the Miller-Rabin test with the bases 2 and 3,
-        a single extra test should suffice, so such a very unlikely event
-        will not do much harm.
-
-        To preemptivly answer the dangling question: no, a witness does not
-        need to be prime.
-      */
-      for (ix = 0; ix < t; ix++) {
-         /* mp_rand() guarantees the first digit to be non-zero */
-         if ((err = mp_rand(&b, 1)) != MP_OKAY) {
-            goto LBL_B;
-         }
-         /*
-          * Reduce digit before casting because mp_digit might be bigger than
-          * an unsigned int and "mask" on the other side is most probably not.
-          */
-         fips_rand = (unsigned int)(b.dp[0] & (mp_digit) mask);
-#ifdef MP_8BIT
-         /*
-          * One 8-bit digit is too small, so concatenate two if the size of
-          * unsigned int allows for it.
-          */
-         if (((sizeof(unsigned int) * CHAR_BIT)/2) >= (sizeof(mp_digit) * CHAR_BIT)) {
-            if ((err = mp_rand(&b, 1)) != MP_OKAY) {
-               goto LBL_B;
-            }
-            fips_rand <<= sizeof(mp_digit) * CHAR_BIT;
-            fips_rand |= (unsigned int) b.dp[0];
-            fips_rand &= mask;
-         }
-#endif
-         if (fips_rand > (unsigned int)(INT_MAX - DIGIT_BIT)) {
-            len = INT_MAX / DIGIT_BIT;
-         } else {
-            len = (((int)fips_rand + DIGIT_BIT) / DIGIT_BIT);
-         }
-         /*  Unlikely. */
-         if (len < 0) {
-            ix--;
-            continue;
-         }
-         /*
-          * As mentioned above, one 8-bit digit is too small and
-          * although it can only happen in the unlikely case that
-          * an "unsigned int" is smaller than 16 bit a simple test
-          * is cheap and the correction even cheaper.
-          */
-#ifdef MP_8BIT
-         /* All "a" < 2^8 have been caught before */
-         if (len == 1) {
-            len++;
-         }
-#endif
-         if ((err = mp_rand(&b, len)) != MP_OKAY) {
-            goto LBL_B;
-         }
-         /*
-          * That number might got too big and the witness has to be
-          * smaller than or equal to "a"
-          */
-         len = mp_count_bits(&b);
-         if (len > size_a) {
-            len = len - size_a;
-            if ((err = mp_div_2d(&b, len, &b, NULL)) != MP_OKAY) {
-               goto LBL_B;
-            }
-         }
-
-         /* Although the chance for b <= 3 is miniscule, try again. */
-         if (mp_cmp_d(&b, 3uL) != MP_GT) {
-            ix--;
-            continue;
-         }
-         if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
-            goto LBL_B;
-         }
-         if (res == MP_NO) {
-            goto LBL_B;
-         }
-      }
-   }
-
-   /* passed the test */
-   *result = MP_YES;
-LBL_B:
-   mp_clear(&b);
-   return err;
-}
-
-/* End: bn_mp_prime_is_prime.c */
-
-/* Start: bn_mp_prime_miller_rabin.c */
-
-/* Miller-Rabin test of "a" to the base of "b" as described in
- * HAC pp. 139 Algorithm 4.24
- *
- * Sets result to 0 if definitely composite or 1 if probably prime.
- * Randomly the chance of error is no more than 1/4 and often
- * very much lower.
- */
-int mp_prime_miller_rabin(const mp_int *a, const mp_int *b, int *result)
-{
-   mp_int  n1, y, r;
-   int     s, j, err;
-
-   /* default */
-   *result = MP_NO;
-
-   /* ensure b > 1 */
-   if (mp_cmp_d(b, 1uL) != MP_GT) {
-      return MP_VAL;
-   }
-
-   /* get n1 = a - 1 */
-   if ((err = mp_init_copy(&n1, a)) != MP_OKAY) {
-      return err;
-   }
-   if ((err = mp_sub_d(&n1, 1uL, &n1)) != MP_OKAY) {
-      goto LBL_N1;
-   }
-
-   /* set 2**s * r = n1 */
-   if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) {
-      goto LBL_N1;
-   }
-
-   /* count the number of least significant bits
-    * which are zero
-    */
-   s = mp_cnt_lsb(&r);
-
-   /* now divide n - 1 by 2**s */
-   if ((err = mp_div_2d(&r, s, &r, NULL)) != MP_OKAY) {
-      goto LBL_R;
-   }
-
-   /* compute y = b**r mod a */
-   if ((err = mp_init(&y)) != MP_OKAY) {
-      goto LBL_R;
-   }
-   if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) {
-      goto LBL_Y;
-   }
-
-   /* if y != 1 and y != n1 do */
-   if ((mp_cmp_d(&y, 1uL) != MP_EQ) && (mp_cmp(&y, &n1) != MP_EQ)) {
-      j = 1;
-      /* while j <= s-1 and y != n1 */
-      while ((j <= (s - 1)) && (mp_cmp(&y, &n1) != MP_EQ)) {
-         if ((err = mp_sqrmod(&y, a, &y)) != MP_OKAY) {
-            goto LBL_Y;
-         }
-
-         /* if y == 1 then composite */
-         if (mp_cmp_d(&y, 1uL) == MP_EQ) {
-            goto LBL_Y;
-         }
-
-         ++j;
-      }
-
-      /* if y != n1 then composite */
-      if (mp_cmp(&y, &n1) != MP_EQ) {
-         goto LBL_Y;
-      }
-   }
-
-   /* probably prime now */
-   *result = MP_YES;
-LBL_Y:
-   mp_clear(&y);
-LBL_R:
-   mp_clear(&r);
-LBL_N1:
-   mp_clear(&n1);
-   return err;
-}
-
-/* End: bn_mp_prime_miller_rabin.c */
-
-/* Start: bn_mp_prime_next_prime.c */
-
-/* finds the next prime after the number "a" using "t" trials
- * of Miller-Rabin.
- *
- * bbs_style = 1 means the prime must be congruent to 3 mod 4
- */
-int mp_prime_next_prime(mp_int *a, int t, int bbs_style)
-{
-   int      err, res = MP_NO, x, y;
-   mp_digit res_tab[PRIME_SIZE], step, kstep;
-   mp_int   b;
-
-   /* force positive */
-   a->sign = MP_ZPOS;
-
-   /* simple algo if a is less than the largest prime in the table */
-   if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE-1]) == MP_LT) {
-      /* find which prime it is bigger than */
-      for (x = PRIME_SIZE - 2; x >= 0; x--) {
-         if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) {
-            if (bbs_style == 1) {
-               /* ok we found a prime smaller or
-                * equal [so the next is larger]
-                *
-                * however, the prime must be
-                * congruent to 3 mod 4
-                */
-               if ((ltm_prime_tab[x + 1] & 3u) != 3u) {
-                  /* scan upwards for a prime congruent to 3 mod 4 */
-                  for (y = x + 1; y < PRIME_SIZE; y++) {
-                     if ((ltm_prime_tab[y] & 3u) == 3u) {
-                        mp_set(a, ltm_prime_tab[y]);
-                        return MP_OKAY;
-                     }
-                  }
-               }
-            } else {
-               mp_set(a, ltm_prime_tab[x + 1]);
-               return MP_OKAY;
-            }
-         }
-      }
-      /* at this point a maybe 1 */
-      if (mp_cmp_d(a, 1uL) == MP_EQ) {
-         mp_set(a, 2uL);
-         return MP_OKAY;
-      }
-      /* fall through to the sieve */
-   }
-
-   /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */
-   if (bbs_style == 1) {
-      kstep   = 4;
-   } else {
-      kstep   = 2;
-   }
-
-   /* at this point we will use a combination of a sieve and Miller-Rabin */
-
-   if (bbs_style == 1) {
-      /* if a mod 4 != 3 subtract the correct value to make it so */
-      if ((a->dp[0] & 3u) != 3u) {
-         if ((err = mp_sub_d(a, (a->dp[0] & 3u) + 1u, a)) != MP_OKAY) {
-            return err;
-         };
-      }
-   } else {
-      if (mp_iseven(a) == MP_YES) {
-         /* force odd */
-         if ((err = mp_sub_d(a, 1uL, a)) != MP_OKAY) {
-            return err;
-         }
-      }
-   }
-
-   /* generate the restable */
-   for (x = 1; x < PRIME_SIZE; x++) {
-      if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) {
-         return err;
-      }
-   }
-
-   /* init temp used for Miller-Rabin Testing */
-   if ((err = mp_init(&b)) != MP_OKAY) {
-      return err;
-   }
-
-   for (;;) {
-      /* skip to the next non-trivially divisible candidate */
-      step = 0;
-      do {
-         /* y == 1 if any residue was zero [e.g. cannot be prime] */
-         y     =  0;
-
-         /* increase step to next candidate */
-         step += kstep;
-
-         /* compute the new residue without using division */
-         for (x = 1; x < PRIME_SIZE; x++) {
-            /* add the step to each residue */
-            res_tab[x] += kstep;
-
-            /* subtract the modulus [instead of using division] */
-            if (res_tab[x] >= ltm_prime_tab[x]) {
-               res_tab[x]  -= ltm_prime_tab[x];
-            }
-
-            /* set flag if zero */
-            if (res_tab[x] == 0u) {
-               y = 1;
-            }
-         }
-      } while ((y == 1) && (step < (((mp_digit)1 << DIGIT_BIT) - kstep)));
-
-      /* add the step */
-      if ((err = mp_add_d(a, step, a)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-
-      /* if didn't pass sieve and step == MAX then skip test */
-      if ((y == 1) && (step >= (((mp_digit)1 << DIGIT_BIT) - kstep))) {
-         continue;
-      }
-
-      if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      if (res == MP_YES) {
-         break;
-      }
-   }
-
-   err = MP_OKAY;
-LBL_ERR:
-   mp_clear(&b);
-   return err;
-}
-
-/* End: bn_mp_prime_next_prime.c */
-
-/* Start: bn_mp_prime_rabin_miller_trials.c */
-
-static const struct {
-   int k, t;
-} sizes[] = {
-   {    80,    -1 }, /* Use deterministic algorithm for size <= 80 bits */
-   {    81,    39 },
-   {    96,    37 },
-   {   128,    32 },
-   {   160,    27 },
-   {   192,    21 },
-   {   256,    16 },
-   {   384,    10 },
-   {   512,     7 },
-   {   640,     6 },
-   {   768,     5 },
-   {   896,     4 },
-   {  1024,     4 },
-   {  2048,     2 },
-   {  4096,     1 },
-};
-
-/* returns # of RM trials required for a given bit size and max. error of 2^(-96)*/
-int mp_prime_rabin_miller_trials(int size)
-{
-   int x;
-
-   for (x = 0; x < (int)(sizeof(sizes)/(sizeof(sizes[0]))); x++) {
-      if (sizes[x].k == size) {
-         return sizes[x].t;
-      } else if (sizes[x].k > size) {
-         return (x == 0) ? sizes[0].t : sizes[x - 1].t;
-      }
-   }
-   return sizes[x-1].t + 1;
-}
-
-/* End: bn_mp_prime_rabin_miller_trials.c */
-
-/* Start: bn_mp_prime_random_ex.c */
-
-/* makes a truly random prime of a given size (bits),
- *
- * Flags are as follows:
- *
- *   LTM_PRIME_BBS      - make prime congruent to 3 mod 4
- *   LTM_PRIME_SAFE     - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS)
- *   LTM_PRIME_2MSB_ON  - make the 2nd highest bit one
- *
- * You have to supply a callback which fills in a buffer with random bytes.  "dat" is a parameter you can
- * have passed to the callback (e.g. a state or something).  This function doesn't use "dat" itself
- * so it can be NULL
- *
- */
-
-/* This is possibly the mother of all prime generation functions, muahahahahaha! */
-int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat)
-{
-   unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb;
-   int res, err, bsize, maskOR_msb_offset;
-
-   /* sanity check the input */
-   if ((size <= 1) || (t <= 0)) {
-      return MP_VAL;
-   }
-
-   /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */
-   if ((flags & LTM_PRIME_SAFE) != 0) {
-      flags |= LTM_PRIME_BBS;
-   }
-
-   /* calc the byte size */
-   bsize = (size>>3) + ((size&7)?1:0);
-
-   /* we need a buffer of bsize bytes */
-   tmp = OPT_CAST(unsigned char) XMALLOC((size_t)bsize);
-   if (tmp == NULL) {
-      return MP_MEM;
-   }
-
-   /* calc the maskAND value for the MSbyte*/
-   maskAND = ((size&7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7)));
-
-   /* calc the maskOR_msb */
-   maskOR_msb        = 0;
-   maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0;
-   if ((flags & LTM_PRIME_2MSB_ON) != 0) {
-      maskOR_msb       |= 0x80 >> ((9 - size) & 7);
-   }
-
-   /* get the maskOR_lsb */
-   maskOR_lsb         = 1;
-   if ((flags & LTM_PRIME_BBS) != 0) {
-      maskOR_lsb     |= 3;
-   }
-
-   do {
-      /* read the bytes */
-      if (cb(tmp, bsize, dat) != bsize) {
-         err = MP_VAL;
-         goto error;
-      }
-
-      /* work over the MSbyte */
-      tmp[0]    &= maskAND;
-      tmp[0]    |= 1 << ((size - 1) & 7);
-
-      /* mix in the maskORs */
-      tmp[maskOR_msb_offset]   |= maskOR_msb;
-      tmp[bsize-1]             |= maskOR_lsb;
-
-      /* read it in */
-      if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) {
-         goto error;
-      }
-
-      /* is it prime? */
-      if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
-         goto error;
-      }
-      if (res == MP_NO) {
-         continue;
-      }
-
-      if ((flags & LTM_PRIME_SAFE) != 0) {
-         /* see if (a-1)/2 is prime */
-         if ((err = mp_sub_d(a, 1uL, a)) != MP_OKAY) {
-            goto error;
-         }
-         if ((err = mp_div_2(a, a)) != MP_OKAY) {
-            goto error;
-         }
-
-         /* is it prime? */
-         if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
-            goto error;
-         }
-      }
-   } while (res == MP_NO);
-
-   if ((flags & LTM_PRIME_SAFE) != 0) {
-      /* restore a to the original value */
-      if ((err = mp_mul_2(a, a)) != MP_OKAY) {
-         goto error;
-      }
-      if ((err = mp_add_d(a, 1uL, a)) != MP_OKAY) {
-         goto error;
-      }
-   }
-
-   err = MP_OKAY;
-error:
-   XFREE(tmp);
-   return err;
-}
-
-/* End: bn_mp_prime_random_ex.c */
-
-/* Start: bn_mp_prime_strong_lucas_selfridge.c */
-
-/*
- *  See file bn_mp_prime_is_prime.c or the documentation in doc/bn.tex for the details
- */
-#ifndef LTM_USE_FIPS_ONLY
-
-/*
- *  8-bit is just too small. You can try the Frobenius test
- *  but that frobenius test can fail, too, for the same reason.
- */
-#ifndef MP_8BIT
-
-/*
- * multiply bigint a with int d and put the result in c
- * Like mp_mul_d() but with a signed long as the small input
- */
-static int s_mp_mul_si(const mp_int *a, long d, mp_int *c)
-{
-   mp_int t;
-   int err, neg = 0;
-
-   if ((err = mp_init(&t)) != MP_OKAY) {
-      return err;
-   }
-   if (d < 0) {
-      neg = 1;
-      d = -d;
-   }
-
-   /*
-    * mp_digit might be smaller than a long, which excludes
-    * the use of mp_mul_d() here.
-    */
-   if ((err = mp_set_long(&t, (unsigned long) d)) != MP_OKAY) {
-      goto LBL_MPMULSI_ERR;
-   }
-   if ((err = mp_mul(a, &t, c)) != MP_OKAY) {
-      goto LBL_MPMULSI_ERR;
-   }
-   if (neg ==  1) {
-      c->sign = (a->sign == MP_NEG) ? MP_ZPOS: MP_NEG;
-   }
-LBL_MPMULSI_ERR:
-   mp_clear(&t);
-   return err;
-}
-/*
-    Strong Lucas-Selfridge test.
-    returns MP_YES if it is a strong L-S prime, MP_NO if it is composite
-
-    Code ported from  Thomas Ray Nicely's implementation of the BPSW test
-    at http://www.trnicely.net/misc/bpsw.html
-
-    Freeware copyright (C) 2016 Thomas R. Nicely <http://www.trnicely.net>.
-    Released into the public domain by the author, who disclaims any legal
-    liability arising from its use
-
-    The multi-line comments are made by Thomas R. Nicely and are copied verbatim.
-    Additional comments marked "CZ" (without the quotes) are by the code-portist.
-
-    (If that name sounds familiar, he is the guy who found the fdiv bug in the
-     Pentium (P5x, I think) Intel processor)
-*/
-int mp_prime_strong_lucas_selfridge(const mp_int *a, int *result)
-{
-   /* CZ TODO: choose better variable names! */
-   mp_int Dz, gcd, Np1, Uz, Vz, U2mz, V2mz, Qmz, Q2mz, Qkdz, T1z, T2z, T3z, T4z, Q2kdz;
-   /* CZ TODO: Some of them need the full 32 bit, hence the (temporary) exclusion of MP_8BIT */
-   int32_t D, Ds, J, sign, P, Q, r, s, u, Nbits;
-   int e;
-   int isset, oddness;
-
-   *result = MP_NO;
-   /*
-   Find the first element D in the sequence {5, -7, 9, -11, 13, ...}
-   such that Jacobi(D,N) = -1 (Selfridge's algorithm). Theory
-   indicates that, if N is not a perfect square, D will "nearly
-   always" be "small." Just in case, an overflow trap for D is
-   included.
-   */
-
-   if ((e = mp_init_multi(&Dz, &gcd, &Np1, &Uz, &Vz, &U2mz, &V2mz, &Qmz, &Q2mz, &Qkdz, &T1z, &T2z, &T3z, &T4z, &Q2kdz,
-                          NULL)) != MP_OKAY) {
-      return e;
-   }
-
-   D = 5;
-   sign = 1;
-
-   for (;;) {
-      Ds   = sign * D;
-      sign = -sign;
-      if ((e = mp_set_long(&Dz, (unsigned long)D)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_gcd(a, &Dz, &gcd)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      /* if 1 < GCD < N then N is composite with factor "D", and
-         Jacobi(D,N) is technically undefined (but often returned
-         as zero). */
-      if ((mp_cmp_d(&gcd, 1uL) == MP_GT) && (mp_cmp(&gcd, a) == MP_LT)) {
-         goto LBL_LS_ERR;
-      }
-      if (Ds < 0) {
-         Dz.sign = MP_NEG;
-      }
-      if ((e = mp_kronecker(&Dz, a, &J)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-
-      if (J == -1) {
-         break;
-      }
-      D += 2;
-
-      if (D > (INT_MAX - 2)) {
-         e = MP_VAL;
-         goto LBL_LS_ERR;
-      }
-   }
-
-
-
-   P = 1;              /* Selfridge's choice */
-   Q = (1 - Ds) / 4;   /* Required so D = P*P - 4*Q */
-
-   /* NOTE: The conditions (a) N does not divide Q, and
-      (b) D is square-free or not a perfect square, are included by
-      some authors; e.g., "Prime numbers and computer methods for
-      factorization," Hans Riesel (2nd ed., 1994, Birkhauser, Boston),
-      p. 130. For this particular application of Lucas sequences,
-      these conditions were found to be immaterial. */
-
-   /* Now calculate N - Jacobi(D,N) = N + 1 (even), and calculate the
-      odd positive integer d and positive integer s for which
-      N + 1 = 2^s*d (similar to the step for N - 1 in Miller's test).
-      The strong Lucas-Selfridge test then returns N as a strong
-      Lucas probable prime (slprp) if any of the following
-      conditions is met: U_d=0, V_d=0, V_2d=0, V_4d=0, V_8d=0,
-      V_16d=0, ..., etc., ending with V_{2^(s-1)*d}=V_{(N+1)/2}=0
-      (all equalities mod N). Thus d is the highest index of U that
-      must be computed (since V_2m is independent of U), compared
-      to U_{N+1} for the standard Lucas-Selfridge test; and no
-      index of V beyond (N+1)/2 is required, just as in the
-      standard Lucas-Selfridge test. However, the quantity Q^d must
-      be computed for use (if necessary) in the latter stages of
-      the test. The result is that the strong Lucas-Selfridge test
-      has a running time only slightly greater (order of 10 %) than
-      that of the standard Lucas-Selfridge test, while producing
-      only (roughly) 30 % as many pseudoprimes (and every strong
-      Lucas pseudoprime is also a standard Lucas pseudoprime). Thus
-      the evidence indicates that the strong Lucas-Selfridge test is
-      more effective than the standard Lucas-Selfridge test, and a
-      Baillie-PSW test based on the strong Lucas-Selfridge test
-      should be more reliable. */
-
-   if ((e = mp_add_d(a, 1uL, &Np1)) != MP_OKAY) {
-      goto LBL_LS_ERR;
-   }
-   s = mp_cnt_lsb(&Np1);
-
-   /* CZ
-    * This should round towards zero because
-    * Thomas R. Nicely used GMP's mpz_tdiv_q_2exp()
-    * and mp_div_2d() is equivalent. Additionally:
-    * dividing an even number by two does not produce
-    * any leftovers.
-    */
-   if ((e = mp_div_2d(&Np1, s, &Dz, NULL)) != MP_OKAY) {
-      goto LBL_LS_ERR;
-   }
-   /* We must now compute U_d and V_d. Since d is odd, the accumulated
-      values U and V are initialized to U_1 and V_1 (if the target
-      index were even, U and V would be initialized instead to U_0=0
-      and V_0=2). The values of U_2m and V_2m are also initialized to
-      U_1 and V_1; the FOR loop calculates in succession U_2 and V_2,
-      U_4 and V_4, U_8 and V_8, etc. If the corresponding bits
-      (1, 2, 3, ...) of t are on (the zero bit having been accounted
-      for in the initialization of U and V), these values are then
-      combined with the previous totals for U and V, using the
-      composition formulas for addition of indices. */
-
-   mp_set(&Uz, 1uL);    /* U=U_1 */
-   mp_set(&Vz, (mp_digit)P);    /* V=V_1 */
-   mp_set(&U2mz, 1uL);  /* U_1 */
-   mp_set(&V2mz, (mp_digit)P);  /* V_1 */
-
-   if (Q < 0) {
-      Q = -Q;
-      if ((e = mp_set_long(&Qmz, (unsigned long)Q)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      /* Initializes calculation of Q^d */
-      if ((e = mp_set_long(&Qkdz, (unsigned long)Q)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      Qmz.sign = MP_NEG;
-      Q2mz.sign = MP_NEG;
-      Qkdz.sign = MP_NEG;
-      Q = -Q;
-   } else {
-      if ((e = mp_set_long(&Qmz, (unsigned long)Q)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      /* Initializes calculation of Q^d */
-      if ((e = mp_set_long(&Qkdz, (unsigned long)Q)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-   }
-
-   Nbits = mp_count_bits(&Dz);
-
-   for (u = 1; u < Nbits; u++) { /* zero bit off, already accounted for */
-      /* Formulas for doubling of indices (carried out mod N). Note that
-       * the indices denoted as "2m" are actually powers of 2, specifically
-       * 2^(ul-1) beginning each loop and 2^ul ending each loop.
-       *
-       * U_2m = U_m*V_m
-       * V_2m = V_m*V_m - 2*Q^m
-       */
-
-      if ((e = mp_mul(&U2mz, &V2mz, &U2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_mod(&U2mz, a, &U2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_sqr(&V2mz, &V2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_sub(&V2mz, &Q2mz, &V2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_mod(&V2mz, a, &V2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      /* Must calculate powers of Q for use in V_2m, also for Q^d later */
-      if ((e = mp_sqr(&Qmz, &Qmz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      /* prevents overflow */ /* CZ  still necessary without a fixed prealloc'd mem.? */
-      if ((e = mp_mod(&Qmz, a, &Qmz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((isset = mp_get_bit(&Dz, u)) == MP_VAL) {
-         e = isset;
-         goto LBL_LS_ERR;
-      }
-      if (isset == MP_YES) {
-         /* Formulas for addition of indices (carried out mod N);
-          *
-          * U_(m+n) = (U_m*V_n + U_n*V_m)/2
-          * V_(m+n) = (V_m*V_n + D*U_m*U_n)/2
-          *
-          * Be careful with division by 2 (mod N)!
-          */
-         if ((e = mp_mul(&U2mz, &Vz, &T1z)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mul(&Uz, &V2mz, &T2z)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mul(&V2mz, &Vz, &T3z)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mul(&U2mz, &Uz, &T4z)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = s_mp_mul_si(&T4z, (long)Ds, &T4z)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_add(&T1z, &T2z, &Uz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if (mp_isodd(&Uz) != MP_NO) {
-            if ((e = mp_add(&Uz, a, &Uz)) != MP_OKAY) {
-               goto LBL_LS_ERR;
-            }
-         }
-         /* CZ
-          * This should round towards negative infinity because
-          * Thomas R. Nicely used GMP's mpz_fdiv_q_2exp().
-          * But mp_div_2() does not do so, it is truncating instead.
-          */
-         oddness = mp_isodd(&Uz);
-         if ((e = mp_div_2(&Uz, &Uz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((Uz.sign == MP_NEG) && (oddness != MP_NO)) {
-            if ((e = mp_sub_d(&Uz, 1uL, &Uz)) != MP_OKAY) {
-               goto LBL_LS_ERR;
-            }
-         }
-         if ((e = mp_add(&T3z, &T4z, &Vz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if (mp_isodd(&Vz) != MP_NO) {
-            if ((e = mp_add(&Vz, a, &Vz)) != MP_OKAY) {
-               goto LBL_LS_ERR;
-            }
-         }
-         oddness = mp_isodd(&Vz);
-         if ((e = mp_div_2(&Vz, &Vz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((Vz.sign == MP_NEG) && (oddness != MP_NO)) {
-            if ((e = mp_sub_d(&Vz, 1uL, &Vz)) != MP_OKAY) {
-               goto LBL_LS_ERR;
-            }
-         }
-         if ((e = mp_mod(&Uz, a, &Uz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mod(&Vz, a, &Vz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         /* Calculating Q^d for later use */
-         if ((e = mp_mul(&Qkdz, &Qmz, &Qkdz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mod(&Qkdz, a, &Qkdz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-      }
-   }
-
-   /* If U_d or V_d is congruent to 0 mod N, then N is a prime or a
-      strong Lucas pseudoprime. */
-   if ((mp_iszero(&Uz) != MP_NO) || (mp_iszero(&Vz) != MP_NO)) {
-      *result = MP_YES;
-      goto LBL_LS_ERR;
-   }
-
-   /* NOTE: Ribenboim ("The new book of prime number records," 3rd ed.,
-      1995/6) omits the condition V0 on p.142, but includes it on
-      p. 130. The condition is NECESSARY; otherwise the test will
-      return false negatives---e.g., the primes 29 and 2000029 will be
-      returned as composite. */
-
-   /* Otherwise, we must compute V_2d, V_4d, V_8d, ..., V_{2^(s-1)*d}
-      by repeated use of the formula V_2m = V_m*V_m - 2*Q^m. If any of
-      these are congruent to 0 mod N, then N is a prime or a strong
-      Lucas pseudoprime. */
-
-   /* Initialize 2*Q^(d*2^r) for V_2m */
-   if ((e = mp_mul_2(&Qkdz, &Q2kdz)) != MP_OKAY) {
-      goto LBL_LS_ERR;
-   }
-
-   for (r = 1; r < s; r++) {
-      if ((e = mp_sqr(&Vz, &Vz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_sub(&Vz, &Q2kdz, &Vz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if ((e = mp_mod(&Vz, a, &Vz)) != MP_OKAY) {
-         goto LBL_LS_ERR;
-      }
-      if (mp_iszero(&Vz) != MP_NO) {
-         *result = MP_YES;
-         goto LBL_LS_ERR;
-      }
-      /* Calculate Q^{d*2^r} for next r (final iteration irrelevant). */
-      if (r < (s - 1)) {
-         if ((e = mp_sqr(&Qkdz, &Qkdz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mod(&Qkdz, a, &Qkdz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-         if ((e = mp_mul_2(&Qkdz, &Q2kdz)) != MP_OKAY) {
-            goto LBL_LS_ERR;
-         }
-      }
-   }
-LBL_LS_ERR:
-   mp_clear_multi(&Q2kdz, &T4z, &T3z, &T2z, &T1z, &Qkdz, &Q2mz, &Qmz, &V2mz, &U2mz, &Vz, &Uz, &Np1, &gcd, &Dz, NULL);
-   return e;
-}
-#endif
-#endif
-
-/* End: bn_mp_prime_strong_lucas_selfridge.c */
-
-/* Start: bn_mp_radix_size.c */
-
-/* returns size of ASCII reprensentation */
-int mp_radix_size(const mp_int *a, int radix, int *size)
-{
-   int     res, digs;
-   mp_int  t;
-   mp_digit d;
-
-   *size = 0;
-
-   /* make sure the radix is in range */
-   if ((radix < 2) || (radix > 64)) {
-      return MP_VAL;
-   }
-
-   if (mp_iszero(a) == MP_YES) {
-      *size = 2;
-      return MP_OKAY;
-   }
-
-   /* special case for binary */
-   if (radix == 2) {
-      *size = mp_count_bits(a) + ((a->sign == MP_NEG) ? 1 : 0) + 1;
-      return MP_OKAY;
-   }
-
-   /* digs is the digit count */
-   digs = 0;
-
-   /* if it's negative add one for the sign */
-   if (a->sign == MP_NEG) {
-      ++digs;
-   }
-
-   /* init a copy of the input */
-   if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-      return res;
-   }
-
-   /* force temp to positive */
-   t.sign = MP_ZPOS;
-
-   /* fetch out all of the digits */
-   while (mp_iszero(&t) == MP_NO) {
-      if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) {
-         mp_clear(&t);
-         return res;
-      }
-      ++digs;
-   }
-   mp_clear(&t);
-
-   /* return digs + 1, the 1 is for the NULL byte that would be required. */
-   *size = digs + 1;
-   return MP_OKAY;
-}
-
-/* End: bn_mp_radix_size.c */
-
-/* Start: bn_mp_radix_smap.c */
-
-/* chars used in radix conversions */
-const char *const mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
-const uint8_t mp_s_rmap_reverse[] = {
-   0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, /* ()*+,-./ */
-   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */
-   0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 89:;<=>? */
-   0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, /* @ABCDEFG */
-   0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* HIJKLMNO */
-   0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* PQRSTUVW */
-   0x21, 0x22, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, /* XYZ[\]^_ */
-   0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, /* `abcdefg */
-   0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, /* hijklmno */
-   0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, /* pqrstuvw */
-   0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, /* xyz{|}~. */
-};
-const size_t mp_s_rmap_reverse_sz = sizeof(mp_s_rmap_reverse);
-
-/* End: bn_mp_radix_smap.c */
-
-/* Start: bn_mp_rand.c */
-
-/* First the OS-specific special cases
- * - *BSD
- * - Windows
- */
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
-#define MP_ARC4RANDOM
-#define MP_GEN_RANDOM_MAX     0xffffffffu
-#define MP_GEN_RANDOM_SHIFT   32
-
-static int s_read_arc4random(mp_digit *p)
-{
-   mp_digit d = 0, msk = 0;
-   do {
-      d <<= MP_GEN_RANDOM_SHIFT;
-      d |= ((mp_digit) arc4random());
-      msk <<= MP_GEN_RANDOM_SHIFT;
-      msk |= (MP_MASK & MP_GEN_RANDOM_MAX);
-   } while ((MP_MASK & msk) != MP_MASK);
-   *p = d;
-   return MP_OKAY;
-}
-#endif
-
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#define MP_WIN_CSP
-
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0400
-#endif
-#ifdef _WIN32_WCE
-#define UNDER_CE
-#define ARM
-#endif
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <ntsecapi.h>
-
-static int s_read_win_csp(mp_digit *p)
-{
-   int ret = -1;
-   if (RtlGenRandom(p, sizeof(*p)) == TRUE) {
-      ret = MP_OKAY;
-   }
-   return ret;
-}
-#endif /* WIN32 */
-
-#if !defined(MP_WIN_CSP) && defined(__linux__) && defined(__GLIBC_PREREQ)
-#if __GLIBC_PREREQ(2, 25)
-#define MP_GETRANDOM
-#include <sys/random.h>
-#include <errno.h>
-
-static int s_read_getrandom(mp_digit *p)
-{
-   int ret;
-   do {
-      ret = getrandom(p, sizeof(*p), 0);
-   } while ((ret == -1) && (errno == EINTR));
-   if (ret == sizeof(*p)) return MP_OKAY;
-   return -1;
-}
-#endif
-#endif
-
-/* We assume all platforms besides windows provide "/dev/urandom".
- * In case yours doesn't, define MP_NO_DEV_URANDOM at compile-time.
- */
-#if !defined(MP_WIN_CSP) && !defined(MP_NO_DEV_URANDOM)
-#ifndef MP_DEV_URANDOM
-#define MP_DEV_URANDOM "/dev/urandom"
-#endif
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-
-static int s_read_dev_urandom(mp_digit *p)
-{
-   ssize_t r;
-   int fd;
-   do {
-      fd = open(MP_DEV_URANDOM, O_RDONLY);
-   } while ((fd == -1) && (errno == EINTR));
-   if (fd == -1) return -1;
-   do {
-      r = read(fd, p, sizeof(*p));
-   } while ((r == -1) && (errno == EINTR));
-   close(fd);
-   if (r != sizeof(*p)) return -1;
-   return MP_OKAY;
-}
-#endif
-
-#if defined(MP_PRNG_ENABLE_LTM_RNG)
-unsigned long (*ltm_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void));
-void (*ltm_rng_callback)(void);
-
-static int s_read_ltm_rng(mp_digit *p)
-{
-   unsigned long ret;
-   if (ltm_rng == NULL) return -1;
-   ret = ltm_rng((void *)p, sizeof(*p), ltm_rng_callback);
-   if (ret != sizeof(*p)) return -1;
-   return MP_OKAY;
-}
-#endif
-
-static int s_rand_digit(mp_digit *p)
-{
-   int ret = -1;
-
-#if defined(MP_ARC4RANDOM)
-   ret = s_read_arc4random(p);
-   if (ret == MP_OKAY) return ret;
-#endif
-
-#if defined(MP_WIN_CSP)
-   ret = s_read_win_csp(p);
-   if (ret == MP_OKAY) return ret;
-#else
-
-#if defined(MP_GETRANDOM)
-   ret = s_read_getrandom(p);
-   if (ret == MP_OKAY) return ret;
-#endif
-#if defined(MP_DEV_URANDOM)
-   ret = s_read_dev_urandom(p);
-   if (ret == MP_OKAY) return ret;
-#endif
-
-#endif /* MP_WIN_CSP */
-
-#if defined(MP_PRNG_ENABLE_LTM_RNG)
-   ret = s_read_ltm_rng(p);
-   if (ret == MP_OKAY) return ret;
-#endif
-
-   return ret;
-}
-
-/* makes a pseudo-random int of a given size */
-int mp_rand_digit(mp_digit *r)
-{
-   int ret = s_rand_digit(r);
-   *r &= MP_MASK;
-   return ret;
-}
-
-int mp_rand(mp_int *a, int digits)
-{
-   int     res;
-   mp_digit d;
-
-   mp_zero(a);
-   if (digits <= 0) {
-      return MP_OKAY;
-   }
-
-   /* first place a random non-zero digit */
-   do {
-      if (mp_rand_digit(&d) != MP_OKAY) {
-         return MP_VAL;
-      }
-   } while (d == 0u);
-
-   if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
-      return res;
-   }
-
-   while (--digits > 0) {
-      if ((res = mp_lshd(a, 1)) != MP_OKAY) {
-         return res;
-      }
-
-      if (mp_rand_digit(&d) != MP_OKAY) {
-         return MP_VAL;
-      }
-      if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   return MP_OKAY;
-}
-
-/* Start: bn_mp_read_radix.c */
-
-/* read a string [ASCII] in a given radix */
-int mp_read_radix(mp_int *a, const char *str, int radix)
-{
-   int     y, res, neg;
-   unsigned pos;
-   char    ch;
-
-   /* zero the digit bignum */
-   mp_zero(a);
-
-   /* make sure the radix is ok */
-   if ((radix < 2) || (radix > 64)) {
-      return MP_VAL;
-   }
-
-   /* if the leading digit is a
-    * minus set the sign to negative.
-    */
-   if (*str == '-') {
-      ++str;
-      neg = MP_NEG;
-   } else {
-      neg = MP_ZPOS;
-   }
-
-   /* set the integer to the default of zero */
-   mp_zero(a);
-
-   /* process each digit of the string */
-   while (*str != '\0') {
-      /* if the radix <= 36 the conversion is case insensitive
-       * this allows numbers like 1AB and 1ab to represent the same  value
-       * [e.g. in hex]
-       */
-      ch = (radix <= 36) ? (char)toupper((int)*str) : *str;
-      pos = (unsigned)(ch - '(');
-      if (mp_s_rmap_reverse_sz < pos) {
-         break;
-      }
-      y = (int)mp_s_rmap_reverse[pos];
-
-      /* if the char was found in the map
-       * and is less than the given radix add it
-       * to the number, otherwise exit the loop.
-       */
-      if ((y == 0xff) || (y >= radix)) {
-         break;
-      }
-      if ((res = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) {
-         return res;
-      }
-      if ((res = mp_add_d(a, (mp_digit)y, a)) != MP_OKAY) {
-         return res;
-      }
-      ++str;
-   }
-
-   /* if an illegal character was found, fail. */
-   if (!((*str == '\0') || (*str == '\r') || (*str == '\n'))) {
-      mp_zero(a);
-      return MP_VAL;
-   }
-
-   /* set the sign only if a != 0 */
-   if (mp_iszero(a) != MP_YES) {
-      a->sign = neg;
-   }
-   return MP_OKAY;
-}
-
-/* End: bn_mp_read_radix.c */
-
-/* Start: bn_mp_read_signed_bin.c */
-
-/* read signed bin, big endian, first byte is 0==positive or 1==negative */
-int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c)
-{
-   int     res;
-
-   /* read magnitude */
-   if ((res = mp_read_unsigned_bin(a, b + 1, c - 1)) != MP_OKAY) {
-      return res;
-   }
-
-   /* first byte is 0 for positive, non-zero for negative */
-   if (b[0] == (unsigned char)0) {
-      a->sign = MP_ZPOS;
-   } else {
-      a->sign = MP_NEG;
-   }
-
-   return MP_OKAY;
-}
-
-/* End: bn_mp_read_signed_bin.c */
-
-/* Start: bn_mp_read_unsigned_bin.c */
-
-/* reads a unsigned char array, assumes the msb is stored first [big endian] */
-int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c)
-{
-   int     res;
-
-   /* make sure there are at least two digits */
-   if (a->alloc < 2) {
-      if ((res = mp_grow(a, 2)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* zero the int */
-   mp_zero(a);
-
-   /* read the bytes in */
-   while (c-- > 0) {
-      if ((res = mp_mul_2d(a, 8, a)) != MP_OKAY) {
-         return res;
-      }
-
-#ifndef MP_8BIT
-      a->dp[0] |= *b++;
-      a->used += 1;
-#else
-      a->dp[0] = (*b & MP_MASK);
-      a->dp[1] |= ((*b++ >> 7) & 1u);
-      a->used += 2;
-#endif
-   }
-   mp_clamp(a);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_read_unsigned_bin.c */
-
-/* Start: bn_mp_reduce.c */
-
-/* reduces x mod m, assumes 0 < x < m**2, mu is
- * precomputed via mp_reduce_setup.
- * From HAC pp.604 Algorithm 14.42
- */
-int mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu)
-{
-   mp_int  q;
-   int     res, um = m->used;
-
-   /* q = x */
-   if ((res = mp_init_copy(&q, x)) != MP_OKAY) {
-      return res;
-   }
-
-   /* q1 = x / b**(k-1)  */
-   mp_rshd(&q, um - 1);
-
-   /* according to HAC this optimization is ok */
-   if ((mp_digit)um > ((mp_digit)1 << (DIGIT_BIT - 1))) {
-      if ((res = mp_mul(&q, mu, &q)) != MP_OKAY) {
-         goto CLEANUP;
-      }
-   } else {
-      if ((res = s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) {
-         goto CLEANUP;
-      }
-   }
-
-   /* q3 = q2 / b**(k+1) */
-   mp_rshd(&q, um + 1);
-
-   /* x = x mod b**(k+1), quick (no division) */
-   if ((res = mp_mod_2d(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
-      goto CLEANUP;
-   }
-
-   /* q = q * m mod b**(k+1), quick (no division) */
-   if ((res = s_mp_mul_digs(&q, m, &q, um + 1)) != MP_OKAY) {
-      goto CLEANUP;
-   }
-
-   /* x = x - q */
-   if ((res = mp_sub(x, &q, x)) != MP_OKAY) {
-      goto CLEANUP;
-   }
-
-   /* If x < 0, add b**(k+1) to it */
-   if (mp_cmp_d(x, 0uL) == MP_LT) {
-      mp_set(&q, 1uL);
-      if ((res = mp_lshd(&q, um + 1)) != MP_OKAY)
-         goto CLEANUP;
-      if ((res = mp_add(x, &q, x)) != MP_OKAY)
-         goto CLEANUP;
-   }
-
-   /* Back off if it's too big */
-   while (mp_cmp(x, m) != MP_LT) {
-      if ((res = s_mp_sub(x, m, x)) != MP_OKAY) {
-         goto CLEANUP;
-      }
-   }
-
-CLEANUP:
-   mp_clear(&q);
-
-   return res;
-}
-
-/* End: bn_mp_reduce.c */
-
-/* Start: bn_mp_reduce_2k.c */
-
-/* reduces a modulo n where n is of the form 2**p - d */
-int mp_reduce_2k(mp_int *a, const mp_int *n, mp_digit d)
-{
-   mp_int q;
-   int    p, res;
-
-   if ((res = mp_init(&q)) != MP_OKAY) {
-      return res;
-   }
-
-   p = mp_count_bits(n);
-top:
-   /* q = a/2**p, a = a mod 2**p */
-   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if (d != 1u) {
-      /* q = q * d */
-      if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* a = a + q */
-   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if (mp_cmp_mag(a, n) != MP_LT) {
-      if ((res = s_mp_sub(a, n, a)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      goto top;
-   }
-
-LBL_ERR:
-   mp_clear(&q);
-   return res;
-}
-
-/* End: bn_mp_reduce_2k.c */
-
-/* Start: bn_mp_reduce_2k_l.c */
-
-/* reduces a modulo n where n is of the form 2**p - d
-   This differs from reduce_2k since "d" can be larger
-   than a single digit.
-*/
-int mp_reduce_2k_l(mp_int *a, const mp_int *n, const mp_int *d)
-{
-   mp_int q;
-   int    p, res;
-
-   if ((res = mp_init(&q)) != MP_OKAY) {
-      return res;
-   }
-
-   p = mp_count_bits(n);
-top:
-   /* q = a/2**p, a = a mod 2**p */
-   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* q = q * d */
-   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* a = a + q */
-   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if (mp_cmp_mag(a, n) != MP_LT) {
-      if ((res = s_mp_sub(a, n, a)) != MP_OKAY) {
-         goto LBL_ERR;
-      }
-      goto top;
-   }
-
-LBL_ERR:
-   mp_clear(&q);
-   return res;
-}
-
-/* End: bn_mp_reduce_2k_l.c */
-
-/* Start: bn_mp_reduce_2k_setup.c */
-
-/* determines the setup value */
-int mp_reduce_2k_setup(const mp_int *a, mp_digit *d)
-{
-   int res, p;
-   mp_int tmp;
-
-   if ((res = mp_init(&tmp)) != MP_OKAY) {
-      return res;
-   }
-
-   p = mp_count_bits(a);
-   if ((res = mp_2expt(&tmp, p)) != MP_OKAY) {
-      mp_clear(&tmp);
-      return res;
-   }
-
-   if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) {
-      mp_clear(&tmp);
-      return res;
-   }
-
-   *d = tmp.dp[0];
-   mp_clear(&tmp);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_reduce_2k_setup.c */
-
-/* Start: bn_mp_reduce_2k_setup_l.c */
-
-/* determines the setup value */
-int mp_reduce_2k_setup_l(const mp_int *a, mp_int *d)
-{
-   int    res;
-   mp_int tmp;
-
-   if ((res = mp_init(&tmp)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-LBL_ERR:
-   mp_clear(&tmp);
-   return res;
-}
-
-/* End: bn_mp_reduce_2k_setup_l.c */
-
-/* Start: bn_mp_reduce_is_2k.c */
-
-/* determines if mp_reduce_2k can be used */
-int mp_reduce_is_2k(const mp_int *a)
-{
-   int ix, iy, iw;
-   mp_digit iz;
-
-   if (a->used == 0) {
-      return MP_NO;
-   } else if (a->used == 1) {
-      return MP_YES;
-   } else if (a->used > 1) {
-      iy = mp_count_bits(a);
-      iz = 1;
-      iw = 1;
-
-      /* Test every bit from the second digit up, must be 1 */
-      for (ix = DIGIT_BIT; ix < iy; ix++) {
-         if ((a->dp[iw] & iz) == 0u) {
-            return MP_NO;
-         }
-         iz <<= 1;
-         if (iz > (mp_digit)MP_MASK) {
-            ++iw;
-            iz = 1;
-         }
-      }
-   }
-   return MP_YES;
-}
-
-/* End: bn_mp_reduce_is_2k.c */
-
-/* Start: bn_mp_reduce_is_2k_l.c */
-
-/* determines if reduce_2k_l can be used */
-int mp_reduce_is_2k_l(const mp_int *a)
-{
-   int ix, iy;
-
-   if (a->used == 0) {
-      return MP_NO;
-   } else if (a->used == 1) {
-      return MP_YES;
-   } else if (a->used > 1) {
-      /* if more than half of the digits are -1 we're sold */
-      for (iy = ix = 0; ix < a->used; ix++) {
-         if (a->dp[ix] == MP_MASK) {
-            ++iy;
-         }
-      }
-      return (iy >= (a->used/2)) ? MP_YES : MP_NO;
-
-   }
-   return MP_NO;
-}
-
-/* End: bn_mp_reduce_is_2k_l.c */
-
-/* Start: bn_mp_reduce_setup.c */
-
-/* pre-calculate the value required for Barrett reduction
- * For a given modulus "b" it calulates the value required in "a"
- */
-int mp_reduce_setup(mp_int *a, const mp_int *b)
-{
-   int     res;
-
-   if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
-      return res;
-   }
-   return mp_div(a, b, a, NULL);
-}
-
-/* End: bn_mp_reduce_setup.c */
-
-/* Start: bn_mp_rshd.c */
-
-/* shift right a certain amount of digits */
-void mp_rshd(mp_int *a, int b)
-{
-   int     x;
-
-   /* if b <= 0 then ignore it */
-   if (b <= 0) {
-      return;
-   }
-
-   /* if b > used then simply zero it and return */
-   if (a->used <= b) {
-      mp_zero(a);
-      return;
-   }
-
-   {
-      mp_digit *bottom, *top;
-
-      /* shift the digits down */
-
-      /* bottom */
-      bottom = a->dp;
-
-      /* top [offset into digits] */
-      top = a->dp + b;
-
-      /* this is implemented as a sliding window where
-       * the window is b-digits long and digits from
-       * the top of the window are copied to the bottom
-       *
-       * e.g.
-
-       b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
-                   /\                   |      ---->
-                    \-------------------/      ---->
-       */
-      for (x = 0; x < (a->used - b); x++) {
-         *bottom++ = *top++;
-      }
-
-      /* zero the top digits */
-      for (; x < a->used; x++) {
-         *bottom++ = 0;
-      }
-   }
-
-   /* remove excess digits */
-   a->used -= b;
-}
-
-/* End: bn_mp_rshd.c */
-
-/* Start: bn_mp_set.c */
-
-/* set to a digit */
-void mp_set(mp_int *a, mp_digit b)
-{
-   mp_zero(a);
-   a->dp[0] = b & MP_MASK;
-   a->used  = (a->dp[0] != 0u) ? 1 : 0;
-}
-
-/* End: bn_mp_set.c */
-
-/* Start: bn_mp_set_int.c */
-
-/* set a 32-bit const */
-int mp_set_int(mp_int *a, unsigned long b)
-{
-   int     x, res;
-
-   mp_zero(a);
-
-   /* set four bits at a time */
-   for (x = 0; x < 8; x++) {
-      /* shift the number up four bits */
-      if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) {
-         return res;
-      }
-
-      /* OR in the top four bits of the source */
-      a->dp[0] |= (mp_digit)(b >> 28) & 15uL;
-
-      /* shift the source up to the next four bits */
-      b <<= 4;
-
-      /* ensure that digits are not clamped off */
-      a->used += 1;
-   }
-   mp_clamp(a);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_set_int.c */
-
-/* Start: bn_mp_set_long.c */
-
-/* set a platform dependent unsigned long int */
-MP_SET_XLONG(mp_set_long, unsigned long)
-
-/* End: bn_mp_set_long.c */
-
-/* Start: bn_mp_set_long_long.c */
-
-/* set a platform dependent unsigned long long int */
-MP_SET_XLONG(mp_set_long_long, unsigned long long)
-
-/* End: bn_mp_set_long_long.c */
-
-/* Start: bn_mp_shrink.c */
-
-/* shrink a bignum */
-int mp_shrink(mp_int *a)
-{
-   mp_digit *tmp;
-   int used = 1;
-
-   if (a->used > 0) {
-      used = a->used;
-   }
-
-   if (a->alloc != used) {
-      if ((tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * (size_t)used)) == NULL) {
-         return MP_MEM;
-      }
-      a->dp    = tmp;
-      a->alloc = used;
-   }
-   return MP_OKAY;
-}
-
-/* End: bn_mp_shrink.c */
-
-/* Start: bn_mp_signed_bin_size.c */
-
-/* get the size for an signed equivalent */
-int mp_signed_bin_size(const mp_int *a)
-{
-   return 1 + mp_unsigned_bin_size(a);
-}
-
-/* End: bn_mp_signed_bin_size.c */
-
-/* Start: bn_mp_sqr.c */
-
-/* computes b = a*a */
-int mp_sqr(const mp_int *a, mp_int *b)
-{
-   int     res;
-
-   /* use Toom-Cook? */
-   if (a->used >= TOOM_SQR_CUTOFF) {
-      res = mp_toom_sqr(a, b);
-      /* Karatsuba? */
-   } else
-      if (a->used >= KARATSUBA_SQR_CUTOFF) {
-         res = mp_karatsuba_sqr(a, b);
-      } else
-      {
-         /* can we use the fast comba multiplier? */
-         if ((((a->used * 2) + 1) < (int)MP_WARRAY) &&
-             (a->used <
-              (int)(1u << (((sizeof(mp_word) * (size_t)CHAR_BIT) - (2u * (size_t)DIGIT_BIT)) - 1u)))) {
-            res = fast_s_mp_sqr(a, b);
-         } else
-         {
-            res = s_mp_sqr(a, b);
-         }
-      }
-   b->sign = MP_ZPOS;
-   return res;
-}
-
-/* End: bn_mp_sqr.c */
-
-/* Start: bn_mp_sqrmod.c */
-
-/* c = a * a (mod b) */
-int mp_sqrmod(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     res;
-   mp_int  t;
-
-   if ((res = mp_init(&t)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_sqr(a, &t)) != MP_OKAY) {
-      mp_clear(&t);
-      return res;
-   }
-   res = mp_mod(&t, b, c);
-   mp_clear(&t);
-   return res;
-}
-
-/* End: bn_mp_sqrmod.c */
-
-/* Start: bn_mp_sqrt.c */
-
-/* this function is less generic than mp_n_root, simpler and faster */
-int mp_sqrt(const mp_int *arg, mp_int *ret)
-{
-   int res;
-   mp_int t1, t2;
-
-   /* must be positive */
-   if (arg->sign == MP_NEG) {
-      return MP_VAL;
-   }
-
-   /* easy out */
-   if (mp_iszero(arg) == MP_YES) {
-      mp_zero(ret);
-      return MP_OKAY;
-   }
-
-   if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_init(&t2)) != MP_OKAY) {
-      goto E2;
-   }
-
-   /* First approx. (not very bad for large arg) */
-   mp_rshd(&t1, t1.used/2);
-
-   /* t1 > 0  */
-   if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) {
-      goto E1;
-   }
-   if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) {
-      goto E1;
-   }
-   if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) {
-      goto E1;
-   }
-   /* And now t1 > sqrt(arg) */
-   do {
-      if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) {
-         goto E1;
-      }
-      if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) {
-         goto E1;
-      }
-      if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) {
-         goto E1;
-      }
-      /* t1 >= sqrt(arg) >= t2 at this point */
-   } while (mp_cmp_mag(&t1, &t2) == MP_GT);
-
-   mp_exch(&t1, ret);
-
-E1:
-   mp_clear(&t2);
-E2:
-   mp_clear(&t1);
-   return res;
-}
-
-/* End: bn_mp_sqrt.c */
-
-/* Start: bn_mp_sqrtmod_prime.c */
-
-/* Tonelli-Shanks algorithm
- * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
- * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html
- *
- */
-
-int mp_sqrtmod_prime(const mp_int *n, const mp_int *prime, mp_int *ret)
-{
-   int res, legendre;
-   mp_int t1, C, Q, S, Z, M, T, R, two;
-   mp_digit i;
-
-   /* first handle the simple cases */
-   if (mp_cmp_d(n, 0uL) == MP_EQ) {
-      mp_zero(ret);
-      return MP_OKAY;
-   }
-   if (mp_cmp_d(prime, 2uL) == MP_EQ)                            return MP_VAL; /* prime must be odd */
-   if ((res = mp_jacobi(n, prime, &legendre)) != MP_OKAY)        return res;
-   if (legendre == -1)                                           return MP_VAL; /* quadratic non-residue mod prime */
-
-   if ((res = mp_init_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL)) != MP_OKAY) {
-      return res;
-   }
-
-   /* SPECIAL CASE: if prime mod 4 == 3
-    * compute directly: res = n^(prime+1)/4 mod prime
-    * Handbook of Applied Cryptography algorithm 3.36
-    */
-   if ((res = mp_mod_d(prime, 4uL, &i)) != MP_OKAY)               goto cleanup;
-   if (i == 3u) {
-      if ((res = mp_add_d(prime, 1uL, &t1)) != MP_OKAY)           goto cleanup;
-      if ((res = mp_div_2(&t1, &t1)) != MP_OKAY)                  goto cleanup;
-      if ((res = mp_div_2(&t1, &t1)) != MP_OKAY)                  goto cleanup;
-      if ((res = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY)      goto cleanup;
-      res = MP_OKAY;
-      goto cleanup;
-   }
-
-   /* NOW: Tonelli-Shanks algorithm */
-
-   /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */
-   if ((res = mp_copy(prime, &Q)) != MP_OKAY)                    goto cleanup;
-   if ((res = mp_sub_d(&Q, 1uL, &Q)) != MP_OKAY)                 goto cleanup;
-   /* Q = prime - 1 */
-   mp_zero(&S);
-   /* S = 0 */
-   while (mp_iseven(&Q) != MP_NO) {
-      if ((res = mp_div_2(&Q, &Q)) != MP_OKAY)                    goto cleanup;
-      /* Q = Q / 2 */
-      if ((res = mp_add_d(&S, 1uL, &S)) != MP_OKAY)               goto cleanup;
-      /* S = S + 1 */
-   }
-
-   /* find a Z such that the Legendre symbol (Z|prime) == -1 */
-   if ((res = mp_set_int(&Z, 2uL)) != MP_OKAY)                    goto cleanup;
-   /* Z = 2 */
-   while (1) {
-      if ((res = mp_jacobi(&Z, prime, &legendre)) != MP_OKAY)     goto cleanup;
-      if (legendre == -1) break;
-      if ((res = mp_add_d(&Z, 1uL, &Z)) != MP_OKAY)               goto cleanup;
-      /* Z = Z + 1 */
-   }
-
-   if ((res = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY)         goto cleanup;
-   /* C = Z ^ Q mod prime */
-   if ((res = mp_add_d(&Q, 1uL, &t1)) != MP_OKAY)                goto cleanup;
-   if ((res = mp_div_2(&t1, &t1)) != MP_OKAY)                    goto cleanup;
-   /* t1 = (Q + 1) / 2 */
-   if ((res = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY)         goto cleanup;
-   /* R = n ^ ((Q + 1) / 2) mod prime */
-   if ((res = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY)          goto cleanup;
-   /* T = n ^ Q mod prime */
-   if ((res = mp_copy(&S, &M)) != MP_OKAY)                       goto cleanup;
-   /* M = S */
-   if ((res = mp_set_int(&two, 2uL)) != MP_OKAY)                 goto cleanup;
-
-   res = MP_VAL;
-   while (1) {
-      if ((res = mp_copy(&T, &t1)) != MP_OKAY)                    goto cleanup;
-      i = 0;
-      while (1) {
-         if (mp_cmp_d(&t1, 1uL) == MP_EQ) break;
-         if ((res = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto cleanup;
-         i++;
-      }
-      if (i == 0u) {
-         if ((res = mp_copy(&R, ret)) != MP_OKAY)                  goto cleanup;
-         res = MP_OKAY;
-         goto cleanup;
-      }
-      if ((res = mp_sub_d(&M, i, &t1)) != MP_OKAY)                goto cleanup;
-      if ((res = mp_sub_d(&t1, 1uL, &t1)) != MP_OKAY)             goto cleanup;
-      if ((res = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY)   goto cleanup;
-      /* t1 = 2 ^ (M - i - 1) */
-      if ((res = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY)     goto cleanup;
-      /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */
-      if ((res = mp_sqrmod(&t1, prime, &C)) != MP_OKAY)           goto cleanup;
-      /* C = (t1 * t1) mod prime */
-      if ((res = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY)       goto cleanup;
-      /* R = (R * t1) mod prime */
-      if ((res = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY)        goto cleanup;
-      /* T = (T * C) mod prime */
-      mp_set(&M, i);
-      /* M = i */
-   }
-
-cleanup:
-   mp_clear_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL);
-   return res;
-}
-
-/* End: bn_mp_sqrtmod_prime.c */
-
-/* Start: bn_mp_sub.c */
-
-/* high level subtraction (handles signs) */
-int mp_sub(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     sa, sb, res;
-
-   sa = a->sign;
-   sb = b->sign;
-
-   if (sa != sb) {
-      /* subtract a negative from a positive, OR */
-      /* subtract a positive from a negative. */
-      /* In either case, ADD their magnitudes, */
-      /* and use the sign of the first number. */
-      c->sign = sa;
-      res = s_mp_add(a, b, c);
-   } else {
-      /* subtract a positive from a positive, OR */
-      /* subtract a negative from a negative. */
-      /* First, take the difference between their */
-      /* magnitudes, then... */
-      if (mp_cmp_mag(a, b) != MP_LT) {
-         /* Copy the sign from the first */
-         c->sign = sa;
-         /* The first has a larger or equal magnitude */
-         res = s_mp_sub(a, b, c);
-      } else {
-         /* The result has the *opposite* sign from */
-         /* the first number. */
-         c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
-         /* The second has a larger magnitude */
-         res = s_mp_sub(b, a, c);
-      }
-   }
-   return res;
-}
-
-/* End: bn_mp_sub.c */
-
-/* Start: bn_mp_sub_d.c */
-
-/* single digit subtraction */
-int mp_sub_d(const mp_int *a, mp_digit b, mp_int *c)
-{
-   mp_digit *tmpa, *tmpc, mu;
-   int       res, ix, oldused;
-
-   /* grow c as required */
-   if (c->alloc < (a->used + 1)) {
-      if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* if a is negative just do an unsigned
-    * addition [with fudged signs]
-    */
-   if (a->sign == MP_NEG) {
-      mp_int a_ = *a;
-      a_.sign = MP_ZPOS;
-      res     = mp_add_d(&a_, b, c);
-      c->sign = MP_NEG;
-
-      /* clamp */
-      mp_clamp(c);
-
-      return res;
-   }
-
-   /* setup regs */
-   oldused = c->used;
-   tmpa    = a->dp;
-   tmpc    = c->dp;
-
-   /* if a <= b simply fix the single digit */
-   if (((a->used == 1) && (a->dp[0] <= b)) || (a->used == 0)) {
-      if (a->used == 1) {
-         *tmpc++ = b - *tmpa;
-      } else {
-         *tmpc++ = b;
-      }
-      ix      = 1;
-
-      /* negative/1digit */
-      c->sign = MP_NEG;
-      c->used = 1;
-   } else {
-      /* positive/size */
-      c->sign = MP_ZPOS;
-      c->used = a->used;
-
-      /* subtract first digit */
-      *tmpc    = *tmpa++ - b;
-      mu       = *tmpc >> ((sizeof(mp_digit) * (size_t)CHAR_BIT) - 1u);
-      *tmpc++ &= MP_MASK;
-
-      /* handle rest of the digits */
-      for (ix = 1; ix < a->used; ix++) {
-         *tmpc    = *tmpa++ - mu;
-         mu       = *tmpc >> ((sizeof(mp_digit) * (size_t)CHAR_BIT) - 1u);
-         *tmpc++ &= MP_MASK;
-      }
-   }
-
-   /* zero excess digits */
-   while (ix++ < oldused) {
-      *tmpc++ = 0;
-   }
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_sub_d.c */
-
-/* Start: bn_mp_submod.c */
-
-/* d = a - b (mod c) */
-int mp_submod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d)
-{
-   int     res;
-   mp_int  t;
-
-
-   if ((res = mp_init(&t)) != MP_OKAY) {
-      return res;
-   }
-
-   if ((res = mp_sub(a, b, &t)) != MP_OKAY) {
-      mp_clear(&t);
-      return res;
-   }
-   res = mp_mod(&t, c, d);
-   mp_clear(&t);
-   return res;
-}
-
-/* End: bn_mp_submod.c */
-
-/* Start: bn_mp_tc_and.c */
-
-/* two complement and */
-int mp_tc_and(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int res = MP_OKAY, bits, abits, bbits;
-   int as = mp_isneg(a), bs = mp_isneg(b);
-   mp_int *mx = NULL, _mx, acpy, bcpy;
-
-   if ((as != MP_NO) || (bs != MP_NO)) {
-      abits = mp_count_bits(a);
-      bbits = mp_count_bits(b);
-      bits = MAX(abits, bbits);
-      res = mp_init_set_int(&_mx, 1uL);
-      if (res != MP_OKAY) {
-         goto end;
-      }
-
-      mx = &_mx;
-      res = mp_mul_2d(mx, bits + 1, mx);
-      if (res != MP_OKAY) {
-         goto end;
-      }
-
-      if (as != MP_NO) {
-         res = mp_init(&acpy);
-         if (res != MP_OKAY) {
-            goto end;
-         }
-
-         res = mp_add(mx, a, &acpy);
-         if (res != MP_OKAY) {
-            mp_clear(&acpy);
-            goto end;
-         }
-         a = &acpy;
-      }
-      if (bs != MP_NO) {
-         res = mp_init(&bcpy);
-         if (res != MP_OKAY) {
-            goto end;
-         }
-
-         res = mp_add(mx, b, &bcpy);
-         if (res != MP_OKAY) {
-            mp_clear(&bcpy);
-            goto end;
-         }
-         b = &bcpy;
-      }
-   }
-
-   res = mp_and(a, b, c);
-
-   if ((as != MP_NO) && (bs != MP_NO) && (res == MP_OKAY)) {
-      res = mp_sub(c, mx, c);
-   }
-
-end:
-   if (a == &acpy) {
-      mp_clear(&acpy);
-   }
-
-   if (b == &bcpy) {
-      mp_clear(&bcpy);
-   }
-
-   if (mx == &_mx) {
-      mp_clear(mx);
-   }
-
-   return res;
-}
-
-/* End: bn_mp_tc_and.c */
-
-/* Start: bn_mp_tc_div_2d.c */
-
-/* two complement right shift */
-int mp_tc_div_2d(const mp_int *a, int b, mp_int *c)
-{
-   int res;
-   if (mp_isneg(a) == MP_NO) {
-      return mp_div_2d(a, b, c, NULL);
-   }
-
-   res = mp_add_d(a, 1uL, c);
-   if (res != MP_OKAY) {
-      return res;
-   }
-
-   res = mp_div_2d(c, b, c, NULL);
-   return (res == MP_OKAY) ? mp_sub_d(c, 1uL, c) : res;
-}
-
-/* End: bn_mp_tc_div_2d.c */
-
-/* Start: bn_mp_tc_or.c */
-
-/* two complement or */
-int mp_tc_or(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int res = MP_OKAY, bits, abits, bbits;
-   int as = mp_isneg(a), bs = mp_isneg(b);
-   mp_int *mx = NULL, _mx, acpy, bcpy;
-
-   if ((as != MP_NO) || (bs != MP_NO)) {
-      abits = mp_count_bits(a);
-      bbits = mp_count_bits(b);
-      bits = MAX(abits, bbits);
-      res = mp_init_set_int(&_mx, 1uL);
-      if (res != MP_OKAY) {
-         goto end;
-      }
-
-      mx = &_mx;
-      res = mp_mul_2d(mx, bits + 1, mx);
-      if (res != MP_OKAY) {
-         goto end;
-      }
-
-      if (as != MP_NO) {
-         res = mp_init(&acpy);
-         if (res != MP_OKAY) {
-            goto end;
-         }
-
-         res = mp_add(mx, a, &acpy);
-         if (res != MP_OKAY) {
-            mp_clear(&acpy);
-            goto end;
-         }
-         a = &acpy;
-      }
-      if (bs != MP_NO) {
-         res = mp_init(&bcpy);
-         if (res != MP_OKAY) {
-            goto end;
-         }
-
-         res = mp_add(mx, b, &bcpy);
-         if (res != MP_OKAY) {
-            mp_clear(&bcpy);
-            goto end;
-         }
-         b = &bcpy;
-      }
-   }
-
-   res = mp_or(a, b, c);
-
-   if (((as != MP_NO) || (bs != MP_NO)) && (res == MP_OKAY)) {
-      res = mp_sub(c, mx, c);
-   }
-
-end:
-   if (a == &acpy) {
-      mp_clear(&acpy);
-   }
-
-   if (b == &bcpy) {
-      mp_clear(&bcpy);
-   }
-
-   if (mx == &_mx) {
-      mp_clear(mx);
-   }
-
-   return res;
-}
-
-/* End: bn_mp_tc_or.c */
-
-/* Start: bn_mp_tc_xor.c */
-
-/* two complement xor */
-int mp_tc_xor(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int res = MP_OKAY, bits, abits, bbits;
-   int as = mp_isneg(a), bs = mp_isneg(b);
-   mp_int *mx = NULL, _mx, acpy, bcpy;
-
-   if ((as != MP_NO) || (bs != MP_NO)) {
-      abits = mp_count_bits(a);
-      bbits = mp_count_bits(b);
-      bits = MAX(abits, bbits);
-      res = mp_init_set_int(&_mx, 1uL);
-      if (res != MP_OKAY) {
-         goto end;
-      }
-
-      mx = &_mx;
-      res = mp_mul_2d(mx, bits + 1, mx);
-      if (res != MP_OKAY) {
-         goto end;
-      }
-
-      if (as != MP_NO) {
-         res = mp_init(&acpy);
-         if (res != MP_OKAY) {
-            goto end;
-         }
-
-         res = mp_add(mx, a, &acpy);
-         if (res != MP_OKAY) {
-            mp_clear(&acpy);
-            goto end;
-         }
-         a = &acpy;
-      }
-      if (bs != MP_NO) {
-         res = mp_init(&bcpy);
-         if (res != MP_OKAY) {
-            goto end;
-         }
-
-         res = mp_add(mx, b, &bcpy);
-         if (res != MP_OKAY) {
-            mp_clear(&bcpy);
-            goto end;
-         }
-         b = &bcpy;
-      }
-   }
-
-   res = mp_xor(a, b, c);
-
-   if ((as != bs) && (res == MP_OKAY)) {
-      res = mp_sub(c, mx, c);
-   }
-
-end:
-   if (a == &acpy) {
-      mp_clear(&acpy);
-   }
-
-   if (b == &bcpy) {
-      mp_clear(&bcpy);
-   }
-
-   if (mx == &_mx) {
-      mp_clear(mx);
-   }
-
-   return res;
-}
-
-/* End: bn_mp_tc_xor.c */
-
-/* Start: bn_mp_to_signed_bin.c */
-
-/* store in signed [big endian] format */
-int mp_to_signed_bin(const mp_int *a, unsigned char *b)
-{
-   int     res;
-
-   if ((res = mp_to_unsigned_bin(a, b + 1)) != MP_OKAY) {
-      return res;
-   }
-   b[0] = (a->sign == MP_ZPOS) ? (unsigned char)0 : (unsigned char)1;
-   return MP_OKAY;
-}
-
-/* End: bn_mp_to_signed_bin.c */
-
-/* Start: bn_mp_to_signed_bin_n.c */
-
-/* store in signed [big endian] format */
-int mp_to_signed_bin_n(const mp_int *a, unsigned char *b, unsigned long *outlen)
-{
-   if (*outlen < (unsigned long)mp_signed_bin_size(a)) {
-      return MP_VAL;
-   }
-   *outlen = (unsigned long)mp_signed_bin_size(a);
-   return mp_to_signed_bin(a, b);
-}
-
-/* End: bn_mp_to_signed_bin_n.c */
-
-/* Start: bn_mp_to_unsigned_bin.c */
-
-/* store in unsigned [big endian] format */
-int mp_to_unsigned_bin(const mp_int *a, unsigned char *b)
-{
-   int     x, res;
-   mp_int  t;
-
-   if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-      return res;
-   }
-
-   x = 0;
-   while (mp_iszero(&t) == MP_NO) {
-#ifndef MP_8BIT
-      b[x++] = (unsigned char)(t.dp[0] & 255u);
-#else
-      b[x++] = (unsigned char)(t.dp[0] | ((t.dp[1] & 1u) << 7));
-#endif
-      if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) {
-         mp_clear(&t);
-         return res;
-      }
-   }
-   bn_reverse(b, x);
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_to_unsigned_bin.c */
-
-/* Start: bn_mp_to_unsigned_bin_n.c */
-
-/* store in unsigned [big endian] format */
-int mp_to_unsigned_bin_n(const mp_int *a, unsigned char *b, unsigned long *outlen)
-{
-   if (*outlen < (unsigned long)mp_unsigned_bin_size(a)) {
-      return MP_VAL;
-   }
-   *outlen = (unsigned long)mp_unsigned_bin_size(a);
-   return mp_to_unsigned_bin(a, b);
-}
-
-/* End: bn_mp_to_unsigned_bin_n.c */
-
-/* Start: bn_mp_toom_mul.c */
-
-/* multiplication using the Toom-Cook 3-way algorithm
- *
- * Much more complicated than Karatsuba but has a lower
- * asymptotic running time of O(N**1.464).  This algorithm is
- * only particularly useful on VERY large inputs
- * (we're talking 1000s of digits here...).
-*/
-int mp_toom_mul(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2;
-   int res, B;
-
-   /* init temps */
-   if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4,
-                            &a0, &a1, &a2, &b0, &b1,
-                            &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) {
-      return res;
-   }
-
-   /* B */
-   B = MIN(a->used, b->used) / 3;
-
-   /* a = a2 * B**2 + a1 * B + a0 */
-   if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_copy(a, &a1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_rshd(&a1, B);
-   if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_copy(a, &a2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_rshd(&a2, B*2);
-
-   /* b = b2 * B**2 + b1 * B + b0 */
-   if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_copy(b, &b1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_rshd(&b1, B);
-   (void)mp_mod_2d(&b1, DIGIT_BIT * B, &b1);
-
-   if ((res = mp_copy(b, &b2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_rshd(&b2, B*2);
-
-   /* w0 = a0*b0 */
-   if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* w4 = a2 * b2 */
-   if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */
-   if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */
-   if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-
-   /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */
-   if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* now solve the matrix
-
-      0  0  0  0  1
-      1  2  4  8  16
-      1  1  1  1  1
-      16 8  4  2  1
-      1  0  0  0  0
-
-      using 12 subtractions, 4 shifts,
-             2 small divisions and 1 small multiplication
-    */
-
-   /* r1 - r4 */
-   if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - r0 */
-   if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1/2 */
-   if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3/2 */
-   if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r2 - r0 - r4 */
-   if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1 - r2 */
-   if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - r2 */
-   if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1 - 8r0 */
-   if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - 8r4 */
-   if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* 3r2 - r1 - r3 */
-   if ((res = mp_mul_d(&w2, 3uL, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1 - r2 */
-   if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - r2 */
-   if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1/3 */
-   if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3/3 */
-   if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* at this point shift W[n] by B*n */
-   if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-LBL_ERR:
-   mp_clear_multi(&w0, &w1, &w2, &w3, &w4,
-                  &a0, &a1, &a2, &b0, &b1,
-                  &b2, &tmp1, &tmp2, NULL);
-   return res;
-}
-
-/* End: bn_mp_toom_mul.c */
-
-/* Start: bn_mp_toom_sqr.c */
-
-/* squaring using Toom-Cook 3-way algorithm */
-int mp_toom_sqr(const mp_int *a, mp_int *b)
-{
-   mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2;
-   int res, B;
-
-   /* init temps */
-   if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) {
-      return res;
-   }
-
-   /* B */
-   B = a->used / 3;
-
-   /* a = a2 * B**2 + a1 * B + a0 */
-   if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_copy(a, &a1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_rshd(&a1, B);
-   if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_copy(a, &a2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   mp_rshd(&a2, B*2);
-
-   /* w0 = a0*a0 */
-   if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* w4 = a2 * a2 */
-   if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* w1 = (a2 + 2(a1 + 2a0))**2 */
-   if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* w3 = (a0 + 2(a1 + 2a2))**2 */
-   if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-
-   /* w2 = (a2 + a1 + a0)**2 */
-   if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* now solve the matrix
-
-      0  0  0  0  1
-      1  2  4  8  16
-      1  1  1  1  1
-      16 8  4  2  1
-      1  0  0  0  0
-
-      using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication.
-    */
-
-   /* r1 - r4 */
-   if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - r0 */
-   if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1/2 */
-   if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3/2 */
-   if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r2 - r0 - r4 */
-   if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1 - r2 */
-   if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - r2 */
-   if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1 - 8r0 */
-   if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - 8r4 */
-   if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* 3r2 - r1 - r3 */
-   if ((res = mp_mul_d(&w2, 3uL, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1 - r2 */
-   if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3 - r2 */
-   if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r1/3 */
-   if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   /* r3/3 */
-   if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   /* at this point shift W[n] by B*n */
-   if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-   if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-   if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
-
-LBL_ERR:
-   mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL);
-   return res;
-}
-
-/* End: bn_mp_toom_sqr.c */
-
-/* Start: bn_mp_toradix.c */
-
-/* stores a bignum as a ASCII string in a given radix (2..64) */
-int mp_toradix(const mp_int *a, char *str, int radix)
-{
-   int     res, digs;
-   mp_int  t;
-   mp_digit d;
-   char   *_s = str;
-
-   /* check range of the radix */
-   if ((radix < 2) || (radix > 64)) {
-      return MP_VAL;
-   }
-
-   /* quick out if its zero */
-   if (mp_iszero(a) == MP_YES) {
-      *str++ = '0';
-      *str = '\0';
-      return MP_OKAY;
-   }
-
-   if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-      return res;
-   }
-
-   /* if it is negative output a - */
-   if (t.sign == MP_NEG) {
-      ++_s;
-      *str++ = '-';
-      t.sign = MP_ZPOS;
-   }
-
-   digs = 0;
-   while (mp_iszero(&t) == MP_NO) {
-      if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) {
-         mp_clear(&t);
-         return res;
-      }
-      *str++ = mp_s_rmap[d];
-      ++digs;
-   }
-
-   /* reverse the digits of the string.  In this case _s points
-    * to the first digit [exluding the sign] of the number]
-    */
-   bn_reverse((unsigned char *)_s, digs);
-
-   /* append a NULL so the string is properly terminated */
-   *str = '\0';
-
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_toradix.c */
-
-/* Start: bn_mp_toradix_n.c */
-
-/* stores a bignum as a ASCII string in a given radix (2..64)
- *
- * Stores upto maxlen-1 chars and always a NULL byte
- */
-int mp_toradix_n(const mp_int *a, char *str, int radix, int maxlen)
-{
-   int     res, digs;
-   mp_int  t;
-   mp_digit d;
-   char   *_s = str;
-
-   /* check range of the maxlen, radix */
-   if ((maxlen < 2) || (radix < 2) || (radix > 64)) {
-      return MP_VAL;
-   }
-
-   /* quick out if its zero */
-   if (mp_iszero(a) == MP_YES) {
-      *str++ = '0';
-      *str = '\0';
-      return MP_OKAY;
-   }
-
-   if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-      return res;
-   }
-
-   /* if it is negative output a - */
-   if (t.sign == MP_NEG) {
-      /* we have to reverse our digits later... but not the - sign!! */
-      ++_s;
-
-      /* store the flag and mark the number as positive */
-      *str++ = '-';
-      t.sign = MP_ZPOS;
-
-      /* subtract a char */
-      --maxlen;
-   }
-
-   digs = 0;
-   while (mp_iszero(&t) == MP_NO) {
-      if (--maxlen < 1) {
-         /* no more room */
-         break;
-      }
-      if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) {
-         mp_clear(&t);
-         return res;
-      }
-      *str++ = mp_s_rmap[d];
-      ++digs;
-   }
-
-   /* reverse the digits of the string.  In this case _s points
-    * to the first digit [exluding the sign] of the number
-    */
-   bn_reverse((unsigned char *)_s, digs);
-
-   /* append a NULL so the string is properly terminated */
-   *str = '\0';
-
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_toradix_n.c */
-
-/* Start: bn_mp_unsigned_bin_size.c */
-
-/* get the size for an unsigned equivalent */
-int mp_unsigned_bin_size(const mp_int *a)
-{
-   int     size = mp_count_bits(a);
-   return (size / 8) + ((((unsigned)size & 7u) != 0u) ? 1 : 0);
-}
-
-/* End: bn_mp_unsigned_bin_size.c */
-
-/* Start: bn_mp_xor.c */
-
-/* XOR two ints together */
-int mp_xor(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     res, ix, px;
-   mp_int  t;
-   const mp_int *x;
-
-   if (a->used > b->used) {
-      if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
-         return res;
-      }
-      px = b->used;
-      x = b;
-   } else {
-      if ((res = mp_init_copy(&t, b)) != MP_OKAY) {
-         return res;
-      }
-      px = a->used;
-      x = a;
-   }
-
-   for (ix = 0; ix < px; ix++) {
-      t.dp[ix] ^= x->dp[ix];
-   }
-   mp_clamp(&t);
-   mp_exch(c, &t);
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_mp_xor.c */
-
-/* Start: bn_mp_zero.c */
-
-/* set to zero */
-void mp_zero(mp_int *a)
-{
-   int       n;
-   mp_digit *tmp;
-
-   a->sign = MP_ZPOS;
-   a->used = 0;
-
-   tmp = a->dp;
-   for (n = 0; n < a->alloc; n++) {
-      *tmp++ = 0;
-   }
-}
-
-/* End: bn_mp_zero.c */
-
-/* Start: bn_prime_tab.c */
-
-const mp_digit ltm_prime_tab[] = {
-   0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013,
-   0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035,
-   0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059,
-   0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F,
-#ifndef MP_8BIT
-   0x0083,
-   0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD,
-   0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF,
-   0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107,
-   0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137,
-
-   0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167,
-   0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199,
-   0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9,
-   0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7,
-   0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239,
-   0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265,
-   0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293,
-   0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF,
-
-   0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301,
-   0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B,
-   0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371,
-   0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD,
-   0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5,
-   0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419,
-   0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449,
-   0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B,
-
-   0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7,
-   0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503,
-   0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529,
-   0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F,
-   0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3,
-   0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7,
-   0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623,
-   0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653
-#endif
-};
-
-/* End: bn_prime_tab.c */
-
-/* Start: bn_reverse.c */
-
-/* reverse an array, used for radix code */
-void bn_reverse(unsigned char *s, int len)
-{
-   int     ix, iy;
-   unsigned char t;
-
-   ix = 0;
-   iy = len - 1;
-   while (ix < iy) {
-      t     = s[ix];
-      s[ix] = s[iy];
-      s[iy] = t;
-      ++ix;
-      --iy;
-   }
-}
-
-/* End: bn_reverse.c */
-
-/* Start: bn_s_mp_add.c */
-
-/* low level addition, based on HAC pp.594, Algorithm 14.7 */
-int s_mp_add(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   const mp_int *x;
-   int     olduse, res, min, max;
-
-   /* find sizes, we let |a| <= |b| which means we have to sort
-    * them.  "x" will point to the input with the most digits
-    */
-   if (a->used > b->used) {
-      min = b->used;
-      max = a->used;
-      x = a;
-   } else {
-      min = a->used;
-      max = b->used;
-      x = b;
-   }
-
-   /* init result */
-   if (c->alloc < (max + 1)) {
-      if ((res = mp_grow(c, max + 1)) != MP_OKAY) {
-         return res;
-      }
-   }
-
-   /* get old used digit count and set new one */
-   olduse = c->used;
-   c->used = max + 1;
-
-   {
-      mp_digit u, *tmpa, *tmpb, *tmpc;
-      int i;
-
-      /* alias for digit pointers */
-
-      /* first input */
-      tmpa = a->dp;
-
-      /* second input */
-      tmpb = b->dp;
-
-      /* destination */
-      tmpc = c->dp;
-
-      /* zero the carry */
-      u = 0;
-      for (i = 0; i < min; i++) {
-         /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
-         *tmpc = *tmpa++ + *tmpb++ + u;
-
-         /* U = carry bit of T[i] */
-         u = *tmpc >> (mp_digit)DIGIT_BIT;
-
-         /* take away carry bit from T[i] */
-         *tmpc++ &= MP_MASK;
-      }
-
-      /* now copy higher words if any, that is in A+B
-       * if A or B has more digits add those in
-       */
-      if (min != max) {
-         for (; i < max; i++) {
-            /* T[i] = X[i] + U */
-            *tmpc = x->dp[i] + u;
-
-            /* U = carry bit of T[i] */
-            u = *tmpc >> (mp_digit)DIGIT_BIT;
-
-            /* take away carry bit from T[i] */
-            *tmpc++ &= MP_MASK;
-         }
-      }
-
-      /* add carry */
-      *tmpc++ = u;
-
-      /* clear digits above oldused */
-      for (i = c->used; i < olduse; i++) {
-         *tmpc++ = 0;
-      }
-   }
-
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_s_mp_add.c */
-
-/* Start: bn_s_mp_exptmod.c */
-
-#ifdef MP_LOW_MEM
-#   define TAB_SIZE 32
-#else
-#   define TAB_SIZE 256
-#endif
-
-int s_mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode)
-{
-   mp_int  M[TAB_SIZE], res, mu;
-   mp_digit buf;
-   int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
-   int (*redux)(mp_int *x, const mp_int *m, const mp_int *mu);
-
-   /* find window size */
-   x = mp_count_bits(X);
-   if (x <= 7) {
-      winsize = 2;
-   } else if (x <= 36) {
-      winsize = 3;
-   } else if (x <= 140) {
-      winsize = 4;
-   } else if (x <= 450) {
-      winsize = 5;
-   } else if (x <= 1303) {
-      winsize = 6;
-   } else if (x <= 3529) {
-      winsize = 7;
-   } else {
-      winsize = 8;
-   }
-
-#ifdef MP_LOW_MEM
-   if (winsize > 5) {
-      winsize = 5;
-   }
-#endif
-
-   /* init M array */
-   /* init first cell */
-   if ((err = mp_init(&M[1])) != MP_OKAY) {
-      return err;
-   }
-
-   /* now init the second half of the array */
-   for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
-      if ((err = mp_init(&M[x])) != MP_OKAY) {
-         for (y = 1<<(winsize-1); y < x; y++) {
-            mp_clear(&M[y]);
-         }
-         mp_clear(&M[1]);
-         return err;
-      }
-   }
-
-   /* create mu, used for Barrett reduction */
-   if ((err = mp_init(&mu)) != MP_OKAY) {
-      goto LBL_M;
-   }
-
-   if (redmode == 0) {
-      if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) {
-         goto LBL_MU;
-      }
-      redux = mp_reduce;
-   } else {
-      if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) {
-         goto LBL_MU;
-      }
-      redux = mp_reduce_2k_l;
-   }
-
-   /* create M table
-    *
-    * The M table contains powers of the base,
-    * e.g. M[x] = G**x mod P
-    *
-    * The first half of the table is not
-    * computed though accept for M[0] and M[1]
-    */
-   if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
-      goto LBL_MU;
-   }
-
-   /* compute the value at M[1<<(winsize-1)] by squaring
-    * M[1] (winsize-1) times
-    */
-   if ((err = mp_copy(&M[1], &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) {
-      goto LBL_MU;
-   }
-
-   for (x = 0; x < (winsize - 1); x++) {
-      /* square it */
-      if ((err = mp_sqr(&M[(size_t)1 << (winsize - 1)],
-                        &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) {
-         goto LBL_MU;
-      }
-
-      /* reduce modulo P */
-      if ((err = redux(&M[(size_t)1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
-         goto LBL_MU;
-      }
-   }
-
-   /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
-    * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
-    */
-   for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
-      if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
-         goto LBL_MU;
-      }
-      if ((err = redux(&M[x], P, &mu)) != MP_OKAY) {
-         goto LBL_MU;
-      }
-   }
-
-   /* setup result */
-   if ((err = mp_init(&res)) != MP_OKAY) {
-      goto LBL_MU;
-   }
-   mp_set(&res, 1uL);
-
-   /* set initial mode and bit cnt */
-   mode   = 0;
-   bitcnt = 1;
-   buf    = 0;
-   digidx = X->used - 1;
-   bitcpy = 0;
-   bitbuf = 0;
-
-   for (;;) {
-      /* grab next digit as required */
-      if (--bitcnt == 0) {
-         /* if digidx == -1 we are out of digits */
-         if (digidx == -1) {
-            break;
-         }
-         /* read next digit and reset the bitcnt */
-         buf    = X->dp[digidx--];
-         bitcnt = (int)DIGIT_BIT;
-      }
-
-      /* grab the next msb from the exponent */
-      y     = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
-      buf <<= (mp_digit)1;
-
-      /* if the bit is zero and mode == 0 then we ignore it
-       * These represent the leading zero bits before the first 1 bit
-       * in the exponent.  Technically this opt is not required but it
-       * does lower the # of trivial squaring/reductions used
-       */
-      if ((mode == 0) && (y == 0)) {
-         continue;
-      }
-
-      /* if the bit is zero and mode == 1 then we square */
-      if ((mode == 1) && (y == 0)) {
-         if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         if ((err = redux(&res, P, &mu)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         continue;
-      }
-
-      /* else we add it to the window */
-      bitbuf |= (y << (winsize - ++bitcpy));
-      mode    = 2;
-
-      if (bitcpy == winsize) {
-         /* ok window is filled so square as required and multiply  */
-         /* square first */
-         for (x = 0; x < winsize; x++) {
-            if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-            if ((err = redux(&res, P, &mu)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-         }
-
-         /* then multiply */
-         if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         if ((err = redux(&res, P, &mu)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-
-         /* empty window and reset */
-         bitcpy = 0;
-         bitbuf = 0;
-         mode   = 1;
-      }
-   }
-
-   /* if bits remain then square/multiply */
-   if ((mode == 2) && (bitcpy > 0)) {
-      /* square then multiply if the bit is set */
-      for (x = 0; x < bitcpy; x++) {
-         if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-         if ((err = redux(&res, P, &mu)) != MP_OKAY) {
-            goto LBL_RES;
-         }
-
-         bitbuf <<= 1;
-         if ((bitbuf & (1 << winsize)) != 0) {
-            /* then multiply */
-            if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-            if ((err = redux(&res, P, &mu)) != MP_OKAY) {
-               goto LBL_RES;
-            }
-         }
-      }
-   }
-
-   mp_exch(&res, Y);
-   err = MP_OKAY;
-LBL_RES:
-   mp_clear(&res);
-LBL_MU:
-   mp_clear(&mu);
-LBL_M:
-   mp_clear(&M[1]);
-   for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
-      mp_clear(&M[x]);
-   }
-   return err;
-}
-
-/* End: bn_s_mp_exptmod.c */
-
-/* Start: bn_s_mp_mul_digs.c */
-
-/* multiplies |a| * |b| and only computes upto digs digits of result
- * HAC pp. 595, Algorithm 14.12  Modified so you can control how
- * many digits of output are created.
- */
-int s_mp_mul_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs)
-{
-   mp_int  t;
-   int     res, pa, pb, ix, iy;
-   mp_digit u;
-   mp_word r;
-   mp_digit tmpx, *tmpt, *tmpy;
-
-   /* can we use the fast multiplier? */
-   if ((digs < (int)MP_WARRAY) &&
-       (MIN(a->used, b->used) <
-        (int)(1u << (((size_t)CHAR_BIT * sizeof(mp_word)) - (2u * (size_t)DIGIT_BIT))))) {
-      return fast_s_mp_mul_digs(a, b, c, digs);
-   }
-
-   if ((res = mp_init_size(&t, digs)) != MP_OKAY) {
-      return res;
-   }
-   t.used = digs;
-
-   /* compute the digits of the product directly */
-   pa = a->used;
-   for (ix = 0; ix < pa; ix++) {
-      /* set the carry to zero */
-      u = 0;
-
-      /* limit ourselves to making digs digits of output */
-      pb = MIN(b->used, digs - ix);
-
-      /* setup some aliases */
-      /* copy of the digit from a used within the nested loop */
-      tmpx = a->dp[ix];
-
-      /* an alias for the destination shifted ix places */
-      tmpt = t.dp + ix;
-
-      /* an alias for the digits of b */
-      tmpy = b->dp;
-
-      /* compute the columns of the output and propagate the carry */
-      for (iy = 0; iy < pb; iy++) {
-         /* compute the column as a mp_word */
-         r       = (mp_word)*tmpt +
-                   ((mp_word)tmpx * (mp_word)*tmpy++) +
-                   (mp_word)u;
-
-         /* the new column is the lower part of the result */
-         *tmpt++ = (mp_digit)(r & (mp_word)MP_MASK);
-
-         /* get the carry word from the result */
-         u       = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-      }
-      /* set carry if it is placed below digs */
-      if ((ix + iy) < digs) {
-         *tmpt = u;
-      }
-   }
-
-   mp_clamp(&t);
-   mp_exch(&t, c);
-
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_s_mp_mul_digs.c */
-
-/* Start: bn_s_mp_mul_high_digs.c */
-
-/* multiplies |a| * |b| and does not compute the lower digs digits
- * [meant to get the higher part of the product]
- */
-int s_mp_mul_high_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs)
-{
-   mp_int  t;
-   int     res, pa, pb, ix, iy;
-   mp_digit u;
-   mp_word r;
-   mp_digit tmpx, *tmpt, *tmpy;
-
-   /* can we use the fast multiplier? */
-   if (((a->used + b->used + 1) < (int)MP_WARRAY)
-       && (MIN(a->used, b->used) < (int)(1u << (((size_t)CHAR_BIT * sizeof(mp_word)) - (2u * (size_t)DIGIT_BIT))))) {
-      return fast_s_mp_mul_high_digs(a, b, c, digs);
-   }
-
-   if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) {
-      return res;
-   }
-   t.used = a->used + b->used + 1;
-
-   pa = a->used;
-   pb = b->used;
-   for (ix = 0; ix < pa; ix++) {
-      /* clear the carry */
-      u = 0;
-
-      /* left hand side of A[ix] * B[iy] */
-      tmpx = a->dp[ix];
-
-      /* alias to the address of where the digits will be stored */
-      tmpt = &(t.dp[digs]);
-
-      /* alias for where to read the right hand side from */
-      tmpy = b->dp + (digs - ix);
-
-      for (iy = digs - ix; iy < pb; iy++) {
-         /* calculate the double precision result */
-         r       = (mp_word)*tmpt +
-                   ((mp_word)tmpx * (mp_word)*tmpy++) +
-                   (mp_word)u;
-
-         /* get the lower part */
-         *tmpt++ = (mp_digit)(r & (mp_word)MP_MASK);
-
-         /* carry the carry */
-         u       = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-      }
-      *tmpt = u;
-   }
-   mp_clamp(&t);
-   mp_exch(&t, c);
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_s_mp_mul_high_digs.c */
-
-/* Start: bn_s_mp_sqr.c */
-
-/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
-int s_mp_sqr(const mp_int *a, mp_int *b)
-{
-   mp_int  t;
-   int     res, ix, iy, pa;
-   mp_word r;
-   mp_digit u, tmpx, *tmpt;
-
-   pa = a->used;
-   if ((res = mp_init_size(&t, (2 * pa) + 1)) != MP_OKAY) {
-      return res;
-   }
-
-   /* default used is maximum possible size */
-   t.used = (2 * pa) + 1;
-
-   for (ix = 0; ix < pa; ix++) {
-      /* first calculate the digit at 2*ix */
-      /* calculate double precision result */
-      r = (mp_word)t.dp[2*ix] +
-          ((mp_word)a->dp[ix] * (mp_word)a->dp[ix]);
-
-      /* store lower part in result */
-      t.dp[ix+ix] = (mp_digit)(r & (mp_word)MP_MASK);
-
-      /* get the carry */
-      u           = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-
-      /* left hand side of A[ix] * A[iy] */
-      tmpx        = a->dp[ix];
-
-      /* alias for where to store the results */
-      tmpt        = t.dp + ((2 * ix) + 1);
-
-      for (iy = ix + 1; iy < pa; iy++) {
-         /* first calculate the product */
-         r       = (mp_word)tmpx * (mp_word)a->dp[iy];
-
-         /* now calculate the double precision result, note we use
-          * addition instead of *2 since it's easier to optimize
-          */
-         r       = (mp_word)*tmpt + r + r + (mp_word)u;
-
-         /* store lower part */
-         *tmpt++ = (mp_digit)(r & (mp_word)MP_MASK);
-
-         /* get carry */
-         u       = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-      }
-      /* propagate upwards */
-      while (u != 0uL) {
-         r       = (mp_word)*tmpt + (mp_word)u;
-         *tmpt++ = (mp_digit)(r & (mp_word)MP_MASK);
-         u       = (mp_digit)(r >> (mp_word)DIGIT_BIT);
-      }
-   }
-
-   mp_clamp(&t);
-   mp_exch(&t, b);
-   mp_clear(&t);
-   return MP_OKAY;
-}
-
-/* End: bn_s_mp_sqr.c */
-
-/* Start: bn_s_mp_sub.c */
-
-/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
-int s_mp_sub(const mp_int *a, const mp_int *b, mp_int *c)
-{
-   int     olduse, res, min, max;
-
-   /* find sizes */
-   min = b->used;
-   max = a->used;
-
-   /* init result */
-   if (c->alloc < max) {
-      if ((res = mp_grow(c, max)) != MP_OKAY) {
-         return res;
-      }
-   }
-   olduse = c->used;
-   c->used = max;
-
-   {
-      mp_digit u, *tmpa, *tmpb, *tmpc;
-      int i;
-
-      /* alias for digit pointers */
-      tmpa = a->dp;
-      tmpb = b->dp;
-      tmpc = c->dp;
-
-      /* set carry to zero */
-      u = 0;
-      for (i = 0; i < min; i++) {
-         /* T[i] = A[i] - B[i] - U */
-         *tmpc = (*tmpa++ - *tmpb++) - u;
-
-         /* U = carry bit of T[i]
-          * Note this saves performing an AND operation since
-          * if a carry does occur it will propagate all the way to the
-          * MSB.  As a result a single shift is enough to get the carry
-          */
-         u = *tmpc >> (((size_t)CHAR_BIT * sizeof(mp_digit)) - 1u);
-
-         /* Clear carry from T[i] */
-         *tmpc++ &= MP_MASK;
-      }
-
-      /* now copy higher words if any, e.g. if A has more digits than B  */
-      for (; i < max; i++) {
-         /* T[i] = A[i] - U */
-         *tmpc = *tmpa++ - u;
-
-         /* U = carry bit of T[i] */
-         u = *tmpc >> (((size_t)CHAR_BIT * sizeof(mp_digit)) - 1u);
-
-         /* Clear carry from T[i] */
-         *tmpc++ &= MP_MASK;
-      }
-
-      /* clear digits above used (since we may not have grown result above) */
-      for (i = c->used; i < olduse; i++) {
-         *tmpc++ = 0;
-      }
-   }
-
-   mp_clamp(c);
-   return MP_OKAY;
-}
-
-/* End: bn_s_mp_sub.c */
-
-/* Start: bncore.c */
-
-/* Known optimal configurations
-
- CPU                    /Compiler     /MUL CUTOFF/SQR CUTOFF
--------------------------------------------------------------
- Intel P4 Northwood     /GCC v3.4.1   /        88/       128/LTM 0.32 ;-)
- AMD Athlon64           /GCC v3.4.4   /        80/       120/LTM 0.35
-
-*/
-
-int     KARATSUBA_MUL_CUTOFF = 80,      /* Min. number of digits before Karatsuba multiplication is used. */
-        KARATSUBA_SQR_CUTOFF = 120,     /* Min. number of digits before Karatsuba squaring is used. */
-
-        TOOM_MUL_CUTOFF      = 350,      /* no optimal values of these are known yet so set em high */
-        TOOM_SQR_CUTOFF      = 400;
-
-/* End: bncore.c */
diff --git a/libs/tomcrypt/src/ciphers/aes/aes.c b/libs/tomcrypt/src/ciphers/aes/aes.c
deleted file mode 100644
index e55966ff2821..000000000000
--- a/libs/tomcrypt/src/ciphers/aes/aes.c
+++ /dev/null
@@ -1,738 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* AES implementation by Tom St Denis
- *
- * Derived from the Public Domain source code by
-
----
-  * rijndael-alg-fst.c
-  *
-  * @version 3.0 (December 2000)
-  *
-  * Optimised ANSI C code for the Rijndael cipher (now AES)
-  *
-  * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
-  * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
-  * @author Paulo Barreto <paulo.barreto@terra.com.br>
----
- */
-/**
-  @file aes.c
-  Implementation of AES
-*/
-
-#include "tomcrypt.h"
-
-#ifdef LTC_RIJNDAEL
-
-#ifndef ENCRYPT_ONLY
-
-#define SETUP    rijndael_setup
-#define ECB_ENC  rijndael_ecb_encrypt
-#define ECB_DEC  rijndael_ecb_decrypt
-#define ECB_DONE rijndael_done
-#define ECB_TEST rijndael_test
-#define ECB_KS   rijndael_keysize
-
-const struct ltc_cipher_descriptor rijndael_desc =
-{
-    "rijndael",
-    6,
-    16, 32, 16, 10,
-    SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-const struct ltc_cipher_descriptor aes_desc =
-{
-    "aes",
-    6,
-    16, 32, 16, 10,
-    SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-#else
-
-#define SETUP    rijndael_enc_setup
-#define ECB_ENC  rijndael_enc_ecb_encrypt
-#define ECB_KS   rijndael_enc_keysize
-#define ECB_DONE rijndael_enc_done
-
-const struct ltc_cipher_descriptor rijndael_enc_desc =
-{
-    "rijndael",
-    6,
-    16, 32, 16, 10,
-    SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-const struct ltc_cipher_descriptor aes_enc_desc =
-{
-    "aes",
-    6,
-    16, 32, 16, 10,
-    SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-#endif
-
-#define __LTC_AES_TAB_C__
-#include "aes_tab.c"
-
-static ulong32 setup_mix(ulong32 temp)
-{
-   return (Te4_3[byte(temp, 2)]) ^
-          (Te4_2[byte(temp, 1)]) ^
-          (Te4_1[byte(temp, 0)]) ^
-          (Te4_0[byte(temp, 3)]);
-}
-
-#ifndef ENCRYPT_ONLY
-#ifdef LTC_SMALL_CODE
-static ulong32 setup_mix2(ulong32 temp)
-{
-   return Td0(255 & Te4[byte(temp, 3)]) ^
-          Td1(255 & Te4[byte(temp, 2)]) ^
-          Td2(255 & Te4[byte(temp, 1)]) ^
-          Td3(255 & Te4[byte(temp, 0)]);
-}
-#endif
-#endif
-
- /**
-    Initialize the AES (Rijndael) block cipher
-    @param key The symmetric key you wish to pass
-    @param keylen The key length in bytes
-    @param num_rounds The number of rounds desired (0 for default)
-    @param skey The key in as scheduled by this function.
-    @return CRYPT_OK if successful
- */
-int SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
-{
-    int i;
-    ulong32 temp, *rk;
-#ifndef ENCRYPT_ONLY
-    ulong32 *rrk;
-#endif
-    LTC_ARGCHK(key  != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-    if (keylen != 16 && keylen != 24 && keylen != 32) {
-       return CRYPT_INVALID_KEYSIZE;
-    }
-
-    if (num_rounds != 0 && num_rounds != (10 + ((keylen/8)-2)*2)) {
-       return CRYPT_INVALID_ROUNDS;
-    }
-
-    skey->rijndael.Nr = 10 + ((keylen/8)-2)*2;
-
-    /* setup the forward key */
-    i                 = 0;
-    rk                = skey->rijndael.eK;
-    LOAD32H(rk[0], key     );
-    LOAD32H(rk[1], key +  4);
-    LOAD32H(rk[2], key +  8);
-    LOAD32H(rk[3], key + 12);
-    if (keylen == 16) {
-        for (;;) {
-            temp  = rk[3];
-            rk[4] = rk[0] ^ setup_mix(temp) ^ rcon[i];
-            rk[5] = rk[1] ^ rk[4];
-            rk[6] = rk[2] ^ rk[5];
-            rk[7] = rk[3] ^ rk[6];
-            if (++i == 10) {
-               break;
-            }
-            rk += 4;
-        }
-    } else if (keylen == 24) {
-        LOAD32H(rk[4], key + 16);
-        LOAD32H(rk[5], key + 20);
-        for (;;) {
-        #ifdef _MSC_VER
-            temp = skey->rijndael.eK[rk - skey->rijndael.eK + 5];
-        #else
-            temp = rk[5];
-        #endif
-            rk[ 6] = rk[ 0] ^ setup_mix(temp) ^ rcon[i];
-            rk[ 7] = rk[ 1] ^ rk[ 6];
-            rk[ 8] = rk[ 2] ^ rk[ 7];
-            rk[ 9] = rk[ 3] ^ rk[ 8];
-            if (++i == 8) {
-                break;
-            }
-            rk[10] = rk[ 4] ^ rk[ 9];
-            rk[11] = rk[ 5] ^ rk[10];
-            rk += 6;
-        }
-    } else if (keylen == 32) {
-        LOAD32H(rk[4], key + 16);
-        LOAD32H(rk[5], key + 20);
-        LOAD32H(rk[6], key + 24);
-        LOAD32H(rk[7], key + 28);
-        for (;;) {
-        #ifdef _MSC_VER
-            temp = skey->rijndael.eK[rk - skey->rijndael.eK + 7];
-        #else
-            temp = rk[7];
-        #endif
-            rk[ 8] = rk[ 0] ^ setup_mix(temp) ^ rcon[i];
-            rk[ 9] = rk[ 1] ^ rk[ 8];
-            rk[10] = rk[ 2] ^ rk[ 9];
-            rk[11] = rk[ 3] ^ rk[10];
-            if (++i == 7) {
-                break;
-            }
-            temp = rk[11];
-            rk[12] = rk[ 4] ^ setup_mix(RORc(temp, 8));
-            rk[13] = rk[ 5] ^ rk[12];
-            rk[14] = rk[ 6] ^ rk[13];
-            rk[15] = rk[ 7] ^ rk[14];
-            rk += 8;
-        }
-    } else {
-       /* this can't happen */
-       /* coverity[dead_error_line] */
-       return CRYPT_ERROR;
-    }
-
-#ifndef ENCRYPT_ONLY
-    /* setup the inverse key now */
-    rk   = skey->rijndael.dK;
-    rrk  = skey->rijndael.eK + (28 + keylen) - 4;
-
-    /* apply the inverse MixColumn transform to all round keys but the first and the last: */
-    /* copy first */
-    *rk++ = *rrk++;
-    *rk++ = *rrk++;
-    *rk++ = *rrk++;
-    *rk   = *rrk;
-    rk -= 3; rrk -= 3;
-
-    for (i = 1; i < skey->rijndael.Nr; i++) {
-        rrk -= 4;
-        rk  += 4;
-    #ifdef LTC_SMALL_CODE
-        temp = rrk[0];
-        rk[0] = setup_mix2(temp);
-        temp = rrk[1];
-        rk[1] = setup_mix2(temp);
-        temp = rrk[2];
-        rk[2] = setup_mix2(temp);
-        temp = rrk[3];
-        rk[3] = setup_mix2(temp);
-     #else
-        temp = rrk[0];
-        rk[0] =
-            Tks0[byte(temp, 3)] ^
-            Tks1[byte(temp, 2)] ^
-            Tks2[byte(temp, 1)] ^
-            Tks3[byte(temp, 0)];
-        temp = rrk[1];
-        rk[1] =
-            Tks0[byte(temp, 3)] ^
-            Tks1[byte(temp, 2)] ^
-            Tks2[byte(temp, 1)] ^
-            Tks3[byte(temp, 0)];
-        temp = rrk[2];
-        rk[2] =
-            Tks0[byte(temp, 3)] ^
-            Tks1[byte(temp, 2)] ^
-            Tks2[byte(temp, 1)] ^
-            Tks3[byte(temp, 0)];
-        temp = rrk[3];
-        rk[3] =
-            Tks0[byte(temp, 3)] ^
-            Tks1[byte(temp, 2)] ^
-            Tks2[byte(temp, 1)] ^
-            Tks3[byte(temp, 0)];
-      #endif
-
-    }
-
-    /* copy last */
-    rrk -= 4;
-    rk  += 4;
-    *rk++ = *rrk++;
-    *rk++ = *rrk++;
-    *rk++ = *rrk++;
-    *rk   = *rrk;
-#endif /* ENCRYPT_ONLY */
-
-    return CRYPT_OK;
-}
-
-/**
-  Encrypts a block of text with AES
-  @param pt The input plaintext (16 bytes)
-  @param ct The output ciphertext (16 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-#ifdef LTC_CLEAN_STACK
-static int _rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-#else
-int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-#endif
-{
-    ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk;
-    int Nr, r;
-
-    LTC_ARGCHK(pt != NULL);
-    LTC_ARGCHK(ct != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-    Nr = skey->rijndael.Nr;
-    rk = skey->rijndael.eK;
-
-    /*
-     * map byte array block to cipher state
-     * and add initial round key:
-     */
-    LOAD32H(s0, pt      ); s0 ^= rk[0];
-    LOAD32H(s1, pt  +  4); s1 ^= rk[1];
-    LOAD32H(s2, pt  +  8); s2 ^= rk[2];
-    LOAD32H(s3, pt  + 12); s3 ^= rk[3];
-
-#ifdef LTC_SMALL_CODE
-
-    for (r = 0; ; r++) {
-        rk += 4;
-        t0 =
-            Te0(byte(s0, 3)) ^
-            Te1(byte(s1, 2)) ^
-            Te2(byte(s2, 1)) ^
-            Te3(byte(s3, 0)) ^
-            rk[0];
-        t1 =
-            Te0(byte(s1, 3)) ^
-            Te1(byte(s2, 2)) ^
-            Te2(byte(s3, 1)) ^
-            Te3(byte(s0, 0)) ^
-            rk[1];
-        t2 =
-            Te0(byte(s2, 3)) ^
-            Te1(byte(s3, 2)) ^
-            Te2(byte(s0, 1)) ^
-            Te3(byte(s1, 0)) ^
-            rk[2];
-        t3 =
-            Te0(byte(s3, 3)) ^
-            Te1(byte(s0, 2)) ^
-            Te2(byte(s1, 1)) ^
-            Te3(byte(s2, 0)) ^
-            rk[3];
-        if (r == Nr-2) {
-           break;
-        }
-        s0 = t0; s1 = t1; s2 = t2; s3 = t3;
-    }
-    rk += 4;
-
-#else
-
-    /*
-     * Nr - 1 full rounds:
-     */
-    r = Nr >> 1;
-    for (;;) {
-        t0 =
-            Te0(byte(s0, 3)) ^
-            Te1(byte(s1, 2)) ^
-            Te2(byte(s2, 1)) ^
-            Te3(byte(s3, 0)) ^
-            rk[4];
-        t1 =
-            Te0(byte(s1, 3)) ^
-            Te1(byte(s2, 2)) ^
-            Te2(byte(s3, 1)) ^
-            Te3(byte(s0, 0)) ^
-            rk[5];
-        t2 =
-            Te0(byte(s2, 3)) ^
-            Te1(byte(s3, 2)) ^
-            Te2(byte(s0, 1)) ^
-            Te3(byte(s1, 0)) ^
-            rk[6];
-        t3 =
-            Te0(byte(s3, 3)) ^
-            Te1(byte(s0, 2)) ^
-            Te2(byte(s1, 1)) ^
-            Te3(byte(s2, 0)) ^
-            rk[7];
-
-        rk += 8;
-        if (--r == 0) {
-            break;
-        }
-
-        s0 =
-            Te0(byte(t0, 3)) ^
-            Te1(byte(t1, 2)) ^
-            Te2(byte(t2, 1)) ^
-            Te3(byte(t3, 0)) ^
-            rk[0];
-        s1 =
-            Te0(byte(t1, 3)) ^
-            Te1(byte(t2, 2)) ^
-            Te2(byte(t3, 1)) ^
-            Te3(byte(t0, 0)) ^
-            rk[1];
-        s2 =
-            Te0(byte(t2, 3)) ^
-            Te1(byte(t3, 2)) ^
-            Te2(byte(t0, 1)) ^
-            Te3(byte(t1, 0)) ^
-            rk[2];
-        s3 =
-            Te0(byte(t3, 3)) ^
-            Te1(byte(t0, 2)) ^
-            Te2(byte(t1, 1)) ^
-            Te3(byte(t2, 0)) ^
-            rk[3];
-    }
-
-#endif
-
-    /*
-     * apply last round and
-     * map cipher state to byte array block:
-     */
-    s0 =
-        (Te4_3[byte(t0, 3)]) ^
-        (Te4_2[byte(t1, 2)]) ^
-        (Te4_1[byte(t2, 1)]) ^
-        (Te4_0[byte(t3, 0)]) ^
-        rk[0];
-    STORE32H(s0, ct);
-    s1 =
-        (Te4_3[byte(t1, 3)]) ^
-        (Te4_2[byte(t2, 2)]) ^
-        (Te4_1[byte(t3, 1)]) ^
-        (Te4_0[byte(t0, 0)]) ^
-        rk[1];
-    STORE32H(s1, ct+4);
-    s2 =
-        (Te4_3[byte(t2, 3)]) ^
-        (Te4_2[byte(t3, 2)]) ^
-        (Te4_1[byte(t0, 1)]) ^
-        (Te4_0[byte(t1, 0)]) ^
-        rk[2];
-    STORE32H(s2, ct+8);
-    s3 =
-        (Te4_3[byte(t3, 3)]) ^
-        (Te4_2[byte(t0, 2)]) ^
-        (Te4_1[byte(t1, 1)]) ^
-        (Te4_0[byte(t2, 0)]) ^
-        rk[3];
-    STORE32H(s3, ct+12);
-
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-{
-   int err = _rijndael_ecb_encrypt(pt, ct, skey);
-   burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2);
-   return err;
-}
-#endif
-
-#ifndef ENCRYPT_ONLY
-
-/**
-  Decrypts a block of text with AES
-  @param ct The input ciphertext (16 bytes)
-  @param pt The output plaintext (16 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-#ifdef LTC_CLEAN_STACK
-static int _rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-#else
-int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-#endif
-{
-    ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk;
-    int Nr, r;
-
-    LTC_ARGCHK(pt != NULL);
-    LTC_ARGCHK(ct != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-    Nr = skey->rijndael.Nr;
-    rk = skey->rijndael.dK;
-
-    /*
-     * map byte array block to cipher state
-     * and add initial round key:
-     */
-    LOAD32H(s0, ct      ); s0 ^= rk[0];
-    LOAD32H(s1, ct  +  4); s1 ^= rk[1];
-    LOAD32H(s2, ct  +  8); s2 ^= rk[2];
-    LOAD32H(s3, ct  + 12); s3 ^= rk[3];
-
-#ifdef LTC_SMALL_CODE
-    for (r = 0; ; r++) {
-        rk += 4;
-        t0 =
-            Td0(byte(s0, 3)) ^
-            Td1(byte(s3, 2)) ^
-            Td2(byte(s2, 1)) ^
-            Td3(byte(s1, 0)) ^
-            rk[0];
-        t1 =
-            Td0(byte(s1, 3)) ^
-            Td1(byte(s0, 2)) ^
-            Td2(byte(s3, 1)) ^
-            Td3(byte(s2, 0)) ^
-            rk[1];
-        t2 =
-            Td0(byte(s2, 3)) ^
-            Td1(byte(s1, 2)) ^
-            Td2(byte(s0, 1)) ^
-            Td3(byte(s3, 0)) ^
-            rk[2];
-        t3 =
-            Td0(byte(s3, 3)) ^
-            Td1(byte(s2, 2)) ^
-            Td2(byte(s1, 1)) ^
-            Td3(byte(s0, 0)) ^
-            rk[3];
-        if (r == Nr-2) {
-           break;
-        }
-        s0 = t0; s1 = t1; s2 = t2; s3 = t3;
-    }
-    rk += 4;
-
-#else
-
-    /*
-     * Nr - 1 full rounds:
-     */
-    r = Nr >> 1;
-    for (;;) {
-
-        t0 =
-            Td0(byte(s0, 3)) ^
-            Td1(byte(s3, 2)) ^
-            Td2(byte(s2, 1)) ^
-            Td3(byte(s1, 0)) ^
-            rk[4];
-        t1 =
-            Td0(byte(s1, 3)) ^
-            Td1(byte(s0, 2)) ^
-            Td2(byte(s3, 1)) ^
-            Td3(byte(s2, 0)) ^
-            rk[5];
-        t2 =
-            Td0(byte(s2, 3)) ^
-            Td1(byte(s1, 2)) ^
-            Td2(byte(s0, 1)) ^
-            Td3(byte(s3, 0)) ^
-            rk[6];
-        t3 =
-            Td0(byte(s3, 3)) ^
-            Td1(byte(s2, 2)) ^
-            Td2(byte(s1, 1)) ^
-            Td3(byte(s0, 0)) ^
-            rk[7];
-
-        rk += 8;
-        if (--r == 0) {
-            break;
-        }
-
-
-        s0 =
-            Td0(byte(t0, 3)) ^
-            Td1(byte(t3, 2)) ^
-            Td2(byte(t2, 1)) ^
-            Td3(byte(t1, 0)) ^
-            rk[0];
-        s1 =
-            Td0(byte(t1, 3)) ^
-            Td1(byte(t0, 2)) ^
-            Td2(byte(t3, 1)) ^
-            Td3(byte(t2, 0)) ^
-            rk[1];
-        s2 =
-            Td0(byte(t2, 3)) ^
-            Td1(byte(t1, 2)) ^
-            Td2(byte(t0, 1)) ^
-            Td3(byte(t3, 0)) ^
-            rk[2];
-        s3 =
-            Td0(byte(t3, 3)) ^
-            Td1(byte(t2, 2)) ^
-            Td2(byte(t1, 1)) ^
-            Td3(byte(t0, 0)) ^
-            rk[3];
-    }
-#endif
-
-    /*
-     * apply last round and
-     * map cipher state to byte array block:
-     */
-    s0 =
-        (Td4[byte(t0, 3)] & 0xff000000) ^
-        (Td4[byte(t3, 2)] & 0x00ff0000) ^
-        (Td4[byte(t2, 1)] & 0x0000ff00) ^
-        (Td4[byte(t1, 0)] & 0x000000ff) ^
-        rk[0];
-    STORE32H(s0, pt);
-    s1 =
-        (Td4[byte(t1, 3)] & 0xff000000) ^
-        (Td4[byte(t0, 2)] & 0x00ff0000) ^
-        (Td4[byte(t3, 1)] & 0x0000ff00) ^
-        (Td4[byte(t2, 0)] & 0x000000ff) ^
-        rk[1];
-    STORE32H(s1, pt+4);
-    s2 =
-        (Td4[byte(t2, 3)] & 0xff000000) ^
-        (Td4[byte(t1, 2)] & 0x00ff0000) ^
-        (Td4[byte(t0, 1)] & 0x0000ff00) ^
-        (Td4[byte(t3, 0)] & 0x000000ff) ^
-        rk[2];
-    STORE32H(s2, pt+8);
-    s3 =
-        (Td4[byte(t3, 3)] & 0xff000000) ^
-        (Td4[byte(t2, 2)] & 0x00ff0000) ^
-        (Td4[byte(t1, 1)] & 0x0000ff00) ^
-        (Td4[byte(t0, 0)] & 0x000000ff) ^
-        rk[3];
-    STORE32H(s3, pt+12);
-
-    return CRYPT_OK;
-}
-
-
-#ifdef LTC_CLEAN_STACK
-int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-{
-   int err = _rijndael_ecb_decrypt(ct, pt, skey);
-   burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2);
-   return err;
-}
-#endif
-
-/**
-  Performs a self-test of the AES block cipher
-  @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
-*/
-int ECB_TEST(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
- int err;
- static const struct {
-     int keylen;
-     unsigned char key[32], pt[16], ct[16];
- } tests[] = {
-    { 16,
-      { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
-      { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
-      { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
-        0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a }
-    }, {
-      24,
-      { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
-      { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
-      { 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0,
-        0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91 }
-    }, {
-      32,
-      { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
-      { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
-      { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
-        0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 }
-    }
- };
-
-  symmetric_key key;
-  unsigned char tmp[2][16];
-  int i, y;
-
-  for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
-    zeromem(&key, sizeof(key));
-    if ((err = rijndael_setup(tests[i].key, tests[i].keylen, 0, &key)) != CRYPT_OK) {
-       return err;
-    }
-
-    rijndael_ecb_encrypt(tests[i].pt, tmp[0], &key);
-    rijndael_ecb_decrypt(tmp[0], tmp[1], &key);
-    if (compare_testvector(tmp[0], 16, tests[i].ct, 16, "AES Encrypt", i) ||
-          compare_testvector(tmp[1], 16, tests[i].pt, 16, "AES Decrypt", i)) {
-        return CRYPT_FAIL_TESTVECTOR;
-    }
-
-    /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */
-    for (y = 0; y < 16; y++) tmp[0][y] = 0;
-    for (y = 0; y < 1000; y++) rijndael_ecb_encrypt(tmp[0], tmp[0], &key);
-    for (y = 0; y < 1000; y++) rijndael_ecb_decrypt(tmp[0], tmp[0], &key);
-    for (y = 0; y < 16; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR;
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif /* ENCRYPT_ONLY */
-
-
-/** Terminate the context
-   @param skey    The scheduled key
-*/
-void ECB_DONE(symmetric_key *skey)
-{
-  LTC_UNUSED_PARAM(skey);
-}
-
-
-/**
-  Gets suitable key size
-  @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.
-  @return CRYPT_OK if the input key size is acceptable.
-*/
-int ECB_KS(int *keysize)
-{
-   LTC_ARGCHK(keysize != NULL);
-
-   if (*keysize < 16)
-      return CRYPT_INVALID_KEYSIZE;
-   if (*keysize < 24) {
-      *keysize = 16;
-      return CRYPT_OK;
-   } else if (*keysize < 32) {
-      *keysize = 24;
-      return CRYPT_OK;
-   } else {
-      *keysize = 32;
-      return CRYPT_OK;
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/ciphers/aes/aes_tab.c b/libs/tomcrypt/src/ciphers/aes/aes_tab.c
deleted file mode 100644
index df5ccb4a8f09..000000000000
--- a/libs/tomcrypt/src/ciphers/aes/aes_tab.c
+++ /dev/null
@@ -1,1028 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/* The precomputed tables for AES */
-/*
-Te0[x] = S [x].[02, 01, 01, 03];
-Te1[x] = S [x].[03, 02, 01, 01];
-Te2[x] = S [x].[01, 03, 02, 01];
-Te3[x] = S [x].[01, 01, 03, 02];
-Te4[x] = S [x].[01, 01, 01, 01];
-
-Td0[x] = Si[x].[0e, 09, 0d, 0b];
-Td1[x] = Si[x].[0b, 0e, 09, 0d];
-Td2[x] = Si[x].[0d, 0b, 0e, 09];
-Td3[x] = Si[x].[09, 0d, 0b, 0e];
-Td4[x] = Si[x].[01, 01, 01, 01];
-*/
-
-#ifdef __LTC_AES_TAB_C__
-
-/**
-  @file aes_tab.c
-  AES tables
-*/
-static const ulong32 TE0[256] = {
-    0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL,
-    0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL,
-    0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL,
-    0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL,
-    0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL,
-    0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL,
-    0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL,
-    0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL,
-    0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL,
-    0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL,
-    0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL,
-    0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL,
-    0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL,
-    0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL,
-    0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL,
-    0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL,
-    0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL,
-    0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL,
-    0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL,
-    0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL,
-    0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL,
-    0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL,
-    0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL,
-    0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL,
-    0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL,
-    0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL,
-    0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL,
-    0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL,
-    0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL,
-    0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL,
-    0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL,
-    0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL,
-    0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL,
-    0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL,
-    0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL,
-    0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL,
-    0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL,
-    0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL,
-    0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL,
-    0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL,
-    0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL,
-    0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL,
-    0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL,
-    0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL,
-    0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL,
-    0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL,
-    0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL,
-    0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL,
-    0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL,
-    0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL,
-    0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL,
-    0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL,
-    0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL,
-    0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL,
-    0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL,
-    0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL,
-    0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL,
-    0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL,
-    0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL,
-    0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL,
-    0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL,
-    0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL,
-    0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL,
-    0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL,
-};
-
-#if !defined(PELI_TAB) && defined(LTC_SMALL_CODE)
-static const ulong32 Te4[256] = {
-    0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL,
-    0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL,
-    0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL,
-    0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL,
-    0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL,
-    0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL,
-    0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL,
-    0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL,
-    0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL,
-    0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL,
-    0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL,
-    0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL,
-    0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL,
-    0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL,
-    0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL,
-    0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL,
-    0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL,
-    0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL,
-    0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL,
-    0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL,
-    0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL,
-    0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL,
-    0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL,
-    0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL,
-    0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL,
-    0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL,
-    0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL,
-    0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL,
-    0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL,
-    0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL,
-    0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL,
-    0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL,
-    0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL,
-    0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL,
-    0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL,
-    0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL,
-    0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL,
-    0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL,
-    0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL,
-    0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL,
-    0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL,
-    0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL,
-    0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL,
-    0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL,
-    0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL,
-    0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL,
-    0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL,
-    0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL,
-    0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL,
-    0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL,
-    0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL,
-    0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL,
-    0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL,
-    0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL,
-    0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL,
-    0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL,
-    0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL,
-    0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL,
-    0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL,
-    0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL,
-    0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL,
-    0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL,
-    0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL,
-    0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL,
-};
-#endif
-
-#ifndef ENCRYPT_ONLY
-
-static const ulong32 TD0[256] = {
-    0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL,
-    0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL,
-    0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL,
-    0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL,
-    0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL,
-    0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL,
-    0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL,
-    0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL,
-    0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL,
-    0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL,
-    0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL,
-    0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL,
-    0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL,
-    0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL,
-    0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL,
-    0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL,
-    0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL,
-    0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL,
-    0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL,
-    0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL,
-    0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL,
-    0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL,
-    0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL,
-    0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL,
-    0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL,
-    0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL,
-    0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL,
-    0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL,
-    0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL,
-    0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL,
-    0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL,
-    0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL,
-    0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL,
-    0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL,
-    0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL,
-    0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL,
-    0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL,
-    0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL,
-    0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL,
-    0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL,
-    0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL,
-    0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL,
-    0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL,
-    0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL,
-    0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL,
-    0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL,
-    0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL,
-    0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL,
-    0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL,
-    0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL,
-    0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL,
-    0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL,
-    0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL,
-    0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL,
-    0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL,
-    0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL,
-    0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL,
-    0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL,
-    0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL,
-    0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL,
-    0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL,
-    0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL,
-    0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL,
-    0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL,
-};
-
-static const ulong32 Td4[256] = {
-    0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL,
-    0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL,
-    0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL,
-    0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL,
-    0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL,
-    0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL,
-    0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL,
-    0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL,
-    0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL,
-    0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL,
-    0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL,
-    0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL,
-    0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL,
-    0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL,
-    0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL,
-    0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL,
-    0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL,
-    0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL,
-    0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL,
-    0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL,
-    0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL,
-    0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL,
-    0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL,
-    0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL,
-    0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL,
-    0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL,
-    0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL,
-    0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL,
-    0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL,
-    0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL,
-    0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL,
-    0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL,
-    0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL,
-    0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL,
-    0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL,
-    0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL,
-    0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL,
-    0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL,
-    0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL,
-    0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL,
-    0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL,
-    0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL,
-    0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL,
-    0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL,
-    0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL,
-    0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL,
-    0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL,
-    0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL,
-    0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL,
-    0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL,
-    0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL,
-    0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL,
-    0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL,
-    0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL,
-    0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL,
-    0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL,
-    0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL,
-    0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL,
-    0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL,
-    0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL,
-    0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL,
-    0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL,
-    0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL,
-    0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL,
-};
-
-#endif /* ENCRYPT_ONLY */
-
-#ifdef LTC_SMALL_CODE
-
-#define Te0(x) TE0[x]
-#define Te1(x) RORc(TE0[x], 8)
-#define Te2(x) RORc(TE0[x], 16)
-#define Te3(x) RORc(TE0[x], 24)
-
-#define Td0(x) TD0[x]
-#define Td1(x) RORc(TD0[x], 8)
-#define Td2(x) RORc(TD0[x], 16)
-#define Td3(x) RORc(TD0[x], 24)
-
-#define Te4_0 0x000000FF & Te4
-#define Te4_1 0x0000FF00 & Te4
-#define Te4_2 0x00FF0000 & Te4
-#define Te4_3 0xFF000000 & Te4
-
-#else
-
-#define Te0(x) TE0[x]
-#define Te1(x) TE1[x]
-#define Te2(x) TE2[x]
-#define Te3(x) TE3[x]
-
-#define Td0(x) TD0[x]
-#define Td1(x) TD1[x]
-#define Td2(x) TD2[x]
-#define Td3(x) TD3[x]
-
-static const ulong32 TE1[256] = {
-    0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL,
-    0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL,
-    0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL,
-    0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL,
-    0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL,
-    0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL,
-    0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL,
-    0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL,
-    0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL,
-    0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL,
-    0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL,
-    0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL,
-    0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL,
-    0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL,
-    0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL,
-    0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL,
-    0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL,
-    0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL,
-    0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL,
-    0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL,
-    0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL,
-    0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL,
-    0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL,
-    0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL,
-    0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL,
-    0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL,
-    0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL,
-    0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL,
-    0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL,
-    0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL,
-    0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL,
-    0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL,
-    0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL,
-    0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL,
-    0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL,
-    0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL,
-    0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL,
-    0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL,
-    0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL,
-    0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL,
-    0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL,
-    0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL,
-    0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL,
-    0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL,
-    0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL,
-    0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL,
-    0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL,
-    0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL,
-    0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL,
-    0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL,
-    0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL,
-    0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL,
-    0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL,
-    0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL,
-    0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL,
-    0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL,
-    0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL,
-    0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL,
-    0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL,
-    0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL,
-    0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL,
-    0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL,
-    0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL,
-    0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL,
-};
-static const ulong32 TE2[256] = {
-    0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL,
-    0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL,
-    0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL,
-    0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL,
-    0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL,
-    0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL,
-    0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL,
-    0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL,
-    0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL,
-    0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL,
-    0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL,
-    0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL,
-    0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL,
-    0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL,
-    0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL,
-    0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL,
-    0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL,
-    0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL,
-    0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL,
-    0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL,
-    0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL,
-    0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL,
-    0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL,
-    0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL,
-    0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL,
-    0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL,
-    0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL,
-    0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL,
-    0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL,
-    0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL,
-    0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL,
-    0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL,
-    0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL,
-    0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL,
-    0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL,
-    0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL,
-    0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL,
-    0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL,
-    0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL,
-    0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL,
-    0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL,
-    0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL,
-    0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL,
-    0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL,
-    0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL,
-    0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL,
-    0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL,
-    0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL,
-    0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL,
-    0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL,
-    0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL,
-    0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL,
-    0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL,
-    0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL,
-    0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL,
-    0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL,
-    0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL,
-    0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL,
-    0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL,
-    0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL,
-    0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL,
-    0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL,
-    0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL,
-    0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL,
-};
-static const ulong32 TE3[256] = {
-
-    0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL,
-    0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL,
-    0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL,
-    0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL,
-    0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL,
-    0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL,
-    0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL,
-    0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL,
-    0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL,
-    0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL,
-    0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL,
-    0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL,
-    0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL,
-    0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL,
-    0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL,
-    0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL,
-    0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL,
-    0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL,
-    0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL,
-    0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL,
-    0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL,
-    0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL,
-    0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL,
-    0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL,
-    0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL,
-    0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL,
-    0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL,
-    0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL,
-    0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL,
-    0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL,
-    0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL,
-    0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL,
-    0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL,
-    0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL,
-    0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL,
-    0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL,
-    0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL,
-    0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL,
-    0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL,
-    0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL,
-    0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL,
-    0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL,
-    0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL,
-    0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL,
-    0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL,
-    0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL,
-    0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL,
-    0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL,
-    0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL,
-    0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL,
-    0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL,
-    0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL,
-    0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL,
-    0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL,
-    0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL,
-    0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL,
-    0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL,
-    0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL,
-    0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL,
-    0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL,
-    0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL,
-    0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL,
-    0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL,
-    0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL,
-};
-
-#ifndef PELI_TAB
-static const ulong32 Te4_0[] = {
-0x00000063UL, 0x0000007cUL, 0x00000077UL, 0x0000007bUL, 0x000000f2UL, 0x0000006bUL, 0x0000006fUL, 0x000000c5UL,
-0x00000030UL, 0x00000001UL, 0x00000067UL, 0x0000002bUL, 0x000000feUL, 0x000000d7UL, 0x000000abUL, 0x00000076UL,
-0x000000caUL, 0x00000082UL, 0x000000c9UL, 0x0000007dUL, 0x000000faUL, 0x00000059UL, 0x00000047UL, 0x000000f0UL,
-0x000000adUL, 0x000000d4UL, 0x000000a2UL, 0x000000afUL, 0x0000009cUL, 0x000000a4UL, 0x00000072UL, 0x000000c0UL,
-0x000000b7UL, 0x000000fdUL, 0x00000093UL, 0x00000026UL, 0x00000036UL, 0x0000003fUL, 0x000000f7UL, 0x000000ccUL,
-0x00000034UL, 0x000000a5UL, 0x000000e5UL, 0x000000f1UL, 0x00000071UL, 0x000000d8UL, 0x00000031UL, 0x00000015UL,
-0x00000004UL, 0x000000c7UL, 0x00000023UL, 0x000000c3UL, 0x00000018UL, 0x00000096UL, 0x00000005UL, 0x0000009aUL,
-0x00000007UL, 0x00000012UL, 0x00000080UL, 0x000000e2UL, 0x000000ebUL, 0x00000027UL, 0x000000b2UL, 0x00000075UL,
-0x00000009UL, 0x00000083UL, 0x0000002cUL, 0x0000001aUL, 0x0000001bUL, 0x0000006eUL, 0x0000005aUL, 0x000000a0UL,
-0x00000052UL, 0x0000003bUL, 0x000000d6UL, 0x000000b3UL, 0x00000029UL, 0x000000e3UL, 0x0000002fUL, 0x00000084UL,
-0x00000053UL, 0x000000d1UL, 0x00000000UL, 0x000000edUL, 0x00000020UL, 0x000000fcUL, 0x000000b1UL, 0x0000005bUL,
-0x0000006aUL, 0x000000cbUL, 0x000000beUL, 0x00000039UL, 0x0000004aUL, 0x0000004cUL, 0x00000058UL, 0x000000cfUL,
-0x000000d0UL, 0x000000efUL, 0x000000aaUL, 0x000000fbUL, 0x00000043UL, 0x0000004dUL, 0x00000033UL, 0x00000085UL,
-0x00000045UL, 0x000000f9UL, 0x00000002UL, 0x0000007fUL, 0x00000050UL, 0x0000003cUL, 0x0000009fUL, 0x000000a8UL,
-0x00000051UL, 0x000000a3UL, 0x00000040UL, 0x0000008fUL, 0x00000092UL, 0x0000009dUL, 0x00000038UL, 0x000000f5UL,
-0x000000bcUL, 0x000000b6UL, 0x000000daUL, 0x00000021UL, 0x00000010UL, 0x000000ffUL, 0x000000f3UL, 0x000000d2UL,
-0x000000cdUL, 0x0000000cUL, 0x00000013UL, 0x000000ecUL, 0x0000005fUL, 0x00000097UL, 0x00000044UL, 0x00000017UL,
-0x000000c4UL, 0x000000a7UL, 0x0000007eUL, 0x0000003dUL, 0x00000064UL, 0x0000005dUL, 0x00000019UL, 0x00000073UL,
-0x00000060UL, 0x00000081UL, 0x0000004fUL, 0x000000dcUL, 0x00000022UL, 0x0000002aUL, 0x00000090UL, 0x00000088UL,
-0x00000046UL, 0x000000eeUL, 0x000000b8UL, 0x00000014UL, 0x000000deUL, 0x0000005eUL, 0x0000000bUL, 0x000000dbUL,
-0x000000e0UL, 0x00000032UL, 0x0000003aUL, 0x0000000aUL, 0x00000049UL, 0x00000006UL, 0x00000024UL, 0x0000005cUL,
-0x000000c2UL, 0x000000d3UL, 0x000000acUL, 0x00000062UL, 0x00000091UL, 0x00000095UL, 0x000000e4UL, 0x00000079UL,
-0x000000e7UL, 0x000000c8UL, 0x00000037UL, 0x0000006dUL, 0x0000008dUL, 0x000000d5UL, 0x0000004eUL, 0x000000a9UL,
-0x0000006cUL, 0x00000056UL, 0x000000f4UL, 0x000000eaUL, 0x00000065UL, 0x0000007aUL, 0x000000aeUL, 0x00000008UL,
-0x000000baUL, 0x00000078UL, 0x00000025UL, 0x0000002eUL, 0x0000001cUL, 0x000000a6UL, 0x000000b4UL, 0x000000c6UL,
-0x000000e8UL, 0x000000ddUL, 0x00000074UL, 0x0000001fUL, 0x0000004bUL, 0x000000bdUL, 0x0000008bUL, 0x0000008aUL,
-0x00000070UL, 0x0000003eUL, 0x000000b5UL, 0x00000066UL, 0x00000048UL, 0x00000003UL, 0x000000f6UL, 0x0000000eUL,
-0x00000061UL, 0x00000035UL, 0x00000057UL, 0x000000b9UL, 0x00000086UL, 0x000000c1UL, 0x0000001dUL, 0x0000009eUL,
-0x000000e1UL, 0x000000f8UL, 0x00000098UL, 0x00000011UL, 0x00000069UL, 0x000000d9UL, 0x0000008eUL, 0x00000094UL,
-0x0000009bUL, 0x0000001eUL, 0x00000087UL, 0x000000e9UL, 0x000000ceUL, 0x00000055UL, 0x00000028UL, 0x000000dfUL,
-0x0000008cUL, 0x000000a1UL, 0x00000089UL, 0x0000000dUL, 0x000000bfUL, 0x000000e6UL, 0x00000042UL, 0x00000068UL,
-0x00000041UL, 0x00000099UL, 0x0000002dUL, 0x0000000fUL, 0x000000b0UL, 0x00000054UL, 0x000000bbUL, 0x00000016UL
-};
-
-static const ulong32 Te4_1[] = {
-0x00006300UL, 0x00007c00UL, 0x00007700UL, 0x00007b00UL, 0x0000f200UL, 0x00006b00UL, 0x00006f00UL, 0x0000c500UL,
-0x00003000UL, 0x00000100UL, 0x00006700UL, 0x00002b00UL, 0x0000fe00UL, 0x0000d700UL, 0x0000ab00UL, 0x00007600UL,
-0x0000ca00UL, 0x00008200UL, 0x0000c900UL, 0x00007d00UL, 0x0000fa00UL, 0x00005900UL, 0x00004700UL, 0x0000f000UL,
-0x0000ad00UL, 0x0000d400UL, 0x0000a200UL, 0x0000af00UL, 0x00009c00UL, 0x0000a400UL, 0x00007200UL, 0x0000c000UL,
-0x0000b700UL, 0x0000fd00UL, 0x00009300UL, 0x00002600UL, 0x00003600UL, 0x00003f00UL, 0x0000f700UL, 0x0000cc00UL,
-0x00003400UL, 0x0000a500UL, 0x0000e500UL, 0x0000f100UL, 0x00007100UL, 0x0000d800UL, 0x00003100UL, 0x00001500UL,
-0x00000400UL, 0x0000c700UL, 0x00002300UL, 0x0000c300UL, 0x00001800UL, 0x00009600UL, 0x00000500UL, 0x00009a00UL,
-0x00000700UL, 0x00001200UL, 0x00008000UL, 0x0000e200UL, 0x0000eb00UL, 0x00002700UL, 0x0000b200UL, 0x00007500UL,
-0x00000900UL, 0x00008300UL, 0x00002c00UL, 0x00001a00UL, 0x00001b00UL, 0x00006e00UL, 0x00005a00UL, 0x0000a000UL,
-0x00005200UL, 0x00003b00UL, 0x0000d600UL, 0x0000b300UL, 0x00002900UL, 0x0000e300UL, 0x00002f00UL, 0x00008400UL,
-0x00005300UL, 0x0000d100UL, 0x00000000UL, 0x0000ed00UL, 0x00002000UL, 0x0000fc00UL, 0x0000b100UL, 0x00005b00UL,
-0x00006a00UL, 0x0000cb00UL, 0x0000be00UL, 0x00003900UL, 0x00004a00UL, 0x00004c00UL, 0x00005800UL, 0x0000cf00UL,
-0x0000d000UL, 0x0000ef00UL, 0x0000aa00UL, 0x0000fb00UL, 0x00004300UL, 0x00004d00UL, 0x00003300UL, 0x00008500UL,
-0x00004500UL, 0x0000f900UL, 0x00000200UL, 0x00007f00UL, 0x00005000UL, 0x00003c00UL, 0x00009f00UL, 0x0000a800UL,
-0x00005100UL, 0x0000a300UL, 0x00004000UL, 0x00008f00UL, 0x00009200UL, 0x00009d00UL, 0x00003800UL, 0x0000f500UL,
-0x0000bc00UL, 0x0000b600UL, 0x0000da00UL, 0x00002100UL, 0x00001000UL, 0x0000ff00UL, 0x0000f300UL, 0x0000d200UL,
-0x0000cd00UL, 0x00000c00UL, 0x00001300UL, 0x0000ec00UL, 0x00005f00UL, 0x00009700UL, 0x00004400UL, 0x00001700UL,
-0x0000c400UL, 0x0000a700UL, 0x00007e00UL, 0x00003d00UL, 0x00006400UL, 0x00005d00UL, 0x00001900UL, 0x00007300UL,
-0x00006000UL, 0x00008100UL, 0x00004f00UL, 0x0000dc00UL, 0x00002200UL, 0x00002a00UL, 0x00009000UL, 0x00008800UL,
-0x00004600UL, 0x0000ee00UL, 0x0000b800UL, 0x00001400UL, 0x0000de00UL, 0x00005e00UL, 0x00000b00UL, 0x0000db00UL,
-0x0000e000UL, 0x00003200UL, 0x00003a00UL, 0x00000a00UL, 0x00004900UL, 0x00000600UL, 0x00002400UL, 0x00005c00UL,
-0x0000c200UL, 0x0000d300UL, 0x0000ac00UL, 0x00006200UL, 0x00009100UL, 0x00009500UL, 0x0000e400UL, 0x00007900UL,
-0x0000e700UL, 0x0000c800UL, 0x00003700UL, 0x00006d00UL, 0x00008d00UL, 0x0000d500UL, 0x00004e00UL, 0x0000a900UL,
-0x00006c00UL, 0x00005600UL, 0x0000f400UL, 0x0000ea00UL, 0x00006500UL, 0x00007a00UL, 0x0000ae00UL, 0x00000800UL,
-0x0000ba00UL, 0x00007800UL, 0x00002500UL, 0x00002e00UL, 0x00001c00UL, 0x0000a600UL, 0x0000b400UL, 0x0000c600UL,
-0x0000e800UL, 0x0000dd00UL, 0x00007400UL, 0x00001f00UL, 0x00004b00UL, 0x0000bd00UL, 0x00008b00UL, 0x00008a00UL,
-0x00007000UL, 0x00003e00UL, 0x0000b500UL, 0x00006600UL, 0x00004800UL, 0x00000300UL, 0x0000f600UL, 0x00000e00UL,
-0x00006100UL, 0x00003500UL, 0x00005700UL, 0x0000b900UL, 0x00008600UL, 0x0000c100UL, 0x00001d00UL, 0x00009e00UL,
-0x0000e100UL, 0x0000f800UL, 0x00009800UL, 0x00001100UL, 0x00006900UL, 0x0000d900UL, 0x00008e00UL, 0x00009400UL,
-0x00009b00UL, 0x00001e00UL, 0x00008700UL, 0x0000e900UL, 0x0000ce00UL, 0x00005500UL, 0x00002800UL, 0x0000df00UL,
-0x00008c00UL, 0x0000a100UL, 0x00008900UL, 0x00000d00UL, 0x0000bf00UL, 0x0000e600UL, 0x00004200UL, 0x00006800UL,
-0x00004100UL, 0x00009900UL, 0x00002d00UL, 0x00000f00UL, 0x0000b000UL, 0x00005400UL, 0x0000bb00UL, 0x00001600UL
-};
-
-static const ulong32 Te4_2[] = {
-0x00630000UL, 0x007c0000UL, 0x00770000UL, 0x007b0000UL, 0x00f20000UL, 0x006b0000UL, 0x006f0000UL, 0x00c50000UL,
-0x00300000UL, 0x00010000UL, 0x00670000UL, 0x002b0000UL, 0x00fe0000UL, 0x00d70000UL, 0x00ab0000UL, 0x00760000UL,
-0x00ca0000UL, 0x00820000UL, 0x00c90000UL, 0x007d0000UL, 0x00fa0000UL, 0x00590000UL, 0x00470000UL, 0x00f00000UL,
-0x00ad0000UL, 0x00d40000UL, 0x00a20000UL, 0x00af0000UL, 0x009c0000UL, 0x00a40000UL, 0x00720000UL, 0x00c00000UL,
-0x00b70000UL, 0x00fd0000UL, 0x00930000UL, 0x00260000UL, 0x00360000UL, 0x003f0000UL, 0x00f70000UL, 0x00cc0000UL,
-0x00340000UL, 0x00a50000UL, 0x00e50000UL, 0x00f10000UL, 0x00710000UL, 0x00d80000UL, 0x00310000UL, 0x00150000UL,
-0x00040000UL, 0x00c70000UL, 0x00230000UL, 0x00c30000UL, 0x00180000UL, 0x00960000UL, 0x00050000UL, 0x009a0000UL,
-0x00070000UL, 0x00120000UL, 0x00800000UL, 0x00e20000UL, 0x00eb0000UL, 0x00270000UL, 0x00b20000UL, 0x00750000UL,
-0x00090000UL, 0x00830000UL, 0x002c0000UL, 0x001a0000UL, 0x001b0000UL, 0x006e0000UL, 0x005a0000UL, 0x00a00000UL,
-0x00520000UL, 0x003b0000UL, 0x00d60000UL, 0x00b30000UL, 0x00290000UL, 0x00e30000UL, 0x002f0000UL, 0x00840000UL,
-0x00530000UL, 0x00d10000UL, 0x00000000UL, 0x00ed0000UL, 0x00200000UL, 0x00fc0000UL, 0x00b10000UL, 0x005b0000UL,
-0x006a0000UL, 0x00cb0000UL, 0x00be0000UL, 0x00390000UL, 0x004a0000UL, 0x004c0000UL, 0x00580000UL, 0x00cf0000UL,
-0x00d00000UL, 0x00ef0000UL, 0x00aa0000UL, 0x00fb0000UL, 0x00430000UL, 0x004d0000UL, 0x00330000UL, 0x00850000UL,
-0x00450000UL, 0x00f90000UL, 0x00020000UL, 0x007f0000UL, 0x00500000UL, 0x003c0000UL, 0x009f0000UL, 0x00a80000UL,
-0x00510000UL, 0x00a30000UL, 0x00400000UL, 0x008f0000UL, 0x00920000UL, 0x009d0000UL, 0x00380000UL, 0x00f50000UL,
-0x00bc0000UL, 0x00b60000UL, 0x00da0000UL, 0x00210000UL, 0x00100000UL, 0x00ff0000UL, 0x00f30000UL, 0x00d20000UL,
-0x00cd0000UL, 0x000c0000UL, 0x00130000UL, 0x00ec0000UL, 0x005f0000UL, 0x00970000UL, 0x00440000UL, 0x00170000UL,
-0x00c40000UL, 0x00a70000UL, 0x007e0000UL, 0x003d0000UL, 0x00640000UL, 0x005d0000UL, 0x00190000UL, 0x00730000UL,
-0x00600000UL, 0x00810000UL, 0x004f0000UL, 0x00dc0000UL, 0x00220000UL, 0x002a0000UL, 0x00900000UL, 0x00880000UL,
-0x00460000UL, 0x00ee0000UL, 0x00b80000UL, 0x00140000UL, 0x00de0000UL, 0x005e0000UL, 0x000b0000UL, 0x00db0000UL,
-0x00e00000UL, 0x00320000UL, 0x003a0000UL, 0x000a0000UL, 0x00490000UL, 0x00060000UL, 0x00240000UL, 0x005c0000UL,
-0x00c20000UL, 0x00d30000UL, 0x00ac0000UL, 0x00620000UL, 0x00910000UL, 0x00950000UL, 0x00e40000UL, 0x00790000UL,
-0x00e70000UL, 0x00c80000UL, 0x00370000UL, 0x006d0000UL, 0x008d0000UL, 0x00d50000UL, 0x004e0000UL, 0x00a90000UL,
-0x006c0000UL, 0x00560000UL, 0x00f40000UL, 0x00ea0000UL, 0x00650000UL, 0x007a0000UL, 0x00ae0000UL, 0x00080000UL,
-0x00ba0000UL, 0x00780000UL, 0x00250000UL, 0x002e0000UL, 0x001c0000UL, 0x00a60000UL, 0x00b40000UL, 0x00c60000UL,
-0x00e80000UL, 0x00dd0000UL, 0x00740000UL, 0x001f0000UL, 0x004b0000UL, 0x00bd0000UL, 0x008b0000UL, 0x008a0000UL,
-0x00700000UL, 0x003e0000UL, 0x00b50000UL, 0x00660000UL, 0x00480000UL, 0x00030000UL, 0x00f60000UL, 0x000e0000UL,
-0x00610000UL, 0x00350000UL, 0x00570000UL, 0x00b90000UL, 0x00860000UL, 0x00c10000UL, 0x001d0000UL, 0x009e0000UL,
-0x00e10000UL, 0x00f80000UL, 0x00980000UL, 0x00110000UL, 0x00690000UL, 0x00d90000UL, 0x008e0000UL, 0x00940000UL,
-0x009b0000UL, 0x001e0000UL, 0x00870000UL, 0x00e90000UL, 0x00ce0000UL, 0x00550000UL, 0x00280000UL, 0x00df0000UL,
-0x008c0000UL, 0x00a10000UL, 0x00890000UL, 0x000d0000UL, 0x00bf0000UL, 0x00e60000UL, 0x00420000UL, 0x00680000UL,
-0x00410000UL, 0x00990000UL, 0x002d0000UL, 0x000f0000UL, 0x00b00000UL, 0x00540000UL, 0x00bb0000UL, 0x00160000UL
-};
-
-static const ulong32 Te4_3[] = {
-0x63000000UL, 0x7c000000UL, 0x77000000UL, 0x7b000000UL, 0xf2000000UL, 0x6b000000UL, 0x6f000000UL, 0xc5000000UL,
-0x30000000UL, 0x01000000UL, 0x67000000UL, 0x2b000000UL, 0xfe000000UL, 0xd7000000UL, 0xab000000UL, 0x76000000UL,
-0xca000000UL, 0x82000000UL, 0xc9000000UL, 0x7d000000UL, 0xfa000000UL, 0x59000000UL, 0x47000000UL, 0xf0000000UL,
-0xad000000UL, 0xd4000000UL, 0xa2000000UL, 0xaf000000UL, 0x9c000000UL, 0xa4000000UL, 0x72000000UL, 0xc0000000UL,
-0xb7000000UL, 0xfd000000UL, 0x93000000UL, 0x26000000UL, 0x36000000UL, 0x3f000000UL, 0xf7000000UL, 0xcc000000UL,
-0x34000000UL, 0xa5000000UL, 0xe5000000UL, 0xf1000000UL, 0x71000000UL, 0xd8000000UL, 0x31000000UL, 0x15000000UL,
-0x04000000UL, 0xc7000000UL, 0x23000000UL, 0xc3000000UL, 0x18000000UL, 0x96000000UL, 0x05000000UL, 0x9a000000UL,
-0x07000000UL, 0x12000000UL, 0x80000000UL, 0xe2000000UL, 0xeb000000UL, 0x27000000UL, 0xb2000000UL, 0x75000000UL,
-0x09000000UL, 0x83000000UL, 0x2c000000UL, 0x1a000000UL, 0x1b000000UL, 0x6e000000UL, 0x5a000000UL, 0xa0000000UL,
-0x52000000UL, 0x3b000000UL, 0xd6000000UL, 0xb3000000UL, 0x29000000UL, 0xe3000000UL, 0x2f000000UL, 0x84000000UL,
-0x53000000UL, 0xd1000000UL, 0x00000000UL, 0xed000000UL, 0x20000000UL, 0xfc000000UL, 0xb1000000UL, 0x5b000000UL,
-0x6a000000UL, 0xcb000000UL, 0xbe000000UL, 0x39000000UL, 0x4a000000UL, 0x4c000000UL, 0x58000000UL, 0xcf000000UL,
-0xd0000000UL, 0xef000000UL, 0xaa000000UL, 0xfb000000UL, 0x43000000UL, 0x4d000000UL, 0x33000000UL, 0x85000000UL,
-0x45000000UL, 0xf9000000UL, 0x02000000UL, 0x7f000000UL, 0x50000000UL, 0x3c000000UL, 0x9f000000UL, 0xa8000000UL,
-0x51000000UL, 0xa3000000UL, 0x40000000UL, 0x8f000000UL, 0x92000000UL, 0x9d000000UL, 0x38000000UL, 0xf5000000UL,
-0xbc000000UL, 0xb6000000UL, 0xda000000UL, 0x21000000UL, 0x10000000UL, 0xff000000UL, 0xf3000000UL, 0xd2000000UL,
-0xcd000000UL, 0x0c000000UL, 0x13000000UL, 0xec000000UL, 0x5f000000UL, 0x97000000UL, 0x44000000UL, 0x17000000UL,
-0xc4000000UL, 0xa7000000UL, 0x7e000000UL, 0x3d000000UL, 0x64000000UL, 0x5d000000UL, 0x19000000UL, 0x73000000UL,
-0x60000000UL, 0x81000000UL, 0x4f000000UL, 0xdc000000UL, 0x22000000UL, 0x2a000000UL, 0x90000000UL, 0x88000000UL,
-0x46000000UL, 0xee000000UL, 0xb8000000UL, 0x14000000UL, 0xde000000UL, 0x5e000000UL, 0x0b000000UL, 0xdb000000UL,
-0xe0000000UL, 0x32000000UL, 0x3a000000UL, 0x0a000000UL, 0x49000000UL, 0x06000000UL, 0x24000000UL, 0x5c000000UL,
-0xc2000000UL, 0xd3000000UL, 0xac000000UL, 0x62000000UL, 0x91000000UL, 0x95000000UL, 0xe4000000UL, 0x79000000UL,
-0xe7000000UL, 0xc8000000UL, 0x37000000UL, 0x6d000000UL, 0x8d000000UL, 0xd5000000UL, 0x4e000000UL, 0xa9000000UL,
-0x6c000000UL, 0x56000000UL, 0xf4000000UL, 0xea000000UL, 0x65000000UL, 0x7a000000UL, 0xae000000UL, 0x08000000UL,
-0xba000000UL, 0x78000000UL, 0x25000000UL, 0x2e000000UL, 0x1c000000UL, 0xa6000000UL, 0xb4000000UL, 0xc6000000UL,
-0xe8000000UL, 0xdd000000UL, 0x74000000UL, 0x1f000000UL, 0x4b000000UL, 0xbd000000UL, 0x8b000000UL, 0x8a000000UL,
-0x70000000UL, 0x3e000000UL, 0xb5000000UL, 0x66000000UL, 0x48000000UL, 0x03000000UL, 0xf6000000UL, 0x0e000000UL,
-0x61000000UL, 0x35000000UL, 0x57000000UL, 0xb9000000UL, 0x86000000UL, 0xc1000000UL, 0x1d000000UL, 0x9e000000UL,
-0xe1000000UL, 0xf8000000UL, 0x98000000UL, 0x11000000UL, 0x69000000UL, 0xd9000000UL, 0x8e000000UL, 0x94000000UL,
-0x9b000000UL, 0x1e000000UL, 0x87000000UL, 0xe9000000UL, 0xce000000UL, 0x55000000UL, 0x28000000UL, 0xdf000000UL,
-0x8c000000UL, 0xa1000000UL, 0x89000000UL, 0x0d000000UL, 0xbf000000UL, 0xe6000000UL, 0x42000000UL, 0x68000000UL,
-0x41000000UL, 0x99000000UL, 0x2d000000UL, 0x0f000000UL, 0xb0000000UL, 0x54000000UL, 0xbb000000UL, 0x16000000UL
-};
-#endif /* pelimac */
-
-#ifndef ENCRYPT_ONLY
-
-static const ulong32 TD1[256] = {
-    0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL,
-    0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL,
-    0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL,
-    0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL,
-    0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL,
-    0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL,
-    0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL,
-    0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL,
-    0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL,
-    0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL,
-    0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL,
-    0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL,
-    0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL,
-    0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL,
-    0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL,
-    0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL,
-    0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL,
-    0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL,
-    0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL,
-    0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL,
-    0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL,
-    0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL,
-    0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL,
-    0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL,
-    0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL,
-    0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL,
-    0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL,
-    0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL,
-    0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL,
-    0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL,
-    0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL,
-    0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL,
-    0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL,
-    0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL,
-    0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL,
-    0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL,
-    0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL,
-    0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL,
-    0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL,
-    0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL,
-    0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL,
-    0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL,
-    0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL,
-    0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL,
-    0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL,
-    0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL,
-    0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL,
-    0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL,
-    0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL,
-    0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL,
-    0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL,
-    0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL,
-    0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL,
-    0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL,
-    0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL,
-    0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL,
-    0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL,
-    0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL,
-    0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL,
-    0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL,
-    0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL,
-    0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL,
-    0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL,
-    0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL,
-};
-static const ulong32 TD2[256] = {
-    0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL,
-    0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL,
-    0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL,
-    0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL,
-    0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL,
-    0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL,
-    0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL,
-    0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL,
-    0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL,
-    0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL,
-    0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL,
-    0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL,
-    0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL,
-    0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL,
-    0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL,
-    0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL,
-    0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL,
-    0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL,
-    0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL,
-    0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL,
-    0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL,
-    0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL,
-    0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL,
-    0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL,
-    0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL,
-    0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL,
-    0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL,
-    0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL,
-    0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL,
-    0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL,
-    0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL,
-    0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL,
-    0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL,
-    0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL,
-    0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL,
-    0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL,
-    0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL,
-    0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL,
-    0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL,
-    0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL,
-    0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL,
-    0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL,
-    0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL,
-    0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL,
-    0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL,
-    0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL,
-    0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL,
-    0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL,
-    0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL,
-    0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL,
-    0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL,
-    0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL,
-    0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL,
-    0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL,
-    0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL,
-    0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL,
-    0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL,
-    0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL,
-    0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL,
-    0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL,
-    0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL,
-    0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL,
-    0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL,
-    0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL,
-};
-static const ulong32 TD3[256] = {
-    0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL,
-    0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL,
-    0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL,
-    0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL,
-    0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL,
-    0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL,
-    0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL,
-    0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL,
-    0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL,
-    0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL,
-    0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL,
-    0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL,
-    0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL,
-    0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL,
-    0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL,
-    0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL,
-    0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL,
-    0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL,
-    0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL,
-    0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL,
-    0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL,
-    0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL,
-    0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL,
-    0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL,
-    0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL,
-    0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL,
-    0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL,
-    0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL,
-    0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL,
-    0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL,
-    0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL,
-    0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL,
-    0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL,
-    0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL,
-    0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL,
-    0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL,
-    0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL,
-    0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL,
-    0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL,
-    0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL,
-    0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL,
-    0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL,
-    0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL,
-    0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL,
-    0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL,
-    0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL,
-    0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL,
-    0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL,
-    0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL,
-    0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL,
-    0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL,
-    0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL,
-    0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL,
-    0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL,
-    0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL,
-    0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL,
-    0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL,
-    0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL,
-    0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL,
-    0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL,
-    0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL,
-    0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL,
-    0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL,
-    0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL,
-};
-
-static const ulong32 Tks0[] = {
-0x00000000UL, 0x0e090d0bUL, 0x1c121a16UL, 0x121b171dUL, 0x3824342cUL, 0x362d3927UL, 0x24362e3aUL, 0x2a3f2331UL,
-0x70486858UL, 0x7e416553UL, 0x6c5a724eUL, 0x62537f45UL, 0x486c5c74UL, 0x4665517fUL, 0x547e4662UL, 0x5a774b69UL,
-0xe090d0b0UL, 0xee99ddbbUL, 0xfc82caa6UL, 0xf28bc7adUL, 0xd8b4e49cUL, 0xd6bde997UL, 0xc4a6fe8aUL, 0xcaaff381UL,
-0x90d8b8e8UL, 0x9ed1b5e3UL, 0x8ccaa2feUL, 0x82c3aff5UL, 0xa8fc8cc4UL, 0xa6f581cfUL, 0xb4ee96d2UL, 0xbae79bd9UL,
-0xdb3bbb7bUL, 0xd532b670UL, 0xc729a16dUL, 0xc920ac66UL, 0xe31f8f57UL, 0xed16825cUL, 0xff0d9541UL, 0xf104984aUL,
-0xab73d323UL, 0xa57ade28UL, 0xb761c935UL, 0xb968c43eUL, 0x9357e70fUL, 0x9d5eea04UL, 0x8f45fd19UL, 0x814cf012UL,
-0x3bab6bcbUL, 0x35a266c0UL, 0x27b971ddUL, 0x29b07cd6UL, 0x038f5fe7UL, 0x0d8652ecUL, 0x1f9d45f1UL, 0x119448faUL,
-0x4be30393UL, 0x45ea0e98UL, 0x57f11985UL, 0x59f8148eUL, 0x73c737bfUL, 0x7dce3ab4UL, 0x6fd52da9UL, 0x61dc20a2UL,
-0xad766df6UL, 0xa37f60fdUL, 0xb16477e0UL, 0xbf6d7aebUL, 0x955259daUL, 0x9b5b54d1UL, 0x894043ccUL, 0x87494ec7UL,
-0xdd3e05aeUL, 0xd33708a5UL, 0xc12c1fb8UL, 0xcf2512b3UL, 0xe51a3182UL, 0xeb133c89UL, 0xf9082b94UL, 0xf701269fUL,
-0x4de6bd46UL, 0x43efb04dUL, 0x51f4a750UL, 0x5ffdaa5bUL, 0x75c2896aUL, 0x7bcb8461UL, 0x69d0937cUL, 0x67d99e77UL,
-0x3daed51eUL, 0x33a7d815UL, 0x21bccf08UL, 0x2fb5c203UL, 0x058ae132UL, 0x0b83ec39UL, 0x1998fb24UL, 0x1791f62fUL,
-0x764dd68dUL, 0x7844db86UL, 0x6a5fcc9bUL, 0x6456c190UL, 0x4e69e2a1UL, 0x4060efaaUL, 0x527bf8b7UL, 0x5c72f5bcUL,
-0x0605bed5UL, 0x080cb3deUL, 0x1a17a4c3UL, 0x141ea9c8UL, 0x3e218af9UL, 0x302887f2UL, 0x223390efUL, 0x2c3a9de4UL,
-0x96dd063dUL, 0x98d40b36UL, 0x8acf1c2bUL, 0x84c61120UL, 0xaef93211UL, 0xa0f03f1aUL, 0xb2eb2807UL, 0xbce2250cUL,
-0xe6956e65UL, 0xe89c636eUL, 0xfa877473UL, 0xf48e7978UL, 0xdeb15a49UL, 0xd0b85742UL, 0xc2a3405fUL, 0xccaa4d54UL,
-0x41ecdaf7UL, 0x4fe5d7fcUL, 0x5dfec0e1UL, 0x53f7cdeaUL, 0x79c8eedbUL, 0x77c1e3d0UL, 0x65daf4cdUL, 0x6bd3f9c6UL,
-0x31a4b2afUL, 0x3fadbfa4UL, 0x2db6a8b9UL, 0x23bfa5b2UL, 0x09808683UL, 0x07898b88UL, 0x15929c95UL, 0x1b9b919eUL,
-0xa17c0a47UL, 0xaf75074cUL, 0xbd6e1051UL, 0xb3671d5aUL, 0x99583e6bUL, 0x97513360UL, 0x854a247dUL, 0x8b432976UL,
-0xd134621fUL, 0xdf3d6f14UL, 0xcd267809UL, 0xc32f7502UL, 0xe9105633UL, 0xe7195b38UL, 0xf5024c25UL, 0xfb0b412eUL,
-0x9ad7618cUL, 0x94de6c87UL, 0x86c57b9aUL, 0x88cc7691UL, 0xa2f355a0UL, 0xacfa58abUL, 0xbee14fb6UL, 0xb0e842bdUL,
-0xea9f09d4UL, 0xe49604dfUL, 0xf68d13c2UL, 0xf8841ec9UL, 0xd2bb3df8UL, 0xdcb230f3UL, 0xcea927eeUL, 0xc0a02ae5UL,
-0x7a47b13cUL, 0x744ebc37UL, 0x6655ab2aUL, 0x685ca621UL, 0x42638510UL, 0x4c6a881bUL, 0x5e719f06UL, 0x5078920dUL,
-0x0a0fd964UL, 0x0406d46fUL, 0x161dc372UL, 0x1814ce79UL, 0x322bed48UL, 0x3c22e043UL, 0x2e39f75eUL, 0x2030fa55UL,
-0xec9ab701UL, 0xe293ba0aUL, 0xf088ad17UL, 0xfe81a01cUL, 0xd4be832dUL, 0xdab78e26UL, 0xc8ac993bUL, 0xc6a59430UL,
-0x9cd2df59UL, 0x92dbd252UL, 0x80c0c54fUL, 0x8ec9c844UL, 0xa4f6eb75UL, 0xaaffe67eUL, 0xb8e4f163UL, 0xb6edfc68UL,
-0x0c0a67b1UL, 0x02036abaUL, 0x10187da7UL, 0x1e1170acUL, 0x342e539dUL, 0x3a275e96UL, 0x283c498bUL, 0x26354480UL,
-0x7c420fe9UL, 0x724b02e2UL, 0x605015ffUL, 0x6e5918f4UL, 0x44663bc5UL, 0x4a6f36ceUL, 0x587421d3UL, 0x567d2cd8UL,
-0x37a10c7aUL, 0x39a80171UL, 0x2bb3166cUL, 0x25ba1b67UL, 0x0f853856UL, 0x018c355dUL, 0x13972240UL, 0x1d9e2f4bUL,
-0x47e96422UL, 0x49e06929UL, 0x5bfb7e34UL, 0x55f2733fUL, 0x7fcd500eUL, 0x71c45d05UL, 0x63df4a18UL, 0x6dd64713UL,
-0xd731dccaUL, 0xd938d1c1UL, 0xcb23c6dcUL, 0xc52acbd7UL, 0xef15e8e6UL, 0xe11ce5edUL, 0xf307f2f0UL, 0xfd0efffbUL,
-0xa779b492UL, 0xa970b999UL, 0xbb6bae84UL, 0xb562a38fUL, 0x9f5d80beUL, 0x91548db5UL, 0x834f9aa8UL, 0x8d4697a3UL
-};
-
-static const ulong32 Tks1[] = {
-0x00000000UL, 0x0b0e090dUL, 0x161c121aUL, 0x1d121b17UL, 0x2c382434UL, 0x27362d39UL, 0x3a24362eUL, 0x312a3f23UL,
-0x58704868UL, 0x537e4165UL, 0x4e6c5a72UL, 0x4562537fUL, 0x74486c5cUL, 0x7f466551UL, 0x62547e46UL, 0x695a774bUL,
-0xb0e090d0UL, 0xbbee99ddUL, 0xa6fc82caUL, 0xadf28bc7UL, 0x9cd8b4e4UL, 0x97d6bde9UL, 0x8ac4a6feUL, 0x81caaff3UL,
-0xe890d8b8UL, 0xe39ed1b5UL, 0xfe8ccaa2UL, 0xf582c3afUL, 0xc4a8fc8cUL, 0xcfa6f581UL, 0xd2b4ee96UL, 0xd9bae79bUL,
-0x7bdb3bbbUL, 0x70d532b6UL, 0x6dc729a1UL, 0x66c920acUL, 0x57e31f8fUL, 0x5ced1682UL, 0x41ff0d95UL, 0x4af10498UL,
-0x23ab73d3UL, 0x28a57adeUL, 0x35b761c9UL, 0x3eb968c4UL, 0x0f9357e7UL, 0x049d5eeaUL, 0x198f45fdUL, 0x12814cf0UL,
-0xcb3bab6bUL, 0xc035a266UL, 0xdd27b971UL, 0xd629b07cUL, 0xe7038f5fUL, 0xec0d8652UL, 0xf11f9d45UL, 0xfa119448UL,
-0x934be303UL, 0x9845ea0eUL, 0x8557f119UL, 0x8e59f814UL, 0xbf73c737UL, 0xb47dce3aUL, 0xa96fd52dUL, 0xa261dc20UL,
-0xf6ad766dUL, 0xfda37f60UL, 0xe0b16477UL, 0xebbf6d7aUL, 0xda955259UL, 0xd19b5b54UL, 0xcc894043UL, 0xc787494eUL,
-0xaedd3e05UL, 0xa5d33708UL, 0xb8c12c1fUL, 0xb3cf2512UL, 0x82e51a31UL, 0x89eb133cUL, 0x94f9082bUL, 0x9ff70126UL,
-0x464de6bdUL, 0x4d43efb0UL, 0x5051f4a7UL, 0x5b5ffdaaUL, 0x6a75c289UL, 0x617bcb84UL, 0x7c69d093UL, 0x7767d99eUL,
-0x1e3daed5UL, 0x1533a7d8UL, 0x0821bccfUL, 0x032fb5c2UL, 0x32058ae1UL, 0x390b83ecUL, 0x241998fbUL, 0x2f1791f6UL,
-0x8d764dd6UL, 0x867844dbUL, 0x9b6a5fccUL, 0x906456c1UL, 0xa14e69e2UL, 0xaa4060efUL, 0xb7527bf8UL, 0xbc5c72f5UL,
-0xd50605beUL, 0xde080cb3UL, 0xc31a17a4UL, 0xc8141ea9UL, 0xf93e218aUL, 0xf2302887UL, 0xef223390UL, 0xe42c3a9dUL,
-0x3d96dd06UL, 0x3698d40bUL, 0x2b8acf1cUL, 0x2084c611UL, 0x11aef932UL, 0x1aa0f03fUL, 0x07b2eb28UL, 0x0cbce225UL,
-0x65e6956eUL, 0x6ee89c63UL, 0x73fa8774UL, 0x78f48e79UL, 0x49deb15aUL, 0x42d0b857UL, 0x5fc2a340UL, 0x54ccaa4dUL,
-0xf741ecdaUL, 0xfc4fe5d7UL, 0xe15dfec0UL, 0xea53f7cdUL, 0xdb79c8eeUL, 0xd077c1e3UL, 0xcd65daf4UL, 0xc66bd3f9UL,
-0xaf31a4b2UL, 0xa43fadbfUL, 0xb92db6a8UL, 0xb223bfa5UL, 0x83098086UL, 0x8807898bUL, 0x9515929cUL, 0x9e1b9b91UL,
-0x47a17c0aUL, 0x4caf7507UL, 0x51bd6e10UL, 0x5ab3671dUL, 0x6b99583eUL, 0x60975133UL, 0x7d854a24UL, 0x768b4329UL,
-0x1fd13462UL, 0x14df3d6fUL, 0x09cd2678UL, 0x02c32f75UL, 0x33e91056UL, 0x38e7195bUL, 0x25f5024cUL, 0x2efb0b41UL,
-0x8c9ad761UL, 0x8794de6cUL, 0x9a86c57bUL, 0x9188cc76UL, 0xa0a2f355UL, 0xabacfa58UL, 0xb6bee14fUL, 0xbdb0e842UL,
-0xd4ea9f09UL, 0xdfe49604UL, 0xc2f68d13UL, 0xc9f8841eUL, 0xf8d2bb3dUL, 0xf3dcb230UL, 0xeecea927UL, 0xe5c0a02aUL,
-0x3c7a47b1UL, 0x37744ebcUL, 0x2a6655abUL, 0x21685ca6UL, 0x10426385UL, 0x1b4c6a88UL, 0x065e719fUL, 0x0d507892UL,
-0x640a0fd9UL, 0x6f0406d4UL, 0x72161dc3UL, 0x791814ceUL, 0x48322bedUL, 0x433c22e0UL, 0x5e2e39f7UL, 0x552030faUL,
-0x01ec9ab7UL, 0x0ae293baUL, 0x17f088adUL, 0x1cfe81a0UL, 0x2dd4be83UL, 0x26dab78eUL, 0x3bc8ac99UL, 0x30c6a594UL,
-0x599cd2dfUL, 0x5292dbd2UL, 0x4f80c0c5UL, 0x448ec9c8UL, 0x75a4f6ebUL, 0x7eaaffe6UL, 0x63b8e4f1UL, 0x68b6edfcUL,
-0xb10c0a67UL, 0xba02036aUL, 0xa710187dUL, 0xac1e1170UL, 0x9d342e53UL, 0x963a275eUL, 0x8b283c49UL, 0x80263544UL,
-0xe97c420fUL, 0xe2724b02UL, 0xff605015UL, 0xf46e5918UL, 0xc544663bUL, 0xce4a6f36UL, 0xd3587421UL, 0xd8567d2cUL,
-0x7a37a10cUL, 0x7139a801UL, 0x6c2bb316UL, 0x6725ba1bUL, 0x560f8538UL, 0x5d018c35UL, 0x40139722UL, 0x4b1d9e2fUL,
-0x2247e964UL, 0x2949e069UL, 0x345bfb7eUL, 0x3f55f273UL, 0x0e7fcd50UL, 0x0571c45dUL, 0x1863df4aUL, 0x136dd647UL,
-0xcad731dcUL, 0xc1d938d1UL, 0xdccb23c6UL, 0xd7c52acbUL, 0xe6ef15e8UL, 0xede11ce5UL, 0xf0f307f2UL, 0xfbfd0effUL,
-0x92a779b4UL, 0x99a970b9UL, 0x84bb6baeUL, 0x8fb562a3UL, 0xbe9f5d80UL, 0xb591548dUL, 0xa8834f9aUL, 0xa38d4697UL
-};
-
-static const ulong32 Tks2[] = {
-0x00000000UL, 0x0d0b0e09UL, 0x1a161c12UL, 0x171d121bUL, 0x342c3824UL, 0x3927362dUL, 0x2e3a2436UL, 0x23312a3fUL,
-0x68587048UL, 0x65537e41UL, 0x724e6c5aUL, 0x7f456253UL, 0x5c74486cUL, 0x517f4665UL, 0x4662547eUL, 0x4b695a77UL,
-0xd0b0e090UL, 0xddbbee99UL, 0xcaa6fc82UL, 0xc7adf28bUL, 0xe49cd8b4UL, 0xe997d6bdUL, 0xfe8ac4a6UL, 0xf381caafUL,
-0xb8e890d8UL, 0xb5e39ed1UL, 0xa2fe8ccaUL, 0xaff582c3UL, 0x8cc4a8fcUL, 0x81cfa6f5UL, 0x96d2b4eeUL, 0x9bd9bae7UL,
-0xbb7bdb3bUL, 0xb670d532UL, 0xa16dc729UL, 0xac66c920UL, 0x8f57e31fUL, 0x825ced16UL, 0x9541ff0dUL, 0x984af104UL,
-0xd323ab73UL, 0xde28a57aUL, 0xc935b761UL, 0xc43eb968UL, 0xe70f9357UL, 0xea049d5eUL, 0xfd198f45UL, 0xf012814cUL,
-0x6bcb3babUL, 0x66c035a2UL, 0x71dd27b9UL, 0x7cd629b0UL, 0x5fe7038fUL, 0x52ec0d86UL, 0x45f11f9dUL, 0x48fa1194UL,
-0x03934be3UL, 0x0e9845eaUL, 0x198557f1UL, 0x148e59f8UL, 0x37bf73c7UL, 0x3ab47dceUL, 0x2da96fd5UL, 0x20a261dcUL,
-0x6df6ad76UL, 0x60fda37fUL, 0x77e0b164UL, 0x7aebbf6dUL, 0x59da9552UL, 0x54d19b5bUL, 0x43cc8940UL, 0x4ec78749UL,
-0x05aedd3eUL, 0x08a5d337UL, 0x1fb8c12cUL, 0x12b3cf25UL, 0x3182e51aUL, 0x3c89eb13UL, 0x2b94f908UL, 0x269ff701UL,
-0xbd464de6UL, 0xb04d43efUL, 0xa75051f4UL, 0xaa5b5ffdUL, 0x896a75c2UL, 0x84617bcbUL, 0x937c69d0UL, 0x9e7767d9UL,
-0xd51e3daeUL, 0xd81533a7UL, 0xcf0821bcUL, 0xc2032fb5UL, 0xe132058aUL, 0xec390b83UL, 0xfb241998UL, 0xf62f1791UL,
-0xd68d764dUL, 0xdb867844UL, 0xcc9b6a5fUL, 0xc1906456UL, 0xe2a14e69UL, 0xefaa4060UL, 0xf8b7527bUL, 0xf5bc5c72UL,
-0xbed50605UL, 0xb3de080cUL, 0xa4c31a17UL, 0xa9c8141eUL, 0x8af93e21UL, 0x87f23028UL, 0x90ef2233UL, 0x9de42c3aUL,
-0x063d96ddUL, 0x0b3698d4UL, 0x1c2b8acfUL, 0x112084c6UL, 0x3211aef9UL, 0x3f1aa0f0UL, 0x2807b2ebUL, 0x250cbce2UL,
-0x6e65e695UL, 0x636ee89cUL, 0x7473fa87UL, 0x7978f48eUL, 0x5a49deb1UL, 0x5742d0b8UL, 0x405fc2a3UL, 0x4d54ccaaUL,
-0xdaf741ecUL, 0xd7fc4fe5UL, 0xc0e15dfeUL, 0xcdea53f7UL, 0xeedb79c8UL, 0xe3d077c1UL, 0xf4cd65daUL, 0xf9c66bd3UL,
-0xb2af31a4UL, 0xbfa43fadUL, 0xa8b92db6UL, 0xa5b223bfUL, 0x86830980UL, 0x8b880789UL, 0x9c951592UL, 0x919e1b9bUL,
-0x0a47a17cUL, 0x074caf75UL, 0x1051bd6eUL, 0x1d5ab367UL, 0x3e6b9958UL, 0x33609751UL, 0x247d854aUL, 0x29768b43UL,
-0x621fd134UL, 0x6f14df3dUL, 0x7809cd26UL, 0x7502c32fUL, 0x5633e910UL, 0x5b38e719UL, 0x4c25f502UL, 0x412efb0bUL,
-0x618c9ad7UL, 0x6c8794deUL, 0x7b9a86c5UL, 0x769188ccUL, 0x55a0a2f3UL, 0x58abacfaUL, 0x4fb6bee1UL, 0x42bdb0e8UL,
-0x09d4ea9fUL, 0x04dfe496UL, 0x13c2f68dUL, 0x1ec9f884UL, 0x3df8d2bbUL, 0x30f3dcb2UL, 0x27eecea9UL, 0x2ae5c0a0UL,
-0xb13c7a47UL, 0xbc37744eUL, 0xab2a6655UL, 0xa621685cUL, 0x85104263UL, 0x881b4c6aUL, 0x9f065e71UL, 0x920d5078UL,
-0xd9640a0fUL, 0xd46f0406UL, 0xc372161dUL, 0xce791814UL, 0xed48322bUL, 0xe0433c22UL, 0xf75e2e39UL, 0xfa552030UL,
-0xb701ec9aUL, 0xba0ae293UL, 0xad17f088UL, 0xa01cfe81UL, 0x832dd4beUL, 0x8e26dab7UL, 0x993bc8acUL, 0x9430c6a5UL,
-0xdf599cd2UL, 0xd25292dbUL, 0xc54f80c0UL, 0xc8448ec9UL, 0xeb75a4f6UL, 0xe67eaaffUL, 0xf163b8e4UL, 0xfc68b6edUL,
-0x67b10c0aUL, 0x6aba0203UL, 0x7da71018UL, 0x70ac1e11UL, 0x539d342eUL, 0x5e963a27UL, 0x498b283cUL, 0x44802635UL,
-0x0fe97c42UL, 0x02e2724bUL, 0x15ff6050UL, 0x18f46e59UL, 0x3bc54466UL, 0x36ce4a6fUL, 0x21d35874UL, 0x2cd8567dUL,
-0x0c7a37a1UL, 0x017139a8UL, 0x166c2bb3UL, 0x1b6725baUL, 0x38560f85UL, 0x355d018cUL, 0x22401397UL, 0x2f4b1d9eUL,
-0x642247e9UL, 0x692949e0UL, 0x7e345bfbUL, 0x733f55f2UL, 0x500e7fcdUL, 0x5d0571c4UL, 0x4a1863dfUL, 0x47136dd6UL,
-0xdccad731UL, 0xd1c1d938UL, 0xc6dccb23UL, 0xcbd7c52aUL, 0xe8e6ef15UL, 0xe5ede11cUL, 0xf2f0f307UL, 0xfffbfd0eUL,
-0xb492a779UL, 0xb999a970UL, 0xae84bb6bUL, 0xa38fb562UL, 0x80be9f5dUL, 0x8db59154UL, 0x9aa8834fUL, 0x97a38d46UL
-};
-
-static const ulong32 Tks3[] = {
-0x00000000UL, 0x090d0b0eUL, 0x121a161cUL, 0x1b171d12UL, 0x24342c38UL, 0x2d392736UL, 0x362e3a24UL, 0x3f23312aUL,
-0x48685870UL, 0x4165537eUL, 0x5a724e6cUL, 0x537f4562UL, 0x6c5c7448UL, 0x65517f46UL, 0x7e466254UL, 0x774b695aUL,
-0x90d0b0e0UL, 0x99ddbbeeUL, 0x82caa6fcUL, 0x8bc7adf2UL, 0xb4e49cd8UL, 0xbde997d6UL, 0xa6fe8ac4UL, 0xaff381caUL,
-0xd8b8e890UL, 0xd1b5e39eUL, 0xcaa2fe8cUL, 0xc3aff582UL, 0xfc8cc4a8UL, 0xf581cfa6UL, 0xee96d2b4UL, 0xe79bd9baUL,
-0x3bbb7bdbUL, 0x32b670d5UL, 0x29a16dc7UL, 0x20ac66c9UL, 0x1f8f57e3UL, 0x16825cedUL, 0x0d9541ffUL, 0x04984af1UL,
-0x73d323abUL, 0x7ade28a5UL, 0x61c935b7UL, 0x68c43eb9UL, 0x57e70f93UL, 0x5eea049dUL, 0x45fd198fUL, 0x4cf01281UL,
-0xab6bcb3bUL, 0xa266c035UL, 0xb971dd27UL, 0xb07cd629UL, 0x8f5fe703UL, 0x8652ec0dUL, 0x9d45f11fUL, 0x9448fa11UL,
-0xe303934bUL, 0xea0e9845UL, 0xf1198557UL, 0xf8148e59UL, 0xc737bf73UL, 0xce3ab47dUL, 0xd52da96fUL, 0xdc20a261UL,
-0x766df6adUL, 0x7f60fda3UL, 0x6477e0b1UL, 0x6d7aebbfUL, 0x5259da95UL, 0x5b54d19bUL, 0x4043cc89UL, 0x494ec787UL,
-0x3e05aeddUL, 0x3708a5d3UL, 0x2c1fb8c1UL, 0x2512b3cfUL, 0x1a3182e5UL, 0x133c89ebUL, 0x082b94f9UL, 0x01269ff7UL,
-0xe6bd464dUL, 0xefb04d43UL, 0xf4a75051UL, 0xfdaa5b5fUL, 0xc2896a75UL, 0xcb84617bUL, 0xd0937c69UL, 0xd99e7767UL,
-0xaed51e3dUL, 0xa7d81533UL, 0xbccf0821UL, 0xb5c2032fUL, 0x8ae13205UL, 0x83ec390bUL, 0x98fb2419UL, 0x91f62f17UL,
-0x4dd68d76UL, 0x44db8678UL, 0x5fcc9b6aUL, 0x56c19064UL, 0x69e2a14eUL, 0x60efaa40UL, 0x7bf8b752UL, 0x72f5bc5cUL,
-0x05bed506UL, 0x0cb3de08UL, 0x17a4c31aUL, 0x1ea9c814UL, 0x218af93eUL, 0x2887f230UL, 0x3390ef22UL, 0x3a9de42cUL,
-0xdd063d96UL, 0xd40b3698UL, 0xcf1c2b8aUL, 0xc6112084UL, 0xf93211aeUL, 0xf03f1aa0UL, 0xeb2807b2UL, 0xe2250cbcUL,
-0x956e65e6UL, 0x9c636ee8UL, 0x877473faUL, 0x8e7978f4UL, 0xb15a49deUL, 0xb85742d0UL, 0xa3405fc2UL, 0xaa4d54ccUL,
-0xecdaf741UL, 0xe5d7fc4fUL, 0xfec0e15dUL, 0xf7cdea53UL, 0xc8eedb79UL, 0xc1e3d077UL, 0xdaf4cd65UL, 0xd3f9c66bUL,
-0xa4b2af31UL, 0xadbfa43fUL, 0xb6a8b92dUL, 0xbfa5b223UL, 0x80868309UL, 0x898b8807UL, 0x929c9515UL, 0x9b919e1bUL,
-0x7c0a47a1UL, 0x75074cafUL, 0x6e1051bdUL, 0x671d5ab3UL, 0x583e6b99UL, 0x51336097UL, 0x4a247d85UL, 0x4329768bUL,
-0x34621fd1UL, 0x3d6f14dfUL, 0x267809cdUL, 0x2f7502c3UL, 0x105633e9UL, 0x195b38e7UL, 0x024c25f5UL, 0x0b412efbUL,
-0xd7618c9aUL, 0xde6c8794UL, 0xc57b9a86UL, 0xcc769188UL, 0xf355a0a2UL, 0xfa58abacUL, 0xe14fb6beUL, 0xe842bdb0UL,
-0x9f09d4eaUL, 0x9604dfe4UL, 0x8d13c2f6UL, 0x841ec9f8UL, 0xbb3df8d2UL, 0xb230f3dcUL, 0xa927eeceUL, 0xa02ae5c0UL,
-0x47b13c7aUL, 0x4ebc3774UL, 0x55ab2a66UL, 0x5ca62168UL, 0x63851042UL, 0x6a881b4cUL, 0x719f065eUL, 0x78920d50UL,
-0x0fd9640aUL, 0x06d46f04UL, 0x1dc37216UL, 0x14ce7918UL, 0x2bed4832UL, 0x22e0433cUL, 0x39f75e2eUL, 0x30fa5520UL,
-0x9ab701ecUL, 0x93ba0ae2UL, 0x88ad17f0UL, 0x81a01cfeUL, 0xbe832dd4UL, 0xb78e26daUL, 0xac993bc8UL, 0xa59430c6UL,
-0xd2df599cUL, 0xdbd25292UL, 0xc0c54f80UL, 0xc9c8448eUL, 0xf6eb75a4UL, 0xffe67eaaUL, 0xe4f163b8UL, 0xedfc68b6UL,
-0x0a67b10cUL, 0x036aba02UL, 0x187da710UL, 0x1170ac1eUL, 0x2e539d34UL, 0x275e963aUL, 0x3c498b28UL, 0x35448026UL,
-0x420fe97cUL, 0x4b02e272UL, 0x5015ff60UL, 0x5918f46eUL, 0x663bc544UL, 0x6f36ce4aUL, 0x7421d358UL, 0x7d2cd856UL,
-0xa10c7a37UL, 0xa8017139UL, 0xb3166c2bUL, 0xba1b6725UL, 0x8538560fUL, 0x8c355d01UL, 0x97224013UL, 0x9e2f4b1dUL,
-0xe9642247UL, 0xe0692949UL, 0xfb7e345bUL, 0xf2733f55UL, 0xcd500e7fUL, 0xc45d0571UL, 0xdf4a1863UL, 0xd647136dUL,
-0x31dccad7UL, 0x38d1c1d9UL, 0x23c6dccbUL, 0x2acbd7c5UL, 0x15e8e6efUL, 0x1ce5ede1UL, 0x07f2f0f3UL, 0x0efffbfdUL,
-0x79b492a7UL, 0x70b999a9UL, 0x6bae84bbUL, 0x62a38fb5UL, 0x5d80be9fUL, 0x548db591UL, 0x4f9aa883UL, 0x4697a38dUL
-};
-
-#endif /* ENCRYPT_ONLY */
-
-#endif /* SMALL CODE */
-
-#ifndef PELI_TAB
-static const ulong32 rcon[] = {
-    0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL,
-    0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL,
-    0x1B000000UL, 0x36000000UL, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
-};
-#endif
-
-#endif /* __LTC_AES_TAB_C__ */
diff --git a/libs/tomcrypt/src/ciphers/blowfish.c b/libs/tomcrypt/src/ciphers/blowfish.c
deleted file mode 100644
index 745b59ea2834..000000000000
--- a/libs/tomcrypt/src/ciphers/blowfish.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**
-  @file blowfish.c
-  Implementation of the Blowfish block cipher, Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_BLOWFISH
-
-const struct ltc_cipher_descriptor blowfish_desc =
-{
-    "blowfish",
-    0,
-    8, 56, 8, 16,
-    &blowfish_setup,
-    &blowfish_ecb_encrypt,
-    &blowfish_ecb_decrypt,
-    &blowfish_test,
-    &blowfish_done,
-    &blowfish_keysize,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-static const ulong32 ORIG_P[16 + 2] = {
-        0x243F6A88UL, 0x85A308D3UL, 0x13198A2EUL, 0x03707344UL,
-        0xA4093822UL, 0x299F31D0UL, 0x082EFA98UL, 0xEC4E6C89UL,
-        0x452821E6UL, 0x38D01377UL, 0xBE5466CFUL, 0x34E90C6CUL,
-        0xC0AC29B7UL, 0xC97C50DDUL, 0x3F84D5B5UL, 0xB5470917UL,
-        0x9216D5D9UL, 0x8979FB1BUL
-};
-
-static const ulong32 ORIG_S[4][256] = {
-    {   0xD1310BA6UL, 0x98DFB5ACUL, 0x2FFD72DBUL, 0xD01ADFB7UL,
-        0xB8E1AFEDUL, 0x6A267E96UL, 0xBA7C9045UL, 0xF12C7F99UL,
-        0x24A19947UL, 0xB3916CF7UL, 0x0801F2E2UL, 0x858EFC16UL,
-        0x636920D8UL, 0x71574E69UL, 0xA458FEA3UL, 0xF4933D7EUL,
-        0x0D95748FUL, 0x728EB658UL, 0x718BCD58UL, 0x82154AEEUL,
-        0x7B54A41DUL, 0xC25A59B5UL, 0x9C30D539UL, 0x2AF26013UL,
-        0xC5D1B023UL, 0x286085F0UL, 0xCA417918UL, 0xB8DB38EFUL,
-        0x8E79DCB0UL, 0x603A180EUL, 0x6C9E0E8BUL, 0xB01E8A3EUL,
-        0xD71577C1UL, 0xBD314B27UL, 0x78AF2FDAUL, 0x55605C60UL,
-        0xE65525F3UL, 0xAA55AB94UL, 0x57489862UL, 0x63E81440UL,
-        0x55CA396AUL, 0x2AAB10B6UL, 0xB4CC5C34UL, 0x1141E8CEUL,
-        0xA15486AFUL, 0x7C72E993UL, 0xB3EE1411UL, 0x636FBC2AUL,
-        0x2BA9C55DUL, 0x741831F6UL, 0xCE5C3E16UL, 0x9B87931EUL,
-        0xAFD6BA33UL, 0x6C24CF5CUL, 0x7A325381UL, 0x28958677UL,
-        0x3B8F4898UL, 0x6B4BB9AFUL, 0xC4BFE81BUL, 0x66282193UL,
-        0x61D809CCUL, 0xFB21A991UL, 0x487CAC60UL, 0x5DEC8032UL,
-        0xEF845D5DUL, 0xE98575B1UL, 0xDC262302UL, 0xEB651B88UL,
-        0x23893E81UL, 0xD396ACC5UL, 0x0F6D6FF3UL, 0x83F44239UL,
-        0x2E0B4482UL, 0xA4842004UL, 0x69C8F04AUL, 0x9E1F9B5EUL,
-        0x21C66842UL, 0xF6E96C9AUL, 0x670C9C61UL, 0xABD388F0UL,
-        0x6A51A0D2UL, 0xD8542F68UL, 0x960FA728UL, 0xAB5133A3UL,
-        0x6EEF0B6CUL, 0x137A3BE4UL, 0xBA3BF050UL, 0x7EFB2A98UL,
-        0xA1F1651DUL, 0x39AF0176UL, 0x66CA593EUL, 0x82430E88UL,
-        0x8CEE8619UL, 0x456F9FB4UL, 0x7D84A5C3UL, 0x3B8B5EBEUL,
-        0xE06F75D8UL, 0x85C12073UL, 0x401A449FUL, 0x56C16AA6UL,
-        0x4ED3AA62UL, 0x363F7706UL, 0x1BFEDF72UL, 0x429B023DUL,
-        0x37D0D724UL, 0xD00A1248UL, 0xDB0FEAD3UL, 0x49F1C09BUL,
-        0x075372C9UL, 0x80991B7BUL, 0x25D479D8UL, 0xF6E8DEF7UL,
-        0xE3FE501AUL, 0xB6794C3BUL, 0x976CE0BDUL, 0x04C006BAUL,
-        0xC1A94FB6UL, 0x409F60C4UL, 0x5E5C9EC2UL, 0x196A2463UL,
-        0x68FB6FAFUL, 0x3E6C53B5UL, 0x1339B2EBUL, 0x3B52EC6FUL,
-        0x6DFC511FUL, 0x9B30952CUL, 0xCC814544UL, 0xAF5EBD09UL,
-        0xBEE3D004UL, 0xDE334AFDUL, 0x660F2807UL, 0x192E4BB3UL,
-        0xC0CBA857UL, 0x45C8740FUL, 0xD20B5F39UL, 0xB9D3FBDBUL,
-        0x5579C0BDUL, 0x1A60320AUL, 0xD6A100C6UL, 0x402C7279UL,
-        0x679F25FEUL, 0xFB1FA3CCUL, 0x8EA5E9F8UL, 0xDB3222F8UL,
-        0x3C7516DFUL, 0xFD616B15UL, 0x2F501EC8UL, 0xAD0552ABUL,
-        0x323DB5FAUL, 0xFD238760UL, 0x53317B48UL, 0x3E00DF82UL,
-        0x9E5C57BBUL, 0xCA6F8CA0UL, 0x1A87562EUL, 0xDF1769DBUL,
-        0xD542A8F6UL, 0x287EFFC3UL, 0xAC6732C6UL, 0x8C4F5573UL,
-        0x695B27B0UL, 0xBBCA58C8UL, 0xE1FFA35DUL, 0xB8F011A0UL,
-        0x10FA3D98UL, 0xFD2183B8UL, 0x4AFCB56CUL, 0x2DD1D35BUL,
-        0x9A53E479UL, 0xB6F84565UL, 0xD28E49BCUL, 0x4BFB9790UL,
-        0xE1DDF2DAUL, 0xA4CB7E33UL, 0x62FB1341UL, 0xCEE4C6E8UL,
-        0xEF20CADAUL, 0x36774C01UL, 0xD07E9EFEUL, 0x2BF11FB4UL,
-        0x95DBDA4DUL, 0xAE909198UL, 0xEAAD8E71UL, 0x6B93D5A0UL,
-        0xD08ED1D0UL, 0xAFC725E0UL, 0x8E3C5B2FUL, 0x8E7594B7UL,
-        0x8FF6E2FBUL, 0xF2122B64UL, 0x8888B812UL, 0x900DF01CUL,
-        0x4FAD5EA0UL, 0x688FC31CUL, 0xD1CFF191UL, 0xB3A8C1ADUL,
-        0x2F2F2218UL, 0xBE0E1777UL, 0xEA752DFEUL, 0x8B021FA1UL,
-        0xE5A0CC0FUL, 0xB56F74E8UL, 0x18ACF3D6UL, 0xCE89E299UL,
-        0xB4A84FE0UL, 0xFD13E0B7UL, 0x7CC43B81UL, 0xD2ADA8D9UL,
-        0x165FA266UL, 0x80957705UL, 0x93CC7314UL, 0x211A1477UL,
-        0xE6AD2065UL, 0x77B5FA86UL, 0xC75442F5UL, 0xFB9D35CFUL,
-        0xEBCDAF0CUL, 0x7B3E89A0UL, 0xD6411BD3UL, 0xAE1E7E49UL,
-        0x00250E2DUL, 0x2071B35EUL, 0x226800BBUL, 0x57B8E0AFUL,
-        0x2464369BUL, 0xF009B91EUL, 0x5563911DUL, 0x59DFA6AAUL,
-        0x78C14389UL, 0xD95A537FUL, 0x207D5BA2UL, 0x02E5B9C5UL,
-        0x83260376UL, 0x6295CFA9UL, 0x11C81968UL, 0x4E734A41UL,
-        0xB3472DCAUL, 0x7B14A94AUL, 0x1B510052UL, 0x9A532915UL,
-        0xD60F573FUL, 0xBC9BC6E4UL, 0x2B60A476UL, 0x81E67400UL,
-        0x08BA6FB5UL, 0x571BE91FUL, 0xF296EC6BUL, 0x2A0DD915UL,
-        0xB6636521UL, 0xE7B9F9B6UL, 0xFF34052EUL, 0xC5855664UL,
-        0x53B02D5DUL, 0xA99F8FA1UL, 0x08BA4799UL, 0x6E85076AUL   },
-    {   0x4B7A70E9UL, 0xB5B32944UL, 0xDB75092EUL, 0xC4192623UL,
-        0xAD6EA6B0UL, 0x49A7DF7DUL, 0x9CEE60B8UL, 0x8FEDB266UL,
-        0xECAA8C71UL, 0x699A17FFUL, 0x5664526CUL, 0xC2B19EE1UL,
-        0x193602A5UL, 0x75094C29UL, 0xA0591340UL, 0xE4183A3EUL,
-        0x3F54989AUL, 0x5B429D65UL, 0x6B8FE4D6UL, 0x99F73FD6UL,
-        0xA1D29C07UL, 0xEFE830F5UL, 0x4D2D38E6UL, 0xF0255DC1UL,
-        0x4CDD2086UL, 0x8470EB26UL, 0x6382E9C6UL, 0x021ECC5EUL,
-        0x09686B3FUL, 0x3EBAEFC9UL, 0x3C971814UL, 0x6B6A70A1UL,
-        0x687F3584UL, 0x52A0E286UL, 0xB79C5305UL, 0xAA500737UL,
-        0x3E07841CUL, 0x7FDEAE5CUL, 0x8E7D44ECUL, 0x5716F2B8UL,
-        0xB03ADA37UL, 0xF0500C0DUL, 0xF01C1F04UL, 0x0200B3FFUL,
-        0xAE0CF51AUL, 0x3CB574B2UL, 0x25837A58UL, 0xDC0921BDUL,
-        0xD19113F9UL, 0x7CA92FF6UL, 0x94324773UL, 0x22F54701UL,
-        0x3AE5E581UL, 0x37C2DADCUL, 0xC8B57634UL, 0x9AF3DDA7UL,
-        0xA9446146UL, 0x0FD0030EUL, 0xECC8C73EUL, 0xA4751E41UL,
-        0xE238CD99UL, 0x3BEA0E2FUL, 0x3280BBA1UL, 0x183EB331UL,
-        0x4E548B38UL, 0x4F6DB908UL, 0x6F420D03UL, 0xF60A04BFUL,
-        0x2CB81290UL, 0x24977C79UL, 0x5679B072UL, 0xBCAF89AFUL,
-        0xDE9A771FUL, 0xD9930810UL, 0xB38BAE12UL, 0xDCCF3F2EUL,
-        0x5512721FUL, 0x2E6B7124UL, 0x501ADDE6UL, 0x9F84CD87UL,
-        0x7A584718UL, 0x7408DA17UL, 0xBC9F9ABCUL, 0xE94B7D8CUL,
-        0xEC7AEC3AUL, 0xDB851DFAUL, 0x63094366UL, 0xC464C3D2UL,
-        0xEF1C1847UL, 0x3215D908UL, 0xDD433B37UL, 0x24C2BA16UL,
-        0x12A14D43UL, 0x2A65C451UL, 0x50940002UL, 0x133AE4DDUL,
-        0x71DFF89EUL, 0x10314E55UL, 0x81AC77D6UL, 0x5F11199BUL,
-        0x043556F1UL, 0xD7A3C76BUL, 0x3C11183BUL, 0x5924A509UL,
-        0xF28FE6EDUL, 0x97F1FBFAUL, 0x9EBABF2CUL, 0x1E153C6EUL,
-        0x86E34570UL, 0xEAE96FB1UL, 0x860E5E0AUL, 0x5A3E2AB3UL,
-        0x771FE71CUL, 0x4E3D06FAUL, 0x2965DCB9UL, 0x99E71D0FUL,
-        0x803E89D6UL, 0x5266C825UL, 0x2E4CC978UL, 0x9C10B36AUL,
-        0xC6150EBAUL, 0x94E2EA78UL, 0xA5FC3C53UL, 0x1E0A2DF4UL,
-        0xF2F74EA7UL, 0x361D2B3DUL, 0x1939260FUL, 0x19C27960UL,
-        0x5223A708UL, 0xF71312B6UL, 0xEBADFE6EUL, 0xEAC31F66UL,
-        0xE3BC4595UL, 0xA67BC883UL, 0xB17F37D1UL, 0x018CFF28UL,
-        0xC332DDEFUL, 0xBE6C5AA5UL, 0x65582185UL, 0x68AB9802UL,
-        0xEECEA50FUL, 0xDB2F953BUL, 0x2AEF7DADUL, 0x5B6E2F84UL,
-        0x1521B628UL, 0x29076170UL, 0xECDD4775UL, 0x619F1510UL,
-        0x13CCA830UL, 0xEB61BD96UL, 0x0334FE1EUL, 0xAA0363CFUL,
-        0xB5735C90UL, 0x4C70A239UL, 0xD59E9E0BUL, 0xCBAADE14UL,
-        0xEECC86BCUL, 0x60622CA7UL, 0x9CAB5CABUL, 0xB2F3846EUL,
-        0x648B1EAFUL, 0x19BDF0CAUL, 0xA02369B9UL, 0x655ABB50UL,
-        0x40685A32UL, 0x3C2AB4B3UL, 0x319EE9D5UL, 0xC021B8F7UL,
-        0x9B540B19UL, 0x875FA099UL, 0x95F7997EUL, 0x623D7DA8UL,
-        0xF837889AUL, 0x97E32D77UL, 0x11ED935FUL, 0x16681281UL,
-        0x0E358829UL, 0xC7E61FD6UL, 0x96DEDFA1UL, 0x7858BA99UL,
-        0x57F584A5UL, 0x1B227263UL, 0x9B83C3FFUL, 0x1AC24696UL,
-        0xCDB30AEBUL, 0x532E3054UL, 0x8FD948E4UL, 0x6DBC3128UL,
-        0x58EBF2EFUL, 0x34C6FFEAUL, 0xFE28ED61UL, 0xEE7C3C73UL,
-        0x5D4A14D9UL, 0xE864B7E3UL, 0x42105D14UL, 0x203E13E0UL,
-        0x45EEE2B6UL, 0xA3AAABEAUL, 0xDB6C4F15UL, 0xFACB4FD0UL,
-        0xC742F442UL, 0xEF6ABBB5UL, 0x654F3B1DUL, 0x41CD2105UL,
-        0xD81E799EUL, 0x86854DC7UL, 0xE44B476AUL, 0x3D816250UL,
-        0xCF62A1F2UL, 0x5B8D2646UL, 0xFC8883A0UL, 0xC1C7B6A3UL,
-        0x7F1524C3UL, 0x69CB7492UL, 0x47848A0BUL, 0x5692B285UL,
-        0x095BBF00UL, 0xAD19489DUL, 0x1462B174UL, 0x23820E00UL,
-        0x58428D2AUL, 0x0C55F5EAUL, 0x1DADF43EUL, 0x233F7061UL,
-        0x3372F092UL, 0x8D937E41UL, 0xD65FECF1UL, 0x6C223BDBUL,
-        0x7CDE3759UL, 0xCBEE7460UL, 0x4085F2A7UL, 0xCE77326EUL,
-        0xA6078084UL, 0x19F8509EUL, 0xE8EFD855UL, 0x61D99735UL,
-        0xA969A7AAUL, 0xC50C06C2UL, 0x5A04ABFCUL, 0x800BCADCUL,
-        0x9E447A2EUL, 0xC3453484UL, 0xFDD56705UL, 0x0E1E9EC9UL,
-        0xDB73DBD3UL, 0x105588CDUL, 0x675FDA79UL, 0xE3674340UL,
-        0xC5C43465UL, 0x713E38D8UL, 0x3D28F89EUL, 0xF16DFF20UL,
-        0x153E21E7UL, 0x8FB03D4AUL, 0xE6E39F2BUL, 0xDB83ADF7UL   },
-    {   0xE93D5A68UL, 0x948140F7UL, 0xF64C261CUL, 0x94692934UL,
-        0x411520F7UL, 0x7602D4F7UL, 0xBCF46B2EUL, 0xD4A20068UL,
-        0xD4082471UL, 0x3320F46AUL, 0x43B7D4B7UL, 0x500061AFUL,
-        0x1E39F62EUL, 0x97244546UL, 0x14214F74UL, 0xBF8B8840UL,
-        0x4D95FC1DUL, 0x96B591AFUL, 0x70F4DDD3UL, 0x66A02F45UL,
-        0xBFBC09ECUL, 0x03BD9785UL, 0x7FAC6DD0UL, 0x31CB8504UL,
-        0x96EB27B3UL, 0x55FD3941UL, 0xDA2547E6UL, 0xABCA0A9AUL,
-        0x28507825UL, 0x530429F4UL, 0x0A2C86DAUL, 0xE9B66DFBUL,
-        0x68DC1462UL, 0xD7486900UL, 0x680EC0A4UL, 0x27A18DEEUL,
-        0x4F3FFEA2UL, 0xE887AD8CUL, 0xB58CE006UL, 0x7AF4D6B6UL,
-        0xAACE1E7CUL, 0xD3375FECUL, 0xCE78A399UL, 0x406B2A42UL,
-        0x20FE9E35UL, 0xD9F385B9UL, 0xEE39D7ABUL, 0x3B124E8BUL,
-        0x1DC9FAF7UL, 0x4B6D1856UL, 0x26A36631UL, 0xEAE397B2UL,
-        0x3A6EFA74UL, 0xDD5B4332UL, 0x6841E7F7UL, 0xCA7820FBUL,
-        0xFB0AF54EUL, 0xD8FEB397UL, 0x454056ACUL, 0xBA489527UL,
-        0x55533A3AUL, 0x20838D87UL, 0xFE6BA9B7UL, 0xD096954BUL,
-        0x55A867BCUL, 0xA1159A58UL, 0xCCA92963UL, 0x99E1DB33UL,
-        0xA62A4A56UL, 0x3F3125F9UL, 0x5EF47E1CUL, 0x9029317CUL,
-        0xFDF8E802UL, 0x04272F70UL, 0x80BB155CUL, 0x05282CE3UL,
-        0x95C11548UL, 0xE4C66D22UL, 0x48C1133FUL, 0xC70F86DCUL,
-        0x07F9C9EEUL, 0x41041F0FUL, 0x404779A4UL, 0x5D886E17UL,
-        0x325F51EBUL, 0xD59BC0D1UL, 0xF2BCC18FUL, 0x41113564UL,
-        0x257B7834UL, 0x602A9C60UL, 0xDFF8E8A3UL, 0x1F636C1BUL,
-        0x0E12B4C2UL, 0x02E1329EUL, 0xAF664FD1UL, 0xCAD18115UL,
-        0x6B2395E0UL, 0x333E92E1UL, 0x3B240B62UL, 0xEEBEB922UL,
-        0x85B2A20EUL, 0xE6BA0D99UL, 0xDE720C8CUL, 0x2DA2F728UL,
-        0xD0127845UL, 0x95B794FDUL, 0x647D0862UL, 0xE7CCF5F0UL,
-        0x5449A36FUL, 0x877D48FAUL, 0xC39DFD27UL, 0xF33E8D1EUL,
-        0x0A476341UL, 0x992EFF74UL, 0x3A6F6EABUL, 0xF4F8FD37UL,
-        0xA812DC60UL, 0xA1EBDDF8UL, 0x991BE14CUL, 0xDB6E6B0DUL,
-        0xC67B5510UL, 0x6D672C37UL, 0x2765D43BUL, 0xDCD0E804UL,
-        0xF1290DC7UL, 0xCC00FFA3UL, 0xB5390F92UL, 0x690FED0BUL,
-        0x667B9FFBUL, 0xCEDB7D9CUL, 0xA091CF0BUL, 0xD9155EA3UL,
-        0xBB132F88UL, 0x515BAD24UL, 0x7B9479BFUL, 0x763BD6EBUL,
-        0x37392EB3UL, 0xCC115979UL, 0x8026E297UL, 0xF42E312DUL,
-        0x6842ADA7UL, 0xC66A2B3BUL, 0x12754CCCUL, 0x782EF11CUL,
-        0x6A124237UL, 0xB79251E7UL, 0x06A1BBE6UL, 0x4BFB6350UL,
-        0x1A6B1018UL, 0x11CAEDFAUL, 0x3D25BDD8UL, 0xE2E1C3C9UL,
-        0x44421659UL, 0x0A121386UL, 0xD90CEC6EUL, 0xD5ABEA2AUL,
-        0x64AF674EUL, 0xDA86A85FUL, 0xBEBFE988UL, 0x64E4C3FEUL,
-        0x9DBC8057UL, 0xF0F7C086UL, 0x60787BF8UL, 0x6003604DUL,
-        0xD1FD8346UL, 0xF6381FB0UL, 0x7745AE04UL, 0xD736FCCCUL,
-        0x83426B33UL, 0xF01EAB71UL, 0xB0804187UL, 0x3C005E5FUL,
-        0x77A057BEUL, 0xBDE8AE24UL, 0x55464299UL, 0xBF582E61UL,
-        0x4E58F48FUL, 0xF2DDFDA2UL, 0xF474EF38UL, 0x8789BDC2UL,
-        0x5366F9C3UL, 0xC8B38E74UL, 0xB475F255UL, 0x46FCD9B9UL,
-        0x7AEB2661UL, 0x8B1DDF84UL, 0x846A0E79UL, 0x915F95E2UL,
-        0x466E598EUL, 0x20B45770UL, 0x8CD55591UL, 0xC902DE4CUL,
-        0xB90BACE1UL, 0xBB8205D0UL, 0x11A86248UL, 0x7574A99EUL,
-        0xB77F19B6UL, 0xE0A9DC09UL, 0x662D09A1UL, 0xC4324633UL,
-        0xE85A1F02UL, 0x09F0BE8CUL, 0x4A99A025UL, 0x1D6EFE10UL,
-        0x1AB93D1DUL, 0x0BA5A4DFUL, 0xA186F20FUL, 0x2868F169UL,
-        0xDCB7DA83UL, 0x573906FEUL, 0xA1E2CE9BUL, 0x4FCD7F52UL,
-        0x50115E01UL, 0xA70683FAUL, 0xA002B5C4UL, 0x0DE6D027UL,
-        0x9AF88C27UL, 0x773F8641UL, 0xC3604C06UL, 0x61A806B5UL,
-        0xF0177A28UL, 0xC0F586E0UL, 0x006058AAUL, 0x30DC7D62UL,
-        0x11E69ED7UL, 0x2338EA63UL, 0x53C2DD94UL, 0xC2C21634UL,
-        0xBBCBEE56UL, 0x90BCB6DEUL, 0xEBFC7DA1UL, 0xCE591D76UL,
-        0x6F05E409UL, 0x4B7C0188UL, 0x39720A3DUL, 0x7C927C24UL,
-        0x86E3725FUL, 0x724D9DB9UL, 0x1AC15BB4UL, 0xD39EB8FCUL,
-        0xED545578UL, 0x08FCA5B5UL, 0xD83D7CD3UL, 0x4DAD0FC4UL,
-        0x1E50EF5EUL, 0xB161E6F8UL, 0xA28514D9UL, 0x6C51133CUL,
-        0x6FD5C7E7UL, 0x56E14EC4UL, 0x362ABFCEUL, 0xDDC6C837UL,
-        0xD79A3234UL, 0x92638212UL, 0x670EFA8EUL, 0x406000E0UL  },
-    {   0x3A39CE37UL, 0xD3FAF5CFUL, 0xABC27737UL, 0x5AC52D1BUL,
-        0x5CB0679EUL, 0x4FA33742UL, 0xD3822740UL, 0x99BC9BBEUL,
-        0xD5118E9DUL, 0xBF0F7315UL, 0xD62D1C7EUL, 0xC700C47BUL,
-        0xB78C1B6BUL, 0x21A19045UL, 0xB26EB1BEUL, 0x6A366EB4UL,
-        0x5748AB2FUL, 0xBC946E79UL, 0xC6A376D2UL, 0x6549C2C8UL,
-        0x530FF8EEUL, 0x468DDE7DUL, 0xD5730A1DUL, 0x4CD04DC6UL,
-        0x2939BBDBUL, 0xA9BA4650UL, 0xAC9526E8UL, 0xBE5EE304UL,
-        0xA1FAD5F0UL, 0x6A2D519AUL, 0x63EF8CE2UL, 0x9A86EE22UL,
-        0xC089C2B8UL, 0x43242EF6UL, 0xA51E03AAUL, 0x9CF2D0A4UL,
-        0x83C061BAUL, 0x9BE96A4DUL, 0x8FE51550UL, 0xBA645BD6UL,
-        0x2826A2F9UL, 0xA73A3AE1UL, 0x4BA99586UL, 0xEF5562E9UL,
-        0xC72FEFD3UL, 0xF752F7DAUL, 0x3F046F69UL, 0x77FA0A59UL,
-        0x80E4A915UL, 0x87B08601UL, 0x9B09E6ADUL, 0x3B3EE593UL,
-        0xE990FD5AUL, 0x9E34D797UL, 0x2CF0B7D9UL, 0x022B8B51UL,
-        0x96D5AC3AUL, 0x017DA67DUL, 0xD1CF3ED6UL, 0x7C7D2D28UL,
-        0x1F9F25CFUL, 0xADF2B89BUL, 0x5AD6B472UL, 0x5A88F54CUL,
-        0xE029AC71UL, 0xE019A5E6UL, 0x47B0ACFDUL, 0xED93FA9BUL,
-        0xE8D3C48DUL, 0x283B57CCUL, 0xF8D56629UL, 0x79132E28UL,
-        0x785F0191UL, 0xED756055UL, 0xF7960E44UL, 0xE3D35E8CUL,
-        0x15056DD4UL, 0x88F46DBAUL, 0x03A16125UL, 0x0564F0BDUL,
-        0xC3EB9E15UL, 0x3C9057A2UL, 0x97271AECUL, 0xA93A072AUL,
-        0x1B3F6D9BUL, 0x1E6321F5UL, 0xF59C66FBUL, 0x26DCF319UL,
-        0x7533D928UL, 0xB155FDF5UL, 0x03563482UL, 0x8ABA3CBBUL,
-        0x28517711UL, 0xC20AD9F8UL, 0xABCC5167UL, 0xCCAD925FUL,
-        0x4DE81751UL, 0x3830DC8EUL, 0x379D5862UL, 0x9320F991UL,
-        0xEA7A90C2UL, 0xFB3E7BCEUL, 0x5121CE64UL, 0x774FBE32UL,
-        0xA8B6E37EUL, 0xC3293D46UL, 0x48DE5369UL, 0x6413E680UL,
-        0xA2AE0810UL, 0xDD6DB224UL, 0x69852DFDUL, 0x09072166UL,
-        0xB39A460AUL, 0x6445C0DDUL, 0x586CDECFUL, 0x1C20C8AEUL,
-        0x5BBEF7DDUL, 0x1B588D40UL, 0xCCD2017FUL, 0x6BB4E3BBUL,
-        0xDDA26A7EUL, 0x3A59FF45UL, 0x3E350A44UL, 0xBCB4CDD5UL,
-        0x72EACEA8UL, 0xFA6484BBUL, 0x8D6612AEUL, 0xBF3C6F47UL,
-        0xD29BE463UL, 0x542F5D9EUL, 0xAEC2771BUL, 0xF64E6370UL,
-        0x740E0D8DUL, 0xE75B1357UL, 0xF8721671UL, 0xAF537D5DUL,
-        0x4040CB08UL, 0x4EB4E2CCUL, 0x34D2466AUL, 0x0115AF84UL,
-        0xE1B00428UL, 0x95983A1DUL, 0x06B89FB4UL, 0xCE6EA048UL,
-        0x6F3F3B82UL, 0x3520AB82UL, 0x011A1D4BUL, 0x277227F8UL,
-        0x611560B1UL, 0xE7933FDCUL, 0xBB3A792BUL, 0x344525BDUL,
-        0xA08839E1UL, 0x51CE794BUL, 0x2F32C9B7UL, 0xA01FBAC9UL,
-        0xE01CC87EUL, 0xBCC7D1F6UL, 0xCF0111C3UL, 0xA1E8AAC7UL,
-        0x1A908749UL, 0xD44FBD9AUL, 0xD0DADECBUL, 0xD50ADA38UL,
-        0x0339C32AUL, 0xC6913667UL, 0x8DF9317CUL, 0xE0B12B4FUL,
-        0xF79E59B7UL, 0x43F5BB3AUL, 0xF2D519FFUL, 0x27D9459CUL,
-        0xBF97222CUL, 0x15E6FC2AUL, 0x0F91FC71UL, 0x9B941525UL,
-        0xFAE59361UL, 0xCEB69CEBUL, 0xC2A86459UL, 0x12BAA8D1UL,
-        0xB6C1075EUL, 0xE3056A0CUL, 0x10D25065UL, 0xCB03A442UL,
-        0xE0EC6E0EUL, 0x1698DB3BUL, 0x4C98A0BEUL, 0x3278E964UL,
-        0x9F1F9532UL, 0xE0D392DFUL, 0xD3A0342BUL, 0x8971F21EUL,
-        0x1B0A7441UL, 0x4BA3348CUL, 0xC5BE7120UL, 0xC37632D8UL,
-        0xDF359F8DUL, 0x9B992F2EUL, 0xE60B6F47UL, 0x0FE3F11DUL,
-        0xE54CDA54UL, 0x1EDAD891UL, 0xCE6279CFUL, 0xCD3E7E6FUL,
-        0x1618B166UL, 0xFD2C1D05UL, 0x848FD2C5UL, 0xF6FB2299UL,
-        0xF523F357UL, 0xA6327623UL, 0x93A83531UL, 0x56CCCD02UL,
-        0xACF08162UL, 0x5A75EBB5UL, 0x6E163697UL, 0x88D273CCUL,
-        0xDE966292UL, 0x81B949D0UL, 0x4C50901BUL, 0x71C65614UL,
-        0xE6C6C7BDUL, 0x327A140AUL, 0x45E1D006UL, 0xC3F27B9AUL,
-        0xC9AA53FDUL, 0x62A80F00UL, 0xBB25BFE2UL, 0x35BDD2F6UL,
-        0x71126905UL, 0xB2040222UL, 0xB6CBCF7CUL, 0xCD769C2BUL,
-        0x53113EC0UL, 0x1640E3D3UL, 0x38ABBD60UL, 0x2547ADF0UL,
-        0xBA38209CUL, 0xF746CE76UL, 0x77AFA1C5UL, 0x20756060UL,
-        0x85CBFE4EUL, 0x8AE88DD8UL, 0x7AAAF9B0UL, 0x4CF9AA7EUL,
-        0x1948C25CUL, 0x02FB8A8CUL, 0x01C36AE4UL, 0xD6EBE1F9UL,
-        0x90D4F869UL, 0xA65CDEA0UL, 0x3F09252DUL, 0xC208E69FUL,
-        0xB74E6132UL, 0xCE77E25BUL, 0x578FDFE3UL, 0x3AC372E6UL  }
-};
-
- /**
-    Initialize the Blowfish block cipher
-    @param key The symmetric key you wish to pass
-    @param keylen The key length in bytes
-    @param num_rounds The number of rounds desired (0 for default)
-    @param skey The key in as scheduled by this function.
-    @return CRYPT_OK if successful
- */
-int blowfish_setup(const unsigned char *key, int keylen, int num_rounds,
-                   symmetric_key *skey)
-{
-   ulong32 x, y, z, A;
-   unsigned char B[8];
-
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(skey != NULL);
-
-   /* check key length */
-   if (keylen < 8 || keylen > 56) {
-      return CRYPT_INVALID_KEYSIZE;
-   }
-
-   /* check rounds */
-   if (num_rounds != 0 && num_rounds != 16) {
-      return CRYPT_INVALID_ROUNDS;
-   }
-
-   /* load in key bytes (Supplied by David Hopwood) */
-   for (x = y = 0; x < 18; x++) {
-       A = 0;
-       for (z = 0; z < 4; z++) {
-           A = (A << 8) | ((ulong32)key[y++] & 255);
-           if (y == (ulong32)keylen) {
-              y = 0;
-           }
-       }
-       skey->blowfish.K[x] = ORIG_P[x] ^ A;
-   }
-
-   /* copy sboxes */
-   for (x = 0; x < 4; x++) {
-       for (y = 0; y < 256; y++) {
-           skey->blowfish.S[x][y] = ORIG_S[x][y];
-       }
-   }
-
-   /* encrypt K array */
-   for (x = 0; x < 8; x++) {
-       B[x] = 0;
-   }
-
-   for (x = 0; x < 18; x += 2) {
-       /* encrypt it */
-       blowfish_ecb_encrypt(B, B, skey);
-       /* copy it */
-       LOAD32H(skey->blowfish.K[x], &B[0]);
-       LOAD32H(skey->blowfish.K[x+1], &B[4]);
-   }
-
-   /* encrypt S array */
-   for (x = 0; x < 4; x++) {
-       for (y = 0; y < 256; y += 2) {
-          /* encrypt it */
-          blowfish_ecb_encrypt(B, B, skey);
-          /* copy it */
-          LOAD32H(skey->blowfish.S[x][y], &B[0]);
-          LOAD32H(skey->blowfish.S[x][y+1], &B[4]);
-       }
-   }
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(B, sizeof(B));
-#endif
-
-   return CRYPT_OK;
-}
-
-#ifndef __GNUC__
-#define F(x) ((S1[byte(x,3)] + S2[byte(x,2)]) ^ S3[byte(x,1)]) + S4[byte(x,0)]
-#else
-#define F(x) ((skey->blowfish.S[0][byte(x,3)] + skey->blowfish.S[1][byte(x,2)]) ^ skey->blowfish.S[2][byte(x,1)]) + skey->blowfish.S[3][byte(x,0)]
-#endif
-
-/**
-  Encrypts a block of text with Blowfish
-  @param pt The input plaintext (8 bytes)
-  @param ct The output ciphertext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-#ifdef LTC_CLEAN_STACK
-static int _blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-#else
-int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-#endif
-{
-   ulong32 L, R;
-   int r;
-#ifndef __GNUC__
-   ulong32 *S1, *S2, *S3, *S4;
-#endif
-
-    LTC_ARGCHK(pt   != NULL);
-    LTC_ARGCHK(ct   != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-#ifndef __GNUC__
-    S1 = skey->blowfish.S[0];
-    S2 = skey->blowfish.S[1];
-    S3 = skey->blowfish.S[2];
-    S4 = skey->blowfish.S[3];
-#endif
-
-   /* load it */
-   LOAD32H(L, &pt[0]);
-   LOAD32H(R, &pt[4]);
-
-   /* do 16 rounds */
-   for (r = 0; r < 16; ) {
-      L ^= skey->blowfish.K[r++];  R ^= F(L);
-      R ^= skey->blowfish.K[r++];  L ^= F(R);
-      L ^= skey->blowfish.K[r++];  R ^= F(L);
-      R ^= skey->blowfish.K[r++];  L ^= F(R);
-   }
-
-   /* last keying */
-   R ^= skey->blowfish.K[17];
-   L ^= skey->blowfish.K[16];
-
-   /* store */
-   STORE32H(R, &ct[0]);
-   STORE32H(L, &ct[4]);
-
-   return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-{
-    int err = _blowfish_ecb_encrypt(pt, ct, skey);
-    burn_stack(sizeof(ulong32) * 2 + sizeof(int));
-    return err;
-}
-#endif
-
-/**
-  Decrypts a block of text with Blowfish
-  @param ct The input ciphertext (8 bytes)
-  @param pt The output plaintext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-#ifdef LTC_CLEAN_STACK
-static int _blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-#else
-int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-#endif
-{
-   ulong32 L, R;
-   int r;
-#ifndef __GNUC__
-   ulong32 *S1, *S2, *S3, *S4;
-#endif
-
-    LTC_ARGCHK(pt   != NULL);
-    LTC_ARGCHK(ct   != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-#ifndef __GNUC__
-    S1 = skey->blowfish.S[0];
-    S2 = skey->blowfish.S[1];
-    S3 = skey->blowfish.S[2];
-    S4 = skey->blowfish.S[3];
-#endif
-
-   /* load it */
-   LOAD32H(R, &ct[0]);
-   LOAD32H(L, &ct[4]);
-
-   /* undo last keying */
-   R ^= skey->blowfish.K[17];
-   L ^= skey->blowfish.K[16];
-
-   /* do 16 rounds */
-   for (r = 15; r > 0; ) {
-      L ^= F(R); R ^= skey->blowfish.K[r--];
-      R ^= F(L); L ^= skey->blowfish.K[r--];
-      L ^= F(R); R ^= skey->blowfish.K[r--];
-      R ^= F(L); L ^= skey->blowfish.K[r--];
-   }
-
-   /* store */
-   STORE32H(L, &pt[0]);
-   STORE32H(R, &pt[4]);
-   return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-{
-    int err = _blowfish_ecb_decrypt(ct, pt, skey);
-    burn_stack(sizeof(ulong32) * 2 + sizeof(int));
-    return err;
-}
-#endif
-
-
-/**
-  Performs a self-test of the Blowfish block cipher
-  @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
-*/
-int blowfish_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-   int err;
-   symmetric_key key;
-   static const struct {
-          unsigned char key[8], pt[8], ct[8];
-   } tests[] = {
-       {
-           { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-           { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-           { 0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}
-       },
-       {
-           { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
-           { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
-           { 0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}
-       },
-       {
-           { 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-           { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
-           { 0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}
-       }
-   };
-   unsigned char tmp[2][8];
-   int x, y;
-
-   for (x = 0; x < (int)(sizeof(tests) / sizeof(tests[0])); x++) {
-      /* setup key */
-      if ((err = blowfish_setup(tests[x].key, 8, 16, &key)) != CRYPT_OK) {
-         return err;
-      }
-
-      /* encrypt and decrypt */
-      blowfish_ecb_encrypt(tests[x].pt, tmp[0], &key);
-      blowfish_ecb_decrypt(tmp[0], tmp[1], &key);
-
-      /* compare */
-      if ((compare_testvector(tmp[0], 8, tests[x].ct, 8, "Blowfish Encrypt", x) != 0) ||
-            (compare_testvector(tmp[1], 8, tests[x].pt, 8, "Blowfish Decrypt", x) != 0)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-
-      /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */
-      for (y = 0; y < 8; y++) tmp[0][y] = 0;
-      for (y = 0; y < 1000; y++) blowfish_ecb_encrypt(tmp[0], tmp[0], &key);
-      for (y = 0; y < 1000; y++) blowfish_ecb_decrypt(tmp[0], tmp[0], &key);
-      for (y = 0; y < 8; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR;
-   }
-   return CRYPT_OK;
- #endif
-}
-
-/** Terminate the context
-   @param skey    The scheduled key
-*/
-void blowfish_done(symmetric_key *skey)
-{
-  LTC_UNUSED_PARAM(skey);
-}
-
-/**
-  Gets suitable key size
-  @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.
-  @return CRYPT_OK if the input key size is acceptable.
-*/
-int blowfish_keysize(int *keysize)
-{
-   LTC_ARGCHK(keysize != NULL);
-
-   if (*keysize < 8) {
-      return CRYPT_INVALID_KEYSIZE;
-   } else if (*keysize > 56) {
-      *keysize = 56;
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/ciphers/des.c b/libs/tomcrypt/src/ciphers/des.c
deleted file mode 100644
index 5c6c85f5087e..000000000000
--- a/libs/tomcrypt/src/ciphers/des.c
+++ /dev/null
@@ -1,2081 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file des.c
-  DES code submitted by Dobes Vandermeer
-*/
-
-#ifdef LTC_DES
-
-#define EN0 0
-#define DE1 1
-
-const struct ltc_cipher_descriptor des_desc =
-{
-    "des",
-    13,
-    8, 8, 8, 16,
-    &des_setup,
-    &des_ecb_encrypt,
-    &des_ecb_decrypt,
-    &des_test,
-    &des_done,
-    &des_keysize,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-const struct ltc_cipher_descriptor des3_desc =
-{
-    "3des",
-    14,
-    16, 24, 8, 16,
-    &des3_setup,
-    &des3_ecb_encrypt,
-    &des3_ecb_decrypt,
-    &des3_test,
-    &des3_done,
-    &des3_keysize,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-static const ulong32 bytebit[8] =
-{
-    0200, 0100, 040, 020, 010, 04, 02, 01
-};
-
-static const ulong32 bigbyte[24] =
-{
-    0x800000UL,  0x400000UL,  0x200000UL,  0x100000UL,
-    0x80000UL,   0x40000UL,   0x20000UL,   0x10000UL,
-    0x8000UL,    0x4000UL,    0x2000UL,    0x1000UL,
-    0x800UL,     0x400UL,     0x200UL,     0x100UL,
-    0x80UL,      0x40UL,      0x20UL,      0x10UL,
-    0x8UL,       0x4UL,       0x2UL,       0x1L
-};
-
-/* Use the key schedule specific in the standard (ANSI X3.92-1981) */
-
-static const unsigned char pc1[56] = {
-    56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,
-     9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35,
-    62, 54, 46, 38, 30, 22, 14,  6, 61, 53, 45, 37, 29, 21,
-    13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3
-};
-
-static const unsigned char totrot[16] = {
-    1,   2,  4,  6,
-    8,  10, 12, 14,
-    15, 17, 19, 21,
-    23, 25, 27, 28
-};
-
-static const unsigned char pc2[48] = {
-    13, 16, 10, 23,  0,  4,      2, 27, 14,  5, 20,  9,
-    22, 18, 11,  3, 25,  7,     15,  6, 26, 19, 12,  1,
-    40, 51, 30, 36, 46, 54,     29, 39, 50, 44, 32, 47,
-    43, 48, 38, 55, 33, 52,     45, 41, 49, 35, 28, 31
-};
-
-
-static const ulong32 SP1[64] =
-{
-    0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL,
-    0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL,
-    0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL,
-    0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL,
-    0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL,
-    0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL,
-    0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL,
-    0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL,
-    0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL,
-    0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL,
-    0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL,
-    0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL,
-    0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL,
-    0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL,
-    0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL,
-    0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL
-};
-
-static const ulong32 SP2[64] =
-{
-    0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL,
-    0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL,
-    0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL,
-    0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL,
-    0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL,
-    0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL,
-    0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL,
-    0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL,
-    0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL,
-    0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL,
-    0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL,
-    0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL,
-    0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL,
-    0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL,
-    0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL,
-    0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL
-};
-
-static const ulong32 SP3[64] =
-{
-    0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL,
-    0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL,
-    0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL,
-    0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL,
-    0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL,
-    0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL,
-    0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL,
-    0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL,
-    0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL,
-    0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL,
-    0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL,
-    0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL,
-    0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL,
-    0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL,
-    0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL,
-    0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL
-};
-
-static const ulong32 SP4[64] =
-{
-    0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
-    0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL,
-    0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL,
-    0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL,
-    0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL,
-    0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL,
-    0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL,
-    0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL,
-    0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL,
-    0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL,
-    0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL,
-    0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
-    0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL,
-    0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL,
-    0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL,
-    0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL
-};
-
-static const ulong32 SP5[64] =
-{
-    0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL,
-    0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL,
-    0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL,
-    0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL,
-    0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL,
-    0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL,
-    0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL,
-    0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL,
-    0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL,
-    0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL,
-    0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL,
-    0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL,
-    0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL,
-    0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL,
-    0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL,
-    0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL
-};
-
-static const ulong32 SP6[64] =
-{
-    0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL,
-    0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL,
-    0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL,
-    0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
-    0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL,
-    0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL,
-    0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL,
-    0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL,
-    0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL,
-    0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL,
-    0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
-    0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL,
-    0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL,
-    0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL,
-    0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL,
-    0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL
-};
-
-static const ulong32 SP7[64] =
-{
-    0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL,
-    0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL,
-    0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL,
-    0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL,
-    0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL,
-    0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL,
-    0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL,
-    0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL,
-    0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL,
-    0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL,
-    0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL,
-    0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL,
-    0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL,
-    0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL,
-    0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL,
-    0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL
-};
-
-static const ulong32 SP8[64] =
-{
-    0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL,
-    0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL,
-    0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL,
-    0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL,
-    0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL,
-    0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL,
-    0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL,
-    0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL,
-    0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL,
-    0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL,
-    0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL,
-    0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL,
-    0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL,
-    0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL,
-    0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL,
-    0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL
-};
-
-#ifndef LTC_SMALL_CODE
-
-static const ulong64 des_ip[8][256] = {
-
-{ CONST64(0x0000000000000000), CONST64(0x0000001000000000), CONST64(0x0000000000000010), CONST64(0x0000001000000010),
-  CONST64(0x0000100000000000), CONST64(0x0000101000000000), CONST64(0x0000100000000010), CONST64(0x0000101000000010),
-  CONST64(0x0000000000001000), CONST64(0x0000001000001000), CONST64(0x0000000000001010), CONST64(0x0000001000001010),
-  CONST64(0x0000100000001000), CONST64(0x0000101000001000), CONST64(0x0000100000001010), CONST64(0x0000101000001010),
-  CONST64(0x0010000000000000), CONST64(0x0010001000000000), CONST64(0x0010000000000010), CONST64(0x0010001000000010),
-  CONST64(0x0010100000000000), CONST64(0x0010101000000000), CONST64(0x0010100000000010), CONST64(0x0010101000000010),
-  CONST64(0x0010000000001000), CONST64(0x0010001000001000), CONST64(0x0010000000001010), CONST64(0x0010001000001010),
-  CONST64(0x0010100000001000), CONST64(0x0010101000001000), CONST64(0x0010100000001010), CONST64(0x0010101000001010),
-  CONST64(0x0000000000100000), CONST64(0x0000001000100000), CONST64(0x0000000000100010), CONST64(0x0000001000100010),
-  CONST64(0x0000100000100000), CONST64(0x0000101000100000), CONST64(0x0000100000100010), CONST64(0x0000101000100010),
-  CONST64(0x0000000000101000), CONST64(0x0000001000101000), CONST64(0x0000000000101010), CONST64(0x0000001000101010),
-  CONST64(0x0000100000101000), CONST64(0x0000101000101000), CONST64(0x0000100000101010), CONST64(0x0000101000101010),
-  CONST64(0x0010000000100000), CONST64(0x0010001000100000), CONST64(0x0010000000100010), CONST64(0x0010001000100010),
-  CONST64(0x0010100000100000), CONST64(0x0010101000100000), CONST64(0x0010100000100010), CONST64(0x0010101000100010),
-  CONST64(0x0010000000101000), CONST64(0x0010001000101000), CONST64(0x0010000000101010), CONST64(0x0010001000101010),
-  CONST64(0x0010100000101000), CONST64(0x0010101000101000), CONST64(0x0010100000101010), CONST64(0x0010101000101010),
-  CONST64(0x1000000000000000), CONST64(0x1000001000000000), CONST64(0x1000000000000010), CONST64(0x1000001000000010),
-  CONST64(0x1000100000000000), CONST64(0x1000101000000000), CONST64(0x1000100000000010), CONST64(0x1000101000000010),
-  CONST64(0x1000000000001000), CONST64(0x1000001000001000), CONST64(0x1000000000001010), CONST64(0x1000001000001010),
-  CONST64(0x1000100000001000), CONST64(0x1000101000001000), CONST64(0x1000100000001010), CONST64(0x1000101000001010),
-  CONST64(0x1010000000000000), CONST64(0x1010001000000000), CONST64(0x1010000000000010), CONST64(0x1010001000000010),
-  CONST64(0x1010100000000000), CONST64(0x1010101000000000), CONST64(0x1010100000000010), CONST64(0x1010101000000010),
-  CONST64(0x1010000000001000), CONST64(0x1010001000001000), CONST64(0x1010000000001010), CONST64(0x1010001000001010),
-  CONST64(0x1010100000001000), CONST64(0x1010101000001000), CONST64(0x1010100000001010), CONST64(0x1010101000001010),
-  CONST64(0x1000000000100000), CONST64(0x1000001000100000), CONST64(0x1000000000100010), CONST64(0x1000001000100010),
-  CONST64(0x1000100000100000), CONST64(0x1000101000100000), CONST64(0x1000100000100010), CONST64(0x1000101000100010),
-  CONST64(0x1000000000101000), CONST64(0x1000001000101000), CONST64(0x1000000000101010), CONST64(0x1000001000101010),
-  CONST64(0x1000100000101000), CONST64(0x1000101000101000), CONST64(0x1000100000101010), CONST64(0x1000101000101010),
-  CONST64(0x1010000000100000), CONST64(0x1010001000100000), CONST64(0x1010000000100010), CONST64(0x1010001000100010),
-  CONST64(0x1010100000100000), CONST64(0x1010101000100000), CONST64(0x1010100000100010), CONST64(0x1010101000100010),
-  CONST64(0x1010000000101000), CONST64(0x1010001000101000), CONST64(0x1010000000101010), CONST64(0x1010001000101010),
-  CONST64(0x1010100000101000), CONST64(0x1010101000101000), CONST64(0x1010100000101010), CONST64(0x1010101000101010),
-  CONST64(0x0000000010000000), CONST64(0x0000001010000000), CONST64(0x0000000010000010), CONST64(0x0000001010000010),
-  CONST64(0x0000100010000000), CONST64(0x0000101010000000), CONST64(0x0000100010000010), CONST64(0x0000101010000010),
-  CONST64(0x0000000010001000), CONST64(0x0000001010001000), CONST64(0x0000000010001010), CONST64(0x0000001010001010),
-  CONST64(0x0000100010001000), CONST64(0x0000101010001000), CONST64(0x0000100010001010), CONST64(0x0000101010001010),
-  CONST64(0x0010000010000000), CONST64(0x0010001010000000), CONST64(0x0010000010000010), CONST64(0x0010001010000010),
-  CONST64(0x0010100010000000), CONST64(0x0010101010000000), CONST64(0x0010100010000010), CONST64(0x0010101010000010),
-  CONST64(0x0010000010001000), CONST64(0x0010001010001000), CONST64(0x0010000010001010), CONST64(0x0010001010001010),
-  CONST64(0x0010100010001000), CONST64(0x0010101010001000), CONST64(0x0010100010001010), CONST64(0x0010101010001010),
-  CONST64(0x0000000010100000), CONST64(0x0000001010100000), CONST64(0x0000000010100010), CONST64(0x0000001010100010),
-  CONST64(0x0000100010100000), CONST64(0x0000101010100000), CONST64(0x0000100010100010), CONST64(0x0000101010100010),
-  CONST64(0x0000000010101000), CONST64(0x0000001010101000), CONST64(0x0000000010101010), CONST64(0x0000001010101010),
-  CONST64(0x0000100010101000), CONST64(0x0000101010101000), CONST64(0x0000100010101010), CONST64(0x0000101010101010),
-  CONST64(0x0010000010100000), CONST64(0x0010001010100000), CONST64(0x0010000010100010), CONST64(0x0010001010100010),
-  CONST64(0x0010100010100000), CONST64(0x0010101010100000), CONST64(0x0010100010100010), CONST64(0x0010101010100010),
-  CONST64(0x0010000010101000), CONST64(0x0010001010101000), CONST64(0x0010000010101010), CONST64(0x0010001010101010),
-  CONST64(0x0010100010101000), CONST64(0x0010101010101000), CONST64(0x0010100010101010), CONST64(0x0010101010101010),
-  CONST64(0x1000000010000000), CONST64(0x1000001010000000), CONST64(0x1000000010000010), CONST64(0x1000001010000010),
-  CONST64(0x1000100010000000), CONST64(0x1000101010000000), CONST64(0x1000100010000010), CONST64(0x1000101010000010),
-  CONST64(0x1000000010001000), CONST64(0x1000001010001000), CONST64(0x1000000010001010), CONST64(0x1000001010001010),
-  CONST64(0x1000100010001000), CONST64(0x1000101010001000), CONST64(0x1000100010001010), CONST64(0x1000101010001010),
-  CONST64(0x1010000010000000), CONST64(0x1010001010000000), CONST64(0x1010000010000010), CONST64(0x1010001010000010),
-  CONST64(0x1010100010000000), CONST64(0x1010101010000000), CONST64(0x1010100010000010), CONST64(0x1010101010000010),
-  CONST64(0x1010000010001000), CONST64(0x1010001010001000), CONST64(0x1010000010001010), CONST64(0x1010001010001010),
-  CONST64(0x1010100010001000), CONST64(0x1010101010001000), CONST64(0x1010100010001010), CONST64(0x1010101010001010),
-  CONST64(0x1000000010100000), CONST64(0x1000001010100000), CONST64(0x1000000010100010), CONST64(0x1000001010100010),
-  CONST64(0x1000100010100000), CONST64(0x1000101010100000), CONST64(0x1000100010100010), CONST64(0x1000101010100010),
-  CONST64(0x1000000010101000), CONST64(0x1000001010101000), CONST64(0x1000000010101010), CONST64(0x1000001010101010),
-  CONST64(0x1000100010101000), CONST64(0x1000101010101000), CONST64(0x1000100010101010), CONST64(0x1000101010101010),
-  CONST64(0x1010000010100000), CONST64(0x1010001010100000), CONST64(0x1010000010100010), CONST64(0x1010001010100010),
-  CONST64(0x1010100010100000), CONST64(0x1010101010100000), CONST64(0x1010100010100010), CONST64(0x1010101010100010),
-  CONST64(0x1010000010101000), CONST64(0x1010001010101000), CONST64(0x1010000010101010), CONST64(0x1010001010101010),
-  CONST64(0x1010100010101000), CONST64(0x1010101010101000), CONST64(0x1010100010101010), CONST64(0x1010101010101010)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000800000000), CONST64(0x0000000000000008), CONST64(0x0000000800000008),
-  CONST64(0x0000080000000000), CONST64(0x0000080800000000), CONST64(0x0000080000000008), CONST64(0x0000080800000008),
-  CONST64(0x0000000000000800), CONST64(0x0000000800000800), CONST64(0x0000000000000808), CONST64(0x0000000800000808),
-  CONST64(0x0000080000000800), CONST64(0x0000080800000800), CONST64(0x0000080000000808), CONST64(0x0000080800000808),
-  CONST64(0x0008000000000000), CONST64(0x0008000800000000), CONST64(0x0008000000000008), CONST64(0x0008000800000008),
-  CONST64(0x0008080000000000), CONST64(0x0008080800000000), CONST64(0x0008080000000008), CONST64(0x0008080800000008),
-  CONST64(0x0008000000000800), CONST64(0x0008000800000800), CONST64(0x0008000000000808), CONST64(0x0008000800000808),
-  CONST64(0x0008080000000800), CONST64(0x0008080800000800), CONST64(0x0008080000000808), CONST64(0x0008080800000808),
-  CONST64(0x0000000000080000), CONST64(0x0000000800080000), CONST64(0x0000000000080008), CONST64(0x0000000800080008),
-  CONST64(0x0000080000080000), CONST64(0x0000080800080000), CONST64(0x0000080000080008), CONST64(0x0000080800080008),
-  CONST64(0x0000000000080800), CONST64(0x0000000800080800), CONST64(0x0000000000080808), CONST64(0x0000000800080808),
-  CONST64(0x0000080000080800), CONST64(0x0000080800080800), CONST64(0x0000080000080808), CONST64(0x0000080800080808),
-  CONST64(0x0008000000080000), CONST64(0x0008000800080000), CONST64(0x0008000000080008), CONST64(0x0008000800080008),
-  CONST64(0x0008080000080000), CONST64(0x0008080800080000), CONST64(0x0008080000080008), CONST64(0x0008080800080008),
-  CONST64(0x0008000000080800), CONST64(0x0008000800080800), CONST64(0x0008000000080808), CONST64(0x0008000800080808),
-  CONST64(0x0008080000080800), CONST64(0x0008080800080800), CONST64(0x0008080000080808), CONST64(0x0008080800080808),
-  CONST64(0x0800000000000000), CONST64(0x0800000800000000), CONST64(0x0800000000000008), CONST64(0x0800000800000008),
-  CONST64(0x0800080000000000), CONST64(0x0800080800000000), CONST64(0x0800080000000008), CONST64(0x0800080800000008),
-  CONST64(0x0800000000000800), CONST64(0x0800000800000800), CONST64(0x0800000000000808), CONST64(0x0800000800000808),
-  CONST64(0x0800080000000800), CONST64(0x0800080800000800), CONST64(0x0800080000000808), CONST64(0x0800080800000808),
-  CONST64(0x0808000000000000), CONST64(0x0808000800000000), CONST64(0x0808000000000008), CONST64(0x0808000800000008),
-  CONST64(0x0808080000000000), CONST64(0x0808080800000000), CONST64(0x0808080000000008), CONST64(0x0808080800000008),
-  CONST64(0x0808000000000800), CONST64(0x0808000800000800), CONST64(0x0808000000000808), CONST64(0x0808000800000808),
-  CONST64(0x0808080000000800), CONST64(0x0808080800000800), CONST64(0x0808080000000808), CONST64(0x0808080800000808),
-  CONST64(0x0800000000080000), CONST64(0x0800000800080000), CONST64(0x0800000000080008), CONST64(0x0800000800080008),
-  CONST64(0x0800080000080000), CONST64(0x0800080800080000), CONST64(0x0800080000080008), CONST64(0x0800080800080008),
-  CONST64(0x0800000000080800), CONST64(0x0800000800080800), CONST64(0x0800000000080808), CONST64(0x0800000800080808),
-  CONST64(0x0800080000080800), CONST64(0x0800080800080800), CONST64(0x0800080000080808), CONST64(0x0800080800080808),
-  CONST64(0x0808000000080000), CONST64(0x0808000800080000), CONST64(0x0808000000080008), CONST64(0x0808000800080008),
-  CONST64(0x0808080000080000), CONST64(0x0808080800080000), CONST64(0x0808080000080008), CONST64(0x0808080800080008),
-  CONST64(0x0808000000080800), CONST64(0x0808000800080800), CONST64(0x0808000000080808), CONST64(0x0808000800080808),
-  CONST64(0x0808080000080800), CONST64(0x0808080800080800), CONST64(0x0808080000080808), CONST64(0x0808080800080808),
-  CONST64(0x0000000008000000), CONST64(0x0000000808000000), CONST64(0x0000000008000008), CONST64(0x0000000808000008),
-  CONST64(0x0000080008000000), CONST64(0x0000080808000000), CONST64(0x0000080008000008), CONST64(0x0000080808000008),
-  CONST64(0x0000000008000800), CONST64(0x0000000808000800), CONST64(0x0000000008000808), CONST64(0x0000000808000808),
-  CONST64(0x0000080008000800), CONST64(0x0000080808000800), CONST64(0x0000080008000808), CONST64(0x0000080808000808),
-  CONST64(0x0008000008000000), CONST64(0x0008000808000000), CONST64(0x0008000008000008), CONST64(0x0008000808000008),
-  CONST64(0x0008080008000000), CONST64(0x0008080808000000), CONST64(0x0008080008000008), CONST64(0x0008080808000008),
-  CONST64(0x0008000008000800), CONST64(0x0008000808000800), CONST64(0x0008000008000808), CONST64(0x0008000808000808),
-  CONST64(0x0008080008000800), CONST64(0x0008080808000800), CONST64(0x0008080008000808), CONST64(0x0008080808000808),
-  CONST64(0x0000000008080000), CONST64(0x0000000808080000), CONST64(0x0000000008080008), CONST64(0x0000000808080008),
-  CONST64(0x0000080008080000), CONST64(0x0000080808080000), CONST64(0x0000080008080008), CONST64(0x0000080808080008),
-  CONST64(0x0000000008080800), CONST64(0x0000000808080800), CONST64(0x0000000008080808), CONST64(0x0000000808080808),
-  CONST64(0x0000080008080800), CONST64(0x0000080808080800), CONST64(0x0000080008080808), CONST64(0x0000080808080808),
-  CONST64(0x0008000008080000), CONST64(0x0008000808080000), CONST64(0x0008000008080008), CONST64(0x0008000808080008),
-  CONST64(0x0008080008080000), CONST64(0x0008080808080000), CONST64(0x0008080008080008), CONST64(0x0008080808080008),
-  CONST64(0x0008000008080800), CONST64(0x0008000808080800), CONST64(0x0008000008080808), CONST64(0x0008000808080808),
-  CONST64(0x0008080008080800), CONST64(0x0008080808080800), CONST64(0x0008080008080808), CONST64(0x0008080808080808),
-  CONST64(0x0800000008000000), CONST64(0x0800000808000000), CONST64(0x0800000008000008), CONST64(0x0800000808000008),
-  CONST64(0x0800080008000000), CONST64(0x0800080808000000), CONST64(0x0800080008000008), CONST64(0x0800080808000008),
-  CONST64(0x0800000008000800), CONST64(0x0800000808000800), CONST64(0x0800000008000808), CONST64(0x0800000808000808),
-  CONST64(0x0800080008000800), CONST64(0x0800080808000800), CONST64(0x0800080008000808), CONST64(0x0800080808000808),
-  CONST64(0x0808000008000000), CONST64(0x0808000808000000), CONST64(0x0808000008000008), CONST64(0x0808000808000008),
-  CONST64(0x0808080008000000), CONST64(0x0808080808000000), CONST64(0x0808080008000008), CONST64(0x0808080808000008),
-  CONST64(0x0808000008000800), CONST64(0x0808000808000800), CONST64(0x0808000008000808), CONST64(0x0808000808000808),
-  CONST64(0x0808080008000800), CONST64(0x0808080808000800), CONST64(0x0808080008000808), CONST64(0x0808080808000808),
-  CONST64(0x0800000008080000), CONST64(0x0800000808080000), CONST64(0x0800000008080008), CONST64(0x0800000808080008),
-  CONST64(0x0800080008080000), CONST64(0x0800080808080000), CONST64(0x0800080008080008), CONST64(0x0800080808080008),
-  CONST64(0x0800000008080800), CONST64(0x0800000808080800), CONST64(0x0800000008080808), CONST64(0x0800000808080808),
-  CONST64(0x0800080008080800), CONST64(0x0800080808080800), CONST64(0x0800080008080808), CONST64(0x0800080808080808),
-  CONST64(0x0808000008080000), CONST64(0x0808000808080000), CONST64(0x0808000008080008), CONST64(0x0808000808080008),
-  CONST64(0x0808080008080000), CONST64(0x0808080808080000), CONST64(0x0808080008080008), CONST64(0x0808080808080008),
-  CONST64(0x0808000008080800), CONST64(0x0808000808080800), CONST64(0x0808000008080808), CONST64(0x0808000808080808),
-  CONST64(0x0808080008080800), CONST64(0x0808080808080800), CONST64(0x0808080008080808), CONST64(0x0808080808080808)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000400000000), CONST64(0x0000000000000004), CONST64(0x0000000400000004),
-  CONST64(0x0000040000000000), CONST64(0x0000040400000000), CONST64(0x0000040000000004), CONST64(0x0000040400000004),
-  CONST64(0x0000000000000400), CONST64(0x0000000400000400), CONST64(0x0000000000000404), CONST64(0x0000000400000404),
-  CONST64(0x0000040000000400), CONST64(0x0000040400000400), CONST64(0x0000040000000404), CONST64(0x0000040400000404),
-  CONST64(0x0004000000000000), CONST64(0x0004000400000000), CONST64(0x0004000000000004), CONST64(0x0004000400000004),
-  CONST64(0x0004040000000000), CONST64(0x0004040400000000), CONST64(0x0004040000000004), CONST64(0x0004040400000004),
-  CONST64(0x0004000000000400), CONST64(0x0004000400000400), CONST64(0x0004000000000404), CONST64(0x0004000400000404),
-  CONST64(0x0004040000000400), CONST64(0x0004040400000400), CONST64(0x0004040000000404), CONST64(0x0004040400000404),
-  CONST64(0x0000000000040000), CONST64(0x0000000400040000), CONST64(0x0000000000040004), CONST64(0x0000000400040004),
-  CONST64(0x0000040000040000), CONST64(0x0000040400040000), CONST64(0x0000040000040004), CONST64(0x0000040400040004),
-  CONST64(0x0000000000040400), CONST64(0x0000000400040400), CONST64(0x0000000000040404), CONST64(0x0000000400040404),
-  CONST64(0x0000040000040400), CONST64(0x0000040400040400), CONST64(0x0000040000040404), CONST64(0x0000040400040404),
-  CONST64(0x0004000000040000), CONST64(0x0004000400040000), CONST64(0x0004000000040004), CONST64(0x0004000400040004),
-  CONST64(0x0004040000040000), CONST64(0x0004040400040000), CONST64(0x0004040000040004), CONST64(0x0004040400040004),
-  CONST64(0x0004000000040400), CONST64(0x0004000400040400), CONST64(0x0004000000040404), CONST64(0x0004000400040404),
-  CONST64(0x0004040000040400), CONST64(0x0004040400040400), CONST64(0x0004040000040404), CONST64(0x0004040400040404),
-  CONST64(0x0400000000000000), CONST64(0x0400000400000000), CONST64(0x0400000000000004), CONST64(0x0400000400000004),
-  CONST64(0x0400040000000000), CONST64(0x0400040400000000), CONST64(0x0400040000000004), CONST64(0x0400040400000004),
-  CONST64(0x0400000000000400), CONST64(0x0400000400000400), CONST64(0x0400000000000404), CONST64(0x0400000400000404),
-  CONST64(0x0400040000000400), CONST64(0x0400040400000400), CONST64(0x0400040000000404), CONST64(0x0400040400000404),
-  CONST64(0x0404000000000000), CONST64(0x0404000400000000), CONST64(0x0404000000000004), CONST64(0x0404000400000004),
-  CONST64(0x0404040000000000), CONST64(0x0404040400000000), CONST64(0x0404040000000004), CONST64(0x0404040400000004),
-  CONST64(0x0404000000000400), CONST64(0x0404000400000400), CONST64(0x0404000000000404), CONST64(0x0404000400000404),
-  CONST64(0x0404040000000400), CONST64(0x0404040400000400), CONST64(0x0404040000000404), CONST64(0x0404040400000404),
-  CONST64(0x0400000000040000), CONST64(0x0400000400040000), CONST64(0x0400000000040004), CONST64(0x0400000400040004),
-  CONST64(0x0400040000040000), CONST64(0x0400040400040000), CONST64(0x0400040000040004), CONST64(0x0400040400040004),
-  CONST64(0x0400000000040400), CONST64(0x0400000400040400), CONST64(0x0400000000040404), CONST64(0x0400000400040404),
-  CONST64(0x0400040000040400), CONST64(0x0400040400040400), CONST64(0x0400040000040404), CONST64(0x0400040400040404),
-  CONST64(0x0404000000040000), CONST64(0x0404000400040000), CONST64(0x0404000000040004), CONST64(0x0404000400040004),
-  CONST64(0x0404040000040000), CONST64(0x0404040400040000), CONST64(0x0404040000040004), CONST64(0x0404040400040004),
-  CONST64(0x0404000000040400), CONST64(0x0404000400040400), CONST64(0x0404000000040404), CONST64(0x0404000400040404),
-  CONST64(0x0404040000040400), CONST64(0x0404040400040400), CONST64(0x0404040000040404), CONST64(0x0404040400040404),
-  CONST64(0x0000000004000000), CONST64(0x0000000404000000), CONST64(0x0000000004000004), CONST64(0x0000000404000004),
-  CONST64(0x0000040004000000), CONST64(0x0000040404000000), CONST64(0x0000040004000004), CONST64(0x0000040404000004),
-  CONST64(0x0000000004000400), CONST64(0x0000000404000400), CONST64(0x0000000004000404), CONST64(0x0000000404000404),
-  CONST64(0x0000040004000400), CONST64(0x0000040404000400), CONST64(0x0000040004000404), CONST64(0x0000040404000404),
-  CONST64(0x0004000004000000), CONST64(0x0004000404000000), CONST64(0x0004000004000004), CONST64(0x0004000404000004),
-  CONST64(0x0004040004000000), CONST64(0x0004040404000000), CONST64(0x0004040004000004), CONST64(0x0004040404000004),
-  CONST64(0x0004000004000400), CONST64(0x0004000404000400), CONST64(0x0004000004000404), CONST64(0x0004000404000404),
-  CONST64(0x0004040004000400), CONST64(0x0004040404000400), CONST64(0x0004040004000404), CONST64(0x0004040404000404),
-  CONST64(0x0000000004040000), CONST64(0x0000000404040000), CONST64(0x0000000004040004), CONST64(0x0000000404040004),
-  CONST64(0x0000040004040000), CONST64(0x0000040404040000), CONST64(0x0000040004040004), CONST64(0x0000040404040004),
-  CONST64(0x0000000004040400), CONST64(0x0000000404040400), CONST64(0x0000000004040404), CONST64(0x0000000404040404),
-  CONST64(0x0000040004040400), CONST64(0x0000040404040400), CONST64(0x0000040004040404), CONST64(0x0000040404040404),
-  CONST64(0x0004000004040000), CONST64(0x0004000404040000), CONST64(0x0004000004040004), CONST64(0x0004000404040004),
-  CONST64(0x0004040004040000), CONST64(0x0004040404040000), CONST64(0x0004040004040004), CONST64(0x0004040404040004),
-  CONST64(0x0004000004040400), CONST64(0x0004000404040400), CONST64(0x0004000004040404), CONST64(0x0004000404040404),
-  CONST64(0x0004040004040400), CONST64(0x0004040404040400), CONST64(0x0004040004040404), CONST64(0x0004040404040404),
-  CONST64(0x0400000004000000), CONST64(0x0400000404000000), CONST64(0x0400000004000004), CONST64(0x0400000404000004),
-  CONST64(0x0400040004000000), CONST64(0x0400040404000000), CONST64(0x0400040004000004), CONST64(0x0400040404000004),
-  CONST64(0x0400000004000400), CONST64(0x0400000404000400), CONST64(0x0400000004000404), CONST64(0x0400000404000404),
-  CONST64(0x0400040004000400), CONST64(0x0400040404000400), CONST64(0x0400040004000404), CONST64(0x0400040404000404),
-  CONST64(0x0404000004000000), CONST64(0x0404000404000000), CONST64(0x0404000004000004), CONST64(0x0404000404000004),
-  CONST64(0x0404040004000000), CONST64(0x0404040404000000), CONST64(0x0404040004000004), CONST64(0x0404040404000004),
-  CONST64(0x0404000004000400), CONST64(0x0404000404000400), CONST64(0x0404000004000404), CONST64(0x0404000404000404),
-  CONST64(0x0404040004000400), CONST64(0x0404040404000400), CONST64(0x0404040004000404), CONST64(0x0404040404000404),
-  CONST64(0x0400000004040000), CONST64(0x0400000404040000), CONST64(0x0400000004040004), CONST64(0x0400000404040004),
-  CONST64(0x0400040004040000), CONST64(0x0400040404040000), CONST64(0x0400040004040004), CONST64(0x0400040404040004),
-  CONST64(0x0400000004040400), CONST64(0x0400000404040400), CONST64(0x0400000004040404), CONST64(0x0400000404040404),
-  CONST64(0x0400040004040400), CONST64(0x0400040404040400), CONST64(0x0400040004040404), CONST64(0x0400040404040404),
-  CONST64(0x0404000004040000), CONST64(0x0404000404040000), CONST64(0x0404000004040004), CONST64(0x0404000404040004),
-  CONST64(0x0404040004040000), CONST64(0x0404040404040000), CONST64(0x0404040004040004), CONST64(0x0404040404040004),
-  CONST64(0x0404000004040400), CONST64(0x0404000404040400), CONST64(0x0404000004040404), CONST64(0x0404000404040404),
-  CONST64(0x0404040004040400), CONST64(0x0404040404040400), CONST64(0x0404040004040404), CONST64(0x0404040404040404)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000200000000), CONST64(0x0000000000000002), CONST64(0x0000000200000002),
-  CONST64(0x0000020000000000), CONST64(0x0000020200000000), CONST64(0x0000020000000002), CONST64(0x0000020200000002),
-  CONST64(0x0000000000000200), CONST64(0x0000000200000200), CONST64(0x0000000000000202), CONST64(0x0000000200000202),
-  CONST64(0x0000020000000200), CONST64(0x0000020200000200), CONST64(0x0000020000000202), CONST64(0x0000020200000202),
-  CONST64(0x0002000000000000), CONST64(0x0002000200000000), CONST64(0x0002000000000002), CONST64(0x0002000200000002),
-  CONST64(0x0002020000000000), CONST64(0x0002020200000000), CONST64(0x0002020000000002), CONST64(0x0002020200000002),
-  CONST64(0x0002000000000200), CONST64(0x0002000200000200), CONST64(0x0002000000000202), CONST64(0x0002000200000202),
-  CONST64(0x0002020000000200), CONST64(0x0002020200000200), CONST64(0x0002020000000202), CONST64(0x0002020200000202),
-  CONST64(0x0000000000020000), CONST64(0x0000000200020000), CONST64(0x0000000000020002), CONST64(0x0000000200020002),
-  CONST64(0x0000020000020000), CONST64(0x0000020200020000), CONST64(0x0000020000020002), CONST64(0x0000020200020002),
-  CONST64(0x0000000000020200), CONST64(0x0000000200020200), CONST64(0x0000000000020202), CONST64(0x0000000200020202),
-  CONST64(0x0000020000020200), CONST64(0x0000020200020200), CONST64(0x0000020000020202), CONST64(0x0000020200020202),
-  CONST64(0x0002000000020000), CONST64(0x0002000200020000), CONST64(0x0002000000020002), CONST64(0x0002000200020002),
-  CONST64(0x0002020000020000), CONST64(0x0002020200020000), CONST64(0x0002020000020002), CONST64(0x0002020200020002),
-  CONST64(0x0002000000020200), CONST64(0x0002000200020200), CONST64(0x0002000000020202), CONST64(0x0002000200020202),
-  CONST64(0x0002020000020200), CONST64(0x0002020200020200), CONST64(0x0002020000020202), CONST64(0x0002020200020202),
-  CONST64(0x0200000000000000), CONST64(0x0200000200000000), CONST64(0x0200000000000002), CONST64(0x0200000200000002),
-  CONST64(0x0200020000000000), CONST64(0x0200020200000000), CONST64(0x0200020000000002), CONST64(0x0200020200000002),
-  CONST64(0x0200000000000200), CONST64(0x0200000200000200), CONST64(0x0200000000000202), CONST64(0x0200000200000202),
-  CONST64(0x0200020000000200), CONST64(0x0200020200000200), CONST64(0x0200020000000202), CONST64(0x0200020200000202),
-  CONST64(0x0202000000000000), CONST64(0x0202000200000000), CONST64(0x0202000000000002), CONST64(0x0202000200000002),
-  CONST64(0x0202020000000000), CONST64(0x0202020200000000), CONST64(0x0202020000000002), CONST64(0x0202020200000002),
-  CONST64(0x0202000000000200), CONST64(0x0202000200000200), CONST64(0x0202000000000202), CONST64(0x0202000200000202),
-  CONST64(0x0202020000000200), CONST64(0x0202020200000200), CONST64(0x0202020000000202), CONST64(0x0202020200000202),
-  CONST64(0x0200000000020000), CONST64(0x0200000200020000), CONST64(0x0200000000020002), CONST64(0x0200000200020002),
-  CONST64(0x0200020000020000), CONST64(0x0200020200020000), CONST64(0x0200020000020002), CONST64(0x0200020200020002),
-  CONST64(0x0200000000020200), CONST64(0x0200000200020200), CONST64(0x0200000000020202), CONST64(0x0200000200020202),
-  CONST64(0x0200020000020200), CONST64(0x0200020200020200), CONST64(0x0200020000020202), CONST64(0x0200020200020202),
-  CONST64(0x0202000000020000), CONST64(0x0202000200020000), CONST64(0x0202000000020002), CONST64(0x0202000200020002),
-  CONST64(0x0202020000020000), CONST64(0x0202020200020000), CONST64(0x0202020000020002), CONST64(0x0202020200020002),
-  CONST64(0x0202000000020200), CONST64(0x0202000200020200), CONST64(0x0202000000020202), CONST64(0x0202000200020202),
-  CONST64(0x0202020000020200), CONST64(0x0202020200020200), CONST64(0x0202020000020202), CONST64(0x0202020200020202),
-  CONST64(0x0000000002000000), CONST64(0x0000000202000000), CONST64(0x0000000002000002), CONST64(0x0000000202000002),
-  CONST64(0x0000020002000000), CONST64(0x0000020202000000), CONST64(0x0000020002000002), CONST64(0x0000020202000002),
-  CONST64(0x0000000002000200), CONST64(0x0000000202000200), CONST64(0x0000000002000202), CONST64(0x0000000202000202),
-  CONST64(0x0000020002000200), CONST64(0x0000020202000200), CONST64(0x0000020002000202), CONST64(0x0000020202000202),
-  CONST64(0x0002000002000000), CONST64(0x0002000202000000), CONST64(0x0002000002000002), CONST64(0x0002000202000002),
-  CONST64(0x0002020002000000), CONST64(0x0002020202000000), CONST64(0x0002020002000002), CONST64(0x0002020202000002),
-  CONST64(0x0002000002000200), CONST64(0x0002000202000200), CONST64(0x0002000002000202), CONST64(0x0002000202000202),
-  CONST64(0x0002020002000200), CONST64(0x0002020202000200), CONST64(0x0002020002000202), CONST64(0x0002020202000202),
-  CONST64(0x0000000002020000), CONST64(0x0000000202020000), CONST64(0x0000000002020002), CONST64(0x0000000202020002),
-  CONST64(0x0000020002020000), CONST64(0x0000020202020000), CONST64(0x0000020002020002), CONST64(0x0000020202020002),
-  CONST64(0x0000000002020200), CONST64(0x0000000202020200), CONST64(0x0000000002020202), CONST64(0x0000000202020202),
-  CONST64(0x0000020002020200), CONST64(0x0000020202020200), CONST64(0x0000020002020202), CONST64(0x0000020202020202),
-  CONST64(0x0002000002020000), CONST64(0x0002000202020000), CONST64(0x0002000002020002), CONST64(0x0002000202020002),
-  CONST64(0x0002020002020000), CONST64(0x0002020202020000), CONST64(0x0002020002020002), CONST64(0x0002020202020002),
-  CONST64(0x0002000002020200), CONST64(0x0002000202020200), CONST64(0x0002000002020202), CONST64(0x0002000202020202),
-  CONST64(0x0002020002020200), CONST64(0x0002020202020200), CONST64(0x0002020002020202), CONST64(0x0002020202020202),
-  CONST64(0x0200000002000000), CONST64(0x0200000202000000), CONST64(0x0200000002000002), CONST64(0x0200000202000002),
-  CONST64(0x0200020002000000), CONST64(0x0200020202000000), CONST64(0x0200020002000002), CONST64(0x0200020202000002),
-  CONST64(0x0200000002000200), CONST64(0x0200000202000200), CONST64(0x0200000002000202), CONST64(0x0200000202000202),
-  CONST64(0x0200020002000200), CONST64(0x0200020202000200), CONST64(0x0200020002000202), CONST64(0x0200020202000202),
-  CONST64(0x0202000002000000), CONST64(0x0202000202000000), CONST64(0x0202000002000002), CONST64(0x0202000202000002),
-  CONST64(0x0202020002000000), CONST64(0x0202020202000000), CONST64(0x0202020002000002), CONST64(0x0202020202000002),
-  CONST64(0x0202000002000200), CONST64(0x0202000202000200), CONST64(0x0202000002000202), CONST64(0x0202000202000202),
-  CONST64(0x0202020002000200), CONST64(0x0202020202000200), CONST64(0x0202020002000202), CONST64(0x0202020202000202),
-  CONST64(0x0200000002020000), CONST64(0x0200000202020000), CONST64(0x0200000002020002), CONST64(0x0200000202020002),
-  CONST64(0x0200020002020000), CONST64(0x0200020202020000), CONST64(0x0200020002020002), CONST64(0x0200020202020002),
-  CONST64(0x0200000002020200), CONST64(0x0200000202020200), CONST64(0x0200000002020202), CONST64(0x0200000202020202),
-  CONST64(0x0200020002020200), CONST64(0x0200020202020200), CONST64(0x0200020002020202), CONST64(0x0200020202020202),
-  CONST64(0x0202000002020000), CONST64(0x0202000202020000), CONST64(0x0202000002020002), CONST64(0x0202000202020002),
-  CONST64(0x0202020002020000), CONST64(0x0202020202020000), CONST64(0x0202020002020002), CONST64(0x0202020202020002),
-  CONST64(0x0202000002020200), CONST64(0x0202000202020200), CONST64(0x0202000002020202), CONST64(0x0202000202020202),
-  CONST64(0x0202020002020200), CONST64(0x0202020202020200), CONST64(0x0202020002020202), CONST64(0x0202020202020202)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000010000000000), CONST64(0x0000000000000100), CONST64(0x0000010000000100),
-  CONST64(0x0001000000000000), CONST64(0x0001010000000000), CONST64(0x0001000000000100), CONST64(0x0001010000000100),
-  CONST64(0x0000000000010000), CONST64(0x0000010000010000), CONST64(0x0000000000010100), CONST64(0x0000010000010100),
-  CONST64(0x0001000000010000), CONST64(0x0001010000010000), CONST64(0x0001000000010100), CONST64(0x0001010000010100),
-  CONST64(0x0100000000000000), CONST64(0x0100010000000000), CONST64(0x0100000000000100), CONST64(0x0100010000000100),
-  CONST64(0x0101000000000000), CONST64(0x0101010000000000), CONST64(0x0101000000000100), CONST64(0x0101010000000100),
-  CONST64(0x0100000000010000), CONST64(0x0100010000010000), CONST64(0x0100000000010100), CONST64(0x0100010000010100),
-  CONST64(0x0101000000010000), CONST64(0x0101010000010000), CONST64(0x0101000000010100), CONST64(0x0101010000010100),
-  CONST64(0x0000000001000000), CONST64(0x0000010001000000), CONST64(0x0000000001000100), CONST64(0x0000010001000100),
-  CONST64(0x0001000001000000), CONST64(0x0001010001000000), CONST64(0x0001000001000100), CONST64(0x0001010001000100),
-  CONST64(0x0000000001010000), CONST64(0x0000010001010000), CONST64(0x0000000001010100), CONST64(0x0000010001010100),
-  CONST64(0x0001000001010000), CONST64(0x0001010001010000), CONST64(0x0001000001010100), CONST64(0x0001010001010100),
-  CONST64(0x0100000001000000), CONST64(0x0100010001000000), CONST64(0x0100000001000100), CONST64(0x0100010001000100),
-  CONST64(0x0101000001000000), CONST64(0x0101010001000000), CONST64(0x0101000001000100), CONST64(0x0101010001000100),
-  CONST64(0x0100000001010000), CONST64(0x0100010001010000), CONST64(0x0100000001010100), CONST64(0x0100010001010100),
-  CONST64(0x0101000001010000), CONST64(0x0101010001010000), CONST64(0x0101000001010100), CONST64(0x0101010001010100),
-  CONST64(0x0000000100000000), CONST64(0x0000010100000000), CONST64(0x0000000100000100), CONST64(0x0000010100000100),
-  CONST64(0x0001000100000000), CONST64(0x0001010100000000), CONST64(0x0001000100000100), CONST64(0x0001010100000100),
-  CONST64(0x0000000100010000), CONST64(0x0000010100010000), CONST64(0x0000000100010100), CONST64(0x0000010100010100),
-  CONST64(0x0001000100010000), CONST64(0x0001010100010000), CONST64(0x0001000100010100), CONST64(0x0001010100010100),
-  CONST64(0x0100000100000000), CONST64(0x0100010100000000), CONST64(0x0100000100000100), CONST64(0x0100010100000100),
-  CONST64(0x0101000100000000), CONST64(0x0101010100000000), CONST64(0x0101000100000100), CONST64(0x0101010100000100),
-  CONST64(0x0100000100010000), CONST64(0x0100010100010000), CONST64(0x0100000100010100), CONST64(0x0100010100010100),
-  CONST64(0x0101000100010000), CONST64(0x0101010100010000), CONST64(0x0101000100010100), CONST64(0x0101010100010100),
-  CONST64(0x0000000101000000), CONST64(0x0000010101000000), CONST64(0x0000000101000100), CONST64(0x0000010101000100),
-  CONST64(0x0001000101000000), CONST64(0x0001010101000000), CONST64(0x0001000101000100), CONST64(0x0001010101000100),
-  CONST64(0x0000000101010000), CONST64(0x0000010101010000), CONST64(0x0000000101010100), CONST64(0x0000010101010100),
-  CONST64(0x0001000101010000), CONST64(0x0001010101010000), CONST64(0x0001000101010100), CONST64(0x0001010101010100),
-  CONST64(0x0100000101000000), CONST64(0x0100010101000000), CONST64(0x0100000101000100), CONST64(0x0100010101000100),
-  CONST64(0x0101000101000000), CONST64(0x0101010101000000), CONST64(0x0101000101000100), CONST64(0x0101010101000100),
-  CONST64(0x0100000101010000), CONST64(0x0100010101010000), CONST64(0x0100000101010100), CONST64(0x0100010101010100),
-  CONST64(0x0101000101010000), CONST64(0x0101010101010000), CONST64(0x0101000101010100), CONST64(0x0101010101010100),
-  CONST64(0x0000000000000001), CONST64(0x0000010000000001), CONST64(0x0000000000000101), CONST64(0x0000010000000101),
-  CONST64(0x0001000000000001), CONST64(0x0001010000000001), CONST64(0x0001000000000101), CONST64(0x0001010000000101),
-  CONST64(0x0000000000010001), CONST64(0x0000010000010001), CONST64(0x0000000000010101), CONST64(0x0000010000010101),
-  CONST64(0x0001000000010001), CONST64(0x0001010000010001), CONST64(0x0001000000010101), CONST64(0x0001010000010101),
-  CONST64(0x0100000000000001), CONST64(0x0100010000000001), CONST64(0x0100000000000101), CONST64(0x0100010000000101),
-  CONST64(0x0101000000000001), CONST64(0x0101010000000001), CONST64(0x0101000000000101), CONST64(0x0101010000000101),
-  CONST64(0x0100000000010001), CONST64(0x0100010000010001), CONST64(0x0100000000010101), CONST64(0x0100010000010101),
-  CONST64(0x0101000000010001), CONST64(0x0101010000010001), CONST64(0x0101000000010101), CONST64(0x0101010000010101),
-  CONST64(0x0000000001000001), CONST64(0x0000010001000001), CONST64(0x0000000001000101), CONST64(0x0000010001000101),
-  CONST64(0x0001000001000001), CONST64(0x0001010001000001), CONST64(0x0001000001000101), CONST64(0x0001010001000101),
-  CONST64(0x0000000001010001), CONST64(0x0000010001010001), CONST64(0x0000000001010101), CONST64(0x0000010001010101),
-  CONST64(0x0001000001010001), CONST64(0x0001010001010001), CONST64(0x0001000001010101), CONST64(0x0001010001010101),
-  CONST64(0x0100000001000001), CONST64(0x0100010001000001), CONST64(0x0100000001000101), CONST64(0x0100010001000101),
-  CONST64(0x0101000001000001), CONST64(0x0101010001000001), CONST64(0x0101000001000101), CONST64(0x0101010001000101),
-  CONST64(0x0100000001010001), CONST64(0x0100010001010001), CONST64(0x0100000001010101), CONST64(0x0100010001010101),
-  CONST64(0x0101000001010001), CONST64(0x0101010001010001), CONST64(0x0101000001010101), CONST64(0x0101010001010101),
-  CONST64(0x0000000100000001), CONST64(0x0000010100000001), CONST64(0x0000000100000101), CONST64(0x0000010100000101),
-  CONST64(0x0001000100000001), CONST64(0x0001010100000001), CONST64(0x0001000100000101), CONST64(0x0001010100000101),
-  CONST64(0x0000000100010001), CONST64(0x0000010100010001), CONST64(0x0000000100010101), CONST64(0x0000010100010101),
-  CONST64(0x0001000100010001), CONST64(0x0001010100010001), CONST64(0x0001000100010101), CONST64(0x0001010100010101),
-  CONST64(0x0100000100000001), CONST64(0x0100010100000001), CONST64(0x0100000100000101), CONST64(0x0100010100000101),
-  CONST64(0x0101000100000001), CONST64(0x0101010100000001), CONST64(0x0101000100000101), CONST64(0x0101010100000101),
-  CONST64(0x0100000100010001), CONST64(0x0100010100010001), CONST64(0x0100000100010101), CONST64(0x0100010100010101),
-  CONST64(0x0101000100010001), CONST64(0x0101010100010001), CONST64(0x0101000100010101), CONST64(0x0101010100010101),
-  CONST64(0x0000000101000001), CONST64(0x0000010101000001), CONST64(0x0000000101000101), CONST64(0x0000010101000101),
-  CONST64(0x0001000101000001), CONST64(0x0001010101000001), CONST64(0x0001000101000101), CONST64(0x0001010101000101),
-  CONST64(0x0000000101010001), CONST64(0x0000010101010001), CONST64(0x0000000101010101), CONST64(0x0000010101010101),
-  CONST64(0x0001000101010001), CONST64(0x0001010101010001), CONST64(0x0001000101010101), CONST64(0x0001010101010101),
-  CONST64(0x0100000101000001), CONST64(0x0100010101000001), CONST64(0x0100000101000101), CONST64(0x0100010101000101),
-  CONST64(0x0101000101000001), CONST64(0x0101010101000001), CONST64(0x0101000101000101), CONST64(0x0101010101000101),
-  CONST64(0x0100000101010001), CONST64(0x0100010101010001), CONST64(0x0100000101010101), CONST64(0x0100010101010101),
-  CONST64(0x0101000101010001), CONST64(0x0101010101010001), CONST64(0x0101000101010101), CONST64(0x0101010101010101)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000008000000000), CONST64(0x0000000000000080), CONST64(0x0000008000000080),
-  CONST64(0x0000800000000000), CONST64(0x0000808000000000), CONST64(0x0000800000000080), CONST64(0x0000808000000080),
-  CONST64(0x0000000000008000), CONST64(0x0000008000008000), CONST64(0x0000000000008080), CONST64(0x0000008000008080),
-  CONST64(0x0000800000008000), CONST64(0x0000808000008000), CONST64(0x0000800000008080), CONST64(0x0000808000008080),
-  CONST64(0x0080000000000000), CONST64(0x0080008000000000), CONST64(0x0080000000000080), CONST64(0x0080008000000080),
-  CONST64(0x0080800000000000), CONST64(0x0080808000000000), CONST64(0x0080800000000080), CONST64(0x0080808000000080),
-  CONST64(0x0080000000008000), CONST64(0x0080008000008000), CONST64(0x0080000000008080), CONST64(0x0080008000008080),
-  CONST64(0x0080800000008000), CONST64(0x0080808000008000), CONST64(0x0080800000008080), CONST64(0x0080808000008080),
-  CONST64(0x0000000000800000), CONST64(0x0000008000800000), CONST64(0x0000000000800080), CONST64(0x0000008000800080),
-  CONST64(0x0000800000800000), CONST64(0x0000808000800000), CONST64(0x0000800000800080), CONST64(0x0000808000800080),
-  CONST64(0x0000000000808000), CONST64(0x0000008000808000), CONST64(0x0000000000808080), CONST64(0x0000008000808080),
-  CONST64(0x0000800000808000), CONST64(0x0000808000808000), CONST64(0x0000800000808080), CONST64(0x0000808000808080),
-  CONST64(0x0080000000800000), CONST64(0x0080008000800000), CONST64(0x0080000000800080), CONST64(0x0080008000800080),
-  CONST64(0x0080800000800000), CONST64(0x0080808000800000), CONST64(0x0080800000800080), CONST64(0x0080808000800080),
-  CONST64(0x0080000000808000), CONST64(0x0080008000808000), CONST64(0x0080000000808080), CONST64(0x0080008000808080),
-  CONST64(0x0080800000808000), CONST64(0x0080808000808000), CONST64(0x0080800000808080), CONST64(0x0080808000808080),
-  CONST64(0x8000000000000000), CONST64(0x8000008000000000), CONST64(0x8000000000000080), CONST64(0x8000008000000080),
-  CONST64(0x8000800000000000), CONST64(0x8000808000000000), CONST64(0x8000800000000080), CONST64(0x8000808000000080),
-  CONST64(0x8000000000008000), CONST64(0x8000008000008000), CONST64(0x8000000000008080), CONST64(0x8000008000008080),
-  CONST64(0x8000800000008000), CONST64(0x8000808000008000), CONST64(0x8000800000008080), CONST64(0x8000808000008080),
-  CONST64(0x8080000000000000), CONST64(0x8080008000000000), CONST64(0x8080000000000080), CONST64(0x8080008000000080),
-  CONST64(0x8080800000000000), CONST64(0x8080808000000000), CONST64(0x8080800000000080), CONST64(0x8080808000000080),
-  CONST64(0x8080000000008000), CONST64(0x8080008000008000), CONST64(0x8080000000008080), CONST64(0x8080008000008080),
-  CONST64(0x8080800000008000), CONST64(0x8080808000008000), CONST64(0x8080800000008080), CONST64(0x8080808000008080),
-  CONST64(0x8000000000800000), CONST64(0x8000008000800000), CONST64(0x8000000000800080), CONST64(0x8000008000800080),
-  CONST64(0x8000800000800000), CONST64(0x8000808000800000), CONST64(0x8000800000800080), CONST64(0x8000808000800080),
-  CONST64(0x8000000000808000), CONST64(0x8000008000808000), CONST64(0x8000000000808080), CONST64(0x8000008000808080),
-  CONST64(0x8000800000808000), CONST64(0x8000808000808000), CONST64(0x8000800000808080), CONST64(0x8000808000808080),
-  CONST64(0x8080000000800000), CONST64(0x8080008000800000), CONST64(0x8080000000800080), CONST64(0x8080008000800080),
-  CONST64(0x8080800000800000), CONST64(0x8080808000800000), CONST64(0x8080800000800080), CONST64(0x8080808000800080),
-  CONST64(0x8080000000808000), CONST64(0x8080008000808000), CONST64(0x8080000000808080), CONST64(0x8080008000808080),
-  CONST64(0x8080800000808000), CONST64(0x8080808000808000), CONST64(0x8080800000808080), CONST64(0x8080808000808080),
-  CONST64(0x0000000080000000), CONST64(0x0000008080000000), CONST64(0x0000000080000080), CONST64(0x0000008080000080),
-  CONST64(0x0000800080000000), CONST64(0x0000808080000000), CONST64(0x0000800080000080), CONST64(0x0000808080000080),
-  CONST64(0x0000000080008000), CONST64(0x0000008080008000), CONST64(0x0000000080008080), CONST64(0x0000008080008080),
-  CONST64(0x0000800080008000), CONST64(0x0000808080008000), CONST64(0x0000800080008080), CONST64(0x0000808080008080),
-  CONST64(0x0080000080000000), CONST64(0x0080008080000000), CONST64(0x0080000080000080), CONST64(0x0080008080000080),
-  CONST64(0x0080800080000000), CONST64(0x0080808080000000), CONST64(0x0080800080000080), CONST64(0x0080808080000080),
-  CONST64(0x0080000080008000), CONST64(0x0080008080008000), CONST64(0x0080000080008080), CONST64(0x0080008080008080),
-  CONST64(0x0080800080008000), CONST64(0x0080808080008000), CONST64(0x0080800080008080), CONST64(0x0080808080008080),
-  CONST64(0x0000000080800000), CONST64(0x0000008080800000), CONST64(0x0000000080800080), CONST64(0x0000008080800080),
-  CONST64(0x0000800080800000), CONST64(0x0000808080800000), CONST64(0x0000800080800080), CONST64(0x0000808080800080),
-  CONST64(0x0000000080808000), CONST64(0x0000008080808000), CONST64(0x0000000080808080), CONST64(0x0000008080808080),
-  CONST64(0x0000800080808000), CONST64(0x0000808080808000), CONST64(0x0000800080808080), CONST64(0x0000808080808080),
-  CONST64(0x0080000080800000), CONST64(0x0080008080800000), CONST64(0x0080000080800080), CONST64(0x0080008080800080),
-  CONST64(0x0080800080800000), CONST64(0x0080808080800000), CONST64(0x0080800080800080), CONST64(0x0080808080800080),
-  CONST64(0x0080000080808000), CONST64(0x0080008080808000), CONST64(0x0080000080808080), CONST64(0x0080008080808080),
-  CONST64(0x0080800080808000), CONST64(0x0080808080808000), CONST64(0x0080800080808080), CONST64(0x0080808080808080),
-  CONST64(0x8000000080000000), CONST64(0x8000008080000000), CONST64(0x8000000080000080), CONST64(0x8000008080000080),
-  CONST64(0x8000800080000000), CONST64(0x8000808080000000), CONST64(0x8000800080000080), CONST64(0x8000808080000080),
-  CONST64(0x8000000080008000), CONST64(0x8000008080008000), CONST64(0x8000000080008080), CONST64(0x8000008080008080),
-  CONST64(0x8000800080008000), CONST64(0x8000808080008000), CONST64(0x8000800080008080), CONST64(0x8000808080008080),
-  CONST64(0x8080000080000000), CONST64(0x8080008080000000), CONST64(0x8080000080000080), CONST64(0x8080008080000080),
-  CONST64(0x8080800080000000), CONST64(0x8080808080000000), CONST64(0x8080800080000080), CONST64(0x8080808080000080),
-  CONST64(0x8080000080008000), CONST64(0x8080008080008000), CONST64(0x8080000080008080), CONST64(0x8080008080008080),
-  CONST64(0x8080800080008000), CONST64(0x8080808080008000), CONST64(0x8080800080008080), CONST64(0x8080808080008080),
-  CONST64(0x8000000080800000), CONST64(0x8000008080800000), CONST64(0x8000000080800080), CONST64(0x8000008080800080),
-  CONST64(0x8000800080800000), CONST64(0x8000808080800000), CONST64(0x8000800080800080), CONST64(0x8000808080800080),
-  CONST64(0x8000000080808000), CONST64(0x8000008080808000), CONST64(0x8000000080808080), CONST64(0x8000008080808080),
-  CONST64(0x8000800080808000), CONST64(0x8000808080808000), CONST64(0x8000800080808080), CONST64(0x8000808080808080),
-  CONST64(0x8080000080800000), CONST64(0x8080008080800000), CONST64(0x8080000080800080), CONST64(0x8080008080800080),
-  CONST64(0x8080800080800000), CONST64(0x8080808080800000), CONST64(0x8080800080800080), CONST64(0x8080808080800080),
-  CONST64(0x8080000080808000), CONST64(0x8080008080808000), CONST64(0x8080000080808080), CONST64(0x8080008080808080),
-  CONST64(0x8080800080808000), CONST64(0x8080808080808000), CONST64(0x8080800080808080), CONST64(0x8080808080808080)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000004000000000), CONST64(0x0000000000000040), CONST64(0x0000004000000040),
-  CONST64(0x0000400000000000), CONST64(0x0000404000000000), CONST64(0x0000400000000040), CONST64(0x0000404000000040),
-  CONST64(0x0000000000004000), CONST64(0x0000004000004000), CONST64(0x0000000000004040), CONST64(0x0000004000004040),
-  CONST64(0x0000400000004000), CONST64(0x0000404000004000), CONST64(0x0000400000004040), CONST64(0x0000404000004040),
-  CONST64(0x0040000000000000), CONST64(0x0040004000000000), CONST64(0x0040000000000040), CONST64(0x0040004000000040),
-  CONST64(0x0040400000000000), CONST64(0x0040404000000000), CONST64(0x0040400000000040), CONST64(0x0040404000000040),
-  CONST64(0x0040000000004000), CONST64(0x0040004000004000), CONST64(0x0040000000004040), CONST64(0x0040004000004040),
-  CONST64(0x0040400000004000), CONST64(0x0040404000004000), CONST64(0x0040400000004040), CONST64(0x0040404000004040),
-  CONST64(0x0000000000400000), CONST64(0x0000004000400000), CONST64(0x0000000000400040), CONST64(0x0000004000400040),
-  CONST64(0x0000400000400000), CONST64(0x0000404000400000), CONST64(0x0000400000400040), CONST64(0x0000404000400040),
-  CONST64(0x0000000000404000), CONST64(0x0000004000404000), CONST64(0x0000000000404040), CONST64(0x0000004000404040),
-  CONST64(0x0000400000404000), CONST64(0x0000404000404000), CONST64(0x0000400000404040), CONST64(0x0000404000404040),
-  CONST64(0x0040000000400000), CONST64(0x0040004000400000), CONST64(0x0040000000400040), CONST64(0x0040004000400040),
-  CONST64(0x0040400000400000), CONST64(0x0040404000400000), CONST64(0x0040400000400040), CONST64(0x0040404000400040),
-  CONST64(0x0040000000404000), CONST64(0x0040004000404000), CONST64(0x0040000000404040), CONST64(0x0040004000404040),
-  CONST64(0x0040400000404000), CONST64(0x0040404000404000), CONST64(0x0040400000404040), CONST64(0x0040404000404040),
-  CONST64(0x4000000000000000), CONST64(0x4000004000000000), CONST64(0x4000000000000040), CONST64(0x4000004000000040),
-  CONST64(0x4000400000000000), CONST64(0x4000404000000000), CONST64(0x4000400000000040), CONST64(0x4000404000000040),
-  CONST64(0x4000000000004000), CONST64(0x4000004000004000), CONST64(0x4000000000004040), CONST64(0x4000004000004040),
-  CONST64(0x4000400000004000), CONST64(0x4000404000004000), CONST64(0x4000400000004040), CONST64(0x4000404000004040),
-  CONST64(0x4040000000000000), CONST64(0x4040004000000000), CONST64(0x4040000000000040), CONST64(0x4040004000000040),
-  CONST64(0x4040400000000000), CONST64(0x4040404000000000), CONST64(0x4040400000000040), CONST64(0x4040404000000040),
-  CONST64(0x4040000000004000), CONST64(0x4040004000004000), CONST64(0x4040000000004040), CONST64(0x4040004000004040),
-  CONST64(0x4040400000004000), CONST64(0x4040404000004000), CONST64(0x4040400000004040), CONST64(0x4040404000004040),
-  CONST64(0x4000000000400000), CONST64(0x4000004000400000), CONST64(0x4000000000400040), CONST64(0x4000004000400040),
-  CONST64(0x4000400000400000), CONST64(0x4000404000400000), CONST64(0x4000400000400040), CONST64(0x4000404000400040),
-  CONST64(0x4000000000404000), CONST64(0x4000004000404000), CONST64(0x4000000000404040), CONST64(0x4000004000404040),
-  CONST64(0x4000400000404000), CONST64(0x4000404000404000), CONST64(0x4000400000404040), CONST64(0x4000404000404040),
-  CONST64(0x4040000000400000), CONST64(0x4040004000400000), CONST64(0x4040000000400040), CONST64(0x4040004000400040),
-  CONST64(0x4040400000400000), CONST64(0x4040404000400000), CONST64(0x4040400000400040), CONST64(0x4040404000400040),
-  CONST64(0x4040000000404000), CONST64(0x4040004000404000), CONST64(0x4040000000404040), CONST64(0x4040004000404040),
-  CONST64(0x4040400000404000), CONST64(0x4040404000404000), CONST64(0x4040400000404040), CONST64(0x4040404000404040),
-  CONST64(0x0000000040000000), CONST64(0x0000004040000000), CONST64(0x0000000040000040), CONST64(0x0000004040000040),
-  CONST64(0x0000400040000000), CONST64(0x0000404040000000), CONST64(0x0000400040000040), CONST64(0x0000404040000040),
-  CONST64(0x0000000040004000), CONST64(0x0000004040004000), CONST64(0x0000000040004040), CONST64(0x0000004040004040),
-  CONST64(0x0000400040004000), CONST64(0x0000404040004000), CONST64(0x0000400040004040), CONST64(0x0000404040004040),
-  CONST64(0x0040000040000000), CONST64(0x0040004040000000), CONST64(0x0040000040000040), CONST64(0x0040004040000040),
-  CONST64(0x0040400040000000), CONST64(0x0040404040000000), CONST64(0x0040400040000040), CONST64(0x0040404040000040),
-  CONST64(0x0040000040004000), CONST64(0x0040004040004000), CONST64(0x0040000040004040), CONST64(0x0040004040004040),
-  CONST64(0x0040400040004000), CONST64(0x0040404040004000), CONST64(0x0040400040004040), CONST64(0x0040404040004040),
-  CONST64(0x0000000040400000), CONST64(0x0000004040400000), CONST64(0x0000000040400040), CONST64(0x0000004040400040),
-  CONST64(0x0000400040400000), CONST64(0x0000404040400000), CONST64(0x0000400040400040), CONST64(0x0000404040400040),
-  CONST64(0x0000000040404000), CONST64(0x0000004040404000), CONST64(0x0000000040404040), CONST64(0x0000004040404040),
-  CONST64(0x0000400040404000), CONST64(0x0000404040404000), CONST64(0x0000400040404040), CONST64(0x0000404040404040),
-  CONST64(0x0040000040400000), CONST64(0x0040004040400000), CONST64(0x0040000040400040), CONST64(0x0040004040400040),
-  CONST64(0x0040400040400000), CONST64(0x0040404040400000), CONST64(0x0040400040400040), CONST64(0x0040404040400040),
-  CONST64(0x0040000040404000), CONST64(0x0040004040404000), CONST64(0x0040000040404040), CONST64(0x0040004040404040),
-  CONST64(0x0040400040404000), CONST64(0x0040404040404000), CONST64(0x0040400040404040), CONST64(0x0040404040404040),
-  CONST64(0x4000000040000000), CONST64(0x4000004040000000), CONST64(0x4000000040000040), CONST64(0x4000004040000040),
-  CONST64(0x4000400040000000), CONST64(0x4000404040000000), CONST64(0x4000400040000040), CONST64(0x4000404040000040),
-  CONST64(0x4000000040004000), CONST64(0x4000004040004000), CONST64(0x4000000040004040), CONST64(0x4000004040004040),
-  CONST64(0x4000400040004000), CONST64(0x4000404040004000), CONST64(0x4000400040004040), CONST64(0x4000404040004040),
-  CONST64(0x4040000040000000), CONST64(0x4040004040000000), CONST64(0x4040000040000040), CONST64(0x4040004040000040),
-  CONST64(0x4040400040000000), CONST64(0x4040404040000000), CONST64(0x4040400040000040), CONST64(0x4040404040000040),
-  CONST64(0x4040000040004000), CONST64(0x4040004040004000), CONST64(0x4040000040004040), CONST64(0x4040004040004040),
-  CONST64(0x4040400040004000), CONST64(0x4040404040004000), CONST64(0x4040400040004040), CONST64(0x4040404040004040),
-  CONST64(0x4000000040400000), CONST64(0x4000004040400000), CONST64(0x4000000040400040), CONST64(0x4000004040400040),
-  CONST64(0x4000400040400000), CONST64(0x4000404040400000), CONST64(0x4000400040400040), CONST64(0x4000404040400040),
-  CONST64(0x4000000040404000), CONST64(0x4000004040404000), CONST64(0x4000000040404040), CONST64(0x4000004040404040),
-  CONST64(0x4000400040404000), CONST64(0x4000404040404000), CONST64(0x4000400040404040), CONST64(0x4000404040404040),
-  CONST64(0x4040000040400000), CONST64(0x4040004040400000), CONST64(0x4040000040400040), CONST64(0x4040004040400040),
-  CONST64(0x4040400040400000), CONST64(0x4040404040400000), CONST64(0x4040400040400040), CONST64(0x4040404040400040),
-  CONST64(0x4040000040404000), CONST64(0x4040004040404000), CONST64(0x4040000040404040), CONST64(0x4040004040404040),
-  CONST64(0x4040400040404000), CONST64(0x4040404040404000), CONST64(0x4040400040404040), CONST64(0x4040404040404040)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000002000000000), CONST64(0x0000000000000020), CONST64(0x0000002000000020),
-  CONST64(0x0000200000000000), CONST64(0x0000202000000000), CONST64(0x0000200000000020), CONST64(0x0000202000000020),
-  CONST64(0x0000000000002000), CONST64(0x0000002000002000), CONST64(0x0000000000002020), CONST64(0x0000002000002020),
-  CONST64(0x0000200000002000), CONST64(0x0000202000002000), CONST64(0x0000200000002020), CONST64(0x0000202000002020),
-  CONST64(0x0020000000000000), CONST64(0x0020002000000000), CONST64(0x0020000000000020), CONST64(0x0020002000000020),
-  CONST64(0x0020200000000000), CONST64(0x0020202000000000), CONST64(0x0020200000000020), CONST64(0x0020202000000020),
-  CONST64(0x0020000000002000), CONST64(0x0020002000002000), CONST64(0x0020000000002020), CONST64(0x0020002000002020),
-  CONST64(0x0020200000002000), CONST64(0x0020202000002000), CONST64(0x0020200000002020), CONST64(0x0020202000002020),
-  CONST64(0x0000000000200000), CONST64(0x0000002000200000), CONST64(0x0000000000200020), CONST64(0x0000002000200020),
-  CONST64(0x0000200000200000), CONST64(0x0000202000200000), CONST64(0x0000200000200020), CONST64(0x0000202000200020),
-  CONST64(0x0000000000202000), CONST64(0x0000002000202000), CONST64(0x0000000000202020), CONST64(0x0000002000202020),
-  CONST64(0x0000200000202000), CONST64(0x0000202000202000), CONST64(0x0000200000202020), CONST64(0x0000202000202020),
-  CONST64(0x0020000000200000), CONST64(0x0020002000200000), CONST64(0x0020000000200020), CONST64(0x0020002000200020),
-  CONST64(0x0020200000200000), CONST64(0x0020202000200000), CONST64(0x0020200000200020), CONST64(0x0020202000200020),
-  CONST64(0x0020000000202000), CONST64(0x0020002000202000), CONST64(0x0020000000202020), CONST64(0x0020002000202020),
-  CONST64(0x0020200000202000), CONST64(0x0020202000202000), CONST64(0x0020200000202020), CONST64(0x0020202000202020),
-  CONST64(0x2000000000000000), CONST64(0x2000002000000000), CONST64(0x2000000000000020), CONST64(0x2000002000000020),
-  CONST64(0x2000200000000000), CONST64(0x2000202000000000), CONST64(0x2000200000000020), CONST64(0x2000202000000020),
-  CONST64(0x2000000000002000), CONST64(0x2000002000002000), CONST64(0x2000000000002020), CONST64(0x2000002000002020),
-  CONST64(0x2000200000002000), CONST64(0x2000202000002000), CONST64(0x2000200000002020), CONST64(0x2000202000002020),
-  CONST64(0x2020000000000000), CONST64(0x2020002000000000), CONST64(0x2020000000000020), CONST64(0x2020002000000020),
-  CONST64(0x2020200000000000), CONST64(0x2020202000000000), CONST64(0x2020200000000020), CONST64(0x2020202000000020),
-  CONST64(0x2020000000002000), CONST64(0x2020002000002000), CONST64(0x2020000000002020), CONST64(0x2020002000002020),
-  CONST64(0x2020200000002000), CONST64(0x2020202000002000), CONST64(0x2020200000002020), CONST64(0x2020202000002020),
-  CONST64(0x2000000000200000), CONST64(0x2000002000200000), CONST64(0x2000000000200020), CONST64(0x2000002000200020),
-  CONST64(0x2000200000200000), CONST64(0x2000202000200000), CONST64(0x2000200000200020), CONST64(0x2000202000200020),
-  CONST64(0x2000000000202000), CONST64(0x2000002000202000), CONST64(0x2000000000202020), CONST64(0x2000002000202020),
-  CONST64(0x2000200000202000), CONST64(0x2000202000202000), CONST64(0x2000200000202020), CONST64(0x2000202000202020),
-  CONST64(0x2020000000200000), CONST64(0x2020002000200000), CONST64(0x2020000000200020), CONST64(0x2020002000200020),
-  CONST64(0x2020200000200000), CONST64(0x2020202000200000), CONST64(0x2020200000200020), CONST64(0x2020202000200020),
-  CONST64(0x2020000000202000), CONST64(0x2020002000202000), CONST64(0x2020000000202020), CONST64(0x2020002000202020),
-  CONST64(0x2020200000202000), CONST64(0x2020202000202000), CONST64(0x2020200000202020), CONST64(0x2020202000202020),
-  CONST64(0x0000000020000000), CONST64(0x0000002020000000), CONST64(0x0000000020000020), CONST64(0x0000002020000020),
-  CONST64(0x0000200020000000), CONST64(0x0000202020000000), CONST64(0x0000200020000020), CONST64(0x0000202020000020),
-  CONST64(0x0000000020002000), CONST64(0x0000002020002000), CONST64(0x0000000020002020), CONST64(0x0000002020002020),
-  CONST64(0x0000200020002000), CONST64(0x0000202020002000), CONST64(0x0000200020002020), CONST64(0x0000202020002020),
-  CONST64(0x0020000020000000), CONST64(0x0020002020000000), CONST64(0x0020000020000020), CONST64(0x0020002020000020),
-  CONST64(0x0020200020000000), CONST64(0x0020202020000000), CONST64(0x0020200020000020), CONST64(0x0020202020000020),
-  CONST64(0x0020000020002000), CONST64(0x0020002020002000), CONST64(0x0020000020002020), CONST64(0x0020002020002020),
-  CONST64(0x0020200020002000), CONST64(0x0020202020002000), CONST64(0x0020200020002020), CONST64(0x0020202020002020),
-  CONST64(0x0000000020200000), CONST64(0x0000002020200000), CONST64(0x0000000020200020), CONST64(0x0000002020200020),
-  CONST64(0x0000200020200000), CONST64(0x0000202020200000), CONST64(0x0000200020200020), CONST64(0x0000202020200020),
-  CONST64(0x0000000020202000), CONST64(0x0000002020202000), CONST64(0x0000000020202020), CONST64(0x0000002020202020),
-  CONST64(0x0000200020202000), CONST64(0x0000202020202000), CONST64(0x0000200020202020), CONST64(0x0000202020202020),
-  CONST64(0x0020000020200000), CONST64(0x0020002020200000), CONST64(0x0020000020200020), CONST64(0x0020002020200020),
-  CONST64(0x0020200020200000), CONST64(0x0020202020200000), CONST64(0x0020200020200020), CONST64(0x0020202020200020),
-  CONST64(0x0020000020202000), CONST64(0x0020002020202000), CONST64(0x0020000020202020), CONST64(0x0020002020202020),
-  CONST64(0x0020200020202000), CONST64(0x0020202020202000), CONST64(0x0020200020202020), CONST64(0x0020202020202020),
-  CONST64(0x2000000020000000), CONST64(0x2000002020000000), CONST64(0x2000000020000020), CONST64(0x2000002020000020),
-  CONST64(0x2000200020000000), CONST64(0x2000202020000000), CONST64(0x2000200020000020), CONST64(0x2000202020000020),
-  CONST64(0x2000000020002000), CONST64(0x2000002020002000), CONST64(0x2000000020002020), CONST64(0x2000002020002020),
-  CONST64(0x2000200020002000), CONST64(0x2000202020002000), CONST64(0x2000200020002020), CONST64(0x2000202020002020),
-  CONST64(0x2020000020000000), CONST64(0x2020002020000000), CONST64(0x2020000020000020), CONST64(0x2020002020000020),
-  CONST64(0x2020200020000000), CONST64(0x2020202020000000), CONST64(0x2020200020000020), CONST64(0x2020202020000020),
-  CONST64(0x2020000020002000), CONST64(0x2020002020002000), CONST64(0x2020000020002020), CONST64(0x2020002020002020),
-  CONST64(0x2020200020002000), CONST64(0x2020202020002000), CONST64(0x2020200020002020), CONST64(0x2020202020002020),
-  CONST64(0x2000000020200000), CONST64(0x2000002020200000), CONST64(0x2000000020200020), CONST64(0x2000002020200020),
-  CONST64(0x2000200020200000), CONST64(0x2000202020200000), CONST64(0x2000200020200020), CONST64(0x2000202020200020),
-  CONST64(0x2000000020202000), CONST64(0x2000002020202000), CONST64(0x2000000020202020), CONST64(0x2000002020202020),
-  CONST64(0x2000200020202000), CONST64(0x2000202020202000), CONST64(0x2000200020202020), CONST64(0x2000202020202020),
-  CONST64(0x2020000020200000), CONST64(0x2020002020200000), CONST64(0x2020000020200020), CONST64(0x2020002020200020),
-  CONST64(0x2020200020200000), CONST64(0x2020202020200000), CONST64(0x2020200020200020), CONST64(0x2020202020200020),
-  CONST64(0x2020000020202000), CONST64(0x2020002020202000), CONST64(0x2020000020202020), CONST64(0x2020002020202020),
-  CONST64(0x2020200020202000), CONST64(0x2020202020202000), CONST64(0x2020200020202020), CONST64(0x2020202020202020)
-  }};
-
-static const ulong64 des_fp[8][256] = {
-
-{ CONST64(0x0000000000000000), CONST64(0x0000008000000000), CONST64(0x0000000002000000), CONST64(0x0000008002000000),
-  CONST64(0x0000000000020000), CONST64(0x0000008000020000), CONST64(0x0000000002020000), CONST64(0x0000008002020000),
-  CONST64(0x0000000000000200), CONST64(0x0000008000000200), CONST64(0x0000000002000200), CONST64(0x0000008002000200),
-  CONST64(0x0000000000020200), CONST64(0x0000008000020200), CONST64(0x0000000002020200), CONST64(0x0000008002020200),
-  CONST64(0x0000000000000002), CONST64(0x0000008000000002), CONST64(0x0000000002000002), CONST64(0x0000008002000002),
-  CONST64(0x0000000000020002), CONST64(0x0000008000020002), CONST64(0x0000000002020002), CONST64(0x0000008002020002),
-  CONST64(0x0000000000000202), CONST64(0x0000008000000202), CONST64(0x0000000002000202), CONST64(0x0000008002000202),
-  CONST64(0x0000000000020202), CONST64(0x0000008000020202), CONST64(0x0000000002020202), CONST64(0x0000008002020202),
-  CONST64(0x0200000000000000), CONST64(0x0200008000000000), CONST64(0x0200000002000000), CONST64(0x0200008002000000),
-  CONST64(0x0200000000020000), CONST64(0x0200008000020000), CONST64(0x0200000002020000), CONST64(0x0200008002020000),
-  CONST64(0x0200000000000200), CONST64(0x0200008000000200), CONST64(0x0200000002000200), CONST64(0x0200008002000200),
-  CONST64(0x0200000000020200), CONST64(0x0200008000020200), CONST64(0x0200000002020200), CONST64(0x0200008002020200),
-  CONST64(0x0200000000000002), CONST64(0x0200008000000002), CONST64(0x0200000002000002), CONST64(0x0200008002000002),
-  CONST64(0x0200000000020002), CONST64(0x0200008000020002), CONST64(0x0200000002020002), CONST64(0x0200008002020002),
-  CONST64(0x0200000000000202), CONST64(0x0200008000000202), CONST64(0x0200000002000202), CONST64(0x0200008002000202),
-  CONST64(0x0200000000020202), CONST64(0x0200008000020202), CONST64(0x0200000002020202), CONST64(0x0200008002020202),
-  CONST64(0x0002000000000000), CONST64(0x0002008000000000), CONST64(0x0002000002000000), CONST64(0x0002008002000000),
-  CONST64(0x0002000000020000), CONST64(0x0002008000020000), CONST64(0x0002000002020000), CONST64(0x0002008002020000),
-  CONST64(0x0002000000000200), CONST64(0x0002008000000200), CONST64(0x0002000002000200), CONST64(0x0002008002000200),
-  CONST64(0x0002000000020200), CONST64(0x0002008000020200), CONST64(0x0002000002020200), CONST64(0x0002008002020200),
-  CONST64(0x0002000000000002), CONST64(0x0002008000000002), CONST64(0x0002000002000002), CONST64(0x0002008002000002),
-  CONST64(0x0002000000020002), CONST64(0x0002008000020002), CONST64(0x0002000002020002), CONST64(0x0002008002020002),
-  CONST64(0x0002000000000202), CONST64(0x0002008000000202), CONST64(0x0002000002000202), CONST64(0x0002008002000202),
-  CONST64(0x0002000000020202), CONST64(0x0002008000020202), CONST64(0x0002000002020202), CONST64(0x0002008002020202),
-  CONST64(0x0202000000000000), CONST64(0x0202008000000000), CONST64(0x0202000002000000), CONST64(0x0202008002000000),
-  CONST64(0x0202000000020000), CONST64(0x0202008000020000), CONST64(0x0202000002020000), CONST64(0x0202008002020000),
-  CONST64(0x0202000000000200), CONST64(0x0202008000000200), CONST64(0x0202000002000200), CONST64(0x0202008002000200),
-  CONST64(0x0202000000020200), CONST64(0x0202008000020200), CONST64(0x0202000002020200), CONST64(0x0202008002020200),
-  CONST64(0x0202000000000002), CONST64(0x0202008000000002), CONST64(0x0202000002000002), CONST64(0x0202008002000002),
-  CONST64(0x0202000000020002), CONST64(0x0202008000020002), CONST64(0x0202000002020002), CONST64(0x0202008002020002),
-  CONST64(0x0202000000000202), CONST64(0x0202008000000202), CONST64(0x0202000002000202), CONST64(0x0202008002000202),
-  CONST64(0x0202000000020202), CONST64(0x0202008000020202), CONST64(0x0202000002020202), CONST64(0x0202008002020202),
-  CONST64(0x0000020000000000), CONST64(0x0000028000000000), CONST64(0x0000020002000000), CONST64(0x0000028002000000),
-  CONST64(0x0000020000020000), CONST64(0x0000028000020000), CONST64(0x0000020002020000), CONST64(0x0000028002020000),
-  CONST64(0x0000020000000200), CONST64(0x0000028000000200), CONST64(0x0000020002000200), CONST64(0x0000028002000200),
-  CONST64(0x0000020000020200), CONST64(0x0000028000020200), CONST64(0x0000020002020200), CONST64(0x0000028002020200),
-  CONST64(0x0000020000000002), CONST64(0x0000028000000002), CONST64(0x0000020002000002), CONST64(0x0000028002000002),
-  CONST64(0x0000020000020002), CONST64(0x0000028000020002), CONST64(0x0000020002020002), CONST64(0x0000028002020002),
-  CONST64(0x0000020000000202), CONST64(0x0000028000000202), CONST64(0x0000020002000202), CONST64(0x0000028002000202),
-  CONST64(0x0000020000020202), CONST64(0x0000028000020202), CONST64(0x0000020002020202), CONST64(0x0000028002020202),
-  CONST64(0x0200020000000000), CONST64(0x0200028000000000), CONST64(0x0200020002000000), CONST64(0x0200028002000000),
-  CONST64(0x0200020000020000), CONST64(0x0200028000020000), CONST64(0x0200020002020000), CONST64(0x0200028002020000),
-  CONST64(0x0200020000000200), CONST64(0x0200028000000200), CONST64(0x0200020002000200), CONST64(0x0200028002000200),
-  CONST64(0x0200020000020200), CONST64(0x0200028000020200), CONST64(0x0200020002020200), CONST64(0x0200028002020200),
-  CONST64(0x0200020000000002), CONST64(0x0200028000000002), CONST64(0x0200020002000002), CONST64(0x0200028002000002),
-  CONST64(0x0200020000020002), CONST64(0x0200028000020002), CONST64(0x0200020002020002), CONST64(0x0200028002020002),
-  CONST64(0x0200020000000202), CONST64(0x0200028000000202), CONST64(0x0200020002000202), CONST64(0x0200028002000202),
-  CONST64(0x0200020000020202), CONST64(0x0200028000020202), CONST64(0x0200020002020202), CONST64(0x0200028002020202),
-  CONST64(0x0002020000000000), CONST64(0x0002028000000000), CONST64(0x0002020002000000), CONST64(0x0002028002000000),
-  CONST64(0x0002020000020000), CONST64(0x0002028000020000), CONST64(0x0002020002020000), CONST64(0x0002028002020000),
-  CONST64(0x0002020000000200), CONST64(0x0002028000000200), CONST64(0x0002020002000200), CONST64(0x0002028002000200),
-  CONST64(0x0002020000020200), CONST64(0x0002028000020200), CONST64(0x0002020002020200), CONST64(0x0002028002020200),
-  CONST64(0x0002020000000002), CONST64(0x0002028000000002), CONST64(0x0002020002000002), CONST64(0x0002028002000002),
-  CONST64(0x0002020000020002), CONST64(0x0002028000020002), CONST64(0x0002020002020002), CONST64(0x0002028002020002),
-  CONST64(0x0002020000000202), CONST64(0x0002028000000202), CONST64(0x0002020002000202), CONST64(0x0002028002000202),
-  CONST64(0x0002020000020202), CONST64(0x0002028000020202), CONST64(0x0002020002020202), CONST64(0x0002028002020202),
-  CONST64(0x0202020000000000), CONST64(0x0202028000000000), CONST64(0x0202020002000000), CONST64(0x0202028002000000),
-  CONST64(0x0202020000020000), CONST64(0x0202028000020000), CONST64(0x0202020002020000), CONST64(0x0202028002020000),
-  CONST64(0x0202020000000200), CONST64(0x0202028000000200), CONST64(0x0202020002000200), CONST64(0x0202028002000200),
-  CONST64(0x0202020000020200), CONST64(0x0202028000020200), CONST64(0x0202020002020200), CONST64(0x0202028002020200),
-  CONST64(0x0202020000000002), CONST64(0x0202028000000002), CONST64(0x0202020002000002), CONST64(0x0202028002000002),
-  CONST64(0x0202020000020002), CONST64(0x0202028000020002), CONST64(0x0202020002020002), CONST64(0x0202028002020002),
-  CONST64(0x0202020000000202), CONST64(0x0202028000000202), CONST64(0x0202020002000202), CONST64(0x0202028002000202),
-  CONST64(0x0202020000020202), CONST64(0x0202028000020202), CONST64(0x0202020002020202), CONST64(0x0202028002020202)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000200000000), CONST64(0x0000000008000000), CONST64(0x0000000208000000),
-  CONST64(0x0000000000080000), CONST64(0x0000000200080000), CONST64(0x0000000008080000), CONST64(0x0000000208080000),
-  CONST64(0x0000000000000800), CONST64(0x0000000200000800), CONST64(0x0000000008000800), CONST64(0x0000000208000800),
-  CONST64(0x0000000000080800), CONST64(0x0000000200080800), CONST64(0x0000000008080800), CONST64(0x0000000208080800),
-  CONST64(0x0000000000000008), CONST64(0x0000000200000008), CONST64(0x0000000008000008), CONST64(0x0000000208000008),
-  CONST64(0x0000000000080008), CONST64(0x0000000200080008), CONST64(0x0000000008080008), CONST64(0x0000000208080008),
-  CONST64(0x0000000000000808), CONST64(0x0000000200000808), CONST64(0x0000000008000808), CONST64(0x0000000208000808),
-  CONST64(0x0000000000080808), CONST64(0x0000000200080808), CONST64(0x0000000008080808), CONST64(0x0000000208080808),
-  CONST64(0x0800000000000000), CONST64(0x0800000200000000), CONST64(0x0800000008000000), CONST64(0x0800000208000000),
-  CONST64(0x0800000000080000), CONST64(0x0800000200080000), CONST64(0x0800000008080000), CONST64(0x0800000208080000),
-  CONST64(0x0800000000000800), CONST64(0x0800000200000800), CONST64(0x0800000008000800), CONST64(0x0800000208000800),
-  CONST64(0x0800000000080800), CONST64(0x0800000200080800), CONST64(0x0800000008080800), CONST64(0x0800000208080800),
-  CONST64(0x0800000000000008), CONST64(0x0800000200000008), CONST64(0x0800000008000008), CONST64(0x0800000208000008),
-  CONST64(0x0800000000080008), CONST64(0x0800000200080008), CONST64(0x0800000008080008), CONST64(0x0800000208080008),
-  CONST64(0x0800000000000808), CONST64(0x0800000200000808), CONST64(0x0800000008000808), CONST64(0x0800000208000808),
-  CONST64(0x0800000000080808), CONST64(0x0800000200080808), CONST64(0x0800000008080808), CONST64(0x0800000208080808),
-  CONST64(0x0008000000000000), CONST64(0x0008000200000000), CONST64(0x0008000008000000), CONST64(0x0008000208000000),
-  CONST64(0x0008000000080000), CONST64(0x0008000200080000), CONST64(0x0008000008080000), CONST64(0x0008000208080000),
-  CONST64(0x0008000000000800), CONST64(0x0008000200000800), CONST64(0x0008000008000800), CONST64(0x0008000208000800),
-  CONST64(0x0008000000080800), CONST64(0x0008000200080800), CONST64(0x0008000008080800), CONST64(0x0008000208080800),
-  CONST64(0x0008000000000008), CONST64(0x0008000200000008), CONST64(0x0008000008000008), CONST64(0x0008000208000008),
-  CONST64(0x0008000000080008), CONST64(0x0008000200080008), CONST64(0x0008000008080008), CONST64(0x0008000208080008),
-  CONST64(0x0008000000000808), CONST64(0x0008000200000808), CONST64(0x0008000008000808), CONST64(0x0008000208000808),
-  CONST64(0x0008000000080808), CONST64(0x0008000200080808), CONST64(0x0008000008080808), CONST64(0x0008000208080808),
-  CONST64(0x0808000000000000), CONST64(0x0808000200000000), CONST64(0x0808000008000000), CONST64(0x0808000208000000),
-  CONST64(0x0808000000080000), CONST64(0x0808000200080000), CONST64(0x0808000008080000), CONST64(0x0808000208080000),
-  CONST64(0x0808000000000800), CONST64(0x0808000200000800), CONST64(0x0808000008000800), CONST64(0x0808000208000800),
-  CONST64(0x0808000000080800), CONST64(0x0808000200080800), CONST64(0x0808000008080800), CONST64(0x0808000208080800),
-  CONST64(0x0808000000000008), CONST64(0x0808000200000008), CONST64(0x0808000008000008), CONST64(0x0808000208000008),
-  CONST64(0x0808000000080008), CONST64(0x0808000200080008), CONST64(0x0808000008080008), CONST64(0x0808000208080008),
-  CONST64(0x0808000000000808), CONST64(0x0808000200000808), CONST64(0x0808000008000808), CONST64(0x0808000208000808),
-  CONST64(0x0808000000080808), CONST64(0x0808000200080808), CONST64(0x0808000008080808), CONST64(0x0808000208080808),
-  CONST64(0x0000080000000000), CONST64(0x0000080200000000), CONST64(0x0000080008000000), CONST64(0x0000080208000000),
-  CONST64(0x0000080000080000), CONST64(0x0000080200080000), CONST64(0x0000080008080000), CONST64(0x0000080208080000),
-  CONST64(0x0000080000000800), CONST64(0x0000080200000800), CONST64(0x0000080008000800), CONST64(0x0000080208000800),
-  CONST64(0x0000080000080800), CONST64(0x0000080200080800), CONST64(0x0000080008080800), CONST64(0x0000080208080800),
-  CONST64(0x0000080000000008), CONST64(0x0000080200000008), CONST64(0x0000080008000008), CONST64(0x0000080208000008),
-  CONST64(0x0000080000080008), CONST64(0x0000080200080008), CONST64(0x0000080008080008), CONST64(0x0000080208080008),
-  CONST64(0x0000080000000808), CONST64(0x0000080200000808), CONST64(0x0000080008000808), CONST64(0x0000080208000808),
-  CONST64(0x0000080000080808), CONST64(0x0000080200080808), CONST64(0x0000080008080808), CONST64(0x0000080208080808),
-  CONST64(0x0800080000000000), CONST64(0x0800080200000000), CONST64(0x0800080008000000), CONST64(0x0800080208000000),
-  CONST64(0x0800080000080000), CONST64(0x0800080200080000), CONST64(0x0800080008080000), CONST64(0x0800080208080000),
-  CONST64(0x0800080000000800), CONST64(0x0800080200000800), CONST64(0x0800080008000800), CONST64(0x0800080208000800),
-  CONST64(0x0800080000080800), CONST64(0x0800080200080800), CONST64(0x0800080008080800), CONST64(0x0800080208080800),
-  CONST64(0x0800080000000008), CONST64(0x0800080200000008), CONST64(0x0800080008000008), CONST64(0x0800080208000008),
-  CONST64(0x0800080000080008), CONST64(0x0800080200080008), CONST64(0x0800080008080008), CONST64(0x0800080208080008),
-  CONST64(0x0800080000000808), CONST64(0x0800080200000808), CONST64(0x0800080008000808), CONST64(0x0800080208000808),
-  CONST64(0x0800080000080808), CONST64(0x0800080200080808), CONST64(0x0800080008080808), CONST64(0x0800080208080808),
-  CONST64(0x0008080000000000), CONST64(0x0008080200000000), CONST64(0x0008080008000000), CONST64(0x0008080208000000),
-  CONST64(0x0008080000080000), CONST64(0x0008080200080000), CONST64(0x0008080008080000), CONST64(0x0008080208080000),
-  CONST64(0x0008080000000800), CONST64(0x0008080200000800), CONST64(0x0008080008000800), CONST64(0x0008080208000800),
-  CONST64(0x0008080000080800), CONST64(0x0008080200080800), CONST64(0x0008080008080800), CONST64(0x0008080208080800),
-  CONST64(0x0008080000000008), CONST64(0x0008080200000008), CONST64(0x0008080008000008), CONST64(0x0008080208000008),
-  CONST64(0x0008080000080008), CONST64(0x0008080200080008), CONST64(0x0008080008080008), CONST64(0x0008080208080008),
-  CONST64(0x0008080000000808), CONST64(0x0008080200000808), CONST64(0x0008080008000808), CONST64(0x0008080208000808),
-  CONST64(0x0008080000080808), CONST64(0x0008080200080808), CONST64(0x0008080008080808), CONST64(0x0008080208080808),
-  CONST64(0x0808080000000000), CONST64(0x0808080200000000), CONST64(0x0808080008000000), CONST64(0x0808080208000000),
-  CONST64(0x0808080000080000), CONST64(0x0808080200080000), CONST64(0x0808080008080000), CONST64(0x0808080208080000),
-  CONST64(0x0808080000000800), CONST64(0x0808080200000800), CONST64(0x0808080008000800), CONST64(0x0808080208000800),
-  CONST64(0x0808080000080800), CONST64(0x0808080200080800), CONST64(0x0808080008080800), CONST64(0x0808080208080800),
-  CONST64(0x0808080000000008), CONST64(0x0808080200000008), CONST64(0x0808080008000008), CONST64(0x0808080208000008),
-  CONST64(0x0808080000080008), CONST64(0x0808080200080008), CONST64(0x0808080008080008), CONST64(0x0808080208080008),
-  CONST64(0x0808080000000808), CONST64(0x0808080200000808), CONST64(0x0808080008000808), CONST64(0x0808080208000808),
-  CONST64(0x0808080000080808), CONST64(0x0808080200080808), CONST64(0x0808080008080808), CONST64(0x0808080208080808)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000800000000), CONST64(0x0000000020000000), CONST64(0x0000000820000000),
-  CONST64(0x0000000000200000), CONST64(0x0000000800200000), CONST64(0x0000000020200000), CONST64(0x0000000820200000),
-  CONST64(0x0000000000002000), CONST64(0x0000000800002000), CONST64(0x0000000020002000), CONST64(0x0000000820002000),
-  CONST64(0x0000000000202000), CONST64(0x0000000800202000), CONST64(0x0000000020202000), CONST64(0x0000000820202000),
-  CONST64(0x0000000000000020), CONST64(0x0000000800000020), CONST64(0x0000000020000020), CONST64(0x0000000820000020),
-  CONST64(0x0000000000200020), CONST64(0x0000000800200020), CONST64(0x0000000020200020), CONST64(0x0000000820200020),
-  CONST64(0x0000000000002020), CONST64(0x0000000800002020), CONST64(0x0000000020002020), CONST64(0x0000000820002020),
-  CONST64(0x0000000000202020), CONST64(0x0000000800202020), CONST64(0x0000000020202020), CONST64(0x0000000820202020),
-  CONST64(0x2000000000000000), CONST64(0x2000000800000000), CONST64(0x2000000020000000), CONST64(0x2000000820000000),
-  CONST64(0x2000000000200000), CONST64(0x2000000800200000), CONST64(0x2000000020200000), CONST64(0x2000000820200000),
-  CONST64(0x2000000000002000), CONST64(0x2000000800002000), CONST64(0x2000000020002000), CONST64(0x2000000820002000),
-  CONST64(0x2000000000202000), CONST64(0x2000000800202000), CONST64(0x2000000020202000), CONST64(0x2000000820202000),
-  CONST64(0x2000000000000020), CONST64(0x2000000800000020), CONST64(0x2000000020000020), CONST64(0x2000000820000020),
-  CONST64(0x2000000000200020), CONST64(0x2000000800200020), CONST64(0x2000000020200020), CONST64(0x2000000820200020),
-  CONST64(0x2000000000002020), CONST64(0x2000000800002020), CONST64(0x2000000020002020), CONST64(0x2000000820002020),
-  CONST64(0x2000000000202020), CONST64(0x2000000800202020), CONST64(0x2000000020202020), CONST64(0x2000000820202020),
-  CONST64(0x0020000000000000), CONST64(0x0020000800000000), CONST64(0x0020000020000000), CONST64(0x0020000820000000),
-  CONST64(0x0020000000200000), CONST64(0x0020000800200000), CONST64(0x0020000020200000), CONST64(0x0020000820200000),
-  CONST64(0x0020000000002000), CONST64(0x0020000800002000), CONST64(0x0020000020002000), CONST64(0x0020000820002000),
-  CONST64(0x0020000000202000), CONST64(0x0020000800202000), CONST64(0x0020000020202000), CONST64(0x0020000820202000),
-  CONST64(0x0020000000000020), CONST64(0x0020000800000020), CONST64(0x0020000020000020), CONST64(0x0020000820000020),
-  CONST64(0x0020000000200020), CONST64(0x0020000800200020), CONST64(0x0020000020200020), CONST64(0x0020000820200020),
-  CONST64(0x0020000000002020), CONST64(0x0020000800002020), CONST64(0x0020000020002020), CONST64(0x0020000820002020),
-  CONST64(0x0020000000202020), CONST64(0x0020000800202020), CONST64(0x0020000020202020), CONST64(0x0020000820202020),
-  CONST64(0x2020000000000000), CONST64(0x2020000800000000), CONST64(0x2020000020000000), CONST64(0x2020000820000000),
-  CONST64(0x2020000000200000), CONST64(0x2020000800200000), CONST64(0x2020000020200000), CONST64(0x2020000820200000),
-  CONST64(0x2020000000002000), CONST64(0x2020000800002000), CONST64(0x2020000020002000), CONST64(0x2020000820002000),
-  CONST64(0x2020000000202000), CONST64(0x2020000800202000), CONST64(0x2020000020202000), CONST64(0x2020000820202000),
-  CONST64(0x2020000000000020), CONST64(0x2020000800000020), CONST64(0x2020000020000020), CONST64(0x2020000820000020),
-  CONST64(0x2020000000200020), CONST64(0x2020000800200020), CONST64(0x2020000020200020), CONST64(0x2020000820200020),
-  CONST64(0x2020000000002020), CONST64(0x2020000800002020), CONST64(0x2020000020002020), CONST64(0x2020000820002020),
-  CONST64(0x2020000000202020), CONST64(0x2020000800202020), CONST64(0x2020000020202020), CONST64(0x2020000820202020),
-  CONST64(0x0000200000000000), CONST64(0x0000200800000000), CONST64(0x0000200020000000), CONST64(0x0000200820000000),
-  CONST64(0x0000200000200000), CONST64(0x0000200800200000), CONST64(0x0000200020200000), CONST64(0x0000200820200000),
-  CONST64(0x0000200000002000), CONST64(0x0000200800002000), CONST64(0x0000200020002000), CONST64(0x0000200820002000),
-  CONST64(0x0000200000202000), CONST64(0x0000200800202000), CONST64(0x0000200020202000), CONST64(0x0000200820202000),
-  CONST64(0x0000200000000020), CONST64(0x0000200800000020), CONST64(0x0000200020000020), CONST64(0x0000200820000020),
-  CONST64(0x0000200000200020), CONST64(0x0000200800200020), CONST64(0x0000200020200020), CONST64(0x0000200820200020),
-  CONST64(0x0000200000002020), CONST64(0x0000200800002020), CONST64(0x0000200020002020), CONST64(0x0000200820002020),
-  CONST64(0x0000200000202020), CONST64(0x0000200800202020), CONST64(0x0000200020202020), CONST64(0x0000200820202020),
-  CONST64(0x2000200000000000), CONST64(0x2000200800000000), CONST64(0x2000200020000000), CONST64(0x2000200820000000),
-  CONST64(0x2000200000200000), CONST64(0x2000200800200000), CONST64(0x2000200020200000), CONST64(0x2000200820200000),
-  CONST64(0x2000200000002000), CONST64(0x2000200800002000), CONST64(0x2000200020002000), CONST64(0x2000200820002000),
-  CONST64(0x2000200000202000), CONST64(0x2000200800202000), CONST64(0x2000200020202000), CONST64(0x2000200820202000),
-  CONST64(0x2000200000000020), CONST64(0x2000200800000020), CONST64(0x2000200020000020), CONST64(0x2000200820000020),
-  CONST64(0x2000200000200020), CONST64(0x2000200800200020), CONST64(0x2000200020200020), CONST64(0x2000200820200020),
-  CONST64(0x2000200000002020), CONST64(0x2000200800002020), CONST64(0x2000200020002020), CONST64(0x2000200820002020),
-  CONST64(0x2000200000202020), CONST64(0x2000200800202020), CONST64(0x2000200020202020), CONST64(0x2000200820202020),
-  CONST64(0x0020200000000000), CONST64(0x0020200800000000), CONST64(0x0020200020000000), CONST64(0x0020200820000000),
-  CONST64(0x0020200000200000), CONST64(0x0020200800200000), CONST64(0x0020200020200000), CONST64(0x0020200820200000),
-  CONST64(0x0020200000002000), CONST64(0x0020200800002000), CONST64(0x0020200020002000), CONST64(0x0020200820002000),
-  CONST64(0x0020200000202000), CONST64(0x0020200800202000), CONST64(0x0020200020202000), CONST64(0x0020200820202000),
-  CONST64(0x0020200000000020), CONST64(0x0020200800000020), CONST64(0x0020200020000020), CONST64(0x0020200820000020),
-  CONST64(0x0020200000200020), CONST64(0x0020200800200020), CONST64(0x0020200020200020), CONST64(0x0020200820200020),
-  CONST64(0x0020200000002020), CONST64(0x0020200800002020), CONST64(0x0020200020002020), CONST64(0x0020200820002020),
-  CONST64(0x0020200000202020), CONST64(0x0020200800202020), CONST64(0x0020200020202020), CONST64(0x0020200820202020),
-  CONST64(0x2020200000000000), CONST64(0x2020200800000000), CONST64(0x2020200020000000), CONST64(0x2020200820000000),
-  CONST64(0x2020200000200000), CONST64(0x2020200800200000), CONST64(0x2020200020200000), CONST64(0x2020200820200000),
-  CONST64(0x2020200000002000), CONST64(0x2020200800002000), CONST64(0x2020200020002000), CONST64(0x2020200820002000),
-  CONST64(0x2020200000202000), CONST64(0x2020200800202000), CONST64(0x2020200020202000), CONST64(0x2020200820202000),
-  CONST64(0x2020200000000020), CONST64(0x2020200800000020), CONST64(0x2020200020000020), CONST64(0x2020200820000020),
-  CONST64(0x2020200000200020), CONST64(0x2020200800200020), CONST64(0x2020200020200020), CONST64(0x2020200820200020),
-  CONST64(0x2020200000002020), CONST64(0x2020200800002020), CONST64(0x2020200020002020), CONST64(0x2020200820002020),
-  CONST64(0x2020200000202020), CONST64(0x2020200800202020), CONST64(0x2020200020202020), CONST64(0x2020200820202020)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000002000000000), CONST64(0x0000000080000000), CONST64(0x0000002080000000),
-  CONST64(0x0000000000800000), CONST64(0x0000002000800000), CONST64(0x0000000080800000), CONST64(0x0000002080800000),
-  CONST64(0x0000000000008000), CONST64(0x0000002000008000), CONST64(0x0000000080008000), CONST64(0x0000002080008000),
-  CONST64(0x0000000000808000), CONST64(0x0000002000808000), CONST64(0x0000000080808000), CONST64(0x0000002080808000),
-  CONST64(0x0000000000000080), CONST64(0x0000002000000080), CONST64(0x0000000080000080), CONST64(0x0000002080000080),
-  CONST64(0x0000000000800080), CONST64(0x0000002000800080), CONST64(0x0000000080800080), CONST64(0x0000002080800080),
-  CONST64(0x0000000000008080), CONST64(0x0000002000008080), CONST64(0x0000000080008080), CONST64(0x0000002080008080),
-  CONST64(0x0000000000808080), CONST64(0x0000002000808080), CONST64(0x0000000080808080), CONST64(0x0000002080808080),
-  CONST64(0x8000000000000000), CONST64(0x8000002000000000), CONST64(0x8000000080000000), CONST64(0x8000002080000000),
-  CONST64(0x8000000000800000), CONST64(0x8000002000800000), CONST64(0x8000000080800000), CONST64(0x8000002080800000),
-  CONST64(0x8000000000008000), CONST64(0x8000002000008000), CONST64(0x8000000080008000), CONST64(0x8000002080008000),
-  CONST64(0x8000000000808000), CONST64(0x8000002000808000), CONST64(0x8000000080808000), CONST64(0x8000002080808000),
-  CONST64(0x8000000000000080), CONST64(0x8000002000000080), CONST64(0x8000000080000080), CONST64(0x8000002080000080),
-  CONST64(0x8000000000800080), CONST64(0x8000002000800080), CONST64(0x8000000080800080), CONST64(0x8000002080800080),
-  CONST64(0x8000000000008080), CONST64(0x8000002000008080), CONST64(0x8000000080008080), CONST64(0x8000002080008080),
-  CONST64(0x8000000000808080), CONST64(0x8000002000808080), CONST64(0x8000000080808080), CONST64(0x8000002080808080),
-  CONST64(0x0080000000000000), CONST64(0x0080002000000000), CONST64(0x0080000080000000), CONST64(0x0080002080000000),
-  CONST64(0x0080000000800000), CONST64(0x0080002000800000), CONST64(0x0080000080800000), CONST64(0x0080002080800000),
-  CONST64(0x0080000000008000), CONST64(0x0080002000008000), CONST64(0x0080000080008000), CONST64(0x0080002080008000),
-  CONST64(0x0080000000808000), CONST64(0x0080002000808000), CONST64(0x0080000080808000), CONST64(0x0080002080808000),
-  CONST64(0x0080000000000080), CONST64(0x0080002000000080), CONST64(0x0080000080000080), CONST64(0x0080002080000080),
-  CONST64(0x0080000000800080), CONST64(0x0080002000800080), CONST64(0x0080000080800080), CONST64(0x0080002080800080),
-  CONST64(0x0080000000008080), CONST64(0x0080002000008080), CONST64(0x0080000080008080), CONST64(0x0080002080008080),
-  CONST64(0x0080000000808080), CONST64(0x0080002000808080), CONST64(0x0080000080808080), CONST64(0x0080002080808080),
-  CONST64(0x8080000000000000), CONST64(0x8080002000000000), CONST64(0x8080000080000000), CONST64(0x8080002080000000),
-  CONST64(0x8080000000800000), CONST64(0x8080002000800000), CONST64(0x8080000080800000), CONST64(0x8080002080800000),
-  CONST64(0x8080000000008000), CONST64(0x8080002000008000), CONST64(0x8080000080008000), CONST64(0x8080002080008000),
-  CONST64(0x8080000000808000), CONST64(0x8080002000808000), CONST64(0x8080000080808000), CONST64(0x8080002080808000),
-  CONST64(0x8080000000000080), CONST64(0x8080002000000080), CONST64(0x8080000080000080), CONST64(0x8080002080000080),
-  CONST64(0x8080000000800080), CONST64(0x8080002000800080), CONST64(0x8080000080800080), CONST64(0x8080002080800080),
-  CONST64(0x8080000000008080), CONST64(0x8080002000008080), CONST64(0x8080000080008080), CONST64(0x8080002080008080),
-  CONST64(0x8080000000808080), CONST64(0x8080002000808080), CONST64(0x8080000080808080), CONST64(0x8080002080808080),
-  CONST64(0x0000800000000000), CONST64(0x0000802000000000), CONST64(0x0000800080000000), CONST64(0x0000802080000000),
-  CONST64(0x0000800000800000), CONST64(0x0000802000800000), CONST64(0x0000800080800000), CONST64(0x0000802080800000),
-  CONST64(0x0000800000008000), CONST64(0x0000802000008000), CONST64(0x0000800080008000), CONST64(0x0000802080008000),
-  CONST64(0x0000800000808000), CONST64(0x0000802000808000), CONST64(0x0000800080808000), CONST64(0x0000802080808000),
-  CONST64(0x0000800000000080), CONST64(0x0000802000000080), CONST64(0x0000800080000080), CONST64(0x0000802080000080),
-  CONST64(0x0000800000800080), CONST64(0x0000802000800080), CONST64(0x0000800080800080), CONST64(0x0000802080800080),
-  CONST64(0x0000800000008080), CONST64(0x0000802000008080), CONST64(0x0000800080008080), CONST64(0x0000802080008080),
-  CONST64(0x0000800000808080), CONST64(0x0000802000808080), CONST64(0x0000800080808080), CONST64(0x0000802080808080),
-  CONST64(0x8000800000000000), CONST64(0x8000802000000000), CONST64(0x8000800080000000), CONST64(0x8000802080000000),
-  CONST64(0x8000800000800000), CONST64(0x8000802000800000), CONST64(0x8000800080800000), CONST64(0x8000802080800000),
-  CONST64(0x8000800000008000), CONST64(0x8000802000008000), CONST64(0x8000800080008000), CONST64(0x8000802080008000),
-  CONST64(0x8000800000808000), CONST64(0x8000802000808000), CONST64(0x8000800080808000), CONST64(0x8000802080808000),
-  CONST64(0x8000800000000080), CONST64(0x8000802000000080), CONST64(0x8000800080000080), CONST64(0x8000802080000080),
-  CONST64(0x8000800000800080), CONST64(0x8000802000800080), CONST64(0x8000800080800080), CONST64(0x8000802080800080),
-  CONST64(0x8000800000008080), CONST64(0x8000802000008080), CONST64(0x8000800080008080), CONST64(0x8000802080008080),
-  CONST64(0x8000800000808080), CONST64(0x8000802000808080), CONST64(0x8000800080808080), CONST64(0x8000802080808080),
-  CONST64(0x0080800000000000), CONST64(0x0080802000000000), CONST64(0x0080800080000000), CONST64(0x0080802080000000),
-  CONST64(0x0080800000800000), CONST64(0x0080802000800000), CONST64(0x0080800080800000), CONST64(0x0080802080800000),
-  CONST64(0x0080800000008000), CONST64(0x0080802000008000), CONST64(0x0080800080008000), CONST64(0x0080802080008000),
-  CONST64(0x0080800000808000), CONST64(0x0080802000808000), CONST64(0x0080800080808000), CONST64(0x0080802080808000),
-  CONST64(0x0080800000000080), CONST64(0x0080802000000080), CONST64(0x0080800080000080), CONST64(0x0080802080000080),
-  CONST64(0x0080800000800080), CONST64(0x0080802000800080), CONST64(0x0080800080800080), CONST64(0x0080802080800080),
-  CONST64(0x0080800000008080), CONST64(0x0080802000008080), CONST64(0x0080800080008080), CONST64(0x0080802080008080),
-  CONST64(0x0080800000808080), CONST64(0x0080802000808080), CONST64(0x0080800080808080), CONST64(0x0080802080808080),
-  CONST64(0x8080800000000000), CONST64(0x8080802000000000), CONST64(0x8080800080000000), CONST64(0x8080802080000000),
-  CONST64(0x8080800000800000), CONST64(0x8080802000800000), CONST64(0x8080800080800000), CONST64(0x8080802080800000),
-  CONST64(0x8080800000008000), CONST64(0x8080802000008000), CONST64(0x8080800080008000), CONST64(0x8080802080008000),
-  CONST64(0x8080800000808000), CONST64(0x8080802000808000), CONST64(0x8080800080808000), CONST64(0x8080802080808000),
-  CONST64(0x8080800000000080), CONST64(0x8080802000000080), CONST64(0x8080800080000080), CONST64(0x8080802080000080),
-  CONST64(0x8080800000800080), CONST64(0x8080802000800080), CONST64(0x8080800080800080), CONST64(0x8080802080800080),
-  CONST64(0x8080800000008080), CONST64(0x8080802000008080), CONST64(0x8080800080008080), CONST64(0x8080802080008080),
-  CONST64(0x8080800000808080), CONST64(0x8080802000808080), CONST64(0x8080800080808080), CONST64(0x8080802080808080)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000004000000000), CONST64(0x0000000001000000), CONST64(0x0000004001000000),
-  CONST64(0x0000000000010000), CONST64(0x0000004000010000), CONST64(0x0000000001010000), CONST64(0x0000004001010000),
-  CONST64(0x0000000000000100), CONST64(0x0000004000000100), CONST64(0x0000000001000100), CONST64(0x0000004001000100),
-  CONST64(0x0000000000010100), CONST64(0x0000004000010100), CONST64(0x0000000001010100), CONST64(0x0000004001010100),
-  CONST64(0x0000000000000001), CONST64(0x0000004000000001), CONST64(0x0000000001000001), CONST64(0x0000004001000001),
-  CONST64(0x0000000000010001), CONST64(0x0000004000010001), CONST64(0x0000000001010001), CONST64(0x0000004001010001),
-  CONST64(0x0000000000000101), CONST64(0x0000004000000101), CONST64(0x0000000001000101), CONST64(0x0000004001000101),
-  CONST64(0x0000000000010101), CONST64(0x0000004000010101), CONST64(0x0000000001010101), CONST64(0x0000004001010101),
-  CONST64(0x0100000000000000), CONST64(0x0100004000000000), CONST64(0x0100000001000000), CONST64(0x0100004001000000),
-  CONST64(0x0100000000010000), CONST64(0x0100004000010000), CONST64(0x0100000001010000), CONST64(0x0100004001010000),
-  CONST64(0x0100000000000100), CONST64(0x0100004000000100), CONST64(0x0100000001000100), CONST64(0x0100004001000100),
-  CONST64(0x0100000000010100), CONST64(0x0100004000010100), CONST64(0x0100000001010100), CONST64(0x0100004001010100),
-  CONST64(0x0100000000000001), CONST64(0x0100004000000001), CONST64(0x0100000001000001), CONST64(0x0100004001000001),
-  CONST64(0x0100000000010001), CONST64(0x0100004000010001), CONST64(0x0100000001010001), CONST64(0x0100004001010001),
-  CONST64(0x0100000000000101), CONST64(0x0100004000000101), CONST64(0x0100000001000101), CONST64(0x0100004001000101),
-  CONST64(0x0100000000010101), CONST64(0x0100004000010101), CONST64(0x0100000001010101), CONST64(0x0100004001010101),
-  CONST64(0x0001000000000000), CONST64(0x0001004000000000), CONST64(0x0001000001000000), CONST64(0x0001004001000000),
-  CONST64(0x0001000000010000), CONST64(0x0001004000010000), CONST64(0x0001000001010000), CONST64(0x0001004001010000),
-  CONST64(0x0001000000000100), CONST64(0x0001004000000100), CONST64(0x0001000001000100), CONST64(0x0001004001000100),
-  CONST64(0x0001000000010100), CONST64(0x0001004000010100), CONST64(0x0001000001010100), CONST64(0x0001004001010100),
-  CONST64(0x0001000000000001), CONST64(0x0001004000000001), CONST64(0x0001000001000001), CONST64(0x0001004001000001),
-  CONST64(0x0001000000010001), CONST64(0x0001004000010001), CONST64(0x0001000001010001), CONST64(0x0001004001010001),
-  CONST64(0x0001000000000101), CONST64(0x0001004000000101), CONST64(0x0001000001000101), CONST64(0x0001004001000101),
-  CONST64(0x0001000000010101), CONST64(0x0001004000010101), CONST64(0x0001000001010101), CONST64(0x0001004001010101),
-  CONST64(0x0101000000000000), CONST64(0x0101004000000000), CONST64(0x0101000001000000), CONST64(0x0101004001000000),
-  CONST64(0x0101000000010000), CONST64(0x0101004000010000), CONST64(0x0101000001010000), CONST64(0x0101004001010000),
-  CONST64(0x0101000000000100), CONST64(0x0101004000000100), CONST64(0x0101000001000100), CONST64(0x0101004001000100),
-  CONST64(0x0101000000010100), CONST64(0x0101004000010100), CONST64(0x0101000001010100), CONST64(0x0101004001010100),
-  CONST64(0x0101000000000001), CONST64(0x0101004000000001), CONST64(0x0101000001000001), CONST64(0x0101004001000001),
-  CONST64(0x0101000000010001), CONST64(0x0101004000010001), CONST64(0x0101000001010001), CONST64(0x0101004001010001),
-  CONST64(0x0101000000000101), CONST64(0x0101004000000101), CONST64(0x0101000001000101), CONST64(0x0101004001000101),
-  CONST64(0x0101000000010101), CONST64(0x0101004000010101), CONST64(0x0101000001010101), CONST64(0x0101004001010101),
-  CONST64(0x0000010000000000), CONST64(0x0000014000000000), CONST64(0x0000010001000000), CONST64(0x0000014001000000),
-  CONST64(0x0000010000010000), CONST64(0x0000014000010000), CONST64(0x0000010001010000), CONST64(0x0000014001010000),
-  CONST64(0x0000010000000100), CONST64(0x0000014000000100), CONST64(0x0000010001000100), CONST64(0x0000014001000100),
-  CONST64(0x0000010000010100), CONST64(0x0000014000010100), CONST64(0x0000010001010100), CONST64(0x0000014001010100),
-  CONST64(0x0000010000000001), CONST64(0x0000014000000001), CONST64(0x0000010001000001), CONST64(0x0000014001000001),
-  CONST64(0x0000010000010001), CONST64(0x0000014000010001), CONST64(0x0000010001010001), CONST64(0x0000014001010001),
-  CONST64(0x0000010000000101), CONST64(0x0000014000000101), CONST64(0x0000010001000101), CONST64(0x0000014001000101),
-  CONST64(0x0000010000010101), CONST64(0x0000014000010101), CONST64(0x0000010001010101), CONST64(0x0000014001010101),
-  CONST64(0x0100010000000000), CONST64(0x0100014000000000), CONST64(0x0100010001000000), CONST64(0x0100014001000000),
-  CONST64(0x0100010000010000), CONST64(0x0100014000010000), CONST64(0x0100010001010000), CONST64(0x0100014001010000),
-  CONST64(0x0100010000000100), CONST64(0x0100014000000100), CONST64(0x0100010001000100), CONST64(0x0100014001000100),
-  CONST64(0x0100010000010100), CONST64(0x0100014000010100), CONST64(0x0100010001010100), CONST64(0x0100014001010100),
-  CONST64(0x0100010000000001), CONST64(0x0100014000000001), CONST64(0x0100010001000001), CONST64(0x0100014001000001),
-  CONST64(0x0100010000010001), CONST64(0x0100014000010001), CONST64(0x0100010001010001), CONST64(0x0100014001010001),
-  CONST64(0x0100010000000101), CONST64(0x0100014000000101), CONST64(0x0100010001000101), CONST64(0x0100014001000101),
-  CONST64(0x0100010000010101), CONST64(0x0100014000010101), CONST64(0x0100010001010101), CONST64(0x0100014001010101),
-  CONST64(0x0001010000000000), CONST64(0x0001014000000000), CONST64(0x0001010001000000), CONST64(0x0001014001000000),
-  CONST64(0x0001010000010000), CONST64(0x0001014000010000), CONST64(0x0001010001010000), CONST64(0x0001014001010000),
-  CONST64(0x0001010000000100), CONST64(0x0001014000000100), CONST64(0x0001010001000100), CONST64(0x0001014001000100),
-  CONST64(0x0001010000010100), CONST64(0x0001014000010100), CONST64(0x0001010001010100), CONST64(0x0001014001010100),
-  CONST64(0x0001010000000001), CONST64(0x0001014000000001), CONST64(0x0001010001000001), CONST64(0x0001014001000001),
-  CONST64(0x0001010000010001), CONST64(0x0001014000010001), CONST64(0x0001010001010001), CONST64(0x0001014001010001),
-  CONST64(0x0001010000000101), CONST64(0x0001014000000101), CONST64(0x0001010001000101), CONST64(0x0001014001000101),
-  CONST64(0x0001010000010101), CONST64(0x0001014000010101), CONST64(0x0001010001010101), CONST64(0x0001014001010101),
-  CONST64(0x0101010000000000), CONST64(0x0101014000000000), CONST64(0x0101010001000000), CONST64(0x0101014001000000),
-  CONST64(0x0101010000010000), CONST64(0x0101014000010000), CONST64(0x0101010001010000), CONST64(0x0101014001010000),
-  CONST64(0x0101010000000100), CONST64(0x0101014000000100), CONST64(0x0101010001000100), CONST64(0x0101014001000100),
-  CONST64(0x0101010000010100), CONST64(0x0101014000010100), CONST64(0x0101010001010100), CONST64(0x0101014001010100),
-  CONST64(0x0101010000000001), CONST64(0x0101014000000001), CONST64(0x0101010001000001), CONST64(0x0101014001000001),
-  CONST64(0x0101010000010001), CONST64(0x0101014000010001), CONST64(0x0101010001010001), CONST64(0x0101014001010001),
-  CONST64(0x0101010000000101), CONST64(0x0101014000000101), CONST64(0x0101010001000101), CONST64(0x0101014001000101),
-  CONST64(0x0101010000010101), CONST64(0x0101014000010101), CONST64(0x0101010001010101), CONST64(0x0101014001010101)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000100000000), CONST64(0x0000000004000000), CONST64(0x0000000104000000),
-  CONST64(0x0000000000040000), CONST64(0x0000000100040000), CONST64(0x0000000004040000), CONST64(0x0000000104040000),
-  CONST64(0x0000000000000400), CONST64(0x0000000100000400), CONST64(0x0000000004000400), CONST64(0x0000000104000400),
-  CONST64(0x0000000000040400), CONST64(0x0000000100040400), CONST64(0x0000000004040400), CONST64(0x0000000104040400),
-  CONST64(0x0000000000000004), CONST64(0x0000000100000004), CONST64(0x0000000004000004), CONST64(0x0000000104000004),
-  CONST64(0x0000000000040004), CONST64(0x0000000100040004), CONST64(0x0000000004040004), CONST64(0x0000000104040004),
-  CONST64(0x0000000000000404), CONST64(0x0000000100000404), CONST64(0x0000000004000404), CONST64(0x0000000104000404),
-  CONST64(0x0000000000040404), CONST64(0x0000000100040404), CONST64(0x0000000004040404), CONST64(0x0000000104040404),
-  CONST64(0x0400000000000000), CONST64(0x0400000100000000), CONST64(0x0400000004000000), CONST64(0x0400000104000000),
-  CONST64(0x0400000000040000), CONST64(0x0400000100040000), CONST64(0x0400000004040000), CONST64(0x0400000104040000),
-  CONST64(0x0400000000000400), CONST64(0x0400000100000400), CONST64(0x0400000004000400), CONST64(0x0400000104000400),
-  CONST64(0x0400000000040400), CONST64(0x0400000100040400), CONST64(0x0400000004040400), CONST64(0x0400000104040400),
-  CONST64(0x0400000000000004), CONST64(0x0400000100000004), CONST64(0x0400000004000004), CONST64(0x0400000104000004),
-  CONST64(0x0400000000040004), CONST64(0x0400000100040004), CONST64(0x0400000004040004), CONST64(0x0400000104040004),
-  CONST64(0x0400000000000404), CONST64(0x0400000100000404), CONST64(0x0400000004000404), CONST64(0x0400000104000404),
-  CONST64(0x0400000000040404), CONST64(0x0400000100040404), CONST64(0x0400000004040404), CONST64(0x0400000104040404),
-  CONST64(0x0004000000000000), CONST64(0x0004000100000000), CONST64(0x0004000004000000), CONST64(0x0004000104000000),
-  CONST64(0x0004000000040000), CONST64(0x0004000100040000), CONST64(0x0004000004040000), CONST64(0x0004000104040000),
-  CONST64(0x0004000000000400), CONST64(0x0004000100000400), CONST64(0x0004000004000400), CONST64(0x0004000104000400),
-  CONST64(0x0004000000040400), CONST64(0x0004000100040400), CONST64(0x0004000004040400), CONST64(0x0004000104040400),
-  CONST64(0x0004000000000004), CONST64(0x0004000100000004), CONST64(0x0004000004000004), CONST64(0x0004000104000004),
-  CONST64(0x0004000000040004), CONST64(0x0004000100040004), CONST64(0x0004000004040004), CONST64(0x0004000104040004),
-  CONST64(0x0004000000000404), CONST64(0x0004000100000404), CONST64(0x0004000004000404), CONST64(0x0004000104000404),
-  CONST64(0x0004000000040404), CONST64(0x0004000100040404), CONST64(0x0004000004040404), CONST64(0x0004000104040404),
-  CONST64(0x0404000000000000), CONST64(0x0404000100000000), CONST64(0x0404000004000000), CONST64(0x0404000104000000),
-  CONST64(0x0404000000040000), CONST64(0x0404000100040000), CONST64(0x0404000004040000), CONST64(0x0404000104040000),
-  CONST64(0x0404000000000400), CONST64(0x0404000100000400), CONST64(0x0404000004000400), CONST64(0x0404000104000400),
-  CONST64(0x0404000000040400), CONST64(0x0404000100040400), CONST64(0x0404000004040400), CONST64(0x0404000104040400),
-  CONST64(0x0404000000000004), CONST64(0x0404000100000004), CONST64(0x0404000004000004), CONST64(0x0404000104000004),
-  CONST64(0x0404000000040004), CONST64(0x0404000100040004), CONST64(0x0404000004040004), CONST64(0x0404000104040004),
-  CONST64(0x0404000000000404), CONST64(0x0404000100000404), CONST64(0x0404000004000404), CONST64(0x0404000104000404),
-  CONST64(0x0404000000040404), CONST64(0x0404000100040404), CONST64(0x0404000004040404), CONST64(0x0404000104040404),
-  CONST64(0x0000040000000000), CONST64(0x0000040100000000), CONST64(0x0000040004000000), CONST64(0x0000040104000000),
-  CONST64(0x0000040000040000), CONST64(0x0000040100040000), CONST64(0x0000040004040000), CONST64(0x0000040104040000),
-  CONST64(0x0000040000000400), CONST64(0x0000040100000400), CONST64(0x0000040004000400), CONST64(0x0000040104000400),
-  CONST64(0x0000040000040400), CONST64(0x0000040100040400), CONST64(0x0000040004040400), CONST64(0x0000040104040400),
-  CONST64(0x0000040000000004), CONST64(0x0000040100000004), CONST64(0x0000040004000004), CONST64(0x0000040104000004),
-  CONST64(0x0000040000040004), CONST64(0x0000040100040004), CONST64(0x0000040004040004), CONST64(0x0000040104040004),
-  CONST64(0x0000040000000404), CONST64(0x0000040100000404), CONST64(0x0000040004000404), CONST64(0x0000040104000404),
-  CONST64(0x0000040000040404), CONST64(0x0000040100040404), CONST64(0x0000040004040404), CONST64(0x0000040104040404),
-  CONST64(0x0400040000000000), CONST64(0x0400040100000000), CONST64(0x0400040004000000), CONST64(0x0400040104000000),
-  CONST64(0x0400040000040000), CONST64(0x0400040100040000), CONST64(0x0400040004040000), CONST64(0x0400040104040000),
-  CONST64(0x0400040000000400), CONST64(0x0400040100000400), CONST64(0x0400040004000400), CONST64(0x0400040104000400),
-  CONST64(0x0400040000040400), CONST64(0x0400040100040400), CONST64(0x0400040004040400), CONST64(0x0400040104040400),
-  CONST64(0x0400040000000004), CONST64(0x0400040100000004), CONST64(0x0400040004000004), CONST64(0x0400040104000004),
-  CONST64(0x0400040000040004), CONST64(0x0400040100040004), CONST64(0x0400040004040004), CONST64(0x0400040104040004),
-  CONST64(0x0400040000000404), CONST64(0x0400040100000404), CONST64(0x0400040004000404), CONST64(0x0400040104000404),
-  CONST64(0x0400040000040404), CONST64(0x0400040100040404), CONST64(0x0400040004040404), CONST64(0x0400040104040404),
-  CONST64(0x0004040000000000), CONST64(0x0004040100000000), CONST64(0x0004040004000000), CONST64(0x0004040104000000),
-  CONST64(0x0004040000040000), CONST64(0x0004040100040000), CONST64(0x0004040004040000), CONST64(0x0004040104040000),
-  CONST64(0x0004040000000400), CONST64(0x0004040100000400), CONST64(0x0004040004000400), CONST64(0x0004040104000400),
-  CONST64(0x0004040000040400), CONST64(0x0004040100040400), CONST64(0x0004040004040400), CONST64(0x0004040104040400),
-  CONST64(0x0004040000000004), CONST64(0x0004040100000004), CONST64(0x0004040004000004), CONST64(0x0004040104000004),
-  CONST64(0x0004040000040004), CONST64(0x0004040100040004), CONST64(0x0004040004040004), CONST64(0x0004040104040004),
-  CONST64(0x0004040000000404), CONST64(0x0004040100000404), CONST64(0x0004040004000404), CONST64(0x0004040104000404),
-  CONST64(0x0004040000040404), CONST64(0x0004040100040404), CONST64(0x0004040004040404), CONST64(0x0004040104040404),
-  CONST64(0x0404040000000000), CONST64(0x0404040100000000), CONST64(0x0404040004000000), CONST64(0x0404040104000000),
-  CONST64(0x0404040000040000), CONST64(0x0404040100040000), CONST64(0x0404040004040000), CONST64(0x0404040104040000),
-  CONST64(0x0404040000000400), CONST64(0x0404040100000400), CONST64(0x0404040004000400), CONST64(0x0404040104000400),
-  CONST64(0x0404040000040400), CONST64(0x0404040100040400), CONST64(0x0404040004040400), CONST64(0x0404040104040400),
-  CONST64(0x0404040000000004), CONST64(0x0404040100000004), CONST64(0x0404040004000004), CONST64(0x0404040104000004),
-  CONST64(0x0404040000040004), CONST64(0x0404040100040004), CONST64(0x0404040004040004), CONST64(0x0404040104040004),
-  CONST64(0x0404040000000404), CONST64(0x0404040100000404), CONST64(0x0404040004000404), CONST64(0x0404040104000404),
-  CONST64(0x0404040000040404), CONST64(0x0404040100040404), CONST64(0x0404040004040404), CONST64(0x0404040104040404)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000000400000000), CONST64(0x0000000010000000), CONST64(0x0000000410000000),
-  CONST64(0x0000000000100000), CONST64(0x0000000400100000), CONST64(0x0000000010100000), CONST64(0x0000000410100000),
-  CONST64(0x0000000000001000), CONST64(0x0000000400001000), CONST64(0x0000000010001000), CONST64(0x0000000410001000),
-  CONST64(0x0000000000101000), CONST64(0x0000000400101000), CONST64(0x0000000010101000), CONST64(0x0000000410101000),
-  CONST64(0x0000000000000010), CONST64(0x0000000400000010), CONST64(0x0000000010000010), CONST64(0x0000000410000010),
-  CONST64(0x0000000000100010), CONST64(0x0000000400100010), CONST64(0x0000000010100010), CONST64(0x0000000410100010),
-  CONST64(0x0000000000001010), CONST64(0x0000000400001010), CONST64(0x0000000010001010), CONST64(0x0000000410001010),
-  CONST64(0x0000000000101010), CONST64(0x0000000400101010), CONST64(0x0000000010101010), CONST64(0x0000000410101010),
-  CONST64(0x1000000000000000), CONST64(0x1000000400000000), CONST64(0x1000000010000000), CONST64(0x1000000410000000),
-  CONST64(0x1000000000100000), CONST64(0x1000000400100000), CONST64(0x1000000010100000), CONST64(0x1000000410100000),
-  CONST64(0x1000000000001000), CONST64(0x1000000400001000), CONST64(0x1000000010001000), CONST64(0x1000000410001000),
-  CONST64(0x1000000000101000), CONST64(0x1000000400101000), CONST64(0x1000000010101000), CONST64(0x1000000410101000),
-  CONST64(0x1000000000000010), CONST64(0x1000000400000010), CONST64(0x1000000010000010), CONST64(0x1000000410000010),
-  CONST64(0x1000000000100010), CONST64(0x1000000400100010), CONST64(0x1000000010100010), CONST64(0x1000000410100010),
-  CONST64(0x1000000000001010), CONST64(0x1000000400001010), CONST64(0x1000000010001010), CONST64(0x1000000410001010),
-  CONST64(0x1000000000101010), CONST64(0x1000000400101010), CONST64(0x1000000010101010), CONST64(0x1000000410101010),
-  CONST64(0x0010000000000000), CONST64(0x0010000400000000), CONST64(0x0010000010000000), CONST64(0x0010000410000000),
-  CONST64(0x0010000000100000), CONST64(0x0010000400100000), CONST64(0x0010000010100000), CONST64(0x0010000410100000),
-  CONST64(0x0010000000001000), CONST64(0x0010000400001000), CONST64(0x0010000010001000), CONST64(0x0010000410001000),
-  CONST64(0x0010000000101000), CONST64(0x0010000400101000), CONST64(0x0010000010101000), CONST64(0x0010000410101000),
-  CONST64(0x0010000000000010), CONST64(0x0010000400000010), CONST64(0x0010000010000010), CONST64(0x0010000410000010),
-  CONST64(0x0010000000100010), CONST64(0x0010000400100010), CONST64(0x0010000010100010), CONST64(0x0010000410100010),
-  CONST64(0x0010000000001010), CONST64(0x0010000400001010), CONST64(0x0010000010001010), CONST64(0x0010000410001010),
-  CONST64(0x0010000000101010), CONST64(0x0010000400101010), CONST64(0x0010000010101010), CONST64(0x0010000410101010),
-  CONST64(0x1010000000000000), CONST64(0x1010000400000000), CONST64(0x1010000010000000), CONST64(0x1010000410000000),
-  CONST64(0x1010000000100000), CONST64(0x1010000400100000), CONST64(0x1010000010100000), CONST64(0x1010000410100000),
-  CONST64(0x1010000000001000), CONST64(0x1010000400001000), CONST64(0x1010000010001000), CONST64(0x1010000410001000),
-  CONST64(0x1010000000101000), CONST64(0x1010000400101000), CONST64(0x1010000010101000), CONST64(0x1010000410101000),
-  CONST64(0x1010000000000010), CONST64(0x1010000400000010), CONST64(0x1010000010000010), CONST64(0x1010000410000010),
-  CONST64(0x1010000000100010), CONST64(0x1010000400100010), CONST64(0x1010000010100010), CONST64(0x1010000410100010),
-  CONST64(0x1010000000001010), CONST64(0x1010000400001010), CONST64(0x1010000010001010), CONST64(0x1010000410001010),
-  CONST64(0x1010000000101010), CONST64(0x1010000400101010), CONST64(0x1010000010101010), CONST64(0x1010000410101010),
-  CONST64(0x0000100000000000), CONST64(0x0000100400000000), CONST64(0x0000100010000000), CONST64(0x0000100410000000),
-  CONST64(0x0000100000100000), CONST64(0x0000100400100000), CONST64(0x0000100010100000), CONST64(0x0000100410100000),
-  CONST64(0x0000100000001000), CONST64(0x0000100400001000), CONST64(0x0000100010001000), CONST64(0x0000100410001000),
-  CONST64(0x0000100000101000), CONST64(0x0000100400101000), CONST64(0x0000100010101000), CONST64(0x0000100410101000),
-  CONST64(0x0000100000000010), CONST64(0x0000100400000010), CONST64(0x0000100010000010), CONST64(0x0000100410000010),
-  CONST64(0x0000100000100010), CONST64(0x0000100400100010), CONST64(0x0000100010100010), CONST64(0x0000100410100010),
-  CONST64(0x0000100000001010), CONST64(0x0000100400001010), CONST64(0x0000100010001010), CONST64(0x0000100410001010),
-  CONST64(0x0000100000101010), CONST64(0x0000100400101010), CONST64(0x0000100010101010), CONST64(0x0000100410101010),
-  CONST64(0x1000100000000000), CONST64(0x1000100400000000), CONST64(0x1000100010000000), CONST64(0x1000100410000000),
-  CONST64(0x1000100000100000), CONST64(0x1000100400100000), CONST64(0x1000100010100000), CONST64(0x1000100410100000),
-  CONST64(0x1000100000001000), CONST64(0x1000100400001000), CONST64(0x1000100010001000), CONST64(0x1000100410001000),
-  CONST64(0x1000100000101000), CONST64(0x1000100400101000), CONST64(0x1000100010101000), CONST64(0x1000100410101000),
-  CONST64(0x1000100000000010), CONST64(0x1000100400000010), CONST64(0x1000100010000010), CONST64(0x1000100410000010),
-  CONST64(0x1000100000100010), CONST64(0x1000100400100010), CONST64(0x1000100010100010), CONST64(0x1000100410100010),
-  CONST64(0x1000100000001010), CONST64(0x1000100400001010), CONST64(0x1000100010001010), CONST64(0x1000100410001010),
-  CONST64(0x1000100000101010), CONST64(0x1000100400101010), CONST64(0x1000100010101010), CONST64(0x1000100410101010),
-  CONST64(0x0010100000000000), CONST64(0x0010100400000000), CONST64(0x0010100010000000), CONST64(0x0010100410000000),
-  CONST64(0x0010100000100000), CONST64(0x0010100400100000), CONST64(0x0010100010100000), CONST64(0x0010100410100000),
-  CONST64(0x0010100000001000), CONST64(0x0010100400001000), CONST64(0x0010100010001000), CONST64(0x0010100410001000),
-  CONST64(0x0010100000101000), CONST64(0x0010100400101000), CONST64(0x0010100010101000), CONST64(0x0010100410101000),
-  CONST64(0x0010100000000010), CONST64(0x0010100400000010), CONST64(0x0010100010000010), CONST64(0x0010100410000010),
-  CONST64(0x0010100000100010), CONST64(0x0010100400100010), CONST64(0x0010100010100010), CONST64(0x0010100410100010),
-  CONST64(0x0010100000001010), CONST64(0x0010100400001010), CONST64(0x0010100010001010), CONST64(0x0010100410001010),
-  CONST64(0x0010100000101010), CONST64(0x0010100400101010), CONST64(0x0010100010101010), CONST64(0x0010100410101010),
-  CONST64(0x1010100000000000), CONST64(0x1010100400000000), CONST64(0x1010100010000000), CONST64(0x1010100410000000),
-  CONST64(0x1010100000100000), CONST64(0x1010100400100000), CONST64(0x1010100010100000), CONST64(0x1010100410100000),
-  CONST64(0x1010100000001000), CONST64(0x1010100400001000), CONST64(0x1010100010001000), CONST64(0x1010100410001000),
-  CONST64(0x1010100000101000), CONST64(0x1010100400101000), CONST64(0x1010100010101000), CONST64(0x1010100410101000),
-  CONST64(0x1010100000000010), CONST64(0x1010100400000010), CONST64(0x1010100010000010), CONST64(0x1010100410000010),
-  CONST64(0x1010100000100010), CONST64(0x1010100400100010), CONST64(0x1010100010100010), CONST64(0x1010100410100010),
-  CONST64(0x1010100000001010), CONST64(0x1010100400001010), CONST64(0x1010100010001010), CONST64(0x1010100410001010),
-  CONST64(0x1010100000101010), CONST64(0x1010100400101010), CONST64(0x1010100010101010), CONST64(0x1010100410101010)
-  },
-{ CONST64(0x0000000000000000), CONST64(0x0000001000000000), CONST64(0x0000000040000000), CONST64(0x0000001040000000),
-  CONST64(0x0000000000400000), CONST64(0x0000001000400000), CONST64(0x0000000040400000), CONST64(0x0000001040400000),
-  CONST64(0x0000000000004000), CONST64(0x0000001000004000), CONST64(0x0000000040004000), CONST64(0x0000001040004000),
-  CONST64(0x0000000000404000), CONST64(0x0000001000404000), CONST64(0x0000000040404000), CONST64(0x0000001040404000),
-  CONST64(0x0000000000000040), CONST64(0x0000001000000040), CONST64(0x0000000040000040), CONST64(0x0000001040000040),
-  CONST64(0x0000000000400040), CONST64(0x0000001000400040), CONST64(0x0000000040400040), CONST64(0x0000001040400040),
-  CONST64(0x0000000000004040), CONST64(0x0000001000004040), CONST64(0x0000000040004040), CONST64(0x0000001040004040),
-  CONST64(0x0000000000404040), CONST64(0x0000001000404040), CONST64(0x0000000040404040), CONST64(0x0000001040404040),
-  CONST64(0x4000000000000000), CONST64(0x4000001000000000), CONST64(0x4000000040000000), CONST64(0x4000001040000000),
-  CONST64(0x4000000000400000), CONST64(0x4000001000400000), CONST64(0x4000000040400000), CONST64(0x4000001040400000),
-  CONST64(0x4000000000004000), CONST64(0x4000001000004000), CONST64(0x4000000040004000), CONST64(0x4000001040004000),
-  CONST64(0x4000000000404000), CONST64(0x4000001000404000), CONST64(0x4000000040404000), CONST64(0x4000001040404000),
-  CONST64(0x4000000000000040), CONST64(0x4000001000000040), CONST64(0x4000000040000040), CONST64(0x4000001040000040),
-  CONST64(0x4000000000400040), CONST64(0x4000001000400040), CONST64(0x4000000040400040), CONST64(0x4000001040400040),
-  CONST64(0x4000000000004040), CONST64(0x4000001000004040), CONST64(0x4000000040004040), CONST64(0x4000001040004040),
-  CONST64(0x4000000000404040), CONST64(0x4000001000404040), CONST64(0x4000000040404040), CONST64(0x4000001040404040),
-  CONST64(0x0040000000000000), CONST64(0x0040001000000000), CONST64(0x0040000040000000), CONST64(0x0040001040000000),
-  CONST64(0x0040000000400000), CONST64(0x0040001000400000), CONST64(0x0040000040400000), CONST64(0x0040001040400000),
-  CONST64(0x0040000000004000), CONST64(0x0040001000004000), CONST64(0x0040000040004000), CONST64(0x0040001040004000),
-  CONST64(0x0040000000404000), CONST64(0x0040001000404000), CONST64(0x0040000040404000), CONST64(0x0040001040404000),
-  CONST64(0x0040000000000040), CONST64(0x0040001000000040), CONST64(0x0040000040000040), CONST64(0x0040001040000040),
-  CONST64(0x0040000000400040), CONST64(0x0040001000400040), CONST64(0x0040000040400040), CONST64(0x0040001040400040),
-  CONST64(0x0040000000004040), CONST64(0x0040001000004040), CONST64(0x0040000040004040), CONST64(0x0040001040004040),
-  CONST64(0x0040000000404040), CONST64(0x0040001000404040), CONST64(0x0040000040404040), CONST64(0x0040001040404040),
-  CONST64(0x4040000000000000), CONST64(0x4040001000000000), CONST64(0x4040000040000000), CONST64(0x4040001040000000),
-  CONST64(0x4040000000400000), CONST64(0x4040001000400000), CONST64(0x4040000040400000), CONST64(0x4040001040400000),
-  CONST64(0x4040000000004000), CONST64(0x4040001000004000), CONST64(0x4040000040004000), CONST64(0x4040001040004000),
-  CONST64(0x4040000000404000), CONST64(0x4040001000404000), CONST64(0x4040000040404000), CONST64(0x4040001040404000),
-  CONST64(0x4040000000000040), CONST64(0x4040001000000040), CONST64(0x4040000040000040), CONST64(0x4040001040000040),
-  CONST64(0x4040000000400040), CONST64(0x4040001000400040), CONST64(0x4040000040400040), CONST64(0x4040001040400040),
-  CONST64(0x4040000000004040), CONST64(0x4040001000004040), CONST64(0x4040000040004040), CONST64(0x4040001040004040),
-  CONST64(0x4040000000404040), CONST64(0x4040001000404040), CONST64(0x4040000040404040), CONST64(0x4040001040404040),
-  CONST64(0x0000400000000000), CONST64(0x0000401000000000), CONST64(0x0000400040000000), CONST64(0x0000401040000000),
-  CONST64(0x0000400000400000), CONST64(0x0000401000400000), CONST64(0x0000400040400000), CONST64(0x0000401040400000),
-  CONST64(0x0000400000004000), CONST64(0x0000401000004000), CONST64(0x0000400040004000), CONST64(0x0000401040004000),
-  CONST64(0x0000400000404000), CONST64(0x0000401000404000), CONST64(0x0000400040404000), CONST64(0x0000401040404000),
-  CONST64(0x0000400000000040), CONST64(0x0000401000000040), CONST64(0x0000400040000040), CONST64(0x0000401040000040),
-  CONST64(0x0000400000400040), CONST64(0x0000401000400040), CONST64(0x0000400040400040), CONST64(0x0000401040400040),
-  CONST64(0x0000400000004040), CONST64(0x0000401000004040), CONST64(0x0000400040004040), CONST64(0x0000401040004040),
-  CONST64(0x0000400000404040), CONST64(0x0000401000404040), CONST64(0x0000400040404040), CONST64(0x0000401040404040),
-  CONST64(0x4000400000000000), CONST64(0x4000401000000000), CONST64(0x4000400040000000), CONST64(0x4000401040000000),
-  CONST64(0x4000400000400000), CONST64(0x4000401000400000), CONST64(0x4000400040400000), CONST64(0x4000401040400000),
-  CONST64(0x4000400000004000), CONST64(0x4000401000004000), CONST64(0x4000400040004000), CONST64(0x4000401040004000),
-  CONST64(0x4000400000404000), CONST64(0x4000401000404000), CONST64(0x4000400040404000), CONST64(0x4000401040404000),
-  CONST64(0x4000400000000040), CONST64(0x4000401000000040), CONST64(0x4000400040000040), CONST64(0x4000401040000040),
-  CONST64(0x4000400000400040), CONST64(0x4000401000400040), CONST64(0x4000400040400040), CONST64(0x4000401040400040),
-  CONST64(0x4000400000004040), CONST64(0x4000401000004040), CONST64(0x4000400040004040), CONST64(0x4000401040004040),
-  CONST64(0x4000400000404040), CONST64(0x4000401000404040), CONST64(0x4000400040404040), CONST64(0x4000401040404040),
-  CONST64(0x0040400000000000), CONST64(0x0040401000000000), CONST64(0x0040400040000000), CONST64(0x0040401040000000),
-  CONST64(0x0040400000400000), CONST64(0x0040401000400000), CONST64(0x0040400040400000), CONST64(0x0040401040400000),
-  CONST64(0x0040400000004000), CONST64(0x0040401000004000), CONST64(0x0040400040004000), CONST64(0x0040401040004000),
-  CONST64(0x0040400000404000), CONST64(0x0040401000404000), CONST64(0x0040400040404000), CONST64(0x0040401040404000),
-  CONST64(0x0040400000000040), CONST64(0x0040401000000040), CONST64(0x0040400040000040), CONST64(0x0040401040000040),
-  CONST64(0x0040400000400040), CONST64(0x0040401000400040), CONST64(0x0040400040400040), CONST64(0x0040401040400040),
-  CONST64(0x0040400000004040), CONST64(0x0040401000004040), CONST64(0x0040400040004040), CONST64(0x0040401040004040),
-  CONST64(0x0040400000404040), CONST64(0x0040401000404040), CONST64(0x0040400040404040), CONST64(0x0040401040404040),
-  CONST64(0x4040400000000000), CONST64(0x4040401000000000), CONST64(0x4040400040000000), CONST64(0x4040401040000000),
-  CONST64(0x4040400000400000), CONST64(0x4040401000400000), CONST64(0x4040400040400000), CONST64(0x4040401040400000),
-  CONST64(0x4040400000004000), CONST64(0x4040401000004000), CONST64(0x4040400040004000), CONST64(0x4040401040004000),
-  CONST64(0x4040400000404000), CONST64(0x4040401000404000), CONST64(0x4040400040404000), CONST64(0x4040401040404000),
-  CONST64(0x4040400000000040), CONST64(0x4040401000000040), CONST64(0x4040400040000040), CONST64(0x4040401040000040),
-  CONST64(0x4040400000400040), CONST64(0x4040401000400040), CONST64(0x4040400040400040), CONST64(0x4040401040400040),
-  CONST64(0x4040400000004040), CONST64(0x4040401000004040), CONST64(0x4040400040004040), CONST64(0x4040401040004040),
-  CONST64(0x4040400000404040), CONST64(0x4040401000404040), CONST64(0x4040400040404040), CONST64(0x4040401040404040)
-  }};
-
-#endif
-
-
-static void cookey(const ulong32 *raw1, ulong32 *keyout);
-
-#ifdef LTC_CLEAN_STACK
-static void _deskey(const unsigned char *key, short edf, ulong32 *keyout)
-#else
-static void deskey(const unsigned char *key, short edf, ulong32 *keyout)
-#endif
-{
-    ulong32 i, j, l, m, n, kn[32];
-    unsigned char pc1m[56], pcr[56];
-
-    for (j=0; j < 56; j++) {
-        l = (ulong32)pc1[j];
-        m = l & 7;
-        pc1m[j] = (unsigned char)((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0);
-    }
-
-    for (i=0; i < 16; i++) {
-        if (edf == DE1) {
-           m = (15 - i) << 1;
-        } else {
-           m = i << 1;
-        }
-        n = m + 1;
-        kn[m] = kn[n] = 0L;
-        for (j=0; j < 28; j++) {
-            l = j + (ulong32)totrot[i];
-            if (l < 28) {
-               pcr[j] = pc1m[l];
-            } else {
-               pcr[j] = pc1m[l - 28];
-            }
-        }
-        for (/*j = 28*/; j < 56; j++) {
-            l = j + (ulong32)totrot[i];
-            if (l < 56) {
-               pcr[j] = pc1m[l];
-            } else {
-               pcr[j] = pc1m[l - 28];
-            }
-        }
-        for (j=0; j < 24; j++)  {
-            if ((int)pcr[(int)pc2[j]] != 0) {
-               kn[m] |= bigbyte[j];
-            }
-            if ((int)pcr[(int)pc2[j+24]] != 0) {
-               kn[n] |= bigbyte[j];
-            }
-        }
-    }
-
-    cookey(kn, keyout);
-}
-
-#ifdef LTC_CLEAN_STACK
-static void deskey(const unsigned char *key, short edf, ulong32 *keyout)
-{
-   _deskey(key, edf, keyout);
-   burn_stack(sizeof(int)*5 + sizeof(ulong32)*32 + sizeof(unsigned char)*112);
-}
-#endif
-
-#ifdef LTC_CLEAN_STACK
-static void _cookey(const ulong32 *raw1, ulong32 *keyout)
-#else
-static void cookey(const ulong32 *raw1, ulong32 *keyout)
-#endif
-{
-    ulong32 *cook;
-    const ulong32 *raw0;
-    ulong32 dough[32];
-    int i;
-
-    cook = dough;
-    for(i=0; i < 16; i++, raw1++)
-    {
-        raw0 = raw1++;
-        *cook    = (*raw0 & 0x00fc0000L) << 6;
-        *cook   |= (*raw0 & 0x00000fc0L) << 10;
-        *cook   |= (*raw1 & 0x00fc0000L) >> 10;
-        *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
-        *cook    = (*raw0 & 0x0003f000L) << 12;
-        *cook   |= (*raw0 & 0x0000003fL) << 16;
-        *cook   |= (*raw1 & 0x0003f000L) >> 4;
-        *cook++ |= (*raw1 & 0x0000003fL);
-    }
-
-    XMEMCPY(keyout, dough, sizeof(dough));
-}
-
-#ifdef LTC_CLEAN_STACK
-static void cookey(const ulong32 *raw1, ulong32 *keyout)
-{
-   _cookey(raw1, keyout);
-   burn_stack(sizeof(ulong32 *) * 2 + sizeof(ulong32)*32 + sizeof(int));
-}
-#endif
-
-#ifndef LTC_CLEAN_STACK
-static void desfunc(ulong32 *block, const ulong32 *keys)
-#else
-static void _desfunc(ulong32 *block, const ulong32 *keys)
-#endif
-{
-    ulong32 work, right, leftt;
-    int cur_round;
-
-    leftt = block[0];
-    right = block[1];
-
-#ifdef LTC_SMALL_CODE
-    work = ((leftt >> 4)  ^ right) & 0x0f0f0f0fL;
-    right ^= work;
-    leftt ^= (work << 4);
-
-    work = ((leftt >> 16) ^ right) & 0x0000ffffL;
-    right ^= work;
-    leftt ^= (work << 16);
-
-    work = ((right >> 2)  ^ leftt) & 0x33333333L;
-    leftt ^= work;
-    right ^= (work << 2);
-
-    work = ((right >> 8)  ^ leftt) & 0x00ff00ffL;
-    leftt ^= work;
-    right ^= (work << 8);
-
-    right = ROLc(right, 1);
-    work = (leftt ^ right) & 0xaaaaaaaaL;
-
-    leftt ^= work;
-    right ^= work;
-    leftt = ROLc(leftt, 1);
-#else
-   {
-      ulong64 tmp;
-      tmp = des_ip[0][byte(leftt, 0)] ^
-            des_ip[1][byte(leftt, 1)] ^
-            des_ip[2][byte(leftt, 2)] ^
-            des_ip[3][byte(leftt, 3)] ^
-            des_ip[4][byte(right, 0)] ^
-            des_ip[5][byte(right, 1)] ^
-            des_ip[6][byte(right, 2)] ^
-            des_ip[7][byte(right, 3)];
-      leftt = (ulong32)(tmp >> 32);
-      right = (ulong32)(tmp & 0xFFFFFFFFUL);
-   }
-#endif
-
-    for (cur_round = 0; cur_round < 8; cur_round++) {
-        work  = RORc(right, 4) ^ *keys++;
-        leftt ^= SP7[work        & 0x3fL]
-              ^  SP5[(work >>  8) & 0x3fL]
-              ^  SP3[(work >> 16) & 0x3fL]
-              ^  SP1[(work >> 24) & 0x3fL];
-        work  = right ^ *keys++;
-        leftt ^= SP8[ work        & 0x3fL]
-              ^  SP6[(work >>  8) & 0x3fL]
-              ^  SP4[(work >> 16) & 0x3fL]
-              ^  SP2[(work >> 24) & 0x3fL];
-
-        work = RORc(leftt, 4) ^ *keys++;
-        right ^= SP7[ work        & 0x3fL]
-              ^  SP5[(work >>  8) & 0x3fL]
-              ^  SP3[(work >> 16) & 0x3fL]
-              ^  SP1[(work >> 24) & 0x3fL];
-        work  = leftt ^ *keys++;
-        right ^= SP8[ work        & 0x3fL]
-              ^  SP6[(work >>  8) & 0x3fL]
-              ^  SP4[(work >> 16) & 0x3fL]
-              ^  SP2[(work >> 24) & 0x3fL];
-    }
-
-#ifdef LTC_SMALL_CODE
-    right = RORc(right, 1);
-    work = (leftt ^ right) & 0xaaaaaaaaL;
-    leftt ^= work;
-    right ^= work;
-    leftt = RORc(leftt, 1);
-    work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
-    right ^= work;
-    leftt ^= (work << 8);
-    /* -- */
-    work = ((leftt >> 2) ^ right) & 0x33333333L;
-    right ^= work;
-    leftt ^= (work << 2);
-    work = ((right >> 16) ^ leftt) & 0x0000ffffL;
-    leftt ^= work;
-    right ^= (work << 16);
-    work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
-    leftt ^= work;
-    right ^= (work << 4);
-#else
-   {
-      ulong64 tmp;
-      tmp = des_fp[0][byte(leftt, 0)] ^
-            des_fp[1][byte(leftt, 1)] ^
-            des_fp[2][byte(leftt, 2)] ^
-            des_fp[3][byte(leftt, 3)] ^
-            des_fp[4][byte(right, 0)] ^
-            des_fp[5][byte(right, 1)] ^
-            des_fp[6][byte(right, 2)] ^
-            des_fp[7][byte(right, 3)];
-      leftt = (ulong32)(tmp >> 32);
-      right = (ulong32)(tmp & 0xFFFFFFFFUL);
-   }
-#endif
-
-    block[0] = right;
-    block[1] = leftt;
-}
-
-#ifdef LTC_CLEAN_STACK
-static void desfunc(ulong32 *block, const ulong32 *keys)
-{
-   _desfunc(block, keys);
-   burn_stack(sizeof(ulong32) * 4 + sizeof(int));
-}
-#endif
-
- /**
-    Initialize the LTC_DES block cipher
-    @param key The symmetric key you wish to pass
-    @param keylen The key length in bytes
-    @param num_rounds The number of rounds desired (0 for default)
-    @param skey The key in as scheduled by this function.
-    @return CRYPT_OK if successful
- */
-int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
-{
-    LTC_ARGCHK(key != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-    if (num_rounds != 0 && num_rounds != 16) {
-        return CRYPT_INVALID_ROUNDS;
-    }
-
-    if (keylen != 8) {
-        return CRYPT_INVALID_KEYSIZE;
-    }
-
-    deskey(key, EN0, skey->des.ek);
-    deskey(key, DE1, skey->des.dk);
-
-    return CRYPT_OK;
-}
-
- /**
-    Initialize the 3LTC_DES-EDE block cipher
-    @param key The symmetric key you wish to pass
-    @param keylen The key length in bytes
-    @param num_rounds The number of rounds desired (0 for default)
-    @param skey The key in as scheduled by this function.
-    @return CRYPT_OK if successful
- */
-int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
-{
-    LTC_ARGCHK(key != NULL);
-    LTC_ARGCHK(skey != NULL);
-
-    if(num_rounds != 0 && num_rounds != 16) {
-        return CRYPT_INVALID_ROUNDS;
-    }
-
-    if (keylen != 24 && keylen != 16) {
-        return CRYPT_INVALID_KEYSIZE;
-    }
-
-    deskey(key,    EN0, skey->des3.ek[0]);
-    deskey(key+8,  DE1, skey->des3.ek[1]);
-    if (keylen == 24) {
-        deskey(key+16, EN0, skey->des3.ek[2]);
-    } else {
-        /* two-key 3DES: K3=K1 */
-        deskey(key, EN0, skey->des3.ek[2]);
-    }
-
-    deskey(key,    DE1, skey->des3.dk[2]);
-    deskey(key+8,  EN0, skey->des3.dk[1]);
-    if (keylen == 24) {
-        deskey(key+16, DE1, skey->des3.dk[0]);
-    } else {
-        /* two-key 3DES: K3=K1 */
-        deskey(key, DE1, skey->des3.dk[0]);
-    }
-
-    return CRYPT_OK;
-}
-
-/**
-  Encrypts a block of text with LTC_DES
-  @param pt The input plaintext (8 bytes)
-  @param ct The output ciphertext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-{
-    ulong32 work[2];
-    LTC_ARGCHK(pt   != NULL);
-    LTC_ARGCHK(ct   != NULL);
-    LTC_ARGCHK(skey != NULL);
-    LOAD32H(work[0], pt+0);
-    LOAD32H(work[1], pt+4);
-    desfunc(work, skey->des.ek);
-    STORE32H(work[0],ct+0);
-    STORE32H(work[1],ct+4);
-    return CRYPT_OK;
-}
-
-/**
-  Decrypts a block of text with LTC_DES
-  @param ct The input ciphertext (8 bytes)
-  @param pt The output plaintext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-{
-    ulong32 work[2];
-    LTC_ARGCHK(pt   != NULL);
-    LTC_ARGCHK(ct   != NULL);
-    LTC_ARGCHK(skey != NULL);
-    LOAD32H(work[0], ct+0);
-    LOAD32H(work[1], ct+4);
-    desfunc(work, skey->des.dk);
-    STORE32H(work[0],pt+0);
-    STORE32H(work[1],pt+4);
-    return CRYPT_OK;
-}
-
-/**
-  Encrypts a block of text with 3LTC_DES-EDE
-  @param pt The input plaintext (8 bytes)
-  @param ct The output ciphertext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
-{
-    ulong32 work[2];
-
-    LTC_ARGCHK(pt   != NULL);
-    LTC_ARGCHK(ct   != NULL);
-    LTC_ARGCHK(skey != NULL);
-    LOAD32H(work[0], pt+0);
-    LOAD32H(work[1], pt+4);
-    desfunc(work, skey->des3.ek[0]);
-    desfunc(work, skey->des3.ek[1]);
-    desfunc(work, skey->des3.ek[2]);
-    STORE32H(work[0],ct+0);
-    STORE32H(work[1],ct+4);
-    return CRYPT_OK;
-}
-
-/**
-  Decrypts a block of text with 3LTC_DES-EDE
-  @param ct The input ciphertext (8 bytes)
-  @param pt The output plaintext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
-{
-    ulong32 work[2];
-    LTC_ARGCHK(pt   != NULL);
-    LTC_ARGCHK(ct   != NULL);
-    LTC_ARGCHK(skey != NULL);
-    LOAD32H(work[0], ct+0);
-    LOAD32H(work[1], ct+4);
-    desfunc(work, skey->des3.dk[0]);
-    desfunc(work, skey->des3.dk[1]);
-    desfunc(work, skey->des3.dk[2]);
-    STORE32H(work[0],pt+0);
-    STORE32H(work[1],pt+4);
-    return CRYPT_OK;
-}
-
-/**
-  Performs a self-test of the LTC_DES block cipher
-  @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
-*/
-int des_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-    int err;
-    static const struct des_test_case {
-        int num, mode; /* mode 1 = encrypt */
-        unsigned char key[8], txt[8], out[8];
-    } cases[] = {
-        { 1, 1,     { 0x10, 0x31, 0x6E, 0x02, 0x8C, 0x8F, 0x3B, 0x4A },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x82, 0xDC, 0xBA, 0xFB, 0xDE, 0xAB, 0x66, 0x02 } },
-        { 2, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00 },
-                    { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 3, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0xDD, 0x7F, 0x12, 0x1C, 0xA5, 0x01, 0x56, 0x19 },
-                    { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 4, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x2E, 0x86, 0x53, 0x10, 0x4F, 0x38, 0x34, 0xEA },
-                    { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 5, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x4B, 0xD3, 0x88, 0xFF, 0x6C, 0xD8, 0x1D, 0x4F },
-                    { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 6, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x20, 0xB9, 0xE7, 0x67, 0xB2, 0xFB, 0x14, 0x56 },
-                    { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 7, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x55, 0x57, 0x93, 0x80, 0xD7, 0x71, 0x38, 0xEF },
-                    { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 8, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x6C, 0xC5, 0xDE, 0xFA, 0xAF, 0x04, 0x51, 0x2F },
-                    { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 9, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x0D, 0x9F, 0x27, 0x9B, 0xA5, 0xD8, 0x72, 0x60 },
-                    { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        {10, 1,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0xD9, 0x03, 0x1B, 0x02, 0x71, 0xBD, 0x5A, 0x0A },
-                    { 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-
-        { 1, 0,     { 0x10, 0x31, 0x6E, 0x02, 0x8C, 0x8F, 0x3B, 0x4A },
-                    { 0x82, 0xDC, 0xBA, 0xFB, 0xDE, 0xAB, 0x66, 0x02 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-        { 2, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00 } },
-        { 3, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xDD, 0x7F, 0x12, 0x1C, 0xA5, 0x01, 0x56, 0x19 } },
-        { 4, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x2E, 0x86, 0x53, 0x10, 0x4F, 0x38, 0x34, 0xEA } },
-        { 5, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x4B, 0xD3, 0x88, 0xFF, 0x6C, 0xD8, 0x1D, 0x4F } },
-        { 6, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x20, 0xB9, 0xE7, 0x67, 0xB2, 0xFB, 0x14, 0x56 } },
-        { 7, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x55, 0x57, 0x93, 0x80, 0xD7, 0x71, 0x38, 0xEF } },
-        { 8, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x6C, 0xC5, 0xDE, 0xFA, 0xAF, 0x04, 0x51, 0x2F } },
-        { 9, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x0D, 0x9F, 0x27, 0x9B, 0xA5, 0xD8, 0x72, 0x60 } },
-        {10, 0,     { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xD9, 0x03, 0x1B, 0x02, 0x71, 0xBD, 0x5A, 0x0A } },
-
-#ifdef LTC_TEST_EXT
-        { 0+11, 0,  { 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x95, 0xA8, 0xD7, 0x28, 0x13, 0xDA, 0xA9, 0x4D } },
-        { 1+11, 0,  { 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x0E, 0xEC, 0x14, 0x87, 0xDD, 0x8C, 0x26, 0xD5 } },
-        { 2+11, 0,  { 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x7A, 0xD1, 0x6F, 0xFB, 0x79, 0xC4, 0x59, 0x26 } },
-        { 3+11, 0,  { 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xD3, 0x74, 0x62, 0x94, 0xCA, 0x6A, 0x6C, 0xF3 } },
-        { 4+11, 0,  { 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x80, 0x9F, 0x5F, 0x87, 0x3C, 0x1F, 0xD7, 0x61 } },
-        { 5+11, 0,  { 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xC0, 0x2F, 0xAF, 0xFE, 0xC9, 0x89, 0xD1, 0xFC } },
-        { 6+11, 0,  { 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x46, 0x15, 0xAA, 0x1D, 0x33, 0xE7, 0x2F, 0x10 } },
-        { 7+11, 0,  { 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x20, 0x55, 0x12, 0x33, 0x50, 0xC0, 0x08, 0x58 } },
-        { 8+11, 0,  { 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xDF, 0x3B, 0x99, 0xD6, 0x57, 0x73, 0x97, 0xC8 } },
-        { 9+11, 0,  { 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x31, 0xFE, 0x17, 0x36, 0x9B, 0x52, 0x88, 0xC9 } },
-        {10+11, 0,  { 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xDF, 0xDD, 0x3C, 0xC6, 0x4D, 0xAE, 0x16, 0x42 } },
-        {11+11, 0,  { 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x17, 0x8C, 0x83, 0xCE, 0x2B, 0x39, 0x9D, 0x94 } },
-        {12+11, 0,  { 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x50, 0xF6, 0x36, 0x32, 0x4A, 0x9B, 0x7F, 0x80 } },
-        {13+11, 0,  { 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xA8, 0x46, 0x8E, 0xE3, 0xBC, 0x18, 0xF0, 0x6D } },
-        {14+11, 0,  { 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xA2, 0xDC, 0x9E, 0x92, 0xFD, 0x3C, 0xDE, 0x92 } },
-        {15+11, 0,  { 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xCA, 0xC0, 0x9F, 0x79, 0x7D, 0x03, 0x12, 0x87 } },
-        {16+11, 0,  { 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x90, 0xBA, 0x68, 0x0B, 0x22, 0xAE, 0xB5, 0x25 } },
-        {17+11, 0,  { 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xCE, 0x7A, 0x24, 0xF3, 0x50, 0xE2, 0x80, 0xB6 } },
-        {18+11, 0,  { 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x88, 0x2B, 0xFF, 0x0A, 0xA0, 0x1A, 0x0B, 0x87 } },
-        {19+11, 0,  { 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x25, 0x61, 0x02, 0x88, 0x92, 0x45, 0x11, 0xC2 } },
-        {20+11, 0,  { 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xC7, 0x15, 0x16, 0xC2, 0x9C, 0x75, 0xD1, 0x70 } },
-        {21+11, 0,  { 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x51, 0x99, 0xC2, 0x9A, 0x52, 0xC9, 0xF0, 0x59 } },
-        {22+11, 0,  { 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xC2, 0x2F, 0x0A, 0x29, 0x4A, 0x71, 0xF2, 0x9F } },
-        {23+11, 0,  { 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xEE, 0x37, 0x14, 0x83, 0x71, 0x4C, 0x02, 0xEA } },
-        {24+11, 0,  { 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xA8, 0x1F, 0xBD, 0x44, 0x8F, 0x9E, 0x52, 0x2F } },
-        {25+11, 0,  { 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x4F, 0x64, 0x4C, 0x92, 0xE1, 0x92, 0xDF, 0xED } },
-        {26+11, 0,  { 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x1A, 0xFA, 0x9A, 0x66, 0xA6, 0xDF, 0x92, 0xAE } },
-        {27+11, 0,  { 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xB3, 0xC1, 0xCC, 0x71, 0x5C, 0xB8, 0x79, 0xD8 } },
-        {28+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x19, 0xD0, 0x32, 0xE6, 0x4A, 0xB0, 0xBD, 0x8B } },
-        {29+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x3C, 0xFA, 0xA7, 0xA7, 0xDC, 0x87, 0x20, 0xDC } },
-        {30+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xB7, 0x26, 0x5F, 0x7F, 0x44, 0x7A, 0xC6, 0xF3 } },
-        {31+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x9D, 0xB7, 0x3B, 0x3C, 0x0D, 0x16, 0x3F, 0x54 } },
-        {32+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x81, 0x81, 0xB6, 0x5B, 0xAB, 0xF4, 0xA9, 0x75 } },
-        {33+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x93, 0xC9, 0xB6, 0x40, 0x42, 0xEA, 0xA2, 0x40 } },
-        {34+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x55, 0x70, 0x53, 0x08, 0x29, 0x70, 0x55, 0x92 } },
-        {35+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x86, 0x38, 0x80, 0x9E, 0x87, 0x87, 0x87, 0xA0 } },
-        {36+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x41, 0xB9, 0xA7, 0x9A, 0xF7, 0x9A, 0xC2, 0x08 } },
-        {37+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x7A, 0x9B, 0xE4, 0x2F, 0x20, 0x09, 0xA8, 0x92 } },
-        {38+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x29, 0x03, 0x8D, 0x56, 0xBA, 0x6D, 0x27, 0x45 } },
-        {39+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x54, 0x95, 0xC6, 0xAB, 0xF1, 0xE5, 0xDF, 0x51 } },
-        {40+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xAE, 0x13, 0xDB, 0xD5, 0x61, 0x48, 0x89, 0x33 } },
-        {41+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x02, 0x4D, 0x1F, 0xFA, 0x89, 0x04, 0xE3, 0x89 } },
-        {42+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xD1, 0x39, 0x97, 0x12, 0xF9, 0x9B, 0xF0, 0x2E } },
-        {43+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x14, 0xC1, 0xD7, 0xC1, 0xCF, 0xFE, 0xC7, 0x9E } },
-        {44+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x1D, 0xE5, 0x27, 0x9D, 0xAE, 0x3B, 0xED, 0x6F } },
-        {45+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xE9, 0x41, 0xA3, 0x3F, 0x85, 0x50, 0x13, 0x03 } },
-        {46+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xDA, 0x99, 0xDB, 0xBC, 0x9A, 0x03, 0xF3, 0x79 } },
-        {47+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xB7, 0xFC, 0x92, 0xF9, 0x1D, 0x8E, 0x92, 0xE9 } },
-        {48+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xAE, 0x8E, 0x5C, 0xAA, 0x3C, 0xA0, 0x4E, 0x85 } },
-        {49+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x9C, 0xC6, 0x2D, 0xF4, 0x3B, 0x6E, 0xED, 0x74 } },
-        {50+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xD8, 0x63, 0xDB, 0xB5, 0xC5, 0x9A, 0x91, 0xA0 } },
-        {51+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xA1, 0xAB, 0x21, 0x90, 0x54, 0x5B, 0x91, 0xD7 } },
-        {52+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x08, 0x75, 0x04, 0x1E, 0x64, 0xC5, 0x70, 0xF7 } },
-        {53+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x5A, 0x59, 0x45, 0x28, 0xBE, 0xBE, 0xF1, 0xCC } },
-        {54+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0xFC, 0xDB, 0x32, 0x91, 0xDE, 0x21, 0xF0, 0xC0 } },
-        {55+11, 0,  { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 },
-                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-                    { 0x86, 0x9E, 0xFD, 0x7F, 0x9F, 0x26, 0x5A, 0x09 } },
-#endif /* LTC_TEST_EXT */
-
-        /*** more test cases you could add if you are not convinced (the above test cases aren't really too good):
-
-                key              plaintext        ciphertext
-                0000000000000000 0000000000000000 8CA64DE9C1B123A7
-                FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 7359B2163E4EDC58
-                3000000000000000 1000000000000001 958E6E627A05557B
-                1111111111111111 1111111111111111 F40379AB9E0EC533
-                0123456789ABCDEF 1111111111111111 17668DFC7292532D
-                1111111111111111 0123456789ABCDEF 8A5AE1F81AB8F2DD
-                0000000000000000 0000000000000000 8CA64DE9C1B123A7
-                FEDCBA9876543210 0123456789ABCDEF ED39D950FA74BCC4
-                7CA110454A1A6E57 01A1D6D039776742 690F5B0D9A26939B
-                0131D9619DC1376E 5CD54CA83DEF57DA 7A389D10354BD271
-                07A1133E4A0B2686 0248D43806F67172 868EBB51CAB4599A
-                3849674C2602319E 51454B582DDF440A 7178876E01F19B2A
-                04B915BA43FEB5B6 42FD443059577FA2 AF37FB421F8C4095
-                0113B970FD34F2CE 059B5E0851CF143A 86A560F10EC6D85B
-                0170F175468FB5E6 0756D8E0774761D2 0CD3DA020021DC09
-                43297FAD38E373FE 762514B829BF486A EA676B2CB7DB2B7A
-                07A7137045DA2A16 3BDD119049372802 DFD64A815CAF1A0F
-                04689104C2FD3B2F 26955F6835AF609A 5C513C9C4886C088
-                37D06BB516CB7546 164D5E404F275232 0A2AEEAE3FF4AB77
-                1F08260D1AC2465E 6B056E18759F5CCA EF1BF03E5DFA575A
-                584023641ABA6176 004BD6EF09176062 88BF0DB6D70DEE56
-                025816164629B007 480D39006EE762F2 A1F9915541020B56
-                49793EBC79B3258F 437540C8698F3CFA 6FBF1CAFCFFD0556
-                4FB05E1515AB73A7 072D43A077075292 2F22E49BAB7CA1AC
-                49E95D6D4CA229BF 02FE55778117F12A 5A6B612CC26CCE4A
-                018310DC409B26D6 1D9D5C5018F728C2 5F4C038ED12B2E41
-                1C587F1C13924FEF 305532286D6F295A 63FAC0D034D9F793
-                0101010101010101 0123456789ABCDEF 617B3A0CE8F07100
-                1F1F1F1F0E0E0E0E 0123456789ABCDEF DB958605F8C8C606
-                E0FEE0FEF1FEF1FE 0123456789ABCDEF EDBFD1C66C29CCC7
-                0000000000000000 FFFFFFFFFFFFFFFF 355550B2150E2451
-                FFFFFFFFFFFFFFFF 0000000000000000 CAAAAF4DEAF1DBAE
-                0123456789ABCDEF 0000000000000000 D5D44FF720683D0D
-                FEDCBA9876543210 FFFFFFFFFFFFFFFF 2A2BB008DF97C2F2
-
-            http://www.ecs.soton.ac.uk/~prw99r/ez438/vectors.txt
-        ***/
-    };
-    int i, y;
-    unsigned char tmp[8];
-    symmetric_key des;
-
-    for(i=0; i < (int)(sizeof(cases)/sizeof(cases[0])); i++)
-    {
-        if ((err = des_setup(cases[i].key, 8, 0, &des)) != CRYPT_OK) {
-           return err;
-        }
-        if (cases[i].mode != 0) {
-           des_ecb_encrypt(cases[i].txt, tmp, &des);
-        } else {
-           des_ecb_decrypt(cases[i].txt, tmp, &des);
-        }
-
-        if (compare_testvector(cases[i].out, sizeof(tmp), tmp, sizeof(tmp), "DES", i) != 0) {
-           return CRYPT_FAIL_TESTVECTOR;
-        }
-
-        /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */
-        for (y = 0; y < 8; y++) tmp[y] = 0;
-        for (y = 0; y < 1000; y++) des_ecb_encrypt(tmp, tmp, &des);
-        for (y = 0; y < 1000; y++) des_ecb_decrypt(tmp, tmp, &des);
-        for (y = 0; y < 8; y++) if (tmp[y] != 0) return CRYPT_FAIL_TESTVECTOR;
-    }
-
-    return CRYPT_OK;
-  #endif
-}
-
-int des3_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-   unsigned char key[24], pt[8], ct[8], tmp[8];
-   symmetric_key skey;
-   int x, err;
-
-   if ((err = des_test()) != CRYPT_OK) {
-      return err;
-   }
-
-   for (x = 0; x < 8; x++) {
-       pt[x] = x;
-   }
-
-   for (x = 0; x < 24; x++) {
-       key[x] = x;
-   }
-
-   if ((err = des3_setup(key, 24, 0, &skey)) != CRYPT_OK) {
-      return err;
-   }
-
-   des3_ecb_encrypt(pt, ct, &skey);
-   des3_ecb_decrypt(ct, tmp, &skey);
-
-   if (compare_testvector(pt, 8, tmp, 8, "3DES", 0) != 0) {
-      return CRYPT_FAIL_TESTVECTOR;
-   }
-
-   return CRYPT_OK;
- #endif
-}
-
-/** Terminate the context
-   @param skey    The scheduled key
-*/
-void des_done(symmetric_key *skey)
-{
-  LTC_UNUSED_PARAM(skey);
-}
-
-/** Terminate the context
-   @param skey    The scheduled key
-*/
-void des3_done(symmetric_key *skey)
-{
-  LTC_UNUSED_PARAM(skey);
-}
-
-
-/**
-  Gets suitable key size
-  @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.
-  @return CRYPT_OK if the input key size is acceptable.
-*/
-int des_keysize(int *keysize)
-{
-    LTC_ARGCHK(keysize != NULL);
-    if(*keysize < 8) {
-        return CRYPT_INVALID_KEYSIZE;
-    }
-    *keysize = 8;
-    return CRYPT_OK;
-}
-
-/**
-  Gets suitable key size
-  @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.
-  @return CRYPT_OK if the input key size is acceptable.
-*/
-int des3_keysize(int *keysize)
-{
-    LTC_ARGCHK(keysize != NULL);
-    if (*keysize < 16)
-       return CRYPT_INVALID_KEYSIZE;
-    if (*keysize < 24) {
-       *keysize = 16;
-       return CRYPT_OK;
-    }
-    *keysize = 24;
-    return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/ciphers/rc2.c b/libs/tomcrypt/src/ciphers/rc2.c
deleted file mode 100644
index 0c24c3237bce..000000000000
--- a/libs/tomcrypt/src/ciphers/rc2.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**********************************************************************\
-* To commemorate the 1996 RSA Data Security Conference, the following  *
-* code is released into the public domain by its author.  Prost!       *
-*                                                                      *
-* This cipher uses 16-bit words and little-endian byte ordering.       *
-* I wonder which processor it was optimized for?                       *
-*                                                                      *
-* Thanks to CodeView, SoftIce, and D86 for helping bring this code to  *
-* the public.                                                          *
-\**********************************************************************/
-#include "tomcrypt.h"
-
-/**
-  @file rc2.c
-  Implementation of RC2 with fixed effective key length of 64bits
-*/
-
-#ifdef LTC_RC2
-
-const struct ltc_cipher_descriptor rc2_desc = {
-   "rc2",
-   12, 8, 128, 8, 16,
-   &rc2_setup,
-   &rc2_ecb_encrypt,
-   &rc2_ecb_decrypt,
-   &rc2_test,
-   &rc2_done,
-   &rc2_keysize,
-   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-/* 256-entry permutation table, probably derived somehow from pi */
-static const unsigned char permute[256] = {
-        217,120,249,196, 25,221,181,237, 40,233,253,121, 74,160,216,157,
-        198,126, 55,131, 43,118, 83,142, 98, 76,100,136, 68,139,251,162,
-         23,154, 89,245,135,179, 79, 19, 97, 69,109,141,  9,129,125, 50,
-        189,143, 64,235,134,183,123, 11,240,149, 33, 34, 92,107, 78,130,
-         84,214,101,147,206, 96,178, 28,115, 86,192, 20,167,140,241,220,
-         18,117,202, 31, 59,190,228,209, 66, 61,212, 48,163, 60,182, 38,
-        111,191, 14,218, 70,105,  7, 87, 39,242, 29,155,188,148, 67,  3,
-        248, 17,199,246,144,239, 62,231,  6,195,213, 47,200,102, 30,215,
-          8,232,234,222,128, 82,238,247,132,170,114,172, 53, 77,106, 42,
-        150, 26,210,113, 90, 21, 73,116, 75,159,208, 94,  4, 24,164,236,
-        194,224, 65,110, 15, 81,203,204, 36,145,175, 80,161,244,112, 57,
-        153,124, 58,133, 35,184,180,122,252,  2, 54, 91, 37, 85,151, 49,
-         45, 93,250,152,227,138,146,174,  5,223, 41, 16,103,108,186,201,
-        211,  0,230,207,225,158,168, 44, 99, 22,  1, 63, 88,226,137,169,
-         13, 56, 52, 27,171, 51,255,176,187, 72, 12, 95,185,177,205, 46,
-        197,243,219, 71,229,165,156,119, 10,166, 32,104,254,127,193,173
-};
-
- /**
-    Initialize the RC2 block cipher
-    @param key The symmetric key you wish to pass
-    @param keylen The key length in bytes
-    @param bits The effective key length in bits
-    @param num_rounds The number of rounds desired (0 for default)
-    @param skey The key in as scheduled by this function.
-    @return CRYPT_OK if successful
- */
-int rc2_setup_ex(const unsigned char *key, int keylen, int bits, int num_rounds, symmetric_key *skey)
-{
-   unsigned *xkey = skey->rc2.xkey;
-   unsigned char tmp[128];
-   unsigned T8, TM;
-   int i;
-
-   LTC_ARGCHK(key  != NULL);
-   LTC_ARGCHK(skey != NULL);
-
-   if (keylen == 0 || keylen > 128 || bits > 1024) {
-      return CRYPT_INVALID_KEYSIZE;
-   }
-   if (bits == 0) {
-      bits = 1024;
-   }
-
-   if (num_rounds != 0 && num_rounds != 16) {
-      return CRYPT_INVALID_ROUNDS;
-   }
-
-   for (i = 0; i < keylen; i++) {
-      tmp[i] = key[i] & 255;
-   }
-
-   /* Phase 1: Expand input key to 128 bytes */
-   if (keylen < 128) {
-      for (i = keylen; i < 128; i++) {
-         tmp[i] = permute[(tmp[i - 1] + tmp[i - keylen]) & 255];
-      }
-   }
-
-   /* Phase 2 - reduce effective key size to "bits" */
-   T8   = (unsigned)(bits+7)>>3;
-   TM   = (255 >> (unsigned)(7 & -bits));
-   tmp[128 - T8] = permute[tmp[128 - T8] & TM];
-   for (i = 127 - T8; i >= 0; i--) {
-      tmp[i] = permute[tmp[i + 1] ^ tmp[i + T8]];
-   }
-
-   /* Phase 3 - copy to xkey in little-endian order */
-   for (i = 0; i < 64; i++) {
-      xkey[i] =  (unsigned)tmp[2*i] + ((unsigned)tmp[2*i+1] << 8);
-   }
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(tmp, sizeof(tmp));
-#endif
-
-   return CRYPT_OK;
-}
-
-/**
-   Initialize the RC2 block cipher
-
-     The effective key length is here always keylen * 8
-
-   @param key The symmetric key you wish to pass
-   @param keylen The key length in bytes
-   @param num_rounds The number of rounds desired (0 for default)
-   @param skey The key in as scheduled by this function.
-   @return CRYPT_OK if successful
-*/
-int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
-{
-   return rc2_setup_ex(key, keylen, keylen * 8, num_rounds, skey);
-}
-
-/**********************************************************************\
-* Encrypt an 8-byte block of plaintext using the given key.            *
-\**********************************************************************/
-/**
-  Encrypts a block of text with RC2
-  @param pt The input plaintext (8 bytes)
-  @param ct The output ciphertext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-#ifdef LTC_CLEAN_STACK
-static int _rc2_ecb_encrypt( const unsigned char *pt,
-                            unsigned char *ct,
-                            symmetric_key *skey)
-#else
-int rc2_ecb_encrypt( const unsigned char *pt,
-                            unsigned char *ct,
-                            symmetric_key *skey)
-#endif
-{
-    unsigned *xkey;
-    unsigned x76, x54, x32, x10, i;
-
-    LTC_ARGCHK(pt  != NULL);
-    LTC_ARGCHK(ct != NULL);
-    LTC_ARGCHK(skey   != NULL);
-
-    xkey = skey->rc2.xkey;
-
-    x76 = ((unsigned)pt[7] << 8) + (unsigned)pt[6];
-    x54 = ((unsigned)pt[5] << 8) + (unsigned)pt[4];
-    x32 = ((unsigned)pt[3] << 8) + (unsigned)pt[2];
-    x10 = ((unsigned)pt[1] << 8) + (unsigned)pt[0];
-
-    for (i = 0; i < 16; i++) {
-        x10 = (x10 + (x32 & ~x76) + (x54 & x76) + xkey[4*i+0]) & 0xFFFF;
-        x10 = ((x10 << 1) | (x10 >> 15));
-
-        x32 = (x32 + (x54 & ~x10) + (x76 & x10) + xkey[4*i+1]) & 0xFFFF;
-        x32 = ((x32 << 2) | (x32 >> 14));
-
-        x54 = (x54 + (x76 & ~x32) + (x10 & x32) + xkey[4*i+2]) & 0xFFFF;
-        x54 = ((x54 << 3) | (x54 >> 13));
-
-        x76 = (x76 + (x10 & ~x54) + (x32 & x54) + xkey[4*i+3]) & 0xFFFF;
-        x76 = ((x76 << 5) | (x76 >> 11));
-
-        if (i == 4 || i == 10) {
-            x10 = (x10 + xkey[x76 & 63]) & 0xFFFF;
-            x32 = (x32 + xkey[x10 & 63]) & 0xFFFF;
-            x54 = (x54 + xkey[x32 & 63]) & 0xFFFF;
-            x76 = (x76 + xkey[x54 & 63]) & 0xFFFF;
-        }
-    }
-
-    ct[0] = (unsigned char)x10;
-    ct[1] = (unsigned char)(x10 >> 8);
-    ct[2] = (unsigned char)x32;
-    ct[3] = (unsigned char)(x32 >> 8);
-    ct[4] = (unsigned char)x54;
-    ct[5] = (unsigned char)(x54 >> 8);
-    ct[6] = (unsigned char)x76;
-    ct[7] = (unsigned char)(x76 >> 8);
-
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-int rc2_ecb_encrypt( const unsigned char *pt,
-                            unsigned char *ct,
-                            symmetric_key *skey)
-{
-    int err = _rc2_ecb_encrypt(pt, ct, skey);
-    burn_stack(sizeof(unsigned *) + sizeof(unsigned) * 5);
-    return err;
-}
-#endif
-
-/**********************************************************************\
-* Decrypt an 8-byte block of ciphertext using the given key.           *
-\**********************************************************************/
-/**
-  Decrypts a block of text with RC2
-  @param ct The input ciphertext (8 bytes)
-  @param pt The output plaintext (8 bytes)
-  @param skey The key as scheduled
-  @return CRYPT_OK if successful
-*/
-#ifdef LTC_CLEAN_STACK
-static int _rc2_ecb_decrypt( const unsigned char *ct,
-                            unsigned char *pt,
-                            symmetric_key *skey)
-#else
-int rc2_ecb_decrypt( const unsigned char *ct,
-                            unsigned char *pt,
-                            symmetric_key *skey)
-#endif
-{
-    unsigned x76, x54, x32, x10;
-    unsigned *xkey;
-    int i;
-
-    LTC_ARGCHK(pt  != NULL);
-    LTC_ARGCHK(ct != NULL);
-    LTC_ARGCHK(skey   != NULL);
-
-    xkey = skey->rc2.xkey;
-
-    x76 = ((unsigned)ct[7] << 8) + (unsigned)ct[6];
-    x54 = ((unsigned)ct[5] << 8) + (unsigned)ct[4];
-    x32 = ((unsigned)ct[3] << 8) + (unsigned)ct[2];
-    x10 = ((unsigned)ct[1] << 8) + (unsigned)ct[0];
-
-    for (i = 15; i >= 0; i--) {
-        if (i == 4 || i == 10) {
-            x76 = (x76 - xkey[x54 & 63]) & 0xFFFF;
-            x54 = (x54 - xkey[x32 & 63]) & 0xFFFF;
-            x32 = (x32 - xkey[x10 & 63]) & 0xFFFF;
-            x10 = (x10 - xkey[x76 & 63]) & 0xFFFF;
-        }
-
-        x76 = ((x76 << 11) | (x76 >> 5));
-        x76 = (x76 - ((x10 & ~x54) + (x32 & x54) + xkey[4*i+3])) & 0xFFFF;
-
-        x54 = ((x54 << 13) | (x54 >> 3));
-        x54 = (x54 - ((x76 & ~x32) + (x10 & x32) + xkey[4*i+2])) & 0xFFFF;
-
-        x32 = ((x32 << 14) | (x32 >> 2));
-        x32 = (x32 - ((x54 & ~x10) + (x76 & x10) + xkey[4*i+1])) & 0xFFFF;
-
-        x10 = ((x10 << 15) | (x10 >> 1));
-        x10 = (x10 - ((x32 & ~x76) + (x54 & x76) + xkey[4*i+0])) & 0xFFFF;
-    }
-
-    pt[0] = (unsigned char)x10;
-    pt[1] = (unsigned char)(x10 >> 8);
-    pt[2] = (unsigned char)x32;
-    pt[3] = (unsigned char)(x32 >> 8);
-    pt[4] = (unsigned char)x54;
-    pt[5] = (unsigned char)(x54 >> 8);
-    pt[6] = (unsigned char)x76;
-    pt[7] = (unsigned char)(x76 >> 8);
-
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-int rc2_ecb_decrypt( const unsigned char *ct,
-                            unsigned char *pt,
-                            symmetric_key *skey)
-{
-    int err = _rc2_ecb_decrypt(ct, pt, skey);
-    burn_stack(sizeof(unsigned *) + sizeof(unsigned) * 4 + sizeof(int));
-    return err;
-}
-#endif
-
-/**
-  Performs a self-test of the RC2 block cipher
-  @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
-*/
-int rc2_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-   static const struct {
-        int keylen, bits;
-        unsigned char key[16], pt[8], ct[8];
-   } tests[] = {
-
-   { 8, 63,
-     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0xeb, 0xb7, 0x73, 0xf9, 0x93, 0x27, 0x8e, 0xff }
-   },
-   { 8, 64,
-     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
-     { 0x27, 0x8b, 0x27, 0xe4, 0x2e, 0x2f, 0x0d, 0x49 }
-   },
-   { 8, 64,
-     { 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
-     { 0x30, 0x64, 0x9e, 0xdf, 0x9b, 0xe7, 0xd2, 0xc2 }
-   },
-   { 1, 64,
-     { 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x61, 0xa8, 0xa2, 0x44, 0xad, 0xac, 0xcc, 0xf0 }
-   },
-   { 7, 64,
-     { 0x88, 0xbc, 0xa9, 0x0e, 0x90, 0x87, 0x5a, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x6c, 0xcf, 0x43, 0x08, 0x97, 0x4c, 0x26, 0x7f }
-   },
-   { 16, 64,
-     { 0x88, 0xbc, 0xa9, 0x0e, 0x90, 0x87, 0x5a, 0x7f,
-       0x0f, 0x79, 0xc3, 0x84, 0x62, 0x7b, 0xaf, 0xb2 },
-     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x1a, 0x80, 0x7d, 0x27, 0x2b, 0xbe, 0x5d, 0xb1 }
-   },
-   { 16, 128,
-     { 0x88, 0xbc, 0xa9, 0x0e, 0x90, 0x87, 0x5a, 0x7f,
-       0x0f, 0x79, 0xc3, 0x84, 0x62, 0x7b, 0xaf, 0xb2 },
-     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
-     { 0x22, 0x69, 0x55, 0x2a, 0xb0, 0xf8, 0x5c, 0xa6 }
-   }
-  };
-    int x, y, err;
-    symmetric_key skey;
-    unsigned char tmp[2][8];
-
-    for (x = 0; x < (int)(sizeof(tests) / sizeof(tests[0])); x++) {
-        zeromem(tmp, sizeof(tmp));
-        if (tests[x].bits == (tests[x].keylen * 8)) {
-           if ((err = rc2_setup(tests[x].key, tests[x].keylen, 0, &skey)) != CRYPT_OK) {
-              return err;
-           }
-        }
-        else {
-           if ((err = rc2_setup_ex(tests[x].key, tests[x].keylen, tests[x].bits, 0, &skey)) != CRYPT_OK) {
-              return err;
-           }
-        }
-
-        rc2_ecb_encrypt(tests[x].pt, tmp[0], &skey);
-        rc2_ecb_decrypt(tmp[0], tmp[1], &skey);
-
-        if (compare_testvector(tmp[0], 8, tests[x].ct, 8, "RC2 CT", x) ||
-              compare_testvector(tmp[1], 8, tests[x].pt, 8, "RC2 PT", x)) {
-           return CRYPT_FAIL_TESTVECTOR;
-        }
-
-      /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */
-      for (y = 0; y < 8; y++) tmp[0][y] = 0;
-      for (y = 0; y < 1000; y++) rc2_ecb_encrypt(tmp[0], tmp[0], &skey);
-      for (y = 0; y < 1000; y++) rc2_ecb_decrypt(tmp[0], tmp[0], &skey);
-      for (y = 0; y < 8; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR;
-    }
-    return CRYPT_OK;
-   #endif
-}
-
-/** Terminate the context
-   @param skey    The scheduled key
-*/
-void rc2_done(symmetric_key *skey)
-{
-  LTC_UNUSED_PARAM(skey);
-}
-
-/**
-  Gets suitable key size
-  @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.
-  @return CRYPT_OK if the input key size is acceptable.
-*/
-int rc2_keysize(int *keysize)
-{
-   LTC_ARGCHK(keysize != NULL);
-   if (*keysize < 1) {
-       return CRYPT_INVALID_KEYSIZE;
-   } else if (*keysize > 128) {
-       *keysize = 128;
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_add_aad.c b/libs/tomcrypt/src/encauth/ccm/ccm_add_aad.c
deleted file mode 100644
index a547c58bed30..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_add_aad.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_CCM_MODE
-
-/**
-  Add AAD to the CCM state
-  @param ccm       The CCM state
-  @param adata     The additional authentication data to add to the CCM state
-  @param adatalen  The length of the AAD data.
-  @return CRYPT_OK on success
- */
-int ccm_add_aad(ccm_state *ccm,
-                const unsigned char *adata,  unsigned long adatalen)
-{
-   unsigned long y;
-   int            err;
-
-   LTC_ARGCHK(ccm   != NULL);
-   LTC_ARGCHK(adata != NULL);
-
-   if (ccm->aadlen < ccm->current_aadlen + adatalen) {
-      return CRYPT_INVALID_ARG;
-   }
-   ccm->current_aadlen += adatalen;
-
-   /* now add the data */
-   for (y = 0; y < adatalen; y++) {
-      if (ccm->x == 16) {
-         /* full block so let's encrypt it */
-         if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->PAD, ccm->PAD, &ccm->K)) != CRYPT_OK) {
-            return err;
-         }
-         ccm->x = 0;
-      }
-      ccm->PAD[ccm->x++] ^= adata[y];
-   }
-
-   /* remainder? */
-   if (ccm->aadlen == ccm->current_aadlen) {
-      if (ccm->x != 0) {
-         if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->PAD, ccm->PAD, &ccm->K)) != CRYPT_OK) {
-            return err;
-         }
-      }
-      ccm->x = 0;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_add_nonce.c b/libs/tomcrypt/src/encauth/ccm/ccm_add_nonce.c
deleted file mode 100644
index 5c11bbb9b994..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_add_nonce.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_CCM_MODE
-
-/**
-  Add nonce data to the CCM state
-  @param ccm       The CCM state
-  @param nonce     The nonce data to add
-  @param noncelen  The length of the nonce
-  @return CRYPT_OK on success
- */
-int ccm_add_nonce(ccm_state *ccm,
-                  const unsigned char *nonce,     unsigned long noncelen)
-{
-   unsigned long x, y, len;
-   int           err;
-
-   LTC_ARGCHK(ccm   != NULL);
-   LTC_ARGCHK(nonce != NULL);
-
-   /* increase L to match the nonce len */
-   ccm->noncelen = (noncelen > 13) ? 13 : noncelen;
-   if ((15 - ccm->noncelen) > ccm->L) {
-      ccm->L = 15 - ccm->noncelen;
-   }
-
-   /* decrease noncelen to match L */
-   if ((ccm->noncelen + ccm->L) > 15) {
-      ccm->noncelen = 15 - ccm->L;
-   }
-
-   /* form B_0 == flags | Nonce N | l(m) */
-   x = 0;
-   ccm->PAD[x++] = (unsigned char)(((ccm->aadlen > 0) ? (1<<6) : 0) |
-                   (((ccm->taglen - 2)>>1)<<3)        |
-                   (ccm->L-1));
-
-   /* nonce */
-   for (y = 0; y < (16 - (ccm->L + 1)); y++) {
-      ccm->PAD[x++] = nonce[y];
-   }
-
-   /* store len */
-   len = ccm->ptlen;
-
-   /* shift len so the upper bytes of len are the contents of the length */
-   for (y = ccm->L; y < 4; y++) {
-      len <<= 8;
-   }
-
-   /* store l(m) (only store 32-bits) */
-   for (y = 0; ccm->L > 4 && (ccm->L-y)>4; y++) {
-      ccm->PAD[x++] = 0;
-   }
-   for (; y < ccm->L; y++) {
-      ccm->PAD[x++] = (unsigned char)((len >> 24) & 255);
-      len <<= 8;
-   }
-
-   /* encrypt PAD */
-   if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->PAD, ccm->PAD, &ccm->K)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* handle header */
-   ccm->x = 0;
-   if (ccm->aadlen > 0) {
-      /* store length */
-      if (ccm->aadlen < ((1UL<<16) - (1UL<<8))) {
-         ccm->PAD[ccm->x++] ^= (ccm->aadlen>>8) & 255;
-         ccm->PAD[ccm->x++] ^= ccm->aadlen & 255;
-      } else {
-         ccm->PAD[ccm->x++] ^= 0xFF;
-         ccm->PAD[ccm->x++] ^= 0xFE;
-         ccm->PAD[ccm->x++] ^= (ccm->aadlen>>24) & 255;
-         ccm->PAD[ccm->x++] ^= (ccm->aadlen>>16) & 255;
-         ccm->PAD[ccm->x++] ^= (ccm->aadlen>>8) & 255;
-         ccm->PAD[ccm->x++] ^= ccm->aadlen & 255;
-      }
-   }
-
-   /* setup the ctr counter */
-   x = 0;
-
-   /* flags */
-   ccm->ctr[x++] = (unsigned char)ccm->L-1;
-
-   /* nonce */
-   for (y = 0; y < (16 - (ccm->L+1)); ++y) {
-      ccm->ctr[x++] = nonce[y];
-   }
-   /* offset */
-   while (x < 16) {
-      ccm->ctr[x++] = 0;
-   }
-
-   ccm->CTRlen = 16;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_done.c b/libs/tomcrypt/src/encauth/ccm/ccm_done.c
deleted file mode 100644
index bd7fea31c51a..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_done.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_CCM_MODE
-
-/**
-  Terminate a CCM stream
-  @param ccm     The CCM state
-  @param tag     [out] The destination for the MAC tag
-  @param taglen  [in/out]  The length of the MAC tag
-  @return CRYPT_OK on success
- */
-int ccm_done(ccm_state *ccm,
-             unsigned char *tag,    unsigned long *taglen)
-{
-   unsigned long x, y;
-   int            err;
-
-   LTC_ARGCHK(ccm != NULL);
-
-   /* Check all data have been processed */
-   if (ccm->ptlen != ccm->current_ptlen) {
-      return CRYPT_ERROR;
-   }
-
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-
-   if (ccm->x != 0) {
-      if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->PAD, ccm->PAD, &ccm->K)) != CRYPT_OK) {
-         return err;
-      }
-   }
-
-   /* setup CTR for the TAG (zero the count) */
-   for (y = 15; y > 15 - ccm->L; y--) {
-      ccm->ctr[y] = 0x00;
-   }
-   if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->ctr, ccm->CTRPAD, &ccm->K)) != CRYPT_OK) {
-      return err;
-   }
-
-   cipher_descriptor[ccm->cipher].done(&ccm->K);
-
-   /* store the TAG */
-   for (x = 0; x < 16 && x < *taglen; x++) {
-      tag[x] = ccm->PAD[x] ^ ccm->CTRPAD[x];
-   }
-   *taglen = x;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_init.c b/libs/tomcrypt/src/encauth/ccm/ccm_init.c
deleted file mode 100644
index 4d4aaca3233d..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_init.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_CCM_MODE
-
-/**
-  Initialize a CCM state
-  @param ccm     The CCM state to initialize
-  @param cipher  The index of the cipher to use
-  @param key     The secret key
-  @param keylen  The length of the secret key
-  @param ptlen   The length of the plain/cipher text that will be processed
-  @param taglen  The max length of the MAC tag
-  @param aadlen  The length of the AAD
-
-  @return CRYPT_OK on success
- */
-int ccm_init(ccm_state *ccm, int cipher,
-             const unsigned char *key, int keylen, int ptlen, int taglen, int aadlen)
-{
-   int            err;
-
-   LTC_ARGCHK(ccm    != NULL);
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(taglen != 0);
-
-   XMEMSET(ccm, 0, sizeof(ccm_state));
-
-   /* check cipher input */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (cipher_descriptor[cipher].block_length != 16) {
-      return CRYPT_INVALID_CIPHER;
-   }
-
-   /* make sure the taglen is even and <= 16 */
-   ccm->taglen = taglen;
-   ccm->taglen &= ~1;
-   if (ccm->taglen > 16) {
-      ccm->taglen = 16;
-   }
-
-   /* can't use < 4 */
-   if (ccm->taglen < 4) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* schedule key */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &ccm->K)) != CRYPT_OK) {
-      return err;
-   }
-   ccm->cipher = cipher;
-
-   /* let's get the L value */
-   ccm->ptlen = ptlen;
-   ccm->L   = 0;
-   while (ptlen) {
-      ++ccm->L;
-      ptlen >>= 8;
-   }
-   if (ccm->L <= 1) {
-      ccm->L = 2;
-   }
-
-   ccm->aadlen = aadlen;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_memory.c b/libs/tomcrypt/src/encauth/ccm/ccm_memory.c
deleted file mode 100644
index 22de2b1f8740..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_memory.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ccm_memory.c
-  CCM support, process a block of memory, Tom St Denis
-*/
-
-#ifdef LTC_CCM_MODE
-
-/**
-   CCM encrypt/decrypt and produce an authentication tag
-
-     *1 'pt', 'ct' and 'tag' can both be 'in' or 'out', depending on 'direction'
-
-   @param cipher     The index of the cipher desired
-   @param key        The secret key to use
-   @param keylen     The length of the secret key (octets)
-   @param uskey      A previously scheduled key [optional can be NULL]
-   @param nonce      The session nonce [use once]
-   @param noncelen   The length of the nonce
-   @param header     The header for the session
-   @param headerlen  The length of the header (octets)
-   @param pt         [*1] The plaintext
-   @param ptlen      The length of the plaintext (octets)
-   @param ct         [*1] The ciphertext
-   @param tag        [*1] The destination tag
-   @param taglen     The max size and resulting size of the authentication tag
-   @param direction  Encrypt or Decrypt direction (0 or 1)
-   @return CRYPT_OK if successful
-*/
-int ccm_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    symmetric_key       *uskey,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *header, unsigned long headerlen,
-          unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen,
-                    int  direction)
-{
-   unsigned char  PAD[16], ctr[16], CTRPAD[16], ptTag[16], b, *pt_real;
-   unsigned char *pt_work = NULL;
-   symmetric_key *skey;
-   int            err;
-   unsigned long  len, L, x, y, z, CTRlen;
-#ifdef LTC_FAST
-   LTC_FAST_TYPE fastMask = ~(LTC_FAST_TYPE)0; /* initialize fastMask at all zeroes */
-#endif
-   unsigned char mask = 0xff; /* initialize mask at all zeroes */
-
-   if (uskey == NULL) {
-      LTC_ARGCHK(key    != NULL);
-   }
-   LTC_ARGCHK(nonce  != NULL);
-   if (headerlen > 0) {
-      LTC_ARGCHK(header != NULL);
-   }
-   LTC_ARGCHK(pt     != NULL);
-   LTC_ARGCHK(ct     != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-
-   pt_real = pt;
-
-#ifdef LTC_FAST
-   if (16 % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   /* check cipher input */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (cipher_descriptor[cipher].block_length != 16) {
-      return CRYPT_INVALID_CIPHER;
-   }
-
-   /* make sure the taglen is even and <= 16 */
-   *taglen &= ~1;
-   if (*taglen > 16) {
-      *taglen = 16;
-   }
-
-   /* can't use < 4 */
-   if (*taglen < 4) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* is there an accelerator? */
-   if (cipher_descriptor[cipher].accel_ccm_memory != NULL) {
-       return cipher_descriptor[cipher].accel_ccm_memory(
-           key,    keylen,
-           uskey,
-           nonce,  noncelen,
-           header, headerlen,
-           pt,     ptlen,
-           ct,
-           tag,    taglen,
-           direction);
-   }
-
-   /* let's get the L value */
-   len = ptlen;
-   L   = 0;
-   while (len) {
-      ++L;
-      len >>= 8;
-   }
-   if (L <= 1) {
-      L = 2;
-   }
-
-   /* increase L to match the nonce len */
-   noncelen = (noncelen > 13) ? 13 : noncelen;
-   if ((15 - noncelen) > L) {
-      L = 15 - noncelen;
-   }
-
-   /* allocate mem for the symmetric key */
-   if (uskey == NULL) {
-      skey = XMALLOC(sizeof(*skey));
-      if (skey == NULL) {
-         return CRYPT_MEM;
-      }
-
-      /* initialize the cipher */
-      if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, skey)) != CRYPT_OK) {
-         XFREE(skey);
-         return err;
-      }
-   } else {
-      skey = uskey;
-   }
-
-   /* initialize buffer for pt */
-   if (direction == CCM_DECRYPT && ptlen > 0) {
-      pt_work = XMALLOC(ptlen);
-      if (pt_work == NULL) {
-         goto error;
-      }
-      pt = pt_work;
-   }
-
-   /* form B_0 == flags | Nonce N | l(m) */
-   x = 0;
-   PAD[x++] = (unsigned char)(((headerlen > 0) ? (1<<6) : 0) |
-            (((*taglen - 2)>>1)<<3)        |
-            (L-1));
-
-   /* nonce */
-   for (y = 0; y < (16 - (L + 1)); y++) {
-       PAD[x++] = nonce[y];
-   }
-
-   /* store len */
-   len = ptlen;
-
-   /* shift len so the upper bytes of len are the contents of the length */
-   for (y = L; y < 4; y++) {
-       len <<= 8;
-   }
-
-   /* store l(m) (only store 32-bits) */
-   for (y = 0; L > 4 && (L-y)>4; y++) {
-       PAD[x++] = 0;
-   }
-   for (; y < L; y++) {
-       PAD[x++] = (unsigned char)((len >> 24) & 255);
-       len <<= 8;
-   }
-
-   /* encrypt PAD */
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-       goto error;
-   }
-
-   /* handle header */
-   if (headerlen > 0) {
-      x = 0;
-
-      /* store length */
-      if (headerlen < ((1UL<<16) - (1UL<<8))) {
-         PAD[x++] ^= (headerlen>>8) & 255;
-         PAD[x++] ^= headerlen & 255;
-      } else {
-         PAD[x++] ^= 0xFF;
-         PAD[x++] ^= 0xFE;
-         PAD[x++] ^= (headerlen>>24) & 255;
-         PAD[x++] ^= (headerlen>>16) & 255;
-         PAD[x++] ^= (headerlen>>8) & 255;
-         PAD[x++] ^= headerlen & 255;
-      }
-
-      /* now add the data */
-      for (y = 0; y < headerlen; y++) {
-          if (x == 16) {
-             /* full block so let's encrypt it */
-             if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-                goto error;
-             }
-             x = 0;
-          }
-          PAD[x++] ^= header[y];
-      }
-
-      /* remainder */
-      if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-         goto error;
-      }
-   }
-
-   /* setup the ctr counter */
-   x = 0;
-
-   /* flags */
-   ctr[x++] = (unsigned char)L-1;
-
-   /* nonce */
-   for (y = 0; y < (16 - (L+1)); ++y) {
-      ctr[x++] = nonce[y];
-   }
-   /* offset */
-   while (x < 16) {
-      ctr[x++] = 0;
-   }
-
-   x      = 0;
-   CTRlen = 16;
-
-   /* now handle the PT */
-   if (ptlen > 0) {
-      y = 0;
-#ifdef LTC_FAST
-      if (ptlen & ~15)  {
-          if (direction == CCM_ENCRYPT) {
-             for (; y < (ptlen & ~15); y += 16) {
-                /* increment the ctr? */
-                for (z = 15; z > 15-L; z--) {
-                    ctr[z] = (ctr[z] + 1) & 255;
-                    if (ctr[z]) break;
-                }
-                if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
-                   goto error;
-                }
-
-                /* xor the PT against the pad first */
-                for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) {
-                    *(LTC_FAST_TYPE_PTR_CAST(&PAD[z]))  ^= *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z]));
-                    *(LTC_FAST_TYPE_PTR_CAST(&ct[y+z])) = *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z])) ^ *(LTC_FAST_TYPE_PTR_CAST(&CTRPAD[z]));
-                }
-                if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-                   goto error;
-                }
-             }
-          } else { /* direction == CCM_DECRYPT */
-             for (; y < (ptlen & ~15); y += 16) {
-                /* increment the ctr? */
-                for (z = 15; z > 15-L; z--) {
-                    ctr[z] = (ctr[z] + 1) & 255;
-                    if (ctr[z]) break;
-                }
-                if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
-                   goto error;
-                }
-
-                /* xor the PT against the pad last */
-                for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) {
-                    *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z])) = *(LTC_FAST_TYPE_PTR_CAST(&ct[y+z])) ^ *(LTC_FAST_TYPE_PTR_CAST(&CTRPAD[z]));
-                    *(LTC_FAST_TYPE_PTR_CAST(&PAD[z]))  ^= *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z]));
-                }
-                if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-                   goto error;
-                }
-             }
-          }
-      }
-#endif
-
-      for (; y < ptlen; y++) {
-          /* increment the ctr? */
-          if (CTRlen == 16) {
-             for (z = 15; z > 15-L; z--) {
-                 ctr[z] = (ctr[z] + 1) & 255;
-                 if (ctr[z]) break;
-             }
-             if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
-                goto error;
-             }
-             CTRlen = 0;
-          }
-
-          /* if we encrypt we add the bytes to the MAC first */
-          if (direction == CCM_ENCRYPT) {
-             b     = pt[y];
-             ct[y] = b ^ CTRPAD[CTRlen++];
-          } else {
-             b     = ct[y] ^ CTRPAD[CTRlen++];
-             pt[y] = b;
-          }
-
-          if (x == 16) {
-             if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-                goto error;
-             }
-             x = 0;
-          }
-          PAD[x++] ^= b;
-      }
-
-      if (x != 0) {
-         if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) {
-            goto error;
-         }
-      }
-   }
-
-   /* setup CTR for the TAG (zero the count) */
-   for (y = 15; y > 15 - L; y--) {
-      ctr[y] = 0x00;
-   }
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) {
-      goto error;
-   }
-
-   if (skey != uskey) {
-      cipher_descriptor[cipher].done(skey);
-#ifdef LTC_CLEAN_STACK
-      zeromem(skey,   sizeof(*skey));
-#endif
-   }
-
-   if (direction == CCM_ENCRYPT) {
-      /* store the TAG */
-      for (x = 0; x < 16 && x < *taglen; x++) {
-          tag[x] = PAD[x] ^ CTRPAD[x];
-      }
-      *taglen = x;
-   } else { /* direction == CCM_DECRYPT */
-      /* decrypt the tag */
-      for (x = 0; x < 16 && x < *taglen; x++) {
-         ptTag[x] = tag[x] ^ CTRPAD[x];
-      }
-      *taglen = x;
-
-      /* check validity of the decrypted tag against the computed PAD (in constant time) */
-      /* HACK: the boolean value of XMEM_NEQ becomes either 0 (CRYPT_OK) or 1 (CRYPT_ERR).
-       *       there should be a better way of setting the correct error code in constant
-       *       time.
-       */
-      err = XMEM_NEQ(ptTag, PAD, *taglen);
-
-      /* Zero the plaintext if the tag was invalid (in constant time) */
-      if (ptlen > 0) {
-         y = 0;
-         mask *= 1 - err; /* mask = ( err ? 0 : 0xff ) */
-#ifdef LTC_FAST
-         fastMask *= 1 - err;
-         if (ptlen & ~15) {
-            for (; y < (ptlen & ~15); y += 16) {
-              for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) {
-                *(LTC_FAST_TYPE_PTR_CAST(&pt_real[y+z])) = *(LTC_FAST_TYPE_PTR_CAST(&pt[y+z])) & fastMask;
-              }
-            }
-         }
-#endif
-         for (; y < ptlen; y++) {
-            pt_real[y] = pt[y] & mask;
-         }
-      }
-   }
-
-#ifdef LTC_CLEAN_STACK
-#ifdef LTC_FAST
-   fastMask = 0;
-#endif
-   mask = 0;
-   zeromem(PAD,    sizeof(PAD));
-   zeromem(CTRPAD, sizeof(CTRPAD));
-   if (pt_work != NULL) {
-     zeromem(pt_work, ptlen);
-   }
-#endif
-error:
-   if (pt_work) {
-      XFREE(pt_work);
-   }
-   if (skey != uskey) {
-      XFREE(skey);
-   }
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_process.c b/libs/tomcrypt/src/encauth/ccm/ccm_process.c
deleted file mode 100644
index 6fa4303cbf03..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_process.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_CCM_MODE
-
-/**
-  Process plaintext/ciphertext through CCM
-  @param ccm       The CCM state
-  @param pt        The plaintext
-  @param ptlen     The plaintext length (ciphertext length is the same)
-  @param ct        The ciphertext
-  @param direction Encrypt or Decrypt mode (CCM_ENCRYPT or CCM_DECRYPT)
-  @return CRYPT_OK on success
- */
-int ccm_process(ccm_state *ccm,
-                unsigned char *pt,     unsigned long ptlen,
-                unsigned char *ct,
-                int direction)
-{
-   unsigned char z, b;
-   unsigned long y;
-   int err;
-
-   LTC_ARGCHK(ccm != NULL);
-
-   /* Check aad has been correctly added */
-   if (ccm->aadlen != ccm->current_aadlen) {
-      return CRYPT_ERROR;
-   }
-
-   /* Check we do not process too much data */
-   if (ccm->ptlen < ccm->current_ptlen + ptlen) {
-      return CRYPT_ERROR;
-   }
-   ccm->current_ptlen += ptlen;
-
-   /* now handle the PT */
-   if (ptlen > 0) {
-      LTC_ARGCHK(pt != NULL);
-      LTC_ARGCHK(ct != NULL);
-
-      for (y = 0; y < ptlen; y++) {
-         /* increment the ctr? */
-         if (ccm->CTRlen == 16) {
-            for (z = 15; z > 15-ccm->L; z--) {
-               ccm->ctr[z] = (ccm->ctr[z] + 1) & 255;
-               if (ccm->ctr[z]) break;
-            }
-            if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->ctr, ccm->CTRPAD, &ccm->K)) != CRYPT_OK) {
-               return err;
-            }
-            ccm->CTRlen = 0;
-         }
-
-         /* if we encrypt we add the bytes to the MAC first */
-         if (direction == CCM_ENCRYPT) {
-            b     = pt[y];
-            ct[y] = b ^ ccm->CTRPAD[ccm->CTRlen++];
-         } else {
-            b     = ct[y] ^ ccm->CTRPAD[ccm->CTRlen++];
-            pt[y] = b;
-         }
-
-         if (ccm->x == 16) {
-            if ((err = cipher_descriptor[ccm->cipher].ecb_encrypt(ccm->PAD, ccm->PAD, &ccm->K)) != CRYPT_OK) {
-               return err;
-            }
-            ccm->x = 0;
-         }
-         ccm->PAD[ccm->x++] ^= b;
-      }
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ccm/ccm_reset.c b/libs/tomcrypt/src/encauth/ccm/ccm_reset.c
deleted file mode 100644
index 076abc518b4b..000000000000
--- a/libs/tomcrypt/src/encauth/ccm/ccm_reset.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_CCM_MODE
-
-/**
-  Reset a CCM state to as if you just called ccm_init().  This saves the initialization time.
-  @param ccm   The CCM state to reset
-  @return CRYPT_OK on success
-*/
-int ccm_reset(ccm_state *ccm)
-{
-   LTC_ARGCHK(ccm != NULL);
-   zeromem(ccm->PAD, sizeof(ccm->PAD));
-   zeromem(ccm->ctr, sizeof(ccm->ctr));
-   zeromem(ccm->CTRPAD, sizeof(ccm->CTRPAD));
-   ccm->CTRlen = 0;
-   ccm->current_ptlen = 0;
-   ccm->current_aadlen = 0;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_addheader.c b/libs/tomcrypt/src/encauth/eax/eax_addheader.c
deleted file mode 100644
index bb6e60a99813..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_addheader.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**
-    @file eax_addheader.c
-    EAX implementation, add meta-data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-    add header (metadata) to the stream
-    @param eax    The current EAX state
-    @param header The header (meta-data) data you wish to add to the state
-    @param length The length of the header data
-    @return CRYPT_OK if successful
-*/
-int eax_addheader(eax_state *eax, const unsigned char *header,
-                  unsigned long length)
-{
-   LTC_ARGCHK(eax    != NULL);
-   LTC_ARGCHK(header != NULL);
-   return omac_process(&eax->headeromac, header, length);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_decrypt.c b/libs/tomcrypt/src/encauth/eax/eax_decrypt.c
deleted file mode 100644
index c5f27319ea5d..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_decrypt.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-    @file eax_decrypt.c
-    EAX implementation, decrypt block, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-   Decrypt data with the EAX protocol
-   @param eax     The EAX state
-   @param ct      The ciphertext
-   @param pt      [out] The plaintext
-   @param length  The length (octets) of the ciphertext
-   @return CRYPT_OK if successful
-*/
-int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt,
-                unsigned long length)
-{
-   int err;
-
-   LTC_ARGCHK(eax != NULL);
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-
-   /* omac ciphertext */
-   if ((err = omac_process(&eax->ctomac, ct, length)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* decrypt  */
-   return ctr_decrypt(ct, pt, length, &eax->ctr);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_decrypt_verify_memory.c b/libs/tomcrypt/src/encauth/eax/eax_decrypt_verify_memory.c
deleted file mode 100644
index a3e29294783c..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_decrypt_verify_memory.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-    @file eax_decrypt_verify_memory.c
-    EAX implementation, decrypt block of memory, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-   Decrypt a block of memory and verify the provided MAC tag with EAX
-   @param cipher     The index of the cipher desired
-   @param key        The secret key
-   @param keylen     The length of the key (octets)
-   @param nonce      The nonce data (use once) for the session
-   @param noncelen   The length of the nonce data.
-   @param header     The session header data
-   @param headerlen  The length of the header (octets)
-   @param ct         The ciphertext
-   @param ctlen      The length of the ciphertext (octets)
-   @param pt         [out] The plaintext
-   @param tag        The authentication tag provided by the encoder
-   @param taglen     [in/out] The length of the tag (octets)
-   @param stat       [out] The result of the decryption (1==valid tag, 0==invalid)
-   @return CRYPT_OK if successful regardless of the resulting tag comparison
-*/
-int eax_decrypt_verify_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *header, unsigned long headerlen,
-    const unsigned char *ct,     unsigned long ctlen,
-          unsigned char *pt,
-          unsigned char *tag,    unsigned long taglen,
-          int           *stat)
-{
-   int            err;
-   eax_state     *eax;
-   unsigned char *buf;
-   unsigned long  buflen;
-
-   LTC_ARGCHK(stat != NULL);
-   LTC_ARGCHK(key  != NULL);
-   LTC_ARGCHK(pt   != NULL);
-   LTC_ARGCHK(ct   != NULL);
-   LTC_ARGCHK(tag  != NULL);
-
-   /* default to zero */
-   *stat = 0;
-
-   /* limit taglen */
-   taglen = MIN(taglen, MAXBLOCKSIZE);
-
-   /* allocate ram */
-   buf = XMALLOC(taglen);
-   eax = XMALLOC(sizeof(*eax));
-   if (eax == NULL || buf == NULL) {
-      if (eax != NULL) {
-         XFREE(eax);
-      }
-      if (buf != NULL) {
-         XFREE(buf);
-      }
-      return CRYPT_MEM;
-   }
-
-   if ((err = eax_init(eax, cipher, key, keylen, nonce, noncelen, header, headerlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   if ((err = eax_decrypt(eax, ct, pt, ctlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   buflen = taglen;
-   if ((err = eax_done(eax, buf, &buflen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* compare tags */
-   if (buflen >= taglen && XMEM_NEQ(buf, tag, taglen) == 0) {
-      *stat = 1;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, taglen);
-   zeromem(eax, sizeof(*eax));
-#endif
-
-   XFREE(eax);
-   XFREE(buf);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_done.c b/libs/tomcrypt/src/encauth/eax/eax_done.c
deleted file mode 100644
index d59d6f42cd87..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_done.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file eax_done.c
-   EAX implementation, terminate session, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-   Terminate an EAX session and get the tag.
-   @param eax       The EAX state
-   @param tag       [out] The destination of the authentication tag
-   @param taglen    [in/out] The max length and resulting length of the authentication tag
-   @return CRYPT_OK if successful
-*/
-int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen)
-{
-   int           err;
-   unsigned char *headermac, *ctmac;
-   unsigned long x, len;
-
-   LTC_ARGCHK(eax    != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-
-   /* allocate ram */
-   headermac = XMALLOC(MAXBLOCKSIZE);
-   ctmac     = XMALLOC(MAXBLOCKSIZE);
-
-   if (headermac == NULL || ctmac == NULL) {
-      if (headermac != NULL) {
-         XFREE(headermac);
-      }
-      if (ctmac != NULL) {
-         XFREE(ctmac);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* finish ctomac */
-   len = MAXBLOCKSIZE;
-   if ((err = omac_done(&eax->ctomac, ctmac, &len)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* finish headeromac */
-
-   /* note we specifically don't reset len so the two lens are minimal */
-
-   if ((err = omac_done(&eax->headeromac, headermac, &len)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* terminate the CTR chain */
-   if ((err = ctr_done(&eax->ctr)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* compute N xor H xor C */
-   for (x = 0; x < len && x < *taglen; x++) {
-       tag[x] = eax->N[x] ^ headermac[x] ^ ctmac[x];
-   }
-   *taglen = x;
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(ctmac,     MAXBLOCKSIZE);
-   zeromem(headermac, MAXBLOCKSIZE);
-   zeromem(eax,       sizeof(*eax));
-#endif
-
-   XFREE(ctmac);
-   XFREE(headermac);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_encrypt.c b/libs/tomcrypt/src/encauth/eax/eax_encrypt.c
deleted file mode 100644
index be65d032a661..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_encrypt.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file eax_encrypt.c
-   EAX implementation, encrypt block by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-   Encrypt with EAX a block of data.
-   @param eax        The EAX state
-   @param pt         The plaintext to encrypt
-   @param ct         [out] The ciphertext as encrypted
-   @param length     The length of the plaintext (octets)
-   @return CRYPT_OK if successful
-*/
-int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct,
-                unsigned long length)
-{
-   int err;
-
-   LTC_ARGCHK(eax != NULL);
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-
-   /* encrypt */
-   if ((err = ctr_encrypt(pt, ct, length, &eax->ctr)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* omac ciphertext */
-   return omac_process(&eax->ctomac, ct, length);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_encrypt_authenticate_memory.c b/libs/tomcrypt/src/encauth/eax/eax_encrypt_authenticate_memory.c
deleted file mode 100644
index ca9983c20a29..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_encrypt_authenticate_memory.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-  @file eax_encrypt_authenticate_memory.c
-  EAX implementation, encrypt a block of memory, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-   EAX encrypt and produce an authentication tag
-   @param cipher     The index of the cipher desired
-   @param key        The secret key to use
-   @param keylen     The length of the secret key (octets)
-   @param nonce      The session nonce [use once]
-   @param noncelen   The length of the nonce
-   @param header     The header for the session
-   @param headerlen  The length of the header (octets)
-   @param pt         The plaintext
-   @param ptlen      The length of the plaintext (octets)
-   @param ct         [out] The ciphertext
-   @param tag        [out] The destination tag
-   @param taglen     [in/out] The max size and resulting size of the authentication tag
-   @return CRYPT_OK if successful
-*/
-int eax_encrypt_authenticate_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *header, unsigned long headerlen,
-    const unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen)
-{
-   int err;
-   eax_state *eax;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(pt     != NULL);
-   LTC_ARGCHK(ct     != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-
-   eax = XMALLOC(sizeof(*eax));
-
-   if ((err = eax_init(eax, cipher, key, keylen, nonce, noncelen, header, headerlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   if ((err = eax_encrypt(eax, pt, ct, ptlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   if ((err = eax_done(eax, tag, taglen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(eax, sizeof(*eax));
-#endif
-
-   XFREE(eax);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/eax/eax_init.c b/libs/tomcrypt/src/encauth/eax/eax_init.c
deleted file mode 100644
index ec4544314ec4..000000000000
--- a/libs/tomcrypt/src/encauth/eax/eax_init.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file eax_init.c
-   EAX implementation, initialized EAX state, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_EAX_MODE
-
-/**
-   Initialized an EAX state
-   @param eax       [out] The EAX state to initialize
-   @param cipher    The index of the desired cipher
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param nonce     The use-once nonce for the session
-   @param noncelen  The length of the nonce (octets)
-   @param header    The header for the EAX state
-   @param headerlen The header length (octets)
-   @return CRYPT_OK if successful
-*/
-int eax_init(eax_state *eax, int cipher,
-             const unsigned char *key,    unsigned long keylen,
-             const unsigned char *nonce,  unsigned long noncelen,
-             const unsigned char *header, unsigned long headerlen)
-{
-   unsigned char *buf;
-   int           err, blklen;
-   omac_state    *omac;
-   unsigned long len;
-
-
-   LTC_ARGCHK(eax   != NULL);
-   LTC_ARGCHK(key   != NULL);
-   LTC_ARGCHK(nonce != NULL);
-   if (headerlen > 0) {
-      LTC_ARGCHK(header != NULL);
-   }
-
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   blklen = cipher_descriptor[cipher].block_length;
-
-   /* allocate ram */
-   buf  = XMALLOC(MAXBLOCKSIZE);
-   omac = XMALLOC(sizeof(*omac));
-
-   if (buf == NULL || omac == NULL) {
-      if (buf != NULL) {
-         XFREE(buf);
-      }
-      if (omac != NULL) {
-         XFREE(omac);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* N = LTC_OMAC_0K(nonce) */
-   zeromem(buf, MAXBLOCKSIZE);
-   if ((err = omac_init(omac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* omac the [0]_n */
-   if ((err = omac_process(omac, buf, blklen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   /* omac the nonce */
-   if ((err = omac_process(omac, nonce, noncelen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   /* store result */
-   len = sizeof(eax->N);
-   if ((err = omac_done(omac, eax->N, &len)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* H = LTC_OMAC_1K(header) */
-   zeromem(buf, MAXBLOCKSIZE);
-   buf[blklen - 1] = 1;
-
-   if ((err = omac_init(&eax->headeromac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* omac the [1]_n */
-   if ((err = omac_process(&eax->headeromac, buf, blklen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   /* omac the header */
-   if (headerlen != 0) {
-      if ((err = omac_process(&eax->headeromac, header, headerlen)) != CRYPT_OK) {
-          goto LBL_ERR;
-      }
-   }
-
-   /* note we don't finish the headeromac, this allows us to add more header later */
-
-   /* setup the CTR mode */
-   if ((err = ctr_start(cipher, eax->N, key, keylen, 0, CTR_COUNTER_BIG_ENDIAN, &eax->ctr)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* setup the LTC_OMAC for the ciphertext */
-   if ((err = omac_init(&eax->ctomac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* omac [2]_n */
-   zeromem(buf, MAXBLOCKSIZE);
-   buf[blklen-1] = 2;
-   if ((err = omac_process(&eax->ctomac, buf, blklen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf,  MAXBLOCKSIZE);
-   zeromem(omac, sizeof(*omac));
-#endif
-
-   XFREE(omac);
-   XFREE(buf);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_add_aad.c b/libs/tomcrypt/src/encauth/gcm/gcm_add_aad.c
deleted file mode 100644
index a606b672c599..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_add_aad.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_add_aad.c
-   GCM implementation, Add AAD data to the stream, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Add AAD to the GCM state
-  @param gcm       The GCM state
-  @param adata     The additional authentication data to add to the GCM state
-  @param adatalen  The length of the AAD data.
-  @return CRYPT_OK on success
- */
-int gcm_add_aad(gcm_state *gcm,
-               const unsigned char *adata,  unsigned long adatalen)
-{
-   unsigned long x;
-   int           err;
-#ifdef LTC_FAST
-   unsigned long y;
-#endif
-
-   LTC_ARGCHK(gcm    != NULL);
-   if (adatalen > 0) {
-      LTC_ARGCHK(adata  != NULL);
-   }
-
-   if (gcm->buflen > 16 || gcm->buflen < 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* in IV mode? */
-   if (gcm->mode == LTC_GCM_MODE_IV) {
-      /* IV length must be > 0 */
-      if (gcm->buflen == 0 && gcm->totlen == 0) return CRYPT_ERROR;
-      /* let's process the IV */
-      if (gcm->ivmode || gcm->buflen != 12) {
-         for (x = 0; x < (unsigned long)gcm->buflen; x++) {
-             gcm->X[x] ^= gcm->buf[x];
-         }
-         if (gcm->buflen) {
-            gcm->totlen += gcm->buflen * CONST64(8);
-            gcm_mult_h(gcm, gcm->X);
-         }
-
-         /* mix in the length */
-         zeromem(gcm->buf, 8);
-         STORE64H(gcm->totlen, gcm->buf+8);
-         for (x = 0; x < 16; x++) {
-             gcm->X[x] ^= gcm->buf[x];
-         }
-         gcm_mult_h(gcm, gcm->X);
-
-         /* copy counter out */
-         XMEMCPY(gcm->Y, gcm->X, 16);
-         zeromem(gcm->X, 16);
-      } else {
-         XMEMCPY(gcm->Y, gcm->buf, 12);
-         gcm->Y[12] = 0;
-         gcm->Y[13] = 0;
-         gcm->Y[14] = 0;
-         gcm->Y[15] = 1;
-      }
-      XMEMCPY(gcm->Y_0, gcm->Y, 16);
-      zeromem(gcm->buf, 16);
-      gcm->buflen = 0;
-      gcm->totlen = 0;
-      gcm->mode   = LTC_GCM_MODE_AAD;
-   }
-
-   if (gcm->mode != LTC_GCM_MODE_AAD || gcm->buflen >= 16) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   x = 0;
-#ifdef LTC_FAST
-   if (gcm->buflen == 0) {
-      for (x = 0; x < (adatalen & ~15); x += 16) {
-          for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-              *(LTC_FAST_TYPE_PTR_CAST(&gcm->X[y])) ^= *(LTC_FAST_TYPE_PTR_CAST(&adata[x + y]));
-          }
-          gcm_mult_h(gcm, gcm->X);
-          gcm->totlen += 128;
-      }
-      adata += x;
-   }
-#endif
-
-
-   /* start adding AAD data to the state */
-   for (; x < adatalen; x++) {
-      gcm->X[gcm->buflen++] ^= *adata++;
-
-      if (gcm->buflen == 16) {
-         /* GF mult it */
-         gcm_mult_h(gcm, gcm->X);
-         gcm->buflen = 0;
-         gcm->totlen += 128;
-      }
-   }
-
-   return CRYPT_OK;
-}
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_add_iv.c b/libs/tomcrypt/src/encauth/gcm/gcm_add_iv.c
deleted file mode 100644
index 40d516100737..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_add_iv.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_add_iv.c
-   GCM implementation, add IV data to the state, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Add IV data to the GCM state
-  @param gcm    The GCM state
-  @param IV     The initial value data to add
-  @param IVlen  The length of the IV
-  @return CRYPT_OK on success
- */
-int gcm_add_iv(gcm_state *gcm,
-               const unsigned char *IV,     unsigned long IVlen)
-{
-   unsigned long x, y;
-   int           err;
-
-   LTC_ARGCHK(gcm != NULL);
-   if (IVlen > 0) {
-      LTC_ARGCHK(IV  != NULL);
-   }
-
-   /* must be in IV mode */
-   if (gcm->mode != LTC_GCM_MODE_IV) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (gcm->buflen >= 16 || gcm->buflen < 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-
-   /* trip the ivmode flag */
-   if (IVlen + gcm->buflen > 12) {
-      gcm->ivmode |= 1;
-   }
-
-   x = 0;
-#ifdef LTC_FAST
-   if (gcm->buflen == 0) {
-      for (x = 0; x < (IVlen & ~15); x += 16) {
-          for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-              *(LTC_FAST_TYPE_PTR_CAST(&gcm->X[y])) ^= *(LTC_FAST_TYPE_PTR_CAST(&IV[x + y]));
-          }
-          gcm_mult_h(gcm, gcm->X);
-          gcm->totlen += 128;
-      }
-      IV += x;
-   }
-#endif
-
-   /* start adding IV data to the state */
-   for (; x < IVlen; x++) {
-       gcm->buf[gcm->buflen++] = *IV++;
-
-      if (gcm->buflen == 16) {
-         /* GF mult it */
-         for (y = 0; y < 16; y++) {
-             gcm->X[y] ^= gcm->buf[y];
-         }
-         gcm_mult_h(gcm, gcm->X);
-         gcm->buflen = 0;
-         gcm->totlen += 128;
-      }
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_done.c b/libs/tomcrypt/src/encauth/gcm/gcm_done.c
deleted file mode 100644
index f38ef60d0964..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_done.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_done.c
-   GCM implementation, Terminate the stream, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Terminate a GCM stream
-  @param gcm     The GCM state
-  @param tag     [out] The destination for the MAC tag
-  @param taglen  [in/out]  The length of the MAC tag
-  @return CRYPT_OK on success
- */
-int gcm_done(gcm_state *gcm,
-                     unsigned char *tag,    unsigned long *taglen)
-{
-   unsigned long x;
-   int err;
-
-   LTC_ARGCHK(gcm     != NULL);
-   LTC_ARGCHK(tag     != NULL);
-   LTC_ARGCHK(taglen  != NULL);
-
-   if (gcm->buflen > 16 || gcm->buflen < 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (gcm->mode == LTC_GCM_MODE_IV) {
-      /* let's process the IV */
-      if ((err = gcm_add_aad(gcm, NULL, 0)) != CRYPT_OK) return err;
-   }
-
-   if (gcm->mode == LTC_GCM_MODE_AAD) {
-      /* let's process the AAD */
-      if ((err = gcm_process(gcm, NULL, 0, NULL, 0)) != CRYPT_OK) return err;
-   }
-
-   if (gcm->mode != LTC_GCM_MODE_TEXT) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* handle remaining ciphertext */
-   if (gcm->buflen) {
-      gcm->pttotlen += gcm->buflen * CONST64(8);
-      gcm_mult_h(gcm, gcm->X);
-   }
-
-   /* length */
-   STORE64H(gcm->totlen, gcm->buf);
-   STORE64H(gcm->pttotlen, gcm->buf+8);
-   for (x = 0; x < 16; x++) {
-       gcm->X[x] ^= gcm->buf[x];
-   }
-   gcm_mult_h(gcm, gcm->X);
-
-   /* encrypt original counter */
-   if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y_0, gcm->buf, &gcm->K)) != CRYPT_OK) {
-      return err;
-   }
-   for (x = 0; x < 16 && x < *taglen; x++) {
-       tag[x] = gcm->buf[x] ^ gcm->X[x];
-   }
-   *taglen = x;
-
-   cipher_descriptor[gcm->cipher].done(&gcm->K);
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_gf_mult.c b/libs/tomcrypt/src/encauth/gcm/gcm_gf_mult.c
deleted file mode 100644
index 36a52d4ba7d3..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_gf_mult.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_gf_mult.c
-   GCM implementation, do the GF mult, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#if defined(LTC_GCM_TABLES) || defined(LTC_LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST))
-
-/* this is x*2^128 mod p(x) ... the results are 16 bytes each stored in a packed format.  Since only the
- * lower 16 bits are not zero'ed I removed the upper 14 bytes */
-const unsigned char gcm_shift_table[256*2] = {
-0x00, 0x00, 0x01, 0xc2, 0x03, 0x84, 0x02, 0x46, 0x07, 0x08, 0x06, 0xca, 0x04, 0x8c, 0x05, 0x4e,
-0x0e, 0x10, 0x0f, 0xd2, 0x0d, 0x94, 0x0c, 0x56, 0x09, 0x18, 0x08, 0xda, 0x0a, 0x9c, 0x0b, 0x5e,
-0x1c, 0x20, 0x1d, 0xe2, 0x1f, 0xa4, 0x1e, 0x66, 0x1b, 0x28, 0x1a, 0xea, 0x18, 0xac, 0x19, 0x6e,
-0x12, 0x30, 0x13, 0xf2, 0x11, 0xb4, 0x10, 0x76, 0x15, 0x38, 0x14, 0xfa, 0x16, 0xbc, 0x17, 0x7e,
-0x38, 0x40, 0x39, 0x82, 0x3b, 0xc4, 0x3a, 0x06, 0x3f, 0x48, 0x3e, 0x8a, 0x3c, 0xcc, 0x3d, 0x0e,
-0x36, 0x50, 0x37, 0x92, 0x35, 0xd4, 0x34, 0x16, 0x31, 0x58, 0x30, 0x9a, 0x32, 0xdc, 0x33, 0x1e,
-0x24, 0x60, 0x25, 0xa2, 0x27, 0xe4, 0x26, 0x26, 0x23, 0x68, 0x22, 0xaa, 0x20, 0xec, 0x21, 0x2e,
-0x2a, 0x70, 0x2b, 0xb2, 0x29, 0xf4, 0x28, 0x36, 0x2d, 0x78, 0x2c, 0xba, 0x2e, 0xfc, 0x2f, 0x3e,
-0x70, 0x80, 0x71, 0x42, 0x73, 0x04, 0x72, 0xc6, 0x77, 0x88, 0x76, 0x4a, 0x74, 0x0c, 0x75, 0xce,
-0x7e, 0x90, 0x7f, 0x52, 0x7d, 0x14, 0x7c, 0xd6, 0x79, 0x98, 0x78, 0x5a, 0x7a, 0x1c, 0x7b, 0xde,
-0x6c, 0xa0, 0x6d, 0x62, 0x6f, 0x24, 0x6e, 0xe6, 0x6b, 0xa8, 0x6a, 0x6a, 0x68, 0x2c, 0x69, 0xee,
-0x62, 0xb0, 0x63, 0x72, 0x61, 0x34, 0x60, 0xf6, 0x65, 0xb8, 0x64, 0x7a, 0x66, 0x3c, 0x67, 0xfe,
-0x48, 0xc0, 0x49, 0x02, 0x4b, 0x44, 0x4a, 0x86, 0x4f, 0xc8, 0x4e, 0x0a, 0x4c, 0x4c, 0x4d, 0x8e,
-0x46, 0xd0, 0x47, 0x12, 0x45, 0x54, 0x44, 0x96, 0x41, 0xd8, 0x40, 0x1a, 0x42, 0x5c, 0x43, 0x9e,
-0x54, 0xe0, 0x55, 0x22, 0x57, 0x64, 0x56, 0xa6, 0x53, 0xe8, 0x52, 0x2a, 0x50, 0x6c, 0x51, 0xae,
-0x5a, 0xf0, 0x5b, 0x32, 0x59, 0x74, 0x58, 0xb6, 0x5d, 0xf8, 0x5c, 0x3a, 0x5e, 0x7c, 0x5f, 0xbe,
-0xe1, 0x00, 0xe0, 0xc2, 0xe2, 0x84, 0xe3, 0x46, 0xe6, 0x08, 0xe7, 0xca, 0xe5, 0x8c, 0xe4, 0x4e,
-0xef, 0x10, 0xee, 0xd2, 0xec, 0x94, 0xed, 0x56, 0xe8, 0x18, 0xe9, 0xda, 0xeb, 0x9c, 0xea, 0x5e,
-0xfd, 0x20, 0xfc, 0xe2, 0xfe, 0xa4, 0xff, 0x66, 0xfa, 0x28, 0xfb, 0xea, 0xf9, 0xac, 0xf8, 0x6e,
-0xf3, 0x30, 0xf2, 0xf2, 0xf0, 0xb4, 0xf1, 0x76, 0xf4, 0x38, 0xf5, 0xfa, 0xf7, 0xbc, 0xf6, 0x7e,
-0xd9, 0x40, 0xd8, 0x82, 0xda, 0xc4, 0xdb, 0x06, 0xde, 0x48, 0xdf, 0x8a, 0xdd, 0xcc, 0xdc, 0x0e,
-0xd7, 0x50, 0xd6, 0x92, 0xd4, 0xd4, 0xd5, 0x16, 0xd0, 0x58, 0xd1, 0x9a, 0xd3, 0xdc, 0xd2, 0x1e,
-0xc5, 0x60, 0xc4, 0xa2, 0xc6, 0xe4, 0xc7, 0x26, 0xc2, 0x68, 0xc3, 0xaa, 0xc1, 0xec, 0xc0, 0x2e,
-0xcb, 0x70, 0xca, 0xb2, 0xc8, 0xf4, 0xc9, 0x36, 0xcc, 0x78, 0xcd, 0xba, 0xcf, 0xfc, 0xce, 0x3e,
-0x91, 0x80, 0x90, 0x42, 0x92, 0x04, 0x93, 0xc6, 0x96, 0x88, 0x97, 0x4a, 0x95, 0x0c, 0x94, 0xce,
-0x9f, 0x90, 0x9e, 0x52, 0x9c, 0x14, 0x9d, 0xd6, 0x98, 0x98, 0x99, 0x5a, 0x9b, 0x1c, 0x9a, 0xde,
-0x8d, 0xa0, 0x8c, 0x62, 0x8e, 0x24, 0x8f, 0xe6, 0x8a, 0xa8, 0x8b, 0x6a, 0x89, 0x2c, 0x88, 0xee,
-0x83, 0xb0, 0x82, 0x72, 0x80, 0x34, 0x81, 0xf6, 0x84, 0xb8, 0x85, 0x7a, 0x87, 0x3c, 0x86, 0xfe,
-0xa9, 0xc0, 0xa8, 0x02, 0xaa, 0x44, 0xab, 0x86, 0xae, 0xc8, 0xaf, 0x0a, 0xad, 0x4c, 0xac, 0x8e,
-0xa7, 0xd0, 0xa6, 0x12, 0xa4, 0x54, 0xa5, 0x96, 0xa0, 0xd8, 0xa1, 0x1a, 0xa3, 0x5c, 0xa2, 0x9e,
-0xb5, 0xe0, 0xb4, 0x22, 0xb6, 0x64, 0xb7, 0xa6, 0xb2, 0xe8, 0xb3, 0x2a, 0xb1, 0x6c, 0xb0, 0xae,
-0xbb, 0xf0, 0xba, 0x32, 0xb8, 0x74, 0xb9, 0xb6, 0xbc, 0xf8, 0xbd, 0x3a, 0xbf, 0x7c, 0xbe, 0xbe };
-
-#endif
-
-
-#if defined(LTC_GCM_MODE) || defined(LRW_MODE)
-
-#ifndef LTC_FAST
-/* right shift */
-static void _gcm_rightshift(unsigned char *a)
-{
-   int x;
-   for (x = 15; x > 0; x--) {
-       a[x] = (a[x]>>1) | ((a[x-1]<<7)&0x80);
-   }
-   a[0] >>= 1;
-}
-
-/* c = b*a */
-static const unsigned char mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
-static const unsigned char poly[] = { 0x00, 0xE1 };
-
-
-/**
-  GCM GF multiplier (internal use only)  bitserial
-  @param a   First value
-  @param b   Second value
-  @param c   Destination for a * b
- */
-void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c)
-{
-   unsigned char Z[16], V[16];
-   unsigned char x, y, z;
-
-   zeromem(Z, 16);
-   XMEMCPY(V, a, 16);
-   for (x = 0; x < 128; x++) {
-       if (b[x>>3] & mask[x&7]) {
-          for (y = 0; y < 16; y++) {
-              Z[y] ^= V[y];
-          }
-       }
-       z     = V[15] & 0x01;
-       _gcm_rightshift(V);
-       V[0] ^= poly[z];
-   }
-   XMEMCPY(c, Z, 16);
-}
-
-#else
-
-/* map normal numbers to "ieee" way ... e.g. bit reversed */
-#define M(x) ( ((x&8)>>3) | ((x&4)>>1) | ((x&2)<<1) | ((x&1)<<3) )
-
-#define BPD (sizeof(LTC_FAST_TYPE) * 8)
-#define WPV (1 + (16 / sizeof(LTC_FAST_TYPE)))
-
-/**
-  GCM GF multiplier (internal use only)  word oriented
-  @param a   First value
-  @param b   Second value
-  @param c   Destination for a * b
- */
-void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c)
-{
-   int i, j, k, u;
-   LTC_FAST_TYPE B[16][WPV], tmp[32 / sizeof(LTC_FAST_TYPE)], pB[16 / sizeof(LTC_FAST_TYPE)], zz, z;
-   unsigned char pTmp[32];
-
-   /* create simple tables */
-   zeromem(B[0],       sizeof(B[0]));
-   zeromem(B[M(1)],    sizeof(B[M(1)]));
-
-#ifdef ENDIAN_32BITWORD
-   for (i = 0; i < 4; i++) {
-       LOAD32H(B[M(1)][i], a + (i<<2));
-       LOAD32L(pB[i],      b + (i<<2));
-   }
-#else
-   for (i = 0; i < 2; i++) {
-       LOAD64H(B[M(1)][i], a + (i<<3));
-       LOAD64L(pB[i],      b + (i<<3));
-   }
-#endif
-
-   /* now create 2, 4 and 8 */
-   B[M(2)][0] = B[M(1)][0] >> 1;
-   B[M(4)][0] = B[M(1)][0] >> 2;
-   B[M(8)][0] = B[M(1)][0] >> 3;
-   for (i = 1; i < (int)WPV; i++) {
-      B[M(2)][i] = (B[M(1)][i-1] << (BPD-1)) | (B[M(1)][i] >> 1);
-      B[M(4)][i] = (B[M(1)][i-1] << (BPD-2)) | (B[M(1)][i] >> 2);
-      B[M(8)][i] = (B[M(1)][i-1] << (BPD-3)) | (B[M(1)][i] >> 3);
-   }
-
-   /*  now all values with two bits which are 3, 5, 6, 9, 10, 12 */
-   for (i = 0; i < (int)WPV; i++) {
-      B[M(3)][i]  = B[M(1)][i] ^ B[M(2)][i];
-      B[M(5)][i]  = B[M(1)][i] ^ B[M(4)][i];
-      B[M(6)][i]  = B[M(2)][i] ^ B[M(4)][i];
-      B[M(9)][i]  = B[M(1)][i] ^ B[M(8)][i];
-      B[M(10)][i] = B[M(2)][i] ^ B[M(8)][i];
-      B[M(12)][i] = B[M(8)][i] ^ B[M(4)][i];
-
-   /*  now all 3 bit values and the only 4 bit value: 7, 11, 13, 14, 15 */
-      B[M(7)][i]  = B[M(3)][i] ^ B[M(4)][i];
-      B[M(11)][i] = B[M(3)][i] ^ B[M(8)][i];
-      B[M(13)][i] = B[M(1)][i] ^ B[M(12)][i];
-      B[M(14)][i] = B[M(6)][i] ^ B[M(8)][i];
-      B[M(15)][i] = B[M(7)][i] ^ B[M(8)][i];
-   }
-
-   zeromem(tmp, sizeof(tmp));
-
-   /* compute product four bits of each word at a time */
-   /* for each nibble */
-   for (i = (BPD/4)-1; i >= 0; i--) {
-       /* for each word */
-       for (j = 0; j < (int)(WPV-1); j++) {
-        /* grab the 4 bits recall the nibbles are backwards so it's a shift by (i^1)*4 */
-           u = (pB[j] >> ((i^1)<<2)) & 15;
-
-        /* add offset by the word count the table looked up value to the result */
-           for (k = 0; k < (int)WPV; k++) {
-               tmp[k+j] ^= B[u][k];
-           }
-       }
-     /* shift result up by 4 bits */
-       if (i != 0) {
-          for (z = j = 0; j < (int)(32 / sizeof(LTC_FAST_TYPE)); j++) {
-              zz = tmp[j] << (BPD-4);
-              tmp[j] = (tmp[j] >> 4) | z;
-              z = zz;
-          }
-       }
-   }
-
-   /* store product */
-#ifdef ENDIAN_32BITWORD
-   for (i = 0; i < 8; i++) {
-       STORE32H(tmp[i], pTmp + (i<<2));
-   }
-#else
-   for (i = 0; i < 4; i++) {
-       STORE64H(tmp[i], pTmp + (i<<3));
-   }
-#endif
-
-   /* reduce by taking most significant byte and adding the appropriate two byte sequence 16 bytes down */
-   for (i = 31; i >= 16; i--) {
-       pTmp[i-16] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)];
-       pTmp[i-15] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)+1];
-   }
-
-   for (i = 0; i < 16; i++) {
-       c[i] = pTmp[i];
-   }
-
-}
-
-#endif
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_init.c b/libs/tomcrypt/src/encauth/gcm/gcm_init.c
deleted file mode 100644
index b294deb62706..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_init.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_init.c
-   GCM implementation, initialize state, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Initialize a GCM state
-  @param gcm     The GCM state to initialize
-  @param cipher  The index of the cipher to use
-  @param key     The secret key
-  @param keylen  The length of the secret key
-  @return CRYPT_OK on success
- */
-int gcm_init(gcm_state *gcm, int cipher,
-             const unsigned char *key,  int keylen)
-{
-   int           err;
-   unsigned char B[16];
-#ifdef LTC_GCM_TABLES
-   int           x, y, z, t;
-#endif
-
-   LTC_ARGCHK(gcm != NULL);
-   LTC_ARGCHK(key != NULL);
-
-#ifdef LTC_FAST
-   if (16 % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   /* is cipher valid? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (cipher_descriptor[cipher].block_length != 16) {
-      return CRYPT_INVALID_CIPHER;
-   }
-
-   /* schedule key */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &gcm->K)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* H = E(0) */
-   zeromem(B, 16);
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(B, gcm->H, &gcm->K)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* setup state */
-   zeromem(gcm->buf, sizeof(gcm->buf));
-   zeromem(gcm->X,   sizeof(gcm->X));
-   gcm->cipher   = cipher;
-   gcm->mode     = LTC_GCM_MODE_IV;
-   gcm->ivmode   = 0;
-   gcm->buflen   = 0;
-   gcm->totlen   = 0;
-   gcm->pttotlen = 0;
-
-#ifdef LTC_GCM_TABLES
-   /* setup tables */
-
-   /* generate the first table as it has no shifting (from which we make the other tables) */
-   zeromem(B, 16);
-   for (y = 0; y < 256; y++) {
-        B[0] = y;
-        gcm_gf_mult(gcm->H, B, &gcm->PC[0][y][0]);
-   }
-
-   /* now generate the rest of the tables based the previous table */
-   for (x = 1; x < 16; x++) {
-      for (y = 0; y < 256; y++) {
-         /* now shift it right by 8 bits */
-         t = gcm->PC[x-1][y][15];
-         for (z = 15; z > 0; z--) {
-             gcm->PC[x][y][z] = gcm->PC[x-1][y][z-1];
-         }
-         gcm->PC[x][y][0] = gcm_shift_table[t<<1];
-         gcm->PC[x][y][1] ^= gcm_shift_table[(t<<1)+1];
-      }
-   }
-
-#endif
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_memory.c b/libs/tomcrypt/src/encauth/gcm/gcm_memory.c
deleted file mode 100644
index f5fe3d57c50c..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_memory.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_memory.c
-   GCM implementation, process a packet, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Process an entire GCM packet in one call.
-  @param cipher            Index of cipher to use
-  @param key               The secret key
-  @param keylen            The length of the secret key
-  @param IV                The initialization vector
-  @param IVlen             The length of the initialization vector
-  @param adata             The additional authentication data (header)
-  @param adatalen          The length of the adata
-  @param pt                The plaintext
-  @param ptlen             The length of the plaintext (ciphertext length is the same)
-  @param ct                The ciphertext
-  @param tag               [out] The MAC tag
-  @param taglen            [in/out] The MAC tag length
-  @param direction         Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
-  @return CRYPT_OK on success
- */
-int gcm_memory(      int           cipher,
-               const unsigned char *key,    unsigned long keylen,
-               const unsigned char *IV,     unsigned long IVlen,
-               const unsigned char *adata,  unsigned long adatalen,
-                     unsigned char *pt,     unsigned long ptlen,
-                     unsigned char *ct,
-                     unsigned char *tag,    unsigned long *taglen,
-                               int direction)
-{
-    void      *orig;
-    gcm_state *gcm;
-    int        err;
-
-    if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-       return err;
-    }
-
-    if (cipher_descriptor[cipher].accel_gcm_memory != NULL) {
-       return cipher_descriptor[cipher].accel_gcm_memory
-                                          (key,   keylen,
-                                           IV,    IVlen,
-                                           adata, adatalen,
-                                           pt,    ptlen,
-                                           ct,
-                                           tag,   taglen,
-                                           direction);
-    }
-
-
-
-#ifndef LTC_GCM_TABLES_SSE2
-    orig = gcm = XMALLOC(sizeof(*gcm));
-#else
-    orig = gcm = XMALLOC(sizeof(*gcm) + 16);
-#endif
-    if (gcm == NULL) {
-        return CRYPT_MEM;
-    }
-
-   /* Force GCM to be on a multiple of 16 so we can use 128-bit aligned operations
-    * note that we only modify gcm and keep orig intact.  This code is not portable
-    * but again it's only for SSE2 anyways, so who cares?
-    */
-#ifdef LTC_GCM_TABLES_SSE2
-   if ((unsigned long)gcm & 15) {
-      gcm = (gcm_state *)((unsigned long)gcm + (16 - ((unsigned long)gcm & 15)));
-   }
-#endif
-
-    if ((err = gcm_init(gcm, cipher, key, keylen)) != CRYPT_OK) {
-       goto LTC_ERR;
-    }
-    if ((err = gcm_add_iv(gcm, IV, IVlen)) != CRYPT_OK) {
-       goto LTC_ERR;
-    }
-    if ((err = gcm_add_aad(gcm, adata, adatalen)) != CRYPT_OK) {
-       goto LTC_ERR;
-    }
-    if ((err = gcm_process(gcm, pt, ptlen, ct, direction)) != CRYPT_OK) {
-       goto LTC_ERR;
-    }
-    err = gcm_done(gcm, tag, taglen);
-LTC_ERR:
-    XFREE(orig);
-    return err;
-}
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_mult_h.c b/libs/tomcrypt/src/encauth/gcm/gcm_mult_h.c
deleted file mode 100644
index 17e281b07b97..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_mult_h.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_mult_h.c
-   GCM implementation, do the GF mult, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#if defined(LTC_GCM_MODE)
-/**
-  GCM multiply by H
-  @param gcm   The GCM state which holds the H value
-  @param I     The value to multiply H by
- */
-void gcm_mult_h(gcm_state *gcm, unsigned char *I)
-{
-   unsigned char T[16];
-#ifdef LTC_GCM_TABLES
-   int x;
-#ifdef LTC_GCM_TABLES_SSE2
-   asm("movdqa (%0),%%xmm0"::"r"(&gcm->PC[0][I[0]][0]));
-   for (x = 1; x < 16; x++) {
-      asm("pxor (%0),%%xmm0"::"r"(&gcm->PC[x][I[x]][0]));
-   }
-   asm("movdqa %%xmm0,(%0)"::"r"(&T));
-#else
-   int y;
-   XMEMCPY(T, &gcm->PC[0][I[0]][0], 16);
-   for (x = 1; x < 16; x++) {
-#ifdef LTC_FAST
-       for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-           *(LTC_FAST_TYPE_PTR_CAST(T + y)) ^= *(LTC_FAST_TYPE_PTR_CAST(&gcm->PC[x][I[x]][y]));
-       }
-#else
-       for (y = 0; y < 16; y++) {
-           T[y] ^= gcm->PC[x][I[x]][y];
-       }
-#endif /* LTC_FAST */
-   }
-#endif /* LTC_GCM_TABLES_SSE2 */
-#else
-   gcm_gf_mult(gcm->H, I, T);
-#endif
-   XMEMCPY(I, T, 16);
-}
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_process.c b/libs/tomcrypt/src/encauth/gcm/gcm_process.c
deleted file mode 100644
index 142bcbe809be..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_process.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_process.c
-   GCM implementation, process message data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Process plaintext/ciphertext through GCM
-  @param gcm       The GCM state
-  @param pt        The plaintext
-  @param ptlen     The plaintext length (ciphertext length is the same)
-  @param ct        The ciphertext
-  @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
-  @return CRYPT_OK on success
- */
-int gcm_process(gcm_state *gcm,
-                     unsigned char *pt,     unsigned long ptlen,
-                     unsigned char *ct,
-                     int direction)
-{
-   unsigned long x;
-   int           y, err;
-   unsigned char b;
-
-   LTC_ARGCHK(gcm != NULL);
-   if (ptlen > 0) {
-      LTC_ARGCHK(pt  != NULL);
-      LTC_ARGCHK(ct  != NULL);
-   }
-
-   if (gcm->buflen > 16 || gcm->buflen < 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* 0xFFFFFFFE0 = ((2^39)-256)/8 */
-   if (gcm->pttotlen / 8 + (ulong64)gcm->buflen + (ulong64)ptlen >= CONST64(0xFFFFFFFE0)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (gcm->mode == LTC_GCM_MODE_IV) {
-      /* let's process the IV */
-      if ((err = gcm_add_aad(gcm, NULL, 0)) != CRYPT_OK) return err;
-   }
-
-   /* in AAD mode? */
-   if (gcm->mode == LTC_GCM_MODE_AAD) {
-      /* let's process the AAD */
-      if (gcm->buflen) {
-         gcm->totlen += gcm->buflen * CONST64(8);
-         gcm_mult_h(gcm, gcm->X);
-      }
-
-      /* increment counter */
-      for (y = 15; y >= 12; y--) {
-          if (++gcm->Y[y] & 255) { break; }
-      }
-      /* encrypt the counter */
-      if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
-         return err;
-      }
-
-      gcm->buflen = 0;
-      gcm->mode   = LTC_GCM_MODE_TEXT;
-   }
-
-   if (gcm->mode != LTC_GCM_MODE_TEXT) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   x = 0;
-#ifdef LTC_FAST
-   if (gcm->buflen == 0) {
-      if (direction == GCM_ENCRYPT) {
-         for (x = 0; x < (ptlen & ~15); x += 16) {
-             /* ctr encrypt */
-             for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-                 *(LTC_FAST_TYPE_PTR_CAST(&ct[x + y])) = *(LTC_FAST_TYPE_PTR_CAST(&pt[x+y])) ^ *(LTC_FAST_TYPE_PTR_CAST(&gcm->buf[y]));
-                 *(LTC_FAST_TYPE_PTR_CAST(&gcm->X[y])) ^= *(LTC_FAST_TYPE_PTR_CAST(&ct[x+y]));
-             }
-             /* GMAC it */
-             gcm->pttotlen += 128;
-             gcm_mult_h(gcm, gcm->X);
-             /* increment counter */
-             for (y = 15; y >= 12; y--) {
-                 if (++gcm->Y[y] & 255) { break; }
-             }
-             if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
-                return err;
-             }
-         }
-      } else {
-         for (x = 0; x < (ptlen & ~15); x += 16) {
-             /* ctr encrypt */
-             for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-                 *(LTC_FAST_TYPE_PTR_CAST(&gcm->X[y])) ^= *(LTC_FAST_TYPE_PTR_CAST(&ct[x+y]));
-                 *(LTC_FAST_TYPE_PTR_CAST(&pt[x + y])) = *(LTC_FAST_TYPE_PTR_CAST(&ct[x+y])) ^ *(LTC_FAST_TYPE_PTR_CAST(&gcm->buf[y]));
-             }
-             /* GMAC it */
-             gcm->pttotlen += 128;
-             gcm_mult_h(gcm, gcm->X);
-             /* increment counter */
-             for (y = 15; y >= 12; y--) {
-                 if (++gcm->Y[y] & 255) { break; }
-             }
-             if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
-                return err;
-             }
-         }
-      }
-   }
-#endif
-
-   /* process text */
-   for (; x < ptlen; x++) {
-       if (gcm->buflen == 16) {
-          gcm->pttotlen += 128;
-          gcm_mult_h(gcm, gcm->X);
-
-          /* increment counter */
-          for (y = 15; y >= 12; y--) {
-              if (++gcm->Y[y] & 255) { break; }
-          }
-          if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
-             return err;
-          }
-          gcm->buflen = 0;
-       }
-
-       if (direction == GCM_ENCRYPT) {
-          b = ct[x] = pt[x] ^ gcm->buf[gcm->buflen];
-       } else {
-          b = ct[x];
-          pt[x] = ct[x] ^ gcm->buf[gcm->buflen];
-       }
-       gcm->X[gcm->buflen++] ^= b;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/gcm/gcm_reset.c b/libs/tomcrypt/src/encauth/gcm/gcm_reset.c
deleted file mode 100644
index a0b5433adc17..000000000000
--- a/libs/tomcrypt/src/encauth/gcm/gcm_reset.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file gcm_reset.c
-   GCM implementation, reset a used state so it can accept IV data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_GCM_MODE
-
-/**
-  Reset a GCM state to as if you just called gcm_init().  This saves the initialization time.
-  @param gcm   The GCM state to reset
-  @return CRYPT_OK on success
-*/
-int gcm_reset(gcm_state *gcm)
-{
-   LTC_ARGCHK(gcm != NULL);
-
-   zeromem(gcm->buf, sizeof(gcm->buf));
-   zeromem(gcm->X,   sizeof(gcm->X));
-   gcm->mode     = LTC_GCM_MODE_IV;
-   gcm->ivmode   = 0;
-   gcm->buflen   = 0;
-   gcm->totlen   = 0;
-   gcm->pttotlen = 0;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_decrypt.c b/libs/tomcrypt/src/encauth/ocb/ocb_decrypt.c
deleted file mode 100644
index 1f7120374eee..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_decrypt.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_decrypt.c
-   OCB implementation, decrypt data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-  Decrypt a block with OCB.
-  @param ocb    The OCB state
-  @param ct     The ciphertext (length of the block size of the block cipher)
-  @param pt     [out] The plaintext (length of ct)
-  @return CRYPT_OK if successful
-*/
-int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt)
-{
-   unsigned char Z[MAXBLOCKSIZE], tmp[MAXBLOCKSIZE];
-   int err, x;
-
-   LTC_ARGCHK(ocb != NULL);
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-
-   /* check if valid cipher */
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   LTC_ARGCHK(cipher_descriptor[ocb->cipher].ecb_decrypt != NULL);
-
-   /* check length */
-   if (ocb->block_len != cipher_descriptor[ocb->cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* Get Z[i] value */
-   ocb_shift_xor(ocb, Z);
-
-   /* xor ct in, encrypt, xor Z out */
-   for (x = 0; x < ocb->block_len; x++) {
-       tmp[x] = ct[x] ^ Z[x];
-   }
-   if ((err = cipher_descriptor[ocb->cipher].ecb_decrypt(tmp, pt, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-   for (x = 0; x < ocb->block_len; x++) {
-       pt[x] ^= Z[x];
-   }
-
-   /* compute checksum */
-   for (x = 0; x < ocb->block_len; x++) {
-       ocb->checksum[x] ^= pt[x];
-   }
-
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(Z, sizeof(Z));
-   zeromem(tmp, sizeof(tmp));
-#endif
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_decrypt_verify_memory.c b/libs/tomcrypt/src/encauth/ocb/ocb_decrypt_verify_memory.c
deleted file mode 100644
index d2897352d347..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_decrypt_verify_memory.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-  @file ocb_decrypt_verify_memory.c
-  OCB implementation, helper to decrypt block of memory, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Decrypt and compare the tag with OCB.
-   @param cipher     The index of the cipher desired
-   @param key        The secret key
-   @param keylen     The length of the secret key (octets)
-   @param nonce      The session nonce (length of the block size of the block cipher)
-   @param ct         The ciphertext
-   @param ctlen      The length of the ciphertext (octets)
-   @param pt         [out] The plaintext
-   @param tag        The tag to compare against
-   @param taglen     The length of the tag (octets)
-   @param stat       [out] The result of the tag comparison (1==valid, 0==invalid)
-   @return CRYPT_OK if successful regardless of the tag comparison
-*/
-int ocb_decrypt_verify_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,
-    const unsigned char *ct,     unsigned long ctlen,
-          unsigned char *pt,
-    const unsigned char *tag,    unsigned long taglen,
-          int           *stat)
-{
-   int err;
-   ocb_state *ocb;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(nonce  != NULL);
-   LTC_ARGCHK(pt     != NULL);
-   LTC_ARGCHK(ct     != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(stat    != NULL);
-
-   /* allocate memory */
-   ocb = XMALLOC(sizeof(ocb_state));
-   if (ocb == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = ocb_init(ocb, cipher, key, keylen, nonce)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   while (ctlen > (unsigned long)ocb->block_len) {
-        if ((err = ocb_decrypt(ocb, ct, pt)) != CRYPT_OK) {
-            goto LBL_ERR;
-        }
-        ctlen   -= ocb->block_len;
-        pt      += ocb->block_len;
-        ct      += ocb->block_len;
-   }
-
-   err = ocb_done_decrypt(ocb, ct, ctlen, pt, tag, taglen, stat);
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(ocb, sizeof(ocb_state));
-#endif
-
-   XFREE(ocb);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_done_decrypt.c b/libs/tomcrypt/src/encauth/ocb/ocb_done_decrypt.c
deleted file mode 100644
index 7b4428b185a9..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_done_decrypt.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_done_decrypt.c
-   OCB implementation, terminate decryption, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Terminate a decrypting OCB state
-   @param ocb    The OCB state
-   @param ct     The ciphertext (if any)
-   @param ctlen  The length of the ciphertext (octets)
-   @param pt     [out] The plaintext
-   @param tag    The authentication tag (to compare against)
-   @param taglen The length of the authentication tag provided
-   @param stat    [out] The result of the tag comparison
-   @return CRYPT_OK if the process was successful regardless if the tag is valid
-*/
-int ocb_done_decrypt(ocb_state *ocb,
-                     const unsigned char *ct,  unsigned long ctlen,
-                           unsigned char *pt,
-                     const unsigned char *tag, unsigned long taglen, int *stat)
-{
-   int err;
-   unsigned char *tagbuf;
-   unsigned long tagbuflen;
-
-   LTC_ARGCHK(ocb  != NULL);
-   LTC_ARGCHK(pt   != NULL);
-   LTC_ARGCHK(ct   != NULL);
-   LTC_ARGCHK(tag  != NULL);
-   LTC_ARGCHK(stat != NULL);
-
-   /* default to failed */
-   *stat = 0;
-
-   /* allocate memory */
-   tagbuf = XMALLOC(MAXBLOCKSIZE);
-   if (tagbuf == NULL) {
-      return CRYPT_MEM;
-   }
-
-   tagbuflen = MAXBLOCKSIZE;
-   if ((err = s_ocb_done(ocb, ct, ctlen, pt, tagbuf, &tagbuflen, 1)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   if (taglen <= tagbuflen && XMEM_NEQ(tagbuf, tag, taglen) == 0) {
-      *stat = 1;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(tagbuf, MAXBLOCKSIZE);
-#endif
-
-   XFREE(tagbuf);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_done_encrypt.c b/libs/tomcrypt/src/encauth/ocb/ocb_done_encrypt.c
deleted file mode 100644
index b126100ec77f..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_done_encrypt.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_done_encrypt.c
-   OCB implementation, terminate encryption, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Terminate an encryption OCB state
-   @param ocb       The OCB state
-   @param pt        Remaining plaintext (if any)
-   @param ptlen     The length of the plaintext (octets)
-   @param ct        [out] The ciphertext (if any)
-   @param tag       [out] The tag for the OCB stream
-   @param taglen    [in/out] The max size and resulting size of the tag
-   @return CRYPT_OK if successful
-*/
-int ocb_done_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen,
-                     unsigned char *ct, unsigned char *tag, unsigned long *taglen)
-{
-   LTC_ARGCHK(ocb    != NULL);
-   LTC_ARGCHK(pt     != NULL);
-   LTC_ARGCHK(ct     != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-   return s_ocb_done(ocb, pt, ptlen, ct, tag, taglen, 0);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_encrypt.c b/libs/tomcrypt/src/encauth/ocb/ocb_encrypt.c
deleted file mode 100644
index fd94b17f8c3c..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_encrypt.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_encrypt.c
-   OCB implementation, encrypt data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Encrypt a block of data with OCB.
-   @param ocb     The OCB state
-   @param pt      The plaintext (length of the block size of the block cipher)
-   @param ct      [out] The ciphertext (same size as the pt)
-   @return CRYPT_OK if successful
-*/
-int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct)
-{
-   unsigned char Z[MAXBLOCKSIZE], tmp[MAXBLOCKSIZE];
-   int err, x;
-
-   LTC_ARGCHK(ocb != NULL);
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (ocb->block_len != cipher_descriptor[ocb->cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* compute checksum */
-   for (x = 0; x < ocb->block_len; x++) {
-       ocb->checksum[x] ^= pt[x];
-   }
-
-   /* Get Z[i] value */
-   ocb_shift_xor(ocb, Z);
-
-   /* xor pt in, encrypt, xor Z out */
-   for (x = 0; x < ocb->block_len; x++) {
-       tmp[x] = pt[x] ^ Z[x];
-   }
-   if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, ct, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-   for (x = 0; x < ocb->block_len; x++) {
-       ct[x] ^= Z[x];
-   }
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(Z, sizeof(Z));
-   zeromem(tmp, sizeof(tmp));
-#endif
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_encrypt_authenticate_memory.c b/libs/tomcrypt/src/encauth/ocb/ocb_encrypt_authenticate_memory.c
deleted file mode 100644
index 5acab3549f43..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_encrypt_authenticate_memory.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-  @file ocb_encrypt_authenticate_memory.c
-  OCB implementation, encrypt block of memory, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Encrypt and generate an authentication code for a buffer of memory
-   @param cipher     The index of the cipher desired
-   @param key        The secret key
-   @param keylen     The length of the secret key (octets)
-   @param nonce      The session nonce (length of the block ciphers block size)
-   @param pt         The plaintext
-   @param ptlen      The length of the plaintext (octets)
-   @param ct         [out] The ciphertext
-   @param tag        [out] The authentication tag
-   @param taglen     [in/out] The max size and resulting size of the authentication tag
-   @return CRYPT_OK if successful
-*/
-int ocb_encrypt_authenticate_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,
-    const unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen)
-{
-   int err;
-   ocb_state *ocb;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(nonce  != NULL);
-   LTC_ARGCHK(pt     != NULL);
-   LTC_ARGCHK(ct     != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-
-   /* allocate ram */
-   ocb = XMALLOC(sizeof(ocb_state));
-   if (ocb == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = ocb_init(ocb, cipher, key, keylen, nonce)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   while (ptlen > (unsigned long)ocb->block_len) {
-        if ((err = ocb_encrypt(ocb, pt, ct)) != CRYPT_OK) {
-           goto LBL_ERR;
-        }
-        ptlen   -= ocb->block_len;
-        pt      += ocb->block_len;
-        ct      += ocb->block_len;
-   }
-
-   err = ocb_done_encrypt(ocb, pt, ptlen, ct, tag, taglen);
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(ocb, sizeof(ocb_state));
-#endif
-
-   XFREE(ocb);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_init.c b/libs/tomcrypt/src/encauth/ocb/ocb_init.c
deleted file mode 100644
index 3b12f3311363..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_init.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_init.c
-   OCB implementation, initialize state, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-static const struct {
-    int           len;
-    unsigned char poly_div[MAXBLOCKSIZE],
-                  poly_mul[MAXBLOCKSIZE];
-} polys[] = {
-{
-    8,
-    { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D },
-    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B }
-}, {
-    16,
-    { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 },
-    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 }
-}
-};
-
-/**
-  Initialize an OCB context.
-  @param ocb     [out] The destination of the OCB state
-  @param cipher  The index of the desired cipher
-  @param key     The secret key
-  @param keylen  The length of the secret key (octets)
-  @param nonce   The session nonce (length of the block size of the cipher)
-  @return CRYPT_OK if successful
-*/
-int ocb_init(ocb_state *ocb, int cipher,
-             const unsigned char *key, unsigned long keylen, const unsigned char *nonce)
-{
-   int poly, x, y, m, err;
-
-   LTC_ARGCHK(ocb   != NULL);
-   LTC_ARGCHK(key   != NULL);
-   LTC_ARGCHK(nonce != NULL);
-
-   /* valid cipher? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* determine which polys to use */
-   ocb->block_len = cipher_descriptor[cipher].block_length;
-   x = (int)(sizeof(polys)/sizeof(polys[0]));
-   for (poly = 0; poly < x; poly++) {
-       if (polys[poly].len == ocb->block_len) {
-          break;
-       }
-   }
-   if (poly == x) {
-      return CRYPT_INVALID_ARG; /* block_len not found in polys */
-   }
-   if (polys[poly].len != ocb->block_len) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* schedule the key */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* find L = E[0] */
-   zeromem(ocb->L, ocb->block_len);
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(ocb->L, ocb->L, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* find R = E[N xor L] */
-   for (x = 0; x < ocb->block_len; x++) {
-       ocb->R[x] = ocb->L[x] ^ nonce[x];
-   }
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(ocb->R, ocb->R, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* find Ls[i] = L << i for i == 0..31 */
-   XMEMCPY(ocb->Ls[0], ocb->L, ocb->block_len);
-   for (x = 1; x < 32; x++) {
-       m = ocb->Ls[x-1][0] >> 7;
-       for (y = 0; y < ocb->block_len-1; y++) {
-           ocb->Ls[x][y] = ((ocb->Ls[x-1][y] << 1) | (ocb->Ls[x-1][y+1] >> 7)) & 255;
-       }
-       ocb->Ls[x][ocb->block_len-1] = (ocb->Ls[x-1][ocb->block_len-1] << 1) & 255;
-
-       if (m == 1) {
-          for (y = 0; y < ocb->block_len; y++) {
-              ocb->Ls[x][y] ^= polys[poly].poly_mul[y];
-          }
-       }
-   }
-
-   /* find Lr = L / x */
-   m = ocb->L[ocb->block_len-1] & 1;
-
-   /* shift right */
-   for (x = ocb->block_len - 1; x > 0; x--) {
-      ocb->Lr[x] = ((ocb->L[x] >> 1) | (ocb->L[x-1] << 7)) & 255;
-   }
-   ocb->Lr[0] = ocb->L[0] >> 1;
-
-   if (m == 1) {
-      for (x = 0; x < ocb->block_len; x++) {
-         ocb->Lr[x] ^= polys[poly].poly_div[x];
-      }
-   }
-
-   /* set Li, checksum */
-   zeromem(ocb->Li,       ocb->block_len);
-   zeromem(ocb->checksum, ocb->block_len);
-
-   /* set other params */
-   ocb->block_index = 1;
-   ocb->cipher      = cipher;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_ntz.c b/libs/tomcrypt/src/encauth/ocb/ocb_ntz.c
deleted file mode 100644
index 6db1732575ab..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_ntz.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_ntz.c
-   OCB implementation, internal function, by Tom St Denis
-*/
-
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Returns the number of leading zero bits [from lsb up]
-   @param x  The 32-bit value to observe
-   @return   The number of bits [from the lsb up] that are zero
-*/
-int ocb_ntz(unsigned long x)
-{
-   int c;
-   x &= 0xFFFFFFFFUL;
-   c = 0;
-   while ((x & 1) == 0) {
-      ++c;
-      x >>= 1;
-   }
-   return c;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/ocb_shift_xor.c b/libs/tomcrypt/src/encauth/ocb/ocb_shift_xor.c
deleted file mode 100644
index 5dd42a9a1ae9..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/ocb_shift_xor.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb_shift_xor.c
-   OCB implementation, internal function, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/**
-   Compute the shift/xor for OCB (internal function)
-   @param ocb  The OCB state
-   @param Z    The destination of the shift
-*/
-void ocb_shift_xor(ocb_state *ocb, unsigned char *Z)
-{
-   int x, y;
-   y = ocb_ntz(ocb->block_index++);
-   for (x = 0; x < ocb->block_len; x++) {
-       ocb->Li[x] ^= ocb->Ls[y][x];
-       Z[x]        = ocb->Li[x] ^ ocb->R[x];
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb/s_ocb_done.c b/libs/tomcrypt/src/encauth/ocb/s_ocb_done.c
deleted file mode 100644
index a8aa8353b7bb..000000000000
--- a/libs/tomcrypt/src/encauth/ocb/s_ocb_done.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file s_ocb_done.c
-   OCB implementation, internal helper, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB_MODE
-
-/* Since the last block is encrypted in CTR mode the same code can
- * be used to finish a decrypt or encrypt stream.  The only difference
- * is we XOR the final ciphertext into the checksum so we have to xor it
- * before we CTR [decrypt] or after [encrypt]
- *
- * the names pt/ptlen/ct really just mean in/inlen/out but this is the way I wrote it...
- */
-
-/**
-   Shared code to finish an OCB stream
-   @param ocb    The OCB state
-   @param pt     The remaining plaintext [or input]
-   @param ptlen  The length of the input (octets)
-   @param ct     [out] The output buffer
-   @param tag    [out] The destination for the authentication tag
-   @param taglen [in/out] The max size and resulting size of the authentication tag
-   @param mode   The mode we are terminating, 0==encrypt, 1==decrypt
-   @return       CRYPT_OK if successful
-*/
-int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen,
-               unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode)
-
-{
-   unsigned char *Z, *Y, *X;
-   int err, x;
-
-   LTC_ARGCHK(ocb    != NULL);
-   LTC_ARGCHK(pt     != NULL);
-   LTC_ARGCHK(ct     != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (ocb->block_len != cipher_descriptor[ocb->cipher].block_length ||
-       (int)ptlen > ocb->block_len || (int)ptlen < 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* allocate ram */
-   Z = XMALLOC(MAXBLOCKSIZE);
-   Y = XMALLOC(MAXBLOCKSIZE);
-   X = XMALLOC(MAXBLOCKSIZE);
-   if (X == NULL || Y == NULL || Z == NULL) {
-      if (X != NULL) {
-         XFREE(X);
-      }
-      if (Y != NULL) {
-         XFREE(Y);
-      }
-      if (Z != NULL) {
-         XFREE(Z);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* compute X[m] = len(pt[m]) XOR Lr XOR Z[m] */
-   ocb_shift_xor(ocb, X);
-   XMEMCPY(Z, X, ocb->block_len);
-
-   X[ocb->block_len-1] ^= (ptlen*8)&255;
-   X[ocb->block_len-2] ^= ((ptlen*8)>>8)&255;
-   for (x = 0; x < ocb->block_len; x++) {
-       X[x] ^= ocb->Lr[x];
-   }
-
-   /* Y[m] = E(X[m])) */
-   if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(X, Y, &ocb->key)) != CRYPT_OK) {
-      goto error;
-   }
-
-   if (mode == 1) {
-      /* decrypt mode, so let's xor it first */
-      /* xor C[m] into checksum */
-      for (x = 0; x < (int)ptlen; x++) {
-         ocb->checksum[x] ^= ct[x];
-      }
-   }
-
-   /* C[m] = P[m] xor Y[m] */
-   for (x = 0; x < (int)ptlen; x++) {
-       ct[x] = pt[x] ^ Y[x];
-   }
-
-   if (mode == 0) {
-      /* encrypt mode */
-      /* xor C[m] into checksum */
-      for (x = 0; x < (int)ptlen; x++) {
-          ocb->checksum[x] ^= ct[x];
-      }
-   }
-
-   /* xor Y[m] and Z[m] into checksum */
-   for (x = 0; x < ocb->block_len; x++) {
-       ocb->checksum[x] ^= Y[x] ^ Z[x];
-   }
-
-   /* encrypt checksum, er... tag!! */
-   if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->checksum, X, &ocb->key)) != CRYPT_OK) {
-      goto error;
-   }
-   cipher_descriptor[ocb->cipher].done(&ocb->key);
-
-   /* now store it */
-   for (x = 0; x < ocb->block_len && x < (int)*taglen; x++) {
-       tag[x] = X[x];
-   }
-   *taglen = x;
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(X, MAXBLOCKSIZE);
-   zeromem(Y, MAXBLOCKSIZE);
-   zeromem(Z, MAXBLOCKSIZE);
-   zeromem(ocb, sizeof(*ocb));
-#endif
-error:
-   XFREE(X);
-   XFREE(Y);
-   XFREE(Z);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_add_aad.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_add_aad.c
deleted file mode 100644
index aaeb4c0ce144..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_add_aad.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_add_aad.c
-   OCB implementation, add AAD data, by Karel Miko
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Add one block of AAD data (internal function)
-   @param ocb        The OCB state
-   @param aad_block  [in] AAD data (block_len size)
-   @return CRYPT_OK if successful
-*/
-static int _ocb3_int_aad_add_block(ocb3_state *ocb, const unsigned char *aad_block)
-{
-   unsigned char tmp[MAXBLOCKSIZE];
-   int err;
-
-   /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */
-   ocb3_int_xor_blocks(ocb->aOffset_current, ocb->aOffset_current, ocb->L_[ocb3_int_ntz(ocb->ablock_index)], ocb->block_len);
-
-   /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) */
-   ocb3_int_xor_blocks(tmp, aad_block, ocb->aOffset_current, ocb->block_len);
-   if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) {
-     return err;
-   }
-   ocb3_int_xor_blocks(ocb->aSum_current, ocb->aSum_current, tmp, ocb->block_len);
-
-   ocb->ablock_index++;
-
-   return CRYPT_OK;
-}
-
-/**
-   Add AAD - additional associated data
-   @param ocb       The OCB state
-   @param aad       The AAD data
-   @param aadlen    The size of AAD data (octets)
-   @return CRYPT_OK if successful
-*/
-int ocb3_add_aad(ocb3_state *ocb, const unsigned char *aad, unsigned long aadlen)
-{
-   int err, x, full_blocks, full_blocks_len, last_block_len;
-   unsigned char *data;
-   unsigned long datalen, l;
-
-   LTC_ARGCHK(ocb != NULL);
-   if (aadlen == 0) return CRYPT_OK;
-   LTC_ARGCHK(aad != NULL);
-
-   if (ocb->adata_buffer_bytes > 0) {
-     l = ocb->block_len - ocb->adata_buffer_bytes;
-     if (l > aadlen) l = aadlen;
-     XMEMCPY(ocb->adata_buffer+ocb->adata_buffer_bytes, aad, l);
-     ocb->adata_buffer_bytes += l;
-
-     if (ocb->adata_buffer_bytes == ocb->block_len) {
-       if ((err = _ocb3_int_aad_add_block(ocb, ocb->adata_buffer)) != CRYPT_OK) {
-         return err;
-       }
-       ocb->adata_buffer_bytes = 0;
-     }
-
-     data = (unsigned char *)aad + l;
-     datalen = aadlen - l;
-   }
-   else {
-     data = (unsigned char *)aad;
-     datalen = aadlen;
-   }
-
-   if (datalen == 0) return CRYPT_OK;
-
-   full_blocks = datalen/ocb->block_len;
-   full_blocks_len = full_blocks * ocb->block_len;
-   last_block_len = datalen - full_blocks_len;
-
-   for (x=0; x<full_blocks; x++) {
-     if ((err = _ocb3_int_aad_add_block(ocb, data+x*ocb->block_len)) != CRYPT_OK) {
-       return err;
-     }
-   }
-
-   if (last_block_len>0) {
-     XMEMCPY(ocb->adata_buffer, data+full_blocks_len, last_block_len);
-     ocb->adata_buffer_bytes = last_block_len;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt.c
deleted file mode 100644
index fe175db532e2..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_decrypt.c
-   OCB implementation, decrypt data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Decrypt blocks of ciphertext with OCB
-   @param ocb     The OCB state
-   @param ct      The ciphertext (length multiple of the block size of the block cipher)
-   @param ctlen   The length of the input (octets)
-   @param pt      [out] The plaintext (length of ct)
-   @return CRYPT_OK if successful
-*/
-int ocb3_decrypt(ocb3_state *ocb, const unsigned char *ct, unsigned long ctlen, unsigned char *pt)
-{
-   unsigned char tmp[MAXBLOCKSIZE];
-   int err, i, full_blocks;
-   unsigned char *pt_b, *ct_b;
-
-   LTC_ARGCHK(ocb != NULL);
-   if (ctlen == 0) return CRYPT_OK; /* no data, nothing to do */
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(pt != NULL);
-
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (ocb->block_len != cipher_descriptor[ocb->cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (ctlen % ocb->block_len) { /* ctlen has to bu multiple of block_len */
-      return CRYPT_INVALID_ARG;
-   }
-
-   full_blocks = ctlen/ocb->block_len;
-   for(i=0; i<full_blocks; i++) {
-     pt_b = (unsigned char *)pt+i*ocb->block_len;
-     ct_b = (unsigned char *)ct+i*ocb->block_len;
-
-     /* ocb->Offset_current[] = ocb->Offset_current[] ^ Offset_{ntz(block_index)} */
-     ocb3_int_xor_blocks(ocb->Offset_current, ocb->Offset_current, ocb->L_[ocb3_int_ntz(ocb->block_index)], ocb->block_len);
-
-     /* tmp[] = ct[] XOR ocb->Offset_current[] */
-     ocb3_int_xor_blocks(tmp, ct_b, ocb->Offset_current, ocb->block_len);
-
-     /* decrypt */
-     if ((err = cipher_descriptor[ocb->cipher].ecb_decrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) {
-        goto LBL_ERR;
-     }
-
-     /* pt[] = tmp[] XOR ocb->Offset_current[] */
-     ocb3_int_xor_blocks(pt_b, tmp, ocb->Offset_current, ocb->block_len);
-
-     /* ocb->checksum[] = ocb->checksum[] XOR pt[] */
-     ocb3_int_xor_blocks(ocb->checksum, ocb->checksum, pt_b, ocb->block_len);
-
-     ocb->block_index++;
-   }
-
-   err = CRYPT_OK;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(tmp, sizeof(tmp));
-#endif
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt_last.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt_last.c
deleted file mode 100644
index 00607f110fd7..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt_last.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_decrypt_last.c
-   OCB implementation, internal helper, by Karel Miko
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Finish an OCB (decryption) stream
-   @param ocb    The OCB state
-   @param ct     The remaining ciphertext
-   @param ctlen  The length of the ciphertext (octets)
-   @param pt     [out] The output buffer
-   @return CRYPT_OK if successful
-*/
-int ocb3_decrypt_last(ocb3_state *ocb, const unsigned char *ct, unsigned long ctlen, unsigned char *pt)
-{
-   unsigned char iOffset_star[MAXBLOCKSIZE];
-   unsigned char iPad[MAXBLOCKSIZE];
-   int err, x, full_blocks, full_blocks_len, last_block_len;
-
-   LTC_ARGCHK(ocb != NULL);
-   if (ct == NULL) LTC_ARGCHK(ctlen == 0);
-   if (ctlen != 0) {
-      LTC_ARGCHK(ct    != NULL);
-      LTC_ARGCHK(pt    != NULL);
-   }
-
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   full_blocks = ctlen/ocb->block_len;
-   full_blocks_len = full_blocks * ocb->block_len;
-   last_block_len = ctlen - full_blocks_len;
-
-   /* process full blocks first */
-   if (full_blocks>0) {
-     if ((err = ocb3_decrypt(ocb, ct, full_blocks_len, pt)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-   }
-
-   if (last_block_len>0) {
-     /* Offset_* = Offset_m xor L_* */
-     ocb3_int_xor_blocks(iOffset_star, ocb->Offset_current, ocb->L_star, ocb->block_len);
-
-     /* Pad = ENCIPHER(K, Offset_*) */
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(iOffset_star, iPad, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-
-     /* P_* = C_* xor Pad[1..bitlen(C_*)] */
-     ocb3_int_xor_blocks(pt+full_blocks_len, (unsigned char *)ct+full_blocks_len, iPad, last_block_len);
-
-     /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */
-     ocb3_int_xor_blocks(ocb->checksum, ocb->checksum, pt+full_blocks_len, last_block_len);
-     for(x=last_block_len; x<ocb->block_len; x++) {
-       if (x == last_block_len)
-         ocb->checksum[x] ^= 0x80;
-       else
-         ocb->checksum[x] ^= 0x00;
-     }
-
-     /* Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A) */
-     /* at this point we calculate only: Tag_part = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) */
-     for(x=0; x<ocb->block_len; x++) {
-       ocb->tag_part[x] = (ocb->checksum[x] ^ iOffset_star[x]) ^ ocb->L_dollar[x];
-     }
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->tag_part, ocb->tag_part, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-   }
-   else {
-     /* Tag = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) xor HASH(K,A) */
-     /* at this point we calculate only: Tag_part = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) */
-     for(x=0; x<ocb->block_len; x++) {
-       ocb->tag_part[x] = (ocb->checksum[x] ^ ocb->Offset_current[x]) ^ ocb->L_dollar[x];
-     }
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->tag_part, ocb->tag_part, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-   }
-
-   err = CRYPT_OK;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(iOffset_star, MAXBLOCKSIZE);
-   zeromem(iPad, MAXBLOCKSIZE);
-#endif
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt_verify_memory.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt_verify_memory.c
deleted file mode 100644
index 7b500818630e..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_decrypt_verify_memory.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-  @file ocb3_decrypt_verify_memory.c
-  OCB implementation, helper to decrypt block of memory, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Decrypt and compare the tag with OCB
-   @param cipher     The index of the cipher desired
-   @param key        The secret key
-   @param keylen     The length of the secret key (octets)
-   @param nonce      The session nonce (length of the block size of the block cipher)
-   @param noncelen   The length of the nonce (octets)
-   @param adata      The AAD - additional associated data
-   @param adatalen   The length of AAD (octets)
-   @param ct         The ciphertext
-   @param ctlen      The length of the ciphertext (octets)
-   @param pt         [out] The plaintext
-   @param tag        The tag to compare against
-   @param taglen     The length of the tag (octets)
-   @param stat       [out] The result of the tag comparison (1==valid, 0==invalid)
-   @return CRYPT_OK if successful regardless of the tag comparison
-*/
-int ocb3_decrypt_verify_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *adata,  unsigned long adatalen,
-    const unsigned char *ct,     unsigned long ctlen,
-          unsigned char *pt,
-    const unsigned char *tag,    unsigned long taglen,
-          int           *stat)
-{
-   int            err;
-   ocb3_state     *ocb;
-   unsigned char *buf;
-   unsigned long  buflen;
-
-   LTC_ARGCHK(stat    != NULL);
-
-   /* default to zero */
-   *stat = 0;
-
-   /* limit taglen */
-   taglen = MIN(taglen, MAXBLOCKSIZE);
-
-   /* allocate memory */
-   buf = XMALLOC(taglen);
-   ocb = XMALLOC(sizeof(ocb3_state));
-   if (ocb == NULL || buf == NULL) {
-      if (ocb != NULL) {
-         XFREE(ocb);
-      }
-      if (buf != NULL) {
-         XFREE(buf);
-      }
-      return CRYPT_MEM;
-   }
-
-   if ((err = ocb3_init(ocb, cipher, key, keylen, nonce, noncelen, taglen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   if (adata != NULL || adatalen != 0) {
-      if ((err = ocb3_add_aad(ocb, adata, adatalen)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-   }
-
-   if ((err = ocb3_decrypt_last(ocb, ct, ctlen, pt)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   buflen = taglen;
-   if ((err = ocb3_done(ocb, buf, &buflen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* compare tags */
-   if (buflen >= taglen && XMEM_NEQ(buf, tag, taglen) == 0) {
-      *stat = 1;
-   }
-
-   err = CRYPT_OK;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(ocb, sizeof(ocb3_state));
-#endif
-
-   XFREE(ocb);
-   XFREE(buf);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_done.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_done.c
deleted file mode 100644
index fd73d3089cae..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_done.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_done.c
-   OCB implementation, INTERNAL ONLY helper, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Finish OCB processing and compute the tag
-   @param ocb     The OCB state
-   @param tag     [out] The destination for the authentication tag
-   @param taglen  [in/out] The max size and resulting size of the authentication tag
-   @return CRYPT_OK if successful
-*/
-int ocb3_done(ocb3_state *ocb, unsigned char *tag, unsigned long *taglen)
-{
-   unsigned char tmp[MAXBLOCKSIZE];
-   int err, x;
-
-   LTC_ARGCHK(ocb    != NULL);
-   LTC_ARGCHK(tag    != NULL);
-   LTC_ARGCHK(taglen != NULL);
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* check taglen */
-   if ((int)*taglen < ocb->tag_len) {
-      *taglen = (unsigned long)ocb->tag_len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* finalize AAD processing */
-
-   if (ocb->adata_buffer_bytes>0) {
-     /* Offset_* = Offset_m xor L_* */
-     ocb3_int_xor_blocks(ocb->aOffset_current, ocb->aOffset_current, ocb->L_star, ocb->block_len);
-
-     /* CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_* */
-     ocb3_int_xor_blocks(tmp, ocb->adata_buffer, ocb->aOffset_current, ocb->adata_buffer_bytes);
-     for(x=ocb->adata_buffer_bytes; x<ocb->block_len; x++) {
-       if (x == ocb->adata_buffer_bytes) {
-         tmp[x] = 0x80 ^ ocb->aOffset_current[x];
-       }
-       else {
-         tmp[x] = 0x00 ^ ocb->aOffset_current[x];
-       }
-     }
-
-     /* Sum = Sum_m xor ENCIPHER(K, CipherInput) */
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-     ocb3_int_xor_blocks(ocb->aSum_current, ocb->aSum_current, tmp, ocb->block_len);
-   }
-
-   /* finalize TAG computing */
-
-   /* at this point ocb->aSum_current = HASH(K, A) */
-   /* tag = tag ^ HASH(K, A) */
-   ocb3_int_xor_blocks(tmp, ocb->tag_part, ocb->aSum_current, ocb->block_len);
-
-   /* copy tag bytes */
-   for(x = 0; x < ocb->tag_len; x++) tag[x] = tmp[x];
-   *taglen = (unsigned long)ocb->tag_len;
-
-   err = CRYPT_OK;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(tmp, MAXBLOCKSIZE);
-   zeromem(ocb, sizeof(*ocb));
-#endif
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt.c
deleted file mode 100644
index b7b523ed793c..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_encrypt.c
-   OCB implementation, encrypt data, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Encrypt blocks of data with OCB
-   @param ocb     The OCB state
-   @param pt      The plaintext (length multiple of the block size of the block cipher)
-   @param ptlen   The length of the input (octets)
-   @param ct      [out] The ciphertext (same size as the pt)
-   @return CRYPT_OK if successful
-*/
-int ocb3_encrypt(ocb3_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct)
-{
-   unsigned char tmp[MAXBLOCKSIZE];
-   int err, i, full_blocks;
-   unsigned char *pt_b, *ct_b;
-
-   LTC_ARGCHK(ocb != NULL);
-   if (ptlen == 0) return CRYPT_OK; /* no data, nothing to do */
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (ocb->block_len != cipher_descriptor[ocb->cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (ptlen % ocb->block_len) { /* ptlen has to bu multiple of block_len */
-      return CRYPT_INVALID_ARG;
-   }
-
-   full_blocks = ptlen/ocb->block_len;
-   for(i=0; i<full_blocks; i++) {
-     pt_b = (unsigned char *)pt+i*ocb->block_len;
-     ct_b = (unsigned char *)ct+i*ocb->block_len;
-
-     /* ocb->Offset_current[] = ocb->Offset_current[] ^ Offset_{ntz(block_index)} */
-     ocb3_int_xor_blocks(ocb->Offset_current, ocb->Offset_current, ocb->L_[ocb3_int_ntz(ocb->block_index)], ocb->block_len);
-
-     /* tmp[] = pt[] XOR ocb->Offset_current[] */
-     ocb3_int_xor_blocks(tmp, pt_b, ocb->Offset_current, ocb->block_len);
-
-     /* encrypt */
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) {
-        goto LBL_ERR;
-     }
-
-     /* ct[] = tmp[] XOR ocb->Offset_current[] */
-     ocb3_int_xor_blocks(ct_b, tmp, ocb->Offset_current, ocb->block_len);
-
-     /* ocb->checksum[] = ocb->checksum[] XOR pt[] */
-     ocb3_int_xor_blocks(ocb->checksum, ocb->checksum, pt_b, ocb->block_len);
-
-     ocb->block_index++;
-   }
-
-   err = CRYPT_OK;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(tmp, sizeof(tmp));
-#endif
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt_authenticate_memory.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt_authenticate_memory.c
deleted file mode 100644
index 46eb9345bd1d..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt_authenticate_memory.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-  @file ocb3_encrypt_authenticate_memory.c
-  OCB implementation, encrypt block of memory, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Encrypt and generate an authentication code for a buffer of memory
-   @param cipher     The index of the cipher desired
-   @param key        The secret key
-   @param keylen     The length of the secret key (octets)
-   @param nonce      The session nonce (length of the block ciphers block size)
-   @param noncelen   The length of the nonce (octets)
-   @param adata      The AAD - additional associated data
-   @param adatalen   The length of AAD (octets)
-   @param pt         The plaintext
-   @param ptlen      The length of the plaintext (octets)
-   @param ct         [out] The ciphertext
-   @param tag        [out] The authentication tag
-   @param taglen     [in/out] The max size and resulting size of the authentication tag
-   @return CRYPT_OK if successful
-*/
-int ocb3_encrypt_authenticate_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *adata,  unsigned long adatalen,
-    const unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen)
-{
-   int err;
-   ocb3_state *ocb;
-
-   LTC_ARGCHK(taglen != NULL);
-
-   /* allocate memory */
-   ocb = XMALLOC(sizeof(ocb3_state));
-   if (ocb == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = ocb3_init(ocb, cipher, key, keylen, nonce, noncelen, *taglen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   if (adata != NULL || adatalen != 0) {
-      if ((err = ocb3_add_aad(ocb, adata, adatalen)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-   }
-
-   if ((err = ocb3_encrypt_last(ocb, pt, ptlen, ct)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   err = ocb3_done(ocb, tag, taglen);
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(ocb, sizeof(ocb3_state));
-#endif
-
-   XFREE(ocb);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt_last.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt_last.c
deleted file mode 100644
index dd3be718a1c0..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_encrypt_last.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_encrypt_last.c
-   OCB implementation, internal helper, by Karel Miko
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Finish an OCB (encryption) stream
-   @param ocb    The OCB state
-   @param pt     The remaining plaintext
-   @param ptlen  The length of the plaintext (octets)
-   @param ct     [out] The output buffer
-   @return CRYPT_OK if successful
-*/
-int ocb3_encrypt_last(ocb3_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct)
-{
-   unsigned char iOffset_star[MAXBLOCKSIZE];
-   unsigned char iPad[MAXBLOCKSIZE];
-   int err, x, full_blocks, full_blocks_len, last_block_len;
-
-   LTC_ARGCHK(ocb != NULL);
-   if (pt == NULL) LTC_ARGCHK(ptlen == 0);
-   if (ptlen != 0) {
-      LTC_ARGCHK(pt    != NULL);
-      LTC_ARGCHK(ct    != NULL);
-   }
-
-   if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   full_blocks = ptlen/ocb->block_len;
-   full_blocks_len = full_blocks * ocb->block_len;
-   last_block_len = ptlen - full_blocks_len;
-
-   /* process full blocks first */
-   if (full_blocks>0) {
-     if ((err = ocb3_encrypt(ocb, pt, full_blocks_len, ct)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-   }
-
-   /* at this point: m = ocb->block_index (last block index), Offset_m = ocb->Offset_current */
-
-   if (last_block_len>0) {
-     /* Offset_* = Offset_m xor L_* */
-     ocb3_int_xor_blocks(iOffset_star, ocb->Offset_current, ocb->L_star, ocb->block_len);
-
-     /* Pad = ENCIPHER(K, Offset_*) */
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(iOffset_star, iPad, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-
-     /* C_* = P_* xor Pad[1..bitlen(P_*)] */
-     ocb3_int_xor_blocks(ct+full_blocks_len, pt+full_blocks_len, iPad, last_block_len);
-
-     /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */
-     ocb3_int_xor_blocks(ocb->checksum, ocb->checksum, pt+full_blocks_len, last_block_len);
-     for(x=last_block_len; x<ocb->block_len; x++) {
-       if (x == last_block_len)
-         ocb->checksum[x] ^= 0x80;
-       else
-         ocb->checksum[x] ^= 0x00;
-     }
-
-     /* Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A) */
-     /* at this point we calculate only: Tag_part = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) */
-     for(x=0; x<ocb->block_len; x++) {
-       ocb->tag_part[x] = (ocb->checksum[x] ^ iOffset_star[x]) ^ ocb->L_dollar[x];
-     }
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->tag_part, ocb->tag_part, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-   }
-   else {
-     /* Tag = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) xor HASH(K,A) */
-     /* at this point we calculate only: Tag_part = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) */
-     for(x=0; x<ocb->block_len; x++) {
-       ocb->tag_part[x] = (ocb->checksum[x] ^ ocb->Offset_current[x]) ^ ocb->L_dollar[x];
-     }
-     if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->tag_part, ocb->tag_part, &ocb->key)) != CRYPT_OK) {
-       goto LBL_ERR;
-     }
-   }
-
-   err = CRYPT_OK;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(iOffset_star, MAXBLOCKSIZE);
-   zeromem(iPad, MAXBLOCKSIZE);
-#endif
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_init.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_init.c
deleted file mode 100644
index 7b8c097fb791..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_init.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_init.c
-   OCB implementation, initialize state, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-static void _ocb3_int_calc_offset_zero(ocb3_state *ocb, const unsigned char *nonce, unsigned long noncelen, unsigned long taglen)
-{
-   int x, y, bottom;
-   int idx, shift;
-   unsigned char iNonce[MAXBLOCKSIZE];
-   unsigned char iKtop[MAXBLOCKSIZE];
-   unsigned char iStretch[MAXBLOCKSIZE+8];
-
-   /* Nonce = zeros(127-bitlen(N)) || 1 || N          */
-   zeromem(iNonce, sizeof(iNonce));
-   for (x = ocb->block_len-1, y=0; y<(int)noncelen; x--, y++) {
-     iNonce[x] = nonce[noncelen-y-1];
-   }
-   iNonce[x] = 0x01;
-   iNonce[0] |= ((taglen*8) % 128) << 1;
-
-   /* bottom = str2num(Nonce[123..128])               */
-   bottom = iNonce[ocb->block_len-1] & 0x3F;
-
-   /* Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6))   */
-   iNonce[ocb->block_len-1] = iNonce[ocb->block_len-1] & 0xC0;
-   if ((cipher_descriptor[ocb->cipher].ecb_encrypt(iNonce, iKtop, &ocb->key)) != CRYPT_OK) {
-      zeromem(ocb->Offset_current, ocb->block_len);
-      return;
-   }
-
-   /* Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72]) */
-   for (x = 0; x < ocb->block_len; x++) {
-     iStretch[x] = iKtop[x];
-   }
-   for (y = 0; y < 8; y++) {
-     iStretch[x+y] = iKtop[y] ^ iKtop[y+1];
-   }
-
-   /* Offset_0 = Stretch[1+bottom..128+bottom]        */
-   idx = bottom / 8;
-   shift = (bottom % 8);
-   for (x = 0; x < ocb->block_len; x++) {
-      ocb->Offset_current[x] = iStretch[idx+x] << shift;
-      if (shift > 0) {
-        ocb->Offset_current[x] |= iStretch[idx+x+1] >> (8-shift);
-      }
-   }
-}
-
-static const struct {
-    int           len;
-    unsigned char poly_mul[MAXBLOCKSIZE];
-} polys[] = {
-{
-    8,
-    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B }
-}, {
-    16,
-    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 }
-}
-};
-
-/**
-   Initialize an OCB context
-   @param ocb       [out] The destination of the OCB state
-   @param cipher    The index of the desired cipher
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param nonce     The session nonce
-   @param noncelen  The length of the session nonce (octets, up to 15)
-   @param taglen    The length of the tag (octets, up to 16)
-   @return CRYPT_OK if successful
-*/
-int ocb3_init(ocb3_state *ocb, int cipher,
-             const unsigned char *key, unsigned long keylen,
-             const unsigned char *nonce, unsigned long noncelen,
-             unsigned long taglen)
-{
-   int poly, x, y, m, err;
-   unsigned char *previous, *current;
-
-   LTC_ARGCHK(ocb   != NULL);
-   LTC_ARGCHK(key   != NULL);
-   LTC_ARGCHK(nonce != NULL);
-
-   /* valid cipher? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   ocb->cipher = cipher;
-
-   /* Valid Nonce?
-    * As of RFC7253: "string of no more than 120 bits" */
-   if (noncelen > (120/8)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* The blockcipher must have a 128-bit blocksize */
-   if (cipher_descriptor[cipher].block_length != 16) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* The TAGLEN may be any value up to 128 (bits) */
-   if (taglen > 16) {
-      return CRYPT_INVALID_ARG;
-   }
-   ocb->tag_len = taglen;
-
-   /* determine which polys to use */
-   ocb->block_len = cipher_descriptor[cipher].block_length;
-   x = (int)(sizeof(polys)/sizeof(polys[0]));
-   for (poly = 0; poly < x; poly++) {
-       if (polys[poly].len == ocb->block_len) {
-          break;
-       }
-   }
-   if (poly == x) {
-      return CRYPT_INVALID_ARG; /* block_len not found in polys */
-   }
-   if (polys[poly].len != ocb->block_len) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* schedule the key */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* L_* = ENCIPHER(K, zeros(128)) */
-   zeromem(ocb->L_star, ocb->block_len);
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(ocb->L_star, ocb->L_star, &ocb->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* compute L_$, L_0, L_1, ... */
-   for (x = -1; x < 32; x++) {
-      if (x == -1) {                /* gonna compute: L_$ = double(L_*) */
-         current  = ocb->L_dollar;
-         previous = ocb->L_star;
-      }
-      else if (x == 0) {            /* gonna compute: L_0 = double(L_$) */
-         current  = ocb->L_[0];
-         previous = ocb->L_dollar;
-      }
-      else {                        /* gonna compute: L_i = double(L_{i-1}) for every integer i > 0 */
-         current  = ocb->L_[x];
-         previous = ocb->L_[x-1];
-      }
-      m = previous[0] >> 7;
-      for (y = 0; y < ocb->block_len-1; y++) {
-         current[y] = ((previous[y] << 1) | (previous[y+1] >> 7)) & 255;
-      }
-      current[ocb->block_len-1] = (previous[ocb->block_len-1] << 1) & 255;
-      if (m == 1) {
-         /* current[] = current[] XOR polys[poly].poly_mul[]*/
-         ocb3_int_xor_blocks(current, current, polys[poly].poly_mul, ocb->block_len);
-      }
-   }
-
-   /* initialize ocb->Offset_current = Offset_0 */
-   _ocb3_int_calc_offset_zero(ocb, nonce, noncelen, taglen);
-
-   /* initialize checksum to all zeros */
-   zeromem(ocb->checksum, ocb->block_len);
-
-   /* set block index */
-   ocb->block_index = 1;
-
-   /* initialize AAD related stuff */
-   ocb->ablock_index = 1;
-   ocb->adata_buffer_bytes = 0;
-   zeromem(ocb->aOffset_current, ocb->block_len);
-   zeromem(ocb->aSum_current, ocb->block_len);
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_int_ntz.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_int_ntz.c
deleted file mode 100644
index 1f0ed3126fc1..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_int_ntz.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_int_ntz.c
-   OCB implementation, INTERNAL ONLY helper, by Tom St Denis
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Returns the number of leading zero bits [from lsb up] (internal function)
-   @param x  The 32-bit value to observe
-   @return The number of bits [from the lsb up] that are zero
-*/
-int ocb3_int_ntz(unsigned long x)
-{
-   int c;
-   x &= 0xFFFFFFFFUL;
-   c = 0;
-   while ((x & 1) == 0) {
-      ++c;
-      x >>= 1;
-   }
-   return c;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/encauth/ocb3/ocb3_int_xor_blocks.c b/libs/tomcrypt/src/encauth/ocb3/ocb3_int_xor_blocks.c
deleted file mode 100644
index 0c4afd01156c..000000000000
--- a/libs/tomcrypt/src/encauth/ocb3/ocb3_int_xor_blocks.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/**
-   @file ocb3_int_xor_blocks.c
-   OCB implementation, INTERNAL ONLY helper, by Karel Miko
-*/
-#include "tomcrypt.h"
-
-#ifdef LTC_OCB3_MODE
-
-/**
-   Compute xor for two blocks of bytes 'out = block_a XOR block_b' (internal function)
-   @param out        The block of bytes (output)
-   @param block_a    The block of bytes (input)
-   @param block_b    The block of bytes (input)
-   @param block_len  The size of block_a, block_b, out
-*/
-void ocb3_int_xor_blocks(unsigned char *out, const unsigned char *block_a, const unsigned char *block_b, unsigned long block_len)
-{
-   int x;
-   if (out == block_a) {
-     for (x = 0; x < (int)block_len; x++) out[x] ^= block_b[x];
-   }
-   else {
-     for (x = 0; x < (int)block_len; x++) out[x] = block_a[x] ^ block_b[x];
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/md2.c b/libs/tomcrypt/src/hashes/md2.c
deleted file mode 100644
index ef230813bb5f..000000000000
--- a/libs/tomcrypt/src/hashes/md2.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @param md2.c
-   LTC_MD2 (RFC 1319) hash function implementation by Tom St Denis
-*/
-
-#ifdef LTC_MD2
-
-const struct ltc_hash_descriptor md2_desc =
-{
-    "md2",
-    7,
-    16,
-    16,
-
-    /* OID */
-   { 1, 2, 840, 113549, 2, 2,  },
-   6,
-
-    &md2_init,
-    &md2_process,
-    &md2_done,
-    &md2_test,
-    NULL
-};
-
-static const unsigned char PI_SUBST[256] = {
-  41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
-  19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
-  76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
-  138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
-  245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
-  148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
-  39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
-  181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
-  150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
-  112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
-  96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
-  85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
-  234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
-  129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
-  8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
-  203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
-  166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
-  31, 26, 219, 153, 141, 51, 159, 17, 131, 20
-};
-
-/* adds 16 bytes to the checksum */
-static void md2_update_chksum(hash_state *md)
-{
-   int j;
-   unsigned char L;
-   L = md->md2.chksum[15];
-   for (j = 0; j < 16; j++) {
-
-/* caution, the RFC says its "C[j] = S[M[i*16+j] xor L]" but the reference source code [and test vectors] say
-   otherwise.
-*/
-       L = (md->md2.chksum[j] ^= PI_SUBST[(int)(md->md2.buf[j] ^ L)] & 255);
-   }
-}
-
-static void md2_compress(hash_state *md)
-{
-   int j, k;
-   unsigned char t;
-
-   /* copy block */
-   for (j = 0; j < 16; j++) {
-       md->md2.X[16+j] = md->md2.buf[j];
-       md->md2.X[32+j] = md->md2.X[j] ^ md->md2.X[16+j];
-   }
-
-   t = (unsigned char)0;
-
-   /* do 18 rounds */
-   for (j = 0; j < 18; j++) {
-       for (k = 0; k < 48; k++) {
-           t = (md->md2.X[k] ^= PI_SUBST[(int)(t & 255)]);
-       }
-       t = (t + (unsigned char)j) & 255;
-   }
-}
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int md2_init(hash_state *md)
-{
-   LTC_ARGCHK(md != NULL);
-
-   /* LTC_MD2 uses a zero'ed state... */
-   zeromem(md->md2.X, sizeof(md->md2.X));
-   zeromem(md->md2.chksum, sizeof(md->md2.chksum));
-   zeromem(md->md2.buf, sizeof(md->md2.buf));
-   md->md2.curlen = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-int md2_process(hash_state *md, const unsigned char *in, unsigned long inlen)
-{
-    unsigned long n;
-    LTC_ARGCHK(md != NULL);
-    LTC_ARGCHK(in != NULL);
-    if (md-> md2 .curlen > sizeof(md-> md2 .buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-    while (inlen > 0) {
-        n = MIN(inlen, (16 - md->md2.curlen));
-        XMEMCPY(md->md2.buf + md->md2.curlen, in, (size_t)n);
-        md->md2.curlen += n;
-        in             += n;
-        inlen          -= n;
-
-        /* is 16 bytes full? */
-        if (md->md2.curlen == 16) {
-            md2_compress(md);
-            md2_update_chksum(md);
-            md->md2.curlen = 0;
-        }
-    }
-    return CRYPT_OK;
-}
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (16 bytes)
-   @return CRYPT_OK if successful
-*/
-int md2_done(hash_state * md, unsigned char *out)
-{
-    unsigned long i, k;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->md2.curlen >= sizeof(md->md2.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* pad the message */
-    k = 16 - md->md2.curlen;
-    for (i = md->md2.curlen; i < 16; i++) {
-        md->md2.buf[i] = (unsigned char)k;
-    }
-
-    /* hash and update */
-    md2_compress(md);
-    md2_update_chksum(md);
-
-    /* hash checksum */
-    XMEMCPY(md->md2.buf, md->md2.chksum, 16);
-    md2_compress(md);
-
-    /* output is lower 16 bytes of X */
-    XMEMCPY(out, md->md2.X, 16);
-
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int md2_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-   static const struct {
-        const char *msg;
-        unsigned char hash[16];
-   } tests[] = {
-      { "",
-        {0x83,0x50,0xe5,0xa3,0xe2,0x4c,0x15,0x3d,
-         0xf2,0x27,0x5c,0x9f,0x80,0x69,0x27,0x73
-        }
-      },
-      { "a",
-        {0x32,0xec,0x01,0xec,0x4a,0x6d,0xac,0x72,
-         0xc0,0xab,0x96,0xfb,0x34,0xc0,0xb5,0xd1
-        }
-      },
-      { "message digest",
-        {0xab,0x4f,0x49,0x6b,0xfb,0x2a,0x53,0x0b,
-         0x21,0x9f,0xf3,0x30,0x31,0xfe,0x06,0xb0
-        }
-      },
-      { "abcdefghijklmnopqrstuvwxyz",
-        {0x4e,0x8d,0xdf,0xf3,0x65,0x02,0x92,0xab,
-         0x5a,0x41,0x08,0xc3,0xaa,0x47,0x94,0x0b
-        }
-      },
-      { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-        {0xda,0x33,0xde,0xf2,0xa4,0x2d,0xf1,0x39,
-         0x75,0x35,0x28,0x46,0xc3,0x03,0x38,0xcd
-        }
-      },
-      { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
-        {0xd5,0x97,0x6f,0x79,0xd8,0x3d,0x3a,0x0d,
-         0xc9,0x80,0x6c,0x3c,0x66,0xf3,0xef,0xd8
-        }
-      }
-   };
-
-   int i;
-   unsigned char tmp[16];
-   hash_state md;
-
-   for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-       md2_init(&md);
-       md2_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-       md2_done(&md, tmp);
-       if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "MD2", i)) {
-          return CRYPT_FAIL_TESTVECTOR;
-       }
-   }
-   return CRYPT_OK;
-  #endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/md4.c b/libs/tomcrypt/src/hashes/md4.c
deleted file mode 100644
index cf25e6bd303d..000000000000
--- a/libs/tomcrypt/src/hashes/md4.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @param md4.c
-   Submitted by Dobes Vandermeer  (dobes@smartt.com)
-*/
-
-#ifdef LTC_MD4
-
-const struct ltc_hash_descriptor md4_desc =
-{
-    "md4",
-    6,
-    16,
-    64,
-
-    /* OID */
-   { 1, 2, 840, 113549, 2, 4,  },
-   6,
-
-    &md4_init,
-    &md4_process,
-    &md4_done,
-    &md4_test,
-    NULL
-};
-
-#define S11 3
-#define S12 7
-#define S13 11
-#define S14 19
-#define S21 3
-#define S22 5
-#define S23 9
-#define S24 13
-#define S31 3
-#define S32 9
-#define S33 11
-#define S34 15
-
-/* F, G and H are basic LTC_MD4 functions. */
-#define F(x, y, z) (z ^ (x & (y ^ z)))
-#define G(x, y, z) ((x & y) | (z & (x | y)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-
-/* ROTATE_LEFT rotates x left n bits. */
-#define ROTATE_LEFT(x, n) ROLc(x, n)
-
-/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
-/* Rotation is separate from addition to prevent recomputation */
-
-#define FF(a, b, c, d, x, s) { \
-    (a) += F ((b), (c), (d)) + (x); \
-    (a) = ROTATE_LEFT ((a), (s)); \
-  }
-#define GG(a, b, c, d, x, s) { \
-    (a) += G ((b), (c), (d)) + (x) + 0x5a827999UL; \
-    (a) = ROTATE_LEFT ((a), (s)); \
-  }
-#define HH(a, b, c, d, x, s) { \
-    (a) += H ((b), (c), (d)) + (x) + 0x6ed9eba1UL; \
-    (a) = ROTATE_LEFT ((a), (s)); \
-  }
-
-#ifdef LTC_CLEAN_STACK
-static int _md4_compress(hash_state *md, unsigned char *buf)
-#else
-static int  md4_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-    ulong32 x[16], a, b, c, d;
-    int i;
-
-    /* copy state */
-    a = md->md4.state[0];
-    b = md->md4.state[1];
-    c = md->md4.state[2];
-    d = md->md4.state[3];
-
-    /* copy the state into 512-bits into W[0..15] */
-    for (i = 0; i < 16; i++) {
-        LOAD32L(x[i], buf + (4*i));
-    }
-
-    /* Round 1 */
-    FF (a, b, c, d, x[ 0], S11); /* 1 */
-    FF (d, a, b, c, x[ 1], S12); /* 2 */
-    FF (c, d, a, b, x[ 2], S13); /* 3 */
-    FF (b, c, d, a, x[ 3], S14); /* 4 */
-    FF (a, b, c, d, x[ 4], S11); /* 5 */
-    FF (d, a, b, c, x[ 5], S12); /* 6 */
-    FF (c, d, a, b, x[ 6], S13); /* 7 */
-    FF (b, c, d, a, x[ 7], S14); /* 8 */
-    FF (a, b, c, d, x[ 8], S11); /* 9 */
-    FF (d, a, b, c, x[ 9], S12); /* 10 */
-    FF (c, d, a, b, x[10], S13); /* 11 */
-    FF (b, c, d, a, x[11], S14); /* 12 */
-    FF (a, b, c, d, x[12], S11); /* 13 */
-    FF (d, a, b, c, x[13], S12); /* 14 */
-    FF (c, d, a, b, x[14], S13); /* 15 */
-    FF (b, c, d, a, x[15], S14); /* 16 */
-
-    /* Round 2 */
-    GG (a, b, c, d, x[ 0], S21); /* 17 */
-    GG (d, a, b, c, x[ 4], S22); /* 18 */
-    GG (c, d, a, b, x[ 8], S23); /* 19 */
-    GG (b, c, d, a, x[12], S24); /* 20 */
-    GG (a, b, c, d, x[ 1], S21); /* 21 */
-    GG (d, a, b, c, x[ 5], S22); /* 22 */
-    GG (c, d, a, b, x[ 9], S23); /* 23 */
-    GG (b, c, d, a, x[13], S24); /* 24 */
-    GG (a, b, c, d, x[ 2], S21); /* 25 */
-    GG (d, a, b, c, x[ 6], S22); /* 26 */
-    GG (c, d, a, b, x[10], S23); /* 27 */
-    GG (b, c, d, a, x[14], S24); /* 28 */
-    GG (a, b, c, d, x[ 3], S21); /* 29 */
-    GG (d, a, b, c, x[ 7], S22); /* 30 */
-    GG (c, d, a, b, x[11], S23); /* 31 */
-    GG (b, c, d, a, x[15], S24); /* 32 */
-
-    /* Round 3 */
-    HH (a, b, c, d, x[ 0], S31); /* 33 */
-    HH (d, a, b, c, x[ 8], S32); /* 34 */
-    HH (c, d, a, b, x[ 4], S33); /* 35 */
-    HH (b, c, d, a, x[12], S34); /* 36 */
-    HH (a, b, c, d, x[ 2], S31); /* 37 */
-    HH (d, a, b, c, x[10], S32); /* 38 */
-    HH (c, d, a, b, x[ 6], S33); /* 39 */
-    HH (b, c, d, a, x[14], S34); /* 40 */
-    HH (a, b, c, d, x[ 1], S31); /* 41 */
-    HH (d, a, b, c, x[ 9], S32); /* 42 */
-    HH (c, d, a, b, x[ 5], S33); /* 43 */
-    HH (b, c, d, a, x[13], S34); /* 44 */
-    HH (a, b, c, d, x[ 3], S31); /* 45 */
-    HH (d, a, b, c, x[11], S32); /* 46 */
-    HH (c, d, a, b, x[ 7], S33); /* 47 */
-    HH (b, c, d, a, x[15], S34); /* 48 */
-
-
-    /* Update our state */
-    md->md4.state[0] = md->md4.state[0] + a;
-    md->md4.state[1] = md->md4.state[1] + b;
-    md->md4.state[2] = md->md4.state[2] + c;
-    md->md4.state[3] = md->md4.state[3] + d;
-
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int md4_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _md4_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 20 + sizeof(int));
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int md4_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->md4.state[0] = 0x67452301UL;
-   md->md4.state[1] = 0xefcdab89UL;
-   md->md4.state[2] = 0x98badcfeUL;
-   md->md4.state[3] = 0x10325476UL;
-   md->md4.length  = 0;
-   md->md4.curlen  = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(md4_process, md4_compress, md4, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (16 bytes)
-   @return CRYPT_OK if successful
-*/
-int md4_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->md4.curlen >= sizeof(md->md4.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-    /* increase the length of the message */
-    md->md4.length += md->md4.curlen * 8;
-
-    /* append the '1' bit */
-    md->md4.buf[md->md4.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->md4.curlen > 56) {
-        while (md->md4.curlen < 64) {
-            md->md4.buf[md->md4.curlen++] = (unsigned char)0;
-        }
-        md4_compress(md, md->md4.buf);
-        md->md4.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->md4.curlen < 56) {
-        md->md4.buf[md->md4.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64L(md->md4.length, md->md4.buf+56);
-    md4_compress(md, md->md4.buf);
-
-    /* copy output */
-    for (i = 0; i < 4; i++) {
-        STORE32L(md->md4.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int md4_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-    static const struct md4_test_case {
-        const char *input;
-        unsigned char hash[16];
-    } tests[] = {
-        { "",
-          {0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
-           0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0} },
-        { "a",
-          {0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46,
-           0x24, 0x5e, 0x05, 0xfb, 0xdb, 0xd6, 0xfb, 0x24} },
-        { "abc",
-          {0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52,
-           0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d} },
-        { "message digest",
-          {0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8,
-           0x18, 0x87, 0x48, 0x06, 0xe1, 0xc7, 0x01, 0x4b} },
-        { "abcdefghijklmnopqrstuvwxyz",
-          {0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd,
-           0xee, 0xa8, 0xed, 0x63, 0xdf, 0x41, 0x2d, 0xa9} },
-        { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-          {0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35,
-           0x1c, 0xe6, 0x27, 0xe1, 0x53, 0xe7, 0xf0, 0xe4} },
-        { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
-          {0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19,
-           0x9c, 0x3e, 0x7b, 0x16, 0x4f, 0xcc, 0x05, 0x36} },
-    };
-
-    int i;
-    unsigned char tmp[16];
-    hash_state md;
-
-    for(i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-        md4_init(&md);
-        md4_process(&md, (unsigned char *)tests[i].input, (unsigned long)strlen(tests[i].input));
-        md4_done(&md, tmp);
-        if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "MD4", i)) {
-           return CRYPT_FAIL_TESTVECTOR;
-        }
-
-    }
-    return CRYPT_OK;
-  #endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/md5.c b/libs/tomcrypt/src/hashes/md5.c
deleted file mode 100644
index 7910f7e57f83..000000000000
--- a/libs/tomcrypt/src/hashes/md5.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-
-/**
-  @file md5.c
-  LTC_MD5 hash function by Tom St Denis
-*/
-
-#ifdef LTC_MD5
-
-const struct ltc_hash_descriptor md5_desc =
-{
-    "md5",
-    3,
-    16,
-    64,
-
-    /* OID */
-   { 1, 2, 840, 113549, 2, 5,  },
-   6,
-
-    &md5_init,
-    &md5_process,
-    &md5_done,
-    &md5_test,
-    NULL
-};
-
-#define F(x,y,z)  (z ^ (x & (y ^ z)))
-#define G(x,y,z)  (y ^ (z & (y ^ x)))
-#define H(x,y,z)  (x^y^z)
-#define I(x,y,z)  (y^(x|(~z)))
-
-#ifdef LTC_SMALL_CODE
-
-#define FF(a,b,c,d,M,s,t) \
-    a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b;
-
-#define GG(a,b,c,d,M,s,t) \
-    a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b;
-
-#define HH(a,b,c,d,M,s,t) \
-    a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b;
-
-#define II(a,b,c,d,M,s,t) \
-    a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b;
-
-static const unsigned char Worder[64] = {
-   0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
-   1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
-   5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
-   0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
-};
-
-static const unsigned char Rorder[64] = {
-   7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
-   5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
-   4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
-   6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
-};
-
-static const ulong32 Korder[64] = {
-0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL,
-0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL,
-0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL,
-0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL,
-0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL,
-0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL,
-0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL,
-0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL
-};
-
-#else
-
-#define FF(a,b,c,d,M,s,t) \
-    a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-#define GG(a,b,c,d,M,s,t) \
-    a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-#define HH(a,b,c,d,M,s,t) \
-    a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-#define II(a,b,c,d,M,s,t) \
-    a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-
-#endif
-
-#ifdef LTC_CLEAN_STACK
-static int _md5_compress(hash_state *md, unsigned char *buf)
-#else
-static int  md5_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-    ulong32 i, W[16], a, b, c, d;
-#ifdef LTC_SMALL_CODE
-    ulong32 t;
-#endif
-
-    /* copy the state into 512-bits into W[0..15] */
-    for (i = 0; i < 16; i++) {
-        LOAD32L(W[i], buf + (4*i));
-    }
-
-    /* copy state */
-    a = md->md5.state[0];
-    b = md->md5.state[1];
-    c = md->md5.state[2];
-    d = md->md5.state[3];
-
-#ifdef LTC_SMALL_CODE
-    for (i = 0; i < 16; ++i) {
-        FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
-        t = d; d = c; c = b; b = a; a = t;
-    }
-
-    for (; i < 32; ++i) {
-        GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
-        t = d; d = c; c = b; b = a; a = t;
-    }
-
-    for (; i < 48; ++i) {
-        HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
-        t = d; d = c; c = b; b = a; a = t;
-    }
-
-    for (; i < 64; ++i) {
-        II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
-        t = d; d = c; c = b; b = a; a = t;
-    }
-
-#else
-    FF(a,b,c,d,W[0],7,0xd76aa478UL)
-    FF(d,a,b,c,W[1],12,0xe8c7b756UL)
-    FF(c,d,a,b,W[2],17,0x242070dbUL)
-    FF(b,c,d,a,W[3],22,0xc1bdceeeUL)
-    FF(a,b,c,d,W[4],7,0xf57c0fafUL)
-    FF(d,a,b,c,W[5],12,0x4787c62aUL)
-    FF(c,d,a,b,W[6],17,0xa8304613UL)
-    FF(b,c,d,a,W[7],22,0xfd469501UL)
-    FF(a,b,c,d,W[8],7,0x698098d8UL)
-    FF(d,a,b,c,W[9],12,0x8b44f7afUL)
-    FF(c,d,a,b,W[10],17,0xffff5bb1UL)
-    FF(b,c,d,a,W[11],22,0x895cd7beUL)
-    FF(a,b,c,d,W[12],7,0x6b901122UL)
-    FF(d,a,b,c,W[13],12,0xfd987193UL)
-    FF(c,d,a,b,W[14],17,0xa679438eUL)
-    FF(b,c,d,a,W[15],22,0x49b40821UL)
-    GG(a,b,c,d,W[1],5,0xf61e2562UL)
-    GG(d,a,b,c,W[6],9,0xc040b340UL)
-    GG(c,d,a,b,W[11],14,0x265e5a51UL)
-    GG(b,c,d,a,W[0],20,0xe9b6c7aaUL)
-    GG(a,b,c,d,W[5],5,0xd62f105dUL)
-    GG(d,a,b,c,W[10],9,0x02441453UL)
-    GG(c,d,a,b,W[15],14,0xd8a1e681UL)
-    GG(b,c,d,a,W[4],20,0xe7d3fbc8UL)
-    GG(a,b,c,d,W[9],5,0x21e1cde6UL)
-    GG(d,a,b,c,W[14],9,0xc33707d6UL)
-    GG(c,d,a,b,W[3],14,0xf4d50d87UL)
-    GG(b,c,d,a,W[8],20,0x455a14edUL)
-    GG(a,b,c,d,W[13],5,0xa9e3e905UL)
-    GG(d,a,b,c,W[2],9,0xfcefa3f8UL)
-    GG(c,d,a,b,W[7],14,0x676f02d9UL)
-    GG(b,c,d,a,W[12],20,0x8d2a4c8aUL)
-    HH(a,b,c,d,W[5],4,0xfffa3942UL)
-    HH(d,a,b,c,W[8],11,0x8771f681UL)
-    HH(c,d,a,b,W[11],16,0x6d9d6122UL)
-    HH(b,c,d,a,W[14],23,0xfde5380cUL)
-    HH(a,b,c,d,W[1],4,0xa4beea44UL)
-    HH(d,a,b,c,W[4],11,0x4bdecfa9UL)
-    HH(c,d,a,b,W[7],16,0xf6bb4b60UL)
-    HH(b,c,d,a,W[10],23,0xbebfbc70UL)
-    HH(a,b,c,d,W[13],4,0x289b7ec6UL)
-    HH(d,a,b,c,W[0],11,0xeaa127faUL)
-    HH(c,d,a,b,W[3],16,0xd4ef3085UL)
-    HH(b,c,d,a,W[6],23,0x04881d05UL)
-    HH(a,b,c,d,W[9],4,0xd9d4d039UL)
-    HH(d,a,b,c,W[12],11,0xe6db99e5UL)
-    HH(c,d,a,b,W[15],16,0x1fa27cf8UL)
-    HH(b,c,d,a,W[2],23,0xc4ac5665UL)
-    II(a,b,c,d,W[0],6,0xf4292244UL)
-    II(d,a,b,c,W[7],10,0x432aff97UL)
-    II(c,d,a,b,W[14],15,0xab9423a7UL)
-    II(b,c,d,a,W[5],21,0xfc93a039UL)
-    II(a,b,c,d,W[12],6,0x655b59c3UL)
-    II(d,a,b,c,W[3],10,0x8f0ccc92UL)
-    II(c,d,a,b,W[10],15,0xffeff47dUL)
-    II(b,c,d,a,W[1],21,0x85845dd1UL)
-    II(a,b,c,d,W[8],6,0x6fa87e4fUL)
-    II(d,a,b,c,W[15],10,0xfe2ce6e0UL)
-    II(c,d,a,b,W[6],15,0xa3014314UL)
-    II(b,c,d,a,W[13],21,0x4e0811a1UL)
-    II(a,b,c,d,W[4],6,0xf7537e82UL)
-    II(d,a,b,c,W[11],10,0xbd3af235UL)
-    II(c,d,a,b,W[2],15,0x2ad7d2bbUL)
-    II(b,c,d,a,W[9],21,0xeb86d391UL)
-#endif
-
-    md->md5.state[0] = md->md5.state[0] + a;
-    md->md5.state[1] = md->md5.state[1] + b;
-    md->md5.state[2] = md->md5.state[2] + c;
-    md->md5.state[3] = md->md5.state[3] + d;
-
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int md5_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _md5_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 21);
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int md5_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->md5.state[0] = 0x67452301UL;
-   md->md5.state[1] = 0xefcdab89UL;
-   md->md5.state[2] = 0x98badcfeUL;
-   md->md5.state[3] = 0x10325476UL;
-   md->md5.curlen = 0;
-   md->md5.length = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(md5_process, md5_compress, md5, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (16 bytes)
-   @return CRYPT_OK if successful
-*/
-int md5_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->md5.curlen >= sizeof(md->md5.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* increase the length of the message */
-    md->md5.length += md->md5.curlen * 8;
-
-    /* append the '1' bit */
-    md->md5.buf[md->md5.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->md5.curlen > 56) {
-        while (md->md5.curlen < 64) {
-            md->md5.buf[md->md5.curlen++] = (unsigned char)0;
-        }
-        md5_compress(md, md->md5.buf);
-        md->md5.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->md5.curlen < 56) {
-        md->md5.buf[md->md5.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64L(md->md5.length, md->md5.buf+56);
-    md5_compress(md, md->md5.buf);
-
-    /* copy output */
-    for (i = 0; i < 4; i++) {
-        STORE32L(md->md5.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  md5_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[16];
-  } tests[] = {
-    { "",
-      { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
-        0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } },
-    { "a",
-      {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
-       0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } },
-    { "abc",
-      { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
-        0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } },
-    { "message digest",
-      { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
-        0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } },
-    { "abcdefghijklmnopqrstuvwxyz",
-      { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
-        0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } },
-    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-      { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
-        0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } },
-    { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
-      { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
-        0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } },
-    { NULL, { 0 } }
-  };
-
-  int i;
-  unsigned char tmp[16];
-  hash_state md;
-
-  for (i = 0; tests[i].msg != NULL; i++) {
-      md5_init(&md);
-      md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      md5_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "MD5", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/rmd128.c b/libs/tomcrypt/src/hashes/rmd128.c
deleted file mode 100644
index 3ec652dc9a7c..000000000000
--- a/libs/tomcrypt/src/hashes/rmd128.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @param rmd128.c
-   RMD128 Hash function
-*/
-
-/* Implementation of LTC_RIPEMD-128 based on the source by Antoon Bosselaers, ESAT-COSIC
- *
- * This source has been radically overhauled to be portable and work within
- * the LibTomCrypt API by Tom St Denis
- */
-
-#ifdef LTC_RIPEMD128
-
-const struct ltc_hash_descriptor rmd128_desc =
-{
-    "rmd128",
-    8,
-    16,
-    64,
-
-    /* OID */
-   { 1, 0, 10118, 3, 0, 50 },
-   6,
-
-    &rmd128_init,
-    &rmd128_process,
-    &rmd128_done,
-    &rmd128_test,
-    NULL
-};
-
-/* the four basic functions F(), G() and H() */
-#define F(x, y, z)        ((x) ^ (y) ^ (z))
-#define G(x, y, z)        (((x) & (y)) | (~(x) & (z)))
-#define H(x, y, z)        (((x) | ~(y)) ^ (z))
-#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z)))
-
-/* the eight basic operations FF() through III() */
-#define FF(a, b, c, d, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s));
-
-#define GG(a, b, c, d, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
-      (a) = ROLc((a), (s));
-
-#define HH(a, b, c, d, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
-      (a) = ROLc((a), (s));
-
-#define II(a, b, c, d, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
-      (a) = ROLc((a), (s));
-
-#define FFF(a, b, c, d, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s));
-
-#define GGG(a, b, c, d, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x6d703ef3UL;\
-      (a) = ROLc((a), (s));
-
-#define HHH(a, b, c, d, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x5c4dd124UL;\
-      (a) = ROLc((a), (s));
-
-#define III(a, b, c, d, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x50a28be6UL;\
-      (a) = ROLc((a), (s));
-
-#ifdef LTC_CLEAN_STACK
-static int _rmd128_compress(hash_state *md, unsigned char *buf)
-#else
-static int  rmd128_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-   ulong32 aa,bb,cc,dd,aaa,bbb,ccc,ddd,X[16];
-   int i;
-
-   /* load words X */
-   for (i = 0; i < 16; i++){
-      LOAD32L(X[i], buf + (4 * i));
-   }
-
-   /* load state */
-   aa = aaa = md->rmd128.state[0];
-   bb = bbb = md->rmd128.state[1];
-   cc = ccc = md->rmd128.state[2];
-   dd = ddd = md->rmd128.state[3];
-
-   /* round 1 */
-   FF(aa, bb, cc, dd, X[ 0], 11);
-   FF(dd, aa, bb, cc, X[ 1], 14);
-   FF(cc, dd, aa, bb, X[ 2], 15);
-   FF(bb, cc, dd, aa, X[ 3], 12);
-   FF(aa, bb, cc, dd, X[ 4],  5);
-   FF(dd, aa, bb, cc, X[ 5],  8);
-   FF(cc, dd, aa, bb, X[ 6],  7);
-   FF(bb, cc, dd, aa, X[ 7],  9);
-   FF(aa, bb, cc, dd, X[ 8], 11);
-   FF(dd, aa, bb, cc, X[ 9], 13);
-   FF(cc, dd, aa, bb, X[10], 14);
-   FF(bb, cc, dd, aa, X[11], 15);
-   FF(aa, bb, cc, dd, X[12],  6);
-   FF(dd, aa, bb, cc, X[13],  7);
-   FF(cc, dd, aa, bb, X[14],  9);
-   FF(bb, cc, dd, aa, X[15],  8);
-
-   /* round 2 */
-   GG(aa, bb, cc, dd, X[ 7],  7);
-   GG(dd, aa, bb, cc, X[ 4],  6);
-   GG(cc, dd, aa, bb, X[13],  8);
-   GG(bb, cc, dd, aa, X[ 1], 13);
-   GG(aa, bb, cc, dd, X[10], 11);
-   GG(dd, aa, bb, cc, X[ 6],  9);
-   GG(cc, dd, aa, bb, X[15],  7);
-   GG(bb, cc, dd, aa, X[ 3], 15);
-   GG(aa, bb, cc, dd, X[12],  7);
-   GG(dd, aa, bb, cc, X[ 0], 12);
-   GG(cc, dd, aa, bb, X[ 9], 15);
-   GG(bb, cc, dd, aa, X[ 5],  9);
-   GG(aa, bb, cc, dd, X[ 2], 11);
-   GG(dd, aa, bb, cc, X[14],  7);
-   GG(cc, dd, aa, bb, X[11], 13);
-   GG(bb, cc, dd, aa, X[ 8], 12);
-
-   /* round 3 */
-   HH(aa, bb, cc, dd, X[ 3], 11);
-   HH(dd, aa, bb, cc, X[10], 13);
-   HH(cc, dd, aa, bb, X[14],  6);
-   HH(bb, cc, dd, aa, X[ 4],  7);
-   HH(aa, bb, cc, dd, X[ 9], 14);
-   HH(dd, aa, bb, cc, X[15],  9);
-   HH(cc, dd, aa, bb, X[ 8], 13);
-   HH(bb, cc, dd, aa, X[ 1], 15);
-   HH(aa, bb, cc, dd, X[ 2], 14);
-   HH(dd, aa, bb, cc, X[ 7],  8);
-   HH(cc, dd, aa, bb, X[ 0], 13);
-   HH(bb, cc, dd, aa, X[ 6],  6);
-   HH(aa, bb, cc, dd, X[13],  5);
-   HH(dd, aa, bb, cc, X[11], 12);
-   HH(cc, dd, aa, bb, X[ 5],  7);
-   HH(bb, cc, dd, aa, X[12],  5);
-
-   /* round 4 */
-   II(aa, bb, cc, dd, X[ 1], 11);
-   II(dd, aa, bb, cc, X[ 9], 12);
-   II(cc, dd, aa, bb, X[11], 14);
-   II(bb, cc, dd, aa, X[10], 15);
-   II(aa, bb, cc, dd, X[ 0], 14);
-   II(dd, aa, bb, cc, X[ 8], 15);
-   II(cc, dd, aa, bb, X[12],  9);
-   II(bb, cc, dd, aa, X[ 4],  8);
-   II(aa, bb, cc, dd, X[13],  9);
-   II(dd, aa, bb, cc, X[ 3], 14);
-   II(cc, dd, aa, bb, X[ 7],  5);
-   II(bb, cc, dd, aa, X[15],  6);
-   II(aa, bb, cc, dd, X[14],  8);
-   II(dd, aa, bb, cc, X[ 5],  6);
-   II(cc, dd, aa, bb, X[ 6],  5);
-   II(bb, cc, dd, aa, X[ 2], 12);
-
-   /* parallel round 1 */
-   III(aaa, bbb, ccc, ddd, X[ 5],  8);
-   III(ddd, aaa, bbb, ccc, X[14],  9);
-   III(ccc, ddd, aaa, bbb, X[ 7],  9);
-   III(bbb, ccc, ddd, aaa, X[ 0], 11);
-   III(aaa, bbb, ccc, ddd, X[ 9], 13);
-   III(ddd, aaa, bbb, ccc, X[ 2], 15);
-   III(ccc, ddd, aaa, bbb, X[11], 15);
-   III(bbb, ccc, ddd, aaa, X[ 4],  5);
-   III(aaa, bbb, ccc, ddd, X[13],  7);
-   III(ddd, aaa, bbb, ccc, X[ 6],  7);
-   III(ccc, ddd, aaa, bbb, X[15],  8);
-   III(bbb, ccc, ddd, aaa, X[ 8], 11);
-   III(aaa, bbb, ccc, ddd, X[ 1], 14);
-   III(ddd, aaa, bbb, ccc, X[10], 14);
-   III(ccc, ddd, aaa, bbb, X[ 3], 12);
-   III(bbb, ccc, ddd, aaa, X[12],  6);
-
-   /* parallel round 2 */
-   HHH(aaa, bbb, ccc, ddd, X[ 6],  9);
-   HHH(ddd, aaa, bbb, ccc, X[11], 13);
-   HHH(ccc, ddd, aaa, bbb, X[ 3], 15);
-   HHH(bbb, ccc, ddd, aaa, X[ 7],  7);
-   HHH(aaa, bbb, ccc, ddd, X[ 0], 12);
-   HHH(ddd, aaa, bbb, ccc, X[13],  8);
-   HHH(ccc, ddd, aaa, bbb, X[ 5],  9);
-   HHH(bbb, ccc, ddd, aaa, X[10], 11);
-   HHH(aaa, bbb, ccc, ddd, X[14],  7);
-   HHH(ddd, aaa, bbb, ccc, X[15],  7);
-   HHH(ccc, ddd, aaa, bbb, X[ 8], 12);
-   HHH(bbb, ccc, ddd, aaa, X[12],  7);
-   HHH(aaa, bbb, ccc, ddd, X[ 4],  6);
-   HHH(ddd, aaa, bbb, ccc, X[ 9], 15);
-   HHH(ccc, ddd, aaa, bbb, X[ 1], 13);
-   HHH(bbb, ccc, ddd, aaa, X[ 2], 11);
-
-   /* parallel round 3 */
-   GGG(aaa, bbb, ccc, ddd, X[15],  9);
-   GGG(ddd, aaa, bbb, ccc, X[ 5],  7);
-   GGG(ccc, ddd, aaa, bbb, X[ 1], 15);
-   GGG(bbb, ccc, ddd, aaa, X[ 3], 11);
-   GGG(aaa, bbb, ccc, ddd, X[ 7],  8);
-   GGG(ddd, aaa, bbb, ccc, X[14],  6);
-   GGG(ccc, ddd, aaa, bbb, X[ 6],  6);
-   GGG(bbb, ccc, ddd, aaa, X[ 9], 14);
-   GGG(aaa, bbb, ccc, ddd, X[11], 12);
-   GGG(ddd, aaa, bbb, ccc, X[ 8], 13);
-   GGG(ccc, ddd, aaa, bbb, X[12],  5);
-   GGG(bbb, ccc, ddd, aaa, X[ 2], 14);
-   GGG(aaa, bbb, ccc, ddd, X[10], 13);
-   GGG(ddd, aaa, bbb, ccc, X[ 0], 13);
-   GGG(ccc, ddd, aaa, bbb, X[ 4],  7);
-   GGG(bbb, ccc, ddd, aaa, X[13],  5);
-
-   /* parallel round 4 */
-   FFF(aaa, bbb, ccc, ddd, X[ 8], 15);
-   FFF(ddd, aaa, bbb, ccc, X[ 6],  5);
-   FFF(ccc, ddd, aaa, bbb, X[ 4],  8);
-   FFF(bbb, ccc, ddd, aaa, X[ 1], 11);
-   FFF(aaa, bbb, ccc, ddd, X[ 3], 14);
-   FFF(ddd, aaa, bbb, ccc, X[11], 14);
-   FFF(ccc, ddd, aaa, bbb, X[15],  6);
-   FFF(bbb, ccc, ddd, aaa, X[ 0], 14);
-   FFF(aaa, bbb, ccc, ddd, X[ 5],  6);
-   FFF(ddd, aaa, bbb, ccc, X[12],  9);
-   FFF(ccc, ddd, aaa, bbb, X[ 2], 12);
-   FFF(bbb, ccc, ddd, aaa, X[13],  9);
-   FFF(aaa, bbb, ccc, ddd, X[ 9], 12);
-   FFF(ddd, aaa, bbb, ccc, X[ 7],  5);
-   FFF(ccc, ddd, aaa, bbb, X[10], 15);
-   FFF(bbb, ccc, ddd, aaa, X[14],  8);
-
-   /* combine results */
-   ddd += cc + md->rmd128.state[1];               /* final result for MDbuf[0] */
-   md->rmd128.state[1] = md->rmd128.state[2] + dd + aaa;
-   md->rmd128.state[2] = md->rmd128.state[3] + aa + bbb;
-   md->rmd128.state[3] = md->rmd128.state[0] + bb + ccc;
-   md->rmd128.state[0] = ddd;
-
-   return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int rmd128_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _rmd128_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 24 + sizeof(int));
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int rmd128_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->rmd128.state[0] = 0x67452301UL;
-   md->rmd128.state[1] = 0xefcdab89UL;
-   md->rmd128.state[2] = 0x98badcfeUL;
-   md->rmd128.state[3] = 0x10325476UL;
-   md->rmd128.curlen   = 0;
-   md->rmd128.length   = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(rmd128_process, rmd128_compress, rmd128, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (16 bytes)
-   @return CRYPT_OK if successful
-*/
-int rmd128_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->rmd128.curlen >= sizeof(md->rmd128.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* increase the length of the message */
-    md->rmd128.length += md->rmd128.curlen * 8;
-
-    /* append the '1' bit */
-    md->rmd128.buf[md->rmd128.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->rmd128.curlen > 56) {
-        while (md->rmd128.curlen < 64) {
-            md->rmd128.buf[md->rmd128.curlen++] = (unsigned char)0;
-        }
-        rmd128_compress(md, md->rmd128.buf);
-        md->rmd128.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->rmd128.curlen < 56) {
-        md->rmd128.buf[md->rmd128.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64L(md->rmd128.length, md->rmd128.buf+56);
-    rmd128_compress(md, md->rmd128.buf);
-
-    /* copy output */
-    for (i = 0; i < 4; i++) {
-        STORE32L(md->rmd128.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-   return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int rmd128_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   static const struct {
-        const char *msg;
-        unsigned char hash[16];
-   } tests[] = {
-   { "",
-     { 0xcd, 0xf2, 0x62, 0x13, 0xa1, 0x50, 0xdc, 0x3e,
-       0xcb, 0x61, 0x0f, 0x18, 0xf6, 0xb3, 0x8b, 0x46 }
-   },
-   { "a",
-     { 0x86, 0xbe, 0x7a, 0xfa, 0x33, 0x9d, 0x0f, 0xc7,
-       0xcf, 0xc7, 0x85, 0xe7, 0x2f, 0x57, 0x8d, 0x33 }
-   },
-   { "abc",
-     { 0xc1, 0x4a, 0x12, 0x19, 0x9c, 0x66, 0xe4, 0xba,
-       0x84, 0x63, 0x6b, 0x0f, 0x69, 0x14, 0x4c, 0x77 }
-   },
-   { "message digest",
-     { 0x9e, 0x32, 0x7b, 0x3d, 0x6e, 0x52, 0x30, 0x62,
-       0xaf, 0xc1, 0x13, 0x2d, 0x7d, 0xf9, 0xd1, 0xb8 }
-   },
-   { "abcdefghijklmnopqrstuvwxyz",
-     { 0xfd, 0x2a, 0xa6, 0x07, 0xf7, 0x1d, 0xc8, 0xf5,
-       0x10, 0x71, 0x49, 0x22, 0xb3, 0x71, 0x83, 0x4e }
-   },
-   { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-     { 0xd1, 0xe9, 0x59, 0xeb, 0x17, 0x9c, 0x91, 0x1f,
-       0xae, 0xa4, 0x62, 0x4c, 0x60, 0xc5, 0xc7, 0x02 }
-   }
-   };
-
-   int i;
-   unsigned char tmp[16];
-   hash_state md;
-
-   for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
-       rmd128_init(&md);
-       rmd128_process(&md, (unsigned char *)tests[i].msg, strlen(tests[i].msg));
-       rmd128_done(&md, tmp);
-       if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "RIPEMD128", i)) {
-          return CRYPT_FAIL_TESTVECTOR;
-       }
-   }
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/rmd160.c b/libs/tomcrypt/src/hashes/rmd160.c
deleted file mode 100644
index e89ec925677a..000000000000
--- a/libs/tomcrypt/src/hashes/rmd160.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file rmd160.c
-   RMD160 hash function
-*/
-
-/* Implementation of LTC_RIPEMD-160 based on the source by Antoon Bosselaers, ESAT-COSIC
- *
- * This source has been radically overhauled to be portable and work within
- * the LibTomCrypt API by Tom St Denis
- */
-
-#ifdef LTC_RIPEMD160
-
-const struct ltc_hash_descriptor rmd160_desc =
-{
-    "rmd160",
-    9,
-    20,
-    64,
-
-    /* OID */
-   { 1, 3, 36, 3, 2, 1,  },
-   6,
-
-    &rmd160_init,
-    &rmd160_process,
-    &rmd160_done,
-    &rmd160_test,
-    NULL
-};
-
-/* the five basic functions F(), G() and H() */
-#define F(x, y, z)        ((x) ^ (y) ^ (z))
-#define G(x, y, z)        (((x) & (y)) | (~(x) & (z)))
-#define H(x, y, z)        (((x) | ~(y)) ^ (z))
-#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z)))
-#define J(x, y, z)        ((x) ^ ((y) | ~(z)))
-
-/* the ten basic operations FF() through III() */
-#define FF(a, b, c, d, e, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define GG(a, b, c, d, e, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define HH(a, b, c, d, e, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define II(a, b, c, d, e, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define JJ(a, b, c, d, e, x, s)        \
-      (a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define FFF(a, b, c, d, e, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define GGG(a, b, c, d, e, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define HHH(a, b, c, d, e, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define III(a, b, c, d, e, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define JJJ(a, b, c, d, e, x, s)        \
-      (a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-
-#ifdef LTC_CLEAN_STACK
-static int _rmd160_compress(hash_state *md, unsigned char *buf)
-#else
-static int  rmd160_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-   ulong32 aa,bb,cc,dd,ee,aaa,bbb,ccc,ddd,eee,X[16];
-   int i;
-
-   /* load words X */
-   for (i = 0; i < 16; i++){
-      LOAD32L(X[i], buf + (4 * i));
-   }
-
-   /* load state */
-   aa = aaa = md->rmd160.state[0];
-   bb = bbb = md->rmd160.state[1];
-   cc = ccc = md->rmd160.state[2];
-   dd = ddd = md->rmd160.state[3];
-   ee = eee = md->rmd160.state[4];
-
-   /* round 1 */
-   FF(aa, bb, cc, dd, ee, X[ 0], 11);
-   FF(ee, aa, bb, cc, dd, X[ 1], 14);
-   FF(dd, ee, aa, bb, cc, X[ 2], 15);
-   FF(cc, dd, ee, aa, bb, X[ 3], 12);
-   FF(bb, cc, dd, ee, aa, X[ 4],  5);
-   FF(aa, bb, cc, dd, ee, X[ 5],  8);
-   FF(ee, aa, bb, cc, dd, X[ 6],  7);
-   FF(dd, ee, aa, bb, cc, X[ 7],  9);
-   FF(cc, dd, ee, aa, bb, X[ 8], 11);
-   FF(bb, cc, dd, ee, aa, X[ 9], 13);
-   FF(aa, bb, cc, dd, ee, X[10], 14);
-   FF(ee, aa, bb, cc, dd, X[11], 15);
-   FF(dd, ee, aa, bb, cc, X[12],  6);
-   FF(cc, dd, ee, aa, bb, X[13],  7);
-   FF(bb, cc, dd, ee, aa, X[14],  9);
-   FF(aa, bb, cc, dd, ee, X[15],  8);
-
-   /* round 2 */
-   GG(ee, aa, bb, cc, dd, X[ 7],  7);
-   GG(dd, ee, aa, bb, cc, X[ 4],  6);
-   GG(cc, dd, ee, aa, bb, X[13],  8);
-   GG(bb, cc, dd, ee, aa, X[ 1], 13);
-   GG(aa, bb, cc, dd, ee, X[10], 11);
-   GG(ee, aa, bb, cc, dd, X[ 6],  9);
-   GG(dd, ee, aa, bb, cc, X[15],  7);
-   GG(cc, dd, ee, aa, bb, X[ 3], 15);
-   GG(bb, cc, dd, ee, aa, X[12],  7);
-   GG(aa, bb, cc, dd, ee, X[ 0], 12);
-   GG(ee, aa, bb, cc, dd, X[ 9], 15);
-   GG(dd, ee, aa, bb, cc, X[ 5],  9);
-   GG(cc, dd, ee, aa, bb, X[ 2], 11);
-   GG(bb, cc, dd, ee, aa, X[14],  7);
-   GG(aa, bb, cc, dd, ee, X[11], 13);
-   GG(ee, aa, bb, cc, dd, X[ 8], 12);
-
-   /* round 3 */
-   HH(dd, ee, aa, bb, cc, X[ 3], 11);
-   HH(cc, dd, ee, aa, bb, X[10], 13);
-   HH(bb, cc, dd, ee, aa, X[14],  6);
-   HH(aa, bb, cc, dd, ee, X[ 4],  7);
-   HH(ee, aa, bb, cc, dd, X[ 9], 14);
-   HH(dd, ee, aa, bb, cc, X[15],  9);
-   HH(cc, dd, ee, aa, bb, X[ 8], 13);
-   HH(bb, cc, dd, ee, aa, X[ 1], 15);
-   HH(aa, bb, cc, dd, ee, X[ 2], 14);
-   HH(ee, aa, bb, cc, dd, X[ 7],  8);
-   HH(dd, ee, aa, bb, cc, X[ 0], 13);
-   HH(cc, dd, ee, aa, bb, X[ 6],  6);
-   HH(bb, cc, dd, ee, aa, X[13],  5);
-   HH(aa, bb, cc, dd, ee, X[11], 12);
-   HH(ee, aa, bb, cc, dd, X[ 5],  7);
-   HH(dd, ee, aa, bb, cc, X[12],  5);
-
-   /* round 4 */
-   II(cc, dd, ee, aa, bb, X[ 1], 11);
-   II(bb, cc, dd, ee, aa, X[ 9], 12);
-   II(aa, bb, cc, dd, ee, X[11], 14);
-   II(ee, aa, bb, cc, dd, X[10], 15);
-   II(dd, ee, aa, bb, cc, X[ 0], 14);
-   II(cc, dd, ee, aa, bb, X[ 8], 15);
-   II(bb, cc, dd, ee, aa, X[12],  9);
-   II(aa, bb, cc, dd, ee, X[ 4],  8);
-   II(ee, aa, bb, cc, dd, X[13],  9);
-   II(dd, ee, aa, bb, cc, X[ 3], 14);
-   II(cc, dd, ee, aa, bb, X[ 7],  5);
-   II(bb, cc, dd, ee, aa, X[15],  6);
-   II(aa, bb, cc, dd, ee, X[14],  8);
-   II(ee, aa, bb, cc, dd, X[ 5],  6);
-   II(dd, ee, aa, bb, cc, X[ 6],  5);
-   II(cc, dd, ee, aa, bb, X[ 2], 12);
-
-   /* round 5 */
-   JJ(bb, cc, dd, ee, aa, X[ 4],  9);
-   JJ(aa, bb, cc, dd, ee, X[ 0], 15);
-   JJ(ee, aa, bb, cc, dd, X[ 5],  5);
-   JJ(dd, ee, aa, bb, cc, X[ 9], 11);
-   JJ(cc, dd, ee, aa, bb, X[ 7],  6);
-   JJ(bb, cc, dd, ee, aa, X[12],  8);
-   JJ(aa, bb, cc, dd, ee, X[ 2], 13);
-   JJ(ee, aa, bb, cc, dd, X[10], 12);
-   JJ(dd, ee, aa, bb, cc, X[14],  5);
-   JJ(cc, dd, ee, aa, bb, X[ 1], 12);
-   JJ(bb, cc, dd, ee, aa, X[ 3], 13);
-   JJ(aa, bb, cc, dd, ee, X[ 8], 14);
-   JJ(ee, aa, bb, cc, dd, X[11], 11);
-   JJ(dd, ee, aa, bb, cc, X[ 6],  8);
-   JJ(cc, dd, ee, aa, bb, X[15],  5);
-   JJ(bb, cc, dd, ee, aa, X[13],  6);
-
-   /* parallel round 1 */
-   JJJ(aaa, bbb, ccc, ddd, eee, X[ 5],  8);
-   JJJ(eee, aaa, bbb, ccc, ddd, X[14],  9);
-   JJJ(ddd, eee, aaa, bbb, ccc, X[ 7],  9);
-   JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11);
-   JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13);
-   JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15);
-   JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15);
-   JJJ(ddd, eee, aaa, bbb, ccc, X[ 4],  5);
-   JJJ(ccc, ddd, eee, aaa, bbb, X[13],  7);
-   JJJ(bbb, ccc, ddd, eee, aaa, X[ 6],  7);
-   JJJ(aaa, bbb, ccc, ddd, eee, X[15],  8);
-   JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11);
-   JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14);
-   JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14);
-   JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12);
-   JJJ(aaa, bbb, ccc, ddd, eee, X[12],  6);
-
-   /* parallel round 2 */
-   III(eee, aaa, bbb, ccc, ddd, X[ 6],  9);
-   III(ddd, eee, aaa, bbb, ccc, X[11], 13);
-   III(ccc, ddd, eee, aaa, bbb, X[ 3], 15);
-   III(bbb, ccc, ddd, eee, aaa, X[ 7],  7);
-   III(aaa, bbb, ccc, ddd, eee, X[ 0], 12);
-   III(eee, aaa, bbb, ccc, ddd, X[13],  8);
-   III(ddd, eee, aaa, bbb, ccc, X[ 5],  9);
-   III(ccc, ddd, eee, aaa, bbb, X[10], 11);
-   III(bbb, ccc, ddd, eee, aaa, X[14],  7);
-   III(aaa, bbb, ccc, ddd, eee, X[15],  7);
-   III(eee, aaa, bbb, ccc, ddd, X[ 8], 12);
-   III(ddd, eee, aaa, bbb, ccc, X[12],  7);
-   III(ccc, ddd, eee, aaa, bbb, X[ 4],  6);
-   III(bbb, ccc, ddd, eee, aaa, X[ 9], 15);
-   III(aaa, bbb, ccc, ddd, eee, X[ 1], 13);
-   III(eee, aaa, bbb, ccc, ddd, X[ 2], 11);
-
-   /* parallel round 3 */
-   HHH(ddd, eee, aaa, bbb, ccc, X[15],  9);
-   HHH(ccc, ddd, eee, aaa, bbb, X[ 5],  7);
-   HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15);
-   HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11);
-   HHH(eee, aaa, bbb, ccc, ddd, X[ 7],  8);
-   HHH(ddd, eee, aaa, bbb, ccc, X[14],  6);
-   HHH(ccc, ddd, eee, aaa, bbb, X[ 6],  6);
-   HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14);
-   HHH(aaa, bbb, ccc, ddd, eee, X[11], 12);
-   HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13);
-   HHH(ddd, eee, aaa, bbb, ccc, X[12],  5);
-   HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14);
-   HHH(bbb, ccc, ddd, eee, aaa, X[10], 13);
-   HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13);
-   HHH(eee, aaa, bbb, ccc, ddd, X[ 4],  7);
-   HHH(ddd, eee, aaa, bbb, ccc, X[13],  5);
-
-   /* parallel round 4 */
-   GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15);
-   GGG(bbb, ccc, ddd, eee, aaa, X[ 6],  5);
-   GGG(aaa, bbb, ccc, ddd, eee, X[ 4],  8);
-   GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11);
-   GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14);
-   GGG(ccc, ddd, eee, aaa, bbb, X[11], 14);
-   GGG(bbb, ccc, ddd, eee, aaa, X[15],  6);
-   GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14);
-   GGG(eee, aaa, bbb, ccc, ddd, X[ 5],  6);
-   GGG(ddd, eee, aaa, bbb, ccc, X[12],  9);
-   GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12);
-   GGG(bbb, ccc, ddd, eee, aaa, X[13],  9);
-   GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12);
-   GGG(eee, aaa, bbb, ccc, ddd, X[ 7],  5);
-   GGG(ddd, eee, aaa, bbb, ccc, X[10], 15);
-   GGG(ccc, ddd, eee, aaa, bbb, X[14],  8);
-
-   /* parallel round 5 */
-   FFF(bbb, ccc, ddd, eee, aaa, X[12] ,  8);
-   FFF(aaa, bbb, ccc, ddd, eee, X[15] ,  5);
-   FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12);
-   FFF(ddd, eee, aaa, bbb, ccc, X[ 4] ,  9);
-   FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12);
-   FFF(bbb, ccc, ddd, eee, aaa, X[ 5] ,  5);
-   FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14);
-   FFF(eee, aaa, bbb, ccc, ddd, X[ 7] ,  6);
-   FFF(ddd, eee, aaa, bbb, ccc, X[ 6] ,  8);
-   FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13);
-   FFF(bbb, ccc, ddd, eee, aaa, X[13] ,  6);
-   FFF(aaa, bbb, ccc, ddd, eee, X[14] ,  5);
-   FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15);
-   FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13);
-   FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11);
-   FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11);
-
-   /* combine results */
-   ddd += cc + md->rmd160.state[1];               /* final result for md->rmd160.state[0] */
-   md->rmd160.state[1] = md->rmd160.state[2] + dd + eee;
-   md->rmd160.state[2] = md->rmd160.state[3] + ee + aaa;
-   md->rmd160.state[3] = md->rmd160.state[4] + aa + bbb;
-   md->rmd160.state[4] = md->rmd160.state[0] + bb + ccc;
-   md->rmd160.state[0] = ddd;
-
-   return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int rmd160_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _rmd160_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 26 + sizeof(int));
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int rmd160_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->rmd160.state[0] = 0x67452301UL;
-   md->rmd160.state[1] = 0xefcdab89UL;
-   md->rmd160.state[2] = 0x98badcfeUL;
-   md->rmd160.state[3] = 0x10325476UL;
-   md->rmd160.state[4] = 0xc3d2e1f0UL;
-   md->rmd160.curlen   = 0;
-   md->rmd160.length   = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(rmd160_process, rmd160_compress, rmd160, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (20 bytes)
-   @return CRYPT_OK if successful
-*/
-int rmd160_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->rmd160.curlen >= sizeof(md->rmd160.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* increase the length of the message */
-    md->rmd160.length += md->rmd160.curlen * 8;
-
-    /* append the '1' bit */
-    md->rmd160.buf[md->rmd160.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->rmd160.curlen > 56) {
-        while (md->rmd160.curlen < 64) {
-            md->rmd160.buf[md->rmd160.curlen++] = (unsigned char)0;
-        }
-        rmd160_compress(md, md->rmd160.buf);
-        md->rmd160.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->rmd160.curlen < 56) {
-        md->rmd160.buf[md->rmd160.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64L(md->rmd160.length, md->rmd160.buf+56);
-    rmd160_compress(md, md->rmd160.buf);
-
-    /* copy output */
-    for (i = 0; i < 5; i++) {
-        STORE32L(md->rmd160.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int rmd160_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   static const struct {
-        const char *msg;
-        unsigned char hash[20];
-   } tests[] = {
-   { "",
-     { 0x9c, 0x11, 0x85, 0xa5, 0xc5, 0xe9, 0xfc, 0x54, 0x61, 0x28,
-       0x08, 0x97, 0x7e, 0xe8, 0xf5, 0x48, 0xb2, 0x25, 0x8d, 0x31 }
-   },
-   { "a",
-     { 0x0b, 0xdc, 0x9d, 0x2d, 0x25, 0x6b, 0x3e, 0xe9, 0xda, 0xae,
-       0x34, 0x7b, 0xe6, 0xf4, 0xdc, 0x83, 0x5a, 0x46, 0x7f, 0xfe }
-   },
-   { "abc",
-     { 0x8e, 0xb2, 0x08, 0xf7, 0xe0, 0x5d, 0x98, 0x7a, 0x9b, 0x04,
-       0x4a, 0x8e, 0x98, 0xc6, 0xb0, 0x87, 0xf1, 0x5a, 0x0b, 0xfc }
-   },
-   { "message digest",
-     { 0x5d, 0x06, 0x89, 0xef, 0x49, 0xd2, 0xfa, 0xe5, 0x72, 0xb8,
-       0x81, 0xb1, 0x23, 0xa8, 0x5f, 0xfa, 0x21, 0x59, 0x5f, 0x36 }
-   },
-   { "abcdefghijklmnopqrstuvwxyz",
-     { 0xf7, 0x1c, 0x27, 0x10, 0x9c, 0x69, 0x2c, 0x1b, 0x56, 0xbb,
-       0xdc, 0xeb, 0x5b, 0x9d, 0x28, 0x65, 0xb3, 0x70, 0x8d, 0xbc }
-   },
-   { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-     { 0x12, 0xa0, 0x53, 0x38, 0x4a, 0x9c, 0x0c, 0x88, 0xe4, 0x05,
-       0xa0, 0x6c, 0x27, 0xdc, 0xf4, 0x9a, 0xda, 0x62, 0xeb, 0x2b }
-   }
-   };
-
-   int i;
-   unsigned char tmp[20];
-   hash_state md;
-
-   for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
-       rmd160_init(&md);
-       rmd160_process(&md, (unsigned char *)tests[i].msg, strlen(tests[i].msg));
-       rmd160_done(&md, tmp);
-       if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "RIPEMD160", i)) {
-          return CRYPT_FAIL_TESTVECTOR;
-       }
-   }
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/rmd256.c b/libs/tomcrypt/src/hashes/rmd256.c
deleted file mode 100644
index 39f142ffabe7..000000000000
--- a/libs/tomcrypt/src/hashes/rmd256.c
+++ /dev/null
@@ -1,426 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @param rmd256.c
-   RLTC_MD256 Hash function
-*/
-
-#ifdef LTC_RIPEMD256
-
-const struct ltc_hash_descriptor rmd256_desc =
-{
-    "rmd256",
-    13,
-    32,
-    64,
-
-    /* OID */
-   { 1, 3, 36, 3, 2, 3 },
-   6,
-
-    &rmd256_init,
-    &rmd256_process,
-    &rmd256_done,
-    &rmd256_test,
-    NULL
-};
-
-/* the four basic functions F(), G() and H() */
-#define F(x, y, z)        ((x) ^ (y) ^ (z))
-#define G(x, y, z)        (((x) & (y)) | (~(x) & (z)))
-#define H(x, y, z)        (((x) | ~(y)) ^ (z))
-#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z)))
-
-/* the eight basic operations FF() through III() */
-#define FF(a, b, c, d, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s));
-
-#define GG(a, b, c, d, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
-      (a) = ROLc((a), (s));
-
-#define HH(a, b, c, d, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
-      (a) = ROLc((a), (s));
-
-#define II(a, b, c, d, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
-      (a) = ROLc((a), (s));
-
-#define FFF(a, b, c, d, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s));
-
-#define GGG(a, b, c, d, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x6d703ef3UL;\
-      (a) = ROLc((a), (s));
-
-#define HHH(a, b, c, d, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x5c4dd124UL;\
-      (a) = ROLc((a), (s));
-
-#define III(a, b, c, d, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x50a28be6UL;\
-      (a) = ROLc((a), (s));
-
-#ifdef LTC_CLEAN_STACK
-static int _rmd256_compress(hash_state *md, unsigned char *buf)
-#else
-static int  rmd256_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-   ulong32 aa,bb,cc,dd,aaa,bbb,ccc,ddd,tmp,X[16];
-   int i;
-
-   /* load words X */
-   for (i = 0; i < 16; i++){
-      LOAD32L(X[i], buf + (4 * i));
-   }
-
-   /* load state */
-   aa = md->rmd256.state[0];
-   bb = md->rmd256.state[1];
-   cc = md->rmd256.state[2];
-   dd = md->rmd256.state[3];
-   aaa = md->rmd256.state[4];
-   bbb = md->rmd256.state[5];
-   ccc = md->rmd256.state[6];
-   ddd = md->rmd256.state[7];
-
-   /* round 1 */
-   FF(aa, bb, cc, dd, X[ 0], 11);
-   FF(dd, aa, bb, cc, X[ 1], 14);
-   FF(cc, dd, aa, bb, X[ 2], 15);
-   FF(bb, cc, dd, aa, X[ 3], 12);
-   FF(aa, bb, cc, dd, X[ 4],  5);
-   FF(dd, aa, bb, cc, X[ 5],  8);
-   FF(cc, dd, aa, bb, X[ 6],  7);
-   FF(bb, cc, dd, aa, X[ 7],  9);
-   FF(aa, bb, cc, dd, X[ 8], 11);
-   FF(dd, aa, bb, cc, X[ 9], 13);
-   FF(cc, dd, aa, bb, X[10], 14);
-   FF(bb, cc, dd, aa, X[11], 15);
-   FF(aa, bb, cc, dd, X[12],  6);
-   FF(dd, aa, bb, cc, X[13],  7);
-   FF(cc, dd, aa, bb, X[14],  9);
-   FF(bb, cc, dd, aa, X[15],  8);
-
-   /* parallel round 1 */
-   III(aaa, bbb, ccc, ddd, X[ 5],  8);
-   III(ddd, aaa, bbb, ccc, X[14],  9);
-   III(ccc, ddd, aaa, bbb, X[ 7],  9);
-   III(bbb, ccc, ddd, aaa, X[ 0], 11);
-   III(aaa, bbb, ccc, ddd, X[ 9], 13);
-   III(ddd, aaa, bbb, ccc, X[ 2], 15);
-   III(ccc, ddd, aaa, bbb, X[11], 15);
-   III(bbb, ccc, ddd, aaa, X[ 4],  5);
-   III(aaa, bbb, ccc, ddd, X[13],  7);
-   III(ddd, aaa, bbb, ccc, X[ 6],  7);
-   III(ccc, ddd, aaa, bbb, X[15],  8);
-   III(bbb, ccc, ddd, aaa, X[ 8], 11);
-   III(aaa, bbb, ccc, ddd, X[ 1], 14);
-   III(ddd, aaa, bbb, ccc, X[10], 14);
-   III(ccc, ddd, aaa, bbb, X[ 3], 12);
-   III(bbb, ccc, ddd, aaa, X[12],  6);
-
-   tmp = aa; aa = aaa; aaa = tmp;
-
-   /* round 2 */
-   GG(aa, bb, cc, dd, X[ 7],  7);
-   GG(dd, aa, bb, cc, X[ 4],  6);
-   GG(cc, dd, aa, bb, X[13],  8);
-   GG(bb, cc, dd, aa, X[ 1], 13);
-   GG(aa, bb, cc, dd, X[10], 11);
-   GG(dd, aa, bb, cc, X[ 6],  9);
-   GG(cc, dd, aa, bb, X[15],  7);
-   GG(bb, cc, dd, aa, X[ 3], 15);
-   GG(aa, bb, cc, dd, X[12],  7);
-   GG(dd, aa, bb, cc, X[ 0], 12);
-   GG(cc, dd, aa, bb, X[ 9], 15);
-   GG(bb, cc, dd, aa, X[ 5],  9);
-   GG(aa, bb, cc, dd, X[ 2], 11);
-   GG(dd, aa, bb, cc, X[14],  7);
-   GG(cc, dd, aa, bb, X[11], 13);
-   GG(bb, cc, dd, aa, X[ 8], 12);
-
-   /* parallel round 2 */
-   HHH(aaa, bbb, ccc, ddd, X[ 6],  9);
-   HHH(ddd, aaa, bbb, ccc, X[11], 13);
-   HHH(ccc, ddd, aaa, bbb, X[ 3], 15);
-   HHH(bbb, ccc, ddd, aaa, X[ 7],  7);
-   HHH(aaa, bbb, ccc, ddd, X[ 0], 12);
-   HHH(ddd, aaa, bbb, ccc, X[13],  8);
-   HHH(ccc, ddd, aaa, bbb, X[ 5],  9);
-   HHH(bbb, ccc, ddd, aaa, X[10], 11);
-   HHH(aaa, bbb, ccc, ddd, X[14],  7);
-   HHH(ddd, aaa, bbb, ccc, X[15],  7);
-   HHH(ccc, ddd, aaa, bbb, X[ 8], 12);
-   HHH(bbb, ccc, ddd, aaa, X[12],  7);
-   HHH(aaa, bbb, ccc, ddd, X[ 4],  6);
-   HHH(ddd, aaa, bbb, ccc, X[ 9], 15);
-   HHH(ccc, ddd, aaa, bbb, X[ 1], 13);
-   HHH(bbb, ccc, ddd, aaa, X[ 2], 11);
-
-   tmp = bb; bb = bbb; bbb = tmp;
-
-   /* round 3 */
-   HH(aa, bb, cc, dd, X[ 3], 11);
-   HH(dd, aa, bb, cc, X[10], 13);
-   HH(cc, dd, aa, bb, X[14],  6);
-   HH(bb, cc, dd, aa, X[ 4],  7);
-   HH(aa, bb, cc, dd, X[ 9], 14);
-   HH(dd, aa, bb, cc, X[15],  9);
-   HH(cc, dd, aa, bb, X[ 8], 13);
-   HH(bb, cc, dd, aa, X[ 1], 15);
-   HH(aa, bb, cc, dd, X[ 2], 14);
-   HH(dd, aa, bb, cc, X[ 7],  8);
-   HH(cc, dd, aa, bb, X[ 0], 13);
-   HH(bb, cc, dd, aa, X[ 6],  6);
-   HH(aa, bb, cc, dd, X[13],  5);
-   HH(dd, aa, bb, cc, X[11], 12);
-   HH(cc, dd, aa, bb, X[ 5],  7);
-   HH(bb, cc, dd, aa, X[12],  5);
-
-   /* parallel round 3 */
-   GGG(aaa, bbb, ccc, ddd, X[15],  9);
-   GGG(ddd, aaa, bbb, ccc, X[ 5],  7);
-   GGG(ccc, ddd, aaa, bbb, X[ 1], 15);
-   GGG(bbb, ccc, ddd, aaa, X[ 3], 11);
-   GGG(aaa, bbb, ccc, ddd, X[ 7],  8);
-   GGG(ddd, aaa, bbb, ccc, X[14],  6);
-   GGG(ccc, ddd, aaa, bbb, X[ 6],  6);
-   GGG(bbb, ccc, ddd, aaa, X[ 9], 14);
-   GGG(aaa, bbb, ccc, ddd, X[11], 12);
-   GGG(ddd, aaa, bbb, ccc, X[ 8], 13);
-   GGG(ccc, ddd, aaa, bbb, X[12],  5);
-   GGG(bbb, ccc, ddd, aaa, X[ 2], 14);
-   GGG(aaa, bbb, ccc, ddd, X[10], 13);
-   GGG(ddd, aaa, bbb, ccc, X[ 0], 13);
-   GGG(ccc, ddd, aaa, bbb, X[ 4],  7);
-   GGG(bbb, ccc, ddd, aaa, X[13],  5);
-
-   tmp = cc; cc = ccc; ccc = tmp;
-
-   /* round 4 */
-   II(aa, bb, cc, dd, X[ 1], 11);
-   II(dd, aa, bb, cc, X[ 9], 12);
-   II(cc, dd, aa, bb, X[11], 14);
-   II(bb, cc, dd, aa, X[10], 15);
-   II(aa, bb, cc, dd, X[ 0], 14);
-   II(dd, aa, bb, cc, X[ 8], 15);
-   II(cc, dd, aa, bb, X[12],  9);
-   II(bb, cc, dd, aa, X[ 4],  8);
-   II(aa, bb, cc, dd, X[13],  9);
-   II(dd, aa, bb, cc, X[ 3], 14);
-   II(cc, dd, aa, bb, X[ 7],  5);
-   II(bb, cc, dd, aa, X[15],  6);
-   II(aa, bb, cc, dd, X[14],  8);
-   II(dd, aa, bb, cc, X[ 5],  6);
-   II(cc, dd, aa, bb, X[ 6],  5);
-   II(bb, cc, dd, aa, X[ 2], 12);
-
-   /* parallel round 4 */
-   FFF(aaa, bbb, ccc, ddd, X[ 8], 15);
-   FFF(ddd, aaa, bbb, ccc, X[ 6],  5);
-   FFF(ccc, ddd, aaa, bbb, X[ 4],  8);
-   FFF(bbb, ccc, ddd, aaa, X[ 1], 11);
-   FFF(aaa, bbb, ccc, ddd, X[ 3], 14);
-   FFF(ddd, aaa, bbb, ccc, X[11], 14);
-   FFF(ccc, ddd, aaa, bbb, X[15],  6);
-   FFF(bbb, ccc, ddd, aaa, X[ 0], 14);
-   FFF(aaa, bbb, ccc, ddd, X[ 5],  6);
-   FFF(ddd, aaa, bbb, ccc, X[12],  9);
-   FFF(ccc, ddd, aaa, bbb, X[ 2], 12);
-   FFF(bbb, ccc, ddd, aaa, X[13],  9);
-   FFF(aaa, bbb, ccc, ddd, X[ 9], 12);
-   FFF(ddd, aaa, bbb, ccc, X[ 7],  5);
-   FFF(ccc, ddd, aaa, bbb, X[10], 15);
-   FFF(bbb, ccc, ddd, aaa, X[14],  8);
-
-   tmp = dd; dd = ddd; ddd = tmp;
-
-   /* combine results */
-   md->rmd256.state[0] += aa;
-   md->rmd256.state[1] += bb;
-   md->rmd256.state[2] += cc;
-   md->rmd256.state[3] += dd;
-   md->rmd256.state[4] += aaa;
-   md->rmd256.state[5] += bbb;
-   md->rmd256.state[6] += ccc;
-   md->rmd256.state[7] += ddd;
-
-   return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int rmd256_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _rmd256_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 25 + sizeof(int));
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int rmd256_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->rmd256.state[0] = 0x67452301UL;
-   md->rmd256.state[1] = 0xefcdab89UL;
-   md->rmd256.state[2] = 0x98badcfeUL;
-   md->rmd256.state[3] = 0x10325476UL;
-   md->rmd256.state[4] = 0x76543210UL;
-   md->rmd256.state[5] = 0xfedcba98UL;
-   md->rmd256.state[6] = 0x89abcdefUL;
-   md->rmd256.state[7] = 0x01234567UL;
-   md->rmd256.curlen   = 0;
-   md->rmd256.length   = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(rmd256_process, rmd256_compress, rmd256, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (16 bytes)
-   @return CRYPT_OK if successful
-*/
-int rmd256_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->rmd256.curlen >= sizeof(md->rmd256.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* increase the length of the message */
-    md->rmd256.length += md->rmd256.curlen * 8;
-
-    /* append the '1' bit */
-    md->rmd256.buf[md->rmd256.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->rmd256.curlen > 56) {
-        while (md->rmd256.curlen < 64) {
-            md->rmd256.buf[md->rmd256.curlen++] = (unsigned char)0;
-        }
-        rmd256_compress(md, md->rmd256.buf);
-        md->rmd256.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->rmd256.curlen < 56) {
-        md->rmd256.buf[md->rmd256.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64L(md->rmd256.length, md->rmd256.buf+56);
-    rmd256_compress(md, md->rmd256.buf);
-
-    /* copy output */
-    for (i = 0; i < 8; i++) {
-        STORE32L(md->rmd256.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-   return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int rmd256_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   static const struct {
-        const char *msg;
-        unsigned char hash[32];
-   } tests[] = {
-   { "",
-     { 0x02, 0xba, 0x4c, 0x4e, 0x5f, 0x8e, 0xcd, 0x18,
-       0x77, 0xfc, 0x52, 0xd6, 0x4d, 0x30, 0xe3, 0x7a,
-       0x2d, 0x97, 0x74, 0xfb, 0x1e, 0x5d, 0x02, 0x63,
-       0x80, 0xae, 0x01, 0x68, 0xe3, 0xc5, 0x52, 0x2d }
-   },
-   { "a",
-     { 0xf9, 0x33, 0x3e, 0x45, 0xd8, 0x57, 0xf5, 0xd9,
-       0x0a, 0x91, 0xba, 0xb7, 0x0a, 0x1e, 0xba, 0x0c,
-       0xfb, 0x1b, 0xe4, 0xb0, 0x78, 0x3c, 0x9a, 0xcf,
-       0xcd, 0x88, 0x3a, 0x91, 0x34, 0x69, 0x29, 0x25 }
-   },
-   { "abc",
-     { 0xaf, 0xbd, 0x6e, 0x22, 0x8b, 0x9d, 0x8c, 0xbb,
-       0xce, 0xf5, 0xca, 0x2d, 0x03, 0xe6, 0xdb, 0xa1,
-       0x0a, 0xc0, 0xbc, 0x7d, 0xcb, 0xe4, 0x68, 0x0e,
-       0x1e, 0x42, 0xd2, 0xe9, 0x75, 0x45, 0x9b, 0x65 }
-   },
-   { "message digest",
-     { 0x87, 0xe9, 0x71, 0x75, 0x9a, 0x1c, 0xe4, 0x7a,
-       0x51, 0x4d, 0x5c, 0x91, 0x4c, 0x39, 0x2c, 0x90,
-       0x18, 0xc7, 0xc4, 0x6b, 0xc1, 0x44, 0x65, 0x55,
-       0x4a, 0xfc, 0xdf, 0x54, 0xa5, 0x07, 0x0c, 0x0e }
-   },
-   { "abcdefghijklmnopqrstuvwxyz",
-     { 0x64, 0x9d, 0x30, 0x34, 0x75, 0x1e, 0xa2, 0x16,
-       0x77, 0x6b, 0xf9, 0xa1, 0x8a, 0xcc, 0x81, 0xbc,
-       0x78, 0x96, 0x11, 0x8a, 0x51, 0x97, 0x96, 0x87,
-       0x82, 0xdd, 0x1f, 0xd9, 0x7d, 0x8d, 0x51, 0x33 }
-   },
-   { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-     { 0x57, 0x40, 0xa4, 0x08, 0xac, 0x16, 0xb7, 0x20,
-       0xb8, 0x44, 0x24, 0xae, 0x93, 0x1c, 0xbb, 0x1f,
-       0xe3, 0x63, 0xd1, 0xd0, 0xbf, 0x40, 0x17, 0xf1,
-       0xa8, 0x9f, 0x7e, 0xa6, 0xde, 0x77, 0xa0, 0xb8 }
-   }
-   };
-
-   int i;
-   unsigned char tmp[32];
-   hash_state md;
-
-   for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
-       rmd256_init(&md);
-       rmd256_process(&md, (unsigned char *)tests[i].msg, strlen(tests[i].msg));
-       rmd256_done(&md, tmp);
-       if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "RIPEMD256", i)) {
-          return CRYPT_FAIL_TESTVECTOR;
-       }
-   }
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/rmd320.c b/libs/tomcrypt/src/hashes/rmd320.c
deleted file mode 100644
index e28a050826ac..000000000000
--- a/libs/tomcrypt/src/hashes/rmd320.c
+++ /dev/null
@@ -1,491 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file rmd320.c
-   RMD320 hash function
-*/
-
-#ifdef LTC_RIPEMD320
-
-const struct ltc_hash_descriptor rmd320_desc =
-{
-    "rmd320",
-    14,
-    40,
-    64,
-
-    /* OID ... does not exist
-     * http://oid-info.com/get/1.3.36.3.2 */
-   { 0 },
-   0,
-
-    &rmd320_init,
-    &rmd320_process,
-    &rmd320_done,
-    &rmd320_test,
-    NULL
-};
-
-/* the five basic functions F(), G() and H() */
-#define F(x, y, z)        ((x) ^ (y) ^ (z))
-#define G(x, y, z)        (((x) & (y)) | (~(x) & (z)))
-#define H(x, y, z)        (((x) | ~(y)) ^ (z))
-#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z)))
-#define J(x, y, z)        ((x) ^ ((y) | ~(z)))
-
-/* the ten basic operations FF() through III() */
-#define FF(a, b, c, d, e, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define GG(a, b, c, d, e, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define HH(a, b, c, d, e, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define II(a, b, c, d, e, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define JJ(a, b, c, d, e, x, s)        \
-      (a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define FFF(a, b, c, d, e, x, s)        \
-      (a) += F((b), (c), (d)) + (x);\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define GGG(a, b, c, d, e, x, s)        \
-      (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define HHH(a, b, c, d, e, x, s)        \
-      (a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define III(a, b, c, d, e, x, s)        \
-      (a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-#define JJJ(a, b, c, d, e, x, s)        \
-      (a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\
-      (a) = ROLc((a), (s)) + (e);\
-      (c) = ROLc((c), 10);
-
-
-#ifdef LTC_CLEAN_STACK
-static int _rmd320_compress(hash_state *md, unsigned char *buf)
-#else
-static int  rmd320_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-   ulong32 aa,bb,cc,dd,ee,aaa,bbb,ccc,ddd,eee,tmp,X[16];
-   int i;
-
-   /* load words X */
-   for (i = 0; i < 16; i++){
-      LOAD32L(X[i], buf + (4 * i));
-   }
-
-   /* load state */
-   aa = md->rmd320.state[0];
-   bb = md->rmd320.state[1];
-   cc = md->rmd320.state[2];
-   dd = md->rmd320.state[3];
-   ee = md->rmd320.state[4];
-   aaa = md->rmd320.state[5];
-   bbb = md->rmd320.state[6];
-   ccc = md->rmd320.state[7];
-   ddd = md->rmd320.state[8];
-   eee = md->rmd320.state[9];
-
-   /* round 1 */
-   FF(aa, bb, cc, dd, ee, X[ 0], 11);
-   FF(ee, aa, bb, cc, dd, X[ 1], 14);
-   FF(dd, ee, aa, bb, cc, X[ 2], 15);
-   FF(cc, dd, ee, aa, bb, X[ 3], 12);
-   FF(bb, cc, dd, ee, aa, X[ 4],  5);
-   FF(aa, bb, cc, dd, ee, X[ 5],  8);
-   FF(ee, aa, bb, cc, dd, X[ 6],  7);
-   FF(dd, ee, aa, bb, cc, X[ 7],  9);
-   FF(cc, dd, ee, aa, bb, X[ 8], 11);
-   FF(bb, cc, dd, ee, aa, X[ 9], 13);
-   FF(aa, bb, cc, dd, ee, X[10], 14);
-   FF(ee, aa, bb, cc, dd, X[11], 15);
-   FF(dd, ee, aa, bb, cc, X[12],  6);
-   FF(cc, dd, ee, aa, bb, X[13],  7);
-   FF(bb, cc, dd, ee, aa, X[14],  9);
-   FF(aa, bb, cc, dd, ee, X[15],  8);
-
-   /* parallel round 1 */
-   JJJ(aaa, bbb, ccc, ddd, eee, X[ 5],  8);
-   JJJ(eee, aaa, bbb, ccc, ddd, X[14],  9);
-   JJJ(ddd, eee, aaa, bbb, ccc, X[ 7],  9);
-   JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11);
-   JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13);
-   JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15);
-   JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15);
-   JJJ(ddd, eee, aaa, bbb, ccc, X[ 4],  5);
-   JJJ(ccc, ddd, eee, aaa, bbb, X[13],  7);
-   JJJ(bbb, ccc, ddd, eee, aaa, X[ 6],  7);
-   JJJ(aaa, bbb, ccc, ddd, eee, X[15],  8);
-   JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11);
-   JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14);
-   JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14);
-   JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12);
-   JJJ(aaa, bbb, ccc, ddd, eee, X[12],  6);
-
-   tmp = aa; aa = aaa; aaa = tmp;
-
-   /* round 2 */
-   GG(ee, aa, bb, cc, dd, X[ 7],  7);
-   GG(dd, ee, aa, bb, cc, X[ 4],  6);
-   GG(cc, dd, ee, aa, bb, X[13],  8);
-   GG(bb, cc, dd, ee, aa, X[ 1], 13);
-   GG(aa, bb, cc, dd, ee, X[10], 11);
-   GG(ee, aa, bb, cc, dd, X[ 6],  9);
-   GG(dd, ee, aa, bb, cc, X[15],  7);
-   GG(cc, dd, ee, aa, bb, X[ 3], 15);
-   GG(bb, cc, dd, ee, aa, X[12],  7);
-   GG(aa, bb, cc, dd, ee, X[ 0], 12);
-   GG(ee, aa, bb, cc, dd, X[ 9], 15);
-   GG(dd, ee, aa, bb, cc, X[ 5],  9);
-   GG(cc, dd, ee, aa, bb, X[ 2], 11);
-   GG(bb, cc, dd, ee, aa, X[14],  7);
-   GG(aa, bb, cc, dd, ee, X[11], 13);
-   GG(ee, aa, bb, cc, dd, X[ 8], 12);
-
-   /* parallel round 2 */
-   III(eee, aaa, bbb, ccc, ddd, X[ 6],  9);
-   III(ddd, eee, aaa, bbb, ccc, X[11], 13);
-   III(ccc, ddd, eee, aaa, bbb, X[ 3], 15);
-   III(bbb, ccc, ddd, eee, aaa, X[ 7],  7);
-   III(aaa, bbb, ccc, ddd, eee, X[ 0], 12);
-   III(eee, aaa, bbb, ccc, ddd, X[13],  8);
-   III(ddd, eee, aaa, bbb, ccc, X[ 5],  9);
-   III(ccc, ddd, eee, aaa, bbb, X[10], 11);
-   III(bbb, ccc, ddd, eee, aaa, X[14],  7);
-   III(aaa, bbb, ccc, ddd, eee, X[15],  7);
-   III(eee, aaa, bbb, ccc, ddd, X[ 8], 12);
-   III(ddd, eee, aaa, bbb, ccc, X[12],  7);
-   III(ccc, ddd, eee, aaa, bbb, X[ 4],  6);
-   III(bbb, ccc, ddd, eee, aaa, X[ 9], 15);
-   III(aaa, bbb, ccc, ddd, eee, X[ 1], 13);
-   III(eee, aaa, bbb, ccc, ddd, X[ 2], 11);
-
-   tmp = bb; bb = bbb; bbb = tmp;
-
-   /* round 3 */
-   HH(dd, ee, aa, bb, cc, X[ 3], 11);
-   HH(cc, dd, ee, aa, bb, X[10], 13);
-   HH(bb, cc, dd, ee, aa, X[14],  6);
-   HH(aa, bb, cc, dd, ee, X[ 4],  7);
-   HH(ee, aa, bb, cc, dd, X[ 9], 14);
-   HH(dd, ee, aa, bb, cc, X[15],  9);
-   HH(cc, dd, ee, aa, bb, X[ 8], 13);
-   HH(bb, cc, dd, ee, aa, X[ 1], 15);
-   HH(aa, bb, cc, dd, ee, X[ 2], 14);
-   HH(ee, aa, bb, cc, dd, X[ 7],  8);
-   HH(dd, ee, aa, bb, cc, X[ 0], 13);
-   HH(cc, dd, ee, aa, bb, X[ 6],  6);
-   HH(bb, cc, dd, ee, aa, X[13],  5);
-   HH(aa, bb, cc, dd, ee, X[11], 12);
-   HH(ee, aa, bb, cc, dd, X[ 5],  7);
-   HH(dd, ee, aa, bb, cc, X[12],  5);
-
-   /* parallel round 3 */
-   HHH(ddd, eee, aaa, bbb, ccc, X[15],  9);
-   HHH(ccc, ddd, eee, aaa, bbb, X[ 5],  7);
-   HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15);
-   HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11);
-   HHH(eee, aaa, bbb, ccc, ddd, X[ 7],  8);
-   HHH(ddd, eee, aaa, bbb, ccc, X[14],  6);
-   HHH(ccc, ddd, eee, aaa, bbb, X[ 6],  6);
-   HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14);
-   HHH(aaa, bbb, ccc, ddd, eee, X[11], 12);
-   HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13);
-   HHH(ddd, eee, aaa, bbb, ccc, X[12],  5);
-   HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14);
-   HHH(bbb, ccc, ddd, eee, aaa, X[10], 13);
-   HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13);
-   HHH(eee, aaa, bbb, ccc, ddd, X[ 4],  7);
-   HHH(ddd, eee, aaa, bbb, ccc, X[13],  5);
-
-   tmp = cc; cc = ccc; ccc = tmp;
-
-   /* round 4 */
-   II(cc, dd, ee, aa, bb, X[ 1], 11);
-   II(bb, cc, dd, ee, aa, X[ 9], 12);
-   II(aa, bb, cc, dd, ee, X[11], 14);
-   II(ee, aa, bb, cc, dd, X[10], 15);
-   II(dd, ee, aa, bb, cc, X[ 0], 14);
-   II(cc, dd, ee, aa, bb, X[ 8], 15);
-   II(bb, cc, dd, ee, aa, X[12],  9);
-   II(aa, bb, cc, dd, ee, X[ 4],  8);
-   II(ee, aa, bb, cc, dd, X[13],  9);
-   II(dd, ee, aa, bb, cc, X[ 3], 14);
-   II(cc, dd, ee, aa, bb, X[ 7],  5);
-   II(bb, cc, dd, ee, aa, X[15],  6);
-   II(aa, bb, cc, dd, ee, X[14],  8);
-   II(ee, aa, bb, cc, dd, X[ 5],  6);
-   II(dd, ee, aa, bb, cc, X[ 6],  5);
-   II(cc, dd, ee, aa, bb, X[ 2], 12);
-
-   /* parallel round 4 */
-   GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15);
-   GGG(bbb, ccc, ddd, eee, aaa, X[ 6],  5);
-   GGG(aaa, bbb, ccc, ddd, eee, X[ 4],  8);
-   GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11);
-   GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14);
-   GGG(ccc, ddd, eee, aaa, bbb, X[11], 14);
-   GGG(bbb, ccc, ddd, eee, aaa, X[15],  6);
-   GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14);
-   GGG(eee, aaa, bbb, ccc, ddd, X[ 5],  6);
-   GGG(ddd, eee, aaa, bbb, ccc, X[12],  9);
-   GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12);
-   GGG(bbb, ccc, ddd, eee, aaa, X[13],  9);
-   GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12);
-   GGG(eee, aaa, bbb, ccc, ddd, X[ 7],  5);
-   GGG(ddd, eee, aaa, bbb, ccc, X[10], 15);
-   GGG(ccc, ddd, eee, aaa, bbb, X[14],  8);
-
-   tmp = dd; dd = ddd; ddd = tmp;
-
-   /* round 5 */
-   JJ(bb, cc, dd, ee, aa, X[ 4],  9);
-   JJ(aa, bb, cc, dd, ee, X[ 0], 15);
-   JJ(ee, aa, bb, cc, dd, X[ 5],  5);
-   JJ(dd, ee, aa, bb, cc, X[ 9], 11);
-   JJ(cc, dd, ee, aa, bb, X[ 7],  6);
-   JJ(bb, cc, dd, ee, aa, X[12],  8);
-   JJ(aa, bb, cc, dd, ee, X[ 2], 13);
-   JJ(ee, aa, bb, cc, dd, X[10], 12);
-   JJ(dd, ee, aa, bb, cc, X[14],  5);
-   JJ(cc, dd, ee, aa, bb, X[ 1], 12);
-   JJ(bb, cc, dd, ee, aa, X[ 3], 13);
-   JJ(aa, bb, cc, dd, ee, X[ 8], 14);
-   JJ(ee, aa, bb, cc, dd, X[11], 11);
-   JJ(dd, ee, aa, bb, cc, X[ 6],  8);
-   JJ(cc, dd, ee, aa, bb, X[15],  5);
-   JJ(bb, cc, dd, ee, aa, X[13],  6);
-
-   /* parallel round 5 */
-   FFF(bbb, ccc, ddd, eee, aaa, X[12] ,  8);
-   FFF(aaa, bbb, ccc, ddd, eee, X[15] ,  5);
-   FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12);
-   FFF(ddd, eee, aaa, bbb, ccc, X[ 4] ,  9);
-   FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12);
-   FFF(bbb, ccc, ddd, eee, aaa, X[ 5] ,  5);
-   FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14);
-   FFF(eee, aaa, bbb, ccc, ddd, X[ 7] ,  6);
-   FFF(ddd, eee, aaa, bbb, ccc, X[ 6] ,  8);
-   FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13);
-   FFF(bbb, ccc, ddd, eee, aaa, X[13] ,  6);
-   FFF(aaa, bbb, ccc, ddd, eee, X[14] ,  5);
-   FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15);
-   FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13);
-   FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11);
-   FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11);
-
-   tmp = ee; ee = eee; eee = tmp;
-
-   /* combine results */
-   md->rmd320.state[0] += aa;
-   md->rmd320.state[1] += bb;
-   md->rmd320.state[2] += cc;
-   md->rmd320.state[3] += dd;
-   md->rmd320.state[4] += ee;
-   md->rmd320.state[5] += aaa;
-   md->rmd320.state[6] += bbb;
-   md->rmd320.state[7] += ccc;
-   md->rmd320.state[8] += ddd;
-   md->rmd320.state[9] += eee;
-
-   return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int rmd320_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _rmd320_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 27 + sizeof(int));
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int rmd320_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->rmd320.state[0] = 0x67452301UL;
-   md->rmd320.state[1] = 0xefcdab89UL;
-   md->rmd320.state[2] = 0x98badcfeUL;
-   md->rmd320.state[3] = 0x10325476UL;
-   md->rmd320.state[4] = 0xc3d2e1f0UL;
-   md->rmd320.state[5] = 0x76543210UL;
-   md->rmd320.state[6] = 0xfedcba98UL;
-   md->rmd320.state[7] = 0x89abcdefUL;
-   md->rmd320.state[8] = 0x01234567UL;
-   md->rmd320.state[9] = 0x3c2d1e0fUL;
-   md->rmd320.curlen   = 0;
-   md->rmd320.length   = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(rmd320_process, rmd320_compress, rmd320, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (20 bytes)
-   @return CRYPT_OK if successful
-*/
-int rmd320_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->rmd320.curlen >= sizeof(md->rmd320.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* increase the length of the message */
-    md->rmd320.length += md->rmd320.curlen * 8;
-
-    /* append the '1' bit */
-    md->rmd320.buf[md->rmd320.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->rmd320.curlen > 56) {
-        while (md->rmd320.curlen < 64) {
-            md->rmd320.buf[md->rmd320.curlen++] = (unsigned char)0;
-        }
-        rmd320_compress(md, md->rmd320.buf);
-        md->rmd320.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->rmd320.curlen < 56) {
-        md->rmd320.buf[md->rmd320.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64L(md->rmd320.length, md->rmd320.buf+56);
-    rmd320_compress(md, md->rmd320.buf);
-
-    /* copy output */
-    for (i = 0; i < 10; i++) {
-        STORE32L(md->rmd320.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int rmd320_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   static const struct {
-        const char *msg;
-        unsigned char hash[40];
-   } tests[] = {
-   { "",
-     { 0x22, 0xd6, 0x5d, 0x56, 0x61, 0x53, 0x6c, 0xdc, 0x75, 0xc1,
-       0xfd, 0xf5, 0xc6, 0xde, 0x7b, 0x41, 0xb9, 0xf2, 0x73, 0x25,
-       0xeb, 0xc6, 0x1e, 0x85, 0x57, 0x17, 0x7d, 0x70, 0x5a, 0x0e,
-       0xc8, 0x80, 0x15, 0x1c, 0x3a, 0x32, 0xa0, 0x08, 0x99, 0xb8 }
-   },
-   { "a",
-     { 0xce, 0x78, 0x85, 0x06, 0x38, 0xf9, 0x26, 0x58, 0xa5, 0xa5,
-       0x85, 0x09, 0x75, 0x79, 0x92, 0x6d, 0xda, 0x66, 0x7a, 0x57,
-       0x16, 0x56, 0x2c, 0xfc, 0xf6, 0xfb, 0xe7, 0x7f, 0x63, 0x54,
-       0x2f, 0x99, 0xb0, 0x47, 0x05, 0xd6, 0x97, 0x0d, 0xff, 0x5d }
-   },
-   { "abc",
-     { 0xde, 0x4c, 0x01, 0xb3, 0x05, 0x4f, 0x89, 0x30, 0xa7, 0x9d,
-       0x09, 0xae, 0x73, 0x8e, 0x92, 0x30, 0x1e, 0x5a, 0x17, 0x08,
-       0x5b, 0xef, 0xfd, 0xc1, 0xb8, 0xd1, 0x16, 0x71, 0x3e, 0x74,
-       0xf8, 0x2f, 0xa9, 0x42, 0xd6, 0x4c, 0xdb, 0xc4, 0x68, 0x2d }
-   },
-   { "message digest",
-     { 0x3a, 0x8e, 0x28, 0x50, 0x2e, 0xd4, 0x5d, 0x42, 0x2f, 0x68,
-       0x84, 0x4f, 0x9d, 0xd3, 0x16, 0xe7, 0xb9, 0x85, 0x33, 0xfa,
-       0x3f, 0x2a, 0x91, 0xd2, 0x9f, 0x84, 0xd4, 0x25, 0xc8, 0x8d,
-       0x6b, 0x4e, 0xff, 0x72, 0x7d, 0xf6, 0x6a, 0x7c, 0x01, 0x97 }
-   },
-   { "abcdefghijklmnopqrstuvwxyz",
-     { 0xca, 0xbd, 0xb1, 0x81, 0x0b, 0x92, 0x47, 0x0a, 0x20, 0x93,
-       0xaa, 0x6b, 0xce, 0x05, 0x95, 0x2c, 0x28, 0x34, 0x8c, 0xf4,
-       0x3f, 0xf6, 0x08, 0x41, 0x97, 0x51, 0x66, 0xbb, 0x40, 0xed,
-       0x23, 0x40, 0x04, 0xb8, 0x82, 0x44, 0x63, 0xe6, 0xb0, 0x09 }
-   },
-   { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-     { 0xd0, 0x34, 0xa7, 0x95, 0x0c, 0xf7, 0x22, 0x02, 0x1b, 0xa4,
-       0xb8, 0x4d, 0xf7, 0x69, 0xa5, 0xde, 0x20, 0x60, 0xe2, 0x59,
-       0xdf, 0x4c, 0x9b, 0xb4, 0xa4, 0x26, 0x8c, 0x0e, 0x93, 0x5b,
-       0xbc, 0x74, 0x70, 0xa9, 0x69, 0xc9, 0xd0, 0x72, 0xa1, 0xac }
-   }
-   };
-
-   int i;
-   unsigned char tmp[40];
-   hash_state md;
-
-   for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
-       rmd320_init(&md);
-       rmd320_process(&md, (unsigned char *)tests[i].msg, strlen(tests[i].msg));
-       rmd320_done(&md, tmp);
-       if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "RIPEMD320", i)) {
-          return CRYPT_FAIL_TESTVECTOR;
-       }
-   }
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/sha1.c b/libs/tomcrypt/src/hashes/sha1.c
deleted file mode 100644
index 41a830bac0db..000000000000
--- a/libs/tomcrypt/src/hashes/sha1.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file sha1.c
-  LTC_SHA1 code by Tom St Denis
-*/
-
-
-#ifdef LTC_SHA1
-
-const struct ltc_hash_descriptor sha1_desc =
-{
-    "sha1",
-    2,
-    20,
-    64,
-
-    /* OID */
-   { 1, 3, 14, 3, 2, 26,  },
-   6,
-
-    &sha1_init,
-    &sha1_process,
-    &sha1_done,
-    &sha1_test,
-    NULL
-};
-
-#define F0(x,y,z)  (z ^ (x & (y ^ z)))
-#define F1(x,y,z)  (x ^ y ^ z)
-#define F2(x,y,z)  ((x & y) | (z & (x | y)))
-#define F3(x,y,z)  (x ^ y ^ z)
-
-#ifdef LTC_CLEAN_STACK
-static int _sha1_compress(hash_state *md, unsigned char *buf)
-#else
-static int  sha1_compress(hash_state *md, unsigned char *buf)
-#endif
-{
-    ulong32 a,b,c,d,e,W[80],i;
-#ifdef LTC_SMALL_CODE
-    ulong32 t;
-#endif
-
-    /* copy the state into 512-bits into W[0..15] */
-    for (i = 0; i < 16; i++) {
-        LOAD32H(W[i], buf + (4*i));
-    }
-
-    /* copy state */
-    a = md->sha1.state[0];
-    b = md->sha1.state[1];
-    c = md->sha1.state[2];
-    d = md->sha1.state[3];
-    e = md->sha1.state[4];
-
-    /* expand it */
-    for (i = 16; i < 80; i++) {
-        W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
-    }
-
-    /* compress */
-    /* round one */
-    #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30);
-    #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30);
-    #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30);
-    #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30);
-
-#ifdef LTC_SMALL_CODE
-
-    for (i = 0; i < 20; ) {
-       FF0(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t;
-    }
-
-    for (; i < 40; ) {
-       FF1(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t;
-    }
-
-    for (; i < 60; ) {
-       FF2(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t;
-    }
-
-    for (; i < 80; ) {
-       FF3(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t;
-    }
-
-#else
-
-    for (i = 0; i < 20; ) {
-       FF0(a,b,c,d,e,i++);
-       FF0(e,a,b,c,d,i++);
-       FF0(d,e,a,b,c,i++);
-       FF0(c,d,e,a,b,i++);
-       FF0(b,c,d,e,a,i++);
-    }
-
-    /* round two */
-    for (; i < 40; )  {
-       FF1(a,b,c,d,e,i++);
-       FF1(e,a,b,c,d,i++);
-       FF1(d,e,a,b,c,i++);
-       FF1(c,d,e,a,b,i++);
-       FF1(b,c,d,e,a,i++);
-    }
-
-    /* round three */
-    for (; i < 60; )  {
-       FF2(a,b,c,d,e,i++);
-       FF2(e,a,b,c,d,i++);
-       FF2(d,e,a,b,c,i++);
-       FF2(c,d,e,a,b,i++);
-       FF2(b,c,d,e,a,i++);
-    }
-
-    /* round four */
-    for (; i < 80; )  {
-       FF3(a,b,c,d,e,i++);
-       FF3(e,a,b,c,d,i++);
-       FF3(d,e,a,b,c,i++);
-       FF3(c,d,e,a,b,i++);
-       FF3(b,c,d,e,a,i++);
-    }
-#endif
-
-    #undef FF0
-    #undef FF1
-    #undef FF2
-    #undef FF3
-
-    /* store */
-    md->sha1.state[0] = md->sha1.state[0] + a;
-    md->sha1.state[1] = md->sha1.state[1] + b;
-    md->sha1.state[2] = md->sha1.state[2] + c;
-    md->sha1.state[3] = md->sha1.state[3] + d;
-    md->sha1.state[4] = md->sha1.state[4] + e;
-
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int sha1_compress(hash_state *md, unsigned char *buf)
-{
-   int err;
-   err = _sha1_compress(md, buf);
-   burn_stack(sizeof(ulong32) * 87);
-   return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha1_init(hash_state * md)
-{
-   LTC_ARGCHK(md != NULL);
-   md->sha1.state[0] = 0x67452301UL;
-   md->sha1.state[1] = 0xefcdab89UL;
-   md->sha1.state[2] = 0x98badcfeUL;
-   md->sha1.state[3] = 0x10325476UL;
-   md->sha1.state[4] = 0xc3d2e1f0UL;
-   md->sha1.curlen = 0;
-   md->sha1.length = 0;
-   return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(sha1_process, sha1_compress, sha1, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (20 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha1_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->sha1.curlen >= sizeof(md->sha1.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-    /* increase the length of the message */
-    md->sha1.length += md->sha1.curlen * 8;
-
-    /* append the '1' bit */
-    md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->sha1.curlen > 56) {
-        while (md->sha1.curlen < 64) {
-            md->sha1.buf[md->sha1.curlen++] = (unsigned char)0;
-        }
-        sha1_compress(md, md->sha1.buf);
-        md->sha1.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->sha1.curlen < 56) {
-        md->sha1.buf[md->sha1.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64H(md->sha1.length, md->sha1.buf+56);
-    sha1_compress(md, md->sha1.buf);
-
-    /* copy output */
-    for (i = 0; i < 5; i++) {
-        STORE32H(md->sha1.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha1_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[20];
-  } tests[] = {
-    { "abc",
-      { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a,
-        0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c,
-        0x9c, 0xd0, 0xd8, 0x9d }
-    },
-    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-      { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E,
-        0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5,
-        0xE5, 0x46, 0x70, 0xF1 }
-    }
-  };
-
-  int i;
-  unsigned char tmp[20];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0]));  i++) {
-      sha1_init(&md);
-      sha1_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha1_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA1", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
-  #endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/sha2/sha224.c b/libs/tomcrypt/src/hashes/sha2/sha224.c
deleted file mode 100644
index 4a063a74f74a..000000000000
--- a/libs/tomcrypt/src/hashes/sha2/sha224.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**
-   @param sha224.c
-   LTC_SHA-224 new NIST standard based off of LTC_SHA-256 truncated to 224 bits (Tom St Denis)
-*/
-
-#include "tomcrypt.h"
-
-#if defined(LTC_SHA224) && defined(LTC_SHA256)
-
-const struct ltc_hash_descriptor sha224_desc =
-{
-    "sha224",
-    10,
-    28,
-    64,
-
-    /* OID */
-   { 2, 16, 840, 1, 101, 3, 4, 2, 4,  },
-   9,
-
-    &sha224_init,
-    &sha256_process,
-    &sha224_done,
-    &sha224_test,
-    NULL
-};
-
-/* init the sha256 er... sha224 state ;-) */
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha224_init(hash_state * md)
-{
-    LTC_ARGCHK(md != NULL);
-
-    md->sha256.curlen = 0;
-    md->sha256.length = 0;
-    md->sha256.state[0] = 0xc1059ed8UL;
-    md->sha256.state[1] = 0x367cd507UL;
-    md->sha256.state[2] = 0x3070dd17UL;
-    md->sha256.state[3] = 0xf70e5939UL;
-    md->sha256.state[4] = 0xffc00b31UL;
-    md->sha256.state[5] = 0x68581511UL;
-    md->sha256.state[6] = 0x64f98fa7UL;
-    md->sha256.state[7] = 0xbefa4fa4UL;
-    return CRYPT_OK;
-}
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (28 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha224_done(hash_state * md, unsigned char *out)
-{
-    unsigned char buf[32];
-    int err;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    err = sha256_done(md, buf);
-    XMEMCPY(out, buf, 28);
-#ifdef LTC_CLEAN_STACK
-    zeromem(buf, sizeof(buf));
-#endif
-    return err;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha224_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[28];
-  } tests[] = {
-    { "abc",
-      { 0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8,
-        0x22, 0x86, 0x42, 0xa4, 0x77, 0xbd, 0xa2,
-        0x55, 0xb3, 0x2a, 0xad, 0xbc, 0xe4, 0xbd,
-        0xa0, 0xb3, 0xf7, 0xe3, 0x6c, 0x9d, 0xa7 }
-    },
-    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-      { 0x75, 0x38, 0x8b, 0x16, 0x51, 0x27, 0x76,
-        0xcc, 0x5d, 0xba, 0x5d, 0xa1, 0xfd, 0x89,
-        0x01, 0x50, 0xb0, 0xc6, 0x45, 0x5c, 0xb4,
-        0xf5, 0x8b, 0x19, 0x52, 0x52, 0x25, 0x25 }
-    },
-  };
-
-  int i;
-  unsigned char tmp[28];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-      sha224_init(&md);
-      sha224_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha224_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA224", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif /* defined(LTC_SHA224) && defined(LTC_SHA256) */
diff --git a/libs/tomcrypt/src/hashes/sha2/sha256.c b/libs/tomcrypt/src/hashes/sha2/sha256.c
deleted file mode 100644
index 693e74b104fd..000000000000
--- a/libs/tomcrypt/src/hashes/sha2/sha256.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file sha256.c
-  LTC_SHA256 by Tom St Denis
-*/
-
-#ifdef LTC_SHA256
-
-const struct ltc_hash_descriptor sha256_desc =
-{
-    "sha256",
-    0,
-    32,
-    64,
-
-    /* OID */
-   { 2, 16, 840, 1, 101, 3, 4, 2, 1,  },
-   9,
-
-    &sha256_init,
-    &sha256_process,
-    &sha256_done,
-    &sha256_test,
-    NULL
-};
-
-#ifdef LTC_SMALL_CODE
-/* the K array */
-static const ulong32 K[64] = {
-    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
-    0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
-    0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
-    0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
-    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
-    0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
-    0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
-    0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
-    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
-    0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
-    0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
-    0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
-    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
-};
-#endif
-
-/* Various logical functions */
-#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
-#define Maj(x,y,z)      (((x | y) & z) | (x & y))
-#define S(x, n)         RORc((x),(n))
-#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
-#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
-#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
-#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
-#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
-
-/* compress 512-bits */
-#ifdef LTC_CLEAN_STACK
-static int _sha256_compress(hash_state * md, unsigned char *buf)
-#else
-static int  sha256_compress(hash_state * md, unsigned char *buf)
-#endif
-{
-    ulong32 S[8], W[64], t0, t1;
-#ifdef LTC_SMALL_CODE
-    ulong32 t;
-#endif
-    int i;
-
-    /* copy state into S */
-    for (i = 0; i < 8; i++) {
-        S[i] = md->sha256.state[i];
-    }
-
-    /* copy the state into 512-bits into W[0..15] */
-    for (i = 0; i < 16; i++) {
-        LOAD32H(W[i], buf + (4*i));
-    }
-
-    /* fill W[16..63] */
-    for (i = 16; i < 64; i++) {
-        W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
-    }
-
-    /* Compress */
-#ifdef LTC_SMALL_CODE
-#define RND(a,b,c,d,e,f,g,h,i)                         \
-     t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   \
-     t1 = Sigma0(a) + Maj(a, b, c);                    \
-     d += t0;                                          \
-     h  = t0 + t1;
-
-     for (i = 0; i < 64; ++i) {
-         RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
-         t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
-         S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
-     }
-#else
-#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
-     t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i];   \
-     t1 = Sigma0(a) + Maj(a, b, c);                  \
-     d += t0;                                        \
-     h  = t0 + t1;
-
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
-    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
-    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
-    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
-    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
-    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
-    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
-    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
-    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
-
-#undef RND
-
-#endif
-
-    /* feedback */
-    for (i = 0; i < 8; i++) {
-        md->sha256.state[i] = md->sha256.state[i] + S[i];
-    }
-    return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int sha256_compress(hash_state * md, unsigned char *buf)
-{
-    int err;
-    err = _sha256_compress(md, buf);
-    burn_stack(sizeof(ulong32) * 74);
-    return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha256_init(hash_state * md)
-{
-    LTC_ARGCHK(md != NULL);
-
-    md->sha256.curlen = 0;
-    md->sha256.length = 0;
-    md->sha256.state[0] = 0x6A09E667UL;
-    md->sha256.state[1] = 0xBB67AE85UL;
-    md->sha256.state[2] = 0x3C6EF372UL;
-    md->sha256.state[3] = 0xA54FF53AUL;
-    md->sha256.state[4] = 0x510E527FUL;
-    md->sha256.state[5] = 0x9B05688CUL;
-    md->sha256.state[6] = 0x1F83D9ABUL;
-    md->sha256.state[7] = 0x5BE0CD19UL;
-    return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(sha256_process, sha256_compress, sha256, 64)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (32 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha256_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->sha256.curlen >= sizeof(md->sha256.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-
-    /* increase the length of the message */
-    md->sha256.length += md->sha256.curlen * 8;
-
-    /* append the '1' bit */
-    md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 56 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->sha256.curlen > 56) {
-        while (md->sha256.curlen < 64) {
-            md->sha256.buf[md->sha256.curlen++] = (unsigned char)0;
-        }
-        sha256_compress(md, md->sha256.buf);
-        md->sha256.curlen = 0;
-    }
-
-    /* pad upto 56 bytes of zeroes */
-    while (md->sha256.curlen < 56) {
-        md->sha256.buf[md->sha256.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64H(md->sha256.length, md->sha256.buf+56);
-    sha256_compress(md, md->sha256.buf);
-
-    /* copy output */
-    for (i = 0; i < 8; i++) {
-        STORE32H(md->sha256.state[i], out+(4*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha256_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[32];
-  } tests[] = {
-    { "abc",
-      { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
-        0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
-        0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
-        0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }
-    },
-    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-      { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
-        0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
-        0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
-        0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }
-    },
-  };
-
-  int i;
-  unsigned char tmp[32];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-      sha256_init(&md);
-      sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha256_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA256", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/sha2/sha384.c b/libs/tomcrypt/src/hashes/sha2/sha384.c
deleted file mode 100644
index f68d875094b6..000000000000
--- a/libs/tomcrypt/src/hashes/sha2/sha384.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**
-   @param sha384.c
-   LTC_SHA384 hash included in sha512.c, Tom St Denis
-*/
-
-#include "tomcrypt.h"
-
-#if defined(LTC_SHA384) && defined(LTC_SHA512)
-
-const struct ltc_hash_descriptor sha384_desc =
-{
-    "sha384",
-    4,
-    48,
-    128,
-
-    /* OID */
-   { 2, 16, 840, 1, 101, 3, 4, 2, 2,  },
-   9,
-
-    &sha384_init,
-    &sha512_process,
-    &sha384_done,
-    &sha384_test,
-    NULL
-};
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha384_init(hash_state * md)
-{
-    LTC_ARGCHK(md != NULL);
-
-    md->sha512.curlen = 0;
-    md->sha512.length = 0;
-    md->sha512.state[0] = CONST64(0xcbbb9d5dc1059ed8);
-    md->sha512.state[1] = CONST64(0x629a292a367cd507);
-    md->sha512.state[2] = CONST64(0x9159015a3070dd17);
-    md->sha512.state[3] = CONST64(0x152fecd8f70e5939);
-    md->sha512.state[4] = CONST64(0x67332667ffc00b31);
-    md->sha512.state[5] = CONST64(0x8eb44a8768581511);
-    md->sha512.state[6] = CONST64(0xdb0c2e0d64f98fa7);
-    md->sha512.state[7] = CONST64(0x47b5481dbefa4fa4);
-    return CRYPT_OK;
-}
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (48 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha384_done(hash_state * md, unsigned char *out)
-{
-   unsigned char buf[64];
-
-   LTC_ARGCHK(md  != NULL);
-   LTC_ARGCHK(out != NULL);
-
-    if (md->sha512.curlen >= sizeof(md->sha512.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-   sha512_done(md, buf);
-   XMEMCPY(out, buf, 48);
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, sizeof(buf));
-#endif
-   return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha384_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[48];
-  } tests[] = {
-    { "abc",
-      { 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
-        0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
-        0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
-        0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
-        0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
-        0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 }
-    },
-    { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
-      { 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8,
-        0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47,
-        0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2,
-        0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12,
-        0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9,
-        0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 }
-    },
-  };
-
-  int i;
-  unsigned char tmp[48];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-      sha384_init(&md);
-      sha384_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha384_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA384", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */
diff --git a/libs/tomcrypt/src/hashes/sha2/sha512.c b/libs/tomcrypt/src/hashes/sha2/sha512.c
deleted file mode 100644
index 7c5e81bc1df1..000000000000
--- a/libs/tomcrypt/src/hashes/sha2/sha512.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @param sha512.c
-   LTC_SHA512 by Tom St Denis
-*/
-
-#ifdef LTC_SHA512
-
-const struct ltc_hash_descriptor sha512_desc =
-{
-    "sha512",
-    5,
-    64,
-    128,
-
-    /* OID */
-   { 2, 16, 840, 1, 101, 3, 4, 2, 3,  },
-   9,
-
-    &sha512_init,
-    &sha512_process,
-    &sha512_done,
-    &sha512_test,
-    NULL
-};
-
-/* the K array */
-static const ulong64 K[80] = {
-CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
-CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
-CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
-CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
-CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
-CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
-CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
-CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
-CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
-CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
-CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
-CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
-CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
-CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
-CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
-CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
-CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
-CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
-CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
-CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
-CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
-CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
-CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
-CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
-CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
-CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
-CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
-CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
-CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
-CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
-CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
-CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
-CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
-CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
-CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
-CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
-CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
-CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
-CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
-CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
-};
-
-/* Various logical functions */
-#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
-#define Maj(x,y,z)      (((x | y) & z) | (x & y))
-#define S(x, n)         ROR64c(x, n)
-#define R(x, n)         (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)n))
-#define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
-#define Sigma1(x)       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
-#define Gamma0(x)       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
-#define Gamma1(x)       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
-
-/* compress 1024-bits */
-#ifdef LTC_CLEAN_STACK
-static int _sha512_compress(hash_state * md, unsigned char *buf)
-#else
-static int  sha512_compress(hash_state * md, unsigned char *buf)
-#endif
-{
-    ulong64 S[8], W[80], t0, t1;
-    int i;
-
-    /* copy state into S */
-    for (i = 0; i < 8; i++) {
-        S[i] = md->sha512.state[i];
-    }
-
-    /* copy the state into 1024-bits into W[0..15] */
-    for (i = 0; i < 16; i++) {
-        LOAD64H(W[i], buf + (8*i));
-    }
-
-    /* fill W[16..79] */
-    for (i = 16; i < 80; i++) {
-        W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
-    }
-
-    /* Compress */
-#ifdef LTC_SMALL_CODE
-    for (i = 0; i < 80; i++) {
-        t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
-        t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
-        S[7] = S[6];
-        S[6] = S[5];
-        S[5] = S[4];
-        S[4] = S[3] + t0;
-        S[3] = S[2];
-        S[2] = S[1];
-        S[1] = S[0];
-        S[0] = t0 + t1;
-    }
-#else
-#define RND(a,b,c,d,e,f,g,h,i)                    \
-     t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   \
-     t1 = Sigma0(a) + Maj(a, b, c);                  \
-     d += t0;                                        \
-     h  = t0 + t1;
-
-    for (i = 0; i < 80; i += 8) {
-        RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
-        RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
-        RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
-        RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
-        RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
-        RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
-        RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
-        RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
-    }
-#endif
-
-
-    /* feedback */
-    for (i = 0; i < 8; i++) {
-        md->sha512.state[i] = md->sha512.state[i] + S[i];
-    }
-
-    return CRYPT_OK;
-}
-
-/* compress 1024-bits */
-#ifdef LTC_CLEAN_STACK
-static int sha512_compress(hash_state * md, unsigned char *buf)
-{
-    int err;
-    err = _sha512_compress(md, buf);
-    burn_stack(sizeof(ulong64) * 90 + sizeof(int));
-    return err;
-}
-#endif
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha512_init(hash_state * md)
-{
-    LTC_ARGCHK(md != NULL);
-    md->sha512.curlen = 0;
-    md->sha512.length = 0;
-    md->sha512.state[0] = CONST64(0x6a09e667f3bcc908);
-    md->sha512.state[1] = CONST64(0xbb67ae8584caa73b);
-    md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b);
-    md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1);
-    md->sha512.state[4] = CONST64(0x510e527fade682d1);
-    md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f);
-    md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b);
-    md->sha512.state[7] = CONST64(0x5be0cd19137e2179);
-    return CRYPT_OK;
-}
-
-/**
-   Process a block of memory though the hash
-   @param md     The hash state
-   @param in     The data to hash
-   @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
-*/
-HASH_PROCESS(sha512_process, sha512_compress, sha512, 128)
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (64 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha512_done(hash_state * md, unsigned char *out)
-{
-    int i;
-
-    LTC_ARGCHK(md  != NULL);
-    LTC_ARGCHK(out != NULL);
-
-    if (md->sha512.curlen >= sizeof(md->sha512.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-    /* increase the length of the message */
-    md->sha512.length += md->sha512.curlen * CONST64(8);
-
-    /* append the '1' bit */
-    md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
-
-    /* if the length is currently above 112 bytes we append zeros
-     * then compress.  Then we can fall back to padding zeros and length
-     * encoding like normal.
-     */
-    if (md->sha512.curlen > 112) {
-        while (md->sha512.curlen < 128) {
-            md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
-        }
-        sha512_compress(md, md->sha512.buf);
-        md->sha512.curlen = 0;
-    }
-
-    /* pad upto 120 bytes of zeroes
-     * note: that from 112 to 120 is the 64 MSB of the length.  We assume that you won't hash
-     * > 2^64 bits of data... :-)
-     */
-    while (md->sha512.curlen < 120) {
-        md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
-    }
-
-    /* store length */
-    STORE64H(md->sha512.length, md->sha512.buf+120);
-    sha512_compress(md, md->sha512.buf);
-
-    /* copy output */
-    for (i = 0; i < 8; i++) {
-        STORE64H(md->sha512.state[i], out+(8*i));
-    }
-#ifdef LTC_CLEAN_STACK
-    zeromem(md, sizeof(hash_state));
-#endif
-    return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha512_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[64];
-  } tests[] = {
-    { "abc",
-     { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
-       0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
-       0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
-       0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
-       0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
-       0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
-       0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
-       0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f }
-    },
-    { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
-     { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
-       0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
-       0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
-       0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
-       0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
-       0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
-       0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
-       0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 }
-    },
-  };
-
-  int i;
-  unsigned char tmp[64];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-      sha512_init(&md);
-      sha512_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha512_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA512", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
-  #endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/hashes/sha2/sha512_224.c b/libs/tomcrypt/src/hashes/sha2/sha512_224.c
deleted file mode 100644
index 7fbab4bb4e1e..000000000000
--- a/libs/tomcrypt/src/hashes/sha2/sha512_224.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**
-   @param sha512_224.c
-   SHA512/224 hash included in sha512.c
-*/
-
-#include "tomcrypt.h"
-
-#if defined(LTC_SHA512_224) && defined(LTC_SHA512)
-
-const struct ltc_hash_descriptor sha512_224_desc =
-{
-    "sha512-224",
-    15,
-    28,
-    128,
-
-    /* OID */
-   { 2, 16, 840, 1, 101, 3, 4, 2, 5,  },
-   9,
-
-    &sha512_224_init,
-    &sha512_process,
-    &sha512_224_done,
-    &sha512_224_test,
-    NULL
-};
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha512_224_init(hash_state * md)
-{
-    LTC_ARGCHK(md != NULL);
-
-    md->sha512.curlen = 0;
-    md->sha512.length = 0;
-    md->sha512.state[0] = CONST64(0x8C3D37C819544DA2);
-    md->sha512.state[1] = CONST64(0x73E1996689DCD4D6);
-    md->sha512.state[2] = CONST64(0x1DFAB7AE32FF9C82);
-    md->sha512.state[3] = CONST64(0x679DD514582F9FCF);
-    md->sha512.state[4] = CONST64(0x0F6D2B697BD44DA8);
-    md->sha512.state[5] = CONST64(0x77E36F7304C48942);
-    md->sha512.state[6] = CONST64(0x3F9D85A86A1D36C8);
-    md->sha512.state[7] = CONST64(0x1112E6AD91D692A1);
-    return CRYPT_OK;
-}
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (48 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha512_224_done(hash_state * md, unsigned char *out)
-{
-   unsigned char buf[64];
-
-   LTC_ARGCHK(md  != NULL);
-   LTC_ARGCHK(out != NULL);
-
-    if (md->sha512.curlen >= sizeof(md->sha512.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-   sha512_done(md, buf);
-   XMEMCPY(out, buf, 28);
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, sizeof(buf));
-#endif
-   return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha512_224_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[28];
-  } tests[] = {
-    { "abc",
-      { 0x46, 0x34, 0x27, 0x0F, 0x70, 0x7B, 0x6A, 0x54,
-        0xDA, 0xAE, 0x75, 0x30, 0x46, 0x08, 0x42, 0xE2,
-        0x0E, 0x37, 0xED, 0x26, 0x5C, 0xEE, 0xE9, 0xA4,
-        0x3E, 0x89, 0x24, 0xAA }
-    },
-    { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
-      { 0x23, 0xFE, 0xC5, 0xBB, 0x94, 0xD6, 0x0B, 0x23,
-        0x30, 0x81, 0x92, 0x64, 0x0B, 0x0C, 0x45, 0x33,
-        0x35, 0xD6, 0x64, 0x73, 0x4F, 0xE4, 0x0E, 0x72,
-        0x68, 0x67, 0x4A, 0xF9 }
-    },
-  };
-
-  int i;
-  unsigned char tmp[28];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-      sha512_224_init(&md);
-      sha512_224_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha512_224_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA512-224", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */
diff --git a/libs/tomcrypt/src/hashes/sha2/sha512_256.c b/libs/tomcrypt/src/hashes/sha2/sha512_256.c
deleted file mode 100644
index 988213274dcf..000000000000
--- a/libs/tomcrypt/src/hashes/sha2/sha512_256.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-/**
-   @param sha512_256.c
-   SHA512/256 hash included in sha512.c
-*/
-
-#include "tomcrypt.h"
-
-#if defined(LTC_SHA512_256) && defined(LTC_SHA512)
-
-const struct ltc_hash_descriptor sha512_256_desc =
-{
-    "sha512-256",
-    16,
-    32,
-    128,
-
-    /* OID */
-   { 2, 16, 840, 1, 101, 3, 4, 2, 6,  },
-   9,
-
-    &sha512_256_init,
-    &sha512_process,
-    &sha512_256_done,
-    &sha512_256_test,
-    NULL
-};
-
-/**
-   Initialize the hash state
-   @param md   The hash state you wish to initialize
-   @return CRYPT_OK if successful
-*/
-int sha512_256_init(hash_state * md)
-{
-    LTC_ARGCHK(md != NULL);
-
-    md->sha512.curlen = 0;
-    md->sha512.length = 0;
-    md->sha512.state[0] = CONST64(0x22312194FC2BF72C);
-    md->sha512.state[1] = CONST64(0x9F555FA3C84C64C2);
-    md->sha512.state[2] = CONST64(0x2393B86B6F53B151);
-    md->sha512.state[3] = CONST64(0x963877195940EABD);
-    md->sha512.state[4] = CONST64(0x96283EE2A88EFFE3);
-    md->sha512.state[5] = CONST64(0xBE5E1E2553863992);
-    md->sha512.state[6] = CONST64(0x2B0199FC2C85B8AA);
-    md->sha512.state[7] = CONST64(0x0EB72DDC81C52CA2);
-    return CRYPT_OK;
-}
-
-/**
-   Terminate the hash to get the digest
-   @param md  The hash state
-   @param out [out] The destination of the hash (48 bytes)
-   @return CRYPT_OK if successful
-*/
-int sha512_256_done(hash_state * md, unsigned char *out)
-{
-   unsigned char buf[64];
-
-   LTC_ARGCHK(md  != NULL);
-   LTC_ARGCHK(out != NULL);
-
-    if (md->sha512.curlen >= sizeof(md->sha512.buf)) {
-       return CRYPT_INVALID_ARG;
-    }
-
-   sha512_done(md, buf);
-   XMEMCPY(out, buf, 32);
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, sizeof(buf));
-#endif
-   return CRYPT_OK;
-}
-
-/**
-  Self-test the hash
-  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int  sha512_256_test(void)
-{
- #ifndef LTC_TEST
-    return CRYPT_NOP;
- #else
-  static const struct {
-      const char *msg;
-      unsigned char hash[32];
-  } tests[] = {
-    { "abc",
-      { 0x53, 0x04, 0x8E, 0x26, 0x81, 0x94, 0x1E, 0xF9,
-        0x9B, 0x2E, 0x29, 0xB7, 0x6B, 0x4C, 0x7D, 0xAB,
-        0xE4, 0xC2, 0xD0, 0xC6, 0x34, 0xFC, 0x6D, 0x46,
-        0xE0, 0xE2, 0xF1, 0x31, 0x07, 0xE7, 0xAF, 0x23 }
-    },
-    { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
-      { 0x39, 0x28, 0xE1, 0x84, 0xFB, 0x86, 0x90, 0xF8,
-        0x40, 0xDA, 0x39, 0x88, 0x12, 0x1D, 0x31, 0xBE,
-        0x65, 0xCB, 0x9D, 0x3E, 0xF8, 0x3E, 0xE6, 0x14,
-        0x6F, 0xEA, 0xC8, 0x61, 0xE1, 0x9B, 0x56, 0x3A }
-    },
-  };
-
-  int i;
-  unsigned char tmp[32];
-  hash_state md;
-
-  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
-      sha512_256_init(&md);
-      sha512_256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
-      sha512_256_done(&md, tmp);
-      if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "SHA512-265", i)) {
-         return CRYPT_FAIL_TESTVECTOR;
-      }
-  }
-  return CRYPT_OK;
- #endif
-}
-
-#endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */
diff --git a/libs/tomcrypt/src/hashes/sha3.c b/libs/tomcrypt/src/hashes/sha3.c
deleted file mode 100644
index 3b80a8e88587..000000000000
--- a/libs/tomcrypt/src/hashes/sha3.c
+++ /dev/null
@@ -1,302 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* based on https://github.com/brainhub/SHA3IUF (public domain) */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_SHA3
-
-const struct ltc_hash_descriptor sha3_224_desc =
-{
-   "sha3-224",                  /* name of hash */
-   17,                          /* internal ID */
-   28,                          /* Size of digest in octets */
-   144,                         /* Input block size in octets */
-   { 2,16,840,1,101,3,4,2,7 },  /* ASN.1 OID */
-   9,                           /* Length OID */
-   &sha3_224_init,
-   &sha3_process,
-   &sha3_done,
-   &sha3_224_test,
-   NULL
-};
-
-const struct ltc_hash_descriptor sha3_256_desc =
-{
-   "sha3-256",                  /* name of hash */
-   18,                          /* internal ID */
-   32,                          /* Size of digest in octets */
-   136,                         /* Input block size in octets */
-   { 2,16,840,1,101,3,4,2,8 },  /* ASN.1 OID */
-   9,                           /* Length OID */
-   &sha3_256_init,
-   &sha3_process,
-   &sha3_done,
-   &sha3_256_test,
-   NULL
-};
-
-const struct ltc_hash_descriptor sha3_384_desc =
-{
-   "sha3-384",                  /* name of hash */
-   19,                          /* internal ID */
-   48,                          /* Size of digest in octets */
-   104,                         /* Input block size in octets */
-   { 2,16,840,1,101,3,4,2,9 },  /* ASN.1 OID */
-   9,                           /* Length OID */
-   &sha3_384_init,
-   &sha3_process,
-   &sha3_done,
-   &sha3_384_test,
-   NULL
-};
-
-const struct ltc_hash_descriptor sha3_512_desc =
-{
-   "sha3-512",                  /* name of hash */
-   20,                          /* internal ID */
-   64,                          /* Size of digest in octets */
-   72,                          /* Input block size in octets */
-   { 2,16,840,1,101,3,4,2,10 }, /* ASN.1 OID */
-   9,                           /* Length OID */
-   &sha3_512_init,
-   &sha3_process,
-   &sha3_done,
-   &sha3_512_test,
-   NULL
-};
-
-#define SHA3_KECCAK_SPONGE_WORDS 25 /* 1600 bits > 200 bytes > 25 x ulong64 */
-#define SHA3_KECCAK_ROUNDS 24
-
-static const ulong64 keccakf_rndc[24] = {
-   CONST64(0x0000000000000001), CONST64(0x0000000000008082),
-   CONST64(0x800000000000808a), CONST64(0x8000000080008000),
-   CONST64(0x000000000000808b), CONST64(0x0000000080000001),
-   CONST64(0x8000000080008081), CONST64(0x8000000000008009),
-   CONST64(0x000000000000008a), CONST64(0x0000000000000088),
-   CONST64(0x0000000080008009), CONST64(0x000000008000000a),
-   CONST64(0x000000008000808b), CONST64(0x800000000000008b),
-   CONST64(0x8000000000008089), CONST64(0x8000000000008003),
-   CONST64(0x8000000000008002), CONST64(0x8000000000000080),
-   CONST64(0x000000000000800a), CONST64(0x800000008000000a),
-   CONST64(0x8000000080008081), CONST64(0x8000000000008080),
-   CONST64(0x0000000080000001), CONST64(0x8000000080008008)
-};
-
-static const unsigned keccakf_rotc[24] = {
-   1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
-};
-
-static const unsigned keccakf_piln[24] = {
-   10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
-};
-
-static void keccakf(ulong64 s[25])
-{
-   int i, j, round;
-   ulong64 t, bc[5];
-
-   for(round = 0; round < SHA3_KECCAK_ROUNDS; round++) {
-      /* Theta */
-      for(i = 0; i < 5; i++)
-         bc[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20];
-
-      for(i = 0; i < 5; i++) {
-         t = bc[(i + 4) % 5] ^ ROL64(bc[(i + 1) % 5], 1);
-         for(j = 0; j < 25; j += 5)
-            s[j + i] ^= t;
-      }
-      /* Rho Pi */
-      t = s[1];
-      for(i = 0; i < 24; i++) {
-         j = keccakf_piln[i];
-         bc[0] = s[j];
-         s[j] = ROL64(t, keccakf_rotc[i]);
-         t = bc[0];
-      }
-      /* Chi */
-      for(j = 0; j < 25; j += 5) {
-         for(i = 0; i < 5; i++)
-            bc[i] = s[j + i];
-         for(i = 0; i < 5; i++)
-            s[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
-      }
-      /* Iota */
-      s[0] ^= keccakf_rndc[round];
-   }
-}
-
-/* Public Inteface */
-
-int sha3_224_init(hash_state *md)
-{
-   LTC_ARGCHK(md != NULL);
-   XMEMSET(&md->sha3, 0, sizeof(md->sha3));
-   md->sha3.capacity_words = 2 * 224 / (8 * sizeof(ulong64));
-   return CRYPT_OK;
-}
-
-int sha3_256_init(hash_state *md)
-{
-   LTC_ARGCHK(md != NULL);
-   XMEMSET(&md->sha3, 0, sizeof(md->sha3));
-   md->sha3.capacity_words = 2 * 256 / (8 * sizeof(ulong64));
-   return CRYPT_OK;
-}
-
-int sha3_384_init(hash_state *md)
-{
-   LTC_ARGCHK(md != NULL);
-   XMEMSET(&md->sha3, 0, sizeof(md->sha3));
-   md->sha3.capacity_words = 2 * 384 / (8 * sizeof(ulong64));
-   return CRYPT_OK;
-}
-
-int sha3_512_init(hash_state *md)
-{
-   LTC_ARGCHK(md != NULL);
-   XMEMSET(&md->sha3, 0, sizeof(md->sha3));
-   md->sha3.capacity_words = 2 * 512 / (8 * sizeof(ulong64));
-   return CRYPT_OK;
-}
-
-int sha3_shake_init(hash_state *md, int num)
-{
-   LTC_ARGCHK(md != NULL);
-   if (num != 128 && num != 256) return CRYPT_INVALID_ARG;
-   XMEMSET(&md->sha3, 0, sizeof(md->sha3));
-   md->sha3.capacity_words = (unsigned short)(2 * num / (8 * sizeof(ulong64)));
-   return CRYPT_OK;
-}
-
-int sha3_process(hash_state *md, const unsigned char *in, unsigned long inlen)
-{
-   /* 0...7 -- how much is needed to have a word */
-   unsigned old_tail = (8 - md->sha3.byte_index) & 7;
-
-   unsigned long words;
-   unsigned tail;
-   unsigned long i;
-
-   if (inlen == 0) return CRYPT_OK; /* nothing to do */
-   LTC_ARGCHK(md != NULL);
-   LTC_ARGCHK(in != NULL);
-
-   if(inlen < old_tail) {       /* have no complete word or haven't started the word yet */
-      while (inlen--) md->sha3.saved |= (ulong64) (*(in++)) << ((md->sha3.byte_index++) * 8);
-      return CRYPT_OK;
-   }
-
-   if(old_tail) {               /* will have one word to process */
-      inlen -= old_tail;
-      while (old_tail--) md->sha3.saved |= (ulong64) (*(in++)) << ((md->sha3.byte_index++) * 8);
-      /* now ready to add saved to the sponge */
-      md->sha3.s[md->sha3.word_index] ^= md->sha3.saved;
-      md->sha3.byte_index = 0;
-      md->sha3.saved = 0;
-      if(++md->sha3.word_index == (SHA3_KECCAK_SPONGE_WORDS - md->sha3.capacity_words)) {
-         keccakf(md->sha3.s);
-         md->sha3.word_index = 0;
-      }
-   }
-
-   /* now work in full words directly from input */
-   words = inlen / sizeof(ulong64);
-   tail = inlen - words * sizeof(ulong64);
-
-   for(i = 0; i < words; i++, in += sizeof(ulong64)) {
-      ulong64 t;
-      LOAD64L(t, in);
-      md->sha3.s[md->sha3.word_index] ^= t;
-      if(++md->sha3.word_index == (SHA3_KECCAK_SPONGE_WORDS - md->sha3.capacity_words)) {
-         keccakf(md->sha3.s);
-         md->sha3.word_index = 0;
-      }
-   }
-
-   /* finally, save the partial word */
-   while (tail--) {
-      md->sha3.saved |= (ulong64) (*(in++)) << ((md->sha3.byte_index++) * 8);
-   }
-   return CRYPT_OK;
-}
-
-int sha3_done(hash_state *md, unsigned char *hash)
-{
-   unsigned i;
-
-   LTC_ARGCHK(md   != NULL);
-   LTC_ARGCHK(hash != NULL);
-
-   md->sha3.s[md->sha3.word_index] ^= (md->sha3.saved ^ (CONST64(0x06) << (md->sha3.byte_index * 8)));
-   md->sha3.s[SHA3_KECCAK_SPONGE_WORDS - md->sha3.capacity_words - 1] ^= CONST64(0x8000000000000000);
-   keccakf(md->sha3.s);
-
-   /* store sha3.s[] as little-endian bytes into sha3.sb */
-   for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) {
-      STORE64L(md->sha3.s[i], md->sha3.sb + i * 8);
-   }
-
-   XMEMCPY(hash, md->sha3.sb, md->sha3.capacity_words * 4);
-   return CRYPT_OK;
-}
-
-int sha3_shake_done(hash_state *md, unsigned char *out, unsigned long outlen)
-{
-   /* IMPORTANT NOTE: sha3_shake_done can be called many times */
-   unsigned long idx;
-   unsigned i;
-
-   if (outlen == 0) return CRYPT_OK; /* nothing to do */
-   LTC_ARGCHK(md  != NULL);
-   LTC_ARGCHK(out != NULL);
-
-   if (!md->sha3.xof_flag) {
-      /* shake_xof operation must be done only once */
-      md->sha3.s[md->sha3.word_index] ^= (md->sha3.saved ^ (CONST64(0x1F) << (md->sha3.byte_index * 8)));
-      md->sha3.s[SHA3_KECCAK_SPONGE_WORDS - md->sha3.capacity_words - 1] ^= CONST64(0x8000000000000000);
-      keccakf(md->sha3.s);
-      /* store sha3.s[] as little-endian bytes into sha3.sb */
-      for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) {
-         STORE64L(md->sha3.s[i], md->sha3.sb + i * 8);
-      }
-      md->sha3.byte_index = 0;
-      md->sha3.xof_flag = 1;
-   }
-
-   for (idx = 0; idx < outlen; idx++) {
-      if(md->sha3.byte_index >= (SHA3_KECCAK_SPONGE_WORDS - md->sha3.capacity_words) * 8) {
-         keccakf(md->sha3.s);
-         /* store sha3.s[] as little-endian bytes into sha3.sb */
-         for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) {
-            STORE64L(md->sha3.s[i], md->sha3.sb + i * 8);
-         }
-         md->sha3.byte_index = 0;
-      }
-      out[idx] = md->sha3.sb[md->sha3.byte_index++];
-   }
-   return CRYPT_OK;
-}
-
-int sha3_shake_memory(int num, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen)
-{
-   hash_state md;
-   int err;
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(out != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   if ((err = sha3_shake_init(&md, num))          != CRYPT_OK) return err;
-   if ((err = sha3_shake_process(&md, in, inlen)) != CRYPT_OK) return err;
-   if ((err = sha3_shake_done(&md, out, *outlen)) != CRYPT_OK) return err;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt.h b/libs/tomcrypt/src/headers/tomcrypt.h
deleted file mode 100644
index 4d348ca3e9a4..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#ifndef TOMCRYPT_H_
-#define TOMCRYPT_H_
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <time.h>
-#include <ctype.h>
-#include <limits.h>
-
-/* use configuration data */
-#include <tomcrypt_custom.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* version */
-#define CRYPT   0x0118
-#define SCRYPT  "1.18.2"
-
-/* max size of either a cipher/hash block or symmetric key [largest of the two] */
-#define MAXBLOCKSIZE  128
-
-#ifndef TAB_SIZE
-/* descriptor table size */
-#define TAB_SIZE      32
-#endif
-
-/* error codes [will be expanded in future releases] */
-enum {
-   CRYPT_OK=0,             /* Result OK */
-   CRYPT_ERROR,            /* Generic Error */
-   CRYPT_NOP,              /* Not a failure but no operation was performed */
-
-   CRYPT_INVALID_KEYSIZE,  /* Invalid key size given */
-   CRYPT_INVALID_ROUNDS,   /* Invalid number of rounds */
-   CRYPT_FAIL_TESTVECTOR,  /* Algorithm failed test vectors */
-
-   CRYPT_BUFFER_OVERFLOW,  /* Not enough space for output */
-   CRYPT_INVALID_PACKET,   /* Invalid input packet given */
-
-   CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */
-   CRYPT_ERROR_READPRNG,   /* Could not read enough from PRNG */
-
-   CRYPT_INVALID_CIPHER,   /* Invalid cipher specified */
-   CRYPT_INVALID_HASH,     /* Invalid hash specified */
-   CRYPT_INVALID_PRNG,     /* Invalid PRNG specified */
-
-   CRYPT_MEM,              /* Out of memory */
-
-   CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */
-   CRYPT_PK_NOT_PRIVATE,   /* Requires a private PK key */
-
-   CRYPT_INVALID_ARG,      /* Generic invalid argument */
-   CRYPT_FILE_NOTFOUND,    /* File Not Found */
-
-   CRYPT_PK_INVALID_TYPE,  /* Invalid type of PK key */
-
-   CRYPT_OVERFLOW,         /* An overflow of a value was detected/prevented */
-
-   CRYPT_UNUSED1,          /* UNUSED1 */
-
-   CRYPT_INPUT_TOO_LONG,   /* The input was longer than expected. */
-
-   CRYPT_PK_INVALID_SIZE,  /* Invalid size input for PK parameters */
-
-   CRYPT_INVALID_PRIME_SIZE,/* Invalid size of prime requested */
-   CRYPT_PK_INVALID_PADDING, /* Invalid padding on input */
-
-   CRYPT_HASH_OVERFLOW      /* Hash applied to too many bits */
-};
-
-#include <tomcrypt_cfg.h>
-#include <tomcrypt_macros.h>
-#include <tomcrypt_cipher.h>
-#include <tomcrypt_hash.h>
-#include <tomcrypt_mac.h>
-#include <tomcrypt_prng.h>
-#include <tomcrypt_pk.h>
-#include <tomcrypt_math.h>
-#include <tomcrypt_misc.h>
-#include <tomcrypt_argchk.h>
-#include <tomcrypt_pkcs.h>
-
-#ifdef __cplusplus
-   }
-#endif
-
-#endif /* TOMCRYPT_H_ */
diff --git a/libs/tomcrypt/src/headers/tomcrypt_argchk.h b/libs/tomcrypt/src/headers/tomcrypt_argchk.h
deleted file mode 100644
index d321368486b3..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_argchk.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Defines the LTC_ARGCHK macro used within the library */
-/* ARGTYPE is defined in tomcrypt_cfg.h */
-#if ARGTYPE == 0
-
-#include <signal.h>
-
-/* this is the default LibTomCrypt macro  */
-#if defined(__clang__) || defined(__GNUC_MINOR__)
-#define NORETURN __attribute__ ((noreturn))
-#else
-#define NORETURN
-#endif
-
-void crypt_argchk(const char *v, const char *s, int d) NORETURN;
-#define LTC_ARGCHK(x) do { if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } }while(0)
-#define LTC_ARGCHKVD(x) do { if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } }while(0)
-
-#elif ARGTYPE == 1
-
-/* fatal type of error */
-#define LTC_ARGCHK(x) assert((x))
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 2
-
-#define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); }
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 3
-
-#define LTC_ARGCHK(x)
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 4
-
-#define LTC_ARGCHK(x)   if (!(x)) return CRYPT_INVALID_ARG;
-#define LTC_ARGCHKVD(x) if (!(x)) return;
-
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt_cfg.h b/libs/tomcrypt/src/headers/tomcrypt_cfg.h
deleted file mode 100644
index ce9cc11542f1..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_cfg.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* This is the build config file.
- *
- * With this you can setup what to inlcude/exclude automatically during any build.  Just comment
- * out the line that #define's the word for the thing you want to remove.  phew!
- */
-
-#ifndef TOMCRYPT_CFG_H
-#define TOMCRYPT_CFG_H
-
-#if defined(_WIN32) || defined(_MSC_VER)
-   #define LTC_CALL __cdecl
-#elif !defined(LTC_CALL)
-   #define LTC_CALL
-#endif
-
-#ifndef LTC_EXPORT
-   #define LTC_EXPORT
-#endif
-
-/* certain platforms use macros for these, making the prototypes broken */
-#ifndef LTC_NO_PROTOTYPES
-
-/* you can change how memory allocation works ... */
-LTC_EXPORT void * LTC_CALL XMALLOC(size_t n);
-LTC_EXPORT void * LTC_CALL XREALLOC(void *p, size_t n);
-LTC_EXPORT void * LTC_CALL XCALLOC(size_t n, size_t s);
-LTC_EXPORT void LTC_CALL XFREE(void *p);
-
-LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
-
-
-/* change the clock function too */
-LTC_EXPORT clock_t LTC_CALL XCLOCK(void);
-
-/* various other functions */
-LTC_EXPORT void * LTC_CALL XMEMCPY(void *dest, const void *src, size_t n);
-LTC_EXPORT int   LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n);
-LTC_EXPORT void * LTC_CALL XMEMSET(void *s, int c, size_t n);
-
-LTC_EXPORT int   LTC_CALL XSTRCMP(const char *s1, const char *s2);
-
-#endif
-
-/* some compilers do not like "inline" (or maybe "static inline"), namely: HP cc, IBM xlc */
-#if defined(__HP_cc) || defined(__xlc__)
-   #define LTC_INLINE
-#elif defined(_MSC_VER)
-   #define LTC_INLINE __inline
-#else
-   #define LTC_INLINE inline
-#endif
-
-/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */
-#ifndef ARGTYPE
-   #define ARGTYPE  4
-#endif
-
-#undef LTC_ENCRYPT
-#define LTC_ENCRYPT 0
-#undef LTC_DECRYPT
-#define LTC_DECRYPT 1
-
-/* Controls endianess and size of registers.  Leave uncommented to get platform neutral [slower] code
- *
- * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes.
- * The x86 platforms allow this but some others [ARM for instance] do not.  On those platforms you **MUST**
- * use the portable [slower] macros.
- */
-/* detect x86/i386 32bit */
-#if defined(__i386__) || defined(__i386) || defined(_M_IX86)
-   #define ENDIAN_LITTLE
-   #define ENDIAN_32BITWORD
-   #define LTC_FAST
-#endif
-
-/* detect amd64/x64 */
-#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
-   #define ENDIAN_LITTLE
-   #define ENDIAN_64BITWORD
-   #define LTC_FAST
-#endif
-
-/* detect PPC32 */
-#if defined(LTC_PPC32)
-   #define ENDIAN_BIG
-   #define ENDIAN_32BITWORD
-   #define LTC_FAST
-#endif
-
-/* detects MIPS R5900 processors (PS2) */
-#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips))
-   #define ENDIAN_64BITWORD
-   #if defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
-     #define ENDIAN_BIG
-   #endif
-     #define ENDIAN_LITTLE
-   #endif
-#endif
-
-/* detect AIX */
-#if defined(_AIX) && defined(_BIG_ENDIAN)
-  #define ENDIAN_BIG
-  #if defined(__LP64__) || defined(_ARCH_PPC64)
-    #define ENDIAN_64BITWORD
-  #else
-    #define ENDIAN_32BITWORD
-  #endif
-#endif
-
-/* detect HP-UX */
-#if defined(__hpux) || defined(__hpux__)
-  #define ENDIAN_BIG
-  #if defined(__ia64) || defined(__ia64__) || defined(__LP64__)
-    #define ENDIAN_64BITWORD
-  #else
-    #define ENDIAN_32BITWORD
-  #endif
-#endif
-
-/* detect Apple OS X */
-#if defined(__APPLE__) && defined(__MACH__)
-  #if defined(__LITTLE_ENDIAN__) || defined(__x86_64__)
-    #define ENDIAN_LITTLE
-  #else
-    #define ENDIAN_BIG
-  #endif
-  #if defined(__LP64__) || defined(__x86_64__)
-    #define ENDIAN_64BITWORD
-  #else
-    #define ENDIAN_32BITWORD
-  #endif
-#endif
-
-/* detect SPARC and SPARC64 */
-#if defined(__sparc__) || defined(__sparc)
-  #define ENDIAN_BIG
-  #if defined(__arch64__) || defined(__sparcv9) || defined(__sparc_v9__)
-    #define ENDIAN_64BITWORD
-  #else
-    #define ENDIAN_32BITWORD
-  #endif
-#endif
-
-/* detect IBM S390(x) */
-#if defined(__s390x__) || defined(__s390__)
-  #define ENDIAN_BIG
-  #if defined(__s390x__)
-    #define ENDIAN_64BITWORD
-  #else
-    #define ENDIAN_32BITWORD
-  #endif
-#endif
-
-/* detect PPC64 */
-#if defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__)
-   #define ENDIAN_64BITWORD
-   #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-      #define ENDIAN_BIG
-   #elif  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-      #define ENDIAN_LITTLE
-   #endif
-   #define LTC_FAST
-#endif
-
-/* endianness fallback */
-#if !defined(ENDIAN_BIG) && !defined(ENDIAN_LITTLE)
-  #if defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN || \
-      defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
-      defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || \
-      defined(__BIG_ENDIAN__) || \
-      defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
-      defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
-    #define ENDIAN_BIG
-  #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN || \
-      defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
-      defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || \
-      defined(__LITTLE_ENDIAN__) || \
-      defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
-      defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
-    #define ENDIAN_LITTLE
-  #else
-    #error Cannot detect endianness
-  #endif
-#endif
-
-/* ulong64: 64-bit data type */
-#ifdef _MSC_VER
-   #define CONST64(n) n ## ui64
-   typedef unsigned __int64 ulong64;
-#else
-   #define CONST64(n) n ## ULL
-   typedef unsigned long long ulong64;
-#endif
-
-/* ulong32: "32-bit at least" data type */
-#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) || \
-    defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || \
-    defined(__s390x__) || defined(__arch64__) || defined(__aarch64__) || \
-    defined(__sparcv9) || defined(__sparc_v9__) || defined(__sparc64__) || \
-    defined(__ia64) || defined(__ia64__) || defined(__itanium__) || defined(_M_IA64) || \
-    defined(__LP64__) || defined(_LP64) || defined(__64BIT__)
-   typedef unsigned ulong32;
-   #if !defined(ENDIAN_64BITWORD) && !defined(ENDIAN_32BITWORD)
-     #define ENDIAN_64BITWORD
-   #endif
-#else
-   typedef unsigned long ulong32;
-   #if !defined(ENDIAN_64BITWORD) && !defined(ENDIAN_32BITWORD)
-     #define ENDIAN_32BITWORD
-   #endif
-#endif
-
-#if defined(ENDIAN_64BITWORD) && !defined(_MSC_VER)
-typedef unsigned long long ltc_mp_digit;
-#else
-typedef unsigned long ltc_mp_digit;
-#endif
-
-/* No asm is a quick way to disable anything "not portable" */
-#ifdef LTC_NO_ASM
-   #define ENDIAN_NEUTRAL
-   #undef ENDIAN_32BITWORD
-   #undef ENDIAN_64BITWORD
-   #undef LTC_FAST
-   #define LTC_NO_ROLC
-   #define LTC_NO_BSWAP
-#endif
-
-/* No LTC_FAST if: explicitly disabled OR non-gcc/non-clang compiler OR old gcc OR using -ansi -std=c99 */
-#if defined(LTC_NO_FAST) || (__GNUC__ < 4) || defined(__STRICT_ANSI__)
-   #undef LTC_FAST
-#endif
-
-#ifdef LTC_FAST
-   #define LTC_FAST_TYPE_PTR_CAST(x) ((LTC_FAST_TYPE*)(void*)(x))
-   #ifdef ENDIAN_64BITWORD
-   typedef ulong64 __attribute__((__may_alias__)) LTC_FAST_TYPE;
-   #else
-   typedef ulong32 __attribute__((__may_alias__)) LTC_FAST_TYPE;
-   #endif
-#endif
-
-#if !defined(ENDIAN_NEUTRAL) && (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD))
-   #error You must specify a word size as well as endianess in tomcrypt_cfg.h
-#endif
-
-#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE))
-   #define ENDIAN_NEUTRAL
-#endif
-
-#if (defined(ENDIAN_32BITWORD) && defined(ENDIAN_64BITWORD))
-   #error Cannot be 32 and 64 bit words...
-#endif
-
-/* gcc 4.3 and up has a bswap builtin; detect it by gcc version.
- * clang also supports the bswap builtin, and although clang pretends
- * to be gcc (macro-wise, anyway), clang pretends to be a version
- * prior to gcc 4.3, so we can't detect bswap that way.  Instead,
- * clang has a __has_builtin mechanism that can be used to check
- * for builtins:
- * http://clang.llvm.org/docs/LanguageExtensions.html#feature_check */
-#ifndef __has_builtin
-   #define __has_builtin(x) 0
-#endif
-#if !defined(LTC_NO_BSWAP) && defined(__GNUC__) &&                      \
-   ((__GNUC__ * 100 + __GNUC_MINOR__ >= 403) ||                         \
-    (__has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64)))
-   #define LTC_HAVE_BSWAP_BUILTIN
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt_cipher.h b/libs/tomcrypt/src/headers/tomcrypt_cipher.h
deleted file mode 100644
index c192093e4861..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_cipher.h
+++ /dev/null
@@ -1,1004 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* ---- SYMMETRIC KEY STUFF -----
- *
- * We put each of the ciphers scheduled keys in their own structs then we put all of
- * the key formats in one union.  This makes the function prototypes easier to use.
- */
-#ifdef LTC_BLOWFISH
-struct blowfish_key {
-   ulong32 S[4][256];
-   ulong32 K[18];
-};
-#endif
-
-#ifdef LTC_RC5
-struct rc5_key {
-   int rounds;
-   ulong32 K[50];
-};
-#endif
-
-#ifdef LTC_RC6
-struct rc6_key {
-   ulong32 K[44];
-};
-#endif
-
-#ifdef LTC_SAFERP
-struct saferp_key {
-   unsigned char K[33][16];
-   long rounds;
-};
-#endif
-
-#ifdef LTC_RIJNDAEL
-struct rijndael_key {
-   ulong32 eK[60], dK[60];
-   int Nr;
-};
-#endif
-
-#ifdef LTC_KSEED
-struct kseed_key {
-    ulong32 K[32], dK[32];
-};
-#endif
-
-#ifdef LTC_KASUMI
-struct kasumi_key {
-    ulong32 KLi1[8], KLi2[8],
-            KOi1[8], KOi2[8], KOi3[8],
-            KIi1[8], KIi2[8], KIi3[8];
-};
-#endif
-
-#ifdef LTC_XTEA
-struct xtea_key {
-   unsigned long A[32], B[32];
-};
-#endif
-
-#ifdef LTC_TWOFISH
-#ifndef LTC_TWOFISH_SMALL
-   struct twofish_key {
-      ulong32 S[4][256], K[40];
-   };
-#else
-   struct twofish_key {
-      ulong32 K[40];
-      unsigned char S[32], start;
-   };
-#endif
-#endif
-
-#ifdef LTC_SAFER
-#define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS     6
-#define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS   10
-#define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS    8
-#define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS  10
-#define LTC_SAFER_MAX_NOF_ROUNDS            13
-#define LTC_SAFER_BLOCK_LEN                  8
-#define LTC_SAFER_KEY_LEN     (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS))
-typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN];
-typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN];
-struct safer_key { safer_key_t key; };
-#endif
-
-#ifdef LTC_RC2
-struct rc2_key { unsigned xkey[64]; };
-#endif
-
-#ifdef LTC_DES
-struct des_key {
-    ulong32 ek[32], dk[32];
-};
-
-struct des3_key {
-    ulong32 ek[3][32], dk[3][32];
-};
-#endif
-
-#ifdef LTC_CAST5
-struct cast5_key {
-    ulong32 K[32], keylen;
-};
-#endif
-
-#ifdef LTC_NOEKEON
-struct noekeon_key {
-    ulong32 K[4], dK[4];
-};
-#endif
-
-#ifdef LTC_SKIPJACK
-struct skipjack_key {
-    unsigned char key[10];
-};
-#endif
-
-#ifdef LTC_KHAZAD
-struct khazad_key {
-   ulong64 roundKeyEnc[8 + 1];
-   ulong64 roundKeyDec[8 + 1];
-};
-#endif
-
-#ifdef LTC_ANUBIS
-struct anubis_key {
-   int keyBits;
-   int R;
-   ulong32 roundKeyEnc[18 + 1][4];
-   ulong32 roundKeyDec[18 + 1][4];
-};
-#endif
-
-#ifdef LTC_MULTI2
-struct multi2_key {
-    int N;
-    ulong32 uk[8];
-};
-#endif
-
-#ifdef LTC_CAMELLIA
-struct camellia_key {
-    int R;
-    ulong64 kw[4], k[24], kl[6];
-};
-#endif
-
-typedef union Symmetric_key {
-#ifdef LTC_DES
-   struct des_key des;
-   struct des3_key des3;
-#endif
-#ifdef LTC_RC2
-   struct rc2_key rc2;
-#endif
-#ifdef LTC_SAFER
-   struct safer_key safer;
-#endif
-#ifdef LTC_TWOFISH
-   struct twofish_key  twofish;
-#endif
-#ifdef LTC_BLOWFISH
-   struct blowfish_key blowfish;
-#endif
-#ifdef LTC_RC5
-   struct rc5_key      rc5;
-#endif
-#ifdef LTC_RC6
-   struct rc6_key      rc6;
-#endif
-#ifdef LTC_SAFERP
-   struct saferp_key   saferp;
-#endif
-#ifdef LTC_RIJNDAEL
-   struct rijndael_key rijndael;
-#endif
-#ifdef LTC_XTEA
-   struct xtea_key     xtea;
-#endif
-#ifdef LTC_CAST5
-   struct cast5_key    cast5;
-#endif
-#ifdef LTC_NOEKEON
-   struct noekeon_key  noekeon;
-#endif
-#ifdef LTC_SKIPJACK
-   struct skipjack_key skipjack;
-#endif
-#ifdef LTC_KHAZAD
-   struct khazad_key   khazad;
-#endif
-#ifdef LTC_ANUBIS
-   struct anubis_key   anubis;
-#endif
-#ifdef LTC_KSEED
-   struct kseed_key    kseed;
-#endif
-#ifdef LTC_KASUMI
-   struct kasumi_key   kasumi;
-#endif
-#ifdef LTC_MULTI2
-   struct multi2_key   multi2;
-#endif
-#ifdef LTC_CAMELLIA
-   struct camellia_key camellia;
-#endif
-   void   *data;
-} symmetric_key;
-
-#ifdef LTC_ECB_MODE
-/** A block cipher ECB structure */
-typedef struct {
-   /** The index of the cipher chosen */
-   int                 cipher,
-   /** The block size of the given cipher */
-                       blocklen;
-   /** The scheduled key */
-   symmetric_key       key;
-} symmetric_ECB;
-#endif
-
-#ifdef LTC_CFB_MODE
-/** A block cipher CFB structure */
-typedef struct {
-   /** The index of the cipher chosen */
-   int                 cipher,
-   /** The block size of the given cipher */
-                       blocklen,
-   /** The padding offset */
-                       padlen;
-   /** The current IV */
-   unsigned char       IV[MAXBLOCKSIZE],
-   /** The pad used to encrypt/decrypt */
-                       pad[MAXBLOCKSIZE];
-   /** The scheduled key */
-   symmetric_key       key;
-} symmetric_CFB;
-#endif
-
-#ifdef LTC_OFB_MODE
-/** A block cipher OFB structure */
-typedef struct {
-   /** The index of the cipher chosen */
-   int                 cipher,
-   /** The block size of the given cipher */
-                       blocklen,
-   /** The padding offset */
-                       padlen;
-   /** The current IV */
-   unsigned char       IV[MAXBLOCKSIZE];
-   /** The scheduled key */
-   symmetric_key       key;
-} symmetric_OFB;
-#endif
-
-#ifdef LTC_CBC_MODE
-/** A block cipher CBC structure */
-typedef struct {
-   /** The index of the cipher chosen */
-   int                 cipher,
-   /** The block size of the given cipher */
-                       blocklen;
-   /** The current IV */
-   unsigned char       IV[MAXBLOCKSIZE];
-   /** The scheduled key */
-   symmetric_key       key;
-} symmetric_CBC;
-#endif
-
-
-#ifdef LTC_CTR_MODE
-/** A block cipher CTR structure */
-typedef struct {
-   /** The index of the cipher chosen */
-   int                 cipher,
-   /** The block size of the given cipher */
-                       blocklen,
-   /** The padding offset */
-                       padlen,
-   /** The mode (endianess) of the CTR, 0==little, 1==big */
-                       mode,
-   /** counter width */
-                       ctrlen;
-
-   /** The counter */
-   unsigned char       ctr[MAXBLOCKSIZE],
-   /** The pad used to encrypt/decrypt */
-                       pad[MAXBLOCKSIZE];
-   /** The scheduled key */
-   symmetric_key       key;
-} symmetric_CTR;
-#endif
-
-
-#ifdef LTC_LRW_MODE
-/** A LRW structure */
-typedef struct {
-    /** The index of the cipher chosen (must be a 128-bit block cipher) */
-    int               cipher;
-
-    /** The current IV */
-    unsigned char     IV[16],
-
-    /** the tweak key */
-                      tweak[16],
-
-    /** The current pad, it's the product of the first 15 bytes against the tweak key */
-                      pad[16];
-
-    /** The scheduled symmetric key */
-    symmetric_key     key;
-
-#ifdef LTC_LRW_TABLES
-    /** The pre-computed multiplication table */
-    unsigned char     PC[16][256][16];
-#endif
-} symmetric_LRW;
-#endif
-
-#ifdef LTC_F8_MODE
-/** A block cipher F8 structure */
-typedef struct {
-   /** The index of the cipher chosen */
-   int                 cipher,
-   /** The block size of the given cipher */
-                       blocklen,
-   /** The padding offset */
-                       padlen;
-   /** The current IV */
-   unsigned char       IV[MAXBLOCKSIZE],
-                       MIV[MAXBLOCKSIZE];
-   /** Current block count */
-   ulong32             blockcnt;
-   /** The scheduled key */
-   symmetric_key       key;
-} symmetric_F8;
-#endif
-
-
-/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */
-extern struct ltc_cipher_descriptor {
-   /** name of cipher */
-   const char *name;
-   /** internal ID */
-   unsigned char ID;
-   /** min keysize (octets) */
-   int  min_key_length,
-   /** max keysize (octets) */
-        max_key_length,
-   /** block size (octets) */
-        block_length,
-   /** default number of rounds */
-        default_rounds;
-   /** Setup the cipher
-      @param key         The input symmetric key
-      @param keylen      The length of the input key (octets)
-      @param num_rounds  The requested number of rounds (0==default)
-      @param skey        [out] The destination of the scheduled key
-      @return CRYPT_OK if successful
-   */
-   int  (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-   /** Encrypt a block
-      @param pt      The plaintext
-      @param ct      [out] The ciphertext
-      @param skey    The scheduled key
-      @return CRYPT_OK if successful
-   */
-   int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-   /** Decrypt a block
-      @param ct      The ciphertext
-      @param pt      [out] The plaintext
-      @param skey    The scheduled key
-      @return CRYPT_OK if successful
-   */
-   int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-   /** Test the block cipher
-       @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
-   */
-   int (*test)(void);
-
-   /** Terminate the context
-      @param skey    The scheduled key
-   */
-   void (*done)(symmetric_key *skey);
-
-   /** Determine a key size
-       @param keysize    [in/out] The size of the key desired and the suggested size
-       @return CRYPT_OK if successful
-   */
-   int  (*keysize)(int *keysize);
-
-/** Accelerators **/
-   /** Accelerated ECB encryption
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey);
-
-   /** Accelerated ECB decryption
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey);
-
-   /** Accelerated CBC encryption
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param IV      The initial value (input/output)
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey);
-
-   /** Accelerated CBC decryption
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param IV      The initial value (input/output)
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey);
-
-   /** Accelerated CTR encryption
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param IV      The initial value (input/output)
-       @param mode    little or big endian counter (mode=0 or mode=1)
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey);
-
-   /** Accelerated LRW
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param IV      The initial value (input/output)
-       @param tweak   The LRW tweak
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey);
-
-   /** Accelerated LRW
-       @param ct      Ciphertext
-       @param pt      Plaintext
-       @param blocks  The number of complete blocks to process
-       @param IV      The initial value (input/output)
-       @param tweak   The LRW tweak
-       @param skey    The scheduled key context
-       @return CRYPT_OK if successful
-   */
-   int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey);
-
-   /** Accelerated CCM packet (one-shot)
-       @param key        The secret key to use
-       @param keylen     The length of the secret key (octets)
-       @param uskey      A previously scheduled key [optional can be NULL]
-       @param nonce      The session nonce [use once]
-       @param noncelen   The length of the nonce
-       @param header     The header for the session
-       @param headerlen  The length of the header (octets)
-       @param pt         [out] The plaintext
-       @param ptlen      The length of the plaintext (octets)
-       @param ct         [out] The ciphertext
-       @param tag        [out] The destination tag
-       @param taglen     [in/out] The max size and resulting size of the authentication tag
-       @param direction  Encrypt or Decrypt direction (0 or 1)
-       @return CRYPT_OK if successful
-   */
-   int (*accel_ccm_memory)(
-       const unsigned char *key,    unsigned long keylen,
-       symmetric_key       *uskey,
-       const unsigned char *nonce,  unsigned long noncelen,
-       const unsigned char *header, unsigned long headerlen,
-             unsigned char *pt,     unsigned long ptlen,
-             unsigned char *ct,
-             unsigned char *tag,    unsigned long *taglen,
-                       int  direction);
-
-   /** Accelerated GCM packet (one shot)
-       @param key        The secret key
-       @param keylen     The length of the secret key
-       @param IV         The initialization vector
-       @param IVlen      The length of the initialization vector
-       @param adata      The additional authentication data (header)
-       @param adatalen   The length of the adata
-       @param pt         The plaintext
-       @param ptlen      The length of the plaintext (ciphertext length is the same)
-       @param ct         The ciphertext
-       @param tag        [out] The MAC tag
-       @param taglen     [in/out] The MAC tag length
-       @param direction  Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
-       @return CRYPT_OK on success
-   */
-   int (*accel_gcm_memory)(
-       const unsigned char *key,    unsigned long keylen,
-       const unsigned char *IV,     unsigned long IVlen,
-       const unsigned char *adata,  unsigned long adatalen,
-             unsigned char *pt,     unsigned long ptlen,
-             unsigned char *ct,
-             unsigned char *tag,    unsigned long *taglen,
-                       int direction);
-
-   /** Accelerated one shot LTC_OMAC
-       @param key            The secret key
-       @param keylen         The key length (octets)
-       @param in             The message
-       @param inlen          Length of message (octets)
-       @param out            [out] Destination for tag
-       @param outlen         [in/out] Initial and final size of out
-       @return CRYPT_OK on success
-   */
-   int (*omac_memory)(
-       const unsigned char *key, unsigned long keylen,
-       const unsigned char *in,  unsigned long inlen,
-             unsigned char *out, unsigned long *outlen);
-
-   /** Accelerated one shot XCBC
-       @param key            The secret key
-       @param keylen         The key length (octets)
-       @param in             The message
-       @param inlen          Length of message (octets)
-       @param out            [out] Destination for tag
-       @param outlen         [in/out] Initial and final size of out
-       @return CRYPT_OK on success
-   */
-   int (*xcbc_memory)(
-       const unsigned char *key, unsigned long keylen,
-       const unsigned char *in,  unsigned long inlen,
-             unsigned char *out, unsigned long *outlen);
-
-   /** Accelerated one shot F9
-       @param key            The secret key
-       @param keylen         The key length (octets)
-       @param in             The message
-       @param inlen          Length of message (octets)
-       @param out            [out] Destination for tag
-       @param outlen         [in/out] Initial and final size of out
-       @return CRYPT_OK on success
-       @remark Requires manual padding
-   */
-   int (*f9_memory)(
-       const unsigned char *key, unsigned long keylen,
-       const unsigned char *in,  unsigned long inlen,
-             unsigned char *out, unsigned long *outlen);
-
-   /** Accelerated XTS encryption
-       @param pt      Plaintext
-       @param ct      Ciphertext
-       @param blocks  The number of complete blocks to process
-       @param tweak   The 128-bit encryption tweak (input/output).
-                      The tweak should not be encrypted on input, but
-                      next tweak will be copied encrypted on output.
-       @param skey1   The first scheduled key context
-       @param skey2   The second scheduled key context
-       @return CRYPT_OK if successful
-    */
-    int (*accel_xts_encrypt)(const unsigned char *pt, unsigned char *ct,
-        unsigned long blocks, unsigned char *tweak, symmetric_key *skey1,
-        symmetric_key *skey2);
-
-    /** Accelerated XTS decryption
-        @param ct      Ciphertext
-        @param pt      Plaintext
-        @param blocks  The number of complete blocks to process
-        @param tweak   The 128-bit encryption tweak (input/output).
-                       The tweak should not be encrypted on input, but
-                       next tweak will be copied encrypted on output.
-        @param skey1   The first scheduled key context
-        @param skey2   The second scheduled key context
-        @return CRYPT_OK if successful
-     */
-     int (*accel_xts_decrypt)(const unsigned char *ct, unsigned char *pt,
-         unsigned long blocks, unsigned char *tweak, symmetric_key *skey1,
-         symmetric_key *skey2);
-} cipher_descriptor[];
-
-#ifdef LTC_BLOWFISH
-int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int blowfish_test(void);
-void blowfish_done(symmetric_key *skey);
-int blowfish_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor blowfish_desc;
-#endif
-
-#ifdef LTC_RC5
-int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rc5_test(void);
-void rc5_done(symmetric_key *skey);
-int rc5_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rc5_desc;
-#endif
-
-#ifdef LTC_RC6
-int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rc6_test(void);
-void rc6_done(symmetric_key *skey);
-int rc6_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rc6_desc;
-#endif
-
-#ifdef LTC_RC2
-int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rc2_setup_ex(const unsigned char *key, int keylen, int bits, int num_rounds, symmetric_key *skey);
-int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rc2_test(void);
-void rc2_done(symmetric_key *skey);
-int rc2_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rc2_desc;
-#endif
-
-#ifdef LTC_SAFERP
-int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int saferp_test(void);
-void saferp_done(symmetric_key *skey);
-int saferp_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor saferp_desc;
-#endif
-
-#ifdef LTC_SAFER
-int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key);
-int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key);
-int safer_k64_test(void);
-int safer_sk64_test(void);
-int safer_sk128_test(void);
-void safer_done(symmetric_key *skey);
-int safer_64_keysize(int *keysize);
-int safer_128_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc;
-#endif
-
-#ifdef LTC_RIJNDAEL
-
-/* make aes an alias */
-#define aes_setup           rijndael_setup
-#define aes_ecb_encrypt     rijndael_ecb_encrypt
-#define aes_ecb_decrypt     rijndael_ecb_decrypt
-#define aes_test            rijndael_test
-#define aes_done            rijndael_done
-#define aes_keysize         rijndael_keysize
-
-#define aes_enc_setup           rijndael_enc_setup
-#define aes_enc_ecb_encrypt     rijndael_enc_ecb_encrypt
-#define aes_enc_keysize         rijndael_enc_keysize
-
-int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rijndael_test(void);
-void rijndael_done(symmetric_key *skey);
-int rijndael_keysize(int *keysize);
-int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-void rijndael_enc_done(symmetric_key *skey);
-int rijndael_enc_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc;
-extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc;
-#endif
-
-#ifdef LTC_XTEA
-int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int xtea_test(void);
-void xtea_done(symmetric_key *skey);
-int xtea_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor xtea_desc;
-#endif
-
-#ifdef LTC_TWOFISH
-int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int twofish_test(void);
-void twofish_done(symmetric_key *skey);
-int twofish_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor twofish_desc;
-#endif
-
-#ifdef LTC_DES
-int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int des_test(void);
-void des_done(symmetric_key *skey);
-int des_keysize(int *keysize);
-int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int des3_test(void);
-void des3_done(symmetric_key *skey);
-int des3_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor des_desc, des3_desc;
-#endif
-
-#ifdef LTC_CAST5
-int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int cast5_test(void);
-void cast5_done(symmetric_key *skey);
-int cast5_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor cast5_desc;
-#endif
-
-#ifdef LTC_NOEKEON
-int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int noekeon_test(void);
-void noekeon_done(symmetric_key *skey);
-int noekeon_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor noekeon_desc;
-#endif
-
-#ifdef LTC_SKIPJACK
-int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int skipjack_test(void);
-void skipjack_done(symmetric_key *skey);
-int skipjack_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor skipjack_desc;
-#endif
-
-#ifdef LTC_KHAZAD
-int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int khazad_test(void);
-void khazad_done(symmetric_key *skey);
-int khazad_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor khazad_desc;
-#endif
-
-#ifdef LTC_ANUBIS
-int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int anubis_test(void);
-void anubis_done(symmetric_key *skey);
-int anubis_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor anubis_desc;
-#endif
-
-#ifdef LTC_KSEED
-int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int kseed_test(void);
-void kseed_done(symmetric_key *skey);
-int kseed_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor kseed_desc;
-#endif
-
-#ifdef LTC_KASUMI
-int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int kasumi_test(void);
-void kasumi_done(symmetric_key *skey);
-int kasumi_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor kasumi_desc;
-#endif
-
-
-#ifdef LTC_MULTI2
-int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int multi2_test(void);
-void multi2_done(symmetric_key *skey);
-int multi2_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor multi2_desc;
-#endif
-
-#ifdef LTC_CAMELLIA
-int camellia_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int camellia_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int camellia_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int camellia_test(void);
-void camellia_done(symmetric_key *skey);
-int camellia_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor camellia_desc;
-#endif
-
-#ifdef LTC_ECB_MODE
-int ecb_start(int cipher, const unsigned char *key,
-              int keylen, int num_rounds, symmetric_ECB *ecb);
-int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb);
-int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb);
-int ecb_done(symmetric_ECB *ecb);
-#endif
-
-#ifdef LTC_CFB_MODE
-int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key,
-              int keylen, int num_rounds, symmetric_CFB *cfb);
-int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb);
-int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb);
-int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb);
-int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb);
-int cfb_done(symmetric_CFB *cfb);
-#endif
-
-#ifdef LTC_OFB_MODE
-int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key,
-              int keylen, int num_rounds, symmetric_OFB *ofb);
-int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb);
-int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb);
-int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb);
-int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb);
-int ofb_done(symmetric_OFB *ofb);
-#endif
-
-#ifdef LTC_CBC_MODE
-int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key,
-               int keylen, int num_rounds, symmetric_CBC *cbc);
-int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc);
-int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc);
-int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc);
-int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc);
-int cbc_done(symmetric_CBC *cbc);
-#endif
-
-#ifdef LTC_CTR_MODE
-
-#define CTR_COUNTER_LITTLE_ENDIAN    0x0000
-#define CTR_COUNTER_BIG_ENDIAN       0x1000
-#define LTC_CTR_RFC3686              0x2000
-
-int ctr_start(               int   cipher,
-              const unsigned char *IV,
-              const unsigned char *key,       int keylen,
-                             int  num_rounds, int ctr_mode,
-                   symmetric_CTR *ctr);
-int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr);
-int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr);
-int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr);
-int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr);
-int ctr_done(symmetric_CTR *ctr);
-int ctr_test(void);
-#endif
-
-#ifdef LTC_LRW_MODE
-
-#define LRW_ENCRYPT LTC_ENCRYPT
-#define LRW_DECRYPT LTC_DECRYPT
-
-int lrw_start(               int   cipher,
-              const unsigned char *IV,
-              const unsigned char *key,       int keylen,
-              const unsigned char *tweak,
-                             int  num_rounds,
-                   symmetric_LRW *lrw);
-int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw);
-int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw);
-int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw);
-int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw);
-int lrw_done(symmetric_LRW *lrw);
-int lrw_test(void);
-
-/* don't call */
-int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw);
-#endif
-
-#ifdef LTC_F8_MODE
-int f8_start(                int  cipher, const unsigned char *IV,
-             const unsigned char *key,                    int  keylen,
-             const unsigned char *salt_key,               int  skeylen,
-                             int  num_rounds,   symmetric_F8  *f8);
-int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8);
-int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8);
-int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8);
-int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8);
-int f8_done(symmetric_F8 *f8);
-int f8_test_mode(void);
-#endif
-
-#ifdef LTC_XTS_MODE
-typedef struct {
-   symmetric_key  key1, key2;
-   int            cipher;
-} symmetric_xts;
-
-int xts_start(                int  cipher,
-              const unsigned char *key1,
-              const unsigned char *key2,
-                    unsigned long  keylen,
-                              int  num_rounds,
-                    symmetric_xts *xts);
-
-int xts_encrypt(
-   const unsigned char *pt, unsigned long ptlen,
-         unsigned char *ct,
-         unsigned char *tweak,
-         symmetric_xts *xts);
-int xts_decrypt(
-   const unsigned char *ct, unsigned long ptlen,
-         unsigned char *pt,
-         unsigned char *tweak,
-         symmetric_xts *xts);
-
-void xts_done(symmetric_xts *xts);
-int  xts_test(void);
-void xts_mult_x(unsigned char *I);
-#endif
-
-int find_cipher(const char *name);
-int find_cipher_any(const char *name, int blocklen, int keylen);
-int find_cipher_id(unsigned char ID);
-int register_cipher(const struct ltc_cipher_descriptor *cipher);
-int unregister_cipher(const struct ltc_cipher_descriptor *cipher);
-int register_all_ciphers(void);
-int cipher_is_valid(int idx);
-
-LTC_MUTEX_PROTO(ltc_cipher_mutex)
-
-/* ---- stream ciphers ---- */
-
-#ifdef LTC_CHACHA
-
-typedef struct {
-   ulong32 input[16];
-   unsigned char kstream[64];
-   unsigned long ksleft;
-   unsigned long ivlen;
-   int rounds;
-} chacha_state;
-
-int chacha_setup(chacha_state *st, const unsigned char *key, unsigned long keylen, int rounds);
-int chacha_ivctr32(chacha_state *st, const unsigned char *iv, unsigned long ivlen, ulong32 counter);
-int chacha_ivctr64(chacha_state *st, const unsigned char *iv, unsigned long ivlen, ulong64 counter);
-int chacha_crypt(chacha_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out);
-int chacha_keystream(chacha_state *st, unsigned char *out, unsigned long outlen);
-int chacha_done(chacha_state *st);
-int chacha_test(void);
-
-#endif /* LTC_CHACHA */
-
-#ifdef LTC_RC4_STREAM
-
-typedef struct {
-   unsigned int x, y;
-   unsigned char buf[256];
-} rc4_state;
-
-int rc4_stream_setup(rc4_state *st, const unsigned char *key, unsigned long keylen);
-int rc4_stream_crypt(rc4_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out);
-int rc4_stream_keystream(rc4_state *st, unsigned char *out, unsigned long outlen);
-int rc4_stream_done(rc4_state *st);
-int rc4_stream_test(void);
-
-#endif /* LTC_RC4_STREAM */
-
-#ifdef LTC_SOBER128_STREAM
-
-typedef struct {
-   ulong32 R[17],       /* Working storage for the shift register */
-           initR[17],   /* saved register contents */
-           konst,       /* key dependent constant */
-           sbuf;        /* partial word encryption buffer */
-   int     nbuf;        /* number of part-word stream bits buffered */
-} sober128_state;
-
-int sober128_stream_setup(sober128_state *st, const unsigned char *key, unsigned long keylen);
-int sober128_stream_setiv(sober128_state *st, const unsigned char *iv, unsigned long ivlen);
-int sober128_stream_crypt(sober128_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out);
-int sober128_stream_keystream(sober128_state *st, unsigned char *out, unsigned long outlen);
-int sober128_stream_done(sober128_state *st);
-int sober128_stream_test(void);
-
-#endif /* LTC_SOBER128_STREAM */
diff --git a/libs/tomcrypt/src/headers/tomcrypt_custom.h b/libs/tomcrypt/src/headers/tomcrypt_custom.h
deleted file mode 100644
index 4b154f4d5dc7..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_custom.h
+++ /dev/null
@@ -1,586 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#ifndef TOMCRYPT_CUSTOM_H_
-#define TOMCRYPT_CUSTOM_H_
-
-/* macros for various libc functions you can change for embedded targets */
-#ifndef XMALLOC
-#define XMALLOC  malloc
-#endif
-#ifndef XREALLOC
-#define XREALLOC realloc
-#endif
-#ifndef XCALLOC
-#define XCALLOC  calloc
-#endif
-#ifndef XFREE
-#define XFREE    free
-#endif
-
-#ifndef XMEMSET
-#define XMEMSET  memset
-#endif
-#ifndef XMEMCPY
-#define XMEMCPY  memcpy
-#endif
-#ifndef XMEMMOVE
-#define XMEMMOVE memmove
-#endif
-#ifndef XMEMCMP
-#define XMEMCMP  memcmp
-#endif
-/* A memory compare function that has to run in constant time,
- * c.f. mem_neq() API summary.
- */
-#ifndef XMEM_NEQ
-#define XMEM_NEQ  mem_neq
-#endif
-#ifndef XSTRCMP
-#define XSTRCMP strcmp
-#endif
-
-#ifndef XCLOCK
-#define XCLOCK   clock
-#endif
-
-#ifndef XQSORT
-#define XQSORT qsort
-#endif
-
-#if ( defined(malloc) || defined(realloc) || defined(calloc) || defined(free) || \
-      defined(memset) || defined(memcpy) || defined(memcmp) || defined(strcmp) || \
-      defined(clock) || defined(qsort) ) && !defined(LTC_NO_PROTOTYPES)
-#define LTC_NO_PROTOTYPES
-#endif
-
-/* shortcut to disable automatic inclusion */
-#if defined LTC_NOTHING && !defined LTC_EASY
-  #define LTC_NO_CIPHERS
-  #define LTC_NO_MODES
-  #define LTC_NO_HASHES
-  #define LTC_NO_MACS
-  #define LTC_NO_PRNGS
-  #define LTC_NO_PK
-  #define LTC_NO_PKCS
-  #define LTC_NO_MISC
-#endif /* LTC_NOTHING */
-
-/* Easy button? */
-#ifdef LTC_EASY
-   #define LTC_NO_CIPHERS
-   #define LTC_RIJNDAEL
-   #define LTC_BLOWFISH
-   #define LTC_DES
-   #define LTC_CAST5
-
-   #define LTC_NO_MODES
-   #define LTC_ECB_MODE
-   #define LTC_CBC_MODE
-   #define LTC_CTR_MODE
-
-   #define LTC_NO_HASHES
-   #define LTC_SHA1
-   #define LTC_SHA3
-   #define LTC_SHA512
-   #define LTC_SHA384
-   #define LTC_SHA256
-   #define LTC_SHA224
-   #define LTC_HASH_HELPERS
-
-   #define LTC_NO_MACS
-   #define LTC_HMAC
-   #define LTC_OMAC
-   #define LTC_CCM_MODE
-
-   #define LTC_NO_PRNGS
-   #define LTC_SPRNG
-   #define LTC_YARROW
-   #define LTC_DEVRANDOM
-   #define LTC_TRY_URANDOM_FIRST
-   #define LTC_RNG_GET_BYTES
-   #define LTC_RNG_MAKE_PRNG
-
-   #define LTC_NO_PK
-   #define LTC_MRSA
-   #define LTC_MECC
-
-   #define LTC_NO_MISC
-   #define LTC_BASE64
-#endif
-
-/* The minimal set of functionality to run the tests */
-#ifdef LTC_MINIMAL
-   #define LTC_RIJNDAEL
-   #define LTC_SHA256
-   #define LTC_YARROW
-   #define LTC_CTR_MODE
-
-   #define LTC_RNG_MAKE_PRNG
-   #define LTC_RNG_GET_BYTES
-   #define LTC_DEVRANDOM
-   #define LTC_TRY_URANDOM_FIRST
-
-   #undef LTC_NO_FILE
-#endif
-
-/* Enable self-test test vector checking */
-#ifndef LTC_NO_TEST
-   #define LTC_TEST
-#endif
-/* Enable extended self-tests */
-/* #define LTC_TEST_EXT */
-
-/* Use small code where possible */
-/* #define LTC_SMALL_CODE */
-
-/* clean the stack of functions which put private information on stack */
-/* #define LTC_CLEAN_STACK */
-
-/* disable all file related functions */
-#define LTC_NO_FILE
-
-/* disable all forms of ASM */
-/* #define LTC_NO_ASM */
-
-/* disable FAST mode */
-/* #define LTC_NO_FAST */
-
-/* disable BSWAP on x86 */
-/* #define LTC_NO_BSWAP */
-
-/* ---> math provider? <--- */
-#ifndef LTC_NO_MATH
-
-/* LibTomMath */
-#define LTM_DESC
-
-/* TomsFastMath */
-/* #define TFM_DESC */
-
-/* GNU Multiple Precision Arithmetic Library */
-/* #define GMP_DESC */
-
-#endif /* LTC_NO_MATH */
-
-/* ---> Symmetric Block Ciphers <--- */
-#ifndef LTC_NO_CIPHERS
-
-#define LTC_BLOWFISH
-#define LTC_RC2
-#define LTC_RC5
-#define LTC_RC6
-#define LTC_SAFERP
-#define LTC_RIJNDAEL
-#define LTC_XTEA
-/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format
- * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */
-#define LTC_TWOFISH
-#ifndef LTC_NO_TABLES
-   #define LTC_TWOFISH_TABLES
-   /* #define LTC_TWOFISH_ALL_TABLES */
-#else
-   #define LTC_TWOFISH_SMALL
-#endif
-/* #define LTC_TWOFISH_SMALL */
-/* LTC_DES includes EDE triple-DES */
-#define LTC_DES
-#define LTC_CAST5
-#define LTC_NOEKEON
-#define LTC_SKIPJACK
-#define LTC_SAFER
-#define LTC_KHAZAD
-#define LTC_ANUBIS
-#define LTC_ANUBIS_TWEAK
-#define LTC_KSEED
-#define LTC_KASUMI
-#define LTC_MULTI2
-#define LTC_CAMELLIA
-
-/* stream ciphers */
-#define LTC_CHACHA
-#define LTC_RC4_STREAM
-#define LTC_SOBER128_STREAM
-
-#endif /* LTC_NO_CIPHERS */
-
-
-/* ---> Block Cipher Modes of Operation <--- */
-#ifndef LTC_NO_MODES
-
-#define LTC_CFB_MODE
-#define LTC_OFB_MODE
-#define LTC_ECB_MODE
-#define LTC_CBC_MODE
-#define LTC_CTR_MODE
-
-/* F8 chaining mode */
-#define LTC_F8_MODE
-
-/* LRW mode */
-#define LTC_LRW_MODE
-#ifndef LTC_NO_TABLES
-   /* like GCM mode this will enable 16 8x128 tables [64KB] that make
-    * seeking very fast.
-    */
-   #define LTC_LRW_TABLES
-#endif
-
-/* XTS mode */
-#define LTC_XTS_MODE
-
-#endif /* LTC_NO_MODES */
-
-/* ---> One-Way Hash Functions <--- */
-#ifndef LTC_NO_HASHES
-
-#define LTC_CHC_HASH
-#define LTC_WHIRLPOOL
-#define LTC_SHA3
-#define LTC_SHA512
-#define LTC_SHA512_256
-#define LTC_SHA512_224
-#define LTC_SHA384
-#define LTC_SHA256
-#define LTC_SHA224
-#define LTC_TIGER
-#define LTC_SHA1
-#define LTC_MD5
-#define LTC_MD4
-#define LTC_MD2
-#define LTC_RIPEMD128
-#define LTC_RIPEMD160
-#define LTC_RIPEMD256
-#define LTC_RIPEMD320
-#define LTC_BLAKE2S
-#define LTC_BLAKE2B
-
-#define LTC_HASH_HELPERS
-
-#endif /* LTC_NO_HASHES */
-
-
-/* ---> MAC functions <--- */
-#ifndef LTC_NO_MACS
-
-#define LTC_HMAC
-#define LTC_OMAC
-#define LTC_PMAC
-#define LTC_XCBC
-#define LTC_F9_MODE
-#define LTC_PELICAN
-#define LTC_POLY1305
-#define LTC_BLAKE2SMAC
-#define LTC_BLAKE2BMAC
-
-/* ---> Encrypt + Authenticate Modes <--- */
-
-#define LTC_EAX_MODE
-
-#define LTC_OCB_MODE
-#define LTC_OCB3_MODE
-#define LTC_CCM_MODE
-#define LTC_GCM_MODE
-#define LTC_CHACHA20POLY1305_MODE
-
-/* Use 64KiB tables */
-#ifndef LTC_NO_TABLES
-   #define LTC_GCM_TABLES
-#endif
-
-/* USE SSE2? requires GCC works on x86_32 and x86_64*/
-#ifdef LTC_GCM_TABLES
-/* #define LTC_GCM_TABLES_SSE2 */
-#endif
-
-#endif /* LTC_NO_MACS */
-
-
-/* --> Pseudo Random Number Generators <--- */
-#ifndef LTC_NO_PRNGS
-
-/* Yarrow */
-#define LTC_YARROW
-
-/* a PRNG that simply reads from an available system source */
-#define LTC_SPRNG
-
-/* The RC4 stream cipher based PRNG */
-#define LTC_RC4
-
-/* The ChaCha20 stream cipher based PRNG */
-#define LTC_CHACHA20_PRNG
-
-/* Fortuna PRNG */
-#define LTC_FORTUNA
-
-/* Greg's SOBER128 stream cipher based PRNG */
-#define LTC_SOBER128
-
-/* the *nix style /dev/random device */
-#define LTC_DEVRANDOM
-/* try /dev/urandom before trying /dev/random
- * are you sure you want to disable this? http://www.2uo.de/myths-about-urandom/ */
-#define LTC_TRY_URANDOM_FIRST
-/* rng_get_bytes() */
-#define LTC_RNG_GET_BYTES
-/* rng_make_prng() */
-#define LTC_RNG_MAKE_PRNG
-
-/* enable the ltc_rng hook to integrate e.g. embedded hardware RNG's easily */
-/* #define LTC_PRNG_ENABLE_LTC_RNG */
-
-#endif /* LTC_NO_PRNGS */
-
-#ifdef LTC_YARROW
-
-/* which descriptor of AES to use?  */
-/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */
-#ifdef ENCRYPT_ONLY
-  #define LTC_YARROW_AES 0
-#else
-  #define LTC_YARROW_AES 2
-#endif
-
-#endif
-
-#ifdef LTC_FORTUNA
-
-#ifndef LTC_FORTUNA_WD
-/* reseed every N calls to the read function */
-#define LTC_FORTUNA_WD    10
-#endif
-
-#ifndef LTC_FORTUNA_POOLS
-/* number of pools (4..32) can save a bit of ram by lowering the count */
-#define LTC_FORTUNA_POOLS 32
-#endif
-
-#endif /* LTC_FORTUNA */
-
-
-/* ---> Public Key Crypto <--- */
-#ifndef LTC_NO_PK
-
-/* Include RSA support */
-#define LTC_MRSA
-
-/* Include Diffie-Hellman support */
-/* is_prime fails for GMP */
-#define LTC_MDH
-/* Supported Key Sizes */
-#define LTC_DH768
-#define LTC_DH1024
-#define LTC_DH1536
-#define LTC_DH2048
-
-#ifndef TFM_DESC
-/* tfm has a problem in fp_isprime for larger key sizes */
-#define LTC_DH3072
-#define LTC_DH4096
-#define LTC_DH6144
-#define LTC_DH8192
-#endif
-
-/* Include Katja (a Rabin variant like RSA) */
-/* #define LTC_MKAT */
-
-/* Digital Signature Algorithm */
-#define LTC_MDSA
-
-/* ECC */
-#define LTC_MECC
-
-/* use Shamir's trick for point mul (speeds up signature verification) */
-#define LTC_ECC_SHAMIR
-
-#if defined(TFM_DESC) && defined(LTC_MECC)
-   #define LTC_MECC_ACCEL
-#endif
-
-/* do we want fixed point ECC */
-/* #define LTC_MECC_FP */
-
-#endif /* LTC_NO_PK */
-
-#if defined(LTC_MRSA) && !defined(LTC_NO_RSA_BLINDING)
-/* Enable RSA blinding when doing private key operations by default */
-#define LTC_RSA_BLINDING
-#endif  /* LTC_NO_RSA_BLINDING */
-
-#if defined(LTC_MRSA) && !defined(LTC_NO_RSA_CRT_HARDENING)
-/* Enable RSA CRT hardening when doing private key operations by default */
-#define LTC_RSA_CRT_HARDENING
-#endif  /* LTC_NO_RSA_CRT_HARDENING */
-
-#if defined(LTC_MECC) && !defined(LTC_NO_ECC_TIMING_RESISTANT)
-/* Enable ECC timing resistant version by default */
-#define LTC_ECC_TIMING_RESISTANT
-#endif
-
-/* PKCS #1 (RSA) and #5 (Password Handling) stuff */
-#ifndef LTC_NO_PKCS
-
-#define LTC_PKCS_1
-#define LTC_PKCS_5
-
-/* Include ASN.1 DER (required by DSA/RSA) */
-#define LTC_DER
-
-#endif /* LTC_NO_PKCS */
-
-/* misc stuff */
-#ifndef LTC_NO_MISC
-
-/* Various tidbits of modern neatoness */
-#define LTC_BASE64
-/* ... and it's URL safe version */
-#define LTC_BASE64_URL
-
-/* Keep LTC_NO_HKDF for compatibility reasons
- * superseeded by LTC_NO_MISC*/
-#ifndef LTC_NO_HKDF
-/* HKDF Key Derivation/Expansion stuff */
-#define LTC_HKDF
-#endif /* LTC_NO_HKDF */
-
-#define LTC_ADLER32
-
-#define LTC_CRC32
-
-#endif /* LTC_NO_MISC */
-
-/* cleanup */
-
-#ifdef LTC_MECC
-/* Supported ECC Key Sizes */
-#ifndef LTC_NO_CURVES
-   #define LTC_ECC112
-   #define LTC_ECC128
-   #define LTC_ECC160
-   #define LTC_ECC192
-   #define LTC_ECC224
-   #define LTC_ECC256
-   #define LTC_ECC384
-   #define LTC_ECC521
-#endif
-#endif
-
-#if defined(LTC_DER)
-   #ifndef LTC_DER_MAX_RECURSION
-      /* Maximum recursion limit when processing nested ASN.1 types. */
-      #define LTC_DER_MAX_RECURSION 30
-   #endif
-#endif
-
-#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(LTC_MKAT)
-   /* Include the MPI functionality?  (required by the PK algorithms) */
-   #define LTC_MPI
-
-   #ifndef LTC_PK_MAX_RETRIES
-      /* iterations limit for retry-loops */
-      #define LTC_PK_MAX_RETRIES  20
-   #endif
-#endif
-
-#ifdef LTC_MRSA
-   #define LTC_PKCS_1
-#endif
-
-#if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL)
-   #error Pelican-MAC requires LTC_RIJNDAEL
-#endif
-
-#if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC))
-   #error LTC_EAX_MODE requires CTR and LTC_OMAC mode
-#endif
-
-#if defined(LTC_YARROW) && !defined(LTC_CTR_MODE)
-   #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined!
-#endif
-
-#if defined(LTC_DER) && !defined(LTC_MPI)
-   #error ASN.1 DER requires MPI functionality
-#endif
-
-#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(LTC_MKAT)) && !defined(LTC_DER)
-   #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled
-#endif
-
-#if defined(LTC_CHACHA20POLY1305_MODE) && (!defined(LTC_CHACHA) || !defined(LTC_POLY1305))
-   #error LTC_CHACHA20POLY1305_MODE requires LTC_CHACHA + LTC_POLY1305
-#endif
-
-#if defined(LTC_CHACHA20_PRNG) && !defined(LTC_CHACHA)
-   #error LTC_CHACHA20_PRNG requires LTC_CHACHA
-#endif
-
-#if defined(LTC_RC4) && !defined(LTC_RC4_STREAM)
-   #error LTC_RC4 requires LTC_RC4_STREAM
-#endif
-
-#if defined(LTC_SOBER128) && !defined(LTC_SOBER128_STREAM)
-   #error LTC_SOBER128 requires LTC_SOBER128_STREAM
-#endif
-
-#if defined(LTC_BLAKE2SMAC) && !defined(LTC_BLAKE2S)
-   #error LTC_BLAKE2SMAC requires LTC_BLAKE2S
-#endif
-
-#if defined(LTC_BLAKE2BMAC) && !defined(LTC_BLAKE2B)
-   #error LTC_BLAKE2BMAC requires LTC_BLAKE2B
-#endif
-
-#if defined(LTC_SPRNG) && !defined(LTC_RNG_GET_BYTES)
-   #error LTC_SPRNG requires LTC_RNG_GET_BYTES
-#endif
-
-#if defined(LTC_NO_MATH) && (defined(LTM_DESC) || defined(TFM_DESC) || defined(GMP_DESC))
-   #error LTC_NO_MATH defined, but also a math descriptor
-#endif
-
-/* THREAD management */
-#ifdef LTC_PTHREAD
-
-#include <pthread.h>
-
-#define LTC_MUTEX_GLOBAL(x)   pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
-#define LTC_MUTEX_PROTO(x)    extern pthread_mutex_t x;
-#define LTC_MUTEX_TYPE(x)     pthread_mutex_t x;
-#define LTC_MUTEX_INIT(x)     LTC_ARGCHK(pthread_mutex_init(x, NULL) == 0);
-#define LTC_MUTEX_LOCK(x)     LTC_ARGCHK(pthread_mutex_lock(x) == 0);
-#define LTC_MUTEX_UNLOCK(x)   LTC_ARGCHK(pthread_mutex_unlock(x) == 0);
-#define LTC_MUTEX_DESTROY(x)  LTC_ARGCHK(pthread_mutex_destroy(x) == 0);
-
-#else
-
-/* default no functions */
-#define LTC_MUTEX_GLOBAL(x)
-#define LTC_MUTEX_PROTO(x)
-#define LTC_MUTEX_TYPE(x)
-#define LTC_MUTEX_INIT(x)
-#define LTC_MUTEX_LOCK(x)
-#define LTC_MUTEX_UNLOCK(x)
-#define LTC_MUTEX_DESTROY(x)
-
-#endif
-
-/* Debuggers */
-
-/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and RC4 work (see the code) */
-/* #define LTC_VALGRIND */
-
-#endif
-
-#ifndef LTC_NO_FILE
-   /* buffer size for reading from a file via fread(..) */
-   #ifndef LTC_FILE_READ_BUFSIZE
-   #define LTC_FILE_READ_BUFSIZE 8192
-   #endif
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt_hash.h b/libs/tomcrypt/src/headers/tomcrypt_hash.h
deleted file mode 100644
index bb70c4aa7efb..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_hash.h
+++ /dev/null
@@ -1,527 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* ---- HASH FUNCTIONS ---- */
-#ifdef LTC_SHA3
-struct sha3_state {
-    ulong64 saved;                  /* the portion of the input message that we didn't consume yet */
-    ulong64 s[25];
-    unsigned char sb[25 * 8];       /* used for storing `ulong64 s[25]` as little-endian bytes */
-    unsigned short byte_index;      /* 0..7--the next byte after the set one (starts from 0; 0--none are buffered) */
-    unsigned short word_index;      /* 0..24--the next word to integrate input (starts from 0) */
-    unsigned short capacity_words;  /* the double size of the hash output in words (e.g. 16 for Keccak 512) */
-    unsigned short xof_flag;
-};
-#endif
-
-#ifdef LTC_SHA512
-struct sha512_state {
-    ulong64  length, state[8];
-    unsigned long curlen;
-    unsigned char buf[128];
-};
-#endif
-
-#ifdef LTC_SHA256
-struct sha256_state {
-    ulong64 length;
-    ulong32 state[8], curlen;
-    unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_SHA1
-struct sha1_state {
-    ulong64 length;
-    ulong32 state[5], curlen;
-    unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_MD5
-struct md5_state {
-    ulong64 length;
-    ulong32 state[4], curlen;
-    unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_MD4
-struct md4_state {
-    ulong64 length;
-    ulong32 state[4], curlen;
-    unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_TIGER
-struct tiger_state {
-    ulong64 state[3], length;
-    unsigned long curlen;
-    unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_MD2
-struct md2_state {
-    unsigned char chksum[16], X[48], buf[16];
-    unsigned long curlen;
-};
-#endif
-
-#ifdef LTC_RIPEMD128
-struct rmd128_state {
-    ulong64 length;
-    unsigned char buf[64];
-    ulong32 curlen, state[4];
-};
-#endif
-
-#ifdef LTC_RIPEMD160
-struct rmd160_state {
-    ulong64 length;
-    unsigned char buf[64];
-    ulong32 curlen, state[5];
-};
-#endif
-
-#ifdef LTC_RIPEMD256
-struct rmd256_state {
-    ulong64 length;
-    unsigned char buf[64];
-    ulong32 curlen, state[8];
-};
-#endif
-
-#ifdef LTC_RIPEMD320
-struct rmd320_state {
-    ulong64 length;
-    unsigned char buf[64];
-    ulong32 curlen, state[10];
-};
-#endif
-
-#ifdef LTC_WHIRLPOOL
-struct whirlpool_state {
-    ulong64 length, state[8];
-    unsigned char buf[64];
-    ulong32 curlen;
-};
-#endif
-
-#ifdef LTC_CHC_HASH
-struct chc_state {
-    ulong64 length;
-    unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE];
-    ulong32 curlen;
-};
-#endif
-
-#ifdef LTC_BLAKE2S
-struct blake2s_state {
-    ulong32 h[8];
-    ulong32 t[2];
-    ulong32 f[2];
-    unsigned char buf[64];
-    unsigned long curlen;
-    unsigned long outlen;
-    unsigned char last_node;
-};
-#endif
-
-#ifdef LTC_BLAKE2B
-struct blake2b_state {
-    ulong64 h[8];
-    ulong64 t[2];
-    ulong64 f[2];
-    unsigned char buf[128];
-    unsigned long curlen;
-    unsigned long outlen;
-    unsigned char last_node;
-};
-#endif
-
-typedef union Hash_state {
-    char dummy[1];
-#ifdef LTC_CHC_HASH
-    struct chc_state chc;
-#endif
-#ifdef LTC_WHIRLPOOL
-    struct whirlpool_state whirlpool;
-#endif
-#ifdef LTC_SHA3
-    struct sha3_state sha3;
-#endif
-#ifdef LTC_SHA512
-    struct sha512_state sha512;
-#endif
-#ifdef LTC_SHA256
-    struct sha256_state sha256;
-#endif
-#ifdef LTC_SHA1
-    struct sha1_state   sha1;
-#endif
-#ifdef LTC_MD5
-    struct md5_state    md5;
-#endif
-#ifdef LTC_MD4
-    struct md4_state    md4;
-#endif
-#ifdef LTC_MD2
-    struct md2_state    md2;
-#endif
-#ifdef LTC_TIGER
-    struct tiger_state  tiger;
-#endif
-#ifdef LTC_RIPEMD128
-    struct rmd128_state rmd128;
-#endif
-#ifdef LTC_RIPEMD160
-    struct rmd160_state rmd160;
-#endif
-#ifdef LTC_RIPEMD256
-    struct rmd256_state rmd256;
-#endif
-#ifdef LTC_RIPEMD320
-    struct rmd320_state rmd320;
-#endif
-#ifdef LTC_BLAKE2S
-    struct blake2s_state blake2s;
-#endif
-#ifdef LTC_BLAKE2B
-    struct blake2b_state blake2b;
-#endif
-
-    void *data;
-} hash_state;
-
-/** hash descriptor */
-extern  struct ltc_hash_descriptor {
-    /** name of hash */
-    const char *name;
-    /** internal ID */
-    unsigned char ID;
-    /** Size of digest in octets */
-    unsigned long hashsize;
-    /** Input block size in octets */
-    unsigned long blocksize;
-    /** ASN.1 OID */
-    unsigned long OID[16];
-    /** Length of DER encoding */
-    unsigned long OIDlen;
-
-    /** Init a hash state
-      @param hash   The hash to initialize
-      @return CRYPT_OK if successful
-    */
-    int (*init)(hash_state *hash);
-    /** Process a block of data
-      @param hash   The hash state
-      @param in     The data to hash
-      @param inlen  The length of the data (octets)
-      @return CRYPT_OK if successful
-    */
-    int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen);
-    /** Produce the digest and store it
-      @param hash   The hash state
-      @param out    [out] The destination of the digest
-      @return CRYPT_OK if successful
-    */
-    int (*done)(hash_state *hash, unsigned char *out);
-    /** Self-test
-      @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-    */
-    int (*test)(void);
-
-    /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */
-    int  (*hmac_block)(const unsigned char *key, unsigned long  keylen,
-                       const unsigned char *in,  unsigned long  inlen,
-                             unsigned char *out, unsigned long *outlen);
-
-} hash_descriptor[];
-
-#ifdef LTC_CHC_HASH
-int chc_register(int cipher);
-int chc_init(hash_state * md);
-int chc_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int chc_done(hash_state * md, unsigned char *hash);
-int chc_test(void);
-extern const struct ltc_hash_descriptor chc_desc;
-#endif
-
-#ifdef LTC_WHIRLPOOL
-int whirlpool_init(hash_state * md);
-int whirlpool_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int whirlpool_done(hash_state * md, unsigned char *hash);
-int whirlpool_test(void);
-extern const struct ltc_hash_descriptor whirlpool_desc;
-#endif
-
-#ifdef LTC_SHA3
-int sha3_512_init(hash_state * md);
-int sha3_512_test(void);
-extern const struct ltc_hash_descriptor sha3_512_desc;
-int sha3_384_init(hash_state * md);
-int sha3_384_test(void);
-extern const struct ltc_hash_descriptor sha3_384_desc;
-int sha3_256_init(hash_state * md);
-int sha3_256_test(void);
-extern const struct ltc_hash_descriptor sha3_256_desc;
-int sha3_224_init(hash_state * md);
-int sha3_224_test(void);
-extern const struct ltc_hash_descriptor sha3_224_desc;
-/* process + done are the same for all variants */
-int sha3_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha3_done(hash_state *md, unsigned char *hash);
-/* SHAKE128 + SHAKE256 */
-int sha3_shake_init(hash_state *md, int num);
-#define sha3_shake_process(a,b,c) sha3_process(a,b,c)
-int sha3_shake_done(hash_state *md, unsigned char *out, unsigned long outlen);
-int sha3_shake_test(void);
-int sha3_shake_memory(int num, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen);
-#endif
-
-#ifdef LTC_SHA512
-int sha512_init(hash_state * md);
-int sha512_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha512_done(hash_state * md, unsigned char *hash);
-int sha512_test(void);
-extern const struct ltc_hash_descriptor sha512_desc;
-#endif
-
-#ifdef LTC_SHA384
-#ifndef LTC_SHA512
-   #error LTC_SHA512 is required for LTC_SHA384
-#endif
-int sha384_init(hash_state * md);
-#define sha384_process sha512_process
-int sha384_done(hash_state * md, unsigned char *hash);
-int sha384_test(void);
-extern const struct ltc_hash_descriptor sha384_desc;
-#endif
-
-#ifdef LTC_SHA512_256
-#ifndef LTC_SHA512
-   #error LTC_SHA512 is required for LTC_SHA512_256
-#endif
-int sha512_256_init(hash_state * md);
-#define sha512_256_process sha512_process
-int sha512_256_done(hash_state * md, unsigned char *hash);
-int sha512_256_test(void);
-extern const struct ltc_hash_descriptor sha512_256_desc;
-#endif
-
-#ifdef LTC_SHA512_224
-#ifndef LTC_SHA512
-   #error LTC_SHA512 is required for LTC_SHA512_224
-#endif
-int sha512_224_init(hash_state * md);
-#define sha512_224_process sha512_process
-int sha512_224_done(hash_state * md, unsigned char *hash);
-int sha512_224_test(void);
-extern const struct ltc_hash_descriptor sha512_224_desc;
-#endif
-
-#ifdef LTC_SHA256
-int sha256_init(hash_state * md);
-int sha256_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha256_done(hash_state * md, unsigned char *hash);
-int sha256_test(void);
-extern const struct ltc_hash_descriptor sha256_desc;
-
-#ifdef LTC_SHA224
-#ifndef LTC_SHA256
-   #error LTC_SHA256 is required for LTC_SHA224
-#endif
-int sha224_init(hash_state * md);
-#define sha224_process sha256_process
-int sha224_done(hash_state * md, unsigned char *hash);
-int sha224_test(void);
-extern const struct ltc_hash_descriptor sha224_desc;
-#endif
-#endif
-
-#ifdef LTC_SHA1
-int sha1_init(hash_state * md);
-int sha1_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha1_done(hash_state * md, unsigned char *hash);
-int sha1_test(void);
-extern const struct ltc_hash_descriptor sha1_desc;
-#endif
-
-#ifdef LTC_BLAKE2S
-extern const struct ltc_hash_descriptor blake2s_256_desc;
-int blake2s_256_init(hash_state * md);
-int blake2s_256_test(void);
-
-extern const struct ltc_hash_descriptor blake2s_224_desc;
-int blake2s_224_init(hash_state * md);
-int blake2s_224_test(void);
-
-extern const struct ltc_hash_descriptor blake2s_160_desc;
-int blake2s_160_init(hash_state * md);
-int blake2s_160_test(void);
-
-extern const struct ltc_hash_descriptor blake2s_128_desc;
-int blake2s_128_init(hash_state * md);
-int blake2s_128_test(void);
-
-int blake2s_init(hash_state * md, unsigned long outlen, const unsigned char *key, unsigned long keylen);
-int blake2s_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int blake2s_done(hash_state * md, unsigned char *hash);
-#endif
-
-#ifdef LTC_BLAKE2B
-extern const struct ltc_hash_descriptor blake2b_512_desc;
-int blake2b_512_init(hash_state * md);
-int blake2b_512_test(void);
-
-extern const struct ltc_hash_descriptor blake2b_384_desc;
-int blake2b_384_init(hash_state * md);
-int blake2b_384_test(void);
-
-extern const struct ltc_hash_descriptor blake2b_256_desc;
-int blake2b_256_init(hash_state * md);
-int blake2b_256_test(void);
-
-extern const struct ltc_hash_descriptor blake2b_160_desc;
-int blake2b_160_init(hash_state * md);
-int blake2b_160_test(void);
-
-int blake2b_init(hash_state * md, unsigned long outlen, const unsigned char *key, unsigned long keylen);
-int blake2b_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int blake2b_done(hash_state * md, unsigned char *hash);
-#endif
-
-#ifdef LTC_MD5
-int md5_init(hash_state * md);
-int md5_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int md5_done(hash_state * md, unsigned char *hash);
-int md5_test(void);
-extern const struct ltc_hash_descriptor md5_desc;
-#endif
-
-#ifdef LTC_MD4
-int md4_init(hash_state * md);
-int md4_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int md4_done(hash_state * md, unsigned char *hash);
-int md4_test(void);
-extern const struct ltc_hash_descriptor md4_desc;
-#endif
-
-#ifdef LTC_MD2
-int md2_init(hash_state * md);
-int md2_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int md2_done(hash_state * md, unsigned char *hash);
-int md2_test(void);
-extern const struct ltc_hash_descriptor md2_desc;
-#endif
-
-#ifdef LTC_TIGER
-int tiger_init(hash_state * md);
-int tiger_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int tiger_done(hash_state * md, unsigned char *hash);
-int tiger_test(void);
-extern const struct ltc_hash_descriptor tiger_desc;
-#endif
-
-#ifdef LTC_RIPEMD128
-int rmd128_init(hash_state * md);
-int rmd128_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd128_done(hash_state * md, unsigned char *hash);
-int rmd128_test(void);
-extern const struct ltc_hash_descriptor rmd128_desc;
-#endif
-
-#ifdef LTC_RIPEMD160
-int rmd160_init(hash_state * md);
-int rmd160_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd160_done(hash_state * md, unsigned char *hash);
-int rmd160_test(void);
-extern const struct ltc_hash_descriptor rmd160_desc;
-#endif
-
-#ifdef LTC_RIPEMD256
-int rmd256_init(hash_state * md);
-int rmd256_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd256_done(hash_state * md, unsigned char *hash);
-int rmd256_test(void);
-extern const struct ltc_hash_descriptor rmd256_desc;
-#endif
-
-#ifdef LTC_RIPEMD320
-int rmd320_init(hash_state * md);
-int rmd320_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd320_done(hash_state * md, unsigned char *hash);
-int rmd320_test(void);
-extern const struct ltc_hash_descriptor rmd320_desc;
-#endif
-
-
-int find_hash(const char *name);
-int find_hash_id(unsigned char ID);
-int find_hash_oid(const unsigned long *ID, unsigned long IDlen);
-int find_hash_any(const char *name, int digestlen);
-int register_hash(const struct ltc_hash_descriptor *hash);
-int unregister_hash(const struct ltc_hash_descriptor *hash);
-int register_all_hashes(void);
-int hash_is_valid(int idx);
-
-LTC_MUTEX_PROTO(ltc_hash_mutex)
-
-int hash_memory(int hash,
-                const unsigned char *in,  unsigned long inlen,
-                      unsigned char *out, unsigned long *outlen);
-int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen,
-                      const unsigned char *in, unsigned long inlen, ...);
-
-#ifndef LTC_NO_FILE
-int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen);
-int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen);
-#endif
-
-/* a simple macro for making hash "process" functions */
-#define HASH_PROCESS(func_name, compress_name, state_var, block_size)                       \
-int func_name (hash_state * md, const unsigned char *in, unsigned long inlen)               \
-{                                                                                           \
-    unsigned long n;                                                                        \
-    int           err;                                                                      \
-    LTC_ARGCHK(md != NULL);                                                                 \
-    LTC_ARGCHK(in != NULL);                                                                 \
-    if (md-> state_var .curlen > sizeof(md-> state_var .buf)) {                             \
-       return CRYPT_INVALID_ARG;                                                            \
-    }                                                                                       \
-    if ((md-> state_var .length + inlen) < md-> state_var .length) {                        \
-      return CRYPT_HASH_OVERFLOW;                                                           \
-    }                                                                                       \
-    while (inlen > 0) {                                                                     \
-        if (md-> state_var .curlen == 0 && inlen >= block_size) {                           \
-           if ((err = compress_name (md, (unsigned char *)in)) != CRYPT_OK) {               \
-              return err;                                                                   \
-           }                                                                                \
-           md-> state_var .length += block_size * 8;                                        \
-           in             += block_size;                                                    \
-           inlen          -= block_size;                                                    \
-        } else {                                                                            \
-           n = MIN(inlen, (block_size - md-> state_var .curlen));                           \
-           XMEMCPY(md-> state_var .buf + md-> state_var.curlen, in, (size_t)n);             \
-           md-> state_var .curlen += n;                                                     \
-           in             += n;                                                             \
-           inlen          -= n;                                                             \
-           if (md-> state_var .curlen == block_size) {                                      \
-              if ((err = compress_name (md, md-> state_var .buf)) != CRYPT_OK) {            \
-                 return err;                                                                \
-              }                                                                             \
-              md-> state_var .length += 8*block_size;                                       \
-              md-> state_var .curlen = 0;                                                   \
-           }                                                                                \
-       }                                                                                    \
-    }                                                                                       \
-    return CRYPT_OK;                                                                        \
-}
diff --git a/libs/tomcrypt/src/headers/tomcrypt_mac.h b/libs/tomcrypt/src/headers/tomcrypt_mac.h
deleted file mode 100644
index 70378ff61de3..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_mac.h
+++ /dev/null
@@ -1,561 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#ifdef LTC_HMAC
-typedef struct Hmac_state {
-     hash_state     md;
-     int            hash;
-     hash_state     hashstate;
-     unsigned char  *key;
-} hmac_state;
-
-int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen);
-int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen);
-int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen);
-int hmac_test(void);
-int hmac_memory(int hash,
-                const unsigned char *key, unsigned long keylen,
-                const unsigned char *in,  unsigned long inlen,
-                      unsigned char *out, unsigned long *outlen);
-int hmac_memory_multi(int hash,
-                const unsigned char *key,  unsigned long keylen,
-                      unsigned char *out,  unsigned long *outlen,
-                const unsigned char *in,   unsigned long inlen, ...);
-int hmac_file(int hash, const char *fname, const unsigned char *key,
-              unsigned long keylen,
-              unsigned char *dst, unsigned long *dstlen);
-#endif
-
-#ifdef LTC_OMAC
-
-typedef struct {
-   int             cipher_idx,
-                   buflen,
-                   blklen;
-   unsigned char   block[MAXBLOCKSIZE],
-                   prev[MAXBLOCKSIZE],
-                   Lu[2][MAXBLOCKSIZE];
-   symmetric_key   key;
-} omac_state;
-
-int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen);
-int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen);
-int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen);
-int omac_memory(int cipher,
-               const unsigned char *key, unsigned long keylen,
-               const unsigned char *in,  unsigned long inlen,
-                     unsigned char *out, unsigned long *outlen);
-int omac_memory_multi(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in,  unsigned long inlen, ...);
-int omac_file(int cipher,
-              const unsigned char *key, unsigned long keylen,
-              const          char *filename,
-                    unsigned char *out, unsigned long *outlen);
-int omac_test(void);
-#endif /* LTC_OMAC */
-
-#ifdef LTC_PMAC
-
-typedef struct {
-   unsigned char     Ls[32][MAXBLOCKSIZE],    /* L shifted by i bits to the left */
-                     Li[MAXBLOCKSIZE],        /* value of Li [current value, we calc from previous recall] */
-                     Lr[MAXBLOCKSIZE],        /* L * x^-1 */
-                     block[MAXBLOCKSIZE],     /* currently accumulated block */
-                     checksum[MAXBLOCKSIZE];  /* current checksum */
-
-   symmetric_key     key;                     /* scheduled key for cipher */
-   unsigned long     block_index;             /* index # for current block */
-   int               cipher_idx,              /* cipher idx */
-                     block_len,               /* length of block */
-                     buflen;                  /* number of bytes in the buffer */
-} pmac_state;
-
-int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen);
-int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen);
-int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen);
-
-int pmac_memory(int cipher,
-               const unsigned char *key, unsigned long keylen,
-               const unsigned char *msg, unsigned long msglen,
-                     unsigned char *out, unsigned long *outlen);
-
-int pmac_memory_multi(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in, unsigned long inlen, ...);
-
-int pmac_file(int cipher,
-             const unsigned char *key, unsigned long keylen,
-             const          char *filename,
-                   unsigned char *out, unsigned long *outlen);
-
-int pmac_test(void);
-
-/* internal functions */
-int pmac_ntz(unsigned long x);
-void pmac_shift_xor(pmac_state *pmac);
-
-#endif /* PMAC */
-
-#ifdef LTC_POLY1305
-typedef struct {
-   ulong32 r[5];
-   ulong32 h[5];
-   ulong32 pad[4];
-   unsigned long leftover;
-   unsigned char buffer[16];
-   int final;
-} poly1305_state;
-
-int poly1305_init(poly1305_state *st, const unsigned char *key, unsigned long keylen);
-int poly1305_process(poly1305_state *st, const unsigned char *in, unsigned long inlen);
-int poly1305_done(poly1305_state *st, unsigned char *mac, unsigned long *maclen);
-int poly1305_memory(const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *mac, unsigned long *maclen);
-int poly1305_memory_multi(const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen, const unsigned char *in,  unsigned long inlen, ...);
-int poly1305_file(const char *fname, const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen);
-int poly1305_test(void);
-#endif /* LTC_POLY1305 */
-
-#ifdef LTC_BLAKE2SMAC
-typedef hash_state blake2smac_state;
-int blake2smac_init(blake2smac_state *st, unsigned long outlen, const unsigned char *key, unsigned long keylen);
-int blake2smac_process(blake2smac_state *st, const unsigned char *in, unsigned long inlen);
-int blake2smac_done(blake2smac_state *st, unsigned char *mac, unsigned long *maclen);
-int blake2smac_memory(const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *mac, unsigned long *maclen);
-int blake2smac_memory_multi(const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen, const unsigned char *in,  unsigned long inlen, ...);
-int blake2smac_file(const char *fname, const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen);
-int blake2smac_test(void);
-#endif /* LTC_BLAKE2SMAC */
-
-#ifdef LTC_BLAKE2BMAC
-typedef hash_state blake2bmac_state;
-int blake2bmac_init(blake2bmac_state *st, unsigned long outlen, const unsigned char *key, unsigned long keylen);
-int blake2bmac_process(blake2bmac_state *st, const unsigned char *in, unsigned long inlen);
-int blake2bmac_done(blake2bmac_state *st, unsigned char *mac, unsigned long *maclen);
-int blake2bmac_memory(const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *mac, unsigned long *maclen);
-int blake2bmac_memory_multi(const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen, const unsigned char *in,  unsigned long inlen, ...);
-int blake2bmac_file(const char *fname, const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen);
-int blake2bmac_test(void);
-#endif /* LTC_BLAKE2BMAC */
-
-#ifdef LTC_EAX_MODE
-
-#if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE))
-   #error LTC_EAX_MODE requires LTC_OMAC and CTR
-#endif
-
-typedef struct {
-   unsigned char N[MAXBLOCKSIZE];
-   symmetric_CTR ctr;
-   omac_state    headeromac, ctomac;
-} eax_state;
-
-int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen,
-             const unsigned char *nonce, unsigned long noncelen,
-             const unsigned char *header, unsigned long headerlen);
-
-int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length);
-int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length);
-int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length);
-int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen);
-
-int eax_encrypt_authenticate_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *header, unsigned long headerlen,
-    const unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen);
-
-int eax_decrypt_verify_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *header, unsigned long headerlen,
-    const unsigned char *ct,     unsigned long ctlen,
-          unsigned char *pt,
-          unsigned char *tag,    unsigned long taglen,
-          int           *stat);
-
- int eax_test(void);
-#endif /* EAX MODE */
-
-#ifdef LTC_OCB_MODE
-typedef struct {
-   unsigned char     L[MAXBLOCKSIZE],         /* L value */
-                     Ls[32][MAXBLOCKSIZE],    /* L shifted by i bits to the left */
-                     Li[MAXBLOCKSIZE],        /* value of Li [current value, we calc from previous recall] */
-                     Lr[MAXBLOCKSIZE],        /* L * x^-1 */
-                     R[MAXBLOCKSIZE],         /* R value */
-                     checksum[MAXBLOCKSIZE];  /* current checksum */
-
-   symmetric_key     key;                     /* scheduled key for cipher */
-   unsigned long     block_index;             /* index # for current block */
-   int               cipher,                  /* cipher idx */
-                     block_len;               /* length of block */
-} ocb_state;
-
-int ocb_init(ocb_state *ocb, int cipher,
-             const unsigned char *key, unsigned long keylen, const unsigned char *nonce);
-
-int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct);
-int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt);
-
-int ocb_done_encrypt(ocb_state *ocb,
-                     const unsigned char *pt,  unsigned long ptlen,
-                           unsigned char *ct,
-                           unsigned char *tag, unsigned long *taglen);
-
-int ocb_done_decrypt(ocb_state *ocb,
-                     const unsigned char *ct,  unsigned long ctlen,
-                           unsigned char *pt,
-                     const unsigned char *tag, unsigned long taglen, int *stat);
-
-int ocb_encrypt_authenticate_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,
-    const unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen);
-
-int ocb_decrypt_verify_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,
-    const unsigned char *ct,     unsigned long ctlen,
-          unsigned char *pt,
-    const unsigned char *tag,    unsigned long taglen,
-          int           *stat);
-
-int ocb_test(void);
-
-/* internal functions */
-void ocb_shift_xor(ocb_state *ocb, unsigned char *Z);
-int ocb_ntz(unsigned long x);
-int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen,
-               unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode);
-
-#endif /* LTC_OCB_MODE */
-
-#ifdef LTC_OCB3_MODE
-typedef struct {
-   unsigned char     Offset_0[MAXBLOCKSIZE],       /* Offset_0 value */
-                     Offset_current[MAXBLOCKSIZE], /* Offset_{current_block_index} value */
-                     L_dollar[MAXBLOCKSIZE],       /* L_$ value */
-                     L_star[MAXBLOCKSIZE],         /* L_* value */
-                     L_[32][MAXBLOCKSIZE],         /* L_{i} values */
-                     tag_part[MAXBLOCKSIZE],       /* intermediate result of tag calculation */
-                     checksum[MAXBLOCKSIZE];       /* current checksum */
-
-   /* AAD related members */
-   unsigned char     aSum_current[MAXBLOCKSIZE],    /* AAD related helper variable */
-                     aOffset_current[MAXBLOCKSIZE], /* AAD related helper variable */
-                     adata_buffer[MAXBLOCKSIZE];    /* AAD buffer */
-   int               adata_buffer_bytes;            /* bytes in AAD buffer */
-   unsigned long     ablock_index;                  /* index # for current adata (AAD) block */
-
-   symmetric_key     key;                     /* scheduled key for cipher */
-   unsigned long     block_index;             /* index # for current data block */
-   int               cipher,                  /* cipher idx */
-                     tag_len,                 /* length of tag */
-                     block_len;               /* length of block */
-} ocb3_state;
-
-int ocb3_init(ocb3_state *ocb, int cipher,
-             const unsigned char *key, unsigned long keylen,
-             const unsigned char *nonce, unsigned long noncelen,
-             unsigned long taglen);
-
-int ocb3_encrypt(ocb3_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct);
-int ocb3_decrypt(ocb3_state *ocb, const unsigned char *ct, unsigned long ctlen, unsigned char *pt);
-int ocb3_encrypt_last(ocb3_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct);
-int ocb3_decrypt_last(ocb3_state *ocb, const unsigned char *ct, unsigned long ctlen, unsigned char *pt);
-int ocb3_add_aad(ocb3_state *ocb, const unsigned char *aad, unsigned long aadlen);
-int ocb3_done(ocb3_state *ocb, unsigned char *tag, unsigned long *taglen);
-
-int ocb3_encrypt_authenticate_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *adata,  unsigned long adatalen,
-    const unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen);
-
-int ocb3_decrypt_verify_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *adata,  unsigned long adatalen,
-    const unsigned char *ct,     unsigned long ctlen,
-          unsigned char *pt,
-    const unsigned char *tag,    unsigned long taglen,
-          int           *stat);
-
-int ocb3_test(void);
-
-#ifdef LTC_SOURCE
-/* internal helper functions */
-int ocb3_int_ntz(unsigned long x);
-void ocb3_int_xor_blocks(unsigned char *out, const unsigned char *block_a, const unsigned char *block_b, unsigned long block_len);
-#endif /* LTC_SOURCE */
-
-#endif /* LTC_OCB3_MODE */
-
-#ifdef LTC_CCM_MODE
-
-#define CCM_ENCRYPT LTC_ENCRYPT
-#define CCM_DECRYPT LTC_DECRYPT
-
-typedef struct {
-   symmetric_key       K;
-   int                 cipher,               /* which cipher */
-                       taglen,               /* length of the tag */
-                       x;                    /* index in PAD */
-
-   unsigned long       L,                    /* L value */
-                       ptlen,                /* length that will be enc / dec */
-                       current_ptlen,        /* current processed length */
-                       aadlen,               /* length of the aad */
-                       current_aadlen,       /* length of the currently provided add */
-                       noncelen;             /* length of the nonce */
-
-   unsigned char       PAD[16],
-                       ctr[16],
-                       CTRPAD[16],
-                       CTRlen;
-} ccm_state;
-
-int ccm_init(ccm_state *ccm, int cipher,
-             const unsigned char *key, int keylen, int ptlen, int taglen, int aad_len);
-
-int ccm_reset(ccm_state *ccm);
-
-int ccm_add_nonce(ccm_state *ccm,
-                  const unsigned char *nonce,     unsigned long noncelen);
-
-int ccm_add_aad(ccm_state *ccm,
-                const unsigned char *adata,  unsigned long adatalen);
-
-int ccm_process(ccm_state *ccm,
-                unsigned char *pt,     unsigned long ptlen,
-                unsigned char *ct,
-                int direction);
-
-int ccm_done(ccm_state *ccm,
-             unsigned char *tag,    unsigned long *taglen);
-
-int ccm_memory(int cipher,
-    const unsigned char *key,    unsigned long keylen,
-    symmetric_key       *uskey,
-    const unsigned char *nonce,  unsigned long noncelen,
-    const unsigned char *header, unsigned long headerlen,
-          unsigned char *pt,     unsigned long ptlen,
-          unsigned char *ct,
-          unsigned char *tag,    unsigned long *taglen,
-                    int  direction);
-
-int ccm_test(void);
-
-#endif /* LTC_CCM_MODE */
-
-#if defined(LRW_MODE) || defined(LTC_GCM_MODE)
-void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c);
-#endif
-
-
-/* table shared between GCM and LRW */
-#if defined(LTC_GCM_TABLES) || defined(LTC_LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST))
-extern const unsigned char gcm_shift_table[];
-#endif
-
-#ifdef LTC_GCM_MODE
-
-#define GCM_ENCRYPT LTC_ENCRYPT
-#define GCM_DECRYPT LTC_DECRYPT
-
-#define LTC_GCM_MODE_IV    0
-#define LTC_GCM_MODE_AAD   1
-#define LTC_GCM_MODE_TEXT  2
-
-typedef struct {
-   symmetric_key       K;
-   unsigned char       H[16],        /* multiplier */
-                       X[16],        /* accumulator */
-                       Y[16],        /* counter */
-                       Y_0[16],      /* initial counter */
-                       buf[16];      /* buffer for stuff */
-
-   int                 cipher,       /* which cipher */
-                       ivmode,       /* Which mode is the IV in? */
-                       mode,         /* mode the GCM code is in */
-                       buflen;       /* length of data in buf */
-
-   ulong64             totlen,       /* 64-bit counter used for IV and AAD */
-                       pttotlen;     /* 64-bit counter for the PT */
-
-#ifdef LTC_GCM_TABLES
-   unsigned char       PC[16][256][16]  /* 16 tables of 8x128 */
-#ifdef LTC_GCM_TABLES_SSE2
-__attribute__ ((aligned (16)))
-#endif
-;
-#endif
-} gcm_state;
-
-void gcm_mult_h(gcm_state *gcm, unsigned char *I);
-
-int gcm_init(gcm_state *gcm, int cipher,
-             const unsigned char *key, int keylen);
-
-int gcm_reset(gcm_state *gcm);
-
-int gcm_add_iv(gcm_state *gcm,
-               const unsigned char *IV,     unsigned long IVlen);
-
-int gcm_add_aad(gcm_state *gcm,
-               const unsigned char *adata,  unsigned long adatalen);
-
-int gcm_process(gcm_state *gcm,
-                     unsigned char *pt,     unsigned long ptlen,
-                     unsigned char *ct,
-                     int direction);
-
-int gcm_done(gcm_state *gcm,
-                     unsigned char *tag,    unsigned long *taglen);
-
-int gcm_memory(      int           cipher,
-               const unsigned char *key,    unsigned long keylen,
-               const unsigned char *IV,     unsigned long IVlen,
-               const unsigned char *adata,  unsigned long adatalen,
-                     unsigned char *pt,     unsigned long ptlen,
-                     unsigned char *ct,
-                     unsigned char *tag,    unsigned long *taglen,
-                               int direction);
-int gcm_test(void);
-
-#endif /* LTC_GCM_MODE */
-
-#ifdef LTC_PELICAN
-
-typedef struct pelican_state
-{
-    symmetric_key K;
-    unsigned char state[16];
-    int           buflen;
-} pelican_state;
-
-int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen);
-int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen);
-int pelican_done(pelican_state *pelmac, unsigned char *out);
-int pelican_test(void);
-
-int pelican_memory(const unsigned char *key, unsigned long keylen,
-                   const unsigned char *in, unsigned long inlen,
-                         unsigned char *out);
-
-#endif
-
-#ifdef LTC_XCBC
-
-/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */
-#define LTC_XCBC_PURE  0x8000UL
-
-typedef struct {
-   unsigned char K[3][MAXBLOCKSIZE],
-                 IV[MAXBLOCKSIZE];
-
-   symmetric_key key;
-
-             int cipher,
-                 buflen,
-                 blocksize;
-} xcbc_state;
-
-int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen);
-int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen);
-int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen);
-int xcbc_memory(int cipher,
-               const unsigned char *key, unsigned long keylen,
-               const unsigned char *in,  unsigned long inlen,
-                     unsigned char *out, unsigned long *outlen);
-int xcbc_memory_multi(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in,  unsigned long inlen, ...);
-int xcbc_file(int cipher,
-              const unsigned char *key, unsigned long keylen,
-              const          char *filename,
-                    unsigned char *out, unsigned long *outlen);
-int xcbc_test(void);
-
-#endif
-
-#ifdef LTC_F9_MODE
-
-typedef struct {
-   unsigned char akey[MAXBLOCKSIZE],
-                 ACC[MAXBLOCKSIZE],
-                 IV[MAXBLOCKSIZE];
-
-   symmetric_key key;
-
-             int cipher,
-                 buflen,
-                 keylen,
-                 blocksize;
-} f9_state;
-
-int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen);
-int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen);
-int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen);
-int f9_memory(int cipher,
-               const unsigned char *key, unsigned long keylen,
-               const unsigned char *in,  unsigned long inlen,
-                     unsigned char *out, unsigned long *outlen);
-int f9_memory_multi(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in,  unsigned long inlen, ...);
-int f9_file(int cipher,
-              const unsigned char *key, unsigned long keylen,
-              const          char *filename,
-                    unsigned char *out, unsigned long *outlen);
-int f9_test(void);
-
-#endif
-
-#ifdef LTC_CHACHA20POLY1305_MODE
-
-typedef struct {
-   poly1305_state poly;
-   chacha_state chacha;
-   ulong64 aadlen;
-   ulong64 ctlen;
-   int aadflg;
-} chacha20poly1305_state;
-
-#define CHACHA20POLY1305_ENCRYPT LTC_ENCRYPT
-#define CHACHA20POLY1305_DECRYPT LTC_DECRYPT
-
-int chacha20poly1305_init(chacha20poly1305_state *st, const unsigned char *key, unsigned long keylen);
-int chacha20poly1305_setiv(chacha20poly1305_state *st, const unsigned char *iv, unsigned long ivlen);
-int chacha20poly1305_setiv_rfc7905(chacha20poly1305_state *st, const unsigned char *iv, unsigned long ivlen, ulong64 sequence_number);
-int chacha20poly1305_add_aad(chacha20poly1305_state *st, const unsigned char *in, unsigned long inlen);
-int chacha20poly1305_encrypt(chacha20poly1305_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out);
-int chacha20poly1305_decrypt(chacha20poly1305_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out);
-int chacha20poly1305_done(chacha20poly1305_state *st, unsigned char *tag, unsigned long *taglen);
-int chacha20poly1305_memory(const unsigned char *key, unsigned long keylen,
-                            const unsigned char *iv,  unsigned long ivlen,
-                            const unsigned char *aad, unsigned long aadlen,
-                            const unsigned char *in,  unsigned long inlen,
-                                  unsigned char *out,
-                                  unsigned char *tag, unsigned long *taglen,
-                            int direction);
-int chacha20poly1305_test(void);
-
-#endif /* LTC_CHACHA20POLY1305_MODE */
diff --git a/libs/tomcrypt/src/headers/tomcrypt_macros.h b/libs/tomcrypt/src/headers/tomcrypt_macros.h
deleted file mode 100644
index 19ec63bdd1bc..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_macros.h
+++ /dev/null
@@ -1,442 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* ---- HELPER MACROS ---- */
-#ifdef ENDIAN_NEUTRAL
-
-#define STORE32L(x, y)                                                                     \
-  do { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \
-       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD32L(x, y)                            \
-  do { x = ((ulong32)((y)[3] & 255)<<24) | \
-           ((ulong32)((y)[2] & 255)<<16) | \
-           ((ulong32)((y)[1] & 255)<<8)  | \
-           ((ulong32)((y)[0] & 255)); } while(0)
-
-#define STORE64L(x, y)                                                                     \
-  do { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);   \
-       (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);   \
-       (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \
-       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD64L(x, y)                                                       \
-  do { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \
-           (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \
-           (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \
-           (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } while(0)
-
-#define STORE32H(x, y)                                                                     \
-  do { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \
-       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD32H(x, y)                            \
-  do { x = ((ulong32)((y)[0] & 255)<<24) | \
-           ((ulong32)((y)[1] & 255)<<16) | \
-           ((ulong32)((y)[2] & 255)<<8)  | \
-           ((ulong32)((y)[3] & 255)); } while(0)
-
-#define STORE64H(x, y)                                                                     \
-do { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);     \
-     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);     \
-     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);     \
-     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD64H(x, y)                                                      \
-do { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \
-         (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \
-         (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \
-         (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } while(0)
-
-
-#elif defined(ENDIAN_LITTLE)
-
-#ifdef LTC_HAVE_BSWAP_BUILTIN
-
-#define STORE32H(x, y)                          \
-do { ulong32 __t = __builtin_bswap32 ((x));     \
-      XMEMCPY ((y), &__t, 4); } while(0)
-
-#define LOAD32H(x, y)                           \
-do { XMEMCPY (&(x), (y), 4);                    \
-      (x) = __builtin_bswap32 ((x)); } while(0)
-
-#elif !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__))))
-
-#define STORE32H(x, y)           \
-asm __volatile__ (               \
-   "bswapl %0     \n\t"          \
-   "movl   %0,(%1)\n\t"          \
-   "bswapl %0     \n\t"          \
-      ::"r"(x), "r"(y));
-
-#define LOAD32H(x, y)          \
-asm __volatile__ (             \
-   "movl (%1),%0\n\t"          \
-   "bswapl %0\n\t"             \
-   :"=r"(x): "r"(y));
-
-#else
-
-#define STORE32H(x, y)                                                                     \
-  do { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \
-       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD32H(x, y)                            \
-  do { x = ((ulong32)((y)[0] & 255)<<24) | \
-           ((ulong32)((y)[1] & 255)<<16) | \
-           ((ulong32)((y)[2] & 255)<<8)  | \
-           ((ulong32)((y)[3] & 255)); } while(0)
-
-#endif
-
-#ifdef LTC_HAVE_BSWAP_BUILTIN
-
-#define STORE64H(x, y)                          \
-do { ulong64 __t = __builtin_bswap64 ((x));     \
-      XMEMCPY ((y), &__t, 8); } while(0)
-
-#define LOAD64H(x, y)                           \
-do { XMEMCPY (&(x), (y), 8);                    \
-      (x) = __builtin_bswap64 ((x)); } while(0)
-
-/* x86_64 processor */
-#elif !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__))
-
-#define STORE64H(x, y)           \
-asm __volatile__ (               \
-   "bswapq %0     \n\t"          \
-   "movq   %0,(%1)\n\t"          \
-   "bswapq %0     \n\t"          \
-   ::"r"(x), "r"(y): "memory");
-
-#define LOAD64H(x, y)          \
-asm __volatile__ (             \
-   "movq (%1),%0\n\t"          \
-   "bswapq %0\n\t"             \
-   :"=r"(x): "r"(y): "memory");
-
-#else
-
-#define STORE64H(x, y)                                                                     \
-do { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);     \
-     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);     \
-     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);     \
-     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD64H(x, y)                                                      \
-do { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \
-         (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \
-         (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \
-         (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } while(0)
-
-#endif
-
-#ifdef ENDIAN_32BITWORD
-
-#define STORE32L(x, y)        \
-  do { ulong32  __t = (x); XMEMCPY(y, &__t, 4); } while(0)
-
-#define LOAD32L(x, y)         \
-  do { XMEMCPY(&(x), y, 4); } while(0)
-
-#define STORE64L(x, y)                                                                     \
-  do { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);   \
-       (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);   \
-       (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \
-       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD64L(x, y)                                                       \
-  do { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \
-           (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \
-           (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \
-           (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } while(0)
-
-#else /* 64-bit words then  */
-
-#define STORE32L(x, y)        \
-  do { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } while(0)
-
-#define LOAD32L(x, y)         \
-  do { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } while(0)
-
-#define STORE64L(x, y)        \
-  do { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } while(0)
-
-#define LOAD64L(x, y)         \
-  do { XMEMCPY(&(x), y, 8); } while(0)
-
-#endif /* ENDIAN_64BITWORD */
-
-#elif defined(ENDIAN_BIG)
-
-#define STORE32L(x, y)                                                                     \
-  do { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \
-       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD32L(x, y)                            \
-  do { x = ((ulong32)((y)[3] & 255)<<24) | \
-           ((ulong32)((y)[2] & 255)<<16) | \
-           ((ulong32)((y)[1] & 255)<<8)  | \
-           ((ulong32)((y)[0] & 255)); } while(0)
-
-#define STORE64L(x, y)                                                                     \
-do { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);     \
-     (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);     \
-     (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);     \
-     (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD64L(x, y)                                                      \
-do { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48) | \
-         (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32) | \
-         (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16) | \
-         (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } while(0)
-
-#ifdef ENDIAN_32BITWORD
-
-#define STORE32H(x, y)        \
-  do { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } while(0)
-
-#define LOAD32H(x, y)         \
-  do { XMEMCPY(&(x), y, 4); } while(0)
-
-#define STORE64H(x, y)                                                                     \
-  do { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);   \
-       (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);   \
-       (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);   \
-       (y)[6] = (unsigned char)(((x)>>8)&255);  (y)[7] = (unsigned char)((x)&255); } while(0)
-
-#define LOAD64H(x, y)                                                       \
-  do { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48)| \
-           (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32)| \
-           (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16)| \
-           (((ulong64)((y)[6] & 255))<<8)| (((ulong64)((y)[7] & 255))); } while(0)
-
-#else /* 64-bit words then  */
-
-#define STORE32H(x, y)        \
-  do { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } while(0)
-
-#define LOAD32H(x, y)         \
-  do { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } while(0)
-
-#define STORE64H(x, y)        \
-  do { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } while(0)
-
-#define LOAD64H(x, y)         \
-  do { XMEMCPY(&(x), y, 8); } while(0)
-
-#endif /* ENDIAN_64BITWORD */
-#endif /* ENDIAN_BIG */
-
-#define BSWAP(x)  ( ((x>>24)&0x000000FFUL) | ((x<<24)&0xFF000000UL)  | \
-                    ((x>>8)&0x0000FF00UL)  | ((x<<8)&0x00FF0000UL) )
-
-
-/* 32-bit Rotates */
-#if defined(_MSC_VER)
-#define LTC_ROx_ASM
-
-/* instrinsic rotate */
-#include <stdlib.h>
-#pragma intrinsic(_lrotr,_lrotl)
-#define ROR(x,n) _lrotr(x,n)
-#define ROL(x,n) _lrotl(x,n)
-#define RORc(x,n) _lrotr(x,n)
-#define ROLc(x,n) _lrotl(x,n)
-
-#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM)
-#define LTC_ROx_ASM
-
-static inline ulong32 ROL(ulong32 word, int i)
-{
-   asm ("roll %%cl,%0"
-      :"=r" (word)
-      :"0" (word),"c" (i));
-   return word;
-}
-
-static inline ulong32 ROR(ulong32 word, int i)
-{
-   asm ("rorl %%cl,%0"
-      :"=r" (word)
-      :"0" (word),"c" (i));
-   return word;
-}
-
-#ifndef LTC_NO_ROLC
-
-#define ROLc(word,i) ({ \
-   ulong32 __ROLc_tmp = (word); \
-   __asm__ ("roll %2, %0" : \
-            "=r" (__ROLc_tmp) : \
-            "0" (__ROLc_tmp), \
-            "I" (i)); \
-            __ROLc_tmp; \
-   })
-#define RORc(word,i) ({ \
-   ulong32 __RORc_tmp = (word); \
-   __asm__ ("rorl %2, %0" : \
-            "=r" (__RORc_tmp) : \
-            "0" (__RORc_tmp), \
-            "I" (i)); \
-            __RORc_tmp; \
-   })
-
-#else
-
-#define ROLc ROL
-#define RORc ROR
-
-#endif
-
-#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32)
-#define LTC_ROx_ASM
-
-static inline ulong32 ROL(ulong32 word, int i)
-{
-   asm ("rotlw %0,%0,%2"
-      :"=r" (word)
-      :"0" (word),"r" (i));
-   return word;
-}
-
-static inline ulong32 ROR(ulong32 word, int i)
-{
-   asm ("rotlw %0,%0,%2"
-      :"=r" (word)
-      :"0" (word),"r" (32-i));
-   return word;
-}
-
-#ifndef LTC_NO_ROLC
-
-static inline ulong32 ROLc(ulong32 word, const int i)
-{
-   asm ("rotlwi %0,%0,%2"
-      :"=r" (word)
-      :"0" (word),"I" (i));
-   return word;
-}
-
-static inline ulong32 RORc(ulong32 word, const int i)
-{
-   asm ("rotrwi %0,%0,%2"
-      :"=r" (word)
-      :"0" (word),"I" (i));
-   return word;
-}
-
-#else
-
-#define ROLc ROL
-#define RORc ROR
-
-#endif
-
-
-#else
-
-/* rotates the hard way */
-#define ROL(x, y) ( (((ulong32)(x)<<(ulong32)((y)&31)) | (((ulong32)(x)&0xFFFFFFFFUL)>>(ulong32)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
-#define ROR(x, y) ( ((((ulong32)(x)&0xFFFFFFFFUL)>>(ulong32)((y)&31)) | ((ulong32)(x)<<(ulong32)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
-#define ROLc(x, y) ( (((ulong32)(x)<<(ulong32)((y)&31)) | (((ulong32)(x)&0xFFFFFFFFUL)>>(ulong32)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
-#define RORc(x, y) ( ((((ulong32)(x)&0xFFFFFFFFUL)>>(ulong32)((y)&31)) | ((ulong32)(x)<<(ulong32)((32-((y)&31))&31))) & 0xFFFFFFFFUL)
-
-#endif
-
-
-/* 64-bit Rotates */
-#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(_WIN64) && !defined(LTC_NO_ASM)
-
-static inline ulong64 ROL64(ulong64 word, int i)
-{
-   asm("rolq %%cl,%0"
-      :"=r" (word)
-      :"0" (word),"c" (i));
-   return word;
-}
-
-static inline ulong64 ROR64(ulong64 word, int i)
-{
-   asm("rorq %%cl,%0"
-      :"=r" (word)
-      :"0" (word),"c" (i));
-   return word;
-}
-
-#ifndef LTC_NO_ROLC
-
-#define ROL64c(word,i) ({ \
-   ulong64 __ROL64c_tmp = word; \
-   __asm__ ("rolq %2, %0" : \
-            "=r" (__ROL64c_tmp) : \
-            "0" (__ROL64c_tmp), \
-            "J" (i)); \
-            __ROL64c_tmp; \
-   })
-#define ROR64c(word,i) ({ \
-   ulong64 __ROR64c_tmp = word; \
-   __asm__ ("rorq %2, %0" : \
-            "=r" (__ROR64c_tmp) : \
-            "0" (__ROR64c_tmp), \
-            "J" (i)); \
-            __ROR64c_tmp; \
-   })
-
-#else /* LTC_NO_ROLC */
-
-#define ROL64c ROL64
-#define ROR64c ROR64
-
-#endif
-
-#else /* Not x86_64  */
-
-#define ROL64(x, y) \
-    ( (((x)<<((ulong64)(y)&63)) | \
-      (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>(((ulong64)64-((y)&63))&63))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#define ROR64(x, y) \
-    ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \
-      ((x)<<(((ulong64)64-((y)&63))&63))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#define ROL64c(x, y) \
-    ( (((x)<<((ulong64)(y)&63)) | \
-      (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>(((ulong64)64-((y)&63))&63))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#define ROR64c(x, y) \
-    ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \
-      ((x)<<(((ulong64)64-((y)&63))&63))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#endif
-
-#ifndef MAX
-   #define MAX(x, y) ( ((x)>(y))?(x):(y) )
-#endif
-
-#ifndef MIN
-   #define MIN(x, y) ( ((x)<(y))?(x):(y) )
-#endif
-
-#ifndef LTC_UNUSED_PARAM
-   #define LTC_UNUSED_PARAM(x) (void)(x)
-#endif
-
-/* extract a byte portably */
-#ifdef _MSC_VER
-   #define byte(x, n) ((unsigned char)((x) >> (8 * (n))))
-#else
-   #define byte(x, n) (((x) >> (8 * (n))) & 255)
-#endif
-
-/* there is no snprintf before Visual C++ 2015 */
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#define snprintf _snprintf
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt_math.h b/libs/tomcrypt/src/headers/tomcrypt_math.h
deleted file mode 100644
index 986c026c2fbf..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_math.h
+++ /dev/null
@@ -1,579 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/** math functions **/
-
-#define LTC_MP_LT   -1
-#define LTC_MP_EQ    0
-#define LTC_MP_GT    1
-
-#define LTC_MP_NO    0
-#define LTC_MP_YES   1
-
-#ifndef LTC_MECC
-   typedef void ecc_point;
-#endif
-
-#ifndef LTC_MRSA
-   typedef void rsa_key;
-#endif
-
-#ifndef LTC_MILLER_RABIN_REPS
-   /* Number of rounds of the Miller-Rabin test
-    * "Reasonable values of reps are between 15 and 50." c.f. gmp doc of mpz_probab_prime_p()
-    * As of https://security.stackexchange.com/a/4546 we should use 40 rounds */
-   #define LTC_MILLER_RABIN_REPS    40
-#endif
-
-int radix_to_bin(const void *in, int radix, void *out, unsigned long *len);
-
-/** math descriptor */
-typedef struct {
-   /** Name of the math provider */
-   const char *name;
-
-   /** Bits per digit, amount of bits must fit in an unsigned long */
-   int  bits_per_digit;
-
-/* ---- init/deinit functions ---- */
-
-   /** initialize a bignum
-     @param   a     The number to initialize
-     @return  CRYPT_OK on success
-   */
-   int (*init)(void **a);
-
-   /** init copy
-     @param  dst    The number to initialize and write to
-     @param  src    The number to copy from
-     @return CRYPT_OK on success
-   */
-   int (*init_copy)(void **dst, void *src);
-
-   /** deinit
-      @param   a    The number to free
-      @return CRYPT_OK on success
-   */
-   void (*deinit)(void *a);
-
-/* ---- data movement ---- */
-
-   /** negate
-      @param   src   The number to negate
-      @param   dst   The destination
-      @return CRYPT_OK on success
-   */
-   int (*neg)(void *src, void *dst);
-
-   /** copy
-      @param   src   The number to copy from
-      @param   dst   The number to write to
-      @return CRYPT_OK on success
-   */
-   int (*copy)(void *src, void *dst);
-
-/* ---- trivial low level functions ---- */
-
-   /** set small constant
-      @param a    Number to write to
-      @param n    Source upto bits_per_digit (actually meant for very small constants)
-      @return CRYPT_OK on success
-   */
-   int (*set_int)(void *a, ltc_mp_digit n);
-
-   /** get small constant
-      @param a  Small number to read,
-                only fetches up to bits_per_digit from the number
-      @return   The lower bits_per_digit of the integer (unsigned)
-   */
-   unsigned long (*get_int)(void *a);
-
-   /** get digit n
-     @param a  The number to read from
-     @param n  The number of the digit to fetch
-     @return  The bits_per_digit  sized n'th digit of a
-   */
-   ltc_mp_digit (*get_digit)(void *a, int n);
-
-   /** Get the number of digits that represent the number
-     @param a   The number to count
-     @return The number of digits used to represent the number
-   */
-   int (*get_digit_count)(void *a);
-
-   /** compare two integers
-     @param a   The left side integer
-     @param b   The right side integer
-     @return LTC_MP_LT if a < b,
-             LTC_MP_GT if a > b and
-             LTC_MP_EQ otherwise.  (signed comparison)
-   */
-   int (*compare)(void *a, void *b);
-
-   /** compare against int
-     @param a   The left side integer
-     @param b   The right side integer (upto bits_per_digit)
-     @return LTC_MP_LT if a < b,
-             LTC_MP_GT if a > b and
-             LTC_MP_EQ otherwise.  (signed comparison)
-   */
-   int (*compare_d)(void *a, ltc_mp_digit n);
-
-   /** Count the number of bits used to represent the integer
-     @param a   The integer to count
-     @return The number of bits required to represent the integer
-   */
-   int (*count_bits)(void * a);
-
-   /** Count the number of LSB bits which are zero
-     @param a   The integer to count
-     @return The number of contiguous zero LSB bits
-   */
-   int (*count_lsb_bits)(void *a);
-
-   /** Compute a power of two
-     @param a  The integer to store the power in
-     @param n  The power of two you want to store (a = 2^n)
-     @return CRYPT_OK on success
-   */
-   int (*twoexpt)(void *a , int n);
-
-/* ---- radix conversions ---- */
-
-   /** read ascii string
-     @param a     The integer to store into
-     @param str   The string to read
-     @param radix The radix the integer has been represented in (2-64)
-     @return CRYPT_OK on success
-   */
-   int (*read_radix)(void *a, const char *str, int radix);
-
-   /** write number to string
-     @param a     The integer to store
-     @param str   The destination for the string
-     @param radix The radix the integer is to be represented in (2-64)
-     @return CRYPT_OK on success
-   */
-   int (*write_radix)(void *a, char *str, int radix);
-
-   /** get size as unsigned char string
-     @param a  The integer to get the size (when stored in array of octets)
-     @return   The length of the integer in octets
-   */
-   unsigned long (*unsigned_size)(void *a);
-
-   /** store an integer as an array of octets
-     @param src   The integer to store
-     @param dst   The buffer to store the integer in
-     @return CRYPT_OK on success
-   */
-   int (*unsigned_write)(void *src, unsigned char *dst);
-
-   /** read an array of octets and store as integer
-     @param dst   The integer to load
-     @param src   The array of octets
-     @param len   The number of octets
-     @return CRYPT_OK on success
-   */
-   int (*unsigned_read)(         void *dst,
-                        unsigned char *src,
-                        unsigned long  len);
-
-/* ---- basic math ---- */
-
-   /** add two integers
-     @param a   The first source integer
-     @param b   The second source integer
-     @param c   The destination of "a + b"
-     @return CRYPT_OK on success
-   */
-   int (*add)(void *a, void *b, void *c);
-
-   /** add two integers
-     @param a   The first source integer
-     @param b   The second source integer
-                (single digit of upto bits_per_digit in length)
-     @param c   The destination of "a + b"
-     @return CRYPT_OK on success
-   */
-   int (*addi)(void *a, ltc_mp_digit b, void *c);
-
-   /** subtract two integers
-     @param a   The first source integer
-     @param b   The second source integer
-     @param c   The destination of "a - b"
-     @return CRYPT_OK on success
-   */
-   int (*sub)(void *a, void *b, void *c);
-
-   /** subtract two integers
-     @param a   The first source integer
-     @param b   The second source integer
-                (single digit of upto bits_per_digit in length)
-     @param c   The destination of "a - b"
-     @return CRYPT_OK on success
-   */
-   int (*subi)(void *a, ltc_mp_digit b, void *c);
-
-   /** multiply two integers
-     @param a   The first source integer
-     @param b   The second source integer
-                (single digit of upto bits_per_digit in length)
-     @param c   The destination of "a * b"
-     @return CRYPT_OK on success
-   */
-   int (*mul)(void *a, void *b, void *c);
-
-   /** multiply two integers
-     @param a   The first source integer
-     @param b   The second source integer
-                (single digit of upto bits_per_digit in length)
-     @param c   The destination of "a * b"
-     @return CRYPT_OK on success
-   */
-   int (*muli)(void *a, ltc_mp_digit b, void *c);
-
-   /** Square an integer
-     @param a    The integer to square
-     @param b    The destination
-     @return CRYPT_OK on success
-   */
-   int (*sqr)(void *a, void *b);
-
-   /** Divide an integer
-     @param a    The dividend
-     @param b    The divisor
-     @param c    The quotient (can be NULL to signify don't care)
-     @param d    The remainder (can be NULL to signify don't care)
-     @return CRYPT_OK on success
-   */
-   int (*mpdiv)(void *a, void *b, void *c, void *d);
-
-   /** divide by two
-      @param  a   The integer to divide (shift right)
-      @param  b   The destination
-      @return CRYPT_OK on success
-   */
-   int (*div_2)(void *a, void *b);
-
-   /** Get remainder (small value)
-      @param  a    The integer to reduce
-      @param  b    The modulus (upto bits_per_digit in length)
-      @param  c    The destination for the residue
-      @return CRYPT_OK on success
-   */
-   int (*modi)(void *a, ltc_mp_digit b, ltc_mp_digit *c);
-
-   /** gcd
-      @param  a     The first integer
-      @param  b     The second integer
-      @param  c     The destination for (a, b)
-      @return CRYPT_OK on success
-   */
-   int (*gcd)(void *a, void *b, void *c);
-
-   /** lcm
-      @param  a     The first integer
-      @param  b     The second integer
-      @param  c     The destination for [a, b]
-      @return CRYPT_OK on success
-   */
-   int (*lcm)(void *a, void *b, void *c);
-
-   /** Modular multiplication
-      @param  a     The first source
-      @param  b     The second source
-      @param  c     The modulus
-      @param  d     The destination (a*b mod c)
-      @return CRYPT_OK on success
-   */
-   int (*mulmod)(void *a, void *b, void *c, void *d);
-
-   /** Modular squaring
-      @param  a     The first source
-      @param  b     The modulus
-      @param  c     The destination (a*a mod b)
-      @return CRYPT_OK on success
-   */
-   int (*sqrmod)(void *a, void *b, void *c);
-
-   /** Modular inversion
-      @param  a     The value to invert
-      @param  b     The modulus
-      @param  c     The destination (1/a mod b)
-      @return CRYPT_OK on success
-   */
-   int (*invmod)(void *, void *, void *);
-
-/* ---- reduction ---- */
-
-   /** setup Montgomery
-       @param a  The modulus
-       @param b  The destination for the reduction digit
-       @return CRYPT_OK on success
-   */
-   int (*montgomery_setup)(void *a, void **b);
-
-   /** get normalization value
-       @param a   The destination for the normalization value
-       @param b   The modulus
-       @return  CRYPT_OK on success
-   */
-   int (*montgomery_normalization)(void *a, void *b);
-
-   /** reduce a number
-       @param a   The number [and dest] to reduce
-       @param b   The modulus
-       @param c   The value "b" from montgomery_setup()
-       @return CRYPT_OK on success
-   */
-   int (*montgomery_reduce)(void *a, void *b, void *c);
-
-   /** clean up  (frees memory)
-       @param a   The value "b" from montgomery_setup()
-       @return CRYPT_OK on success
-   */
-   void (*montgomery_deinit)(void *a);
-
-/* ---- exponentiation ---- */
-
-   /** Modular exponentiation
-       @param a    The base integer
-       @param b    The power (can be negative) integer
-       @param c    The modulus integer
-       @param d    The destination
-       @return CRYPT_OK on success
-   */
-   int (*exptmod)(void *a, void *b, void *c, void *d);
-
-   /** Primality testing
-       @param a     The integer to test
-       @param b     The number of Miller-Rabin tests that shall be executed
-       @param c     The destination of the result (FP_YES if prime)
-       @return CRYPT_OK on success
-   */
-   int (*isprime)(void *a, int b, int *c);
-
-/* ----  (optional) ecc point math ---- */
-
-   /** ECC GF(p) point multiplication (from the NIST curves)
-       @param k   The integer to multiply the point by
-       @param G   The point to multiply
-       @param R   The destination for kG
-       @param modulus  The modulus for the field
-       @param map Boolean indicated whether to map back to affine or not
-                  (can be ignored if you work in affine only)
-       @return CRYPT_OK on success
-   */
-   int (*ecc_ptmul)(     void *k,
-                    ecc_point *G,
-                    ecc_point *R,
-                         void *modulus,
-                          int  map);
-
-   /** ECC GF(p) point addition
-       @param P    The first point
-       @param Q    The second point
-       @param R    The destination of P + Q
-       @param modulus  The modulus
-       @param mp   The "b" value from montgomery_setup()
-       @return CRYPT_OK on success
-   */
-   int (*ecc_ptadd)(ecc_point *P,
-                    ecc_point *Q,
-                    ecc_point *R,
-                         void *modulus,
-                         void *mp);
-
-   /** ECC GF(p) point double
-       @param P    The first point
-       @param R    The destination of 2P
-       @param modulus  The modulus
-       @param mp   The "b" value from montgomery_setup()
-       @return CRYPT_OK on success
-   */
-   int (*ecc_ptdbl)(ecc_point *P,
-                    ecc_point *R,
-                         void *modulus,
-                         void *mp);
-
-   /** ECC mapping from projective to affine,
-       currently uses (x,y,z) => (x/z^2, y/z^3, 1)
-       @param P     The point to map
-       @param modulus The modulus
-       @param mp    The "b" value from montgomery_setup()
-       @return CRYPT_OK on success
-       @remark The mapping can be different but keep in mind a
-               ecc_point only has three integers (x,y,z) so if
-               you use a different mapping you have to make it fit.
-   */
-   int (*ecc_map)(ecc_point *P, void *modulus, void *mp);
-
-   /** Computes kA*A + kB*B = C using Shamir's Trick
-       @param A        First point to multiply
-       @param kA       What to multiple A by
-       @param B        Second point to multiply
-       @param kB       What to multiple B by
-       @param C        [out] Destination point (can overlap with A or B)
-       @param modulus  Modulus for curve
-       @return CRYPT_OK on success
-   */
-   int (*ecc_mul2add)(ecc_point *A, void *kA,
-                      ecc_point *B, void *kB,
-                      ecc_point *C,
-                           void *modulus);
-
-/* ---- (optional) rsa optimized math (for internal CRT) ---- */
-
-   /** RSA Key Generation
-       @param prng     An active PRNG state
-       @param wprng    The index of the PRNG desired
-       @param size     The size of the key in octets
-       @param e        The "e" value (public key).
-                       e==65537 is a good choice
-       @param key      [out] Destination of a newly created private key pair
-       @return CRYPT_OK if successful, upon error all allocated ram is freed
-    */
-    int (*rsa_keygen)(prng_state *prng,
-                             int  wprng,
-                             int  size,
-                            long  e,
-                         rsa_key *key);
-
-   /** RSA exponentiation
-      @param in       The octet array representing the base
-      @param inlen    The length of the input
-      @param out      The destination (to be stored in an octet array format)
-      @param outlen   The length of the output buffer and the resulting size
-                      (zero padded to the size of the modulus)
-      @param which    PK_PUBLIC for public RSA and PK_PRIVATE for private RSA
-      @param key      The RSA key to use
-      @return CRYPT_OK on success
-   */
-   int (*rsa_me)(const unsigned char *in,   unsigned long inlen,
-                       unsigned char *out,  unsigned long *outlen, int which,
-                       rsa_key *key);
-
-/* ---- basic math continued ---- */
-
-   /** Modular addition
-      @param  a     The first source
-      @param  b     The second source
-      @param  c     The modulus
-      @param  d     The destination (a + b mod c)
-      @return CRYPT_OK on success
-   */
-   int (*addmod)(void *a, void *b, void *c, void *d);
-
-   /** Modular substraction
-      @param  a     The first source
-      @param  b     The second source
-      @param  c     The modulus
-      @param  d     The destination (a - b mod c)
-      @return CRYPT_OK on success
-   */
-   int (*submod)(void *a, void *b, void *c, void *d);
-
-/* ---- misc stuff ---- */
-
-   /** Make a pseudo-random mpi
-      @param  a     The mpi to make random
-      @param  size  The desired length
-      @return CRYPT_OK on success
-   */
-   int (*rand)(void *a, int size);
-} ltc_math_descriptor;
-
-extern ltc_math_descriptor ltc_mp;
-
-int ltc_init_multi(void **a, ...);
-void ltc_deinit_multi(void *a, ...);
-void ltc_cleanup_multi(void **a, ...);
-
-#ifdef LTM_DESC
-extern const ltc_math_descriptor ltm_desc;
-#endif
-
-#ifdef TFM_DESC
-extern const ltc_math_descriptor tfm_desc;
-#endif
-
-#ifdef GMP_DESC
-extern const ltc_math_descriptor gmp_desc;
-#endif
-
-#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE)
-
-#define MP_DIGIT_BIT                 ltc_mp.bits_per_digit
-
-/* some handy macros */
-#define mp_init(a)                   ltc_mp.init(a)
-#define mp_init_multi                ltc_init_multi
-#define mp_clear(a)                  ltc_mp.deinit(a)
-#define mp_clear_multi               ltc_deinit_multi
-#define mp_cleanup_multi             ltc_cleanup_multi
-#define mp_init_copy(a, b)           ltc_mp.init_copy(a, b)
-
-#define mp_neg(a, b)                 ltc_mp.neg(a, b)
-#define mp_copy(a, b)                ltc_mp.copy(a, b)
-
-#define mp_set(a, b)                 ltc_mp.set_int(a, b)
-#define mp_set_int(a, b)             ltc_mp.set_int(a, b)
-#define mp_get_int(a)                ltc_mp.get_int(a)
-#define mp_get_digit(a, n)           ltc_mp.get_digit(a, n)
-#define mp_get_digit_count(a)        ltc_mp.get_digit_count(a)
-#define mp_cmp(a, b)                 ltc_mp.compare(a, b)
-#define mp_cmp_d(a, b)               ltc_mp.compare_d(a, b)
-#define mp_count_bits(a)             ltc_mp.count_bits(a)
-#define mp_cnt_lsb(a)                ltc_mp.count_lsb_bits(a)
-#define mp_2expt(a, b)               ltc_mp.twoexpt(a, b)
-
-#define mp_read_radix(a, b, c)       ltc_mp.read_radix(a, b, c)
-#define mp_toradix(a, b, c)          ltc_mp.write_radix(a, b, c)
-#define mp_unsigned_bin_size(a)      ltc_mp.unsigned_size(a)
-#define mp_to_unsigned_bin(a, b)     ltc_mp.unsigned_write(a, b)
-#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
-
-#define mp_add(a, b, c)              ltc_mp.add(a, b, c)
-#define mp_add_d(a, b, c)            ltc_mp.addi(a, b, c)
-#define mp_sub(a, b, c)              ltc_mp.sub(a, b, c)
-#define mp_sub_d(a, b, c)            ltc_mp.subi(a, b, c)
-#define mp_mul(a, b, c)              ltc_mp.mul(a, b, c)
-#define mp_mul_d(a, b, c)            ltc_mp.muli(a, b, c)
-#define mp_sqr(a, b)                 ltc_mp.sqr(a, b)
-#define mp_div(a, b, c, d)           ltc_mp.mpdiv(a, b, c, d)
-#define mp_div_2(a, b)               ltc_mp.div_2(a, b)
-#define mp_mod(a, b, c)              ltc_mp.mpdiv(a, b, NULL, c)
-#define mp_mod_d(a, b, c)            ltc_mp.modi(a, b, c)
-#define mp_gcd(a, b, c)              ltc_mp.gcd(a, b, c)
-#define mp_lcm(a, b, c)              ltc_mp.lcm(a, b, c)
-
-#define mp_addmod(a, b, c, d)        ltc_mp.addmod(a, b, c, d)
-#define mp_submod(a, b, c, d)        ltc_mp.submod(a, b, c, d)
-#define mp_mulmod(a, b, c, d)        ltc_mp.mulmod(a, b, c, d)
-#define mp_sqrmod(a, b, c)           ltc_mp.sqrmod(a, b, c)
-#define mp_invmod(a, b, c)           ltc_mp.invmod(a, b, c)
-
-#define mp_montgomery_setup(a, b)    ltc_mp.montgomery_setup(a, b)
-#define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b)
-#define mp_montgomery_reduce(a, b, c)   ltc_mp.montgomery_reduce(a, b, c)
-#define mp_montgomery_free(a)        ltc_mp.montgomery_deinit(a)
-
-#define mp_exptmod(a,b,c,d)          ltc_mp.exptmod(a,b,c,d)
-#define mp_prime_is_prime(a, b, c)   ltc_mp.isprime(a, b, c)
-
-#define mp_iszero(a)                 (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO)
-#define mp_isodd(a)                  (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO)
-#define mp_exch(a, b)                do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while(0)
-
-#define mp_tohex(a, b)               mp_toradix(a, b, 16)
-
-#define mp_rand(a, b)                ltc_mp.rand(a, b)
-
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt_misc.h b/libs/tomcrypt/src/headers/tomcrypt_misc.h
deleted file mode 100644
index e33f67b3a5b0..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_misc.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* ---- LTC_BASE64 Routines ---- */
-#ifdef LTC_BASE64
-int base64_encode(const unsigned char *in,  unsigned long len,
-                        unsigned char *out, unsigned long *outlen);
-
-int base64_decode(const unsigned char *in,  unsigned long len,
-                        unsigned char *out, unsigned long *outlen);
-int base64_strict_decode(const unsigned char *in,  unsigned long len,
-                        unsigned char *out, unsigned long *outlen);
-#endif
-
-#ifdef LTC_BASE64_URL
-int base64url_encode(const unsigned char *in,  unsigned long len,
-                        unsigned char *out, unsigned long *outlen);
-int base64url_strict_encode(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen);
-
-int base64url_decode(const unsigned char *in,  unsigned long len,
-                        unsigned char *out, unsigned long *outlen);
-int base64url_strict_decode(const unsigned char *in,  unsigned long len,
-                        unsigned char *out, unsigned long *outlen);
-#endif
-
-/* ===> LTC_HKDF -- RFC5869 HMAC-based Key Derivation Function <=== */
-#ifdef LTC_HKDF
-
-int hkdf_test(void);
-
-int hkdf_extract(int hash_idx,
-                 const unsigned char *salt, unsigned long saltlen,
-                 const unsigned char *in,   unsigned long inlen,
-                       unsigned char *out,  unsigned long *outlen);
-
-int hkdf_expand(int hash_idx,
-                const unsigned char *info, unsigned long infolen,
-                const unsigned char *in,   unsigned long inlen,
-                      unsigned char *out,  unsigned long outlen);
-
-int hkdf(int hash_idx,
-         const unsigned char *salt, unsigned long saltlen,
-         const unsigned char *info, unsigned long infolen,
-         const unsigned char *in,   unsigned long inlen,
-               unsigned char *out,  unsigned long outlen);
-
-#endif  /* LTC_HKDF */
-
-/* ---- MEM routines ---- */
-int mem_neq(const void *a, const void *b, size_t len);
-void zeromem(volatile void *dst, size_t len);
-void burn_stack(unsigned long len);
-
-const char *error_to_string(int err);
-
-extern const char *crypt_build_settings;
-
-/* ---- HMM ---- */
-int crypt_fsa(void *mp, ...);
-
-/* ---- Dynamic language support ---- */
-int crypt_get_constant(const char* namein, int *valueout);
-int crypt_list_all_constants(char *names_list, unsigned int *names_list_size);
-
-int crypt_get_size(const char* namein, unsigned int *sizeout);
-int crypt_list_all_sizes(char *names_list, unsigned int *names_list_size);
-
-#ifdef LTM_DESC
-void init_LTM(void);
-#endif
-#ifdef TFM_DESC
-void init_TFM(void);
-#endif
-#ifdef GMP_DESC
-void init_GMP(void);
-#endif
-
-#ifdef LTC_ADLER32
-typedef struct adler32_state_s
-{
-   unsigned short s[2];
-} adler32_state;
-
-void adler32_init(adler32_state *ctx);
-void adler32_update(adler32_state *ctx, const unsigned char *input, unsigned long length);
-void adler32_finish(adler32_state *ctx, void *hash, unsigned long size);
-int adler32_test(void);
-#endif
-
-#ifdef LTC_CRC32
-typedef struct crc32_state_s
-{
-   ulong32 crc;
-} crc32_state;
-
-void crc32_init(crc32_state *ctx);
-void crc32_update(crc32_state *ctx, const unsigned char *input, unsigned long length);
-void crc32_finish(crc32_state *ctx, void *hash, unsigned long size);
-int crc32_test(void);
-#endif
-
-int compare_testvector(const void* is, const unsigned long is_len, const void* should, const unsigned long should_len, const char* what, int which);
diff --git a/libs/tomcrypt/src/headers/tomcrypt_pk.h b/libs/tomcrypt/src/headers/tomcrypt_pk.h
deleted file mode 100644
index 9375bd22a6e3..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_pk.h
+++ /dev/null
@@ -1,743 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* ---- NUMBER THEORY ---- */
-
-enum {
-   PK_PUBLIC=0,
-   PK_PRIVATE=1
-};
-
-/* Indicates standard output formats that can be read e.g. by OpenSSL or GnuTLS */
-#define PK_STD          0x1000
-
-int rand_prime(void *N, long len, prng_state *prng, int wprng);
-
-#ifdef LTC_SOURCE
-/* internal helper functions */
-int rand_bn_bits(void *N, int bits, prng_state *prng, int wprng);
-int rand_bn_upto(void *N, void *limit, prng_state *prng, int wprng);
-
-enum public_key_algorithms {
-   PKA_RSA,
-   PKA_DSA
-};
-
-typedef struct Oid {
-    unsigned long OID[16];
-    /** Number of OID digits in use */
-    unsigned long OIDlen;
-} oid_st;
-
-int pk_get_oid(int pk, oid_st *st);
-#endif /* LTC_SOURCE */
-
-/* ---- RSA ---- */
-#ifdef LTC_MRSA
-
-/** RSA PKCS style key */
-typedef struct Rsa_key {
-    /** Type of key, PK_PRIVATE or PK_PUBLIC */
-    int type;
-    /** The public exponent */
-    void *e;
-    /** The private exponent */
-    void *d;
-    /** The modulus */
-    void *N;
-    /** The p factor of N */
-    void *p;
-    /** The q factor of N */
-    void *q;
-    /** The 1/q mod p CRT param */
-    void *qP;
-    /** The d mod (p - 1) CRT param */
-    void *dP;
-    /** The d mod (q - 1) CRT param */
-    void *dQ;
-} rsa_key;
-
-int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key);
-
-int rsa_get_size(rsa_key *key);
-
-int rsa_exptmod(const unsigned char *in,   unsigned long inlen,
-                      unsigned char *out,  unsigned long *outlen, int which,
-                      rsa_key *key);
-
-void rsa_free(rsa_key *key);
-
-/* These use PKCS #1 v2.0 padding */
-#define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \
-  rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_PKCS_1_OAEP, _key)
-
-#define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \
-  rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_PKCS_1_OAEP, _stat, _key)
-
-#define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \
-  rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key)
-
-#define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \
-  rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key)
-
-#define rsa_sign_saltlen_get_max(_hash_idx, _key) \
-  rsa_sign_saltlen_get_max_ex(LTC_PKCS_1_PSS, _hash_idx, _key)
-
-/* These can be switched between PKCS #1 v2.x and PKCS #1 v1.5 paddings */
-int rsa_encrypt_key_ex(const unsigned char *in,     unsigned long inlen,
-                             unsigned char *out,    unsigned long *outlen,
-                       const unsigned char *lparam, unsigned long lparamlen,
-                       prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key);
-
-int rsa_decrypt_key_ex(const unsigned char *in,       unsigned long  inlen,
-                             unsigned char *out,      unsigned long *outlen,
-                       const unsigned char *lparam,   unsigned long  lparamlen,
-                             int            hash_idx, int            padding,
-                             int           *stat,     rsa_key       *key);
-
-int rsa_sign_hash_ex(const unsigned char *in,       unsigned long  inlen,
-                           unsigned char *out,      unsigned long *outlen,
-                           int            padding,
-                           prng_state    *prng,     int            prng_idx,
-                           int            hash_idx, unsigned long  saltlen,
-                           rsa_key *key);
-
-int rsa_verify_hash_ex(const unsigned char *sig,      unsigned long siglen,
-                       const unsigned char *hash,     unsigned long hashlen,
-                             int            padding,
-                             int            hash_idx, unsigned long saltlen,
-                             int           *stat,     rsa_key      *key);
-
-int rsa_sign_saltlen_get_max_ex(int padding, int hash_idx, rsa_key *key);
-
-/* PKCS #1 import/export */
-int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key);
-int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key);
-
-int rsa_import_x509(const unsigned char *in, unsigned long inlen, rsa_key *key);
-int rsa_import_pkcs8(const unsigned char *in, unsigned long inlen,
-                     const void *passwd, unsigned long passwdlen, rsa_key *key);
-
-int rsa_set_key(const unsigned char *N,  unsigned long Nlen,
-                const unsigned char *e,  unsigned long elen,
-                const unsigned char *d,  unsigned long dlen,
-                rsa_key *key);
-int rsa_set_factors(const unsigned char *p,  unsigned long plen,
-                    const unsigned char *q,  unsigned long qlen,
-                    rsa_key *key);
-int rsa_set_crt_params(const unsigned char *dP, unsigned long dPlen,
-                       const unsigned char *dQ, unsigned long dQlen,
-                       const unsigned char *qP, unsigned long qPlen,
-                       rsa_key *key);
-#endif
-
-/* ---- Katja ---- */
-#ifdef LTC_MKAT
-
-/* Min and Max KAT key sizes (in bits) */
-#define MIN_KAT_SIZE 1024
-#define MAX_KAT_SIZE 4096
-
-/** Katja PKCS style key */
-typedef struct KAT_key {
-    /** Type of key, PK_PRIVATE or PK_PUBLIC */
-    int type;
-    /** The private exponent */
-    void *d;
-    /** The modulus */
-    void *N;
-    /** The p factor of N */
-    void *p;
-    /** The q factor of N */
-    void *q;
-    /** The 1/q mod p CRT param */
-    void *qP;
-    /** The d mod (p - 1) CRT param */
-    void *dP;
-    /** The d mod (q - 1) CRT param */
-    void *dQ;
-    /** The pq param */
-    void *pq;
-} katja_key;
-
-int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key);
-
-int katja_exptmod(const unsigned char *in,   unsigned long inlen,
-                        unsigned char *out,  unsigned long *outlen, int which,
-                        katja_key *key);
-
-void katja_free(katja_key *key);
-
-/* These use PKCS #1 v2.0 padding */
-int katja_encrypt_key(const unsigned char *in,     unsigned long inlen,
-                            unsigned char *out,    unsigned long *outlen,
-                      const unsigned char *lparam, unsigned long lparamlen,
-                      prng_state *prng, int prng_idx, int hash_idx, katja_key *key);
-
-int katja_decrypt_key(const unsigned char *in,       unsigned long inlen,
-                            unsigned char *out,      unsigned long *outlen,
-                      const unsigned char *lparam,   unsigned long lparamlen,
-                            int            hash_idx, int *stat,
-                            katja_key       *key);
-
-/* PKCS #1 import/export */
-int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key);
-int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key);
-
-#endif
-
-/* ---- DH Routines ---- */
-#ifdef LTC_MDH
-
-typedef struct {
-    int type;
-    void *x;
-    void *y;
-    void *base;
-    void *prime;
-} dh_key;
-
-int dh_get_groupsize(dh_key *key);
-
-int dh_export(unsigned char *out, unsigned long *outlen, int type, dh_key *key);
-int dh_import(const unsigned char *in, unsigned long inlen, dh_key *key);
-
-int dh_set_pg(const unsigned char *p, unsigned long plen,
-              const unsigned char *g, unsigned long glen,
-              dh_key *key);
-int dh_set_pg_dhparam(const unsigned char *dhparam, unsigned long dhparamlen, dh_key *key);
-int dh_set_pg_groupsize(int groupsize, dh_key *key);
-
-int dh_set_key(const unsigned char *in, unsigned long inlen, int type, dh_key *key);
-int dh_generate_key(prng_state *prng, int wprng, dh_key *key);
-
-int dh_shared_secret(dh_key        *private_key, dh_key        *public_key,
-                     unsigned char *out,         unsigned long *outlen);
-
-void dh_free(dh_key *key);
-
-int dh_export_key(void *out, unsigned long *outlen, int type, dh_key *key);
-
-#ifdef LTC_SOURCE
-typedef struct {
-  int size;
-  const char *name, *base, *prime;
-} ltc_dh_set_type;
-
-extern const ltc_dh_set_type ltc_dh_sets[];
-
-/* internal helper functions */
-int dh_check_pubkey(dh_key *key);
-#endif
-
-#endif /* LTC_MDH */
-
-
-/* ---- ECC Routines ---- */
-#ifdef LTC_MECC
-
-/* size of our temp buffers for exported keys */
-#define ECC_BUF_SIZE 256
-
-/* max private key size */
-#define ECC_MAXSIZE  66
-
-/** Structure defines a NIST GF(p) curve */
-typedef struct {
-   /** The size of the curve in octets */
-   int size;
-
-   /** name of curve */
-   const char *name;
-
-   /** The prime that defines the field the curve is in (encoded in hex) */
-   const char *prime;
-
-   /** The fields B param (hex) */
-   const char *B;
-
-   /** The order of the curve (hex) */
-   const char *order;
-
-   /** The x co-ordinate of the base point on the curve (hex) */
-   const char *Gx;
-
-   /** The y co-ordinate of the base point on the curve (hex) */
-   const char *Gy;
-} ltc_ecc_set_type;
-
-/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */
-typedef struct {
-    /** The x co-ordinate */
-    void *x;
-
-    /** The y co-ordinate */
-    void *y;
-
-    /** The z co-ordinate */
-    void *z;
-} ecc_point;
-
-/** An ECC key */
-typedef struct {
-    /** Type of key, PK_PRIVATE or PK_PUBLIC */
-    int type;
-
-    /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */
-    int idx;
-
-    /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */
-    const ltc_ecc_set_type *dp;
-
-    /** The public key */
-    ecc_point pubkey;
-
-    /** The private key */
-    void *k;
-} ecc_key;
-
-/** the ECC params provided */
-extern const ltc_ecc_set_type ltc_ecc_sets[];
-
-int  ecc_test(void);
-void ecc_sizes(int *low, int *high);
-int  ecc_get_size(ecc_key *key);
-
-int  ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key);
-int  ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp);
-void ecc_free(ecc_key *key);
-
-int  ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key);
-int  ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key);
-int  ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp);
-
-int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen);
-int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key);
-int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp);
-
-int  ecc_shared_secret(ecc_key *private_key, ecc_key *public_key,
-                       unsigned char *out, unsigned long *outlen);
-
-int  ecc_encrypt_key(const unsigned char *in,   unsigned long inlen,
-                           unsigned char *out,  unsigned long *outlen,
-                           prng_state *prng, int wprng, int hash,
-                           ecc_key *key);
-
-int  ecc_decrypt_key(const unsigned char *in,  unsigned long  inlen,
-                           unsigned char *out, unsigned long *outlen,
-                           ecc_key *key);
-
-int ecc_sign_hash_rfc7518(const unsigned char *in,  unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen,
-                                prng_state *prng, int wprng, ecc_key *key);
-
-int  ecc_sign_hash(const unsigned char *in,  unsigned long inlen,
-                         unsigned char *out, unsigned long *outlen,
-                         prng_state *prng, int wprng, ecc_key *key);
-
-int ecc_verify_hash_rfc7518(const unsigned char *sig,  unsigned long siglen,
-                            const unsigned char *hash, unsigned long hashlen,
-                            int *stat, ecc_key *key);
-
-int  ecc_verify_hash(const unsigned char *sig,  unsigned long siglen,
-                     const unsigned char *hash, unsigned long hashlen,
-                     int *stat, ecc_key *key);
-
-/* low level functions */
-ecc_point *ltc_ecc_new_point(void);
-void       ltc_ecc_del_point(ecc_point *p);
-int        ltc_ecc_is_valid_idx(int n);
-
-/* point ops (mp == montgomery digit) */
-#if !defined(LTC_MECC_ACCEL) || defined(LTM_DESC) || defined(GMP_DESC)
-/* R = 2P */
-int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp);
-
-/* R = P + Q */
-int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp);
-#endif
-
-#if defined(LTC_MECC_FP)
-/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */
-int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
-
-/* functions for saving/loading/freeing/adding to fixed point cache */
-int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen);
-int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen);
-void ltc_ecc_fp_free(void);
-int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock);
-
-/* lock/unlock all points currently in fixed point cache */
-void ltc_ecc_fp_tablelock(int lock);
-#endif
-
-/* R = kG */
-int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
-
-#ifdef LTC_ECC_SHAMIR
-/* kA*A + kB*B = C */
-int ltc_ecc_mul2add(ecc_point *A, void *kA,
-                    ecc_point *B, void *kB,
-                    ecc_point *C,
-                         void *modulus);
-
-#ifdef LTC_MECC_FP
-/* Shamir's trick with optimized point multiplication using fixed point cache */
-int ltc_ecc_fp_mul2add(ecc_point *A, void *kA,
-                       ecc_point *B, void *kB,
-                       ecc_point *C, void *modulus);
-#endif
-
-#endif
-
-
-/* map P to affine from projective */
-int ltc_ecc_map(ecc_point *P, void *modulus, void *mp);
-
-#endif
-
-#ifdef LTC_MDSA
-
-/* Max diff between group and modulus size in bytes */
-#define LTC_MDSA_DELTA     512
-
-/* Max DSA group size in bytes (default allows 4k-bit groups) */
-#define LTC_MDSA_MAX_GROUP 512
-
-/** DSA key structure */
-typedef struct {
-   /** The key type, PK_PRIVATE or PK_PUBLIC */
-   int type;
-
-   /** The order of the sub-group used in octets */
-   int qord;
-
-   /** The generator  */
-   void *g;
-
-   /** The prime used to generate the sub-group */
-   void *q;
-
-   /** The large prime that generats the field the contains the sub-group */
-   void *p;
-
-   /** The private key */
-   void *x;
-
-   /** The public key */
-   void *y;
-} dsa_key;
-
-int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key);
-
-int dsa_set_pqg(const unsigned char *p,  unsigned long plen,
-                const unsigned char *q,  unsigned long qlen,
-                const unsigned char *g,  unsigned long glen,
-                dsa_key *key);
-int dsa_set_pqg_dsaparam(const unsigned char *dsaparam, unsigned long dsaparamlen, dsa_key *key);
-int dsa_generate_pqg(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key);
-
-int dsa_set_key(const unsigned char *in, unsigned long inlen, int type, dsa_key *key);
-int dsa_generate_key(prng_state *prng, int wprng, dsa_key *key);
-
-void dsa_free(dsa_key *key);
-
-int dsa_sign_hash_raw(const unsigned char *in,  unsigned long inlen,
-                                   void *r,   void *s,
-                               prng_state *prng, int wprng, dsa_key *key);
-
-int dsa_sign_hash(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen,
-                        prng_state *prng, int wprng, dsa_key *key);
-
-int dsa_verify_hash_raw(         void *r,          void *s,
-                    const unsigned char *hash, unsigned long hashlen,
-                                    int *stat,      dsa_key *key);
-
-int dsa_verify_hash(const unsigned char *sig,  unsigned long siglen,
-                    const unsigned char *hash, unsigned long hashlen,
-                          int           *stat, dsa_key       *key);
-
-int dsa_encrypt_key(const unsigned char *in,   unsigned long inlen,
-                          unsigned char *out,  unsigned long *outlen,
-                          prng_state *prng, int wprng, int hash,
-                          dsa_key *key);
-
-int dsa_decrypt_key(const unsigned char *in,  unsigned long  inlen,
-                          unsigned char *out, unsigned long *outlen,
-                          dsa_key *key);
-
-int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key);
-int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key);
-int dsa_verify_key(dsa_key *key, int *stat);
-#ifdef LTC_SOURCE
-/* internal helper functions */
-int dsa_int_validate_xy(dsa_key *key, int *stat);
-int dsa_int_validate_pqg(dsa_key *key, int *stat);
-int dsa_int_validate_primes(dsa_key *key, int *stat);
-#endif
-int dsa_shared_secret(void          *private_key, void *base,
-                      dsa_key       *public_key,
-                      unsigned char *out,         unsigned long *outlen);
-#endif
-
-#ifdef LTC_DER
-/* DER handling */
-
-typedef enum ltc_asn1_type_ {
- /*  0 */
- LTC_ASN1_EOL,
- LTC_ASN1_BOOLEAN,
- LTC_ASN1_INTEGER,
- LTC_ASN1_SHORT_INTEGER,
- LTC_ASN1_BIT_STRING,
- /*  5 */
- LTC_ASN1_OCTET_STRING,
- LTC_ASN1_NULL,
- LTC_ASN1_OBJECT_IDENTIFIER,
- LTC_ASN1_IA5_STRING,
- LTC_ASN1_PRINTABLE_STRING,
- /* 10 */
- LTC_ASN1_UTF8_STRING,
- LTC_ASN1_UTCTIME,
- LTC_ASN1_CHOICE,
- LTC_ASN1_SEQUENCE,
- LTC_ASN1_SET,
- /* 15 */
- LTC_ASN1_SETOF,
- LTC_ASN1_RAW_BIT_STRING,
- LTC_ASN1_TELETEX_STRING,
- LTC_ASN1_CONSTRUCTED,
- LTC_ASN1_CONTEXT_SPECIFIC,
- /* 20 */
- LTC_ASN1_GENERALIZEDTIME,
-} ltc_asn1_type;
-
-/** A LTC ASN.1 list type */
-typedef struct ltc_asn1_list_ {
-   /** The LTC ASN.1 enumerated type identifier */
-   ltc_asn1_type type;
-   /** The data to encode or place for decoding */
-   void         *data;
-   /** The size of the input or resulting output */
-   unsigned long size;
-   /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */
-   int           used;
-   /** prev/next entry in the list */
-   struct ltc_asn1_list_ *prev, *next, *child, *parent;
-} ltc_asn1_list;
-
-#define LTC_SET_ASN1(list, index, Type, Data, Size)  \
-   do {                                              \
-      int LTC_MACRO_temp            = (index);       \
-      ltc_asn1_list *LTC_MACRO_list = (list);        \
-      LTC_MACRO_list[LTC_MACRO_temp].type = (Type);  \
-      LTC_MACRO_list[LTC_MACRO_temp].data = (void*)(Data);  \
-      LTC_MACRO_list[LTC_MACRO_temp].size = (Size);  \
-      LTC_MACRO_list[LTC_MACRO_temp].used = 0;       \
-   } while (0)
-
-/* SEQUENCE */
-int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
-                           unsigned char *out,  unsigned long *outlen, int type_of);
-
-#define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE)
-
-int der_decode_sequence_ex(const unsigned char *in, unsigned long  inlen,
-                           ltc_asn1_list *list,     unsigned long  outlen, int ordered);
-
-#define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1)
-
-int der_length_sequence(ltc_asn1_list *list, unsigned long inlen,
-                        unsigned long *outlen);
-
-
-#ifdef LTC_SOURCE
-/* internal helper functions */
-int der_length_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
-                           unsigned long *outlen, unsigned long *payloadlen);
-/* SUBJECT PUBLIC KEY INFO */
-int der_encode_subject_public_key_info(unsigned char *out, unsigned long *outlen,
-        unsigned int algorithm, void* public_key, unsigned long public_key_len,
-        unsigned long parameters_type, void* parameters, unsigned long parameters_len);
-
-int der_decode_subject_public_key_info(const unsigned char *in, unsigned long inlen,
-        unsigned int algorithm, void* public_key, unsigned long* public_key_len,
-        unsigned long parameters_type, ltc_asn1_list* parameters, unsigned long parameters_len);
-#endif /* LTC_SOURCE */
-
-/* SET */
-#define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0)
-#define der_length_set der_length_sequence
-int der_encode_set(ltc_asn1_list *list, unsigned long inlen,
-                   unsigned char *out,  unsigned long *outlen);
-
-int der_encode_setof(ltc_asn1_list *list, unsigned long inlen,
-                     unsigned char *out,  unsigned long *outlen);
-
-/* VA list handy helpers with triplets of <type, size, data> */
-int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...);
-int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...);
-
-/* FLEXI DECODER handle unknown list decoder */
-int  der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out);
-#define der_free_sequence_flexi         der_sequence_free
-void der_sequence_free(ltc_asn1_list *in);
-void der_sequence_shrink(ltc_asn1_list *in);
-
-/* BOOLEAN */
-int der_length_boolean(unsigned long *outlen);
-int der_encode_boolean(int in,
-                       unsigned char *out, unsigned long *outlen);
-int der_decode_boolean(const unsigned char *in, unsigned long inlen,
-                                       int *out);
-/* INTEGER */
-int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen);
-int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num);
-int der_length_integer(void *num, unsigned long *len);
-
-/* INTEGER -- handy for 0..2^32-1 values */
-int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num);
-int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen);
-int der_length_short_integer(unsigned long num, unsigned long *outlen);
-
-/* BIT STRING */
-int der_encode_bit_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_decode_bit_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_encode_raw_bit_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_decode_raw_bit_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_length_bit_string(unsigned long nbits, unsigned long *outlen);
-
-/* OCTET STRING */
-int der_encode_octet_string(const unsigned char *in, unsigned long inlen,
-                                  unsigned char *out, unsigned long *outlen);
-int der_decode_octet_string(const unsigned char *in, unsigned long inlen,
-                                  unsigned char *out, unsigned long *outlen);
-int der_length_octet_string(unsigned long noctets, unsigned long *outlen);
-
-/* OBJECT IDENTIFIER */
-int der_encode_object_identifier(unsigned long *words, unsigned long  nwords,
-                                 unsigned char *out,   unsigned long *outlen);
-int der_decode_object_identifier(const unsigned char *in,    unsigned long  inlen,
-                                       unsigned long *words, unsigned long *outlen);
-int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen);
-unsigned long der_object_identifier_bits(unsigned long x);
-
-/* IA5 STRING */
-int der_encode_ia5_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
-
-int der_ia5_char_encode(int c);
-int der_ia5_value_decode(int v);
-
-/* TELETEX STRING */
-int der_decode_teletex_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_length_teletex_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
-
-#ifdef LTC_SOURCE
-/* internal helper functions */
-int der_teletex_char_encode(int c);
-int der_teletex_value_decode(int v);
-#endif /* LTC_SOURCE */
-
-
-/* PRINTABLE STRING */
-int der_encode_printable_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_decode_printable_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen);
-int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
-
-int der_printable_char_encode(int c);
-int der_printable_value_decode(int v);
-
-/* UTF-8 */
-#if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(__WCHAR_MAX__) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR)
-   #if defined(__WCHAR_MAX__)
-      #define LTC_WCHAR_MAX __WCHAR_MAX__
-   #else
-      #include <wchar.h>
-      #define LTC_WCHAR_MAX WCHAR_MAX
-   #endif
-/* please note that it might happen that LTC_WCHAR_MAX is undefined */
-#else
-   typedef ulong32 wchar_t;
-   #define LTC_WCHAR_MAX 0xFFFFFFFF
-#endif
-
-int der_encode_utf8_string(const wchar_t *in,  unsigned long inlen,
-                           unsigned char *out, unsigned long *outlen);
-
-int der_decode_utf8_string(const unsigned char *in,  unsigned long inlen,
-                                       wchar_t *out, unsigned long *outlen);
-unsigned long der_utf8_charsize(const wchar_t c);
-#ifdef LTC_SOURCE
-/* internal helper functions */
-int der_utf8_valid_char(const wchar_t c);
-#endif /* LTC_SOURCE */
-int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen);
-
-
-/* CHOICE */
-int der_decode_choice(const unsigned char *in,   unsigned long *inlen,
-                            ltc_asn1_list *list, unsigned long  outlen);
-
-/* UTCTime */
-typedef struct {
-   unsigned YY, /* year */
-            MM, /* month */
-            DD, /* day */
-            hh, /* hour */
-            mm, /* minute */
-            ss, /* second */
-            off_dir, /* timezone offset direction 0 == +, 1 == - */
-            off_hh, /* timezone offset hours */
-            off_mm; /* timezone offset minutes */
-} ltc_utctime;
-
-int der_encode_utctime(ltc_utctime *utctime,
-                       unsigned char *out,   unsigned long *outlen);
-
-int der_decode_utctime(const unsigned char *in, unsigned long *inlen,
-                             ltc_utctime   *out);
-
-int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen);
-
-/* GeneralizedTime */
-typedef struct {
-   unsigned YYYY, /* year */
-            MM, /* month */
-            DD, /* day */
-            hh, /* hour */
-            mm, /* minute */
-            ss, /* second */
-            fs, /* fractional seconds */
-            off_dir, /* timezone offset direction 0 == +, 1 == - */
-            off_hh, /* timezone offset hours */
-            off_mm; /* timezone offset minutes */
-} ltc_generalizedtime;
-
-int der_encode_generalizedtime(ltc_generalizedtime *gtime,
-                               unsigned char       *out, unsigned long *outlen);
-
-int der_decode_generalizedtime(const unsigned char *in, unsigned long *inlen,
-                               ltc_generalizedtime *out);
-
-int der_length_generalizedtime(ltc_generalizedtime *gtime, unsigned long *outlen);
-
-
-#endif
diff --git a/libs/tomcrypt/src/headers/tomcrypt_pkcs.h b/libs/tomcrypt/src/headers/tomcrypt_pkcs.h
deleted file mode 100644
index 6ac324bd09ff..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_pkcs.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* PKCS Header Info */
-
-/* ===> PKCS #1 -- RSA Cryptography <=== */
-#ifdef LTC_PKCS_1
-
-enum ltc_pkcs_1_v1_5_blocks
-{
-  LTC_PKCS_1_EMSA   = 1,        /* Block type 1 (PKCS #1 v1.5 signature padding) */
-  LTC_PKCS_1_EME    = 2         /* Block type 2 (PKCS #1 v1.5 encryption padding) */
-};
-
-enum ltc_pkcs_1_paddings
-{
-  LTC_PKCS_1_V1_5     = 1,        /* PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */
-  LTC_PKCS_1_OAEP     = 2,        /* PKCS #1 v2.0 encryption padding */
-  LTC_PKCS_1_PSS      = 3,        /* PKCS #1 v2.1 signature padding */
-  LTC_PKCS_1_V1_5_NA1 = 4         /* PKCS #1 v1.5 padding - No ASN.1 (\sa ltc_pkcs_1_v1_5_blocks) */
-};
-
-int pkcs_1_mgf1(      int            hash_idx,
-                const unsigned char *seed, unsigned long seedlen,
-                      unsigned char *mask, unsigned long masklen);
-
-int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out);
-int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen);
-
-/* *** v1.5 padding */
-int pkcs_1_v1_5_encode(const unsigned char *msg,
-                             unsigned long  msglen,
-                             int            block_type,
-                             unsigned long  modulus_bitlen,
-                                prng_state *prng,
-                                       int  prng_idx,
-                             unsigned char *out,
-                             unsigned long *outlen);
-
-int pkcs_1_v1_5_decode(const unsigned char *msg,
-                             unsigned long  msglen,
-                                       int  block_type,
-                             unsigned long  modulus_bitlen,
-                             unsigned char *out,
-                             unsigned long *outlen,
-                                       int *is_valid);
-
-/* *** v2.1 padding */
-int pkcs_1_oaep_encode(const unsigned char *msg,    unsigned long msglen,
-                       const unsigned char *lparam, unsigned long lparamlen,
-                             unsigned long modulus_bitlen, prng_state *prng,
-                             int           prng_idx,         int  hash_idx,
-                             unsigned char *out,    unsigned long *outlen);
-
-int pkcs_1_oaep_decode(const unsigned char *msg,    unsigned long msglen,
-                       const unsigned char *lparam, unsigned long lparamlen,
-                             unsigned long modulus_bitlen, int hash_idx,
-                             unsigned char *out,    unsigned long *outlen,
-                             int           *res);
-
-int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen,
-                            unsigned long saltlen,  prng_state   *prng,
-                            int           prng_idx, int           hash_idx,
-                            unsigned long modulus_bitlen,
-                            unsigned char *out,     unsigned long *outlen);
-
-int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen,
-                      const unsigned char *sig,     unsigned long siglen,
-                            unsigned long saltlen,  int           hash_idx,
-                            unsigned long modulus_bitlen, int    *res);
-
-#endif /* LTC_PKCS_1 */
-
-/* ===> PKCS #5 -- Password Based Cryptography <=== */
-#ifdef LTC_PKCS_5
-
-/* Algorithm #1 (PBKDF1) */
-int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
-                const unsigned char *salt,
-                int iteration_count,  int hash_idx,
-                unsigned char *out,   unsigned long *outlen);
-
-/* Algorithm #1 (PBKDF1) - OpenSSL-compatible variant for arbitrarily-long keys.
-   Compatible with EVP_BytesToKey() */
-int pkcs_5_alg1_openssl(const unsigned char *password,
-                        unsigned long password_len,
-                        const unsigned char *salt,
-                        int iteration_count,  int hash_idx,
-                        unsigned char *out,   unsigned long *outlen);
-
-/* Algorithm #2 (PBKDF2) */
-int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,
-                const unsigned char *salt,     unsigned long salt_len,
-                int iteration_count,           int hash_idx,
-                unsigned char *out,            unsigned long *outlen);
-
-int pkcs_5_test (void);
-#endif  /* LTC_PKCS_5 */
diff --git a/libs/tomcrypt/src/headers/tomcrypt_prng.h b/libs/tomcrypt/src/headers/tomcrypt_prng.h
deleted file mode 100644
index 1a79d6d512fd..000000000000
--- a/libs/tomcrypt/src/headers/tomcrypt_prng.h
+++ /dev/null
@@ -1,227 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* ---- PRNG Stuff ---- */
-#ifdef LTC_YARROW
-struct yarrow_prng {
-    int                   cipher, hash;
-    unsigned char         pool[MAXBLOCKSIZE];
-    symmetric_CTR         ctr;
-};
-#endif
-
-#ifdef LTC_RC4
-struct rc4_prng {
-    rc4_state s;
-};
-#endif
-
-#ifdef LTC_CHACHA20_PRNG
-struct chacha20_prng {
-    chacha_state s;        /* chacha state */
-    unsigned char ent[40]; /* entropy buffer */
-    unsigned long idx;     /* entropy counter */
-};
-#endif
-
-#ifdef LTC_FORTUNA
-struct fortuna_prng {
-    hash_state pool[LTC_FORTUNA_POOLS];     /* the  pools */
-
-    symmetric_key skey;
-
-    unsigned char K[32],      /* the current key */
-                  IV[16];     /* IV for CTR mode */
-
-    unsigned long pool_idx,   /* current pool we will add to */
-                  pool0_len,  /* length of 0'th pool */
-                  wd;
-
-    ulong64       reset_cnt;  /* number of times we have reset */
-};
-#endif
-
-#ifdef LTC_SOBER128
-struct sober128_prng {
-    sober128_state s;      /* sober128 state */
-    unsigned char ent[40]; /* entropy buffer */
-    unsigned long idx;     /* entropy counter */
-};
-#endif
-
-typedef struct {
-   union {
-      char dummy[1];
-#ifdef LTC_YARROW
-      struct yarrow_prng    yarrow;
-#endif
-#ifdef LTC_RC4
-      struct rc4_prng       rc4;
-#endif
-#ifdef LTC_CHACHA20_PRNG
-      struct chacha20_prng  chacha;
-#endif
-#ifdef LTC_FORTUNA
-      struct fortuna_prng   fortuna;
-#endif
-#ifdef LTC_SOBER128
-      struct sober128_prng  sober128;
-#endif
-   };
-   short ready;            /* ready flag 0-1 */
-   LTC_MUTEX_TYPE(lock)    /* lock */
-} prng_state;
-
-/** PRNG descriptor */
-extern struct ltc_prng_descriptor {
-    /** Name of the PRNG */
-    const char *name;
-    /** size in bytes of exported state */
-    int  export_size;
-    /** Start a PRNG state
-        @param prng   [out] The state to initialize
-        @return CRYPT_OK if successful
-    */
-    int (*start)(prng_state *prng);
-    /** Add entropy to the PRNG
-        @param in         The entropy
-        @param inlen      Length of the entropy (octets)\
-        @param prng       The PRNG state
-        @return CRYPT_OK if successful
-    */
-    int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng);
-    /** Ready a PRNG state to read from
-        @param prng       The PRNG state to ready
-        @return CRYPT_OK if successful
-    */
-    int (*ready)(prng_state *prng);
-    /** Read from the PRNG
-        @param out     [out] Where to store the data
-        @param outlen  Length of data desired (octets)
-        @param prng    The PRNG state to read from
-        @return Number of octets read
-    */
-    unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng);
-    /** Terminate a PRNG state
-        @param prng   The PRNG state to terminate
-        @return CRYPT_OK if successful
-    */
-    int (*done)(prng_state *prng);
-    /** Export a PRNG state
-        @param out     [out] The destination for the state
-        @param outlen  [in/out] The max size and resulting size of the PRNG state
-        @param prng    The PRNG to export
-        @return CRYPT_OK if successful
-    */
-    int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng);
-    /** Import a PRNG state
-        @param in      The data to import
-        @param inlen   The length of the data to import (octets)
-        @param prng    The PRNG to initialize/import
-        @return CRYPT_OK if successful
-    */
-    int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng);
-    /** Self-test the PRNG
-        @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
-    */
-    int (*test)(void);
-} prng_descriptor[];
-
-#ifdef LTC_YARROW
-int yarrow_start(prng_state *prng);
-int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int yarrow_ready(prng_state *prng);
-unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int yarrow_done(prng_state *prng);
-int  yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int  yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int  yarrow_test(void);
-extern const struct ltc_prng_descriptor yarrow_desc;
-#endif
-
-#ifdef LTC_FORTUNA
-int fortuna_start(prng_state *prng);
-int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int fortuna_ready(prng_state *prng);
-unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int fortuna_done(prng_state *prng);
-int  fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int  fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int  fortuna_test(void);
-extern const struct ltc_prng_descriptor fortuna_desc;
-#endif
-
-#ifdef LTC_RC4
-int rc4_start(prng_state *prng);
-int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int rc4_ready(prng_state *prng);
-unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int  rc4_done(prng_state *prng);
-int  rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int  rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int  rc4_test(void);
-extern const struct ltc_prng_descriptor rc4_desc;
-#endif
-
-#ifdef LTC_CHACHA20_PRNG
-int chacha20_prng_start(prng_state *prng);
-int chacha20_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int chacha20_prng_ready(prng_state *prng);
-unsigned long chacha20_prng_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int  chacha20_prng_done(prng_state *prng);
-int  chacha20_prng_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int  chacha20_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int  chacha20_prng_test(void);
-extern const struct ltc_prng_descriptor chacha20_prng_desc;
-#endif
-
-#ifdef LTC_SPRNG
-int sprng_start(prng_state *prng);
-int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int sprng_ready(prng_state *prng);
-unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int sprng_done(prng_state *prng);
-int  sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int  sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int  sprng_test(void);
-extern const struct ltc_prng_descriptor sprng_desc;
-#endif
-
-#ifdef LTC_SOBER128
-int sober128_start(prng_state *prng);
-int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int sober128_ready(prng_state *prng);
-unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int sober128_done(prng_state *prng);
-int  sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int  sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int  sober128_test(void);
-extern const struct ltc_prng_descriptor sober128_desc;
-#endif
-
-int find_prng(const char *name);
-int register_prng(const struct ltc_prng_descriptor *prng);
-int unregister_prng(const struct ltc_prng_descriptor *prng);
-int register_all_prngs(void);
-int prng_is_valid(int idx);
-LTC_MUTEX_PROTO(ltc_prng_mutex)
-
-/* Slow RNG you **might** be able to use to seed a PRNG with.  Be careful as this
- * might not work on all platforms as planned
- */
-unsigned long rng_get_bytes(unsigned char *out,
-                            unsigned long outlen,
-                            void (*callback)(void));
-
-int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void));
-
-#ifdef LTC_PRNG_ENABLE_LTC_RNG
-extern unsigned long (*ltc_rng)(unsigned char *out, unsigned long outlen,
-      void (*callback)(void));
-#endif
diff --git a/libs/tomcrypt/src/headers/tommath.h b/libs/tomcrypt/src/headers/tommath.h
deleted file mode 100644
index 1cd754558cd2..000000000000
--- a/libs/tomcrypt/src/headers/tommath.h
+++ /dev/null
@@ -1,609 +0,0 @@
-/* LibTomMath, multiple-precision integer library -- Tom St Denis
- *
- * LibTomMath is a library that provides multiple-precision
- * integer arithmetic as well as number theoretic functionality.
- *
- * The library was designed directly after the MPI library by
- * Michael Fromberger but has been written from scratch with
- * additional optimizations in place.
- *
- * SPDX-License-Identifier: Unlicense
- */
-#ifndef BN_H_
-#define BN_H_
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define LTM_NO_FILE
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* MS Visual C++ doesn't have a 128bit type for words, so fall back to 32bit MPI's (where words are 64bit) */
-#if defined(_MSC_VER) || defined(__LLP64__) || defined(__e2k__) || defined(__LCC__)
-#   define MP_32BIT
-#endif
-
-/* detect 64-bit mode if possible */
-#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) || \
-    defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || \
-    defined(__s390x__) || defined(__arch64__) || defined(__aarch64__) || \
-    defined(__sparcv9) || defined(__sparc_v9__) || defined(__sparc64__) || \
-    defined(__ia64) || defined(__ia64__) || defined(__itanium__) || defined(_M_IA64) || \
-    defined(__LP64__) || defined(_LP64) || defined(__64BIT__)
-#   if !(defined(MP_32BIT) || defined(MP_16BIT) || defined(MP_8BIT))
-#      if defined(__GNUC__)
-/* we support 128bit integers only via: __attribute__((mode(TI))) */
-#         define MP_64BIT
-#      else
-/* otherwise we fall back to MP_32BIT even on 64bit platforms */
-#         define MP_32BIT
-#      endif
-#   endif
-#endif
-
-/* some default configurations.
- *
- * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits
- * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits
- *
- * At the very least a mp_digit must be able to hold 7 bits
- * [any size beyond that is ok provided it doesn't overflow the data type]
- */
-#ifdef MP_8BIT
-typedef uint8_t              mp_digit;
-typedef uint16_t             mp_word;
-#   define MP_SIZEOF_MP_DIGIT 1
-#   ifdef DIGIT_BIT
-#      error You must not define DIGIT_BIT when using MP_8BIT
-#   endif
-#elif defined(MP_16BIT)
-typedef uint16_t             mp_digit;
-typedef uint32_t             mp_word;
-#   define MP_SIZEOF_MP_DIGIT 2
-#   ifdef DIGIT_BIT
-#      error You must not define DIGIT_BIT when using MP_16BIT
-#   endif
-#elif defined(MP_64BIT)
-/* for GCC only on supported platforms */
-typedef uint64_t mp_digit;
-typedef unsigned long        mp_word __attribute__((mode(TI)));
-#   define DIGIT_BIT 60
-#else
-/* this is the default case, 28-bit digits */
-
-/* this is to make porting into LibTomCrypt easier :-) */
-typedef uint32_t             mp_digit;
-typedef uint64_t             mp_word;
-
-#   ifdef MP_31BIT
-/* this is an extension that uses 31-bit digits */
-#      define DIGIT_BIT 31
-#   else
-/* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */
-#      define DIGIT_BIT 28
-#      define MP_28BIT
-#   endif
-#endif
-
-/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */
-#ifndef DIGIT_BIT
-#   define DIGIT_BIT (((CHAR_BIT * MP_SIZEOF_MP_DIGIT) - 1))  /* bits per digit */
-typedef uint_least32_t mp_min_u32;
-#else
-typedef mp_digit mp_min_u32;
-#endif
-
-#define MP_DIGIT_BIT     DIGIT_BIT
-#define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
-#define MP_DIGIT_MAX     MP_MASK
-
-/* equalities */
-#define MP_LT        -1   /* less than */
-#define MP_EQ         0   /* equal to */
-#define MP_GT         1   /* greater than */
-
-#define MP_ZPOS       0   /* positive integer */
-#define MP_NEG        1   /* negative */
-
-#define MP_OKAY       0   /* ok result */
-#define MP_MEM        -2  /* out of mem */
-#define MP_VAL        -3  /* invalid input */
-#define MP_RANGE      MP_VAL
-#define MP_ITER       -4  /* Max. iterations reached */
-
-#define MP_YES        1   /* yes response */
-#define MP_NO         0   /* no response */
-
-/* Primality generation flags */
-#define LTM_PRIME_BBS      0x0001 /* BBS style prime */
-#define LTM_PRIME_SAFE     0x0002 /* Safe prime (p-1)/2 == prime */
-#define LTM_PRIME_2MSB_ON  0x0008 /* force 2nd MSB to 1 */
-
-typedef int           mp_err;
-
-/* you'll have to tune these... */
-extern int KARATSUBA_MUL_CUTOFF,
-       KARATSUBA_SQR_CUTOFF,
-       TOOM_MUL_CUTOFF,
-       TOOM_SQR_CUTOFF;
-
-/* define this to use lower memory usage routines (exptmods mostly) */
-/* #define MP_LOW_MEM */
-
-/* default precision */
-#ifndef MP_PREC
-#   ifndef MP_LOW_MEM
-#      define MP_PREC 32        /* default digits of precision */
-#   else
-#      define MP_PREC 8         /* default digits of precision */
-#   endif
-#endif
-
-/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
-#define MP_WARRAY               (1u << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) + 1))
-
-/* the infamous mp_int structure */
-typedef struct  {
-   int used, alloc, sign;
-   mp_digit *dp;
-} mp_int;
-
-/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */
-typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat);
-
-
-#define USED(m)     ((m)->used)
-#define DIGIT(m, k) ((m)->dp[(k)])
-#define SIGN(m)     ((m)->sign)
-
-/* error code to char* string */
-const char *mp_error_to_string(int code);
-
-/* ---> init and deinit bignum functions <--- */
-/* init a bignum */
-int mp_init(mp_int *a);
-
-/* free a bignum */
-void mp_clear(mp_int *a);
-
-/* init a null terminated series of arguments */
-int mp_init_multi(mp_int *mp, ...);
-
-/* clear a null terminated series of arguments */
-void mp_clear_multi(mp_int *mp, ...);
-
-/* exchange two ints */
-void mp_exch(mp_int *a, mp_int *b);
-
-/* shrink ram required for a bignum */
-int mp_shrink(mp_int *a);
-
-/* grow an int to a given size */
-int mp_grow(mp_int *a, int size);
-
-/* init to a given number of digits */
-int mp_init_size(mp_int *a, int size);
-
-/* ---> Basic Manipulations <--- */
-#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
-#define mp_iseven(a) ((((a)->used == 0) || (((a)->dp[0] & 1u) == 0u)) ? MP_YES : MP_NO)
-#define mp_isodd(a)  ((((a)->used > 0) && (((a)->dp[0] & 1u) == 1u)) ? MP_YES : MP_NO)
-#define mp_isneg(a)  (((a)->sign != MP_ZPOS) ? MP_YES : MP_NO)
-
-/* set to zero */
-void mp_zero(mp_int *a);
-
-/* set to a digit */
-void mp_set(mp_int *a, mp_digit b);
-
-/* set a double */
-int mp_set_double(mp_int *a, double b);
-
-/* set a 32-bit const */
-int mp_set_int(mp_int *a, unsigned long b);
-
-/* set a platform dependent unsigned long value */
-int mp_set_long(mp_int *a, unsigned long b);
-
-/* set a platform dependent unsigned long long value */
-int mp_set_long_long(mp_int *a, unsigned long long b);
-
-/* get a double */
-double mp_get_double(const mp_int *a);
-
-/* get a 32-bit value */
-unsigned long mp_get_int(const mp_int *a);
-
-/* get a platform dependent unsigned long value */
-unsigned long mp_get_long(const mp_int *a);
-
-/* get a platform dependent unsigned long long value */
-unsigned long long mp_get_long_long(const mp_int *a);
-
-/* initialize and set a digit */
-int mp_init_set(mp_int *a, mp_digit b);
-
-/* initialize and set 32-bit value */
-int mp_init_set_int(mp_int *a, unsigned long b);
-
-/* copy, b = a */
-int mp_copy(const mp_int *a, mp_int *b);
-
-/* inits and copies, a = b */
-int mp_init_copy(mp_int *a, const mp_int *b);
-
-/* trim unused digits */
-void mp_clamp(mp_int *a);
-
-/* import binary data */
-int mp_import(mp_int *rop, size_t count, int order, size_t size, int endian, size_t nails, const void *op);
-
-/* export binary data */
-int mp_export(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, const mp_int *op);
-
-/* ---> digit manipulation <--- */
-
-/* right shift by "b" digits */
-void mp_rshd(mp_int *a, int b);
-
-/* left shift by "b" digits */
-int mp_lshd(mp_int *a, int b);
-
-/* c = a / 2**b, implemented as c = a >> b */
-int mp_div_2d(const mp_int *a, int b, mp_int *c, mp_int *d);
-
-/* b = a/2 */
-int mp_div_2(const mp_int *a, mp_int *b);
-
-/* c = a * 2**b, implemented as c = a << b */
-int mp_mul_2d(const mp_int *a, int b, mp_int *c);
-
-/* b = a*2 */
-int mp_mul_2(const mp_int *a, mp_int *b);
-
-/* c = a mod 2**b */
-int mp_mod_2d(const mp_int *a, int b, mp_int *c);
-
-/* computes a = 2**b */
-int mp_2expt(mp_int *a, int b);
-
-/* Counts the number of lsbs which are zero before the first zero bit */
-int mp_cnt_lsb(const mp_int *a);
-
-/* I Love Earth! */
-
-/* makes a pseudo-random mp_int of a given size */
-int mp_rand(mp_int *a, int digits);
-/* makes a pseudo-random small int of a given size */
-int mp_rand_digit(mp_digit *r);
-
-#ifdef MP_PRNG_ENABLE_LTM_RNG
-/* A last resort to provide random data on systems without any of the other
- * implemented ways to gather entropy.
- * It is compatible with `rng_get_bytes()` from libtomcrypt so you could
- * provide that one and then set `ltm_rng = rng_get_bytes;` */
-extern unsigned long (*ltm_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void));
-extern void (*ltm_rng_callback)(void);
-#endif
-
-/* ---> binary operations <--- */
-/* c = a XOR b  */
-int mp_xor(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = a OR b */
-int mp_or(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = a AND b */
-int mp_and(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* Checks the bit at position b and returns MP_YES
-   if the bit is 1, MP_NO if it is 0 and MP_VAL
-   in case of error */
-int mp_get_bit(const mp_int *a, int b);
-
-/* c = a XOR b (two complement) */
-int mp_tc_xor(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = a OR b (two complement) */
-int mp_tc_or(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = a AND b (two complement) */
-int mp_tc_and(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* right shift (two complement) */
-int mp_tc_div_2d(const mp_int *a, int b, mp_int *c);
-
-/* ---> Basic arithmetic <--- */
-
-/* b = ~a */
-int mp_complement(const mp_int *a, mp_int *b);
-
-/* b = -a */
-int mp_neg(const mp_int *a, mp_int *b);
-
-/* b = |a| */
-int mp_abs(const mp_int *a, mp_int *b);
-
-/* compare a to b */
-int mp_cmp(const mp_int *a, const mp_int *b);
-
-/* compare |a| to |b| */
-int mp_cmp_mag(const mp_int *a, const mp_int *b);
-
-/* c = a + b */
-int mp_add(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = a - b */
-int mp_sub(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = a * b */
-int mp_mul(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* b = a*a  */
-int mp_sqr(const mp_int *a, mp_int *b);
-
-/* a/b => cb + d == a */
-int mp_div(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d);
-
-/* c = a mod b, 0 <= c < b  */
-int mp_mod(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* ---> single digit functions <--- */
-
-/* compare against a single digit */
-int mp_cmp_d(const mp_int *a, mp_digit b);
-
-/* c = a + b */
-int mp_add_d(const mp_int *a, mp_digit b, mp_int *c);
-
-/* c = a - b */
-int mp_sub_d(const mp_int *a, mp_digit b, mp_int *c);
-
-/* c = a * b */
-int mp_mul_d(const mp_int *a, mp_digit b, mp_int *c);
-
-/* a/b => cb + d == a */
-int mp_div_d(const mp_int *a, mp_digit b, mp_int *c, mp_digit *d);
-
-/* a/3 => 3c + d == a */
-int mp_div_3(const mp_int *a, mp_int *c, mp_digit *d);
-
-/* c = a**b */
-int mp_expt_d(const mp_int *a, mp_digit b, mp_int *c);
-int mp_expt_d_ex(const mp_int *a, mp_digit b, mp_int *c, int fast);
-
-/* c = a mod b, 0 <= c < b  */
-int mp_mod_d(const mp_int *a, mp_digit b, mp_digit *c);
-
-/* ---> number theory <--- */
-
-/* d = a + b (mod c) */
-int mp_addmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d);
-
-/* d = a - b (mod c) */
-int mp_submod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d);
-
-/* d = a * b (mod c) */
-int mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d);
-
-/* c = a * a (mod b) */
-int mp_sqrmod(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = 1/a (mod b) */
-int mp_invmod(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* c = (a, b) */
-int mp_gcd(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* produces value such that U1*a + U2*b = U3 */
-int mp_exteuclid(const mp_int *a, const mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3);
-
-/* c = [a, b] or (a*b)/(a, b) */
-int mp_lcm(const mp_int *a, const mp_int *b, mp_int *c);
-
-/* finds one of the b'th root of a, such that |c|**b <= |a|
- *
- * returns error if a < 0 and b is even
- */
-int mp_n_root(const mp_int *a, mp_digit b, mp_int *c);
-int mp_n_root_ex(const mp_int *a, mp_digit b, mp_int *c, int fast);
-
-/* special sqrt algo */
-int mp_sqrt(const mp_int *arg, mp_int *ret);
-
-/* special sqrt (mod prime) */
-int mp_sqrtmod_prime(const mp_int *n, const mp_int *prime, mp_int *ret);
-
-/* is number a square? */
-int mp_is_square(const mp_int *arg, int *ret);
-
-/* computes the jacobi c = (a | n) (or Legendre if b is prime)  */
-int mp_jacobi(const mp_int *a, const mp_int *n, int *c);
-
-/* computes the Kronecker symbol c = (a | p) (like jacobi() but with {a,p} in Z */
-int mp_kronecker(const mp_int *a, const mp_int *p, int *c);
-
-/* used to setup the Barrett reduction for a given modulus b */
-int mp_reduce_setup(mp_int *a, const mp_int *b);
-
-/* Barrett Reduction, computes a (mod b) with a precomputed value c
- *
- * Assumes that 0 < x <= m*m, note if 0 > x > -(m*m) then you can merely
- * compute the reduction as -1 * mp_reduce(mp_abs(x)) [pseudo code].
- */
-int mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu);
-
-/* setups the montgomery reduction */
-int mp_montgomery_setup(const mp_int *n, mp_digit *rho);
-
-/* computes a = B**n mod b without division or multiplication useful for
- * normalizing numbers in a Montgomery system.
- */
-int mp_montgomery_calc_normalization(mp_int *a, const mp_int *b);
-
-/* computes x/R == x (mod N) via Montgomery Reduction */
-int mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho);
-
-/* returns 1 if a is a valid DR modulus */
-int mp_dr_is_modulus(const mp_int *a);
-
-/* sets the value of "d" required for mp_dr_reduce */
-void mp_dr_setup(const mp_int *a, mp_digit *d);
-
-/* reduces a modulo n using the Diminished Radix method */
-int mp_dr_reduce(mp_int *x, const mp_int *n, mp_digit k);
-
-/* returns true if a can be reduced with mp_reduce_2k */
-int mp_reduce_is_2k(const mp_int *a);
-
-/* determines k value for 2k reduction */
-int mp_reduce_2k_setup(const mp_int *a, mp_digit *d);
-
-/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */
-int mp_reduce_2k(mp_int *a, const mp_int *n, mp_digit d);
-
-/* returns true if a can be reduced with mp_reduce_2k_l */
-int mp_reduce_is_2k_l(const mp_int *a);
-
-/* determines k value for 2k reduction */
-int mp_reduce_2k_setup_l(const mp_int *a, mp_int *d);
-
-/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */
-int mp_reduce_2k_l(mp_int *a, const mp_int *n, const mp_int *d);
-
-/* Y = G**X (mod P) */
-int mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y);
-
-/* ---> Primes <--- */
-
-/* number of primes */
-#ifdef MP_8BIT
-#  define PRIME_SIZE 31
-#else
-#  define PRIME_SIZE 256
-#endif
-
-/* table of first PRIME_SIZE primes */
-extern const mp_digit ltm_prime_tab[PRIME_SIZE];
-
-/* result=1 if a is divisible by one of the first PRIME_SIZE primes */
-int mp_prime_is_divisible(const mp_int *a, int *result);
-
-/* performs one Fermat test of "a" using base "b".
- * Sets result to 0 if composite or 1 if probable prime
- */
-int mp_prime_fermat(const mp_int *a, const mp_int *b, int *result);
-
-/* performs one Miller-Rabin test of "a" using base "b".
- * Sets result to 0 if composite or 1 if probable prime
- */
-int mp_prime_miller_rabin(const mp_int *a, const mp_int *b, int *result);
-
-/* This gives [for a given bit size] the number of trials required
- * such that Miller-Rabin gives a prob of failure lower than 2^-96
- */
-int mp_prime_rabin_miller_trials(int size);
-
-/* performs one strong Lucas-Selfridge test of "a".
- * Sets result to 0 if composite or 1 if probable prime
- */
-int mp_prime_strong_lucas_selfridge(const mp_int *a, int *result);
-
-/* performs one Frobenius test of "a" as described by Paul Underwood.
- * Sets result to 0 if composite or 1 if probable prime
- */
-int mp_prime_frobenius_underwood(const mp_int *N, int *result);
-
-/* performs t random rounds of Miller-Rabin on "a" additional to
- * bases 2 and 3.  Also performs an initial sieve of trial
- * division.  Determines if "a" is prime with probability
- * of error no more than (1/4)**t.
- * Both a strong Lucas-Selfridge to complete the BPSW test
- * and a separate Frobenius test are available at compile time.
- * With t<0 a deterministic test is run for primes up to
- * 318665857834031151167461. With t<13 (abs(t)-13) additional
- * tests with sequential small primes are run starting at 43.
- * Is Fips 186.4 compliant if called with t as computed by
- * mp_prime_rabin_miller_trials();
- *
- * Sets result to 1 if probably prime, 0 otherwise
- */
-int mp_prime_is_prime(const mp_int *a, int t, int *result);
-
-/* finds the next prime after the number "a" using "t" trials
- * of Miller-Rabin.
- *
- * bbs_style = 1 means the prime must be congruent to 3 mod 4
- */
-int mp_prime_next_prime(mp_int *a, int t, int bbs_style);
-
-/* makes a truly random prime of a given size (bytes),
- * call with bbs = 1 if you want it to be congruent to 3 mod 4
- *
- * You have to supply a callback which fills in a buffer with random bytes.  "dat" is a parameter you can
- * have passed to the callback (e.g. a state or something).  This function doesn't use "dat" itself
- * so it can be NULL
- *
- * The prime generated will be larger than 2^(8*size).
- */
-#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs==1)?LTM_PRIME_BBS:0, cb, dat)
-
-/* makes a truly random prime of a given size (bits),
- *
- * Flags are as follows:
- *
- *   LTM_PRIME_BBS      - make prime congruent to 3 mod 4
- *   LTM_PRIME_SAFE     - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS)
- *   LTM_PRIME_2MSB_ON  - make the 2nd highest bit one
- *
- * You have to supply a callback which fills in a buffer with random bytes.  "dat" is a parameter you can
- * have passed to the callback (e.g. a state or something).  This function doesn't use "dat" itself
- * so it can be NULL
- *
- */
-int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat);
-
-/* ---> radix conversion <--- */
-int mp_count_bits(const mp_int *a);
-
-int mp_unsigned_bin_size(const mp_int *a);
-int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c);
-int mp_to_unsigned_bin(const mp_int *a, unsigned char *b);
-int mp_to_unsigned_bin_n(const mp_int *a, unsigned char *b, unsigned long *outlen);
-
-int mp_signed_bin_size(const mp_int *a);
-int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c);
-int mp_to_signed_bin(const mp_int *a,  unsigned char *b);
-int mp_to_signed_bin_n(const mp_int *a, unsigned char *b, unsigned long *outlen);
-
-int mp_read_radix(mp_int *a, const char *str, int radix);
-int mp_toradix(const mp_int *a, char *str, int radix);
-int mp_toradix_n(const mp_int *a, char *str, int radix, int maxlen);
-int mp_radix_size(const mp_int *a, int radix, int *size);
-
-#ifndef LTM_NO_FILE
-int mp_fread(mp_int *a, int radix, FILE *stream);
-int mp_fwrite(const mp_int *a, int radix, FILE *stream);
-#endif
-
-#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len))
-#define mp_raw_size(mp)           mp_signed_bin_size(mp)
-#define mp_toraw(mp, str)         mp_to_signed_bin((mp), (str))
-#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len))
-#define mp_mag_size(mp)           mp_unsigned_bin_size(mp)
-#define mp_tomag(mp, str)         mp_to_unsigned_bin((mp), (str))
-
-#define mp_tobinary(M, S)  mp_toradix((M), (S), 2)
-#define mp_tooctal(M, S)   mp_toradix((M), (S), 8)
-#define mp_todecimal(M, S) mp_toradix((M), (S), 10)
-#define mp_tohex(M, S)     mp_toradix((M), (S), 16)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libs/tomcrypt/src/headers/tommath_private.h b/libs/tomcrypt/src/headers/tommath_private.h
deleted file mode 100644
index 7e4b90f9dbbb..000000000000
--- a/libs/tomcrypt/src/headers/tommath_private.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* LibTomMath, multiple-precision integer library -- Tom St Denis
- *
- * LibTomMath is a library that provides multiple-precision
- * integer arithmetic as well as number theoretic functionality.
- *
- * The library was designed directly after the MPI library by
- * Michael Fromberger but has been written from scratch with
- * additional optimizations in place.
- *
- * SPDX-License-Identifier: Unlicense
- */
-#ifndef TOMMATH_PRIV_H_
-#define TOMMATH_PRIV_H_
-
-#include "tommath.h"
-#include <ctype.h>
-
-#ifndef MIN
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#endif
-
-#ifndef MAX
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-
-/* C++ compilers don't like assigning void * to mp_digit * */
-#define OPT_CAST(x) (x *)
-
-#else
-
-/* C on the other hand doesn't care */
-#define OPT_CAST(x)
-
-#endif
-
-/* define heap macros */
-#ifndef XMALLOC
-/* default to libc stuff */
-#   define XMALLOC   malloc
-#   define XFREE     free
-#   define XREALLOC  realloc
-#   define XCALLOC   calloc
-#else
-/* prototypes for our heap functions */
-extern void *XMALLOC(size_t n);
-extern void *XREALLOC(void *p, size_t n);
-extern void *XCALLOC(size_t n, size_t s);
-extern void XFREE(void *p);
-#endif
-
-/* lowlevel functions, do not call! */
-int s_mp_add(const mp_int *a, const mp_int *b, mp_int *c);
-int s_mp_sub(const mp_int *a, const mp_int *b, mp_int *c);
-#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
-int fast_s_mp_mul_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs);
-int s_mp_mul_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs);
-int fast_s_mp_mul_high_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs);
-int s_mp_mul_high_digs(const mp_int *a, const mp_int *b, mp_int *c, int digs);
-int fast_s_mp_sqr(const mp_int *a, mp_int *b);
-int s_mp_sqr(const mp_int *a, mp_int *b);
-int mp_karatsuba_mul(const mp_int *a, const mp_int *b, mp_int *c);
-int mp_toom_mul(const mp_int *a, const mp_int *b, mp_int *c);
-int mp_karatsuba_sqr(const mp_int *a, mp_int *b);
-int mp_toom_sqr(const mp_int *a, mp_int *b);
-int fast_mp_invmod(const mp_int *a, const mp_int *b, mp_int *c);
-int mp_invmod_slow(const mp_int *a, const mp_int *b, mp_int *c);
-int fast_mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho);
-int mp_exptmod_fast(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode);
-int s_mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode);
-void bn_reverse(unsigned char *s, int len);
-
-extern const char *const mp_s_rmap;
-extern const uint8_t mp_s_rmap_reverse[];
-extern const size_t mp_s_rmap_reverse_sz;
-
-/* Fancy macro to set an MPI from another type.
- * There are several things assumed:
- *  x is the counter and unsigned
- *  a is the pointer to the MPI
- *  b is the original value that should be set in the MPI.
- */
-#define MP_SET_XLONG(func_name, type)                    \
-int func_name (mp_int * a, type b)                       \
-{                                                        \
-  unsigned int  x;                                       \
-  int           res;                                     \
-                                                         \
-  mp_zero (a);                                           \
-                                                         \
-  /* set four bits at a time */                          \
-  for (x = 0; x < (sizeof(type) * 2u); x++) {            \
-    /* shift the number up four bits */                  \
-    if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) {        \
-      return res;                                        \
-    }                                                    \
-                                                         \
-    /* OR in the top four bits of the source */          \
-    a->dp[0] |= (mp_digit)(b >> ((sizeof(type) * 8u) - 4u)) & 15uL;\
-                                                         \
-    /* shift the source up to the next four bits */      \
-    b <<= 4;                                             \
-                                                         \
-    /* ensure that digits are not clamped off */         \
-    a->used += 1;                                        \
-  }                                                      \
-  mp_clamp (a);                                          \
-  return MP_OKAY;                                        \
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libs/tomcrypt/src/mac/hmac/hmac_done.c b/libs/tomcrypt/src/mac/hmac/hmac_done.c
deleted file mode 100644
index dfe86c29cc4b..000000000000
--- a/libs/tomcrypt/src/mac/hmac/hmac_done.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file hmac_done.c
-  HMAC support, terminate stream, Tom St Denis/Dobes Vandermeer
-*/
-
-#ifdef LTC_HMAC
-
-#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize
-
-/**
-   Terminate an HMAC session
-   @param hmac    The HMAC state
-   @param out     [out] The destination of the HMAC authentication tag
-   @param outlen  [in/out]  The max size and resulting size of the HMAC authentication tag
-   @return CRYPT_OK if successful
-*/
-int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen)
-{
-    unsigned char *buf, *isha;
-    unsigned long hashsize, i;
-    int hash, err;
-
-    LTC_ARGCHK(hmac  != NULL);
-    LTC_ARGCHK(out   != NULL);
-
-    /* test hash */
-    hash = hmac->hash;
-    if((err = hash_is_valid(hash)) != CRYPT_OK) {
-        return err;
-    }
-
-    /* get the hash message digest size */
-    hashsize = hash_descriptor[hash].hashsize;
-
-    /* allocate buffers */
-    buf  = XMALLOC(LTC_HMAC_BLOCKSIZE);
-    isha = XMALLOC(hashsize);
-    if (buf == NULL || isha == NULL) {
-       if (buf != NULL) {
-          XFREE(buf);
-       }
-       if (isha != NULL) {
-          XFREE(isha);
-       }
-       return CRYPT_MEM;
-    }
-
-    /* Get the hash of the first HMAC vector plus the data */
-    if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    /* Create the second HMAC vector vector for step (3) */
-    for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) {
-        buf[i] = hmac->key[i] ^ 0x5C;
-    }
-
-    /* Now calculate the "outer" hash for step (5), (6), and (7) */
-    if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-    if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-    if ((err = hash_descriptor[hash].process(&hmac->md, isha, hashsize)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-    if ((err = hash_descriptor[hash].done(&hmac->md, buf)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    /* copy to output  */
-    for (i = 0; i < hashsize && i < *outlen; i++) {
-        out[i] = buf[i];
-    }
-    *outlen = i;
-
-    err = CRYPT_OK;
-LBL_ERR:
-    XFREE(hmac->key);
-#ifdef LTC_CLEAN_STACK
-    zeromem(isha, hashsize);
-    zeromem(buf,  hashsize);
-    zeromem(hmac, sizeof(*hmac));
-#endif
-
-    XFREE(isha);
-    XFREE(buf);
-
-    return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/hmac/hmac_init.c b/libs/tomcrypt/src/mac/hmac/hmac_init.c
deleted file mode 100644
index 566ce04949d4..000000000000
--- a/libs/tomcrypt/src/mac/hmac/hmac_init.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file hmac_init.c
-  HMAC support, initialize state, Tom St Denis/Dobes Vandermeer
-*/
-
-#ifdef LTC_HMAC
-
-#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize
-
-/**
-   Initialize an HMAC context.
-   @param hmac     The HMAC state
-   @param hash     The index of the hash you want to use
-   @param key      The secret key
-   @param keylen   The length of the secret key (octets)
-   @return CRYPT_OK if successful
-*/
-int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen)
-{
-    unsigned char *buf;
-    unsigned long hashsize;
-    unsigned long i, z;
-    int err;
-
-    LTC_ARGCHK(hmac != NULL);
-    LTC_ARGCHK(key  != NULL);
-
-    /* valid hash? */
-    if ((err = hash_is_valid(hash)) != CRYPT_OK) {
-        return err;
-    }
-    hmac->hash = hash;
-    hashsize   = hash_descriptor[hash].hashsize;
-
-    /* valid key length? */
-    if (keylen == 0) {
-        return CRYPT_INVALID_KEYSIZE;
-    }
-
-    /* allocate ram for buf */
-    buf = XMALLOC(LTC_HMAC_BLOCKSIZE);
-    if (buf == NULL) {
-       return CRYPT_MEM;
-    }
-
-    /* allocate memory for key */
-    hmac->key = XMALLOC(LTC_HMAC_BLOCKSIZE);
-    if (hmac->key == NULL) {
-       XFREE(buf);
-       return CRYPT_MEM;
-    }
-
-    /* (1) make sure we have a large enough key */
-    if(keylen > LTC_HMAC_BLOCKSIZE) {
-        z = LTC_HMAC_BLOCKSIZE;
-        if ((err = hash_memory(hash, key, keylen, hmac->key, &z)) != CRYPT_OK) {
-           goto LBL_ERR;
-        }
-        keylen = hashsize;
-    } else {
-        XMEMCPY(hmac->key, key, (size_t)keylen);
-    }
-
-    if(keylen < LTC_HMAC_BLOCKSIZE) {
-       zeromem((hmac->key) + keylen, (size_t)(LTC_HMAC_BLOCKSIZE - keylen));
-    }
-
-    /* Create the initialization vector for step (3) */
-    for(i=0; i < LTC_HMAC_BLOCKSIZE;   i++) {
-       buf[i] = hmac->key[i] ^ 0x36;
-    }
-
-    /* Pre-pend that to the hash data */
-    if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-    goto done;
-LBL_ERR:
-    /* free the key since we failed */
-    XFREE(hmac->key);
-done:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, LTC_HMAC_BLOCKSIZE);
-#endif
-
-   XFREE(buf);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/hmac/hmac_memory.c b/libs/tomcrypt/src/mac/hmac/hmac_memory.c
deleted file mode 100644
index ac74a6f20b1d..000000000000
--- a/libs/tomcrypt/src/mac/hmac/hmac_memory.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file hmac_memory.c
-  HMAC support, process a block of memory, Tom St Denis/Dobes Vandermeer
-*/
-
-#ifdef LTC_HMAC
-
-/**
-   HMAC a block of memory to produce the authentication tag
-   @param hash      The index of the hash to use
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param in        The data to HMAC
-   @param inlen     The length of the data to HMAC (octets)
-   @param out       [out] Destination of the authentication tag
-   @param outlen    [in/out] Max size and resulting size of authentication tag
-   @return CRYPT_OK if successful
-*/
-int hmac_memory(int hash,
-                const unsigned char *key,  unsigned long keylen,
-                const unsigned char *in,   unsigned long inlen,
-                      unsigned char *out,  unsigned long *outlen)
-{
-    hmac_state *hmac;
-    int         err;
-
-    LTC_ARGCHK(key    != NULL);
-    LTC_ARGCHK(in     != NULL);
-    LTC_ARGCHK(out    != NULL);
-    LTC_ARGCHK(outlen != NULL);
-
-    /* make sure hash descriptor is valid */
-    if ((err = hash_is_valid(hash)) != CRYPT_OK) {
-       return err;
-    }
-
-    /* is there a descriptor? */
-    if (hash_descriptor[hash].hmac_block != NULL) {
-        return hash_descriptor[hash].hmac_block(key, keylen, in, inlen, out, outlen);
-    }
-
-    /* nope, so call the hmac functions */
-    /* allocate ram for hmac state */
-    hmac = XMALLOC(sizeof(hmac_state));
-    if (hmac == NULL) {
-       return CRYPT_MEM;
-    }
-
-    if ((err = hmac_init(hmac, hash, key, keylen)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    if ((err = hmac_process(hmac, in, inlen)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    if ((err = hmac_done(hmac, out, outlen)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(hmac, sizeof(hmac_state));
-#endif
-
-   XFREE(hmac);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/hmac/hmac_memory_multi.c b/libs/tomcrypt/src/mac/hmac/hmac_memory_multi.c
deleted file mode 100644
index 4bac0684c38b..000000000000
--- a/libs/tomcrypt/src/mac/hmac/hmac_memory_multi.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-#include <stdarg.h>
-
-/**
-  @file hmac_memory_multi.c
-  HMAC support, process multiple blocks of memory, Tom St Denis/Dobes Vandermeer
-*/
-
-#ifdef LTC_HMAC
-
-/**
-   HMAC multiple blocks of memory to produce the authentication tag
-   @param hash      The index of the hash to use
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param out       [out] Destination of the authentication tag
-   @param outlen    [in/out] Max size and resulting size of authentication tag
-   @param in        The data to HMAC
-   @param inlen     The length of the data to HMAC (octets)
-   @param ...       tuples of (data,len) pairs to HMAC, terminated with a (NULL,x) (x=don't care)
-   @return CRYPT_OK if successful
-*/
-int hmac_memory_multi(int hash,
-                const unsigned char *key,  unsigned long keylen,
-                      unsigned char *out,  unsigned long *outlen,
-                const unsigned char *in,   unsigned long inlen, ...)
-
-{
-    hmac_state          *hmac;
-    int                  err;
-    va_list              args;
-    const unsigned char *curptr;
-    unsigned long        curlen;
-
-    LTC_ARGCHK(key    != NULL);
-    LTC_ARGCHK(in     != NULL);
-    LTC_ARGCHK(out    != NULL);
-    LTC_ARGCHK(outlen != NULL);
-
-    /* allocate ram for hmac state */
-    hmac = XMALLOC(sizeof(hmac_state));
-    if (hmac == NULL) {
-       return CRYPT_MEM;
-    }
-
-    if ((err = hmac_init(hmac, hash, key, keylen)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    va_start(args, inlen);
-    curptr = in;
-    curlen = inlen;
-    for (;;) {
-       /* process buf */
-       if ((err = hmac_process(hmac, curptr, curlen)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       /* step to next */
-       curptr = va_arg(args, const unsigned char*);
-       if (curptr == NULL) {
-          break;
-       }
-       curlen = va_arg(args, unsigned long);
-    }
-    if ((err = hmac_done(hmac, out, outlen)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(hmac, sizeof(hmac_state));
-#endif
-   XFREE(hmac);
-   va_end(args);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/hmac/hmac_process.c b/libs/tomcrypt/src/mac/hmac/hmac_process.c
deleted file mode 100644
index 70919d933767..000000000000
--- a/libs/tomcrypt/src/mac/hmac/hmac_process.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file hmac_process.c
-  HMAC support, process data, Tom St Denis/Dobes Vandermeer
-*/
-
-#ifdef LTC_HMAC
-
-/**
-  Process data through HMAC
-  @param hmac    The hmac state
-  @param in      The data to send through HMAC
-  @param inlen   The length of the data to HMAC (octets)
-  @return CRYPT_OK if successful
-*/
-int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen)
-{
-    int err;
-    LTC_ARGCHK(hmac != NULL);
-    LTC_ARGCHK(in != NULL);
-    if ((err = hash_is_valid(hmac->hash)) != CRYPT_OK) {
-        return err;
-    }
-    return hash_descriptor[hmac->hash].process(&hmac->md, in, inlen);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/omac/omac_done.c b/libs/tomcrypt/src/mac/omac/omac_done.c
deleted file mode 100644
index 8a29833a646b..000000000000
--- a/libs/tomcrypt/src/mac/omac/omac_done.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file omac_done.c
-  OMAC1 support, terminate a stream, Tom St Denis
-*/
-
-#ifdef LTC_OMAC
-
-/**
-  Terminate an OMAC stream
-  @param omac   The OMAC state
-  @param out    [out] Destination for the authentication tag
-  @param outlen [in/out]  The max size and resulting size of the authentication tag
-  @return CRYPT_OK if successful
-*/
-int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen)
-{
-   int       err, mode;
-   unsigned  x;
-
-   LTC_ARGCHK(omac   != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   if ((err = cipher_is_valid(omac->cipher_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((omac->buflen > (int)sizeof(omac->block)) || (omac->buflen < 0) ||
-       (omac->blklen > (int)sizeof(omac->block)) || (omac->buflen > omac->blklen)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* figure out mode */
-   if (omac->buflen != omac->blklen) {
-      /* add the 0x80 byte */
-      omac->block[omac->buflen++] = 0x80;
-
-      /* pad with 0x00 */
-      while (omac->buflen < omac->blklen) {
-         omac->block[omac->buflen++] = 0x00;
-      }
-      mode = 1;
-   } else {
-      mode = 0;
-   }
-
-   /* now xor prev + Lu[mode] */
-   for (x = 0; x < (unsigned)omac->blklen; x++) {
-       omac->block[x] ^= omac->prev[x] ^ omac->Lu[mode][x];
-   }
-
-   /* encrypt it */
-   if ((err = cipher_descriptor[omac->cipher_idx].ecb_encrypt(omac->block, omac->block, &omac->key)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[omac->cipher_idx].done(&omac->key);
-
-   /* output it */
-   for (x = 0; x < (unsigned)omac->blklen && x < *outlen; x++) {
-       out[x] = omac->block[x];
-   }
-   *outlen = x;
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(omac, sizeof(*omac));
-#endif
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/omac/omac_init.c b/libs/tomcrypt/src/mac/omac/omac_init.c
deleted file mode 100644
index 08a98b354341..000000000000
--- a/libs/tomcrypt/src/mac/omac/omac_init.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file omac_init.c
-  OMAC1 support, initialize state, by Tom St Denis
-*/
-
-
-#ifdef LTC_OMAC
-
-/**
-   Initialize an OMAC state
-   @param omac    The OMAC state to initialize
-   @param cipher  The index of the desired cipher
-   @param key     The secret key
-   @param keylen  The length of the secret key (octets)
-   @return CRYPT_OK if successful
-*/
-int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen)
-{
-   int err, x, y, mask, msb, len;
-
-   LTC_ARGCHK(omac != NULL);
-   LTC_ARGCHK(key  != NULL);
-
-   /* schedule the key */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-#ifdef LTC_FAST
-   if (cipher_descriptor[cipher].block_length % sizeof(LTC_FAST_TYPE)) {
-       return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   /* now setup the system */
-   switch (cipher_descriptor[cipher].block_length) {
-       case 8:  mask = 0x1B;
-                len  = 8;
-                break;
-       case 16: mask = 0x87;
-                len  = 16;
-                break;
-       default: return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &omac->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* ok now we need Lu and Lu^2 [calc one from the other] */
-
-   /* first calc L which is Ek(0) */
-   zeromem(omac->Lu[0], cipher_descriptor[cipher].block_length);
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(omac->Lu[0], omac->Lu[0], &omac->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* now do the mults, whoopy! */
-   for (x = 0; x < 2; x++) {
-       /* if msb(L * u^(x+1)) = 0 then just shift, otherwise shift and xor constant mask */
-       msb = omac->Lu[x][0] >> 7;
-
-       /* shift left */
-       for (y = 0; y < (len - 1); y++) {
-           omac->Lu[x][y] = ((omac->Lu[x][y] << 1) | (omac->Lu[x][y+1] >> 7)) & 255;
-       }
-       omac->Lu[x][len - 1] = ((omac->Lu[x][len - 1] << 1) ^ (msb ? mask : 0)) & 255;
-
-       /* copy up as require */
-       if (x == 0) {
-          XMEMCPY(omac->Lu[1], omac->Lu[0], sizeof(omac->Lu[0]));
-       }
-   }
-
-   /* setup state */
-   omac->cipher_idx = cipher;
-   omac->buflen     = 0;
-   omac->blklen     = len;
-   zeromem(omac->prev,  sizeof(omac->prev));
-   zeromem(omac->block, sizeof(omac->block));
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/omac/omac_memory.c b/libs/tomcrypt/src/mac/omac/omac_memory.c
deleted file mode 100644
index 85a11f4ba705..000000000000
--- a/libs/tomcrypt/src/mac/omac/omac_memory.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file omac_memory.c
-  OMAC1 support, process a block of memory, Tom St Denis
-*/
-
-#ifdef LTC_OMAC
-
-/**
-   OMAC a block of memory
-   @param cipher    The index of the desired cipher
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param in        The data to send through OMAC
-   @param inlen     The length of the data to send through OMAC (octets)
-   @param out       [out] The destination of the authentication tag
-   @param outlen    [in/out]  The max size and resulting size of the authentication tag (octets)
-   @return CRYPT_OK if successful
-*/
-int omac_memory(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                const unsigned char *in,  unsigned long inlen,
-                      unsigned char *out, unsigned long *outlen)
-{
-   int err;
-   omac_state *omac;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* is the cipher valid? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* Use accelerator if found */
-   if (cipher_descriptor[cipher].omac_memory != NULL) {
-      return cipher_descriptor[cipher].omac_memory(key, keylen, in, inlen, out, outlen);
-   }
-
-   /* allocate ram for omac state */
-   omac = XMALLOC(sizeof(omac_state));
-   if (omac == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* omac process the message */
-   if ((err = omac_init(omac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = omac_process(omac, in, inlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = omac_done(omac, out, outlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(omac, sizeof(omac_state));
-#endif
-
-   XFREE(omac);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/omac/omac_memory_multi.c b/libs/tomcrypt/src/mac/omac/omac_memory_multi.c
deleted file mode 100644
index a1dcbb24b68c..000000000000
--- a/libs/tomcrypt/src/mac/omac/omac_memory_multi.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-#include <stdarg.h>
-
-/**
-  @file omac_memory_multi.c
-  OMAC1 support, process multiple blocks of memory, Tom St Denis
-*/
-
-#ifdef LTC_OMAC
-
-/**
-   OMAC multiple blocks of memory
-   @param cipher    The index of the desired cipher
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param out       [out] The destination of the authentication tag
-   @param outlen    [in/out]  The max size and resulting size of the authentication tag (octets)
-   @param in        The data to send through OMAC
-   @param inlen     The length of the data to send through OMAC (octets)
-   @param ...       tuples of (data,len) pairs to OMAC, terminated with a (NULL,x) (x=don't care)
-   @return CRYPT_OK if successful
-*/
-int omac_memory_multi(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in,  unsigned long inlen, ...)
-{
-   int                  err;
-   omac_state          *omac;
-   va_list              args;
-   const unsigned char *curptr;
-   unsigned long        curlen;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* allocate ram for omac state */
-   omac = XMALLOC(sizeof(omac_state));
-   if (omac == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* omac process the message */
-   if ((err = omac_init(omac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   va_start(args, inlen);
-   curptr = in;
-   curlen = inlen;
-   for (;;) {
-      /* process buf */
-      if ((err = omac_process(omac, curptr, curlen)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-      /* step to next */
-      curptr = va_arg(args, const unsigned char*);
-      if (curptr == NULL) {
-         break;
-      }
-      curlen = va_arg(args, unsigned long);
-   }
-   if ((err = omac_done(omac, out, outlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(omac, sizeof(omac_state));
-#endif
-   XFREE(omac);
-   va_end(args);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/omac/omac_process.c b/libs/tomcrypt/src/mac/omac/omac_process.c
deleted file mode 100644
index 978dd8a02543..000000000000
--- a/libs/tomcrypt/src/mac/omac/omac_process.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file omac_process.c
-  OMAC1 support, process data, Tom St Denis
-*/
-
-
-#ifdef LTC_OMAC
-
-/**
-   Process data through OMAC
-   @param omac     The OMAC state
-   @param in       The input data to send through OMAC
-   @param inlen    The length of the input (octets)
-   @return CRYPT_OK if successful
-*/
-int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen)
-{
-   unsigned long n, x;
-   int           err;
-
-   LTC_ARGCHK(omac  != NULL);
-   LTC_ARGCHK(in    != NULL);
-   if ((err = cipher_is_valid(omac->cipher_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((omac->buflen > (int)sizeof(omac->block)) || (omac->buflen < 0) ||
-       (omac->blklen > (int)sizeof(omac->block)) || (omac->buflen > omac->blklen)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-#ifdef LTC_FAST
-   {
-     unsigned long blklen = cipher_descriptor[omac->cipher_idx].block_length;
-
-     if (omac->buflen == 0 && inlen > blklen) {
-        unsigned long y;
-        for (x = 0; x < (inlen - blklen); x += blklen) {
-            for (y = 0; y < blklen; y += sizeof(LTC_FAST_TYPE)) {
-                *(LTC_FAST_TYPE_PTR_CAST(&omac->prev[y])) ^= *(LTC_FAST_TYPE_PTR_CAST(&in[y]));
-            }
-            in += blklen;
-            if ((err = cipher_descriptor[omac->cipher_idx].ecb_encrypt(omac->prev, omac->prev, &omac->key)) != CRYPT_OK) {
-               return err;
-            }
-        }
-        inlen -= x;
-     }
-   }
-#endif
-
-   while (inlen != 0) {
-       /* ok if the block is full we xor in prev, encrypt and replace prev */
-       if (omac->buflen == omac->blklen) {
-          for (x = 0; x < (unsigned long)omac->blklen; x++) {
-              omac->block[x] ^= omac->prev[x];
-          }
-          if ((err = cipher_descriptor[omac->cipher_idx].ecb_encrypt(omac->block, omac->prev, &omac->key)) != CRYPT_OK) {
-             return err;
-          }
-          omac->buflen = 0;
-       }
-
-       /* add bytes */
-       n = MIN(inlen, (unsigned long)(omac->blklen - omac->buflen));
-       XMEMCPY(omac->block + omac->buflen, in, n);
-       omac->buflen  += n;
-       inlen         -= n;
-       in            += n;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_done.c b/libs/tomcrypt/src/mac/pmac/pmac_done.c
deleted file mode 100644
index 8b98f26afa1e..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_done.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pmac_done.c
-  PMAC implementation, terminate a session, by Tom St Denis
-*/
-
-#ifdef LTC_PMAC
-
-int pmac_done(pmac_state *state, unsigned char *out, unsigned long *outlen)
-{
-   int err, x;
-
-   LTC_ARGCHK(state != NULL);
-   LTC_ARGCHK(out   != NULL);
-   if ((err = cipher_is_valid(state->cipher_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((state->buflen > (int)sizeof(state->block)) || (state->buflen < 0) ||
-       (state->block_len > (int)sizeof(state->block)) || (state->buflen > state->block_len)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-
-   /* handle padding.  If multiple xor in L/x */
-
-   if (state->buflen == state->block_len) {
-      /* xor Lr against the checksum */
-      for (x = 0; x < state->block_len; x++) {
-          state->checksum[x] ^= state->block[x] ^ state->Lr[x];
-      }
-   } else {
-      /* otherwise xor message bytes then the 0x80 byte */
-      for (x = 0; x < state->buflen; x++) {
-          state->checksum[x] ^= state->block[x];
-      }
-      state->checksum[x] ^= 0x80;
-   }
-
-   /* encrypt it */
-   if ((err = cipher_descriptor[state->cipher_idx].ecb_encrypt(state->checksum, state->checksum, &state->key)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[state->cipher_idx].done(&state->key);
-
-   /* store it */
-   for (x = 0; x < state->block_len && x < (int)*outlen; x++) {
-       out[x] = state->checksum[x];
-   }
-   *outlen = x;
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(state, sizeof(*state));
-#endif
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_init.c b/libs/tomcrypt/src/mac/pmac/pmac_init.c
deleted file mode 100644
index fe2de83f5d2d..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_init.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pmac_init.c
-   PMAC implementation, initialize state, by Tom St Denis
-*/
-
-#ifdef LTC_PMAC
-
-static const struct {
-    int           len;
-    unsigned char poly_div[MAXBLOCKSIZE],
-                  poly_mul[MAXBLOCKSIZE];
-} polys[] = {
-{
-    8,
-    { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D },
-    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B }
-}, {
-    16,
-    { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 },
-    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 }
-}
-};
-
-/**
-   Initialize a PMAC state
-   @param pmac      The PMAC state to initialize
-   @param cipher    The index of the desired cipher
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @return CRYPT_OK if successful
-*/
-int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen)
-{
-   int poly, x, y, m, err;
-   unsigned char *L;
-
-   LTC_ARGCHK(pmac  != NULL);
-   LTC_ARGCHK(key   != NULL);
-
-   /* valid cipher? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* determine which polys to use */
-   pmac->block_len = cipher_descriptor[cipher].block_length;
-   for (poly = 0; poly < (int)(sizeof(polys)/sizeof(polys[0])); poly++) {
-       if (polys[poly].len == pmac->block_len) {
-          break;
-       }
-   }
-   if (poly >= (int)(sizeof(polys)/sizeof(polys[0]))) {
-      return CRYPT_INVALID_ARG;
-    }
-   if (polys[poly].len != pmac->block_len) {
-      return CRYPT_INVALID_ARG;
-   }
-
-#ifdef LTC_FAST
-   if (pmac->block_len % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-
-   /* schedule the key */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &pmac->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* allocate L */
-   L = XMALLOC(pmac->block_len);
-   if (L == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* find L = E[0] */
-   zeromem(L, pmac->block_len);
-   if ((err = cipher_descriptor[cipher].ecb_encrypt(L, L, &pmac->key)) != CRYPT_OK) {
-      goto error;
-   }
-
-   /* find Ls[i] = L << i for i == 0..31 */
-   XMEMCPY(pmac->Ls[0], L, pmac->block_len);
-   for (x = 1; x < 32; x++) {
-       m = pmac->Ls[x-1][0] >> 7;
-       for (y = 0; y < pmac->block_len-1; y++) {
-           pmac->Ls[x][y] = ((pmac->Ls[x-1][y] << 1) | (pmac->Ls[x-1][y+1] >> 7)) & 255;
-       }
-       pmac->Ls[x][pmac->block_len-1] = (pmac->Ls[x-1][pmac->block_len-1] << 1) & 255;
-
-       if (m == 1) {
-          for (y = 0; y < pmac->block_len; y++) {
-              pmac->Ls[x][y] ^= polys[poly].poly_mul[y];
-          }
-       }
-    }
-
-   /* find Lr = L / x */
-   m = L[pmac->block_len-1] & 1;
-
-   /* shift right */
-   for (x = pmac->block_len - 1; x > 0; x--) {
-      pmac->Lr[x] = ((L[x] >> 1) | (L[x-1] << 7)) & 255;
-   }
-   pmac->Lr[0] = L[0] >> 1;
-
-   if (m == 1) {
-      for (x = 0; x < pmac->block_len; x++) {
-         pmac->Lr[x] ^= polys[poly].poly_div[x];
-      }
-   }
-
-   /* zero buffer, counters, etc... */
-   pmac->block_index = 1;
-   pmac->cipher_idx  = cipher;
-   pmac->buflen      = 0;
-   zeromem(pmac->block,    sizeof(pmac->block));
-   zeromem(pmac->Li,       sizeof(pmac->Li));
-   zeromem(pmac->checksum, sizeof(pmac->checksum));
-   err = CRYPT_OK;
-error:
-#ifdef LTC_CLEAN_STACK
-   zeromem(L, pmac->block_len);
-#endif
-
-   XFREE(L);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_memory.c b/libs/tomcrypt/src/mac/pmac/pmac_memory.c
deleted file mode 100644
index 4b85015ceb85..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_memory.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pmac_memory.c
-   PMAC implementation, process a block of memory, by Tom St Denis
-*/
-
-#ifdef LTC_PMAC
-
-/**
-   PMAC a block of memory
-   @param cipher   The index of the cipher desired
-   @param key      The secret key
-   @param keylen   The length of the secret key (octets)
-   @param in       The data you wish to send through PMAC
-   @param inlen    The length of data you wish to send through PMAC (octets)
-   @param out      [out] Destination for the authentication tag
-   @param outlen   [in/out] The max size and resulting size of the authentication tag
-   @return CRYPT_OK if successful
-*/
-int pmac_memory(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                const unsigned char *in, unsigned long inlen,
-                      unsigned char *out, unsigned long *outlen)
-{
-   int err;
-   pmac_state *pmac;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(in    != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* allocate ram for pmac state */
-   pmac = XMALLOC(sizeof(pmac_state));
-   if (pmac == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = pmac_init(pmac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = pmac_process(pmac, in, inlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = pmac_done(pmac, out, outlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(pmac, sizeof(pmac_state));
-#endif
-
-   XFREE(pmac);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_memory_multi.c b/libs/tomcrypt/src/mac/pmac/pmac_memory_multi.c
deleted file mode 100644
index 1d378296e1a8..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_memory_multi.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-#include <stdarg.h>
-
-/**
-   @file pmac_memory_multi.c
-   PMAC implementation, process multiple blocks of memory, by Tom St Denis
-*/
-
-#ifdef LTC_PMAC
-
-/**
-   PMAC multiple blocks of memory
-   @param cipher   The index of the cipher desired
-   @param key      The secret key
-   @param keylen   The length of the secret key (octets)
-   @param out      [out] Destination for the authentication tag
-   @param outlen   [in/out] The max size and resulting size of the authentication tag
-   @param in       The data you wish to send through PMAC
-   @param inlen    The length of data you wish to send through PMAC (octets)
-   @param ...      tuples of (data,len) pairs to PMAC, terminated with a (NULL,x) (x=don't care)
-   @return CRYPT_OK if successful
-*/
-int pmac_memory_multi(int cipher,
-                const unsigned char *key, unsigned long  keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in,  unsigned long  inlen, ...)
-{
-   int                  err;
-   pmac_state          *pmac;
-   va_list              args;
-   const unsigned char *curptr;
-   unsigned long        curlen;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* allocate ram for pmac state */
-   pmac = XMALLOC(sizeof(pmac_state));
-   if (pmac == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = pmac_init(pmac, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   va_start(args, inlen);
-   curptr = in;
-   curlen = inlen;
-   for (;;) {
-      /* process buf */
-      if ((err = pmac_process(pmac, curptr, curlen)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-      /* step to next */
-      curptr = va_arg(args, const unsigned char*);
-      if (curptr == NULL) {
-         break;
-      }
-      curlen = va_arg(args, unsigned long);
-   }
-   if ((err = pmac_done(pmac, out, outlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(pmac, sizeof(pmac_state));
-#endif
-   XFREE(pmac);
-   va_end(args);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_ntz.c b/libs/tomcrypt/src/mac/pmac/pmac_ntz.c
deleted file mode 100644
index b0851b3acf98..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_ntz.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pmac_ntz.c
-   PMAC implementation, internal function, by Tom St Denis
-*/
-
-#ifdef LTC_PMAC
-
-/**
-  Internal PMAC function
-*/
-int pmac_ntz(unsigned long x)
-{
-   int c;
-   x &= 0xFFFFFFFFUL;
-   c = 0;
-   while ((x & 1) == 0) {
-      ++c;
-      x >>= 1;
-   }
-   return c;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_process.c b/libs/tomcrypt/src/mac/pmac/pmac_process.c
deleted file mode 100644
index 027b3b0b9fef..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_process.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pmac_process.c
-   PMAC implementation, process data, by Tom St Denis
-*/
-
-
-#ifdef LTC_PMAC
-
-/**
-  Process data in a PMAC stream
-  @param pmac     The PMAC state
-  @param in       The data to send through PMAC
-  @param inlen    The length of the data to send through PMAC
-  @return CRYPT_OK if successful
-*/
-int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen)
-{
-   int err, n;
-   unsigned long x;
-   unsigned char Z[MAXBLOCKSIZE];
-
-   LTC_ARGCHK(pmac != NULL);
-   LTC_ARGCHK(in   != NULL);
-   if ((err = cipher_is_valid(pmac->cipher_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((pmac->buflen > (int)sizeof(pmac->block)) || (pmac->buflen < 0) ||
-       (pmac->block_len > (int)sizeof(pmac->block)) || (pmac->buflen > pmac->block_len)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-#ifdef LTC_FAST
-   if (pmac->buflen == 0 && inlen > 16) {
-      unsigned long y;
-      for (x = 0; x < (inlen - 16); x += 16) {
-          pmac_shift_xor(pmac);
-          for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-              *(LTC_FAST_TYPE_PTR_CAST(&Z[y])) = *(LTC_FAST_TYPE_PTR_CAST(&in[y])) ^ *(LTC_FAST_TYPE_PTR_CAST(&pmac->Li[y]));
-          }
-          if ((err = cipher_descriptor[pmac->cipher_idx].ecb_encrypt(Z, Z, &pmac->key)) != CRYPT_OK) {
-             return err;
-          }
-          for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-              *(LTC_FAST_TYPE_PTR_CAST(&pmac->checksum[y])) ^= *(LTC_FAST_TYPE_PTR_CAST(&Z[y]));
-          }
-          in += 16;
-      }
-      inlen -= x;
-   }
-#endif
-
-   while (inlen != 0) {
-       /* ok if the block is full we xor in prev, encrypt and replace prev */
-       if (pmac->buflen == pmac->block_len) {
-          pmac_shift_xor(pmac);
-          for (x = 0; x < (unsigned long)pmac->block_len; x++) {
-               Z[x] = pmac->Li[x] ^ pmac->block[x];
-          }
-          if ((err = cipher_descriptor[pmac->cipher_idx].ecb_encrypt(Z, Z, &pmac->key)) != CRYPT_OK) {
-             return err;
-           }
-          for (x = 0; x < (unsigned long)pmac->block_len; x++) {
-              pmac->checksum[x] ^= Z[x];
-          }
-          pmac->buflen = 0;
-       }
-
-       /* add bytes */
-       n = MIN(inlen, (unsigned long)(pmac->block_len - pmac->buflen));
-       XMEMCPY(pmac->block + pmac->buflen, in, n);
-       pmac->buflen  += n;
-       inlen         -= n;
-       in            += n;
-   }
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(Z, sizeof(Z));
-#endif
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/pmac/pmac_shift_xor.c b/libs/tomcrypt/src/mac/pmac/pmac_shift_xor.c
deleted file mode 100644
index c983802ef30b..000000000000
--- a/libs/tomcrypt/src/mac/pmac/pmac_shift_xor.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pmac_shift_xor.c
-   PMAC implementation, internal function, by Tom St Denis
-*/
-
-#ifdef LTC_PMAC
-
-/**
-  Internal function.  Performs the state update (adding correct multiple)
-  @param pmac   The PMAC state.
-*/
-void pmac_shift_xor(pmac_state *pmac)
-{
-   int x, y;
-   y = pmac_ntz(pmac->block_index++);
-#ifdef LTC_FAST
-   for (x = 0; x < pmac->block_len; x += sizeof(LTC_FAST_TYPE)) {
-       *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)pmac->Li + x)) ^=
-       *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)pmac->Ls[y] + x));
-   }
-#else
-   for (x = 0; x < pmac->block_len; x++) {
-       pmac->Li[x] ^= pmac->Ls[y][x];
-   }
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/xcbc/xcbc_done.c b/libs/tomcrypt/src/mac/xcbc/xcbc_done.c
deleted file mode 100644
index d83836585efb..000000000000
--- a/libs/tomcrypt/src/mac/xcbc/xcbc_done.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file xcbc_done.c
-  XCBC Support, terminate the state
-*/
-
-#ifdef LTC_XCBC
-
-/** Terminate the XCBC-MAC state
-  @param xcbc     XCBC state to terminate
-  @param out      [out] Destination for the MAC tag
-  @param outlen   [in/out] Destination size and final tag size
-  Return CRYPT_OK on success
-*/
-int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen)
-{
-   int err, x;
-   LTC_ARGCHK(xcbc != NULL);
-   LTC_ARGCHK(out  != NULL);
-
-   /* check structure */
-   if ((err = cipher_is_valid(xcbc->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((xcbc->blocksize > cipher_descriptor[xcbc->cipher].block_length) || (xcbc->blocksize < 0) ||
-       (xcbc->buflen > xcbc->blocksize) || (xcbc->buflen < 0)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* which key do we use? */
-   if (xcbc->buflen == xcbc->blocksize) {
-      /* k2 */
-      for (x = 0; x < xcbc->blocksize; x++) {
-         xcbc->IV[x] ^= xcbc->K[1][x];
-      }
-   } else {
-      xcbc->IV[xcbc->buflen] ^= 0x80;
-      /* k3 */
-      for (x = 0; x < xcbc->blocksize; x++) {
-         xcbc->IV[x] ^= xcbc->K[2][x];
-      }
-   }
-
-   /* encrypt */
-   cipher_descriptor[xcbc->cipher].ecb_encrypt(xcbc->IV, xcbc->IV, &xcbc->key);
-   cipher_descriptor[xcbc->cipher].done(&xcbc->key);
-
-   /* extract tag */
-   for (x = 0; x < xcbc->blocksize && (unsigned long)x < *outlen; x++) {
-      out[x] = xcbc->IV[x];
-   }
-   *outlen = x;
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(xcbc, sizeof(*xcbc));
-#endif
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/xcbc/xcbc_init.c b/libs/tomcrypt/src/mac/xcbc/xcbc_init.c
deleted file mode 100644
index c8d40838cb2b..000000000000
--- a/libs/tomcrypt/src/mac/xcbc/xcbc_init.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file xcbc_init.c
-  XCBC Support, start an XCBC state
-*/
-
-#ifdef LTC_XCBC
-
-/** Initialize XCBC-MAC state
-  @param xcbc    [out] XCBC state to initialize
-  @param cipher  Index of cipher to use
-  @param key     [in]  Secret key
-  @param keylen  Length of secret key in octets
-  Return CRYPT_OK on success
-*/
-int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen)
-{
-   int            x, y, err;
-   symmetric_key *skey;
-   unsigned long  k1;
-
-   LTC_ARGCHK(xcbc != NULL);
-   LTC_ARGCHK(key  != NULL);
-
-   /* schedule the key */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-#ifdef LTC_FAST
-   if (cipher_descriptor[cipher].block_length % sizeof(LTC_FAST_TYPE)) {
-       return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   skey = NULL;
-
-   /* are we in pure XCBC mode with three keys? */
-   if (keylen & LTC_XCBC_PURE) {
-      keylen &= ~LTC_XCBC_PURE;
-
-      if (keylen < 2UL*cipher_descriptor[cipher].block_length) {
-         return CRYPT_INVALID_ARG;
-      }
-
-      k1      = keylen - 2*cipher_descriptor[cipher].block_length;
-      XMEMCPY(xcbc->K[0], key, k1);
-      XMEMCPY(xcbc->K[1], key+k1, cipher_descriptor[cipher].block_length);
-      XMEMCPY(xcbc->K[2], key+k1 + cipher_descriptor[cipher].block_length, cipher_descriptor[cipher].block_length);
-   } else {
-      /* use the key expansion */
-      k1      = cipher_descriptor[cipher].block_length;
-
-      /* schedule the user key */
-      skey = XCALLOC(1, sizeof(*skey));
-      if (skey == NULL) {
-         return CRYPT_MEM;
-      }
-
-      if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, skey)) != CRYPT_OK) {
-         goto done;
-      }
-
-      /* make the three keys */
-      for (y = 0; y < 3; y++) {
-        for (x = 0; x < cipher_descriptor[cipher].block_length; x++) {
-           xcbc->K[y][x] = y + 1;
-        }
-        cipher_descriptor[cipher].ecb_encrypt(xcbc->K[y], xcbc->K[y], skey);
-      }
-   }
-
-   /* setup K1 */
-   err = cipher_descriptor[cipher].setup(xcbc->K[0], k1, 0, &xcbc->key);
-
-   /* setup struct */
-   zeromem(xcbc->IV, cipher_descriptor[cipher].block_length);
-   xcbc->blocksize = cipher_descriptor[cipher].block_length;
-   xcbc->cipher    = cipher;
-   xcbc->buflen    = 0;
-done:
-   cipher_descriptor[cipher].done(skey);
-   if (skey != NULL) {
-#ifdef LTC_CLEAN_STACK
-      zeromem(skey, sizeof(*skey));
-#endif
-      XFREE(skey);
-   }
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/xcbc/xcbc_memory.c b/libs/tomcrypt/src/mac/xcbc/xcbc_memory.c
deleted file mode 100644
index 21a0b315fa93..000000000000
--- a/libs/tomcrypt/src/mac/xcbc/xcbc_memory.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file xcbc_process.c
-  XCBC Support, XCBC-MAC a block of memory
-*/
-
-#ifdef LTC_XCBC
-
-/** XCBC-MAC a block of memory
-  @param cipher     Index of cipher to use
-  @param key        [in]  Secret key
-  @param keylen     Length of key in octets
-  @param in         [in]  Message to MAC
-  @param inlen      Length of input in octets
-  @param out        [out] Destination for the MAC tag
-  @param outlen     [in/out] Output size and final tag size
-  Return CRYPT_OK on success.
-*/
-int xcbc_memory(int cipher,
-               const unsigned char *key, unsigned long keylen,
-               const unsigned char *in,  unsigned long inlen,
-                     unsigned char *out, unsigned long *outlen)
-{
-   xcbc_state *xcbc;
-   int         err;
-
-   /* is the cipher valid? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* Use accelerator if found */
-   if (cipher_descriptor[cipher].xcbc_memory != NULL) {
-      return cipher_descriptor[cipher].xcbc_memory(key, keylen, in, inlen, out, outlen);
-   }
-
-   xcbc = XCALLOC(1, sizeof(*xcbc));
-   if (xcbc == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = xcbc_init(xcbc, cipher, key, keylen)) != CRYPT_OK) {
-     goto done;
-   }
-
-   if ((err = xcbc_process(xcbc, in, inlen)) != CRYPT_OK) {
-     goto done;
-   }
-
-   err = xcbc_done(xcbc, out, outlen);
-done:
-   XFREE(xcbc);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/xcbc/xcbc_memory_multi.c b/libs/tomcrypt/src/mac/xcbc/xcbc_memory_multi.c
deleted file mode 100644
index cf3f92ce4bc4..000000000000
--- a/libs/tomcrypt/src/mac/xcbc/xcbc_memory_multi.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-#include <stdarg.h>
-
-/**
-  @file xcbc_memory_multi.c
-  XCBC support, process multiple blocks of memory, Tom St Denis
-*/
-
-#ifdef LTC_XCBC
-
-/**
-   XCBC multiple blocks of memory
-   @param cipher    The index of the desired cipher
-   @param key       The secret key
-   @param keylen    The length of the secret key (octets)
-   @param out       [out] The destination of the authentication tag
-   @param outlen    [in/out]  The max size and resulting size of the authentication tag (octets)
-   @param in        The data to send through XCBC
-   @param inlen     The length of the data to send through XCBC (octets)
-   @param ...       tuples of (data,len) pairs to XCBC, terminated with a (NULL,x) (x=don't care)
-   @return CRYPT_OK if successful
-*/
-int xcbc_memory_multi(int cipher,
-                const unsigned char *key, unsigned long keylen,
-                      unsigned char *out, unsigned long *outlen,
-                const unsigned char *in,  unsigned long inlen, ...)
-{
-   int                  err;
-   xcbc_state          *xcbc;
-   va_list              args;
-   const unsigned char *curptr;
-   unsigned long        curlen;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* allocate ram for xcbc state */
-   xcbc = XMALLOC(sizeof(xcbc_state));
-   if (xcbc == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* xcbc process the message */
-   if ((err = xcbc_init(xcbc, cipher, key, keylen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   va_start(args, inlen);
-   curptr = in;
-   curlen = inlen;
-   for (;;) {
-      /* process buf */
-      if ((err = xcbc_process(xcbc, curptr, curlen)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-      /* step to next */
-      curptr = va_arg(args, const unsigned char*);
-      if (curptr == NULL) {
-         break;
-      }
-      curlen = va_arg(args, unsigned long);
-   }
-   if ((err = xcbc_done(xcbc, out, outlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(xcbc, sizeof(xcbc_state));
-#endif
-   XFREE(xcbc);
-   va_end(args);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/mac/xcbc/xcbc_process.c b/libs/tomcrypt/src/mac/xcbc/xcbc_process.c
deleted file mode 100644
index a1dad164d7d7..000000000000
--- a/libs/tomcrypt/src/mac/xcbc/xcbc_process.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file xcbc_process.c
-  XCBC Support, process blocks with XCBC
-*/
-
-#ifdef LTC_XCBC
-
-/** Process data through XCBC-MAC
-  @param xcbc     The XCBC-MAC state
-  @param in       Input data to process
-  @param inlen    Length of input in octets
-  Return CRYPT_OK on success
-*/
-int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen)
-{
-   int err;
-#ifdef LTC_FAST
-   int x;
-#endif
-
-   LTC_ARGCHK(xcbc != NULL);
-   LTC_ARGCHK(in   != NULL);
-
-   /* check structure */
-   if ((err = cipher_is_valid(xcbc->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((xcbc->blocksize > cipher_descriptor[xcbc->cipher].block_length) || (xcbc->blocksize < 0) ||
-       (xcbc->buflen > xcbc->blocksize) || (xcbc->buflen < 0)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-#ifdef LTC_FAST
-   if (xcbc->buflen == 0) {
-       while (inlen > (unsigned long)xcbc->blocksize) {
-           for (x = 0; x < xcbc->blocksize; x += sizeof(LTC_FAST_TYPE)) {
-              *(LTC_FAST_TYPE_PTR_CAST(&(xcbc->IV[x]))) ^= *(LTC_FAST_TYPE_PTR_CAST(&(in[x])));
-           }
-           cipher_descriptor[xcbc->cipher].ecb_encrypt(xcbc->IV, xcbc->IV, &xcbc->key);
-           in    += xcbc->blocksize;
-           inlen -= xcbc->blocksize;
-       }
-   }
-#endif
-
-   while (inlen) {
-      if (xcbc->buflen == xcbc->blocksize) {
-         cipher_descriptor[xcbc->cipher].ecb_encrypt(xcbc->IV, xcbc->IV, &xcbc->key);
-         xcbc->buflen = 0;
-      }
-      xcbc->IV[xcbc->buflen++] ^= *in++;
-      --inlen;
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/math/fp/ltc_ecc_fp_mulmod.c b/libs/tomcrypt/src/math/fp/ltc_ecc_fp_mulmod.c
deleted file mode 100644
index ec472ba2a539..000000000000
--- a/libs/tomcrypt/src/math/fp/ltc_ecc_fp_mulmod.c
+++ /dev/null
@@ -1,1579 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_fp_mulmod.c
-  ECC Crypto, Tom St Denis
-*/
-
-#if defined(LTC_MECC) && defined(LTC_MECC_FP)
-#include <limits.h>
-
-/* number of entries in the cache */
-#ifndef FP_ENTRIES
-#define FP_ENTRIES 16
-#endif
-
-/* number of bits in LUT */
-#ifndef FP_LUT
-#define FP_LUT     8U
-#endif
-
-#if (FP_LUT > 12) || (FP_LUT < 2)
-   #error FP_LUT must be between 2 and 12 inclusively
-#endif
-
-/** Our FP cache */
-static struct {
-   ecc_point *g,              /* cached COPY of base point */
-             *LUT[1U<<FP_LUT]; /* fixed point lookup */
-   void      *mu;             /* copy of the montgomery constant */
-   int        lru_count;      /* amount of times this entry has been used */
-   int        lock;           /* flag to indicate cache eviction permitted (0) or not (1) */
-} fp_cache[FP_ENTRIES];
-
-LTC_MUTEX_GLOBAL(ltc_ecc_fp_lock)
-
-/* simple table to help direct the generation of the LUT */
-static const struct {
-   int ham, terma, termb;
-} lut_orders[] = {
-   { 0, 0, 0 }, { 1, 0, 0 }, { 1, 0, 0 }, { 2, 1, 2 }, { 1, 0, 0 }, { 2, 1, 4 }, { 2, 2, 4 }, { 3, 3, 4 },
-   { 1, 0, 0 }, { 2, 1, 8 }, { 2, 2, 8 }, { 3, 3, 8 }, { 2, 4, 8 }, { 3, 5, 8 }, { 3, 6, 8 }, { 4, 7, 8 },
-   { 1, 0, 0 }, { 2, 1, 16 }, { 2, 2, 16 }, { 3, 3, 16 }, { 2, 4, 16 }, { 3, 5, 16 }, { 3, 6, 16 }, { 4, 7, 16 },
-   { 2, 8, 16 }, { 3, 9, 16 }, { 3, 10, 16 }, { 4, 11, 16 }, { 3, 12, 16 }, { 4, 13, 16 }, { 4, 14, 16 }, { 5, 15, 16 },
-   { 1, 0, 0 }, { 2, 1, 32 }, { 2, 2, 32 }, { 3, 3, 32 }, { 2, 4, 32 }, { 3, 5, 32 }, { 3, 6, 32 }, { 4, 7, 32 },
-   { 2, 8, 32 }, { 3, 9, 32 }, { 3, 10, 32 }, { 4, 11, 32 }, { 3, 12, 32 }, { 4, 13, 32 }, { 4, 14, 32 }, { 5, 15, 32 },
-   { 2, 16, 32 }, { 3, 17, 32 }, { 3, 18, 32 }, { 4, 19, 32 }, { 3, 20, 32 }, { 4, 21, 32 }, { 4, 22, 32 }, { 5, 23, 32 },
-   { 3, 24, 32 }, { 4, 25, 32 }, { 4, 26, 32 }, { 5, 27, 32 }, { 4, 28, 32 }, { 5, 29, 32 }, { 5, 30, 32 }, { 6, 31, 32 },
-#if FP_LUT > 6
-   { 1, 0, 0 }, { 2, 1, 64 }, { 2, 2, 64 }, { 3, 3, 64 }, { 2, 4, 64 }, { 3, 5, 64 }, { 3, 6, 64 }, { 4, 7, 64 },
-   { 2, 8, 64 }, { 3, 9, 64 }, { 3, 10, 64 }, { 4, 11, 64 }, { 3, 12, 64 }, { 4, 13, 64 }, { 4, 14, 64 }, { 5, 15, 64 },
-   { 2, 16, 64 }, { 3, 17, 64 }, { 3, 18, 64 }, { 4, 19, 64 }, { 3, 20, 64 }, { 4, 21, 64 }, { 4, 22, 64 }, { 5, 23, 64 },
-   { 3, 24, 64 }, { 4, 25, 64 }, { 4, 26, 64 }, { 5, 27, 64 }, { 4, 28, 64 }, { 5, 29, 64 }, { 5, 30, 64 }, { 6, 31, 64 },
-   { 2, 32, 64 }, { 3, 33, 64 }, { 3, 34, 64 }, { 4, 35, 64 }, { 3, 36, 64 }, { 4, 37, 64 }, { 4, 38, 64 }, { 5, 39, 64 },
-   { 3, 40, 64 }, { 4, 41, 64 }, { 4, 42, 64 }, { 5, 43, 64 }, { 4, 44, 64 }, { 5, 45, 64 }, { 5, 46, 64 }, { 6, 47, 64 },
-   { 3, 48, 64 }, { 4, 49, 64 }, { 4, 50, 64 }, { 5, 51, 64 }, { 4, 52, 64 }, { 5, 53, 64 }, { 5, 54, 64 }, { 6, 55, 64 },
-   { 4, 56, 64 }, { 5, 57, 64 }, { 5, 58, 64 }, { 6, 59, 64 }, { 5, 60, 64 }, { 6, 61, 64 }, { 6, 62, 64 }, { 7, 63, 64 },
-#if FP_LUT > 7
-   { 1, 0, 0 }, { 2, 1, 128 }, { 2, 2, 128 }, { 3, 3, 128 }, { 2, 4, 128 }, { 3, 5, 128 }, { 3, 6, 128 }, { 4, 7, 128 },
-   { 2, 8, 128 }, { 3, 9, 128 }, { 3, 10, 128 }, { 4, 11, 128 }, { 3, 12, 128 }, { 4, 13, 128 }, { 4, 14, 128 }, { 5, 15, 128 },
-   { 2, 16, 128 }, { 3, 17, 128 }, { 3, 18, 128 }, { 4, 19, 128 }, { 3, 20, 128 }, { 4, 21, 128 }, { 4, 22, 128 }, { 5, 23, 128 },
-   { 3, 24, 128 }, { 4, 25, 128 }, { 4, 26, 128 }, { 5, 27, 128 }, { 4, 28, 128 }, { 5, 29, 128 }, { 5, 30, 128 }, { 6, 31, 128 },
-   { 2, 32, 128 }, { 3, 33, 128 }, { 3, 34, 128 }, { 4, 35, 128 }, { 3, 36, 128 }, { 4, 37, 128 }, { 4, 38, 128 }, { 5, 39, 128 },
-   { 3, 40, 128 }, { 4, 41, 128 }, { 4, 42, 128 }, { 5, 43, 128 }, { 4, 44, 128 }, { 5, 45, 128 }, { 5, 46, 128 }, { 6, 47, 128 },
-   { 3, 48, 128 }, { 4, 49, 128 }, { 4, 50, 128 }, { 5, 51, 128 }, { 4, 52, 128 }, { 5, 53, 128 }, { 5, 54, 128 }, { 6, 55, 128 },
-   { 4, 56, 128 }, { 5, 57, 128 }, { 5, 58, 128 }, { 6, 59, 128 }, { 5, 60, 128 }, { 6, 61, 128 }, { 6, 62, 128 }, { 7, 63, 128 },
-   { 2, 64, 128 }, { 3, 65, 128 }, { 3, 66, 128 }, { 4, 67, 128 }, { 3, 68, 128 }, { 4, 69, 128 }, { 4, 70, 128 }, { 5, 71, 128 },
-   { 3, 72, 128 }, { 4, 73, 128 }, { 4, 74, 128 }, { 5, 75, 128 }, { 4, 76, 128 }, { 5, 77, 128 }, { 5, 78, 128 }, { 6, 79, 128 },
-   { 3, 80, 128 }, { 4, 81, 128 }, { 4, 82, 128 }, { 5, 83, 128 }, { 4, 84, 128 }, { 5, 85, 128 }, { 5, 86, 128 }, { 6, 87, 128 },
-   { 4, 88, 128 }, { 5, 89, 128 }, { 5, 90, 128 }, { 6, 91, 128 }, { 5, 92, 128 }, { 6, 93, 128 }, { 6, 94, 128 }, { 7, 95, 128 },
-   { 3, 96, 128 }, { 4, 97, 128 }, { 4, 98, 128 }, { 5, 99, 128 }, { 4, 100, 128 }, { 5, 101, 128 }, { 5, 102, 128 }, { 6, 103, 128 },
-   { 4, 104, 128 }, { 5, 105, 128 }, { 5, 106, 128 }, { 6, 107, 128 }, { 5, 108, 128 }, { 6, 109, 128 }, { 6, 110, 128 }, { 7, 111, 128 },
-   { 4, 112, 128 }, { 5, 113, 128 }, { 5, 114, 128 }, { 6, 115, 128 }, { 5, 116, 128 }, { 6, 117, 128 }, { 6, 118, 128 }, { 7, 119, 128 },
-   { 5, 120, 128 }, { 6, 121, 128 }, { 6, 122, 128 }, { 7, 123, 128 }, { 6, 124, 128 }, { 7, 125, 128 }, { 7, 126, 128 }, { 8, 127, 128 },
-#if FP_LUT > 8
-   { 1, 0, 0 }, { 2, 1, 256 }, { 2, 2, 256 }, { 3, 3, 256 }, { 2, 4, 256 }, { 3, 5, 256 }, { 3, 6, 256 }, { 4, 7, 256 },
-   { 2, 8, 256 }, { 3, 9, 256 }, { 3, 10, 256 }, { 4, 11, 256 }, { 3, 12, 256 }, { 4, 13, 256 }, { 4, 14, 256 }, { 5, 15, 256 },
-   { 2, 16, 256 }, { 3, 17, 256 }, { 3, 18, 256 }, { 4, 19, 256 }, { 3, 20, 256 }, { 4, 21, 256 }, { 4, 22, 256 }, { 5, 23, 256 },
-   { 3, 24, 256 }, { 4, 25, 256 }, { 4, 26, 256 }, { 5, 27, 256 }, { 4, 28, 256 }, { 5, 29, 256 }, { 5, 30, 256 }, { 6, 31, 256 },
-   { 2, 32, 256 }, { 3, 33, 256 }, { 3, 34, 256 }, { 4, 35, 256 }, { 3, 36, 256 }, { 4, 37, 256 }, { 4, 38, 256 }, { 5, 39, 256 },
-   { 3, 40, 256 }, { 4, 41, 256 }, { 4, 42, 256 }, { 5, 43, 256 }, { 4, 44, 256 }, { 5, 45, 256 }, { 5, 46, 256 }, { 6, 47, 256 },
-   { 3, 48, 256 }, { 4, 49, 256 }, { 4, 50, 256 }, { 5, 51, 256 }, { 4, 52, 256 }, { 5, 53, 256 }, { 5, 54, 256 }, { 6, 55, 256 },
-   { 4, 56, 256 }, { 5, 57, 256 }, { 5, 58, 256 }, { 6, 59, 256 }, { 5, 60, 256 }, { 6, 61, 256 }, { 6, 62, 256 }, { 7, 63, 256 },
-   { 2, 64, 256 }, { 3, 65, 256 }, { 3, 66, 256 }, { 4, 67, 256 }, { 3, 68, 256 }, { 4, 69, 256 }, { 4, 70, 256 }, { 5, 71, 256 },
-   { 3, 72, 256 }, { 4, 73, 256 }, { 4, 74, 256 }, { 5, 75, 256 }, { 4, 76, 256 }, { 5, 77, 256 }, { 5, 78, 256 }, { 6, 79, 256 },
-   { 3, 80, 256 }, { 4, 81, 256 }, { 4, 82, 256 }, { 5, 83, 256 }, { 4, 84, 256 }, { 5, 85, 256 }, { 5, 86, 256 }, { 6, 87, 256 },
-   { 4, 88, 256 }, { 5, 89, 256 }, { 5, 90, 256 }, { 6, 91, 256 }, { 5, 92, 256 }, { 6, 93, 256 }, { 6, 94, 256 }, { 7, 95, 256 },
-   { 3, 96, 256 }, { 4, 97, 256 }, { 4, 98, 256 }, { 5, 99, 256 }, { 4, 100, 256 }, { 5, 101, 256 }, { 5, 102, 256 }, { 6, 103, 256 },
-   { 4, 104, 256 }, { 5, 105, 256 }, { 5, 106, 256 }, { 6, 107, 256 }, { 5, 108, 256 }, { 6, 109, 256 }, { 6, 110, 256 }, { 7, 111, 256 },
-   { 4, 112, 256 }, { 5, 113, 256 }, { 5, 114, 256 }, { 6, 115, 256 }, { 5, 116, 256 }, { 6, 117, 256 }, { 6, 118, 256 }, { 7, 119, 256 },
-   { 5, 120, 256 }, { 6, 121, 256 }, { 6, 122, 256 }, { 7, 123, 256 }, { 6, 124, 256 }, { 7, 125, 256 }, { 7, 126, 256 }, { 8, 127, 256 },
-   { 2, 128, 256 }, { 3, 129, 256 }, { 3, 130, 256 }, { 4, 131, 256 }, { 3, 132, 256 }, { 4, 133, 256 }, { 4, 134, 256 }, { 5, 135, 256 },
-   { 3, 136, 256 }, { 4, 137, 256 }, { 4, 138, 256 }, { 5, 139, 256 }, { 4, 140, 256 }, { 5, 141, 256 }, { 5, 142, 256 }, { 6, 143, 256 },
-   { 3, 144, 256 }, { 4, 145, 256 }, { 4, 146, 256 }, { 5, 147, 256 }, { 4, 148, 256 }, { 5, 149, 256 }, { 5, 150, 256 }, { 6, 151, 256 },
-   { 4, 152, 256 }, { 5, 153, 256 }, { 5, 154, 256 }, { 6, 155, 256 }, { 5, 156, 256 }, { 6, 157, 256 }, { 6, 158, 256 }, { 7, 159, 256 },
-   { 3, 160, 256 }, { 4, 161, 256 }, { 4, 162, 256 }, { 5, 163, 256 }, { 4, 164, 256 }, { 5, 165, 256 }, { 5, 166, 256 }, { 6, 167, 256 },
-   { 4, 168, 256 }, { 5, 169, 256 }, { 5, 170, 256 }, { 6, 171, 256 }, { 5, 172, 256 }, { 6, 173, 256 }, { 6, 174, 256 }, { 7, 175, 256 },
-   { 4, 176, 256 }, { 5, 177, 256 }, { 5, 178, 256 }, { 6, 179, 256 }, { 5, 180, 256 }, { 6, 181, 256 }, { 6, 182, 256 }, { 7, 183, 256 },
-   { 5, 184, 256 }, { 6, 185, 256 }, { 6, 186, 256 }, { 7, 187, 256 }, { 6, 188, 256 }, { 7, 189, 256 }, { 7, 190, 256 }, { 8, 191, 256 },
-   { 3, 192, 256 }, { 4, 193, 256 }, { 4, 194, 256 }, { 5, 195, 256 }, { 4, 196, 256 }, { 5, 197, 256 }, { 5, 198, 256 }, { 6, 199, 256 },
-   { 4, 200, 256 }, { 5, 201, 256 }, { 5, 202, 256 }, { 6, 203, 256 }, { 5, 204, 256 }, { 6, 205, 256 }, { 6, 206, 256 }, { 7, 207, 256 },
-   { 4, 208, 256 }, { 5, 209, 256 }, { 5, 210, 256 }, { 6, 211, 256 }, { 5, 212, 256 }, { 6, 213, 256 }, { 6, 214, 256 }, { 7, 215, 256 },
-   { 5, 216, 256 }, { 6, 217, 256 }, { 6, 218, 256 }, { 7, 219, 256 }, { 6, 220, 256 }, { 7, 221, 256 }, { 7, 222, 256 }, { 8, 223, 256 },
-   { 4, 224, 256 }, { 5, 225, 256 }, { 5, 226, 256 }, { 6, 227, 256 }, { 5, 228, 256 }, { 6, 229, 256 }, { 6, 230, 256 }, { 7, 231, 256 },
-   { 5, 232, 256 }, { 6, 233, 256 }, { 6, 234, 256 }, { 7, 235, 256 }, { 6, 236, 256 }, { 7, 237, 256 }, { 7, 238, 256 }, { 8, 239, 256 },
-   { 5, 240, 256 }, { 6, 241, 256 }, { 6, 242, 256 }, { 7, 243, 256 }, { 6, 244, 256 }, { 7, 245, 256 }, { 7, 246, 256 }, { 8, 247, 256 },
-   { 6, 248, 256 }, { 7, 249, 256 }, { 7, 250, 256 }, { 8, 251, 256 }, { 7, 252, 256 }, { 8, 253, 256 }, { 8, 254, 256 }, { 9, 255, 256 },
-#if FP_LUT > 9
-   { 1, 0, 0 }, { 2, 1, 512 }, { 2, 2, 512 }, { 3, 3, 512 }, { 2, 4, 512 }, { 3, 5, 512 }, { 3, 6, 512 }, { 4, 7, 512 },
-   { 2, 8, 512 }, { 3, 9, 512 }, { 3, 10, 512 }, { 4, 11, 512 }, { 3, 12, 512 }, { 4, 13, 512 }, { 4, 14, 512 }, { 5, 15, 512 },
-   { 2, 16, 512 }, { 3, 17, 512 }, { 3, 18, 512 }, { 4, 19, 512 }, { 3, 20, 512 }, { 4, 21, 512 }, { 4, 22, 512 }, { 5, 23, 512 },
-   { 3, 24, 512 }, { 4, 25, 512 }, { 4, 26, 512 }, { 5, 27, 512 }, { 4, 28, 512 }, { 5, 29, 512 }, { 5, 30, 512 }, { 6, 31, 512 },
-   { 2, 32, 512 }, { 3, 33, 512 }, { 3, 34, 512 }, { 4, 35, 512 }, { 3, 36, 512 }, { 4, 37, 512 }, { 4, 38, 512 }, { 5, 39, 512 },
-   { 3, 40, 512 }, { 4, 41, 512 }, { 4, 42, 512 }, { 5, 43, 512 }, { 4, 44, 512 }, { 5, 45, 512 }, { 5, 46, 512 }, { 6, 47, 512 },
-   { 3, 48, 512 }, { 4, 49, 512 }, { 4, 50, 512 }, { 5, 51, 512 }, { 4, 52, 512 }, { 5, 53, 512 }, { 5, 54, 512 }, { 6, 55, 512 },
-   { 4, 56, 512 }, { 5, 57, 512 }, { 5, 58, 512 }, { 6, 59, 512 }, { 5, 60, 512 }, { 6, 61, 512 }, { 6, 62, 512 }, { 7, 63, 512 },
-   { 2, 64, 512 }, { 3, 65, 512 }, { 3, 66, 512 }, { 4, 67, 512 }, { 3, 68, 512 }, { 4, 69, 512 }, { 4, 70, 512 }, { 5, 71, 512 },
-   { 3, 72, 512 }, { 4, 73, 512 }, { 4, 74, 512 }, { 5, 75, 512 }, { 4, 76, 512 }, { 5, 77, 512 }, { 5, 78, 512 }, { 6, 79, 512 },
-   { 3, 80, 512 }, { 4, 81, 512 }, { 4, 82, 512 }, { 5, 83, 512 }, { 4, 84, 512 }, { 5, 85, 512 }, { 5, 86, 512 }, { 6, 87, 512 },
-   { 4, 88, 512 }, { 5, 89, 512 }, { 5, 90, 512 }, { 6, 91, 512 }, { 5, 92, 512 }, { 6, 93, 512 }, { 6, 94, 512 }, { 7, 95, 512 },
-   { 3, 96, 512 }, { 4, 97, 512 }, { 4, 98, 512 }, { 5, 99, 512 }, { 4, 100, 512 }, { 5, 101, 512 }, { 5, 102, 512 }, { 6, 103, 512 },
-   { 4, 104, 512 }, { 5, 105, 512 }, { 5, 106, 512 }, { 6, 107, 512 }, { 5, 108, 512 }, { 6, 109, 512 }, { 6, 110, 512 }, { 7, 111, 512 },
-   { 4, 112, 512 }, { 5, 113, 512 }, { 5, 114, 512 }, { 6, 115, 512 }, { 5, 116, 512 }, { 6, 117, 512 }, { 6, 118, 512 }, { 7, 119, 512 },
-   { 5, 120, 512 }, { 6, 121, 512 }, { 6, 122, 512 }, { 7, 123, 512 }, { 6, 124, 512 }, { 7, 125, 512 }, { 7, 126, 512 }, { 8, 127, 512 },
-   { 2, 128, 512 }, { 3, 129, 512 }, { 3, 130, 512 }, { 4, 131, 512 }, { 3, 132, 512 }, { 4, 133, 512 }, { 4, 134, 512 }, { 5, 135, 512 },
-   { 3, 136, 512 }, { 4, 137, 512 }, { 4, 138, 512 }, { 5, 139, 512 }, { 4, 140, 512 }, { 5, 141, 512 }, { 5, 142, 512 }, { 6, 143, 512 },
-   { 3, 144, 512 }, { 4, 145, 512 }, { 4, 146, 512 }, { 5, 147, 512 }, { 4, 148, 512 }, { 5, 149, 512 }, { 5, 150, 512 }, { 6, 151, 512 },
-   { 4, 152, 512 }, { 5, 153, 512 }, { 5, 154, 512 }, { 6, 155, 512 }, { 5, 156, 512 }, { 6, 157, 512 }, { 6, 158, 512 }, { 7, 159, 512 },
-   { 3, 160, 512 }, { 4, 161, 512 }, { 4, 162, 512 }, { 5, 163, 512 }, { 4, 164, 512 }, { 5, 165, 512 }, { 5, 166, 512 }, { 6, 167, 512 },
-   { 4, 168, 512 }, { 5, 169, 512 }, { 5, 170, 512 }, { 6, 171, 512 }, { 5, 172, 512 }, { 6, 173, 512 }, { 6, 174, 512 }, { 7, 175, 512 },
-   { 4, 176, 512 }, { 5, 177, 512 }, { 5, 178, 512 }, { 6, 179, 512 }, { 5, 180, 512 }, { 6, 181, 512 }, { 6, 182, 512 }, { 7, 183, 512 },
-   { 5, 184, 512 }, { 6, 185, 512 }, { 6, 186, 512 }, { 7, 187, 512 }, { 6, 188, 512 }, { 7, 189, 512 }, { 7, 190, 512 }, { 8, 191, 512 },
-   { 3, 192, 512 }, { 4, 193, 512 }, { 4, 194, 512 }, { 5, 195, 512 }, { 4, 196, 512 }, { 5, 197, 512 }, { 5, 198, 512 }, { 6, 199, 512 },
-   { 4, 200, 512 }, { 5, 201, 512 }, { 5, 202, 512 }, { 6, 203, 512 }, { 5, 204, 512 }, { 6, 205, 512 }, { 6, 206, 512 }, { 7, 207, 512 },
-   { 4, 208, 512 }, { 5, 209, 512 }, { 5, 210, 512 }, { 6, 211, 512 }, { 5, 212, 512 }, { 6, 213, 512 }, { 6, 214, 512 }, { 7, 215, 512 },
-   { 5, 216, 512 }, { 6, 217, 512 }, { 6, 218, 512 }, { 7, 219, 512 }, { 6, 220, 512 }, { 7, 221, 512 }, { 7, 222, 512 }, { 8, 223, 512 },
-   { 4, 224, 512 }, { 5, 225, 512 }, { 5, 226, 512 }, { 6, 227, 512 }, { 5, 228, 512 }, { 6, 229, 512 }, { 6, 230, 512 }, { 7, 231, 512 },
-   { 5, 232, 512 }, { 6, 233, 512 }, { 6, 234, 512 }, { 7, 235, 512 }, { 6, 236, 512 }, { 7, 237, 512 }, { 7, 238, 512 }, { 8, 239, 512 },
-   { 5, 240, 512 }, { 6, 241, 512 }, { 6, 242, 512 }, { 7, 243, 512 }, { 6, 244, 512 }, { 7, 245, 512 }, { 7, 246, 512 }, { 8, 247, 512 },
-   { 6, 248, 512 }, { 7, 249, 512 }, { 7, 250, 512 }, { 8, 251, 512 }, { 7, 252, 512 }, { 8, 253, 512 }, { 8, 254, 512 }, { 9, 255, 512 },
-   { 2, 256, 512 }, { 3, 257, 512 }, { 3, 258, 512 }, { 4, 259, 512 }, { 3, 260, 512 }, { 4, 261, 512 }, { 4, 262, 512 }, { 5, 263, 512 },
-   { 3, 264, 512 }, { 4, 265, 512 }, { 4, 266, 512 }, { 5, 267, 512 }, { 4, 268, 512 }, { 5, 269, 512 }, { 5, 270, 512 }, { 6, 271, 512 },
-   { 3, 272, 512 }, { 4, 273, 512 }, { 4, 274, 512 }, { 5, 275, 512 }, { 4, 276, 512 }, { 5, 277, 512 }, { 5, 278, 512 }, { 6, 279, 512 },
-   { 4, 280, 512 }, { 5, 281, 512 }, { 5, 282, 512 }, { 6, 283, 512 }, { 5, 284, 512 }, { 6, 285, 512 }, { 6, 286, 512 }, { 7, 287, 512 },
-   { 3, 288, 512 }, { 4, 289, 512 }, { 4, 290, 512 }, { 5, 291, 512 }, { 4, 292, 512 }, { 5, 293, 512 }, { 5, 294, 512 }, { 6, 295, 512 },
-   { 4, 296, 512 }, { 5, 297, 512 }, { 5, 298, 512 }, { 6, 299, 512 }, { 5, 300, 512 }, { 6, 301, 512 }, { 6, 302, 512 }, { 7, 303, 512 },
-   { 4, 304, 512 }, { 5, 305, 512 }, { 5, 306, 512 }, { 6, 307, 512 }, { 5, 308, 512 }, { 6, 309, 512 }, { 6, 310, 512 }, { 7, 311, 512 },
-   { 5, 312, 512 }, { 6, 313, 512 }, { 6, 314, 512 }, { 7, 315, 512 }, { 6, 316, 512 }, { 7, 317, 512 }, { 7, 318, 512 }, { 8, 319, 512 },
-   { 3, 320, 512 }, { 4, 321, 512 }, { 4, 322, 512 }, { 5, 323, 512 }, { 4, 324, 512 }, { 5, 325, 512 }, { 5, 326, 512 }, { 6, 327, 512 },
-   { 4, 328, 512 }, { 5, 329, 512 }, { 5, 330, 512 }, { 6, 331, 512 }, { 5, 332, 512 }, { 6, 333, 512 }, { 6, 334, 512 }, { 7, 335, 512 },
-   { 4, 336, 512 }, { 5, 337, 512 }, { 5, 338, 512 }, { 6, 339, 512 }, { 5, 340, 512 }, { 6, 341, 512 }, { 6, 342, 512 }, { 7, 343, 512 },
-   { 5, 344, 512 }, { 6, 345, 512 }, { 6, 346, 512 }, { 7, 347, 512 }, { 6, 348, 512 }, { 7, 349, 512 }, { 7, 350, 512 }, { 8, 351, 512 },
-   { 4, 352, 512 }, { 5, 353, 512 }, { 5, 354, 512 }, { 6, 355, 512 }, { 5, 356, 512 }, { 6, 357, 512 }, { 6, 358, 512 }, { 7, 359, 512 },
-   { 5, 360, 512 }, { 6, 361, 512 }, { 6, 362, 512 }, { 7, 363, 512 }, { 6, 364, 512 }, { 7, 365, 512 }, { 7, 366, 512 }, { 8, 367, 512 },
-   { 5, 368, 512 }, { 6, 369, 512 }, { 6, 370, 512 }, { 7, 371, 512 }, { 6, 372, 512 }, { 7, 373, 512 }, { 7, 374, 512 }, { 8, 375, 512 },
-   { 6, 376, 512 }, { 7, 377, 512 }, { 7, 378, 512 }, { 8, 379, 512 }, { 7, 380, 512 }, { 8, 381, 512 }, { 8, 382, 512 }, { 9, 383, 512 },
-   { 3, 384, 512 }, { 4, 385, 512 }, { 4, 386, 512 }, { 5, 387, 512 }, { 4, 388, 512 }, { 5, 389, 512 }, { 5, 390, 512 }, { 6, 391, 512 },
-   { 4, 392, 512 }, { 5, 393, 512 }, { 5, 394, 512 }, { 6, 395, 512 }, { 5, 396, 512 }, { 6, 397, 512 }, { 6, 398, 512 }, { 7, 399, 512 },
-   { 4, 400, 512 }, { 5, 401, 512 }, { 5, 402, 512 }, { 6, 403, 512 }, { 5, 404, 512 }, { 6, 405, 512 }, { 6, 406, 512 }, { 7, 407, 512 },
-   { 5, 408, 512 }, { 6, 409, 512 }, { 6, 410, 512 }, { 7, 411, 512 }, { 6, 412, 512 }, { 7, 413, 512 }, { 7, 414, 512 }, { 8, 415, 512 },
-   { 4, 416, 512 }, { 5, 417, 512 }, { 5, 418, 512 }, { 6, 419, 512 }, { 5, 420, 512 }, { 6, 421, 512 }, { 6, 422, 512 }, { 7, 423, 512 },
-   { 5, 424, 512 }, { 6, 425, 512 }, { 6, 426, 512 }, { 7, 427, 512 }, { 6, 428, 512 }, { 7, 429, 512 }, { 7, 430, 512 }, { 8, 431, 512 },
-   { 5, 432, 512 }, { 6, 433, 512 }, { 6, 434, 512 }, { 7, 435, 512 }, { 6, 436, 512 }, { 7, 437, 512 }, { 7, 438, 512 }, { 8, 439, 512 },
-   { 6, 440, 512 }, { 7, 441, 512 }, { 7, 442, 512 }, { 8, 443, 512 }, { 7, 444, 512 }, { 8, 445, 512 }, { 8, 446, 512 }, { 9, 447, 512 },
-   { 4, 448, 512 }, { 5, 449, 512 }, { 5, 450, 512 }, { 6, 451, 512 }, { 5, 452, 512 }, { 6, 453, 512 }, { 6, 454, 512 }, { 7, 455, 512 },
-   { 5, 456, 512 }, { 6, 457, 512 }, { 6, 458, 512 }, { 7, 459, 512 }, { 6, 460, 512 }, { 7, 461, 512 }, { 7, 462, 512 }, { 8, 463, 512 },
-   { 5, 464, 512 }, { 6, 465, 512 }, { 6, 466, 512 }, { 7, 467, 512 }, { 6, 468, 512 }, { 7, 469, 512 }, { 7, 470, 512 }, { 8, 471, 512 },
-   { 6, 472, 512 }, { 7, 473, 512 }, { 7, 474, 512 }, { 8, 475, 512 }, { 7, 476, 512 }, { 8, 477, 512 }, { 8, 478, 512 }, { 9, 479, 512 },
-   { 5, 480, 512 }, { 6, 481, 512 }, { 6, 482, 512 }, { 7, 483, 512 }, { 6, 484, 512 }, { 7, 485, 512 }, { 7, 486, 512 }, { 8, 487, 512 },
-   { 6, 488, 512 }, { 7, 489, 512 }, { 7, 490, 512 }, { 8, 491, 512 }, { 7, 492, 512 }, { 8, 493, 512 }, { 8, 494, 512 }, { 9, 495, 512 },
-   { 6, 496, 512 }, { 7, 497, 512 }, { 7, 498, 512 }, { 8, 499, 512 }, { 7, 500, 512 }, { 8, 501, 512 }, { 8, 502, 512 }, { 9, 503, 512 },
-   { 7, 504, 512 }, { 8, 505, 512 }, { 8, 506, 512 }, { 9, 507, 512 }, { 8, 508, 512 }, { 9, 509, 512 }, { 9, 510, 512 }, { 10, 511, 512 },
-#if FP_LUT > 10
-   { 1, 0, 0 }, { 2, 1, 1024 }, { 2, 2, 1024 }, { 3, 3, 1024 }, { 2, 4, 1024 }, { 3, 5, 1024 }, { 3, 6, 1024 }, { 4, 7, 1024 },
-   { 2, 8, 1024 }, { 3, 9, 1024 }, { 3, 10, 1024 }, { 4, 11, 1024 }, { 3, 12, 1024 }, { 4, 13, 1024 }, { 4, 14, 1024 }, { 5, 15, 1024 },
-   { 2, 16, 1024 }, { 3, 17, 1024 }, { 3, 18, 1024 }, { 4, 19, 1024 }, { 3, 20, 1024 }, { 4, 21, 1024 }, { 4, 22, 1024 }, { 5, 23, 1024 },
-   { 3, 24, 1024 }, { 4, 25, 1024 }, { 4, 26, 1024 }, { 5, 27, 1024 }, { 4, 28, 1024 }, { 5, 29, 1024 }, { 5, 30, 1024 }, { 6, 31, 1024 },
-   { 2, 32, 1024 }, { 3, 33, 1024 }, { 3, 34, 1024 }, { 4, 35, 1024 }, { 3, 36, 1024 }, { 4, 37, 1024 }, { 4, 38, 1024 }, { 5, 39, 1024 },
-   { 3, 40, 1024 }, { 4, 41, 1024 }, { 4, 42, 1024 }, { 5, 43, 1024 }, { 4, 44, 1024 }, { 5, 45, 1024 }, { 5, 46, 1024 }, { 6, 47, 1024 },
-   { 3, 48, 1024 }, { 4, 49, 1024 }, { 4, 50, 1024 }, { 5, 51, 1024 }, { 4, 52, 1024 }, { 5, 53, 1024 }, { 5, 54, 1024 }, { 6, 55, 1024 },
-   { 4, 56, 1024 }, { 5, 57, 1024 }, { 5, 58, 1024 }, { 6, 59, 1024 }, { 5, 60, 1024 }, { 6, 61, 1024 }, { 6, 62, 1024 }, { 7, 63, 1024 },
-   { 2, 64, 1024 }, { 3, 65, 1024 }, { 3, 66, 1024 }, { 4, 67, 1024 }, { 3, 68, 1024 }, { 4, 69, 1024 }, { 4, 70, 1024 }, { 5, 71, 1024 },
-   { 3, 72, 1024 }, { 4, 73, 1024 }, { 4, 74, 1024 }, { 5, 75, 1024 }, { 4, 76, 1024 }, { 5, 77, 1024 }, { 5, 78, 1024 }, { 6, 79, 1024 },
-   { 3, 80, 1024 }, { 4, 81, 1024 }, { 4, 82, 1024 }, { 5, 83, 1024 }, { 4, 84, 1024 }, { 5, 85, 1024 }, { 5, 86, 1024 }, { 6, 87, 1024 },
-   { 4, 88, 1024 }, { 5, 89, 1024 }, { 5, 90, 1024 }, { 6, 91, 1024 }, { 5, 92, 1024 }, { 6, 93, 1024 }, { 6, 94, 1024 }, { 7, 95, 1024 },
-   { 3, 96, 1024 }, { 4, 97, 1024 }, { 4, 98, 1024 }, { 5, 99, 1024 }, { 4, 100, 1024 }, { 5, 101, 1024 }, { 5, 102, 1024 }, { 6, 103, 1024 },
-   { 4, 104, 1024 }, { 5, 105, 1024 }, { 5, 106, 1024 }, { 6, 107, 1024 }, { 5, 108, 1024 }, { 6, 109, 1024 }, { 6, 110, 1024 }, { 7, 111, 1024 },
-   { 4, 112, 1024 }, { 5, 113, 1024 }, { 5, 114, 1024 }, { 6, 115, 1024 }, { 5, 116, 1024 }, { 6, 117, 1024 }, { 6, 118, 1024 }, { 7, 119, 1024 },
-   { 5, 120, 1024 }, { 6, 121, 1024 }, { 6, 122, 1024 }, { 7, 123, 1024 }, { 6, 124, 1024 }, { 7, 125, 1024 }, { 7, 126, 1024 }, { 8, 127, 1024 },
-   { 2, 128, 1024 }, { 3, 129, 1024 }, { 3, 130, 1024 }, { 4, 131, 1024 }, { 3, 132, 1024 }, { 4, 133, 1024 }, { 4, 134, 1024 }, { 5, 135, 1024 },
-   { 3, 136, 1024 }, { 4, 137, 1024 }, { 4, 138, 1024 }, { 5, 139, 1024 }, { 4, 140, 1024 }, { 5, 141, 1024 }, { 5, 142, 1024 }, { 6, 143, 1024 },
-   { 3, 144, 1024 }, { 4, 145, 1024 }, { 4, 146, 1024 }, { 5, 147, 1024 }, { 4, 148, 1024 }, { 5, 149, 1024 }, { 5, 150, 1024 }, { 6, 151, 1024 },
-   { 4, 152, 1024 }, { 5, 153, 1024 }, { 5, 154, 1024 }, { 6, 155, 1024 }, { 5, 156, 1024 }, { 6, 157, 1024 }, { 6, 158, 1024 }, { 7, 159, 1024 },
-   { 3, 160, 1024 }, { 4, 161, 1024 }, { 4, 162, 1024 }, { 5, 163, 1024 }, { 4, 164, 1024 }, { 5, 165, 1024 }, { 5, 166, 1024 }, { 6, 167, 1024 },
-   { 4, 168, 1024 }, { 5, 169, 1024 }, { 5, 170, 1024 }, { 6, 171, 1024 }, { 5, 172, 1024 }, { 6, 173, 1024 }, { 6, 174, 1024 }, { 7, 175, 1024 },
-   { 4, 176, 1024 }, { 5, 177, 1024 }, { 5, 178, 1024 }, { 6, 179, 1024 }, { 5, 180, 1024 }, { 6, 181, 1024 }, { 6, 182, 1024 }, { 7, 183, 1024 },
-   { 5, 184, 1024 }, { 6, 185, 1024 }, { 6, 186, 1024 }, { 7, 187, 1024 }, { 6, 188, 1024 }, { 7, 189, 1024 }, { 7, 190, 1024 }, { 8, 191, 1024 },
-   { 3, 192, 1024 }, { 4, 193, 1024 }, { 4, 194, 1024 }, { 5, 195, 1024 }, { 4, 196, 1024 }, { 5, 197, 1024 }, { 5, 198, 1024 }, { 6, 199, 1024 },
-   { 4, 200, 1024 }, { 5, 201, 1024 }, { 5, 202, 1024 }, { 6, 203, 1024 }, { 5, 204, 1024 }, { 6, 205, 1024 }, { 6, 206, 1024 }, { 7, 207, 1024 },
-   { 4, 208, 1024 }, { 5, 209, 1024 }, { 5, 210, 1024 }, { 6, 211, 1024 }, { 5, 212, 1024 }, { 6, 213, 1024 }, { 6, 214, 1024 }, { 7, 215, 1024 },
-   { 5, 216, 1024 }, { 6, 217, 1024 }, { 6, 218, 1024 }, { 7, 219, 1024 }, { 6, 220, 1024 }, { 7, 221, 1024 }, { 7, 222, 1024 }, { 8, 223, 1024 },
-   { 4, 224, 1024 }, { 5, 225, 1024 }, { 5, 226, 1024 }, { 6, 227, 1024 }, { 5, 228, 1024 }, { 6, 229, 1024 }, { 6, 230, 1024 }, { 7, 231, 1024 },
-   { 5, 232, 1024 }, { 6, 233, 1024 }, { 6, 234, 1024 }, { 7, 235, 1024 }, { 6, 236, 1024 }, { 7, 237, 1024 }, { 7, 238, 1024 }, { 8, 239, 1024 },
-   { 5, 240, 1024 }, { 6, 241, 1024 }, { 6, 242, 1024 }, { 7, 243, 1024 }, { 6, 244, 1024 }, { 7, 245, 1024 }, { 7, 246, 1024 }, { 8, 247, 1024 },
-   { 6, 248, 1024 }, { 7, 249, 1024 }, { 7, 250, 1024 }, { 8, 251, 1024 }, { 7, 252, 1024 }, { 8, 253, 1024 }, { 8, 254, 1024 }, { 9, 255, 1024 },
-   { 2, 256, 1024 }, { 3, 257, 1024 }, { 3, 258, 1024 }, { 4, 259, 1024 }, { 3, 260, 1024 }, { 4, 261, 1024 }, { 4, 262, 1024 }, { 5, 263, 1024 },
-   { 3, 264, 1024 }, { 4, 265, 1024 }, { 4, 266, 1024 }, { 5, 267, 1024 }, { 4, 268, 1024 }, { 5, 269, 1024 }, { 5, 270, 1024 }, { 6, 271, 1024 },
-   { 3, 272, 1024 }, { 4, 273, 1024 }, { 4, 274, 1024 }, { 5, 275, 1024 }, { 4, 276, 1024 }, { 5, 277, 1024 }, { 5, 278, 1024 }, { 6, 279, 1024 },
-   { 4, 280, 1024 }, { 5, 281, 1024 }, { 5, 282, 1024 }, { 6, 283, 1024 }, { 5, 284, 1024 }, { 6, 285, 1024 }, { 6, 286, 1024 }, { 7, 287, 1024 },
-   { 3, 288, 1024 }, { 4, 289, 1024 }, { 4, 290, 1024 }, { 5, 291, 1024 }, { 4, 292, 1024 }, { 5, 293, 1024 }, { 5, 294, 1024 }, { 6, 295, 1024 },
-   { 4, 296, 1024 }, { 5, 297, 1024 }, { 5, 298, 1024 }, { 6, 299, 1024 }, { 5, 300, 1024 }, { 6, 301, 1024 }, { 6, 302, 1024 }, { 7, 303, 1024 },
-   { 4, 304, 1024 }, { 5, 305, 1024 }, { 5, 306, 1024 }, { 6, 307, 1024 }, { 5, 308, 1024 }, { 6, 309, 1024 }, { 6, 310, 1024 }, { 7, 311, 1024 },
-   { 5, 312, 1024 }, { 6, 313, 1024 }, { 6, 314, 1024 }, { 7, 315, 1024 }, { 6, 316, 1024 }, { 7, 317, 1024 }, { 7, 318, 1024 }, { 8, 319, 1024 },
-   { 3, 320, 1024 }, { 4, 321, 1024 }, { 4, 322, 1024 }, { 5, 323, 1024 }, { 4, 324, 1024 }, { 5, 325, 1024 }, { 5, 326, 1024 }, { 6, 327, 1024 },
-   { 4, 328, 1024 }, { 5, 329, 1024 }, { 5, 330, 1024 }, { 6, 331, 1024 }, { 5, 332, 1024 }, { 6, 333, 1024 }, { 6, 334, 1024 }, { 7, 335, 1024 },
-   { 4, 336, 1024 }, { 5, 337, 1024 }, { 5, 338, 1024 }, { 6, 339, 1024 }, { 5, 340, 1024 }, { 6, 341, 1024 }, { 6, 342, 1024 }, { 7, 343, 1024 },
-   { 5, 344, 1024 }, { 6, 345, 1024 }, { 6, 346, 1024 }, { 7, 347, 1024 }, { 6, 348, 1024 }, { 7, 349, 1024 }, { 7, 350, 1024 }, { 8, 351, 1024 },
-   { 4, 352, 1024 }, { 5, 353, 1024 }, { 5, 354, 1024 }, { 6, 355, 1024 }, { 5, 356, 1024 }, { 6, 357, 1024 }, { 6, 358, 1024 }, { 7, 359, 1024 },
-   { 5, 360, 1024 }, { 6, 361, 1024 }, { 6, 362, 1024 }, { 7, 363, 1024 }, { 6, 364, 1024 }, { 7, 365, 1024 }, { 7, 366, 1024 }, { 8, 367, 1024 },
-   { 5, 368, 1024 }, { 6, 369, 1024 }, { 6, 370, 1024 }, { 7, 371, 1024 }, { 6, 372, 1024 }, { 7, 373, 1024 }, { 7, 374, 1024 }, { 8, 375, 1024 },
-   { 6, 376, 1024 }, { 7, 377, 1024 }, { 7, 378, 1024 }, { 8, 379, 1024 }, { 7, 380, 1024 }, { 8, 381, 1024 }, { 8, 382, 1024 }, { 9, 383, 1024 },
-   { 3, 384, 1024 }, { 4, 385, 1024 }, { 4, 386, 1024 }, { 5, 387, 1024 }, { 4, 388, 1024 }, { 5, 389, 1024 }, { 5, 390, 1024 }, { 6, 391, 1024 },
-   { 4, 392, 1024 }, { 5, 393, 1024 }, { 5, 394, 1024 }, { 6, 395, 1024 }, { 5, 396, 1024 }, { 6, 397, 1024 }, { 6, 398, 1024 }, { 7, 399, 1024 },
-   { 4, 400, 1024 }, { 5, 401, 1024 }, { 5, 402, 1024 }, { 6, 403, 1024 }, { 5, 404, 1024 }, { 6, 405, 1024 }, { 6, 406, 1024 }, { 7, 407, 1024 },
-   { 5, 408, 1024 }, { 6, 409, 1024 }, { 6, 410, 1024 }, { 7, 411, 1024 }, { 6, 412, 1024 }, { 7, 413, 1024 }, { 7, 414, 1024 }, { 8, 415, 1024 },
-   { 4, 416, 1024 }, { 5, 417, 1024 }, { 5, 418, 1024 }, { 6, 419, 1024 }, { 5, 420, 1024 }, { 6, 421, 1024 }, { 6, 422, 1024 }, { 7, 423, 1024 },
-   { 5, 424, 1024 }, { 6, 425, 1024 }, { 6, 426, 1024 }, { 7, 427, 1024 }, { 6, 428, 1024 }, { 7, 429, 1024 }, { 7, 430, 1024 }, { 8, 431, 1024 },
-   { 5, 432, 1024 }, { 6, 433, 1024 }, { 6, 434, 1024 }, { 7, 435, 1024 }, { 6, 436, 1024 }, { 7, 437, 1024 }, { 7, 438, 1024 }, { 8, 439, 1024 },
-   { 6, 440, 1024 }, { 7, 441, 1024 }, { 7, 442, 1024 }, { 8, 443, 1024 }, { 7, 444, 1024 }, { 8, 445, 1024 }, { 8, 446, 1024 }, { 9, 447, 1024 },
-   { 4, 448, 1024 }, { 5, 449, 1024 }, { 5, 450, 1024 }, { 6, 451, 1024 }, { 5, 452, 1024 }, { 6, 453, 1024 }, { 6, 454, 1024 }, { 7, 455, 1024 },
-   { 5, 456, 1024 }, { 6, 457, 1024 }, { 6, 458, 1024 }, { 7, 459, 1024 }, { 6, 460, 1024 }, { 7, 461, 1024 }, { 7, 462, 1024 }, { 8, 463, 1024 },
-   { 5, 464, 1024 }, { 6, 465, 1024 }, { 6, 466, 1024 }, { 7, 467, 1024 }, { 6, 468, 1024 }, { 7, 469, 1024 }, { 7, 470, 1024 }, { 8, 471, 1024 },
-   { 6, 472, 1024 }, { 7, 473, 1024 }, { 7, 474, 1024 }, { 8, 475, 1024 }, { 7, 476, 1024 }, { 8, 477, 1024 }, { 8, 478, 1024 }, { 9, 479, 1024 },
-   { 5, 480, 1024 }, { 6, 481, 1024 }, { 6, 482, 1024 }, { 7, 483, 1024 }, { 6, 484, 1024 }, { 7, 485, 1024 }, { 7, 486, 1024 }, { 8, 487, 1024 },
-   { 6, 488, 1024 }, { 7, 489, 1024 }, { 7, 490, 1024 }, { 8, 491, 1024 }, { 7, 492, 1024 }, { 8, 493, 1024 }, { 8, 494, 1024 }, { 9, 495, 1024 },
-   { 6, 496, 1024 }, { 7, 497, 1024 }, { 7, 498, 1024 }, { 8, 499, 1024 }, { 7, 500, 1024 }, { 8, 501, 1024 }, { 8, 502, 1024 }, { 9, 503, 1024 },
-   { 7, 504, 1024 }, { 8, 505, 1024 }, { 8, 506, 1024 }, { 9, 507, 1024 }, { 8, 508, 1024 }, { 9, 509, 1024 }, { 9, 510, 1024 }, { 10, 511, 1024 },
-   { 2, 512, 1024 }, { 3, 513, 1024 }, { 3, 514, 1024 }, { 4, 515, 1024 }, { 3, 516, 1024 }, { 4, 517, 1024 }, { 4, 518, 1024 }, { 5, 519, 1024 },
-   { 3, 520, 1024 }, { 4, 521, 1024 }, { 4, 522, 1024 }, { 5, 523, 1024 }, { 4, 524, 1024 }, { 5, 525, 1024 }, { 5, 526, 1024 }, { 6, 527, 1024 },
-   { 3, 528, 1024 }, { 4, 529, 1024 }, { 4, 530, 1024 }, { 5, 531, 1024 }, { 4, 532, 1024 }, { 5, 533, 1024 }, { 5, 534, 1024 }, { 6, 535, 1024 },
-   { 4, 536, 1024 }, { 5, 537, 1024 }, { 5, 538, 1024 }, { 6, 539, 1024 }, { 5, 540, 1024 }, { 6, 541, 1024 }, { 6, 542, 1024 }, { 7, 543, 1024 },
-   { 3, 544, 1024 }, { 4, 545, 1024 }, { 4, 546, 1024 }, { 5, 547, 1024 }, { 4, 548, 1024 }, { 5, 549, 1024 }, { 5, 550, 1024 }, { 6, 551, 1024 },
-   { 4, 552, 1024 }, { 5, 553, 1024 }, { 5, 554, 1024 }, { 6, 555, 1024 }, { 5, 556, 1024 }, { 6, 557, 1024 }, { 6, 558, 1024 }, { 7, 559, 1024 },
-   { 4, 560, 1024 }, { 5, 561, 1024 }, { 5, 562, 1024 }, { 6, 563, 1024 }, { 5, 564, 1024 }, { 6, 565, 1024 }, { 6, 566, 1024 }, { 7, 567, 1024 },
-   { 5, 568, 1024 }, { 6, 569, 1024 }, { 6, 570, 1024 }, { 7, 571, 1024 }, { 6, 572, 1024 }, { 7, 573, 1024 }, { 7, 574, 1024 }, { 8, 575, 1024 },
-   { 3, 576, 1024 }, { 4, 577, 1024 }, { 4, 578, 1024 }, { 5, 579, 1024 }, { 4, 580, 1024 }, { 5, 581, 1024 }, { 5, 582, 1024 }, { 6, 583, 1024 },
-   { 4, 584, 1024 }, { 5, 585, 1024 }, { 5, 586, 1024 }, { 6, 587, 1024 }, { 5, 588, 1024 }, { 6, 589, 1024 }, { 6, 590, 1024 }, { 7, 591, 1024 },
-   { 4, 592, 1024 }, { 5, 593, 1024 }, { 5, 594, 1024 }, { 6, 595, 1024 }, { 5, 596, 1024 }, { 6, 597, 1024 }, { 6, 598, 1024 }, { 7, 599, 1024 },
-   { 5, 600, 1024 }, { 6, 601, 1024 }, { 6, 602, 1024 }, { 7, 603, 1024 }, { 6, 604, 1024 }, { 7, 605, 1024 }, { 7, 606, 1024 }, { 8, 607, 1024 },
-   { 4, 608, 1024 }, { 5, 609, 1024 }, { 5, 610, 1024 }, { 6, 611, 1024 }, { 5, 612, 1024 }, { 6, 613, 1024 }, { 6, 614, 1024 }, { 7, 615, 1024 },
-   { 5, 616, 1024 }, { 6, 617, 1024 }, { 6, 618, 1024 }, { 7, 619, 1024 }, { 6, 620, 1024 }, { 7, 621, 1024 }, { 7, 622, 1024 }, { 8, 623, 1024 },
-   { 5, 624, 1024 }, { 6, 625, 1024 }, { 6, 626, 1024 }, { 7, 627, 1024 }, { 6, 628, 1024 }, { 7, 629, 1024 }, { 7, 630, 1024 }, { 8, 631, 1024 },
-   { 6, 632, 1024 }, { 7, 633, 1024 }, { 7, 634, 1024 }, { 8, 635, 1024 }, { 7, 636, 1024 }, { 8, 637, 1024 }, { 8, 638, 1024 }, { 9, 639, 1024 },
-   { 3, 640, 1024 }, { 4, 641, 1024 }, { 4, 642, 1024 }, { 5, 643, 1024 }, { 4, 644, 1024 }, { 5, 645, 1024 }, { 5, 646, 1024 }, { 6, 647, 1024 },
-   { 4, 648, 1024 }, { 5, 649, 1024 }, { 5, 650, 1024 }, { 6, 651, 1024 }, { 5, 652, 1024 }, { 6, 653, 1024 }, { 6, 654, 1024 }, { 7, 655, 1024 },
-   { 4, 656, 1024 }, { 5, 657, 1024 }, { 5, 658, 1024 }, { 6, 659, 1024 }, { 5, 660, 1024 }, { 6, 661, 1024 }, { 6, 662, 1024 }, { 7, 663, 1024 },
-   { 5, 664, 1024 }, { 6, 665, 1024 }, { 6, 666, 1024 }, { 7, 667, 1024 }, { 6, 668, 1024 }, { 7, 669, 1024 }, { 7, 670, 1024 }, { 8, 671, 1024 },
-   { 4, 672, 1024 }, { 5, 673, 1024 }, { 5, 674, 1024 }, { 6, 675, 1024 }, { 5, 676, 1024 }, { 6, 677, 1024 }, { 6, 678, 1024 }, { 7, 679, 1024 },
-   { 5, 680, 1024 }, { 6, 681, 1024 }, { 6, 682, 1024 }, { 7, 683, 1024 }, { 6, 684, 1024 }, { 7, 685, 1024 }, { 7, 686, 1024 }, { 8, 687, 1024 },
-   { 5, 688, 1024 }, { 6, 689, 1024 }, { 6, 690, 1024 }, { 7, 691, 1024 }, { 6, 692, 1024 }, { 7, 693, 1024 }, { 7, 694, 1024 }, { 8, 695, 1024 },
-   { 6, 696, 1024 }, { 7, 697, 1024 }, { 7, 698, 1024 }, { 8, 699, 1024 }, { 7, 700, 1024 }, { 8, 701, 1024 }, { 8, 702, 1024 }, { 9, 703, 1024 },
-   { 4, 704, 1024 }, { 5, 705, 1024 }, { 5, 706, 1024 }, { 6, 707, 1024 }, { 5, 708, 1024 }, { 6, 709, 1024 }, { 6, 710, 1024 }, { 7, 711, 1024 },
-   { 5, 712, 1024 }, { 6, 713, 1024 }, { 6, 714, 1024 }, { 7, 715, 1024 }, { 6, 716, 1024 }, { 7, 717, 1024 }, { 7, 718, 1024 }, { 8, 719, 1024 },
-   { 5, 720, 1024 }, { 6, 721, 1024 }, { 6, 722, 1024 }, { 7, 723, 1024 }, { 6, 724, 1024 }, { 7, 725, 1024 }, { 7, 726, 1024 }, { 8, 727, 1024 },
-   { 6, 728, 1024 }, { 7, 729, 1024 }, { 7, 730, 1024 }, { 8, 731, 1024 }, { 7, 732, 1024 }, { 8, 733, 1024 }, { 8, 734, 1024 }, { 9, 735, 1024 },
-   { 5, 736, 1024 }, { 6, 737, 1024 }, { 6, 738, 1024 }, { 7, 739, 1024 }, { 6, 740, 1024 }, { 7, 741, 1024 }, { 7, 742, 1024 }, { 8, 743, 1024 },
-   { 6, 744, 1024 }, { 7, 745, 1024 }, { 7, 746, 1024 }, { 8, 747, 1024 }, { 7, 748, 1024 }, { 8, 749, 1024 }, { 8, 750, 1024 }, { 9, 751, 1024 },
-   { 6, 752, 1024 }, { 7, 753, 1024 }, { 7, 754, 1024 }, { 8, 755, 1024 }, { 7, 756, 1024 }, { 8, 757, 1024 }, { 8, 758, 1024 }, { 9, 759, 1024 },
-   { 7, 760, 1024 }, { 8, 761, 1024 }, { 8, 762, 1024 }, { 9, 763, 1024 }, { 8, 764, 1024 }, { 9, 765, 1024 }, { 9, 766, 1024 }, { 10, 767, 1024 },
-   { 3, 768, 1024 }, { 4, 769, 1024 }, { 4, 770, 1024 }, { 5, 771, 1024 }, { 4, 772, 1024 }, { 5, 773, 1024 }, { 5, 774, 1024 }, { 6, 775, 1024 },
-   { 4, 776, 1024 }, { 5, 777, 1024 }, { 5, 778, 1024 }, { 6, 779, 1024 }, { 5, 780, 1024 }, { 6, 781, 1024 }, { 6, 782, 1024 }, { 7, 783, 1024 },
-   { 4, 784, 1024 }, { 5, 785, 1024 }, { 5, 786, 1024 }, { 6, 787, 1024 }, { 5, 788, 1024 }, { 6, 789, 1024 }, { 6, 790, 1024 }, { 7, 791, 1024 },
-   { 5, 792, 1024 }, { 6, 793, 1024 }, { 6, 794, 1024 }, { 7, 795, 1024 }, { 6, 796, 1024 }, { 7, 797, 1024 }, { 7, 798, 1024 }, { 8, 799, 1024 },
-   { 4, 800, 1024 }, { 5, 801, 1024 }, { 5, 802, 1024 }, { 6, 803, 1024 }, { 5, 804, 1024 }, { 6, 805, 1024 }, { 6, 806, 1024 }, { 7, 807, 1024 },
-   { 5, 808, 1024 }, { 6, 809, 1024 }, { 6, 810, 1024 }, { 7, 811, 1024 }, { 6, 812, 1024 }, { 7, 813, 1024 }, { 7, 814, 1024 }, { 8, 815, 1024 },
-   { 5, 816, 1024 }, { 6, 817, 1024 }, { 6, 818, 1024 }, { 7, 819, 1024 }, { 6, 820, 1024 }, { 7, 821, 1024 }, { 7, 822, 1024 }, { 8, 823, 1024 },
-   { 6, 824, 1024 }, { 7, 825, 1024 }, { 7, 826, 1024 }, { 8, 827, 1024 }, { 7, 828, 1024 }, { 8, 829, 1024 }, { 8, 830, 1024 }, { 9, 831, 1024 },
-   { 4, 832, 1024 }, { 5, 833, 1024 }, { 5, 834, 1024 }, { 6, 835, 1024 }, { 5, 836, 1024 }, { 6, 837, 1024 }, { 6, 838, 1024 }, { 7, 839, 1024 },
-   { 5, 840, 1024 }, { 6, 841, 1024 }, { 6, 842, 1024 }, { 7, 843, 1024 }, { 6, 844, 1024 }, { 7, 845, 1024 }, { 7, 846, 1024 }, { 8, 847, 1024 },
-   { 5, 848, 1024 }, { 6, 849, 1024 }, { 6, 850, 1024 }, { 7, 851, 1024 }, { 6, 852, 1024 }, { 7, 853, 1024 }, { 7, 854, 1024 }, { 8, 855, 1024 },
-   { 6, 856, 1024 }, { 7, 857, 1024 }, { 7, 858, 1024 }, { 8, 859, 1024 }, { 7, 860, 1024 }, { 8, 861, 1024 }, { 8, 862, 1024 }, { 9, 863, 1024 },
-   { 5, 864, 1024 }, { 6, 865, 1024 }, { 6, 866, 1024 }, { 7, 867, 1024 }, { 6, 868, 1024 }, { 7, 869, 1024 }, { 7, 870, 1024 }, { 8, 871, 1024 },
-   { 6, 872, 1024 }, { 7, 873, 1024 }, { 7, 874, 1024 }, { 8, 875, 1024 }, { 7, 876, 1024 }, { 8, 877, 1024 }, { 8, 878, 1024 }, { 9, 879, 1024 },
-   { 6, 880, 1024 }, { 7, 881, 1024 }, { 7, 882, 1024 }, { 8, 883, 1024 }, { 7, 884, 1024 }, { 8, 885, 1024 }, { 8, 886, 1024 }, { 9, 887, 1024 },
-   { 7, 888, 1024 }, { 8, 889, 1024 }, { 8, 890, 1024 }, { 9, 891, 1024 }, { 8, 892, 1024 }, { 9, 893, 1024 }, { 9, 894, 1024 }, { 10, 895, 1024 },
-   { 4, 896, 1024 }, { 5, 897, 1024 }, { 5, 898, 1024 }, { 6, 899, 1024 }, { 5, 900, 1024 }, { 6, 901, 1024 }, { 6, 902, 1024 }, { 7, 903, 1024 },
-   { 5, 904, 1024 }, { 6, 905, 1024 }, { 6, 906, 1024 }, { 7, 907, 1024 }, { 6, 908, 1024 }, { 7, 909, 1024 }, { 7, 910, 1024 }, { 8, 911, 1024 },
-   { 5, 912, 1024 }, { 6, 913, 1024 }, { 6, 914, 1024 }, { 7, 915, 1024 }, { 6, 916, 1024 }, { 7, 917, 1024 }, { 7, 918, 1024 }, { 8, 919, 1024 },
-   { 6, 920, 1024 }, { 7, 921, 1024 }, { 7, 922, 1024 }, { 8, 923, 1024 }, { 7, 924, 1024 }, { 8, 925, 1024 }, { 8, 926, 1024 }, { 9, 927, 1024 },
-   { 5, 928, 1024 }, { 6, 929, 1024 }, { 6, 930, 1024 }, { 7, 931, 1024 }, { 6, 932, 1024 }, { 7, 933, 1024 }, { 7, 934, 1024 }, { 8, 935, 1024 },
-   { 6, 936, 1024 }, { 7, 937, 1024 }, { 7, 938, 1024 }, { 8, 939, 1024 }, { 7, 940, 1024 }, { 8, 941, 1024 }, { 8, 942, 1024 }, { 9, 943, 1024 },
-   { 6, 944, 1024 }, { 7, 945, 1024 }, { 7, 946, 1024 }, { 8, 947, 1024 }, { 7, 948, 1024 }, { 8, 949, 1024 }, { 8, 950, 1024 }, { 9, 951, 1024 },
-   { 7, 952, 1024 }, { 8, 953, 1024 }, { 8, 954, 1024 }, { 9, 955, 1024 }, { 8, 956, 1024 }, { 9, 957, 1024 }, { 9, 958, 1024 }, { 10, 959, 1024 },
-   { 5, 960, 1024 }, { 6, 961, 1024 }, { 6, 962, 1024 }, { 7, 963, 1024 }, { 6, 964, 1024 }, { 7, 965, 1024 }, { 7, 966, 1024 }, { 8, 967, 1024 },
-   { 6, 968, 1024 }, { 7, 969, 1024 }, { 7, 970, 1024 }, { 8, 971, 1024 }, { 7, 972, 1024 }, { 8, 973, 1024 }, { 8, 974, 1024 }, { 9, 975, 1024 },
-   { 6, 976, 1024 }, { 7, 977, 1024 }, { 7, 978, 1024 }, { 8, 979, 1024 }, { 7, 980, 1024 }, { 8, 981, 1024 }, { 8, 982, 1024 }, { 9, 983, 1024 },
-   { 7, 984, 1024 }, { 8, 985, 1024 }, { 8, 986, 1024 }, { 9, 987, 1024 }, { 8, 988, 1024 }, { 9, 989, 1024 }, { 9, 990, 1024 }, { 10, 991, 1024 },
-   { 6, 992, 1024 }, { 7, 993, 1024 }, { 7, 994, 1024 }, { 8, 995, 1024 }, { 7, 996, 1024 }, { 8, 997, 1024 }, { 8, 998, 1024 }, { 9, 999, 1024 },
-   { 7, 1000, 1024 }, { 8, 1001, 1024 }, { 8, 1002, 1024 }, { 9, 1003, 1024 }, { 8, 1004, 1024 }, { 9, 1005, 1024 }, { 9, 1006, 1024 }, { 10, 1007, 1024 },
-   { 7, 1008, 1024 }, { 8, 1009, 1024 }, { 8, 1010, 1024 }, { 9, 1011, 1024 }, { 8, 1012, 1024 }, { 9, 1013, 1024 }, { 9, 1014, 1024 }, { 10, 1015, 1024 },
-   { 8, 1016, 1024 }, { 9, 1017, 1024 }, { 9, 1018, 1024 }, { 10, 1019, 1024 }, { 9, 1020, 1024 }, { 10, 1021, 1024 }, { 10, 1022, 1024 }, { 11, 1023, 1024 },
-#if FP_LUT > 11
-   { 1, 0, 0 }, { 2, 1, 2048 }, { 2, 2, 2048 }, { 3, 3, 2048 }, { 2, 4, 2048 }, { 3, 5, 2048 }, { 3, 6, 2048 }, { 4, 7, 2048 },
-   { 2, 8, 2048 }, { 3, 9, 2048 }, { 3, 10, 2048 }, { 4, 11, 2048 }, { 3, 12, 2048 }, { 4, 13, 2048 }, { 4, 14, 2048 }, { 5, 15, 2048 },
-   { 2, 16, 2048 }, { 3, 17, 2048 }, { 3, 18, 2048 }, { 4, 19, 2048 }, { 3, 20, 2048 }, { 4, 21, 2048 }, { 4, 22, 2048 }, { 5, 23, 2048 },
-   { 3, 24, 2048 }, { 4, 25, 2048 }, { 4, 26, 2048 }, { 5, 27, 2048 }, { 4, 28, 2048 }, { 5, 29, 2048 }, { 5, 30, 2048 }, { 6, 31, 2048 },
-   { 2, 32, 2048 }, { 3, 33, 2048 }, { 3, 34, 2048 }, { 4, 35, 2048 }, { 3, 36, 2048 }, { 4, 37, 2048 }, { 4, 38, 2048 }, { 5, 39, 2048 },
-   { 3, 40, 2048 }, { 4, 41, 2048 }, { 4, 42, 2048 }, { 5, 43, 2048 }, { 4, 44, 2048 }, { 5, 45, 2048 }, { 5, 46, 2048 }, { 6, 47, 2048 },
-   { 3, 48, 2048 }, { 4, 49, 2048 }, { 4, 50, 2048 }, { 5, 51, 2048 }, { 4, 52, 2048 }, { 5, 53, 2048 }, { 5, 54, 2048 }, { 6, 55, 2048 },
-   { 4, 56, 2048 }, { 5, 57, 2048 }, { 5, 58, 2048 }, { 6, 59, 2048 }, { 5, 60, 2048 }, { 6, 61, 2048 }, { 6, 62, 2048 }, { 7, 63, 2048 },
-   { 2, 64, 2048 }, { 3, 65, 2048 }, { 3, 66, 2048 }, { 4, 67, 2048 }, { 3, 68, 2048 }, { 4, 69, 2048 }, { 4, 70, 2048 }, { 5, 71, 2048 },
-   { 3, 72, 2048 }, { 4, 73, 2048 }, { 4, 74, 2048 }, { 5, 75, 2048 }, { 4, 76, 2048 }, { 5, 77, 2048 }, { 5, 78, 2048 }, { 6, 79, 2048 },
-   { 3, 80, 2048 }, { 4, 81, 2048 }, { 4, 82, 2048 }, { 5, 83, 2048 }, { 4, 84, 2048 }, { 5, 85, 2048 }, { 5, 86, 2048 }, { 6, 87, 2048 },
-   { 4, 88, 2048 }, { 5, 89, 2048 }, { 5, 90, 2048 }, { 6, 91, 2048 }, { 5, 92, 2048 }, { 6, 93, 2048 }, { 6, 94, 2048 }, { 7, 95, 2048 },
-   { 3, 96, 2048 }, { 4, 97, 2048 }, { 4, 98, 2048 }, { 5, 99, 2048 }, { 4, 100, 2048 }, { 5, 101, 2048 }, { 5, 102, 2048 }, { 6, 103, 2048 },
-   { 4, 104, 2048 }, { 5, 105, 2048 }, { 5, 106, 2048 }, { 6, 107, 2048 }, { 5, 108, 2048 }, { 6, 109, 2048 }, { 6, 110, 2048 }, { 7, 111, 2048 },
-   { 4, 112, 2048 }, { 5, 113, 2048 }, { 5, 114, 2048 }, { 6, 115, 2048 }, { 5, 116, 2048 }, { 6, 117, 2048 }, { 6, 118, 2048 }, { 7, 119, 2048 },
-   { 5, 120, 2048 }, { 6, 121, 2048 }, { 6, 122, 2048 }, { 7, 123, 2048 }, { 6, 124, 2048 }, { 7, 125, 2048 }, { 7, 126, 2048 }, { 8, 127, 2048 },
-   { 2, 128, 2048 }, { 3, 129, 2048 }, { 3, 130, 2048 }, { 4, 131, 2048 }, { 3, 132, 2048 }, { 4, 133, 2048 }, { 4, 134, 2048 }, { 5, 135, 2048 },
-   { 3, 136, 2048 }, { 4, 137, 2048 }, { 4, 138, 2048 }, { 5, 139, 2048 }, { 4, 140, 2048 }, { 5, 141, 2048 }, { 5, 142, 2048 }, { 6, 143, 2048 },
-   { 3, 144, 2048 }, { 4, 145, 2048 }, { 4, 146, 2048 }, { 5, 147, 2048 }, { 4, 148, 2048 }, { 5, 149, 2048 }, { 5, 150, 2048 }, { 6, 151, 2048 },
-   { 4, 152, 2048 }, { 5, 153, 2048 }, { 5, 154, 2048 }, { 6, 155, 2048 }, { 5, 156, 2048 }, { 6, 157, 2048 }, { 6, 158, 2048 }, { 7, 159, 2048 },
-   { 3, 160, 2048 }, { 4, 161, 2048 }, { 4, 162, 2048 }, { 5, 163, 2048 }, { 4, 164, 2048 }, { 5, 165, 2048 }, { 5, 166, 2048 }, { 6, 167, 2048 },
-   { 4, 168, 2048 }, { 5, 169, 2048 }, { 5, 170, 2048 }, { 6, 171, 2048 }, { 5, 172, 2048 }, { 6, 173, 2048 }, { 6, 174, 2048 }, { 7, 175, 2048 },
-   { 4, 176, 2048 }, { 5, 177, 2048 }, { 5, 178, 2048 }, { 6, 179, 2048 }, { 5, 180, 2048 }, { 6, 181, 2048 }, { 6, 182, 2048 }, { 7, 183, 2048 },
-   { 5, 184, 2048 }, { 6, 185, 2048 }, { 6, 186, 2048 }, { 7, 187, 2048 }, { 6, 188, 2048 }, { 7, 189, 2048 }, { 7, 190, 2048 }, { 8, 191, 2048 },
-   { 3, 192, 2048 }, { 4, 193, 2048 }, { 4, 194, 2048 }, { 5, 195, 2048 }, { 4, 196, 2048 }, { 5, 197, 2048 }, { 5, 198, 2048 }, { 6, 199, 2048 },
-   { 4, 200, 2048 }, { 5, 201, 2048 }, { 5, 202, 2048 }, { 6, 203, 2048 }, { 5, 204, 2048 }, { 6, 205, 2048 }, { 6, 206, 2048 }, { 7, 207, 2048 },
-   { 4, 208, 2048 }, { 5, 209, 2048 }, { 5, 210, 2048 }, { 6, 211, 2048 }, { 5, 212, 2048 }, { 6, 213, 2048 }, { 6, 214, 2048 }, { 7, 215, 2048 },
-   { 5, 216, 2048 }, { 6, 217, 2048 }, { 6, 218, 2048 }, { 7, 219, 2048 }, { 6, 220, 2048 }, { 7, 221, 2048 }, { 7, 222, 2048 }, { 8, 223, 2048 },
-   { 4, 224, 2048 }, { 5, 225, 2048 }, { 5, 226, 2048 }, { 6, 227, 2048 }, { 5, 228, 2048 }, { 6, 229, 2048 }, { 6, 230, 2048 }, { 7, 231, 2048 },
-   { 5, 232, 2048 }, { 6, 233, 2048 }, { 6, 234, 2048 }, { 7, 235, 2048 }, { 6, 236, 2048 }, { 7, 237, 2048 }, { 7, 238, 2048 }, { 8, 239, 2048 },
-   { 5, 240, 2048 }, { 6, 241, 2048 }, { 6, 242, 2048 }, { 7, 243, 2048 }, { 6, 244, 2048 }, { 7, 245, 2048 }, { 7, 246, 2048 }, { 8, 247, 2048 },
-   { 6, 248, 2048 }, { 7, 249, 2048 }, { 7, 250, 2048 }, { 8, 251, 2048 }, { 7, 252, 2048 }, { 8, 253, 2048 }, { 8, 254, 2048 }, { 9, 255, 2048 },
-   { 2, 256, 2048 }, { 3, 257, 2048 }, { 3, 258, 2048 }, { 4, 259, 2048 }, { 3, 260, 2048 }, { 4, 261, 2048 }, { 4, 262, 2048 }, { 5, 263, 2048 },
-   { 3, 264, 2048 }, { 4, 265, 2048 }, { 4, 266, 2048 }, { 5, 267, 2048 }, { 4, 268, 2048 }, { 5, 269, 2048 }, { 5, 270, 2048 }, { 6, 271, 2048 },
-   { 3, 272, 2048 }, { 4, 273, 2048 }, { 4, 274, 2048 }, { 5, 275, 2048 }, { 4, 276, 2048 }, { 5, 277, 2048 }, { 5, 278, 2048 }, { 6, 279, 2048 },
-   { 4, 280, 2048 }, { 5, 281, 2048 }, { 5, 282, 2048 }, { 6, 283, 2048 }, { 5, 284, 2048 }, { 6, 285, 2048 }, { 6, 286, 2048 }, { 7, 287, 2048 },
-   { 3, 288, 2048 }, { 4, 289, 2048 }, { 4, 290, 2048 }, { 5, 291, 2048 }, { 4, 292, 2048 }, { 5, 293, 2048 }, { 5, 294, 2048 }, { 6, 295, 2048 },
-   { 4, 296, 2048 }, { 5, 297, 2048 }, { 5, 298, 2048 }, { 6, 299, 2048 }, { 5, 300, 2048 }, { 6, 301, 2048 }, { 6, 302, 2048 }, { 7, 303, 2048 },
-   { 4, 304, 2048 }, { 5, 305, 2048 }, { 5, 306, 2048 }, { 6, 307, 2048 }, { 5, 308, 2048 }, { 6, 309, 2048 }, { 6, 310, 2048 }, { 7, 311, 2048 },
-   { 5, 312, 2048 }, { 6, 313, 2048 }, { 6, 314, 2048 }, { 7, 315, 2048 }, { 6, 316, 2048 }, { 7, 317, 2048 }, { 7, 318, 2048 }, { 8, 319, 2048 },
-   { 3, 320, 2048 }, { 4, 321, 2048 }, { 4, 322, 2048 }, { 5, 323, 2048 }, { 4, 324, 2048 }, { 5, 325, 2048 }, { 5, 326, 2048 }, { 6, 327, 2048 },
-   { 4, 328, 2048 }, { 5, 329, 2048 }, { 5, 330, 2048 }, { 6, 331, 2048 }, { 5, 332, 2048 }, { 6, 333, 2048 }, { 6, 334, 2048 }, { 7, 335, 2048 },
-   { 4, 336, 2048 }, { 5, 337, 2048 }, { 5, 338, 2048 }, { 6, 339, 2048 }, { 5, 340, 2048 }, { 6, 341, 2048 }, { 6, 342, 2048 }, { 7, 343, 2048 },
-   { 5, 344, 2048 }, { 6, 345, 2048 }, { 6, 346, 2048 }, { 7, 347, 2048 }, { 6, 348, 2048 }, { 7, 349, 2048 }, { 7, 350, 2048 }, { 8, 351, 2048 },
-   { 4, 352, 2048 }, { 5, 353, 2048 }, { 5, 354, 2048 }, { 6, 355, 2048 }, { 5, 356, 2048 }, { 6, 357, 2048 }, { 6, 358, 2048 }, { 7, 359, 2048 },
-   { 5, 360, 2048 }, { 6, 361, 2048 }, { 6, 362, 2048 }, { 7, 363, 2048 }, { 6, 364, 2048 }, { 7, 365, 2048 }, { 7, 366, 2048 }, { 8, 367, 2048 },
-   { 5, 368, 2048 }, { 6, 369, 2048 }, { 6, 370, 2048 }, { 7, 371, 2048 }, { 6, 372, 2048 }, { 7, 373, 2048 }, { 7, 374, 2048 }, { 8, 375, 2048 },
-   { 6, 376, 2048 }, { 7, 377, 2048 }, { 7, 378, 2048 }, { 8, 379, 2048 }, { 7, 380, 2048 }, { 8, 381, 2048 }, { 8, 382, 2048 }, { 9, 383, 2048 },
-   { 3, 384, 2048 }, { 4, 385, 2048 }, { 4, 386, 2048 }, { 5, 387, 2048 }, { 4, 388, 2048 }, { 5, 389, 2048 }, { 5, 390, 2048 }, { 6, 391, 2048 },
-   { 4, 392, 2048 }, { 5, 393, 2048 }, { 5, 394, 2048 }, { 6, 395, 2048 }, { 5, 396, 2048 }, { 6, 397, 2048 }, { 6, 398, 2048 }, { 7, 399, 2048 },
-   { 4, 400, 2048 }, { 5, 401, 2048 }, { 5, 402, 2048 }, { 6, 403, 2048 }, { 5, 404, 2048 }, { 6, 405, 2048 }, { 6, 406, 2048 }, { 7, 407, 2048 },
-   { 5, 408, 2048 }, { 6, 409, 2048 }, { 6, 410, 2048 }, { 7, 411, 2048 }, { 6, 412, 2048 }, { 7, 413, 2048 }, { 7, 414, 2048 }, { 8, 415, 2048 },
-   { 4, 416, 2048 }, { 5, 417, 2048 }, { 5, 418, 2048 }, { 6, 419, 2048 }, { 5, 420, 2048 }, { 6, 421, 2048 }, { 6, 422, 2048 }, { 7, 423, 2048 },
-   { 5, 424, 2048 }, { 6, 425, 2048 }, { 6, 426, 2048 }, { 7, 427, 2048 }, { 6, 428, 2048 }, { 7, 429, 2048 }, { 7, 430, 2048 }, { 8, 431, 2048 },
-   { 5, 432, 2048 }, { 6, 433, 2048 }, { 6, 434, 2048 }, { 7, 435, 2048 }, { 6, 436, 2048 }, { 7, 437, 2048 }, { 7, 438, 2048 }, { 8, 439, 2048 },
-   { 6, 440, 2048 }, { 7, 441, 2048 }, { 7, 442, 2048 }, { 8, 443, 2048 }, { 7, 444, 2048 }, { 8, 445, 2048 }, { 8, 446, 2048 }, { 9, 447, 2048 },
-   { 4, 448, 2048 }, { 5, 449, 2048 }, { 5, 450, 2048 }, { 6, 451, 2048 }, { 5, 452, 2048 }, { 6, 453, 2048 }, { 6, 454, 2048 }, { 7, 455, 2048 },
-   { 5, 456, 2048 }, { 6, 457, 2048 }, { 6, 458, 2048 }, { 7, 459, 2048 }, { 6, 460, 2048 }, { 7, 461, 2048 }, { 7, 462, 2048 }, { 8, 463, 2048 },
-   { 5, 464, 2048 }, { 6, 465, 2048 }, { 6, 466, 2048 }, { 7, 467, 2048 }, { 6, 468, 2048 }, { 7, 469, 2048 }, { 7, 470, 2048 }, { 8, 471, 2048 },
-   { 6, 472, 2048 }, { 7, 473, 2048 }, { 7, 474, 2048 }, { 8, 475, 2048 }, { 7, 476, 2048 }, { 8, 477, 2048 }, { 8, 478, 2048 }, { 9, 479, 2048 },
-   { 5, 480, 2048 }, { 6, 481, 2048 }, { 6, 482, 2048 }, { 7, 483, 2048 }, { 6, 484, 2048 }, { 7, 485, 2048 }, { 7, 486, 2048 }, { 8, 487, 2048 },
-   { 6, 488, 2048 }, { 7, 489, 2048 }, { 7, 490, 2048 }, { 8, 491, 2048 }, { 7, 492, 2048 }, { 8, 493, 2048 }, { 8, 494, 2048 }, { 9, 495, 2048 },
-   { 6, 496, 2048 }, { 7, 497, 2048 }, { 7, 498, 2048 }, { 8, 499, 2048 }, { 7, 500, 2048 }, { 8, 501, 2048 }, { 8, 502, 2048 }, { 9, 503, 2048 },
-   { 7, 504, 2048 }, { 8, 505, 2048 }, { 8, 506, 2048 }, { 9, 507, 2048 }, { 8, 508, 2048 }, { 9, 509, 2048 }, { 9, 510, 2048 }, { 10, 511, 2048 },
-   { 2, 512, 2048 }, { 3, 513, 2048 }, { 3, 514, 2048 }, { 4, 515, 2048 }, { 3, 516, 2048 }, { 4, 517, 2048 }, { 4, 518, 2048 }, { 5, 519, 2048 },
-   { 3, 520, 2048 }, { 4, 521, 2048 }, { 4, 522, 2048 }, { 5, 523, 2048 }, { 4, 524, 2048 }, { 5, 525, 2048 }, { 5, 526, 2048 }, { 6, 527, 2048 },
-   { 3, 528, 2048 }, { 4, 529, 2048 }, { 4, 530, 2048 }, { 5, 531, 2048 }, { 4, 532, 2048 }, { 5, 533, 2048 }, { 5, 534, 2048 }, { 6, 535, 2048 },
-   { 4, 536, 2048 }, { 5, 537, 2048 }, { 5, 538, 2048 }, { 6, 539, 2048 }, { 5, 540, 2048 }, { 6, 541, 2048 }, { 6, 542, 2048 }, { 7, 543, 2048 },
-   { 3, 544, 2048 }, { 4, 545, 2048 }, { 4, 546, 2048 }, { 5, 547, 2048 }, { 4, 548, 2048 }, { 5, 549, 2048 }, { 5, 550, 2048 }, { 6, 551, 2048 },
-   { 4, 552, 2048 }, { 5, 553, 2048 }, { 5, 554, 2048 }, { 6, 555, 2048 }, { 5, 556, 2048 }, { 6, 557, 2048 }, { 6, 558, 2048 }, { 7, 559, 2048 },
-   { 4, 560, 2048 }, { 5, 561, 2048 }, { 5, 562, 2048 }, { 6, 563, 2048 }, { 5, 564, 2048 }, { 6, 565, 2048 }, { 6, 566, 2048 }, { 7, 567, 2048 },
-   { 5, 568, 2048 }, { 6, 569, 2048 }, { 6, 570, 2048 }, { 7, 571, 2048 }, { 6, 572, 2048 }, { 7, 573, 2048 }, { 7, 574, 2048 }, { 8, 575, 2048 },
-   { 3, 576, 2048 }, { 4, 577, 2048 }, { 4, 578, 2048 }, { 5, 579, 2048 }, { 4, 580, 2048 }, { 5, 581, 2048 }, { 5, 582, 2048 }, { 6, 583, 2048 },
-   { 4, 584, 2048 }, { 5, 585, 2048 }, { 5, 586, 2048 }, { 6, 587, 2048 }, { 5, 588, 2048 }, { 6, 589, 2048 }, { 6, 590, 2048 }, { 7, 591, 2048 },
-   { 4, 592, 2048 }, { 5, 593, 2048 }, { 5, 594, 2048 }, { 6, 595, 2048 }, { 5, 596, 2048 }, { 6, 597, 2048 }, { 6, 598, 2048 }, { 7, 599, 2048 },
-   { 5, 600, 2048 }, { 6, 601, 2048 }, { 6, 602, 2048 }, { 7, 603, 2048 }, { 6, 604, 2048 }, { 7, 605, 2048 }, { 7, 606, 2048 }, { 8, 607, 2048 },
-   { 4, 608, 2048 }, { 5, 609, 2048 }, { 5, 610, 2048 }, { 6, 611, 2048 }, { 5, 612, 2048 }, { 6, 613, 2048 }, { 6, 614, 2048 }, { 7, 615, 2048 },
-   { 5, 616, 2048 }, { 6, 617, 2048 }, { 6, 618, 2048 }, { 7, 619, 2048 }, { 6, 620, 2048 }, { 7, 621, 2048 }, { 7, 622, 2048 }, { 8, 623, 2048 },
-   { 5, 624, 2048 }, { 6, 625, 2048 }, { 6, 626, 2048 }, { 7, 627, 2048 }, { 6, 628, 2048 }, { 7, 629, 2048 }, { 7, 630, 2048 }, { 8, 631, 2048 },
-   { 6, 632, 2048 }, { 7, 633, 2048 }, { 7, 634, 2048 }, { 8, 635, 2048 }, { 7, 636, 2048 }, { 8, 637, 2048 }, { 8, 638, 2048 }, { 9, 639, 2048 },
-   { 3, 640, 2048 }, { 4, 641, 2048 }, { 4, 642, 2048 }, { 5, 643, 2048 }, { 4, 644, 2048 }, { 5, 645, 2048 }, { 5, 646, 2048 }, { 6, 647, 2048 },
-   { 4, 648, 2048 }, { 5, 649, 2048 }, { 5, 650, 2048 }, { 6, 651, 2048 }, { 5, 652, 2048 }, { 6, 653, 2048 }, { 6, 654, 2048 }, { 7, 655, 2048 },
-   { 4, 656, 2048 }, { 5, 657, 2048 }, { 5, 658, 2048 }, { 6, 659, 2048 }, { 5, 660, 2048 }, { 6, 661, 2048 }, { 6, 662, 2048 }, { 7, 663, 2048 },
-   { 5, 664, 2048 }, { 6, 665, 2048 }, { 6, 666, 2048 }, { 7, 667, 2048 }, { 6, 668, 2048 }, { 7, 669, 2048 }, { 7, 670, 2048 }, { 8, 671, 2048 },
-   { 4, 672, 2048 }, { 5, 673, 2048 }, { 5, 674, 2048 }, { 6, 675, 2048 }, { 5, 676, 2048 }, { 6, 677, 2048 }, { 6, 678, 2048 }, { 7, 679, 2048 },
-   { 5, 680, 2048 }, { 6, 681, 2048 }, { 6, 682, 2048 }, { 7, 683, 2048 }, { 6, 684, 2048 }, { 7, 685, 2048 }, { 7, 686, 2048 }, { 8, 687, 2048 },
-   { 5, 688, 2048 }, { 6, 689, 2048 }, { 6, 690, 2048 }, { 7, 691, 2048 }, { 6, 692, 2048 }, { 7, 693, 2048 }, { 7, 694, 2048 }, { 8, 695, 2048 },
-   { 6, 696, 2048 }, { 7, 697, 2048 }, { 7, 698, 2048 }, { 8, 699, 2048 }, { 7, 700, 2048 }, { 8, 701, 2048 }, { 8, 702, 2048 }, { 9, 703, 2048 },
-   { 4, 704, 2048 }, { 5, 705, 2048 }, { 5, 706, 2048 }, { 6, 707, 2048 }, { 5, 708, 2048 }, { 6, 709, 2048 }, { 6, 710, 2048 }, { 7, 711, 2048 },
-   { 5, 712, 2048 }, { 6, 713, 2048 }, { 6, 714, 2048 }, { 7, 715, 2048 }, { 6, 716, 2048 }, { 7, 717, 2048 }, { 7, 718, 2048 }, { 8, 719, 2048 },
-   { 5, 720, 2048 }, { 6, 721, 2048 }, { 6, 722, 2048 }, { 7, 723, 2048 }, { 6, 724, 2048 }, { 7, 725, 2048 }, { 7, 726, 2048 }, { 8, 727, 2048 },
-   { 6, 728, 2048 }, { 7, 729, 2048 }, { 7, 730, 2048 }, { 8, 731, 2048 }, { 7, 732, 2048 }, { 8, 733, 2048 }, { 8, 734, 2048 }, { 9, 735, 2048 },
-   { 5, 736, 2048 }, { 6, 737, 2048 }, { 6, 738, 2048 }, { 7, 739, 2048 }, { 6, 740, 2048 }, { 7, 741, 2048 }, { 7, 742, 2048 }, { 8, 743, 2048 },
-   { 6, 744, 2048 }, { 7, 745, 2048 }, { 7, 746, 2048 }, { 8, 747, 2048 }, { 7, 748, 2048 }, { 8, 749, 2048 }, { 8, 750, 2048 }, { 9, 751, 2048 },
-   { 6, 752, 2048 }, { 7, 753, 2048 }, { 7, 754, 2048 }, { 8, 755, 2048 }, { 7, 756, 2048 }, { 8, 757, 2048 }, { 8, 758, 2048 }, { 9, 759, 2048 },
-   { 7, 760, 2048 }, { 8, 761, 2048 }, { 8, 762, 2048 }, { 9, 763, 2048 }, { 8, 764, 2048 }, { 9, 765, 2048 }, { 9, 766, 2048 }, { 10, 767, 2048 },
-   { 3, 768, 2048 }, { 4, 769, 2048 }, { 4, 770, 2048 }, { 5, 771, 2048 }, { 4, 772, 2048 }, { 5, 773, 2048 }, { 5, 774, 2048 }, { 6, 775, 2048 },
-   { 4, 776, 2048 }, { 5, 777, 2048 }, { 5, 778, 2048 }, { 6, 779, 2048 }, { 5, 780, 2048 }, { 6, 781, 2048 }, { 6, 782, 2048 }, { 7, 783, 2048 },
-   { 4, 784, 2048 }, { 5, 785, 2048 }, { 5, 786, 2048 }, { 6, 787, 2048 }, { 5, 788, 2048 }, { 6, 789, 2048 }, { 6, 790, 2048 }, { 7, 791, 2048 },
-   { 5, 792, 2048 }, { 6, 793, 2048 }, { 6, 794, 2048 }, { 7, 795, 2048 }, { 6, 796, 2048 }, { 7, 797, 2048 }, { 7, 798, 2048 }, { 8, 799, 2048 },
-   { 4, 800, 2048 }, { 5, 801, 2048 }, { 5, 802, 2048 }, { 6, 803, 2048 }, { 5, 804, 2048 }, { 6, 805, 2048 }, { 6, 806, 2048 }, { 7, 807, 2048 },
-   { 5, 808, 2048 }, { 6, 809, 2048 }, { 6, 810, 2048 }, { 7, 811, 2048 }, { 6, 812, 2048 }, { 7, 813, 2048 }, { 7, 814, 2048 }, { 8, 815, 2048 },
-   { 5, 816, 2048 }, { 6, 817, 2048 }, { 6, 818, 2048 }, { 7, 819, 2048 }, { 6, 820, 2048 }, { 7, 821, 2048 }, { 7, 822, 2048 }, { 8, 823, 2048 },
-   { 6, 824, 2048 }, { 7, 825, 2048 }, { 7, 826, 2048 }, { 8, 827, 2048 }, { 7, 828, 2048 }, { 8, 829, 2048 }, { 8, 830, 2048 }, { 9, 831, 2048 },
-   { 4, 832, 2048 }, { 5, 833, 2048 }, { 5, 834, 2048 }, { 6, 835, 2048 }, { 5, 836, 2048 }, { 6, 837, 2048 }, { 6, 838, 2048 }, { 7, 839, 2048 },
-   { 5, 840, 2048 }, { 6, 841, 2048 }, { 6, 842, 2048 }, { 7, 843, 2048 }, { 6, 844, 2048 }, { 7, 845, 2048 }, { 7, 846, 2048 }, { 8, 847, 2048 },
-   { 5, 848, 2048 }, { 6, 849, 2048 }, { 6, 850, 2048 }, { 7, 851, 2048 }, { 6, 852, 2048 }, { 7, 853, 2048 }, { 7, 854, 2048 }, { 8, 855, 2048 },
-   { 6, 856, 2048 }, { 7, 857, 2048 }, { 7, 858, 2048 }, { 8, 859, 2048 }, { 7, 860, 2048 }, { 8, 861, 2048 }, { 8, 862, 2048 }, { 9, 863, 2048 },
-   { 5, 864, 2048 }, { 6, 865, 2048 }, { 6, 866, 2048 }, { 7, 867, 2048 }, { 6, 868, 2048 }, { 7, 869, 2048 }, { 7, 870, 2048 }, { 8, 871, 2048 },
-   { 6, 872, 2048 }, { 7, 873, 2048 }, { 7, 874, 2048 }, { 8, 875, 2048 }, { 7, 876, 2048 }, { 8, 877, 2048 }, { 8, 878, 2048 }, { 9, 879, 2048 },
-   { 6, 880, 2048 }, { 7, 881, 2048 }, { 7, 882, 2048 }, { 8, 883, 2048 }, { 7, 884, 2048 }, { 8, 885, 2048 }, { 8, 886, 2048 }, { 9, 887, 2048 },
-   { 7, 888, 2048 }, { 8, 889, 2048 }, { 8, 890, 2048 }, { 9, 891, 2048 }, { 8, 892, 2048 }, { 9, 893, 2048 }, { 9, 894, 2048 }, { 10, 895, 2048 },
-   { 4, 896, 2048 }, { 5, 897, 2048 }, { 5, 898, 2048 }, { 6, 899, 2048 }, { 5, 900, 2048 }, { 6, 901, 2048 }, { 6, 902, 2048 }, { 7, 903, 2048 },
-   { 5, 904, 2048 }, { 6, 905, 2048 }, { 6, 906, 2048 }, { 7, 907, 2048 }, { 6, 908, 2048 }, { 7, 909, 2048 }, { 7, 910, 2048 }, { 8, 911, 2048 },
-   { 5, 912, 2048 }, { 6, 913, 2048 }, { 6, 914, 2048 }, { 7, 915, 2048 }, { 6, 916, 2048 }, { 7, 917, 2048 }, { 7, 918, 2048 }, { 8, 919, 2048 },
-   { 6, 920, 2048 }, { 7, 921, 2048 }, { 7, 922, 2048 }, { 8, 923, 2048 }, { 7, 924, 2048 }, { 8, 925, 2048 }, { 8, 926, 2048 }, { 9, 927, 2048 },
-   { 5, 928, 2048 }, { 6, 929, 2048 }, { 6, 930, 2048 }, { 7, 931, 2048 }, { 6, 932, 2048 }, { 7, 933, 2048 }, { 7, 934, 2048 }, { 8, 935, 2048 },
-   { 6, 936, 2048 }, { 7, 937, 2048 }, { 7, 938, 2048 }, { 8, 939, 2048 }, { 7, 940, 2048 }, { 8, 941, 2048 }, { 8, 942, 2048 }, { 9, 943, 2048 },
-   { 6, 944, 2048 }, { 7, 945, 2048 }, { 7, 946, 2048 }, { 8, 947, 2048 }, { 7, 948, 2048 }, { 8, 949, 2048 }, { 8, 950, 2048 }, { 9, 951, 2048 },
-   { 7, 952, 2048 }, { 8, 953, 2048 }, { 8, 954, 2048 }, { 9, 955, 2048 }, { 8, 956, 2048 }, { 9, 957, 2048 }, { 9, 958, 2048 }, { 10, 959, 2048 },
-   { 5, 960, 2048 }, { 6, 961, 2048 }, { 6, 962, 2048 }, { 7, 963, 2048 }, { 6, 964, 2048 }, { 7, 965, 2048 }, { 7, 966, 2048 }, { 8, 967, 2048 },
-   { 6, 968, 2048 }, { 7, 969, 2048 }, { 7, 970, 2048 }, { 8, 971, 2048 }, { 7, 972, 2048 }, { 8, 973, 2048 }, { 8, 974, 2048 }, { 9, 975, 2048 },
-   { 6, 976, 2048 }, { 7, 977, 2048 }, { 7, 978, 2048 }, { 8, 979, 2048 }, { 7, 980, 2048 }, { 8, 981, 2048 }, { 8, 982, 2048 }, { 9, 983, 2048 },
-   { 7, 984, 2048 }, { 8, 985, 2048 }, { 8, 986, 2048 }, { 9, 987, 2048 }, { 8, 988, 2048 }, { 9, 989, 2048 }, { 9, 990, 2048 }, { 10, 991, 2048 },
-   { 6, 992, 2048 }, { 7, 993, 2048 }, { 7, 994, 2048 }, { 8, 995, 2048 }, { 7, 996, 2048 }, { 8, 997, 2048 }, { 8, 998, 2048 }, { 9, 999, 2048 },
-   { 7, 1000, 2048 }, { 8, 1001, 2048 }, { 8, 1002, 2048 }, { 9, 1003, 2048 }, { 8, 1004, 2048 }, { 9, 1005, 2048 }, { 9, 1006, 2048 }, { 10, 1007, 2048 },
-   { 7, 1008, 2048 }, { 8, 1009, 2048 }, { 8, 1010, 2048 }, { 9, 1011, 2048 }, { 8, 1012, 2048 }, { 9, 1013, 2048 }, { 9, 1014, 2048 }, { 10, 1015, 2048 },
-   { 8, 1016, 2048 }, { 9, 1017, 2048 }, { 9, 1018, 2048 }, { 10, 1019, 2048 }, { 9, 1020, 2048 }, { 10, 1021, 2048 }, { 10, 1022, 2048 }, { 11, 1023, 2048 },
-   { 2, 1024, 2048 }, { 3, 1025, 2048 }, { 3, 1026, 2048 }, { 4, 1027, 2048 }, { 3, 1028, 2048 }, { 4, 1029, 2048 }, { 4, 1030, 2048 }, { 5, 1031, 2048 },
-   { 3, 1032, 2048 }, { 4, 1033, 2048 }, { 4, 1034, 2048 }, { 5, 1035, 2048 }, { 4, 1036, 2048 }, { 5, 1037, 2048 }, { 5, 1038, 2048 }, { 6, 1039, 2048 },
-   { 3, 1040, 2048 }, { 4, 1041, 2048 }, { 4, 1042, 2048 }, { 5, 1043, 2048 }, { 4, 1044, 2048 }, { 5, 1045, 2048 }, { 5, 1046, 2048 }, { 6, 1047, 2048 },
-   { 4, 1048, 2048 }, { 5, 1049, 2048 }, { 5, 1050, 2048 }, { 6, 1051, 2048 }, { 5, 1052, 2048 }, { 6, 1053, 2048 }, { 6, 1054, 2048 }, { 7, 1055, 2048 },
-   { 3, 1056, 2048 }, { 4, 1057, 2048 }, { 4, 1058, 2048 }, { 5, 1059, 2048 }, { 4, 1060, 2048 }, { 5, 1061, 2048 }, { 5, 1062, 2048 }, { 6, 1063, 2048 },
-   { 4, 1064, 2048 }, { 5, 1065, 2048 }, { 5, 1066, 2048 }, { 6, 1067, 2048 }, { 5, 1068, 2048 }, { 6, 1069, 2048 }, { 6, 1070, 2048 }, { 7, 1071, 2048 },
-   { 4, 1072, 2048 }, { 5, 1073, 2048 }, { 5, 1074, 2048 }, { 6, 1075, 2048 }, { 5, 1076, 2048 }, { 6, 1077, 2048 }, { 6, 1078, 2048 }, { 7, 1079, 2048 },
-   { 5, 1080, 2048 }, { 6, 1081, 2048 }, { 6, 1082, 2048 }, { 7, 1083, 2048 }, { 6, 1084, 2048 }, { 7, 1085, 2048 }, { 7, 1086, 2048 }, { 8, 1087, 2048 },
-   { 3, 1088, 2048 }, { 4, 1089, 2048 }, { 4, 1090, 2048 }, { 5, 1091, 2048 }, { 4, 1092, 2048 }, { 5, 1093, 2048 }, { 5, 1094, 2048 }, { 6, 1095, 2048 },
-   { 4, 1096, 2048 }, { 5, 1097, 2048 }, { 5, 1098, 2048 }, { 6, 1099, 2048 }, { 5, 1100, 2048 }, { 6, 1101, 2048 }, { 6, 1102, 2048 }, { 7, 1103, 2048 },
-   { 4, 1104, 2048 }, { 5, 1105, 2048 }, { 5, 1106, 2048 }, { 6, 1107, 2048 }, { 5, 1108, 2048 }, { 6, 1109, 2048 }, { 6, 1110, 2048 }, { 7, 1111, 2048 },
-   { 5, 1112, 2048 }, { 6, 1113, 2048 }, { 6, 1114, 2048 }, { 7, 1115, 2048 }, { 6, 1116, 2048 }, { 7, 1117, 2048 }, { 7, 1118, 2048 }, { 8, 1119, 2048 },
-   { 4, 1120, 2048 }, { 5, 1121, 2048 }, { 5, 1122, 2048 }, { 6, 1123, 2048 }, { 5, 1124, 2048 }, { 6, 1125, 2048 }, { 6, 1126, 2048 }, { 7, 1127, 2048 },
-   { 5, 1128, 2048 }, { 6, 1129, 2048 }, { 6, 1130, 2048 }, { 7, 1131, 2048 }, { 6, 1132, 2048 }, { 7, 1133, 2048 }, { 7, 1134, 2048 }, { 8, 1135, 2048 },
-   { 5, 1136, 2048 }, { 6, 1137, 2048 }, { 6, 1138, 2048 }, { 7, 1139, 2048 }, { 6, 1140, 2048 }, { 7, 1141, 2048 }, { 7, 1142, 2048 }, { 8, 1143, 2048 },
-   { 6, 1144, 2048 }, { 7, 1145, 2048 }, { 7, 1146, 2048 }, { 8, 1147, 2048 }, { 7, 1148, 2048 }, { 8, 1149, 2048 }, { 8, 1150, 2048 }, { 9, 1151, 2048 },
-   { 3, 1152, 2048 }, { 4, 1153, 2048 }, { 4, 1154, 2048 }, { 5, 1155, 2048 }, { 4, 1156, 2048 }, { 5, 1157, 2048 }, { 5, 1158, 2048 }, { 6, 1159, 2048 },
-   { 4, 1160, 2048 }, { 5, 1161, 2048 }, { 5, 1162, 2048 }, { 6, 1163, 2048 }, { 5, 1164, 2048 }, { 6, 1165, 2048 }, { 6, 1166, 2048 }, { 7, 1167, 2048 },
-   { 4, 1168, 2048 }, { 5, 1169, 2048 }, { 5, 1170, 2048 }, { 6, 1171, 2048 }, { 5, 1172, 2048 }, { 6, 1173, 2048 }, { 6, 1174, 2048 }, { 7, 1175, 2048 },
-   { 5, 1176, 2048 }, { 6, 1177, 2048 }, { 6, 1178, 2048 }, { 7, 1179, 2048 }, { 6, 1180, 2048 }, { 7, 1181, 2048 }, { 7, 1182, 2048 }, { 8, 1183, 2048 },
-   { 4, 1184, 2048 }, { 5, 1185, 2048 }, { 5, 1186, 2048 }, { 6, 1187, 2048 }, { 5, 1188, 2048 }, { 6, 1189, 2048 }, { 6, 1190, 2048 }, { 7, 1191, 2048 },
-   { 5, 1192, 2048 }, { 6, 1193, 2048 }, { 6, 1194, 2048 }, { 7, 1195, 2048 }, { 6, 1196, 2048 }, { 7, 1197, 2048 }, { 7, 1198, 2048 }, { 8, 1199, 2048 },
-   { 5, 1200, 2048 }, { 6, 1201, 2048 }, { 6, 1202, 2048 }, { 7, 1203, 2048 }, { 6, 1204, 2048 }, { 7, 1205, 2048 }, { 7, 1206, 2048 }, { 8, 1207, 2048 },
-   { 6, 1208, 2048 }, { 7, 1209, 2048 }, { 7, 1210, 2048 }, { 8, 1211, 2048 }, { 7, 1212, 2048 }, { 8, 1213, 2048 }, { 8, 1214, 2048 }, { 9, 1215, 2048 },
-   { 4, 1216, 2048 }, { 5, 1217, 2048 }, { 5, 1218, 2048 }, { 6, 1219, 2048 }, { 5, 1220, 2048 }, { 6, 1221, 2048 }, { 6, 1222, 2048 }, { 7, 1223, 2048 },
-   { 5, 1224, 2048 }, { 6, 1225, 2048 }, { 6, 1226, 2048 }, { 7, 1227, 2048 }, { 6, 1228, 2048 }, { 7, 1229, 2048 }, { 7, 1230, 2048 }, { 8, 1231, 2048 },
-   { 5, 1232, 2048 }, { 6, 1233, 2048 }, { 6, 1234, 2048 }, { 7, 1235, 2048 }, { 6, 1236, 2048 }, { 7, 1237, 2048 }, { 7, 1238, 2048 }, { 8, 1239, 2048 },
-   { 6, 1240, 2048 }, { 7, 1241, 2048 }, { 7, 1242, 2048 }, { 8, 1243, 2048 }, { 7, 1244, 2048 }, { 8, 1245, 2048 }, { 8, 1246, 2048 }, { 9, 1247, 2048 },
-   { 5, 1248, 2048 }, { 6, 1249, 2048 }, { 6, 1250, 2048 }, { 7, 1251, 2048 }, { 6, 1252, 2048 }, { 7, 1253, 2048 }, { 7, 1254, 2048 }, { 8, 1255, 2048 },
-   { 6, 1256, 2048 }, { 7, 1257, 2048 }, { 7, 1258, 2048 }, { 8, 1259, 2048 }, { 7, 1260, 2048 }, { 8, 1261, 2048 }, { 8, 1262, 2048 }, { 9, 1263, 2048 },
-   { 6, 1264, 2048 }, { 7, 1265, 2048 }, { 7, 1266, 2048 }, { 8, 1267, 2048 }, { 7, 1268, 2048 }, { 8, 1269, 2048 }, { 8, 1270, 2048 }, { 9, 1271, 2048 },
-   { 7, 1272, 2048 }, { 8, 1273, 2048 }, { 8, 1274, 2048 }, { 9, 1275, 2048 }, { 8, 1276, 2048 }, { 9, 1277, 2048 }, { 9, 1278, 2048 }, { 10, 1279, 2048 },
-   { 3, 1280, 2048 }, { 4, 1281, 2048 }, { 4, 1282, 2048 }, { 5, 1283, 2048 }, { 4, 1284, 2048 }, { 5, 1285, 2048 }, { 5, 1286, 2048 }, { 6, 1287, 2048 },
-   { 4, 1288, 2048 }, { 5, 1289, 2048 }, { 5, 1290, 2048 }, { 6, 1291, 2048 }, { 5, 1292, 2048 }, { 6, 1293, 2048 }, { 6, 1294, 2048 }, { 7, 1295, 2048 },
-   { 4, 1296, 2048 }, { 5, 1297, 2048 }, { 5, 1298, 2048 }, { 6, 1299, 2048 }, { 5, 1300, 2048 }, { 6, 1301, 2048 }, { 6, 1302, 2048 }, { 7, 1303, 2048 },
-   { 5, 1304, 2048 }, { 6, 1305, 2048 }, { 6, 1306, 2048 }, { 7, 1307, 2048 }, { 6, 1308, 2048 }, { 7, 1309, 2048 }, { 7, 1310, 2048 }, { 8, 1311, 2048 },
-   { 4, 1312, 2048 }, { 5, 1313, 2048 }, { 5, 1314, 2048 }, { 6, 1315, 2048 }, { 5, 1316, 2048 }, { 6, 1317, 2048 }, { 6, 1318, 2048 }, { 7, 1319, 2048 },
-   { 5, 1320, 2048 }, { 6, 1321, 2048 }, { 6, 1322, 2048 }, { 7, 1323, 2048 }, { 6, 1324, 2048 }, { 7, 1325, 2048 }, { 7, 1326, 2048 }, { 8, 1327, 2048 },
-   { 5, 1328, 2048 }, { 6, 1329, 2048 }, { 6, 1330, 2048 }, { 7, 1331, 2048 }, { 6, 1332, 2048 }, { 7, 1333, 2048 }, { 7, 1334, 2048 }, { 8, 1335, 2048 },
-   { 6, 1336, 2048 }, { 7, 1337, 2048 }, { 7, 1338, 2048 }, { 8, 1339, 2048 }, { 7, 1340, 2048 }, { 8, 1341, 2048 }, { 8, 1342, 2048 }, { 9, 1343, 2048 },
-   { 4, 1344, 2048 }, { 5, 1345, 2048 }, { 5, 1346, 2048 }, { 6, 1347, 2048 }, { 5, 1348, 2048 }, { 6, 1349, 2048 }, { 6, 1350, 2048 }, { 7, 1351, 2048 },
-   { 5, 1352, 2048 }, { 6, 1353, 2048 }, { 6, 1354, 2048 }, { 7, 1355, 2048 }, { 6, 1356, 2048 }, { 7, 1357, 2048 }, { 7, 1358, 2048 }, { 8, 1359, 2048 },
-   { 5, 1360, 2048 }, { 6, 1361, 2048 }, { 6, 1362, 2048 }, { 7, 1363, 2048 }, { 6, 1364, 2048 }, { 7, 1365, 2048 }, { 7, 1366, 2048 }, { 8, 1367, 2048 },
-   { 6, 1368, 2048 }, { 7, 1369, 2048 }, { 7, 1370, 2048 }, { 8, 1371, 2048 }, { 7, 1372, 2048 }, { 8, 1373, 2048 }, { 8, 1374, 2048 }, { 9, 1375, 2048 },
-   { 5, 1376, 2048 }, { 6, 1377, 2048 }, { 6, 1378, 2048 }, { 7, 1379, 2048 }, { 6, 1380, 2048 }, { 7, 1381, 2048 }, { 7, 1382, 2048 }, { 8, 1383, 2048 },
-   { 6, 1384, 2048 }, { 7, 1385, 2048 }, { 7, 1386, 2048 }, { 8, 1387, 2048 }, { 7, 1388, 2048 }, { 8, 1389, 2048 }, { 8, 1390, 2048 }, { 9, 1391, 2048 },
-   { 6, 1392, 2048 }, { 7, 1393, 2048 }, { 7, 1394, 2048 }, { 8, 1395, 2048 }, { 7, 1396, 2048 }, { 8, 1397, 2048 }, { 8, 1398, 2048 }, { 9, 1399, 2048 },
-   { 7, 1400, 2048 }, { 8, 1401, 2048 }, { 8, 1402, 2048 }, { 9, 1403, 2048 }, { 8, 1404, 2048 }, { 9, 1405, 2048 }, { 9, 1406, 2048 }, { 10, 1407, 2048 },
-   { 4, 1408, 2048 }, { 5, 1409, 2048 }, { 5, 1410, 2048 }, { 6, 1411, 2048 }, { 5, 1412, 2048 }, { 6, 1413, 2048 }, { 6, 1414, 2048 }, { 7, 1415, 2048 },
-   { 5, 1416, 2048 }, { 6, 1417, 2048 }, { 6, 1418, 2048 }, { 7, 1419, 2048 }, { 6, 1420, 2048 }, { 7, 1421, 2048 }, { 7, 1422, 2048 }, { 8, 1423, 2048 },
-   { 5, 1424, 2048 }, { 6, 1425, 2048 }, { 6, 1426, 2048 }, { 7, 1427, 2048 }, { 6, 1428, 2048 }, { 7, 1429, 2048 }, { 7, 1430, 2048 }, { 8, 1431, 2048 },
-   { 6, 1432, 2048 }, { 7, 1433, 2048 }, { 7, 1434, 2048 }, { 8, 1435, 2048 }, { 7, 1436, 2048 }, { 8, 1437, 2048 }, { 8, 1438, 2048 }, { 9, 1439, 2048 },
-   { 5, 1440, 2048 }, { 6, 1441, 2048 }, { 6, 1442, 2048 }, { 7, 1443, 2048 }, { 6, 1444, 2048 }, { 7, 1445, 2048 }, { 7, 1446, 2048 }, { 8, 1447, 2048 },
-   { 6, 1448, 2048 }, { 7, 1449, 2048 }, { 7, 1450, 2048 }, { 8, 1451, 2048 }, { 7, 1452, 2048 }, { 8, 1453, 2048 }, { 8, 1454, 2048 }, { 9, 1455, 2048 },
-   { 6, 1456, 2048 }, { 7, 1457, 2048 }, { 7, 1458, 2048 }, { 8, 1459, 2048 }, { 7, 1460, 2048 }, { 8, 1461, 2048 }, { 8, 1462, 2048 }, { 9, 1463, 2048 },
-   { 7, 1464, 2048 }, { 8, 1465, 2048 }, { 8, 1466, 2048 }, { 9, 1467, 2048 }, { 8, 1468, 2048 }, { 9, 1469, 2048 }, { 9, 1470, 2048 }, { 10, 1471, 2048 },
-   { 5, 1472, 2048 }, { 6, 1473, 2048 }, { 6, 1474, 2048 }, { 7, 1475, 2048 }, { 6, 1476, 2048 }, { 7, 1477, 2048 }, { 7, 1478, 2048 }, { 8, 1479, 2048 },
-   { 6, 1480, 2048 }, { 7, 1481, 2048 }, { 7, 1482, 2048 }, { 8, 1483, 2048 }, { 7, 1484, 2048 }, { 8, 1485, 2048 }, { 8, 1486, 2048 }, { 9, 1487, 2048 },
-   { 6, 1488, 2048 }, { 7, 1489, 2048 }, { 7, 1490, 2048 }, { 8, 1491, 2048 }, { 7, 1492, 2048 }, { 8, 1493, 2048 }, { 8, 1494, 2048 }, { 9, 1495, 2048 },
-   { 7, 1496, 2048 }, { 8, 1497, 2048 }, { 8, 1498, 2048 }, { 9, 1499, 2048 }, { 8, 1500, 2048 }, { 9, 1501, 2048 }, { 9, 1502, 2048 }, { 10, 1503, 2048 },
-   { 6, 1504, 2048 }, { 7, 1505, 2048 }, { 7, 1506, 2048 }, { 8, 1507, 2048 }, { 7, 1508, 2048 }, { 8, 1509, 2048 }, { 8, 1510, 2048 }, { 9, 1511, 2048 },
-   { 7, 1512, 2048 }, { 8, 1513, 2048 }, { 8, 1514, 2048 }, { 9, 1515, 2048 }, { 8, 1516, 2048 }, { 9, 1517, 2048 }, { 9, 1518, 2048 }, { 10, 1519, 2048 },
-   { 7, 1520, 2048 }, { 8, 1521, 2048 }, { 8, 1522, 2048 }, { 9, 1523, 2048 }, { 8, 1524, 2048 }, { 9, 1525, 2048 }, { 9, 1526, 2048 }, { 10, 1527, 2048 },
-   { 8, 1528, 2048 }, { 9, 1529, 2048 }, { 9, 1530, 2048 }, { 10, 1531, 2048 }, { 9, 1532, 2048 }, { 10, 1533, 2048 }, { 10, 1534, 2048 }, { 11, 1535, 2048 },
-   { 3, 1536, 2048 }, { 4, 1537, 2048 }, { 4, 1538, 2048 }, { 5, 1539, 2048 }, { 4, 1540, 2048 }, { 5, 1541, 2048 }, { 5, 1542, 2048 }, { 6, 1543, 2048 },
-   { 4, 1544, 2048 }, { 5, 1545, 2048 }, { 5, 1546, 2048 }, { 6, 1547, 2048 }, { 5, 1548, 2048 }, { 6, 1549, 2048 }, { 6, 1550, 2048 }, { 7, 1551, 2048 },
-   { 4, 1552, 2048 }, { 5, 1553, 2048 }, { 5, 1554, 2048 }, { 6, 1555, 2048 }, { 5, 1556, 2048 }, { 6, 1557, 2048 }, { 6, 1558, 2048 }, { 7, 1559, 2048 },
-   { 5, 1560, 2048 }, { 6, 1561, 2048 }, { 6, 1562, 2048 }, { 7, 1563, 2048 }, { 6, 1564, 2048 }, { 7, 1565, 2048 }, { 7, 1566, 2048 }, { 8, 1567, 2048 },
-   { 4, 1568, 2048 }, { 5, 1569, 2048 }, { 5, 1570, 2048 }, { 6, 1571, 2048 }, { 5, 1572, 2048 }, { 6, 1573, 2048 }, { 6, 1574, 2048 }, { 7, 1575, 2048 },
-   { 5, 1576, 2048 }, { 6, 1577, 2048 }, { 6, 1578, 2048 }, { 7, 1579, 2048 }, { 6, 1580, 2048 }, { 7, 1581, 2048 }, { 7, 1582, 2048 }, { 8, 1583, 2048 },
-   { 5, 1584, 2048 }, { 6, 1585, 2048 }, { 6, 1586, 2048 }, { 7, 1587, 2048 }, { 6, 1588, 2048 }, { 7, 1589, 2048 }, { 7, 1590, 2048 }, { 8, 1591, 2048 },
-   { 6, 1592, 2048 }, { 7, 1593, 2048 }, { 7, 1594, 2048 }, { 8, 1595, 2048 }, { 7, 1596, 2048 }, { 8, 1597, 2048 }, { 8, 1598, 2048 }, { 9, 1599, 2048 },
-   { 4, 1600, 2048 }, { 5, 1601, 2048 }, { 5, 1602, 2048 }, { 6, 1603, 2048 }, { 5, 1604, 2048 }, { 6, 1605, 2048 }, { 6, 1606, 2048 }, { 7, 1607, 2048 },
-   { 5, 1608, 2048 }, { 6, 1609, 2048 }, { 6, 1610, 2048 }, { 7, 1611, 2048 }, { 6, 1612, 2048 }, { 7, 1613, 2048 }, { 7, 1614, 2048 }, { 8, 1615, 2048 },
-   { 5, 1616, 2048 }, { 6, 1617, 2048 }, { 6, 1618, 2048 }, { 7, 1619, 2048 }, { 6, 1620, 2048 }, { 7, 1621, 2048 }, { 7, 1622, 2048 }, { 8, 1623, 2048 },
-   { 6, 1624, 2048 }, { 7, 1625, 2048 }, { 7, 1626, 2048 }, { 8, 1627, 2048 }, { 7, 1628, 2048 }, { 8, 1629, 2048 }, { 8, 1630, 2048 }, { 9, 1631, 2048 },
-   { 5, 1632, 2048 }, { 6, 1633, 2048 }, { 6, 1634, 2048 }, { 7, 1635, 2048 }, { 6, 1636, 2048 }, { 7, 1637, 2048 }, { 7, 1638, 2048 }, { 8, 1639, 2048 },
-   { 6, 1640, 2048 }, { 7, 1641, 2048 }, { 7, 1642, 2048 }, { 8, 1643, 2048 }, { 7, 1644, 2048 }, { 8, 1645, 2048 }, { 8, 1646, 2048 }, { 9, 1647, 2048 },
-   { 6, 1648, 2048 }, { 7, 1649, 2048 }, { 7, 1650, 2048 }, { 8, 1651, 2048 }, { 7, 1652, 2048 }, { 8, 1653, 2048 }, { 8, 1654, 2048 }, { 9, 1655, 2048 },
-   { 7, 1656, 2048 }, { 8, 1657, 2048 }, { 8, 1658, 2048 }, { 9, 1659, 2048 }, { 8, 1660, 2048 }, { 9, 1661, 2048 }, { 9, 1662, 2048 }, { 10, 1663, 2048 },
-   { 4, 1664, 2048 }, { 5, 1665, 2048 }, { 5, 1666, 2048 }, { 6, 1667, 2048 }, { 5, 1668, 2048 }, { 6, 1669, 2048 }, { 6, 1670, 2048 }, { 7, 1671, 2048 },
-   { 5, 1672, 2048 }, { 6, 1673, 2048 }, { 6, 1674, 2048 }, { 7, 1675, 2048 }, { 6, 1676, 2048 }, { 7, 1677, 2048 }, { 7, 1678, 2048 }, { 8, 1679, 2048 },
-   { 5, 1680, 2048 }, { 6, 1681, 2048 }, { 6, 1682, 2048 }, { 7, 1683, 2048 }, { 6, 1684, 2048 }, { 7, 1685, 2048 }, { 7, 1686, 2048 }, { 8, 1687, 2048 },
-   { 6, 1688, 2048 }, { 7, 1689, 2048 }, { 7, 1690, 2048 }, { 8, 1691, 2048 }, { 7, 1692, 2048 }, { 8, 1693, 2048 }, { 8, 1694, 2048 }, { 9, 1695, 2048 },
-   { 5, 1696, 2048 }, { 6, 1697, 2048 }, { 6, 1698, 2048 }, { 7, 1699, 2048 }, { 6, 1700, 2048 }, { 7, 1701, 2048 }, { 7, 1702, 2048 }, { 8, 1703, 2048 },
-   { 6, 1704, 2048 }, { 7, 1705, 2048 }, { 7, 1706, 2048 }, { 8, 1707, 2048 }, { 7, 1708, 2048 }, { 8, 1709, 2048 }, { 8, 1710, 2048 }, { 9, 1711, 2048 },
-   { 6, 1712, 2048 }, { 7, 1713, 2048 }, { 7, 1714, 2048 }, { 8, 1715, 2048 }, { 7, 1716, 2048 }, { 8, 1717, 2048 }, { 8, 1718, 2048 }, { 9, 1719, 2048 },
-   { 7, 1720, 2048 }, { 8, 1721, 2048 }, { 8, 1722, 2048 }, { 9, 1723, 2048 }, { 8, 1724, 2048 }, { 9, 1725, 2048 }, { 9, 1726, 2048 }, { 10, 1727, 2048 },
-   { 5, 1728, 2048 }, { 6, 1729, 2048 }, { 6, 1730, 2048 }, { 7, 1731, 2048 }, { 6, 1732, 2048 }, { 7, 1733, 2048 }, { 7, 1734, 2048 }, { 8, 1735, 2048 },
-   { 6, 1736, 2048 }, { 7, 1737, 2048 }, { 7, 1738, 2048 }, { 8, 1739, 2048 }, { 7, 1740, 2048 }, { 8, 1741, 2048 }, { 8, 1742, 2048 }, { 9, 1743, 2048 },
-   { 6, 1744, 2048 }, { 7, 1745, 2048 }, { 7, 1746, 2048 }, { 8, 1747, 2048 }, { 7, 1748, 2048 }, { 8, 1749, 2048 }, { 8, 1750, 2048 }, { 9, 1751, 2048 },
-   { 7, 1752, 2048 }, { 8, 1753, 2048 }, { 8, 1754, 2048 }, { 9, 1755, 2048 }, { 8, 1756, 2048 }, { 9, 1757, 2048 }, { 9, 1758, 2048 }, { 10, 1759, 2048 },
-   { 6, 1760, 2048 }, { 7, 1761, 2048 }, { 7, 1762, 2048 }, { 8, 1763, 2048 }, { 7, 1764, 2048 }, { 8, 1765, 2048 }, { 8, 1766, 2048 }, { 9, 1767, 2048 },
-   { 7, 1768, 2048 }, { 8, 1769, 2048 }, { 8, 1770, 2048 }, { 9, 1771, 2048 }, { 8, 1772, 2048 }, { 9, 1773, 2048 }, { 9, 1774, 2048 }, { 10, 1775, 2048 },
-   { 7, 1776, 2048 }, { 8, 1777, 2048 }, { 8, 1778, 2048 }, { 9, 1779, 2048 }, { 8, 1780, 2048 }, { 9, 1781, 2048 }, { 9, 1782, 2048 }, { 10, 1783, 2048 },
-   { 8, 1784, 2048 }, { 9, 1785, 2048 }, { 9, 1786, 2048 }, { 10, 1787, 2048 }, { 9, 1788, 2048 }, { 10, 1789, 2048 }, { 10, 1790, 2048 }, { 11, 1791, 2048 },
-   { 4, 1792, 2048 }, { 5, 1793, 2048 }, { 5, 1794, 2048 }, { 6, 1795, 2048 }, { 5, 1796, 2048 }, { 6, 1797, 2048 }, { 6, 1798, 2048 }, { 7, 1799, 2048 },
-   { 5, 1800, 2048 }, { 6, 1801, 2048 }, { 6, 1802, 2048 }, { 7, 1803, 2048 }, { 6, 1804, 2048 }, { 7, 1805, 2048 }, { 7, 1806, 2048 }, { 8, 1807, 2048 },
-   { 5, 1808, 2048 }, { 6, 1809, 2048 }, { 6, 1810, 2048 }, { 7, 1811, 2048 }, { 6, 1812, 2048 }, { 7, 1813, 2048 }, { 7, 1814, 2048 }, { 8, 1815, 2048 },
-   { 6, 1816, 2048 }, { 7, 1817, 2048 }, { 7, 1818, 2048 }, { 8, 1819, 2048 }, { 7, 1820, 2048 }, { 8, 1821, 2048 }, { 8, 1822, 2048 }, { 9, 1823, 2048 },
-   { 5, 1824, 2048 }, { 6, 1825, 2048 }, { 6, 1826, 2048 }, { 7, 1827, 2048 }, { 6, 1828, 2048 }, { 7, 1829, 2048 }, { 7, 1830, 2048 }, { 8, 1831, 2048 },
-   { 6, 1832, 2048 }, { 7, 1833, 2048 }, { 7, 1834, 2048 }, { 8, 1835, 2048 }, { 7, 1836, 2048 }, { 8, 1837, 2048 }, { 8, 1838, 2048 }, { 9, 1839, 2048 },
-   { 6, 1840, 2048 }, { 7, 1841, 2048 }, { 7, 1842, 2048 }, { 8, 1843, 2048 }, { 7, 1844, 2048 }, { 8, 1845, 2048 }, { 8, 1846, 2048 }, { 9, 1847, 2048 },
-   { 7, 1848, 2048 }, { 8, 1849, 2048 }, { 8, 1850, 2048 }, { 9, 1851, 2048 }, { 8, 1852, 2048 }, { 9, 1853, 2048 }, { 9, 1854, 2048 }, { 10, 1855, 2048 },
-   { 5, 1856, 2048 }, { 6, 1857, 2048 }, { 6, 1858, 2048 }, { 7, 1859, 2048 }, { 6, 1860, 2048 }, { 7, 1861, 2048 }, { 7, 1862, 2048 }, { 8, 1863, 2048 },
-   { 6, 1864, 2048 }, { 7, 1865, 2048 }, { 7, 1866, 2048 }, { 8, 1867, 2048 }, { 7, 1868, 2048 }, { 8, 1869, 2048 }, { 8, 1870, 2048 }, { 9, 1871, 2048 },
-   { 6, 1872, 2048 }, { 7, 1873, 2048 }, { 7, 1874, 2048 }, { 8, 1875, 2048 }, { 7, 1876, 2048 }, { 8, 1877, 2048 }, { 8, 1878, 2048 }, { 9, 1879, 2048 },
-   { 7, 1880, 2048 }, { 8, 1881, 2048 }, { 8, 1882, 2048 }, { 9, 1883, 2048 }, { 8, 1884, 2048 }, { 9, 1885, 2048 }, { 9, 1886, 2048 }, { 10, 1887, 2048 },
-   { 6, 1888, 2048 }, { 7, 1889, 2048 }, { 7, 1890, 2048 }, { 8, 1891, 2048 }, { 7, 1892, 2048 }, { 8, 1893, 2048 }, { 8, 1894, 2048 }, { 9, 1895, 2048 },
-   { 7, 1896, 2048 }, { 8, 1897, 2048 }, { 8, 1898, 2048 }, { 9, 1899, 2048 }, { 8, 1900, 2048 }, { 9, 1901, 2048 }, { 9, 1902, 2048 }, { 10, 1903, 2048 },
-   { 7, 1904, 2048 }, { 8, 1905, 2048 }, { 8, 1906, 2048 }, { 9, 1907, 2048 }, { 8, 1908, 2048 }, { 9, 1909, 2048 }, { 9, 1910, 2048 }, { 10, 1911, 2048 },
-   { 8, 1912, 2048 }, { 9, 1913, 2048 }, { 9, 1914, 2048 }, { 10, 1915, 2048 }, { 9, 1916, 2048 }, { 10, 1917, 2048 }, { 10, 1918, 2048 }, { 11, 1919, 2048 },
-   { 5, 1920, 2048 }, { 6, 1921, 2048 }, { 6, 1922, 2048 }, { 7, 1923, 2048 }, { 6, 1924, 2048 }, { 7, 1925, 2048 }, { 7, 1926, 2048 }, { 8, 1927, 2048 },
-   { 6, 1928, 2048 }, { 7, 1929, 2048 }, { 7, 1930, 2048 }, { 8, 1931, 2048 }, { 7, 1932, 2048 }, { 8, 1933, 2048 }, { 8, 1934, 2048 }, { 9, 1935, 2048 },
-   { 6, 1936, 2048 }, { 7, 1937, 2048 }, { 7, 1938, 2048 }, { 8, 1939, 2048 }, { 7, 1940, 2048 }, { 8, 1941, 2048 }, { 8, 1942, 2048 }, { 9, 1943, 2048 },
-   { 7, 1944, 2048 }, { 8, 1945, 2048 }, { 8, 1946, 2048 }, { 9, 1947, 2048 }, { 8, 1948, 2048 }, { 9, 1949, 2048 }, { 9, 1950, 2048 }, { 10, 1951, 2048 },
-   { 6, 1952, 2048 }, { 7, 1953, 2048 }, { 7, 1954, 2048 }, { 8, 1955, 2048 }, { 7, 1956, 2048 }, { 8, 1957, 2048 }, { 8, 1958, 2048 }, { 9, 1959, 2048 },
-   { 7, 1960, 2048 }, { 8, 1961, 2048 }, { 8, 1962, 2048 }, { 9, 1963, 2048 }, { 8, 1964, 2048 }, { 9, 1965, 2048 }, { 9, 1966, 2048 }, { 10, 1967, 2048 },
-   { 7, 1968, 2048 }, { 8, 1969, 2048 }, { 8, 1970, 2048 }, { 9, 1971, 2048 }, { 8, 1972, 2048 }, { 9, 1973, 2048 }, { 9, 1974, 2048 }, { 10, 1975, 2048 },
-   { 8, 1976, 2048 }, { 9, 1977, 2048 }, { 9, 1978, 2048 }, { 10, 1979, 2048 }, { 9, 1980, 2048 }, { 10, 1981, 2048 }, { 10, 1982, 2048 }, { 11, 1983, 2048 },
-   { 6, 1984, 2048 }, { 7, 1985, 2048 }, { 7, 1986, 2048 }, { 8, 1987, 2048 }, { 7, 1988, 2048 }, { 8, 1989, 2048 }, { 8, 1990, 2048 }, { 9, 1991, 2048 },
-   { 7, 1992, 2048 }, { 8, 1993, 2048 }, { 8, 1994, 2048 }, { 9, 1995, 2048 }, { 8, 1996, 2048 }, { 9, 1997, 2048 }, { 9, 1998, 2048 }, { 10, 1999, 2048 },
-   { 7, 2000, 2048 }, { 8, 2001, 2048 }, { 8, 2002, 2048 }, { 9, 2003, 2048 }, { 8, 2004, 2048 }, { 9, 2005, 2048 }, { 9, 2006, 2048 }, { 10, 2007, 2048 },
-   { 8, 2008, 2048 }, { 9, 2009, 2048 }, { 9, 2010, 2048 }, { 10, 2011, 2048 }, { 9, 2012, 2048 }, { 10, 2013, 2048 }, { 10, 2014, 2048 }, { 11, 2015, 2048 },
-   { 7, 2016, 2048 }, { 8, 2017, 2048 }, { 8, 2018, 2048 }, { 9, 2019, 2048 }, { 8, 2020, 2048 }, { 9, 2021, 2048 }, { 9, 2022, 2048 }, { 10, 2023, 2048 },
-   { 8, 2024, 2048 }, { 9, 2025, 2048 }, { 9, 2026, 2048 }, { 10, 2027, 2048 }, { 9, 2028, 2048 }, { 10, 2029, 2048 }, { 10, 2030, 2048 }, { 11, 2031, 2048 },
-   { 8, 2032, 2048 }, { 9, 2033, 2048 }, { 9, 2034, 2048 }, { 10, 2035, 2048 }, { 9, 2036, 2048 }, { 10, 2037, 2048 }, { 10, 2038, 2048 }, { 11, 2039, 2048 },
-   { 9, 2040, 2048 }, { 10, 2041, 2048 }, { 10, 2042, 2048 }, { 11, 2043, 2048 }, { 10, 2044, 2048 }, { 11, 2045, 2048 }, { 11, 2046, 2048 }, { 12, 2047, 2048 },
-#endif
-#endif
-#endif
-#endif
-#endif
-#endif
-};
-
-/* find a hole and free as required, return -1 if no hole found */
-static int _find_hole(void)
-{
-   unsigned x;
-   int      y, z;
-   for (z = -1, y = INT_MAX, x = 0; x < FP_ENTRIES; x++) {
-       if (fp_cache[x].lru_count < y && fp_cache[x].lock == 0) {
-          z = x;
-          y = fp_cache[x].lru_count;
-       }
-   }
-
-   /* decrease all */
-   for (x = 0; x < FP_ENTRIES; x++) {
-      if (fp_cache[x].lru_count > 3) {
-         --(fp_cache[x].lru_count);
-      }
-   }
-
-   /* free entry z */
-   if (z >= 0 && fp_cache[z].g) {
-      if (fp_cache[z].mu != NULL) {
-         mp_clear(fp_cache[z].mu);
-         fp_cache[z].mu = NULL;
-      }
-      ltc_ecc_del_point(fp_cache[z].g);
-      fp_cache[z].g  = NULL;
-      for (x = 0; x < (1U<<FP_LUT); x++) {
-         ltc_ecc_del_point(fp_cache[z].LUT[x]);
-         fp_cache[z].LUT[x] = NULL;
-      }
-      fp_cache[z].lru_count = 0;
-   }
-   return z;
-}
-
-/* determine if a base is already in the cache and if so, where */
-static int _find_base(ecc_point *g)
-{
-   int x;
-   for (x = 0; x < FP_ENTRIES; x++) {
-      if (fp_cache[x].g != NULL &&
-          mp_cmp(fp_cache[x].g->x, g->x) == LTC_MP_EQ &&
-          mp_cmp(fp_cache[x].g->y, g->y) == LTC_MP_EQ &&
-          mp_cmp(fp_cache[x].g->z, g->z) == LTC_MP_EQ) {
-         break;
-      }
-   }
-   if (x == FP_ENTRIES) {
-      x = -1;
-   }
-   return x;
-}
-
-/* add a new base to the cache */
-static int _add_entry(int idx, ecc_point *g)
-{
-   unsigned x, y;
-
-   /* allocate base and LUT */
-   fp_cache[idx].g = ltc_ecc_new_point();
-   if (fp_cache[idx].g == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* copy x and y */
-   if ((mp_copy(g->x, fp_cache[idx].g->x) != CRYPT_OK) ||
-       (mp_copy(g->y, fp_cache[idx].g->y) != CRYPT_OK) ||
-       (mp_copy(g->z, fp_cache[idx].g->z) != CRYPT_OK)) {
-      ltc_ecc_del_point(fp_cache[idx].g);
-      fp_cache[idx].g = NULL;
-      return CRYPT_MEM;
-   }
-
-   for (x = 0; x < (1U<<FP_LUT); x++) {
-      fp_cache[idx].LUT[x] = ltc_ecc_new_point();
-      if (fp_cache[idx].LUT[x] == NULL) {
-         for (y = 0; y < x; y++) {
-            ltc_ecc_del_point(fp_cache[idx].LUT[y]);
-            fp_cache[idx].LUT[y] = NULL;
-         }
-         ltc_ecc_del_point(fp_cache[idx].g);
-         fp_cache[idx].g         = NULL;
-         fp_cache[idx].lru_count = 0;
-         return CRYPT_MEM;
-      }
-   }
-
-   fp_cache[idx].lru_count = 0;
-   return CRYPT_OK;
-}
-
-/* build the LUT by spacing the bits of the input by #modulus/FP_LUT bits apart
- *
- * The algorithm builds patterns in increasing bit order by first making all
- * single bit input patterns, then all two bit input patterns and so on
- */
-static int _build_lut(int idx, void *modulus, void *mp, void *mu)
-{
-   unsigned x, y, err, bitlen, lut_gap;
-   void    *tmp;
-
-   tmp = NULL;
-
-   /* sanity check to make sure lut_order table is of correct size, should compile out to a NOP if true */
-   if ((sizeof(lut_orders) / sizeof(lut_orders[0])) < (1U<<FP_LUT)) {
-       err = CRYPT_INVALID_ARG;
-       goto DONE;
-   }
-
-   /* get bitlen and round up to next multiple of FP_LUT */
-   bitlen  = mp_unsigned_bin_size(modulus) << 3;
-   x       = bitlen % FP_LUT;
-   if (x) {
-      bitlen += FP_LUT - x;
-   }
-   lut_gap = bitlen / FP_LUT;
-
-   /* init the mu */
-   if ((err = mp_init_copy(&fp_cache[idx].mu, mu)) != CRYPT_OK) {
-      goto ERR;
-   }
-
-   /* copy base */
-   if ((mp_mulmod(fp_cache[idx].g->x, mu, modulus, fp_cache[idx].LUT[1]->x) != CRYPT_OK) ||
-       (mp_mulmod(fp_cache[idx].g->y, mu, modulus, fp_cache[idx].LUT[1]->y) != CRYPT_OK) ||
-       (mp_mulmod(fp_cache[idx].g->z, mu, modulus, fp_cache[idx].LUT[1]->z) != CRYPT_OK))        { goto ERR; }
-
-   /* make all single bit entries */
-   for (x = 1; x < FP_LUT; x++) {
-      if ((mp_copy(fp_cache[idx].LUT[1<<(x-1)]->x, fp_cache[idx].LUT[1<<x]->x) != CRYPT_OK) ||
-          (mp_copy(fp_cache[idx].LUT[1<<(x-1)]->y, fp_cache[idx].LUT[1<<x]->y) != CRYPT_OK) ||
-          (mp_copy(fp_cache[idx].LUT[1<<(x-1)]->z, fp_cache[idx].LUT[1<<x]->z) != CRYPT_OK))     { goto ERR; }
-
-      /* now double it bitlen/FP_LUT times */
-      for (y = 0; y < lut_gap; y++) {
-          if ((err = ltc_mp.ecc_ptdbl(fp_cache[idx].LUT[1<<x], fp_cache[idx].LUT[1<<x], modulus, mp)) != CRYPT_OK) {
-             goto ERR;
-          }
-      }
-   }
-
-   /* now make all entries in increase order of hamming weight */
-   for (x = 2; x <= FP_LUT; x++) {
-       for (y = 0; y < (1UL<<FP_LUT); y++) {
-           if (lut_orders[y].ham != (int)x) continue;
-
-           /* perform the add */
-           if ((err = ltc_mp.ecc_ptadd(fp_cache[idx].LUT[lut_orders[y].terma], fp_cache[idx].LUT[lut_orders[y].termb],
-                                       fp_cache[idx].LUT[y], modulus, mp)) != CRYPT_OK) {
-              goto ERR;
-           }
-       }
-   }
-
-   /* now map all entries back to affine space to make point addition faster */
-   if ((err = mp_init(&tmp)) != CRYPT_OK)                                                                    { goto ERR; }
-   for (x = 1; x < (1UL<<FP_LUT); x++) {
-       /* convert z to normal from montgomery */
-       if ((err = mp_montgomery_reduce(fp_cache[idx].LUT[x]->z, modulus, mp)) != CRYPT_OK)                   { goto ERR; }
-
-       /* invert it */
-       if ((err = mp_invmod(fp_cache[idx].LUT[x]->z, modulus, fp_cache[idx].LUT[x]->z)) != CRYPT_OK)         { goto ERR; }
-
-       /* now square it */
-       if ((err = mp_sqrmod(fp_cache[idx].LUT[x]->z, modulus, tmp)) != CRYPT_OK)                             { goto ERR; }
-
-       /* fix x */
-       if ((err = mp_mulmod(fp_cache[idx].LUT[x]->x, tmp, modulus, fp_cache[idx].LUT[x]->x)) != CRYPT_OK)    { goto ERR; }
-
-       /* get 1/z^3 */
-       if ((err = mp_mulmod(tmp, fp_cache[idx].LUT[x]->z, modulus, tmp)) != CRYPT_OK)                        { goto ERR; }
-
-       /* fix y */
-       if ((err = mp_mulmod(fp_cache[idx].LUT[x]->y, tmp, modulus, fp_cache[idx].LUT[x]->y)) != CRYPT_OK)    { goto ERR; }
-
-       /* free z */
-       mp_clear(fp_cache[idx].LUT[x]->z);
-       fp_cache[idx].LUT[x]->z = NULL;
-   }
-   mp_clear(tmp);
-
-   return CRYPT_OK;
-ERR:
-   err = CRYPT_MEM;
-DONE:
-   for (y = 0; y < (1U<<FP_LUT); y++) {
-      ltc_ecc_del_point(fp_cache[idx].LUT[y]);
-      fp_cache[idx].LUT[y] = NULL;
-   }
-   ltc_ecc_del_point(fp_cache[idx].g);
-   fp_cache[idx].g         = NULL;
-   fp_cache[idx].lru_count = 0;
-   if (fp_cache[idx].mu != NULL) {
-      mp_clear(fp_cache[idx].mu);
-      fp_cache[idx].mu = NULL;
-   }
-   if (tmp != NULL) {
-      mp_clear(tmp);
-   }
-   return err;
-}
-
-/* perform a fixed point ECC mulmod */
-static int _accel_fp_mul(int idx, void *k, ecc_point *R, void *modulus, void *mp, int map)
-{
-   unsigned char kb[128];
-   int      x;
-   unsigned y, z, err, bitlen, bitpos, lut_gap, first;
-   void     *tk, *order;
-
-   /* if it's smaller than modulus we fine */
-   if (mp_unsigned_bin_size(k) > mp_unsigned_bin_size(modulus)) {
-      /* find order */
-      y = mp_unsigned_bin_size(modulus);
-      for (x = 0; ltc_ecc_sets[x].size; x++) {
-         if (y <= (unsigned)ltc_ecc_sets[x].size) break;
-      }
-
-      /* back off if we are on the 521 bit curve */
-      if (y == 66) --x;
-
-      if ((err = mp_init(&order)) != CRYPT_OK) {
-         return err;
-      }
-      if ((err = mp_read_radix(order, ltc_ecc_sets[x].order, 16)) != CRYPT_OK) {
-         mp_clear(&order);
-         return err;
-      }
-
-      /* k must be less than modulus */
-      if (mp_cmp(k, order) != LTC_MP_LT) {
-         if ((err = mp_init(&tk)) != CRYPT_OK) {
-            mp_clear(order);
-            return err;
-         }
-         if ((err = mp_mod(k, order, tk)) != CRYPT_OK) {
-            mp_clear(tk);
-            mp_clear(order);
-            return err;
-         }
-      } else {
-         tk = k;
-      }
-      mp_clear(order);
-   } else {
-       tk = k;
-   }
-
-   /* get bitlen and round up to next multiple of FP_LUT */
-   bitlen  = mp_unsigned_bin_size(modulus) << 3;
-   x       = bitlen % FP_LUT;
-   if (x) {
-      bitlen += FP_LUT - x;
-   }
-   lut_gap = bitlen / FP_LUT;
-
-   /* get the k value */
-   if (mp_unsigned_bin_size(tk) > (sizeof(kb) - 2)) {
-      if (tk != k) {
-         mp_clear(tk);
-      }
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* store k */
-   zeromem(kb, sizeof(kb));
-   if ((err = mp_to_unsigned_bin(tk, kb)) != CRYPT_OK) {
-      if (tk != k) {
-         mp_clear(tk);
-      }
-      return err;
-   }
-
-   /* let's reverse kb so it's little endian */
-   x = 0;
-   y = mp_unsigned_bin_size(tk) - 1;
-   if (tk != k) {
-      mp_clear(tk);
-   }
-   while ((unsigned)x < y) {
-      z = kb[x]; kb[x] = kb[y]; kb[y] = z;
-      ++x; --y;
-   }
-
-   /* at this point we can start, yipee */
-   first = 1;
-   for (x = lut_gap-1; x >= 0; x--) {
-       /* extract FP_LUT bits from kb spread out by lut_gap bits and offset by x bits from the start */
-       bitpos = x;
-       for (y = z = 0; y < FP_LUT; y++) {
-          z |= ((kb[bitpos>>3] >> (bitpos&7)) & 1) << y;
-          bitpos += lut_gap;                               /* it's y*lut_gap + x, but here we can avoid the mult in each loop */
-       }
-
-       /* double if not first */
-       if (!first) {
-          if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) {
-             return err;
-          }
-       }
-
-       /* add if not first, otherwise copy */
-       if (!first && z) {
-          if ((err = ltc_mp.ecc_ptadd(R, fp_cache[idx].LUT[z], R, modulus, mp)) != CRYPT_OK) {
-             return err;
-          }
-       } else if (z) {
-          if ((mp_copy(fp_cache[idx].LUT[z]->x, R->x) != CRYPT_OK) ||
-              (mp_copy(fp_cache[idx].LUT[z]->y, R->y) != CRYPT_OK) ||
-              (mp_copy(fp_cache[idx].mu,        R->z) != CRYPT_OK)) { return CRYPT_MEM; }
-              first = 0;
-       }
-   }
-   z = 0;
-   zeromem(kb, sizeof(kb));
-   /* map R back from projective space */
-   if (map) {
-      err = ltc_ecc_map(R, modulus, mp);
-   } else {
-      err = CRYPT_OK;
-   }
-   return err;
-}
-
-#ifdef LTC_ECC_SHAMIR
-/* perform a fixed point ECC mulmod */
-static int _accel_fp_mul2add(int idx1, int idx2,
-                            void *kA, void *kB,
-                            ecc_point *R, void *modulus, void *mp)
-{
-   unsigned char kb[2][128];
-   int      x;
-   unsigned y, z, err, bitlen, bitpos, lut_gap, first, zA, zB;
-   void     *tka, *tkb, *order;
-
-   /* if it's smaller than modulus we fine */
-   if (mp_unsigned_bin_size(kA) > mp_unsigned_bin_size(modulus)) {
-      /* find order */
-      y = mp_unsigned_bin_size(modulus);
-      for (x = 0; ltc_ecc_sets[x].size; x++) {
-         if (y <= (unsigned)ltc_ecc_sets[x].size) break;
-      }
-
-      /* back off if we are on the 521 bit curve */
-      if (y == 66) --x;
-
-      if ((err = mp_init(&order)) != CRYPT_OK) {
-         return err;
-      }
-      if ((err = mp_read_radix(order, ltc_ecc_sets[x].order, 16)) != CRYPT_OK) {
-         mp_clear(&order);
-         return err;
-      }
-
-      /* kA must be less than modulus */
-      if (mp_cmp(kA, order) != LTC_MP_LT) {
-         if ((err = mp_init(&tka)) != CRYPT_OK) {
-            mp_clear(order);
-            return err;
-         }
-         if ((err = mp_mod(kA, order, tka)) != CRYPT_OK) {
-            mp_clear(tka);
-            mp_clear(order);
-            return err;
-         }
-      } else {
-         tka = kA;
-      }
-      mp_clear(order);
-   } else {
-      tka = kA;
-   }
-
-   /* if it's smaller than modulus we fine */
-   if (mp_unsigned_bin_size(kB) > mp_unsigned_bin_size(modulus)) {
-      /* find order */
-      y = mp_unsigned_bin_size(modulus);
-      for (x = 0; ltc_ecc_sets[x].size; x++) {
-         if (y <= (unsigned)ltc_ecc_sets[x].size) break;
-      }
-
-      /* back off if we are on the 521 bit curve */
-      if (y == 66) --x;
-
-      if ((err = mp_init(&order)) != CRYPT_OK) {
-         return err;
-      }
-      if ((err = mp_read_radix(order, ltc_ecc_sets[x].order, 16)) != CRYPT_OK) {
-         mp_clear(&order);
-         return err;
-      }
-
-      /* kB must be less than modulus */
-      if (mp_cmp(kB, order) != LTC_MP_LT) {
-         if ((err = mp_init(&tkb)) != CRYPT_OK) {
-            mp_clear(order);
-            return err;
-         }
-         if ((err = mp_mod(kB, order, tkb)) != CRYPT_OK) {
-            mp_clear(tkb);
-            mp_clear(order);
-            return err;
-         }
-      } else {
-         tkb = kB;
-      }
-      mp_clear(order);
-   } else {
-      tkb = kB;
-   }
-
-   /* get bitlen and round up to next multiple of FP_LUT */
-   bitlen  = mp_unsigned_bin_size(modulus) << 3;
-   x       = bitlen % FP_LUT;
-   if (x) {
-      bitlen += FP_LUT - x;
-   }
-   lut_gap = bitlen / FP_LUT;
-
-   /* get the k value */
-   if ((mp_unsigned_bin_size(tka) > (sizeof(kb[0]) - 2)) || (mp_unsigned_bin_size(tkb) > (sizeof(kb[0]) - 2))  ) {
-      if (tka != kA) {
-         mp_clear(tka);
-      }
-      if (tkb != kB) {
-         mp_clear(tkb);
-      }
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* store k */
-   zeromem(kb, sizeof(kb));
-   if ((err = mp_to_unsigned_bin(tka, kb[0])) != CRYPT_OK) {
-      if (tka != kA) {
-         mp_clear(tka);
-      }
-      if (tkb != kB) {
-         mp_clear(tkb);
-      }
-      return err;
-   }
-
-   /* let's reverse kb so it's little endian */
-   x = 0;
-   y = mp_unsigned_bin_size(tka) - 1;
-   if (tka != kA) {
-      mp_clear(tka);
-   }
-   while ((unsigned)x < y) {
-      z = kb[0][x]; kb[0][x] = kb[0][y]; kb[0][y] = z;
-      ++x; --y;
-   }
-
-   /* store b */
-   if ((err = mp_to_unsigned_bin(tkb, kb[1])) != CRYPT_OK) {
-      if (tkb != kB) {
-         mp_clear(tkb);
-      }
-      return err;
-   }
-
-   x = 0;
-   y = mp_unsigned_bin_size(tkb) - 1;
-   if (tkb != kB) {
-      mp_clear(tkb);
-   }
-   while ((unsigned)x < y) {
-      z = kb[1][x]; kb[1][x] = kb[1][y]; kb[1][y] = z;
-      ++x; --y;
-   }
-
-   /* at this point we can start, yipee */
-   first = 1;
-   for (x = lut_gap-1; x >= 0; x--) {
-       /* extract FP_LUT bits from kb spread out by lut_gap bits and offset by x bits from the start */
-       bitpos = x;
-       for (y = zA = zB = 0; y < FP_LUT; y++) {
-          zA |= ((kb[0][bitpos>>3] >> (bitpos&7)) & 1) << y;
-          zB |= ((kb[1][bitpos>>3] >> (bitpos&7)) & 1) << y;
-          bitpos += lut_gap;                               /* it's y*lut_gap + x, but here we can avoid the mult in each loop */
-       }
-
-       /* double if not first */
-       if (!first) {
-          if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) {
-             return err;
-          }
-       }
-
-       /* add if not first, otherwise copy */
-       if (!first) {
-          if (zA) {
-             if ((err = ltc_mp.ecc_ptadd(R, fp_cache[idx1].LUT[zA], R, modulus, mp)) != CRYPT_OK) {
-                return err;
-             }
-          }
-          if (zB) {
-             if ((err = ltc_mp.ecc_ptadd(R, fp_cache[idx2].LUT[zB], R, modulus, mp)) != CRYPT_OK) {
-                return err;
-             }
-          }
-       } else {
-          if (zA) {
-              if ((mp_copy(fp_cache[idx1].LUT[zA]->x, R->x) != CRYPT_OK) ||
-                 (mp_copy(fp_cache[idx1].LUT[zA]->y, R->y) != CRYPT_OK) ||
-                 (mp_copy(fp_cache[idx1].mu,        R->z) != CRYPT_OK)) { return CRYPT_MEM; }
-                 first = 0;
-          }
-          if (zB && first == 0) {
-             if (zB) {
-                if ((err = ltc_mp.ecc_ptadd(R, fp_cache[idx2].LUT[zB], R, modulus, mp)) != CRYPT_OK) {
-                   return err;
-                }
-             }
-          } else if (zB && first == 1) {
-              if ((mp_copy(fp_cache[idx2].LUT[zB]->x, R->x) != CRYPT_OK) ||
-                 (mp_copy(fp_cache[idx2].LUT[zB]->y, R->y) != CRYPT_OK) ||
-                 (mp_copy(fp_cache[idx2].mu,        R->z) != CRYPT_OK)) { return CRYPT_MEM; }
-                 first = 0;
-          }
-       }
-   }
-   zeromem(kb, sizeof(kb));
-   return ltc_ecc_map(R, modulus, mp);
-}
-
-/** ECC Fixed Point mulmod global
-  Computes kA*A + kB*B = C using Shamir's Trick
-  @param A        First point to multiply
-  @param kA       What to multiple A by
-  @param B        Second point to multiply
-  @param kB       What to multiple B by
-  @param C        [out] Destination point (can overlap with A or B)
-  @param modulus  Modulus for curve
-  @return CRYPT_OK on success
-*/
-int ltc_ecc_fp_mul2add(ecc_point *A, void *kA,
-                       ecc_point *B, void *kB,
-                       ecc_point *C, void *modulus)
-{
-   int  idx1, idx2, err;
-   void *mp, *mu;
-
-   mp = NULL;
-   mu = NULL;
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-      /* find point */
-      idx1 = _find_base(A);
-
-      /* no entry? */
-      if (idx1 == -1) {
-         /* find hole and add it */
-         if ((idx1 = _find_hole()) >= 0) {
-            if ((err = _add_entry(idx1, A)) != CRYPT_OK) {
-               goto LBL_ERR;
-            }
-         }
-      }
-      if (idx1 != -1) {
-         /* increment LRU */
-         ++(fp_cache[idx1].lru_count);
-      }
-
-      /* find point */
-      idx2 = _find_base(B);
-
-      /* no entry? */
-      if (idx2 == -1) {
-         /* find hole and add it */
-         if ((idx2 = _find_hole()) >= 0) {
-            if ((err = _add_entry(idx2, B)) != CRYPT_OK) {
-               goto LBL_ERR;
-            }
-         }
-      }
-      if (idx2 != -1) {
-         /* increment LRU */
-         ++(fp_cache[idx2].lru_count);
-      }
-
-      /* if it's 2 build the LUT, if it's higher just use the LUT */
-      if (idx1 >= 0 && fp_cache[idx1].lru_count == 2) {
-         /* compute mp */
-         if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto LBL_ERR; }
-
-         /* compute mu */
-         if ((err = mp_init(&mu)) != CRYPT_OK) {
-             goto LBL_ERR;
-         }
-         if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-            goto LBL_ERR;
-         }
-
-         /* build the LUT */
-         if ((err = _build_lut(idx1, modulus, mp, mu)) != CRYPT_OK) {
-             goto LBL_ERR;
-         }
-      }
-
-      /* if it's 2 build the LUT, if it's higher just use the LUT */
-      if (idx2 >= 0 && fp_cache[idx2].lru_count == 2) {
-         if (mp == NULL) {
-            /* compute mp */
-            if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto LBL_ERR; }
-
-            /* compute mu */
-            if ((err = mp_init(&mu)) != CRYPT_OK) {
-                goto LBL_ERR;
-            }
-            if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-               goto LBL_ERR;
-            }
-         }
-
-         /* build the LUT */
-         if ((err = _build_lut(idx2, modulus, mp, mu)) != CRYPT_OK) {
-             goto LBL_ERR;
-         }
-      }
-
-
-      if (idx1 >=0 && idx2 >= 0 && fp_cache[idx1].lru_count >= 2 && fp_cache[idx2].lru_count >= 2) {
-         if (mp == NULL) {
-            /* compute mp */
-            if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto LBL_ERR; }
-         }
-         err = _accel_fp_mul2add(idx1, idx2, kA, kB, C, modulus, mp);
-      } else {
-         err = ltc_ecc_mul2add(A, kA, B, kB, C, modulus);
-      }
-LBL_ERR:
-    LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-    if (mp != NULL) {
-       mp_montgomery_free(mp);
-    }
-    if (mu != NULL) {
-       mp_clear(mu);
-    }
-    return err;
-}
-#endif
-
-/** ECC Fixed Point mulmod global
-    @param k        The multiplicand
-    @param G        Base point to multiply
-    @param R        [out] Destination of product
-    @param modulus  The modulus for the curve
-    @param map      [boolean] If non-zero maps the point back to affine co-ordinates, otherwise it's left in jacobian-montgomery form
-    @return CRYPT_OK if successful
-*/
-int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map)
-{
-   int   idx, err;
-   void *mp, *mu;
-
-   mp = NULL;
-   mu = NULL;
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-      /* find point */
-      idx = _find_base(G);
-
-      /* no entry? */
-      if (idx == -1) {
-         /* find hole and add it */
-         idx = _find_hole();
-
-         if (idx >= 0) {
-            if ((err = _add_entry(idx, G)) != CRYPT_OK) {
-               goto LBL_ERR;
-            }
-         }
-      }
-      if (idx != -1) {
-         /* increment LRU */
-         ++(fp_cache[idx].lru_count);
-      }
-
-
-      /* if it's 2 build the LUT, if it's higher just use the LUT */
-      if (idx >= 0 && fp_cache[idx].lru_count == 2) {
-         /* compute mp */
-         if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto LBL_ERR; }
-
-         /* compute mu */
-         if ((err = mp_init(&mu)) != CRYPT_OK) {
-             goto LBL_ERR;
-         }
-         if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-            goto LBL_ERR;
-         }
-
-         /* build the LUT */
-         if ((err = _build_lut(idx, modulus, mp, mu)) != CRYPT_OK) {
-             goto LBL_ERR;
-         }
-      }
-
-      if (idx >= 0 && fp_cache[idx].lru_count >= 2) {
-         if (mp == NULL) {
-            /* compute mp */
-            if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto LBL_ERR; }
-         }
-         err = _accel_fp_mul(idx, k, R, modulus, mp, map);
-      } else {
-         err = ltc_ecc_mulmod(k, G, R, modulus, map);
-      }
-LBL_ERR:
-    LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-    if (mp != NULL) {
-       mp_montgomery_free(mp);
-    }
-    if (mu != NULL) {
-       mp_clear(mu);
-    }
-    return err;
-}
-
-/* helper function for freeing the cache ... must be called with the cache mutex locked */
-static void _ltc_ecc_fp_free_cache(void)
-{
-   unsigned x, y;
-   for (x = 0; x < FP_ENTRIES; x++) {
-      if (fp_cache[x].g != NULL) {
-         for (y = 0; y < (1U<<FP_LUT); y++) {
-            ltc_ecc_del_point(fp_cache[x].LUT[y]);
-            fp_cache[x].LUT[y] = NULL;
-         }
-         ltc_ecc_del_point(fp_cache[x].g);
-         fp_cache[x].g         = NULL;
-         if (fp_cache[x].mu != NULL) {
-            mp_clear(fp_cache[x].mu);
-            fp_cache[x].mu     = NULL;
-         }
-         fp_cache[x].lru_count = 0;
-         fp_cache[x].lock = 0;
-      }
-   }
-}
-
-/** Free the Fixed Point cache */
-void ltc_ecc_fp_free(void)
-{
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-   _ltc_ecc_fp_free_cache();
-   LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-}
-
-/** Add a point to the cache and initialize the LUT
-  @param g        The point to add
-  @param modulus  Modulus for curve
-  @param lock     Flag to indicate if this entry should be locked into the cache or not
-  @return CRYPT_OK on success
-*/
-int
-ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock)
-{
-   int idx;
-   int err;
-   void *mp = NULL;
-   void *mu = NULL;
-
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-   if ((idx = _find_base(g)) >= 0) {
-      /* it is already in the cache ... just check that the LUT is initialized */
-      if(fp_cache[idx].lru_count >= 2) {
-         LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-         return CRYPT_OK;
-      }
-   }
-
-   if(idx == -1 && (idx = _find_hole()) == -1) {
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-   if ((err = _add_entry(idx, g)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   /* compute mp */
-   if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* compute mu */
-   if ((err = mp_init(&mu)) != CRYPT_OK) {
-       goto LBL_ERR;
-   }
-   if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* build the LUT */
-   if ((err = _build_lut(idx, modulus, mp, mu)) != CRYPT_OK) {
-       goto LBL_ERR;
-   }
-   fp_cache[idx].lru_count = 2;
-   fp_cache[idx].lock = lock;
-LBL_ERR:
-   LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-   if (mp != NULL) {
-      mp_montgomery_free(mp);
-   }
-   if (mu != NULL) {
-      mp_clear(mu);
-   }
-   return err;
-}
-
-/** Prevent/permit the FP cache from being updated
-    @param flag        If flag is 0, remove cache lock (unlock), otherwise lock it
-*/
-void ltc_ecc_fp_tablelock(int lock)
-{
-   int i;
-
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-   for (i = 0; i < FP_ENTRIES; i++) {
-      fp_cache[i].lock = lock;
-   }
-   LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-}
-
-/** Export the current cache as a binary packet
-    @param out      [out] pointer to malloc'ed space containing the packet
-    @param outlen   [out] size of exported packet
-    @return CRYPT_OK if successful
-*/
-int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen)
-{
-   ltc_asn1_list *cache_entry;
-   unsigned int   i, j, k;
-   unsigned long  fp_entries, fp_lut, num_entries;
-   int            err;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   fp_entries  = FP_ENTRIES;
-   fp_lut      = FP_LUT;
-   num_entries = 0;
-
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-   /*
-    * build the list;
-      Cache DEFINITIONS ::=
-      BEGIN
-       CacheDump ::= SEQUENCE {
-         numEntries    SHORTINTEGER,
-         maxEntries    SHORTINTEGER,
-         numLUT        SHORTINTEGER,
-         cache         SEQUENCE OF INTEGER
-       }
-      END
-    *
-    */
-   /*
-    * The cache itself is a point (3 INTEGERS),
-    * the LUT as pairs of INTEGERS (2 * 1<<FP_LUT),
-    * and the mu INTEGER
-    */
-   cache_entry = XCALLOC(FP_ENTRIES*(2*(1U<<FP_LUT)+4)+3, sizeof(ltc_asn1_list));
-   if (cache_entry == NULL)
-      return CRYPT_MEM;
-   j = 1;   /* handle the zero'th element later */
-
-   LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_SHORT_INTEGER, &fp_entries, 1);
-   LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_SHORT_INTEGER, &fp_lut, 1);
-
-   for (i = 0; i < FP_ENTRIES; i++) {
-      /*
-       * do not save empty entries, or entries that have not yet had the lut built
-       */
-      if (fp_cache[i].g == NULL || fp_cache[i].lru_count < 2) {
-         continue;
-      }
-      num_entries++;
-      LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_INTEGER, fp_cache[i].g->x, 1);
-      LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_INTEGER, fp_cache[i].g->y, 1);
-      LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_INTEGER, fp_cache[i].g->z, 1);
-      for (k = 0; k < (1U<<FP_LUT); k++) {
-         LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_INTEGER, fp_cache[i].LUT[k]->x, 1);
-         LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_INTEGER, fp_cache[i].LUT[k]->y, 1);
-      }
-      LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_INTEGER, fp_cache[i].mu, 1);
-   }
-   LTC_SET_ASN1(cache_entry, j++, LTC_ASN1_EOL, 0, 0);
-
-   LTC_SET_ASN1(cache_entry, 0, LTC_ASN1_SHORT_INTEGER, &num_entries, 1);
-
-   if ((err = der_length_sequence(cache_entry, j, outlen)) != CRYPT_OK) {
-      goto save_err;
-   }
-   if ((*out = XMALLOC(*outlen)) == NULL) {
-      err = CRYPT_MEM;
-      goto save_err;
-   }
-   err = der_encode_sequence(cache_entry, j, *out, outlen);
-save_err:
-   XFREE(cache_entry);
-   LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-   return err;
-}
-
-/** Import a binary packet into the current cache
-    @param in      [in] pointer to packet
-    @param inlen   [in] size of packet (bytes)
-    @return CRYPT_OK if successful
-*/
-int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen)
-{
-   int            err;
-   ltc_asn1_list *asn1_list;
-   unsigned long  num_entries, fp_entries, fp_lut;
-   unsigned long  i, j;
-   unsigned int   x;
-
-   LTC_ARGCHK(in != NULL);
-   if (inlen == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* zero indecies */
-   i         = 0;
-   j         = 0;
-   asn1_list = NULL;
-
-   LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);
-   /*
-    * start with an empty cache
-    */
-   _ltc_ecc_fp_free_cache();
-
-   /*
-    * decode the input packet: It consists of a sequence with a few
-    * integers (including the FP_ENTRIES and FP_LUT sizes), followed by a
-    * SEQUENCE which is the cache itself.
-    *
-    * use standard decoding for the first part, then flexible for the second
-    */
-   if((err = der_decode_sequence_multi(in, inlen,
-                                       LTC_ASN1_SHORT_INTEGER, 1, &num_entries,
-                                       LTC_ASN1_SHORT_INTEGER, 1, &fp_entries,
-                                       LTC_ASN1_SHORT_INTEGER, 1, &fp_lut,
-                                       LTC_ASN1_EOL,           0, 0)) != CRYPT_OK) {
-      goto ERR_OUT;
-   }
-   if (fp_entries != FP_ENTRIES || fp_lut != FP_LUT || num_entries > fp_entries) {
-      err = CRYPT_INVALID_PACKET;
-      goto ERR_OUT;
-   }
-   if ((asn1_list = XCALLOC(3+num_entries*(4+2*(1<<FP_LUT))+1, sizeof(ltc_asn1_list))) == NULL) {
-      err = CRYPT_MEM;
-      goto ERR_OUT;
-   }
-   j = 0;
-   LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_SHORT_INTEGER, &num_entries, 1);
-   LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_SHORT_INTEGER, &fp_entries, 1);
-   LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_SHORT_INTEGER, &fp_lut, 1);
-   for (i = 0; i < num_entries; i++) {
-      if((fp_cache[i].g = ltc_ecc_new_point()) == NULL) {
-         err = CRYPT_MEM;
-         goto ERR_OUT;
-      }
-      LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_INTEGER, fp_cache[i].g->x, 1);
-      LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_INTEGER, fp_cache[i].g->y, 1);
-      LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_INTEGER, fp_cache[i].g->z, 1);
-      for (x = 0; x < (1U<<FP_LUT); x++) {
-         /* since we don't store z in the cache, don't use ltc_ecc_new_point()
-          * (which allocates space for z, only to have to free it later) */
-         ecc_point *p = XCALLOC(1, sizeof(*p));
-
-         if (p == NULL) {
-            err = CRYPT_MEM;
-            goto ERR_OUT;
-         }
-         fp_cache[i].LUT[x] = p;
-         if ((err = mp_init_multi(&p->x, &p->y, NULL)) != CRYPT_OK) {
-            goto ERR_OUT;
-         }
-         p->z = NULL;
-         LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_INTEGER, p->x, 1);
-         LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_INTEGER, p->y, 1);
-      }
-      if((err = mp_init(&fp_cache[i].mu)) != CRYPT_OK) {
-         goto ERR_OUT;
-      }
-      LTC_SET_ASN1(asn1_list, j++, LTC_ASN1_INTEGER, fp_cache[i].mu, 1);
-      fp_cache[i].lru_count = 3;
-      fp_cache[i].lock = 1;
-   }
-
-   if ((err = der_decode_sequence(in, inlen, asn1_list, j)) != CRYPT_OK) {
-      goto ERR_OUT;
-   }
-   XFREE(asn1_list);
-   LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-   return CRYPT_OK;
-ERR_OUT:
-   if(asn1_list)
-      XFREE(asn1_list);
-   _ltc_ecc_fp_free_cache();
-   LTC_MUTEX_UNLOCK(&ltc_ecc_fp_lock);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/math/ltm_desc.c b/libs/tomcrypt/src/math/ltm_desc.c
deleted file mode 100644
index 48ae1b209be9..000000000000
--- a/libs/tomcrypt/src/math/ltm_desc.c
+++ /dev/null
@@ -1,509 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#define DESC_DEF_ONLY
-#include "tomcrypt.h"
-
-#ifdef LTM_DESC
-
-#include <tommath.h>
-
-static const struct {
-    int mpi_code, ltc_code;
-} mpi_to_ltc_codes[] = {
-   { MP_OKAY ,  CRYPT_OK},
-   { MP_MEM  ,  CRYPT_MEM},
-   { MP_VAL  ,  CRYPT_INVALID_ARG},
-};
-
-/**
-   Convert a MPI error to a LTC error (Possibly the most powerful function ever!  Oh wait... no)
-   @param err    The error to convert
-   @return The equivalent LTC error code or CRYPT_ERROR if none found
-*/
-static int mpi_to_ltc_error(int err)
-{
-   int x;
-
-   for (x = 0; x < (int)(sizeof(mpi_to_ltc_codes)/sizeof(mpi_to_ltc_codes[0])); x++) {
-       if (err == mpi_to_ltc_codes[x].mpi_code) {
-          return mpi_to_ltc_codes[x].ltc_code;
-       }
-   }
-   return CRYPT_ERROR;
-}
-
-static int init(void **a)
-{
-   int err;
-
-   LTC_ARGCHK(a != NULL);
-
-   *a = XCALLOC(1, sizeof(mp_int));
-   if (*a == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = mpi_to_ltc_error(mp_init(*a))) != CRYPT_OK) {
-      XFREE(*a);
-   }
-   return err;
-}
-
-static void deinit(void *a)
-{
-   LTC_ARGCHKVD(a != NULL);
-   mp_clear(a);
-   XFREE(a);
-}
-
-static int neg(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_neg(a, b));
-}
-
-static int copy(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_copy(a, b));
-}
-
-static int init_copy(void **a, void *b)
-{
-   if (init(a) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-   return copy(b, *a);
-}
-
-/* ---- trivial ---- */
-static int set_int(void *a, ltc_mp_digit b)
-{
-   LTC_ARGCHK(a != NULL);
-   return mpi_to_ltc_error(mp_set_int(a, b));
-}
-
-static unsigned long get_int(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return mp_get_int(a);
-}
-
-static ltc_mp_digit get_digit(void *a, int n)
-{
-   mp_int *A;
-   LTC_ARGCHK(a != NULL);
-   A = a;
-   return (n >= A->used || n < 0) ? 0 : A->dp[n];
-}
-
-static int get_digit_count(void *a)
-{
-   mp_int *A;
-   LTC_ARGCHK(a != NULL);
-   A = a;
-   return A->used;
-}
-
-static int compare(void *a, void *b)
-{
-   int ret;
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   ret = mp_cmp(a, b);
-   switch (ret) {
-      case MP_LT: return LTC_MP_LT;
-      case MP_EQ: return LTC_MP_EQ;
-      case MP_GT: return LTC_MP_GT;
-      default:    return 0;
-   }
-}
-
-static int compare_d(void *a, ltc_mp_digit b)
-{
-   int ret;
-   LTC_ARGCHK(a != NULL);
-   ret = mp_cmp_d(a, b);
-   switch (ret) {
-      case MP_LT: return LTC_MP_LT;
-      case MP_EQ: return LTC_MP_EQ;
-      case MP_GT: return LTC_MP_GT;
-      default:    return 0;
-   }
-}
-
-static int count_bits(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return mp_count_bits(a);
-}
-
-static int count_lsb_bits(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return mp_cnt_lsb(a);
-}
-
-
-static int twoexpt(void *a, int n)
-{
-   LTC_ARGCHK(a != NULL);
-   return mpi_to_ltc_error(mp_2expt(a, n));
-}
-
-/* ---- conversions ---- */
-
-/* read ascii string */
-static int read_radix(void *a, const char *b, int radix)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_read_radix(a, b, radix));
-}
-
-/* write one */
-static int write_radix(void *a, char *b, int radix)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_toradix(a, b, radix));
-}
-
-/* get size as unsigned char string */
-static unsigned long unsigned_size(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return mp_unsigned_bin_size(a);
-}
-
-/* store */
-static int unsigned_write(void *a, unsigned char *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_to_unsigned_bin(a, b));
-}
-
-/* read */
-static int unsigned_read(void *a, unsigned char *b, unsigned long len)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_read_unsigned_bin(a, b, len));
-}
-
-/* add */
-static int add(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_add(a, b, c));
-}
-
-static int addi(void *a, ltc_mp_digit b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_add_d(a, b, c));
-}
-
-/* sub */
-static int sub(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_sub(a, b, c));
-}
-
-static int subi(void *a, ltc_mp_digit b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_sub_d(a, b, c));
-}
-
-/* mul */
-static int mul(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_mul(a, b, c));
-}
-
-static int muli(void *a, ltc_mp_digit b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_mul_d(a, b, c));
-}
-
-/* sqr */
-static int sqr(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_sqr(a, b));
-}
-
-/* div */
-static int divide(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_div(a, b, c, d));
-}
-
-static int div_2(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_div_2(a, b));
-}
-
-/* modi */
-static int modi(void *a, ltc_mp_digit b, ltc_mp_digit *c)
-{
-   mp_digit tmp;
-   int      err;
-
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-
-   if ((err = mpi_to_ltc_error(mp_mod_d(a, b, &tmp))) != CRYPT_OK) {
-      return err;
-   }
-   *c = tmp;
-   return CRYPT_OK;
-}
-
-/* gcd */
-static int gcd(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_gcd(a, b, c));
-}
-
-/* lcm */
-static int lcm(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_lcm(a, b, c));
-}
-
-static int addmod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return mpi_to_ltc_error(mp_addmod(a,b,c,d));
-}
-
-static int submod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return mpi_to_ltc_error(mp_submod(a,b,c,d));
-}
-
-static int mulmod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return mpi_to_ltc_error(mp_mulmod(a,b,c,d));
-}
-
-static int sqrmod(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_sqrmod(a,b,c));
-}
-
-/* invmod */
-static int invmod(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_invmod(a, b, c));
-}
-
-/* setup */
-static int montgomery_setup(void *a, void **b)
-{
-   int err;
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   *b = XCALLOC(1, sizeof(mp_digit));
-   if (*b == NULL) {
-      return CRYPT_MEM;
-   }
-   if ((err = mpi_to_ltc_error(mp_montgomery_setup(a, (mp_digit *)*b))) != CRYPT_OK) {
-      XFREE(*b);
-   }
-   return err;
-}
-
-/* get normalization value */
-static int montgomery_normalization(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return mpi_to_ltc_error(mp_montgomery_calc_normalization(a, b));
-}
-
-/* reduce */
-static int montgomery_reduce(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return mpi_to_ltc_error(mp_montgomery_reduce(a, b, *((mp_digit *)c)));
-}
-
-/* clean up */
-static void montgomery_deinit(void *a)
-{
-   XFREE(a);
-}
-
-static int exptmod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return mpi_to_ltc_error(mp_exptmod(a,b,c,d));
-}
-
-static int isprime(void *a, int b, int *c)
-{
-   int err;
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   if (b == 0) {
-       b = LTC_MILLER_RABIN_REPS;
-   } /* if */
-   err = mpi_to_ltc_error(mp_prime_is_prime(a, b, c));
-   *c = (*c == MP_YES) ? LTC_MP_YES : LTC_MP_NO;
-   return err;
-}
-
-static int set_rand(void *a, int size)
-{
-   LTC_ARGCHK(a != NULL);
-   return mpi_to_ltc_error(mp_rand(a, size));
-}
-
-const ltc_math_descriptor ltm_desc = {
-
-   "LibTomMath",
-   (int)DIGIT_BIT,
-
-   &init,
-   &init_copy,
-   &deinit,
-
-   &neg,
-   &copy,
-
-   &set_int,
-   &get_int,
-   &get_digit,
-   &get_digit_count,
-   &compare,
-   &compare_d,
-   &count_bits,
-   &count_lsb_bits,
-   &twoexpt,
-
-   &read_radix,
-   &write_radix,
-   &unsigned_size,
-   &unsigned_write,
-   &unsigned_read,
-
-   &add,
-   &addi,
-   &sub,
-   &subi,
-   &mul,
-   &muli,
-   &sqr,
-   &divide,
-   &div_2,
-   &modi,
-   &gcd,
-   &lcm,
-
-   &mulmod,
-   &sqrmod,
-   &invmod,
-
-   &montgomery_setup,
-   &montgomery_normalization,
-   &montgomery_reduce,
-   &montgomery_deinit,
-
-   &exptmod,
-   &isprime,
-
-#ifdef LTC_MECC
-#ifdef LTC_MECC_FP
-   &ltc_ecc_fp_mulmod,
-#else
-   &ltc_ecc_mulmod,
-#endif
-   &ltc_ecc_projective_add_point,
-   &ltc_ecc_projective_dbl_point,
-   &ltc_ecc_map,
-#ifdef LTC_ECC_SHAMIR
-#ifdef LTC_MECC_FP
-   &ltc_ecc_fp_mul2add,
-#else
-   &ltc_ecc_mul2add,
-#endif /* LTC_MECC_FP */
-#else
-   NULL,
-#endif /* LTC_ECC_SHAMIR */
-#else
-   NULL, NULL, NULL, NULL, NULL,
-#endif /* LTC_MECC */
-
-#ifdef LTC_MRSA
-   &rsa_make_key,
-   &rsa_exptmod,
-#else
-   NULL, NULL,
-#endif
-   &addmod,
-   &submod,
-
-   &set_rand,
-
-};
-
-
-#endif
diff --git a/libs/tomcrypt/src/math/multi.c b/libs/tomcrypt/src/math/multi.c
deleted file mode 100644
index 5d7721e57326..000000000000
--- a/libs/tomcrypt/src/math/multi.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_MPI
-#include <stdarg.h>
-
-int ltc_init_multi(void **a, ...)
-{
-   void    **cur = a;
-   int       np  = 0;
-   va_list   args;
-
-   va_start(args, a);
-   while (cur != NULL) {
-       if (mp_init(cur) != CRYPT_OK) {
-          /* failed */
-          va_list clean_list;
-
-          va_start(clean_list, a);
-          cur = a;
-          while (np--) {
-              mp_clear(*cur);
-              cur = va_arg(clean_list, void**);
-          }
-          va_end(clean_list);
-          va_end(args);
-          return CRYPT_MEM;
-       }
-       ++np;
-       cur = va_arg(args, void**);
-   }
-   va_end(args);
-   return CRYPT_OK;
-}
-
-void ltc_deinit_multi(void *a, ...)
-{
-   void     *cur = a;
-   va_list   args;
-
-   va_start(args, a);
-   while (cur != NULL) {
-       mp_clear(cur);
-       cur = va_arg(args, void *);
-   }
-   va_end(args);
-}
-
-void ltc_cleanup_multi(void **a, ...)
-{
-   void **cur = a;
-   va_list args;
-
-   va_start(args, a);
-   while (cur != NULL) {
-      if (*cur != NULL) {
-         mp_clear(*cur);
-         *cur = NULL;
-      }
-      cur = va_arg(args, void**);
-   }
-   va_end(args);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/math/radix_to_bin.c b/libs/tomcrypt/src/math/radix_to_bin.c
deleted file mode 100644
index 345f0d099b19..000000000000
--- a/libs/tomcrypt/src/math/radix_to_bin.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file radix_to_bin.c
-   Convert data from a specific radix to binary.
-   Steffen Jaeckel
-*/
-
-/**
-   Convert data from a specific radix to binary
-
-      The default MPI descriptors #ltm_desc, #tfm_desc and #gmp_desc
-      have the following restrictions on parameters:
-
-        \p in    - NUL-terminated char buffer
-
-        \p radix - 2..64
-
-   @param in    The input
-   @param radix The radix of the input
-   @param out   The output buffer
-   @param len   [in/out] The length of the output buffer
-
-   @return CRYPT_OK on success.
-*/
-int radix_to_bin(const void *in, int radix, void *out, unsigned long *len)
-{
-   unsigned long l;
-   void* mpi;
-   int err;
-
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(len != NULL);
-
-   if ((err = mp_init(&mpi)) != CRYPT_OK) return err;
-   if ((err = mp_read_radix(mpi, in, radix)) != CRYPT_OK) goto LBL_ERR;
-
-   if ((l = mp_unsigned_bin_size(mpi)) > *len) {
-      *len = l;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-   *len = l;
-
-   if ((err = mp_to_unsigned_bin(mpi, out)) != CRYPT_OK) goto LBL_ERR;
-
-LBL_ERR:
-   mp_clear(mpi);
-   return err;
-}
diff --git a/libs/tomcrypt/src/math/rand_bn.c b/libs/tomcrypt/src/math/rand_bn.c
deleted file mode 100644
index 8fccd333264e..000000000000
--- a/libs/tomcrypt/src/math/rand_bn.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#if defined(LTC_MDSA) || defined(LTC_MECC)
-/**
-  Generate a random number N with given bitlength (note: MSB can be 0)
-*/
-
-int rand_bn_bits(void *N, int bits, prng_state *prng, int wprng)
-{
-   int res, bytes;
-   unsigned char *buf, mask;
-
-   LTC_ARGCHK(N != NULL);
-   LTC_ARGCHK(bits > 1);
-
-   /* check PRNG */
-   if ((res = prng_is_valid(wprng)) != CRYPT_OK) return res;
-
-   bytes = (bits+7) >> 3;
-   mask = 0xff << (8 - bits % 8);
-
-   /* allocate buffer */
-   if ((buf = XCALLOC(1, bytes)) == NULL) return CRYPT_MEM;
-
-   /* generate random bytes */
-   if (prng_descriptor[wprng].read(buf, bytes, prng) != (unsigned long)bytes) {
-      res = CRYPT_ERROR_READPRNG;
-      goto cleanup;
-   }
-   /* mask bits */
-   buf[0] &= ~mask;
-   /* load value */
-   if ((res = mp_read_unsigned_bin(N, buf, bytes)) != CRYPT_OK) goto cleanup;
-
-   res = CRYPT_OK;
-
-cleanup:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, bytes);
-#endif
-   XFREE(buf);
-   return res;
-}
-
-/**
-  Generate a random number N in a range: 1 <= N < limit
-*/
-int rand_bn_upto(void *N, void *limit, prng_state *prng, int wprng)
-{
-   int res, bits;
-
-   LTC_ARGCHK(N != NULL);
-   LTC_ARGCHK(limit != NULL);
-
-   bits = mp_count_bits(limit);
-   do {
-     res = rand_bn_bits(N, bits, prng, wprng);
-     if (res != CRYPT_OK) return res;
-   } while (mp_cmp_d(N, 0) != LTC_MP_GT || mp_cmp(N, limit) != LTC_MP_LT);
-
-   return CRYPT_OK;
-}
-#endif
diff --git a/libs/tomcrypt/src/math/rand_prime.c b/libs/tomcrypt/src/math/rand_prime.c
deleted file mode 100644
index c016f3b5ce3c..000000000000
--- a/libs/tomcrypt/src/math/rand_prime.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#if defined(LTC_MRSA) || (!defined(LTC_NO_MATH) && !defined(LTC_NO_PRNGS))
-
-/**
-  @file rand_prime.c
-  Generate a random prime, Tom St Denis
-*/
-
-#define USE_BBS 1
-
-int rand_prime(void *N, long len, prng_state *prng, int wprng)
-{
-   int            err, res, type;
-   unsigned char *buf;
-
-   LTC_ARGCHK(N != NULL);
-
-   /* get type */
-   if (len < 0) {
-      type = USE_BBS;
-      len = -len;
-   } else {
-      type = 0;
-   }
-
-   /* allow sizes between 2 and 512 bytes for a prime size */
-   if (len < 2 || len > 512) {
-      return CRYPT_INVALID_PRIME_SIZE;
-   }
-
-   /* valid PRNG? Better be! */
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* allocate buffer to work with */
-   buf = XCALLOC(1, len);
-   if (buf == NULL) {
-       return CRYPT_MEM;
-   }
-
-   do {
-      /* generate value */
-      if (prng_descriptor[wprng].read(buf, len, prng) != (unsigned long)len) {
-         XFREE(buf);
-         return CRYPT_ERROR_READPRNG;
-      }
-
-      /* munge bits */
-      buf[0]     |= 0x80 | 0x40;
-      buf[len-1] |= 0x01 | ((type & USE_BBS) ? 0x02 : 0x00);
-
-      /* load value */
-      if ((err = mp_read_unsigned_bin(N, buf, len)) != CRYPT_OK) {
-         XFREE(buf);
-         return err;
-      }
-
-      /* test */
-      if ((err = mp_prime_is_prime(N, LTC_MILLER_RABIN_REPS, &res)) != CRYPT_OK) {
-         XFREE(buf);
-         return err;
-      }
-   } while (res == LTC_MP_NO);
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, len);
-#endif
-
-   XFREE(buf);
-   return CRYPT_OK;
-}
-
-#endif /* LTC_NO_MATH */
diff --git a/libs/tomcrypt/src/math/tfm_desc.c b/libs/tomcrypt/src/math/tfm_desc.c
deleted file mode 100644
index aa95ea6a712d..000000000000
--- a/libs/tomcrypt/src/math/tfm_desc.c
+++ /dev/null
@@ -1,803 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#define DESC_DEF_ONLY
-#include "tomcrypt.h"
-
-#ifdef TFM_DESC
-
-#include <tfm.h>
-
-static const struct {
-    int tfm_code, ltc_code;
-} tfm_to_ltc_codes[] = {
-   { FP_OKAY ,  CRYPT_OK},
-   { FP_MEM  ,  CRYPT_MEM},
-   { FP_VAL  ,  CRYPT_INVALID_ARG},
-};
-
-/**
-   Convert a tfm error to a LTC error (Possibly the most powerful function ever!  Oh wait... no)
-   @param err    The error to convert
-   @return The equivalent LTC error code or CRYPT_ERROR if none found
-*/
-static int tfm_to_ltc_error(int err)
-{
-   int x;
-
-   for (x = 0; x < (int)(sizeof(tfm_to_ltc_codes)/sizeof(tfm_to_ltc_codes[0])); x++) {
-       if (err == tfm_to_ltc_codes[x].tfm_code) {
-          return tfm_to_ltc_codes[x].ltc_code;
-       }
-   }
-   return CRYPT_ERROR;
-}
-
-static int init(void **a)
-{
-   LTC_ARGCHK(a != NULL);
-
-   *a = XCALLOC(1, sizeof(fp_int));
-   if (*a == NULL) {
-      return CRYPT_MEM;
-   }
-   fp_init(*a);
-   return CRYPT_OK;
-}
-
-static void deinit(void *a)
-{
-   LTC_ARGCHKVD(a != NULL);
-   XFREE(a);
-}
-
-static int neg(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_neg(((fp_int*)a), ((fp_int*)b));
-   return CRYPT_OK;
-}
-
-static int copy(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_copy(a, b);
-   return CRYPT_OK;
-}
-
-static int init_copy(void **a, void *b)
-{
-   if (init(a) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-   return copy(b, *a);
-}
-
-/* ---- trivial ---- */
-static int set_int(void *a, ltc_mp_digit b)
-{
-   LTC_ARGCHK(a != NULL);
-   fp_set(a, b);
-   return CRYPT_OK;
-}
-
-static unsigned long get_int(void *a)
-{
-   fp_int *A;
-   LTC_ARGCHK(a != NULL);
-   A = a;
-   return A->used > 0 ? A->dp[0] : 0;
-}
-
-static ltc_mp_digit get_digit(void *a, int n)
-{
-   fp_int *A;
-   LTC_ARGCHK(a != NULL);
-   A = a;
-   return (n >= A->used || n < 0) ? 0 : A->dp[n];
-}
-
-static int get_digit_count(void *a)
-{
-   fp_int *A;
-   LTC_ARGCHK(a != NULL);
-   A = a;
-   return A->used;
-}
-
-static int compare(void *a, void *b)
-{
-   int ret;
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   ret = fp_cmp(a, b);
-   switch (ret) {
-      case FP_LT: return LTC_MP_LT;
-      case FP_EQ: return LTC_MP_EQ;
-      case FP_GT: return LTC_MP_GT;
-   }
-   return 0;
-}
-
-static int compare_d(void *a, ltc_mp_digit b)
-{
-   int ret;
-   LTC_ARGCHK(a != NULL);
-   ret = fp_cmp_d(a, b);
-   switch (ret) {
-      case FP_LT: return LTC_MP_LT;
-      case FP_EQ: return LTC_MP_EQ;
-      case FP_GT: return LTC_MP_GT;
-   }
-   return 0;
-}
-
-static int count_bits(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return fp_count_bits(a);
-}
-
-static int count_lsb_bits(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return fp_cnt_lsb(a);
-}
-
-static int twoexpt(void *a, int n)
-{
-   LTC_ARGCHK(a != NULL);
-   fp_2expt(a, n);
-   return CRYPT_OK;
-}
-
-/* ---- conversions ---- */
-
-/* read ascii string */
-static int read_radix(void *a, const char *b, int radix)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return tfm_to_ltc_error(fp_read_radix(a, (char *)b, radix));
-}
-
-/* write one */
-static int write_radix(void *a, char *b, int radix)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return tfm_to_ltc_error(fp_toradix(a, b, radix));
-}
-
-/* get size as unsigned char string */
-static unsigned long unsigned_size(void *a)
-{
-   LTC_ARGCHK(a != NULL);
-   return fp_unsigned_bin_size(a);
-}
-
-/* store */
-static int unsigned_write(void *a, unsigned char *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_to_unsigned_bin(a, b);
-   return CRYPT_OK;
-}
-
-/* read */
-static int unsigned_read(void *a, unsigned char *b, unsigned long len)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_read_unsigned_bin(a, b, len);
-   return CRYPT_OK;
-}
-
-/* add */
-static int add(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_add(a, b, c);
-   return CRYPT_OK;
-}
-
-static int addi(void *a, ltc_mp_digit b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_add_d(a, b, c);
-   return CRYPT_OK;
-}
-
-/* sub */
-static int sub(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_sub(a, b, c);
-   return CRYPT_OK;
-}
-
-static int subi(void *a, ltc_mp_digit b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_sub_d(a, b, c);
-   return CRYPT_OK;
-}
-
-/* mul */
-static int mul(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_mul(a, b, c);
-   return CRYPT_OK;
-}
-
-static int muli(void *a, ltc_mp_digit b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_mul_d(a, b, c);
-   return CRYPT_OK;
-}
-
-/* sqr */
-static int sqr(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_sqr(a, b);
-   return CRYPT_OK;
-}
-
-/* div */
-static int divide(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   return tfm_to_ltc_error(fp_div(a, b, c, d));
-}
-
-static int div_2(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_div_2(a, b);
-   return CRYPT_OK;
-}
-
-/* modi */
-static int modi(void *a, ltc_mp_digit b, ltc_mp_digit *c)
-{
-   fp_digit tmp;
-   int      err;
-
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-
-   if ((err = tfm_to_ltc_error(fp_mod_d(a, b, &tmp))) != CRYPT_OK) {
-      return err;
-   }
-   *c = tmp;
-   return CRYPT_OK;
-}
-
-/* gcd */
-static int gcd(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_gcd(a, b, c);
-   return CRYPT_OK;
-}
-
-/* lcm */
-static int lcm(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_lcm(a, b, c);
-   return CRYPT_OK;
-}
-
-static int addmod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return tfm_to_ltc_error(fp_addmod(a,b,c,d));
-}
-
-static int submod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return tfm_to_ltc_error(fp_submod(a,b,c,d));
-}
-
-static int mulmod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return tfm_to_ltc_error(fp_mulmod(a,b,c,d));
-}
-
-static int sqrmod(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return tfm_to_ltc_error(fp_sqrmod(a,b,c));
-}
-
-/* invmod */
-static int invmod(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   return tfm_to_ltc_error(fp_invmod(a, b, c));
-}
-
-/* setup */
-static int montgomery_setup(void *a, void **b)
-{
-   int err;
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   *b = XCALLOC(1, sizeof(fp_digit));
-   if (*b == NULL) {
-      return CRYPT_MEM;
-   }
-   if ((err = tfm_to_ltc_error(fp_montgomery_setup(a, (fp_digit *)*b))) != CRYPT_OK) {
-      XFREE(*b);
-   }
-   return err;
-}
-
-/* get normalization value */
-static int montgomery_normalization(void *a, void *b)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   fp_montgomery_calc_normalization(a, b);
-   return CRYPT_OK;
-}
-
-/* reduce */
-static int montgomery_reduce(void *a, void *b, void *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   fp_montgomery_reduce(a, b, *((fp_digit *)c));
-   return CRYPT_OK;
-}
-
-/* clean up */
-static void montgomery_deinit(void *a)
-{
-   XFREE(a);
-}
-
-static int exptmod(void *a, void *b, void *c, void *d)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-   LTC_ARGCHK(c != NULL);
-   LTC_ARGCHK(d != NULL);
-   return tfm_to_ltc_error(fp_exptmod(a,b,c,d));
-}
-
-static int isprime(void *a, int b, int *c)
-{
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(c != NULL);
-   if (b == 0) {
-       b = LTC_MILLER_RABIN_REPS;
-   } /* if */
-   *c = (fp_isprime_ex(a, b) == FP_YES) ? LTC_MP_YES : LTC_MP_NO;
-   return CRYPT_OK;
-}
-
-#if defined(LTC_MECC) && defined(LTC_MECC_ACCEL)
-
-static int tfm_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *Mp)
-{
-   fp_int t1, t2;
-   fp_digit mp;
-
-   LTC_ARGCHK(P       != NULL);
-   LTC_ARGCHK(R       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-   LTC_ARGCHK(Mp      != NULL);
-
-   mp = *((fp_digit*)Mp);
-
-   fp_init(&t1);
-   fp_init(&t2);
-
-   if (P != R) {
-      fp_copy(P->x, R->x);
-      fp_copy(P->y, R->y);
-      fp_copy(P->z, R->z);
-   }
-
-   /* t1 = Z * Z */
-   fp_sqr(R->z, &t1);
-   fp_montgomery_reduce(&t1, modulus, mp);
-   /* Z = Y * Z */
-   fp_mul(R->z, R->y, R->z);
-   fp_montgomery_reduce(R->z, modulus, mp);
-   /* Z = 2Z */
-   fp_add(R->z, R->z, R->z);
-   if (fp_cmp(R->z, modulus) != FP_LT) {
-      fp_sub(R->z, modulus, R->z);
-   }
-
-   /* &t2 = X - T1 */
-   fp_sub(R->x, &t1, &t2);
-   if (fp_cmp_d(&t2, 0) == FP_LT) {
-      fp_add(&t2, modulus, &t2);
-   }
-   /* T1 = X + T1 */
-   fp_add(&t1, R->x, &t1);
-   if (fp_cmp(&t1, modulus) != FP_LT) {
-      fp_sub(&t1, modulus, &t1);
-   }
-   /* T2 = T1 * T2 */
-   fp_mul(&t1, &t2, &t2);
-   fp_montgomery_reduce(&t2, modulus, mp);
-   /* T1 = 2T2 */
-   fp_add(&t2, &t2, &t1);
-   if (fp_cmp(&t1, modulus) != FP_LT) {
-      fp_sub(&t1, modulus, &t1);
-   }
-   /* T1 = T1 + T2 */
-   fp_add(&t1, &t2, &t1);
-   if (fp_cmp(&t1, modulus) != FP_LT) {
-      fp_sub(&t1, modulus, &t1);
-   }
-
-   /* Y = 2Y */
-   fp_add(R->y, R->y, R->y);
-   if (fp_cmp(R->y, modulus) != FP_LT) {
-      fp_sub(R->y, modulus, R->y);
-   }
-   /* Y = Y * Y */
-   fp_sqr(R->y, R->y);
-   fp_montgomery_reduce(R->y, modulus, mp);
-   /* T2 = Y * Y */
-   fp_sqr(R->y, &t2);
-   fp_montgomery_reduce(&t2, modulus, mp);
-   /* T2 = T2/2 */
-   if (fp_isodd(&t2)) {
-      fp_add(&t2, modulus, &t2);
-   }
-   fp_div_2(&t2, &t2);
-   /* Y = Y * X */
-   fp_mul(R->y, R->x, R->y);
-   fp_montgomery_reduce(R->y, modulus, mp);
-
-   /* X  = T1 * T1 */
-   fp_sqr(&t1, R->x);
-   fp_montgomery_reduce(R->x, modulus, mp);
-   /* X = X - Y */
-   fp_sub(R->x, R->y, R->x);
-   if (fp_cmp_d(R->x, 0) == FP_LT) {
-      fp_add(R->x, modulus, R->x);
-   }
-   /* X = X - Y */
-   fp_sub(R->x, R->y, R->x);
-   if (fp_cmp_d(R->x, 0) == FP_LT) {
-      fp_add(R->x, modulus, R->x);
-   }
-
-   /* Y = Y - X */
-   fp_sub(R->y, R->x, R->y);
-   if (fp_cmp_d(R->y, 0) == FP_LT) {
-      fp_add(R->y, modulus, R->y);
-   }
-   /* Y = Y * T1 */
-   fp_mul(R->y, &t1, R->y);
-   fp_montgomery_reduce(R->y, modulus, mp);
-   /* Y = Y - T2 */
-   fp_sub(R->y, &t2, R->y);
-   if (fp_cmp_d(R->y, 0) == FP_LT) {
-      fp_add(R->y, modulus, R->y);
-   }
-
-   return CRYPT_OK;
-}
-
-/**
-   Add two ECC points
-   @param P        The point to add
-   @param Q        The point to add
-   @param R        [out] The destination of the double
-   @param modulus  The modulus of the field the ECC curve is in
-   @param Mp       The "b" value from montgomery_setup()
-   @return CRYPT_OK on success
-*/
-static int tfm_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *Mp)
-{
-   fp_int  t1, t2, x, y, z;
-   fp_digit mp;
-
-   LTC_ARGCHK(P       != NULL);
-   LTC_ARGCHK(Q       != NULL);
-   LTC_ARGCHK(R       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-   LTC_ARGCHK(Mp      != NULL);
-
-   mp = *((fp_digit*)Mp);
-
-   fp_init(&t1);
-   fp_init(&t2);
-   fp_init(&x);
-   fp_init(&y);
-   fp_init(&z);
-
-   /* should we dbl instead? */
-   fp_sub(modulus, Q->y, &t1);
-   if ( (fp_cmp(P->x, Q->x) == FP_EQ) &&
-        (Q->z != NULL && fp_cmp(P->z, Q->z) == FP_EQ) &&
-        (fp_cmp(P->y, Q->y) == FP_EQ || fp_cmp(P->y, &t1) == FP_EQ)) {
-        return tfm_ecc_projective_dbl_point(P, R, modulus, Mp);
-   }
-
-   fp_copy(P->x, &x);
-   fp_copy(P->y, &y);
-   fp_copy(P->z, &z);
-
-   /* if Z is one then these are no-operations */
-   if (Q->z != NULL) {
-      /* T1 = Z' * Z' */
-      fp_sqr(Q->z, &t1);
-      fp_montgomery_reduce(&t1, modulus, mp);
-      /* X = X * T1 */
-      fp_mul(&t1, &x, &x);
-      fp_montgomery_reduce(&x, modulus, mp);
-      /* T1 = Z' * T1 */
-      fp_mul(Q->z, &t1, &t1);
-      fp_montgomery_reduce(&t1, modulus, mp);
-      /* Y = Y * T1 */
-      fp_mul(&t1, &y, &y);
-      fp_montgomery_reduce(&y, modulus, mp);
-   }
-
-   /* T1 = Z*Z */
-   fp_sqr(&z, &t1);
-   fp_montgomery_reduce(&t1, modulus, mp);
-   /* T2 = X' * T1 */
-   fp_mul(Q->x, &t1, &t2);
-   fp_montgomery_reduce(&t2, modulus, mp);
-   /* T1 = Z * T1 */
-   fp_mul(&z, &t1, &t1);
-   fp_montgomery_reduce(&t1, modulus, mp);
-   /* T1 = Y' * T1 */
-   fp_mul(Q->y, &t1, &t1);
-   fp_montgomery_reduce(&t1, modulus, mp);
-
-   /* Y = Y - T1 */
-   fp_sub(&y, &t1, &y);
-   if (fp_cmp_d(&y, 0) == FP_LT) {
-      fp_add(&y, modulus, &y);
-   }
-   /* T1 = 2T1 */
-   fp_add(&t1, &t1, &t1);
-   if (fp_cmp(&t1, modulus) != FP_LT) {
-      fp_sub(&t1, modulus, &t1);
-   }
-   /* T1 = Y + T1 */
-   fp_add(&t1, &y, &t1);
-   if (fp_cmp(&t1, modulus) != FP_LT) {
-      fp_sub(&t1, modulus, &t1);
-   }
-   /* X = X - T2 */
-   fp_sub(&x, &t2, &x);
-   if (fp_cmp_d(&x, 0) == FP_LT) {
-      fp_add(&x, modulus, &x);
-   }
-   /* T2 = 2T2 */
-   fp_add(&t2, &t2, &t2);
-   if (fp_cmp(&t2, modulus) != FP_LT) {
-      fp_sub(&t2, modulus, &t2);
-   }
-   /* T2 = X + T2 */
-   fp_add(&t2, &x, &t2);
-   if (fp_cmp(&t2, modulus) != FP_LT) {
-      fp_sub(&t2, modulus, &t2);
-   }
-
-   /* if Z' != 1 */
-   if (Q->z != NULL) {
-      /* Z = Z * Z' */
-      fp_mul(&z, Q->z, &z);
-      fp_montgomery_reduce(&z, modulus, mp);
-   }
-
-   /* Z = Z * X */
-   fp_mul(&z, &x, &z);
-   fp_montgomery_reduce(&z, modulus, mp);
-
-   /* T1 = T1 * X  */
-   fp_mul(&t1, &x, &t1);
-   fp_montgomery_reduce(&t1, modulus, mp);
-   /* X = X * X */
-   fp_sqr(&x, &x);
-   fp_montgomery_reduce(&x, modulus, mp);
-   /* T2 = T2 * x */
-   fp_mul(&t2, &x, &t2);
-   fp_montgomery_reduce(&t2, modulus, mp);
-   /* T1 = T1 * X  */
-   fp_mul(&t1, &x, &t1);
-   fp_montgomery_reduce(&t1, modulus, mp);
-
-   /* X = Y*Y */
-   fp_sqr(&y, &x);
-   fp_montgomery_reduce(&x, modulus, mp);
-   /* X = X - T2 */
-   fp_sub(&x, &t2, &x);
-   if (fp_cmp_d(&x, 0) == FP_LT) {
-      fp_add(&x, modulus, &x);
-   }
-
-   /* T2 = T2 - X */
-   fp_sub(&t2, &x, &t2);
-   if (fp_cmp_d(&t2, 0) == FP_LT) {
-      fp_add(&t2, modulus, &t2);
-   }
-   /* T2 = T2 - X */
-   fp_sub(&t2, &x, &t2);
-   if (fp_cmp_d(&t2, 0) == FP_LT) {
-      fp_add(&t2, modulus, &t2);
-   }
-   /* T2 = T2 * Y */
-   fp_mul(&t2, &y, &t2);
-   fp_montgomery_reduce(&t2, modulus, mp);
-   /* Y = T2 - T1 */
-   fp_sub(&t2, &t1, &y);
-   if (fp_cmp_d(&y, 0) == FP_LT) {
-      fp_add(&y, modulus, &y);
-   }
-   /* Y = Y/2 */
-   if (fp_isodd(&y)) {
-      fp_add(&y, modulus, &y);
-   }
-   fp_div_2(&y, &y);
-
-   fp_copy(&x, R->x);
-   fp_copy(&y, R->y);
-   fp_copy(&z, R->z);
-
-   return CRYPT_OK;
-}
-
-
-#endif
-
-static int set_rand(void *a, int size)
-{
-   LTC_ARGCHK(a != NULL);
-   fp_rand(a, size);
-   return CRYPT_OK;
-}
-
-const ltc_math_descriptor tfm_desc = {
-
-   "TomsFastMath",
-   (int)DIGIT_BIT,
-
-   &init,
-   &init_copy,
-   &deinit,
-
-   &neg,
-   &copy,
-
-   &set_int,
-   &get_int,
-   &get_digit,
-   &get_digit_count,
-   &compare,
-   &compare_d,
-   &count_bits,
-   &count_lsb_bits,
-   &twoexpt,
-
-   &read_radix,
-   &write_radix,
-   &unsigned_size,
-   &unsigned_write,
-   &unsigned_read,
-
-   &add,
-   &addi,
-   &sub,
-   &subi,
-   &mul,
-   &muli,
-   &sqr,
-   &divide,
-   &div_2,
-   &modi,
-   &gcd,
-   &lcm,
-
-   &mulmod,
-   &sqrmod,
-   &invmod,
-
-   &montgomery_setup,
-   &montgomery_normalization,
-   &montgomery_reduce,
-   &montgomery_deinit,
-
-   &exptmod,
-   &isprime,
-
-#ifdef LTC_MECC
-#ifdef LTC_MECC_FP
-   &ltc_ecc_fp_mulmod,
-#else
-   &ltc_ecc_mulmod,
-#endif /* LTC_MECC_FP */
-#ifdef LTC_MECC_ACCEL
-   &tfm_ecc_projective_add_point,
-   &tfm_ecc_projective_dbl_point,
-#else
-   &ltc_ecc_projective_add_point,
-   &ltc_ecc_projective_dbl_point,
-#endif /* LTC_MECC_ACCEL */
-   &ltc_ecc_map,
-#ifdef LTC_ECC_SHAMIR
-#ifdef LTC_MECC_FP
-   &ltc_ecc_fp_mul2add,
-#else
-   &ltc_ecc_mul2add,
-#endif /* LTC_MECC_FP */
-#else
-   NULL,
-#endif /* LTC_ECC_SHAMIR */
-#else
-   NULL, NULL, NULL, NULL, NULL,
-#endif /* LTC_MECC */
-
-#ifdef LTC_MRSA
-   &rsa_make_key,
-   &rsa_exptmod,
-#else
-   NULL, NULL,
-#endif
-   &addmod,
-   &submod,
-
-   set_rand,
-
-};
-
-
-#endif
diff --git a/libs/tomcrypt/src/misc/adler32.c b/libs/tomcrypt/src/misc/adler32.c
deleted file mode 100644
index 18a434771d16..000000000000
--- a/libs/tomcrypt/src/misc/adler32.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file adler32.c
-   Adler-32 checksum algorithm
-   Written and placed in the public domain by Wei Dai
-   Adapted for libtomcrypt by Steffen Jaeckel
-*/
-#ifdef LTC_ADLER32
-
-static const unsigned long _adler32_base = 65521;
-
-void adler32_init(adler32_state *ctx)
-{
-   LTC_ARGCHKVD(ctx != NULL);
-   ctx->s[0] = 1;
-   ctx->s[1] = 0;
-}
-
-void adler32_update(adler32_state *ctx, const unsigned char *input, unsigned long length)
-{
-   unsigned long s1, s2;
-
-   LTC_ARGCHKVD(ctx != NULL);
-   LTC_ARGCHKVD(input != NULL);
-   s1 = ctx->s[0];
-   s2 = ctx->s[1];
-
-   if (length % 8 != 0) {
-      do {
-         s1 += *input++;
-         s2 += s1;
-         length--;
-      } while (length % 8 != 0);
-
-      if (s1 >= _adler32_base)
-         s1 -= _adler32_base;
-      s2 %= _adler32_base;
-   }
-
-   while (length > 0) {
-      s1 += input[0];
-      s2 += s1;
-      s1 += input[1];
-      s2 += s1;
-      s1 += input[2];
-      s2 += s1;
-      s1 += input[3];
-      s2 += s1;
-      s1 += input[4];
-      s2 += s1;
-      s1 += input[5];
-      s2 += s1;
-      s1 += input[6];
-      s2 += s1;
-      s1 += input[7];
-      s2 += s1;
-
-      length -= 8;
-      input += 8;
-
-      if (s1 >= _adler32_base)
-         s1 -= _adler32_base;
-      s2 %= _adler32_base;
-   }
-
-   LTC_ARGCHKVD(s1 < _adler32_base);
-   LTC_ARGCHKVD(s2 < _adler32_base);
-
-   ctx->s[0] = (unsigned short)s1;
-   ctx->s[1] = (unsigned short)s2;
-}
-
-void adler32_finish(adler32_state *ctx, void *hash, unsigned long size)
-{
-   unsigned char* h;
-
-   LTC_ARGCHKVD(ctx != NULL);
-   LTC_ARGCHKVD(hash != NULL);
-
-   h = hash;
-
-   switch (size) {
-      default:
-         h[3] = ctx->s[0] & 0x0ff;
-         /* FALLTHROUGH */
-      case 3:
-         h[2] = (ctx->s[0] >> 8) & 0x0ff;
-         /* FALLTHROUGH */
-      case 2:
-         h[1] = ctx->s[1] & 0x0ff;
-         /* FALLTHROUGH */
-      case 1:
-         h[0] = (ctx->s[1] >> 8) & 0x0ff;
-         /* FALLTHROUGH */
-      case 0:
-         ;
-   }
-}
-
-int adler32_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   const void* in = "libtomcrypt";
-   const unsigned char adler32[] = { 0x1b, 0xe8, 0x04, 0xba };
-   unsigned char out[4];
-   adler32_state ctx;
-   adler32_init(&ctx);
-   adler32_update(&ctx, in, strlen(in));
-   adler32_finish(&ctx, out, 4);
-   if (compare_testvector(adler32, 4, out, 4, "adler32", 0)) {
-      return CRYPT_FAIL_TESTVECTOR;
-   }
-   return CRYPT_OK;
-#endif
-}
-#endif
diff --git a/libs/tomcrypt/src/misc/base64/base64_decode.c b/libs/tomcrypt/src/misc/base64/base64_decode.c
deleted file mode 100644
index 8b0458c95fe2..000000000000
--- a/libs/tomcrypt/src/misc/base64/base64_decode.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file base64_decode.c
-  Compliant base64 code donated by Wayne Scott (wscott@bitmover.com)
-  base64 URL Safe variant (RFC 4648 section 5) by Karel Miko
-*/
-
-
-#if defined(LTC_BASE64) || defined (LTC_BASE64_URL)
-
-#if defined(LTC_BASE64)
-static const unsigned char map_base64[256] = {
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
- 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-  7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
- 19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
-255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
- 37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
- 49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255 };
-#endif /* LTC_BASE64 */
-
-static const unsigned char map_base64url[] = {
-#if defined(LTC_BASE64_URL)
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255,
- 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-  7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
- 19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255,  63,
-255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
- 37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
- 49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255
-#endif /* LTC_BASE64_URL */
-};
-
-enum {
-   relaxed = 0,
-   strict = 1
-};
-
-static int _base64_decode_internal(const unsigned char *in,  unsigned long inlen,
-                                 unsigned char *out, unsigned long *outlen,
-                           const unsigned char *map, int is_strict)
-{
-   unsigned long t, x, y, z;
-   unsigned char c;
-   int           g;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   g = 0; /* '=' counter */
-   for (x = y = z = t = 0; x < inlen; x++) {
-       c = map[in[x]&0xFF];
-       if (c == 254) {
-          g++;
-          continue;
-       }
-       else if (is_strict && g > 0) {
-          /* we only allow '=' to be at the end */
-          return CRYPT_INVALID_PACKET;
-       }
-       if (c == 255) {
-          if (is_strict)
-             return CRYPT_INVALID_PACKET;
-          else
-             continue;
-       }
-
-       t = (t<<6)|c;
-
-       if (++y == 4) {
-          if (z + 3 > *outlen) return CRYPT_BUFFER_OVERFLOW;
-          out[z++] = (unsigned char)((t>>16)&255);
-          out[z++] = (unsigned char)((t>>8)&255);
-          out[z++] = (unsigned char)(t&255);
-          y = t = 0;
-       }
-   }
-
-   if (y != 0) {
-      if (y == 1) return CRYPT_INVALID_PACKET;
-      if ((y + g) != 4 && is_strict && map != map_base64url) return CRYPT_INVALID_PACKET;
-      t = t << (6 * (4 - y));
-      if (z + y - 1 > *outlen) return CRYPT_BUFFER_OVERFLOW;
-      if (y >= 2) out[z++] = (unsigned char) ((t >> 16) & 255);
-      if (y == 3) out[z++] = (unsigned char) ((t >> 8) & 255);
-   }
-   *outlen = z;
-   return CRYPT_OK;
-}
-
-#if defined(LTC_BASE64)
-/**
-   Relaxed base64 decode a block of memory
-   @param in       The base64 data to decode
-   @param inlen    The length of the base64 data
-   @param out      [out] The destination of the binary decoded data
-   @param outlen   [in/out] The max size and resulting size of the decoded data
-   @return CRYPT_OK if successful
-*/
-int base64_decode(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen)
-{
-    return _base64_decode_internal(in, inlen, out, outlen, map_base64, relaxed);
-}
-
-/**
-   Strict base64 decode a block of memory
-   @param in       The base64 data to decode
-   @param inlen    The length of the base64 data
-   @param out      [out] The destination of the binary decoded data
-   @param outlen   [in/out] The max size and resulting size of the decoded data
-   @return CRYPT_OK if successful
-*/
-int base64_strict_decode(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen)
-{
-   return _base64_decode_internal(in, inlen, out, outlen, map_base64, strict);
-}
-#endif /* LTC_BASE64 */
-
-#if defined(LTC_BASE64_URL)
-/**
-   Relaxed base64 (URL Safe, RFC 4648 section 5) decode a block of memory
-   @param in       The base64 data to decode
-   @param inlen    The length of the base64 data
-   @param out      [out] The destination of the binary decoded data
-   @param outlen   [in/out] The max size and resulting size of the decoded data
-   @return CRYPT_OK if successful
-*/
-int base64url_decode(const unsigned char *in,  unsigned long inlen,
-                           unsigned char *out, unsigned long *outlen)
-{
-    return _base64_decode_internal(in, inlen, out, outlen, map_base64url, relaxed);
-}
-
-/**
-   Strict base64 (URL Safe, RFC 4648 section 5) decode a block of memory
-   @param in       The base64 data to decode
-   @param inlen    The length of the base64 data
-   @param out      [out] The destination of the binary decoded data
-   @param outlen   [in/out] The max size and resulting size of the decoded data
-   @return CRYPT_OK if successful
-*/
-int base64url_strict_decode(const unsigned char *in,  unsigned long inlen,
-                           unsigned char *out, unsigned long *outlen)
-{
-    return _base64_decode_internal(in, inlen, out, outlen, map_base64url, strict);
-}
-#endif /* LTC_BASE64_URL */
-
-#endif
diff --git a/libs/tomcrypt/src/misc/base64/base64_encode.c b/libs/tomcrypt/src/misc/base64/base64_encode.c
deleted file mode 100644
index 54ff5cf3145c..000000000000
--- a/libs/tomcrypt/src/misc/base64/base64_encode.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file base64_encode.c
-  Compliant base64 encoder donated by Wayne Scott (wscott@bitmover.com)
-  base64 URL Safe variant (RFC 4648 section 5) by Karel Miko
-*/
-
-
-#if defined(LTC_BASE64) || defined (LTC_BASE64_URL)
-
-#if defined(LTC_BASE64)
-static const char * const codes_base64 =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-#endif /* LTC_BASE64 */
-
-#if defined(LTC_BASE64_URL)
-static const char * const codes_base64url =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-#endif /* LTC_BASE64_URL */
-
-static int _base64_encode_internal(const unsigned char *in,  unsigned long inlen,
-                                 unsigned char *out, unsigned long *outlen,
-                                 const char *codes, int pad)
-{
-   unsigned long i, len2, leven;
-   unsigned char *p;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* valid output size ? */
-   len2 = 4 * ((inlen + 2) / 3);
-   if (*outlen < len2 + 1) {
-      *outlen = len2 + 1;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   p = out;
-   leven = 3*(inlen / 3);
-   for (i = 0; i < leven; i += 3) {
-       *p++ = codes[(in[0] >> 2) & 0x3F];
-       *p++ = codes[(((in[0] & 3) << 4) + (in[1] >> 4)) & 0x3F];
-       *p++ = codes[(((in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F];
-       *p++ = codes[in[2] & 0x3F];
-       in += 3;
-   }
-   /* Pad it if necessary...  */
-   if (i < inlen) {
-       unsigned a = in[0];
-       unsigned b = (i+1 < inlen) ? in[1] : 0;
-
-       *p++ = codes[(a >> 2) & 0x3F];
-       *p++ = codes[(((a & 3) << 4) + (b >> 4)) & 0x3F];
-       if (pad) {
-         *p++ = (i+1 < inlen) ? codes[(((b & 0xf) << 2)) & 0x3F] : '=';
-         *p++ = '=';
-       }
-       else {
-         if (i+1 < inlen) *p++ = codes[(((b & 0xf) << 2)) & 0x3F];
-       }
-   }
-
-   /* append a NULL byte */
-   *p = '\0';
-
-   /* return ok */
-   *outlen = (unsigned long)(p - out);
-   return CRYPT_OK;
-}
-
-#if defined(LTC_BASE64)
-/**
-   base64 Encode a buffer (NUL terminated)
-   @param in      The input buffer to encode
-   @param inlen   The length of the input buffer
-   @param out     [out] The destination of the base64 encoded data
-   @param outlen  [in/out] The max size and resulting size
-   @return CRYPT_OK if successful
-*/
-int base64_encode(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen)
-{
-    return _base64_encode_internal(in, inlen, out, outlen, codes_base64, 1);
-}
-#endif /* LTC_BASE64 */
-
-
-#if defined(LTC_BASE64_URL)
-/**
-   base64 (URL Safe, RFC 4648 section 5) Encode a buffer (NUL terminated)
-   @param in      The input buffer to encode
-   @param inlen   The length of the input buffer
-   @param out     [out] The destination of the base64 encoded data
-   @param outlen  [in/out] The max size and resulting size
-   @return CRYPT_OK if successful
-*/
-int base64url_encode(const unsigned char *in,  unsigned long inlen,
-                           unsigned char *out, unsigned long *outlen)
-{
-    return _base64_encode_internal(in, inlen, out, outlen, codes_base64url, 0);
-}
-
-int base64url_strict_encode(const unsigned char *in,  unsigned long inlen,
-                           unsigned char *out, unsigned long *outlen)
-{
-    return _base64_encode_internal(in, inlen, out, outlen, codes_base64url, 1);
-}
-#endif /* LTC_BASE64_URL */
-
-#endif
diff --git a/libs/tomcrypt/src/misc/burn_stack.c b/libs/tomcrypt/src/misc/burn_stack.c
deleted file mode 100644
index a94589da8caa..000000000000
--- a/libs/tomcrypt/src/misc/burn_stack.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file burn_stack.c
-   Burn stack, Tom St Denis
-*/
-
-/**
-   Burn some stack memory
-   @param len amount of stack to burn in bytes
-*/
-void burn_stack(unsigned long len)
-{
-   unsigned char buf[32];
-   zeromem(buf, sizeof(buf));
-   if (len > (unsigned long)sizeof(buf))
-      burn_stack(len - sizeof(buf));
-}
diff --git a/libs/tomcrypt/src/misc/crc32.c b/libs/tomcrypt/src/misc/crc32.c
deleted file mode 100644
index 937014add90e..000000000000
--- a/libs/tomcrypt/src/misc/crc32.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file crc32.c
-   CRC-32 checksum algorithm
-   Written and placed in the public domain by Wei Dai
-   Adapted for libtomcrypt by Steffen Jaeckel
-*/
-#ifdef LTC_CRC32
-
-static const ulong32 _CRC32_NEGL = 0xffffffffUL;
-
-#if defined(ENDIAN_LITTLE)
-#define CRC32_INDEX(c) (c & 0xff)
-#define CRC32_SHIFTED(c) (c >> 8)
-#elif defined(ENDIAN_BIG)
-#define CRC32_INDEX(c) (c >> 24)
-#define CRC32_SHIFTED(c) (c << 8)
-#else
-#error The existing CRC32 implementation only works properly when the endianness of the target platform is known.
-#endif
-
-/* Table of CRC-32's of all single byte values (made by makecrc.c) */
-static const ulong32 crc32_m_tab[] =
-{
-#if defined(ENDIAN_LITTLE)
-      0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
-      0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
-      0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
-      0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
-      0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
-      0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
-      0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
-      0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
-      0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
-      0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
-      0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
-      0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
-      0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
-      0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
-      0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
-      0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
-      0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
-      0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
-      0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
-      0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
-      0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
-      0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
-      0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
-      0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
-      0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
-      0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
-      0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
-      0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
-      0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
-      0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
-      0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
-      0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
-      0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
-      0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
-      0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
-      0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
-      0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
-      0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
-      0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
-      0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
-      0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
-      0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
-      0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
-      0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
-      0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
-      0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
-      0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
-      0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
-      0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
-      0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
-      0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
-      0x2d02ef8dL
-#else
-      0x00000000L, 0x96300777L, 0x2c610eeeL, 0xba510999L, 0x19c46d07L,
-      0x8ff46a70L, 0x35a563e9L, 0xa395649eL, 0x3288db0eL, 0xa4b8dc79L,
-      0x1ee9d5e0L, 0x88d9d297L, 0x2b4cb609L, 0xbd7cb17eL, 0x072db8e7L,
-      0x911dbf90L, 0x6410b71dL, 0xf220b06aL, 0x4871b9f3L, 0xde41be84L,
-      0x7dd4da1aL, 0xebe4dd6dL, 0x51b5d4f4L, 0xc785d383L, 0x56986c13L,
-      0xc0a86b64L, 0x7af962fdL, 0xecc9658aL, 0x4f5c0114L, 0xd96c0663L,
-      0x633d0ffaL, 0xf50d088dL, 0xc8206e3bL, 0x5e10694cL, 0xe44160d5L,
-      0x727167a2L, 0xd1e4033cL, 0x47d4044bL, 0xfd850dd2L, 0x6bb50aa5L,
-      0xfaa8b535L, 0x6c98b242L, 0xd6c9bbdbL, 0x40f9bcacL, 0xe36cd832L,
-      0x755cdf45L, 0xcf0dd6dcL, 0x593dd1abL, 0xac30d926L, 0x3a00de51L,
-      0x8051d7c8L, 0x1661d0bfL, 0xb5f4b421L, 0x23c4b356L, 0x9995bacfL,
-      0x0fa5bdb8L, 0x9eb80228L, 0x0888055fL, 0xb2d90cc6L, 0x24e90bb1L,
-      0x877c6f2fL, 0x114c6858L, 0xab1d61c1L, 0x3d2d66b6L, 0x9041dc76L,
-      0x0671db01L, 0xbc20d298L, 0x2a10d5efL, 0x8985b171L, 0x1fb5b606L,
-      0xa5e4bf9fL, 0x33d4b8e8L, 0xa2c90778L, 0x34f9000fL, 0x8ea80996L,
-      0x18980ee1L, 0xbb0d6a7fL, 0x2d3d6d08L, 0x976c6491L, 0x015c63e6L,
-      0xf4516b6bL, 0x62616c1cL, 0xd8306585L, 0x4e0062f2L, 0xed95066cL,
-      0x7ba5011bL, 0xc1f40882L, 0x57c40ff5L, 0xc6d9b065L, 0x50e9b712L,
-      0xeab8be8bL, 0x7c88b9fcL, 0xdf1ddd62L, 0x492dda15L, 0xf37cd38cL,
-      0x654cd4fbL, 0x5861b24dL, 0xce51b53aL, 0x7400bca3L, 0xe230bbd4L,
-      0x41a5df4aL, 0xd795d83dL, 0x6dc4d1a4L, 0xfbf4d6d3L, 0x6ae96943L,
-      0xfcd96e34L, 0x468867adL, 0xd0b860daL, 0x732d0444L, 0xe51d0333L,
-      0x5f4c0aaaL, 0xc97c0dddL, 0x3c710550L, 0xaa410227L, 0x10100bbeL,
-      0x86200cc9L, 0x25b56857L, 0xb3856f20L, 0x09d466b9L, 0x9fe461ceL,
-      0x0ef9de5eL, 0x98c9d929L, 0x2298d0b0L, 0xb4a8d7c7L, 0x173db359L,
-      0x810db42eL, 0x3b5cbdb7L, 0xad6cbac0L, 0x2083b8edL, 0xb6b3bf9aL,
-      0x0ce2b603L, 0x9ad2b174L, 0x3947d5eaL, 0xaf77d29dL, 0x1526db04L,
-      0x8316dc73L, 0x120b63e3L, 0x843b6494L, 0x3e6a6d0dL, 0xa85a6a7aL,
-      0x0bcf0ee4L, 0x9dff0993L, 0x27ae000aL, 0xb19e077dL, 0x44930ff0L,
-      0xd2a30887L, 0x68f2011eL, 0xfec20669L, 0x5d5762f7L, 0xcb676580L,
-      0x71366c19L, 0xe7066b6eL, 0x761bd4feL, 0xe02bd389L, 0x5a7ada10L,
-      0xcc4add67L, 0x6fdfb9f9L, 0xf9efbe8eL, 0x43beb717L, 0xd58eb060L,
-      0xe8a3d6d6L, 0x7e93d1a1L, 0xc4c2d838L, 0x52f2df4fL, 0xf167bbd1L,
-      0x6757bca6L, 0xdd06b53fL, 0x4b36b248L, 0xda2b0dd8L, 0x4c1b0aafL,
-      0xf64a0336L, 0x607a0441L, 0xc3ef60dfL, 0x55df67a8L, 0xef8e6e31L,
-      0x79be6946L, 0x8cb361cbL, 0x1a8366bcL, 0xa0d26f25L, 0x36e26852L,
-      0x95770cccL, 0x03470bbbL, 0xb9160222L, 0x2f260555L, 0xbe3bbac5L,
-      0x280bbdb2L, 0x925ab42bL, 0x046ab35cL, 0xa7ffd7c2L, 0x31cfd0b5L,
-      0x8b9ed92cL, 0x1daede5bL, 0xb0c2649bL, 0x26f263ecL, 0x9ca36a75L,
-      0x0a936d02L, 0xa906099cL, 0x3f360eebL, 0x85670772L, 0x13570005L,
-      0x824abf95L, 0x147ab8e2L, 0xae2bb17bL, 0x381bb60cL, 0x9b8ed292L,
-      0x0dbed5e5L, 0xb7efdc7cL, 0x21dfdb0bL, 0xd4d2d386L, 0x42e2d4f1L,
-      0xf8b3dd68L, 0x6e83da1fL, 0xcd16be81L, 0x5b26b9f6L, 0xe177b06fL,
-      0x7747b718L, 0xe65a0888L, 0x706a0fffL, 0xca3b0666L, 0x5c0b0111L,
-      0xff9e658fL, 0x69ae62f8L, 0xd3ff6b61L, 0x45cf6c16L, 0x78e20aa0L,
-      0xeed20dd7L, 0x5483044eL, 0xc2b30339L, 0x612667a7L, 0xf71660d0L,
-      0x4d476949L, 0xdb776e3eL, 0x4a6ad1aeL, 0xdc5ad6d9L, 0x660bdf40L,
-      0xf03bd837L, 0x53aebca9L, 0xc59ebbdeL, 0x7fcfb247L, 0xe9ffb530L,
-      0x1cf2bdbdL, 0x8ac2bacaL, 0x3093b353L, 0xa6a3b424L, 0x0536d0baL,
-      0x9306d7cdL, 0x2957de54L, 0xbf67d923L, 0x2e7a66b3L, 0xb84a61c4L,
-      0x021b685dL, 0x942b6f2aL, 0x37be0bb4L, 0xa18e0cc3L, 0x1bdf055aL,
-      0x8def022dL
-#endif
-};
-
-void crc32_init(crc32_state *ctx)
-{
-   LTC_ARGCHKVD(ctx != NULL);
-   ctx->crc = _CRC32_NEGL;
-}
-
-void crc32_update(crc32_state *ctx, const unsigned char *input, unsigned long length)
-{
-   ulong32 crc;
-   LTC_ARGCHKVD(ctx != NULL);
-   LTC_ARGCHKVD(input != NULL);
-   crc = ctx->crc;
-
-   while (length--)
-      crc = crc32_m_tab[CRC32_INDEX(crc) ^ *input++] ^ CRC32_SHIFTED(crc);
-
-   ctx->crc = crc;
-}
-
-void crc32_finish(crc32_state *ctx, void *hash, unsigned long size)
-{
-   unsigned long i;
-   unsigned char* h;
-   ulong32 crc;
-   LTC_ARGCHKVD(ctx != NULL);
-   LTC_ARGCHKVD(hash != NULL);
-
-   h = hash;
-   crc = ctx->crc;
-   crc ^= _CRC32_NEGL;
-
-   if (size > 4) size = 4;
-   for (i = 0; i < size; i++) {
-      h[i] = ((unsigned char*)&(crc))[size-i-1];
-   }
-}
-
-int crc32_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   const void* in = "libtomcrypt";
-   const unsigned char crc32[] = { 0xb3, 0x73, 0x76, 0xef };
-   unsigned char out[4];
-   crc32_state ctx;
-   crc32_init(&ctx);
-   crc32_update(&ctx, in, strlen(in));
-   crc32_finish(&ctx, out, 4);
-   if (compare_testvector(crc32, 4, out, 4, "CRC32", 0)) {
-      return CRYPT_FAIL_TESTVECTOR;
-   }
-   return CRYPT_OK;
-#endif
-}
-#endif
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_cipher_descriptor.c b/libs/tomcrypt/src/misc/crypt/crypt_cipher_descriptor.c
deleted file mode 100644
index ea4e55a57ffa..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_cipher_descriptor.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_cipher_descriptor.c
-  Stores the cipher descriptor table, Tom St Denis
-*/
-
-struct ltc_cipher_descriptor cipher_descriptor[TAB_SIZE] = {
-{ NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
- };
-
-LTC_MUTEX_GLOBAL(ltc_cipher_mutex)
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_cipher_is_valid.c b/libs/tomcrypt/src/misc/crypt/crypt_cipher_is_valid.c
deleted file mode 100644
index 5da325034da0..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_cipher_is_valid.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_cipher_is_valid.c
-  Determine if cipher is valid, Tom St Denis
-*/
-
-/*
-   Test if a cipher index is valid
-   @param idx   The index of the cipher to search for
-   @return CRYPT_OK if valid
-*/
-int cipher_is_valid(int idx)
-{
-   LTC_MUTEX_LOCK(&ltc_cipher_mutex);
-   if (idx < 0 || idx >= TAB_SIZE || cipher_descriptor[idx].name == NULL) {
-      LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-      return CRYPT_INVALID_CIPHER;
-   }
-   LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-   return CRYPT_OK;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_cipher.c b/libs/tomcrypt/src/misc/crypt/crypt_find_cipher.c
deleted file mode 100644
index 659882188c6a..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_cipher.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_cipher.c
-  Find a cipher in the descriptor tables, Tom St Denis
-*/
-
-/**
-   Find a registered cipher by name
-   @param name   The name of the cipher to look for
-   @return >= 0 if found, -1 if not present
-*/
-int find_cipher(const char *name)
-{
-   int x;
-   LTC_ARGCHK(name != NULL);
-   LTC_MUTEX_LOCK(&ltc_cipher_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (cipher_descriptor[x].name != NULL && !XSTRCMP(cipher_descriptor[x].name, name)) {
-          LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-          return x;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_cipher_any.c b/libs/tomcrypt/src/misc/crypt/crypt_find_cipher_any.c
deleted file mode 100644
index 6ec31caedae4..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_cipher_any.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_cipher_any.c
-  Find a cipher in the descriptor tables, Tom St Denis
-*/
-
-/**
-   Find a cipher flexibly.  First by name then if not present by block and key size
-   @param name        The name of the cipher desired
-   @param blocklen    The minimum length of the block cipher desired (octets)
-   @param keylen      The minimum length of the key size desired (octets)
-   @return >= 0 if found, -1 if not present
-*/
-int find_cipher_any(const char *name, int blocklen, int keylen)
-{
-   int x;
-
-   if(name != NULL) {
-      x = find_cipher(name);
-      if (x != -1) return x;
-   }
-
-   LTC_MUTEX_LOCK(&ltc_cipher_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (cipher_descriptor[x].name == NULL) {
-          continue;
-       }
-       if (blocklen <= (int)cipher_descriptor[x].block_length && keylen <= (int)cipher_descriptor[x].max_key_length) {
-          LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-          return x;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_cipher_id.c b/libs/tomcrypt/src/misc/crypt/crypt_find_cipher_id.c
deleted file mode 100644
index f586d727082d..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_cipher_id.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_cipher_id.c
-  Find cipher by ID, Tom St Denis
-*/
-
-/**
-   Find a cipher by ID number
-   @param ID    The ID (not same as index) of the cipher to find
-   @return >= 0 if found, -1 if not present
-*/
-int find_cipher_id(unsigned char ID)
-{
-   int x;
-   LTC_MUTEX_LOCK(&ltc_cipher_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (cipher_descriptor[x].ID == ID) {
-          x = (cipher_descriptor[x].name == NULL) ? -1 : x;
-          LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-          return x;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_hash.c b/libs/tomcrypt/src/misc/crypt/crypt_find_hash.c
deleted file mode 100644
index b93ce52f686f..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_hash.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_hash.c
-  Find a hash, Tom St Denis
-*/
-
-/**
-   Find a registered hash by name
-   @param name   The name of the hash to look for
-   @return >= 0 if found, -1 if not present
-*/
-int find_hash(const char *name)
-{
-   int x;
-   LTC_ARGCHK(name != NULL);
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (hash_descriptor[x].name != NULL && XSTRCMP(hash_descriptor[x].name, name) == 0) {
-          LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-          return x;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_hash_any.c b/libs/tomcrypt/src/misc/crypt/crypt_find_hash_any.c
deleted file mode 100644
index be22ddff6723..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_hash_any.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_hash_any.c
-  Find a hash, Tom St Denis
-*/
-
-/**
-   Find a hash flexibly.  First by name then if not present by digest size
-   @param name        The name of the hash desired
-   @param digestlen   The minimum length of the digest size (octets)
-   @return >= 0 if found, -1 if not present
-*/int find_hash_any(const char *name, int digestlen)
-{
-   int x, y, z;
-   LTC_ARGCHK(name != NULL);
-
-   x = find_hash(name);
-   if (x != -1) return x;
-
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   y = MAXBLOCKSIZE+1;
-   z = -1;
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (hash_descriptor[x].name == NULL) {
-          continue;
-       }
-       if ((int)hash_descriptor[x].hashsize >= digestlen && (int)hash_descriptor[x].hashsize < y) {
-          z = x;
-          y = hash_descriptor[x].hashsize;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return z;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_hash_id.c b/libs/tomcrypt/src/misc/crypt/crypt_find_hash_id.c
deleted file mode 100644
index 6b82e7884a22..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_hash_id.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_hash_id.c
-  Find hash by ID, Tom St Denis
-*/
-
-/**
-   Find a hash by ID number
-   @param ID    The ID (not same as index) of the hash to find
-   @return >= 0 if found, -1 if not present
-*/
-int find_hash_id(unsigned char ID)
-{
-   int x;
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-      if (hash_descriptor[x].ID == ID) {
-          x = (hash_descriptor[x].name == NULL) ? -1 : x;
-          LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-          return x;
-      }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_hash_oid.c b/libs/tomcrypt/src/misc/crypt/crypt_find_hash_oid.c
deleted file mode 100644
index e600fff73039..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_hash_oid.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_hash_oid.c
-  Find a hash, Tom St Denis
-*/
-
-int find_hash_oid(const unsigned long *ID, unsigned long IDlen)
-{
-   int x;
-   LTC_ARGCHK(ID != NULL);
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (hash_descriptor[x].name != NULL && hash_descriptor[x].OIDlen == IDlen && !XMEMCMP(hash_descriptor[x].OID, ID, sizeof(unsigned long) * IDlen)) {
-          LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-          return x;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_find_prng.c b/libs/tomcrypt/src/misc/crypt/crypt_find_prng.c
deleted file mode 100644
index fe8ec97aa2a2..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_find_prng.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_find_prng.c
-  Find a PRNG, Tom St Denis
-*/
-
-/**
-   Find a registered PRNG by name
-   @param name   The name of the PRNG to look for
-   @return >= 0 if found, -1 if not present
-*/
-int find_prng(const char *name)
-{
-   int x;
-   LTC_ARGCHK(name != NULL);
-   LTC_MUTEX_LOCK(&ltc_prng_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if ((prng_descriptor[x].name != NULL) && XSTRCMP(prng_descriptor[x].name, name) == 0) {
-          LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-          return x;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_hash_descriptor.c b/libs/tomcrypt/src/misc/crypt/crypt_hash_descriptor.c
deleted file mode 100644
index 5f9c4f49b248..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_hash_descriptor.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_hash_descriptor.c
-  Stores the hash descriptor table, Tom St Denis
-*/
-
-struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = {
-{ NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL }
-};
-
-LTC_MUTEX_GLOBAL(ltc_hash_mutex)
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_hash_is_valid.c b/libs/tomcrypt/src/misc/crypt/crypt_hash_is_valid.c
deleted file mode 100644
index 52fc8559fe50..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_hash_is_valid.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_hash_is_valid.c
-  Determine if hash is valid, Tom St Denis
-*/
-
-/*
-   Test if a hash index is valid
-   @param idx   The index of the hash to search for
-   @return CRYPT_OK if valid
-*/
-int hash_is_valid(int idx)
-{
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   if (idx < 0 || idx >= TAB_SIZE || hash_descriptor[idx].name == NULL) {
-      LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-      return CRYPT_INVALID_HASH;
-   }
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return CRYPT_OK;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_inits.c b/libs/tomcrypt/src/misc/crypt/crypt_inits.c
deleted file mode 100644
index 81b2002c9c79..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_inits.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_inits.c
-
-  Provide math library functions for dynamic languages
-  like Python - Larry Bugbee, February 2013
-*/
-
-
-#ifdef LTM_DESC
-void init_LTM(void)
-{
-    ltc_mp = ltm_desc;
-}
-#endif
-
-#ifdef TFM_DESC
-void init_TFM(void)
-{
-    ltc_mp = tfm_desc;
-}
-#endif
-
-#ifdef GMP_DESC
-void init_GMP(void)
-{
-    ltc_mp = gmp_desc;
-}
-#endif
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c b/libs/tomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c
deleted file mode 100644
index fe5ae2e271d8..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/* Initialize ltc_mp to nulls, to force allocation on all platforms, including macOS. */
-ltc_math_descriptor ltc_mp = { 0 };
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_prng_descriptor.c b/libs/tomcrypt/src/misc/crypt/crypt_prng_descriptor.c
deleted file mode 100644
index 5e6307e542d6..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_prng_descriptor.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_prng_descriptor.c
-  Stores the PRNG descriptors, Tom St Denis
-*/
-struct ltc_prng_descriptor prng_descriptor[TAB_SIZE] = {
-{ NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
-};
-
-LTC_MUTEX_GLOBAL(ltc_prng_mutex)
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_prng_is_valid.c b/libs/tomcrypt/src/misc/crypt/crypt_prng_is_valid.c
deleted file mode 100644
index 8db951796dd7..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_prng_is_valid.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_prng_is_valid.c
-  Determine if PRNG is valid, Tom St Denis
-*/
-
-/*
-   Test if a PRNG index is valid
-   @param idx   The index of the PRNG to search for
-   @return CRYPT_OK if valid
-*/
-int prng_is_valid(int idx)
-{
-   LTC_MUTEX_LOCK(&ltc_prng_mutex);
-   if (idx < 0 || idx >= TAB_SIZE || prng_descriptor[idx].name == NULL) {
-      LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-      return CRYPT_INVALID_PRNG;
-   }
-   LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-   return CRYPT_OK;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_prng_rng_descriptor.c b/libs/tomcrypt/src/misc/crypt/crypt_prng_rng_descriptor.c
deleted file mode 100644
index ccb6852c78b2..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_prng_rng_descriptor.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_PRNG_ENABLE_LTC_RNG
-unsigned long (*ltc_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void));
-#endif
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_register_cipher.c b/libs/tomcrypt/src/misc/crypt/crypt_register_cipher.c
deleted file mode 100644
index 256cd8e05c8f..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_register_cipher.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_register_cipher.c
-  Register a cipher, Tom St Denis
-*/
-
-/**
-   Register a cipher with the descriptor table
-   @param cipher   The cipher you wish to register
-   @return value >= 0 if successfully added (or already present), -1 if unsuccessful
-*/
-int register_cipher(const struct ltc_cipher_descriptor *cipher)
-{
-   int x;
-
-   LTC_ARGCHK(cipher != NULL);
-
-   /* is it already registered? */
-   LTC_MUTEX_LOCK(&ltc_cipher_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (cipher_descriptor[x].name != NULL && cipher_descriptor[x].ID == cipher->ID) {
-          LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-          return x;
-       }
-   }
-
-   /* find a blank spot */
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (cipher_descriptor[x].name == NULL) {
-          XMEMCPY(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor));
-          LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-          return x;
-       }
-   }
-
-   /* no spot */
-   LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_register_hash.c b/libs/tomcrypt/src/misc/crypt/crypt_register_hash.c
deleted file mode 100644
index c23858b41a1e..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_register_hash.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_register_hash.c
-  Register a HASH, Tom St Denis
-*/
-
-/**
-   Register a hash with the descriptor table
-   @param hash   The hash you wish to register
-   @return value >= 0 if successfully added (or already present), -1 if unsuccessful
-*/
-int register_hash(const struct ltc_hash_descriptor *hash)
-{
-   int x;
-
-   LTC_ARGCHK(hash != NULL);
-
-   /* is it already registered? */
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) {
-          LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-          return x;
-       }
-   }
-
-   /* find a blank spot */
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (hash_descriptor[x].name == NULL) {
-          XMEMCPY(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor));
-          LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-          return x;
-       }
-   }
-
-   /* no spot */
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_register_prng.c b/libs/tomcrypt/src/misc/crypt/crypt_register_prng.c
deleted file mode 100644
index 920197ea06e9..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_register_prng.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_register_prng.c
-  Register a PRNG, Tom St Denis
-*/
-
-/**
-   Register a PRNG with the descriptor table
-   @param prng   The PRNG you wish to register
-   @return value >= 0 if successfully added (or already present), -1 if unsuccessful
-*/
-int register_prng(const struct ltc_prng_descriptor *prng)
-{
-   int x;
-
-   LTC_ARGCHK(prng != NULL);
-
-   /* is it already registered? */
-   LTC_MUTEX_LOCK(&ltc_prng_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) {
-          LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-          return x;
-       }
-   }
-
-   /* find a blank spot */
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (prng_descriptor[x].name == NULL) {
-          XMEMCPY(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor));
-          LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-          return x;
-       }
-   }
-
-   /* no spot */
-   LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-   return -1;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_unregister_cipher.c b/libs/tomcrypt/src/misc/crypt/crypt_unregister_cipher.c
deleted file mode 100644
index 8b9cbb7d8457..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_unregister_cipher.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_unregister_cipher.c
-  Unregister a cipher, Tom St Denis
-*/
-
-/**
-  Unregister a cipher from the descriptor table
-  @param cipher   The cipher descriptor to remove
-  @return CRYPT_OK on success
-*/
-int unregister_cipher(const struct ltc_cipher_descriptor *cipher)
-{
-   int x;
-
-   LTC_ARGCHK(cipher != NULL);
-
-   /* is it already registered? */
-   LTC_MUTEX_LOCK(&ltc_cipher_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (XMEMCMP(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)) == 0) {
-          cipher_descriptor[x].name = NULL;
-          cipher_descriptor[x].ID   = 255;
-          LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-          return CRYPT_OK;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_cipher_mutex);
-   return CRYPT_ERROR;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_unregister_hash.c b/libs/tomcrypt/src/misc/crypt/crypt_unregister_hash.c
deleted file mode 100644
index 7c4a7f9f6eac..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_unregister_hash.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_unregister_hash.c
-  Unregister a hash, Tom St Denis
-*/
-
-/**
-  Unregister a hash from the descriptor table
-  @param hash   The hash descriptor to remove
-  @return CRYPT_OK on success
-*/
-int unregister_hash(const struct ltc_hash_descriptor *hash)
-{
-   int x;
-
-   LTC_ARGCHK(hash != NULL);
-
-   /* is it already registered? */
-   LTC_MUTEX_LOCK(&ltc_hash_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) {
-          hash_descriptor[x].name = NULL;
-          LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-          return CRYPT_OK;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
-   return CRYPT_ERROR;
-}
diff --git a/libs/tomcrypt/src/misc/crypt/crypt_unregister_prng.c b/libs/tomcrypt/src/misc/crypt/crypt_unregister_prng.c
deleted file mode 100644
index 0022e3708ab1..000000000000
--- a/libs/tomcrypt/src/misc/crypt/crypt_unregister_prng.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file crypt_unregister_prng.c
-  Unregister a PRNG, Tom St Denis
-*/
-
-/**
-  Unregister a PRNG from the descriptor table
-  @param prng   The PRNG descriptor to remove
-  @return CRYPT_OK on success
-*/
-int unregister_prng(const struct ltc_prng_descriptor *prng)
-{
-   int x;
-
-   LTC_ARGCHK(prng != NULL);
-
-   /* is it already registered? */
-   LTC_MUTEX_LOCK(&ltc_prng_mutex);
-   for (x = 0; x < TAB_SIZE; x++) {
-       if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) {
-          prng_descriptor[x].name = NULL;
-          LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-          return CRYPT_OK;
-       }
-   }
-   LTC_MUTEX_UNLOCK(&ltc_prng_mutex);
-   return CRYPT_ERROR;
-}
diff --git a/libs/tomcrypt/src/misc/mem_neq.c b/libs/tomcrypt/src/misc/mem_neq.c
deleted file mode 100644
index d66566ba3106..000000000000
--- a/libs/tomcrypt/src/misc/mem_neq.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file mem_neq.c
-   Compare two blocks of memory for inequality in constant time.
-   Steffen Jaeckel
-*/
-
-/**
-   Compare two blocks of memory for inequality in constant time.
-
-   The usage is similar to that of standard memcmp, but you can only test
-   if the memory is equal or not - you can not determine by how much the
-   first different byte differs.
-
-   This function shall be used to compare results of cryptographic
-   operations where inequality means most likely usage of a wrong key.
-   The execution time has therefore to be constant as otherwise
-   timing attacks could be possible.
-
-   @param a     The first memory region
-   @param b     The second memory region
-   @param len   The length of the area to compare (octets)
-
-   @return 0 when a and b are equal for len bytes, 1 they are not equal.
-*/
-int mem_neq(const void *a, const void *b, size_t len)
-{
-   unsigned char ret = 0;
-   const unsigned char* pa;
-   const unsigned char* pb;
-
-   LTC_ARGCHK(a != NULL);
-   LTC_ARGCHK(b != NULL);
-
-   pa = a;
-   pb = b;
-
-   while (len-- > 0) {
-      ret |= *pa ^ *pb;
-      ++pa;
-      ++pb;
-   }
-
-   ret |= ret >> 4;
-   ret |= ret >> 2;
-   ret |= ret >> 1;
-   ret &= 1;
-
-   return ret;
-}
diff --git a/libs/tomcrypt/src/misc/pk_get_oid.c b/libs/tomcrypt/src/misc/pk_get_oid.c
deleted file mode 100644
index e25bccbd5ba4..000000000000
--- a/libs/tomcrypt/src/misc/pk_get_oid.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_DER
-static const oid_st rsa_oid = {
-   { 1, 2, 840, 113549, 1, 1, 1  },
-   7,
-};
-
-static const oid_st dsa_oid = {
-   { 1, 2, 840, 10040, 4, 1  },
-   6,
-};
-
-/*
-   Returns the OID of the public key algorithm.
-   @return CRYPT_OK if valid
-*/
-int pk_get_oid(int pk, oid_st *st)
-{
-   switch (pk) {
-      case PKA_RSA:
-         XMEMCPY(st, &rsa_oid, sizeof(*st));
-         break;
-      case PKA_DSA:
-         XMEMCPY(st, &dsa_oid, sizeof(*st));
-         break;
-      default:
-         return CRYPT_INVALID_ARG;
-   }
-   return CRYPT_OK;
-}
-#endif
diff --git a/libs/tomcrypt/src/misc/pkcs5/pkcs_5_1.c b/libs/tomcrypt/src/misc/pkcs5/pkcs_5_1.c
deleted file mode 100644
index a1caa78eac62..000000000000
--- a/libs/tomcrypt/src/misc/pkcs5/pkcs_5_1.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pkcs_5_1.c
-   PKCS #5, Algorithm #1, Tom St Denis
-*/
-#ifdef LTC_PKCS_5
-/**
-   Execute PKCS #5 v1 in strict or OpenSSL EVP_BytesToKey()-compat mode.
-
-   PKCS#5 v1 specifies that the output key length can be no larger than
-   the hash output length.  OpenSSL unilaterally extended that by repeating
-   the hash process on a block-by-block basis for as long as needed to make
-   bigger keys.  If you want to be compatible with KDF for e.g. "openssl enc",
-   you'll want that.
-
-   If you want strict PKCS behavior, turn openssl_compat off.  Or (more
-   likely), use one of the convenience functions below.
-
-   @param password         The password (or key)
-   @param password_len     The length of the password (octet)
-   @param salt             The salt (or nonce) which is 8 octets long
-   @param iteration_count  The PKCS #5 v1 iteration count
-   @param hash_idx         The index of the hash desired
-   @param out              [out] The destination for this algorithm
-   @param outlen           [in/out] The max size and resulting size of the algorithm output
-   @param openssl_compat   [in] Whether or not to grow the key to the buffer size ala OpenSSL
-   @return CRYPT_OK if successful
-*/
-static int _pkcs_5_alg1_common(const unsigned char *password,
-                       unsigned long password_len,
-                       const unsigned char *salt,
-                       int iteration_count,  int hash_idx,
-                       unsigned char *out,   unsigned long *outlen,
-                       int openssl_compat)
-{
-   int err;
-   unsigned long x;
-   hash_state    *md;
-   unsigned char *buf;
-   /* Storage vars in case we need to support > hashsize (OpenSSL compat) */
-   unsigned long block = 0, iter;
-   /* How many bytes to put in the outbut buffer (convenience calc) */
-   unsigned long outidx = 0, nb = 0;
-
-   LTC_ARGCHK(password != NULL);
-   LTC_ARGCHK(salt     != NULL);
-   LTC_ARGCHK(out      != NULL);
-   LTC_ARGCHK(outlen   != NULL);
-
-   /* test hash IDX */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* allocate memory */
-   md  = XMALLOC(sizeof(hash_state));
-   buf = XMALLOC(MAXBLOCKSIZE);
-   if (md == NULL || buf == NULL) {
-      if (md != NULL) {
-         XFREE(md);
-      }
-      if (buf != NULL) {
-         XFREE(buf);
-      }
-      return CRYPT_MEM;
-   }
-
-   while(block * hash_descriptor[hash_idx].hashsize < *outlen) {
-
-      /* hash initial (maybe previous hash) + password + salt */
-      if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
-          goto LBL_ERR;
-      }
-      /* in OpenSSL mode, we first hash the previous result for blocks 2-n */
-      if (openssl_compat && block) {
-          if ((err = hash_descriptor[hash_idx].process(md, buf, hash_descriptor[hash_idx].hashsize)) != CRYPT_OK) {
-              goto LBL_ERR;
-          }
-      }
-      if ((err = hash_descriptor[hash_idx].process(md, password, password_len)) != CRYPT_OK) {
-          goto LBL_ERR;
-      }
-      if ((err = hash_descriptor[hash_idx].process(md, salt, 8)) != CRYPT_OK) {
-          goto LBL_ERR;
-      }
-      if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) {
-          goto LBL_ERR;
-      }
-
-      iter = iteration_count;
-      while (--iter) {
-         /* code goes here. */
-         x = MAXBLOCKSIZE;
-         if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
-            goto LBL_ERR;
-         }
-      }
-
-      /* limit the size of the copy to however many bytes we have left in
-         the output buffer (and how many bytes we have to copy) */
-      outidx = block*hash_descriptor[hash_idx].hashsize;
-      nb = hash_descriptor[hash_idx].hashsize;
-      if(outidx+nb > *outlen)
-          nb = *outlen - outidx;
-      if(nb > 0)
-          XMEMCPY(out+outidx, buf, nb);
-
-      block++;
-      if (!openssl_compat)
-          break;
-   }
-   /* In strict mode, we always return the hashsize, in compat we filled it
-      as much as was requested, so we leave it alone. */
-   if(!openssl_compat)
-      *outlen = hash_descriptor[hash_idx].hashsize;
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, MAXBLOCKSIZE);
-   zeromem(md, sizeof(hash_state));
-#endif
-
-   XFREE(buf);
-   XFREE(md);
-
-   return err;
-}
-
-/**
-   Execute PKCS #5 v1 - Strict mode (no OpenSSL-compatible extension)
-   @param password         The password (or key)
-   @param password_len     The length of the password (octet)
-   @param salt             The salt (or nonce) which is 8 octets long
-   @param iteration_count  The PKCS #5 v1 iteration count
-   @param hash_idx         The index of the hash desired
-   @param out              [out] The destination for this algorithm
-   @param outlen           [in/out] The max size and resulting size of the algorithm output
-   @return CRYPT_OK if successful
-*/
-int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
-                const unsigned char *salt,
-                int iteration_count,  int hash_idx,
-                unsigned char *out,   unsigned long *outlen)
-{
-   return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
-                             hash_idx, out, outlen, 0);
-}
-
-/**
-   Execute PKCS #5 v1 - OpenSSL-extension-compatible mode
-
-   Use this one if you need to derive keys as "openssl enc" does by default.
-   OpenSSL (for better or worse), uses MD5 as the hash and iteration_count=1.
-   @param password         The password (or key)
-   @param password_len     The length of the password (octet)
-   @param salt             The salt (or nonce) which is 8 octets long
-   @param iteration_count  The PKCS #5 v1 iteration count
-   @param hash_idx         The index of the hash desired
-   @param out              [out] The destination for this algorithm
-   @param outlen           [in/out] The max size and resulting size of the algorithm output
-   @return CRYPT_OK if successful
-*/
-int pkcs_5_alg1_openssl(const unsigned char *password,
-                        unsigned long password_len,
-                        const unsigned char *salt,
-                        int iteration_count,  int hash_idx,
-                        unsigned char *out,   unsigned long *outlen)
-{
-   return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
-                             hash_idx, out, outlen, 1);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/misc/pkcs5/pkcs_5_2.c b/libs/tomcrypt/src/misc/pkcs5/pkcs_5_2.c
deleted file mode 100644
index 15db38e9b83e..000000000000
--- a/libs/tomcrypt/src/misc/pkcs5/pkcs_5_2.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file pkcs_5_2.c
-   PKCS #5, Algorithm #2, Tom St Denis
-*/
-#ifdef LTC_PKCS_5
-
-/**
-   Execute PKCS #5 v2
-   @param password          The input password (or key)
-   @param password_len      The length of the password (octets)
-   @param salt              The salt (or nonce)
-   @param salt_len          The length of the salt (octets)
-   @param iteration_count   # of iterations desired for PKCS #5 v2 [read specs for more]
-   @param hash_idx          The index of the hash desired
-   @param out               [out] The destination for this algorithm
-   @param outlen            [in/out] The max size and resulting size of the algorithm output
-   @return CRYPT_OK if successful
-*/
-int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,
-                const unsigned char *salt,     unsigned long salt_len,
-                int iteration_count,           int hash_idx,
-                unsigned char *out,            unsigned long *outlen)
-{
-   int err, itts;
-   ulong32  blkno;
-   unsigned long stored, left, x, y;
-   unsigned char *buf[2];
-   hmac_state    *hmac;
-
-   LTC_ARGCHK(password != NULL);
-   LTC_ARGCHK(salt     != NULL);
-   LTC_ARGCHK(out      != NULL);
-   LTC_ARGCHK(outlen   != NULL);
-
-   /* test hash IDX */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   buf[0] = XMALLOC(MAXBLOCKSIZE * 2);
-   hmac   = XMALLOC(sizeof(hmac_state));
-   if (hmac == NULL || buf[0] == NULL) {
-      if (hmac != NULL) {
-         XFREE(hmac);
-      }
-      if (buf[0] != NULL) {
-         XFREE(buf[0]);
-      }
-      return CRYPT_MEM;
-   }
-   /* buf[1] points to the second block of MAXBLOCKSIZE bytes */
-   buf[1] = buf[0] + MAXBLOCKSIZE;
-
-   left   = *outlen;
-   blkno  = 1;
-   stored = 0;
-   while (left != 0) {
-       /* process block number blkno */
-       zeromem(buf[0], MAXBLOCKSIZE*2);
-
-       /* store current block number and increment for next pass */
-       STORE32H(blkno, buf[1]);
-       ++blkno;
-
-       /* get PRF(P, S||int(blkno)) */
-       if ((err = hmac_init(hmac, hash_idx, password, password_len)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       if ((err = hmac_process(hmac, salt, salt_len)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       if ((err = hmac_process(hmac, buf[1], 4)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       x = MAXBLOCKSIZE;
-       if ((err = hmac_done(hmac, buf[0], &x)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-
-       /* now compute repeated and XOR it in buf[1] */
-       XMEMCPY(buf[1], buf[0], x);
-       for (itts = 1; itts < iteration_count; ++itts) {
-           if ((err = hmac_memory(hash_idx, password, password_len, buf[0], x, buf[0], &x)) != CRYPT_OK) {
-              goto LBL_ERR;
-           }
-           for (y = 0; y < x; y++) {
-               buf[1][y] ^= buf[0][y];
-           }
-       }
-
-       /* now emit upto x bytes of buf[1] to output */
-       for (y = 0; y < x && left != 0; ++y) {
-           out[stored++] = buf[1][y];
-           --left;
-       }
-   }
-   *outlen = stored;
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf[0], MAXBLOCKSIZE*2);
-   zeromem(hmac, sizeof(hmac_state));
-#endif
-
-   XFREE(hmac);
-   XFREE(buf[0]);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/misc/zeromem.c b/libs/tomcrypt/src/misc/zeromem.c
deleted file mode 100644
index 9776b1d03e3e..000000000000
--- a/libs/tomcrypt/src/misc/zeromem.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file zeromem.c
-   Zero a block of memory, Tom St Denis
-*/
-
-/**
-   Zero a block of memory
-   @param out    The destination of the area to zero
-   @param outlen The length of the area to zero (octets)
-*/
-void zeromem(volatile void *out, size_t outlen)
-{
-   volatile char *mem = out;
-   LTC_ARGCHKVD(out != NULL);
-   while (outlen-- > 0) {
-      *mem++ = '\0';
-   }
-}
diff --git a/libs/tomcrypt/src/modes/cbc/cbc_decrypt.c b/libs/tomcrypt/src/modes/cbc/cbc_decrypt.c
deleted file mode 100644
index 7396df53063d..000000000000
--- a/libs/tomcrypt/src/modes/cbc/cbc_decrypt.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cbc_decrypt.c
-   CBC implementation, encrypt block, Tom St Denis
-*/
-
-
-#ifdef LTC_CBC_MODE
-
-/**
-  CBC decrypt
-  @param ct     Ciphertext
-  @param pt     [out] Plaintext
-  @param len    The number of bytes to process (must be multiple of block length)
-  @param cbc    CBC state
-  @return CRYPT_OK if successful
-*/
-int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc)
-{
-   int x, err;
-   unsigned char tmp[16];
-#ifdef LTC_FAST
-   LTC_FAST_TYPE tmpy;
-#else
-   unsigned char tmpy;
-#endif
-
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-   LTC_ARGCHK(cbc != NULL);
-
-   if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen valid? */
-   if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV) || cbc->blocklen > (int)sizeof(tmp)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (len % cbc->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-#ifdef LTC_FAST
-   if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   if (cipher_descriptor[cbc->cipher].accel_cbc_decrypt != NULL) {
-      return cipher_descriptor[cbc->cipher].accel_cbc_decrypt(ct, pt, len / cbc->blocklen, cbc->IV, &cbc->key);
-   } else {
-      while (len) {
-         /* decrypt */
-         if ((err = cipher_descriptor[cbc->cipher].ecb_decrypt(ct, tmp, &cbc->key)) != CRYPT_OK) {
-            return err;
-         }
-
-         /* xor IV against plaintext */
-         #if defined(LTC_FAST)
-         for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) {
-            tmpy = *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)cbc->IV + x)) ^ *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)tmp + x));
-            *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)cbc->IV + x)) = *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)ct + x));
-            *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)pt + x)) = tmpy;
-         }
-    #else
-         for (x = 0; x < cbc->blocklen; x++) {
-            tmpy       = tmp[x] ^ cbc->IV[x];
-            cbc->IV[x] = ct[x];
-            pt[x]      = tmpy;
-         }
-    #endif
-
-         ct  += cbc->blocklen;
-         pt  += cbc->blocklen;
-         len -= cbc->blocklen;
-      }
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cbc/cbc_done.c b/libs/tomcrypt/src/modes/cbc/cbc_done.c
deleted file mode 100644
index 3a2f15728584..000000000000
--- a/libs/tomcrypt/src/modes/cbc/cbc_done.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cbc_done.c
-   CBC implementation, finish chain, Tom St Denis
-*/
-
-#ifdef LTC_CBC_MODE
-
-/** Terminate the chain
-  @param cbc    The CBC chain to terminate
-  @return CRYPT_OK on success
-*/
-int cbc_done(symmetric_CBC *cbc)
-{
-   int err;
-   LTC_ARGCHK(cbc != NULL);
-
-   if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[cbc->cipher].done(&cbc->key);
-   return CRYPT_OK;
-}
-
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cbc/cbc_encrypt.c b/libs/tomcrypt/src/modes/cbc/cbc_encrypt.c
deleted file mode 100644
index 3ad488658bb8..000000000000
--- a/libs/tomcrypt/src/modes/cbc/cbc_encrypt.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cbc_encrypt.c
-   CBC implementation, encrypt block, Tom St Denis
-*/
-
-
-#ifdef LTC_CBC_MODE
-
-/**
-  CBC encrypt
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    The number of bytes to process (must be multiple of block length)
-  @param cbc    CBC state
-  @return CRYPT_OK if successful
-*/
-int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc)
-{
-   int x, err;
-
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(cbc != NULL);
-
-   if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen valid? */
-   if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (len % cbc->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-#ifdef LTC_FAST
-   if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   if (cipher_descriptor[cbc->cipher].accel_cbc_encrypt != NULL) {
-      return cipher_descriptor[cbc->cipher].accel_cbc_encrypt(pt, ct, len / cbc->blocklen, cbc->IV, &cbc->key);
-   } else {
-      while (len) {
-         /* xor IV against plaintext */
-         #if defined(LTC_FAST)
-         for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) {
-            *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)cbc->IV + x)) ^= *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)pt + x));
-         }
-    #else
-         for (x = 0; x < cbc->blocklen; x++) {
-            cbc->IV[x] ^= pt[x];
-         }
-    #endif
-
-         /* encrypt */
-         if ((err = cipher_descriptor[cbc->cipher].ecb_encrypt(cbc->IV, ct, &cbc->key)) != CRYPT_OK) {
-            return err;
-         }
-
-         /* store IV [ciphertext] for a future block */
-         #if defined(LTC_FAST)
-         for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) {
-            *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)cbc->IV + x)) = *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)ct + x));
-         }
-    #else
-         for (x = 0; x < cbc->blocklen; x++) {
-            cbc->IV[x] = ct[x];
-         }
-    #endif
-
-         ct  += cbc->blocklen;
-         pt  += cbc->blocklen;
-         len -= cbc->blocklen;
-      }
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cbc/cbc_getiv.c b/libs/tomcrypt/src/modes/cbc/cbc_getiv.c
deleted file mode 100644
index 742207599049..000000000000
--- a/libs/tomcrypt/src/modes/cbc/cbc_getiv.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cbc_getiv.c
-   CBC implementation, get IV, Tom St Denis
-*/
-
-#ifdef LTC_CBC_MODE
-
-/**
-   Get the current initialization vector
-   @param IV   [out] The destination of the initialization vector
-   @param len  [in/out]  The max size and resulting size of the initialization vector
-   @param cbc  The CBC state
-   @return CRYPT_OK if successful
-*/
-int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc)
-{
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(len != NULL);
-   LTC_ARGCHK(cbc != NULL);
-   if ((unsigned long)cbc->blocklen > *len) {
-      *len = cbc->blocklen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   XMEMCPY(IV, cbc->IV, cbc->blocklen);
-   *len = cbc->blocklen;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cbc/cbc_setiv.c b/libs/tomcrypt/src/modes/cbc/cbc_setiv.c
deleted file mode 100644
index 76fc05d5c7bb..000000000000
--- a/libs/tomcrypt/src/modes/cbc/cbc_setiv.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cbc_setiv.c
-   CBC implementation, set IV, Tom St Denis
-*/
-
-
-#ifdef LTC_CBC_MODE
-
-/**
-   Set an initialization vector
-   @param IV   The initialization vector
-   @param len  The length of the vector (in octets)
-   @param cbc  The CBC state
-   @return CRYPT_OK if successful
-*/
-int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc)
-{
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(cbc != NULL);
-   if (len != (unsigned long)cbc->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-   XMEMCPY(cbc->IV, IV, len);
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cbc/cbc_start.c b/libs/tomcrypt/src/modes/cbc/cbc_start.c
deleted file mode 100644
index 38ec33a1c497..000000000000
--- a/libs/tomcrypt/src/modes/cbc/cbc_start.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cbc_start.c
-   CBC implementation, start chain, Tom St Denis
-*/
-
-#ifdef LTC_CBC_MODE
-
-/**
-   Initialize a CBC context
-   @param cipher      The index of the cipher desired
-   @param IV          The initialization vector
-   @param key         The secret key
-   @param keylen      The length of the secret key (octets)
-   @param num_rounds  Number of rounds in the cipher desired (0 for default)
-   @param cbc         The CBC state to initialize
-   @return CRYPT_OK if successful
-*/
-int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key,
-              int keylen, int num_rounds, symmetric_CBC *cbc)
-{
-   int x, err;
-
-   LTC_ARGCHK(IV != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(cbc != NULL);
-
-   /* bad param? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* setup cipher */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &cbc->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* copy IV */
-   cbc->blocklen = cipher_descriptor[cipher].block_length;
-   cbc->cipher   = cipher;
-   for (x = 0; x < cbc->blocklen; x++) {
-       cbc->IV[x] = IV[x];
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cfb/cfb_decrypt.c b/libs/tomcrypt/src/modes/cfb/cfb_decrypt.c
deleted file mode 100644
index 026cd7cf45ce..000000000000
--- a/libs/tomcrypt/src/modes/cfb/cfb_decrypt.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file cfb_decrypt.c
-  CFB implementation, decrypt data, Tom St Denis
-*/
-
-#ifdef LTC_CFB_MODE
-
-/**
-   CFB decrypt
-   @param ct      Ciphertext
-   @param pt      [out] Plaintext
-   @param len     Length of ciphertext (octets)
-   @param cfb     CFB state
-   @return CRYPT_OK if successful
-*/
-int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb)
-{
-   int err;
-
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(cfb != NULL);
-
-   if ((err = cipher_is_valid(cfb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen/padlen valid? */
-   if (cfb->blocklen < 0 || cfb->blocklen > (int)sizeof(cfb->IV) ||
-       cfb->padlen   < 0 || cfb->padlen   > (int)sizeof(cfb->pad)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   while (len-- > 0) {
-       if (cfb->padlen == cfb->blocklen) {
-          if ((err = cipher_descriptor[cfb->cipher].ecb_encrypt(cfb->pad, cfb->IV, &cfb->key)) != CRYPT_OK) {
-             return err;
-          }
-          cfb->padlen = 0;
-       }
-       cfb->pad[cfb->padlen] = *ct;
-       *pt = *ct ^ cfb->IV[cfb->padlen];
-       ++pt;
-       ++ct;
-       ++(cfb->padlen);
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cfb/cfb_done.c b/libs/tomcrypt/src/modes/cfb/cfb_done.c
deleted file mode 100644
index 1de444ab2022..000000000000
--- a/libs/tomcrypt/src/modes/cfb/cfb_done.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cfb_done.c
-   CFB implementation, finish chain, Tom St Denis
-*/
-
-#ifdef LTC_CFB_MODE
-
-/** Terminate the chain
-  @param cfb    The CFB chain to terminate
-  @return CRYPT_OK on success
-*/
-int cfb_done(symmetric_CFB *cfb)
-{
-   int err;
-   LTC_ARGCHK(cfb != NULL);
-
-   if ((err = cipher_is_valid(cfb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[cfb->cipher].done(&cfb->key);
-   return CRYPT_OK;
-}
-
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cfb/cfb_encrypt.c b/libs/tomcrypt/src/modes/cfb/cfb_encrypt.c
deleted file mode 100644
index 8dcec22bf7c2..000000000000
--- a/libs/tomcrypt/src/modes/cfb/cfb_encrypt.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file cfb_encrypt.c
-  CFB implementation, encrypt data, Tom St Denis
-*/
-
-#ifdef LTC_CFB_MODE
-
-/**
-  CFB encrypt
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    Length of plaintext (octets)
-  @param cfb    CFB state
-  @return CRYPT_OK if successful
-*/
-int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb)
-{
-   int err;
-
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(cfb != NULL);
-
-   if ((err = cipher_is_valid(cfb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen/padlen valid? */
-   if (cfb->blocklen < 0 || cfb->blocklen > (int)sizeof(cfb->IV) ||
-       cfb->padlen   < 0 || cfb->padlen   > (int)sizeof(cfb->pad)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   while (len-- > 0) {
-       if (cfb->padlen == cfb->blocklen) {
-          if ((err = cipher_descriptor[cfb->cipher].ecb_encrypt(cfb->pad, cfb->IV, &cfb->key)) != CRYPT_OK) {
-             return err;
-          }
-          cfb->padlen = 0;
-       }
-       cfb->pad[cfb->padlen] = (*ct = *pt ^ cfb->IV[cfb->padlen]);
-       ++pt;
-       ++ct;
-       ++(cfb->padlen);
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cfb/cfb_getiv.c b/libs/tomcrypt/src/modes/cfb/cfb_getiv.c
deleted file mode 100644
index 0a615e71e8ed..000000000000
--- a/libs/tomcrypt/src/modes/cfb/cfb_getiv.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cfb_getiv.c
-   CFB implementation, get IV, Tom St Denis
-*/
-
-#ifdef LTC_CFB_MODE
-
-/**
-   Get the current initialization vector
-   @param IV   [out] The destination of the initialization vector
-   @param len  [in/out]  The max size and resulting size of the initialization vector
-   @param cfb  The CFB state
-   @return CRYPT_OK if successful
-*/
-int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb)
-{
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(len != NULL);
-   LTC_ARGCHK(cfb != NULL);
-   if ((unsigned long)cfb->blocklen > *len) {
-      *len = cfb->blocklen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   XMEMCPY(IV, cfb->IV, cfb->blocklen);
-   *len = cfb->blocklen;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cfb/cfb_setiv.c b/libs/tomcrypt/src/modes/cfb/cfb_setiv.c
deleted file mode 100644
index 76063758b048..000000000000
--- a/libs/tomcrypt/src/modes/cfb/cfb_setiv.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file cfb_setiv.c
-  CFB implementation, set IV, Tom St Denis
-*/
-
-#ifdef LTC_CFB_MODE
-
-/**
-   Set an initialization vector
-   @param IV   The initialization vector
-   @param len  The length of the vector (in octets)
-   @param cfb  The CFB state
-   @return CRYPT_OK if successful
-*/
-int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb)
-{
-   int err;
-
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(cfb != NULL);
-
-   if ((err = cipher_is_valid(cfb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   if (len != (unsigned long)cfb->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* force next block */
-   cfb->padlen = 0;
-   return cipher_descriptor[cfb->cipher].ecb_encrypt(IV, cfb->IV, &cfb->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/cfb/cfb_start.c b/libs/tomcrypt/src/modes/cfb/cfb_start.c
deleted file mode 100644
index b767160a2332..000000000000
--- a/libs/tomcrypt/src/modes/cfb/cfb_start.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file cfb_start.c
-   CFB implementation, start chain, Tom St Denis
-*/
-
-
-#ifdef LTC_CFB_MODE
-
-/**
-   Initialize a CFB context
-   @param cipher      The index of the cipher desired
-   @param IV          The initialization vector
-   @param key         The secret key
-   @param keylen      The length of the secret key (octets)
-   @param num_rounds  Number of rounds in the cipher desired (0 for default)
-   @param cfb         The CFB state to initialize
-   @return CRYPT_OK if successful
-*/
-int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key,
-              int keylen, int num_rounds, symmetric_CFB *cfb)
-{
-   int x, err;
-
-   LTC_ARGCHK(IV != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(cfb != NULL);
-
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-
-   /* copy data */
-   cfb->cipher = cipher;
-   cfb->blocklen = cipher_descriptor[cipher].block_length;
-   for (x = 0; x < cfb->blocklen; x++)
-       cfb->IV[x] = IV[x];
-
-   /* init the cipher */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &cfb->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* encrypt the IV */
-   cfb->padlen = 0;
-   return cipher_descriptor[cfb->cipher].ecb_encrypt(cfb->IV, cfb->IV, &cfb->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ctr/ctr_decrypt.c b/libs/tomcrypt/src/modes/ctr/ctr_decrypt.c
deleted file mode 100644
index a597346c725c..000000000000
--- a/libs/tomcrypt/src/modes/ctr/ctr_decrypt.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ctr_decrypt.c
-  CTR implementation, decrypt data, Tom St Denis
-*/
-
-#ifdef LTC_CTR_MODE
-
-/**
-   CTR decrypt
-   @param ct      Ciphertext
-   @param pt      [out] Plaintext
-   @param len     Length of ciphertext (octets)
-   @param ctr     CTR state
-   @return CRYPT_OK if successful
-*/
-int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr)
-{
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(ctr != NULL);
-
-   return ctr_encrypt(ct, pt, len, ctr);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ctr/ctr_done.c b/libs/tomcrypt/src/modes/ctr/ctr_done.c
deleted file mode 100644
index d6af2718026a..000000000000
--- a/libs/tomcrypt/src/modes/ctr/ctr_done.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ctr_done.c
-   CTR implementation, finish chain, Tom St Denis
-*/
-
-#ifdef LTC_CTR_MODE
-
-/** Terminate the chain
-  @param ctr    The CTR chain to terminate
-  @return CRYPT_OK on success
-*/
-int ctr_done(symmetric_CTR *ctr)
-{
-   int err;
-   LTC_ARGCHK(ctr != NULL);
-
-   if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[ctr->cipher].done(&ctr->key);
-   return CRYPT_OK;
-}
-
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ctr/ctr_encrypt.c b/libs/tomcrypt/src/modes/ctr/ctr_encrypt.c
deleted file mode 100644
index e94c5ba4ff50..000000000000
--- a/libs/tomcrypt/src/modes/ctr/ctr_encrypt.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ctr_encrypt.c
-  CTR implementation, encrypt data, Tom St Denis
-*/
-
-
-#ifdef LTC_CTR_MODE
-
-/**
-  CTR encrypt software implementation
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    Length of plaintext (octets)
-  @param ctr    CTR state
-  @return CRYPT_OK if successful
-*/
-static int _ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr)
-{
-   int x, err;
-
-   while (len) {
-      /* is the pad empty? */
-      if (ctr->padlen == ctr->blocklen) {
-         /* increment counter */
-         if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) {
-            /* little-endian */
-            for (x = 0; x < ctr->ctrlen; x++) {
-               ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
-               if (ctr->ctr[x] != (unsigned char)0) {
-                  break;
-               }
-            }
-         } else {
-            /* big-endian */
-            for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) {
-               ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
-               if (ctr->ctr[x] != (unsigned char)0) {
-                  break;
-               }
-            }
-         }
-
-         /* encrypt it */
-         if ((err = cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key)) != CRYPT_OK) {
-            return err;
-         }
-         ctr->padlen = 0;
-      }
-#ifdef LTC_FAST
-      if ((ctr->padlen == 0) && (len >= (unsigned long)ctr->blocklen)) {
-         for (x = 0; x < ctr->blocklen; x += sizeof(LTC_FAST_TYPE)) {
-            *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)ct + x)) = *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)pt + x)) ^
-                                                           *(LTC_FAST_TYPE_PTR_CAST((unsigned char *)ctr->pad + x));
-         }
-       pt         += ctr->blocklen;
-       ct         += ctr->blocklen;
-       len        -= ctr->blocklen;
-       ctr->padlen = ctr->blocklen;
-       continue;
-      }
-#endif
-      *ct++ = *pt++ ^ ctr->pad[ctr->padlen++];
-      --len;
-   }
-   return CRYPT_OK;
-}
-
-/**
-  CTR encrypt
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    Length of plaintext (octets)
-  @param ctr    CTR state
-  @return CRYPT_OK if successful
-*/
-int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr)
-{
-   int err, fr;
-
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(ctr != NULL);
-
-   if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen/padlen valid? */
-   if ((ctr->blocklen < 1) || (ctr->blocklen > (int)sizeof(ctr->ctr)) ||
-       (ctr->padlen   < 0) || (ctr->padlen   > (int)sizeof(ctr->pad))) {
-      return CRYPT_INVALID_ARG;
-   }
-
-#ifdef LTC_FAST
-   if (ctr->blocklen % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   /* handle acceleration only if pad is empty, accelerator is present and length is >= a block size */
-   if ((cipher_descriptor[ctr->cipher].accel_ctr_encrypt != NULL) && (len >= (unsigned long)ctr->blocklen)) {
-     if (ctr->padlen < ctr->blocklen) {
-       fr = ctr->blocklen - ctr->padlen;
-       if ((err = _ctr_encrypt(pt, ct, fr, ctr)) != CRYPT_OK) {
-          return err;
-       }
-       pt += fr;
-       ct += fr;
-       len -= fr;
-     }
-
-     if (len >= (unsigned long)ctr->blocklen) {
-       if ((err = cipher_descriptor[ctr->cipher].accel_ctr_encrypt(pt, ct, len/ctr->blocklen, ctr->ctr, ctr->mode, &ctr->key)) != CRYPT_OK) {
-          return err;
-       }
-       pt += (len / ctr->blocklen) * ctr->blocklen;
-       ct += (len / ctr->blocklen) * ctr->blocklen;
-       len %= ctr->blocklen;
-     }
-   }
-
-   return _ctr_encrypt(pt, ct, len, ctr);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ctr/ctr_getiv.c b/libs/tomcrypt/src/modes/ctr/ctr_getiv.c
deleted file mode 100644
index 61d9a3271c3c..000000000000
--- a/libs/tomcrypt/src/modes/ctr/ctr_getiv.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ctr_getiv.c
-   CTR implementation, get IV, Tom St Denis
-*/
-
-#ifdef LTC_CTR_MODE
-
-/**
-   Get the current initialization vector
-   @param IV   [out] The destination of the initialization vector
-   @param len  [in/out]  The max size and resulting size of the initialization vector
-   @param ctr  The CTR state
-   @return CRYPT_OK if successful
-*/
-int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr)
-{
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(len != NULL);
-   LTC_ARGCHK(ctr != NULL);
-   if ((unsigned long)ctr->blocklen > *len) {
-      *len = ctr->blocklen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   XMEMCPY(IV, ctr->ctr, ctr->blocklen);
-   *len = ctr->blocklen;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ctr/ctr_setiv.c b/libs/tomcrypt/src/modes/ctr/ctr_setiv.c
deleted file mode 100644
index 20397b90d54d..000000000000
--- a/libs/tomcrypt/src/modes/ctr/ctr_setiv.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ctr_setiv.c
-  CTR implementation, set IV, Tom St Denis
-*/
-
-#ifdef LTC_CTR_MODE
-
-/**
-   Set an initialization vector
-   @param IV   The initialization vector
-   @param len  The length of the vector (in octets)
-   @param ctr  The CTR state
-   @return CRYPT_OK if successful
-*/
-int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr)
-{
-   int err;
-
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(ctr != NULL);
-
-   /* bad param? */
-   if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (len != (unsigned long)ctr->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* set IV */
-   XMEMCPY(ctr->ctr, IV, len);
-
-   /* force next block */
-   ctr->padlen = 0;
-   return cipher_descriptor[ctr->cipher].ecb_encrypt(IV, ctr->pad, &ctr->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ctr/ctr_start.c b/libs/tomcrypt/src/modes/ctr/ctr_start.c
deleted file mode 100644
index 20f08dfa1569..000000000000
--- a/libs/tomcrypt/src/modes/ctr/ctr_start.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ctr_start.c
-   CTR implementation, start chain, Tom St Denis
-*/
-
-
-#ifdef LTC_CTR_MODE
-
-/**
-   Initialize a CTR context
-   @param cipher      The index of the cipher desired
-   @param IV          The initialization vector
-   @param key         The secret key
-   @param keylen      The length of the secret key (octets)
-   @param num_rounds  Number of rounds in the cipher desired (0 for default)
-   @param ctr_mode    The counter mode (CTR_COUNTER_LITTLE_ENDIAN or CTR_COUNTER_BIG_ENDIAN)
-   @param ctr         The CTR state to initialize
-   @return CRYPT_OK if successful
-*/
-int ctr_start(               int   cipher,
-              const unsigned char *IV,
-              const unsigned char *key,       int keylen,
-                             int  num_rounds, int ctr_mode,
-                   symmetric_CTR *ctr)
-{
-   int x, err;
-
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(ctr != NULL);
-
-   /* bad param? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* ctrlen == counter width */
-   ctr->ctrlen   = (ctr_mode & 255) ? (ctr_mode & 255) : cipher_descriptor[cipher].block_length;
-   if (ctr->ctrlen > cipher_descriptor[cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((ctr_mode & 0x1000) == CTR_COUNTER_BIG_ENDIAN) {
-      ctr->ctrlen = cipher_descriptor[cipher].block_length - ctr->ctrlen;
-   }
-
-   /* setup cipher */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &ctr->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* copy ctr */
-   ctr->blocklen = cipher_descriptor[cipher].block_length;
-   ctr->cipher   = cipher;
-   ctr->padlen   = 0;
-   ctr->mode     = ctr_mode & 0x1000;
-   for (x = 0; x < ctr->blocklen; x++) {
-       ctr->ctr[x] = IV[x];
-   }
-
-   if (ctr_mode & LTC_CTR_RFC3686) {
-      /* increment the IV as per RFC 3686 */
-      if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) {
-         /* little-endian */
-         for (x = 0; x < ctr->ctrlen; x++) {
-             ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
-             if (ctr->ctr[x] != (unsigned char)0) {
-                break;
-             }
-         }
-      } else {
-         /* big-endian */
-         for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) {
-             ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
-             if (ctr->ctr[x] != (unsigned char)0) {
-                break;
-             }
-         }
-      }
-   }
-
-   return cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ecb/ecb_decrypt.c b/libs/tomcrypt/src/modes/ecb/ecb_decrypt.c
deleted file mode 100644
index 2a80c74fcfde..000000000000
--- a/libs/tomcrypt/src/modes/ecb/ecb_decrypt.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecb_decrypt.c
-  ECB implementation, decrypt a block, Tom St Denis
-*/
-
-#ifdef LTC_ECB_MODE
-
-/**
-  ECB decrypt
-  @param ct     Ciphertext
-  @param pt     [out] Plaintext
-  @param len    The number of octets to process (must be multiple of the cipher block size)
-  @param ecb    ECB state
-  @return CRYPT_OK if successful
-*/
-int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb)
-{
-   int err;
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(ecb != NULL);
-   if ((err = cipher_is_valid(ecb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-   if (len % cipher_descriptor[ecb->cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* check for accel */
-   if (cipher_descriptor[ecb->cipher].accel_ecb_decrypt != NULL) {
-      return cipher_descriptor[ecb->cipher].accel_ecb_decrypt(ct, pt, len / cipher_descriptor[ecb->cipher].block_length, &ecb->key);
-   } else {
-      while (len) {
-         if ((err = cipher_descriptor[ecb->cipher].ecb_decrypt(ct, pt, &ecb->key)) != CRYPT_OK) {
-            return err;
-         }
-         pt  += cipher_descriptor[ecb->cipher].block_length;
-         ct  += cipher_descriptor[ecb->cipher].block_length;
-         len -= cipher_descriptor[ecb->cipher].block_length;
-      }
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ecb/ecb_done.c b/libs/tomcrypt/src/modes/ecb/ecb_done.c
deleted file mode 100644
index 1f01dfa5899a..000000000000
--- a/libs/tomcrypt/src/modes/ecb/ecb_done.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ecb_done.c
-   ECB implementation, finish chain, Tom St Denis
-*/
-
-#ifdef LTC_ECB_MODE
-
-/** Terminate the chain
-  @param ecb    The ECB chain to terminate
-  @return CRYPT_OK on success
-*/
-int ecb_done(symmetric_ECB *ecb)
-{
-   int err;
-   LTC_ARGCHK(ecb != NULL);
-
-   if ((err = cipher_is_valid(ecb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[ecb->cipher].done(&ecb->key);
-   return CRYPT_OK;
-}
-
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ecb/ecb_encrypt.c b/libs/tomcrypt/src/modes/ecb/ecb_encrypt.c
deleted file mode 100644
index 7a645985880d..000000000000
--- a/libs/tomcrypt/src/modes/ecb/ecb_encrypt.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecb_encrypt.c
-  ECB implementation, encrypt a block, Tom St Denis
-*/
-
-#ifdef LTC_ECB_MODE
-
-/**
-  ECB encrypt
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    The number of octets to process (must be multiple of the cipher block size)
-  @param ecb    ECB state
-  @return CRYPT_OK if successful
-*/
-int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb)
-{
-   int err;
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(ecb != NULL);
-   if ((err = cipher_is_valid(ecb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-   if (len % cipher_descriptor[ecb->cipher].block_length) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* check for accel */
-   if (cipher_descriptor[ecb->cipher].accel_ecb_encrypt != NULL) {
-      return cipher_descriptor[ecb->cipher].accel_ecb_encrypt(pt, ct, len / cipher_descriptor[ecb->cipher].block_length, &ecb->key);
-   } else {
-      while (len) {
-         if ((err = cipher_descriptor[ecb->cipher].ecb_encrypt(pt, ct, &ecb->key)) != CRYPT_OK) {
-            return err;
-         }
-         pt  += cipher_descriptor[ecb->cipher].block_length;
-         ct  += cipher_descriptor[ecb->cipher].block_length;
-         len -= cipher_descriptor[ecb->cipher].block_length;
-      }
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ecb/ecb_start.c b/libs/tomcrypt/src/modes/ecb/ecb_start.c
deleted file mode 100644
index add5fbddf2c4..000000000000
--- a/libs/tomcrypt/src/modes/ecb/ecb_start.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ecb_start.c
-   ECB implementation, start chain, Tom St Denis
-*/
-
-
-#ifdef LTC_ECB_MODE
-
-/**
-   Initialize a ECB context
-   @param cipher      The index of the cipher desired
-   @param key         The secret key
-   @param keylen      The length of the secret key (octets)
-   @param num_rounds  Number of rounds in the cipher desired (0 for default)
-   @param ecb         The ECB state to initialize
-   @return CRYPT_OK if successful
-*/
-int ecb_start(int cipher, const unsigned char *key, int keylen, int num_rounds, symmetric_ECB *ecb)
-{
-   int err;
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(ecb != NULL);
-
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   ecb->cipher = cipher;
-   ecb->blocklen = cipher_descriptor[cipher].block_length;
-   return cipher_descriptor[cipher].setup(key, keylen, num_rounds, &ecb->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_decrypt.c b/libs/tomcrypt/src/modes/f8/f8_decrypt.c
deleted file mode 100644
index c740786ff760..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_decrypt.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file f8_decrypt.c
-  F8 implementation, decrypt data, Tom St Denis
-*/
-
-#ifdef LTC_F8_MODE
-
-/**
-   F8 decrypt
-   @param ct      Ciphertext
-   @param pt      [out] Plaintext
-   @param len     Length of ciphertext (octets)
-   @param f8      F8 state
-   @return CRYPT_OK if successful
-*/
-int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8)
-{
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(f8 != NULL);
-   return f8_encrypt(ct, pt, len, f8);
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_done.c b/libs/tomcrypt/src/modes/f8/f8_done.c
deleted file mode 100644
index 09afa80a1cb6..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_done.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file f8_done.c
-   F8 implementation, finish chain, Tom St Denis
-*/
-
-#ifdef LTC_F8_MODE
-
-/** Terminate the chain
-  @param f8    The F8 chain to terminate
-  @return CRYPT_OK on success
-*/
-int f8_done(symmetric_F8 *f8)
-{
-   int err;
-   LTC_ARGCHK(f8 != NULL);
-
-   if ((err = cipher_is_valid(f8->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[f8->cipher].done(&f8->key);
-   return CRYPT_OK;
-}
-
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_encrypt.c b/libs/tomcrypt/src/modes/f8/f8_encrypt.c
deleted file mode 100644
index ff995f440b99..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_encrypt.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file f8_encrypt.c
-  F8 implementation, encrypt data, Tom St Denis
-*/
-
-#ifdef LTC_F8_MODE
-
-/**
-  F8 encrypt
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    Length of plaintext (octets)
-  @param f8     F8 state
-  @return CRYPT_OK if successful
-*/
-int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8)
-{
-   int           err, x;
-   unsigned char buf[MAXBLOCKSIZE];
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(f8 != NULL);
-   if ((err = cipher_is_valid(f8->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen/padlen valid? */
-   if (f8->blocklen < 0 || f8->blocklen > (int)sizeof(f8->IV) ||
-       f8->padlen   < 0 || f8->padlen   > (int)sizeof(f8->IV)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   zeromem(buf, sizeof(buf));
-
-   /* make sure the pad is empty */
-   if (f8->padlen == f8->blocklen) {
-      /* xor of IV, MIV and blockcnt == what goes into cipher */
-      STORE32H(f8->blockcnt, (buf+(f8->blocklen-4)));
-      ++(f8->blockcnt);
-      for (x = 0; x < f8->blocklen; x++) {
-          f8->IV[x] ^= f8->MIV[x] ^ buf[x];
-      }
-      if ((err = cipher_descriptor[f8->cipher].ecb_encrypt(f8->IV, f8->IV, &f8->key)) != CRYPT_OK) {
-         return err;
-      }
-      f8->padlen = 0;
-   }
-
-#ifdef LTC_FAST
-   if (f8->padlen == 0) {
-      while (len >= (unsigned long)f8->blocklen) {
-         STORE32H(f8->blockcnt, (buf+(f8->blocklen-4)));
-         ++(f8->blockcnt);
-         for (x = 0; x < f8->blocklen; x += sizeof(LTC_FAST_TYPE)) {
-             *(LTC_FAST_TYPE_PTR_CAST(&ct[x])) = *(LTC_FAST_TYPE_PTR_CAST(&pt[x])) ^ *(LTC_FAST_TYPE_PTR_CAST(&f8->IV[x]));
-             *(LTC_FAST_TYPE_PTR_CAST(&f8->IV[x])) ^= *(LTC_FAST_TYPE_PTR_CAST(&f8->MIV[x])) ^ *(LTC_FAST_TYPE_PTR_CAST(&buf[x]));
-         }
-         if ((err = cipher_descriptor[f8->cipher].ecb_encrypt(f8->IV, f8->IV, &f8->key)) != CRYPT_OK) {
-            return err;
-         }
-         len -= x;
-         pt  += x;
-         ct  += x;
-      }
-   }
-#endif
-
-   while (len > 0) {
-       if (f8->padlen == f8->blocklen) {
-          /* xor of IV, MIV and blockcnt == what goes into cipher */
-          STORE32H(f8->blockcnt, (buf+(f8->blocklen-4)));
-          ++(f8->blockcnt);
-          for (x = 0; x < f8->blocklen; x++) {
-              f8->IV[x] ^= f8->MIV[x] ^ buf[x];
-          }
-          if ((err = cipher_descriptor[f8->cipher].ecb_encrypt(f8->IV, f8->IV, &f8->key)) != CRYPT_OK) {
-             return err;
-          }
-          f8->padlen = 0;
-       }
-       *ct++ = *pt++ ^ f8->IV[f8->padlen++];
-       --len;
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_getiv.c b/libs/tomcrypt/src/modes/f8/f8_getiv.c
deleted file mode 100644
index 93e7c42829d0..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_getiv.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ofb_getiv.c
-   F8 implementation, get IV, Tom St Denis
-*/
-
-#ifdef LTC_F8_MODE
-
-/**
-   Get the current initialization vector
-   @param IV   [out] The destination of the initialization vector
-   @param len  [in/out]  The max size and resulting size of the initialization vector
-   @param f8   The F8 state
-   @return CRYPT_OK if successful
-*/
-int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8)
-{
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(len != NULL);
-   LTC_ARGCHK(f8  != NULL);
-   if ((unsigned long)f8->blocklen > *len) {
-      *len = f8->blocklen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   XMEMCPY(IV, f8->IV, f8->blocklen);
-   *len = f8->blocklen;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_setiv.c b/libs/tomcrypt/src/modes/f8/f8_setiv.c
deleted file mode 100644
index 0aaa7dccf865..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_setiv.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file f8_setiv.c
-   F8 implementation, set IV, Tom St Denis
-*/
-
-#ifdef LTC_F8_MODE
-
-/**
-   Set an initialization vector
-   @param IV   The initialization vector
-   @param len  The length of the vector (in octets)
-   @param f8   The F8 state
-   @return CRYPT_OK if successful
-*/
-int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8)
-{
-   int err;
-
-   LTC_ARGCHK(IV != NULL);
-   LTC_ARGCHK(f8 != NULL);
-
-   if ((err = cipher_is_valid(f8->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   if (len != (unsigned long)f8->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* force next block */
-   f8->padlen = 0;
-   return cipher_descriptor[f8->cipher].ecb_encrypt(IV, f8->IV, &f8->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_start.c b/libs/tomcrypt/src/modes/f8/f8_start.c
deleted file mode 100644
index a5102b0d7771..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_start.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file f8_start.c
-   F8 implementation, start chain, Tom St Denis
-*/
-
-
-#ifdef LTC_F8_MODE
-
-/**
-   Initialize an F8 context
-   @param cipher      The index of the cipher desired
-   @param IV          The initialization vector
-   @param key         The secret key
-   @param keylen      The length of the secret key (octets)
-   @param salt_key    The salting key for the IV
-   @param skeylen     The length of the salting key (octets)
-   @param num_rounds  Number of rounds in the cipher desired (0 for default)
-   @param f8          The F8 state to initialize
-   @return CRYPT_OK if successful
-*/
-int f8_start(                int  cipher, const unsigned char *IV,
-             const unsigned char *key,                    int  keylen,
-             const unsigned char *salt_key,               int  skeylen,
-                             int  num_rounds,   symmetric_F8  *f8)
-{
-   int           x, err;
-   unsigned char tkey[MAXBLOCKSIZE];
-
-   LTC_ARGCHK(IV       != NULL);
-   LTC_ARGCHK(key      != NULL);
-   LTC_ARGCHK(salt_key != NULL);
-   LTC_ARGCHK(f8       != NULL);
-
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-#ifdef LTC_FAST
-   if (cipher_descriptor[cipher].block_length % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   /* copy details */
-   f8->blockcnt = 0;
-   f8->cipher   = cipher;
-   f8->blocklen = cipher_descriptor[cipher].block_length;
-   f8->padlen   = f8->blocklen;
-
-   /* now get key ^ salt_key [extend salt_ket with 0x55 as required to match length] */
-   zeromem(tkey, sizeof(tkey));
-   for (x = 0; x < keylen && x < (int)sizeof(tkey); x++) {
-       tkey[x] = key[x];
-   }
-   for (x = 0; x < skeylen && x < (int)sizeof(tkey); x++) {
-       tkey[x] ^= salt_key[x];
-   }
-   for (; x < keylen && x < (int)sizeof(tkey); x++) {
-       tkey[x] ^= 0x55;
-   }
-
-   /* now encrypt with tkey[0..keylen-1] the IV and use that as the IV */
-   if ((err = cipher_descriptor[cipher].setup(tkey, keylen, num_rounds, &f8->key)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* encrypt IV */
-   if ((err = cipher_descriptor[f8->cipher].ecb_encrypt(IV, f8->MIV, &f8->key)) != CRYPT_OK) {
-      cipher_descriptor[f8->cipher].done(&f8->key);
-      return err;
-   }
-   zeromem(tkey, sizeof(tkey));
-   zeromem(f8->IV, sizeof(f8->IV));
-
-   /* terminate this cipher */
-   cipher_descriptor[f8->cipher].done(&f8->key);
-
-   /* init the cipher */
-   return cipher_descriptor[cipher].setup(key, keylen, num_rounds, &f8->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/f8/f8_test_mode.c b/libs/tomcrypt/src/modes/f8/f8_test_mode.c
deleted file mode 100644
index 0643135745c7..000000000000
--- a/libs/tomcrypt/src/modes/f8/f8_test_mode.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file f8_test_mode.c
-   F8 implementation, test, Tom St Denis
-*/
-
-
-#ifdef LTC_F8_MODE
-
-int f8_test_mode(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   static const unsigned char key[16] = { 0x23, 0x48, 0x29, 0x00, 0x84, 0x67, 0xbe, 0x18,
-                                          0x6c, 0x3d, 0xe1, 0x4a, 0xae, 0x72, 0xd6, 0x2c };
-   static const unsigned char salt[4] = { 0x32, 0xf2, 0x87, 0x0d };
-   static const unsigned char IV[16]  = { 0x00, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
-                                          0x5c, 0x62, 0x15, 0x99, 0xd4, 0x62, 0x56, 0x4a };
-   static const unsigned char pt[39]  = { 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x72, 0x61,
-                                          0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73,
-                                          0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
-                                          0x6e, 0x65, 0x78, 0x74, 0x20, 0x62, 0x65, 0x73,
-                                          0x74, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67       };
-   static const unsigned char ct[39]  = { 0x01, 0x9c, 0xe7, 0xa2, 0x6e, 0x78, 0x54, 0x01,
-                                          0x4a, 0x63, 0x66, 0xaa, 0x95, 0xd4, 0xee, 0xfd,
-                                          0x1a, 0xd4, 0x17, 0x2a, 0x14, 0xf9, 0xfa, 0xf4,
-                                          0x55, 0xb7, 0xf1, 0xd4, 0xb6, 0x2b, 0xd0, 0x8f,
-                                          0x56, 0x2c, 0x0e, 0xef, 0x7c, 0x48, 0x02       };
-   unsigned char buf[39];
-   symmetric_F8  f8;
-   int           err, idx;
-
-   idx = find_cipher("aes");
-   if (idx == -1) {
-      idx = find_cipher("rijndael");
-      if (idx == -1) return CRYPT_NOP;
-   }
-
-   /* initialize the context */
-   if ((err = f8_start(idx, IV, key, sizeof(key), salt, sizeof(salt), 0, &f8)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* encrypt block */
-   if ((err = f8_encrypt(pt, buf, sizeof(pt), &f8)) != CRYPT_OK) {
-      f8_done(&f8);
-      return err;
-   }
-   f8_done(&f8);
-
-   /* compare */
-   if (compare_testvector(buf, sizeof(ct), ct, sizeof(ct), "f8", 0)) {
-      return CRYPT_FAIL_TESTVECTOR;
-   }
-
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_decrypt.c b/libs/tomcrypt/src/modes/lrw/lrw_decrypt.c
deleted file mode 100644
index eff45e9de94b..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_decrypt.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_decrypt.c
-   LRW_MODE implementation, Decrypt blocks, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  LRW decrypt blocks
-  @param ct     The ciphertext
-  @param pt     [out] The plaintext
-  @param len    The length in octets, must be a multiple of 16
-  @param lrw    The LRW state
-*/
-int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw)
-{
-   int err;
-
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-   LTC_ARGCHK(lrw != NULL);
-
-   if ((err = cipher_is_valid(lrw->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (cipher_descriptor[lrw->cipher].accel_lrw_decrypt != NULL) {
-      return cipher_descriptor[lrw->cipher].accel_lrw_decrypt(ct, pt, len, lrw->IV, lrw->tweak, &lrw->key);
-   }
-
-   return lrw_process(ct, pt, len, LRW_DECRYPT, lrw);
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_done.c b/libs/tomcrypt/src/modes/lrw/lrw_done.c
deleted file mode 100644
index 5e294f0d6956..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_done.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_done.c
-   LRW_MODE implementation, Free resources, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  Terminate a LRW state
-  @param lrw   The state to terminate
-  @return CRYPT_OK if successful
-*/
-int lrw_done(symmetric_LRW *lrw)
-{
-   int err;
-
-   LTC_ARGCHK(lrw != NULL);
-
-   if ((err = cipher_is_valid(lrw->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[lrw->cipher].done(&lrw->key);
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_encrypt.c b/libs/tomcrypt/src/modes/lrw/lrw_encrypt.c
deleted file mode 100644
index 0961afffc190..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_encrypt.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_encrypt.c
-   LRW_MODE implementation, Encrypt blocks, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  LRW encrypt blocks
-  @param pt     The plaintext
-  @param ct     [out] The ciphertext
-  @param len    The length in octets, must be a multiple of 16
-  @param lrw    The LRW state
-*/
-int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw)
-{
-   int err;
-
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-   LTC_ARGCHK(lrw != NULL);
-
-   if ((err = cipher_is_valid(lrw->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (cipher_descriptor[lrw->cipher].accel_lrw_encrypt != NULL) {
-      return cipher_descriptor[lrw->cipher].accel_lrw_encrypt(pt, ct, len, lrw->IV, lrw->tweak, &lrw->key);
-   }
-
-   return lrw_process(pt, ct, len, LRW_ENCRYPT, lrw);
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_getiv.c b/libs/tomcrypt/src/modes/lrw/lrw_getiv.c
deleted file mode 100644
index bae69ee93552..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_getiv.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_getiv.c
-   LRW_MODE implementation, Retrieve the current IV, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  Get the IV for LRW
-  @param IV      [out] The IV, must be 16 octets
-  @param len     Length ... must be at least 16 :-)
-  @param lrw     The LRW state to read
-  @return CRYPT_OK if successful
-*/
-int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw)
-{
-   LTC_ARGCHK(IV != NULL);
-   LTC_ARGCHK(len != NULL);
-   LTC_ARGCHK(lrw != NULL);
-   if (*len < 16) {
-       *len = 16;
-       return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   XMEMCPY(IV, lrw->IV, 16);
-   *len = 16;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_process.c b/libs/tomcrypt/src/modes/lrw/lrw_process.c
deleted file mode 100644
index 38563810a11f..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_process.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_process.c
-   LRW_MODE implementation, Encrypt/decrypt blocks, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  Process blocks with LRW, since decrypt/encrypt are largely the same they share this code.
-  @param pt        The "input" data
-  @param ct        [out] The "output" data
-  @param len       The length of the input, must be a multiple of 128-bits (16 octets)
-  @param mode      LRW_ENCRYPT or LRW_DECRYPT
-  @param lrw       The LRW state
-  @return  CRYPT_OK if successful
-*/
-int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw)
-{
-   unsigned char prod[16];
-   int           x, err;
-#ifdef LTC_LRW_TABLES
-   int           y;
-#endif
-
-   LTC_ARGCHK(pt  != NULL);
-   LTC_ARGCHK(ct  != NULL);
-   LTC_ARGCHK(lrw != NULL);
-
-   if (len & 15) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   while (len) {
-      /* copy pad */
-      XMEMCPY(prod, lrw->pad, 16);
-
-      /* increment IV */
-      for (x = 15; x >= 0; x--) {
-          lrw->IV[x] = (lrw->IV[x] + 1) & 255;
-          if (lrw->IV[x]) {
-              break;
-          }
-      }
-
-      /* update pad */
-#ifdef LTC_LRW_TABLES
-      /* for each byte changed we undo it's affect on the pad then add the new product */
-      for (; x < 16; x++) {
-#ifdef LTC_FAST
-          for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-              *(LTC_FAST_TYPE_PTR_CAST(lrw->pad + y)) ^= *(LTC_FAST_TYPE_PTR_CAST(&lrw->PC[x][lrw->IV[x]][y])) ^ *(LTC_FAST_TYPE_PTR_CAST(&lrw->PC[x][(lrw->IV[x]-1)&255][y]));
-          }
-#else
-          for (y = 0; y < 16; y++) {
-              lrw->pad[y] ^= lrw->PC[x][lrw->IV[x]][y] ^ lrw->PC[x][(lrw->IV[x]-1)&255][y];
-          }
-#endif
-      }
-#else
-      gcm_gf_mult(lrw->tweak, lrw->IV, lrw->pad);
-#endif
-
-      /* xor prod */
-#ifdef LTC_FAST
-      for (x = 0; x < 16; x += sizeof(LTC_FAST_TYPE)) {
-           *(LTC_FAST_TYPE_PTR_CAST(ct + x)) = *(LTC_FAST_TYPE_PTR_CAST(pt + x)) ^ *(LTC_FAST_TYPE_PTR_CAST(prod + x));
-      }
-#else
-      for (x = 0; x < 16; x++) {
-         ct[x] = pt[x] ^ prod[x];
-      }
-#endif
-
-      /* send through cipher */
-      if (mode == LRW_ENCRYPT) {
-         if ((err = cipher_descriptor[lrw->cipher].ecb_encrypt(ct, ct, &lrw->key)) != CRYPT_OK) {
-            return err;
-         }
-      } else {
-         if ((err = cipher_descriptor[lrw->cipher].ecb_decrypt(ct, ct, &lrw->key)) != CRYPT_OK) {
-            return err;
-         }
-      }
-
-      /* xor prod */
-#ifdef LTC_FAST
-      for (x = 0; x < 16; x += sizeof(LTC_FAST_TYPE)) {
-           *(LTC_FAST_TYPE_PTR_CAST(ct + x)) = *(LTC_FAST_TYPE_PTR_CAST(ct + x)) ^ *(LTC_FAST_TYPE_PTR_CAST(prod + x));
-      }
-#else
-      for (x = 0; x < 16; x++) {
-         ct[x] = ct[x] ^ prod[x];
-      }
-#endif
-
-      /* move to next */
-      pt  += 16;
-      ct  += 16;
-      len -= 16;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_setiv.c b/libs/tomcrypt/src/modes/lrw/lrw_setiv.c
deleted file mode 100644
index f1c6f3d71ce0..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_setiv.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_setiv.c
-   LRW_MODE implementation, Set the current IV, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  Set the IV for LRW
-  @param IV      The IV, must be 16 octets
-  @param len     Length ... must be 16 :-)
-  @param lrw     The LRW state to update
-  @return CRYPT_OK if successful
-*/
-int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw)
-{
-   int           err;
-#ifdef LTC_LRW_TABLES
-   unsigned char T[16];
-   int           x, y;
-#endif
-   LTC_ARGCHK(IV != NULL);
-   LTC_ARGCHK(lrw != NULL);
-
-   if (len != 16) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = cipher_is_valid(lrw->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* copy the IV */
-   XMEMCPY(lrw->IV, IV, 16);
-
-   /* check if we have to actually do work */
-   if (cipher_descriptor[lrw->cipher].accel_lrw_encrypt != NULL && cipher_descriptor[lrw->cipher].accel_lrw_decrypt != NULL) {
-       /* we have accelerators, let's bail since they don't use lrw->pad anyways */
-       return CRYPT_OK;
-   }
-
-#ifdef LTC_LRW_TABLES
-   XMEMCPY(T, &lrw->PC[0][IV[0]][0], 16);
-   for (x = 1; x < 16; x++) {
-#ifdef LTC_FAST
-       for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
-           *(LTC_FAST_TYPE_PTR_CAST(T + y)) ^= *(LTC_FAST_TYPE_PTR_CAST(&lrw->PC[x][IV[x]][y]));
-       }
-#else
-       for (y = 0; y < 16; y++) {
-           T[y] ^= lrw->PC[x][IV[x]][y];
-       }
-#endif
-   }
-   XMEMCPY(lrw->pad, T, 16);
-#else
-   gcm_gf_mult(lrw->tweak, IV, lrw->pad);
-#endif
-
-   return CRYPT_OK;
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/lrw/lrw_start.c b/libs/tomcrypt/src/modes/lrw/lrw_start.c
deleted file mode 100644
index ca6eb7c852ea..000000000000
--- a/libs/tomcrypt/src/modes/lrw/lrw_start.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file lrw_start.c
-   LRW_MODE implementation, start mode, Tom St Denis
-*/
-
-#ifdef LTC_LRW_MODE
-
-/**
-  Initialize the LRW context
-  @param cipher        The cipher desired, must be a 128-bit block cipher
-  @param IV            The index value, must be 128-bits
-  @param key           The cipher key
-  @param keylen        The length of the cipher key in octets
-  @param tweak         The tweak value (second key), must be 128-bits
-  @param num_rounds    The number of rounds for the cipher (0 == default)
-  @param lrw           [out] The LRW state
-  @return CRYPT_OK on success.
-*/
-int lrw_start(               int   cipher,
-              const unsigned char *IV,
-              const unsigned char *key,       int keylen,
-              const unsigned char *tweak,
-                             int  num_rounds,
-                   symmetric_LRW *lrw)
-{
-   int           err;
-#ifdef LTC_LRW_TABLES
-   unsigned char B[16];
-   int           x, y, z, t;
-#endif
-
-   LTC_ARGCHK(IV    != NULL);
-   LTC_ARGCHK(key   != NULL);
-   LTC_ARGCHK(tweak != NULL);
-   LTC_ARGCHK(lrw   != NULL);
-
-#ifdef LTC_FAST
-   if (16 % sizeof(LTC_FAST_TYPE)) {
-      return CRYPT_INVALID_ARG;
-   }
-#endif
-
-   /* is cipher valid? */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-   if (cipher_descriptor[cipher].block_length != 16) {
-      return CRYPT_INVALID_CIPHER;
-   }
-
-   /* schedule key */
-   if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &lrw->key)) != CRYPT_OK) {
-      return err;
-   }
-   lrw->cipher = cipher;
-
-   /* copy the IV and tweak */
-   XMEMCPY(lrw->tweak, tweak, 16);
-
-#ifdef LTC_LRW_TABLES
-   /* setup tables */
-   /* generate the first table as it has no shifting (from which we make the other tables) */
-   zeromem(B, 16);
-   for (y = 0; y < 256; y++) {
-        B[0] = y;
-        gcm_gf_mult(tweak, B, &lrw->PC[0][y][0]);
-   }
-
-   /* now generate the rest of the tables based the previous table */
-   for (x = 1; x < 16; x++) {
-      for (y = 0; y < 256; y++) {
-         /* now shift it right by 8 bits */
-         t = lrw->PC[x-1][y][15];
-         for (z = 15; z > 0; z--) {
-             lrw->PC[x][y][z] = lrw->PC[x-1][y][z-1];
-         }
-         lrw->PC[x][y][0]  = gcm_shift_table[t<<1];
-         lrw->PC[x][y][1] ^= gcm_shift_table[(t<<1)+1];
-      }
-   }
-#endif
-
-   /* generate first pad */
-   return lrw_setiv(IV, 16, lrw);
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ofb/ofb_decrypt.c b/libs/tomcrypt/src/modes/ofb/ofb_decrypt.c
deleted file mode 100644
index 7ec42ad34bba..000000000000
--- a/libs/tomcrypt/src/modes/ofb/ofb_decrypt.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ofb_decrypt.c
-  OFB implementation, decrypt data, Tom St Denis
-*/
-
-#ifdef LTC_OFB_MODE
-
-/**
-   OFB decrypt
-   @param ct      Ciphertext
-   @param pt      [out] Plaintext
-   @param len     Length of ciphertext (octets)
-   @param ofb     OFB state
-   @return CRYPT_OK if successful
-*/
-int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb)
-{
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(ofb != NULL);
-   return ofb_encrypt(ct, pt, len, ofb);
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ofb/ofb_done.c b/libs/tomcrypt/src/modes/ofb/ofb_done.c
deleted file mode 100644
index d1a9e2b5f869..000000000000
--- a/libs/tomcrypt/src/modes/ofb/ofb_done.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ofb_done.c
-   OFB implementation, finish chain, Tom St Denis
-*/
-
-#ifdef LTC_OFB_MODE
-
-/** Terminate the chain
-  @param ofb    The OFB chain to terminate
-  @return CRYPT_OK on success
-*/
-int ofb_done(symmetric_OFB *ofb)
-{
-   int err;
-   LTC_ARGCHK(ofb != NULL);
-
-   if ((err = cipher_is_valid(ofb->cipher)) != CRYPT_OK) {
-      return err;
-   }
-   cipher_descriptor[ofb->cipher].done(&ofb->key);
-   return CRYPT_OK;
-}
-
-
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ofb/ofb_encrypt.c b/libs/tomcrypt/src/modes/ofb/ofb_encrypt.c
deleted file mode 100644
index 723bc122b40b..000000000000
--- a/libs/tomcrypt/src/modes/ofb/ofb_encrypt.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file ofb_encrypt.c
-  OFB implementation, encrypt data, Tom St Denis
-*/
-
-#ifdef LTC_OFB_MODE
-
-/**
-  OFB encrypt
-  @param pt     Plaintext
-  @param ct     [out] Ciphertext
-  @param len    Length of plaintext (octets)
-  @param ofb    OFB state
-  @return CRYPT_OK if successful
-*/
-int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb)
-{
-   int err;
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(ofb != NULL);
-   if ((err = cipher_is_valid(ofb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   /* is blocklen/padlen valid? */
-   if (ofb->blocklen < 0 || ofb->blocklen > (int)sizeof(ofb->IV) ||
-       ofb->padlen   < 0 || ofb->padlen   > (int)sizeof(ofb->IV)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   while (len-- > 0) {
-       if (ofb->padlen == ofb->blocklen) {
-          if ((err = cipher_descriptor[ofb->cipher].ecb_encrypt(ofb->IV, ofb->IV, &ofb->key)) != CRYPT_OK) {
-             return err;
-          }
-          ofb->padlen = 0;
-       }
-       *ct++ = *pt++ ^ ofb->IV[(ofb->padlen)++];
-   }
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ofb/ofb_getiv.c b/libs/tomcrypt/src/modes/ofb/ofb_getiv.c
deleted file mode 100644
index 37bf357aacdb..000000000000
--- a/libs/tomcrypt/src/modes/ofb/ofb_getiv.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ofb_getiv.c
-   OFB implementation, get IV, Tom St Denis
-*/
-
-#ifdef LTC_OFB_MODE
-
-/**
-   Get the current initialization vector
-   @param IV   [out] The destination of the initialization vector
-   @param len  [in/out]  The max size and resulting size of the initialization vector
-   @param ofb  The OFB state
-   @return CRYPT_OK if successful
-*/
-int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb)
-{
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(len != NULL);
-   LTC_ARGCHK(ofb != NULL);
-   if ((unsigned long)ofb->blocklen > *len) {
-      *len = ofb->blocklen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   XMEMCPY(IV, ofb->IV, ofb->blocklen);
-   *len = ofb->blocklen;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ofb/ofb_setiv.c b/libs/tomcrypt/src/modes/ofb/ofb_setiv.c
deleted file mode 100644
index ee577e52d150..000000000000
--- a/libs/tomcrypt/src/modes/ofb/ofb_setiv.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ofb_setiv.c
-   OFB implementation, set IV, Tom St Denis
-*/
-
-#ifdef LTC_OFB_MODE
-
-/**
-   Set an initialization vector
-   @param IV   The initialization vector
-   @param len  The length of the vector (in octets)
-   @param ofb  The OFB state
-   @return CRYPT_OK if successful
-*/
-int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb)
-{
-   int err;
-
-   LTC_ARGCHK(IV  != NULL);
-   LTC_ARGCHK(ofb != NULL);
-
-   if ((err = cipher_is_valid(ofb->cipher)) != CRYPT_OK) {
-       return err;
-   }
-
-   if (len != (unsigned long)ofb->blocklen) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* force next block */
-   ofb->padlen = 0;
-   return cipher_descriptor[ofb->cipher].ecb_encrypt(IV, ofb->IV, &ofb->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/ofb/ofb_start.c b/libs/tomcrypt/src/modes/ofb/ofb_start.c
deleted file mode 100644
index 280606359a36..000000000000
--- a/libs/tomcrypt/src/modes/ofb/ofb_start.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file ofb_start.c
-   OFB implementation, start chain, Tom St Denis
-*/
-
-
-#ifdef LTC_OFB_MODE
-
-/**
-   Initialize a OFB context
-   @param cipher      The index of the cipher desired
-   @param IV          The initialization vector
-   @param key         The secret key
-   @param keylen      The length of the secret key (octets)
-   @param num_rounds  Number of rounds in the cipher desired (0 for default)
-   @param ofb         The OFB state to initialize
-   @return CRYPT_OK if successful
-*/
-int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key,
-              int keylen, int num_rounds, symmetric_OFB *ofb)
-{
-   int x, err;
-
-   LTC_ARGCHK(IV != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(ofb != NULL);
-
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* copy details */
-   ofb->cipher = cipher;
-   ofb->blocklen = cipher_descriptor[cipher].block_length;
-   for (x = 0; x < ofb->blocklen; x++) {
-       ofb->IV[x] = IV[x];
-   }
-
-   /* init the cipher */
-   ofb->padlen = ofb->blocklen;
-   return cipher_descriptor[cipher].setup(key, keylen, num_rounds, &ofb->key);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/xts/xts_decrypt.c b/libs/tomcrypt/src/modes/xts/xts_decrypt.c
deleted file mode 100644
index e87a92e8103b..000000000000
--- a/libs/tomcrypt/src/modes/xts/xts_decrypt.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
- Source donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) to the LibTom Projects
- */
-
-#ifdef LTC_XTS_MODE
-
-static int _tweak_uncrypt(const unsigned char *C, unsigned char *P, unsigned char *T, symmetric_xts *xts)
-{
-   unsigned long x;
-   int err;
-
-   /* tweak encrypt block i */
-#ifdef LTC_FAST
-   for (x = 0; x < 16; x += sizeof(LTC_FAST_TYPE)) {
-      *(LTC_FAST_TYPE_PTR_CAST(&P[x])) = *(LTC_FAST_TYPE_PTR_CAST(&C[x])) ^ *(LTC_FAST_TYPE_PTR_CAST(&T[x]));
-   }
-#else
-   for (x = 0; x < 16; x++) {
-      P[x] = C[x] ^ T[x];
-   }
-#endif
-
-   err = cipher_descriptor[xts->cipher].ecb_decrypt(P, P, &xts->key1);
-
-#ifdef LTC_FAST
-   for (x = 0; x < 16; x += sizeof(LTC_FAST_TYPE)) {
-      *(LTC_FAST_TYPE_PTR_CAST(&P[x])) ^= *(LTC_FAST_TYPE_PTR_CAST(&T[x]));
-   }
-#else
-   for (x = 0; x < 16; x++) {
-      P[x] = P[x] ^ T[x];
-   }
-#endif
-
-   /* LFSR the tweak */
-   xts_mult_x(T);
-
-   return err;
-}
-
-/** XTS Decryption
- @param ct     [in] Ciphertext
- @param ptlen  Length of plaintext (and ciphertext)
- @param pt     [out]  Plaintext
- @param tweak  [in] The 128--bit encryption tweak (e.g. sector number)
- @param xts    The XTS structure
- Returns CRYPT_OK upon success
- */
-int xts_decrypt(const unsigned char *ct, unsigned long ptlen, unsigned char *pt, unsigned char *tweak,
-                symmetric_xts *xts)
-{
-   unsigned char PP[16], CC[16], T[16];
-   unsigned long i, m, mo, lim;
-   int err;
-
-   /* check inputs */
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(tweak != NULL);
-   LTC_ARGCHK(xts != NULL);
-
-   /* check if valid */
-   if ((err = cipher_is_valid(xts->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* get number of blocks */
-   m = ptlen >> 4;
-   mo = ptlen & 15;
-
-   /* must have at least one full block */
-   if (m == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (mo == 0) {
-      lim = m;
-   } else {
-      lim = m - 1;
-   }
-
-   if (cipher_descriptor[xts->cipher].accel_xts_decrypt && lim > 0) {
-
-      /* use accelerated decryption for whole blocks */
-      if ((err = cipher_descriptor[xts->cipher].accel_xts_decrypt(ct, pt, lim, tweak, &xts->key1, &xts->key2)) !=
-                 CRYPT_OK) {
-         return err;
-      }
-      ct += lim * 16;
-      pt += lim * 16;
-
-      /* tweak is encrypted on output */
-      XMEMCPY(T, tweak, sizeof(T));
-   } else {
-      /* encrypt the tweak */
-      if ((err = cipher_descriptor[xts->cipher].ecb_encrypt(tweak, T, &xts->key2)) != CRYPT_OK) {
-         return err;
-      }
-
-      for (i = 0; i < lim; i++) {
-         if ((err = _tweak_uncrypt(ct, pt, T, xts)) != CRYPT_OK) {
-            return err;
-         }
-         ct += 16;
-         pt += 16;
-      }
-   }
-
-   /* if ptlen not divide 16 then */
-   if (mo > 0) {
-      XMEMCPY(CC, T, 16);
-      xts_mult_x(CC);
-
-      /* PP = tweak decrypt block m-1 */
-      if ((err = _tweak_uncrypt(ct, PP, CC, xts)) != CRYPT_OK) {
-         return err;
-      }
-
-      /* Pm = first ptlen % 16 bytes of PP */
-      for (i = 0; i < mo; i++) {
-         CC[i] = ct[16 + i];
-         pt[16 + i] = PP[i];
-      }
-      for (; i < 16; i++) {
-         CC[i] = PP[i];
-      }
-
-      /* Pm-1 = Tweak uncrypt CC */
-      if ((err = _tweak_uncrypt(CC, pt, T, xts)) != CRYPT_OK) {
-         return err;
-      }
-   }
-
-   /* Decrypt the tweak back */
-   if ((err = cipher_descriptor[xts->cipher].ecb_decrypt(T, tweak, &xts->key2)) != CRYPT_OK) {
-      return err;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/xts/xts_done.c b/libs/tomcrypt/src/modes/xts/xts_done.c
deleted file mode 100644
index 3d1977e44775..000000000000
--- a/libs/tomcrypt/src/modes/xts/xts_done.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  Source donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) to the LibTom Projects
-*/
-
-#ifdef LTC_XTS_MODE
-
-/** Terminate XTS state
-   @param xts    The state to terminate
-*/
-void xts_done(symmetric_xts *xts)
-{
-   LTC_ARGCHKVD(xts != NULL);
-   cipher_descriptor[xts->cipher].done(&xts->key1);
-   cipher_descriptor[xts->cipher].done(&xts->key2);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/xts/xts_encrypt.c b/libs/tomcrypt/src/modes/xts/xts_encrypt.c
deleted file mode 100644
index c96aeec954a2..000000000000
--- a/libs/tomcrypt/src/modes/xts/xts_encrypt.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
- Source donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) to the LibTom Projects
- */
-
-#ifdef LTC_XTS_MODE
-
-static int _tweak_crypt(const unsigned char *P, unsigned char *C, unsigned char *T, symmetric_xts *xts)
-{
-   unsigned long x;
-   int err;
-
-   /* tweak encrypt block i */
-#ifdef LTC_FAST
-   for (x = 0; x < 16; x += sizeof(LTC_FAST_TYPE)) {
-      *(LTC_FAST_TYPE_PTR_CAST(&C[x])) = *(LTC_FAST_TYPE_PTR_CAST(&P[x])) ^ *(LTC_FAST_TYPE_PTR_CAST(&T[x]));
-   }
-#else
-   for (x = 0; x < 16; x++) {
-      C[x] = P[x] ^ T[x];
-   }
-#endif
-
-   if ((err = cipher_descriptor[xts->cipher].ecb_encrypt(C, C, &xts->key1)) != CRYPT_OK) {
-      return err;
-   }
-
-#ifdef LTC_FAST
-   for (x = 0; x < 16; x += sizeof(LTC_FAST_TYPE)) {
-      *(LTC_FAST_TYPE_PTR_CAST(&C[x])) ^= *(LTC_FAST_TYPE_PTR_CAST(&T[x]));
-   }
-#else
-   for (x = 0; x < 16; x++) {
-      C[x] = C[x] ^ T[x];
-   }
-#endif
-
-   /* LFSR the tweak */
-   xts_mult_x(T);
-
-   return CRYPT_OK;
-}
-
-/** XTS Encryption
- @param pt     [in]  Plaintext
- @param ptlen  Length of plaintext (and ciphertext)
- @param ct     [out] Ciphertext
- @param tweak  [in] The 128--bit encryption tweak (e.g. sector number)
- @param xts    The XTS structure
- Returns CRYPT_OK upon success
- */
-int xts_encrypt(const unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tweak,
-                symmetric_xts *xts)
-{
-   unsigned char PP[16], CC[16], T[16];
-   unsigned long i, m, mo, lim;
-   int err;
-
-   /* check inputs */
-   LTC_ARGCHK(pt != NULL);
-   LTC_ARGCHK(ct != NULL);
-   LTC_ARGCHK(tweak != NULL);
-   LTC_ARGCHK(xts != NULL);
-
-   /* check if valid */
-   if ((err = cipher_is_valid(xts->cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* get number of blocks */
-   m = ptlen >> 4;
-   mo = ptlen & 15;
-
-   /* must have at least one full block */
-   if (m == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (mo == 0) {
-      lim = m;
-   } else {
-      lim = m - 1;
-   }
-
-   if (cipher_descriptor[xts->cipher].accel_xts_encrypt && lim > 0) {
-
-      /* use accelerated encryption for whole blocks */
-      if ((err = cipher_descriptor[xts->cipher].accel_xts_encrypt(pt, ct, lim, tweak, &xts->key1, &xts->key2)) !=
-                 CRYPT_OK) {
-         return err;
-      }
-      ct += lim * 16;
-      pt += lim * 16;
-
-      /* tweak is encrypted on output */
-      XMEMCPY(T, tweak, sizeof(T));
-   } else {
-
-      /* encrypt the tweak */
-      if ((err = cipher_descriptor[xts->cipher].ecb_encrypt(tweak, T, &xts->key2)) != CRYPT_OK) {
-         return err;
-      }
-
-      for (i = 0; i < lim; i++) {
-         if ((err = _tweak_crypt(pt, ct, T, xts)) != CRYPT_OK) {
-            return err;
-         }
-         ct += 16;
-         pt += 16;
-      }
-   }
-
-   /* if ptlen not divide 16 then */
-   if (mo > 0) {
-      /* CC = tweak encrypt block m-1 */
-      if ((err = _tweak_crypt(pt, CC, T, xts)) != CRYPT_OK) {
-         return err;
-      }
-
-      /* Cm = first ptlen % 16 bytes of CC */
-      for (i = 0; i < mo; i++) {
-         PP[i] = pt[16 + i];
-         ct[16 + i] = CC[i];
-      }
-
-      for (; i < 16; i++) {
-         PP[i] = CC[i];
-      }
-
-      /* Cm-1 = Tweak encrypt PP */
-      if ((err = _tweak_crypt(PP, ct, T, xts)) != CRYPT_OK) {
-         return err;
-      }
-   }
-
-   /* Decrypt the tweak back */
-   if ((err = cipher_descriptor[xts->cipher].ecb_decrypt(T, tweak, &xts->key2)) != CRYPT_OK) {
-      return err;
-   }
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/xts/xts_init.c b/libs/tomcrypt/src/modes/xts/xts_init.c
deleted file mode 100644
index e237139656a9..000000000000
--- a/libs/tomcrypt/src/modes/xts/xts_init.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  Source donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) to the LibTom Projects
-*/
-
-#ifdef LTC_XTS_MODE
-
-/** Start XTS mode
-   @param cipher      The index of the cipher to use
-   @param key1        The encrypt key
-   @param key2        The tweak encrypt key
-   @param keylen      The length of the keys (each) in octets
-   @param num_rounds  The number of rounds for the cipher (0 == default)
-   @param xts         [out] XTS structure
-   Returns CRYPT_OK upon success.
-*/
-int xts_start(int cipher, const unsigned char *key1, const unsigned char *key2, unsigned long keylen, int num_rounds,
-              symmetric_xts *xts)
-{
-   int err;
-
-   /* check inputs */
-   LTC_ARGCHK(key1 != NULL);
-   LTC_ARGCHK(key2 != NULL);
-   LTC_ARGCHK(xts != NULL);
-
-   /* check if valid */
-   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (cipher_descriptor[cipher].block_length != 16) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* schedule the two ciphers */
-   if ((err = cipher_descriptor[cipher].setup(key1, keylen, num_rounds, &xts->key1)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = cipher_descriptor[cipher].setup(key2, keylen, num_rounds, &xts->key2)) != CRYPT_OK) {
-      return err;
-   }
-   xts->cipher = cipher;
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/modes/xts/xts_mult_x.c b/libs/tomcrypt/src/modes/xts/xts_mult_x.c
deleted file mode 100644
index bb63dfe80875..000000000000
--- a/libs/tomcrypt/src/modes/xts/xts_mult_x.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  Source donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) to the LibTom Projects
-*/
-
-#ifdef LTC_XTS_MODE
-
-/** multiply by x
-  @param I      The value to multiply by x (LFSR shift)
-*/
-void xts_mult_x(unsigned char *I)
-{
-   int x;
-   unsigned char t, tt;
-
-   for (x = t = 0; x < 16; x++) {
-      tt = I[x] >> 7;
-      I[x] = ((I[x] << 1) | t) & 0xFF;
-      t = tt;
-   }
-   if (tt) {
-      I[0] ^= 0x87;
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c b/libs/tomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c
deleted file mode 100644
index f0a490724fcb..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_bit_string.c
-  ASN.1 DER, encode a BIT STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a BIT STRING
-  @param in      The DER encoded BIT STRING
-  @param inlen   The size of the DER BIT STRING
-  @param out     [out] The array of bits stored (one per char)
-  @param outlen  [in/out] The number of bits stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_bit_string(const unsigned char *in,  unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long dlen, blen, x, y;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* packet must be at least 4 bytes */
-   if (inlen < 4) {
-       return CRYPT_INVALID_ARG;
-   }
-
-   /* check for 0x03 */
-   if ((in[0]&0x1F) != 0x03) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* offset in the data */
-   x = 1;
-
-   /* get the length of the data */
-   if (in[x] & 0x80) {
-      /* long format get number of length bytes */
-      y = in[x++] & 0x7F;
-
-      /* invalid if 0 or > 2 */
-      if (y == 0 || y > 2) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the data len */
-      dlen = 0;
-      while (y--) {
-         dlen = (dlen << 8) | (unsigned long)in[x++];
-      }
-   } else {
-      /* short format */
-      dlen = in[x++] & 0x7F;
-   }
-
-   /* is the data len too long or too short? */
-   if ((dlen == 0) || (dlen + x > inlen)) {
-       return CRYPT_INVALID_PACKET;
-   }
-
-   /* get padding count */
-   blen = ((dlen - 1) << 3) - (in[x++] & 7);
-
-   /* too many bits? */
-   if (blen > *outlen) {
-      *outlen = blen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* decode/store the bits */
-   for (y = 0; y < blen; y++) {
-       out[y] = (in[x] & (1 << (7 - (y & 7)))) ? 1 : 0;
-       if ((y & 7) == 7) {
-          ++x;
-       }
-   }
-
-   /* we done */
-   *outlen = blen;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/bit/der_decode_raw_bit_string.c b/libs/tomcrypt/src/pk/asn1/der/bit/der_decode_raw_bit_string.c
deleted file mode 100644
index 740a5a6e691a..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/bit/der_decode_raw_bit_string.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_bit_string.c
-  ASN.1 DER, encode a BIT STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-#define SETBIT(v, n)    (v=((unsigned char)(v) | (1U << (unsigned char)(n))))
-#define CLRBIT(v, n)    (v=((unsigned char)(v) & ~(1U << (unsigned char)(n))))
-
-/**
-  Store a BIT STRING
-  @param in      The DER encoded BIT STRING
-  @param inlen   The size of the DER BIT STRING
-  @param out     [out] The array of bits stored (8 per char)
-  @param outlen  [in/out] The number of bits stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_raw_bit_string(const unsigned char *in,  unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long dlen, blen, x, y;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* packet must be at least 4 bytes */
-   if (inlen < 4) {
-       return CRYPT_INVALID_ARG;
-   }
-
-   /* check for 0x03 */
-   if ((in[0]&0x1F) != 0x03) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* offset in the data */
-   x = 1;
-
-   /* get the length of the data */
-   if (in[x] & 0x80) {
-      /* long format get number of length bytes */
-      y = in[x++] & 0x7F;
-
-      /* invalid if 0 or > 2 */
-      if (y == 0 || y > 2) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the data len */
-      dlen = 0;
-      while (y--) {
-         dlen = (dlen << 8) | (unsigned long)in[x++];
-      }
-   } else {
-      /* short format */
-      dlen = in[x++] & 0x7F;
-   }
-
-   /* is the data len too long or too short? */
-   if ((dlen == 0) || (dlen + x > inlen)) {
-       return CRYPT_INVALID_PACKET;
-   }
-
-   /* get padding count */
-   blen = ((dlen - 1) << 3) - (in[x++] & 7);
-
-   /* too many bits? */
-   if (blen > *outlen) {
-      *outlen = blen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* decode/store the bits */
-   for (y = 0; y < blen; y++) {
-      if (in[x] & (1 << (7 - (y & 7)))) {
-         SETBIT(out[y/8], 7-(y%8));
-      } else {
-         CLRBIT(out[y/8], 7-(y%8));
-      }
-      if ((y & 7) == 7) {
-         ++x;
-      }
-   }
-
-   /* we done */
-   *outlen = blen;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c b/libs/tomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c
deleted file mode 100644
index 29999ab3c03c..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_bit_string.c
-  ASN.1 DER, encode a BIT STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a BIT STRING
-  @param in       The array of bits to store (one per char)
-  @param inlen    The number of bits tostore
-  @param out      [out] The destination for the DER encoded BIT STRING
-  @param outlen   [in/out] The max size and resulting size of the DER BIT STRING
-  @return CRYPT_OK if successful
-*/
-int der_encode_bit_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long len, x, y;
-   unsigned char buf;
-   int           err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* avoid overflows */
-   if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* store header (include bit padding count in length) */
-   x = 0;
-   y = (inlen >> 3) + ((inlen&7) ? 1 : 0) + 1;
-
-   out[x++] = 0x03;
-   if (y < 128) {
-      out[x++] = (unsigned char)y;
-   } else if (y < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)y;
-   } else if (y < 65536) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((y>>8)&255);
-      out[x++] = (unsigned char)(y&255);
-   }
-
-   /* store number of zero padding bits */
-   out[x++] = (unsigned char)((8 - inlen) & 7);
-
-   /* store the bits in big endian format */
-   for (y = buf = 0; y < inlen; y++) {
-       buf |= (in[y] ? 1 : 0) << (7 - (y & 7));
-       if ((y & 7) == 7) {
-          out[x++] = buf;
-          buf      = 0;
-       }
-   }
-   /* store last byte */
-   if (inlen & 7) {
-      out[x++] = buf;
-   }
-   *outlen = x;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/bit/der_encode_raw_bit_string.c b/libs/tomcrypt/src/pk/asn1/der/bit/der_encode_raw_bit_string.c
deleted file mode 100644
index 36076fdb1ebb..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/bit/der_encode_raw_bit_string.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_bit_string.c
-  ASN.1 DER, encode a BIT STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-#define getbit(n, k) (((n) & ( 1 << (k) )) >> (k))
-
-/**
-  Store a BIT STRING
-  @param in       The array of bits to store (8 per char)
-  @param inlen    The number of bits to store
-  @param out      [out] The destination for the DER encoded BIT STRING
-  @param outlen   [in/out] The max size and resulting size of the DER BIT STRING
-  @return CRYPT_OK if successful
-*/
-int der_encode_raw_bit_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long len, x, y;
-   unsigned char buf;
-   int           err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* avoid overflows */
-   if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* store header (include bit padding count in length) */
-   x = 0;
-   y = (inlen >> 3) + ((inlen&7) ? 1 : 0) + 1;
-
-   out[x++] = 0x03;
-   if (y < 128) {
-      out[x++] = (unsigned char)y;
-   } else if (y < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)y;
-   } else if (y < 65536) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((y>>8)&255);
-      out[x++] = (unsigned char)(y&255);
-   }
-
-   /* store number of zero padding bits */
-   out[x++] = (unsigned char)((8 - inlen) & 7);
-
-   /* store the bits in big endian format */
-   for (y = buf = 0; y < inlen; y++) {
-      buf |= (getbit(in[y/8],7-y%8)?1:0) << (7 - (y & 7));
-      if ((y & 7) == 7) {
-         out[x++] = buf;
-         buf      = 0;
-      }
-   }
-   /* store last byte */
-   if (inlen & 7) {
-      out[x++] = buf;
-   }
-
-   *outlen = x;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c b/libs/tomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c
deleted file mode 100644
index 05de007a6fc9..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_bit_string.c
-  ASN.1 DER, get length of BIT STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-/**
-  Gets length of DER encoding of BIT STRING
-  @param nbits  The number of bits in the string to encode
-  @param outlen [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_bit_string(unsigned long nbits, unsigned long *outlen)
-{
-   unsigned long nbytes;
-   LTC_ARGCHK(outlen != NULL);
-
-   /* get the number of the bytes */
-   nbytes = (nbits >> 3) + ((nbits & 7) ? 1 : 0) + 1;
-
-   if (nbytes < 128) {
-      /* 03 LL PP DD DD DD ... */
-      *outlen = 2 + nbytes;
-   } else if (nbytes < 256) {
-      /* 03 81 LL PP DD DD DD ... */
-      *outlen = 3 + nbytes;
-   } else if (nbytes < 65536) {
-      /* 03 82 LL LL PP DD DD DD ... */
-      *outlen = 4 + nbytes;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c b/libs/tomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c
deleted file mode 100644
index 7cd624f25b34..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_boolean.c
-  ASN.1 DER, decode a BOOLEAN, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Read a BOOLEAN
-  @param in      The destination for the DER encoded BOOLEAN
-  @param inlen   The size of the DER BOOLEAN
-  @param out     [out]  The boolean to decode
-  @return CRYPT_OK if successful
-*/
-int der_decode_boolean(const unsigned char *in, unsigned long inlen,
-                                       int *out)
-{
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(out != NULL);
-
-   if (inlen < 3 || in[0] != 0x01 || in[1] != 0x01 || (in[2] != 0x00 && in[2] != 0xFF)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   *out = (in[2]==0xFF) ? 1 : 0;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c b/libs/tomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c
deleted file mode 100644
index 5d6835a0e1dd..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_boolean.c
-  ASN.1 DER, encode a BOOLEAN, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a BOOLEAN
-  @param in       The boolean to encode
-  @param out      [out] The destination for the DER encoded BOOLEAN
-  @param outlen   [in/out] The max size and resulting size of the DER BOOLEAN
-  @return CRYPT_OK if successful
-*/
-int der_encode_boolean(int in,
-                       unsigned char *out, unsigned long *outlen)
-{
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(out    != NULL);
-
-   if (*outlen < 3) {
-       *outlen = 3;
-       return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   *outlen = 3;
-   out[0] = 0x01;
-   out[1] = 0x01;
-   out[2] = in ? 0xFF : 0x00;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c b/libs/tomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c
deleted file mode 100644
index f31f05eaa63f..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_boolean.c
-  ASN.1 DER, get length of a BOOLEAN, Tom St Denis
-*/
-
-#ifdef LTC_DER
-/**
-  Gets length of DER encoding of a BOOLEAN
-  @param outlen [out] The length of the DER encoding
-  @return CRYPT_OK if successful
-*/
-int der_length_boolean(unsigned long *outlen)
-{
-   LTC_ARGCHK(outlen != NULL);
-   *outlen = 3;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/choice/der_decode_choice.c b/libs/tomcrypt/src/pk/asn1/der/choice/der_decode_choice.c
deleted file mode 100644
index 41b2e5d49852..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/choice/der_decode_choice.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_choice.c
-  ASN.1 DER, decode a CHOICE, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-   Decode a CHOICE
-   @param in       The DER encoded input
-   @param inlen    [in/out] The size of the input and resulting size of read type
-   @param list     The list of items to decode
-   @param outlen   The number of items in the list
-   @return CRYPT_OK on success
-*/
-int der_decode_choice(const unsigned char *in,   unsigned long *inlen,
-                            ltc_asn1_list *list, unsigned long  outlen)
-{
-   unsigned long size, x, z;
-   void          *data;
-
-   LTC_ARGCHK(in    != NULL);
-   LTC_ARGCHK(inlen != NULL);
-   LTC_ARGCHK(list  != NULL);
-
-   /* get blk size */
-   if (*inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* set all of the "used" flags to zero */
-   for (x = 0; x < outlen; x++) {
-       list[x].used = 0;
-   }
-
-   /* now scan until we have a winner */
-   for (x = 0; x < outlen; x++) {
-       size = list[x].size;
-       data = list[x].data;
-
-       switch (list[x].type) {
-           case LTC_ASN1_BOOLEAN:
-               if (der_decode_boolean(in, *inlen, data) == CRYPT_OK) {
-                  if (der_length_boolean(&z) == CRYPT_OK) {
-                      list[x].used = 1;
-                      *inlen       = z;
-                      return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_INTEGER:
-               if (der_decode_integer(in, *inlen, data) == CRYPT_OK) {
-                  if (der_length_integer(data, &z) == CRYPT_OK) {
-                      list[x].used = 1;
-                      *inlen       = z;
-                      return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_SHORT_INTEGER:
-               if (der_decode_short_integer(in, *inlen, data) == CRYPT_OK) {
-                  if (der_length_short_integer(size, &z) == CRYPT_OK) {
-                      list[x].used = 1;
-                      *inlen       = z;
-                      return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_BIT_STRING:
-               if (der_decode_bit_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_bit_string(size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_RAW_BIT_STRING:
-               if (der_decode_raw_bit_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_bit_string(size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_OCTET_STRING:
-               if (der_decode_octet_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_octet_string(size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_NULL:
-               if (*inlen == 2 && in[x] == 0x05 && in[x+1] == 0x00) {
-                  *inlen = 2;
-                  list[x].used   = 1;
-                  return CRYPT_OK;
-               }
-               break;
-
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-               if (der_decode_object_identifier(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_object_identifier(data, size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_TELETEX_STRING:
-               if (der_decode_teletex_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_teletex_string(data, size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_IA5_STRING:
-               if (der_decode_ia5_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_ia5_string(data, size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_PRINTABLE_STRING:
-               if (der_decode_printable_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_printable_string(data, size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_UTF8_STRING:
-               if (der_decode_utf8_string(in, *inlen, data, &size) == CRYPT_OK) {
-                  if (der_length_utf8_string(data, size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     list[x].size = size;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_UTCTIME:
-               z = *inlen;
-               if (der_decode_utctime(in, &z, data) == CRYPT_OK) {
-                  list[x].used = 1;
-                  *inlen       = z;
-                  return CRYPT_OK;
-               }
-               break;
-
-           case LTC_ASN1_GENERALIZEDTIME:
-               z = *inlen;
-               if (der_decode_generalizedtime(in, &z, data) == CRYPT_OK) {
-                  list[x].used = 1;
-                  *inlen       = z;
-                  return CRYPT_OK;
-               }
-               break;
-
-           case LTC_ASN1_SET:
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_SEQUENCE:
-               if (der_decode_sequence(in, *inlen, data, size) == CRYPT_OK) {
-                  if (der_length_sequence(data, size, &z) == CRYPT_OK) {
-                     list[x].used = 1;
-                     *inlen       = z;
-                     return CRYPT_OK;
-                  }
-               }
-               break;
-
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-           case LTC_ASN1_EOL:
-               return CRYPT_INVALID_ARG;
-       }
-   }
-
-   return CRYPT_INVALID_PACKET;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c b/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c
deleted file mode 100644
index b9b7db97eadf..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_generalizedtime.c
-  ASN.1 DER, decode a GeneralizedTime, Steffen Jaeckel
-  Based on der_decode_utctime.c
-*/
-
-#ifdef LTC_DER
-
-static int _char_to_int(unsigned char x)
-{
-   switch (x)  {
-      case '0': return 0;
-      case '1': return 1;
-      case '2': return 2;
-      case '3': return 3;
-      case '4': return 4;
-      case '5': return 5;
-      case '6': return 6;
-      case '7': return 7;
-      case '8': return 8;
-      case '9': return 9;
-      default:  return 100;
-   }
-}
-
-#define DECODE_V(y, max) do {\
-   y  = _char_to_int(buf[x])*10 + _char_to_int(buf[x+1]); \
-   if (y >= max) return CRYPT_INVALID_PACKET;           \
-   x += 2; \
-} while(0)
-
-#define DECODE_V4(y, max) do {\
-   y  = _char_to_int(buf[x])*1000 + _char_to_int(buf[x+1])*100 + _char_to_int(buf[x+2])*10 + _char_to_int(buf[x+3]); \
-   if (y >= max) return CRYPT_INVALID_PACKET; \
-   x += 4; \
-} while(0)
-
-/**
-  Decodes a Generalized time structure in DER format (reads all 6 valid encoding formats)
-  @param in     Input buffer
-  @param inlen  Length of input buffer in octets
-  @param out    [out] Destination of Generalized time structure
-  @return CRYPT_OK   if successful
-*/
-int der_decode_generalizedtime(const unsigned char *in, unsigned long *inlen,
-                               ltc_generalizedtime *out)
-{
-   unsigned char buf[32];
-   unsigned long x;
-   int           y;
-
-   LTC_ARGCHK(in    != NULL);
-   LTC_ARGCHK(inlen != NULL);
-   LTC_ARGCHK(out   != NULL);
-
-   /* check header */
-   if (*inlen < 2UL || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* decode the string */
-   for (x = 0; x < in[1]; x++) {
-       y = der_ia5_value_decode(in[x+2]);
-       if (y == -1) {
-          return CRYPT_INVALID_PACKET;
-       }
-       if (!((y >= '0' && y <= '9')
-            || y == 'Z' || y == '.'
-            || y == '+' || y == '-')) {
-          return CRYPT_INVALID_PACKET;
-       }
-       buf[x] = y;
-   }
-   *inlen = 2 + x;
-
-   if (x < 15) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* possible encodings are
-YYYYMMDDhhmmssZ
-YYYYMMDDhhmmss+hh'mm'
-YYYYMMDDhhmmss-hh'mm'
-YYYYMMDDhhmmss.fsZ
-YYYYMMDDhhmmss.fs+hh'mm'
-YYYYMMDDhhmmss.fs-hh'mm'
-
-    So let's do a trivial decode upto [including] ss
-   */
-
-    x = 0;
-    DECODE_V4(out->YYYY, 10000);
-    DECODE_V(out->MM, 13);
-    DECODE_V(out->DD, 32);
-    DECODE_V(out->hh, 24);
-    DECODE_V(out->mm, 60);
-    DECODE_V(out->ss, 60);
-
-    /* clear fractional seconds info */
-    out->fs = 0;
-
-    /* now is it Z or . */
-    if (buf[x] == 'Z') {
-       return CRYPT_OK;
-    } else if (buf[x] == '.') {
-       x++;
-       while (buf[x] >= '0' && buf[x] <= '9') {
-          unsigned fs = out->fs;
-          if (x >= sizeof(buf)) return CRYPT_INVALID_PACKET;
-          out->fs *= 10;
-          out->fs += _char_to_int(buf[x]);
-          if (fs > out->fs) return CRYPT_OVERFLOW;
-          x++;
-       }
-    }
-
-    /* now is it Z, +, - */
-    if (buf[x] == 'Z') {
-       return CRYPT_OK;
-    } else if (buf[x] == '+' || buf[x] == '-') {
-       out->off_dir = (buf[x++] == '+') ? 0 : 1;
-       DECODE_V(out->off_hh, 24);
-       DECODE_V(out->off_mm, 60);
-       return CRYPT_OK;
-    } else {
-       return CRYPT_INVALID_PACKET;
-    }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c b/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c
deleted file mode 100644
index 888f1758b673..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_utctime.c
-  ASN.1 DER, encode a GeneralizedTime, Steffen Jaeckel
-  Based on der_encode_utctime.c
-*/
-
-#ifdef LTC_DER
-
-static const char * const baseten = "0123456789";
-
-#define STORE_V(y) do {\
-    out[x++] = der_ia5_char_encode(baseten[(y/10) % 10]); \
-    out[x++] = der_ia5_char_encode(baseten[y % 10]); \
-} while(0)
-
-#define STORE_V4(y) do {\
-    out[x++] = der_ia5_char_encode(baseten[(y/1000) % 10]); \
-    out[x++] = der_ia5_char_encode(baseten[(y/100) % 10]); \
-    out[x++] = der_ia5_char_encode(baseten[(y/10) % 10]); \
-    out[x++] = der_ia5_char_encode(baseten[y % 10]); \
-} while(0)
-
-/**
-  Encodes a Generalized time structure in DER format
-  @param gtime        The GeneralizedTime structure to encode
-  @param out          The destination of the DER encoding of the GeneralizedTime structure
-  @param outlen       [in/out] The length of the DER encoding
-  @return CRYPT_OK if successful
-*/
-int der_encode_generalizedtime(ltc_generalizedtime *gtime,
-                               unsigned char       *out,   unsigned long *outlen)
-{
-    unsigned long x, tmplen;
-    int           err;
-
-    LTC_ARGCHK(gtime != NULL);
-    LTC_ARGCHK(out     != NULL);
-    LTC_ARGCHK(outlen  != NULL);
-
-    if ((err = der_length_generalizedtime(gtime, &tmplen)) != CRYPT_OK) {
-       return err;
-    }
-    if (tmplen > *outlen) {
-        *outlen = tmplen;
-        return CRYPT_BUFFER_OVERFLOW;
-    }
-
-    /* store header */
-    out[0] = 0x18;
-
-    /* store values */
-    x = 2;
-    STORE_V4(gtime->YYYY);
-    STORE_V(gtime->MM);
-    STORE_V(gtime->DD);
-    STORE_V(gtime->hh);
-    STORE_V(gtime->mm);
-    STORE_V(gtime->ss);
-
-    if (gtime->fs) {
-       unsigned long divisor;
-       unsigned fs = gtime->fs;
-       unsigned len = 0;
-       out[x++] = der_ia5_char_encode('.');
-       divisor = 1;
-       do {
-          fs /= 10;
-          divisor *= 10;
-          len++;
-       } while(fs != 0);
-       while (len-- > 1) {
-          divisor /= 10;
-          out[x++] = der_ia5_char_encode(baseten[(gtime->fs/divisor) % 10]);
-       }
-       out[x++] = der_ia5_char_encode(baseten[gtime->fs % 10]);
-    }
-
-    if (gtime->off_mm || gtime->off_hh) {
-       out[x++] = der_ia5_char_encode(gtime->off_dir ? '-' : '+');
-       STORE_V(gtime->off_hh);
-       STORE_V(gtime->off_mm);
-    } else {
-       out[x++] = der_ia5_char_encode('Z');
-    }
-
-    /* store length */
-    out[1] = (unsigned char)(x - 2);
-
-    /* all good let's return */
-    *outlen = x;
-    return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c b/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c
deleted file mode 100644
index a58e11ef1ede..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_utctime.c
-  ASN.1 DER, get length of GeneralizedTime, Steffen Jaeckel
-  Based on der_length_utctime.c
-*/
-
-#ifdef LTC_DER
-
-/**
-  Gets length of DER encoding of GeneralizedTime
-  @param gtime        The GeneralizedTime structure to get the size of
-  @param outlen [out] The length of the DER encoding
-  @return CRYPT_OK if successful
-*/
-int der_length_generalizedtime(ltc_generalizedtime *gtime, unsigned long *outlen)
-{
-   LTC_ARGCHK(outlen  != NULL);
-   LTC_ARGCHK(gtime != NULL);
-
-   if (gtime->fs == 0) {
-      /* we encode as YYYYMMDDhhmmssZ */
-      *outlen = 2 + 14 + 1;
-   } else {
-      unsigned long len = 2 + 14 + 1;
-      unsigned fs = gtime->fs;
-      do {
-         fs /= 10;
-         len++;
-      } while(fs != 0);
-      if (gtime->off_hh == 0 && gtime->off_mm == 0) {
-         /* we encode as YYYYMMDDhhmmss.fsZ */
-         len += 1;
-      }
-      else {
-         /* we encode as YYYYMMDDhhmmss.fs{+|-}hh'mm' */
-         len += 5;
-      }
-      *outlen = len;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c b/libs/tomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c
deleted file mode 100644
index 401e208ef4e8..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_ia5_string.c
-  ASN.1 DER, encode a IA5 STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a IA5 STRING
-  @param in      The DER encoded IA5 STRING
-  @param inlen   The size of the DER IA5 STRING
-  @param out     [out] The array of octets stored (one per char)
-  @param outlen  [in/out] The number of octets stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-   int           t;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* must have header at least */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* check for 0x16 */
-   if ((in[0] & 0x1F) != 0x16) {
-      return CRYPT_INVALID_PACKET;
-   }
-   x = 1;
-
-   /* decode the length */
-   if (in[x] & 0x80) {
-      /* valid # of bytes in length are 1,2,3 */
-      y = in[x] & 0x7F;
-      if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the length in */
-      len = 0;
-      ++x;
-      while (y--) {
-         len = (len << 8) | in[x++];
-      }
-   } else {
-      len = in[x++] & 0x7F;
-   }
-
-   /* is it too long? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (len + x > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* read the data */
-   for (y = 0; y < len; y++) {
-       t = der_ia5_value_decode(in[x++]);
-       if (t == -1) {
-           return CRYPT_INVALID_ARG;
-       }
-       out[y] = t;
-   }
-
-   *outlen = y;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c b/libs/tomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c
deleted file mode 100644
index 6f183fac6852..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_ia5_string.c
-  ASN.1 DER, encode a IA5 STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-  Store an IA5 STRING
-  @param in       The array of IA5 to store (one per char)
-  @param inlen    The number of IA5 to store
-  @param out      [out] The destination for the DER encoded IA5 STRING
-  @param outlen   [in/out] The max size and resulting size of the DER IA5 STRING
-  @return CRYPT_OK if successful
-*/
-int der_encode_ia5_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-   int           err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* get the size */
-   if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* too big? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* encode the header+len */
-   x = 0;
-   out[x++] = 0x16;
-   if (inlen < 128) {
-      out[x++] = (unsigned char)inlen;
-   } else if (inlen < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)inlen;
-   } else if (inlen < 65536UL) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((inlen>>8)&255);
-      out[x++] = (unsigned char)(inlen&255);
-   } else if (inlen < 16777216UL) {
-      out[x++] = 0x83;
-      out[x++] = (unsigned char)((inlen>>16)&255);
-      out[x++] = (unsigned char)((inlen>>8)&255);
-      out[x++] = (unsigned char)(inlen&255);
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* store octets */
-   for (y = 0; y < inlen; y++) {
-       out[x++] = der_ia5_char_encode(in[y]);
-   }
-
-   /* retun length */
-   *outlen = x;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c b/libs/tomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c
deleted file mode 100644
index fa40af1c1283..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_ia5_string.c
-  ASN.1 DER, get length of IA5 STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-static const struct {
-   int code, value;
-} ia5_table[] = {
-{ '\0', 0 },
-{ '\a', 7 },
-{ '\b', 8 },
-{ '\t', 9 },
-{ '\n', 10 },
-{ '\f', 12 },
-{ '\r', 13 },
-{ ' ', 32 },
-{ '!', 33 },
-{ '"', 34 },
-{ '#', 35 },
-{ '$', 36 },
-{ '%', 37 },
-{ '&', 38 },
-{ '\'', 39 },
-{ '(', 40 },
-{ ')', 41 },
-{ '*', 42 },
-{ '+', 43 },
-{ ',', 44 },
-{ '-', 45 },
-{ '.', 46 },
-{ '/', 47 },
-{ '0', 48 },
-{ '1', 49 },
-{ '2', 50 },
-{ '3', 51 },
-{ '4', 52 },
-{ '5', 53 },
-{ '6', 54 },
-{ '7', 55 },
-{ '8', 56 },
-{ '9', 57 },
-{ ':', 58 },
-{ ';', 59 },
-{ '<', 60 },
-{ '=', 61 },
-{ '>', 62 },
-{ '?', 63 },
-{ '@', 64 },
-{ 'A', 65 },
-{ 'B', 66 },
-{ 'C', 67 },
-{ 'D', 68 },
-{ 'E', 69 },
-{ 'F', 70 },
-{ 'G', 71 },
-{ 'H', 72 },
-{ 'I', 73 },
-{ 'J', 74 },
-{ 'K', 75 },
-{ 'L', 76 },
-{ 'M', 77 },
-{ 'N', 78 },
-{ 'O', 79 },
-{ 'P', 80 },
-{ 'Q', 81 },
-{ 'R', 82 },
-{ 'S', 83 },
-{ 'T', 84 },
-{ 'U', 85 },
-{ 'V', 86 },
-{ 'W', 87 },
-{ 'X', 88 },
-{ 'Y', 89 },
-{ 'Z', 90 },
-{ '[', 91 },
-{ '\\', 92 },
-{ ']', 93 },
-{ '^', 94 },
-{ '_', 95 },
-{ '`', 96 },
-{ 'a', 97 },
-{ 'b', 98 },
-{ 'c', 99 },
-{ 'd', 100 },
-{ 'e', 101 },
-{ 'f', 102 },
-{ 'g', 103 },
-{ 'h', 104 },
-{ 'i', 105 },
-{ 'j', 106 },
-{ 'k', 107 },
-{ 'l', 108 },
-{ 'm', 109 },
-{ 'n', 110 },
-{ 'o', 111 },
-{ 'p', 112 },
-{ 'q', 113 },
-{ 'r', 114 },
-{ 's', 115 },
-{ 't', 116 },
-{ 'u', 117 },
-{ 'v', 118 },
-{ 'w', 119 },
-{ 'x', 120 },
-{ 'y', 121 },
-{ 'z', 122 },
-{ '{', 123 },
-{ '|', 124 },
-{ '}', 125 },
-{ '~', 126 }
-};
-
-int der_ia5_char_encode(int c)
-{
-   int x;
-   for (x = 0; x < (int)(sizeof(ia5_table)/sizeof(ia5_table[0])); x++) {
-       if (ia5_table[x].code == c) {
-          return ia5_table[x].value;
-       }
-   }
-   return -1;
-}
-
-int der_ia5_value_decode(int v)
-{
-   int x;
-   for (x = 0; x < (int)(sizeof(ia5_table)/sizeof(ia5_table[0])); x++) {
-       if (ia5_table[x].value == v) {
-          return ia5_table[x].code;
-       }
-   }
-   return -1;
-}
-
-/**
-  Gets length of DER encoding of IA5 STRING
-  @param octets   The values you want to encode
-  @param noctets  The number of octets in the string to encode
-  @param outlen   [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen)
-{
-   unsigned long x;
-
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(octets != NULL);
-
-   /* scan string for validity */
-   for (x = 0; x < noctets; x++) {
-       if (der_ia5_char_encode(octets[x]) == -1) {
-          return CRYPT_INVALID_ARG;
-       }
-   }
-
-   if (noctets < 128) {
-      /* 16 LL DD DD DD ... */
-      *outlen = 2 + noctets;
-   } else if (noctets < 256) {
-      /* 16 81 LL DD DD DD ... */
-      *outlen = 3 + noctets;
-   } else if (noctets < 65536UL) {
-      /* 16 82 LL LL DD DD DD ... */
-      *outlen = 4 + noctets;
-   } else if (noctets < 16777216UL) {
-      /* 16 83 LL LL LL DD DD DD ... */
-      *outlen = 5 + noctets;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/integer/der_decode_integer.c b/libs/tomcrypt/src/pk/asn1/der/integer/der_decode_integer.c
deleted file mode 100644
index 0ba76fcc6394..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/integer/der_decode_integer.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_integer.c
-  ASN.1 DER, decode an integer, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Read a mp_int integer
-  @param in       The DER encoded data
-  @param inlen    Size of DER encoded data
-  @param num      The first mp_int to decode
-  @return CRYPT_OK if successful
-*/
-int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num)
-{
-   unsigned long x, y, z;
-   int           err;
-
-   LTC_ARGCHK(num    != NULL);
-   LTC_ARGCHK(in     != NULL);
-
-   /* min DER INTEGER is 0x02 01 00 == 0 */
-   if (inlen < (1 + 1 + 1)) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* ok expect 0x02 when we AND with 0001 1111 [1F] */
-   x = 0;
-   if ((in[x++] & 0x1F) != 0x02) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* now decode the len stuff */
-   z = in[x++];
-
-   if ((z & 0x80) == 0x00) {
-      /* short form */
-
-      /* will it overflow? */
-      if (x + z > inlen) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* no so read it */
-      if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, z)) != CRYPT_OK) {
-         return err;
-      }
-   } else {
-      /* long form */
-      z &= 0x7F;
-
-      /* will number of length bytes overflow? (or > 4) */
-      if (((x + z) > inlen) || (z > 4) || (z == 0)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* now read it in */
-      y = 0;
-      while (z--) {
-         y = ((unsigned long)(in[x++])) | (y << 8);
-      }
-
-      /* now will reading y bytes overrun? */
-      if ((x + y) > inlen) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* no so read it */
-      if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, y)) != CRYPT_OK) {
-         return err;
-      }
-   }
-
-   /* see if it's negative */
-   if (in[x] & 0x80) {
-      void *tmp;
-      if (mp_init(&tmp) != CRYPT_OK) {
-         return CRYPT_MEM;
-      }
-
-      if (mp_2expt(tmp, mp_count_bits(num)) != CRYPT_OK || mp_sub(num, tmp, num) != CRYPT_OK) {
-         mp_clear(tmp);
-         return CRYPT_MEM;
-      }
-      mp_clear(tmp);
-   }
-
-   return CRYPT_OK;
-
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/integer/der_encode_integer.c b/libs/tomcrypt/src/pk/asn1/der/integer/der_encode_integer.c
deleted file mode 100644
index bd55883b96c5..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/integer/der_encode_integer.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_integer.c
-  ASN.1 DER, encode an integer, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/* Exports a positive bignum as DER format (upto 2^32 bytes in size) */
-/**
-  Store a mp_int integer
-  @param num      The first mp_int to encode
-  @param out      [out] The destination for the DER encoded integers
-  @param outlen   [in/out] The max size and resulting size of the DER encoded integers
-  @return CRYPT_OK if successful
-*/
-int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen)
-{
-   unsigned long tmplen, y;
-   int           err, leading_zero;
-
-   LTC_ARGCHK(num    != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* find out how big this will be */
-   if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (*outlen < tmplen) {
-      *outlen = tmplen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (mp_cmp_d(num, 0) != LTC_MP_LT) {
-      /* we only need a leading zero if the msb of the first byte is one */
-      if ((mp_count_bits(num) & 7) == 0 || mp_iszero(num) == LTC_MP_YES) {
-         leading_zero = 1;
-      } else {
-         leading_zero = 0;
-      }
-
-      /* get length of num in bytes (plus 1 since we force the msbyte to zero) */
-      y = mp_unsigned_bin_size(num) + leading_zero;
-   } else {
-      leading_zero = 0;
-      y            = mp_count_bits(num);
-      y            = y + (8 - (y & 7));
-      y            = y >> 3;
-      if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) --y;
-   }
-
-   /* now store initial data */
-   *out++ = 0x02;
-   if (y < 128) {
-      /* short form */
-      *out++ = (unsigned char)y;
-   } else if (y < 256) {
-      *out++ = 0x81;
-      *out++ = (unsigned char)y;
-   } else if (y < 65536UL) {
-      *out++ = 0x82;
-      *out++ = (unsigned char)((y>>8)&255);
-      *out++ = (unsigned char)y;
-   } else if (y < 16777216UL) {
-      *out++ = 0x83;
-      *out++ = (unsigned char)((y>>16)&255);
-      *out++ = (unsigned char)((y>>8)&255);
-      *out++ = (unsigned char)y;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* now store msbyte of zero if num is non-zero */
-   if (leading_zero) {
-      *out++ = 0x00;
-   }
-
-   /* if it's not zero store it as big endian */
-   if (mp_cmp_d(num, 0) == LTC_MP_GT) {
-      /* now store the mpint */
-      if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) {
-          return err;
-      }
-   } else if (mp_iszero(num) != LTC_MP_YES) {
-      void *tmp;
-
-      /* negative */
-      if (mp_init(&tmp) != CRYPT_OK) {
-         return CRYPT_MEM;
-      }
-
-      /* 2^roundup and subtract */
-      y = mp_count_bits(num);
-      y = y + (8 - (y & 7));
-      if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) y -= 8;
-      if (mp_2expt(tmp, y) != CRYPT_OK || mp_add(tmp, num, tmp) != CRYPT_OK) {
-         mp_clear(tmp);
-         return CRYPT_MEM;
-      }
-      if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) {
-         mp_clear(tmp);
-         return err;
-      }
-      mp_clear(tmp);
-   }
-
-   /* we good */
-   *outlen = tmplen;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/integer/der_length_integer.c b/libs/tomcrypt/src/pk/asn1/der/integer/der_length_integer.c
deleted file mode 100644
index 0ef46b59e5d4..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/integer/der_length_integer.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_integer.c
-  ASN.1 DER, get length of encoding, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-/**
-  Gets length of DER encoding of num
-  @param num    The int to get the size of
-  @param outlen [out] The length of the DER encoding for the given integer
-  @return CRYPT_OK if successful
-*/
-int der_length_integer(void *num, unsigned long *outlen)
-{
-   unsigned long z, len;
-   int           leading_zero;
-
-   LTC_ARGCHK(num     != NULL);
-   LTC_ARGCHK(outlen  != NULL);
-
-   if (mp_cmp_d(num, 0) != LTC_MP_LT) {
-      /* positive */
-
-      /* we only need a leading zero if the msb of the first byte is one */
-      if ((mp_count_bits(num) & 7) == 0 || mp_iszero(num) == LTC_MP_YES) {
-         leading_zero = 1;
-      } else {
-         leading_zero = 0;
-      }
-
-      /* size for bignum */
-      z = len = leading_zero + mp_unsigned_bin_size(num);
-   } else {
-      /* it's negative */
-      /* find power of 2 that is a multiple of eight and greater than count bits */
-      z = mp_count_bits(num);
-      z = z + (8 - (z & 7));
-      if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) --z;
-      len = z = z >> 3;
-   }
-
-   /* now we need a length */
-   if (z < 128) {
-      /* short form */
-      ++len;
-   } else {
-      /* long form (relies on z != 0), assumes length bytes < 128 */
-      ++len;
-
-      while (z) {
-         ++len;
-         z >>= 8;
-      }
-   }
-
-   /* we need a 0x02 to indicate it's INTEGER */
-   ++len;
-
-   /* return length */
-   *outlen = len;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c b/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c
deleted file mode 100644
index 90a9ff175d24..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_object_identifier.c
-  ASN.1 DER, Decode Object Identifier, Tom St Denis
-*/
-
-#ifdef LTC_DER
-/**
-  Decode OID data and store the array of integers in words
-  @param in      The OID DER encoded data
-  @param inlen   The length of the OID data
-  @param words   [out] The destination of the OID words
-  @param outlen  [in/out] The number of OID words
-  @return CRYPT_OK if successful
-*/
-int der_decode_object_identifier(const unsigned char *in,    unsigned long  inlen,
-                                       unsigned long *words, unsigned long *outlen)
-{
-   unsigned long x, y, t, len;
-   int err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(words  != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* header is at least 3 bytes */
-   if (inlen < 3) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* must be room for at least two words */
-   if (*outlen < 2) {
-      *outlen = 2;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* decode the packet header */
-   x = 0;
-   if ((in[x++] & 0x1F) != 0x06) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* get the length */
-   if (in[x] < 128) {
-      len = in[x++];
-   } else {
-      if (in[x] < 0x81 || in[x] > 0x82) {
-         return CRYPT_INVALID_PACKET;
-      }
-      y   = in[x++] & 0x7F;
-      len = 0;
-      while (y--) {
-         len = (len << 8) | (unsigned long)in[x++];
-      }
-   }
-
-   if (len < 1 || (len + x) > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* decode words */
-   y = 0;
-   t = 0;
-   while (len--) {
-      t = (t << 7) | (in[x] & 0x7F);
-      if (!(in[x++] & 0x80)) {
-         /* store t */
-         if (y >= *outlen) {
-            y++;
-         } else {
-            if (y == 0) {
-               words[0] = t / 40;
-               words[1] = t % 40;
-               y = 2;
-            } else {
-               words[y++] = t;
-            }
-         }
-         t = 0;
-      }
-   }
-
-   if (y > *outlen) {
-      err =  CRYPT_BUFFER_OVERFLOW;
-   } else {
-      err =  CRYPT_OK;
-   }
-
-   *outlen = y;
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c b/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c
deleted file mode 100644
index 6fac1d80598c..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_object_identifier.c
-  ASN.1 DER, Encode Object Identifier, Tom St Denis
-*/
-
-#ifdef LTC_DER
-/**
-  Encode an OID
-  @param words   The words to encode  (upto 32-bits each)
-  @param nwords  The number of words in the OID
-  @param out     [out] Destination of OID data
-  @param outlen  [in/out] The max and resulting size of the OID
-  @return CRYPT_OK if successful
-*/
-int der_encode_object_identifier(unsigned long *words, unsigned long  nwords,
-                                 unsigned char *out,   unsigned long *outlen)
-{
-   unsigned long i, x, y, z, t, mask, wordbuf;
-   int           err;
-
-   LTC_ARGCHK(words  != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* check length */
-   if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) {
-      return err;
-   }
-   if (x > *outlen) {
-      *outlen = x;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* compute length to store OID data */
-   z = 0;
-   wordbuf = words[0] * 40 + words[1];
-   for (y = 1; y < nwords; y++) {
-       t = der_object_identifier_bits(wordbuf);
-       z += t/7 + ((t%7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0);
-       if (y < nwords - 1) {
-          wordbuf = words[y + 1];
-       }
-   }
-
-   /* store header + length */
-   x = 0;
-   out[x++] = 0x06;
-   if (z < 128) {
-      out[x++] = (unsigned char)z;
-   } else if (z < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)z;
-   } else if (z < 65536UL) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((z>>8)&255);
-      out[x++] = (unsigned char)(z&255);
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* store first byte */
-   wordbuf = words[0] * 40 + words[1];
-   for (i = 1; i < nwords; i++) {
-      /* store 7 bit words in little endian */
-      t    = wordbuf & 0xFFFFFFFF;
-      if (t) {
-         y    = x;
-         mask = 0;
-         while (t) {
-            out[x++] = (unsigned char)((t & 0x7F) | mask);
-            t    >>= 7;
-            mask  |= 0x80;  /* upper bit is set on all but the last byte */
-         }
-         /* now swap bytes y...x-1 */
-         z = x - 1;
-         while (y < z) {
-            t = out[y]; out[y] = out[z]; out[z] = (unsigned char)t;
-            ++y;
-            --z;
-         }
-      } else {
-         /* zero word */
-         out[x++] = 0x00;
-      }
-
-      if (i < nwords - 1) {
-         wordbuf = words[i + 1];
-      }
-   }
-
-   *outlen = x;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c b/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c
deleted file mode 100644
index d490f51285da..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_object_identifier.c
-  ASN.1 DER, get length of Object Identifier, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-unsigned long der_object_identifier_bits(unsigned long x)
-{
-   unsigned long c;
-   x &= 0xFFFFFFFF;
-   c  = 0;
-   while (x) {
-     ++c;
-     x >>= 1;
-   }
-   return c;
-}
-
-
-/**
-  Gets length of DER encoding of Object Identifier
-  @param nwords   The number of OID words
-  @param words    The actual OID words to get the size of
-  @param outlen   [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen)
-{
-   unsigned long y, z, t, wordbuf;
-
-   LTC_ARGCHK(words  != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-
-   /* must be >= 2 words */
-   if (nwords < 2) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* word1 = 0,1,2,3 and word2 0..39 */
-   if (words[0] > 3 || (words[0] < 2 && words[1] > 39)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* leading word is the first two */
-   z = 0;
-   wordbuf = words[0] * 40 + words[1];
-   for (y = 1; y < nwords; y++) {
-       t = der_object_identifier_bits(wordbuf);
-       z += t/7 + ((t%7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0);
-       if (y < nwords - 1) {
-          /* grab next word */
-          wordbuf = words[y+1];
-       }
-   }
-
-   /* now depending on the length our length encoding changes */
-   if (z < 128) {
-      z += 2;
-   } else if (z < 256) {
-      z += 3;
-   } else if (z < 65536UL) {
-      z += 4;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   *outlen = z;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c b/libs/tomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c
deleted file mode 100644
index eba13b0364cb..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_octet_string.c
-  ASN.1 DER, encode a OCTET STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a OCTET STRING
-  @param in      The DER encoded OCTET STRING
-  @param inlen   The size of the DER OCTET STRING
-  @param out     [out] The array of octets stored (one per char)
-  @param outlen  [in/out] The number of octets stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_octet_string(const unsigned char *in, unsigned long inlen,
-                                  unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* must have header at least */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* check for 0x04 */
-   if ((in[0] & 0x1F) != 0x04) {
-      return CRYPT_INVALID_PACKET;
-   }
-   x = 1;
-
-   /* decode the length */
-   if (in[x] & 0x80) {
-      /* valid # of bytes in length are 1,2,3 */
-      y = in[x] & 0x7F;
-      if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the length in */
-      len = 0;
-      ++x;
-      while (y--) {
-         len = (len << 8) | in[x++];
-      }
-   } else {
-      len = in[x++] & 0x7F;
-   }
-
-   /* is it too long? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (len + x > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* read the data */
-   for (y = 0; y < len; y++) {
-       out[y] = in[x++];
-   }
-
-   *outlen = y;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c b/libs/tomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c
deleted file mode 100644
index 60c0e0e58482..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_octet_string.c
-  ASN.1 DER, encode a OCTET STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store an OCTET STRING
-  @param in       The array of OCTETS to store (one per char)
-  @param inlen    The number of OCTETS to store
-  @param out      [out] The destination for the DER encoded OCTET STRING
-  @param outlen   [in/out] The max size and resulting size of the DER OCTET STRING
-  @return CRYPT_OK if successful
-*/
-int der_encode_octet_string(const unsigned char *in, unsigned long inlen,
-                                  unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-   int           err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* get the size */
-   if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* too big? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* encode the header+len */
-   x = 0;
-   out[x++] = 0x04;
-   if (inlen < 128) {
-      out[x++] = (unsigned char)inlen;
-   } else if (inlen < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)inlen;
-   } else if (inlen < 65536UL) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((inlen>>8)&255);
-      out[x++] = (unsigned char)(inlen&255);
-   } else if (inlen < 16777216UL) {
-      out[x++] = 0x83;
-      out[x++] = (unsigned char)((inlen>>16)&255);
-      out[x++] = (unsigned char)((inlen>>8)&255);
-      out[x++] = (unsigned char)(inlen&255);
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* store octets */
-   for (y = 0; y < inlen; y++) {
-       out[x++] = in[y];
-   }
-
-   /* retun length */
-   *outlen = x;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c b/libs/tomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c
deleted file mode 100644
index 06957441a3a7..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_octet_string.c
-  ASN.1 DER, get length of OCTET STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-/**
-  Gets length of DER encoding of OCTET STRING
-  @param noctets  The number of octets in the string to encode
-  @param outlen   [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_octet_string(unsigned long noctets, unsigned long *outlen)
-{
-   LTC_ARGCHK(outlen != NULL);
-
-   if (noctets < 128) {
-      /* 04 LL DD DD DD ... */
-      *outlen = 2 + noctets;
-   } else if (noctets < 256) {
-      /* 04 81 LL DD DD DD ... */
-      *outlen = 3 + noctets;
-   } else if (noctets < 65536UL) {
-      /* 04 82 LL LL DD DD DD ... */
-      *outlen = 4 + noctets;
-   } else if (noctets < 16777216UL) {
-      /* 04 83 LL LL LL DD DD DD ... */
-      *outlen = 5 + noctets;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c b/libs/tomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c
deleted file mode 100644
index 9e6c8ebebff0..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_printable_string.c
-  ASN.1 DER, encode a printable STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a printable STRING
-  @param in      The DER encoded printable STRING
-  @param inlen   The size of the DER printable STRING
-  @param out     [out] The array of octets stored (one per char)
-  @param outlen  [in/out] The number of octets stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_printable_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-   int           t;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* must have header at least */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* check for 0x13 */
-   if ((in[0] & 0x1F) != 0x13) {
-      return CRYPT_INVALID_PACKET;
-   }
-   x = 1;
-
-   /* decode the length */
-   if (in[x] & 0x80) {
-      /* valid # of bytes in length are 1,2,3 */
-      y = in[x] & 0x7F;
-      if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the length in */
-      len = 0;
-      ++x;
-      while (y--) {
-         len = (len << 8) | in[x++];
-      }
-   } else {
-      len = in[x++] & 0x7F;
-   }
-
-   /* is it too long? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (len + x > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* read the data */
-   for (y = 0; y < len; y++) {
-       t = der_printable_value_decode(in[x++]);
-       if (t == -1) {
-           return CRYPT_INVALID_ARG;
-       }
-       out[y] = t;
-   }
-
-   *outlen = y;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c b/libs/tomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c
deleted file mode 100644
index 9fd5ee786b00..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_printable_string.c
-  ASN.1 DER, encode a printable STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-  Store an printable STRING
-  @param in       The array of printable to store (one per char)
-  @param inlen    The number of printable to store
-  @param out      [out] The destination for the DER encoded printable STRING
-  @param outlen   [in/out] The max size and resulting size of the DER printable STRING
-  @return CRYPT_OK if successful
-*/
-int der_encode_printable_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-   int           err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* get the size */
-   if ((err = der_length_printable_string(in, inlen, &len)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* too big? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* encode the header+len */
-   x = 0;
-   out[x++] = 0x13;
-   if (inlen < 128) {
-      out[x++] = (unsigned char)inlen;
-   } else if (inlen < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)inlen;
-   } else if (inlen < 65536UL) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((inlen>>8)&255);
-      out[x++] = (unsigned char)(inlen&255);
-   } else if (inlen < 16777216UL) {
-      out[x++] = 0x83;
-      out[x++] = (unsigned char)((inlen>>16)&255);
-      out[x++] = (unsigned char)((inlen>>8)&255);
-      out[x++] = (unsigned char)(inlen&255);
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* store octets */
-   for (y = 0; y < inlen; y++) {
-       out[x++] = der_printable_char_encode(in[y]);
-   }
-
-   /* retun length */
-   *outlen = x;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c b/libs/tomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c
deleted file mode 100644
index 5d589b7928b4..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_printable_string.c
-  ASN.1 DER, get length of Printable STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-static const struct {
-   int code, value;
-} printable_table[] = {
-{ ' ', 32 },
-{ '\'', 39 },
-{ '(', 40 },
-{ ')', 41 },
-{ '+', 43 },
-{ ',', 44 },
-{ '-', 45 },
-{ '.', 46 },
-{ '/', 47 },
-{ '0', 48 },
-{ '1', 49 },
-{ '2', 50 },
-{ '3', 51 },
-{ '4', 52 },
-{ '5', 53 },
-{ '6', 54 },
-{ '7', 55 },
-{ '8', 56 },
-{ '9', 57 },
-{ ':', 58 },
-{ '=', 61 },
-{ '?', 63 },
-{ 'A', 65 },
-{ 'B', 66 },
-{ 'C', 67 },
-{ 'D', 68 },
-{ 'E', 69 },
-{ 'F', 70 },
-{ 'G', 71 },
-{ 'H', 72 },
-{ 'I', 73 },
-{ 'J', 74 },
-{ 'K', 75 },
-{ 'L', 76 },
-{ 'M', 77 },
-{ 'N', 78 },
-{ 'O', 79 },
-{ 'P', 80 },
-{ 'Q', 81 },
-{ 'R', 82 },
-{ 'S', 83 },
-{ 'T', 84 },
-{ 'U', 85 },
-{ 'V', 86 },
-{ 'W', 87 },
-{ 'X', 88 },
-{ 'Y', 89 },
-{ 'Z', 90 },
-{ 'a', 97 },
-{ 'b', 98 },
-{ 'c', 99 },
-{ 'd', 100 },
-{ 'e', 101 },
-{ 'f', 102 },
-{ 'g', 103 },
-{ 'h', 104 },
-{ 'i', 105 },
-{ 'j', 106 },
-{ 'k', 107 },
-{ 'l', 108 },
-{ 'm', 109 },
-{ 'n', 110 },
-{ 'o', 111 },
-{ 'p', 112 },
-{ 'q', 113 },
-{ 'r', 114 },
-{ 's', 115 },
-{ 't', 116 },
-{ 'u', 117 },
-{ 'v', 118 },
-{ 'w', 119 },
-{ 'x', 120 },
-{ 'y', 121 },
-{ 'z', 122 },
-};
-
-int der_printable_char_encode(int c)
-{
-   int x;
-   for (x = 0; x < (int)(sizeof(printable_table)/sizeof(printable_table[0])); x++) {
-       if (printable_table[x].code == c) {
-          return printable_table[x].value;
-       }
-   }
-   return -1;
-}
-
-int der_printable_value_decode(int v)
-{
-   int x;
-   for (x = 0; x < (int)(sizeof(printable_table)/sizeof(printable_table[0])); x++) {
-       if (printable_table[x].value == v) {
-          return printable_table[x].code;
-       }
-   }
-   return -1;
-}
-
-/**
-  Gets length of DER encoding of Printable STRING
-  @param octets   The values you want to encode
-  @param noctets  The number of octets in the string to encode
-  @param outlen   [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen)
-{
-   unsigned long x;
-
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(octets != NULL);
-
-   /* scan string for validity */
-   for (x = 0; x < noctets; x++) {
-       if (der_printable_char_encode(octets[x]) == -1) {
-          return CRYPT_INVALID_ARG;
-       }
-   }
-
-   if (noctets < 128) {
-      /* 16 LL DD DD DD ... */
-      *outlen = 2 + noctets;
-   } else if (noctets < 256) {
-      /* 16 81 LL DD DD DD ... */
-      *outlen = 3 + noctets;
-   } else if (noctets < 65536UL) {
-      /* 16 82 LL LL DD DD DD ... */
-      *outlen = 4 + noctets;
-   } else if (noctets < 16777216UL) {
-      /* 16 83 LL LL LL DD DD DD ... */
-      *outlen = 5 + noctets;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c
deleted file mode 100644
index e467ea91adbc..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-
-/**
-  @file der_decode_sequence_ex.c
-  ASN.1 DER, decode a SEQUENCE, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-   Decode a SEQUENCE
-   @param in       The DER encoded input
-   @param inlen    The size of the input
-   @param list     The list of items to decode
-   @param outlen   The number of items in the list
-   @param ordered  Search an unordeded or ordered list
-   @return CRYPT_OK on success
-*/
-int der_decode_sequence_ex(const unsigned char *in, unsigned long  inlen,
-                           ltc_asn1_list *list,     unsigned long  outlen, int ordered)
-{
-   int           err, i;
-   ltc_asn1_type type;
-   unsigned long size, x, y, z, blksize;
-   void          *data;
-
-   LTC_ARGCHK(in   != NULL);
-   LTC_ARGCHK(list != NULL);
-
-   /* get blk size */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */
-   x = 0;
-   if (in[x] != 0x30 && in[x] != 0x31) {
-      return CRYPT_INVALID_PACKET;
-   }
-   ++x;
-
-   /* check if the msb is set, which signals that the
-    * 7 lsb bits represent the number of bytes of the length
-    */
-   if (in[x] < 128) {
-      blksize = in[x++];
-   } else {
-      if (in[x] < 0x81 || in[x] > 0x83) {
-         return CRYPT_INVALID_PACKET;
-      }
-      y = in[x++] & 0x7F;
-
-      /* would reading the len bytes overrun? */
-      if (x + y > inlen) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read len */
-      blksize = 0;
-      while (y--) {
-          blksize = (blksize << 8) | (unsigned long)in[x++];
-      }
-   }
-
-   /* would this blksize overflow? */
-   if (x + blksize > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* mark all as unused */
-   for (i = 0; i < (int)outlen; i++) {
-       list[i].used = 0;
-   }
-
-   /* ok read data */
-   inlen = blksize;
-   for (i = 0; i < (int)outlen; i++) {
-       z    = 0;
-       type = list[i].type;
-       size = list[i].size;
-       data = list[i].data;
-       if (!ordered && list[i].used == 1) { continue; }
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-           case LTC_ASN1_BOOLEAN:
-               z = inlen;
-               if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) {
-                   if (!ordered) { continue; }
-                   goto LBL_ERR;
-               }
-               if ((err = der_length_boolean(&z)) != CRYPT_OK) {
-                   goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_INTEGER:
-               z = inlen;
-               if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) {
-                  if (!ordered) {  continue; }
-                  goto LBL_ERR;
-               }
-               if ((err = der_length_integer(data, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SHORT_INTEGER:
-               z = inlen;
-               if ((err = der_decode_short_integer(in + x, z, data)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               if ((err = der_length_short_integer(((unsigned long*)data)[0], &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-
-               break;
-
-           case LTC_ASN1_BIT_STRING:
-               z = inlen;
-               if ((err = der_decode_bit_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_RAW_BIT_STRING:
-               z = inlen;
-               if ((err = der_decode_raw_bit_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_OCTET_STRING:
-               z = inlen;
-               if ((err = der_decode_octet_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_NULL:
-               if (inlen < 2 || in[x] != 0x05 || in[x+1] != 0x00) {
-                  if (!ordered) { continue; }
-                  err = CRYPT_INVALID_PACKET;
-                  goto LBL_ERR;
-               }
-               z = 2;
-               break;
-
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-               z = inlen;
-               if ((err = der_decode_object_identifier(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_object_identifier(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_TELETEX_STRING:
-               z = inlen;
-               if ((err = der_decode_teletex_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_teletex_string(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_IA5_STRING:
-               z = inlen;
-               if ((err = der_decode_ia5_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_ia5_string(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-
-           case LTC_ASN1_PRINTABLE_STRING:
-               z = inlen;
-               if ((err = der_decode_printable_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_printable_string(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_UTF8_STRING:
-               z = inlen;
-               if ((err = der_decode_utf8_string(in + x, z, data, &size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               list[i].size = size;
-               if ((err = der_length_utf8_string(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_UTCTIME:
-               z = inlen;
-               if ((err = der_decode_utctime(in + x, &z, data)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_GENERALIZEDTIME:
-               z = inlen;
-               if ((err = der_decode_generalizedtime(in + x, &z, data)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SET:
-               z = inlen;
-               if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_SEQUENCE:
-               /* detect if we have the right type */
-               if ((type == LTC_ASN1_SETOF && (in[x] & 0x3F) != 0x31) || (type == LTC_ASN1_SEQUENCE && (in[x] & 0x3F) != 0x30)) {
-                  err = CRYPT_INVALID_PACKET;
-                  goto LBL_ERR;
-               }
-
-               z = inlen;
-               if ((err = der_decode_sequence(in + x, z, data, size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-
-           case LTC_ASN1_CHOICE:
-               z = inlen;
-               if ((err = der_decode_choice(in + x, &z, data, size)) != CRYPT_OK) {
-                  if (!ordered) { continue; }
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-           case LTC_ASN1_EOL:
-               err = CRYPT_INVALID_ARG;
-               goto LBL_ERR;
-       }
-       x           += z;
-       inlen       -= z;
-       list[i].used = 1;
-       if (!ordered) {
-          /* restart the decoder */
-          i = -1;
-       }
-   }
-
-   for (i = 0; i < (int)outlen; i++) {
-      if (list[i].used == 0) {
-          err = CRYPT_INVALID_PACKET;
-          goto LBL_ERR;
-      }
-   }
-
-   if (inlen == 0) {
-      err = CRYPT_OK;
-   } else {
-      err = CRYPT_INPUT_TOO_LONG;
-   }
-
-LBL_ERR:
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c
deleted file mode 100644
index 425fe8de9b23..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c
+++ /dev/null
@@ -1,479 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_sequence_flexi.c
-  ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-static unsigned long _fetch_length(const unsigned char *in, unsigned long inlen, unsigned long *data_offset)
-{
-   unsigned long x, z;
-
-   *data_offset = 0;
-
-   /* skip type and read len */
-   if (inlen < 2) {
-      return 0xFFFFFFFF;
-   }
-   ++in; ++(*data_offset);
-
-   /* read len */
-   x = *in++; ++(*data_offset);
-
-   /* <128 means literal */
-   if (x < 128) {
-      return x+*data_offset;
-   }
-   x     &= 0x7F; /* the lower 7 bits are the length of the length */
-   inlen -= 2;
-
-   /* len means len of len! */
-   if (x == 0 || x > 4 || x > inlen) {
-      return 0xFFFFFFFF;
-   }
-
-   *data_offset += x;
-   z = 0;
-   while (x--) {
-      z = (z<<8) | ((unsigned long)*in);
-      ++in;
-   }
-   return z+*data_offset;
-}
-
-static int _new_element(ltc_asn1_list **l)
-{
-   /* alloc new link */
-   if (*l == NULL) {
-      *l = XCALLOC(1, sizeof(ltc_asn1_list));
-      if (*l == NULL) {
-         return CRYPT_MEM;
-      }
-   } else {
-      (*l)->next = XCALLOC(1, sizeof(ltc_asn1_list));
-      if ((*l)->next == NULL) {
-         return CRYPT_MEM;
-      }
-      (*l)->next->prev = *l;
-      *l = (*l)->next;
-   }
-   return CRYPT_OK;
-}
-
-/**
-   ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements.
-   @param in      The input buffer
-   @param inlen   [in/out] The length of the input buffer and on output the amount of decoded data
-   @param out     [out] A pointer to the linked list
-   @return CRYPT_OK on success.
-*/
-int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out)
-{
-   ltc_asn1_list *l, *t;
-   unsigned long err, type, len, totlen, data_offset, len_len;
-   void          *realloc_tmp;
-
-   LTC_ARGCHK(in    != NULL);
-   LTC_ARGCHK(inlen != NULL);
-   LTC_ARGCHK(out   != NULL);
-
-   l = NULL;
-   totlen = 0;
-
-   if (*inlen == 0) {
-      /* alloc new link */
-      if ((err = _new_element(&l)) != CRYPT_OK) {
-         goto error;
-      }
-   }
-
-   /* scan the input and and get lengths and what not */
-   while (*inlen) {
-      /* read the type byte */
-      type = *in;
-
-      /* fetch length */
-      len = _fetch_length(in, *inlen, &data_offset);
-      if (len > *inlen) {
-         err = CRYPT_INVALID_PACKET;
-         goto error;
-      }
-
-      /* alloc new link */
-      if ((err = _new_element(&l)) != CRYPT_OK) {
-         goto error;
-      }
-
-      if ((type & 0x20) && (type != 0x30) && (type != 0x31)) {
-         /* constructed, use the 'used' field to store the original identifier */
-         l->used = type;
-         /* treat constructed elements like SETs */
-         type = 0x20;
-      }
-      else if ((type & 0xC0) == 0x80) {
-         /* context-specific, use the 'used' field to store the original identifier */
-         l->used = type;
-         /* context-specific elements are treated as opaque data */
-         type = 0x80;
-      }
-
-     /* now switch on type */
-      switch (type) {
-         case 0x01: /* BOOLEAN */
-            l->type = LTC_ASN1_BOOLEAN;
-            l->size = 1;
-            l->data = XCALLOC(1, sizeof(int));
-
-            if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_boolean(&len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x02: /* INTEGER */
-             /* init field */
-             l->type = LTC_ASN1_INTEGER;
-             l->size = 1;
-             if ((err = mp_init(&l->data)) != CRYPT_OK) {
-                 goto error;
-             }
-
-             /* decode field */
-             if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) {
-                 goto error;
-             }
-
-             /* calc length of object */
-             if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) {
-                 goto error;
-             }
-             break;
-
-         case 0x03: /* BIT */
-            /* init field */
-            l->type = LTC_ASN1_BIT_STRING;
-            l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char.  */
-
-            if ((l->data = XCALLOC(1, l->size)) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x04: /* OCTET */
-
-            /* init field */
-            l->type = LTC_ASN1_OCTET_STRING;
-            l->size = len;
-
-            if ((l->data = XCALLOC(1, l->size)) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x05: /* NULL */
-
-            /* valid NULL is 0x05 0x00 */
-            if (in[0] != 0x05 || in[1] != 0x00) {
-               err = CRYPT_INVALID_PACKET;
-               goto error;
-            }
-
-            /* simple to store ;-) */
-            l->type = LTC_ASN1_NULL;
-            l->data = NULL;
-            l->size = 0;
-            len     = 2;
-
-            break;
-
-         case 0x06: /* OID */
-
-            /* init field */
-            l->type = LTC_ASN1_OBJECT_IDENTIFIER;
-            l->size = len;
-
-            if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-
-            /* resize it to save a bunch of mem */
-            if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) {
-               /* out of heap but this is not an error */
-               break;
-            }
-            l->data = realloc_tmp;
-            break;
-
-         case 0x0C: /* UTF8 */
-
-            /* init field */
-            l->type = LTC_ASN1_UTF8_STRING;
-            l->size = len;
-
-            if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x13: /* PRINTABLE */
-
-            /* init field */
-            l->type = LTC_ASN1_PRINTABLE_STRING;
-            l->size = len;
-
-            if ((l->data = XCALLOC(1, l->size)) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x14: /* TELETEXT */
-
-            /* init field */
-            l->type = LTC_ASN1_TELETEX_STRING;
-            l->size = len;
-
-            if ((l->data = XCALLOC(1, l->size)) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_teletex_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_teletex_string(l->data, l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x16: /* IA5 */
-
-            /* init field */
-            l->type = LTC_ASN1_IA5_STRING;
-            l->size = len;
-
-            if ((l->data = XCALLOC(1, l->size)) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x17: /* UTC TIME */
-
-            /* init field */
-            l->type = LTC_ASN1_UTCTIME;
-            l->size = 1;
-
-            if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            len = *inlen;
-            if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) {
-               goto error;
-            }
-            break;
-
-         case 0x18:
-            l->type = LTC_ASN1_GENERALIZEDTIME;
-            l->size = len;
-
-            if ((l->data = XCALLOC(1, sizeof(ltc_generalizedtime))) == NULL) {
-               err = CRYPT_MEM;
-               goto error;
-            }
-
-            if ((err = der_decode_generalizedtime(in, &len, l->data)) != CRYPT_OK) {
-               goto error;
-            }
-
-            if ((err = der_length_generalizedtime(l->data, &len)) != CRYPT_OK) {
-               goto error;
-            }
-
-            break;
-
-         case 0x20: /* Any CONSTRUCTED element that is neither SEQUENCE nor SET */
-         case 0x30: /* SEQUENCE */
-         case 0x31: /* SET */
-
-             /* init field */
-             if (type == 0x20) {
-                l->type = LTC_ASN1_CONSTRUCTED;
-             }
-             else if (type == 0x30) {
-                l->type = LTC_ASN1_SEQUENCE;
-             }
-             else {
-                l->type = LTC_ASN1_SET;
-             }
-
-             if ((l->data = XMALLOC(len)) == NULL) {
-                err = CRYPT_MEM;
-                goto error;
-             }
-
-             XMEMCPY(l->data, in, len);
-             l->size = len;
-
-
-             /* jump to the start of the data */
-             in     += data_offset;
-             *inlen -= data_offset;
-             len = len - data_offset;
-
-             /* Sequence elements go as child */
-             if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) {
-                goto error;
-             }
-
-             /* len update */
-             totlen += data_offset;
-
-             /* the flexi decoder can also do nothing, so make sure a child has been allocated */
-             if (l->child) {
-                /* link them up y0 */
-                l->child->parent = l;
-             }
-
-             t = l;
-             len_len = 0;
-             while((t != NULL) && (t->child != NULL)) {
-                len_len++;
-                t = t->child;
-             }
-             if (len_len > LTC_DER_MAX_RECURSION) {
-                err = CRYPT_ERROR;
-                goto error;
-             }
-
-             break;
-
-         case 0x80: /* Context-specific */
-             l->type = LTC_ASN1_CONTEXT_SPECIFIC;
-
-             if ((l->data = XCALLOC(1, len - data_offset)) == NULL) {
-                err = CRYPT_MEM;
-                goto error;
-             }
-
-             XMEMCPY(l->data, in + data_offset, len - data_offset);
-             l->size = len - data_offset;
-
-             break;
-
-         default:
-           /* invalid byte ... this is a soft error */
-           /* remove link */
-           if (l->prev) {
-              l       = l->prev;
-              XFREE(l->next);
-              l->next = NULL;
-           }
-           goto outside;
-      }
-
-      /* advance pointers */
-      totlen  += len;
-      in      += len;
-      *inlen  -= len;
-   }
-
-outside:
-
-   /* in case we processed anything */
-   if (totlen) {
-      /* rewind l please */
-      while (l->prev != NULL || l->parent != NULL) {
-         if (l->parent != NULL) {
-            l = l->parent;
-         } else {
-            l = l->prev;
-         }
-      }
-   }
-
-   /* return */
-   *out   = l;
-   *inlen = totlen;
-   return CRYPT_OK;
-
-error:
-   /* free list */
-   der_sequence_free(l);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c
deleted file mode 100644
index 87b239cb57b3..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-#include <stdarg.h>
-
-
-/**
-  @file der_decode_sequence_multi.c
-  ASN.1 DER, decode a SEQUENCE, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-  Decode a SEQUENCE type using a VA list
-  @param in    Input buffer
-  @param inlen Length of input in octets
-  @remark <...> is of the form <type, size, data> (int, unsigned long, void*)
-  @return CRYPT_OK on success
-*/
-int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
-{
-   int           err;
-   ltc_asn1_type type;
-   unsigned long size, x;
-   void          *data;
-   va_list       args;
-   ltc_asn1_list *list;
-
-   LTC_ARGCHK(in    != NULL);
-
-   /* get size of output that will be required */
-   va_start(args, inlen);
-   x = 0;
-   for (;;) {
-       type = (ltc_asn1_type)va_arg(args, int);
-       size = va_arg(args, unsigned long);
-       data = va_arg(args, void*);
-       LTC_UNUSED_PARAM(size);
-       LTC_UNUSED_PARAM(data);
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-           case LTC_ASN1_BOOLEAN:
-           case LTC_ASN1_INTEGER:
-           case LTC_ASN1_SHORT_INTEGER:
-           case LTC_ASN1_BIT_STRING:
-           case LTC_ASN1_OCTET_STRING:
-           case LTC_ASN1_NULL:
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-           case LTC_ASN1_IA5_STRING:
-           case LTC_ASN1_PRINTABLE_STRING:
-           case LTC_ASN1_UTF8_STRING:
-           case LTC_ASN1_UTCTIME:
-           case LTC_ASN1_SET:
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_SEQUENCE:
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_RAW_BIT_STRING:
-           case LTC_ASN1_TELETEX_STRING:
-           case LTC_ASN1_GENERALIZEDTIME:
-                ++x;
-                break;
-
-           case LTC_ASN1_EOL:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-               va_end(args);
-               return CRYPT_INVALID_ARG;
-       }
-   }
-   va_end(args);
-
-   /* allocate structure for x elements */
-   if (x == 0) {
-      return CRYPT_NOP;
-   }
-
-   list = XCALLOC(sizeof(*list), x);
-   if (list == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* fill in the structure */
-   va_start(args, inlen);
-   x = 0;
-   for (;;) {
-       type = (ltc_asn1_type)va_arg(args, int);
-       size = va_arg(args, unsigned long);
-       data = va_arg(args, void*);
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-           case LTC_ASN1_BOOLEAN:
-           case LTC_ASN1_INTEGER:
-           case LTC_ASN1_SHORT_INTEGER:
-           case LTC_ASN1_BIT_STRING:
-           case LTC_ASN1_OCTET_STRING:
-           case LTC_ASN1_NULL:
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-           case LTC_ASN1_IA5_STRING:
-           case LTC_ASN1_PRINTABLE_STRING:
-           case LTC_ASN1_UTF8_STRING:
-           case LTC_ASN1_UTCTIME:
-           case LTC_ASN1_SEQUENCE:
-           case LTC_ASN1_SET:
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_RAW_BIT_STRING:
-           case LTC_ASN1_TELETEX_STRING:
-           case LTC_ASN1_GENERALIZEDTIME:
-                LTC_SET_ASN1(list, x++, type, data, size);
-                break;
-           /* coverity[dead_error_line] */
-           case LTC_ASN1_EOL:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-                break;
-       }
-   }
-   va_end(args);
-
-   err = der_decode_sequence(in, inlen, list, x);
-   XFREE(list);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_subject_public_key_info.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_subject_public_key_info.c
deleted file mode 100644
index b6a7990894c8..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_decode_subject_public_key_info.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-/**
-  @file der_decode_subject_public_key_info.c
-  ASN.1 DER, encode a Subject Public Key structure --nmav
-*/
-
-#ifdef LTC_DER
-
-/* AlgorithmIdentifier := SEQUENCE {
- *    algorithm OBJECT IDENTIFIER,
- *    parameters ANY DEFINED BY algorithm
- * }
- *
- * SubjectPublicKeyInfo := SEQUENCE {
- *    algorithm AlgorithmIdentifier,
- *    subjectPublicKey BIT STRING
- * }
- */
-/**
-  Decode a subject public key info
-   @param in      The input buffer
-   @param inlen   The length of the input buffer
-   @param algorithm             One out of the enum #public_key_algorithms
-   @param public_key            The buffer for the public key
-   @param public_key_len        [in/out] The length of the public key buffer and the written length
-   @param parameters_type       The parameters' type out of the enum ltc_asn1_type
-   @param parameters            The parameters to include
-   @param parameters_len        The number of parameters to include
-   @return CRYPT_OK on success
-*/
-int der_decode_subject_public_key_info(const unsigned char *in, unsigned long inlen,
-        unsigned int algorithm, void* public_key, unsigned long* public_key_len,
-        unsigned long parameters_type, ltc_asn1_list* parameters, unsigned long parameters_len)
-{
-   int err;
-   unsigned long len;
-   oid_st oid;
-   unsigned char *tmpbuf;
-   unsigned long  tmpoid[16];
-   ltc_asn1_list alg_id[2];
-   ltc_asn1_list subject_pubkey[2];
-
-   LTC_ARGCHK(in    != NULL);
-   LTC_ARGCHK(inlen != 0);
-   LTC_ARGCHK(public_key_len != NULL);
-
-   err = pk_get_oid(algorithm, &oid);
-   if (err != CRYPT_OK) {
-        return err;
-   }
-
-   /* see if the OpenSSL DER format RSA public key will work */
-   tmpbuf = XCALLOC(1, inlen);
-   if (tmpbuf == NULL) {
-       err = CRYPT_MEM;
-       goto LBL_ERR;
-   }
-
-   /* this includes the internal hash ID and optional params (NULL in this case) */
-   LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid)/sizeof(tmpoid[0]));
-   LTC_SET_ASN1(alg_id, 1, (ltc_asn1_type)parameters_type, parameters, parameters_len);
-
-   /* the actual format of the SSL DER key is odd, it stores a RSAPublicKey
-    * in a **BIT** string ... so we have to extract it then proceed to convert bit to octet
-    */
-   LTC_SET_ASN1(subject_pubkey, 0, LTC_ASN1_SEQUENCE, alg_id, 2);
-   LTC_SET_ASN1(subject_pubkey, 1, LTC_ASN1_RAW_BIT_STRING, tmpbuf, inlen*8U);
-
-   err=der_decode_sequence(in, inlen, subject_pubkey, 2UL);
-   if (err != CRYPT_OK) {
-           goto LBL_ERR;
-   }
-
-   if ((alg_id[0].size != oid.OIDlen) ||
-        XMEMCMP(oid.OID, alg_id[0].data, oid.OIDlen * sizeof(oid.OID[0])) != 0) {
-        /* OID mismatch */
-        err = CRYPT_PK_INVALID_TYPE;
-        goto LBL_ERR;
-   }
-
-   len = subject_pubkey[1].size/8;
-   if (*public_key_len > len) {
-       XMEMCPY(public_key, subject_pubkey[1].data, len);
-       *public_key_len = len;
-    } else {
-        *public_key_len = len;
-        err = CRYPT_BUFFER_OVERFLOW;
-        goto LBL_ERR;
-    }
-
-    err = CRYPT_OK;
-
-LBL_ERR:
-
-    XFREE(tmpbuf);
-
-    return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_ex.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_ex.c
deleted file mode 100644
index 6e5ba0d52ca9..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_ex.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-
-/**
-  @file der_encode_sequence_ex.c
-  ASN.1 DER, encode a SEQUENCE, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-   Encode a SEQUENCE
-   @param list      The list of items to encode
-   @param inlen     The number of items in the list
-   @param out       [out] The destination
-   @param outlen    [in/out] The size of the output
-   @param type_of   LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF
-   @return CRYPT_OK on success
-*/
-int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
-                           unsigned char *out,  unsigned long *outlen, int type_of)
-{
-   int           err;
-   ltc_asn1_type type;
-   unsigned long size, x, y, z, i;
-   void          *data;
-
-   LTC_ARGCHK(list    != NULL);
-   LTC_ARGCHK(out     != NULL);
-   LTC_ARGCHK(outlen  != NULL);
-
-   /* get size of output that will be required */
-   y = 0; z = 0;
-   if ((err = der_length_sequence_ex(list, inlen, &y, &z)) != CRYPT_OK) return CRYPT_INVALID_ARG;
-
-   /* too big ? */
-   if (*outlen < y) {
-      *outlen = y;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-
-   /* store header */
-   x = 0;
-   out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31;
-
-   if (z < 128) {
-      out[x++] = (unsigned char)z;
-   } else if (z < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)z;
-   } else if (z < 65536UL) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((z>>8UL)&255);
-      out[x++] = (unsigned char)(z&255);
-   } else if (z < 16777216UL) {
-      out[x++] = 0x83;
-      out[x++] = (unsigned char)((z>>16UL)&255);
-      out[x++] = (unsigned char)((z>>8UL)&255);
-      out[x++] = (unsigned char)(z&255);
-   }
-
-   /* store data */
-   *outlen -= x;
-   for (i = 0; i < inlen; i++) {
-       type = list[i].type;
-       size = list[i].size;
-       data = list[i].data;
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-            case LTC_ASN1_BOOLEAN:
-               z = *outlen;
-               if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_INTEGER:
-               z = *outlen;
-               if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SHORT_INTEGER:
-               z = *outlen;
-               if ((err = der_encode_short_integer(*((unsigned long*)data), out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_BIT_STRING:
-               z = *outlen;
-               if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_RAW_BIT_STRING:
-               z = *outlen;
-               if ((err = der_encode_raw_bit_string(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_OCTET_STRING:
-               z = *outlen;
-               if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_NULL:
-               out[x] = 0x05;
-               out[x+1] = 0x00;
-               z = 2;
-               break;
-
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-               z = *outlen;
-               if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_IA5_STRING:
-               z = *outlen;
-               if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_PRINTABLE_STRING:
-               z = *outlen;
-               if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_UTF8_STRING:
-               z = *outlen;
-               if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_UTCTIME:
-               z = *outlen;
-               if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_GENERALIZEDTIME:
-               z = *outlen;
-               if ((err = der_encode_generalizedtime(data, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SET:
-               z = *outlen;
-               if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SETOF:
-               z = *outlen;
-               if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_SEQUENCE:
-               z = *outlen;
-               if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               break;
-
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-           case LTC_ASN1_EOL:
-           case LTC_ASN1_TELETEX_STRING:
-               err = CRYPT_INVALID_ARG;
-               goto LBL_ERR;
-       }
-
-       x       += z;
-       *outlen -= z;
-   }
-   *outlen = x;
-   err = CRYPT_OK;
-
-LBL_ERR:
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c
deleted file mode 100644
index 492fc024f683..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-#include <stdarg.h>
-
-
-/**
-  @file der_encode_sequence_multi.c
-  ASN.1 DER, encode a SEQUENCE, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-  Encode a SEQUENCE type using a VA list
-  @param out    [out] Destination for data
-  @param outlen [in/out] Length of buffer and resulting length of output
-  @remark <...> is of the form <type, size, data> (int, unsigned long, void*)
-  @return CRYPT_OK on success
-*/
-int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
-{
-   int           err;
-   ltc_asn1_type type;
-   unsigned long size, x;
-   void          *data;
-   va_list       args;
-   ltc_asn1_list *list;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* get size of output that will be required */
-   va_start(args, outlen);
-   x = 0;
-   for (;;) {
-       type = (ltc_asn1_type)va_arg(args, int);
-       size = va_arg(args, unsigned long);
-       data = va_arg(args, void*);
-       LTC_UNUSED_PARAM(size);
-       LTC_UNUSED_PARAM(data);
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-           case LTC_ASN1_BOOLEAN:
-           case LTC_ASN1_INTEGER:
-           case LTC_ASN1_SHORT_INTEGER:
-           case LTC_ASN1_BIT_STRING:
-           case LTC_ASN1_OCTET_STRING:
-           case LTC_ASN1_NULL:
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-           case LTC_ASN1_IA5_STRING:
-           case LTC_ASN1_PRINTABLE_STRING:
-           case LTC_ASN1_UTF8_STRING:
-           case LTC_ASN1_UTCTIME:
-           case LTC_ASN1_SEQUENCE:
-           case LTC_ASN1_SET:
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_RAW_BIT_STRING:
-           case LTC_ASN1_GENERALIZEDTIME:
-                ++x;
-                break;
-
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-           case LTC_ASN1_EOL:
-           case LTC_ASN1_TELETEX_STRING:
-               va_end(args);
-               return CRYPT_INVALID_ARG;
-       }
-   }
-   va_end(args);
-
-   /* allocate structure for x elements */
-   if (x == 0) {
-      return CRYPT_NOP;
-   }
-
-   list = XCALLOC(sizeof(*list), x);
-   if (list == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* fill in the structure */
-   va_start(args, outlen);
-   x = 0;
-   for (;;) {
-       type = (ltc_asn1_type)va_arg(args, int);
-       size = va_arg(args, unsigned long);
-       data = va_arg(args, void*);
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-           case LTC_ASN1_BOOLEAN:
-           case LTC_ASN1_INTEGER:
-           case LTC_ASN1_SHORT_INTEGER:
-           case LTC_ASN1_BIT_STRING:
-           case LTC_ASN1_OCTET_STRING:
-           case LTC_ASN1_NULL:
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-           case LTC_ASN1_IA5_STRING:
-           case LTC_ASN1_PRINTABLE_STRING:
-           case LTC_ASN1_UTF8_STRING:
-           case LTC_ASN1_UTCTIME:
-           case LTC_ASN1_SEQUENCE:
-           case LTC_ASN1_SET:
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_RAW_BIT_STRING:
-           case LTC_ASN1_GENERALIZEDTIME:
-                LTC_SET_ASN1(list, x++, type, data, size);
-                break;
-
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-           case LTC_ASN1_EOL:
-           case LTC_ASN1_TELETEX_STRING:
-               va_end(args);
-               err = CRYPT_INVALID_ARG;
-               goto LBL_ERR;
-       }
-   }
-   va_end(args);
-
-   err = der_encode_sequence(list, x, out, outlen);
-LBL_ERR:
-   XFREE(list);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_subject_public_key_info.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_subject_public_key_info.c
deleted file mode 100644
index 4cd8759a9234..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_encode_subject_public_key_info.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_subject_public_key_info.c
-  ASN.1 DER, encode a Subject Public Key structure --nmav
-*/
-
-#ifdef LTC_DER
-
-/* AlgorithmIdentifier := SEQUENCE {
- *    algorithm OBJECT IDENTIFIER,
- *    parameters ANY DEFINED BY algorithm
- * }
- *
- * SubjectPublicKeyInfo := SEQUENCE {
- *    algorithm AlgorithmIdentifier,
- *    subjectPublicKey BIT STRING
- * }
- */
-/**
-  Encode a subject public key info
-   @param out           The output buffer
-   @param outlen        [in/out] Length of buffer and resulting length of output
-   @param algorithm             One out of the enum #public_key_algorithms
-   @param public_key            The buffer for the public key
-   @param public_key_len        The length of the public key buffer
-   @param parameters_type       The parameters' type out of the enum ltc_asn1_type
-   @param parameters            The parameters to include
-   @param parameters_len        The number of parameters to include
-   @return CRYPT_OK on success
-*/
-int der_encode_subject_public_key_info(unsigned char *out, unsigned long *outlen,
-        unsigned int algorithm, void* public_key, unsigned long public_key_len,
-        unsigned long parameters_type, void* parameters, unsigned long parameters_len)
-{
-   int           err;
-   ltc_asn1_list alg_id[2];
-   oid_st oid;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   err = pk_get_oid(algorithm, &oid);
-   if (err != CRYPT_OK) {
-        return err;
-   }
-
-   LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid.OID,    oid.OIDlen);
-   LTC_SET_ASN1(alg_id, 1, (ltc_asn1_type)parameters_type,            parameters, parameters_len);
-
-   return der_encode_sequence_multi(out, outlen,
-        LTC_ASN1_SEQUENCE, (unsigned long)sizeof(alg_id)/sizeof(alg_id[0]), alg_id,
-        LTC_ASN1_RAW_BIT_STRING, public_key_len*8U, public_key,
-        LTC_ASN1_EOL,     0UL, NULL);
-
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c
deleted file mode 100644
index 64a253993522..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_sequence.c
-  ASN.1 DER, length a SEQUENCE, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-   Get the length of a DER sequence
-   @param list   The sequences of items in the SEQUENCE
-   @param inlen  The number of items
-   @param outlen [out] The length required in octets to store it
-   @return CRYPT_OK on success
-*/
-int der_length_sequence(ltc_asn1_list *list, unsigned long inlen,
-                        unsigned long *outlen)
-{
-   return der_length_sequence_ex(list, inlen, outlen, NULL);
-}
-
-int der_length_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
-                           unsigned long *outlen, unsigned long *payloadlen)
-{
-   int           err;
-   ltc_asn1_type type;
-   unsigned long size, x, y, i, z;
-   void          *data;
-
-   LTC_ARGCHK(list    != NULL);
-   LTC_ARGCHK(outlen  != NULL);
-
-   /* get size of output that will be required */
-   y = 0;
-   for (i = 0; i < inlen; i++) {
-       type = list[i].type;
-       size = list[i].size;
-       data = list[i].data;
-
-       if (type == LTC_ASN1_EOL) {
-          break;
-       }
-
-       switch (type) {
-           case LTC_ASN1_BOOLEAN:
-              if ((err = der_length_boolean(&x)) != CRYPT_OK) {
-                 goto LBL_ERR;
-              }
-              y += x;
-              break;
-
-           case LTC_ASN1_INTEGER:
-               if ((err = der_length_integer(data, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_SHORT_INTEGER:
-               if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_BIT_STRING:
-           case LTC_ASN1_RAW_BIT_STRING:
-               if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_OCTET_STRING:
-               if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_NULL:
-               y += 2;
-               break;
-
-           case LTC_ASN1_OBJECT_IDENTIFIER:
-               if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_IA5_STRING:
-               if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_TELETEX_STRING:
-               if ((err = der_length_teletex_string(data, size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_PRINTABLE_STRING:
-               if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_UTCTIME:
-               if ((err = der_length_utctime(data, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_GENERALIZEDTIME:
-               if ((err = der_length_generalizedtime(data, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_UTF8_STRING:
-               if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-           case LTC_ASN1_SET:
-           case LTC_ASN1_SETOF:
-           case LTC_ASN1_SEQUENCE:
-               if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) {
-                  goto LBL_ERR;
-               }
-               y += x;
-               break;
-
-
-           case LTC_ASN1_CHOICE:
-           case LTC_ASN1_CONSTRUCTED:
-           case LTC_ASN1_CONTEXT_SPECIFIC:
-           case LTC_ASN1_EOL:
-               err = CRYPT_INVALID_ARG;
-               goto LBL_ERR;
-       }
-   }
-
-   /* calc header size */
-   z = y;
-   if (y < 128) {
-      y += 2;
-   } else if (y < 256) {
-      /* 0x30 0x81 LL */
-      y += 3;
-   } else if (y < 65536UL) {
-      /* 0x30 0x82 LL LL */
-      y += 4;
-   } else if (y < 16777216UL) {
-      /* 0x30 0x83 LL LL LL */
-      y += 5;
-   } else {
-      err = CRYPT_INVALID_ARG;
-      goto LBL_ERR;
-   }
-
-   /* store size */
-   if (payloadlen) *payloadlen = z;
-   *outlen = y;
-   err     = CRYPT_OK;
-
-LBL_ERR:
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c
deleted file mode 100644
index 55b75b1984db..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_sequence_free.c
-  ASN.1 DER, free's a structure allocated by der_decode_sequence_flexi(), Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-  Free memory allocated by der_decode_sequence_flexi()
-  @param in     The list to free
-*/
-void der_sequence_free(ltc_asn1_list *in)
-{
-   ltc_asn1_list *l;
-
-   if (!in) return;
-
-   /* walk to the start of the chain */
-   while (in->prev != NULL || in->parent != NULL) {
-      if (in->parent != NULL) {
-          in = in->parent;
-      } else {
-          in = in->prev;
-      }
-   }
-
-   /* now walk the list and free stuff */
-   while (in != NULL) {
-      /* is there a child? */
-      if (in->child) {
-         /* disconnect */
-         in->child->parent = NULL;
-         der_sequence_free(in->child);
-      }
-
-      switch (in->type) {
-         case LTC_ASN1_SETOF: break;
-         case LTC_ASN1_INTEGER : if (in->data != NULL) { mp_clear(in->data); } break;
-         default               : if (in->data != NULL) { XFREE(in->data);    }
-      }
-
-      /* move to next and free current */
-      l = in->next;
-      XFREE(in);
-      in = l;
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/sequence/der_sequence_shrink.c b/libs/tomcrypt/src/pk/asn1/der/sequence/der_sequence_shrink.c
deleted file mode 100644
index d4a89158c9b7..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/sequence/der_sequence_shrink.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_sequence_shrink.c
-  Free memory allocated for CONSTRUCTED, SET or SEQUENCE elements by der_decode_sequence_flexi(), Steffen Jaeckel
-*/
-
-#ifdef LTC_DER
-
-/**
-  Free memory allocated for CONSTRUCTED,
-  SET or SEQUENCE elements by der_decode_sequence_flexi()
-  @param in     The list to shrink
-*/
-void der_sequence_shrink(ltc_asn1_list *in)
-{
-   if (!in) return;
-
-   /* now walk the list and free stuff */
-   while (in != NULL) {
-      /* is there a child? */
-      if (in->child) {
-         der_sequence_shrink(in->child);
-      }
-
-      switch (in->type) {
-         case LTC_ASN1_CONSTRUCTED:
-         case LTC_ASN1_SET:
-         case LTC_ASN1_SEQUENCE : if (in->data != NULL) { XFREE(in->data); in->data = NULL; } break;
-         default: break;
-      }
-
-      /* move to next and free current */
-      in = in->next;
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/set/der_encode_set.c b/libs/tomcrypt/src/pk/asn1/der/set/der_encode_set.c
deleted file mode 100644
index 3cfb783b9804..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/set/der_encode_set.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_set.c
-  ASN.1 DER, Encode a SET, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/* LTC define to ASN.1 TAG */
-static int _ltc_to_asn1(ltc_asn1_type v)
-{
-   switch (v) {
-      case LTC_ASN1_BOOLEAN:                 return 0x01;
-      case LTC_ASN1_INTEGER:
-      case LTC_ASN1_SHORT_INTEGER:           return 0x02;
-      case LTC_ASN1_RAW_BIT_STRING:
-      case LTC_ASN1_BIT_STRING:              return 0x03;
-      case LTC_ASN1_OCTET_STRING:            return 0x04;
-      case LTC_ASN1_NULL:                    return 0x05;
-      case LTC_ASN1_OBJECT_IDENTIFIER:       return 0x06;
-      case LTC_ASN1_UTF8_STRING:             return 0x0C;
-      case LTC_ASN1_PRINTABLE_STRING:        return 0x13;
-      case LTC_ASN1_TELETEX_STRING:          return 0x14;
-      case LTC_ASN1_IA5_STRING:              return 0x16;
-      case LTC_ASN1_UTCTIME:                 return 0x17;
-      case LTC_ASN1_GENERALIZEDTIME:         return 0x18;
-      case LTC_ASN1_SEQUENCE:                return 0x30;
-      case LTC_ASN1_SET:
-      case LTC_ASN1_SETOF:                   return 0x31;
-      case LTC_ASN1_CHOICE:
-      case LTC_ASN1_CONSTRUCTED:
-      case LTC_ASN1_CONTEXT_SPECIFIC:
-      case LTC_ASN1_EOL:                     return -1;
-   }
-   return -1;
-}
-
-
-static int _qsort_helper(const void *a, const void *b)
-{
-   ltc_asn1_list *A = (ltc_asn1_list *)a, *B = (ltc_asn1_list *)b;
-   int            r;
-
-   r = _ltc_to_asn1(A->type) - _ltc_to_asn1(B->type);
-
-   /* for QSORT the order is UNDEFINED if they are "equal" which means it is NOT DETERMINISTIC.  So we force it to be :-) */
-   if (r == 0) {
-      /* their order in the original list now determines the position */
-      return A->used - B->used;
-   } else {
-      return r;
-   }
-}
-
-/*
-   Encode a SET type
-   @param list      The list of items to encode
-   @param inlen     The number of items in the list
-   @param out       [out] The destination
-   @param outlen    [in/out] The size of the output
-   @return CRYPT_OK on success
-*/
-int der_encode_set(ltc_asn1_list *list, unsigned long inlen,
-                   unsigned char *out,  unsigned long *outlen)
-{
-   ltc_asn1_list  *copy;
-   unsigned long   x;
-   int             err;
-
-   /* make copy of list */
-   copy = XCALLOC(inlen, sizeof(*copy));
-   if (copy == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* fill in used member with index so we can fully sort it */
-   for (x = 0; x < inlen; x++) {
-       copy[x]      = list[x];
-       copy[x].used = x;
-   }
-
-   /* sort it by the "type" field */
-   XQSORT(copy, inlen, sizeof(*copy), &_qsort_helper);
-
-   /* call der_encode_sequence_ex() */
-   err = der_encode_sequence_ex(copy, inlen, out, outlen, LTC_ASN1_SET);
-
-   /* free list */
-   XFREE(copy);
-
-   return err;
-}
-
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/set/der_encode_setof.c b/libs/tomcrypt/src/pk/asn1/der/set/der_encode_setof.c
deleted file mode 100644
index 07b8627e2ca9..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/set/der_encode_setof.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_setof.c
-  ASN.1 DER, Encode SET OF, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-struct edge {
-   unsigned char *start;
-   unsigned long  size;
-};
-
-static int _qsort_helper(const void *a, const void *b)
-{
-   struct edge   *A = (struct edge *)a, *B = (struct edge *)b;
-   int            r;
-   unsigned long  x;
-
-   /* compare min length */
-   r = XMEMCMP(A->start, B->start, MIN(A->size, B->size));
-
-   if (r == 0 && A->size != B->size) {
-      if (A->size > B->size) {
-         for (x = B->size; x < A->size; x++) {
-            if (A->start[x]) {
-               return 1;
-            }
-         }
-      } else {
-         for (x = A->size; x < B->size; x++) {
-            if (B->start[x]) {
-               return -1;
-            }
-         }
-      }
-   }
-
-   return r;
-}
-
-/**
-   Encode a SETOF stucture
-   @param list      The list of items to encode
-   @param inlen     The number of items in the list
-   @param out       [out] The destination
-   @param outlen    [in/out] The size of the output
-   @return CRYPT_OK on success
-*/
-int der_encode_setof(ltc_asn1_list *list, unsigned long inlen,
-                     unsigned char *out,  unsigned long *outlen)
-{
-   unsigned long  x, y, z;
-   ptrdiff_t hdrlen;
-   int            err;
-   struct edge   *edges;
-   unsigned char *ptr, *buf;
-
-   /* check that they're all the same type */
-   for (x = 1; x < inlen; x++) {
-      if (list[x].type != list[x-1].type) {
-         return CRYPT_INVALID_ARG;
-      }
-   }
-
-   /* alloc buffer to store copy of output */
-   buf = XCALLOC(1, *outlen);
-   if (buf == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* encode list */
-   if ((err = der_encode_sequence_ex(list, inlen, buf, outlen, LTC_ASN1_SETOF)) != CRYPT_OK) {
-       XFREE(buf);
-       return err;
-   }
-
-   /* allocate edges */
-   edges = XCALLOC(inlen, sizeof(*edges));
-   if (edges == NULL) {
-      XFREE(buf);
-      return CRYPT_MEM;
-   }
-
-   /* skip header */
-   ptr = buf + 1;
-
-   /* now skip length data */
-   x = *ptr++;
-   if (x >= 0x80) {
-      ptr += (x & 0x7F);
-   }
-
-   /* get the size of the static header */
-   hdrlen = ptr - buf;
-
-
-   /* scan for edges */
-   x = 0;
-   while (ptr < (buf + *outlen)) {
-      /* store start */
-      edges[x].start = ptr;
-
-      /* skip type */
-      z = 1;
-
-      /* parse length */
-      y = ptr[z++];
-      if (y < 128) {
-         edges[x].size = y;
-      } else {
-         y &= 0x7F;
-         edges[x].size = 0;
-         while (y--) {
-            edges[x].size = (edges[x].size << 8) | ((unsigned long)ptr[z++]);
-         }
-      }
-
-      /* skip content */
-      edges[x].size += z;
-      ptr           += edges[x].size;
-      ++x;
-   }
-
-   /* sort based on contents (using edges) */
-   XQSORT(edges, inlen, sizeof(*edges), &_qsort_helper);
-
-   /* copy static header */
-   XMEMCPY(out, buf, hdrlen);
-
-   /* copy+sort using edges+indecies to output from buffer */
-   for (y = (unsigned long)hdrlen, x = 0; x < inlen; x++) {
-      XMEMCPY(out+y, edges[x].start, edges[x].size);
-      y += edges[x].size;
-   }
-
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, *outlen);
-#endif
-
-   /* free buffers */
-   XFREE(edges);
-   XFREE(buf);
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c b/libs/tomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c
deleted file mode 100644
index ac57017b34d9..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_short_integer.c
-  ASN.1 DER, decode an integer, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Read a short integer
-  @param in       The DER encoded data
-  @param inlen    Size of data
-  @param num      [out] The integer to decode
-  @return CRYPT_OK if successful
-*/
-int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num)
-{
-   unsigned long len, x, y;
-
-   LTC_ARGCHK(num    != NULL);
-   LTC_ARGCHK(in     != NULL);
-
-   /* check length */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* check header */
-   x = 0;
-   if ((in[x++] & 0x1F) != 0x02) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* get the packet len */
-   len = in[x++];
-
-   if (x + len > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* read number */
-   y = 0;
-   while (len--) {
-      y = (y<<8) | (unsigned long)in[x++];
-   }
-   *num = y;
-
-   return CRYPT_OK;
-
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c b/libs/tomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c
deleted file mode 100644
index c36a83343a28..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_short_integer.c
-  ASN.1 DER, encode an integer, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a short integer in the range (0,2^32-1)
-  @param num      The integer to encode
-  @param out      [out] The destination for the DER encoded integers
-  @param outlen   [in/out] The max size and resulting size of the DER encoded integers
-  @return CRYPT_OK if successful
-*/
-int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen)
-{
-   unsigned long len, x, y, z;
-   int           err;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* force to 32 bits */
-   num &= 0xFFFFFFFFUL;
-
-   /* find out how big this will be */
-   if ((err = der_length_short_integer(num, &len)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (*outlen < len) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* get len of output */
-   z = 0;
-   y = num;
-   while (y) {
-     ++z;
-     y >>= 8;
-   }
-
-   /* handle zero */
-   if (z == 0) {
-      z = 1;
-   }
-
-   /* see if msb is set */
-   z += (num&(1UL<<((z<<3) - 1))) ? 1 : 0;
-
-   /* adjust the number so the msB is non-zero */
-   for (x = 0; (z <= 4) && (x < (4 - z)); x++) {
-      num <<= 8;
-   }
-
-   /* store header */
-   x = 0;
-   out[x++] = 0x02;
-   out[x++] = (unsigned char)z;
-
-   /* if 31st bit is set output a leading zero and decrement count */
-   if (z == 5) {
-      out[x++] = 0;
-      --z;
-   }
-
-   /* store values */
-   for (y = 0; y < z; y++) {
-      out[x++] = (unsigned char)((num >> 24) & 0xFF);
-      num    <<= 8;
-   }
-
-   /* we good */
-   *outlen = x;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c b/libs/tomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c
deleted file mode 100644
index a8ca93be7237..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_short_integer.c
-  ASN.1 DER, get length of encoding, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-/**
-  Gets length of DER encoding of num
-  @param num    The integer to get the size of
-  @param outlen [out] The length of the DER encoding for the given integer
-  @return CRYPT_OK if successful
-*/
-int der_length_short_integer(unsigned long num, unsigned long *outlen)
-{
-   unsigned long z, y, len;
-
-   LTC_ARGCHK(outlen  != NULL);
-
-   /* force to 32 bits */
-   num &= 0xFFFFFFFFUL;
-
-   /* get the number of bytes */
-   z = 0;
-   y = num;
-   while (y) {
-     ++z;
-     y >>= 8;
-   }
-
-   /* handle zero */
-   if (z == 0) {
-      z = 1;
-   }
-
-   /* we need a 0x02 to indicate it's INTEGER */
-   len = 1;
-
-   /* length byte */
-   ++len;
-
-   /* bytes in value */
-   len += z;
-
-   /* see if msb is set */
-   len += (num&(1UL<<((z<<3) - 1))) ? 1 : 0;
-
-   /* return length */
-   *outlen = len;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/teletex_string/der_decode_teletex_string.c b/libs/tomcrypt/src/pk/asn1/der/teletex_string/der_decode_teletex_string.c
deleted file mode 100644
index 820ea3ad8e42..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/teletex_string/der_decode_teletex_string.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_teletex_string.c
-  ASN.1 DER, encode a teletex STRING
-*/
-
-#ifdef LTC_DER
-
-/**
-  Store a teletex STRING
-  @param in      The DER encoded teletex STRING
-  @param inlen   The size of the DER teletex STRING
-  @param out     [out] The array of octets stored (one per char)
-  @param outlen  [in/out] The number of octets stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_teletex_string(const unsigned char *in, unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-   int           t;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* must have header at least */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* check for 0x14 */
-   if ((in[0] & 0x1F) != 0x14) {
-      return CRYPT_INVALID_PACKET;
-   }
-   x = 1;
-
-   /* decode the length */
-   if (in[x] & 0x80) {
-      /* valid # of bytes in length are 1,2,3 */
-      y = in[x] & 0x7F;
-      if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the length in */
-      len = 0;
-      ++x;
-      while (y--) {
-         len = (len << 8) | in[x++];
-      }
-   } else {
-      len = in[x++] & 0x7F;
-   }
-
-   /* is it too long? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (len + x > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* read the data */
-   for (y = 0; y < len; y++) {
-       t = der_teletex_value_decode(in[x++]);
-       if (t == -1) {
-           return CRYPT_INVALID_ARG;
-       }
-       out[y] = t;
-   }
-
-   *outlen = y;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/teletex_string/der_length_teletex_string.c b/libs/tomcrypt/src/pk/asn1/der/teletex_string/der_length_teletex_string.c
deleted file mode 100644
index dd4092000eb4..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/teletex_string/der_length_teletex_string.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_teletex_string.c
-  ASN.1 DER, get length of teletex STRING
-*/
-
-#ifdef LTC_DER
-
-static const struct {
-   int code, value;
-} teletex_table[] = {
-{ '\0',  0 },
-{ '\a',  7 },
-{ '\b',  8 },
-{ '\t',  9 },
-{ '\n', 10 },
-{ '\v', 11 },
-{ '\f', 12 },
-{ '\r', 13 },
-{ ' ',  32 },
-{ '!',  33 },
-{ '"',  34 },
-{ '%',  37 },
-{ '&',  38 },
-{ '\'', 39 },
-{ '(',  40 },
-{ ')',  41 },
-{ '+',  43 },
-{ ',',  44 },
-{ '-',  45 },
-{ '.',  46 },
-{ '/',  47 },
-{ '0',  48 },
-{ '1',  49 },
-{ '2',  50 },
-{ '3',  51 },
-{ '4',  52 },
-{ '5',  53 },
-{ '6',  54 },
-{ '7',  55 },
-{ '8',  56 },
-{ '9',  57 },
-{ ':',  58 },
-{ ';',  59 },
-{ '<',  60 },
-{ '=',  61 },
-{ '>',  62 },
-{ '?',  63 },
-{ '@',  64 },
-{ 'A',  65 },
-{ 'B',  66 },
-{ 'C',  67 },
-{ 'D',  68 },
-{ 'E',  69 },
-{ 'F',  70 },
-{ 'G',  71 },
-{ 'H',  72 },
-{ 'I',  73 },
-{ 'J',  74 },
-{ 'K',  75 },
-{ 'L',  76 },
-{ 'M',  77 },
-{ 'N',  78 },
-{ 'O',  79 },
-{ 'P',  80 },
-{ 'Q',  81 },
-{ 'R',  82 },
-{ 'S',  83 },
-{ 'T',  84 },
-{ 'U',  85 },
-{ 'V',  86 },
-{ 'W',  87 },
-{ 'X',  88 },
-{ 'Y',  89 },
-{ 'Z',  90 },
-{ '[',  91 },
-{ ']',  93 },
-{ '_',  95 },
-{ 'a',  97 },
-{ 'b',  98 },
-{ 'c',  99 },
-{ 'd',  100 },
-{ 'e',  101 },
-{ 'f',  102 },
-{ 'g',  103 },
-{ 'h',  104 },
-{ 'i',  105 },
-{ 'j',  106 },
-{ 'k',  107 },
-{ 'l',  108 },
-{ 'm',  109 },
-{ 'n',  110 },
-{ 'o',  111 },
-{ 'p',  112 },
-{ 'q',  113 },
-{ 'r',  114 },
-{ 's',  115 },
-{ 't',  116 },
-{ 'u',  117 },
-{ 'v',  118 },
-{ 'w',  119 },
-{ 'x',  120 },
-{ 'y',  121 },
-{ 'z',  122 },
-{ '|',  124 },
-{ ' ',  160 },
-{ 0xa1, 161 },
-{ 0xa2, 162 },
-{ 0xa3, 163 },
-{ '$',  164 },
-{ 0xa5, 165 },
-{ '#',  166 },
-{ 0xa7, 167 },
-{ 0xa4, 168 },
-{ 0xab, 171 },
-{ 0xb0, 176 },
-{ 0xb1, 177 },
-{ 0xb2, 178 },
-{ 0xb3, 179 },
-{ 0xd7, 180 },
-{ 0xb5, 181 },
-{ 0xb6, 182 },
-{ 0xb7, 183 },
-{ 0xf7, 184 },
-{ 0xbb, 187 },
-{ 0xbc, 188 },
-{ 0xbd, 189 },
-{ 0xbe, 190 },
-{ 0xbf, 191 },
-};
-
-int der_teletex_char_encode(int c)
-{
-   int x;
-   for (x = 0; x < (int)(sizeof(teletex_table)/sizeof(teletex_table[0])); x++) {
-       if (teletex_table[x].code == c) {
-          return teletex_table[x].value;
-       }
-   }
-   return -1;
-}
-
-int der_teletex_value_decode(int v)
-{
-   int x;
-   for (x = 0; x < (int)(sizeof(teletex_table)/sizeof(teletex_table[0])); x++) {
-       if (teletex_table[x].value == v) {
-          return teletex_table[x].code;
-       }
-   }
-   return -1;
-}
-
-/**
-  Gets length of DER encoding of teletex STRING
-  @param octets   The values you want to encode
-  @param noctets  The number of octets in the string to encode
-  @param outlen   [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_teletex_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen)
-{
-   unsigned long x;
-
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(octets != NULL);
-
-   /* scan string for validity */
-   for (x = 0; x < noctets; x++) {
-       if (der_teletex_char_encode(octets[x]) == -1) {
-          return CRYPT_INVALID_ARG;
-       }
-   }
-
-   if (noctets < 128) {
-      /* 16 LL DD DD DD ... */
-      *outlen = 2 + noctets;
-   } else if (noctets < 256) {
-      /* 16 81 LL DD DD DD ... */
-      *outlen = 3 + noctets;
-   } else if (noctets < 65536UL) {
-      /* 16 82 LL LL DD DD DD ... */
-      *outlen = 4 + noctets;
-   } else if (noctets < 16777216UL) {
-      /* 16 83 LL LL LL DD DD DD ... */
-      *outlen = 5 + noctets;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c b/libs/tomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c
deleted file mode 100644
index aea0a2d28fff..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_utctime.c
-  ASN.1 DER, decode a  UTCTIME, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-static int _char_to_int(unsigned char x)
-{
-   switch (x)  {
-      case '0': return 0;
-      case '1': return 1;
-      case '2': return 2;
-      case '3': return 3;
-      case '4': return 4;
-      case '5': return 5;
-      case '6': return 6;
-      case '7': return 7;
-      case '8': return 8;
-      case '9': return 9;
-      default:  return 100;
-   }
-}
-
-#define DECODE_V(y, max) \
-   y  = _char_to_int(buf[x])*10 + _char_to_int(buf[x+1]); \
-   if (y >= max) return CRYPT_INVALID_PACKET;           \
-   x += 2;
-
-/**
-  Decodes a UTC time structure in DER format (reads all 6 valid encoding formats)
-  @param in     Input buffer
-  @param inlen  Length of input buffer in octets
-  @param out    [out] Destination of UTC time structure
-  @return CRYPT_OK   if successful
-*/
-int der_decode_utctime(const unsigned char *in, unsigned long *inlen,
-                             ltc_utctime   *out)
-{
-   unsigned char buf[32] = { 0 }; /* initialize as all zeroes */
-   unsigned long x;
-   int           y;
-
-   LTC_ARGCHK(in    != NULL);
-   LTC_ARGCHK(inlen != NULL);
-   LTC_ARGCHK(out   != NULL);
-
-   /* check header */
-   if (*inlen < 2UL || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* decode the string */
-   for (x = 0; x < in[1]; x++) {
-       y = der_ia5_value_decode(in[x+2]);
-       if (y == -1) {
-          return CRYPT_INVALID_PACKET;
-       }
-       buf[x] = y;
-   }
-   *inlen = 2 + x;
-
-
-   /* possible encodings are
-YYMMDDhhmmZ
-YYMMDDhhmm+hh'mm'
-YYMMDDhhmm-hh'mm'
-YYMMDDhhmmssZ
-YYMMDDhhmmss+hh'mm'
-YYMMDDhhmmss-hh'mm'
-
-    So let's do a trivial decode upto [including] mm
-   */
-
-    x = 0;
-    DECODE_V(out->YY, 100);
-    DECODE_V(out->MM, 13);
-    DECODE_V(out->DD, 32);
-    DECODE_V(out->hh, 24);
-    DECODE_V(out->mm, 60);
-
-    /* clear timezone and seconds info */
-    out->off_dir = out->off_hh = out->off_mm = out->ss = 0;
-
-    /* now is it Z, +, - or 0-9 */
-    if (buf[x] == 'Z') {
-       return CRYPT_OK;
-    } else if (buf[x] == '+' || buf[x] == '-') {
-       out->off_dir = (buf[x++] == '+') ? 0 : 1;
-       DECODE_V(out->off_hh, 24);
-       DECODE_V(out->off_mm, 60);
-       return CRYPT_OK;
-    }
-
-    /* decode seconds */
-    DECODE_V(out->ss, 60);
-
-    /* now is it Z, +, - */
-    if (buf[x] == 'Z') {
-       return CRYPT_OK;
-    } else if (buf[x] == '+' || buf[x] == '-') {
-       out->off_dir = (buf[x++] == '+') ? 0 : 1;
-       DECODE_V(out->off_hh, 24);
-       DECODE_V(out->off_mm, 60);
-       return CRYPT_OK;
-    } else {
-       return CRYPT_INVALID_PACKET;
-    }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c b/libs/tomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c
deleted file mode 100644
index 8dcc12a85446..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_utctime.c
-  ASN.1 DER, encode a  UTCTIME, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-static const char * const baseten = "0123456789";
-
-#define STORE_V(y) \
-    out[x++] = der_ia5_char_encode(baseten[(y/10) % 10]); \
-    out[x++] = der_ia5_char_encode(baseten[y % 10]);
-
-/**
-  Encodes a UTC time structure in DER format
-  @param utctime      The UTC time structure to encode
-  @param out          The destination of the DER encoding of the UTC time structure
-  @param outlen       [in/out] The length of the DER encoding
-  @return CRYPT_OK if successful
-*/
-int der_encode_utctime(ltc_utctime *utctime,
-                       unsigned char *out,   unsigned long *outlen)
-{
-    unsigned long x, tmplen;
-    int           err;
-
-    LTC_ARGCHK(utctime != NULL);
-    LTC_ARGCHK(out     != NULL);
-    LTC_ARGCHK(outlen  != NULL);
-
-    if ((err = der_length_utctime(utctime, &tmplen)) != CRYPT_OK) {
-       return err;
-    }
-    if (tmplen > *outlen) {
-        *outlen = tmplen;
-        return CRYPT_BUFFER_OVERFLOW;
-    }
-
-    /* store header */
-    out[0] = 0x17;
-
-    /* store values */
-    x = 2;
-    STORE_V(utctime->YY);
-    STORE_V(utctime->MM);
-    STORE_V(utctime->DD);
-    STORE_V(utctime->hh);
-    STORE_V(utctime->mm);
-    STORE_V(utctime->ss);
-
-    if (utctime->off_mm || utctime->off_hh) {
-       out[x++] = der_ia5_char_encode(utctime->off_dir ? '-' : '+');
-       STORE_V(utctime->off_hh);
-       STORE_V(utctime->off_mm);
-    } else {
-       out[x++] = der_ia5_char_encode('Z');
-    }
-
-    /* store length */
-    out[1] = (unsigned char)(x - 2);
-
-    /* all good let's return */
-    *outlen = x;
-    return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c b/libs/tomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c
deleted file mode 100644
index a182e494f1aa..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_utctime.c
-  ASN.1 DER, get length of UTCTIME, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/**
-  Gets length of DER encoding of UTCTIME
-  @param utctime      The UTC time structure to get the size of
-  @param outlen [out] The length of the DER encoding
-  @return CRYPT_OK if successful
-*/
-int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen)
-{
-   LTC_ARGCHK(outlen  != NULL);
-   LTC_ARGCHK(utctime != NULL);
-
-   if (utctime->off_hh == 0 && utctime->off_mm == 0) {
-      /* we encode as YYMMDDhhmmssZ */
-      *outlen = 2 + 13;
-   } else {
-      /* we encode as YYMMDDhhmmss{+|-}hh'mm' */
-      *outlen = 2 + 17;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c b/libs/tomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c
deleted file mode 100644
index 3fe0396de94b..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_decode_utf8_string.c
-  ASN.1 DER, encode a UTF8 STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store a UTF8 STRING
-  @param in      The DER encoded UTF8 STRING
-  @param inlen   The size of the DER UTF8 STRING
-  @param out     [out] The array of utf8s stored (one per char)
-  @param outlen  [in/out] The number of utf8s stored
-  @return CRYPT_OK if successful
-*/
-int der_decode_utf8_string(const unsigned char *in,  unsigned long inlen,
-                                       wchar_t *out, unsigned long *outlen)
-{
-   wchar_t       tmp;
-   unsigned long x, y, z, len;
-   int err;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* must have header at least */
-   if (inlen < 2) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* check for 0x0C */
-   if ((in[0] & 0x1F) != 0x0C) {
-      return CRYPT_INVALID_PACKET;
-   }
-   x = 1;
-
-   /* decode the length */
-   if (in[x] & 0x80) {
-      /* valid # of bytes in length are 1,2,3 */
-      y = in[x] & 0x7F;
-      if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read the length in */
-      len = 0;
-      ++x;
-      while (y--) {
-         len = (len << 8) | in[x++];
-      }
-   } else {
-      len = in[x++] & 0x7F;
-   }
-
-   if (len + x > inlen) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* proceed to decode */
-   for (y = 0; x < inlen; ) {
-      /* get first byte */
-      tmp = in[x++];
-
-      /* count number of bytes */
-      for (z = 0; (tmp & 0x80) && (z <= 4); z++, tmp = (tmp << 1) & 0xFF);
-
-      if (z > 4 || (x + (z - 1) > inlen)) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* decode, grab upper bits */
-      tmp >>= z;
-
-      /* grab remaining bytes */
-      if (z > 1) { --z; }
-      while (z-- != 0) {
-         if ((in[x] & 0xC0) != 0x80) {
-            return CRYPT_INVALID_PACKET;
-         }
-         tmp = (tmp << 6) | ((wchar_t)in[x++] & 0x3F);
-      }
-
-      if (y < *outlen) {
-         out[y] = tmp;
-      }
-      y++;
-   }
-   if (y > *outlen) {
-      err = CRYPT_BUFFER_OVERFLOW;
-   } else {
-      err = CRYPT_OK;
-   }
-   *outlen = y;
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c b/libs/tomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c
deleted file mode 100644
index 9e2ca7d5a20a..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_encode_utf8_string.c
-  ASN.1 DER, encode a UTF8 STRING, Tom St Denis
-*/
-
-
-#ifdef LTC_DER
-
-/**
-  Store an UTF8 STRING
-  @param in       The array of UTF8 to store (one per wchar_t)
-  @param inlen    The number of UTF8 to store
-  @param out      [out] The destination for the DER encoded UTF8 STRING
-  @param outlen   [in/out] The max size and resulting size of the DER UTF8 STRING
-  @return CRYPT_OK if successful
-*/
-int der_encode_utf8_string(const wchar_t *in,  unsigned long inlen,
-                           unsigned char *out, unsigned long *outlen)
-{
-   unsigned long x, y, len;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* get the size */
-   for (x = len = 0; x < inlen; x++) {
-       if (!der_utf8_valid_char(in[x])) return CRYPT_INVALID_ARG;
-       len += der_utf8_charsize(in[x]);
-   }
-
-   if (len < 128) {
-      y = 2 + len;
-   } else if (len < 256) {
-      y = 3 + len;
-   } else if (len < 65536UL) {
-      y = 4 + len;
-   } else if (len < 16777216UL) {
-      y = 5 + len;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* too big? */
-   if (y > *outlen) {
-      *outlen = y;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* encode the header+len */
-   x = 0;
-   out[x++] = 0x0C;
-   if (len < 128) {
-      out[x++] = (unsigned char)len;
-   } else if (len < 256) {
-      out[x++] = 0x81;
-      out[x++] = (unsigned char)len;
-   } else if (len < 65536UL) {
-      out[x++] = 0x82;
-      out[x++] = (unsigned char)((len>>8)&255);
-      out[x++] = (unsigned char)(len&255);
-   } else if (len < 16777216UL) {
-      out[x++] = 0x83;
-      out[x++] = (unsigned char)((len>>16)&255);
-      out[x++] = (unsigned char)((len>>8)&255);
-      out[x++] = (unsigned char)(len&255);
-   } else {
-       /* coverity[dead_error_line] */
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* store UTF8 */
-   for (y = 0; y < inlen; y++) {
-       switch (der_utf8_charsize(in[y])) {
-          case 1: out[x++] = (unsigned char)in[y]; break;
-          case 2: out[x++] = 0xC0 | ((in[y] >> 6) & 0x1F);  out[x++] = 0x80 | (in[y] & 0x3F); break;
-          case 3: out[x++] = 0xE0 | ((in[y] >> 12) & 0x0F); out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); out[x++] = 0x80 | (in[y] & 0x3F); break;
-#if !defined(LTC_WCHAR_MAX) || LTC_WCHAR_MAX > 0xFFFF
-          case 4: out[x++] = 0xF0 | ((in[y] >> 18) & 0x07); out[x++] = 0x80 | ((in[y] >> 12) & 0x3F); out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); out[x++] = 0x80 | (in[y] & 0x3F); break;
-#endif
-       }
-   }
-
-   /* retun length */
-   *outlen = x;
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c b/libs/tomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c
deleted file mode 100644
index b7bb082397d2..000000000000
--- a/libs/tomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file der_length_utf8_string.c
-  ASN.1 DER, get length of UTF8 STRING, Tom St Denis
-*/
-
-#ifdef LTC_DER
-
-/** Return the size in bytes of a UTF-8 character
-  @param c   The UTF-8 character to measure
-  @return    The size in bytes
-*/
-unsigned long der_utf8_charsize(const wchar_t c)
-{
-   if (c <= 0x7F) {
-      return 1;
-   } else if (c <= 0x7FF) {
-      return 2;
-#if LTC_WCHAR_MAX == 0xFFFF
-   } else {
-      return 3;
-   }
-#else
-   } else if (c <= 0xFFFF) {
-      return 3;
-   } else {
-      return 4;
-   }
-#endif
-}
-
-/**
-  Test whether the given code point is valid character
-  @param c   The UTF-8 character to test
-  @return    1 - valid, 0 - invalid
-*/
-int der_utf8_valid_char(const wchar_t c)
-{
-   LTC_UNUSED_PARAM(c);
-#if !defined(LTC_WCHAR_MAX) || LTC_WCHAR_MAX > 0xFFFF
-   if (c > 0x10FFFF) return 0;
-#endif
-#if LTC_WCHAR_MAX != 0xFFFF && LTC_WCHAR_MAX != 0xFFFFFFFF
-   if (c < 0) return 0;
-#endif
-   return 1;
-}
-
-/**
-  Gets length of DER encoding of UTF8 STRING
-  @param in       The characters to measure the length of
-  @param noctets  The number of octets in the string to encode
-  @param outlen   [out] The length of the DER encoding for the given string
-  @return CRYPT_OK if successful
-*/
-int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen)
-{
-   unsigned long x, len;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   len = 0;
-   for (x = 0; x < noctets; x++) {
-      if (!der_utf8_valid_char(in[x])) return CRYPT_INVALID_ARG;
-      len += der_utf8_charsize(in[x]);
-   }
-
-   if (len < 128) {
-      /* 0C LL DD DD DD ... */
-      *outlen = 2 + len;
-   } else if (len < 256) {
-      /* 0C 81 LL DD DD DD ... */
-      *outlen = 3 + len;
-   } else if (len < 65536UL) {
-      /* 0C 82 LL LL DD DD DD ... */
-      *outlen = 4 + len;
-   } else if (len < 16777216UL) {
-      /* 0C 83 LL LL LL DD DD DD ... */
-      *outlen = 5 + len;
-   } else {
-      return CRYPT_INVALID_ARG;
-   }
-
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dh/dh.c b/libs/tomcrypt/src/pk/dh/dh.c
deleted file mode 100644
index b30e01e04d7e..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh.c
+++ /dev/null
@@ -1,233 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/* This holds the key settings.  ***MUST*** be organized by size from smallest to largest. */
-const ltc_dh_set_type ltc_dh_sets[] = {
-#ifdef LTC_DH768
-{  /* 768-bit MODP Group 1 - https://tools.ietf.org/html/rfc7296#appendix-B.1 */
-   96,
-   "DH-768",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH1024
-{  /* 1024-bit MODP Group 2 - https://tools.ietf.org/html/rfc7296#appendix-B.2 */
-   128,
-   "DH-1024",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
-   "FFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH1536
-{  /* 1536-bit MODP Group 5 - https://tools.ietf.org/html/rfc3526#section-2 */
-   192,
-   "DH-1536",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-   "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-   "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-   "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH2048
-{  /* 2048-bit MODP Group 14 - https://tools.ietf.org/html/rfc3526#section-3 */
-   256,
-   "DH-2048",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-   "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-   "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-   "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
-   "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
-   "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
-   "15728E5A8AACAA68FFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH3072
-{  /* 3072-bit MODP Group 15 - https://tools.ietf.org/html/rfc3526#section-4 */
-   384,
-   "DH-3072",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-   "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-   "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-   "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
-   "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
-   "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
-   "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
-   "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
-   "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
-   "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
-   "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
-   "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH4096
-{  /* 4096-bit MODP Group 16 - https://tools.ietf.org/html/rfc3526#section-5 */
-   512,
-   "DH-4096",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-   "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-   "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-   "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
-   "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
-   "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
-   "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
-   "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
-   "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
-   "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
-   "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
-   "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
-   "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
-   "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
-   "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
-   "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
-   "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
-   "FFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH6144
-{  /* 6144-bit MODP Group 17 - https://tools.ietf.org/html/rfc3526#section-6 */
-   768,
-   "DH-6144",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-   "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-   "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-   "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
-   "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
-   "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
-   "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
-   "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
-   "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
-   "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
-   "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
-   "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
-   "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
-   "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
-   "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
-   "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
-   "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
-   "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD"
-   "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
-   "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B"
-   "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
-   "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6"
-   "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
-   "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
-   "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
-   "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C"
-   "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
-   "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"
-},
-#endif
-#ifdef LTC_DH8192
-{  /* 8192-bit MODP Group 18 - https://tools.ietf.org/html/rfc3526#section-7 */
-   1024,
-   "DH-8192",
-   "2",
-   "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-   "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-   "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-   "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-   "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-   "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-   "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-   "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
-   "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
-   "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
-   "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
-   "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
-   "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
-   "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
-   "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
-   "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
-   "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
-   "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
-   "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
-   "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
-   "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
-   "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD"
-   "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
-   "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B"
-   "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
-   "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6"
-   "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
-   "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
-   "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
-   "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C"
-   "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
-   "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4"
-   "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"
-   "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568"
-   "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
-   "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B"
-   "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"
-   "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36"
-   "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"
-   "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92"
-   "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"
-   "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
-   "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"
-},
-#endif
-{
-   0,
-   NULL,
-   NULL,
-   NULL
-}
-};
-
-/**
-  Returns the DH group size (octets) for given key
-  @param key   The DH key to get the size of
-  @return The group size in octets (0 on error)
- */
-int dh_get_groupsize(dh_key *key)
-{
-   if (key == NULL) return 0;
-   return mp_unsigned_bin_size(key->prime);
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_check_pubkey.c b/libs/tomcrypt/src/pk/dh/dh_check_pubkey.c
deleted file mode 100644
index 9a8c7f6c98af..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_check_pubkey.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Check DH public key (INTERNAL ONLY, not part of public API)
-  @param key    The key you wish to test
-  @return CRYPT_OK if successful
-*/
-int dh_check_pubkey(dh_key *key)
-{
-   void *p_minus1;
-   ltc_mp_digit digit;
-   int i, digit_count, bits_set = 0, err;
-
-   LTC_ARGCHK(key != NULL);
-
-   if ((err = mp_init(&p_minus1)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* avoid: y <= 1 OR y >= p-1 */
-   if ((err = mp_sub_d(key->prime, 1, p_minus1)) != CRYPT_OK) {
-      goto error;
-   }
-   if (mp_cmp(key->y, p_minus1) != LTC_MP_LT || mp_cmp_d(key->y, 1) != LTC_MP_GT) {
-      err = CRYPT_INVALID_ARG;
-      goto error;
-   }
-
-   /* public key must have more than one bit set */
-   digit_count = mp_get_digit_count(key->y);
-   for (i = 0; i < digit_count && bits_set < 2; i++) {
-      digit = mp_get_digit(key->y, i);
-      while (digit > 0) {
-         if (digit & 1) bits_set++;
-         digit >>= 1;
-      }
-   }
-   if (bits_set > 1) {
-      err = CRYPT_OK;
-   }
-   else {
-      err = CRYPT_INVALID_ARG;
-   }
-
-error:
-   mp_clear(p_minus1);
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_export.c b/libs/tomcrypt/src/pk/dh/dh_export.c
deleted file mode 100644
index 5a260b6d55ff..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_export.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Export a DH key to a binary packet
-  @param out    [out] The destination for the key
-  @param outlen [in/out] The max size and resulting size of the DH key
-  @param type   Which type of key (PK_PRIVATE or PK_PUBLIC)
-  @param key    The key you wish to export
-  @return CRYPT_OK if successful
-*/
-int dh_export(unsigned char *out, unsigned long *outlen, int type, dh_key *key)
-{
-   unsigned char flags[1];
-   int err;
-   unsigned long version = 0;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   if (type == PK_PRIVATE) {
-      /* export x - private key */
-      flags[0] = 1;
-      err = der_encode_sequence_multi(out, outlen,
-                                LTC_ASN1_SHORT_INTEGER, 1UL, &version,
-                                LTC_ASN1_BIT_STRING,    1UL, flags,
-                                LTC_ASN1_INTEGER,       1UL, key->prime,
-                                LTC_ASN1_INTEGER,       1UL, key->base,
-                                LTC_ASN1_INTEGER,       1UL, key->x,
-                                LTC_ASN1_EOL,           0UL, NULL);
-   }
-   else {
-      /* export y - public key */
-      flags[0] = 0;
-      err = der_encode_sequence_multi(out, outlen,
-                                LTC_ASN1_SHORT_INTEGER, 1UL, &version,
-                                LTC_ASN1_BIT_STRING,    1UL, flags,
-                                LTC_ASN1_INTEGER,       1UL, key->prime,
-                                LTC_ASN1_INTEGER,       1UL, key->base,
-                                LTC_ASN1_INTEGER,       1UL, key->y,
-                                LTC_ASN1_EOL,           0UL, NULL);
-   }
-
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_export_key.c b/libs/tomcrypt/src/pk/dh/dh_export_key.c
deleted file mode 100644
index ebaadd6832b2..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_export_key.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Binary export a DH key to a buffer
-  @param out    [out] The destination for the key
-  @param outlen [in/out] The max size and resulting size of the DH key
-  @param type   Which type of key (PK_PRIVATE or PK_PUBLIC)
-  @param key    The key you wish to export
-  @return CRYPT_OK if successful
-*/
-int dh_export_key(void *out, unsigned long *outlen, int type, dh_key *key)
-{
-   unsigned long len;
-   void *k;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   k = (type == PK_PRIVATE) ? key->x : key->y;
-   len = mp_unsigned_bin_size(k);
-
-   if (*outlen < len) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-   *outlen = len;
-
-   return mp_to_unsigned_bin(k, out);
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_free.c b/libs/tomcrypt/src/pk/dh/dh_free.c
deleted file mode 100644
index 4fb96ca5830a..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_free.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Free the allocated ram for a DH key
-  @param key   The key which you wish to free
-*/
-void dh_free(dh_key *key)
-{
-   LTC_ARGCHKVD(key != NULL);
-   mp_cleanup_multi(&key->prime, &key->base, &key->y, &key->x, NULL);
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_generate_key.c b/libs/tomcrypt/src/pk/dh/dh_generate_key.c
deleted file mode 100644
index af9d274b88c3..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_generate_key.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-static int _dh_groupsize_to_keysize(int groupsize)
-{
-   /* The strength estimates from https://tools.ietf.org/html/rfc3526#section-8
-    * We use "Estimate 2" to get an appropriate private key (exponent) size.
-    */
-   if (groupsize <= 0) {
-      return 0;
-   }
-   else if (groupsize <= 192) {
-      return 30;     /* 1536-bit => key size 240-bit */
-   }
-   else if (groupsize <= 256) {
-      return 40;     /* 2048-bit => key size 320-bit */
-   }
-   else if (groupsize <= 384) {
-      return 52;     /* 3072-bit => key size 416-bit */
-   }
-   else if (groupsize <= 512) {
-      return 60;     /* 4096-bit => key size 480-bit */
-   }
-   else if (groupsize <= 768) {
-      return 67;     /* 6144-bit => key size 536-bit */
-   }
-   else if (groupsize <= 1024) {
-      return 77;     /* 8192-bit => key size 616-bit */
-   }
-   else {
-      return 0;
-   }
-}
-
-int dh_generate_key(prng_state *prng, int wprng, dh_key *key)
-{
-   unsigned char *buf;
-   unsigned long keysize;
-   int err, max_iterations = LTC_PK_MAX_RETRIES;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* good prng? */
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-
-   keysize = _dh_groupsize_to_keysize(mp_unsigned_bin_size(key->prime));
-   if (keysize == 0) {
-      err = CRYPT_INVALID_KEYSIZE;
-      goto freemp;
-   }
-
-   /* allocate buffer */
-   buf = XMALLOC(keysize);
-   if (buf == NULL) {
-      err = CRYPT_MEM;
-      goto freemp;
-   }
-
-   key->type = PK_PRIVATE;
-   do {
-      /* make up random buf */
-      if (prng_descriptor[wprng].read(buf, keysize, prng) != keysize) {
-         err = CRYPT_ERROR_READPRNG;
-         goto freebuf;
-      }
-      /* load the x value - private key */
-      if ((err = mp_read_unsigned_bin(key->x, buf, keysize)) != CRYPT_OK) {
-         goto freebuf;
-      }
-      /* compute the y value - public key */
-      if ((err = mp_exptmod(key->base, key->x, key->prime, key->y)) != CRYPT_OK) {
-         goto freebuf;
-      }
-      err = dh_check_pubkey(key);
-   } while (err != CRYPT_OK && max_iterations-- > 0);
-
-freebuf:
-   zeromem(buf, keysize);
-   XFREE(buf);
-freemp:
-   if (err != CRYPT_OK) dh_free(key);
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_import.c b/libs/tomcrypt/src/pk/dh/dh_import.c
deleted file mode 100644
index 22aa63129c75..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_import.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Import a DH key from a binary packet
-  @param in     The packet to read
-  @param inlen  The length of the input packet
-  @param key    [out] Where to import the key to
-  @return CRYPT_OK if successful, on error all allocated memory is freed automatically
-*/
-int dh_import(const unsigned char *in, unsigned long inlen, dh_key *key)
-{
-   unsigned char flags[1];
-   int err;
-   unsigned long version;
-
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(key != NULL);
-
-   /* init */
-   if ((err = mp_init_multi(&key->x, &key->y, &key->base, &key->prime, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* find out what type of key it is */
-   err = der_decode_sequence_multi(in, inlen,
-                                   LTC_ASN1_SHORT_INTEGER, 1UL, &version,
-                                   LTC_ASN1_BIT_STRING, 1UL, &flags,
-                                   LTC_ASN1_EOL, 0UL, NULL);
-   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
-      goto error;
-   }
-
-   if (version == 0) {
-      if (flags[0] == 1) {
-         key->type = PK_PRIVATE;
-         if ((err = der_decode_sequence_multi(in, inlen,
-                                              LTC_ASN1_SHORT_INTEGER, 1UL, &version,
-                                              LTC_ASN1_BIT_STRING,    1UL, flags,
-                                              LTC_ASN1_INTEGER,       1UL, key->prime,
-                                              LTC_ASN1_INTEGER,       1UL, key->base,
-                                              LTC_ASN1_INTEGER,       1UL, key->x,
-                                              LTC_ASN1_EOL,           0UL, NULL)) != CRYPT_OK) {
-            goto error;
-         }
-         /* compute public key: y = (base ^ x) mod prime */
-         if ((err = mp_exptmod(key->base, key->x, key->prime, key->y)) != CRYPT_OK) {
-            goto error;
-         }
-      }
-      else if (flags[0] == 0) {
-         key->type = PK_PUBLIC;
-         if ((err = der_decode_sequence_multi(in, inlen,
-                                              LTC_ASN1_SHORT_INTEGER, 1UL, &version,
-                                              LTC_ASN1_BIT_STRING,    1UL, flags,
-                                              LTC_ASN1_INTEGER,       1UL, key->prime,
-                                              LTC_ASN1_INTEGER,       1UL, key->base,
-                                              LTC_ASN1_INTEGER,       1UL, key->y,
-                                              LTC_ASN1_EOL,           0UL, NULL)) != CRYPT_OK) {
-            goto error;
-         }
-      }
-      else {
-         err = CRYPT_INVALID_PACKET;
-         goto error;
-      }
-   }
-   else {
-      err = CRYPT_INVALID_PACKET;
-      goto error;
-   }
-
-   /* check public key */
-   if ((err = dh_check_pubkey(key)) != CRYPT_OK) {
-      goto error;
-   }
-
-   return CRYPT_OK;
-
-error:
-   dh_free(key);
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_set.c b/libs/tomcrypt/src/pk/dh/dh_set.c
deleted file mode 100644
index 9feb90b34d36..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_set.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Import DH key parts p and g from raw numbers
-
-  @param p       DH's p (prime)
-  @param plen    DH's p's length
-  @param g       DH's g (group)
-  @param glen    DH's g's length
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful
-*/
-int dh_set_pg(const unsigned char *p, unsigned long plen,
-              const unsigned char *g, unsigned long glen,
-              dh_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(p           != NULL);
-   LTC_ARGCHK(g           != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   if ((err = mp_init_multi(&key->x, &key->y, &key->base, &key->prime, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((err = mp_read_unsigned_bin(key->base, (unsigned char*)g, glen)) != CRYPT_OK)     { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->prime, (unsigned char*)p, plen)) != CRYPT_OK)  { goto LBL_ERR; }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dh_free(key);
-   return err;
-}
-
-/**
-  Import DH key parts p and g from built-in DH groups
-
-  @param groupsize  The size of the DH group to use
-  @param key        [out] Where the newly created DH key will be stored
-  @return CRYPT_OK if successful, note: on error all allocated memory will be freed automatically.
-*/
-int dh_set_pg_groupsize(int groupsize, dh_key *key)
-{
-   int err, i;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-   LTC_ARGCHK(groupsize   > 0);
-
-   for (i = 0; (groupsize > ltc_dh_sets[i].size) && (ltc_dh_sets[i].size != 0); i++);
-   if (ltc_dh_sets[i].size == 0) return CRYPT_INVALID_KEYSIZE;
-
-   if ((err = mp_init_multi(&key->x, &key->y, &key->base, &key->prime, NULL)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = mp_read_radix(key->base, ltc_dh_sets[i].base, 16)) != CRYPT_OK)  { goto LBL_ERR; }
-   if ((err = mp_read_radix(key->prime, ltc_dh_sets[i].prime, 16)) != CRYPT_OK) { goto LBL_ERR; }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dh_free(key);
-   return err;
-}
-
-/**
-  Import DH public or private key part from raw numbers
-
-     NB: The p & g parts must be set beforehand
-
-  @param in      The key-part to import, either public or private.
-  @param inlen   The key-part's length
-  @param type    Which type of key (PK_PRIVATE or PK_PUBLIC)
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful
-*/
-int dh_set_key(const unsigned char *in, unsigned long inlen, int type, dh_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   if (type == PK_PRIVATE) {
-      key->type = PK_PRIVATE;
-      if ((err = mp_read_unsigned_bin(key->x, (unsigned char*)in, inlen)) != CRYPT_OK) { goto LBL_ERR; }
-      if ((err = mp_exptmod(key->base, key->x, key->prime, key->y)) != CRYPT_OK)       { goto LBL_ERR; }
-   }
-   else {
-      key->type = PK_PUBLIC;
-      if ((err = mp_read_unsigned_bin(key->y, (unsigned char*)in, inlen)) != CRYPT_OK) { goto LBL_ERR; }
-   }
-
-   /* check public key */
-   if ((err = dh_check_pubkey(key)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dh_free(key);
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_set_pg_dhparam.c b/libs/tomcrypt/src/pk/dh/dh_set_pg_dhparam.c
deleted file mode 100644
index 289ed41da263..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_set_pg_dhparam.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-  Import DH key parts p and g from dhparam
-
-      dhparam data: openssl dhparam -outform DER -out dhparam.der 2048
-
-  @param dhparam    The DH param DER encoded data
-  @param dhparamlen The length of dhparam data
-  @param key        [out] Where the newly created DH key will be stored
-  @return CRYPT_OK if successful, note: on error all allocated memory will be freed automatically.
-*/
-int dh_set_pg_dhparam(const unsigned char *dhparam, unsigned long dhparamlen, dh_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-   LTC_ARGCHK(dhparam     != NULL);
-   LTC_ARGCHK(dhparamlen  > 0);
-
-   if ((err = mp_init_multi(&key->x, &key->y, &key->base, &key->prime, NULL)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = der_decode_sequence_multi(dhparam, dhparamlen,
-                                        LTC_ASN1_INTEGER, 1UL, key->prime,
-                                        LTC_ASN1_INTEGER, 1UL, key->base,
-                                        LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dh_free(key);
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dh/dh_shared_secret.c b/libs/tomcrypt/src/pk/dh/dh_shared_secret.c
deleted file mode 100644
index 44344222de3f..000000000000
--- a/libs/tomcrypt/src/pk/dh/dh_shared_secret.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MDH
-
-/**
-   Create a DH shared secret.
-   @param private_key     The private DH key in the pair
-   @param public_key      The public DH key in the pair
-   @param out             [out] The destination of the shared data
-   @param outlen          [in/out] The max size and resulting size of the shared data.
-   @return CRYPT_OK if successful
-*/
-int dh_shared_secret(dh_key *private_key, dh_key *public_key,
-                     unsigned char *out, unsigned long *outlen)
-{
-   void *tmp;
-   unsigned long x;
-   int err;
-
-   LTC_ARGCHK(private_key != NULL);
-   LTC_ARGCHK(public_key  != NULL);
-   LTC_ARGCHK(out         != NULL);
-   LTC_ARGCHK(outlen      != NULL);
-
-   /* types valid? */
-   if (private_key->type != PK_PRIVATE) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   /* same DH group? */
-   if (mp_cmp(private_key->prime, public_key->prime) != LTC_MP_EQ) { return CRYPT_PK_TYPE_MISMATCH; }
-   if (mp_cmp(private_key->base, public_key->base) != LTC_MP_EQ)   { return CRYPT_PK_TYPE_MISMATCH; }
-
-   /* init big numbers */
-   if ((err = mp_init(&tmp)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* check public key */
-   if ((err = dh_check_pubkey(public_key)) != CRYPT_OK) {
-      goto error;
-   }
-
-   /* compute tmp = y^x mod p */
-   if ((err = mp_exptmod(public_key->y, private_key->x, private_key->prime, tmp)) != CRYPT_OK)  {
-      goto error;
-   }
-
-   /* enough space for output? */
-   x = (unsigned long)mp_unsigned_bin_size(tmp);
-   if (*outlen < x) {
-      *outlen = x;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto error;
-   }
-   if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) {
-      goto error;
-   }
-   *outlen = x;
-   err = CRYPT_OK;
-
-error:
-   mp_clear(tmp);
-   return err;
-}
-
-#endif /* LTC_MDH */
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_decrypt_key.c b/libs/tomcrypt/src/pk/dsa/dsa_decrypt_key.c
deleted file mode 100644
index 00a5472e7846..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_decrypt_key.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file dsa_decrypt_key.c
-  DSA Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Decrypt an DSA encrypted key
-  @param in       The ciphertext
-  @param inlen    The length of the ciphertext (octets)
-  @param out      [out] The plaintext
-  @param outlen   [in/out] The max size and resulting size of the plaintext
-  @param key      The corresponding private DSA key
-  @return CRYPT_OK if successful
-*/
-int dsa_decrypt_key(const unsigned char *in,  unsigned long  inlen,
-                          unsigned char *out, unsigned long *outlen,
-                          dsa_key *key)
-{
-   unsigned char  *skey, *expt;
-   void           *g_pub;
-   unsigned long  x, y;
-   unsigned long  hashOID[32] = { 0 };
-   int            hash, err;
-   ltc_asn1_list  decode[3];
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   /* right key type? */
-   if (key->type != PK_PRIVATE) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   /* decode to find out hash */
-   LTC_SET_ASN1(decode, 0, LTC_ASN1_OBJECT_IDENTIFIER, hashOID, sizeof(hashOID)/sizeof(hashOID[0]));
-   err = der_decode_sequence(in, inlen, decode, 1);
-   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
-      return err;
-   }
-
-   hash = find_hash_oid(hashOID, decode[0].size);
-   if (hash_is_valid(hash) != CRYPT_OK) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* we now have the hash! */
-
-   if ((err = mp_init(&g_pub)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* allocate memory */
-   expt   = XMALLOC(mp_unsigned_bin_size(key->p) + 1);
-   skey   = XMALLOC(MAXBLOCKSIZE);
-   if (expt == NULL || skey == NULL) {
-      if (expt != NULL) {
-         XFREE(expt);
-      }
-      if (skey != NULL) {
-         XFREE(skey);
-      }
-      mp_clear(g_pub);
-      return CRYPT_MEM;
-   }
-
-   LTC_SET_ASN1(decode, 1, LTC_ASN1_INTEGER,          g_pub,      1UL);
-   LTC_SET_ASN1(decode, 2, LTC_ASN1_OCTET_STRING,      skey,      MAXBLOCKSIZE);
-
-   /* read the structure in now */
-   if ((err = der_decode_sequence(in, inlen, decode, 3)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* make shared key */
-   x = mp_unsigned_bin_size(key->p) + 1;
-   if ((err = dsa_shared_secret(key->x, g_pub, key, expt, &x)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   y = mp_unsigned_bin_size(key->p) + 1;
-   y = MIN(y, MAXBLOCKSIZE);
-   if ((err = hash_memory(hash, expt, x, expt, &y)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* ensure the hash of the shared secret is at least as big as the encrypt itself */
-   if (decode[2].size > y) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   /* avoid buffer overflow */
-   if (*outlen < decode[2].size) {
-      *outlen = decode[2].size;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-
-   /* Decrypt the key */
-   for (x = 0; x < decode[2].size; x++) {
-     out[x] = expt[x] ^ skey[x];
-   }
-   *outlen = x;
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(expt,   mp_unsigned_bin_size(key->p) + 1);
-   zeromem(skey,   MAXBLOCKSIZE);
-#endif
-
-   XFREE(expt);
-   XFREE(skey);
-
-   mp_clear(g_pub);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_encrypt_key.c b/libs/tomcrypt/src/pk/dsa/dsa_encrypt_key.c
deleted file mode 100644
index 392c4ed121c7..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_encrypt_key.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file dsa_encrypt_key.c
-  DSA Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Encrypt a symmetric key with DSA
-  @param in         The symmetric key you want to encrypt
-  @param inlen      The length of the key to encrypt (octets)
-  @param out        [out] The destination for the ciphertext
-  @param outlen     [in/out] The max size and resulting size of the ciphertext
-  @param prng       An active PRNG state
-  @param wprng      The index of the PRNG you wish to use
-  @param hash       The index of the hash you want to use
-  @param key        The DSA key you want to encrypt to
-  @return CRYPT_OK if successful
-*/
-int dsa_encrypt_key(const unsigned char *in,   unsigned long inlen,
-                          unsigned char *out,  unsigned long *outlen,
-                          prng_state *prng, int wprng, int hash,
-                          dsa_key *key)
-{
-    unsigned char *expt, *skey;
-    void          *g_pub, *g_priv;
-    unsigned long  x, y;
-    int            err;
-
-    LTC_ARGCHK(in      != NULL);
-    LTC_ARGCHK(out     != NULL);
-    LTC_ARGCHK(outlen  != NULL);
-    LTC_ARGCHK(key     != NULL);
-
-    /* check that wprng/cipher/hash are not invalid */
-    if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-       return err;
-    }
-
-    if ((err = hash_is_valid(hash)) != CRYPT_OK) {
-       return err;
-    }
-
-    if (inlen > hash_descriptor[hash].hashsize) {
-       return CRYPT_INVALID_HASH;
-    }
-
-    /* make a random key and export the public copy */
-    if ((err = mp_init_multi(&g_pub, &g_priv, NULL)) != CRYPT_OK) {
-       return err;
-    }
-
-    expt       = XMALLOC(mp_unsigned_bin_size(key->p) + 1);
-    skey       = XMALLOC(MAXBLOCKSIZE);
-    if (expt == NULL  || skey == NULL) {
-       if (expt != NULL) {
-          XFREE(expt);
-       }
-       if (skey != NULL) {
-          XFREE(skey);
-       }
-       mp_clear_multi(g_pub, g_priv, NULL);
-       return CRYPT_MEM;
-    }
-
-    /* make a random g_priv, g_pub = g^x pair
-       private key x should be in range: 1 <= x <= q-1 (see FIPS 186-4 B.1.2)
-     */
-    if ((err = rand_bn_upto(g_priv, key->q, prng, wprng)) != CRYPT_OK) {
-      goto LBL_ERR;
-    }
-
-    /* compute y */
-    if ((err = mp_exptmod(key->g, g_priv, key->p, g_pub)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    /* make random key */
-    x        = mp_unsigned_bin_size(key->p) + 1;
-    if ((err = dsa_shared_secret(g_priv, key->y, key, expt, &x)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    y = MAXBLOCKSIZE;
-    if ((err = hash_memory(hash, expt, x, skey, &y)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    /* Encrypt key */
-    for (x = 0; x < inlen; x++) {
-      skey[x] ^= in[x];
-    }
-
-    err = der_encode_sequence_multi(out, outlen,
-                                    LTC_ASN1_OBJECT_IDENTIFIER,  hash_descriptor[hash].OIDlen,   hash_descriptor[hash].OID,
-                                    LTC_ASN1_INTEGER,            1UL,                            g_pub,
-                                    LTC_ASN1_OCTET_STRING,       inlen,                          skey,
-                                    LTC_ASN1_EOL,                0UL,                            NULL);
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-    /* clean up */
-    zeromem(expt,   mp_unsigned_bin_size(key->p) + 1);
-    zeromem(skey,   MAXBLOCKSIZE);
-#endif
-
-    XFREE(skey);
-    XFREE(expt);
-
-    mp_clear_multi(g_pub, g_priv, NULL);
-    return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_export.c b/libs/tomcrypt/src/pk/dsa/dsa_export.c
deleted file mode 100644
index 5abc3448b073..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_export.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_export.c
-   DSA implementation, export key, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Export a DSA key to a binary packet
-  @param out    [out] Where to store the packet
-  @param outlen [in/out] The max size and resulting size of the packet
-  @param type   The type of key to export (PK_PRIVATE or PK_PUBLIC)
-  @param key    The key to export
-  @return CRYPT_OK if successful
-*/
-int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key)
-{
-   unsigned long zero=0;
-   int err, std;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   std = type & PK_STD;
-   type &= ~PK_STD;
-
-   /* can we store the static header?  */
-   if (type == PK_PRIVATE && key->type != PK_PRIVATE) {
-      return CRYPT_PK_TYPE_MISMATCH;
-   }
-
-   if (type != PK_PUBLIC && type != PK_PRIVATE) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (type == PK_PRIVATE) {
-      if (std) {
-          return der_encode_sequence_multi(out, outlen,
-                                         LTC_ASN1_SHORT_INTEGER, 1UL, &zero,
-                                         LTC_ASN1_INTEGER,      1UL, key->p,
-                                         LTC_ASN1_INTEGER,      1UL, key->q,
-                                         LTC_ASN1_INTEGER,      1UL, key->g,
-                                         LTC_ASN1_INTEGER,      1UL, key->y,
-                                         LTC_ASN1_INTEGER,      1UL, key->x,
-                                         LTC_ASN1_EOL,          0UL, NULL);
-      }
-      else {
-          unsigned char flags[1];
-          flags[0] = 1;
-          return der_encode_sequence_multi(out, outlen,
-                                         LTC_ASN1_BIT_STRING,   1UL, flags,
-                                         LTC_ASN1_INTEGER,      1UL, key->g,
-                                         LTC_ASN1_INTEGER,      1UL, key->p,
-                                         LTC_ASN1_INTEGER,      1UL, key->q,
-                                         LTC_ASN1_INTEGER,      1UL, key->y,
-                                         LTC_ASN1_INTEGER,      1UL, key->x,
-                                         LTC_ASN1_EOL,          0UL, NULL);
-      }
-   } else {
-      if (std) {
-          unsigned long tmplen = (unsigned long)(mp_count_bits(key->y) / 8) + 8;
-          unsigned char* tmp = XMALLOC(tmplen);
-          ltc_asn1_list int_list[3];
-
-          if (tmp == NULL) {
-              return CRYPT_MEM;
-          }
-
-          err = der_encode_integer(key->y, tmp, &tmplen);
-          if (err != CRYPT_OK) {
-              goto error;
-          }
-
-          LTC_SET_ASN1(int_list, 0, LTC_ASN1_INTEGER, key->p, 1UL);
-          LTC_SET_ASN1(int_list, 1, LTC_ASN1_INTEGER, key->q, 1UL);
-          LTC_SET_ASN1(int_list, 2, LTC_ASN1_INTEGER, key->g, 1UL);
-
-          err = der_encode_subject_public_key_info(out, outlen, PKA_DSA, tmp,
-              tmplen, LTC_ASN1_SEQUENCE, int_list,
-              sizeof(int_list) / sizeof(int_list[0]));
-
-error:
-          XFREE(tmp);
-          return err;
-      }
-      else {
-          unsigned char flags[1];
-          flags[0] = 0;
-          return der_encode_sequence_multi(out, outlen,
-                                     LTC_ASN1_BIT_STRING,   1UL, flags,
-                                     LTC_ASN1_INTEGER,      1UL, key->g,
-                                     LTC_ASN1_INTEGER,      1UL, key->p,
-                                     LTC_ASN1_INTEGER,      1UL, key->q,
-                                     LTC_ASN1_INTEGER,      1UL, key->y,
-                                     LTC_ASN1_EOL,          0UL, NULL);
-      }
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_free.c b/libs/tomcrypt/src/pk/dsa/dsa_free.c
deleted file mode 100644
index 4a3c11908a1f..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_free.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_free.c
-   DSA implementation, free a DSA key, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-   Free a DSA key
-   @param key   The key to free from memory
-*/
-void dsa_free(dsa_key *key)
-{
-   LTC_ARGCHKVD(key != NULL);
-   mp_cleanup_multi(&key->y, &key->x, &key->q, &key->g, &key->p, NULL);
-   key->type = key->qord = 0;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_generate_key.c b/libs/tomcrypt/src/pk/dsa/dsa_generate_key.c
deleted file mode 100644
index 7c3b1eb3e6f6..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_generate_key.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_make_key.c
-   DSA implementation, generate a DSA key
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Create a DSA key
-  @param prng          An active PRNG state
-  @param wprng         The index of the PRNG desired
-  @param key           [in/out] Where to store the created key
-  @return CRYPT_OK if successful.
-*/
-int dsa_generate_key(prng_state *prng, int wprng, dsa_key *key)
-{
-  int err;
-
-  LTC_ARGCHK(key         != NULL);
-  LTC_ARGCHK(ltc_mp.name != NULL);
-
-  /* so now we have our DH structure, generator g, order q, modulus p
-     Now we need a random exponent [mod q] and it's power g^x mod p
-   */
-  /* private key x should be from range: 1 <= x <= q-1 (see FIPS 186-4 B.1.2) */
-  if ((err = rand_bn_upto(key->x, key->q, prng, wprng)) != CRYPT_OK)          { return err; }
-  if ((err = mp_exptmod(key->g, key->x, key->p, key->y)) != CRYPT_OK)            { return err; }
-  key->type = PK_PRIVATE;
-
-  return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_generate_pqg.c b/libs/tomcrypt/src/pk/dsa/dsa_generate_pqg.c
deleted file mode 100644
index 9862a88482bc..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_generate_pqg.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_generate_pqg.c
-   DSA implementation - generate DSA parameters p, q & g
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Create DSA parameters (INTERNAL ONLY, not part of public API)
-  @param prng          An active PRNG state
-  @param wprng         The index of the PRNG desired
-  @param group_size    Size of the multiplicative group (octets)
-  @param modulus_size  Size of the modulus (octets)
-  @param p             [out] bignum where generated 'p' is stored (must be initialized by caller)
-  @param q             [out] bignum where generated 'q' is stored (must be initialized by caller)
-  @param g             [out] bignum where generated 'g' is stored (must be initialized by caller)
-  @return CRYPT_OK if successful, upon error this function will free all allocated memory
-*/
-static int _dsa_make_params(prng_state *prng, int wprng, int group_size, int modulus_size, void *p, void *q, void *g)
-{
-  unsigned long L, N, n, outbytes, seedbytes, counter, j, i;
-  int err, res, mr_tests_q, mr_tests_p, found_p, found_q, hash;
-  unsigned char *wbuf, *sbuf, digest[MAXBLOCKSIZE];
-  void *t2L1, *t2N1, *t2q, *t2seedlen, *U, *W, *X, *c, *h, *e, *seedinc;
-
-  /* check size */
-  if (group_size >= LTC_MDSA_MAX_GROUP || group_size < 1 || group_size >= modulus_size) {
-    return CRYPT_INVALID_ARG;
-  }
-
- /* FIPS-186-4 A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function
-  *
-  * L = The desired length of the prime p (in bits e.g. L = 1024)
-  * N = The desired length of the prime q (in bits e.g. N = 160)
-  * seedlen = The desired bit length of the domain parameter seed; seedlen shallbe equal to or greater than N
-  * outlen  = The bit length of Hash function
-  *
-  * 1.  Check that the (L, N)
-  * 2.  If (seedlen <N), then return INVALID.
-  * 3.  n = ceil(L / outlen) - 1
-  * 4.  b = L- 1 - (n * outlen)
-  * 5.  domain_parameter_seed = an arbitrary sequence of seedlen bits
-  * 6.  U = Hash (domain_parameter_seed) mod 2^(N-1)
-  * 7.  q = 2^(N-1) + U + 1 - (U mod 2)
-  * 8.  Test whether or not q is prime as specified in Appendix C.3
-  * 9.  If qis not a prime, then go to step 5.
-  * 10. offset = 1
-  * 11. For counter = 0 to (4L- 1) do {
-  *       For j=0 to n do {
-  *         Vj = Hash ((domain_parameter_seed+ offset + j) mod 2^seedlen
-  *       }
-  *       W = V0 + (V1 *2^outlen) + ... + (Vn-1 * 2^((n-1) * outlen)) + ((Vn mod 2^b) * 2^(n * outlen))
-  *       X = W + 2^(L-1)           Comment: 0 <= W < 2^(L-1); hence 2^(L-1) <= X < 2^L
-  *       c = X mod 2*q
-  *       p = X - (c - 1)           Comment: p ~ 1 (mod 2*q)
-  *       If (p >= 2^(L-1)) {
-  *         Test whether or not p is prime as specified in Appendix C.3.
-  *         If p is determined to be prime, then return VALID and the values of p, qand (optionally) the values of domain_parameter_seed and counter
-  *       }
-  *       offset = offset + n + 1   Comment: Increment offset
-  *     }
-  */
-
-  seedbytes = group_size;
-  L = (unsigned long)modulus_size * 8;
-  N = (unsigned long)group_size * 8;
-
-  /* XXX-TODO no Lucas test */
-#ifdef LTC_MPI_HAS_LUCAS_TEST
-  /* M-R tests (when followed by one Lucas test) according FIPS-186-4 - Appendix C.3 - table C.1 */
-  mr_tests_p = (L <= 2048) ? 3 : 2;
-  if      (N <= 160)  { mr_tests_q = 19; }
-  else if (N <= 224)  { mr_tests_q = 24; }
-  else                { mr_tests_q = 27; }
-#else
-  /* M-R tests (without Lucas test) according FIPS-186-4 - Appendix C.3 - table C.1 */
-  if      (L <= 1024) { mr_tests_p = 40; }
-  else if (L <= 2048) { mr_tests_p = 56; }
-  else                { mr_tests_p = 64; }
-
-  if      (N <= 160)  { mr_tests_q = 40; }
-  else if (N <= 224)  { mr_tests_q = 56; }
-  else                { mr_tests_q = 64; }
-#endif
-
-  if (N <= 256) {
-    hash = register_hash(&sha256_desc);
-  }
-  else if (N <= 384) {
-    hash = register_hash(&sha384_desc);
-  }
-  else if (N <= 512) {
-    hash = register_hash(&sha512_desc);
-  }
-  else {
-    return CRYPT_INVALID_ARG; /* group_size too big */
-  }
-
-  if ((err = hash_is_valid(hash)) != CRYPT_OK)                                   { return err; }
-  outbytes = hash_descriptor[hash].hashsize;
-
-  n = ((L + outbytes*8 - 1) / (outbytes*8)) - 1;
-
-  if ((wbuf = XMALLOC((n+1)*outbytes)) == NULL)                                  { err = CRYPT_MEM; goto cleanup3; }
-  if ((sbuf = XMALLOC(seedbytes)) == NULL)                                       { err = CRYPT_MEM; goto cleanup2; }
-
-  err = mp_init_multi(&t2L1, &t2N1, &t2q, &t2seedlen, &U, &W, &X, &c, &h, &e, &seedinc, NULL);
-  if (err != CRYPT_OK)                                                           { goto cleanup1; }
-
-  if ((err = mp_2expt(t2L1, L-1)) != CRYPT_OK)                                   { goto cleanup; }
-  /* t2L1 = 2^(L-1) */
-  if ((err = mp_2expt(t2N1, N-1)) != CRYPT_OK)                                   { goto cleanup; }
-  /* t2N1 = 2^(N-1) */
-  if ((err = mp_2expt(t2seedlen, seedbytes*8)) != CRYPT_OK)                      { goto cleanup; }
-  /* t2seedlen = 2^seedlen */
-
-  for(found_p=0; !found_p;) {
-    /* q */
-    for(found_q=0; !found_q;) {
-      if (prng_descriptor[wprng].read(sbuf, seedbytes, prng) != seedbytes)       { err = CRYPT_ERROR_READPRNG; goto cleanup; }
-      i = outbytes;
-      if ((err = hash_memory(hash, sbuf, seedbytes, digest, &i)) != CRYPT_OK)    { goto cleanup; }
-      if ((err = mp_read_unsigned_bin(U, digest, outbytes)) != CRYPT_OK)         { goto cleanup; }
-      if ((err = mp_mod(U, t2N1, U)) != CRYPT_OK)                                { goto cleanup; }
-      if ((err = mp_add(t2N1, U, q)) != CRYPT_OK)                                { goto cleanup; }
-      if (!mp_isodd(q)) mp_add_d(q, 1, q);
-      if ((err = mp_prime_is_prime(q, mr_tests_q, &res)) != CRYPT_OK)            { goto cleanup; }
-      if (res == LTC_MP_YES) found_q = 1;
-    }
-
-    /* p */
-    if ((err = mp_read_unsigned_bin(seedinc, sbuf, seedbytes)) != CRYPT_OK)      { goto cleanup; }
-    if ((err = mp_add(q, q, t2q)) != CRYPT_OK)                                   { goto cleanup; }
-    for(counter=0; counter < 4*L && !found_p; counter++) {
-      for(j=0; j<=n; j++) {
-        if ((err = mp_add_d(seedinc, 1, seedinc)) != CRYPT_OK)                   { goto cleanup; }
-        if ((err = mp_mod(seedinc, t2seedlen, seedinc)) != CRYPT_OK)             { goto cleanup; }
-        /* seedinc = (seedinc+1) % 2^seed_bitlen */
-        if ((i = mp_unsigned_bin_size(seedinc)) > seedbytes)                     { err = CRYPT_INVALID_ARG; goto cleanup; }
-        zeromem(sbuf, seedbytes);
-        if ((err = mp_to_unsigned_bin(seedinc, sbuf + seedbytes-i)) != CRYPT_OK) { goto cleanup; }
-        i = outbytes;
-        err = hash_memory(hash, sbuf, seedbytes, wbuf+(n-j)*outbytes, &i);
-        if (err != CRYPT_OK)                                                     { goto cleanup; }
-      }
-      if ((err = mp_read_unsigned_bin(W, wbuf, (n+1)*outbytes)) != CRYPT_OK)     { goto cleanup; }
-      if ((err = mp_mod(W, t2L1, W)) != CRYPT_OK)                                { goto cleanup; }
-      if ((err = mp_add(W, t2L1, X)) != CRYPT_OK)                                { goto cleanup; }
-      if ((err = mp_mod(X, t2q, c))  != CRYPT_OK)                                { goto cleanup; }
-      if ((err = mp_sub_d(c, 1, p))  != CRYPT_OK)                                { goto cleanup; }
-      if ((err = mp_sub(X, p, p))    != CRYPT_OK)                                { goto cleanup; }
-      if (mp_cmp(p, t2L1) != LTC_MP_LT) {
-        /* p >= 2^(L-1) */
-        if ((err = mp_prime_is_prime(p, mr_tests_p, &res)) != CRYPT_OK)          { goto cleanup; }
-        if (res == LTC_MP_YES) {
-          found_p = 1;
-        }
-      }
-    }
-  }
-
- /* FIPS-186-4 A.2.1 Unverifiable Generation of the Generator g
-  * 1. e = (p - 1)/q
-  * 2. h = any integer satisfying: 1 < h < (p - 1)
-  *    h could be obtained from a random number generator or from a counter that changes after each use
-  * 3. g = h^e mod p
-  * 4. if (g == 1), then go to step 2.
-  *
-  */
-
-  if ((err = mp_sub_d(p, 1, e)) != CRYPT_OK)                                     { goto cleanup; }
-  if ((err = mp_div(e, q, e, c)) != CRYPT_OK)                                    { goto cleanup; }
-  /* e = (p - 1)/q */
-  i = mp_count_bits(p);
-  do {
-    do {
-      if ((err = rand_bn_bits(h, i, prng, wprng)) != CRYPT_OK)                   { goto cleanup; }
-    } while (mp_cmp(h, p) != LTC_MP_LT || mp_cmp_d(h, 2) != LTC_MP_GT);
-    if ((err = mp_sub_d(h, 1, h)) != CRYPT_OK)                                   { goto cleanup; }
-    /* h is randon and 1 < h < (p-1) */
-    if ((err = mp_exptmod(h, e, p, g)) != CRYPT_OK)                              { goto cleanup; }
-  } while (mp_cmp_d(g, 1) == LTC_MP_EQ);
-
-  err = CRYPT_OK;
-cleanup:
-  mp_clear_multi(t2L1, t2N1, t2q, t2seedlen, U, W, X, c, h, e, seedinc, NULL);
-cleanup1:
-  XFREE(sbuf);
-cleanup2:
-  XFREE(wbuf);
-cleanup3:
-  return err;
-}
-
-/**
-  Generate DSA parameters p, q & g
-  @param prng          An active PRNG state
-  @param wprng         The index of the PRNG desired
-  @param group_size    Size of the multiplicative group (octets)
-  @param modulus_size  Size of the modulus (octets)
-  @param key           [out] Where to store the created key
-  @return CRYPT_OK if successful.
-*/
-int dsa_generate_pqg(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init mp_ints */
-   if ((err = mp_init_multi(&key->p, &key->g, &key->q, &key->x, &key->y, NULL)) != CRYPT_OK) {
-      return err;
-   }
-   /* generate params */
-   err = _dsa_make_params(prng, wprng, group_size, modulus_size, key->p, key->q, key->g);
-   if (err != CRYPT_OK) {
-      goto cleanup;
-   }
-
-   key->qord = group_size;
-
-   return CRYPT_OK;
-
-cleanup:
-   dsa_free(key);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_import.c b/libs/tomcrypt/src/pk/dsa/dsa_import.c
deleted file mode 100644
index 74a904703062..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_import.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_import.c
-   DSA implementation, import a DSA key, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-   Import a DSA key
-   @param in       The binary packet to import from
-   @param inlen    The length of the binary packet
-   @param key      [out] Where to store the imported key
-   @return CRYPT_OK if successful, upon error this function will free all allocated memory
-*/
-int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key)
-{
-   int           err, stat;
-   unsigned long zero = 0;
-   unsigned char* tmpbuf = NULL;
-   unsigned char flags[1];
-
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init key */
-   if (mp_init_multi(&key->p, &key->g, &key->q, &key->x, &key->y, NULL) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-
-   /* try to match the old libtomcrypt format */
-   err = der_decode_sequence_multi(in, inlen, LTC_ASN1_BIT_STRING, 1UL, flags,
-                                              LTC_ASN1_EOL,        0UL, NULL);
-
-   if (err == CRYPT_OK || err == CRYPT_INPUT_TOO_LONG) {
-       /* private key */
-       if (flags[0] == 1) {
-           if ((err = der_decode_sequence_multi(in, inlen,
-                                  LTC_ASN1_BIT_STRING,   1UL, flags,
-                                  LTC_ASN1_INTEGER,      1UL, key->g,
-                                  LTC_ASN1_INTEGER,      1UL, key->p,
-                                  LTC_ASN1_INTEGER,      1UL, key->q,
-                                  LTC_ASN1_INTEGER,      1UL, key->y,
-                                  LTC_ASN1_INTEGER,      1UL, key->x,
-                                  LTC_ASN1_EOL,          0UL, NULL)) != CRYPT_OK) {
-               goto LBL_ERR;
-           }
-           key->type = PK_PRIVATE;
-           goto LBL_OK;
-       }
-       /* public key */
-       else if (flags[0] == 0) {
-           if ((err = der_decode_sequence_multi(in, inlen,
-                                      LTC_ASN1_BIT_STRING,   1UL, flags,
-                                      LTC_ASN1_INTEGER,      1UL, key->g,
-                                      LTC_ASN1_INTEGER,      1UL, key->p,
-                                      LTC_ASN1_INTEGER,      1UL, key->q,
-                                      LTC_ASN1_INTEGER,      1UL, key->y,
-                                      LTC_ASN1_EOL,          0UL, NULL)) != CRYPT_OK) {
-               goto LBL_ERR;
-           }
-           key->type = PK_PUBLIC;
-           goto LBL_OK;
-       }
-       else {
-          err = CRYPT_INVALID_PACKET;
-          goto LBL_ERR;
-       }
-   }
-   /* get key type */
-   if ((err = der_decode_sequence_multi(in, inlen,
-                          LTC_ASN1_SHORT_INTEGER, 1UL, &zero,
-                          LTC_ASN1_INTEGER,      1UL, key->p,
-                          LTC_ASN1_INTEGER,      1UL, key->q,
-                          LTC_ASN1_INTEGER,      1UL, key->g,
-                          LTC_ASN1_INTEGER,      1UL, key->y,
-                          LTC_ASN1_INTEGER,      1UL, key->x,
-                          LTC_ASN1_EOL,          0UL, NULL)) == CRYPT_OK) {
-
-       key->type = PK_PRIVATE;
-   } else { /* public */
-      ltc_asn1_list params[3];
-      unsigned long tmpbuf_len = inlen;
-
-      LTC_SET_ASN1(params, 0, LTC_ASN1_INTEGER, key->p, 1UL);
-      LTC_SET_ASN1(params, 1, LTC_ASN1_INTEGER, key->q, 1UL);
-      LTC_SET_ASN1(params, 2, LTC_ASN1_INTEGER, key->g, 1UL);
-
-      tmpbuf = XCALLOC(1, tmpbuf_len);
-      if (tmpbuf == NULL) {
-         err = CRYPT_MEM;
-         goto LBL_ERR;
-      }
-
-      err = der_decode_subject_public_key_info(in, inlen, PKA_DSA,
-                                               tmpbuf, &tmpbuf_len,
-                                               LTC_ASN1_SEQUENCE, params, 3);
-      if (err != CRYPT_OK) {
-         XFREE(tmpbuf);
-         goto LBL_ERR;
-      }
-
-      if ((err=der_decode_integer(tmpbuf, tmpbuf_len, key->y)) != CRYPT_OK) {
-         XFREE(tmpbuf);
-         goto LBL_ERR;
-      }
-
-      XFREE(tmpbuf);
-      key->type = PK_PUBLIC;
-   }
-
-LBL_OK:
-   key->qord = mp_unsigned_bin_size(key->q);
-
-   /* quick p, q, g validation, without primality testing */
-   if ((err = dsa_int_validate_pqg(key, &stat)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if (stat == 0) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-   /* validate x, y */
-   if ((err = dsa_int_validate_xy(key, &stat)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if (stat == 0) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-  return CRYPT_OK;
-LBL_ERR:
-   dsa_free(key);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_make_key.c b/libs/tomcrypt/src/pk/dsa/dsa_make_key.c
deleted file mode 100644
index f7fba2f02236..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_make_key.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_make_key.c
-   DSA implementation, generate a DSA key
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Old-style creation of a DSA key
-  @param prng          An active PRNG state
-  @param wprng         The index of the PRNG desired
-  @param group_size    Size of the multiplicative group (octets)
-  @param modulus_size  Size of the modulus (octets)
-  @param key           [out] Where to store the created key
-  @return CRYPT_OK if successful.
-*/
-int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key)
-{
-  int err;
-
-  if ((err = dsa_generate_pqg(prng, wprng, group_size, modulus_size, key)) != CRYPT_OK) { return err; }
-  if ((err = dsa_generate_key(prng, wprng, key)) != CRYPT_OK) { return err; }
-
-  return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_set.c b/libs/tomcrypt/src/pk/dsa/dsa_set.c
deleted file mode 100644
index e6695d8f3947..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_set.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-
-#ifdef LTC_MDSA
-
-/**
-  Import DSA's p, q & g from raw numbers
-  @param p       DSA's p  in binary representation
-  @param plen    The length of p
-  @param q       DSA's q  in binary representation
-  @param qlen    The length of q
-  @param g       DSA's g  in binary representation
-  @param glen    The length of g
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful.
-*/
-int dsa_set_pqg(const unsigned char *p,  unsigned long plen,
-                const unsigned char *q,  unsigned long qlen,
-                const unsigned char *g,  unsigned long glen,
-                dsa_key *key)
-{
-   int err, stat;
-
-   LTC_ARGCHK(p           != NULL);
-   LTC_ARGCHK(q           != NULL);
-   LTC_ARGCHK(g           != NULL);
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init key */
-   err = mp_init_multi(&key->p, &key->g, &key->q, &key->x, &key->y, NULL);
-   if (err != CRYPT_OK) return err;
-
-   if ((err = mp_read_unsigned_bin(key->p, (unsigned char *)p , plen)) != CRYPT_OK) { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->g, (unsigned char *)g , glen)) != CRYPT_OK) { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->q, (unsigned char *)q , qlen)) != CRYPT_OK) { goto LBL_ERR; }
-
-   key->qord = mp_unsigned_bin_size(key->q);
-
-   /* do only a quick validation, without primality testing */
-   if ((err = dsa_int_validate_pqg(key, &stat)) != CRYPT_OK)                        { goto LBL_ERR; }
-   if (stat == 0) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dsa_free(key);
-   return err;
-}
-
-/**
-  Import DSA public or private key-part from raw numbers
-
-     NB: The p, q & g parts must be set beforehand
-
-  @param in      The key-part to import, either public or private.
-  @param inlen   The key-part's length
-  @param type    Which type of key (PK_PRIVATE or PK_PUBLIC)
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful.
-*/
-int dsa_set_key(const unsigned char *in, unsigned long inlen, int type, dsa_key *key)
-{
-   int err, stat = 0;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(key->x      != NULL);
-   LTC_ARGCHK(key->y      != NULL);
-   LTC_ARGCHK(key->p      != NULL);
-   LTC_ARGCHK(key->g      != NULL);
-   LTC_ARGCHK(key->q      != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   if (type == PK_PRIVATE) {
-      key->type = PK_PRIVATE;
-      if ((err = mp_read_unsigned_bin(key->x, (unsigned char *)in, inlen)) != CRYPT_OK) { goto LBL_ERR; }
-      if ((err = mp_exptmod(key->g, key->x, key->p, key->y)) != CRYPT_OK)               { goto LBL_ERR; }
-   }
-   else {
-      key->type = PK_PUBLIC;
-      if ((err = mp_read_unsigned_bin(key->y, (unsigned char *)in, inlen)) != CRYPT_OK) { goto LBL_ERR; }
-   }
-
-   if ((err = dsa_int_validate_xy(key, &stat)) != CRYPT_OK)                             { goto LBL_ERR; }
-   if (stat == 0) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dsa_free(key);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_set_pqg_dsaparam.c b/libs/tomcrypt/src/pk/dsa/dsa_set_pqg_dsaparam.c
deleted file mode 100644
index f8c6843bd45c..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_set_pqg_dsaparam.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-
-#ifdef LTC_MDSA
-
-/**
-  Import DSA's p, q & g from dsaparam
-
-      dsaparam data: openssl dsaparam -outform DER -out dsaparam.der 2048
-
-  @param dsaparam    The DSA param DER encoded data
-  @param dsaparamlen The length of dhparam data
-  @param key         [out] the destination for the imported key
-  @return CRYPT_OK if successful.
-*/
-int dsa_set_pqg_dsaparam(const unsigned char *dsaparam, unsigned long dsaparamlen,
-                         dsa_key *key)
-{
-   int err, stat;
-
-   LTC_ARGCHK(dsaparam    != NULL);
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init key */
-   err = mp_init_multi(&key->p, &key->g, &key->q, &key->x, &key->y, NULL);
-   if (err != CRYPT_OK) return err;
-
-   if ((err = der_decode_sequence_multi(dsaparam, dsaparamlen,
-                                        LTC_ASN1_INTEGER, 1UL, key->p,
-                                        LTC_ASN1_INTEGER, 1UL, key->q,
-                                        LTC_ASN1_INTEGER, 1UL, key->g,
-                                        LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   key->qord = mp_unsigned_bin_size(key->q);
-
-   /* quick p, q, g validation, without primality testing */
-   if ((err = dsa_int_validate_pqg(key, &stat)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if (stat == 0) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   return CRYPT_OK;
-
-LBL_ERR:
-   dsa_free(key);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_shared_secret.c b/libs/tomcrypt/src/pk/dsa/dsa_shared_secret.c
deleted file mode 100644
index 54513689fa1b..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_shared_secret.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file dsa_shared_secret.c
-  DSA Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Create a DSA shared secret between two keys
-  @param private_key      The private DSA key (the exponent)
-  @param base             The base of the exponentiation (allows this to be used for both encrypt and decrypt)
-  @param public_key       The public key
-  @param out              [out] Destination of the shared secret
-  @param outlen           [in/out] The max size and resulting size of the shared secret
-  @return CRYPT_OK if successful
-*/
-int dsa_shared_secret(void          *private_key, void *base,
-                      dsa_key       *public_key,
-                      unsigned char *out,         unsigned long *outlen)
-{
-   unsigned long  x;
-   void          *res;
-   int            err;
-
-   LTC_ARGCHK(private_key != NULL);
-   LTC_ARGCHK(public_key  != NULL);
-   LTC_ARGCHK(out         != NULL);
-   LTC_ARGCHK(outlen      != NULL);
-
-   /* make new point */
-   if ((err = mp_init(&res)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((err = mp_exptmod(base, private_key, public_key->p, res)) != CRYPT_OK) {
-      mp_clear(res);
-      return err;
-   }
-
-   x = (unsigned long)mp_unsigned_bin_size(res);
-   if (*outlen < x) {
-      *outlen = x;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto done;
-   }
-   zeromem(out, x);
-   if ((err = mp_to_unsigned_bin(res, out + (x - mp_unsigned_bin_size(res))))   != CRYPT_OK)          { goto done; }
-
-   err     = CRYPT_OK;
-   *outlen = x;
-done:
-   mp_clear(res);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_sign_hash.c b/libs/tomcrypt/src/pk/dsa/dsa_sign_hash.c
deleted file mode 100644
index fdb630702f36..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_sign_hash.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_sign_hash.c
-   DSA implementation, sign a hash, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-  Sign a hash with DSA
-  @param in       The hash to sign
-  @param inlen    The length of the hash to sign
-  @param r        The "r" integer of the signature (caller must initialize with mp_init() first)
-  @param s        The "s" integer of the signature (caller must initialize with mp_init() first)
-  @param prng     An active PRNG state
-  @param wprng    The index of the PRNG desired
-  @param key      A private DSA key
-  @return CRYPT_OK if successful
-*/
-int dsa_sign_hash_raw(const unsigned char *in,  unsigned long inlen,
-                                   void   *r,   void *s,
-                               prng_state *prng, int wprng, dsa_key *key)
-{
-   void         *k, *kinv, *tmp;
-   unsigned char *buf;
-   int            err, qbits;
-
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(r   != NULL);
-   LTC_ARGCHK(s   != NULL);
-   LTC_ARGCHK(key != NULL);
-
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-   if (key->type != PK_PRIVATE) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   /* check group order size  */
-   if (key->qord >= LTC_MDSA_MAX_GROUP) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   buf = XMALLOC(LTC_MDSA_MAX_GROUP);
-   if (buf == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* Init our temps */
-   if ((err = mp_init_multi(&k, &kinv, &tmp, NULL)) != CRYPT_OK)                       { goto ERRBUF; }
-
-   qbits = mp_count_bits(key->q);
-retry:
-
-   do {
-      /* gen random k */
-      if ((err = rand_bn_bits(k, qbits, prng, wprng)) != CRYPT_OK)                     { goto error; }
-
-      /* k should be from range: 1 <= k <= q-1 (see FIPS 186-4 B.2.2) */
-      if (mp_cmp_d(k, 0) != LTC_MP_GT || mp_cmp(k, key->q) != LTC_MP_LT)               { goto retry; }
-
-      /* test gcd */
-      if ((err = mp_gcd(k, key->q, tmp)) != CRYPT_OK)                                  { goto error; }
-   } while (mp_cmp_d(tmp, 1) != LTC_MP_EQ);
-
-   /* now find 1/k mod q */
-   if ((err = mp_invmod(k, key->q, kinv)) != CRYPT_OK)                                 { goto error; }
-
-   /* now find r = g^k mod p mod q */
-   if ((err = mp_exptmod(key->g, k, key->p, r)) != CRYPT_OK)                           { goto error; }
-   if ((err = mp_mod(r, key->q, r)) != CRYPT_OK)                                       { goto error; }
-
-   if (mp_iszero(r) == LTC_MP_YES)                                                     { goto retry; }
-
-   /* FIPS 186-4 4.6: use leftmost min(bitlen(q), bitlen(hash)) bits of 'hash'*/
-   inlen = MIN(inlen, (unsigned long)(key->qord));
-
-   /* now find s = (in + xr)/k mod q */
-   if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, inlen)) != CRYPT_OK)      { goto error; }
-   if ((err = mp_mul(key->x, r, s)) != CRYPT_OK)                                       { goto error; }
-   if ((err = mp_add(s, tmp, s)) != CRYPT_OK)                                          { goto error; }
-   if ((err = mp_mulmod(s, kinv, key->q, s)) != CRYPT_OK)                              { goto error; }
-
-   if (mp_iszero(s) == LTC_MP_YES)                                                     { goto retry; }
-
-   err = CRYPT_OK;
-error:
-   mp_clear_multi(k, kinv, tmp, NULL);
-ERRBUF:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, LTC_MDSA_MAX_GROUP);
-#endif
-   XFREE(buf);
-   return err;
-}
-
-/**
-  Sign a hash with DSA
-  @param in       The hash to sign
-  @param inlen    The length of the hash to sign
-  @param out      [out] Where to store the signature
-  @param outlen   [in/out] The max size and resulting size of the signature
-  @param prng     An active PRNG state
-  @param wprng    The index of the PRNG desired
-  @param key      A private DSA key
-  @return CRYPT_OK if successful
-*/
-int dsa_sign_hash(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen,
-                        prng_state *prng, int wprng, dsa_key *key)
-{
-   void         *r, *s;
-   int           err;
-
-   LTC_ARGCHK(in      != NULL);
-   LTC_ARGCHK(out     != NULL);
-   LTC_ARGCHK(outlen  != NULL);
-   LTC_ARGCHK(key     != NULL);
-
-   if (mp_init_multi(&r, &s, NULL) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = dsa_sign_hash_raw(in, inlen, r, s, prng, wprng, key)) != CRYPT_OK) {
-      goto error;
-   }
-
-   err = der_encode_sequence_multi(out, outlen,
-                             LTC_ASN1_INTEGER, 1UL, r,
-                             LTC_ASN1_INTEGER, 1UL, s,
-                             LTC_ASN1_EOL,     0UL, NULL);
-
-error:
-   mp_clear_multi(r, s, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_verify_hash.c b/libs/tomcrypt/src/pk/dsa/dsa_verify_hash.c
deleted file mode 100644
index abb39dfc8e7a..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_verify_hash.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_verify_hash.c
-   DSA implementation, verify a signature, Tom St Denis
-*/
-
-
-#ifdef LTC_MDSA
-
-/**
-  Verify a DSA signature
-  @param r        DSA "r" parameter
-  @param s        DSA "s" parameter
-  @param hash     The hash that was signed
-  @param hashlen  The length of the hash that was signed
-  @param stat     [out] The result of the signature verification, 1==valid, 0==invalid
-  @param key      The corresponding public DSA key
-  @return CRYPT_OK if successful (even if the signature is invalid)
-*/
-int dsa_verify_hash_raw(         void   *r,          void   *s,
-                    const unsigned char *hash, unsigned long hashlen,
-                                    int *stat,      dsa_key *key)
-{
-   void          *w, *v, *u1, *u2;
-   int           err;
-
-   LTC_ARGCHK(r    != NULL);
-   LTC_ARGCHK(s    != NULL);
-   LTC_ARGCHK(stat != NULL);
-   LTC_ARGCHK(key  != NULL);
-
-   /* default to invalid signature */
-   *stat = 0;
-
-   /* init our variables */
-   if ((err = mp_init_multi(&w, &v, &u1, &u2, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* neither r or s can be null or >q*/
-   if (mp_cmp_d(r, 0) != LTC_MP_GT || mp_cmp_d(s, 0) != LTC_MP_GT || mp_cmp(r, key->q) != LTC_MP_LT || mp_cmp(s, key->q) != LTC_MP_LT) {
-      err = CRYPT_INVALID_PACKET;
-      goto error;
-   }
-
-   /* FIPS 186-4 4.7: use leftmost min(bitlen(q), bitlen(hash)) bits of 'hash' */
-   hashlen = MIN(hashlen, (unsigned long)(key->qord));
-
-   /* w = 1/s mod q */
-   if ((err = mp_invmod(s, key->q, w)) != CRYPT_OK)                                       { goto error; }
-
-   /* u1 = m * w mod q */
-   if ((err = mp_read_unsigned_bin(u1, (unsigned char *)hash, hashlen)) != CRYPT_OK)      { goto error; }
-   if ((err = mp_mulmod(u1, w, key->q, u1)) != CRYPT_OK)                                  { goto error; }
-
-   /* u2 = r*w mod q */
-   if ((err = mp_mulmod(r, w, key->q, u2)) != CRYPT_OK)                                   { goto error; }
-
-   /* v = g^u1 * y^u2 mod p mod q */
-   if ((err = mp_exptmod(key->g, u1, key->p, u1)) != CRYPT_OK)                            { goto error; }
-   if ((err = mp_exptmod(key->y, u2, key->p, u2)) != CRYPT_OK)                            { goto error; }
-   if ((err = mp_mulmod(u1, u2, key->p, v)) != CRYPT_OK)                                  { goto error; }
-   if ((err = mp_mod(v, key->q, v)) != CRYPT_OK)                                          { goto error; }
-
-   /* if r = v then we're set */
-   if (mp_cmp(r, v) == LTC_MP_EQ) {
-      *stat = 1;
-   }
-
-   err = CRYPT_OK;
-error:
-   mp_clear_multi(w, v, u1, u2, NULL);
-   return err;
-}
-
-/**
-  Verify a DSA signature
-  @param sig      The signature
-  @param siglen   The length of the signature (octets)
-  @param hash     The hash that was signed
-  @param hashlen  The length of the hash that was signed
-  @param stat     [out] The result of the signature verification, 1==valid, 0==invalid
-  @param key      The corresponding public DSA key
-  @return CRYPT_OK if successful (even if the signature is invalid)
-*/
-int dsa_verify_hash(const unsigned char *sig, unsigned long siglen,
-                    const unsigned char *hash, unsigned long hashlen,
-                    int *stat, dsa_key *key)
-{
-   int    err;
-   void   *r, *s;
-   ltc_asn1_list sig_seq[2];
-   unsigned long reallen = 0;
-
-   LTC_ARGCHK(stat != NULL);
-   *stat = 0; /* must be set before the first return */
-
-   if ((err = mp_init_multi(&r, &s, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   LTC_SET_ASN1(sig_seq, 0, LTC_ASN1_INTEGER, r, 1UL);
-   LTC_SET_ASN1(sig_seq, 1, LTC_ASN1_INTEGER, s, 1UL);
-
-   err = der_decode_sequence(sig, siglen, sig_seq, 2);
-   if (err != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   err = der_length_sequence(sig_seq, 2, &reallen);
-   if (err != CRYPT_OK || reallen != siglen) {
-      goto LBL_ERR;
-   }
-
-   /* do the op */
-   err = dsa_verify_hash_raw(r, s, hash, hashlen, stat, key);
-
-LBL_ERR:
-   mp_clear_multi(r, s, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/dsa/dsa_verify_key.c b/libs/tomcrypt/src/pk/dsa/dsa_verify_key.c
deleted file mode 100644
index e2119aab57dd..000000000000
--- a/libs/tomcrypt/src/pk/dsa/dsa_verify_key.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file dsa_verify_key.c
-   DSA implementation, verify a key, Tom St Denis
-*/
-
-#ifdef LTC_MDSA
-
-/**
-   Validate a DSA key
-
-     Yeah, this function should've been called dsa_validate_key()
-     in the first place and for compat-reasons we keep it
-     as it was (for now).
-
-   @param key   The key to validate
-   @param stat  [out]  Result of test, 1==valid, 0==invalid
-   @return CRYPT_OK if successful
-*/
-int dsa_verify_key(dsa_key *key, int *stat)
-{
-   int err;
-
-   err = dsa_int_validate_primes(key, stat);
-   if (err != CRYPT_OK || *stat == 0) return err;
-
-   err = dsa_int_validate_pqg(key, stat);
-   if (err != CRYPT_OK || *stat == 0) return err;
-
-   return dsa_int_validate_xy(key, stat);
-}
-
-/**
-   Non-complex part (no primality testing) of the validation
-   of DSA params (p, q, g)
-
-   @param key   The key to validate
-   @param stat  [out]  Result of test, 1==valid, 0==invalid
-   @return CRYPT_OK if successful
-*/
-int dsa_int_validate_pqg(dsa_key *key, int *stat)
-{
-   void *tmp1, *tmp2;
-   int  err;
-
-   LTC_ARGCHK(key  != NULL);
-   LTC_ARGCHK(stat != NULL);
-   *stat = 0;
-
-   /* check q-order */
-   if ( key->qord >= LTC_MDSA_MAX_GROUP || key->qord <= 15 ||
-        (unsigned long)key->qord >= mp_unsigned_bin_size(key->p) ||
-        (mp_unsigned_bin_size(key->p) - key->qord) >= LTC_MDSA_DELTA ) {
-      return CRYPT_OK;
-   }
-
-   /* FIPS 186-4 chapter 4.1: 1 < g < p */
-   if (mp_cmp_d(key->g, 1) != LTC_MP_GT || mp_cmp(key->g, key->p) != LTC_MP_LT) {
-      return CRYPT_OK;
-   }
-
-   if ((err = mp_init_multi(&tmp1, &tmp2, NULL)) != CRYPT_OK)        { return err; }
-
-   /* FIPS 186-4 chapter 4.1: q is a divisor of (p - 1) */
-   if ((err = mp_sub_d(key->p, 1, tmp1)) != CRYPT_OK)                { goto error; }
-   if ((err = mp_div(tmp1, key->q, tmp1, tmp2)) != CRYPT_OK)         { goto error; }
-   if (mp_iszero(tmp2) != LTC_MP_YES) {
-      err = CRYPT_OK;
-      goto error;
-   }
-
-   /* FIPS 186-4 chapter 4.1: g is a generator of a subgroup of order q in
-    * the multiplicative group of GF(p) - so we make sure that g^q mod p = 1
-    */
-   if ((err = mp_exptmod(key->g, key->q, key->p, tmp1)) != CRYPT_OK) { goto error; }
-   if (mp_cmp_d(tmp1, 1) != LTC_MP_EQ) {
-      err = CRYPT_OK;
-      goto error;
-   }
-
-   err   = CRYPT_OK;
-   *stat = 1;
-error:
-   mp_clear_multi(tmp2, tmp1, NULL);
-   return err;
-}
-
-/**
-   Primality testing of DSA params p and q
-
-   @param key   The key to validate
-   @param stat  [out]  Result of test, 1==valid, 0==invalid
-   @return CRYPT_OK if successful
-*/
-int dsa_int_validate_primes(dsa_key *key, int *stat)
-{
-   int err, res;
-
-   *stat = 0;
-   LTC_ARGCHK(key  != NULL);
-   LTC_ARGCHK(stat != NULL);
-
-   /* key->q prime? */
-   if ((err = mp_prime_is_prime(key->q, LTC_MILLER_RABIN_REPS, &res)) != CRYPT_OK) {
-      return err;
-   }
-   if (res == LTC_MP_NO) {
-      return CRYPT_OK;
-   }
-
-   /* key->p prime? */
-   if ((err = mp_prime_is_prime(key->p, LTC_MILLER_RABIN_REPS, &res)) != CRYPT_OK) {
-      return err;
-   }
-   if (res == LTC_MP_NO) {
-      return CRYPT_OK;
-   }
-
-   *stat = 1;
-   return CRYPT_OK;
-}
-
-/**
-   Validation of a DSA key (x and y values)
-
-   @param key   The key to validate
-   @param stat  [out]  Result of test, 1==valid, 0==invalid
-   @return CRYPT_OK if successful
-*/
-int dsa_int_validate_xy(dsa_key *key, int *stat)
-{
-   void *tmp;
-   int  err;
-
-   *stat = 0;
-   LTC_ARGCHK(key  != NULL);
-   LTC_ARGCHK(stat != NULL);
-
-   /* 1 < y < p-1 */
-   if ((err = mp_init(&tmp)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = mp_sub_d(key->p, 1, tmp)) != CRYPT_OK) {
-      goto error;
-   }
-   if (mp_cmp_d(key->y, 1) != LTC_MP_GT || mp_cmp(key->y, tmp) != LTC_MP_LT) {
-      err = CRYPT_OK;
-      goto error;
-   }
-
-   if (key->type == PK_PRIVATE) {
-      /* FIPS 186-4 chapter 4.1: 0 < x < q */
-      if (mp_cmp_d(key->x, 0) != LTC_MP_GT || mp_cmp(key->x, key->q) != LTC_MP_LT) {
-         err = CRYPT_OK;
-         goto error;
-      }
-      /* FIPS 186-4 chapter 4.1: y = g^x mod p */
-      if ((err = mp_exptmod(key->g, key->x, key->p, tmp)) != CRYPT_OK) {
-         goto error;
-      }
-      if (mp_cmp(tmp, key->y) != LTC_MP_EQ) {
-         err = CRYPT_OK;
-         goto error;
-      }
-   }
-   else {
-      /* with just a public key we cannot test y = g^x mod p therefore we
-       * only test that y^q mod p = 1, which makes sure y is in g^x mod p
-       */
-      if ((err = mp_exptmod(key->y, key->q, key->p, tmp)) != CRYPT_OK) {
-         goto error;
-      }
-      if (mp_cmp_d(tmp, 1) != LTC_MP_EQ) {
-         err = CRYPT_OK;
-         goto error;
-      }
-   }
-
-   err   = CRYPT_OK;
-   *stat = 1;
-error:
-   mp_clear(tmp);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc.c b/libs/tomcrypt/src/pk/ecc/ecc.c
deleted file mode 100644
index f055b327b136..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/* This holds the key settings.  ***MUST*** be organized by size from smallest to largest. */
-const ltc_ecc_set_type ltc_ecc_sets[] = {
-#ifdef LTC_ECC112
-{
-        14,
-        "SECP112R1",
-        "DB7C2ABF62E35E668076BEAD208B",
-        "659EF8BA043916EEDE8911702B22",
-        "DB7C2ABF62E35E7628DFAC6561C5",
-        "09487239995A5EE76B55F9C2F098",
-        "A89CE5AF8724C0A23E0E0FF77500"
-},
-#endif
-#ifdef LTC_ECC128
-{
-        16,
-        "SECP128R1",
-        "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF",
-        "E87579C11079F43DD824993C2CEE5ED3",
-        "FFFFFFFE0000000075A30D1B9038A115",
-        "161FF7528B899B2D0C28607CA52C5B86",
-        "CF5AC8395BAFEB13C02DA292DDED7A83",
-},
-#endif
-#ifdef LTC_ECC160
-{
-        20,
-        "SECP160R1",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
-        "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
-        "0100000000000000000001F4C8F927AED3CA752257",
-        "4A96B5688EF573284664698968C38BB913CBFC82",
-        "23A628553168947D59DCC912042351377AC5FB32",
-},
-#endif
-#ifdef LTC_ECC192
-{
-        24,
-        "ECC-192",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
-        "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1",
-        "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
-        "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
-        "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
-},
-#endif
-#ifdef LTC_ECC224
-{
-        28,
-        "ECC-224",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
-        "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
-        "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
-        "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
-},
-#endif
-#ifdef LTC_ECC256
-{
-        32,
-        "ECC-256",
-        "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
-        "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
-        "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
-        "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
-        "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
-},
-#endif
-#ifdef LTC_ECC384
-{
-        48,
-        "ECC-384",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
-        "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
-        "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
-        "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
-},
-#endif
-#ifdef LTC_ECC521
-{
-        66,
-        "ECC-521",
-        "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
-        "51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
-        "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
-        "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
-        "11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
-},
-#endif
-{
-   0,
-   NULL, NULL, NULL, NULL, NULL, NULL
-}
-};
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_ansi_x963_export.c b/libs/tomcrypt/src/pk/ecc/ecc_ansi_x963_export.c
deleted file mode 100644
index fb31727ac747..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_ansi_x963_export.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_ansi_x963_export.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/** ECC X9.63 (Sec. 4.3.6) uncompressed export
-  @param key     Key to export
-  @param out     [out] destination of export
-  @param outlen  [in/out]  Length of destination and final output size
-  Return CRYPT_OK on success
-*/
-int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen)
-{
-   unsigned char buf[ECC_BUF_SIZE];
-   unsigned long numlen, xlen, ylen;
-
-   LTC_ARGCHK(key    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   if (ltc_ecc_is_valid_idx(key->idx) == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-   numlen = key->dp->size;
-   xlen = mp_unsigned_bin_size(key->pubkey.x);
-   ylen = mp_unsigned_bin_size(key->pubkey.y);
-
-   if (xlen > numlen || ylen > numlen || sizeof(buf) < numlen) {
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (*outlen < (1 + 2*numlen)) {
-      *outlen = 1 + 2*numlen;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   LTC_ARGCHK(out    != NULL);
-
-   /* store byte 0x04 */
-   out[0] = 0x04;
-
-   /* pad and store x */
-   zeromem(buf, sizeof(buf));
-   mp_to_unsigned_bin(key->pubkey.x, buf + (numlen - xlen));
-   XMEMCPY(out+1, buf, numlen);
-
-   /* pad and store y */
-   zeromem(buf, sizeof(buf));
-   mp_to_unsigned_bin(key->pubkey.y, buf + (numlen - ylen));
-   XMEMCPY(out+1+numlen, buf, numlen);
-
-   *outlen = 1 + 2*numlen;
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_ansi_x963_import.c b/libs/tomcrypt/src/pk/ecc/ecc_ansi_x963_import.c
deleted file mode 100644
index 697da106029b..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_ansi_x963_import.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_ansi_x963_import.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/** Import an ANSI X9.63 format public key
-  @param in      The input data to read
-  @param inlen   The length of the input data
-  @param key     [out] destination to store imported key \
-*/
-int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key)
-{
-   return ecc_ansi_x963_import_ex(in, inlen, key, NULL);
-}
-
-int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp)
-{
-   int x, err;
-
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(key != NULL);
-
-   /* must be odd */
-   if ((inlen & 1) == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* init key */
-   if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-
-   /* check for 4, 6 or 7 */
-   if (in[0] != 4 && in[0] != 6 && in[0] != 7) {
-      err = CRYPT_INVALID_PACKET;
-      goto error;
-   }
-
-   /* read data */
-   if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)in+1, (inlen-1)>>1)) != CRYPT_OK) {
-      goto error;
-   }
-
-   if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)in+1+((inlen-1)>>1), (inlen-1)>>1)) != CRYPT_OK) {
-      goto error;
-   }
-   if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto error; }
-
-   if (dp == NULL) {
-     /* determine the idx */
-      for (x = 0; ltc_ecc_sets[x].size != 0; x++) {
-         if ((unsigned)ltc_ecc_sets[x].size >= ((inlen-1)>>1)) {
-            break;
-         }
-      }
-      if (ltc_ecc_sets[x].size == 0) {
-         err = CRYPT_INVALID_PACKET;
-         goto error;
-      }
-      /* set the idx */
-      key->idx  = x;
-      key->dp = &ltc_ecc_sets[x];
-   } else {
-      if (((inlen-1)>>1) != (unsigned long) dp->size) {
-         err = CRYPT_INVALID_PACKET;
-         goto error;
-      }
-      key->idx = -1;
-      key->dp  = dp;
-   }
-   key->type = PK_PUBLIC;
-
-   /* we're done */
-   return CRYPT_OK;
-error:
-   mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_decrypt_key.c b/libs/tomcrypt/src/pk/ecc/ecc_decrypt_key.c
deleted file mode 100644
index 4694fbbed69f..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_decrypt_key.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_decrypt_key.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Decrypt an ECC encrypted key
-  @param in       The ciphertext
-  @param inlen    The length of the ciphertext (octets)
-  @param out      [out] The plaintext
-  @param outlen   [in/out] The max size and resulting size of the plaintext
-  @param key      The corresponding private ECC key
-  @return CRYPT_OK if successful
-*/
-int ecc_decrypt_key(const unsigned char *in,  unsigned long  inlen,
-                          unsigned char *out, unsigned long *outlen,
-                          ecc_key *key)
-{
-   unsigned char *ecc_shared, *skey, *pub_expt;
-   unsigned long  x, y;
-   unsigned long  hashOID[32] = { 0 };
-   int            hash, err;
-   ecc_key        pubkey;
-   ltc_asn1_list  decode[3];
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   /* right key type? */
-   if (key->type != PK_PRIVATE) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   /* decode to find out hash */
-   LTC_SET_ASN1(decode, 0, LTC_ASN1_OBJECT_IDENTIFIER, hashOID, sizeof(hashOID)/sizeof(hashOID[0]));
-   err = der_decode_sequence(in, inlen, decode, 1);
-   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
-      return err;
-   }
-
-   hash = find_hash_oid(hashOID, decode[0].size);
-   if (hash_is_valid(hash) != CRYPT_OK) {
-      return CRYPT_INVALID_PACKET;
-   }
-
-   /* we now have the hash! */
-
-   /* allocate memory */
-   pub_expt   = XMALLOC(ECC_BUF_SIZE);
-   ecc_shared = XMALLOC(ECC_BUF_SIZE);
-   skey       = XMALLOC(MAXBLOCKSIZE);
-   if (pub_expt == NULL || ecc_shared == NULL || skey == NULL) {
-      if (pub_expt != NULL) {
-         XFREE(pub_expt);
-      }
-      if (ecc_shared != NULL) {
-         XFREE(ecc_shared);
-      }
-      if (skey != NULL) {
-         XFREE(skey);
-      }
-      return CRYPT_MEM;
-   }
-   LTC_SET_ASN1(decode, 1, LTC_ASN1_OCTET_STRING,      pub_expt,  ECC_BUF_SIZE);
-   LTC_SET_ASN1(decode, 2, LTC_ASN1_OCTET_STRING,      skey,      MAXBLOCKSIZE);
-
-   /* read the structure in now */
-   if ((err = der_decode_sequence(in, inlen, decode, 3)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* import ECC key from packet */
-   if ((err = ecc_import(decode[1].data, decode[1].size, &pubkey)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* make shared key */
-   x = ECC_BUF_SIZE;
-   if ((err = ecc_shared_secret(key, &pubkey, ecc_shared, &x)) != CRYPT_OK) {
-      ecc_free(&pubkey);
-      goto LBL_ERR;
-   }
-   ecc_free(&pubkey);
-
-   y = MIN(ECC_BUF_SIZE, MAXBLOCKSIZE);
-   if ((err = hash_memory(hash, ecc_shared, x, ecc_shared, &y)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* ensure the hash of the shared secret is at least as big as the encrypt itself */
-   if (decode[2].size > y) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   /* avoid buffer overflow */
-   if (*outlen < decode[2].size) {
-      *outlen = decode[2].size;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-
-   /* Decrypt the key */
-   for (x = 0; x < decode[2].size; x++) {
-     out[x] = skey[x] ^ ecc_shared[x];
-   }
-   *outlen = x;
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(pub_expt,   ECC_BUF_SIZE);
-   zeromem(ecc_shared, ECC_BUF_SIZE);
-   zeromem(skey,       MAXBLOCKSIZE);
-#endif
-
-   XFREE(pub_expt);
-   XFREE(ecc_shared);
-   XFREE(skey);
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_encrypt_key.c b/libs/tomcrypt/src/pk/ecc/ecc_encrypt_key.c
deleted file mode 100644
index 833f5c530035..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_encrypt_key.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_encrypt_key.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Encrypt a symmetric key with ECC
-  @param in         The symmetric key you want to encrypt
-  @param inlen      The length of the key to encrypt (octets)
-  @param out        [out] The destination for the ciphertext
-  @param outlen     [in/out] The max size and resulting size of the ciphertext
-  @param prng       An active PRNG state
-  @param wprng      The index of the PRNG you wish to use
-  @param hash       The index of the hash you want to use
-  @param key        The ECC key you want to encrypt to
-  @return CRYPT_OK if successful
-*/
-int ecc_encrypt_key(const unsigned char *in,   unsigned long inlen,
-                          unsigned char *out,  unsigned long *outlen,
-                          prng_state *prng, int wprng, int hash,
-                          ecc_key *key)
-{
-    unsigned char *pub_expt, *ecc_shared, *skey;
-    ecc_key        pubkey;
-    unsigned long  x, y, pubkeysize;
-    int            err;
-
-    LTC_ARGCHK(in      != NULL);
-    LTC_ARGCHK(out     != NULL);
-    LTC_ARGCHK(outlen  != NULL);
-    LTC_ARGCHK(key     != NULL);
-
-    /* check that wprng/cipher/hash are not invalid */
-    if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-       return err;
-    }
-
-    if ((err = hash_is_valid(hash)) != CRYPT_OK) {
-       return err;
-    }
-
-    if (inlen > hash_descriptor[hash].hashsize) {
-       return CRYPT_INVALID_HASH;
-    }
-
-    /* make a random key and export the public copy */
-    if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) {
-       return err;
-    }
-
-    pub_expt   = XMALLOC(ECC_BUF_SIZE);
-    ecc_shared = XMALLOC(ECC_BUF_SIZE);
-    skey       = XMALLOC(MAXBLOCKSIZE);
-    if (pub_expt == NULL || ecc_shared == NULL || skey == NULL) {
-       if (pub_expt != NULL) {
-          XFREE(pub_expt);
-       }
-       if (ecc_shared != NULL) {
-          XFREE(ecc_shared);
-       }
-       if (skey != NULL) {
-          XFREE(skey);
-       }
-       ecc_free(&pubkey);
-       return CRYPT_MEM;
-    }
-
-    pubkeysize = ECC_BUF_SIZE;
-    if ((err = ecc_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) {
-       ecc_free(&pubkey);
-       goto LBL_ERR;
-    }
-
-    /* make random key */
-    x        = ECC_BUF_SIZE;
-    if ((err = ecc_shared_secret(&pubkey, key, ecc_shared, &x)) != CRYPT_OK) {
-       ecc_free(&pubkey);
-       goto LBL_ERR;
-    }
-    ecc_free(&pubkey);
-    y = MAXBLOCKSIZE;
-    if ((err = hash_memory(hash, ecc_shared, x, skey, &y)) != CRYPT_OK) {
-       goto LBL_ERR;
-    }
-
-    /* Encrypt key */
-    for (x = 0; x < inlen; x++) {
-      skey[x] ^= in[x];
-    }
-
-    err = der_encode_sequence_multi(out, outlen,
-                                    LTC_ASN1_OBJECT_IDENTIFIER,  hash_descriptor[hash].OIDlen,   hash_descriptor[hash].OID,
-                                    LTC_ASN1_OCTET_STRING,       pubkeysize,                     pub_expt,
-                                    LTC_ASN1_OCTET_STRING,       inlen,                          skey,
-                                    LTC_ASN1_EOL,                0UL,                            NULL);
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-    /* clean up */
-    zeromem(pub_expt,   ECC_BUF_SIZE);
-    zeromem(ecc_shared, ECC_BUF_SIZE);
-    zeromem(skey,       MAXBLOCKSIZE);
-#endif
-
-    XFREE(skey);
-    XFREE(ecc_shared);
-    XFREE(pub_expt);
-
-    return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_export.c b/libs/tomcrypt/src/pk/ecc/ecc_export.c
deleted file mode 100644
index 5f61ab9c60a6..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_export.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_export.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Export an ECC key as a binary packet
-  @param out     [out] Destination for the key
-  @param outlen  [in/out] Max size and resulting size of the exported key
-  @param type    The type of key you want to export (PK_PRIVATE or PK_PUBLIC)
-  @param key     The key to export
-  @return CRYPT_OK if successful
-*/
-int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key)
-{
-   int           err;
-   unsigned char flags[1];
-   unsigned long key_size;
-
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   /* type valid? */
-   if (key->type != PK_PRIVATE && type == PK_PRIVATE) {
-      return CRYPT_PK_TYPE_MISMATCH;
-   }
-
-   if (ltc_ecc_is_valid_idx(key->idx) == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   /* we store the NIST byte size */
-   key_size = key->dp->size;
-
-   if (type == PK_PRIVATE) {
-       flags[0] = 1;
-       err = der_encode_sequence_multi(out, outlen,
-                                 LTC_ASN1_BIT_STRING,      1UL, flags,
-                                 LTC_ASN1_SHORT_INTEGER,   1UL, &key_size,
-                                 LTC_ASN1_INTEGER,         1UL, key->pubkey.x,
-                                 LTC_ASN1_INTEGER,         1UL, key->pubkey.y,
-                                 LTC_ASN1_INTEGER,         1UL, key->k,
-                                 LTC_ASN1_EOL,             0UL, NULL);
-   } else {
-       flags[0] = 0;
-       err = der_encode_sequence_multi(out, outlen,
-                                 LTC_ASN1_BIT_STRING,      1UL, flags,
-                                 LTC_ASN1_SHORT_INTEGER,   1UL, &key_size,
-                                 LTC_ASN1_INTEGER,         1UL, key->pubkey.x,
-                                 LTC_ASN1_INTEGER,         1UL, key->pubkey.y,
-                                 LTC_ASN1_EOL,             0UL, NULL);
-   }
-
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_free.c b/libs/tomcrypt/src/pk/ecc/ecc_free.c
deleted file mode 100644
index 98e146be4691..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_free.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_free.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Free an ECC key from memory
-  @param key   The key you wish to free
-*/
-void ecc_free(ecc_key *key)
-{
-   LTC_ARGCHKVD(key != NULL);
-   mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_get_size.c b/libs/tomcrypt/src/pk/ecc/ecc_get_size.c
deleted file mode 100644
index 5f67153be4ed..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_get_size.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_get_size.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Get the size of an ECC key
-  @param key    The key to get the size of
-  @return The size (octets) of the key or INT_MAX on error
-*/
-int ecc_get_size(ecc_key *key)
-{
-   LTC_ARGCHK(key != NULL);
-   if (ltc_ecc_is_valid_idx(key->idx))
-      return key->dp->size;
-   else
-      return INT_MAX; /* large value known to cause it to fail when passed to ecc_make_key() */
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_import.c b/libs/tomcrypt/src/pk/ecc/ecc_import.c
deleted file mode 100644
index 47b392c16d35..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_import.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_import.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-static int _is_point(ecc_key *key)
-{
-   void *prime, *b, *t1, *t2;
-   int err;
-
-   if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* load prime and b */
-   if ((err = mp_read_radix(prime, key->dp->prime, 16)) != CRYPT_OK)                          { goto error; }
-   if ((err = mp_read_radix(b, key->dp->B, 16)) != CRYPT_OK)                                  { goto error; }
-
-   /* compute y^2 */
-   if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK)                                         { goto error; }
-
-   /* compute x^3 */
-   if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK)                                         { goto error; }
-   if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK)                                             { goto error; }
-   if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK)                                     { goto error; }
-
-   /* compute y^2 - x^3 */
-   if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK)                                                { goto error; }
-
-   /* compute y^2 - x^3 + 3x */
-   if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK)                                     { goto error; }
-   if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK)                                     { goto error; }
-   if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK)                                     { goto error; }
-   if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK)                                             { goto error; }
-   while (mp_cmp_d(t1, 0) == LTC_MP_LT) {
-      if ((err = mp_add(t1, prime, t1)) != CRYPT_OK)                                          { goto error; }
-   }
-   while (mp_cmp(t1, prime) != LTC_MP_LT) {
-      if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK)                                          { goto error; }
-   }
-
-   /* compare to b */
-   if (mp_cmp(t1, b) != LTC_MP_EQ) {
-      err = CRYPT_INVALID_PACKET;
-   } else {
-      err = CRYPT_OK;
-   }
-
-error:
-   mp_clear_multi(prime, b, t1, t2, NULL);
-   return err;
-}
-
-/**
-  Import an ECC key from a binary packet
-  @param in      The packet to import
-  @param inlen   The length of the packet
-  @param key     [out] The destination of the import
-  @return CRYPT_OK if successful, upon error all allocated memory will be freed
-*/
-int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key)
-{
-   return ecc_import_ex(in, inlen, key, NULL);
-}
-
-/**
-  Import an ECC key from a binary packet, using user supplied domain params rather than one of the NIST ones
-  @param in      The packet to import
-  @param inlen   The length of the packet
-  @param key     [out] The destination of the import
-  @param dp      pointer to user supplied params; must be the same as the params used when exporting
-  @return CRYPT_OK if successful, upon error all allocated memory will be freed
-*/
-int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp)
-{
-   unsigned long key_size;
-   unsigned char flags[1];
-   int           err;
-
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init key */
-   if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-
-   /* find out what type of key it is */
-   err = der_decode_sequence_multi(in, inlen, LTC_ASN1_BIT_STRING, 1UL, flags,
-                                              LTC_ASN1_EOL,        0UL, NULL);
-   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
-      goto done;
-   }
-
-
-   if (flags[0] == 1) {
-      /* private key */
-      key->type = PK_PRIVATE;
-      if ((err = der_decode_sequence_multi(in, inlen,
-                                     LTC_ASN1_BIT_STRING,      1UL, flags,
-                                     LTC_ASN1_SHORT_INTEGER,   1UL, &key_size,
-                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.x,
-                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.y,
-                                     LTC_ASN1_INTEGER,         1UL, key->k,
-                                     LTC_ASN1_EOL,             0UL, NULL)) != CRYPT_OK) {
-         goto done;
-      }
-   } else if (flags[0] == 0) {
-      /* public key */
-      key->type = PK_PUBLIC;
-      if ((err = der_decode_sequence_multi(in, inlen,
-                                     LTC_ASN1_BIT_STRING,      1UL, flags,
-                                     LTC_ASN1_SHORT_INTEGER,   1UL, &key_size,
-                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.x,
-                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.y,
-                                     LTC_ASN1_EOL,             0UL, NULL)) != CRYPT_OK) {
-         goto done;
-      }
-   }
-   else {
-      err = CRYPT_INVALID_PACKET;
-      goto done;
-   }
-
-   if (dp == NULL) {
-     /* find the idx */
-     for (key->idx = 0; ltc_ecc_sets[key->idx].size && (unsigned long)ltc_ecc_sets[key->idx].size != key_size; ++key->idx);
-     if (ltc_ecc_sets[key->idx].size == 0) {
-       err = CRYPT_INVALID_PACKET;
-       goto done;
-     }
-     key->dp = &ltc_ecc_sets[key->idx];
-   } else {
-     key->idx = -1;
-     key->dp = dp;
-   }
-   /* set z */
-   if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto done; }
-
-   /* is it a point on the curve?  */
-   if ((err = _is_point(key)) != CRYPT_OK) {
-      goto done;
-   }
-
-   /* we're good */
-   return CRYPT_OK;
-done:
-   mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
-   return err;
-}
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_make_key.c b/libs/tomcrypt/src/pk/ecc/ecc_make_key.c
deleted file mode 100644
index 71fe3f30c3b7..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_make_key.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_make_key.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Make a new ECC key
-  @param prng         An active PRNG state
-  @param wprng        The index of the PRNG you wish to use
-  @param keysize      The keysize for the new key (in octets from 20 to 65 bytes)
-  @param key          [out] Destination of the newly created key
-  @return CRYPT_OK if successful, upon error all allocated memory will be freed
-*/
-int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key)
-{
-   int x, err;
-
-   /* find key size */
-   for (x = 0; (keysize > ltc_ecc_sets[x].size) && (ltc_ecc_sets[x].size != 0); x++);
-   keysize = ltc_ecc_sets[x].size;
-
-   if (keysize > ECC_MAXSIZE || ltc_ecc_sets[x].size == 0) {
-      return CRYPT_INVALID_KEYSIZE;
-   }
-   err = ecc_make_key_ex(prng, wprng, key, &ltc_ecc_sets[x]);
-   key->idx = x;
-   return err;
-}
-
-int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp)
-{
-   int            err;
-   ecc_point     *base;
-   void          *prime, *order;
-   unsigned char *buf;
-   int            keysize;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-   LTC_ARGCHK(dp          != NULL);
-
-   /* good prng? */
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-
-   key->idx = -1;
-   key->dp  = dp;
-   keysize  = dp->size;
-
-   /* allocate ram */
-   base = NULL;
-   buf  = XMALLOC(ECC_MAXSIZE);
-   if (buf == NULL) {
-      return CRYPT_MEM;
-   }
-
-   /* make up random string */
-   if (prng_descriptor[wprng].read(buf, (unsigned long)keysize, prng) != (unsigned long)keysize) {
-      err = CRYPT_ERROR_READPRNG;
-      goto ERR_BUF;
-   }
-
-   /* setup the key variables */
-   if ((err = mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, &prime, &order, NULL)) != CRYPT_OK) {
-      goto ERR_BUF;
-   }
-   base = ltc_ecc_new_point();
-   if (base == NULL) {
-      err = CRYPT_MEM;
-      goto errkey;
-   }
-
-   /* read in the specs for this key */
-   if ((err = mp_read_radix(prime,   (char *)key->dp->prime, 16)) != CRYPT_OK)                  { goto errkey; }
-   if ((err = mp_read_radix(order,   (char *)key->dp->order, 16)) != CRYPT_OK)                  { goto errkey; }
-   if ((err = mp_read_radix(base->x, (char *)key->dp->Gx, 16)) != CRYPT_OK)                     { goto errkey; }
-   if ((err = mp_read_radix(base->y, (char *)key->dp->Gy, 16)) != CRYPT_OK)                     { goto errkey; }
-   if ((err = mp_set(base->z, 1)) != CRYPT_OK)                                                  { goto errkey; }
-   if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)buf, keysize)) != CRYPT_OK)         { goto errkey; }
-
-   /* the key should be smaller than the order of base point */
-   if (mp_cmp(key->k, order) != LTC_MP_LT) {
-       if((err = mp_mod(key->k, order, key->k)) != CRYPT_OK)                                    { goto errkey; }
-   }
-   /* make the public key */
-   if ((err = ltc_mp.ecc_ptmul(key->k, base, &key->pubkey, prime, 1)) != CRYPT_OK)              { goto errkey; }
-   key->type = PK_PRIVATE;
-
-   /* free up ram */
-   err = CRYPT_OK;
-   goto cleanup;
-errkey:
-   mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
-cleanup:
-   ltc_ecc_del_point(base);
-   mp_clear_multi(prime, order, NULL);
-ERR_BUF:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, ECC_MAXSIZE);
-#endif
-   XFREE(buf);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_shared_secret.c b/libs/tomcrypt/src/pk/ecc/ecc_shared_secret.c
deleted file mode 100644
index bb5d14ec2c6c..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_shared_secret.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_shared_secret.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Create an ECC shared secret between two keys
-  @param private_key      The private ECC key
-  @param public_key       The public key
-  @param out              [out] Destination of the shared secret (Conforms to EC-DH from ANSI X9.63)
-  @param outlen           [in/out] The max size and resulting size of the shared secret
-  @return CRYPT_OK if successful
-*/
-int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key,
-                      unsigned char *out, unsigned long *outlen)
-{
-   unsigned long  x;
-   ecc_point     *result;
-   void          *prime;
-   int            err;
-
-   LTC_ARGCHK(private_key != NULL);
-   LTC_ARGCHK(public_key  != NULL);
-   LTC_ARGCHK(out         != NULL);
-   LTC_ARGCHK(outlen      != NULL);
-
-   /* type valid? */
-   if (private_key->type != PK_PRIVATE) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   if (ltc_ecc_is_valid_idx(private_key->idx) == 0 || ltc_ecc_is_valid_idx(public_key->idx) == 0) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if (XSTRCMP(private_key->dp->name, public_key->dp->name) != 0) {
-      return CRYPT_PK_TYPE_MISMATCH;
-   }
-
-   /* make new point */
-   result = ltc_ecc_new_point();
-   if (result == NULL) {
-      return CRYPT_MEM;
-   }
-
-   if ((err = mp_init(&prime)) != CRYPT_OK) {
-      ltc_ecc_del_point(result);
-      return err;
-   }
-
-   if ((err = mp_read_radix(prime, (char *)private_key->dp->prime, 16)) != CRYPT_OK)                               { goto done; }
-   if ((err = ltc_mp.ecc_ptmul(private_key->k, &public_key->pubkey, result, prime, 1)) != CRYPT_OK)                { goto done; }
-
-   x = (unsigned long)mp_unsigned_bin_size(prime);
-   if (*outlen < x) {
-      *outlen = x;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto done;
-   }
-   zeromem(out, x);
-   if ((err = mp_to_unsigned_bin(result->x, out + (x - mp_unsigned_bin_size(result->x))))   != CRYPT_OK)           { goto done; }
-
-   err     = CRYPT_OK;
-   *outlen = x;
-done:
-   mp_clear(prime);
-   ltc_ecc_del_point(result);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_sign_hash.c b/libs/tomcrypt/src/pk/ecc/ecc_sign_hash.c
deleted file mode 100644
index 6314952aaf55..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_sign_hash.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MECC
-
-/**
-  @file ecc_sign_hash.c
-  ECC Crypto, Tom St Denis
-*/
-
-static int _ecc_sign_hash(const unsigned char *in,  unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen,
-                                prng_state *prng, int wprng, ecc_key *key, int sigformat)
-{
-   ecc_key       pubkey;
-   void          *r, *s, *e, *p, *b;
-   int           err, max_iterations = LTC_PK_MAX_RETRIES;
-   unsigned long pbits, pbytes, i, shift_right;
-   unsigned char ch, buf[MAXBLOCKSIZE];
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   /* is this a private key? */
-   if (key->type != PK_PRIVATE) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   /* is the IDX valid ?  */
-   if (ltc_ecc_is_valid_idx(key->idx) != 1) {
-      return CRYPT_PK_INVALID_TYPE;
-   }
-
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* init the bignums */
-   if ((err = mp_init_multi(&r, &s, &p, &e, &b, NULL)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK)              { goto errnokey; }
-
-   /* get the hash and load it as a bignum into 'e' */
-   pbits = mp_count_bits(p);
-   pbytes = (pbits+7) >> 3;
-   if (pbits > inlen*8) {
-      if ((err = mp_read_unsigned_bin(e, (unsigned char *)in, inlen)) != CRYPT_OK)    { goto errnokey; }
-   }
-   else if (pbits % 8 == 0) {
-      if ((err = mp_read_unsigned_bin(e, (unsigned char *)in, pbytes)) != CRYPT_OK)   { goto errnokey; }
-   }
-   else {
-      shift_right = 8 - pbits % 8;
-      for (i=0, ch=0; i<pbytes; i++) {
-        buf[i] = ch;
-        ch = (in[i] << (8-shift_right));
-        buf[i] = buf[i] ^ (in[i] >> shift_right);
-      }
-      if ((err = mp_read_unsigned_bin(e, (unsigned char *)buf, pbytes)) != CRYPT_OK)  { goto errnokey; }
-   }
-
-   /* make up a key and export the public copy */
-   do {
-      if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) {
-         goto errnokey;
-      }
-
-      /* find r = x1 mod n */
-      if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK)                          { goto error; }
-
-      if (mp_iszero(r) == LTC_MP_YES) {
-         ecc_free(&pubkey);
-      } else {
-         if ((err = rand_bn_upto(b, p, prng, wprng)) != CRYPT_OK)          { goto error; } /* b = blinding value */
-         /* find s = (e + xr)/k */
-         if ((err = mp_mulmod(pubkey.k, b, p, pubkey.k)) != CRYPT_OK)      { goto error; } /* k = kb */
-         if ((err = mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK)         { goto error; } /* k = 1/kb */
-         if ((err = mp_mulmod(key->k, r, p, s)) != CRYPT_OK)               { goto error; } /* s = xr */
-         if ((err = mp_mulmod(pubkey.k, s, p, s)) != CRYPT_OK)             { goto error; } /* s = xr/kb */
-         if ((err = mp_mulmod(pubkey.k, e, p, e)) != CRYPT_OK)             { goto error; } /* e = e/kb */
-         if ((err = mp_add(e, s, s)) != CRYPT_OK)                          { goto error; } /* s = e/kb + xr/kb */
-         if ((err = mp_mulmod(s, b, p, s)) != CRYPT_OK)                    { goto error; } /* s = b(e/kb + xr/kb) = (e + xr)/k */
-         ecc_free(&pubkey);
-         if (mp_iszero(s) == LTC_MP_NO) {
-            break;
-         }
-      }
-   } while (--max_iterations > 0);
-
-   if (max_iterations == 0) {
-      goto errnokey;
-   }
-
-   if (sigformat == 1) {
-      /* RFC7518 format */
-      if (*outlen < 2*pbytes) { err = CRYPT_MEM; goto errnokey; }
-      zeromem(out, 2*pbytes);
-      i = mp_unsigned_bin_size(r);
-      if ((err = mp_to_unsigned_bin(r, out + (pbytes - i)))   != CRYPT_OK) { goto errnokey; }
-      i = mp_unsigned_bin_size(s);
-      if ((err = mp_to_unsigned_bin(s, out + (2*pbytes - i))) != CRYPT_OK) { goto errnokey; }
-      *outlen = 2*pbytes;
-      err = CRYPT_OK;
-   }
-   else {
-      /* store as ASN.1 SEQUENCE { r, s -- integer } */
-      err = der_encode_sequence_multi(out, outlen,
-                               LTC_ASN1_INTEGER, 1UL, r,
-                               LTC_ASN1_INTEGER, 1UL, s,
-                               LTC_ASN1_EOL, 0UL, NULL);
-   }
-   goto errnokey;
-error:
-   ecc_free(&pubkey);
-errnokey:
-   mp_clear_multi(r, s, p, e, b, NULL);
-   return err;
-}
-
-/**
-  Sign a message digest
-  @param in        The message digest to sign
-  @param inlen     The length of the digest
-  @param out       [out] The destination for the signature
-  @param outlen    [in/out] The max size and resulting size of the signature
-  @param prng      An active PRNG state
-  @param wprng     The index of the PRNG you wish to use
-  @param key       A private ECC key
-  @return CRYPT_OK if successful
-*/
-int ecc_sign_hash(const unsigned char *in,  unsigned long inlen,
-                        unsigned char *out, unsigned long *outlen,
-                        prng_state *prng, int wprng, ecc_key *key)
-{
-   return _ecc_sign_hash(in, inlen, out, outlen, prng, wprng, key, 0);
-}
-
-/**
-  Sign a message digest in RFC7518 format
-  @param in        The message digest to sign
-  @param inlen     The length of the digest
-  @param out       [out] The destination for the signature
-  @param outlen    [in/out] The max size and resulting size of the signature
-  @param prng      An active PRNG state
-  @param wprng     The index of the PRNG you wish to use
-  @param key       A private ECC key
-  @return CRYPT_OK if successful
-*/
-int ecc_sign_hash_rfc7518(const unsigned char *in,  unsigned long inlen,
-                                unsigned char *out, unsigned long *outlen,
-                                prng_state *prng, int wprng, ecc_key *key)
-{
-   return _ecc_sign_hash(in, inlen, out, outlen, prng, wprng, key, 1);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_sizes.c b/libs/tomcrypt/src/pk/ecc/ecc_sizes.c
deleted file mode 100644
index 217292fb69fc..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_sizes.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ecc_sizes.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-void ecc_sizes(int *low, int *high)
-{
- int i;
- LTC_ARGCHKVD(low  != NULL);
- LTC_ARGCHKVD(high != NULL);
-
- *low = INT_MAX;
- *high = 0;
- for (i = 0; ltc_ecc_sets[i].size != 0; i++) {
-     if (ltc_ecc_sets[i].size < *low)  {
-        *low  = ltc_ecc_sets[i].size;
-     }
-     if (ltc_ecc_sets[i].size > *high) {
-        *high = ltc_ecc_sets[i].size;
-     }
- }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ecc_verify_hash.c b/libs/tomcrypt/src/pk/ecc/ecc_verify_hash.c
deleted file mode 100644
index d3779c071376..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ecc_verify_hash.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_MECC
-
-/**
-  @file ecc_verify_hash.c
-  ECC Crypto, Tom St Denis
-*/
-
-static int _ecc_verify_hash(const unsigned char *sig,  unsigned long siglen,
-                            const unsigned char *hash, unsigned long hashlen,
-                            int *stat, ecc_key *key, int sigformat)
-{
-   ecc_point    *mG, *mQ;
-   void          *r, *s, *v, *w, *u1, *u2, *e, *p, *m;
-   void          *mp;
-   int           err;
-   unsigned long pbits, pbytes, i, shift_right;
-   unsigned char ch, buf[MAXBLOCKSIZE];
-
-   LTC_ARGCHK(sig  != NULL);
-   LTC_ARGCHK(hash != NULL);
-   LTC_ARGCHK(stat != NULL);
-   LTC_ARGCHK(key  != NULL);
-
-   /* default to invalid signature */
-   *stat = 0;
-   mp    = NULL;
-
-   /* is the IDX valid ?  */
-   if (ltc_ecc_is_valid_idx(key->idx) != 1) {
-      return CRYPT_PK_INVALID_TYPE;
-   }
-
-   /* allocate ints */
-   if ((err = mp_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL)) != CRYPT_OK) {
-      return CRYPT_MEM;
-   }
-
-   /* allocate points */
-   mG = ltc_ecc_new_point();
-   mQ = ltc_ecc_new_point();
-   if (mQ  == NULL || mG == NULL) {
-      err = CRYPT_MEM;
-      goto error;
-   }
-
-   if (sigformat == 1) {
-      /* RFC7518 format */
-      if ((siglen % 2) == 1) {
-         err = CRYPT_INVALID_PACKET;
-         goto error;
-      }
-      i = siglen / 2;
-      if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig,   i)) != CRYPT_OK)                       { goto error; }
-      if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+i, i)) != CRYPT_OK)                       { goto error; }
-   }
-   else {
-      /* ASN.1 format */
-      if ((err = der_decode_sequence_multi(sig, siglen,
-                                     LTC_ASN1_INTEGER, 1UL, r,
-                                     LTC_ASN1_INTEGER, 1UL, s,
-                                     LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK)                             { goto error; }
-   }
-
-   /* get the order */
-   if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK)                                { goto error; }
-
-   /* get the modulus */
-   if ((err = mp_read_radix(m, (char *)key->dp->prime, 16)) != CRYPT_OK)                                { goto error; }
-
-   /* check for zero */
-   if (mp_iszero(r) || mp_iszero(s) || mp_cmp(r, p) != LTC_MP_LT || mp_cmp(s, p) != LTC_MP_LT) {
-      err = CRYPT_INVALID_PACKET;
-      goto error;
-   }
-
-   /* read hash - truncate if needed */
-   pbits = mp_count_bits(p);
-   pbytes = (pbits+7) >> 3;
-   if (pbits > hashlen*8) {
-      if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, hashlen)) != CRYPT_OK)                  { goto error; }
-   }
-   else if (pbits % 8 == 0) {
-      if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, pbytes)) != CRYPT_OK)                   { goto error; }
-   }
-   else {
-      shift_right = 8 - pbits % 8;
-      for (i=0, ch=0; i<pbytes; i++) {
-        buf[i] = ch;
-        ch = (hash[i] << (8-shift_right));
-        buf[i] = buf[i] ^ (hash[i] >> shift_right);
-      }
-      if ((err = mp_read_unsigned_bin(e, (unsigned char *)buf, pbytes)) != CRYPT_OK)                    { goto error; }
-   }
-
-   /*  w  = s^-1 mod n */
-   if ((err = mp_invmod(s, p, w)) != CRYPT_OK)                                                          { goto error; }
-
-   /* u1 = ew */
-   if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK)                                                      { goto error; }
-
-   /* u2 = rw */
-   if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK)                                                      { goto error; }
-
-   /* find mG and mQ */
-   if ((err = mp_read_radix(mG->x, (char *)key->dp->Gx, 16)) != CRYPT_OK)                               { goto error; }
-   if ((err = mp_read_radix(mG->y, (char *)key->dp->Gy, 16)) != CRYPT_OK)                               { goto error; }
-   if ((err = mp_set(mG->z, 1)) != CRYPT_OK)                                                            { goto error; }
-
-   if ((err = mp_copy(key->pubkey.x, mQ->x)) != CRYPT_OK)                                               { goto error; }
-   if ((err = mp_copy(key->pubkey.y, mQ->y)) != CRYPT_OK)                                               { goto error; }
-   if ((err = mp_copy(key->pubkey.z, mQ->z)) != CRYPT_OK)                                               { goto error; }
-
-   /* compute u1*mG + u2*mQ = mG */
-   if (ltc_mp.ecc_mul2add == NULL) {
-      if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, m, 0)) != CRYPT_OK)                                       { goto error; }
-      if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0)) != CRYPT_OK)                                       { goto error; }
-
-      /* find the montgomery mp */
-      if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK)                                              { goto error; }
-
-      /* add them */
-      if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp)) != CRYPT_OK)                                      { goto error; }
-
-      /* reduce */
-      if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK)                                                { goto error; }
-   } else {
-      /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */
-      if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m)) != CRYPT_OK)                                { goto error; }
-   }
-
-   /* v = X_x1 mod n */
-   if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK)                                                         { goto error; }
-
-   /* does v == r */
-   if (mp_cmp(v, r) == LTC_MP_EQ) {
-      *stat = 1;
-   }
-
-   /* clear up and return */
-   err = CRYPT_OK;
-error:
-   ltc_ecc_del_point(mG);
-   ltc_ecc_del_point(mQ);
-   mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL);
-   if (mp != NULL) {
-      mp_montgomery_free(mp);
-   }
-   return err;
-}
-
-/**
-   Verify an ECC signature
-   @param sig         The signature to verify
-   @param siglen      The length of the signature (octets)
-   @param hash        The hash (message digest) that was signed
-   @param hashlen     The length of the hash (octets)
-   @param stat        Result of signature, 1==valid, 0==invalid
-   @param key         The corresponding public ECC key
-   @return CRYPT_OK if successful (even if the signature is not valid)
-*/
-int ecc_verify_hash(const unsigned char *sig,  unsigned long siglen,
-                    const unsigned char *hash, unsigned long hashlen,
-                    int *stat, ecc_key *key)
-{
-   return _ecc_verify_hash(sig, siglen, hash, hashlen, stat, key, 0);
-}
-
-/**
-   Verify an ECC signature in RFC7518 format
-   @param sig         The signature to verify
-   @param siglen      The length of the signature (octets)
-   @param hash        The hash (message digest) that was signed
-   @param hashlen     The length of the hash (octets)
-   @param stat        Result of signature, 1==valid, 0==invalid
-   @param key         The corresponding public ECC key
-   @return CRYPT_OK if successful (even if the signature is not valid)
-*/
-int ecc_verify_hash_rfc7518(const unsigned char *sig,  unsigned long siglen,
-                            const unsigned char *hash, unsigned long hashlen,
-                            int *stat, ecc_key *key)
-{
-   return _ecc_verify_hash(sig, siglen, hash, hashlen, stat, key, 1);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c
deleted file mode 100644
index 0bc7142bad8a..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_is_valid_idx.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/** Returns whether an ECC idx is valid or not
-  @param n   The idx number to check
-  @return 1 if valid, 0 if not
-*/
-int ltc_ecc_is_valid_idx(int n)
-{
-   int x;
-
-   for (x = 0; ltc_ecc_sets[x].size != 0; x++);
-   /* -1 is a valid index --- indicating that the domain params were supplied by the user */
-   if ((n >= -1) && (n < x)) {
-      return 1;
-   }
-   return 0;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_map.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_map.c
deleted file mode 100644
index 7f953964cb77..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_map.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_map.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-  Map a projective jacbobian point back to affine space
-  @param P        [in/out] The point to map
-  @param modulus  The modulus of the field the ECC curve is in
-  @param mp       The "b" value from montgomery_setup()
-  @return CRYPT_OK on success
-*/
-int ltc_ecc_map(ecc_point *P, void *modulus, void *mp)
-{
-   void *t1, *t2;
-   int   err;
-
-   LTC_ARGCHK(P       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-   LTC_ARGCHK(mp      != NULL);
-
-   if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* first map z back to normal */
-   if ((err = mp_montgomery_reduce(P->z, modulus, mp)) != CRYPT_OK)           { goto done; }
-
-   /* get 1/z */
-   if ((err = mp_invmod(P->z, modulus, t1)) != CRYPT_OK)                      { goto done; }
-
-   /* get 1/z^2 and 1/z^3 */
-   if ((err = mp_sqr(t1, t2)) != CRYPT_OK)                                    { goto done; }
-   if ((err = mp_mod(t2, modulus, t2)) != CRYPT_OK)                           { goto done; }
-   if ((err = mp_mul(t1, t2, t1)) != CRYPT_OK)                                { goto done; }
-   if ((err = mp_mod(t1, modulus, t1)) != CRYPT_OK)                           { goto done; }
-
-   /* multiply against x/y */
-   if ((err = mp_mul(P->x, t2, P->x)) != CRYPT_OK)                            { goto done; }
-   if ((err = mp_montgomery_reduce(P->x, modulus, mp)) != CRYPT_OK)           { goto done; }
-   if ((err = mp_mul(P->y, t1, P->y)) != CRYPT_OK)                            { goto done; }
-   if ((err = mp_montgomery_reduce(P->y, modulus, mp)) != CRYPT_OK)           { goto done; }
-   if ((err = mp_set(P->z, 1)) != CRYPT_OK)                                   { goto done; }
-
-   err = CRYPT_OK;
-done:
-   mp_clear_multi(t1, t2, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_mul2add.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_mul2add.c
deleted file mode 100644
index bbbca8133d7a..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_mul2add.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_mul2add.c
-  ECC Crypto, Shamir's Trick, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-#ifdef LTC_ECC_SHAMIR
-
-/** Computes kA*A + kB*B = C using Shamir's Trick
-  @param A        First point to multiply
-  @param kA       What to multiple A by
-  @param B        Second point to multiply
-  @param kB       What to multiple B by
-  @param C        [out] Destination point (can overlap with A or B
-  @param modulus  Modulus for curve
-  @return CRYPT_OK on success
-*/
-int ltc_ecc_mul2add(ecc_point *A, void *kA,
-                    ecc_point *B, void *kB,
-                    ecc_point *C,
-                         void *modulus)
-{
-  ecc_point     *precomp[16];
-  unsigned       bitbufA, bitbufB, lenA, lenB, len, x, y, nA, nB, nibble;
-  unsigned char *tA, *tB;
-  int            err, first;
-  void          *mp, *mu;
-
-  /* argchks */
-  LTC_ARGCHK(A       != NULL);
-  LTC_ARGCHK(B       != NULL);
-  LTC_ARGCHK(C       != NULL);
-  LTC_ARGCHK(kA      != NULL);
-  LTC_ARGCHK(kB      != NULL);
-  LTC_ARGCHK(modulus != NULL);
-
-  /* allocate memory */
-  tA = XCALLOC(1, ECC_BUF_SIZE);
-  if (tA == NULL) {
-     return CRYPT_MEM;
-  }
-  tB = XCALLOC(1, ECC_BUF_SIZE);
-  if (tB == NULL) {
-     XFREE(tA);
-     return CRYPT_MEM;
-  }
-
-  /* get sizes */
-  lenA = mp_unsigned_bin_size(kA);
-  lenB = mp_unsigned_bin_size(kB);
-  len  = MAX(lenA, lenB);
-
-  /* sanity check */
-  if ((lenA > ECC_BUF_SIZE) || (lenB > ECC_BUF_SIZE)) {
-     err = CRYPT_INVALID_ARG;
-     goto ERR_T;
-  }
-
-  /* extract and justify kA */
-  mp_to_unsigned_bin(kA, (len - lenA) + tA);
-
-  /* extract and justify kB */
-  mp_to_unsigned_bin(kB, (len - lenB) + tB);
-
-  /* allocate the table */
-  for (x = 0; x < 16; x++) {
-     precomp[x] = ltc_ecc_new_point();
-     if (precomp[x] == NULL) {
-         for (y = 0; y < x; ++y) {
-            ltc_ecc_del_point(precomp[y]);
-         }
-         err = CRYPT_MEM;
-         goto ERR_T;
-     }
-  }
-
-  /* init montgomery reduction */
-  if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
-      goto ERR_P;
-  }
-  if ((err = mp_init(&mu)) != CRYPT_OK) {
-      goto ERR_MP;
-  }
-  if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-      goto ERR_MU;
-  }
-
-  /* copy ones ... */
-  if ((err = mp_mulmod(A->x, mu, modulus, precomp[1]->x)) != CRYPT_OK)                                         { goto ERR_MU; }
-  if ((err = mp_mulmod(A->y, mu, modulus, precomp[1]->y)) != CRYPT_OK)                                         { goto ERR_MU; }
-  if ((err = mp_mulmod(A->z, mu, modulus, precomp[1]->z)) != CRYPT_OK)                                         { goto ERR_MU; }
-
-  if ((err = mp_mulmod(B->x, mu, modulus, precomp[1<<2]->x)) != CRYPT_OK)                                      { goto ERR_MU; }
-  if ((err = mp_mulmod(B->y, mu, modulus, precomp[1<<2]->y)) != CRYPT_OK)                                      { goto ERR_MU; }
-  if ((err = mp_mulmod(B->z, mu, modulus, precomp[1<<2]->z)) != CRYPT_OK)                                      { goto ERR_MU; }
-
-  /* precomp [i,0](A + B) table */
-  if ((err = ltc_mp.ecc_ptdbl(precomp[1], precomp[2], modulus, mp)) != CRYPT_OK)                               { goto ERR_MU; }
-  if ((err = ltc_mp.ecc_ptadd(precomp[1], precomp[2], precomp[3], modulus, mp)) != CRYPT_OK)                   { goto ERR_MU; }
-
-  /* precomp [0,i](A + B) table */
-  if ((err = ltc_mp.ecc_ptdbl(precomp[1<<2], precomp[2<<2], modulus, mp)) != CRYPT_OK)                         { goto ERR_MU; }
-  if ((err = ltc_mp.ecc_ptadd(precomp[1<<2], precomp[2<<2], precomp[3<<2], modulus, mp)) != CRYPT_OK)          { goto ERR_MU; }
-
-  /* precomp [i,j](A + B) table (i != 0, j != 0) */
-  for (x = 1; x < 4; x++) {
-     for (y = 1; y < 4; y++) {
-        if ((err = ltc_mp.ecc_ptadd(precomp[x], precomp[(y<<2)], precomp[x+(y<<2)], modulus, mp)) != CRYPT_OK) { goto ERR_MU; }
-     }
-  }
-
-  nibble  = 3;
-  first   = 1;
-  bitbufA = tA[0];
-  bitbufB = tB[0];
-
-  /* for every byte of the multiplicands */
-  for (x = 0;; ) {
-     /* grab a nibble */
-     if (++nibble == 4) {
-        if (x == len) break;
-        bitbufA = tA[x];
-        bitbufB = tB[x];
-        nibble  = 0;
-        ++x;
-     }
-
-     /* extract two bits from both, shift/update */
-     nA = (bitbufA >> 6) & 0x03;
-     nB = (bitbufB >> 6) & 0x03;
-     bitbufA = (bitbufA << 2) & 0xFF;
-     bitbufB = (bitbufB << 2) & 0xFF;
-
-     /* if both zero, if first, continue */
-     if ((nA == 0) && (nB == 0) && (first == 1)) {
-        continue;
-     }
-
-     /* double twice, only if this isn't the first */
-     if (first == 0) {
-        /* double twice */
-        if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK)                  { goto ERR_MU; }
-        if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK)                  { goto ERR_MU; }
-     }
-
-     /* if not both zero */
-     if ((nA != 0) || (nB != 0)) {
-        if (first == 1) {
-           /* if first, copy from table */
-           first = 0;
-           if ((err = mp_copy(precomp[nA + (nB<<2)]->x, C->x)) != CRYPT_OK)           { goto ERR_MU; }
-           if ((err = mp_copy(precomp[nA + (nB<<2)]->y, C->y)) != CRYPT_OK)           { goto ERR_MU; }
-           if ((err = mp_copy(precomp[nA + (nB<<2)]->z, C->z)) != CRYPT_OK)           { goto ERR_MU; }
-        } else {
-           /* if not first, add from table */
-           if ((err = ltc_mp.ecc_ptadd(C, precomp[nA + (nB<<2)], C, modulus, mp)) != CRYPT_OK) { goto ERR_MU; }
-        }
-     }
-  }
-
-  /* reduce to affine */
-  err = ltc_ecc_map(C, modulus, mp);
-
-  /* clean up */
-ERR_MU:
-   mp_clear(mu);
-ERR_MP:
-   mp_montgomery_free(mp);
-ERR_P:
-   for (x = 0; x < 16; x++) {
-       ltc_ecc_del_point(precomp[x]);
-   }
-ERR_T:
-#ifdef LTC_CLEAN_STACK
-   zeromem(tA, ECC_BUF_SIZE);
-   zeromem(tB, ECC_BUF_SIZE);
-#endif
-   XFREE(tA);
-   XFREE(tB);
-
-   return err;
-}
-
-#endif
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_mulmod.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_mulmod.c
deleted file mode 100644
index 8a64d788c100..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_mulmod.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_mulmod.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-#ifndef LTC_ECC_TIMING_RESISTANT
-
-/* size of sliding window, don't change this! */
-#define WINSIZE 4
-
-/**
-   Perform a point multiplication
-   @param k    The scalar to multiply by
-   @param G    The base point
-   @param R    [out] Destination for kG
-   @param modulus  The modulus of the field the ECC curve is in
-   @param map      Boolean whether to map back to affine or not (1==map, 0 == leave in projective)
-   @return CRYPT_OK on success
-*/
-int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map)
-{
-   ecc_point *tG, *M[8];
-   int        i, j, err;
-   void       *mu, *mp;
-   ltc_mp_digit buf;
-   int        first, bitbuf, bitcpy, bitcnt, mode, digidx;
-
-   LTC_ARGCHK(k       != NULL);
-   LTC_ARGCHK(G       != NULL);
-   LTC_ARGCHK(R       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-
-   /* init montgomery reduction */
-   if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = mp_init(&mu)) != CRYPT_OK) {
-      mp_montgomery_free(mp);
-      return err;
-   }
-   if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-      mp_montgomery_free(mp);
-      mp_clear(mu);
-      return err;
-   }
-
-  /* alloc ram for window temps */
-  for (i = 0; i < 8; i++) {
-      M[i] = ltc_ecc_new_point();
-      if (M[i] == NULL) {
-         for (j = 0; j < i; j++) {
-             ltc_ecc_del_point(M[j]);
-         }
-         mp_montgomery_free(mp);
-         mp_clear(mu);
-         return CRYPT_MEM;
-      }
-  }
-
-   /* make a copy of G incase R==G */
-   tG = ltc_ecc_new_point();
-   if (tG == NULL)                                                                   { err = CRYPT_MEM; goto done; }
-
-   /* tG = G  and convert to montgomery */
-   if (mp_cmp_d(mu, 1) == LTC_MP_EQ) {
-      if ((err = mp_copy(G->x, tG->x)) != CRYPT_OK)                                  { goto done; }
-      if ((err = mp_copy(G->y, tG->y)) != CRYPT_OK)                                  { goto done; }
-      if ((err = mp_copy(G->z, tG->z)) != CRYPT_OK)                                  { goto done; }
-   } else {
-      if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK)                   { goto done; }
-      if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK)                   { goto done; }
-      if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK)                   { goto done; }
-   }
-   mp_clear(mu);
-   mu = NULL;
-
-   /* calc the M tab, which holds kG for k==8..15 */
-   /* M[0] == 8G */
-   if ((err = ltc_mp.ecc_ptdbl(tG, M[0], modulus, mp)) != CRYPT_OK)                 { goto done; }
-   if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK)               { goto done; }
-   if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK)               { goto done; }
-
-   /* now find (8+k)G for k=1..7 */
-   for (j = 9; j < 16; j++) {
-       if ((err = ltc_mp.ecc_ptadd(M[j-9], tG, M[j-8], modulus, mp)) != CRYPT_OK)   { goto done; }
-   }
-
-   /* setup sliding window */
-   mode   = 0;
-   bitcnt = 1;
-   buf    = 0;
-   digidx = mp_get_digit_count(k) - 1;
-   bitcpy = bitbuf = 0;
-   first  = 1;
-
-   /* perform ops */
-   for (;;) {
-     /* grab next digit as required */
-     if (--bitcnt == 0) {
-       if (digidx == -1) {
-          break;
-       }
-       buf    = mp_get_digit(k, digidx);
-       bitcnt = (int) ltc_mp.bits_per_digit;
-       --digidx;
-     }
-
-     /* grab the next msb from the ltiplicand */
-     i = (buf >> (ltc_mp.bits_per_digit - 1)) & 1;
-     buf <<= 1;
-
-     /* skip leading zero bits */
-     if (mode == 0 && i == 0) {
-        continue;
-     }
-
-     /* if the bit is zero and mode == 1 then we double */
-     if (mode == 1 && i == 0) {
-        if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK)                 { goto done; }
-        continue;
-     }
-
-     /* else we add it to the window */
-     bitbuf |= (i << (WINSIZE - ++bitcpy));
-     mode = 2;
-
-     if (bitcpy == WINSIZE) {
-       /* if this is the first window we do a simple copy */
-       if (first == 1) {
-          /* R = kG [k = first window] */
-          if ((err = mp_copy(M[bitbuf-8]->x, R->x)) != CRYPT_OK)                     { goto done; }
-          if ((err = mp_copy(M[bitbuf-8]->y, R->y)) != CRYPT_OK)                     { goto done; }
-          if ((err = mp_copy(M[bitbuf-8]->z, R->z)) != CRYPT_OK)                     { goto done; }
-          first = 0;
-       } else {
-         /* normal window */
-         /* ok window is filled so double as required and add  */
-         /* double first */
-         for (j = 0; j < WINSIZE; j++) {
-           if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK)              { goto done; }
-         }
-
-         /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */
-         if ((err = ltc_mp.ecc_ptadd(R, M[bitbuf-8], R, modulus, mp)) != CRYPT_OK)   { goto done; }
-       }
-       /* empty window and reset */
-       bitcpy = bitbuf = 0;
-       mode = 1;
-    }
-  }
-
-   /* if bits remain then double/add */
-   if (mode == 2 && bitcpy > 0) {
-     /* double then add */
-     for (j = 0; j < bitcpy; j++) {
-       /* only double if we have had at least one add first */
-       if (first == 0) {
-          if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK)              { goto done; }
-       }
-
-       bitbuf <<= 1;
-       if ((bitbuf & (1 << WINSIZE)) != 0) {
-         if (first == 1){
-            /* first add, so copy */
-            if ((err = mp_copy(tG->x, R->x)) != CRYPT_OK)                           { goto done; }
-            if ((err = mp_copy(tG->y, R->y)) != CRYPT_OK)                           { goto done; }
-            if ((err = mp_copy(tG->z, R->z)) != CRYPT_OK)                           { goto done; }
-            first = 0;
-         } else {
-            /* then add */
-            if ((err = ltc_mp.ecc_ptadd(R, tG, R, modulus, mp)) != CRYPT_OK)        { goto done; }
-         }
-       }
-     }
-   }
-
-   /* map R back from projective space */
-   if (map) {
-      err = ltc_ecc_map(R, modulus, mp);
-   } else {
-      err = CRYPT_OK;
-   }
-done:
-   if (mu != NULL) {
-      mp_clear(mu);
-   }
-   mp_montgomery_free(mp);
-   ltc_ecc_del_point(tG);
-   for (i = 0; i < 8; i++) {
-       ltc_ecc_del_point(M[i]);
-   }
-   return err;
-}
-
-#endif
-
-#undef WINSIZE
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c
deleted file mode 100644
index 299ef2087f28..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_mulmod_timing.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-#ifdef LTC_ECC_TIMING_RESISTANT
-
-/**
-   Perform a point multiplication  (timing resistant)
-   @param k    The scalar to multiply by
-   @param G    The base point
-   @param R    [out] Destination for kG
-   @param modulus  The modulus of the field the ECC curve is in
-   @param map      Boolean whether to map back to affine or not (1==map, 0 == leave in projective)
-   @return CRYPT_OK on success
-*/
-int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map)
-{
-   ecc_point *tG, *M[3];
-   int        i, j, err;
-   void       *mu, *mp;
-   ltc_mp_digit buf;
-   int        bitcnt, mode, digidx;
-
-   LTC_ARGCHK(k       != NULL);
-   LTC_ARGCHK(G       != NULL);
-   LTC_ARGCHK(R       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-
-   /* init montgomery reduction */
-   if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = mp_init(&mu)) != CRYPT_OK) {
-      mp_montgomery_free(mp);
-      return err;
-   }
-   if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
-      mp_clear(mu);
-      mp_montgomery_free(mp);
-      return err;
-   }
-
-   /* alloc ram for window temps */
-   for (i = 0; i < 3; i++) {
-      M[i] = ltc_ecc_new_point();
-      if (M[i] == NULL) {
-         for (j = 0; j < i; j++) {
-             ltc_ecc_del_point(M[j]);
-         }
-         mp_clear(mu);
-         mp_montgomery_free(mp);
-         return CRYPT_MEM;
-      }
-   }
-
-   /* make a copy of G incase R==G */
-   tG = ltc_ecc_new_point();
-   if (tG == NULL)                                                                   { err = CRYPT_MEM; goto done; }
-
-   /* tG = G  and convert to montgomery */
-   if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK)                      { goto done; }
-   if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK)                      { goto done; }
-   if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK)                      { goto done; }
-   mp_clear(mu);
-   mu = NULL;
-
-   /* calc the M tab */
-   /* M[0] == G */
-   if ((err = mp_copy(tG->x, M[0]->x)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_copy(tG->y, M[0]->y)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_copy(tG->z, M[0]->z)) != CRYPT_OK)                                  { goto done; }
-   /* M[1] == 2G */
-   if ((err = ltc_mp.ecc_ptdbl(tG, M[1], modulus, mp)) != CRYPT_OK)                  { goto done; }
-
-   /* setup sliding window */
-   mode   = 0;
-   bitcnt = 1;
-   buf    = 0;
-   digidx = mp_get_digit_count(k) - 1;
-
-   /* perform ops */
-   for (;;) {
-     /* grab next digit as required */
-      if (--bitcnt == 0) {
-         if (digidx == -1) {
-            break;
-         }
-         buf    = mp_get_digit(k, digidx);
-         bitcnt = (int) MP_DIGIT_BIT;
-         --digidx;
-      }
-
-      /* grab the next msb from the ltiplicand */
-      i = (buf >> (MP_DIGIT_BIT - 1)) & 1;
-      buf <<= 1;
-
-      if (mode == 0 && i == 0) {
-         /* dummy operations */
-         if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK)    { goto done; }
-         if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK)          { goto done; }
-         continue;
-      }
-
-      if (mode == 0 && i == 1) {
-         mode = 1;
-         /* dummy operations */
-         if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK)    { goto done; }
-         if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK)          { goto done; }
-         continue;
-      }
-
-      if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[i^1], modulus, mp)) != CRYPT_OK)     { goto done; }
-      if ((err = ltc_mp.ecc_ptdbl(M[i], M[i], modulus, mp)) != CRYPT_OK)             { goto done; }
-   }
-
-   /* copy result out */
-   if ((err = mp_copy(M[0]->x, R->x)) != CRYPT_OK)                                   { goto done; }
-   if ((err = mp_copy(M[0]->y, R->y)) != CRYPT_OK)                                   { goto done; }
-   if ((err = mp_copy(M[0]->z, R->z)) != CRYPT_OK)                                   { goto done; }
-
-   /* map R back from projective space */
-   if (map) {
-      err = ltc_ecc_map(R, modulus, mp);
-   } else {
-      err = CRYPT_OK;
-   }
-done:
-   if (mu != NULL) {
-      mp_clear(mu);
-   }
-   mp_montgomery_free(mp);
-   ltc_ecc_del_point(tG);
-   for (i = 0; i < 3; i++) {
-       ltc_ecc_del_point(M[i]);
-   }
-   return err;
-}
-
-#endif
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_points.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_points.c
deleted file mode 100644
index eadc9e8668ca..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_points.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_points.c
-  ECC Crypto, Tom St Denis
-*/
-
-#ifdef LTC_MECC
-
-/**
-   Allocate a new ECC point
-   @return A newly allocated point or NULL on error
-*/
-ecc_point *ltc_ecc_new_point(void)
-{
-   ecc_point *p;
-   p = XCALLOC(1, sizeof(*p));
-   if (p == NULL) {
-      return NULL;
-   }
-   if (mp_init_multi(&p->x, &p->y, &p->z, NULL) != CRYPT_OK) {
-      XFREE(p);
-      return NULL;
-   }
-   return p;
-}
-
-/** Free an ECC point from memory
-  @param p   The point to free
-*/
-void ltc_ecc_del_point(ecc_point *p)
-{
-   /* prevents free'ing null arguments */
-   if (p != NULL) {
-      mp_clear_multi(p->x, p->y, p->z, NULL); /* note: p->z may be NULL but that's ok with this function anyways */
-      XFREE(p);
-   }
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c
deleted file mode 100644
index d2813194f571..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_projective_add_point.c
-  ECC Crypto, Tom St Denis
-*/
-
-#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_DESC))
-
-/**
-   Add two ECC points
-   @param P        The point to add
-   @param Q        The point to add
-   @param R        [out] The destination of the double
-   @param modulus  The modulus of the field the ECC curve is in
-   @param mp       The "b" value from montgomery_setup()
-   @return CRYPT_OK on success
-*/
-int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp)
-{
-   void  *t1, *t2, *x, *y, *z;
-   int    err;
-
-   LTC_ARGCHK(P       != NULL);
-   LTC_ARGCHK(Q       != NULL);
-   LTC_ARGCHK(R       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-   LTC_ARGCHK(mp      != NULL);
-
-   if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* should we dbl instead? */
-   if ((err = mp_sub(modulus, Q->y, t1)) != CRYPT_OK)                          { goto done; }
-
-   if ( (mp_cmp(P->x, Q->x) == LTC_MP_EQ) &&
-        (Q->z != NULL && mp_cmp(P->z, Q->z) == LTC_MP_EQ) &&
-        (mp_cmp(P->y, Q->y) == LTC_MP_EQ || mp_cmp(P->y, t1) == LTC_MP_EQ)) {
-        mp_clear_multi(t1, t2, x, y, z, NULL);
-        return ltc_ecc_projective_dbl_point(P, R, modulus, mp);
-   }
-
-   if ((err = mp_copy(P->x, x)) != CRYPT_OK)                                   { goto done; }
-   if ((err = mp_copy(P->y, y)) != CRYPT_OK)                                   { goto done; }
-   if ((err = mp_copy(P->z, z)) != CRYPT_OK)                                   { goto done; }
-
-   /* if Z is one then these are no-operations */
-   if (Q->z != NULL) {
-      /* T1 = Z' * Z' */
-      if ((err = mp_sqr(Q->z, t1)) != CRYPT_OK)                                { goto done; }
-      if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)           { goto done; }
-      /* X = X * T1 */
-      if ((err = mp_mul(t1, x, x)) != CRYPT_OK)                                { goto done; }
-      if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK)            { goto done; }
-      /* T1 = Z' * T1 */
-      if ((err = mp_mul(Q->z, t1, t1)) != CRYPT_OK)                            { goto done; }
-      if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)           { goto done; }
-      /* Y = Y * T1 */
-      if ((err = mp_mul(t1, y, y)) != CRYPT_OK)                                { goto done; }
-      if ((err = mp_montgomery_reduce(y, modulus, mp)) != CRYPT_OK)            { goto done; }
-   }
-
-   /* T1 = Z*Z */
-   if ((err = mp_sqr(z, t1)) != CRYPT_OK)                                      { goto done; }
-   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; }
-   /* T2 = X' * T1 */
-   if ((err = mp_mul(Q->x, t1, t2)) != CRYPT_OK)                               { goto done; }
-   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)              { goto done; }
-   /* T1 = Z * T1 */
-   if ((err = mp_mul(z, t1, t1)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; }
-   /* T1 = Y' * T1 */
-   if ((err = mp_mul(Q->y, t1, t1)) != CRYPT_OK)                               { goto done; }
-   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; }
-
-   /* Y = Y - T1 */
-   if ((err = mp_sub(y, t1, y)) != CRYPT_OK)                                   { goto done; }
-   if (mp_cmp_d(y, 0) == LTC_MP_LT) {
-      if ((err = mp_add(y, modulus, y)) != CRYPT_OK)                           { goto done; }
-   }
-   /* T1 = 2T1 */
-   if ((err = mp_add(t1, t1, t1)) != CRYPT_OK)                                 { goto done; }
-   if (mp_cmp(t1, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                         { goto done; }
-   }
-   /* T1 = Y + T1 */
-   if ((err = mp_add(t1, y, t1)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp(t1, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                         { goto done; }
-   }
-   /* X = X - T2 */
-   if ((err = mp_sub(x, t2, x)) != CRYPT_OK)                                   { goto done; }
-   if (mp_cmp_d(x, 0) == LTC_MP_LT) {
-      if ((err = mp_add(x, modulus, x)) != CRYPT_OK)                           { goto done; }
-   }
-   /* T2 = 2T2 */
-   if ((err = mp_add(t2, t2, t2)) != CRYPT_OK)                                 { goto done; }
-   if (mp_cmp(t2, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK)                         { goto done; }
-   }
-   /* T2 = X + T2 */
-   if ((err = mp_add(t2, x, t2)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp(t2, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK)                         { goto done; }
-   }
-
-   /* if Z' != 1 */
-   if (Q->z != NULL) {
-      /* Z = Z * Z' */
-      if ((err = mp_mul(z, Q->z, z)) != CRYPT_OK)                              { goto done; }
-      if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK)            { goto done; }
-   }
-
-   /* Z = Z * X */
-   if ((err = mp_mul(z, x, z)) != CRYPT_OK)                                    { goto done; }
-   if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK)               { goto done; }
-
-   /* T1 = T1 * X  */
-   if ((err = mp_mul(t1, x, t1)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; }
-   /* X = X * X */
-   if ((err = mp_sqr(x, x)) != CRYPT_OK)                                       { goto done; }
-   if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK)               { goto done; }
-   /* T2 = T2 * x */
-   if ((err = mp_mul(t2, x, t2)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)              { goto done; }
-   /* T1 = T1 * X  */
-   if ((err = mp_mul(t1, x, t1)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; }
-
-   /* X = Y*Y */
-   if ((err = mp_sqr(y, x)) != CRYPT_OK)                                       { goto done; }
-   if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK)               { goto done; }
-   /* X = X - T2 */
-   if ((err = mp_sub(x, t2, x)) != CRYPT_OK)                                   { goto done; }
-   if (mp_cmp_d(x, 0) == LTC_MP_LT) {
-      if ((err = mp_add(x, modulus, x)) != CRYPT_OK)                           { goto done; }
-   }
-
-   /* T2 = T2 - X */
-   if ((err = mp_sub(t2, x, t2)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp_d(t2, 0) == LTC_MP_LT) {
-      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                         { goto done; }
-   }
-   /* T2 = T2 - X */
-   if ((err = mp_sub(t2, x, t2)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp_d(t2, 0) == LTC_MP_LT) {
-      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                         { goto done; }
-   }
-   /* T2 = T2 * Y */
-   if ((err = mp_mul(t2, y, t2)) != CRYPT_OK)                                  { goto done; }
-   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)              { goto done; }
-   /* Y = T2 - T1 */
-   if ((err = mp_sub(t2, t1, y)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp_d(y, 0) == LTC_MP_LT) {
-      if ((err = mp_add(y, modulus, y)) != CRYPT_OK)                           { goto done; }
-   }
-   /* Y = Y/2 */
-   if (mp_isodd(y)) {
-      if ((err = mp_add(y, modulus, y)) != CRYPT_OK)                           { goto done; }
-   }
-   if ((err = mp_div_2(y, y)) != CRYPT_OK)                                     { goto done; }
-
-   if ((err = mp_copy(x, R->x)) != CRYPT_OK)                                   { goto done; }
-   if ((err = mp_copy(y, R->y)) != CRYPT_OK)                                   { goto done; }
-   if ((err = mp_copy(z, R->z)) != CRYPT_OK)                                   { goto done; }
-
-   err = CRYPT_OK;
-done:
-   mp_clear_multi(t1, t2, x, y, z, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c b/libs/tomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c
deleted file mode 100644
index fd7f98142c1a..000000000000
--- a/libs/tomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
- *
- * All curves taken from NIST recommendation paper of July 1999
- * Available at http://csrc.nist.gov/cryptval/dss.htm
- */
-#include "tomcrypt.h"
-
-/**
-  @file ltc_ecc_projective_dbl_point.c
-  ECC Crypto, Tom St Denis
-*/
-
-#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_DESC))
-
-/**
-   Double an ECC point
-   @param P   The point to double
-   @param R   [out] The destination of the double
-   @param modulus  The modulus of the field the ECC curve is in
-   @param mp       The "b" value from montgomery_setup()
-   @return CRYPT_OK on success
-*/
-int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp)
-{
-   void *t1, *t2;
-   int   err;
-
-   LTC_ARGCHK(P       != NULL);
-   LTC_ARGCHK(R       != NULL);
-   LTC_ARGCHK(modulus != NULL);
-   LTC_ARGCHK(mp      != NULL);
-
-   if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (P != R) {
-      if ((err = mp_copy(P->x, R->x)) != CRYPT_OK)                                { goto done; }
-      if ((err = mp_copy(P->y, R->y)) != CRYPT_OK)                                { goto done; }
-      if ((err = mp_copy(P->z, R->z)) != CRYPT_OK)                                { goto done; }
-   }
-
-   /* t1 = Z * Z */
-   if ((err = mp_sqr(R->z, t1)) != CRYPT_OK)                                      { goto done; }
-   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)                 { goto done; }
-   /* Z = Y * Z */
-   if ((err = mp_mul(R->z, R->y, R->z)) != CRYPT_OK)                              { goto done; }
-   if ((err = mp_montgomery_reduce(R->z, modulus, mp)) != CRYPT_OK)               { goto done; }
-   /* Z = 2Z */
-   if ((err = mp_add(R->z, R->z, R->z)) != CRYPT_OK)                              { goto done; }
-   if (mp_cmp(R->z, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(R->z, modulus, R->z)) != CRYPT_OK)                        { goto done; }
-   }
-
-   /* T2 = X - T1 */
-   if ((err = mp_sub(R->x, t1, t2)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp_d(t2, 0) == LTC_MP_LT) {
-      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                            { goto done; }
-   }
-   /* T1 = X + T1 */
-   if ((err = mp_add(t1, R->x, t1)) != CRYPT_OK)                                  { goto done; }
-   if (mp_cmp(t1, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                            { goto done; }
-   }
-   /* T2 = T1 * T2 */
-   if ((err = mp_mul(t1, t2, t2)) != CRYPT_OK)                                    { goto done; }
-   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)                 { goto done; }
-   /* T1 = 2T2 */
-   if ((err = mp_add(t2, t2, t1)) != CRYPT_OK)                                    { goto done; }
-   if (mp_cmp(t1, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                            { goto done; }
-   }
-   /* T1 = T1 + T2 */
-   if ((err = mp_add(t1, t2, t1)) != CRYPT_OK)                                    { goto done; }
-   if (mp_cmp(t1, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                            { goto done; }
-   }
-
-   /* Y = 2Y */
-   if ((err = mp_add(R->y, R->y, R->y)) != CRYPT_OK)                              { goto done; }
-   if (mp_cmp(R->y, modulus) != LTC_MP_LT) {
-      if ((err = mp_sub(R->y, modulus, R->y)) != CRYPT_OK)                        { goto done; }
-   }
-   /* Y = Y * Y */
-   if ((err = mp_sqr(R->y, R->y)) != CRYPT_OK)                                    { goto done; }
-   if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK)               { goto done; }
-   /* T2 = Y * Y */
-   if ((err = mp_sqr(R->y, t2)) != CRYPT_OK)                                      { goto done; }
-   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)                 { goto done; }
-   /* T2 = T2/2 */
-   if (mp_isodd(t2)) {
-      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                            { goto done; }
-   }
-   if ((err = mp_div_2(t2, t2)) != CRYPT_OK)                                      { goto done; }
-   /* Y = Y * X */
-   if ((err = mp_mul(R->y, R->x, R->y)) != CRYPT_OK)                              { goto done; }
-   if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK)               { goto done; }
-
-   /* X  = T1 * T1 */
-   if ((err = mp_sqr(t1, R->x)) != CRYPT_OK)                                      { goto done; }
-   if ((err = mp_montgomery_reduce(R->x, modulus, mp)) != CRYPT_OK)               { goto done; }
-   /* X = X - Y */
-   if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK)                              { goto done; }
-   if (mp_cmp_d(R->x, 0) == LTC_MP_LT) {
-      if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK)                        { goto done; }
-   }
-   /* X = X - Y */
-   if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK)                              { goto done; }
-   if (mp_cmp_d(R->x, 0) == LTC_MP_LT) {
-      if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK)                        { goto done; }
-   }
-
-   /* Y = Y - X */
-   if ((err = mp_sub(R->y, R->x, R->y)) != CRYPT_OK)                              { goto done; }
-   if (mp_cmp_d(R->y, 0) == LTC_MP_LT) {
-      if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK)                        { goto done; }
-   }
-   /* Y = Y * T1 */
-   if ((err = mp_mul(R->y, t1, R->y)) != CRYPT_OK)                                { goto done; }
-   if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK)               { goto done; }
-   /* Y = Y - T2 */
-   if ((err = mp_sub(R->y, t2, R->y)) != CRYPT_OK)                                { goto done; }
-   if (mp_cmp_d(R->y, 0) == LTC_MP_LT) {
-      if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK)                        { goto done; }
-   }
-
-   err = CRYPT_OK;
-done:
-   mp_clear_multi(t1, t2, NULL);
-   return err;
-}
-#endif
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c
deleted file mode 100644
index 1864fbc7642f..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_i2osp.c
-  Integer to Octet I2OSP, Tom St Denis
-*/
-
-#ifdef LTC_PKCS_1
-
-/* always stores the same # of bytes, pads with leading zero bytes
-   as required
- */
-
-/**
-   PKCS #1 Integer to binary
-   @param n             The integer to store
-   @param modulus_len   The length of the RSA modulus
-   @param out           [out] The destination for the integer
-   @return CRYPT_OK if successful
-*/
-int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out)
-{
-   unsigned long size;
-
-   size = mp_unsigned_bin_size(n);
-
-   if (size > modulus_len) {
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   /* store it */
-   zeromem(out, modulus_len);
-   return mp_to_unsigned_bin(n, out+(modulus_len-size));
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c
deleted file mode 100644
index 9990511b7d7a..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_mgf1.c
-  The Mask Generation Function (MGF1) for PKCS #1, Tom St Denis
-*/
-
-#ifdef LTC_PKCS_1
-
-/**
-   Perform PKCS #1 MGF1 (internal)
-   @param hash_idx    The index of the hash desired
-   @param seed        The seed for MGF1
-   @param seedlen     The length of the seed
-   @param mask        [out] The destination
-   @param masklen     The length of the mask desired
-   @return CRYPT_OK if successful
-*/
-int pkcs_1_mgf1(int                  hash_idx,
-                const unsigned char *seed, unsigned long seedlen,
-                      unsigned char *mask, unsigned long masklen)
-{
-   unsigned long hLen, x;
-   ulong32       counter;
-   int           err;
-   hash_state    *md;
-   unsigned char *buf;
-
-   LTC_ARGCHK(seed != NULL);
-   LTC_ARGCHK(mask != NULL);
-
-   /* ensure valid hash */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* get hash output size */
-   hLen = hash_descriptor[hash_idx].hashsize;
-
-   /* allocate memory */
-   md  = XMALLOC(sizeof(hash_state));
-   buf = XMALLOC(hLen);
-   if (md == NULL || buf == NULL) {
-      if (md != NULL) {
-         XFREE(md);
-      }
-      if (buf != NULL) {
-         XFREE(buf);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* start counter */
-   counter = 0;
-
-   while (masklen > 0) {
-       /* handle counter */
-       STORE32H(counter, buf);
-       ++counter;
-
-       /* get hash of seed || counter */
-       if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       if ((err = hash_descriptor[hash_idx].process(md, seed, seedlen)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       if ((err = hash_descriptor[hash_idx].process(md, buf, 4)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-       if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) {
-          goto LBL_ERR;
-       }
-
-       /* store it */
-       for (x = 0; x < hLen && masklen > 0; x++, masklen--) {
-          *mask++ = buf[x];
-       }
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(buf, hLen);
-   zeromem(md,  sizeof(hash_state));
-#endif
-
-   XFREE(buf);
-   XFREE(md);
-
-   return err;
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c
deleted file mode 100644
index 2ade070d67c0..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_oaep_decode.c
-  OAEP Padding for PKCS #1, Tom St Denis
-*/
-
-#ifdef LTC_PKCS_1
-
-/**
-   PKCS #1 v2.00 OAEP decode
-   @param msg              The encoded data to decode
-   @param msglen           The length of the encoded data (octets)
-   @param lparam           The session or system data (can be NULL)
-   @param lparamlen        The length of the lparam
-   @param modulus_bitlen   The bit length of the RSA modulus
-   @param hash_idx         The index of the hash desired
-   @param out              [out] Destination of decoding
-   @param outlen           [in/out] The max size and resulting size of the decoding
-   @param res              [out] Result of decoding, 1==valid, 0==invalid
-   @return CRYPT_OK if successful
-*/
-int pkcs_1_oaep_decode(const unsigned char *msg,    unsigned long msglen,
-                       const unsigned char *lparam, unsigned long lparamlen,
-                             unsigned long modulus_bitlen, int hash_idx,
-                             unsigned char *out,    unsigned long *outlen,
-                             int           *res)
-{
-   unsigned char *DB, *seed, *mask;
-   unsigned long hLen, x, y, modulus_len;
-   int           err, ret;
-
-   LTC_ARGCHK(msg    != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(res    != NULL);
-
-   /* default to invalid packet */
-   *res = 0;
-
-   /* test valid hash */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-   hLen        = hash_descriptor[hash_idx].hashsize;
-   modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
-
-   /* test hash/message size */
-   if ((2*hLen >= (modulus_len - 2)) || (msglen != modulus_len)) {
-      return CRYPT_PK_INVALID_SIZE;
-   }
-
-   /* allocate ram for DB/mask/salt of size modulus_len */
-   DB   = XMALLOC(modulus_len);
-   mask = XMALLOC(modulus_len);
-   seed = XMALLOC(hLen);
-   if (DB == NULL || mask == NULL || seed == NULL) {
-      if (DB != NULL) {
-         XFREE(DB);
-      }
-      if (mask != NULL) {
-         XFREE(mask);
-      }
-      if (seed != NULL) {
-         XFREE(seed);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* ok so it's now in the form
-
-      0x00  || maskedseed || maskedDB
-
-       1    ||   hLen     ||  modulus_len - hLen - 1
-
-    */
-
-   ret = CRYPT_OK;
-
-   /* must have leading 0x00 byte */
-   if (msg[0] != 0x00) {
-      ret = CRYPT_INVALID_PACKET;
-   }
-
-   /* now read the masked seed */
-   x = 1;
-   XMEMCPY(seed, msg + x, hLen);
-   x += hLen;
-
-   /* now read the masked DB */
-   XMEMCPY(DB, msg + x, modulus_len - hLen - 1);
-   x += modulus_len - hLen - 1;
-
-   /* compute MGF1 of maskedDB (hLen) */
-   if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* XOR against seed */
-   for (y = 0; y < hLen; y++) {
-      seed[y] ^= mask[y];
-   }
-
-   /* compute MGF1 of seed (k - hlen - 1) */
-   if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* xor against DB */
-   for (y = 0; y < (modulus_len - hLen - 1); y++) {
-       DB[y] ^= mask[y];
-   }
-
-   /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */
-
-   /* compute lhash and store it in seed [reuse temps!] */
-   x = modulus_len;
-   if (lparam != NULL) {
-      if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-   } else {
-      /* can't pass hash_memory a NULL so use DB with zero length */
-      if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* compare the lhash'es */
-   if (XMEM_NEQ(seed, DB, hLen) != 0) {
-      ret = CRYPT_INVALID_PACKET;
-   }
-
-   /* now zeroes before a 0x01 */
-   for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) {
-      /* step... */
-   }
-
-   /* error if wasn't 0x01 */
-   if (x == (modulus_len - hLen - 1) || DB[x] != 0x01) {
-      ret = CRYPT_INVALID_PACKET;
-   }
-
-   /* rest is the message (and skip 0x01) */
-   if ((modulus_len - hLen - 1 - ++x) > *outlen) {
-      ret = CRYPT_INVALID_PACKET;
-   }
-
-   if (ret == CRYPT_OK) {
-      /* copy message */
-      *outlen = modulus_len - hLen - 1 - x;
-      XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x);
-
-      /* valid packet */
-      *res = 1;
-   }
-   err = ret;
-
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(DB,   modulus_len);
-   zeromem(seed, hLen);
-   zeromem(mask, modulus_len);
-#endif
-
-   XFREE(seed);
-   XFREE(mask);
-   XFREE(DB);
-
-   return err;
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c
deleted file mode 100644
index e9185a99e099..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_oaep_encode.c
-  OAEP Padding for PKCS #1, Tom St Denis
-*/
-
-#ifdef LTC_PKCS_1
-
-/**
-  PKCS #1 v2.00 OAEP encode
-  @param msg             The data to encode
-  @param msglen          The length of the data to encode (octets)
-  @param lparam          A session or system parameter (can be NULL)
-  @param lparamlen       The length of the lparam data
-  @param modulus_bitlen  The bit length of the RSA modulus
-  @param prng            An active PRNG state
-  @param prng_idx        The index of the PRNG desired
-  @param hash_idx        The index of the hash desired
-  @param out             [out] The destination for the encoded data
-  @param outlen          [in/out] The max size and resulting size of the encoded data
-  @return CRYPT_OK if successful
-*/
-int pkcs_1_oaep_encode(const unsigned char *msg,    unsigned long msglen,
-                       const unsigned char *lparam, unsigned long lparamlen,
-                             unsigned long modulus_bitlen, prng_state *prng,
-                             int           prng_idx,         int  hash_idx,
-                             unsigned char *out,    unsigned long *outlen)
-{
-   unsigned char *DB, *seed, *mask;
-   unsigned long hLen, x, y, modulus_len;
-   int           err;
-
-   LTC_ARGCHK(msg    != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   /* test valid hash */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* valid prng */
-   if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   hLen        = hash_descriptor[hash_idx].hashsize;
-   modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
-
-   /* test message size */
-   if ((2*hLen >= (modulus_len - 2)) || (msglen > (modulus_len - 2*hLen - 2))) {
-      return CRYPT_PK_INVALID_SIZE;
-   }
-
-   /* allocate ram for DB/mask/salt of size modulus_len */
-   DB   = XMALLOC(modulus_len);
-   mask = XMALLOC(modulus_len);
-   seed = XMALLOC(hLen);
-   if (DB == NULL || mask == NULL || seed == NULL) {
-      if (DB != NULL) {
-         XFREE(DB);
-      }
-      if (mask != NULL) {
-         XFREE(mask);
-      }
-      if (seed != NULL) {
-         XFREE(seed);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* get lhash */
-   /* DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */
-   x = modulus_len;
-   if (lparam != NULL) {
-      if ((err = hash_memory(hash_idx, lparam, lparamlen, DB, &x)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-   } else {
-      /* can't pass hash_memory a NULL so use DB with zero length */
-      if ((err = hash_memory(hash_idx, DB, 0, DB, &x)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-   }
-
-   /* append PS then 0x01 (to lhash)  */
-   x = hLen;
-   y = modulus_len - msglen - 2*hLen - 2;
-   XMEMSET(DB+x, 0, y);
-   x += y;
-
-   /* 0x01 byte */
-   DB[x++] = 0x01;
-
-   /* message (length = msglen) */
-   XMEMCPY(DB+x, msg, msglen);
-   x += msglen;
-
-   /* now choose a random seed */
-   if (prng_descriptor[prng_idx].read(seed, hLen, prng) != hLen) {
-      err = CRYPT_ERROR_READPRNG;
-      goto LBL_ERR;
-   }
-
-   /* compute MGF1 of seed (k - hlen - 1) */
-   if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* xor against DB */
-   for (y = 0; y < (modulus_len - hLen - 1); y++) {
-       DB[y] ^= mask[y];
-   }
-
-   /* compute MGF1 of maskedDB (hLen) */
-   if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* XOR against seed */
-   for (y = 0; y < hLen; y++) {
-      seed[y] ^= mask[y];
-   }
-
-   /* create string of length modulus_len */
-   if (*outlen < modulus_len) {
-      *outlen = modulus_len;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-
-   /* start output which is 0x00 || maskedSeed || maskedDB */
-   x = 0;
-   out[x++] = 0x00;
-   XMEMCPY(out+x, seed, hLen);
-   x += hLen;
-   XMEMCPY(out+x, DB, modulus_len - hLen - 1);
-   x += modulus_len - hLen - 1;
-
-   *outlen = x;
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(DB,   modulus_len);
-   zeromem(seed, hLen);
-   zeromem(mask, modulus_len);
-#endif
-
-   XFREE(seed);
-   XFREE(mask);
-   XFREE(DB);
-
-   return err;
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c
deleted file mode 100644
index 1460a17a3f39..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_os2ip.c
-  Octet to Integer OS2IP, Tom St Denis
-*/
-#ifdef LTC_PKCS_1
-
-/**
-  Read a binary string into an mp_int
-  @param n          [out] The mp_int destination
-  @param in         The binary string to read
-  @param inlen      The length of the binary string
-  @return CRYPT_OK if successful
-*/
-int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen)
-{
-   return mp_read_unsigned_bin(n, in, inlen);
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c
deleted file mode 100644
index 86d2af1d53f5..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_pss_decode.c
-  PKCS #1 PSS Signature Padding, Tom St Denis
-*/
-
-#ifdef LTC_PKCS_1
-
-/**
-   PKCS #1 v2.00 PSS decode
-   @param  msghash         The hash to verify
-   @param  msghashlen      The length of the hash (octets)
-   @param  sig             The signature data (encoded data)
-   @param  siglen          The length of the signature data (octets)
-   @param  saltlen         The length of the salt used (octets)
-   @param  hash_idx        The index of the hash desired
-   @param  modulus_bitlen  The bit length of the RSA modulus
-   @param  res             [out] The result of the comparison, 1==valid, 0==invalid
-   @return CRYPT_OK if successful (even if the comparison failed)
-*/
-int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen,
-                      const unsigned char *sig,     unsigned long siglen,
-                            unsigned long saltlen,  int           hash_idx,
-                            unsigned long modulus_bitlen, int    *res)
-{
-   unsigned char *DB, *mask, *salt, *hash;
-   unsigned long x, y, hLen, modulus_len;
-   int           err;
-   hash_state    md;
-
-   LTC_ARGCHK(msghash != NULL);
-   LTC_ARGCHK(res     != NULL);
-
-   /* default to invalid */
-   *res = 0;
-
-   /* ensure hash is valid */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   hLen        = hash_descriptor[hash_idx].hashsize;
-   modulus_bitlen--;
-   modulus_len = (modulus_bitlen>>3) + (modulus_bitlen & 7 ? 1 : 0);
-
-   /* check sizes */
-   if ((saltlen > modulus_len) ||
-       (modulus_len < hLen + saltlen + 2)) {
-      return CRYPT_PK_INVALID_SIZE;
-   }
-
-   /* allocate ram for DB/mask/salt/hash of size modulus_len */
-   DB   = XMALLOC(modulus_len);
-   mask = XMALLOC(modulus_len);
-   salt = XMALLOC(modulus_len);
-   hash = XMALLOC(modulus_len);
-   if (DB == NULL || mask == NULL || salt == NULL || hash == NULL) {
-      if (DB != NULL) {
-         XFREE(DB);
-      }
-      if (mask != NULL) {
-         XFREE(mask);
-      }
-      if (salt != NULL) {
-         XFREE(salt);
-      }
-      if (hash != NULL) {
-         XFREE(hash);
-      }
-      return CRYPT_MEM;
-   }
-
-   /* ensure the 0xBC byte */
-   if (sig[siglen-1] != 0xBC) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   /* copy out the DB */
-   x = 0;
-   XMEMCPY(DB, sig + x, modulus_len - hLen - 1);
-   x += modulus_len - hLen - 1;
-
-   /* copy out the hash */
-   XMEMCPY(hash, sig + x, hLen);
-   /* x += hLen; */
-
-   /* check the MSB */
-   if ((sig[0] & ~(0xFF >> ((modulus_len<<3) - (modulus_bitlen)))) != 0) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   /* generate mask of length modulus_len - hLen - 1 from hash */
-   if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* xor against DB */
-   for (y = 0; y < (modulus_len - hLen - 1); y++) {
-      DB[y] ^= mask[y];
-   }
-
-   /* now clear the first byte [make sure smaller than modulus] */
-   DB[0] &= 0xFF >> ((modulus_len<<3) - (modulus_bitlen));
-
-   /* DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */
-
-   /* check for zeroes and 0x01 */
-   for (x = 0; x < modulus_len - saltlen - hLen - 2; x++) {
-       if (DB[x] != 0x00) {
-          err = CRYPT_INVALID_PACKET;
-          goto LBL_ERR;
-       }
-   }
-
-   /* check for the 0x01 */
-   if (DB[x++] != 0x01) {
-      err = CRYPT_INVALID_PACKET;
-      goto LBL_ERR;
-   }
-
-   /* M = (eight) 0x00 || msghash || salt, mask = H(M) */
-   if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   zeromem(mask, 8);
-   if ((err = hash_descriptor[hash_idx].process(&md, mask, 8)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = hash_descriptor[hash_idx].process(&md, DB+x, saltlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = hash_descriptor[hash_idx].done(&md, mask)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* mask == hash means valid signature */
-   if (XMEM_NEQ(mask, hash, hLen) == 0) {
-      *res = 1;
-   }
-
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(DB,   modulus_len);
-   zeromem(mask, modulus_len);
-   zeromem(salt, modulus_len);
-   zeromem(hash, modulus_len);
-#endif
-
-   XFREE(hash);
-   XFREE(salt);
-   XFREE(mask);
-   XFREE(DB);
-
-   return err;
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c
deleted file mode 100644
index a19e727d520b..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file pkcs_1_pss_encode.c
-  PKCS #1 PSS Signature Padding, Tom St Denis
-*/
-
-#ifdef LTC_PKCS_1
-
-/**
-   PKCS #1 v2.00 Signature Encoding
-   @param msghash          The hash to encode
-   @param msghashlen       The length of the hash (octets)
-   @param saltlen          The length of the salt desired (octets)
-   @param prng             An active PRNG context
-   @param prng_idx         The index of the PRNG desired
-   @param hash_idx         The index of the hash desired
-   @param modulus_bitlen   The bit length of the RSA modulus
-   @param out              [out] The destination of the encoding
-   @param outlen           [in/out] The max size and resulting size of the encoded data
-   @return CRYPT_OK if successful
-*/
-int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen,
-                            unsigned long saltlen,  prng_state   *prng,
-                            int           prng_idx, int           hash_idx,
-                            unsigned long modulus_bitlen,
-                            unsigned char *out,     unsigned long *outlen)
-{
-   unsigned char *DB, *mask, *salt, *hash;
-   unsigned long x, y, hLen, modulus_len;
-   int           err;
-   hash_state    md;
-
-   LTC_ARGCHK(msghash != NULL);
-   LTC_ARGCHK(out     != NULL);
-   LTC_ARGCHK(outlen  != NULL);
-
-   /* ensure hash and PRNG are valid */
-   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-      return err;
-   }
-   if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
-      return err;
-   }
-
-   hLen        = hash_descriptor[hash_idx].hashsize;
-   modulus_bitlen--;
-   modulus_len = (modulus_bitlen>>3) + (modulus_bitlen & 7 ? 1 : 0);
-
-   /* check sizes */
-   if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2)) {
-      return CRYPT_PK_INVALID_SIZE;
-   }
-
-   /* allocate ram for DB/mask/salt/hash of size modulus_len */
-   DB   = XMALLOC(modulus_len);
-   mask = XMALLOC(modulus_len);
-   salt = XMALLOC(modulus_len);
-   hash = XMALLOC(modulus_len);
-   if (DB == NULL || mask == NULL || salt == NULL || hash == NULL) {
-      if (DB != NULL) {
-         XFREE(DB);
-      }
-      if (mask != NULL) {
-         XFREE(mask);
-      }
-      if (salt != NULL) {
-         XFREE(salt);
-      }
-      if (hash != NULL) {
-         XFREE(hash);
-      }
-      return CRYPT_MEM;
-   }
-
-
-   /* generate random salt */
-   if (saltlen > 0) {
-      if (prng_descriptor[prng_idx].read(salt, saltlen, prng) != saltlen) {
-         err = CRYPT_ERROR_READPRNG;
-         goto LBL_ERR;
-      }
-   }
-
-   /* M = (eight) 0x00 || msghash || salt, hash = H(M) */
-   if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   zeromem(DB, 8);
-   if ((err = hash_descriptor[hash_idx].process(&md, DB, 8)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = hash_descriptor[hash_idx].process(&md, salt, saltlen)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if ((err = hash_descriptor[hash_idx].done(&md, hash)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* generate DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */
-   x = 0;
-   XMEMSET(DB + x, 0, modulus_len - saltlen - hLen - 2);
-   x += modulus_len - saltlen - hLen - 2;
-   DB[x++] = 0x01;
-   XMEMCPY(DB + x, salt, saltlen);
-   /* x += saltlen; */
-
-   /* generate mask of length modulus_len - hLen - 1 from hash */
-   if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-
-   /* xor against DB */
-   for (y = 0; y < (modulus_len - hLen - 1); y++) {
-      DB[y] ^= mask[y];
-   }
-
-   /* output is DB || hash || 0xBC */
-   if (*outlen < modulus_len) {
-      *outlen = modulus_len;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto LBL_ERR;
-   }
-
-   /* DB len = modulus_len - hLen - 1 */
-   y = 0;
-   XMEMCPY(out + y, DB, modulus_len - hLen - 1);
-   y += modulus_len - hLen - 1;
-
-   /* hash */
-   XMEMCPY(out + y, hash, hLen);
-   y += hLen;
-
-   /* 0xBC */
-   out[y] = 0xBC;
-
-   /* now clear the 8*modulus_len - modulus_bitlen most significant bits */
-   out[0] &= 0xFF >> ((modulus_len<<3) - modulus_bitlen);
-
-   /* store output size */
-   *outlen = modulus_len;
-   err = CRYPT_OK;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
-   zeromem(DB,   modulus_len);
-   zeromem(mask, modulus_len);
-   zeromem(salt, modulus_len);
-   zeromem(hash, modulus_len);
-#endif
-
-   XFREE(hash);
-   XFREE(salt);
-   XFREE(mask);
-   XFREE(DB);
-
-   return err;
-}
-
-#endif /* LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c
deleted file mode 100644
index 75ba945876fd..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/** @file pkcs_1_v1_5_decode.c
- *
- *  PKCS #1 v1.5 Padding. (Andreas Lange)
- */
-
-#ifdef LTC_PKCS_1
-
-/** @brief PKCS #1 v1.5 decode.
- *
- *  @param msg              The encoded data to decode
- *  @param msglen           The length of the encoded data (octets)
- *  @param block_type       Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks)
- *  @param modulus_bitlen   The bit length of the RSA modulus
- *  @param out              [out] Destination of decoding
- *  @param outlen           [in/out] The max size and resulting size of the decoding
- *  @param is_valid         [out] Boolean whether the padding was valid
- *
- *  @return CRYPT_OK if successful
- */
-int pkcs_1_v1_5_decode(const unsigned char *msg,
-                             unsigned long  msglen,
-                                       int  block_type,
-                             unsigned long  modulus_bitlen,
-                             unsigned char *out,
-                             unsigned long *outlen,
-                                       int *is_valid)
-{
-  unsigned long modulus_len, ps_len, i;
-  int result;
-
-  /* default to invalid packet */
-  *is_valid = 0;
-
-  modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
-
-  /* test message size */
-
-  if ((msglen > modulus_len) || (modulus_len < 11)) {
-    return CRYPT_PK_INVALID_SIZE;
-  }
-
-  result = CRYPT_OK;
-
-  /* separate encoded message */
-
-  if ((msg[0] != 0x00) || (msg[1] != (unsigned char)block_type)) {
-    result = CRYPT_INVALID_PACKET;
-  }
-
-  if (block_type == LTC_PKCS_1_EME) {
-    for (i = 2; i < modulus_len; i++) {
-      /* separator */
-      if (msg[i] == 0x00) { break; }
-    }
-    ps_len = i++ - 2;
-
-    if (i >= modulus_len) {
-      /* There was no octet with hexadecimal value 0x00 to separate ps from m.
-       */
-      result = CRYPT_INVALID_PACKET;
-    }
-  } else {
-    for (i = 2; i < modulus_len - 1; i++) {
-       if (msg[i] != 0xFF) { break; }
-    }
-
-    /* separator check */
-    if (msg[i] != 0) {
-      /* There was no octet with hexadecimal value 0x00 to separate ps from m. */
-      result = CRYPT_INVALID_PACKET;
-    }
-
-    ps_len = i - 2;
-  }
-
-  if (ps_len < 8)
-  {
-    /* The length of ps is less than 8 octets.
-     */
-    result = CRYPT_INVALID_PACKET;
-  }
-
-  if (*outlen < (msglen - (2 + ps_len + 1))) {
-    result = CRYPT_INVALID_PACKET;
-  }
-
-  if (result == CRYPT_OK) {
-     *outlen = (msglen - (2 + ps_len + 1));
-     XMEMCPY(out, &msg[2 + ps_len + 1], *outlen);
-
-     /* valid packet */
-     *is_valid = 1;
-  }
-
-  return result;
-} /* pkcs_1_v1_5_decode */
-
-#endif /* #ifdef LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c b/libs/tomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c
deleted file mode 100644
index 9311b5761a08..000000000000
--- a/libs/tomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/*! \file pkcs_1_v1_5_encode.c
- *
- *  PKCS #1 v1.5 Padding (Andreas Lange)
- */
-
-#ifdef LTC_PKCS_1
-
-/*! \brief PKCS #1 v1.5 encode.
- *
- *  \param msg              The data to encode
- *  \param msglen           The length of the data to encode (octets)
- *  \param block_type       Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks)
- *  \param modulus_bitlen   The bit length of the RSA modulus
- *  \param prng             An active PRNG state (only for LTC_PKCS_1_EME)
- *  \param prng_idx         The index of the PRNG desired (only for LTC_PKCS_1_EME)
- *  \param out              [out] The destination for the encoded data
- *  \param outlen           [in/out] The max size and resulting size of the encoded data
- *
- *  \return CRYPT_OK if successful
- */
-int pkcs_1_v1_5_encode(const unsigned char *msg,
-                             unsigned long  msglen,
-                                       int  block_type,
-                             unsigned long  modulus_bitlen,
-                                prng_state *prng,
-                                       int  prng_idx,
-                             unsigned char *out,
-                             unsigned long *outlen)
-{
-  unsigned long modulus_len, ps_len, i;
-  unsigned char *ps;
-  int result;
-
-  /* valid block_type? */
-  if ((block_type != LTC_PKCS_1_EMSA) &&
-      (block_type != LTC_PKCS_1_EME)) {
-     return CRYPT_PK_INVALID_PADDING;
-  }
-
-  if (block_type == LTC_PKCS_1_EME) {    /* encryption padding, we need a valid PRNG */
-    if ((result = prng_is_valid(prng_idx)) != CRYPT_OK) {
-       return result;
-    }
-  }
-
-  modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
-
-  /* test message size */
-  if ((msglen + 11) > modulus_len) {
-    return CRYPT_PK_INVALID_SIZE;
-  }
-
-  if (*outlen < modulus_len) {
-    *outlen = modulus_len;
-    result = CRYPT_BUFFER_OVERFLOW;
-    goto bail;
-  }
-
-  /* generate an octets string PS */
-  ps = &out[2];
-  ps_len = modulus_len - msglen - 3;
-
-  if (block_type == LTC_PKCS_1_EME) {
-    /* now choose a random ps */
-    if (prng_descriptor[prng_idx].read(ps, ps_len, prng) != ps_len) {
-      result = CRYPT_ERROR_READPRNG;
-      goto bail;
-    }
-
-    /* transform zero bytes (if any) to non-zero random bytes */
-    for (i = 0; i < ps_len; i++) {
-      while (ps[i] == 0) {
-        if (prng_descriptor[prng_idx].read(&ps[i], 1, prng) != 1) {
-          result = CRYPT_ERROR_READPRNG;
-          goto bail;
-        }
-      }
-    }
-  } else {
-    XMEMSET(ps, 0xFF, ps_len);
-  }
-
-  /* create string of length modulus_len */
-  out[0]          = 0x00;
-  out[1]          = (unsigned char)block_type;  /* block_type 1 or 2 */
-  out[2 + ps_len] = 0x00;
-  XMEMCPY(&out[2 + ps_len + 1], msg, msglen);
-  *outlen = modulus_len;
-
-  result  = CRYPT_OK;
-bail:
-  return result;
-} /* pkcs_1_v1_5_encode */
-
-#endif /* #ifdef LTC_PKCS_1 */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_decrypt_key.c b/libs/tomcrypt/src/pk/rsa/rsa_decrypt_key.c
deleted file mode 100644
index 3489ef8c2a67..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_decrypt_key.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_decrypt_key.c
-  RSA PKCS #1 Decryption, Tom St Denis and Andreas Lange
-*/
-
-#ifdef LTC_MRSA
-
-/**
-   PKCS #1 decrypt then v1.5 or OAEP depad
-   @param in          The ciphertext
-   @param inlen       The length of the ciphertext (octets)
-   @param out         [out] The plaintext
-   @param outlen      [in/out] The max size and resulting size of the plaintext (octets)
-   @param lparam      The system "lparam" value
-   @param lparamlen   The length of the lparam value (octets)
-   @param hash_idx    The index of the hash desired
-   @param padding     Type of padding (LTC_PKCS_1_OAEP or LTC_PKCS_1_V1_5)
-   @param stat        [out] Result of the decryption, 1==valid, 0==invalid
-   @param key         The corresponding private RSA key
-   @return CRYPT_OK if succcessul (even if invalid)
-*/
-int rsa_decrypt_key_ex(const unsigned char *in,       unsigned long  inlen,
-                             unsigned char *out,      unsigned long *outlen,
-                       const unsigned char *lparam,   unsigned long  lparamlen,
-                             int            hash_idx, int            padding,
-                             int           *stat,     rsa_key       *key)
-{
-  unsigned long modulus_bitlen, modulus_bytelen, x;
-  int           err;
-  unsigned char *tmp;
-
-  LTC_ARGCHK(out    != NULL);
-  LTC_ARGCHK(outlen != NULL);
-  LTC_ARGCHK(key    != NULL);
-  LTC_ARGCHK(stat   != NULL);
-
-  /* default to invalid */
-  *stat = 0;
-
-  /* valid padding? */
-
-  if ((padding != LTC_PKCS_1_V1_5) &&
-      (padding != LTC_PKCS_1_OAEP)) {
-    return CRYPT_PK_INVALID_PADDING;
-  }
-
-  if (padding == LTC_PKCS_1_OAEP) {
-    /* valid hash ? */
-    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-       return err;
-    }
-  }
-
-  /* get modulus len in bits */
-  modulus_bitlen = mp_count_bits( (key->N));
-
-  /* outlen must be at least the size of the modulus */
-  modulus_bytelen = mp_unsigned_bin_size( (key->N));
-  if (modulus_bytelen != inlen) {
-     return CRYPT_INVALID_PACKET;
-  }
-
-  /* allocate ram */
-  tmp = XMALLOC(inlen);
-  if (tmp == NULL) {
-     return CRYPT_MEM;
-  }
-
-  /* rsa decode the packet */
-  x = inlen;
-  if ((err = ltc_mp.rsa_me(in, inlen, tmp, &x, PK_PRIVATE, key)) != CRYPT_OK) {
-     XFREE(tmp);
-     return err;
-  }
-
-  if (padding == LTC_PKCS_1_OAEP) {
-    /* now OAEP decode the packet */
-    err = pkcs_1_oaep_decode(tmp, x, lparam, lparamlen, modulus_bitlen, hash_idx,
-                             out, outlen, stat);
-  } else {
-    /* now PKCS #1 v1.5 depad the packet */
-    err = pkcs_1_v1_5_decode(tmp, x, LTC_PKCS_1_EME, modulus_bitlen, out, outlen, stat);
-  }
-
-  XFREE(tmp);
-  return err;
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_encrypt_key.c b/libs/tomcrypt/src/pk/rsa/rsa_encrypt_key.c
deleted file mode 100644
index 029d77a81764..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_encrypt_key.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_encrypt_key.c
-  RSA PKCS #1 encryption, Tom St Denis and Andreas Lange
-*/
-
-#ifdef LTC_MRSA
-
-/**
-    (PKCS #1 v2.0) OAEP pad then encrypt
-    @param in          The plaintext
-    @param inlen       The length of the plaintext (octets)
-    @param out         [out] The ciphertext
-    @param outlen      [in/out] The max size and resulting size of the ciphertext
-    @param lparam      The system "lparam" for the encryption
-    @param lparamlen   The length of lparam (octets)
-    @param prng        An active PRNG
-    @param prng_idx    The index of the desired prng
-    @param hash_idx    The index of the desired hash
-    @param padding     Type of padding (LTC_PKCS_1_OAEP or LTC_PKCS_1_V1_5)
-    @param key         The RSA key to encrypt to
-    @return CRYPT_OK if successful
-*/
-int rsa_encrypt_key_ex(const unsigned char *in,     unsigned long inlen,
-                             unsigned char *out,    unsigned long *outlen,
-                       const unsigned char *lparam, unsigned long lparamlen,
-                       prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key)
-{
-  unsigned long modulus_bitlen, modulus_bytelen, x;
-  int           err;
-
-  LTC_ARGCHK(in     != NULL);
-  LTC_ARGCHK(out    != NULL);
-  LTC_ARGCHK(outlen != NULL);
-  LTC_ARGCHK(key    != NULL);
-
-  /* valid padding? */
-  if ((padding != LTC_PKCS_1_V1_5) &&
-      (padding != LTC_PKCS_1_OAEP)) {
-    return CRYPT_PK_INVALID_PADDING;
-  }
-
-  /* valid prng? */
-  if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
-     return err;
-  }
-
-  if (padding == LTC_PKCS_1_OAEP) {
-    /* valid hash? */
-    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-       return err;
-    }
-  }
-
-  /* get modulus len in bits */
-  modulus_bitlen = mp_count_bits( (key->N));
-
-  /* outlen must be at least the size of the modulus */
-  modulus_bytelen = mp_unsigned_bin_size( (key->N));
-  if (modulus_bytelen > *outlen) {
-     *outlen = modulus_bytelen;
-     return CRYPT_BUFFER_OVERFLOW;
-  }
-
-  if (padding == LTC_PKCS_1_OAEP) {
-    /* OAEP pad the key */
-    x = *outlen;
-    if ((err = pkcs_1_oaep_encode(in, inlen, lparam,
-                                  lparamlen, modulus_bitlen, prng, prng_idx, hash_idx,
-                                  out, &x)) != CRYPT_OK) {
-       return err;
-    }
-  } else {
-    /* PKCS #1 v1.5 pad the key */
-    x = *outlen;
-    if ((err = pkcs_1_v1_5_encode(in, inlen, LTC_PKCS_1_EME,
-                                  modulus_bitlen, prng, prng_idx,
-                                  out, &x)) != CRYPT_OK) {
-      return err;
-    }
-  }
-
-  /* rsa exptmod the OAEP or PKCS #1 v1.5 pad */
-  return ltc_mp.rsa_me(out, x, out, outlen, PK_PUBLIC, key);
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_export.c b/libs/tomcrypt/src/pk/rsa/rsa_export.c
deleted file mode 100644
index 218beee18b80..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_export.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_export.c
-  Export RSA PKCS keys, Tom St Denis
-*/
-
-#ifdef LTC_MRSA
-
-/**
-    This will export either an RSAPublicKey or RSAPrivateKey [defined in PKCS #1 v2.1]
-    @param out       [out] Destination of the packet
-    @param outlen    [in/out] The max size and resulting size of the packet
-    @param type      The type of exported key (PK_PRIVATE or PK_PUBLIC)
-    @param key       The RSA key to export
-    @return CRYPT_OK if successful
-*/
-int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key)
-{
-   unsigned long zero=0;
-   int err;
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   /* type valid? */
-   if (!(key->type == PK_PRIVATE) && (type == PK_PRIVATE)) {
-      return CRYPT_PK_INVALID_TYPE;
-   }
-
-   if (type == PK_PRIVATE) {
-      /* private key */
-      /* output is
-            Version, n, e, d, p, q, d mod (p-1), d mod (q - 1), 1/q mod p
-       */
-      return der_encode_sequence_multi(out, outlen,
-                          LTC_ASN1_SHORT_INTEGER, 1UL, &zero,
-                          LTC_ASN1_INTEGER, 1UL,  key->N,
-                          LTC_ASN1_INTEGER, 1UL,  key->e,
-                          LTC_ASN1_INTEGER, 1UL,  key->d,
-                          LTC_ASN1_INTEGER, 1UL,  key->p,
-                          LTC_ASN1_INTEGER, 1UL,  key->q,
-                          LTC_ASN1_INTEGER, 1UL,  key->dP,
-                          LTC_ASN1_INTEGER, 1UL,  key->dQ,
-                          LTC_ASN1_INTEGER, 1UL,  key->qP,
-                          LTC_ASN1_EOL,     0UL, NULL);
-   } else {
-      /* public key */
-      unsigned long tmplen, *ptmplen;
-      unsigned char* tmp = NULL;
-
-      if (type & PK_STD) {
-          tmplen = (unsigned long)(mp_count_bits(key->N) / 8) * 2 + 8;
-          tmp = XMALLOC(tmplen);
-          ptmplen = &tmplen;
-          if (tmp == NULL) {
-              return CRYPT_MEM;
-          }
-      }
-      else {
-          tmp = out;
-          ptmplen = outlen;
-      }
-
-      err = der_encode_sequence_multi(tmp, ptmplen,
-                                 LTC_ASN1_INTEGER, 1UL,  key->N,
-                                 LTC_ASN1_INTEGER, 1UL,  key->e,
-                                 LTC_ASN1_EOL,     0UL, NULL);
-
-      if ((err != CRYPT_OK) || !(type & PK_STD)) {
-          goto finish;
-      }
-
-      err = der_encode_subject_public_key_info(out, outlen,
-        PKA_RSA, tmp, tmplen, LTC_ASN1_NULL, NULL, 0);
-
-finish:
-      if (tmp != out)
-        XFREE(tmp);
-      return err;
-
-   }
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_exptmod.c b/libs/tomcrypt/src/pk/rsa/rsa_exptmod.c
deleted file mode 100644
index 510b14cb8993..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_exptmod.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_exptmod.c
-  RSA PKCS exptmod, Tom St Denis
-  Added RSA blinding --nmav
-*/
-
-#ifdef LTC_MRSA
-
-/**
-   Compute an RSA modular exponentiation
-   @param in         The input data to send into RSA
-   @param inlen      The length of the input (octets)
-   @param out        [out] The destination
-   @param outlen     [in/out] The max size and resulting size of the output
-   @param which      Which exponent to use, e.g. PK_PRIVATE or PK_PUBLIC
-   @param key        The RSA key to use
-   @return CRYPT_OK if successful
-*/
-int rsa_exptmod(const unsigned char *in,   unsigned long inlen,
-                      unsigned char *out,  unsigned long *outlen, int which,
-                      rsa_key *key)
-{
-   void        *tmp, *tmpa, *tmpb;
-   #ifdef LTC_RSA_BLINDING
-   void        *rnd, *rndi /* inverse of rnd */;
-   #endif
-   unsigned long x;
-   int           err, has_crt_parameters;
-
-   LTC_ARGCHK(in     != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-   LTC_ARGCHK(key    != NULL);
-
-   /* is the key of the right type for the operation? */
-   if (which == PK_PRIVATE && (key->type != PK_PRIVATE)) {
-      return CRYPT_PK_NOT_PRIVATE;
-   }
-
-   /* must be a private or public operation */
-   if (which != PK_PRIVATE && which != PK_PUBLIC) {
-      return CRYPT_PK_INVALID_TYPE;
-   }
-
-   /* init and copy into tmp */
-   if ((err = mp_init_multi(&tmp, &tmpa, &tmpb,
-#ifdef LTC_RSA_BLINDING
-                                               &rnd, &rndi,
-#endif /* LTC_RSA_BLINDING */
-                                                           NULL)) != CRYPT_OK)
-        { return err; }
-   if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, (int)inlen)) != CRYPT_OK)
-        { goto error; }
-
-
-   /* sanity check on the input */
-   if (mp_cmp(key->N, tmp) == LTC_MP_LT) {
-      err = CRYPT_PK_INVALID_SIZE;
-      goto error;
-   }
-
-   /* are we using the private exponent and is the key optimized? */
-   if (which == PK_PRIVATE) {
-      #ifdef LTC_RSA_BLINDING
-      /* do blinding */
-      err = mp_rand(rnd, mp_get_digit_count(key->N));
-      if (err != CRYPT_OK) {
-             goto error;
-      }
-
-      /* rndi = 1/rnd mod N */
-      err = mp_invmod(rnd, key->N, rndi);
-      if (err != CRYPT_OK) {
-             goto error;
-      }
-
-      /* rnd = rnd^e */
-      err = mp_exptmod( rnd, key->e, key->N, rnd);
-      if (err != CRYPT_OK) {
-             goto error;
-      }
-
-      /* tmp = tmp*rnd mod N */
-      err = mp_mulmod( tmp, rnd, key->N, tmp);
-      if (err != CRYPT_OK) {
-             goto error;
-      }
-      #endif /* LTC_RSA_BLINDING */
-
-      has_crt_parameters = (key->p != NULL) && (mp_get_digit_count(key->p) != 0) &&
-                              (key->q != NULL) && (mp_get_digit_count(key->q) != 0) &&
-                                 (key->dP != NULL) && (mp_get_digit_count(key->dP) != 0) &&
-                                    (key->dQ != NULL) && (mp_get_digit_count(key->dQ) != 0) &&
-                                       (key->qP != NULL) && (mp_get_digit_count(key->qP) != 0);
-
-      if (!has_crt_parameters) {
-         /*
-          * In case CRT optimization parameters are not provided,
-          * the private key is directly used to exptmod it
-          */
-         if ((err = mp_exptmod(tmp, key->d, key->N, tmp)) != CRYPT_OK)                              { goto error; }
-      } else {
-         /* tmpa = tmp^dP mod p */
-         if ((err = mp_exptmod(tmp, key->dP, key->p, tmpa)) != CRYPT_OK)                            { goto error; }
-
-         /* tmpb = tmp^dQ mod q */
-         if ((err = mp_exptmod(tmp, key->dQ, key->q, tmpb)) != CRYPT_OK)                            { goto error; }
-
-         /* tmp = (tmpa - tmpb) * qInv (mod p) */
-         if ((err = mp_sub(tmpa, tmpb, tmp)) != CRYPT_OK)                                           { goto error; }
-         if ((err = mp_mulmod(tmp, key->qP, key->p, tmp)) != CRYPT_OK)                              { goto error; }
-
-         /* tmp = tmpb + q * tmp */
-         if ((err = mp_mul(tmp, key->q, tmp)) != CRYPT_OK)                                          { goto error; }
-         if ((err = mp_add(tmp, tmpb, tmp)) != CRYPT_OK)                                            { goto error; }
-      }
-
-      #ifdef LTC_RSA_BLINDING
-      /* unblind */
-      err = mp_mulmod( tmp, rndi, key->N, tmp);
-      if (err != CRYPT_OK) {
-             goto error;
-      }
-      #endif
-
-      #ifdef LTC_RSA_CRT_HARDENING
-      if (has_crt_parameters) {
-         if ((err = mp_exptmod(tmp, key->e, key->N, tmpa)) != CRYPT_OK)                              { goto error; }
-         if ((err = mp_read_unsigned_bin(tmpb, (unsigned char *)in, (int)inlen)) != CRYPT_OK)        { goto error; }
-         if (mp_cmp(tmpa, tmpb) != LTC_MP_EQ)                                     { err = CRYPT_ERROR; goto error; }
-      }
-      #endif
-   } else {
-      /* exptmod it */
-      if ((err = mp_exptmod(tmp, key->e, key->N, tmp)) != CRYPT_OK)                                { goto error; }
-   }
-
-   /* read it back */
-   x = (unsigned long)mp_unsigned_bin_size(key->N);
-   if (x > *outlen) {
-      *outlen = x;
-      err = CRYPT_BUFFER_OVERFLOW;
-      goto error;
-   }
-
-   /* this should never happen ... */
-   if (mp_unsigned_bin_size(tmp) > mp_unsigned_bin_size(key->N)) {
-      err = CRYPT_ERROR;
-      goto error;
-   }
-   *outlen = x;
-
-   /* convert it */
-   zeromem(out, x);
-   if ((err = mp_to_unsigned_bin(tmp, out+(x-mp_unsigned_bin_size(tmp)))) != CRYPT_OK)               { goto error; }
-
-   /* clean up and return */
-   err = CRYPT_OK;
-error:
-   mp_clear_multi(
-#ifdef LTC_RSA_BLINDING
-                  rndi, rnd,
-#endif /* LTC_RSA_BLINDING */
-                             tmpb, tmpa, tmp, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_free.c b/libs/tomcrypt/src/pk/rsa/rsa_free.c
deleted file mode 100644
index 5f6b099dc829..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_free.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_free.c
-  Free an RSA key, Tom St Denis
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  Free an RSA key from memory
-  @param key   The RSA key to free
-*/
-void rsa_free(rsa_key *key)
-{
-   LTC_ARGCHKVD(key != NULL);
-   mp_cleanup_multi(&key->q, &key->p, &key->qP, &key->dP, &key->dQ, &key->N, &key->d, &key->e, NULL);
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_get_size.c b/libs/tomcrypt/src/pk/rsa/rsa_get_size.c
deleted file mode 100644
index 65df3ce01991..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_get_size.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_get_size.c
-  Retrieve the size of an RSA key, Steffen Jaeckel.
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  Retrieve the size in bytes of an RSA key.
-  @param key      The RSA key
-  @return The size in bytes of the RSA key or INT_MAX on error.
-*/
-int rsa_get_size(rsa_key *key)
-{
-  int ret = INT_MAX;
-  LTC_ARGCHK(key != NULL);
-
-  if (key)
-  {
-    ret = mp_unsigned_bin_size(key->N);
-  } /* if */
-
-  return ret;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_import.c b/libs/tomcrypt/src/pk/rsa/rsa_import.c
deleted file mode 100644
index 8ec117ae5f4b..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_import.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_import.c
-  Import a PKCS RSA key, Tom St Denis
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  Import an RSAPublicKey or RSAPrivateKey [two-prime only, only support >= 1024-bit keys, defined in PKCS #1 v2.1]
-  @param in      The packet to import from
-  @param inlen   It's length (octets)
-  @param key     [out] Destination for newly imported key
-  @return CRYPT_OK if successful, upon error allocated memory is freed
-*/
-int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key)
-{
-   int           err;
-   void         *zero;
-   unsigned char *tmpbuf=NULL;
-   unsigned long tmpbuf_len;
-
-   LTC_ARGCHK(in          != NULL);
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init key */
-   if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ,
-                            &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* see if the OpenSSL DER format RSA public key will work */
-   tmpbuf_len = inlen;
-   tmpbuf = XCALLOC(1, tmpbuf_len);
-   if (tmpbuf == NULL) {
-       err = CRYPT_MEM;
-       goto LBL_ERR;
-   }
-
-   err = der_decode_subject_public_key_info(in, inlen,
-        PKA_RSA, tmpbuf, &tmpbuf_len,
-        LTC_ASN1_NULL, NULL, 0);
-
-   if (err == CRYPT_OK) { /* SubjectPublicKeyInfo format */
-
-      /* now it should be SEQUENCE { INTEGER, INTEGER } */
-      if ((err = der_decode_sequence_multi(tmpbuf, tmpbuf_len,
-                                           LTC_ASN1_INTEGER, 1UL, key->N,
-                                           LTC_ASN1_INTEGER, 1UL, key->e,
-                                           LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-      key->type = PK_PUBLIC;
-      err = CRYPT_OK;
-      goto LBL_FREE;
-   }
-
-   /* not SSL public key, try to match against PKCS #1 standards */
-   err = der_decode_sequence_multi(in, inlen, LTC_ASN1_INTEGER, 1UL, key->N,
-                                              LTC_ASN1_EOL,     0UL, NULL);
-
-   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
-      goto LBL_ERR;
-   }
-
-   if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) {
-      if ((err = mp_init(&zero)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-      /* it's a private key */
-      if ((err = der_decode_sequence_multi(in, inlen,
-                          LTC_ASN1_INTEGER, 1UL, zero,
-                          LTC_ASN1_INTEGER, 1UL, key->N,
-                          LTC_ASN1_INTEGER, 1UL, key->e,
-                          LTC_ASN1_INTEGER, 1UL, key->d,
-                          LTC_ASN1_INTEGER, 1UL, key->p,
-                          LTC_ASN1_INTEGER, 1UL, key->q,
-                          LTC_ASN1_INTEGER, 1UL, key->dP,
-                          LTC_ASN1_INTEGER, 1UL, key->dQ,
-                          LTC_ASN1_INTEGER, 1UL, key->qP,
-                          LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) {
-         mp_clear(zero);
-         goto LBL_ERR;
-      }
-      mp_clear(zero);
-      key->type = PK_PRIVATE;
-   } else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) {
-      /* we don't support multi-prime RSA */
-      err = CRYPT_PK_INVALID_TYPE;
-      goto LBL_ERR;
-   } else {
-      /* it's a public key and we lack e */
-      if ((err = der_decode_sequence_multi(in, inlen,
-                                     LTC_ASN1_INTEGER, 1UL, key->N,
-                                     LTC_ASN1_INTEGER, 1UL, key->e,
-                                     LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) {
-         goto LBL_ERR;
-      }
-      key->type = PK_PUBLIC;
-   }
-   err = CRYPT_OK;
-   goto LBL_FREE;
-
-LBL_ERR:
-   mp_clear_multi(key->d,  key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL);
-
-LBL_FREE:
-   if (tmpbuf != NULL)
-     XFREE(tmpbuf);
-
-   return err;
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_import_pkcs8.c b/libs/tomcrypt/src/pk/rsa/rsa_import_pkcs8.c
deleted file mode 100644
index 3c012ec98e84..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_import_pkcs8.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_import_pkcs8.c
-  Import a PKCS RSA key
-*/
-
-#ifdef LTC_MRSA
-
-/* Public-Key Cryptography Standards (PKCS) #8:
- * Private-Key Information Syntax Specification Version 1.2
- * https://tools.ietf.org/html/rfc5208
- *
- * PrivateKeyInfo ::= SEQUENCE {
- *      version                   Version,
- *      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
- *      privateKey                PrivateKey,
- *      attributes           [0]  IMPLICIT Attributes OPTIONAL }
- * where:
- * - Version ::= INTEGER
- * - PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
- * - PrivateKey ::= OCTET STRING
- * - Attributes ::= SET OF Attribute
- *
- * EncryptedPrivateKeyInfo ::= SEQUENCE {
- *        encryptionAlgorithm  EncryptionAlgorithmIdentifier,
- *        encryptedData        EncryptedData }
- * where:
- * - EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
- * - EncryptedData ::= OCTET STRING
- */
-
-/**
-  Import an RSAPublicKey or RSAPrivateKey in PKCS#8 format
-  @param in        The packet to import from
-  @param inlen     It's length (octets)
-  @param passwd    The password for decrypting privkey (NOT SUPPORTED YET)
-  @param passwdlen Password's length (octets)
-  @param key       [out] Destination for newly imported key
-  @return CRYPT_OK if successful, upon error allocated memory is freed
-*/
-int rsa_import_pkcs8(const unsigned char *in, unsigned long inlen,
-                     const void *passwd, unsigned long passwdlen,
-                     rsa_key *key)
-{
-   int           err;
-   void          *zero, *iter;
-   unsigned char *buf1 = NULL, *buf2 = NULL;
-   unsigned long buf1len, buf2len;
-   unsigned long oid[16];
-   oid_st        rsaoid;
-   ltc_asn1_list alg_seq[2], top_seq[3];
-   ltc_asn1_list alg_seq_e[2], key_seq_e[2], top_seq_e[2];
-   unsigned char *decrypted = NULL;
-   unsigned long decryptedlen;
-
-   LTC_ARGCHK(in          != NULL);
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* get RSA alg oid */
-   err = pk_get_oid(PKA_RSA, &rsaoid);
-   if (err != CRYPT_OK) { goto LBL_NOFREE; }
-
-   /* alloc buffers */
-   buf1len = inlen; /* approx. */
-   buf1 = XMALLOC(buf1len);
-   if (buf1 == NULL) { err = CRYPT_MEM; goto LBL_NOFREE; }
-   buf2len = inlen; /* approx. */
-   buf2 = XMALLOC(buf2len);
-   if (buf2 == NULL) { err = CRYPT_MEM; goto LBL_FREE1; }
-
-   /* init key */
-   err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, &zero, &iter, NULL);
-   if (err != CRYPT_OK) { goto LBL_FREE2; }
-
-   /* try to decode encrypted priv key */
-   LTC_SET_ASN1(key_seq_e, 0, LTC_ASN1_OCTET_STRING, buf1, buf1len);
-   LTC_SET_ASN1(key_seq_e, 1, LTC_ASN1_INTEGER, iter, 1UL);
-   LTC_SET_ASN1(alg_seq_e, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, 16UL);
-   LTC_SET_ASN1(alg_seq_e, 1, LTC_ASN1_SEQUENCE, key_seq_e, 2UL);
-   LTC_SET_ASN1(top_seq_e, 0, LTC_ASN1_SEQUENCE, alg_seq_e, 2UL);
-   LTC_SET_ASN1(top_seq_e, 1, LTC_ASN1_OCTET_STRING, buf2, buf2len);
-   err=der_decode_sequence(in, inlen, top_seq_e, 2UL);
-   if (err == CRYPT_OK) {
-      LTC_UNUSED_PARAM(passwd);
-      LTC_UNUSED_PARAM(passwdlen);
-      /* XXX: TODO encrypted pkcs8 not implemented yet */
-      /* fprintf(stderr, "decrypt: iter=%ld salt.len=%ld encdata.len=%ld\n", mp_get_int(iter), key_seq_e[0].size, top_seq_e[1].size); */
-      err = CRYPT_PK_INVALID_TYPE;
-      goto LBL_ERR;
-   }
-   else {
-      decrypted    = (unsigned char *)in;
-      decryptedlen = inlen;
-   }
-
-   /* try to decode unencrypted priv key */
-   LTC_SET_ASN1(alg_seq, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, 16UL);
-   LTC_SET_ASN1(alg_seq, 1, LTC_ASN1_NULL, NULL, 0UL);
-   LTC_SET_ASN1(top_seq, 0, LTC_ASN1_INTEGER, zero, 1UL);
-   LTC_SET_ASN1(top_seq, 1, LTC_ASN1_SEQUENCE, alg_seq, 2UL);
-   LTC_SET_ASN1(top_seq, 2, LTC_ASN1_OCTET_STRING, buf1, buf1len);
-   err=der_decode_sequence(decrypted, decryptedlen, top_seq, 3UL);
-   if (err != CRYPT_OK) { goto LBL_ERR; }
-
-   /* check alg oid */
-   if ((alg_seq[0].size != rsaoid.OIDlen) ||
-      XMEMCMP(rsaoid.OID, alg_seq[0].data, rsaoid.OIDlen * sizeof(rsaoid.OID[0])) != 0) {
-      err = CRYPT_PK_INVALID_TYPE;
-      goto LBL_ERR;
-   }
-
-   err = der_decode_sequence_multi(buf1, top_seq[2].size,
-                                   LTC_ASN1_INTEGER, 1UL, zero,
-                                   LTC_ASN1_INTEGER, 1UL, key->N,
-                                   LTC_ASN1_INTEGER, 1UL, key->e,
-                                   LTC_ASN1_INTEGER, 1UL, key->d,
-                                   LTC_ASN1_INTEGER, 1UL, key->p,
-                                   LTC_ASN1_INTEGER, 1UL, key->q,
-                                   LTC_ASN1_INTEGER, 1UL, key->dP,
-                                   LTC_ASN1_INTEGER, 1UL, key->dQ,
-                                   LTC_ASN1_INTEGER, 1UL, key->qP,
-                                   LTC_ASN1_EOL,     0UL, NULL);
-   if (err != CRYPT_OK) { goto LBL_ERR; }
-   key->type = PK_PRIVATE;
-   err = CRYPT_OK;
-   goto LBL_FREE2;
-
-LBL_ERR:
-   rsa_free(key);
-LBL_FREE2:
-   mp_clear_multi(iter, zero, NULL);
-   XFREE(buf2);
-LBL_FREE1:
-   XFREE(buf1);
-LBL_NOFREE:
-   return err;
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_import_x509.c b/libs/tomcrypt/src/pk/rsa/rsa_import_x509.c
deleted file mode 100644
index 457cbabcee31..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_import_x509.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_import.c
-  Import an RSA key from a X.509 certificate, Steffen Jaeckel
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  Import an RSA key from a X.509 certificate
-  @param in      The packet to import from
-  @param inlen   It's length (octets)
-  @param key     [out] Destination for newly imported key
-  @return CRYPT_OK if successful, upon error allocated memory is freed
-*/
-int rsa_import_x509(const unsigned char *in, unsigned long inlen, rsa_key *key)
-{
-   int           err;
-   unsigned char *tmpbuf;
-   unsigned long tmpbuf_len, tmp_inlen;
-   ltc_asn1_list *decoded_list = NULL, *l;
-
-   LTC_ARGCHK(in          != NULL);
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   /* init key */
-   if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ,
-                            &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   tmpbuf_len = inlen;
-   tmpbuf = XCALLOC(1, tmpbuf_len);
-   if (tmpbuf == NULL) {
-       err = CRYPT_MEM;
-       goto LBL_ERR;
-   }
-
-   tmp_inlen = inlen;
-   if ((err = der_decode_sequence_flexi(in, &tmp_inlen, &decoded_list)) == CRYPT_OK) {
-      l = decoded_list;
-      /* Move 2 levels up in the tree
-         SEQUENCE
-             SEQUENCE
-                 ...
-       */
-      if (l->type == LTC_ASN1_SEQUENCE && l->child) {
-         l = l->child;
-         if (l->type == LTC_ASN1_SEQUENCE && l->child) {
-            l = l->child;
-
-            err = CRYPT_ERROR;
-
-            /* Move forward in the tree until we find this combination
-                 ...
-                 SEQUENCE
-                     SEQUENCE
-                         OBJECT IDENTIFIER 1.2.840.113549.1.1.1
-                         NULL
-                     BIT STRING
-             */
-            do {
-               /* The additional check for l->data is there to make sure
-                * we won't try to decode a list that has been 'shrunk'
-                */
-               if (l->type == LTC_ASN1_SEQUENCE && l->data && l->child &&
-                     l->child->type == LTC_ASN1_SEQUENCE && l->child->child &&
-                     l->child->child->type == LTC_ASN1_OBJECT_IDENTIFIER && l->child->next &&
-                     l->child->next->type == LTC_ASN1_BIT_STRING) {
-                  err = der_decode_subject_public_key_info(l->data, l->size,
-                       PKA_RSA, tmpbuf, &tmpbuf_len,
-                       LTC_ASN1_NULL, NULL, 0);
-                  if (err == CRYPT_OK) {
-                     /* now it should be SEQUENCE { INTEGER, INTEGER } */
-                     if ((err = der_decode_sequence_multi(tmpbuf, tmpbuf_len,
-                                                          LTC_ASN1_INTEGER, 1UL, key->N,
-                                                          LTC_ASN1_INTEGER, 1UL, key->e,
-                                                          LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) {
-                        goto LBL_ERR;
-                     }
-                     key->type = PK_PUBLIC;
-                     err = CRYPT_OK;
-                     goto LBL_FREE;
-                  }
-               }
-               l = l->next;
-            } while(l);
-         }
-      }
-   }
-
-
-LBL_ERR:
-   rsa_free(key);
-
-LBL_FREE:
-   if (decoded_list) der_free_sequence_flexi(decoded_list);
-   if (tmpbuf != NULL) XFREE(tmpbuf);
-
-   return err;
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_make_key.c b/libs/tomcrypt/src/pk/rsa/rsa_make_key.c
deleted file mode 100644
index 51c6d05c4224..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_make_key.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_make_key.c
-  RSA key generation, Tom St Denis
-*/
-
-#ifdef LTC_MRSA
-
-/**
-   Create an RSA key
-   @param prng     An active PRNG state
-   @param wprng    The index of the PRNG desired
-   @param size     The size of the modulus (key size) desired (octets)
-   @param e        The "e" value (public key).  e==65537 is a good choice
-   @param key      [out] Destination of a newly created private key pair
-   @return CRYPT_OK if successful, upon error all allocated ram is freed
-*/
-int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key)
-{
-   void *p, *q, *tmp1, *tmp2, *tmp3;
-   int    err;
-
-   LTC_ARGCHK(ltc_mp.name != NULL);
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(size        > 0);
-
-   if ((e < 3) || ((e & 1) == 0)) {
-      return CRYPT_INVALID_ARG;
-   }
-
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != CRYPT_OK) {
-      return err;
-   }
-
-   /* make primes p and q (optimization provided by Wayne Scott) */
-   if ((err = mp_set_int(tmp3, e)) != CRYPT_OK)                      { goto cleanup; }  /* tmp3 = e */
-
-   /* make prime "p" */
-   do {
-       if ((err = rand_prime( p, size/2, prng, wprng)) != CRYPT_OK)  { goto cleanup; }
-       if ((err = mp_sub_d( p, 1,  tmp1)) != CRYPT_OK)               { goto cleanup; }  /* tmp1 = p-1 */
-       if ((err = mp_gcd( tmp1,  tmp3,  tmp2)) != CRYPT_OK)          { goto cleanup; }  /* tmp2 = gcd(p-1, e) */
-   } while (mp_cmp_d( tmp2, 1) != 0);                                                  /* while e divides p-1 */
-
-   /* make prime "q" */
-   do {
-       if ((err = rand_prime( q, size/2, prng, wprng)) != CRYPT_OK)  { goto cleanup; }
-       if ((err = mp_sub_d( q, 1,  tmp1)) != CRYPT_OK)               { goto cleanup; } /* tmp1 = q-1 */
-       if ((err = mp_gcd( tmp1,  tmp3,  tmp2)) != CRYPT_OK)          { goto cleanup; } /* tmp2 = gcd(q-1, e) */
-   } while (mp_cmp_d( tmp2, 1) != 0);                                                 /* while e divides q-1 */
-
-   /* tmp1 = lcm(p-1, q-1) */
-   if ((err = mp_sub_d( p, 1,  tmp2)) != CRYPT_OK)                   { goto cleanup; } /* tmp2 = p-1 */
-                                                                                      /* tmp1 = q-1 (previous do/while loop) */
-   if ((err = mp_lcm( tmp1,  tmp2,  tmp1)) != CRYPT_OK)              { goto cleanup; } /* tmp1 = lcm(p-1, q-1) */
-
-   /* make key */
-   if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) {
-      goto errkey;
-   }
-
-   if ((err = mp_set_int( key->e, e)) != CRYPT_OK)                     { goto errkey; } /* key->e =  e */
-   if ((err = mp_invmod( key->e,  tmp1,  key->d)) != CRYPT_OK)         { goto errkey; } /* key->d = 1/e mod lcm(p-1,q-1) */
-   if ((err = mp_mul( p,  q,  key->N)) != CRYPT_OK)                    { goto errkey; } /* key->N = pq */
-
-   /* optimize for CRT now */
-   /* find d mod q-1 and d mod p-1 */
-   if ((err = mp_sub_d( p, 1,  tmp1)) != CRYPT_OK)                     { goto errkey; } /* tmp1 = q-1 */
-   if ((err = mp_sub_d( q, 1,  tmp2)) != CRYPT_OK)                     { goto errkey; } /* tmp2 = p-1 */
-   if ((err = mp_mod( key->d,  tmp1,  key->dP)) != CRYPT_OK)           { goto errkey; } /* dP = d mod p-1 */
-   if ((err = mp_mod( key->d,  tmp2,  key->dQ)) != CRYPT_OK)           { goto errkey; } /* dQ = d mod q-1 */
-   if ((err = mp_invmod( q,  p,  key->qP)) != CRYPT_OK)                { goto errkey; } /* qP = 1/q mod p */
-
-   if ((err = mp_copy( p,  key->p)) != CRYPT_OK)                       { goto errkey; }
-   if ((err = mp_copy( q,  key->q)) != CRYPT_OK)                       { goto errkey; }
-
-   /* set key type (in this case it's CRT optimized) */
-   key->type = PK_PRIVATE;
-
-   /* return ok and free temps */
-   err       = CRYPT_OK;
-   goto cleanup;
-errkey:
-   rsa_free(key);
-cleanup:
-   mp_clear_multi(tmp3, tmp2, tmp1, q, p, NULL);
-   return err;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_set.c b/libs/tomcrypt/src/pk/rsa/rsa_set.c
deleted file mode 100644
index 88aa37df6aab..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_set.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-
-#ifdef LTC_MRSA
-
-/**
-  Import RSA key from raw numbers
-
-  @param N       RSA's N
-  @param Nlen    RSA's N's length
-  @param e       RSA's e
-  @param elen    RSA's e's length
-  @param d       RSA's d  (only private key, NULL for public key)
-  @param dlen    RSA's d's length
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful
-*/
-int rsa_set_key(const unsigned char *N,  unsigned long Nlen,
-                const unsigned char *e,  unsigned long elen,
-                const unsigned char *d,  unsigned long dlen,
-                rsa_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(N           != NULL);
-   LTC_ARGCHK(e           != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL);
-   if (err != CRYPT_OK) return err;
-
-   if ((err = mp_read_unsigned_bin(key->N , (unsigned char *)N , Nlen)) != CRYPT_OK)    { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->e , (unsigned char *)e , elen)) != CRYPT_OK)    { goto LBL_ERR; }
-   if (d && dlen) {
-      if ((err = mp_read_unsigned_bin(key->d , (unsigned char *)d , dlen)) != CRYPT_OK) { goto LBL_ERR; }
-      key->type = PK_PRIVATE;
-   }
-   else {
-      key->type = PK_PUBLIC;
-   }
-   return CRYPT_OK;
-
-LBL_ERR:
-   rsa_free(key);
-   return err;
-}
-
-/**
-  Import factors of an RSA key from raw numbers
-
-  Only for private keys.
-
-  @param p       RSA's p
-  @param plen    RSA's p's length
-  @param q       RSA's q
-  @param qlen    RSA's q's length
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful
-*/
-int rsa_set_factors(const unsigned char *p,  unsigned long plen,
-                    const unsigned char *q,  unsigned long qlen,
-                    rsa_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(p           != NULL);
-   LTC_ARGCHK(q           != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   if (key->type != PK_PRIVATE) return CRYPT_PK_TYPE_MISMATCH;
-
-   if ((err = mp_read_unsigned_bin(key->p , (unsigned char *)p , plen)) != CRYPT_OK) { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->q , (unsigned char *)q , qlen)) != CRYPT_OK) { goto LBL_ERR; }
-   return CRYPT_OK;
-
-LBL_ERR:
-   rsa_free(key);
-   return err;
-}
-
-/**
-  Import CRT parameters of an RSA key from raw numbers
-
-  Only for private keys.
-
-  @param dP      RSA's dP
-  @param dPlen   RSA's dP's length
-  @param dQ      RSA's dQ
-  @param dQlen   RSA's dQ's length
-  @param qP      RSA's qP
-  @param qPlen   RSA's qP's length
-  @param key     [out] the destination for the imported key
-  @return CRYPT_OK if successful
-*/
-int rsa_set_crt_params(const unsigned char *dP, unsigned long dPlen,
-                       const unsigned char *dQ, unsigned long dQlen,
-                       const unsigned char *qP, unsigned long qPlen,
-                       rsa_key *key)
-{
-   int err;
-
-   LTC_ARGCHK(key         != NULL);
-   LTC_ARGCHK(dP          != NULL);
-   LTC_ARGCHK(dQ          != NULL);
-   LTC_ARGCHK(qP          != NULL);
-   LTC_ARGCHK(ltc_mp.name != NULL);
-
-   if (key->type != PK_PRIVATE) return CRYPT_PK_TYPE_MISMATCH;
-
-   if ((err = mp_read_unsigned_bin(key->dP, (unsigned char *)dP, dPlen)) != CRYPT_OK) { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->dQ, (unsigned char *)dQ, dQlen)) != CRYPT_OK) { goto LBL_ERR; }
-   if ((err = mp_read_unsigned_bin(key->qP, (unsigned char *)qP, qPlen)) != CRYPT_OK) { goto LBL_ERR; }
-   return CRYPT_OK;
-
-LBL_ERR:
-   rsa_free(key);
-   return err;
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_sign_hash.c b/libs/tomcrypt/src/pk/rsa/rsa_sign_hash.c
deleted file mode 100644
index f28593013691..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_sign_hash.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_sign_hash.c
-  RSA PKCS #1 v1.5 and v2 PSS sign hash, Tom St Denis and Andreas Lange
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  PKCS #1 pad then sign
-  @param in        The hash to sign
-  @param inlen     The length of the hash to sign (octets)
-  @param out       [out] The signature
-  @param outlen    [in/out] The max size and resulting size of the signature
-  @param padding   Type of padding (LTC_PKCS_1_PSS, LTC_PKCS_1_V1_5 or LTC_PKCS_1_V1_5_NA1)
-  @param prng      An active PRNG state
-  @param prng_idx  The index of the PRNG desired
-  @param hash_idx  The index of the hash desired
-  @param saltlen   The length of the salt desired (octets)
-  @param key       The private RSA key to use
-  @return CRYPT_OK if successful
-*/
-int rsa_sign_hash_ex(const unsigned char *in,       unsigned long  inlen,
-                           unsigned char *out,      unsigned long *outlen,
-                           int            padding,
-                           prng_state    *prng,     int            prng_idx,
-                           int            hash_idx, unsigned long  saltlen,
-                           rsa_key *key)
-{
-   unsigned long modulus_bitlen, modulus_bytelen, x, y;
-   int           err;
-
-   LTC_ARGCHK(in       != NULL);
-   LTC_ARGCHK(out      != NULL);
-   LTC_ARGCHK(outlen   != NULL);
-   LTC_ARGCHK(key      != NULL);
-
-   /* valid padding? */
-   if ((padding != LTC_PKCS_1_V1_5) &&
-       (padding != LTC_PKCS_1_PSS) &&
-       (padding != LTC_PKCS_1_V1_5_NA1)) {
-     return CRYPT_PK_INVALID_PADDING;
-   }
-
-   if (padding == LTC_PKCS_1_PSS) {
-     /* valid prng ? */
-     if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
-        return err;
-     }
-   }
-
-   if (padding != LTC_PKCS_1_V1_5_NA1) {
-     /* valid hash ? */
-     if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-        return err;
-     }
-   }
-
-   /* get modulus len in bits */
-   modulus_bitlen = mp_count_bits((key->N));
-
-  /* outlen must be at least the size of the modulus */
-  modulus_bytelen = mp_unsigned_bin_size((key->N));
-  if (modulus_bytelen > *outlen) {
-     *outlen = modulus_bytelen;
-     return CRYPT_BUFFER_OVERFLOW;
-  }
-
-  if (padding == LTC_PKCS_1_PSS) {
-    /* PSS pad the key */
-    x = *outlen;
-    if ((err = pkcs_1_pss_encode(in, inlen, saltlen, prng, prng_idx,
-                                 hash_idx, modulus_bitlen, out, &x)) != CRYPT_OK) {
-       return err;
-    }
-  } else {
-    /* PKCS #1 v1.5 pad the hash */
-    unsigned char *tmpin;
-
-    if (padding == LTC_PKCS_1_V1_5) {
-      ltc_asn1_list digestinfo[2], siginfo[2];
-      /* not all hashes have OIDs... so sad */
-      if (hash_descriptor[hash_idx].OIDlen == 0) {
-         return CRYPT_INVALID_ARG;
-      }
-
-    /* construct the SEQUENCE
-        SEQUENCE {
-           SEQUENCE {hashoid OID
-                     blah    NULL
-           }
-         hash    OCTET STRING
-        }
-     */
-      LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash_idx].OID, hash_descriptor[hash_idx].OIDlen);
-      LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL,              NULL,                          0);
-      LTC_SET_ASN1(siginfo,    0, LTC_ASN1_SEQUENCE,          digestinfo,                    2);
-      LTC_SET_ASN1(siginfo,    1, LTC_ASN1_OCTET_STRING,      in,                            inlen);
-
-      /* allocate memory for the encoding */
-      y = mp_unsigned_bin_size(key->N);
-      tmpin = XMALLOC(y);
-      if (tmpin == NULL) {
-         return CRYPT_MEM;
-      }
-
-      if ((err = der_encode_sequence(siginfo, 2, tmpin, &y)) != CRYPT_OK) {
-         XFREE(tmpin);
-         return err;
-      }
-    } else {
-      /* set the pointer and data-length to the input values */
-      tmpin = (unsigned char *)in;
-      y = inlen;
-    }
-
-    x = *outlen;
-    err = pkcs_1_v1_5_encode(tmpin, y, LTC_PKCS_1_EMSA, modulus_bitlen, NULL, 0, out, &x);
-
-    if (padding == LTC_PKCS_1_V1_5) {
-      XFREE(tmpin);
-    }
-
-    if (err != CRYPT_OK) {
-      return err;
-    }
-  }
-
-  /* RSA encode it */
-  return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key);
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_sign_saltlen_get.c b/libs/tomcrypt/src/pk/rsa/rsa_sign_saltlen_get.c
deleted file mode 100644
index a622bbdeb7c1..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_sign_saltlen_get.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_sign_saltlen_get.c
-  Retrieve the maximum size of the salt, Steffen Jaeckel.
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  Retrieve the maximum possible size of the salt when creating a PKCS#1 PSS signature.
-  @param padding    Type of padding (LTC_PKCS_1_PSS only)
-  @param hash_idx   The index of the desired hash
-  @param key        The RSA key
-  @return The maximum salt length in bytes or INT_MAX on error.
-*/
-int rsa_sign_saltlen_get_max_ex(int padding, int hash_idx, rsa_key *key)
-{
-  int ret = INT_MAX;
-  LTC_ARGCHK(key != NULL);
-
-  if ((hash_is_valid(hash_idx) == CRYPT_OK) &&
-      (padding == LTC_PKCS_1_PSS))
-  {
-    ret = rsa_get_size(key);
-    if (ret < INT_MAX)
-    {
-      ret -= (hash_descriptor[hash_idx].hashsize + 2);
-    } /* if */
-  } /* if */
-
-  return ret;
-}
-
-#endif
diff --git a/libs/tomcrypt/src/pk/rsa/rsa_verify_hash.c b/libs/tomcrypt/src/pk/rsa/rsa_verify_hash.c
deleted file mode 100644
index 353ffdc7319d..000000000000
--- a/libs/tomcrypt/src/pk/rsa/rsa_verify_hash.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file rsa_verify_hash.c
-  RSA PKCS #1 v1.5 or v2 PSS signature verification, Tom St Denis and Andreas Lange
-*/
-
-#ifdef LTC_MRSA
-
-/**
-  PKCS #1 de-sign then v1.5 or PSS depad
-  @param sig              The signature data
-  @param siglen           The length of the signature data (octets)
-  @param hash             The hash of the message that was signed
-  @param hashlen          The length of the hash of the message that was signed (octets)
-  @param padding          Type of padding (LTC_PKCS_1_PSS, LTC_PKCS_1_V1_5 or LTC_PKCS_1_V1_5_NA1)
-  @param hash_idx         The index of the desired hash
-  @param saltlen          The length of the salt used during signature
-  @param stat             [out] The result of the signature comparison, 1==valid, 0==invalid
-  @param key              The public RSA key corresponding to the key that performed the signature
-  @return CRYPT_OK on success (even if the signature is invalid)
-*/
-int rsa_verify_hash_ex(const unsigned char *sig,      unsigned long siglen,
-                       const unsigned char *hash,     unsigned long hashlen,
-                             int            padding,
-                             int            hash_idx, unsigned long saltlen,
-                             int           *stat,     rsa_key      *key)
-{
-  unsigned long modulus_bitlen, modulus_bytelen, x;
-  int           err;
-  unsigned char *tmpbuf;
-
-  LTC_ARGCHK(hash  != NULL);
-  LTC_ARGCHK(sig   != NULL);
-  LTC_ARGCHK(stat  != NULL);
-  LTC_ARGCHK(key   != NULL);
-
-  /* default to invalid */
-  *stat = 0;
-
-  /* valid padding? */
-
-  if ((padding != LTC_PKCS_1_V1_5) &&
-      (padding != LTC_PKCS_1_PSS) &&
-      (padding != LTC_PKCS_1_V1_5_NA1)) {
-    return CRYPT_PK_INVALID_PADDING;
-  }
-
-  if (padding != LTC_PKCS_1_V1_5_NA1) {
-    /* valid hash ? */
-    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
-       return err;
-    }
-  }
-
-  /* get modulus len in bits */
-  modulus_bitlen = mp_count_bits( (key->N));
-
-  /* outlen must be at least the size of the modulus */
-  modulus_bytelen = mp_unsigned_bin_size( (key->N));
-  if (modulus_bytelen != siglen) {
-     return CRYPT_INVALID_PACKET;
-  }
-
-  /* allocate temp buffer for decoded sig */
-  tmpbuf = XMALLOC(siglen);
-  if (tmpbuf == NULL) {
-     return CRYPT_MEM;
-  }
-
-  /* RSA decode it  */
-  x = siglen;
-  if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) {
-     XFREE(tmpbuf);
-     return err;
-  }
-
-  /* make sure the output is the right size */
-  if (x != siglen) {
-     XFREE(tmpbuf);
-     return CRYPT_INVALID_PACKET;
-  }
-
-  if (padding == LTC_PKCS_1_PSS) {
-    /* PSS decode and verify it */
-
-    if(modulus_bitlen%8 == 1){
-      err = pkcs_1_pss_decode(hash, hashlen, tmpbuf+1, x-1, saltlen, hash_idx, modulus_bitlen, stat);
-    }
-    else{
-      err = pkcs_1_pss_decode(hash, hashlen, tmpbuf, x, saltlen, hash_idx, modulus_bitlen, stat);
-    }
-
-  } else {
-    /* PKCS #1 v1.5 decode it */
-    unsigned char *out;
-    unsigned long outlen;
-    int           decoded;
-
-    /* allocate temp buffer for decoded hash */
-    outlen = ((modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0)) - 3;
-    out    = XMALLOC(outlen);
-    if (out == NULL) {
-      err = CRYPT_MEM;
-      goto bail_2;
-    }
-
-    if ((err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_PKCS_1_EMSA, modulus_bitlen, out, &outlen, &decoded)) != CRYPT_OK) {
-      XFREE(out);
-      goto bail_2;
-    }
-
-    if (padding == LTC_PKCS_1_V1_5) {
-      unsigned long loid[16], reallen;
-      ltc_asn1_list digestinfo[2], siginfo[2];
-
-      /* not all hashes have OIDs... so sad */
-      if (hash_descriptor[hash_idx].OIDlen == 0) {
-         err = CRYPT_INVALID_ARG;
-         goto bail_2;
-      }
-
-      /* now we must decode out[0...outlen-1] using ASN.1, test the OID and then test the hash */
-      /* construct the SEQUENCE
-        SEQUENCE {
-           SEQUENCE {hashoid OID
-                     blah    NULL
-           }
-           hash    OCTET STRING
-        }
-     */
-      LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, loid, sizeof(loid)/sizeof(loid[0]));
-      LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL,              NULL,                          0);
-      LTC_SET_ASN1(siginfo,    0, LTC_ASN1_SEQUENCE,          digestinfo,                    2);
-      LTC_SET_ASN1(siginfo,    1, LTC_ASN1_OCTET_STRING,      tmpbuf,                        siglen);
-
-      if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) {
-         /* fallback to Legacy:missing NULL */
-         LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE,          digestinfo,                    1);
-         if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) {
-           XFREE(out);
-           goto bail_2;
-         }
-      }
-
-      if ((err = der_length_sequence(siginfo, 2, &reallen)) != CRYPT_OK) {
-         XFREE(out);
-         goto bail_2;
-      }
-
-      /* test OID */
-      if ((reallen == outlen) &&
-          (digestinfo[0].size == hash_descriptor[hash_idx].OIDlen) &&
-        (XMEMCMP(digestinfo[0].data, hash_descriptor[hash_idx].OID, sizeof(unsigned long) * hash_descriptor[hash_idx].OIDlen) == 0) &&
-          (siginfo[1].size == hashlen) &&
-        (XMEMCMP(siginfo[1].data, hash, hashlen) == 0)) {
-         *stat = 1;
-      }
-    } else {
-      /* only check if the hash is equal */
-      if ((hashlen == outlen) &&
-          (XMEMCMP(out, hash, hashlen) == 0)) {
-        *stat = 1;
-      }
-    }
-
-#ifdef LTC_CLEAN_STACK
-    zeromem(out, outlen);
-#endif
-    XFREE(out);
-  }
-
-bail_2:
-#ifdef LTC_CLEAN_STACK
-  zeromem(tmpbuf, siglen);
-#endif
-  XFREE(tmpbuf);
-  return err;
-}
-
-#endif /* LTC_MRSA */
diff --git a/libs/tomcrypt/src/prngs/rc4.c b/libs/tomcrypt/src/prngs/rc4.c
deleted file mode 100644
index 05f4dd1c44e7..000000000000
--- a/libs/tomcrypt/src/prngs/rc4.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-  @file prngs/rc4.c
-  RC4 PRNG, Tom St Denis
-*/
-
-#ifdef LTC_RC4
-
-const struct ltc_prng_descriptor rc4_desc =
-{
-   "rc4",
-   32,
-   &rc4_start,
-   &rc4_add_entropy,
-   &rc4_ready,
-   &rc4_read,
-   &rc4_done,
-   &rc4_export,
-   &rc4_import,
-   &rc4_test
-};
-
-/**
-  Start the PRNG
-  @param prng     [out] The PRNG state to initialize
-  @return CRYPT_OK if successful
-*/
-int rc4_start(prng_state *prng)
-{
-   LTC_ARGCHK(prng != NULL);
-   prng->ready = 0;
-   /* set entropy (key) size to zero */
-   prng->rc4.s.x = 0;
-   /* clear entropy (key) buffer */
-   XMEMSET(&prng->rc4.s.buf, 0, sizeof(prng->rc4.s.buf));
-   LTC_MUTEX_INIT(&prng->lock)
-   return CRYPT_OK;
-}
-
-/**
-  Add entropy to the PRNG state
-  @param in       The data to add
-  @param inlen    Length of the data to add
-  @param prng     PRNG state to update
-  @return CRYPT_OK if successful
-*/
-int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
-{
-   unsigned char buf[256];
-   unsigned long i;
-   int err;
-
-   LTC_ARGCHK(prng != NULL);
-   LTC_ARGCHK(in != NULL);
-   LTC_ARGCHK(inlen > 0);
-
-   LTC_MUTEX_LOCK(&prng->lock);
-   if (prng->ready) {
-      /* rc4_ready() was already called, do "rekey" operation */
-      if ((err = rc4_stream_keystream(&prng->rc4.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
-      for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i];
-      /* initialize RC4 */
-      if ((err = rc4_stream_setup(&prng->rc4.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
-      /* drop first 3072 bytes - https://en.wikipedia.org/wiki/RC4#Fluhrer.2C_Mantin_and_Shamir_attack */
-      for (i = 0; i < 12; i++) rc4_stream_keystream(&prng->rc4.s, buf, sizeof(buf));
-      zeromem(buf, sizeof(buf));
-   }
-   else {
-      /* rc4_ready() was not called yet, add entropy to the buffer */
-      while (inlen--) prng->rc4.s.buf[prng->rc4.s.x++ % sizeof(prng->rc4.s.buf)] ^= *in++;
-   }
-   err = CRYPT_OK;
-LBL_UNLOCK:
-   LTC_MUTEX_UNLOCK(&prng->lock);
-   return err;
-}
-
-/**
-  Make the PRNG ready to read from
-  @param prng   The PRNG to make active
-  @return CRYPT_OK if successful
-*/
-int rc4_ready(prng_state *prng)
-{
-   unsigned char buf[256] = { 0 };
-   unsigned long len;
-   int err, i;
-
-   LTC_ARGCHK(prng != NULL);
-
-   LTC_MUTEX_LOCK(&prng->lock);
-   if (prng->ready) { err = CRYPT_OK; goto LBL_UNLOCK; }
-   XMEMCPY(buf, prng->rc4.s.buf, sizeof(buf));
-   /* initialize RC4 */
-   len = MIN(prng->rc4.s.x, 256); /* TODO: we can perhaps always use all 256 bytes */
-   if ((err = rc4_stream_setup(&prng->rc4.s, buf, len)) != CRYPT_OK) goto LBL_UNLOCK;
-   /* drop first 3072 bytes - https://en.wikipedia.org/wiki/RC4#Fluhrer.2C_Mantin_and_Shamir_attack */
-   for (i = 0; i < 12; i++) rc4_stream_keystream(&prng->rc4.s, buf, sizeof(buf));
-   prng->ready = 1;
-LBL_UNLOCK:
-   LTC_MUTEX_UNLOCK(&prng->lock);
-   return err;
-}
-
-/**
-  Read from the PRNG
-  @param out      Destination
-  @param outlen   Length of output
-  @param prng     The active PRNG to read from
-  @return Number of octets read
-*/
-unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng)
-{
-   if (outlen == 0 || prng == NULL || out == NULL) return 0;
-   LTC_MUTEX_LOCK(&prng->lock);
-   if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; }
-   if (rc4_stream_keystream(&prng->rc4.s, out, outlen) != CRYPT_OK) outlen = 0;
-LBL_UNLOCK:
-   LTC_MUTEX_UNLOCK(&prng->lock);
-   return outlen;
-}
-
-/**
-  Terminate the PRNG
-  @param prng   The PRNG to terminate
-  @return CRYPT_OK if successful
-*/
-int rc4_done(prng_state *prng)
-{
-   int err;
-   LTC_ARGCHK(prng != NULL);
-   LTC_MUTEX_LOCK(&prng->lock);
-   prng->ready = 0;
-   err = rc4_stream_done(&prng->rc4.s);
-   LTC_MUTEX_UNLOCK(&prng->lock);
-   LTC_MUTEX_DESTROY(&prng->lock);
-   return err;
-}
-
-/**
-  Export the PRNG state
-  @param out       [out] Destination
-  @param outlen    [in/out] Max size and resulting size of the state
-  @param prng      The PRNG to export
-  @return CRYPT_OK if successful
-*/
-int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
-{
-   unsigned long len = rc4_desc.export_size;
-
-   LTC_ARGCHK(prng   != NULL);
-   LTC_ARGCHK(out    != NULL);
-   LTC_ARGCHK(outlen != NULL);
-
-   if (*outlen < len) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
-   if (rc4_read(out, len, prng) != len) {
-      return CRYPT_ERROR_READPRNG;
-   }
-
-   *outlen = len;
-   return CRYPT_OK;
-}
-
-/**
-  Import a PRNG state
-  @param in       The PRNG state
-  @param inlen    Size of the state
-  @param prng     The PRNG to import
-  @return CRYPT_OK if successful
-*/
-int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
-{
-   int err;
-
-   LTC_ARGCHK(prng != NULL);
-   LTC_ARGCHK(in   != NULL);
-   if (inlen < (unsigned long)rc4_desc.export_size) return CRYPT_INVALID_ARG;
-
-   if ((err = rc4_start(prng)) != CRYPT_OK)                  return err;
-   if ((err = rc4_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
-   return CRYPT_OK;
-}
-
-/**
-  PRNG self-test
-  @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
-*/
-int rc4_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   prng_state st;
-   unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
-                          0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
-                          0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
-                          0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
-                          0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 };
-   unsigned char dmp[500];
-   unsigned long dmplen = sizeof(dmp);
-   unsigned char out[1000];
-   unsigned char t1[] = { 0xE0, 0x4D, 0x9A, 0xF6, 0xA8, 0x9D, 0x77, 0x53, 0xAE, 0x09 };
-   unsigned char t2[] = { 0xEF, 0x80, 0xA2, 0xE6, 0x50, 0x91, 0xF3, 0x17, 0x4A, 0x8A };
-   unsigned char t3[] = { 0x4B, 0xD6, 0x5C, 0x67, 0x99, 0x03, 0x56, 0x12, 0x80, 0x48 };
-   int err;
-
-   if ((err = rc4_start(&st)) != CRYPT_OK)                         return err;
-   /* add entropy to uninitialized prng */
-   if ((err = rc4_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
-   if ((err = rc4_ready(&st)) != CRYPT_OK)                         return err;
-   if (rc4_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
-   if (compare_testvector(out, 10, t1, sizeof(t1), "RC4-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR;
-   if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
-   /* add entropy to already initialized prng */
-   if ((err = rc4_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
-   if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
-   if ((err = rc4_export(dmp, &dmplen, &st)) != CRYPT_OK)          return err;
-   if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
-   if (rc4_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
-   if (compare_testvector(out, 10, t2, sizeof(t2), "RC4-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR;
-   if ((err = rc4_done(&st)) != CRYPT_OK)                          return err;
-   if ((err = rc4_import(dmp, dmplen, &st)) != CRYPT_OK)           return err;
-   if ((err = rc4_ready(&st)) != CRYPT_OK)                         return err;
-   if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
-   if (rc4_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
-   if (compare_testvector(out, 10, t3, sizeof(t3), "RC4-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR;
-   if ((err = rc4_done(&st)) != CRYPT_OK)                          return err;
-
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/prngs/rng_get_bytes.c b/libs/tomcrypt/src/prngs/rng_get_bytes.c
deleted file mode 100644
index 6a0cfade43b9..000000000000
--- a/libs/tomcrypt/src/prngs/rng_get_bytes.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_RNG_GET_BYTES
-/**
-   @file rng_get_bytes.c
-   portable way to get secure random bits to feed a PRNG (Tom St Denis)
-*/
-
-#if defined(LTC_DEVRANDOM) && !defined(_WIN32)
-/* on *NIX read /dev/random */
-static unsigned long _rng_nix(unsigned char *buf, unsigned long len,
-                             void (*callback)(void))
-{
-#ifdef LTC_NO_FILE
-    LTC_UNUSED_PARAM(callback);
-    LTC_UNUSED_PARAM(buf);
-    LTC_UNUSED_PARAM(len);
-    return 0;
-#else
-    FILE *f;
-    unsigned long x;
-    LTC_UNUSED_PARAM(callback);
-#ifdef LTC_TRY_URANDOM_FIRST
-    f = fopen("/dev/urandom", "rb");
-    if (f == NULL)
-#endif /* LTC_TRY_URANDOM_FIRST */
-       f = fopen("/dev/random", "rb");
-
-    if (f == NULL) {
-       return 0;
-    }
-
-    /* disable buffering */
-    if (setvbuf(f, NULL, _IONBF, 0) != 0) {
-       fclose(f);
-       return 0;
-    }
-
-    x = (unsigned long)fread(buf, 1, (size_t)len, f);
-    fclose(f);
-    return x;
-#endif /* LTC_NO_FILE */
-}
-
-#endif /* LTC_DEVRANDOM */
-
-#if !defined(_WIN32_WCE)
-
-#define ANSI_RNG
-
-static unsigned long _rng_ansic(unsigned char *buf, unsigned long len,
-                               void (*callback)(void))
-{
-   clock_t t1;
-   int l, acc, bits, a, b;
-
-   l = len;
-   bits = 8;
-   acc  = a = b = 0;
-   while (len--) {
-       if (callback != NULL) callback();
-       while (bits--) {
-          do {
-             t1 = XCLOCK(); while (t1 == XCLOCK()) a ^= 1;
-             t1 = XCLOCK(); while (t1 == XCLOCK()) b ^= 1;
-          } while (a == b);
-          acc = (acc << 1) | a;
-       }
-       *buf++ = acc;
-       acc  = 0;
-       bits = 8;
-   }
-   return l;
-}
-
-#endif
-
-/* Try the Microsoft CSP */
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#ifndef _WIN32_WINNT
-  #define _WIN32_WINNT 0x0400
-#endif
-#ifdef _WIN32_WCE
-   #define UNDER_CE
-   #define ARM
-#endif
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <ntsecapi.h>
-
-static unsigned long _rng_win32(unsigned char *buf, unsigned long len,
-                               void (*callback)(void))
-{
-   LTC_UNUSED_PARAM(callback);
-   RtlGenRandom(buf, len);
-   return len;
-}
-
-#endif /* WIN32 */
-
-/**
-  Read the system RNG
-  @param out       Destination
-  @param outlen    Length desired (octets)
-  @param callback  Pointer to void function to act as "callback" when RNG is slow.  This can be NULL
-  @return Number of octets read
-*/
-unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen,
-                            void (*callback)(void))
-{
-   unsigned long x;
-
-   LTC_ARGCHK(out != NULL);
-
-#ifdef LTC_PRNG_ENABLE_LTC_RNG
-   if (ltc_rng) {
-      x = ltc_rng(out, outlen, callback);
-      if (x != 0) {
-         return x;
-      }
-   }
-#endif
-
-#if defined(_WIN32) || defined(_WIN32_WCE)
-   x = _rng_win32(out, outlen, callback); if (x != 0) { return x; }
-#elif defined(LTC_DEVRANDOM)
-   x = _rng_nix(out, outlen, callback);   if (x != 0) { return x; }
-#endif
-#ifdef ANSI_RNG
-   x = _rng_ansic(out, outlen, callback); if (x != 0) { return x; }
-#endif
-   return 0;
-}
-#endif /* #ifdef LTC_RNG_GET_BYTES */
diff --git a/libs/tomcrypt/src/prngs/rng_make_prng.c b/libs/tomcrypt/src/prngs/rng_make_prng.c
deleted file mode 100644
index b42a047fb198..000000000000
--- a/libs/tomcrypt/src/prngs/rng_make_prng.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-#ifdef LTC_RNG_MAKE_PRNG
-/**
-  @file rng_make_prng.c
-  portable way to get secure random bits to feed a PRNG  (Tom St Denis)
-*/
-
-/**
-  Create a PRNG from a RNG
-  @param bits     Number of bits of entropy desired (64 ... 1024)
-  @param wprng    Index of which PRNG to setup
-  @param prng     [out] PRNG state to initialize
-  @param callback A pointer to a void function for when the RNG is slow, this can be NULL
-  @return CRYPT_OK if successful
-*/
-int rng_make_prng(int bits, int wprng, prng_state *prng,
-                  void (*callback)(void))
-{
-   unsigned char buf[256];
-   int err;
-
-   LTC_ARGCHK(prng != NULL);
-
-   /* check parameter */
-   if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
-      return err;
-   }
-
-   if (bits < 64 || bits > 1024) {
-      return CRYPT_INVALID_PRNGSIZE;
-   }
-
-   if ((err = prng_descriptor[wprng].start(prng)) != CRYPT_OK) {
-      return err;
-   }
-
-   bits = ((bits/8)+((bits&7)!=0?1:0)) * 2;
-   if (rng_get_bytes(buf, (unsigned long)bits, callback) != (unsigned long)bits) {
-      return CRYPT_ERROR_READPRNG;
-   }
-
-   if ((err = prng_descriptor[wprng].add_entropy(buf, (unsigned long)bits, prng)) != CRYPT_OK) {
-      return err;
-   }
-
-   if ((err = prng_descriptor[wprng].ready(prng)) != CRYPT_OK) {
-      return err;
-   }
-
-   #ifdef LTC_CLEAN_STACK
-      zeromem(buf, sizeof(buf));
-   #endif
-   return CRYPT_OK;
-}
-#endif /* #ifdef LTC_RNG_MAKE_PRNG */
diff --git a/libs/tomcrypt/src/prngs/sprng.c b/libs/tomcrypt/src/prngs/sprng.c
deleted file mode 100644
index a30cc3ed4244..000000000000
--- a/libs/tomcrypt/src/prngs/sprng.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-#include "tomcrypt.h"
-
-/**
-   @file sprng.c
-   Secure PRNG, Tom St Denis
-*/
-
-/* A secure PRNG using the RNG functions.  Basically this is a
- * wrapper that allows you to use a secure RNG as a PRNG
- * in the various other functions.
- */
-
-#ifdef LTC_SPRNG
-
-const struct ltc_prng_descriptor sprng_desc =
-{
-    "sprng", 0,
-    &sprng_start,
-    &sprng_add_entropy,
-    &sprng_ready,
-    &sprng_read,
-    &sprng_done,
-    &sprng_export,
-    &sprng_import,
-    &sprng_test
-};
-
-/**
-  Start the PRNG
-  @param prng     [out] The PRNG state to initialize
-  @return CRYPT_OK if successful
-*/
-int sprng_start(prng_state *prng)
-{
-   LTC_UNUSED_PARAM(prng);
-   return CRYPT_OK;
-}
-
-/**
-  Add entropy to the PRNG state
-  @param in       The data to add
-  @param inlen    Length of the data to add
-  @param prng     PRNG state to update
-  @return CRYPT_OK if successful
-*/
-int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
-{
-   LTC_UNUSED_PARAM(in);
-   LTC_UNUSED_PARAM(inlen);
-   LTC_UNUSED_PARAM(prng);
-   return CRYPT_OK;
-}
-
-/**
-  Make the PRNG ready to read from
-  @param prng   The PRNG to make active
-  @return CRYPT_OK if successful
-*/
-int sprng_ready(prng_state *prng)
-{
-   LTC_UNUSED_PARAM(prng);
-   return CRYPT_OK;
-}
-
-/**
-  Read from the PRNG
-  @param out      Destination
-  @param outlen   Length of output
-  @param prng     The active PRNG to read from
-  @return Number of octets read
-*/
-unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng)
-{
-   LTC_ARGCHK(out != NULL);
-   LTC_UNUSED_PARAM(prng);
-   return rng_get_bytes(out, outlen, NULL);
-}
-
-/**
-  Terminate the PRNG
-  @param prng   The PRNG to terminate
-  @return CRYPT_OK if successful
-*/
-int sprng_done(prng_state *prng)
-{
-   LTC_UNUSED_PARAM(prng);
-   return CRYPT_OK;
-}
-
-/**
-  Export the PRNG state
-  @param out       [out] Destination
-  @param outlen    [in/out] Max size and resulting size of the state
-  @param prng      The PRNG to export
-  @return CRYPT_OK if successful
-*/
-int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
-{
-   LTC_ARGCHK(outlen != NULL);
-   LTC_UNUSED_PARAM(out);
-   LTC_UNUSED_PARAM(prng);
-
-   *outlen = 0;
-   return CRYPT_OK;
-}
-
-/**
-  Import a PRNG state
-  @param in       The PRNG state
-  @param inlen    Size of the state
-  @param prng     The PRNG to import
-  @return CRYPT_OK if successful
-*/
-int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
-{
-  LTC_UNUSED_PARAM(in);
-  LTC_UNUSED_PARAM(inlen);
-  LTC_UNUSED_PARAM(prng);
-   return CRYPT_OK;
-}
-
-/**
-  PRNG self-test
-  @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
-*/
-int sprng_test(void)
-{
-#ifndef LTC_TEST
-   return CRYPT_NOP;
-#else
-   prng_state st;
-   unsigned char en[] = { 0x01, 0x02, 0x03, 0x04 };
-   unsigned char out[1000];
-   int err;
-
-   if ((err = sprng_start(&st)) != CRYPT_OK)                         return err;
-   if ((err = sprng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
-   if ((err = sprng_ready(&st)) != CRYPT_OK)                         return err;
-   if (sprng_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
-   if ((err = sprng_done(&st)) != CRYPT_OK)                          return err;
-
-   return CRYPT_OK;
-#endif
-}
-
-#endif
diff --git a/libs/tomcrypt/src/stream/rc4/rc4_stream.c b/libs/tomcrypt/src/stream/rc4/rc4_stream.c
deleted file mode 100644
index ec174a0c2b93..000000000000
--- a/libs/tomcrypt/src/stream/rc4/rc4_stream.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
-
-#include "tomcrypt.h"
-
-#ifdef LTC_RC4_STREAM
-
-/**
-   Initialize an RC4 context (only the key)
-   @param st        [out] The destination of the RC4 state
-   @param key       The secret key
-   @param keylen    The length of the secret key (8 - 256 bytes)
-   @return CRYPT_OK if successful
-*/
-int rc4_stream_setup(rc4_state *st, const unsigned char *key, unsigned long keylen)
-{
-   unsigned char tmp, *s;
-   int x, y;
-   unsigned long j;
-
-   LTC_ARGCHK(st  != NULL);
-   LTC_ARGCHK(key != NULL);
-   LTC_ARGCHK(keylen >= 5); /* 40-2048 bits */
-
-   s = st->buf;
-   for (x = 0; x < 256; x++) {
-      s[x] = x;
-   }
-
-   for (j = x = y = 0; x < 256; x++) {
-      y = (y + s[x] + key[j++]) & 255;
-      if (j == keylen) {
-         j = 0;
-      }
-      tmp = s[x]; s[x] = s[y]; s[y] = tmp;
-   }
-   st->x = 0;
-   st->y = 0;
-
-   return CRYPT_OK;
-}
-
-/**
-   Encrypt (or decrypt) bytes of ciphertext (or plaintext) with RC4
-   @param st      The RC4 state
-   @param in      The plaintext (or ciphertext)
-   @param inlen   The length of the input (octets)
-   @param out     [out] The ciphertext (or plaintext), length inlen
-   @return CRYPT_OK if successful
-*/
-int rc4_stream_crypt(rc4_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out)
-{
-   unsigned char x, y, *s, tmp;
-
-   LTC_ARGCHK(st  != NULL);
-   LTC_ARGCHK(in  != NULL);
-   LTC_ARGCHK(out != NULL);
-
-   x = st->x;
-   y = st->y;
-   s = st->buf;
-   while (inlen--) {
-      x = (x + 1) & 255;
-      y = (y + s[x]) & 255;
-      tmp = s[x]; s[x] = s[y]; s[y] = tmp;
-      tmp = (s[x] + s[y]) & 255;
-      *out++ = *in++ ^ s[tmp];
-   }
-   st->x = x;
-   st->y = y;
-   return CRYPT_OK;
-}
-
-/**
-  Generate a stream of random bytes via RC4
-  @param st      The RC420 state
-  @param out     [out] The output buffer
-  @param outlen  The output length
-  @return CRYPT_OK on success
- */
-int rc4_stream_keystream(rc4_state *st, unsigned char *out, unsigned long outlen)
-{
-   if (outlen == 0) return CRYPT_OK; /* nothing to do */
-   LTC_ARGCHK(out != NULL);
-   XMEMSET(out, 0, outlen);
-   return rc4_stream_crypt(st, out, outlen, out);
-}
-
-/**
-  Terminate and clear RC4 state
-  @param st      The RC4 state
-  @return CRYPT_OK on success
-*/
-int rc4_stream_done(rc4_state *st)
-{
-   LTC_ARGCHK(st != NULL);
-   XMEMSET(st, 0, sizeof(rc4_state));
-   return CRYPT_OK;
-}
-
-#endif
diff --git a/libs/vkd3d/Makefile.in b/libs/vkd3d/Makefile.in
index beaa3d7a7dd8..58e9a382c1c7 100644
--- a/libs/vkd3d/Makefile.in
+++ b/libs/vkd3d/Makefile.in
@@ -32,7 +32,6 @@ SOURCES = \
 	libs/vkd3d-shader/spirv.c \
 	libs/vkd3d-shader/tpf.c \
 	libs/vkd3d-shader/vkd3d_shader_main.c \
-	libs/vkd3d-utils/reflection.c \
 	libs/vkd3d-utils/vkd3d_utils_main.c \
 	libs/vkd3d/cache.c \
 	libs/vkd3d/command.c \
diff --git a/libs/vkd3d/libs/vkd3d-utils/reflection.c b/libs/vkd3d/libs/vkd3d-utils/reflection.c
deleted file mode 100644
index d6d9ce2a508f..000000000000
--- a/libs/vkd3d/libs/vkd3d-utils/reflection.c
+++ /dev/null
@@ -1,1116 +0,0 @@
-/*
- * Copyright 2009 Henri Verbeet for CodeWeavers
- * Copyright 2010 Rico Schüller
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "vkd3d_utils_private.h"
-#include <d3dcommon.h>
-#include <d3d12shader.h>
-
-struct d3d12_type
-{
-    ID3D12ShaderReflectionType ID3D12ShaderReflectionType_iface;
-    D3D12_SHADER_TYPE_DESC desc;
-
-    struct d3d12_field *fields;
-};
-
-struct d3d12_field
-{
-    struct d3d12_type type;
-};
-
-struct d3d12_variable
-{
-    ID3D12ShaderReflectionVariable ID3D12ShaderReflectionVariable_iface;
-    D3D12_SHADER_VARIABLE_DESC desc;
-    struct d3d12_buffer *buffer;
-
-    struct d3d12_type type;
-};
-
-struct d3d12_buffer
-{
-    ID3D12ShaderReflectionConstantBuffer ID3D12ShaderReflectionConstantBuffer_iface;
-    D3D12_SHADER_BUFFER_DESC desc;
-
-    struct d3d12_variable *variables;
-};
-
-struct d3d12_reflection
-{
-    ID3D12ShaderReflection ID3D12ShaderReflection_iface;
-    unsigned int refcount;
-
-    struct vkd3d_shader_scan_signature_info signature_info;
-
-    D3D12_SHADER_DESC desc;
-
-    struct d3d12_buffer *buffers;
-
-    D3D12_SHADER_INPUT_BIND_DESC *bindings;
-};
-
-static struct d3d12_buffer null_buffer;
-static struct d3d12_variable null_variable;
-static struct d3d12_type null_type;
-
-static struct d3d12_type *impl_from_ID3D12ShaderReflectionType(ID3D12ShaderReflectionType *iface)
-{
-    return CONTAINING_RECORD(iface, struct d3d12_type, ID3D12ShaderReflectionType_iface);
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_type_GetDesc(
-        ID3D12ShaderReflectionType *iface, D3D12_SHADER_TYPE_DESC *desc)
-{
-    struct d3d12_type *type = impl_from_ID3D12ShaderReflectionType(iface);
-
-    TRACE("iface %p, desc %p.\n", iface, desc);
-
-    if (type == &null_type)
-    {
-        WARN("Null type, returning E_FAIL.\n");
-        return E_FAIL;
-    }
-
-    if (!desc)
-    {
-        WARN("NULL pointer, returning E_FAIL.\n");
-        return E_FAIL;
-    }
-
-    *desc = type->desc;
-    return S_OK;
-}
-
-static ID3D12ShaderReflectionType * STDMETHODCALLTYPE d3d12_type_GetMemberTypeByIndex(
-        ID3D12ShaderReflectionType *iface, UINT index)
-{
-    struct d3d12_type *type = impl_from_ID3D12ShaderReflectionType(iface);
-
-    TRACE("iface %p, index %u.\n", iface, index);
-
-    if (index >= type->desc.Members)
-    {
-        WARN("Invalid index %u.\n", index);
-        return &null_type.ID3D12ShaderReflectionType_iface;
-    }
-
-    return &type->fields[index].type.ID3D12ShaderReflectionType_iface;
-}
-
-static ID3D12ShaderReflectionType * STDMETHODCALLTYPE d3d12_type_GetMemberTypeByName(
-        ID3D12ShaderReflectionType *iface, const char *name)
-{
-    FIXME("iface %p, name %s, stub!\n", iface, debugstr_a(name));
-
-    return NULL;
-}
-
-static const char * STDMETHODCALLTYPE d3d12_type_GetMemberTypeName(
-        ID3D12ShaderReflectionType *iface, UINT index)
-{
-    FIXME("iface %p, index %u, stub!\n", iface, index);
-
-    return NULL;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_type_IsEqual(
-        ID3D12ShaderReflectionType *iface, ID3D12ShaderReflectionType *other)
-{
-    FIXME("iface %p, other %p, stub!\n", iface, other);
-    return E_NOTIMPL;
-}
-
-static ID3D12ShaderReflectionType * STDMETHODCALLTYPE d3d12_type_GetSubType(
-        ID3D12ShaderReflectionType *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return NULL;
-}
-
-static ID3D12ShaderReflectionType * STDMETHODCALLTYPE d3d12_type_GetBaseClass(
-        ID3D12ShaderReflectionType *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return NULL;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_type_GetNumInterfaces(
-        ID3D12ShaderReflectionType *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static ID3D12ShaderReflectionType * STDMETHODCALLTYPE d3d12_type_GetInterfaceByIndex(
-        ID3D12ShaderReflectionType *iface, UINT index)
-{
-    FIXME("iface %p, index %u stub!\n", iface, index);
-
-    return NULL;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_type_IsOfType(
-        ID3D12ShaderReflectionType *iface, ID3D12ShaderReflectionType *type)
-{
-    FIXME("iface %p, type %p stub!\n", iface, type);
-
-    return E_NOTIMPL;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_type_ImplementsInterface(
-        ID3D12ShaderReflectionType *iface, ID3D12ShaderReflectionType *base)
-{
-    FIXME("iface %p, base %p stub!\n", iface, base);
-
-    return E_NOTIMPL;
-}
-
-static const struct ID3D12ShaderReflectionTypeVtbl d3d12_type_vtbl =
-{
-    d3d12_type_GetDesc,
-    d3d12_type_GetMemberTypeByIndex,
-    d3d12_type_GetMemberTypeByName,
-    d3d12_type_GetMemberTypeName,
-    d3d12_type_IsEqual,
-    d3d12_type_GetSubType,
-    d3d12_type_GetBaseClass,
-    d3d12_type_GetNumInterfaces,
-    d3d12_type_GetInterfaceByIndex,
-    d3d12_type_IsOfType,
-    d3d12_type_ImplementsInterface,
-};
-
-static struct d3d12_type null_type = {{&d3d12_type_vtbl}};
-
-static struct d3d12_variable *impl_from_ID3D12ShaderReflectionVariable(ID3D12ShaderReflectionVariable *iface)
-{
-    return CONTAINING_RECORD(iface, struct d3d12_variable, ID3D12ShaderReflectionVariable_iface);
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_variable_GetDesc(
-        ID3D12ShaderReflectionVariable *iface, D3D12_SHADER_VARIABLE_DESC *desc)
-{
-    struct d3d12_variable *variable = impl_from_ID3D12ShaderReflectionVariable(iface);
-
-    TRACE("iface %p, desc %p.\n", iface, desc);
-
-    if (variable == &null_variable)
-    {
-        WARN("Null variable, returning E_FAIL.\n");
-        return E_FAIL;
-    }
-
-    if (!desc)
-    {
-        WARN("NULL pointer, returning E_FAIL.\n");
-        return E_FAIL;
-    }
-
-    *desc = variable->desc;
-    return S_OK;
-}
-
-static ID3D12ShaderReflectionType * STDMETHODCALLTYPE d3d12_variable_GetType(
-        ID3D12ShaderReflectionVariable *iface)
-{
-    struct d3d12_variable *variable = impl_from_ID3D12ShaderReflectionVariable(iface);
-
-    TRACE("iface %p.\n", iface);
-
-    if (variable == &null_variable)
-        return &null_type.ID3D12ShaderReflectionType_iface;
-
-    return &variable->type.ID3D12ShaderReflectionType_iface;
-}
-
-static ID3D12ShaderReflectionConstantBuffer * STDMETHODCALLTYPE d3d12_variable_GetBuffer(
-        ID3D12ShaderReflectionVariable *iface)
-{
-    struct d3d12_variable *variable = impl_from_ID3D12ShaderReflectionVariable(iface);
-
-    TRACE("iface %p.\n", iface);
-
-    return &variable->buffer->ID3D12ShaderReflectionConstantBuffer_iface;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_variable_GetInterfaceSlot(
-        ID3D12ShaderReflectionVariable *iface, UINT index)
-{
-    FIXME("iface %p, index %u, stub!\n", iface, index);
-
-    return 0;
-}
-
-static const struct ID3D12ShaderReflectionVariableVtbl d3d12_variable_vtbl =
-{
-    d3d12_variable_GetDesc,
-    d3d12_variable_GetType,
-    d3d12_variable_GetBuffer,
-    d3d12_variable_GetInterfaceSlot,
-};
-
-static struct d3d12_variable null_variable = {{&d3d12_variable_vtbl}};
-
-static struct d3d12_buffer *impl_from_ID3D12ShaderReflectionConstantBuffer(ID3D12ShaderReflectionConstantBuffer *iface)
-{
-    return CONTAINING_RECORD(iface, struct d3d12_buffer, ID3D12ShaderReflectionConstantBuffer_iface);
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_buffer_GetDesc(
-        ID3D12ShaderReflectionConstantBuffer *iface, D3D12_SHADER_BUFFER_DESC *desc)
-{
-    struct d3d12_buffer *buffer = impl_from_ID3D12ShaderReflectionConstantBuffer(iface);
-
-    TRACE("iface %p, desc %p.\n", iface, desc);
-
-    if (buffer == &null_buffer)
-    {
-        WARN("Null constant buffer, returning E_FAIL.\n");
-        return E_FAIL;
-    }
-
-    if (!desc)
-    {
-        WARN("NULL pointer, returning E_FAIL.\n");
-        return E_FAIL;
-    }
-
-    *desc = buffer->desc;
-    return S_OK;
-}
-
-static ID3D12ShaderReflectionVariable * STDMETHODCALLTYPE d3d12_buffer_GetVariableByIndex(
-        ID3D12ShaderReflectionConstantBuffer *iface, UINT index)
-{
-    struct d3d12_buffer *buffer = impl_from_ID3D12ShaderReflectionConstantBuffer(iface);
-
-    TRACE("iface %p, index %u.\n", iface, index);
-
-    if (index >= buffer->desc.Variables)
-    {
-        WARN("Invalid index %u.\n", index);
-        return &null_variable.ID3D12ShaderReflectionVariable_iface;
-    }
-
-    return &buffer->variables[index].ID3D12ShaderReflectionVariable_iface;
-}
-
-static ID3D12ShaderReflectionVariable * STDMETHODCALLTYPE d3d12_buffer_GetVariableByName(
-        ID3D12ShaderReflectionConstantBuffer *iface, const char *name)
-{
-    FIXME("iface %p, name %s, stub!\n", iface, debugstr_a(name));
-
-    return NULL;
-}
-
-static const struct ID3D12ShaderReflectionConstantBufferVtbl d3d12_buffer_vtbl =
-{
-    d3d12_buffer_GetDesc,
-    d3d12_buffer_GetVariableByIndex,
-    d3d12_buffer_GetVariableByName,
-};
-
-static struct d3d12_buffer null_buffer = {{&d3d12_buffer_vtbl}};
-
-static struct d3d12_reflection *impl_from_ID3D12ShaderReflection(ID3D12ShaderReflection *iface)
-{
-    return CONTAINING_RECORD(iface, struct d3d12_reflection, ID3D12ShaderReflection_iface);
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_QueryInterface(
-        ID3D12ShaderReflection *iface, REFIID riid, void **object)
-{
-    TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object);
-
-    if (IsEqualGUID(riid, &IID_ID3D12ShaderReflection)
-            || IsEqualGUID(riid, &IID_IUnknown))
-    {
-        ID3D12ShaderReflection_AddRef(iface);
-        *object = iface;
-        return S_OK;
-    }
-
-    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
-
-    *object = NULL;
-    return E_NOINTERFACE;
-}
-
-static ULONG STDMETHODCALLTYPE d3d12_reflection_AddRef(ID3D12ShaderReflection *iface)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-    unsigned int refcount = vkd3d_atomic_increment_u32(&reflection->refcount);
-
-    TRACE("%p increasing refcount to %u.\n", reflection, refcount);
-
-    return refcount;
-}
-
-static void free_type(struct d3d12_type *type)
-{
-    for (UINT i = 0; i < type->desc.Members; ++i)
-        free_type(&type->fields[i].type);
-    vkd3d_free(type->fields);
-    vkd3d_free((void *)type->desc.Name);
-}
-
-static ULONG STDMETHODCALLTYPE d3d12_reflection_Release(ID3D12ShaderReflection *iface)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-    unsigned int refcount = vkd3d_atomic_decrement_u32(&reflection->refcount);
-
-    TRACE("%p decreasing refcount to %u.\n", reflection, refcount);
-
-    if (!refcount)
-    {
-        for (UINT i = 0; i < reflection->desc.ConstantBuffers; ++i)
-        {
-            struct d3d12_buffer *buffer = &reflection->buffers[i];
-
-            for (UINT j = 0; j < buffer->desc.Variables; ++j)
-            {
-                struct d3d12_variable *variable = &buffer->variables[j];
-
-                free_type(&variable->type);
-                vkd3d_free((void *)variable->desc.DefaultValue);
-                vkd3d_free((void *)variable->desc.Name);
-            }
-            vkd3d_free(buffer->variables);
-            vkd3d_free((void *)buffer->desc.Name);
-        }
-        vkd3d_free(reflection->buffers);
-
-        for (UINT i = 0; i < reflection->desc.BoundResources; ++i)
-            vkd3d_free((void *)reflection->bindings[i].Name);
-        vkd3d_free(reflection->bindings);
-
-        vkd3d_shader_free_scan_signature_info(&reflection->signature_info);
-        free(reflection);
-    }
-
-    return refcount;
-}
-
-/* ID3D12ShaderReflection methods */
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetDesc(ID3D12ShaderReflection *iface, D3D12_SHADER_DESC *desc)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-
-    FIXME("iface %p, desc %p partial stub!\n", iface, desc);
-
-    *desc = reflection->desc;
-
-    return S_OK;
-}
-
-static struct ID3D12ShaderReflectionConstantBuffer * STDMETHODCALLTYPE d3d12_reflection_GetConstantBufferByIndex(
-        ID3D12ShaderReflection *iface, UINT index)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-
-    TRACE("iface %p, index %u.\n", iface, index);
-
-    if (index >= reflection->desc.ConstantBuffers)
-    {
-        WARN("Invalid index %u.\n", index);
-        return &null_buffer.ID3D12ShaderReflectionConstantBuffer_iface;
-    }
-
-    return &reflection->buffers[index].ID3D12ShaderReflectionConstantBuffer_iface;
-}
-
-static struct ID3D12ShaderReflectionConstantBuffer * STDMETHODCALLTYPE d3d12_reflection_GetConstantBufferByName(
-        ID3D12ShaderReflection *iface, const char *name)
-{
-    FIXME("iface %p, name %s stub!\n", iface, debugstr_a(name));
-
-    return NULL;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetResourceBindingDesc(
-        ID3D12ShaderReflection *iface, UINT index, D3D12_SHADER_INPUT_BIND_DESC *desc)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-
-    TRACE("iface %p, index %u, desc %p.\n", iface, index, desc);
-
-    if (index >= reflection->desc.BoundResources)
-    {
-        WARN("Invalid index %u.\n", index);
-        return E_INVALIDARG;
-    }
-
-    *desc = reflection->bindings[index];
-    return S_OK;
-}
-
-static HRESULT get_signature_parameter(const struct vkd3d_shader_signature *signature,
-        unsigned int index, D3D12_SIGNATURE_PARAMETER_DESC *desc, bool output)
-{
-    const struct vkd3d_shader_signature_element *e;
-
-    if (!desc || index >= signature->element_count)
-    {
-        WARN("Invalid argument specified.\n");
-        return E_INVALIDARG;
-    }
-    e = &signature->elements[index];
-
-    desc->SemanticName = e->semantic_name;
-    desc->SemanticIndex = e->semantic_index;
-    desc->Register = e->register_index;
-    desc->SystemValueType = (D3D_NAME)e->sysval_semantic;
-    desc->ComponentType = (D3D_REGISTER_COMPONENT_TYPE)e->component_type;
-    desc->Mask = e->mask;
-    desc->ReadWriteMask = output ? (0xf ^ e->used_mask) : e->used_mask;
-    desc->Stream = e->stream_index;
-    desc->MinPrecision = (D3D_MIN_PRECISION)e->min_precision;
-
-    return S_OK;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetInputParameterDesc(
-        ID3D12ShaderReflection *iface, UINT index, D3D12_SIGNATURE_PARAMETER_DESC *desc)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-
-    TRACE("iface %p, index %u, desc %p.\n", iface, index, desc);
-
-    return get_signature_parameter(&reflection->signature_info.input, index, desc, false);
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetOutputParameterDesc(
-        ID3D12ShaderReflection *iface, UINT index, D3D12_SIGNATURE_PARAMETER_DESC *desc)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-
-    TRACE("iface %p, index %u, desc %p.\n", iface, index, desc);
-
-    return get_signature_parameter(&reflection->signature_info.output, index, desc, true);
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetPatchConstantParameterDesc(
-        ID3D12ShaderReflection *iface, UINT index, D3D12_SIGNATURE_PARAMETER_DESC *desc)
-{
-    struct d3d12_reflection *reflection = impl_from_ID3D12ShaderReflection(iface);
-    bool output = ((reflection->desc.Version & 0xffff0000) >> 16) == D3D12_SHVER_HULL_SHADER;
-
-    TRACE("iface %p, index %u, desc %p.\n", iface, index, desc);
-
-    return get_signature_parameter(&reflection->signature_info.patch_constant, index, desc, output);
-}
-
-static struct ID3D12ShaderReflectionVariable * STDMETHODCALLTYPE d3d12_reflection_GetVariableByName(
-        ID3D12ShaderReflection *iface, const char *name)
-{
-    FIXME("iface %p, name %s stub!\n", iface, debugstr_a(name));
-
-    return NULL;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetResourceBindingDescByName(
-        ID3D12ShaderReflection *iface, const char *name, D3D12_SHADER_INPUT_BIND_DESC *desc)
-{
-    FIXME("iface %p, name %s, desc %p stub!\n", iface, debugstr_a(name), desc);
-
-    return E_NOTIMPL;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_reflection_GetMovInstructionCount(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_reflection_GetMovcInstructionCount(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_reflection_GetConversionInstructionCount(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_reflection_GetBitwiseInstructionCount(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static D3D_PRIMITIVE STDMETHODCALLTYPE d3d12_reflection_GetGSInputPrimitive(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static BOOL STDMETHODCALLTYPE d3d12_reflection_IsSampleFrequencyShader(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return FALSE;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_reflection_GetNumInterfaceSlots(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static HRESULT STDMETHODCALLTYPE d3d12_reflection_GetMinFeatureLevel(
-        ID3D12ShaderReflection *iface, D3D_FEATURE_LEVEL *level)
-{
-    FIXME("iface %p, level %p stub!\n", iface, level);
-
-    return E_NOTIMPL;
-}
-
-static UINT STDMETHODCALLTYPE d3d12_reflection_GetThreadGroupSize(
-        ID3D12ShaderReflection *iface, UINT *sizex, UINT *sizey, UINT *sizez)
-{
-    FIXME("iface %p, sizex %p, sizey %p, sizez %p stub!\n", iface, sizex, sizey, sizez);
-
-    return 0;
-}
-
-static UINT64 STDMETHODCALLTYPE d3d12_reflection_GetRequiresFlags(ID3D12ShaderReflection *iface)
-{
-    FIXME("iface %p stub!\n", iface);
-
-    return 0;
-}
-
-static const struct ID3D12ShaderReflectionVtbl d3d12_reflection_vtbl =
-{
-    /* IUnknown methods */
-    d3d12_reflection_QueryInterface,
-    d3d12_reflection_AddRef,
-    d3d12_reflection_Release,
-    /* ID3D12ShaderReflection methods */
-    d3d12_reflection_GetDesc,
-    d3d12_reflection_GetConstantBufferByIndex,
-    d3d12_reflection_GetConstantBufferByName,
-    d3d12_reflection_GetResourceBindingDesc,
-    d3d12_reflection_GetInputParameterDesc,
-    d3d12_reflection_GetOutputParameterDesc,
-    d3d12_reflection_GetPatchConstantParameterDesc,
-    d3d12_reflection_GetVariableByName,
-    d3d12_reflection_GetResourceBindingDescByName,
-    d3d12_reflection_GetMovInstructionCount,
-    d3d12_reflection_GetMovcInstructionCount,
-    d3d12_reflection_GetConversionInstructionCount,
-    d3d12_reflection_GetBitwiseInstructionCount,
-    d3d12_reflection_GetGSInputPrimitive,
-    d3d12_reflection_IsSampleFrequencyShader,
-    d3d12_reflection_GetNumInterfaceSlots,
-    d3d12_reflection_GetMinFeatureLevel,
-    d3d12_reflection_GetThreadGroupSize,
-    d3d12_reflection_GetRequiresFlags,
-};
-
-static bool require_space(size_t offset, size_t count, size_t size, size_t data_size)
-{
-    return !count || (data_size - offset) / count >= size;
-}
-
-/* Return a pointer to data in a code blob, with bounds checking. */
-static const void *get_data_ptr(const struct vkd3d_shader_code *code,
-        uint32_t offset, uint32_t count, uint32_t size)
-{
-    if (!require_space(offset, count, size, code->size))
-    {
-        WARN("Offset %#x and size %#x exceeds section size %#zx.\n", offset, size, code->size);
-        return NULL;
-    }
-
-    return (const uint8_t *)code->code + offset;
-}
-
-static HRESULT get_string(const struct vkd3d_shader_code *code, uint32_t offset, char **ret)
-{
-    const char *str;
-    char *end;
-
-    if (offset >= code->size)
-    {
-        WARN("Offset %#x exceeds size %#zx.\n", offset, code->size);
-        return E_INVALIDARG;
-    }
-
-    str = (const char *)code->code + offset;
-    if (!(end = memchr(str, 0, code->size - offset)))
-    {
-        WARN("String at %#x is not properly zero-terminated.\n", offset);
-        return E_INVALIDARG;
-    }
-
-    if (!(*ret = vkd3d_memdup(str, end + 1 - str)))
-        return E_OUTOFMEMORY;
-    return S_OK;
-}
-
-struct rdef_header
-{
-    uint32_t buffer_count;
-    uint32_t buffers_offset;
-    uint32_t binding_count;
-    uint32_t bindings_offset;
-    uint8_t minor_version;
-    uint8_t major_version;
-    uint16_t type;
-    uint32_t compile_flags;
-    uint32_t creator_offset;
-};
-
-struct rdef_rd11
-{
-    uint32_t magic;
-    uint32_t header_size;
-    uint32_t buffer_size;
-    uint32_t binding_size;
-    uint32_t variable_size;
-    uint32_t type_size;
-    uint32_t field_size;
-    /* Always zero. Possibly either padding or a null terminator? */
-    uint32_t zero;
-};
-
-struct rdef_buffer
-{
-    uint32_t name_offset;
-    uint32_t var_count;
-    uint32_t vars_offset;
-    uint32_t size;
-    uint32_t flags;
-    uint32_t type;
-};
-
-struct rdef_variable
-{
-    uint32_t name_offset;
-    uint32_t offset;
-    uint32_t size;
-    uint32_t flags;
-    uint32_t type_offset;
-    uint32_t default_value_offset;
-    uint32_t resource_binding;
-    uint32_t resource_count;
-    uint32_t sampler_binding;
-    uint32_t sampler_count;
-};
-
-struct rdef_type
-{
-    uint16_t class;
-    uint16_t base_type;
-    uint16_t row_count;
-    uint16_t column_count;
-    uint16_t element_count;
-    uint16_t field_count;
-    uint32_t fields_offset;
-    /* Probably related to interfaces. */
-    uint32_t unknown[4];
-    uint32_t name_offset;
-};
-
-struct rdef_field
-{
-    uint32_t name_offset;
-    uint32_t type_offset;
-    uint32_t offset;
-};
-
-struct rdef_binding
-{
-    uint32_t name_offset;
-    uint32_t type;
-    uint32_t resource_format;
-    uint32_t dimension;
-    uint32_t multisample_count;
-    uint32_t index;
-    uint32_t count;
-    uint32_t flags;
-    uint32_t space;
-    uint32_t id;
-};
-
-static HRESULT d3d12_type_init(struct d3d12_type *type, uint32_t type_offset, uint32_t type_size,
-        const struct vkd3d_shader_code *section, uint32_t field_offset)
-{
-    struct rdef_type normalized_type = {0};
-    const struct rdef_type *rdef_type;
-    char *name = NULL;
-    HRESULT hr;
-
-    if (!(rdef_type = get_data_ptr(section, type_offset, 1, type_size)))
-        return E_INVALIDARG;
-    memcpy(&normalized_type, rdef_type, type_size);
-
-    if (normalized_type.name_offset && FAILED(hr = get_string(section, normalized_type.name_offset, &name)))
-        return hr;
-
-    type->ID3D12ShaderReflectionType_iface.lpVtbl = &d3d12_type_vtbl;
-
-    type->desc.Class = normalized_type.class;
-    type->desc.Type = normalized_type.base_type;
-    type->desc.Rows = normalized_type.row_count;
-    type->desc.Columns = normalized_type.column_count;
-    type->desc.Elements = normalized_type.element_count;
-    type->desc.Members = normalized_type.field_count;
-    type->desc.Offset = field_offset;
-    type->desc.Name = name;
-
-    if (normalized_type.field_count)
-    {
-        const struct rdef_field *rdef_fields;
-
-        if (!(rdef_fields = get_data_ptr(section, normalized_type.fields_offset,
-                normalized_type.field_count, sizeof(*rdef_fields))))
-            return E_INVALIDARG;
-
-        if (!(type->fields = vkd3d_calloc(normalized_type.field_count, sizeof(*type->fields))))
-            return false;
-
-        for (uint32_t i = 0; i < normalized_type.field_count; ++i)
-        {
-            const struct rdef_field *rdef_field = &rdef_fields[i];
-
-            if ((hr = d3d12_type_init(&type->fields[i].type, rdef_field->type_offset,
-                    type_size, section, rdef_field->offset)))
-                return hr;
-        }
-    }
-
-    return S_OK;
-}
-
-static HRESULT d3d12_variable_init(struct d3d12_variable *variable, const struct rdef_variable *rdef_variable,
-        const struct vkd3d_shader_code *section, uint32_t type_size)
-{
-    HRESULT hr;
-    char *name;
-
-    if (FAILED(hr = get_string(section, rdef_variable->name_offset, &name)))
-        return hr;
-
-    variable->ID3D12ShaderReflectionVariable_iface.lpVtbl = &d3d12_variable_vtbl;
-
-    variable->desc.Name = name;
-    variable->desc.StartOffset = rdef_variable->offset;
-    variable->desc.Size = rdef_variable->size;
-    variable->desc.uFlags = rdef_variable->flags;
-    variable->desc.StartTexture = rdef_variable->resource_binding;
-    variable->desc.TextureSize = rdef_variable->resource_count;
-    variable->desc.StartSampler = rdef_variable->sampler_binding;
-    variable->desc.SamplerSize = rdef_variable->sampler_count;
-
-    if (rdef_variable->default_value_offset)
-    {
-        const void *default_value;
-
-        if (!(default_value = get_data_ptr(section, rdef_variable->default_value_offset, 1, rdef_variable->size)))
-            return E_INVALIDARG;
-
-        if (!(variable->desc.DefaultValue = vkd3d_memdup(default_value, rdef_variable->size)))
-            return E_OUTOFMEMORY;
-    }
-
-    return d3d12_type_init(&variable->type, rdef_variable->type_offset, type_size, section, 0);
-}
-
-static HRESULT d3d12_buffer_init(struct d3d12_buffer *buffer, const struct rdef_buffer *rdef_buffer,
-        const struct vkd3d_shader_code *section, uint32_t variable_size, uint32_t type_size)
-{
-    HRESULT hr;
-    char *name;
-
-    if ((FAILED(hr = get_string(section, rdef_buffer->name_offset, &name))))
-        return hr;
-
-    buffer->ID3D12ShaderReflectionConstantBuffer_iface.lpVtbl = &d3d12_buffer_vtbl;
-
-    buffer->desc.Type = rdef_buffer->type;
-    buffer->desc.Variables = rdef_buffer->var_count;
-    buffer->desc.Size = rdef_buffer->size;
-    buffer->desc.uFlags = rdef_buffer->flags;
-    buffer->desc.Name = name;
-
-    if (!(buffer->variables = vkd3d_calloc(rdef_buffer->var_count, sizeof(*buffer->variables))))
-        return E_OUTOFMEMORY;
-
-    for (uint32_t i = 0; i < rdef_buffer->var_count; ++i)
-    {
-        struct rdef_variable normalized_variable = {0};
-        const struct rdef_variable *rdef_variable;
-
-        if (!(rdef_variable = get_data_ptr(section, rdef_buffer->vars_offset + (i * variable_size), 1, variable_size)))
-            return E_INVALIDARG;
-
-        normalized_variable.resource_binding = ~0u;
-        normalized_variable.sampler_binding = ~0u;
-        memcpy(&normalized_variable, rdef_variable, variable_size);
-
-        if ((hr = d3d12_variable_init(&buffer->variables[i], &normalized_variable, section, type_size)))
-            return hr;
-    }
-
-    return S_OK;
-}
-
-static HRESULT parse_rdef(struct d3d12_reflection *reflection, const struct vkd3d_shader_code *section)
-{
-    uint32_t variable_size = offsetof(struct rdef_variable, resource_binding);
-    uint32_t binding_size = offsetof(struct rdef_binding, space);
-    uint32_t type_size = offsetof(struct rdef_type, unknown);
-    const struct rdef_header *header;
-    const struct rdef_rd11 *rd11;
-    HRESULT hr;
-
-    if (!(header = get_data_ptr(section, 0, 1, sizeof(*header))))
-        return E_INVALIDARG;
-
-    if (header->major_version >= 5)
-    {
-        if (!(rd11 = get_data_ptr(section, sizeof(*header), 1, sizeof(*rd11))))
-            return E_INVALIDARG;
-
-        /* RD11 is emitted for 5.0, the reversed version for 5.1 and 6.0.
-         * This corresponds to a difference in the binding_size member, but
-         * it's not clear why the magic also changed there. */
-        if (rd11->magic != TAG_RD11 && rd11->magic != TAG_RD11_REVERSE)
-        {
-            FIXME("Unknown tag %#x.\n", rd11->magic);
-            return E_INVALIDARG;
-        }
-
-        if (rd11->header_size != sizeof(struct rdef_header) + sizeof(struct rdef_rd11))
-        {
-            FIXME("Unexpected header size %#x.\n", rd11->header_size);
-            return E_INVALIDARG;
-        }
-
-        if (rd11->buffer_size != sizeof(struct rdef_buffer))
-        {
-            FIXME("Unexpected buffer size %#x.\n", rd11->buffer_size);
-            return E_INVALIDARG;
-        }
-
-        if (rd11->variable_size != sizeof(struct rdef_variable))
-        {
-            FIXME("Unexpected variable size %#x.\n", rd11->variable_size);
-            return E_INVALIDARG;
-        }
-        variable_size = rd11->variable_size;
-
-        if (rd11->binding_size != sizeof(struct rdef_binding)
-                && rd11->binding_size != offsetof(struct rdef_binding, space))
-        {
-            FIXME("Unexpected binding size %#x.\n", rd11->binding_size);
-            return E_INVALIDARG;
-        }
-        binding_size = rd11->binding_size;
-
-        if (rd11->type_size != sizeof(struct rdef_type))
-        {
-            FIXME("Unexpected type size %#x.\n", rd11->type_size);
-            return E_INVALIDARG;
-        }
-        type_size = rd11->type_size;
-
-        if (rd11->zero)
-        {
-            FIXME("Unexpected field %#x.\n", rd11->zero);
-            return E_INVALIDARG;
-        }
-    }
-
-    reflection->desc.ConstantBuffers = header->buffer_count;
-
-    if (header->buffer_count)
-    {
-        const struct rdef_buffer *rdef_buffers;
-
-        if (!(rdef_buffers = get_data_ptr(section, header->buffers_offset,
-                header->buffer_count, sizeof(*rdef_buffers))))
-            return E_INVALIDARG;
-
-        if (!(reflection->buffers = vkd3d_calloc(header->buffer_count, sizeof(*reflection->buffers))))
-            return E_OUTOFMEMORY;
-
-        for (uint32_t i = 0; i < header->buffer_count; ++i)
-        {
-            if ((hr = d3d12_buffer_init(&reflection->buffers[i], &rdef_buffers[i], section, variable_size, type_size)))
-                return hr;
-        }
-    }
-
-    reflection->desc.BoundResources = header->binding_count;
-
-    if (header->binding_count)
-    {
-        if (!(reflection->bindings = vkd3d_calloc(header->binding_count, sizeof(*reflection->bindings))))
-            return E_OUTOFMEMORY;
-
-        for (uint32_t i = 0; i < header->binding_count; ++i)
-        {
-            const struct rdef_binding *rdef_binding;
-            D3D12_SHADER_INPUT_BIND_DESC *binding;
-            char *name;
-
-            if (!(rdef_binding = get_data_ptr(section, header->bindings_offset + (i * binding_size), 1, binding_size)))
-                return E_INVALIDARG;
-
-            if (FAILED(hr = get_string(section, rdef_binding->name_offset, &name)))
-                return hr;
-
-            binding = &reflection->bindings[i];
-
-            binding->Name = name;
-            binding->Type = rdef_binding->type;
-            binding->BindPoint = rdef_binding->index;
-            binding->BindCount = rdef_binding->count;
-            binding->uFlags = rdef_binding->flags;
-            binding->ReturnType = rdef_binding->resource_format;
-            binding->Dimension = rdef_binding->dimension;
-            binding->NumSamples = rdef_binding->multisample_count;
-            if (binding_size == sizeof(*rdef_binding))
-            {
-                binding->Space = rdef_binding->space;
-                binding->uID = rdef_binding->id;
-            }
-            else
-            {
-                binding->Space = 0;
-                binding->uID = rdef_binding->index;
-            }
-        }
-    }
-
-    return S_OK;
-}
-
-static HRESULT d3d12_reflection_init(struct d3d12_reflection *reflection, const void *data, size_t data_size)
-{
-    struct vkd3d_shader_compile_info compile_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO};
-    struct vkd3d_shader_dxbc_desc dxbc_desc;
-    bool found_rdef = false;
-    enum vkd3d_result ret;
-    HRESULT hr;
-
-    reflection->ID3D12ShaderReflection_iface.lpVtbl = &d3d12_reflection_vtbl;
-    reflection->refcount = 1;
-
-    compile_info.source.code = data;
-    compile_info.source.size = data_size;
-    compile_info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF;
-
-    compile_info.next = &reflection->signature_info;
-    reflection->signature_info.type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO;
-
-    if (FAILED(hr = hresult_from_vkd3d_result(vkd3d_shader_scan(&compile_info, NULL))))
-        return hr;
-
-    if ((ret = vkd3d_shader_parse_dxbc(&compile_info.source, 0, &dxbc_desc, NULL)))
-    {
-        vkd3d_shader_free_scan_signature_info(&reflection->signature_info);
-        return hresult_from_vkd3d_result(ret);
-    }
-
-    for (unsigned int i = 0; i < dxbc_desc.section_count; ++i)
-    {
-        const struct vkd3d_shader_dxbc_section_desc *section = &dxbc_desc.sections[i];
-
-        if (section->tag == TAG_RDEF)
-        {
-            if (found_rdef)
-            {
-                FIXME("Multiple RDEF chunks.\n");
-                continue;
-            }
-
-            if (FAILED(hr = parse_rdef(reflection, &section->data)))
-                goto fail;
-            found_rdef = true;
-        }
-        else if (section->tag == TAG_SHDR || section->tag == TAG_SHEX)
-        {
-            const uint32_t *version;
-
-            if (!(version = get_data_ptr(&section->data, 0, 1, sizeof(*version))))
-            {
-                hr = E_INVALIDARG;
-                goto fail;
-            }
-            reflection->desc.Version = *version;
-        }
-    }
-
-    reflection->desc.InputParameters = reflection->signature_info.input.element_count;
-    reflection->desc.OutputParameters = reflection->signature_info.output.element_count;
-    reflection->desc.PatchConstantParameters = reflection->signature_info.patch_constant.element_count;
-
-    vkd3d_shader_free_dxbc(&dxbc_desc);
-
-    return S_OK;
-
-fail:
-    vkd3d_shader_free_scan_signature_info(&reflection->signature_info);
-    vkd3d_shader_free_dxbc(&dxbc_desc);
-    return hr;
-}
-
-HRESULT WINAPI D3DReflect(const void *data, SIZE_T data_size, REFIID iid, void **reflection)
-{
-    struct d3d12_reflection *object;
-    HRESULT hr;
-
-    TRACE("data %p, data_size %"PRIuPTR", iid %s, reflection %p.\n",
-            data, (uintptr_t)data_size, debugstr_guid(iid), reflection);
-
-    if (!IsEqualGUID(iid, &IID_ID3D12ShaderReflection))
-    {
-        WARN("Invalid iid %s.\n", debugstr_guid(iid));
-        return E_INVALIDARG;
-    }
-
-    if (!(object = vkd3d_calloc(1, sizeof(*object))))
-        return E_OUTOFMEMORY;
-
-    if (FAILED(hr = d3d12_reflection_init(object, data, data_size)))
-    {
-        free(object);
-        return hr;
-    }
-
-    *reflection = &object->ID3D12ShaderReflection_iface;
-    TRACE("Created reflection %p.\n", object);
-    return S_OK;
-}
diff --git a/libs/xml2/Makefile.in b/libs/xml2/Makefile.in
index 46508f20b847..1b819f478b2d 100644
--- a/libs/xml2/Makefile.in
+++ b/libs/xml2/Makefile.in
@@ -5,7 +5,6 @@ EXTRADEFS = -DHAVE_VA_COPY
 SOURCES = \
 	HTMLparser.c \
 	HTMLtree.c \
-	SAX.c \
 	SAX2.c \
 	buf.c \
 	catalog.c \
diff --git a/libs/xml2/SAX.c b/libs/xml2/SAX.c
deleted file mode 100644
index 7f2f297406c7..000000000000
--- a/libs/xml2/SAX.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SAX.c : Old SAX v1 handlers to build a tree.
- *         Deprecated except for compatibility
- *
- * See Copyright for the status of this software.
- *
- * Daniel Veillard <daniel@veillard.com>
- */
-
-
-#define IN_LIBXML
-#include "libxml.h"
-#include <stdlib.h>
-#include <string.h>
-#include <libxml/xmlmemory.h>
-#include <libxml/tree.h>
-#include <libxml/parser.h>
-#include <libxml/parserInternals.h>
-#include <libxml/valid.h>
-#include <libxml/entities.h>
-#include <libxml/xmlerror.h>
-#include <libxml/debugXML.h>
-#include <libxml/xmlIO.h>
-#include <libxml/SAX.h>
-#include <libxml/uri.h>
-#include <libxml/valid.h>
-#include <libxml/HTMLtree.h>
-#include <libxml/SAX2.h>
-
-#ifdef LIBXML_LEGACY_ENABLED
-#ifdef LIBXML_SAX1_ENABLED
-/**
- * initxmlDefaultSAXHandler:
- * @hdlr:  the SAX handler
- * @warning:  flag if non-zero sets the handler warning procedure
- *
- * Initialize the default XML SAX version 1 handler
- * DEPRECATED: use xmlSAX2InitDefaultSAXHandler() for the new SAX2 blocks
- */
-void
-initxmlDefaultSAXHandler(xmlSAXHandlerV1 *hdlr, int warning)
-{
-
-    if(hdlr->initialized == 1)
-	return;
-
-    hdlr->internalSubset = xmlSAX2InternalSubset;
-    hdlr->externalSubset = xmlSAX2ExternalSubset;
-    hdlr->isStandalone = xmlSAX2IsStandalone;
-    hdlr->hasInternalSubset = xmlSAX2HasInternalSubset;
-    hdlr->hasExternalSubset = xmlSAX2HasExternalSubset;
-    hdlr->resolveEntity = xmlSAX2ResolveEntity;
-    hdlr->getEntity = xmlSAX2GetEntity;
-    hdlr->getParameterEntity = xmlSAX2GetParameterEntity;
-    hdlr->entityDecl = xmlSAX2EntityDecl;
-    hdlr->attributeDecl = xmlSAX2AttributeDecl;
-    hdlr->elementDecl = xmlSAX2ElementDecl;
-    hdlr->notationDecl = xmlSAX2NotationDecl;
-    hdlr->unparsedEntityDecl = xmlSAX2UnparsedEntityDecl;
-    hdlr->setDocumentLocator = xmlSAX2SetDocumentLocator;
-    hdlr->startDocument = xmlSAX2StartDocument;
-    hdlr->endDocument = xmlSAX2EndDocument;
-    hdlr->startElement = xmlSAX2StartElement;
-    hdlr->endElement = xmlSAX2EndElement;
-    hdlr->reference = xmlSAX2Reference;
-    hdlr->characters = xmlSAX2Characters;
-    hdlr->cdataBlock = xmlSAX2CDataBlock;
-    hdlr->ignorableWhitespace = xmlSAX2Characters;
-    hdlr->processingInstruction = xmlSAX2ProcessingInstruction;
-    if (warning == 0)
-	hdlr->warning = NULL;
-    else
-	hdlr->warning = xmlParserWarning;
-    hdlr->error = xmlParserError;
-    hdlr->fatalError = xmlParserError;
-
-    hdlr->initialized = 1;
-}
-
-#ifdef LIBXML_HTML_ENABLED
-
-/**
- * inithtmlDefaultSAXHandler:
- * @hdlr:  the SAX handler
- *
- * Initialize the default HTML SAX version 1 handler
- * DEPRECATED: use xmlSAX2InitHtmlDefaultSAXHandler() for the new SAX2 blocks
- */
-void
-inithtmlDefaultSAXHandler(xmlSAXHandlerV1 *hdlr)
-{
-    if(hdlr->initialized == 1)
-	return;
-
-    hdlr->internalSubset = xmlSAX2InternalSubset;
-    hdlr->externalSubset = NULL;
-    hdlr->isStandalone = NULL;
-    hdlr->hasInternalSubset = NULL;
-    hdlr->hasExternalSubset = NULL;
-    hdlr->resolveEntity = NULL;
-    hdlr->getEntity = xmlSAX2GetEntity;
-    hdlr->getParameterEntity = NULL;
-    hdlr->entityDecl = NULL;
-    hdlr->attributeDecl = NULL;
-    hdlr->elementDecl = NULL;
-    hdlr->notationDecl = NULL;
-    hdlr->unparsedEntityDecl = NULL;
-    hdlr->setDocumentLocator = xmlSAX2SetDocumentLocator;
-    hdlr->startDocument = xmlSAX2StartDocument;
-    hdlr->endDocument = xmlSAX2EndDocument;
-    hdlr->startElement = xmlSAX2StartElement;
-    hdlr->endElement = xmlSAX2EndElement;
-    hdlr->reference = NULL;
-    hdlr->characters = xmlSAX2Characters;
-    hdlr->cdataBlock = xmlSAX2CDataBlock;
-    hdlr->ignorableWhitespace = xmlSAX2IgnorableWhitespace;
-    hdlr->processingInstruction = xmlSAX2ProcessingInstruction;
-    hdlr->comment = xmlSAX2Comment;
-    hdlr->warning = xmlParserWarning;
-    hdlr->error = xmlParserError;
-    hdlr->fatalError = xmlParserError;
-
-    hdlr->initialized = 1;
-}
-
-#endif /* LIBXML_HTML_ENABLED */
-
-#endif /* LIBXML_SAX1_ENABLED */
-
-#endif /* LIBXML_LEGACY_ENABLED */
diff --git a/programs/wineboot/Makefile.in b/programs/wineboot/Makefile.in
index d346b8984b19..2d3b4dc2bde3 100644
--- a/programs/wineboot/Makefile.in
+++ b/programs/wineboot/Makefile.in
@@ -1,5 +1,5 @@
 MODULE    = wineboot.exe
-IMPORTS   = uuid advapi32 ws2_32 kernelbase
+IMPORTS   = uuid rpcrt4 advapi32 ws2_32 kernelbase
 DELAYIMPORTS = shell32 shlwapi version user32 gdi32 setupapi newdev wininet
 
 EXTRADLLFLAGS = -mconsole
diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
index 6ffcbe1266cb..aa91d01c92f2 100644
--- a/programs/wineboot/wineboot.c
+++ b/programs/wineboot/wineboot.c
@@ -912,6 +912,26 @@ static void create_volatile_environment_registry_key(void)
     RegCloseKey( hkey );
 }
 
+static void create_sqmclient_registry_key(void)
+{
+    HKEY hkey;
+    LONG r;
+    UUID uuid;
+    RPC_WSTR uuid_str;
+
+    r = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\SQMClient", 0, NULL, 0,
+                         KEY_ALL_ACCESS, NULL, &hkey, NULL );
+    if (r) return;
+
+    r = RegQueryValueExW( hkey, L"MachineId", NULL, NULL, NULL, NULL );
+    if (r == ERROR_FILE_NOT_FOUND && UuidCreate( &uuid ) == S_OK && UuidToStringW( &uuid, &uuid_str ) == RPC_S_OK)
+    {
+        set_reg_value( hkey, L"MachineId", uuid_str );
+        RpcStringFreeW( &uuid_str );
+    }
+    RegCloseKey( hkey );
+}
+
 static const WCHAR *get_known_dll_ntdir( WORD machine )
 {
     switch (machine)
@@ -1904,6 +1924,7 @@ int __cdecl main( int argc, char *argv[] )
     create_volatile_environment_registry_key();
     create_known_dlls();
     initialize_internet();
+    create_sqmclient_registry_key();
 
     ProcessRunKeys( HKEY_LOCAL_MACHINE, L"RunOnce", TRUE, TRUE );
 
diff --git a/server/class.c b/server/class.c
index 2ca94bca7bee..daaa344f00a2 100644
--- a/server/class.c
+++ b/server/class.c
@@ -38,15 +38,19 @@
 #include "winuser.h"
 #include "winternl.h"
 
+/* also defined in win32u/ntuser_private.h */
+#define MAKE_WNDPROC(index)     ((UINT_PTR)(UINT)MAKELONG(index, 0xffff))
+
 struct window_class
 {
     struct list         entry;           /* entry in process list */
     struct winstation  *winstation;      /* winstation the class was created on */
     struct process     *process;         /* process owning the class */
     int                 count;           /* reference count */
-    int                 local;           /* local class? */
     atom_t              atom;            /* class atom for versioned class */
+    unsigned int        fnid;            /* builtin control FNID, or 0 */
     client_ptr_t        client_ptr;      /* pointer to class in client address space */
+    bool                ansi;            /* class wndproc is ansi */
     class_shm_t        *shared;          /* class in session shared memory */
 };
 
@@ -60,7 +64,7 @@ static struct window_class *create_class( struct process *process, int local, in
 
     class->process = (struct process *)grab_object( process );
     class->count = 0;
-    class->local = local;
+    class->ansi = false;
 
     if (!(class->shared = alloc_shared_object( offsetof(class_shm_t, extra[cls_extra]) ))) goto failed;
 
@@ -109,20 +113,18 @@ static struct window_class *find_class( struct process *process, atom_t atom, mo
         const class_shm_t *shared = class->shared;
         if (class->atom != atom) continue;
         is_win16 = !(shared->info.instance >> 16);
-        if (!instance || !class->local || shared->info.instance == instance ||
+        if (!instance || !shared->local || shared->info.instance == instance ||
             (!is_win16 && ((shared->info.instance & ~0xffff) == (instance & ~0xffff)))) return class;
     }
     return NULL;
 }
 
-struct window_class *grab_class( struct process *process, atom_t atom, mod_handle_t instance,
-                                 int *extra_bytes, struct obj_locator *locator )
+struct window_class *grab_class( struct process *process, atom_t atom, mod_handle_t instance, struct obj_locator *locator )
 {
     struct window_class *class = find_class( process, atom, instance );
     if (class)
     {
         class->count++;
-        *extra_bytes = class->shared->info.win_extra;
         *locator = get_shared_object_locator( class->shared );
     }
     else set_error( STATUS_INVALID_HANDLE );
@@ -137,7 +139,7 @@ void release_class( struct window_class *class )
 
 int is_desktop_class( struct window_class *class )
 {
-    return (class->shared->info.atom == DESKTOP_ATOM && !class->local);
+    return class->shared->info.atom == DESKTOP_ATOM && !class->shared->local;
 }
 
 int is_message_class( struct window_class *class )
@@ -145,8 +147,7 @@ int is_message_class( struct window_class *class )
     static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
     static const struct unicode_str name = { messageW, sizeof(messageW) };
     struct atom_table *table = get_user_atom_table();
-
-    return (!class->local && class->shared->info.atom == find_atom( table, &name ));
+    return !class->shared->local && class->shared->info.atom == find_atom( table, &name );
 }
 
 int get_class_style( struct window_class *class )
@@ -159,6 +160,31 @@ atom_t get_class_atom( struct window_class *class )
     return class->shared->info.atom;
 }
 
+unsigned int get_class_fnid( struct window_class *class, data_size_t *extra_size, data_size_t *private_size )
+{
+    *extra_size = class->shared->info.win_extra;
+
+    if ((class->fnid & ~0x7fff) != 0x8000) *private_size = 0;
+    else switch (class->fnid & 0x7fff)
+    {
+    case NTUSER_WNDPROC_DIALOG: *private_size = 0; break;
+    case NTUSER_WNDPROC_MDICLIENT: *private_size = 0; break;
+    default: *private_size = *extra_size; break;
+    }
+
+    return class->fnid;
+}
+
+client_ptr_t get_class_wndproc( struct window_class *class, bool *is_ansi )
+{
+    client_ptr_t wndproc = class->shared->info.wndproc;
+    UINT index = LOWORD(wndproc);
+
+    /* builtin wndproc can be both ansi or unicode, otherwise use the class wndproc kind */
+    if (wndproc != MAKE_WNDPROC(index) || index >= NTUSER_NB_PROCS) *is_ansi = !!class->ansi;
+    return wndproc;
+}
+
 client_ptr_t get_class_client_ptr( struct window_class *class )
 {
     return class->client_ptr;
@@ -185,6 +211,7 @@ DECL_HANDLER(create_class)
     atom_t atom = req->atom;
     struct class_info info;
     WCHAR buffer[16];
+    bool local;
 
     if (name.len < sizeof(info)) return set_error( STATUS_INVALID_PARAMETER );
     memcpy( &info, name.str, sizeof(info) );
@@ -213,8 +240,9 @@ DECL_HANDLER(create_class)
         info.atom = grab_atom( table, atom );
     }
 
+    local = !req->fnid && !(info.style & CS_GLOBALCLASS);
     class = find_class( current->process, atom, info.instance );
-    if (class && !class->local == !req->local)
+    if (class && !class->shared->local == !local)
     {
         set_win32_error( ERROR_CLASS_ALREADY_EXISTS );
         release_atom( table, atom );
@@ -230,20 +258,23 @@ DECL_HANDLER(create_class)
         return;
     }
 
-    if (!(class = create_class( current->process, req->local, info.cls_extra )))
+    if (!(class = create_class( current->process, local, info.cls_extra )))
     {
         release_atom( table, atom );
         release_atom( table, info.atom );
         return;
     }
     class->atom       = atom;
+    class->fnid       = req->fnid;
     class->client_ptr = req->client_ptr;
+    class->ansi       = !!req->ansi;
 
     SHARED_WRITE_BEGIN( class->shared, class_shm_t )
     {
         memcpy( (void *)shared->name, name.str, name.len );
         shared->name_offset  = name_offset;
         shared->name_len     = name.len;
+        shared->local        = local;
         memcpy( (void *)&shared->info, &info, sizeof(info) );
         memset( (void *)shared->extra, 0, info.cls_extra );
     }
@@ -315,6 +346,7 @@ DECL_HANDLER(set_class_info)
         case GCLP_WNDPROC:
             reply->old_info = shared->info.wndproc;
             shared->info.wndproc = req->new_info;
+            class->ansi = !!req->ansi;
             break;
         case GCLP_HCURSOR:
             reply->old_info = shared->info.cursor;
diff --git a/server/protocol.def b/server/protocol.def
index 4f4f90c80e1f..7b467618f7ad 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1058,17 +1058,30 @@ typedef volatile struct
     data_size_t          name_offset;        /* offset in WCHAR of the unversioned class name, constant */
     data_size_t          name_len;           /* len in bytes of the class name, constant */
     WCHAR                name[MAX_ATOM_LEN]; /* class name, constant */
-    unsigned short       __pad;
+    unsigned short       local;              /* class is local, constant */
     struct class_info    info;               /* class info (GCLP_*) */
     char                 extra[];            /* extra bytes storage */
 } class_shm_t;
 
+struct window_info
+{
+    lparam_t             id;               /* window id */
+    mod_handle_t         instance;         /* creator instance */
+    lparam_t             user_data;        /* user-specific data */
+    client_ptr_t         wndproc;          /* window proc */
+};
+
 typedef volatile struct
 {
     struct obj_locator   class;            /* object locator for the window class shared object */
     unsigned int         dpi_context;      /* DPI awareness context */
     unsigned int         fnid;             /* builtin class FNID, or 0 */
+    unsigned int         ansi;             /* window wndproc is ansi */
+    int                  __pad;
     data_size_t          private_size;     /* length of private extra bytes range */
+    data_size_t          extra_size;       /* size of the extra info */
+    struct window_info   info;             /* window info (GWLP_*) */
+    char                 extra[];          /* extra bytes storage */
 } window_shm_t;
 
 typedef volatile union
@@ -2627,12 +2640,12 @@ enum message_type
     unsigned int   dpi_context; /* thread DPI context */
     unsigned int   style;       /* window style */
     unsigned int   ex_style;    /* window extended style */
+    unsigned int   ansi;        /* window created as ansi */
     VARARG(class,unicode_str);  /* class name */
 @REPLY
     user_handle_t  handle;      /* created window */
     user_handle_t  parent;      /* full handle of parent */
     user_handle_t  owner;       /* full handle of owner */
-    int            extra;       /* number of extra bytes */
     client_ptr_t   class_ptr;   /* pointer to class in client address space */
 @END
 
@@ -2669,7 +2682,6 @@ enum message_type
     data_size_t    size;        /* size of the info value to read */
 @REPLY
     user_handle_t  last_active; /* last active popup */
-    int            is_unicode;  /* ANSI or unicode */
     lparam_t       info;        /* current window info value */
 @END
 
@@ -2679,7 +2691,6 @@ enum message_type
     user_handle_t  handle;        /* handle to the window */
     unsigned int   style;         /* window style */
     unsigned int   ex_style;      /* window extended style */
-    short int      is_unicode;    /* ANSI or unicode */
 @REPLY
 @END
 
@@ -2690,8 +2701,11 @@ enum message_type
     int            offset;        /* offset of the info */
     data_size_t    size;          /* size of the info value to write */
     lparam_t       new_info;      /* new window info value */
+    unsigned int   new_ansi;      /* new window info is ansi */
+    unsigned int   internal;      /* set internal control info */
 @REPLY
     lparam_t       old_info;      /* previous window info value */
+    unsigned int   old_ansi;      /* previous window info is ansi */
 @END
 
 
@@ -2699,7 +2713,6 @@ enum message_type
 @REQ(set_window_fnid)
     user_handle_t  handle;        /* handle to the window */
     atom_t         atom;          /* class atom */
-    unsigned int   fnid;          /* builtin class FNID */
 @END
 
 
@@ -3287,8 +3300,9 @@ enum caret_state
 
 /* Create a window class */
 @REQ(create_class)
-    int            local;          /* is it a local class? */
     atom_t         atom;           /* class atom */
+    unsigned int   fnid;           /* FNID for builtin classes, or 0 */
+    unsigned int   ansi;           /* class wndproc is ansi */
     client_ptr_t   client_ptr;     /* pointer to class in client address space */
     data_size_t    name_offset;    /* base class name offset for specified atom */
     VARARG(info,class_info);       /* class info */
@@ -3317,6 +3331,7 @@ enum caret_state
     int            offset;        /* offset of the info */
     data_size_t    size;          /* size of the info value to write */
     lparam_t       new_info;      /* new class info value */
+    unsigned int   ansi;          /* new class info is ansi */
 @REPLY
     lparam_t       old_info;      /* previous class info value */
 @END
diff --git a/server/queue.c b/server/queue.c
index abae70fc91b8..a04b53b70ded 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -2419,24 +2419,24 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c
     }
 
     /* send numpad vkeys if NumLock is active */
-    if ((input->kbd.vkey & KBDNUMPAD) && (desktop->keystate[VK_NUMLOCK] & 0x01) &&
-        !(desktop->keystate[VK_SHIFT] & 0x80))
-    {
-       switch (vkey)
-       {
-       case VK_INSERT: hook_vkey = vkey = VK_NUMPAD0; break;
-       case VK_END:    hook_vkey = vkey = VK_NUMPAD1; break;
-       case VK_DOWN:   hook_vkey = vkey = VK_NUMPAD2; break;
-       case VK_NEXT:   hook_vkey = vkey = VK_NUMPAD3; break;
-       case VK_LEFT:   hook_vkey = vkey = VK_NUMPAD4; break;
-       case VK_CLEAR:  hook_vkey = vkey = VK_NUMPAD5; break;
-       case VK_RIGHT:  hook_vkey = vkey = VK_NUMPAD6; break;
-       case VK_HOME:   hook_vkey = vkey = VK_NUMPAD7; break;
-       case VK_UP:     hook_vkey = vkey = VK_NUMPAD8; break;
-       case VK_PRIOR:  hook_vkey = vkey = VK_NUMPAD9; break;
-       case VK_DELETE: hook_vkey = vkey = VK_DECIMAL; break;
-       default: break;
-       }
+    if ((input->kbd.vkey & KBDNUMPAD) && (desktop_shm->keystate[VK_NUMLOCK] & 0x01) &&
+        !(desktop_shm->keystate[VK_SHIFT] & 0x80))
+    {
+        switch (vkey)
+        {
+        case VK_INSERT: hook_vkey = vkey = VK_NUMPAD0; break;
+        case VK_END:    hook_vkey = vkey = VK_NUMPAD1; break;
+        case VK_DOWN:   hook_vkey = vkey = VK_NUMPAD2; break;
+        case VK_NEXT:   hook_vkey = vkey = VK_NUMPAD3; break;
+        case VK_LEFT:   hook_vkey = vkey = VK_NUMPAD4; break;
+        case VK_CLEAR:  hook_vkey = vkey = VK_NUMPAD5; break;
+        case VK_RIGHT:  hook_vkey = vkey = VK_NUMPAD6; break;
+        case VK_HOME:   hook_vkey = vkey = VK_NUMPAD7; break;
+        case VK_UP:     hook_vkey = vkey = VK_NUMPAD8; break;
+        case VK_PRIOR:  hook_vkey = vkey = VK_NUMPAD9; break;
+        case VK_DELETE: hook_vkey = vkey = VK_DECIMAL; break;
+        default: break;
+        }
     }
 
     if (origin == IMO_HARDWARE)
diff --git a/server/request_handlers.h b/server/request_handlers.h
index 0d7a66ef38ea..23ced153af81 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -1488,11 +1488,11 @@ C_ASSERT( offsetof(struct create_window_request, instance) == 32 );
 C_ASSERT( offsetof(struct create_window_request, dpi_context) == 40 );
 C_ASSERT( offsetof(struct create_window_request, style) == 44 );
 C_ASSERT( offsetof(struct create_window_request, ex_style) == 48 );
+C_ASSERT( offsetof(struct create_window_request, ansi) == 52 );
 C_ASSERT( sizeof(struct create_window_request) == 56 );
 C_ASSERT( offsetof(struct create_window_reply, handle) == 8 );
 C_ASSERT( offsetof(struct create_window_reply, parent) == 12 );
 C_ASSERT( offsetof(struct create_window_reply, owner) == 16 );
-C_ASSERT( offsetof(struct create_window_reply, extra) == 20 );
 C_ASSERT( offsetof(struct create_window_reply, class_ptr) == 24 );
 C_ASSERT( sizeof(struct create_window_reply) == 32 );
 C_ASSERT( offsetof(struct destroy_window_request, handle) == 12 );
@@ -1513,25 +1513,25 @@ C_ASSERT( offsetof(struct get_window_info_request, offset) == 16 );
 C_ASSERT( offsetof(struct get_window_info_request, size) == 20 );
 C_ASSERT( sizeof(struct get_window_info_request) == 24 );
 C_ASSERT( offsetof(struct get_window_info_reply, last_active) == 8 );
-C_ASSERT( offsetof(struct get_window_info_reply, is_unicode) == 12 );
 C_ASSERT( offsetof(struct get_window_info_reply, info) == 16 );
 C_ASSERT( sizeof(struct get_window_info_reply) == 24 );
 C_ASSERT( offsetof(struct init_window_info_request, handle) == 12 );
 C_ASSERT( offsetof(struct init_window_info_request, style) == 16 );
 C_ASSERT( offsetof(struct init_window_info_request, ex_style) == 20 );
-C_ASSERT( offsetof(struct init_window_info_request, is_unicode) == 24 );
-C_ASSERT( sizeof(struct init_window_info_request) == 32 );
+C_ASSERT( sizeof(struct init_window_info_request) == 24 );
 C_ASSERT( sizeof(struct init_window_info_reply) == 8 );
 C_ASSERT( offsetof(struct set_window_info_request, handle) == 12 );
 C_ASSERT( offsetof(struct set_window_info_request, offset) == 16 );
 C_ASSERT( offsetof(struct set_window_info_request, size) == 20 );
 C_ASSERT( offsetof(struct set_window_info_request, new_info) == 24 );
-C_ASSERT( sizeof(struct set_window_info_request) == 32 );
+C_ASSERT( offsetof(struct set_window_info_request, new_ansi) == 32 );
+C_ASSERT( offsetof(struct set_window_info_request, internal) == 36 );
+C_ASSERT( sizeof(struct set_window_info_request) == 40 );
 C_ASSERT( offsetof(struct set_window_info_reply, old_info) == 8 );
-C_ASSERT( sizeof(struct set_window_info_reply) == 16 );
+C_ASSERT( offsetof(struct set_window_info_reply, old_ansi) == 16 );
+C_ASSERT( sizeof(struct set_window_info_reply) == 24 );
 C_ASSERT( offsetof(struct set_window_fnid_request, handle) == 12 );
 C_ASSERT( offsetof(struct set_window_fnid_request, atom) == 16 );
-C_ASSERT( offsetof(struct set_window_fnid_request, fnid) == 20 );
 C_ASSERT( sizeof(struct set_window_fnid_request) == 24 );
 C_ASSERT( offsetof(struct set_parent_request, handle) == 12 );
 C_ASSERT( offsetof(struct set_parent_request, parent) == 16 );
@@ -1845,8 +1845,9 @@ C_ASSERT( offsetof(struct get_hook_info_reply, tid) == 20 );
 C_ASSERT( offsetof(struct get_hook_info_reply, proc) == 24 );
 C_ASSERT( offsetof(struct get_hook_info_reply, unicode) == 32 );
 C_ASSERT( sizeof(struct get_hook_info_reply) == 40 );
-C_ASSERT( offsetof(struct create_class_request, local) == 12 );
-C_ASSERT( offsetof(struct create_class_request, atom) == 16 );
+C_ASSERT( offsetof(struct create_class_request, atom) == 12 );
+C_ASSERT( offsetof(struct create_class_request, fnid) == 16 );
+C_ASSERT( offsetof(struct create_class_request, ansi) == 20 );
 C_ASSERT( offsetof(struct create_class_request, client_ptr) == 24 );
 C_ASSERT( offsetof(struct create_class_request, name_offset) == 32 );
 C_ASSERT( sizeof(struct create_class_request) == 40 );
@@ -1864,7 +1865,8 @@ C_ASSERT( offsetof(struct set_class_info_request, window) == 12 );
 C_ASSERT( offsetof(struct set_class_info_request, offset) == 16 );
 C_ASSERT( offsetof(struct set_class_info_request, size) == 20 );
 C_ASSERT( offsetof(struct set_class_info_request, new_info) == 24 );
-C_ASSERT( sizeof(struct set_class_info_request) == 32 );
+C_ASSERT( offsetof(struct set_class_info_request, ansi) == 32 );
+C_ASSERT( sizeof(struct set_class_info_request) == 40 );
 C_ASSERT( offsetof(struct set_class_info_reply, old_info) == 8 );
 C_ASSERT( sizeof(struct set_class_info_reply) == 16 );
 C_ASSERT( offsetof(struct get_class_info_request, window) == 12 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 65c7530e05a9..58fb6a375166 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -1693,6 +1693,7 @@ static void dump_create_window_request( const struct create_window_request *req
     fprintf( stderr, ", dpi_context=%08x", req->dpi_context );
     fprintf( stderr, ", style=%08x", req->style );
     fprintf( stderr, ", ex_style=%08x", req->ex_style );
+    fprintf( stderr, ", ansi=%08x", req->ansi );
     dump_varargs_unicode_str( ", class=", cur_size );
 }
 
@@ -1701,7 +1702,6 @@ static void dump_create_window_reply( const struct create_window_reply *req )
     fprintf( stderr, " handle=%08x", req->handle );
     fprintf( stderr, ", parent=%08x", req->parent );
     fprintf( stderr, ", owner=%08x", req->owner );
-    fprintf( stderr, ", extra=%d", req->extra );
     dump_uint64( ", class_ptr=", &req->class_ptr );
 }
 
@@ -1743,7 +1743,6 @@ static void dump_get_window_info_request( const struct get_window_info_request *
 static void dump_get_window_info_reply( const struct get_window_info_reply *req )
 {
     fprintf( stderr, " last_active=%08x", req->last_active );
-    fprintf( stderr, ", is_unicode=%d", req->is_unicode );
     dump_uint64( ", info=", &req->info );
 }
 
@@ -1752,7 +1751,6 @@ static void dump_init_window_info_request( const struct init_window_info_request
     fprintf( stderr, " handle=%08x", req->handle );
     fprintf( stderr, ", style=%08x", req->style );
     fprintf( stderr, ", ex_style=%08x", req->ex_style );
-    fprintf( stderr, ", is_unicode=%d", req->is_unicode );
 }
 
 static void dump_set_window_info_request( const struct set_window_info_request *req )
@@ -1761,18 +1759,20 @@ static void dump_set_window_info_request( const struct set_window_info_request *
     fprintf( stderr, ", offset=%d", req->offset );
     fprintf( stderr, ", size=%u", req->size );
     dump_uint64( ", new_info=", &req->new_info );
+    fprintf( stderr, ", new_ansi=%08x", req->new_ansi );
+    fprintf( stderr, ", internal=%08x", req->internal );
 }
 
 static void dump_set_window_info_reply( const struct set_window_info_reply *req )
 {
     dump_uint64( " old_info=", &req->old_info );
+    fprintf( stderr, ", old_ansi=%08x", req->old_ansi );
 }
 
 static void dump_set_window_fnid_request( const struct set_window_fnid_request *req )
 {
     fprintf( stderr, " handle=%08x", req->handle );
     fprintf( stderr, ", atom=%04x", req->atom );
-    fprintf( stderr, ", fnid=%08x", req->fnid );
 }
 
 static void dump_set_parent_request( const struct set_parent_request *req )
@@ -2401,8 +2401,9 @@ static void dump_get_hook_info_reply( const struct get_hook_info_reply *req )
 
 static void dump_create_class_request( const struct create_class_request *req )
 {
-    fprintf( stderr, " local=%d", req->local );
-    fprintf( stderr, ", atom=%04x", req->atom );
+    fprintf( stderr, " atom=%04x", req->atom );
+    fprintf( stderr, ", fnid=%08x", req->fnid );
+    fprintf( stderr, ", ansi=%08x", req->ansi );
     dump_uint64( ", client_ptr=", &req->client_ptr );
     fprintf( stderr, ", name_offset=%u", req->name_offset );
     dump_varargs_class_info( ", info=", cur_size );
@@ -2435,6 +2436,7 @@ static void dump_set_class_info_request( const struct set_class_info_request *re
     fprintf( stderr, ", offset=%d", req->offset );
     fprintf( stderr, ", size=%u", req->size );
     dump_uint64( ", new_info=", &req->new_info );
+    fprintf( stderr, ", ansi=%08x", req->ansi );
 }
 
 static void dump_set_class_info_reply( const struct set_class_info_reply *req )
diff --git a/server/user.h b/server/user.h
index 060e54ec775c..1d16e2aab950 100644
--- a/server/user.h
+++ b/server/user.h
@@ -81,7 +81,6 @@ struct desktop
     struct thread_input *foreground_input; /* thread input of foreground thread */
     process_id_t         foreground_pid;   /* id of the foreground process */
     unsigned int         users;            /* processes and threads using this desktop */
-    unsigned char        keystate[256];    /* asynchronous key state */
     unsigned char        alt_pressed;      /* last key press was Alt (used to determine msg on release) */
     struct key_repeat    key_repeat;       /* key auto-repeat */
     unsigned int         clip_flags;       /* last cursor clip flags */
@@ -189,13 +188,14 @@ extern struct window_class *get_window_class( user_handle_t window );
 /* window class functions */
 
 extern void destroy_process_classes( struct process *process );
-extern struct window_class *grab_class( struct process *process, atom_t atom, mod_handle_t instance,
-                                        int *extra_bytes, struct obj_locator *locator );
+extern struct window_class *grab_class( struct process *process, atom_t atom, mod_handle_t instance, struct obj_locator *locator );
 extern void release_class( struct window_class *class );
 extern int is_desktop_class( struct window_class *class );
 extern int is_message_class( struct window_class *class );
 extern int get_class_style( struct window_class *class );
 extern atom_t get_class_atom( struct window_class *class );
+extern unsigned int get_class_fnid( struct window_class *class, data_size_t *extra_size, data_size_t *private_size );
+extern client_ptr_t get_class_wndproc( struct window_class *class, bool *ansi );
 extern client_ptr_t get_class_client_ptr( struct window_class *class );
 
 /* windows station functions */
diff --git a/server/window.c b/server/window.c
index 3fa71fc3f1c3..6bff48e7a220 100644
--- a/server/window.c
+++ b/server/window.c
@@ -74,9 +74,6 @@ struct window
     struct region   *update_region;   /* update region (relative to window rect) */
     unsigned int     style;           /* window style */
     unsigned int     ex_style;        /* window extended style */
-    lparam_t         id;              /* window id */
-    mod_handle_t     instance;        /* creator instance */
-    unsigned int     is_unicode : 1;  /* ANSI or unicode */
     unsigned int     is_linked : 1;   /* is it linked into the parent z-order list? */
     unsigned int     is_layered : 1;  /* has layered info been set? */
     unsigned int     is_orphan : 1;   /* is window orphaned */
@@ -85,18 +82,17 @@ struct window
     unsigned int     alpha;           /* alpha value for a layered window */
     unsigned int     layered_flags;   /* flags for a layered window */
     unsigned int     monitor_dpi;     /* DPI of the window monitor */
-    lparam_t         user_data;       /* user-specific data */
     WCHAR           *text;            /* window caption text */
     data_size_t      text_len;        /* length of window caption */
     unsigned int     paint_flags;     /* various painting flags */
     int              prop_inuse;      /* number of in-use window properties */
     int              prop_alloc;      /* number of allocated window properties */
     struct property *properties;      /* window properties array */
-    int              nb_extra_bytes;  /* number of extra bytes */
-    char            *extra_bytes;     /* extra bytes storage */
     window_shm_t    *shared;          /* window in session shared memory */
 };
 
+C_ASSERT( sizeof(window_shm_t) == offsetof(window_shm_t, extra[0]) );
+
 static void window_dump( struct object *obj, int verbose );
 static void window_destroy( struct object *obj );
 
@@ -177,12 +173,6 @@ static void window_destroy( struct object *obj )
     if (win->class) release_class( win->class );
     free( win->text );
 
-    if (win->nb_extra_bytes)
-    {
-        memset( win->extra_bytes, 0x55, win->nb_extra_bytes );
-        free( win->extra_bytes );
-    }
-
     if (win->shared) free_shared_object( win->shared );
 }
 
@@ -614,21 +604,23 @@ void post_desktop_message( struct desktop *desktop, unsigned int message,
 
 /* create a new window structure (note: the window is not linked in the window tree) */
 static struct window *create_window( struct window *parent, struct window *owner, atom_t atom,
-                                     mod_handle_t class_instance, mod_handle_t instance )
+                                     mod_handle_t class_instance, bool ansi )
 {
-    int extra_bytes;
+    data_size_t extra_size, private_size;
     struct window *win = NULL;
     struct desktop *desktop;
     struct window_class *class;
     struct obj_locator class_locator;
+    unsigned int fnid;
 
     if (!(desktop = get_thread_desktop( current, DESKTOP_CREATEWINDOW ))) return NULL;
 
-    if (!(class = grab_class( current->process, atom, class_instance, &extra_bytes, &class_locator )))
+    if (!(class = grab_class( current->process, atom, class_instance, &class_locator )))
     {
         release_object( desktop );
         return NULL;
     }
+    fnid = get_class_fnid( class, &extra_size, &private_size );
 
     if (!parent)  /* null parent is only allowed for desktop or HWND_MESSAGE top window */
     {
@@ -662,44 +654,37 @@ static struct window *create_window( struct window *parent, struct window *owner
     win->update_region  = NULL;
     win->style          = 0;
     win->ex_style       = 0;
-    win->id             = 0;
-    win->instance       = instance;
-    win->is_unicode     = 1;
     win->is_linked      = 0;
     win->is_layered     = 0;
     win->is_orphan      = 0;
     win->set_foreground = 0;
     win->monitor_dpi    = USER_DEFAULT_SCREEN_DPI;
-    win->user_data      = 0;
     win->text           = NULL;
     win->text_len       = 0;
     win->paint_flags    = 0;
     win->prop_inuse     = 0;
     win->prop_alloc     = 0;
     win->properties     = NULL;
-    win->nb_extra_bytes = 0;
-    win->extra_bytes    = NULL;
     win->shared         = NULL;
     win->window_rect = win->visible_rect = win->surface_rect = win->client_rect = empty_rect;
     list_init( &win->children );
     list_init( &win->unlinked );
 
-    if (!(win->shared = alloc_shared_object( sizeof(*win->shared) ))) goto failed;
+    if (!(win->shared = alloc_shared_object( offsetof(window_shm_t, extra[extra_size]) ))) goto failed;
     SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
         shared->class           = class_locator;
         shared->dpi_context     = NTUSER_DPI_PER_MONITOR_AWARE;
-        shared->fnid            = 0;
-        shared->private_size    = 0;
+        shared->fnid            = fnid;
+        shared->private_size    = private_size;
+        shared->extra_size      = extra_size;
+        memset( (void *)&shared->info, 0, sizeof(shared->info) );
+        memset( (void *)shared->extra, 0, extra_size );
+        shared->info.wndproc    = get_class_wndproc( win->class, &ansi );
+        shared->ansi            = ansi;
     }
     SHARED_WRITE_END;
 
-    if (extra_bytes)
-    {
-        if (!(win->extra_bytes = mem_alloc( extra_bytes ))) goto failed;
-        memset( win->extra_bytes, 0, extra_bytes );
-        win->nb_extra_bytes = extra_bytes;
-    }
     if (!(win->handle = alloc_user_handle( win, win->shared, NTUSER_OBJ_WINDOW ))) goto failed;
     win->last_active = win->handle;
 
@@ -2250,7 +2235,7 @@ DECL_HANDLER(create_window)
 
     if (!atom) atom = find_atom( table, &cls_name );
 
-    if (!(win = create_window( parent, owner, atom, req->class_instance, req->instance ))) return;
+    if (!(win = create_window( parent, owner, atom, req->class_instance, !!req->ansi ))) return;
 
     if (parent && !is_desktop_window( parent ))
         dpi_context = parent->shared->dpi_context;
@@ -2261,7 +2246,8 @@ DECL_HANDLER(create_window)
 
     SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
-        shared->dpi_context = dpi_context;
+        shared->dpi_context     = dpi_context;
+        shared->info.instance   = req->instance;
     }
     SHARED_WRITE_END;
 
@@ -2271,7 +2257,6 @@ DECL_HANDLER(create_window)
     reply->handle      = win->handle;
     reply->parent      = win->parent ? win->parent->handle : 0;
     reply->owner       = win->owner;
-    reply->extra       = win->nb_extra_bytes;
     reply->class_ptr   = get_class_client_ptr( win->class );
 }
 
@@ -2279,20 +2264,23 @@ DECL_HANDLER(create_window)
 /* Set the window builtin class FNID */
 DECL_HANDLER(set_window_fnid)
 {
+    data_size_t extra_size, private_size;
     struct obj_locator class_locator;
     struct window_class *class;
     struct window *win;
-    int extra_bytes;
+    unsigned int fnid;
 
     if (!(win = get_window( req->handle ))) return;
     if (is_desktop_window( win ) && win->thread != current) return set_error( STATUS_ACCESS_DENIED );
-    if (win->shared->fnid && win->shared->fnid != req->fnid) return set_error( STATUS_INVALID_PARAMETER );
 
-    if (!(class = grab_class( current->process, req->atom, 0, &extra_bytes, &class_locator ))) return;
-    SHARED_WRITE_BEGIN( win->shared, window_shm_t )
+    if (!(class = grab_class( current->process, req->atom, 0, &class_locator ))) return;
+    fnid = get_class_fnid( class, &extra_size, &private_size );
+
+    if (win->shared->fnid && win->shared->fnid != fnid) set_error( STATUS_INVALID_PARAMETER );
+    else SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
-        shared->fnid            = req->fnid;
-        shared->private_size    = extra_bytes;
+        shared->fnid            = fnid;
+        shared->private_size    = private_size;
     }
     SHARED_WRITE_END;
     release_class( class );
@@ -2345,7 +2333,7 @@ DECL_HANDLER(get_desktop_window)
 
     if (!desktop->top_window && req->force)  /* create it */
     {
-        if ((desktop->top_window = create_window( NULL, NULL, DESKTOP_ATOM, 0, 0 )))
+        if ((desktop->top_window = create_window( NULL, NULL, DESKTOP_ATOM, 0, false )))
         {
             detach_window_thread( desktop->top_window );
             desktop->top_window->style  = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
@@ -2358,7 +2346,7 @@ DECL_HANDLER(get_desktop_window)
         static const struct unicode_str name = { messageW, sizeof(messageW) };
         struct atom_table *table = get_user_atom_table();
         atom_t atom = add_atom( table, &name );
-        if (atom && (desktop->msg_window = create_window( NULL, NULL, atom, 0, 0 )))
+        if (atom && (desktop->msg_window = create_window( NULL, NULL, atom, 0, false )))
         {
             detach_window_thread( desktop->msg_window );
             desktop->msg_window->style = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
@@ -2408,26 +2396,14 @@ DECL_HANDLER(get_window_info)
     if (!(win = get_window( req->handle ))) return;
 
     reply->last_active = win->handle;
-    reply->is_unicode  = win->is_unicode;
     if (get_user_object( win->last_active, NTUSER_OBJ_WINDOW )) reply->last_active = win->last_active;
 
     switch (req->offset)
     {
     case GWL_STYLE:       reply->info = win->style;  break;
     case GWL_EXSTYLE:     reply->info = win->ex_style;  break;
-    case GWLP_ID:         reply->info = win->id;  break;
-    case GWLP_HINSTANCE:  reply->info = win->instance;  break;
-    case GWLP_WNDPROC:    reply->info = win->is_unicode;  break;
-    case GWLP_USERDATA:   reply->info = win->user_data;  break;
     default:
-        if (req->size > sizeof(reply->info) || req->offset < 0 ||
-            req->offset > win->nb_extra_bytes - (int)req->size ||
-            req->offset < win->shared->private_size)
-        {
-            set_win32_error( ERROR_INVALID_INDEX );
-            break;
-        }
-        memcpy( &reply->info, win->extra_bytes + req->offset, req->size );
+        if (req->size) set_win32_error( ERROR_INVALID_INDEX );
         break;
     }
 }
@@ -2441,7 +2417,6 @@ DECL_HANDLER(init_window_info)
     if (!(win = get_window( req->handle ))) return;
     win->style = req->style;
     win->ex_style = req->ex_style;
-    win->is_unicode = req->is_unicode;
 
     /* changing window style triggers a non-client paint */
     win->paint_flags |= PAINT_NONCLIENT;
@@ -2452,6 +2427,7 @@ DECL_HANDLER(init_window_info)
 DECL_HANDLER(set_window_info)
 {
     struct window *win;
+    bool ansi;
 
     if (!(win = get_window( req->handle ))) return;
     if (is_desktop_window( win ) && win->thread != current)
@@ -2460,46 +2436,55 @@ DECL_HANDLER(set_window_info)
         return;
     }
 
-    switch (req->offset)
+    SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
-    case GWL_STYLE:
-        reply->old_info = win->style;
-        win->style = req->new_info;
-        fix_window_ex_style( win );
-        /* changing window style triggers a non-client paint */
-        win->paint_flags |= PAINT_NONCLIENT;
-        break;
-    case GWL_EXSTYLE:
-        reply->old_info = win->ex_style;
-        set_window_ex_style( win, req->new_info );
-        break;
-    case GWLP_ID:
-        reply->old_info = win->id;
-        win->id = req->new_info;
-        break;
-    case GWLP_HINSTANCE:
-        reply->old_info = win->instance;
-        win->instance = req->new_info;
-        break;
-    case GWLP_WNDPROC:
-        reply->old_info = win->is_unicode;
-        win->is_unicode = req->new_info;
-        break;
-    case GWLP_USERDATA:
-        reply->old_info = win->user_data;
-        win->user_data = req->new_info;
-        break;
-    default:
-        if (req->size > sizeof(req->new_info) || req->offset < 0 ||
-            req->offset > win->nb_extra_bytes - (int)req->size)
+        switch (req->offset)
         {
-            set_win32_error( ERROR_INVALID_INDEX );
+        case GWL_STYLE:
+            reply->old_info = win->style;
+            win->style = req->new_info;
+            fix_window_ex_style( win );
+            /* changing window style triggers a non-client paint */
+            win->paint_flags |= PAINT_NONCLIENT;
+            break;
+        case GWL_EXSTYLE:
+            reply->old_info = win->ex_style;
+            set_window_ex_style( win, req->new_info );
+            break;
+        case GWLP_ID:
+            reply->old_info = shared->info.id;
+            shared->info.id = req->new_info;
+            break;
+        case GWLP_HINSTANCE:
+            reply->old_info = shared->info.instance;
+            shared->info.instance = req->new_info;
+            break;
+        case GWLP_WNDPROC:
+            reply->old_info = shared->info.wndproc;
+            reply->old_ansi = shared->ansi;
+            if (req->new_info) shared->info.wndproc = req->new_info;
+            else shared->info.wndproc = get_class_wndproc( win->class, &ansi );
+            shared->ansi = req->new_ansi; /* class ansi is actually ignored */
+            break;
+        case GWLP_USERDATA:
+            reply->old_info = shared->info.user_data;
+            if (req->size > sizeof(WORD)) shared->info.user_data = req->new_info;
+            else shared->info.user_data = MAKELONG(req->new_info, shared->info.user_data >> 16);
+            break;
+        default:
+            if (req->size > sizeof(req->new_info) || req->offset < 0 ||
+                req->offset > shared->extra_size - (int)req->size ||
+                (!req->internal && req->offset < shared->private_size))
+            {
+                set_win32_error( ERROR_INVALID_INDEX );
+                break;
+            }
+            memcpy( &reply->old_info, (char *)shared->extra + req->offset, req->size );
+            memcpy( (char *)shared->extra + req->offset, &req->new_info, req->size );
             break;
         }
-        memcpy( &reply->old_info, win->extra_bytes + req->offset, req->size );
-        memcpy( win->extra_bytes + req->offset, &req->new_info, req->size );
-        break;
     }
+    SHARED_WRITE_END;
 }
 
 
diff --git a/tools/make_makefiles b/tools/make_makefiles
index a6d0d6b210c8..194940d8aacd 100755
--- a/tools/make_makefiles
+++ b/tools/make_makefiles
@@ -303,6 +303,11 @@ sub assign_sources_to_makefiles(@)
         {
             next;
         }
+        else
+        {
+            my %flags = get_makedep_flags($file);
+            next if defined $flags{header};
+        }
         push @{${$make}{"=SOURCES"}}, $name;
     }
 
diff --git a/tools/makedep.c b/tools/makedep.c
index 90a522640fe4..9116df34ee07 100644
--- a/tools/makedep.c
+++ b/tools/makedep.c
@@ -98,7 +98,8 @@ struct incl_file
 #define FLAG_C_IMPLIB       0x01000000  /* file is part of an import library */
 #define FLAG_C_UNIX         0x02000000  /* file is part of a Unix library */
 #define FLAG_C_CXX          0x04000000  /* file uses C++ */
-#define FLAG_ARM64EC_X64    0x08000000  /* use x86_64 object on ARM64EC */
+#define FLAG_C_ASM          0x08000000  /* file uses assembly */
+#define FLAG_ARM64EC_X64    0x10000000  /* use x86_64 object on ARM64EC */
 
 static const struct
 {
@@ -486,6 +487,22 @@ static const char *get_base_name( const char *name )
 }
 
 
+/*******************************************************************
+ *         skip_initial_dir
+ */
+static const char *skip_initial_dir( const char *name, const char *dir )
+{
+    unsigned int len = strlen( dir );
+
+    if (!strncmp( name, dir, len ) && (!name[len] || name[len] == '/'))
+    {
+        while (name[len] == '/') len++;
+        return name + len;
+    }
+    return NULL;
+}
+
+
 /*******************************************************************
  *         replace_filename
  */
@@ -552,6 +569,7 @@ static char *concat_paths( const char *base, const char *path )
 {
     int i, len;
     char *ret;
+    const char *p;
 
     if (!base || !base[0]) return xstrdup( path && path[0] ? path : "." );
     if (!path || !path[0]) return xstrdup( base );
@@ -559,15 +577,11 @@ static char *concat_paths( const char *base, const char *path )
 
     len = strlen( base );
     while (len && base[len - 1] == '/') len--;
-    while (len && !strncmp( path, "..", 2 ) && (!path[2] || path[2] == '/'))
+    while (len && (p = skip_initial_dir( path, ".." )))
     {
         for (i = len; i > 0; i--) if (base[i - 1] == '/') break;
         if (i == len - 2 && !memcmp( base + i, "..", 2 )) break;  /* we can't go up if we already have ".." */
-        if (i != len - 1 || base[i] != '.')
-        {
-            path += 2;
-            while (*path == '/') path++;
-        }
+        if (i != len - 1 || base[i] != '.') path = p;
         /* else ignore "." element */
         while (i > 0 && base[i - 1] == '/') i--;
         len = i;
@@ -634,7 +648,6 @@ static bool is_subdir_other_arch( const char *name, unsigned int arch )
     if (!p || p == name) return false;
     dir = get_basename( strmake( "%.*s", (int)(p - name), name ));
     if ((cpu = get_cpu_from_name( dir )) == -1) return false;
-    if (native_archs[arch] && cpu == get_cpu_from_name( archs.str[native_archs[arch]] )) return false;
     return cpu != get_cpu_from_name( archs.str[arch] );
 }
 
@@ -1141,6 +1154,16 @@ static void parse_c_file( struct file *source, FILE *file )
 }
 
 
+/*******************************************************************
+ *         parse_asm_file
+ */
+static void parse_asm_file( struct file *source, FILE *file )
+{
+    source->flags |= FLAG_C_ASM;
+    parse_c_file( source, file );
+}
+
+
 /*******************************************************************
  *         parse_cxx_file
  */
@@ -1260,12 +1283,14 @@ static const struct
 {
     { ".c",   parse_c_file },
     { ".h",   parse_c_file },
+    { ".inc", parse_c_file },
     { ".inl", parse_c_file },
     { ".l",   parse_c_file },
     { ".m",   parse_c_file },
     { ".rh",  parse_c_file },
     { ".x",   parse_c_file },
     { ".y",   parse_c_file },
+    { ".S",   parse_asm_file },
     { ".idl", parse_idl_file },
     { ".cpp", parse_cxx_file },
     { ".hpp", parse_cxx_file },
@@ -1526,22 +1551,46 @@ static struct makefile *find_importlib_module( const char *name )
 
     for (i = 0; i < subdirs.count; i++)
     {
-        if (strncmp( submakes[i]->obj_dir, "dlls/", 5 )) continue;
-        len = strlen(submakes[i]->obj_dir);
-        if (strncmp( submakes[i]->obj_dir + 5, name, len - 5 )) continue;
-        if (!name[len - 5] || !strcmp( name + len - 5, ".dll" )) return submakes[i];
+        const char *dir = skip_initial_dir( submakes[i]->obj_dir, "dlls" );
+        if (!dir) continue;
+        len = strlen(dir);
+        if (strncmp( dir, name, len )) continue;
+        if (!name[len] || !strcmp( name + len, ".dll" )) return submakes[i];
     }
     return NULL;
 }
 
 
+/*******************************************************************
+ *         is_external_header
+ */
+static bool is_external_header( struct incl_file *file )
+{
+    const char *filename = file->sourcename ? file->sourcename : file->file->name;
+    char *p, *name;
+
+    if (root_src_dir)
+    {
+        const char *relpath = skip_initial_dir( filename, root_src_dir );
+        if (relpath) filename = relpath;
+    }
+
+    name = xstrdup( filename );
+    while ((p = strrchr( name, '/' )))
+    {
+        *p = 0;
+        if (strarray_exists( external_dirs, name )) return true;
+    }
+    return false;
+}
+
+
 /*******************************************************************
  *         open_include_file
  */
 static struct file *open_include_file( const struct makefile *make, struct incl_file *source )
 {
     struct file *file = NULL;
-    unsigned int len;
 
     errno = ENOENT;
 
@@ -1599,12 +1648,8 @@ static struct file *open_include_file( const struct makefile *make, struct incl_
     {
         if (root_src_dir)
         {
-            len = strlen( root_src_dir );
-            if (!strncmp( dir, root_src_dir, len ) && (!dir[len] || dir[len] == '/'))
-            {
-                while (dir[len] == '/') len++;
-                file = open_global_file( concat_paths( dir + len, source->name ), &source->filename );
-            }
+            const char *relpath = skip_initial_dir( dir, root_src_dir );
+            if (relpath) file = open_global_file( concat_paths( relpath, source->name ), &source->filename );
         }
         else
         {
@@ -1621,7 +1666,9 @@ static struct file *open_include_file( const struct makefile *make, struct incl_
     if ((file = open_same_dir_generated_file( make, source->included_by, source, ".h", ".idl" ))) return file;
     if ((file = open_file_same_dir( source->included_by, source->name, &source->filename ))) return file;
 
-    if (make->external) return NULL; /* ignore missing files in external libs */
+    /* ignore missing files in external libs */
+    if (make->external) return NULL;
+    if (source->included_by && is_external_header( source->included_by )) return NULL;
 
     fprintf( stderr, "%s:%d: error: ", source->included_by->file->name, source->included_line );
     perror( source->name );
@@ -3547,12 +3594,13 @@ static void output_source_one_arch( struct makefile *make, struct incl_file *sou
         if (!is_multiarch( arch )) return;
         if (!is_using_msvcrt( make ) && !make->staticlib && !(source->file->flags & FLAG_C_IMPLIB)) return;
         if ((source->file->flags & FLAG_C_CXX) && !get_expanded_arch_var( make, "CXX", arch )) return;
+        if ((source->file->flags & FLAG_C_ASM) && is_subdir_other_arch( source->name, arch )) return;
     }
     else if (source->file->flags & FLAG_C_UNIX)
     {
         if (!unix_lib_supported) return;
     }
-    else if (source->file->flags & FLAG_C_CXX)
+    else if (source->file->flags & (FLAG_C_CXX | FLAG_C_ASM))
     {
         return;
     }
@@ -3562,8 +3610,6 @@ static void output_source_one_arch( struct makefile *make, struct incl_file *sou
         if (!(source->file->flags & FLAG_C_IMPLIB) && (!make->staticlib || make->external)) return;
     }
 
-    if (strendswith( source->name, ".S" ) && is_subdir_other_arch( source->name, arch )) return;
-
     obj_name = strmake( "%s%s.o", source->arch ? "" : arch_dirs[arch], obj );
     strarray_add( targets, obj_name );
 
@@ -3748,6 +3794,7 @@ static const struct
     { "l", output_source_l },
     { "h", output_source_h },
     { "rh", output_source_h },
+    { "inc", output_source_h },
     { "inl", output_source_h },
     { "ver", output_source_ver },
     { "rc", output_source_rc },
@@ -4829,12 +4876,8 @@ static void load_sources( struct makefile *make )
     {
         if (!strncmp( arg, "-I", 2 ))
         {
-            const char *dir = arg + 2;
-            if (!strncmp( dir, "./", 2 ))
-            {
-                dir += 2;
-                while (*dir == '/') dir++;
-            }
+            const char *dir = skip_initial_dir( arg + 2, "." );
+            if (!dir) dir = arg + 2;
             strarray_add_uniq( &make->include_paths, dir );
         }
         else if (!strncmp( arg, "-D", 2 ) || !strncmp( arg, "-U", 2 ))
diff --git a/tools/widl/write_sltg.c b/tools/widl/write_sltg.c
index 3c80e21d86bd..20e07d1f5ca3 100644
--- a/tools/widl/write_sltg.c
+++ b/tools/widl/write_sltg.c
@@ -1487,12 +1487,226 @@ static void add_interface_typeinfo(struct sltg_typelib *typelib, type_t *iface)
 
 static void add_enum_typeinfo(struct sltg_typelib *typelib, type_t *type)
 {
-    error("add_enum_typeinfo: %s not implemented\n", type->name);
+    struct sltg_data data, *var_data = NULL;
+    struct sltg_hrefmap hrefmap;
+    const char *index_name;
+    struct sltg_typeinfo_header ti;
+    struct sltg_member_header member;
+    struct sltg_tail tail;
+    int member_offset, var_count = 0;
+    short *type_desc_offset = NULL;
+
+    if (type->typelib_idx != -1) return;
+
+    chat("add_enum_typeinfo: type %p, type->name %s\n", type, type->name);
+
+    type->typelib_idx = typelib->block_count;
+
+    hrefmap.href_count = 0;
+    hrefmap.href = NULL;
+
+    if (type_enum_get_values(type))
+    {
+        int i = 0;
+        var_t *var;
+
+        var_count = list_count(type_enum_get_values(type));
+
+        var_data = xmalloc(var_count * sizeof(*var_data));
+        type_desc_offset = xmalloc(var_count * sizeof(*type_desc_offset));
+
+        LIST_FOR_EACH_ENTRY(var, type_enum_get_values(type), var_t, entry)
+        {
+            short base_offset;
+
+            chat("add_enum_typeinfo: var %p (%s), type %p (%s)\n",
+                 var, var->name, var->declspec.type, var->declspec.type->name);
+
+            init_sltg_data(&var_data[i]);
+
+            base_offset = (i + 1) * (sizeof(struct sltg_variable) + sizeof(int));
+            type_desc_offset[i] = write_var_desc(typelib, &var_data[i], var->declspec.type, 0, 0,
+                                                 base_offset, NULL, &hrefmap);
+            dump_var_desc(var_data[i].data, var_data[i].size);
+            i++;
+        }
+    }
+
+    init_sltg_data(&data);
+
+    index_name = add_typeinfo_block(typelib, type, TKIND_ENUM);
+
+    init_typeinfo(&ti, type, TKIND_ENUM, &hrefmap);
+    append_data(&data, &ti, sizeof(ti));
+
+    write_hrefmap(&data, &hrefmap);
+
+    member_offset = data.size;
+
+    member.res00 = 0x0001;
+    member.res02 = 0xffff;
+    member.res04 = 0x01;
+    member.extra = var_count * (sizeof(struct sltg_variable) + sizeof(int));
+    append_data(&data, &member, sizeof(member));
+
+    if (type_enum_get_values(type))
+    {
+        int i = 0;
+        short next = member_offset;
+        var_t *var;
+
+        LIST_FOR_EACH_ENTRY(var, type_enum_get_values(type), var_t, entry)
+        {
+            struct sltg_variable variable;
+
+            next += sizeof(variable);
+
+            variable.magic = 0x2a; /* always write flags to simplify calculations */
+            variable.name = add_name(&typelib->name_table, var->name);
+            variable.byte_offs = next - member_offset;
+            variable.flags = 0x12;
+            variable.type = VT_INT;
+            variable.next = i < var_count - 1 ? variable.byte_offs + sizeof(int) : -1;
+            variable.memid = 0x40000000 + i;
+            variable.helpcontext = -2; /* 0xfffe */
+            variable.helpstring = -1;
+            variable.varflags = 0;
+
+            append_data(&data, &variable, sizeof(variable));
+            append_data(&data, &var->eval->cval, sizeof(var->eval->cval));
+
+            next += sizeof(int);
+            i++;
+        }
+    }
+
+    init_sltg_tail(&tail);
+    tail.cVars = var_count;
+    tail.vars_off = 0;
+    tail.vars_bytes = 0;
+    tail.cbSizeInstance = sizeof(int);
+    tail.type_bytes = data.size - member_offset - sizeof(member);
+    append_data(&data, &tail, sizeof(tail));
+
+    add_block(typelib, data.data, data.size, index_name);
 }
 
 static void add_union_typeinfo(struct sltg_typelib *typelib, type_t *type)
 {
-    error("add_union_typeinfo: %s not implemented\n", type->name);
+    struct sltg_data data, *var_data = NULL;
+    struct sltg_hrefmap hrefmap;
+    const char *index_name;
+    struct sltg_typeinfo_header ti;
+    struct sltg_member_header member;
+    struct sltg_tail tail;
+    int member_offset, var_count = 0, var_data_size = 0, size_instance = 0;
+    short *type_desc_offset = NULL;
+
+    if (type->typelib_idx != -1) return;
+
+    chat("add_union_typeinfo: type %p, type->name %s\n", type, type->name);
+
+    type->typelib_idx = typelib->block_count;
+
+    hrefmap.href_count = 0;
+    hrefmap.href = NULL;
+
+    if (type_union_get_cases(type))
+    {
+        int i = 0;
+        var_t *var;
+
+        var_count = list_count(type_union_get_cases(type));
+
+        var_data = xmalloc(var_count * sizeof(*var_data));
+        type_desc_offset = xmalloc(var_count * sizeof(*type_desc_offset));
+
+        LIST_FOR_EACH_ENTRY(var, type_union_get_cases(type), var_t, entry)
+        {
+            short base_offset;
+
+            chat("add_union_typeinfo: var %p (%s), type %p (%s)\n",
+                 var, var->name, var->declspec.type, var->declspec.type->name);
+
+            init_sltg_data(&var_data[i]);
+
+            base_offset = var_data_size + (i + 1) * sizeof(struct sltg_variable);
+            type_desc_offset[i] = write_var_desc(typelib, &var_data[i], var->declspec.type, 0, 0,
+                                                 base_offset, &size_instance, &hrefmap);
+            dump_var_desc(var_data[i].data, var_data[i].size);
+
+            if (var_data[i].size > sizeof(short))
+                var_data_size += var_data[i].size;
+            i++;
+        }
+    }
+
+    init_sltg_data(&data);
+
+    index_name = add_typeinfo_block(typelib, type, TKIND_UNION);
+
+    init_typeinfo(&ti, type, TKIND_UNION, &hrefmap);
+    append_data(&data, &ti, sizeof(ti));
+
+    write_hrefmap(&data, &hrefmap);
+
+    member_offset = data.size;
+
+    member.res00 = 0x0001;
+    member.res02 = 0xffff;
+    member.res04 = 0x01;
+    member.extra = var_data_size + var_count * sizeof(struct sltg_variable);
+    append_data(&data, &member, sizeof(member));
+
+    if (type_union_get_cases(type))
+    {
+        int i = 0;
+        short next = member_offset;
+        var_t *var;
+
+        LIST_FOR_EACH_ENTRY(var, type_union_get_cases(type), var_t, entry)
+        {
+            struct sltg_variable variable;
+
+            next += sizeof(variable);
+
+            variable.magic = 0x2a; /* always write flags to simplify calculations */
+            variable.name = add_name(&typelib->name_table, var->name);
+            variable.byte_offs = 0;
+            if (var_data[i].size > sizeof(short))
+            {
+                variable.flags = 0;
+                variable.type = next - member_offset + type_desc_offset[i];
+                next += var_data[i].size;
+            }
+            else
+            {
+                variable.flags = 0x02;
+                variable.type = *(short *)var_data[i].data;
+            }
+            variable.next = i < var_count - 1 ? next - member_offset : -1;
+            variable.memid = 0x40000000 + i;
+            variable.helpcontext = -2; /* 0xfffe */
+            variable.helpstring = -1;
+            variable.varflags = 0;
+
+            append_data(&data, &variable, sizeof(variable));
+            if (var_data[i].size > sizeof(short))
+                append_data(&data, var_data[i].data, var_data[i].size);
+
+            i++;
+        }
+    }
+
+    init_sltg_tail(&tail);
+    tail.cVars = var_count;
+    tail.vars_off = 0;
+    tail.vars_bytes = 0;
+    tail.cbSizeInstance = size_instance;
+    tail.type_bytes = data.size - member_offset - sizeof(member);
+    append_data(&data, &tail, sizeof(tail));
+
+    add_block(typelib, data.data, data.size, index_name);
 }
 
 static void add_coclass_typeinfo(struct sltg_typelib *typelib, type_t *type)
